mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
vim-mode: support more text objects for i and a
Now Vim supports word, WORD, '', "", ``, (), [], <>, {} as text object.
This commit is contained in:
parent
4b1e256308
commit
6df4dbe12a
@ -400,3 +400,172 @@ void VEditUtils::deleteIndentAndListMark(QTextCursor &p_cursor)
|
||||
p_cursor.removeSelectedText();
|
||||
}
|
||||
|
||||
|
||||
bool VEditUtils::selectPairTargetAround(QTextCursor &p_cursor,
|
||||
QChar p_opening,
|
||||
QChar p_closing,
|
||||
bool p_inclusive,
|
||||
bool p_crossBlock,
|
||||
int p_repeat)
|
||||
{
|
||||
Q_ASSERT(p_repeat >= 1);
|
||||
|
||||
QTextDocument *doc = p_cursor.document();
|
||||
int pos = p_cursor.position();
|
||||
|
||||
// Search range [start, end].
|
||||
int start = 0;
|
||||
int end = doc->characterCount() - 1;
|
||||
if (!p_crossBlock) {
|
||||
QTextBlock block = p_cursor.block();
|
||||
start = block.position();
|
||||
end = block.position() + block.length() - 1;
|
||||
}
|
||||
|
||||
if (start == end || pos > end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_ASSERT(!doc->characterAt(pos).isNull());
|
||||
|
||||
bool found = false;
|
||||
|
||||
// The number of un-paired symbols before we meet a target.
|
||||
// For example, when we are searching the `(`, nrPair is the number of
|
||||
// the un-paired `)` currently.
|
||||
int nrPair = 0;
|
||||
|
||||
// The absolute position of the found target.
|
||||
// vnote|(vnote|)vnote
|
||||
int opening = pos;
|
||||
int closing = pos;
|
||||
|
||||
round:
|
||||
// "abc|"def", after `di"`, becomes "|"def"
|
||||
// So we need to try closing first.
|
||||
QChar ch = doc->characterAt(closing);
|
||||
Q_ASSERT(!ch.isNull());
|
||||
if (ch == p_closing) {
|
||||
// Try to find the opening.
|
||||
nrPair = 1;
|
||||
int i = opening;
|
||||
if (opening == closing) {
|
||||
--i;
|
||||
}
|
||||
|
||||
for (; i >= start; --i) {
|
||||
ch = doc->characterAt(i);
|
||||
Q_ASSERT(!ch.isNull());
|
||||
if (ch == p_opening) {
|
||||
if (--nrPair == 0) {
|
||||
break;
|
||||
}
|
||||
} else if (ch == p_closing) {
|
||||
++nrPair;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= start) {
|
||||
// Found the opening. Done.
|
||||
opening = i;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
ch = doc->characterAt(opening);
|
||||
Q_ASSERT(!ch.isNull());
|
||||
if (!found && ch == p_opening) {
|
||||
// Try to find the closing.
|
||||
nrPair = 1;
|
||||
int j = closing;
|
||||
if (opening == closing) {
|
||||
++j;
|
||||
}
|
||||
|
||||
for (; j <= end; ++j) {
|
||||
ch = doc->characterAt(j);
|
||||
Q_ASSERT(!ch.isNull());
|
||||
if (ch == p_closing) {
|
||||
if (--nrPair == 0) {
|
||||
break;
|
||||
}
|
||||
} else if (ch == p_opening) {
|
||||
++nrPair;
|
||||
}
|
||||
}
|
||||
|
||||
if (j <= end) {
|
||||
// Foudnd the closing. Done.
|
||||
closing = j;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found
|
||||
&& doc->characterAt(opening) != p_opening
|
||||
&& doc->characterAt(closing) != p_closing) {
|
||||
// Need to find both the opening and closing.
|
||||
int i = opening - 1;
|
||||
int j = closing + 1;
|
||||
// Pretend that we have found one.
|
||||
nrPair = 1;
|
||||
for (; i >= start; --i) {
|
||||
ch = doc->characterAt(i);
|
||||
Q_ASSERT(!ch.isNull());
|
||||
if (ch == p_opening) {
|
||||
if (--nrPair == 0) {
|
||||
break;
|
||||
}
|
||||
} else if (ch == p_closing) {
|
||||
++nrPair;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= start) {
|
||||
opening = i;
|
||||
// Continue to find the closing.
|
||||
nrPair = 1;
|
||||
for (; j <= end; ++j) {
|
||||
ch = doc->characterAt(j);
|
||||
Q_ASSERT(!ch.isNull());
|
||||
if (ch == p_closing) {
|
||||
if (--nrPair == 0) {
|
||||
break;
|
||||
}
|
||||
} else if (ch == p_opening) {
|
||||
++nrPair;
|
||||
}
|
||||
}
|
||||
|
||||
if (j <= end) {
|
||||
closing = j;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return false;
|
||||
} else if (--p_repeat) {
|
||||
// Need to find more.
|
||||
found = false;
|
||||
--opening;
|
||||
++closing;
|
||||
|
||||
if (opening < start && closing > end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
goto round;
|
||||
}
|
||||
|
||||
if (p_inclusive) {
|
||||
++closing;
|
||||
} else {
|
||||
++opening;
|
||||
}
|
||||
|
||||
p_cursor.setPosition(opening, QTextCursor::MoveAnchor);
|
||||
p_cursor.setPosition(closing, QTextCursor::KeepAnchor);
|
||||
return true;
|
||||
}
|
||||
|
@ -74,6 +74,17 @@ public:
|
||||
bool p_inclusive,
|
||||
int p_repeat);
|
||||
|
||||
// Find a pair target (@p_opening, @p_closing) containing current cursor and
|
||||
// select the range between them.
|
||||
// Need to call setTextCursor() to make it take effect.
|
||||
// Returns true if target is found.
|
||||
static bool selectPairTargetAround(QTextCursor &p_cursor,
|
||||
QChar p_opening,
|
||||
QChar p_closing,
|
||||
bool p_inclusive,
|
||||
bool p_crossBlock,
|
||||
int p_repeat);
|
||||
|
||||
// Get the count of blocks selected.
|
||||
static int selectedBlockCount(const QTextCursor &p_cursor);
|
||||
|
||||
|
@ -655,6 +655,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
if (modifiers == Qt::NoModifier) {
|
||||
if (hasActionTokenValidForTextObject()) {
|
||||
// Inner text object.
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (!m_keys.isEmpty()) {
|
||||
// Invalid sequence;
|
||||
break;
|
||||
@ -713,6 +714,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
if (modifiers == Qt::NoModifier) {
|
||||
if (hasActionTokenValidForTextObject()) {
|
||||
// Around text object.
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (!m_keys.isEmpty()) {
|
||||
// Invalid sequence;
|
||||
break;
|
||||
@ -1122,15 +1124,51 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_BracketRight:
|
||||
{
|
||||
if (modifiers == Qt::NoModifier) {
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (checkPendingKey(Key(Qt::Key_I))
|
||||
|| checkPendingKey(Key(Qt::Key_A))) {
|
||||
// BracketInner/BracketAround.
|
||||
Range range = Range::BracketInner;
|
||||
if (checkPendingKey(Key(Qt::Key_A))) {
|
||||
range = Range::BracketAround;
|
||||
}
|
||||
|
||||
addRangeToken(range);
|
||||
processCommand(m_tokens);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Should be kept together with Qt::Key_Escape.
|
||||
case Qt::Key_BracketLeft:
|
||||
{
|
||||
if (isControlModifier(modifiers)) {
|
||||
// fallthrough.
|
||||
} else {
|
||||
} else if (modifiers == Qt::NoModifier) {
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (checkPendingKey(Key(Qt::Key_I))
|
||||
|| checkPendingKey(Key(Qt::Key_A))) {
|
||||
// BracketInner/BracketAround.
|
||||
Range range = Range::BracketInner;
|
||||
if (checkPendingKey(Key(Qt::Key_A))) {
|
||||
range = Range::BracketAround;
|
||||
}
|
||||
|
||||
addRangeToken(range);
|
||||
processCommand(m_tokens);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case Qt::Key_Escape:
|
||||
@ -1197,7 +1235,8 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier) {
|
||||
bool shift = modifiers == Qt::ShiftModifier;
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (checkPendingKey(Key(Qt::Key_I)) || checkPendingKey(Key(Qt::Key_A))) {
|
||||
if (checkPendingKey(Key(Qt::Key_I))
|
||||
|| checkPendingKey(Key(Qt::Key_A))) {
|
||||
// WordInner/WORDInner/WordAournd/WORDAround.
|
||||
bool around = checkPendingKey(Key(Qt::Key_A));
|
||||
Range range = Range::Invalid;
|
||||
@ -1287,13 +1326,24 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
case Qt::Key_QuoteDbl:
|
||||
{
|
||||
if (modifiers == Qt::ShiftModifier) {
|
||||
// Specify a register.
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (!m_keys.isEmpty() || hasActionToken()) {
|
||||
if (checkPendingKey(Key(Qt::Key_I))
|
||||
|| checkPendingKey(Key(Qt::Key_A))) {
|
||||
// DoubleQuoteInner/DoubleQuoteAround.
|
||||
Range range = Range::DoubleQuoteInner;
|
||||
if (checkPendingKey(Key(Qt::Key_A))) {
|
||||
range = Range::DoubleQuoteAround;
|
||||
}
|
||||
|
||||
addRangeToken(range);
|
||||
processCommand(m_tokens);
|
||||
break;
|
||||
} else if (!m_keys.isEmpty() || hasActionToken()) {
|
||||
// Invalid sequence.
|
||||
break;
|
||||
}
|
||||
|
||||
// ", specify a register.
|
||||
m_keys.append(keyInfo);
|
||||
goto accept;
|
||||
}
|
||||
@ -1452,8 +1502,16 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
addRangeToken(Range::Line);
|
||||
processCommand(m_tokens);
|
||||
break;
|
||||
} else {
|
||||
// An invalid sequence.
|
||||
} else if (checkPendingKey(Key(Qt::Key_I))
|
||||
|| checkPendingKey(Key(Qt::Key_A))) {
|
||||
// AngleBracketInner/AngleBracketAround.
|
||||
Range range = Range::AngleBracketInner;
|
||||
if (checkPendingKey(Key(Qt::Key_A))) {
|
||||
range = Range::AngleBracketAround;
|
||||
}
|
||||
|
||||
addRangeToken(range);
|
||||
processCommand(m_tokens);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@ -1464,7 +1522,8 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
cursor,
|
||||
m_editConfig->m_tabSpaces,
|
||||
!unindent);
|
||||
setMode(VimMode::Normal);
|
||||
// Different from Vim:
|
||||
// Do not exit Visual mode after indentation/unindentation.
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1664,10 +1723,22 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
case Qt::Key_Apostrophe:
|
||||
{
|
||||
if (modifiers == Qt::NoModifier) {
|
||||
// ', jump to the start of line of a mark.
|
||||
// Repeat is useless.
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (checkPendingKey(Key(Qt::Key_I))
|
||||
|| checkPendingKey(Key(Qt::Key_A))) {
|
||||
// QuoteInner/QuoteAround.
|
||||
Range range = Range::QuoteInner;
|
||||
if (checkPendingKey(Key(Qt::Key_A))) {
|
||||
range = Range::QuoteAround;
|
||||
}
|
||||
|
||||
addRangeToken(range);
|
||||
processCommand(m_tokens);
|
||||
break;
|
||||
}
|
||||
|
||||
// ', jump to the start of line of a mark.
|
||||
// Repeat is useless in this case.
|
||||
if (m_keys.isEmpty()) {
|
||||
m_keys.append(keyInfo);
|
||||
goto accept;
|
||||
@ -1680,10 +1751,22 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
case Qt::Key_QuoteLeft:
|
||||
{
|
||||
if (modifiers == Qt::NoModifier) {
|
||||
// `, jump to a mark.
|
||||
// Repeat is useless.
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (checkPendingKey(Key(Qt::Key_I))
|
||||
|| checkPendingKey(Key(Qt::Key_A))) {
|
||||
// BackQuoteInner/BackQuoteAround.
|
||||
Range range = Range::BackQuoteInner;
|
||||
if (checkPendingKey(Key(Qt::Key_A))) {
|
||||
range = Range::BackQuoteAround;
|
||||
}
|
||||
|
||||
addRangeToken(range);
|
||||
processCommand(m_tokens);
|
||||
break;
|
||||
}
|
||||
|
||||
// `, jump to a mark.
|
||||
// Repeat is useless in this case.
|
||||
if (m_keys.isEmpty()) {
|
||||
m_keys.append(keyInfo);
|
||||
goto accept;
|
||||
@ -1693,6 +1776,71 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_ParenLeft:
|
||||
// Fall through.
|
||||
case Qt::Key_ParenRight:
|
||||
{
|
||||
if (modifiers == Qt::ShiftModifier) {
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (checkPendingKey(Key(Qt::Key_I))
|
||||
|| checkPendingKey(Key(Qt::Key_A))) {
|
||||
// ParenthesisInner/ParenthesisAround.
|
||||
Range range = Range::ParenthesisInner;
|
||||
if (checkPendingKey(Key(Qt::Key_A))) {
|
||||
range = Range::ParenthesisAround;
|
||||
}
|
||||
|
||||
addRangeToken(range);
|
||||
processCommand(m_tokens);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_BraceLeft:
|
||||
{
|
||||
if (modifiers == Qt::ShiftModifier) {
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (checkPendingKey(Key(Qt::Key_I))
|
||||
|| checkPendingKey(Key(Qt::Key_A))) {
|
||||
// BraceInner/BraceAround.
|
||||
Range range = Range::BraceInner;
|
||||
if (checkPendingKey(Key(Qt::Key_A))) {
|
||||
range = Range::BraceAround;
|
||||
}
|
||||
|
||||
addRangeToken(range);
|
||||
processCommand(m_tokens);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_BraceRight:
|
||||
{
|
||||
if (modifiers == Qt::ShiftModifier) {
|
||||
tryGetRepeatToken(m_keys, m_tokens);
|
||||
if (checkPendingKey(Key(Qt::Key_I))
|
||||
|| checkPendingKey(Key(Qt::Key_A))) {
|
||||
// BraceInner/BraceAround.
|
||||
Range range = Range::BraceInner;
|
||||
if (checkPendingKey(Key(Qt::Key_A))) {
|
||||
range = Range::BraceAround;
|
||||
}
|
||||
|
||||
addRangeToken(range);
|
||||
processCommand(m_tokens);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2426,6 +2574,10 @@ bool VVim::selectRange(QTextCursor &p_cursor, const QTextDocument *p_doc,
|
||||
bool hasMoved = false;
|
||||
QTextCursor::MoveMode moveMode = QTextCursor::KeepAnchor;
|
||||
bool around = false;
|
||||
QChar opening;
|
||||
QChar closing;
|
||||
bool crossBlock = false;
|
||||
bool multipleTargets = false;
|
||||
|
||||
Q_UNUSED(p_doc);
|
||||
|
||||
@ -2528,6 +2680,163 @@ bool VVim::selectRange(QTextCursor &p_cursor, const QTextDocument *p_doc,
|
||||
break;
|
||||
}
|
||||
|
||||
case Range::ParenthesisAround:
|
||||
{
|
||||
around = true;
|
||||
opening = '(';
|
||||
closing = ')';
|
||||
crossBlock = true;
|
||||
multipleTargets = true;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::ParenthesisInner:
|
||||
{
|
||||
around = false;
|
||||
opening = '(';
|
||||
closing = ')';
|
||||
crossBlock = true;
|
||||
multipleTargets = true;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::BracketAround:
|
||||
{
|
||||
around = true;
|
||||
opening = '[';
|
||||
closing = ']';
|
||||
crossBlock = true;
|
||||
multipleTargets = true;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::BracketInner:
|
||||
{
|
||||
around = false;
|
||||
opening = '[';
|
||||
closing = ']';
|
||||
crossBlock = true;
|
||||
multipleTargets = true;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::AngleBracketAround:
|
||||
{
|
||||
around = true;
|
||||
opening = '<';
|
||||
closing = '>';
|
||||
crossBlock = true;
|
||||
multipleTargets = true;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::AngleBracketInner:
|
||||
{
|
||||
around = false;
|
||||
opening = '<';
|
||||
closing = '>';
|
||||
crossBlock = true;
|
||||
multipleTargets = true;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::BraceAround:
|
||||
{
|
||||
around = true;
|
||||
opening = '{';
|
||||
closing = '}';
|
||||
crossBlock = true;
|
||||
multipleTargets = true;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::BraceInner:
|
||||
{
|
||||
around = false;
|
||||
opening = '{';
|
||||
closing = '}';
|
||||
crossBlock = true;
|
||||
multipleTargets = true;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::DoubleQuoteAround:
|
||||
{
|
||||
around = true;
|
||||
opening = '"';
|
||||
closing = '"';
|
||||
crossBlock = false;
|
||||
multipleTargets = false;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::DoubleQuoteInner:
|
||||
{
|
||||
around = false;
|
||||
opening = '"';
|
||||
closing = '"';
|
||||
crossBlock = false;
|
||||
multipleTargets = false;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::BackQuoteAround:
|
||||
{
|
||||
around = true;
|
||||
opening = '`';
|
||||
closing = '`';
|
||||
crossBlock = false;
|
||||
multipleTargets = false;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::BackQuoteInner:
|
||||
{
|
||||
around = false;
|
||||
opening = '`';
|
||||
closing = '`';
|
||||
crossBlock = false;
|
||||
multipleTargets = false;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::QuoteAround:
|
||||
{
|
||||
around = true;
|
||||
opening = '\'';
|
||||
closing = '\'';
|
||||
crossBlock = false;
|
||||
multipleTargets = false;
|
||||
goto handlePairTarget;
|
||||
}
|
||||
|
||||
case Range::QuoteInner:
|
||||
{
|
||||
around = false;
|
||||
opening = '\'';
|
||||
closing = '\'';
|
||||
crossBlock = false;
|
||||
multipleTargets = false;
|
||||
|
||||
handlePairTarget:
|
||||
|
||||
if (p_repeat == -1) {
|
||||
p_repeat = 1;
|
||||
} else if (p_repeat > 1 && !multipleTargets) {
|
||||
// According to the behavior of Vim.
|
||||
p_repeat = 1;
|
||||
around = true;
|
||||
}
|
||||
|
||||
hasMoved = VEditUtils::selectPairTargetAround(p_cursor,
|
||||
opening,
|
||||
closing,
|
||||
around,
|
||||
crossBlock,
|
||||
p_repeat);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2557,8 +2866,10 @@ void VVim::processDeleteAction(QList<Token> &p_tokens)
|
||||
if (to.isRange()) {
|
||||
cursor.beginEditBlock();
|
||||
hasMoved = selectRange(cursor, doc, to.m_range, repeat);
|
||||
bool around = false;
|
||||
if (hasMoved) {
|
||||
// Whether the range may cross blocks.
|
||||
bool mayCrossBlock = false;
|
||||
|
||||
switch (to.m_range) {
|
||||
case Range::Line:
|
||||
{
|
||||
@ -2575,34 +2886,69 @@ void VVim::processDeleteAction(QList<Token> &p_tokens)
|
||||
|
||||
message(tr("%1 fewer %2").arg(repeat).arg(repeat > 1 ? tr("lines")
|
||||
: tr("line")));
|
||||
|
||||
qDebug() << "delete" << repeat << "lines";
|
||||
break;
|
||||
}
|
||||
|
||||
case Range::ParenthesisInner:
|
||||
// Fall through.
|
||||
case Range::ParenthesisAround:
|
||||
// Fall through.
|
||||
case Range::BracketInner:
|
||||
// Fall through.
|
||||
case Range::BracketAround:
|
||||
// Fall through.
|
||||
case Range::AngleBracketInner:
|
||||
// Fall through.
|
||||
case Range::AngleBracketAround:
|
||||
// Fall through.
|
||||
case Range::BraceInner:
|
||||
// Fall through.
|
||||
case Range::BraceAround:
|
||||
// Fall through.
|
||||
mayCrossBlock = true;
|
||||
|
||||
case Range::WordAround:
|
||||
around = true;
|
||||
// Fall through.
|
||||
case Range::WordInner:
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
deleteSelectedText(cursor, false);
|
||||
}
|
||||
|
||||
qDebug() << "delete" << (around ? "around" : "inner") << "word";
|
||||
break;
|
||||
}
|
||||
|
||||
// Fall through.
|
||||
case Range::WORDAround:
|
||||
around = true;
|
||||
// Fall through.
|
||||
case Range::WORDInner:
|
||||
// Fall through.
|
||||
case Range::QuoteInner:
|
||||
// Fall through.
|
||||
case Range::QuoteAround:
|
||||
// Fall through.
|
||||
case Range::DoubleQuoteInner:
|
||||
// Fall through.
|
||||
case Range::DoubleQuoteAround:
|
||||
// Fall through.
|
||||
case Range::BackQuoteInner:
|
||||
// Fall through.
|
||||
case Range::BackQuoteAround:
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
deleteSelectedText(cursor, false);
|
||||
bool clearEmptyBlock = false;
|
||||
if (mayCrossBlock
|
||||
&& VEditUtils::selectedBlockCount(cursor) > 1) {
|
||||
clearEmptyBlock = true;
|
||||
}
|
||||
|
||||
int blockCount = 0;
|
||||
if (clearEmptyBlock) {
|
||||
blockCount = doc->blockCount();
|
||||
}
|
||||
|
||||
deleteSelectedText(cursor, clearEmptyBlock);
|
||||
|
||||
if (clearEmptyBlock) {
|
||||
int nrBlock = blockCount - doc->blockCount();
|
||||
Q_ASSERT(nrBlock > 0);
|
||||
message(tr("%1 fewer %2").arg(nrBlock).arg(nrBlock > 1 ? tr("lines")
|
||||
: tr("line")));
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "delete" << (around ? "around" : "inner") << "WORD";
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2733,8 +3079,10 @@ void VVim::processCopyAction(QList<Token> &p_tokens)
|
||||
if (to.isRange()) {
|
||||
cursor.beginEditBlock();
|
||||
changed = selectRange(cursor, doc, to.m_range, repeat);
|
||||
bool around = false;
|
||||
if (changed) {
|
||||
// Whether the range may cross blocks.
|
||||
bool mayCrossBlock = false;
|
||||
|
||||
switch (to.m_range) {
|
||||
case Range::Line:
|
||||
{
|
||||
@ -2751,34 +3099,63 @@ void VVim::processCopyAction(QList<Token> &p_tokens)
|
||||
|
||||
message(tr("%1 %2 yanked").arg(repeat).arg(repeat > 1 ? tr("lines")
|
||||
: tr("line")));
|
||||
|
||||
qDebug() << "copy" << repeat << "lines";
|
||||
break;
|
||||
}
|
||||
|
||||
case Range::ParenthesisInner:
|
||||
// Fall through.
|
||||
case Range::ParenthesisAround:
|
||||
// Fall through.
|
||||
case Range::BracketInner:
|
||||
// Fall through.
|
||||
case Range::BracketAround:
|
||||
// Fall through.
|
||||
case Range::AngleBracketInner:
|
||||
// Fall through.
|
||||
case Range::AngleBracketAround:
|
||||
// Fall through.
|
||||
case Range::BraceInner:
|
||||
// Fall through.
|
||||
case Range::BraceAround:
|
||||
// Fall through.
|
||||
mayCrossBlock = true;
|
||||
|
||||
case Range::WordAround:
|
||||
around = true;
|
||||
// Fall through.
|
||||
case Range::WordInner:
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
copySelectedText(cursor, false);
|
||||
}
|
||||
|
||||
qDebug() << "copy" << (around ? "around" : "inner") << "word";
|
||||
break;
|
||||
}
|
||||
|
||||
// Fall through.
|
||||
case Range::WORDAround:
|
||||
around = true;
|
||||
// Fall through.
|
||||
case Range::WORDInner:
|
||||
// Fall through.
|
||||
case Range::QuoteInner:
|
||||
// Fall through.
|
||||
case Range::QuoteAround:
|
||||
// Fall through.
|
||||
case Range::DoubleQuoteInner:
|
||||
// Fall through.
|
||||
case Range::DoubleQuoteAround:
|
||||
// Fall through.
|
||||
case Range::BackQuoteInner:
|
||||
// Fall through.
|
||||
case Range::BackQuoteAround:
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
copySelectedText(cursor, false);
|
||||
bool multipleBlocks = false;
|
||||
int nrBlock = VEditUtils::selectedBlockCount(cursor);
|
||||
if (mayCrossBlock && nrBlock > 1) {
|
||||
multipleBlocks = true;
|
||||
}
|
||||
|
||||
// No need to add new line even crossing multiple blocks.
|
||||
copySelectedText(cursor, false);
|
||||
|
||||
if (multipleBlocks) {
|
||||
message(tr("%1 %2 yanked").arg(nrBlock).arg(nrBlock > 1 ? tr("lines")
|
||||
: tr("line")));
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "copy" << (around ? "around" : "inner") << "WORD";
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2798,7 +3175,7 @@ void VVim::processCopyAction(QList<Token> &p_tokens)
|
||||
|
||||
V_ASSERT(to.isMovement());
|
||||
|
||||
// Filter out not supported movement for DELETE action.
|
||||
// Filter out not supported movement for Copy action.
|
||||
switch (to.m_movement) {
|
||||
case Movement::PageUp:
|
||||
case Movement::PageDown:
|
||||
@ -2973,17 +3350,13 @@ void VVim::processChangeAction(QList<Token> &p_tokens)
|
||||
if (to.isRange()) {
|
||||
cursor.beginEditBlock();
|
||||
hasMoved = selectRange(cursor, doc, to.m_range, repeat);
|
||||
bool around = false;
|
||||
if (hasMoved) {
|
||||
int pos = cursor.selectionStart();
|
||||
|
||||
switch (to.m_range) {
|
||||
case Range::Line:
|
||||
{
|
||||
// cc, change current line.
|
||||
if (repeat == -1) {
|
||||
repeat = 1;
|
||||
}
|
||||
|
||||
if (cursor.hasSelection()) {
|
||||
deleteSelectedText(cursor, true);
|
||||
insertChangeBlockAfterDeletion(cursor, pos);
|
||||
@ -2991,37 +3364,49 @@ void VVim::processChangeAction(QList<Token> &p_tokens)
|
||||
saveToRegister("\n");
|
||||
}
|
||||
|
||||
qDebug() << "change" << repeat << "lines";
|
||||
break;
|
||||
}
|
||||
|
||||
case Range::ParenthesisInner:
|
||||
// Fall through.
|
||||
case Range::ParenthesisAround:
|
||||
// Fall through.
|
||||
case Range::BracketInner:
|
||||
// Fall through.
|
||||
case Range::BracketAround:
|
||||
// Fall through.
|
||||
case Range::AngleBracketInner:
|
||||
// Fall through.
|
||||
case Range::AngleBracketAround:
|
||||
// Fall through.
|
||||
case Range::BraceInner:
|
||||
// Fall through.
|
||||
case Range::BraceAround:
|
||||
// Fall through.
|
||||
case Range::WordAround:
|
||||
around = true;
|
||||
// Fall through.
|
||||
case Range::WordInner:
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
deleteSelectedText(cursor, false);
|
||||
} else {
|
||||
saveToRegister("\n");
|
||||
}
|
||||
|
||||
qDebug() << "delete" << (around ? "around" : "inner") << "word";
|
||||
break;
|
||||
}
|
||||
|
||||
// Fall through.
|
||||
case Range::WORDAround:
|
||||
around = true;
|
||||
// Fall through.
|
||||
case Range::WORDInner:
|
||||
// Fall through.
|
||||
case Range::QuoteInner:
|
||||
// Fall through.
|
||||
case Range::QuoteAround:
|
||||
// Fall through.
|
||||
case Range::DoubleQuoteInner:
|
||||
// Fall through.
|
||||
case Range::DoubleQuoteAround:
|
||||
// Fall through.
|
||||
case Range::BackQuoteInner:
|
||||
// Fall through.
|
||||
case Range::BackQuoteAround:
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
deleteSelectedText(cursor, false);
|
||||
} else {
|
||||
saveToRegister("\n");
|
||||
}
|
||||
|
||||
qDebug() << "delete" << (around ? "around" : "inner") << "WORD";
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3241,7 +3626,8 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
|
||||
QTextDocument *doc = m_editor->document();
|
||||
|
||||
if (to.isRange()) {
|
||||
selectRange(cursor, doc, to.m_range, repeat);
|
||||
bool changed = selectRange(cursor, doc, to.m_range, repeat);
|
||||
if (changed) {
|
||||
switch (to.m_range) {
|
||||
case Range::Line:
|
||||
{
|
||||
@ -3266,6 +3652,22 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
|
||||
break;
|
||||
}
|
||||
|
||||
case Range::ParenthesisInner:
|
||||
// Fall through.
|
||||
case Range::ParenthesisAround:
|
||||
// Fall through.
|
||||
case Range::BracketInner:
|
||||
// Fall through.
|
||||
case Range::BracketAround:
|
||||
// Fall through.
|
||||
case Range::AngleBracketInner:
|
||||
// Fall through.
|
||||
case Range::AngleBracketAround:
|
||||
// Fall through.
|
||||
case Range::BraceInner:
|
||||
// Fall through.
|
||||
case Range::BraceAround:
|
||||
// Fall through.
|
||||
case Range::WordAround:
|
||||
// Fall through.
|
||||
case Range::WordInner:
|
||||
@ -3273,18 +3675,40 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
|
||||
case Range::WORDAround:
|
||||
// Fall through.
|
||||
case Range::WORDInner:
|
||||
// Fall through.
|
||||
case Range::QuoteInner:
|
||||
// Fall through.
|
||||
case Range::QuoteAround:
|
||||
// Fall through.
|
||||
case Range::DoubleQuoteInner:
|
||||
// Fall through.
|
||||
case Range::DoubleQuoteAround:
|
||||
// Fall through.
|
||||
case Range::BackQuoteInner:
|
||||
// Fall through.
|
||||
case Range::BackQuoteAround:
|
||||
{
|
||||
cursor.clearSelection();
|
||||
int nrBlock = VEditUtils::selectedBlockCount(cursor);
|
||||
VEditUtils::indentSelectedBlocks(doc,
|
||||
cursor,
|
||||
m_editConfig->m_tabSpaces,
|
||||
p_isIndent);
|
||||
|
||||
if (p_isIndent) {
|
||||
message(tr("%1 %2 >ed 1 time").arg(nrBlock).arg(nrBlock > 1 ? tr("lines")
|
||||
: tr("line")));
|
||||
} else {
|
||||
message(tr("%1 %2 <ed 1 time").arg(nrBlock).arg(nrBlock > 1 ? tr("lines")
|
||||
: tr("line")));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@ -3349,12 +3773,11 @@ void VVim::processToLowerAction(QList<Token> &p_tokens, bool p_toLower)
|
||||
changed = selectRange(cursor, doc, to.m_range, repeat);
|
||||
if (changed) {
|
||||
oriPos = cursor.selectionStart();
|
||||
convertCaseOfSelectedText(cursor, p_toLower);
|
||||
int nrBlock = VEditUtils::selectedBlockCount(cursor);
|
||||
message(tr("%1 %2 changed").arg(nrBlock)
|
||||
.arg(nrBlock > 1 ? tr("lines") : tr("line")));
|
||||
|
||||
if (to.m_range == Range::Line) {
|
||||
message(tr("%1 %2 changed").arg(repeat == -1 ? 1 : repeat)
|
||||
.arg(repeat > 1 ? tr("lines") : tr("line")));
|
||||
}
|
||||
convertCaseOfSelectedText(cursor, p_toLower);
|
||||
|
||||
cursor.setPosition(oriPos);
|
||||
}
|
||||
@ -3389,40 +3812,34 @@ void VVim::processToLowerAction(QList<Token> &p_tokens, bool p_toLower)
|
||||
|
||||
if (changed) {
|
||||
oriPos = cursor.selectionStart();
|
||||
bool isBlock = false;
|
||||
|
||||
switch (to.m_movement) {
|
||||
case Movement::Up:
|
||||
{
|
||||
isBlock = true;
|
||||
expandSelectionToWholeLines(cursor);
|
||||
break;
|
||||
}
|
||||
|
||||
case Movement::Down:
|
||||
{
|
||||
isBlock = true;
|
||||
expandSelectionToWholeLines(cursor);
|
||||
break;
|
||||
}
|
||||
|
||||
case Movement::LineJump:
|
||||
{
|
||||
isBlock = true;
|
||||
expandSelectionToWholeLines(cursor);
|
||||
break;
|
||||
}
|
||||
|
||||
case Movement::StartOfDocument:
|
||||
{
|
||||
isBlock = true;
|
||||
expandSelectionToWholeLines(cursor);
|
||||
break;
|
||||
}
|
||||
|
||||
case Movement::EndOfDocument:
|
||||
{
|
||||
isBlock = true;
|
||||
expandSelectionToWholeLines(cursor);
|
||||
break;
|
||||
}
|
||||
@ -3431,11 +3848,9 @@ void VVim::processToLowerAction(QList<Token> &p_tokens, bool p_toLower)
|
||||
break;
|
||||
}
|
||||
|
||||
if (isBlock) {
|
||||
int nrBlock = VEditUtils::selectedBlockCount(cursor);
|
||||
message(tr("%1 %2 changed").arg(nrBlock).arg(nrBlock > 1 ? tr("lines")
|
||||
: tr("line")));
|
||||
}
|
||||
|
||||
convertCaseOfSelectedText(cursor, p_toLower);
|
||||
|
||||
@ -3741,7 +4156,9 @@ bool VVim::hasActionTokenValidForTextObject() const
|
||||
|| act == Action::Copy
|
||||
|| act == Action::Change
|
||||
|| act == Action::ToLower
|
||||
|| act == Action::ToUpper) {
|
||||
|| act == Action::ToUpper
|
||||
|| act == Action::Indent
|
||||
|| act == Action::UnIndent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -313,6 +313,8 @@ private:
|
||||
QuoteAround,
|
||||
DoubleQuoteInner,
|
||||
DoubleQuoteAround,
|
||||
BackQuoteInner,
|
||||
BackQuoteAround,
|
||||
ParenthesisInner,
|
||||
ParenthesisAround,
|
||||
BracketInner,
|
||||
|
Loading…
x
Reference in New Issue
Block a user