fakevim: Match same text with same regular expression

Match same text on same regex in forward and backward search modes.
E.g. with '\w\{2}' Vim regex and 'abc' text always match '<ab>c'
(should never match 'a<bc>' as with QTextDocument::find()).

Change-Id: Ie920c76540f3be426fc7b842d38137e95c1a65b2
Reviewed-by: hjk <qthjk@ovi.com>
This commit is contained in:
Lukas Holecek
2012-11-18 19:58:02 +01:00
committed by hjk
parent c3ce444c8e
commit cd7c91d440
2 changed files with 141 additions and 22 deletions

View File

@@ -954,12 +954,29 @@ void FakeVimPlugin::test_vim_search()
KEYS("n", X "abc abc abc abc"); KEYS("n", X "abc abc abc abc");
KEYS("N", "abc " X "abc abc abc"); KEYS("N", "abc " X "abc abc abc");
NOT_IMPLEMENTED // search is greedy
data.doCommand("set ws");
data.setText("abc" N "def" N "abc" N "ghi abc jkl");
KEYS("/[a-z]*<CR>", "abc" N X "def" N "abc" N "ghi abc jkl");
KEYS("2n", "abc" N "def" N "abc" N X "ghi abc jkl");
KEYS("3n", "abc" N "def" N "abc" N "ghi abc" X " jkl");
KEYS("3N", "abc" N "def" N "abc" N X "ghi abc jkl");
KEYS("2N", "abc" N X "def" N "abc" N "ghi abc jkl");
data.setText("a.b.c" N "" N "d.e.f");
KEYS("/[a-z]*<CR>", "a" X ".b.c" N "" N "d.e.f");
KEYS("n", "a." X "b.c" N "" N "d.e.f");
KEYS("2n", "a.b." X "c" N "" N "d.e.f");
KEYS("n", "a.b.c" N X "" N "d.e.f");
KEYS("n", "a.b.c" N "" N X "d.e.f");
KEYS("2N", "a.b." X "c" N "" N "d.e.f");
KEYS("2n", "a.b.c" N "" N X "d.e.f");
// find same stuff forward and backward, // find same stuff forward and backward,
// i.e. '<ab>c' forward but not 'a<bc>' backward // i.e. '<ab>c' forward but not 'a<bc>' backward
data.setText("abc" N "def" N "ghi"); data.setText("abc" N "def" N "ghi");
KEYS("/\\w\\{2}<CR>", X "abc" N "def" N "ghi"); KEYS("/\\w\\{2}<CR>", "abc" N X "def" N "ghi");
KEYS("2n", "abc" N "def" N X "ghi"); KEYS("n", "abc" N "def" N X "ghi");
KEYS("N", "abc" N X "def" N "ghi"); KEYS("N", "abc" N X "def" N "ghi");
KEYS("N", X "abc" N "def" N "ghi"); KEYS("N", X "abc" N "def" N "ghi");
KEYS("2n2N", X "abc" N "def" N "ghi"); KEYS("2n2N", X "abc" N "def" N "ghi");

View File

