From 1264d0ea5ea817e89d0ffead3cbeae024008462a Mon Sep 17 00:00:00 2001
From: Lukas Holecek <hluk@email.cz>
Date: Mon, 10 Sep 2012 22:10:23 +0200
Subject: [PATCH] fakevim: Appearance of command line widget.

Widget for Ex and search mode is QLineEdit.

Messages are displayed using QLabel widget with different style for
error, warnings etc.

Change-Id: I4d4f799bbe261febaf6c2c21c01f6b66e1beec6e
Reviewed-by: hjk <qthjk@ovi.com>
---
 src/plugins/fakevim/fakevimhandler.cpp | 226 ++++++++++++++-----------
 src/plugins/fakevim/fakevimhandler.h   |  17 +-
 src/plugins/fakevim/fakevimplugin.cpp  | 139 ++++++++++-----
 3 files changed, 240 insertions(+), 142 deletions(-)

diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index db333bddc6..95f23d4732 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -880,6 +880,8 @@ public:
     void setPrompt(const QChar &prompt) { m_prompt = prompt; }
     void setContents(const QString &s) { m_buffer = s; m_pos = s.size(); }
 
+    void setContents(const QString &s, int pos) { m_buffer = s; m_pos = m_userPos = pos; }
+
     QStringRef userContents() const { return m_buffer.leftRef(m_userPos); }
     const QChar &prompt() const { return m_prompt; }
     const QString &contents() const { return m_buffer; }
@@ -1143,11 +1145,12 @@ public:
     void finishMovement(const QString &dotCommand = QString());
     void finishMovement(const QString &dotCommand, int count);
     void resetCommandMode();
-    void search(const SearchData &sd);
+    void search(const SearchData &sd, bool showMessages = true);
     void searchNext(bool forward = true);
     void searchBalanced(bool forward, QChar needle, QChar other);
     void highlightMatches(const QString &needle);
     void stopIncrementalFind();
+    void updateFind(bool isComplete);
 
     int mvCount() const { return m_mvcount.isEmpty() ? 1 : m_mvcount.toInt(); }
     int opCount() const { return m_opcount.isEmpty() ? 1 : m_opcount.toInt(); }
@@ -1286,8 +1289,8 @@ public:
     void enterReplaceMode();
     void enterCommandMode();
     void enterExMode();
-    void showRedMessage(const QString &msg);
-    void showBlackMessage(const QString &msg);
+    void showMessage(MessageLevel level, const QString &msg);
+    void clearMessage() { showMessage(MessageInfo, QString()); }
     void notImplementedYet();
     void updateMiniBuffer();
     void updateSelection();
@@ -1514,9 +1517,12 @@ public:
     signed char m_charClass[256];
     bool m_ctrlVActive;
 
+    void miniBufferTextEdited(const QString &text, int cursorPos);
+
     static struct GlobalData
     {
-        GlobalData() : mappings(), currentMap(&mappings), inputTimer(-1)
+        GlobalData()
+            : mappings(), currentMap(&mappings), inputTimer(-1), currentMessageLevel(MessageInfo)
         {
             // default mapping state - shouldn't be removed
             mapStates << MappingState();
@@ -1544,6 +1550,7 @@ public:
 
         // Current mini buffer message.
         QString currentMessage;
+        MessageLevel currentMessageLevel;
     } g;
 };
 
@@ -1847,7 +1854,7 @@ void FakeVimHandler::Private::updateEditor()
 
 void FakeVimHandler::Private::restoreWidget(int tabSize)
 {
-    //showBlackMessage(QString());
+    //clearMessage();
     //updateMiniBuffer();
     //EDITOR(removeEventFilter(q));
     //EDITOR(setReadOnly(m_wasReadOnly));
@@ -1968,7 +1975,7 @@ void FakeVimHandler::Private::handleMappedKeys()
     }
 
     if (maxMapDepth <= 0) {
-        showRedMessage("recursive mapping");
+        showMessage(MessageError, "recursive mapping");
         g.pendingInput.remove(0, g.currentMap.mapLength() + invalidCount);
     } else {
         const Inputs &inputs = g.currentMap.inputs();
@@ -2002,6 +2009,22 @@ void FakeVimHandler::Private::stopIncrementalFind()
     }
 }
 
+void FakeVimHandler::Private::updateFind(bool isComplete)
+{
+    if (!isComplete && !hasConfig(ConfigIncSearch))
+        return;
+
+    g.currentMessage.clear();
+
+    const QString &needle = g.searchBuffer.contents();
+    SearchData sd;
+    sd.needle = needle;
+    sd.forward = m_lastSearchForward;
+    sd.highlightCursor = !isComplete;
+    sd.highlightMatches = isComplete;
+    search(sd, isComplete);
+}
+
 bool FakeVimHandler::Private::atEmptyLine(const QTextCursor &tc) const
 {
     if (tc.isNull())
@@ -2245,7 +2268,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommand)
                 setPosition(anchor());
         }
         if (la != lp)
