mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
vim-mode: support registers
This commit is contained in:
parent
cbf207d9ed
commit
71ea514bfa
@ -7,10 +7,14 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "vedit.h"
|
#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)
|
VVim::VVim(VEdit *p_editor)
|
||||||
: QObject(p_editor), m_editor(p_editor),
|
: QObject(p_editor), m_editor(p_editor),
|
||||||
m_editConfig(&p_editor->getConfig()), m_mode(VimMode::Normal),
|
m_editConfig(&p_editor->getConfig()), m_mode(VimMode::Normal),
|
||||||
m_resetPositionInBlock(true)
|
m_resetPositionInBlock(true), m_register(c_unnamedRegister)
|
||||||
{
|
{
|
||||||
connect(m_editor, &VEdit::copyAvailable,
|
connect(m_editor, &VEdit::copyAvailable,
|
||||||
this, &VVim::selectionToVisualMode);
|
this, &VVim::selectionToVisualMode);
|
||||||
@ -179,17 +183,30 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
goto exit;
|
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.
|
// Ctrl and Shift may be sent out first.
|
||||||
case Qt::Key_Control:
|
if (key == Qt::Key_Control || key == Qt::Key_Shift) {
|
||||||
// Fall through.
|
|
||||||
case Qt::Key_Shift:
|
|
||||||
{
|
|
||||||
goto accept;
|
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:
|
case Qt::Key_0:
|
||||||
{
|
{
|
||||||
if (modifiers == Qt::NoModifier) {
|
if (modifiers == Qt::NoModifier) {
|
||||||
@ -202,10 +219,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
goto accept;
|
goto accept;
|
||||||
} else {
|
} else {
|
||||||
// StartOfLine.
|
// StartOfLine.
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(Movement::StartOfLine));
|
m_tokens.append(Token(Movement::StartOfLine));
|
||||||
|
|
||||||
@ -300,10 +314,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
V_ASSERT(mm != Movement::Invalid);
|
V_ASSERT(mm != Movement::Invalid);
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(mm));
|
m_tokens.append(Token(mm));
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
@ -435,10 +446,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
tryGetRepeatToken(m_keys, m_tokens);
|
tryGetRepeatToken(m_keys, m_tokens);
|
||||||
|
|
||||||
if (m_keys.isEmpty()) {
|
if (m_keys.isEmpty()) {
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(Movement::EndOfLine));
|
m_tokens.append(Token(Movement::EndOfLine));
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
@ -478,10 +486,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mm != Movement::Invalid) {
|
if (mm != Movement::Invalid) {
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(mm));
|
m_tokens.append(Token(mm));
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
@ -510,11 +515,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
mm = Movement::WORDBackward;
|
mm = Movement::WORDBackward;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(mm));
|
m_tokens.append(Token(mm));
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
break;
|
break;
|
||||||
@ -533,11 +534,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Movement mm = Movement::PageUp;
|
Movement mm = Movement::PageUp;
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(mm));
|
m_tokens.append(Token(mm));
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
resetPositionInBlock = false;
|
resetPositionInBlock = false;
|
||||||
@ -557,11 +554,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Movement mm = Movement::HalfPageUp;
|
Movement mm = Movement::HalfPageUp;
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(mm));
|
m_tokens.append(Token(mm));
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
resetPositionInBlock = false;
|
resetPositionInBlock = false;
|
||||||
@ -581,11 +574,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Movement mm = Movement::PageDown;
|
Movement mm = Movement::PageDown;
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(mm));
|
m_tokens.append(Token(mm));
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
resetPositionInBlock = false;
|
resetPositionInBlock = false;
|
||||||
@ -605,14 +594,12 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Movement mm = Movement::HalfPageDown;
|
Movement mm = Movement::HalfPageDown;
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(mm));
|
m_tokens.append(Token(mm));
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
resetPositionInBlock = false;
|
resetPositionInBlock = false;
|
||||||
|
} else if (modifiers == Qt::NoModifier) {
|
||||||
|
// d, delete action.
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -680,11 +667,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Movement mm = Movement::FirstCharacter;
|
Movement mm = Movement::FirstCharacter;
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(mm));
|
m_tokens.append(Token(mm));
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
}
|
}
|
||||||
@ -708,11 +691,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
mm = Movement::WORDForward;
|
mm = Movement::WORDForward;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(mm));
|
m_tokens.append(Token(mm));
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
}
|
}
|
||||||
@ -747,11 +726,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_tokens.isEmpty()) {
|
tryInsertMoveAction();
|
||||||
// Move.
|
|
||||||
m_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tokens.append(Token(mm));
|
m_tokens.append(Token(mm));
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
}
|
}
|
||||||
@ -759,13 +734,28 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_accept:
|
clear_accept:
|
||||||
m_keys.clear();
|
resetState();
|
||||||
m_tokens.clear();
|
|
||||||
|
|
||||||
accept:
|
accept:
|
||||||
p_event->accept();
|
p_event->accept();
|
||||||
@ -780,6 +770,7 @@ void VVim::resetState()
|
|||||||
{
|
{
|
||||||
m_keys.clear();
|
m_keys.clear();
|
||||||
m_tokens.clear();
|
m_tokens.clear();
|
||||||
|
m_register = c_unnamedRegister;
|
||||||
m_resetPositionInBlock = true;
|
m_resetPositionInBlock = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -846,11 +837,6 @@ bool VVim::tryGetRepeatToken(QList<Key> &p_keys, QList<Token> &p_tokens)
|
|||||||
if (!p_keys.isEmpty()) {
|
if (!p_keys.isEmpty()) {
|
||||||
int repeat = numberFromKeySequence(p_keys);
|
int repeat = numberFromKeySequence(p_keys);
|
||||||
if (repeat != -1) {
|
if (repeat != -1) {
|
||||||
if (p_tokens.isEmpty()) {
|
|
||||||
// Move.
|
|
||||||
p_tokens.append(Token(Action::Move));
|
|
||||||
}
|
|
||||||
|
|
||||||
p_tokens.append(Token(repeat));
|
p_tokens.append(Token(repeat));
|
||||||
p_keys.clear();
|
p_keys.clear();
|
||||||
|
|
||||||
@ -1371,3 +1357,74 @@ void VVim::expandSelectionInVisualLineMode(QTextCursor &p_cursor)
|
|||||||
QTextCursor::KeepAnchor);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
|
#include <QHash>
|
||||||
#include "vutils.h"
|
#include "vutils.h"
|
||||||
|
|
||||||
class VEdit;
|
class VEdit;
|
||||||
@ -70,6 +71,23 @@ private:
|
|||||||
return m_key - Qt::Key_0;
|
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
|
bool operator==(const Key &p_key) const
|
||||||
{
|
{
|
||||||
return p_key.m_key == m_key && p_key.m_modifiers == m_modifiers;
|
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.
|
// Reset all key state info.
|
||||||
void resetState();
|
void resetState();
|
||||||
|
|
||||||
@ -234,14 +278,46 @@ private:
|
|||||||
// of @p_cursor.
|
// of @p_cursor.
|
||||||
void expandSelectionInVisualLineMode(QTextCursor &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;
|
VEdit *m_editor;
|
||||||
const VEditConfig *m_editConfig;
|
const VEditConfig *m_editConfig;
|
||||||
VimMode m_mode;
|
VimMode m_mode;
|
||||||
|
|
||||||
|
// A valid command token should follow the rule:
|
||||||
|
// Action, Repeat, Movement.
|
||||||
|
// Action, Repeat, Range.
|
||||||
QList<Key> m_keys;
|
QList<Key> m_keys;
|
||||||
QList<Token> m_tokens;
|
QList<Token> m_tokens;
|
||||||
|
|
||||||
// Whether reset the position in block when moving cursor.
|
// Whether reset the position in block when moving cursor.
|
||||||
bool m_resetPositionInBlock;
|
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
|
#endif // VVIM_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user