aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukas Holecek <hluk@email.cz>2014-11-20 19:57:16 +0100
committerhjk <hjk121@nokiamail.com>2014-11-25 10:32:12 +0100
commit577fcd97d4f0ae6dd944d1587b2c4ed0ac2c66c9 (patch)
tree894c662299aa21fadfa345743cd67f5403b6a680
parente8aecca215fdc5c01507e06e0a26e09eab6b4655 (diff)
FakeVim: Select and modify paragraph object
Change-Id: Ib528fa2914bfcb17caed114d7da2d201079b0725 Reviewed-by: hjk <hjk121@nokiamail.com>
-rw-r--r--src/plugins/fakevim/fakevim_test.cpp426
-rw-r--r--src/plugins/fakevim/fakevimhandler.cpp134
-rw-r--r--src/plugins/fakevim/fakevimplugin.h14
3 files changed, 558 insertions, 16 deletions
diff --git a/src/plugins/fakevim/fakevim_test.cpp b/src/plugins/fakevim/fakevim_test.cpp
index e012b04969a..cd1ed10b1f8 100644
--- a/src/plugins/fakevim/fakevim_test.cpp
+++ b/src/plugins/fakevim/fakevim_test.cpp
@@ -1573,6 +1573,432 @@ void FakeVimPlugin::test_vim_block_selection_insert()
"a" X "bXYZc" N
" XYZ" N
"deXYZf" N
+ );
+}
+
+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"
);
}
diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index 7376a2166cc..ec00465840b 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -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,
diff --git a/src/plugins/fakevim/fakevimplugin.h b/src/plugins/fakevim/fakevimplugin.h
index b8cd0d246a2..620cf6b7d0a 100644
--- a/src/plugins/fakevim/fakevimplugin.h
+++ b/src/plugins/fakevim/fakevimplugin.h
@@ -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();