-            showBlackMessage(QString("%1 lines yanked").arg(qAbs(la - lp) + 1));
+            showMessage(MessageInfo, QString("%1 lines yanked").arg(qAbs(la - lp) + 1));
     } else if (m_submode == TransformSubMode) {
         if (m_subsubmode == InvertCaseSubSubMode) {
             invertCase(currentRange());
@@ -2334,12 +2357,13 @@ void FakeVimHandler::Private::updateMiniBuffer()
 
     QString msg;
     int cursorPos = -1;
+    MessageLevel messageLevel = MessageMode;
 
-    if (g.mapStates.last().silent)
+    if (g.mapStates.last().silent && g.currentMessageLevel < MessageInfo)
         g.currentMessage.clear();
 
     if (m_passing) {
-        msg = "-- PASSING --  ";
+        msg = "PASSING";
     } else if (m_subsubmode == SearchSubSubMode) {
         msg = g.searchBuffer.display();
         if (g.mapStates.size() == 1)
@@ -2351,27 +2375,28 @@ void FakeVimHandler::Private::updateMiniBuffer()
     } else if (!g.currentMessage.isEmpty()) {
         msg = g.currentMessage;
         g.currentMessage.clear();
+        messageLevel = g.currentMessageLevel;
     } else if (g.mapStates.size() > 1 && !g.mapStates.last().silent) {
         // Do not reset previous message when after running a mapped command.
         return;
     } else if (m_mode == CommandMode && isVisualMode()) {
         if (isVisualCharMode()) {
-            msg = "-- VISUAL --";
+            msg = "VISUAL";
         } else if (isVisualLineMode()) {
-            msg = "-- VISUAL LINE --";
+            msg = "VISUAL LINE";
         } else if (isVisualBlockMode()) {
-            msg = "-- VISUAL BLOCK --";
+            msg = "VISUAL BLOCK";
         }
     } else if (m_mode == InsertMode) {
-        msg = "-- INSERT --";
+        msg = "INSERT";
     } else if (m_mode == ReplaceMode) {
-        msg = "-- REPLACE --";
+        msg = "REPLACE";
     } else {
         QTC_CHECK(m_mode == CommandMode && m_subsubmode != SearchSubSubMode);
-        msg = "-- COMMAND --";
+        msg = "COMMAND";
     }
 
-    emit q->commandBufferChanged(msg, cursorPos);
+    emit q->commandBufferChanged(msg, cursorPos, messageLevel, q);
 
     int linesInDoc = linesInDocument();
     int l = cursorLine();
@@ -2387,24 +2412,18 @@ void FakeVimHandler::Private::updateMiniBuffer()
     emit q->statusDataChanged(status);
 }
 
-void FakeVimHandler::Private::showRedMessage(const QString &msg)
-{
-    //qDebug() << "MSG: " << msg;
-    g.currentMessage = msg;
-    updateMiniBuffer();
-}
-
-void FakeVimHandler::Private::showBlackMessage(const QString &msg)
+void FakeVimHandler::Private::showMessage(MessageLevel level, const QString &msg)
 {
     //qDebug() << "MSG: " << msg;
     g.currentMessage = msg;
+    g.currentMessageLevel = level;
     updateMiniBuffer();
 }
 
 void FakeVimHandler::Private::notImplementedYet()
 {
     qDebug() << "Not implemented in FakeVim";
-    showRedMessage(FakeVimHandler::tr("Not implemented in FakeVim"));
+    showMessage(MessageError, FakeVimHandler::tr("Not implemented in FakeVim"));
     updateMiniBuffer();
 }
 
@@ -2486,7 +2505,7 @@ EventResult FakeVimHandler::Private::handleCommandSubSubMode(const Input &input)
                 moveToFirstNonBlankOnLine();
             finishMovement();
         } else {
-            showRedMessage(msgMarkNotSet(input.text()));
+            showMessage(MessageError, msgMarkNotSet(input.text()));
         }
         m_subsubmode = NoSubSubMode;
     } else {
@@ -2896,7 +2915,7 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
         finishMovement();
     } else if (input.isControl('c')) {
         if (isNoVisualMode())
-            showBlackMessage("Type Alt-v,Alt-v  to quit FakeVim mode");
+            showMessage(MessageInfo, "Type Alt-v,Alt-v  to quit FakeVim mode");
         else
             leaveVisualMode();
     } else if (input.is('d') && isNoVisualMode()) {
@@ -3460,6 +3479,7 @@ EventResult FakeVimHandler::Private::handleReplaceMode(const Input &input)
         m_submode = NoSubMode;
         m_mode = CommandMode;
         finishMovement();
+        updateMiniBuffer();
     } else if (input.isKey(Key_Left)) {
         breakEditBlock();
         moveLeft(1);
@@ -3735,15 +3755,11 @@ EventResult FakeVimHandler::Private::handleExMode(const Input &input)
     } else if (input.isKey(Key_Left)) {
         g.commandBuffer.moveLeft();
     } else if (input.isReturn()) {
-        if (!g.commandBuffer.isEmpty()) {
-            //g.commandHistory.takeLast();
-            handleExCommand(g.commandBuffer.contents());
-            if (g.currentMessage.isEmpty())
-                g.currentMessage = g.commandBuffer.display();
-            g.commandBuffer.clear();
-            if (m_textedit || m_plaintextedit)
-                leaveVisualMode();
-        }
+        showMessage(MessageCommand, g.commandBuffer.display());
+        handleExCommand(g.commandBuffer.contents());
+        g.commandBuffer.clear();
+        if (m_textedit || m_plaintextedit)
+            leaveVisualMode();
     } else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
         g.commandBuffer.historyUp();
     } else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
