fakevimplugin.cpp 49.7 KB
Newer Older
1
/**************************************************************************
hjk's avatar
hjk committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
hjk's avatar
hjk committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
hjk's avatar
hjk committed
8
**
con's avatar
con committed
9
** No Commercial Usage
hjk's avatar
hjk committed
10
**
con's avatar
con committed
11 12 13 14
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
hjk's avatar
hjk committed
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
**
18 19 20 21 22 23
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
con's avatar
con committed
25 26 27 28 29 30
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
hjk's avatar
hjk committed
31
**
32
**************************************************************************/
hjk's avatar
hjk committed
33 34 35

#include "fakevimplugin.h"

36
#include "fakevimhandler.h"
hjk's avatar
hjk committed
37 38
#include "ui_fakevimoptions.h"

39
#include <coreplugin/actionmanager/actioncontainer.h>
40
#include <coreplugin/actionmanager/actionmanager.h>
41 42
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/command.h>
43
#include <coreplugin/actionmanager/commandmappings.h>
hjk's avatar
hjk committed
44
#include <coreplugin/coreconstants.h>
45
#include <coreplugin/dialogs/ioptionspage.h>
hjk's avatar
hjk committed
46
#include <coreplugin/editormanager/editormanager.h>
47
#include <coreplugin/editormanager/openeditorsmodel.h>
48
#include <coreplugin/filemanager.h>
hjk's avatar
hjk committed
49
#include <coreplugin/icore.h>
50
#include <coreplugin/ifile.h>
hjk's avatar
hjk committed
51 52
#include <coreplugin/messagemanager.h>
#include <coreplugin/uniqueidmanager.h>
53 54
#include <coreplugin/statusbarwidget.h>
#include <coreplugin/statusbarmanager.h>
hjk's avatar
hjk committed
55 56 57

#include <projectexplorer/projectexplorerconstants.h>

58
#include <texteditor/basetextdocumentlayout.h>
hjk's avatar
hjk committed
59 60
#include <texteditor/basetexteditor.h>
#include <texteditor/basetextmark.h>
hjk's avatar
hjk committed
61
#include <texteditor/completionsupport.h>
hjk's avatar
hjk committed
62
#include <texteditor/texteditorconstants.h>
63 64
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorsettings.h>
65
#include <texteditor/indenter.h>
66
#include <texteditor/icompletioncollector.h>
hjk's avatar
hjk committed
67

68
#include <find/findplugin.h>
69 70
#include <find/textfindconstants.h>

hjk's avatar
hjk committed
71
#include <utils/qtcassert.h>
hjk's avatar
hjk committed
72
#include <utils/savedaction.h>
73
#include <utils/treewidgetcolumnstretcher.h>
hjk's avatar
hjk committed
74

75 76
#include <cppeditor/cppeditorconstants.h>

77 78
#include <cpptools/cpptoolsconstants.h>

hjk's avatar
hjk committed
79
#include <QtCore/QDebug>
80
#include <QtCore/QFile>
81
#include <QtCore/QtPlugin>
hjk's avatar
hjk committed
82 83
#include <QtCore/QObject>
#include <QtCore/QSettings>
84
#include <QtCore/QTextStream>
hjk's avatar
hjk committed
85

86
#include <QtGui/QDesktopServices>
87
#include <QtGui/QMessageBox>
hjk's avatar
hjk committed
88
#include <QtGui/QPlainTextEdit>
89
#include <QtGui/QShortcut>
hjk's avatar
hjk committed
90 91
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
92
#include <QtGui/QTextEdit>
93
#include <QtGui/QTreeWidgetItem>
hjk's avatar
hjk committed
94 95 96 97 98 99 100 101

using namespace FakeVim::Internal;
using namespace TextEditor;
using namespace Core;

