fakevimplugin.cpp 48.8 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:
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 340
        item->setData(0, CommandRole, int(c->id()));
        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();
434 435 436 437 438 439 440 441 442 443
    foreach (QTreeWidgetItem *item, m_citems) {
        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);
444 445 446
    }
}

hjk's avatar
hjk committed
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470

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

class WordCompletion : public ICompletionCollector
{
    Q_OBJECT

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

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

471
    virtual ITextEditor *editor() const
hjk's avatar
hjk committed
472 473 474 475 476
    {
        //qDebug() << "NO EDITOR?";
        return m_editable;
    }

477 478 479 480
    virtual int startPosition() const
    {
        return m_startPosition;
    }
hjk's avatar
hjk committed
481

482
    virtual bool supportsEditor(ITextEditor *) const
483 484 485 486 487 488 489 490
    {
        return true;
    }

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

492
    virtual bool triggersCompletion(ITextEditor *editable)
hjk's avatar
hjk committed
493 494 495 496 497 498
    {
        //qDebug() << "TRIGGERS?";
        QTC_ASSERT(m_editable == editable, /**/);
        return true;
    }

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

521
        CompletionSupport::instance()->complete(m_editable, TextCompletion, false);
hjk's avatar
hjk committed
522 523 524 525 526 527 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
    }

    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;
595 596
    BaseTextEditorWidget *m_editor;
    ITextEditor *m_editable;
hjk's avatar
hjk committed
597 598 599 600 601
    QString m_needle;
    QString m_currentPrefix;
    QList<CompletionItem> m_items;
    int m_startPosition;
};
602 603


hjk's avatar
hjk committed
604 605
///////////////////////////////////////////////////////////////////////
//
606
// FakeVimPluginPrivate
hjk's avatar
hjk committed
607 608 609
//
///////////////////////////////////////////////////////////////////////

610
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
611
{
612 613 614 615 616 617
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
618
    friend class FakeVimExCommandsPage;
619

620
    bool initialize();
621
    void aboutToShutdown();
622 623

private slots:
624
    void onCoreAboutToClose();
625 626
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
627

628
    void setUseFakeVim(const QVariant &value);
629
    void quitFakeVim();
630
    void triggerCompletions();
631
    void triggerSimpleCompletions(const QString &needle, bool forward);
hjk's avatar
hjk committed
632
    void windowCommand(int key);
633
    void find(bool reverse);
634
    void findNext(bool reverse);
hjk's avatar
hjk committed
635
    void showSettingsDialog();
636
    void maybeReadVimRc();
hjk's avatar
hjk committed
637 638
    void setBlockSelection(bool);
    void hasBlockSelection(bool*);
639 640 641 642

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

648 649 650
    void writeSettings();
    void readSettings();

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

654 655
    void switchToFile(int n);
    int currentFile() const;
656

657 658 659
signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
660 661 662

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
663
    FakeVimOptionPage *m_fakeVimOptionsPage;
664
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
665
    QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
hjk's avatar
hjk committed
666 667 668 669
    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
670
    EditorManager *editorManager() const { return m_editorManager; }
hjk's avatar
hjk committed
671
    ActionManager *actionManager() const { return m_actionManager; }
672

673 674
    void triggerAction(const QString &code);
    void setActionChecked(const QString &code, bool check);
675

676 677 678
    typedef int (*DistFunction)(const QRect &cursor, const QRect &other);
    void moveSomewhere(DistFunction f);

679
    CommandMap &exCommandMap() { return m_exCommandMap; }
680
    CommandMap &defaultExCommandMap() { return m_defaultExCommandMap; }
681 682
    CommandMap m_exCommandMap;
    CommandMap m_defaultExCommandMap;
683
    Core::StatusBarWidget *m_statusBar;
hjk's avatar
hjk committed
684
    WordCompletion *m_wordCompletion;
685 686 687
};

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

707
FakeVimPluginPrivate::~FakeVimPluginPrivate()
hjk's avatar
hjk committed
708
{
hjk's avatar
hjk committed
709 710 711
    q->removeObject(m_fakeVimOptionsPage);
    delete m_fakeVimOptionsPage;
    m_fakeVimOptionsPage = 0;
dt's avatar
dt committed
712
    delete theFakeVimSettings();
713 714 715 716

    q->removeObject(m_fakeVimExCommandsPage);
    delete m_fakeVimExCommandsPage;
    m_fakeVimExCommandsPage = 0;
717 718
}

719 720 721 722 723 724 725
void FakeVimPluginPrivate::onCoreAboutToClose()
{
    // don't attach to editors any more
    disconnect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
        this, SLOT(editorOpened(Core::IEditor*)));
}

