mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
fix(cli): preserve Unicode queries in at completion (#13866)
This commit is contained in:
1 parent
bab0e99d89
commit
ec76c3d44e
5 files changed
+96
-2
No files matched your search
@@ -522,6 +522,8 @@ Use `/auth` inside a Qwen Code session to configure authentication. Use `/doctor
|
||||
|
||||
Note: Spaces in paths need to be escaped with backslash (e.g., `@My\ Documents/file.txt`)
|
||||
|
||||
In the interactive prompt, `@` completion supports Unicode filenames, including emoji. Emoji elsewhere in the prompt do not change the file path used for completion.
|
||||
|
||||
## 3. Exclamation Commands (`!`) - Shell Command Execution
|
||||
|
||||
Exclamation commands allow you to execute system commands directly within Qwen Code.
|
||||
|
||||
@@ -487,6 +487,61 @@ describe('useCommandCompletion', () => {
|
||||
});
|
||||
|
||||
describe('Completion mode detection', () => {
|
||||
it.each([
|
||||
['hello @README.md', 'README.md'],
|
||||
['中文 @文件.ts', '文件.ts'],
|
||||
['😀 @README.md', 'README.md'],
|
||||
['@😀file.ts', '😀file.ts'],
|
||||
['😀😀 @src/𠮷file.ts', 'src/𠮷file.ts'],
|
||||
['😀 @src/😀file.ts', 'src/😀file.ts'],
|
||||
])('uses the complete file query from %s', (text, pattern) => {
|
||||
renderHook(() =>
|
||||
useCommandCompletion(
|
||||
useTextBufferForTest(text, Array.from(text).length),
|
||||
testRootDir,
|
||||
[],
|
||||
mockCommandContext,
|
||||
false,
|
||||
mockConfig,
|
||||
),
|
||||
);
|
||||
|
||||
expect(useAtCompletion).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ enabled: true, pattern }),
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps code-point replacement bounds with the cursor inside a Unicode path', async () => {
|
||||
setupMocks({
|
||||
atSuggestions: [{ label: '😀done.ts', value: '😀done.ts' }],
|
||||
});
|
||||
const { result } = renderHook(() => {
|
||||
const textBuffer = useTextBufferForTest('😀 @src/😀file.ts tail', 7);
|
||||
const completion = useCommandCompletion(
|
||||
textBuffer,
|
||||
testRootDir,
|
||||
[],
|
||||
mockCommandContext,
|
||||
false,
|
||||
mockConfig,
|
||||
);
|
||||
return { ...completion, textBuffer };
|
||||
});
|
||||
|
||||
expect(useAtCompletion).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ enabled: true, pattern: 'src/😀file.ts' }),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(1);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.handleAutocomplete(0);
|
||||
});
|
||||
|
||||
expect(result.current.textBuffer.text).toBe('😀 @😀done.ts tail');
|
||||
});
|
||||
|
||||
it('should switch to AT mode when typing @ after a slash command (#2518)', async () => {
|
||||
setupMocks({
|
||||
atSuggestions: [{ label: 'src/file.txt', value: 'src/file.txt' }],
|
||||
|
||||
@@ -143,7 +143,7 @@ export function useCommandCompletion(
|
||||
}
|
||||
}
|
||||
const pathStart = i + 1;
|
||||
const partialPath = currentLine.substring(pathStart, end);
|
||||
const partialPath = codePoints.slice(pathStart, end).join('');
|
||||
return {
|
||||
completionMode: CompletionMode.AT,
|
||||
query: partialPath,
|
||||
|
||||
@@ -97,6 +97,43 @@ const GATING_COMMANDS: readonly SlashCommand[] = [
|
||||
cmd({ name: 'review' }),
|
||||
];
|
||||
|
||||
describe('detectCompletionTarget AT queries', () => {
|
||||
it.each([
|
||||
['hello @README.md', 'README.md'],
|
||||
['中文 @文件.ts', '文件.ts'],
|
||||
['😀 @README.md', 'README.md'],
|
||||
['@😀file.ts', '😀file.ts'],
|
||||
['😀😀 @src/𠮷file.ts', 'src/𠮷file.ts'],
|
||||
['😀 @src/😀file.ts', 'src/😀file.ts'],
|
||||
])('uses the complete file query from %s', (text, query) => {
|
||||
const cursor = Array.from(text).length;
|
||||
expect(detectCompletionTarget([text], 0, cursor, text, cursor)).toEqual({
|
||||
mode: CompletionMode.AT,
|
||||
query,
|
||||
start: Array.from(text).indexOf('@') + 1,
|
||||
end: cursor,
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps code-point replacement bounds with the cursor inside a Unicode path', () => {
|
||||
const text = '😀 @src/😀file.ts tail';
|
||||
const target = detectCompletionTarget([text], 0, 7, text, 7);
|
||||
expect(target).toEqual({
|
||||
mode: CompletionMode.AT,
|
||||
query: 'src/😀file.ts',
|
||||
start: 3,
|
||||
end: 15,
|
||||
});
|
||||
const applied = applyCompletion(
|
||||
text,
|
||||
target!,
|
||||
{ label: '😀done.ts', value: '😀done.ts' },
|
||||
false,
|
||||
);
|
||||
expect(applied.line).toBe('😀 @😀done.ts tail');
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectCompletionTarget mid-input gating (useCommandCompletion port, R1-86)', () => {
|
||||
const detect = (text: string, cursorOffset: number) => {
|
||||
const lines = text.split('\n');
|
||||
|
||||
@@ -216,7 +216,7 @@ export function detectCompletionTarget(
|
||||
const pathStart = i + 1;
|
||||
return {
|
||||
mode: CompletionMode.AT,
|
||||
query: currentLine.substring(pathStart, end),
|
||||
query: codePoints.slice(pathStart, end).join(''),
|
||||
start: pathStart,
|
||||
end,
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user