fakevimplugin.cpp 49.1 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()),
201
        SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
202
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
203
        SLOT(setQtStyle()));
hjk's avatar
hjk committed
204
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
205 206
        SLOT(setPlainStyle()));

207
    if (m_searchKeywords.isEmpty()) {
208
        QLatin1Char sep(' ');
209
        QTextStream(&m_searchKeywords)
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
                << 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();
226 227
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
228 229 230 231 232
    return w;
}

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

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

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

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

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

274 275 276 277 278 279
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

280
enum { CommandRole = Qt::UserRole };
281

282
class FakeVimExCommandsPage : public Core::CommandMappings
283 284 285 286
{
    Q_OBJECT

public:
287
    FakeVimExCommandsPage(FakeVimPluginPrivate *q) : m_q(q) {}
288
    ~FakeVimExCommandsPage() {}
289 290

    // IOptionsPage
hjk's avatar
hjk committed
291
    QString id() const { return _(Constants::SETTINGS_EX_CMDS_ID); }
292
    QString displayName() const { return tr("Ex Command Mapping"); }
hjk's avatar
hjk committed
293
    QString category() const { return _(Constants::SETTINGS_CATEGORY); }
294
    QString displayCategory() const { return tr("FakeVim"); }
295
    QIcon categoryIcon() const { return QIcon(); } // TODO: Icon for FakeVim
296 297 298

    QWidget *createPage(QWidget *parent);
    void initialize();
299 300
    CommandMap &exCommandMap();
    CommandMap &defaultExCommandMap();
301 302 303

public slots:
    void commandChanged(QTreeWidgetItem *current);
304 305 306
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
307 308 309
    void defaultAction();

private:
hjk's avatar
hjk committed
310
    //QList<QTreeWidgetItem *> m_citems;
311
    FakeVimPluginPrivate *m_q;
312 313 314 315
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
316 317 318
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
319
    setTargetLabelText(tr("Regular expression:"));
320 321
    setTargetEditTitle(tr("Ex Command"));
    setImportExportEnabled(false);
322 323 324 325 326
    return w;
}

