mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-06 14:29:54 +08:00
vim-mode: support Ctrl+R to read a register
Support `Ctrl+R` to read a register both in Insert mode and command line.
This commit is contained in:
parent
cd2ac10509
commit
eb71c8eff1
@ -177,6 +177,7 @@ VNote supports following features of Vim:
|
|||||||
- `/` and `?` to search
|
- `/` and `?` to search
|
||||||
- `n` and `N` to find next or previous occurence;
|
- `n` and `N` to find next or previous occurence;
|
||||||
- `Ctrl+N` and `Ctrl+P` to navigate through the search history;
|
- `Ctrl+N` and `Ctrl+P` to navigate through the search history;
|
||||||
|
- `Ctrl+R` to read the content of a register;
|
||||||
|
|
||||||
For now, VNote does **NOT** support the macro and repeat(`.`) features of Vim.
|
For now, VNote does **NOT** support the macro and repeat(`.`) features of Vim.
|
||||||
|
|
||||||
|
@ -178,6 +178,7 @@ VNote支持以下几个Vim的特性:
|
|||||||
- `/` 和 `?` 开始查找
|
- `/` 和 `?` 开始查找
|
||||||
- `n` 和 `N` 查找下一处或上一处;
|
- `n` 和 `N` 查找下一处或上一处;
|
||||||
- `Ctrl+N` 和 `Ctrl+P` 浏览查找历史;
|
- `Ctrl+N` 和 `Ctrl+P` 浏览查找历史;
|
||||||
|
- `Ctrl+R` 读取指定寄存器的值;
|
||||||
|
|
||||||
VNote目前暂时不支持Vim的宏和重复(`.`)特性。
|
VNote目前暂时不支持Vim的宏和重复(`.`)特性。
|
||||||
|
|
||||||
|
@ -23,18 +23,24 @@ const int VVim::SearchHistory::c_capacity = 50;
|
|||||||
|
|
||||||
#define ADDKEY(x, y) case (x): {ch = (y); break;}
|
#define ADDKEY(x, y) case (x): {ch = (y); break;}
|
||||||
|
|
||||||
|
// See if @p_modifiers is Control which is different on macOs and Windows.
|
||||||
|
static bool isControlModifier(int p_modifiers)
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
|
||||||
|
return p_modifiers == Qt::MetaModifier;
|
||||||
|
#else
|
||||||
|
return p_modifiers == Qt::ControlModifier;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Returns NULL QChar if invalid.
|
// Returns NULL QChar if invalid.
|
||||||
static QChar keyToChar(int p_key, int p_modifiers)
|
static QChar keyToChar(int p_key, int p_modifiers)
|
||||||
{
|
{
|
||||||
if (p_modifiers != Qt::NoModifier
|
|
||||||
&& p_modifiers != Qt::ShiftModifier) {
|
|
||||||
return QChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_key >= Qt::Key_0 && p_key <= Qt::Key_9) {
|
if (p_key >= Qt::Key_0 && p_key <= Qt::Key_9) {
|
||||||
return QChar('0' + (p_key - Qt::Key_0));
|
return QChar('0' + (p_key - Qt::Key_0));
|
||||||
} else if (p_key >= Qt::Key_A && p_key <= Qt::Key_Z) {
|
} else if (p_key >= Qt::Key_A && p_key <= Qt::Key_Z) {
|
||||||
if (p_modifiers == Qt::ShiftModifier) {
|
if (p_modifiers == Qt::ShiftModifier
|
||||||
|
|| isControlModifier(p_modifiers)) {
|
||||||
return QChar('A' + (p_key - Qt::Key_A));
|
return QChar('A' + (p_key - Qt::Key_A));
|
||||||
} else {
|
} else {
|
||||||
return QChar('a' + (p_key - Qt::Key_A));
|
return QChar('a' + (p_key - Qt::Key_A));
|
||||||
@ -85,11 +91,26 @@ static QChar keyToChar(int p_key, int p_modifiers)
|
|||||||
return ch;
|
return ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString keyToString(int p_key, int p_modifiers)
|
||||||
|
{
|
||||||
|
QChar ch = keyToChar(p_key, p_modifiers);
|
||||||
|
if (ch.isNull()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isControlModifier(p_modifiers)) {
|
||||||
|
return QString("^") + ch;
|
||||||
|
} else {
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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::Invalid),
|
m_editConfig(&p_editor->getConfig()), m_mode(VimMode::Invalid),
|
||||||
m_resetPositionInBlock(true), m_regName(c_unnamedRegister),
|
m_resetPositionInBlock(true), m_regName(c_unnamedRegister),
|
||||||
m_leaderKey(Key(Qt::Key_Space)), m_replayLeaderSequence(false)
|
m_leaderKey(Key(Qt::Key_Space)), m_replayLeaderSequence(false),
|
||||||
|
m_registerPending(false)
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_editConfig->m_enableVimMode);
|
Q_ASSERT(m_editConfig->m_enableVimMode);
|
||||||
|
|
||||||
@ -348,16 +369,6 @@ static int percentageToBlockNumber(const QTextDocument *p_doc, int p_percent)
|
|||||||
return num >= 0 ? num : 0;
|
return num >= 0 ? num : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if @p_modifiers is Control which is different on macOs and Windows.
|
|
||||||
static bool isControlModifier(int p_modifiers)
|
|
||||||
{
|
|
||||||
#if defined(Q_OS_MACOS) || defined(Q_OS_MAC)
|
|
||||||
return p_modifiers == Qt::MetaModifier;
|
|
||||||
#else
|
|
||||||
return p_modifiers == Qt::ControlModifier;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace each of the character of selected text with @p_char.
|
// Replace each of the character of selected text with @p_char.
|
||||||
// Returns true if replacement has taken place.
|
// Returns true if replacement has taken place.
|
||||||
// Need to setTextCursor() after calling this.
|
// Need to setTextCursor() after calling this.
|
||||||
@ -479,6 +490,27 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
goto clear_accept;
|
goto clear_accept;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_registerPending) {
|
||||||
|
// Ctrl and Shift may be sent out first.
|
||||||
|
if (key == Qt::Key_Control || key == Qt::Key_Shift || key == Qt::Key_Meta) {
|
||||||
|
goto accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expecting a register name.
|
||||||
|
QChar reg = keyToRegisterName(keyInfo);
|
||||||
|
if (!reg.isNull()) {
|
||||||
|
// Insert register content.
|
||||||
|
m_editor->insertPlainText(m_registers[reg].read());
|
||||||
|
}
|
||||||
|
|
||||||
|
goto clear_accept;
|
||||||
|
} else if (key == Qt::Key_R && isControlModifier(modifiers)) {
|
||||||
|
// Ctrl+R, insert the content of a register.
|
||||||
|
m_pendingKeys.append(keyInfo);
|
||||||
|
m_registerPending = true;
|
||||||
|
goto accept;
|
||||||
|
}
|
||||||
|
|
||||||
// Let it be handled outside VVim.
|
// Let it be handled outside VVim.
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@ -2101,6 +2133,7 @@ void VVim::resetState()
|
|||||||
m_pendingKeys.clear();
|
m_pendingKeys.clear();
|
||||||
setRegister(c_unnamedRegister);
|
setRegister(c_unnamedRegister);
|
||||||
m_resetPositionInBlock = true;
|
m_resetPositionInBlock = true;
|
||||||
|
m_registerPending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
VimMode VVim::getMode() const
|
VimMode VVim::getMode() const
|
||||||
@ -4941,7 +4974,7 @@ QString VVim::getPendingKeys() const
|
|||||||
{
|
{
|
||||||
QString str;
|
QString str;
|
||||||
for (auto const & key : m_pendingKeys) {
|
for (auto const & key : m_pendingKeys) {
|
||||||
str.append(keyToChar(key.m_key, key.m_modifiers));
|
str.append(keyToString(key.m_key, key.m_modifiers));
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
@ -5450,3 +5483,14 @@ void VVim::clearSearchHighlight()
|
|||||||
{
|
{
|
||||||
m_editor->clearSearchedWordHighlight();
|
m_editor->clearSearchedWordHighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString VVim::readRegister(int p_key, int p_modifiers)
|
||||||
|
{
|
||||||
|
Key keyInfo(p_key, p_modifiers);
|
||||||
|
QChar reg = keyToRegisterName(keyInfo);
|
||||||
|
if (!reg.isNull()) {
|
||||||
|
return m_registers[reg].read();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
@ -200,6 +200,10 @@ public:
|
|||||||
QString getPreviousCommandHistory(VVim::CommandLineType p_type,
|
QString getPreviousCommandHistory(VVim::CommandLineType p_type,
|
||||||
const QString &p_cmd);
|
const QString &p_cmd);
|
||||||
|
|
||||||
|
// Read the register content.
|
||||||
|
// Returns empty string if it is not a valid register.
|
||||||
|
QString readRegister(int p_key, int p_modifiers);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
// Emit when current mode has been changed.
|
// Emit when current mode has been changed.
|
||||||
void modeChanged(VimMode p_mode);
|
void modeChanged(VimMode p_mode);
|
||||||
@ -825,6 +829,9 @@ private:
|
|||||||
// Search history.
|
// Search history.
|
||||||
SearchHistory m_searchHistory;
|
SearchHistory m_searchHistory;
|
||||||
|
|
||||||
|
// Whether we are expecting to read a register to insert.
|
||||||
|
bool m_registerPending;
|
||||||
|
|
||||||
static const QChar c_unnamedRegister;
|
static const QChar c_unnamedRegister;
|
||||||
static const QChar c_blackHoleRegister;
|
static const QChar c_blackHoleRegister;
|
||||||
static const QChar c_selectionRegister;
|
static const QChar c_selectionRegister;
|
||||||
|
@ -29,9 +29,7 @@ VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file)
|
|||||||
|
|
||||||
void VEditOperations::insertTextAtCurPos(const QString &p_text)
|
void VEditOperations::insertTextAtCurPos(const QString &p_text)
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
m_editor->insertPlainText(p_text);
|
||||||
cursor.insertText(p_text);
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VEditOperations::~VEditOperations()
|
VEditOperations::~VEditOperations()
|
||||||
|
@ -80,6 +80,16 @@ void VVimIndicator::setupUI()
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(m_cmdLineEdit, &VVimCmdLineEdit::requestRegister,
|
||||||
|
this, [this](int p_key, int p_modifiers){
|
||||||
|
if (m_vim) {
|
||||||
|
QString val = m_vim->readRegister(p_key, p_modifiers);
|
||||||
|
if (!val.isEmpty()) {
|
||||||
|
m_cmdLineEdit->setText(m_cmdLineEdit->text() + val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
m_cmdLineEdit->hide();
|
m_cmdLineEdit->hide();
|
||||||
|
|
||||||
m_modeLabel = new QLabel(this);
|
m_modeLabel = new QLabel(this);
|
||||||
@ -320,7 +330,8 @@ void VVimIndicator::triggerCommandLine(VVim::CommandLineType p_type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
VVimCmdLineEdit::VVimCmdLineEdit(QWidget *p_parent)
|
VVimCmdLineEdit::VVimCmdLineEdit(QWidget *p_parent)
|
||||||
: QLineEdit(p_parent), m_type(VVim::CommandLineType::Invalid)
|
: QLineEdit(p_parent), m_type(VVim::CommandLineType::Invalid),
|
||||||
|
m_registerPending(false)
|
||||||
{
|
{
|
||||||
// When user delete all the text, cancel command input.
|
// When user delete all the text, cancel command input.
|
||||||
connect(this, &VVimCmdLineEdit::textChanged,
|
connect(this, &VVimCmdLineEdit::textChanged,
|
||||||
@ -340,6 +351,8 @@ VVimCmdLineEdit::VVimCmdLineEdit(QWidget *p_parent)
|
|||||||
m_userLastInput = p_text.right(p_text.size() - 1);
|
m_userLastInput = p_text.right(p_text.size() - 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
m_originStyleSheet = styleSheet();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString VVimCmdLineEdit::getCommand() const
|
QString VVimCmdLineEdit::getCommand() const
|
||||||
@ -404,6 +417,20 @@ void VVimCmdLineEdit::keyPressEvent(QKeyEvent *p_event)
|
|||||||
int key = p_event->key();
|
int key = p_event->key();
|
||||||
int modifiers = p_event->modifiers();
|
int modifiers = p_event->modifiers();
|
||||||
|
|
||||||
|
if (m_registerPending) {
|
||||||
|
// Ctrl and Shift may be sent out first.
|
||||||
|
if (key == Qt::Key_Control || key == Qt::Key_Shift || key == Qt::Key_Meta) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expecting a register name.
|
||||||
|
emit requestRegister(key, modifiers);
|
||||||
|
|
||||||
|
p_event->accept();
|
||||||
|
setRegisterPending(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((key == Qt::Key_Return && modifiers == Qt::NoModifier)
|
if ((key == Qt::Key_Return && modifiers == Qt::NoModifier)
|
||||||
|| (key == Qt::Key_Enter && modifiers == Qt::KeypadModifier)) {
|
|| (key == Qt::Key_Enter && modifiers == Qt::KeypadModifier)) {
|
||||||
// Enter, complete the command line input.
|
// Enter, complete the command line input.
|
||||||
@ -456,10 +483,21 @@ void VVimCmdLineEdit::keyPressEvent(QKeyEvent *p_event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Qt::Key_R:
|
||||||
|
{
|
||||||
|
if (isControlModifier(modifiers)) {
|
||||||
|
// Ctrl+R, insert the content of a register.
|
||||||
|
setRegisterPending(true);
|
||||||
|
p_event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
QLineEdit::keyPressEvent(p_event);
|
QLineEdit::keyPressEvent(p_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -479,3 +517,16 @@ void VVimCmdLineEdit::restoreUserLastInput()
|
|||||||
{
|
{
|
||||||
setCommand(m_userLastInput);
|
setCommand(m_userLastInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VVimCmdLineEdit::setRegisterPending(bool p_pending)
|
||||||
|
{
|
||||||
|
if (p_pending && !m_registerPending) {
|
||||||
|
// Going to pending.
|
||||||
|
setStyleSheet("QLineEdit { background: #D6EACE }");
|
||||||
|
} else if (!p_pending && m_registerPending) {
|
||||||
|
// Leaving pending.
|
||||||
|
setStyleSheet(m_originStyleSheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_registerPending = p_pending;
|
||||||
|
}
|
||||||
|
@ -45,6 +45,9 @@ signals:
|
|||||||
// Emit when the input text changed.
|
// Emit when the input text changed.
|
||||||
void commandChanged(VVim::CommandLineType p_type, const QString &p_cmd);
|
void commandChanged(VVim::CommandLineType p_type, const QString &p_cmd);
|
||||||
|
|
||||||
|
// Emit when expecting to read a register.
|
||||||
|
void requestRegister(int p_key, int p_modifiers);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
@ -54,10 +57,17 @@ private:
|
|||||||
// Return the leader of @p_type.
|
// Return the leader of @p_type.
|
||||||
QString commandLineTypeLeader(VVim::CommandLineType p_type);
|
QString commandLineTypeLeader(VVim::CommandLineType p_type);
|
||||||
|
|
||||||
|
void setRegisterPending(bool p_pending);
|
||||||
|
|
||||||
VVim::CommandLineType m_type;
|
VVim::CommandLineType m_type;
|
||||||
|
|
||||||
// The latest command user input.
|
// The latest command user input.
|
||||||
QString m_userLastInput;
|
QString m_userLastInput;
|
||||||
|
|
||||||
|
// Whether we are expecting a register name to read.
|
||||||
|
bool m_registerPending;
|
||||||
|
|
||||||
|
QString m_originStyleSheet;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VVimIndicator : public QWidget
|
class VVimIndicator : public QWidget
|
||||||
|
Loading…
x
Reference in New Issue
Block a user