From 82dfd41db758167b8b83af91093d1324ae00fcd7 Mon Sep 17 00:00:00 2001 From: Pratik Chanda Date: Tue, 27 Jan 2026 01:50:35 +0530 Subject: [PATCH] search_suggestion: Refactor `get_operator_suggestions`. We change how we calculate operators suggestion that match with type query. First we just remove all canonicalized operators based on incompatible patterns. This means their equivalent legacy operators are also out of the suggestion and filter them out using the removed canonicalized operators. Then we just run our phrase match on the combined array to further filter them out. This will help us in removing some legacy types from incompatible_patterns. --- tools/linter_lib/custom_check.py | 1 - web/src/search_suggestion.ts | 48 +++++++++++++++++--------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/tools/linter_lib/custom_check.py b/tools/linter_lib/custom_check.py index d274fcd735..14089fc620 100644 --- a/tools/linter_lib/custom_check.py +++ b/tools/linter_lib/custom_check.py @@ -128,7 +128,6 @@ js_rules = RuleList( "web/src/message_helper.ts", "web/src/server_message.ts", "web/src/state_data.ts", - "web/src/search_suggestion.ts", "web/tests/", }, "exclude_pattern": "emails", diff --git a/web/src/search_suggestion.ts b/web/src/search_suggestion.ts index 818c809b7e..9a3bd35f6e 100644 --- a/web/src/search_suggestion.ts +++ b/web/src/search_suggestion.ts @@ -62,7 +62,7 @@ const descriptions: Record = { }; type SearchFilter = - | NarrowTerm["operator"] + | NarrowCanonicalOperator | "is:resolved" | "-is:resolved" | "is:dm" @@ -79,8 +79,6 @@ type SearchFilter = const incompatible_patterns: Record = { channel: channel_incompatible_patterns, - stream: channel_incompatible_patterns, - streams: channel_incompatible_patterns, channels: channel_incompatible_patterns, topic: [ {operator: "dm"}, @@ -94,12 +92,6 @@ const incompatible_patterns: Record = { {operator: "channel"}, {operator: "is", operand: "resolved"}, ], - "pm-with": [ - {operator: "dm"}, - {operator: "pm-with"}, - {operator: "channel"}, - {operator: "is", operand: "resolved"}, - ], "dm-including": [{operator: "channel"}, {operator: "stream"}], "is:resolved": [ {operator: "is", operand: "resolved"}, @@ -122,7 +114,6 @@ const incompatible_patterns: Record = { {operator: "topic"}, ], sender: [{operator: "sender"}, {operator: "from"}], - from: [{operator: "sender"}, {operator: "from"}], "is:starred": [{operator: "is", operand: "starred"}], "is:mentioned": [{operator: "is", operand: "mentioned"}], "is:followed": [ @@ -150,8 +141,6 @@ const incompatible_patterns: Record = { is: [], search: [], with: [], - "group-pm-with": [], - subject: [], }; // TODO: We have stripped suggestion of all other attributes, we should now @@ -904,12 +893,16 @@ function get_operator_suggestions( last_operand = last_operand.slice(1); } - let choices: NarrowTerm["operator"][]; + let canonicalized_operator_choices: NarrowCanonicalOperator[]; + let legacy_operator_choices: NarrowTerm["operator"][]; + + const incompatible_operators = new Set(); if (last.operator === "") { - choices = ["channels", "channel", "streams", "stream"]; + canonicalized_operator_choices = ["channels", "channel"]; + legacy_operator_choices = ["streams", "stream"]; } else { - choices = [ + canonicalized_operator_choices = [ "channels", "channel", "topic", @@ -917,19 +910,28 @@ function get_operator_suggestions( "dm-including", "sender", "near", - "from", - "pm-with", - "streams", - "stream", ]; + legacy_operator_choices = ["from", "pm-with", "streams", "stream"]; } // We remove suggestion choice if its incompatible_pattern matches // that of current search terms. - choices = choices.filter( - (choice) => - common.phrase_match(last_operand, choice) && - !match_criteria(terms, incompatible_patterns[choice]), + canonicalized_operator_choices = canonicalized_operator_choices.filter((choice) => { + if (match_criteria(terms, incompatible_patterns[choice])) { + incompatible_operators.add(choice); + return false; + } + return true; + }); + + // Add equivalent legacy operators for canonicalized operators + legacy_operator_choices = legacy_operator_choices.filter((choice) => { + const canonical = filter_util.canonicalize_operator(choice); + return !incompatible_operators.has(canonical); + }); + + const choices = [...canonicalized_operator_choices, ...legacy_operator_choices].filter( + (choice) => common.phrase_match(last_operand, choice), ); return choices.map((choice) => {