mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-06 14:29:54 +08:00
vim-mode: Ctrl+O to support autoindent and autolist
This commit is contained in:
parent
eba2556a3a
commit
f6a91d04a8
@ -3,6 +3,8 @@
|
|||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "vutils.h"
|
||||||
|
|
||||||
void VEditUtils::removeBlock(QTextBlock &p_block, QString *p_text)
|
void VEditUtils::removeBlock(QTextBlock &p_block, QString *p_text)
|
||||||
{
|
{
|
||||||
QTextCursor cursor(p_block);
|
QTextCursor cursor(p_block);
|
||||||
@ -39,3 +41,95 @@ void VEditUtils::removeBlock(QTextCursor &p_cursor, QString *p_text)
|
|||||||
|
|
||||||
p_cursor.movePosition(QTextCursor::StartOfBlock);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,25 @@ public:
|
|||||||
// Need to call setTextCursor() to make it take effect.
|
// Need to call setTextCursor() to make it take effect.
|
||||||
static void removeBlock(QTextCursor &p_cursor, QString *p_text = NULL);
|
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:
|
private:
|
||||||
VEditUtils() {}
|
VEditUtils() {}
|
||||||
};
|
};
|
||||||
|
@ -5,9 +5,12 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include "vconfigmanager.h"
|
||||||
#include "vedit.h"
|
#include "vedit.h"
|
||||||
#include "utils/veditutils.h"
|
#include "utils/veditutils.h"
|
||||||
|
|
||||||
|
extern VConfigManager vconfig;
|
||||||
|
|
||||||
const QChar VVim::c_unnamedRegister = QChar('"');
|
const QChar VVim::c_unnamedRegister = QChar('"');
|
||||||
const QChar VVim::c_blackHoleRegister = QChar('_');
|
const QChar VVim::c_blackHoleRegister = QChar('_');
|
||||||
const QChar VVim::c_selectionRegister = 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).
|
// 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 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.
|
// @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();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
if (m_mode == VimMode::Normal) {
|
if (m_mode == VimMode::Normal) {
|
||||||
// Insert at the first non-space character.
|
// 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) {
|
} else if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) {
|
||||||
// Insert at the start of line.
|
// Insert at the start of line.
|
||||||
cursor.movePosition(QTextCursor::StartOfBlock,
|
cursor.movePosition(QTextCursor::StartOfBlock,
|
||||||
@ -391,31 +375,39 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event)
|
|||||||
|
|
||||||
case Qt::Key_O:
|
case Qt::Key_O:
|
||||||
{
|
{
|
||||||
if (modifiers == Qt::NoModifier) {
|
if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier) {
|
||||||
// Insert a new block under current block and enter insert mode.
|
// Insert a new block under/above current block and enter insert mode.
|
||||||
|
bool insertAbove = modifiers == Qt::ShiftModifier;
|
||||||
if (m_mode == VimMode::Normal) {
|
if (m_mode == VimMode::Normal) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
cursor.movePosition(QTextCursor::EndOfBlock,
|
cursor.beginEditBlock();
|
||||||
|
cursor.movePosition(insertAbove ? QTextCursor::StartOfBlock
|
||||||
|
: QTextCursor::EndOfBlock,
|
||||||
QTextCursor::MoveAnchor,
|
QTextCursor::MoveAnchor,
|
||||||
1);
|
1);
|
||||||
|
|
||||||
cursor.insertBlock();
|
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);
|
m_editor->setTextCursor(cursor);
|
||||||
|
|
||||||
setMode(VimMode::Insert);
|
setMode(VimMode::Insert);
|
||||||
}
|
}
|
||||||
} else if (modifiers == Qt::ShiftModifier) {
|
|
||||||
// Insert a new block above current block and enter insert mode.
|
break;
|
||||||
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.
|
// p_repeat is not considered in this command.
|
||||||
// If all the block is space, just move to the end of block; otherwise,
|
// If all the block is space, just move to the end of block; otherwise,
|
||||||
// move to the first non-space character.
|
// move to the first non-space character.
|
||||||
moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
||||||
hasMoved = true;
|
hasMoved = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1182,7 +1174,7 @@ bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc,
|
|||||||
p_cursor.movePosition(QTextCursor::End, p_moveMode, 1);
|
p_cursor.movePosition(QTextCursor::End, p_moveMode, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
||||||
hasMoved = true;
|
hasMoved = true;
|
||||||
break;
|
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.
|
// Jump to the first non-space character of the start of the document.
|
||||||
V_ASSERT(p_repeat == -1);
|
V_ASSERT(p_repeat == -1);
|
||||||
p_cursor.movePosition(QTextCursor::Start, p_moveMode, 1);
|
p_cursor.movePosition(QTextCursor::Start, p_moveMode, 1);
|
||||||
moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
||||||
hasMoved = true;
|
hasMoved = true;
|
||||||
break;
|
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.
|
// Jump to the first non-space character of the end of the document.
|
||||||
V_ASSERT(p_repeat == -1);
|
V_ASSERT(p_repeat == -1);
|
||||||
p_cursor.movePosition(QTextCursor::End, p_moveMode, 1);
|
p_cursor.movePosition(QTextCursor::End, p_moveMode, 1);
|
||||||
moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
VEditUtils::moveCursorFirstNonSpaceCharacter(p_cursor, p_moveMode);
|
||||||
hasMoved = true;
|
hasMoved = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "vmdedit.h"
|
#include "vmdedit.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "utils/vvim.h"
|
#include "utils/vvim.h"
|
||||||
|
#include "utils/veditutils.h"
|
||||||
|
|
||||||
extern VConfigManager vconfig;
|
extern VConfigManager vconfig;
|
||||||
|
|
||||||
@ -718,14 +719,21 @@ bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
|
|||||||
if (vconfig.getAutoIndent()) {
|
if (vconfig.getAutoIndent()) {
|
||||||
handled = true;
|
handled = true;
|
||||||
|
|
||||||
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
bool textInserted = false;
|
bool textInserted = false;
|
||||||
|
cursor.beginEditBlock();
|
||||||
|
cursor.removeSelectedText();
|
||||||
|
|
||||||
// Indent the new line as previous line.
|
// Indent the new line as previous line.
|
||||||
textInserted = insertNewBlockWithIndent();
|
textInserted = VEditUtils::insertBlockWithIndent(cursor);
|
||||||
|
|
||||||
// Continue the list from previous line.
|
// Continue the list from previous line.
|
||||||
if (vconfig.getAutoList()) {
|
if (vconfig.getAutoList()) {
|
||||||
textInserted = insertListMarkAsPreviousLine() || textInserted;
|
textInserted = VEditUtils::insertListMarkAsPreviousBlock(cursor) || textInserted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursor.endEditBlock();
|
||||||
|
m_editor->setTextCursor(cursor);
|
||||||
if (textInserted) {
|
if (textInserted) {
|
||||||
m_autoIndentPos = m_editor->textCursor().position();
|
m_autoIndentPos = m_editor->textCursor().position();
|
||||||
}
|
}
|
||||||
@ -734,61 +742,6 @@ bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
|
|||||||
return handled;
|
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)
|
bool VMdEditOperations::isListBlock(const QTextBlock &p_block, int *p_seq)
|
||||||
{
|
{
|
||||||
QString text = p_block.text();
|
QString text = p_block.text();
|
||||||
|
@ -42,8 +42,6 @@ private:
|
|||||||
bool handleKeyReturn(QKeyEvent *p_event);
|
bool handleKeyReturn(QKeyEvent *p_event);
|
||||||
bool handleKeyBracketLeft(QKeyEvent *p_event);
|
bool handleKeyBracketLeft(QKeyEvent *p_event);
|
||||||
bool insertTitle(int p_level);
|
bool insertTitle(int p_level);
|
||||||
bool insertNewBlockWithIndent();
|
|
||||||
bool insertListMarkAsPreviousLine();
|
|
||||||
void deleteIndentAndListMark();
|
void deleteIndentAndListMark();
|
||||||
|
|
||||||
// Check if @p_block is a auto list block.
|
// Check if @p_block is a auto list block.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user