mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-09 17:19:02 +00:00
fix(cli): prefer command name over alias in slash completion ranking (#5577)
When slash completion fuzzy-matches both a command's primary name and another command's alias (altName) at the same match strength, the shorter string wins on fzf score — e.g. typing /re ranks 'clear' (via its 'reset' alias) above 'resume', because 'reset' is shorter. Add a name-vs-alias dimension to the sort chain so that a command's own name always ranks above an alias match, regardless of fzf score. This resolves 5 known prefix collision cases (/re, /co, /su, /ex, /ta) where an alias from one command would appear before the intended name match of another command. Signed-off-by: Alex <alex.tech.lab@outlook.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
This commit is contained in:
parent
75fc0a5c18
commit
d59a0227fe
2 changed files with 56 additions and 0 deletions
|
|
@ -121,4 +121,52 @@ describe('useSlashCompletion integration', () => {
|
|||
expect(result.current.suggestions[0]?.value).toBe('model');
|
||||
expect(result.current.suggestions[1]?.value).toBe('memory');
|
||||
});
|
||||
|
||||
it('prefers command name match over alias match for same-strength prefixes', async () => {
|
||||
// Real-world conflict: /re matches `resume` (name) and `reset` (alias of clear).
|
||||
// Without the name-vs-alias sort dimension, fzf gives the shorter `reset` a higher
|
||||
// score, so `clear` would appear first. The fix ensures `resume` (name match)
|
||||
// always ranks above any alias match at the same strength level.
|
||||
const slashCommands = [
|
||||
createTestCommand({
|
||||
name: 'clear',
|
||||
altNames: ['reset', 'new'],
|
||||
description: 'Clear conversation history',
|
||||
}),
|
||||
createTestCommand({
|
||||
name: 'resume',
|
||||
altNames: ['continue'],
|
||||
description: 'Resume a previous session',
|
||||
}),
|
||||
createTestCommand({
|
||||
name: 'recap',
|
||||
description: 'Show session recap',
|
||||
}),
|
||||
];
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useTestHarnessForSlashCompletion(
|
||||
true,
|
||||
'/re',
|
||||
slashCommands,
|
||||
mockCommandContext,
|
||||
),
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
// resume and recap are name matches; reset is an alias match.
|
||||
// Name matches must come before alias matches regardless of fzf score.
|
||||
const names = result.current.suggestions.map((s) => s.value);
|
||||
const resumeIndex = names.indexOf('resume');
|
||||
const recapIndex = names.indexOf('recap');
|
||||
const clearIndex = names.indexOf('clear');
|
||||
expect(resumeIndex).toBeGreaterThanOrEqual(0);
|
||||
expect(recapIndex).toBeGreaterThanOrEqual(0);
|
||||
expect(clearIndex).toBeGreaterThanOrEqual(0);
|
||||
expect(resumeIndex).toBeLessThan(clearIndex);
|
||||
expect(recapIndex).toBeLessThan(clearIndex);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -237,10 +237,18 @@ function compareRankedCommandMatches(
|
|||
left: RankedCommandMatch,
|
||||
right: RankedCommandMatch,
|
||||
): number {
|
||||
// Name match beats alias match — e.g. /re should prefer `resume` (name)
|
||||
// over `clear` via its `reset` alias, since users type the primary name
|
||||
// more often than obscure alternates.
|
||||
const leftIsName = left.matchedAlias === undefined ? 1 : 0;
|
||||
const rightIsName = right.matchedAlias === undefined ? 1 : 0;
|
||||
const nameVsAlias = rightIsName - leftIsName;
|
||||
|
||||
return (
|
||||
right.matchStrength - left.matchStrength ||
|
||||
right.completionPriority - left.completionPriority ||
|
||||
right.recentScore - left.recentScore ||
|
||||
nameVsAlias ||
|
||||
right.score - left.score ||
|
||||
left.start - right.start ||
|
||||
left.itemLength - right.itemLength ||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue