fakevimplugin.cpp 39.3 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
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
105
106
107
108
109

} // namespace Constants
} // namespace FakeVim


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

namespace FakeVim {
namespace Internal {

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

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

public:
    FakeVimOptionPage() {}

    // IOptionsPage
hjk's avatar
hjk committed
130
    QString id() const { return _(Constants::SETTINGS_ID); }
131
    QString displayName() const { return tr("General"); }
hjk's avatar
hjk committed
132
    QString category() const { return _(Constants::SETTINGS_CATEGORY); }
133
    QString displayCategory() const { return tr("FakeVim"); }
hjk's avatar
hjk committed
134
135
    QIcon categoryIcon() const
        { return QIcon(_(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);
hjk's avatar
hjk committed
241
    m_ui.lineEditBackspace->setText(_("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
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

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

hjk's avatar
hjk committed
278
Q_DECLARE_METATYPE(CommandItem *);
279
280
281
282

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

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

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

public slots:
    void commandChanged(QTreeWidgetItem *current);
304
305
306
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
307
308
309
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
    void switchToFile(int n);
    int currentFile() const;
517

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

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
524
    FakeVimOptionPage *m_fakeVimOptionsPage;
525
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
526
    QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
hjk's avatar
hjk committed
527
528
    QPointer<EditorManager> m_editorManager;
    EditorManager *editorManager() const { return m_editorManager; }
529

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

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

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

} // namespace Internal
} // namespace FakeVim

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

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

    q->removeObject(m_fakeVimExCommandsPage);
    delete m_fakeVimExCommandsPage;
    m_fakeVimExCommandsPage = 0;
574
575
576
577
578
}

void FakeVimPluginPrivate::aboutToShutdown()
{
    theFakeVimSettings()->writeSettings(Core::ICore::instance()->settings());
579
    writeSettings(Core::ICore::instance()->settings());
hjk's avatar
hjk committed
580
581
}

582
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
583
{
hjk's avatar
hjk committed
584
    m_editorManager = Core::ICore::instance()->editorManager();
hjk's avatar
hjk committed
585
    Core::ActionManager *actionManager = Core::ICore::instance()->actionManager();
hjk's avatar
hjk committed
586
587
    QTC_ASSERT(actionManager, return false);

588
    Core::Context globalcontext(Core::Constants::C_GLOBAL);
hjk's avatar
hjk committed
589

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

594
    m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
595
596
    q->addObject(m_fakeVimExCommandsPage);
    readSettings(Core::ICore::instance()->settings());
597

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

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

607
    // EditorManager
hjk's avatar
hjk committed
608
    connect(editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
609
        this, SLOT(editorAboutToClose(Core::IEditor*)));
hjk's avatar
hjk committed
610
    connect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
611
612
        this, SLOT(editorOpened(Core::IEditor*)));

613
614
    connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
        this, SLOT(setUseFakeVim(QVariant)));
615
616
    connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
        this, SLOT(maybeReadVimRc()));
hjk's avatar
hjk committed
617

618
    // Delayed operations.
619
620
621
622
    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);
623
624
    maybeReadVimRc();
    //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
625

hjk's avatar
hjk committed
626
627
628
    return true;
}

629
630
631
632
633
634
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
635
    settings->beginWriteArray(_(exCommandMapGroup));
636
637

    int count = 0;
638
639
640
    typedef CommandMap::const_iterator Iterator;
    const Iterator end = exCommandMap().constEnd();
    for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
641
642
643
        const QString &id = it.key();
        const QRegExp &re = it.value();

644
645
        if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
            || (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
646
            settings->setArrayIndex(count);
hjk's avatar
hjk committed
647
648
            settings->setValue(_(idKey), id);
            settings->setValue(_(reKey), re.pattern());
649
650
651
652
653
654
655
656
657
            ++count;
        }
    }

    settings->endArray();
}

void FakeVimPluginPrivate::readSettings(QSettings *settings)
{
658
    exCommandMap() = defaultExCommandMap();
659

hjk's avatar
hjk committed
660
    int size = settings->beginReadArray(_(exCommandMapGroup));
661
    for (int i = 0; i < size; ++i) {
662
        settings->setArrayIndex(i);
hjk's avatar
hjk committed
663
664
        const QString id = settings->value(_(idKey)).toString();
        const QString re = settings->value(_(reKey)).toString();
665
        exCommandMap()[id] = QRegExp(re);
666
667
668
669
    }
    settings->endArray();
}

