From 2f997060311f09e4495c4217b430aafb16ab8bc1 Mon Sep 17 00:00:00 2001
From: hjk <qtc-committer@nokia.com>
Date: Tue, 18 May 2010 14:48:12 +0200
Subject: [PATCH] fakevim: prepare for better integration with creator core

For :ex commands, the plugin is now asked first unconditionally.
The handler only provides fallbacks for the standalone case.
---
 src/plugins/fakevim/fakevimactions.cpp |   5 -
 src/plugins/fakevim/fakevimactions.h   |   1 -
 src/plugins/fakevim/fakevimhandler.cpp | 276 ++++++++++++-------------
 src/plugins/fakevim/fakevimhandler.h   |  42 +++-
 src/plugins/fakevim/fakevimplugin.cpp  | 119 +++++------
 5 files changed, 219 insertions(+), 224 deletions(-)

diff --git a/src/plugins/fakevim/fakevimactions.cpp b/src/plugins/fakevim/fakevimactions.cpp
index 8f9aeba53af..a17fbe146c1 100644
--- a/src/plugins/fakevim/fakevimactions.cpp
+++ b/src/plugins/fakevim/fakevimactions.cpp
@@ -209,11 +209,6 @@ FakeVimSettings *theFakeVimSettings()
     item->setSettingsKey(group, _("IsKeyword"));
     instance->insertItem(ConfigIsKeyword, item, _("iskeyword"), _("isk"));
 
-    item = new SavedAction(instance);
-    item->setText(QCoreApplication::translate("FakeVim::Internal",
-        "FakeVim properties..."));
-    instance->insertItem(SettingsDialog, item);
-
     // Invented here.
     item = new SavedAction(instance);
     item->setDefaultValue(false);
diff --git a/src/plugins/fakevim/fakevimactions.h b/src/plugins/fakevim/fakevimactions.h
index 2b8c2fbd7df..5929376bc68 100644
--- a/src/plugins/fakevim/fakevimactions.h
+++ b/src/plugins/fakevim/fakevimactions.h
@@ -64,7 +64,6 @@ enum FakeVimSettingsCode
     ConfigIsKeyword,
 
     // other actions
-    SettingsDialog,
     ConfigShowMarks,
 };
 
diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index 94d34687409..ca6147d1406 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -217,14 +217,6 @@ enum MoveType
                            all characters in the affected lines up to the end
                            of these lines.
 */
-enum RangeMode
-{
-    RangeCharMode,         // v
-    RangeLineMode,         // V
-    RangeLineModeExclusive,
-    RangeBlockMode,        // Ctrl-v
-    RangeBlockAndTailMode, // Ctrl-v for D and X
-};
 
 enum EventResult
 {
@@ -258,38 +250,36 @@ struct Register
     RangeMode rangemode;
 };
 
-struct Range
-{
-    Range()
-        : beginPos(-1), endPos(-1), rangemode(RangeCharMode)
-    {}
 
-    Range(int b, int e, RangeMode m = RangeCharMode)
-        : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
-    {}
 
-    QString toString() const
-    {
-        return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
-            .arg(rangemode);
-    }
+Range::Range()
+    : beginPos(-1), endPos(-1), rangemode(RangeCharMode)
+{}
 
-    int beginPos;
-    int endPos;
-    RangeMode rangemode;
-};
+Range::Range(int b, int e, RangeMode m)
+    : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
+{}
 
-struct ExCommand
+QString Range::toString() const
 {
-    ExCommand(const QString &c, int b = -1, int e = -1)
-        : line(c), range(b, e, RangeLineMode) {}
-    QString line;
-    Range range;
-};
+    return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
+        .arg(rangemode);
+}
+
+QDebug operator<<(QDebug ts, const Range &range)
+{
+    return ts << '[' << range.beginPos << ',' << range.endPos << ']';
+}
+
+
+
+ExCommand::ExCommand(const QString &c, const QString &a, const Range &r)
+    : cmd(c), hasBang(false), args(a), range(r)
+{}
 
 QDebug operator<<(QDebug ts, const ExCommand &cmd)
 {
-    return ts << cmd.line << cmd.range.beginPos << cmd.range.endPos;
+    return ts << cmd.cmd << ' ' << cmd.args << ' ' << cmd.range;
 }
 
 QDebug operator<<(QDebug ts, const QList<QTextEdit::ExtraSelection> &sels)
@@ -650,13 +640,6 @@ public:
         { m_tc.beginEditBlock(); m_tc.insertText("x");
           m_tc.deletePreviousChar(); m_tc.endEditBlock(); }
 
