diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 3075a24da..ec84522c2 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1749,7 +1749,8 @@ class CommandDispatcher: elif going_up and tab.scroller.pos_px().y() > old_scroll_pos.y(): message.info("Search hit TOP, continuing at BOTTOM") else: - message.warning("Text '{}' not found on page!".format(text)) + message.warning("Text '{}' not found on page!".format(text), + replace=True) @cmdutils.register(instance='command-dispatcher', scope='window', maxsplit=0) @@ -1769,7 +1770,7 @@ class CommandDispatcher: return options = { - 'ignore_case': config.val.ignore_case, + 'ignore_case': config.val.search.ignore_case, 'reverse': reverse, } diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index 1318e8979..15be675ae 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -34,6 +34,9 @@ history_gap_interval: `:history`. Use -1 to disable separation. ignore_case: + renamed: search.ignore_case + +search.ignore_case: type: name: String valid_values: @@ -43,6 +46,11 @@ ignore_case: default: smart desc: When to find text on a page case-insensitively. +search.incremental: + type: Bool + default: True + desc: Find text on a page incrementally, renewing the search for each typed character. + new_instance_open_target: type: name: String diff --git a/qutebrowser/mainwindow/statusbar/command.py b/qutebrowser/mainwindow/statusbar/command.py index af2dc3dc9..8ae4fd566 100644 --- a/qutebrowser/mainwindow/statusbar/command.py +++ b/qutebrowser/mainwindow/statusbar/command.py @@ -27,6 +27,7 @@ from qutebrowser.commands import cmdexc, cmdutils from qutebrowser.misc import cmdhistory, editor from qutebrowser.misc import miscwidgets as misc from qutebrowser.utils import usertypes, log, objreg, message +from qutebrowser.config import config class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): @@ -66,6 +67,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): self.cursorPositionChanged.connect(self.update_completion) self.textChanged.connect(self.update_completion) self.textChanged.connect(self.updateGeometry) + self.textChanged.connect(self._incremental_search) def prefix(self): """Get the currently entered command prefix.""" @@ -238,3 +240,16 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): text = 'x' width = self.fontMetrics().width(text) return QSize(width, height) + + @pyqtSlot(str) + def _incremental_search(self, text): + if not config.val.search.incremental: + return + + search_prefixes = { + '/': 'search -- ', + '?': 'search -r -- ', + } + + if self.prefix() in ['/', '?']: + self.got_cmd[str].emit(search_prefixes[text[0]] + text[1:]) diff --git a/tests/end2end/features/qutescheme.feature b/tests/end2end/features/qutescheme.feature index 1f13a8ac1..755c103e7 100644 --- a/tests/end2end/features/qutescheme.feature +++ b/tests/end2end/features/qutescheme.feature @@ -99,19 +99,19 @@ Feature: Special qute:// pages # qute://settings Scenario: Focusing input fields in qute://settings and entering valid value - When I set ignore_case to never + When I set search.ignore_case to never And I open qute://settings # scroll to the right - the table does not fit in the default screen And I run :scroll-to-perc -x 100 - And I run :jseval document.getElementById('input-ignore_case').value = '' - And I run :click-element id input-ignore_case + And I run :jseval document.getElementById('input-search.ignore_case').value = '' + And I run :click-element id input-search.ignore_case And I wait for "Entering mode KeyMode.insert *" in the log And I press the keys "always" And I press the key "" # an explicit Tab to unfocus the input field seems to stabilize the tests And I press the key "" - And I wait for "Config option changed: ignore_case *" in the log - Then the option ignore_case should be set to always + And I wait for "Config option changed: search.ignore_case *" in the log + Then the option search.ignore_case should be set to always # Sometimes, an unrelated value gets set @flaky @@ -119,8 +119,8 @@ Feature: Special qute:// pages When I open qute://settings # scroll to the right - the table does not fit in the default screen And I run :scroll-to-perc -x 100 - And I run :jseval document.getElementById('input-ignore_case').value = '' - And I run :click-element id input-ignore_case + And I run :jseval document.getElementById('input-search.ignore_case').value = '' + And I run :click-element id input-search.ignore_case And I wait for "Entering mode KeyMode.insert *" in the log And I press the keys "foo" And I press the key "" diff --git a/tests/end2end/features/search.feature b/tests/end2end/features/search.feature index 56fcca207..d1a00b5b6 100644 --- a/tests/end2end/features/search.feature +++ b/tests/end2end/features/search.feature @@ -22,7 +22,7 @@ Feature: Searching on a page Then "Bar" should be found Scenario: Searching with --reverse - When I set ignore_case to always + When I set search.ignore_case to always And I run :search -r foo And I wait for "search found foo with flags FindBackward" in the log Then "Foo" should be found @@ -52,28 +52,28 @@ Feature: Searching on a page And I wait for "search didn't find blub" in the log Then the warning "Text 'blub' not found on page!" should be shown - ## ignore_case + ## search.ignore_case - Scenario: Searching text with ignore_case = always - When I set ignore_case to always + Scenario: Searching text with search.ignore_case = always + When I set search.ignore_case to always And I run :search bar And I wait for "search found bar" in the log Then "Bar" should be found - Scenario: Searching text with ignore_case = never - When I set ignore_case to never + Scenario: Searching text with search.ignore_case = never + When I set search.ignore_case to never And I run :search bar And I wait for "search found bar with flags FindCaseSensitively" in the log Then "bar" should be found - Scenario: Searching text with ignore_case = smart (lower-case) - When I set ignore_case to smart + Scenario: Searching text with search.ignore_case = smart (lower-case) + When I set search.ignore_case to smart And I run :search bar And I wait for "search found bar" in the log Then "Bar" should be found - Scenario: Searching text with ignore_case = smart (upper-case) - When I set ignore_case to smart + Scenario: Searching text with search.ignore_case = smart (upper-case) + When I set search.ignore_case to smart And I run :search Foo And I wait for "search found Foo with flags FindCaseSensitively" in the log Then "Foo" should be found # even though foo was first @@ -81,7 +81,7 @@ Feature: Searching on a page ## :search-next Scenario: Jumping to next match - When I set ignore_case to always + When I set search.ignore_case to always And I run :search foo And I wait for "search found foo" in the log And I run :search-next @@ -89,7 +89,7 @@ Feature: Searching on a page Then "Foo" should be found Scenario: Jumping to next match with count - When I set ignore_case to always + When I set search.ignore_case to always And I run :search baz And I wait for "search found baz" in the log And I run :search-next with count 2 @@ -97,7 +97,7 @@ Feature: Searching on a page Then "BAZ" should be found Scenario: Jumping to next match with --reverse - When I set ignore_case to always + When I set search.ignore_case to always And I run :search --reverse foo And I wait for "search found foo with flags FindBackward" in the log And I run :search-next @@ -121,7 +121,7 @@ Feature: Searching on a page # https://github.com/qutebrowser/qutebrowser/issues/2438 Scenario: Jumping to next match after clearing - When I set ignore_case to always + When I set search.ignore_case to always And I run :search foo And I wait for "search found foo" in the log And I run :search @@ -132,7 +132,7 @@ Feature: Searching on a page ## :search-prev Scenario: Jumping to previous match - When I set ignore_case to always + When I set search.ignore_case to always And I run :search foo And I wait for "search found foo" in the log And I run :search-next @@ -142,7 +142,7 @@ Feature: Searching on a page Then "foo" should be found Scenario: Jumping to previous match with count - When I set ignore_case to always + When I set search.ignore_case to always And I run :search baz And I wait for "search found baz" in the log And I run :search-next @@ -154,7 +154,7 @@ Feature: Searching on a page Then "baz" should be found Scenario: Jumping to previous match with --reverse - When I set ignore_case to always + When I set search.ignore_case to always And I run :search --reverse foo And I wait for "search found foo with flags FindBackward" in the log And I run :search-next diff --git a/tests/end2end/test_invocations.py b/tests/end2end/test_invocations.py index dc9486142..ef4808718 100644 --- a/tests/end2end/test_invocations.py +++ b/tests/end2end/test_invocations.py @@ -359,14 +359,14 @@ def test_qute_settings_persistence(short_tmpdir, request, quteproc_new): args = _base_args(request.config) + ['--basedir', str(short_tmpdir)] quteproc_new.start(args) quteproc_new.open_path( - 'qute://settings/set?option=ignore_case&value=always') - assert quteproc_new.get_setting('ignore_case') == 'always' + 'qute://settings/set?option=search.ignore_case&value=always') + assert quteproc_new.get_setting('search.ignore_case') == 'always' quteproc_new.send_cmd(':quit') quteproc_new.wait_for_quit() quteproc_new.start(args) - assert quteproc_new.get_setting('ignore_case') == 'always' + assert quteproc_new.get_setting('search.ignore_case') == 'always' @pytest.mark.no_xvfb diff --git a/tests/unit/config/test_configcommands.py b/tests/unit/config/test_configcommands.py index 7137f50db..4c0c833a1 100644 --- a/tests/unit/config/test_configcommands.py +++ b/tests/unit/config/test_configcommands.py @@ -281,7 +281,7 @@ class TestSource: def test_config_source(self, tmpdir, commands, config_stub, config_tmpdir, use_default_dir, clear): assert config_stub.val.content.javascript.enabled - config_stub.val.ignore_case = 'always' + config_stub.val.search.ignore_case = 'always' if use_default_dir: pyfile = config_tmpdir / 'config.py' @@ -295,7 +295,8 @@ class TestSource: commands.config_source(arg, clear=clear) assert not config_stub.val.content.javascript.enabled - assert config_stub.val.ignore_case == ('smart' if clear else 'always') + ignore_case = config_stub.val.search.ignore_case + assert ignore_case == ('smart' if clear else 'always') def test_errors(self, commands, config_tmpdir): pyfile = config_tmpdir / 'config.py' diff --git a/tests/unit/config/test_configdata.py b/tests/unit/config/test_configdata.py index 7edb1b0d6..b7e960ac6 100644 --- a/tests/unit/config/test_configdata.py +++ b/tests/unit/config/test_configdata.py @@ -34,7 +34,7 @@ def test_init(config_stub): # configdata.init() is called by config_stub config_stub.val.aliases = {} assert isinstance(configdata.DATA, dict) - assert 'ignore_case' in configdata.DATA + assert 'search.ignore_case' in configdata.DATA def test_data(config_stub):