Skip to content

Commit ece199a

Browse files
committed
fix: keep command palette search results relevant for short queries
Fixes #345
1 parent 8dcd606 commit ece199a

2 files changed

Lines changed: 41 additions & 9 deletions

File tree

datalab/gui/commandpalette.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,20 @@ def fuzzy_score(query: str, text: str) -> int | None:
4343
consecutive matches and matches at word boundaries are rewarded, while
4444
gaps between matched characters are penalised.
4545
46+
A plain subsequence match is too permissive on its own: a short query
47+
like "rota" would match unrelated commands such as "impoRt annOTAtions".
48+
To cull that noise, a match is only accepted when its matched characters
49+
form a single contiguous run (a plain substring, e.g. "rota" in "Rotate")
50+
or when every run of matched characters starts at a word boundary
51+
(acronym / word-initials style, e.g. "fan" in "Fourier ANalysis").
52+
4653
Args:
4754
query: Lowercased, trimmed search query.
4855
text: Lowercased haystack to match against.
4956
5057
Returns:
51-
A score (higher is better) when ``query`` is a subsequence of
52-
``text``, otherwise ``None``.
58+
A score (higher is better) when ``query`` matches ``text`` under the
59+
rule above, otherwise ``None``.
5360
"""
5461
if not query:
5562
return 0
@@ -58,19 +65,36 @@ def fuzzy_score(query: str, text: str) -> int | None:
5865
score = 0
5966
text_index = 0
6067
prev = -2
68+
# A "run" is a maximal block of contiguous matched characters. We track
69+
# how many runs the match spans and how many start at a word boundary,
70+
# to reject scattered mid-word noise afterwards.
71+
runs = 0
72+
boundary_runs = 0
6173
for char in query:
6274
found = text.find(char, text_index)
6375
if found == -1:
6476
return None
77+
contiguous = found == prev + 1
78+
at_boundary = found == 0 or text[found - 1] in _BOUNDARY_CHARS
6579
score += 1
66-
if found == prev + 1:
80+
if contiguous:
6781
score += 5
68-
if found == 0 or text[found - 1] in _BOUNDARY_CHARS:
82+
if at_boundary:
6983
score += 3
7084
if prev >= 0:
7185
score -= min(found - prev - 1, 3)
86+
if not contiguous:
87+
runs += 1
88+
if at_boundary:
89+
boundary_runs += 1
7290
prev = found
7391
text_index = found + 1
92+
# Cull scattered noise: keep the match only when the query occurs as a
93+
# plain substring (a single contiguous run — the greedy scan above can miss
94+
# it, e.g. "fft" in "… Fourier … fft") or when every run of matched
95+
# characters starts at a word boundary (acronym / word-initials style).
96+
if boundary_runs != runs and query not in text:
97+
return None
7498
return score
7599

76100

datalab/tests/backbone/commandpalette_unit_test.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,25 @@ def test_fuzzy_score():
2929
"""Test the fuzzy subsequence matcher."""
3030
# Empty query matches anything with a neutral score
3131
assert fuzzy_score("", "anything") == 0
32-
# Contiguous and non-contiguous subsequences match
32+
# A plain substring (single contiguous run) matches, even mid-word
3333
assert fuzzy_score("fft", "processing › fourier analysis › fft") is not None
34+
assert fuzzy_score("rota", "rotate") is not None
35+
assert fuzzy_score("bration", "calibration") is not None
36+
# Acronym-style matches where every run starts at a word boundary match
3437
assert fuzzy_score("fan", "fourier analysis") is not None
3538
# A missing character or an over-long query does not match
3639
assert fuzzy_score("xyz", "fourier analysis") is None
3740
assert fuzzy_score("abcdef", "abc") is None
38-
# A contiguous match scores higher than a scattered one
41+
# Scattered mid-word noise is rejected: "rota" must not match paths that
42+
# merely contain r, o, t, a as scattered mid-word runs
43+
assert fuzzy_score("rota", "edit › annotations › import annotations") is None
44+
assert fuzzy_score("rota", "analysis › horizontal projection") is None
45+
assert fuzzy_score("abc", "a1b2c3 def") is None
46+
# A contiguous match scores higher than a boundary-run one
3947
contiguous = fuzzy_score("abc", "abc def")
40-
scattered = fuzzy_score("abc", "a1b2c3 def")
41-
assert contiguous is not None and scattered is not None
42-
assert contiguous > scattered
48+
boundary = fuzzy_score("abc", "a b c")
49+
assert contiguous is not None and boundary is not None
50+
assert contiguous > boundary
4351

4452

4553
def test_collect_commands():

0 commit comments

Comments
 (0)