@@ -3781,20 +3797,13 @@ EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
         m_searchCursor = QTextCursor();
         const QString &needle = g.searchBuffer.contents();
         if (!needle.isEmpty()) {
-            if (!hasConfig(ConfigIncSearch)) {
-                SearchData sd;
-                sd.needle = needle;
-                sd.forward = m_lastSearchForward;
-                sd.highlightCursor = false;
-                sd.highlightMatches = true;
-                search(sd);
-            }
+            updateFind(true);
             finishMovement(g.searchBuffer.prompt() + needle + '\n');
         } else {
             finishMovement();
         }
         if (g.currentMessage.isEmpty())
-            g.currentMessage = g.searchBuffer.display();
+            showMessage(MessageCommand, g.searchBuffer.display());
         enterCommandMode();
         highlightMatches(needle);
         g.searchBuffer.clear();
@@ -3805,23 +3814,15 @@ EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
     } else if (input.isKey(Key_Tab)) {
         g.searchBuffer.insertChar(QChar(9));
     } else if (!g.searchBuffer.handleInput(input)) {
+        //qDebug() << "IGNORED IN SEARCH MODE: " << input.key() << input.text();
         return EventUnhandled;
     }
+
     updateMiniBuffer();
 
-    if (hasConfig(ConfigIncSearch) && !input.isReturn() && !input.isEscape()) {
-        SearchData sd;
-        sd.needle = g.searchBuffer.contents();
-        sd.forward = m_lastSearchForward;
-        sd.highlightCursor = true;
-        sd.highlightMatches = false;
-        search(sd);
-    }
+    if (!input.isReturn() && !input.isEscape())
+        updateFind(false);
 
-    //else {
-    //   qDebug() << "IGNORED IN SEARCH MODE: " << input.key() << input.text();
-    //   return EventUnhandled;
-    //}
     return EventHandled;
 }
 
@@ -3848,12 +3849,12 @@ int FakeVimHandler::Private::readLineCode(QString &cmd)
         return linesInDocument();
     if (c == '\'' && !cmd.isEmpty()) {
         if (cmd.isEmpty()) {
-            showRedMessage(msgMarkNotSet(QString()));
+            showMessage(MessageError, msgMarkNotSet(QString()));
             return -1;
         }
         int m = mark(cmd.at(0).unicode());
         if (m == -1) {
-            showRedMessage(msgMarkNotSet(cmd.at(0)));
+            showMessage(MessageError, msgMarkNotSet(cmd.at(0)));
             cmd = cmd.mid(1);
             return -1;
         }
@@ -3871,7 +3872,7 @@ int FakeVimHandler::Private::readLineCode(QString &cmd)
     if (c == '\'' && !cmd.isEmpty()) {
         int pos = mark(cmd.at(0).unicode());
         if (pos == -1) {
-            showRedMessage(msgMarkNotSet(cmd.at(0)));
+            showMessage(MessageError, msgMarkNotSet(cmd.at(0)));
             cmd = cmd.mid(1);
             return -1;
         }
@@ -4189,7 +4190,7 @@ bool FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd)
     if (!cmd.matches("se", "set"))
         return false;
 
-    showBlackMessage(QString());
+    clearMessage();
     SavedAction *act = theFakeVimSettings()->item(cmd.args);
     QTC_CHECK(!cmd.args.isEmpty()); // Handled by plugin.
     if (act && act->value().canConvert(QVariant::Bool)) {
@@ -4201,7 +4202,7 @@ bool FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd)
             {} // nothing to do
     } else if (act) {
         // Non-boolean to show.
-        showBlackMessage(cmd.args + '=' + act->value().toString());
+        showMessage(MessageInfo, cmd.args + '=' + act->value().toString());
     } else if (cmd.args.startsWith(_("no"))
             && (act = theFakeVimSettings()->item(cmd.args.mid(2)))) {
         // Boolean config to be switched off.
@@ -4216,9 +4217,9 @@ bool FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd)
         QString error = theFakeVimSettings()
                 ->trySetValue(cmd.args.left(p), cmd.args.mid(p + 1));
         if (!error.isEmpty())