670
671
void FakeVimPluginPrivate::maybeReadVimRc()
{
hjk's avatar
hjk committed
672
673
674
    //qDebug() << theFakeVimSetting(ConfigReadVimRc)
    //    << theFakeVimSetting(ConfigReadVimRc)->value();
    //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
675
676
677
678
679
680
681
682
683
684
685
    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
686
    //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
687
688
}

hjk's avatar
hjk committed
689
690
void FakeVimPluginPrivate::showSettingsDialog()
{
691
    Core::ICore::instance()->showOptionsDialog(
hjk's avatar
hjk committed
692
693
        _(Constants::SETTINGS_CATEGORY),
        _(Constants::SETTINGS_ID));
hjk's avatar
hjk committed
694
695
}

696
void FakeVimPluginPrivate::triggerAction(const QString &code)
697
698
699
700
{
    Core::ActionManager *am = Core::ICore::instance()->actionManager();
    QTC_ASSERT(am, return);
    Core::Command *cmd = am->command(code);
hjk's avatar
hjk committed
701
    QTC_ASSERT(cmd, qDebug() << "UNKNOW CODE: " << code; return);
702
703
704
705
706
    QAction *action = cmd->action();
    QTC_ASSERT(action, return);
    action->trigger();
}

707
void FakeVimPluginPrivate::setActionChecked(const QString &code, bool check)
708
709
710
711
712
713
714
715
716
717
718
719
{
    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
720
721
void FakeVimPluginPrivate::windowCommand(int key)
{
Tobias Hunger's avatar
Tobias Hunger committed
722
#    define control(n) (256 + n)
hjk's avatar
hjk committed
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
    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
745
#    undef control
hjk's avatar
hjk committed
746
    //qDebug() << "RUNNING WINDOW COMMAND: " << key << code;
hjk's avatar
hjk committed
747
    if (code.isEmpty()) {
hjk's avatar
hjk committed
748
        //qDebug() << "UNKNOWN WINDOWS COMMAND: " << key;
hjk's avatar
hjk committed
749
750
        return;
    }
751
752
753
754
755
    triggerAction(code);
}

void FakeVimPluginPrivate::find(bool reverse)
{
756
    if (Find::FindPlugin *plugin = Find::FindPlugin::instance()) {
757
758
        plugin->setUseFakeVim(true);
        plugin->openFindToolBar(reverse
759
760
                ? Find::FindPlugin::FindBackward
                : Find::FindPlugin::FindForward);
761
    }
hjk's avatar
hjk committed
762
763
}

764
765
766
767
768
769
770
771
void FakeVimPluginPrivate::findNext(bool reverse)
{
    if (reverse)
        triggerAction(Find::Constants::FIND_PREVIOUS);
    else
        triggerAction(Find::Constants::FIND_NEXT);
}

772
// This class defers deletion of a child FakeVimHandler using deleteLater().
773
774
775
776
777
778
779
780
class DeferredDeleter : public QObject
{
    Q_OBJECT

    FakeVimHandler *m_handler;

public:
    DeferredDeleter(QObject *parent, FakeVimHandler *handler)
781
        : QObject(parent), m_handler(handler)
782
783
784
785
786
787
788
789
790
791
792
793
    {}

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

794
void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
795
{
796
797
798
    if (!editor)
        return;

799
    QWidget *widget = editor->widget();
800
801
    if (!widget)
        return;
802
803
804
805

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

807
808
809
    //qDebug() << "OPENING: " << editor << editor->widget()
    //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();

810
811
812
813
    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);
814
    m_editorToHandler[editor] = handler;
815
816
817
818

    connect(handler, SIGNAL(extraInformationChanged(QString)),
        this, SLOT(showExtraInformation(QString)));
    connect(handler, SIGNAL(commandBufferChanged(QString)),
hjk's avatar
hjk committed
819
        this, SLOT(showCommandBuffer(QString)));
820
821
    connect(handler, SIGNAL(selectionChanged(QList<QTextEdit::ExtraSelection>)),
        this, SLOT(changeSelection(QList<QTextEdit::ExtraSelection>)));
822
823
    connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
        this, SLOT(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)));
