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

/src/plugins/fakevim/fakevimhandler.cpp

https://bitbucket.org/kyanha/qt-creator
C++ | 7466 lines | 6667 code | 466 blank | 333 comment | 1336 complexity | f5f9888d50ce84b29206b229987fc20a MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  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. //
  30. // ATTENTION:
  31. //
  32. // 1 Please do not add any direct dependencies to other Qt Creator code here.
  33. // Instead emit signals and let the FakeVimPlugin channel the information to
  34. // Qt Creator. The idea is to keep this file here in a "clean" state that
  35. // allows easy reuse with any QTextEdit or QPlainTextEdit derived class.
  36. //
  37. // 2 There are a few auto tests located in ../../../tests/auto/fakevim.
  38. // Commands that are covered there are marked as "// tested" below.
  39. //
  40. // 3 Some conventions:
  41. //
  42. // Use 1 based line numbers and 0 based column numbers. Even though
  43. // the 1 based line are not nice it matches vim's and QTextEdit's 'line'
  44. // concepts.
  45. //
  46. // Do not pass QTextCursor etc around unless really needed. Convert
  47. // early to line/column.
  48. //
  49. // A QTextCursor is always between characters, whereas vi's cursor is always
  50. // over a character. FakeVim interprets the QTextCursor to be over the character
  51. // to the right of the QTextCursor's position().
  52. //
  53. // A current "region of interest"
  54. // spans between anchor(), (i.e. the character below anchor()), and
  55. // position(). The character below position() is not included
  56. // if the last movement command was exclusive (MoveExclusive).
  57. //
  58. #include "fakevimhandler.h"
  59. #include <utils/hostosinfo.h>
  60. #include <utils/qtcassert.h>
  61. #include <QDebug>
  62. #include <QFile>
  63. #include <QObject>
  64. #include <QPointer>
  65. #include <QProcess>
  66. #include <QRegExp>
  67. #include <QTextStream>
  68. #include <QTimer>
  69. #include <QtAlgorithms>
  70. #include <QStack>
  71. #include <QApplication>
  72. #include <QClipboard>
  73. #include <QInputMethodEvent>
  74. #include <QKeyEvent>
  75. #include <QLineEdit>
  76. #include <QPlainTextEdit>
  77. #include <QScrollBar>
  78. #include <QTextBlock>
  79. #include <QTextCursor>
  80. #include <QTextDocumentFragment>
  81. #include <QTextEdit>
  82. #include <QMimeData>
  83. #include <algorithm>
  84. #include <climits>
  85. #include <ctype.h>
  86. //#define DEBUG_KEY 1
  87. #if DEBUG_KEY
  88. # define KEY_DEBUG(s) qDebug() << s
  89. #else
  90. # define KEY_DEBUG(s)
  91. #endif
  92. //#define DEBUG_UNDO 1
  93. #if DEBUG_UNDO
  94. # define UNDO_DEBUG(s) qDebug() << << revision() << s
  95. #else
  96. # define UNDO_DEBUG(s)
  97. #endif
  98. using namespace Utils;
  99. namespace FakeVim {
  100. namespace Internal {
  101. ///////////////////////////////////////////////////////////////////////
  102. //
  103. // FakeVimHandler
  104. //
  105. ///////////////////////////////////////////////////////////////////////
  106. #define StartOfLine QTextCursor::StartOfLine
  107. #define EndOfLine QTextCursor::EndOfLine
  108. #define MoveAnchor QTextCursor::MoveAnchor
  109. #define KeepAnchor QTextCursor::KeepAnchor
  110. #define Up QTextCursor::Up
  111. #define Down QTextCursor::Down
  112. #define Right QTextCursor::Right
  113. #define Left QTextCursor::Left
  114. #define EndOfDocument QTextCursor::End
  115. #define StartOfDocument QTextCursor::Start
  116. #define ParagraphSeparator QChar::ParagraphSeparator
  117. #define EDITOR(s) (m_textedit ? m_textedit->s : m_plaintextedit->s)
  118. #define MetaModifier // Use HostOsInfo::controlModifier() instead
  119. #define ControlModifier // Use HostOsInfo::controlModifier() instead
  120. typedef QLatin1String _;
  121. /* Clipboard MIME types used by Vim. */
  122. static const QString vimMimeText = _("_VIM_TEXT");
  123. static const QString vimMimeTextEncoded = _("_VIMENC_TEXT");
  124. using namespace Qt;
  125. /*! A \e Mode represents one of the basic modes of operation of FakeVim.
  126. */
  127. enum Mode
  128. {
  129. InsertMode,
  130. ReplaceMode,
  131. CommandMode,
  132. ExMode
  133. };
  134. /*! A \e SubMode is used for things that require one more data item
  135. and are 'nested' behind a \l Mode.
  136. */
  137. enum SubMode
  138. {
  139. NoSubMode,
  140. ChangeSubMode, // Used for c
  141. DeleteSubMode, // Used for d
  142. FilterSubMode, // Used for !
  143. IndentSubMode, // Used for =
  144. RegisterSubMode, // Used for "
  145. ShiftLeftSubMode, // Used for <
  146. ShiftRightSubMode, // Used for >
  147. InvertCaseSubMode, // Used for g~
  148. DownCaseSubMode, // Used for gu
  149. UpCaseSubMode, // Used for gU
  150. WindowSubMode, // Used for Ctrl-w
  151. YankSubMode, // Used for y
  152. ZSubMode, // Used for z
  153. CapitalZSubMode, // Used for Z
  154. ReplaceSubMode // Used for r
  155. };
  156. /*! A \e SubSubMode is used for things that require one more data item
  157. and are 'nested' behind a \l SubMode.
  158. */
  159. enum SubSubMode
  160. {
  161. NoSubSubMode,
  162. FtSubSubMode, // Used for f, F, t, T.
  163. MarkSubSubMode, // Used for m.
  164. BackTickSubSubMode, // Used for `.
  165. TickSubSubMode, // Used for '.
  166. TextObjectSubSubMode, // Used for thing like iw, aW, as etc.
  167. ZSubSubMode, // Used for zj, zk
  168. OpenSquareSubSubMode, // Used for [{, {(, [z
  169. CloseSquareSubSubMode, // Used for ]}, ]), ]z
  170. SearchSubSubMode
  171. };
  172. enum VisualMode
  173. {
  174. NoVisualMode,
  175. VisualCharMode,
  176. VisualLineMode,
  177. VisualBlockMode
  178. };
  179. enum MoveType
  180. {
  181. MoveExclusive,
  182. MoveInclusive,
  183. MoveLineWise
  184. };
  185. /*!
  186. \enum RangeMode
  187. The \e RangeMode serves as a means to define how the "Range" between
  188. the \l cursor and the \l anchor position is to be interpreted.
  189. \value RangeCharMode Entered by pressing \key v. The range includes
  190. all characters between cursor and anchor.
  191. \value RangeLineMode Entered by pressing \key V. The range includes
  192. all lines between the line of the cursor and
  193. the line of the anchor.
  194. \value RangeLineModeExclusice Like \l RangeLineMode, but keeps one
  195. newline when deleting.
  196. \value RangeBlockMode Entered by pressing \key Ctrl-v. The range includes
  197. all characters with line and column coordinates
  198. between line and columns coordinates of cursor and
  199. anchor.
  200. \value RangeBlockAndTailMode Like \l RangeBlockMode, but also includes
  201. all characters in the affected lines up to the end
  202. of these lines.
  203. */
  204. enum EventResult
  205. {
  206. EventHandled,
  207. EventUnhandled,
  208. EventCancelled, // Event is handled but a sub mode was cancelled.
  209. EventPassedToCore
  210. };
  211. struct CursorPosition
  212. {
  213. CursorPosition() : line(-1), column(-1) {}
  214. CursorPosition(int block, int column) : line(block), column(column) {}
  215. explicit CursorPosition(const QTextCursor &tc)
  216. : line(tc.block().blockNumber()), column(tc.positionInBlock()) {}
  217. CursorPosition(const QTextDocument *document, int position)
  218. {
  219. QTextBlock block = document->findBlock(position);
  220. line = block.blockNumber();
  221. column = position - block.position();
  222. }
  223. bool isValid() const { return line >= 0 && column >= 0; }
  224. bool operator>(const CursorPosition &other) const
  225. { return line > other.line || column > other.column; }
  226. bool operator==(const CursorPosition &other) const
  227. { return line == other.line && column == other.column; }
  228. bool operator!=(const CursorPosition &other) const { return !operator==(other); }
  229. int line; // Line in document (from 0, folded lines included).
  230. int column; // Position on line.
  231. };
  232. struct Mark
  233. {
  234. Mark(const CursorPosition &pos = CursorPosition(), const QString &fileName = QString())
  235. : position(pos), fileName(fileName) {}
  236. bool isValid() const { return position.isValid(); }
  237. bool isLocal(const QString &localFileName) const
  238. {
  239. return fileName.isEmpty() || fileName == localFileName;
  240. }
  241. CursorPosition position;
  242. QString fileName;
  243. };
  244. typedef QHash<QChar, Mark> Marks;
  245. typedef QHashIterator<QChar, Mark> MarksIterator;
  246. struct State
  247. {
  248. State() : revision(-1), position(), marks(), lastVisualMode(NoVisualMode),
  249. lastVisualModeInverted(false) {}
  250. State(int revision, const CursorPosition &position, const Marks &marks,
  251. VisualMode lastVisualMode, bool lastVisualModeInverted) : revision(revision),
  252. position(position), marks(marks), lastVisualMode(lastVisualMode),
  253. lastVisualModeInverted(lastVisualModeInverted) {}
  254. int revision;
  255. CursorPosition position;
  256. Marks marks;
  257. VisualMode lastVisualMode;
  258. bool lastVisualModeInverted;
  259. };
  260. struct Column
  261. {
  262. Column(int p, int l) : physical(p), logical(l) {}
  263. int physical; // Number of characters in the data.
  264. int logical; // Column on screen.
  265. };
  266. QDebug operator<<(QDebug ts, const Column &col)
  267. {
  268. return ts << "(p: " << col.physical << ", l: " << col.logical << ")";
  269. }
  270. struct Register
  271. {
  272. Register() : rangemode(RangeCharMode) {}
  273. Register(const QString &c) : contents(c), rangemode(RangeCharMode) {}
  274. Register(const QString &c, RangeMode m) : contents(c), rangemode(m) {}
  275. QString contents;
  276. RangeMode rangemode;
  277. };
  278. QDebug operator<<(QDebug ts, const Register &reg)
  279. {
  280. return ts << reg.contents;
  281. }
  282. struct SearchData
  283. {
  284. SearchData()
  285. {
  286. forward = true;
  287. highlightMatches = true;
  288. }
  289. QString needle;
  290. bool forward;
  291. bool highlightMatches;
  292. };
  293. // If string begins with given prefix remove it with trailing spaces and return true.
  294. static bool eatString(const char *prefix, QString *str)
  295. {
  296. if (!str->startsWith(_(prefix)))
  297. return false;
  298. *str = str->mid(strlen(prefix)).trimmed();
  299. return true;
  300. }
  301. static QRegExp vimPatternToQtPattern(QString needle, bool smartcase)
  302. {
  303. /* Transformations (Vim regexp -> QRegExp):
  304. * \a -> [A-Za-z]
  305. * \A -> [^A-Za-z]
  306. * \h -> [A-Za-z_]
  307. * \H -> [^A-Za-z_]
  308. * \l -> [a-z]
  309. * \L -> [^a-z]
  310. * \o -> [0-7]
  311. * \O -> [^0-7]
  312. * \u -> [A-Z]
  313. * \U -> [^A-Z]
  314. * \x -> [0-9A-Fa-f]
  315. * \X -> [^0-9A-Fa-f]
  316. *
  317. * \< -> \b
  318. * \> -> \b
  319. * [] -> \[\]
  320. * \= -> ?
  321. *
  322. * (...) <-> \(...\)
  323. * {...} <-> \{...\}
  324. * | <-> \|
  325. * ? <-> \?
  326. * + <-> \+
  327. * \{...} -> {...}
  328. *
  329. * \c - set ignorecase for rest
  330. * \C - set noignorecase for rest
  331. */
  332. bool ignorecase = smartcase && !needle.contains(QRegExp(_("[A-Z]")));
  333. QString pattern;
  334. pattern.reserve(2 * needle.size());
  335. bool escape = false;
  336. bool brace = false;
  337. bool embraced = false;
  338. bool range = false;
  339. bool curly = false;
  340. foreach (const QChar &c, needle) {
  341. if (brace) {
  342. brace = false;
  343. if (c == QLatin1Char(']')) {
  344. pattern.append(_("\\[\\]"));
  345. continue;
  346. }
  347. pattern.append(QLatin1Char('['));
  348. escape = true;
  349. embraced = true;
  350. }
  351. if (embraced) {
  352. if (range) {
  353. QChar c2 = pattern[pattern.size() - 2];
  354. pattern.remove(pattern.size() - 2, 2);
  355. pattern.append(c2.toUpper() + QLatin1Char('-') + c.toUpper());
  356. pattern.append(c2.toLower() + QLatin1Char('-') + c.toLower());
  357. range = false;
  358. } else if (escape) {
  359. escape = false;
  360. pattern.append(c);
  361. } else if (c == QLatin1Char('\\')) {
  362. escape = true;
  363. } else if (c == QLatin1Char(']')) {
  364. pattern.append(QLatin1Char(']'));
  365. embraced = false;
  366. } else if (c == QLatin1Char('-')) {
  367. range = ignorecase && pattern[pattern.size() - 1].isLetter();
  368. pattern.append(QLatin1Char('-'));
  369. } else if (c.isLetter() && ignorecase) {
  370. pattern.append(c.toLower()).append(c.toUpper());
  371. } else {
  372. pattern.append(c);
  373. }
  374. } else if (QString::fromLatin1("(){}+|?").indexOf(c) != -1) {
  375. if (c == QLatin1Char('{')) {
  376. curly = escape;
  377. } else if (c == QLatin1Char('}') && curly) {
  378. curly = false;
  379. escape = true;
  380. }
  381. if (escape)
  382. escape = false;
  383. else
  384. pattern.append(QLatin1Char('\\'));
  385. pattern.append(c);
  386. } else if (escape) {
  387. // escape expression
  388. escape = false;
  389. if (c == QLatin1Char('<') || c == QLatin1Char('>'))
  390. pattern.append(_("\\b"));
  391. else if (c == QLatin1Char('a'))
  392. pattern.append(_("[a-zA-Z]"));
  393. else if (c == QLatin1Char('A'))
  394. pattern.append(_("[^a-zA-Z]"));
  395. else if (c == QLatin1Char('h'))
  396. pattern.append(_("[A-Za-z_]"));
  397. else if (c == QLatin1Char('H'))
  398. pattern.append(_("[^A-Za-z_]"));
  399. else if (c == QLatin1Char('c') || c == QLatin1Char('C'))
  400. ignorecase = (c == QLatin1Char('c'));
  401. else if (c == QLatin1Char('l'))
  402. pattern.append(_("[a-z]"));
  403. else if (c == QLatin1Char('L'))
  404. pattern.append(_("[^a-z]"));
  405. else if (c == QLatin1Char('o'))
  406. pattern.append(_("[0-7]"));
  407. else if (c == QLatin1Char('O'))
  408. pattern.append(_("[^0-7]"));
  409. else if (c == QLatin1Char('u'))
  410. pattern.append(_("[A-Z]"));
  411. else if (c == QLatin1Char('U'))
  412. pattern.append(_("[^A-Z]"));
  413. else if (c == QLatin1Char('x'))
  414. pattern.append(_("[0-9A-Fa-f]"));
  415. else if (c == QLatin1Char('X'))
  416. pattern.append(_("[^0-9A-Fa-f]"));
  417. else if (c == QLatin1Char('='))
  418. pattern.append(_("?"));
  419. else
  420. pattern.append(QLatin1Char('\\') + c);
  421. } else {
  422. // unescaped expression
  423. if (c == QLatin1Char('\\'))
  424. escape = true;
  425. else if (c == QLatin1Char('['))
  426. brace = true;
  427. else if (c.isLetter() && ignorecase)
  428. pattern.append(QLatin1Char('[') + c.toLower() + c.toUpper() + QLatin1Char(']'));
  429. else
  430. pattern.append(c);
  431. }
  432. }
  433. if (escape)
  434. pattern.append(QLatin1Char('\\'));
  435. else if (brace)
  436. pattern.append(QLatin1Char('['));
  437. return QRegExp(pattern);
  438. }
  439. static bool afterEndOfLine(const QTextDocument *doc, int position)
  440. {
  441. return doc->characterAt(position) == ParagraphSeparator
  442. && doc->findBlock(position).length() > 1;
  443. }
  444. static void searchForward(QTextCursor *tc, QRegExp &needleExp, int *repeat)
  445. {
  446. const QTextDocument *doc = tc->document();
  447. const int startPos = tc->position();
  448. // Search from beginning of line so that matched text is the same.
  449. tc->movePosition(StartOfLine);
  450. // forward to current position
  451. *tc = doc->find(needleExp, *tc);
  452. while (!tc->isNull() && tc->anchor() < startPos) {
  453. if (!tc->hasSelection())
  454. tc->movePosition(Right);
  455. if (tc->atBlockEnd())
  456. tc->movePosition(Right);
  457. *tc = doc->find(needleExp, *tc);
  458. }
  459. if (tc->isNull())
  460. return;
  461. --*repeat;
  462. while (*repeat > 0) {
  463. if (!tc->hasSelection())
  464. tc->movePosition(Right);
  465. if (tc->atBlockEnd())
  466. tc->movePosition(Right);
  467. *tc = doc->find(needleExp, *tc);
  468. if (tc->isNull())
  469. return;
  470. --*repeat;
  471. }
  472. if (!tc->isNull() && afterEndOfLine(doc, tc->anchor()))
  473. tc->movePosition(Left);
  474. }
  475. static void searchBackward(QTextCursor *tc, QRegExp &needleExp, int *repeat)
  476. {
  477. // Search from beginning of line so that matched text is the same.
  478. QTextBlock block = tc->block();
  479. QString line = block.text();
  480. int i = line.indexOf(needleExp, 0);
  481. while (i != -1 && i < tc->positionInBlock()) {
  482. --*repeat;
  483. i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength()));
  484. if (i == line.size())
  485. i = -1;
  486. }
  487. if (i == tc->positionInBlock())
  488. --*repeat;
  489. while (*repeat > 0) {
  490. block = block.previous();
  491. if (!block.isValid())
  492. break;
  493. line = block.text();
  494. i = line.indexOf(needleExp, 0);
  495. while (i != -1) {
  496. --*repeat;
  497. i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength()));
  498. if (i == line.size())
  499. i = -1;
  500. }
  501. }
  502. if (!block.isValid()) {
  503. *tc = QTextCursor();
  504. return;
  505. }
  506. i = line.indexOf(needleExp, 0);
  507. while (*repeat < 0) {
  508. i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength()));
  509. ++*repeat;
  510. }
  511. tc->setPosition(block.position() + i);
  512. }
  513. static bool substituteText(QString *text, QRegExp &pattern, const QString &replacement,
  514. bool global)
  515. {
  516. bool substituted = false;
  517. int pos = 0;
  518. while (true) {
  519. pos = pattern.indexIn(*text, pos, QRegExp::CaretAtZero);
  520. if (pos == -1)
  521. break;
  522. substituted = true;
  523. QString matched = text->mid(pos, pattern.cap(0).size());
  524. QString repl;
  525. bool escape = false;
  526. // insert captured texts
  527. for (int i = 0; i < replacement.size(); ++i) {
  528. const QChar &c = replacement[i];
  529. if (escape) {
  530. escape = false;
  531. if (c.isDigit()) {
  532. if (c.digitValue() <= pattern.captureCount())
  533. repl += pattern.cap(c.digitValue());
  534. } else {
  535. repl += c;
  536. }
  537. } else {
  538. if (c == QLatin1Char('\\'))
  539. escape = true;
  540. else if (c == QLatin1Char('&'))
  541. repl += pattern.cap(0);
  542. else
  543. repl += c;
  544. }
  545. }
  546. text->replace(pos, matched.size(), repl);
  547. pos += qMax(1, repl.size());
  548. if (pos >= text->size() || !global)
  549. break;
  550. }
  551. return substituted;
  552. }
  553. static int findUnescaped(QChar c, const QString &line, int from)
  554. {
  555. for (int i = from; i < line.size(); ++i) {
  556. if (line.at(i) == c && (i == 0 || line.at(i - 1) != QLatin1Char('\\')))
  557. return i;
  558. }
  559. return -1;
  560. }
  561. static void setClipboardData(const QString &content, RangeMode mode,
  562. QClipboard::Mode clipboardMode)
  563. {
  564. QClipboard *clipboard = QApplication::clipboard();
  565. char vimRangeMode = mode;
  566. QByteArray bytes1;
  567. bytes1.append(vimRangeMode);
  568. bytes1.append(content.toUtf8());
  569. QByteArray bytes2;
  570. bytes2.append(vimRangeMode);
  571. bytes2.append("utf-8");
  572. bytes2.append('\0');
  573. bytes2.append(content.toUtf8());
  574. QMimeData *data = new QMimeData;
  575. data->setText(content);
  576. data->setData(vimMimeText, bytes1);
  577. data->setData(vimMimeTextEncoded, bytes2);
  578. clipboard->setMimeData(data, clipboardMode);
  579. }
  580. static const QMap<QString, int> &vimKeyNames()
  581. {
  582. static QMap<QString, int> k;
  583. if (!k.isEmpty())
  584. return k;
  585. // FIXME: Should be value of mapleader.
  586. k.insert(_("LEADER"), Key_Backslash);
  587. k.insert(_("SPACE"), Key_Space);
  588. k.insert(_("TAB"), Key_Tab);
  589. k.insert(_("NL"), Key_Return);
  590. k.insert(_("NEWLINE"), Key_Return);
  591. k.insert(_("LINEFEED"), Key_Return);
  592. k.insert(_("LF"), Key_Return);
  593. k.insert(_("CR"), Key_Return);
  594. k.insert(_("RETURN"), Key_Return);
  595. k.insert(_("ENTER"), Key_Return);
  596. k.insert(_("BS"), Key_Backspace);
  597. k.insert(_("BACKSPACE"), Key_Backspace);
  598. k.insert(_("ESC"), Key_Escape);
  599. k.insert(_("BAR"), Key_Bar);
  600. k.insert(_("BSLASH"), Key_Backslash);
  601. k.insert(_("DEL"), Key_Delete);
  602. k.insert(_("DELETE"), Key_Delete);
  603. k.insert(_("KDEL"), Key_Delete);
  604. k.insert(_("UP"), Key_Up);
  605. k.insert(_("DOWN"), Key_Down);
  606. k.insert(_("LEFT"), Key_Left);
  607. k.insert(_("RIGHT"), Key_Right);
  608. k.insert(_("LT"), Key_Less);
  609. k.insert(_("F1"), Key_F1);
  610. k.insert(_("F2"), Key_F2);
  611. k.insert(_("F3"), Key_F3);
  612. k.insert(_("F4"), Key_F4);
  613. k.insert(_("F5"), Key_F5);
  614. k.insert(_("F6"), Key_F6);
  615. k.insert(_("F7"), Key_F7);
  616. k.insert(_("F8"), Key_F8);
  617. k.insert(_("F9"), Key_F9);
  618. k.insert(_("F10"), Key_F10);
  619. k.insert(_("F11"), Key_F11);
  620. k.insert(_("F12"), Key_F12);
  621. k.insert(_("F13"), Key_F13);
  622. k.insert(_("F14"), Key_F14);
  623. k.insert(_("F15"), Key_F15);
  624. k.insert(_("F16"), Key_F16);
  625. k.insert(_("F17"), Key_F17);
  626. k.insert(_("F18"), Key_F18);
  627. k.insert(_("F19"), Key_F19);
  628. k.insert(_("F20"), Key_F20);
  629. k.insert(_("F21"), Key_F21);
  630. k.insert(_("F22"), Key_F22);
  631. k.insert(_("F23"), Key_F23);
  632. k.insert(_("F24"), Key_F24);
  633. k.insert(_("F25"), Key_F25);
  634. k.insert(_("F26"), Key_F26);
  635. k.insert(_("F27"), Key_F27);
  636. k.insert(_("F28"), Key_F28);
  637. k.insert(_("F29"), Key_F29);
  638. k.insert(_("F30"), Key_F30);
  639. k.insert(_("F31"), Key_F31);
  640. k.insert(_("F32"), Key_F32);
  641. k.insert(_("F33"), Key_F33);
  642. k.insert(_("F34"), Key_F34);
  643. k.insert(_("F35"), Key_F35);
  644. k.insert(_("INSERT"), Key_Insert);
  645. k.insert(_("INS"), Key_Insert);
  646. k.insert(_("KINSERT"), Key_Insert);
  647. k.insert(_("HOME"), Key_Home);
  648. k.insert(_("END"), Key_End);
  649. k.insert(_("PAGEUP"), Key_PageUp);
  650. k.insert(_("PAGEDOWN"), Key_PageDown);
  651. k.insert(_("KPLUS"), Key_Plus);
  652. k.insert(_("KMINUS"), Key_Minus);
  653. k.insert(_("KDIVIDE"), Key_Slash);
  654. k.insert(_("KMULTIPLY"), Key_Asterisk);
  655. k.insert(_("KENTER"), Key_Enter);
  656. k.insert(_("KPOINT"), Key_Period);
  657. return k;
  658. }
  659. Range::Range()
  660. : beginPos(-1), endPos(-1), rangemode(RangeCharMode)
  661. {}
  662. Range::Range(int b, int e, RangeMode m)
  663. : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
  664. {}
  665. QString Range::toString() const
  666. {
  667. return QString::fromLatin1("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
  668. .arg(rangemode);
  669. }
  670. QDebug operator<<(QDebug ts, const Range &range)
  671. {
  672. return ts << '[' << range.beginPos << ',' << range.endPos << ']';
  673. }
  674. ExCommand::ExCommand(const QString &c, const QString &a, const Range &r)
  675. : cmd(c), hasBang(false), args(a), range(r), count(1)
  676. {}
  677. bool ExCommand::matches(const QString &min, const QString &full) const
  678. {
  679. return cmd.startsWith(min) && full.startsWith(cmd);
  680. }
  681. QDebug operator<<(QDebug ts, const ExCommand &cmd)
  682. {
  683. return ts << cmd.cmd << ' ' << cmd.args << ' ' << cmd.range;
  684. }
  685. QDebug operator<<(QDebug ts, const QList<QTextEdit::ExtraSelection> &sels)
  686. {
  687. foreach (const QTextEdit::ExtraSelection &sel, sels)
  688. ts << "SEL: " << sel.cursor.anchor() << sel.cursor.position();
  689. return ts;
  690. }
  691. QString quoteUnprintable(const QString &ba)
  692. {
  693. QString res;
  694. for (int i = 0, n = ba.size(); i != n; ++i) {
  695. const QChar c = ba.at(i);
  696. const int cc = c.unicode();
  697. if (c.isPrint())
  698. res += c;
  699. else if (cc == QLatin1Char('\n'))
  700. res += _("<CR>");
  701. else
  702. res += QString::fromLatin1("\\x%1").arg(c.unicode(), 2, 16, QLatin1Char('0'));
  703. }
  704. return res;
  705. }
  706. static bool startsWithWhitespace(const QString &str, int col)
  707. {
  708. QTC_ASSERT(str.size() >= col, return false);
  709. for (int i = 0; i < col; ++i) {
  710. uint u = str.at(i).unicode();
  711. if (u != QLatin1Char(' ') && u != QLatin1Char('\t'))
  712. return false;
  713. }
  714. return true;
  715. }
  716. inline QString msgMarkNotSet(const QString &text)
  717. {
  718. return FakeVimHandler::tr("Mark '%1' not set").arg(text);
  719. }
  720. class Input
  721. {
  722. public:
  723. // Remove some extra "information" on Mac.
  724. static int cleanModifier(int m) { return m & ~Qt::KeypadModifier; }
  725. Input()
  726. : m_key(0), m_xkey(0), m_modifiers(0) {}
  727. explicit Input(QChar x)
  728. : m_key(x.unicode()), m_xkey(x.unicode()), m_modifiers(0), m_text(x)
  729. {
  730. if (x.isUpper())
  731. m_modifiers = Qt::ShiftModifier;
  732. else if (x.isLower())
  733. m_key = x.toUpper().unicode();
  734. }
  735. Input(int k, int m, const QString &t = QString())
  736. : m_key(k), m_modifiers(cleanModifier(m)), m_text(t)
  737. {
  738. // On Mac, QKeyEvent::text() returns non-empty strings for
  739. // cursor keys. This breaks some of the logic later on
  740. // relying on text() being empty for "special" keys.
  741. // FIXME: Check the real conditions.
  742. if (m_text.size() == 1 && m_text.at(0).unicode() < ' ')
  743. m_text.clear();
  744. // Set text only if input is ascii key without control modifier.
  745. if (m_text.isEmpty() && k <= 0x7f && (m & (HostOsInfo::controlModifier())) == 0) {
  746. QChar c = QChar::fromAscii(k);
  747. m_text = QString((m & ShiftModifier) != 0 ? c.toUpper() : c.toLower());
  748. }
  749. // m_xkey is only a cache.
  750. m_xkey = (m_text.size() == 1 ? m_text.at(0).unicode() : m_key);
  751. }
  752. bool isValid() const
  753. {
  754. return m_key != 0 || !m_text.isNull();
  755. }
  756. bool isDigit() const
  757. {
  758. return m_xkey >= QLatin1Char('0') && m_xkey <= QLatin1Char('9');
  759. }
  760. bool isKey(int c) const
  761. {
  762. return !m_modifiers && m_key == c;
  763. }
  764. bool isBackspace() const
  765. {
  766. return m_key == Key_Backspace || isControl('h');
  767. }
  768. bool isReturn() const
  769. {
  770. return m_key == QLatin1Char('\n') || m_key == Key_Return || m_key == Key_Enter;
  771. }
  772. bool isEscape() const
  773. {
  774. return isKey(Key_Escape) || isKey(27) || isControl('c')
  775. || isControl(Key_BracketLeft);
  776. }
  777. bool is(int c) const
  778. {
  779. return m_xkey == c && m_modifiers != int(HostOsInfo::controlModifier());
  780. }
  781. bool isControl(int c) const
  782. {
  783. return m_modifiers == int(HostOsInfo::controlModifier())
  784. && (m_xkey == c || m_xkey + 32 == c || m_xkey + 64 == c || m_xkey + 96 == c);
  785. }
  786. bool isShift(int c) const
  787. {
  788. return m_modifiers == Qt::ShiftModifier && m_xkey == c;
  789. }
  790. bool operator<(const Input &a) const
  791. {
  792. if (m_key != a.m_key)
  793. return m_key < a.m_key;
  794. // Text for some mapped key cannot be determined (e.g. <C-J>) so if text is not set for
  795. // one of compared keys ignore it.
  796. if (!m_text.isEmpty() && !a.m_text.isEmpty())
  797. return m_text < a.m_text;
  798. return m_modifiers < a.m_modifiers;
  799. }
  800. bool operator==(const Input &a) const
  801. {
  802. return !(*this < a || a < *this);
  803. }
  804. bool operator!=(const Input &a) const { return !operator==(a); }
  805. QString text() const { return m_text; }
  806. QChar asChar() const
  807. {
  808. return (m_text.size() == 1 ? m_text.at(0) : QChar());
  809. }
  810. int key() const { return m_key; }
  811. QChar raw() const
  812. {
  813. if (m_key == Key_Tab)
  814. return QLatin1Char('\t');
  815. if (m_key == Key_Return)
  816. return QLatin1Char('\n');
  817. return m_key;
  818. }
  819. QString toString() const
  820. {
  821. bool hasCtrl = m_modifiers & int(HostOsInfo::controlModifier());
  822. QString key = vimKeyNames().key(m_key);
  823. if (key.isEmpty())
  824. key = QChar(m_xkey);
  825. else
  826. key = QLatin1Char('<') + key + QLatin1Char('>');
  827. return (hasCtrl ? QString::fromLatin1("^") : QString()) + key;
  828. }
  829. QDebug dump(QDebug ts) const
  830. {
  831. return ts << m_key << '-' << m_modifiers << '-'
  832. << quoteUnprintable(m_text);
  833. }
  834. private:
  835. int m_key;
  836. int m_xkey;
  837. int m_modifiers;
  838. QString m_text;
  839. };
  840. // mapping to <Nop> (do nothing)
  841. static const Input Nop(-1, -1, QString());
  842. QDebug operator<<(QDebug ts, const Input &input) { return input.dump(ts); }
  843. class Inputs : public QVector<Input>
  844. {
  845. public:
  846. Inputs() : m_noremap(true), m_silent(false) {}
  847. explicit Inputs(const QString &str, bool noremap = true, bool silent = false)
  848. : m_noremap(noremap), m_silent(silent)
  849. {
  850. parseFrom(str);
  851. }
  852. bool noremap() const { return m_noremap; }
  853. bool silent() const { return m_silent; }
  854. private:
  855. void parseFrom(const QString &str);
  856. bool m_noremap;
  857. bool m_silent;
  858. };
  859. static Input parseVimKeyName(const QString &keyName)
  860. {
  861. if (keyName.length() == 1)
  862. return Input(keyName.at(0));
  863. const QStringList keys = keyName.split(QLatin1Char('-'));
  864. const int len = keys.length();
  865. if (len == 1 && keys.at(0) == _("nop"))
  866. return Nop;
  867. int mods = NoModifier;
  868. for (int i = 0; i < len - 1; ++i) {
  869. const QString &key = keys[i].toUpper();
  870. if (key == _("S"))
  871. mods |= Qt::ShiftModifier;
  872. else if (key == _("C"))
  873. mods |= HostOsInfo::controlModifier();
  874. else
  875. return Input();
  876. }
  877. if (!keys.isEmpty()) {
  878. const QString key = keys.last();
  879. if (key.length() == 1) {
  880. // simple character
  881. QChar c = key.at(0).toUpper();
  882. return Input(c.unicode(), mods);
  883. }
  884. // find key name
  885. QMap<QString, int>::ConstIterator it = vimKeyNames().constFind(key.toUpper());
  886. if (it != vimKeyNames().end())
  887. return Input(*it, mods);
  888. }
  889. return Input();
  890. }
  891. void Inputs::parseFrom(const QString &str)
  892. {
  893. const int n = str.size();
  894. for (int i = 0; i < n; ++i) {
  895. uint c = str.at(i).unicode();
  896. if (c == QLatin1Char('<')) {
  897. int j = str.indexOf(QLatin1Char('>'), i);
  898. Input input;
  899. if (j != -1) {
  900. const QString key = str.mid(i+1, j - i - 1);
  901. if (!key.contains(QLatin1Char('<')))
  902. input = parseVimKeyName(key);
  903. }
  904. if (input.isValid()) {
  905. append(input);
  906. i = j;
  907. } else {
  908. append(Input(QLatin1Char(c)));
  909. }
  910. } else {
  911. append(Input(QLatin1Char(c)));
  912. }
  913. }
  914. }
  915. class History
  916. {
  917. public:
  918. History() : m_items(QString()), m_index(0) {}
  919. void append(const QString &item);
  920. const QString &move(const QStringRef &prefix, int skip);
  921. const QString &current() const { return m_items[m_index]; }
  922. const QStringList &items() const { return m_items; }
  923. void restart() { m_index = m_items.size() - 1; }
  924. private:
  925. // Last item is always empty or current search prefix.
  926. QStringList m_items;
  927. int m_index;
  928. };
  929. void History::append(const QString &item)
  930. {
  931. if (item.isEmpty())
  932. return;
  933. m_items.pop_back();
  934. m_items.removeAll(item);
  935. m_items << item << QString();
  936. restart();
  937. }
  938. const QString &History::move(const QStringRef &prefix, int skip)
  939. {
  940. if (!current().startsWith(prefix))
  941. restart();
  942. if (m_items.last() != prefix)
  943. m_items[m_items.size() - 1] = prefix.toString();
  944. int i = m_index + skip;
  945. if (!prefix.isEmpty())
  946. for (; i >= 0 && i < m_items.size() && !m_items[i].startsWith(prefix); i += skip);
  947. if (i >= 0 && i < m_items.size())
  948. m_index = i;
  949. return current();
  950. }
  951. // Command line buffer with prompt (i.e. :, / or ? characters), text contents and cursor position.
  952. class CommandBuffer
  953. {
  954. public:
  955. CommandBuffer() : m_pos(0), m_anchor(0), m_userPos(0), m_historyAutoSave(true) {}
  956. void setPrompt(const QChar &prompt) { m_prompt = prompt; }
  957. void setContents(const QString &s) { m_buffer = s; m_anchor = m_pos = s.size(); }
  958. void setContents(const QString &s, int pos, int anchor = -1)
  959. {
  960. m_buffer = s; m_pos = m_userPos = pos; m_anchor = anchor >= 0 ? anchor : pos;
  961. }
  962. QStringRef userContents() const { return m_buffer.leftRef(m_userPos); }
  963. const QChar &prompt() const { return m_prompt; }
  964. const QString &contents() const { return m_buffer; }
  965. bool isEmpty() const { return m_buffer.isEmpty(); }
  966. int cursorPos() const { return m_pos; }
  967. int anchorPos() const { return m_anchor; }
  968. bool hasSelection() const { return m_pos != m_anchor; }
  969. void insertChar(QChar c) { m_buffer.insert(m_pos++, c); m_anchor = m_userPos = m_pos; }
  970. void insertText(const QString &s)
  971. {
  972. m_buffer.insert(m_pos, s); m_anchor = m_userPos = m_pos = m_pos + s.size();
  973. }
  974. void deleteChar() { if (m_pos) m_buffer.remove(--m_pos, 1); m_anchor = m_userPos = m_pos; }
  975. void moveLeft() { if (m_pos) m_userPos = --m_pos; }
  976. void moveRight() { if (m_pos < m_buffer.size()) m_userPos = ++m_pos; }
  977. void moveStart() { m_userPos = m_pos = 0; }
  978. void moveEnd() { m_userPos = m_pos = m_buffer.size(); }
  979. void setHistoryAutoSave(bool autoSave) { m_historyAutoSave = autoSave; }
  980. void historyDown() { setContents(m_history.move(userContents(), 1)); }
  981. void historyUp() { setContents(m_history.move(userContents(), -1)); }
  982. const QStringList &historyItems() const { return m_history.items(); }
  983. void historyPush(const QString &item = QString())
  984. {
  985. m_history.append(item.isNull() ? contents() : item);
  986. }
  987. void clear()
  988. {
  989. if (m_historyAutoSave)
  990. historyPush();
  991. m_buffer.clear();
  992. m_anchor = m_userPos = m_pos = 0;
  993. }
  994. QString display() const
  995. {
  996. QString msg(m_prompt);
  997. for (int i = 0; i != m_buffer.size(); ++i) {
  998. const QChar c = m_buffer.at(i);
  999. if (c.unicode() < 32) {
  1000. msg += QLatin1Char('^');
  1001. msg += QLatin1Char(c.unicode() + 64);
  1002. } else {
  1003. msg += c;
  1004. }
  1005. }
  1006. return msg;
  1007. }
  1008. void deleteSelected()
  1009. {
  1010. if (m_pos < m_anchor) {
  1011. m_buffer.remove(m_pos, m_anchor - m_pos);
  1012. m_anchor = m_pos;
  1013. } else {
  1014. m_buffer.remove(m_anchor, m_pos - m_anchor);
  1015. m_pos = m_anchor;
  1016. }
  1017. }
  1018. bool handleInput(const Input &input)
  1019. {
  1020. if (input.isShift(Key_Left)) {
  1021. moveLeft();
  1022. } else if (input.isShift(Key_Right)) {
  1023. moveRight();
  1024. } else if (input.isShift(Key_Home)) {
  1025. moveStart();
  1026. } else if (input.isShift(Key_End)) {
  1027. moveEnd();
  1028. } else if (input.isKey(Key_Left)) {
  1029. moveLeft();
  1030. m_anchor = m_pos;
  1031. } else if (input.isKey(Key_Right)) {
  1032. moveRight();
  1033. m_anchor = m_pos;
  1034. } else if (input.isKey(Key_Home)) {
  1035. moveStart();
  1036. m_anchor = m_pos;
  1037. } else if (input.isKey(Key_End)) {
  1038. moveEnd();
  1039. m_anchor = m_pos;
  1040. } else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
  1041. historyUp();
  1042. } else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
  1043. historyDown();
  1044. } else if (input.isKey(Key_Delete)) {
  1045. if (hasSelection()) {
  1046. deleteSelected();
  1047. } else {
  1048. if (m_pos < m_buffer.size())
  1049. m_buffer.remove(m_pos, 1);
  1050. else
  1051. deleteChar();
  1052. }
  1053. } else if (!input.text().isEmpty()) {
  1054. if (hasSelection())
  1055. deleteSelected();
  1056. insertText(input.text());
  1057. } else {
  1058. return false;
  1059. }
  1060. return true;
  1061. }
  1062. private:
  1063. QString m_buffer;
  1064. QChar m_prompt;
  1065. History m_history;
  1066. int m_pos;
  1067. int m_anchor;
  1068. int m_userPos; // last position of inserted text (for retrieving history items)
  1069. bool m_historyAutoSave; // store items to history on clear()?
  1070. };
  1071. // Mappings for a specific mode (trie structure)
  1072. class ModeMapping : public QMap<Input, ModeMapping>
  1073. {
  1074. public:
  1075. const Inputs &value() const { return m_value; }
  1076. void setValue(const Inputs &value) { m_value = value; }
  1077. private:
  1078. Inputs m_value;
  1079. };
  1080. // Mappings for all modes
  1081. typedef QHash<char, ModeMapping> Mappings;
  1082. // Iterator for mappings
  1083. class MappingsIterator : public QVector<ModeMapping::Iterator>
  1084. {
  1085. public:
  1086. MappingsIterator(Mappings *mappings, char mode = -1, const Inputs &inputs = Inputs())
  1087. : m_parent(mappings)
  1088. {
  1089. reset(mode);
  1090. walk(inputs);
  1091. }
  1092. // Reset iterator state. Keep previous mode if 0.
  1093. void reset(char mode = 0)
  1094. {
  1095. clear();
  1096. m_lastValid = -1;
  1097. m_invalidInputCount = 0;
  1098. if (mode != 0) {
  1099. m_mode = mode;
  1100. if (mode != -1)
  1101. m_modeMapping = m_parent->find(mode);
  1102. }
  1103. }
  1104. bool isValid() const { return !empty(); }
  1105. // Return true if mapping can be extended.
  1106. bool canExtend() const { return isValid() && !last()->empty(); }
  1107. // Return true if this mapping can be used.
  1108. bool isComplete() const { return m_lastValid != -1; }
  1109. // Return size of current map.
  1110. int mapLength() const { return m_lastValid + 1; }
  1111. int invalidInputCount() const { return m_invalidInputCount; }
  1112. bool walk(const Input &input)
  1113. {
  1114. if (m_modeMapping == m_parent->end())
  1115. return false;
  1116. if (!input.isValid()) {
  1117. m_invalidInputCount += 1;
  1118. return true;
  1119. }
  1120. ModeMapping::Iterator it;
  1121. if (isValid()) {
  1122. it = last()->find(input);
  1123. if (it == last()->end())
  1124. return false;
  1125. } else {
  1126. it = m_modeMapping->find(input);
  1127. if (it == m_modeMapping->end())
  1128. return false;
  1129. }
  1130. if (!it->value().isEmpty())
  1131. m_lastValid = size();
  1132. append(it);
  1133. return true;
  1134. }
  1135. bool walk(const Inputs &inputs)
  1136. {
  1137. foreach (const Input &input, inputs) {
  1138. if (!walk(input))
  1139. return false;
  1140. }
  1141. return true;
  1142. }
  1143. // Return current mapped value. Iterator must be valid.
  1144. const Inputs &inputs() const
  1145. {
  1146. return at(m_lastValid)->value();
  1147. }
  1148. void remove()
  1149. {
  1150. if (isValid()) {
  1151. if (canExtend()) {
  1152. last()->setValue(Inputs());
  1153. } else {
  1154. if (size() > 1) {
  1155. while (last()->empty()) {
  1156. at(size() - 2)->erase(last());
  1157. pop_back();
  1158. if (size() == 1 || !last()->value().isEmpty())
  1159. break;
  1160. }
  1161. if (last()->empty() && last()->value().isEmpty())
  1162. m_modeMapping->erase(last());
  1163. } else if (last()->empty() && !last()->value().isEmpty()) {
  1164. m_modeMapping->erase(last());
  1165. }
  1166. }
  1167. }
  1168. }
  1169. void setInputs(const Inputs &key, const Inputs &inputs, bool unique = false)
  1170. {
  1171. ModeMapping *current = &(*m_parent)[m_mode];
  1172. foreach (const Input &input, key)
  1173. current = &(*current)[input];
  1174. if (!unique || current->value().isEmpty())
  1175. current->setValue(inputs);
  1176. }
  1177. private:
  1178. Mappings *m_parent;
  1179. Mappings::Iterator m_modeMapping;
  1180. int m_lastValid;
  1181. int m_invalidInputCount;
  1182. char m_mode;
  1183. };
  1184. // state of current mapping
  1185. struct MappingState {
  1186. MappingState()
  1187. : maxMapDepth(1000), noremap(false), silent(false) {}
  1188. MappingState(int depth, bool noremap, bool silent)
  1189. : maxMapDepth(depth), noremap(noremap), silent(silent) {}
  1190. int maxMapDepth;
  1191. bool noremap;
  1192. bool silent;
  1193. };
  1194. class FakeVimHandler::Private : public QObject
  1195. {
  1196. Q_OBJECT
  1197. public:
  1198. Private(FakeVimHandler *parent, QWidget *widget);
  1199. EventResult handleEvent(QKeyEvent *ev);
  1200. bool wantsOverride(QKeyEvent *ev);
  1201. bool parseExCommmand(QString *line, ExCommand *cmd);
  1202. bool parseLineRange(QString *line, ExCommand *cmd);
  1203. int parseLineAddress(QString *cmd);
  1204. void parseRangeCount(const QString &line, Range *range) const;
  1205. void handleCommand(const QString &cmd); // Sets m_tc + handleExCommand
  1206. void handleExCommand(const QString &cmd);
  1207. void installEventFilter();
  1208. void passShortcuts(bool enable);
  1209. void setupWidget();
  1210. void restoreWidget(int tabSize);
  1211. friend class FakeVimHandler;
  1212. void init();
  1213. void focus();
  1214. void enterFakeVim(); // Call before any FakeVim processing (import cursor position from editor)
  1215. void leaveFakeVim(); // Call after any FakeVim processing (export cursor position to editor)
  1216. EventResult handleKey(const Input &input);
  1217. EventResult handleDefaultKey(const Input &input);
  1218. void handleMappedKeys();
  1219. void unhandleMappedKeys();
  1220. EventResult handleInsertMode(const Input &);
  1221. EventResult handleReplaceMode(const Input &);
  1222. EventResult handleCommandMode(const Input &);
  1223. // return true only if input in current mode and sub-mode was correctly handled
  1224. bool handleEscape();
  1225. bool handleNoSubMode(const Input &);
  1226. bool handleChangeDeleteSubModes(const Input &);
  1227. bool handleReplaceSubMode(const Input &);
  1228. bool handleFilterSubMode(const Input &);
  1229. bool handleRegisterSubMode(const Input &);
  1230. bool handleShiftSubMode(const Input &);
  1231. bool handleChangeCaseSubMode(const Input &);
  1232. bool handleWindowSubMode(const Input &);
  1233. bool handleYankSubMode(const Input &);
  1234. bool handleZSubMode(const Input &);
  1235. bool handleCapitalZSubMode(const Input &);
  1236. bool handleMovement(const Input &);
  1237. EventResult handleExMode(const Input &);
  1238. EventResult handleSearchSubSubMode(const Input &);
  1239. bool handleCommandSubSubMode(const Input &);
  1240. void fixSelection(); // Fix selection according to current range, move and command modes.
  1241. void finishMovement(const QString &dotCommandMovement = QString());
  1242. void finishMovement(const QString &dotCommandMovement, int count);
  1243. void resetCommandMode();
  1244. QTextCursor search(const SearchData &sd, int startPos, int count, bool showMessages);
  1245. void search(const SearchData &sd, bool showMessages = true);
  1246. void searchNext(bool forward = true);
  1247. void searchBalanced(bool forward, QChar needle, QChar other);
  1248. void highlightMatches(const QString &needle);
  1249. void stopIncrementalFind();
  1250. void updateFind(bool isComplete);
  1251. int mvCount() const { return m_mvcount.isEmpty() ? 1 : m_mvcount.toInt(); }
  1252. int opCount() const { return m_opcount.isEmpty() ? 1 : m_opcount.toInt(); }
  1253. int count() const { return mvCount() * opCount(); }
  1254. QTextBlock block() const { return cursor().block(); }
  1255. int leftDist() const { return position() - block().position(); }
  1256. int rightDist() const { return block().length() - leftDist() - 1; }
  1257. bool atBlockStart() const { return cursor().atBlockStart(); }
  1258. bool atBlockEnd() const { return cursor().atBlockEnd(); }
  1259. bool atEndOfLine() const { return atBlockEnd() && block().length() > 1; }
  1260. bool atDocumentEnd() const { return position() >= lastPositionInDocument(); }
  1261. bool atDocumentStart() const { return cursor().atStart(); }
  1262. bool atEmptyLine(const QTextCursor &tc = QTextCursor()) const;
  1263. bool atBoundary(bool end, bool simple, bool onlyWords = false,
  1264. const QTextCursor &tc = QTextCursor()) const;
  1265. bool atWordBoundary(bool end, bool simple, const QTextCursor &tc = QTextCursor()) const;
  1266. bool atWordStart(bool simple, const QTextCursor &tc = QTextCursor()) const;
  1267. bool atWordEnd(bool simple, const QTextCursor &tc = QTextCursor()) const;
  1268. bool isFirstNonBlankOnLine(int pos);
  1269. int lastPositionInDocument(bool ignoreMode = false) const; // Returns last valid position in doc.
  1270. int firstPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos
  1271. int lastPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos
  1272. int lineForPosition(int pos) const; // 1 based line, 0 based pos
  1273. QString lineContents(int line) const; // 1 based line
  1274. void setLineContents(int line, const QString &contents); // 1 based line
  1275. int blockBoundary(const QString &left, const QString &right,
  1276. bool end, int count) const; // end or start position of current code block
  1277. int lineNumber(const QTextBlock &block) const;
  1278. int linesOnScreen() const;
  1279. int columnsOnScreen() const;
  1280. int linesInDocument() const;
  1281. // The following use all zero-based counting.
  1282. int cursorLineOnScreen() const;
  1283. int cursorLine() const;
  1284. int cursorBlockNumber() const; // "." address
  1285. int physicalCursorColumn() const; // as stored in the data
  1286. int logicalCursorColumn() const; // as visible on screen
  1287. int physicalToLogicalColumn(int physical, const QString &text) const;
  1288. int logicalToPhysicalColumn(int logical, const QString &text) const;
  1289. Column cursorColumn() const; // as visible on screen
  1290. int firstVisibleLine() const;
  1291. void scrollToLine(int line);
  1292. void scrollUp(int count);
  1293. void scrollDown(int count) { scrollUp(-count); }
  1294. void alignViewportToCursor(Qt::AlignmentFlag align, int line = -1,
  1295. bool moveToNonBlank = false);
  1296. void setCursorPosition(const CursorPosition &p);
  1297. void setCursorPosition(QTextCursor *tc, const CursorPosition &p);
  1298. // Helper functions for indenting/
  1299. bool isElectricCharacter(QChar c) const;
  1300. void indentSelectedText(QChar lastTyped = QChar());
  1301. void indentText(const Range &range, QChar lastTyped = QChar());
  1302. void shiftRegionLeft(int repeat = 1);
  1303. void shiftRegionRight(int repeat = 1);
  1304. void moveToFirstNonBlankOnLine();
  1305. void moveToFirstNonBlankOnLine(QTextCursor *tc);
  1306. void moveToTargetColumn();
  1307. void setTargetColumn() {
  1308. m_targetColumn = logicalCursorColumn();
  1309. m_visualTargetColumn = m_targetColumn;
  1310. //qDebug() << "TARGET: " << m_targetColumn;
  1311. }
  1312. void moveToMatchingParanthesis();
  1313. void moveToBoundary(bool simple, bool forward = true);
  1314. void moveToNextBoundary(bool end, int count, bool simple, bool forward);
  1315. void moveToNextBoundaryStart(int count, bool simple, bool forward = true);
  1316. void moveToNextBoundaryEnd(int count, bool simple, bool forward = true);
  1317. void moveToBoundaryStart(int count, bool simple, bool forward = true);
  1318. void moveToBoundaryEnd(int count, bool simple, bool forward = true);
  1319. void moveToNextWord(bool end, int count, bool simple, bool forward, bool emptyLines);
  1320. void moveToNextWordStart(int count, bool simple, bool forward = true, bool emptyLines = true);
  1321. void moveToNextWordEnd(int count, bool simple, bool forward = true, bool emptyLines = true);
  1322. void moveToWordStart(int count, bool simple, bool forward = true, bool emptyLines = true);
  1323. void moveToWordEnd(int count, bool simple, bool forward = true, bool emptyLines = true);
  1324. // Convenience wrappers to reduce line noise.
  1325. void moveToStartOfLine();
  1326. void moveToEndOfLine();
  1327. void moveBehindEndOfLine();
  1328. void moveUp(int n = 1) { moveDown(-n); }
  1329. void moveDown(int n = 1);
  1330. void dump(const char *msg) const {
  1331. qDebug() << msg << "POS: " << anchor() << position()
  1332. << "EXT: " << m_oldExternalAnchor << m_oldExternalPosition
  1333. << "INT: " << m_oldInternalAnchor << m_oldInternalPosition
  1334. << "VISUAL: " << m_visualMode;
  1335. }
  1336. void moveRight(int n = 1) {
  1337. //dump("RIGHT 1");
  1338. QTextCursor tc = cursor();
  1339. tc.movePosition(Right, KeepAnchor, n);
  1340. setCursor(tc);
  1341. if (atEndOfLine())
  1342. emit q->fold(1, false);
  1343. //dump("RIGHT 2");
  1344. }
  1345. void moveLeft(int n = 1) {
  1346. QTextCursor tc = cursor();
  1347. tc.movePosition(Left, KeepAnchor, n);
  1348. setCursor(tc);
  1349. }
  1350. void setAnchor() {
  1351. QTextCursor tc = cursor();
  1352. tc.setPosition(tc.position(), MoveAnchor);
  1353. setCursor(tc);
  1354. }
  1355. void setAnchor(int position) {
  1356. QTextCursor tc = cursor();
  1357. tc.setPosition(tc.anchor(), MoveAnchor);
  1358. tc.setPosition(position, KeepAnchor);
  1359. setCursor(tc);
  1360. }
  1361. void setPosition(int position) {
  1362. QTextCursor tc = cursor();
  1363. tc.setPosition(position, KeepAnchor);
  1364. setCursor(tc);
  1365. }
  1366. void setAnchorAndPosition(int anchor, int position) {
  1367. QTextCursor tc = cursor();
  1368. tc.setPosition(anchor, MoveAnchor);
  1369. tc.setPosition(position, KeepAnchor);
  1370. setCursor(tc);
  1371. }
  1372. // Workaround for occational crash when setting text cursor while in edit block.
  1373. QTextCursor m_cursor;
  1374. QTextCursor cursor() const {
  1375. if (m_editBlockLevel > 0)
  1376. return m_cursor;
  1377. return EDITOR(textCursor());
  1378. }
  1379. void setCursor(const QTextCursor &tc) {
  1380. m_cursor = tc;
  1381. if (m_editBlockLevel == 0)
  1382. EDITOR(setTextCursor(tc));
  1383. }
  1384. bool moveToPreviousParagraph(int count) { return moveToNextParagraph(-count); }
  1385. bool moveToNextParagraph(int count);
  1386. bool handleFfTt(QString key);
  1387. void enterInsertMode();
  1388. void initVisualBlockInsertMode(QChar command);
  1389. void enterReplaceMode();
  1390. void enterCommandMode(Mode returnToMode = CommandMode);
  1391. void enterExMode(const QString &

Large files files are truncated, but you can click here to view the full file