Skip to content

Commit b8b0779

Browse files
committed
boxes: Improve trigger sequence for generic_autocomplete.
Updated generic_autocomplete along with its autocomplete_mentions/ streams/emojis to trigger typeahead even when there are some characters before their respective trigger character. Tests amended. Fixes #541.
1 parent 0023f9e commit b8b0779

File tree

2 files changed

+33
-12
lines changed

2 files changed

+33
-12
lines changed

tests/ui_tools/test_boxes.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ def test_init(self, write_box):
2929
('@_Boo', '@_', 1),
3030
('Plain Text', '', 0),
3131
('Plain Text', '', 1),
32+
('(@Boo', '@', 0),
33+
('(@_Boo', '@_', 0),
34+
('(', '', 0),
3235
])
3336
def test_generic_autocomplete(self, mocker, write_box, text,
3437
prefix_string, state):
@@ -93,6 +96,13 @@ def test_generic_autocomplete(self, mocker, write_box, text,
9396
('@_', 3, '@_', None), # Reached last match
9497
('@_', 4, '@_', None), # Beyond end
9598
('@_', -1, '@_', '@_**Human 2**'),
99+
# When some character is preceding the prefix_string (trigger).
100+
('(@H', 0, '@', '(@**Human Myself**'),
101+
('(@H', 1, '@', '(@**Human 1**'),
102+
('(@_H', 0, '@_', '(@_**Human Myself**'),
103+
('(@_H', 1, '@_', '(@_**Human 1**'),
104+
('(@G', 0, '@', '(@*Group 1*'),
105+
('(@G', 1, '@', '(@*Group 2*'),
96106
])
97107
def test_autocomplete_mentions(self, write_box, users_fixture,
98108
text, state, prefix_string,
@@ -124,7 +134,9 @@ def test_autocomplete_mentions(self, write_box, users_fixture,
124134
('#St', 1, '#**Stream 2**'),
125135
('#Stream 1', 0, '#**Stream 1**'),
126136
('No match', 0, None),
127-
('No match', -1, None)
137+
('No match', -1, None),
138+
('(#Stream', 0, '(#**Stream 1**'),
139+
('(#Stream', 1, '(#**Stream 2**'),
128140
])
129141
@pytest.mark.parametrize('prefix_string', '#')
130142
def test_autocomplete_streams(self, write_box, streams_fixture,
@@ -158,6 +170,8 @@ def test_autocomplete_streams(self, write_box, streams_fixture,
158170
('no match', 0, None),
159171
(':nomatch', 0, None),
160172
(':nomatch', -1, None),
173+
('(:smi', 0, '(:smile:'),
174+
('(:smi', 1, '(:smiley:'),
161175
])
162176
@pytest.mark.parametrize('prefix_string', ':')
163177
def test_autocomplete_emojis(self, write_box, emojis_fixture,

zulipterminal/ui_tools/boxes.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,13 @@ def stream_box_view(self, caption: str='', title: str='') -> None:
9595
self.contents = write_box
9696

9797
def generic_autocomplete(self, text: str, state: int) -> Optional[str]:
98-
if text.startswith('@_'):
98+
if '@_' in text:
9999
return self.autocomplete_mentions(text, state, '@_')
100-
elif text.startswith('@'):
100+
elif '@' in text:
101101
return self.autocomplete_mentions(text, state, '@')
102-
elif text.startswith('#'):
102+
elif '#' in text:
103103
return self.autocomplete_streams(text, state, '#')
104-
elif text.startswith(':'):
104+
elif ':' in text:
105105
return self.autocomplete_emojis(text, state, ':')
106106
else:
107107
return text
@@ -110,39 +110,46 @@ def autocomplete_mentions(self, text: str, state: int,
110110
prefix_string: str) -> Optional[str]:
111111
# Handles user mentions (@ mentions and silent mentions)
112112
# and group mentions.
113+
prefix_index = max(text.find(prefix_string), 0)
113114
group_typeahead = [prefix_string+'*{}*'.format(group_name)
114115
for group_name in self.model.user_group_names
115-
if match_groups(group_name, text[1:])]
116+
if match_groups(group_name, text[1+prefix_index:])]
116117

117118
users_list = self.view.users
118119
user_typeahead = [prefix_string+'**{}**'.format(user['full_name'])
119120
for user in users_list
120-
if match_user(user, text[len(prefix_string):])]
121+
if match_user(
122+
user, text[len(prefix_string)+prefix_index:])]
121123
combined_typeahead = group_typeahead + user_typeahead
122124
try:
123-
return combined_typeahead[state]
125+
return text[:prefix_index] + combined_typeahead[state]
124126
except (IndexError, TypeError):
125127
return None
126128

127129
def autocomplete_streams(self, text: str, state: int,
128130
prefix_string: str) -> Optional[str]:
131+
prefix_index = max(text.find(prefix_string), 0)
129132
streams_list = self.view.pinned_streams + self.view.unpinned_streams
130133
stream_typeahead = [prefix_string+'**{}**'.format(stream[0])
131134
for stream in streams_list
132-
if match_stream(stream, text[len(prefix_string):])]
135+
if match_stream(
136+
stream, text[len(prefix_string)
137+
+ prefix_index:])]
133138
try:
134-
return stream_typeahead[state]
139+
return text[:prefix_index] + stream_typeahead[state]
135140
except (IndexError, TypeError):
136141
return None
137142

138143
def autocomplete_emojis(self, text: str, state: int, prefix_string: str,
139144
emoji_list: List[str] = EMOJI_NAMES
140145
) -> Optional[str]:
146+
prefix_index = max(text.find(prefix_string), 0)
141147
emoji_typeahead = [(prefix_string+'{}'+prefix_string).format(emoji)
142148
for emoji in emoji_list
143-
if match_emoji(emoji, text[len(prefix_string):])]
149+
if match_emoji(emoji, text[len(prefix_string)
150+
+ prefix_index:])]
144151
try:
145-
return emoji_typeahead[state]
152+
return text[:prefix_index] + emoji_typeahead[state]
146153
except (IndexError, TypeError):
147154
return None
148155

0 commit comments

Comments
 (0)