-            showRedMessage(error);
+            showMessage(MessageError, error);
     } else {
-        showRedMessage(FakeVimHandler::tr("Unknown option: ") + cmd.args);
+        showMessage(MessageError, FakeVimHandler::tr("Unknown option: ") + cmd.args);
     }
     updateMiniBuffer();
     updateEditor();
@@ -4278,7 +4279,7 @@ bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
     QFile file1(fileName);
     const bool exists = file1.exists();
     if (exists && !forced && !noArgs) {
-        showRedMessage(FakeVimHandler::tr
+        showMessage(MessageError, FakeVimHandler::tr
             ("File \"%1\" exists (add ! to override)").arg(fileName));
     } else if (file1.open(QIODevice::ReadWrite)) {
         // Nobody cared, so act ourselves.
@@ -4292,14 +4293,14 @@ bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
             QTextStream ts(&file2);
             ts << contents;
         } else {
-            showRedMessage(FakeVimHandler::tr
+            showMessage(MessageError, FakeVimHandler::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();
-        showBlackMessage(FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
+        showMessage(MessageInfo, FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
             .arg(fileName).arg(exists ? " " : tr(" [New] "))
             .arg(ba.count('\n')).arg(ba.size()));
         //if (quitAll)
@@ -4307,7 +4308,7 @@ bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
         //else if (quit)
         //    passUnknownExCommand(forced ? "q!" : "q");
     } else {
-        showRedMessage(FakeVimHandler::tr
+        showMessage(MessageError, FakeVimHandler::tr
             ("Cannot open file \"%1\" for reading").arg(fileName));
     }
     return true;
@@ -4330,7 +4331,7 @@ bool FakeVimHandler::Private::handleExReadCommand(const ExCommand &cmd)
     QString data = ts.readAll();
     insertText(data);
     endEditBlock();
-    showBlackMessage(FakeVimHandler::tr("\"%1\" %2L, %3C")
+    showMessage(MessageInfo, FakeVimHandler::tr("\"%1\" %2L, %3C")
         .arg(m_currentFileName).arg(data.count('\n')).arg(data.size()));
     return true;
 }
@@ -4363,7 +4364,7 @@ bool FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :!
         endEditBlock();
         leaveVisualMode();
         //qDebug() << "FILTER: " << command;
-        showBlackMessage(FakeVimHandler::tr("%n lines filtered", 0,
+        showMessage(MessageInfo, FakeVimHandler::tr("%n lines filtered", 0,
             text.count('\n')));
     }
     return true;
@@ -4386,7 +4387,7 @@ bool FakeVimHandler::Private::handleExShiftCommand(const ExCommand &cmd)
     leaveVisualMode();
     const int beginLine = lineForPosition(range.beginPos);
     const int endLine = lineForPosition(range.endPos);
-    showBlackMessage(FakeVimHandler::tr("%n lines %1ed %2 time", 0,
+    showMessage(MessageInfo, FakeVimHandler::tr("%n lines %1ed %2 time", 0,
         (endLine - beginLine + 1)).arg(cmd.cmd).arg(count));
     return true;
 }
@@ -4432,7 +4433,7 @@ bool FakeVimHandler::Private::handleExGotoCommand(const ExCommand &cmd)
 
     const int beginLine = lineForPosition(cmd.range.beginPos);
     setPosition(firstPositionInLine(beginLine));
-    showBlackMessage(QString());
+    clearMessage();
     return true;
 }
 
@@ -4445,7 +4446,7 @@ bool FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd)
     QString fileName = cmd.args;
     QFile file(fileName);
     if (!file.open(QIODevice::ReadOnly)) {
-        showRedMessage(FakeVimHandler::tr("Cannot open file %1").arg(fileName));
+        showMessage(MessageError, FakeVimHandler::tr("Cannot open file %1").arg(fileName));
         return true;
     }
 
@@ -4479,7 +4480,7 @@ bool FakeVimHandler::Private::handleExEchoCommand(const ExCommand &cmd)
     // :echo
     if (cmd.cmd != "echo")
         return false;
-    g.currentMessage = cmd.args;
+    showMessage(MessageInfo, cmd.args);
     return true;
 }
 
@@ -4491,7 +4492,7 @@ void FakeVimHandler::Private::handleExCommand(const QString &line0)
         line.chop(1);
         int percent = line.toInt();
         setPosition(firstPositionInLine(percent * linesInDocument() / 100));
-        showBlackMessage(QString());
+        clearMessage();
         return;
     }
 
@@ -4517,9 +4518,8 @@ void FakeVimHandler::Private::handleExCommand(const QString &line0)
     }
     //qDebug() << "CMD: " << cmd;
 
-    showBlackMessage(QString());
     if (!handleExCommandHelper(cmd))
-        showRedMessage(tr("Not an editor command: %1").arg(cmd.cmd));
+        showMessage(MessageError, tr("Not an editor command: %1").arg(cmd.cmd));
     enterCommandMode();
 }
 
@@ -4584,7 +4584,7 @@ void FakeVimHandler::Private::searchBalanced(bool forward, QChar needle, QChar o
     }
 }
 
