forked from qt-creator/qt-creator
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:
@@ -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");
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user