summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiquel Sabaté <mikisabate@gmail.com>2014-01-18 21:07:26 (GMT)
committerMiquel Sabaté <mikisabate@gmail.com>2014-01-18 21:07:26 (GMT)
commit50593e34dfa0330e3226c0b24ac709b29ef27a4f (patch)
treea98894f358b8f375b1adb3ed79059ce9b58c708b
parent63c86371d04aa711fdb755bd147aadce3dd36fcc (diff)
vimode: added the sentence text object.
REVIEW: 115081
-rw-r--r--part/vimode/katevinormalmode.cpp120
-rw-r--r--part/vimode/katevinormalmode.h5
-rw-r--r--tests/vimode_test.cpp32
3 files changed, 157 insertions, 0 deletions
diff --git a/part/vimode/katevinormalmode.cpp b/part/vimode/katevinormalmode.cpp
index 0edfcdb..bef5cc3 100644
--- a/part/vimode/katevinormalmode.cpp
+++ b/part/vimode/katevinormalmode.cpp
@@ -3181,6 +3181,124 @@ KateViRange KateViNormalMode::textObjectInnerWORD()
return r;
}
+Cursor KateViNormalMode::findSentenceStart()
+{
+ Cursor c(m_view->cursorPosition());
+ int linenum = c.line(), column = c.column();
+ int prev = column;
+
+ for (int i = linenum; i >= 0; i--) {
+ const QString &line = doc()->line(i);
+ if (i != linenum) {
+ column = line.size() - 1;
+ }
+
+ // An empty line is the end of a paragraph.
+ if (line.isEmpty()) {
+ return Cursor((i != linenum) ? i + 1 : i, prev);
+ }
+
+ prev = column;
+ for (int j = column; j >= 0; j--) {
+ if (line.at(j).isSpace()) {
+ int lastSpace = j--;
+ for (; j >= 0 && QString::fromLatin1("\"')]").indexOf(line.at(j)) != -1; j--);
+
+ if (QString::fromLatin1(".!?").indexOf(line.at(j)) != -1) {
+ return Cursor(i, prev);
+ }
+ j = lastSpace;
+ } else
+ prev = j;
+ }
+ }
+
+ return Cursor(0, 0);
+}
+
+Cursor KateViNormalMode::findSentenceEnd()
+{
+ Cursor c(m_view->cursorPosition());
+ int linenum = c.line(), column = c.column();
+ int j = 0, prev = 0;
+
+ for (int i = linenum; i < doc()->lines(); i++) {
+ const QString &line = doc()->line(i);
+
+ // An empty line is the end of a paragraph.
+ if (line.isEmpty()) {
+ return Cursor(linenum, j);
+ }
+
+ // Iterating over the line to reach any '.', '!', '?'
+ for (j = column; j < line.size(); j++) {
+ if (QString::fromLatin1(".!?").indexOf(line.at(j)) != -1) {
+ prev = j++;
+ // Skip possible closing characters.
+ for (; j < line.size() && QString::fromLatin1("\"')]").indexOf(line.at(j)) != -1; j++);
+
+ if (j >= line.size()) {
+ return Cursor(i, j - 1);
+ }
+
+ // And hopefully we're done...
+ if (line.at(j).isSpace()) {
+ return Cursor(i, j - 1);
+ }
+ j = prev;
+ }
+ }
+ linenum = i;
+ prev = column;
+ column = 0;
+ }
+
+ return Cursor(linenum, j - 1);
+}
+
+KateViRange KateViNormalMode::textObjectInnerSentence()
+{
+ KateViRange r;
+ Cursor c1 = findSentenceStart();
+ Cursor c2 = findSentenceEnd();
+ updateCursor(c1);
+
+ r.startLine = c1.line();
+ r.startColumn = c1.column();
+ r.endLine = c2.line();
+ r.endColumn = c2.column();
+ return r;
+}
+
+KateViRange KateViNormalMode::textObjectASentence()
+{
+ int i;
+ KateViRange r = textObjectInnerSentence();
+ const QString &line = doc()->line(r.endLine);
+
+ // Skip whitespaces and tabs.
+ for (i = r.endColumn + 1; i < line.size(); i++) {
+ if (!line.at(i).isSpace()) {
+ break;
+ }
+ }
+ r.endColumn = i - 1;
+
+ // Remove preceding spaces.
+ if (r.startColumn != 0) {
+ if (r.endColumn == line.size() - 1 && !line.at(r.endColumn).isSpace()) {
+ const QString &line = doc()->line(r.startLine);
+ for (i = r.startColumn - 1; i >= 0; i--) {
+ if (!line.at(i).isSpace()) {
+ break;
+ }
+ }
+ r.startColumn = i + 1;
+ }
+ }
+ return r;
+}
+
KateViRange KateViNormalMode::textObjectAQuoteDouble()
{
return findSurroundingQuotes( '"', false );
@@ -3489,6 +3607,8 @@ void KateViNormalMode::initializeCommands()
ADDMOTION("aw", textObjectAWord, IS_NOT_LINEWISE );
ADDMOTION("iW", textObjectInnerWORD, 0 );
ADDMOTION("aW", textObjectAWORD, IS_NOT_LINEWISE );
+ ADDMOTION("is", textObjectInnerSentence, IS_NOT_LINEWISE );
+ ADDMOTION("as", textObjectASentence, 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 1a99c8c..774f566 100644
--- a/part/vimode/katevinormalmode.h
+++ b/part/vimode/katevinormalmode.h
@@ -250,6 +250,9 @@ class KATEPART_TESTS_EXPORT KateViNormalMode : public KateViModeBase
KateViRange textObjectAWORD();
KateViRange textObjectInnerWORD();
+ KateViRange textObjectInnerSentence();
+ KateViRange textObjectASentence();
+
KateViRange textObjectAQuoteDouble();
KateViRange textObjectInnerQuoteDouble();
@@ -297,6 +300,8 @@ class KATEPART_TESTS_EXPORT KateViNormalMode : public KateViModeBase
KateViRange textObjectComma(bool inner);
void shrinkRangeAroundCursor(KateViRange& toShrink, const KateViRange& rangeToShrinkTo);
+ Cursor findSentenceStart();
+ Cursor findSentenceEnd();
QString m_keys;
unsigned int m_countTemp;
diff --git a/tests/vimode_test.cpp b/tests/vimode_test.cpp
index 6bf389c..70588ee 100644
--- a/tests/vimode_test.cpp
+++ b/tests/vimode_test.cpp
@@ -1350,6 +1350,38 @@ void ViModeTest::NormalModeMotionsTest() {
DoTest("fo(o", "llldiW", "");
DoTest("foo \"\"B!!", "fBdaW", "foo");
+ // Inner / Sentence text object ("is")
+ DoTest("", "cis", "");
+ DoTest("hello", "cis", "");
+ DoTest("hello", "flcis", "");
+ DoTest("hello. bye", "cisX", "X bye");
+ DoTest("hello. bye", "f.cisX", "X bye");
+ DoTest("hello. bye", "fbcisX", "hello. X");
+ DoTest("hello\n\nbye.", "cisX", "X\n\nbye.");
+ DoTest("Hello. Bye.\n", "GcisX", "Hello. Bye.\nX");
+ DoTest("hello. by.. another.", "cisX", "X by.. another.");
+ DoTest("hello. by.. another.", "fbcisX", "hello. X another.");
+ DoTest("hello. by.. another.\n", "GcisX", "hello. by.. another.\nX");
+ DoTest("hello. yay\nis this a string?!?.. another.\n", "fycisX", "hello. X another.\n");
+ DoTest("hello. yay\nis this a string?!?.. another.\n", "jcisX", "hello. X another.\n");
+
+ // Around / Sentence text object ("as")
+ DoTest("", "cas", "");
+ DoTest("hello", "cas", "");
+ DoTest("hello", "flcas", "");
+ DoTest("hello. bye", "casX", "Xbye");
+ DoTest("hello. bye", "f.casX", "Xbye");
+ DoTest("hello. bye.", "fbcasX", "hello.X");
+ DoTest("hello. bye", "fbcasX", "hello.X");
+ DoTest("hello\n\nbye.", "casX", "X\n\nbye.");
+ DoTest("Hello. Bye.\n", "GcasX", "Hello. Bye.\nX");
+ DoTest("hello. by.. another.", "casX", "Xby.. another.");
+ DoTest("hello. by.. another.", "fbcasX", "hello. Xanother.");
+ DoTest("hello. by.. another.\n", "GcasX", "hello. by.. another.\nX");
+ DoTest("hello. yay\nis this a string?!?.. another.\n", "fycasX", "hello. Xanother.\n");
+ 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");
+
DoTest( "{\nfoo\n}", "jdiB", "{\n}");
DoTest( "{\n}", "diB", "{\n}");
DoTest( "{\nfoo}", "jdiB", "{\n}");