Skip to content

Commit 06a00e7

Browse files
committed
autocomplete [nfc]: Add bucket-sort step in mention-autocomplete
Like we do in emoji autocomplete. This commit doesn't make any changes to the results ordering; bucketSort sorts stably, and the input is still just the wildcard results followed by the user results. Soon, though, we'd like to rank by match quality and add user-group results (for zulip#233) interleaved with user results. Bucket sorting will help us do this without making many intermediate copies of lists of results; see discussion: https://chat.zulip.org/#narrow/channel/48-mobile/topic/user-group.20mentions.20.23F233/near/2216353
1 parent 8cba9a7 commit 06a00e7

File tree

1 file changed

+41
-9
lines changed

1 file changed

+41
-9
lines changed

lib/model/autocomplete.dart

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import '../api/model/model.dart';
88
import '../api/route/channels.dart';
99
import '../generated/l10n/zulip_localizations.dart';
1010
import '../widgets/compose_box.dart';
11+
import 'algorithms.dart';
1112
import 'compose.dart';
1213
import 'emoji.dart';
1314
import 'narrow.dart';
@@ -636,16 +637,18 @@ class MentionAutocompleteView extends AutocompleteView<MentionAutocompleteQuery,
636637

637638
@override
638639
Future<List<MentionAutocompleteResult>?> computeResults() async {
639-
final results = <MentionAutocompleteResult>[];
640+
final unsorted = <MentionAutocompleteResult>[];
640641
// Give priority to wildcard mentions.
641-
computeWildcardMentionResults(results: results,
642+
computeWildcardMentionResults(results: unsorted,
642643
isComposingChannelMessage: narrow is ChannelNarrow || narrow is TopicNarrow);
643644

644645
if (await filterCandidates(filter: _testUser,
645-
candidates: sortedUsers, results: results)) {
646+
candidates: sortedUsers, results: unsorted)) {
646647
return null;
647648
}
648-
return results;
649+
650+
return bucketSort(unsorted,
651+
(r) => r.rank, numBuckets: MentionAutocompleteQuery._numResultRanks);
649652
}
650653

651654
MentionAutocompleteResult? _testUser(MentionAutocompleteQuery query, User user) {
@@ -750,7 +753,8 @@ class MentionAutocompleteQuery extends ComposeAutocompleteQuery {
750753
final matches = wildcardOption.canonicalString.contains(_lowercase)
751754
|| wildcardOption.localizedCanonicalString(localizations).contains(_lowercase);
752755
if (!matches) return null;
753-
return WildcardMentionAutocompleteResult(wildcardOption: wildcardOption);
756+
return WildcardMentionAutocompleteResult(
757+
wildcardOption: wildcardOption, rank: _rankWildcardResult);
754758
}
755759

756760
MentionAutocompleteResult? testUser(User user, AutocompleteDataCache cache, UserStore store) {
@@ -760,13 +764,30 @@ class MentionAutocompleteQuery extends ComposeAutocompleteQuery {
760764
// TODO(#236) test email too, not just name
761765
if (!_testName(user, cache)) return null;
762766

763-
return UserMentionAutocompleteResult(userId: user.userId);
767+
return UserMentionAutocompleteResult(
768+
userId: user.userId, rank: _rankUserResult);
764769
}
765770

766771
bool _testName(User user, AutocompleteDataCache cache) {
767772
return _testContainsQueryWords(cache.nameWordsForUser(user));
768773
}
769774

775+
/// A measure of a wildcard result's quality in the context of the query,
776+
/// from 0 (best) to one less than [_numResultRanks].
777+
///
778+
/// See also [_rankUserResult].
779+
static const _rankWildcardResult = 0;
780+
781+
/// A measure of a user result's quality in the context of the query,
782+
/// from 0 (best) to one less than [_numResultRanks].
783+
///
784+
/// See also [_rankWildcardResult].
785+
static const _rankUserResult = 1;
786+
787+
/// The number of possible values returned by
788+
/// [_rankWildcardResult] and [_rankUserResult].
789+
static const _numResultRanks = 2;
790+
770791
@override
771792
String toString() {
772793
return '${objectRuntimeType(this, 'MentionAutocompleteQuery')}(raw: $raw, silent: $silent})';
@@ -853,20 +874,31 @@ class EmojiAutocompleteResult extends ComposeAutocompleteResult {
853874
/// This is abstract because there are several kinds of result
854875
/// that can all be offered in the same @-mention autocomplete interaction:
855876
/// a user, a wildcard, or a user group.
856-
sealed class MentionAutocompleteResult extends ComposeAutocompleteResult {}
877+
sealed class MentionAutocompleteResult extends ComposeAutocompleteResult {
878+
/// A measure of the result's quality in the context of the query.
879+
///
880+
/// Used internally by [MentionAutocompleteView] for ranking the results.
881+
int get rank;
882+
}
857883

858884
/// An autocomplete result for an @-mention of an individual user.
859885
class UserMentionAutocompleteResult extends MentionAutocompleteResult {
860-
UserMentionAutocompleteResult({required this.userId});
886+
UserMentionAutocompleteResult({required this.userId, required this.rank});
861887

862888
final int userId;
889+
890+
@override
891+
final int rank;
863892
}
864893

865894
/// An autocomplete result for an @-mention of all the users in a conversation.
866895
class WildcardMentionAutocompleteResult extends MentionAutocompleteResult {
867-
WildcardMentionAutocompleteResult({required this.wildcardOption});
896+
WildcardMentionAutocompleteResult({required this.wildcardOption, required this.rank});
868897

869898
final WildcardMentionOption wildcardOption;
899+
900+
@override
901+
final int rank;
870902
}
871903

872904
// TODO(#233): // class UserGroupMentionAutocompleteResult extends MentionAutocompleteResult {

0 commit comments

Comments
 (0)