Changeset View
Changeset View
Standalone View
Standalone View
src/search/katesearchbar.cpp
Show All 21 Lines | |||||
22 | */ | 22 | */ | ||
23 | 23 | | |||
24 | #include "katesearchbar.h" | 24 | #include "katesearchbar.h" | ||
25 | 25 | | |||
26 | #include "kateconfig.h" | 26 | #include "kateconfig.h" | ||
27 | #include "katedocument.h" | 27 | #include "katedocument.h" | ||
28 | #include "kateglobal.h" | 28 | #include "kateglobal.h" | ||
29 | #include "katematch.h" | 29 | #include "katematch.h" | ||
30 | #include "kateregexp.h" | | |||
31 | #include "katerenderer.h" | 30 | #include "katerenderer.h" | ||
32 | #include "kateundomanager.h" | 31 | #include "kateundomanager.h" | ||
33 | #include "kateview.h" | 32 | #include "kateview.h" | ||
34 | 33 | | |||
35 | #include <KTextEditor/DocumentCursor> | 34 | #include <KTextEditor/DocumentCursor> | ||
36 | #include <KTextEditor/Message> | 35 | #include <KTextEditor/Message> | ||
37 | #include <KTextEditor/MovingRange> | 36 | #include <KTextEditor/MovingRange> | ||
38 | 37 | | |||
▲ Show 20 Lines • Show All 434 Lines • ▼ Show 20 Line(s) | 463 | { | |||
473 | } | 472 | } | ||
474 | } | 473 | } | ||
475 | 474 | | |||
476 | bool KateSearchBar::matchCase() const | 475 | bool KateSearchBar::matchCase() const | ||
477 | { | 476 | { | ||
478 | return isPower() ? m_powerUi->matchCase->isChecked() : m_incUi->matchCase->isChecked(); | 477 | return isPower() ? m_powerUi->matchCase->isChecked() : m_incUi->matchCase->isChecked(); | ||
479 | } | 478 | } | ||
480 | 479 | | |||
481 | void KateSearchBar::fixForSingleLine(Range &range, SearchDirection searchDirection) | | |||
482 | { | | |||
483 | FAST_DEBUG("Single-line workaround checking BEFORE" << range); | | |||
484 | if (searchDirection == SearchForward) { | | |||
485 | const int line = range.start().line(); | | |||
486 | const int col = range.start().column(); | | |||
487 | const int maxColWithNewline = m_view->document()->lineLength(line) + 1; | | |||
488 | if (col == maxColWithNewline) { | | |||
489 | FAST_DEBUG("Starting on a newline" << range); | | |||
490 | const int maxLine = m_view->document()->lines() - 1; | | |||
491 | if (line < maxLine) { | | |||
492 | range.setRange(Cursor(line + 1, 0), range.end()); | | |||
493 | FAST_DEBUG("Search range fixed to " << range); | | |||
494 | } else { | | |||
495 | FAST_DEBUG("Already at last line"); | | |||
496 | range = Range::invalid(); | | |||
497 | } | | |||
498 | } | | |||
499 | } else { | | |||
500 | const int col = range.end().column(); | | |||
501 | if (col == 0) { | | |||
502 | FAST_DEBUG("Ending after a newline" << range); | | |||
503 | const int line = range.end().line(); | | |||
504 | if (line > 0) { | | |||
505 | const int maxColWithNewline = m_view->document()->lineLength(line - 1); | | |||
506 | range.setRange(range.start(), Cursor(line - 1, maxColWithNewline)); | | |||
507 | FAST_DEBUG("Search range fixed to " << range); | | |||
508 | } else { | | |||
509 | FAST_DEBUG("Already at first line"); | | |||
510 | range = Range::invalid(); | | |||
511 | } | | |||
512 | } | | |||
513 | } | | |||
514 | FAST_DEBUG("Single-line workaround checking AFTER" << range); | | |||
515 | } | | |||
516 | | ||||
517 | void KateSearchBar::onReturnPressed() | 480 | void KateSearchBar::onReturnPressed() | ||
518 | { | 481 | { | ||
519 | const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); | 482 | const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); | ||
520 | const bool shiftDown = (modifiers & Qt::ShiftModifier) != 0; | 483 | const bool shiftDown = (modifiers & Qt::ShiftModifier) != 0; | ||
521 | const bool controlDown = (modifiers & Qt::ControlModifier) != 0; | 484 | const bool controlDown = (modifiers & Qt::ControlModifier) != 0; | ||
522 | 485 | | |||
523 | if (shiftDown) { | 486 | if (shiftDown) { | ||
524 | // Shift down, search backwards | 487 | // Shift down, search backwards | ||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Line(s) | 529 | } else { | |||
569 | if (searchDirection == SearchForward) { | 532 | if (searchDirection == SearchForward) { | ||
570 | inputRange.setRange(cursorPos, m_view->document()->documentEnd()); | 533 | inputRange.setRange(cursorPos, m_view->document()->documentEnd()); | ||
571 | } else { | 534 | } else { | ||
572 | inputRange.setRange(Cursor(0, 0), cursorPos); | 535 | inputRange.setRange(Cursor(0, 0), cursorPos); | ||
573 | } | 536 | } | ||
574 | } | 537 | } | ||
575 | FAST_DEBUG("Search range is" << inputRange); | 538 | FAST_DEBUG("Search range is" << inputRange); | ||
576 | 539 | | |||
577 | { | | |||
578 | const bool regexMode = enabledOptions.testFlag(Regex); | | |||
579 | const bool multiLinePattern = regexMode ? KateRegExp(searchPattern()).isMultiLine() : false; | | |||
580 | | ||||
581 | // Single-line pattern workaround | | |||
582 | if (regexMode && !multiLinePattern) { | | |||
583 | fixForSingleLine(inputRange, searchDirection); | | |||
584 | } | | |||
585 | } | | |||
586 | | ||||
587 | KateMatch match(m_view->doc(), enabledOptions); | 540 | KateMatch match(m_view->doc(), enabledOptions); | ||
588 | Range afterReplace = Range::invalid(); | 541 | Range afterReplace = Range::invalid(); | ||
589 | 542 | | |||
590 | // Find, first try | 543 | // Find, first try | ||
591 | match.searchText(inputRange, searchPattern()); | 544 | match.searchText(inputRange, searchPattern()); | ||
592 | if (match.isValid() && match.range() == selection) { | 545 | if (match.isValid()) { | ||
546 | if (match.range() == selection) { | ||||
593 | // Same match again | 547 | // Same match again | ||
594 | if (replacement != nullptr) { | 548 | if (replacement != nullptr) { | ||
595 | // Selection is match -> replace | 549 | // Selection is match -> replace | ||
596 | KTextEditor::MovingRange *smartInputRange = m_view->doc()->newMovingRange(inputRange, KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); | 550 | KTextEditor::MovingRange *smartInputRange = m_view->doc()->newMovingRange(inputRange, KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); | ||
597 | afterReplace = match.replace(*replacement, m_view->blockSelection()); | 551 | afterReplace = match.replace(*replacement, m_view->blockSelection()); | ||
598 | inputRange = *smartInputRange; | 552 | inputRange = *smartInputRange; | ||
599 | delete smartInputRange; | 553 | delete smartInputRange; | ||
600 | } | 554 | } | ||
601 | 555 | | |||
602 | if (!selectionOnly()) { | 556 | if (!selectionOnly()) { | ||
603 | // Find, second try after old selection | 557 | // Find, second try after old selection | ||
604 | if (searchDirection == SearchForward) { | 558 | if (searchDirection == SearchForward) { | ||
605 | const Cursor start = (replacement != nullptr) ? afterReplace.end() : selection.end(); | 559 | const Cursor start = (replacement != nullptr) ? afterReplace.end() : selection.end(); | ||
606 | inputRange.setRange(start, inputRange.end()); | 560 | inputRange.setRange(start, inputRange.end()); | ||
607 | } else { | 561 | } else { | ||
608 | const Cursor end = (replacement != nullptr) ? afterReplace.start() : selection.start(); | 562 | const Cursor end = (replacement != nullptr) ? afterReplace.start() : selection.start(); | ||
609 | inputRange.setRange(inputRange.start(), end); | 563 | inputRange.setRange(inputRange.start(), end); | ||
610 | } | 564 | } | ||
611 | } | 565 | } | ||
612 | 566 | | |||
613 | // Single-line pattern workaround | 567 | match.searchText(inputRange, searchPattern()); | ||
614 | fixForSingleLine(inputRange, searchDirection); | 568 | | ||
569 | } else if (match.isEmpty()) { | ||||
570 | // valid, zero-length match, e.g.: '^', '$', '\b' | ||||
571 | // advance the range to avoid looping | ||||
572 | KTextEditor::DocumentCursor zeroLenMatch(m_view->doc(), match.range().end()); | ||||
573 | | ||||
574 | if (searchDirection == SearchForward) { | ||||
575 | zeroLenMatch.move(1); | ||||
576 | inputRange.setRange(zeroLenMatch.toCursor(), inputRange.end()); | ||||
577 | } else { // SearchBackward | ||||
578 | zeroLenMatch.move(-1); | ||||
579 | inputRange.setRange(inputRange.start(), zeroLenMatch.toCursor()); | ||||
580 | } | ||||
615 | 581 | | |||
616 | match.searchText(inputRange, searchPattern()); | 582 | match.searchText(inputRange, searchPattern()); | ||
617 | } | 583 | } | ||
584 | } | ||||
618 | 585 | | |||
619 | bool askWrap = !match.isValid() && (!selection.isValid() || !selectionOnly()); | 586 | bool askWrap = !match.isValid() && (!selection.isValid() || !selectionOnly()); | ||
620 | bool wrap = false; | 587 | bool wrap = false; | ||
621 | 588 | | |||
622 | if (askWrap) { | 589 | if (askWrap) { | ||
623 | askWrap = false; | 590 | askWrap = false; | ||
624 | wrap = true; | 591 | wrap = true; | ||
625 | } | 592 | } | ||
▲ Show 20 Lines • Show All 164 Lines • ▼ Show 20 Line(s) | 736 | { | |||
790 | 757 | | |||
791 | findOrReplaceAll(); | 758 | findOrReplaceAll(); | ||
792 | } | 759 | } | ||
793 | 760 | | |||
794 | void KateSearchBar::findOrReplaceAll() | 761 | void KateSearchBar::findOrReplaceAll() | ||
795 | { | 762 | { | ||
796 | const SearchOptions enabledOptions = searchOptions(SearchForward); | 763 | const SearchOptions enabledOptions = searchOptions(SearchForward); | ||
797 | 764 | | |||
798 | const bool regexMode = enabledOptions.testFlag(Regex); | | |||
799 | const bool multiLinePattern = regexMode ? KateRegExp(searchPattern()).isMultiLine() : false; | | |||
800 | | ||||
801 | // we highlight all ranges of a replace, up to some hard limit | 765 | // we highlight all ranges of a replace, up to some hard limit | ||
802 | // e.g. if you replace 100000 things, rendering will break down otherwise ;=) | 766 | // e.g. if you replace 100000 things, rendering will break down otherwise ;=) | ||
803 | const int maxHighlightings = 65536; | 767 | const int maxHighlightings = 65536; | ||
804 | 768 | | |||
805 | // reuse match object to avoid massive moving range creation | 769 | // reuse match object to avoid massive moving range creation | ||
806 | KateMatch match(m_view->doc(), enabledOptions); | 770 | KateMatch match(m_view->doc(), enabledOptions); | ||
807 | 771 | | |||
808 | bool block = m_view->selection() && m_view->blockSelection(); | 772 | bool block = m_view->selection() && m_view->blockSelection(); | ||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Line(s) | 786 | do { | |||
856 | } | 820 | } | ||
857 | 821 | | |||
858 | KTextEditor::DocumentCursor workingStart(m_view->doc(), lastRange.end()); | 822 | KTextEditor::DocumentCursor workingStart(m_view->doc(), lastRange.end()); | ||
859 | 823 | | |||
860 | if (originalMatchEmpty) { | 824 | if (originalMatchEmpty) { | ||
861 | // Can happen for regex patterns like "^". | 825 | // Can happen for regex patterns like "^". | ||
862 | // If we don't advance here we will loop forever... | 826 | // If we don't advance here we will loop forever... | ||
863 | workingStart.move(1); | 827 | workingStart.move(1); | ||
864 | } else if (regexMode && !multiLinePattern && workingStart.atEndOfLine()) { | | |||
865 | // single-line regexps might match the naked line end | | |||
866 | // therefore we better advance to the next line | | |||
867 | workingStart.move(1); | | |||
868 | } | 828 | } | ||
869 | m_workingRange->setRange(workingStart.toCursor(), m_workingRange->end()); | 829 | m_workingRange->setRange(workingStart.toCursor(), m_workingRange->end()); | ||
870 | 830 | | |||
871 | // Are we done? | 831 | // Are we done? | ||
872 | if (!m_workingRange->toRange().isValid() || workingStart.atEndOfDocument()) { | 832 | if (!m_workingRange->toRange().isValid() || workingStart.atEndOfDocument()) { | ||
873 | done = true; | 833 | done = true; | ||
874 | break; | 834 | break; | ||
875 | } | 835 | } | ||
▲ Show 20 Lines • Show All 265 Lines • ▼ Show 20 Line(s) | 1100 | if (!extendMenu) { | |||
1141 | addMenuManager.enableMenu(extendMenu); | 1101 | addMenuManager.enableMenu(extendMenu); | ||
1142 | } else { | 1102 | } else { | ||
1143 | // Build menu | 1103 | // Build menu | ||
1144 | if (forPattern) { | 1104 | if (forPattern) { | ||
1145 | if (regexMode) { | 1105 | if (regexMode) { | ||
1146 | addMenuManager.addEntry(QStringLiteral("^"), QString(), i18n("Beginning of line")); | 1106 | addMenuManager.addEntry(QStringLiteral("^"), QString(), i18n("Beginning of line")); | ||
1147 | addMenuManager.addEntry(QStringLiteral("$"), QString(), i18n("End of line")); | 1107 | addMenuManager.addEntry(QStringLiteral("$"), QString(), i18n("End of line")); | ||
1148 | addMenuManager.addSeparator(); | 1108 | addMenuManager.addSeparator(); | ||
1149 | addMenuManager.addEntry(QStringLiteral("."), QString(), i18n("Any single character (excluding line breaks)")); | 1109 | addMenuManager.addEntry(QStringLiteral("."), QString(), i18n("Match any character execluding new line (by default)")); | ||
1150 | addMenuManager.addSeparator(); | | |||
1151 | addMenuManager.addEntry(QStringLiteral("+"), QString(), i18n("One or more occurrences")); | 1110 | addMenuManager.addEntry(QStringLiteral("+"), QString(), i18n("One or more occurrences")); | ||
1152 | addMenuManager.addEntry(QStringLiteral("*"), QString(), i18n("Zero or more occurrences")); | 1111 | addMenuManager.addEntry(QStringLiteral("*"), QString(), i18n("Zero or more occurrences")); | ||
1153 | addMenuManager.addEntry(QStringLiteral("?"), QString(), i18n("Zero or one occurrences")); | 1112 | addMenuManager.addEntry(QStringLiteral("?"), QString(), i18n("Zero or one occurrences")); | ||
1154 | addMenuManager.addEntry(QStringLiteral("{a"), QStringLiteral(",b}"), i18n("<a> through <b> occurrences"), QStringLiteral("{"), QStringLiteral(",}")); | 1113 | addMenuManager.addEntry(QStringLiteral("{a"), QStringLiteral(",b}"), i18n("<a> through <b> occurrences"), QStringLiteral("{"), QStringLiteral(",}")); | ||
1114 | | ||||
1115 | addMenuManager.addSeparator(); | ||||
1155 | addMenuManager.addSeparator(); | 1116 | addMenuManager.addSeparator(); | ||
1156 | addMenuManager.addEntry(QStringLiteral("("), QStringLiteral(")"), i18n("Group, capturing")); | 1117 | addMenuManager.addEntry(QStringLiteral("("), QStringLiteral(")"), i18n("Group, capturing")); | ||
1157 | addMenuManager.addEntry(QStringLiteral("|"), QString(), i18n("Or")); | 1118 | addMenuManager.addEntry(QStringLiteral("|"), QString(), i18n("Or")); | ||
1158 | addMenuManager.addEntry(QStringLiteral("["), QStringLiteral("]"), i18n("Set of characters")); | 1119 | addMenuManager.addEntry(QStringLiteral("["), QStringLiteral("]"), i18n("Set of characters")); | ||
1159 | addMenuManager.addEntry(QStringLiteral("[^"), QStringLiteral("]"), i18n("Negative set of characters")); | 1120 | addMenuManager.addEntry(QStringLiteral("[^"), QStringLiteral("]"), i18n("Negative set of characters")); | ||
1160 | addMenuManager.addSeparator(); | 1121 | addMenuManager.addSeparator(); | ||
1161 | } | 1122 | } | ||
1162 | } else { | 1123 | } else { | ||
Show All 18 Lines | |||||
1181 | addMenuManager.addEntry(QStringLiteral("\\t"), QString(), i18n("Tab")); | 1142 | addMenuManager.addEntry(QStringLiteral("\\t"), QString(), i18n("Tab")); | ||
1182 | 1143 | | |||
1183 | if (forPattern && regexMode) { | 1144 | if (forPattern && regexMode) { | ||
1184 | addMenuManager.addEntry(QStringLiteral("\\b"), QString(), i18n("Word boundary")); | 1145 | addMenuManager.addEntry(QStringLiteral("\\b"), QString(), i18n("Word boundary")); | ||
1185 | addMenuManager.addEntry(QStringLiteral("\\B"), QString(), i18n("Not word boundary")); | 1146 | addMenuManager.addEntry(QStringLiteral("\\B"), QString(), i18n("Not word boundary")); | ||
1186 | addMenuManager.addEntry(QStringLiteral("\\d"), QString(), i18n("Digit")); | 1147 | addMenuManager.addEntry(QStringLiteral("\\d"), QString(), i18n("Digit")); | ||
1187 | addMenuManager.addEntry(QStringLiteral("\\D"), QString(), i18n("Non-digit")); | 1148 | addMenuManager.addEntry(QStringLiteral("\\D"), QString(), i18n("Non-digit")); | ||
1188 | addMenuManager.addEntry(QStringLiteral("\\s"), QString(), i18n("Whitespace (excluding line breaks)")); | 1149 | addMenuManager.addEntry(QStringLiteral("\\s"), QString(), i18n("Whitespace (excluding line breaks)")); | ||
1189 | addMenuManager.addEntry(QStringLiteral("\\S"), QString(), i18n("Non-whitespace (excluding line breaks)")); | 1150 | addMenuManager.addEntry(QStringLiteral("\\S"), QString(), i18n("Non-whitespace")); | ||
1190 | addMenuManager.addEntry(QStringLiteral("\\w"), QString(), i18n("Word character (alphanumerics plus '_')")); | 1151 | addMenuManager.addEntry(QStringLiteral("\\w"), QString(), i18n("Word character (alphanumerics plus '_')")); | ||
1191 | addMenuManager.addEntry(QStringLiteral("\\W"), QString(), i18n("Non-word character")); | 1152 | addMenuManager.addEntry(QStringLiteral("\\W"), QString(), i18n("Non-word character")); | ||
1192 | } | 1153 | } | ||
1193 | 1154 | | |||
1194 | addMenuManager.addEntry(QStringLiteral("\\0???"), QString(), i18n("Octal character 000 to 377 (2^8-1)"), QStringLiteral("\\0")); | 1155 | addMenuManager.addEntry(QStringLiteral("\\0???"), QString(), i18n("Octal character 000 to 377 (2^8-1)"), QStringLiteral("\\0")); | ||
1195 | addMenuManager.addEntry(QStringLiteral("\\x????"), QString(), i18n("Hex character 0000 to FFFF (2^16-1)"), QStringLiteral("\\x")); | 1156 | addMenuManager.addEntry(QStringLiteral("\\x????"), QString(), i18n("Hex character 0000 to FFFF (2^16-1)"), QStringLiteral("\\x")); | ||
1196 | addMenuManager.addEntry(QStringLiteral("\\\\"), QString(), i18n("Backslash")); | 1157 | addMenuManager.addEntry(QStringLiteral("\\\\"), QString(), i18n("Backslash")); | ||
1197 | 1158 | | |||
▲ Show 20 Lines • Show All 489 Lines • Show Last 20 Lines |