diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index ee0634dc4..ec9655280 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -71,7 +71,12 @@ class SearchRunner(QObject): self.do_search.emit('', QWebPage.HighlightAllOccurrences) self._text = text self._flags = 0 - if not config.get('general', 'ignore-case'): + ignore_case = config.get('general', 'ignore-case') + if ignore_case == 'smart': + if not text.islower(): + self._flags |= QWebPage.FindCaseSensitively + elif ignore_case: + # True, but not 'smart' self._flags |= QWebPage.FindCaseSensitively if config.get('general', 'wrap-search'): self._flags |= QWebPage.FindWrapsAroundDocument diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 27ef05c70..61bbead1a 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -168,7 +168,7 @@ SECTION_DESC = { DATA = OrderedDict([ ('general', sect.KeyValue( ('ignore-case', - SettingValue(types.Bool(), 'true'), + SettingValue(types.IgnoreCase(), 'smart'), "Whether to find text on a page case-insensitively."), ('wrap-search', diff --git a/qutebrowser/config/conftypes.py b/qutebrowser/config/conftypes.py index 4452056f0..295b5de2c 100644 --- a/qutebrowser/config/conftypes.py +++ b/qutebrowser/config/conftypes.py @@ -1182,3 +1182,26 @@ class NewTabPosition(BaseType): ('right', "On the right of the current tab."), ('first', "At the left end."), ('last', "At the right end.")) + + +class IgnoreCase(Bool): + + """Whether to ignore case when searching.""" + + def transform(self, value): + if value.lower() == 'smart': + return 'smart' + else: + return super().transform(value) + + def validate(self, value): + if value.lower() == 'smart': + return + else: + super().validate(value) + + def complete(self): + return [('true', 'Search case-insensitively'), + ('false', 'Search case-sensitively'), + ('smart', 'Search case-sensitively if there are capital ' + 'chars')] diff --git a/qutebrowser/test/config/test_conftypes.py b/qutebrowser/test/config/test_conftypes.py index fa1593754..3b9cbee34 100644 --- a/qutebrowser/test/config/test_conftypes.py +++ b/qutebrowser/test/config/test_conftypes.py @@ -1826,5 +1826,55 @@ class AutoSearchTests(unittest.TestCase): self.assertIsNone(self.t.transform('')) +class IgnoreCase(unittest.TestCase): + + """Test IgnoreCase.""" + + TESTS = { + 'smart': ['smart', 'SMART'], + True: BoolTests.TESTS[True], + False: BoolTests.TESTS[False], + } + INVALID = ['ssmart', 'foo'] + + def setUp(self): + self.t = conftypes.IgnoreCase() + + def test_validate_empty(self): + """Test validate with empty string and none_ok = False.""" + with self.assertRaises(conftypes.ValidationError): + self.t.validate('') + + def test_validate_empty_none_ok(self): + """Test validate with empty string and none_ok = True.""" + t = conftypes.IgnoreCase(none_ok=True) + t.validate('') + + def test_validate_valid(self): + """Test validate with valid values.""" + for vallist in self.TESTS.values(): + for val in vallist: + with self.subTest(val=val): + self.t.validate(val) + + def test_validate_invalid(self): + """Test validate with invalid values.""" + for val in self.INVALID: + with self.subTest(val=val): + with self.assertRaises(conftypes.ValidationError): + self.t.validate(val) + + def test_transform(self): + """Test transform with all values.""" + for out, inputs in self.TESTS.items(): + for inp in inputs: + with self.subTest(inp=inp): + self.assertEqual(self.t.transform(inp), out, inp) + + def test_transform_empty(self): + """Test transform with none_ok = False and an empty value.""" + self.assertIsNone(self.t.transform('')) + + if __name__ == '__main__': unittest.main()