-void FakeVimHandler::Private::search(const SearchData &sd)
+void FakeVimHandler::Private::search(const SearchData &sd, bool showMessages)
 {
     QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
     if (!sd.forward)
@@ -4610,19 +4610,22 @@ void FakeVimHandler::Private::search(const SearchData &sd)
                 tc = document()->find(needleExp, tc, flags);
             if (tc.isNull()) {
                 highlightMatches(QString());
-                showRedMessage(FakeVimHandler::tr("Pattern not found: %1").arg(sd.needle));
+                if (showMessages) {
+                    showMessage(MessageError,
+                        FakeVimHandler::tr("Pattern not found: %1").arg(sd.needle));
+                }
                 updateSelection();
-            } else {
+            } else if (showMessages) {
                 QString msg = sd.forward
                     ? FakeVimHandler::tr("search hit BOTTOM, continuing at TOP")
                     : FakeVimHandler::tr("search hit TOP, continuing at BOTTOM");
-                showRedMessage(msg);
+                showMessage(MessageWarning, msg);
             }
-        } else {
+        } else if (showMessages) {
             QString msg = sd.forward
                 ? FakeVimHandler::tr("search hit BOTTOM without match for: %1")
                 : FakeVimHandler::tr("search hit TOP without match for: %1");
-            showRedMessage(msg.arg(sd.needle));
+            showMessage(MessageError, msg.arg(sd.needle));
         }
     }
 
@@ -4663,8 +4666,7 @@ void FakeVimHandler::Private::searchNext(bool forward)
     sd.highlightCursor = false;
     sd.highlightMatches = true;
     m_searchStartPosition = position();
-    g.currentMessage = (m_lastSearchForward ? '/' : '?') + sd.needle;
-    updateMiniBuffer();
+    showMessage(MessageCommand, (m_lastSearchForward ? '/' : '?') + sd.needle);
     search(sd);
 }
 
@@ -4683,7 +4685,7 @@ void FakeVimHandler::Private::highlightMatches(const QString &needle)
         QRegExp needleExp = vimPatternToQtPattern(needle, hasConfig(ConfigSmartCase));
         if (!needleExp.isValid()) {
             QString error = needleExp.errorString();
-            showRedMessage(
+            showMessage(MessageError,
                 FakeVimHandler::tr("Invalid regular expression: %1").arg(error));
             return;
         }
@@ -4753,7 +4755,7 @@ void FakeVimHandler::Private::indentText(const Range &range, QChar typedChar)
     // LineForPosition has returned 1-based line numbers.
     emit q->indentRegion(beginLine - 1, endLine - 1, typedChar);
     if (beginLine != endLine)
-        showBlackMessage("MARKS ARE OFF NOW");
+        showMessage(MessageError, "MARKS ARE OFF NOW");
 }
 
 bool FakeVimHandler::Private::isElectricCharacter(QChar c) const
@@ -4877,6 +4879,32 @@ int FakeVimHandler::Private::charClass(QChar c, bool simple) const
     return c.isSpace() ? 0 : 1;
 }
 