void FakeVimExCommandsPage::initialize()
{
hjk's avatar
hjk committed
327
    ActionManager *am = ICore::instance()->actionManager();
328 329 330 331
    QTC_ASSERT(am, return);
    UniqueIDManager *uidm = UniqueIDManager::instance();
    QTC_ASSERT(uidm, return);

332 333
    QMap<QString, QTreeWidgetItem *> sections;

334 335 336 337
    foreach (Command *c, am->commands()) {
        if (c->action() && c->action()->isSeparator())
            continue;

338
        QTreeWidgetItem *item = new QTreeWidgetItem;
339
        item->setData(0, CommandRole, int(c->id()));
hjk's avatar
hjk committed
340
        //m_citems.append(item);
341 342

        const QString name = uidm->stringForUniqueIdentifier(c->id());
343 344
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
345
        const QString subId = name.mid(pos + 1);
346 347

        if (!sections.contains(section)) {
348 349
            QTreeWidgetItem *categoryItem =
                new QTreeWidgetItem(commandList(), QStringList() << section);
350 351 352 353
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
354
            commandList()->expandItem(categoryItem);
355 356 357 358
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
359 360

        if (c->action()) {
361 362 363
            QString text = c->hasAttribute(Command::CA_UpdateText)
                    && !c->defaultText().isNull()
                ? c->defaultText() : c->action()->text();
364 365 366 367 368 369
            text.remove(QRegExp("&(?!&)"));
            item->setText(1, text);
        } else {
            item->setText(1, c->shortcut()->whatsThis());
        }

370 371 372 373
        QString regex;
        if (exCommandMap().contains(name))
            regex = exCommandMap()[name].pattern();
        item->setText(2, regex);
374

375
        if (regex != defaultExCommandMap()[name].pattern())
376
            setModified(item, true);
377 378 379 380 381 382 383
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
384
    CommandMappings::commandChanged(current);
385 386
    if (current)
        targetEdit()->setText(current->text(2));
387 388
}

389
void FakeVimExCommandsPage::targetIdentifierChanged()
390
{
391
    QTreeWidgetItem *current = commandList()->currentItem();
392 393 394 395
    if (!current)
        return;

    UniqueIDManager *uidm = UniqueIDManager::instance();
396 397 398
    int id = current->data(0, CommandRole).toInt();
    const QString name = uidm->stringForUniqueIdentifier(id);
    const QString regex = targetEdit()->text();
399 400

    if (current->data(0, Qt::UserRole).isValid()) {
401 402
        current->setText(2, regex);
        exCommandMap()[name] = QRegExp(regex);
403
    }
404

405
    if (regex != defaultExCommandMap()[name].pattern())
406 407 408
        setModified(current, true);
    else
        setModified(current, false);
409

410 411
}

412
void FakeVimExCommandsPage::resetTargetIdentifier()
413
{
414
    QTreeWidgetItem *current = commandList()->currentItem();
415 416 417 418 419 420 421 422 423
    if (!current)
        return;
    UniqueIDManager *uidm = UniqueIDManager::instance();
    int id = current->data(0, CommandRole).toInt();
    const QString name = uidm->stringForUniqueIdentifier(id);
    QString regex;
    if (defaultExCommandMap().contains(name))
        regex = defaultExCommandMap()[name].pattern();
    targetEdit()->setText(regex);
424 425
}

426
void FakeVimExCommandsPage::removeTargetIdentifier()
427
{
428
    targetEdit()->clear();
429 430 431 432 433
}

void FakeVimExCommandsPage::defaultAction()
{
    UniqueIDManager *uidm = UniqueIDManager::instance();
hjk's avatar
hjk committed
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
    int n = commandList()->topLevelItemCount();
    for (int i = 0; i != n; ++i) {
        QTreeWidgetItem *section = commandList()->topLevelItem(i);
        int m = section->childCount();
        for (int j = 0; j != m; ++j) {
            QTreeWidgetItem *item = section->child(j);
            const int id = item->data(0, CommandRole).toInt();
            const QString name = uidm->stringForUniqueIdentifier(id);
            QString regex;
            if (defaultExCommandMap().contains(name))
                regex = defaultExCommandMap()[name].pattern();
            setModified(item, false);
            item->setText(2, regex);
            if (item == commandList()->currentItem())
                commandChanged(item);
        }
450 451 452
    }
}

hjk's avatar
hjk committed
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476

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

class WordCompletion : public ICompletionCollector
{
    Q_OBJECT

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

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

477
    virtual ITextEditor *editor() const
hjk's avatar
hjk committed
478 479 480 481 482
    {
        //qDebug() << "NO EDITOR?";
        return m_editable;
    }

483 484 485 486
    virtual int startPosition() const
    {
        return m_startPosition;
    }
hjk's avatar
hjk committed
487

488
    virtual bool supportsEditor(ITextEditor *) const
489 490 491 492 493 494 495 496
    {
        return true;
    }

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

498
    virtual bool triggersCompletion(ITextEditor *editable)
hjk's avatar
hjk committed
499 500 501 502 503 504
    {
        //qDebug() << "TRIGGERS?";
        QTC_ASSERT(m_editable == editable, /**/);
        return true;
    }

505
    virtual int startCompletion(ITextEditor *editable)
hjk's avatar
hjk committed
506 507 508 509 510 511 512 513 514 515 516 517 518
    {
        //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;
519
        m_editor = qobject_cast<BaseTextEditorWidget *>(handler->widget());
hjk's avatar
hjk committed
520 521 522 523
        if (!m_editor)
            return;
        //qDebug() << "ACTIVATE: " << needle << forward;
        m_needle = needle;
524
        m_editable = m_editor->editor();
hjk's avatar
hjk committed
525 526
        m_startPosition = m_editor->textCursor().position() - needle.size();

527
        CompletionSupport::instance()->complete(m_editable, TextCompletion, false);
hjk's avatar
hjk committed
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 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
    }

    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;
601 602
    BaseTextEditorWidget *m_editor;
    ITextEditor *m_editable;
hjk's avatar
hjk committed
603 604 605 606 607
    QString m_needle;
    QString m_currentPrefix;
    QList<CompletionItem> m_items;
    int m_startPosition;
};
608 609


hjk's avatar
hjk committed
610 611
///////////////////////////////////////////////////////////////////////
//
612
// FakeVimPluginPrivate
hjk's avatar
hjk committed
613 614 615
//
///////////////////////////////////////////////////////////////////////

616
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
617
{
618 619 620 621 622 623
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
624
    friend class FakeVimExCommandsPage;
625

626
    bool initialize();
627
    void aboutToShutdown();
628 629

private slots:
630
    void onCoreAboutToClose();
631 632
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
633

634
    void setUseFakeVim(const QVariant &value);
635
    void quitFakeVim();
636
    void triggerCompletions();
637
    void triggerSimpleCompletions(const QString &needle, bool forward);
hjk's avatar
hjk committed
638
    void windowCommand(int key);
639
    void find(bool reverse);
640
    void findNext(bool reverse);
hjk's avatar
hjk committed
641
    void showSettingsDialog();
642
    void maybeReadVimRc();
hjk's avatar
hjk committed
643 644
    void setBlockSelection(bool);
    void hasBlockSelection(bool*);
645 646 647 648

    void showCommandBuffer(const QString &contents);
    void showExtraInformation(const QString &msg);
    void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
649
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
650
    void checkForElectricCharacter(bool *result, QChar c);
651
    void indentRegion(int beginLine, int endLine, QChar typedChar);
652
    void handleExCommand(bool *handled, const ExCommand &cmd);
653

654 655 656
    void writeSettings();
    void readSettings();

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

660 661
    void switchToFile(int n);
    int currentFile() const;
662

663 664 665
signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
666 667 668

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
669
    FakeVimOptionPage *m_fakeVimOptionsPage;
670
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
671
    QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
hjk's avatar
hjk committed
672 673 674 675
    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
676
    EditorManager *editorManager() const { return m_editorManager; }
hjk's avatar
hjk committed
677
    ActionManager *actionManager() const { return m_actionManager; }
678

679 680
    void triggerAction(const QString &code);
    void setActionChecked(const QString &code, bool check);
681

682 683 684
    typedef int (*DistFunction)(const QRect &cursor, const QRect &other);
    void moveSomewhere(DistFunction f);

685
    CommandMap &exCommandMap() { return m_exCommandMap; }
686
    CommandMap &defaultExCommandMap() { return m_defaultExCommandMap; }
687 688
    CommandMap m_exCommandMap;
    CommandMap m_defaultExCommandMap;
689
    Core::StatusBarWidget *m_statusBar;
hjk's avatar
hjk committed
690
    WordCompletion *m_wordCompletion;
691 692 693
};

FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
694
{
695
    q = plugin;
hjk's avatar
hjk committed
696
    m_fakeVimOptionsPage = 0;
697
    m_fakeVimExCommandsPage = 0;
698
    defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
699
        QRegExp("^A$");
700
    defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
701
        QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
702
    defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
703
        QRegExp("^cn(ext)?!?( (.*))?$");
704
    defaultExCommandMap()[CppEditor::Constants::JUMP_TO_DEFINITION] =
705
        QRegExp("^tag?$");
706
    defaultExCommandMap()[Core::Constants::GO_BACK] =
707
        QRegExp("^pop?$");
hjk's avatar
hjk committed
708
    defaultExCommandMap()[_("QtCreator.Locate")] =
709
        QRegExp("^e$");
710
    m_statusBar = 0;
hjk's avatar
hjk committed
711 712
}

713
FakeVimPluginPrivate::~FakeVimPluginPrivate()
hjk's avatar
hjk committed
714
{
hjk's avatar
hjk committed
715 716 717
    q->removeObject(m_fakeVimOptionsPage);
    delete m_fakeVimOptionsPage;
    m_fakeVimOptionsPage = 0;
dt's avatar
dt committed
718
    delete theFakeVimSettings();
719 720 721 722

    q->removeObject(m_fakeVimExCommandsPage);
    delete m_fakeVimExCommandsPage;
    m_fakeVimExCommandsPage = 0;
723 724
}

725 726 727 728 729 730 731
void FakeVimPluginPrivate::onCoreAboutToClose()
{
    // don't attach to editors any more
    disconnect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
        this, SLOT(editorOpened(Core::IEditor*)));
}

732 733
void FakeVimPluginPrivate::aboutToShutdown()
{
hjk's avatar
hjk committed
734 735
}

736
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
737
{
hjk's avatar
hjk committed
738 739 740 741
    m_core = Core::ICore::instance();
    m_editorManager = core()->editorManager();
    m_actionManager = core()->actionManager();
    QTC_ASSERT(actionManager(), return false);
hjk's avatar
hjk committed
742

hjk's avatar
hjk committed
743 744 745 746 747 748 749 750 751 752 753 754
    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
755
    Context globalcontext(Core::Constants::C_GLOBAL);
hjk's avatar
hjk committed
756

hjk's avatar
hjk committed
757 758
    m_fakeVimOptionsPage = new FakeVimOptionPage;
    q->addObject(m_fakeVimOptionsPage);
759

760
    m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
761
    q->addObject(m_fakeVimExCommandsPage);
762
    readSettings();
763

con's avatar
con committed
764
    Core::Command *cmd = 0;
hjk's avatar
hjk committed
765
    cmd = actionManager()->registerAction(theFakeVimSetting(ConfigUseFakeVim),
hjk's avatar
hjk committed
766 767 768
        Constants::INSTALL_HANDLER, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));

769
    ActionContainer *advancedMenu =
hjk's avatar
hjk committed
770
        actionManager()->actionContainer(Core::Constants::M_EDIT_ADVANCED);
771
    advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
hjk's avatar
hjk committed
772

773 774
    connect(m_core, SIGNAL(coreAboutToClose()), this, SLOT(onCoreAboutToClose()));

775
    // EditorManager
hjk's avatar
hjk committed
776
    connect(editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
777
        this, SLOT(editorAboutToClose(Core::IEditor*)));
hjk's avatar
hjk committed
778
    connect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
779 780
        this, SLOT(editorOpened(Core::IEditor*)));

