Skip to content

Commit 94d17f5

Browse files
authored
Merge pull request #1180 from three-comrades/tag_complete
Tag completion: escape RE characters in search pattern.
2 parents 5c2a735 + f895550 commit 94d17f5

File tree

6 files changed

+33
-3
lines changed

6 files changed

+33
-3
lines changed

alot/addressbook/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def get_contacts(self): # pragma no cover
3434
def lookup(self, query=''):
3535
"""looks up all contacts where name or address match query"""
3636
res = []
37-
query = re.compile('.*%s.*' % query, self.reflags)
37+
query = re.compile('.*%s.*' % re.escape(query), self.reflags)
3838
for name, email in self.get_contacts():
3939
if query.match(name) or query.match(email):
4040
res.append((name, email))

alot/completion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def complete(self, original, pos):
7575
re_prefix = '.*' if self.match_anywhere else ''
7676

7777
def match(s, m):
78-
r = re_prefix + m + '.*'
78+
r = '{}{}.*'.format(re_prefix, re.escape(m))
7979
return re.match(r, s, flags=self.flags) is not None
8080

8181
return [(a, len(a)) for a in self.resultlist if match(a, pref)]

alot/settings/manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ def colourpick(triple):
320320
fallback_focus = resolve_att(onebelow_focus, default_focus)
321321

322322
for sec in cfg['tags'].sections:
323-
if re.match('^' + sec + '$', tag):
323+
if re.match('^{}$'.format(re.escape(sec)), tag):
324324
normal = resolve_att(colourpick(cfg['tags'][sec]['normal']),
325325
fallback_normal)
326326
focus = resolve_att(colourpick(cfg['tags'][sec]['focus']),

tests/addressbook/init_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,12 @@ def test_lookup_will_match_partial_in_the_middle(self):
6565
actual = abook.lookup('Own')
6666
expected = [contacts[1]]
6767
self.assertListEqual(actual, expected)
68+
69+
def test_lookup_can_handle_special_regex_chars(self):
70+
contacts = [('name [work]', 'email@example.com'),
71+
('My Own Name', 'other@example.com'),
72+
('someone', 'someone@example.com')]
73+
abook = _AddressBook(contacts)
74+
actual = abook.lookup('[wor')
75+
expected = [contacts[0]]
76+
self.assertListEqual(actual, expected)

tests/completion_test.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,12 @@ def test_real_name_with_quotes_and_comma(self):
8787
expected = [(r""""all 'fanzy' \"stuff\" at, once" <all@example.com>""",
8888
50)]
8989
self._assert_only_one_list_entry(actual, expected)
90+
91+
92+
class StringlistCompleterTest(unittest.TestCase):
93+
def test_dont_choke_on_special_regex_characters(self):
94+
tags = ['[match]', 'nomatch']
95+
completer = completion.StringlistCompleter(tags)
96+
actual = completer.complete('[', 1)
97+
expected = [(tags[0], len(tags[0]))]
98+
self.assertListEqual(actual, expected)

tests/settings/manager_test.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ def test_read_notmuch_config_doesnt_exist(self):
9494
setting = manager.get_notmuch_setting('foo', 'bar')
9595
self.assertIsNone(setting)
9696

97+
def test_dont_choke_on_regex_special_chars_in_tagstring(self):
98+
tag = 'to**do'
99+
with tempfile.NamedTemporaryFile(delete=False) as f:
100+
f.write(textwrap.dedent("""\
101+
[tags]
102+
[[{tag}]]
103+
normal = '','', 'white','light red', 'white','#d66'
104+
""".format(tag=tag)))
105+
self.addCleanup(os.unlink, f.name)
106+
manager = SettingsManager(alot_rc=f.name)
107+
manager.get_tagstring_representation(tag)
108+
97109

98110
class TestSettingsManagerGetAccountByAddress(utilities.TestCaseClassCleanup):
99111
"""Test the get_account_by_address helper."""

0 commit comments

Comments
 (0)