vim-mode: support = to auto indent selected blocks as previous block

This commit is contained in:
Le Tan 2017-11-24 19:57:37 +08:00
parent fcbd591b69
commit d3ff787153
5 changed files with 178 additions and 59 deletions

View File

@ -88,14 +88,21 @@ bool VEditUtils::insertListMarkAsPreviousBlock(QTextCursor &p_cursor)
bool VEditUtils::indentBlockAsBlock(QTextCursor &p_cursor, bool p_next) bool VEditUtils::indentBlockAsBlock(QTextCursor &p_cursor, bool p_next)
{ {
bool changed = false;
QTextBlock block = p_cursor.block(); QTextBlock block = p_cursor.block();
QTextBlock refBlock = p_next ? block.next() : block.previous(); QTextBlock refBlock = p_next ? block.next() : block.previous();
if (!refBlock.isValid()) { return indentBlockAsBlock(p_cursor, refBlock);
}
bool VEditUtils::indentBlockAsBlock(QTextCursor &p_cursor, const QTextBlock &p_refBlock)
{
if (!p_refBlock.isValid()) {
return false; return false;
} }
QString leadingSpaces = fetchIndentSpaces(refBlock); Q_ASSERT(!p_cursor.hasSelection());
bool changed = false;
QString leadingSpaces = fetchIndentSpaces(p_refBlock);
moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::MoveAnchor); moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::MoveAnchor);
if (!p_cursor.atBlockStart()) { if (!p_cursor.atBlockStart()) {
@ -112,6 +119,42 @@ bool VEditUtils::indentBlockAsBlock(QTextCursor &p_cursor, bool p_next)
return changed; return changed;
} }
// Use another QTextCursor to remain the selection.
void VEditUtils::indentSelectedBlocksAsBlock(const QTextCursor &p_cursor, bool p_next)
{
int nrBlocks = 1;
int start = p_cursor.selectionStart();
int end = p_cursor.selectionEnd();
QTextDocument *doc = p_cursor.document();
QTextBlock sBlock = doc->findBlock(start);
QTextBlock refBlock;
if (!p_next) {
refBlock = sBlock.previous();
}
if (start != end) {
QTextBlock eBlock = doc->findBlock(end);
nrBlocks = eBlock.blockNumber() - sBlock.blockNumber() + 1;
if (p_next) {
refBlock = eBlock.next();
}
} else {
refBlock = sBlock.next();
}
QTextCursor bCursor(sBlock);
bCursor.beginEditBlock();
for (int i = 0; i < nrBlocks; ++i) {
indentBlockAsBlock(bCursor, refBlock);
bCursor.movePosition(QTextCursor::NextBlock);
}
bCursor.endEditBlock();
}
bool VEditUtils::hasSameIndent(const QTextBlock &p_blocka, const QTextBlock &p_blockb) bool VEditUtils::hasSameIndent(const QTextBlock &p_blocka, const QTextBlock &p_blockb)
{ {
int nonSpaceIdxa = 0; int nonSpaceIdxa = 0;
@ -170,8 +213,7 @@ QString VEditUtils::selectedText(const QTextCursor &p_cursor)
} }
// Use another QTextCursor to remain the selection. // Use another QTextCursor to remain the selection.
void VEditUtils::indentSelectedBlocks(const QTextDocument *p_doc, void VEditUtils::indentSelectedBlocks(const QTextCursor &p_cursor,
const QTextCursor &p_cursor,
const QString &p_indentationText, const QString &p_indentationText,
bool p_isIndent) bool p_isIndent)
{ {
@ -179,9 +221,10 @@ void VEditUtils::indentSelectedBlocks(const QTextDocument *p_doc,
int start = p_cursor.selectionStart(); int start = p_cursor.selectionStart();
int end = p_cursor.selectionEnd(); int end = p_cursor.selectionEnd();
QTextBlock sBlock = p_doc->findBlock(start); QTextDocument *doc = p_cursor.document();
QTextBlock sBlock = doc->findBlock(start);
if (start != end) { if (start != end) {
QTextBlock eBlock = p_doc->findBlock(end); QTextBlock eBlock = doc->findBlock(end);
nrBlocks = eBlock.blockNumber() - sBlock.blockNumber() + 1; nrBlocks = eBlock.blockNumber() - sBlock.blockNumber() + 1;
} }
@ -196,6 +239,7 @@ void VEditUtils::indentSelectedBlocks(const QTextDocument *p_doc,
bCursor.movePosition(QTextCursor::NextBlock); bCursor.movePosition(QTextCursor::NextBlock);
} }
bCursor.endEditBlock(); bCursor.endEditBlock();
} }

