PageRenderTime 60ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/src/plugins/fakevim/fakevimplugin.cpp

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