summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2022-06-13 15:47:31 +0200
committerFlorian Bruhin <me@the-compiler.org>2022-06-13 15:47:31 +0200
commit583354d524ff42cfb24ff5618d3f8b1919e9d554 (patch)
tree6c273aa0b6bb6e5a59763d038a45d316b91defc6
parent06587ea43a0cf44bc53b7b6da3eabfd935b0918c (diff)
parent57f0155fa0d9972f22298d1189ea54e9efa15433 (diff)
downloadqutebrowser-583354d524ff42cfb24ff5618d3f8b1919e9d554.tar.gz
qutebrowser-583354d524ff42cfb24ff5618d3f8b1919e9d554.zip
Merge remote-tracking branch 'origin/pr/6670' into dev
-rw-r--r--qutebrowser/browser/browsertab.py17
-rw-r--r--qutebrowser/browser/commands.py36
-rw-r--r--qutebrowser/browser/webengine/webenginetab.py135
-rw-r--r--qutebrowser/config/configdata.yml10
-rw-r--r--qutebrowser/mainwindow/mainwindow.py3
-rw-r--r--qutebrowser/mainwindow/statusbar/bar.py9
-rw-r--r--qutebrowser/mainwindow/statusbar/searchmatch.py49
-rw-r--r--qutebrowser/mainwindow/tabbedbrowser.py6
-rw-r--r--scripts/dev/check_coverage.py2
-rw-r--r--tests/end2end/features/search.feature49
10 files changed, 223 insertions, 93 deletions
diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py
index 699fe1b0b..f8ac3d24c 100644
--- a/qutebrowser/browser/browsertab.py
+++ b/qutebrowser/browser/browsertab.py
@@ -295,14 +295,23 @@ class AbstractSearch(QObject):
text: The last thing this view was searched for.
search_displayed: Whether we're currently displaying search results in
this view.
+ current_match: The currently active search match on the page.
+ 0 if no search is active or the feature isn't available.
+ total_match_count: The total number of search matches on the page.
+ 0 if no search is active or the feature isn't available.
_flags: The flags of the last search (needs to be set by subclasses).
_widget: The underlying WebView widget.
+
+ Signals:
+ finished: A search has finished. True if the text was found, false otherwise.
+ search_match_changed: The currently active search match has changed.
+ Emits (0, 0) if no search is active.
+ Will not be emitted if search matches are not available.
+ cleared: An existing search was cleared.
"""
- #: Signal emitted when a search was finished
- #: (True if the text was found, False otherwise)
finished = pyqtSignal(bool)
- #: Signal emitted when an existing search was cleared.
+ search_match_changed = pyqtSignal(int, int)
cleared = pyqtSignal()
_Callback = Callable[[bool], None]
@@ -313,6 +322,8 @@ class AbstractSearch(QObject):
self._widget = cast(_WidgetType, None)
self.text: Optional[str] = None
self.search_displayed = False
+ self.current_match = 0
+ self.total_match_count = 0
def _is_case_sensitive(self, ignore_case: usertypes.IgnoreCase) -> bool:
"""Check if case-sensitivity should be used.
diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py
index 4f782c3ee..939f02108 100644
--- a/qutebrowser/browser/commands.py
+++ b/qutebrowser/browser/commands.py
@@ -1538,13 +1538,14 @@ class CommandDispatcher:
message.error(str(e))
ed.backup()
- def _search_cb(self, found, *, tab, old_scroll_pos, options, text, prev):
+ def _search_cb(self, found, *, tab, old_current_match, options, text, prev):
"""Callback called from search/search_next/search_prev.
Args:
found: Whether the text was found.
tab: The AbstractTab in which the search was made.
- old_scroll_pos: The scroll position (QPoint) before the search.
+ old_current_match: The previously active match before the search
+ was performed.
options: The options (dict) the search was made with.
text: The text searched for.
prev: Whether we're searching backwards (i.e. :search-prev)
@@ -1556,11 +1557,24 @@ class CommandDispatcher:
going_up = options['reverse'] ^ prev
if found:
- # Check if the scroll position got smaller and show info.
- if not going_up and tab.scroller.pos_px().y() < old_scroll_pos.y():
- message.info("Search hit BOTTOM, continuing at TOP")
- elif going_up and tab.scroller.pos_px().y() > old_scroll_pos.y():
- message.info("Search hit TOP, continuing at BOTTOM")
+ if not config.val.search.wrap_messages:
+ return
+
+ new_current_match = tab.search.current_match
+ # Check if the match count change is opposite to the search direction
+ if old_current_match > 0:
+ if not going_up:
+ if old_current_match > new_current_match:
+ message.info("Search hit BOTTOM, continuing at TOP",
+ replace="search-hit-msg")
+ elif old_current_match == new_current_match:
+ message.info("Search hit BOTTOM", replace="search-hit-msg")
+ elif going_up:
+ if old_current_match < new_current_match:
+ message.info("Search hit TOP, continuing at BOTTOM",
+ replace="search-hit-msg")
+ elif old_current_match == new_current_match:
+ message.info("Search hit TOP", replace="search-hit-msg")
else:
message.warning(f"Text '{text}' not found on page!",
replace='find-in-page')
@@ -1591,8 +1605,8 @@ class CommandDispatcher:
self._tabbed_browser.search_options = dict(options)
cb = functools.partial(self._search_cb, tab=tab,
- old_scroll_pos=tab.scroller.pos_px(),
- options=options, text=text, prev=False)
+ old_current_match=0, options=options,
+ text=text, prev=False)
options['result_cb'] = cb
tab.scroller.before_jump_requested.emit()
@@ -1624,7 +1638,7 @@ class CommandDispatcher:
return
cb = functools.partial(self._search_cb, tab=tab,
- old_scroll_pos=tab.scroller.pos_px(),
+ old_current_match=tab.search.current_match,
options=window_options, text=window_text,
prev=False)
@@ -1658,7 +1672,7 @@ class CommandDispatcher:
return
cb = functools.partial(self._search_cb, tab=tab,
- old_scroll_pos=tab.scroller.pos_px(),
+ old_current_match=tab.search.current_match,
options=window_options, text=window_text,
prev=True)
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
index 0a2333afc..1d02f7e99 100644
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -104,83 +104,33 @@ class _WebEngineSearchWrapHandler:
Attributes:
flag_wrap: An additional flag indicating whether the last search
used wrapping.
- _active_match: The 1-based index of the currently active match
- on the page.
- _total_matches: The total number of search matches on the page.
- _nowrap_available: Whether the functionality to prevent wrapping
- is available.
+ nowrap_available: Whether the functionality to prevent wrapping
+ is available.
"""
def __init__(self):
- self._active_match = 0
- self._total_matches = 0
self.flag_wrap = True
- self._nowrap_available = False
+ self.nowrap_available = False
- def connect_signal(self, page):
- """Connect to the findTextFinished signal of the page.
-
- Args:
- page: The QtWebEnginePage to connect to this handler.
- """
- if not qtutils.version_check("5.14"):
- return
-
- try:
- # pylint: disable=unused-import
- from PyQt5.QtWebEngineCore import QWebEngineFindTextResult
- except ImportError:
- # WORKAROUND for some odd PyQt/packaging bug where the
- # findTextResult signal is available, but QWebEngineFindTextResult
- # is not. Seems to happen on e.g. Gentoo.
- log.webview.warning("Could not import QWebEngineFindTextResult "
- "despite running on Qt 5.14. You might need "
- "to rebuild PyQtWebEngine.")
- return
-
- page.findTextFinished.connect(self._store_match_data)
- self._nowrap_available = True
-
- def _store_match_data(self, result):
- """Store information on the last match.
-
- The information will be checked against when wrapping is turned off.
-
- Args:
- result: A FindTextResult passed by the findTextFinished signal.
- """
- self._active_match = result.activeMatch()
- self._total_matches = result.numberOfMatches()
- log.webview.debug("Active search match: {}/{}"
- .format(self._active_match, self._total_matches))
-
- def reset_match_data(self):
- """Reset match information.
-
- Stale information could lead to next_result or prev_result misbehaving.
- """
- self._active_match = 0
- self._total_matches = 0
-
- def prevent_wrapping(self, *, going_up):
+ def prevent_wrapping(self, current_match, total_match_count, *, going_up):
"""Prevent wrapping if possible and required.
Returns True if a wrap was prevented and False if not.
Args:
+ current_match: The currently active search match on the page.
+ total_match_count: The total number of search matches on the page.
going_up: Whether the search would scroll the page up or down.
"""
- if (not self._nowrap_available or
- self.flag_wrap or self._total_matches == 0):
- return False
- elif going_up and self._active_match == 1:
- message.info("Search hit TOP")
- return True
- elif not going_up and self._active_match == self._total_matches:
- message.info("Search hit BOTTOM")
- return True
- else:
- return False
+ return (
+ self.nowrap_available and
+ not self.flag_wrap and
+ total_match_count != 0 and
+ (
+ going_up and current_match == 1 or
+ not going_up and current_match == total_match_count
+ )
+ )
class WebEngineSearch(browsertab.AbstractSearch):
@@ -191,6 +141,7 @@ class WebEngineSearch(browsertab.AbstractSearch):
_flags: The QWebEnginePage.FindFlags of the last search.
_pending_searches: How many searches have been started but not called
back yet.
+
"""
_widget: webview.WebEngineView
@@ -199,7 +150,6 @@ class WebEngineSearch(browsertab.AbstractSearch):
super().__init__(tab, parent)
self._flags = self._empty_flags()
self._pending_searches = 0
- # The API necessary to stop wrapping was added in this version
self._wrap_handler = _WebEngineSearchWrapHandler()
def _empty_flags(self):
@@ -214,7 +164,25 @@ class WebEngineSearch(browsertab.AbstractSearch):
return flags
def connect_signals(self):
- self._wrap_handler.connect_signal(self._widget.page())
+ """Connect the signals necessary for this class to function."""
+ # The API necessary to stop wrapping was added in this version
+ if not qtutils.version_check("5.14"):
+ return
+
+ try:
+ # pylint: disable=unused-import
+ from PyQt5.QtWebEngineCore import QWebEngineFindTextResult
+ except ImportError:
+ # WORKAROUND for some odd PyQt/packaging bug where the
+ # findTextResult signal is available, but QWebEngineFindTextResult
+ # is not. Seems to happen on e.g. Gentoo.
+ log.webview.warning("Could not import QWebEngineFindTextResult "
+ "despite running on Qt 5.14. You might need "
+ "to rebuild PyQtWebEngine.")
+ return
+
+ self._wrap_handler.nowrap_available = True
+ self._widget.page().findTextFinished.connect(self._on_find_finished)
def _find(self, text, flags, callback, caller):
"""Call findText on the widget."""
@@ -254,6 +222,14 @@ class WebEngineSearch(browsertab.AbstractSearch):
self._widget.page().findText(text, flags, wrapped_callback)
+ def _on_find_finished(self, find_text_result):
+ """Unwrap the result, store it, and pass it along."""
+ self.current_match = find_text_result.activeMatch()
+ self.total_match_count = find_text_result.numberOfMatches()
+ log.webview.debug("Active search match: {}/{}"
+ .format(self.current_match, self.total_match_count))
+ self.search_match_changed.emit(self.current_match, self.total_match_count)
+
def search(self, text, *, ignore_case=usertypes.IgnoreCase.never,
reverse=False, wrap=True, result_cb=None):
# Don't go to next entry on duplicate search
@@ -265,7 +241,7 @@ class WebEngineSearch(browsertab.AbstractSearch):
self.text = text
self._flags = self._args_to_flags(reverse, ignore_case)
- self._wrap_handler.reset_match_data()
+ self._reset_match_data()
self._wrap_handler.flag_wrap = wrap
self._find(text, self._flags, result_cb, 'search')
@@ -273,29 +249,44 @@ class WebEngineSearch(browsertab.AbstractSearch):
def clear(self):
if self.search_displayed:
self.cleared.emit()
+ self.search_match_changed.emit(0, 0)
self.search_displayed = False
- self._wrap_handler.reset_match_data()
+ self._reset_match_data()
self._widget.page().findText('')
def prev_result(self, *, result_cb=None):
# The int() here makes sure we get a copy of the flags.
flags = QWebEnginePage.FindFlags(int(self._flags))
if flags & QWebEnginePage.FindBackward:
- if self._wrap_handler.prevent_wrapping(going_up=False):
+ if self._wrap_handler.prevent_wrapping(self.current_match,
+ self.total_match_count, going_up=False):
+ result_cb(True)
return
flags &= ~QWebEnginePage.FindBackward
else:
- if self._wrap_handler.prevent_wrapping(going_up=True):
+ if self._wrap_handler.prevent_wrapping(self.current_match,
+ self.total_match_count, going_up=True):
+ result_cb(True)
return
flags |= QWebEnginePage.FindBackward
self._find(self.text, flags, result_cb, 'prev_result')
def next_result(self, *, result_cb=None):
going_up = self._flags & QWebEnginePage.FindBackward
- if self._wrap_handler.prevent_wrapping(going_up=going_up):
+ if self._wrap_handler.prevent_wrapping(self.current_match,
+ self.total_match_count, going_up=going_up):
+ result_cb(True)
return
self._find(self.text, self._flags, result_cb, 'next_result')
+ def _reset_match_data(self):
+ """Reset match counter information.
+
+ Stale information could lead to next_result or prev_result misbehaving.
+ """
+ self.current_match = 0
+ self.total_match_count = 0
+
class WebEngineCaret(browsertab.AbstractCaret):
diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml
index 5f348d102..4da003b37 100644
--- a/qutebrowser/config/configdata.yml
+++ b/qutebrowser/config/configdata.yml
@@ -72,6 +72,13 @@ search.wrap:
Wrap around at the top and bottom of the page when advancing through text
matches using `:search-next` and `:search-prev`.
+search.wrap_messages:
+ type: Bool
+ default: true
+ desc: >-
+ Display messages when advancing through text matches at the top and bottom
+ of the page, e.g. `Search hit TOP`.
+
new_instance_open_target:
type:
name: String
@@ -2058,6 +2065,7 @@ statusbar.widgets:
- scroll_raw: "Raw percentage of the current page position like `10`."
- history: "Display an arrow when possible to go back/forward in
history."
+ - search_match: "A match count when searching, e.g. `Match [2/10]`."
- tabs: "Current active tab, e.g. `2`."
- keypress: "Display pressed keys when composing a vi command."
- progress: "Progress bar for the current page loading."
@@ -2067,7 +2075,7 @@ statusbar.widgets:
https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes[the
Python datetime documentation]."
none_ok: true
- default: ['keypress', 'url', 'scroll', 'history', 'tabs', 'progress']
+ default: ['keypress', 'search_match', 'url', 'scroll', 'history', 'tabs', 'progress']
desc: "List of widgets displayed in the statusbar."
## tabs
diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py
index b247da632..c9744cfc1 100644
--- a/qutebrowser/mainwindow/mainwindow.py
+++ b/qutebrowser/mainwindow/mainwindow.py
@@ -537,6 +537,9 @@ class MainWindow(QWidget):
self.tabbed_browser.cur_load_status_changed.connect(
self.status.url.on_load_status_changed)
+ self.tabbed_browser.cur_search_match_changed.connect(
+ self.status.search_match.set_match_index)
+
self.tabbed_browser.cur_caret_selection_toggled.connect(
self.status.on_caret_selection_toggled)
diff --git a/qutebrowser/mainwindow/statusbar/bar.py b/qutebrowser/mainwindow/statusbar/bar.py
index ae33a386a..eaf60db3e 100644
--- a/qutebrowser/mainwindow/statusbar/bar.py
+++ b/qutebrowser/mainwindow/statusbar/bar.py
@@ -31,7 +31,7 @@ from qutebrowser.keyinput import modeman
from qutebrowser.utils import usertypes, log, objreg, utils
from qutebrowser.mainwindow.statusbar import (backforward, command, progress,
keystring, percentage, url,
- tabindex, textbase, clock)
+ tabindex, textbase, clock, searchmatch)
@dataclasses.dataclass
@@ -143,6 +143,7 @@ class StatusBar(QWidget):
url: The UrlText widget in the statusbar.
prog: The Progress widget in the statusbar.
cmd: The Command widget in the statusbar.
+ search_match: The SearchMatch widget in the statusbar.
_hbox: The main QHBoxLayout.
_stack: The QStackedLayout with cmd/txt widgets.
_win_id: The window ID the statusbar is associated with.
@@ -193,6 +194,8 @@ class StatusBar(QWidget):
self.cmd.hide_cmd.connect(self._hide_cmd_widget)
self._hide_cmd_widget()
+ self.search_match = searchmatch.SearchMatch()
+
self.url = url.UrlText()
self.percentage = percentage.Percentage()
self.backforward = backforward.Backforward()
@@ -225,6 +228,8 @@ class StatusBar(QWidget):
return self.keystring
elif key == 'progress':
return self.prog
+ elif key == 'search_match':
+ return self.search_match
elif key.startswith('text:'):
new_text_widget = textbase.TextBase()
self._text_widgets.append(new_text_widget)
@@ -243,7 +248,7 @@ class StatusBar(QWidget):
elif option == 'statusbar.widgets':
self._draw_widgets()
- def _draw_widgets(self):
+ def _draw_widgets(self): # noqa: C901
"""Draw statusbar widgets."""
self._clear_widgets()
diff --git a/qutebrowser/mainwindow/statusbar/searchmatch.py b/qutebrowser/mainwindow/statusbar/searchmatch.py
new file mode 100644
index 000000000..6e9a435de
--- /dev/null
+++ b/qutebrowser/mainwindow/statusbar/searchmatch.py
@@ -0,0 +1,49 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
+#
+# This file is part of qutebrowser.
+#
+# qutebrowser is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# qutebrowser is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
+
+"""The search match indicator in the statusbar."""
+
+
+from PyQt5.QtCore import pyqtSlot
+
+from qutebrowser.utils import log
+from qutebrowser.mainwindow.statusbar import textbase
+
+
+class SearchMatch(textbase.TextBase):
+
+ """The part of the statusbar that displays the search match counter."""
+
+ @pyqtSlot(int, int)
+ def set_match_index(self, current: int, total: int) -> None:
+ """Set the match counts in the statusbar.
+
+ Passing (0, 0) hides the match counter.
+
+ Args:
+ current: The currently active search match.
+ total: The total number of search matches on the page.
+ """
+ if current <= 0 and total <= 0:
+ self.setText('')
+ log.statusbar.debug('Clearing search match text.')
+ else:
+ self.setText('Match [{}/{}]'.format(current, total))
+ log.statusbar.debug('Setting search match text to {}/{}'
+ .format(current, total))
diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py
index 68b4adfdb..9a98b3d70 100644
--- a/qutebrowser/mainwindow/tabbedbrowser.py
+++ b/qutebrowser/mainwindow/tabbedbrowser.py
@@ -185,6 +185,7 @@ class TabbedBrowser(QWidget):
arg 1: x-position in %.
arg 2: y-position in %.
cur_load_status_changed: Loading status of current tab changed.
+ cur_search_match_changed: The active search match changed.
close_window: The last tab was closed, close this window.
resized: Emitted when the browser window has resized, so the completion
widget can adjust its size to it.
@@ -201,6 +202,7 @@ class TabbedBrowser(QWidget):
cur_link_hovered = pyqtSignal(str)
cur_scroll_perc_changed = pyqtSignal(int, int)
cur_load_status_changed = pyqtSignal(usertypes.LoadStatus)
+ cur_search_match_changed = pyqtSignal(int, int)
cur_fullscreen_requested = pyqtSignal(bool)
cur_caret_selection_toggled = pyqtSignal(browsertab.SelectionState)
close_window = pyqtSignal()
@@ -347,6 +349,8 @@ class TabbedBrowser(QWidget):
self._filter.create(self.cur_fullscreen_requested, tab))
tab.caret.selection_toggled.connect(
self._filter.create(self.cur_caret_selection_toggled, tab))
+ tab.search.search_match_changed.connect(
+ self._filter.create(self.cur_search_match_changed, tab))
# misc
tab.scroller.perc_changed.connect(self._on_scroll_pos_changed)
tab.scroller.before_jump_requested.connect(lambda: self.set_mark("'"))
@@ -901,6 +905,8 @@ class TabbedBrowser(QWidget):
.format(current_mode.name, mode_on_change))
self._now_focused = tab
self.current_tab_changed.emit(tab)
+ self.cur_search_match_changed.emit(tab.search.current_match,
+ tab.search.total_match_count)
QTimer.singleShot(0, self._update_window_title)
self._tab_insert_idx_left = self.widget.currentIndex()
self._tab_insert_idx_right = self.widget.currentIndex() + 1
diff --git a/scripts/dev/check_coverage.py b/scripts/dev/check_coverage.py
index 8f1d2df2b..0f8b23554 100644
--- a/scripts/dev/check_coverage.py
+++ b/scripts/dev/check_coverage.py
@@ -139,6 +139,8 @@ PERFECT_FILES = [
(None,
'qutebrowser/mainwindow/statusbar/keystring.py'),
+ (None,
+ 'qutebrowser/mainwindow/statusbar/searchmatch.py'),
('tests/unit/mainwindow/statusbar/test_percentage.py',
'qutebrowser/mainwindow/statusbar/percentage.py'),
('tests/unit/mainwindow/statusbar/test_progress.py',
diff --git a/tests/end2end/features/search.feature b/tests/end2end/features/search.feature
index 305b45690..86ceab70e 100644
--- a/tests/end2end/features/search.feature
+++ b/tests/end2end/features/search.feature
@@ -237,13 +237,13 @@ Feature: Searching on a page
Scenario: Preventing wrapping at the top of the page with QtWebEngine
When I set search.ignore_case to always
And I set search.wrap to false
+ And I set search.wrap_messages to true
And I run :search --reverse foo
And I wait for "search found foo with flags FindBackward" in the log
And I run :search-next
And I wait for "next_result found foo with flags FindBackward" in the log
And I run :search-next
- And I wait for "Search hit TOP" in the log
- Then "foo" should be found
+ Then the message "Search hit TOP" should be shown
@qtwebkit_skip @qt>=5.14
Scenario: Preventing wrapping at the bottom of the page with QtWebEngine
@@ -254,8 +254,7 @@ Feature: Searching on a page
And I run :search-next
And I wait for "next_result found foo" in the log
And I run :search-next
- And I wait for "Search hit BOTTOM" in the log
- Then "Foo" should be found
+ Then the message "Search hit BOTTOM" should be shown
@qtwebengine_skip
Scenario: Preventing wrapping at the top of the page with QtWebKit
@@ -281,6 +280,48 @@ Feature: Searching on a page
And I wait for "next_result didn't find foo" in the log
Then the warning "Text 'foo' not found on page!" should be shown
+ ## search match counter
+
+ @qtwebkit_skip @qt>=5.14
+ Scenario: Setting search match counter on search
+ When I set search.ignore_case to always
+ And I set search.wrap to true
+ And I run :search ba
+ And I wait for "search found ba" in the log
+ Then "Setting search match text to 1/5" should be logged
+
+ @qtwebkit_skip @qt>=5.14
+ Scenario: Updating search match counter on search-next
+ When I set search.ignore_case to always
+ And I set search.wrap to true
+ And I run :search ba
+ And I wait for "search found ba" in the log
+ And I run :search-next
+ And I wait for "next_result found ba" in the log
+ And I run :search-next
+ And I wait for "next_result found ba" in the log
+ Then "Setting search match text to 3/5" should be logged
+
+ @qtwebkit_skip @qt>=5.14
+ Scenario: Updating search match counter on search-prev with wrapping
+ When I set search.ignore_case to always
+ And I set search.wrap to true
+ And I run :search ba
+ And I wait for "search found ba" in the log
+ And I run :search-prev
+ And I wait for the message "Search hit TOP, continuing at BOTTOM"
+ Then "Setting search match text to 5/5" should be logged
+
+ @qtwebkit_skip @qt>=5.14
+ Scenario: Updating search match counter on search-prev without wrapping
+ When I set search.ignore_case to always
+ And I set search.wrap to false
+ And I run :search ba
+ And I wait for "search found ba" in the log
+ And I run :search-prev
+ And I wait for the message "Search hit TOP"
+ Then "Setting search match text to 1/5" should be logged
+
## follow searched links
@skip # Too flaky
Scenario: Follow a searched link