@@ -452,6 +452,94 @@ static QRegExp vimPatternToQtPattern(QString needle, bool smartcase)
return QRegExp(pattern); return QRegExp(pattern);
} }
static bool afterEndOfLine(const QTextDocument *doc, int position)
{
return doc->characterAt(position) == ParagraphSeparator
&& doc->findBlock(position).length() > 1;
}
static void searchForward(QTextCursor *tc, QRegExp &needleExp, int *repeat)
{
const QTextDocument *doc = tc->document();
const int startPos = tc->position();
// Search from beginning of line so that matched text is the same.
tc->movePosition(StartOfLine);
// forward to current position
*tc = doc->find(needleExp, *tc);
while (!tc->isNull() && tc->anchor() < startPos) {
if (!tc->hasSelection())
tc->movePosition(Right);
if (tc->atBlockEnd())
tc->movePosition(Right);
*tc = doc->find(needleExp, *tc);
}
if (tc->isNull())
return;
--*repeat;
while (*repeat > 0) {
if (!tc->hasSelection())
tc->movePosition(Right);
if (tc->atBlockEnd())
tc->movePosition(Right);
*tc = doc->find(needleExp, *tc);
if (tc->isNull())
return;
--*repeat;
}
if (!tc->isNull() && afterEndOfLine(doc, tc->anchor()))
tc->movePosition(Left);
}
static void searchBackward(QTextCursor *tc, QRegExp &needleExp, int *repeat)
{
// Search from beginning of line so that matched text is the same.
QTextBlock block = tc->block();
QString line = block.text();
int i = line.indexOf(needleExp, 0);
while (i != -1 && i < tc->positionInBlock()) {
--*repeat;
i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength()));
if (i == line.size())
i = -1;
}
if (i == tc->positionInBlock())
--*repeat;
while (*repeat > 0) {
block = block.previous();
if (!block.isValid())
break;
line = block.text();
i = line.indexOf(needleExp, 0);
while (i != -1) {
--*repeat;
i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength()));
if (i == line.size())
i = -1;
}
}
if (!block.isValid()) {
*tc = QTextCursor();
return;
}
i = line.indexOf(needleExp, 0);
while (*repeat < 0) {
i = line.indexOf(needleExp, i + qMax(1, needleExp.matchedLength()));
++*repeat;
}
tc->setPosition(block.position() + i);
}
static bool substituteText(QString *text, QRegExp &pattern, const QString &replacement, static bool substituteText(QString *text, QRegExp &pattern, const QString &replacement,
bool global) bool global)
{ {
@@ -2360,8 +2448,7 @@ void FakeVimHandler::Private::fixSelection()
// Omit first character in selection if it's line break on non-empty line. // Omit first character in selection if it's line break on non-empty line.
int start = anchor(); int start = anchor();
int end = position(); int end = position();
if (document()->characterAt(start) == ParagraphSeparator if (afterEndOfLine(document(), start) && start > 0) {
&& start > 0 && document()->characterAt(start - 1) != ParagraphSeparator) {
start = qMin(start + 1, end); start = qMin(start + 1, end);
if (m_submode == DeleteSubMode && !atDocumentEnd()) if (m_submode == DeleteSubMode && !atDocumentEnd())
setAnchorAndPosition(start, end + 1); setAnchorAndPosition(start, end + 1);
@@ -5072,23 +5159,44 @@ void FakeVimHandler::Private::searchBalanced(bool forward, QChar needle, QChar o
QTextCursor FakeVimHandler::Private::search(const SearchData &sd, int startPos, int count, QTextCursor FakeVimHandler::Private::search(const SearchData &sd, int startPos, int count,
bool showMessages) bool showMessages)
{ {
QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
if (!sd.forward)
flags |= QTextDocument::FindBackward;
QRegExp needleExp = vimPatternToQtPattern(sd.needle, hasConfig(ConfigSmartCase)); QRegExp needleExp = vimPatternToQtPattern(sd.needle, hasConfig(ConfigSmartCase));
if (!needleExp.isValid()) {
if (showMessages) {
QString error = needleExp.errorString();
showMessage(MessageError,
FakeVimHandler::tr("Invalid regular expression: %1").arg(error));
}
if (sd.highlightMatches)
highlightMatches(QString());
return QTextCursor();
}
QTextCursor tc = document()->find(needleExp, startPos + (sd.forward ? 1 : -1), flags);
int repeat = count; int repeat = count;
while (!tc.isNull() && --repeat >= 1) const int pos = startPos + (sd.forward ? 1 : -1);
tc = document()->find(needleExp, tc, flags);
QTextCursor tc;
if (pos >= 0 && pos < document()->characterCount()) {
tc = QTextCursor(document());
tc.setPosition(pos);
if (sd.forward && afterEndOfLine(document(), pos))
tc.movePosition(Right);
if (!tc.isNull()) {
if (sd.forward)
searchForward(&tc, needleExp, &repeat);
else
searchBackward(&tc, needleExp, &repeat);
}
}
if (tc.isNull()) { if (tc.isNull()) {
if (hasConfig(ConfigWrapScan)) { if (hasConfig(ConfigWrapScan)) {
int newStartPos = sd.forward ? 0 : lastPositionInDocument(true); tc = QTextCursor(document());
tc = document()->find(needleExp, newStartPos, flags); tc.movePosition(sd.forward ? StartOfDocument : EndOfDocument);
while (!tc.isNull() && --repeat >= 1) if (sd.forward)
tc = document()->find(needleExp, tc, flags); searchForward(&tc, needleExp, &repeat);
else
searchBackward(&tc, needleExp, &repeat);
if (tc.isNull()) { if (tc.isNull()) {
if (showMessages) { if (showMessages) {
showMessage(MessageError, showMessage(MessageError,
@@ -5108,12 +5216,6 @@ QTextCursor FakeVimHandler::Private::search(const SearchData &sd, int startPos,
} }
} }
if (tc.isNull() && !needleExp.isValid() && showMessages) {
QString error = needleExp.errorString();
showMessage(MessageError,
FakeVimHandler::tr("Invalid regular expression: %1").arg(error));
}
if (sd.highlightMatches) if (sd.highlightMatches)
highlightMatches(needleExp.pattern()); highlightMatches(needleExp.pattern());