-    // this asks the layer above (e.g. the fake vim plugin or the
-    // stand-alone test application to handle the command)
-    void passUnknownExCommand(const QString &cmd);
-    // this asks the layer above (e.g. the fake vim plugin or the
-    // stand-alone test application to handle the set command)
-    void passUnknownSetCommand(const QString &cmd);
-
     bool isVisualMode() const { return m_visualMode != NoVisualMode; }
     bool isNoVisualMode() const { return m_visualMode == NoVisualMode; }
     bool isVisualCharMode() const { return m_visualMode == VisualCharMode; }
@@ -806,6 +789,7 @@ public:
     QString m_oldNeedle;
 
     bool handleExCommandHelper(const ExCommand &cmd); // Returns success.
+    bool handleExPluginCommand(const ExCommand &cmd); // Handled by plugin?
     bool handleExBangCommand(const ExCommand &cmd);
     bool handleExDeleteCommand(const ExCommand &cmd);
     bool handleExGotoCommand(const ExCommand &cmd);
@@ -815,7 +799,7 @@ public:
     bool handleExReadCommand(const ExCommand &cmd);
     bool handleExRedoCommand(const ExCommand &cmd);
     bool handleExSetCommand(const ExCommand &cmd);
-    bool handleExShiftRightCommand(const ExCommand &cmd);
+    bool handleExShiftCommand(const ExCommand &cmd);
     bool handleExSourceCommand(const ExCommand &cmd);
     bool handleExSubstituteCommand(const ExCommand &cmd);
     bool handleExWriteCommand(const ExCommand &cmd);
