PageRenderTime 59ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/plugins/fakevim/fakevimplugin.cpp

https://bitbucket.org/kyanha/qt-creator
C++ | 2073 lines | 1631 code | 275 blank | 167 comment | 281 complexity | 7c0ffc4d2bcc85dca9482b5003e0c395 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
  4. ** Contact: http://www.qt-project.org/legal
  5. **
  6. ** This file is part of Qt Creator.
  7. **
  8. ** Commercial License Usage
  9. ** Licensees holding valid commercial Qt licenses may use this file in
  10. ** accordance with the commercial license agreement provided with the
  11. ** Software or, alternatively, in accordance with the terms contained in
  12. ** a written agreement between you and Digia. For licensing terms and
  13. ** conditions see http://qt.digia.com/licensing. For further information
  14. ** use the contact form at http://qt.digia.com/contact-us.
  15. **
  16. ** GNU Lesser General Public License Usage
  17. ** Alternatively, this file may be used under the terms of the GNU Lesser
  18. ** General Public License version 2.1 as published by the Free Software
  19. ** Foundation and appearing in the file LICENSE.LGPL included in the
  20. ** packaging of this file. Please review the following information to
  21. ** ensure the GNU Lesser General Public License version 2.1 requirements
  22. ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  23. **
  24. ** In addition, as a special exception, Digia gives you certain additional
  25. ** rights. These rights are described in the Digia Qt LGPL Exception
  26. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  27. **
  28. ****************************************************************************/
  29. #include "fakevimplugin.h"
  30. #include "fakevimhandler.h"
  31. #include "ui_fakevimoptions.h"
  32. #include <coreplugin/actionmanager/actioncontainer.h>
  33. #include <coreplugin/actionmanager/actionmanager.h>
  34. #include <coreplugin/actionmanager/command.h>
  35. #include <coreplugin/actionmanager/command.h>
  36. #include <coreplugin/actionmanager/commandmappings.h>
  37. #include <coreplugin/coreconstants.h>
  38. #include <coreplugin/dialogs/ioptionspage.h>
  39. #include <coreplugin/editormanager/editormanager.h>
  40. #include <coreplugin/editormanager/openeditorsmodel.h>
  41. #include <coreplugin/documentmanager.h>
  42. #include <coreplugin/icore.h>
  43. #include <coreplugin/idocument.h>
  44. #include <coreplugin/messagemanager.h>
  45. #include <coreplugin/id.h>
  46. #include <coreplugin/statusbarwidget.h>
  47. #include <coreplugin/statusbarmanager.h>
  48. #include <projectexplorer/projectexplorerconstants.h>
  49. #include <texteditor/basetextdocumentlayout.h>
  50. #include <texteditor/basetexteditor.h>
  51. #include <texteditor/basetextmark.h>
  52. #include <texteditor/texteditorconstants.h>
  53. #include <texteditor/typingsettings.h>
  54. #include <texteditor/tabsettings.h>
  55. #include <texteditor/icodestylepreferences.h>
  56. #include <texteditor/texteditorsettings.h>
  57. #include <texteditor/indenter.h>
  58. #include <texteditor/codeassist/basicproposalitem.h>
  59. #include <texteditor/codeassist/basicproposalitemlistmodel.h>
  60. #include <texteditor/codeassist/completionassistprovider.h>
  61. #include <texteditor/codeassist/iassistprocessor.h>
  62. #include <texteditor/codeassist/iassistinterface.h>
  63. #include <texteditor/codeassist/genericproposal.h>
  64. #include <find/findplugin.h>
  65. #include <find/textfindconstants.h>
  66. #include <find/ifindsupport.h>
  67. #include <utils/qtcassert.h>
  68. #include <utils/savedaction.h>
  69. #include <utils/treewidgetcolumnstretcher.h>
  70. #include <utils/stylehelper.h>
  71. #include <cpptools/cpptoolsconstants.h>
  72. #include <extensionsystem/pluginmanager.h>
  73. #include <QAbstractTableModel>
  74. #include <QDebug>
  75. #include <QFile>
  76. #include <QtPlugin>
  77. #include <QObject>
  78. #include <QSettings>
  79. #include <QStackedWidget>
  80. #include <QTextStream>
  81. #include <QDesktopServices>
  82. #include <QItemDelegate>
  83. #include <QMessageBox>
  84. #include <QPlainTextEdit>
  85. #include <QShortcut>
  86. #include <QTextBlock>
  87. #include <QTextCursor>
  88. #include <QTextEdit>
  89. #include <QTreeWidgetItem>
  90. using namespace TextEditor;
  91. using namespace Core;
  92. namespace FakeVim {
  93. namespace Internal {
  94. const char INSTALL_HANDLER[] = "TextEditor.FakeVimHandler";
  95. const char SETTINGS_CATEGORY[] = "D.FakeVim";
  96. const char SETTINGS_CATEGORY_FAKEVIM_ICON[] = ":/core/images/category_fakevim.png";
  97. const char SETTINGS_ID[] = "A.General";
  98. const char SETTINGS_EX_CMDS_ID[] = "B.ExCommands";
  99. const char SETTINGS_USER_CMDS_ID[] = "C.UserCommands";
  100. typedef QLatin1String _;
  101. class MiniBuffer : public QStackedWidget
  102. {
  103. Q_OBJECT
  104. public:
  105. MiniBuffer() : m_label(new QLabel(this)), m_edit(new QLineEdit(this)), m_eventFilter(0)
  106. {
  107. m_edit->installEventFilter(this);
  108. connect(m_edit, SIGNAL(textEdited(QString)), SLOT(changed()));
  109. connect(m_edit, SIGNAL(cursorPositionChanged(int,int)), SLOT(changed()));
  110. connect(m_edit, SIGNAL(selectionChanged()), SLOT(changed()));
  111. m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
  112. addWidget(m_label);
  113. addWidget(m_edit);
  114. }
  115. void setContents(const QString &contents, int cursorPos, int anchorPos,
  116. int messageLevel, QObject *eventFilter)
  117. {
  118. if (cursorPos != -1) {
  119. m_edit->blockSignals(true);
  120. m_label->clear();
  121. m_edit->setText(contents);
  122. if (anchorPos != -1 && anchorPos != cursorPos)
  123. m_edit->setSelection(anchorPos, cursorPos - anchorPos);
  124. else
  125. m_edit->setCursorPosition(cursorPos);
  126. m_edit->blockSignals(false);
  127. setCurrentWidget(m_edit);
  128. m_edit->setFocus();
  129. } else if (contents.isEmpty() && messageLevel != MessageShowCmd) {
  130. hide();
  131. } else {
  132. show();
  133. m_label->setText(messageLevel == MessageMode ? _("-- ") + contents + _(" --") : contents);
  134. QString css;
  135. if (messageLevel == MessageError) {
  136. css = _("border:1px solid rgba(255,255,255,150);"
  137. "background-color:rgba(255,0,0,100);");
  138. } else if (messageLevel == MessageWarning) {
  139. css = _("border:1px solid rgba(255,255,255,120);"
  140. "background-color:rgba(255,255,0,20);");
  141. } else if (messageLevel == MessageShowCmd) {
  142. css = _("border:1px solid rgba(255,255,255,120);"
  143. "background-color:rgba(100,255,100,30);");
  144. }
  145. m_label->setStyleSheet(QString::fromLatin1(
  146. "*{border-radius:2px;padding-left:4px;padding-right:4px;%1}").arg(css));
  147. if (m_edit->hasFocus())
  148. emit edited(QString(), -1, -1);
  149. setCurrentWidget(m_label);
  150. }
  151. if (m_eventFilter != eventFilter) {
  152. if (m_eventFilter != 0) {
  153. m_edit->removeEventFilter(m_eventFilter);
  154. disconnect(SIGNAL(edited(QString,int,int)));
  155. }
  156. if (eventFilter != 0) {
  157. m_edit->installEventFilter(eventFilter);
  158. connect(this, SIGNAL(edited(QString,int,int)),
  159. eventFilter, SLOT(miniBufferTextEdited(QString,int,int)));
  160. }
  161. m_eventFilter = eventFilter;
  162. }
  163. }
  164. QSize sizeHint() const
  165. {
  166. QSize size = QWidget::sizeHint();
  167. // reserve maximal width for line edit widget
  168. return currentWidget() == m_edit ? QSize(maximumWidth(), size.height()) : size;
  169. }
  170. signals:
  171. void edited(const QString &text, int cursorPos, int anchorPos);
  172. private slots:
  173. void changed()
  174. {
  175. const int cursorPos = m_edit->cursorPosition();
  176. int anchorPos = m_edit->selectionStart();
  177. if (anchorPos == cursorPos)
  178. anchorPos = cursorPos + m_edit->selectedText().length();
  179. emit edited(m_edit->text(), cursorPos, anchorPos);
  180. }
  181. bool eventFilter(QObject *ob, QEvent *ev)
  182. {
  183. // cancel editing on escape
  184. if (m_eventFilter != 0 && ob == m_edit && ev->type() == QEvent::ShortcutOverride
  185. && static_cast<QKeyEvent*>(ev)->key() == Qt::Key_Escape) {
  186. emit edited(QString(), -1, -1);
  187. ev->accept();
  188. return true;
  189. }
  190. return false;
  191. }
  192. private:
  193. QLabel *m_label;
  194. QLineEdit *m_edit;
  195. QObject *m_eventFilter;
  196. };
  197. ///////////////////////////////////////////////////////////////////////
  198. //
  199. // FakeVimOptionPage
  200. //
  201. ///////////////////////////////////////////////////////////////////////
  202. typedef QMap<QString, QRegExp> ExCommandMap;
  203. typedef QMap<int, QString> UserCommandMap;
  204. class FakeVimOptionPage : public IOptionsPage
  205. {
  206. Q_OBJECT
  207. public:
  208. FakeVimOptionPage()
  209. {
  210. setId(_(SETTINGS_ID));
  211. setDisplayName(tr("General"));
  212. setCategory(SETTINGS_CATEGORY);
  213. setDisplayCategory(tr("FakeVim"));
  214. setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
  215. }
  216. QWidget *createPage(QWidget *parent);
  217. void apply() { m_group.apply(ICore::settings()); }
  218. void finish() { m_group.finish(); }
  219. virtual bool matches(const QString &) const;
  220. private slots:
  221. void copyTextEditorSettings();
  222. void setQtStyle();
  223. void setPlainStyle();
  224. private:
  225. friend class DebuggerPlugin;
  226. Ui::FakeVimOptionPage m_ui;
  227. QString m_searchKeywords;
  228. Utils::SavedActionSet m_group;
  229. };
  230. QWidget *FakeVimOptionPage::createPage(QWidget *parent)
  231. {
  232. QWidget *w = new QWidget(parent);
  233. m_ui.setupUi(w);
  234. m_group.clear();
  235. m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
  236. m_ui.checkBoxUseFakeVim);
  237. m_group.insert(theFakeVimSetting(ConfigReadVimRc),
  238. m_ui.checkBoxReadVimRc);
  239. m_group.insert(theFakeVimSetting(ConfigExpandTab),
  240. m_ui.checkBoxExpandTab);
  241. m_group.insert(theFakeVimSetting(ConfigHlSearch),
  242. m_ui.checkBoxHlSearch);
  243. m_group.insert(theFakeVimSetting(ConfigShiftWidth),
  244. m_ui.spinBoxShiftWidth);
  245. m_group.insert(theFakeVimSetting(ConfigShowMarks),
  246. m_ui.checkBoxShowMarks);
  247. m_group.insert(theFakeVimSetting(ConfigSmartTab),
  248. m_ui.checkBoxSmartTab);
  249. m_group.insert(theFakeVimSetting(ConfigStartOfLine),
  250. m_ui.checkBoxStartOfLine);
  251. m_group.insert(theFakeVimSetting(ConfigTabStop),
  252. m_ui.spinBoxTabStop);
  253. m_group.insert(theFakeVimSetting(ConfigBackspace),
  254. m_ui.lineEditBackspace);
  255. m_group.insert(theFakeVimSetting(ConfigIsKeyword),
  256. m_ui.lineEditIsKeyword);
  257. m_group.insert(theFakeVimSetting(ConfigPassControlKey),
  258. m_ui.checkBoxPassControlKey);
  259. m_group.insert(theFakeVimSetting(ConfigAutoIndent),
  260. m_ui.checkBoxAutoIndent);
  261. m_group.insert(theFakeVimSetting(ConfigSmartIndent),
  262. m_ui.checkBoxSmartIndent);
  263. m_group.insert(theFakeVimSetting(ConfigIncSearch),
  264. m_ui.checkBoxIncSearch);
  265. m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
  266. m_ui.checkBoxUseCoreSearch);
  267. m_group.insert(theFakeVimSetting(ConfigSmartCase),
  268. m_ui.checkBoxSmartCase);
  269. m_group.insert(theFakeVimSetting(ConfigWrapScan),
  270. m_ui.checkBoxWrapScan);
  271. m_group.insert(theFakeVimSetting(ConfigShowCmd),
  272. m_ui.checkBoxShowCmd);
  273. connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
  274. SLOT(copyTextEditorSettings()));
  275. connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
  276. SLOT(setQtStyle()));
  277. connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
  278. SLOT(setPlainStyle()));
  279. if (m_searchKeywords.isEmpty()) {
  280. QLatin1Char sep(' ');
  281. QTextStream(&m_searchKeywords)
  282. << sep << m_ui.checkBoxUseFakeVim->text()
  283. << sep << m_ui.checkBoxReadVimRc->text()
  284. << sep << m_ui.checkBoxAutoIndent->text()
  285. << sep << m_ui.checkBoxSmartIndent->text()
  286. << sep << m_ui.checkBoxExpandTab->text()
  287. << sep << m_ui.checkBoxSmartTab->text()
  288. << sep << m_ui.checkBoxHlSearch->text()
  289. << sep << m_ui.checkBoxIncSearch->text()
  290. << sep << m_ui.checkBoxStartOfLine->text()
  291. << sep << m_ui.checkBoxUseCoreSearch->text()
  292. << sep << m_ui.checkBoxSmartCase->text()
  293. << sep << m_ui.checkBoxShowMarks->text()
  294. << sep << m_ui.checkBoxPassControlKey->text()
  295. << sep << m_ui.labelShiftWidth->text()
  296. << sep << m_ui.labelTabulator->text()
  297. << sep << m_ui.labelBackspace->text()
  298. << sep << m_ui.labelIsKeyword->text();
  299. m_searchKeywords.remove(QLatin1Char('&'));
  300. }
  301. return w;
  302. }
  303. void FakeVimOptionPage::copyTextEditorSettings()
  304. {
  305. TabSettings ts = TextEditorSettings::instance()->codeStyle()->tabSettings();
  306. TypingSettings tps = TextEditorSettings::instance()->typingSettings();
  307. m_ui.checkBoxExpandTab->setChecked(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
  308. m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
  309. m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
  310. m_ui.checkBoxSmartTab->setChecked(
  311. tps.m_smartBackspaceBehavior == TypingSettings::BackspaceFollowsPreviousIndents);
  312. m_ui.checkBoxAutoIndent->setChecked(true);
  313. m_ui.checkBoxSmartIndent->setChecked(tps.m_autoIndent);
  314. m_ui.checkBoxIncSearch->setChecked(true);
  315. }
  316. void FakeVimOptionPage::setQtStyle()
  317. {
  318. m_ui.checkBoxExpandTab->setChecked(true);
  319. m_ui.spinBoxTabStop->setValue(4);
  320. m_ui.spinBoxShiftWidth->setValue(4);
  321. m_ui.checkBoxSmartTab->setChecked(true);
  322. m_ui.checkBoxAutoIndent->setChecked(true);
  323. m_ui.checkBoxSmartIndent->setChecked(true);
  324. m_ui.checkBoxIncSearch->setChecked(true);
  325. m_ui.lineEditBackspace->setText(_("indent,eol,start"));
  326. }
  327. void FakeVimOptionPage::setPlainStyle()
  328. {
  329. m_ui.checkBoxExpandTab->setChecked(false);
  330. m_ui.spinBoxTabStop->setValue(8);
  331. m_ui.spinBoxShiftWidth->setValue(8);
  332. m_ui.checkBoxSmartTab->setChecked(false);
  333. m_ui.checkBoxAutoIndent->setChecked(false);
  334. m_ui.checkBoxSmartIndent->setChecked(false);
  335. m_ui.checkBoxIncSearch->setChecked(false);
  336. m_ui.lineEditBackspace->setText(QString());
  337. }
  338. bool FakeVimOptionPage::matches(const QString &s) const
  339. {
  340. return m_searchKeywords.contains(s, Qt::CaseInsensitive);
  341. }
  342. //const char *FAKEVIM_CONTEXT = "FakeVim";
  343. ///////////////////////////////////////////////////////////////////////
  344. //
  345. // FakeVimExCommandsPage
  346. //
  347. ///////////////////////////////////////////////////////////////////////
  348. enum { CommandRole = Qt::UserRole };
  349. class FakeVimExCommandsPage : public CommandMappings
  350. {
  351. Q_OBJECT
  352. public:
  353. FakeVimExCommandsPage(FakeVimPluginPrivate *q)
  354. : m_q(q)
  355. {
  356. setId(_(SETTINGS_EX_CMDS_ID));
  357. setDisplayName(tr("Ex Command Mapping"));
  358. setCategory(SETTINGS_CATEGORY);
  359. setDisplayCategory(tr("FakeVim"));
  360. setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
  361. }
  362. QWidget *createPage(QWidget *parent);
  363. void initialize();
  364. ExCommandMap &exCommandMap();
  365. ExCommandMap &defaultExCommandMap();
  366. public slots:
  367. void commandChanged(QTreeWidgetItem *current);
  368. void targetIdentifierChanged();
  369. void resetTargetIdentifier();
  370. void removeTargetIdentifier();
  371. void defaultAction();
  372. private:
  373. FakeVimPluginPrivate *m_q;
  374. };
  375. QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
  376. {
  377. QWidget *w = CommandMappings::createPage(parent);
  378. setPageTitle(tr("Ex Command Mapping"));
  379. setTargetHeader(tr("Ex Trigger Expression"));
  380. setTargetLabelText(tr("Regular expression:"));
  381. setTargetEditTitle(tr("Ex Command"));
  382. setImportExportEnabled(false);
  383. return w;
  384. }
  385. void FakeVimExCommandsPage::initialize()
  386. {
  387. QMap<QString, QTreeWidgetItem *> sections;
  388. foreach (Command *c, ActionManager::commands()) {
  389. if (c->action() && c->action()->isSeparator())
  390. continue;
  391. QTreeWidgetItem *item = new QTreeWidgetItem;
  392. const QString name = c->id().toString();
  393. const int pos = name.indexOf(QLatin1Char('.'));
  394. const QString section = name.left(pos);
  395. const QString subId = name.mid(pos + 1);
  396. item->setData(0, CommandRole, name);
  397. if (!sections.contains(section)) {
  398. QTreeWidgetItem *categoryItem =
  399. new QTreeWidgetItem(commandList(), QStringList() << section);
  400. QFont f = categoryItem->font(0);
  401. f.setBold(true);
  402. categoryItem->setFont(0, f);
  403. sections.insert(section, categoryItem);
  404. commandList()->expandItem(categoryItem);
  405. }
  406. sections[section]->addChild(item);
  407. item->setText(0, subId);
  408. item->setText(1, c->description());
  409. QString regex;
  410. if (exCommandMap().contains(name))
  411. regex = exCommandMap()[name].pattern();
  412. item->setText(2, regex);
  413. if (regex != defaultExCommandMap()[name].pattern())
  414. setModified(item, true);
  415. }
  416. commandChanged(0);
  417. }
  418. void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
  419. {
  420. CommandMappings::commandChanged(current);
  421. if (current)
  422. targetEdit()->setText(current->text(2));
  423. }
  424. void FakeVimExCommandsPage::targetIdentifierChanged()
  425. {
  426. QTreeWidgetItem *current = commandList()->currentItem();
  427. if (!current)
  428. return;
  429. const QString name = current->data(0, CommandRole).toString();
  430. const QString regex = targetEdit()->text();
  431. if (current->data(0, Qt::UserRole).isValid()) {
  432. current->setText(2, regex);
  433. exCommandMap()[name] = QRegExp(regex);
  434. }
  435. setModified(current, regex != defaultExCommandMap()[name].pattern());
  436. }
  437. void FakeVimExCommandsPage::resetTargetIdentifier()
  438. {
  439. QTreeWidgetItem *current = commandList()->currentItem();
  440. if (!current)
  441. return;
  442. const QString name = current->data(0, CommandRole).toString();
  443. QString regex;
  444. if (defaultExCommandMap().contains(name))
  445. regex = defaultExCommandMap()[name].pattern();
  446. targetEdit()->setText(regex);
  447. }
  448. void FakeVimExCommandsPage::removeTargetIdentifier()
  449. {
  450. targetEdit()->clear();
  451. }
  452. void FakeVimExCommandsPage::defaultAction()
  453. {
  454. int n = commandList()->topLevelItemCount();
  455. for (int i = 0; i != n; ++i) {
  456. QTreeWidgetItem *section = commandList()->topLevelItem(i);
  457. int m = section->childCount();
  458. for (int j = 0; j != m; ++j) {
  459. QTreeWidgetItem *item = section->child(j);
  460. const QString name = item->data(0, CommandRole).toString();
  461. QString regex;
  462. if (defaultExCommandMap().contains(name))
  463. regex = defaultExCommandMap()[name].pattern();
  464. setModified(item, false);
  465. item->setText(2, regex);
  466. if (item == commandList()->currentItem())
  467. commandChanged(item);
  468. }
  469. }
  470. }
  471. ///////////////////////////////////////////////////////////////////////
  472. //
  473. // FakeVimUserCommandsPage
  474. //
  475. ///////////////////////////////////////////////////////////////////////
  476. class FakeVimUserCommandsModel : public QAbstractTableModel
  477. {
  478. Q_OBJECT
  479. public:
  480. FakeVimUserCommandsModel(FakeVimPluginPrivate *q) : m_q(q) {}
  481. ~FakeVimUserCommandsModel() {}
  482. int rowCount(const QModelIndex &parent) const;
  483. int columnCount(const QModelIndex &parent) const;
  484. QVariant data(const QModelIndex &index, int role) const;
  485. bool setData(const QModelIndex &index, const QVariant &data, int role);
  486. QVariant headerData(int section, Qt::Orientation orientation, int role) const;
  487. Qt::ItemFlags flags(const QModelIndex &index) const;
  488. private:
  489. FakeVimPluginPrivate *m_q;
  490. };
  491. int FakeVimUserCommandsModel::rowCount(const QModelIndex &parent) const
  492. {
  493. return parent.isValid() ? 0 : 9;
  494. }
  495. int FakeVimUserCommandsModel::columnCount(const QModelIndex &parent) const
  496. {
  497. return parent.isValid() ? 0 : 2;
  498. }
  499. QVariant FakeVimUserCommandsModel::headerData(int section,
  500. Qt::Orientation orient, int role) const
  501. {
  502. if (orient == Qt::Horizontal && role == Qt::DisplayRole) {
  503. switch (section) {
  504. case 0: return tr("Action");
  505. case 1: return tr("Command");
  506. };
  507. }
  508. return QVariant();
  509. }
  510. Qt::ItemFlags FakeVimUserCommandsModel::flags(const QModelIndex &index) const
  511. {
  512. if (index.column() == 1)
  513. return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
  514. return QAbstractTableModel::flags(index);
  515. }
  516. class FakeVimUserCommandsDelegate : public QItemDelegate
  517. {
  518. public:
  519. explicit FakeVimUserCommandsDelegate(QObject *parent)
  520. : QItemDelegate(parent)
  521. {}
  522. QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
  523. const QModelIndex &) const
  524. {
  525. QLineEdit *lineEdit = new QLineEdit(parent);
  526. lineEdit->setFrame(false);
  527. return lineEdit;
  528. }
  529. void setModelData(QWidget *editor, QAbstractItemModel *model,
  530. const QModelIndex &index) const
  531. {
  532. QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor);
  533. QTC_ASSERT(lineEdit, return);
  534. model->setData(index, lineEdit->text(), Qt::EditRole);
  535. }
  536. };
  537. class FakeVimUserCommandsPage : public IOptionsPage
  538. {
  539. Q_OBJECT
  540. public:
  541. FakeVimUserCommandsPage(FakeVimPluginPrivate *q)
  542. : m_q(q)
  543. {
  544. setId(_(SETTINGS_USER_CMDS_ID));
  545. setDisplayName(tr("User Command Mapping"));
  546. setCategory(SETTINGS_CATEGORY);
  547. setDisplayCategory(tr("FakeVim"));
  548. setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
  549. }
  550. void apply();
  551. void finish() {}
  552. QWidget *createPage(QWidget *parent);
  553. void initialize() {}
  554. UserCommandMap &userCommandMap();
  555. UserCommandMap &defaultUserCommandMap();
  556. private:
  557. FakeVimPluginPrivate *m_q;
  558. };
  559. QWidget *FakeVimUserCommandsPage::createPage(QWidget *parent)
  560. {
  561. QGroupBox *box = new QGroupBox(parent);
  562. FakeVimUserCommandsModel *model = new FakeVimUserCommandsModel(m_q);
  563. QTreeView *widget = new QTreeView;
  564. widget->setModel(model);
  565. widget->resizeColumnToContents(0);
  566. FakeVimUserCommandsDelegate *delegate = new FakeVimUserCommandsDelegate(widget);
  567. widget->setItemDelegateForColumn(1, delegate);
  568. QGridLayout *layout = new QGridLayout(box);
  569. layout->addWidget(widget, 0, 0);
  570. box->setLayout(layout);
  571. return box;
  572. }
  573. void FakeVimUserCommandsPage::apply()
  574. {
  575. //m_q->writeSettings();
  576. }
  577. ///////////////////////////////////////////////////////////////////////
  578. //
  579. // WordCompletion
  580. //
  581. ///////////////////////////////////////////////////////////////////////
  582. class FakeVimCompletionAssistProvider : public CompletionAssistProvider
  583. {
  584. public:
  585. bool supportsEditor(const Id &) const
  586. {
  587. return false;
  588. }
  589. IAssistProcessor *createProcessor() const;
  590. void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
  591. {
  592. Q_UNUSED(forward);
  593. m_handler = handler;
  594. if (!m_handler)
  595. return;
  596. BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(handler->widget());
  597. if (!editor)
  598. return;
  599. //qDebug() << "ACTIVATE: " << needle << forward;
  600. m_needle = needle;
  601. editor->invokeAssist(Completion, this);
  602. }
  603. void setInactive()
  604. {
  605. m_needle.clear();
  606. m_handler = 0;
  607. }
  608. const QString &needle() const
  609. {
  610. return m_needle;
  611. }
  612. void appendNeedle(const QChar &c)
  613. {
  614. m_needle.append(c);
  615. }
  616. FakeVimHandler *handler() const
  617. {
  618. return m_handler;
  619. }
  620. private:
  621. FakeVimHandler *m_handler;
  622. QString m_needle;
  623. };
  624. class FakeVimAssistProposalItem : public BasicProposalItem
  625. {
  626. public:
  627. FakeVimAssistProposalItem(const FakeVimCompletionAssistProvider *provider)
  628. : m_provider(const_cast<FakeVimCompletionAssistProvider *>(provider))
  629. {}
  630. virtual bool implicitlyApplies() const
  631. {
  632. return false;
  633. }
  634. virtual bool prematurelyApplies(const QChar &c) const
  635. {
  636. m_provider->appendNeedle(c);
  637. return text() == m_provider->needle();
  638. }
  639. virtual void applyContextualContent(BaseTextEditor *, int) const
  640. {
  641. QTC_ASSERT(m_provider->handler(), return);
  642. m_provider->handler()->handleReplay(text().mid(m_provider->needle().size()));
  643. const_cast<FakeVimCompletionAssistProvider *>(m_provider)->setInactive();
  644. }
  645. private:
  646. FakeVimCompletionAssistProvider *m_provider;
  647. };
  648. class FakeVimAssistProposalModel : public BasicProposalItemListModel
  649. {
  650. public:
  651. FakeVimAssistProposalModel(const QList<BasicProposalItem *> &items)
  652. : BasicProposalItemListModel(items)
  653. {}
  654. virtual bool supportsPrefixExpansion() const
  655. {
  656. return false;
  657. }
  658. };
  659. class FakeVimCompletionAssistProcessor : public IAssistProcessor
  660. {
  661. public:
  662. FakeVimCompletionAssistProcessor(const IAssistProvider *provider)
  663. : m_provider(static_cast<const FakeVimCompletionAssistProvider *>(provider))
  664. {}
  665. IAssistProposal *perform(const IAssistInterface *interface)
  666. {
  667. const QString &needle = m_provider->needle();
  668. const int basePosition = interface->position() - needle.size();
  669. QTextCursor tc(interface->textDocument());
  670. tc.setPosition(interface->position());
  671. tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
  672. QList<BasicProposalItem *> items;
  673. QSet<QString> seen;
  674. QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
  675. while (1) {
  676. tc = tc.document()->find(needle, tc.position(), flags);
  677. if (tc.isNull())
  678. break;
  679. QTextCursor sel = tc;
  680. sel.select(QTextCursor::WordUnderCursor);
  681. QString found = sel.selectedText();
  682. // Only add "real" completions.
  683. if (found.startsWith(needle)
  684. && !seen.contains(found)
  685. && sel.anchor() != basePosition) {
  686. seen.insert(found);
  687. BasicProposalItem *item = new FakeVimAssistProposalItem(m_provider);
  688. item->setText(found);
  689. items.append(item);
  690. }
  691. tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
  692. }
  693. //qDebug() << "COMPLETIONS" << completions->size();
  694. delete interface;
  695. return new GenericProposal(basePosition, new FakeVimAssistProposalModel(items));
  696. }
  697. private:
  698. const FakeVimCompletionAssistProvider *m_provider;
  699. };
  700. IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor() const
  701. {
  702. return new FakeVimCompletionAssistProcessor(this);
  703. }
  704. ///////////////////////////////////////////////////////////////////////
  705. //
  706. // FakeVimPluginPrivate
  707. //
  708. ///////////////////////////////////////////////////////////////////////
  709. class FakeVimPluginPrivate : public QObject
  710. {
  711. Q_OBJECT
  712. public:
  713. FakeVimPluginPrivate(FakeVimPlugin *);
  714. ~FakeVimPluginPrivate();
  715. friend class FakeVimPlugin;
  716. friend class FakeVimExCommandsPage;
  717. friend class FakeVimUserCommandsPage;
  718. friend class FakeVimUserCommandsModel;
  719. bool initialize();
  720. void aboutToShutdown();
  721. private slots:
  722. void onCoreAboutToClose();
  723. void editorOpened(Core::IEditor *);
  724. void editorAboutToClose(Core::IEditor *);
  725. void setUseFakeVim(const QVariant &value);
  726. void setUseFakeVimInternal(bool on);
  727. void quitFakeVim();
  728. void triggerCompletions();
  729. void triggerSimpleCompletions(const QString &needle, bool forward);
  730. void windowCommand(int key);
  731. void find(bool reverse);
  732. void findNext(bool reverse);
  733. void foldToggle(int depth);
  734. void foldAll(bool fold);
  735. void fold(int depth, bool fold);
  736. void foldGoTo(int count, bool current);
  737. void jumpToGlobalMark(QChar mark, bool backTickMode, const QString &fileName);
  738. void showSettingsDialog();
  739. void maybeReadVimRc();
  740. void setBlockSelection(bool);
  741. void hasBlockSelection(bool*);
  742. void resetCommandBuffer();
  743. void showCommandBuffer(const QString &contents, int cursorPos, int anchorPos,
  744. int messageLevel, QObject *eventFilter);
  745. void showExtraInformation(const QString &msg);
  746. void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
  747. void highlightMatches(const QString &needle);
  748. void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
  749. void checkForElectricCharacter(bool *result, QChar c);
  750. void indentRegion(int beginBlock, int endBlock, QChar typedChar);
  751. void handleExCommand(bool *handled, const ExCommand &cmd);
  752. void writeSettings();
  753. void readSettings();
  754. void handleDelayedQuitAll(bool forced);
  755. void handleDelayedQuit(bool forced, Core::IEditor *editor);
  756. void userActionTriggered();
  757. void switchToFile(int n);
  758. int currentFile() const;
  759. signals:
  760. void delayedQuitRequested(bool forced, Core::IEditor *editor);
  761. void delayedQuitAllRequested(bool forced);
  762. private:
  763. FakeVimPlugin *q;
  764. FakeVimOptionPage *m_fakeVimOptionsPage;
  765. FakeVimExCommandsPage *m_fakeVimExCommandsPage;
  766. FakeVimUserCommandsPage *m_fakeVimUserCommandsPage;
  767. QHash<IEditor *, FakeVimHandler *> m_editorToHandler;
  768. void triggerAction(const Id &id);
  769. void setActionChecked(const Id &id, bool check);
  770. typedef int (*DistFunction)(const QRect &cursor, const QRect &other);
  771. void moveSomewhere(DistFunction f);
  772. ExCommandMap &exCommandMap() { return m_exCommandMap; }
  773. ExCommandMap &defaultExCommandMap() { return m_defaultExCommandMap; }
  774. ExCommandMap m_exCommandMap;
  775. ExCommandMap m_defaultExCommandMap;
  776. UserCommandMap &userCommandMap() { return m_userCommandMap; }
  777. UserCommandMap &defaultUserCommandMap() { return m_defaultUserCommandMap; }
  778. UserCommandMap m_userCommandMap;
  779. UserCommandMap m_defaultUserCommandMap;
  780. StatusBarWidget *m_statusBar;
  781. // @TODO: Delete
  782. //WordCompletion *m_wordCompletion;
  783. FakeVimCompletionAssistProvider *m_wordProvider;
  784. };
  785. QVariant FakeVimUserCommandsModel::data(const QModelIndex &index, int role) const
  786. {
  787. if (!index.isValid())
  788. return QVariant();
  789. if (role == Qt::DisplayRole || role == Qt::EditRole) {
  790. switch (index.column()) {
  791. case 0: // Action
  792. return tr("User command #%1").arg(index.row() + 1);
  793. case 1: // Command
  794. return m_q->userCommandMap().value(index.row() + 1);
  795. }
  796. }
  797. return QVariant();
  798. }
  799. bool FakeVimUserCommandsModel::setData(const QModelIndex &index,
  800. const QVariant &data, int role)
  801. {
  802. if (role == Qt::DisplayRole || role == Qt::EditRole)
  803. if (index.column() == 1)
  804. m_q->userCommandMap()[index.row() + 1] = data.toString();
  805. return true;
  806. }
  807. FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
  808. {
  809. q = plugin;
  810. m_fakeVimOptionsPage = 0;
  811. m_fakeVimExCommandsPage = 0;
  812. m_fakeVimUserCommandsPage = 0;
  813. defaultExCommandMap()[_(CppTools::Constants::SWITCH_HEADER_SOURCE)] =
  814. QRegExp(_("^A$"));
  815. defaultExCommandMap()[_("Coreplugin.OutputPane.previtem")] =
  816. QRegExp(_("^(cN(ext)?|cp(revious)?)!?( (.*))?$"));
  817. defaultExCommandMap()[_("Coreplugin.OutputPane.nextitem")] =
  818. QRegExp(_("^cn(ext)?!?( (.*))?$"));
  819. defaultExCommandMap()[_(TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR)] =
  820. QRegExp(_("^tag?$"));
  821. defaultExCommandMap()[_(Core::Constants::GO_BACK)] =
  822. QRegExp(_("^pop?$"));
  823. defaultExCommandMap()[_("QtCreator.Locate")] =
  824. QRegExp(_("^e$"));
  825. for (int i = 1; i < 10; ++i) {
  826. QString cmd = QString::fromLatin1(":echo User command %1 executed.<CR>");
  827. defaultUserCommandMap().insert(i, cmd.arg(i));
  828. }
  829. m_statusBar = 0;
  830. }
  831. FakeVimPluginPrivate::~FakeVimPluginPrivate()
  832. {
  833. q->removeObject(m_fakeVimOptionsPage);
  834. delete m_fakeVimOptionsPage;
  835. m_fakeVimOptionsPage = 0;
  836. delete theFakeVimSettings();
  837. q->removeObject(m_fakeVimExCommandsPage);
  838. delete m_fakeVimExCommandsPage;
  839. m_fakeVimExCommandsPage = 0;
  840. q->removeObject(m_fakeVimUserCommandsPage);
  841. delete m_fakeVimUserCommandsPage;
  842. m_fakeVimUserCommandsPage = 0;
  843. }
  844. void FakeVimPluginPrivate::onCoreAboutToClose()
  845. {
  846. // Don't attach to editors anymore.
  847. disconnect(ICore::editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
  848. this, SLOT(editorOpened(Core::IEditor*)));
  849. }
  850. void FakeVimPluginPrivate::aboutToShutdown()
  851. {
  852. }
  853. bool FakeVimPluginPrivate::initialize()
  854. {
  855. EditorManager *editorManager = ICore::editorManager();
  856. //m_wordCompletion = new WordCompletion;
  857. //q->addAutoReleasedObject(m_wordCompletion);
  858. m_wordProvider = new FakeVimCompletionAssistProvider;
  859. /*
  860. // Set completion settings and keep them up to date.
  861. TextEditorSettings *textEditorSettings = TextEditorSettings::instance();
  862. completion->setCompletionSettings(textEditorSettings->completionSettings());
  863. connect(textEditorSettings,
  864. SIGNAL(completionSettingsChanged(TextEditor::CompletionSettings)),
  865. completion,
  866. SLOT(setCompletionSettings(TextEditor::CompletionSettings)));
  867. */
  868. Context globalcontext(Core::Constants::C_GLOBAL);
  869. m_fakeVimOptionsPage = new FakeVimOptionPage;
  870. q->addObject(m_fakeVimOptionsPage);
  871. m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
  872. q->addObject(m_fakeVimExCommandsPage);
  873. m_fakeVimUserCommandsPage = new FakeVimUserCommandsPage(this);
  874. q->addObject(m_fakeVimUserCommandsPage);
  875. readSettings();
  876. Command *cmd = 0;
  877. cmd = ActionManager::registerAction(theFakeVimSetting(ConfigUseFakeVim),
  878. INSTALL_HANDLER, globalcontext, true);
  879. cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+V,Meta+V") : tr("Alt+V,Alt+V")));
  880. ActionContainer *advancedMenu =
  881. ActionManager::actionContainer(Core::Constants::M_EDIT_ADVANCED);
  882. advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
  883. for (int i = 1; i < 10; ++i) {
  884. QAction *act = new QAction(this);
  885. act->setText(tr("Execute User Action #%1").arg(i));
  886. act->setData(i);
  887. QString id = QString::fromLatin1("FakeVim.UserAction%1").arg(i);
  888. cmd = ActionManager::registerAction(act, Id(id), globalcontext);
  889. cmd->setDefaultKeySequence(QKeySequence((UseMacShortcuts ? tr("Meta+V,%1") : tr("Alt+V,%1")).arg(i)));
  890. connect(act, SIGNAL(triggered()), SLOT(userActionTriggered()));
  891. }
  892. connect(ICore::instance(), SIGNAL(coreAboutToClose()), this, SLOT(onCoreAboutToClose()));
  893. // EditorManager
  894. connect(editorManager, SIGNAL(editorAboutToClose(Core::IEditor*)),
  895. this, SLOT(editorAboutToClose(Core::IEditor*)));
  896. connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
  897. this, SLOT(editorOpened(Core::IEditor*)));
  898. connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
  899. this, SLOT(setUseFakeVim(QVariant)));
  900. connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
  901. this, SLOT(maybeReadVimRc()));
  902. // Delayed operations.
  903. connect(this, SIGNAL(delayedQuitRequested(bool,Core::IEditor*)),
  904. this, SLOT(handleDelayedQuit(bool,Core::IEditor*)), Qt::QueuedConnection);
  905. connect(this, SIGNAL(delayedQuitAllRequested(bool)),
  906. this, SLOT(handleDelayedQuitAll(bool)), Qt::QueuedConnection);
  907. // Vimrc can break test so don't source it if running tests.
  908. if (!ExtensionSystem::PluginManager::runningTests())
  909. maybeReadVimRc();
  910. // << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
  911. return true;
  912. }
  913. void FakeVimPluginPrivate::userActionTriggered()
  914. {
  915. QAction *act = qobject_cast<QAction *>(sender());
  916. if (!act)
  917. return;
  918. const int key = act->data().toInt();
  919. if (!key)
  920. return;
  921. QString cmd = userCommandMap().value(key);
  922. IEditor *editor = EditorManager::currentEditor();
  923. FakeVimHandler *handler = m_editorToHandler[editor];
  924. if (handler)
  925. handler->handleInput(cmd);
  926. }
  927. const char exCommandMapGroup[] = "FakeVimExCommand";
  928. const char userCommandMapGroup[] = "FakeVimUserCommand";
  929. const char reKey[] = "RegEx";
  930. const char cmdKey[] = "Cmd";
  931. const char idKey[] = "Command";
  932. void FakeVimPluginPrivate::writeSettings()
  933. {
  934. QSettings *settings = ICore::settings();
  935. theFakeVimSettings()->writeSettings(settings);
  936. { // block
  937. settings->beginWriteArray(_(exCommandMapGroup));
  938. int count = 0;
  939. typedef ExCommandMap::const_iterator Iterator;
  940. const Iterator end = exCommandMap().constEnd();
  941. for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
  942. const QString id = it.key();
  943. const QRegExp re = it.value();
  944. if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
  945. || (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
  946. settings->setArrayIndex(count);
  947. settings->setValue(_(idKey), id);
  948. settings->setValue(_(reKey), re.pattern());
  949. ++count;
  950. }
  951. }
  952. settings->endArray();
  953. } // block
  954. { // block
  955. settings->beginWriteArray(_(userCommandMapGroup));
  956. int count = 0;
  957. typedef UserCommandMap::const_iterator Iterator;
  958. const Iterator end = userCommandMap().constEnd();
  959. for (Iterator it = userCommandMap().constBegin(); it != end; ++it) {
  960. const int key = it.key();
  961. const QString cmd = it.value();
  962. if ((defaultUserCommandMap().contains(key)
  963. && defaultUserCommandMap()[key] != cmd)
  964. || (!defaultUserCommandMap().contains(key) && !cmd.isEmpty())) {
  965. settings->setArrayIndex(count);
  966. settings->setValue(_(idKey), key);
  967. settings->setValue(_(cmdKey), cmd);
  968. ++count;
  969. }
  970. }
  971. settings->endArray();
  972. } // block
  973. }
  974. void FakeVimPluginPrivate::readSettings()
  975. {
  976. QSettings *settings = ICore::settings();
  977. theFakeVimSettings()->readSettings(settings);
  978. exCommandMap() = defaultExCommandMap();
  979. int size = settings->beginReadArray(_(exCommandMapGroup));
  980. for (int i = 0; i < size; ++i) {
  981. settings->setArrayIndex(i);
  982. const QString id = settings->value(_(idKey)).toString();
  983. const QString re = settings->value(_(reKey)).toString();
  984. exCommandMap()[id] = QRegExp(re);
  985. }
  986. settings->endArray();
  987. userCommandMap() = defaultUserCommandMap();
  988. size = settings->beginReadArray(_(userCommandMapGroup));
  989. for (int i = 0; i < size; ++i) {
  990. settings->setArrayIndex(i);
  991. const int id = settings->value(_(idKey)).toInt();
  992. const QString cmd = settings->value(_(cmdKey)).toString();
  993. userCommandMap()[id] = cmd;
  994. }
  995. settings->endArray();
  996. }
  997. void FakeVimPluginPrivate::maybeReadVimRc()
  998. {
  999. //qDebug() << theFakeVimSetting(ConfigReadVimRc)
  1000. // << theFakeVimSetting(ConfigReadVimRc)->value();
  1001. //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
  1002. if (!theFakeVimSetting(ConfigReadVimRc)->value().toBool())
  1003. return;
  1004. QString fileName =
  1005. QDesktopServices::storageLocation(QDesktopServices::HomeLocation)
  1006. + _("/.vimrc");
  1007. //qDebug() << "READING VIMRC: " << fileName;
  1008. // Read it into a temporary handler for effects modifying global state.
  1009. QPlainTextEdit editor;
  1010. FakeVimHandler handler(&editor);
  1011. handler.handleCommand(_("source ") + fileName);
  1012. //writeSettings();
  1013. //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value();
  1014. }
  1015. void FakeVimPluginPrivate::showSettingsDialog()
  1016. {
  1017. ICore::showOptionsDialog(SETTINGS_CATEGORY, SETTINGS_ID);
  1018. }
  1019. void FakeVimPluginPrivate::triggerAction(const Id &id)
  1020. {
  1021. Core::Command *cmd = ActionManager::command(id);
  1022. QTC_ASSERT(cmd, qDebug() << "UNKNOWN CODE: " << id.name(); return);
  1023. QAction *action = cmd->action();
  1024. QTC_ASSERT(action, return);
  1025. action->trigger();
  1026. }
  1027. void FakeVimPluginPrivate::setActionChecked(const Id &id, bool check)
  1028. {
  1029. Core::Command *cmd = ActionManager::command(id);
  1030. QTC_ASSERT(cmd, return);
  1031. QAction *action = cmd->action();
  1032. QTC_ASSERT(action, return);
  1033. QTC_ASSERT(action->isCheckable(), return);
  1034. action->setChecked(!check); // trigger negates the action's state
  1035. action->trigger();
  1036. }
  1037. static int moveRightWeight(const QRect &cursor, const QRect &other)
  1038. {
  1039. int dx = other.left() - cursor.right();
  1040. if (dx < 0)
  1041. return -1;
  1042. int w = 10000 * dx;
  1043. int dy1 = cursor.top() - other.bottom();
  1044. int dy2 = cursor.bottom() - other.top();
  1045. w += dy1 * (dy1 > 0);
  1046. w += dy2 * (dy2 > 0);
  1047. qDebug() << " DX: " << dx << dy1 << dy2 << w;
  1048. return w;
  1049. }
  1050. static int moveLeftWeight(const QRect &cursor, const QRect &other)
  1051. {
  1052. int dx = other.right() - cursor.left();
  1053. if (dx < 0)
  1054. return -1;
  1055. int w = 10000 * dx;
  1056. int dy1 = cursor.top() - other.bottom();
  1057. int dy2 = cursor.bottom() - other.top();
  1058. w += dy1 * (dy1 > 0);
  1059. w += dy2 * (dy2 > 0);
  1060. return w;
  1061. }
  1062. static int moveUpWeight(const QRect &cursor, const QRect &other)
  1063. {
  1064. int dy = other.bottom() - cursor.top();
  1065. if (dy < 0)
  1066. return -1;
  1067. int w = 10000 * dy;
  1068. int dx1 = cursor.left() - other.right();
  1069. int dx2 = cursor.right() - other.left();
  1070. w += dx1 * (dx1 > 0);
  1071. w += dx2 * (dx2 > 0);
  1072. return w;
  1073. }
  1074. static int moveDownWeight(const QRect &cursor, const QRect &other)
  1075. {
  1076. int dy = other.top() - cursor.bottom();
  1077. if (dy < 0)
  1078. return -1;
  1079. int w = 10000 * dy;
  1080. int dx1 = cursor.left() - other.right();
  1081. int dx2 = cursor.right() - other.left();
  1082. w += dx1 * (dx1 > 0);
  1083. w += dx2 * (dx2 > 0);
  1084. return w;
  1085. }
  1086. void FakeVimPluginPrivate::windowCommand(int key)
  1087. {
  1088. # define control(n) (256 + n)
  1089. switch (key) {
  1090. case 'c': case 'C': case control('c'):
  1091. triggerAction(Core::Constants::CLOSE);
  1092. break;
  1093. case 'n': case 'N': case control('n'):
  1094. triggerAction(Core::Constants::GOTONEXT);
  1095. break;
  1096. case 'o': case 'O': case control('o'):
  1097. //triggerAction(Core::Constants::REMOVE_ALL_SPLITS);
  1098. triggerAction(Core::Constants::REMOVE_CURRENT_SPLIT);
  1099. break;
  1100. case 'p': case 'P': case control('p'):
  1101. triggerAction(Core::Constants::GOTOPREV);
  1102. break;
  1103. case 's': case 'S': case control('s'):
  1104. triggerAction(Core::Constants::SPLIT);
  1105. break;
  1106. case 'w': case 'W': case control('w'):
  1107. triggerAction(Core::Constants::GOTO_OTHER_SPLIT);
  1108. break;
  1109. case Qt::Key_Right:
  1110. moveSomewhere(&moveRightWeight);
  1111. break;
  1112. case Qt::Key_Left:
  1113. moveSomewhere(&moveLeftWeight);
  1114. break;
  1115. case Qt::Key_Up:
  1116. moveSomewhere(&moveUpWeight);
  1117. break;
  1118. case Qt::Key_Down:
  1119. moveSomewhere(&moveDownWeight);
  1120. break;
  1121. default:
  1122. qDebug() << "UNKNOWN WINDOWS COMMAND: " << key;
  1123. break;
  1124. }
  1125. # undef control
  1126. }
  1127. void FakeVimPluginPrivate::moveSomewhere(DistFunction f)
  1128. {
  1129. IEditor *editor = EditorManager::currentEditor();
  1130. QWidget *w = editor->widget();
  1131. QPlainTextEdit *pe = qobject_cast<QPlainTextEdit *>(w);
  1132. QTC_ASSERT(pe, return);
  1133. QRect rc = pe->cursorRect();
  1134. QRect cursorRect(w->mapToGlobal(rc.topLeft()),
  1135. w->mapToGlobal(rc.bottomRight()));
  1136. //qDebug() << "\nCURSOR: " << cursorRect;
  1137. IEditor *bestEditor = 0;
  1138. int bestValue = 1 << 30;
  1139. foreach (IEditor *editor, EditorManager::instance()->visibleEditors()) {
  1140. QWidget *w = editor->widget();
  1141. QRect editorRect(w->mapToGlobal(w->geometry().topLeft()),
  1142. w->mapToGlobal(w->geometry().bottomRight()));
  1143. //qDebug() << " EDITOR: " << editorRect << editor;
  1144. int value = f(cursorRect, editorRect);
  1145. if (value != -1 && value < bestValue) {
  1146. bestValue = value;
  1147. bestEditor = editor;
  1148. //qDebug() << " BEST SO FAR: " << bestValue << bestEditor;
  1149. }
  1150. }
  1151. //qDebug() << " BEST: " << bestValue << bestEditor;
  1152. // FIME: This is know to fail as the EditorManager will fall back to
  1153. // the current editor's view. Needs additional public API there.
  1154. if (bestEditor)
  1155. EditorManager::activateEditor(bestEditor);
  1156. }
  1157. void FakeVimPluginPrivate::find(bool reverse)
  1158. {
  1159. if (Find::FindPlugin *plugin = Find::FindPlugin::instance()) {
  1160. plugin->setUseFakeVim(true);
  1161. plugin->openFindToolBar(reverse
  1162. ? Find::FindPlugin::FindBackward
  1163. : Find::FindPlugin::FindForward);
  1164. }
  1165. }
  1166. void FakeVimPluginPrivate::findNext(bool reverse)
  1167. {
  1168. if (reverse)
  1169. triggerAction(Find::Constants::FIND_PREVIOUS);
  1170. else
  1171. triggerAction(Find::Constants::FIND_NEXT);
  1172. }
  1173. void FakeVimPluginPrivate::foldToggle(int depth)
  1174. {
  1175. IEditor *ieditor = EditorManager::currentEditor();
  1176. BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(ieditor->widget());
  1177. QTC_ASSERT(editor != 0, return);
  1178. QTextBlock block = editor->textCursor().block();
  1179. fold(depth, !BaseTextDocumentLayout::isFolded(block));
  1180. }
  1181. void FakeVimPluginPrivate::foldAll(bool fold)
  1182. {
  1183. IEditor *ieditor = EditorManager::currentEditor();
  1184. BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(ieditor->widget());
  1185. QTC_ASSERT(editor != 0, return);
  1186. QTextDocument *doc = editor->document();
  1187. BaseTextDocumentLayout *documentLayout =
  1188. qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
  1189. QTC_ASSERT(documentLayout != 0, return);
  1190. QTextBlock block = editor->document()->firstBlock();
  1191. while (block.isValid()) {
  1192. BaseTextDocumentLayout::doFoldOrUnfold(block, !fold);
  1193. block = block.next();
  1194. }
  1195. documentLayout->requestUpdate();
  1196. documentLayout->emitDocumentSizeChanged();
  1197. }
  1198. void FakeVimPluginPrivate::fold(int depth, bool fold)
  1199. {
  1200. IEditor *ieditor = EditorManager::currentEditor();
  1201. BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(ieditor->widget());
  1202. QTC_ASSERT(editor != 0, return);
  1203. QTextDocument *doc = editor->document();
  1204. BaseTextDocumentLayout *documentLayout =
  1205. qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
  1206. QTC_ASSERT(documentLayout != 0, return);
  1207. QTextBlock block = editor->textCursor().block();
  1208. int indent = BaseTextDocumentLayout::foldingIndent(block);
  1209. if (fold) {
  1210. if (BaseTextDocumentLayout::isFolded(block)) {
  1211. while (block.isValid() && (BaseTextDocumentLayout::foldingIndent(block) >= indent
  1212. || !block.isVisible())) {
  1213. block = block.previous();
  1214. }
  1215. }
  1216. if (BaseTextDocumentLayout::canFold(block))
  1217. ++indent;
  1218. while (depth != 0 && block.isValid()) {
  1219. const int indent2 = BaseTextDocumentLayout::foldingIndent(block);
  1220. if (BaseTextDocumentLayout::canFold(block) && indent2 < indent) {
  1221. BaseTextDocumentLayout::doFoldOrUnfold(block, false);
  1222. if (depth > 0)
  1223. --depth;
  1224. indent = indent2;
  1225. }
  1226. block = block.previous();
  1227. }
  1228. } else {
  1229. if (BaseTextDocumentLayout::isFolded(block)) {
  1230. if (depth < 0) {
  1231. // recursively open fold
  1232. while (depth < 0 && block.isValid()
  1233. && BaseTextDocumentLayout::foldingIndent(block) >= indent) {
  1234. if (BaseTextDocumentLayout::canFold(block)) {
  1235. BaseTextDocumentLayout::doFoldOrUnfold(block, true);
  1236. if (depth > 0)
  1237. --depth;
  1238. }
  1239. block = block.next();
  1240. }
  1241. } else {
  1242. if (BaseTextDocumentLayout::canFold(block)) {
  1243. BaseTextDocumentLayout::doFoldOrUnfold(block, true);
  1244. if (depth > 0)
  1245. --depth;
  1246. }
  1247. }
  1248. }
  1249. }
  1250. documentLayout->requestUpdate();
  1251. documentLayout->emitDocumentSizeChanged();
  1252. }
  1253. void FakeVimPluginPrivate::foldGoTo(int count, bool current)
  1254. {
  1255. IEditor *ieditor = EditorManager::currentEditor();
  1256. BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(ieditor->widget());
  1257. QTC_ASSERT(editor != 0, return);
  1258. QTextCursor tc = editor->textCursor();
  1259. QTextBlock block = tc.block();
  1260. int pos = -1;
  1261. if (count > 0) {
  1262. int repeat = count;
  1263. block = block.next();
  1264. QTextBlock prevBlock = block;
  1265. int indent = BaseTextDocumentLayout::foldingIndent(block);
  1266. block = block.next();
  1267. while (block.isValid()) {
  1268. int newIndent = BaseTextDocumentLayout::foldingIndent(block);
  1269. if (current ? indent > newIndent : indent < newIndent) {
  1270. if (prevBlock.isVisible()) {
  1271. pos = prevBlock.position();
  1272. if (--repeat <= 0)
  1273. break;
  1274. } else if (current) {
  1275. indent = newIndent;
  1276. }
  1277. }
  1278. if (!current)
  1279. indent = newIndent;
  1280. prevBlock = block;
  1281. block = block.next();
  1282. }
  1283. } else if (count < 0) {
  1284. int repeat = -count;
  1285. int indent = BaseTextDocumentLayout::foldingIndent(block);
  1286. block = block.previous();
  1287. while (block.isValid()) {
  1288. int newIndent = BaseTextDocumentLayout::foldingIndent(block);
  1289. if (current ? indent > newIndent : indent < newIndent) {
  1290. while (block.isValid() && !block.isVisible())
  1291. block = block.previous();
  1292. pos = block.position();
  1293. if (--repeat <= 0)
  1294. break;
  1295. }
  1296. if (!current)
  1297. indent = newIndent;
  1298. block = block.previous();
  1299. }
  1300. }
  1301. if (pos != -1) {
  1302. tc.setPosition(pos, QTextCursor::KeepAnchor);
  1303. editor->setTextCursor(tc);
  1304. }
  1305. }
  1306. void FakeVimPluginPrivate::jumpToGlobalMark(QChar mark, bool backTickMode,
  1307. const QString &fileName)
  1308. {
  1309. Core::IEditor *iedit = Core::EditorManager::openEditor(fileName);
  1310. if (!iedit)
  1311. return;
  1312. FakeVimHandler *handler = m_editorToHandler.value(iedit, 0);
  1313. if (handler)
  1314. handler->jumpToLocalMark(mark, backTickMode);
  1315. }
  1316. // This class defers deletion of a child FakeVimHandler using deleteLater().
  1317. class DeferredDeleter : public QObject
  1318. {
  1319. Q_OBJECT
  1320. FakeVimHandler *m_handler;
  1321. public:
  1322. DeferredDeleter(QObject *parent, FakeVimHandler *handler)
  1323. : QObject(parent), m_handler(handler)
  1324. {}
  1325. virtual ~DeferredDeleter()
  1326. {
  1327. if (m_handler) {
  1328. m_handler->disconnectFromEditor();
  1329. m_handler->deleteLater();
  1330. m_handler = 0;
  1331. }
  1332. }
  1333. };
  1334. void FakeVimPluginPrivate::editorOpened(IEditor *editor)
  1335. {
  1336. if (!editor)
  1337. return;
  1338. QWidget *widget = editor->widget();
  1339. if (!widget)
  1340. return;
  1341. // we can only handle QTextEdit and QPlainTextEdit
  1342. if (!qobject_cast<QTextEdit *>(widget) && !qobject_cast<QPlainTextEdit *>(widget))
  1343. return;
  1344. //qDebug() << "OPENING: " << editor << editor->widget()
  1345. // << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
  1346. FakeVimHandler *handler = new FakeVimHandler(widget, 0);
  1347. // the handler might have triggered the deletion of the editor:
  1348. // make sure that it can return before being deleted itself
  1349. new DeferredDeleter(widget, handler);
  1350. m_editorToHandler[editor] = handler;
  1351. connect(handler, SIGNAL(extraInformationChanged(QString)),
  1352. SLOT(showExtraInformation(QString)));
  1353. connect(handler, SIGNAL(commandBufferChanged(QString,int,int,int,QObject*)),
  1354. SLOT(showCommandBuffer(QString,int,int,int,QObject*)));
  1355. connect(handler, SIGNAL(selectionChanged(QList<QTextEdit::ExtraSelection>)),
  1356. SLOT(changeSelection(QList<QTextEdit::ExtraSelection>)));
  1357. connect(handler, SIGNAL(highlightMatches(QString)),
  1358. SLOT(highlightMatches(QString)));
  1359. connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
  1360. SLOT(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)));
  1361. connect(handler, SIGNAL(indentRegion(int,int,QChar)),
  1362. SLOT(indentRegion(int,int,QChar)));
  1363. connect(handler, SIGNAL(checkForElectricCharacter(bool*,QChar)),
  1364. SLOT(checkForElectricCharacter(bool*,QChar)));
  1365. connect(handler, SIGNAL(requestSetBlockSelection(bool)),
  1366. SLOT(setBlockSelection(bool)));
  1367. connect(handler, SIGNAL(requestHasBlockSelection(bool*)),
  1368. SLOT(hasBlockSelection(bool*)));
  1369. connect(handler, SIGNAL(completionRequested()),
  1370. SLOT(triggerCompletions()));
  1371. connect(handler, SIGNAL(simpleCompletionRequested(QString,bool)),
  1372. SLOT(triggerSimpleCompletions(QString,bool)));
  1373. connect(handler, SIGNAL(windowCommandRequested(int)),
  1374. SLOT(windowCommand(int)));
  1375. connect(handler, SIGNAL(findRequested(bool)),
  1376. SLOT(find(bool)));
  1377. connect(handler, SIGNAL(findNextRequested(bool)),
  1378. SLOT(findNext(bool)));
  1379. connect(handler, SIGNAL(foldToggle(int)),
  1380. SLOT(foldToggle(int)));
  1381. connect(handler, SIGNAL(foldAll(bool)),
  1382. SLOT(foldAll(bool)));
  1383. connect(handler, SIGNAL(fold(int,bool)),
  1384. SLOT(fold(int,bool)));
  1385. connect(handler, SIGNAL(foldGoTo(int, bool)),
  1386. SLOT(foldGoTo(int, bool)));
  1387. connect(handler, SIGNAL(jumpToGlobalMark(QChar,bool,QString)),
  1388. SLOT(jumpToGlobalMark(QChar,bool,QString)));
  1389. connect(handler, SIGNAL(handleExCommandRequested(bool*,ExCommand)),
  1390. SLOT(handleExCommand(bool*,ExCommand)));
  1391. connect(ICore::instance(), SIGNAL(saveSettingsRequested()),
  1392. SLOT(writeSettings()));
  1393. handler->setCurrentFileName(editor->document()->fileName());
  1394. handler->installEventFilter();
  1395. // pop up the bar
  1396. if (theFakeVimSetting(ConfigUseFakeVim)->value().toBool()) {
  1397. resetCommandBuffer();
  1398. handler->setupWidget();
  1399. }
  1400. }
  1401. void FakeVimPluginPrivate::editorAboutToClose(IEditor *editor)
  1402. {
  1403. //qDebug() << "CLOSING: " << editor << editor->widget();
  1404. m_editorToHandler.remove(editor);
  1405. }
  1406. void FakeVimPluginPrivate::setUseFakeVim(const QVariant &value)
  1407. {
  1408. //qDebug() << "SET USE FAKEVIM" << value;
  1409. bool on = value.toBool();
  1410. if (Find::FindPlugin::instance())
  1411. Find::FindPlugin::instance()->setUseFakeVim(on);
  1412. setUseFakeVimInternal(on);
  1413. }
  1414. void FakeVimPluginPrivate::setUseFakeVimInternal(bool on)
  1415. {
  1416. if (on) {
  1417. //ICore *core = ICore::instance();
  1418. //core->updateAdditionalContexts(Context(FAKEVIM_CONTEXT),
  1419. // Context());
  1420. foreach (IEditor *editor, m_editorToHandler.keys())
  1421. m_editorToHandler[editor]->setupWidget();
  1422. } else {
  1423. //ICore *core = ICore::instance();
  1424. //core->updateAdditionalContexts(Context(),
  1425. // Context(FAKEVIM_CONTEXT));
  1426. resetCommandBuffer();
  1427. foreach (IEditor *editor, m_editorToHandler.keys()) {
  1428. if (BaseTextEditorWidget *textEditor =
  1429. qobject_cast<BaseTextEditorWidget *>(editor->widget())) {
  1430. m_editorToHandler[editor]->restoreWidget(textEditor->tabSettings().m_tabSize);
  1431. }
  1432. }
  1433. }
  1434. }
  1435. void FakeVimPluginPrivate::triggerCompletions()
  1436. {
  1437. FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
  1438. if (!handler)
  1439. return;
  1440. if (BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(handler->widget()))
  1441. editor->invokeAssist(Completion, m_wordProvider);
  1442. // CompletionSupport::instance()->complete(editor->editor(), TextCompletion, false);
  1443. }
  1444. void FakeVimPluginPrivate::triggerSimpleCompletions(const QString &needle,
  1445. bool forward)
  1446. {
  1447. // m_wordCompletion->setActive(needle, forward, qobject_cast<FakeVimHandler *>(sender()));
  1448. m_wordProvider->setActive(needle, forward, qobject_cast<FakeVimHandler *>(sender()));
  1449. }
  1450. void FakeVimPluginPrivate::setBlockSelection(bool on)
  1451. {
  1452. FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
  1453. if (!handler)
  1454. return;
  1455. if (BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget()))
  1456. bt->setBlockSelection(on);
  1457. }
  1458. void FakeVimPluginPrivate::hasBlockSelection(bool *on)
  1459. {
  1460. FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
  1461. if (!handler)
  1462. return;
  1463. if (BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget()))
  1464. *on = bt->hasBlockSelection();
  1465. }
  1466. void FakeVimPluginPrivate::checkForElectricCharacter(bool *result, QChar c)
  1467. {
  1468. FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
  1469. if (!handler)
  1470. return;
  1471. if (BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget()))
  1472. *result = bt->indenter()->isElectricCharacter(c);
  1473. }
  1474. void FakeVimPluginPrivate::handleExCommand(bool *handled, const ExCommand &cmd)
  1475. {
  1476. using namespace Core;
  1477. //qDebug() << "PLUGIN HANDLE: " << cmd.cmd << cmd.count;
  1478. *handled = false;
  1479. FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
  1480. if (!handler)
  1481. return;
  1482. *handled = true;
  1483. if (cmd.matches(_("w"), _("write")) || cmd.cmd == _("wq")) {
  1484. // :w[rite]
  1485. IEditor *editor = m_editorToHandler.key(handler);
  1486. const QString fileName = handler->currentFileName();
  1487. if (editor && editor->document()->fileName() == fileName) {
  1488. // Handle that as a special case for nicer interaction with core
  1489. DocumentManager::saveDocument(editor->document());
  1490. // Check result by reading back.
  1491. QFile file3(fileName);
  1492. file3.open(QIODevice::ReadOnly);
  1493. QByteArray ba = file3.readAll();
  1494. handler->showMessage(MessageInfo, FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
  1495. .arg(fileName).arg(QLatin1Char(' '))
  1496. .arg(ba.count('\n')).arg(ba.size()));
  1497. if (cmd.cmd == _("wq"))
  1498. delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
  1499. } else {
  1500. handler->showMessage(MessageError, tr("File not saved"));
  1501. }
  1502. } else if (cmd.matches(_("wa"), _("wall"))) {
  1503. // :w[all]
  1504. QList<IDocument *> toSave = DocumentManager::modifiedDocuments();
  1505. QList<IDocument *> failed = DocumentManager::saveModifiedDocumentsSilently(toSave);
  1506. if (failed.isEmpty())
  1507. handler->showMessage(MessageInfo, tr("Saving succeeded"));
  1508. else
  1509. handler->showMessage(MessageError, tr("%n files not saved", 0, failed.size()));
  1510. } else if (cmd.matches(_("q"), _("quit"))) {
  1511. // :q[uit]
  1512. emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
  1513. } else if (cmd.matches(_("qa"), _("qall"))) {
  1514. // :qa[ll]
  1515. emit delayedQuitAllRequested(cmd.hasBang);
  1516. } else if (cmd.matches(_("sp"), _("split"))) {
  1517. // :sp[lit]
  1518. triggerAction(Core::Constants::SPLIT);
  1519. } else if (cmd.matches(_("vs"), _("vsplit"))) {
  1520. // :vs[plit]
  1521. triggerAction(Core::Constants::SPLIT_SIDE_BY_SIDE);
  1522. } else if (cmd.matches(_("mak"), _("make"))) {
  1523. // :mak[e][!] [arguments]
  1524. triggerAction(ProjectExplorer::Constants::BUILD);
  1525. } else if (cmd.matches(_("se"), _("set"))) {
  1526. if (cmd.args.isEmpty()) {
  1527. // :se[t]
  1528. showSettingsDialog();
  1529. } else if (cmd.args == _("ic") || cmd.args == _("ignorecase")) {
  1530. // :set nc
  1531. setActionChecked(Find::Constants::CASE_SENSITIVE, false);
  1532. } else if (cmd.args == _("noic") || cmd.args == _("noignorecase")) {
  1533. // :set noic
  1534. setActionChecked(Find::Constants::CASE_SENSITIVE, true);
  1535. } else {
  1536. *handled = false; // Let the handler see it as well.
  1537. }
  1538. } else if (cmd.matches(_("n"), _("next"))) {
  1539. // :n[ext]
  1540. switchToFile(currentFile() + cmd.count);
  1541. } else if (cmd.matches(_("prev"), _("previous")) || cmd.matches(_("N"), _("Next"))) {
  1542. // :prev[ious], :N[ext]
  1543. switchToFile(currentFile() - cmd.count);
  1544. } else if (cmd.matches(_("bn"), _("bnext"))) {
  1545. // :bn[ext]
  1546. switchToFile(currentFile() + cmd.count);
  1547. } else if (cmd.matches(_("bp"), _("bprevious")) || cmd.matches(_("bN"), _("bNext"))) {
  1548. // :bp[revious], :bN[ext]
  1549. switchToFile(currentFile() - cmd.count);
  1550. } else if (cmd.matches(_("on"), _("only"))) {
  1551. // :on[ly]
  1552. //triggerAction(Core::Constants::REMOVE_ALL_SPLITS);
  1553. triggerAction(Core::Constants::REMOVE_CURRENT_SPLIT);
  1554. } else if (cmd.cmd == _("AS")) {
  1555. triggerAction(Core::Constants::SPLIT);
  1556. triggerAction(CppTools::Constants::SWITCH_HEADER_SOURCE);
  1557. } else if (cmd.cmd == _("AV")) {
  1558. triggerAction(Core::Constants::SPLIT_SIDE_BY_SIDE);
  1559. triggerAction(CppTools::Constants::SWITCH_HEADER_SOURCE);
  1560. } else {
  1561. // Check whether one of the configure commands matches.
  1562. typedef ExCommandMap::const_iterator Iterator;
  1563. const Iterator end = exCommandMap().constEnd();
  1564. for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
  1565. const QString &id = it.key();
  1566. QRegExp re = it.value();
  1567. if (!re.pattern().isEmpty() && re.indexIn(cmd.cmd) != -1) {
  1568. triggerAction(Core::Id(id));
  1569. return;
  1570. }
  1571. }
  1572. *handled = false;
  1573. }
  1574. }
  1575. void FakeVimPluginPrivate::handleDelayedQuit(bool forced, IEditor *editor)
  1576. {
  1577. // This tries to simulate vim behaviour. But the models of vim and
  1578. // Qt Creator core do not match well...
  1579. EditorManager *editorManager = ICore::editorManager();
  1580. if (editorManager->hasSplitter()) {
  1581. triggerAction(Core::Constants::REMOVE_CURRENT_SPLIT);
  1582. } else {
  1583. QList<IEditor *> editors;
  1584. editors.append(editor);
  1585. editorManager->closeEditors(editors, !forced);
  1586. }
  1587. }
  1588. void FakeVimPluginPrivate::handleDelayedQuitAll(bool forced)
  1589. {
  1590. triggerAction(Core::Constants::REMOVE_ALL_SPLITS);
  1591. ICore::editorManager()->closeAllEditors(!forced);
  1592. }
  1593. void FakeVimPluginPrivate::moveToMatchingParenthesis(bool *moved, bool *forward,
  1594. QTextCursor *cursor)
  1595. {
  1596. *moved = false;
  1597. bool undoFakeEOL = false;
  1598. if (cursor->atBlockEnd() && cursor->block().length() > 1) {
  1599. cursor->movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
  1600. undoFakeEOL = true;
  1601. }
  1602. TextBlockUserData::MatchType match
  1603. = TextBlockUserData::matchCursorForward(cursor);
  1604. if (match == TextBlockUserData::Match) {
  1605. *moved = true;
  1606. *forward = true;
  1607. } else {
  1608. if (undoFakeEOL)
  1609. cursor->movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
  1610. if (match == TextBlockUserData::NoMatch) {
  1611. // Backward matching is according to the character before the cursor.
  1612. bool undoMove = false;
  1613. if (!cursor->atBlockEnd()) {
  1614. cursor->movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
  1615. undoMove = true;
  1616. }
  1617. match = TextBlockUserData::matchCursorBackward(cursor);
  1618. if (match == TextBlockUserData::Match) {
  1619. *moved = true;
  1620. *forward = false;
  1621. } else if (undoMove) {
  1622. cursor->movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
  1623. }
  1624. }
  1625. }
  1626. }
  1627. void FakeVimPluginPrivate::indentRegion(int beginBlock, int endBlock,
  1628. QChar typedChar)
  1629. {
  1630. FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
  1631. if (!handler)
  1632. return;
  1633. BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget());
  1634. if (!bt)
  1635. return;
  1636. TabSettings tabSettings;
  1637. tabSettings.m_indentSize = theFakeVimSetting(ConfigShiftWidth)->value().toInt();
  1638. tabSettings.m_tabSize = theFakeVimSetting(ConfigTabStop)->value().toInt();
  1639. tabSettings.m_tabPolicy = theFakeVimSetting(ConfigExpandTab)->value().toBool()
  1640. ? TabSettings::SpacesOnlyTabPolicy : TabSettings::TabsOnlyTabPolicy;
  1641. QTextDocument *doc = bt->document();
  1642. QTextBlock startBlock = doc->findBlockByNumber(beginBlock);
  1643. // Record line lenghts for mark adjustments
  1644. QVector<int> lineLengths(endBlock - beginBlock + 1);
  1645. QTextBlock block = startBlock;
  1646. for (int i = beginBlock; i <= endBlock; ++i) {
  1647. lineLengths[i - beginBlock] = block.text().length();
  1648. if (typedChar == 0 && block.text().simplified().isEmpty()) {
  1649. // clear empty lines
  1650. QTextCursor cursor(block);
  1651. while (!cursor.atBlockEnd())
  1652. cursor.deleteChar();
  1653. } else {
  1654. bt->indenter()->indentBlock(doc, block, typedChar, tabSettings);
  1655. }
  1656. block = block.next();
  1657. }
  1658. }
  1659. void FakeVimPluginPrivate::quitFakeVim()
  1660. {
  1661. theFakeVimSetting(ConfigUseFakeVim)->setValue(false);
  1662. }
  1663. void FakeVimPluginPrivate::resetCommandBuffer()
  1664. {
  1665. showCommandBuffer(QString(), -1, -1, 0, 0);
  1666. }
  1667. void FakeVimPluginPrivate::showCommandBuffer(const QString &contents,
  1668. int cursorPos, int anchorPos, int messageLevel, QObject *eventFilter)
  1669. {
  1670. //qDebug() << "SHOW COMMAND BUFFER" << contents;
  1671. if (MiniBuffer *w = qobject_cast<MiniBuffer *>(m_statusBar->widget()))
  1672. w->setContents(contents, cursorPos, anchorPos, messageLevel, eventFilter);
  1673. }
  1674. void FakeVimPluginPrivate::showExtraInformation(const QString &text)
  1675. {
  1676. FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
  1677. if (handler)
  1678. QMessageBox::information(handler->widget(), tr("FakeVim Information"), text);
  1679. }
  1680. void FakeVimPluginPrivate::changeSelection(const QList<QTextEdit::ExtraSelection> &selection)
  1681. {
  1682. if (FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender()))
  1683. if (BaseTextEditorWidget *bt = qobject_cast<BaseTextEditorWidget *>(handler->widget()))
  1684. bt->setExtraSelections(BaseTextEditorWidget::FakeVimSelection, selection);
  1685. }
  1686. void FakeVimPluginPrivate::highlightMatches(const QString &needle)
  1687. {
  1688. IEditor *editor = EditorManager::currentEditor();
  1689. QWidget *w = editor->widget();
  1690. Find::IFindSupport *find = Aggregation::query<Find::IFindSupport>(w);
  1691. if (find != 0)
  1692. find->highlightAll(needle, Find::FindRegularExpression | Find::FindCaseSensitively);
  1693. }
  1694. int FakeVimPluginPrivate::currentFile() const
  1695. {
  1696. OpenEditorsModel *model = EditorManager::instance()->openedEditorsModel();
  1697. IEditor *editor = EditorManager::currentEditor();
  1698. return model->indexOf(editor).row();
  1699. }
  1700. void FakeVimPluginPrivate::switchToFile(int n)
  1701. {
  1702. EditorManager *editorManager = ICore::editorManager();
  1703. OpenEditorsModel *model = editorManager->openedEditorsModel();
  1704. int size = model->rowCount();
  1705. QTC_ASSERT(size, return);
  1706. n = n % size;
  1707. if (n < 0)
  1708. n += size;
  1709. editorManager->activateEditorForIndex(model->index(n, 0));
  1710. }
  1711. ExCommandMap &FakeVimExCommandsPage::exCommandMap()
  1712. {
  1713. return m_q->exCommandMap();
  1714. }
  1715. ExCommandMap &FakeVimExCommandsPage::defaultExCommandMap()
  1716. {
  1717. return m_q->defaultExCommandMap();
  1718. }
  1719. UserCommandMap &FakeVimUserCommandsPage::userCommandMap()
  1720. {
  1721. return m_q->userCommandMap();
  1722. }
  1723. UserCommandMap &FakeVimUserCommandsPage::defaultUserCommandMap()
  1724. {
  1725. return m_q->defaultUserCommandMap();
  1726. }
  1727. ///////////////////////////////////////////////////////////////////////
  1728. //
  1729. // FakeVimPlugin
  1730. //
  1731. ///////////////////////////////////////////////////////////////////////
  1732. FakeVimPlugin::FakeVimPlugin()
  1733. : d(new FakeVimPluginPrivate(this))
  1734. {}
  1735. FakeVimPlugin::~FakeVimPlugin()
  1736. {
  1737. delete d;
  1738. }
  1739. bool FakeVimPlugin::initialize(const QStringList &arguments, QString *errorMessage)
  1740. {
  1741. Q_UNUSED(arguments)
  1742. Q_UNUSED(errorMessage)
  1743. return d->initialize();
  1744. }
  1745. ExtensionSystem::IPlugin::ShutdownFlag FakeVimPlugin::aboutToShutdown()
  1746. {
  1747. d->aboutToShutdown();
  1748. return SynchronousShutdown;
  1749. }
  1750. void FakeVimPlugin::extensionsInitialized()
  1751. {
  1752. d->m_statusBar = new StatusBarWidget;
  1753. d->m_statusBar->setWidget(new MiniBuffer);
  1754. d->m_statusBar->setPosition(StatusBarWidget::Last);
  1755. addAutoReleasedObject(d->m_statusBar);
  1756. }
  1757. #ifdef WITH_TESTS
  1758. void FakeVimPlugin::setupTest(QString *title, FakeVimHandler **handler, QWidget **edit)
  1759. {
  1760. *title = QString::fromLatin1("test.cpp");
  1761. Core::IEditor *iedit = Core::EditorManager::openEditorWithContents(Core::Id(), title);
  1762. Core::EditorManager::activateEditor(iedit);
  1763. *edit = iedit->widget();
  1764. *handler = d->m_editorToHandler.value(iedit, 0);
  1765. (*handler)->handleCommand(_("set startofline"));
  1766. // *handler = 0;
  1767. // m_statusMessage.clear();
  1768. // m_statusData.clear();
  1769. // m_infoMessage.clear();
  1770. // if (m_textedit) {
  1771. // m_textedit->setPlainText(lines);
  1772. // QTextCursor tc = m_textedit->textCursor();
  1773. // tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
  1774. // m_textedit->setTextCursor(tc);
  1775. // m_textedit->setPlainText(lines);
  1776. // *handler = new FakeVimHandler(m_textedit);
  1777. // } else {
  1778. // m_plaintextedit->setPlainText(lines);
  1779. // QTextCursor tc = m_plaintextedit->textCursor();
  1780. // tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
  1781. // m_plaintextedit->setTextCursor(tc);
  1782. // m_plaintextedit->setPlainText(lines);
  1783. // *handler = new FakeVimHandler(m_plaintextedit);
  1784. // }
  1785. // QObject::connect(*handler, SIGNAL(commandBufferChanged(QString,int)),
  1786. // this, SLOT(changeStatusMessage(QString,int)));
  1787. // QObject::connect(*handler, SIGNAL(extraInformationChanged(QString)),
  1788. // this, SLOT(changeExtraInformation(QString)));
  1789. // QObject::connect(*handler, SIGNAL(statusDataChanged(QString)),
  1790. // this, SLOT(changeStatusData(QString)));
  1791. // QCOMPARE(EDITOR(toPlainText()), lines);
  1792. (*handler)->handleCommand(_("set iskeyword=@,48-57,_,192-255,a-z,A-Z"));
  1793. }
  1794. #endif
  1795. } // namespace Internal
  1796. } // namespace FakeVim
  1797. #include "fakevimplugin.moc"
  1798. Q_EXPORT_PLUGIN(FakeVim::Internal::FakeVimPlugin)