fakevimplugin.cpp 40.4 KB
Newer Older
1
/**************************************************************************
hjk's avatar
hjk committed
2 3 4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 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
**
9
** Commercial Usage
hjk's avatar
hjk committed
10
**
11 12 13 14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
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
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
hjk's avatar
hjk committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29 30 31

#include "fakevimplugin.h"

32
#include "fakevimhandler.h"
hjk's avatar
hjk committed
33 34
#include "ui_fakevimoptions.h"

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

#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/session.h>

55
#include <texteditor/basetextdocumentlayout.h>
hjk's avatar
hjk committed
56 57
#include <texteditor/basetexteditor.h>
#include <texteditor/basetextmark.h>
hjk's avatar
hjk committed
58
#include <texteditor/completionsupport.h>
hjk's avatar
hjk committed
59
#include <texteditor/texteditorconstants.h>
60 61
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorsettings.h>
hjk's avatar
hjk committed
62

63
#include <find/findplugin.h>
64 65
#include <find/textfindconstants.h>

hjk's avatar
hjk committed
66
#include <utils/qtcassert.h>
hjk's avatar
hjk committed
67
#include <utils/savedaction.h>
68
#include <utils/treewidgetcolumnstretcher.h>
hjk's avatar
hjk committed
69

70 71
#include <cppeditor/cppeditorconstants.h>

72 73
#include <cpptools/cpptoolsconstants.h>

hjk's avatar
hjk committed
74
#include <QtCore/QDebug>
75
#include <QtCore/QFile>
76
#include <QtCore/QtPlugin>
hjk's avatar
hjk committed
77 78
#include <QtCore/QObject>
#include <QtCore/QSettings>
79
#include <QtCore/QTextStream>
hjk's avatar
hjk committed
80

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

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


namespace FakeVim {
namespace Constants {

con's avatar
con committed
99 100 101 102 103 104 105
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
106 107 108 109 110

} // namespace Constants
} // namespace FakeVim


hjk's avatar
hjk committed
111 112 113 114 115 116 117 118 119
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

namespace FakeVim {
namespace Internal {

120
typedef QMap<QString, QRegExp> CommandMap;
hjk's avatar
hjk committed
121
typedef QLatin1String _;
122

hjk's avatar
hjk committed
123 124 125 126 127 128 129 130
class FakeVimOptionPage : public Core::IOptionsPage
{
    Q_OBJECT

public:
    FakeVimOptionPage() {}

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

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

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

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

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

    m_group.clear();
161
    m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
162
        m_ui.checkBoxUseFakeVim);
163 164
    m_group.insert(theFakeVimSetting(ConfigReadVimRc),
        m_ui.checkBoxReadVimRc);
hjk's avatar
hjk committed
165

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

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

186
    m_group.insert(theFakeVimSetting(ConfigAutoIndent),
hjk's avatar
hjk committed
187
        m_ui.checkBoxAutoIndent);
188
    m_group.insert(theFakeVimSetting(ConfigSmartIndent),
189
        m_ui.checkBoxSmartIndent);
190
    m_group.insert(theFakeVimSetting(ConfigIncSearch),
191
        m_ui.checkBoxIncSearch);
192 193
    m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
        m_ui.checkBoxUseCoreSearch);
hjk's avatar
hjk committed
194 195

    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
hjk's avatar
hjk committed
196
        this, SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
197 198 199 200
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
        this, SLOT(setQtStyle()));
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
        this, SLOT(setPlainStyle()));
201 202
    if (m_searchKeywords.isEmpty()) {
        QTextStream(&m_searchKeywords)
203 204
            << ' ' << m_ui.checkBoxAutoIndent->text()
            << ' ' << m_ui.checkBoxExpandTab->text()
205
            << ' ' << m_ui.checkBoxShowMarks->text()
206 207 208 209 210
            << ' ' << m_ui.checkBoxSmartIndent->text()
            << ' ' << m_ui.checkBoxHlSearch->text()
            << ' ' << m_ui.checkBoxIncSearch->text()
            << ' ' << m_ui.checkBoxSmartTab->text()
            << ' ' << m_ui.checkBoxStartOfLine->text()
hjk's avatar
hjk committed
211
            << ' ' << m_ui.labelShiftWidth->text()
212
            << ' ' << m_ui.labelTabulator->text()
hjk's avatar
hjk committed
213
            << ' ' << m_ui.labelBackspace->text();
214 215
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
216 217 218 219 220
    return w;
}

void FakeVimOptionPage::copyTextEditorSettings()
{
221
    TextEditor::TabSettings ts =
hjk's avatar
hjk committed
222
        TextEditor::TextEditorSettings::instance()->tabSettings();
223

hjk's avatar
hjk committed
224
    m_ui.checkBoxExpandTab->setChecked(ts.m_spacesForTabs);
225 226
    m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
    m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
hjk's avatar
hjk committed
227
    m_ui.checkBoxSmartTab->setChecked(ts.m_smartBackspace);
228 229
    m_ui.checkBoxAutoIndent->setChecked(true);
    m_ui.checkBoxSmartIndent->setChecked(ts.m_autoIndent);
230
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
231 232 233 234 235
}

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
236 237
    m_ui.spinBoxTabStop->setValue(4);
    m_ui.spinBoxShiftWidth->setValue(4);
hjk's avatar
hjk committed
238 239
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
240
    m_ui.checkBoxSmartIndent->setChecked(true);
241
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
242
    m_ui.lineEditBackspace->setText(_("indent,eol,start"));
hjk's avatar
hjk committed
243 244 245 246 247
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
248 249
    m_ui.spinBoxTabStop->setValue(8);
    m_ui.spinBoxShiftWidth->setValue(8);
hjk's avatar
hjk committed
250 251
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
252
    m_ui.checkBoxSmartIndent->setChecked(false);
253
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
254
    m_ui.lineEditBackspace->setText(QString());
hjk's avatar
hjk committed
255 256
}

257 258 259 260 261
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

hjk's avatar
hjk committed
262 263 264
} // namespace Internal
} // namespace FakeVim

265
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
266

267 268 269 270 271 272 273 274 275 276 277 278 279
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

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

hjk's avatar
hjk committed
280
Q_DECLARE_METATYPE(CommandItem *);
281 282 283 284

namespace FakeVim {
namespace Internal {

285
class FakeVimExCommandsPage : public Core::CommandMappings
286 287 288 289
{
    Q_OBJECT

public:
290
    FakeVimExCommandsPage(FakeVimPluginPrivate *q) : m_q(q) {}
291 292

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

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

public slots:
    void commandChanged(QTreeWidgetItem *current);
306 307 308
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
309 310 311 312 313
    void defaultAction();

private:
    void setRegex(const QString &regex);
    QList<CommandItem *> m_citems;
314
    FakeVimPluginPrivate *m_q;
315 316 317 318
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
319 320 321
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
322
    setTargetLabelText(tr("Regular expression:"));
323
    setTargetEditTitle(tr("Ex Command"));
324

325
    setImportExportEnabled(false);
326 327 328 329 330 331

    return w;
}

void FakeVimExCommandsPage::initialize()
{
hjk's avatar
hjk committed
332
    ActionManager *am = ICore::instance()->actionManager();
333 334 335 336
    QTC_ASSERT(am, return);
    UniqueIDManager *uidm = UniqueIDManager::instance();
    QTC_ASSERT(uidm, return);

337 338
    QMap<QString, QTreeWidgetItem *> sections;

339 340 341 342 343
    foreach (Command *c, am->commands()) {
        if (c->action() && c->action()->isSeparator())
            continue;

        CommandItem *ci = new CommandItem;
344
        QTreeWidgetItem *item = new QTreeWidgetItem;
345 346
        ci->m_cmd = c;
        ci->m_item = item;
347
        m_citems.append(ci);
348 349

        const QString name = uidm->stringForUniqueIdentifier(c->id());
350 351
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
352
        const QString subId = name.mid(pos + 1);
353 354

        if (!sections.contains(section)) {
355 356
            QTreeWidgetItem *categoryItem =
                new QTreeWidgetItem(commandList(), QStringList() << section);
357 358 359 360
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
361
            commandList()->expandItem(categoryItem);
362 363 364 365
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
366 367

        if (c->action()) {
368 369 370
            QString text = c->hasAttribute(Command::CA_UpdateText)
                    && !c->defaultText().isNull()
                ? c->defaultText() : c->action()->text();
371 372 373 374 375
            text.remove(QRegExp("&(?!&)"));
            item->setText(1, text);
        } else {
            item->setText(1, c->shortcut()->whatsThis());
        }
376 377
        if (exCommandMap().contains(name)) {
            ci->m_regex = exCommandMap()[name].pattern();
378
        } else {
hjk's avatar
hjk committed
379
            ci->m_regex.clear();
380 381 382 383
        }

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

385
        if (ci->m_regex != defaultExCommandMap()[name].pattern())
386
            setModified(item, true);
387 388 389 390 391 392 393
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
394 395 396
    CommandMappings::commandChanged(current);

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

399
    CommandItem *citem = qVariantValue<CommandItem *>(current->data(0, Qt::UserRole));
400
    targetEdit()->setText(citem->m_regex);
401 402
}

403
void FakeVimExCommandsPage::targetIdentifierChanged()
404
{
405
    QTreeWidgetItem *current = commandList()->currentItem();
406 407 408 409 410 411 412 413
    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()) {
414
        citem->m_regex = targetEdit()->text();
415
        current->setText(2, citem->m_regex);
416
        exCommandMap()[name] = QRegExp(citem->m_regex);
417
    }
418

419
    if (citem->m_regex != defaultExCommandMap()[name].pattern())
420 421 422
        setModified(current, true);
    else
        setModified(current, false);
423

424 425 426 427
}

void FakeVimExCommandsPage::setRegex(const QString &regex)
{
428
    targetEdit()->setText(regex);
429 430
}

431
void FakeVimExCommandsPage::resetTargetIdentifier()
432 433
{
    UniqueIDManager *uidm = UniqueIDManager::instance();
434
    QTreeWidgetItem *current = commandList()->currentItem();
435 436 437
    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());
438 439
        if (defaultExCommandMap().contains(name))
            setRegex(defaultExCommandMap()[name].pattern());
440
        else
hjk's avatar
hjk committed
441
            setRegex(QString());
442 443 444
    }
}

445
void FakeVimExCommandsPage::removeTargetIdentifier()
446
{
447
    targetEdit()->clear();
448 449 450 451 452 453 454
}

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

} // namespace Internal
} // namespace FakeVim


hjk's avatar
hjk committed
471 472
///////////////////////////////////////////////////////////////////////
//
473
// FakeVimPluginPrivate
hjk's avatar
hjk committed
474 475 476
//
///////////////////////////////////////////////////////////////////////

477 478 479 480
namespace FakeVim {
namespace Internal {

class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
481
{
482 483 484 485 486 487
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
488
    friend class FakeVimExCommandsPage;
489

490
    bool initialize();
491
    void aboutToShutdown();
492 493 494 495

private slots:
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
496

497
    void setUseFakeVim(const QVariant &value);
498
    void quitFakeVim();
499
    void triggerCompletions();
hjk's avatar
hjk committed
500
    void windowCommand(int key);
501
    void find(bool reverse);
502
    void findNext(bool reverse);
hjk's avatar
hjk committed
503
    void showSettingsDialog();
504
    void maybeReadVimRc();
hjk's avatar
hjk committed
505 506
    void setBlockSelection(bool);
    void hasBlockSelection(bool*);
507 508 509 510

    void showCommandBuffer(const QString &contents);
    void showExtraInformation(const QString &msg);
    void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
511
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
512
    void checkForElectricCharacter(bool *result, QChar c);
513
    void indentRegion(int beginLine, int endLine, QChar typedChar);
514
    void handleExCommand(bool *handled, const ExCommand &cmd);
515 516 517 518

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

519 520
    void switchToFile(int n);
    int currentFile() const;
521

522 523 524
signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
525 526 527

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
528
    FakeVimOptionPage *m_fakeVimOptionsPage;
529
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
530
    QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
hjk's avatar
hjk committed
531 532 533 534
    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
535
    EditorManager *editorManager() const { return m_editorManager; }
hjk's avatar
hjk committed
536
    ActionManager *actionManager() const { return m_actionManager; }
537

538 539
    void triggerAction(const QString &code);
    void setActionChecked(const QString &code, bool check);
540 541 542

    void readSettings(QSettings *settings);
    void writeSettings(QSettings *settings);
543 544 545 546 547

    CommandMap &exCommandMap() { return m_exCommandMap; }
    CommandMap &defaultExCommandMap() { return m_exCommandMap; }
    CommandMap m_exCommandMap;
    CommandMap m_defaultExCommandMap;
548
    Core::StatusBarWidget *m_statusBar;
549 550 551 552 553 554
};

} // namespace Internal
} // namespace FakeVim

FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
555
{
556
    q = plugin;
hjk's avatar
hjk committed
557
    m_fakeVimOptionsPage = 0;
558
    m_fakeVimExCommandsPage = 0;
559
    defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
560
        QRegExp("^A$");
561
    defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
562
        QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
563
    defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
564
        QRegExp("^cn(ext)?!?( (.*))?$");
565
    defaultExCommandMap()[CppEditor::Constants::JUMP_TO_DEFINITION] =
566
        QRegExp("^tag?$");
567
    defaultExCommandMap()[Core::Constants::GO_BACK] =
568
        QRegExp("^pop?$");
hjk's avatar
hjk committed
569
    defaultExCommandMap()[_("QtCreator.Locate")] =
570
        QRegExp("^e$");
571
    m_statusBar = 0;
hjk's avatar
hjk committed
572 573
}

574
FakeVimPluginPrivate::~FakeVimPluginPrivate()
hjk's avatar
hjk committed
575
{
hjk's avatar
hjk committed
576 577 578
    q->removeObject(m_fakeVimOptionsPage);
    delete m_fakeVimOptionsPage;
    m_fakeVimOptionsPage = 0;
dt's avatar
dt committed
579
    delete theFakeVimSettings();
580 581 582 583

    q->removeObject(m_fakeVimExCommandsPage);
    delete m_fakeVimExCommandsPage;
    m_fakeVimExCommandsPage = 0;
584 585 586 587
}

void FakeVimPluginPrivate::aboutToShutdown()
{
hjk's avatar
hjk committed
588 589
    theFakeVimSettings()->writeSettings(ICore::instance()->settings());
    writeSettings(ICore::instance()->settings());
hjk's avatar
hjk committed
590 591
}

592
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
593
{
hjk's avatar
hjk committed
594 595 596 597
    m_core = Core::ICore::instance();
    m_editorManager = core()->editorManager();
    m_actionManager = core()->actionManager();
    QTC_ASSERT(actionManager(), return false);
hjk's avatar
hjk committed
598

hjk's avatar
hjk committed
599
    Context globalcontext(Core::Constants::C_GLOBAL);
hjk's avatar
hjk committed
600

hjk's avatar
hjk committed
601 602
    m_fakeVimOptionsPage = new FakeVimOptionPage;
    q->addObject(m_fakeVimOptionsPage);
hjk's avatar
hjk committed
603
    theFakeVimSettings()->readSettings(ICore::instance()->settings());
604

605
    m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
606
    q->addObject(m_fakeVimExCommandsPage);
hjk's avatar
hjk committed
607
    readSettings(core()->settings());
608

con's avatar
con committed
609
    Core::Command *cmd = 0;
hjk's avatar
hjk committed
610
    cmd = actionManager()->registerAction(theFakeVimSetting(ConfigUseFakeVim),
hjk's avatar
hjk committed
611 612 613
        Constants::INSTALL_HANDLER, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));

614
    ActionContainer *advancedMenu =
hjk's avatar
hjk committed
615
        actionManager()->actionContainer(Core::Constants::M_EDIT_ADVANCED);
616
    advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
hjk's avatar
hjk committed
617

618
    // EditorManager
hjk's avatar
hjk committed
619
    connect(editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
620
        this, SLOT(editorAboutToClose(Core::IEditor*)));
hjk's avatar
hjk committed
621
    connect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
622 623
        this, SLOT(editorOpened(Core::IEditor*)));

624 625
    connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
        this, SLOT(setUseFakeVim(QVariant)));
626 627
    connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
        this, SLOT(maybeReadVimRc()));
hjk's avatar
hjk committed
628

629
    // Delayed operations.
630 631 632 633
    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);
634 635
    maybeReadVimRc();
    //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
636

hjk's avatar
hjk committed
637 638 639
    return true;
}

640 641 642 643 644 645
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
646
    settings->beginWriteArray(_(exCommandMapGroup));
647 648

    int count = 0;
649 650 651
    typedef CommandMap::const_iterator Iterator;
    const Iterator end = exCommandMap().constEnd();
    for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
652 653 654
        const QString &id = it.key();
        const QRegExp &re = it.value();

655 656
        if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
            || (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
657
            settings->setArrayIndex(count);
hjk's avatar
hjk committed
658 659
            settings->setValue(_(idKey), id);
            settings->setValue(_(reKey), re.pattern());
660 661 662 663 664 665 666 667 668
            ++count;
        }
    }

    settings->endArray();
}

void FakeVimPluginPrivate::readSettings(QSettings *settings)
{
669
    exCommandMap() = defaultExCommandMap();
670

hjk's avatar
hjk committed
671
    int size = settings->beginReadArray(_(exCommandMapGroup));
672
    for (int i = 0; i < size; ++i) {
673
        settings->setArrayIndex(i);
hjk's avatar
hjk committed
674 675
        const QString id = settings->value(_(idKey)).toString();
        const QString re = settings->value(_(reKey)).toString();
676
        exCommandMap()[id] = QRegExp(re);
677 678 679 680
    }
    settings->endArray();
}

681 682
void FakeVimPluginPrivate::maybeReadVimRc()
{
hjk's avatar
hjk committed
683 684 685
    //qDebug() << theFakeVimSetting(ConfigReadVimRc)
    //    << theFakeVimSetting(ConfigReadVimRc)->value();
    //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
686 687 688 689 690 691 692 693 694 695
    if (!theFakeVimSetting(ConfigReadVimRc)->value().toBool())
        return;
    QString fileName =
        QDesktopServices::storageLocation(QDesktopServices::HomeLocation)
            + "/.vimrc";
    //qDebug() << "READING VIMRC: " << fileName;
    // Read it into a temporary handler for effects modifying global state.
    QPlainTextEdit editor;
    FakeVimHandler handler(&editor);
    handler.handleCommand("source " + fileName);
hjk's avatar
hjk committed
696
    theFakeVimSettings()->writeSettings(core()->settings());
hjk's avatar
hjk committed
697
    //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
698 699
}

hjk's avatar
hjk committed
700 701
void FakeVimPluginPrivate::showSettingsDialog()
{
hjk's avatar
hjk committed
702
    core()->showOptionsDialog(
hjk's avatar
hjk committed
703 704
        _(Constants::SETTINGS_CATEGORY),
        _(Constants::SETTINGS_ID));
hjk's avatar
hjk committed
705 706
}

707
void FakeVimPluginPrivate::triggerAction(const QString &code)
708
{
hjk's avatar
hjk committed
709
    Core::ActionManager *am = actionManager();
710 711
    QTC_ASSERT(am, return);
    Core::Command *cmd = am->command(code);
hjk's avatar
hjk committed
712
    QTC_ASSERT(cmd, qDebug() << "UNKNOW CODE: " << code; return);
713 714 715 716 717
    QAction *action = cmd->action();
    QTC_ASSERT(action, return);
    action->trigger();
}

718
void FakeVimPluginPrivate::setActionChecked(const QString &code, bool check)
719
{
hjk's avatar
hjk committed
720
    Core::ActionManager *am = actionManager();
721 722 723 724 725 726 727 728 729 730
    QTC_ASSERT(am, return);
    Core::Command *cmd = am->command(code);
    QTC_ASSERT(cmd, return);
    QAction *action = cmd->action();
    QTC_ASSERT(action, return);
    QTC_ASSERT(action->isCheckable(), return);
    action->setChecked(check);
    action->trigger();
}

hjk's avatar
hjk committed
731 732
void FakeVimPluginPrivate::windowCommand(int key)
{
Tobias Hunger's avatar
Tobias Hunger committed
733
#    define control(n) (256 + n)
hjk's avatar
hjk committed
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
    QString code;
    switch (key) {
        case 'c': case 'C': case control('c'):
            code = Core::Constants::CLOSE;
            break;
        case 'n': case 'N': case control('n'):
            code = Core::Constants::GOTONEXT;
            break;
        case 'o': case 'O': case control('o'):
            code = Core::Constants::REMOVE_ALL_SPLITS;
            code = Core::Constants::REMOVE_CURRENT_SPLIT;
            break;
        case 'p': case 'P': case control('p'):
            code = Core::Constants::GOTOPREV;
            break;
        case 's': case 'S': case control('s'):
            code = Core::Constants::SPLIT;
            break;
        case 'w': case 'W': case control('w'):
            code = Core::Constants::GOTO_OTHER_SPLIT;
            break;
    }
Tobias Hunger's avatar
Tobias Hunger committed
756
#    undef control
hjk's avatar
hjk committed
757
    //qDebug() << "RUNNING WINDOW COMMAND: " << key << code;
hjk's avatar
hjk committed
758
    if (code.isEmpty()) {
hjk's avatar
hjk committed
759
        //qDebug() << "UNKNOWN WINDOWS COMMAND: " << key;
hjk's avatar
hjk committed
760 761
        return;
    }
762 763 764 765 766
    triggerAction(code);
}

void FakeVimPluginPrivate::find(bool reverse)
{
767
    if (Find::FindPlugin *plugin = Find::FindPlugin::instance()) {
768 769
        plugin->setUseFakeVim(true);
        plugin->openFindToolBar(reverse
770 771
                ? Find::FindPlugin::FindBackward
                : Find::FindPlugin::FindForward);
772
    }
hjk's avatar
hjk committed
773 774
}

775 776 777 778 779 780 781 782
void FakeVimPluginPrivate::findNext(bool reverse)
{
    if (reverse)
        triggerAction(Find::Constants::FIND_PREVIOUS);
    else
        triggerAction(Find::Constants::FIND_NEXT);
}

783
// This class defers deletion of a child FakeVimHandler using deleteLater().
784 785 786 787 788 789 790 791
class DeferredDeleter : public QObject
{
    Q_OBJECT

    FakeVimHandler *m_handler;

public:
    DeferredDeleter(QObject *parent, FakeVimHandler *handler)
792
        : QObject(parent), m_handler(handler)
793 794 795