781 782
    connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
        this, SLOT(setUseFakeVim(QVariant)));
783 784
    connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
        this, SLOT(maybeReadVimRc()));
hjk's avatar
hjk committed
785

786
    // Delayed operations.
787 788 789 790
    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);
791 792
    maybeReadVimRc();
    //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
793

hjk's avatar
hjk committed
794 795 796
    return true;
}

797 798 799 800
static const char *exCommandMapGroup = "FakeVimExCommand";
static const char *reKey = "RegEx";
static const char *idKey = "Command";

801
void FakeVimPluginPrivate::writeSettings()
802
{
803 804 805
    QSettings *settings = ICore::instance()->settings();

    theFakeVimSettings()->writeSettings(settings);
806

807
    settings->beginWriteArray(_(exCommandMapGroup));
808
    int count = 0;
809 810 811
    typedef CommandMap::const_iterator Iterator;
    const Iterator end = exCommandMap().constEnd();
    for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
812 813
        const QString id = it.key();
        const QRegExp re = it.value();
814

815 816
        if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
            || (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
817
            settings->setArrayIndex(count);
hjk's avatar
hjk committed
818 819
            settings->setValue(_(idKey), id);
            settings->setValue(_(reKey), re.pattern());
820 821 822 823 824 825 826
            ++count;
        }
    }

    settings->endArray();
}

827
void FakeVimPluginPrivate::readSettings()
828
{
829 830 831
    QSettings *settings = ICore::instance()->settings();

    theFakeVimSettings()->readSettings(settings);
832

833
    exCommandMap() = defaultExCommandMap();
hjk's avatar
hjk committed
834
    int size = settings->beginReadArray(_(exCommandMapGroup));
835
    for (int i = 0; i < size; ++i) {
836
        settings->setArrayIndex(i);
hjk's avatar
hjk committed
837 838
        const QString id = settings->value(_(idKey)).toString();
        const QString re = settings->value(_(reKey)).toString();
839
        exCommandMap()[id] = QRegExp(re);
840 841 842 843
    }
    settings->endArray();
}

844 845
void FakeVimPluginPrivate::maybeReadVimRc()
{
hjk's avatar
hjk committed
846 847 848
    //qDebug() << theFakeVimSetting(ConfigReadVimRc)
    //    << theFakeVimSetting(ConfigReadVimRc)->value();
    //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();