Browse Source

vim-mode: support registers

Le Tan 8 years ago
parent
commit
71ea514bfa
2 changed files with 204 additions and 71 deletions
  1. 128 71
      src/utils/vvim.cpp
  2. 76 0
      src/utils/vvim.h

+ 128 - 71
src/utils/vvim.cpp

@@ -7,10 +7,14 @@
 #include <QDebug>
 #include "vedit.h"
 
+const QChar VVim::c_unnamedRegister = QChar('"');
+const QChar VVim::c_blackHoleRegister = QChar('_');
+const QChar VVim::c_selectionRegister = QChar('+');
+
 VVim::VVim(VEdit *p_editor)
     : QObject(p_editor), m_editor(p_editor),
       m_editConfig(&p_editor->getConfig()), m_mode(VimMode::Normal),
-      m_resetPositionInBlock(true)
+      m_resetPositionInBlock(true), m_register(c_unnamedRegister)
 {
     connect(m_editor, &VEdit::copyAvailable,
             this, &VVim::selectionToVisualMode);
@@ -179,17 +183,30 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
         goto exit;
     }
 
-    // We will add key to m_keys. If all m_keys can combined to a token, add
-    // a new token to m_tokens, clear m_keys and try to process m_tokens.
-    switch (key) {
     // Ctrl and Shift may be sent out first.
-    case Qt::Key_Control:
-        // Fall through.
-    case Qt::Key_Shift:
-    {
+    if (key == Qt::Key_Control || key == Qt::Key_Shift) {
         goto accept;
     }
 
+    if (expectingRegisterName()) {
+        // Expecting a register name.
+        QChar reg = keyToRegisterName(keyInfo);
+        if (!reg.isNull()) {
+            resetState();
+            m_register = reg;
+            m_registers[reg].m_append = (modifiers == Qt::ShiftModifier);
+
+            qDebug() << "use register" << reg << m_registers[reg].m_append;
+
+            goto accept;
+        }
+
+        goto clear_accept;
+    }
+
+    // We will add key to m_keys. If all m_keys can combined to a token, add
+    // a new token to m_tokens, clear m_keys and try to process m_tokens.
+    switch (key) {
     case Qt::Key_0:
     {
         if (modifiers == Qt::NoModifier) {
@@ -202,10 +219,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
                 goto accept;
             } else {
                 // StartOfLine.
-                if (m_tokens.isEmpty()) {
-                    // Move.
-                    m_tokens.append(Token(Action::Move));
-                }
+                tryInsertMoveAction();
 
                 m_tokens.append(Token(Movement::StartOfLine));
 
@@ -300,10 +314,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
             }
 
             V_ASSERT(mm != Movement::Invalid);
-            if (m_tokens.isEmpty()) {
-                // Move.
-                m_tokens.append(Token(Action::Move));
-            }
+            tryInsertMoveAction();
 
             m_tokens.append(Token(mm));
             processCommand(m_tokens);
@@ -435,10 +446,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
             tryGetRepeatToken(m_keys, m_tokens);
 
             if (m_keys.isEmpty()) {
-                if (m_tokens.isEmpty()) {
-                    // Move.
-                    m_tokens.append(Token(Action::Move));
-                }
+                tryInsertMoveAction();
 
                 m_tokens.append(Token(Movement::EndOfLine));
                 processCommand(m_tokens);
@@ -478,10 +486,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
         }
 
         if (mm != Movement::Invalid) {
-            if (m_tokens.isEmpty()) {
-                // Move.
-                m_tokens.append(Token(Action::Move));
-            }
+            tryInsertMoveAction();
 
             m_tokens.append(Token(mm));
             processCommand(m_tokens);
@@ -510,11 +515,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
                 mm = Movement::WORDBackward;
             }
 
-            if (m_tokens.isEmpty()) {
-                // Move.
-                m_tokens.append(Token(Action::Move));
-            }
-
+            tryInsertMoveAction();
             m_tokens.append(Token(mm));
             processCommand(m_tokens);
             break;
@@ -533,11 +534,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
             }
 
             Movement mm = Movement::PageUp;
-            if (m_tokens.isEmpty()) {
-                // Move.
-                m_tokens.append(Token(Action::Move));
-            }
-
+            tryInsertMoveAction();
             m_tokens.append(Token(mm));
             processCommand(m_tokens);
             resetPositionInBlock = false;
@@ -557,11 +554,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
             }
 
             Movement mm = Movement::HalfPageUp;
-            if (m_tokens.isEmpty()) {
-                // Move.
-                m_tokens.append(Token(Action::Move));
-            }
-
+            tryInsertMoveAction();
             m_tokens.append(Token(mm));
             processCommand(m_tokens);
             resetPositionInBlock = false;
@@ -581,11 +574,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
             }
 
             Movement mm = Movement::PageDown;
-            if (m_tokens.isEmpty()) {
-                // Move.
-                m_tokens.append(Token(Action::Move));
-            }
-
+            tryInsertMoveAction();
             m_tokens.append(Token(mm));
             processCommand(m_tokens);
             resetPositionInBlock = false;
@@ -605,14 +594,12 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
             }
 
             Movement mm = Movement::HalfPageDown;