View File

@ -31,6 +31,9 @@ public:
// @p_next: indent as next block or previous block. // @p_next: indent as next block or previous block.
static bool indentBlockAsBlock(QTextCursor &p_cursor, bool p_next); static bool indentBlockAsBlock(QTextCursor &p_cursor, bool p_next);
// Indent current block as @p_refBlock.
static bool indentBlockAsBlock(QTextCursor &p_cursor, const QTextBlock &p_refBlock);
// Returns true if two blocks has the same indent. // Returns true if two blocks has the same indent.
static bool hasSameIndent(const QTextBlock &p_blocka, const QTextBlock &p_blockb); static bool hasSameIndent(const QTextBlock &p_blocka, const QTextBlock &p_blockb);
@ -56,12 +59,18 @@ public:
static QString selectedText(const QTextCursor &p_cursor); static QString selectedText(const QTextCursor &p_cursor);
// Indent selected blocks. If no selection, indent current block. // Indent selected blocks. If no selection, indent current block.
// Cursor position and selection is not changed.
// @p_isIndent: whether it is indentation or unindentation. // @p_isIndent: whether it is indentation or unindentation.
static void indentSelectedBlocks(const QTextDocument *p_doc, static void indentSelectedBlocks(const QTextCursor &p_cursor,
const QTextCursor &p_cursor,
const QString &p_indentationText, const QString &p_indentationText,
bool p_isIndent); bool p_isIndent);
// Indent seleced block as next/previous block.
// Cursor position and selection is not changed.
// Return true if some changes have been made.
// @p_next: indent as next block or previous block.
static void indentSelectedBlocksAsBlock(const QTextCursor &p_cursor, bool p_next);
// Indent current block. // Indent current block.
// @p_skipEmpty: skip empty block. // @p_skipEmpty: skip empty block.
static void indentBlock(QTextCursor &p_cursor, static void indentBlock(QTextCursor &p_cursor,

View File

@ -1688,18 +1688,47 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
} }
} else { } else {
// The first >/<, an Action. // The first >/<, an Action.
if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) { addActionToken(unindent ? Action::UnIndent : Action::Indent);
QTextCursor cursor = m_editor->textCursorW();
VEditUtils::indentSelectedBlocks(m_editor->documentW(), if (checkMode(VimMode::Visual)
cursor, || checkMode(VimMode::VisualLine)) {
m_editConfig->m_tabSpaces, // Movement will be ignored.
!unindent); addMovementToken(Movement::Left);
// Different from Vim: processCommand(m_tokens);
// Do not exit Visual mode after indentation/unindentation. break;
}
goto accept;
}
}
break;
}
case Qt::Key_Equal:
{
if (modifiers == Qt::NoModifier) {
// =, AutoIndent.
tryGetRepeatToken(m_keys, m_tokens);
if (hasActionToken()) {
// ==.
if (checkActionToken(Action::AutoIndent)) {
addRangeToken(Range::Line);
processCommand(m_tokens);
break;
}
} else {
// The first =, an Action.
addActionToken(Action::AutoIndent);
if (checkMode(VimMode::Visual)
|| checkMode(VimMode::VisualLine)) {
// Movement will be ignored.
addMovementToken(Movement::Left);
processCommand(m_tokens);
break; break;
} }
addActionToken(unindent ? Action::UnIndent : Action::Indent);
goto accept; goto accept;
} }
} }
@ -2251,11 +2280,15 @@ void VVim::processCommand(QList<Token> &p_tokens)
break; break;
case Action::Indent: case Action::Indent:
processIndentAction(p_tokens, true); processIndentAction(p_tokens, IndentType::Indent);
break; break;
case Action::UnIndent: case Action::UnIndent:
processIndentAction(p_tokens, false); processIndentAction(p_tokens, IndentType::UnIndent);
break;
case Action::AutoIndent:
processIndentAction(p_tokens, IndentType::AutoIndent);
break; break;
case Action::ToLower: case Action::ToLower:
@ -4265,7 +4298,7 @@ exit:
setMode(VimMode::Insert); setMode(VimMode::Insert);
} }
void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent) void VVim::processIndentAction(QList<Token> &p_tokens, IndentType p_type)
{ {
Token to = p_tokens.takeFirst(); Token to = p_tokens.takeFirst();
int repeat = -1; int repeat = -1;
@ -4280,10 +4313,27 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
} }
QTextCursor cursor = m_editor->textCursorW(); QTextCursor cursor = m_editor->textCursorW();
QTextDocument *doc = m_editor->documentW();
QString op;
switch (p_type) {
case IndentType::Indent:
op = ">";
break;
case IndentType::UnIndent:
op = "<";
break;
case IndentType::AutoIndent:
op = "=";
break;
default:
Q_ASSERT(false);
}
if (to.isRange()) { if (to.isRange()) {
bool changed = selectRange(cursor, doc, to.m_range, repeat); bool changed = selectRange(cursor, m_editor->documentW(), to.m_range, repeat);
if (changed) { if (changed) {
switch (to.m_range) { switch (to.m_range) {
case Range::Line: case Range::Line:
@ -4293,19 +4343,18 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
repeat = 1; repeat = 1;
} }
VEditUtils::indentSelectedBlocks(doc, if (p_type == IndentType::AutoIndent) {
cursor, VEditUtils::indentSelectedBlocksAsBlock(cursor, false);
m_editConfig->m_tabSpaces,
p_isIndent);
if (p_isIndent) {
message(tr("%1 %2 >ed 1 time").arg(repeat).arg(repeat > 1 ? tr("lines")
: tr("line")));
} else { } else {
message(tr("%1 %2 <ed 1 time").arg(repeat).arg(repeat > 1 ? tr("lines") VEditUtils::indentSelectedBlocks(cursor,
: tr("line"))); m_editConfig->m_tabSpaces,
p_type == IndentType::Indent);
} }
message(tr("%1 %2 %3ed 1 time").arg(repeat)
.arg(repeat > 1 ? tr("lines")
: tr("line"))
.arg(op));
break; break;
} }
@ -4346,19 +4395,18 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
case Range::BackQuoteAround: case Range::BackQuoteAround:
{ {
int nrBlock = VEditUtils::selectedBlockCount(cursor); int nrBlock = VEditUtils::selectedBlockCount(cursor);
VEditUtils::indentSelectedBlocks(doc, if (p_type == IndentType::AutoIndent) {
cursor, VEditUtils::indentSelectedBlocksAsBlock(cursor, false);
m_editConfig->m_tabSpaces,
p_isIndent);
if (p_isIndent) {
message(tr("%1 %2 >ed 1 time").arg(nrBlock).arg(nrBlock > 1 ? tr("lines")
: tr("line")));
} else { } else {
message(tr("%1 %2 <ed 1 time").arg(nrBlock).arg(nrBlock > 1 ? tr("lines") VEditUtils::indentSelectedBlocks(cursor,
: tr("line"))); m_editConfig->m_tabSpaces,
p_type == IndentType::Indent);
} }
message(tr("%1 %2 %3ed 1 time").arg(nrBlock)
.arg(nrBlock > 1 ? tr("lines")
: tr("line"))
.arg(op));
break; break;
} }
@ -4384,24 +4432,39 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
break; break;
} }
if (checkMode(VimMode::VisualLine) || checkMode(VimMode::Visual)) {
// Visual mode, omitting repeat and movement.
// Different from Vim:
// Do not exit Visual mode after indentation/unindentation.
if (p_type == IndentType::AutoIndent) {
VEditUtils::indentSelectedBlocksAsBlock(cursor, false);
} else {
VEditUtils::indentSelectedBlocks(cursor,
m_editConfig->m_tabSpaces,
p_type == IndentType::Indent);
}
return;
}
processMovement(cursor, processMovement(cursor,
QTextCursor::KeepAnchor, QTextCursor::KeepAnchor,
to, to,
repeat); repeat);
int nrBlock = VEditUtils::selectedBlockCount(cursor); int nrBlock = VEditUtils::selectedBlockCount(cursor);
if (p_isIndent) { if (p_type == IndentType::AutoIndent) {
message(tr("%1 %2 >ed 1 time").arg(nrBlock).arg(nrBlock > 1 ? tr("lines") VEditUtils::indentSelectedBlocksAsBlock(cursor, false);
: tr("line")));
} else { } else {
message(tr("%1 %2 <ed 1 time").arg(nrBlock).arg(nrBlock > 1 ? tr("lines") VEditUtils::indentSelectedBlocks(cursor,
: tr("line"))); m_editConfig->m_tabSpaces,
p_type == IndentType::Indent);
} }
VEditUtils::indentSelectedBlocks(doc, message(tr("%1 %2 %3ed 1 time").arg(nrBlock)
cursor, .arg(nrBlock > 1 ? tr("lines")
m_editConfig->m_tabSpaces, : tr("line"))
p_isIndent); .arg(op));
} }
void VVim::processToLowerAction(QList<Token> &p_tokens, bool p_toLower) void VVim::processToLowerAction(QList<Token> &p_tokens, bool p_toLower)