+void FakeVimHandler::Private::miniBufferTextEdited(const QString &text, int cursorPos)
+{
+    if (m_subsubmode != SearchSubSubMode && m_mode != ExMode) {
+        editor()->setFocus();
+    } else if (text.isEmpty()) {
+        // editing cancelled
+        handleDefaultKey(Input(Qt::Key_Escape, Qt::NoModifier, QString()));
+        editor()->setFocus();
+        updateCursorShape();
+    } else {
+        CommandBuffer &cmdBuf = (m_mode == ExMode) ? g.commandBuffer : g.searchBuffer;
+        // prepend prompt character if missing
+        if (!text.startsWith(cmdBuf.prompt())) {
+            emit q->commandBufferChanged(cmdBuf.prompt() + text, cmdBuf.cursorPos() + 1, 0, q);
+            cmdBuf.setContents(text, cursorPos - 1);
+        } else {
+            cmdBuf.setContents(text.mid(1), cursorPos - 1);
+        }
+        // update search expression
+        if (m_subsubmode == SearchSubSubMode) {
+            updateFind(false);
+            exportSelection();
+        }
+    }
+}
+
 // Helper to parse a-z,A-Z,48-57,_
 static int someInt(const QString &str)
 {
@@ -5657,10 +5685,10 @@ void FakeVimHandler::Private::undo()
         m_undo.pop();
 
     if (current == rev) {
-        showBlackMessage(FakeVimHandler::tr("Already at oldest change"));
+        showMessage(MessageInfo, FakeVimHandler::tr("Already at oldest change"));
         return;
     }
-    showBlackMessage(QString());
+    clearMessage();
 
     if (!m_undo.empty()) {
         State &state = m_undo.top();
@@ -5689,10 +5717,10 @@ void FakeVimHandler::Private::redo()
         m_redo.pop();
 
     if (rev == current) {
-        showBlackMessage(FakeVimHandler::tr("Already at newest change"));
+        showMessage(MessageInfo, FakeVimHandler::tr("Already at newest change"));
         return;
     }
-    showBlackMessage(QString());
+    clearMessage();
 
     if (!m_redo.empty()) {
         State &state = m_redo.top();
@@ -6224,7 +6252,7 @@ bool FakeVimHandler::eventFilter(QObject *ob, QEvent *ev)
         return res == EventHandled;
     }
 
-    if (active && ev->type() == QEvent::KeyPress && ob == d->editor()) {
+    if (active && ev->type() == QEvent::KeyPress) {
         QKeyEvent *kev = static_cast<QKeyEvent *>(ev);
         KEY_DEBUG("KEYPRESS" << kev->key() << kev->text() << QChar(kev->key()));
         EventResult res = d->handleEvent(kev);
@@ -6300,14 +6328,9 @@ QString FakeVimHandler::currentFileName() const
     return d->m_currentFileName;
 }
 
-void FakeVimHandler::showBlackMessage(const QString &msg)
-{
-   d->showBlackMessage(msg);
-}
-
-void FakeVimHandler::showRedMessage(const QString &msg)
+void FakeVimHandler::showMessage(MessageLevel level, const QString &msg)
 {
-   d->showRedMessage(msg);
+   d->showMessage(level, msg);
 }
 
 QWidget *FakeVimHandler::widget()
@@ -6333,6 +6356,11 @@ QString FakeVimHandler::tabExpand(int n) const
     return d->tabExpand(n);
 }
 
+void FakeVimHandler::miniBufferTextEdited(const QString &text, int cursorPos)
+{
+    d->miniBufferTextEdited(text, cursorPos);
+}
+
 } // namespace Internal
 } // namespace FakeVim
 
diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h
index 7ef0d1afbf..63c1123fbb 100644
--- a/src/plugins/fakevim/fakevimhandler.h
+++ b/src/plugins/fakevim/fakevimhandler.h
@@ -77,6 +77,16 @@ struct ExCommand
     int count;
 };
 
