PageRenderTime 70ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 2ms

/src/plugins/fakevim/fakevimhandler.cpp

https://bitbucket.org/kpozn/qtcreator
C++ | 5170 lines | 4487 code | 352 blank | 331 comment | 1237 complexity | 037e0e1e9718f5e62091d284438a0b25 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-3.0

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

  1. /**************************************************************************
  2. **
  3. ** This file is part of Qt Creator
  4. **
  5. ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
  6. **
  7. ** Contact: Nokia Corporation (info@qt.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 info@qt.nokia.com.
  30. **
  31. **************************************************************************/
  32. //
  33. // ATTENTION:
  34. //
  35. // 1 Please do not add any direct dependencies to other Qt Creator code here.
  36. // Instead emit signals and let the FakeVimPlugin channel the information to
  37. // Qt Creator. The idea is to keep this file here in a "clean" state that
  38. // allows easy reuse with any QTextEdit or QPlainTextEdit derived class.
  39. //
  40. // 2 There are a few auto tests located in ../../../tests/auto/fakevim.
  41. // Commands that are covered there are marked as "// tested" below.
  42. //
  43. // 3 Some conventions:
  44. //
  45. // Use 1 based line numbers and 0 based column numbers. Even though
  46. // the 1 based line are not nice it matches vim's and QTextEdit's 'line'
  47. // concepts.
  48. //
  49. // Do not pass QTextCursor etc around unless really needed. Convert
  50. // early to line/column.
  51. //
  52. // A QTextCursor is always between characters, whereas vi's cursor is always
  53. // over a character. FakeVim interprets the QTextCursor to be over the character
  54. // to the right of the QTextCursor's position().
  55. //
  56. // A current "region of interest"
  57. // spans between anchor(), (i.e. the character below anchor()), and
  58. // position(). The character below position() is not included
  59. // if the last movement command was exclusive (MoveExclusive).
  60. //
  61. #include "fakevimhandler.h"
  62. #include <utils/qtcassert.h>
  63. #include <QtCore/QDebug>
  64. #include <QtCore/QFile>
  65. #include <QtCore/QObject>
  66. #include <QtCore/QPointer>
  67. #include <QtCore/QProcess>
  68. #include <QtCore/QRegExp>
  69. #include <QtCore/QTextStream>
  70. #include <QtCore/QTimer>
  71. #include <QtCore/QtAlgorithms>
  72. #include <QtCore/QStack>
  73. #include <QtGui/QApplication>
  74. #include <QtGui/QInputMethodEvent>
  75. #include <QtGui/QKeyEvent>
  76. #include <QtGui/QLineEdit>
  77. #include <QtGui/QPlainTextEdit>
  78. #include <QtGui/QScrollBar>
  79. #include <QtGui/QTextBlock>
  80. #include <QtGui/QTextCursor>
  81. #include <QtGui/QTextDocumentFragment>
  82. #include <QtGui/QTextEdit>
  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() << << document()->availableUndoSteps() << 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 EDITOR(s) (m_textedit ? m_textedit->s : m_plaintextedit->s)
  117. enum {
  118. #ifdef Q_WS_MAC
  119. RealControlModifier = Qt::MetaModifier
  120. #else
  121. RealControlModifier = Qt::ControlModifier
  122. #endif
  123. };
  124. // Enforce use of RealControlModifier by breaking the compilation.
  125. #define MetaModifier // Use RealControlModifier instead
  126. #define ControlModifier // Use RealControlModifier instead
  127. const int ParagraphSeparator = 0x00002029;
  128. typedef QLatin1String _;
  129. using namespace Qt;
  130. /*! A \e Mode represents one of the basic modes of operation of FakeVim.
  131. */
  132. enum Mode
  133. {
  134. InsertMode,
  135. ReplaceMode,
  136. CommandMode,
  137. ExMode
  138. };
  139. /*! A \e SubMode is used for things that require one more data item
  140. and are 'nested' behind a \l Mode.
  141. */
  142. enum SubMode
  143. {
  144. NoSubMode,
  145. ChangeSubMode, // Used for c
  146. DeleteSubMode, // Used for d
  147. FilterSubMode, // Used for !
  148. IndentSubMode, // Used for =
  149. RegisterSubMode, // Used for "
  150. ShiftLeftSubMode, // Used for <
  151. ShiftRightSubMode, // Used for >
  152. TransformSubMode, // Used for ~/gu/gU
  153. WindowSubMode, // Used for Ctrl-w
  154. YankSubMode, // Used for y
  155. ZSubMode, // Used for z
  156. CapitalZSubMode, // Used for Z
  157. ReplaceSubMode, // Used for r
  158. OpenSquareSubMode, // Used for [
  159. CloseSquareSubMode // Used for ]
  160. };
  161. /*! A \e SubSubMode is used for things that require one more data item
  162. and are 'nested' behind a \l SubMode.
  163. */
  164. enum SubSubMode
  165. {
  166. NoSubSubMode,
  167. FtSubSubMode, // Used for f, F, t, T.
  168. MarkSubSubMode, // Used for m.
  169. BackTickSubSubMode, // Used for `.
  170. TickSubSubMode, // Used for '.
  171. InvertCaseSubSubMode, // Used for ~.
  172. DownCaseSubSubMode, // Used for gu.
  173. UpCaseSubSubMode, // Used for gU.
  174. TextObjectSubSubMode, // Used for thing like iw, aW, as etc.
  175. SearchSubSubMode
  176. };
  177. enum VisualMode
  178. {
  179. NoVisualMode,
  180. VisualCharMode,
  181. VisualLineMode,
  182. VisualBlockMode
  183. };
  184. enum MoveType
  185. {
  186. MoveExclusive,
  187. MoveInclusive,
  188. MoveLineWise
  189. };
  190. /*!
  191. \enum RangeMode
  192. The \e RangeMode serves as a means to define how the "Range" between
  193. the \l cursor and the \l anchor position is to be interpreted.
  194. \value RangeCharMode Entered by pressing \key v. The range includes
  195. all characters between cursor and anchor.
  196. \value RangeLineMode Entered by pressing \key V. The range includes
  197. all lines between the line of the cursor and
  198. the line of the anchor.
  199. \value RangeLineModeExclusice Like \l RangeLineMode, but keeps one
  200. newline when deleting.
  201. \value RangeBlockMode Entered by pressing \key Ctrl-v. The range includes
  202. all characters with line and column coordinates
  203. between line and columns coordinates of cursor and
  204. anchor.
  205. \value RangeBlockAndTailMode Like \l RangeBlockMode, but also includes
  206. all characters in the affected lines up to the end
  207. of these lines.
  208. */
  209. enum EventResult
  210. {
  211. EventHandled,
  212. EventUnhandled,
  213. EventPassedToCore
  214. };
  215. struct Column
  216. {
  217. Column(int p, int l) : physical(p), logical(l) {}
  218. int physical; // Number of characters in the data.
  219. int logical; // Column on screen.
  220. };
  221. QDebug operator<<(QDebug ts, const Column &col)
  222. {
  223. return ts << "(p: " << col.physical << ", l: " << col.logical << ")";
  224. }
  225. struct CursorPosition
  226. {
  227. // for jump history
  228. CursorPosition() : position(-1), scrollLine(-1) {}
  229. CursorPosition(int pos, int line) : position(pos), scrollLine(line) {}
  230. int position; // Position in document
  231. int scrollLine; // First visible line
  232. };
  233. struct Register
  234. {
  235. Register() : rangemode(RangeCharMode) {}
  236. Register(const QString &c) : contents(c), rangemode(RangeCharMode) {}
  237. Register(const QString &c, RangeMode m) : contents(c), rangemode(m) {}
  238. QString contents;
  239. RangeMode rangemode;
  240. };
  241. QDebug operator<<(QDebug ts, const Register &reg)
  242. {
  243. return ts << reg.contents;
  244. }
  245. struct SearchData
  246. {
  247. SearchData()
  248. {
  249. forward = true;
  250. mustMove = true;
  251. highlightMatches = true;
  252. highlightCursor = true;
  253. }
  254. QString needle;
  255. bool forward;
  256. bool mustMove;
  257. bool highlightMatches;
  258. bool highlightCursor;
  259. };
  260. Range::Range()
  261. : beginPos(-1), endPos(-1), rangemode(RangeCharMode)
  262. {}
  263. Range::Range(int b, int e, RangeMode m)
  264. : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
  265. {}
  266. QString Range::toString() const
  267. {
  268. return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
  269. .arg(rangemode);
  270. }
  271. QDebug operator<<(QDebug ts, const Range &range)
  272. {
  273. return ts << '[' << range.beginPos << ',' << range.endPos << ']';
  274. }
  275. ExCommand::ExCommand(const QString &c, const QString &a, const Range &r)
  276. : cmd(c), hasBang(false), args(a), range(r), count(1)
  277. {}
  278. bool ExCommand::matches(const QString &min, const QString &full) const
  279. {
  280. return cmd.startsWith(min) && full.startsWith(cmd);
  281. }
  282. QDebug operator<<(QDebug ts, const ExCommand &cmd)
  283. {
  284. return ts << cmd.cmd << ' ' << cmd.args << ' ' << cmd.range;
  285. }
  286. QDebug operator<<(QDebug ts, const QList<QTextEdit::ExtraSelection> &sels)
  287. {
  288. foreach (const QTextEdit::ExtraSelection &sel, sels)
  289. ts << "SEL: " << sel.cursor.anchor() << sel.cursor.position();
  290. return ts;
  291. }
  292. QString quoteUnprintable(const QString &ba)
  293. {
  294. QString res;
  295. for (int i = 0, n = ba.size(); i != n; ++i) {
  296. const QChar c = ba.at(i);
  297. const int cc = c.unicode();
  298. if (c.isPrint())
  299. res += c;
  300. else if (cc == '\n')
  301. res += _("<CR>");
  302. else
  303. res += QString("\\x%1").arg(c.unicode(), 2, 16, QLatin1Char('0'));
  304. }
  305. return res;
  306. }
  307. static bool startsWithWhitespace(const QString &str, int col)
  308. {
  309. QTC_ASSERT(str.size() >= col, return false);
  310. for (int i = 0; i < col; ++i) {
  311. uint u = str.at(i).unicode();
  312. if (u != ' ' && u != '\t')
  313. return false;
  314. }
  315. return true;
  316. }
  317. inline QString msgMarkNotSet(const QString &text)
  318. {
  319. return FakeVimHandler::tr("Mark '%1' not set").arg(text);
  320. }
  321. class Input
  322. {
  323. public:
  324. // Remove some extra "information" on Mac.
  325. static int cleanModifier(int m) { return m & ~Qt::KeypadModifier; }
  326. Input()
  327. : m_key(0), m_xkey(0), m_modifiers(0) {}
  328. explicit Input(QChar x)
  329. : m_key(x.unicode()), m_xkey(x.unicode()), m_modifiers(0), m_text(x) {}
  330. Input(int k, int m, const QString &t)
  331. : m_key(k), m_modifiers(cleanModifier(m)), m_text(t)
  332. {
  333. // On Mac, QKeyEvent::text() returns non-empty strings for
  334. // cursor keys. This breaks some of the logic later on
  335. // relying on text() being empty for "special" keys.
  336. // FIXME: Check the real conditions.
  337. if (m_text.size() == 1 && m_text.at(0).unicode() < ' ')
  338. m_text.clear();
  339. // m_xkey is only a cache.
  340. m_xkey = (m_text.size() == 1 ? m_text.at(0).unicode() : m_key);
  341. }
  342. bool isDigit() const
  343. {
  344. return m_xkey >= '0' && m_xkey <= '9';
  345. }
  346. bool isKey(int c) const
  347. {
  348. return !m_modifiers && m_key == c;
  349. }
  350. bool isBackspace() const
  351. {
  352. return m_key == Key_Backspace || isControl('h');
  353. }
  354. bool isReturn() const
  355. {
  356. return m_key == Key_Return;
  357. }
  358. bool isEscape() const
  359. {
  360. return isKey(Key_Escape) || isKey(27) || isControl('c')
  361. || isControl(Key_BracketLeft);
  362. }
  363. bool is(int c) const
  364. {
  365. return m_xkey == c && m_modifiers != RealControlModifier;
  366. }
  367. bool isControl(int c) const
  368. {
  369. return m_modifiers == RealControlModifier
  370. && (m_xkey == c || m_xkey + 32 == c || m_xkey + 64 == c || m_xkey + 96 == c);
  371. }
  372. bool isShift(int c) const
  373. {
  374. return m_modifiers == Qt::ShiftModifier && m_xkey == c;
  375. }
  376. bool operator==(const Input &a) const
  377. {
  378. return a.m_key == m_key && a.m_modifiers == m_modifiers
  379. && m_text == a.m_text;
  380. }
  381. bool operator!=(const Input &a) const { return !operator==(a); }
  382. QString text() const { return m_text; }
  383. QChar asChar() const
  384. {
  385. return (m_text.size() == 1 ? m_text.at(0) : QChar());
  386. }
  387. int key() const { return m_key; }
  388. QChar raw() const
  389. {
  390. if (m_key == Key_Tab)
  391. return '\t';
  392. if (m_key == Key_Return)
  393. return '\n';
  394. return m_key;
  395. }
  396. QDebug dump(QDebug ts) const
  397. {
  398. return ts << m_key << '-' << m_modifiers << '-'
  399. << quoteUnprintable(m_text);
  400. }
  401. private:
  402. int m_key;
  403. int m_xkey;
  404. int m_modifiers;
  405. QString m_text;
  406. };
  407. QDebug operator<<(QDebug ts, const Input &input) { return input.dump(ts); }
  408. class Inputs : public QVector<Input>
  409. {
  410. public:
  411. Inputs() {}
  412. explicit Inputs(const QString &str) { parseFrom(str); }
  413. void parseFrom(const QString &str);
  414. };
  415. void Inputs::parseFrom(const QString &str)
  416. {
  417. const int n = str.size();
  418. for (int i = 0; i < n; ++i) {
  419. uint c0 = str.at(i).unicode(), c1 = 0, c2 = 0, c3 = 0, c4 = 0, c5 = 0;
  420. if (i + 1 < n)
  421. c1 = str.at(i + 1).unicode();
  422. if (i + 2 < n)
  423. c2 = str.at(i + 2).unicode();
  424. if (i + 3 < n)
  425. c3 = str.at(i + 3).unicode();
  426. if (i + 4 < n)
  427. c4 = str.at(i + 4).unicode();
  428. if (i + 5 < n)
  429. c5 = str.at(i + 5).unicode();
  430. if (c0 == '<') {
  431. if ((c1 == 'C' || c1 == 'c') && c2 == '-' && c4 == '>') {
  432. uint c = (c3 < 90 ? c3 : c3 - 32);
  433. append(Input(c, RealControlModifier, QString(QChar(c - 64))));
  434. i += 4;
  435. } else if (c1 == 'C' && c2 == 'R' && c3 == '>') {
  436. append(Input(Key_Return, Qt::NoModifier, QString(QChar(13))));
  437. i += 3;
  438. } else if (c1 == 'E' && c2 == 's' && c3 == 'c' && c4 == '>') {
  439. append(Input(Key_Escape, Qt::NoModifier, QString(QChar(27))));
  440. i += 4;
  441. } else {
  442. append(Input(QLatin1Char(c0)));
  443. }
  444. } else {
  445. append(Input(QLatin1Char(c0)));
  446. }
  447. }
  448. }
  449. // This wraps a string and a "cursor position".
  450. class CommandBuffer
  451. {
  452. public:
  453. CommandBuffer() : m_pos(0) {}
  454. void clear() { m_buffer.clear(); m_pos = 0; }
  455. void setContents(const QString &s) { m_buffer = s; m_pos = s.size(); }
  456. QString contents() const { return m_buffer; }
  457. bool isEmpty() const { return m_buffer.isEmpty(); }
  458. int cursorPos() const { return m_pos; }
  459. void insertChar(QChar c) { m_buffer.insert(m_pos++, c); }
  460. void insertText(const QString &s) { m_buffer.insert(m_pos, s); m_pos += s.size(); }
  461. void deleteChar() { if (m_pos) m_buffer.remove(--m_pos, 1); }
  462. void moveLeft() { if (m_pos) --m_pos; }
  463. void moveRight() { if (m_pos < m_buffer.size()) ++m_pos; }
  464. void moveStart() { m_pos = 0; }
  465. void moveEnd() { m_pos = m_buffer.size(); }
  466. QString display() const
  467. {
  468. QString msg;
  469. for (int i = 0; i != m_buffer.size(); ++i) {
  470. const QChar c = m_buffer.at(i);
  471. if (c.unicode() < 32) {
  472. msg += '^';
  473. msg += QChar(c.unicode() + 64);
  474. } else {
  475. msg += c;
  476. }
  477. }
  478. return msg;
  479. }
  480. bool handleInput(const Input &input)
  481. {
  482. if (input.isKey(Key_Left)) {
  483. moveLeft();
  484. } else if (input.isKey(Key_Right)) {
  485. moveRight();
  486. } else if (input.isKey(Key_Home)) {
  487. moveStart();
  488. } else if (input.isKey(Key_End)) {
  489. moveEnd();
  490. } else if (input.isKey(Key_Delete)) {
  491. if (m_pos < m_buffer.size())
  492. m_buffer.remove(m_pos, 1);
  493. } else if (!input.text().isEmpty()) {
  494. insertText(input.text());
  495. } else {
  496. return false;
  497. }
  498. return true;
  499. }
  500. private:
  501. QString m_buffer;
  502. int m_pos;
  503. };
  504. class History
  505. {
  506. public:
  507. History() : m_index(0) {}
  508. void append(const QString &item);
  509. void down() { m_index = qMin(m_index + 1, m_items.size()); }
  510. void up() { m_index = qMax(m_index - 1, 0); }
  511. void restart() { m_index = m_items.size(); }
  512. QString current() const { return m_items.value(m_index, QString()); }
  513. QStringList items() const { return m_items; }
  514. private:
  515. QStringList m_items;
  516. int m_index;
  517. };
  518. void History::append(const QString &item)
  519. {
  520. if (item.isEmpty())
  521. return;
  522. m_items.removeAll(item);
  523. m_items.append(item); m_index = m_items.size() - 1;
  524. }
  525. // Mappings for a specific mode.
  526. class ModeMapping : public QList<QPair<Inputs, Inputs> >
  527. {
  528. public:
  529. ModeMapping() { test(); }
  530. void test()
  531. {
  532. //insert(Inputs() << Input('A') << Input('A'),
  533. // Inputs() << Input('x') << Input('x'));
  534. }
  535. void insert(const Inputs &from, const Inputs &to)
  536. {
  537. for (int i = 0; i != size(); ++i)
  538. if (at(i).first == from) {
  539. (*this)[i].second = to;
  540. return;
  541. }
  542. append(QPair<Inputs, Inputs>(from, to));
  543. }
  544. void remove(const Inputs &from)
  545. {
  546. for (int i = 0; i != size(); ++i)
  547. if (at(i).first == from) {
  548. removeAt(i);
  549. return;
  550. }
  551. }
  552. // Returns 'false' if more input is needed to decide whether a mapping
  553. // needs to be applied. If a decision can be made, return 'true',
  554. // and replace *input with the mapped data.
  555. bool mappingDone(Inputs *inputs) const
  556. {
  557. // FIXME: inefficient.
  558. for (int i = 0; i != size(); ++i) {
  559. const Inputs &haystack = at(i).first;
  560. // A mapping
  561. if (startsWith(haystack, *inputs)) {
  562. if (haystack.size() != inputs->size())
  563. return false; // This can be extended.
  564. // Actual mapping.
  565. *inputs = at(i).second;
  566. return true;
  567. }
  568. }
  569. // No extensible mapping found. Use inputs as-is.
  570. return true;
  571. }
  572. private:
  573. static bool startsWith(const Inputs &haystack, const Inputs &needle)
  574. {
  575. // Input is already too long.
  576. if (needle.size() > haystack.size())
  577. return false;
  578. for (int i = 0; i != needle.size(); ++i) {
  579. if (needle.at(i) != haystack.at(i))
  580. return false;
  581. }
  582. return true;
  583. }
  584. };
  585. class FakeVimHandler::Private : public QObject
  586. {
  587. Q_OBJECT
  588. public:
  589. Private(FakeVimHandler *parent, QWidget *widget);
  590. EventResult handleEvent(QKeyEvent *ev);
  591. bool wantsOverride(QKeyEvent *ev);
  592. void handleCommand(const QString &cmd); // Sets m_tc + handleExCommand
  593. void handleExCommand(const QString &cmd);
  594. void installEventFilter();
  595. void passShortcuts(bool enable);
  596. void setupWidget();
  597. void restoreWidget(int tabSize);
  598. friend class FakeVimHandler;
  599. void init();
  600. EventResult handleKey(const Input &);
  601. Q_SLOT EventResult handleKey2();
  602. EventResult handleInsertMode(const Input &);
  603. EventResult handleReplaceMode(const Input &);
  604. EventResult handleCommandMode(const Input &);
  605. EventResult handleCommandMode1(const Input &);
  606. EventResult handleCommandMode2(const Input &);
  607. EventResult handleRegisterMode(const Input &);
  608. EventResult handleExMode(const Input &);
  609. EventResult handleOpenSquareSubMode(const Input &);
  610. EventResult handleCloseSquareSubMode(const Input &);
  611. EventResult handleSearchSubSubMode(const Input &);
  612. EventResult handleCommandSubSubMode(const Input &);
  613. void finishMovement(const QString &dotCommand = QString());
  614. void finishMovement(const QString &dotCommand, int count);
  615. void resetCommandMode();
  616. void search(const SearchData &sd);
  617. void searchBalanced(bool forward, QChar needle, QChar other);
  618. void highlightMatches(const QString &needle);
  619. void stopIncrementalFind();
  620. int mvCount() const { return m_mvcount.isEmpty() ? 1 : m_mvcount.toInt(); }
  621. int opCount() const { return m_opcount.isEmpty() ? 1 : m_opcount.toInt(); }
  622. int count() const { return mvCount() * opCount(); }
  623. QTextBlock block() const { return cursor().block(); }
  624. int leftDist() const { return position() - block().position(); }
  625. int rightDist() const { return block().length() - leftDist() - 1; }
  626. bool atBlockEnd() const { return cursor().atBlockEnd(); }
  627. bool atEndOfLine() const { return atBlockEnd() && block().length() > 1; }
  628. int lastPositionInDocument() const; // Returns last valid position in doc.
  629. int firstPositionInLine(int line) const; // 1 based line, 0 based pos
  630. int lastPositionInLine(int line) const; // 1 based line, 0 based pos
  631. int lineForPosition(int pos) const; // 1 based line, 0 based pos
  632. QString lineContents(int line) const; // 1 based line
  633. void setLineContents(int line, const QString &contents); // 1 based line
  634. int linesOnScreen() const;
  635. int columnsOnScreen() const;
  636. int linesInDocument() const;
  637. // The following use all zero-based counting.
  638. int cursorLineOnScreen() const;
  639. int cursorLine() const;
  640. int physicalCursorColumn() const; // as stored in the data
  641. int logicalCursorColumn() const; // as visible on screen
  642. int physicalToLogicalColumn(int physical, const QString &text) const;
  643. int logicalToPhysicalColumn(int logical, const QString &text) const;
  644. Column cursorColumn() const; // as visible on screen
  645. int firstVisibleLine() const;
  646. void scrollToLine(int line);
  647. void scrollUp(int count);
  648. void scrollDown(int count) { scrollUp(-count); }
  649. CursorPosition cursorPosition() const
  650. { return CursorPosition(position(), firstVisibleLine()); }
  651. void setCursorPosition(const CursorPosition &p)
  652. { setPosition(p.position); scrollToLine(p.scrollLine); }
  653. // Helper functions for indenting/
  654. bool isElectricCharacter(QChar c) const;
  655. void indentSelectedText(QChar lastTyped = QChar());
  656. void indentText(const Range &range, QChar lastTyped = QChar());
  657. void shiftRegionLeft(int repeat = 1);
  658. void shiftRegionRight(int repeat = 1);
  659. void moveToFirstNonBlankOnLine();
  660. void moveToTargetColumn();
  661. void setTargetColumn() {
  662. m_targetColumn = logicalCursorColumn();
  663. m_visualTargetColumn = m_targetColumn;
  664. //qDebug() << "TARGET: " << m_targetColumn;
  665. }
  666. void moveToNextWord(bool simple, bool deleteWord = false);
  667. void moveToMatchingParanthesis();
  668. void moveToWordBoundary(bool simple, bool forward, bool changeWord = false);
  669. // Convenience wrappers to reduce line noise.
  670. void moveToStartOfLine();
  671. void moveToEndOfLine();
  672. void moveBehindEndOfLine();
  673. void moveUp(int n = 1) { moveDown(-n); }
  674. void moveDown(int n = 1);
  675. void dump(const char *msg) const {
  676. qDebug() << msg << "POS: " << anchor() << position()
  677. << "EXT: " << m_oldExternalAnchor << m_oldExternalPosition
  678. << "INT: " << m_oldInternalAnchor << m_oldInternalPosition
  679. << "VISUAL: " << m_visualMode;
  680. }
  681. void moveRight(int n = 1) {
  682. //dump("RIGHT 1");
  683. QTextCursor tc = cursor();
  684. tc.movePosition(Right, KeepAnchor, n);
  685. setCursor(tc);
  686. //dump("RIGHT 2");
  687. }
  688. void moveLeft(int n = 1) {
  689. QTextCursor tc = cursor();
  690. tc.movePosition(Left, KeepAnchor, n);
  691. setCursor(tc);
  692. }
  693. void setAnchor() {
  694. QTextCursor tc = cursor();
  695. tc.setPosition(tc.position(), MoveAnchor);
  696. setCursor(tc);
  697. }
  698. void setAnchor(int position) {
  699. QTextCursor tc = cursor();
  700. tc.setPosition(tc.anchor(), MoveAnchor);
  701. tc.setPosition(position, KeepAnchor);
  702. setCursor(tc);
  703. }
  704. void setPosition(int position) {
  705. QTextCursor tc = cursor();
  706. tc.setPosition(position, KeepAnchor);
  707. setCursor(tc);
  708. }
  709. void setAnchorAndPosition(int anchor, int position) {
  710. QTextCursor tc = cursor();
  711. tc.setPosition(anchor, MoveAnchor);
  712. tc.setPosition(position, KeepAnchor);
  713. setCursor(tc);
  714. }
  715. QTextCursor cursor() const { return EDITOR(textCursor()); }
  716. void setCursor(const QTextCursor &tc) { EDITOR(setTextCursor(tc)); }
  717. bool handleFfTt(QString key);
  718. // Helper function for handleExCommand returning 1 based line index.
  719. int readLineCode(QString &cmd);
  720. void enterInsertMode();
  721. void enterReplaceMode();
  722. void enterCommandMode();
  723. void enterExMode();
  724. void showRedMessage(const QString &msg);
  725. void showBlackMessage(const QString &msg);
  726. void notImplementedYet();
  727. void updateMiniBuffer();
  728. void updateSelection();
  729. void updateCursorShape();
  730. QWidget *editor() const;
  731. QTextDocument *document() const { return EDITOR(document()); }
  732. QChar characterAtCursor() const
  733. { return document()->characterAt(position()); }
  734. void beginEditBlock()
  735. { UNDO_DEBUG("BEGIN EDIT BLOCK"); cursor().beginEditBlock(); }
  736. void beginEditBlock(int pos)
  737. { setUndoPosition(pos); cursor().beginEditBlock(); }
  738. void endEditBlock()
  739. { UNDO_DEBUG("END EDIT BLOCK"); cursor().endEditBlock(); }
  740. void joinPreviousEditBlock()
  741. { UNDO_DEBUG("JOIN"); cursor().joinPreviousEditBlock(); }
  742. void breakEditBlock() {
  743. QTextCursor tc = cursor();
  744. tc.clearSelection();
  745. tc.beginEditBlock();
  746. tc.insertText("x");
  747. tc.deletePreviousChar();
  748. tc.endEditBlock();
  749. setCursor(tc);
  750. }
  751. bool isVisualMode() const { return m_visualMode != NoVisualMode; }
  752. bool isNoVisualMode() const { return m_visualMode == NoVisualMode; }
  753. bool isVisualCharMode() const { return m_visualMode == VisualCharMode; }
  754. bool isVisualLineMode() const { return m_visualMode == VisualLineMode; }
  755. bool isVisualBlockMode() const { return m_visualMode == VisualBlockMode; }
  756. void updateEditor();
  757. void selectWordTextObject(bool inner);
  758. void selectWORDTextObject(bool inner);
  759. void selectSentenceTextObject(bool inner);
  760. void selectParagraphTextObject(bool inner);
  761. void selectBlockTextObject(bool inner, char left, char right);
  762. void selectQuotedStringTextObject(bool inner, int type);
  763. Q_SLOT void importSelection();
  764. void exportSelection();
  765. void insertInInsertMode(const QString &text);
  766. public:
  767. QTextEdit *m_textedit;
  768. QPlainTextEdit *m_plaintextedit;
  769. bool m_wasReadOnly; // saves read-only state of document
  770. FakeVimHandler *q;
  771. Mode m_mode;
  772. bool m_passing; // let the core see the next event
  773. SubMode m_submode;
  774. SubSubMode m_subsubmode;
  775. Input m_subsubdata;
  776. int m_oldExternalPosition; // copy from last event to check for external changes
  777. int m_oldExternalAnchor;
  778. int m_oldInternalPosition; // copy from last event to check for external changes
  779. int m_oldInternalAnchor;
  780. int m_oldPosition; // FIXME: Merge with above.
  781. int m_register;
  782. QString m_mvcount;
  783. QString m_opcount;
  784. MoveType m_movetype;
  785. RangeMode m_rangemode;
  786. int m_visualInsertCount;
  787. bool m_fakeEnd;
  788. bool m_anchorPastEnd;
  789. bool m_positionPastEnd; // '$' & 'l' in visual mode can move past eol
  790. int m_gflag; // whether current command started with 'g'
  791. QString m_commandPrefix;
  792. CommandBuffer m_commandBuffer;
  793. QString m_currentFileName;
  794. QString m_currentMessage;
  795. bool m_lastSearchForward;
  796. bool m_findPending;
  797. int m_findStartPosition;
  798. QString m_lastInsertion;
  799. QString m_lastDeletion;
  800. int anchor() const { return cursor().anchor(); }
  801. int position() const { return cursor().position(); }
  802. struct TransformationData
  803. {
  804. TransformationData(const QString &s, const QVariant &d)
  805. : from(s), extraData(d) {}
  806. QString from;
  807. QString to;
  808. QVariant extraData;
  809. };
  810. typedef void (Private::*Transformation)(TransformationData *td);
  811. void transformText(const Range &range, Transformation transformation,
  812. const QVariant &extraData = QVariant());
  813. void insertText(const Register &reg);
  814. void removeText(const Range &range);
  815. void removeTransform(TransformationData *td);
  816. void invertCase(const Range &range);
  817. void invertCaseTransform(TransformationData *td);
  818. void upCase(const Range &range);
  819. void upCaseTransform(TransformationData *td);
  820. void downCase(const Range &range);
  821. void downCaseTransform(TransformationData *td);
  822. void replaceText(const Range &range, const QString &str);
  823. void replaceByStringTransform(TransformationData *td);
  824. void replaceByCharTransform(TransformationData *td);
  825. QString selectText(const Range &range) const;
  826. void setCurrentRange(const Range &range);
  827. Range currentRange() const { return Range(position(), anchor(), m_rangemode); }
  828. Range rangeFromCurrentLine() const;
  829. void yankText(const Range &range, int toregister = '"');
  830. void pasteText(bool afterCursor);
  831. // undo handling
  832. void undo();
  833. void redo();
  834. void setUndoPosition(int pos);
  835. QMap<int, int> m_undoCursorPosition; // revision -> position
  836. // extra data for '.'
  837. void replay(const QString &text, int count);
  838. void setDotCommand(const QString &cmd) { g.dotCommand = cmd; }
  839. void setDotCommand(const QString &cmd, int n) { g.dotCommand = cmd.arg(n); }
  840. // extra data for ';'
  841. QString m_semicolonCount;
  842. Input m_semicolonType; // 'f', 'F', 't', 'T'
  843. QString m_semicolonKey;
  844. // visual modes
  845. void toggleVisualMode(VisualMode visualMode);
  846. void leaveVisualMode();
  847. VisualMode m_visualMode;
  848. VisualMode m_oldVisualMode;
  849. // marks as lines
  850. int mark(int code) const;
  851. void setMark(int code, int position);
  852. typedef QHash<int, QTextCursor> Marks;
  853. typedef QHashIterator<int, QTextCursor> MarksIterator;
  854. Marks m_marks;
  855. // vi style configuration
  856. QVariant config(int code) const { return theFakeVimSetting(code)->value(); }
  857. bool hasConfig(int code) const { return config(code).toBool(); }
  858. bool hasConfig(int code, const char *value) const // FIXME
  859. { return config(code).toString().contains(value); }
  860. int m_targetColumn; // -1 if past end of line
  861. int m_visualTargetColumn; // 'l' can move past eol in visual mode only
  862. // auto-indent
  863. QString tabExpand(int len) const;
  864. Column indentation(const QString &line) const;
  865. void insertAutomaticIndentation(bool goingDown);
  866. bool removeAutomaticIndentation(); // true if something removed
  867. // number of autoindented characters
  868. int m_justAutoIndented;
  869. void handleStartOfLine();
  870. void recordJump();
  871. QVector<CursorPosition> m_jumpListUndo;
  872. QVector<CursorPosition> m_jumpListRedo;
  873. QList<QTextEdit::ExtraSelection> m_searchSelections;
  874. QTextCursor m_searchCursor;
  875. QString m_oldNeedle;
  876. bool handleExCommandHelper(const ExCommand &cmd); // Returns success.
  877. bool handleExPluginCommand(const ExCommand &cmd); // Handled by plugin?
  878. bool handleExBangCommand(const ExCommand &cmd);
  879. bool handleExDeleteCommand(const ExCommand &cmd);
  880. bool handleExGotoCommand(const ExCommand &cmd);
  881. bool handleExHistoryCommand(const ExCommand &cmd);
  882. bool handleExRegisterCommand(const ExCommand &cmd);
  883. bool handleExMapCommand(const ExCommand &cmd);
  884. bool handleExNohlsearchCommand(const ExCommand &cmd);
  885. bool handleExNormalCommand(const ExCommand &cmd);
  886. bool handleExReadCommand(const ExCommand &cmd);
  887. bool handleExRedoCommand(const ExCommand &cmd);
  888. bool handleExSetCommand(const ExCommand &cmd);
  889. bool handleExShiftCommand(const ExCommand &cmd);
  890. bool handleExSourceCommand(const ExCommand &cmd);
  891. bool handleExSubstituteCommand(const ExCommand &cmd);
  892. bool handleExWriteCommand(const ExCommand &cmd);
  893. bool handleExEchoCommand(const ExCommand &cmd);
  894. void timerEvent(QTimerEvent *ev);
  895. void setupCharClass();
  896. int charClass(QChar c, bool simple) const;
  897. signed char m_charClass[256];
  898. bool m_ctrlVActive;
  899. static struct GlobalData
  900. {
  901. GlobalData()
  902. {
  903. inputTimer = -1;
  904. }
  905. // Input.
  906. Inputs pendingInput;
  907. int inputTimer;
  908. // Repetition.
  909. QString dotCommand;
  910. // History for searches.
  911. History searchHistory;
  912. // History for :ex commands.
  913. History commandHistory;
  914. QHash<int, Register> registers;
  915. // All mappings.
  916. typedef QHash<char, ModeMapping> Mappings;
  917. Mappings mappings;
  918. } g;
  919. };
  920. FakeVimHandler::Private::GlobalData FakeVimHandler::Private::g;
  921. FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget)
  922. {
  923. //static PythonHighlighterRules pythonRules;
  924. q = parent;
  925. m_textedit = qobject_cast<QTextEdit *>(widget);
  926. m_plaintextedit = qobject_cast<QPlainTextEdit *>(widget);
  927. //new Highlighter(document(), &pythonRules);
  928. init();
  929. }
  930. void FakeVimHandler::Private::init()
  931. {
  932. m_mode = CommandMode;
  933. m_submode = NoSubMode;
  934. m_subsubmode = NoSubSubMode;
  935. m_passing = false;
  936. m_findPending = false;
  937. m_findStartPosition = -1;
  938. m_fakeEnd = false;
  939. m_positionPastEnd = false;
  940. m_anchorPastEnd = false;
  941. m_lastSearchForward = true;
  942. m_register = '"';
  943. m_gflag = false;
  944. m_visualMode = NoVisualMode;
  945. m_oldVisualMode = NoVisualMode;
  946. m_targetColumn = 0;
  947. m_visualTargetColumn = 0;
  948. m_movetype = MoveInclusive;
  949. m_justAutoIndented = 0;
  950. m_rangemode = RangeCharMode;
  951. m_ctrlVActive = false;
  952. m_oldInternalAnchor = -1;
  953. m_oldInternalPosition = -1;
  954. m_oldExternalAnchor = -1;
  955. m_oldExternalPosition = -1;
  956. m_oldPosition = -1;
  957. setupCharClass();
  958. }
  959. bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev)
  960. {
  961. const int key = ev->key();
  962. const int mods = ev->modifiers();
  963. KEY_DEBUG("SHORTCUT OVERRIDE" << key << " PASSING: " << m_passing);
  964. if (key == Key_Escape) {
  965. if (m_subsubmode == SearchSubSubMode)
  966. return true;
  967. // Not sure this feels good. People often hit Esc several times.
  968. if (isNoVisualMode()
  969. && m_mode == CommandMode
  970. && m_submode == NoSubMode
  971. && m_opcount.isEmpty()
  972. && m_mvcount.isEmpty())
  973. return false;
  974. return true;
  975. }
  976. // We are interested in overriding most Ctrl key combinations.
  977. if (mods == RealControlModifier
  978. && !config(ConfigPassControlKey).toBool()
  979. && ((key >= Key_A && key <= Key_Z && key != Key_K)
  980. || key == Key_BracketLeft || key == Key_BracketRight)) {
  981. // Ctrl-K is special as it is the Core's default notion of Locator
  982. if (m_passing) {
  983. KEY_DEBUG(" PASSING CTRL KEY");
  984. // We get called twice on the same key
  985. //m_passing = false;
  986. return false;
  987. }
  988. KEY_DEBUG(" NOT PASSING CTRL KEY");
  989. //updateMiniBuffer();
  990. return true;
  991. }
  992. // Let other shortcuts trigger.
  993. return false;
  994. }
  995. EventResult FakeVimHandler::Private::handleEvent(QKeyEvent *ev)
  996. {
  997. const int key = ev->key();
  998. const int mods = ev->modifiers();
  999. if (key == Key_Shift || key == Key_Alt || key == Key_Control
  1000. || key == Key_Alt || key == Key_AltGr || key == Key_Meta)
  1001. {
  1002. KEY_DEBUG("PLAIN MODIFIER");
  1003. return EventUnhandled;
  1004. }
  1005. if (m_passing) {
  1006. passShortcuts(false);
  1007. KEY_DEBUG("PASSING PLAIN KEY..." << ev->key() << ev->text());
  1008. //if (input.is(',')) { // use ',,' to leave, too.
  1009. // qDebug() << "FINISHED...";
  1010. // return EventHandled;
  1011. //}
  1012. m_passing = false;
  1013. updateMiniBuffer();
  1014. KEY_DEBUG(" PASS TO CORE");
  1015. return EventPassedToCore;
  1016. }
  1017. bool inSnippetMode = false;
  1018. QMetaObject::invokeMethod(editor(),
  1019. "inSnippetMode", Q_ARG(bool *, &inSnippetMode));
  1020. if (inSnippetMode)
  1021. return EventPassedToCore;
  1022. // Fake "End of line"
  1023. //m_tc = cursor();
  1024. //bool hasBlock = false;
  1025. //emit q->requestHasBlockSelection(&hasBlock);
  1026. //qDebug() << "IMPORT BLOCK 2:" << hasBlock;
  1027. //if (0 && hasBlock) {
  1028. // (pos > anc) ? --pos : --anc;
  1029. importSelection();
  1030. // Position changed externally, e.g. by code completion.
  1031. if (position() != m_oldPosition) {
  1032. setTargetColumn();
  1033. //qDebug() << "POSITION CHANGED EXTERNALLY";
  1034. if (m_mode == InsertMode) {
  1035. int dist = position() - m_oldPosition;
  1036. // Try to compensate for code completion
  1037. if (dist > 0 && dist <= physicalCursorColumn()) {
  1038. Range range(m_oldPosition, position());
  1039. m_lastInsertion.append(selectText(range));
  1040. }
  1041. } else if (!isVisualMode()) {
  1042. if (atEndOfLine())
  1043. moveLeft();
  1044. }
  1045. }
  1046. QTextCursor tc = cursor();
  1047. tc.setVisualNavigation(true);
  1048. setCursor(tc);
  1049. if (m_fakeEnd)
  1050. moveRight();
  1051. //if ((mods & RealControlModifier) != 0) {
  1052. // if (key >= Key_A && key <= Key_Z)
  1053. // key = shift(key); // make it lower case
  1054. // key = control(key);
  1055. //} else if (key >= Key_A && key <= Key_Z && (mods & Qt::ShiftModifier) == 0) {
  1056. // key = shift(key);
  1057. //}
  1058. //QTC_ASSERT(m_mode == InsertMode || m_mode == ReplaceMode
  1059. // || !atBlockEnd() || block().length() <= 1,
  1060. // qDebug() << "Cursor at EOL before key handler");
  1061. EventResult result = handleKey(Input(key, mods, ev->text()));
  1062. // The command might have destroyed the editor.
  1063. if (m_textedit || m_plaintextedit) {
  1064. // We fake vi-style end-of-line behaviour
  1065. m_fakeEnd = atEndOfLine() && m_mode == CommandMode && !isVisualBlockMode();
  1066. //QTC_ASSERT(m_mode == InsertMode || m_mode == ReplaceMode
  1067. // || !atBlockEnd() || block().length() <= 1,
  1068. // qDebug() << "Cursor at EOL after key handler");
  1069. if (m_fakeEnd)
  1070. moveLeft();
  1071. m_oldPosition = position();
  1072. if (hasConfig(ConfigShowMarks))
  1073. updateSelection();
  1074. exportSelection();
  1075. updateCursorShape();
  1076. }
  1077. return result;
  1078. }
  1079. void FakeVimHandler::Private::installEventFilter()
  1080. {
  1081. EDITOR(viewport()->installEventFilter(q));
  1082. EDITOR(installEventFilter(q));
  1083. }
  1084. void FakeVimHandler::Private::setupWidget()
  1085. {
  1086. enterCommandMode();
  1087. if (m_textedit) {
  1088. m_textedit->setLineWrapMode(QTextEdit::NoWrap);
  1089. } else if (m_plaintextedit) {
  1090. m_plaintextedit->setLineWrapMode(QPlainTextEdit::NoWrap);
  1091. }
  1092. m_wasReadOnly = EDITOR(isReadOnly());
  1093. updateEditor();
  1094. importSelection();
  1095. updateMiniBuffer();
  1096. updateCursorShape();
  1097. }
  1098. void FakeVimHandler::Private::exportSelection()
  1099. {
  1100. int pos = position();
  1101. int anc = anchor();
  1102. m_oldInternalPosition = pos;
  1103. m_oldInternalAnchor = anc;
  1104. if (isVisualMode()) {
  1105. if (pos >= anc)
  1106. setAnchorAndPosition(anc, pos + 1);
  1107. else
  1108. setAnchorAndPosition(anc + 1, pos);
  1109. if (m_visualMode == VisualBlockMode) {
  1110. emit q->requestSetBlockSelection(false);
  1111. emit q->requestSetBlockSelection(true);
  1112. } else if (m_visualMode == VisualLineMode) {
  1113. const int posLine = lineForPosition(pos);
  1114. const int ancLine = lineForPosition(anc);
  1115. if (anc < pos) {
  1116. pos = lastPositionInLine(posLine);
  1117. anc = firstPositionInLine(ancLine);
  1118. } else {
  1119. pos = firstPositionInLine(posLine);
  1120. anc = lastPositionInLine(ancLine);
  1121. }
  1122. setAnchorAndPosition(anc, pos);
  1123. } else if (m_visualMode == VisualCharMode) {
  1124. /* Nothing */
  1125. } else {
  1126. QTC_CHECK(false);
  1127. }
  1128. } else {
  1129. setAnchorAndPosition(pos, pos);
  1130. }
  1131. m_oldExternalPosition = position();
  1132. m_oldExternalAnchor = anchor();
  1133. m_oldVisualMode = m_visualMode;
  1134. }
  1135. void FakeVimHandler::Private::importSelection()
  1136. {
  1137. bool hasBlock = false;
  1138. emit q->requestHasBlockSelection(&hasBlock);
  1139. if (position() == m_oldExternalPosition
  1140. && anchor() == m_oldExternalAnchor) {
  1141. // Undo drawing correction.
  1142. m_visualMode = m_oldVisualMode;
  1143. setAnchorAndPosition(m_oldInternalAnchor, m_oldInternalPosition);
  1144. //setMark('<', m_oldInternalAnchor);
  1145. //setMark('>', m_oldInternalPosition);
  1146. } else {
  1147. // Import new selection.
  1148. Qt::KeyboardModifiers mods = QApplication::keyboardModifiers();
  1149. if (cursor().hasSelection()) {
  1150. if (mods & RealControlModifier)
  1151. m_visualMode = VisualBlockMode;
  1152. else if (mods & Qt::AltModifier)
  1153. m_visualMode = VisualBlockMode;
  1154. else if (mods & Qt::ShiftModifier)
  1155. m_visualMode = VisualLineMode;
  1156. else
  1157. m_visualMode = VisualCharMode;
  1158. } else {
  1159. m_visualMode = NoVisualMode;
  1160. }
  1161. //dump("IS @");
  1162. //setMark('<', tc.anchor());
  1163. //setMark('>', tc.position());
  1164. }
  1165. }
  1166. void FakeVimHandler::Private::updateEditor()
  1167. {
  1168. const int charWidth = QFontMetrics(EDITOR(font())).width(QChar(' '));
  1169. EDITOR(setTabStopWidth(charWidth * config(ConfigTabStop).toInt()));
  1170. setupCharClass();
  1171. }
  1172. void FakeVimHandler::Private::restoreWidget(int tabSize)
  1173. {
  1174. //showBlackMessage(QString());
  1175. //updateMiniBuffer();
  1176. //EDITOR(removeEventFilter(q));
  1177. //EDITOR(setReadOnly(m_wasReadOnly));
  1178. const int charWidth = QFontMetrics(EDITOR(font())).width(QChar(' '));
  1179. EDITOR(setTabStopWidth(charWidth * tabSize));
  1180. m_visualMode = NoVisualMode;
  1181. // Force "ordinary" cursor.
  1182. m_mode = InsertMode;
  1183. m_submode = NoSubMode;
  1184. m_subsubmode = NoSubSubMode;
  1185. updateCursorShape();
  1186. updateSelection();
  1187. }
  1188. EventResult FakeVimHandler::Private::handleKey(const Input &input)
  1189. {
  1190. KEY_DEBUG("HANDLE INPUT: " << input << " MODE: " << mode);
  1191. if (m_mode == ExMode)
  1192. return handleExMode(input);
  1193. if (m_subsubmode == SearchSubSubMode)
  1194. return handleSearchSubSubMode(input);
  1195. if (m_mode == InsertMode || m_mode == ReplaceMode || m_mode == CommandMode) {
  1196. g.pendingInput.append(input);
  1197. const char code = m_mode == InsertMode ? 'i' : 'n';
  1198. if (g.mappings[code].mappingDone(&g.pendingInput))
  1199. return handleKey2();
  1200. if (g.inputTimer != -1)
  1201. killTimer(g.inputTimer);
  1202. g.inputTimer = startTimer(1000);
  1203. return EventHandled;
  1204. }
  1205. return EventUnhandled;
  1206. }
  1207. EventResult FakeVimHandler::Private::handleKey2()
  1208. {
  1209. setUndoPosition(position());
  1210. if (m_mode == InsertMode) {
  1211. EventResult result = EventUnhandled;
  1212. foreach (const Input &in, g.pendingInput) {
  1213. EventResult r = handleInsertMode(in);
  1214. if (r == EventHandled)
  1215. result = EventHandled;
  1216. }
  1217. g.pendingInput.clear();
  1218. return result;
  1219. }
  1220. if (m_mode == ReplaceMode) {
  1221. EventResult result = EventUnhandled;
  1222. foreach (const Input &in, g.pendingInput) {
  1223. EventResult r = handleReplaceMode(in);
  1224. if (r == EventHandled)
  1225. result = EventHandled;
  1226. }
  1227. g.pendingInput.clear();
  1228. return result;
  1229. }
  1230. if (m_mode == CommandMode) {
  1231. EventResult result = EventUnhandled;
  1232. foreach (const Input &in, g.pendingInput) {
  1233. EventResult r = handleCommandMode(in);
  1234. if (r == EventHandled)
  1235. result = EventHandled;
  1236. }
  1237. g.pendingInput.clear();
  1238. return result;
  1239. }
  1240. return EventUnhandled;
  1241. }
  1242. void FakeVimHandler::Private::timerEvent(QTimerEvent *ev)
  1243. {
  1244. Q_UNUSED(ev);
  1245. handleKey2();
  1246. }
  1247. void FakeVimHandler::Private::stopIncrementalFind()
  1248. {
  1249. if (m_findPending) {
  1250. m_findPending = false;
  1251. QTextCursor tc = cursor();
  1252. setAnchorAndPosition(m_findStartPosition, tc.selectionStart());
  1253. finishMovement();
  1254. setAnchor();
  1255. }
  1256. }
  1257. void FakeVimHandler::Private::setUndoPosition(int pos)
  1258. {
  1259. //qDebug() << " CURSOR POS: " << m_undoCursorPosition;
  1260. m_undoCursorPosition[document()->availableUndoSteps()] = pos;
  1261. }
  1262. void FakeVimHandler::Private::moveDown(int n)
  1263. {
  1264. #if 0
  1265. // does not work for "hidden" documents like in the autotests
  1266. tc.movePosition(Down, MoveAnchor, n);
  1267. #else
  1268. const int col = position() - block().position();
  1269. const int lastLine = document()->lastBlock().blockNumber();
  1270. const int targetLine = qMax(0, qMin(lastLine, block().blockNumber() + n));
  1271. const QTextBlock &block = document()->findBlockByNumber(targetLine);
  1272. const int pos = block.position();
  1273. setPosition(pos + qMax(0, qMin(block.length() - 2, col)));
  1274. moveToTargetColumn();
  1275. #endif
  1276. }
  1277. void FakeVimHandler::Private::moveToEndOfLine()
  1278. {
  1279. #if 0
  1280. // does not work for "hidden" documents like in the autotests
  1281. tc.movePosition(EndOfLine, MoveAnchor);
  1282. #else
  1283. const int pos = block().position() + block().length() - 2;
  1284. setPosition(qMax(block().position(), pos));
  1285. #endif
  1286. }
  1287. void FakeVimHandler::Private::moveBehindEndOfLine()
  1288. {
  1289. int pos = qMin(block().position() + block().length() - 1,
  1290. lastPositionInDocument());
  1291. setPosition(pos);
  1292. }
  1293. void FakeVimHandler::Private::moveToStartOfLine()
  1294. {
  1295. #if 0
  1296. // does not work for "hidden" documents like in the autotests
  1297. tc.movePosition(StartOfLine, MoveAnchor);
  1298. #else
  1299. setPosition(block().position());
  1300. #endif
  1301. }
  1302. void FakeVimHandler::Private::finishMovement(const QString &dotCommand, int count)
  1303. {
  1304. finishMovement(dotCommand.arg(count));
  1305. }
  1306. void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
  1307. {
  1308. //dump("FINISH MOVEMENT");
  1309. if (m_submode == FilterSubMode) {
  1310. int beginLine = lineForPosition(anchor());
  1311. int endLine = lineForPosition(position());
  1312. setPosition(qMin(anchor(), position()));
  1313. enterExMode();
  1314. m_currentMessage.clear();
  1315. m_commandBuffer.setContents(QString(".,+%1!").arg(qAbs(endLine - beginLine)));
  1316. //g.commandHistory.append(QString());
  1317. updateMiniBuffer();
  1318. return;
  1319. }
  1320. //if (isVisualMode())
  1321. // setMark('>', position());
  1322. if (m_submode == ChangeSubMode
  1323. || m_submode == DeleteSubMode
  1324. || m_submode == YankSubMode
  1325. || m_submode == TransformSubMode) {
  1326. if (m_submode != YankSubMode)
  1327. beginEditBlock();
  1328. if (m_movetype == MoveLineWise)
  1329. m_rangemode = (m_submode == ChangeSubMode)
  1330. ? RangeLineModeExclusive
  1331. : RangeLineMode;
  1332. if (m_movetype == MoveInclusive) {
  1333. if (anchor() <= position()) {
  1334. if (!cursor().atBlockEnd())
  1335. setPosition(position() + 1); // correction
  1336. } else {
  1337. setAnchorAndPosition(anchor() + 1, position());
  1338. }
  1339. }
  1340. if (m_positionPastEnd) {
  1341. const int anc = anchor();
  1342. moveBehindEndOfLine();
  1343. moveRight();
  1344. setAnchorAndPosition(anc, position());
  1345. }
  1346. if (m_anchorPastEnd) {
  1347. setAnchorAndPosition(anchor() + 1, position());
  1348. }
  1349. if (m_submode != TransformSubMode) {
  1350. yankText(currentRange(), m_register);
  1351. if (m_movetype == MoveLineWise)
  1352. g.registers[m_register].rangemode = RangeLineMode;
  1353. }
  1354. m_positionPastEnd = m_anchorPastEnd = false;
  1355. }
  1356. if (m_submode == ChangeSubMode) {
  1357. if (m_rangemode == RangeLineMode)
  1358. m_rangemode = RangeLineModeExclusive;
  1359. removeText(currentRange());
  1360. if (!dotCommand.isEmpty())
  1361. setDotCommand(QLatin1Char('c') + dotCommand);
  1362. if (m_movetype == MoveLineWise)
  1363. insertAutomaticIndentation(true);
  1364. endEditBlock();
  1365. setUndoPosition(position());
  1366. enterInsertMode();
  1367. m_submode = NoSubMode;
  1368. } else if (m_submode == DeleteSubMode) {
  1369. removeText(currentRange());
  1370. if (!dotCommand.isEmpty())
  1371. setDotCommand(QLatin1Char('d') + dotCommand);
  1372. if (m_movetype == MoveLineWise)
  1373. handleStartOfLine();
  1374. m_submode = NoSubMode;
  1375. if (atEndOfLine())
  1376. moveLeft();
  1377. else
  1378. setTargetColumn();
  1379. endEditBlock();
  1380. } else if (m_submode == YankSubMode) {
  1381. m_submode = NoSubMode;
  1382. const int la = lineForPosition(anchor());
  1383. const int lp = lineForPosition(position());
  1384. if (m_register != '"') {
  1385. setPosition(mark(m_register));
  1386. moveToStartOfLine();
  1387. } else {
  1388. if (anchor() <= position())
  1389. setPosition(anchor());
  1390. }
  1391. if (la != lp)
  1392. showBlackMessage(QString("%1 lines yanked").arg(qAbs(la - lp) + 1));
  1393. } else if (m_submode == TransformSubMode) {
  1394. if (m_subsubmode == InvertCaseSubSubMode) {
  1395. invertCase(currentRange());
  1396. if (!dotCommand.isEmpty())
  1397. setDotCommand(QLatin1Char('~') + dotCommand);
  1398. } else if (m_subsubmode == UpCaseSubSubMode) {
  1399. upCase(currentRange());
  1400. if (!dotCommand.isEmpty())
  1401. setDotCommand("gU" + dotCommand);
  1402. } else if (m_subsubmode == DownCaseSubSubMode) {
  1403. downCase(currentRange());
  1404. if (!dotCommand.isEmpty())
  1405. setDotCommand("gu" + dotCommand);
  1406. }
  1407. m_submode = NoSubMode;
  1408. m_subsubmode = NoSubSubMode;
  1409. setPosition(qMin(anchor(), position()));
  1410. if (m_movetype ==

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