/src/plugins/fakevim/fakevimhandler.cpp
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
- /**************************************************************************
- **
- ** This file is part of Qt Creator
- **
- ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
- **
- ** Contact: Nokia Corporation (info@qt.nokia.com)
- **
- **
- ** GNU Lesser General Public License Usage
- **
- ** 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, Nokia gives you certain additional
- ** rights. These rights are described in the Nokia Qt LGPL Exception
- ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
- **
- ** Other Usage
- **
- ** Alternatively, this file may be used in accordance with the terms and
- ** conditions contained in a signed written agreement between you and Nokia.
- **
- ** If you have questions regarding the use of this file, please contact
- ** Nokia at info@qt.nokia.com.
- **
- **************************************************************************/
- //
- // 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/qtcassert.h>
- #include <QtCore/QDebug>
- #include <QtCore/QFile>
- #include <QtCore/QObject>
- #include <QtCore/QPointer>
- #include <QtCore/QProcess>
- #include <QtCore/QRegExp>
- #include <QtCore/QTextStream>
- #include <QtCore/QTimer>
- #include <QtCore/QtAlgorithms>
- #include <QtCore/QStack>
- #include <QtGui/QApplication>
- #include <QtGui/QInputMethodEvent>
- #include <QtGui/QKeyEvent>
- #include <QtGui/QLineEdit>
- #include <QtGui/QPlainTextEdit>
- #include <QtGui/QScrollBar>
- #include <QtGui/QTextBlock>
- #include <QtGui/QTextCursor>
- #include <QtGui/QTextDocumentFragment>
- #include <QtGui/QTextEdit>
- #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() << << document()->availableUndoSteps() << 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 EDITOR(s) (m_textedit ? m_textedit->s : m_plaintextedit->s)
- enum {
- #ifdef Q_WS_MAC
- RealControlModifier = Qt::MetaModifier
- #else
- RealControlModifier = Qt::ControlModifier
- #endif
- };
- // Enforce use of RealControlModifier by breaking the compilation.
- #define MetaModifier // Use RealControlModifier instead
- #define ControlModifier // Use RealControlModifier instead
- const int ParagraphSeparator = 0x00002029;
- typedef QLatin1String _;
- 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 >
- TransformSubMode, // Used for ~/gu/gU
- WindowSubMode, // Used for Ctrl-w
- YankSubMode, // Used for y
- ZSubMode, // Used for z
- CapitalZSubMode, // Used for Z
- ReplaceSubMode, // Used for r
- OpenSquareSubMode, // Used for [
- CloseSquareSubMode // Used for ]
- };
- /*! 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 '.
- InvertCaseSubSubMode, // Used for ~.
- DownCaseSubSubMode, // Used for gu.
- UpCaseSubSubMode, // Used for gU.
- TextObjectSubSubMode, // Used for thing like iw, aW, as etc.
- 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,
- EventPassedToCore
- };
- 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 CursorPosition
- {
- // for jump history
- CursorPosition() : position(-1), scrollLine(-1) {}
- CursorPosition(int pos, int line) : position(pos), scrollLine(line) {}
- int position; // Position in document
- int scrollLine; // First visible line
- };
- 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;
- mustMove = true;
- highlightMatches = true;
- highlightCursor = true;
- }
- QString needle;
- bool forward;
- bool mustMove;
- bool highlightMatches;
- bool highlightCursor;
- };
- 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("%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 == '\n')
- res += _("<CR>");
- else
- res += QString("\\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 != ' ' && u != '\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) {}
- Input(int k, int m, const QString &t)
- : 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();
- // m_xkey is only a cache.
- m_xkey = (m_text.size() == 1 ? m_text.at(0).unicode() : m_key);
- }
- bool isDigit() const
- {
- return m_xkey >= '0' && m_xkey <= '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 == Key_Return;
- }
- 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 != RealControlModifier;
- }
- bool isControl(int c) const
- {
- return m_modifiers == RealControlModifier
- && (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
- {
- return a.m_key == m_key && a.m_modifiers == m_modifiers
- && m_text == a.m_text;
- }
- 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 '\t';
- if (m_key == Key_Return)
- return '\n';
- return m_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;
- };
- QDebug operator<<(QDebug ts, const Input &input) { return input.dump(ts); }
- class Inputs : public QVector<Input>
- {
- public:
- Inputs() {}
- explicit Inputs(const QString &str) { parseFrom(str); }
- void parseFrom(const QString &str);
- };
- void Inputs::parseFrom(const QString &str)
- {
- const int n = str.size();
- for (int i = 0; i < n; ++i) {
- uint c0 = str.at(i).unicode(), c1 = 0, c2 = 0, c3 = 0, c4 = 0, c5 = 0;
- if (i + 1 < n)
- c1 = str.at(i + 1).unicode();
- if (i + 2 < n)
- c2 = str.at(i + 2).unicode();
- if (i + 3 < n)
- c3 = str.at(i + 3).unicode();
- if (i + 4 < n)
- c4 = str.at(i + 4).unicode();
- if (i + 5 < n)
- c5 = str.at(i + 5).unicode();
- if (c0 == '<') {
- if ((c1 == 'C' || c1 == 'c') && c2 == '-' && c4 == '>') {
- uint c = (c3 < 90 ? c3 : c3 - 32);
- append(Input(c, RealControlModifier, QString(QChar(c - 64))));
- i += 4;
- } else if (c1 == 'C' && c2 == 'R' && c3 == '>') {
- append(Input(Key_Return, Qt::NoModifier, QString(QChar(13))));
- i += 3;
- } else if (c1 == 'E' && c2 == 's' && c3 == 'c' && c4 == '>') {
- append(Input(Key_Escape, Qt::NoModifier, QString(QChar(27))));
- i += 4;
- } else {
- append(Input(QLatin1Char(c0)));
- }
- } else {
- append(Input(QLatin1Char(c0)));
- }
- }
- }
- // This wraps a string and a "cursor position".
- class CommandBuffer
- {
- public:
- CommandBuffer() : m_pos(0) {}
- void clear() { m_buffer.clear(); m_pos = 0; }
- void setContents(const QString &s) { m_buffer = s; m_pos = s.size(); }
- QString contents() const { return m_buffer; }
- bool isEmpty() const { return m_buffer.isEmpty(); }
- int cursorPos() const { return m_pos; }
- void insertChar(QChar c) { m_buffer.insert(m_pos++, c); }
- void insertText(const QString &s) { m_buffer.insert(m_pos, s); m_pos += s.size(); }
- void deleteChar() { if (m_pos) m_buffer.remove(--m_pos, 1); }
- void moveLeft() { if (m_pos) --m_pos; }
- void moveRight() { if (m_pos < m_buffer.size()) ++m_pos; }
- void moveStart() { m_pos = 0; }
- void moveEnd() { m_pos = m_buffer.size(); }
- QString display() const
- {
- QString msg;
- for (int i = 0; i != m_buffer.size(); ++i) {
- const QChar c = m_buffer.at(i);
- if (c.unicode() < 32) {
- msg += '^';
- msg += QChar(c.unicode() + 64);
- } else {
- msg += c;
- }
- }
- return msg;
- }
- bool handleInput(const Input &input)
- {
- if (input.isKey(Key_Left)) {
- moveLeft();
- } else if (input.isKey(Key_Right)) {
- moveRight();
- } else if (input.isKey(Key_Home)) {
- moveStart();
- } else if (input.isKey(Key_End)) {
- moveEnd();
- } else if (input.isKey(Key_Delete)) {
- if (m_pos < m_buffer.size())
- m_buffer.remove(m_pos, 1);
- } else if (!input.text().isEmpty()) {
- insertText(input.text());
- } else {
- return false;
- }
- return true;
- }
- private:
- QString m_buffer;
- int m_pos;
- };
- class History
- {
- public:
- History() : m_index(0) {}
- void append(const QString &item);
- void down() { m_index = qMin(m_index + 1, m_items.size()); }
- void up() { m_index = qMax(m_index - 1, 0); }
- void restart() { m_index = m_items.size(); }
- QString current() const { return m_items.value(m_index, QString()); }
- QStringList items() const { return m_items; }
- private:
- QStringList m_items;
- int m_index;
- };
- void History::append(const QString &item)
- {
- if (item.isEmpty())
- return;
- m_items.removeAll(item);
- m_items.append(item); m_index = m_items.size() - 1;
- }
- // Mappings for a specific mode.
- class ModeMapping : public QList<QPair<Inputs, Inputs> >
- {
- public:
- ModeMapping() { test(); }
- void test()
- {
- //insert(Inputs() << Input('A') << Input('A'),
- // Inputs() << Input('x') << Input('x'));
- }
- void insert(const Inputs &from, const Inputs &to)
- {
- for (int i = 0; i != size(); ++i)
- if (at(i).first == from) {
- (*this)[i].second = to;
- return;
- }
- append(QPair<Inputs, Inputs>(from, to));
- }
- void remove(const Inputs &from)
- {
- for (int i = 0; i != size(); ++i)
- if (at(i).first == from) {
- removeAt(i);
- return;
- }
- }
- // Returns 'false' if more input is needed to decide whether a mapping
- // needs to be applied. If a decision can be made, return 'true',
- // and replace *input with the mapped data.
- bool mappingDone(Inputs *inputs) const
- {
- // FIXME: inefficient.
- for (int i = 0; i != size(); ++i) {
- const Inputs &haystack = at(i).first;
- // A mapping
- if (startsWith(haystack, *inputs)) {
- if (haystack.size() != inputs->size())
- return false; // This can be extended.
- // Actual mapping.
- *inputs = at(i).second;
- return true;
- }
- }
- // No extensible mapping found. Use inputs as-is.
- return true;
- }
- private:
- static bool startsWith(const Inputs &haystack, const Inputs &needle)
- {
- // Input is already too long.
- if (needle.size() > haystack.size())
- return false;
- for (int i = 0; i != needle.size(); ++i) {
- if (needle.at(i) != haystack.at(i))
- return false;
- }
- return true;
- }
- };
- class FakeVimHandler::Private : public QObject
- {
- Q_OBJECT
- public:
- Private(FakeVimHandler *parent, QWidget *widget);
- EventResult handleEvent(QKeyEvent *ev);
- bool wantsOverride(QKeyEvent *ev);
- 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();
- EventResult handleKey(const Input &);
- Q_SLOT EventResult handleKey2();
- EventResult handleInsertMode(const Input &);
- EventResult handleReplaceMode(const Input &);
- EventResult handleCommandMode(const Input &);
- EventResult handleCommandMode1(const Input &);
- EventResult handleCommandMode2(const Input &);
- EventResult handleRegisterMode(const Input &);
- EventResult handleExMode(const Input &);
- EventResult handleOpenSquareSubMode(const Input &);
- EventResult handleCloseSquareSubMode(const Input &);
- EventResult handleSearchSubSubMode(const Input &);
- EventResult handleCommandSubSubMode(const Input &);
- void finishMovement(const QString &dotCommand = QString());
- void finishMovement(const QString &dotCommand, int count);
- void resetCommandMode();
- void search(const SearchData &sd);
- void searchBalanced(bool forward, QChar needle, QChar other);
- void highlightMatches(const QString &needle);
- void stopIncrementalFind();
- 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 atBlockEnd() const { return cursor().atBlockEnd(); }
- bool atEndOfLine() const { return atBlockEnd() && block().length() > 1; }
- int lastPositionInDocument() const; // Returns last valid position in doc.
- int firstPositionInLine(int line) const; // 1 based line, 0 based pos
- int lastPositionInLine(int line) 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 linesOnScreen() const;
- int columnsOnScreen() const;
- int linesInDocument() const;
- // The following use all zero-based counting.
- int cursorLineOnScreen() const;
- int cursorLine() const;
- 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); }
- CursorPosition cursorPosition() const
- { return CursorPosition(position(), firstVisibleLine()); }
- void setCursorPosition(const CursorPosition &p)
- { setPosition(p.position); scrollToLine(p.scrollLine); }
- // 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 moveToTargetColumn();
- void setTargetColumn() {
- m_targetColumn = logicalCursorColumn();
- m_visualTargetColumn = m_targetColumn;
- //qDebug() << "TARGET: " << m_targetColumn;
- }
- void moveToNextWord(bool simple, bool deleteWord = false);
- void moveToMatchingParanthesis();
- void moveToWordBoundary(bool simple, bool forward, bool changeWord = false);
- // 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);
- //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);
- }
- QTextCursor cursor() const { return EDITOR(textCursor()); }
- void setCursor(const QTextCursor &tc) { EDITOR(setTextCursor(tc)); }
- bool handleFfTt(QString key);
- // Helper function for handleExCommand returning 1 based line index.
- int readLineCode(QString &cmd);
- void enterInsertMode();
- void enterReplaceMode();
- void enterCommandMode();
- void enterExMode();
- void showRedMessage(const QString &msg);
- void showBlackMessage(const QString &msg);
- void notImplementedYet();
- void updateMiniBuffer();
- void updateSelection();
- void updateCursorShape();
- QWidget *editor() const;
- QTextDocument *document() const { return EDITOR(document()); }
- QChar characterAtCursor() const
- { return document()->characterAt(position()); }
- void beginEditBlock()
- { UNDO_DEBUG("BEGIN EDIT BLOCK"); cursor().beginEditBlock(); }
- void beginEditBlock(int pos)
- { setUndoPosition(pos); cursor().beginEditBlock(); }
- void endEditBlock()
- { UNDO_DEBUG("END EDIT BLOCK"); cursor().endEditBlock(); }
- void joinPreviousEditBlock()
- { UNDO_DEBUG("JOIN"); cursor().joinPreviousEditBlock(); }
- void breakEditBlock() {
- QTextCursor tc = cursor();
- tc.clearSelection();
- tc.beginEditBlock();
- tc.insertText("x");
- tc.deletePreviousChar();
- tc.endEditBlock();
- setCursor(tc);
- }
- bool isVisualMode() const { return m_visualMode != NoVisualMode; }
- bool isNoVisualMode() const { return m_visualMode == NoVisualMode; }
- bool isVisualCharMode() const { return m_visualMode == VisualCharMode; }
- bool isVisualLineMode() const { return m_visualMode == VisualLineMode; }
- bool isVisualBlockMode() const { return m_visualMode == VisualBlockMode; }
- void updateEditor();
- void selectWordTextObject(bool inner);
- void selectWORDTextObject(bool inner);
- void selectSentenceTextObject(bool inner);
- void selectParagraphTextObject(bool inner);
- void selectBlockTextObject(bool inner, char left, char right);
- void selectQuotedStringTextObject(bool inner, int type);
- Q_SLOT void importSelection();
- void exportSelection();
- void insertInInsertMode(const QString &text);
- public:
- QTextEdit *m_textedit;
- QPlainTextEdit *m_plaintextedit;
- bool m_wasReadOnly; // saves read-only state of document
- FakeVimHandler *q;
- Mode m_mode;
- bool m_passing; // let the core see the next event
- SubMode m_submode;
- SubSubMode m_subsubmode;
- Input m_subsubdata;
- int m_oldExternalPosition; // copy from last event to check for external changes
- int m_oldExternalAnchor;
- int m_oldInternalPosition; // copy from last event to check for external changes
- int m_oldInternalAnchor;
- int m_oldPosition; // FIXME: Merge with above.
- int m_register;
- QString m_mvcount;
- QString m_opcount;
- MoveType m_movetype;
- RangeMode m_rangemode;
- int m_visualInsertCount;
- bool m_fakeEnd;
- bool m_anchorPastEnd;
- bool m_positionPastEnd; // '$' & 'l' in visual mode can move past eol
- int m_gflag; // whether current command started with 'g'
- QString m_commandPrefix;
- CommandBuffer m_commandBuffer;
- QString m_currentFileName;
- QString m_currentMessage;
- bool m_lastSearchForward;
- bool m_findPending;
- int m_findStartPosition;
- QString m_lastInsertion;
- QString m_lastDeletion;
- int anchor() const { return cursor().anchor(); }
- int position() const { return cursor().position(); }
- struct TransformationData
- {
- TransformationData(const QString &s, const QVariant &d)
- : from(s), extraData(d) {}
- QString from;
- QString to;
- QVariant extraData;
- };
- typedef void (Private::*Transformation)(TransformationData *td);
- void transformText(const Range &range, Transformation transformation,
- const QVariant &extraData = QVariant());
- void insertText(const Register ®);
- void removeText(const Range &range);
- void removeTransform(TransformationData *td);
- void invertCase(const Range &range);
- void invertCaseTransform(TransformationData *td);
- void upCase(const Range &range);
- void upCaseTransform(TransformationData *td);
- void downCase(const Range &range);
- void downCaseTransform(TransformationData *td);
- void replaceText(const Range &range, const QString &str);
- void replaceByStringTransform(TransformationData *td);
- void replaceByCharTransform(TransformationData *td);
- QString selectText(const Range &range) const;
- void setCurrentRange(const Range &range);
- Range currentRange() const { return Range(position(), anchor(), m_rangemode); }
- Range rangeFromCurrentLine() const;
- void yankText(const Range &range, int toregister = '"');
- void pasteText(bool afterCursor);
- // undo handling
- void undo();
- void redo();
- void setUndoPosition(int pos);
- QMap<int, int> m_undoCursorPosition; // revision -> position
- // extra data for '.'
- void replay(const QString &text, int count);
- void setDotCommand(const QString &cmd) { g.dotCommand = cmd; }
- void setDotCommand(const QString &cmd, int n) { g.dotCommand = cmd.arg(n); }
- // extra data for ';'
- QString m_semicolonCount;
- Input m_semicolonType; // 'f', 'F', 't', 'T'
- QString m_semicolonKey;
- // visual modes
- void toggleVisualMode(VisualMode visualMode);
- void leaveVisualMode();
- VisualMode m_visualMode;
- VisualMode m_oldVisualMode;
- // marks as lines
- int mark(int code) const;
- void setMark(int code, int position);
- typedef QHash<int, QTextCursor> Marks;
- typedef QHashIterator<int, QTextCursor> MarksIterator;
- Marks m_marks;
- // vi style configuration
- QVariant config(int code) const { return theFakeVimSetting(code)->value(); }
- bool hasConfig(int code) const { return config(code).toBool(); }
- bool hasConfig(int code, const char *value) const // FIXME
- { return config(code).toString().contains(value); }
- int m_targetColumn; // -1 if past end of line
- int m_visualTargetColumn; // 'l' can move past eol in visual mode only
- // auto-indent
- QString tabExpand(int len) const;
- Column indentation(const QString &line) const;
- void insertAutomaticIndentation(bool goingDown);
- bool removeAutomaticIndentation(); // true if something removed
- // number of autoindented characters
- int m_justAutoIndented;
- void handleStartOfLine();
- void recordJump();
- QVector<CursorPosition> m_jumpListUndo;
- QVector<CursorPosition> m_jumpListRedo;
- QList<QTextEdit::ExtraSelection> m_searchSelections;
- QTextCursor m_searchCursor;
- QString m_oldNeedle;
- bool handleExCommandHelper(const ExCommand &cmd); // Returns success.
- bool handleExPluginCommand(const ExCommand &cmd); // Handled by plugin?
- bool handleExBangCommand(const ExCommand &cmd);
- bool handleExDeleteCommand(const ExCommand &cmd);
- bool handleExGotoCommand(const ExCommand &cmd);
- bool handleExHistoryCommand(const ExCommand &cmd);
- bool handleExRegisterCommand(const ExCommand &cmd);
- bool handleExMapCommand(const ExCommand &cmd);
- bool handleExNohlsearchCommand(const ExCommand &cmd);
- bool handleExNormalCommand(const ExCommand &cmd);
- bool handleExReadCommand(const ExCommand &cmd);
- bool handleExRedoCommand(const ExCommand &cmd);
- bool handleExSetCommand(const ExCommand &cmd);
- bool handleExShiftCommand(const ExCommand &cmd);
- bool handleExSourceCommand(const ExCommand &cmd);
- bool handleExSubstituteCommand(const ExCommand &cmd);
- bool handleExWriteCommand(const ExCommand &cmd);
- bool handleExEchoCommand(const ExCommand &cmd);
- void timerEvent(QTimerEvent *ev);
- void setupCharClass();
- int charClass(QChar c, bool simple) const;
- signed char m_charClass[256];
- bool m_ctrlVActive;
- static struct GlobalData
- {
- GlobalData()
- {
- inputTimer = -1;
- }
- // Input.
- Inputs pendingInput;
- int inputTimer;
- // Repetition.
- QString dotCommand;
- // History for searches.
- History searchHistory;
- // History for :ex commands.
- History commandHistory;
- QHash<int, Register> registers;
- // All mappings.
- typedef QHash<char, ModeMapping> Mappings;
- Mappings mappings;
- } g;
- };
- FakeVimHandler::Private::GlobalData FakeVimHandler::Private::g;
- FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget)
- {
- //static PythonHighlighterRules pythonRules;
- q = parent;
- m_textedit = qobject_cast<QTextEdit *>(widget);
- m_plaintextedit = qobject_cast<QPlainTextEdit *>(widget);
- //new Highlighter(document(), &pythonRules);
- init();
- }
- void FakeVimHandler::Private::init()
- {
- m_mode = CommandMode;
- m_submode = NoSubMode;
- m_subsubmode = NoSubSubMode;
- m_passing = false;
- m_findPending = false;
- m_findStartPosition = -1;
- m_fakeEnd = false;
- m_positionPastEnd = false;
- m_anchorPastEnd = false;
- m_lastSearchForward = true;
- m_register = '"';
- m_gflag = false;
- m_visualMode = NoVisualMode;
- m_oldVisualMode = NoVisualMode;
- m_targetColumn = 0;
- m_visualTargetColumn = 0;
- m_movetype = MoveInclusive;
- m_justAutoIndented = 0;
- m_rangemode = RangeCharMode;
- m_ctrlVActive = false;
- m_oldInternalAnchor = -1;
- m_oldInternalPosition = -1;
- m_oldExternalAnchor = -1;
- m_oldExternalPosition = -1;
- m_oldPosition = -1;
- setupCharClass();
- }
- bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev)
- {
- const int key = ev->key();
- const int mods = ev->modifiers();
- KEY_DEBUG("SHORTCUT OVERRIDE" << key << " PASSING: " << m_passing);
- if (key == Key_Escape) {
- if (m_subsubmode == SearchSubSubMode)
- return true;
- // Not sure this feels good. People often hit Esc several times.
- if (isNoVisualMode()
- && m_mode == CommandMode
- && m_submode == NoSubMode
- && m_opcount.isEmpty()
- && m_mvcount.isEmpty())
- return false;
- return true;
- }
- // We are interested in overriding most Ctrl key combinations.
- if (mods == RealControlModifier
- && !config(ConfigPassControlKey).toBool()
- && ((key >= Key_A && key <= Key_Z && key != Key_K)
- || key == Key_BracketLeft || key == Key_BracketRight)) {
- // Ctrl-K is special as it is the Core's default notion of Locator
- if (m_passing) {
- KEY_DEBUG(" PASSING CTRL KEY");
- // We get called twice on the same key
- //m_passing = false;
- return false;
- }
- KEY_DEBUG(" NOT PASSING CTRL KEY");
- //updateMiniBuffer();
- return true;
- }
- // Let other shortcuts trigger.
- return false;
- }
- EventResult FakeVimHandler::Private::handleEvent(QKeyEvent *ev)
- {
- const int key = ev->key();
- const int mods = ev->modifiers();
- if (key == Key_Shift || key == Key_Alt || key == Key_Control
- || key == Key_Alt || key == Key_AltGr || key == Key_Meta)
- {
- KEY_DEBUG("PLAIN MODIFIER");
- return EventUnhandled;
- }
- if (m_passing) {
- passShortcuts(false);
- KEY_DEBUG("PASSING PLAIN KEY..." << ev->key() << ev->text());
- //if (input.is(',')) { // use ',,' to leave, too.
- // qDebug() << "FINISHED...";
- // return EventHandled;
- //}
- m_passing = false;
- updateMiniBuffer();
- KEY_DEBUG(" PASS TO CORE");
- return EventPassedToCore;
- }
- bool inSnippetMode = false;
- QMetaObject::invokeMethod(editor(),
- "inSnippetMode", Q_ARG(bool *, &inSnippetMode));
- if (inSnippetMode)
- return EventPassedToCore;
- // Fake "End of line"
- //m_tc = cursor();
- //bool hasBlock = false;
- //emit q->requestHasBlockSelection(&hasBlock);
- //qDebug() << "IMPORT BLOCK 2:" << hasBlock;
- //if (0 && hasBlock) {
- // (pos > anc) ? --pos : --anc;
- importSelection();
- // Position changed externally, e.g. by code completion.
- if (position() != m_oldPosition) {
- setTargetColumn();
- //qDebug() << "POSITION CHANGED EXTERNALLY";
- if (m_mode == InsertMode) {
- int dist = position() - m_oldPosition;
- // Try to compensate for code completion
- if (dist > 0 && dist <= physicalCursorColumn()) {
- Range range(m_oldPosition, position());
- m_lastInsertion.append(selectText(range));
- }
- } else if (!isVisualMode()) {
- if (atEndOfLine())
- moveLeft();
- }
- }
- QTextCursor tc = cursor();
- tc.setVisualNavigation(true);
- setCursor(tc);
- if (m_fakeEnd)
- moveRight();
- //if ((mods & RealControlModifier) != 0) {
- // if (key >= Key_A && key <= Key_Z)
- // key = shift(key); // make it lower case
- // key = control(key);
- //} else if (key >= Key_A && key <= Key_Z && (mods & Qt::ShiftModifier) == 0) {
- // key = shift(key);
- //}
- //QTC_ASSERT(m_mode == InsertMode || m_mode == ReplaceMode
- // || !atBlockEnd() || block().length() <= 1,
- // qDebug() << "Cursor at EOL before key handler");
- EventResult result = handleKey(Input(key, mods, ev->text()));
- // The command might have destroyed the editor.
- if (m_textedit || m_plaintextedit) {
- // We fake vi-style end-of-line behaviour
- m_fakeEnd = atEndOfLine() && m_mode == CommandMode && !isVisualBlockMode();
- //QTC_ASSERT(m_mode == InsertMode || m_mode == ReplaceMode
- // || !atBlockEnd() || block().length() <= 1,
- // qDebug() << "Cursor at EOL after key handler");
- if (m_fakeEnd)
- moveLeft();
- m_oldPosition = position();
- if (hasConfig(ConfigShowMarks))
- updateSelection();
- exportSelection();
- updateCursorShape();
- }
- return result;
- }
- void FakeVimHandler::Private::installEventFilter()
- {
- EDITOR(viewport()->installEventFilter(q));
- EDITOR(installEventFilter(q));
- }
- void FakeVimHandler::Private::setupWidget()
- {
- enterCommandMode();
- if (m_textedit) {
- m_textedit->setLineWrapMode(QTextEdit::NoWrap);
- } else if (m_plaintextedit) {
- m_plaintextedit->setLineWrapMode(QPlainTextEdit::NoWrap);
- }
- m_wasReadOnly = EDITOR(isReadOnly());
- updateEditor();
- importSelection();
- updateMiniBuffer();
- updateCursorShape();
- }
- void FakeVimHandler::Private::exportSelection()
- {
- int pos = position();
- int anc = anchor();
- m_oldInternalPosition = pos;
- m_oldInternalAnchor = anc;
- if (isVisualMode()) {
- if (pos >= anc)
- setAnchorAndPosition(anc, pos + 1);
- else
- setAnchorAndPosition(anc + 1, pos);
- if (m_visualMode == VisualBlockMode) {
- emit q->requestSetBlockSelection(false);
- emit q->requestSetBlockSelection(true);
- } else if (m_visualMode == VisualLineMode) {
- const int posLine = lineForPosition(pos);
- const int ancLine = lineForPosition(anc);
- if (anc < pos) {
- pos = lastPositionInLine(posLine);
- anc = firstPositionInLine(ancLine);
- } else {
- pos = firstPositionInLine(posLine);
- anc = lastPositionInLine(ancLine);
- }
- setAnchorAndPosition(anc, pos);
- } else if (m_visualMode == VisualCharMode) {
- /* Nothing */
- } else {
- QTC_CHECK(false);
- }
- } else {
- setAnchorAndPosition(pos, pos);
- }
- m_oldExternalPosition = position();
- m_oldExternalAnchor = anchor();
- m_oldVisualMode = m_visualMode;
- }
- void FakeVimHandler::Private::importSelection()
- {
- bool hasBlock = false;
- emit q->requestHasBlockSelection(&hasBlock);
- if (position() == m_oldExternalPosition
- && anchor() == m_oldExternalAnchor) {
- // Undo drawing correction.
- m_visualMode = m_oldVisualMode;
- setAnchorAndPosition(m_oldInternalAnchor, m_oldInternalPosition);
- //setMark('<', m_oldInternalAnchor);
- //setMark('>', m_oldInternalPosition);
- } else {
- // Import new selection.
- Qt::KeyboardModifiers mods = QApplication::keyboardModifiers();
- if (cursor().hasSelection()) {
- if (mods & RealControlModifier)
- m_visualMode = VisualBlockMode;
- else if (mods & Qt::AltModifier)
- m_visualMode = VisualBlockMode;
- else if (mods & Qt::ShiftModifier)
- m_visualMode = VisualLineMode;
- else
- m_visualMode = VisualCharMode;
- } else {
- m_visualMode = NoVisualMode;
- }
- //dump("IS @");
- //setMark('<', tc.anchor());
- //setMark('>', tc.position());
- }
- }
- void FakeVimHandler::Private::updateEditor()
- {
- const int charWidth = QFontMetrics(EDITOR(font())).width(QChar(' '));
- EDITOR(setTabStopWidth(charWidth * config(ConfigTabStop).toInt()));
- setupCharClass();
- }
- void FakeVimHandler::Private::restoreWidget(int tabSize)
- {
- //showBlackMessage(QString());
- //updateMiniBuffer();
- //EDITOR(removeEventFilter(q));
- //EDITOR(setReadOnly(m_wasReadOnly));
- const int charWidth = QFontMetrics(EDITOR(font())).width(QChar(' '));
- EDITOR(setTabStopWidth(charWidth * tabSize));
- m_visualMode = NoVisualMode;
- // Force "ordinary" cursor.
- m_mode = InsertMode;
- m_submode = NoSubMode;
- m_subsubmode = NoSubSubMode;
- updateCursorShape();
- updateSelection();
- }
- EventResult FakeVimHandler::Private::handleKey(const Input &input)
- {
- KEY_DEBUG("HANDLE INPUT: " << input << " MODE: " << mode);
- if (m_mode == ExMode)
- return handleExMode(input);
- if (m_subsubmode == SearchSubSubMode)
- return handleSearchSubSubMode(input);
- if (m_mode == InsertMode || m_mode == ReplaceMode || m_mode == CommandMode) {
- g.pendingInput.append(input);
- const char code = m_mode == InsertMode ? 'i' : 'n';
- if (g.mappings[code].mappingDone(&g.pendingInput))
- return handleKey2();
- if (g.inputTimer != -1)
- killTimer(g.inputTimer);
- g.inputTimer = startTimer(1000);
- return EventHandled;
- }
- return EventUnhandled;
- }
- EventResult FakeVimHandler::Private::handleKey2()
- {
- setUndoPosition(position());
- if (m_mode == InsertMode) {
- EventResult result = EventUnhandled;
- foreach (const Input &in, g.pendingInput) {
- EventResult r = handleInsertMode(in);
- if (r == EventHandled)
- result = EventHandled;
- }
- g.pendingInput.clear();
- return result;
- }
- if (m_mode == ReplaceMode) {
- EventResult result = EventUnhandled;
- foreach (const Input &in, g.pendingInput) {
- EventResult r = handleReplaceMode(in);
- if (r == EventHandled)
- result = EventHandled;
- }
- g.pendingInput.clear();
- return result;
- }
- if (m_mode == CommandMode) {
- EventResult result = EventUnhandled;
- foreach (const Input &in, g.pendingInput) {
- EventResult r = handleCommandMode(in);
- if (r == EventHandled)
- result = EventHandled;
- }
- g.pendingInput.clear();
- return result;
- }
- return EventUnhandled;
- }
- void FakeVimHandler::Private::timerEvent(QTimerEvent *ev)
- {
- Q_UNUSED(ev);
- handleKey2();
- }
- void FakeVimHandler::Private::stopIncrementalFind()
- {
- if (m_findPending) {
- m_findPending = false;
- QTextCursor tc = cursor();
- setAnchorAndPosition(m_findStartPosition, tc.selectionStart());
- finishMovement();
- setAnchor();
- }
- }
- void FakeVimHandler::Private::setUndoPosition(int pos)
- {
- //qDebug() << " CURSOR POS: " << m_undoCursorPosition;
- m_undoCursorPosition[document()->availableUndoSteps()] = pos;
- }
- void FakeVimHandler::Private::moveDown(int n)
- {
- #if 0
- // does not work for "hidden" documents like in the autotests
- tc.movePosition(Down, MoveAnchor, n);
- #else
- const int col = position() - block().position();
- const int lastLine = document()->lastBlock().blockNumber();
- const int targetLine = qMax(0, qMin(lastLine, block().blockNumber() + n));
- const QTextBlock &block = document()->findBlockByNumber(targetLine);
- const int pos = block.position();
- setPosition(pos + qMax(0, qMin(block.length() - 2, col)));
- moveToTargetColumn();
- #endif
- }
- void FakeVimHandler::Private::moveToEndOfLine()
- {
- #if 0
- // does not work for "hidden" documents like in the autotests
- tc.movePosition(EndOfLine, MoveAnchor);
- #else
- const int pos = block().position() + block().length() - 2;
- setPosition(qMax(block().position(), pos));
- #endif
- }
- void FakeVimHandler::Private::moveBehindEndOfLine()
- {
- int pos = qMin(block().position() + block().length() - 1,
- lastPositionInDocument());
- setPosition(pos);
- }
- void FakeVimHandler::Private::moveToStartOfLine()
- {
- #if 0
- // does not work for "hidden" documents like in the autotests
- tc.movePosition(StartOfLine, MoveAnchor);
- #else
- setPosition(block().position());
- #endif
- }
- void FakeVimHandler::Private::finishMovement(const QString &dotCommand, int count)
- {
- finishMovement(dotCommand.arg(count));
- }
- void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
- {
- //dump("FINISH MOVEMENT");
- if (m_submode == FilterSubMode) {
- int beginLine = lineForPosition(anchor());
- int endLine = lineForPosition(position());
- setPosition(qMin(anchor(), position()));
- enterExMode();
- m_currentMessage.clear();
- m_commandBuffer.setContents(QString(".,+%1!").arg(qAbs(endLine - beginLine)));
- //g.commandHistory.append(QString());
- updateMiniBuffer();
- return;
- }
- //if (isVisualMode())
- // setMark('>', position());
- if (m_submode == ChangeSubMode
- || m_submode == DeleteSubMode
- || m_submode == YankSubMode
- || m_submode == TransformSubMode) {
- if (m_submode != YankSubMode)
- beginEditBlock();
- if (m_movetype == MoveLineWise)
- m_rangemode = (m_submode == ChangeSubMode)
- ? RangeLineModeExclusive
- : RangeLineMode;
- if (m_movetype == MoveInclusive) {
- if (anchor() <= position()) {
- if (!cursor().atBlockEnd())
- setPosition(position() + 1); // correction
- } else {
- setAnchorAndPosition(anchor() + 1, position());
- }
- }
- if (m_positionPastEnd) {
- const int anc = anchor();
- moveBehindEndOfLine();
- moveRight();
- setAnchorAndPosition(anc, position());
- }
- if (m_anchorPastEnd) {
- setAnchorAndPosition(anchor() + 1, position());
- }
- if (m_submode != TransformSubMode) {
- yankText(currentRange(), m_register);
- if (m_movetype == MoveLineWise)
- g.registers[m_register].rangemode = RangeLineMode;
- }
- m_positionPastEnd = m_anchorPastEnd = false;
- }
- if (m_submode == ChangeSubMode) {
- if (m_rangemode == RangeLineMode)
- m_rangemode = RangeLineModeExclusive;
- removeText(currentRange());
- if (!dotCommand.isEmpty())
- setDotCommand(QLatin1Char('c') + dotCommand);
- if (m_movetype == MoveLineWise)
- insertAutomaticIndentation(true);
- endEditBlock();
- setUndoPosition(position());
- enterInsertMode();
- m_submode = NoSubMode;
- } else if (m_submode == DeleteSubMode) {
- removeText(currentRange());
- if (!dotCommand.isEmpty())
- setDotCommand(QLatin1Char('d') + dotCommand);
- if (m_movetype == MoveLineWise)
- handleStartOfLine();
- m_submode = NoSubMode;
- if (atEndOfLine())
- moveLeft();
- else
- setTargetColumn();
- endEditBlock();
- } else if (m_submode == YankSubMode) {
- m_submode = NoSubMode;
- const int la = lineForPosition(anchor());
- const int lp = lineForPosition(position());
- if (m_register != '"') {
- setPosition(mark(m_register));
- moveToStartOfLine();
- } else {
- if (anchor() <= position())
- setPosition(anchor());
- }
- if (la != lp)
- showBlackMessage(QString("%1 lines yanked").arg(qAbs(la - lp) + 1));
- } else if (m_submode == TransformSubMode) {
- if (m_subsubmode == InvertCaseSubSubMode) {
- invertCase(currentRange());
- if (!dotCommand.isEmpty())
- setDotCommand(QLatin1Char('~') + dotCommand);
- } else if (m_subsubmode == UpCaseSubSubMode) {
- upCase(currentRange());
- if (!dotCommand.isEmpty())
- setDotCommand("gU" + dotCommand);
- } else if (m_subsubmode == DownCaseSubSubMode) {
- downCase(currentRange());
- if (!dotCommand.isEmpty())
- setDotCommand("gu" + dotCommand);
- }
- m_submode = NoSubMode;
- m_subsubmode = NoSubSubMode;
- setPosition(qMin(anchor(), position()));
- if (m_movetype ==…
Large files files are truncated, but you can click here to view the full file