aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté <mikisabate@gmail.com>2014-01-19 22:44:53 (GMT)
committerMiquel Sabaté <mikisabate@gmail.com>2014-01-19 22:44:53 (GMT)
commit8037623d6b42ef52d2d664de7488fc074a919100 (patch)
tree3eb7823460512b5f6eaa965ede2c9e7cab04e0de
parent5b3cb387d2505049a1e5b91298eb771a54b6336f (diff)
vimode: added the paragraph text object.
REVIEW: 115130
-rw-r--r--part/vimode/katevinormalmode.cpp100
-rw-r--r--part/vimode/katevinormalmode.h5
-rw-r--r--tests/vimode_test.cpp19
3 files changed, 124 insertions, 0 deletions
diff --git a/part/vimode/katevinormalmode.cpp b/part/vimode/katevinormalmode.cpp
index 74b7488..b255d90 100644
--- a/part/vimode/katevinormalmode.cpp
+++ b/part/vimode/katevinormalmode.cpp
@@ -3317,6 +3317,53 @@ Cursor KateViNormalMode::findSentenceEnd()
return Cursor(linenum, j - 1);
}
+Cursor KateViNormalMode::findParagraphStart()
+{
+ Cursor c(m_view->cursorPosition());
+ const bool firstBlank = doc()->line(c.line()).isEmpty();
+ int prev = c.line();
+
+ for (int i = prev; i >= 0; i--) {
+ if (doc()->line(i).isEmpty()) {
+ if (i != prev) {
+ prev = i + 1;
+ }
+
+ /* Skip consecutive empty lines. */
+ if (firstBlank) {
+ i--;
+ for (; i >= 0 && doc()->line(i).isEmpty(); i--, prev--);
+ }
+ return Cursor(prev, 0);
+ }
+ }
+ return Cursor(0, 0);
+}
+
+Cursor KateViNormalMode::findParagraphEnd()
+{
+ Cursor c(m_view->cursorPosition());
+ int prev = c.line(), lines = doc()->lines();
+ const bool firstBlank = doc()->line(prev).isEmpty();
+
+ for (int i = prev; i < lines; i++) {
+ if (doc()->line(i).isEmpty()) {
+ if (i != prev) {
+ prev = i - 1;
+ }
+
+ /* Skip consecutive empty lines. */
+ if (firstBlank) {
+ i++;
+ for (; i < lines && doc()->line(i).isEmpty(); i++, prev++);
+ }
+ int length = doc()->lineLength(prev);
+ return Cursor(prev, (length <= 0) ? 0 : length - 1);
+ }
+ }
+ return doc()->documentEnd();
+}
+
KateViRange KateViNormalMode::textObjectInnerSentence()
{
KateViRange r;
@@ -3360,6 +3407,57 @@ KateViRange KateViNormalMode::textObjectASentence()
return r;
}
+KateViRange KateViNormalMode::textObjectInnerParagraph()
+{
+ KateViRange r;
+ Cursor c1 = findParagraphStart();
+ Cursor c2 = findParagraphEnd();
+ updateCursor(c1);
+
+ r.startLine = c1.line();
+ r.startColumn = c1.column();
+ r.endLine = c2.line();
+ r.endColumn = c2.column();
+ return r;
+}
+
+KateViRange KateViNormalMode::textObjectAParagraph()
+{
+ Cursor original(m_view->cursorPosition());
+ KateViRange r = textObjectInnerParagraph();
+ int lines = doc()->lines();
+
+ if (r.endLine + 1 < lines) {
+ // If the next line is empty, remove all subsequent empty lines.
+ // Otherwise we'll grab the next paragraph.
+ if (doc()->line(r.endLine + 1).isEmpty()) {
+ for (int i = r.endLine + 1; i < lines && doc()->line(i).isEmpty(); i++) {
+ r.endLine++;
+ }
+ } else {
+ Cursor prev = m_view->cursorPosition();
+ Cursor c(r.endLine + 1, 0);
+ updateCursor(c);
+ c = findParagraphEnd();
+ updateCursor(prev);
+ r.endLine = c.line();
+ r.endColumn = c.column();
+ }
+ } else if (doc()->lineLength(r.startLine) > 0) {
+ // We went too far, but maybe we can grab previous empty lines.
+ for (int i = r.startLine - 1; i >= 0 && doc()->line(i).isEmpty(); i--) {
+ r.startLine--;
+ }
+ r.startColumn = 0;
+ updateCursor(Cursor(r.startLine, r.startColumn));
+ } else {
+ // We went too far and we're on empty lines, do nothing.
+ updateCursor(original);
+ return KateViRange::invalid();
+ }
+ return r;
+}
+
KateViRange KateViNormalMode::textObjectAQuoteDouble()
{
return findSurroundingQuotes( '"', false );
@@ -3672,6 +3770,8 @@ void KateViNormalMode::initializeCommands()
ADDMOTION("aW", textObjectAWORD, IS_NOT_LINEWISE );
ADDMOTION("is", textObjectInnerSentence, IS_NOT_LINEWISE );
ADDMOTION("as", textObjectASentence, IS_NOT_LINEWISE );
+ ADDMOTION("ip", textObjectInnerParagraph, IS_NOT_LINEWISE );
+ ADDMOTION("ap", textObjectAParagraph, IS_NOT_LINEWISE );
ADDMOTION("i\"", textObjectInnerQuoteDouble, IS_NOT_LINEWISE );
ADDMOTION("a\"", textObjectAQuoteDouble, IS_NOT_LINEWISE );
ADDMOTION("i'", textObjectInnerQuoteSingle, IS_NOT_LINEWISE );
diff --git a/part/vimode/katevinormalmode.h b/part/vimode/katevinormalmode.h
index 0cd58f0..56ffea5 100644
--- a/part/vimode/katevinormalmode.h
+++ b/part/vimode/katevinormalmode.h
@@ -256,6 +256,9 @@ class KATEPART_TESTS_EXPORT KateViNormalMode : public KateViModeBase
KateViRange textObjectInnerSentence();
KateViRange textObjectASentence();
+ KateViRange textObjectInnerParagraph();
+ KateViRange textObjectAParagraph();
+
KateViRange textObjectAQuoteDouble();
KateViRange textObjectInnerQuoteDouble();
@@ -305,6 +308,8 @@ class KATEPART_TESTS_EXPORT KateViNormalMode : public KateViModeBase
void shrinkRangeAroundCursor(KateViRange& toShrink, const KateViRange& rangeToShrinkTo);
Cursor findSentenceStart();
Cursor findSentenceEnd();
+ Cursor findParagraphStart();
+ Cursor findParagraphEnd();
QString m_keys;
unsigned int m_countTemp;
diff --git a/tests/vimode_test.cpp b/tests/vimode_test.cpp
index 4cd952e..b9305de 100644
--- a/tests/vimode_test.cpp
+++ b/tests/vimode_test.cpp
@@ -1382,6 +1382,25 @@ void ViModeTest::NormalModeMotionsTest() {
DoTest("hello. yay\nis this a string?!?.. another.\n", "jcasX", "hello. Xanother.\n");
DoTest("hello. yay\nis this a string?!?.. \t another.\n", "jcasX", "hello. Xanother.\n");
+ // Inner / Paragraph text object ("ip")
+ DoTest("", "cip", "");
+ DoTest("\nhello", "cipX", "X\nhello");
+ DoTest("\nhello\n\nanother. text.", "jcipX", "\nX\n\nanother. text.");
+ DoTest("\nhello\n\n\nanother. text.", "jjcipX", "\nhello\nX\nanother. text.");
+ DoTest("\nhello\n\n\nanother. text.", "jjjcipX", "\nhello\nX\nanother. text.");
+ DoTest("\nhello\n\n\nanother. text.", "jjjjcipX", "\nhello\n\n\nX");
+ DoTest("hello\n\n", "jcipX", "hello\nX");
+ DoTest("hello\n\n", "jjcipX", "hello\nX");
+
+ // Around / Paragraph text object ("ap")
+ DoTest("", "cap", "");
+ DoTest("\nhello", "capX", "X");
+ DoTest("\nhello\n\nanother.text.", "jcapX", "\nX\nanother.text.");
+ DoTest("\nhello\n\nanother.text.\n\n\nAnother.", "jjjcapX", "\nhello\n\nX\nAnother.");
+ DoTest("\nhello\n\nanother.text.\n\n\nAnother.", "jjjjjcapX", "\nhello\n\nanother.text.\nX");
+ DoTest("hello\n\n\n", "jjcapX", "hello\n\n\n");
+ DoTest("hello\n\nasd", "jjjcapX", "hello\nX");
+
DoTest( "{\nfoo\n}", "jdiB", "{\n}");
DoTest( "{\n}", "diB", "{\n}");
DoTest( "{\nfoo}", "jdiB", "{\n}");