-            if (m_tokens.isEmpty()) {
-                // Move.
-                m_tokens.append(Token(Action::Move));
-            }
-
+            tryInsertMoveAction();
             m_tokens.append(Token(mm));
             processCommand(m_tokens);
             resetPositionInBlock = false;
+        } else if (modifiers == Qt::NoModifier) {
+            // d, delete action.
         }
 
         break;
@@ -680,11 +667,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
             }
 
             Movement mm = Movement::FirstCharacter;
-            if (m_tokens.isEmpty()) {
-                // Move.
-                m_tokens.append(Token(Action::Move));
-            }
-
+            tryInsertMoveAction();
             m_tokens.append(Token(mm));
             processCommand(m_tokens);
         }
@@ -708,11 +691,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
                 mm = Movement::WORDForward;
             }
 
-            if (m_tokens.isEmpty()) {
-                // Move.
-                m_tokens.append(Token(Action::Move));
-            }
-
+            tryInsertMoveAction();
             m_tokens.append(Token(mm));
             processCommand(m_tokens);
         }
@@ -747,11 +726,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
                 }
             }
 
-            if (m_tokens.isEmpty()) {
-                // Move.
-                m_tokens.append(Token(Action::Move));
-            }
-
+            tryInsertMoveAction();
             m_tokens.append(Token(mm));
             processCommand(m_tokens);
         }
@@ -759,13 +734,28 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
         break;
     }
 
+    case Qt::Key_QuoteDbl:
+    {
+        if (modifiers == Qt::ShiftModifier) {
+            // Specify a register.
+            if (!m_keys.isEmpty() || !m_tokens.isEmpty()) {
+                // Invalid sequence.
+                break;
+            }
+
+            m_keys.append(keyInfo);
+            goto accept;
+        }
+
+        break;
+    }
+
     default:
         break;
     }
 
 clear_accept:
-    m_keys.clear();
-    m_tokens.clear();
+    resetState();
 
 accept:
     p_event->accept();
@@ -780,6 +770,7 @@ void VVim::resetState()
 {
     m_keys.clear();
     m_tokens.clear();
+    m_register = c_unnamedRegister;
     m_resetPositionInBlock = true;
 }
 
@@ -846,11 +837,6 @@ bool VVim::tryGetRepeatToken(QList<Key> &p_keys, QList<Token> &p_tokens)
     if (!p_keys.isEmpty()) {
         int repeat = numberFromKeySequence(p_keys);
         if (repeat != -1) {
-            if (p_tokens.isEmpty()) {
-                // Move.
-                p_tokens.append(Token(Action::Move));
-            }
-
             p_tokens.append(Token(repeat));
             p_keys.clear();
 
@@ -1371,3 +1357,74 @@ void VVim::expandSelectionInVisualLineMode(QTextCursor &p_cursor)
                              QTextCursor::KeepAnchor);
     }
 }
