Skip to content

Commit 79e7eed

Browse files
committed
fix(vscode): match slash commands on the command name only
A description is prose, so matching it pulled in commands unrelated to what was typed: "/sk" reached "/yolo" because its description contains those letters, and the selection sat on it. Matching is now limited to the command name, which is what the user is typing and the only part they can predict.
1 parent 164af64 commit 79e7eed

2 files changed

Lines changed: 16 additions & 20 deletions

File tree

apps/vscode/test/slash-menu.test.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,15 @@ describe("scoreCommand", () => {
4242
expect(Number.isFinite(scoreCommand(RESEARCH_SKILL, "reswrit"))).toBe(true);
4343
});
4444

45-
it("still falls back to the description when nothing matches the name", () => {
46-
expect(Number.isFinite(scoreCommand(CUSTOM_THEME, "color"))).toBe(true);
47-
});
48-
49-
it("ranks every name match above a description match", () => {
50-
expect(rank([CUSTOM_THEME, command("theme-picker", "Nothing to see.")], "theme")).toEqual([
51-
"theme-picker",
52-
"custom-theme",
45+
it("never matches on the description", () => {
46+
// "/sk" used to reach "/yolo" because its description contains those
47+
// letters, so the highlight sat on an unrelated command.
48+
const yolo = command("yolo", "Skip every approval prompt for this session.");
49+
expect(scoreCommand(yolo, "sk")).toBe(NO_MATCH);
50+
expect(rank([yolo, command("sub-skill"), command("invoke-skill")], "sk")).toEqual([
51+
"sub-skill",
52+
"invoke-skill",
5353
]);
54+
expect(scoreCommand(CUSTOM_THEME, "color")).toBe(NO_MATCH);
5455
});
5556
});

apps/vscode/webview-ui/src/components/inputarea/hooks/slash-command-match.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ function matchesAnyWordPrefix(text: string, query: string): boolean {
2525
}
2626

2727
/**
28-
* Lower is a better match, `NO_MATCH` means the command is filtered out. Every
29-
* way of matching the name outranks the description: the name is what the user
30-
* is typing, and matching a long description loosely lets nearly every command
31-
* through — which is what made the menu look unfiltered.
28+
* Lower is a better match, `NO_MATCH` means the command is filtered out.
29+
*
30+
* Only the command name is matched. A description is prose — matching it pulls
31+
* in commands that have nothing to do with what was typed (`/sk` reaching
32+
* `/yolo` because its description happens to contain those letters), and the
33+
* user cannot tell why they are listed.
3234
*/
3335
export function scoreCommand(command: SlashCommandInfo, query: string): number {
3436
const name = command.name.toLowerCase();
@@ -45,14 +47,7 @@ export function scoreCommand(command: SlashCommandInfo, query: string): number {
4547
return 2;
4648
}
4749
// Forgiving tier: skipped letters and dropped separators still match.
48-
if (isSubsequence(letters(name), letters(q))) {
49-
return 3;
50-
}
51-
const description = command.description;
52-
if (matchesAnyWordPrefix(description, q)) {
53-
return 4;
54-
}
55-
return description.toLowerCase().includes(q) ? 5 : NO_MATCH;
50+
return isSubsequence(letters(name), letters(q)) ? 3 : NO_MATCH;
5651
}
5752

5853
/** Commands that match `query`, best match first. An empty query keeps the original order. */

0 commit comments

Comments
 (0)