mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-06 06:19:52 +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 <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);
|
||||
}
|
||||
|
||||
|
@ -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() {}
|
||||
};
|
||||
|
@ -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,
|
||||
QTextCursor::MoveAnchor,
|
||||
1);
|
||||
cursor.insertBlock();
|
||||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user