+// 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
+};
+
 class FakeVimHandler : public QObject
 {
     Q_OBJECT
@@ -94,8 +104,7 @@ public slots:
     void setCurrentFileName(const QString &fileName);
     QString currentFileName() const;
 
-    void showBlackMessage(const QString &msg);
-    void showRedMessage(const QString &msg);
+    void showMessage(MessageLevel level, const QString &msg);
 
     // This executes an "ex" style command taking context
     // information from the current widget.
@@ -114,8 +123,10 @@ public slots:
     int logicalIndentation(const QString &line) const;
     QString tabExpand(int n) const;
 
+    void miniBufferTextEdited(const QString &text, int cursorPos);
+
 signals:
-    void commandBufferChanged(const QString &msg, int pos);
+    void commandBufferChanged(const QString &msg, int pos, int messageLevel, QObject *eventFilter);
     void statusDataChanged(const QString &msg);
     void extraInformationChanged(const QString &msg);
     void selectionChanged(const QList<QTextEdit::ExtraSelection> &selection);
diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp
index e24ca6f724..03da387b7f 100644
--- a/src/plugins/fakevim/fakevimplugin.cpp
+++ b/src/plugins/fakevim/fakevimplugin.cpp
@@ -86,6 +86,7 @@
 #include <QtPlugin>
 #include <QObject>
 #include <QSettings>
+#include <QStackedWidget>
 #include <QTextStream>
 
 #include <QDesktopServices>
@@ -111,49 +112,99 @@ const char SETTINGS_ID[]                    = "A.General";
 const char SETTINGS_EX_CMDS_ID[]            = "B.ExCommands";
 const char SETTINGS_USER_CMDS_ID[]          = "C.UserCommands";
 
-class MiniBuffer : public QLabel
+class MiniBuffer : public QStackedWidget
 {
     Q_OBJECT
 
 public:
-    void setContents(const QString &contents, int cursorPos)
+    MiniBuffer() : m_label(new QLabel(this)), m_edit(new QLineEdit(this)), m_eventFilter(0)
     {
-        QString msg = contents;
-        if (cursorPos != -1)
-            msg = contents.left(cursorPos) + QChar(10073) + contents.mid(cursorPos);
-        setText("  " + msg);
+        m_edit->installEventFilter(this);
+        connect(m_edit, SIGNAL(textEdited(QString)), SLOT(changed()));
+        connect(m_edit, SIGNAL(cursorPositionChanged(int,int)), SLOT(changed()));
+        m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
+
+        addWidget(m_label);
+        addWidget(m_edit);
     }
-};
 
-class MiniBuffer1 : public QLineEdit
-{
-    Q_OBJECT
+    void setContents(const QString &contents, int cursorPos, int messageLevel, QObject *eventFilter)
+    {
+        if (cursorPos != -1) {
+            m_edit->blockSignals(true);
+            m_label->clear();
+            m_edit->setText(contents);
+            m_edit->setCursorPosition(cursorPos);
+            m_edit->blockSignals(false);
+
+            setCurrentWidget(m_edit);
+            m_edit->setFocus();
+        } else {
+            m_label->setText(messageLevel == MessageMode ? "-- " + contents + " --" : contents);
+
+            QString css;
+            if (messageLevel == MessageError) {
+                css = QString("border:1px solid rgba(255,255,255,150);"
+                              "background-color:rgba(255,0,0,100);");
+            } else if (messageLevel == MessageWarning) {
+                css = QString("border:1px solid rgba(255,255,255,120);"
+                              "background-color:rgba(255,255,0,20);");
+            }
+            m_label->setStyleSheet(QString(
+                "*{border-radius:2px;padding-left:4px;padding-right:4px;%1}").arg(css));
 
-public:
-    MiniBuffer1()
+            if (m_edit->hasFocus())
+                emit edited(QString(), -1);
+
+            setCurrentWidget(m_label);
+        }
+
+        if (m_eventFilter != eventFilter) {
+            if (m_eventFilter != 0) {
+                m_edit->removeEventFilter(m_eventFilter);
+                disconnect(SIGNAL(edited(QString,int)));
+            }
+            if (eventFilter != 0) {
+                m_edit->installEventFilter(eventFilter);
+                connect(this, SIGNAL(edited(QString,int)),
+                        eventFilter, SLOT(miniBufferTextEdited(QString,int)));
+            }
+            m_eventFilter = eventFilter;
+        }
+    }
+
+    QSize sizeHint() const
     {
-        setFrame(false);
+        QSize size = QWidget::sizeHint();
+        // reserve maximal width for line edit widget
+        return currentWidget() == m_edit ? QSize(maximumWidth(), size.height()) : size;
     }
-    void showEvent(QShowEvent *ev)
+
+signals:
+    void edited(const QString &text, int cursorPos);
+
+private slots:
+    void changed()
     {
-        QLineEdit::showEvent(ev);
-        QColor color = Qt::black;
-        QPalette pal = parentWidget()->palette();
-        pal.setBrush(QPalette::All, QPalette::WindowText, color);
-        pal.setBrush(QPalette::All, QPalette::ButtonText, color);
-        pal.setBrush(QPalette::All, QPalette::Foreground, color);
-        pal.setBrush(QPalette::All, QPalette::Background, color);
-        //color.setAlpha(100);
-        //pal.setBrush(QPalette::Disabled, QPalette::WindowText, color);
-        //pal.setBrush(QPalette::Disabled, QPalette::ButtonText, color);
-        //pal.setBrush(QPalette::Disabled, QPalette::Foreground, color);
-        setPalette(pal);
+        emit edited(m_edit->text(), m_edit->cursorPosition());
     }
-    void setContents(const QString &contents, int cursorPos)
+
+    bool eventFilter(QObject *ob, QEvent *ev)
     {
-        setText(contents);
-        setCursorPosition(cursorPos);
+        // cancel editing on escape
+        if (m_eventFilter != 0 && ob == m_edit && ev->type() == QEvent::ShortcutOverride
+            && static_cast<QKeyEvent*>(ev)->key() == Qt::Key_Escape) {
+            emit edited(QString(), -1);
+            ev->accept();
+            return true;
+        }
+        return false;
     }
+
+private:
+    QLabel *m_label;
+    QLineEdit *m_edit;
+    QObject *m_eventFilter;
 };
 
 ///////////////////////////////////////////////////////////////////////
@@ -797,7 +848,9 @@ private slots:
     void setBlockSelection(bool);
     void hasBlockSelection(bool*);
 
-    void showCommandBuffer(const QString &contents, int cursorPos);
+    void resetCommandBuffer();
+    void showCommandBuffer(const QString &contents, int cursorPos, int messageLevel,
+                           QObject *eventFilter);
     void showExtraInformation(const QString &msg);
     void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
     void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
@@ -1340,8 +1393,8 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
 
     connect(handler, SIGNAL(extraInformationChanged(QString)),
         SLOT(showExtraInformation(QString)));
-    connect(handler, SIGNAL(commandBufferChanged(QString,int)),
-        SLOT(showCommandBuffer(QString,int)));
+    connect(handler, SIGNAL(commandBufferChanged(QString,int,int,QObject*)),
+        SLOT(showCommandBuffer(QString,int,int,QObject*)));
     connect(handler, SIGNAL(selectionChanged(QList<QTextEdit::ExtraSelection>)),
         SLOT(changeSelection(QList<QTextEdit::ExtraSelection>)));
     connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
@@ -1376,7 +1429,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
 
     // pop up the bar
     if (theFakeVimSetting(ConfigUseFakeVim)->value().toBool()) {
-       showCommandBuffer(QString(), -1);
+       resetCommandBuffer();
        handler->setupWidget();
     }
 }
