mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-06 06:19:52 +08:00
vim-mode: support Copy/Paste/Change/Indent/UnIndent/ToLower/ToUpper action
This commit is contained in:
parent
f6a91d04a8
commit
5047e19b24
@ -19,7 +19,7 @@ void VEditUtils::removeBlock(QTextCursor &p_cursor, QString *p_text)
|
||||
|
||||
p_cursor.select(QTextCursor::BlockUnderCursor);
|
||||
if (p_text) {
|
||||
*p_text = p_cursor.selectedText() + "\n";
|
||||
*p_text = selectedText(p_cursor) + "\n";
|
||||
}
|
||||
|
||||
p_cursor.deleteChar();
|
||||
@ -133,3 +133,80 @@ void VEditUtils::moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor,
|
||||
p_cursor.setPosition(block.position() + idx, p_mode);
|
||||
}
|
||||
|
||||
void VEditUtils::removeObjectReplacementCharacter(QString &p_text)
|
||||
{
|
||||
QRegExp orcBlockExp(QString("[\\n|^][ |\\t]*\\xfffc[ |\\t]*(?=\\n)"));
|
||||
p_text.remove(orcBlockExp);
|
||||
p_text.remove(QChar::ObjectReplacementCharacter);
|
||||
}
|
||||
|
||||
QString VEditUtils::selectedText(const QTextCursor &p_cursor)
|
||||
{
|
||||
QString text = p_cursor.selectedText();
|
||||
text.replace(QChar::ParagraphSeparator, '\n');
|
||||
return text;
|
||||
}
|
||||
|
||||
// Use another QTextCursor to remain the selection.
|
||||
void VEditUtils::indentSelectedBlocks(const QTextDocument *p_doc,
|
||||
const QTextCursor &p_cursor,
|
||||
const QString &p_indentationText,
|
||||
bool p_isIndent)
|
||||
{
|
||||
int nrBlocks = 1;
|
||||
int start = p_cursor.selectionStart();
|
||||
int end = p_cursor.selectionEnd();
|
||||
|
||||
QTextBlock sBlock = p_doc->findBlock(start);
|
||||
if (start != end) {
|
||||
QTextBlock eBlock = p_doc->findBlock(end);
|
||||
nrBlocks = eBlock.blockNumber() - sBlock.blockNumber() + 1;
|
||||
}
|
||||
|
||||
QTextCursor bCursor(sBlock);
|
||||
bCursor.beginEditBlock();
|
||||
for (int i = 0; i < nrBlocks; ++i) {
|
||||
if (p_isIndent) {
|
||||
indentBlock(bCursor, p_indentationText);
|
||||
} else {
|
||||
unindentBlock(bCursor, p_indentationText);
|
||||
}
|
||||
|
||||
bCursor.movePosition(QTextCursor::NextBlock);
|
||||
}
|
||||
bCursor.endEditBlock();
|
||||
}
|
||||
|
||||
void VEditUtils::indentBlock(QTextCursor &p_cursor,
|
||||
const QString &p_indentationText)
|
||||
{
|
||||
QTextBlock block = p_cursor.block();
|
||||
if (block.length() > 1) {
|
||||
p_cursor.movePosition(QTextCursor::StartOfBlock);
|
||||
p_cursor.insertText(p_indentationText);
|
||||
}
|
||||
}
|
||||
|
||||
void VEditUtils::unindentBlock(QTextCursor &p_cursor,
|
||||
const QString &p_indentationText)
|
||||
{
|
||||
QTextBlock block = p_cursor.block();
|
||||
QString text = block.text();
|
||||
if (text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
p_cursor.movePosition(QTextCursor::StartOfBlock);
|
||||
if (text[0] == '\t') {
|
||||
p_cursor.deleteChar();
|
||||
} else if (text[0].isSpace()) {
|
||||
int width = p_indentationText.size();
|
||||
for (int i = 0; i < width; ++i) {
|
||||
if (text[i] == ' ') {
|
||||
p_cursor.deleteChar();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <QTextBlock>
|
||||
#include <QTextCursor>
|
||||
|
||||
class QTextDocument;
|
||||
|
||||
// Utils for text edit.
|
||||
class VEditUtils
|
||||
{
|
||||
@ -36,6 +38,31 @@ public:
|
||||
// Need to call setTextCursor() to make it take effect.
|
||||
static bool insertListMarkAsPreviousBlock(QTextCursor &p_cursor);
|
||||
|
||||
// Remove ObjectReplaceCharacter in p_text.
|
||||
// If the ObjectReplaceCharacter is in a block with only other spaces, remove the
|
||||
// whole block.
|
||||
static void removeObjectReplacementCharacter(QString &p_text);
|
||||
|
||||
// p_cursor.selectedText() will use U+2029 (QChar::ParagraphSeparator)
|
||||
// instead of \n for a new line.
|
||||
// This function will translate it to \n.
|
||||
static QString selectedText(const QTextCursor &p_cursor);
|
||||
|
||||
// Indent selected blocks. If no selection, indent current block.
|
||||
// @p_isIndent: whether it is indentation or unindentation.
|
||||
static void indentSelectedBlocks(const QTextDocument *p_doc,
|
||||
const QTextCursor &p_cursor,
|
||||
const QString &p_indentationText,
|
||||
bool p_isIndent);
|
||||
|
||||
// Indent current block.
|
||||
// Skip empty block.
|
||||
static void indentBlock(QTextCursor &p_cursor,
|
||||
const QString &p_indentationText);
|
||||
|
||||
static void unindentBlock(QTextCursor &p_cursor,
|
||||
const QString &p_indentationText);
|
||||
|
||||
private:
|
||||
VEditUtils() {}
|
||||
};
|
||||
|
1677
src/utils/vvim.cpp
1677
src/utils/vvim.cpp
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@
|
||||
#include <QString>
|
||||
#include <QTextCursor>
|
||||
#include <QHash>
|
||||
#include <QDebug>
|
||||
#include "vutils.h"
|
||||
|
||||
class VEdit;
|
||||
@ -101,6 +102,7 @@ private:
|
||||
Delete,
|
||||
Copy,
|
||||
Paste,
|
||||
PasteBefore,
|
||||
Change,
|
||||
Indent,
|
||||
UnIndent,
|
||||
@ -143,7 +145,22 @@ private:
|
||||
enum class Range
|
||||
{
|
||||
Line = 0,
|
||||
Word,
|
||||
WordInner,
|
||||
WordAround,
|
||||
WORDInner,
|
||||
WORDAround,
|
||||
QuoteInner,
|
||||
QuoteAround,
|
||||
DoubleQuoteInner,
|
||||
DoubleQuoteAround,
|
||||
ParenthesisInner,
|
||||
ParenthesisAround,
|
||||
BracketInner,
|
||||
BracketAround,
|
||||
AngleBracketInner,
|
||||
AngleBracketAround,
|
||||
BraceInner,
|
||||
BraceAround,
|
||||
Invalid
|
||||
};
|
||||
|
||||
@ -257,6 +274,26 @@ private:
|
||||
return m_name == c_blackHoleRegister;
|
||||
}
|
||||
|
||||
bool isSelectionRegister() const
|
||||
{
|
||||
return m_name == c_selectionRegister;
|
||||
}
|
||||
|
||||
bool isBlock() const
|
||||
{
|
||||
return m_value.endsWith('\n');
|
||||
}
|
||||
|
||||
// @p_value is the content to update.
|
||||
// If @p_value ends with \n, then it is a block.
|
||||
// When @p_value is a block, we need to add \n at the end if necessary.
|
||||
// If @m_append is true and @p_value is a block, we need to add \n between
|
||||
// them if necessary.
|
||||
void update(const QString &p_value);
|
||||
|
||||
// Read the value of this register.
|
||||
const QString &read();
|
||||
|
||||
QChar m_name;
|
||||
QString m_value;
|
||||
|
||||
@ -287,6 +324,21 @@ private:
|
||||
// @p_tokens is the arguments of the Action::Delete action.
|
||||
void processDeleteAction(QList<Token> &p_tokens);
|
||||
|
||||
// @p_tokens is the arguments of the Action::Copy action.
|
||||
void processCopyAction(QList<Token> &p_tokens);
|
||||
|
||||
// @p_tokens is the arguments of the Action::Paste and Action::PasteBefore action.
|
||||
void processPasteAction(QList<Token> &p_tokens, bool p_pasteBefore);
|
||||
|
||||
// @p_tokens is the arguments of the Action::Change action.
|
||||
void processChangeAction(QList<Token> &p_tokens);
|
||||
|
||||
// @p_tokens is the arguments of the Action::Indent and Action::UnIndent action.
|
||||
void processIndentAction(QList<Token> &p_tokens, bool p_isIndent);
|
||||
|
||||
// @p_tokens is the arguments of the Action::ToLower and Action::ToUpper action.
|
||||
void processToLowerAction(QList<Token> &p_tokens, bool p_toLower);
|
||||
|
||||
// Clear selection if there is any.
|
||||
// Returns true if there is selection.
|
||||
bool clearSelection();
|
||||
@ -331,19 +383,44 @@ private:
|
||||
|
||||
// Delete selected text if there is any.
|
||||
// @p_clearEmptyBlock: whether to remove the empty block after deletion.
|
||||
void deleteSelectedText(bool p_clearEmptyBlock);
|
||||
|
||||
void deleteSelectedText(QTextCursor &p_cursor, bool p_clearEmptyBlock);
|
||||
|
||||
// Save @p_text to the Register pointed by m_register.
|
||||
// Copy selected text if there is any.
|
||||
// Will clear selection.
|
||||
// @p_addNewLine: whether to add a new line \n to the selection.
|
||||
void copySelectedText(bool p_addNewLine);
|
||||
|
||||
void copySelectedText(QTextCursor &p_cursor, bool p_addNewLine);
|
||||
|
||||
// Convert the case of selected text if there is any.
|
||||
// Will clear selection.
|
||||
// @p_toLower: to lower or upper.
|
||||
void convertCaseOfSelectedText(QTextCursor &p_cursor, bool p_toLower);
|
||||
|
||||
// Save @p_text to the Register pointed by m_regName.
|
||||
// Remove QChar::ObjectReplacementCharacter before saving.
|
||||
void saveToRegister(const QString &p_text);
|
||||
|
||||
// Move @p_cursor according to @p_moveMode and @p_movement.
|
||||
// Return true if it has moved @p_cursor.
|
||||
bool processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc,
|
||||
QTextCursor::MoveMode &p_moveMode,
|
||||
QTextCursor::MoveMode p_moveMode,
|
||||
Movement p_movement, int p_repeat);
|
||||
|
||||
// Move @p_cursor according to @p_moveMode and @p_range.
|
||||
// Return true if it has moved @p_cursor.
|
||||
bool selectRange(QTextCursor &p_cursor, const QTextDocument *p_doc,
|
||||
Range p_range, int p_repeat);
|
||||
|
||||
// Check if there is an Action token with Delete/Copy/Change action.
|
||||
bool hasActionTokenValidForTextObject() const;
|
||||
|
||||
// Check if m_keys only contains @p_key.
|
||||
bool checkPendingKey(const Key &p_key) const;
|
||||
|
||||
// Check if m_tokens only contains action token @p_action.
|
||||
bool checkActionToken(Action p_action) const;
|
||||
|
||||
VEdit *m_editor;
|
||||
const VEditConfig *m_editConfig;
|
||||
VimMode m_mode;
|
||||
@ -351,6 +428,7 @@ private:
|
||||
// A valid command token should follow the rule:
|
||||
// Action, Repeat, Movement.
|
||||
// Action, Repeat, Range.
|
||||
// Action, Repeat.
|
||||
QList<Key> m_keys;
|
||||
QList<Token> m_tokens;
|
||||
|
||||
@ -360,7 +438,7 @@ private:
|
||||
QHash<QChar, Register> m_registers;
|
||||
|
||||
// Currently used register.
|
||||
QChar m_register;
|
||||
QChar m_regName;
|
||||
|
||||
static const QChar c_unnamedRegister;
|
||||
static const QChar c_blackHoleRegister;
|
||||
|
@ -363,23 +363,9 @@ bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
|
||||
m_autoIndentPos = -1;
|
||||
cursor.beginEditBlock();
|
||||
// Indent each selected line.
|
||||
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
||||
QTextBlock endBlock = doc->findBlock(cursor.selectionEnd());
|
||||
int endBlockNum = endBlock.blockNumber();
|
||||
while (true) {
|
||||
Q_ASSERT(block.isValid());
|
||||
if (!block.text().isEmpty()) {
|
||||
QTextCursor blockCursor(block);
|
||||
blockCursor.insertText(text);
|
||||
}
|
||||
|
||||
if (block.blockNumber() == endBlockNum) {
|
||||
break;
|
||||
}
|
||||
|
||||
block = block.next();
|
||||
}
|
||||
VEditUtils::indentSelectedBlocks(doc, cursor, text, true);
|
||||
cursor.endEditBlock();
|
||||
m_editor->setTextCursor(cursor);
|
||||
} else {
|
||||
// If it is a Tab key following auto list, increase the indent level.
|
||||
QTextBlock block = cursor.block();
|
||||
@ -417,49 +403,19 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
|
||||
QTextDocument *doc = m_editor->document();
|
||||
QTextCursor cursor = m_editor->textCursor();
|
||||
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
||||
QTextBlock endBlock = doc->findBlock(cursor.selectionEnd());
|
||||
|
||||
bool continueAutoIndent = false;
|
||||
int seq = -1;
|
||||
if (cursor.position() == m_autoIndentPos && isListBlock(block, &seq) &&
|
||||
!cursor.hasSelection()) {
|
||||
continueAutoIndent = true;
|
||||
}
|
||||
int endBlockNum = endBlock.blockNumber();
|
||||
|
||||
cursor.beginEditBlock();
|
||||
if (continueAutoIndent && seq != -1) {
|
||||
changeListBlockSeqNumber(block, 1);
|
||||
}
|
||||
|
||||
for (; block.isValid() && block.blockNumber() <= endBlockNum;
|
||||
block = block.next()) {
|
||||
QTextCursor blockCursor(block);
|
||||
QString text = block.text();
|
||||
if (text.isEmpty()) {
|
||||
continue;
|
||||
} else if (text[0] == '\t') {
|
||||
blockCursor.deleteChar();
|
||||
continue;
|
||||
} else if (text[0] != ' ') {
|
||||
continue;
|
||||
} else {
|
||||
// Spaces.
|
||||
if (m_editConfig->m_expandTab) {
|
||||
int width = m_editConfig->m_tabSpaces.size();
|
||||
for (int i = 0; i < width; ++i) {
|
||||
if (text[i] == ' ') {
|
||||
blockCursor.deleteChar();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
blockCursor.deleteChar();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
VEditUtils::indentSelectedBlocks(doc, cursor, m_editConfig->m_tabSpaces, false);
|
||||
cursor.endEditBlock();
|
||||
|
||||
if (continueAutoIndent) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user