fakevimplugin.cpp 39.7 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/actionmanager.h>
36 37 38
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/command.h>
hjk's avatar
hjk committed
39 40
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
41
#include <coreplugin/editormanager/openeditorsmodel.h>
42
#include <coreplugin/filemanager.h>
hjk's avatar
hjk committed
43
#include <coreplugin/icore.h>
44
#include <coreplugin/ifile.h>
hjk's avatar
hjk committed
45
#include <coreplugin/dialogs/ioptionspage.h>
46
#include <coreplugin/actionmanager/commandmappings.h>
hjk's avatar
hjk committed
47 48 49 50 51 52
#include <coreplugin/messagemanager.h>
#include <coreplugin/uniqueidmanager.h>

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

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

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

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

69 70
#include <cppeditor/cppeditorconstants.h>

71 72
#include <cpptools/cpptoolsconstants.h>

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

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

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


namespace FakeVim {
namespace Constants {

con's avatar
con committed
98 99 100 101 102 103 104 105 106
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";
const char * const CMD_FILE_NEXT                  = "FakeVim.SwitchFileNext";
const char * const CMD_FILE_PREV                  = "FakeVim.SwitchFilePrev";
hjk's avatar
hjk committed
107 108 109 110 111

} // namespace Constants
} // namespace FakeVim


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

namespace FakeVim {
namespace Internal {

121 122
typedef QMap<QString, QRegExp> CommandMap;

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

public:
    FakeVimOptionPage() {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

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

Q_DECLARE_METATYPE(CommandItem*);

namespace FakeVim {
namespace Internal {

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

public:
288
    FakeVimExCommandsPage(FakeVimPluginPrivate *q) : m_q(q) {}
289 290 291 292 293 294

    // IOptionsPage
    QString id() const { return QLatin1String(Constants::SETTINGS_EX_CMDS_ID); }
    QString displayName() const { return tr("Ex Command Mapping"); }
    QString category() const { return QLatin1String(Constants::SETTINGS_CATEGORY); }
    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 310 311
    void defaultAction();

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

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

323
    setImportExportEnabled(false);
324 325 326 327 328 329 330 331 332 333 334

    return w;
}

void FakeVimExCommandsPage::initialize()
{
    Core::ActionManager *am = Core::ICore::instance()->actionManager();
    QTC_ASSERT(am, return);
    UniqueIDManager *uidm = UniqueIDManager::instance();
    QTC_ASSERT(uidm, return);

335 336
    QMap<QString, QTreeWidgetItem *> sections;

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

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

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

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

        item->setText(0, subId);
364 365

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

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

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

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
392 393 394
    CommandMappings::commandChanged(current);

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

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

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

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

422 423 424 425
}

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

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

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

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

} // namespace Internal
} // namespace FakeVim


hjk's avatar
hjk committed
469 470
///////////////////////////////////////////////////////////////////////
//
471
// FakeVimPluginPrivate
hjk's avatar
hjk committed
472 473 474
//
///////////////////////////////////////////////////////////////////////

475 476 477 478
namespace FakeVim {
namespace Internal {

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

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
486
    friend class FakeVimExCommandsPage;
487

488
    bool initialize();
489
    void aboutToShutdown();
490 491 492 493

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

495
    void setUseFakeVim(const QVariant &value);
496
    void quitFakeVim();
497
    void triggerCompletions();
hjk's avatar
hjk committed
498
    void windowCommand(int key);
499
    void find(bool reverse);
500
    void findNext(bool reverse);
hjk's avatar
hjk committed
501
    void showSettingsDialog();
502
    void maybeReadVimRc();
503 504 505 506

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

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

515 516 517 518
    void switchFile(bool previous);
    void switchFileNext();
    void switchFilePrev();

519 520 521
signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
522 523 524

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
525
    FakeVimOptionPage *m_fakeVimOptionsPage;
526
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
527
    QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
528

529 530
    void triggerAction(const QString &code);
    void setActionChecked(const QString &code, bool check);
531 532 533

    void readSettings(QSettings *settings);
    void writeSettings(QSettings *settings);
534 535 536 537 538

