OSDN Git Service

bfb601c9684007aa342b51b9a4b7b8a69ac8160f
[qt-creator-jp/qt-creator-jp.git] / src / plugins / fakevim / fakevimplugin.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "fakevimplugin.h"
35
36 #include "fakevimhandler.h"
37 #include "ui_fakevimoptions.h"
38
39 #include <coreplugin/actionmanager/actioncontainer.h>
40 #include <coreplugin/actionmanager/actionmanager.h>
41 #include <coreplugin/actionmanager/command.h>
42 #include <coreplugin/actionmanager/command.h>
43 #include <coreplugin/actionmanager/commandmappings.h>
44 #include <coreplugin/coreconstants.h>
45 #include <coreplugin/dialogs/ioptionspage.h>
46 #include <coreplugin/editormanager/editormanager.h>
47 #include <coreplugin/editormanager/openeditorsmodel.h>
48 #include <coreplugin/filemanager.h>
49 #include <coreplugin/icore.h>
50 #include <coreplugin/ifile.h>
51 #include <coreplugin/messagemanager.h>
52 #include <coreplugin/uniqueidmanager.h>
53 #include <coreplugin/statusbarwidget.h>
54 #include <coreplugin/statusbarmanager.h>
55
56 #include <projectexplorer/projectexplorerconstants.h>
57
58 #include <texteditor/basetextdocumentlayout.h>
59 #include <texteditor/basetexteditor.h>
60 #include <texteditor/basetextmark.h>
61 #include <texteditor/completionsupport.h>
62 #include <texteditor/texteditorconstants.h>
63 #include <texteditor/tabsettings.h>
64 #include <texteditor/texteditorsettings.h>
65 #include <texteditor/indenter.h>
66 #include <texteditor/icompletioncollector.h>
67
68 #include <find/findplugin.h>
69 #include <find/textfindconstants.h>
70
71 #include <utils/qtcassert.h>
72 #include <utils/savedaction.h>
73 #include <utils/treewidgetcolumnstretcher.h>
74
75 #include <cppeditor/cppeditorconstants.h>
76
77 #include <cpptools/cpptoolsconstants.h>
78
79 #include <QtCore/QDebug>
80 #include <QtCore/QFile>
81 #include <QtCore/QtPlugin>
82 #include <QtCore/QObject>
83 #include <QtCore/QSettings>
84 #include <QtCore/QTextStream>
85
86 #include <QtGui/QDesktopServices>
87 #include <QtGui/QMessageBox>
88 #include <QtGui/QPlainTextEdit>
89 #include <QtGui/QShortcut>
90 #include <QtGui/QTextBlock>
91 #include <QtGui/QTextCursor>
92 #include <QtGui/QTextEdit>
93 #include <QtGui/QTreeWidgetItem>
94
95 using namespace FakeVim::Internal;
96 using namespace TextEditor;
97 using namespace Core;
98
99 namespace FakeVim {
100 namespace Constants {
101
102 const char * const INSTALL_HANDLER                = "TextEditor.FakeVimHandler";
103 const char * const MINI_BUFFER                    = "TextEditor.FakeVimMiniBuffer";
104 const char * const INSTALL_KEY                    = "Alt+V,Alt+V";
105 const char * const SETTINGS_CATEGORY              = "D.FakeVim";
106 const char * const SETTINGS_CATEGORY_FAKEVIM_ICON = ":/core/images/category_fakevim.png";
107 const char * const SETTINGS_ID                    = "A.General";
108 const char * const SETTINGS_EX_CMDS_ID            = "B.ExCommands";
109
110 } // namespace Constants
111 } // namespace FakeVim
112
113
114 namespace FakeVim {
115 namespace Internal {
116
117 ///////////////////////////////////////////////////////////////////////
118 //
119 // FakeVimOptionPage
120 //
121 ///////////////////////////////////////////////////////////////////////
122
123 typedef QMap<QString, QRegExp> CommandMap;
124 typedef QLatin1String _;
125
126 class FakeVimOptionPage : public Core::IOptionsPage
127 {
128     Q_OBJECT
129
130 public:
131     FakeVimOptionPage() {}
132
133     // IOptionsPage
134     QString id() const { return _(Constants::SETTINGS_ID); }
135     QString displayName() const { return tr("General"); }
136     QString category() const { return _(Constants::SETTINGS_CATEGORY); }
137     QString displayCategory() const { return tr("FakeVim"); }
138     QIcon categoryIcon() const
139         { return QIcon(_(Constants::SETTINGS_CATEGORY_FAKEVIM_ICON)); }
140
141     QWidget *createPage(QWidget *parent);
142     void apply() { m_group.apply(ICore::instance()->settings()); }
143     void finish() { m_group.finish(); }
144     virtual bool matches(const QString &) const;
145
146 private slots:
147     void copyTextEditorSettings();
148     void setQtStyle();
149     void setPlainStyle();
150
151 private:
152     friend class DebuggerPlugin;
153     Ui::FakeVimOptionPage m_ui;
154     QString m_searchKeywords;
155     Utils::SavedActionSet m_group;
156 };
157
158 QWidget *FakeVimOptionPage::createPage(QWidget *parent)
159 {
160     QWidget *w = new QWidget(parent);
161     m_ui.setupUi(w);
162
163     m_group.clear();
164     m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
165         m_ui.checkBoxUseFakeVim);
166     m_group.insert(theFakeVimSetting(ConfigReadVimRc),
167         m_ui.checkBoxReadVimRc);
168
169     m_group.insert(theFakeVimSetting(ConfigExpandTab),
170         m_ui.checkBoxExpandTab);
171     m_group.insert(theFakeVimSetting(ConfigHlSearch),
172         m_ui.checkBoxHlSearch);
173     m_group.insert(theFakeVimSetting(ConfigShiftWidth),
174         m_ui.spinBoxShiftWidth);
175     m_group.insert(theFakeVimSetting(ConfigShowMarks),
176         m_ui.checkBoxShowMarks);
177
178     m_group.insert(theFakeVimSetting(ConfigSmartTab),
179         m_ui.checkBoxSmartTab);
180     m_group.insert(theFakeVimSetting(ConfigStartOfLine),
181         m_ui.checkBoxStartOfLine);
182     m_group.insert(theFakeVimSetting(ConfigTabStop),
183         m_ui.spinBoxTabStop);
184     m_group.insert(theFakeVimSetting(ConfigBackspace),
185         m_ui.lineEditBackspace);
186     m_group.insert(theFakeVimSetting(ConfigIsKeyword),
187         m_ui.lineEditIsKeyword);
188
189     m_group.insert(theFakeVimSetting(ConfigPassControlKey),
190         m_ui.checkBoxPassControlKey);
191     m_group.insert(theFakeVimSetting(ConfigAutoIndent),
192         m_ui.checkBoxAutoIndent);
193     m_group.insert(theFakeVimSetting(ConfigSmartIndent),
194         m_ui.checkBoxSmartIndent);
195     m_group.insert(theFakeVimSetting(ConfigIncSearch),
196         m_ui.checkBoxIncSearch);
197     m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
198         m_ui.checkBoxUseCoreSearch);
199
200     connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
201         SLOT(copyTextEditorSettings()));
202     connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
203         SLOT(setQtStyle()));
204     connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
205         SLOT(setPlainStyle()));
206
207     if (m_searchKeywords.isEmpty()) {
208         QLatin1Char sep(' ');
209         QTextStream(&m_searchKeywords)
210                 << sep << m_ui.checkBoxUseFakeVim->text()
211                 << sep << m_ui.checkBoxReadVimRc->text()
212                 << sep << m_ui.checkBoxAutoIndent->text()
213                 << sep << m_ui.checkBoxSmartIndent->text()
214                 << sep << m_ui.checkBoxExpandTab->text()
215                 << sep << m_ui.checkBoxSmartTab->text()
216                 << sep << m_ui.checkBoxHlSearch->text()
217                 << sep << m_ui.checkBoxIncSearch->text()
218                 << sep << m_ui.checkBoxStartOfLine->text()
219                 << sep << m_ui.checkBoxUseCoreSearch->text()
220                 << sep << m_ui.checkBoxShowMarks->text()
221                 << sep << m_ui.checkBoxPassControlKey->text()
222                 << sep << m_ui.labelShiftWidth->text()
223                 << sep << m_ui.labelTabulator->text()
224                 << sep << m_ui.labelBackspace->text()
225                 << sep << m_ui.labelIsKeyword->text();
226         m_searchKeywords.remove(QLatin1Char('&'));
227     }
228     return w;
229 }
230
231 void FakeVimOptionPage::copyTextEditorSettings()
232 {
233     TabSettings ts = TextEditorSettings::instance()->tabSettings();
234     m_ui.checkBoxExpandTab->setChecked(ts.m_spacesForTabs);
235     m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
236     m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
237     m_ui.checkBoxSmartTab->setChecked(ts.m_smartBackspace);
238     m_ui.checkBoxAutoIndent->setChecked(true);
239     m_ui.checkBoxSmartIndent->setChecked(ts.m_autoIndent);
240     m_ui.checkBoxIncSearch->setChecked(true);
241 }
242
243 void FakeVimOptionPage::setQtStyle()
244 {
245     m_ui.checkBoxExpandTab->setChecked(true);
246     m_ui.spinBoxTabStop->setValue(4);
247     m_ui.spinBoxShiftWidth->setValue(4);
248     m_ui.checkBoxSmartTab->setChecked(true);
249     m_ui.checkBoxAutoIndent->setChecked(true);
250     m_ui.checkBoxSmartIndent->setChecked(true);
251     m_ui.checkBoxIncSearch->setChecked(true);
252     m_ui.lineEditBackspace->setText(_("indent,eol,start"));
253 }
254
255 void FakeVimOptionPage::setPlainStyle()
256 {
257     m_ui.checkBoxExpandTab->setChecked(false);
258     m_ui.spinBoxTabStop->setValue(8);
259     m_ui.spinBoxShiftWidth->setValue(8);
260     m_ui.checkBoxSmartTab->setChecked(false);
261     m_ui.checkBoxAutoIndent->setChecked(false);
262     m_ui.checkBoxSmartIndent->setChecked(false);
263     m_ui.checkBoxIncSearch->setChecked(false);
264     m_ui.lineEditBackspace->setText(QString());
265 }
266
267 bool FakeVimOptionPage::matches(const QString &s) const
268 {
269     return m_searchKeywords.contains(s, Qt::CaseInsensitive);
270 }
271
272 //const char *FAKEVIM_CONTEXT = "FakeVim";
273
274 ///////////////////////////////////////////////////////////////////////
275 //
276 // FakeVimExCommandsPage
277 //
278 ///////////////////////////////////////////////////////////////////////
279
280 enum { CommandRole = Qt::UserRole };
281
282 class FakeVimExCommandsPage : public Core::CommandMappings
283 {
284     Q_OBJECT
285
286 public:
287     FakeVimExCommandsPage(FakeVimPluginPrivate *q) : m_q(q) {}
288     ~FakeVimExCommandsPage() {}
289
290     // IOptionsPage
291     QString id() const { return _(Constants::SETTINGS_EX_CMDS_ID); }
292     QString displayName() const { return tr("Ex Command Mapping"); }
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     QWidget *createPage(QWidget *parent);
298     void initialize();
299     CommandMap &exCommandMap();
300     CommandMap &defaultExCommandMap();
301
302 public slots:
303     void commandChanged(QTreeWidgetItem *current);
304     void targetIdentifierChanged();
305     void resetTargetIdentifier();
306     void removeTargetIdentifier();
307     void defaultAction();
308
309 private:
310     //QList<QTreeWidgetItem *> m_citems;
311     FakeVimPluginPrivate *m_q;
312 };
313
314 QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
315 {
316     QWidget *w = CommandMappings::createPage(parent);
317     setPageTitle(tr("Ex Command Mapping"));
318     setTargetHeader(tr("Ex Trigger Expression"));
319     setTargetLabelText(tr("Regular expression:"));
320     setTargetEditTitle(tr("Ex Command"));
321     setImportExportEnabled(false);
322     return w;
323 }
324
325 void FakeVimExCommandsPage::initialize()
326 {
327     ActionManager *am = ICore::instance()->actionManager();
328     QTC_ASSERT(am, return);
329     UniqueIDManager *uidm = UniqueIDManager::instance();
330     QTC_ASSERT(uidm, return);
331
332     QMap<QString, QTreeWidgetItem *> sections;
333
334     foreach (Command *c, am->commands()) {
335         if (c->action() && c->action()->isSeparator())
336             continue;
337
338         QTreeWidgetItem *item = new QTreeWidgetItem;
339         item->setData(0, CommandRole, int(c->id()));
340         //m_citems.append(item);
341
342         const QString name = uidm->stringForUniqueIdentifier(c->id());
343         const int pos = name.indexOf(QLatin1Char('.'));
344         const QString section = name.left(pos);
345         const QString subId = name.mid(pos + 1);
346
347         if (!sections.contains(section)) {
348             QTreeWidgetItem *categoryItem =
349                 new QTreeWidgetItem(commandList(), QStringList() << section);
350             QFont f = categoryItem->font(0);
351             f.setBold(true);
352             categoryItem->setFont(0, f);
353             sections.insert(section, categoryItem);
354             commandList()->expandItem(categoryItem);
355         }
356         sections[section]->addChild(item);
357
358         item->setText(0, subId);
359
360         if (c->action()) {
361             QString text = c->hasAttribute(Command::CA_UpdateText)
362                     && !c->defaultText().isNull()
363                 ? c->defaultText() : c->action()->text();
364             text.remove(QRegExp("&(?!&)"));
365             item->setText(1, text);
366         } else {
367             item->setText(1, c->shortcut()->whatsThis());
368         }
369
370         QString regex;
371         if (exCommandMap().contains(name))
372             regex = exCommandMap()[name].pattern();
373         item->setText(2, regex);
374
375         if (regex != defaultExCommandMap()[name].pattern())
376             setModified(item, true);
377     }
378
379     commandChanged(0);
380 }
381
382 void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
383 {
384     CommandMappings::commandChanged(current);
385     if (current)
386         targetEdit()->setText(current->text(2));
387 }
388
389 void FakeVimExCommandsPage::targetIdentifierChanged()
390 {
391     QTreeWidgetItem *current = commandList()->currentItem();
392     if (!current)
393         return;
394
395     UniqueIDManager *uidm = UniqueIDManager::instance();
396     int id = current->data(0, CommandRole).toInt();
397     const QString name = uidm->stringForUniqueIdentifier(id);
398     const QString regex = targetEdit()->text();
399
400     if (current->data(0, Qt::UserRole).isValid()) {
401         current->setText(2, regex);
402         exCommandMap()[name] = QRegExp(regex);
403     }
404
405     if (regex != defaultExCommandMap()[name].pattern())
406         setModified(current, true);
407     else
408         setModified(current, false);
409
410 }
411
412 void FakeVimExCommandsPage::resetTargetIdentifier()
413 {
414     QTreeWidgetItem *current = commandList()->currentItem();
415     if (!current)
416         return;
417     UniqueIDManager *uidm = UniqueIDManager::instance();
418     int id = current->data(0, CommandRole).toInt();
419     const QString name = uidm->stringForUniqueIdentifier(id);
420     QString regex;
421     if (defaultExCommandMap().contains(name))
422         regex = defaultExCommandMap()[name].pattern();
423     targetEdit()->setText(regex);
424 }
425
426 void FakeVimExCommandsPage::removeTargetIdentifier()
427 {
428     targetEdit()->clear();
429 }
430
431 void FakeVimExCommandsPage::defaultAction()
432 {
433     UniqueIDManager *uidm = UniqueIDManager::instance();
434     int n = commandList()->topLevelItemCount();
435     for (int i = 0; i != n; ++i) {
436         QTreeWidgetItem *section = commandList()->topLevelItem(i);
437         int m = section->childCount();
438         for (int j = 0; j != m; ++j) {
439             QTreeWidgetItem *item = section->child(j);
440             const int id = item->data(0, CommandRole).toInt();
441             const QString name = uidm->stringForUniqueIdentifier(id);
442             QString regex;
443             if (defaultExCommandMap().contains(name))
444                 regex = defaultExCommandMap()[name].pattern();
445             setModified(item, false);
446             item->setText(2, regex);
447             if (item == commandList()->currentItem())
448                 commandChanged(item);
449         }
450     }
451 }
452
453
454 ///////////////////////////////////////////////////////////////////////
455 //
456 // WordCompletion
457 //
458 ///////////////////////////////////////////////////////////////////////
459
460 class WordCompletion : public ICompletionCollector
461 {
462     Q_OBJECT
463
464 public:
465     WordCompletion()
466     {
467         m_editable = 0;
468         m_editor = 0;
469     }
470
471     virtual bool shouldRestartCompletion()
472     {
473         //qDebug() << "SHOULD RESTART COMPLETION?";
474         return false;
475     }
476
477     virtual ITextEditor *editor() const
478     {
479         //qDebug() << "NO EDITOR?";
480         return m_editable;
481     }
482
483     virtual int startPosition() const
484     {
485         return m_startPosition;
486     }
487
488     virtual bool supportsEditor(ITextEditor *) const
489     {
490         return true;
491     }
492
493     virtual bool supportsPolicy(CompletionPolicy policy) const
494     {
495         return policy == TextCompletion;
496     }
497
498     virtual bool triggersCompletion(ITextEditor *editable)
499     {
500         //qDebug() << "TRIGGERS?";
501         QTC_ASSERT(m_editable == editable, /**/);
502         return true;
503     }
504
505     virtual int startCompletion(ITextEditor *editable)
506     {
507         //qDebug() << "START COMPLETION";
508         QTC_ASSERT(m_editor, return -1);
509         QTC_ASSERT(m_editable == editable, return -1);
510         return m_editor->textCursor().position();
511     }
512
513     void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
514     {
515         Q_UNUSED(forward);
516         m_handler = handler;
517         if (!m_handler)
518             return;
519         m_editor = qobject_cast<BaseTextEditorWidget *>(handler->widget());
520         if (!m_editor)
521             return;
522         //qDebug() << "ACTIVATE: " << needle << forward;
523         m_needle = needle;
524         m_editable = m_editor->editor();
525         m_startPosition = m_editor->textCursor().position() - needle.size();
526
527         CompletionSupport::instance()->complete(m_editable, TextCompletion, false);
528     }
529
530     void setInactive()
531     {
532         m_needle.clear();
533         m_editable = 0;
534         m_editor = 0;
535         m_handler = 0;
536         m_startPosition = -1;
537     }
538
539     virtual void completions(QList<CompletionItem> *completions)
540     {
541         QTC_ASSERT(m_editor, return);
542         QTC_ASSERT(completions, return);
543         QTextCursor tc = m_editor->textCursor();
544         tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
545
546         QSet<QString> seen;
547
548         QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
549         while (1) {
550             tc = tc.document()->find(m_needle, tc.position(), flags);
551             if (tc.isNull())
552                 break;
553             QTextCursor sel = tc;
554             sel.select(QTextCursor::WordUnderCursor);
555             QString found = sel.selectedText();
556             // Only add "real" completions.
557             if (found.startsWith(m_needle)
558                     && !seen.contains(found)
559                     && sel.anchor() != m_startPosition) {
560                 seen.insert(found);
561                 CompletionItem item;
562                 item.collector = this;
563                 item.text = found;
564                 completions->append(item);
565             }
566             tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
567         }
568         //qDebug() << "COMPLETIONS" << completions->size();
569     }
570
571     virtual bool typedCharCompletes(const CompletionItem &item, QChar typedChar)
572     {
573         m_needle += typedChar;
574         //qDebug() << "COMPLETE? " << typedChar << item.text << m_needle;
575         return item.text == m_needle;
576     }
577
578     virtual void complete(const CompletionItem &item, QChar typedChar)
579     {
580         Q_UNUSED(typedChar);
581         //qDebug() << "COMPLETE: " << item.text;
582         QTC_ASSERT(m_handler, return);
583         m_handler->handleReplay(item.text.mid(m_needle.size()));
584         setInactive();
585     }
586
587     virtual bool partiallyComplete(const QList<CompletionItem> &completionItems)
588     {
589         //qDebug() << "PARTIALLY";
590         Q_UNUSED(completionItems);
591         return false;
592     }
593
594     virtual void cleanup() {}
595
596 private:
597     int findStartOfName(int pos = -1) const;
598     bool isInComment() const;
599
600     FakeVimHandler *m_handler;
601     BaseTextEditorWidget *m_editor;
602     ITextEditor *m_editable;
603     QString m_needle;
604     QString m_currentPrefix;
605     QList<CompletionItem> m_items;
606     int m_startPosition;
607 };
608
609
610 ///////////////////////////////////////////////////////////////////////
611 //
612 // FakeVimPluginPrivate
613 //
614 ///////////////////////////////////////////////////////////////////////
615
616 class FakeVimPluginPrivate : public QObject
617 {
618     Q_OBJECT
619
620 public:
621     FakeVimPluginPrivate(FakeVimPlugin *);
622     ~FakeVimPluginPrivate();
623     friend class FakeVimPlugin;
624     friend class FakeVimExCommandsPage;
625
626     bool initialize();
627     void aboutToShutdown();
628
629 private slots:
630     void onCoreAboutToClose();
631     void editorOpened(Core::IEditor *);
632     void editorAboutToClose(Core::IEditor *);
633
634     void setUseFakeVim(const QVariant &value);
635     void quitFakeVim();
636     void triggerCompletions();
637     void triggerSimpleCompletions(const QString &needle, bool forward);
638     void windowCommand(int key);
639     void find(bool reverse);
640     void findNext(bool reverse);
641     void showSettingsDialog();
642     void maybeReadVimRc();
643     void setBlockSelection(bool);
644     void hasBlockSelection(bool*);
645
646     void showCommandBuffer(const QString &contents);
647     void showExtraInformation(const QString &msg);
648     void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
649     void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
650     void checkForElectricCharacter(bool *result, QChar c);
651     void indentRegion(int beginLine, int endLine, QChar typedChar);
652     void handleExCommand(bool *handled, const ExCommand &cmd);
653
654     void writeSettings();
655     void readSettings();
656
657     void handleDelayedQuitAll(bool forced);
658     void handleDelayedQuit(bool forced, Core::IEditor *editor);
659
660     void switchToFile(int n);
661     int currentFile() const;
662
663 signals:
664     void delayedQuitRequested(bool forced, Core::IEditor *editor);
665     void delayedQuitAllRequested(bool forced);
666
667 private:
668     FakeVimPlugin *q;
669     FakeVimOptionPage *m_fakeVimOptionsPage;
670     FakeVimExCommandsPage *m_fakeVimExCommandsPage;
671     QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
672     QPointer<Core::ICore> m_core;
673     QPointer<Core::EditorManager> m_editorManager;
674     QPointer<Core::ActionManager> m_actionManager;
675     ICore *core() const { return m_core; }
676     EditorManager *editorManager() const { return m_editorManager; }
677     ActionManager *actionManager() const { return m_actionManager; }
678
679     void triggerAction(const QString &code);
680     void setActionChecked(const QString &code, bool check);
681
682     typedef int (*DistFunction)(const QRect &cursor, const QRect &other);
683     void moveSomewhere(DistFunction f);
684
685     CommandMap &exCommandMap() { return m_exCommandMap; }
686     CommandMap &defaultExCommandMap() { return m_defaultExCommandMap; }
687     CommandMap m_exCommandMap;
688     CommandMap m_defaultExCommandMap;
689     Core::StatusBarWidget *m_statusBar;
690     WordCompletion *m_wordCompletion;
691 };
692
693 FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
694 {
695     q = plugin;
696     m_fakeVimOptionsPage = 0;
697     m_fakeVimExCommandsPage = 0;
698     defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
699         QRegExp("^A$");
700     defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
701         QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
702     defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
703         QRegExp("^cn(ext)?!?( (.*))?$");
704     defaultExCommandMap()[CppEditor::Constants::JUMP_TO_DEFINITION] =
705         QRegExp("^tag?$");
706     defaultExCommandMap()[Core::Constants::GO_BACK] =
707         QRegExp("^pop?$");
708     defaultExCommandMap()[_("QtCreator.Locate")] =
709         QRegExp("^e$");
710     m_statusBar = 0;
711 }
712
713 FakeVimPluginPrivate::~FakeVimPluginPrivate()
714 {
715     q->removeObject(m_fakeVimOptionsPage);
716     delete m_fakeVimOptionsPage;
717     m_fakeVimOptionsPage = 0;
718     delete theFakeVimSettings();
719
720     q->removeObject(m_fakeVimExCommandsPage);
721     delete m_fakeVimExCommandsPage;
722     m_fakeVimExCommandsPage = 0;
723 }
724
725 void FakeVimPluginPrivate::onCoreAboutToClose()
726 {
727     // don't attach to editors any more
728     disconnect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
729         this, SLOT(editorOpened(Core::IEditor*)));
730 }
731
732 void FakeVimPluginPrivate::aboutToShutdown()
733 {
734 }
735
736 bool FakeVimPluginPrivate::initialize()
737 {
738     m_core = Core::ICore::instance();
739     m_editorManager = core()->editorManager();
740     m_actionManager = core()->actionManager();
741     QTC_ASSERT(actionManager(), return false);
742
743     m_wordCompletion = new WordCompletion;
744     q->addAutoReleasedObject(m_wordCompletion);
745 /*
746     // Set completion settings and keep them up to date.
747     TextEditorSettings *textEditorSettings = TextEditorSettings::instance();
748     completion->setCompletionSettings(textEditorSettings->completionSettings());
749     connect(textEditorSettings,
750         SIGNAL(completionSettingsChanged(TextEditor::CompletionSettings)),
751         completion,
752         SLOT(setCompletionSettings(TextEditor::CompletionSettings)));
753 */
754
755     Context globalcontext(Core::Constants::C_GLOBAL);
756
757     m_fakeVimOptionsPage = new FakeVimOptionPage;
758     q->addObject(m_fakeVimOptionsPage);
759
760     m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
761     q->addObject(m_fakeVimExCommandsPage);
762     readSettings();
763
764     Core::Command *cmd = 0;
765     cmd = actionManager()->registerAction(theFakeVimSetting(ConfigUseFakeVim),
766         Constants::INSTALL_HANDLER, globalcontext);
767     cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));
768
769     ActionContainer *advancedMenu =
770         actionManager()->actionContainer(Core::Constants::M_EDIT_ADVANCED);
771     advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
772
773     connect(m_core, SIGNAL(coreAboutToClose()), this, SLOT(onCoreAboutToClose()));
774
775     // EditorManager
776     connect(editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
777         this, SLOT(editorAboutToClose(Core::IEditor*)));
778     connect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
779         this, SLOT(editorOpened(Core::IEditor*)));
780
781     connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
782         this, SLOT(setUseFakeVim(QVariant)));
783     connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
784         this, SLOT(maybeReadVimRc()));
785
786     // Delayed operations.
787     connect(this, SIGNAL(delayedQuitRequested(bool,Core::IEditor*)),
788         this, SLOT(handleDelayedQuit(bool,Core::IEditor*)), Qt::QueuedConnection);
789     connect(this, SIGNAL(delayedQuitAllRequested(bool)),
790         this, SLOT(handleDelayedQuitAll(bool)), Qt::QueuedConnection);
791     maybeReadVimRc();
792     //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
793
794     return true;
795 }
796
797 static const char *exCommandMapGroup = "FakeVimExCommand";
798 static const char *reKey = "RegEx";
799 static const char *idKey = "Command";
800
801 void FakeVimPluginPrivate::writeSettings()
802 {
803     QSettings *settings = ICore::instance()->settings();
804
805     theFakeVimSettings()->writeSettings(settings);
806
807     settings->beginWriteArray(_(exCommandMapGroup));
808     int count = 0;
809     typedef CommandMap::const_iterator Iterator;
810     const Iterator end = exCommandMap().constEnd();
811     for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
812         const QString id = it.key();
813         const QRegExp re = it.value();
814
815         if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
816             || (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
817             settings->setArrayIndex(count);
818             settings->setValue(_(idKey), id);
819             settings->setValue(_(reKey), re.pattern());
820             ++count;
821         }
822     }
823
824     settings->endArray();
825 }
826
827 void FakeVimPluginPrivate::readSettings()
828 {
829     QSettings *settings = ICore::instance()->settings();
830
831     theFakeVimSettings()->readSettings(settings);
832
833     exCommandMap() = defaultExCommandMap();
834     int size = settings->beginReadArray(_(exCommandMapGroup));
835     for (int i = 0; i < size; ++i) {
836         settings->setArrayIndex(i);
837         const QString id = settings->value(_(idKey)).toString();
838         const QString re = settings->value(_(reKey)).toString();
839         exCommandMap()[id] = QRegExp(re);
840     }
841     settings->endArray();
842 }
843
844 void FakeVimPluginPrivate::maybeReadVimRc()
845 {
846     //qDebug() << theFakeVimSetting(ConfigReadVimRc)
847     //    << theFakeVimSetting(ConfigReadVimRc)->value();
848     //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
849     if (!theFakeVimSetting(ConfigReadVimRc)->value().toBool())
850         return;
851     QString fileName =
852         QDesktopServices::storageLocation(QDesktopServices::HomeLocation)
853             + "/.vimrc";
854     //qDebug() << "READING VIMRC: " << fileName;
855     // Read it into a temporary handler for effects modifying global state.
856     QPlainTextEdit editor;
857     FakeVimHandler handler(&editor);
858     handler.handleCommand("source " + fileName);
859     //writeSettings();
860     //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
861 }
862
863 void FakeVimPluginPrivate::showSettingsDialog()
864 {
865     core()->showOptionsDialog(
866         _(Constants::SETTINGS_CATEGORY),
867         _(Constants::SETTINGS_ID));
868 }
869
870 void FakeVimPluginPrivate::triggerAction(const QString &code)
871 {
872     Core::ActionManager *am = actionManager();
873     QTC_ASSERT(am, return);
874     Core::Command *cmd = am->command(code);
875     QTC_ASSERT(cmd, qDebug() << "UNKNOWN CODE: " << code; return);
876     QAction *action = cmd->action();
877     QTC_ASSERT(action, return);
878     action->trigger();
879 }
880
881 void FakeVimPluginPrivate::setActionChecked(const QString &code, bool check)
882 {
883     Core::ActionManager *am = actionManager();
884     QTC_ASSERT(am, return);
885     Core::Command *cmd = am->command(code);
886     QTC_ASSERT(cmd, return);
887     QAction *action = cmd->action();
888     QTC_ASSERT(action, return);
889     QTC_ASSERT(action->isCheckable(), return);
890     action->setChecked(!check); // trigger negates the action's state
891     action->trigger();
892 }
893
894 static int moveRightWeight(const QRect &cursor, const QRect &other)
895 {
896     int dx = other.left() - cursor.right();
897     if (dx < 0)
898         return -1;
899     int w = 10000 * dx;
900     int dy1 = cursor.top() - other.bottom();
901     int dy2 = cursor.bottom() - other.top();
902     w += dy1 * (dy1 > 0);
903     w += dy2 * (dy2 > 0);
904     qDebug() << "      DX: " << dx << dy1 << dy2 << w;
905     return w;
906 }
907
908 static int moveLeftWeight(const QRect &cursor, const QRect &other)
909 {
910     int dx = other.right() - cursor.left();
911     if (dx < 0)
912         return -1;
913     int w = 10000 * dx;
914     int dy1 = cursor.top() - other.bottom();
915     int dy2 = cursor.bottom() - other.top();
916     w += dy1 * (dy1 > 0);
917     w += dy2 * (dy2 > 0);
918     return w;
919 }
920
921 static int moveUpWeight(const QRect &cursor, const QRect &other)
922 {
923     int dy = other.bottom() - cursor.top();
924     if (dy < 0)
925         return -1;
926     int w = 10000 * dy;
927     int dx1 = cursor.left() - other.right();
928     int dx2 = cursor.right() - other.left();
929     w += dx1 * (dx1 > 0);
930     w += dx2 * (dx2 > 0);
931     return w;
932 }
933
934 static int moveDownWeight(const QRect &cursor, const QRect &other)
935 {
936     int dy = other.top() - cursor.bottom();
937     if (dy < 0)
938         return -1;
939     int w = 10000 * dy;
940     int dx1 = cursor.left() - other.right();
941     int dx2 = cursor.right() - other.left();
942     w += dx1 * (dx1 > 0);
943     w += dx2 * (dx2 > 0);
944     return w;
945 }
946
947 void FakeVimPluginPrivate::windowCommand(int key)
948 {
949 #    define control(n) (256 + n)
950     switch (key) {
951         case 'c': case 'C': case control('c'):
952             triggerAction(Core::Constants::CLOSE);
953             break;
954         case 'n': case 'N': case control('n'):
955             triggerAction(Core::Constants::GOTONEXT);
956             break;
957         case 'o': case 'O': case control('o'):
958             //triggerAction(Core::Constants::REMOVE_ALL_SPLITS);
959             triggerAction(Core::Constants::REMOVE_CURRENT_SPLIT);
960             break;
961         case 'p': case 'P': case control('p'):
962             triggerAction(Core::Constants::GOTOPREV);
963             break;
964         case 's': case 'S': case control('s'):
965             triggerAction(Core::Constants::SPLIT);
966             break;
967         case 'w': case 'W': case control('w'):
968             triggerAction(Core::Constants::GOTO_OTHER_SPLIT);
969             break;
970         case Qt::Key_Right:
971             moveSomewhere(&moveRightWeight);
972             break;
973         case Qt::Key_Left:
974             moveSomewhere(&moveLeftWeight);
975             break;
976         case Qt::Key_Up:
977             moveSomewhere(&moveUpWeight);
978             break;
979         case Qt::Key_Down:
980             moveSomewhere(&moveDownWeight);
981             break;
982         default:
983             qDebug() << "UNKNOWN WINDOWS COMMAND: " << key;
984             break;
985     }
986 #    undef control
987 }
988
989 void FakeVimPluginPrivate::moveSomewhere(DistFunction f)
990 {
991     IEditor *editor = editorManager()->currentEditor();
992     QWidget *w = editor->widget();
993     QPlainTextEdit *pe =
994         qobject_cast<QPlainTextEdit *>(editor->widget());
995     QTC_ASSERT(pe, return);
996     QRect rc = pe->cursorRect();
997     QRect cursorRect(w->mapToGlobal(rc.topLeft()),
998             w->mapToGlobal(rc.bottomRight()));
999     //qDebug() << "\nCURSOR: " << cursorRect;
1000
1001     IEditor *bestEditor = 0;
1002     int bestValue = 1 << 30;
1003
1004     QList<IEditor*> editors = editorManager()->visibleEditors();
1005     foreach (IEditor *editor, editors) {
1006         QWidget *w = editor->widget();
1007         QRect editorRect(w->mapToGlobal(w->geometry().topLeft()),
1008                 w->mapToGlobal(w->geometry().bottomRight()));
1009         //qDebug() << "   EDITOR: " << editorRect << editor;
1010
1011         int value = f(cursorRect, editorRect);
1012         if (value != -1 && value < bestValue) {
1013             bestValue = value;
1014             bestEditor = editor;
1015             //qDebug() << "          BEST SO FAR: " << bestValue << bestEditor;
1016         }
1017     }
1018     //qDebug() << "     BEST: " << bestValue << bestEditor;
1019
1020     // FIME: This is know to fail as the EditorManager will fall back to
1021     // the current editor's view. Needs additional public API there.
1022     if (bestEditor)
1023         editorManager()->activateEditor(bestEditor);
1024 }
1025
1026 void FakeVimPluginPrivate::find(bool reverse)
1027 {
1028     if (Find::FindPlugin *plugin = Find::FindPlugin::instance()) {
1029         plugin->setUseFakeVim(true);
1030         plugin->openFindToolBar(reverse
1031                 ? Find::FindPlugin::FindBackward
1032                 : Find::FindPlugin::FindForward);
1033     }
1034 }
1035
1036 void FakeVimPluginPrivate::findNext(bool reverse)
1037 {
1038     if (reverse)
1039         triggerAction(Find::Constants::FIND_PREVIOUS);
1040     else
1041         triggerAction(Find::Constants::FIND_NEXT);
1042 }
1043
1044 // This class defers deletion of a child FakeVimHandler using deleteLater().
1045 class DeferredDeleter : public QObject
1046 {
1047     Q_OBJECT
1048
1049     FakeVimHandler *m_handler;
1050
1051 public:
1052     DeferredDeleter(QObject *parent, FakeVimHandler *handler)
1053         : QObject(parent), m_handler(handler)
1054     {}
1055
1056     virtual ~DeferredDeleter()
1057     {
1058         if (m_handler) {
1059             m_handler->disconnectFromEditor();
1060             m_handler->deleteLater();
1061             m_handler = 0;
1062         }
1063     }
1064 };
1065
1066 void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
1067 {
1068     if (!editor)
1069         return;
1070
1071     QWidget *widget = editor->widget();
1072     if (!widget)
1073         return;
1074
1075     // we can only handle QTextEdit and QPlainTextEdit
1076     if (!qobject_cast<QTextEdit *>(widget) && !qobject_cast<QPlainTextEdit *>(widget))
1077         return;
1078
1079     //qDebug() << "OPENING: " << editor << editor->widget()
1080     //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
1081
1082     FakeVimHandler *handler = new FakeVimHandler(widget, 0);
1083     // the handler might have triggered the deletion of the editor:
1084     // make sure that it can return before being deleted itself
1085     new DeferredDeleter(widget, handler);
1086     m_editorToHandler[editor] = handler;
1087
1088     connect(handler, SIGNAL(extraInformationChanged(QString)),
1089         SLOT(showExtraInformation(QString)));
1090     connect(handler, SIGNAL(commandBufferChanged(QString)),
1091         SLOT(showCommandBuffer(QString)));
1092     connect(handler, SIGNAL(selectionChanged(QList<QTextEdit::ExtraSelection>)),
1093         SLOT(changeSelection(QList<QTextEdit::ExtraSelection>)));
1094     connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
1095         SLOT(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)));
1096     connect(handler, SIGNAL(indentRegion(int,int,QChar)),
1097         SLOT(indentRegion(int,int,QChar)));
1098     connect(handler, SIGNAL(checkForElectricCharacter(bool*,QChar)),
1099         SLOT(checkForElectricCharacter(bool*,QChar)));
1100     connect(handler, SIGNAL(requestSetBlockSelection(bool)),
1101         SLOT(setBlockSelection(bool)));
1102     connect(handler, SIGNAL(requestHasBlockSelection(bool*)),
1103         SLOT(hasBlockSelection(bool*)));
1104     connect(handler, SIGNAL(completionRequested()),
1105         SLOT(triggerCompletions()));
1106     connect(handler, SIGNAL(simpleCompletionRequested(QString,bool)),
1107         SLOT(triggerSimpleCompletions(QString,bool)));
1108     connect(handler, SIGNAL(windowCommandRequested(int)),
1109         SLOT(windowCommand(int)));
1110     connect(handler, SIGNAL(findRequested(bool)),
1111         SLOT(find(bool)));
1112     connect(handler, SIGNAL(findNextRequested(bool)),
1113         SLOT(findNext(bool)));
1114
1115     connect(handler, SIGNAL(handleExCommandRequested(bool*,ExCommand)),
1116         SLOT(handleExCommand(bool*,ExCommand)));
1117
1118     connect(core(), SIGNAL(saveSettingsRequested()),
1119         SLOT(writeSettings()));
1120
1121     handler->setCurrentFileName(editor->file()->fileName());
1122     handler->installEventFilter();
1123
1124     // pop up the bar
1125     if (theFakeVimSetting(ConfigUseFakeVim)->value().toBool()) {
1126        showCommandBuffer(QString());
1127        handler->setupWidget();
1128     }
1129 }
1130
1131 void FakeVimPluginPrivate::editorAboutToClose(Core::IEditor *editor)
1132 {
1133     //qDebug() << "CLOSING: " << editor << editor->widget();
1134     m_editorToHandler.remove(editor);
1135 }
1136
1137 void FakeVimPluginPrivate::setUseFakeVim(const QVariant &value)
1138 {
1139     //qDebug() << "SET USE FAKEVIM" << value;
1140     bool on = value.toBool();
1141     if (Find::FindPlugin::instance())
1142         Find::FindPlugin::instance()->setUseFakeVim(on);
1143     if (on) {
1144         //ICore *core = ICore::instance();
1145         //core->updateAdditionalContexts(Core::Context(FAKEVIM_CONTEXT),
1146         // Core::Context());
1147         foreach (Core::IEditor *editor, m_editorToHandler.keys())
1148             m_editorToHandler[editor]->setupWidget();
1149     } else {
1150         //ICore *core = ICore::instance();
1151         //core->updateAdditionalContexts(Core::Context(),
1152         // Core::Context(FAKEVIM_CONTEXT));
1153         showCommandBuffer(QString());
1154         foreach (Core::IEditor *editor, m_editorToHandler.keys()) {
1155             if (TextEditor::BaseTextEditorWidget *textEditor =
1156                     qobject_cast<TextEditor::BaseTextEditorWidget *>(editor->widget())) {
1157                 m_editorToHandler[editor]->restoreWidget(textEditor->tabSettings().m_tabSize);
1158             }
1159         }
1160     }
1161 }
1162
1163 void FakeVimPluginPrivate::triggerCompletions()
1164 {
1165     FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
1166     if (!handler)
1167         return;
1168     if (BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(handler->widget()))
1169         CompletionSupport::instance()->
1170             complete(editor->editor(), TextCompletion, false);
1171    //     editor->triggerCompletions();
1172 }
1173
1174 void FakeVimPluginPrivate::triggerSimpleCompletions(const QString &needle,
1175    bool forward)
1176 {
1177     m_wordCompletion->setActive(needle, forward,
1178         qobject_cast<FakeVimHandler *>(sender()));
1179 }
1180
1181 void FakeVimPluginPrivate::setBlockSelection(bool on)
1182 {
1183     FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
1184     if (!handler)
1185         return;
1186     if (BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget()))
1187         bt->setBlockSelection(on);
1188 }
1189
1190 void FakeVimPluginPrivate::hasBlockSelection(bool *on)
1191 {
1192     FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
1193     if (!handler)
1194         return;
1195     if (BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget()))
1196         *on = bt->hasBlockSelection();
1197 }
1198
1199 void FakeVimPluginPrivate::checkForElectricCharacter(bool *result, QChar c)
1200 {
1201     FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
1202     if (!handler)
1203         return;
1204     if (BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget()))
1205         *result = bt->indenter()->isElectricCharacter(c);
1206 }
1207
1208 void FakeVimPluginPrivate::handleExCommand(bool *handled, const ExCommand &cmd)
1209 {
1210     using namespace Core;
1211     //qDebug() << "PLUGIN HANDLE: " << cmd.cmd << cmd.count;
1212
1213     *handled = false;
1214
1215     FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
1216     if (!handler)
1217         return;
1218
1219     QTC_ASSERT(editorManager(), return);
1220
1221     *handled = true;
1222     if (cmd.matches("w", "write") || cmd.cmd == "wq") {
1223         // :w[rite]
1224         Core::IEditor *editor = m_editorToHandler.key(handler);
1225         const QString fileName = handler->currentFileName();
1226         if (editor && editor->file()->fileName() == fileName) {
1227             // Handle that as a special case for nicer interaction with core
1228             Core::IFile *file = editor->file();
1229             Core::ICore::instance()->fileManager()->blockFileChange(file);
1230             file->save(fileName);
1231             Core::ICore::instance()->fileManager()->unblockFileChange(file);
1232             // Check result by reading back.
1233             QFile file3(fileName);
1234             file3.open(QIODevice::ReadOnly);
1235             QByteArray ba = file3.readAll();
1236             handler->showBlackMessage(FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
1237                 .arg(fileName).arg(" ")
1238                 .arg(ba.count('\n')).arg(ba.size()));
1239             if (cmd.cmd == "wq")
1240                 delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
1241         } else {
1242             handler->showRedMessage(tr("File not saved"));
1243         }
1244     } else if (cmd.matches("wa", "wall")) {
1245         // :w[all]
1246         FileManager *fm = ICore::instance()->fileManager();
1247         QList<IFile *> toSave = fm->modifiedFiles();
1248         QList<IFile *> failed = fm->saveModifiedFilesSilently(toSave);
1249         if (failed.isEmpty())
1250             handler->showBlackMessage(tr("Saving succeeded"));
1251         else
1252             handler->showRedMessage(tr("%n files not saved", 0, failed.size()));
1253     } else if (cmd.matches("q", "quit")) {
1254         // :q[uit]
1255         emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
1256     } else if (cmd.matches("qa", "qall")) {
1257         // :qa[ll]
1258         emit delayedQuitAllRequested(cmd.hasBang);
1259     } else if (cmd.matches("sp", "split")) {
1260         // :sp[lit]
1261         triggerAction(Core::Constants::SPLIT);
1262     } else if (cmd.matches("vs", "vsplit")) {
1263         // :vs[plit]
1264         triggerAction(Core::Constants::SPLIT_SIDE_BY_SIDE);
1265     } else if (cmd.matches("mak", "make")) {
1266         // :mak[e][!] [arguments]
1267         triggerAction(ProjectExplorer::Constants::BUILD);
1268     } else if (cmd.matches("se", "set")) {
1269         if (cmd.args.isEmpty()) {
1270             // :se[t]
1271             showSettingsDialog();
1272         } else if (cmd.args == "ic" || cmd.args == "ignorecase") {
1273             // :set nc
1274             setActionChecked(Find::Constants::CASE_SENSITIVE, false);
1275         } else if (cmd.args == "noic" || cmd.args == "noignorecase") {
1276             // :set noic
1277             setActionChecked(Find::Constants::CASE_SENSITIVE, true);
1278         } else {
1279             *handled = false; // Let the handler see it as well.
1280         }
1281     } else if (cmd.matches("n", "next")) {
1282         // :n[ext]
1283         switchToFile(currentFile() + cmd.count);
1284     } else if (cmd.matches("prev", "previous") || cmd.matches("N", "Next")) {
1285         // :prev[ious], :N[ext]
1286         switchToFile(currentFile() - cmd.count);
1287     } else if (cmd.matches("bn", "bnext")) {
1288         // :bn[ext]
1289         switchToFile(currentFile() + cmd.count);
1290     } else if (cmd.matches("bp", "bprevious") || cmd.matches("bN", "bNext")) {
1291         // :bp[revious], :bN[ext]
1292         switchToFile(currentFile() - cmd.count);
1293     } else if (cmd.matches("on", "only")) {
1294         // :on[ly]
1295         //triggerAction(Core::Constants::REMOVE_ALL_SPLITS);
1296         triggerAction(Core::Constants::REMOVE_CURRENT_SPLIT);
1297     } else {
1298         // Check whether one of the configure commands matches.
1299         typedef CommandMap::const_iterator Iterator;
1300         const Iterator end = exCommandMap().constEnd();
1301         for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
1302             const QString &id = it.key();
1303             const QRegExp &re = it.value();
1304             if (!re.pattern().isEmpty() && re.indexIn(cmd.cmd) != -1) {
1305                 triggerAction(id);
1306                 return;
1307             }
1308         }
1309         *handled = false;
1310     }
1311 }
1312
1313 void FakeVimPluginPrivate::handleDelayedQuit(bool forced, Core::IEditor *editor)
1314 {
1315     // This tries to simulate vim behaviour. But the models of vim and
1316     // Qt Creator core do not match well...
1317     if (editorManager()->hasSplitter()) {
1318         triggerAction(Core::Constants::REMOVE_CURRENT_SPLIT);
1319     } else {
1320         QList<Core::IEditor *> editors;
1321         editors.append(editor);
1322         editorManager()->closeEditors(editors, !forced);
1323     }
1324 }
1325
1326 void FakeVimPluginPrivate::handleDelayedQuitAll(bool forced)
1327 {
1328     triggerAction(Core::Constants::REMOVE_ALL_SPLITS);
1329     editorManager()->closeAllEditors(!forced);
1330 }
1331
1332 void FakeVimPluginPrivate::moveToMatchingParenthesis(bool *moved, bool *forward,
1333         QTextCursor *cursor)
1334 {
1335     *moved = false;
1336
1337     bool undoFakeEOL = false;
1338     if (cursor->atBlockEnd() && cursor->block().length() > 1) {
1339         cursor->movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
1340         undoFakeEOL = true;
1341     }
1342     TextBlockUserData::MatchType match
1343         = TextBlockUserData::matchCursorForward(cursor);
1344     if (match == TextBlockUserData::Match) {
1345         *moved = true;
1346         *forward = true;
1347     } else {
1348         if (undoFakeEOL)
1349             cursor->movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
1350         if (match == TextBlockUserData::NoMatch) {
1351             // Backward matching is according to the character before the cursor.
1352             bool undoMove = false;
1353             if (!cursor->atBlockEnd()) {
1354                 cursor->movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
1355                 undoMove = true;
1356             }
1357             match = TextBlockUserData::matchCursorBackward(cursor);
1358             if (match == TextBlockUserData::Match) {
1359                 *moved = true;
1360                 *forward = false;
1361             } else if (undoMove) {
1362                 cursor->movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
1363             }
1364         }
1365     }
1366 }
1367
1368 void FakeVimPluginPrivate::indentRegion(int beginLine, int endLine,
1369       QChar typedChar)
1370 {
1371     FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
1372     if (!handler)
1373         return;
1374
1375     BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget());
1376     if (!bt)
1377         return;
1378
1379     const TabSettings oldTabSettings = bt->tabSettings();
1380     TabSettings tabSettings;
1381     tabSettings.m_indentSize = theFakeVimSetting(ConfigShiftWidth)->value().toInt();
1382     tabSettings.m_tabSize = theFakeVimSetting(ConfigTabStop)->value().toInt();
1383     tabSettings.m_spacesForTabs = theFakeVimSetting(ConfigExpandTab)->value().toBool();
1384     bt->setTabSettings(tabSettings);
1385
1386     QTextDocument *doc = bt->document();
1387     QTextBlock startBlock = doc->findBlockByNumber(beginLine);
1388
1389     // Record line lenghts for mark adjustments
1390     QVector<int> lineLengths(endLine - beginLine + 1);
1391     QTextBlock block = startBlock;
1392
1393     for (int i = beginLine; i <= endLine; ++i) {
1394         lineLengths[i - beginLine] = block.text().length();
1395         if (typedChar == 0 && block.text().simplified().isEmpty()) {
1396             // clear empty lines
1397             QTextCursor cursor(block);
1398             while (!cursor.atBlockEnd())
1399                 cursor.deleteChar();
1400         } else {
1401             bt->indenter()->indentBlock(doc, block, typedChar, bt);
1402         }
1403         block = block.next();
1404     }
1405
1406     bt->setTabSettings(oldTabSettings);
1407 }
1408
1409 void FakeVimPluginPrivate::quitFakeVim()
1410 {
1411     theFakeVimSetting(ConfigUseFakeVim)->setValue(false);
1412 }
1413
1414 void FakeVimPluginPrivate::showCommandBuffer(const QString &contents)
1415 {
1416     //qDebug() << "SHOW COMMAND BUFFER" << contents;
1417     if (QLabel *label = qobject_cast<QLabel *>(m_statusBar->widget()))
1418         label->setText("  " + contents);
1419 }
1420
1421 void FakeVimPluginPrivate::showExtraInformation(const QString &text)
1422 {
1423     FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
1424     if (handler)
1425         QMessageBox::information(handler->widget(), tr("FakeVim Information"), text);
1426 }
1427
1428 void FakeVimPluginPrivate::changeSelection
1429     (const QList<QTextEdit::ExtraSelection> &selection)
1430 {
1431     if (FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender()))
1432         if (BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget()))
1433             bt->setExtraSelections(BaseTextEditorWidget::FakeVimSelection, selection);
1434 }
1435
1436 int FakeVimPluginPrivate::currentFile() const
1437 {
1438     Core::OpenEditorsModel *model = editorManager()->openedEditorsModel();
1439     IEditor *editor = editorManager()->currentEditor();
1440     return model->indexOf(editor).row();
1441 }
1442
1443 void FakeVimPluginPrivate::switchToFile(int n)
1444 {
1445     Core::OpenEditorsModel *model = editorManager()->openedEditorsModel();
1446     int size = model->rowCount();
1447     QTC_ASSERT(size, return);
1448     n = n % size;
1449     if (n < 0)
1450         n += size;
1451     editorManager()->activateEditorForIndex(model->index(n, 0));
1452 }
1453
1454 CommandMap &FakeVimExCommandsPage::exCommandMap()
1455 {
1456     return m_q->exCommandMap();
1457 }
1458
1459 CommandMap &FakeVimExCommandsPage::defaultExCommandMap()
1460 {
1461     return m_q->defaultExCommandMap();
1462 }
1463
1464 ///////////////////////////////////////////////////////////////////////
1465 //
1466 // FakeVimPlugin
1467 //
1468 ///////////////////////////////////////////////////////////////////////
1469
1470 FakeVimPlugin::FakeVimPlugin()
1471     : d(new FakeVimPluginPrivate(this))
1472 {}
1473
1474 FakeVimPlugin::~FakeVimPlugin()
1475 {
1476     delete d;
1477 }
1478
1479 bool FakeVimPlugin::initialize(const QStringList &arguments, QString *errorMessage)
1480 {
1481     Q_UNUSED(arguments)
1482     Q_UNUSED(errorMessage)
1483     return d->initialize();
1484 }
1485
1486 ExtensionSystem::IPlugin::ShutdownFlag FakeVimPlugin::aboutToShutdown()
1487 {
1488     d->aboutToShutdown();
1489     return SynchronousShutdown;
1490 }
1491
1492 void FakeVimPlugin::extensionsInitialized()
1493 {
1494     d->m_statusBar = new Core::StatusBarWidget;
1495     d->m_statusBar->setWidget(new QLabel);
1496     //d->m_statusBar->setContext(Context(FAKEVIM_CONTEXT));
1497     d->m_statusBar->setPosition(StatusBarWidget::Last);
1498     addAutoReleasedObject(d->m_statusBar);
1499 }
1500
1501 } // namespace Internal
1502 } // namespace FakeVim
1503
1504 #include "fakevimplugin.moc"
1505
1506 Q_EXPORT_PLUGIN(FakeVimPlugin)