diff --git a/src/utils/vvim.cpp b/src/utils/vvim.cpp index 07a15e12..f7eff2c1 100644 --- a/src/utils/vvim.cpp +++ b/src/utils/vvim.cpp @@ -7,10 +7,14 @@ #include #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 &p_keys, QList &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)); + } +} diff --git a/src/utils/vvim.h b/src/utils/vvim.h index 21facdf6..d71f6516 100644 --- a/src/utils/vvim.h +++ b/src/utils/vvim.h @@ -4,6 +4,7 @@ #include #include #include +#include #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 m_keys; QList m_tokens; // Whether reset the position in block when moving cursor. bool m_resetPositionInBlock; + + QHash 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