namespace FakeVim {
namespace Constants {

con's avatar
con committed
102 103 104 105 106 107 108
const char * const INSTALL_HANDLER                = "TextEditor.FakeVimHandler";
const char * const MINI_BUFFER                    = "TextEditor.FakeVimMiniBuffer";
const char * const INSTALL_KEY                    = "Alt+V,Alt+V";
const char * const SETTINGS_CATEGORY              = "D.FakeVim";
const char * const SETTINGS_CATEGORY_FAKEVIM_ICON = ":/core/images/category_fakevim.png";
const char * const SETTINGS_ID                    = "A.General";
const char * const SETTINGS_EX_CMDS_ID            = "B.ExCommands";
hjk's avatar
hjk committed
109 110 111 112 113

} // namespace Constants
} // namespace FakeVim


hjk's avatar
hjk committed
114 115 116
namespace FakeVim {
namespace Internal {

hjk's avatar
hjk committed
117 118 119 120 121 122
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

123
typedef QMap<QString, QRegExp> CommandMap;
hjk's avatar
hjk committed
124
typedef QLatin1String _;
125

hjk's avatar
hjk committed
126 127 128 129 130 131 132 133
class FakeVimOptionPage : public Core::IOptionsPage
{
    Q_OBJECT

public:
    FakeVimOptionPage() {}

    // IOptionsPage
hjk's avatar
hjk committed
134
    QString id() const { return _(Constants::SETTINGS_ID); }
135
    QString displayName() const { return tr("General"); }
hjk's avatar
hjk committed
136
    QString category() const { return _(Constants::SETTINGS_CATEGORY); }
137
    QString displayCategory() const { return tr("FakeVim"); }
hjk's avatar
hjk committed
138 139
    QIcon categoryIcon() const
        { return QIcon(_(Constants::SETTINGS_CATEGORY_FAKEVIM_ICON)); }
hjk's avatar
hjk committed
140 141 142 143

    QWidget *createPage(QWidget *parent);
    void apply() { m_group.apply(ICore::instance()->settings()); }
    void finish() { m_group.finish(); }
144
    virtual bool matches(const QString &) const;
hjk's avatar
hjk committed
145 146 147 148 149 150 151 152 153

private slots:
    void copyTextEditorSettings();
    void setQtStyle();
    void setPlainStyle();

private:
    friend class DebuggerPlugin;
    Ui::FakeVimOptionPage m_ui;
154
    QString m_searchKeywords;
155
    Utils::SavedActionSet m_group;
hjk's avatar
hjk committed
156 157 158 159 160 161 162 163
};

QWidget *FakeVimOptionPage::createPage(QWidget *parent)
{
    QWidget *w = new QWidget(parent);
    m_ui.setupUi(w);

    m_group.clear();
164
    m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
165
        m_ui.checkBoxUseFakeVim);
166 167
    m_group.insert(theFakeVimSetting(ConfigReadVimRc),
        m_ui.checkBoxReadVimRc);
hjk's avatar
hjk committed
168

169
    m_group.insert(theFakeVimSetting(ConfigExpandTab),
hjk's avatar
hjk committed
170
        m_ui.checkBoxExpandTab);
171
    m_group.insert(theFakeVimSetting(ConfigHlSearch),
hjk's avatar
hjk committed
172
        m_ui.checkBoxHlSearch);
173
    m_group.insert(theFakeVimSetting(ConfigShiftWidth),
174
        m_ui.spinBoxShiftWidth);
175 176
    m_group.insert(theFakeVimSetting(ConfigShowMarks),
        m_ui.checkBoxShowMarks);
hjk's avatar
hjk committed
177

178
    m_group.insert(theFakeVimSetting(ConfigSmartTab),
hjk's avatar
hjk committed
179
        m_ui.checkBoxSmartTab);
180
    m_group.insert(theFakeVimSetting(ConfigStartOfLine),
hjk's avatar
hjk committed
181
        m_ui.checkBoxStartOfLine);
182
    m_group.insert(theFakeVimSetting(ConfigTabStop),
183
        m_ui.spinBoxTabStop);
184
    m_group.insert(theFakeVimSetting(ConfigBackspace),
hjk's avatar
hjk committed
185
        m_ui.lineEditBackspace);
186 187
    m_group.insert(theFakeVimSetting(ConfigIsKeyword),
        m_ui.lineEditIsKeyword);
hjk's avatar
hjk committed
188

189 190
    m_group.insert(theFakeVimSetting(ConfigPassControlKey),
        m_ui.checkBoxPassControlKey);
191
    m_group.insert(theFakeVimSetting(ConfigAutoIndent),
hjk's avatar
hjk committed
192
        m_ui.checkBoxAutoIndent);
193
    m_group.insert(theFakeVimSetting(ConfigSmartIndent),
194
        m_ui.checkBoxSmartIndent);
195
    m_group.insert(theFakeVimSetting(ConfigIncSearch),
196
        m_ui.checkBoxIncSearch);
197 198
    m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
        m_ui.checkBoxUseCoreSearch);
hjk's avatar
hjk committed
199 200

    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
hjk's avatar
hjk committed
201
        this, SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
202 203 204 205
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
        this, SLOT(setQtStyle()));
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
        this, SLOT(setPlainStyle()));