+
+void VVim::initRegisters()
+{
+    m_registers.clear();
+    for (char ch = 'a'; ch > 'z'; ++ch) {
+        m_registers[QChar(ch)] = Register(QChar(ch));
+    }
+
+    m_registers[c_unnamedRegister] = Register(c_unnamedRegister);
+    m_registers[c_blackHoleRegister] = Register(c_blackHoleRegister);
+    m_registers[c_selectionRegister] = Register(c_selectionRegister);
+}
+
+bool VVim::expectingRegisterName() const
+{
+    return m_keys.size() == 1
+           && m_keys.at(0) == Key(Qt::Key_QuoteDbl, Qt::ShiftModifier);
+}
+
+QChar VVim::keyToRegisterName(const Key &p_key) const
+{
+    if (p_key.isAlphabet()) {
+        return p_key.toAlphabet().toLower();
+    }
+
+    switch (p_key.m_key) {
+    case Qt::Key_QuoteDbl:
+        if (p_key.m_modifiers == Qt::ShiftModifier) {
+            return c_unnamedRegister;
+        }
+
+        break;
+
+    case Qt::Key_Plus:
+        if (p_key.m_modifiers == Qt::ShiftModifier) {
+            return c_selectionRegister;
+        }
+
+        break;
+
+    case Qt::Key_Underscore:
+        if (p_key.m_modifiers == Qt::ShiftModifier) {
+            return c_blackHoleRegister;
+        }
+
+        break;
+
+    default:
+        break;
+    }
+
+    return QChar();
+}
+
+bool VVim::hasActionToken() const
+{
+    for (auto const &token : m_tokens) {
+        if (token.isAction()) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void VVim::tryInsertMoveAction()
+{
+    if (!hasActionToken()) {
+        m_tokens.prepend(Token(Action::Move));
+    }
+}

+ 76 - 0
src/utils/vvim.h

@@ -4,6 +4,7 @@
 #include <QObject>
 #include <QString>
 #include <QTextCursor>
+#include <QHash>
 #include "vutils.h"
 
 class VEdit;
@@ -70,6 +71,23 @@ private:
             return m_key - Qt::Key_0;
         }
 
+        bool isAlphabet() const
+        {
+            return m_key >= Qt::Key_A
+                   && m_key <= Qt::Key_Z
+                   && (m_modifiers == Qt::NoModifier || m_modifiers == Qt::ShiftModifier);
+        }
+
+        QChar toAlphabet() const
+        {
+            V_ASSERT(isAlphabet());
+            if (m_modifiers == Qt::NoModifier) {
+                return QChar('a' + (m_key - Qt::Key_A));
+            } else {
+                return QChar('A' + (m_key - Qt::Key_A));
+            }
+        }
+
         bool operator==(const Key &p_key) const
         {
             return p_key.m_key == m_key && p_key.m_modifiers == m_modifiers;
@@ -205,6 +223,32 @@ private:
         };
     };
 
+    struct Register
+    {
+        Register(QChar p_name, const QString &p_value)
+            : m_name(p_name), m_value(p_value), m_append(false)
+        {
+        }
+
+        Register(QChar p_name)
+            : m_name(p_name), m_append(false)
+        {
+        }
+
+        Register()
+            : m_append(false)
+        {
+        }
+
+        QChar m_name;
+        QString m_value;
+
+        // This is not info of Register itself, but a hint to the handling logics
+        // whether we need to append the content to this register.
+        // Only valid for a-z registers.
+        bool m_append;
+    };
+
     // Reset all key state info.
     void resetState();
 
@@ -234,14 +278,46 @@ private:
     // of @p_cursor.
     void expandSelectionInVisualLineMode(QTextCursor &p_cursor);
 
+    // Init m_registers.
+    // Currently supported registers:
+    // a-z, A-Z (append to a-z), ", +, _
+    void initRegisters();
+
+    // Check m_keys to see if we are expecting a register name.
+    bool expectingRegisterName() const;
+
+    // Return the corresponding register name of @p_key.
+    // If @p_key is not a valid register name, return a NULL QChar.
+    QChar keyToRegisterName(const Key &p_key) const;
+
+    // Check if @m_tokens contains an action token.
+    bool hasActionToken() const;
+
+    // Try to insert a Action::Move action at the front if there is no any action
+    // token.
+    void tryInsertMoveAction();
+
     VEdit *m_editor;
     const VEditConfig *m_editConfig;
     VimMode m_mode;
+
+    // A valid command token should follow the rule:
+    // Action, Repeat, Movement.
+    // Action, Repeat, Range.
     QList<Key> m_keys;
     QList<Token> m_tokens;
 
     // Whether reset the position in block when moving cursor.
     bool m_resetPositionInBlock;
+
+    QHash<QChar, Register> m_registers;
+
+    // Currently used register.
+    QChar m_register;
+
+    static const QChar c_unnamedRegister;
+    static const QChar c_blackHoleRegister;
+    static const QChar c_selectionRegister;
 };
 
 #endif // VVIM_H