diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index d169e9e1c24..ef23b65d965 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -3916,22 +3916,121 @@ bool FakeVimHandler::Private::handleExPluginCommand(const ExCommand &cmd) return handled; } -static QRegExp vimPatternToQtPattern(QString needle, QTextDocument::FindFlags *flags) +static QRegExp vimPatternToQtPattern(QString needle) { - // FIXME: Rough mapping of a common case. - if (needle.startsWith(_("\\<")) && needle.endsWith(_("\\>"))) - (*flags) |= QTextDocument::FindWholeWords; - needle.remove(_("\\<")); // start of word - needle.remove(_("\\>")); // end of word + /* Trasformations (Vim regexp -> QRegExp): + * \a -> [A-Za-z] + * \A -> [^A-Za-z] + * \h -> [A-Za-z_] + * \H -> [^A-Za-z_] + * \l -> [a-z] + * \L -> [^a-z] + * \o -> [0-7] + * \O -> [^0-7] + * \u -> [A-Z] + * \U -> [^A-Z] + * \x -> [0-9A-Fa-f] + * \X -> [^0-9A-Fa-f] + * + * \< -> \b + * \> -> \b + * [] -> \[\] + * \= -> ? + * + * (...) <-> \(...\) + * {...} <-> \{...\} + * | <-> \| + * ? <-> \? + * + <-> \+ + * \{...} -> {...} + * + * \c - set ignorecase for rest + * \C - set noignorecase for rest + */ + // TODO: Set initial value and handle smartcase option. + bool ignorecase = false; + QString pattern; + pattern.reserve(2 * needle.size()); - // QRegExp's | and \| have the opposite meaning of vim's. - QString dummy(QLatin1Char(1)); - needle.replace(_("\\|"), dummy); - needle.replace(_("|"), _("\\|")); - needle.replace(dummy, _("|")); + bool escape = false; + bool brace = false; + bool curly = false; + foreach (const QChar &c, needle) { + if (brace) { + brace = false; + if (c == ']') { + pattern.append(_("\\[\\]")); + continue; + } else { + pattern.append('['); + } + } + if (QString("(){}+|?").indexOf(c) != -1) { + if (c == '{') { + curly = escape; + } else if (c == '}' && curly) { + curly = false; + escape = true; + } - //qDebug() << "NEEDLE " << needle << needle; - return QRegExp(needle); + if (escape) + escape = false; + else + pattern.append('\\'); + pattern.append(c); + } else if (escape) { + // escape expression + escape = false; + if (c == '<' || c == '>') + pattern.append(_("\\b")); + else if (c == 'a') + pattern.append(_("[a-zA-Z]")); + else if (c == 'A') + pattern.append(_("[^a-zA-Z]")); + else if (c == 'h') + pattern.append(_("[A-Za-z_]")); + else if (c == 'H') + pattern.append(_("[^A-Za-z_]")); + else if (c == 'c' || c == 'C') + ignorecase = (c == 'c'); + else if (c == 'l') + pattern.append(_("[a-z]")); + else if (c == 'L') + pattern.append(_("[^a-z]")); + else if (c == 'o') + pattern.append(_("[0-7]")); + else if (c == 'O') + pattern.append(_("[^0-7]")); + else if (c == 'u') + pattern.append(_("[A-Z]")); + else if (c == 'U') + pattern.append(_("[^A-Z]")); + else if (c == 'x') + pattern.append(_("[0-9A-Fa-f]")); + else if (c == 'X') + pattern.append(_("[^0-9A-Fa-f]")); + else if (c == '=') + pattern.append(_("?")); + else + pattern.append('\\' + c); + } else { + // unescaped expression + if (c == '\\') + escape = true; + else if (c == '[') + brace = true; + else if (c.isLetter() && ignorecase) + pattern.append('[' + c.toLower() + c.toUpper() + ']'); + else + pattern.append(c); + } + } + if (escape) + pattern.append('\\'); + else if (brace) + pattern.append('['); + + return QRegExp(pattern); } void FakeVimHandler::Private::searchBalanced(bool forward, QChar needle, QChar other) @@ -3975,7 +4074,7 @@ void FakeVimHandler::Private::search(const SearchData &sd) if (!sd.forward) flags |= QTextDocument::FindBackward; - QRegExp needleExp = vimPatternToQtPattern(sd.needle, &flags); + QRegExp needleExp = vimPatternToQtPattern(sd.needle); const int oldLine = cursorLine() - cursorLineOnScreen(); @@ -4036,10 +4135,16 @@ void FakeVimHandler::Private::highlightMatches(const QString &needle) QTextCursor tc = cursor(); tc.movePosition(StartOfDocument, MoveAnchor); - QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively; - QRegExp needleExp = vimPatternToQtPattern(needle, &flags); + QRegExp needleExp = vimPatternToQtPattern(needle); + if (!needleExp.isValid()) { + QString error = needleExp.errorString(); + showRedMessage( + FakeVimHandler::tr("Invalid regular expression: %1").arg(error)); + return; + } + while (!tc.atEnd()) { - tc = tc.document()->find(needleExp, tc.position(), flags); + tc = tc.document()->find(needleExp, tc.position()); if (tc.isNull()) break; if (!tc.hasSelection())