From c381bf6d47284d5632bbaa99b914b31fe69c92cc Mon Sep 17 00:00:00 2001
From: Elliu <elliu@hashi.re>
Date: Thu, 15 Feb 2024 00:59:09 +0900
Subject: [PATCH] UI: Delete ScriptViews

---
 src/UI/FakeVim/FakeVimActions.cc        |  184 -
 src/UI/FakeVim/FakeVimActions.hh        |  122 -
 src/UI/FakeVim/FakeVimHandler.cc        | 9657 -----------------------
 src/UI/FakeVim/FakeVimHandler.hh        |  172 -
 src/UI/FakeVim/FakeVimTr.hh             |   10 -
 src/UI/MainWindow.cc                    |   29 -
 src/UI/MainWindow.hh                    |    1 -
 src/UI/ScriptDocumentView.cc            |   97 +-
 src/UI/ScriptDocumentView.hh            |   17 -
 src/UI/ScriptViews/EditorProxy.cc       |  327 -
 src/UI/ScriptViews/EditorProxy.hh       |   66 -
 src/UI/ScriptViews/ScriptEditor.cc      |  167 -
 src/UI/ScriptViews/ScriptEditor.hh      |   57 -
 src/UI/ScriptViews/ScriptHighlighter.cc |  177 -
 src/UI/ScriptViews/ScriptHighlighter.hh |   58 -
 15 files changed, 1 insertion(+), 11140 deletions(-)
 delete mode 100644 src/UI/FakeVim/FakeVimActions.cc
 delete mode 100644 src/UI/FakeVim/FakeVimActions.hh
 delete mode 100644 src/UI/FakeVim/FakeVimHandler.cc
 delete mode 100644 src/UI/FakeVim/FakeVimHandler.hh
 delete mode 100644 src/UI/FakeVim/FakeVimTr.hh
 delete mode 100644 src/UI/ScriptViews/EditorProxy.cc
 delete mode 100644 src/UI/ScriptViews/EditorProxy.hh
 delete mode 100644 src/UI/ScriptViews/ScriptEditor.cc
 delete mode 100644 src/UI/ScriptViews/ScriptEditor.hh
 delete mode 100644 src/UI/ScriptViews/ScriptHighlighter.cc
 delete mode 100644 src/UI/ScriptViews/ScriptHighlighter.hh

diff --git a/src/UI/FakeVim/FakeVimActions.cc b/src/UI/FakeVim/FakeVimActions.cc
deleted file mode 100644
index afb8aa91..00000000
--- a/src/UI/FakeVim/FakeVimActions.cc
+++ /dev/null
@@ -1,184 +0,0 @@
-#include "PreCompiledHeaders.hh"
-#include "UI/FakeVim/FakeVimActions.hh"
-#include "UI/FakeVim/FakeVimHandler.hh"
-#include "Lib/Utils.hh"
-
-namespace Vivy
-{
-#ifdef FAKEVIM_STANDALONE
-FvBaseAspect::FvBaseAspect() {}
-FvBaseAspect::~FvBaseAspect() {}
-FvBoolAspect::~FvBoolAspect() {}
-FvIntegerAspect::~FvIntegerAspect() {}
-FvStringAspect::~FvStringAspect() {}
-FvAspectContainer::~FvAspectContainer() {}
-
-void
-FvBaseAspect::setValue(const QVariant &value)
-{
-    m_value = value;
-}
-
-QVariant
-FvBaseAspect::value() const
-{
-    return m_value;
-}
-
-void
-FvBaseAspect::setDefaultValue(const QVariant &value)
-{
-    m_defaultValue = value;
-    m_value        = value;
-}
-
-QVariant
-FvBaseAspect::defaultValue() const
-{
-    return m_defaultValue;
-}
-
-void
-FvBaseAspect::setSettingsKey(const QString &group, const QString &key)
-{
-    m_settingsGroup = group;
-    m_settingsKey   = key;
-}
-
-QString
-FvBaseAspect::settingsKey() const
-{
-    return m_settingsKey;
-}
-#endif
-
-FakeVimSettings::FakeVimSettings()
-{
-#ifndef FAKEVIM_STANDALONE
-    setup(&useFakeVim, false, "UseFakeVim", {}, tr("Use FakeVim"));
-#endif
-    // Specific FakeVim settings
-    setup(&readVimRc, false, "ReadVimRc", {}, tr("Read .vimrc from location:"));
-    setup(&vimRcPath, QString(), "VimRcPath", {}, {}); // tr("Path to .vimrc")
-    setup(&showMarks, false, "ShowMarks", "sm", tr("Show position of text marks"));
-    setup(&passControlKey, false, "PassControlKey", "pck", tr("Pass control keys"));
-    setup(&passKeys, true, "PassKeys", "pk", tr("Pass keys in insert mode"));
-
-    // Emulated Vsetting
-    setup(&startOfLine, true, "StartOfLine", "sol", tr("Start of line"));
-    setup(&tabStop, 8, "TabStop", "ts", tr("Tabulator size:"));
-    setup(&smartTab, false, "SmartTab", "sta", tr("Smart tabulators"));
-    setup(&hlSearch, true, "HlSearch", "hls", tr("Highlight search results"));
-    setup(&shiftWidth, 8, "ShiftWidth", "sw", tr("Shift width:"));
-    setup(&expandTab, false, "ExpandTab", "et", tr("Expand tabulators"));
-    setup(&autoIndent, false, "AutoIndent", "ai", tr("Automatic indentation"));
-    setup(&smartIndent, false, "SmartIndent", "si", tr("Smart tabulators"));
-    setup(&incSearch, true, "IncSearch", "is", tr("Incremental search"));
-    setup(&useCoreSearch, false, "UseCoreSearch", "ucs", tr("Use search dialog"));
-    setup(&smartCase, false, "SmartCase", "scs", tr("Use smartcase"));
-    setup(&ignoreCase, false, "IgnoreCase", "ic", tr("Use ignorecase"));
-    setup(&wrapScan, true, "WrapScan", "ws", tr("Use wrapscan"));
-    setup(&tildeOp, false, "TildeOp", "top", tr("Use tildeop"));
-    setup(&showCmd, true, "ShowCmd", "sc", tr("Show partial command"));
-    setup(&relativeNumber, false, "RelativeNumber", "rnu",
-          tr("Show line numbers relative to cursor"));
-    setup(&blinkingCursor, false, "BlinkingCursor", "bc", tr("Blinking cursor"));
-    setup(&scrollOff, 0, "ScrollOff", "so", tr("Scroll offset:"));
-    setup(&backspace, "indent,eol,start", "Backspace", "bs", tr("Backspace:"));
-    setup(&isKeyword, "@,48-57,_,192-255,a-z,A-Z", "IsKeyword", "isk", tr("Keyword characters:"));
-    setup(&clipboard, {}, "Clipboard", "cb", tr(""));
-    setup(&formatOptions, {}, "formatoptions", "fo", tr(""));
-
-    // Emulated plugins
-    setup(&emulateVimCommentary, false, "commentary", {}, "vim-commentary");
-    setup(&emulateReplaceWithRegister, false, "ReplaceWithRegister", {}, "ReplaceWithRegister");
-    setup(&emulateExchange, false, "exchange", {}, "vim-exchange");
-    setup(&emulateArgTextObj, false, "argtextobj", {}, "argtextobj.vim");
-    setup(&emulateSurround, false, "surround", {}, "vim-surround");
-
-    // Some polish
-    useFakeVim.setDisplayName(tr("Use Vim-style Editing"));
-
-    relativeNumber.setToolTip(tr("Displays line numbers relative to the line containing "
-                                 "text cursor."));
-
-    passControlKey.setToolTip(
-        tr("Does not interpret key sequences like Ctrl-S in FakeVim "
-           "but handles them as regular shortcuts. This gives easier access to core functionality "
-           "at the price of losing some features of FakeVim."));
-
-    passKeys.setToolTip(tr("Does not interpret some key presses in insert mode so that "
-                           "code can be properly completed and expanded."));
-
-    tabStop.setToolTip(tr("Vim tabstop option."));
-
-#ifndef FAKEVIM_STANDALONE
-    backspace.setDisplayStyle(FvStringAspect::LineEditDisplay);
-    isKeyword.setDisplayStyle(FvStringAspect::LineEditDisplay);
-
-    const QString vimrcDefault =
-        QLatin1String(HostOsInfo::isAnyUnixHost() ? "$HOME/.vimrc" : "%USERPROFILE%\\_vimrc");
-    vimRcPath.setExpectedKind(PathChooser::File);
-    vimRcPath.setToolTip(tr("Keep empty to use the default path, i.e. "
-                            "%USERPROFILE%\\_vimrc on Windows, ~/.vimrc otherwise."));
-    vimRcPath.setPlaceHolderText(tr("Default: %1").arg(vimrcDefault));
-    vimRcPath.setDisplayStyle(FvStringAspect::PathChooserDisplay);
-#endif
-}
-
-FakeVimSettings::~FakeVimSettings() = default;
-
-FvBaseAspect *
-FakeVimSettings::item(const QString &name)
-{
-    return m_nameToAspect.value(name, nullptr);
-}
-
-QString
-FakeVimSettings::trySetValue(const QString &name, const QString &value)
-{
-    FvBaseAspect *aspect = m_nameToAspect.value(name, nullptr);
-    if (!aspect)
-        return tr("Unknown option: %1").arg(name);
-    if (aspect == &tabStop || aspect == &shiftWidth) {
-        if (value.toInt() <= 0)
-            return tr("Argument must be positive: %1=%2").arg(name).arg(value);
-    }
-    aspect->setValue(value);
-    return QString();
-}
-
-void
-FakeVimSettings::setup(FvBaseAspect *aspect, const QVariant &value, const QString &settingsKey,
-                       const QString &shortName, const QString &labelText)
-{
-    aspect->setSettingsKey("FakeVim", settingsKey);
-    aspect->setDefaultValue(value);
-#ifndef FAKEVIM_STANDALONE
-    aspect->setLabelText(labelText);
-    aspect->setAutoApply(false);
-    registerAspect(aspect);
-
-    if (auto boolAspect = dynamic_cast<FvBoolAspect *>(aspect))
-        boolAspect->setLabelPlacement(FvBoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
-#else
-    Q_UNUSED(labelText)
-#endif
-
-    const QString longName = settingsKey.toLower();
-    if (!longName.isEmpty()) {
-        m_nameToAspect[longName] = aspect;
-        m_aspectToName[aspect]   = longName;
-    }
-    if (!shortName.isEmpty())
-        m_nameToAspect[shortName] = aspect;
-}
-
-FakeVimSettings *
-fakeVimSettings()
-{
-    static FakeVimSettings s;
-    return &s;
-}
-
-}
diff --git a/src/UI/FakeVim/FakeVimActions.hh b/src/UI/FakeVim/FakeVimActions.hh
deleted file mode 100644
index 4a161625..00000000
--- a/src/UI/FakeVim/FakeVimActions.hh
+++ /dev/null
@@ -1,122 +0,0 @@
-#pragma once
-
-#include "PreCompiledHeaders.hh"
-#define FAKEVIM_STANDALONE
-
-namespace Vivy
-{
-class FvBaseAspect {
-public:
-    FvBaseAspect();
-    virtual ~FvBaseAspect();
-
-    void setValue(const QVariant &value);
-    QVariant value() const;
-    void setDefaultValue(const QVariant &value);
-    QVariant defaultValue() const;
-    void setSettingsKey(const QString &group, const QString &key);
-    QString settingsKey() const;
-    void setCheckable(bool) {}
-    void setDisplayName(const QString &) {}
-    void setToolTip(const QString &) {}
-
-private:
-    QVariant m_value;
-    QVariant m_defaultValue;
-    QString m_settingsGroup;
-    QString m_settingsKey;
-};
-
-class FvBoolAspect : public FvBaseAspect {
-public:
-    ~FvBoolAspect() override;
-    bool value() const { return FvBaseAspect::value().toBool(); }
-};
-
-class FvIntegerAspect : public FvBaseAspect {
-public:
-    ~FvIntegerAspect() override;
-    qint64 value() const { return FvBaseAspect::value().toLongLong(); }
-};
-
-class FvStringAspect : public FvBaseAspect {
-public:
-    ~FvStringAspect() override;
-    QString value() const { return FvBaseAspect::value().toString(); }
-};
-
-class FvAspectContainer : public FvBaseAspect {
-public:
-    ~FvAspectContainer() override;
-};
-
-class FakeVimSettings final : public FvAspectContainer {
-    Q_DECLARE_TR_FUNCTIONS(FakeVim)
-
-public:
-    FakeVimSettings();
-    ~FakeVimSettings() override;
-
-    FvBaseAspect *item(const QString &name);
-    QString trySetValue(const QString &name, const QString &value);
-
-    FvBoolAspect useFakeVim;
-    FvBoolAspect readVimRc;
-    FvStringAspect vimRcPath;
-
-    FvBoolAspect startOfLine;
-    FvIntegerAspect tabStop;
-    FvBoolAspect hlSearch;
-    FvBoolAspect smartTab;
-    FvIntegerAspect shiftWidth;
-    FvBoolAspect expandTab;
-    FvBoolAspect autoIndent;
-    FvBoolAspect smartIndent;
-
-    FvBoolAspect incSearch;
-    FvBoolAspect useCoreSearch;
-    FvBoolAspect smartCase;
-    FvBoolAspect ignoreCase;
-    FvBoolAspect wrapScan;
-
-    // command ~ behaves as g~
-    FvBoolAspect tildeOp;
-
-    // indent  allow backspacing over autoindent
-    // eol     allow backspacing over line breaks (join lines)
-    // start   allow backspacing over the start of insert; CTRL-W and CTRL-U
-    //         stop once at the start of insert.
-    FvStringAspect backspace;
-
-    // @,48-57,_,192-255
-    FvStringAspect isKeyword;
-
-    // other actions
-    FvBoolAspect showMarks;
-    FvBoolAspect passControlKey;
-    FvBoolAspect passKeys;
-    FvStringAspect clipboard;
-    FvBoolAspect showCmd;
-    FvIntegerAspect scrollOff;
-    FvBoolAspect relativeNumber;
-    FvStringAspect formatOptions;
-
-    // Plugin emulation
-    FvBoolAspect emulateVimCommentary;
-    FvBoolAspect emulateReplaceWithRegister;
-    FvBoolAspect emulateExchange;
-    FvBoolAspect emulateArgTextObj;
-    FvBoolAspect emulateSurround;
-
-    FvBoolAspect blinkingCursor;
-
-private:
-    void setup(FvBaseAspect *aspect, const QVariant &value, const QString &settingsKey,
-               const QString &shortName, const QString &label);
-
-    QHash<QString, FvBaseAspect *> m_nameToAspect;
-    QHash<FvBaseAspect *, QString> m_aspectToName;
-};
-
-FakeVimSettings *fakeVimSettings();
-}
diff --git a/src/UI/FakeVim/FakeVimHandler.cc b/src/UI/FakeVim/FakeVimHandler.cc
deleted file mode 100644
index 17f564f2..00000000
--- a/src/UI/FakeVim/FakeVimHandler.cc
+++ /dev/null
@@ -1,9657 +0,0 @@
-//
-// 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 "PreCompiledHeaders.hh"
-#include "UI/FakeVim/FakeVimHandler.hh"
-#include "UI/FakeVim/FakeVimActions.hh"
-#include "UI/FakeVim/FakeVimTr.hh"
-#include "Lib/Utils.hh"
-
-//#define DEBUG_KEY  1
-#if defined(DEBUG_KEY) && DEBUG_KEY
-#define KEY_DEBUG(s) logDebug() << s
-#else
-#define KEY_DEBUG(s)
-#endif
-
-//#define DEBUG_UNDO  1
-#if defined(DEBUG_UNDO) && DEBUG_UNDO
-#define UNDO_DEBUG(s) logDebug() << "REV" << revision() << s
-#else
-#define UNDO_DEBUG(s)
-#endif
-
-namespace Vivy
-{
-///////////////////////////////////////////////////////////////////////
-//
-// FakeVimHandler
-//
-///////////////////////////////////////////////////////////////////////
-
-#define EDITOR(s) (m_textedit ? m_textedit->s : m_plaintextedit->s)
-
-#ifdef Q_OS_DARWIN
-static constexpr Qt::KeyboardModifier ControlModifier = Qt::MetaModifier;
-#else
-static constexpr Qt::KeyboardModifier ControlModifier = Qt::ControlModifier;
-#endif
-
-// 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 };
-
-enum BlockInsertMode {
-    NoneBlockInsertMode,
-    AppendBlockInsertMode,
-    AppendToEndOfLineBlockInsertMode,
-    InsertBlockInsertMode,
-    ChangeBlockInsertMode
-};
-
-// 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
-    ExchangeSubMode,            // Used for cx
-    DeleteSurroundingSubMode,   // Used for ds
-    ChangeSurroundingSubMode,   // Used for cs
-    AddSurroundingSubMode,      // Used for ys
-    FilterSubMode,              // Used for !
-    IndentSubMode,              // Used for =
-    RegisterSubMode,            // Used for "
-    ShiftLeftSubMode,           // Used for <
-    ShiftRightSubMode,          // Used for >
-    CommentSubMode,             // Used for gc
-    ReplaceWithRegisterSubMode, // Used for gr
-    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
-    MacroRecordSubMode,         // Used for q
-    MacroExecuteSubMode,        // Used for @
-    CtrlVSubMode,               // Used for Ctrl-v in insert mode
-    CtrlRSubMode                // Used for Ctrl-r in insert mode
-};
-
-// 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,               // Used for /, ?
-    SurroundSubSubMode,             // Used for cs, ds, ys
-    SurroundWithFunctionSubSubMode, // Used for ys{motion}f
-    CtrlVUnicodeSubSubMode          // Used for Ctrl-v based unicode input
-};
-
-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 RangeLineModeExclusive 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() = default;
-    CursorPosition(int block, int columnArg)
-        : line(block)
-        , column(columnArg)
-    {
-    }
-    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   = -1; // Line in document (from 0, folded lines included).
-    int column = -1; // Position on line.
-};
-
-// vi style configuration
-
-class Mark {
-public:
-    Mark(const CursorPosition &pos = CursorPosition(), const QString &fileName = QString())
-        : m_position(pos)
-        , m_fileName(fileName)
-    {
-    }
-
-    bool isValid() const { return m_position.isValid(); }
-
-    bool isLocal(const QString &localFileName) const
-    {
-        return m_fileName.isEmpty() || m_fileName == localFileName;
-    }
-
-    /* Return position of mark within given document.
-     * If saved line number is too big, mark position is at the end of document.
-     * If line number is in document but column is too big, mark position is at the end of line.
-     */
-    CursorPosition position(const QTextDocument *document) const
-    {
-        QTextBlock block = document->findBlockByNumber(m_position.line);
-        CursorPosition pos;
-        if (block.isValid()) {
-            pos.line   = m_position.line;
-            pos.column = qMax(0, qMin(m_position.column, block.length() - 2));
-        } else if (document->isEmpty()) {
-            pos.line   = 0;
-            pos.column = 0;
-        } else {
-            pos.line   = document->blockCount() - 1;
-            pos.column = qMax(0, document->lastBlock().length() - 2);
-        }
-        return pos;
-    }
-
-    const QString &fileName() const { return m_fileName; }
-
-    void setFileName(const QString &fileName) { m_fileName = fileName; }
-
-private:
-    CursorPosition m_position;
-    QString m_fileName;
-};
-using Marks = QHash<QChar, Mark>;
-
-struct State {
-    State() = default;
-    State(int revisionArg, const CursorPosition &positionArg, const Marks &marksArg,
-          VisualMode lastVisualModeArg, bool lastVisualModeInvertedArg)
-        : revision(revisionArg)
-        , position(positionArg)
-        , marks(marksArg)
-        , lastVisualMode(lastVisualModeArg)
-        , lastVisualModeInverted(lastVisualModeInvertedArg)
-    {
-    }
-
-    bool isValid() const { return position.isValid(); }
-
-    int revision = -1;
-    CursorPosition position;
-    Marks marks;
-    VisualMode lastVisualMode   = NoVisualMode;
-    bool lastVisualModeInverted = false;
-};
-
-struct Column {
-    Column(int p, int l)
-        : physical(p)
-        , logical(l)
-    {
-    }
-    int physical; // Number of characters in the data.
-    int logical;  // Column on screen.
-};
-
-struct Register {
-    Register() = default;
-    Register(const QString &c)
-        : contents(c)
-    {
-    }
-    Register(const QString &c, RangeMode m)
-        : contents(c)
-        , rangemode(m)
-    {
-    }
-    QString contents;
-    RangeMode rangemode = RangeCharMode;
-};
-
-struct SearchData {
-    QString needle;
-    bool forward          = true;
-    bool highlightMatches = true;
-};
-
-static QString
-replaceTildeWithHome(QString str)
-{
-    str.replace("~", QDir::homePath());
-    return str;
-}
-
-// If string begins with given prefix remove it with trailing spaces and return true.
-static bool
-eatString(const QString &prefix, QString *str)
-{
-    if (!str->startsWith(prefix))
-        return false;
-    *str = str->mid(prefix.size()).trimmed();
-    return true;
-}
-
-static QRegularExpression
-vimPatternToQtPattern(const QString &needle)
-{
-    /* Transformations (Vim regexp -> QRegularExpression):
-     *   \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
-     */
-
-    // FIXME: Option smartcase should be used only if search was typed by user.
-    const bool ignoreCaseOption = fakeVimSettings()->ignoreCase.value();
-    const bool smartCaseOption  = fakeVimSettings()->smartCase.value();
-    const bool initialIgnoreCase =
-        ignoreCaseOption && !(smartCaseOption && needle.contains(QRegularExpression("[A-Z]")));
-
-    bool ignorecase = initialIgnoreCase;
-
-    QString pattern;
-    pattern.reserve(2 * needle.size());
-
-    bool escape   = false;
-    bool brace    = false;
-    bool embraced = false;
-    bool range    = false;
-    bool curly    = false;
-    for (const QChar &c : needle) {
-        if (brace) {
-            brace = false;
-            if (c == ']') {
-                pattern.append("\\[\\]");
-                continue;
-            }
-            pattern.append('[');
-            escape   = true;
-            embraced = true;
-        }
-        if (embraced) {
-            if (range) {
-                QChar c2 = pattern[pattern.size() - 2];
-                pattern.remove(pattern.size() - 2, 2);
-                pattern.append(c2.toUpper() + '-' + c.toUpper());
-                pattern.append(c2.toLower() + '-' + c.toLower());
-                range = false;
-            } else if (escape) {
-                escape = false;
-                pattern.append(c);
-            } else if (c == '\\') {
-                escape = true;
-            } else if (c == ']') {
-                pattern.append(']');
-                embraced = false;
-            } else if (c == '-') {
-                range = ignorecase && pattern[pattern.size() - 1].isLetter();
-                pattern.append('-');
-            } else if (c.isLetter() && ignorecase) {
-                pattern.append(c.toLower()).append(c.toUpper());
-            } else {
-                pattern.append(c);
-            }
-        } else if (QString("(){}+|?").indexOf(c) != -1) {
-            if (c == '{') {
-                curly = escape;
-            } else if (c == '}' && curly) {
-                curly  = false;
-                escape = true;
-            }
-
-            if (escape)
-                escape = false;
-            else
-                pattern.append('\\');
-            pattern.append(c);
-        } else if (escape) {
-            // escape expression
-            escape = false;
-            if (c == '<' || c == '>')
-                pattern.append("\\b");
-            else if (c == 'a')
-                pattern.append("[a-zA-Z]");
-            else if (c == 'A')
-                pattern.append("[^a-zA-Z]");
-            else if (c == 'h')
-                pattern.append("[A-Za-z_]");
-            else if (c == 'H')
-                pattern.append("[^A-Za-z_]");
-            else if (c == 'c' || c == 'C')
-                ignorecase = (c == 'c');
-            else if (c == 'l')
-                pattern.append("[a-z]");
-            else if (c == 'L')
-                pattern.append("[^a-z]");
-            else if (c == 'o')
-                pattern.append("[0-7]");
-            else if (c == 'O')
-                pattern.append("[^0-7]");
-            else if (c == 'u')
-                pattern.append("[A-Z]");
-            else if (c == 'U')
-                pattern.append("[^A-Z]");
-            else if (c == 'x')
-                pattern.append("[0-9A-Fa-f]");
-            else if (c == 'X')
-                pattern.append("[^0-9A-Fa-f]");
-            else if (c == '=')
-                pattern.append("?");
-            else {
-                pattern.append('\\');
-                pattern.append(c);
-            }
-        } else {
-            // unescaped expression
-            if (c == '\\')
-                escape = true;
-            else if (c == '[')
-                brace = true;
-            else if (c.isLetter() && ignorecase)
-                pattern.append('[').append(c.toLower()).append(c.toUpper()).append(']');
-            else
-                pattern.append(c);
-        }
-    }
-    if (escape)
-        pattern.append('\\');
-    else if (brace)
-        pattern.append('[');
-
-    return QRegularExpression(pattern, initialIgnoreCase ? QRegularExpression::CaseInsensitiveOption
-                                                         : QRegularExpression::NoPatternOption);
-}
-
-static bool
-afterEndOfLine(const QTextDocument *doc, int position)
-{
-    return doc->characterAt(position) == QChar::ParagraphSeparator &&
-           doc->findBlock(position).length() > 1;
-}
-
-static void
-searchForward(QTextCursor *tc, const QRegularExpression &needleExp, int *repeat)
-{
-    const QTextDocument *doc = tc->document();
-    const int startPos       = tc->position();
-
-    QTextDocument::FindFlags flags = {};
-    if (!(needleExp.patternOptions() & QRegularExpression::CaseInsensitiveOption))
-        flags |= QTextDocument::FindCaseSensitively;
-
-    // Search from beginning of line so that matched text is the same.
-    tc->movePosition(QTextCursor::StartOfLine);
-
-    // forward to current position
-    *tc = doc->find(needleExp, *tc, flags);
-    while (!tc->isNull() && tc->anchor() < startPos) {
-        if (!tc->hasSelection())
-            tc->movePosition(QTextCursor::Right);
-        if (tc->atBlockEnd())
-            tc->movePosition(QTextCursor::NextBlock);
-        *tc = doc->find(needleExp, *tc, flags);
-    }
-
-    if (tc->isNull())
-        return;
-
-    --*repeat;
-
-    while (*repeat > 0) {
-        if (!tc->hasSelection())
-            tc->movePosition(QTextCursor::Right);
-        if (tc->atBlockEnd())
-            tc->movePosition(QTextCursor::NextBlock);
-        *tc = doc->find(needleExp, *tc, flags);
-        if (tc->isNull())
-            return;
-        --*repeat;
-    }
-
-    if (!tc->isNull() && afterEndOfLine(doc, tc->anchor()))
-        tc->movePosition(QTextCursor::Left);
-}
-
-static void
-searchBackward(QTextCursor *tc, const QRegularExpression &needleExp, int *repeat)
-{
-    // Search from beginning of line so that matched text is the same.
-    QTextBlock block = tc->block();
-    QString line     = block.text();
-
-    QRegularExpressionMatch match;
-    int i = int(line.indexOf(needleExp, 0, &match));
-    while (i != -1 && i < tc->positionInBlock()) {
-        --*repeat;
-        const int offset = i + qMax(1, int(match.capturedLength()));
-        i                = int(line.indexOf(needleExp, offset, &match));
-        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    = int(line.indexOf(needleExp, 0, &match));
-        while (i != -1) {
-            --*repeat;
-            const int offset = i + qMax(1, int(match.capturedLength()));
-            i                = int(line.indexOf(needleExp, offset, &match));
-            if (i == line.size())
-                i = -1;
-        }
-    }
-
-    if (!block.isValid()) {
-        *tc = QTextCursor();
-        return;
-    }
-
-    i = int(line.indexOf(needleExp, 0, &match));
-    while (*repeat < 0) {
-        const int offset = i + qMax(1, int(match.capturedLength()));
-        i                = int(line.indexOf(needleExp, offset, &match));
-        ++*repeat;
-    }
-    tc->setPosition(block.position() + i);
-    tc->setPosition(tc->position() + int(match.capturedLength()), QTextCursor::KeepAnchor);
-}
-
-// Commands [[, []
-static void
-bracketSearchBackward(QTextCursor *tc, const QString &needleExp, int repeat)
-{
-    const QRegularExpression re(needleExp);
-    QTextCursor tc2 = *tc;
-    tc2.setPosition(tc2.position() - 1);
-    searchBackward(&tc2, re, &repeat);
-    if (repeat <= 1)
-        tc->setPosition(tc2.isNull() ? 0 : tc2.position(), QTextCursor::KeepAnchor);
-}
-
-// Commands ][, ]]
-// When ]] is used after an operator, then also stops below a '}' in the first column.
-static void
-bracketSearchForward(QTextCursor *tc, const QString &needleExp, int repeat, bool searchWithCommand)
-{
-    QRegularExpression re(searchWithCommand ? QString("^\\}|^\\{") : needleExp);
-    QTextCursor tc2 = *tc;
-    tc2.setPosition(tc2.position() + 1);
-    searchForward(&tc2, re, &repeat);
-    if (repeat <= 1) {
-        if (tc2.isNull()) {
-            tc->setPosition(tc->document()->characterCount() - 1, QTextCursor::KeepAnchor);
-        } else {
-            tc->setPosition(tc2.position() - 1, QTextCursor::KeepAnchor);
-            if (searchWithCommand && tc->document()->characterAt(tc->position()).unicode() == '}') {
-                QTextBlock block = tc->block().next();
-                if (block.isValid())
-                    tc->setPosition(block.position(), QTextCursor::KeepAnchor);
-            }
-        }
-    }
-}
-
-static char
-backslashed(char t)
-{
-    switch (t) {
-    case 'e': return 27;
-    case 't': return '\t';
-    case 'r': return '\r';
-    case 'n': return '\n';
-    case 'b': return 8;
-    }
-    return t;
-}
-
-enum class Modifier { NONE, UPPERCASE, LOWERCASE };
-
-static QString
-applyReplacementLetterCases(QString repl, Modifier &toggledModifier,
-                            Modifier &nextCharacterModifier)
-{
-    if (toggledModifier == Modifier::UPPERCASE)
-        repl = repl.toUpper();
-    else if (toggledModifier == Modifier::LOWERCASE)
-        repl = repl.toLower();
-
-    if (nextCharacterModifier == Modifier::UPPERCASE) {
-        repl.replace(0, 1, repl.at(0).toUpper());
-        nextCharacterModifier = Modifier::NONE;
-    } else if (nextCharacterModifier == Modifier::LOWERCASE) {
-        repl.replace(0, 1, repl.at(0).toLower());
-        nextCharacterModifier = Modifier::NONE;
-    }
-    return repl;
-}
-
-static QChar
-applyReplacementLetterCases(QChar repl, Modifier &toggledModifier, Modifier &nextCharacterModifier)
-{
-    if (nextCharacterModifier == Modifier::UPPERCASE) {
-        nextCharacterModifier = Modifier::NONE;
-        return repl.toUpper();
-    } else if (nextCharacterModifier == Modifier::LOWERCASE) {
-        nextCharacterModifier = Modifier::NONE;
-        return repl.toLower();
-    } else if (toggledModifier == Modifier::UPPERCASE)
-        return repl.toUpper();
-    else if (toggledModifier == Modifier::LOWERCASE)
-        return repl.toLower();
-    else
-        return repl;
-}
-
-static bool
-substituteText(QString *text, const QRegularExpression &pattern, const QString &replacement,
-               bool global)
-{
-    bool substituted = false;
-    qsizetype pos    = 0;
-    qsizetype right  = -1;
-    while (true) {
-        const QRegularExpressionMatch match = pattern.match(*text, pos);
-        if (!match.hasMatch())
-            break;
-
-        pos = match.capturedStart();
-
-        // ensure that substitution is advancing towards end of line
-        if (right == text->size() - pos) {
-            ++pos;
-            if (pos == text->size())
-                break;
-            continue;
-        }
-
-        right = text->size() - pos;
-
-        substituted     = true;
-        QString matched = text->mid(pos, match.captured(0).size());
-        QString repl;
-        bool escape                    = false;
-        Modifier toggledModifier       = Modifier::NONE;
-        Modifier nextCharacterModifier = Modifier::NONE;
-        // 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() <= match.lastCapturedIndex()) {
-                        repl += applyReplacementLetterCases(match.captured(c.digitValue()),
-                                                            toggledModifier, nextCharacterModifier);
-                    }
-                } else if (c == 'u') {
-                    nextCharacterModifier = Modifier::UPPERCASE;
-                } else if (c == 'l') {
-                    nextCharacterModifier = Modifier::LOWERCASE;
-                } else if (c == 'U') {
-                    toggledModifier = Modifier::UPPERCASE;
-                } else if (c == 'L') {
-                    toggledModifier = Modifier::LOWERCASE;
-                } else if (c == 'e' || c == 'E') {
-                    nextCharacterModifier = Modifier::NONE;
-                    toggledModifier       = Modifier::NONE;
-                } else {
-                    repl += backslashed(static_cast<char>(c.unicode()));
-                }
-            } else {
-                if (c == '\\')
-                    escape = true;
-                else if (c == '&')
-                    repl += applyReplacementLetterCases(match.captured(0), toggledModifier,
-                                                        nextCharacterModifier);
-                else
-                    repl += applyReplacementLetterCases(c, toggledModifier, nextCharacterModifier);
-            }
-        }
-        text->replace(pos, matched.size(), repl);
-        pos += (repl.isEmpty() && matched.isEmpty()) ? 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) != '\\'))
-            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());
-
-    auto data = new QMimeData;
-    data->setText(content);
-    data->setData(vimMimeText, bytes1);
-    data->setData(vimMimeTextEncoded, bytes2);
-    clipboard->setMimeData(data, clipboardMode);
-}
-
-static QByteArray
-toLocalEncoding(const QString &text)
-{
-#if defined(Q_OS_WIN)
-    return QString(text).replace("\n", "\r\n").toLocal8Bit();
-#else
-    return text.toLocal8Bit();
-#endif
-}
-
-static QString
-fromLocalEncoding(const QByteArray &data)
-{
-#if defined(Q_OS_WIN)
-    return QString::fromLocal8Bit(data).replace("\n", "\r\n");
-#else
-    return QString::fromLocal8Bit(data);
-#endif
-}
-
-static QString
-getProcessOutput(const QString &command, const QString &input)
-{
-    QProcess proc;
-#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
-    QStringList arguments = QProcess::splitCommand(command);
-    QString executable    = arguments.takeFirst();
-    proc.start(executable, arguments);
-#else
-    proc.start(command);
-#endif
-    proc.waitForStarted();
-    proc.write(toLocalEncoding(input));
-    proc.closeWriteChannel();
-
-    // FIXME: Process should be interruptable by user.
-    //        Solution is to create a QObject for each process and emit finished state.
-    proc.waitForFinished();
-
-    return fromLocalEncoding(proc.readAllStandardOutput());
-}
-
-static const QMap<QString, int> &
-vimKeyNames()
-{
-    static const QMap<QString, int> k = { // FIXME: Should be value of mapleader.
-                                          { "LEADER", Key_Backslash },
-
-                                          { "SPACE", Key_Space },
-                                          { "TAB", Key_Tab },
-                                          { "NL", Key_Return },
-                                          { "NEWLINE", Key_Return },
-                                          { "LINEFEED", Key_Return },
-                                          { "LF", Key_Return },
-                                          { "CR", Key_Return },
-                                          { "RETURN", Key_Return },
-                                          { "ENTER", Key_Return },
-                                          { "BS", Key_Backspace },
-                                          { "BACKSPACE", Key_Backspace },
-                                          { "ESC", Key_Escape },
-                                          { "BAR", Key_Bar },
-                                          { "BSLASH", Key_Backslash },
-                                          { "DEL", Key_Delete },
-                                          { "DELETE", Key_Delete },
-                                          { "KDEL", Key_Delete },
-                                          { "UP", Key_Up },
-                                          { "DOWN", Key_Down },
-                                          { "LEFT", Key_Left },
-                                          { "RIGHT", Key_Right },
-
-                                          { "LT", Key_Less },
-                                          { "GT", Key_Greater },
-
-                                          { "F1", Key_F1 },
-                                          { "F2", Key_F2 },
-                                          { "F3", Key_F3 },
-                                          { "F4", Key_F4 },
-                                          { "F5", Key_F5 },
-                                          { "F6", Key_F6 },
-                                          { "F7", Key_F7 },
-                                          { "F8", Key_F8 },
-                                          { "F9", Key_F9 },
-                                          { "F10", Key_F10 },
-
-                                          { "F11", Key_F11 },
-                                          { "F12", Key_F12 },
-                                          { "F13", Key_F13 },
-                                          { "F14", Key_F14 },
-                                          { "F15", Key_F15 },
-                                          { "F16", Key_F16 },
-                                          { "F17", Key_F17 },
-                                          { "F18", Key_F18 },
-                                          { "F19", Key_F19 },
-                                          { "F20", Key_F20 },
-
-                                          { "F21", Key_F21 },
-                                          { "F22", Key_F22 },
-                                          { "F23", Key_F23 },
-                                          { "F24", Key_F24 },
-                                          { "F25", Key_F25 },
-                                          { "F26", Key_F26 },
-                                          { "F27", Key_F27 },
-                                          { "F28", Key_F28 },
-                                          { "F29", Key_F29 },
-                                          { "F30", Key_F30 },
-
-                                          { "F31", Key_F31 },
-                                          { "F32", Key_F32 },
-                                          { "F33", Key_F33 },
-                                          { "F34", Key_F34 },
-                                          { "F35", Key_F35 },
-
-                                          { "INSERT", Key_Insert },
-                                          { "INS", Key_Insert },
-                                          { "KINSERT", Key_Insert },
-                                          { "HOME", Key_Home },
-                                          { "END", Key_End },
-                                          { "PAGEUP", Key_PageUp },
-                                          { "PAGEDOWN", Key_PageDown },
-
-                                          { "KPLUS", Key_Plus },
-                                          { "KMINUS", Key_Minus },
-                                          { "KDIVIDE", Key_Slash },
-                                          { "KMULTIPLY", Key_Asterisk },
-                                          { "KENTER", Key_Enter },
-                                          { "KPOINT", Key_Period },
-
-                                          { "CAPS", Key_CapsLock },
-                                          { "NUM", Key_NumLock },
-                                          { "SCROLL", Key_ScrollLock },
-                                          { "ALTGR", Key_AltGr }
-    };
-
-    return k;
-}
-
-static bool
-isOnlyControlModifier(const Qt::KeyboardModifiers &mods)
-{
-    return (mods ^ ControlModifier) == Qt::NoModifier;
-}
-
-static bool
-isAcceptableModifier(const Qt::KeyboardModifiers &mods)
-{
-    if (mods & ControlModifier) {
-        // Generally, CTRL is not fine, except in combination with ALT.
-        // See QTCREATORBUG-24673
-        return mods & AltModifier;
-    }
-    return true;
-}
-
-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);
-}
-
-bool
-Range::isValid() const
-{
-    return beginPos >= 0 && endPos >= 0;
-}
-
-ExCommand::ExCommand(const QString &c, const QString &a, const Range &r)
-    : cmd(c)
-    , args(a)
-    , range(r)
-{
-}
-
-bool
-ExCommand::matches(const QString &min, const QString &full) const
-{
-    return cmd.startsWith(min) && full.startsWith(cmd);
-}
-
-static QString
-quoteUnprintable(const QString &ba)
-{
-    QString res;
-    for (qsizetype 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)
-{
-    if (col > str.size()) {
-        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 Tr::tr("Mark \"%1\" not set.").arg(text);
-}
-
-static void
-initSingleShotTimer(QTimer *timer, int interval, FakeVimHandler::Private *receiver,
-                    void (FakeVimHandler::Private::*slot)())
-{
-    timer->setSingleShot(true);
-    timer->setInterval(interval);
-    QObject::connect(timer, &QTimer::timeout, receiver, slot);
-}
-
-class Input {
-public:
-    // Remove some extra "information" on Mac.
-    static Qt::KeyboardModifiers cleanModifier(Qt::KeyboardModifiers m)
-    {
-        return m & ~Qt::KeypadModifier;
-    }
-
-    Input() = default;
-    explicit Input(QChar x)
-        : m_key(x.unicode())
-        , m_xkey(x.unicode())
-        , m_text(x)
-    {
-        if (x.isUpper())
-            m_modifiers = Qt::ShiftModifier;
-        else if (x.isLower())
-            m_key = x.toUpper().unicode();
-    }
-
-    Input(int k, Qt::KeyboardModifiers m, const QString &t = QString())
-        : m_key(k)
-        , m_modifiers(cleanModifier(m))
-        , m_text(t)
-    {
-        if (m_text.size() == 1) {
-            QChar x = m_text.at(0);
-
-            // 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 (x.unicode() < ' ' && x.unicode() != 27)
-                m_text.clear();
-            else if (x.isLetter())
-                m_key = x.toUpper().unicode();
-        }
-
-        // Set text only if input is ascii key without control modifier.
-        if (m_text.isEmpty() && k >= 0 && k <= 0x7f && (m & ControlModifier) == 0) {
-            QChar c = QChar(k);
-            if (c.isLetter())
-                m_text = isShift() ? c.toUpper() : c;
-            else if (!isShift())
-                m_text = c;
-        }
-
-        // Normalize <S-TAB>.
-        if (m_key == Qt::Key_Backtab) {
-            m_key = Qt::Key_Tab;
-            m_modifiers |= Qt::ShiftModifier;
-        }
-
-        // 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 >= '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 == '\n' || m_key == Key_Return || m_key == Key_Enter; }
-
-    bool isEscape() const
-    {
-        return isKey(Key_Escape) || isShift(Key_Escape) || isKey(27) || isShift(27) ||
-               isControl('c') || isControl(Key_BracketLeft);
-    }
-
-    bool is(int c) const { return m_xkey == c && isAcceptableModifier(m_modifiers); }
-
-    bool isControl() const { return isOnlyControlModifier(m_modifiers); }
-
-    bool isControl(int c) const
-    {
-        return isControl() &&
-               (m_xkey == c || m_xkey + 32 == c || m_xkey + 64 == c || m_xkey + 96 == c);
-    }
-
-    bool isShift() const { return m_modifiers & Qt::ShiftModifier; }
-
-    bool isShift(int c) const { return isShift() && 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() && m_text != " ")
-            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 toInt(bool *ok, int base) const
-    {
-        const int uc = asChar().unicode();
-        int res;
-        if ('0' <= uc && uc <= '9')
-            res = uc - '0';
-        else if ('a' <= uc && uc <= 'z')
-            res = 10 + uc - 'a';
-        else if ('A' <= uc && uc <= 'Z')
-            res = 10 + uc - 'A';
-        else
-            res = base;
-        *ok = res < base;
-        return *ok ? res : 0;
-    }
-
-    int key() const { return m_key; }
-
-    Qt::KeyboardModifiers modifiers() const { return m_modifiers; }
-
-    // Return raw character for macro recording or dot command.
-    QChar raw() const
-    {
-        if (m_key == Key_Tab)
-            return '\t';
-        if (m_key == Key_Return)
-            return '\n';
-        if (m_key == Key_Escape)
-            return QChar(27);
-        return QChar(m_xkey);
-    }
-
-    QString toString() const
-    {
-        if (!m_text.isEmpty())
-            return QString(m_text).replace("<", "<LT>");
-
-        QString key   = vimKeyNames().key(m_key);
-        bool namedKey = !key.isEmpty();
-
-        if (!namedKey) {
-            if (m_xkey == '<')
-                key = "<LT>";
-            else if (m_xkey == '>')
-                key = "<GT>";
-            else
-                key = QChar(m_xkey);
-        }
-
-        bool shift = isShift();
-        bool ctrl  = isControl();
-        if (shift)
-            key.prepend("S-");
-        if (ctrl)
-            key.prepend("C-");
-
-        if (namedKey || shift || ctrl) {
-            key.prepend('<');
-            key.append('>');
-        }
-
-        return key;
-    }
-
-private:
-    int m_key                         = 0;
-    int m_xkey                        = 0;
-    Qt::KeyboardModifiers m_modifiers = NoModifier;
-    QString m_text;
-};
-
-// mapping to <Nop> (do nothing)
-static const Input Nop(-1, Qt::KeyboardModifiers(-1), QString());
-
-static SubMode
-letterCaseModeFromInput(const Input &input)
-{
-    if (input.is('~'))
-        return InvertCaseSubMode;
-    if (input.is('u'))
-        return DownCaseSubMode;
-    if (input.is('U'))
-        return UpCaseSubMode;
-
-    return NoSubMode;
-}
-
-static SubMode
-indentModeFromInput(const Input &input)
-{
-    if (input.is('<'))
-        return ShiftLeftSubMode;
-    if (input.is('>'))
-        return ShiftRightSubMode;
-    if (input.is('='))
-        return IndentSubMode;
-
-    return NoSubMode;
-}
-
-static SubMode
-changeDeleteYankModeFromInput(const Input &input)
-{
-    if (input.is('c'))
-        return ChangeSubMode;
-    if (input.is('d'))
-        return DeleteSubMode;
-    if (input.is('y'))
-        return YankSubMode;
-
-    return NoSubMode;
-}
-
-static QString
-dotCommandFromSubMode(SubMode submode)
-{
-    if (submode == ChangeSubMode)
-        return QLatin1String("c");
-    if (submode == DeleteSubMode)
-        return QLatin1String("d");
-    if (submode == CommentSubMode)
-        return QLatin1String("gc");
-    if (submode == DeleteSurroundingSubMode)
-        return QLatin1String("ds");
-    if (submode == ChangeSurroundingSubMode)
-        return QLatin1String("c");
-    if (submode == AddSurroundingSubMode)
-        return QLatin1String("y");
-    if (submode == ExchangeSubMode)
-        return QLatin1String("cx");
-    if (submode == ReplaceWithRegisterSubMode)
-        return QLatin1String("gr");
-    if (submode == InvertCaseSubMode)
-        return QLatin1String("g~");
-    if (submode == DownCaseSubMode)
-        return QLatin1String("gu");
-    if (submode == UpCaseSubMode)
-        return QLatin1String("gU");
-    if (submode == IndentSubMode)
-        return QLatin1String("=");
-    if (submode == ShiftRightSubMode)
-        return QLatin1String(">");
-    if (submode == ShiftLeftSubMode)
-        return QLatin1String("<");
-
-    return QString();
-}
-
-class Inputs : public QVector<Input> {
-public:
-    Inputs() = default;
-
-    explicit Inputs(const QString &str, bool noremap = true, bool silent = false)
-        : m_noremap(noremap)
-        , m_silent(silent)
-    {
-        parseFrom(str);
-        squeeze();
-    }
-
-    bool noremap() const { return m_noremap; }
-
-    bool silent() const { return m_silent; }
-
-private:
-    void parseFrom(const QString &str);
-
-    bool m_noremap = true;
-    bool m_silent  = false;
-};
-
-static Input
-parseVimKeyName(const QString &keyName)
-{
-    if (keyName.length() == 1)
-        return Input(keyName.at(0));
-
-    const QStringList keys = keyName.split('-');
-    const qsizetype len    = keys.length();
-
-    if (len == 1 && keys.at(0).toUpper() == "NOP")
-        return Nop;
-
-    Qt::KeyboardModifiers 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 |= 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 qsizetype n = str.size();
-    for (qsizetype i = 0; i < n; ++i) {
-        const QChar c = str.at(i);
-        if (c == '<') {
-            qsizetype j = str.indexOf('>', i);
-            Input input;
-            if (j != -1) {
-                const QString key = str.mid(i + 1, j - i - 1);
-                if (!key.contains('<'))
-                    input = parseVimKeyName(key);
-            }
-            if (input.isValid()) {
-                append(input);
-                i = j;
-            } else {
-                append(Input(c));
-            }
-        } else {
-            append(Input(c));
-        }
-    }
-}
-
-class History {
-public:
-    History()
-        : m_items(QString())
-    {
-    }
-    void append(const QString &item);
-    const QString &move(QStringView prefix, int skip);
-    const QString &current() const { return m_items[m_index]; }
-    const QStringList &items() const { return m_items; }
-    void restart() { m_index = int(m_items.size()) - 1; }
-
-private:
-    // Last item is always empty or current search prefix.
-    QStringList m_items;
-    int m_index = 0;
-};
-
-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(QStringView 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:
-    void setPrompt(const QChar &prompt) { m_prompt = prompt; }
-    void setContents(const QString &s)
-    {
-        m_buffer = s;
-        m_anchor = m_pos = int(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;
-    }
-
-    QStringView userContents() const { return QStringView{ m_buffer }.left(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 + int(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 = int(m_buffer.size()); }
-
-    void setHistoryAutoSave(bool autoSave) { m_historyAutoSave = autoSave; }
-    bool userContentsValid() const { return m_userPos >= 0 && m_userPos <= m_buffer.size(); }
-    void historyDown()
-    {
-        if (userContentsValid())
-            setContents(m_history.move(userContents(), 1));
-    }
-    void historyUp()
-    {
-        if (userContentsValid())
-            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 += '^';
-                msg += QChar(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              = 0;
-    int m_anchor           = 0;
-    int m_userPos          = 0;    // last position of inserted text (for retrieving history items)
-    bool m_historyAutoSave = true; // 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
-using Mappings = QHash<char, ModeMapping>;
-
-// 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_currentInputs.clear();
-        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; }
-
-    bool walk(const Input &input)
-    {
-        m_currentInputs.append(input);
-
-        if (m_modeMapping == m_parent->end())
-            return false;
-
-        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 = int(size());
-        append(it);
-
-        return true;
-    }
-
-    bool walk(const Inputs &inputs)
-    {
-        for (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];
-        for (const Input &input : key)
-            current = &(*current)[input];
-        if (!unique || current->value().isEmpty())
-            current->setValue(inputs);
-    }
-
-    const Inputs &currentInputs() const { return m_currentInputs; }
-
-private:
-    Mappings *m_parent;
-    Mappings::Iterator m_modeMapping;
-    int m_lastValid = -1;
-    char m_mode     = 0;
-    Inputs m_currentInputs;
-};
-
-// state of current mapping
-struct MappingState {
-    MappingState() = default;
-    MappingState(bool noremapArg, bool silentArg, bool editBlockArg)
-        : noremap(noremapArg)
-        , silent(silentArg)
-        , editBlock(editBlockArg)
-    {
-    }
-    bool noremap   = false;
-    bool silent    = false;
-    bool editBlock = false;
-};
-
-class FakeVimHandler::Private : public QObject {
-    VIVY_APP_LOGGABLE_OBJECT(FakeVimHandler::Private, logger)
-
-public:
-    Private(FakeVimHandler *parent, QWidget *widget);
-    ~Private() override;
-
-    EventResult handleEvent(QKeyEvent *ev);
-    bool wantsOverride(QKeyEvent *ev);
-    bool parseExCommand(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 removeEventFilter();
-    void passShortcuts(bool enable);
-    void setupWidget();
-    void restoreWidget(int tabSize);
-
-    friend class FakeVimHandler;
-
-    void init();
-    void focus();
-    void unfocus();
-    void fixExternalCursor(bool focus);
-    void fixExternalCursorPosition(bool focus);
-
-    // Call before any FakeVim processing (import cursor position from editor)
-    void enterFakeVim();
-    // Call after any FakeVim processing
-    // (if needUpdate is true, export cursor position to editor and scroll)
-    void leaveFakeVim(bool needUpdate = true);
-    void leaveFakeVim(EventResult eventResult);
-
-    EventResult handleKey(const Input &input);
-    EventResult handleDefaultKey(const Input &input);
-    bool handleCommandBufferPaste(const Input &input);
-    EventResult handleCurrentMapAsDefault();
-    void prependInputs(const QVector<Input> &inputs); // Handle inputs.
-    void prependMapping(const Inputs &inputs);        // Handle inputs as mapping.
-    bool expandCompleteMapping();           // Return false if current mapping is not complete.
-    bool extendMapping(const Input &input); // Return false if no suitable mappig found.
-    void endMapping();
-    bool canHandleMapping();
-    void clearPendingInput();
-    void waitForMapping();
-    EventResult stopWaitForMapping(bool hasInput);
-    EventResult handleInsertOrReplaceMode(const Input &);
-    void handleInsertMode(const Input &);
-    void handleReplaceMode(const Input &);
-    void finishInsertMode();
-
-    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 handleChangeDeleteYankSubModes(const Input &);
-    void handleChangeDeleteYankSubModes();
-    bool handleReplaceSubMode(const Input &);
-    bool handleCommentSubMode(const Input &);
-    bool handleReplaceWithRegisterSubMode(const Input &);
-    bool handleExchangeSubMode(const Input &);
-    bool handleDeleteChangeSurroundingSubMode(const Input &);
-    bool handleAddSurroundingSubMode(const Input &);
-    bool handleFilterSubMode(const Input &);
-    bool handleRegisterSubMode(const Input &);
-    bool handleShiftSubMode(const Input &);
-    bool handleChangeCaseSubMode(const Input &);
-    bool handleWindowSubMode(const Input &);
-    bool handleZSubMode(const Input &);
-    bool handleCapitalZSubMode(const Input &);
-    bool handleMacroRecordSubMode(const Input &);
-    bool handleMacroExecuteSubMode(const Input &);
-
-    bool
-    handleCount(const Input &); // Handle count for commands (return false if input isn't count).
-    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.
-    bool finishSearch();
-    void finishMovement(const QString &dotCommandMovement = QString());
-
-    // Returns to insert/replace mode after <C-O> command in insert mode,
-    // otherwise returns to command mode.
-    void leaveCurrentMode();
-
-    // Clear data for current (possibly incomplete) command in current mode.
-    // I.e. clears count, register, g flag, sub-modes etc.
-    void clearCurrentMode();
-
-    QTextCursor search(const SearchData &sd, int startPos, int count, bool showMessages);
-    void search(const SearchData &sd, bool showMessages = true);
-    bool searchNext(bool forward = true);
-    void searchBalanced(bool forward, QChar needle, QChar other);
-    void highlightMatches(const QString &needle);
-    void stopIncrementalFind();
-    void updateFind(bool isComplete);
-
-    void resetCount();
-    bool
-    isInputCount(const Input &) const; // Return true if input can be used as count for commands.
-    int mvCount() const { return qMax(1, g.mvcount); }
-    int opCount() const { return qMax(1, g.opcount); }
-    int count() const { return mvCount() * opCount(); }
-    QTextBlock block() const { return m_cursor.block(); }
-    int leftDist() const { return position() - block().position(); }
-    int rightDist() const { return block().length() - leftDist() - (isVisualCharMode() ? 0 : 1); }
-    bool atBlockStart() const { return m_cursor.atBlockStart(); }
-    bool atBlockEnd() const { return m_cursor.atBlockEnd(); }
-    bool atEndOfLine() const { return atBlockEnd() && block().length() > 1; }
-    bool atDocumentEnd() const { return position() >= lastPositionInDocument(true); }
-    bool atDocumentStart() const { return m_cursor.atStart(); }
-
-    bool atEmptyLine(int pos) const;
-    bool atEmptyLine(const QTextCursor &tc) const;
-    bool atEmptyLine() 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
-    QString textAt(int from, int to) const;
-    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 columnAt(int pos) const;
-    int blockNumberAt(int pos) const;
-    QTextBlock blockAt(int pos) const;
-    QTextBlock nextLine(const QTextBlock &block) const; // following line (respects wrapped parts)
-    QTextBlock
-    previousLine(const QTextBlock &block) const; // previous line (respects wrapped parts)
-
-    int linesOnScreen() 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;
-    int windowScrollOffset() const; // return scrolloffset but max half the current window height
-    Column cursorColumn() const;    // as visible on screen
-    void updateFirstVisibleLine();
-    int firstVisibleLine() const;
-    int lastVisibleLine() const;
-    int lineOnTop(int count = 1) const; // [count]-th line from top reachable without scrolling
-    int
-    lineOnBottom(int count = 1) const; // [count]-th line from bottom reachable without scrolling
-    void scrollToLine(int line);
-    void scrollUp(int count);
-    void scrollDown(int count) { scrollUp(-count); }
-    void updateScrollOffset();
-    void alignViewportToCursor(Qt::AlignmentFlag align, int line = -1, bool moveToNonBlank = false);
-
-    int lineToBlockNumber(int line) const;
-
-    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 moveToFirstNonBlankOnLineVisually();
-    void moveToNonBlankOnLine(QTextCursor *tc);
-    void moveToTargetColumn();
-    void setTargetColumn();
-    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 moveToStartOfLineVisually();
-    void moveToEndOfLine();
-    void moveToEndOfLineVisually();
-    void moveToEndOfLineVisually(QTextCursor *tc);
-    void moveBehindEndOfLine();
-    void moveUp(int n = 1) { moveDown(-n); }
-    void moveDown(int n = 1);
-    void moveUpVisually(int n = 1) { moveDownVisually(-n); }
-    void moveDownVisually(int n = 1);
-    void moveVertically(int n = 1)
-    {
-        if (g.gflag) {
-            g.movetype = MoveExclusive;
-            moveDownVisually(n);
-        } else {
-            g.movetype = MoveLineWise;
-            moveDown(n);
-        }
-    }
-    void movePageDown(int count = 1);
-    void movePageUp(int count = 1) { movePageDown(-count); }
-    void dump(const char *msg) const
-    {
-        logDebug() << msg << "POS: " << anchor() << position() << "VISUAL: " << g.visualMode;
-    }
-    void moveRight(int n = 1)
-    {
-        if (isVisualCharMode()) {
-            const QTextBlock currentBlock = block();
-            const int max                 = currentBlock.position() + currentBlock.length() - 1;
-            const int pos                 = position() + n;
-            setPosition(qMin(pos, max));
-        } else {
-            m_cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, n);
-        }
-        if (atEndOfLine())
-            q->fold(1, false);
-        setTargetColumn();
-    }
-    void moveLeft(int n = 1)
-    {
-        m_cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, n);
-        setTargetColumn();
-    }
-    void moveToNextCharacter()
-    {
-        moveRight();
-        if (atEndOfLine())
-            moveRight();
-    }
-    void moveToPreviousCharacter()
-    {
-        moveLeft();
-        if (atBlockStart())
-            moveLeft();
-    }
-    void setAnchor() { m_cursor.setPosition(position(), QTextCursor::MoveAnchor); }
-    void setAnchor(int position) { m_cursor.setPosition(position, QTextCursor::KeepAnchor); }
-    void setPosition(int position) { m_cursor.setPosition(position, QTextCursor::KeepAnchor); }
-    void setAnchorAndPosition(int anchor, int position)
-    {
-        m_cursor.setPosition(anchor, QTextCursor::MoveAnchor);
-        m_cursor.setPosition(position, QTextCursor::KeepAnchor);
-    }
-
-    // Set cursor in text editor widget.
-    void commitCursor();
-
-    // Restore cursor from editor widget.
-    // Update selection, record jump and target column if cursor position
-    // changes externally (e.g. by code completion).
-    void pullCursor();
-
-    QTextCursor editorCursor() const;
-
-    // Values to save when starting FakeVim processing.
-    int m_firstVisibleLine;
-    QTextCursor m_cursor;
-    bool m_cursorNeedsUpdate;
-
-    bool moveToPreviousParagraph(int count = 1) { return moveToNextParagraph(-count); }
-    bool moveToNextParagraph(int count = 1);
-    void moveToParagraphStartOrEnd(int direction = 1);
-
-    bool handleFfTt(const QString &key, bool repeats = false);
-
-    void enterVisualInsertMode(QChar command);
-    void enterReplaceMode();
-    void enterInsertMode();
-    void enterInsertOrReplaceMode(Mode mode);
-    void enterCommandMode(Mode returnToMode = CommandMode);
-    void enterExMode(const QString &contents = QString());
-    void showMessage(MessageLevel level, const QString &msg);
-    void clearMessage() { showMessage(MessageInfo, QString()); }
-    void notImplementedYet();
-    void updateMiniBuffer();
-    void updateSelection();
-    void updateHighlights();
-    void updateCursorShape();
-    void setThinCursor(bool enable = true);
-    bool hasThinCursor() const;
-    QWidget *editor() const;
-    QTextDocument *document() const { return EDITOR(document()); }
-    QChar characterAt(int pos) const { return document()->characterAt(pos); }
-    QChar characterAtCursor() const { return characterAt(position()); }
-
-    void joinPreviousEditBlock();
-    void beginEditBlock(bool largeEditBlock = false);
-    void beginLargeEditBlock() { beginEditBlock(true); }
-    void endEditBlock();
-    void breakEditBlock() { m_buffer->breakEditBlock = true; }
-
-    bool canModifyBufferData() const { return m_buffer->currentHandler.data() == this; }
-
-    void onContentsChanged(int position, int charsRemoved, int charsAdded);
-    void onCursorPositionChanged();
-    void onUndoCommandAdded();
-
-    void onInputTimeout();
-    void onFixCursorTimeout();
-
-    bool isCommandLineMode() const { return g.mode == ExMode || g.subsubmode == SearchSubSubMode; }
-    bool isInsertMode() const { return g.mode == InsertMode || g.mode == ReplaceMode; }
-    // Waiting for movement operator.
-    bool isOperatorPending() const
-    {
-        return g.submode == ChangeSubMode || g.submode == DeleteSubMode ||
-               g.submode == ExchangeSubMode || g.submode == CommentSubMode ||
-               g.submode == ReplaceWithRegisterSubMode || g.submode == AddSurroundingSubMode ||
-               g.submode == FilterSubMode || g.submode == IndentSubMode ||
-               g.submode == ShiftLeftSubMode || g.submode == ShiftRightSubMode ||
-               g.submode == InvertCaseSubMode || g.submode == DownCaseSubMode ||
-               g.submode == UpCaseSubMode || g.submode == YankSubMode;
-    }
-
-    bool isVisualMode() const { return g.visualMode != NoVisualMode; }
-    bool isNoVisualMode() const { return g.visualMode == NoVisualMode; }
-    bool isVisualCharMode() const { return g.visualMode == VisualCharMode; }
-    bool isVisualLineMode() const { return g.visualMode == VisualLineMode; }
-    bool isVisualBlockMode() const { return g.visualMode == VisualBlockMode; }
-    char currentModeCode() const;
-    void updateEditor();
-
-    void selectTextObject(bool simple, bool inner);
-    void selectWordTextObject(bool inner);
-    void selectWORDTextObject(bool inner);
-    void selectSentenceTextObject(bool inner);
-    void selectParagraphTextObject(bool inner);
-    bool changeNumberTextObject(int count);
-    // return true only if cursor is in a block delimited with correct characters
-    bool selectBlockTextObject(bool inner, QChar left, QChar right);
-    bool selectQuotedStringTextObject(bool inner, const QString &quote);
-    bool selectArgumentTextObject(bool inner);
-
-    void commitInsertState();
-    void invalidateInsertState();
-    bool isInsertStateValid() const;
-    void clearLastInsertion();
-    void ensureCursorVisible();
-    void insertInInsertMode(const QString &text);
-
-    // Macro recording
-    bool startRecording(const Input &input);
-    void record(const Input &input);
-    void stopRecording();
-    bool executeRegister(int reg);
-
-    // Handle current command as synonym
-    void handleAs(const QString &command);
-
-public:
-    QTextEdit *m_textedit;
-    QPlainTextEdit *m_plaintextedit;
-    bool m_wasReadOnly; // saves read-only state of document
-
-    bool m_inFakeVim; // true if currently processing a key press or a command
-
-    FakeVimHandler *q;
-    int m_register;
-    BlockInsertMode m_visualBlockInsert;
-
-    bool m_anchorPastEnd;
-    bool m_positionPastEnd; // '$' & 'l' in visual mode can move past eol
-
-    QString m_currentFileName;
-
-    int m_findStartPosition;
-
-    int anchor() const { return m_cursor.anchor(); }
-    int position() const { return m_cursor.position(); }
-
-    // Transform text selected by cursor in current visual mode.
-    using Transformation = std::function<QString(const QString &)>;
-    void transformText(const Range &range, QTextCursor &tc,
-                       const std::function<void()> &transform) const;
-    void transformText(const Range &range, const Transformation &transform);
-
-    void insertText(QTextCursor &tc, const QString &text);
-    void insertText(const Register &reg);
-    void removeText(const Range &range);
-
-    void invertCase(const Range &range);
-
-    void toggleComment(const Range &range);
-
-    void exchangeRange(const Range &range);
-
-    void replaceWithRegister(const Range &range);
-
-    void surroundCurrentRange(const Input &input, const QString &prefix = {});
-
-    void upCase(const Range &range);
-
-    void downCase(const Range &range);
-
-    void replaceText(const Range &range, const QString &str);
-
-    QString selectText(const Range &range) const;
-    void setCurrentRange(const Range &range);
-    Range currentRange() const { return Range(position(), anchor(), g.rangemode); }
-
-    void yankText(const Range &range, int toregister);
-
-    void pasteText(bool afterCursor);
-
-    void cutSelectedText(int reg = 0);
-
-    void joinLines(int count, bool preserveSpace = false);
-
-    void insertNewLine();
-
-    bool handleInsertInEditor(const Input &input);
-    bool passEventToEditor(
-        QEvent &event,
-        QTextCursor &
-            tc); // Pass event to editor widget without filtering. Returns true if event was processed.
-
-    // undo handling
-    int revision() const { return document()->availableUndoSteps(); }
-    void undoRedo(bool undo);
-    void undo();
-    void redo();
-    void pushUndoState(bool overwrite = true);
-
-    // extra data for '.'
-    void replay(const QString &text, int repeat = 1);
-    void setDotCommand(const QString &cmd) { g.dotCommand = cmd; }
-    void setDotCommand(const QString &cmd, int n) { g.dotCommand = cmd.arg(n); }
-    QString visualDotCommand() const;
-
-    // visual modes
-    void toggleVisualMode(VisualMode visualMode);
-    void leaveVisualMode();
-    void saveLastVisualMode();
-
-    // marks
-    Mark mark(QChar code) const;
-    void setMark(QChar code, CursorPosition position);
-    // jump to valid mark return true if mark is valid and local
-    bool jumpToMark(QChar mark, bool backTickMode);
-    // update marks on undo/redo
-    void updateMarks(const Marks &newMarks);
-    CursorPosition markLessPosition() const { return mark('<').position(document()); }
-    CursorPosition markGreaterPosition() const { return mark('>').position(document()); }
-
-    int m_targetColumn;        // -1 if past end of line
-    int m_visualTargetColumn;  // 'l' can move past eol in visual mode only
-    int m_targetColumnWrapped; // column in current part of wrapped line
-
-    // auto-indent
-    QString tabExpand(int len) const;
-    Column indentation(const QString &line) const;
-    void insertAutomaticIndentation(bool goingDown, bool forceAutoIndent = false);
-    // number of autoindented characters
-    void handleStartOfLine();
-
-    // register handling
-    QString registerContents(int reg) const;
-    void setRegister(int reg, const QString &contents, RangeMode mode);
-    RangeMode registerRangeMode(int reg) const;
-    void getRegisterType(int *reg, bool *isClipboard, bool *isSelection,
-                         bool *append = nullptr) const;
-
-    void recordJump(int position = -1);
-    void jump(int distance);
-
-    QList<QTextEdit::ExtraSelection> m_extraSelections;
-    QTextCursor m_searchCursor;
-    int m_searchStartPosition;
-    int m_searchFromScreenLine;
-    QString m_highlighted; // currently highlighted text
-
-    bool handleExCommandHelper(ExCommand &cmd);       // Returns success.
-    bool handleExPluginCommand(const ExCommand &cmd); // Handled by plugin?
-    bool handleExBangCommand(const ExCommand &cmd);
-    bool handleExYankDeleteCommand(const ExCommand &cmd);
-    bool handleExChangeCommand(const ExCommand &cmd);
-    bool handleExMoveCommand(const ExCommand &cmd);
-    bool handleExJoinCommand(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 handleExUndoRedoCommand(const ExCommand &cmd);
-    bool handleExSetCommand(const ExCommand &cmd);
-    bool handleExSortCommand(const ExCommand &cmd);
-    bool handleExShiftCommand(const ExCommand &cmd);
-    bool handleExSourceCommand(const ExCommand &cmd);
-    bool handleExSubstituteCommand(const ExCommand &cmd);
-    bool handleExTabNextCommand(const ExCommand &cmd);
-    bool handleExTabPreviousCommand(const ExCommand &cmd);
-    bool handleExWriteCommand(const ExCommand &cmd);
-    bool handleExEchoCommand(const ExCommand &cmd);
-
-    void setTabSize(int tabSize);
-    void setupCharClass();
-    int charClass(QChar c, bool simple) const;
-    signed char m_charClass[256];
-
-    int m_ctrlVAccumulator;
-    int m_ctrlVLength;
-    int m_ctrlVBase;
-
-    QTimer m_fixCursorTimer;
-    QTimer m_inputTimer;
-
-    void miniBufferTextEdited(const QString &text, int cursorPos, int anchorPos);
-
-    // Data shared among editors with same document.
-    struct BufferData {
-        QStack<State> undo;
-        QStack<State> redo;
-        State undoState;
-        int lastRevision = 0;
-
-        int editBlockLevel  = 0;     // current level of edit blocks
-        bool breakEditBlock = false; // if true, joinPreviousEditBlock() starts new edit block
-
-        QStack<CursorPosition> jumpListUndo;
-        QStack<CursorPosition> jumpListRedo;
-
-        VisualMode lastVisualMode   = NoVisualMode;
-        bool lastVisualModeInverted = false;
-
-        Marks marks;
-
-        // Insert state to get last inserted text.
-        struct InsertState {
-            int pos1;
-            int pos2;
-            int backspaces;
-            int deletes;
-            QSet<int> spaces;
-            bool insertingSpaces;
-            QString textBeforeCursor;
-            bool newLineBefore;
-            bool newLineAfter;
-        } insertState;
-
-        QString lastInsertion;
-
-        // If there are multiple editors with same document,
-        // only the handler with last focused editor can change buffer data.
-        QPointer<FakeVimHandler::Private> currentHandler;
-    };
-
-    using BufferDataPtr = QSharedPointer<BufferData>;
-    void pullOrCreateBufferData();
-    BufferDataPtr m_buffer;
-
-    // Data shared among all editors.
-    static struct GlobalData {
-        GlobalData()
-            : mappings()
-            , currentMap(&mappings)
-        {
-            commandBuffer.setPrompt(':');
-        }
-
-        // Current state.
-        bool passing          = false; // let the core see the next event
-        Mode mode             = CommandMode;
-        SubMode submode       = NoSubMode;
-        SubSubMode subsubmode = NoSubSubMode;
-        Input subsubdata;
-        VisualMode visualMode = NoVisualMode;
-        Input minibufferData;
-
-        // [count] for current command, 0 if no [count] available
-        int mvcount = 0;
-        int opcount = 0;
-
-        MoveType movetype   = MoveInclusive;
-        RangeMode rangemode = RangeCharMode;
-        bool gflag          = false; // whether current command started with 'g'
-
-        // Extra data for ';'.
-        Input semicolonType; // 'f', 'F', 't', 'T'
-        QString semicolonKey;
-
-        // Repetition.
-        QString dotCommand;
-
-        QHash<int, Register> registers;
-
-        // All mappings.
-        Mappings mappings;
-
-        // Input.
-        QList<Input> pendingInput;
-        MappingsIterator currentMap;
-        QStack<MappingState> mapStates;
-        int mapDepth = 0;
-
-        // Command line buffers.
-        CommandBuffer commandBuffer;
-        CommandBuffer searchBuffer;
-
-        // Current mini buffer message.
-        QString currentMessage;
-        MessageLevel currentMessageLevel = MessageInfo;
-        QString currentCommand;
-
-        // Search state.
-        QString lastSearch; // last search expression as entered by user
-        QString lastNeedle; // last search expression translated with vimPatternToQtPattern()
-        bool lastSearchForward = false; // last search command was '/' or '*'
-        bool highlightsCleared = false; // ':nohlsearch' command is active until next search
-        bool findPending =
-            false; // currently searching using external tool (until editor is focused again)
-
-        // Last substitution command.
-        QString lastSubstituteFlags;
-        QString lastSubstitutePattern;
-        QString lastSubstituteReplacement;
-
-        // Global marks.
-        Marks marks;
-
-        // Return to insert/replace mode after single command (<C-O>).
-        Mode returnToMode = CommandMode;
-
-        // Currently recorded macro
-        bool isRecording = false;
-        QString recorded;
-        int currentRegister      = 0;
-        int lastExecutedRegister = 0;
-
-        // If empty, cx{motion} will store the range defined by {motion} here.
-        // If non-empty, cx{motion} replaces the {motion} with selectText(*exchangeData)
-        Range exchangeRange;
-
-        bool surroundUpperCaseS;  // True for yS and cS, false otherwise
-        QString surroundFunction; // Used for storing the function name provided to ys{motion}f
-    } g;
-
-    FakeVimSettings &s = *fakeVimSettings();
-};
-
-FakeVimHandler::Private::~Private() {} // Avoit the weak table warning in clang
-
-FakeVimHandler::Private::GlobalData FakeVimHandler::Private::g;
-
-FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget)
-{
-    q               = parent;
-    m_textedit      = qobject_cast<QTextEdit *>(widget);
-    m_plaintextedit = qobject_cast<QPlainTextEdit *>(widget);
-
-    init();
-
-    if (editor()) {
-        connect(EDITOR(document()), &QTextDocument::contentsChange, this,
-                &Private::onContentsChanged);
-        connect(EDITOR(document()), &QTextDocument::undoCommandAdded, this,
-                &Private::onUndoCommandAdded);
-        m_buffer->lastRevision = revision();
-    }
-}
-
-void
-FakeVimHandler::Private::init()
-{
-    m_cursor               = QTextCursor(document());
-    m_cursorNeedsUpdate    = true;
-    m_inFakeVim            = false;
-    m_findStartPosition    = -1;
-    m_visualBlockInsert    = NoneBlockInsertMode;
-    m_positionPastEnd      = false;
-    m_anchorPastEnd        = false;
-    m_register             = '"';
-    m_targetColumn         = 0;
-    m_visualTargetColumn   = 0;
-    m_targetColumnWrapped  = 0;
-    m_searchStartPosition  = 0;
-    m_searchFromScreenLine = 0;
-    m_firstVisibleLine     = 0;
-    m_ctrlVAccumulator     = 0;
-    m_ctrlVLength          = 0;
-    m_ctrlVBase            = 0;
-
-    initSingleShotTimer(&m_fixCursorTimer, 0, this, &FakeVimHandler::Private::onFixCursorTimeout);
-    initSingleShotTimer(&m_inputTimer, 1000, this, &FakeVimHandler::Private::onInputTimeout);
-
-    pullOrCreateBufferData();
-    setupCharClass();
-}
-
-void
-FakeVimHandler::Private::focus()
-{
-    m_buffer->currentHandler = this;
-
-    enterFakeVim();
-
-    stopIncrementalFind();
-    if (isCommandLineMode()) {
-        if (g.subsubmode == SearchSubSubMode) {
-            setPosition(m_searchStartPosition);
-            scrollToLine(m_searchFromScreenLine);
-        } else {
-            leaveVisualMode();
-            setPosition(qMin(position(), anchor()));
-        }
-        leaveCurrentMode();
-        setTargetColumn();
-        setAnchor();
-        commitCursor();
-    } else {
-        clearCurrentMode();
-    }
-    fixExternalCursor(true);
-    updateHighlights();
-
-    leaveFakeVim(false);
-}
-
-void
-FakeVimHandler::Private::unfocus()
-{
-    fixExternalCursor(false);
-}
-
-void
-FakeVimHandler::Private::fixExternalCursor(bool focus)
-{
-    m_fixCursorTimer.stop();
-
-    if (isVisualCharMode() && !focus && !hasThinCursor()) {
-        // Select the character under thick cursor for external operations with text selection.
-        fixExternalCursorPosition(false);
-    } else if (isVisualCharMode() && focus && hasThinCursor()) {
-        // Fix cursor position if changing its shape.
-        // The fix is postponed so context menu action can be finished.
-        m_fixCursorTimer.start();
-    } else {
-        updateCursorShape();
-    }
-}
-
-void
-FakeVimHandler::Private::fixExternalCursorPosition(bool focus)
-{
-    QTextCursor tc = editorCursor();
-    if (tc.anchor() < tc.position()) {
-        tc.movePosition(focus ? QTextCursor::Left : QTextCursor::Right, QTextCursor::KeepAnchor);
-        EDITOR(setTextCursor(tc));
-    }
-
-    setThinCursor(!focus);
-}
-
-void
-FakeVimHandler::Private::enterFakeVim()
-{
-    if (m_inFakeVim) {
-        qFatal("enterFakeVim() shouldn't be called recursively!");
-    }
-
-    if (!m_buffer->currentHandler)
-        m_buffer->currentHandler = this;
-
-    pullOrCreateBufferData();
-
-    m_inFakeVim = true;
-
-    removeEventFilter();
-
-    pullCursor();
-
-    updateFirstVisibleLine();
-}
-
-void
-FakeVimHandler::Private::leaveFakeVim(bool needUpdate)
-{
-    if (!m_inFakeVim) {
-        qFatal("enterFakeVim() not called before leaveFakeVim()!");
-    }
-
-    // The command might have destroyed the editor.
-    if (m_textedit || m_plaintextedit) {
-        if (s.showMarks.value())
-            updateSelection();
-
-        updateMiniBuffer();
-
-        if (needUpdate) {
-            // Move cursor line to middle of screen if it's not visible.
-            const int line = cursorLine();
-            if (line < firstVisibleLine() || line > firstVisibleLine() + linesOnScreen())
-                scrollToLine(qMax(0, line - linesOnScreen() / 2));
-            else
-                scrollToLine(firstVisibleLine());
-            updateScrollOffset();
-
-            commitCursor();
-        }
-
-        installEventFilter();
-    }
-
-    m_inFakeVim = false;
-}
-
-void
-FakeVimHandler::Private::leaveFakeVim(EventResult eventResult)
-{
-    leaveFakeVim(eventResult == EventHandled || eventResult == EventCancelled);
-}
-
-bool
-FakeVimHandler::Private::wantsOverride(QKeyEvent *ev)
-{
-    const int key                    = ev->key();
-    const Qt::KeyboardModifiers mods = ev->modifiers();
-    KEY_DEBUG("SHORTCUT OVERRIDE" << key << "  PASSING: " << g.passing);
-
-    if (key == Key_Escape) {
-        if (g.subsubmode == SearchSubSubMode)
-            return true;
-        // Not sure this feels good. People often hit Esc several times.
-        if (isNoVisualMode() && g.mode == CommandMode && g.submode == NoSubMode &&
-            g.currentCommand.isEmpty() && g.returnToMode == CommandMode)
-            return false;
-        return true;
-    }
-
-    // We are interested in overriding most Ctrl key combinations.
-    if (isOnlyControlModifier(mods) && !s.passControlKey.value() &&
-        ((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 (g.passing) {
-            KEY_DEBUG(" PASSING CTRL KEY");
-            // We get called twice on the same key
-            //g.passing = false;
-            return false;
-        }
-        KEY_DEBUG(" NOT PASSING CTRL KEY");
-        return true;
-    }
-
-    // Let other shortcuts trigger.
-    return false;
-}
-
-EventResult
-FakeVimHandler::Private::handleEvent(QKeyEvent *ev)
-{
-    const int key                    = ev->key();
-    const Qt::KeyboardModifiers mods = ev->modifiers();
-
-    if (key == Key_Shift || key == Key_Alt || key == Key_Control || key == Key_AltGr ||
-        key == Key_Meta) {
-        KEY_DEBUG("PLAIN MODIFIER");
-        return EventUnhandled;
-    }
-
-    if (g.passing) {
-        passShortcuts(false);
-        KEY_DEBUG("PASSING PLAIN KEY..." << ev->key() << ev->text());
-        KEY_DEBUG("   PASS TO CORE");
-        return EventPassedToCore;
-    }
-
-#ifndef FAKEVIM_STANDALONE
-    bool inSnippetMode = false;
-    QMetaObject::invokeMethod(editor(), "inSnippetMode", Q_ARG(bool *, &inSnippetMode));
-
-    if (inSnippetMode)
-        return EventPassedToCore;
-#endif
-
-    // Fake "End of line"
-    //m_tc = m_cursor;
-
-    //bool hasBlock = false;
-    //q->requestHasBlockSelection(&hasBlock);
-    //qDebug() << "IMPORT BLOCK 2:" << hasBlock;
-
-    //if (0 && hasBlock) {
-    //    (pos > anc) ? --pos : --anc;
-
-    //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(g.mode == InsertMode || g.mode == ReplaceMode
-    //        || !atBlockEnd() || block().length() <= 1,
-    //    qDebug() << "Cursor at EOL before key handler");
-
-    const Input input(key, mods, ev->text());
-    if (!input.isValid())
-        return EventUnhandled;
-
-    enterFakeVim();
-    EventResult result = handleKey(input);
-    leaveFakeVim(result);
-    return result;
-}
-
-void
-FakeVimHandler::Private::installEventFilter()
-{
-    EDITOR(installEventFilter(q));
-}
-
-void
-FakeVimHandler::Private::removeEventFilter()
-{
-    EDITOR(removeEventFilter(q));
-}
-
-void
-FakeVimHandler::Private::setupWidget()
-{
-    m_cursorNeedsUpdate = true;
-    if (m_textedit) {
-        connect(m_textedit, &QTextEdit::cursorPositionChanged, this,
-                &FakeVimHandler::Private::onCursorPositionChanged, Qt::UniqueConnection);
-    } else {
-        connect(m_plaintextedit, &QPlainTextEdit::cursorPositionChanged, this,
-                &FakeVimHandler::Private::onCursorPositionChanged, Qt::UniqueConnection);
-    }
-
-    enterFakeVim();
-
-    leaveCurrentMode();
-    m_wasReadOnly = EDITOR(isReadOnly());
-
-    updateEditor();
-
-    leaveFakeVim();
-}
-
-void
-FakeVimHandler::Private::commitInsertState()
-{
-    if (!isInsertStateValid())
-        return;
-
-    QString &lastInsertion               = m_buffer->lastInsertion;
-    BufferData::InsertState &insertState = m_buffer->insertState;
-
-    // Get raw inserted text.
-    lastInsertion = textAt(insertState.pos1, insertState.pos2);
-
-    // Escape special characters and spaces inserted by user (not by auto-indentation).
-    for (int i = int(lastInsertion.size()) - 1; i >= 0; --i) {
-        const int pos = insertState.pos1 + i;
-        const QChar c = characterAt(pos);
-        if (c == '<')
-            lastInsertion.replace(i, 1, "<LT>");
-        else if ((c == ' ' || c == '\t') && insertState.spaces.contains(pos))
-            lastInsertion.replace(i, 1, QLatin1String(c == ' ' ? "<SPACE>" : "<TAB>"));
-    }
-
-    // Remove unnecessary backspaces.
-    while (insertState.backspaces > 0 && !lastInsertion.isEmpty() && lastInsertion[0].isSpace())
-        --insertState.backspaces;
-
-    // backspaces in front of inserted text
-    lastInsertion.prepend(QString("<BS>").repeated(insertState.backspaces));
-    // deletes after inserted text
-    lastInsertion.prepend(QString("<DELETE>").repeated(insertState.deletes));
-
-    // Remove indentation.
-    lastInsertion.replace(QRegularExpression("(^|\n)[\\t ]+"), "\\1");
-}
-
-void
-FakeVimHandler::Private::invalidateInsertState()
-{
-    BufferData::InsertState &insertState = m_buffer->insertState;
-    insertState.pos1                     = -1;
-    insertState.pos2                     = position();
-    insertState.backspaces               = 0;
-    insertState.deletes                  = 0;
-    insertState.spaces.clear();
-    insertState.insertingSpaces  = false;
-    insertState.textBeforeCursor = textAt(block().position(), position());
-    insertState.newLineBefore    = false;
-    insertState.newLineAfter     = false;
-}
-
-bool
-FakeVimHandler::Private::isInsertStateValid() const
-{
-    return m_buffer->insertState.pos1 != -1;
-}
-
-void
-FakeVimHandler::Private::clearLastInsertion()
-{
-    invalidateInsertState();
-    m_buffer->lastInsertion.clear();
-    m_buffer->insertState.pos1 = m_buffer->insertState.pos2;
-}
-
-void
-FakeVimHandler::Private::ensureCursorVisible()
-{
-    int pos = position();
-    int anc = isVisualMode() ? anchor() : position();
-
-    // fix selection so it is outside folded block
-    int start         = qMin(pos, anc);
-    int end           = qMax(pos, anc) + 1;
-    QTextBlock block  = blockAt(start);
-    QTextBlock block2 = blockAt(end);
-    if (!block.isVisible() || !block2.isVisible()) {
-        // FIXME: Moving cursor left/right or unfolding block immediately after block is folded
-        //        should restore cursor position inside block.
-        // Changing cursor position after folding is not Vim behavior so at least record the jump.
-        if (block.isValid() && !block.isVisible())
-            recordJump();
-
-        pos = start;
-        while (block.isValid() && !block.isVisible())
-            block = block.previous();
-        if (block.isValid())
-            pos = block.position() + qMin(m_targetColumn, block.length() - 2);
-
-        if (isVisualMode()) {
-            anc = end;
-            while (block2.isValid() && !block2.isVisible()) {
-                anc    = block2.position() + block2.length() - 2;
-                block2 = block2.next();
-            }
-        }
-
-        setAnchorAndPosition(anc, pos);
-    }
-}
-
-void
-FakeVimHandler::Private::updateEditor()
-{
-    const int tabSize =
-        std::clamp(static_cast<int>(s.tabStop.value()), 1, std::numeric_limits<int>::max());
-    setTabSize(tabSize);
-    setupCharClass();
-}
-
-void
-FakeVimHandler::Private::setTabSize(int tabSize)
-{
-    const int charWidth = QFontMetrics(EDITOR(font())).horizontalAdvance(' ');
-    const int width     = charWidth * tabSize;
-    EDITOR(setTabStopDistance(width));
-}
-
-void
-FakeVimHandler::Private::restoreWidget(int tabSize)
-{
-    //EDITOR(removeEventFilter(q));
-    //EDITOR(setReadOnly(m_wasReadOnly));
-    setTabSize(tabSize);
-    g.visualMode = NoVisualMode;
-    // Force "ordinary" cursor.
-    setThinCursor();
-    updateSelection();
-    updateHighlights();
-    if (m_textedit) {
-        disconnect(m_textedit, &QTextEdit::cursorPositionChanged, this,
-                   &FakeVimHandler::Private::onCursorPositionChanged);
-    } else {
-        disconnect(m_plaintextedit, &QPlainTextEdit::cursorPositionChanged, this,
-                   &FakeVimHandler::Private::onCursorPositionChanged);
-    }
-}
-
-EventResult
-FakeVimHandler::Private::handleKey(const Input &input)
-{
-    KEY_DEBUG("HANDLE INPUT: " << input);
-
-    bool hasInput = input.isValid();
-
-    // Waiting on input to complete mapping?
-    EventResult r = stopWaitForMapping(hasInput);
-
-    if (hasInput) {
-        record(input);
-        g.pendingInput.append(input);
-    }
-
-    // Process pending input.
-    // Note: Pending input is global state and can be extended by:
-    //         1. handling a user input (though handleKey() is not called recursively),
-    //         2. expanding a user mapping or
-    //         3. executing a register.
-    while (!g.pendingInput.isEmpty() && r == EventHandled) {
-        const Input in = g.pendingInput.takeFirst();
-
-        // invalid input is used to pop mapping state
-        if (!in.isValid()) {
-            endMapping();
-        } else {
-            // Handle user mapping.
-            if (canHandleMapping()) {
-                if (extendMapping(in)) {
-                    if (!hasInput || !g.currentMap.canExtend())
-                        expandCompleteMapping();
-                } else if (!expandCompleteMapping()) {
-                    r = handleCurrentMapAsDefault();
-                }
-            } else {
-                r = handleDefaultKey(in);
-            }
-        }
-    }
-
-    if (g.currentMap.canExtend()) {
-        waitForMapping();
-        return EventHandled;
-    }
-
-    if (r != EventHandled)
-        clearPendingInput();
-
-    return r;
-}
-
-bool
-FakeVimHandler::Private::handleCommandBufferPaste(const Input &input)
-{
-    if (input.isControl('r') && (g.subsubmode == SearchSubSubMode || g.mode == ExMode)) {
-        g.minibufferData = input;
-        return true;
-    }
-    if (g.minibufferData.isControl('r')) {
-        g.minibufferData = Input();
-        if (input.isEscape())
-            return true;
-        CommandBuffer &buffer =
-            (g.subsubmode == SearchSubSubMode) ? g.searchBuffer : g.commandBuffer;
-        if (input.isControl('w')) {
-            QTextCursor tc = m_cursor;
-            tc.select(QTextCursor::WordUnderCursor);
-            QString word = tc.selectedText();
-            buffer.insertText(word);
-        } else {
-            QString r = registerContents(input.asChar().unicode());
-            buffer.insertText(r);
-        }
-        updateMiniBuffer();
-        return true;
-    }
-    return false;
-}
-
-EventResult
-FakeVimHandler::Private::handleDefaultKey(const Input &input)
-{
-    if (g.passing) {
-        passShortcuts(false);
-        QKeyEvent event(QEvent::KeyPress, input.key(), input.modifiers(), input.text());
-        bool accepted = QApplication::sendEvent(editor()->window(), &event);
-        if (accepted || (!m_textedit && !m_plaintextedit))
-            return EventHandled;
-    }
-
-    if (input == Nop)
-        return EventHandled;
-    else if (g.subsubmode == SearchSubSubMode)
-        return handleSearchSubSubMode(input);
-    else if (g.mode == CommandMode)
-        return handleCommandMode(input);
-    else if (g.mode == InsertMode || g.mode == ReplaceMode)
-        return handleInsertOrReplaceMode(input);
-    else if (g.mode == ExMode)
-        return handleExMode(input);
-    return EventUnhandled;
-}
-
-EventResult
-FakeVimHandler::Private::handleCurrentMapAsDefault()
-{
-    // If mapping has failed take the first input from it and try default command.
-    const Inputs &inputs = g.currentMap.currentInputs();
-    if (inputs.isEmpty())
-        return EventHandled;
-
-    Input in = inputs.front();
-    if (inputs.size() > 1)
-        prependInputs(inputs.mid(1));
-    g.currentMap.reset();
-
-    return handleDefaultKey(in);
-}
-
-void
-FakeVimHandler::Private::prependInputs(const QVector<Input> &inputs)
-{
-    for (qsizetype i = inputs.size() - 1; i >= 0; --i)
-        g.pendingInput.prepend(inputs[i]);
-}
-
-void
-FakeVimHandler::Private::prependMapping(const Inputs &inputs)
-{
-    // FIXME: Implement Vim option maxmapdepth (default value is 1000).
-    if (g.mapDepth >= 1000) {
-        const int i                    = qMax(0, int(g.pendingInput.lastIndexOf(Input())));
-        const QList<Input> inputsLocal = g.pendingInput.mid(i);
-        clearPendingInput();
-        g.pendingInput.append(inputsLocal);
-        showMessage(MessageError, Tr::tr("Recursive mapping"));
-        return;
-    }
-
-    ++g.mapDepth;
-    g.pendingInput.prepend(Input());
-    prependInputs(inputs);
-    g.commandBuffer.setHistoryAutoSave(false);
-
-    // start new edit block (undo/redo) only if necessary
-    bool editBlock = m_buffer->editBlockLevel == 0 && !(isInsertMode() && isInsertStateValid());
-    if (editBlock)
-        beginLargeEditBlock();
-    g.mapStates << MappingState(inputs.noremap(), inputs.silent(), editBlock);
-}
-
-bool
-FakeVimHandler::Private::expandCompleteMapping()
-{
-    if (!g.currentMap.isComplete())
-        return false;
-
-    const Inputs &inputs = g.currentMap.inputs();
-    int usedInputs       = g.currentMap.mapLength();
-    prependInputs(g.currentMap.currentInputs().mid(usedInputs));
-    prependMapping(inputs);
-    g.currentMap.reset();
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::extendMapping(const Input &input)
-{
-    if (!g.currentMap.isValid())
-        g.currentMap.reset(currentModeCode());
-    return g.currentMap.walk(input);
-}
-
-void
-FakeVimHandler::Private::endMapping()
-{
-    if (!g.currentMap.canExtend())
-        --g.mapDepth;
-    if (g.mapStates.isEmpty())
-        return;
-    if (g.mapStates.last().editBlock)
-        endEditBlock();
-    g.mapStates.pop_back();
-    if (g.mapStates.isEmpty())
-        g.commandBuffer.setHistoryAutoSave(true);
-}
-
-bool
-FakeVimHandler::Private::canHandleMapping()
-{
-    // Don't handle user mapping in sub-modes that cannot be followed by movement and in "noremap".
-    return g.subsubmode == NoSubSubMode && g.submode != RegisterSubMode &&
-           g.submode != WindowSubMode && g.submode != ZSubMode && g.submode != CapitalZSubMode &&
-           g.submode != ReplaceSubMode && g.submode != MacroRecordSubMode &&
-           g.submode != MacroExecuteSubMode &&
-           (g.mapStates.isEmpty() || !g.mapStates.last().noremap);
-}
-
-void
-FakeVimHandler::Private::clearPendingInput()
-{
-    // Clear pending input on interrupt or bad mapping.
-    g.pendingInput.clear();
-    g.mapStates.clear();
-    g.mapDepth = 0;
-
-    // Clear all started edit blocks.
-    while (m_buffer->editBlockLevel > 0)
-        endEditBlock();
-}
-
-void
-FakeVimHandler::Private::waitForMapping()
-{
-    g.currentCommand.clear();
-    foreach(const Input &input, g.currentMap.currentInputs())
-        g.currentCommand.append(input.toString());
-
-    // wait for user to press any key or trigger complete mapping after interval
-    m_inputTimer.start();
-}
-
-EventResult
-FakeVimHandler::Private::stopWaitForMapping(bool hasInput)
-{
-    if (!hasInput || m_inputTimer.isActive()) {
-        m_inputTimer.stop();
-        g.currentCommand.clear();
-        if (!hasInput && !expandCompleteMapping()) {
-            // Cannot complete mapping so handle the first input from it as default command.
-            return handleCurrentMapAsDefault();
-        }
-    }
-
-    return EventHandled;
-}
-
-void
-FakeVimHandler::Private::stopIncrementalFind()
-{
-    if (g.findPending) {
-        g.findPending = false;
-        setAnchorAndPosition(m_findStartPosition, m_cursor.selectionStart());
-        finishMovement();
-        setAnchor();
-    }
-}
-
-void
-FakeVimHandler::Private::updateFind(bool isComplete)
-{
-    if (!isComplete && !s.incSearch.value())
-        return;
-
-    g.currentMessage.clear();
-
-    const QString &needle = g.searchBuffer.contents();
-    if (isComplete) {
-        setPosition(m_searchStartPosition);
-        if (!needle.isEmpty())
-            recordJump();
-    }
-
-    SearchData sd;
-    sd.needle           = needle;
-    sd.forward          = g.lastSearchForward;
-    sd.highlightMatches = isComplete;
-    search(sd, isComplete);
-}
-
-void
-FakeVimHandler::Private::resetCount()
-{
-    g.mvcount = 0;
-    g.opcount = 0;
-}
-
-bool
-FakeVimHandler::Private::isInputCount(const Input &input) const
-{
-    return input.isDigit() && (!input.is('0') || g.mvcount > 0);
-}
-
-bool
-FakeVimHandler::Private::atEmptyLine(int pos) const
-{
-    return blockAt(pos).length() == 1;
-}
-
-bool
-FakeVimHandler::Private::atEmptyLine(const QTextCursor &tc) const
-{
-    return atEmptyLine(tc.position());
-}
-
-bool
-FakeVimHandler::Private::atEmptyLine() const
-{
-    return atEmptyLine(position());
-}
-
-bool
-FakeVimHandler::Private::atBoundary(bool end, bool simple, bool onlyWords,
-                                    const QTextCursor &tc) const
-{
-    if (tc.isNull())
-        return atBoundary(end, simple, onlyWords, m_cursor);
-    if (atEmptyLine(tc))
-        return true;
-    int pos       = tc.position();
-    QChar c1      = characterAt(pos);
-    QChar c2      = characterAt(pos + (end ? 1 : -1));
-    int thisClass = charClass(c1, simple);
-    return (!onlyWords || thisClass != 0) &&
-           (c2.isNull() || c2 == QChar::ParagraphSeparator || thisClass != charClass(c2, simple));
-}
-
-bool
-FakeVimHandler::Private::atWordBoundary(bool end, bool simple, const QTextCursor &tc) const
-{
-    return atBoundary(end, simple, true, tc);
-}
-
-bool
-FakeVimHandler::Private::atWordStart(bool simple, const QTextCursor &tc) const
-{
-    return atWordBoundary(false, simple, tc);
-}
-
-bool
-FakeVimHandler::Private::atWordEnd(bool simple, const QTextCursor &tc) const
-{
-    return atWordBoundary(true, simple, tc);
-}
-
-bool
-FakeVimHandler::Private::isFirstNonBlankOnLine(int pos)
-{
-    for (int i = blockAt(pos).position(); i < pos; ++i) {
-        if (!document()->characterAt(i).isSpace())
-            return false;
-    }
-    return true;
-}
-
-void
-FakeVimHandler::Private::pushUndoState(bool overwrite)
-{
-    if (m_buffer->editBlockLevel != 0 && m_buffer->undoState.isValid())
-        return; // No need to save undo state for inner edit blocks.
-
-    if (m_buffer->undoState.isValid() && !overwrite)
-        return;
-
-    UNDO_DEBUG("PUSH UNDO");
-    int pos = position();
-    if (!isInsertMode()) {
-        if (isVisualMode() || g.submode == DeleteSubMode ||
-            (g.submode == ChangeSubMode && g.movetype != MoveLineWise)) {
-            pos = qMin(pos, anchor());
-            if (isVisualLineMode())
-                pos = firstPositionInLine(lineForPosition(pos));
-            else if (isVisualBlockMode())
-                pos = blockAt(pos).position() + qMin(columnAt(anchor()), columnAt(position()));
-        } else if (g.movetype == MoveLineWise && s.startOfLine.value()) {
-            QTextCursor tc = m_cursor;
-            if (g.submode == ShiftLeftSubMode || g.submode == ShiftRightSubMode ||
-                g.submode == IndentSubMode) {
-                pos = qMin(pos, anchor());
-            }
-            tc.setPosition(pos);
-            moveToFirstNonBlankOnLine(&tc);
-            pos = qMin(pos, tc.position());
-        }
-    }
-
-    CursorPosition lastChangePosition(document(), pos);
-    setMark('.', lastChangePosition);
-
-    m_buffer->redo.clear();
-    m_buffer->undoState = State(revision(), lastChangePosition, m_buffer->marks,
-                                m_buffer->lastVisualMode, m_buffer->lastVisualModeInverted);
-}
-
-void
-FakeVimHandler::Private::moveDown(int n)
-{
-    if (n == 0)
-        return;
-
-    QTextBlock block = m_cursor.block();
-    const int col    = position() - block.position();
-
-    int lines    = qAbs(n);
-    int position = 0;
-    while (block.isValid()) {
-        position = block.position() + qMax(0, qMin(block.length() - 2, col));
-        if (block.isVisible()) {
-            --lines;
-            if (lines < 0)
-                break;
-        }
-        block = n > 0 ? nextLine(block) : previousLine(block);
-    }
-
-    setPosition(position);
-    moveToTargetColumn();
-    updateScrollOffset();
-}
-
-void
-FakeVimHandler::Private::moveDownVisually(int n)
-{
-    const QTextCursor::MoveOperation moveOperation = (n > 0) ? QTextCursor::Down : QTextCursor::Up;
-    int count                                      = qAbs(n);
-    int oldPos                                     = m_cursor.position();
-
-    while (count > 0) {
-        m_cursor.movePosition(moveOperation, QTextCursor::KeepAnchor, 1);
-        if (oldPos == m_cursor.position())
-            break;
-        oldPos           = m_cursor.position();
-        QTextBlock block = m_cursor.block();
-        if (block.isVisible())
-            --count;
-    }
-
-    QTextCursor tc = m_cursor;
-    tc.movePosition(QTextCursor::StartOfLine);
-    const int minPos = tc.position();
-    moveToEndOfLineVisually(&tc);
-    const int maxPos = tc.position();
-
-    if (m_targetColumn == -1) {
-        setPosition(maxPos);
-    } else {
-        setPosition(qMin(maxPos, minPos + m_targetColumnWrapped));
-        const int targetColumn = m_targetColumnWrapped;
-        setTargetColumn();
-        m_targetColumnWrapped = targetColumn;
-    }
-
-    if (!isInsertMode() && atEndOfLine())
-        m_cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
-
-    updateScrollOffset();
-}
-
-void
-FakeVimHandler::Private::movePageDown(int count)
-{
-    const int scrollOffset = windowScrollOffset();
-    const int screenLines  = linesOnScreen();
-    const int offset       = count > 0 ? scrollOffset - 2 : screenLines - scrollOffset + 2;
-    const int value        = count * screenLines - cursorLineOnScreen() + offset;
-    moveDown(value);
-
-    if (count > 0)
-        scrollToLine(cursorLine());
-    else
-        scrollToLine(qMax(0, cursorLine() - screenLines + 1));
-}
-
-void
-FakeVimHandler::Private::commitCursor()
-{
-    QTextCursor tc = m_cursor;
-
-    if (isVisualMode()) {
-        int pos = tc.position();
-        int anc = tc.anchor();
-
-        if (isVisualBlockMode()) {
-            const int col1 = columnAt(anc);
-            const int col2 = columnAt(pos);
-            if (col1 > col2)
-                ++anc;
-            else if (!tc.atBlockEnd())
-                ++pos;
-            // FIXME: After '$' command (i.e. m_visualTargetColumn == -1), end of selected lines
-            //        should be selected.
-        } else if (isVisualLineMode()) {
-            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) + 1;
-            }
-            // putting cursor on folded line will unfold the line, so move the cursor a bit
-            if (!blockAt(pos).isVisible())
-                ++pos;
-        } else if (isVisualCharMode()) {
-            if (anc > pos)
-                ++anc;
-            else if (!editor()->hasFocus() || isCommandLineMode())
-                m_fixCursorTimer.start();
-        }
-
-        tc.setPosition(anc);
-        tc.setPosition(pos, QTextCursor::KeepAnchor);
-    } else if (g.subsubmode == SearchSubSubMode && !m_searchCursor.isNull()) {
-        tc = m_searchCursor;
-    } else {
-        tc.clearSelection();
-    }
-
-    updateCursorShape();
-
-    if (isVisualBlockMode()) {
-        q->requestSetBlockSelection(tc);
-    } else {
-        q->requestDisableBlockSelection();
-        if (editor())
-            EDITOR(setTextCursor(tc));
-    }
-}
-
-void
-FakeVimHandler::Private::pullCursor()
-{
-    if (!m_cursorNeedsUpdate)
-        return;
-
-    m_cursorNeedsUpdate = false;
-
-    QTextCursor oldCursor = m_cursor;
-
-    bool visualBlockMode = false;
-    q->requestHasBlockSelection(&visualBlockMode);
-
-    if (visualBlockMode)
-        q->requestBlockSelection(&m_cursor);
-    else if (editor())
-        m_cursor = editorCursor();
-
-    // Cursor should be always valid.
-    if (m_cursor.isNull())
-        m_cursor = QTextCursor(document());
-
-    if (visualBlockMode)
-        g.visualMode = VisualBlockMode;
-    else if (m_cursor.hasSelection())
-        g.visualMode = VisualCharMode;
-    else
-        g.visualMode = NoVisualMode;
-
-    // Keep visually the text selection same.
-    // With thick text cursor, the character under cursor is treated as selected.
-    if (isVisualCharMode() && hasThinCursor())
-        moveLeft();
-
-    // Cursor position can be after the end of line only in some modes.
-    if (atEndOfLine() && !isVisualMode() && !isInsertMode())
-        moveLeft();
-
-    // Record external jump to different line.
-    if (lineForPosition(position()) != lineForPosition(oldCursor.position()))
-        recordJump(oldCursor.position());
-
-    setTargetColumn();
-}
-
-QTextCursor
-FakeVimHandler::Private::editorCursor() const
-{
-    QTextCursor tc = EDITOR(textCursor());
-    tc.setVisualNavigation(false);
-    return tc;
-}
-
-bool
-FakeVimHandler::Private::moveToNextParagraph(int count)
-{
-    const bool forward = count > 0;
-    int repeat         = forward ? count : -count;
-    QTextBlock block   = this->block();
-
-    if (block.isValid() && block.length() == 1)
-        ++repeat;
-
-    for (; block.isValid(); block = forward ? block.next() : block.previous()) {
-        if (block.length() == 1) {
-            if (--repeat == 0)
-                break;
-            while (block.isValid() && block.length() == 1)
-                block = forward ? block.next() : block.previous();
-            if (!block.isValid())
-                break;
-        }
-    }
-
-    if (!block.isValid())
-        --repeat;
-
-    if (repeat > 0)
-        return false;
-
-    if (block.isValid())
-        setPosition(block.position());
-    else
-        setPosition(forward ? lastPositionInDocument() : 0);
-
-    return true;
-}
-
-void
-FakeVimHandler::Private::moveToParagraphStartOrEnd(int direction)
-{
-    bool emptyLine = atEmptyLine();
-    int oldPos     = -1;
-
-    while (atEmptyLine() == emptyLine && oldPos != position()) {
-        oldPos = position();
-        moveDown(direction);
-    }
-
-    if (oldPos != position())
-        moveUp(direction);
-}
-
-void
-FakeVimHandler::Private::moveToEndOfLine()
-{
-    // Additionally select (in visual mode) or apply current command on hidden lines following
-    // the current line.
-    bool onlyVisibleLines = isVisualMode() || g.submode != NoSubMode;
-    const int id          = onlyVisibleLines ? lineNumber(block()) : block().blockNumber() + 1;
-    setPosition(lastPositionInLine(id, onlyVisibleLines));
-    setTargetColumn();
-}
-
-void
-FakeVimHandler::Private::moveToEndOfLineVisually()
-{
-    moveToEndOfLineVisually(&m_cursor);
-    setTargetColumn();
-}
-
-void
-FakeVimHandler::Private::moveToEndOfLineVisually(QTextCursor *tc)
-{
-    // Moving to end of line ends up on following line if the line is wrapped.
-    tc->movePosition(QTextCursor::StartOfLine);
-    const int minPos = tc->position();
-    tc->movePosition(QTextCursor::EndOfLine);
-    int maxPos = tc->position();
-    tc->movePosition(QTextCursor::StartOfLine);
-    if (minPos != tc->position())
-        --maxPos;
-    tc->setPosition(maxPos);
-}
-
-void
-FakeVimHandler::Private::moveBehindEndOfLine()
-{
-    q->fold(1, false);
-    int pos = qMin(block().position() + block().length() - 1, lastPositionInDocument() + 1);
-    setPosition(pos);
-    setTargetColumn();
-}
-
-void
-FakeVimHandler::Private::moveToStartOfLine()
-{
-    setPosition(block().position());
-    setTargetColumn();
-}
-
-void
-FakeVimHandler::Private::moveToStartOfLineVisually()
-{
-    m_cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
-    setTargetColumn();
-}
-
-void
-FakeVimHandler::Private::fixSelection()
-{
-    if (g.rangemode == RangeBlockMode)
-        return;
-
-    if (g.movetype == MoveInclusive) {
-        // If position or anchor is after end of non-empty line, include line break in selection.
-        if (characterAtCursor() == QChar::ParagraphSeparator) {
-            if (!atEmptyLine() && !atDocumentEnd()) {
-                setPosition(position() + 1);
-                return;
-            }
-        } else if (characterAt(anchor()) == QChar::ParagraphSeparator) {
-            QTextCursor tc = m_cursor;
-            tc.setPosition(anchor());
-            if (!atEmptyLine(tc)) {
-                setAnchorAndPosition(anchor() + 1, position());
-                return;
-            }
-        }
-    }
-
-    if (g.movetype == MoveExclusive && g.subsubmode == NoSubSubMode) {
-        if (anchor() < position() && atBlockStart()) {
-            // Exclusive motion ending at the beginning of line
-            // becomes inclusive and end is moved to end of previous line.
-            g.movetype = MoveInclusive;
-            moveToStartOfLine();
-            moveLeft();
-
-            // Exclusive motion ending at the beginning of line and
-            // starting at or before first non-blank on a line becomes linewise.
-            if (anchor() < block().position() && isFirstNonBlankOnLine(anchor()))
-                g.movetype = MoveLineWise;
-        }
-    }
-
-    if (g.movetype == MoveLineWise)
-        g.rangemode = (g.submode == ChangeSubMode) ? RangeLineModeExclusive : RangeLineMode;
-
-    if (g.movetype == MoveInclusive) {
-        if (anchor() <= position()) {
-            if (!atBlockEnd())
-                setPosition(position() + 1); // correction
-
-            // Omit first character in selection if it's line break on non-empty line.
-            int start = anchor();
-            int end   = position();
-            if (afterEndOfLine(document(), start) && start > 0) {
-                start = qMin(start + 1, end);
-                if (g.submode == DeleteSubMode && !atDocumentEnd())
-                    setAnchorAndPosition(start, end + 1);
-                else
-                    setAnchorAndPosition(start, end);
-            }
-
-            // If more than one line is selected and all are selected completely
-            // movement becomes linewise.
-            if (start < block().position() && isFirstNonBlankOnLine(start) && atBlockEnd()) {
-                if (g.submode != ChangeSubMode) {
-                    moveRight();
-                    if (atEmptyLine())
-                        moveRight();
-                }
-                g.movetype = MoveLineWise;
-            }
-        } else if (!m_anchorPastEnd) {
-            setAnchorAndPosition(anchor() + 1, position());
-        }
-    }
-
-    if (m_positionPastEnd) {
-        moveBehindEndOfLine();
-        moveRight();
-        setAnchorAndPosition(anchor(), position());
-    }
-
-    if (m_anchorPastEnd) {
-        const int pos = position();
-        setPosition(anchor());
-        moveBehindEndOfLine();
-        moveRight();
-        setAnchorAndPosition(position(), pos);
-    }
-}
-
-bool
-FakeVimHandler::Private::finishSearch()
-{
-    if (g.lastSearch.isEmpty() ||
-        (!g.currentMessage.isEmpty() && g.currentMessageLevel == MessageError)) {
-        return false;
-    }
-    if (g.submode != NoSubMode)
-        setAnchorAndPosition(m_searchStartPosition, position());
-    return true;
-}
-
-void
-FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
-{
-    //dump("FINISH MOVEMENT");
-    if (g.submode == FilterSubMode) {
-        int beginLine = lineForPosition(anchor());
-        int endLine   = lineForPosition(position());
-        setPosition(qMin(anchor(), position()));
-        enterExMode(QString(".,+%1!").arg(qAbs(endLine - beginLine)));
-        return;
-    }
-
-    if (g.submode == ChangeSubMode || g.submode == DeleteSubMode || g.submode == CommentSubMode ||
-        g.submode == ExchangeSubMode || g.submode == ReplaceWithRegisterSubMode ||
-        g.submode == AddSurroundingSubMode || g.submode == YankSubMode ||
-        g.submode == InvertCaseSubMode || g.submode == DownCaseSubMode ||
-        g.submode == UpCaseSubMode || g.submode == IndentSubMode || g.submode == ShiftLeftSubMode ||
-        g.submode == ShiftRightSubMode) {
-        fixSelection();
-
-        if (g.submode == ChangeSubMode || g.submode == DeleteSubMode || g.submode == YankSubMode) {
-            yankText(currentRange(), m_register);
-        }
-    }
-
-    if (g.submode == ChangeSubMode) {
-        pushUndoState(false);
-        beginEditBlock();
-        removeText(currentRange());
-        if (g.movetype == MoveLineWise)
-            insertAutomaticIndentation(true);
-        endEditBlock();
-        setTargetColumn();
-    } else if (g.submode == CommentSubMode) {
-        pushUndoState(false);
-        beginEditBlock();
-        toggleComment(currentRange());
-        endEditBlock();
-    } else if (g.submode == AddSurroundingSubMode) {
-        g.subsubmode = SurroundSubSubMode;
-        g.dotCommand = dotCommandMovement;
-
-        // We now only know the region that should be surrounded, but not the actual
-        // character that should surround it. We thus do NOT want to finish the
-        // movement yet here, so we return early.
-        // The next character entered will be used by the SurroundSubSubMode.
-        return;
-    } else if (g.submode == ExchangeSubMode) {
-        exchangeRange(currentRange());
-    } else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister.value()) {
-        pushUndoState(false);
-        beginEditBlock();
-        replaceWithRegister(currentRange());
-        endEditBlock();
-    } else if (g.submode == DeleteSubMode) {
-        pushUndoState(false);
-        beginEditBlock();
-        const int pos = position();
-        // Always delete something (e.g. 'dw' on an empty line deletes the line).
-        if (pos == anchor() && g.movetype == MoveInclusive)
-            removeText(Range(pos, pos + 1));
-        else
-            removeText(currentRange());
-        if (g.movetype == MoveLineWise)
-            handleStartOfLine();
-        endEditBlock();
-    } else if (g.submode == YankSubMode) {
-        bool isVisualModeYank = isVisualMode();
-        leaveVisualMode();
-        const QTextCursor tc = m_cursor;
-        if (g.rangemode == RangeBlockMode) {
-            const int pos1 = tc.block().position();
-            const int pos2 = blockAt(tc.anchor()).position();
-            const int col  = qMin(tc.position() - pos1, tc.anchor() - pos2);
-            setPosition(qMin(pos1, pos2) + col);
-        } else {
-            setPosition(qMin(position(), anchor()));
-            if (g.rangemode == RangeLineMode) {
-                if (isVisualModeYank)
-                    moveToStartOfLine();
-                else
-                    moveToTargetColumn();
-            }
-        }
-        setTargetColumn();
-    } else if (g.submode == InvertCaseSubMode || g.submode == UpCaseSubMode ||
-               g.submode == DownCaseSubMode) {
-        beginEditBlock();
-        if (g.submode == InvertCaseSubMode)
-            invertCase(currentRange());
-        else if (g.submode == DownCaseSubMode)
-            downCase(currentRange());
-        else if (g.submode == UpCaseSubMode)
-            upCase(currentRange());
-        if (g.movetype == MoveLineWise)
-            handleStartOfLine();
-        endEditBlock();
-    } else if (g.submode == IndentSubMode || g.submode == ShiftRightSubMode ||
-               g.submode == ShiftLeftSubMode) {
-        recordJump();
-        pushUndoState(false);
-        if (g.submode == IndentSubMode)
-            indentSelectedText();
-        else if (g.submode == ShiftRightSubMode)
-            shiftRegionRight(1);
-        else if (g.submode == ShiftLeftSubMode)
-            shiftRegionLeft(1);
-    }
-
-    if (!dotCommandMovement.isEmpty()) {
-        QString dotCommand = dotCommandFromSubMode(g.submode);
-        if (!dotCommand.isEmpty()) {
-            if (g.submode == ReplaceWithRegisterSubMode)
-                dotCommand = QString("\"%1%2").arg(QChar(m_register)).arg(dotCommand);
-
-            setDotCommand(dotCommand + dotCommandMovement);
-        }
-    }
-
-    // Change command continues in insert mode.
-    if (g.submode == ChangeSubMode) {
-        clearCurrentMode();
-        enterInsertMode();
-    } else {
-        leaveCurrentMode();
-    }
-}
-
-void
-FakeVimHandler::Private::leaveCurrentMode()
-{
-    if (isVisualMode())
-        enterCommandMode(g.returnToMode);
-    else if (g.returnToMode == CommandMode)
-        enterCommandMode();
-    else if (g.returnToMode == InsertMode)
-        enterInsertMode();
-    else
-        enterReplaceMode();
-
-    if (isNoVisualMode())
-        setAnchor();
-}
-
-void
-FakeVimHandler::Private::clearCurrentMode()
-{
-    g.submode            = NoSubMode;
-    g.subsubmode         = NoSubSubMode;
-    g.movetype           = MoveInclusive;
-    g.gflag              = false;
-    g.surroundUpperCaseS = false;
-    g.surroundFunction.clear();
-    m_register  = '"';
-    g.rangemode = RangeCharMode;
-    g.currentCommand.clear();
-    resetCount();
-}
-
-void
-FakeVimHandler::Private::updateSelection()
-{
-    QList<QTextEdit::ExtraSelection> selections = m_extraSelections;
-    if (s.showMarks.value()) {
-        for (auto it = m_buffer->marks.cbegin(), end = m_buffer->marks.cend(); it != end; ++it) {
-            QTextEdit::ExtraSelection sel;
-            sel.cursor = m_cursor;
-            setCursorPosition(&sel.cursor, it.value().position(document()));
-            sel.cursor.setPosition(sel.cursor.position(), QTextCursor::MoveAnchor);
-            sel.cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
-            sel.format = m_cursor.blockCharFormat();
-            sel.format.setForeground(Qt::blue);
-            sel.format.setBackground(Qt::green);
-            selections.append(sel);
-        }
-    }
-    q->selectionChanged(selections);
-}
-
-void
-FakeVimHandler::Private::updateHighlights()
-{
-    if (s.useCoreSearch.value() || !s.hlSearch.value() || g.highlightsCleared) {
-        if (m_highlighted.isEmpty())
-            return;
-        m_highlighted.clear();
-    } else if (m_highlighted != g.lastNeedle) {
-        m_highlighted = g.lastNeedle;
-    } else {
-        return;
-    }
-
-    q->highlightMatches(m_highlighted);
-}
-
-void
-FakeVimHandler::Private::updateMiniBuffer()
-{
-    if (!m_textedit && !m_plaintextedit)
-        return;
-
-    QString msg;
-    int cursorPos             = -1;
-    int anchorPos             = -1;
-    MessageLevel messageLevel = MessageMode;
-
-    if (!g.mapStates.isEmpty() && g.mapStates.last().silent && g.currentMessageLevel < MessageInfo)
-        g.currentMessage.clear();
-
-    if (g.passing) {
-        msg = "PASSING";
-    } else if (g.subsubmode == SearchSubSubMode) {
-        msg = g.searchBuffer.display();
-        if (g.mapStates.isEmpty()) {
-            cursorPos = g.searchBuffer.cursorPos() + 1;
-            anchorPos = g.searchBuffer.anchorPos() + 1;
-        }
-    } else if (g.mode == ExMode) {
-        msg = g.commandBuffer.display();
-        if (g.mapStates.isEmpty()) {
-            cursorPos = g.commandBuffer.cursorPos() + 1;
-            anchorPos = g.commandBuffer.anchorPos() + 1;
-        }
-    } else if (!g.currentMessage.isEmpty()) {
-        msg = g.currentMessage;
-        g.currentMessage.clear();
-        messageLevel = g.currentMessageLevel;
-    } else if (!g.mapStates.isEmpty() && !g.mapStates.last().silent) {
-        // Do not reset previous message when after running a mapped command.
-        return;
-    } else if (g.mode == CommandMode && !g.currentCommand.isEmpty() && s.showCmd.value()) {
-        msg          = g.currentCommand;
-        messageLevel = MessageShowCmd;
-    } else if (g.mode == CommandMode && isVisualMode()) {
-        if (isVisualCharMode())
-            msg = "-- VISUAL --";
-        else if (isVisualLineMode())
-            msg = "-- VISUAL LINE --";
-        else if (isVisualBlockMode())
-            msg = "VISUAL BLOCK";
-    } else if (g.mode == InsertMode) {
-        msg = "-- INSERT --";
-        if (g.submode == CtrlRSubMode)
-            msg += " ^R";
-        else if (g.submode == CtrlVSubMode)
-            msg += " ^V";
-    } else if (g.mode == ReplaceMode) {
-        msg = "-- REPLACE --";
-    } else {
-        if (g.returnToMode == CommandMode)
-            msg = "-- COMMAND --";
-        else if (g.returnToMode == InsertMode)
-            msg = "-- (insert) --";
-        else
-            msg = "-- (replace) --";
-    }
-
-    if (g.isRecording && msg.startsWith("--"))
-        msg.append(' ').append("Recording");
-
-    q->commandBufferChanged(msg, cursorPos, anchorPos, messageLevel);
-
-    int linesInDoc = linesInDocument();
-    int l          = cursorLine();
-    QString status;
-    const QString pos = QString("%1,%2").arg(l + 1).arg(physicalCursorColumn() + 1);
-    // FIXME: physical "-" logical
-    if (linesInDoc != 0)
-        status = Tr::tr("%1%2%").arg(pos, -10).arg(l * 100 / linesInDoc, 4);
-    else
-        status = Tr::tr("%1All").arg(pos, -10);
-    q->statusDataChanged(status);
-}
-
-void
-FakeVimHandler::Private::showMessage(MessageLevel level, const QString &msg)
-{
-    g.currentMessage      = msg;
-    g.currentMessageLevel = level;
-}
-
-void
-FakeVimHandler::Private::notImplementedYet()
-{
-    logDebug() << "Not implemented in FakeVim";
-    showMessage(MessageError, Tr::tr("Not implemented in FakeVim."));
-}
-
-void
-FakeVimHandler::Private::passShortcuts(bool enable)
-{
-    g.passing = enable;
-    updateMiniBuffer();
-    if (enable)
-        QCoreApplication::instance()->installEventFilter(q);
-    else
-        QCoreApplication::instance()->removeEventFilter(q);
-}
-
-bool
-FakeVimHandler::Private::handleCommandSubSubMode(const Input &input)
-{
-    bool handled = true;
-
-    if (g.subsubmode == FtSubSubMode) {
-        g.semicolonType = g.subsubdata;
-        g.semicolonKey  = input.text();
-        handled         = handleFfTt(g.semicolonKey);
-        g.subsubmode    = NoSubSubMode;
-        if (handled) {
-            finishMovement(
-                QString("%1%2%3").arg(count()).arg(g.semicolonType.text()).arg(g.semicolonKey));
-        }
-    } else if (g.subsubmode == TextObjectSubSubMode) {
-        // vim-surround treats aw and aW the same as iw and iW, respectively
-        if ((input.is('w') || input.is('W')) && g.submode == AddSurroundingSubMode &&
-            g.subsubdata.is('a'))
-            g.subsubdata = Input('i');
-
-        if (input.is('w'))
-            selectWordTextObject(g.subsubdata.is('i'));
-        else if (input.is('W'))
-            selectWORDTextObject(g.subsubdata.is('i'));
-        else if (input.is('s'))
-            selectSentenceTextObject(g.subsubdata.is('i'));
-        else if (input.is('p'))
-            selectParagraphTextObject(g.subsubdata.is('i'));
-        else if (input.is('[') || input.is(']'))
-            handled = selectBlockTextObject(g.subsubdata.is('i'), '[', ']');
-        else if (input.is('(') || input.is(')') || input.is('b'))
-            handled = selectBlockTextObject(g.subsubdata.is('i'), '(', ')');
-        else if (input.is('<') || input.is('>'))
-            handled = selectBlockTextObject(g.subsubdata.is('i'), '<', '>');
-        else if (input.is('{') || input.is('}') || input.is('B'))
-            handled = selectBlockTextObject(g.subsubdata.is('i'), '{', '}');
-        else if (input.is('"') || input.is('\'') || input.is('`'))
-            handled = selectQuotedStringTextObject(g.subsubdata.is('i'), input.asChar());
-        else if (input.is('a') && s.emulateArgTextObj.value())
-            handled = selectArgumentTextObject(g.subsubdata.is('i'));
-        else
-            handled = false;
-        g.subsubmode = NoSubSubMode;
-        if (handled) {
-            finishMovement(
-                QString("%1%2%3").arg(count()).arg(g.subsubdata.text()).arg(input.text()));
-        }
-    } else if (g.subsubmode == MarkSubSubMode) {
-        setMark(input.asChar(), CursorPosition(m_cursor));
-        g.subsubmode = NoSubSubMode;
-    } else if (g.subsubmode == BackTickSubSubMode || g.subsubmode == TickSubSubMode) {
-        handled = jumpToMark(input.asChar(), g.subsubmode == BackTickSubSubMode);
-        if (handled)
-            finishMovement();
-        g.subsubmode = NoSubSubMode;
-    } else if (g.subsubmode == ZSubSubMode) {
-        handled = false;
-        if (input.is('j') || input.is('k')) {
-            int pos = position();
-            q->foldGoTo(input.is('j') ? count() : -count(), false);
-            if (pos != position()) {
-                handled = true;
-                finishMovement(QString("%1z%2").arg(count()).arg(input.text()));
-            }
-        }
-    } else if (g.subsubmode == OpenSquareSubSubMode || g.subsubmode == CloseSquareSubSubMode) {
-        int pos = position();
-        if (input.is('{') && g.subsubmode == OpenSquareSubSubMode)
-            searchBalanced(false, '{', '}');
-        else if (input.is('}') && g.subsubmode == CloseSquareSubSubMode)
-            searchBalanced(true, '}', '{');
-        else if (input.is('(') && g.subsubmode == OpenSquareSubSubMode)
-            searchBalanced(false, '(', ')');
-        else if (input.is(')') && g.subsubmode == CloseSquareSubSubMode)
-            searchBalanced(true, ')', '(');
-        else if (input.is('[') && g.subsubmode == OpenSquareSubSubMode)
-            bracketSearchBackward(&m_cursor, "^\\{", count());
-        else if (input.is('[') && g.subsubmode == CloseSquareSubSubMode)
-            bracketSearchForward(&m_cursor, "^\\}", count(), false);
-        else if (input.is(']') && g.subsubmode == OpenSquareSubSubMode)
-            bracketSearchBackward(&m_cursor, "^\\}", count());
-        else if (input.is(']') && g.subsubmode == CloseSquareSubSubMode)
-            bracketSearchForward(&m_cursor, "^\\{", count(), g.submode != NoSubMode);
-        else if (input.is('z'))
-            q->foldGoTo(g.subsubmode == OpenSquareSubSubMode ? -count() : count(), true);
-        handled = pos != position();
-        if (handled) {
-            if (lineForPosition(pos) != lineForPosition(position()))
-                recordJump(pos);
-            finishMovement(QString("%1%2%3")
-                               .arg(count())
-                               .arg(g.subsubmode == OpenSquareSubSubMode ? '[' : ']')
-                               .arg(input.text()));
-        }
-    } else if (g.subsubmode == SurroundWithFunctionSubSubMode) {
-        if (input.isReturn()) {
-            pushUndoState(false);
-            beginEditBlock();
-
-            const QString dotCommand = "ys" + g.dotCommand + "f" + g.surroundFunction + "<CR>";
-
-            surroundCurrentRange(Input(')'), g.surroundFunction);
-
-            g.dotCommand = dotCommand;
-
-            endEditBlock();
-            leaveCurrentMode();
-        } else {
-            g.surroundFunction += input.asChar();
-        }
-        return true;
-    } else if (g.subsubmode == SurroundSubSubMode) {
-        if (input.is('f') && g.submode == AddSurroundingSubMode) {
-            g.subsubmode = SurroundWithFunctionSubSubMode;
-            g.commandBuffer.setContents("");
-            return true;
-        }
-
-        pushUndoState(false);
-        beginEditBlock();
-
-        surroundCurrentRange(input);
-
-        endEditBlock();
-        leaveCurrentMode();
-    } else {
-        handled = false;
-    }
-    return handled;
-}
-
-bool
-FakeVimHandler::Private::handleCount(const Input &input)
-{
-    if (!isInputCount(input))
-        return false;
-    g.mvcount = g.mvcount * 10 + input.text().toInt();
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleMovement(const Input &input)
-{
-    bool handled = true;
-    int count    = this->count();
-
-    if (handleCount(input)) {
-        return true;
-    } else if (input.is('0')) {
-        g.movetype = MoveExclusive;
-        if (g.gflag)
-            moveToStartOfLineVisually();
-        else
-            moveToStartOfLine();
-        count = 1;
-    } else if (input.is('a') || input.is('i')) {
-        g.subsubmode = TextObjectSubSubMode;
-        g.subsubdata = input;
-    } else if (input.is('^') || input.is('_')) {
-        if (g.gflag)
-            moveToFirstNonBlankOnLineVisually();
-        else
-            moveToFirstNonBlankOnLine();
-        g.movetype = MoveExclusive;
-#if 0
-    }
-    else if (false && input.is(',')) {
-        // FIXME: fakevim uses ',' by itself, so it is incompatible
-        g.subsubmode = FtSubSubMode;
-        // HACK: toggle 'f' <-> 'F', 't' <-> 'T'
-        //g.subsubdata = g.semicolonType ^ 32;
-        handleFfTt(g.semicolonKey, true);
-        g.subsubmode = NoSubSubMode;
-#endif
-    } else if (input.is(';')) {
-        g.subsubmode = FtSubSubMode;
-        g.subsubdata = g.semicolonType;
-        handleFfTt(g.semicolonKey, true);
-        g.subsubmode = NoSubSubMode;
-    } else if (input.is('/') || input.is('?')) {
-        g.lastSearchForward = input.is('/');
-        if (s.useCoreSearch.value()) {
-            // re-use the core dialog.
-            g.findPending       = true;
-            m_findStartPosition = position();
-            g.movetype          = MoveExclusive;
-            setAnchor(); // clear selection: otherwise, search is restricted to selection
-            q->findRequested(!g.lastSearchForward);
-        } else {
-            // FIXME: make core find dialog sufficiently flexible to
-            // produce the "default vi" behaviour too. For now, roll our own.
-            g.currentMessage.clear();
-            g.movetype   = MoveExclusive;
-            g.subsubmode = SearchSubSubMode;
-            g.searchBuffer.setPrompt(g.lastSearchForward ? '/' : '?');
-            m_searchStartPosition  = position();
-            m_searchFromScreenLine = firstVisibleLine();
-            m_searchCursor         = QTextCursor();
-            g.searchBuffer.clear();
-        }
-    } else if (input.is('`')) {
-        g.subsubmode = BackTickSubSubMode;
-    } else if (input.is('#') || input.is('*')) {
-        // FIXME: That's not proper vim behaviour
-        QString needle;
-        QTextCursor tc = m_cursor;
-        tc.select(QTextCursor::WordUnderCursor);
-        needle = QRegularExpression::escape(tc.selection().toPlainText());
-        if (!g.gflag) {
-            needle.prepend("\\<");
-            needle.append("\\>");
-        }
-        setAnchorAndPosition(tc.position(), tc.anchor());
-        g.searchBuffer.historyPush(needle);
-        g.lastSearch        = needle;
-        g.lastSearchForward = input.is('*');
-        handled             = searchNext();
-    } else if (input.is('\'')) {
-        g.subsubmode = TickSubSubMode;
-        if (g.submode != NoSubMode)
-            g.movetype = MoveLineWise;
-    } else if (input.is('|')) {
-        moveToStartOfLine();
-        const int column = count - 1;
-        moveRight(qMin(column, rightDist() - 1));
-        m_targetColumn       = column;
-        m_visualTargetColumn = column;
-    } else if (input.is('{') || input.is('}')) {
-        const int oldPosition = position();
-        handled = input.is('}') ? moveToNextParagraph(count) : moveToPreviousParagraph(count);
-        if (handled) {
-            recordJump(oldPosition);
-            setTargetColumn();
-            g.movetype = MoveExclusive;
-        }
-    } else if (input.isReturn()) {
-        moveToStartOfLine();
-        moveDown();
-        moveToFirstNonBlankOnLine();
-    } else if (input.is('-')) {
-        moveToStartOfLine();
-        moveUp(count);
-        moveToFirstNonBlankOnLine();
-    } else if (input.is('+')) {
-        moveToStartOfLine();
-        moveDown(count);
-        moveToFirstNonBlankOnLine();
-    } else if (input.isKey(Key_Home)) {
-        moveToStartOfLine();
-    } else if (input.is('$') || input.isKey(Key_End)) {
-        if (g.gflag) {
-            if (count > 1)
-                moveDownVisually(count - 1);
-            moveToEndOfLineVisually();
-        } else {
-            if (count > 1)
-                moveDown(count - 1);
-            moveToEndOfLine();
-        }
-        g.movetype = atEmptyLine() ? MoveExclusive : MoveInclusive;
-        if (g.submode == NoSubMode)
-            m_targetColumn = -1;
-        if (isVisualMode())
-            m_visualTargetColumn = -1;
-    } else if (input.is('%')) {
-        recordJump();
-        if (g.mvcount == 0) {
-            moveToMatchingParanthesis();
-            g.movetype = MoveInclusive;
-        } else {
-            // set cursor position in percentage - formula taken from Vim help
-            setPosition(firstPositionInLine((count * linesInDocument() + 99) / 100));
-            moveToTargetColumn();
-            handleStartOfLine();
-            g.movetype = MoveLineWise;
-        }
-    } else if (input.is('b') || input.isShift(Key_Left)) {
-        moveToNextWordStart(count, false, false);
-    } else if (input.is('B') || input.isControl(Key_Left)) {
-        moveToNextWordStart(count, true, false);
-    } else if (input.is('e') && g.gflag) {
-        moveToNextWordEnd(count, false, false);
-    } else if (input.is('e')) {
-        moveToNextWordEnd(count, false, true, false);
-    } else if (input.is('E') && g.gflag) {
-        moveToNextWordEnd(count, true, false);
-    } else if (input.is('E')) {
-        moveToNextWordEnd(count, true, true, false);
-    } else if (input.isControl('e')) {
-        // FIXME: this should use the "scroll" option, and "count"
-        if (cursorLineOnScreen() == 0)
-            moveDown(1);
-        scrollDown(1);
-    } else if (input.is('f')) {
-        g.subsubmode = FtSubSubMode;
-        g.movetype   = MoveInclusive;
-        g.subsubdata = input;
-    } else if (input.is('F')) {
-        g.subsubmode = FtSubSubMode;
-        g.movetype   = MoveExclusive;
-        g.subsubdata = input;
-    } else if (!g.gflag && input.is('g')) {
-        g.gflag = true;
-        return true;
-    } else if (input.is('g') || input.is('G')) {
-        QString dotCommand = QString("%1G").arg(count);
-        recordJump();
-        if (input.is('G') && g.mvcount == 0)
-            dotCommand = "G";
-        int n = (input.is('g')) ? 1 : linesInDocument();
-        n     = g.mvcount == 0 ? n : count;
-        if (g.submode == NoSubMode || g.submode == ZSubMode || g.submode == CapitalZSubMode ||
-            g.submode == RegisterSubMode) {
-            setPosition(firstPositionInLine(n, false));
-            handleStartOfLine();
-        } else {
-            g.movetype  = MoveLineWise;
-            g.rangemode = RangeLineMode;
-            setAnchor();
-            setPosition(firstPositionInLine(n, false));
-        }
-        setTargetColumn();
-        updateScrollOffset();
-    } else if (input.is('h') || input.isKey(Key_Left) || input.isBackspace()) {
-        g.movetype = MoveExclusive;
-        int n      = qMin(count, leftDist());
-        moveLeft(n);
-    } else if (input.is('H')) {
-        const CursorPosition pos(lineToBlockNumber(lineOnTop(count)), 0);
-        setCursorPosition(&m_cursor, pos);
-        handleStartOfLine();
-    } else if (input.is('j') || input.isKey(Key_Down) || input.isControl('j') ||
-               input.isControl('n')) {
-        moveVertically(count);
-    } else if (input.is('k') || input.isKey(Key_Up) || input.isControl('p')) {
-        moveVertically(-count);
-    } else if (input.is('l') || input.isKey(Key_Right) || input.is(' ')) {
-        g.movetype = MoveExclusive;
-        moveRight(qMax(0, qMin(count, rightDist() - (g.submode == NoSubMode))));
-    } else if (input.is('L')) {
-        const CursorPosition pos(lineToBlockNumber(lineOnBottom(count)), 0);
-        setCursorPosition(&m_cursor, pos);
-        handleStartOfLine();
-    } else if (g.gflag && input.is('m')) {
-        const QPoint pos(EDITOR(viewport()->width()) / 2, EDITOR(cursorRect(m_cursor)).y());
-        QTextCursor tc = EDITOR(cursorForPosition(pos));
-        if (!tc.isNull()) {
-            m_cursor = tc;
-            setTargetColumn();
-        }
-    } else if (input.is('M')) {
-        m_cursor = EDITOR(cursorForPosition(QPoint(0, EDITOR(height()) / 2)));
-        handleStartOfLine();
-    } else if (input.is('n') || input.is('N')) {
-        if (s.useCoreSearch.value()) {
-            bool forward = (input.is('n')) ? g.lastSearchForward : !g.lastSearchForward;
-            int pos      = position();
-            q->findNextRequested(!forward);
-            if (forward && pos == m_cursor.selectionStart()) {
-                // if cursor is already positioned at the start of a find result, this is returned
-                q->findNextRequested(false);
-            }
-            setPosition(m_cursor.selectionStart());
-        } else {
-            handled = searchNext(input.is('n'));
-        }
-    } else if (input.is('t')) {
-        g.movetype   = MoveInclusive;
-        g.subsubmode = FtSubSubMode;
-        g.subsubdata = input;
-    } else if (input.is('T')) {
-        g.movetype   = MoveExclusive;
-        g.subsubmode = FtSubSubMode;
-        g.subsubdata = input;
-    } else if (input.is('w') || input.is('W') || input.isShift(Key_Right) ||
-               input.isControl(Key_Right)) { // tested
-        // Special case: "cw" and "cW" work the same as "ce" and "cE" if the
-        // cursor is on a non-blank - except if the cursor is on the last
-        // character of a word: only the current word will be changed
-        bool simple = input.is('W') || input.isControl(Key_Right);
-        if (g.submode == ChangeSubMode && !characterAtCursor().isSpace()) {
-            moveToWordEnd(count, simple, true);
-        } else {
-            moveToNextWordStart(count, simple, true);
-            // Command 'dw' deletes to the next word on the same line or to end of line.
-            if (g.submode == DeleteSubMode && count == 1) {
-                const QTextBlock currentBlock = blockAt(anchor());
-                setPosition(qMin(position(), currentBlock.position() + currentBlock.length()));
-            }
-        }
-    } else if (input.is('z')) {
-        g.movetype   = MoveLineWise;
-        g.subsubmode = ZSubSubMode;
-    } else if (input.is('[')) {
-        g.subsubmode = OpenSquareSubSubMode;
-    } else if (input.is(']')) {
-        g.subsubmode = CloseSquareSubSubMode;
-    } else if (input.isKey(Key_PageDown) || input.isControl('f')) {
-        movePageDown(count);
-        handleStartOfLine();
-    } else if (input.isKey(Key_PageUp) || input.isControl('b')) {
-        movePageUp(count);
-        handleStartOfLine();
-    } else {
-        handled = false;
-    }
-
-    if (handled && g.subsubmode == NoSubSubMode) {
-        if (g.submode == NoSubMode) {
-            leaveCurrentMode();
-        } else {
-            // finish movement for sub modes
-            const QString dotMovement = (count > 1 ? QString::number(count) : QString()) +
-                                        QLatin1String(g.gflag ? "g" : "") + input.toString();
-            finishMovement(dotMovement);
-            setTargetColumn();
-        }
-    }
-
-    return handled;
-}
-
-EventResult
-FakeVimHandler::Private::handleCommandMode(const Input &input)
-{
-    bool handled = false;
-
-    bool clearGflag    = g.gflag;
-    bool clearRegister = g.submode != RegisterSubMode;
-    bool clearCount    = g.submode != RegisterSubMode && !isInputCount(input);
-
-    // Process input for a sub-mode.
-    if (input.isEscape()) {
-        handled = handleEscape();
-    } else if (m_wasReadOnly) {
-        return EventUnhandled;
-    } else if (g.subsubmode != NoSubSubMode) {
-        handled = handleCommandSubSubMode(input);
-    } else if (g.submode == NoSubMode) {
-        handled = handleNoSubMode(input);
-    } else if (g.submode == ExchangeSubMode) {
-        handled = handleExchangeSubMode(input);
-    } else if (g.submode == ChangeSubMode && input.is('x') && s.emulateExchange.value()) {
-        // Exchange submode is "cx", so we need to switch over from ChangeSubMode here
-        g.submode = ExchangeSubMode;
-        handled   = true;
-    } else if (g.submode == DeleteSurroundingSubMode || g.submode == ChangeSurroundingSubMode) {
-        handled = handleDeleteChangeSurroundingSubMode(input);
-    } else if (g.submode == AddSurroundingSubMode) {
-        handled = handleAddSurroundingSubMode(input);
-    } else if (g.submode == ChangeSubMode && (input.is('s') || input.is('S')) &&
-               s.emulateSurround.value()) {
-        g.submode            = ChangeSurroundingSubMode;
-        g.surroundUpperCaseS = input.is('S');
-        handled              = true;
-    } else if (g.submode == DeleteSubMode && input.is('s') && s.emulateSurround.value()) {
-        g.submode = DeleteSurroundingSubMode;
-        handled   = true;
-    } else if (g.submode == YankSubMode && (input.is('s') || input.is('S')) &&
-               s.emulateSurround.value()) {
-        g.submode            = AddSurroundingSubMode;
-        g.movetype           = MoveInclusive;
-        g.surroundUpperCaseS = input.is('S');
-        handled              = true;
-    } else if (g.submode == ChangeSubMode || g.submode == DeleteSubMode ||
-               g.submode == YankSubMode) {
-        handled = handleChangeDeleteYankSubModes(input);
-    } else if (g.submode == CommentSubMode && s.emulateVimCommentary.value()) {
-        handled = handleCommentSubMode(input);
-    } else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister.value()) {
-        handled = handleReplaceWithRegisterSubMode(input);
-    } else if (g.submode == ReplaceSubMode) {
-        handled = handleReplaceSubMode(input);
-    } else if (g.submode == FilterSubMode) {
-        handled = handleFilterSubMode(input);
-    } else if (g.submode == RegisterSubMode) {
-        handled = handleRegisterSubMode(input);
-    } else if (g.submode == WindowSubMode) {
-        handled = handleWindowSubMode(input);
-    } else if (g.submode == ZSubMode) {
-        handled = handleZSubMode(input);
-    } else if (g.submode == CapitalZSubMode) {
-        handled = handleCapitalZSubMode(input);
-    } else if (g.submode == MacroRecordSubMode) {
-        handled = handleMacroRecordSubMode(input);
-    } else if (g.submode == MacroExecuteSubMode) {
-        handled = handleMacroExecuteSubMode(input);
-    } else if (g.submode == ShiftLeftSubMode || g.submode == ShiftRightSubMode ||
-               g.submode == IndentSubMode) {
-        handled = handleShiftSubMode(input);
-    } else if (g.submode == InvertCaseSubMode || g.submode == DownCaseSubMode ||
-               g.submode == UpCaseSubMode) {
-        handled = handleChangeCaseSubMode(input);
-    }
-
-    if (!handled && isOperatorPending())
-        handled = handleMovement(input);
-
-    // Clear state and display incomplete command if necessary.
-    if (handled) {
-        bool noMode =
-            (g.mode == CommandMode && g.submode == NoSubMode && g.subsubmode == NoSubSubMode);
-        clearCount = clearCount && noMode && !g.gflag;
-        if (clearCount && clearRegister) {
-            leaveCurrentMode();
-        } else {
-            // Use gflag only for next input.
-            if (clearGflag)
-                g.gflag = false;
-            // Clear [count] and [register] if its no longer needed.
-            if (clearCount)
-                resetCount();
-            // Show or clear current command on minibuffer (showcmd).
-            if (input.isEscape() || g.mode != CommandMode || clearCount)
-                g.currentCommand.clear();
-            else
-                g.currentCommand.append(input.toString());
-        }
-
-        saveLastVisualMode();
-    } else {
-        leaveCurrentMode();
-        // if a key which produces text was pressed, don't mark it as unhandled
-        // - otherwise the text would be inserted while being in command mode
-        if (input.text().isEmpty())
-            handled = false;
-    }
-
-    m_positionPastEnd = (m_visualTargetColumn == -1) && isVisualMode() && !atEmptyLine();
-
-    return handled ? EventHandled : EventCancelled;
-}
-
-bool
-FakeVimHandler::Private::handleEscape()
-{
-    if (isVisualMode())
-        leaveVisualMode();
-    leaveCurrentMode();
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleNoSubMode(const Input &input)
-{
-    bool handled = true;
-
-    const int oldRevision = revision();
-    QString dotCommand    = visualDotCommand() + QLatin1String(g.gflag ? "g" : "") +
-                         QString::number(count()) + input.toString();
-
-    if (input.is('&')) {
-        handleExCommand(QLatin1String(g.gflag ? "%s//~/&" : "s"));
-    } else if (input.is(':')) {
-        enterExMode();
-    } else if (input.is('!') && isNoVisualMode()) {
-        g.submode = FilterSubMode;
-    } else if (input.is('!') && isVisualMode()) {
-        enterExMode(QString("!"));
-    } else if (input.is('"')) {
-        g.submode = RegisterSubMode;
-    } else if (input.is(',')) {
-        passShortcuts(true);
-    } else if (input.is('.')) {
-        dotCommand.clear();
-        QString savedCommand = g.dotCommand;
-        g.dotCommand.clear();
-        beginLargeEditBlock();
-        replay(savedCommand);
-        endEditBlock();
-        leaveCurrentMode();
-        g.dotCommand = savedCommand;
-    } else if (input.is('<') || input.is('>') || input.is('=')) {
-        g.submode = indentModeFromInput(input);
-        if (isVisualMode()) {
-            leaveVisualMode();
-            const int repeat = count();
-            if (g.submode == ShiftLeftSubMode)
-                shiftRegionLeft(repeat);
-            else if (g.submode == ShiftRightSubMode)
-                shiftRegionRight(repeat);
-            else
-                indentSelectedText();
-            g.submode = NoSubMode;
-        } else {
-            setAnchor();
-        }
-    } else if ((!isVisualMode() && input.is('a')) || (isVisualMode() && input.is('A'))) {
-        if (isVisualMode()) {
-            if (!isVisualBlockMode())
-                dotCommand = QString::number(count()) + "a";
-            enterVisualInsertMode('A');
-        } else {
-            moveRight(qMin(rightDist(), 1));
-            breakEditBlock();
-            enterInsertMode();
-        }
-    } else if (input.is('A')) {
-        breakEditBlock();
-        moveBehindEndOfLine();
-        setAnchor();
-        enterInsertMode();
-        setTargetColumn();
-    } else if (input.isControl('a')) {
-        changeNumberTextObject(count());
-    } else if (g.gflag && input.is('c') && s.emulateVimCommentary.value()) {
-        if (isVisualMode()) {
-            pushUndoState();
-
-            QTextCursor start(m_cursor);
-            QTextCursor end(start);
-            end.setPosition(end.anchor());
-
-            const int count = qAbs(start.blockNumber() - end.blockNumber());
-            if (count == 0) {
-                dotCommand = "gcc";
-            } else {
-                dotCommand = QString("gc%1j").arg(count);
-            }
-
-            leaveVisualMode();
-            toggleComment(currentRange());
-
-            g.submode = NoSubMode;
-        } else {
-            g.movetype = MoveLineWise;
-            g.submode  = CommentSubMode;
-            pushUndoState();
-            setAnchor();
-        }
-    } else if (g.gflag && input.is('r') && s.emulateReplaceWithRegister.value()) {
-        g.submode = ReplaceWithRegisterSubMode;
-        if (isVisualMode()) {
-            dotCommand = visualDotCommand() + QString::number(count()) + "gr";
-            pasteText(true);
-        } else {
-            setAnchor();
-        }
-    } else if ((input.is('c') || input.is('d') || input.is('y')) && isNoVisualMode()) {
-        setAnchor();
-        g.opcount   = g.mvcount;
-        g.mvcount   = 0;
-        g.rangemode = RangeCharMode;
-        g.movetype  = MoveExclusive;
-        g.submode   = changeDeleteYankModeFromInput(input);
-    } else if ((input.is('c') || input.is('C') || input.is('s') || input.is('R')) &&
-               (isVisualCharMode() || isVisualLineMode())) {
-        leaveVisualMode();
-        g.submode = ChangeSubMode;
-        finishMovement();
-    } else if ((input.is('c') || input.is('s')) && isVisualBlockMode()) {
-        resetCount();
-        enterVisualInsertMode(input.asChar());
-    } else if (input.is('C')) {
-        handleAs("%1c$");
-    } else if (input.isControl('c')) {
-        if (isNoVisualMode()) {
-#if defined(Q_OS_MACOS)
-            showMessage(MessageInfo,
-                        Tr::tr("Type Meta-Shift-Y, Meta-Shift-Y to quit FakeVim mode."));
-#else
-            showMessage(MessageInfo, Tr::tr("Type Alt-Y, Alt-Y to quit FakeVim mode."));
-#endif
-        } else {
-            leaveVisualMode();
-        }
-    } else if ((input.is('d') || input.is('x') || input.isKey(Key_Delete)) && isVisualMode()) {
-        cutSelectedText();
-    } else if (input.is('D') && isNoVisualMode()) {
-        handleAs("%1d$");
-    } else if ((input.is('D') || input.is('X')) && isVisualMode()) {
-        if (isVisualCharMode())
-            toggleVisualMode(VisualLineMode);
-        if (isVisualBlockMode() && input.is('D'))
-            m_visualTargetColumn = -1;
-        cutSelectedText();
-    } else if (input.isControl('d')) {
-        const int scrollOffset = windowScrollOffset();
-        int sline              = cursorLine() < scrollOffset ? scrollOffset : cursorLineOnScreen();
-        // FIXME: this should use the "scroll" option, and "count"
-        moveDown(linesOnScreen() / 2);
-        handleStartOfLine();
-        scrollToLine(cursorLine() - sline);
-    } else if (!g.gflag && input.is('g')) {
-        g.gflag = true;
-    } else if (!isVisualMode() && (input.is('i') || input.isKey(Key_Insert))) {
-        breakEditBlock();
-        enterInsertMode();
-        if (atEndOfLine())
-            moveLeft();
-    } else if (input.is('I')) {
-        if (isVisualMode()) {
-            if (!isVisualBlockMode())
-                dotCommand = QString::number(count()) + "i";
-            enterVisualInsertMode('I');
-        } else {
-            if (g.gflag)
-                moveToStartOfLine();
-            else
-                moveToFirstNonBlankOnLine();
-            breakEditBlock();
-            enterInsertMode();
-        }
-    } else if (input.isControl('i')) {
-        jump(count());
-    } else if (input.is('J')) {
-        pushUndoState();
-        moveBehindEndOfLine();
-        beginEditBlock();
-        if (g.submode == NoSubMode)
-            joinLines(count(), g.gflag);
-        endEditBlock();
-    } else if (input.isControl('l')) {
-        // screen redraw. should not be needed
-    } else if (!g.gflag && input.is('m')) {
-        g.subsubmode = MarkSubSubMode;
-    } else if (isVisualMode() && (input.is('o') || input.is('O'))) {
-        int pos = position();
-        setAnchorAndPosition(pos, anchor());
-        std::swap(m_positionPastEnd, m_anchorPastEnd);
-        setTargetColumn();
-        if (m_positionPastEnd)
-            m_visualTargetColumn = -1;
-    } else if (input.is('o') || input.is('O')) {
-        bool insertAfter = input.is('o');
-        pushUndoState();
-
-        // Prepend line only if on the first line and command is 'O'.
-        bool appendLine = true;
-        if (!insertAfter) {
-            if (block().blockNumber() == 0)
-                appendLine = false;
-            else
-                moveUp();
-        }
-        const int line = lineNumber(block());
-
-        beginEditBlock();
-        enterInsertMode();
-        setPosition(appendLine ? lastPositionInLine(line) : firstPositionInLine(line));
-        clearLastInsertion();
-        setAnchor();
-        insertNewLine();
-        if (appendLine) {
-            m_buffer->insertState.newLineBefore = true;
-        } else {
-            moveUp();
-            m_buffer->insertState.pos1         = position();
-            m_buffer->insertState.newLineAfter = true;
-        }
-        setTargetColumn();
-        endEditBlock();
-
-        // Close accidentally opened block.
-        if (block().blockNumber() > 0) {
-            moveUp();
-            if (line != lineNumber(block()))
-                q->fold(1, true);
-            moveDown();
-        }
-    } else if (input.isControl('o')) {
-        jump(-count());
-    } else if (input.is('p') || input.is('P') || input.isShift(Qt::Key_Insert)) {
-        dotCommand = QString("\"%1%2%3").arg(QChar(m_register)).arg(count()).arg(input.asChar());
-
-        pasteText(!input.is('P'));
-        setTargetColumn();
-        finishMovement();
-    } else if (input.is('q')) {
-        if (g.isRecording) {
-            // Stop recording.
-            stopRecording();
-        } else {
-            // Recording shouldn't work in mapping or while executing register.
-            handled = g.mapStates.empty();
-            if (handled)
-                g.submode = MacroRecordSubMode;
-        }
-    } else if (input.is('r')) {
-        g.submode = ReplaceSubMode;
-    } else if (!isVisualMode() && input.is('R')) {
-        pushUndoState();
-        breakEditBlock();
-        enterReplaceMode();
-    } else if (input.isControl('r')) {
-        dotCommand.clear();
-        int repeat = count();
-        while (--repeat >= 0)
-            redo();
-    } else if (input.is('S') && isVisualMode() && s.emulateSurround.value()) {
-        g.submode    = AddSurroundingSubMode;
-        g.subsubmode = SurroundSubSubMode;
-    } else if (input.is('s')) {
-        handleAs("c%1l");
-    } else if (input.is('S')) {
-        handleAs("%1cc");
-    } else if (g.gflag && input.is('t')) {
-        handleExCommand("tabnext");
-    } else if (g.gflag && input.is('T')) {
-        handleExCommand("tabprevious");
-    } else if (input.isControl('t')) {
-        handleExCommand("pop");
-    } else if (!g.gflag && input.is('u') && !isVisualMode()) {
-        dotCommand.clear();
-        int repeat = count();
-        while (--repeat >= 0)
-            undo();
-    } else if (input.isControl('u')) {
-        int sline = cursorLineOnScreen();
-        // FIXME: this should use the "scroll" option, and "count"
-        moveUp(linesOnScreen() / 2);
-        handleStartOfLine();
-        scrollToLine(cursorLine() - sline);
-    } else if (g.gflag && input.is('v')) {
-        if (isNoVisualMode()) {
-            CursorPosition from = markLessPosition();
-            CursorPosition to   = markGreaterPosition();
-            if (m_buffer->lastVisualModeInverted)
-                std::swap(from, to);
-            toggleVisualMode(m_buffer->lastVisualMode);
-            setCursorPosition(from);
-            setAnchor();
-            setCursorPosition(to);
-            setTargetColumn();
-        }
-    } else if (input.is('v')) {
-        toggleVisualMode(VisualCharMode);
-    } else if (input.is('V')) {
-        toggleVisualMode(VisualLineMode);
-    } else if (input.isControl('v')) {
-        toggleVisualMode(VisualBlockMode);
-    } else if (input.isControl('w')) {
-        g.submode = WindowSubMode;
-    } else if (input.is('x') && isNoVisualMode()) {
-        handleAs("%1dl");
-    } else if (input.isControl('x')) {
-        changeNumberTextObject(-count());
-    } else if (input.is('X')) {
-        handleAs("%1dh");
-    } else if (input.is('Y') && isNoVisualMode()) {
-        handleAs("%1yy");
-    } else if (input.isControl('y')) {
-        // FIXME: this should use the "scroll" option, and "count"
-        if (cursorLineOnScreen() == linesOnScreen() - 1)
-            moveUp(1);
-        scrollUp(1);
-    } else if (input.is('y') && isVisualCharMode()) {
-        g.rangemode = RangeCharMode;
-        g.movetype  = MoveInclusive;
-        g.submode   = YankSubMode;
-        finishMovement();
-    } else if ((input.is('y') && isVisualLineMode()) || (input.is('Y') && isVisualLineMode()) ||
-               (input.is('Y') && isVisualCharMode())) {
-        g.rangemode = RangeLineMode;
-        g.movetype  = MoveLineWise;
-        g.submode   = YankSubMode;
-        finishMovement();
-    } else if ((input.is('y') || input.is('Y')) && isVisualBlockMode()) {
-        g.rangemode = RangeBlockMode;
-        g.movetype  = MoveInclusive;
-        g.submode   = YankSubMode;
-        finishMovement();
-    } else if (input.is('z')) {
-        g.submode = ZSubMode;
-    } else if (input.is('Z')) {
-        g.submode = CapitalZSubMode;
-    } else if ((input.is('~') || input.is('u') || input.is('U'))) {
-        g.movetype = MoveExclusive;
-        g.submode  = letterCaseModeFromInput(input);
-        pushUndoState();
-        if (isVisualMode()) {
-            leaveVisualMode();
-            finishMovement();
-        } else if (g.gflag || (g.submode == InvertCaseSubMode && s.tildeOp.value())) {
-            if (atEndOfLine())
-                moveLeft();
-            setAnchor();
-        } else {
-            const QString movementCommand = QString("%1l%1l").arg(count());
-            handleAs("g" + input.toString() + movementCommand);
-        }
-    } else if (input.is('@')) {
-        g.submode = MacroExecuteSubMode;
-    } else if (input.isKey(Key_Delete)) {
-        setAnchor();
-        moveRight(qMin(1, rightDist()));
-        removeText(currentRange());
-        if (atEndOfLine())
-            moveLeft();
-    } else if (input.isControl(Key_BracketRight)) {
-        handleExCommand("tag");
-    } else if (handleMovement(input)) {
-        // movement handled
-        dotCommand.clear();
-    } else {
-        handled = false;
-    }
-
-    // Set dot command if the current input changed document or entered insert mode.
-    if (handled && !dotCommand.isEmpty() && (oldRevision != revision() || isInsertMode()))
-        setDotCommand(dotCommand);
-
-    return handled;
-}
-
-bool
-FakeVimHandler::Private::handleChangeDeleteYankSubModes(const Input &input)
-{
-    if (g.submode != changeDeleteYankModeFromInput(input))
-        return false;
-
-    handleChangeDeleteYankSubModes();
-
-    return true;
-}
-
-void
-FakeVimHandler::Private::handleChangeDeleteYankSubModes()
-{
-    g.movetype = MoveLineWise;
-
-    const QString dotCommand = dotCommandFromSubMode(g.submode);
-
-    if (!dotCommand.isEmpty())
-        pushUndoState();
-
-    const int anc = firstPositionInLine(cursorLine() + 1);
-    moveDown(count() - 1);
-    const int pos = lastPositionInLine(cursorLine() + 1);
-    setAnchorAndPosition(anc, pos);
-
-    if (!dotCommand.isEmpty())
-        setDotCommand(QString("%2%1%1").arg(dotCommand), count());
-
-    finishMovement();
-
-    g.submode = NoSubMode;
-}
-
-bool
-FakeVimHandler::Private::handleReplaceSubMode(const Input &input)
-{
-    bool handled = true;
-
-    const QChar c = input.asChar();
-    setDotCommand(visualDotCommand() + 'r' + c);
-    if (isVisualMode()) {
-        pushUndoState();
-        leaveVisualMode();
-        Range range = currentRange();
-        if (g.rangemode == RangeCharMode)
-            ++range.endPos;
-        // Replace each character but preserve lines.
-        transformText(range, [&c](const QString &text) {
-            return QString(text).replace(QRegularExpression("[^\\n]"), c);
-        });
-    } else if (count() <= rightDist()) {
-        pushUndoState();
-        setAnchor();
-        moveRight(count());
-        Range range = currentRange();
-        if (input.isReturn()) {
-            beginEditBlock();
-            replaceText(range, QString());
-            insertText(QString("\n"));
-            endEditBlock();
-        } else {
-            replaceText(range, QString(count(), c));
-            moveRight(count() - 1);
-        }
-        setTargetColumn();
-        setDotCommand("%1r" + input.text(), count());
-    } else {
-        handled = false;
-    }
-    g.submode = NoSubMode;
-    finishMovement();
-
-    return handled;
-}
-
-bool
-FakeVimHandler::Private::handleCommentSubMode(const Input &input)
-{
-    if (!input.is('c'))
-        return false;
-
-    g.movetype = MoveLineWise;
-
-    const int anc = firstPositionInLine(cursorLine() + 1);
-    moveDown(count() - 1);
-    const int pos = lastPositionInLine(cursorLine() + 1);
-    setAnchorAndPosition(anc, pos);
-
-    setDotCommand(QString("%1gcc").arg(count()));
-
-    finishMovement();
-
-    g.submode = NoSubMode;
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleReplaceWithRegisterSubMode(const Input &input)
-{
-    if (!input.is('r'))
-        return false;
-
-    pushUndoState(false);
-    beginEditBlock();
-
-    const QString movement = (count() == 1) ? QString() : (QString::number(count() - 1) + "j");
-
-    g.dotCommand = "V" + movement + "gr";
-    replay(g.dotCommand);
-
-    endEditBlock();
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExchangeSubMode(const Input &input)
-{
-    if (input.is('c')) { // cxc
-        g.exchangeRange.reset();
-        g.submode = NoSubMode;
-        return true;
-    }
-
-    if (input.is('x')) { // cxx
-        setAnchorAndPosition(firstPositionInLine(cursorLine() + 1),
-                             lastPositionInLine(cursorLine() + 1) + 1);
-
-        setDotCommand("cxx");
-
-        finishMovement();
-
-        g.submode = NoSubMode;
-
-        return true;
-    }
-
-    return false;
-}
-
-bool
-FakeVimHandler::Private::handleDeleteChangeSurroundingSubMode(const Input &input)
-{
-    if (g.submode != ChangeSurroundingSubMode && g.submode != DeleteSurroundingSubMode)
-        return false;
-
-    bool handled = false;
-
-    if (input.is('(') || input.is(')') || input.is('b')) {
-        handled = selectBlockTextObject(false, '(', ')');
-    } else if (input.is('{') || input.is('}') || input.is('B')) {
-        handled = selectBlockTextObject(false, '{', '}');
-    } else if (input.is('[') || input.is(']')) {
-        handled = selectBlockTextObject(false, '[', ']');
-    } else if (input.is('<') || input.is('>') || input.is('t')) {
-        handled = selectBlockTextObject(false, '<', '>');
-    } else if (input.is('"') || input.is('\'') || input.is('`')) {
-        handled = selectQuotedStringTextObject(false, input.asChar());
-    }
-
-    if (handled) {
-        if (g.submode == DeleteSurroundingSubMode) {
-            pushUndoState(false);
-            beginEditBlock();
-
-            // Surround is always one character, so just delete the first and last one
-            transformText(currentRange(),
-                          [](const QString &text) { return text.mid(1, text.size() - 2); });
-
-            endEditBlock();
-            clearCurrentMode();
-
-            g.dotCommand = QStringLiteral("ds") + input.asChar();
-        } else if (g.submode == ChangeSurroundingSubMode) {
-            g.subsubmode = SurroundSubSubMode;
-        }
-    }
-
-    return handled;
-}
-
-bool
-FakeVimHandler::Private::handleAddSurroundingSubMode(const Input &input)
-{
-    if (!input.is('s'))
-        return false;
-
-    g.subsubmode = SurroundSubSubMode;
-
-    int anc       = firstPositionInLine(cursorLine() + 1);
-    const int pos = lastPositionInLine(cursorLine() + 1);
-
-    // Ignore leading spaces
-    while ((characterAt(anc) == ' ' || characterAt(anc) == '\t') && anc != pos) {
-        anc++;
-    }
-
-    setAnchorAndPosition(anc, pos);
-
-    finishMovement("s");
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleFilterSubMode(const Input &)
-{
-    return false;
-}
-
-bool
-FakeVimHandler::Private::handleRegisterSubMode(const Input &input)
-{
-    bool handled = false;
-
-    QChar reg = input.asChar();
-    if (QString("*+.%#:-\"_").contains(reg) || reg.isLetterOrNumber()) {
-        m_register = reg.unicode();
-        handled    = true;
-    }
-    g.submode = NoSubMode;
-
-    return handled;
-}
-
-bool
-FakeVimHandler::Private::handleShiftSubMode(const Input &input)
-{
-    if (g.submode != indentModeFromInput(input))
-        return false;
-
-    g.movetype = MoveLineWise;
-    pushUndoState();
-    moveDown(count() - 1);
-    setDotCommand(QString("%2%1%1").arg(input.asChar()), count());
-    finishMovement();
-    g.submode = NoSubMode;
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleChangeCaseSubMode(const Input &input)
-{
-    if (g.submode != letterCaseModeFromInput(input))
-        return false;
-
-    if (!isFirstNonBlankOnLine(position())) {
-        moveToStartOfLine();
-        moveToFirstNonBlankOnLine();
-    }
-    setTargetColumn();
-    pushUndoState();
-    setAnchor();
-    setPosition(lastPositionInLine(cursorLine() + count()) + 1);
-    finishMovement(QString("%1%2").arg(count()).arg(input.raw()));
-    g.submode = NoSubMode;
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleWindowSubMode(const Input &input)
-{
-    if (handleCount(input))
-        return true;
-
-    leaveVisualMode();
-    leaveCurrentMode();
-    q->windowCommandRequested(input.toString(), count());
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleZSubMode(const Input &input)
-{
-    bool handled         = true;
-    bool foldMaybeClosed = false;
-    if (input.isReturn() || input.is('t') || input.is('-') || input.is('b') || input.is('.') ||
-        input.is('z')) {
-        // Cursor line to top/center/bottom of window.
-        Qt::AlignmentFlag align;
-        if (input.isReturn() || input.is('t'))
-            align = Qt::AlignTop;
-        else if (input.is('.') || input.is('z'))
-            align = Qt::AlignVCenter;
-        else
-            align = Qt::AlignBottom;
-        const bool moveToNonBlank = (input.is('.') || input.isReturn() || input.is('-'));
-        const int line            = g.mvcount == 0 ? -1 : firstPositionInLine(count());
-        alignViewportToCursor(align, line, moveToNonBlank);
-    } else if (input.is('o') || input.is('c')) {
-        // Open/close current fold.
-        foldMaybeClosed = input.is('c');
-        q->fold(count(), foldMaybeClosed);
-    } else if (input.is('O') || input.is('C')) {
-        // Recursively open/close current fold.
-        foldMaybeClosed = input.is('C');
-        q->fold(-1, foldMaybeClosed);
-    } else if (input.is('a') || input.is('A')) {
-        // Toggle current fold.
-        foldMaybeClosed = true;
-        q->foldToggle(input.is('a') ? count() : -1);
-    } else if (input.is('R') || input.is('M')) {
-        // Open/close all folds in document.
-        foldMaybeClosed = input.is('M');
-        q->foldAll(foldMaybeClosed);
-    } else if (input.is('j') || input.is('k')) {
-        q->foldGoTo(input.is('j') ? count() : -count(), false);
-    } else {
-        handled = false;
-    }
-    if (foldMaybeClosed)
-        ensureCursorVisible();
-    g.submode = NoSubMode;
-    return handled;
-}
-
-bool
-FakeVimHandler::Private::handleCapitalZSubMode(const Input &input)
-{
-    // Recognize ZZ and ZQ as aliases for ":x" and ":q!".
-    bool handled = true;
-    if (input.is('Z'))
-        handleExCommand("x");
-    else if (input.is('Q'))
-        handleExCommand("q!");
-    else
-        handled = false;
-    g.submode = NoSubMode;
-    return handled;
-}
-
-bool
-FakeVimHandler::Private::handleMacroRecordSubMode(const Input &input)
-{
-    g.submode = NoSubMode;
-    return startRecording(input);
-}
-
-bool
-FakeVimHandler::Private::handleMacroExecuteSubMode(const Input &input)
-{
-    g.submode = NoSubMode;
-
-    bool result = true;
-    int repeat  = count();
-    while (result && --repeat >= 0)
-        result = executeRegister(input.asChar().unicode());
-
-    return result;
-}
-
-EventResult
-FakeVimHandler::Private::handleInsertOrReplaceMode(const Input &input)
-{
-    if (position() < m_buffer->insertState.pos1 || position() > m_buffer->insertState.pos2) {
-        commitInsertState();
-        invalidateInsertState();
-    }
-
-    if (g.mode == InsertMode)
-        handleInsertMode(input);
-    else
-        handleReplaceMode(input);
-
-    if (!m_textedit && !m_plaintextedit)
-        return EventHandled;
-
-    if (!isInsertMode() || m_buffer->breakEditBlock || position() < m_buffer->insertState.pos1 ||
-        position() > m_buffer->insertState.pos2) {
-        commitInsertState();
-        invalidateInsertState();
-        breakEditBlock();
-        m_visualBlockInsert = NoneBlockInsertMode;
-    }
-
-    // We don't want fancy stuff in insert mode.
-    return EventHandled;
-}
-
-void
-FakeVimHandler::Private::handleReplaceMode(const Input &input)
-{
-    if (input.isEscape()) {
-        commitInsertState();
-        moveLeft(qMin(1, leftDist()));
-        enterCommandMode();
-        g.dotCommand.append(m_buffer->lastInsertion + "<ESC>");
-    } else if (input.isKey(Key_Left)) {
-        moveLeft();
-    } else if (input.isKey(Key_Right)) {
-        moveRight();
-    } else if (input.isKey(Key_Up)) {
-        moveUp();
-    } else if (input.isKey(Key_Down)) {
-        moveDown();
-    } else if (input.isKey(Key_Insert)) {
-        g.mode = InsertMode;
-    } else if (input.isControl('o')) {
-        enterCommandMode(ReplaceMode);
-    } else {
-        joinPreviousEditBlock();
-        if (!atEndOfLine()) {
-            setAnchor();
-            moveRight();
-            removeText(currentRange());
-        }
-        const QString text = input.text();
-        setAnchor();
-        insertText(text);
-        setTargetColumn();
-        endEditBlock();
-    }
-}
-
-void
-FakeVimHandler::Private::finishInsertMode()
-{
-    bool newLineAfter  = m_buffer->insertState.newLineAfter;
-    bool newLineBefore = m_buffer->insertState.newLineBefore;
-
-    // Repeat insertion [count] times.
-    // One instance was already physically inserted while typing.
-    if (!m_buffer->breakEditBlock && isInsertStateValid()) {
-        commitInsertState();
-
-        QString text             = m_buffer->lastInsertion;
-        const QString dotCommand = g.dotCommand;
-        const int repeat         = count() - 1;
-        m_buffer->lastInsertion.clear();
-        joinPreviousEditBlock();
-
-        if (newLineAfter) {
-            text.chop(1);
-            text.prepend("<END>\n");
-        } else if (newLineBefore) {
-            text.prepend("<END>");
-        }
-
-        replay(text, repeat);
-
-        if (m_visualBlockInsert != NoneBlockInsertMode && !text.contains('\n')) {
-            const CursorPosition lastAnchor   = markLessPosition();
-            const CursorPosition lastPosition = markGreaterPosition();
-            const bool change                 = m_visualBlockInsert == ChangeBlockInsertMode;
-            const int insertColumn = (m_visualBlockInsert == InsertBlockInsertMode || change)
-                                         ? qMin(lastPosition.column, lastAnchor.column)
-                                         : qMax(lastPosition.column, lastAnchor.column) + 1;
-
-            CursorPosition pos(lastAnchor.line, insertColumn);
-
-            if (change)
-                pos.column = columnAt(m_buffer->insertState.pos1);
-
-            // Cursor position after block insert is on the first selected line,
-            // last selected column for 's' command, otherwise first selected column.
-            const int endColumn = change ? qMax(0, m_cursor.positionInBlock() - 1)
-                                         : qMin(lastPosition.column, lastAnchor.column);
-
-            while (pos.line < lastPosition.line) {
-                ++pos.line;
-                setCursorPosition(&m_cursor, pos);
-                if (m_visualBlockInsert == AppendToEndOfLineBlockInsertMode) {
-                    moveToEndOfLine();
-                } else if (m_visualBlockInsert == AppendBlockInsertMode) {
-                    // Prepend spaces if necessary.
-                    int spaces = pos.column - m_cursor.positionInBlock();
-                    if (spaces > 0) {
-                        setAnchor();
-                        m_cursor.insertText(QString(" ").repeated(spaces));
-                    }
-                } else if (m_cursor.positionInBlock() != pos.column) {
-                    continue;
-                }
-                replay(text, repeat + 1);
-            }
-
-            setCursorPosition(CursorPosition(lastAnchor.line, endColumn));
-        } else {
-            moveLeft(qMin(1, leftDist()));
-        }
-
-        endEditBlock();
-        breakEditBlock();
-
-        m_buffer->lastInsertion = text;
-        g.dotCommand            = dotCommand;
-    } else {
-        moveLeft(qMin(1, leftDist()));
-    }
-
-    if (newLineBefore || newLineAfter)
-        m_buffer->lastInsertion.remove(0, m_buffer->lastInsertion.indexOf('\n') + 1);
-    g.dotCommand.append(m_buffer->lastInsertion + "<ESC>");
-
-    setTargetColumn();
-    enterCommandMode();
-}
-
-void
-FakeVimHandler::Private::handleInsertMode(const Input &input)
-{
-    if (input.isEscape()) {
-        if (g.submode == CtrlRSubMode || g.submode == CtrlVSubMode) {
-            g.submode    = NoSubMode;
-            g.subsubmode = NoSubSubMode;
-            updateMiniBuffer();
-        } else {
-            finishInsertMode();
-        }
-    } else if (g.submode == CtrlRSubMode) {
-        m_cursor.insertText(registerContents(input.asChar().unicode()));
-        g.submode = NoSubMode;
-    } else if (g.submode == CtrlVSubMode) {
-        if (g.subsubmode == NoSubSubMode) {
-            g.subsubmode       = CtrlVUnicodeSubSubMode;
-            m_ctrlVAccumulator = 0;
-            if (input.is('x') || input.is('X')) {
-                // ^VXnn or ^Vxnn with 00 <= nn <= FF
-                // BMP Unicode codepoints ^Vunnnn with 0000 <= nnnn <= FFFF
-                // any Unicode codepoint ^VUnnnnnnnn with 00000000 <= nnnnnnnn <= 7FFFFFFF
-                // ^Vnnn with 000 <= nnn <= 255
-                // ^VOnnn or ^Vonnn with 000 <= nnn <= 377
-                m_ctrlVLength = 2;
-                m_ctrlVBase   = 16;
-            } else if (input.is('O') || input.is('o')) {
-                m_ctrlVLength = 3;
-                m_ctrlVBase   = 8;
-            } else if (input.is('u')) {
-                m_ctrlVLength = 4;
-                m_ctrlVBase   = 16;
-            } else if (input.is('U')) {
-                m_ctrlVLength = 8;
-                m_ctrlVBase   = 16;
-            } else if (input.isDigit()) {
-                bool ok;
-                m_ctrlVAccumulator = input.toInt(&ok, 10);
-                m_ctrlVLength      = 2;
-                m_ctrlVBase        = 10;
-            } else {
-                insertInInsertMode(input.raw());
-                g.submode    = NoSubMode;
-                g.subsubmode = NoSubSubMode;
-            }
-        } else {
-            bool ok;
-            int current = input.toInt(&ok, m_ctrlVBase);
-            if (ok)
-                m_ctrlVAccumulator = m_ctrlVAccumulator * m_ctrlVBase + current;
-            --m_ctrlVLength;
-            if (m_ctrlVLength == 0 || !ok) {
-                QString str;
-                if (QChar::requiresSurrogates(static_cast<uint>(m_ctrlVAccumulator))) {
-                    str.append(QChar(QChar::highSurrogate(static_cast<uint>(m_ctrlVAccumulator))));
-                    str.append(QChar(QChar::lowSurrogate(static_cast<uint>(m_ctrlVAccumulator))));
-                } else {
-                    str.append(QChar(m_ctrlVAccumulator));
-                }
-                insertInInsertMode(str);
-                g.submode    = NoSubMode;
-                g.subsubmode = NoSubSubMode;
-
-                // Try again without Ctrl-V interpretation.
-                if (!ok)
-                    handleInsertMode(input);
-            }
-        }
-    } else if (input.isControl('o')) {
-        enterCommandMode(InsertMode);
-    } else if (input.isControl('v')) {
-        g.submode    = CtrlVSubMode;
-        g.subsubmode = NoSubSubMode;
-        updateMiniBuffer();
-    } else if (input.isControl('r')) {
-        g.submode    = CtrlRSubMode;
-        g.subsubmode = NoSubSubMode;
-        updateMiniBuffer();
-    } else if (input.isControl('w')) {
-        const int blockNumber = m_cursor.blockNumber();
-        const int endPos      = position();
-        moveToNextWordStart(1, false, false);
-        if (blockNumber != m_cursor.blockNumber())
-            moveToEndOfLine();
-        const int beginPos = position();
-        Range range(beginPos, endPos, RangeCharMode);
-        removeText(range);
-    } else if (input.isControl('u')) {
-        const int blockNumber = m_cursor.blockNumber();
-        const int endPos      = position();
-        moveToStartOfLine();
-        if (blockNumber != m_cursor.blockNumber())
-            moveToEndOfLine();
-        const int beginPos = position();
-        Range range(beginPos, endPos, RangeCharMode);
-        removeText(range);
-    } else if (input.isKey(Key_Insert)) {
-        g.mode = ReplaceMode;
-    } else if (input.isKey(Key_Left)) {
-        moveLeft();
-    } else if (input.isShift(Key_Left) || input.isControl(Key_Left)) {
-        moveToNextWordStart(1, false, false);
-    } else if (input.isKey(Key_Down)) {
-        g.submode = NoSubMode;
-        moveDown();
-    } else if (input.isKey(Key_Up)) {
-        g.submode = NoSubMode;
-        moveUp();
-    } else if (input.isKey(Key_Right)) {
-        moveRight();
-    } else if (input.isShift(Key_Right) || input.isControl(Key_Right)) {
-        moveToNextWordStart(1, false, true);
-    } else if (input.isKey(Key_Home)) {
-        moveToStartOfLine();
-    } else if (input.isKey(Key_End)) {
-        moveBehindEndOfLine();
-        m_targetColumn = -1;
-    } else if (input.isReturn() || input.isControl('j') || input.isControl('m')) {
-        if (!input.isReturn() || !handleInsertInEditor(input)) {
-            joinPreviousEditBlock();
-            g.submode = NoSubMode;
-            insertNewLine();
-            endEditBlock();
-        }
-    } else if (input.isBackspace()) {
-        // pass C-h as backspace, too
-        if (!handleInsertInEditor(Input(Qt::Key_Backspace, Qt::NoModifier))) {
-            joinPreviousEditBlock();
-            if (!m_buffer->lastInsertion.isEmpty() || s.backspace.value().contains("start") ||
-                s.backspace.value().contains("2")) {
-                const int line   = cursorLine() + 1;
-                const Column col = cursorColumn();
-                QString data     = lineContents(line);
-                const Column ind = indentation(data);
-                if (col.logical <= ind.logical && col.logical &&
-                    startsWithWhitespace(data, col.physical)) {
-                    const int ts         = static_cast<int>(s.tabStop.value());
-                    const int newl       = col.logical - 1 - (col.logical - 1) % ts;
-                    const QString prefix = tabExpand(newl);
-                    setLineContents(line, prefix + data.mid(col.physical));
-                    moveToStartOfLine();
-                    moveRight(int(prefix.size()));
-                } else {
-                    setAnchor();
-                    m_cursor.deletePreviousChar();
-                }
-            }
-            endEditBlock();
-        }
-    } else if (input.isKey(Key_Delete)) {
-        if (!handleInsertInEditor(input)) {
-            joinPreviousEditBlock();
-            m_cursor.deleteChar();
-            endEditBlock();
-        }
-    } else if (input.isKey(Key_PageDown) || input.isControl('f')) {
-        movePageDown();
-    } else if (input.isKey(Key_PageUp) || input.isControl('b')) {
-        movePageUp();
-    } else if (input.isKey(Key_Tab)) {
-        m_buffer->insertState.insertingSpaces = true;
-        if (s.expandTab.value()) {
-            const int ts  = static_cast<int>(s.tabStop.value());
-            const int col = logicalCursorColumn();
-            QString str   = QString(ts - col % ts, ' ');
-            insertText(str);
-        } else {
-            insertInInsertMode(input.raw());
-        }
-        m_buffer->insertState.insertingSpaces = false;
-    } else if (input.isControl('d')) {
-        // remove one level of indentation from the current line
-        const int shift = static_cast<int>(s.shiftWidth.value());
-        const int tab   = static_cast<int>(s.tabStop.value());
-        int line        = cursorLine() + 1;
-        int pos         = firstPositionInLine(line);
-        QString text    = lineContents(line);
-        int amount      = 0;
-        int i           = 0;
-        for (; i < text.size() && amount < shift; ++i) {
-            if (text.at(i) == ' ')
-                ++amount;
-            else if (text.at(i) == '\t')
-                amount += tab; // FIXME: take position into consideration
-            else
-                break;
-        }
-        removeText(Range(pos, pos + i));
-    } else if (input.isControl('p') || input.isControl('n')) {
-        QTextCursor tc = m_cursor;
-        moveToNextWordStart(1, false, false);
-        QString str = selectText(Range(position(), tc.position()));
-        m_cursor    = tc;
-        q->simpleCompletionRequested(str, input.isControl('n'));
-    } else if (input.isShift(Qt::Key_Insert)) {
-        // Insert text from clipboard.
-        QClipboard *clipboard = QApplication::clipboard();
-        const QMimeData *data = clipboard->mimeData();
-        if (data && data->hasText())
-            insertInInsertMode(data->text());
-    } else {
-        m_buffer->insertState.insertingSpaces = input.isKey(Key_Space);
-        if (!handleInsertInEditor(input)) {
-            const QString toInsert = input.text();
-            if (toInsert.isEmpty())
-                return;
-            insertInInsertMode(toInsert);
-        }
-        m_buffer->insertState.insertingSpaces = false;
-    }
-}
-
-void
-FakeVimHandler::Private::insertInInsertMode(const QString &text)
-{
-    joinPreviousEditBlock();
-    insertText(text);
-    if (s.smartIndent.value() && isElectricCharacter(text.at(0))) {
-        const QString leftText = block().text().left(position() - 1 - block().position());
-        if (leftText.simplified().isEmpty()) {
-            Range range(position(), position(), g.rangemode);
-            indentText(range, text.at(0));
-        }
-    }
-    setTargetColumn();
-    endEditBlock();
-    g.submode = NoSubMode;
-}
-
-bool
-FakeVimHandler::Private::startRecording(const Input &input)
-{
-    QChar reg = input.asChar();
-    if (reg == '"' || reg.isLetterOrNumber()) {
-        g.currentRegister = reg.unicode();
-        g.isRecording     = true;
-        g.recorded.clear();
-        return true;
-    }
-
-    return false;
-}
-
-void
-FakeVimHandler::Private::record(const Input &input)
-{
-    if (g.isRecording)
-        g.recorded.append(input.toString());
-}
-
-void
-FakeVimHandler::Private::stopRecording()
-{
-    // Remove q from end (stop recording command).
-    g.isRecording = false;
-    g.recorded.chop(1);
-    setRegister(g.currentRegister, g.recorded, g.rangemode);
-    g.currentRegister = 0;
-    g.recorded.clear();
-}
-
-void
-FakeVimHandler::Private::handleAs(const QString &command)
-{
-    QString cmd = QString("\"%1").arg(QChar(m_register));
-
-    if (command.contains("%1"))
-        cmd.append(command.arg(count()));
-    else
-        cmd.append(command);
-
-    leaveVisualMode();
-    beginLargeEditBlock();
-    replay(cmd);
-    endEditBlock();
-}
-
-bool
-FakeVimHandler::Private::executeRegister(int reg)
-{
-    QChar regChar(reg);
-
-    // TODO: Prompt for an expression to execute if register is '='.
-    if (reg == '@' && g.lastExecutedRegister != 0)
-        reg = g.lastExecutedRegister;
-    else if (QString("\".*+").contains(regChar) || regChar.isLetterOrNumber())
-        g.lastExecutedRegister = reg;
-    else
-        return false;
-
-    // FIXME: In Vim it's possible to interrupt recursive macro with <C-c>.
-    //        One solution may be to call QApplication::processEvents() and check if <C-c> was
-    //        used when a mapping is active.
-    // According to Vim, register is executed like mapping.
-    prependMapping(Inputs(registerContents(reg), false, false));
-
-    return true;
-}
-
-EventResult
-FakeVimHandler::Private::handleExMode(const Input &input)
-{
-    // handle C-R, C-R C-W, C-R {register}
-    if (handleCommandBufferPaste(input))
-        return EventHandled;
-
-    if (input.isEscape()) {
-        g.commandBuffer.clear();
-        leaveCurrentMode();
-        g.submode = NoSubMode;
-    } else if (g.submode == CtrlVSubMode) {
-        g.commandBuffer.insertChar(input.raw());
-        g.submode = NoSubMode;
-    } else if (input.isControl('v')) {
-        g.submode    = CtrlVSubMode;
-        g.subsubmode = NoSubSubMode;
-        return EventHandled;
-    } else if (input.isBackspace()) {
-        if (g.commandBuffer.isEmpty()) {
-            leaveVisualMode();
-            leaveCurrentMode();
-        } else if (g.commandBuffer.hasSelection()) {
-            g.commandBuffer.deleteSelected();
-        } else {
-            g.commandBuffer.deleteChar();
-        }
-    } else if (input.isKey(Key_Tab)) {
-        // FIXME: Complete actual commands.
-        g.commandBuffer.historyUp();
-    } else if (input.isReturn()) {
-        showMessage(MessageCommand, g.commandBuffer.display());
-        handleExCommand(g.commandBuffer.contents());
-        g.commandBuffer.clear();
-    } else if (!g.commandBuffer.handleInput(input)) {
-        logDebug() << "IGNORED IN EX-MODE: " << input.key() << input.text();
-        return EventUnhandled;
-    }
-
-    return EventHandled;
-}
-
-EventResult
-FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
-{
-    EventResult handled = EventHandled;
-
-    // handle C-R, C-R C-W, C-R {register}
-    if (handleCommandBufferPaste(input))
-        return handled;
-
-    if (input.isEscape()) {
-        g.currentMessage.clear();
-        setPosition(m_searchStartPosition);
-        scrollToLine(m_searchFromScreenLine);
-    } else if (input.isBackspace()) {
-        if (g.searchBuffer.isEmpty())
-            leaveCurrentMode();
-        else if (g.searchBuffer.hasSelection())
-            g.searchBuffer.deleteSelected();
-        else
-            g.searchBuffer.deleteChar();
-    } else if (input.isReturn()) {
-        const QString &needle = g.searchBuffer.contents();
-        if (!needle.isEmpty())
-            g.lastSearch = needle;
-        else
-            g.searchBuffer.setContents(g.lastSearch);
-
-        updateFind(true);
-
-        if (finishSearch()) {
-            if (g.submode != NoSubMode)
-                finishMovement(g.searchBuffer.prompt() + g.lastSearch + '\n');
-            if (g.currentMessage.isEmpty())
-                showMessage(MessageCommand, g.searchBuffer.display());
-        } else {
-            handled = EventCancelled; // Not found so cancel mapping if any.
-        }
-    } else if (input.isKey(Key_Tab)) {
-        g.searchBuffer.insertChar(QChar(9));
-    } else if (!g.searchBuffer.handleInput(input)) {
-        return EventUnhandled;
-    }
-
-    if (input.isReturn() || input.isEscape()) {
-        g.searchBuffer.clear();
-        leaveCurrentMode();
-    } else {
-        updateFind(false);
-    }
-
-    return handled;
-}
-
-// This uses 0 based line counting (hidden lines included).
-int
-FakeVimHandler::Private::parseLineAddress(QString *cmd)
-{
-    if (cmd->isEmpty())
-        return -1;
-
-    int result = -1;
-    QChar c    = cmd->at(0);
-    if (c == '.') { // current line
-        result = cursorBlockNumber();
-        cmd->remove(0, 1);
-    } else if (c == '$') { // last line
-        result = document()->blockCount() - 1;
-        cmd->remove(0, 1);
-    } else if (c == '\'') { // mark
-        cmd->remove(0, 1);
-        if (cmd->isEmpty()) {
-            showMessage(MessageError, msgMarkNotSet(QString()));
-            return -1;
-        }
-        c      = cmd->at(0);
-        Mark m = mark(c);
-        if (!m.isValid() || !m.isLocal(m_currentFileName)) {
-            showMessage(MessageError, msgMarkNotSet(c));
-            return -1;
-        }
-        cmd->remove(0, 1);
-        result = m.position(document()).line;
-    } else if (c.isDigit()) { // line with given number
-        result = 0;
-    } else if (c == '-' || c == '+') { // add or subtract from current line number
-        result = cursorBlockNumber();
-    } else if (c == '/' || c == '?' ||
-               (c == '\\' && cmd->size() > 1 && QString("/?&").contains(cmd->at(1)))) {
-        // search for expression
-        SearchData sd;
-        if (c == '/' || c == '?') {
-            const int end = findUnescaped(c, *cmd, 1);
-            if (end == -1)
-                return -1;
-            sd.needle = cmd->mid(1, end - 1);
-            cmd->remove(0, end + 1);
-        } else {
-            c = cmd->at(1);
-            cmd->remove(0, 2);
-            sd.needle = (c == '&') ? g.lastSubstitutePattern : g.lastSearch;
-        }
-        sd.forward         = (c != '?');
-        const QTextBlock b = block();
-        const int pos      = b.position() + (sd.forward ? b.length() - 1 : 0);
-        QTextCursor tc     = search(sd, pos, 1, true);
-        g.lastSearch       = sd.needle;
-        if (tc.isNull())
-            return -1;
-        result = tc.block().blockNumber();
-    } else {
-        return cursorBlockNumber();
-    }
-
-    // basic arithmetic ("-3+5" or "++" means "+2" etc.)
-    int n    = 0;
-    bool add = true;
-    int i    = 0;
-    for (; i < cmd->size(); ++i) {
-        c = cmd->at(i);
-        if (c == '-' || c == '+') {
-            if (n != 0)
-                result = result + (add ? n - 1 : -(n - 1));
-            add    = c == '+';
-            result = result + (add ? 1 : -1);
-            n      = 0;
-        } else if (c.isDigit()) {
-            n = n * 10 + c.digitValue();
-        } else if (!c.isSpace()) {
-            break;
-        }
-    }
-    if (n != 0)
-        result = result + (add ? n - 1 : -(n - 1));
-    *cmd = cmd->mid(i).trimmed();
-
-    return result;
-}
-
-void
-FakeVimHandler::Private::setCurrentRange(const Range &range)
-{
-    setAnchorAndPosition(range.beginPos, range.endPos);
-    g.rangemode = range.rangemode;
-}
-
-bool
-FakeVimHandler::Private::parseExCommand(QString *line, ExCommand *cmd)
-{
-    *cmd = ExCommand();
-    if (line->isEmpty())
-        return false;
-
-    // parse range first
-    if (!parseLineRange(line, cmd))
-        return false;
-
-    // get first command from command line
-    QChar close;
-    bool subst = false;
-    int i      = 0;
-    for (; i < line->size(); ++i) {
-        const QChar &c = line->at(i);
-        if (c == '\\') {
-            ++i; // skip escaped character
-        } else if (close.isNull()) {
-            if (c == '|') {
-                // split on |
-                break;
-            } else if (c == '/') {
-                subst = i > 0 && (line->at(i - 1) == 's');
-                close = c;
-            } else if (c == '"' || c == '\'') {
-                close = c;
-            }
-        } else if (c == close) {
-            if (subst)
-                subst = false;
-            else
-                close = QChar();
-        }
-    }
-
-    cmd->cmd = line->mid(0, i).trimmed();
-
-    // command arguments starts with first non-letter character
-    cmd->args = cmd->cmd.section(QRegularExpression("(?=[^a-zA-Z])"), 1);
-    if (!cmd->args.isEmpty()) {
-        cmd->cmd.chop(cmd->args.size());
-        cmd->args = cmd->args.trimmed();
-
-        // '!' at the end of command
-        cmd->hasBang = cmd->args.startsWith('!');
-        if (cmd->hasBang)
-            cmd->args = cmd->args.mid(1).trimmed();
-    }
-
-    // remove the first command from command line
-    line->remove(0, i + 1);
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::parseLineRange(QString *line, ExCommand *cmd)
-{
-    // remove leading colons and spaces
-    line->remove(QRegularExpression("^\\s*(:+\\s*)*"));
-
-    // special case ':!...' (use invalid range)
-    if (line->startsWith('!')) {
-        cmd->range = Range();
-        return true;
-    }
-
-    // FIXME: that seems to be different for %w and %s
-    if (line->startsWith('%'))
-        line->replace(0, 1, "1,$");
-
-    int beginLine = parseLineAddress(line);
-    int endLine;
-    if (line->startsWith(',')) {
-        *line   = line->mid(1).trimmed();
-        endLine = parseLineAddress(line);
-    } else {
-        endLine = beginLine;
-    }
-    if (beginLine == -1 || endLine == -1)
-        return false;
-
-    const int beginPos = firstPositionInLine(qMin(beginLine, endLine) + 1, false);
-    const int endPos   = lastPositionInLine(qMax(beginLine, endLine) + 1, false);
-    cmd->range         = Range(beginPos, endPos, RangeLineMode);
-    cmd->count         = beginLine;
-
-    return true;
-}
-
-void
-FakeVimHandler::Private::parseRangeCount(const QString &line, Range *range) const
-{
-    bool ok;
-    const int count = qAbs(line.trimmed().toInt(&ok));
-    if (ok) {
-        const int beginLine = blockAt(range->endPos).blockNumber() + 1;
-        const int endLine   = qMin(beginLine + count - 1, document()->blockCount());
-        range->beginPos     = firstPositionInLine(beginLine, false);
-        range->endPos       = lastPositionInLine(endLine, false);
-    }
-}
-
-// use handleExCommand for invoking commands that might move the cursor
-void
-FakeVimHandler::Private::handleCommand(const QString &cmd)
-{
-    handleExCommand(cmd);
-}
-
-bool
-FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
-{
-    // :substitute
-    if (!cmd.matches("s", "substitute") &&
-        !(cmd.cmd.isEmpty() && !cmd.args.isEmpty() && QString("&~").contains(cmd.args[0]))) {
-        return false;
-    }
-
-    int count                           = 1;
-    QString line                        = cmd.args;
-    const QRegularExpressionMatch match = QRegularExpression("\\d+$").match(line);
-    if (match.hasMatch()) {
-        count = match.captured().toInt();
-        line  = line.left(match.capturedStart()).trimmed();
-    }
-
-    if (cmd.cmd.isEmpty()) {
-        // keep previous substitution flags on '&&' and '~&'
-        if (line.size() > 1 && line[1] == '&')
-            g.lastSubstituteFlags += line.mid(2);
-        else
-            g.lastSubstituteFlags = line.mid(1);
-        if (line[0] == '~')
-            g.lastSubstitutePattern = g.lastSearch;
-    } else {
-        if (line.isEmpty()) {
-            g.lastSubstituteFlags.clear();
-        } else {
-            // we have /{pattern}/{string}/[flags]  now
-            const QChar separator = line.at(0);
-            int pos1              = findUnescaped(separator, line, 1);
-            if (pos1 == -1)
-                return false;
-            qsizetype pos2 = findUnescaped(separator, line, pos1 + 1);
-            if (pos2 == -1)
-                pos2 = line.size();
-
-            g.lastSubstitutePattern     = line.mid(1, pos1 - 1);
-            g.lastSubstituteReplacement = line.mid(pos1 + 1, pos2 - pos1 - 1);
-            g.lastSubstituteFlags       = line.mid(pos2 + 1);
-        }
-    }
-
-    count          = qMax(1, count);
-    QString needle = g.lastSubstitutePattern;
-
-    if (g.lastSubstituteFlags.contains('i'))
-        needle.prepend("\\c");
-
-    const QRegularExpression pattern = vimPatternToQtPattern(needle);
-
-    QTextBlock lastBlock;
-    QTextBlock firstBlock;
-    const bool global = g.lastSubstituteFlags.contains('g');
-    for (int a = 0; a != count; ++a) {
-        for (QTextBlock block = blockAt(cmd.range.endPos);
-             block.isValid() && block.position() + block.length() > cmd.range.beginPos;
-             block = block.previous()) {
-            QString text = block.text();
-            if (substituteText(&text, pattern, g.lastSubstituteReplacement, global)) {
-                firstBlock = block;
-                if (!lastBlock.isValid()) {
-                    lastBlock = block;
-                    beginEditBlock();
-                }
-                QTextCursor tc   = m_cursor;
-                const int pos    = block.position();
-                const int anchor = pos + block.length() - 1;
-                tc.setPosition(anchor);
-                tc.setPosition(pos, QTextCursor::KeepAnchor);
-                tc.insertText(text);
-            }
-        }
-    }
-
-    if (lastBlock.isValid()) {
-        m_buffer->undoState.position = CursorPosition(firstBlock.blockNumber(), 0);
-
-        leaveVisualMode();
-        setPosition(lastBlock.position());
-        setAnchor();
-        moveToFirstNonBlankOnLine();
-
-        endEditBlock();
-    }
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExTabNextCommand(const ExCommand &cmd)
-{
-    if (!cmd.matches("tabn", "tabnext"))
-        return false;
-
-    q->tabNextRequested();
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExTabPreviousCommand(const ExCommand &cmd)
-{
-    if (!cmd.matches("tabp", "tabprevious"))
-        return false;
-
-    q->tabPreviousRequested();
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
-{
-    QByteArray modes;
-    enum Type { Map, Noremap, Unmap } type;
-
-    QByteArray cmd = cmd0.cmd.toLatin1();
-
-    // Strange formatting. But everything else is even uglier.
-    if (cmd == "map") {
-        modes = "nvo";
-        type  = Map;
-    } else if (cmd == "nm" || cmd == "nmap") {
-        modes = "n";
-        type  = Map;
-    } else if (cmd == "vm" || cmd == "vmap") {
-        modes = "v";
-        type  = Map;
-    } else if (cmd == "xm" || cmd == "xmap") {
-        modes = "x";
-        type  = Map;
-    } else if (cmd == "smap") {
-        modes = "s";
-        type  = Map;
-    } else if (cmd == "omap") {
-        modes = "o";
-        type  = Map;
-    } else if (cmd == "map!") {
-        modes = "ic";
-        type  = Map;
-    } else if (cmd == "im" || cmd == "imap") {
-        modes = "i";
-        type  = Map;
-    } else if (cmd == "lm" || cmd == "lmap") {
-        modes = "l";
-        type  = Map;
-    } else if (cmd == "cm" || cmd == "cmap") {
-        modes = "c";
-        type  = Map;
-    } else if (cmd == "no" || cmd == "noremap") {
-        modes = "nvo";
-        type  = Noremap;
-    } else if (cmd == "nn" || cmd == "nnoremap") {
-        modes = "n";
-        type  = Noremap;
-    } else if (cmd == "vn" || cmd == "vnoremap") {
-        modes = "v";
-        type  = Noremap;
-    } else if (cmd == "xn" || cmd == "xnoremap") {
-        modes = "x";
-        type  = Noremap;
-    } else if (cmd == "snor" || cmd == "snoremap") {
-        modes = "s";
-        type  = Noremap;
-    } else if (cmd == "ono" || cmd == "onoremap") {
-        modes = "o";
-        type  = Noremap;
-    } else if (cmd == "no!" || cmd == "noremap!") {
-        modes = "ic";
-        type  = Noremap;
-    } else if (cmd == "ino" || cmd == "inoremap") {
-        modes = "i";
-        type  = Noremap;
-    } else if (cmd == "ln" || cmd == "lnoremap") {
-        modes = "l";
-        type  = Noremap;
-    } else if (cmd == "cno" || cmd == "cnoremap") {
-        modes = "c";
-        type  = Noremap;
-    } else if (cmd == "unm" || cmd == "unmap") {
-        modes = "nvo";
-        type  = Unmap;
-    } else if (cmd == "nun" || cmd == "nunmap") {
-        modes = "n";
-        type  = Unmap;
-    } else if (cmd == "vu" || cmd == "vunmap") {
-        modes = "v";
-        type  = Unmap;
-    } else if (cmd == "xu" || cmd == "xunmap") {
-        modes = "x";
-        type  = Unmap;
-    } else if (cmd == "sunm" || cmd == "sunmap") {
-        modes = "s";
-        type  = Unmap;
-    } else if (cmd == "ou" || cmd == "ounmap") {
-        modes = "o";
-        type  = Unmap;
-    } else if (cmd == "unm!" || cmd == "unmap!") {
-        modes = "ic";
-        type  = Unmap;
-    } else if (cmd == "iu" || cmd == "iunmap") {
-        modes = "i";
-        type  = Unmap;
-    } else if (cmd == "lu" || cmd == "lunmap") {
-        modes = "l";
-        type  = Unmap;
-    } else if (cmd == "cu" || cmd == "cunmap") {
-        modes = "c";
-        type  = Unmap;
-    }
-
-    else
-        return false;
-
-    QString args = cmd0.args;
-    bool silent  = false;
-    bool unique  = false;
-    forever {
-        if (eatString("<silent>", &args)) {
-            silent = true;
-        } else if (eatString("<unique>", &args)) {
-            continue;
-        } else if (eatString("<special>", &args)) {
-            continue;
-        } else if (eatString("<buffer>", &args)) {
-            notImplementedYet();
-            continue;
-        } else if (eatString("<script>", &args)) {
-            notImplementedYet();
-            continue;
-        } else if (eatString("<expr>", &args)) {
-            notImplementedYet();
-            return true;
-        }
-        break;
-    }
-
-    const QString lhs = args.section(QRegularExpression("\\s+"), 0, 0);
-    const QString rhs = args.section(QRegularExpression("\\s+"), 1);
-    if ((rhs.isNull() && type != Unmap) || (!rhs.isNull() && type == Unmap)) {
-        // FIXME: Dump mappings here.
-        //qDebug() << g.mappings;
-        return true;
-    }
-
-    Inputs key(lhs);
-    switch (type) {
-    case Unmap: foreach(char c, modes) MappingsIterator(&g.mappings, c, key).remove(); break;
-    case Map: Q_FALLTHROUGH();
-    case Noremap: {
-        Inputs inputs(rhs, type == Noremap, silent);
-        foreach(char c, modes) MappingsIterator(&g.mappings, c).setInputs(key, inputs, unique);
-        break;
-    }
-    }
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExHistoryCommand(const ExCommand &cmd)
-{
-    // :his[tory]
-    if (!cmd.matches("his", "history"))
-        return false;
-
-    if (cmd.args.isEmpty()) {
-        QString info;
-        info += "#  command history\n";
-        int i = 0;
-        foreach(const QString &item, g.commandBuffer.historyItems())
-        {
-            ++i;
-            info += QString("%1 %2\n").arg(i, -8).arg(item);
-        }
-        q->extraInformationChanged(info);
-    } else {
-        notImplementedYet();
-    }
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExRegisterCommand(const ExCommand &cmd)
-{
-    // :reg[isters] and :di[splay]
-    if (!cmd.matches("reg", "registers") && !cmd.matches("di", "display"))
-        return false;
-
-    QByteArray regs = cmd.args.toLatin1();
-    if (regs.isEmpty()) {
-        regs = "\"0123456789";
-        for (auto it = g.registers.cbegin(), end = g.registers.cend(); it != end; ++it) {
-            if (it.key() > '9')
-                regs += char(it.key());
-        }
-    }
-    QString info;
-    info += "--- Registers ---\n";
-    for (char reg : qAsConst(regs)) {
-        QString value = quoteUnprintable(registerContents(reg));
-        info += QString("\"%1   %2\n").arg(reg).arg(value);
-    }
-    q->extraInformationChanged(info);
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd)
-{
-    // :se[t]
-    if (!cmd.matches("se", "set"))
-        return false;
-
-    clearMessage();
-
-    if (cmd.args.contains('=')) {
-        // Non-boolean config to set.
-        qsizetype p         = cmd.args.indexOf('=');
-        QString error = s.trySetValue(cmd.args.left(p), cmd.args.mid(p + 1));
-        if (!error.isEmpty())
-            showMessage(MessageError, error);
-    } else {
-        QString optionName = cmd.args;
-
-        bool toggleOption = optionName.endsWith('!');
-        bool printOption  = !toggleOption && optionName.endsWith('?');
-        if (printOption || toggleOption)
-            optionName.chop(1);
-
-        bool negateOption = optionName.startsWith("no");
-        if (negateOption)
-            optionName.remove(0, 2);
-
-        FvBaseAspect *act = s.item(optionName);
-        if (!act) {
-            showMessage(MessageError, Tr::tr("Unknown option:") + ' ' + cmd.args);
-        } else if (act->defaultValue().type() == QVariant::Bool) {
-            bool oldValue = act->value().toBool();
-            if (printOption) {
-                showMessage(MessageInfo,
-                            QLatin1String(oldValue ? "" : "no") + act->settingsKey().toLower());
-            } else if (toggleOption || negateOption == oldValue) {
-                act->setValue(!oldValue);
-            }
-        } else if (negateOption && !printOption) {
-            showMessage(MessageError, Tr::tr("Invalid argument:") + ' ' + cmd.args);
-        } else if (toggleOption) {
-            showMessage(MessageError, Tr::tr("Trailing characters:") + ' ' + cmd.args);
-        } else {
-            showMessage(MessageInfo, act->settingsKey().toLower() + "=" + act->value().toString());
-        }
-    }
-    updateEditor();
-    updateHighlights();
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExNormalCommand(const ExCommand &cmd)
-{
-    // :norm[al]
-    if (!cmd.matches("norm", "normal"))
-        return false;
-    replay(cmd.args);
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExYankDeleteCommand(const ExCommand &cmd)
-{
-    // :[range]d[elete] [x] [count]
-    // :[range]y[ank] [x] [count]
-    const bool remove = cmd.matches("d", "delete");
-    if (!remove && !cmd.matches("y", "yank"))
-        return false;
-
-    // get register from arguments
-    const bool hasRegisterArg = !cmd.args.isEmpty() && !cmd.args.at(0).isDigit();
-    const int r               = hasRegisterArg ? cmd.args.at(0).unicode() : m_register;
-
-    // get [count] from arguments
-    Range range = cmd.range;
-    parseRangeCount(cmd.args.mid(hasRegisterArg ? 1 : 0).trimmed(), &range);
-
-    yankText(range, r);
-
-    if (remove) {
-        leaveVisualMode();
-        setPosition(range.beginPos);
-        pushUndoState();
-        setCurrentRange(range);
-        removeText(currentRange());
-    }
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExChangeCommand(const ExCommand &cmd)
-{
-    // :[range]c[hange]
-    if (!cmd.matches("c", "change"))
-        return false;
-
-    Range range     = cmd.range;
-    range.rangemode = RangeLineModeExclusive;
-    removeText(range);
-    insertAutomaticIndentation(true, cmd.hasBang);
-
-    // FIXME: In Vim same or less number of lines can be inserted and position after insertion is
-    //        beginning of last inserted line.
-    enterInsertMode();
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExMoveCommand(const ExCommand &cmd)
-{
-    // :[range]m[ove] {address}
-    if (!cmd.matches("m", "move"))
-        return false;
-
-    QString lineCode = cmd.args;
-
-    const int startLine = blockAt(cmd.range.beginPos).blockNumber();
-    const int endLine   = blockAt(cmd.range.endPos).blockNumber();
-    const int lines     = endLine - startLine + 1;
-
-    int targetLine = lineCode == "0" ? -1 : parseLineAddress(&lineCode);
-    if (targetLine >= startLine && targetLine < endLine) {
-        showMessage(MessageError, Tr::tr("Move lines into themselves."));
-        return true;
-    }
-
-    CursorPosition lastAnchor   = markLessPosition();
-    CursorPosition lastPosition = markGreaterPosition();
-
-    recordJump();
-    setPosition(cmd.range.beginPos);
-    pushUndoState();
-
-    setCurrentRange(cmd.range);
-    QString text = selectText(cmd.range);
-    removeText(currentRange());
-
-    const bool insertAtEnd = targetLine == document()->blockCount();
-    if (targetLine >= startLine)
-        targetLine -= lines;
-    QTextBlock block = document()->findBlockByNumber(insertAtEnd ? targetLine : targetLine + 1);
-    setPosition(block.position());
-    setAnchor();
-
-    if (insertAtEnd) {
-        moveBehindEndOfLine();
-        text.chop(1);
-        insertText(QString("\n"));
-    }
-    insertText(text);
-
-    if (!insertAtEnd)
-        moveUp(1);
-    if (s.startOfLine.value())
-        moveToFirstNonBlankOnLine();
-
-    if (lastAnchor.line >= startLine && lastAnchor.line <= endLine)
-        lastAnchor.line += targetLine - startLine + 1;
-    if (lastPosition.line >= startLine && lastPosition.line <= endLine)
-        lastPosition.line += targetLine - startLine + 1;
-    setMark('<', lastAnchor);
-    setMark('>', lastPosition);
-
-    if (lines > 2)
-        showMessage(MessageInfo, Tr::tr("%n lines moved.", nullptr, lines));
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExJoinCommand(const ExCommand &cmd)
-{
-    // :[range]j[oin][!] [count]
-    // FIXME: Argument [count] can follow immediately.
-    if (!cmd.matches("j", "join"))
-        return false;
-
-    // get [count] from arguments
-    bool ok;
-    int count = cmd.args.toInt(&ok);
-
-    if (ok) {
-        setPosition(cmd.range.endPos);
-    } else {
-        setPosition(cmd.range.beginPos);
-        const int startLine = blockAt(cmd.range.beginPos).blockNumber();
-        const int endLine   = blockAt(cmd.range.endPos).blockNumber();
-        count               = endLine - startLine + 1;
-    }
-
-    moveToStartOfLine();
-    pushUndoState();
-    joinLines(count, cmd.hasBang);
-
-    moveToFirstNonBlankOnLine();
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
-{
-    // Note: The cmd.args.isEmpty() case is handled by handleExPluginCommand.
-    // :w, :x, :wq, ...
-    //static QRegularExpression reWrite("^[wx]q?a?!?( (.*))?$");
-    if (cmd.cmd != "w" && cmd.cmd != "x" && cmd.cmd != "wq")
-        return false;
-
-    int beginLine     = lineForPosition(cmd.range.beginPos);
-    int endLine       = lineForPosition(cmd.range.endPos);
-    const bool noArgs = (beginLine == -1);
-    if (beginLine == -1)
-        beginLine = 0;
-    if (endLine == -1)
-        endLine = linesInDocument();
-    const bool forced = cmd.hasBang;
-    QString fileName  = replaceTildeWithHome(cmd.args);
-    if (fileName.isEmpty())
-        fileName = m_currentFileName;
-    QFile file1(fileName);
-    const bool exists = file1.exists();
-    if (exists && !forced && !noArgs) {
-        showMessage(MessageError, Tr::tr("File \"%1\" exists (add ! to override)").arg(fileName));
-    } else if (file1.open(QIODevice::ReadWrite)) {
-        // Nobody cared, so act ourselves.
-        file1.close();
-        Range range(firstPositionInLine(beginLine), firstPositionInLine(endLine), RangeLineMode);
-        QString contents = selectText(range);
-        QFile::remove(fileName);
-        QFile file2(fileName);
-        if (file2.open(QIODevice::ReadWrite)) {
-            QTextStream ts(&file2);
-            ts << contents;
-        } else {
-            showMessage(MessageError, Tr::tr("Cannot open file \"%1\" for writing").arg(fileName));
-        }
-        // Check result by reading back.
-        QFile file3(fileName);
-        file3.open(QIODevice::ReadOnly);
-        QByteArray ba = file3.readAll();
-        showMessage(MessageInfo, Tr::tr("\"%1\" %2 %3L, %4C written.")
-                                     .arg(fileName)
-                                     .arg(exists ? QString(" ") : Tr::tr(" [New] "))
-                                     .arg(ba.count('\n'))
-                                     .arg(ba.size()));
-        //if (quitAll)
-        //    passUnknownExCommand(forced ? "qa!" : "qa");
-        //else if (quit)
-        //    passUnknownExCommand(forced ? "q!" : "q");
-    } else {
-        showMessage(MessageError, Tr::tr("Cannot open file \"%1\" for reading").arg(fileName));
-    }
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExReadCommand(const ExCommand &cmd)
-{
-    // :r[ead]
-    if (!cmd.matches("r", "read"))
-        return false;
-
-    beginEditBlock();
-
-    moveToStartOfLine();
-    moveDown();
-    int pos = position();
-
-    m_currentFileName = replaceTildeWithHome(cmd.args);
-    QFile file(m_currentFileName);
-    file.open(QIODevice::ReadOnly);
-    QTextStream ts(&file);
-    QString data = ts.readAll();
-    insertText(data);
-
-    setAnchorAndPosition(pos, pos);
-
-    endEditBlock();
-
-    showMessage(
-        MessageInfo,
-        Tr::tr("\"%1\" %2L, %3C").arg(m_currentFileName).arg(data.count('\n')).arg(data.size()));
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :!
-{
-    if (!cmd.cmd.isEmpty() || !cmd.hasBang)
-        return false;
-
-    bool replaceText      = cmd.range.isValid();
-    const QString command = QString(cmd.cmd.mid(1) + ' ' + cmd.args).trimmed();
-    const QString input   = replaceText ? selectText(cmd.range) : QString();
-
-    const QString result = getProcessOutput(command, input);
-
-    if (replaceText) {
-        setCurrentRange(cmd.range);
-        int targetPosition = firstPositionInLine(lineForPosition(cmd.range.beginPos));
-        beginEditBlock();
-        removeText(currentRange());
-        insertText(result);
-        setPosition(targetPosition);
-        endEditBlock();
-        leaveVisualMode();
-        showMessage(MessageInfo, Tr::tr("%n lines filtered.", nullptr, int(input.count('\n'))));
-    } else if (!result.isEmpty()) {
-        q->extraInformationChanged(result);
-    }
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExShiftCommand(const ExCommand &cmd)
-{
-    // :[range]{<|>}* [count]
-    if (!cmd.cmd.isEmpty() || (!cmd.args.startsWith('<') && !cmd.args.startsWith('>')))
-        return false;
-
-    const QChar c = cmd.args.at(0);
-
-    // get number of repetition
-    int repeat = 1;
-    int i      = 1;
-    for (; i < cmd.args.size(); ++i) {
-        const QChar c2 = cmd.args.at(i);
-        if (c2 == c)
-            ++repeat;
-        else if (!c2.isSpace())
-            break;
-    }
-
-    // get [count] from arguments
-    Range range = cmd.range;
-    parseRangeCount(cmd.args.mid(i), &range);
-
-    setCurrentRange(range);
-    if (c == '<')
-        shiftRegionLeft(repeat);
-    else
-        shiftRegionRight(repeat);
-
-    leaveVisualMode();
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExSortCommand(const ExCommand &cmd)
-{
-    // :[range]sor[t][!] [b][f][i][n][o][r][u][x] [/{pattern}/]
-    // FIXME: Only the ! for reverse is implemented.
-    if (!cmd.matches("sor", "sort"))
-        return false;
-
-    // Force operation on full lines, and full document if only
-    // one line (the current one...) is specified
-    int beginLine = lineForPosition(cmd.range.beginPos);
-    int endLine   = lineForPosition(cmd.range.endPos);
-    if (beginLine == endLine) {
-        beginLine = 0;
-        endLine   = lineForPosition(lastPositionInDocument());
-    }
-    Range range(firstPositionInLine(beginLine), firstPositionInLine(endLine), RangeLineMode);
-
-    QString input = selectText(range);
-    if (input.endsWith('\n')) // It should always...
-        input.chop(1);
-
-    QStringList lines = input.split('\n');
-    lines.sort();
-    if (cmd.hasBang)
-        std::reverse(lines.begin(), lines.end());
-    QString res = lines.join('\n') + '\n';
-
-    replaceText(range, res);
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExNohlsearchCommand(const ExCommand &cmd)
-{
-    // :noh, :nohl, ..., :nohlsearch
-    if (cmd.cmd.size() < 3 || !QString("nohlsearch").startsWith(cmd.cmd))
-        return false;
-
-    g.highlightsCleared = true;
-    updateHighlights();
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExUndoRedoCommand(const ExCommand &cmd)
-{
-    // :undo
-    // :redo
-    bool undo = (cmd.cmd == "u" || cmd.cmd == "un" || cmd.cmd == "undo");
-    if (!undo && cmd.cmd != "red" && cmd.cmd != "redo")
-        return false;
-
-    undoRedo(undo);
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExGotoCommand(const ExCommand &cmd)
-{
-    // :{address}
-    if (!cmd.cmd.isEmpty() || !cmd.args.isEmpty())
-        return false;
-
-    const int beginLine = lineForPosition(cmd.range.endPos);
-    setPosition(firstPositionInLine(beginLine));
-    clearMessage();
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd)
-{
-    // :source
-    if (cmd.cmd != "so" && cmd.cmd != "source")
-        return false;
-
-    QString fileName = replaceTildeWithHome(cmd.args);
-    QFile file(fileName);
-    if (!file.open(QIODevice::ReadOnly)) {
-        showMessage(MessageError, Tr::tr("Cannot open file %1").arg(fileName));
-        return true;
-    }
-
-    bool inFunction = false;
-    QByteArray line;
-    while (!file.atEnd() || !line.isEmpty()) {
-        QByteArray nextline = !file.atEnd() ? file.readLine() : QByteArray();
-
-        //  remove comment
-        qsizetype i = nextline.lastIndexOf('"');
-        if (i != -1)
-            nextline = nextline.remove(i, nextline.size() - i);
-
-        nextline = nextline.trimmed();
-
-        // multi-line command?
-        if (nextline.startsWith('\\')) {
-            line += nextline.mid(1);
-            continue;
-        }
-
-        if (line.startsWith("function")) {
-            inFunction = true;
-        } else if (inFunction && line.startsWith("endfunction")) {
-            inFunction = false;
-        } else if (!line.isEmpty() && !inFunction) {
-            ExCommand cmdLocal;
-            QString commandLine = QString::fromLocal8Bit(line);
-            while (parseExCommand(&commandLine, &cmdLocal)) {
-                if (!handleExCommandHelper(cmdLocal))
-                    break;
-            }
-        }
-
-        line = nextline;
-    }
-    file.close();
-    return true;
-}
-
-bool
-FakeVimHandler::Private::handleExEchoCommand(const ExCommand &cmd)
-{
-    // :echo
-    if (cmd.cmd != "echo")
-        return false;
-    showMessage(MessageInfo, cmd.args);
-    return true;
-}
-
-void
-FakeVimHandler::Private::handleExCommand(const QString &line0)
-{
-    QString line = line0; // Make sure we have a copy to prevent aliasing.
-
-    if (line.endsWith('%')) {
-        line.chop(1);
-        int percent = line.toInt();
-        setPosition(firstPositionInLine(percent * linesInDocument() / 100));
-        clearMessage();
-        return;
-    }
-
-    enterCommandMode(g.returnToMode);
-
-    beginLargeEditBlock();
-    ExCommand cmd;
-    QString lastCommand = line;
-    while (parseExCommand(&line, &cmd)) {
-        if (!handleExCommandHelper(cmd)) {
-            showMessage(MessageError, Tr::tr("Not an editor command: %1").arg(lastCommand));
-            break;
-        }
-        lastCommand = line;
-    }
-
-    // if the last command closed the editor, we would crash here (:vs and then :on)
-    if (!(m_textedit || m_plaintextedit))
-        return;
-
-    endEditBlock();
-
-    if (isVisualMode())
-        leaveVisualMode();
-    leaveCurrentMode();
-}
-
-bool
-FakeVimHandler::Private::handleExCommandHelper(ExCommand &cmd)
-{
-    return handleExPluginCommand(cmd) || handleExGotoCommand(cmd) || handleExBangCommand(cmd) ||
-           handleExHistoryCommand(cmd) || handleExRegisterCommand(cmd) ||
-           handleExYankDeleteCommand(cmd) || handleExChangeCommand(cmd) ||
-           handleExMoveCommand(cmd) || handleExJoinCommand(cmd) || handleExMapCommand(cmd) ||
-           handleExNohlsearchCommand(cmd) || handleExNormalCommand(cmd) ||
-           handleExReadCommand(cmd) || handleExUndoRedoCommand(cmd) || handleExSetCommand(cmd) ||
-           handleExShiftCommand(cmd) || handleExSortCommand(cmd) || handleExSourceCommand(cmd) ||
-           handleExSubstituteCommand(cmd) || handleExTabNextCommand(cmd) ||
-           handleExTabPreviousCommand(cmd) || handleExWriteCommand(cmd) || handleExEchoCommand(cmd);
-}
-
-bool
-FakeVimHandler::Private::handleExPluginCommand(const ExCommand &cmd)
-{
-    bool handled = false;
-    int pos      = m_cursor.position();
-    commitCursor();
-    q->handleExCommandRequested(&handled, cmd);
-    if (handled && (m_textedit || m_plaintextedit)) {
-        pullCursor();
-        if (m_cursor.position() != pos)
-            recordJump(pos);
-    }
-    return handled;
-}
-
-void
-FakeVimHandler::Private::searchBalanced(bool forward, QChar needle, QChar other)
-{
-    int level      = 1;
-    int pos        = position();
-    const int npos = forward ? lastPositionInDocument() : 0;
-    while (true) {
-        if (forward)
-            ++pos;
-        else
-            --pos;
-        if (pos == npos)
-            return;
-        QChar c = characterAt(pos);
-        if (c == other)
-            ++level;
-        else if (c == needle)
-            --level;
-        if (level == 0) {
-            const int oldLine = cursorLine() - cursorLineOnScreen();
-            // Making this unconditional feels better, but is not "vim like".
-            if (oldLine != cursorLine() - cursorLineOnScreen())
-                scrollToLine(cursorLine() - linesOnScreen() / 2);
-            recordJump();
-            setPosition(pos);
-            setTargetColumn();
-            return;
-        }
-    }
-}
-
-QTextCursor
-FakeVimHandler::Private::search(const SearchData &sd, int startPos, int count, bool showMessages)
-{
-    const QRegularExpression needleExp = vimPatternToQtPattern(sd.needle);
-
-    if (!needleExp.isValid()) {
-        if (showMessages) {
-            QString error = needleExp.errorString();
-            showMessage(MessageError, Tr::tr("Invalid regular expression: %1").arg(error));
-        }
-        if (sd.highlightMatches)
-            highlightMatches(QString());
-        return QTextCursor();
-    }
-
-    int repeat    = count;
-    const int pos = startPos + (sd.forward ? 1 : -1);
-
-    QTextCursor tc;
-    if (pos >= 0 && pos < document()->characterCount()) {
-        tc = QTextCursor(document());
-        tc.setPosition(pos);
-        if (sd.forward && afterEndOfLine(document(), pos))
-            tc.movePosition(QTextCursor::Right);
-
-        if (!tc.isNull()) {
-            if (sd.forward)
-                searchForward(&tc, needleExp, &repeat);
-            else
-                searchBackward(&tc, needleExp, &repeat);
-        }
-    }
-
-    if (tc.isNull()) {
-        if (s.wrapScan.value()) {
-            tc = QTextCursor(document());
-            tc.movePosition(sd.forward ? QTextCursor::Start : QTextCursor::End);
-            if (sd.forward)
-                searchForward(&tc, needleExp, &repeat);
-            else
-                searchBackward(&tc, needleExp, &repeat);
-            if (tc.isNull()) {
-                if (showMessages) {
-                    showMessage(MessageError, Tr::tr("Pattern not found: %1").arg(sd.needle));
-                }
-            } else if (showMessages) {
-                QString msg = sd.forward ? Tr::tr("Search hit BOTTOM, continuing at TOP.")
-                                         : Tr::tr("Search hit TOP, continuing at BOTTOM.");
-                showMessage(MessageWarning, msg);
-            }
-        } else if (showMessages) {
-            QString msg = sd.forward ? Tr::tr("Search hit BOTTOM without match for: %1")
-                                     : Tr::tr("Search hit TOP without match for: %1");
-            showMessage(MessageError, msg.arg(sd.needle));
-        }
-    }
-
-    if (sd.highlightMatches)
-        highlightMatches(needleExp.pattern());
-
-    return tc;
-}
-
-void
-FakeVimHandler::Private::search(const SearchData &sd, bool showMessages)
-{
-    const int oldLine = cursorLine() - cursorLineOnScreen();
-
-    QTextCursor tc = search(sd, m_searchStartPosition, count(), showMessages);
-    if (tc.isNull()) {
-        tc = m_cursor;
-        tc.setPosition(m_searchStartPosition);
-    }
-
-    if (isVisualMode()) {
-        int dLocal = tc.anchor() - tc.position();
-        setPosition(tc.position() + dLocal);
-    } else {
-        // Set Cursor. In contrast to the main editor we have the cursor
-        // position before the anchor position.
-        setAnchorAndPosition(tc.position(), tc.anchor());
-    }
-
-    // Making this unconditional feels better, but is not "vim like".
-    if (oldLine != cursorLine() - cursorLineOnScreen())
-        scrollToLine(cursorLine() - linesOnScreen() / 2);
-
-    m_searchCursor = m_cursor;
-
-    setTargetColumn();
-}
-
-bool
-FakeVimHandler::Private::searchNext(bool forward)
-{
-    SearchData sd;
-    sd.needle             = g.lastSearch;
-    sd.forward            = forward ? g.lastSearchForward : !g.lastSearchForward;
-    sd.highlightMatches   = true;
-    m_searchStartPosition = position();
-    showMessage(MessageCommand, QLatin1Char(g.lastSearchForward ? '/' : '?') + sd.needle);
-    recordJump();
-    search(sd);
-    return finishSearch();
-}
-
-void
-FakeVimHandler::Private::highlightMatches(const QString &needle)
-{
-    g.lastNeedle        = needle;
-    g.highlightsCleared = false;
-    updateHighlights();
-}
-
-void
-FakeVimHandler::Private::moveToFirstNonBlankOnLine()
-{
-    g.movetype = MoveLineWise;
-    moveToFirstNonBlankOnLine(&m_cursor);
-    setTargetColumn();
-}
-
-void
-FakeVimHandler::Private::moveToFirstNonBlankOnLine(QTextCursor *tc)
-{
-    tc->setPosition(tc->block().position(), QTextCursor::KeepAnchor);
-    moveToNonBlankOnLine(tc);
-}
-
-void
-FakeVimHandler::Private::moveToFirstNonBlankOnLineVisually()
-{
-    moveToStartOfLineVisually();
-    moveToNonBlankOnLine(&m_cursor);
-    setTargetColumn();
-}
-
-void
-FakeVimHandler::Private::moveToNonBlankOnLine(QTextCursor *tc)
-{
-    const QTextBlock block = tc->block();
-    const int maxPos       = block.position() + block.length() - 1;
-    int i                  = tc->position();
-    while (characterAt(i).isSpace() && i < maxPos)
-        ++i;
-    tc->setPosition(i, QTextCursor::KeepAnchor);
-}
-
-void
-FakeVimHandler::Private::indentSelectedText(QChar typedChar)
-{
-    beginEditBlock();
-    setTargetColumn();
-    int beginLine = qMin(lineForPosition(position()), lineForPosition(anchor()));
-    int endLine   = qMax(lineForPosition(position()), lineForPosition(anchor()));
-
-    Range range(anchor(), position(), g.rangemode);
-    indentText(range, typedChar);
-
-    setPosition(firstPositionInLine(beginLine));
-    handleStartOfLine();
-    setTargetColumn();
-    setDotCommand("%1==", endLine - beginLine + 1);
-    endEditBlock();
-
-    const int lines = endLine - beginLine + 1;
-    if (lines > 2)
-        showMessage(MessageInfo, Tr::tr("%n lines indented.", nullptr, lines));
-}
-
-void
-FakeVimHandler::Private::indentText(const Range &range, QChar typedChar)
-{
-    int beginBlock = blockAt(range.beginPos).blockNumber();
-    int endBlock   = blockAt(range.endPos).blockNumber();
-    if (beginBlock > endBlock)
-        std::swap(beginBlock, endBlock);
-
-    // Don't remember current indentation in last text insertion.
-    const QString lastInsertion = m_buffer->lastInsertion;
-    q->indentRegion(beginBlock, endBlock, typedChar);
-    m_buffer->lastInsertion = lastInsertion;
-}
-
-bool
-FakeVimHandler::Private::isElectricCharacter(QChar c) const
-{
-    bool result = false;
-    q->checkForElectricCharacter(&result, c);
-    return result;
-}
-
-void
-FakeVimHandler::Private::shiftRegionRight(int repeat)
-{
-    int beginLine = lineForPosition(anchor());
-    int endLine   = lineForPosition(position());
-    int targetPos = anchor();
-    if (beginLine > endLine) {
-        std::swap(beginLine, endLine);
-        targetPos = position();
-    }
-    if (s.startOfLine.value())
-        targetPos = firstPositionInLine(beginLine);
-
-    const int sw = static_cast<int>(s.shiftWidth.value());
-    g.movetype   = MoveLineWise;
-    beginEditBlock();
-    QTextBlock block = document()->findBlockByLineNumber(beginLine - 1);
-    while (block.isValid() && lineNumber(block) <= endLine) {
-        const Column col = indentation(block.text());
-        QTextCursor tc   = m_cursor;
-        tc.setPosition(block.position());
-        if (col.physical > 0)
-            tc.setPosition(tc.position() + col.physical, QTextCursor::KeepAnchor);
-        tc.insertText(tabExpand(col.logical + sw * repeat));
-        block = block.next();
-    }
-    endEditBlock();
-
-    setPosition(targetPos);
-    handleStartOfLine();
-
-    const int lines = endLine - beginLine + 1;
-    if (lines > 2) {
-        showMessage(MessageInfo, Tr::tr("%n lines %1ed %2 time.", nullptr, lines)
-                                     .arg(repeat > 0 ? '>' : '<')
-                                     .arg(qAbs(repeat)));
-    }
-}
-
-void
-FakeVimHandler::Private::shiftRegionLeft(int repeat)
-{
-    shiftRegionRight(-repeat);
-}
-
-void
-FakeVimHandler::Private::moveToTargetColumn()
-{
-    const QTextBlock &bl = block();
-    const int pos        = lastPositionInLine(bl.blockNumber() + 1, false);
-    if (m_targetColumn == -1) {
-        setPosition(pos);
-        return;
-    }
-    const int physical = bl.position() + logicalToPhysicalColumn(m_targetColumn, bl.text());
-    setPosition(qMin(pos, physical));
-}
-
-void
-FakeVimHandler::Private::setTargetColumn()
-{
-    m_targetColumn       = logicalCursorColumn();
-    m_visualTargetColumn = m_targetColumn;
-
-    QTextCursor tc = m_cursor;
-    tc.movePosition(QTextCursor::StartOfLine);
-    m_targetColumnWrapped = m_cursor.position() - tc.position();
-}
-
-/* if simple is given:
- *  class 0: spaces
- *  class 1: non-spaces
- * else
- *  class 0: spaces
- *  class 1: non-space-or-letter-or-number
- *  class 2: letter-or-number
- */
-
-int
-FakeVimHandler::Private::charClass(QChar c, bool simple) const
-{
-    if (simple)
-        return c.isSpace() ? 0 : 1;
-    // FIXME: This means that only characters < 256 in the
-    // ConfigIsKeyword setting are handled properly.
-    if (c.unicode() < 256) {
-        //int old = (c.isLetterOrNumber() || c.unicode() == '_') ? 2
-        //    :  c.isSpace() ? 0 : 1;
-        //qDebug() << c.unicode() << old << m_charClass[c.unicode()];
-        return m_charClass[c.unicode()];
-    }
-    if (c.isLetterOrNumber() || c == '_')
-        return 2;
-    return c.isSpace() ? 0 : 1;
-}
-
-void
-FakeVimHandler::Private::miniBufferTextEdited(const QString &text, int cursorPos, int anchorPos)
-{
-    if (!isCommandLineMode()) {
-        editor()->setFocus();
-    } else if (text.isEmpty()) {
-        // editing cancelled
-        enterFakeVim();
-        handleDefaultKey(Input(Qt::Key_Escape, Qt::NoModifier, QString()));
-        leaveFakeVim();
-        editor()->setFocus();
-    } else {
-        CommandBuffer &cmdBuf = (g.mode == ExMode) ? g.commandBuffer : g.searchBuffer;
-        int pos               = qMax(1, cursorPos);
-        int anchor            = anchorPos == -1 ? pos : qMax(1, anchorPos);
-        QString buffer        = text;
-        // prepend prompt character if missing
-        if (!buffer.startsWith(cmdBuf.prompt())) {
-            buffer.prepend(cmdBuf.prompt());
-            ++pos;
-            ++anchor;
-        }
-        // update command/search buffer
-        cmdBuf.setContents(buffer.mid(1), pos - 1, anchor - 1);
-        if (pos != cursorPos || anchor != anchorPos || buffer != text)
-            q->commandBufferChanged(buffer, pos, anchor, 0);
-        // update search expression
-        if (g.subsubmode == SearchSubSubMode) {
-            updateFind(false);
-            commitCursor();
-        }
-    }
-}
-
-void
-FakeVimHandler::Private::pullOrCreateBufferData()
-{
-    const QVariant data = document()->property("FakeVimSharedData");
-    if (data.isValid()) {
-        // FakeVimHandler has been already created for this document (e.g. in other split).
-        m_buffer = data.value<BufferDataPtr>();
-    } else {
-        // FakeVimHandler has not been created for this document yet.
-        m_buffer = BufferDataPtr(new BufferData);
-        document()->setProperty("FakeVimSharedData", QVariant::fromValue(m_buffer));
-    }
-
-    if (editor()->hasFocus())
-        m_buffer->currentHandler = this;
-}
-
-// Helper to parse a-z,A-Z,48-57,_
-static int
-someInt(const QString &str)
-{
-    if (str.toInt())
-        return str.toInt();
-    if (!str.isEmpty())
-        return str.at(0).unicode();
-    return 0;
-}
-
-void
-FakeVimHandler::Private::setupCharClass()
-{
-    for (int i = 0; i < 256; ++i) {
-        const QChar c  = QLatin1Char(static_cast<char>(i));
-        m_charClass[i] = c.isSpace() ? 0 : 1;
-    }
-    const QString conf = s.isKeyword.value();
-    for (const QString &part : conf.split(',')) {
-        if (part.contains('-')) {
-            const int from = someInt(part.section('-', 0, 0));
-            const int to   = someInt(part.section('-', 1, 1));
-            for (int i = qMax(0, from); i <= qMin(255, to); ++i)
-                m_charClass[i] = 2;
-        } else {
-            m_charClass[qMin(255, someInt(part))] = 2;
-        }
-    }
-}
-
-void
-FakeVimHandler::Private::moveToBoundary(bool simple, bool forward)
-{
-    QTextCursor tc(document());
-    tc.setPosition(position());
-    if (forward ? tc.atBlockEnd() : tc.atBlockStart())
-        return;
-
-    QChar c                       = characterAt(tc.position() + (forward ? -1 : 1));
-    int lastClass                 = tc.atStart() ? -1 : charClass(c, simple);
-    QTextCursor::MoveOperation op = forward ? QTextCursor::Right : QTextCursor::Left;
-    while (true) {
-        c             = characterAt(tc.position());
-        int thisClass = charClass(c, simple);
-        if (thisClass != lastClass || (forward ? tc.atBlockEnd() : tc.atBlockStart())) {
-            if (tc != m_cursor)
-                tc.movePosition(forward ? QTextCursor::Left : QTextCursor::Right);
-            break;
-        }
-        lastClass = thisClass;
-        tc.movePosition(op);
-    }
-    setPosition(tc.position());
-}
-
-void
-FakeVimHandler::Private::moveToNextBoundary(bool end, int count, bool simple, bool forward)
-{
-    int repeat = count;
-    while (repeat > 0 && !(forward ? atDocumentEnd() : atDocumentStart())) {
-        setPosition(position() + (forward ? 1 : -1));
-        moveToBoundary(simple, forward);
-        if (atBoundary(end, simple))
-            --repeat;
-    }
-}
-
-void
-FakeVimHandler::Private::moveToNextBoundaryStart(int count, bool simple, bool forward)
-{
-    moveToNextBoundary(false, count, simple, forward);
-}
-
-void
-FakeVimHandler::Private::moveToNextBoundaryEnd(int count, bool simple, bool forward)
-{
-    moveToNextBoundary(true, count, simple, forward);
-}
-
-void
-FakeVimHandler::Private::moveToBoundaryStart(int count, bool simple, bool forward)
-{
-    moveToNextBoundaryStart(atBoundary(false, simple) ? count - 1 : count, simple, forward);
-}
-
-void
-FakeVimHandler::Private::moveToBoundaryEnd(int count, bool simple, bool forward)
-{
-    moveToNextBoundaryEnd(atBoundary(true, simple) ? count - 1 : count, simple, forward);
-}
-
-void
-FakeVimHandler::Private::moveToNextWord(bool end, int count, bool simple, bool forward,
-                                        bool emptyLines)
-{
-    int repeat = count;
-    while (repeat > 0 && !(forward ? atDocumentEnd() : atDocumentStart())) {
-        setPosition(position() + (forward ? 1 : -1));
-        moveToBoundary(simple, forward);
-        if (atWordBoundary(end, simple) && (emptyLines || !atEmptyLine()))
-            --repeat;
-    }
-}
-
-void
-FakeVimHandler::Private::moveToNextWordStart(int count, bool simple, bool forward, bool emptyLines)
-{
-    g.movetype = MoveExclusive;
-    moveToNextWord(false, count, simple, forward, emptyLines);
-    setTargetColumn();
-}
-
-void
-FakeVimHandler::Private::moveToNextWordEnd(int count, bool simple, bool forward, bool emptyLines)
-{
-    g.movetype = MoveInclusive;
-    moveToNextWord(true, count, simple, forward, emptyLines);
-    setTargetColumn();
-}
-
-void
-FakeVimHandler::Private::moveToWordStart(int count, bool simple, bool forward, bool emptyLines)
-{
-    moveToNextWordStart(atWordStart(simple) ? count - 1 : count, simple, forward, emptyLines);
-}
-
-void
-FakeVimHandler::Private::moveToWordEnd(int count, bool simple, bool forward, bool emptyLines)
-{
-    moveToNextWordEnd(atWordEnd(simple) ? count - 1 : count, simple, forward, emptyLines);
-}
-
-bool
-FakeVimHandler::Private::handleFfTt(const QString &key, bool repeats)
-{
-    int key0 = key.size() == 1 ? key.at(0).unicode() : 0;
-    // g.subsubmode \\in { 'f', 'F', 't', 'T' }
-    bool forward     = g.subsubdata.is('f') || g.subsubdata.is('t');
-    bool exclusive   = g.subsubdata.is('t') || g.subsubdata.is('T');
-    int repeat       = count();
-    int n            = block().position() + (forward ? block().length() : -1);
-    const int dLocal = forward ? 1 : -1;
-    // FIXME: This also depends on whether 'cpositions' Vim option contains ';'.
-    const int skip = (repeats && repeat == 1 && exclusive) ? dLocal : 0;
-    int pos        = position() + dLocal + skip;
-
-    for (; repeat > 0 && (forward ? pos < n : pos > n); pos += dLocal) {
-        if (characterAt(pos).unicode() == key0)
-            --repeat;
-    }
-
-    if (repeat == 0) {
-        setPosition(pos - dLocal - (exclusive ? dLocal : 0));
-        setTargetColumn();
-        return true;
-    }
-
-    return false;
-}
-
-void
-FakeVimHandler::Private::moveToMatchingParanthesis()
-{
-    bool moved   = false;
-    bool forward = false;
-
-    const int anc  = anchor();
-    QTextCursor tc = m_cursor;
-
-    // If no known parenthesis symbol is under cursor find one on the current line after cursor.
-    static const QString parenthesesChars("([{}])");
-    while (!parenthesesChars.contains(characterAt(tc.position())) && !tc.atBlockEnd())
-        tc.setPosition(tc.position() + 1);
-
-    if (tc.atBlockEnd())
-        tc = m_cursor;
-
-    q->moveToMatchingParenthesis(&moved, &forward, &tc);
-    if (moved) {
-        if (forward)
-            tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
-        setAnchorAndPosition(anc, tc.position());
-        setTargetColumn();
-    }
-}
-
-int
-FakeVimHandler::Private::cursorLineOnScreen() const
-{
-    if (!editor())
-        return 0;
-    const QRect rect = EDITOR(cursorRect(m_cursor));
-    return rect.height() > 0 ? rect.y() / rect.height() : 0;
-}
-
-int
-FakeVimHandler::Private::linesOnScreen() const
-{
-    if (!editor())
-        return 1;
-    const int h = EDITOR(cursorRect(m_cursor)).height();
-    return h > 0 ? EDITOR(viewport()->height()) / h : 1;
-}
-
-int
-FakeVimHandler::Private::cursorLine() const
-{
-    return lineForPosition(position()) - 1;
-}
-
-int
-FakeVimHandler::Private::cursorBlockNumber() const
-{
-    return blockAt(qMin(anchor(), position())).blockNumber();
-}
-
-int
-FakeVimHandler::Private::physicalCursorColumn() const
-{
-    return position() - block().position();
-}
-
-int
-FakeVimHandler::Private::physicalToLogicalColumn(const int physical, const QString &line) const
-{
-    const int ts = static_cast<int>(s.tabStop.value());
-    int p        = 0;
-    int logical  = 0;
-    while (p < physical) {
-        QChar c = line.at(p);
-        //if (c == ' ')
-        //    ++logical;
-        //else
-        if (c == '\t')
-            logical += ts - logical % ts;
-        else
-            ++logical;
-        //break;
-        ++p;
-    }
-    return logical;
-}
-
-int
-FakeVimHandler::Private::logicalToPhysicalColumn(const int logical, const QString &line) const
-{
-    const int ts = static_cast<int>(s.tabStop.value());
-    int physical = 0;
-    for (int l = 0; l < logical && physical < line.size(); ++physical) {
-        QChar c = line.at(physical);
-        if (c == '\t')
-            l += ts - l % ts;
-        else
-            ++l;
-    }
-    return physical;
-}
-
-int
-FakeVimHandler::Private::windowScrollOffset() const
-{
-    return qMin(static_cast<int>(s.scrollOff.value()), linesOnScreen() / 2);
-}
-
-int
-FakeVimHandler::Private::logicalCursorColumn() const
-{
-    const int physical = physicalCursorColumn();
-    const QString line = block().text();
-    return physicalToLogicalColumn(physical, line);
-}
-
-Column
-FakeVimHandler::Private::cursorColumn() const
-{
-    return Column(physicalCursorColumn(), logicalCursorColumn());
-}
-
-int
-FakeVimHandler::Private::linesInDocument() const
-{
-    if (m_cursor.isNull())
-        return 0;
-    return document()->blockCount();
-}
-
-void
-FakeVimHandler::Private::scrollToLine(int line)
-{
-    // Don't scroll if the line is already at the top.
-    updateFirstVisibleLine();
-    if (line == m_firstVisibleLine)
-        return;
-
-    const QTextCursor tc = m_cursor;
-
-    QTextCursor tc2 = tc;
-    tc2.setPosition(document()->lastBlock().position());
-    EDITOR(setTextCursor(tc2));
-    EDITOR(ensureCursorVisible());
-
-    int offset             = 0;
-    const QTextBlock block = document()->findBlockByLineNumber(line);
-    if (block.isValid()) {
-        const int blockLineCount = block.layout()->lineCount();
-        const int lineInBlock    = line - block.firstLineNumber();
-        if (0 <= lineInBlock && lineInBlock < blockLineCount) {
-            QTextLine textLine = block.layout()->lineAt(lineInBlock);
-            offset             = textLine.textStart();
-        } else {
-            //            QTC_CHECK(false);
-        }
-    }
-    tc2.setPosition(block.position() + offset);
-    EDITOR(setTextCursor(tc2));
-    EDITOR(ensureCursorVisible());
-
-    EDITOR(setTextCursor(tc));
-
-    m_firstVisibleLine = line;
-}
-
-void
-FakeVimHandler::Private::updateFirstVisibleLine()
-{
-    const QTextCursor tc = EDITOR(cursorForPosition(QPoint(0, 0)));
-    m_firstVisibleLine   = lineForPosition(tc.position()) - 1;
-}
-
-int
-FakeVimHandler::Private::firstVisibleLine() const
-{
-    return m_firstVisibleLine;
-}
-
-int
-FakeVimHandler::Private::lastVisibleLine() const
-{
-    const int line         = m_firstVisibleLine + linesOnScreen();
-    const QTextBlock block = document()->findBlockByLineNumber(line);
-    return block.isValid() ? line : document()->lastBlock().firstLineNumber();
-}
-
-int
-FakeVimHandler::Private::lineOnTop(int count) const
-{
-    const int scrollOffset = qMax(count - 1, windowScrollOffset());
-    const int line         = firstVisibleLine();
-    return line == 0 ? count - 1 : scrollOffset + line;
-}
-
-int
-FakeVimHandler::Private::lineOnBottom(int count) const
-{
-    const int scrollOffset = qMax(count - 1, windowScrollOffset());
-    const int line         = lastVisibleLine();
-    return line >= document()->lastBlock().firstLineNumber() ? line - count + 1
-                                                             : line - scrollOffset - 1;
-}
-
-void
-FakeVimHandler::Private::scrollUp(int count)
-{
-    scrollToLine(cursorLine() - cursorLineOnScreen() - count);
-}
-
-void
-FakeVimHandler::Private::updateScrollOffset()
-{
-    const int line = cursorLine();
-    if (line < lineOnTop())
-        scrollToLine(qMax(0, line - windowScrollOffset()));
-    else if (line > lineOnBottom())
-        scrollToLine(firstVisibleLine() + line - lineOnBottom());
-}
-
-void
-FakeVimHandler::Private::alignViewportToCursor(AlignmentFlag align, int line, bool moveToNonBlank)
-{
-    if (line > 0)
-        setPosition(firstPositionInLine(line));
-    if (moveToNonBlank)
-        moveToFirstNonBlankOnLine();
-
-    if (align == Qt::AlignTop)
-        scrollUp(-cursorLineOnScreen());
-    else if (align == Qt::AlignVCenter)
-        scrollUp(linesOnScreen() / 2 - cursorLineOnScreen());
-    else if (align == Qt::AlignBottom)
-        scrollUp(linesOnScreen() - cursorLineOnScreen() - 1);
-}
-
-int
-FakeVimHandler::Private::lineToBlockNumber(int line) const
-{
-    return document()->findBlockByLineNumber(line).blockNumber();
-}
-
-void
-FakeVimHandler::Private::setCursorPosition(const CursorPosition &p)
-{
-    const int firstLine  = firstVisibleLine();
-    const int firstBlock = lineToBlockNumber(firstLine);
-    const int lastBlock  = lineToBlockNumber(firstLine + linesOnScreen() - 2);
-    bool isLineVisible   = firstBlock <= p.line && p.line <= lastBlock;
-    setCursorPosition(&m_cursor, p);
-    if (!isLineVisible)
-        alignViewportToCursor(Qt::AlignVCenter);
-}
-
-void
-FakeVimHandler::Private::setCursorPosition(QTextCursor *tc, const CursorPosition &p)
-{
-    const int line   = qMin(document()->blockCount() - 1, p.line);
-    QTextBlock block = document()->findBlockByNumber(line);
-    const int column = qMin(p.column, block.length() - 1);
-    tc->setPosition(block.position() + column, QTextCursor::KeepAnchor);
-}
-
-int
-FakeVimHandler::Private::lastPositionInDocument(bool ignoreMode) const
-{
-    return document()->characterCount() - (ignoreMode || isVisualMode() || isInsertMode() ? 1 : 2);
-}
-
-QString
-FakeVimHandler::Private::selectText(const Range &range) const
-{
-    QString contents;
-    const QString lineEnd = range.rangemode == RangeBlockMode ? QString('\n') : QString();
-    QTextCursor tc        = m_cursor;
-    transformText(range, tc, [&tc, &contents, &lineEnd]() {
-        contents.append(tc.selection().toPlainText() + lineEnd);
-    });
-    return contents;
-}
-
-void
-FakeVimHandler::Private::yankText(const Range &range, int reg)
-{
-    const QString text = selectText(range);
-    setRegister(reg, text, range.rangemode);
-
-    // If register is not specified or " ...
-    if (m_register == '"') {
-        // with delete and change commands set register 1 (if text contains more lines) or
-        // small delete register -
-        if (g.submode == DeleteSubMode || g.submode == ChangeSubMode) {
-            if (text.contains('\n'))
-                setRegister('1', text, range.rangemode);
-            else
-                setRegister('-', text, range.rangemode);
-        } else {
-            // copy to yank register 0 too
-            setRegister('0', text, range.rangemode);
-        }
-    } else if (m_register != '_') {
-        // Always copy to " register too (except black hole register).
-        setRegister('"', text, range.rangemode);
-    }
-
-    const int lines =
-        blockAt(range.endPos).blockNumber() - blockAt(range.beginPos).blockNumber() + 1;
-    if (lines > 2)
-        showMessage(MessageInfo, Tr::tr("%n lines yanked.", nullptr, lines));
-}
-
-void
-FakeVimHandler::Private::transformText(const Range &range, QTextCursor &tc,
-                                       const std::function<void()> &transform) const
-{
-    switch (range.rangemode) {
-    case RangeCharMode: {
-        // This can span multiple lines.
-        tc.setPosition(range.beginPos, QTextCursor::MoveAnchor);
-        tc.setPosition(range.endPos, QTextCursor::KeepAnchor);
-        transform();
-        tc.setPosition(range.beginPos);
-        break;
-    }
-    case RangeLineMode:
-    case RangeLineModeExclusive: {
-        tc.setPosition(range.beginPos, QTextCursor::MoveAnchor);
-        tc.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
-        tc.setPosition(range.endPos, QTextCursor::KeepAnchor);
-        tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
-        if (range.rangemode != RangeLineModeExclusive) {
-            // make sure that complete lines are removed
-            // - also at the beginning and at the end of the document
-            if (tc.atEnd()) {
-                tc.setPosition(range.beginPos, QTextCursor::MoveAnchor);
-                tc.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
-                if (!tc.atStart()) {
-                    // also remove first line if it is the only one
-                    tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
-                    tc.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor, 1);
-                }
-                tc.setPosition(range.endPos, QTextCursor::KeepAnchor);
-                tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
-            } else {
-                tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
-            }
-        }
-        const int posAfter = tc.anchor();
-        transform();
-        tc.setPosition(posAfter);
-        break;
-    }
-    case RangeBlockAndTailMode:
-    case RangeBlockMode: {
-        int beginColumn = columnAt(range.beginPos);
-        int endColumn   = columnAt(range.endPos);
-        if (endColumn < beginColumn)
-            std::swap(beginColumn, endColumn);
-        if (range.rangemode == RangeBlockAndTailMode)
-            endColumn = INT_MAX - 1;
-        QTextBlock block           = document()->findBlock(range.beginPos);
-        const QTextBlock lastBlock = document()->findBlock(range.endPos);
-        while (block.isValid() && block.position() <= lastBlock.position()) {
-            int bCol = qMin(beginColumn, block.length() - 1);
-            int eCol = qMin(endColumn + 1, block.length() - 1);
-            tc.setPosition(block.position() + bCol, QTextCursor::MoveAnchor);
-            tc.setPosition(block.position() + eCol, QTextCursor::KeepAnchor);
-            transform();
-            block = block.next();
-        }
-        tc.setPosition(range.beginPos);
-        break;
-    }
-    }
-}
-
-void
-FakeVimHandler::Private::transformText(const Range &range, const Transformation &transform)
-{
-    beginEditBlock();
-    transformText(range, m_cursor, [this, &transform] {
-        m_cursor.insertText(transform(m_cursor.selection().toPlainText()));
-    });
-    endEditBlock();
-    setTargetColumn();
-}
-
-void
-FakeVimHandler::Private::insertText(QTextCursor &tc, const QString &text)
-{
-    if (s.passKeys.value()) {
-        if (tc.hasSelection() && text.isEmpty()) {
-            QKeyEvent event(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier, QString());
-            passEventToEditor(event, tc);
-        }
-
-        for (QChar c : text) {
-            QKeyEvent event(QEvent::KeyPress, -1, Qt::NoModifier, QString(c));
-            passEventToEditor(event, tc);
-        }
-    } else {
-        tc.insertText(text);
-    }
-}
-
-void
-FakeVimHandler::Private::insertText(const Register &reg)
-{
-    if (reg.rangemode != RangeCharMode) {
-        logWarning() << "WRONG INSERT MODE: " << reg.rangemode;
-        return;
-    }
-    setAnchor();
-    m_cursor.insertText(reg.contents);
-    //dump("AFTER INSERT");
-}
-
-void
-FakeVimHandler::Private::removeText(const Range &range)
-{
-    transformText(range, [](const QString &) { return QString(); });
-}
-
-void
-FakeVimHandler::Private::downCase(const Range &range)
-{
-    transformText(range, [](const QString &text) { return text.toLower(); });
-}
-
-void
-FakeVimHandler::Private::upCase(const Range &range)
-{
-    transformText(range, [](const QString &text) { return text.toUpper(); });
-}
-
-void
-FakeVimHandler::Private::invertCase(const Range &range)
-{
-    transformText(range, [](const QString &text) -> QString {
-        QString result = text;
-        for (int i = 0; i < result.length(); ++i) {
-            const QChar c = result[i];
-            result[i]     = c.isUpper() ? c.toLower() : c.toUpper();
-        }
-        return result;
-    });
-}
-
-void
-FakeVimHandler::Private::toggleComment(const Range &range)
-{
-    static const QMap<QString, QString> extensionToCommentString{
-        { "pri", "#" }, { "pro", "#" }, { "h", "//" }, { "hpp", "//" }, { "cpp", "//" },
-    };
-    const QString commentString =
-        extensionToCommentString.value(QFileInfo(m_currentFileName).suffix(), "//");
-
-    transformText(range, [&commentString](const QString &text) -> QString {
-        QStringList lines = text.split('\n');
-
-        const QRegularExpression checkForComment("^\\s*" +
-                                                 QRegularExpression::escape(commentString));
-
-        const bool firstLineIsComment = !lines.empty() && lines.front().contains(checkForComment);
-
-        for (auto &line : lines) {
-            if (!line.isEmpty()) {
-                if (firstLineIsComment) {
-                    const bool hasSpaceAfterCommentString =
-                        line.contains(QRegularExpression(checkForComment.pattern() + "\\s"));
-                    const qsizetype sizeToReplace = hasSpaceAfterCommentString ? commentString.size() + 1
-                                                                         : commentString.size();
-                    line.replace(line.indexOf(commentString), sizeToReplace, "");
-                } else {
-                    const qsizetype indexOfFirstNonSpace = line.indexOf(QRegularExpression("[^\\s]"));
-                    line = line.left(indexOfFirstNonSpace) + commentString + " " +
-                           line.right(line.size() - indexOfFirstNonSpace);
-                }
-            }
-        }
-
-        return lines.size() == 1 ? lines.front() : lines.join("\n");
-    });
-}
-
-void
-FakeVimHandler::Private::exchangeRange(const Range &range)
-{
-    if (g.exchangeRange) {
-        pushUndoState(false);
-        beginEditBlock();
-
-        Range leftRange  = *g.exchangeRange;
-        Range rightRange = range;
-        if (leftRange.beginPos > rightRange.beginPos)
-            std::swap(leftRange, rightRange);
-
-        // First replace the right range, then left one
-        // If we did it the other way around, we would invalidate the positions
-        // of the right range
-        const QString rightText = selectText(rightRange);
-        replaceText(rightRange, selectText(leftRange));
-        replaceText(leftRange, rightText);
-
-        g.exchangeRange.reset();
-
-        endEditBlock();
-    } else {
-        g.exchangeRange = range;
-    }
-}
-
-void
-FakeVimHandler::Private::replaceWithRegister(const Range &range)
-{
-    replaceText(range, registerContents(m_register));
-}
-
-void
-FakeVimHandler::Private::surroundCurrentRange(const Input &input, const QString &prefix)
-{
-    QString dotCommand;
-    if (isVisualMode())
-        dotCommand = visualDotCommand() + "S" + input.asChar();
-
-    const bool wasVisualCharMode = isVisualCharMode();
-    const bool wasVisualLineMode = isVisualLineMode();
-    leaveVisualMode();
-
-    if (dotCommand.isEmpty()) { // i.e. we came from normal mode
-        dotCommand = dotCommandFromSubMode(g.submode) +
-                     QLatin1Char(g.surroundUpperCaseS ? 'S' : 's') + g.dotCommand + input.asChar();
-    }
-
-    if (wasVisualCharMode)
-        setPosition(position() + 1);
-
-    QString newFront, newBack;
-
-    if (input.is('(') || input.is(')') || input.is('b')) {
-        newFront = '(';
-        newBack  = ')';
-    } else if (input.is('{') || input.is('}') || input.is('B')) {
-        newFront = '{';
-        newBack  = '}';
-    } else if (input.is('[') || input.is(']')) {
-        newFront = '[';
-        newBack  = ']';
-    } else if (input.is('<') || input.is('>') || input.is('t')) {
-        newFront = '<';
-        newBack  = '>';
-    } else if (input.is('"') || input.is('\'') || input.is('`')) {
-        newFront = input.asChar();
-        newBack  = input.asChar();
-    }
-
-    if (g.surroundUpperCaseS || wasVisualLineMode) {
-        // yS and cS add a new line before and after the surrounded text
-        newFront += "\n";
-        if (wasVisualLineMode)
-            newBack += "\n";
-        else
-            newBack = "\n" + newBack;
-    } else if (input.is('(') || input.is('{') || input.is('[') || input.is('[')) {
-        // Opening characters add an extra space
-        newFront = newFront + " ";
-        newBack  = " " + newBack;
-    }
-
-    if (!newFront.isEmpty()) {
-        transformText(currentRange(), [&](QString text) -> QString {
-            if (newFront == QChar())
-                return text.mid(1, text.size() - 2);
-
-            const QString newMiddle =
-                (g.submode == ChangeSurroundingSubMode) ? text.mid(1, text.size() - 2) : text;
-
-            return prefix + newFront + newMiddle + newBack;
-        });
-    }
-
-    // yS, cS and VS also indent the surrounded text
-    if (g.surroundUpperCaseS || wasVisualLineMode)
-        replay(QStringLiteral("=a") + input.asChar());
-
-    // Indenting has changed the dotCommand, so now set it back to the correct one
-    g.dotCommand = dotCommand;
-}
-
-void
-FakeVimHandler::Private::replaceText(const Range &range, const QString &str)
-{
-    transformText(range, [&str](const QString &) { return str; });
-}
-
-void
-FakeVimHandler::Private::pasteText(bool afterCursor)
-{
-    const QString text        = registerContents(m_register);
-    const RangeMode rangeMode = registerRangeMode(m_register);
-
-    beginEditBlock();
-
-    // In visual mode paste text only inside selection.
-    bool pasteAfter = isVisualMode() ? false : afterCursor;
-
-    if (isVisualMode())
-        cutSelectedText(g.submode == ReplaceWithRegisterSubMode ? '-' : '"');
-
-    switch (rangeMode) {
-    case RangeCharMode: {
-        m_targetColumn = 0;
-        const int pos  = position() + 1;
-        if (pasteAfter && rightDist() > 0)
-            moveRight();
-        insertText(text.repeated(count()));
-        if (text.contains('\n'))
-            setPosition(pos);
-        else
-            moveLeft();
-        break;
-    }
-    case RangeLineMode:
-    case RangeLineModeExclusive: {
-        QTextCursor tc = m_cursor;
-        moveToStartOfLine();
-        m_targetColumn = 0;
-        bool lastLine  = false;
-        if (pasteAfter) {
-            lastLine = document()->lastBlock() == this->block();
-            if (lastLine) {
-                tc.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor);
-                tc.insertBlock();
-            }
-            moveDown();
-        }
-        const int pos = position();
-        if (lastLine)
-            insertText(text.repeated(count()).left(text.size() * count() - 1));
-        else
-            insertText(text.repeated(count()));
-        setPosition(pos);
-        moveToFirstNonBlankOnLine();
-        break;
-    }
-    case RangeBlockAndTailMode:
-    case RangeBlockMode: {
-        const int pos = position();
-        if (pasteAfter && rightDist() > 0)
-            moveRight();
-        QTextCursor tc          = m_cursor;
-        const int col           = tc.columnNumber();
-        QTextBlock block        = tc.block();
-        const QStringList lines = text.split('\n');
-        for (int i = 0; i < lines.size() - 1; ++i) {
-            if (!block.isValid()) {
-                tc.movePosition(QTextCursor::End);
-                tc.insertBlock();
-                block = tc.block();
-            }
-
-            // resize line
-            int length = block.length();
-            int begin  = block.position();
-            if (col >= length) {
-                tc.setPosition(begin + length - 1);
-                tc.insertText(QString(col - length + 1, ' '));
-            } else {
-                tc.setPosition(begin + col);
-            }
-
-            // insert text
-            const QString line = lines.at(i).repeated(count());
-            tc.insertText(line);
-
-            // next line
-            block = block.next();
-        }
-        setPosition(pos);
-        if (pasteAfter)
-            moveRight();
-        break;
-    }
-    }
-
-    endEditBlock();
-}
-
-void
-FakeVimHandler::Private::cutSelectedText(int reg)
-{
-    pushUndoState();
-
-    bool visualMode = isVisualMode();
-    leaveVisualMode();
-
-    Range range = currentRange();
-    if (visualMode && g.rangemode == RangeCharMode)
-        ++range.endPos;
-
-    if (!reg)
-        reg = m_register;
-
-    g.submode = DeleteSubMode;
-    yankText(range, reg);
-    removeText(range);
-    g.submode = NoSubMode;
-
-    if (g.rangemode == RangeLineMode)
-        handleStartOfLine();
-    else if (g.rangemode == RangeBlockMode)
-        setPosition(qMin(position(), anchor()));
-}
-
-void
-FakeVimHandler::Private::joinLines(int count, bool preserveSpace)
-{
-    int pos               = position();
-    const int blockNumber = m_cursor.blockNumber();
-
-    const QString currentLine = lineContents(blockNumber + 1);
-    const bool startingLineIsComment =
-        currentLine.contains(QRegularExpression("^\\s*\\/\\/"))     // Cpp-style
-        || currentLine.contains(QRegularExpression("^\\s*\\/?\\*")) // C-style
-        || currentLine.contains(QRegularExpression("^\\s*#"));      // Python/Shell-style
-
-    for (int i = qMax(count - 2, 0); i >= 0 && blockNumber < document()->blockCount(); --i) {
-        moveBehindEndOfLine();
-        pos = position();
-        setAnchor();
-        moveRight();
-        if (preserveSpace) {
-            removeText(currentRange());
-        } else {
-            while (characterAtCursor() == ' ' || characterAtCursor() == '\t')
-                moveRight();
-
-            // If the line we started from is a comment, remove the comment string from the next line
-            if (startingLineIsComment && s.formatOptions.value().contains('f')) {
-                if (characterAtCursor() == '/' && characterAt(position() + 1) == '/')
-                    moveRight(2);
-                else if (characterAtCursor() == '*' || characterAtCursor() == '#')
-                    moveRight(1);
-
-                if (characterAtCursor() == ' ')
-                    moveRight();
-            }
-
-            m_cursor.insertText(QString(' '));
-        }
-    }
-    setPosition(pos);
-}
-
-void
-FakeVimHandler::Private::insertNewLine()
-{
-    if (m_buffer->editBlockLevel <= 1 && s.passKeys.value()) {
-        QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "\n");
-        if (passEventToEditor(event, m_cursor))
-            return;
-    }
-
-    insertText(QString("\n"));
-    insertAutomaticIndentation(true);
-}
-
-bool
-FakeVimHandler::Private::handleInsertInEditor(const Input &input)
-{
-    if (m_buffer->editBlockLevel > 0 || !s.passKeys.value())
-        return false;
-
-    joinPreviousEditBlock();
-
-    QKeyEvent event(QEvent::KeyPress, input.key(), input.modifiers(), input.text());
-    setAnchor();
-    if (!passEventToEditor(event, m_cursor))
-        return !m_textedit && !m_plaintextedit; // Mark event as handled if it has destroyed editor.
-
-    endEditBlock();
-
-    setTargetColumn();
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::passEventToEditor(QEvent &event, QTextCursor &tc)
-{
-    removeEventFilter();
-    q->requestDisableBlockSelection();
-
-    setThinCursor();
-    EDITOR(setTextCursor(tc));
-
-    bool accepted = QApplication::sendEvent(editor(), &event);
-    if (!m_textedit && !m_plaintextedit)
-        return false;
-
-    if (accepted)
-        tc = editorCursor();
-
-    return accepted;
-}
-
-QString
-FakeVimHandler::Private::lineContents(int line) const
-{
-    return document()->findBlockByLineNumber(line - 1).text();
-}
-
-QString
-FakeVimHandler::Private::textAt(int from, int to) const
-{
-    QTextCursor tc(document());
-    tc.setPosition(from);
-    tc.setPosition(to, QTextCursor::KeepAnchor);
-    return tc.selectedText().replace(QChar::ParagraphSeparator, '\n');
-}
-
-void
-FakeVimHandler::Private::setLineContents(int line, const QString &contents)
-{
-    QTextBlock block = document()->findBlockByLineNumber(line - 1);
-    QTextCursor tc   = m_cursor;
-    const int begin  = block.position();
-    const int len    = block.length();
-    tc.setPosition(begin);
-    tc.setPosition(begin + len - 1, QTextCursor::KeepAnchor);
-    tc.insertText(contents);
-}
-
-int
-FakeVimHandler::Private::blockBoundary(const QString &left, const QString &right, bool closing,
-                                       int count) const
-{
-    const QString &begin = closing ? left : right;
-    const QString &end   = closing ? right : left;
-
-    // shift cursor if it is already on opening/closing string
-    QTextCursor tc1 = m_cursor;
-    int pos         = tc1.position();
-    int max         = document()->characterCount();
-    int sz          = int(left.size());
-    int from        = qMax(pos - sz + 1, 0);
-    int to          = qMin(pos + sz, max);
-    tc1.setPosition(from);
-    tc1.setPosition(to, QTextCursor::KeepAnchor);
-    int i = int(tc1.selectedText().indexOf(left));
-    if (i != -1) {
-        // - on opening string:
-        tc1.setPosition(from + i + sz);
-    } else {
-        sz   = int(right.size());
-        from = qMax(pos - sz + 1, 0);
-        to   = qMin(pos + sz, max);
-        tc1.setPosition(from);
-        tc1.setPosition(to, QTextCursor::KeepAnchor);
-        i = int(tc1.selectedText().indexOf(right));
-        if (i != -1) {
-            // - on closing string:
-            tc1.setPosition(from + i);
-        } else {
-            tc1 = m_cursor;
-        }
-    }
-
-    QTextCursor tc2 = tc1;
-    QTextDocument::FindFlags flags(closing ? 0 : QTextDocument::FindBackward);
-    int level   = 0;
-    int counter = 0;
-    while (true) {
-        tc2 = document()->find(end, tc2, flags);
-        if (tc2.isNull())
-            return -1;
-        if (!tc1.isNull())
-            tc1 = document()->find(begin, tc1, flags);
-
-        while (!tc1.isNull() && (closing ? (tc1 < tc2) : (tc2 < tc1))) {
-            ++level;
-            tc1 = document()->find(begin, tc1, flags);
-        }
-
-        while (level > 0 && (tc1.isNull() || (closing ? (tc2 < tc1) : (tc1 < tc2)))) {
-            --level;
-            tc2 = document()->find(end, tc2, flags);
-            if (tc2.isNull())
-                return -1;
-        }
-
-        if (level == 0 && (tc1.isNull() || (closing ? (tc2 < tc1) : (tc1 < tc2)))) {
-            ++counter;
-            if (counter >= count)
-                break;
-        }
-    }
-
-    return tc2.position() - int(end.size());
-}
-
-int
-FakeVimHandler::Private::lineNumber(const QTextBlock &block) const
-{
-    if (block.isVisible())
-        return block.firstLineNumber() + 1;
-
-    // Folded block has line number of the nearest previous visible line.
-    QTextBlock block2 = block;
-    while (block2.isValid() && !block2.isVisible())
-        block2 = block2.previous();
-    return block2.firstLineNumber() + 1;
-}
-
-int
-FakeVimHandler::Private::columnAt(int pos) const
-{
-    return pos - blockAt(pos).position();
-}
-
-int
-FakeVimHandler::Private::blockNumberAt(int pos) const
-{
-    return blockAt(pos).blockNumber();
-}
-
-QTextBlock
-FakeVimHandler::Private::blockAt(int pos) const
-{
-    return document()->findBlock(pos);
-}
-
-QTextBlock
-FakeVimHandler::Private::nextLine(const QTextBlock &block) const
-{
-    return blockAt(block.position() + block.length());
-}
-
-QTextBlock
-FakeVimHandler::Private::previousLine(const QTextBlock &block) const
-{
-    return blockAt(block.position() - 1);
-}
-
-int
-FakeVimHandler::Private::firstPositionInLine(int line, bool onlyVisibleLines) const
-{
-    QTextBlock block = onlyVisibleLines ? document()->findBlockByLineNumber(line - 1)
-                                        : document()->findBlockByNumber(line - 1);
-    return block.position();
-}
-
-int
-FakeVimHandler::Private::lastPositionInLine(int line, bool onlyVisibleLines) const
-{
-    QTextBlock block;
-    if (onlyVisibleLines) {
-        block = document()->findBlockByLineNumber(line - 1);
-        // respect folds and wrapped lines
-        do {
-            block = nextLine(block);
-        } while (block.isValid() && !block.isVisible());
-        if (block.isValid()) {
-            if (line > 0)
-                block = block.previous();
-        } else {
-            block = document()->lastBlock();
-        }
-    } else {
-        block = document()->findBlockByNumber(line - 1);
-    }
-
-    const int position = block.position() + block.length() - 1;
-    if (block.length() > 1 && !isVisualMode() && !isInsertMode())
-        return position - 1;
-    return position;
-}
-
-int
-FakeVimHandler::Private::lineForPosition(int pos) const
-{
-    const QTextBlock block = blockAt(pos);
-    if (!block.isValid())
-        return 0;
-    const int positionInBlock   = pos - block.position();
-    const int lineNumberInBlock = block.layout()->lineForTextPosition(positionInBlock).lineNumber();
-    return block.firstLineNumber() + lineNumberInBlock + 1;
-}
-
-void
-FakeVimHandler::Private::toggleVisualMode(VisualMode visualMode)
-{
-    if (visualMode == g.visualMode) {
-        leaveVisualMode();
-    } else {
-        m_positionPastEnd        = false;
-        m_anchorPastEnd          = false;
-        g.visualMode             = visualMode;
-        m_buffer->lastVisualMode = visualMode;
-    }
-}
-
-void
-FakeVimHandler::Private::leaveVisualMode()
-{
-    if (!isVisualMode())
-        return;
-
-    if (isVisualLineMode()) {
-        g.rangemode = RangeLineMode;
-        g.movetype  = MoveLineWise;
-    } else if (isVisualCharMode()) {
-        g.rangemode = RangeCharMode;
-        g.movetype  = MoveInclusive;
-    } else if (isVisualBlockMode()) {
-        g.rangemode = m_visualTargetColumn == -1 ? RangeBlockAndTailMode : RangeBlockMode;
-        g.movetype  = MoveInclusive;
-    }
-
-    g.visualMode = NoVisualMode;
-}
-
-void
-FakeVimHandler::Private::saveLastVisualMode()
-{
-    if (isVisualMode() && g.mode == CommandMode && g.submode == NoSubMode) {
-        setMark('<', markLessPosition());
-        setMark('>', markGreaterPosition());
-        m_buffer->lastVisualModeInverted = anchor() > position();
-        m_buffer->lastVisualMode         = g.visualMode;
-    }
-}
-
-QWidget *
-FakeVimHandler::Private::editor() const
-{
-    return m_textedit ? static_cast<QWidget *>(m_textedit)
-                      : static_cast<QWidget *>(m_plaintextedit);
-}
-
-void
-FakeVimHandler::Private::joinPreviousEditBlock()
-{
-    UNDO_DEBUG("JOIN");
-    if (m_buffer->breakEditBlock) {
-        beginEditBlock();
-        QTextCursor tc(m_cursor);
-        tc.setPosition(tc.position());
-        tc.beginEditBlock();
-        tc.insertText("X");
-        tc.deletePreviousChar();
-        tc.endEditBlock();
-        m_buffer->breakEditBlock = false;
-    } else {
-        if (m_buffer->editBlockLevel == 0 && !m_buffer->undo.empty())
-            m_buffer->undoState = m_buffer->undo.pop();
-        beginEditBlock();
-    }
-}
-
-void
-FakeVimHandler::Private::beginEditBlock(bool largeEditBlock)
-{
-    UNDO_DEBUG("BEGIN EDIT BLOCK" << m_buffer->editBlockLevel + 1);
-    if (!largeEditBlock && !m_buffer->undoState.isValid())
-        pushUndoState(false);
-    if (m_buffer->editBlockLevel == 0)
-        m_buffer->breakEditBlock = true;
-    ++m_buffer->editBlockLevel;
-}
-
-void
-FakeVimHandler::Private::endEditBlock()
-{
-    UNDO_DEBUG("END EDIT BLOCK" << m_buffer->editBlockLevel);
-    if (m_buffer->editBlockLevel <= 0) {
-        logWarning() << "beginEditBlock() not called before endEditBlock()!";
-        return;
-    }
-    --m_buffer->editBlockLevel;
-    if (m_buffer->editBlockLevel == 0 && m_buffer->undoState.isValid()) {
-        m_buffer->undo.push(m_buffer->undoState);
-        m_buffer->undoState = State();
-    }
-    if (m_buffer->editBlockLevel == 0)
-        m_buffer->breakEditBlock = false;
-}
-
-void
-FakeVimHandler::Private::onContentsChanged(int position, int charsRemoved, int charsAdded)
-{
-    // Record inserted and deleted text in insert mode.
-    if (isInsertMode() && (charsAdded > 0 || charsRemoved > 0) && canModifyBufferData()) {
-        BufferData::InsertState &insertState = m_buffer->insertState;
-        const int oldPosition                = insertState.pos2;
-        if (!isInsertStateValid()) {
-            insertState.pos1 = oldPosition;
-            g.dotCommand     = "i";
-            resetCount();
-        }
-
-        // Ignore changes outside inserted text (e.g. renaming other occurrences of a variable).
-        if (position + charsRemoved >= insertState.pos1 && position <= insertState.pos2) {
-            if (charsRemoved > 0) {
-                // Assume that in a manual edit operation a text can be removed only
-                // in front of cursor (<DELETE>) or behind it (<BACKSPACE>).
-
-                // If the recorded amount of backspace/delete keys doesn't correspond with
-                // number of removed characters, assume that the document has been changed
-                // externally and invalidate current insert state.
-
-                const bool wholeDocumentChanged = charsRemoved > 1 && charsAdded > 0 &&
-                                                  charsAdded + 1 == document()->characterCount();
-
-                if (position < insertState.pos1) {
-                    // <BACKSPACE>
-                    const int backspaceCount = insertState.pos1 - position;
-                    if (backspaceCount != charsRemoved ||
-                        (oldPosition == charsRemoved && wholeDocumentChanged)) {
-                        invalidateInsertState();
-                    } else {
-                        const QString inserted = textAt(position, oldPosition);
-                        const QString removed  = insertState.textBeforeCursor.right(backspaceCount);
-                        // Ignore backspaces if same text was just inserted.
-                        if (!inserted.endsWith(removed)) {
-                            insertState.backspaces += backspaceCount;
-                            insertState.pos1 = position;
-                            insertState.pos2 = qMax(position, insertState.pos2 - backspaceCount);
-                        }
-                    }
-                } else if (position + charsRemoved > insertState.pos2) {
-                    // <DELETE>
-                    const int deleteCount = position + charsRemoved - insertState.pos2;
-                    if (deleteCount != charsRemoved || (oldPosition == 0 && wholeDocumentChanged))
-                        invalidateInsertState();
-                    else
-                        insertState.deletes += deleteCount;
-                }
-            } else if (charsAdded > 0 && insertState.insertingSpaces) {
-                for (int i = position; i < position + charsAdded; ++i) {
-                    const QChar c = characterAt(i);
-                    if (c.unicode() == ' ' || c.unicode() == '\t')
-                        insertState.spaces.insert(i);
-                }
-            }
-
-            const int newPosition = position + charsAdded;
-            insertState.pos2      = qMax(insertState.pos2 + charsAdded - charsRemoved, newPosition);
-            insertState.textBeforeCursor = textAt(block().position(), newPosition);
-        }
-    }
-
-    if (!m_highlighted.isEmpty())
-        q->highlightMatches(m_highlighted);
-}
-
-void
-FakeVimHandler::Private::onCursorPositionChanged()
-{
-    if (!m_inFakeVim) {
-        m_cursorNeedsUpdate = true;
-
-        // Selecting text with mouse disables the thick cursor so it's more obvious
-        // that extra character under cursor is not selected when moving text around or
-        // making operations on text outside FakeVim mode.
-        setThinCursor(g.mode == InsertMode || editorCursor().hasSelection());
-    }
-}
-
-void
-FakeVimHandler::Private::onUndoCommandAdded()
-{
-    if (!canModifyBufferData())
-        return;
-
-    // Undo commands removed?
-    UNDO_DEBUG("Undo added"
-               << "previous: REV" << m_buffer->lastRevision);
-    if (m_buffer->lastRevision >= revision()) {
-        UNDO_DEBUG("UNDO REMOVED!");
-        const int removed = m_buffer->lastRevision - revision();
-        for (qsizetype i = m_buffer->undo.size() - 1; i >= 0; --i) {
-            if ((m_buffer->undo[i].revision -= removed) < 0) {
-                m_buffer->undo.remove(0, i + 1);
-                break;
-            }
-        }
-    }
-
-    m_buffer->redo.clear();
-    // External change while FakeVim disabled.
-    if (m_buffer->editBlockLevel == 0 && !m_buffer->undo.isEmpty() && !isInsertMode())
-        m_buffer->undo.push(State());
-}
-
-void
-FakeVimHandler::Private::onInputTimeout()
-{
-    enterFakeVim();
-    EventResult result = handleKey(Input());
-    leaveFakeVim(result);
-}
-
-void
-FakeVimHandler::Private::onFixCursorTimeout()
-{
-    if (editor())
-        fixExternalCursorPosition(editor()->hasFocus() && !isCommandLineMode());
-}
-
-char
-FakeVimHandler::Private::currentModeCode() const
-{
-    if (g.mode == ExMode)
-        return 'c';
-    else if (isVisualMode())
-        return 'v';
-    else if (isOperatorPending())
-        return 'o';
-    else if (g.mode == CommandMode)
-        return 'n';
-    else if (g.submode != NoSubMode)
-        return ' ';
-    else
-        return 'i';
-}
-
-void
-FakeVimHandler::Private::undoRedo(bool undo)
-{
-    UNDO_DEBUG((undo ? "UNDO" : "REDO"));
-
-    // FIXME: That's only an approximaxtion. The real solution might
-    // be to store marks and old userData with QTextBlock setUserData
-    // and retrieve them afterward.
-    QStack<State> &stack  = undo ? m_buffer->undo : m_buffer->redo;
-    QStack<State> &stack2 = undo ? m_buffer->redo : m_buffer->undo;
-
-    State state = m_buffer->undoState.isValid() ? m_buffer->undoState
-                  : !stack.empty()              ? stack.pop()
-                                                : State();
-
-    CursorPosition lastPos(m_cursor);
-    if (undo ? !document()->isUndoAvailable() : !document()->isRedoAvailable()) {
-        const QString msg =
-            undo ? Tr::tr("Already at oldest change.") : Tr::tr("Already at newest change.");
-        showMessage(MessageInfo, msg);
-        UNDO_DEBUG(msg);
-        return;
-    }
-    clearMessage();
-
-    ++m_buffer->editBlockLevel;
-
-    // Do undo/redo [count] times to reach previous revision.
-    const int previousRevision = revision();
-    if (undo) {
-        do {
-            EDITOR(undo());
-        } while (document()->isUndoAvailable() && state.revision >= 0 &&
-                 state.revision < revision());
-    } else {
-        do {
-            EDITOR(redo());
-        } while (document()->isRedoAvailable() && state.revision > revision());
-    }
-
-    --m_buffer->editBlockLevel;
-
-    if (state.isValid()) {
-        Marks marks = m_buffer->marks;
-        marks.swap(state.marks);
-        updateMarks(marks);
-        m_buffer->lastVisualMode         = state.lastVisualMode;
-        m_buffer->lastVisualModeInverted = state.lastVisualModeInverted;
-        setMark('.', state.position);
-        setMark('\'', lastPos);
-        setMark('`', lastPos);
-        setCursorPosition(state.position);
-        setAnchor();
-        state.revision = previousRevision;
-    } else {
-        updateFirstVisibleLine();
-        pullCursor();
-    }
-    stack2.push(state);
-
-    setTargetColumn();
-    if (atEndOfLine())
-        moveLeft();
-
-    UNDO_DEBUG((undo ? "UNDONE" : "REDONE"));
-}
-
-void
-FakeVimHandler::Private::undo()
-{
-    undoRedo(true);
-}
-
-void
-FakeVimHandler::Private::redo()
-{
-    undoRedo(false);
-}
-
-void
-FakeVimHandler::Private::updateCursorShape()
-{
-    setThinCursor(g.mode == InsertMode || isVisualLineMode() || isVisualBlockMode() ||
-                  isCommandLineMode() || !editor()->hasFocus());
-}
-
-void
-FakeVimHandler::Private::setThinCursor(bool enable)
-{
-    EDITOR(setOverwriteMode(!enable));
-}
-
-bool
-FakeVimHandler::Private::hasThinCursor() const
-{
-    return !EDITOR(overwriteMode());
-}
-
-void
-FakeVimHandler::Private::enterReplaceMode()
-{
-    enterInsertOrReplaceMode(ReplaceMode);
-}
-
-void
-FakeVimHandler::Private::enterInsertMode()
-{
-    enterInsertOrReplaceMode(InsertMode);
-}
-
-void
-FakeVimHandler::Private::enterInsertOrReplaceMode(Mode mode)
-{
-    if (mode != InsertMode && mode != ReplaceMode) {
-        logWarning() << "Unexpected mode";
-        return;
-    }
-    if (g.mode == mode)
-        return;
-
-    g.mode = mode;
-
-    if (g.returnToMode == mode) {
-        // Returning to insert mode after <C-O>.
-        clearCurrentMode();
-        moveToTargetColumn();
-        invalidateInsertState();
-    } else {
-        // Entering insert mode from command mode.
-        if (mode == InsertMode) {
-            // m_targetColumn shouldn't be -1 (end of line).
-            if (m_targetColumn == -1)
-                setTargetColumn();
-        }
-
-        g.submode      = NoSubMode;
-        g.subsubmode   = NoSubSubMode;
-        g.returnToMode = mode;
-        clearLastInsertion();
-    }
-}
-
-void
-FakeVimHandler::Private::enterVisualInsertMode(QChar command)
-{
-    if (isVisualBlockMode()) {
-        bool append = command == 'A';
-        bool change = command == 's' || command == 'c';
-
-        leaveVisualMode();
-
-        const CursorPosition lastAnchor   = markLessPosition();
-        const CursorPosition lastPosition = markGreaterPosition();
-        CursorPosition pos(lastAnchor.line, append
-                                                ? qMax(lastPosition.column, lastAnchor.column) + 1
-                                                : qMin(lastPosition.column, lastAnchor.column));
-
-        if (append) {
-            m_visualBlockInsert = m_visualTargetColumn == -1 ? AppendToEndOfLineBlockInsertMode
-                                                             : AppendBlockInsertMode;
-        } else if (change) {
-            m_visualBlockInsert = ChangeBlockInsertMode;
-            beginEditBlock();
-            cutSelectedText();
-            endEditBlock();
-        } else {
-            m_visualBlockInsert = InsertBlockInsertMode;
-        }
-
-        setCursorPosition(pos);
-        if (m_visualBlockInsert == AppendToEndOfLineBlockInsertMode)
-            moveBehindEndOfLine();
-    } else {
-        m_visualBlockInsert = NoneBlockInsertMode;
-        leaveVisualMode();
-        if (command == 'I') {
-            if (lineForPosition(anchor()) <= lineForPosition(position())) {
-                setPosition(qMin(anchor(), position()));
-                moveToStartOfLine();
-            }
-        } else if (command == 'A') {
-            if (lineForPosition(anchor()) <= lineForPosition(position())) {
-                setPosition(position());
-                moveRight(qMin(rightDist(), 1));
-            } else {
-                setPosition(anchor());
-                moveToStartOfLine();
-            }
-        }
-    }
-
-    setAnchor();
-    if (m_visualBlockInsert != ChangeBlockInsertMode)
-        breakEditBlock();
-    enterInsertMode();
-}
-
-void
-FakeVimHandler::Private::enterCommandMode(Mode returnToMode)
-{
-    if (g.isRecording && isCommandLineMode())
-        record(Input(Key_Escape, NoModifier));
-
-    if (isNoVisualMode()) {
-        if (atEndOfLine()) {
-            m_cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
-            if (m_targetColumn != -1)
-                setTargetColumn();
-        }
-        setAnchor();
-    }
-
-    g.mode = CommandMode;
-    clearCurrentMode();
-    g.returnToMode    = returnToMode;
-    m_positionPastEnd = false;
-    m_anchorPastEnd   = false;
-}
-
-void
-FakeVimHandler::Private::enterExMode(const QString &contents)
-{
-    g.currentMessage.clear();
-    g.commandBuffer.clear();
-    if (isVisualMode())
-        g.commandBuffer.setContents(QString("'<,'>") + contents, int(contents.size()) + 5);
-    else
-        g.commandBuffer.setContents(contents, int(contents.size()));
-    g.mode       = ExMode;
-    g.submode    = NoSubMode;
-    g.subsubmode = NoSubSubMode;
-    unfocus();
-}
-
-void
-FakeVimHandler::Private::recordJump(int position)
-{
-    CursorPosition pos =
-        position >= 0 ? CursorPosition(document(), position) : CursorPosition(m_cursor);
-    setMark('\'', pos);
-    setMark('`', pos);
-    if (m_buffer->jumpListUndo.isEmpty() || m_buffer->jumpListUndo.top() != pos)
-        m_buffer->jumpListUndo.push(pos);
-    m_buffer->jumpListRedo.clear();
-    UNDO_DEBUG("jumps: " << m_buffer->jumpListUndo);
-}
-
-void
-FakeVimHandler::Private::jump(int distance)
-{
-    QStack<CursorPosition> &from = (distance > 0) ? m_buffer->jumpListRedo : m_buffer->jumpListUndo;
-    QStack<CursorPosition> &to   = (distance > 0) ? m_buffer->jumpListUndo : m_buffer->jumpListRedo;
-    int len                      = qMin(qAbs(distance), int(from.size()));
-    CursorPosition m(m_cursor);
-    setMark('\'', m);
-    setMark('`', m);
-    for (int i = 0; i < len; ++i) {
-        to.push(m);
-        setCursorPosition(from.top());
-        from.pop();
-    }
-    setTargetColumn();
-}
-
-Column
-FakeVimHandler::Private::indentation(const QString &line) const
-{
-    int ts       = static_cast<int>(s.tabStop.value());
-    int physical = 0;
-    int logical  = 0;
-    qsizetype n  = line.size();
-    while (physical < n) {
-        QChar c = line.at(physical);
-        if (c == ' ')
-            ++logical;
-        else if (c == '\t')
-            logical += ts - logical % ts;
-        else
-            break;
-        ++physical;
-    }
-    return Column(physical, logical);
-}
-
-QString
-FakeVimHandler::Private::tabExpand(int n) const
-{
-    int ts = static_cast<int>(s.tabStop.value());
-    if (s.expandTab.value() || ts < 1)
-        return QString(n, ' ');
-    return QString(n / ts, '\t') + QString(n % ts, ' ');
-}
-
-void
-FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool forceAutoIndent)
-{
-    if (!forceAutoIndent && !s.autoIndent.value() && !s.smartIndent.value())
-        return;
-
-    if (s.smartIndent.value()) {
-        QTextBlock bl = block();
-        Range range(bl.position(), bl.position());
-        indentText(range, '\n');
-    } else {
-        QTextBlock bl = goingDown ? block().previous() : block().next();
-        QString text  = bl.text();
-        int pos       = 0;
-        qsizetype n   = text.size();
-        while (pos < n && text.at(pos).isSpace())
-            ++pos;
-        text.truncate(pos);
-        // FIXME: handle 'smartindent' and 'cindent'
-        insertText(text);
-    }
-}
-
-void
-FakeVimHandler::Private::handleStartOfLine()
-{
-    if (s.startOfLine.value())
-        moveToFirstNonBlankOnLine();
-}
-
-void
-FakeVimHandler::Private::replay(const QString &command, int repeat)
-{
-    if (repeat <= 0)
-        return;
-
-    clearCurrentMode();
-    const Inputs inputs(command);
-    for (int i = 0; i < repeat; ++i) {
-        for (const Input &in : inputs) {
-            if (handleDefaultKey(in) != EventHandled)
-                return;
-        }
-    }
-}
-
-QString
-FakeVimHandler::Private::visualDotCommand() const
-{
-    QTextCursor start(m_cursor);
-    QTextCursor end(start);
-    end.setPosition(end.anchor());
-
-    QString command;
-
-    if (isVisualCharMode())
-        command = "v";
-    else if (isVisualLineMode())
-        command = "V";
-    else if (isVisualBlockMode())
-        command = "<c-v>";
-    else
-        return QString();
-
-    const int down = qAbs(start.blockNumber() - end.blockNumber());
-    if (down != 0)
-        command.append(QString("%1j").arg(down));
-
-    const int right = start.positionInBlock() - end.positionInBlock();
-    if (right != 0) {
-        command.append(QString::number(qAbs(right)));
-        command.append(QLatin1Char(right < 0 && isVisualBlockMode() ? 'h' : 'l'));
-    }
-
-    return command;
-}
-
-void
-FakeVimHandler::Private::selectTextObject(bool simple, bool inner)
-{
-    const int position1 = this->position();
-    const int anchor1   = this->anchor();
-    bool setupAnchor    = (position1 == anchor1);
-    bool forward        = anchor1 <= position1;
-    const int repeat    = count();
-
-    // set anchor if not already set
-    if (setupAnchor) {
-        // Select nothing with 'inner' on empty line.
-        if (inner && atEmptyLine() && repeat == 1) {
-            g.movetype = MoveExclusive;
-            return;
-        }
-        moveToBoundaryStart(1, simple, false);
-        setAnchor();
-    } else if (forward) {
-        moveToNextCharacter();
-    } else {
-        moveToPreviousCharacter();
-    }
-
-    if (inner) {
-        moveToBoundaryEnd(repeat, simple);
-    } else {
-        const int direction = forward ? 1 : -1;
-        for (int i = 0; i < repeat; ++i) {
-            // select leading spaces
-            bool leadingSpace = characterAtCursor().isSpace();
-            if (leadingSpace) {
-                if (forward)
-                    moveToNextBoundaryStart(1, simple);
-                else
-                    moveToNextBoundaryEnd(1, simple, false);
-            }
-
-            // select word
-            if (forward)
-                moveToWordEnd(1, simple);
-            else
-                moveToWordStart(1, simple, false);
-
-            // select trailing spaces if no leading space
-            QChar afterCursor = characterAt(position() + direction);
-            if (!leadingSpace && afterCursor.isSpace() &&
-                afterCursor != QChar::ParagraphSeparator && !atBlockStart()) {
-                if (forward)
-                    moveToNextBoundaryEnd(1, simple);
-                else
-                    moveToNextBoundaryStart(1, simple, false);
-            }
-
-            // if there are no trailing spaces in selection select all leading spaces
-            // after previous character
-            if (setupAnchor && (!characterAtCursor().isSpace() || atBlockEnd())) {
-                int min = block().position();
-                int pos = anchor();
-                while (pos >= min && characterAt(--pos).isSpace()) {}
-                if (pos >= min)
-                    setAnchorAndPosition(pos + 1, position());
-            }
-
-            if (i + 1 < repeat) {
-                if (forward)
-                    moveToNextCharacter();
-                else
-                    moveToPreviousCharacter();
-            }
-        }
-    }
-
-    if (inner) {
-        g.movetype = MoveInclusive;
-    } else {
-        g.movetype = MoveExclusive;
-        if (isNoVisualMode())
-            moveToNextCharacter();
-        else if (isVisualLineMode())
-            g.visualMode = VisualCharMode;
-    }
-
-    setTargetColumn();
-}
-
-void
-FakeVimHandler::Private::selectWordTextObject(bool inner)
-{
-    selectTextObject(false, inner);
-}
-
-void
-FakeVimHandler::Private::selectWORDTextObject(bool inner)
-{
-    selectTextObject(true, inner);
-}
-
-void
-FakeVimHandler::Private::selectSentenceTextObject(bool inner)
-{
-    Q_UNUSED(inner)
-}
-
-void
-FakeVimHandler::Private::selectParagraphTextObject(bool inner)
-{
-    const QTextCursor oldCursor    = m_cursor;
-    const VisualMode oldVisualMode = g.visualMode;
-
-    const int anchorBlock   = blockNumberAt(anchor());
-    const int positionBlock = blockNumberAt(position());
-    const bool setupAnchor  = anchorBlock == positionBlock;
-    int repeat              = count();
-
-    // If anchor and position are in the same block,
-    // start line selection at beginning of current paragraph.
-    if (setupAnchor) {
-        moveToParagraphStartOrEnd(-1);
-        setAnchor();
-
-        if (!isVisualLineMode() && isVisualMode())
-            toggleVisualMode(VisualLineMode);
-    }
-
-    const bool forward = anchor() <= position();
-    const int dLocal   = forward ? 1 : -1;
-
-    bool startsAtParagraph = !atEmptyLine(position());
-
-    moveToParagraphStartOrEnd(dLocal);
-
-    // If selection already changed, decreate count.
-    if ((setupAnchor && g.submode != NoSubMode) || oldVisualMode != g.visualMode ||
-        m_cursor != oldCursor) {
-        --repeat;
-        if (!inner) {
-            moveDown(dLocal);
-            moveToParagraphStartOrEnd(dLocal);
-            startsAtParagraph = !startsAtParagraph;
-        }
-    }
-
-    if (repeat > 0) {
-        bool isCountEven     = repeat % 2 == 0;
-        bool endsOnParagraph = inner ? isCountEven == startsAtParagraph : startsAtParagraph;
-
-        if (inner) {
-            repeat = repeat / 2;
-            if (!isCountEven || endsOnParagraph)
-                ++repeat;
-        } else {
-            if (endsOnParagraph)
-                ++repeat;
-        }
-
-        if (!moveToNextParagraph(dLocal * repeat)) {
-            m_cursor     = oldCursor;
-            g.visualMode = oldVisualMode;
-            return;
-        }
-
-        if (endsOnParagraph && atEmptyLine())
-            moveUp(dLocal);
-        else
-            moveToParagraphStartOrEnd(dLocal);
-    }
-
-    if (!inner && setupAnchor && !atEmptyLine() && !atEmptyLine(anchor())) {
-        // If position cannot select empty lines, try to select them with anchor.
-        setAnchorAndPosition(position(), anchor());
-        moveToNextParagraph(-dLocal);
-        moveToParagraphStartOrEnd(-dLocal);
-        setAnchorAndPosition(position(), anchor());
-    }
-
-    recordJump(oldCursor.position());
-    setTargetColumn();
-    g.movetype = MoveLineWise;
-}
-
-bool
-FakeVimHandler::Private::selectBlockTextObject(bool inner, QChar left, QChar right)
-{
-    int p1 = blockBoundary(left, right, false, count());
-    if (p1 == -1)
-        return false;
-
-    int p2 = blockBoundary(left, right, true, count());
-    if (p2 == -1)
-        return false;
-
-    g.movetype = MoveExclusive;
-
-    if (inner) {
-        p1 += 1;
-        bool moveStart = characterAt(p1) == QChar::ParagraphSeparator;
-        bool moveEnd   = isFirstNonBlankOnLine(p2);
-        if (moveStart)
-            ++p1;
-        if (moveEnd)
-            p2 = blockAt(p2).position() - 1;
-        if (moveStart && moveEnd)
-            g.movetype = MoveLineWise;
-    } else {
-        p2 += 1;
-    }
-
-    if (isVisualMode())
-        --p2;
-
-    setAnchorAndPosition(p1, p2);
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::changeNumberTextObject(int count)
-{
-    const QTextBlock block = this->block();
-    const QString lineText = block.text();
-    const int posMin       = m_cursor.positionInBlock() + 1;
-
-    // find first decimal, hexadecimal or octal number under or after cursor position
-    QRegularExpression re("(0[xX])(0*[0-9a-fA-F]+)|(0)(0*[0-7]+)(?=\\D|$)|(\\d+)");
-    QRegularExpressionMatch match;
-    QRegularExpressionMatchIterator it = re.globalMatch(lineText);
-    while (true) {
-        if (!it.hasNext())
-            return false;
-        match = it.next();
-        if (match.capturedEnd() >= posMin)
-            break;
-    }
-    int pos           = int(match.capturedStart());
-    int len           = int(match.capturedLength());
-    QString prefix    = match.captured(1) + match.captured(3);
-    bool hex          = prefix.length() >= 2 && (prefix[1].toLower() == 'x');
-    bool octal        = !hex && !prefix.isEmpty();
-    const QString num = hex ? match.captured(2) : octal ? match.captured(4) : match.captured(5);
-
-    // parse value
-    bool ok;
-    int base         = hex ? 16 : octal ? 8 : 10;
-    qlonglong value  = 0; // decimal value
-    qlonglong uvalue = 0; // hexadecimal or octal value (only unsigned)
-    if (hex || octal)
-        uvalue = static_cast<qlonglong>(num.toULongLong(&ok, base));
-    else
-        value = num.toLongLong(&ok, base);
-    if (!ok) {
-        logWarning() << "Cannot parse number:" << num << "base:" << base;
-        return false;
-    }
-
-    // negative decimal number
-    if (!octal && !hex && pos > 0 && lineText[pos - 1] == '-') {
-        value = -value;
-        --pos;
-        ++len;
-    }
-
-    // result to string
-    QString repl;
-    if (hex || octal)
-        repl = QString::number(uvalue + count, base);
-    else
-        repl = QString::number(value + count, base);
-
-    // convert hexadecimal number to upper-case if last letter was upper-case
-    if (hex) {
-        const qsizetype lastLetter = num.lastIndexOf(QRegularExpression("[a-fA-F]"));
-        if (lastLetter != -1 && num[lastLetter].isUpper())
-            repl = repl.toUpper();
-    }
-
-    // preserve leading zeroes
-    if ((octal || hex) && repl.size() < num.size())
-        prefix.append(QString("0").repeated(num.size() - repl.size()));
-    repl.prepend(prefix);
-
-    pos += block.position();
-    pushUndoState();
-    setAnchorAndPosition(pos, pos + len);
-    replaceText(currentRange(), repl);
-    setPosition(pos + int(repl.size()) - 1);
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::selectQuotedStringTextObject(bool inner, const QString &quote)
-{
-    QTextCursor tc = m_cursor;
-    int sz         = int(quote.size());
-
-    QTextCursor tc1;
-    QTextCursor tc2(document());
-    while (tc2 <= tc) {
-        tc1 = document()->find(quote, tc2);
-        if (tc1.isNull())
-            return false;
-        tc2 = document()->find(quote, tc1);
-        if (tc2.isNull())
-            return false;
-    }
-
-    int p1 = tc1.position();
-    int p2 = tc2.position();
-    if (inner) {
-        p2 = qMax(p1, p2 - sz);
-        if (characterAt(p1) == QChar::ParagraphSeparator)
-            ++p1;
-    } else {
-        p1 -= sz;
-        p2 -= sz - 1;
-    }
-
-    if (isVisualMode())
-        --p2;
-
-    setAnchorAndPosition(p1, p2);
-    g.movetype = MoveExclusive;
-
-    return true;
-}
-
-bool
-FakeVimHandler::Private::selectArgumentTextObject(bool inner)
-{
-    // We are just interested whether we're currently inside angled brackets,
-    // but selectBlockTextObject also moves the cursor, so set it back to
-    // its original position afterwards
-    QTextCursor prevCursor             = m_cursor;
-    const bool insideTemplateParameter = selectBlockTextObject(true, '<', '>');
-    m_cursor                           = prevCursor;
-
-    int openAngleBracketCount = insideTemplateParameter ? 1 : 0;
-
-    QTextCursor tcStart(m_cursor);
-    while (true) {
-        if (tcStart.atStart())
-            return true;
-
-        const QChar currentChar = characterAt(tcStart.position());
-
-        if (openAngleBracketCount == 0 && (currentChar == '(' || currentChar == ','))
-            break;
-
-        if (currentChar == '<')
-            openAngleBracketCount--;
-        else if (currentChar == '>')
-            openAngleBracketCount++;
-
-        tcStart.setPosition(tcStart.position() - 1);
-    }
-
-    QTextCursor tcEnd(m_cursor);
-    openAngleBracketCount    = insideTemplateParameter ? 1 : 0;
-    int openParanthesisCount = 0;
-
-    while (true) {
-        if (tcEnd.atEnd()) {
-            return true;
-        }
-
-        const QChar currentChar = characterAt(tcEnd.position());
-        if (openAngleBracketCount == 0 && openParanthesisCount == 0 &&
-            (currentChar == ')' || currentChar == ','))
-            break;
-
-        if (currentChar == '<')
-            openAngleBracketCount++;
-        else if (currentChar == '>')
-            openAngleBracketCount--;
-        else if (currentChar == '(')
-            openParanthesisCount++;
-        else if (currentChar == ')')
-            openParanthesisCount--;
-
-        tcEnd.setPosition(tcEnd.position() + 1);
-    }
-
-    if (!inner && characterAt(tcEnd.position()) == ',' && characterAt(tcStart.position()) == '(') {
-        tcEnd.setPosition(tcEnd.position() + 1);
-        if (characterAt(tcEnd.position()) == ' ')
-            tcEnd.setPosition(tcEnd.position() + 1);
-    }
-
-    // Never include the opening paranthesis
-    if (characterAt(tcStart.position()) == '(') {
-        tcStart.setPosition(tcStart.position() + 1);
-    } else if (inner) {
-        tcStart.setPosition(tcStart.position() + 1);
-        if (characterAt(tcStart.position()) == ' ')
-            tcStart.setPosition(tcStart.position() + 1);
-    }
-
-    if (isVisualMode())
-        tcEnd.setPosition(tcEnd.position() - 1);
-
-    g.movetype = MoveExclusive;
-
-    setAnchorAndPosition(tcStart.position(), tcEnd.position());
-    return true;
-}
-
-Mark
-FakeVimHandler::Private::mark(QChar code) const
-{
-    if (isVisualMode()) {
-        if (code == '<')
-            return CursorPosition(document(), qMin(anchor(), position()));
-        if (code == '>')
-            return CursorPosition(document(), qMax(anchor(), position()));
-    }
-
-    if (code.isUpper())
-        return g.marks.value(code);
-
-    return m_buffer->marks.value(code);
-}
-
-void
-FakeVimHandler::Private::setMark(QChar code, CursorPosition position)
-{
-    if (code.isUpper())
-        g.marks[code] = Mark(position, m_currentFileName);
-    else
-        m_buffer->marks[code] = Mark(position);
-}
-
-bool
-FakeVimHandler::Private::jumpToMark(QChar mark, bool backTickMode)
-{
-    Mark m = this->mark(mark);
-    if (!m.isValid()) {
-        showMessage(MessageError, msgMarkNotSet(mark));
-        return false;
-    }
-    if (!m.isLocal(m_currentFileName)) {
-        q->requestJumpToGlobalMark(mark, backTickMode, m.fileName());
-        return false;
-    }
-
-    if ((mark == '\'' || mark == '`') && !m_buffer->jumpListUndo.isEmpty())
-        m_buffer->jumpListUndo.pop();
-    recordJump();
-    setCursorPosition(m.position(document()));
-    if (!backTickMode)
-        moveToFirstNonBlankOnLine();
-    if (g.submode == NoSubMode)
-        setAnchor();
-    setTargetColumn();
-
-    return true;
-}
-
-void
-FakeVimHandler::Private::updateMarks(const Marks &newMarks)
-{
-    for (auto it = newMarks.cbegin(), end = newMarks.cend(); it != end; ++it)
-        m_buffer->marks[it.key()] = it.value();
-}
-
-RangeMode
-FakeVimHandler::Private::registerRangeMode(int reg) const
-{
-    bool isClipboard;
-    bool isSelection;
-    getRegisterType(&reg, &isClipboard, &isSelection);
-
-    if (isClipboard || isSelection) {
-        QClipboard *clipboard = QApplication::clipboard();
-        QClipboard::Mode mode = isClipboard ? QClipboard::Clipboard : QClipboard::Selection;
-
-        // Use range mode from Vim's clipboard data if available.
-        const QMimeData *data = clipboard->mimeData(mode);
-        if (data && data->hasFormat(vimMimeText)) {
-            QByteArray bytes = data->data(vimMimeText);
-            if (bytes.length() > 0)
-                return static_cast<RangeMode>(bytes.at(0));
-        }
-
-        // If register content is clipboard:
-        //  - return RangeLineMode if text ends with new line char,
-        //  - return RangeCharMode otherwise.
-        QString text = clipboard->text(mode);
-        return (text.endsWith('\n') || text.endsWith('\r')) ? RangeLineMode : RangeCharMode;
-    }
-
-    return g.registers[reg].rangemode;
-}
-
-void
-FakeVimHandler::Private::setRegister(int reg, const QString &contents, RangeMode mode)
-{
-    bool copyToClipboard;
-    bool copyToSelection;
-    bool append;
-    getRegisterType(&reg, &copyToClipboard, &copyToSelection, &append);
-
-    QString contents2 = contents;
-    if ((mode == RangeLineMode || mode == RangeLineModeExclusive) && !contents2.endsWith('\n')) {
-        contents2.append('\n');
-    }
-
-    if (copyToClipboard || copyToSelection) {
-        if (copyToClipboard)
-            setClipboardData(contents2, mode, QClipboard::Clipboard);
-        if (copyToSelection)
-            setClipboardData(contents2, mode, QClipboard::Selection);
-    } else {
-        if (append)
-            g.registers[reg].contents.append(contents2);
-        else
-            g.registers[reg].contents = contents2;
-        g.registers[reg].rangemode = mode;
-    }
-}
-
-QString
-FakeVimHandler::Private::registerContents(int reg) const
-{
-    bool copyFromClipboard;
-    bool copyFromSelection;
-    getRegisterType(&reg, &copyFromClipboard, &copyFromSelection);
-
-    if (copyFromClipboard || copyFromSelection) {
-        QClipboard *clipboard = QApplication::clipboard();
-        if (copyFromClipboard)
-            return clipboard->text(QClipboard::Clipboard);
-        if (copyFromSelection)
-            return clipboard->text(QClipboard::Selection);
-    }
-
-    return g.registers[reg].contents;
-}
-
-void
-FakeVimHandler::Private::getRegisterType(int *reg, bool *isClipboard, bool *isSelection,
-                                         bool *append) const
-{
-    bool clipboard = false;
-    bool selection = false;
-
-    // If register is uppercase, append content to lower case register on yank/delete.
-    const QChar c(*reg);
-    if (append != nullptr)
-        *append = c.isUpper();
-    if (c.isUpper())
-        *reg = c.toLower().unicode();
-
-    if (c == '"') {
-        QStringList list = s.clipboard.value().split(',');
-        clipboard        = list.contains("unnamedplus");
-        selection        = list.contains("unnamed");
-    } else if (c == '+') {
-        clipboard = true;
-    } else if (c == '*') {
-        selection = true;
-    }
-
-    // selection (primary) is clipboard on systems without selection support
-    if (selection && !QApplication::clipboard()->supportsSelection()) {
-        clipboard = true;
-        selection = false;
-    }
-
-    if (isClipboard != nullptr)
-        *isClipboard = clipboard;
-    if (isSelection != nullptr)
-        *isSelection = selection;
-}
-
-///////////////////////////////////////////////////////////////////////
-//
-// FakeVimHandler
-//
-///////////////////////////////////////////////////////////////////////
-
-FakeVimHandler::FakeVimHandler(QWidget *widget, QObject *parent)
-    : QObject(parent)
-    , d(new Private(this, widget))
-{
-}
-
-FakeVimHandler::~FakeVimHandler() { delete d; }
-
-// gracefully handle that the parent editor is deleted
-void
-FakeVimHandler::disconnectFromEditor()
-{
-    d->m_textedit      = nullptr;
-    d->m_plaintextedit = nullptr;
-}
-
-void
-FakeVimHandler::updateGlobalMarksFilenames(const QString &oldFileName, const QString &newFileName)
-{
-    for (Mark &mark : Private::g.marks) {
-        if (mark.fileName() == oldFileName)
-            mark.setFileName(newFileName);
-    }
-}
-
-bool
-FakeVimHandler::eventFilter(QObject *ob, QEvent *ev)
-{
-#ifndef FAKEVIM_STANDALONE
-    if (!fakeVimSettings()->useFakeVim.value())
-        return QObject::eventFilter(ob, ev);
-#endif
-
-    if (ev->type() == QEvent::Shortcut) {
-        d->passShortcuts(false);
-        return false;
-    }
-
-    if (ev->type() == QEvent::KeyPress &&
-        (ob == d->editor() ||
-         (Private::g.mode == ExMode || Private::g.subsubmode == SearchSubSubMode))) {
-        auto kev = static_cast<QKeyEvent *>(ev);
-        KEY_DEBUG("KEYPRESS" << kev->key() << kev->text() << QChar(kev->key()));
-        EventResult res = d->handleEvent(kev);
-        //if (Private::g.mode == InsertMode)
-        //    completionRequested();
-        // returning false core the app see it
-        //KEY_DEBUG("HANDLED CODE:" << res);
-        //return res != EventPassedToCore;
-        //return true;
-        return res == EventHandled || res == EventCancelled;
-    }
-
-    if (ev->type() == QEvent::ShortcutOverride &&
-        (ob == d->editor() ||
-         (Private::g.mode == ExMode || Private::g.subsubmode == SearchSubSubMode))) {
-        auto kev = static_cast<QKeyEvent *>(ev);
-        if (d->wantsOverride(kev)) {
-            KEY_DEBUG("OVERRIDING SHORTCUT" << kev->key());
-            ev->accept(); // accepting means "don't run the shortcuts"
-            return true;
-        }
-        KEY_DEBUG("NO SHORTCUT OVERRIDE" << kev->key());
-        return true;
-    }
-
-    if (ev->type() == QEvent::FocusOut && ob == d->editor()) {
-        d->unfocus();
-        return false;
-    }
-
-    if (ev->type() == QEvent::FocusIn && ob == d->editor())
-        d->focus();
-
-    return QObject::eventFilter(ob, ev);
-}
-
-void
-FakeVimHandler::installEventFilter()
-{
-    d->installEventFilter();
-}
-
-void
-FakeVimHandler::setupWidget()
-{
-    d->setupWidget();
-}
-
-void
-FakeVimHandler::restoreWidget(int tabSize)
-{
-    d->restoreWidget(tabSize);
-}
-
-void
-FakeVimHandler::handleCommand(const QString &cmd)
-{
-    d->enterFakeVim();
-    d->handleCommand(cmd);
-    d->leaveFakeVim();
-}
-
-void
-FakeVimHandler::handleReplay(const QString &keys)
-{
-    d->enterFakeVim();
-    d->replay(keys);
-    d->leaveFakeVim();
-}
-
-void
-FakeVimHandler::handleInput(const QString &keys)
-{
-    const Inputs inputs(keys);
-    d->enterFakeVim();
-    for (const Input &input : inputs)
-        d->handleKey(input);
-    d->leaveFakeVim();
-}
-
-void
-FakeVimHandler::enterCommandMode()
-{
-    d->enterCommandMode();
-}
-
-void
-FakeVimHandler::setCurrentFileName(const QString &fileName)
-{
-    d->m_currentFileName = fileName;
-}
-
-QString
-FakeVimHandler::currentFileName() const
-{
-    return d->m_currentFileName;
-}
-
-void
-FakeVimHandler::showMessage(MessageLevel level, const QString &msg)
-{
-    d->showMessage(level, msg);
-}
-
-QWidget *
-FakeVimHandler::widget()
-{
-    return d->editor();
-}
-
-// Test only
-int
-FakeVimHandler::physicalIndentation(const QString &line) const
-{
-    Column ind = d->indentation(line);
-    return ind.physical;
-}
-
-int
-FakeVimHandler::logicalIndentation(const QString &line) const
-{
-    Column ind = d->indentation(line);
-    return ind.logical;
-}
-
-QString
-FakeVimHandler::tabExpand(int n) const
-{
-    return d->tabExpand(n);
-}
-
-void
-FakeVimHandler::miniBufferTextEdited(const QString &text, int cursorPos, int anchorPos)
-{
-    d->miniBufferTextEdited(text, cursorPos, anchorPos);
-}
-
-void
-FakeVimHandler::setTextCursorPosition(int position)
-{
-    int pos = qMax(0, qMin(position, d->lastPositionInDocument()));
-    if (d->isVisualMode())
-        d->setPosition(pos);
-    else
-        d->setAnchorAndPosition(pos, pos);
-    d->setTargetColumn();
-
-    if (!d->m_inFakeVim)
-        d->commitCursor();
-}
-
-QTextCursor
-FakeVimHandler::textCursor() const
-{
-    return d->m_cursor;
-}
-
-void
-FakeVimHandler::setTextCursor(const QTextCursor &cursor)
-{
-    d->m_cursor = cursor;
-}
-
-bool
-FakeVimHandler::jumpToLocalMark(QChar mark, bool backTickMode)
-{
-    return d->jumpToMark(mark, backTickMode);
-}
-
-}
-
-Q_DECLARE_METATYPE(Vivy::FakeVimHandler::Private::BufferDataPtr)
diff --git a/src/UI/FakeVim/FakeVimHandler.hh b/src/UI/FakeVim/FakeVimHandler.hh
deleted file mode 100644
index ea3832f3..00000000
--- a/src/UI/FakeVim/FakeVimHandler.hh
+++ /dev/null
@@ -1,172 +0,0 @@
-#pragma once
-
-#include "PreCompiledHeaders.hh"
-#include "VivyApplication.hh"
-#include "Lib/Log.hh"
-#include "Lib/Utils.hh"
-
-#define FAKEVIM_STANDALONE
-
-namespace Vivy
-{
-enum RangeMode {
-    // Reordering first three enum items here will break
-    // compatibility with clipboard format stored by Vim.
-    RangeCharMode,  // v
-    RangeLineMode,  // V
-    RangeBlockMode, // Ctrl-v
-    RangeLineModeExclusive,
-    RangeBlockAndTailMode // Ctrl-v for D and X
-};
-
-struct Range {
-    Range() = default;
-    Range(int b, int e, RangeMode m = RangeCharMode);
-    QString toString() const;
-    bool isValid() const;
-
-    int beginPos        = -1;
-    int endPos          = -1;
-    RangeMode rangemode = RangeCharMode;
-
-    // Just some convenience functions for compatibility with optional
-    // which is apparently allowed in qtcreator.
-    operator bool() const { return isValid(); }
-    void reset()
-    {
-        beginPos = -1;
-        endPos   = -1;
-    }
-    Range &operator*() { return *this; }
-};
-
-struct ExCommand {
-    ExCommand() = default;
-    ExCommand(const QString &cmd, const QString &args = QString(), const Range &range = Range());
-
-    bool matches(const QString &min, const QString &full) const;
-
-    QString cmd;
-    bool hasBang = false;
-    QString args;
-    Range range;
-    int count = 1;
-};
-
-// message levels sorted by severity
-enum MessageLevel {
-    MessageMode,    // show current mode (format "-- %1 --")
-    MessageCommand, // show last Ex command or search
-    MessageInfo,    // result of a command
-    MessageWarning, // warning
-    MessageError,   // error
-    MessageShowCmd  // partial command
-};
-
-template <typename Type> class Signal {
-public:
-    using Callable = std::function<Type>;
-
-    void connect(const Callable &callable) { m_callables.push_back(callable); }
-
-    template <typename... Args> void operator()(Args... args) const
-    {
-        for (const Callable &callable : m_callables)
-            callable(args...);
-    }
-
-private:
-    std::vector<Callable> m_callables;
-};
-
-class FakeVimHandler : public QObject {
-    Q_OBJECT
-    VIVY_UNMOVABLE_OBJECT(FakeVimHandler)
-    VIVY_APP_LOGGABLE_OBJECT(FakeVimHandler, logger)
-
-public:
-    explicit FakeVimHandler(QWidget *widget, QObject *parent = nullptr);
-    ~FakeVimHandler() override;
-
-    QWidget *widget();
-
-    // call before widget is deleted
-    void disconnectFromEditor();
-
-    static void updateGlobalMarksFilenames(const QString &oldFileName, const QString &newFileName);
-
-public:
-    void setCurrentFileName(const QString &fileName);
-    QString currentFileName() const;
-
-    void showMessage(MessageLevel level, const QString &msg);
-
-    // This executes an "ex" style command taking context
-    // information from the current widget.
-    void handleCommand(const QString &cmd);
-    void handleReplay(const QString &keys);
-    void handleInput(const QString &keys);
-    void enterCommandMode();
-
-    void installEventFilter();
-
-    // Convenience
-    void setupWidget();
-    void restoreWidget(int tabSize);
-
-    // Test only
-    int physicalIndentation(const QString &line) const;
-    int logicalIndentation(const QString &line) const;
-    QString tabExpand(int n) const;
-
-    void miniBufferTextEdited(const QString &text, int cursorPos, int anchorPos);
-
-    // Set text cursor position. Keeps anchor if in visual mode.
-    void setTextCursorPosition(int position);
-
-    QTextCursor textCursor() const;
-    void setTextCursor(const QTextCursor &cursor);
-
-    bool jumpToLocalMark(QChar mark, bool backTickMode);
-
-    bool eventFilter(QObject *ob, QEvent *ev) override;
-
-    Signal<void(const QString &msg, int cursorPos, int anchorPos, int messageLevel)>
-        commandBufferChanged;
-    Signal<void(const QString &msg)> statusDataChanged;
-    Signal<void(const QString &msg)> extraInformationChanged;
-    Signal<void(const QList<QTextEdit::ExtraSelection> &selection)> selectionChanged;
-    Signal<void(const QString &needle)> highlightMatches;
-    Signal<void(bool *moved, bool *forward, QTextCursor *cursor)> moveToMatchingParenthesis;
-    Signal<void(bool *result, QChar c)> checkForElectricCharacter;
-    Signal<void(int beginLine, int endLine, QChar typedChar)> indentRegion;
-    Signal<void(const QString &needle, bool forward)> simpleCompletionRequested;
-    Signal<void(const QString &key, int count)> windowCommandRequested;
-    Signal<void(bool reverse)> findRequested;
-    Signal<void(bool reverse)> findNextRequested;
-    Signal<void(bool *handled, const ExCommand &cmd)> handleExCommandRequested;
-    Signal<void()> requestDisableBlockSelection;
-    Signal<void(const QTextCursor &cursor)> requestSetBlockSelection;
-    Signal<void(QTextCursor *cursor)> requestBlockSelection;
-    Signal<void(bool *on)> requestHasBlockSelection;
-    Signal<void(int depth)> foldToggle;
-    Signal<void(bool fold)> foldAll;
-    Signal<void(int depth, bool dofold)> fold;
-    Signal<void(int count, bool current)> foldGoTo;
-    Signal<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToLocalMark;
-    Signal<void(QChar mark, bool backTickMode, const QString &fileName)> requestJumpToGlobalMark;
-    Signal<void()> completionRequested;
-    Signal<void()> tabPreviousRequested;
-    Signal<void()> tabNextRequested;
-
-public:
-    // Avoid weak tables
-    class Private;
-
-private:
-    Private *d;
-};
-
-}
-
-Q_DECLARE_METATYPE(Vivy::ExCommand)
diff --git a/src/UI/FakeVim/FakeVimTr.hh b/src/UI/FakeVim/FakeVimTr.hh
deleted file mode 100644
index 5e7c7b84..00000000
--- a/src/UI/FakeVim/FakeVimTr.hh
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-#include "PreCompiledHeaders.hh"
-namespace Vivy
-{
-struct Tr {
-    Q_DECLARE_TR_FUNCTIONS(FakeVim)
-};
-
-}
diff --git a/src/UI/MainWindow.cc b/src/UI/MainWindow.cc
index 2d42d295..c216db50 100644
--- a/src/UI/MainWindow.cc
+++ b/src/UI/MainWindow.cc
@@ -76,22 +76,6 @@ MainWindow::MainWindow() noexcept
     ACTION_ADD_SHORTCUT(openDocument, QKeySequence::Open);
     ACTION_ADD_SHORTCUT(saveFile, QKeySequence::Save);
 
-    // Custom useFakeVim action
-    {
-        editMenu->addSeparator();
-        QAction *useFakeVim = new QAction(tr("Use FakeVim"), this);
-        useFakeVim->setStatusTip("Use FakeVim with integrated text editors");
-        useFakeVim->setCheckable(true);
-        editMenu->addAction(useFakeVim);
-        connect(useFakeVim, &QAction::toggled, this, [this](bool checked) noexcept -> void {
-            const bool oldState = vivyApp->getUseFakeVimEditor();
-            if (oldState != checked) {
-                vivyApp->setUseFakeVimEditor(checked);
-                updateFakeVimUsage(checked);
-            }
-        });
-    }
-
     // Setup the tabs to display the documents
     documents = new QTabWidget(this);
     documents->setMovable(true);
@@ -181,19 +165,6 @@ MainWindow::closeEvent(QCloseEvent *event) noexcept
     QMainWindow::closeEvent(event);
 }
 
-void
-MainWindow::updateFakeVimUsage(bool yes) noexcept
-{
-    forEachViews<AbstractDocumentView>([yes](AbstractDocumentView *docView, int) noexcept -> void {
-        const bool documentExists = docView && docView->getDocument();
-        const bool isScriptEditor =
-            documentExists && (docView->getDocument()->getType() == AbstractDocument::Type::Script);
-
-        if (isScriptEditor)
-            static_cast<ScriptDocumentView *>(docView)->setUseFakeVimEditor(yes);
-    });
-}
-
 void
 MainWindow::openProperties(int index) noexcept
 {
diff --git a/src/UI/MainWindow.hh b/src/UI/MainWindow.hh
index bc59b3ce..1ea3af65 100644
--- a/src/UI/MainWindow.hh
+++ b/src/UI/MainWindow.hh
@@ -52,7 +52,6 @@ private:
 
     int getIndexOfTab(const AbstractDocumentView *const) const noexcept;
     int findFirstUntouchedDocument() const noexcept;
-    void updateFakeVimUsage(bool yes) noexcept;
     QString dialogOpenFileName(const QString &title, const QString &folder,
                                const QString &filter) noexcept;
     QString dialogSaveFileName(const QString &title, const QString &folder,
diff --git a/src/UI/ScriptDocumentView.cc b/src/UI/ScriptDocumentView.cc
index 6f98fb99..d7932ebb 100644
--- a/src/UI/ScriptDocumentView.cc
+++ b/src/UI/ScriptDocumentView.cc
@@ -1,8 +1,6 @@
 #include "PreCompiledHeaders.hh"
 #include "UI/ScriptDocumentView.hh"
 #include "UI/MainWindow.hh"
-#include "UI/ScriptViews/ScriptEditor.hh"
-#include "UI/ScriptViews/ScriptHighlighter.hh"
 #include "Lib/Utils.hh"
 #include "Lib/Log.hh"
 #include "VivyApplication.hh"
@@ -11,110 +9,17 @@ using namespace Vivy;
 
 ScriptDocumentView::ScriptDocumentView(std::shared_ptr<ScriptDocument> ptr, QWidget *parent)
     : AbstractDocumentView(AbstractDocumentView::Type::Script, parent)
-    , editor(new ScriptEditor(this))
-    , syntax(new ScriptHighlighter(editor->document()))
-    , document(ptr)
 {
     QFile textFile(ptr->getName());
     if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
         throw std::runtime_error("Failed to open script file");
     }
 
-    editor->setPlainText(QString::fromUtf8(textFile.readAll()));
-    setCentralWidget(editor);
-    editor->setFocus(Qt::OtherFocusReason);
-
-    setUseFakeVimEditor(vivyApp->getUseFakeVimEditor());
-
-    connect(document.get(), &AbstractDocument::documentChanged, this,
-            [=, this]() noexcept -> void { emit documentPropertyChanged(); });
-
-    // Same style as the editor
-    setStyleSheet(QStringLiteral("* {"
-                                 "  background-color: #232629;"
-                                 "  font-family: \"FiraCode\";"
-                                 "  font-size: 10pt"
-                                 "}"));
-
     VIVY_LOG_CTOR() << "Creating view for script " << VIVY_LOG_QUOTED(ptr->getName()) << " with "
                     << ptr->getUuid();
 }
 
-ScriptDocumentView::~ScriptDocumentView()
-{
-    setUseFakeVimEditor(false);
-    VIVY_LOG_DTOR() << "Closing a script view";
-}
-
-void
-ScriptDocumentView::setUseFakeVimEditor(bool yes) noexcept
-{
-    if (yes && !isUsingFakeVim) {
-        MainWindow *const mw = vivyApp->getMainWindow();
-        handler              = new FakeVimHandler(editor);
-        proxy                = EditorProxy::connectSignals(handler, editor);
-        isUsingFakeVim       = true;
-        connect(proxy, &EditorProxy::handleInput, handler, &FakeVimHandler::handleInput);
-        connect(proxy, &EditorProxy::requestQuit, this, [this, mw]() noexcept -> void {
-            mw->closeDocument(static_cast<AbstractDocumentView *>(this));
-        });
-        connect(proxy, &EditorProxy::requestSave, this, [this]() noexcept -> void {
-            try {
-                document->save();
-            } catch (const std::runtime_error &e) {
-                logError() << "Failed to save current document: " << e.what();
-            }
-        });
-        connect(proxy, &EditorProxy::requestSaveAndQuit, this, [this, mw]() noexcept -> void {
-            try {
-                document->save();
-                mw->closeDocument(static_cast<AbstractDocumentView *>(this));
-            } catch (const std::runtime_error &e) {
-                logError() << "Failed to save current document and close it: " << e.what();
-            }
-        });
-        initHandler(handler);
-        clearUndoRedo(editor);
-    }
-
-    else if (!yes) {
-        if (handler) {
-            handler->disconnectFromEditor();
-            delete handler;
-        }
-
-        if (proxy)
-            delete proxy;
-
-        clearUndoRedo(editor);
-        editor->setOverwriteMode(false);
-        isUsingFakeVim = false;
-        proxy          = nullptr;
-        handler        = nullptr;
-    }
-}
-
-void
-ScriptDocumentView::initHandler(FakeVimHandler *handler) noexcept
-{
-    handler->handleCommand(QStringLiteral("set nopasskeys"));
-    handler->handleCommand(QStringLiteral("set nopasscontrolkey"));
-    handler->installEventFilter();
-    handler->setupWidget();
-
-    handler->handleCommand(QStringLiteral("set expandtab"));
-    handler->handleCommand(QStringLiteral("set shiftwidth=4"));
-    handler->handleCommand(QStringLiteral("set tabstop=4"));
-    handler->handleCommand(QStringLiteral("set autoindent"));
-    handler->handleCommand(QStringLiteral("set smartindent"));
-}
-
-void
-ScriptDocumentView::clearUndoRedo(QPlainTextEdit *scriptEditor) noexcept
-{
-    scriptEditor->setUndoRedoEnabled(false);
-    scriptEditor->setUndoRedoEnabled(true);
-}
+ScriptDocumentView::~ScriptDocumentView() { VIVY_LOG_DTOR() << "Closing a script view"; }
 
 void
 ScriptDocumentView::closeDocument() noexcept
diff --git a/src/UI/ScriptDocumentView.hh b/src/UI/ScriptDocumentView.hh
index 08250de4..ff9a3e4c 100644
--- a/src/UI/ScriptDocumentView.hh
+++ b/src/UI/ScriptDocumentView.hh
@@ -9,10 +9,6 @@
 #include "VivyApplication.hh"
 #include "Lib/Script/ScriptDocument.hh"
 #include "UI/AbstractDocumentView.hh"
-#include "UI/FakeVim/FakeVimHandler.hh"
-#include "UI/ScriptViews/EditorProxy.hh"
-#include "UI/ScriptViews/ScriptHighlighter.hh"
-#include "UI/ScriptViews/ScriptEditor.hh"
 
 namespace Vivy
 {
@@ -35,20 +31,7 @@ public:
 
     void loadViews() noexcept override {}
 
-    void setUseFakeVimEditor(bool yes) noexcept;
-
-private:
-    ScriptEditor *editor{ nullptr };
-    EditorProxy *proxy{ nullptr };
-    ScriptHighlighter *syntax{ nullptr };
-    FakeVimHandler *handler{ nullptr };
     std::shared_ptr<ScriptDocument> document{ nullptr };
-    QString lastLuaErrorMsg{};
-    int lastLuaErrorLine{ -1 };
-    bool isUsingFakeVim{ false };
-
-    static void initHandler(FakeVimHandler *handler) noexcept;
-    static void clearUndoRedo(QPlainTextEdit *editor) noexcept;
 };
 }
 
diff --git a/src/UI/ScriptViews/EditorProxy.cc b/src/UI/ScriptViews/EditorProxy.cc
deleted file mode 100644
index 4232b8f0..00000000
--- a/src/UI/ScriptViews/EditorProxy.cc
+++ /dev/null
@@ -1,327 +0,0 @@
-#include "PreCompiledHeaders.hh"
-#include "UI/ScriptViews/EditorProxy.hh"
-#include "UI/MainWindow.hh"
-#include "UI/FakeVim/FakeVimHandler.hh"
-#include "UI/FakeVim/FakeVimActions.hh"
-#include "VivyApplication.hh"
-
-using namespace Vivy;
-
-Vivy::EditorProxy *
-EditorProxy::connectSignals(FakeVimHandler *handler, QPlainTextEdit *editor) noexcept
-{
-    EditorProxy *proxy = new EditorProxy(editor);
-
-    handler->commandBufferChanged.connect(
-        [proxy](const QString &contents, int cursorPos, int, int) noexcept -> void {
-            proxy->changeStatusMessage(contents, cursorPos);
-        });
-
-    handler->extraInformationChanged.connect(
-        [proxy](const QString &text) noexcept -> void { proxy->changeExtraInformation(text); });
-    handler->statusDataChanged.connect(
-        [proxy](const QString &text) noexcept -> void { proxy->changeStatusData(text); });
-    handler->highlightMatches.connect(
-        [proxy](const QString &needle) noexcept -> void { proxy->highlightMatches(needle); });
-    handler->handleExCommandRequested.connect(
-        [proxy](bool *handled, const ExCommand &cmd) noexcept -> void {
-            proxy->handleExCommand(handled, cmd);
-        });
-    handler->requestSetBlockSelection.connect([proxy](const QTextCursor &cursor) noexcept -> void {
-        proxy->requestSetBlockSelection(cursor);
-    });
-    handler->requestDisableBlockSelection.connect(
-        [proxy]() noexcept -> void { proxy->requestDisableBlockSelection(); });
-    handler->requestHasBlockSelection.connect(
-        [proxy](bool *on) noexcept -> void { proxy->requestHasBlockSelection(on); });
-
-    handler->indentRegion.connect(
-        [proxy](int beginBlock, int endBlock, QChar typedChar) noexcept -> void {
-            proxy->indentRegion(beginBlock, endBlock, typedChar);
-        });
-    handler->checkForElectricCharacter.connect([proxy](bool *result, QChar c) noexcept -> void {
-        proxy->checkForElectricCharacter(result, c);
-    });
-
-    return proxy;
-}
-
-EditorProxy::EditorProxy(QPlainTextEdit *widg) noexcept
-    : QObject()
-    , widget(widg)
-{
-    VIVY_LOG_CTOR()
-        << "Creating new proxy for a the text view " << VIVY_LOG_QUOTED(widg->windowFilePath());
-}
-
-EditorProxy::~EditorProxy() { VIVY_LOG_DTOR() << "Delete editor proxy!"; }
-
-void
-EditorProxy::changeStatusData(const QString &info) noexcept
-{
-    statusData = info;
-    updateStatusBar();
-}
-
-void
-EditorProxy::highlightMatches(const QString &pattern) noexcept
-{
-    QTextDocument *doc = widget->document();
-    Q_ASSERT(doc);
-
-    QTextEdit::ExtraSelection selection;
-    selection.format.setBackground(Qt::yellow);
-    selection.format.setForeground(Qt::black);
-
-    // Highlight matches.
-    QRegularExpression re(pattern);
-    QTextCursor cur = doc->find(re);
-    searchSelection.clear();
-
-    int a = cur.position();
-    while (!cur.isNull()) {
-        if (cur.hasSelection()) {
-            selection.cursor = cur;
-            searchSelection.append(selection);
-        } else {
-            cur.movePosition(QTextCursor::NextCharacter);
-        }
-
-        cur   = doc->find(re, cur);
-        int b = cur.position();
-
-        if (a == b) {
-            cur.movePosition(QTextCursor::NextCharacter);
-            cur = doc->find(re, cur);
-            b   = cur.position();
-            if (a == b)
-                break;
-        }
-
-        a = b;
-    }
-
-    updateExtraSelections();
-}
-
-void
-EditorProxy::changeStatusMessage(const QString &contents, int cursorPos) noexcept
-{
-    statusMessage = (cursorPos == -1)
-                        ? contents
-                        : contents.left(cursorPos) + QChar(10073) + contents.mid(cursorPos);
-    updateStatusBar();
-}
-
-void
-EditorProxy::changeExtraInformation(const QString &info) noexcept
-{
-    QMessageBox::information(widget, tr("Information"), info);
-}
-
-void
-EditorProxy::updateStatusBar() noexcept
-{
-    vivyApp->getMainWindow()->statusBar()->showMessage(statusMessage); // + statusData
-}
-
-void
-EditorProxy::handleExCommand(bool *handled, const ExCommand &cmd) noexcept
-{
-    // :wq
-    if (wantSaveAndQuit(cmd))
-        emit requestSaveAndQuit();
-
-    // :w
-    else if (wantSave(cmd))
-        emit requestSave();
-
-    else if (wantQuit(cmd)) {
-        // :q!
-        if (cmd.hasBang) {
-            logInfo() << "Want to force quit the editor!";
-            emit requestQuit();
-        }
-
-        // :q
-        else {
-            logInfo() << "Want to quit the editor";
-            emit requestQuit();
-        }
-    }
-
-    else if (wantRun(cmd))
-        emit requestRun();
-
-    else {
-        *handled = false;
-        return;
-    }
-
-    *handled = true;
-}
-
-void
-EditorProxy::requestSetBlockSelection(const QTextCursor &tc) noexcept
-{
-    const QPalette pal = widget->parentWidget() != nullptr ? widget->parentWidget()->palette()
-                                                           : QApplication::palette();
-
-    blockSelection.clear();
-    clearSelection.clear();
-
-    QTextCursor cur = tc;
-
-    QTextEdit::ExtraSelection selection;
-    selection.format.setBackground(pal.color(QPalette::Base));
-    selection.format.setForeground(pal.color(QPalette::Text));
-    selection.cursor = cur;
-    clearSelection.append(selection);
-
-    selection.format.setBackground(pal.color(QPalette::Highlight));
-    selection.format.setForeground(pal.color(QPalette::HighlightedText));
-
-    const int from = cur.positionInBlock();
-    const int to   = cur.anchor() - cur.document()->findBlock(cur.anchor()).position();
-    const int min  = qMin(cur.position(), cur.anchor());
-    const int max  = qMax(cur.position(), cur.anchor());
-    for (QTextBlock block                                 = cur.document()->findBlock(min);
-         block.isValid() && block.position() < max; block = block.next()) {
-        cur.setPosition(block.position() + qMin(from, block.length()));
-        cur.setPosition(block.position() + qMin(to, block.length()), QTextCursor::KeepAnchor);
-        selection.cursor = cur;
-        blockSelection.append(selection);
-    }
-
-    disconnect(widget, &QPlainTextEdit::selectionChanged, this, &EditorProxy::updateBlockSelection);
-    widget->setTextCursor(tc);
-    connect(widget, &QPlainTextEdit::selectionChanged, this, &EditorProxy::updateBlockSelection);
-
-    QPalette pal2 = widget->palette();
-    pal2.setColor(QPalette::Highlight, Qt::transparent);
-    pal2.setColor(QPalette::HighlightedText, Qt::transparent);
-    widget->setPalette(pal2);
-
-    updateExtraSelections();
-}
-
-void
-EditorProxy::requestDisableBlockSelection() noexcept
-{
-    const QPalette pal = widget->parentWidget() != nullptr ? widget->parentWidget()->palette()
-                                                           : QApplication::palette();
-
-    blockSelection.clear();
-    clearSelection.clear();
-    widget->setPalette(pal);
-
-    disconnect(widget, &QPlainTextEdit::selectionChanged, this, &EditorProxy::updateBlockSelection);
-
-    updateExtraSelections();
-}
-
-void
-EditorProxy::updateBlockSelection() noexcept
-{
-    requestSetBlockSelection(widget->textCursor());
-}
-
-void
-EditorProxy::requestHasBlockSelection(bool *on) noexcept
-{
-    *on = !blockSelection.isEmpty();
-}
-
-void
-EditorProxy::indentRegion(int beginBlock, int endBlock, QChar typedChar) noexcept
-{
-    QTextDocument *doc = widget->document();
-    Q_ASSERT(doc);
-
-    const int indentSize  = static_cast<int>(fakeVimSettings()->shiftWidth.value());
-    QTextBlock startBlock = doc->findBlockByNumber(beginBlock);
-
-    QTextBlock block = startBlock;
-
-    for (int i = beginBlock; i <= endBlock; ++i) {
-        const QString line          = block.text();
-
-        if (typedChar.unicode() == 0 && line.simplified().isEmpty()) {
-            // clear empty lines
-            QTextCursor cursor(block);
-            while (!cursor.atBlockEnd())
-                cursor.deleteChar();
-        }
-
-        else {
-            const QTextBlock previousBlock = block.previous();
-            const QString previousLine = previousBlock.isValid() ? previousBlock.text() : QString();
-
-            int indent = firstNonSpace(previousLine);
-            if (typedChar == '}')
-                indent = std::max(0, indent - indentSize);
-            else if (previousLine.endsWith("{"))
-                indent += indentSize;
-            const QString indentString = QString(" ").repeated(indent);
-
-            QTextCursor cursor(block);
-            cursor.beginEditBlock();
-            cursor.movePosition(QTextCursor::StartOfBlock);
-            cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor,
-                                firstNonSpace(line));
-            cursor.removeSelectedText();
-            cursor.insertText(indentString);
-            cursor.endEditBlock();
-        }
-
-        block = block.next();
-    }
-}
-
-void
-EditorProxy::checkForElectricCharacter(bool *result, QChar c) noexcept
-{
-    *result = c == '{' || c == '}';
-}
-
-int
-EditorProxy::firstNonSpace(const QString &text) noexcept
-{
-    int indent = 0;
-    while (indent < text.length() && text.at(indent) == ' ')
-        ++indent;
-    return indent;
-}
-
-void
-EditorProxy::updateExtraSelections() noexcept
-{
-    widget->setExtraSelections(clearSelection + searchSelection + blockSelection);
-}
-
-bool
-EditorProxy::wantSaveAndQuit(const ExCommand &cmd) noexcept
-{
-    return cmd.cmd == "wq";
-}
-
-bool
-EditorProxy::wantSave(const ExCommand &cmd) noexcept
-{
-    logInfo() << "Want to save the editor's file";
-    return cmd.matches("w", "write") || cmd.matches("wa", "wall");
-}
-
-bool
-EditorProxy::wantQuit(const ExCommand &cmd) noexcept
-{
-    logInfo() << "Want to quit editor";
-    return cmd.matches("q", "quit") || cmd.matches("qa", "qall");
-}
-
-bool
-EditorProxy::wantRun(const ExCommand &cmd) noexcept
-{
-    logInfo() << "Want to run a command: " << cmd.cmd;
-    return cmd.matches("run", "run") || cmd.matches("make", "make");
-}
diff --git a/src/UI/ScriptViews/EditorProxy.hh b/src/UI/ScriptViews/EditorProxy.hh
deleted file mode 100644
index 1ff5bdf9..00000000
--- a/src/UI/ScriptViews/EditorProxy.hh
+++ /dev/null
@@ -1,66 +0,0 @@
-#pragma once
-
-#include "PreCompiledHeaders.hh"
-#include "VivyApplication.hh"
-#include "Lib/Log.hh"
-#include "UI/ScriptViews/ScriptEditor.hh"
-
-namespace Vivy
-{
-class EditorProxy;
-class FakeVimHandler;
-struct ExCommand;
-
-class EditorProxy final : public QObject {
-    Q_OBJECT
-    VIVY_UNMOVABLE_OBJECT(EditorProxy)
-    VIVY_APP_LOGGABLE_OBJECT(EditorProxy, logger)
-
-    explicit EditorProxy(QPlainTextEdit *widget) noexcept;
-
-public:
-    ~EditorProxy() override;
-
-signals:
-    void handleInput(const QString &keys);
-    void requestSave();
-    void requestSaveAndQuit();
-    void requestQuit();
-    void requestRun();
-
-public slots:
-    void changeStatusData(const QString &info) noexcept;
-    void highlightMatches(const QString &pattern) noexcept;
-    void changeStatusMessage(const QString &contents, int cursorPos) noexcept;
-    void changeExtraInformation(const QString &info) noexcept;
-    void updateStatusBar() noexcept;
-    void handleExCommand(bool *handled, const ExCommand &cmd) noexcept;
-    void requestSetBlockSelection(const QTextCursor &tc) noexcept;
-    void requestDisableBlockSelection() noexcept;
-    void updateBlockSelection() noexcept;
-    void requestHasBlockSelection(bool *on) noexcept;
-    void indentRegion(int beginBlock, int endBlock, QChar typedChar) noexcept;
-    void checkForElectricCharacter(bool *result, QChar c) noexcept;
-
-private:
-    static int firstNonSpace(const QString &text) noexcept;
-
-    void updateExtraSelections() noexcept;
-    bool wantSaveAndQuit(const ExCommand &cmd) noexcept;
-    bool wantSave(const ExCommand &cmd) noexcept;
-    bool wantQuit(const ExCommand &cmd) noexcept;
-    bool wantRun(const ExCommand &cmd) noexcept;
-
-    QPlainTextEdit *widget;
-    QString statusMessage;
-    QString statusData;
-
-    QList<QTextEdit::ExtraSelection> searchSelection;
-    QList<QTextEdit::ExtraSelection> clearSelection;
-    QList<QTextEdit::ExtraSelection> blockSelection;
-
-public:
-    static EditorProxy *connectSignals(FakeVimHandler *handler, QPlainTextEdit *editor) noexcept;
-};
-
-}
diff --git a/src/UI/ScriptViews/ScriptEditor.cc b/src/UI/ScriptViews/ScriptEditor.cc
deleted file mode 100644
index 290e6cc7..00000000
--- a/src/UI/ScriptViews/ScriptEditor.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-#include "PreCompiledHeaders.hh"
-#include "UI/ScriptViews/ScriptEditor.hh"
-#include "UI/ScriptViews/ScriptHighlighter.hh"
-
-using namespace Vivy;
-
-ScriptEditor::LineNumberArea::LineNumberArea(ScriptEditor *editor) noexcept
-    : QWidget(editor)
-    , scriptEditor(editor)
-{
-}
-
-QSize
-ScriptEditor::LineNumberArea::sizeHint() const noexcept
-{
-    return QSize(scriptEditor->lineNumberAreaWidth(), 0);
-}
-
-void
-ScriptEditor::LineNumberArea::paintEvent(QPaintEvent *event) noexcept
-{
-    scriptEditor->lineNumberAreaPaintEvent(event);
-}
-
-ScriptEditor::ScriptEditor(QWidget *parent) noexcept
-    : QPlainTextEdit(parent)
-{
-    setStyleSheet(QStringLiteral("* {"
-                                 "  background-color: #232629;"
-                                 "  font-family: \"FiraCode\";"
-                                 "  font-size: 10pt"
-                                 "}"));
-    lineNumberArea = new LineNumberArea(this);
-    setFrameShape(QFrame::NoFrame);
-
-    connect(this, &ScriptEditor::blockCountChanged, this, &ScriptEditor::updateLineNumberAreaWidth);
-    connect(this, &ScriptEditor::updateRequest, this, &ScriptEditor::updateLineNumberArea);
-
-    QTextCharFormat textFormat;
-    textFormat.setForeground(QBrush(Qt::white));
-    mergeCurrentCharFormat(textFormat);
-
-    QPlainTextEdit::setCursorWidth(0);
-    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
-    setBackgroundVisible(true);
-    updateLineNumberAreaWidth(0);
-    setObjectName(QStringLiteral("Editor"));
-    setFocus();
-}
-
-void
-ScriptEditor::paintEvent(QPaintEvent *e) noexcept
-{
-    QPlainTextEdit::paintEvent(e);
-
-    if (!cursorRect.isNull() && e->rect().intersects(cursorRect)) {
-        QRect rect = cursorRect;
-        cursorRect = QRect();
-        QPlainTextEdit::viewport()->update(rect);
-    }
-
-    // Draw text cursor.
-    QRect rect = QPlainTextEdit::cursorRect();
-    if (e->rect().intersects(rect)) {
-        QPainter painter(QPlainTextEdit::viewport());
-
-        if (QPlainTextEdit::overwriteMode()) {
-            QFontMetrics fm(QPlainTextEdit::font());
-            const int position = QPlainTextEdit::textCursor().position();
-            const QChar c      = QPlainTextEdit::document()->characterAt(position);
-            rect.setWidth(fm.horizontalAdvance(c));
-            painter.setPen(Qt::NoPen);
-            painter.setBrush(QPlainTextEdit::palette().color(QPalette::Base));
-            painter.setCompositionMode(QPainter::CompositionMode_Difference);
-        } else {
-            rect.setWidth(QPlainTextEdit::cursorWidth());
-            painter.setPen(QPlainTextEdit::palette().color(QPalette::Text));
-        }
-
-        painter.drawRect(rect);
-        cursorRect = rect;
-    }
-}
-
-void
-ScriptEditor::keyPressEvent(QKeyEvent *e) noexcept
-{
-    if (e->key() == Qt::Key_Tab) {
-        QTextCursor cursor = textCursor();
-        cursor.insertText(spacesForTab);
-        e->accept();
-    }
-
-    else {
-        QPlainTextEdit::keyPressEvent(e);
-    }
-}
-
-int
-ScriptEditor::lineNumberAreaWidth() noexcept
-{
-    int digits = 1;
-    int max    = qMax(1, blockCount());
-    while (max >= 10) {
-        max /= 10;
-        ++digits;
-    }
-    if (digits == 1)
-        digits++;
-
-    const int space = 3 + fontMetrics().horizontalAdvance('9') * digits;
-    return space;
-}
-
-void
-ScriptEditor::updateLineNumberAreaWidth([[maybe_unused]] int newBlockCount) noexcept
-{
-    setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
-}
-
-void
-ScriptEditor::updateLineNumberArea(const QRect &rect, int dy) noexcept
-{
-    if (dy)
-        lineNumberArea->scroll(0, dy);
-    else
-        lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
-
-    if (rect.contains(viewport()->rect()))
-        updateLineNumberAreaWidth(0);
-}
-
-void
-ScriptEditor::resizeEvent(QResizeEvent *e) noexcept
-{
-    QPlainTextEdit::resizeEvent(e);
-    const QRect cr = contentsRect();
-    lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
-}
-
-void
-ScriptEditor::lineNumberAreaPaintEvent(QPaintEvent *event) noexcept
-{
-    QPainter painter(lineNumberArea);
-    painter.fillRect(event->rect(), QColor::fromRgb(49, 54, 59));
-    QTextBlock block  = firstVisibleBlock();
-    int blockNumber   = block.blockNumber();
-    int top           = qRound(blockBoundingGeometry(block).translated(contentOffset()).top());
-    int bottom        = top + qRound(blockBoundingRect(block).height());
-    const int current = textCursor().blockNumber();
-
-    while (block.isValid() && top <= event->rect().bottom()) {
-        if (block.isVisible() && bottom >= event->rect().top()) {
-            const bool drawCurrent = blockNumber == current;
-            const QString number =
-                drawCurrent ? QStringLiteral("->") : QString::number(blockNumber + 1);
-            painter.setPen(drawCurrent ? Qt::white : Qt::black);
-            painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(),
-                             Qt::AlignRight, number);
-        }
-
-        block  = block.next();
-        top    = bottom;
-        bottom = top + qRound(blockBoundingRect(block).height());
-        ++blockNumber;
-    }
-}
diff --git a/src/UI/ScriptViews/ScriptEditor.hh b/src/UI/ScriptViews/ScriptEditor.hh
deleted file mode 100644
index 23b748d9..00000000
--- a/src/UI/ScriptViews/ScriptEditor.hh
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-
-#ifndef __cplusplus
-#error "This is a C++ header"
-#endif
-
-#include "PreCompiledHeaders.hh"
-#include "VivyApplication.hh"
-#include "Lib/Log.hh"
-#include "Lib/Utils.hh"
-
-namespace Vivy
-{
-class ScriptEditor final : public QPlainTextEdit {
-    Q_OBJECT
-    VIVY_UNMOVABLE_OBJECT(ScriptEditor)
-    VIVY_APP_LOGGABLE_OBJECT(ScriptEditor, logger)
-
-    // Get the line numbers, private class
-    class LineNumberArea final : public QWidget {
-        VIVY_UNMOVABLE_OBJECT(LineNumberArea)
-    public:
-        LineNumberArea(ScriptEditor *editor) noexcept;
-
-        QSize sizeHint() const noexcept override;
-
-    protected:
-        void paintEvent(QPaintEvent *event) noexcept override;
-
-    private:
-        ScriptEditor *scriptEditor{ nullptr };
-    };
-
-    // Number of spaces in a tab
-    static constexpr int spacesPerTab        = 4;
-    static inline const QString spacesForTab = QString(QStringLiteral(" ")).repeated(spacesPerTab);
-
-public:
-    ScriptEditor(QWidget *parent) noexcept;
-
-    void lineNumberAreaPaintEvent(QPaintEvent *event) noexcept;
-    int lineNumberAreaWidth() noexcept;
-
-protected:
-    void resizeEvent(QResizeEvent *) noexcept override;
-    void keyPressEvent(QKeyEvent *) noexcept override;
-    void paintEvent(QPaintEvent *) noexcept override;
-
-private slots:
-    void updateLineNumberAreaWidth(int newBlockCount) noexcept;
-    void updateLineNumberArea(const QRect &rect, int dy) noexcept;
-
-private:
-    QWidget *lineNumberArea{ nullptr };
-    QRect cursorRect{};
-};
-}
diff --git a/src/UI/ScriptViews/ScriptHighlighter.cc b/src/UI/ScriptViews/ScriptHighlighter.cc
deleted file mode 100644
index c3ecb349..00000000
--- a/src/UI/ScriptViews/ScriptHighlighter.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-#include "PreCompiledHeaders.hh"
-#include "UI/ScriptViews/ScriptHighlighter.hh"
-#include "UI/Theme/Theme.hh"
-#include "VivyApplication.hh"
-#include "Lib/Utils.hh"
-
-using namespace Vivy;
-
-ScriptHighlighter::HighlightingRule::HighlightingRule(QRegularExpression pttrn, QTextCharFormat frmt)
-    : pattern(pttrn)
-    , format(frmt)
-{
-}
-
-void
-ScriptHighlighter::resetHighlightingRule() noexcept
-{
-    highlightingRules.clear();
-    const Theme::HighlightingTheme &theme = vivyApp->getTheme()->getHighlightingTheme();
-
-    // function calls
-    functionFormat.setForeground(theme.functionForeground);
-    highlightingRules.emplace_back(QRegularExpressionLiteral("\\b[A-Za-z0-9_]+[ ]*(?=\\()"), functionFormat);
-    highlightingRules.emplace_back(QRegularExpressionLiteral("\\b[A-Za-z0-9_]+[ ]*(?=\\{)"), functionFormat);
-
-    // Member and enum members
-    enumFormat.setForeground(theme.enumForeground);
-    enumFormat.setFontWeight(QFont::Bold);
-    memberFormat.setForeground(theme.memberForeground);
-    memberFormat.setFontWeight(QFont::StyleItalic);
-    highlightingRules.emplace_back(QRegularExpressionLiteral("\\.[a-z][\\dA-Za-z]*\\b"), memberFormat);
-    highlightingRules.emplace_back(QRegularExpressionLiteral("\\b[A-Z][\\dA-Z_]*\\b"), enumFormat);
-
-    // keywords
-    const QStringList keywordPatterns = {
-        QStringLiteral("\\bfunction\\b"), QStringLiteral("\\bbreak\\b"),
-        QStringLiteral("\\bgoto\\b"),     QStringLiteral("\\bdo\\b"),
-        QStringLiteral("\\bend\\b"),      QStringLiteral("\\bwhile\\b"),
-        QStringLiteral("\\brepeat\\b"),   QStringLiteral("\\buntil\\b"),
-        QStringLiteral("\\bif\\b"),       QStringLiteral("\\bthen\\b"),
-        QStringLiteral("\\belseif\\b"),   QStringLiteral("\\belse\\b"),
-        QStringLiteral("\\bfor\\b"),      QStringLiteral("\\bin\\b"),
-        QStringLiteral("\\blocal\\b"),    QStringLiteral("\\bor\\b"),
-        QStringLiteral("\\band\\b"),      QStringLiteral("\\bnot\\b"),
-        QStringLiteral("\\breturn\\b"),   QStringLiteral("\\b\\=\\b"),
-        QStringLiteral("\\b\\+\\=\\b"),   QStringLiteral("\\b\\-\\=\\b"),
-        QStringLiteral("\\b\\*\\=\\b"),   QStringLiteral("\\b\\/\\=\\b"),
-    };
-
-    keywordFormat.setForeground(theme.keywordForeground);
-    keywordFormat.setFontWeight(QFont::Bold);
-
-    for (QString const &pattern : keywordPatterns)
-        highlightingRules.emplace_back(QRegularExpression(pattern), keywordFormat);
-
-    // numbers, boolean, nil
-    const QStringList valuePatterns = {
-        QStringLiteral("\\bnil\\b"),
-        QStringLiteral("\\btrue\\b"),
-        QStringLiteral("\\bfalse\\b"),
-        QStringLiteral("\\b\\d+\\b"),
-        QStringLiteral("\\b\\d+.\\b"),
-        QStringLiteral("\\b\\d+e\\b"),
-        QStringLiteral("\\b\\[\\dA-Fa-F]+\\b"),
-    };
-
-    valueFormat.setForeground(theme.valueForeground);
-    valueFormat.setFontWeight(QFont::Normal);
-
-    for (QString const &pattern : valuePatterns)
-        highlightingRules.emplace_back(QRegularExpression(pattern), valueFormat);
-
-    // strings
-    quotationFormat.setForeground(theme.valueForeground);
-    highlightingRules.emplace_back(QRegularExpressionLiteral("\"[^\"]*\""), quotationFormat);
-    highlightingRules.emplace_back(QRegularExpressionLiteral("\'[^\']*\'"), quotationFormat);
-    quoteStartExpression = QRegularExpressionLiteral("\\[\\[");
-    quoteEndExpression   = QRegularExpressionLiteral("\\]\\]");
-
-    // comments
-    singleLineCommentFormat.setForeground(theme.commentForeground);
-    highlightingRules.emplace_back(QRegularExpressionLiteral("--[^\n]*"), singleLineCommentFormat);
-    commentStartExpression = QRegularExpressionLiteral("--\\[\\[");
-    commentEndExpression   = QRegularExpressionLiteral("\\]\\]");
-}
-
-ScriptHighlighter::ScriptHighlighter(QTextDocument *parent) noexcept
-    : QSyntaxHighlighter(parent)
-{
-    resetHighlightingRule();
-}
-
-void
-ScriptHighlighter::setCurrentBlockState(const ScriptHighlighter::BlockState state) noexcept
-{
-    QSyntaxHighlighter::setCurrentBlockState(Utils::toUnderlying(state));
-}
-
-ScriptHighlighter::BlockState
-ScriptHighlighter::previousBlockState() noexcept
-{
-    const int state = QSyntaxHighlighter::previousBlockState();
-    if (state != Utils::toUnderlying(BlockState::Quote) &&
-        state != Utils::toUnderlying(BlockState::Comment))
-        return BlockState::None;
-
-    return static_cast<BlockState>(state);
-}
-
-void
-ScriptHighlighter::highlightBlock(const QString &text) noexcept
-{
-    for (const HighlightingRule &rule : highlightingRules) {
-        QRegularExpression expression(rule.pattern);
-        QRegularExpressionMatch matchStart = expression.match(text);
-        int index = int(matchStart.capturedStart(0));
-        while (index >= 0) {
-            const int length = int(matchStart.capturedLength(0));
-            setFormat(index, length, rule.format);
-            // FIXME: unused, can delete?
-            QRegularExpressionMatch matchEnd = expression.match(text, index + length);
-            index = int(matchEnd.capturedStart(0));
-        }
-    }
-
-    setCurrentBlockState(BlockState::None);
-    const BlockState previousState = previousBlockState();
-
-    highlightStringBlock(text, previousState);
-    highlightCommentBlock(text, previousState);
-}
-
-void
-ScriptHighlighter::highlightBlock(const QString &text, const BlockState previousState,
-                                  const BlockState toHighlight, const QRegularExpression &startExpr,
-                                  const QRegularExpression &endExpr, const QTextCharFormat &format) noexcept
-{
-    int start = -1;
-    if (previousState == toHighlight) {
-        start = 0;
-    } else if (previousState == BlockState::None) {
-        QRegularExpressionMatch match = startExpr.match(text);
-        start = int(startExpr.match(text).capturedStart(0));
-    }
-
-    while (start >= 0) {
-        const int end = int(endExpr.match(text, start).capturedStart(0));
-        int length;
-
-        if (end == -1) {
-            setCurrentBlockState(toHighlight);
-            length = int(text.length()) - start;
-        } else {
-            length = end - start + int(endExpr.match(text).capturedLength(0));
-        }
-
-        setFormat(start, length, format);
-        // FIXME: unused, can delete? or expected as startExpr reference?
-        start = int(startExpr.match(text, start + length).capturedStart(0));
-    }
-}
-
-void
-ScriptHighlighter::highlightStringBlock(const QString &text,
-                                        const BlockState previousState) noexcept
-{
-    highlightBlock(text, previousState, BlockState::Quote, quoteStartExpression, quoteEndExpression,
-                   quotationFormat);
-}
-
-void
-ScriptHighlighter::highlightCommentBlock(const QString &text,
-                                         const BlockState previousState) noexcept
-{
-    highlightBlock(text, previousState, BlockState::Comment, commentStartExpression,
-                   commentEndExpression, singleLineCommentFormat);
-}
diff --git a/src/UI/ScriptViews/ScriptHighlighter.hh b/src/UI/ScriptViews/ScriptHighlighter.hh
deleted file mode 100644
index d2bc60e9..00000000
--- a/src/UI/ScriptViews/ScriptHighlighter.hh
+++ /dev/null
@@ -1,58 +0,0 @@
-#pragma once
-
-#ifndef __cplusplus
-#error "This is a C++ header"
-#endif
-
-#include "PreCompiledHeaders.hh"
-#include "UI/Theme/Theme.hh"
-
-namespace Vivy
-{
-class ScriptHighlighter final : public QSyntaxHighlighter {
-    Q_OBJECT
-
-    struct HighlightingRule {
-        HighlightingRule(QRegularExpression pttrn, QTextCharFormat frmt);
-
-        QRegularExpression pattern;
-        QTextCharFormat format;
-    };
-
-    enum class BlockState { None = -1, Quote = 1, Comment = 2 };
-
-public:
-    ScriptHighlighter(QTextDocument *parent = nullptr) noexcept;
-
-protected:
-    void highlightBlock(const QString &text) noexcept override;
-
-    void setCurrentBlockState(const BlockState) noexcept;
-    BlockState previousBlockState() noexcept;
-
-private:
-    void highlightStringBlock(const QString &text, const BlockState) noexcept;
-    void highlightCommentBlock(const QString &text, const BlockState) noexcept;
-
-    void highlightBlock(const QString &text, const BlockState previous,
-                        const BlockState toHighlight, const QRegularExpression &start, const QRegularExpression &end,
-                        const QTextCharFormat &format) noexcept;
-
-    void resetHighlightingRule() noexcept;
-
-    std::vector<HighlightingRule> highlightingRules;
-
-    QRegularExpression commentStartExpression;
-    QRegularExpression commentEndExpression;
-    QRegularExpression quoteStartExpression;
-    QRegularExpression quoteEndExpression;
-
-    QTextCharFormat keywordFormat;
-    QTextCharFormat valueFormat;
-    QTextCharFormat singleLineCommentFormat;
-    QTextCharFormat quotationFormat;
-    QTextCharFormat functionFormat;
-    QTextCharFormat enumFormat;
-    QTextCharFormat memberFormat;
-};
-}
-- 
GitLab