726 727
void FakeVimPluginPrivate::aboutToShutdown()
{
hjk's avatar
hjk committed
728 729
}

730
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
731
{
hjk's avatar
hjk committed
732 733 734 735
    m_core = Core::ICore::instance();
    m_editorManager = core()->editorManager();
    m_actionManager = core()->actionManager();
    QTC_ASSERT(actionManager(), return false);
hjk's avatar
hjk committed
736

hjk's avatar
hjk committed
737 738 739 740 741 742 743 744 745 746 747 748
    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
749
    Context globalcontext(Core::Constants::C_GLOBAL);
hjk's avatar
hjk committed
750

hjk's avatar
hjk committed
751 752
    m_fakeVimOptionsPage = new FakeVimOptionPage;
    q->addObject(m_fakeVimOptionsPage);
753

754
    m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
755
    q->addObject(m_fakeVimExCommandsPage);
756
    readSettings();
757

con's avatar
con committed
758
    Core::Command *cmd = 0;
hjk's avatar
hjk committed
759
    cmd = actionManager()->registerAction(theFakeVimSetting(ConfigUseFakeVim),
hjk's avatar
hjk committed
760 761 762
        Constants::INSTALL_HANDLER, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));

763
    ActionContainer *advancedMenu =
hjk's avatar
hjk committed
764
        actionManager()->actionContainer(Core::Constants::M_EDIT_ADVANCED);
765
    advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
hjk's avatar
hjk committed
766

767 768
    connect(m_core, SIGNAL(coreAboutToClose()), this, SLOT(onCoreAboutToClose()));

769
    // EditorManager
hjk's avatar
hjk committed
770
    connect(editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
771
        this, SLOT(editorAboutToClose(Core::IEditor*)));
hjk's avatar
hjk committed
772
    connect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
773 774
        this, SLOT(editorOpened(Core::IEditor*)));

775 776
    connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
        this, SLOT(setUseFakeVim(QVariant)));
777 778
    connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
        this, SLOT(maybeReadVimRc()));
hjk's avatar
hjk committed
779

780
    // Delayed operations.
781 782 783 784
    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);
785 786
    maybeReadVimRc();
    //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
787

hjk's avatar
hjk committed
788 789 790
    return true;
}

791 792 793 794
static const char *exCommandMapGroup = "FakeVimExCommand";
static const char *reKey = "RegEx";
static const char *idKey = "Command";

795
void FakeVimPluginPrivate::writeSettings()
796
{
797 798 799
    QSettings *settings = ICore::instance()->settings();

    theFakeVimSettings()->writeSettings(settings);
800

801
    settings->beginWriteArray(_(exCommandMapGroup));
802
    int count = 0;
803 804 805
    typedef CommandMap::const_iterator Iterator;
    const Iterator end = exCommandMap().constEnd();
    for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
806 807
        const QString id = it.key();
        const QRegExp re = it.value();
808

809 810
        if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
            || (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
811
            settings->setArrayIndex(count);
hjk's avatar
hjk committed
812 813
            settings->setValue(_(idKey), id);
            settings->setValue(_(reKey), re.pattern());
814 815 816 817 818 819 820
            ++count;
        }
    }

    settings->endArray();
}

821
void FakeVimPluginPrivate::readSettings()
822
{
823 824 825
    QSettings *settings = ICore::instance()->settings();

    theFakeVimSettings()->readSettings(settings);
826

827
    exCommandMap() = defaultExCommandMap();
hjk's avatar
hjk committed
828
    int size = settings->beginReadArray(_(exCommandMapGroup));
829
    for (int i = 0; i < size; ++i) {
830
        settings->setArrayIndex(i);
hjk's avatar
hjk committed
831 832
        const QString id = settings->value(_(idKey)).toString();
        const QString re = settings->value(_(reKey)).toString();
833
        exCommandMap()[id] = QRegExp(re);
834 835 836 837
    }
    settings->endArray();
}

838 839
void FakeVimPluginPrivate::maybeReadVimRc()
{
hjk's avatar
hjk committed
840 841 842
    //qDebug() << theFakeVimSetting(ConfigReadVimRc)
    //    << theFakeVimSetting(ConfigReadVimRc)->value();
    //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
843 844 845 846 847 848 849 850 851 852
    if (!theFakeVimSetting(ConfigReadVimRc)->value().toBool())
        return;
    QString fileName =
        QDesktopServices::storageLocation(QDesktopServices::HomeLocation)
            + "/.vimrc";