mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
vim-mode: refine command line mode
1. Support `/` and `?` to search. `N` and `Shift+N` to find next/previous occurence. 2. `Ctrl+N` and `Ctrl+Shift+N` to navigate through the command history. 3. `:nohlsearch` or `<leader><space>` to clear search highlight. 4. `#` and `*` to search current word under cursor.
This commit is contained in:
parent
c53950fe77
commit
cd2ac10509
@ -9,14 +9,6 @@ class QLineEdit;
|
|||||||
class QPushButton;
|
class QPushButton;
|
||||||
class QCheckBox;
|
class QCheckBox;
|
||||||
|
|
||||||
enum FindOption
|
|
||||||
{
|
|
||||||
CaseSensitive = 0x1U,
|
|
||||||
WholeWordOnly = 0x2U,
|
|
||||||
RegularExpression = 0x4U,
|
|
||||||
IncrementalSearch = 0x8U
|
|
||||||
};
|
|
||||||
|
|
||||||
class VFindReplaceDialog : public QWidget
|
class VFindReplaceDialog : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
## Normal Shortcuts
|
## Normal Shortcuts
|
||||||
- `Ctrl+E E`
|
- `Ctrl+E E`
|
||||||
Toggle expanding the edit area.
|
Toggle expanding the edit area.
|
||||||
- `Ctrl+N`
|
- `Ctrl+Alt+N`
|
||||||
Create a note in current directory.
|
Create a note in current directory.
|
||||||
- `Ctrl+F`
|
- `Ctrl+F`
|
||||||
Find/Replace in current note.
|
Find/Replace in current note.
|
||||||
@ -162,10 +162,11 @@ VNote supports following features of Vim:
|
|||||||
- Jump locations list (`Ctrl+O` and `Ctrl+I`);
|
- Jump locations list (`Ctrl+O` and `Ctrl+I`);
|
||||||
- Leader key (`Space`)
|
- Leader key (`Space`)
|
||||||
- Currently `<leader>y/d/p` equals to `"+y/d/p`, which will access the system's clipboard;
|
- Currently `<leader>y/d/p` equals to `"+y/d/p`, which will access the system's clipboard;
|
||||||
|
- `<leader><Space>` to clear search highlight;
|
||||||
- `zz`, `zb`, `zt`;
|
- `zz`, `zb`, `zt`;
|
||||||
- `u` and `Ctrl+R` for undo and redo;
|
- `u` and `Ctrl+R` for undo and redo;
|
||||||
- Text objects `i/a`: word, WORD, `''`, `""`, `` ` ` ``, `()`, `[]`, `<>`, and `{}`;
|
- Text objects `i/a`: word, WORD, `''`, `""`, `` ` ` ``, `()`, `[]`, `<>`, and `{}`;
|
||||||
- Command line `:w`, `:wq`, `:x`, `:q`, and `:q!`;
|
- Command line `:w`, `:wq`, `:x`, `:q`, `:q!`, and `:nohlsearch`;
|
||||||
- Jump between titles
|
- Jump between titles
|
||||||
- `[[`: jump to previous title;
|
- `[[`: jump to previous title;
|
||||||
- `]]`: jump to next title;
|
- `]]`: jump to next title;
|
||||||
@ -173,6 +174,9 @@ VNote supports following features of Vim:
|
|||||||
- `][`: jump to next title at the same level;
|
- `][`: jump to next title at the same level;
|
||||||
- `[{`: jump to previous title at a higher level;
|
- `[{`: jump to previous title at a higher level;
|
||||||
- `]}`: jump to next title at a higher level;
|
- `]}`: jump to next title at a higher level;
|
||||||
|
- `/` and `?` to search
|
||||||
|
- `n` and `N` to find next or previous occurence;
|
||||||
|
- `Ctrl+N` and `Ctrl+P` to navigate through the search history;
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
## 常规快捷键
|
## 常规快捷键
|
||||||
- `Ctrl+E E`
|
- `Ctrl+E E`
|
||||||
是否扩展编辑区域。
|
是否扩展编辑区域。
|
||||||
- `Ctrl+N`
|
- `Ctrl+Alt+N`
|
||||||
在当前文件夹下新建笔记。
|
在当前文件夹下新建笔记。
|
||||||
- `Ctrl+F`
|
- `Ctrl+F`
|
||||||
页内查找和替换。
|
页内查找和替换。
|
||||||
@ -163,10 +163,11 @@ VNote支持以下几个Vim的特性:
|
|||||||
- 跳转位置列表 (`Ctrl+O` and `Ctrl+I`);
|
- 跳转位置列表 (`Ctrl+O` and `Ctrl+I`);
|
||||||
- 前导键 (`Space`)
|
- 前导键 (`Space`)
|
||||||
- 目前 `<leader>y/d/p` 等同于 `"+y/d/p`, 从而可以访问系统剪切板;
|
- 目前 `<leader>y/d/p` 等同于 `"+y/d/p`, 从而可以访问系统剪切板;
|
||||||
|
- `<leader><Space>` 清除查找高亮;
|
||||||
- `zz`, `zb`, `zt`;
|
- `zz`, `zb`, `zt`;
|
||||||
- `u` 和 `Ctrl+R` 撤销和重做;
|
- `u` 和 `Ctrl+R` 撤销和重做;
|
||||||
- 文本对象 `i/a`:word, WORD, `''`, `""`, `` ` ` ``, `()`, `[]`, `<>`, and `{}`;
|
- 文本对象 `i/a`:word, WORD, `''`, `""`, `` ` ` ``, `()`, `[]`, `<>`, `{}`;
|
||||||
- 命令行 `:w`, `:wq`, `:x`, `:q`, and `:q!`;
|
- 命令行 `:w`, `:wq`, `:x`, `:q`, `:q!`, `:nohlsearch`;
|
||||||
- 标题跳转
|
- 标题跳转
|
||||||
- `[[`:跳转到上一个标题;
|
- `[[`:跳转到上一个标题;
|
||||||
- `]]`: 跳转到下一个标题;
|
- `]]`: 跳转到下一个标题;
|
||||||
@ -174,6 +175,9 @@ VNote支持以下几个Vim的特性:
|
|||||||
- `][`:跳转到下一个同层级的标题;
|
- `][`:跳转到下一个同层级的标题;
|
||||||
- `[{`:跳转到上一个高一层级的标题;
|
- `[{`:跳转到上一个高一层级的标题;
|
||||||
- `]}`:跳转到下一个高一层级的标题;
|
- `]}`:跳转到下一个高一层级的标题;
|
||||||
|
- `/` 和 `?` 开始查找
|
||||||
|
- `n` 和 `N` 查找下一处或上一处;
|
||||||
|
- `Ctrl+N` 和 `Ctrl+P` 浏览查找历史;
|
||||||
|
|
||||||
VNote目前暂时不支持Vim的宏和重复(`.`)特性。
|
VNote目前暂时不支持Vim的宏和重复(`.`)特性。
|
||||||
|
|
||||||
|
@ -12,11 +12,20 @@ editor
|
|||||||
# QTextEdit just choose the first available font, so specify the Chinese fonts first
|
# QTextEdit just choose the first available font, so specify the Chinese fonts first
|
||||||
# Do not use "" to quote the name
|
# Do not use "" to quote the name
|
||||||
font-family: Hiragino Sans GB, 冬青黑体, Microsoft YaHei, 微软雅黑, Microsoft YaHei UI, WenQuanYi Micro Hei, 文泉驿雅黑, Dengxian, 等线体, STXihei, 华文细黑, Liberation Sans, Droid Sans, NSimSun, 新宋体, SimSun, 宋体, Helvetica, sans-serif, Tahoma, Arial, Verdana, Geneva, Georgia, Times New Roman
|
font-family: Hiragino Sans GB, 冬青黑体, Microsoft YaHei, 微软雅黑, Microsoft YaHei UI, WenQuanYi Micro Hei, 文泉驿雅黑, Dengxian, 等线体, STXihei, 华文细黑, Liberation Sans, Droid Sans, NSimSun, 新宋体, SimSun, 宋体, Helvetica, sans-serif, Tahoma, Arial, Verdana, Geneva, Georgia, Times New Roman
|
||||||
|
font-size: 12
|
||||||
# [VNote] Style for trailing space
|
# [VNote] Style for trailing space
|
||||||
trailing-space: a8a8a8
|
trailing-space: a8a8a8
|
||||||
font-size: 12
|
# [VNote] Style for line number
|
||||||
line-number-background: bdbdbd
|
line-number-background: bdbdbd
|
||||||
line-number-foreground: 424242
|
line-number-foreground: 424242
|
||||||
|
# [VNote] style for selected word highlight
|
||||||
|
selected-word-background: dfdf00
|
||||||
|
# [VNote] style for searched word highlight
|
||||||
|
searched-word-background: 4db6ac
|
||||||
|
# [VNote] style for searched word under cursor highlight
|
||||||
|
searched-word-cursor-background: 66bb6a
|
||||||
|
# [VNote] style for incremental searched word highlight
|
||||||
|
incremental-searched-word-background: ce93d8
|
||||||
|
|
||||||
editor-selection
|
editor-selection
|
||||||
foreground: eeeeee
|
foreground: eeeeee
|
||||||
|
@ -78,7 +78,7 @@ size=4
|
|||||||
; Ctrl+E is reserved for Captain Mode.
|
; Ctrl+E is reserved for Captain Mode.
|
||||||
; Ctrl+Q is reserved for quitting VNote.
|
; Ctrl+Q is reserved for quitting VNote.
|
||||||
1\operation=NewNote
|
1\operation=NewNote
|
||||||
1\keysequence=Ctrl+N
|
1\keysequence=Ctrl+Alt+N
|
||||||
2\operation=SaveNote
|
2\operation=SaveNote
|
||||||
2\keysequence=Ctrl+S
|
2\keysequence=Ctrl+S
|
||||||
3\operation=SaveAndRead
|
3\operation=SaveAndRead
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vedit.h"
|
#include "vedit.h"
|
||||||
#include "utils/veditutils.h"
|
#include "utils/veditutils.h"
|
||||||
|
#include "vconstants.h"
|
||||||
|
|
||||||
extern VConfigManager vconfig;
|
extern VConfigManager vconfig;
|
||||||
|
|
||||||
@ -18,6 +19,8 @@ const QChar VVim::c_unnamedRegister = QChar('"');
|
|||||||
const QChar VVim::c_blackHoleRegister = QChar('_');
|
const QChar VVim::c_blackHoleRegister = QChar('_');
|
||||||
const QChar VVim::c_selectionRegister = QChar('+');
|
const QChar VVim::c_selectionRegister = QChar('+');
|
||||||
|
|
||||||
|
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;}
|
||||||
|
|
||||||
// Returns NULL QChar if invalid.
|
// Returns NULL QChar if invalid.
|
||||||
@ -86,7 +89,7 @@ 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_cmdMode(false), m_leaderKey(Key(Qt::Key_Space)), m_replayLeaderSequence(false)
|
m_leaderKey(Key(Qt::Key_Space)), m_replayLeaderSequence(false)
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_editConfig->m_enableVimMode);
|
Q_ASSERT(m_editConfig->m_enableVimMode);
|
||||||
|
|
||||||
@ -496,16 +499,6 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
qDebug() << "replaying sequence" << keyToChar(key, modifiers);
|
qDebug() << "replaying sequence" << keyToChar(key, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expectingCommandLineInput()) {
|
|
||||||
// All input will be treated as command line input.
|
|
||||||
// [Enter] to execute the command and exit command line mode.
|
|
||||||
if (processCommandLine(keyInfo)) {
|
|
||||||
goto clear_accept;
|
|
||||||
} else {
|
|
||||||
goto accept;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_pendingKeys.append(keyInfo);
|
m_pendingKeys.append(keyInfo);
|
||||||
|
|
||||||
if (expectingLeaderSequence()) {
|
if (expectingLeaderSequence()) {
|
||||||
@ -726,8 +719,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
|
|
||||||
V_ASSERT(mm != Movement::Invalid);
|
V_ASSERT(mm != Movement::Invalid);
|
||||||
tryAddMoveAction();
|
tryAddMoveAction();
|
||||||
|
addMovementToken(mm);
|
||||||
m_tokens.append(Token(mm));
|
|
||||||
processCommand(m_tokens);
|
processCommand(m_tokens);
|
||||||
resetPositionInBlock = false;
|
resetPositionInBlock = false;
|
||||||
}
|
}
|
||||||
@ -1784,15 +1776,8 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
if (m_keys.isEmpty()
|
if (m_keys.isEmpty()
|
||||||
&& m_tokens.isEmpty()
|
&& m_tokens.isEmpty()
|
||||||
&& checkMode(VimMode::Normal)) {
|
&& checkMode(VimMode::Normal)) {
|
||||||
// :, enter command line mode.
|
emit commandLineTriggered(CommandLineType::Command);
|
||||||
// For simplicity, we do not use a standalone mode for this mode.
|
|
||||||
// Just let it be in Normal mode and use another variable to
|
|
||||||
// specify this.
|
|
||||||
m_cmdMode = true;
|
|
||||||
goto accept;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -2003,6 +1988,91 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Qt::Key_Slash:
|
||||||
|
{
|
||||||
|
if (modifiers == Qt::NoModifier) {
|
||||||
|
if (m_tokens.isEmpty()
|
||||||
|
&& m_keys.isEmpty()
|
||||||
|
&& checkMode(VimMode::Normal)) {
|
||||||
|
emit commandLineTriggered(CommandLineType::SearchForward);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::Key_Question:
|
||||||
|
{
|
||||||
|
if (modifiers == Qt::ShiftModifier) {
|
||||||
|
if (m_tokens.isEmpty()
|
||||||
|
&& m_keys.isEmpty()
|
||||||
|
&& checkMode(VimMode::Normal)) {
|
||||||
|
emit commandLineTriggered(CommandLineType::SearchBackward);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::Key_N:
|
||||||
|
{
|
||||||
|
if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier) {
|
||||||
|
// n, FindNext/FindPrevious movement.
|
||||||
|
tryGetRepeatToken(m_keys, m_tokens);
|
||||||
|
|
||||||
|
if (!m_keys.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Movement mm = Movement::FindNext;
|
||||||
|
if (modifiers == Qt::ShiftModifier) {
|
||||||
|
mm = Movement::FindPrevious;
|
||||||
|
}
|
||||||
|
|
||||||
|
tryAddMoveAction();
|
||||||
|
addMovementToken(mm);
|
||||||
|
processCommand(m_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::Key_Asterisk:
|
||||||
|
{
|
||||||
|
if (modifiers == Qt::ShiftModifier) {
|
||||||
|
// *, FindNextWordUnderCursor movement.
|
||||||
|
tryGetRepeatToken(m_keys, m_tokens);
|
||||||
|
|
||||||
|
if (!m_keys.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tryAddMoveAction();
|
||||||
|
addMovementToken(Movement::FindNextWordUnderCursor);
|
||||||
|
processCommand(m_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::Key_NumberSign:
|
||||||
|
{
|
||||||
|
if (modifiers == Qt::ShiftModifier) {
|
||||||
|
// #, FindPreviousWordUnderCursor movement.
|
||||||
|
tryGetRepeatToken(m_keys, m_tokens);
|
||||||
|
|
||||||
|
if (!m_keys.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tryAddMoveAction();
|
||||||
|
addMovementToken(Movement::FindPreviousWordUnderCursor);
|
||||||
|
processCommand(m_tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2031,7 +2101,6 @@ void VVim::resetState()
|
|||||||
m_pendingKeys.clear();
|
m_pendingKeys.clear();
|
||||||
setRegister(c_unnamedRegister);
|
setRegister(c_unnamedRegister);
|
||||||
m_resetPositionInBlock = true;
|
m_resetPositionInBlock = true;
|
||||||
m_cmdMode = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VimMode VVim::getMode() const
|
VimMode VVim::getMode() const
|
||||||
@ -2808,6 +2877,99 @@ handle_target:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Movement::FindPrevious:
|
||||||
|
forward = false;
|
||||||
|
// Fall through.
|
||||||
|
case Movement::FindNext:
|
||||||
|
{
|
||||||
|
if (p_repeat == -1) {
|
||||||
|
p_repeat = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_searchHistory.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record current location.
|
||||||
|
m_locations.addLocation(p_cursor);
|
||||||
|
|
||||||
|
const SearchItem &item = m_searchHistory.lastItem();
|
||||||
|
while (--p_repeat >= 0) {
|
||||||
|
hasMoved = m_editor->findText(item.m_text, item.m_options,
|
||||||
|
forward ? item.m_forward : !item.m_forward,
|
||||||
|
&p_cursor, p_moveMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Movement::FindPreviousWordUnderCursor:
|
||||||
|
forward = false;
|
||||||
|
// Fall through.
|
||||||
|
case Movement::FindNextWordUnderCursor:
|
||||||
|
{
|
||||||
|
if (p_repeat == -1) {
|
||||||
|
p_repeat = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current word under cursor.
|
||||||
|
// Different from Vim:
|
||||||
|
// We do not recognize a word as strict as Vim.
|
||||||
|
int start, end;
|
||||||
|
findCurrentWord(p_cursor, start, end);
|
||||||
|
if (start == end) {
|
||||||
|
// Spaces, find next word.
|
||||||
|
QTextCursor cursor = p_cursor;
|
||||||
|
while (true) {
|
||||||
|
moveCursorAcrossSpaces(cursor, p_moveMode, true);
|
||||||
|
if (cursor.atEnd()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doc->characterAt(cursor.position()).isSpace()) {
|
||||||
|
findCurrentWord(cursor, start, end);
|
||||||
|
Q_ASSERT(start != end);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start == end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor cursor = p_cursor;
|
||||||
|
cursor.setPosition(start);
|
||||||
|
cursor.setPosition(end, QTextCursor::KeepAnchor);
|
||||||
|
QString text = cursor.selectedText();
|
||||||
|
if (text.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record current location.
|
||||||
|
m_locations.addLocation(p_cursor);
|
||||||
|
|
||||||
|
p_cursor.setPosition(start, p_moveMode);
|
||||||
|
|
||||||
|
// Case-insensitive, non-regularexpression.
|
||||||
|
SearchItem item;
|
||||||
|
item.m_rawStr = text;
|
||||||
|
item.m_text = text;
|
||||||
|
item.m_forward = forward;
|
||||||
|
|
||||||
|
m_searchHistory.addItem(item);
|
||||||
|
m_searchHistory.resetIndex();
|
||||||
|
while (--p_repeat >= 0) {
|
||||||
|
hasMoved = m_editor->findText(item.m_text, item.m_options,
|
||||||
|
item.m_forward,
|
||||||
|
&p_cursor, p_moveMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(hasMoved);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -4395,11 +4557,6 @@ bool VVim::expectingReplaceCharacter() const
|
|||||||
&& m_keys.first() == Key(Qt::Key_R, Qt::NoModifier);
|
&& m_keys.first() == Key(Qt::Key_R, Qt::NoModifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VVim::expectingCommandLineInput() const
|
|
||||||
{
|
|
||||||
return m_cmdMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VVim::expectingLeaderSequence() const
|
bool VVim::expectingLeaderSequence() const
|
||||||
{
|
{
|
||||||
if (m_replayLeaderSequence || m_keys.isEmpty()) {
|
if (m_replayLeaderSequence || m_keys.isEmpty()) {
|
||||||
@ -4800,134 +4957,153 @@ bool VVim::checkMode(VimMode p_mode)
|
|||||||
return m_mode == p_mode;
|
return m_mode == p_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VVim::processCommandLine(const Key &p_key)
|
bool VVim::processCommandLine(VVim::CommandLineType p_type, const QString &p_cmd)
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_cmdMode);
|
setMode(VimMode::Normal);
|
||||||
|
|
||||||
if (p_key == Key(Qt::Key_Return)
|
|
||||||
|| p_key == Key(Qt::Key_Enter, Qt::KeypadModifier)) {
|
|
||||||
// Enter, try to execute the command and exit cmd line mode.
|
|
||||||
executeCommand();
|
|
||||||
m_cmdMode = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_key.m_key == Qt::Key_Escape
|
|
||||||
|| (p_key.m_key == Qt::Key_BracketLeft && isControlModifier(p_key.m_modifiers))) {
|
|
||||||
// Go back to Normal mode.
|
|
||||||
m_keys.clear();
|
|
||||||
m_pendingKeys.clear();
|
|
||||||
m_cmdMode = false;
|
|
||||||
|
|
||||||
setMode(VimMode::Normal);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (p_key.m_key) {
|
|
||||||
case Qt::Key_Backspace:
|
|
||||||
// Delete one char backward.
|
|
||||||
if (m_keys.isEmpty()) {
|
|
||||||
// Exit command line mode.
|
|
||||||
Q_ASSERT(m_pendingKeys.size() == 1);
|
|
||||||
m_pendingKeys.pop_back();
|
|
||||||
m_cmdMode = false;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
m_keys.pop_back();
|
|
||||||
m_pendingKeys.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
bool ret = false;
|
||||||
|
switch (p_type) {
|
||||||
|
case CommandLineType::Command:
|
||||||
|
ret = executeCommand(p_cmd);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Qt::Key_U:
|
case CommandLineType::SearchForward:
|
||||||
|
// Fall through.
|
||||||
|
case CommandLineType::SearchBackward:
|
||||||
{
|
{
|
||||||
if (isControlModifier(p_key.m_modifiers)) {
|
SearchItem item = fetchSearchItem(p_type, p_cmd);
|
||||||
// Ctrl+U, delete all input keys.
|
m_editor->findText(item.m_text, item.m_options, item.m_forward);
|
||||||
while (!m_keys.isEmpty()) {
|
m_searchHistory.addItem(item);
|
||||||
m_keys.pop_back();
|
m_searchHistory.resetIndex();
|
||||||
m_pendingKeys.pop_back();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Just pend this key.
|
|
||||||
m_pendingKeys.append(p_key);
|
|
||||||
m_keys.append(p_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Just pend this key.
|
break;
|
||||||
m_pendingKeys.append(p_key);
|
|
||||||
m_keys.append(p_key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VVim::executeCommand()
|
void VVim::processCommandLineChanged(VVim::CommandLineType p_type,
|
||||||
|
const QString &p_cmd)
|
||||||
|
{
|
||||||
|
setMode(VimMode::Normal);
|
||||||
|
|
||||||
|
if (p_type == CommandLineType::SearchForward
|
||||||
|
|| p_type == CommandLineType::SearchBackward) {
|
||||||
|
// Peek text.
|
||||||
|
SearchItem item = fetchSearchItem(p_type, p_cmd);
|
||||||
|
m_editor->peekText(item.m_text, item.m_options, item.m_forward);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VVim::processCommandLineCancelled()
|
||||||
|
{
|
||||||
|
m_searchHistory.resetIndex();
|
||||||
|
m_editor->clearIncrementalSearchedWordHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
VVim::SearchItem VVim::fetchSearchItem(VVim::CommandLineType p_type,
|
||||||
|
const QString &p_cmd)
|
||||||
|
{
|
||||||
|
Q_ASSERT(p_type == CommandLineType::SearchForward
|
||||||
|
|| p_type == CommandLineType::SearchBackward);
|
||||||
|
|
||||||
|
SearchItem item;
|
||||||
|
item.m_rawStr = p_cmd;
|
||||||
|
item.m_text = p_cmd;
|
||||||
|
item.m_forward = p_type == CommandLineType::SearchForward;
|
||||||
|
|
||||||
|
if (p_cmd.indexOf("\\C") > -1) {
|
||||||
|
item.m_options |= FindOption::CaseSensitive;
|
||||||
|
item.m_text.remove("\\C");
|
||||||
|
}
|
||||||
|
|
||||||
|
item.m_options |= FindOption::RegularExpression;
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VVim::executeCommand(const QString &p_cmd)
|
||||||
{
|
{
|
||||||
bool validCommand = true;
|
bool validCommand = true;
|
||||||
QString msg;
|
QString msg;
|
||||||
|
|
||||||
if (m_keys.isEmpty()) {
|
Q_ASSERT(m_tokens.isEmpty() && m_keys.isEmpty());
|
||||||
return;
|
if (p_cmd.isEmpty()) {
|
||||||
} if (m_keys.size() == 1) {
|
return true;
|
||||||
const Key &key0 = m_keys.first();
|
}else if (p_cmd.size() == 1) {
|
||||||
if (key0 == Key(Qt::Key_W)) {
|
if (p_cmd == "w") {
|
||||||
// :w, save current file.
|
// :w, save current file.
|
||||||
emit m_editor->saveNote();
|
emit m_editor->saveNote();
|
||||||
msg = tr("Note has been saved");
|
msg = tr("Note has been saved");
|
||||||
} else if (key0 == Key(Qt::Key_Q)) {
|
} else if (p_cmd == "q") {
|
||||||
// :q, quit edit mode.
|
// :q, quit edit mode.
|
||||||
emit m_editor->discardAndRead();
|
emit m_editor->discardAndRead();
|
||||||
msg = tr("Quit");
|
msg = tr("Quit");
|
||||||
} else if (key0 == Key(Qt::Key_X)) {
|
} else if (p_cmd == "x") {
|
||||||
// :x, save if there is any change and quit edit mode.
|
// :x, save if there is any change and quit edit mode.
|
||||||
emit m_editor->saveAndRead();
|
emit m_editor->saveAndRead();
|
||||||
msg = tr("Quit with note having been saved");
|
msg = tr("Quit with note having been saved");
|
||||||
} else {
|
} else {
|
||||||
validCommand = false;
|
validCommand = false;
|
||||||
}
|
}
|
||||||
} else if (m_keys.size() == 2) {
|
} else if (p_cmd.size() == 2) {
|
||||||
const Key &key0 = m_keys.first();
|
if (p_cmd == "wq") {
|
||||||
const Key &key1 = m_keys.at(1);
|
|
||||||
if (key0 == Key(Qt::Key_W) && key1 == Key(Qt::Key_Q)) {
|
|
||||||
// :wq, save change and quit edit mode.
|
// :wq, save change and quit edit mode.
|
||||||
// We treat it same as :x.
|
// We treat it same as :x.
|
||||||
emit m_editor->saveAndRead();
|
emit m_editor->saveAndRead();
|
||||||
msg = tr("Quit with note having been saved");
|
msg = tr("Quit with note having been saved");
|
||||||
} else if (key0 == Key(Qt::Key_Q) && key1 == Key(Qt::Key_Exclam, Qt::ShiftModifier)) {
|
} else if (p_cmd == "q!") {
|
||||||
// :q!, discard change and quit edit mode.
|
// :q!, discard change and quit edit mode.
|
||||||
emit m_editor->discardAndRead();
|
emit m_editor->discardAndRead();
|
||||||
msg = tr("Quit");
|
msg = tr("Quit");
|
||||||
} else {
|
} else {
|
||||||
validCommand = false;
|
validCommand = false;
|
||||||
}
|
}
|
||||||
|
} else if (p_cmd == "nohlsearch") {
|
||||||
|
// :nohlsearch, clear highlight search.
|
||||||
|
clearSearchHighlight();
|
||||||
} else {
|
} else {
|
||||||
validCommand = false;
|
validCommand = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validCommand && !hasNonDigitPendingKeys() && m_tokens.isEmpty()) {
|
if (!validCommand) {
|
||||||
|
bool allDigits = true;
|
||||||
|
for (int i = 0; i < p_cmd.size(); ++i) {
|
||||||
|
if (!p_cmd[i].isDigit()) {
|
||||||
|
allDigits = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// All digits.
|
// All digits.
|
||||||
// Jump to a specific line.
|
// Jump to a specific line.
|
||||||
tryGetRepeatToken(m_keys, m_tokens);
|
if (allDigits) {
|
||||||
tryAddMoveAction();
|
bool ok;
|
||||||
addMovementToken(Movement::LineJump);
|
int num = p_cmd.toInt(&ok, 10);
|
||||||
processCommand(m_tokens);
|
if (num == 0) {
|
||||||
validCommand = true;
|
num = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok && num > 0) {
|
||||||
|
m_tokens.append(Token(num));
|
||||||
|
tryAddMoveAction();
|
||||||
|
addMovementToken(Movement::LineJump);
|
||||||
|
processCommand(m_tokens);
|
||||||
|
validCommand = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validCommand) {
|
if (!validCommand) {
|
||||||
QString str;
|
message(tr("Not an editor command: %1").arg(p_cmd));
|
||||||
for (auto const & key : m_keys) {
|
|
||||||
str.append(keyToChar(key.m_key, key.m_modifiers));
|
|
||||||
}
|
|
||||||
|
|
||||||
message(tr("Not an editor command: %1").arg(str));
|
|
||||||
} else {
|
} else {
|
||||||
message(msg);
|
message(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return validCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VVim::hasNonDigitPendingKeys(const QList<Key> &p_keys)
|
bool VVim::hasNonDigitPendingKeys(const QList<Key> &p_keys)
|
||||||
@ -4974,6 +5150,9 @@ bool VVim::processLeaderSequence(const Key &p_key)
|
|||||||
replaySeq.append(Key(Qt::Key_QuoteDbl, Qt::ShiftModifier));
|
replaySeq.append(Key(Qt::Key_QuoteDbl, Qt::ShiftModifier));
|
||||||
replaySeq.append(Key(Qt::Key_Plus, Qt::ShiftModifier));
|
replaySeq.append(Key(Qt::Key_Plus, Qt::ShiftModifier));
|
||||||
replaySeq.append(Key(Qt::Key_P, Qt::ShiftModifier));
|
replaySeq.append(Key(Qt::Key_P, Qt::ShiftModifier));
|
||||||
|
} else if (p_key == Key(Qt::Key_Space)) {
|
||||||
|
// <leader><space>, clear search highlight
|
||||||
|
clearSearchHighlight();
|
||||||
} else {
|
} else {
|
||||||
validSequence = false;
|
validSequence = false;
|
||||||
}
|
}
|
||||||
@ -4994,6 +5173,8 @@ bool VVim::processLeaderSequence(const Key &p_key)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_replayLeaderSequence = false;
|
m_replayLeaderSequence = false;
|
||||||
|
} else {
|
||||||
|
resetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
return validSequence;
|
return validSequence;
|
||||||
@ -5163,3 +5344,109 @@ void VVim::processTitleJump(const QList<Token> &p_tokens, bool p_forward, int p_
|
|||||||
m_locations.addLocation(cursor);
|
m_locations.addLocation(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VVim::SearchHistory::addItem(const SearchItem &p_item)
|
||||||
|
{
|
||||||
|
m_isLastItemForward = p_item.m_forward;
|
||||||
|
if (m_isLastItemForward) {
|
||||||
|
m_forwardItems.push_back(p_item);
|
||||||
|
m_forwardIdx = m_forwardItems.size();
|
||||||
|
} else {
|
||||||
|
m_backwardItems.push_back(p_item);
|
||||||
|
m_backwardIdx = m_forwardItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "search history add item" << m_isLastItemForward
|
||||||
|
<< m_forwardIdx << m_forwardItems.size()
|
||||||
|
<< m_backwardIdx << m_backwardItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const VVim::SearchItem &VVim::SearchHistory::lastItem() const
|
||||||
|
{
|
||||||
|
if (m_isLastItemForward) {
|
||||||
|
Q_ASSERT(!m_forwardItems.isEmpty());
|
||||||
|
return m_forwardItems.back();
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(!m_backwardItems.isEmpty());
|
||||||
|
return m_backwardItems.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VVim::SearchItem &VVim::SearchHistory::nextItem(bool p_forward)
|
||||||
|
{
|
||||||
|
Q_ASSERT(hasNext(p_forward));
|
||||||
|
return p_forward ? m_forwardItems.at(++m_forwardIdx)
|
||||||
|
: m_backwardItems.at(++m_backwardIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return previous item in the @p_forward stack.
|
||||||
|
const VVim::SearchItem &VVim::SearchHistory::previousItem(bool p_forward)
|
||||||
|
{
|
||||||
|
Q_ASSERT(hasPrevious(p_forward));
|
||||||
|
qDebug() << "previousItem" << p_forward << m_forwardItems.size() << m_backwardItems.size()
|
||||||
|
<< m_forwardIdx << m_backwardIdx;
|
||||||
|
return p_forward ? m_forwardItems.at(--m_forwardIdx)
|
||||||
|
: m_backwardItems.at(--m_backwardIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VVim::SearchHistory::resetIndex()
|
||||||
|
{
|
||||||
|
m_forwardIdx = m_forwardItems.size();
|
||||||
|
m_backwardIdx = m_backwardItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VVim::getNextCommandHistory(VVim::CommandLineType p_type,
|
||||||
|
const QString &p_cmd)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_cmd);
|
||||||
|
bool forward = false;
|
||||||
|
QString cmd;
|
||||||
|
switch (p_type) {
|
||||||
|
case CommandLineType::SearchForward:
|
||||||
|
forward = true;
|
||||||
|
// Fall through.
|
||||||
|
case CommandLineType::SearchBackward:
|
||||||
|
if (m_searchHistory.hasNext(forward)) {
|
||||||
|
return m_searchHistory.nextItem(forward).m_rawStr;
|
||||||
|
} else {
|
||||||
|
m_searchHistory.resetIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the previous command in history of @p_type. @p_cmd is the current input.
|
||||||
|
QString VVim::getPreviousCommandHistory(VVim::CommandLineType p_type,
|
||||||
|
const QString &p_cmd)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_cmd);
|
||||||
|
bool forward = false;
|
||||||
|
QString cmd;
|
||||||
|
switch (p_type) {
|
||||||
|
case CommandLineType::SearchForward:
|
||||||
|
forward = true;
|
||||||
|
// Fall through.
|
||||||
|
case CommandLineType::SearchBackward:
|
||||||
|
if (m_searchHistory.hasPrevious(forward)) {
|
||||||
|
return m_searchHistory.previousItem(forward).m_rawStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VVim::clearSearchHighlight()
|
||||||
|
{
|
||||||
|
m_editor->clearSearchedWordHighlight();
|
||||||
|
}
|
||||||
|
139
src/utils/vvim.h
139
src/utils/vvim.h
@ -146,6 +146,14 @@ public:
|
|||||||
QChar m_lastUsedMark;
|
QChar m_lastUsedMark;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class CommandLineType
|
||||||
|
{
|
||||||
|
Command,
|
||||||
|
SearchForward,
|
||||||
|
SearchBackward,
|
||||||
|
Invalid
|
||||||
|
};
|
||||||
|
|
||||||
// Handle key press event.
|
// Handle key press event.
|
||||||
// @p_autoIndentPos: the cursor position of last auto indent.
|
// @p_autoIndentPos: the cursor position of last auto indent.
|
||||||
// Returns true if the event is consumed and need no more handling.
|
// Returns true if the event is consumed and need no more handling.
|
||||||
@ -172,6 +180,26 @@ public:
|
|||||||
// Get m_marks.
|
// Get m_marks.
|
||||||
const VVim::Marks &getMarks() const;
|
const VVim::Marks &getMarks() const;
|
||||||
|
|
||||||
|
// Process command line of type @p_type and command @p_cmd.
|
||||||
|
// Returns true if it is a valid command.
|
||||||
|
bool processCommandLine(VVim::CommandLineType p_type, const QString &p_cmd);
|
||||||
|
|
||||||
|
// Process the command line text change.
|
||||||
|
void processCommandLineChanged(VVim::CommandLineType p_type,
|
||||||
|
const QString &p_cmd);
|
||||||
|
|
||||||
|
void processCommandLineCancelled();
|
||||||
|
|
||||||
|
// Get the next command in history of @p_type. @p_cmd is the current input.
|
||||||
|
// Return NULL QString if history is not applicable.
|
||||||
|
QString getNextCommandHistory(VVim::CommandLineType p_type,
|
||||||
|
const QString &p_cmd);
|
||||||
|
|
||||||
|
// Get the previous command in history of @p_type. @p_cmd is the current input.
|
||||||
|
// Return NULL QString if history is not applicable.
|
||||||
|
QString getPreviousCommandHistory(VVim::CommandLineType p_type,
|
||||||
|
const QString &p_cmd);
|
||||||
|
|
||||||
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);
|
||||||
@ -182,6 +210,9 @@ signals:
|
|||||||
// Emit when current status updated.
|
// Emit when current status updated.
|
||||||
void vimStatusUpdated(const VVim *p_vim);
|
void vimStatusUpdated(const VVim *p_vim);
|
||||||
|
|
||||||
|
// Emit when user pressed : to trigger command line.
|
||||||
|
void commandLineTriggered(VVim::CommandLineType p_type);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// When user use mouse to select texts in Normal mode, we should change to
|
// When user use mouse to select texts in Normal mode, we should change to
|
||||||
// Visual mode.
|
// Visual mode.
|
||||||
@ -242,6 +273,79 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Search item including the searched text and options.
|
||||||
|
struct SearchItem
|
||||||
|
{
|
||||||
|
SearchItem() : m_options(0), m_forward(true) {}
|
||||||
|
|
||||||
|
// The user raw input.
|
||||||
|
QString m_rawStr;
|
||||||
|
|
||||||
|
// The string used to search.
|
||||||
|
QString m_text;
|
||||||
|
|
||||||
|
uint m_options;
|
||||||
|
bool m_forward;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SearchHistory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SearchHistory()
|
||||||
|
: m_forwardIdx(0), m_backwardIdx(0), m_isLastItemForward(true) {}
|
||||||
|
|
||||||
|
// Add @p_item to history.
|
||||||
|
void addItem(const SearchItem &p_item);
|
||||||
|
|
||||||
|
// Whether the history is empty.
|
||||||
|
bool isEmpty() const
|
||||||
|
{
|
||||||
|
return m_forwardItems.isEmpty() && m_backwardItems.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasNext(bool p_forward) const
|
||||||
|
{
|
||||||
|
return p_forward ? m_forwardIdx < m_forwardItems.size() - 1
|
||||||
|
: m_backwardIdx < m_backwardItems.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasPrevious(bool p_forward) const
|
||||||
|
{
|
||||||
|
return p_forward ? m_forwardIdx > 0
|
||||||
|
: m_backwardIdx > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the last search item according to m_isLastItemForward.
|
||||||
|
// Make sure the history is not empty before calling this.
|
||||||
|
const SearchItem &lastItem() const;
|
||||||
|
|
||||||
|
// Return next item in the @p_forward stack.
|
||||||
|
// Make sure before by calling hasNext().
|
||||||
|
const SearchItem &nextItem(bool p_forward);
|
||||||
|
|
||||||
|
// Return previous item in the @p_forward stack.
|
||||||
|
// Make sure before by calling hasPrevious().
|
||||||
|
const SearchItem &previousItem(bool p_forward);
|
||||||
|
|
||||||
|
void resetIndex();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Maintain two stacks for the search history. Use the back as the top
|
||||||
|
// of the stack.
|
||||||
|
// The idx points to the next item to push.
|
||||||
|
// Just simply add new search item to the stack, without duplication.
|
||||||
|
QList<SearchItem> m_forwardItems;
|
||||||
|
int m_forwardIdx;
|
||||||
|
|
||||||
|
QList<SearchItem> m_backwardItems;
|
||||||
|
int m_backwardIdx;
|
||||||
|
|
||||||
|
// Whether last search item is forward or not.
|
||||||
|
bool m_isLastItemForward;
|
||||||
|
|
||||||
|
static const int c_capacity;
|
||||||
|
};
|
||||||
|
|
||||||
// Supported actions.
|
// Supported actions.
|
||||||
enum class Action
|
enum class Action
|
||||||
{
|
{
|
||||||
@ -301,6 +405,10 @@ private:
|
|||||||
MarkJump,
|
MarkJump,
|
||||||
MarkJumpLine,
|
MarkJumpLine,
|
||||||
FindPair,
|
FindPair,
|
||||||
|
FindNext,
|
||||||
|
FindPrevious,
|
||||||
|
FindNextWordUnderCursor,
|
||||||
|
FindPreviousWordUnderCursor,
|
||||||
Invalid
|
Invalid
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -549,9 +657,6 @@ private:
|
|||||||
// Check m_keys to see if we are expecting a character to replace with.
|
// Check m_keys to see if we are expecting a character to replace with.
|
||||||
bool expectingReplaceCharacter() const;
|
bool expectingReplaceCharacter() const;
|
||||||
|
|
||||||
// Check if we are in command line mode.
|
|
||||||
bool expectingCommandLineInput() const;
|
|
||||||
|
|
||||||
// Check if we are in a leader sequence.
|
// Check if we are in a leader sequence.
|
||||||
bool expectingLeaderSequence() const;
|
bool expectingLeaderSequence() const;
|
||||||
|
|
||||||
@ -645,15 +750,12 @@ private:
|
|||||||
// Check if m_mode equals to p_mode.
|
// Check if m_mode equals to p_mode.
|
||||||
bool checkMode(VimMode p_mode);
|
bool checkMode(VimMode p_mode);
|
||||||
|
|
||||||
// In command line mode, read input @p_key and process it.
|
// Execute command specified by @p_cmd.
|
||||||
// Returns true if a command has been completed, otherwise returns false.
|
// @p_cmd does not contain the leading colon.
|
||||||
bool processCommandLine(const Key &p_key);
|
// Returns true if it is a valid command.
|
||||||
|
|
||||||
// Execute command specified by m_keys.
|
|
||||||
// @p_keys does not contain the leading colon.
|
|
||||||
// Following commands are supported:
|
// Following commands are supported:
|
||||||
// :w, :wq, :q, :q!, :x
|
// w, wq, q, q!, x, <nums>
|
||||||
void executeCommand();
|
bool executeCommand(const QString &p_cmd);
|
||||||
|
|
||||||
// Check if m_keys has non-digit key.
|
// Check if m_keys has non-digit key.
|
||||||
bool hasNonDigitPendingKeys();
|
bool hasNonDigitPendingKeys();
|
||||||
@ -674,6 +776,15 @@ private:
|
|||||||
// [[, ]], [], ][, [{, ]}.
|
// [[, ]], [], ][, [{, ]}.
|
||||||
void processTitleJump(const QList<Token> &p_tokens, bool p_forward, int p_relativeLevel);
|
void processTitleJump(const QList<Token> &p_tokens, bool p_forward, int p_relativeLevel);
|
||||||
|
|
||||||
|
// Fetch the searched string and options from @p_type and @p_cmd.
|
||||||
|
// \C for case-sensitive;
|
||||||
|
// Case-insensitive by default.
|
||||||
|
// Regular-expression by default.
|
||||||
|
VVim::SearchItem fetchSearchItem(VVim::CommandLineType p_type, const QString &p_cmd);
|
||||||
|
|
||||||
|
// Clear search highlight.
|
||||||
|
void clearSearchHighlight();
|
||||||
|
|
||||||
VEdit *m_editor;
|
VEdit *m_editor;
|
||||||
const VEditConfig *m_editConfig;
|
const VEditConfig *m_editConfig;
|
||||||
VimMode m_mode;
|
VimMode m_mode;
|
||||||
@ -699,9 +810,6 @@ private:
|
|||||||
// Last f/F/t/T Token.
|
// Last f/F/t/T Token.
|
||||||
Token m_lastFindToken;
|
Token m_lastFindToken;
|
||||||
|
|
||||||
// Whether in command line mode.
|
|
||||||
bool m_cmdMode;
|
|
||||||
|
|
||||||
// The leader key, which is Key_Space by default.
|
// The leader key, which is Key_Space by default.
|
||||||
Key m_leaderKey;
|
Key m_leaderKey;
|
||||||
|
|
||||||
@ -714,6 +822,9 @@ private:
|
|||||||
|
|
||||||
Marks m_marks;
|
Marks m_marks;
|
||||||
|
|
||||||
|
// Search history.
|
||||||
|
SearchHistory m_searchHistory;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -340,7 +340,11 @@ void VConfigManager::updateMarkdownEditStyle()
|
|||||||
static const QString defaultVimInsertBg = "#CDC0B0";
|
static const QString defaultVimInsertBg = "#CDC0B0";
|
||||||
static const QString defaultVimVisualBg = "#90CAF9";
|
static const QString defaultVimVisualBg = "#90CAF9";
|
||||||
static const QString defaultVimReplaceBg = "#F8BBD0";
|
static const QString defaultVimReplaceBg = "#F8BBD0";
|
||||||
static const QString defaultTrailingSpaceBackground = "#A8A8A8";
|
static const QString defaultTrailingSpaceBg = "#A8A8A8";
|
||||||
|
static const QString defaultSelectedWordBg = "#DFDF00";
|
||||||
|
static const QString defaultSearchedWordBg = "#81C784";
|
||||||
|
static const QString defaultSearchedWordCursorBg = "#4DB6AC";
|
||||||
|
static const QString defaultIncrementalSearchedWordBg = "#CE93D8";
|
||||||
static const QString defaultLineNumberBg = "#BDBDBD";
|
static const QString defaultLineNumberBg = "#BDBDBD";
|
||||||
static const QString defaultLineNumberFg = "#424242";
|
static const QString defaultLineNumberFg = "#424242";
|
||||||
|
|
||||||
@ -398,7 +402,11 @@ void VConfigManager::updateMarkdownEditStyle()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_editorTrailingSpaceBg = defaultTrailingSpaceBackground;
|
m_editorTrailingSpaceBg = defaultTrailingSpaceBg;
|
||||||
|
m_editorSelectedWordBg = defaultSelectedWordBg;
|
||||||
|
m_editorSearchedWordBg = defaultSearchedWordBg;
|
||||||
|
m_editorSearchedWordCursorBg = defaultSearchedWordCursorBg;
|
||||||
|
m_editorIncrementalSearchedWordBg = defaultIncrementalSearchedWordBg;
|
||||||
m_editorLineNumberBg = defaultLineNumberBg;
|
m_editorLineNumberBg = defaultLineNumberBg;
|
||||||
m_editorLineNumberFg = defaultLineNumberFg;
|
m_editorLineNumberFg = defaultLineNumberFg;
|
||||||
auto editorIt = styles.find("editor");
|
auto editorIt = styles.find("editor");
|
||||||
@ -417,6 +425,26 @@ void VConfigManager::updateMarkdownEditStyle()
|
|||||||
if (it != editorIt->end()) {
|
if (it != editorIt->end()) {
|
||||||
m_editorLineNumberFg = "#" + *it;
|
m_editorLineNumberFg = "#" + *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it = editorIt->find("selected-word-background");
|
||||||
|
if (it != editorIt->end()) {
|
||||||
|
m_editorSelectedWordBg = "#" + *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = editorIt->find("searched-word-background");
|
||||||
|
if (it != editorIt->end()) {
|
||||||
|
m_editorSearchedWordBg = "#" + *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = editorIt->find("searched-word-cursor-background");
|
||||||
|
if (it != editorIt->end()) {
|
||||||
|
m_editorSearchedWordCursorBg = "#" + *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = editorIt->find("incremental-searched-word-background");
|
||||||
|
if (it != editorIt->end()) {
|
||||||
|
m_editorIncrementalSearchedWordBg = "#" + *it;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +163,11 @@ public:
|
|||||||
bool isCustomWebZoomFactor();
|
bool isCustomWebZoomFactor();
|
||||||
|
|
||||||
const QString &getEditorCurrentLineBg() const;
|
const QString &getEditorCurrentLineBg() const;
|
||||||
QString getEditorTrailingSpaceBackground() const;
|
const QString &getEditorTrailingSpaceBg() const;
|
||||||
|
const QString &getEditorSelectedWordBg() const;
|
||||||
|
const QString &getEditorSearchedWordBg() const;
|
||||||
|
const QString &getEditorSearchedWordCursorBg() const;
|
||||||
|
const QString &getEditorIncrementalSearchedWordBg() const;
|
||||||
|
|
||||||
const QString &getEditorVimNormalBg() const;
|
const QString &getEditorVimNormalBg() const;
|
||||||
const QString &getEditorVimInsertBg() const;
|
const QString &getEditorVimInsertBg() const;
|
||||||
@ -363,6 +367,18 @@ private:
|
|||||||
// Trailing space background color in editor.
|
// Trailing space background color in editor.
|
||||||
QString m_editorTrailingSpaceBg;
|
QString m_editorTrailingSpaceBg;
|
||||||
|
|
||||||
|
// Background color of selected word in editor.
|
||||||
|
QString m_editorSelectedWordBg;
|
||||||
|
|
||||||
|
// Background color of searched word in editor.
|
||||||
|
QString m_editorSearchedWordBg;
|
||||||
|
|
||||||
|
// Background color of searched word under cursor in editor.
|
||||||
|
QString m_editorSearchedWordCursorBg;
|
||||||
|
|
||||||
|
// Background color of incremental searched word in editor.
|
||||||
|
QString m_editorIncrementalSearchedWordBg;
|
||||||
|
|
||||||
// Enable colde block syntax highlight.
|
// Enable colde block syntax highlight.
|
||||||
bool m_enableCodeBlockHighlight;
|
bool m_enableCodeBlockHighlight;
|
||||||
|
|
||||||
@ -827,11 +843,31 @@ inline const QString &VConfigManager::getEditorCurrentLineBg() const
|
|||||||
return m_editorCurrentLineBg;
|
return m_editorCurrentLineBg;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QString VConfigManager::getEditorTrailingSpaceBackground() const
|
inline const QString &VConfigManager::getEditorTrailingSpaceBg() const
|
||||||
{
|
{
|
||||||
return m_editorTrailingSpaceBg;
|
return m_editorTrailingSpaceBg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline const QString &VConfigManager::getEditorSelectedWordBg() const
|
||||||
|
{
|
||||||
|
return m_editorSelectedWordBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QString &VConfigManager::getEditorSearchedWordBg() const
|
||||||
|
{
|
||||||
|
return m_editorSearchedWordBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QString &VConfigManager::getEditorSearchedWordCursorBg() const
|
||||||
|
{
|
||||||
|
return m_editorSearchedWordCursorBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QString &VConfigManager::getEditorIncrementalSearchedWordBg() const
|
||||||
|
{
|
||||||
|
return m_editorIncrementalSearchedWordBg;
|
||||||
|
}
|
||||||
|
|
||||||
inline const QString &VConfigManager::getEditorVimNormalBg() const
|
inline const QString &VConfigManager::getEditorVimNormalBg() const
|
||||||
{
|
{
|
||||||
return m_editorVimNormalBg;
|
return m_editorVimNormalBg;
|
||||||
|
@ -41,4 +41,13 @@ enum class TextDecoration { None,
|
|||||||
Underline,
|
Underline,
|
||||||
Strikethrough,
|
Strikethrough,
|
||||||
InlineCode };
|
InlineCode };
|
||||||
|
|
||||||
|
enum FindOption
|
||||||
|
{
|
||||||
|
CaseSensitive = 0x1U,
|
||||||
|
WholeWordOnly = 0x2U,
|
||||||
|
RegularExpression = 0x4U,
|
||||||
|
IncrementalSearch = 0x8U
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vtoc.h"
|
#include "vtoc.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
|
#include "utils/veditutils.h"
|
||||||
#include "veditoperations.h"
|
#include "veditoperations.h"
|
||||||
#include "dialog/vfindreplacedialog.h"
|
|
||||||
#include "vedittab.h"
|
#include "vedittab.h"
|
||||||
|
|
||||||
extern VConfigManager vconfig;
|
extern VConfigManager vconfig;
|
||||||
@ -49,11 +49,11 @@ VEdit::VEdit(VFile *p_file, QWidget *p_parent)
|
|||||||
const int extraSelectionHighlightTimer = 500;
|
const int extraSelectionHighlightTimer = 500;
|
||||||
const int labelSize = 64;
|
const int labelSize = 64;
|
||||||
|
|
||||||
m_selectedWordColor = QColor("Yellow");
|
m_selectedWordColor = QColor(vconfig.getEditorSelectedWordBg());
|
||||||
m_searchedWordColor = QColor(g_vnote->getColorFromPalette("Green4"));
|
m_searchedWordColor = QColor(vconfig.getEditorSearchedWordBg());
|
||||||
m_searchedWordCursorColor = QColor("#64B5F6");
|
m_searchedWordCursorColor = QColor(vconfig.getEditorSearchedWordCursorBg());
|
||||||
m_incrementalSearchedWordColor = QColor(g_vnote->getColorFromPalette("Purple2"));
|
m_incrementalSearchedWordColor = QColor(vconfig.getEditorIncrementalSearchedWordBg());
|
||||||
m_trailingSpaceColor = QColor(vconfig.getEditorTrailingSpaceBackground());
|
m_trailingSpaceColor = QColor(vconfig.getEditorTrailingSpaceBg());
|
||||||
|
|
||||||
QPixmap wrapPixmap(":/resources/icons/search_wrap.svg");
|
QPixmap wrapPixmap(":/resources/icons/search_wrap.svg");
|
||||||
m_wrapLabel = new QLabel(this);
|
m_wrapLabel = new QLabel(this);
|
||||||
@ -151,11 +151,11 @@ void VEdit::scrollToLine(int p_lineNumber)
|
|||||||
{
|
{
|
||||||
Q_ASSERT(p_lineNumber >= 0);
|
Q_ASSERT(p_lineNumber >= 0);
|
||||||
|
|
||||||
// Move the cursor to the end first
|
QTextBlock block = document()->findBlockByLineNumber(p_lineNumber);
|
||||||
moveCursor(QTextCursor::End);
|
if (block.isValid()) {
|
||||||
QTextCursor cursor(document()->findBlockByLineNumber(p_lineNumber));
|
VEditUtils::scrollBlockInPage(this, block.blockNumber(), 0);
|
||||||
cursor.movePosition(QTextCursor::EndOfBlock);
|
moveCursor(QTextCursor::EndOfBlock);
|
||||||
setTextCursor(cursor);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VEdit::isModified() const
|
bool VEdit::isModified() const
|
||||||
@ -178,7 +178,7 @@ void VEdit::insertImage()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VEdit::peekText(const QString &p_text, uint p_options)
|
bool VEdit::peekText(const QString &p_text, uint p_options, bool p_forward)
|
||||||
{
|
{
|
||||||
if (p_text.isEmpty()) {
|
if (p_text.isEmpty()) {
|
||||||
makeBlockVisible(document()->findBlock(textCursor().selectionStart()));
|
makeBlockVisible(document()->findBlock(textCursor().selectionStart()));
|
||||||
@ -188,8 +188,10 @@ bool VEdit::peekText(const QString &p_text, uint p_options)
|
|||||||
|
|
||||||
bool wrapped = false;
|
bool wrapped = false;
|
||||||
QTextCursor retCursor;
|
QTextCursor retCursor;
|
||||||
bool found = findTextHelper(p_text, p_options, true,
|
bool found = findTextHelper(p_text, p_options, p_forward,
|
||||||
textCursor().position() + 1, wrapped, retCursor);
|
p_forward ? textCursor().position() + 1
|
||||||
|
: textCursor().position(),
|
||||||
|
wrapped, retCursor);
|
||||||
if (found) {
|
if (found) {
|
||||||
makeBlockVisible(document()->findBlock(retCursor.selectionStart()));
|
makeBlockVisible(document()->findBlock(retCursor.selectionStart()));
|
||||||
highlightIncrementalSearchedWord(retCursor);
|
highlightIncrementalSearchedWord(retCursor);
|
||||||
@ -324,7 +326,8 @@ QList<QTextCursor> VEdit::findTextAll(const QString &p_text, uint p_options)
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VEdit::findText(const QString &p_text, uint p_options, bool p_forward)
|
bool VEdit::findText(const QString &p_text, uint p_options, bool p_forward,
|
||||||
|
QTextCursor *p_cursor, QTextCursor::MoveMode p_moveMode)
|
||||||
{
|
{
|
||||||
clearIncrementalSearchedWordHighlight();
|
clearIncrementalSearchedWordHighlight();
|
||||||
|
|
||||||
@ -333,12 +336,16 @@ bool VEdit::findText(const QString &p_text, uint p_options, bool p_forward)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTextCursor cursor = textCursor();
|
||||||
bool wrapped = false;
|
bool wrapped = false;
|
||||||
QTextCursor retCursor;
|
QTextCursor retCursor;
|
||||||
int matches = 0;
|
int matches = 0;
|
||||||
bool found = findTextHelper(p_text, p_options, p_forward,
|
int start = p_forward ? cursor.position() + 1 : cursor.position();
|
||||||
p_forward ? textCursor().position() + 1
|
if (p_cursor) {
|
||||||
: textCursor().position(),
|
start = p_forward ? p_cursor->position() + 1 : p_cursor->position();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found = findTextHelper(p_text, p_options, p_forward, start,
|
||||||
wrapped, retCursor);
|
wrapped, retCursor);
|
||||||
if (found) {
|
if (found) {
|
||||||
Q_ASSERT(!retCursor.isNull());
|
Q_ASSERT(!retCursor.isNull());
|
||||||
@ -346,9 +353,12 @@ bool VEdit::findText(const QString &p_text, uint p_options, bool p_forward)
|
|||||||
showWrapLabel();
|
showWrapLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextCursor cursor = textCursor();
|
if (p_cursor) {
|
||||||
cursor.setPosition(retCursor.selectionStart());
|
p_cursor->setPosition(retCursor.selectionStart(), p_moveMode);
|
||||||
setTextCursor(cursor);
|
} else {
|
||||||
|
cursor.setPosition(retCursor.selectionStart(), p_moveMode);
|
||||||
|
setTextCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
highlightSearchedWord(p_text, p_options);
|
highlightSearchedWord(p_text, p_options);
|
||||||
highlightSearchedWordUnderCursor(retCursor);
|
highlightSearchedWordUnderCursor(retCursor);
|
||||||
|
@ -83,9 +83,14 @@ public:
|
|||||||
|
|
||||||
// Used for incremental search.
|
// Used for incremental search.
|
||||||
// User has enter the content to search, but does not enter the "find" button yet.
|
// User has enter the content to search, but does not enter the "find" button yet.
|
||||||
bool peekText(const QString &p_text, uint p_options);
|
bool peekText(const QString &p_text, uint p_options, bool p_forward = true);
|
||||||
|
|
||||||
|
// If @p_cursor is not now, set the position of @p_cursor instead of current
|
||||||
|
// cursor.
|
||||||
|
bool findText(const QString &p_text, uint p_options, bool p_forward,
|
||||||
|
QTextCursor *p_cursor = NULL,
|
||||||
|
QTextCursor::MoveMode p_moveMode = QTextCursor::MoveAnchor);
|
||||||
|
|
||||||
bool findText(const QString &p_text, uint p_options, bool p_forward);
|
|
||||||
void replaceText(const QString &p_text, uint p_options,
|
void replaceText(const QString &p_text, uint p_options,
|
||||||
const QString &p_replaceText, bool p_findNext);
|
const QString &p_replaceText, bool p_findNext);
|
||||||
void replaceTextAll(const QString &p_text, uint p_options,
|
void replaceTextAll(const QString &p_text, uint p_options,
|
||||||
|
@ -1773,10 +1773,10 @@ void VMainWindow::updateStatusInfo(const VEditTabInfo &p_info)
|
|||||||
|
|
||||||
void VMainWindow::handleVimStatusUpdated(const VVim *p_vim)
|
void VMainWindow::handleVimStatusUpdated(const VVim *p_vim)
|
||||||
{
|
{
|
||||||
|
m_vimIndicator->update(p_vim, m_curTab);
|
||||||
if (!p_vim || !m_curTab || !m_curTab->isEditMode()) {
|
if (!p_vim || !m_curTab || !m_curTab->isEditMode()) {
|
||||||
m_vimIndicator->hide();
|
m_vimIndicator->hide();
|
||||||
} else {
|
} else {
|
||||||
m_vimIndicator->update(p_vim);
|
|
||||||
m_vimIndicator->show();
|
m_vimIndicator->show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -354,8 +354,15 @@ void VMdEdit::generateEditOutline()
|
|||||||
void VMdEdit::scrollToHeader(const VAnchor &p_anchor)
|
void VMdEdit::scrollToHeader(const VAnchor &p_anchor)
|
||||||
{
|
{
|
||||||
if (p_anchor.lineNumber == -1
|
if (p_anchor.lineNumber == -1
|
||||||
|| p_anchor.m_outlineIndex < 0
|
|| p_anchor.m_outlineIndex < 0) {
|
||||||
|| p_anchor.m_outlineIndex >= m_headers.size()) {
|
// Move to the start of document if m_headers is not empty.
|
||||||
|
// Otherwise, there is no outline, so just let it be.
|
||||||
|
if (!m_headers.isEmpty()) {
|
||||||
|
moveCursor(QTextCursor::Start);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else if (p_anchor.m_outlineIndex >= m_headers.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vbuttonwithwidget.h"
|
#include "vbuttonwithwidget.h"
|
||||||
|
#include "vedittab.h"
|
||||||
|
|
||||||
extern VConfigManager vconfig;
|
extern VConfigManager vconfig;
|
||||||
|
|
||||||
@ -21,6 +22,66 @@ VVimIndicator::VVimIndicator(QWidget *p_parent)
|
|||||||
|
|
||||||
void VVimIndicator::setupUI()
|
void VVimIndicator::setupUI()
|
||||||
{
|
{
|
||||||
|
m_cmdLineEdit = new VVimCmdLineEdit(this);
|
||||||
|
connect(m_cmdLineEdit, &VVimCmdLineEdit::commandCancelled,
|
||||||
|
this, [this](){
|
||||||
|
if (m_vim) {
|
||||||
|
m_vim->processCommandLineCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_editTab) {
|
||||||
|
m_editTab->focusTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTICE: m_cmdLineEdit should not hide itself before setting
|
||||||
|
// focus to edit tab.
|
||||||
|
m_cmdLineEdit->hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_cmdLineEdit, &VVimCmdLineEdit::commandFinished,
|
||||||
|
this, [this](VVim::CommandLineType p_type, const QString &p_cmd){
|
||||||
|
if (m_vim) {
|
||||||
|
m_vim->processCommandLine(p_type, p_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_editTab) {
|
||||||
|
m_editTab->focusTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cmdLineEdit->hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_cmdLineEdit, &VVimCmdLineEdit::commandChanged,
|
||||||
|
this, [this](VVim::CommandLineType p_type, const QString &p_cmd){
|
||||||
|
if (m_vim) {
|
||||||
|
m_vim->processCommandLineChanged(p_type, p_cmd);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_cmdLineEdit, &VVimCmdLineEdit::requestNextCommand,
|
||||||
|
this, [this](VVim::CommandLineType p_type, const QString &p_cmd){
|
||||||
|
if (m_vim) {
|
||||||
|
QString cmd = m_vim->getNextCommandHistory(p_type, p_cmd);
|
||||||
|
if (!cmd.isNull()) {
|
||||||
|
m_cmdLineEdit->setCommand(cmd);
|
||||||
|
} else {
|
||||||
|
m_cmdLineEdit->restoreUserLastInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_cmdLineEdit, &VVimCmdLineEdit::requestPreviousCommand,
|
||||||
|
this, [this](VVim::CommandLineType p_type, const QString &p_cmd){
|
||||||
|
if (m_vim) {
|
||||||
|
QString cmd = m_vim->getPreviousCommandHistory(p_type, p_cmd);
|
||||||
|
if (!cmd.isNull()) {
|
||||||
|
m_cmdLineEdit->setCommand(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_cmdLineEdit->hide();
|
||||||
|
|
||||||
m_modeLabel = new QLabel(this);
|
m_modeLabel = new QLabel(this);
|
||||||
|
|
||||||
m_regBtn = new VButtonWithWidget(QIcon(":/resources/icons/arrow_dropup.svg"),
|
m_regBtn = new VButtonWithWidget(QIcon(":/resources/icons/arrow_dropup.svg"),
|
||||||
@ -60,6 +121,8 @@ void VVimIndicator::setupUI()
|
|||||||
m_keyLabel->setMinimumWidth(metric.width('A') * 5);
|
m_keyLabel->setMinimumWidth(metric.width('A') * 5);
|
||||||
|
|
||||||
QHBoxLayout *mainLayout = new QHBoxLayout(this);
|
QHBoxLayout *mainLayout = new QHBoxLayout(this);
|
||||||
|
mainLayout->addStretch();
|
||||||
|
mainLayout->addWidget(m_cmdLineEdit);
|
||||||
mainLayout->addWidget(m_modeLabel);
|
mainLayout->addWidget(m_modeLabel);
|
||||||
mainLayout->addWidget(m_regBtn);
|
mainLayout->addWidget(m_regBtn);
|
||||||
mainLayout->addWidget(m_markBtn);
|
mainLayout->addWidget(m_markBtn);
|
||||||
@ -154,9 +217,29 @@ static void fillTreeItemsWithRegisters(QTreeWidget *p_tree,
|
|||||||
p_tree->resizeColumnToContents(1);
|
p_tree->resizeColumnToContents(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VVimIndicator::update(const VVim *p_vim)
|
void VVimIndicator::update(const VVim *p_vim, const VEditTab *p_editTab)
|
||||||
{
|
{
|
||||||
m_vim = p_vim;
|
m_editTab = const_cast<VEditTab *>(p_editTab);
|
||||||
|
if (m_vim != p_vim) {
|
||||||
|
// Disconnect from previous Vim.
|
||||||
|
if (m_vim) {
|
||||||
|
disconnect(m_vim.data(), 0, this, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_vim = const_cast<VVim *>(p_vim);
|
||||||
|
if (m_vim) {
|
||||||
|
// Connect signal.
|
||||||
|
connect(m_vim.data(), &VVim::commandLineTriggered,
|
||||||
|
this, &VVimIndicator::triggerCommandLine);
|
||||||
|
|
||||||
|
m_cmdLineEdit->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_vim) {
|
||||||
|
m_cmdLineEdit->hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
VimMode mode = VimMode::Normal;
|
VimMode mode = VimMode::Normal;
|
||||||
QChar curRegName(' ');
|
QChar curRegName(' ');
|
||||||
@ -230,3 +313,169 @@ void VVimIndicator::updateMarksTree(QWidget *p_widget)
|
|||||||
const QMap<QChar, VVim::Mark> &marks = m_vim->getMarks().getMarks();
|
const QMap<QChar, VVim::Mark> &marks = m_vim->getMarks().getMarks();
|
||||||
fillTreeItemsWithMarks(markTree, marks);
|
fillTreeItemsWithMarks(markTree, marks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VVimIndicator::triggerCommandLine(VVim::CommandLineType p_type)
|
||||||
|
{
|
||||||
|
m_cmdLineEdit->reset(p_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
VVimCmdLineEdit::VVimCmdLineEdit(QWidget *p_parent)
|
||||||
|
: QLineEdit(p_parent), m_type(VVim::CommandLineType::Invalid)
|
||||||
|
{
|
||||||
|
// When user delete all the text, cancel command input.
|
||||||
|
connect(this, &VVimCmdLineEdit::textChanged,
|
||||||
|
this, [this](const QString &p_text){
|
||||||
|
if (p_text.isEmpty()) {
|
||||||
|
emit commandCancelled();
|
||||||
|
} else {
|
||||||
|
emit commandChanged(m_type, p_text.right(p_text.size() - 1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &VVimCmdLineEdit::textEdited,
|
||||||
|
this, [this](const QString &p_text){
|
||||||
|
if (p_text.size() < 2) {
|
||||||
|
m_userLastInput.clear();
|
||||||
|
} else {
|
||||||
|
m_userLastInput = p_text.right(p_text.size() - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VVimCmdLineEdit::getCommand() const
|
||||||
|
{
|
||||||
|
QString tx = text();
|
||||||
|
if (tx.size() < 2) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return tx.right(tx.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VVimCmdLineEdit::commandLineTypeLeader(VVim::CommandLineType p_type)
|
||||||
|
{
|
||||||
|
QString leader;
|
||||||
|
switch (p_type) {
|
||||||
|
case VVim::CommandLineType::Command:
|
||||||
|
leader = ":";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VVim::CommandLineType::SearchForward:
|
||||||
|
leader = "/";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VVim::CommandLineType::SearchBackward:
|
||||||
|
leader = "?";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VVim::CommandLineType::Invalid:
|
||||||
|
leader.clear();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return leader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VVimCmdLineEdit::reset(VVim::CommandLineType p_type)
|
||||||
|
{
|
||||||
|
m_type = p_type;
|
||||||
|
m_userLastInput.clear();
|
||||||
|
setCommand("");
|
||||||
|
show();
|
||||||
|
setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
void VVimCmdLineEdit::keyPressEvent(QKeyEvent *p_event)
|
||||||
|
{
|
||||||
|
int key = p_event->key();
|
||||||
|
int modifiers = p_event->modifiers();
|
||||||
|
|
||||||
|
if ((key == Qt::Key_Return && modifiers == Qt::NoModifier)
|
||||||
|
|| (key == Qt::Key_Enter && modifiers == Qt::KeypadModifier)) {
|
||||||
|
// Enter, complete the command line input.
|
||||||
|
p_event->accept();
|
||||||
|
emit commandFinished(m_type, getCommand());
|
||||||
|
return;
|
||||||
|
} else if (key == Qt::Key_Escape
|
||||||
|
|| (key == Qt::Key_BracketLeft && isControlModifier(modifiers))) {
|
||||||
|
// Exit command line input.
|
||||||
|
setText(commandLineTypeLeader(m_type));
|
||||||
|
p_event->accept();
|
||||||
|
emit commandCancelled();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case Qt::Key_U:
|
||||||
|
if (isControlModifier(modifiers)) {
|
||||||
|
// Ctrl+U, delete all user input.
|
||||||
|
setText(commandLineTypeLeader(m_type));
|
||||||
|
p_event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Qt::Key_N:
|
||||||
|
if (!isControlModifier(modifiers)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Ctrl+N, request next command.
|
||||||
|
// Fall through.
|
||||||
|
case Qt::Key_Down:
|
||||||
|
{
|
||||||
|
emit requestNextCommand(m_type, getCommand());
|
||||||
|
p_event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::Key_P:
|
||||||
|
if (!isControlModifier(modifiers)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Ctrl+P, request previous command.
|
||||||
|
// Fall through.
|
||||||
|
case Qt::Key_Up:
|
||||||
|
{
|
||||||
|
emit requestPreviousCommand(m_type, getCommand());
|
||||||
|
p_event->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLineEdit::keyPressEvent(p_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VVimCmdLineEdit::focusOutEvent(QFocusEvent *p_event)
|
||||||
|
{
|
||||||
|
if (p_event->reason() != Qt::ActiveWindowFocusReason) {
|
||||||
|
emit commandCancelled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VVimCmdLineEdit::setCommand(const QString &p_cmd)
|
||||||
|
{
|
||||||
|
setText(commandLineTypeLeader(m_type) + p_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VVimCmdLineEdit::restoreUserLastInput()
|
||||||
|
{
|
||||||
|
setCommand(m_userLastInput);
|
||||||
|
}
|
||||||
|
@ -2,10 +2,63 @@
|
|||||||
#define VVIMINDICATOR_H
|
#define VVIMINDICATOR_H
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPointer>
|
||||||
#include "utils/vvim.h"
|
#include "utils/vvim.h"
|
||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class VButtonWithWidget;
|
class VButtonWithWidget;
|
||||||
|
class QKeyEvent;
|
||||||
|
class QFocusEvent;
|
||||||
|
class VEditTab;
|
||||||
|
|
||||||
|
class VVimCmdLineEdit : public QLineEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VVimCmdLineEdit(QWidget *p_parent = 0);
|
||||||
|
|
||||||
|
void reset(VVim::CommandLineType p_type);
|
||||||
|
|
||||||
|
// Set the command to @p_cmd with leader unchanged.
|
||||||
|
void setCommand(const QString &p_cmd);
|
||||||
|
|
||||||
|
// Get the command.
|
||||||
|
QString getCommand() const;
|
||||||
|
|
||||||
|
void restoreUserLastInput();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// User has finished the input and the command is ready to execute.
|
||||||
|
void commandFinished(VVim::CommandLineType p_type, const QString &p_cmd);
|
||||||
|
|
||||||
|
// User cancelled the input.
|
||||||
|
void commandCancelled();
|
||||||
|
|
||||||
|
// User request the next command in the history.
|
||||||
|
void requestNextCommand(VVim::CommandLineType p_type, const QString &p_cmd);
|
||||||
|
|
||||||
|
// User request the previous command in the history.
|
||||||
|
void requestPreviousCommand(VVim::CommandLineType p_type, const QString &p_cmd);
|
||||||
|
|
||||||
|
// Emit when the input text changed.
|
||||||
|
void commandChanged(VVim::CommandLineType p_type, const QString &p_cmd);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void focusOutEvent(QFocusEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Return the leader of @p_type.
|
||||||
|
QString commandLineTypeLeader(VVim::CommandLineType p_type);
|
||||||
|
|
||||||
|
VVim::CommandLineType m_type;
|
||||||
|
|
||||||
|
// The latest command user input.
|
||||||
|
QString m_userLastInput;
|
||||||
|
};
|
||||||
|
|
||||||
class VVimIndicator : public QWidget
|
class VVimIndicator : public QWidget
|
||||||
{
|
{
|
||||||
@ -15,18 +68,24 @@ public:
|
|||||||
explicit VVimIndicator(QWidget *p_parent = 0);
|
explicit VVimIndicator(QWidget *p_parent = 0);
|
||||||
|
|
||||||
// Update indicator according to @p_vim.
|
// Update indicator according to @p_vim.
|
||||||
void update(const VVim *p_vim);
|
void update(const VVim *p_vim, const VEditTab *p_editTab);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateRegistersTree(QWidget *p_widget);
|
void updateRegistersTree(QWidget *p_widget);
|
||||||
|
|
||||||
void updateMarksTree(QWidget *p_widget);
|
void updateMarksTree(QWidget *p_widget);
|
||||||
|
|
||||||
|
// Vim request to trigger command line.
|
||||||
|
void triggerCommandLine(VVim::CommandLineType p_type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupUI();
|
void setupUI();
|
||||||
|
|
||||||
QString modeToString(VimMode p_mode) const;
|
QString modeToString(VimMode p_mode) const;
|
||||||
|
|
||||||
|
// Command line input.
|
||||||
|
VVimCmdLineEdit *m_cmdLineEdit;
|
||||||
|
|
||||||
// Indicate the mode.
|
// Indicate the mode.
|
||||||
QLabel *m_modeLabel;
|
QLabel *m_modeLabel;
|
||||||
|
|
||||||
@ -39,7 +98,8 @@ private:
|
|||||||
// Indicate the pending keys.
|
// Indicate the pending keys.
|
||||||
QLabel *m_keyLabel;
|
QLabel *m_keyLabel;
|
||||||
|
|
||||||
const VVim *m_vim;
|
QPointer<VVim> m_vim;
|
||||||
|
QPointer<VEditTab> m_editTab;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VVIMINDICATOR_H
|
#endif // VVIMINDICATOR_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user