[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [ktexteditor] src: Move SearchMode into its own source files.
From: Simon St James <kdedevel () etotheipiplusone ! com>
Date: 2016-06-17 8:18:10
Message-ID: E1bDoyg-0002ZN-LW () scm ! kde ! org
[Download RAW message or body]
Git commit f021d6eba80ebedc11bd79d2926fc315a4ea2fc9 by Simon St James.
Committed on 17/06/2016 at 08:14.
Pushed by sstjames into branch 'master'.
Move SearchMode into its own source files.
M +1 -0 src/CMakeLists.txt
M +1 -356 src/vimode/emulatedcommandbar/emulatedcommandbar.cpp
M +1 -33 src/vimode/emulatedcommandbar/emulatedcommandbar.h
A +373 -0 src/vimode/emulatedcommandbar/searchmode.cpp [License: UNKNOWN] *
A +55 -0 src/vimode/emulatedcommandbar/searchmode.h [License: UNKNOWN] *
The files marked with a * at the end have a non valid license. Please read: \
http://techbase.kde.org/Policies/Licensing_Policy and use the headers which are \
listed at that page.
http://commits.kde.org/ktexteditor/f021d6eba80ebedc11bd79d2926fc315a4ea2fc9
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9e4dc5b..51374c7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -280,6 +280,7 @@ if (BUILD_VIMODE)
vimode/emulatedcommandbar/completer.cpp
vimode/emulatedcommandbar/activemode.cpp
vimode/emulatedcommandbar/interactivesedreplacemode.cpp
+ vimode/emulatedcommandbar/searchmode.cpp
vimode/commandrangeexpressionparser.cpp
vimode/keymapper.cpp
vimode/marks.cpp
diff --git a/src/vimode/emulatedcommandbar/emulatedcommandbar.cpp \
b/src/vimode/emulatedcommandbar/emulatedcommandbar.cpp index 00b0355..513de48 100644
--- a/src/vimode/emulatedcommandbar/emulatedcommandbar.cpp
+++ b/src/vimode/emulatedcommandbar/emulatedcommandbar.cpp
@@ -30,6 +30,7 @@
#include <vimode/modes/normalvimode.h>
#include "matchhighlighter.h"
#include "interactivesedreplacemode.h"
+#include "searchmode.h"
#include <vimode/cmds.h>
#include <vimode/modes/visualvimode.h>
@@ -41,7 +42,6 @@
#include "../registers.h"
#include "../searcher.h"
-#include <KColorScheme>
#include <KLocalizedString>
#include <QLineEdit>
@@ -56,153 +56,6 @@ using namespace KateVi;
namespace
{
-bool isCharEscaped(const QString &string, int charPos)
-{
- if (charPos == 0) {
- return false;
- }
- int numContiguousBackslashesToLeft = 0;
- charPos--;
- while (charPos >= 0 && string[charPos] == QLatin1Char('\\')) {
- numContiguousBackslashesToLeft++;
- charPos--;
- }
- return ((numContiguousBackslashesToLeft % 2) == 1);
-}
-
-QString toggledEscaped(const QString &originalString, QChar escapeChar)
-{
- int searchFrom = 0;
- QString toggledEscapedString = originalString;
- do {
- const int indexOfEscapeChar = toggledEscapedString.indexOf(escapeChar, \
searchFrom);
- if (indexOfEscapeChar == -1) {
- break;
- }
- if (!isCharEscaped(toggledEscapedString, indexOfEscapeChar)) {
- // Escape.
- toggledEscapedString.replace(indexOfEscapeChar, 1, QLatin1String("\\") + \
escapeChar);
- searchFrom = indexOfEscapeChar + 2;
- } else {
- // Unescape.
- toggledEscapedString.remove(indexOfEscapeChar - 1, 1);
- searchFrom = indexOfEscapeChar;
- }
- } while (true);
-
- return toggledEscapedString;
-}
-
-QString ensuredCharEscaped(const QString &originalString, QChar charToEscape)
-{
- QString escapedString = originalString;
- for (int i = 0; i < escapedString.length(); i++) {
- if (escapedString[i] == charToEscape && !isCharEscaped(escapedString, i)) {
- escapedString.replace(i, 1, QLatin1String("\\") + charToEscape);
- }
- }
- return escapedString;
-}
-
-QString vimRegexToQtRegexPattern(const QString &vimRegexPattern)
-{
- QString qtRegexPattern = vimRegexPattern;
- qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char('('));
- qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char(')'));
- qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char('+'));
- qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char('|'));
- qtRegexPattern = ensuredCharEscaped(qtRegexPattern, QLatin1Char('?'));
- {
- // All curly brackets, except the closing curly bracket of a matching pair \
where the opening bracket is escaped,
- // must have their escaping toggled.
- bool lookingForMatchingCloseBracket = false;
- QList<int> matchingClosedCurlyBracketPositions;
- for (int i = 0; i < qtRegexPattern.length(); i++) {
- if (qtRegexPattern[i] == QLatin1Char('{') && \
isCharEscaped(qtRegexPattern, i)) {
- lookingForMatchingCloseBracket = true;
- }
- if (qtRegexPattern[i] == QLatin1Char('}') && \
lookingForMatchingCloseBracket && qtRegexPattern[i - 1] != \
QLatin1Char('\\')) {
- matchingClosedCurlyBracketPositions.append(i);
- }
- }
- if (matchingClosedCurlyBracketPositions.isEmpty()) {
- // Escape all {'s and }'s - there are no matching pairs.
- qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char('{'));
- qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char('}'));
- } else {
- // Ensure that every chunk of qtRegexPattern that does *not* contain a \
curly closing bracket
- // that is matched have their { and } escaping toggled.
- QString qtRegexPatternNonMatchingCurliesToggled;
- int previousNonMatchingClosedCurlyPos = 0; // i.e. the position of the \
last character which is either
- // not a curly closing bracket, or is a curly closing bracket
- // that is not matched.
- foreach (int matchingClosedCurlyPos, \
matchingClosedCurlyBracketPositions) {
- QString chunkExcludingMatchingCurlyClosed = \
qtRegexPattern.mid(previousNonMatchingClosedCurlyPos, matchingClosedCurlyPos - \
previousNonMatchingClosedCurlyPos);
- chunkExcludingMatchingCurlyClosed = \
toggledEscaped(chunkExcludingMatchingCurlyClosed, QLatin1Char('{'));
- chunkExcludingMatchingCurlyClosed = \
toggledEscaped(chunkExcludingMatchingCurlyClosed, QLatin1Char('}'));
- qtRegexPatternNonMatchingCurliesToggled += \
chunkExcludingMatchingCurlyClosed +
- qtRegexPattern[matchingClosedCurlyPos];
- previousNonMatchingClosedCurlyPos = matchingClosedCurlyPos + 1;
- }
- QString chunkAfterLastMatchingClosedCurly = \
qtRegexPattern.mid(matchingClosedCurlyBracketPositions.last() + 1);
- chunkAfterLastMatchingClosedCurly = \
toggledEscaped(chunkAfterLastMatchingClosedCurly, QLatin1Char('{'));
- chunkAfterLastMatchingClosedCurly = \
toggledEscaped(chunkAfterLastMatchingClosedCurly, QLatin1Char('}'));
- qtRegexPatternNonMatchingCurliesToggled += \
chunkAfterLastMatchingClosedCurly;
-
- qtRegexPattern = qtRegexPatternNonMatchingCurliesToggled;
- }
-
- }
-
- // All square brackets, *except* for those that are a) unescaped; and b) form a \
matching pair, must be
- // escaped.
- bool lookingForMatchingCloseBracket = false;
- int openingBracketPos = -1;
- QList<int> matchingSquareBracketPositions;
- for (int i = 0; i < qtRegexPattern.length(); i++) {
- if (qtRegexPattern[i] == QLatin1Char('[') && !isCharEscaped(qtRegexPattern, \
i) && !lookingForMatchingCloseBracket) {
- lookingForMatchingCloseBracket = true;
- openingBracketPos = i;
- }
- if (qtRegexPattern[i] == QLatin1Char(']') && lookingForMatchingCloseBracket \
&& !isCharEscaped(qtRegexPattern, i)) {
- lookingForMatchingCloseBracket = false;
- matchingSquareBracketPositions.append(openingBracketPos);
- matchingSquareBracketPositions.append(i);
- }
- }
-
- if (matchingSquareBracketPositions.isEmpty()) {
- // Escape all ['s and ]'s - there are no matching pairs.
- qtRegexPattern = ensuredCharEscaped(qtRegexPattern, QLatin1Char('['));
- qtRegexPattern = ensuredCharEscaped(qtRegexPattern, QLatin1Char(']'));
- } else {
- // Ensure that every chunk of qtRegexPattern that does *not* contain one of \
the matching pairs of
- // square brackets have their square brackets escaped.
- QString qtRegexPatternNonMatchingSquaresMadeLiteral;
- int previousNonMatchingSquareBracketPos = 0; // i.e. the position of the \
last character which is
- // either not a square bracket, or is a square bracket but
- // which is not matched.
- foreach (int matchingSquareBracketPos, matchingSquareBracketPositions) {
- QString chunkExcludingMatchingSquareBrackets = \
qtRegexPattern.mid(previousNonMatchingSquareBracketPos, matchingSquareBracketPos - \
previousNonMatchingSquareBracketPos);
- chunkExcludingMatchingSquareBrackets = \
ensuredCharEscaped(chunkExcludingMatchingSquareBrackets, \
QLatin1Char('['));
- chunkExcludingMatchingSquareBrackets = \
ensuredCharEscaped(chunkExcludingMatchingSquareBrackets, \
QLatin1Char(']'));
- qtRegexPatternNonMatchingSquaresMadeLiteral += \
chunkExcludingMatchingSquareBrackets + \
qtRegexPattern[matchingSquareBracketPos];
- previousNonMatchingSquareBracketPos = matchingSquareBracketPos + 1;
- }
- QString chunkAfterLastMatchingSquareBracket = \
qtRegexPattern.mid(matchingSquareBracketPositions.last() + 1);
- chunkAfterLastMatchingSquareBracket = \
ensuredCharEscaped(chunkAfterLastMatchingSquareBracket, \
QLatin1Char('['));
- chunkAfterLastMatchingSquareBracket = \
ensuredCharEscaped(chunkAfterLastMatchingSquareBracket, \
QLatin1Char(']'));
- qtRegexPatternNonMatchingSquaresMadeLiteral += \
chunkAfterLastMatchingSquareBracket;
-
- qtRegexPattern = qtRegexPatternNonMatchingSquaresMadeLiteral;
- }
-
- qtRegexPattern = qtRegexPattern.replace(QLatin1String("\\>"), \
QLatin1String("\\b"));
- qtRegexPattern = qtRegexPattern.replace(QLatin1String("\\<"), \
QLatin1String("\\b"));
-
- return qtRegexPattern;
-}
-
/**
* @return \a originalRegex but escaped in such a way that a Qt regex search for
* the resulting string will match the string \a originalRegex.
@@ -221,73 +74,6 @@ QString escapedForSearchingAsLiteral(const QString \
&originalQtRegex)
escapedForSearchingAsLiteral.replace(QLatin1Char('\n'), QLatin1String("\\n"));
return escapedForSearchingAsLiteral;
}
-
-QStringList reversed(const QStringList &originalList)
-{
- QStringList reversedList = originalList;
- std::reverse(reversedList.begin(), reversedList.end());
- return reversedList;
-}
-
-QString withCaseSensitivityMarkersStripped(const QString &originalSearchTerm)
-{
- // Only \C is handled, for now - I'll implement \c if someone asks for it.
- int pos = 0;
- QString caseSensitivityMarkersStripped = originalSearchTerm;
- while (pos < caseSensitivityMarkersStripped.length()) {
- if (caseSensitivityMarkersStripped.at(pos) == QLatin1Char('C') && \
isCharEscaped(caseSensitivityMarkersStripped, pos)) {
- caseSensitivityMarkersStripped.replace(pos - 1, 2, QString());
- pos--;
- }
- pos++;
- }
- return caseSensitivityMarkersStripped;
-}
-
-int findPosOfSearchConfigMarker(const QString &searchText, const bool \
isSearchBackwards)
-{
- const QChar searchConfigMarkerChar = (isSearchBackwards ? QLatin1Char('?') : \
QLatin1Char('/'));
- for (int pos = 0; pos < searchText.length(); pos++) {
- if (searchText.at(pos) == searchConfigMarkerChar) {
- if (!isCharEscaped(searchText, pos)) {
- return pos;
- }
- }
- }
- return -1;
-}
-
-bool isRepeatLastSearch(const QString &searchText, const bool isSearchBackwards)
-{
- const int posOfSearchConfigMarker = findPosOfSearchConfigMarker(searchText, \
isSearchBackwards);
- if (posOfSearchConfigMarker != -1) {
- if (searchText.leftRef(posOfSearchConfigMarker).isEmpty()) {
- return true;
- }
- }
- return false;
-}
-
-bool shouldPlaceCursorAtEndOfMatch(const QString &searchText, const bool \
isSearchBackwards)
-{
- const int posOfSearchConfigMarker = findPosOfSearchConfigMarker(searchText, \
isSearchBackwards);
- if (posOfSearchConfigMarker != -1) {
- if (searchText.length() > posOfSearchConfigMarker + 1 && \
searchText.at(posOfSearchConfigMarker + 1) == QLatin1Char('e')) {
- return true;
- }
- }
- return false;
-}
-
-QString withSearchConfigRemoved(const QString &originalSearchText, const bool \
isSearchBackwards)
-{
- const int posOfSearchConfigMarker = \
findPosOfSearchConfigMarker(originalSearchText, isSearchBackwards);
- if (posOfSearchConfigMarker == -1) {
- return originalSearchText;
- } else {
- return originalSearchText.left(posOfSearchConfigMarker);
- }
-}
}
EmulatedCommandBar::EmulatedCommandBar(InputModeManager *viInputModeManager, QWidget \
*parent) @@ -649,147 +435,6 @@ void EmulatedCommandBar::hideAllWidgetsExcept(QWidget* \
widgetToKeepVisible)
}
-EmulatedCommandBar::SearchMode::SearchMode(EmulatedCommandBar* emulatedCommandBar, \
MatchHighlighter* matchHighlighter, KTextEditor::ViewPrivate* view, \
QLineEdit* edit)
- : ActiveMode ( emulatedCommandBar, matchHighlighter),
- m_emulatedCommandBar(emulatedCommandBar),
- m_view(view),
- m_edit(edit)
-{
-}
-
-void EmulatedCommandBar::SearchMode::init ( \
EmulatedCommandBar::SearchMode::SearchDirection searchDirection)
-{
- m_searchDirection = searchDirection;
- m_startingCursorPos = m_view->cursorPosition();
-}
-
-void EmulatedCommandBar::SearchMode::setViInputModeManager ( InputModeManager* \
viInputModeManager )
-{
- m_viInputModeManager = viInputModeManager;
-}
-
-bool EmulatedCommandBar::SearchMode::handleKeyPress ( const QKeyEvent* keyEvent )
-{
- Q_UNUSED(keyEvent);
- return false;
-}
-
-void EmulatedCommandBar::SearchMode::editTextChanged ( const QString& newText )
-{
- QString qtRegexPattern = newText;
- const bool searchBackwards = (m_searchDirection == SearchDirection::Backward);
- const bool placeCursorAtEndOfMatch = \
shouldPlaceCursorAtEndOfMatch(qtRegexPattern, searchBackwards);
- if (isRepeatLastSearch(qtRegexPattern, searchBackwards)) {
- qtRegexPattern = m_viInputModeManager->searcher()->getLastSearchPattern();
- } else {
- qtRegexPattern = withSearchConfigRemoved(qtRegexPattern, searchBackwards);
- qtRegexPattern = vimRegexToQtRegexPattern(qtRegexPattern);
- }
-
- // Decide case-sensitivity via SmartCase (note: if the expression contains \C, \
the "case-sensitive" marker, then
- // we will be case-sensitive "by coincidence", as it were.).
- bool caseSensitive = true;
- if (qtRegexPattern.toLower() == qtRegexPattern) {
- caseSensitive = false;
- }
-
- qtRegexPattern = withCaseSensitivityMarkersStripped(qtRegexPattern);
-
- m_currentSearchParams.pattern = qtRegexPattern;
- m_currentSearchParams.isCaseSensitive = caseSensitive;
- m_currentSearchParams.isBackwards = searchBackwards;
- m_currentSearchParams.shouldPlaceCursorAtEndOfMatch = placeCursorAtEndOfMatch;
-
- // The "count" for the current search is not shared between Visual & Normal \
mode, so we need to pick
- // the right one to handle the counted search.
- int c = m_viInputModeManager->getCurrentViModeHandler()->getCount();
- KTextEditor::Range match = \
m_viInputModeManager->searcher()->findPattern(m_currentSearchParams, \
m_startingCursorPos, c, false /* Don't add incremental searches to search history \
*/);
-
- if (match.isValid()) {
- // The returned range ends one past the last character of the match, so \
adjust.
- KTextEditor::Cursor realMatchEnd = KTextEditor::Cursor(match.end().line(), \
match.end().column() - 1);
- if (realMatchEnd.column() == -1) {
- realMatchEnd = KTextEditor::Cursor(realMatchEnd.line() - 1, \
m_view->doc()->lineLength(realMatchEnd.line() - 1));
- }
- moveCursorTo(placeCursorAtEndOfMatch ? realMatchEnd : match.start());
- setBarBackground(SearchMode::MatchFound);
- } else {
- moveCursorTo(m_startingCursorPos);
- if (!m_edit->text().isEmpty()) {
- setBarBackground(SearchMode::NoMatchFound);
- } else {
- setBarBackground(SearchMode::Normal);
- }
- }
-
- updateMatchHighlight(match);
-}
-
-void EmulatedCommandBar::SearchMode::deactivate(bool wasAborted)
-{
- // "Deactivate" can be called multiple times between init()'s, so only reset the \
cursor once!
- if (m_startingCursorPos.isValid()) {
- if (wasAborted) {
- moveCursorTo(m_startingCursorPos);
- }
- }
- m_startingCursorPos = KTextEditor::Cursor::invalid();
- setBarBackground(SearchMode::Normal);
- // Send a synthetic keypress through the system that signals whether the search \
was aborted or
- // not. If not, the keypress will "complete" the search motion, thus triggering \
it.
- // We send to KateViewInternal as it updates the status bar and removes the "?".
- const Qt::Key syntheticSearchCompletedKey = (wasAborted ? \
static_cast<Qt::Key>(0) : Qt::Key_Enter);
- QKeyEvent syntheticSearchCompletedKeyPress(QEvent::KeyPress, \
syntheticSearchCompletedKey, Qt::NoModifier);
- m_isSendingSyntheticSearchCompletedKeypress = true;
- QApplication::sendEvent(m_view->focusProxy(), \
&syntheticSearchCompletedKeyPress);
- m_isSendingSyntheticSearchCompletedKeypress = false;
- if (!wasAborted) {
- // Search was actually executed, so store it as the last search.
- m_viInputModeManager->searcher()->setLastSearchParams(m_currentSearchParams);
- }
- // Append the raw text of the search to the search history (i.e. without \
conversion
- // from Vim-style regex; without case-sensitivity markers stripped; etc.
- // Vim does this even if the search was aborted, so we follow suit.
- m_viInputModeManager->globalState()->searchHistory()->append(m_edit->text());
-}
-
-CompletionStartParams EmulatedCommandBar::SearchMode::completionInvoked ( \
Completer::CompletionInvocation invocationType )
-{
- Q_UNUSED(invocationType);
- return activateSearchHistoryCompletion();
-}
-
-void EmulatedCommandBar::SearchMode::completionChosen()
-{
- // Choose completion with Enter/ Return -> close bar (the search will have \
already taken effect at this point), marking as not aborted .
- close(false);
-}
-
-CompletionStartParams \
EmulatedCommandBar::SearchMode::activateSearchHistoryCompletion()
-{
- return CompletionStartParams::createModeSpecific(reversed(m_viInputModeManager->globalState()->searchHistory()->items()), \
0);
-}
-
-void EmulatedCommandBar::SearchMode::setBarBackground ( \
EmulatedCommandBar::SearchMode::BarBackgroundStatus status )
-{
- QPalette barBackground(m_edit->palette());
- switch (status) {
- case MatchFound: {
- KColorScheme::adjustBackground(barBackground, \
KColorScheme::PositiveBackground);
- break;
- }
- case NoMatchFound: {
- KColorScheme::adjustBackground(barBackground, \
KColorScheme::NegativeBackground);
- break;
- }
- case Normal: {
- barBackground = QPalette();
- break;
- }
- }
- m_edit->setPalette(barBackground);
-}
-
EmulatedCommandBar::CommandMode::CommandMode ( EmulatedCommandBar* \
emulatedCommandBar, MatchHighlighter* matchHighlighter, KTextEditor::ViewPrivate* \
view, QLineEdit* edit, InteractiveSedReplaceMode *interactiveSedReplaceMode, \
Completer* completer) : ActiveMode ( emulatedCommandBar, matchHighlighter ),
m_edit(edit),
diff --git a/src/vimode/emulatedcommandbar/emulatedcommandbar.h \
b/src/vimode/emulatedcommandbar/emulatedcommandbar.h index 736a6dc..3831f0b 100644
--- a/src/vimode/emulatedcommandbar/emulatedcommandbar.h
+++ b/src/vimode/emulatedcommandbar/emulatedcommandbar.h
@@ -40,6 +40,7 @@ namespace KateVi
{
class MatchHighlighter;
class InteractiveSedReplaceMode;
+class SearchMode;
/**
* A KateViewBarWidget that attempts to emulate some of the features of Vim's own \
command bar, @@ -87,39 +88,6 @@ private:
QScopedPointer<MatchHighlighter> m_matchHighligher;
QScopedPointer<Completer> m_completer;
- class SearchMode : public ActiveMode
- {
- public:
- SearchMode(EmulatedCommandBar* emulatedCommandBar, MatchHighlighter* \
matchHighlighter, KTextEditor::ViewPrivate* view, QLineEdit* edit);
- virtual ~SearchMode()
- {
- };
- enum class SearchDirection { Forward, Backward };
- void init(SearchDirection);
- void setViInputModeManager(InputModeManager *viInputModeManager);
- virtual bool handleKeyPress ( const QKeyEvent* keyEvent );
- virtual void editTextChanged(const QString &newText);
- virtual CompletionStartParams \
completionInvoked(Completer::CompletionInvocation invocationType);
- virtual void completionChosen();
- virtual void deactivate(bool wasAborted);
- bool isSendingSyntheticSearchCompletedKeypress() const
- {
- return m_isSendingSyntheticSearchCompletedKeypress;
- }
- private:
- EmulatedCommandBar *m_emulatedCommandBar = nullptr;
- KTextEditor::ViewPrivate *m_view = nullptr;
- InputModeManager *m_viInputModeManager = nullptr;
- QLineEdit *m_edit = nullptr;
- SearchDirection m_searchDirection;
- KTextEditor::Cursor m_startingCursorPos;
- KateVi::Searcher::SearchParams m_currentSearchParams;
- CompletionStartParams activateSearchHistoryCompletion();
- enum BarBackgroundStatus { Normal, MatchFound, NoMatchFound };
- void setBarBackground(BarBackgroundStatus status);
- bool m_isSendingSyntheticSearchCompletedKeypress = false;
- };
-
class CommandMode : public ActiveMode
{
public:
diff --git a/src/vimode/emulatedcommandbar/searchmode.cpp \
b/src/vimode/emulatedcommandbar/searchmode.cpp new file mode 100644
index 0000000..20c0128
--- /dev/null
+++ b/src/vimode/emulatedcommandbar/searchmode.cpp
@@ -0,0 +1,373 @@
+#include "searchmode.h"
+
+#include <vimode/inputmodemanager.h>
+#include <vimode/modes/modebase.h>
+#include "../globalstate.h"
+#include "../history.h"
+#include "kateview.h"
+#include "katedocument.h"
+
+#include <KColorScheme>
+
+#include <QLineEdit>
+#include <QApplication>
+
+using namespace KateVi;
+
+namespace
+{
+ bool isCharEscaped(const QString &string, int charPos)
+ {
+ if (charPos == 0) {
+ return false;
+ }
+ int numContiguousBackslashesToLeft = 0;
+ charPos--;
+ while (charPos >= 0 && string[charPos] == QLatin1Char('\\')) {
+ numContiguousBackslashesToLeft++;
+ charPos--;
+ }
+ return ((numContiguousBackslashesToLeft % 2) == 1);
+ }
+
+ QString toggledEscaped(const QString &originalString, QChar escapeChar)
+ {
+ int searchFrom = 0;
+ QString toggledEscapedString = originalString;
+ do {
+ const int indexOfEscapeChar = toggledEscapedString.indexOf(escapeChar, \
searchFrom); + if (indexOfEscapeChar == -1) {
+ break;
+ }
+ if (!isCharEscaped(toggledEscapedString, indexOfEscapeChar)) {
+ // Escape.
+ toggledEscapedString.replace(indexOfEscapeChar, 1, \
QLatin1String("\\") + escapeChar); + searchFrom = indexOfEscapeChar + \
2; + } else {
+ // Unescape.
+ toggledEscapedString.remove(indexOfEscapeChar - 1, 1);
+ searchFrom = indexOfEscapeChar;
+ }
+ } while (true);
+
+ return toggledEscapedString;
+ }
+
+ int findPosOfSearchConfigMarker(const QString &searchText, const bool \
isSearchBackwards) + {
+ const QChar searchConfigMarkerChar = (isSearchBackwards ? QLatin1Char('?') : \
QLatin1Char('/')); + for (int pos = 0; pos < searchText.length(); pos++) {
+ if (searchText.at(pos) == searchConfigMarkerChar) {
+ if (!isCharEscaped(searchText, pos)) {
+ return pos;
+ }
+ }
+ }
+ return -1;
+ }
+
+ bool isRepeatLastSearch(const QString &searchText, const bool isSearchBackwards)
+ {
+ const int posOfSearchConfigMarker = findPosOfSearchConfigMarker(searchText, \
isSearchBackwards); + if (posOfSearchConfigMarker != -1) {
+ if (searchText.leftRef(posOfSearchConfigMarker).isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool shouldPlaceCursorAtEndOfMatch(const QString &searchText, const bool \
isSearchBackwards) + {
+ const int posOfSearchConfigMarker = findPosOfSearchConfigMarker(searchText, \
isSearchBackwards); + if (posOfSearchConfigMarker != -1) {
+ if (searchText.length() > posOfSearchConfigMarker + 1 && \
searchText.at(posOfSearchConfigMarker + 1) == QLatin1Char('e')) { + \
return true; + }
+ }
+ return false;
+ }
+
+ QString withSearchConfigRemoved(const QString &originalSearchText, const bool \
isSearchBackwards) + {
+ const int posOfSearchConfigMarker = \
findPosOfSearchConfigMarker(originalSearchText, isSearchBackwards); + if \
(posOfSearchConfigMarker == -1) { + return originalSearchText;
+ } else {
+ return originalSearchText.left(posOfSearchConfigMarker);
+ }
+ }
+}
+
+QString KateVi::vimRegexToQtRegexPattern(const QString &vimRegexPattern)
+{
+ QString qtRegexPattern = vimRegexPattern;
+ qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char('('));
+ qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char(')'));
+ qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char('+'));
+ qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char('|'));
+ qtRegexPattern = ensuredCharEscaped(qtRegexPattern, QLatin1Char('?'));
+ {
+ // All curly brackets, except the closing curly bracket of a matching pair \
where the opening bracket is escaped, + // must have their escaping toggled.
+ bool lookingForMatchingCloseBracket = false;
+ QList<int> matchingClosedCurlyBracketPositions;
+ for (int i = 0; i < qtRegexPattern.length(); i++) {
+ if (qtRegexPattern[i] == QLatin1Char('{') && \
isCharEscaped(qtRegexPattern, i)) { + lookingForMatchingCloseBracket = \
true; + }
+ if (qtRegexPattern[i] == QLatin1Char('}') && \
lookingForMatchingCloseBracket && qtRegexPattern[i - 1] != QLatin1Char('\\')) { + \
matchingClosedCurlyBracketPositions.append(i); + }
+ }
+ if (matchingClosedCurlyBracketPositions.isEmpty()) {
+ // Escape all {'s and }'s - there are no matching pairs.
+ qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char('{'));
+ qtRegexPattern = toggledEscaped(qtRegexPattern, QLatin1Char('}'));
+ } else {
+ // Ensure that every chunk of qtRegexPattern that does *not* contain a \
curly closing bracket + // that is matched have their { and } escaping \
toggled. + QString qtRegexPatternNonMatchingCurliesToggled;
+ int previousNonMatchingClosedCurlyPos = 0; // i.e. the position of the \
last character which is either + // not a curly closing bracket, or is a \
curly closing bracket + // that is not matched.
+ foreach (int matchingClosedCurlyPos, \
matchingClosedCurlyBracketPositions) { + QString \
chunkExcludingMatchingCurlyClosed = \
qtRegexPattern.mid(previousNonMatchingClosedCurlyPos, matchingClosedCurlyPos - \
previousNonMatchingClosedCurlyPos); + \
chunkExcludingMatchingCurlyClosed = toggledEscaped(chunkExcludingMatchingCurlyClosed, \
QLatin1Char('{')); + chunkExcludingMatchingCurlyClosed = \
toggledEscaped(chunkExcludingMatchingCurlyClosed, QLatin1Char('}')); + \
qtRegexPatternNonMatchingCurliesToggled += chunkExcludingMatchingCurlyClosed + + \
qtRegexPattern[matchingClosedCurlyPos]; + \
previousNonMatchingClosedCurlyPos = matchingClosedCurlyPos + 1; + }
+ QString chunkAfterLastMatchingClosedCurly = \
qtRegexPattern.mid(matchingClosedCurlyBracketPositions.last() + 1); + \
chunkAfterLastMatchingClosedCurly = toggledEscaped(chunkAfterLastMatchingClosedCurly, \
QLatin1Char('{')); + chunkAfterLastMatchingClosedCurly = \
toggledEscaped(chunkAfterLastMatchingClosedCurly, QLatin1Char('}')); + \
qtRegexPatternNonMatchingCurliesToggled += chunkAfterLastMatchingClosedCurly; +
+ qtRegexPattern = qtRegexPatternNonMatchingCurliesToggled;
+ }
+
+ }
+
+ // All square brackets, *except* for those that are a) unescaped; and b) form a \
matching pair, must be + // escaped.
+ bool lookingForMatchingCloseBracket = false;
+ int openingBracketPos = -1;
+ QList<int> matchingSquareBracketPositions;
+ for (int i = 0; i < qtRegexPattern.length(); i++) {
+ if (qtRegexPattern[i] == QLatin1Char('[') && !isCharEscaped(qtRegexPattern, \
i) && !lookingForMatchingCloseBracket) { + lookingForMatchingCloseBracket \
= true; + openingBracketPos = i;
+ }
+ if (qtRegexPattern[i] == QLatin1Char(']') && lookingForMatchingCloseBracket \
&& !isCharEscaped(qtRegexPattern, i)) { + lookingForMatchingCloseBracket = \
false; + matchingSquareBracketPositions.append(openingBracketPos);
+ matchingSquareBracketPositions.append(i);
+ }
+ }
+
+ if (matchingSquareBracketPositions.isEmpty()) {
+ // Escape all ['s and ]'s - there are no matching pairs.
+ qtRegexPattern = ensuredCharEscaped(qtRegexPattern, QLatin1Char('['));
+ qtRegexPattern = ensuredCharEscaped(qtRegexPattern, QLatin1Char(']'));
+ } else {
+ // Ensure that every chunk of qtRegexPattern that does *not* contain one of \
the matching pairs of + // square brackets have their square brackets escaped.
+ QString qtRegexPatternNonMatchingSquaresMadeLiteral;
+ int previousNonMatchingSquareBracketPos = 0; // i.e. the position of the \
last character which is + // either not a square bracket, or is a square \
bracket but + // which is not matched.
+ foreach (int matchingSquareBracketPos, matchingSquareBracketPositions) {
+ QString chunkExcludingMatchingSquareBrackets = \
qtRegexPattern.mid(previousNonMatchingSquareBracketPos, matchingSquareBracketPos - \
previousNonMatchingSquareBracketPos); + \
chunkExcludingMatchingSquareBrackets = \
ensuredCharEscaped(chunkExcludingMatchingSquareBrackets, QLatin1Char('[')); + \
chunkExcludingMatchingSquareBrackets = \
ensuredCharEscaped(chunkExcludingMatchingSquareBrackets, QLatin1Char(']')); + \
qtRegexPatternNonMatchingSquaresMadeLiteral += chunkExcludingMatchingSquareBrackets + \
qtRegexPattern[matchingSquareBracketPos]; + \
previousNonMatchingSquareBracketPos = matchingSquareBracketPos + 1; + }
+ QString chunkAfterLastMatchingSquareBracket = \
qtRegexPattern.mid(matchingSquareBracketPositions.last() + 1); + \
chunkAfterLastMatchingSquareBracket = \
ensuredCharEscaped(chunkAfterLastMatchingSquareBracket, QLatin1Char('[')); + \
chunkAfterLastMatchingSquareBracket = \
ensuredCharEscaped(chunkAfterLastMatchingSquareBracket, QLatin1Char(']')); + \
qtRegexPatternNonMatchingSquaresMadeLiteral += chunkAfterLastMatchingSquareBracket; +
+ qtRegexPattern = qtRegexPatternNonMatchingSquaresMadeLiteral;
+ }
+
+ qtRegexPattern = qtRegexPattern.replace(QLatin1String("\\>"), \
QLatin1String("\\b")); + qtRegexPattern = \
qtRegexPattern.replace(QLatin1String("\\<"), QLatin1String("\\b")); +
+ return qtRegexPattern;
+}
+
+QString KateVi::ensuredCharEscaped(const QString &originalString, QChar \
charToEscape) +{
+ QString escapedString = originalString;
+ for (int i = 0; i < escapedString.length(); i++) {
+ if (escapedString[i] == charToEscape && !isCharEscaped(escapedString, i)) {
+ escapedString.replace(i, 1, QLatin1String("\\") + charToEscape);
+ }
+ }
+ return escapedString;
+}
+
+QString KateVi::withCaseSensitivityMarkersStripped(const QString \
&originalSearchTerm) +{
+ // Only \C is handled, for now - I'll implement \c if someone asks for it.
+ int pos = 0;
+ QString caseSensitivityMarkersStripped = originalSearchTerm;
+ while (pos < caseSensitivityMarkersStripped.length()) {
+ if (caseSensitivityMarkersStripped.at(pos) == QLatin1Char('C') && \
isCharEscaped(caseSensitivityMarkersStripped, pos)) { + \
caseSensitivityMarkersStripped.replace(pos - 1, 2, QString()); + pos--;
+ }
+ pos++;
+ }
+ return caseSensitivityMarkersStripped;
+}
+
+QStringList KateVi::reversed(const QStringList &originalList)
+{
+ QStringList reversedList = originalList;
+ std::reverse(reversedList.begin(), reversedList.end());
+ return reversedList;
+}
+
+SearchMode::SearchMode(EmulatedCommandBar* emulatedCommandBar, MatchHighlighter* \
matchHighlighter, KTextEditor::ViewPrivate* view, QLineEdit* edit) + : ActiveMode \
( emulatedCommandBar, matchHighlighter), + \
m_emulatedCommandBar(emulatedCommandBar), + m_view(view),
+ m_edit(edit)
+{
+}
+
+void SearchMode::init ( SearchMode::SearchDirection searchDirection)
+{
+ m_searchDirection = searchDirection;
+ m_startingCursorPos = m_view->cursorPosition();
+}
+
+void SearchMode::setViInputModeManager ( InputModeManager* viInputModeManager )
+{
+ m_viInputModeManager = viInputModeManager;
+}
+
+bool SearchMode::handleKeyPress ( const QKeyEvent* keyEvent )
+{
+ Q_UNUSED(keyEvent);
+ return false;
+}
+
+void SearchMode::editTextChanged ( const QString& newText )
+{
+ QString qtRegexPattern = newText;
+ const bool searchBackwards = (m_searchDirection == SearchDirection::Backward);
+ const bool placeCursorAtEndOfMatch = \
shouldPlaceCursorAtEndOfMatch(qtRegexPattern, searchBackwards); + if \
(isRepeatLastSearch(qtRegexPattern, searchBackwards)) { + qtRegexPattern = \
m_viInputModeManager->searcher()->getLastSearchPattern(); + } else {
+ qtRegexPattern = withSearchConfigRemoved(qtRegexPattern, searchBackwards);
+ qtRegexPattern = vimRegexToQtRegexPattern(qtRegexPattern);
+ }
+
+ // Decide case-sensitivity via SmartCase (note: if the expression contains \C, \
the "case-sensitive" marker, then + // we will be case-sensitive "by coincidence", \
as it were.). + bool caseSensitive = true;
+ if (qtRegexPattern.toLower() == qtRegexPattern) {
+ caseSensitive = false;
+ }
+
+ qtRegexPattern = withCaseSensitivityMarkersStripped(qtRegexPattern);
+
+ m_currentSearchParams.pattern = qtRegexPattern;
+ m_currentSearchParams.isCaseSensitive = caseSensitive;
+ m_currentSearchParams.isBackwards = searchBackwards;
+ m_currentSearchParams.shouldPlaceCursorAtEndOfMatch = placeCursorAtEndOfMatch;
+
+ // The "count" for the current search is not shared between Visual & Normal \
mode, so we need to pick + // the right one to handle the counted search.
+ int c = m_viInputModeManager->getCurrentViModeHandler()->getCount();
+ KTextEditor::Range match = \
m_viInputModeManager->searcher()->findPattern(m_currentSearchParams, \
m_startingCursorPos, c, false /* Don't add incremental searches to search history \
*/); +
+ if (match.isValid()) {
+ // The returned range ends one past the last character of the match, so \
adjust. + KTextEditor::Cursor realMatchEnd = \
KTextEditor::Cursor(match.end().line(), match.end().column() - 1); + if \
(realMatchEnd.column() == -1) { + realMatchEnd = \
KTextEditor::Cursor(realMatchEnd.line() - 1, \
m_view->doc()->lineLength(realMatchEnd.line() - 1)); + }
+ moveCursorTo(placeCursorAtEndOfMatch ? realMatchEnd : match.start());
+ setBarBackground(SearchMode::MatchFound);
+ } else {
+ moveCursorTo(m_startingCursorPos);
+ if (!m_edit->text().isEmpty()) {
+ setBarBackground(SearchMode::NoMatchFound);
+ } else {
+ setBarBackground(SearchMode::Normal);
+ }
+ }
+
+ updateMatchHighlight(match);
+}
+
+void SearchMode::deactivate(bool wasAborted)
+{
+ // "Deactivate" can be called multiple times between init()'s, so only reset the \
cursor once! + if (m_startingCursorPos.isValid()) {
+ if (wasAborted) {
+ moveCursorTo(m_startingCursorPos);
+ }
+ }
+ m_startingCursorPos = KTextEditor::Cursor::invalid();
+ setBarBackground(SearchMode::Normal);
+ // Send a synthetic keypress through the system that signals whether the search \
was aborted or + // not. If not, the keypress will "complete" the search motion, \
thus triggering it. + // We send to KateViewInternal as it updates the status bar \
and removes the "?". + const Qt::Key syntheticSearchCompletedKey = (wasAborted ? \
static_cast<Qt::Key>(0) : Qt::Key_Enter); + QKeyEvent \
syntheticSearchCompletedKeyPress(QEvent::KeyPress, syntheticSearchCompletedKey, \
Qt::NoModifier); + m_isSendingSyntheticSearchCompletedKeypress = true;
+ QApplication::sendEvent(m_view->focusProxy(), \
&syntheticSearchCompletedKeyPress); + m_isSendingSyntheticSearchCompletedKeypress \
= false; + if (!wasAborted) {
+ // Search was actually executed, so store it as the last search.
+ m_viInputModeManager->searcher()->setLastSearchParams(m_currentSearchParams);
+ }
+ // Append the raw text of the search to the search history (i.e. without \
conversion + // from Vim-style regex; without case-sensitivity markers stripped; \
etc. + // Vim does this even if the search was aborted, so we follow suit.
+ m_viInputModeManager->globalState()->searchHistory()->append(m_edit->text());
+}
+
+CompletionStartParams SearchMode::completionInvoked ( \
Completer::CompletionInvocation invocationType ) +{
+ Q_UNUSED(invocationType);
+ return activateSearchHistoryCompletion();
+}
+
+void SearchMode::completionChosen()
+{
+ // Choose completion with Enter/ Return -> close bar (the search will have \
already taken effect at this point), marking as not aborted . + close(false);
+}
+
+CompletionStartParams SearchMode::activateSearchHistoryCompletion()
+{
+ return CompletionStartParams::createModeSpecific(reversed(m_viInputModeManager->globalState()->searchHistory()->items()), \
0); +}
+
+void SearchMode::setBarBackground ( SearchMode::BarBackgroundStatus status )
+{
+ QPalette barBackground(m_edit->palette());
+ switch (status) {
+ case MatchFound: {
+ KColorScheme::adjustBackground(barBackground, \
KColorScheme::PositiveBackground); + break;
+ }
+ case NoMatchFound: {
+ KColorScheme::adjustBackground(barBackground, \
KColorScheme::NegativeBackground); + break;
+ }
+ case Normal: {
+ barBackground = QPalette();
+ break;
+ }
+ }
+ m_edit->setPalette(barBackground);
+}
diff --git a/src/vimode/emulatedcommandbar/searchmode.h \
b/src/vimode/emulatedcommandbar/searchmode.h new file mode 100644
index 0000000..f691536
--- /dev/null
+++ b/src/vimode/emulatedcommandbar/searchmode.h
@@ -0,0 +1,55 @@
+#ifndef KATEVI_EMULATED_COMMAND_BAR_SEARCHMODE_H
+#define KATEVI_EMULATED_COMMAND_BAR_SEARCHMODE_H
+
+#include "activemode.h"
+#include "../searcher.h"
+
+namespace KTextEditor {
+ class ViewPrivate;
+}
+
+#include <KTextEditor/Cursor>
+
+namespace KateVi
+{
+class EmulatedCommandBar;
+QString vimRegexToQtRegexPattern(const QString &vimRegexPattern); // TODO - move \
these generic helper functions into their own file? +QString \
withCaseSensitivityMarkersStripped(const QString &originalSearchTerm); +QString \
ensuredCharEscaped(const QString &originalString, QChar charToEscape); +QStringList \
reversed(const QStringList &originalList); +
+class SearchMode : public ActiveMode
+{
+public:
+ SearchMode(EmulatedCommandBar* emulatedCommandBar, MatchHighlighter* \
matchHighlighter, KTextEditor::ViewPrivate* view, QLineEdit* edit); + virtual \
~SearchMode() + {
+ };
+ enum class SearchDirection { Forward, Backward };
+ void init(SearchDirection);
+ void setViInputModeManager(InputModeManager *viInputModeManager);
+ virtual bool handleKeyPress ( const QKeyEvent* keyEvent );
+ virtual void editTextChanged(const QString &newText);
+ virtual CompletionStartParams completionInvoked(Completer::CompletionInvocation \
invocationType); + virtual void completionChosen();
+ virtual void deactivate(bool wasAborted);
+ bool isSendingSyntheticSearchCompletedKeypress() const
+ {
+ return m_isSendingSyntheticSearchCompletedKeypress;
+ }
+private:
+ EmulatedCommandBar *m_emulatedCommandBar = nullptr;
+ KTextEditor::ViewPrivate *m_view = nullptr;
+ InputModeManager *m_viInputModeManager = nullptr;
+ QLineEdit *m_edit = nullptr;
+ SearchDirection m_searchDirection;
+ KTextEditor::Cursor m_startingCursorPos;
+ KateVi::Searcher::SearchParams m_currentSearchParams;
+ CompletionStartParams activateSearchHistoryCompletion();
+ enum BarBackgroundStatus { Normal, MatchFound, NoMatchFound };
+ void setBarBackground(BarBackgroundStatus status);
+ bool m_isSendingSyntheticSearchCompletedKeypress = false;
+};
+}
+
+#endif
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic