From d3ff7871538be7c3adc798932a1b627f073bf5e6 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Fri, 24 Nov 2017 19:57:37 +0800 Subject: [PATCH] vim-mode: support = to auto indent selected blocks as previous block --- src/utils/veditutils.cpp | 58 +++++++++++++-- src/utils/veditutils.h | 13 +++- src/utils/vvim.cpp | 149 +++++++++++++++++++++++++++----------- src/utils/vvim.h | 8 +- src/vmdeditoperations.cpp | 9 +-- 5 files changed, 178 insertions(+), 59 deletions(-) diff --git a/src/utils/veditutils.cpp b/src/utils/veditutils.cpp index 6c24538e..4fa85e91 100644 --- a/src/utils/veditutils.cpp +++ b/src/utils/veditutils.cpp @@ -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(); } diff --git a/src/utils/veditutils.h b/src/utils/veditutils.h index 0db357db..0bfb1fa4 100644 --- a/src/utils/veditutils.h +++ b/src/utils/veditutils.h @@ -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, diff --git a/src/utils/vvim.cpp b/src/utils/vvim.cpp index 77c2d8c7..d575c0c1 100644 --- a/src/utils/vvim.cpp +++ b/src/utils/vvim.cpp @@ -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 &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 &p_tokens, bool p_isIndent) +void VVim::processIndentAction(QList &p_tokens, IndentType p_type) { Token to = p_tokens.takeFirst(); int repeat = -1; @@ -4280,10 +4313,27 @@ void VVim::processIndentAction(QList &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 &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 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 &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 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 &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 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 &p_tokens, bool p_toLower) diff --git a/src/utils/vvim.h b/src/utils/vvim.h index 37a7c99b..5da8b5a0 100644 --- a/src/utils/vvim.h +++ b/src/utils/vvim.h @@ -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 &p_tokens); - // @p_tokens is the arguments of the Action::Indent and Action::UnIndent action. - void processIndentAction(QList &p_tokens, bool p_isIndent); + // @p_tokens is the arguments of the Action::Indent, Action::UnIndent, + // and Action::AutoIndent action. + void processIndentAction(QList &p_tokens, IndentType p_type); // @p_tokens is the arguments of the Action::ToLower and Action::ToUpper action. void processToLowerAction(QList &p_tokens, bool p_toLower); diff --git a/src/vmdeditoperations.cpp b/src/vmdeditoperations.cpp index 06edd0ea..5aa9a91c 100644 --- a/src/vmdeditoperations.cpp +++ b/src/vmdeditoperations.cpp @@ -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) {