fakevimplugin.cpp 49.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()),
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) {}
hjk's avatar
hjk committed
301
    ~FakeVimExCommandsPage() { qDeleteAll(m_citems); }
302 303

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

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

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

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

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
330 331 332
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
333
    setTargetLabelText(tr("Regular expression:"));
334 335
    setTargetEditTitle(tr("Ex Command"));
    setImportExportEnabled(false);
336 337 338 339 340
    return w;
}

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

346 347
    QMap<QString, QTreeWidgetItem *> sections;

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

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

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

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

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

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

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

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

    commandChanged(0);
}

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

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

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

412
void FakeVimExCommandsPage::targetIdentifierChanged()
413
{
414
    QTreeWidgetItem *current = commandList()->currentItem();
415 416 417 418 419 420 421 422
    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()) {
423
        citem->m_regex = targetEdit()->text();
424
        current->setText(2, citem->m_regex);
425
        exCommandMap()[name] = QRegExp(citem->m_regex);
426
    }
427

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

433 434 435 436
}

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

440
void FakeVimExCommandsPage::resetTargetIdentifier()
441 442
{
    UniqueIDManager *uidm = UniqueIDManager::instance();
443
    QTreeWidgetItem *current = commandList()->currentItem();
444 445 446
    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());
447 448
        if (defaultExCommandMap().contains(name))
            setRegex(defaultExCommandMap()[name].pattern());
449
        else
hjk's avatar
hjk committed
450
            setRegex(QString());
451 452 453
    }
}

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

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

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

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

class WordCompletion : public ICompletionCollector
{
    Q_OBJECT

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

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

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

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

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

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

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

528
    virtual int startCompletion(ITextEditor *editable)
hjk's avatar
hjk committed
529 530 531 532 533 534 535 536 537 538 539 540 541
    {
        //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;
542
        m_editor = qobject_cast<BaseTextEditorWidget *>(handler->widget());
hjk's avatar
hjk committed
543 544 545 546
        if (!m_editor)
            return;
        //qDebug() << "ACTIVATE: " << needle << forward;
        m_needle = needle;
547
        m_editable = m_editor->editor();
hjk's avatar
hjk committed
548 549
        m_startPosition = m_editor->textCursor().position() - needle.size();

550
        CompletionSupport::instance()->complete(m_editable, TextCompletion, false);
hjk's avatar
hjk committed
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 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
    }

    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;
624 625
    BaseTextEditorWidget *m_editor;
    ITextEditor *m_editable;
hjk's avatar
hjk committed
626 627 628 629 630
    QString m_needle;
    QString m_currentPrefix;
    QList<CompletionItem> m_items;
    int m_startPosition;
};
631 632


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

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

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

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

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

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

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

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

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

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

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
689
    FakeVimOptionPage *m_fakeVimOptionsPage;
690
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
691
    QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
hjk's avatar
hjk committed
692 693 694 695
    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
696
    EditorManager *editorManager() const { return m_editorManager; }
hjk's avatar
hjk committed
697
    ActionManager *actionManager() const { return m_actionManager; }
698

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

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

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

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

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

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

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

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

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

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

hjk's avatar
hjk committed
768 769 770 771 772 773 774 775 776 777 778 779
    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
780
    Context globalcontext(Core::Constants::C_GLOBAL);
hjk's avatar
hjk committed
781

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

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

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

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

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

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

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

812
    // Delayed operations.
813 814 815 816
    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);
817 818
    maybeReadVimRc();
    //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
819

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

823 824 825 826 827 828
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
829
    settings->beginWriteArray(_(exCommandMapGroup));
830 831

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

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

    settings->endArray();
}

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

hjk's avatar
hjk committed
854
    int size = settings->beginReadArray(_(exCommandMapGroup));