10#include "katesearchbar.h"
12#include "kateconfig.h"
13#include "katedocument.h"
14#include "kateglobal.h"
16#include "kateundomanager.h"
19#include <KTextEditor/DocumentCursor>
20#include <KTextEditor/Message>
21#include <KTextEditor/MovingRange>
23#include "ui_searchbarincremental.h"
24#include "ui_searchbarpower.h"
26#include <KColorScheme>
27#include <KLocalizedString>
33#include <QElapsedTimer>
36#include <QRegularExpression>
38#include <QStringListModel>
46#ifdef FAST_DEBUG_ENABLE
47#define FAST_DEBUG(x) qCDebug(LOG_KTE) << x
59 QList<QString> m_insertBefore;
60 QList<QString> m_insertAfter;
61 QSet<QAction *> m_actionPointers;
66 AddMenuManager(QMenu *parent,
int expectedItemCount)
67 : m_insertBefore(QList<QString>(expectedItemCount))
68 , m_insertAfter(QList<QString>(expectedItemCount))
72 Q_ASSERT(parent !=
nullptr);
74 if (m_menu ==
nullptr) {
80 void enableMenu(
bool enabled)
82 if (m_menu ==
nullptr) {
85 m_menu->setEnabled(enabled);
88 void addEntry(
const QString &before,
90 const QString &description,
91 const QString &realBefore = QString(),
92 const QString &realAfter = QString())
94 if (m_menu ==
nullptr) {
97 QAction *
const action = m_menu->addAction(before + after + QLatin1Char(
'\t') + description);
98 m_insertBefore[m_indexWalker] = QString(realBefore.isEmpty() ? before : realBefore);
99 m_insertAfter[m_indexWalker] = QString(realAfter.isEmpty() ? after : realAfter);
100 action->
setData(QVariant(m_indexWalker++));
101 m_actionPointers.insert(action);
106 if (m_menu ==
nullptr) {
109 m_menu->addSeparator();
112 void handle(QAction *action, QLineEdit *lineEdit)
114 if (!m_actionPointers.contains(action)) {
120 const QString &before = m_insertBefore[index];
121 const QString &after = m_insertAfter[index];
122 lineEdit->
insert(before + after);
130KateSearchBar::KateSearchBar(
bool initAsPower, KTextEditor::ViewPrivate *view, KateViewConfig *config)
131 : KateViewBarWidget(true, view)
137 , m_incInitCursor(view->cursorPosition())
139 , highlightMatchAttribute(new
Attribute())
140 , highlightReplacementAttribute(new
Attribute())
141 , m_incHighlightAll(false)
142 , m_incFromCursor(true)
143 , m_incMatchCase(false)
144 , m_powerMatchCase(true)
145 , m_powerFromCursor(false)
146 , m_powerHighlightAll(false)
151 connect(
this, &KateSearchBar::findOrReplaceAllFinished,
this, &KateSearchBar::endFindOrReplaceAll);
153 auto setSelectionChangedByUndoRedo = [
this]() {
154 m_selectionChangedByUndoRedo =
true;
156 auto unsetSelectionChangedByUndoRedo = [
this]() {
157 m_selectionChangedByUndoRedo =
false;
160 connect(docUndoManager, &KateUndoManager::undoStart,
this, setSelectionChangedByUndoRedo);
161 connect(docUndoManager, &KateUndoManager::undoEnd,
this, unsetSelectionChangedByUndoRedo);
162 connect(docUndoManager, &KateUndoManager::redoStart,
this, setSelectionChangedByUndoRedo);
163 connect(docUndoManager, &KateUndoManager::redoEnd,
this, unsetSelectionChangedByUndoRedo);
167 setSelectionOnly(false);
172 mouseInAttribute->setFontBold(
true);
176 caretInAttribute->setFontItalic(
true);
179 updateHighlightColors();
182 QWidget *
const widget = centralWidget();
184 m_layout->setContentsMargins(0, 0, 0, 0);
187 setMinimumWidth(100);
190 const auto searchFlags = m_config->searchFlags();
191 m_incHighlightAll = (searchFlags & KateViewConfig::IncHighlightAll) != 0;
192 m_incFromCursor = (searchFlags & KateViewConfig::IncFromCursor) != 0;
193 m_incMatchCase = (searchFlags & KateViewConfig::IncMatchCase) != 0;
194 m_powerMatchCase = (searchFlags & KateViewConfig::PowerMatchCase) != 0;
195 m_powerFromCursor = (searchFlags & KateViewConfig::PowerFromCursor) != 0;
196 m_powerHighlightAll = (searchFlags & KateViewConfig::PowerHighlightAll) != 0;
197 m_powerMode = ((searchFlags & KateViewConfig::PowerModeRegularExpression) != 0)
199 : (((searchFlags & KateViewConfig::PowerModeEscapeSequences) != 0)
200 ? MODE_ESCAPE_SEQUENCES
201 : (((searchFlags & KateViewConfig::PowerModeWholeWords) != 0) ? MODE_WHOLE_WORDS : MODE_PLAIN_TEXT));
207 enterIncrementalMode();
210 updateSelectionOnly();
213KateSearchBar::~KateSearchBar()
215 if (!m_cancelFindOrReplace) {
217 endFindOrReplaceAll();
226 if (m_workingRange) {
227 delete m_workingRange;
231void KateSearchBar::closed()
236 viewBar()->removeBarWidget(
this);
240 m_replacement.clear();
241 m_unfinishedSearchText.clear();
244void KateSearchBar::setReplacementPattern(
const QString &replacementPattern)
248 if (this->replacementPattern() == replacementPattern) {
252 m_powerUi->replacement->setEditText(replacementPattern);
255QString KateSearchBar::replacementPattern()
const
259 return m_powerUi->replacement->currentText();
262void KateSearchBar::setSearchMode(KateSearchBar::SearchMode mode)
266 m_powerUi->searchMode->setCurrentIndex(mode);
269void KateSearchBar::findNext()
271 const bool found = find();
274 QComboBox *combo = m_powerUi !=
nullptr ? m_powerUi->pattern : m_incUi->pattern;
277 addCurrentTextToHistory(combo);
281void KateSearchBar::findPrevious()
283 const bool found = find(SearchBackward);
286 QComboBox *combo = m_powerUi !=
nullptr ? m_powerUi->pattern : m_incUi->pattern;
289 addCurrentTextToHistory(combo);
293void KateSearchBar::showResultMessage()
298 text =
i18ncp(
"short translation",
"1 replacement made",
"%1 replacements made", m_matchCounter);
300 text =
i18ncp(
"short translation",
"1 match found",
"%1 matches found", m_matchCounter);
304 m_infoMessage->setText(text);
308 m_infoMessage->setAutoHide(3000);
309 m_infoMessage->setView(m_view);
310 m_view->doc()->postMessage(m_infoMessage);
314void KateSearchBar::highlightMatch(
Range range)
322 m_hlRanges.append(highlight);
325void KateSearchBar::highlightReplacement(
Range range)
333 m_hlRanges.append(highlight);
336void KateSearchBar::indicateMatch(MatchResult matchResult)
338 QLineEdit *
const lineEdit = isPower() ? m_powerUi->pattern->lineEdit() : m_incUi->pattern->lineEdit();
339 QPalette background(lineEdit->
palette());
341 switch (matchResult) {
343 case MatchWrappedForward:
344 case MatchWrappedBackward:
354 background = QPalette();
362 if (m_incUi !=
nullptr) {
363 QPalette foreground(m_incUi->status->palette());
364 switch (matchResult) {
368 m_incUi->status->clear();
370 case MatchWrappedForward:
371 case MatchWrappedBackward:
373 if (matchResult == MatchWrappedBackward) {
374 m_incUi->status->setText(
i18n(
"Reached top, continued from bottom"));
376 m_incUi->status->setText(
i18n(
"Reached bottom, continued from top"));
381 m_incUi->status->setText(
i18n(
"Not found"));
387 m_incUi->status->setPalette(foreground);
393 void KateSearchBar::selectRange(KTextEditor::ViewPrivate *view,
KTextEditor::Range range)
395 view->setCursorPositionInternal(range.
end());
396 view->setSelection(range);
402 selectRange(m_view, range);
406void KateSearchBar::onIncPatternChanged(
const QString &pattern)
415 m_incUi->next->setDisabled(pattern.
isEmpty());
416 m_incUi->prev->setDisabled(pattern.
isEmpty());
418 KateMatch
match(m_view->doc(), searchOptions());
422 const Range inputRange = KTextEditor::Range(m_incInitCursor, m_view->document()->documentEnd());
423 match.searchText(inputRange, pattern);
426 const bool wrap = !
match.isValid() && !pattern.
isEmpty();
430 const KTextEditor::Range inputRange = m_view->document()->documentRange();
431 match.searchText(inputRange, pattern);
434 const MatchResult matchResult =
match.isValid() ? (wrap ? MatchWrappedForward : MatchFound) : pattern.isEmpty() ? MatchNothing : MatchMismatch;
440 selectRange2(selectionRange);
443 indicateMatch(matchResult);
446void KateSearchBar::setMatchCase(
bool matchCase)
448 if (this->matchCase() == matchCase) {
453 m_powerUi->matchCase->setChecked(matchCase);
455 m_incUi->matchCase->setChecked(matchCase);
459void KateSearchBar::onMatchCaseToggled(
bool )
463 if (m_incUi !=
nullptr) {
465 const QString pattern = m_incUi->pattern->currentText();
466 onIncPatternChanged(pattern);
468 indicateMatch(MatchNothing);
472bool KateSearchBar::matchCase()
const
474 return isPower() ? m_powerUi->matchCase->isChecked() : m_incUi->matchCase->isChecked();
477void KateSearchBar::onReturnPressed()
496bool KateSearchBar::findOrReplace(SearchDirection searchDirection,
const QString *replacement)
499 if (searchPattern().isEmpty()) {
509 const SearchOptions enabledOptions = searchOptions(searchDirection);
515 if (selectionOnly()) {
516 if (m_workingRange ==
nullptr) {
520 if (!m_workingRange->toRange().isValid()) {
522 inputRange = selection;
525 m_workingRange->
setRange(selection);
529 if (searchDirection == SearchBackward) {
530 inputRange.
setRange(m_workingRange->start(), selection.
end());
532 inputRange.
setRange(selection.
start(), m_workingRange->end());
537 if (searchDirection == SearchForward) {
538 inputRange.
setRange(selection.
start(), m_view->document()->documentEnd());
540 inputRange.
setRange(Cursor(0, 0), selection.
end());
544 if (m_workingRange) {
545 delete m_workingRange;
546 m_workingRange =
nullptr;
551 setSelectionOnly(
false);
552 const Cursor cursorPos = m_view->cursorPosition();
553 if (searchDirection == SearchForward) {
554 inputRange.
setRange(cursorPos, m_view->document()->documentEnd());
556 inputRange.
setRange(Cursor(0, 0), cursorPos);
559 FAST_DEBUG(
"Search range is" << inputRange);
561 KateMatch
match(m_view->doc(), enabledOptions);
565 match.searchText(inputRange, searchPattern());
566 if (
match.isValid()) {
567 if (
match.range() == selection) {
569 if (replacement !=
nullptr) {
571 KTextEditor::MovingRange *smartInputRange =
573 afterReplace =
match.replace(*replacement, m_view->blockSelection());
574 inputRange = *smartInputRange;
575 delete smartInputRange;
579 if (searchDirection == SearchForward) {
580 const Cursor
start = (replacement !=
nullptr) ? afterReplace.
end() : selection.
end();
583 const Cursor
end = (replacement !=
nullptr) ? afterReplace.
start() : selection.
start();
587 match.searchText(inputRange, searchPattern());
589 }
else if (
match.isEmpty() &&
match.range().end() == m_view->cursorPosition()) {
592 KTextEditor::DocumentCursor zeroLenMatch(m_view->doc(),
match.range().end());
594 if (searchDirection == SearchForward) {
595 zeroLenMatch.move(1);
596 inputRange.
setRange(zeroLenMatch.toCursor(), inputRange.
end());
598 zeroLenMatch.move(-1);
599 inputRange.
setRange(inputRange.
start(), zeroLenMatch.toCursor());
602 match.searchText(inputRange, searchPattern());
606 bool askWrap = !
match.isValid() && (!afterReplace.
isValid() || !selectionOnly());
615 searchDirection == SearchForward ?
i18n(
"Bottom of file reached. Continue from top?") :
i18n(
"Top of file reached. Continue from bottom?");
618 i18n(
"Continue search?"),
621 QStringLiteral(
"DoNotShowAgainContinueSearchDialog"))
625 m_view->showSearchWrappedHint(searchDirection == SearchBackward);
626 if (selectionOnly() && m_workingRange !=
nullptr && m_workingRange->toRange().isValid()) {
627 inputRange = m_workingRange->toRange();
629 inputRange = m_view->document()->documentRange();
631 match.searchText(inputRange, searchPattern());
634 if (
match.isValid()) {
635 selectRange2(
match.range());
638 const MatchResult matchResult = !
match.isValid() ? MatchMismatch
640 : searchDirection == SearchForward ? MatchWrappedForward
641 : MatchWrappedBackward;
642 indicateMatch(matchResult);
646 highlightReplacement(afterReplace);
655void KateSearchBar::findAll()
660 Range inputRange = (m_view->selection() && selectionOnly()) ? m_view->selectionRange() : m_view->document()->documentRange();
662 beginFindAll(inputRange);
665void KateSearchBar::onPowerPatternChanged(
const QString & )
667 givePatternFeedback();
668 indicateMatch(MatchNothing);
671bool KateSearchBar::isPatternValid()
const
673 if (searchPattern().isEmpty()) {
677 return searchOptions().testFlag(
WholeWords) ? searchPattern().trimmed() == searchPattern()
678 : searchOptions().testFlag(
Regex) ? QRegularExpression(searchPattern(), QRegularExpression::UseUnicodePropertiesOption).
isValid()
682void KateSearchBar::givePatternFeedback()
685 m_powerUi->findNext->setEnabled(isPatternValid());
686 m_powerUi->findPrev->setEnabled(isPatternValid());
687 m_powerUi->replaceNext->setEnabled(isPatternValid());
688 m_powerUi->replaceAll->setEnabled(isPatternValid());
689 m_powerUi->findAll->setEnabled(isPatternValid());
692void KateSearchBar::addCurrentTextToHistory(
QComboBox *combo)
695 const int index = combo->
findText(text);
709void KateSearchBar::backupConfig(
bool ofPower)
712 m_powerMatchCase = m_powerUi->matchCase->isChecked();
713 m_powerMode = m_powerUi->searchMode->currentIndex();
715 m_incMatchCase = m_incUi->matchCase->isChecked();
719void KateSearchBar::sendConfig()
721 const auto pastFlags = m_config->searchFlags();
722 auto futureFlags = pastFlags;
724 if (m_powerUi !=
nullptr) {
725 const bool OF_POWER =
true;
726 backupConfig(OF_POWER);
729 const auto incFlagsOnly = pastFlags & (KateViewConfig::IncHighlightAll | KateViewConfig::IncFromCursor | KateViewConfig::IncMatchCase);
731 futureFlags = incFlagsOnly | (m_powerMatchCase ? KateViewConfig::PowerMatchCase : 0) | (m_powerFromCursor ? KateViewConfig::PowerFromCursor : 0)
732 | (m_powerHighlightAll ? KateViewConfig::PowerHighlightAll : 0)
733 | ((m_powerMode == MODE_REGEX)
734 ? KateViewConfig::PowerModeRegularExpression
735 : ((m_powerMode == MODE_ESCAPE_SEQUENCES)
736 ? KateViewConfig::PowerModeEscapeSequences
737 : ((m_powerMode == MODE_WHOLE_WORDS) ? KateViewConfig::PowerModeWholeWords : KateViewConfig::PowerModePlainText)));
739 }
else if (m_incUi !=
nullptr) {
740 const bool OF_INCREMENTAL =
false;
741 backupConfig(OF_INCREMENTAL);
744 const auto powerFlagsOnly = pastFlags
745 & (KateViewConfig::PowerMatchCase | KateViewConfig::PowerFromCursor | KateViewConfig::PowerHighlightAll | KateViewConfig::PowerModeRegularExpression
746 | KateViewConfig::PowerModeEscapeSequences | KateViewConfig::PowerModeWholeWords | KateViewConfig::PowerModePlainText);
748 futureFlags = powerFlagsOnly | (m_incHighlightAll ? KateViewConfig::IncHighlightAll : 0) | (m_incFromCursor ? KateViewConfig::IncFromCursor : 0)
749 | (m_incMatchCase ? KateViewConfig::IncMatchCase : 0);
753 m_config->setSearchFlags(futureFlags);
756void KateSearchBar::replaceNext()
758 const QString replacement = m_powerUi->replacement->currentText();
760 if (findOrReplace(SearchForward, &replacement)) {
762 m_view->doc()->undoManager()->undoSafePoint();
765 addCurrentTextToHistory(m_powerUi->pattern);
768 addCurrentTextToHistory(m_powerUi->replacement);
774void KateSearchBar::beginFindOrReplaceAll(
Range inputRange,
const QString &replacement,
bool replaceMode )
783 m_powerUi->searchCancelStacked->setCurrentIndex(m_powerUi->searchCancelStacked->indexOf(m_powerUi->cancelPage));
784 m_powerUi->findNext->setEnabled(
false);
785 m_powerUi->findPrev->setEnabled(
false);
786 m_powerUi->replaceNext->setEnabled(
false);
789 m_highlightRanges.clear();
790 m_inputRange = inputRange;
791 m_workingRange = m_view->doc()->newMovingRange(m_inputRange);
792 m_replacement = replacement;
793 m_replaceMode = replaceMode;
795 m_cancelFindOrReplace =
false;
800void KateSearchBar::findOrReplaceAll()
802 const SearchOptions enabledOptions = searchOptions(SearchForward);
806 const int maxHighlightings = 65536;
809 KateMatch
match(m_view->doc(), enabledOptions);
812 bool block = m_view->selection() && m_view->blockSelection() && selectionOnly();
814 int line = m_inputRange.start().line();
816 bool timeOut =
false;
821 int numLinesSearched = 0;
823 KTextEditor::Range workingRangeCopy = m_workingRange->toRange();
826 delete m_workingRange;
827 m_workingRange = m_view->doc()->newMovingRange(m_view->doc()->rangeOnLine(m_inputRange, line));
828 workingRangeCopy = m_workingRange->toRange();
832 int currentSearchLine = workingRangeCopy.
start().
line();
833 match.searchText(workingRangeCopy, searchPattern());
834 if (!
match.isValid()) {
838 bool const originalMatchEmpty =
match.isEmpty();
843 if (m_matchCounter == 0) {
844 static_cast<KTextEditor::DocumentPrivate *
>(m_view->document())->editStart();
848 lastRange =
match.replace(m_replacement,
false, ++m_matchCounter);
850 workingRangeCopy = m_workingRange->toRange();
852 lastRange =
match.range();
857 if (m_matchCounter < maxHighlightings) {
858 m_highlightRanges.push_back(lastRange);
860 m_highlightRanges.clear();
865 if (lastRange.
end() >= workingRangeCopy.
end()) {
870 KTextEditor::DocumentCursor workingStart(m_view->doc(), lastRange.
end());
872 if (originalMatchEmpty) {
875 workingStart.move(1);
877 workingRangeCopy.
setRange(workingStart.toCursor(), workingRangeCopy.
end());
880 if (!workingRangeCopy.
isValid() || workingStart.atEndOfDocument()) {
887 numLinesSearched += workingRangeCopy.
start().
line() - currentSearchLine;
888 timeOut = numLinesSearched >= 50000;
890 }
while (!m_cancelFindOrReplace && !timeOut);
892 }
while (!m_cancelFindOrReplace && !timeOut && block && ++line <= m_inputRange.end().line());
895 m_workingRange->setRange(workingRangeCopy);
897 if (done || m_cancelFindOrReplace) {
898 Q_EMIT findOrReplaceAllFinished();
899 }
else if (timeOut) {
906void KateSearchBar::endFindOrReplaceAll()
912 if (m_matchCounter > 0) {
914 static_cast<KTextEditor::DocumentPrivate *
>(m_view->document())->editEnd();
919 if (!m_highlightRanges.empty()) {
920 m_view->document()->setMarkDescription(KTextEditor::Document::SearchMatch,
i18n(
"SearchHighLight"));
921 m_view->document()->setMarkIcon(KTextEditor::Document::SearchMatch, QIcon());
922 for (
const Range &r : m_highlightRanges) {
923 m_view->document()->addMark(r.start().line(), KTextEditor::Document::SearchMatch);
929 for (
const Range &r : std::as_const(m_highlightRanges)) {
930 highlightReplacement(r);
933 m_view->doc()->undoManager()->undoSafePoint();
936 for (
const Range &r : std::as_const(m_highlightRanges)) {
943 delete m_workingRange;
944 m_workingRange =
nullptr;
951 m_powerUi->searchCancelStacked->setCurrentIndex(m_powerUi->searchCancelStacked->indexOf(m_powerUi->searchPage));
952 m_powerUi->findNext->setEnabled(
true);
953 m_powerUi->findPrev->setEnabled(
true);
954 m_powerUi->replaceNext->setEnabled(
true);
957 addCurrentTextToHistory(m_powerUi->pattern);
960 addCurrentTextToHistory(m_powerUi->replacement);
963 m_cancelFindOrReplace =
true;
966void KateSearchBar::replaceAll()
972 const QString replacement = m_powerUi->replacement->currentText();
975 const bool selected = m_view->selection();
976 Range inputRange = (selected && selectionOnly()) ? m_view->selectionRange() : m_view->document()->documentRange();
978 beginFindOrReplaceAll(inputRange, replacement);
981void KateSearchBar::setSearchPattern(
const QString &searchPattern)
983 if (searchPattern == this->searchPattern()) {
988 m_powerUi->pattern->setEditText(searchPattern);
990 m_incUi->pattern->setEditText(searchPattern);
994QString KateSearchBar::searchPattern()
const
996 return (m_powerUi !=
nullptr) ? m_powerUi->pattern->currentText() : m_incUi->pattern->currentText();
999void KateSearchBar::setSelectionOnly(
bool selectionOnly)
1001 if (this->selectionOnly() == selectionOnly) {
1006 m_powerUi->selectionOnly->setChecked(selectionOnly);
1010bool KateSearchBar::selectionOnly()
const
1012 return isPower() ? m_powerUi->selectionOnly->isChecked() :
false;
1023 if (searchDirection == SearchBackward) {
1027 if (m_powerUi !=
nullptr) {
1028 switch (m_powerUi->searchMode->currentIndex()) {
1029 case MODE_WHOLE_WORDS:
1033 case MODE_ESCAPE_SEQUENCES:
1038 enabledOptions |=
Regex;
1041 case MODE_PLAIN_TEXT:
1047 return enabledOptions;
1058 QList<QString> capturePatterns;
1060 QStack<ParInfo> parInfos;
1062 const int inputLen = pattern.
length();
1064 bool insideClass =
false;
1065 int captureCount = 0;
1067 while (input < inputLen) {
1070 if (pattern[input].unicode() == L
']') {
1071 insideClass =
false;
1075 switch (pattern[input].unicode()) {
1083 curInfo.openIndex = input;
1084 curInfo.capturing = (input + 1 >= inputLen) || (pattern[input + 1].unicode() !=
'?');
1085 if (curInfo.capturing) {
1088 curInfo.captureNumber = captureCount;
1089 parInfos.
push(curInfo);
1095 if (!parInfos.
empty()) {
1096 ParInfo &top = parInfos.
top();
1097 if (top.capturing && (top.captureNumber <= 9)) {
1098 const int start = top.openIndex + 1;
1099 const int len = input -
start;
1100 if (capturePatterns.
size() < top.captureNumber) {
1101 capturePatterns.
resize(top.captureNumber);
1103 capturePatterns[top.captureNumber - 1] = pattern.
mid(
start, len);
1123 return capturePatterns;
1126void KateSearchBar::showExtendedContextMenu(
bool forPattern,
const QPoint &pos)
1129 QComboBox *comboBox = forPattern ? m_powerUi->pattern : m_powerUi->replacement;
1132 if (contextMenu ==
nullptr) {
1136 bool extendMenu =
false;
1137 bool regexMode =
false;
1138 switch (m_powerUi->searchMode->currentIndex()) {
1143 case MODE_ESCAPE_SEQUENCES:
1151 AddMenuManager addMenuManager(contextMenu, 37);
1153 addMenuManager.enableMenu(extendMenu);
1158 addMenuManager.addEntry(QStringLiteral(
"^"), QString(),
i18n(
"Beginning of line"));
1159 addMenuManager.addEntry(QStringLiteral(
"$"), QString(),
i18n(
"End of line"));
1160 addMenuManager.addSeparator();
1161 addMenuManager.addEntry(QStringLiteral(
"."), QString(),
i18n(
"Match any character excluding new line (by default)"));
1162 addMenuManager.addEntry(QStringLiteral(
"+"), QString(),
i18n(
"One or more occurrences"));
1163 addMenuManager.addEntry(QStringLiteral(
"*"), QString(),
i18n(
"Zero or more occurrences"));
1164 addMenuManager.addEntry(QStringLiteral(
"?"), QString(),
i18n(
"Zero or one occurrences"));
1165 addMenuManager.addEntry(QStringLiteral(
"{a"),
1166 QStringLiteral(
",b}"),
1167 i18n(
"<a> through <b> occurrences"),
1168 QStringLiteral(
"{"),
1169 QStringLiteral(
",}"));
1171 addMenuManager.addSeparator();
1172 addMenuManager.addSeparator();
1173 addMenuManager.addEntry(QStringLiteral(
"("), QStringLiteral(
")"),
i18n(
"Group, capturing"));
1174 addMenuManager.addEntry(QStringLiteral(
"|"), QString(),
i18n(
"Or"));
1175 addMenuManager.addEntry(QStringLiteral(
"["), QStringLiteral(
"]"),
i18n(
"Set of characters"));
1176 addMenuManager.addEntry(QStringLiteral(
"[^"), QStringLiteral(
"]"),
i18n(
"Negative set of characters"));
1177 addMenuManager.addSeparator();
1180 addMenuManager.addEntry(QStringLiteral(
"\\0"), QString(),
i18n(
"Whole match reference"));
1181 addMenuManager.addSeparator();
1183 const QString pattern = m_powerUi->pattern->currentText();
1184 const QList<QString> capturePatterns = getCapturePatterns(pattern);
1186 const int captureCount = capturePatterns.
count();
1187 for (
int i = 1; i <= 9; i++) {
1189 const QString &captureDetails =
1190 (i <= captureCount) ? QLatin1String(
" = (") + QStringView(capturePatterns[i - 1]).left(30) + QLatin1Char(
')') : QString();
1191 addMenuManager.addEntry(QLatin1String(
"\\") + number, QString(),
i18n(
"Reference") + QLatin1Char(
' ') + number + captureDetails);
1194 addMenuManager.addSeparator();
1198 addMenuManager.addEntry(QStringLiteral(
"\\n"), QString(),
i18n(
"Line break"));
1199 addMenuManager.addEntry(QStringLiteral(
"\\t"), QString(),
i18n(
"Tab"));
1201 if (forPattern && regexMode) {
1202 addMenuManager.addEntry(QStringLiteral(
"\\b"), QString(),
i18n(
"Word boundary"));
1203 addMenuManager.addEntry(QStringLiteral(
"\\B"), QString(),
i18n(
"Not word boundary"));
1204 addMenuManager.addEntry(QStringLiteral(
"\\d"), QString(),
i18n(
"Digit"));
1205 addMenuManager.addEntry(QStringLiteral(
"\\D"), QString(),
i18n(
"Non-digit"));
1206 addMenuManager.addEntry(QStringLiteral(
"\\s"), QString(),
i18n(
"Whitespace (excluding line breaks)"));
1207 addMenuManager.addEntry(QStringLiteral(
"\\S"), QString(),
i18n(
"Non-whitespace"));
1208 addMenuManager.addEntry(QStringLiteral(
"\\w"), QString(),
i18n(
"Word character (alphanumerics plus '_')"));
1209 addMenuManager.addEntry(QStringLiteral(
"\\W"), QString(),
i18n(
"Non-word character"));
1212 addMenuManager.addEntry(QStringLiteral(
"\\0???"), QString(),
i18n(
"Octal character 000 to 377 (2^8-1)"), QStringLiteral(
"\\0"));
1213 addMenuManager.addEntry(QStringLiteral(
"\\x{????}"), QString(),
i18n(
"Hex character 0000 to FFFF (2^16-1)"), QStringLiteral(
"\\x{....}"));
1214 addMenuManager.addEntry(QStringLiteral(
"\\\\"), QString(),
i18n(
"Backslash"));
1216 if (forPattern && regexMode) {
1217 addMenuManager.addSeparator();
1218 addMenuManager.addEntry(QStringLiteral(
"(?:E"), QStringLiteral(
")"),
i18n(
"Group, non-capturing"), QStringLiteral(
"(?:"));
1219 addMenuManager.addEntry(QStringLiteral(
"(?=E"), QStringLiteral(
")"),
i18n(
"Positive Lookahead"), QStringLiteral(
"(?="));
1220 addMenuManager.addEntry(QStringLiteral(
"(?!E"), QStringLiteral(
")"),
i18n(
"Negative lookahead"), QStringLiteral(
"(?!"));
1224 addMenuManager.addEntry(QStringLiteral(
"(?<=E"), QStringLiteral(
")"),
i18n(
"Fixed-length positive lookbehind"), QStringLiteral(
"(?<="));
1225 addMenuManager.addEntry(QStringLiteral(
"(?<!E"), QStringLiteral(
")"),
i18n(
"Fixed-length negative lookbehind"), QStringLiteral(
"(?<!"));
1229 addMenuManager.addSeparator();
1230 addMenuManager.addEntry(QStringLiteral(
"\\L"), QString(),
i18n(
"Begin lowercase conversion"));
1231 addMenuManager.addEntry(QStringLiteral(
"\\U"), QString(),
i18n(
"Begin uppercase conversion"));
1232 addMenuManager.addEntry(QStringLiteral(
"\\E"), QString(),
i18n(
"End case conversion"));
1233 addMenuManager.addEntry(QStringLiteral(
"\\l"), QString(),
i18n(
"Lowercase first character conversion"));
1234 addMenuManager.addEntry(QStringLiteral(
"\\u"), QString(),
i18n(
"Uppercase first character conversion"));
1235 addMenuManager.addEntry(QStringLiteral(
"\\#[#..]"), QString(),
i18n(
"Replacement counter (for Replace All)"), QStringLiteral(
"\\#"));
1241 if (result !=
nullptr) {
1242 addMenuManager.handle(result, comboBox->
lineEdit());
1246void KateSearchBar::onPowerModeChanged(
int )
1248 if (m_powerUi->searchMode->currentIndex() == MODE_REGEX) {
1249 m_powerUi->matchCase->setChecked(
true);
1253 indicateMatch(MatchNothing);
1255 givePatternFeedback();
1263void KateSearchBar::nextMatchForSelection(KTextEditor::ViewPrivate *view, SearchDirection searchDirection)
1265 if (!view->selection()) {
1267 const Cursor cursorPos = view->cursorPosition();
1268 KTextEditor::Range wordRange = view->document()->
wordRangeAt(cursorPos);
1270 selectRange(view, wordRange);
1274 if (view->selection()) {
1276 const QString pattern = view->doc()->
text(view->selectionRange());
1280 if (searchDirection == SearchBackward) {
1285 const Range selRange = view->selectionRange();
1288 if (searchDirection == SearchForward) {
1295 KateMatch
match(view->doc(), enabledOptions);
1296 match.searchText(inputRange, pattern);
1298 if (
match.isValid()) {
1299 selectRange(view,
match.range());
1303 m_view->showSearchWrappedHint(searchDirection == SearchBackward);
1304 if (searchDirection == SearchForward) {
1309 KateMatch match2(view->doc(), enabledOptions);
1310 match2.searchText(inputRange, pattern);
1311 if (match2.isValid()) {
1312 selectRange(view, match2.range());
1319void KateSearchBar::enterPowerMode()
1321 QString initialPattern;
1322 bool selectionOnly =
false;
1325 const bool selected = m_view->selection();
1327 const Range &selection = m_view->selectionRange();
1330 initialPattern = m_view->selectionText();
1333 selectionOnly =
true;
1338 if (initialPattern.
isNull()) {
1340 const bool fromReplace = (m_powerUi !=
nullptr) && (m_widget->isVisible());
1342 QLineEdit *
const patternLineEdit = m_powerUi->pattern->lineEdit();
1343 Q_ASSERT(patternLineEdit !=
nullptr);
1350 const bool fromIncremental = (m_incUi !=
nullptr) && (m_widget->isVisible());
1351 if (fromIncremental) {
1352 initialPattern = m_incUi->pattern->currentText();
1355 m_replacement.clear();
1360 const bool create = (m_powerUi ==
nullptr);
1363 if (m_incUi !=
nullptr) {
1365 const bool OF_INCREMENTAL =
false;
1366 backupConfig(OF_INCREMENTAL);
1371 m_layout->removeWidget(m_widget);
1372 m_widget->deleteLater();
1377 m_powerUi =
new Ui::PowerSearchBar;
1378 m_powerUi->setupUi(m_widget);
1379 m_layout->addWidget(m_widget);
1382 m_powerUi->pattern->setDuplicatesEnabled(
false);
1384 m_powerUi->pattern->setMaxCount(m_config->maxHistorySize());
1386 m_powerUi->pattern->lineEdit()->setClearButtonEnabled(
true);
1387 m_powerUi->pattern->setCompleter(
nullptr);
1388 m_powerUi->replacement->setDuplicatesEnabled(
false);
1390 m_powerUi->replacement->setMaxCount(m_config->maxHistorySize());
1392 m_powerUi->replacement->lineEdit()->setClearButtonEnabled(
true);
1393 m_powerUi->replacement->setCompleter(
nullptr);
1396 m_powerUi->pattern->installEventFilter(
this);
1397 m_powerUi->replacement->installEventFilter(
this);
1403 m_powerUi->mutate->setIcon(mutateIcon);
1404 m_powerUi->mutate->setChecked(
true);
1405 m_powerUi->findNext->setIcon(
QIcon::fromTheme(QStringLiteral(
"go-down-search")));
1406 m_powerUi->findPrev->setIcon(
QIcon::fromTheme(QStringLiteral(
"go-up-search")));
1407 m_powerUi->findAll->setIcon(
QIcon::fromTheme(QStringLiteral(
"edit-find")));
1408 m_powerUi->matchCase->setIcon(matchCaseIcon);
1409 m_powerUi->selectionOnly->setIcon(
QIcon::fromTheme(QStringLiteral(
"edit-select-all")));
1412 centralWidget()->setFocusProxy(m_powerUi->pattern);
1415 m_powerUi->selectionOnly->setChecked(selectionOnly);
1419 m_powerUi->matchCase->setChecked(m_powerMatchCase);
1420 m_powerUi->searchMode->setCurrentIndex(m_powerMode);
1424 m_powerUi->pattern->setCurrentIndex(-1);
1425 m_powerUi->replacement->setCurrentIndex(-1);
1428 QLineEdit *
const patternLineEdit = m_powerUi->pattern->lineEdit();
1429 Q_ASSERT(patternLineEdit !=
nullptr);
1430 patternLineEdit->
setText(initialPattern);
1434 QLineEdit *
const replacementLineEdit = m_powerUi->replacement->lineEdit();
1435 Q_ASSERT(replacementLineEdit !=
nullptr);
1436 replacementLineEdit->
setText(m_replacement);
1439 onPowerPatternChanged(initialPattern);
1440 givePatternFeedback();
1464 connect(m_powerUi->replacement,
1467 qOverload<const QPoint &>(&KateSearchBar::onPowerReplacmentContextMenuRequest));
1471 if (m_widget->isVisible()) {
1476 m_powerUi->gridLayout->addWidget(closeButton(), 0, 2, 1, 1);
1479void KateSearchBar::enterIncrementalMode()
1481 QString initialPattern;
1484 const bool selected = m_view->selection();
1486 const Range &selection = m_view->selectionRange();
1489 initialPattern = m_view->selectionText();
1494 if (initialPattern.
isNull()) {
1496 const bool fromIncremental = (m_incUi !=
nullptr) && (m_widget->isVisible());
1497 if (fromIncremental) {
1498 m_incUi->pattern->lineEdit()->selectAll();
1504 const bool fromReplace = (m_powerUi !=
nullptr) && (m_widget->isVisible());
1506 initialPattern = m_powerUi->pattern->currentText();
1508 m_replacement = m_powerUi->replacement->currentText();
1513 if (initialPattern.
isNull()) {
1514 const KTextEditor::Cursor cursorPosition = m_view->cursorPosition();
1515 initialPattern = m_view->doc()->wordAt(cursorPosition);
1519 const bool create = (m_incUi ==
nullptr);
1522 if (m_powerUi !=
nullptr) {
1524 const bool OF_POWER =
true;
1525 backupConfig(OF_POWER);
1529 m_powerUi =
nullptr;
1530 m_layout->removeWidget(m_widget);
1531 m_widget->deleteLater();
1536 m_incUi =
new Ui::IncrementalSearchBar;
1537 m_incUi->setupUi(m_widget);
1538 m_layout->addWidget(m_widget);
1541 m_incUi->pattern->installEventFilter(
this);
1551 m_incUi->mutate->setIcon(mutateIcon);
1552 m_incUi->next->setIcon(
QIcon::fromTheme(QStringLiteral(
"go-down-search")));
1554 m_incUi->matchCase->setIcon(matchCaseIcon);
1557 m_incUi->pattern->setMinimumWidth(12 * m_incUi->pattern->fontMetrics().height());
1563 centralWidget()->setFocusProxy(m_incUi->pattern);
1565 m_incUi->pattern->setDuplicatesEnabled(
false);
1567 m_incUi->pattern->setMaxCount(m_config->maxHistorySize());
1569 m_incUi->pattern->lineEdit()->setClearButtonEnabled(
true);
1570 m_incUi->pattern->setCompleter(
nullptr);
1575 m_incUi->matchCase->setChecked(m_incMatchCase);
1579 m_incUi->pattern->setCurrentIndex(-1);
1585 m_incUi->pattern->setEditText(initialPattern);
1587 m_incUi->pattern->lineEdit()->selectAll();
1590 if (initialPattern.
isEmpty()) {
1592 indicateMatch(MatchNothing);
1596 m_incUi->next->setDisabled(initialPattern.
isEmpty());
1597 m_incUi->prev->setDisabled(initialPattern.
isEmpty());
1609 if (m_widget->isVisible()) {
1614 m_incUi->hboxLayout->addWidget(closeButton());
1617bool KateSearchBar::clearHighlights()
1620 const QHash<int, KTextEditor::Mark *> &marks = m_view->document()->marks();
1621 QHashIterator<int, KTextEditor::Mark *> i(marks);
1622 while (i.hasNext()) {
1624 if (i.value()->type & KTextEditor::Document::SearchMatch) {
1625 m_view->document()->removeMark(i.value()->line, KTextEditor::Document::SearchMatch);
1629 if (m_infoMessage) {
1630 delete m_infoMessage;
1633 if (m_hlRanges.isEmpty()) {
1636 qDeleteAll(m_hlRanges);
1641void KateSearchBar::updateHighlightColors()
1643 const QColor foregroundColor = m_view->defaultStyleAttribute(KSyntaxHighlighting::Theme::TextStyle::Normal)->foreground().color();
1644 const QColor &searchColor = m_view->rendererConfig()->searchHighlightColor();
1645 const QColor &replaceColor = m_view->rendererConfig()->replaceHighlightColor();
1648 highlightMatchAttribute->setForeground(foregroundColor);
1649 highlightMatchAttribute->setBackground(searchColor);
1656 highlightReplacementAttribute->setBackground(replaceColor);
1657 highlightReplacementAttribute->setForeground(foregroundColor);
1660void KateSearchBar::showEvent(
QShowEvent *event)
1663 if (m_incUi !=
nullptr) {
1664 m_incInitCursor = m_view->cursorPosition();
1669 if (m_cancelFindOrReplace) {
1670 updateSelectionOnly();
1680 const int key =
static_cast<QKeyEvent *
>(
event)->key();
1683 QString &unfinishedText = (m_powerUi && combo == m_powerUi->replacement) ? m_replacement : m_unfinishedSearchText;
1684 if (key ==
Qt::Key_Up && currentIndex <= 0 && unfinishedText != currentText) {
1686 combo->setCurrentIndex(-1);
1687 combo->setCurrentText(unfinishedText);
1690 const bool isUnfinishedSearch = (!currentText.
trimmed().isEmpty() && (currentIndex == -1 || combo->itemText(currentIndex) != currentText));
1691 if (isUnfinishedSearch && unfinishedText != currentText) {
1692 unfinishedText = currentText;
1700void KateSearchBar::updateSelectionOnly()
1703 if (m_workingRange && !m_selectionChangedByUndoRedo) {
1704 delete m_workingRange;
1705 m_workingRange =
nullptr;
1708 if (m_powerUi ==
nullptr) {
1713 const bool selected = m_view->selection();
1714 bool selectionOnly = selected;
1716 Range const &selection = m_view->selectionRange();
1719 m_powerUi->selectionOnly->setChecked(selectionOnly);
1722void KateSearchBar::updateIncInitCursor()
1724 if (m_incUi ==
nullptr) {
1729 m_incInitCursor = m_view->cursorPosition();
1732void KateSearchBar::onPowerPatternContextMenuRequest(
const QPoint &pos)
1734 const bool FOR_PATTERN =
true;
1735 showExtendedContextMenu(FOR_PATTERN,
pos);
1738void KateSearchBar::onPowerPatternContextMenuRequest()
1740 onPowerPatternContextMenuRequest(m_powerUi->pattern->mapFromGlobal(
QCursor::pos()));
1743void KateSearchBar::onPowerReplacmentContextMenuRequest(
const QPoint &pos)
1745 const bool FOR_REPLACEMENT =
false;
1746 showExtendedContextMenu(FOR_REPLACEMENT,
pos);
1749void KateSearchBar::onPowerReplacmentContextMenuRequest()
1751 onPowerReplacmentContextMenuRequest(m_powerUi->replacement->mapFromGlobal(
QCursor::pos()));
1754void KateSearchBar::onPowerCancelFindOrReplace()
1756 m_cancelFindOrReplace =
true;
1759bool KateSearchBar::isPower()
const
1761 return m_powerUi !=
nullptr;
1764void KateSearchBar::slotReadWriteChanged()
1766 if (!KateSearchBar::isPower()) {
1771 m_powerUi->replaceNext->setEnabled(m_view->doc()->isReadWrite() && isPatternValid());
1772 m_powerUi->replaceAll->setEnabled(m_view->doc()->isReadWrite() && isPatternValid());
1775#include "moc_katesearchbar.cpp"
static void adjustForeground(QPalette &, ForegroundRole newRole=NormalText, QPalette::ColorRole color=QPalette::Text, ColorSet set=View, KSharedConfigPtr=KSharedConfigPtr())
static void adjustBackground(QPalette &, BackgroundRole newRole=NormalBackground, QPalette::ColorRole color=QPalette::Base, ColorSet set=View, KSharedConfigPtr=KSharedConfigPtr())
A class which provides customized text decorations.
@ ActivateMouseIn
Activate attribute on mouse in.
@ ActivateCaretIn
Activate attribute on caret in.
QExplicitlySharedDataPointer< Attribute > Ptr
Shared data pointer for Attribute.
constexpr int line() const noexcept
Retrieve the line on which this cursor is situated.
KTextEditor::Cursor documentEnd() const override
End position of the document.
QString text(KTextEditor::Range range, bool blockwise=false) const override
Get the document content within the given range.
void reloaded(KTextEditor::Document *document)
Emitted after the current document was reloaded.
virtual KTextEditor::Range wordRangeAt(KTextEditor::Cursor cursor) const =0
Get the text range for the word located under the text position cursor.
void aboutToClose(KTextEditor::Document *document)
Warn anyone listening that the current document is about to close.
void saveSearchReplaceHistoryModels()
Call this function to store the history models to the application config.
static KTextEditor::EditorPrivate * self()
Kate Part Internal stuff ;)
@ BottomInView
show message as view overlay in the bottom right corner.
@ Positive
positive information message
virtual void setAttribute(Attribute::Ptr attribute)=0
Sets the currently active attribute for this range.
virtual void setAttributeOnlyForViews(bool onlyForViews)=0
Set if this range's attribute is only visible in views, not for example prints.
virtual void setView(View *view)=0
Sets the currently active view for this range.
virtual void setZDepth(qreal zDepth)=0
Set the current Z-depth of this range.
@ DoNotExpand
Don't expand to encapsulate new characters in either direction. This is the default.
@ ExpandRight
Expand to encapsulate new characters to the right of the range.
@ ExpandLeft
Expand to encapsulate new characters to the left of the range.
An object representing a section of text, from one Cursor to another.
constexpr Cursor end() const noexcept
Get the end position of this range.
constexpr Cursor start() const noexcept
Get the start position of this range.
void setRange(Range range) noexcept
Set the start and end cursors to range.start() and range.end() respectively.
constexpr bool onSingleLine() const noexcept
Check whether this range is wholly contained within one line, ie.
static constexpr Range invalid() noexcept
Returns an invalid range.
constexpr bool isValid() const noexcept
Validity check.
void cursorPositionChanged(KTextEditor::View *view, KTextEditor::Cursor newPosition)
This signal is emitted whenever the view's cursor position changed.
void selectionChanged(KTextEditor::View *view)
This signal is emitted whenever the view's selection changes.
KateUndoManager implements a document's history.
static constexpr Range invalid() noexcept
Returns an invalid range.
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18n(const char *text, const TYPE &arg...)
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
KIOCORE_EXPORT QString number(KIO::filesize_t size)
ButtonCode questionTwoActions(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const QString &dontAskAgainName=QString(), Options options=Notify)
bool isValid(QStringView ifopt)
const QList< QKeySequence > & end()
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
@ Default
Default settings.
@ CaseInsensitive
Ignores cases, e.g. "a" matches "A".
@ Regex
Treats the pattern as a regular expression.
@ Backwards
Searches in backward direction.
@ EscapeSequences
Plaintext mode: Processes escape sequences.
@ WholeWords
Plaintext mode: Whole words only, e.g. not "amp" in "example".
QFlags< SearchOption > SearchOptions
Stores a combination of SearchOption values.
QVariant data() const const
void setIcon(const QIcon &icon)
void setData(const QVariant &data)
void setCurrentIndex(int index)
void currentIndexChanged(int index)
void editTextChanged(const QString &text)
int findText(const QString &text, Qt::MatchFlags flags) const const
void insertItem(int index, const QIcon &icon, const QString &text, const QVariant &userData)
QLineEdit * lineEdit() const const
void removeItem(int index)
Qt::KeyboardModifiers keyboardModifiers()
QIcon fromTheme(const QString &name)
QMenu * createStandardContextMenu()
void insert(const QString &newText)
void setText(const QString &)
void textChanged(const QString &text)
qsizetype count() const const
void reserve(qsizetype size)
void resize(qsizetype size)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool event(QEvent *e)
virtual bool eventFilter(QObject *watched, QEvent *event)
T qobject_cast(QObject *object)
bool isEmpty() const const
bool isNull() const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString number(double n, char format, int precision)
qsizetype size() const const
QString trimmed() const const
typedef KeyboardModifiers
uint toUInt(bool *ok) const const