824
825
    connect(handler, SIGNAL(indentRegion(int,int,QChar)),
        this, SLOT(indentRegion(int,int,QChar)));
826
827
    connect(handler, SIGNAL(checkForElectricCharacter(bool*,QChar)),
        this, SLOT(checkForElectricCharacter(bool*,QChar)));
828
829
    connect(handler, SIGNAL(completionRequested()),
        this, SLOT(triggerCompletions()));
hjk's avatar
hjk committed
830
831
    connect(handler, SIGNAL(windowCommandRequested(int)),
        this, SLOT(windowCommand(int)));
832
833
    connect(handler, SIGNAL(findRequested(bool)),
        this, SLOT(find(bool)));
834
835
    connect(handler, SIGNAL(findNextRequested(bool)),
        this, SLOT(findNext(bool)));
836

837
838
    connect(handler, SIGNAL(handleExCommandRequested(bool*,ExCommand)),
        this, SLOT(handleExCommand(bool*,ExCommand)));
839

hjk's avatar
hjk committed
840
    handler->setCurrentFileName(editor->file()->fileName());
841
    handler->installEventFilter();
842

843
    // pop up the bar
844
    if (theFakeVimSetting(ConfigUseFakeVim)->value().toBool()) {
hjk's avatar
hjk committed
845
       showCommandBuffer(QString());
846
847
       handler->setupWidget();
    }
hjk's avatar
hjk committed
848
849
}

850
void FakeVimPluginPrivate::editorAboutToClose(Core::IEditor *editor)
hjk's avatar
hjk committed
851
{
852
853
854
855
856
857
    //qDebug() << "CLOSING: " << editor << editor->widget();
    m_editorToHandler.remove(editor);
}

void FakeVimPluginPrivate::setUseFakeVim(const QVariant &value)
{
858
    //qDebug() << "SET USE FAKEVIM" << value;
859
    bool on = value.toBool();
860
861
    if (Find::FindPlugin::instance())
        Find::FindPlugin::instance()->setUseFakeVim(on);
862
    if (on) {
hjk's avatar
hjk committed
863
864
        editorManager()->showEditorStatusBar(
            _(Constants::MINI_BUFFER),
865
            "vi emulation mode. Type :q to leave. Use , Ctrl-R to trigger run.",
866
            tr("Quit FakeVim"), this, SLOT(quitFakeVim()));
867
868
869
        foreach (Core::IEditor *editor, m_editorToHandler.keys())
            m_editorToHandler[editor]->setupWidget();
    } else {
hjk's avatar
hjk committed
870
871
        editorManager()->hideEditorStatusBar(
            _(Constants::MINI_BUFFER));
872
873
        TextEditor::TabSettings ts =
            TextEditor::TextEditorSettings::instance()->tabSettings();
874
        foreach (Core::IEditor *editor, m_editorToHandler.keys())
875
            m_editorToHandler[editor]->restoreWidget(ts.m_tabSize);
876
    }
hjk's avatar
hjk committed
877
878
}

879
880
881
882
883
884
void FakeVimPluginPrivate::triggerCompletions()
{
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (!handler)
        return;
    if (BaseTextEditor *bt = qobject_cast<BaseTextEditor *>(handler->widget()))
hjk's avatar
hjk committed
885
886
887
        TextEditor::Internal::CompletionSupport::instance()->
            autoComplete(bt->editableInterface(), false);
   //     bt->triggerCompletions();
888
889
}

890
891
892
893
894
895
896
897
898
void FakeVimPluginPrivate::checkForElectricCharacter(bool *result, QChar c)
{
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (!handler)
        return;
    if (BaseTextEditor *bt = qobject_cast<BaseTextEditor *>(handler->widget()))
        *result = bt->isElectricCharacter(c);
}

899
void FakeVimPluginPrivate::handleExCommand(bool *handled, const ExCommand &cmd)
900
901
{
    using namespace Core;
902
    //qDebug() << "PLUGIN HANDLE: " << cmd.cmd << cmd.count;
903

904
    *handled = false;
905

hjk's avatar