8#include "globalstate.h"
10#include "kateconfig.h"
11#include "katedocument.h"
13#include <vimode/inputmodemanager.h>
14#include <vimode/modes/modebase.h>
16using namespace KateVi;
18Searcher::Searcher(InputModeManager *manager)
19 : m_viInputModeManager(manager)
20 , m_view(manager->view())
21 , m_lastHlSearchRange(
KTextEditor::Range::invalid())
22 , highlightMatchAttribute(new
KTextEditor::Attribute())
24 updateHighlightColors();
26 if (m_hlMode == HighlightMode::Enable) {
37const QString Searcher::getLastSearchPattern()
const
39 return m_lastSearchConfig.pattern;
42void Searcher::setLastSearchParams(
const SearchParams &searchParams)
44 if (!searchParams.pattern.isEmpty())
45 m_lastSearchConfig = searchParams;
48bool Searcher::lastSearchWrapped()
const
50 return m_lastSearchWrapped;
53void Searcher::findNext()
55 const Range r = motionFindNext();
57 m_viInputModeManager->getCurrentViModeHandler()->goToPos(r);
61void Searcher::findPrevious()
63 const Range r = motionFindPrev();
65 m_viInputModeManager->getCurrentViModeHandler()->goToPos(r);
69Range Searcher::motionFindNext(
int count)
71 Range match = findPatternForMotion(m_lastSearchConfig, m_view->cursorPosition(), count);
76 if (!m_lastSearchConfig.shouldPlaceCursorAtEndOfMatch) {
82Range Searcher::motionFindPrev(
int count)
85 lastSearchReversed.isBackwards = !lastSearchReversed.isBackwards;
86 Range match = findPatternForMotion(lastSearchReversed, m_view->cursorPosition(), count);
91 if (!m_lastSearchConfig.shouldPlaceCursorAtEndOfMatch) {
97Range Searcher::findPatternForMotion(
const SearchParams &searchParams,
const KTextEditor::Cursor startFrom,
int count)
99 if (searchParams.pattern.isEmpty()) {
103 KTextEditor::Range
match = findPatternWorker(searchParams, startFrom, count);
105 if (m_hlMode != HighlightMode::Disable) {
106 if (m_hlMode == HighlightMode::HideCurrent) {
107 m_hlMode = HighlightMode::Enable;
108 highlightVisibleResults(searchParams,
true);
110 highlightVisibleResults(searchParams);
117Range Searcher::findWordForMotion(
const QString &word,
bool backwards,
const KTextEditor::Cursor startFrom,
int count)
119 m_lastSearchConfig.isBackwards = backwards;
120 m_lastSearchConfig.isCaseSensitive =
false;
121 m_lastSearchConfig.shouldPlaceCursorAtEndOfMatch =
false;
123 m_viInputModeManager->globalState()->searchHistory()->append(QStringLiteral(
"\\<%1\\>").arg(word));
124 QString pattern = QStringLiteral(
"\\b%1\\b").arg(word);
125 m_lastSearchConfig.pattern = pattern;
126 if (m_hlMode == HighlightMode::HideCurrent)
127 m_hlMode = HighlightMode::Enable;
129 return findPatternForMotion(m_lastSearchConfig, startFrom, count);
132KTextEditor::Range Searcher::findPattern(
const SearchParams &searchParams,
const KTextEditor::Cursor startFrom,
int count,
bool addToSearchHistory)
134 if (addToSearchHistory) {
135 m_viInputModeManager->globalState()->searchHistory()->append(searchParams.pattern);
136 m_lastSearchConfig = searchParams;
139 KTextEditor::Range r = findPatternWorker(searchParams, startFrom, count);
141 if (m_hlMode != HighlightMode::Disable)
142 highlightVisibleResults(searchParams);
148void Searcher::highlightVisibleResults(
const SearchParams &searchParams,
bool force)
150 if (newPattern && searchParams.pattern.isEmpty())
153 auto vr = m_view->visibleRange();
158 if (!force && l.pattern == r.pattern && l.isCaseSensitive == r.isCaseSensitive && vr == m_lastHlSearchRange) {
162 m_lastHlSearchConfig = searchParams;
163 m_lastHlSearchRange = vr;
168 m_lastSearchWrapped =
false;
170 const QString &pattern = searchParams.pattern;
172 if (!searchParams.isCaseSensitive) {
176 KTextEditor::Range
match;
177 KTextEditor::Cursor current(vr.start());
180 match = m_view->doc()->searchText(KTextEditor::Range(current, vr.end()), pattern, flags).first();
181 if (
match.isValid()) {
186 highlight->setView(m_view);
187 highlight->setAttributeOnlyForViews(
true);
188 highlight->setZDepth(-10000.0);
189 highlight->setAttribute(highlightMatchAttribute);
190 m_hlRanges.append(highlight);
192 current =
match.end();
194 }
while (
match.isValid() && current < vr.end());
197void Searcher::clearHighlights()
199 if (!m_hlRanges.empty()) {
200 qDeleteAll(m_hlRanges);
205void Searcher::hideCurrentHighlight()
207 if (m_hlMode != HighlightMode::Disable) {
208 m_hlMode = HighlightMode::HideCurrent;
213void Searcher::updateHighlightColors()
215 const QColor foregroundColor = m_view->defaultStyleAttribute(KSyntaxHighlighting::Theme::TextStyle::Normal)->foreground().color();
216 const QColor &searchColor = m_view->rendererConfig()->searchHighlightColor();
218 highlightMatchAttribute->setForeground(foregroundColor);
219 highlightMatchAttribute->setBackground(searchColor);
222void Searcher::enableHighlightSearch(
bool enable)
225 m_hlMode = HighlightMode::Enable;
228 highlightVisibleResults(m_lastSearchConfig,
true);
230 m_hlMode = HighlightMode::Disable;
237bool Searcher::isHighlightSearchEnabled()
const
239 return m_hlMode != HighlightMode::Disable;
242void Searcher::disconnectSignals()
248void Searcher::connectSignals()
253 if (m_hlMode == HighlightMode::Enable)
254 highlightVisibleResults(m_lastHlSearchConfig);
257 if (m_hlMode == HighlightMode::Enable)
258 highlightVisibleResults(m_lastHlSearchConfig, true);
262void Searcher::patternDone(
bool wasAborted)
265 if (m_hlMode == HighlightMode::HideCurrent || m_lastSearchConfig.pattern.isEmpty())
267 else if (m_hlMode == HighlightMode::Enable)
268 highlightVisibleResults(m_lastSearchConfig);
271 if (m_hlMode == HighlightMode::HideCurrent)
272 m_hlMode = HighlightMode::Enable;
277KTextEditor::Range Searcher::findPatternWorker(
const SearchParams &searchParams,
const KTextEditor::Cursor startFrom,
int count)
279 KTextEditor::Cursor searchBegin = startFrom;
281 m_lastSearchWrapped =
false;
283 const QString &pattern = searchParams.pattern;
285 if (searchParams.isBackwards) {
288 if (!searchParams.isCaseSensitive) {
291 KTextEditor::Range finalMatch;
292 for (
int i = 0; i < count; i++) {
293 if (!searchParams.isBackwards) {
294 const KTextEditor::Range matchRange =
296 ->searchText(KTextEditor::Range(KTextEditor::Cursor(searchBegin.
line(), searchBegin.
column() + 1), m_view->doc()->documentEnd()),
302 finalMatch = matchRange;
305 const KTextEditor::Range wrappedMatchRange =
306 m_view->doc()->searchText(KTextEditor::Range(m_view->doc()->documentRange().start(), m_view->doc()->documentEnd()), pattern, flags).first();
307 if (wrappedMatchRange.
isValid()) {
308 finalMatch = wrappedMatchRange;
309 m_lastSearchWrapped =
true;
323 KTextEditor::Cursor newSearchBegin = KTextEditor::Cursor(searchBegin.
line(), m_view->doc()->lineLength(searchBegin.
line()));
326 QList<KTextEditor::Range> matchesUnfiltered =
327 m_view->doc()->searchText(KTextEditor::Range(newSearchBegin, m_view->doc()->documentRange().start()), pattern, flags);
329 if (matchesUnfiltered.
size() == 1 && !matchesUnfiltered.
first().isValid()) {
334 std::sort(matchesUnfiltered.
begin(), matchesUnfiltered.
end());
336 QList<KTextEditor::Range> filteredMatches;
337 for (KTextEditor::Range unfilteredMatch : std::as_const(matchesUnfiltered)) {
338 if (unfilteredMatch.start() < searchBegin) {
339 filteredMatches.
append(unfilteredMatch);
342 if (!filteredMatches.
isEmpty()) {
344 bestMatch = filteredMatches.
last();
350 if (filteredMatches.
isEmpty()) {
351 auto oldSearchBegin = newSearchBegin;
352 newSearchBegin = matchesUnfiltered.
first().start();
355 if (oldSearchBegin == newSearchBegin) {
362 KTextEditor::Range matchRange = bestMatch;
365 finalMatch = matchRange;
367 const KTextEditor::Range wrappedMatchRange =
368 m_view->doc()->searchText(KTextEditor::Range(m_view->doc()->documentEnd(), m_view->doc()->documentRange().start()), pattern, flags).first();
370 if (wrappedMatchRange.
isValid()) {
371 finalMatch = wrappedMatchRange;
372 m_lastSearchWrapped =
true;
378 searchBegin = finalMatch.
start();
constexpr int column() const noexcept
Retrieve the column on which this cursor is situated.
constexpr int line() const noexcept
Retrieve the line on which this cursor is situated.
void textChanged(KTextEditor::Document *document)
The document emits this signal whenever its text changes.
@ DoNotExpand
Don't expand to encapsulate new characters in either direction. This is the default.
constexpr Cursor start() const noexcept
Get the start position of this range.
static constexpr Range invalid() noexcept
Returns an invalid range.
constexpr bool isValid() const noexcept
Validity check.
void displayRangeChanged(KTextEditor::View *view)
This signal is emitted whenever the displayed range changes.
static constexpr Range invalid() noexcept
Returns an invalid range.
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
@ CaseInsensitive
Ignores cases, e.g. "a" matches "A".
@ Regex
Treats the pattern as a regular expression.
@ Backwards
Searches in backward direction.
QFlags< SearchOption > SearchOptions
Stores a combination of SearchOption values.
void append(QList< T > &&value)
bool isEmpty() const const
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
Extended searcher for Emulated Command Bar.