vim-mode: support ~ to reverse case

This commit is contained in:
Le Tan 2017-07-03 21:19:19 +08:00
parent a87048cac2
commit c4683dd232
4 changed files with 109 additions and 6 deletions

View File

@ -123,7 +123,7 @@ VNote supports a simple but useful Vim mode, including **Normal**, **Insert**, *
VNote supports following features of Vim:
- `r`, `s`, `i`, `I`, `a`, `A`, `o`, and `O`;
- Actions `d`, `c`, `y`, `p`, `<`, `>`, `gu`, and `gU`;
- Actions `d`, `c`, `y`, `p`, `<`, `>`, `gu`, `gU`, and `~`;
- Movements `h/j/k/l`, `gj/gk`, `Ctrl+U`, `Ctrl+D`, `gg`, `G`, `0`, `^`, and `$`;
- Marks `a-z`;
- Registers `"`, `_`, `+`, `a-z`(`A-Z`);

View File

@ -123,7 +123,8 @@ VNote支持一个简单但有用的Vim模式包括 **正常** **插入**
VNote支持以下几个Vim的特性
- `r`, `s`, `i`, `I`, `a`, `A`, `o`, `O`;
- 操作 `d`, `c`, `y`, `p`, `<`, `>`, `gu`, `gU`
- 操作 `d`, `c`, `y`, `p`, `<`, `>`, `gu`, `gU`, `~`
- 移动 `h/j/k/l`, `gj/gk`, `Ctrl+U`, `Ctrl+D`, `gg`, `G`, `0`, `^`, `$`
- 标记 `a-z`
- 寄存器 `"`, `_`, `+`, `a-z`(`A-Z`)
- 跳转位置列表 (`Ctrl+O` and `Ctrl+I`)

View File

@ -380,6 +380,46 @@ static bool replaceSelectedTextWithCharacter(QTextCursor &p_cursor, QChar p_char
return true;
}
// Reverse the case of selected text.
// Returns true if the reverse has taken place.
// Need to setTextCursor() after calling this.
static bool reverseSelectedTextCase(QTextCursor &p_cursor)
{
if (!p_cursor.hasSelection()) {
return false;
}
QTextDocument *doc = p_cursor.document();
int start = p_cursor.selectionStart();
int end = p_cursor.selectionEnd();
p_cursor.setPosition(start, QTextCursor::MoveAnchor);
while (p_cursor.position() < end) {
if (p_cursor.atBlockEnd()) {
p_cursor.movePosition(QTextCursor::NextCharacter);
} else {
QChar ch = doc->characterAt(p_cursor.position());
bool changed = false;
if (ch.isLower()) {
ch = ch.toUpper();
changed = true;
} else if (ch.isUpper()) {
ch = ch.toLower();
changed = true;
}
if (changed) {
p_cursor.deleteChar();
// insertText() will move the cursor right after the inserted text.
p_cursor.insertText(ch);
} else {
p_cursor.movePosition(QTextCursor::NextCharacter);
}
}
}
return true;
}
bool VVim::handleKeyPressEvent(QKeyEvent *p_event, int *p_autoIndentPos)
{
bool ret = handleKeyPressEvent(p_event->key(), p_event->modifiers(), p_autoIndentPos);
@ -1930,6 +1970,24 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
break;
}
case Qt::Key_AsciiTilde:
{
if (modifiers == Qt::ShiftModifier) {
tryGetRepeatToken(m_keys, m_tokens);
if (hasActionToken() || !m_keys.isEmpty()) {
break;
}
// Reverse the case.
addActionToken(Action::ReverseCase);
processCommand(m_tokens);
break;
}
break;
}
default:
break;
}
@ -1995,10 +2053,6 @@ void VVim::processCommand(QList<Token> &p_tokens)
V_ASSERT(p_tokens.at(0).isAction());
for (int i = 0; i < p_tokens.size(); ++i) {
qDebug() << "token" << i << p_tokens[i].toString();
}
Token act = p_tokens.takeFirst();
switch (act.m_action) {
case Action::Move:
@ -2073,6 +2127,10 @@ void VVim::processCommand(QList<Token> &p_tokens)
processReplaceAction(p_tokens);
break;
case Action::ReverseCase:
processReverseCaseAction(p_tokens);
break;
default:
p_tokens.clear();
break;
@ -4119,6 +4177,46 @@ void VVim::processReplaceAction(QList<Token> &p_tokens)
}
}
void VVim::processReverseCaseAction(QList<Token> &p_tokens)
{
int repeat = 1;
if (!p_tokens.isEmpty()) {
Token to = p_tokens.takeFirst();
Q_ASSERT(to.isRepeat() && p_tokens.isEmpty());
repeat = to.m_repeat;
}
if (!(checkMode(VimMode::Normal)
|| checkMode(VimMode::Visual)
|| checkMode(VimMode::VisualLine))) {
return;
}
// Reverse the next repeat characters' case until the end of line.
// If repeat is greater than the number of left characters in current line,
// just change the actual number of left characters.
// In visual mode, repeat is ignored and reverse the selected text.
QTextCursor cursor = m_editor->textCursor();
cursor.beginEditBlock();
if (checkMode(VimMode::Normal)) {
// Select the characters to be replaced.
cursor.clearSelection();
int pib = cursor.positionInBlock();
int nrChar = cursor.block().length() - 1 - pib;
cursor.movePosition(QTextCursor::Right,
QTextCursor::KeepAnchor,
repeat > nrChar ? nrChar : repeat);
}
bool changed = reverseSelectedTextCase(cursor);
cursor.endEditBlock();
if (changed) {
m_editor->setTextCursor(cursor);
setMode(VimMode::Normal);
}
}
bool VVim::clearSelection()
{
QTextCursor cursor = m_editor->textCursor();

View File

@ -255,6 +255,7 @@ private:
UnIndent,
ToUpper,
ToLower,
ReverseCase,
Undo,
Redo,
RedrawAtTop,
@ -519,6 +520,9 @@ private:
// Action::Replace.
void processReplaceAction(QList<Token> &p_tokens);
// Action::ReverseCase.
void processReverseCaseAction(QList<Token> &p_tokens);
// Clear selection if there is any.
// Returns true if there is selection.
bool clearSelection();