@@ -1408,7 +1461,7 @@ void FakeVimPluginPrivate::setUseFakeVimInternal(bool on)
         //ICore *core = ICore::instance();
         //core->updateAdditionalContexts(Context(),
         // Context(FAKEVIM_CONTEXT));
-        showCommandBuffer(QString(), -1);
+        resetCommandBuffer();
         foreach (IEditor *editor, m_editorToHandler.keys()) {
             if (BaseTextEditorWidget *textEditor =
                     qobject_cast<BaseTextEditorWidget *>(editor->widget())) {
@@ -1485,22 +1538,22 @@ void FakeVimPluginPrivate::handleExCommand(bool *handled, const ExCommand &cmd)
             QFile file3(fileName);
             file3.open(QIODevice::ReadOnly);
             QByteArray ba = file3.readAll();
-            handler->showBlackMessage(FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
+            handler->showMessage(MessageInfo, FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
                 .arg(fileName).arg(" ")
                 .arg(ba.count('\n')).arg(ba.size()));
             if (cmd.cmd == "wq")
                 delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
         } else {
-            handler->showRedMessage(tr("File not saved"));
+            handler->showMessage(MessageError, tr("File not saved"));
         }
     } else if (cmd.matches("wa", "wall")) {
         // :w[all]
         QList<IDocument *> toSave = DocumentManager::modifiedDocuments();
         QList<IDocument *> failed = DocumentManager::saveModifiedDocumentsSilently(toSave);
         if (failed.isEmpty())
-            handler->showBlackMessage(tr("Saving succeeded"));
+            handler->showMessage(MessageInfo, tr("Saving succeeded"));
         else
-            handler->showRedMessage(tr("%n files not saved", 0, failed.size()));
+            handler->showMessage(MessageError, tr("%n files not saved", 0, failed.size()));
     } else if (cmd.matches("q", "quit")) {
         // :q[uit]
         emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
@@ -1666,11 +1719,17 @@ void FakeVimPluginPrivate::quitFakeVim()
     theFakeVimSetting(ConfigUseFakeVim)->setValue(false);
 }
 
-void FakeVimPluginPrivate::showCommandBuffer(const QString &contents, int cursorPos)
+void FakeVimPluginPrivate::resetCommandBuffer()
+{
+    showCommandBuffer(QString(), -1, 0, 0);
+}
+
+void FakeVimPluginPrivate::showCommandBuffer(const QString &contents, int cursorPos,
+                                             int messageLevel, QObject *eventFilter)
 {
     //qDebug() << "SHOW COMMAND BUFFER" << contents;
     if (MiniBuffer *w = qobject_cast<MiniBuffer *>(m_statusBar->widget()))
-        w->setContents(contents, cursorPos);
+        w->setContents(contents, cursorPos, messageLevel, eventFilter);
 }
 
 void FakeVimPluginPrivate::showExtraInformation(const QString &text)
-- 
GitLab