Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions lib/model/autocomplete.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ extension ComposeContentAutocomplete on ComposeContentController {
return null;
}

String stripMentionFormatting(String text) {
// Mentions are inserted as @**Name**; strip formatting so backspacing
// reopens autocomplete.
return text.replaceAll('**', '');
}

// To avoid spending a lot of time searching for autocomplete intents
// in long messages, we bound how far back we look for the intent's start.
final earliest = max(0, selection.end - 30);
Expand All @@ -37,11 +43,15 @@ extension ComposeContentAutocomplete on ComposeContentController {
}

final textUntilCursor = text.substring(0, selection.end);
final rawTextUntilCursor = textUntilCursor;
int pos;
for (pos = selection.end - 1; pos > selection.start; pos--) {
final charAtPos = textUntilCursor[pos];
if (charAtPos == '@') {
final match = _mentionIntentRegex.matchAsPrefix(textUntilCursor, pos);
final candidate = rawTextUntilCursor.substring(pos);
final normalizedCandidate = stripMentionFormatting(candidate);
final normalizedText = rawTextUntilCursor.substring(0, pos) + normalizedCandidate;
final match = _mentionIntentRegex.matchAsPrefix(normalizedText,pos);
if (match == null) continue;
} else if (charAtPos == ':') {
final match = _emojiIntentRegex.matchAsPrefix(textUntilCursor, pos);
Expand All @@ -57,7 +67,10 @@ extension ComposeContentAutocomplete on ComposeContentController {
final charAtPos = textUntilCursor[pos];
final ComposeAutocompleteQuery query;
if (charAtPos == '@') {
final match = _mentionIntentRegex.matchAsPrefix(textUntilCursor, pos);
final candidate = rawTextUntilCursor.substring(pos);
final normalizedCandidate = stripMentionFormatting(candidate);
final normalizedText = rawTextUntilCursor.substring(0, pos) + normalizedCandidate;
final match = _mentionIntentRegex.matchAsPrefix(normalizedText,pos);
if (match == null) continue;
query = MentionAutocompleteQuery(match[2]!, silent: match[1]! == '_');
} else if (charAtPos == ':') {
Expand Down
8 changes: 4 additions & 4 deletions test/model/autocomplete_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ void main() {
doTest('email support@ with details of the issue^', null);
doTest('email support@^ with details of the issue', null);

doTest('Ask @**Chris Bobbe**^', null); doTest('Ask @_**Chris Bobbe**^', null);
doTest('Ask @**Chris Bobbe^**', null); doTest('Ask @_**Chris Bobbe^**', null);
doTest('Ask @**Chris^ Bobbe**', null); doTest('Ask @_**Chris^ Bobbe**', null);
doTest('Ask @**^Chris Bobbe**', null); doTest('Ask @_**^Chris Bobbe**', null);
doTest('Ask ~@**Chris Bobbe**^', mention('Chris Bobbe'));doTest('Ask ~@_**Chris Bobbe**^', silentMention('Chris Bobbe'));
doTest('Ask ~@**Chris Bobbe^**', mention('Chris Bobbe'));doTest('Ask ~@_**Chris Bobbe^**', silentMention('Chris Bobbe'));
doTest('Ask ~@**Chris^ Bobbe**', mention('Chris')); doTest('Ask ~@_**Chris^ Bobbe**', silentMention('Chris'));
doTest('Ask ~@**^Chris Bobbe**', mention('')); doTest('Ask ~@_**^Chris Bobbe**', silentMention(''));

doTest('`@chris^', null); doTest('`@_chris^', null);

Expand Down
22 changes: 9 additions & 13 deletions test/widgets/autocomplete_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ void main() {
check(find.text(user.fullName)).findsExactly(expected ? 1 : 0);
check(findAvatarImage(user.userId)).findsExactly(expected ? 1 : 0);
}
void checkAutocompleteHidden() {
check(find.byType(Autocomplete)).findsNothing();
}

testWidgets('user options appear, disappear, and change correctly', (tester) async {
final user1 = eg.user(userId: 1, fullName: 'User One', avatarUrl: 'user1.png');
Expand All @@ -185,9 +188,8 @@ void main() {
await tester.pump();
check(tester.widget<TextField>(composeInputFinder).controller!.text)
.contains(userMention(user3, users: store));
checkUserShown(user1, expected: false);
checkUserShown(user2, expected: false);
checkUserShown(user3, expected: false);
checkAutocompleteHidden();


// Then a new autocomplete intent brings up options again
// TODO(#226): Remove this extra edit when this bug is fixed.
Expand All @@ -200,9 +202,8 @@ void main() {
// TODO(#226): Remove one of these edits when this bug is fixed.
await tester.enterText(composeInputFinder, '');
await tester.enterText(composeInputFinder, ' ');
checkUserShown(user1, expected: false);
checkUserShown(user2, expected: false);
checkUserShown(user3, expected: false);
checkAutocompleteHidden();


debugNetworkImageHttpClientProvider = null;
});
Expand Down Expand Up @@ -279,15 +280,10 @@ void main() {

// Finishing autocomplete updates compose box; causes options to disappear
await tester.tap(find.text(WildcardMentionOption.channel.canonicalString));
await tester.pump();
await tester.pumpAndSettle();
check(tester.widget<TextField>(composeInputFinder).controller!.text)
.contains(wildcardMention(WildcardMentionOption.channel, store: store));
checkWildcardShown(WildcardMentionOption.channel, expected: false);
checkWildcardShown(WildcardMentionOption.topic, expected: false);
checkWildcardShown(WildcardMentionOption.all, expected: false);
checkWildcardShown(WildcardMentionOption.everyone, expected: false);
checkWildcardShown(WildcardMentionOption.stream, expected: false);

checkAutocompleteHidden();
debugNetworkImageHttpClientProvider = null;
});

Expand Down