    CommandMap &exCommandMap() { return m_exCommandMap; }
    CommandMap &defaultExCommandMap() { return m_exCommandMap; }
    CommandMap m_exCommandMap;
    CommandMap m_defaultExCommandMap;
539 540 541 542 543 544
};

} // namespace Internal
} // namespace FakeVim

FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
545
{
546
    q = plugin;
hjk's avatar
hjk committed
547
    m_fakeVimOptionsPage = 0;
548
    m_fakeVimExCommandsPage = 0;
549
    defaultExCommandMap()[Constants::CMD_FILE_NEXT] =
550
        QRegExp("^n(ext)?!?( (.*))?$");
551
    defaultExCommandMap()[Constants::CMD_FILE_PREV] =
552
        QRegExp("^(N(ext)?|prev(ious)?)!?( (.*))?$");
553
    defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
554
        QRegExp("^A$");
555
    defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
556
        QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
557
    defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
558
        QRegExp("^cn(ext)?!?( (.*))?$");
559
    defaultExCommandMap()[CppEditor::Constants::JUMP_TO_DEFINITION] =
560
        QRegExp("^tag?$");
561
    defaultExCommandMap()[Core::Constants::GO_BACK] =
562
        QRegExp("^pop?$");
563
    defaultExCommandMap()[QLatin1String("QtCreator.Locate")] =
564
        QRegExp("^e$");
hjk's avatar
hjk committed
565 566
}

567
FakeVimPluginPrivate::~FakeVimPluginPrivate()
hjk's avatar
hjk committed
568
{
hjk's avatar
hjk committed
569 570 571
    q->removeObject(m_fakeVimOptionsPage);
    delete m_fakeVimOptionsPage;
    m_fakeVimOptionsPage = 0;
dt's avatar
dt committed
572
    delete theFakeVimSettings();
573 574 575 576

    q->removeObject(m_fakeVimExCommandsPage);
    delete m_fakeVimExCommandsPage;
    m_fakeVimExCommandsPage = 0;
577 578 579 580 581
}

void FakeVimPluginPrivate::aboutToShutdown()
{
    theFakeVimSettings()->writeSettings(Core::ICore::instance()->settings());
582
    writeSettings(Core::ICore::instance()->settings());
hjk's avatar
hjk committed
583 584
}

585
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
586
{
hjk's avatar
hjk committed
587
    Core::ActionManager *actionManager = Core::ICore::instance()->actionManager();
hjk's avatar
hjk committed
588 589 590 591 592
    QTC_ASSERT(actionManager, return false);

    QList<int> globalcontext;
    globalcontext << Core::Constants::C_GLOBAL_ID;

hjk's avatar
hjk committed
593 594 595
    m_fakeVimOptionsPage = new FakeVimOptionPage;
    q->addObject(m_fakeVimOptionsPage);
    theFakeVimSettings()->readSettings(Core::ICore::instance()->settings());
596

597
    m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
598 599
    q->addObject(m_fakeVimExCommandsPage);
    readSettings(Core::ICore::instance()->settings());
600

con's avatar
con committed
601
    Core::Command *cmd = 0;
602
    cmd = actionManager->registerAction(theFakeVimSetting(ConfigUseFakeVim),
hjk's avatar
hjk committed
603 604 605
        Constants::INSTALL_HANDLER, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));

606
    ActionContainer *advancedMenu =
hjk's avatar
hjk committed
607
        actionManager->actionContainer(Core::Constants::M_EDIT_ADVANCED);
608
    advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
hjk's avatar
hjk committed
609

610
    // EditorManager
hjk's avatar
hjk committed
611
    QObject *editorManager = Core::ICore::instance()->editorManager();
612 613 614 615 616
    connect(editorManager, SIGNAL(editorAboutToClose(Core::IEditor*)),
        this, SLOT(editorAboutToClose(Core::IEditor*)));
    connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
        this, SLOT(editorOpened(Core::IEditor*)));

617 618
    connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
        this, SLOT(setUseFakeVim(QVariant)));
619 620
    connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
        this, SLOT(maybeReadVimRc()));
hjk's avatar
hjk committed
621

622 623 624 625 626 627 628 629 630 631
    QAction *switchFileNextAction = new QAction(tr("Switch to next file"), this);
    cmd = actionManager->registerAction(switchFileNextAction, Constants::CMD_FILE_NEXT, globalcontext);
    cmd->setAttribute(Command::CA_Hide);
    connect(switchFileNextAction, SIGNAL(triggered()), this, SLOT(switchFileNext()));

    QAction *switchFilePrevAction = new QAction(tr("Switch to previous file"), this);
    cmd = actionManager->registerAction(switchFilePrevAction, Constants::CMD_FILE_PREV, globalcontext);
    cmd->setAttribute(Command::CA_Hide);
    connect(switchFilePrevAction, SIGNAL(triggered()), this, SLOT(switchFilePrev()));

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

hjk's avatar
hjk committed
640 641 642
    return true;
}

643 644 645 646 647 648 649 650 651
static const char *exCommandMapGroup = "FakeVimExCommand";
static const char *reKey = "RegEx";
static const char *idKey = "Command";

void FakeVimPluginPrivate::writeSettings(QSettings *settings)
{
    settings->beginWriteArray(QLatin1String(exCommandMapGroup));

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

658 659
        if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
            || (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
660 661 662 663 664 665 666 667 668 669 670 671
            settings->setArrayIndex(count);
            settings->setValue(QLatin1String(idKey), id);
            settings->setValue(QLatin1String(reKey), re.pattern());
            ++count;
        }
    }

    settings->endArray();
}

