@@ -5,6 +5,7 @@ import '../api/model/events.dart';
5
5
import '../api/model/initial_snapshot.dart' ;
6
6
import '../api/model/model.dart' ;
7
7
import '../api/route/realm.dart' ;
8
+ import 'algorithms.dart' ;
8
9
import 'autocomplete.dart' ;
9
10
import 'narrow.dart' ;
10
11
import 'store.dart' ;
@@ -319,6 +320,24 @@ class EmojiStoreImpl with EmojiStore {
319
320
}
320
321
}
321
322
323
+ /// The quality of an emoji's match to an autocomplete query.
324
+ ///
325
+ /// (Rather vacuous for the moment; this structure will
326
+ /// gain more substance in an upcoming commit.)
327
+ enum EmojiMatchQuality {
328
+ match;
329
+
330
+ /// The best possible quality of match.
331
+ static const best = match;
332
+
333
+ /// The better of the two given qualities of match,
334
+ /// where null represents no match at all.
335
+ static EmojiMatchQuality ? bestOf (EmojiMatchQuality ? a, EmojiMatchQuality ? b) {
336
+ if (b == null ) return a;
337
+ return b;
338
+ }
339
+ }
340
+
322
341
class EmojiAutocompleteView extends AutocompleteView <EmojiAutocompleteQuery , EmojiAutocompleteResult > {
323
342
EmojiAutocompleteView ._({required super .store, required super .query});
324
343
@@ -333,13 +352,13 @@ class EmojiAutocompleteView extends AutocompleteView<EmojiAutocompleteQuery, Emo
333
352
334
353
@override
335
354
Future <List <EmojiAutocompleteResult >?> computeResults () async {
336
- // TODO(#1068): rank emoji results (popular, realm, other; exact match, prefix, other)
337
- final results = < EmojiAutocompleteResult > [];
355
+ final unsorted = < EmojiAutocompleteResult > [];
338
356
if (await filterCandidates (filter: _testCandidate,
339
- candidates: store.allEmojiCandidates (), results: results )) {
357
+ candidates: store.allEmojiCandidates (), results: unsorted )) {
340
358
return null ;
341
359
}
342
- return results;
360
+ return bucketSort (unsorted,
361
+ (r) => r.rank, numBuckets: EmojiAutocompleteQuery ._numResultRanks);
343
362
}
344
363
345
364
static EmojiAutocompleteResult ? _testCandidate (EmojiAutocompleteQuery query, EmojiCandidate candidate) {
@@ -377,18 +396,32 @@ class EmojiAutocompleteQuery extends ComposeAutocompleteQuery {
377
396
378
397
@visibleForTesting
379
398
EmojiAutocompleteResult ? testCandidate (EmojiCandidate candidate) {
380
- return matches (candidate) ? EmojiAutocompleteResult (candidate) : null ;
399
+ final matchQuality = match (candidate);
400
+ if (matchQuality == null ) return null ;
401
+ return EmojiAutocompleteResult (candidate, _rankResult (matchQuality));
381
402
}
382
403
383
404
// Compare get_emoji_matcher in Zulip web:shared/src/typeahead.ts .
384
405
@visibleForTesting
385
- bool matches (EmojiCandidate candidate) {
386
- if (_adjusted == '' ) return true ;
406
+ EmojiMatchQuality ? match (EmojiCandidate candidate) {
407
+ if (_adjusted == '' ) return EmojiMatchQuality .match;
408
+
387
409
if (candidate.emojiDisplay case UnicodeEmojiDisplay (: var emojiUnicode)) {
388
- if (_adjusted == emojiUnicode) return true ;
410
+ if (_adjusted == emojiUnicode) {
411
+ return EmojiMatchQuality .match;
412
+ }
389
413
}
390
- return _nameMatches (candidate.emojiName)
391
- || candidate.aliases.any ((alias) => _nameMatches (alias));
414
+
415
+ EmojiMatchQuality ? result = _matchName (candidate.emojiName);
416
+ for (final alias in candidate.aliases) {
417
+ if (result == EmojiMatchQuality .best) return result;
418
+ result = EmojiMatchQuality .bestOf (result, _matchName (alias));
419
+ }
420
+ return result;
421
+ }
422
+
423
+ EmojiMatchQuality ? _matchName (String emojiName) {
424
+ return _nameMatches (emojiName) ? EmojiMatchQuality .match : null ;
392
425
}
393
426
394
427
// Compare query_matches_string_in_order in Zulip web:shared/src/typeahead.ts .
@@ -409,6 +442,18 @@ class EmojiAutocompleteQuery extends ComposeAutocompleteQuery {
409
442
|| emojiName.contains (_sepAdjusted);
410
443
}
411
444
445
+ /// A measure of the result's quality in the context of the query,
446
+ /// ranked from 0 (best) to one less than [_numResultRanks] .
447
+ static int _rankResult (EmojiMatchQuality matchQuality) {
448
+ // TODO(#1068): rank emoji results (popular, realm, other; exact match, prefix, other)
449
+ return switch (matchQuality) {
450
+ EmojiMatchQuality .match => 0 ,
451
+ };
452
+ }
453
+
454
+ /// The number of possible values returned by [_rankResult] .
455
+ static const _numResultRanks = 1 ;
456
+
412
457
@override
413
458
String toString () {
414
459
return '${objectRuntimeType (this , 'EmojiAutocompleteQuery' )}($raw )' ;
0 commit comments