vim-mode: Ctrl+O to support autoindent and autolist

This commit is contained in:
Le Tan 2017-06-14 21:12:47 +08:00
parent eba2556a3a
commit f6a91d04a8
5 changed files with 156 additions and 100 deletions

View File

@ -3,6 +3,8 @@
#include <QTextDocument>
#include <QDebug>
#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);
}

View File

@ -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() {}
};

View File

@ -5,9 +5,12 @@
#include <QString>
#include <QScrollBar>
#include <QDebug>
#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;
}

View File

@ -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();

View File

@ -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.