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

View File

@ -31,6 +31,9 @@ public:
// @p_next: indent as next block or previous block.
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.
static bool hasSameIndent(const QTextBlock &p_blocka, const QTextBlock &p_blockb);
@ -56,12 +59,18 @@ public:
static QString selectedText(const QTextCursor &p_cursor);
// Indent selected blocks. If no selection, indent current block.
// Cursor position and selection is not changed.
// @p_isIndent: whether it is indentation or unindentation.
static void indentSelectedBlocks(const QTextDocument *p_doc,
const QTextCursor &p_cursor,
static void indentSelectedBlocks(const QTextCursor &p_cursor,
const QString &p_indentationText,
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.
// @p_skipEmpty: skip empty block.
static void indentBlock(QTextCursor &p_cursor,

View File

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

View File

@ -361,6 +361,7 @@ private:
Change,
Indent,
UnIndent,
AutoIndent,
ToUpper,
ToLower,
ReverseCase,
@ -582,6 +583,8 @@ private:
const int c_maximumLocations;
};
enum IndentType { Indent = 0, UnIndent, AutoIndent };
// Returns true if the event is consumed and need no more handling.
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.
void processChangeAction(QList<Token> &p_tokens);
// @p_tokens is the arguments of the Action::Indent and Action::UnIndent action.
void processIndentAction(QList<Token> &p_tokens, bool p_isIndent);
// @p_tokens is the arguments of the Action::Indent, Action::UnIndent,
// 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.
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)
{
QTextDocument *doc = m_editor->documentW();
QString text(m_editConfig->m_tabSpaces);
if (p_event->modifiers() == Qt::NoModifier) {
@ -419,7 +418,7 @@ bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
m_autoIndentPos = -1;
cursor.beginEditBlock();
// Indent each selected line.
VEditUtils::indentSelectedBlocks(doc, cursor, text, true);
VEditUtils::indentSelectedBlocks(cursor, text, true);
cursor.endEditBlock();
m_editor->setTextCursorW(cursor);
} else {
@ -457,9 +456,9 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
m_autoIndentPos = -1;
return false;
}
QTextDocument *doc = m_editor->documentW();
QTextCursor cursor = m_editor->textCursorW();
QTextBlock block = doc->findBlock(cursor.selectionStart());
QTextBlock block = m_editor->documentW()->findBlock(cursor.selectionStart());
bool continueAutoIndent = false;
int seq = -1;
if (cursor.position() == m_autoIndentPos
@ -473,7 +472,7 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
changeListBlockSeqNumber(block, 1);
}
VEditUtils::indentSelectedBlocks(doc, cursor, m_editConfig->m_tabSpaces, false);
VEditUtils::indentSelectedBlocks(cursor, m_editConfig->m_tabSpaces, false);
cursor.endEditBlock();
if (continueAutoIndent) {