View File

@ -361,6 +361,7 @@ private:
Change, Change,
Indent, Indent,
UnIndent, UnIndent,
AutoIndent,
ToUpper, ToUpper,
ToLower, ToLower,
ReverseCase, ReverseCase,
@ -582,6 +583,8 @@ private:
const int c_maximumLocations; const int c_maximumLocations;
}; };
enum IndentType { Indent = 0, UnIndent, AutoIndent };
// Returns true if the event is consumed and need no more handling. // Returns true if the event is consumed and need no more handling.
bool handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos = NULL); bool handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos = NULL);
@ -615,8 +618,9 @@ private:
// @p_tokens is the arguments of the Action::Change action. // @p_tokens is the arguments of the Action::Change action.
void processChangeAction(QList<Token> &p_tokens); void processChangeAction(QList<Token> &p_tokens);
// @p_tokens is the arguments of the Action::Indent and Action::UnIndent action. // @p_tokens is the arguments of the Action::Indent, Action::UnIndent,
void processIndentAction(QList<Token> &p_tokens, bool p_isIndent); // and Action::AutoIndent action.
void processIndentAction(QList<Token> &p_tokens, IndentType p_type);
// @p_tokens is the arguments of the Action::ToLower and Action::ToUpper action. // @p_tokens is the arguments of the Action::ToLower and Action::ToUpper action.
void processToLowerAction(QList<Token> &p_tokens, bool p_toLower); void processToLowerAction(QList<Token> &p_tokens, bool p_toLower);

