/src/plugins/fakevim/fakevimhandler.cpp
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
- /****************************************************************************
- **
- ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
- ** Contact: http://www.qt-project.org/legal
- **
- ** This file is part of Qt Creator.
- **
- ** Commercial License Usage
- ** Licensees holding valid commercial Qt licenses may use this file in
- ** accordance with the commercial license agreement provided with the
- ** Software or, alternatively, in accordance with the terms contained in
- ** a written agreement between you and Digia. For licensing terms and
- ** conditions see http://qt.digia.com/licensing. For further information
- ** use the contact form at http://qt.digia.com/contact-us.
- **
- ** GNU Lesser General Public License Usage
- ** Alternatively, this file may be used under the terms of the GNU Lesser
- ** General Public License version 2.1 as published by the Free Software
- ** Foundation and appearing in the file LICENSE.LGPL included in the
- ** packaging of this file. Please review the following information to
- ** ensure the GNU Lesser General Public License version 2.1 requirements
- ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
- **
- ** In addition, as a special exception, Digia gives you certain additional
- ** rights. These rights are described in the Digia Qt LGPL Exception
- ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
- **
- ****************************************************************************/
- //
- // ATTENTION:
- //
- // 1 Please do not add any direct dependencies to other Qt Creator code here.
- // Instead emit signals and let the FakeVimPlugin channel the information to
- // Qt Creator. The idea is to keep this file here in a "clean" state that
- // allows easy reuse with any QTextEdit or QPlainTextEdit derived class.
- //
- // 2 There are a few auto tests located in ../../../tests/auto/fakevim.
- // Commands that are covered there are marked as "// tested" below.
- //
- // 3 Some conventions:
- //
- // Use 1 based line numbers and 0 based column numbers. Even though
- // the 1 based line are not nice it matches vim's and QTextEdit's 'line'
- // concepts.
- //
- // Do not pass QTextCursor etc around unless really needed. Convert
- // early to line/column.
- //
- // A QTextCursor is always between characters, whereas vi's cursor is always
- // over a character. FakeVim interprets the QTextCursor to be over the character
- // to the right of the QTextCursor's position().
- //
- // A current "region of interest"
- // spans between anchor(), (i.e. the character below anchor()), and
- // position(). The character below position() is not included
- // if the last movement command was exclusive (MoveExclusive).
- //
- #include "fakevimhandler.h"
- #include <utils/hostosinfo.h>
- #include <utils/qtcassert.h>
- #include <QDebug>
- #include <QFile>
- #include <QObject>
- #include <QPointer>
- #include <QProcess>
- #include <QRegExp>
- #include <QTextStream>
- #include <QTimer>
- #include <QtAlgorithms>
- #include <QStack>
- #include <QApplication>
- #include <QClipboard>
- #include <QInputMethodEvent>
- #include <QKeyEvent>
- #include <QLineEdit>
- #include <QPlainTextEdit>
- #include <QScrollBar>
- #include <QTextBlock>
- #include <QTextCursor>
- #include <QTextDocumentFragment>
- #include <QTextEdit>
- #include <QMimeData>
- #include <algorithm>
- #include <climits>
- #include <ctype.h>
- //#define DEBUG_KEY 1
- #if DEBUG_KEY
- # define KEY_DEBUG(s) qDebug() << s
- #else
- # define KEY_DEBUG(s)
- #endif
- //#define DEBUG_UNDO 1
- #if DEBUG_UNDO
- # define UNDO_DEBUG(s) qDebug() << << revision() << s
- #else
- # define UNDO_DEBUG(s)
- #endif
- using namespace Utils;
- namespace FakeVim {
- namespace Internal {
- ///////////////////////////////////////////////////////////////////////
- //
- // FakeVimHandler
- //
- ///////////////////////////////////////////////////////////////////////
- #define StartOfLine QTextCursor::StartOfLine
- #define EndOfLine QTextCursor::EndOfLine
- #define MoveAnchor QTextCursor::MoveAnchor
- #define KeepAnchor QTextCursor::KeepAnchor
- #define Up QTextCursor::Up
- #define Down QTextCursor::Down
- #define Right QTextCursor::Right
- #define Left QTextCursor::Left
- #define EndOfDocument QTextCursor::End
- #define StartOfDocument QTextCursor::Start
- #define ParagraphSeparator QChar::ParagraphSeparator
- #define EDITOR(s) (m_textedit ? m_textedit->s : m_plaintextedit->s)
- #define MetaModifier // Use HostOsInfo::controlModifier() instead
- #define ControlModifier // Use HostOsInfo::controlModifier() instead
- typedef QLatin1String _;
- /* Clipboard MIME types used by Vim. */
- static const QString vimMimeText = _("_VIM_TEXT");
- static const QString vimMimeTextEncoded = _("_VIMENC_TEXT");
- using namespace Qt;
- /*! A \e Mode represents one of the basic modes of operation of FakeVim.
- */
- enum Mode
- {
- InsertMode,
- ReplaceMode,
- CommandMode,
- ExMode
- };
- /*! A \e SubMode is used for things that require one more data item
- and are 'nested' behind a \l Mode.
- */
- enum SubMode
- {
- NoSubMode,
- ChangeSubMode, // Used for c
- DeleteSubMode, // Used for d
- FilterSubMode, // Used for !
- IndentSubMode, // Used for =
- RegisterSubMode, // Used for "
- ShiftLeftSubMode, // Used for <
- ShiftRightSubMode, // Used for >
- InvertCaseSubMode, // Used for g~
- DownCaseSubMode, // Used for gu
- UpCaseSubMode, // Used for gU
- WindowSubMode, // Used for Ctrl-w
- YankSubMode, // Used for y
- ZSubMode, // Used for z
- CapitalZSubMode, // Used for Z
- ReplaceSubMode // Used for r
- };
- /*! A \e SubSubMode is used for things that require one more data item
- and are 'nested' behind a \l SubMode.
- */
- enum SubSubMode
- {
- NoSubSubMode,
- FtSubSubMode, // Used for f, F, t, T.
- MarkSubSubMode, // Used for m.
- BackTickSubSubMode, // Used for `.
- TickSubSubMode, // Used for '.
- TextObjectSubSubMode, // Used for thing like iw, aW, as etc.
- ZSubSubMode, // Used for zj, zk
- OpenSquareSubSubMode, // Used for [{, {(, [z
- CloseSquareSubSubMode, // Used for ]}, ]), ]z
- SearchSubSubMode
- };
- enum VisualMode
- {
- NoVisualMode,
- VisualCharMode,
- VisualLineMode,
- VisualBlockMode
- };
- enum MoveType
- {
- MoveExclusive,
- MoveInclusive,
- MoveLineWise
- };
- /*!
- \enum RangeMode
- The \e RangeMode serves as a means to define how the "Range" between
- the \l cursor and the \l anchor position is to be interpreted.
- \value RangeCharMode Entered by pressing \key v. The range includes
- all characters between cursor and anchor.
- \value RangeLineMode Entered by pressing \key V. The range includes
- all lines between the line of the cursor and
- the line of the anchor.
- \value RangeLineModeExclusice Like \l RangeLineMode, but keeps one
- newline when deleting.
- \value RangeBlockMode Entered by pressing \key Ctrl-v. The range includes
- all characters with line and column coordinates
- between line and columns coordinates of cursor and
- anchor.
- \value RangeBlockAndTailMode Like \l RangeBlockMode, but also includes
- all characters in the affected lines up to the end
- of these lines.
- */
- enum EventResult
- {
- EventHandled,
- EventUnhandled,
- EventCancelled, // Event is handled but a sub mode was cancelled.
- EventPassedToCore
- };
- struct CursorPosition
- {
- CursorPosition() : line(-1), column(-1) {}
- CursorPosition(int block, int column) : line(block), column(column) {}
- explicit CursorPosition(const QTextCursor &tc)
- : line(tc.block().blockNumber()), column(tc.positionInBlock()) {}
- CursorPosition(const QTextDocument *document, int position)
- {
- QTextBlock block = document->findBlock(position);
- line = block.blockNumber();
- column = position - block.position();
- }
- bool isValid() const { return line >= 0 && column >= 0; }
- bool operator>(const CursorPosition &other) const
- { return line > other.line || column > other.column; }
- bool operator==(const CursorPosition &other) const
- { return line == other.line && column == other.column; }
- bool operator!=(const CursorPosition &other) const { return !operator==(other); }
- int line; // Line in document (from 0, folded lines included).
- int column; // Position on line.
- };
- struct Mark
- {
- Mark(const CursorPosition &pos = CursorPosition(), const QString &fileName = QString())
- : position(pos), fileName(fileName) {}
- bool isValid() const { return position.isValid(); }
- bool isLocal(const QString &localFileName) const
- {
- return fileName.isEmpty() || fileName == localFileName;
- }
- CursorPosition position;
- QString fileName;
- };
- typedef QHash<QChar, Mark> Marks;
- typedef QHashIterator<QChar, Mark> MarksIterator;
- struct State
- {
- State() : revision(-1), position(), marks(), lastVisualMode(NoVisualMode),
- lastVisualModeInverted(false) {}
- State(int revision, const CursorPosition &position, const Marks &marks,
- VisualMode lastVisualMode, bool lastVisualModeInverted) : revision(revision),
- position(position), marks(marks), lastVisualMode(lastVisualMode),
- lastVisualModeInverted(lastVisualModeInverted) {}
- int revision;
- CursorPosition position;
- Marks marks;
- VisualMode lastVisualMode;
- bool lastVisualModeInverted;
- };
- struct Column
- {
- Column(int p, int l) : physical(p), logical(l) {}
- int physical; // Number of characters in the data.
- int logical; // Column on screen.
- };
- QDebug operator<<(QDebug ts, const Column &col)
- {
- return ts << "(p: " << col.physical << ", l: " << col.logical << ")";
- }
- struct Register
- {
- Register() : rangemode(RangeCharMode) {}
- Register(const QString &c) : contents(c), rangemode(RangeCharMode) {}
- Register(const QString &c, RangeMode m) : contents(c), rangemode(m) {}
- QString contents;
- RangeMode rangemode;
- };
- QDebug operator<<(QDebug ts, const Register ®)
- {
- return ts << reg.contents;
- }
- struct SearchData
- {
- SearchData()
- {
- forward = true;
- highlightMatches = true;
- }
- QString needle;
- bool forward;
- bool highlightMatches;
- };
- // If string begins with given prefix remove it with trailing spaces and return true.
- static bool eatString(const char *prefix, QString *str)
- {
- if (!str->startsWith(_(prefix)))
- return false;
- *str = str->mid(strlen(prefix)).trimmed();
- return true;
- }
- static QRegExp vimPatternToQtPattern(QString needle, bool smartcase)
- {
- /* Transformations (Vim regexp -> QRegExp):
- * \a -> [A-Za-z]
- * \A -> [^A-Za-z]
- * \h -> [A-Za-z_]
- * \H -> [^A-Za-z_]
- * \l -> [a-z]
- * \L -> [^a-z]
- * \o -> [0-7]
- * \O -> [^0-7]
- * \u -> [A-Z]
- * \U -> [^A-Z]
- * \x -> [0-9A-Fa-f]
- * \X -> [^0-9A-Fa-f]
- *
- * \< -> \b
- * \> -> \b
- * [] -> \[\]
- * \= -> ?
- *
- * (...) <-> \(...\)
- * {...} <-> \{...\}
- * | <-> \|
- * ? <-> \?
- * + <-> \+
- * \{...} -> {...}
- *
- * \c - set ignorecase for rest
- * \C - set noignorecase for rest
- */
- bool ignorecase = smartcase && !needle.contains(QRegExp(_("[A-Z]")));
- QString pattern;
- pattern.reserve(2 * needle.size());
- bool escape = false;
- bool brace = false;
- bool embraced = false;
- bool range = false;
- bool curly = false;
- foreach (const QChar &c, needle) {
- if (brace) {
- brace = false;
- if (c == QLatin1Char(']')) {
- pattern.append(_("\\[\\]"));
- continue;
- }
- pattern.append(QLatin1Char('['));
- escape = true;
- embraced = true;
- }
- if (embraced) {
- if (range) {
- QChar c2 = pattern[pattern.size() - 2];
- pattern.remove(pattern.size() - 2, 2);
- pattern.append(c2.toUpper() + QLatin1Char('-') + c.toUpper());
- pattern.append(c2.toLower() + QLatin1Char('-') + c.toLower());
- range = false;
- } else if (escape) {
- escape = false;
- pattern.append(c);
- } else if (c == QLatin1Char('\\')) {
- escape = true;
- } else if (c == QLatin1Char(']')) {
- pattern.append(QLatin1Char(']'));
- embraced = false;
- } else if (c == QLatin1Char('-')) {
- range = ignorecase && pattern[pattern.size() - 1].isLetter();
- pattern.append(QLatin1Char('-'));
- } else if (c.isLetter() && ignorecase) {
- pattern.append(c.toLower()).append(c.toUpper());
- } else {
- pattern.append(c);
- }
- } else if (QString::fromLatin1("(){}+|?").indexOf(c) != -1) {
- if (c == QLatin1Char('{')) {
- curly = escape;
- } else if (c == QLatin1Char('}') && curly) {
- curly = false;
- escape = true;
- }
- if (escape)
- escape = false;
- else
- pattern.append(QLatin1Char('\\'));
- pattern.append(c);
- } else if (escape) {
- // escape expression
- escape = false;
- if (c == QLatin1Char('<') || c == QLatin1Char('>'))
- pattern.append(_("\\b"));
- else if (c == QLatin1Char('a'))
- pattern.append(_("[a-zA-Z]"));
- else if (c == QLatin1Char('A'))
- pattern.append(_("[^a-zA-Z]"));
- else if (c == QLatin1Char('h'))
- pattern.append(_("[A-Za-z_]"));
- else if (c == QLatin1Char('H'))
- pattern.append(_("[^A-Za-z_]"));
- else if (c == QLatin1Char('c') || c == QLatin1Char('C'))
- ignorecase = (c == QLatin1Char('c'));
- else if (c == QLatin1Char('l'))
- pattern.append(_("[a-z]"));
- else if (c == QLatin1Char('L'))
- pattern.append(_("[^a-z]"));
- else if (c == QLatin1Char('o'))
- pattern.append(_("[0-7]"));
- else if (c == QLatin1Char('O'))
- pattern.append(_("[^0-7]"));
- else if (c == QLatin1Char('u'))
- pattern.append(_("[A-Z]"));
- else if (c == QLatin1Char('U'))
- pattern.append(_("[^A-Z]"));
- else if (c == QLatin1Char('x'))
- pattern.append(_("[0-9A-Fa-f]"));
- else if (c == QLatin1Char('X'))
- pattern.append(_("[^0-9A-Fa-f]"));
- else if (c == QLatin1Char('='))
- pattern.append(_("?"));
- else
- pattern.append(QLatin1Char('\\') + c);
- } else {
- // unescaped expression
- if (c == QLatin1Char('\\'))
- escape = true;
- else if (c == QLatin1Char('['))
- brace = true;
- else if (c.isLetter() && ignorecase)
- pattern.append(QLatin1Char('[') + c.toLower() + c.toUpper() + QLatin1Char(']'));
- else
- pattern.append(c);
- }
- }
- if (escape)
- pattern.append(QLatin1Char('\\'));
- else if (brace)
- pattern.append(QLatin1Char('['));
- return QRegExp(pattern);
- }
- static bool afterEndOfLine(const QTextDocument *doc, int position)
- {
- return doc->characterAt(position) == ParagraphSeparator
- && doc->findBlock(position).length() > 1;
- }
- static void searchForward(QTextCursor *tc, QRegExp &needleExp, int *repeat)
- {
- const QTextDocument *doc = tc->document();
- const int startPos = tc->position();
- // Search from beginning of line so that matched text is the same.
- tc->movePosition(StartOfLine);
- // forward to current position
- *tc = doc->find(needleExp, *tc);
- while (!tc->isNull() && tc->anchor() < startPos) {
- if (!tc->hasSelection())
- tc->movePosition(Right);
- if (tc->atBlockEnd())
- tc->movePosition(Right);
- *tc = doc->find(needleExp, *tc);
- }
- if (tc->isNull())
- return;
- --*repeat;
- while (*repeat > 0) {
- if (!tc->hasSelection())
- tc->movePosition(Right);
- if (tc->atBlockEnd())
- tc->movePosition(Right);
- *tc = doc->find(needleExp, *tc);
- if (tc->isNull())
- return;
- --*repeat;
- }
- if (!tc->isNull() && afterEndOfLine(doc, tc->anchor()))
- tc->movePosition(Left);
- }
- static void searchBackward(QTextCursor *tc, QRegExp &needleExp, int *repeat)
- {
- // Search from beginning of line so that matched text is the same.
- QTextBlock block = tc->block();
- QString line = block.text();
- int i = line.indexOf(needleExp, 0);
- while (i != -1 && i < tc->positionInBlock()) {
- --*repeat;
- i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength()));
- if (i == line.size())
- i = -1;
- }
- if (i == tc->positionInBlock())
- --*repeat;
- while (*repeat > 0) {
- block = block.previous();
- if (!block.isValid())
- break;
- line = block.text();
- i = line.indexOf(needleExp, 0);
- while (i != -1) {
- --*repeat;
- i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength()));
- if (i == line.size())
- i = -1;
- }
- }
- if (!block.isValid()) {
- *tc = QTextCursor();
- return;
- }
- i = line.indexOf(needleExp, 0);
- while (*repeat < 0) {
- i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength()));
- ++*repeat;
- }
- tc->setPosition(block.position() + i);
- }
- static bool substituteText(QString *text, QRegExp &pattern, const QString &replacement,
- bool global)
- {
- bool substituted = false;
- int pos = 0;
- while (true) {
- pos = pattern.indexIn(*text, pos, QRegExp::CaretAtZero);
- if (pos == -1)
- break;
- substituted = true;
- QString matched = text->mid(pos, pattern.cap(0).size());
- QString repl;
- bool escape = false;
- // insert captured texts
- for (int i = 0; i < replacement.size(); ++i) {
- const QChar &c = replacement[i];
- if (escape) {
- escape = false;
- if (c.isDigit()) {
- if (c.digitValue() <= pattern.captureCount())
- repl += pattern.cap(c.digitValue());
- } else {
- repl += c;
- }
- } else {
- if (c == QLatin1Char('\\'))
- escape = true;
- else if (c == QLatin1Char('&'))
- repl += pattern.cap(0);
- else
- repl += c;
- }
- }
- text->replace(pos, matched.size(), repl);
- pos += qMax(1, repl.size());
- if (pos >= text->size() || !global)
- break;
- }
- return substituted;
- }
- static int findUnescaped(QChar c, const QString &line, int from)
- {
- for (int i = from; i < line.size(); ++i) {
- if (line.at(i) == c && (i == 0 || line.at(i - 1) != QLatin1Char('\\')))
- return i;
- }
- return -1;
- }
- static void setClipboardData(const QString &content, RangeMode mode,
- QClipboard::Mode clipboardMode)
- {
- QClipboard *clipboard = QApplication::clipboard();
- char vimRangeMode = mode;
- QByteArray bytes1;
- bytes1.append(vimRangeMode);
- bytes1.append(content.toUtf8());
- QByteArray bytes2;
- bytes2.append(vimRangeMode);
- bytes2.append("utf-8");
- bytes2.append('\0');
- bytes2.append(content.toUtf8());
- QMimeData *data = new QMimeData;
- data->setText(content);
- data->setData(vimMimeText, bytes1);
- data->setData(vimMimeTextEncoded, bytes2);
- clipboard->setMimeData(data, clipboardMode);
- }
- static const QMap<QString, int> &vimKeyNames()
- {
- static QMap<QString, int> k;
- if (!k.isEmpty())
- return k;
- // FIXME: Should be value of mapleader.
- k.insert(_("LEADER"), Key_Backslash);
- k.insert(_("SPACE"), Key_Space);
- k.insert(_("TAB"), Key_Tab);
- k.insert(_("NL"), Key_Return);
- k.insert(_("NEWLINE"), Key_Return);
- k.insert(_("LINEFEED"), Key_Return);
- k.insert(_("LF"), Key_Return);
- k.insert(_("CR"), Key_Return);
- k.insert(_("RETURN"), Key_Return);
- k.insert(_("ENTER"), Key_Return);
- k.insert(_("BS"), Key_Backspace);
- k.insert(_("BACKSPACE"), Key_Backspace);
- k.insert(_("ESC"), Key_Escape);
- k.insert(_("BAR"), Key_Bar);
- k.insert(_("BSLASH"), Key_Backslash);
- k.insert(_("DEL"), Key_Delete);
- k.insert(_("DELETE"), Key_Delete);
- k.insert(_("KDEL"), Key_Delete);
- k.insert(_("UP"), Key_Up);
- k.insert(_("DOWN"), Key_Down);
- k.insert(_("LEFT"), Key_Left);
- k.insert(_("RIGHT"), Key_Right);
- k.insert(_("LT"), Key_Less);
- k.insert(_("F1"), Key_F1);
- k.insert(_("F2"), Key_F2);
- k.insert(_("F3"), Key_F3);
- k.insert(_("F4"), Key_F4);
- k.insert(_("F5"), Key_F5);
- k.insert(_("F6"), Key_F6);
- k.insert(_("F7"), Key_F7);
- k.insert(_("F8"), Key_F8);
- k.insert(_("F9"), Key_F9);
- k.insert(_("F10"), Key_F10);
- k.insert(_("F11"), Key_F11);
- k.insert(_("F12"), Key_F12);
- k.insert(_("F13"), Key_F13);
- k.insert(_("F14"), Key_F14);
- k.insert(_("F15"), Key_F15);
- k.insert(_("F16"), Key_F16);
- k.insert(_("F17"), Key_F17);
- k.insert(_("F18"), Key_F18);
- k.insert(_("F19"), Key_F19);
- k.insert(_("F20"), Key_F20);
- k.insert(_("F21"), Key_F21);
- k.insert(_("F22"), Key_F22);
- k.insert(_("F23"), Key_F23);
- k.insert(_("F24"), Key_F24);
- k.insert(_("F25"), Key_F25);
- k.insert(_("F26"), Key_F26);
- k.insert(_("F27"), Key_F27);
- k.insert(_("F28"), Key_F28);
- k.insert(_("F29"), Key_F29);
- k.insert(_("F30"), Key_F30);
- k.insert(_("F31"), Key_F31);
- k.insert(_("F32"), Key_F32);
- k.insert(_("F33"), Key_F33);
- k.insert(_("F34"), Key_F34);
- k.insert(_("F35"), Key_F35);
- k.insert(_("INSERT"), Key_Insert);
- k.insert(_("INS"), Key_Insert);
- k.insert(_("KINSERT"), Key_Insert);
- k.insert(_("HOME"), Key_Home);
- k.insert(_("END"), Key_End);
- k.insert(_("PAGEUP"), Key_PageUp);
- k.insert(_("PAGEDOWN"), Key_PageDown);
- k.insert(_("KPLUS"), Key_Plus);
- k.insert(_("KMINUS"), Key_Minus);
- k.insert(_("KDIVIDE"), Key_Slash);
- k.insert(_("KMULTIPLY"), Key_Asterisk);
- k.insert(_("KENTER"), Key_Enter);
- k.insert(_("KPOINT"), Key_Period);
- return k;
- }
- Range::Range()
- : beginPos(-1), endPos(-1), rangemode(RangeCharMode)
- {}
- Range::Range(int b, int e, RangeMode m)
- : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
- {}
- QString Range::toString() const
- {
- return QString::fromLatin1("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
- .arg(rangemode);
- }
- QDebug operator<<(QDebug ts, const Range &range)
- {
- return ts << '[' << range.beginPos << ',' << range.endPos << ']';
- }
- ExCommand::ExCommand(const QString &c, const QString &a, const Range &r)
- : cmd(c), hasBang(false), args(a), range(r), count(1)
- {}
- bool ExCommand::matches(const QString &min, const QString &full) const
- {
- return cmd.startsWith(min) && full.startsWith(cmd);
- }
- QDebug operator<<(QDebug ts, const ExCommand &cmd)
- {
- return ts << cmd.cmd << ' ' << cmd.args << ' ' << cmd.range;
- }
- QDebug operator<<(QDebug ts, const QList<QTextEdit::ExtraSelection> &sels)
- {
- foreach (const QTextEdit::ExtraSelection &sel, sels)
- ts << "SEL: " << sel.cursor.anchor() << sel.cursor.position();
- return ts;
- }
- QString quoteUnprintable(const QString &ba)
- {
- QString res;
- for (int i = 0, n = ba.size(); i != n; ++i) {
- const QChar c = ba.at(i);
- const int cc = c.unicode();
- if (c.isPrint())
- res += c;
- else if (cc == QLatin1Char('\n'))
- res += _("<CR>");
- else
- res += QString::fromLatin1("\\x%1").arg(c.unicode(), 2, 16, QLatin1Char('0'));
- }
- return res;
- }
- static bool startsWithWhitespace(const QString &str, int col)
- {
- QTC_ASSERT(str.size() >= col, return false);
- for (int i = 0; i < col; ++i) {
- uint u = str.at(i).unicode();
- if (u != QLatin1Char(' ') && u != QLatin1Char('\t'))
- return false;
- }
- return true;
- }
- inline QString msgMarkNotSet(const QString &text)
- {
- return FakeVimHandler::tr("Mark '%1' not set").arg(text);
- }
- class Input
- {
- public:
- // Remove some extra "information" on Mac.
- static int cleanModifier(int m) { return m & ~Qt::KeypadModifier; }
- Input()
- : m_key(0), m_xkey(0), m_modifiers(0) {}
- explicit Input(QChar x)
- : m_key(x.unicode()), m_xkey(x.unicode()), m_modifiers(0), m_text(x)
- {
- if (x.isUpper())
- m_modifiers = Qt::ShiftModifier;
- else if (x.isLower())
- m_key = x.toUpper().unicode();
- }
- Input(int k, int m, const QString &t = QString())
- : m_key(k), m_modifiers(cleanModifier(m)), m_text(t)
- {
- // On Mac, QKeyEvent::text() returns non-empty strings for
- // cursor keys. This breaks some of the logic later on
- // relying on text() being empty for "special" keys.
- // FIXME: Check the real conditions.
- if (m_text.size() == 1 && m_text.at(0).unicode() < ' ')
- m_text.clear();
- // Set text only if input is ascii key without control modifier.
- if (m_text.isEmpty() && k <= 0x7f && (m & (HostOsInfo::controlModifier())) == 0) {
- QChar c = QChar::fromAscii(k);
- m_text = QString((m & ShiftModifier) != 0 ? c.toUpper() : c.toLower());
- }
- // m_xkey is only a cache.
- m_xkey = (m_text.size() == 1 ? m_text.at(0).unicode() : m_key);
- }
- bool isValid() const
- {
- return m_key != 0 || !m_text.isNull();
- }
- bool isDigit() const
- {
- return m_xkey >= QLatin1Char('0') && m_xkey <= QLatin1Char('9');
- }
- bool isKey(int c) const
- {
- return !m_modifiers && m_key == c;
- }
- bool isBackspace() const
- {
- return m_key == Key_Backspace || isControl('h');
- }
- bool isReturn() const
- {
- return m_key == QLatin1Char('\n') || m_key == Key_Return || m_key == Key_Enter;
- }
- bool isEscape() const
- {
- return isKey(Key_Escape) || isKey(27) || isControl('c')
- || isControl(Key_BracketLeft);
- }
- bool is(int c) const
- {
- return m_xkey == c && m_modifiers != int(HostOsInfo::controlModifier());
- }
- bool isControl(int c) const
- {
- return m_modifiers == int(HostOsInfo::controlModifier())
- && (m_xkey == c || m_xkey + 32 == c || m_xkey + 64 == c || m_xkey + 96 == c);
- }
- bool isShift(int c) const
- {
- return m_modifiers == Qt::ShiftModifier && m_xkey == c;
- }
- bool operator<(const Input &a) const
- {
- if (m_key != a.m_key)
- return m_key < a.m_key;
- // Text for some mapped key cannot be determined (e.g. <C-J>) so if text is not set for
- // one of compared keys ignore it.
- if (!m_text.isEmpty() && !a.m_text.isEmpty())
- return m_text < a.m_text;
- return m_modifiers < a.m_modifiers;
- }
- bool operator==(const Input &a) const
- {
- return !(*this < a || a < *this);
- }
- bool operator!=(const Input &a) const { return !operator==(a); }
- QString text() const { return m_text; }
- QChar asChar() const
- {
- return (m_text.size() == 1 ? m_text.at(0) : QChar());
- }
- int key() const { return m_key; }
- QChar raw() const
- {
- if (m_key == Key_Tab)
- return QLatin1Char('\t');
- if (m_key == Key_Return)
- return QLatin1Char('\n');
- return m_key;
- }
- QString toString() const
- {
- bool hasCtrl = m_modifiers & int(HostOsInfo::controlModifier());
- QString key = vimKeyNames().key(m_key);
- if (key.isEmpty())
- key = QChar(m_xkey);
- else
- key = QLatin1Char('<') + key + QLatin1Char('>');
- return (hasCtrl ? QString::fromLatin1("^") : QString()) + key;
- }
- QDebug dump(QDebug ts) const
- {
- return ts << m_key << '-' << m_modifiers << '-'
- << quoteUnprintable(m_text);
- }
- private:
- int m_key;
- int m_xkey;
- int m_modifiers;
- QString m_text;
- };
- // mapping to <Nop> (do nothing)
- static const Input Nop(-1, -1, QString());
- QDebug operator<<(QDebug ts, const Input &input) { return input.dump(ts); }
- class Inputs : public QVector<Input>
- {
- public:
- Inputs() : m_noremap(true), m_silent(false) {}
- explicit Inputs(const QString &str, bool noremap = true, bool silent = false)
- : m_noremap(noremap), m_silent(silent)
- {
- parseFrom(str);
- }
- bool noremap() const { return m_noremap; }
- bool silent() const { return m_silent; }
- private:
- void parseFrom(const QString &str);
- bool m_noremap;
- bool m_silent;
- };
- static Input parseVimKeyName(const QString &keyName)
- {
- if (keyName.length() == 1)
- return Input(keyName.at(0));
- const QStringList keys = keyName.split(QLatin1Char('-'));
- const int len = keys.length();
- if (len == 1 && keys.at(0) == _("nop"))
- return Nop;
- int mods = NoModifier;
- for (int i = 0; i < len - 1; ++i) {
- const QString &key = keys[i].toUpper();
- if (key == _("S"))
- mods |= Qt::ShiftModifier;
- else if (key == _("C"))
- mods |= HostOsInfo::controlModifier();
- else
- return Input();
- }
- if (!keys.isEmpty()) {
- const QString key = keys.last();
- if (key.length() == 1) {
- // simple character
- QChar c = key.at(0).toUpper();
- return Input(c.unicode(), mods);
- }
- // find key name
- QMap<QString, int>::ConstIterator it = vimKeyNames().constFind(key.toUpper());
- if (it != vimKeyNames().end())
- return Input(*it, mods);
- }
- return Input();
- }
- void Inputs::parseFrom(const QString &str)
- {
- const int n = str.size();
- for (int i = 0; i < n; ++i) {
- uint c = str.at(i).unicode();
- if (c == QLatin1Char('<')) {
- int j = str.indexOf(QLatin1Char('>'), i);
- Input input;
- if (j != -1) {
- const QString key = str.mid(i+1, j - i - 1);
- if (!key.contains(QLatin1Char('<')))
- input = parseVimKeyName(key);
- }
- if (input.isValid()) {
- append(input);
- i = j;
- } else {
- append(Input(QLatin1Char(c)));
- }
- } else {
- append(Input(QLatin1Char(c)));
- }
- }
- }
- class History
- {
- public:
- History() : m_items(QString()), m_index(0) {}
- void append(const QString &item);
- const QString &move(const QStringRef &prefix, int skip);
- const QString ¤t() const { return m_items[m_index]; }
- const QStringList &items() const { return m_items; }
- void restart() { m_index = m_items.size() - 1; }
- private:
- // Last item is always empty or current search prefix.
- QStringList m_items;
- int m_index;
- };
- void History::append(const QString &item)
- {
- if (item.isEmpty())
- return;
- m_items.pop_back();
- m_items.removeAll(item);
- m_items << item << QString();
- restart();
- }
- const QString &History::move(const QStringRef &prefix, int skip)
- {
- if (!current().startsWith(prefix))
- restart();
- if (m_items.last() != prefix)
- m_items[m_items.size() - 1] = prefix.toString();
- int i = m_index + skip;
- if (!prefix.isEmpty())
- for (; i >= 0 && i < m_items.size() && !m_items[i].startsWith(prefix); i += skip);
- if (i >= 0 && i < m_items.size())
- m_index = i;
- return current();
- }
- // Command line buffer with prompt (i.e. :, / or ? characters), text contents and cursor position.
- class CommandBuffer
- {
- public:
- CommandBuffer() : m_pos(0), m_anchor(0), m_userPos(0), m_historyAutoSave(true) {}
- void setPrompt(const QChar &prompt) { m_prompt = prompt; }
- void setContents(const QString &s) { m_buffer = s; m_anchor = m_pos = s.size(); }
- void setContents(const QString &s, int pos, int anchor = -1)
- {
- m_buffer = s; m_pos = m_userPos = pos; m_anchor = anchor >= 0 ? anchor : pos;
- }
- QStringRef userContents() const { return m_buffer.leftRef(m_userPos); }
- const QChar &prompt() const { return m_prompt; }
- const QString &contents() const { return m_buffer; }
- bool isEmpty() const { return m_buffer.isEmpty(); }
- int cursorPos() const { return m_pos; }
- int anchorPos() const { return m_anchor; }
- bool hasSelection() const { return m_pos != m_anchor; }
- void insertChar(QChar c) { m_buffer.insert(m_pos++, c); m_anchor = m_userPos = m_pos; }
- void insertText(const QString &s)
- {
- m_buffer.insert(m_pos, s); m_anchor = m_userPos = m_pos = m_pos + s.size();
- }
- void deleteChar() { if (m_pos) m_buffer.remove(--m_pos, 1); m_anchor = m_userPos = m_pos; }
- void moveLeft() { if (m_pos) m_userPos = --m_pos; }
- void moveRight() { if (m_pos < m_buffer.size()) m_userPos = ++m_pos; }
- void moveStart() { m_userPos = m_pos = 0; }
- void moveEnd() { m_userPos = m_pos = m_buffer.size(); }
- void setHistoryAutoSave(bool autoSave) { m_historyAutoSave = autoSave; }
- void historyDown() { setContents(m_history.move(userContents(), 1)); }
- void historyUp() { setContents(m_history.move(userContents(), -1)); }
- const QStringList &historyItems() const { return m_history.items(); }
- void historyPush(const QString &item = QString())
- {
- m_history.append(item.isNull() ? contents() : item);
- }
- void clear()
- {
- if (m_historyAutoSave)
- historyPush();
- m_buffer.clear();
- m_anchor = m_userPos = m_pos = 0;
- }
- QString display() const
- {
- QString msg(m_prompt);
- for (int i = 0; i != m_buffer.size(); ++i) {
- const QChar c = m_buffer.at(i);
- if (c.unicode() < 32) {
- msg += QLatin1Char('^');
- msg += QLatin1Char(c.unicode() + 64);
- } else {
- msg += c;
- }
- }
- return msg;
- }
- void deleteSelected()
- {
- if (m_pos < m_anchor) {
- m_buffer.remove(m_pos, m_anchor - m_pos);
- m_anchor = m_pos;
- } else {
- m_buffer.remove(m_anchor, m_pos - m_anchor);
- m_pos = m_anchor;
- }
- }
- bool handleInput(const Input &input)
- {
- if (input.isShift(Key_Left)) {
- moveLeft();
- } else if (input.isShift(Key_Right)) {
- moveRight();
- } else if (input.isShift(Key_Home)) {
- moveStart();
- } else if (input.isShift(Key_End)) {
- moveEnd();
- } else if (input.isKey(Key_Left)) {
- moveLeft();
- m_anchor = m_pos;
- } else if (input.isKey(Key_Right)) {
- moveRight();
- m_anchor = m_pos;
- } else if (input.isKey(Key_Home)) {
- moveStart();
- m_anchor = m_pos;
- } else if (input.isKey(Key_End)) {
- moveEnd();
- m_anchor = m_pos;
- } else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
- historyUp();
- } else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
- historyDown();
- } else if (input.isKey(Key_Delete)) {
- if (hasSelection()) {
- deleteSelected();
- } else {
- if (m_pos < m_buffer.size())
- m_buffer.remove(m_pos, 1);
- else
- deleteChar();
- }
- } else if (!input.text().isEmpty()) {
- if (hasSelection())
- deleteSelected();
- insertText(input.text());
- } else {
- return false;
- }
- return true;
- }
- private:
- QString m_buffer;
- QChar m_prompt;
- History m_history;
- int m_pos;
- int m_anchor;
- int m_userPos; // last position of inserted text (for retrieving history items)
- bool m_historyAutoSave; // store items to history on clear()?
- };
- // Mappings for a specific mode (trie structure)
- class ModeMapping : public QMap<Input, ModeMapping>
- {
- public:
- const Inputs &value() const { return m_value; }
- void setValue(const Inputs &value) { m_value = value; }
- private:
- Inputs m_value;
- };
- // Mappings for all modes
- typedef QHash<char, ModeMapping> Mappings;
- // Iterator for mappings
- class MappingsIterator : public QVector<ModeMapping::Iterator>
- {
- public:
- MappingsIterator(Mappings *mappings, char mode = -1, const Inputs &inputs = Inputs())
- : m_parent(mappings)
- {
- reset(mode);
- walk(inputs);
- }
- // Reset iterator state. Keep previous mode if 0.
- void reset(char mode = 0)
- {
- clear();
- m_lastValid = -1;
- m_invalidInputCount = 0;
- if (mode != 0) {
- m_mode = mode;
- if (mode != -1)
- m_modeMapping = m_parent->find(mode);
- }
- }
- bool isValid() const { return !empty(); }
- // Return true if mapping can be extended.
- bool canExtend() const { return isValid() && !last()->empty(); }
- // Return true if this mapping can be used.
- bool isComplete() const { return m_lastValid != -1; }
- // Return size of current map.
- int mapLength() const { return m_lastValid + 1; }
- int invalidInputCount() const { return m_invalidInputCount; }
- bool walk(const Input &input)
- {
- if (m_modeMapping == m_parent->end())
- return false;
- if (!input.isValid()) {
- m_invalidInputCount += 1;
- return true;
- }
- ModeMapping::Iterator it;
- if (isValid()) {
- it = last()->find(input);
- if (it == last()->end())
- return false;
- } else {
- it = m_modeMapping->find(input);
- if (it == m_modeMapping->end())
- return false;
- }
- if (!it->value().isEmpty())
- m_lastValid = size();
- append(it);
- return true;
- }
- bool walk(const Inputs &inputs)
- {
- foreach (const Input &input, inputs) {
- if (!walk(input))
- return false;
- }
- return true;
- }
- // Return current mapped value. Iterator must be valid.
- const Inputs &inputs() const
- {
- return at(m_lastValid)->value();
- }
- void remove()
- {
- if (isValid()) {
- if (canExtend()) {
- last()->setValue(Inputs());
- } else {
- if (size() > 1) {
- while (last()->empty()) {
- at(size() - 2)->erase(last());
- pop_back();
- if (size() == 1 || !last()->value().isEmpty())
- break;
- }
- if (last()->empty() && last()->value().isEmpty())
- m_modeMapping->erase(last());
- } else if (last()->empty() && !last()->value().isEmpty()) {
- m_modeMapping->erase(last());
- }
- }
- }
- }
- void setInputs(const Inputs &key, const Inputs &inputs, bool unique = false)
- {
- ModeMapping *current = &(*m_parent)[m_mode];
- foreach (const Input &input, key)
- current = &(*current)[input];
- if (!unique || current->value().isEmpty())
- current->setValue(inputs);
- }
- private:
- Mappings *m_parent;
- Mappings::Iterator m_modeMapping;
- int m_lastValid;
- int m_invalidInputCount;
- char m_mode;
- };
- // state of current mapping
- struct MappingState {
- MappingState()
- : maxMapDepth(1000), noremap(false), silent(false) {}
- MappingState(int depth, bool noremap, bool silent)
- : maxMapDepth(depth), noremap(noremap), silent(silent) {}
- int maxMapDepth;
- bool noremap;
- bool silent;
- };
- class FakeVimHandler::Private : public QObject
- {
- Q_OBJECT
- public:
- Private(FakeVimHandler *parent, QWidget *widget);
- EventResult handleEvent(QKeyEvent *ev);
- bool wantsOverride(QKeyEvent *ev);
- bool parseExCommmand(QString *line, ExCommand *cmd);
- bool parseLineRange(QString *line, ExCommand *cmd);
- int parseLineAddress(QString *cmd);
- void parseRangeCount(const QString &line, Range *range) const;
- void handleCommand(const QString &cmd); // Sets m_tc + handleExCommand
- void handleExCommand(const QString &cmd);
- void installEventFilter();
- void passShortcuts(bool enable);
- void setupWidget();
- void restoreWidget(int tabSize);
- friend class FakeVimHandler;
- void init();
- void focus();
- void enterFakeVim(); // Call before any FakeVim processing (import cursor position from editor)
- void leaveFakeVim(); // Call after any FakeVim processing (export cursor position to editor)
- EventResult handleKey(const Input &input);
- EventResult handleDefaultKey(const Input &input);
- void handleMappedKeys();
- void unhandleMappedKeys();
- EventResult handleInsertMode(const Input &);
- EventResult handleReplaceMode(const Input &);
- EventResult handleCommandMode(const Input &);
- // return true only if input in current mode and sub-mode was correctly handled
- bool handleEscape();
- bool handleNoSubMode(const Input &);
- bool handleChangeDeleteSubModes(const Input &);
- bool handleReplaceSubMode(const Input &);
- bool handleFilterSubMode(const Input &);
- bool handleRegisterSubMode(const Input &);
- bool handleShiftSubMode(const Input &);
- bool handleChangeCaseSubMode(const Input &);
- bool handleWindowSubMode(const Input &);
- bool handleYankSubMode(const Input &);
- bool handleZSubMode(const Input &);
- bool handleCapitalZSubMode(const Input &);
- bool handleMovement(const Input &);
- EventResult handleExMode(const Input &);
- EventResult handleSearchSubSubMode(const Input &);
- bool handleCommandSubSubMode(const Input &);
- void fixSelection(); // Fix selection according to current range, move and command modes.
- void finishMovement(const QString &dotCommandMovement = QString());
- void finishMovement(const QString &dotCommandMovement, int count);
- void resetCommandMode();
- QTextCursor search(const SearchData &sd, int startPos, int count, bool showMessages);
- void search(const SearchData &sd, bool showMessages = true);
- void searchNext(bool forward = true);
- void searchBalanced(bool forward, QChar needle, QChar other);
- void highlightMatches(const QString &needle);
- void stopIncrementalFind();
- void updateFind(bool isComplete);
- int mvCount() const { return m_mvcount.isEmpty() ? 1 : m_mvcount.toInt(); }
- int opCount() const { return m_opcount.isEmpty() ? 1 : m_opcount.toInt(); }
- int count() const { return mvCount() * opCount(); }
- QTextBlock block() const { return cursor().block(); }
- int leftDist() const { return position() - block().position(); }
- int rightDist() const { return block().length() - leftDist() - 1; }
- bool atBlockStart() const { return cursor().atBlockStart(); }
- bool atBlockEnd() const { return cursor().atBlockEnd(); }
- bool atEndOfLine() const { return atBlockEnd() && block().length() > 1; }
- bool atDocumentEnd() const { return position() >= lastPositionInDocument(); }
- bool atDocumentStart() const { return cursor().atStart(); }
- bool atEmptyLine(const QTextCursor &tc = QTextCursor()) const;
- bool atBoundary(bool end, bool simple, bool onlyWords = false,
- const QTextCursor &tc = QTextCursor()) const;
- bool atWordBoundary(bool end, bool simple, const QTextCursor &tc = QTextCursor()) const;
- bool atWordStart(bool simple, const QTextCursor &tc = QTextCursor()) const;
- bool atWordEnd(bool simple, const QTextCursor &tc = QTextCursor()) const;
- bool isFirstNonBlankOnLine(int pos);
- int lastPositionInDocument(bool ignoreMode = false) const; // Returns last valid position in doc.
- int firstPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos
- int lastPositionInLine(int line, bool onlyVisibleLines = true) const; // 1 based line, 0 based pos
- int lineForPosition(int pos) const; // 1 based line, 0 based pos
- QString lineContents(int line) const; // 1 based line
- void setLineContents(int line, const QString &contents); // 1 based line
- int blockBoundary(const QString &left, const QString &right,
- bool end, int count) const; // end or start position of current code block
- int lineNumber(const QTextBlock &block) const;
- int linesOnScreen() const;
- int columnsOnScreen() const;
- int linesInDocument() const;
- // The following use all zero-based counting.
- int cursorLineOnScreen() const;
- int cursorLine() const;
- int cursorBlockNumber() const; // "." address
- int physicalCursorColumn() const; // as stored in the data
- int logicalCursorColumn() const; // as visible on screen
- int physicalToLogicalColumn(int physical, const QString &text) const;
- int logicalToPhysicalColumn(int logical, const QString &text) const;
- Column cursorColumn() const; // as visible on screen
- int firstVisibleLine() const;
- void scrollToLine(int line);
- void scrollUp(int count);
- void scrollDown(int count) { scrollUp(-count); }
- void alignViewportToCursor(Qt::AlignmentFlag align, int line = -1,
- bool moveToNonBlank = false);
- void setCursorPosition(const CursorPosition &p);
- void setCursorPosition(QTextCursor *tc, const CursorPosition &p);
- // Helper functions for indenting/
- bool isElectricCharacter(QChar c) const;
- void indentSelectedText(QChar lastTyped = QChar());
- void indentText(const Range &range, QChar lastTyped = QChar());
- void shiftRegionLeft(int repeat = 1);
- void shiftRegionRight(int repeat = 1);
- void moveToFirstNonBlankOnLine();
- void moveToFirstNonBlankOnLine(QTextCursor *tc);
- void moveToTargetColumn();
- void setTargetColumn() {
- m_targetColumn = logicalCursorColumn();
- m_visualTargetColumn = m_targetColumn;
- //qDebug() << "TARGET: " << m_targetColumn;
- }
- void moveToMatchingParanthesis();
- void moveToBoundary(bool simple, bool forward = true);
- void moveToNextBoundary(bool end, int count, bool simple, bool forward);
- void moveToNextBoundaryStart(int count, bool simple, bool forward = true);
- void moveToNextBoundaryEnd(int count, bool simple, bool forward = true);
- void moveToBoundaryStart(int count, bool simple, bool forward = true);
- void moveToBoundaryEnd(int count, bool simple, bool forward = true);
- void moveToNextWord(bool end, int count, bool simple, bool forward, bool emptyLines);
- void moveToNextWordStart(int count, bool simple, bool forward = true, bool emptyLines = true);
- void moveToNextWordEnd(int count, bool simple, bool forward = true, bool emptyLines = true);
- void moveToWordStart(int count, bool simple, bool forward = true, bool emptyLines = true);
- void moveToWordEnd(int count, bool simple, bool forward = true, bool emptyLines = true);
- // Convenience wrappers to reduce line noise.
- void moveToStartOfLine();
- void moveToEndOfLine();
- void moveBehindEndOfLine();
- void moveUp(int n = 1) { moveDown(-n); }
- void moveDown(int n = 1);
- void dump(const char *msg) const {
- qDebug() << msg << "POS: " << anchor() << position()
- << "EXT: " << m_oldExternalAnchor << m_oldExternalPosition
- << "INT: " << m_oldInternalAnchor << m_oldInternalPosition
- << "VISUAL: " << m_visualMode;
- }
- void moveRight(int n = 1) {
- //dump("RIGHT 1");
- QTextCursor tc = cursor();
- tc.movePosition(Right, KeepAnchor, n);
- setCursor(tc);
- if (atEndOfLine())
- emit q->fold(1, false);
- //dump("RIGHT 2");
- }
- void moveLeft(int n = 1) {
- QTextCursor tc = cursor();
- tc.movePosition(Left, KeepAnchor, n);
- setCursor(tc);
- }
- void setAnchor() {
- QTextCursor tc = cursor();
- tc.setPosition(tc.position(), MoveAnchor);
- setCursor(tc);
- }
- void setAnchor(int position) {
- QTextCursor tc = cursor();
- tc.setPosition(tc.anchor(), MoveAnchor);
- tc.setPosition(position, KeepAnchor);
- setCursor(tc);
- }
- void setPosition(int position) {
- QTextCursor tc = cursor();
- tc.setPosition(position, KeepAnchor);
- setCursor(tc);
- }
- void setAnchorAndPosition(int anchor, int position) {
- QTextCursor tc = cursor();
- tc.setPosition(anchor, MoveAnchor);
- tc.setPosition(position, KeepAnchor);
- setCursor(tc);
- }
- // Workaround for occational crash when setting text cursor while in edit block.
- QTextCursor m_cursor;
- QTextCursor cursor() const {
- if (m_editBlockLevel > 0)
- return m_cursor;
- return EDITOR(textCursor());
- }
- void setCursor(const QTextCursor &tc) {
- m_cursor = tc;
- if (m_editBlockLevel == 0)
- EDITOR(setTextCursor(tc));
- }
- bool moveToPreviousParagraph(int count) { return moveToNextParagraph(-count); }
- bool moveToNextParagraph(int count);
- bool handleFfTt(QString key);
- void enterInsertMode();
- void initVisualBlockInsertMode(QChar command);
- void enterReplaceMode();
- void enterCommandMode(Mode returnToMode = CommandMode);
- void enterExMode(const QString &…
Large files files are truncated, but you can click here to view the full file