206
    if (m_searchKeywords.isEmpty()) {
207
        QLatin1Char sep(' ');
208
        QTextStream(&m_searchKeywords)
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
                << sep << m_ui.checkBoxUseFakeVim->text()
                << sep << m_ui.checkBoxReadVimRc->text()
                << sep << m_ui.checkBoxAutoIndent->text()
                << sep << m_ui.checkBoxSmartIndent->text()
                << sep << m_ui.checkBoxExpandTab->text()
                << sep << m_ui.checkBoxSmartTab->text()
                << sep << m_ui.checkBoxHlSearch->text()
                << sep << m_ui.checkBoxIncSearch->text()
                << sep << m_ui.checkBoxStartOfLine->text()
                << sep << m_ui.checkBoxUseCoreSearch->text()
                << sep << m_ui.checkBoxShowMarks->text()
                << sep << m_ui.checkBoxPassControlKey->text()
                << sep << m_ui.labelShiftWidth->text()
                << sep << m_ui.labelTabulator->text()
                << sep << m_ui.labelBackspace->text()
                << sep << m_ui.labelIsKeyword->text();
225 226
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
227 228 229 230 231
    return w;
}

void FakeVimOptionPage::copyTextEditorSettings()
{
hjk's avatar
hjk committed
232
    TabSettings ts = TextEditorSettings::instance()->tabSettings();
hjk's avatar
hjk committed
233
    m_ui.checkBoxExpandTab->setChecked(ts.m_spacesForTabs);
234 235
    m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
    m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
hjk's avatar
hjk committed
236
    m_ui.checkBoxSmartTab->setChecked(ts.m_smartBackspace);
237 238
    m_ui.checkBoxAutoIndent->setChecked(true);
    m_ui.checkBoxSmartIndent->setChecked(ts.m_autoIndent);
239
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
240 241 242 243 244
}

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
245 246
    m_ui.spinBoxTabStop->setValue(4);
    m_ui.spinBoxShiftWidth->setValue(4);
hjk's avatar
hjk committed
247 248
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
249
    m_ui.checkBoxSmartIndent->setChecked(true);
250
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
251
    m_ui.lineEditBackspace->setText(_("indent,eol,start"));
hjk's avatar
hjk committed
252 253 254 255 256
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
257 258
    m_ui.spinBoxTabStop->setValue(8);
    m_ui.spinBoxShiftWidth->setValue(8);
hjk's avatar
hjk committed
259 260
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
261
    m_ui.checkBoxSmartIndent->setChecked(false);
262
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
263
    m_ui.lineEditBackspace->setText(QString());
hjk's avatar
hjk committed
264 265
}

266 267 268 269 270
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

271
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
272

273 274 275 276 277 278 279 280 281 282 283 284 285
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

struct CommandItem
{
    Command *m_cmd;
    QString m_regex;
    QTreeWidgetItem *m_item;
};

hjk's avatar
hjk committed
286 287 288 289 290
} // namespace Internal
} // namespace FakeVim


Q_DECLARE_METATYPE(FakeVim::Internal::CommandItem *)
291 292 293 294

