From f6a91d04a8436c3fe30fd3a21bcab14eedadf69b Mon Sep 17 00:00:00 2001 From: Le Tan Date: Wed, 14 Jun 2017 21:12:47 +0800 Subject: [PATCH] vim-mode: Ctrl+O to support autoindent and autolist --- src/utils/veditutils.cpp | 94 +++++++++++++++++++++++++++++++++++++++ src/utils/veditutils.h | 19 ++++++++ src/utils/vvim.cpp | 74 ++++++++++++++---------------- src/vmdeditoperations.cpp | 67 +++++----------------------- src/vmdeditoperations.h | 2 - 5 files changed, 156 insertions(+), 100 deletions(-) diff --git a/src/utils/veditutils.cpp b/src/utils/veditutils.cpp index b6d3e343..a9448837 100644 --- a/src/utils/veditutils.cpp +++ b/src/utils/veditutils.cpp @@ -3,6 +3,8 @@ #include #include +#include "vutils.h" + void VEditUtils::removeBlock(QTextBlock &p_block, QString *p_text) { QTextCursor cursor(p_block); @@ -39,3 +41,95 @@ void VEditUtils::removeBlock(QTextCursor &p_cursor, QString *p_text) p_cursor.movePosition(QTextCursor::StartOfBlock); } + +bool VEditUtils::insertBlockWithIndent(QTextCursor &p_cursor) +{ + V_ASSERT(!p_cursor.hasSelection()); + p_cursor.insertBlock(); + return indentBlockAsPreviousBlock(p_cursor); +} + +bool VEditUtils::insertListMarkAsPreviousBlock(QTextCursor &p_cursor) +{ + bool ret = false; + QTextBlock block = p_cursor.block(); + QTextBlock preBlock = block.previous(); + if (!preBlock.isValid()) { + return false; + } + + QString text = preBlock.text(); + QRegExp regExp("^\\s*(-|\\d+\\.)\\s"); + int regIdx = regExp.indexIn(text); + if (regIdx != -1) { + ret = true; + V_ASSERT(regExp.captureCount() == 1); + QString markText = regExp.capturedTexts()[1]; + if (markText == "-") { + // Insert - in front. + p_cursor.insertText("- "); + } else { + // markText is like "123.". + V_ASSERT(markText.endsWith('.')); + bool ok = false; + int num = markText.left(markText.size() - 1).toInt(&ok, 10); + V_ASSERT(ok); + num++; + p_cursor.insertText(QString::number(num, 10) + ". "); + } + } + + return ret; + +} + +bool VEditUtils::indentBlockAsPreviousBlock(QTextCursor &p_cursor) +{ + bool changed = false; + QTextBlock block = p_cursor.block(); + if (block.blockNumber() == 0) { + // The first block. + return false; + } + + QTextBlock preBlock = block.previous(); + QString text = preBlock.text(); + QRegExp regExp("(^\\s*)"); + regExp.indexIn(text); + V_ASSERT(regExp.captureCount() == 1); + QString leadingSpaces = regExp.capturedTexts()[1]; + + moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::MoveAnchor); + if (!p_cursor.atBlockStart()) { + p_cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); + p_cursor.removeSelectedText(); + changed = true; + } + + if (!leadingSpaces.isEmpty()) { + p_cursor.insertText(leadingSpaces); + changed = true; + } + + p_cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor); + + return changed; +} + +void VEditUtils::moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor, + QTextCursor::MoveMode p_mode) +{ + QTextBlock block = p_cursor.block(); + QString text = block.text(); + int idx = 0; + for (; idx < text.size(); ++idx) { + if (text[idx].isSpace()) { + continue; + } else { + break; + } + } + + p_cursor.setPosition(block.position() + idx, p_mode); +} + diff --git a/src/utils/veditutils.h b/src/utils/veditutils.h index fc4bc413..a13225cc 100644 --- a/src/utils/veditutils.h +++ b/src/utils/veditutils.h @@ -17,6 +17,25 @@ public: // Need to call setTextCursor() to make it take effect. static void removeBlock(QTextCursor &p_cursor, QString *p_text = NULL); + // Move @p_cursor to the first non-space character of current block. + // Need to call setTextCursor() to make it take effect. + static void moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor, + QTextCursor::MoveMode p_mode); + // Indent current block as previous block. + // Return true if some changes have been made. + static bool indentBlockAsPreviousBlock(QTextCursor &p_cursor); + + // Insert a new block at current position with the same indentation as + // current block. Should clear the selection before calling this. + // Returns true if non-empty indentation has been inserted. + // Need to call setTextCursor() to make it take effect. + static bool insertBlockWithIndent(QTextCursor &p_cursor); + + // Fetch the list mark of previous block, and insert it at current position. + // Returns true if list mark has been inserted. + // Need to call setTextCursor() to make it take effect. + static bool insertListMarkAsPreviousBlock(QTextCursor &p_cursor); + private: VEditUtils() {} }; diff --git a/src/utils/vvim.cpp b/src/utils/vvim.cpp index 25bf0101..d2cff182 100644 --- a/src/utils/vvim.cpp +++ b/src/utils/vvim.cpp @@ -5,9 +5,12 @@ #include #include #include +#include "vconfigmanager.h" #include "vedit.h" #include "utils/veditutils.h" +extern VConfigManager vconfig; + const QChar VVim::c_unnamedRegister = QChar('"'); const QChar VVim::c_blackHoleRegister = QChar('_'); const QChar VVim::c_selectionRegister = QChar('+'); @@ -35,25 +38,6 @@ static void setCursorPositionInBlock(QTextCursor &p_cursor, int p_positionInBloc } } -// Move @p_cursor to the first non-space character of current block. -// Need to setTextCursor() after calling this. -static void moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor, - QTextCursor::MoveMode p_mode) -{ - QTextBlock block = p_cursor.block(); - QString text = block.text(); - int idx = 0; - for (; idx < text.size(); ++idx) { - if (text[idx].isSpace()) { - continue; - } else { - break; - } - } - - p_cursor.setPosition(block.position() + idx, p_mode); -} - // Find the start and end of the WORD @p_cursor locates in (within a single block). // @p_start and @p_end will be the global position of the start and end of the WORD. // @p_start will equals to @p_end if @p_cursor is a space. @@ -336,7 +320,7 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event) QTextCursor cursor = m_editor->textCursor(); if (m_mode == VimMode::Normal) { // Insert at the first non-space character. - moveCursorFirstNonSpaceCharacter(cursor, QTextCursor::MoveAnchor); + VEditUtils::moveCursorFirstNonSpaceCharacter(cursor, QTextCursor::MoveAnchor); } else if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) { // Insert at the start of line. cursor.movePosition(QTextCursor::StartOfBlock, @@ -391,31 +375,39 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event) case Qt::Key_O: { - if (modifiers == Qt::NoModifier) { - // Insert a new block under current block and enter insert mode. + if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier) { + // Insert a new block under/above current block and enter insert mode. + bool insertAbove = modifiers == Qt::ShiftModifier; if (m_mode == VimMode::Normal) { QTextCursor cursor = m_editor->textCursor(); - cursor.movePosition(QTextCursor::EndOfBlock, + cursor.beginEditBlock(); + cursor.movePosition(insertAbove ? QTextCursor::StartOfBlock + : QTextCursor::EndOfBlock, QTextCursor::MoveAnchor, 1); + cursor.insertBlock(); + + if (insertAbove) { + cursor.movePosition(QTextCursor::PreviousBlock, + QTextCursor::MoveAnchor, + 1); + } + + if (vconfig.getAutoIndent()) { + VEditUtils::indentBlockAsPreviousBlock(cursor); + if (vconfig.getAutoList()) { + VEditUtils::insertListMarkAsPreviousBlock(cursor); + } + } + + cursor.endEditBlock(); m_editor->setTextCursor(cursor); + setMode(VimMode::Insert); } - } else if (modifiers == Qt::ShiftModifier) { - // Insert a new block above current block and enter insert mode. - if (m_mode == VimMode::Normal) { - QTextCursor cursor = m_editor->textCursor(); - cursor.movePosition(QTextCursor::StartOfBlock, - QTextCursor::MoveAnchor, - 1); - cursor.insertBlock(); - cursor.movePosition(QTextCursor::PreviousBlock, - QTextCursor::MoveAnchor, - 1); - m_editor->setTextCursor(cursor); - setMode(VimMode::Insert); - } + + break; } break; @@ -1163,7 +1155,7 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc, // p_repeat is not considered in this command. // If all the block is space, just move to the end of block; otherwise, // move to the first non-space character. - moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); + VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); hasMoved = true; break; } @@ -1182,7 +1174,7 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc, p_cursor.movePosition(QTextCursor::End, p_moveMode, 1); } - moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); + VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); hasMoved = true; break; } @@ -1192,7 +1184,7 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc, // Jump to the first non-space character of the start of the document. V_ASSERT(p_repeat == -1); p_cursor.movePosition(QTextCursor::Start, p_moveMode, 1); - moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); + VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); hasMoved = true; break; } @@ -1202,7 +1194,7 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc, // Jump to the first non-space character of the end of the document. V_ASSERT(p_repeat == -1); p_cursor.movePosition(QTextCursor::End, p_moveMode, 1); - moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); + VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode); hasMoved = true; break; } diff --git a/src/vmdeditoperations.cpp b/src/vmdeditoperations.cpp index 9f7dc04f..515a03ba 100644 --- a/src/vmdeditoperations.cpp +++ b/src/vmdeditoperations.cpp @@ -22,6 +22,7 @@ #include "vmdedit.h" #include "vconfigmanager.h" #include "utils/vvim.h" +#include "utils/veditutils.h" extern VConfigManager vconfig; @@ -718,14 +719,21 @@ bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event) if (vconfig.getAutoIndent()) { handled = true; + QTextCursor cursor = m_editor->textCursor(); bool textInserted = false; + cursor.beginEditBlock(); + cursor.removeSelectedText(); + // Indent the new line as previous line. - textInserted = insertNewBlockWithIndent(); + textInserted = VEditUtils::insertBlockWithIndent(cursor); // Continue the list from previous line. if (vconfig.getAutoList()) { - textInserted = insertListMarkAsPreviousLine() || textInserted; + textInserted = VEditUtils::insertListMarkAsPreviousBlock(cursor) || textInserted; } + + cursor.endEditBlock(); + m_editor->setTextCursor(cursor); if (textInserted) { m_autoIndentPos = m_editor->textCursor().position(); } @@ -734,61 +742,6 @@ bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event) return handled; } -bool VMdEditOperations::insertNewBlockWithIndent() -{ - QTextCursor cursor = m_editor->textCursor(); - bool ret = false; - - cursor.beginEditBlock(); - cursor.removeSelectedText(); - QTextBlock block = cursor.block(); - QString text = block.text(); - QRegExp regExp("(^\\s*)"); - regExp.indexIn(text); - V_ASSERT(regExp.captureCount() == 1); - QString leadingSpaces = regExp.capturedTexts()[1]; - cursor.insertBlock(); - if (!leadingSpaces.isEmpty()) { - cursor.insertText(leadingSpaces); - ret = true; - } - cursor.endEditBlock(); - m_editor->setTextCursor(cursor); - return ret; -} - -bool VMdEditOperations::insertListMarkAsPreviousLine() -{ - bool ret = false; - QTextCursor cursor = m_editor->textCursor(); - QTextBlock block = cursor.block(); - QTextBlock preBlock = block.previous(); - QString text = preBlock.text(); - QRegExp regExp("^\\s*(-|\\d+\\.)\\s"); - int regIdx = regExp.indexIn(text); - if (regIdx != -1) { - ret = true; - V_ASSERT(regExp.captureCount() == 1); - cursor.beginEditBlock(); - QString markText = regExp.capturedTexts()[1]; - if (markText == "-") { - // Insert - in front. - cursor.insertText("- "); - } else { - // markText is like "123.". - V_ASSERT(markText.endsWith('.')); - bool ok = false; - int num = markText.left(markText.size() - 1).toInt(&ok, 10); - V_ASSERT(ok); - num++; - cursor.insertText(QString::number(num, 10) + ". "); - } - cursor.endEditBlock(); - m_editor->setTextCursor(cursor); - } - return ret; -} - bool VMdEditOperations::isListBlock(const QTextBlock &p_block, int *p_seq) { QString text = p_block.text(); diff --git a/src/vmdeditoperations.h b/src/vmdeditoperations.h index 32947dca..5cdcc625 100644 --- a/src/vmdeditoperations.h +++ b/src/vmdeditoperations.h @@ -42,8 +42,6 @@ private: bool handleKeyReturn(QKeyEvent *p_event); bool handleKeyBracketLeft(QKeyEvent *p_event); bool insertTitle(int p_level); - bool insertNewBlockWithIndent(); - bool insertListMarkAsPreviousLine(); void deleteIndentAndListMark(); // Check if @p_block is a auto list block.