FakeVim: Select and modify paragraph object

Change-Id: Ib528fa2914bfcb17caed114d7da2d201079b0725
Reviewed-by: hjk <hjk121@nokiamail.com>
This commit is contained in:
Lukas Holecek
2014-11-20 19:57:16 +01:00
committed by hjk
parent e8aecca215
commit 577fcd97d4
3 changed files with 558 additions and 16 deletions

View File

@@ -1576,6 +1576,432 @@ void FakeVimPlugin::test_vim_block_selection_insert()
);
}
void FakeVimPlugin::test_vim_delete_inner_paragraph()
{
TestData data;
setup(&data);
data.setText(
"abc" N
"def" N
"" N
"" N
"ghi" N
"" N
"jkl" N
);
KEYS("dip",
X "" N
"" N
"ghi" N
"" N
"jkl" N
);
KEYS("dip",
X "ghi" N
"" N
"jkl" N
);
KEYS("2dip",
X "jkl" N
);
}
void FakeVimPlugin::test_vim_delete_a_paragraph()
{
TestData data;
setup(&data);
data.setText(
"abc" N
"def" N
"" N
"" N
"ghi" N
"" N
"jkl" N
);
KEYS("dap",
X "ghi" N
"" N
"jkl" N
);
KEYS("dap",
X "jkl" N
);
KEYS("u",
X "ghi" N
"" N
"jkl" N
);
data.setText(
"abc" N
"" N
"" N
"def"
);
KEYS("Gdap",
X "abc"
);
}
void FakeVimPlugin::test_vim_change_inner_paragraph()
{
TestData data;
setup(&data);
data.setText(
"abc" N
"def" N
"" N
"" N
"ghi" N
"" N
"jkl" N
);
KEYS("cipXXX<ESC>",
"XX" X "X" N
"" N
"" N
"ghi" N
"" N
"jkl" N
);
KEYS("3j" "cipYYY<ESC>",
"XXX" N
"" N
"" N
"YY" X "Y" N
"" N
"jkl" N
);
}
void FakeVimPlugin::test_vim_change_a_paragraph()
{
TestData data;
setup(&data);
data.setText(
"abc" N
"def" N
"" N
"" N
"ghi" N
"" N
"jkl" N
);
KEYS("4j" "capXXX<ESC>",
"abc" N
"def" N
"" N
"" N
"XX" X "X" N
"jkl" N
);
KEYS("gg" "capYYY<ESC>",
"YY" X "Y" N
"XXX" N
"jkl" N
);
data.setText(
"abc" N
"" N
"" N
"def"
);
KEYS("GcapXXX<ESC>",
"abc" N
"XX" X "X"
);
}
void FakeVimPlugin::test_vim_select_inner_paragraph()
{
TestData data;
setup(&data);
data.setText(
"" N
X "abc" N
"def" N
"" N
"ghi"
);
KEYS("vip" "r-",
"" N
X "---" N
"---" N
"" N
"ghi"
);
data.setText(
"" N
X "abc" N
"def" N
"" N
"ghi"
);
KEYS("vip" ":s/^/-<CR>",
"" N
"-abc" N
X "-def" N
"" N
"ghi"
);
data.setText(
"" N
X "abc" N
"def" N
"" N
"ghi"
);
KEYS("v2ip" ":s/^/-<CR>",
"" N
"-abc" N
"-def" N
X "-" N
"ghi"
);
data.setText(
"" N
X "abc" N
"def" N
"" N
"ghi"
);
KEYS("Vj" "ip" ":s/^/-<CR>",
"" N
"-abc" N
"-def" N
X "-" N
"ghi"
);
data.setText(
"" N
X "abc" N
"def" N
"" N
"ghi"
);
KEYS("vj" "ip" ":s/^/-<CR>",
"" N
"-abc" N
"-def" N
"-" N
"ghi"
);
data.setText(
"" N
X "abc" N
"def" N
"ghi" N
"" N
"jkl"
);
KEYS("vj" "ip" ":s/^/-<CR>",
"" N
"-abc" N
"-def" N
"-ghi" N
"" N
"jkl"
);
data.setText(
"" N
X "abc" N
"def" N
"" N
"ghi"
);
KEYS("vip" "r-",
"" N
X "---" N
"---" N
"" N
"ghi"
);
data.setText(
"abc" N
"" N
"def"
);
KEYS("G" "vip" "r-",
"abc" N
"" N
"---"
);
data.setText(
"" N
"" N
"ghi"
);
KEYS("vip" ":s/^/-<CR>",
"-" N
"-" N
"ghi"
);
data.setText(
"" N
"ghi"
);
KEYS("vip" "ip" ":s/^/-<CR>",
"-" N
X "-ghi"
);
data.setText(
"abc" N
"" N
""
);
KEYS("j" "vip" ":s/^/-<CR>",
"abc" N
"-" N
"-"
);
// Don't move anchor if it's on different line.
data.setText(
"" N
"abc" N
X "def" N
"ghi" N
"" N
"jkl"
);
KEYS("vj" "ip" ":s/^/-<CR>",
"" N
"abc" N
"-def" N
"-ghi" N
X "-" N
"jkl"
);
// Don't change selection mode if anchor is on different line.
data.setText(
"" N
"abc" N
X "def" N
"ghi" N
"" N
"jkl"
);
KEYS("vj" "2ip" "r-",
"" N
"abc" N
X "---" N
"---" N
"" N
"-kl"
);
KEYS("gv" ":s/^/X<CR>",
"" N
"abc" N
"X---" N
"X---" N
"X" N
X "X-kl"
);
data.setText(
"" N
"abc" N
X "def" N
"ghi" N
"" N
"jkl"
);
KEYS("<C-V>j" "2ip" "r-",
"" N
"abc" N
X "-ef" N
"-hi" N
"" N
"-kl"
);
KEYS("gv" "IX<ESC>",
"" N
"abc" N
"X-ef" N
"X-hi" N
"X" N
"X-kl"
);
}
void FakeVimPlugin::test_vim_select_a_paragraph()
{
TestData data;
setup(&data);
data.setText(
"abc" N
"def" N
"" N
"ghi"
);
KEYS("vap" ":s/^/-<CR>",
"-abc" N
"-def" N
"-" N
"ghi"
);
data.setText(
"" N
"abc" N
"def" N
"" N
"ghi"
);
KEYS("vap" ":s/^/-<CR>",
"-" N
"-abc" N
"-def" N
"" N
"ghi"
);
data.setText(
"abc" N
"def" N
""
);
KEYS("j" "vap" ":s/^/-<CR>",
"-abc" N
"-def" N
"-"
);
data.setText(
"" N
"abc" N
"def"
);
KEYS("j" "vap" ":s/^/-<CR>",
"-" N
"-abc" N
"-def"
);
}
void FakeVimPlugin::test_vim_repeat()
{
TestData data;

View File

@@ -1776,6 +1776,7 @@ public:
int lineNumber(const QTextBlock &block) const;
int columnAt(int pos) const;
int blockNumberAt(int pos) const;
QTextBlock blockAt(int pos) const;
QTextBlock nextLine(const QTextBlock &block) const; // following line (respects wrapped parts)
QTextBlock previousLine(const QTextBlock &block) const; // previous line (respects wrapped parts)
@@ -1916,8 +1917,9 @@ public:
QTextCursor m_cursor;
bool m_cursorNeedsUpdate;
bool moveToPreviousParagraph(int count) { return moveToNextParagraph(-count); }
bool moveToNextParagraph(int count);
bool moveToPreviousParagraph(int count = 1) { return moveToNextParagraph(-count); }
bool moveToNextParagraph(int count = 1);
void moveToParagraphStartOrEnd(int direction = 1);
bool handleFfTt(const QString &key, bool repeats = false);
@@ -3197,7 +3199,6 @@ bool FakeVimHandler::Private::moveToNextParagraph(int count)
{
const bool forward = count > 0;
int repeat = forward ? count : -count;
int pos = position();
QTextBlock block = this->block();
if (block.isValid() && block.length() == 1)
@@ -3209,23 +3210,39 @@ bool FakeVimHandler::Private::moveToNextParagraph(int count)
break;
while (block.isValid() && block.length() == 1)
block = forward ? block.next() : block.previous();
if (!block.isValid())
break;
}
}
if (repeat == 0)
setPosition(block.position());
else if (repeat == 1)
setPosition(forward ? lastPositionInDocument() : 0);
else
if (!block.isValid())
--repeat;
if (repeat > 0)
return false;
recordJump(pos);
setTargetColumn();
g.movetype = MoveExclusive;
if (block.isValid())
setPosition(block.position());
else
setPosition(forward ? lastPositionInDocument() : 0);
return true;
}
void FakeVimHandler::Private::moveToParagraphStartOrEnd(int direction)
{
bool emptyLine = atEmptyLine();
int oldPos = -1;
while (atEmptyLine() == emptyLine && oldPos != position()) {
oldPos = position();
moveDown(direction);
}
if (oldPos != position())
moveUp(direction);
}
void FakeVimHandler::Private::moveToEndOfLine()
{
// Additionally select (in visual mode) or apply current command on hidden lines following
@@ -3836,10 +3853,16 @@ bool FakeVimHandler::Private::handleMovement(const Input &input)
moveRight(qMin(column, rightDist() - 1));
m_targetColumn = column;
m_visualTargetColumn = column;
} else if (input.is('}')) {
handled = moveToNextParagraph(count);
} else if (input.is('{')) {
handled = moveToPreviousParagraph(count);
} else if (input.is('{') || input.is('}')) {
const int oldPosition = position();
handled = input.is('}')
? moveToNextParagraph(count)
: moveToPreviousParagraph(count);
if (handled) {
recordJump(oldPosition);
setTargetColumn();
g.movetype = MoveExclusive;
}
} else if (input.isReturn()) {
moveToStartOfLine();
moveDown();
@@ -7333,6 +7356,11 @@ int FakeVimHandler::Private::columnAt(int pos) const
return pos - blockAt(pos).position();
}
int FakeVimHandler::Private::blockNumberAt(int pos) const
{
return blockAt(pos).blockNumber();
}
QTextBlock FakeVimHandler::Private::blockAt(int pos) const
{
return document()->findBlock(pos);
@@ -8044,7 +8072,81 @@ void FakeVimHandler::Private::selectSentenceTextObject(bool inner)
void FakeVimHandler::Private::selectParagraphTextObject(bool inner)
{
Q_UNUSED(inner);
const QTextCursor oldCursor = m_cursor;
const VisualMode oldVisualMode = g.visualMode;
const int anchorBlock = blockNumberAt(anchor());
const int positionBlock = blockNumberAt(position());
const bool setupAnchor = anchorBlock == positionBlock;
int repeat = count();
// If anchor and position are in the same block,
// start line selection at beginning of current paragraph.
if (setupAnchor) {
moveToParagraphStartOrEnd(-1);
setAnchor();
if (!isVisualLineMode() && isVisualMode())
toggleVisualMode(VisualLineMode);
}
const bool forward = anchor() <= position();
const int d = forward ? 1 : -1;
bool startsAtParagraph = !atEmptyLine(position());
moveToParagraphStartOrEnd(d);
// If selection already changed, decreate count.
if ((setupAnchor && g.submode != NoSubMode)
|| oldVisualMode != g.visualMode
|| m_cursor != oldCursor)
{
--repeat;
if (!inner) {
moveDown(d);
moveToParagraphStartOrEnd(d);
startsAtParagraph = !startsAtParagraph;
}
}
if (repeat > 0) {
bool isCountEven = repeat % 2 == 0;
bool endsOnParagraph =
inner ? isCountEven == startsAtParagraph : startsAtParagraph;
if (inner) {
repeat = repeat / 2;
if (!isCountEven || endsOnParagraph)
++repeat;
} else {
if (endsOnParagraph)
++repeat;
}
if (!moveToNextParagraph(d * repeat)) {
m_cursor = oldCursor;
g.visualMode = oldVisualMode;
return;
}
if (endsOnParagraph && atEmptyLine())
moveUp(d);
else
moveToParagraphStartOrEnd(d);
}
if (!inner && setupAnchor && !atEmptyLine() && !atEmptyLine(anchor())) {
// If position cannot select empty lines, try to select them with anchor.
setAnchorAndPosition(position(), anchor());
moveToNextParagraph(-d);
moveToParagraphStartOrEnd(-d);
setAnchorAndPosition(position(), anchor());
}
recordJump(oldCursor.position());
setTargetColumn();
g.movetype = MoveLineWise;
}
bool FakeVimHandler::Private::selectBlockTextObject(bool inner,

View File

@@ -61,23 +61,37 @@ private:
#ifdef WITH_TESTS
private slots:
void cleanup();
void test_vim_movement();
void test_vim_target_column_normal();
void test_vim_target_column_visual_char();
void test_vim_target_column_visual_block();
void test_vim_target_column_visual_line();
void test_vim_target_column_insert();
void test_vim_target_column_replace();
void test_vim_insert();
void test_vim_fFtT();
void test_vim_transform_numbers();
void test_vim_delete();
void test_vim_delete_inner_word();
void test_vim_delete_a_word();
void test_vim_change_a_word();
void test_vim_change_replace();
void test_vim_block_selection();
void test_vim_block_selection_insert();
void test_vim_delete_inner_paragraph();
void test_vim_delete_a_paragraph();
void test_vim_change_inner_paragraph();
void test_vim_change_a_paragraph();
void test_vim_select_inner_paragraph();
void test_vim_select_a_paragraph();
void test_vim_repeat();
void test_vim_search();
void test_vim_indent();