namespace FakeVim {
namespace Internal {

295
class FakeVimExCommandsPage : public Core::CommandMappings
296 297 298 299
{
    Q_OBJECT

public:
300
    FakeVimExCommandsPage(FakeVimPluginPrivate *q) : m_q(q) {}
301 302

    // IOptionsPage
hjk's avatar
hjk committed
303
    QString id() const { return _(Constants::SETTINGS_EX_CMDS_ID); }
304
    QString displayName() const { return tr("Ex Command Mapping"); }
hjk's avatar
hjk committed
305
    QString category() const { return _(Constants::SETTINGS_CATEGORY); }
306
    QString displayCategory() const { return tr("FakeVim"); }
307
    QIcon categoryIcon() const { return QIcon(); } // TODO: Icon for FakeVim
308 309 310

    QWidget *createPage(QWidget *parent);
    void initialize();
311 312
    CommandMap &exCommandMap();
    CommandMap &defaultExCommandMap();
313 314 315

public slots:
    void commandChanged(QTreeWidgetItem *current);
316 317 318
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
319 320 321 322 323
    void defaultAction();

private:
    void setRegex(const QString &regex);
    QList<CommandItem *> m_citems;
324
    FakeVimPluginPrivate *m_q;
325 326 327 328
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
329 330 331
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
332
    setTargetLabelText(tr("Regular expression:"));
333
    setTargetEditTitle(tr("Ex Command"));
334

335
    setImportExportEnabled(false);
336 337 338 339 340 341

    return w;
}

void FakeVimExCommandsPage::initialize()
{
hjk's avatar
hjk committed
342
    ActionManager *am = ICore::instance()->actionManager();
343 344 345 346
    QTC_ASSERT(am, return);
    UniqueIDManager *uidm = UniqueIDManager::instance();
    QTC_ASSERT(uidm, return);

347 348
    QMap<QString, QTreeWidgetItem *> sections;

349 350 351 352 353
    foreach (Command *c, am->commands()) {
        if (c->action() && c->action()->isSeparator())
            continue;

        CommandItem *ci = new CommandItem;
354
        QTreeWidgetItem *item = new QTreeWidgetItem;
355 356
        ci->m_cmd = c;
        ci->m_item = item;
357
        m_citems.append(ci);
358 359

        const QString name = uidm->stringForUniqueIdentifier(c->id());
360 361
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
362
        const QString subId = name.mid(pos + 1);
363 364

        if (!sections.contains(section)) {
365 366
            QTreeWidgetItem *categoryItem =
                new QTreeWidgetItem(commandList(), QStringList() << section);
367 368 369 370
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
371
            commandList()->expandItem(categoryItem);
372 373 374 375
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
376 377

        if (c->action()) {
378 379 380
            QString text = c->hasAttribute(Command::CA_UpdateText)
                    && !c->defaultText().isNull()
                ? c->defaultText() : c->action()->text();
381 382 383 384 385
            text.remove(QRegExp("&(?!&)"));
            item->setText(1, text);
        } else {
            item->setText(1, c->shortcut()->whatsThis());
        }
386 387
        if (exCommandMap().contains(name)) {
            ci->m_regex = exCommandMap()[name].pattern();
388
        } else {
hjk's avatar
hjk committed
389
            ci->m_regex.clear();
390 391 392 393
        }

        item->setText(2, ci->m_regex);
        item->setData(0, Qt::UserRole, qVariantFromValue(ci));
394

395
        if (ci->m_regex != defaultExCommandMap()[name].pattern())
396
            setModified(item, true);
397 398 399 400 401 402 403
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
404 405 406
    CommandMappings::commandChanged(current);

    if (!current || !current->data(0, Qt::UserRole).isValid())
407
        return;
408

409
    CommandItem *citem = qVariantValue<CommandItem *>(current->data(0, Qt::UserRole));
410
    targetEdit()->setText(citem->m_regex);
411 412
}

413
void FakeVimExCommandsPage::targetIdentifierChanged()
414
{
415
    QTreeWidgetItem *current = commandList()->currentItem();
416 417 418 419 420 421 422 423
    if (!current)
        return;

    UniqueIDManager *uidm = UniqueIDManager::instance();
    CommandItem *citem = qVariantValue<CommandItem *>(current->data(0, Qt::UserRole));
    const QString name = uidm->stringForUniqueIdentifier(citem->m_cmd->id());

    if (current->data(0, Qt::UserRole).isValid()) {
424
        citem->m_regex = targetEdit()->text();
425
        current->setText(2, citem->m_regex);
426
        exCommandMap()[name] = QRegExp(citem->m_regex);
427
    }
428

429
    if (citem->m_regex != defaultExCommandMap()[name].pattern())
430 431 432
        setModified(current, true);
    else
        setModified(current, false);
433

434 435 436 437
}

void FakeVimExCommandsPage::setRegex(const QString &regex)
{
438
    targetEdit()->setText(regex);
439 440
}

441
void FakeVimExCommandsPage::resetTargetIdentifier()
442 443
{
    UniqueIDManager *uidm = UniqueIDManager::instance();
444
    QTreeWidgetItem *current = commandList()->currentItem();
445 446 447
    if (current && current->data(0, Qt::UserRole).isValid()) {
        CommandItem *citem = qVariantValue<CommandItem *>(current->data(0, Qt::UserRole));
        const QString &name = uidm->stringForUniqueIdentifier(citem->m_cmd->id());
448 449
        if (defaultExCommandMap().contains(name))
            setRegex(defaultExCommandMap()[name].pattern());
450
        else
hjk's avatar
hjk committed
451
            setRegex(QString());
452 453 454
    }
}

455
void FakeVimExCommandsPage::removeTargetIdentifier()
456
{
457
    targetEdit()->clear();
458 459 460 461 462 463 464
}

void FakeVimExCommandsPage::defaultAction()
{
    UniqueIDManager *uidm = UniqueIDManager::instance();
    foreach (CommandItem *item, m_citems) {
        const QString &name = uidm->stringForUniqueIdentifier(item->m_cmd->id());
465 466
        if (defaultExCommandMap().contains(name)) {
            item->m_regex = defaultExCommandMap()[name].pattern();
467
        } else {
hjk's avatar
hjk committed
468
            item->m_regex.clear();
469
        }
470
        setModified(item->m_item, false);
471
        item->m_item->setText(2, item->m_regex);
472
        if (item->m_item == commandList()->currentItem())
473 474 475 476
            commandChanged(item->m_item);
    }
}

hjk's avatar
hjk committed
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500

///////////////////////////////////////////////////////////////////////
//
// WordCompletion
//
///////////////////////////////////////////////////////////////////////

class WordCompletion : public ICompletionCollector
{
    Q_OBJECT

public:
    WordCompletion()
    {
        m_editable = 0;
        m_editor = 0;
    }

    virtual bool shouldRestartCompletion()
    {
        //qDebug() << "SHOULD RESTART COMPLETION?";
        return false;
    }

501
    virtual ITextEditor *editor() const
hjk's avatar
hjk committed
502 503 504 505 506
    {
        //qDebug() << "NO EDITOR?";
        return m_editable;
    }

507 508 509 510
    virtual int startPosition() const
    {
        return m_startPosition;
    }
hjk's avatar
hjk committed
511

512
    virtual bool supportsEditor(ITextEditor *) const
513 514 515 516 517 518 519 520
    {
        return true;
    }

    virtual bool supportsPolicy(CompletionPolicy policy) const
    {
        return policy == TextCompletion;
    }
hjk's avatar
hjk committed
521

522
    virtual bool triggersCompletion(ITextEditor *editable)
hjk's avatar
hjk committed
523 524 525 526 527 528
    {
        //qDebug() << "TRIGGERS?";
        QTC_ASSERT(m_editable == editable, /**/);
        return true;
    }

529
    virtual int startCompletion(ITextEditor *editable)
hjk's avatar
hjk committed
530 531 532 533 534 535 536 537 538 539 540 541 542
    {
        //qDebug() << "START COMPLETION";
        QTC_ASSERT(m_editor, return -1);
        QTC_ASSERT(m_editable == editable, return -1);
        return m_editor->textCursor().position();
    }

    void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
    {
        Q_UNUSED(forward);
        m_handler = handler;
        if (!m_handler)
            return;
543
        m_editor = qobject_cast<BaseTextEditorWidget *>(handler->widget());
hjk's avatar
hjk committed
544 545 546 547
        if (!m_editor)
            return;
        //qDebug() << "ACTIVATE: " << needle << forward;
        m_needle = needle;
548
        m_editable = m_editor->editor();
hjk's avatar
hjk committed
549 550
        m_startPosition = m_editor->textCursor().position() - needle.size();

551
        CompletionSupport::instance()->complete(m_editable, TextCompletion, false);
hjk's avatar
hjk committed
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
    }

    void setInactive()
    {
        m_needle.clear();
        m_editable = 0;
        m_editor = 0;
        m_handler = 0;
        m_startPosition = -1;
    }

    virtual void completions(QList<CompletionItem> *completions)
    {
        QTC_ASSERT(m_editor, return);
        QTC_ASSERT(completions, return);
        QTextCursor tc = m_editor->textCursor();
        tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);

        QSet<QString> seen;

        QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
        while (1) {
            tc = tc.document()->find(m_needle, tc.position(), flags);
            if (tc.isNull())
                break;
            QTextCursor sel = tc;
            sel.select(QTextCursor::WordUnderCursor);
            QString found = sel.selectedText();
            // Only add "real" completions.
            if (found.startsWith(m_needle)
                    && !seen.contains(found)
                    && sel.anchor() != m_startPosition) {
                seen.insert(found);
                CompletionItem item;
                item.collector = this;
                item.text = found;
                completions->append(item);
            }
            tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
        }
        //qDebug() << "COMPLETIONS" << completions->size();
    }

    virtual bool typedCharCompletes(const CompletionItem &item, QChar typedChar)
    {
        m_needle += typedChar;
        //qDebug() << "COMPLETE? " << typedChar << item.text << m_needle;
        return item.text == m_needle;
    }

    virtual void complete(const CompletionItem &item, QChar typedChar)
    {
        Q_UNUSED(typedChar);
        //qDebug() << "COMPLETE: " << item.text;
        QTC_ASSERT(m_handler, return);
        m_handler->handleReplay(item.text.mid(m_needle.size()));
        setInactive();
    }

    virtual bool partiallyComplete(const QList<CompletionItem> &completionItems)
    {
        //qDebug() << "PARTIALLY";
        Q_UNUSED(completionItems);
        return false;
    }

    virtual void cleanup() {}

private:
    int findStartOfName(int pos = -1) const;
    bool isInComment() const;

    FakeVimHandler *m_handler;
625 626
    BaseTextEditorWidget *m_editor;
    ITextEditor *m_editable;
hjk's avatar
hjk committed
627 628 629 630 631
    QString m_needle;
    QString m_currentPrefix;
    QList<CompletionItem> m_items;
    int m_startPosition;
};
632 633


hjk's avatar
hjk committed
634 635
///////////////////////////////////////////////////////////////////////
//
636
// FakeVimPluginPrivate
hjk's avatar
hjk committed
637 638 639
//
///////////////////////////////////////////////////////////////////////

640
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
641
{
642 643 644 645 646 647
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
648
    friend class FakeVimExCommandsPage;
649

650
    bool initialize();
651
    void aboutToShutdown();
652 653

private slots:
654
    void onCoreAboutToClose();
655 656
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
657

658
    void setUseFakeVim(const QVariant &value);
659
    void quitFakeVim();
660
    void triggerCompletions();
661
    void triggerSimpleCompletions(const QString &needle, bool forward);
hjk's avatar
hjk committed
662
    void windowCommand(int key);
663
    void find(bool reverse);
664
    void findNext(bool reverse);
hjk's avatar
hjk committed
665
    void showSettingsDialog();
666
    void maybeReadVimRc();
hjk's avatar
hjk committed
667 668
    void setBlockSelection(bool);
    void hasBlockSelection(bool*);
669 670 671 672

    void showCommandBuffer(const QString &contents);
    void showExtraInformation(const QString &msg);
    void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
673
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
674
    void checkForElectricCharacter(bool *result, QChar c);
675
    void indentRegion(int beginLine, int endLine, QChar typedChar);
676
    void handleExCommand(bool *handled, const ExCommand &cmd);
677 678 679 680

    void handleDelayedQuitAll(bool forced);
    void handleDelayedQuit(bool forced, Core::IEditor *editor);

681 682
    void switchToFile(int n);
    int currentFile() const;
683

684 685 686
signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
687 688 689

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
690
    FakeVimOptionPage *m_fakeVimOptionsPage;
691
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
692
    QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
hjk's avatar
hjk committed
693 694 695 696
    QPointer<Core::ICore> m_core;
    QPointer<Core::EditorManager> m_editorManager;
    QPointer<Core::ActionManager> m_actionManager;
    ICore *core() const { return m_core; }
hjk's avatar
hjk committed
697
    EditorManager *editorManager() const { return m_editorManager; }
hjk's avatar
hjk committed
698
    ActionManager *actionManager() const { return m_actionManager; }
699

700 701
    void triggerAction(const QString &code);
    void setActionChecked(const QString &code, bool check);
702 703 704

    void readSettings(QSettings *settings);
    void writeSettings(QSettings *settings);
705

706 707 708
    typedef int (*DistFunction)(const QRect &cursor, const QRect &other);
    void moveSomewhere(DistFunction f);

709 710 711 712
    CommandMap &exCommandMap() { return m_exCommandMap; }
    CommandMap &defaultExCommandMap() { return m_exCommandMap; }
    CommandMap m_exCommandMap;
    CommandMap m_defaultExCommandMap;
713
    Core::StatusBarWidget *m_statusBar;
hjk's avatar
hjk committed
714
    WordCompletion *m_wordCompletion;
715 716 717
};

FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
718
{
719
    q = plugin;
hjk's avatar
hjk committed
720
    m_fakeVimOptionsPage = 0;
721
    m_fakeVimExCommandsPage = 0;
722
    defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
723
        QRegExp("^A$");
724
    defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
725
        QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
726
    defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
727
        QRegExp("^cn(ext)?!?( (.*))?$");
728
    defaultExCommandMap()[CppEditor::Constants::JUMP_TO_DEFINITION] =
729
        QRegExp("^tag?$");
730
    defaultExCommandMap()[Core::Constants::GO_BACK] =
731
        QRegExp("^pop?$");
hjk's avatar
hjk committed
732
    defaultExCommandMap()[_("QtCreator.Locate")] =
733
        QRegExp("^e$");
734
    m_statusBar = 0;
hjk's avatar
hjk committed
735 736
}

737
FakeVimPluginPrivate::~FakeVimPluginPrivate()
hjk's avatar
hjk committed
738
{
hjk's avatar
hjk committed
739 740 741
    q->removeObject(m_fakeVimOptionsPage);
    delete m_fakeVimOptionsPage;
    m_fakeVimOptionsPage = 0;
dt's avatar
dt committed
742
    delete theFakeVimSettings();
743 744 745 746

    q->removeObject(m_fakeVimExCommandsPage);
    delete m_fakeVimExCommandsPage;
    m_fakeVimExCommandsPage = 0;
747 748
}

749 750 751 752 753 754 755
void FakeVimPluginPrivate::onCoreAboutToClose()
{
    // don't attach to editors any more
    disconnect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
        this, SLOT(editorOpened(Core::IEditor*)));
}

756 757
void FakeVimPluginPrivate::aboutToShutdown()
{
hjk's avatar
hjk committed
758 759
    theFakeVimSettings()->writeSettings(ICore::instance()->settings());
    writeSettings(ICore::instance()->settings());
hjk's avatar
hjk committed
760 761
}

762
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
763
{
hjk's avatar
hjk committed
764 765 766 767
    m_core = Core::ICore::instance();
    m_editorManager = core()->editorManager();
    m_actionManager = core()->actionManager();
    QTC_ASSERT(actionManager(), return false);
hjk's avatar
hjk committed
768

hjk's avatar
hjk committed
769 770 771 772 773 774 775 776 777 778 779 780
    m_wordCompletion = new WordCompletion;
    q->addAutoReleasedObject(m_wordCompletion);
/*
    // Set completion settings and keep them up to date.
    TextEditorSettings *textEditorSettings = TextEditorSettings::instance();
    completion->setCompletionSettings(textEditorSettings->completionSettings());
    connect(textEditorSettings,
        SIGNAL(completionSettingsChanged(TextEditor::CompletionSettings)),
        completion,
        SLOT(setCompletionSettings(TextEditor::CompletionSettings)));
*/

hjk's avatar
hjk committed
781
    Context globalcontext(Core::Constants::C_GLOBAL);
hjk's avatar
hjk committed
782

hjk's avatar
hjk committed
783 784
    m_fakeVimOptionsPage = new FakeVimOptionPage;
    q->addObject(m_fakeVimOptionsPage);
hjk's avatar
hjk committed
785
    theFakeVimSettings()->readSettings(ICore::instance()->settings());
786

787
    m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
788
    q->addObject(m_fakeVimExCommandsPage);
hjk's avatar
hjk committed
789
    readSettings(core()->settings());
790

con's avatar
con committed
791
    Core::Command *cmd = 0;
hjk's avatar
hjk committed
792
    cmd = actionManager()->registerAction(theFakeVimSetting(ConfigUseFakeVim),
hjk's avatar
hjk committed
793 794 795
        Constants::INSTALL_HANDLER, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));

796
    ActionContainer *advancedMenu =
hjk's avatar
hjk committed
797
        actionManager()->actionContainer(Core::Constants::M_EDIT_ADVANCED);
798
    advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
hjk's avatar
hjk committed
799

800 801
    connect(m_core, SIGNAL(coreAboutToClose()), this, SLOT(onCoreAboutToClose()));

802
    // EditorManager
hjk's avatar
hjk committed
803
    connect(editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
804
        this, SLOT(editorAboutToClose(Core::IEditor*)));
hjk's avatar
hjk committed
805
    connect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
806 807
        this, SLOT(editorOpened(Core::IEditor*)));

808 809
    connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
        this, SLOT(setUseFakeVim(QVariant)));