void FakeVimPluginPrivate::readSettings(QSettings *settings)
{
672
    exCommandMap() = defaultExCommandMap();
673 674

    int size = settings->beginReadArray(QLatin1String(exCommandMapGroup));
675
    for (int i = 0; i < size; ++i) {
676 677 678
        settings->setArrayIndex(i);
        const QString id = settings->value(QLatin1String(idKey)).toString();
        const QString re = settings->value(QLatin1String(reKey)).toString();
679
        exCommandMap()[id] = QRegExp(re);
680 681 682 683
    }
    settings->endArray();
}

684 685
void FakeVimPluginPrivate::maybeReadVimRc()
{
hjk's avatar
hjk committed
686 687 688
    //qDebug() << theFakeVimSetting(ConfigReadVimRc)
    //    << theFakeVimSetting(ConfigReadVimRc)->value();
    //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
689 690 691 692 693 694 695 696 697 698 699
    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);
    theFakeVimSettings()->writeSettings(Core::ICore::instance()->settings());
hjk's avatar
hjk committed
700
    //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
701 702
}

hjk's avatar
hjk committed
703 704
void FakeVimPluginPrivate::showSettingsDialog()
{
705 706 707
    Core::ICore::instance()->showOptionsDialog(
        QLatin1String(Constants::SETTINGS_CATEGORY),
        QLatin1String(Constants::SETTINGS_ID));
hjk's avatar
hjk committed
708 709
}

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

721
void FakeVimPluginPrivate::setActionChecked(const QString &code, bool check)
722 723 724 725 726 727 728 729 730 731 732 733
{
    Core::ActionManager *am = Core::ICore::instance()->actionManager();
    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
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
void FakeVimPluginPrivate::windowCommand(int key)
{
    #define control(n) (256 + n)
    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;
    }
    #undef control
hjk's avatar
hjk committed
760
    //qDebug() << "RUNNING WINDOW COMMAND: " << key << code;
hjk's avatar
hjk committed
761
    if (code.isEmpty()) {
hjk's avatar
hjk committed
762
        //qDebug() << "UNKNOWN WINDOWS COMMAND: " << key;
hjk's avatar
hjk committed
763 764
        return;
    }
765 766 767 768 769
    triggerAction(code);
}

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

778 779 780 781 782 783 784 785
void FakeVimPluginPrivate::findNext(bool reverse)
{
    if (reverse)
        triggerAction(Find::Constants::FIND_PREVIOUS);
    else
        triggerAction(Find::Constants::FIND_NEXT);
}

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

    FakeVimHandler *m_handler;

public:
    DeferredDeleter(QObject *parent, FakeVimHandler *handler)
795
        : QObject(parent), m_handler(handler)
796 797 798 799 800 801 802 803 804 805 806 807
    {}

    virtual ~DeferredDeleter()
    {
        if (m_handler) {
            m_handler->disconnectFromEditor();
            m_handler->deleteLater();
            m_handler = 0;
        }
    }
};

808
void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
809
{
810 811 812
    if (!editor)
        return;

813
    QWidget *widget = editor->widget();
814 815
    if (!widget)
        return;
816 817 818 819

    // we can only handle QTextEdit and QPlainTextEdit
    if (!qobject_cast<QTextEdit *>(widget) && !qobject_cast<QPlainTextEdit *>(widget))
        return;
820

821 822 823
    //qDebug() << "OPENING: " << editor << editor->widget()
    //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();

824 825 826 827
    FakeVimHandler *handler = new FakeVimHandler(widget, 0);
    // the handler might have triggered the deletion of the editor:
    // make sure that it can return before being deleted itself
    new DeferredDeleter(widget, handler);
828
    m_editorToHandler[editor] = handler;
829 830 831 832

    connect(handler, SIGNAL(extraInformationChanged(QString)),
        this, SLOT(showExtraInformation(QString)));
    connect(handler, SIGNAL(commandBufferChanged(QString)),
hjk's avatar
hjk committed
833
        this, SLOT(showCommandBuffer(QString)));
834 835
    connect(handler, SIGNAL(selectionChanged(QList<QTextEdit::ExtraSelection>)),
        this, SLOT(changeSelection(QList<QTextEdit::ExtraSelection>)));
836 837
    connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
        this, SLOT(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)));
838 839
    connect(handler, SIGNAL(indentRegion(int,int,QChar)),
        this, SLOT(indentRegion(int,int,QChar)));
840 841
    connect(handler, SIGNAL(checkForElectricCharacter(bool*,QChar)),
        this, SLOT(checkForElectricCharacter(bool*,QChar)));
842 843
    connect(handler, SIGNAL(completionRequested()),
        this, SLOT(triggerCompletions()));
hjk's avatar
hjk committed
844 845
    connect(handler, SIGNAL(windowCommandRequested(int)),
        this, SLOT(windowCommand(int)));