@@ -2819,7 +2803,8 @@ void FakeVimHandler::Private::handleCommand(const QString &cmd)
 bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
     // :substitute
 {
-    QString line = cmd.line;
+    QString line = cmd.cmd + ' ' + cmd.args;
+    line = line.trimmed();
     if (line.startsWith(_("substitute")))
         line = line.mid(10);
     else if (line.startsWith('s') && line.size() > 1
@@ -2827,6 +2812,7 @@ bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
         line = line.mid(1);
     else
         return false;
+
     // we have /{pattern}/{string}/[flags]  now
     if (line.isEmpty())
         return false;
@@ -2899,12 +2885,10 @@ bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
 
 bool FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
 {
-    const int pos1 = cmd0.line.indexOf(QLatin1Char(' '));
-
     QByteArray modes;
     enum Type { Map, Noremap, Unmap } type;
 
-    QByteArray cmd = cmd0.line.left(pos1).toLatin1();
+    QByteArray cmd = cmd0.cmd.toLatin1();
 
     // Strange formatting. But everything else is even uglier.
     if (cmd == "map") { modes = "nvo"; type = Map; } else
@@ -2942,15 +2926,15 @@ bool FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
     else
         return false;
 
-    const int pos2 = cmd0.line.indexOf(QLatin1Char(' '), pos1 + 1);
-    if (pos1 == -1 || pos2 == -1) {
+    const int pos = cmd0.args.indexOf(QLatin1Char(' '));
+    if (pos == -1) {
         // FIXME: Dump mappings here.
         //qDebug() << g.mappings;
         return true;;
     }
 
-    QString lhs = cmd0.line.mid(pos1 + 1, pos2 - pos1 - 1);
-    QString rhs = cmd0.line.mid(pos2 + 1);
+    QString lhs = cmd0.args.left(pos);
+    QString rhs = cmd0.args.mid(pos + 1);
     Inputs key;
     key.parseFrom(lhs);
     //qDebug() << "MAPPING: " << modes << lhs << rhs;
@@ -2973,14 +2957,13 @@ bool FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
     return true;
 }
 
-bool FakeVimHandler::Private::handleExHistoryCommand(const ExCommand &cmd) // :history
+bool FakeVimHandler::Private::handleExHistoryCommand(const ExCommand &cmd)
 {
-    static QRegExp reHistory("^his(tory)?( (.*))?$");
-    if (reHistory.indexIn(cmd.line) == -1)
+    // :history
+    if (cmd.cmd != "his" && cmd.cmd != "history")
         return false;
 
-    QString arg = reHistory.cap(3);
-    if (arg.isEmpty()) {
+    if (cmd.args.isEmpty()) {
         QString info;
         info += "#  command history\n";
         int i = 0;
@@ -2996,67 +2979,65 @@ bool FakeVimHandler::Private::handleExHistoryCommand(const ExCommand &cmd) // :h
     return true;
 }
 
-bool FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd) // :set
+bool FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd)
 {
-    static QRegExp reSet("^set?( (.*))?$");
-    if (reSet.indexIn(cmd.line) == -1)
+    // :set
+    if (cmd.cmd != "se" && cmd.cmd != "set")
         return false;
 
     showBlackMessage(QString());
-    QString arg = reSet.cap(2).trimmed();
-    SavedAction *act = theFakeVimSettings()->item(arg);
-    if (arg.isEmpty()) {
-        theFakeVimSetting(SettingsDialog)->trigger(QVariant());
-    } else if (act && act->value().type() == QVariant::Bool) {
-        // boolean config to be switched on
+    SavedAction *act = theFakeVimSettings()->item(cmd.args);
+    QTC_ASSERT(!cmd.args.isEmpty(), /**/); // Handled by plugin.
+    if (act && act->value().type() == QVariant::Bool) {
+        // Boolean config to be switched on.
         bool oldValue = act->value().toBool();
         if (oldValue == false)
             act->setValue(true);
         else if (oldValue == true)
             {} // nothing to do
     } else if (act) {
-        // non-boolean to show
-        showBlackMessage(arg + '=' + act->value().toString());
-    } else if (arg.startsWith(_("no"))
-            && (act = theFakeVimSettings()->item(arg.mid(2)))) {
-        // boolean config to be switched off
+        // Non-boolean to show.
+        showBlackMessage(cmd.args + '=' + act->value().toString());
+    } else if (cmd.args.startsWith(_("no"))
+            && (act = theFakeVimSettings()->item(cmd.args.mid(2)))) {
+        // Boolean config to be switched off.
         bool oldValue = act->value().toBool();
         if (oldValue == true)
             act->setValue(false);
         else if (oldValue == false)
             {} // nothing to do
-    } else if (arg.contains('=')) {
-        // non-boolean config to set
-        int p = arg.indexOf('=');
-        act = theFakeVimSettings()->item(arg.left(p));
+    } else if (cmd.args.contains('=')) {
+        // Non-boolean config to set.
+        int p = cmd.args.indexOf('=');
+        act = theFakeVimSettings()->item(cmd.args.left(p));
         if (act)
-            act->setValue(arg.mid(p + 1));
+            act->setValue(cmd.args.mid(p + 1));
     } else {
-        passUnknownSetCommand(arg);
+        showRedMessage(FakeVimHandler::tr("Unknown option: ") + cmd.args);
     }
     updateMiniBuffer();
     updateEditor();
     return true;
 }
 
-bool FakeVimHandler::Private::handleExNormalCommand(const ExCommand &cmd) // :normal
+bool FakeVimHandler::Private::handleExNormalCommand(const ExCommand &cmd)
 {
-    static QRegExp reNormal("^norm(al)?( (.*))?$");
-    if (reNormal.indexIn(cmd.line) == -1)
+    // :normal
+    if (cmd.cmd != "norm" && cmd.cmd != "normal") 
         return false;
     //qDebug() << "REPLAY NORMAL: " << quoteUnprintable(reNormal.cap(3));
-    replay(reNormal.cap(3), 1);
+    replay(cmd.args, 1);
     return true;
 }
 
-bool FakeVimHandler::Private::handleExDeleteCommand(const ExCommand &cmd) // :d
+bool FakeVimHandler::Private::handleExDeleteCommand(const ExCommand &cmd)
 {
-    static QRegExp reDelete("^d(elete)?( (.*))?$");
-    if (reDelete.indexIn(cmd.line) == -1)
+    // :delete
+    if (cmd.cmd != "d" && cmd.cmd != "delete")
         return false;
 
     setCurrentRange(cmd.range);
-    QString reg = reDelete.cap(3);
+    QString reg = cmd.args;
     QString text = selectText(cmd.range);
     removeText(currentRange());
     if (!reg.isEmpty()) {
@@ -3068,10 +3049,10 @@ bool FakeVimHandler::Private::handleExDeleteCommand(const ExCommand &cmd) // :d
 }
 
 bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
-    // :w, :x, :q, :wq, ...
 {
-    static QRegExp reWrite("^[wx]q?a?!?( (.*))?$");
-    if (reWrite.indexIn(cmd.line) == -1) // :w and :x
+    // :w, :x, :wq, ...
+    //static QRegExp reWrite("^[wx]q?a?!?( (.*))?$");
+    if (cmd.cmd != "w" && cmd.cmd != "x" && cmd.cmd != "wq")
         return false;
 
     int beginLine = lineForPosition(cmd.range.beginPos);
@@ -3082,16 +3063,11 @@ bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
     if (endLine == -1)
         endLine = linesInDocument();
     //qDebug() << "LINES: " << beginLine << endLine;
-    int indexOfSpace = cmd.line.indexOf(QChar(' '));
-    QString prefix;
-    if (indexOfSpace < 0)
-        prefix = cmd.line;
-    else
-        prefix = cmd.line.left(indexOfSpace);
-    const bool forced = prefix.contains(QChar('!'));
-    const bool quit = prefix.contains(QChar('q')) || prefix.contains(QChar('x'));
-    const bool quitAll = quit && prefix.contains(QChar('a'));
-    QString fileName = reWrite.cap(2);
+    QString prefix = cmd.args;
+    const bool forced = cmd.hasBang;
+    //const bool quit = prefix.contains(QChar('q')) || prefix.contains(QChar('x'));
+    //const bool quitAll = quit && prefix.contains(QChar('a'));
+    QString fileName = cmd.args;
     if (fileName.isEmpty())
         fileName = m_currentFileName;
     QFile file1(fileName);
@@ -3100,25 +3076,21 @@ bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
         showRedMessage(FakeVimHandler::tr
             ("File \"%1\" exists (add ! to override)").arg(fileName));
     } else if (file1.open(QIODevice::ReadWrite)) {
+        // Nobody cared, so act ourselves.
         file1.close();
         QTextCursor tc = m_tc;
         Range range(firstPositionInLine(beginLine),
             firstPositionInLine(endLine), RangeLineMode);
         QString contents = selectText(range);
         m_tc = tc;
-        bool handled = false;
-        emit q->writeFileRequested(&handled, fileName, contents);
-        // Nobody cared, so act ourselves.
-        if (!handled) {
-            QFile::remove(fileName);
-            QFile file2(fileName);
-            if (file2.open(QIODevice::ReadWrite)) {
-                QTextStream ts(&file2);
-                ts << contents;
-            } else {
-                showRedMessage(FakeVimHandler::tr
-                   ("Cannot open file \"%1\" for writing").arg(fileName));
-            }
+        QFile::remove(fileName);
+        QFile file2(fileName);
+        if (file2.open(QIODevice::ReadWrite)) {
+            QTextStream ts(&file2);
+            ts << contents;
+        } else {
+            showRedMessage(FakeVimHandler::tr
+               ("Cannot open file \"%1\" for writing").arg(fileName));
         }
         // Check result by reading back.
         QFile file3(fileName);
@@ -3127,10 +3099,10 @@ bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
         showBlackMessage(FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
             .arg(fileName).arg(exists ? " " : " [New] ")
             .arg(ba.count('\n')).arg(ba.size()));
-        if (quitAll)
-            passUnknownExCommand(forced ? "qa!" : "qa");
-        else if (quit)
-            passUnknownExCommand(forced ? "q!" : "q");
+        //if (quitAll)
+        //    passUnknownExCommand(forced ? "qa!" : "qa");
+        //else if (quit)
+        //    passUnknownExCommand(forced ? "q!" : "q");
     } else {
         showRedMessage(FakeVimHandler::tr
             ("Cannot open file \"%1\" for reading").arg(fileName));
@@ -3138,16 +3110,17 @@ bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
     return true;
 }
 
-bool FakeVimHandler::Private::handleExReadCommand(const ExCommand &cmd) // :r
+bool FakeVimHandler::Private::handleExReadCommand(const ExCommand &cmd)
 {
-    if (!cmd.line.startsWith(_("r ")))
+    // :read
+    if (cmd.cmd != "r" && cmd.cmd != "read")
         return false;
 
     beginEditBlock();
     moveToStartOfLine();
     setTargetColumn();
     moveDown();
-    m_currentFileName = cmd.line.mid(2).trimmed();
+    m_currentFileName = cmd.args;
     QFile file(m_currentFileName);
     file.open(QIODevice::ReadOnly);
     QTextStream ts(&file);
@@ -3161,12 +3134,12 @@ bool FakeVimHandler::Private::handleExReadCommand(const ExCommand &cmd) // :r
 
 bool FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :!
 {
-    if (!cmd.line.startsWith(QLatin1Char('!')))
+    if (!cmd.cmd.startsWith(QLatin1Char('!')))
         return false;
 
     setCurrentRange(cmd.range);
     int targetPosition = firstPositionInLine(lineForPosition(cmd.range.beginPos));
-    QString command = cmd.line.mid(1).trimmed();
+    QString command = QString(cmd.cmd.mid(1) + ' ' + cmd.args).trimmed();
     QString text = selectText(cmd.range);
     QProcess proc;
     proc.start(command);
@@ -3190,24 +3163,29 @@ bool FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :!
     return true;
 }
 
-bool FakeVimHandler::Private::handleExShiftRightCommand(const ExCommand &cmd) // :>
+bool FakeVimHandler::Private::handleExShiftCommand(const ExCommand &cmd)
 {
-    if (!cmd.line.startsWith(QLatin1Char('>')))
+    if (cmd.cmd != "<" && cmd.cmd != ">")
         return false;
 
     setCurrentRange(cmd.range);
-    shiftRegionRight(1);
+    int count = qMin(1, cmd.args.toInt());
+    if (cmd.cmd == "<")
+        shiftRegionLeft(count);
+    else
+        shiftRegionRight(count);
     leaveVisualMode();
     const int beginLine = lineForPosition(cmd.range.beginPos);
     const int endLine = lineForPosition(cmd.range.endPos);
-    showBlackMessage(FakeVimHandler::tr("%n lines >ed %1 time", 0,
-        (endLine - beginLine + 1)).arg(1));
+    showBlackMessage(FakeVimHandler::tr("%n lines %1ed %2 time", 0,
+        (endLine - beginLine + 1)).arg(cmd.cmd).arg(count));
     return true;
 }
 
-bool FakeVimHandler::Private::handleExRedoCommand(const ExCommand &cmd) // :redo
+bool FakeVimHandler::Private::handleExRedoCommand(const ExCommand &cmd)
 {
-    if (cmd.line != "red" && cmd.line != "redo")
+    // :redo
+    if (cmd.cmd != "red" && cmd.cmd != "redo")
         return false;
 
     redo();
@@ -3215,10 +3193,10 @@ bool FakeVimHandler::Private::handleExRedoCommand(const ExCommand &cmd) // :redo
     return true;
 }
 
-bool FakeVimHandler::Private::handleExGotoCommand(const ExCommand &cmd) // :<nr>
+bool FakeVimHandler::Private::handleExGotoCommand(const ExCommand &cmd)
 {
-    // Only "range" given.
-    if (!cmd.line.isEmpty())
+    // :<nr>
+    if (!cmd.cmd.isEmpty())
         return false;
 
     const int beginLine = lineForPosition(cmd.range.beginPos);
@@ -3227,13 +3205,13 @@ bool FakeVimHandler::Private::handleExGotoCommand(const ExCommand &cmd) // :<nr>
     return true;
 }
 
-bool FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd) // :source
+bool FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd)
 {
-    const int pos = cmd.line.indexOf(' ');
-    if (cmd.line.leftRef(pos) != "so" && cmd.line.leftRef(pos) != "source")
+    // :source
+    if (cmd.cmd != "so" && cmd.cmd != "source")
         return false;
 
-    QString fileName = cmd.line.mid(pos + 1).trimmed();
+    QString fileName = cmd.args;
     QFile file(fileName);
     if (!file.open(QIODevice::ReadOnly)) {
         showRedMessage(FakeVimHandler::tr("Can't open file %1").arg(fileName));
@@ -3266,8 +3244,6 @@ bool FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd) // :so
 void FakeVimHandler::Private::handleExCommand(const QString &line0)
 {
     QString line = line0; // Make sure we have a copy to prevent aliasing.
-    ExCommand cmd(line, -1, -1);
-
     // FIXME: that seems to be different for %w and %s
     if (line.startsWith(QLatin1Char('%')))
         line = "1,$" + line.mid(1);
@@ -3282,19 +3258,26 @@ void FakeVimHandler::Private::handleExCommand(const QString &line0)
         endLine = beginLine;
     const int beginPos = firstPositionInLine(beginLine);
     const int endPos = lastPositionInLine(endLine);
+    ExCommand cmd;
+    const QString arg0 = line.section(' ', 0, 0);
+    cmd.cmd = arg0;
+    cmd.args = line.mid(arg0.size() + 1).trimmed();
     cmd.range = Range(beginPos, endPos, RangeLineMode);
-    cmd.line = line;
+    cmd.hasBang = arg0.endsWith('!');
+    if (cmd.hasBang)
+        cmd.cmd.chop(1);
     //qDebug() << "CMD: " << cmd;
 
     enterCommandMode();
     showBlackMessage(QString());
     if (!handleExCommandHelper(cmd))
-        passUnknownExCommand(cmd.line);
+        showRedMessage(tr("Not an editor command: %1").arg(cmd.cmd));
 }
 
 bool FakeVimHandler::Private::handleExCommandHelper(const ExCommand &cmd)
 {
-    return handleExGotoCommand(cmd)
+    return handleExPluginCommand(cmd)
+        || handleExGotoCommand(cmd)
         || handleExBangCommand(cmd)
         || handleExHistoryCommand(cmd)
         || handleExDeleteCommand(cmd)
@@ -3303,27 +3286,21 @@ bool FakeVimHandler::Private::handleExCommandHelper(const ExCommand &cmd)
         || handleExReadCommand(cmd)
         || handleExRedoCommand(cmd)
         || handleExSetCommand(cmd)
-        || handleExShiftRightCommand(cmd)
+        || handleExShiftCommand(cmd)
         || handleExSourceCommand(cmd)
         || handleExSubstituteCommand(cmd)
         || handleExWriteCommand(cmd);
 }
 
-void FakeVimHandler::Private::passUnknownExCommand(const QString &cmd)
+bool FakeVimHandler::Private::handleExPluginCommand(const ExCommand &cmd)
 {
     EDITOR(setTextCursor(m_tc));
-    emit q->handleExCommandRequested(cmd);
+    bool handled = false;
+    emit q->handleExCommandRequested(&handled, cmd);
     if (m_plaintextedit || m_textedit)
         m_tc = EDITOR(textCursor());
-}
-
-void FakeVimHandler::Private::passUnknownSetCommand(const QString &arg)
-{
-    bool handled = false;
-    emit q->handleSetCommandRequested(&handled, arg);
-    if (!handled) {
-        showRedMessage(FakeVimHandler::tr("Unknown option: ") + arg);
-    }
+    //qDebug() << "HANDLER REQUEST: " << cmd.cmd << handled;
+    return handled;
 }
 
 static void vimPatternToQtPattern(QString *needle, QTextDocument::FindFlags *flags)
@@ -4585,6 +4562,11 @@ void FakeVimHandler::setCurrentFileName(const QString &fileName)
    d->m_currentFileName = fileName;
 }
 
+QString FakeVimHandler::currentFileName() const
+{
+    return d->m_currentFileName;
+}
+
 void FakeVimHandler::showBlackMessage(const QString &msg)
 {
    d->showBlackMessage(msg);
diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h
index 7543d8cf862..4e877d31c8e 100644
--- a/src/plugins/fakevim/fakevimhandler.h
+++ b/src/plugins/fakevim/fakevimhandler.h
@@ -38,6 +38,38 @@
 namespace FakeVim {
 namespace Internal {
 
+enum RangeMode
+{
+    RangeCharMode,         // v
+    RangeLineMode,         // V
+    RangeLineModeExclusive,
+    RangeBlockMode,        // Ctrl-v
+    RangeBlockAndTailMode, // Ctrl-v for D and X
+};
+
+struct Range
+{
+    Range();
+    Range(int b, int e, RangeMode m = RangeCharMode);
+    QString toString() const;
+
+    int beginPos;
+    int endPos;
+    RangeMode rangemode;
+};
+
+struct ExCommand
+{
+    ExCommand() : hasBang(false) {}
+    ExCommand(const QString &cmd, const QString &args = QString(),
+        const Range &range = Range());
+
+    QString cmd;
+    bool hasBang;
+    QString args;
+    Range range;
+};
+
 class FakeVimHandler : public QObject
 {
     Q_OBJECT
@@ -53,6 +85,8 @@ public:
 
 public slots:
     void setCurrentFileName(const QString &fileName);
+    QString currentFileName() const;
+
     void showBlackMessage(const QString &msg);
     void showRedMessage(const QString &msg);
 
@@ -76,8 +110,6 @@ signals:
     void statusDataChanged(const QString &msg);
     void extraInformationChanged(const QString &msg);
     void selectionChanged(const QList<QTextEdit::ExtraSelection> &selection);
-    void writeFileRequested(bool *handled,
-        const QString &fileName, const QString &contents);
     void writeAllRequested(QString *error);
     void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
     void checkForElectricCharacter(bool *result, QChar c);
@@ -86,8 +118,7 @@ signals:
     void windowCommandRequested(int key);
     void findRequested(bool reverse);
     void findNextRequested(bool reverse);
-    void handleExCommandRequested(const QString &cmd);
-    void handleSetCommandRequested(bool *handled, const QString &cmd);
+    void handleExCommandRequested(bool *handled, const ExCommand &cmd);
 
 public:
     class Private;
@@ -101,4 +132,7 @@ private:
 } // namespace Internal
 } // namespace FakeVim
 
+Q_DECLARE_METATYPE(FakeVim::Internal::ExCommand);
+
+
 #endif // FAKEVIM_HANDLER_H
diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp
index 58f44ff0987..17510545b90 100644
--- a/src/plugins/fakevim/fakevimplugin.cpp
+++ b/src/plugins/fakevim/fakevimplugin.cpp
@@ -73,9 +73,9 @@
 #include <indenter.h>
 
 #include <QtCore/QDebug>
+#include <QtCore/QFile>
 #include <QtCore/QtPlugin>
 #include <QtCore/QObject>
-#include <QtCore/QPoint>
 #include <QtCore/QSettings>
 #include <QtCore/QTextStream>
 
@@ -503,12 +503,10 @@ private slots:
     void showCommandBuffer(const QString &contents);
     void showExtraInformation(const QString &msg);
     void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
-    void writeFile(bool *handled, const QString &fileName, const QString &contents);
     void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
     void checkForElectricCharacter(bool *result, QChar c);
     void indentRegion(int *amount, int beginLine, int endLine,  QChar typedChar);
-    void handleExCommand(const QString &cmd);
-    void handleSetCommand(bool *handled, QString cmd);
+    void handleExCommand(bool *handled, const ExCommand &cmd);
 
     void handleDelayedQuitAll(bool forced);
     void handleDelayedQuit(bool forced, Core::IEditor *editor);
@@ -611,8 +609,6 @@ bool FakeVimPluginPrivate::initialize()
     connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
         this, SLOT(editorOpened(Core::IEditor*)));
 
-    connect(theFakeVimSetting(SettingsDialog), SIGNAL(triggered()),
-        this, SLOT(showSettingsDialog()));
     connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
         this, SLOT(setUseFakeVim(QVariant)));
     connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
@@ -628,7 +624,7 @@ bool FakeVimPluginPrivate::initialize()
     cmd->setAttribute(Command::CA_Hide);
     connect(switchFilePrevAction, SIGNAL(triggered()), this, SLOT(switchFilePrev()));
 
-    // Delayed operatiosn
+    // Delayed operations.
     connect(this, SIGNAL(delayedQuitRequested(bool,Core::IEditor*)),
         this, SLOT(handleDelayedQuit(bool,Core::IEditor*)), Qt::QueuedConnection);
     connect(this, SIGNAL(delayedQuitAllRequested(bool)),
@@ -830,8 +826,6 @@ void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
         this, SLOT(showExtraInformation(QString)));
     connect(handler, SIGNAL(commandBufferChanged(QString)),
         this, SLOT(showCommandBuffer(QString)));
-    connect(handler, SIGNAL(writeFileRequested(bool*,QString,QString)),
-        this, SLOT(writeFile(bool*,QString,QString)));
     connect(handler, SIGNAL(selectionChanged(QList<QTextEdit::ExtraSelection>)),
         this, SLOT(changeSelection(QList<QTextEdit::ExtraSelection>)));
     connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
@@ -849,10 +843,8 @@ void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
     connect(handler, SIGNAL(findNextRequested(bool)),
         this, SLOT(findNext(bool)));
 
-    connect(handler, SIGNAL(handleExCommandRequested(QString)),
-        this, SLOT(handleExCommand(QString)));
-    connect(handler, SIGNAL(handleSetCommandRequested(bool *,QString)),
-        this, SLOT(handleSetCommand(bool *,QString)));
+    connect(handler, SIGNAL(handleExCommandRequested(bool*,ExCommand)),
+        this, SLOT(handleExCommand(bool*,ExCommand)));
 
     handler->setCurrentFileName(editor->file()->fileName());
     handler->installEventFilter();
@@ -913,36 +905,12 @@ void FakeVimPluginPrivate::checkForElectricCharacter(bool *result, QChar c)
         *result = bt->isElectricCharacter(c);
 }
 
-void FakeVimPluginPrivate::writeFile(bool *handled,
-    const QString &fileName, const QString &contents)
-{
-    Q_UNUSED(contents)
-
-    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
-    if (!handler)
-        return;
-
-    Core::IEditor *editor = m_editorToHandler.key(handler);
-    if (editor && editor->file()->fileName() == fileName) {
-        // Handle that as a special case for nicer interaction with core
-        Core::IFile *file = editor->file();
-        Core::ICore::instance()->fileManager()->blockFileChange(file);
-        file->save(fileName);
-        Core::ICore::instance()->fileManager()->unblockFileChange(file);
-        *handled = true;
-    }
-}
-
-void FakeVimPluginPrivate::handleExCommand(const QString &cmd)
+void FakeVimPluginPrivate::handleExCommand(bool *handled, const ExCommand &cmd)
 {
     using namespace Core;
+    //qDebug() << "PLUGIN HANDLE: " << cmd.cmd;
 
-    QString cmd0 = cmd.section(QLatin1Char(' '), 0, 0);
-    bool hasBang = false;
-    if (cmd.endsWith('!')) {
-        hasBang = true;
-        cmd0.chop(1);
-    }
+    *handled = false;
 
     FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
     if (!handler)
@@ -951,7 +919,27 @@ void FakeVimPluginPrivate::handleExCommand(const QString &cmd)
     EditorManager *editorManager = EditorManager::instance();
     QTC_ASSERT(editorManager, return);
 
-    if (cmd0 == "wa" || cmd0 == "wall") {
+    *handled = true;
+    if (cmd.cmd == "w" || cmd.cmd == "write") {
+        Core::IEditor *editor = m_editorToHandler.key(handler);
+        const QString fileName = handler->currentFileName();
+        if (editor && editor->file()->fileName() == fileName) {
+            // Handle that as a special case for nicer interaction with core
+            Core::IFile *file = editor->file();
+            Core::ICore::instance()->fileManager()->blockFileChange(file);
+            file->save(fileName);
+            Core::ICore::instance()->fileManager()->unblockFileChange(file);
+            // Check result by reading back.
+            QFile file3(fileName);
+            file3.open(QIODevice::ReadOnly);
+            QByteArray ba = file3.readAll();
+            handler->showBlackMessage(FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
+                .arg(fileName).arg(" ")
+                .arg(ba.count('\n')).arg(ba.size()));
+        } else {
+            handler->showRedMessage(tr("File not saved"));
+        }
+    } else if (cmd.cmd == "wa" || cmd.cmd == "wall") {
         // :wa
         FileManager *fm = ICore::instance()->fileManager();
         QList<IFile *> toSave = fm->modifiedFiles();
@@ -960,50 +948,47 @@ void FakeVimPluginPrivate::handleExCommand(const QString &cmd)
             handler->showBlackMessage(tr("Saving succeeded"));
         else
             handler->showRedMessage(tr("%n files not saved", 0, failed.size()));
-    } else if (cmd0 == "q" || cmd0 == "quit") {
+    } else if (cmd.cmd == "q" || cmd.cmd == "quit") {
         // :q[uit]
-        emit delayedQuitRequested(hasBang, m_editorToHandler.key(handler));
-    } else if (cmd0 == "qa" || cmd0 == "qall") {
+        emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
+    } else if (cmd.cmd == "qa" || cmd.cmd == "qall") {
         // :qa
-        emit delayedQuitAllRequested(hasBang);
-    } else if (cmd0 == "sp" || cmd0 == "split") {
+        emit delayedQuitAllRequested(cmd.hasBang);
+    } else if (cmd.cmd == "sp" || cmd.cmd == "split") {
         // :sp[lit]
         triggerAction(Core::Constants::SPLIT);
-    } else if (cmd0 == "vs" || cmd0 == "vsplit") {
+    } else if (cmd.cmd == "vs" || cmd.cmd == "vsplit") {
         // :vs[plit]
         triggerAction(Core::Constants::SPLIT_SIDE_BY_SIDE);
-    } else if (cmd0 == "mak" || cmd0 == "make") {
+    } else if (cmd.cmd == "mak" || cmd.cmd == "make") {
         // :mak[e][!] [arguments]
         triggerAction(ProjectExplorer::Constants::BUILD);
+    } else if (cmd.cmd == "se" || cmd.cmd == "set") {
+        if (cmd.args.isEmpty()) {
+            // :set
+            showSettingsDialog();
+        } else if (cmd.args == "ic" || cmd.args == "ignorecase") {
+            // :set noic
+            setActionChecked(Find::Constants::CASE_SENSITIVE, false);
+            *handled = false; // Let the handler see it as well.
+        } else if (cmd.args == "noic" || cmd.args == "noignorecase") {
+            // :set noic
+            setActionChecked(Find::Constants::CASE_SENSITIVE, true);
+            *handled = false; // Let the handler see it as well.
+        }
     } else {
+        // Check whether one of the configure commands matches.
         typedef QMap<QString, QRegExp>::const_iterator Iterator;
         const Iterator end = s_exCommandMap.constEnd();
         for (Iterator it = s_exCommandMap.constBegin(); it != end; ++it) {
             const QString &id = it.key();
             const QRegExp &re = it.value();
-
-            if (!re.pattern().isEmpty() && re.indexIn(cmd) != -1) {
+            if (!re.pattern().isEmpty() && re.indexIn(cmd.args) != -1) {
                 triggerAction(id);
                 return;
             }
         }
-
-        handler->showRedMessage(tr("Not an editor command: %1").arg(cmd));
-    }
-}
-
-void FakeVimPluginPrivate::handleSetCommand(bool *handled, QString cmd)
-{
-    *handled = false;
-    bool value = true;
-    if (cmd.startsWith("no")) {
-        value = false;
-        cmd = cmd.mid(2);
-    }
-
-    if (cmd == "ic" || cmd == "ignorecase") {
-        setActionChecked(Find::Constants::CASE_SENSITIVE, value);
-        *handled = true;
+        *handled = false;
     }
 }
 
-- 
GitLab