810 811
    connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
        this, SLOT(maybeReadVimRc()));
hjk's avatar
hjk committed
812

813
    // Delayed operations.
814 815 816 817
    connect(this, SIGNAL(delayedQuitRequested(bool,Core::IEditor*)),
        this, SLOT(handleDelayedQuit(bool,Core::IEditor*)), Qt::QueuedConnection);
    connect(this, SIGNAL(delayedQuitAllRequested(bool)),
        this, SLOT(handleDelayedQuitAll(bool)), Qt::QueuedConnection);
818 819
    maybeReadVimRc();
    //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
820

hjk's avatar
hjk committed
821 822 823
    return true;
}

824 825 826 827 828 829
static const char *exCommandMapGroup = "FakeVimExCommand";
static const char *reKey = "RegEx";
static const char *idKey = "Command";

void FakeVimPluginPrivate::writeSettings(QSettings *settings)
{
hjk's avatar
hjk committed
830
    settings->beginWriteArray(_(exCommandMapGroup));
831 832

    int count = 0;
833 834 835
    typedef CommandMap::const_iterator Iterator;
    const Iterator end = exCommandMap().constEnd();
    for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
836 837 838
        const QString &id = it.key();
        const QRegExp &re = it.value();

839 840
        if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
            || (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
841
            settings->setArrayIndex(count);
hjk's avatar
hjk committed
842 843
            settings->setValue(_(idKey), id);
            settings->setValue(_(reKey), re.pattern());
844 845 846 847 848 849 850 851 852
            ++count;
        }
    }

    settings->endArray();
}

void FakeVimPluginPrivate::readSettings(QSettings *settings)
{
853
    exCommandMap() = defaultExCommandMap();
854

hjk's avatar
hjk committed