View File

@ -410,7 +410,6 @@ bool VMdEditOperations::handleKeyBracketLeft(QKeyEvent *p_event)
bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event) bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
{ {
QTextDocument *doc = m_editor->documentW();
QString text(m_editConfig->m_tabSpaces); QString text(m_editConfig->m_tabSpaces);
if (p_event->modifiers() == Qt::NoModifier) { if (p_event->modifiers() == Qt::NoModifier) {
@ -419,7 +418,7 @@ bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
m_autoIndentPos = -1; m_autoIndentPos = -1;
cursor.beginEditBlock(); cursor.beginEditBlock();
// Indent each selected line. // Indent each selected line.
VEditUtils::indentSelectedBlocks(doc, cursor, text, true); VEditUtils::indentSelectedBlocks(cursor, text, true);
cursor.endEditBlock(); cursor.endEditBlock();
m_editor->setTextCursorW(cursor); m_editor->setTextCursorW(cursor);
} else { } else {
@ -457,9 +456,9 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
m_autoIndentPos = -1; m_autoIndentPos = -1;
return false; return false;
} }
QTextDocument *doc = m_editor->documentW();
QTextCursor cursor = m_editor->textCursorW(); QTextCursor cursor = m_editor->textCursorW();
QTextBlock block = doc->findBlock(cursor.selectionStart()); QTextBlock block = m_editor->documentW()->findBlock(cursor.selectionStart());
bool continueAutoIndent = false; bool continueAutoIndent = false;
int seq = -1; int seq = -1;
if (cursor.position() == m_autoIndentPos if (cursor.position() == m_autoIndentPos
@ -473,7 +472,7 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
changeListBlockSeqNumber(block, 1); changeListBlockSeqNumber(block, 1);
} }
VEditUtils::indentSelectedBlocks(doc, cursor, m_editConfig->m_tabSpaces, false); VEditUtils::indentSelectedBlocks(cursor, m_editConfig->m_tabSpaces, false);
cursor.endEditBlock(); cursor.endEditBlock();
if (continueAutoIndent) { if (continueAutoIndent) {