fix terminal suggest regression (#287610)

fix terminal suggest regression (#287187)

fixes #287161
This commit is contained in:
Megan Rogge 2026-01-13 15:16:20 -06:00 committed by GitHub
parent 7b6e3721e5
commit 585eba7c0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 44 additions and 8 deletions

View File

@ -413,6 +413,13 @@ export async function resolveCwdFromCurrentCommandString(currentCommandString: s
}
const relativeFolder = lastSlashIndex === -1 ? '' : prefix.slice(0, lastSlashIndex);
// Don't pre-resolve paths with .. segments - let the completion service handle those
// to avoid double-navigation (e.g., typing ../ would resolve cwd to parent here,
// then completion service would navigate up again from the already-parent cwd)
if (relativeFolder.includes('..')) {
return undefined;
}
// Use vscode.Uri.joinPath for path resolution
const resolvedUri = vscode.Uri.joinPath(currentCwd, relativeFolder);

View File

@ -37,7 +37,8 @@ export const cdTestSuiteSpec: ISuiteSpec = {
// Relative directories (changes cwd due to /)
{ input: 'cd child/|', expectedCompletions, expectedResourceRequests: { type: 'folders', cwd: testPaths.cwdChild } },
{ input: 'cd ../|', expectedCompletions, expectedResourceRequests: { type: 'folders', cwd: testPaths.cwdParent } },
{ input: 'cd ../sibling|', expectedCompletions, expectedResourceRequests: { type: 'folders', cwd: testPaths.cwdParent } },
// Paths with .. are handled by the completion service to avoid double-navigation (no cwd resolution)
{ input: 'cd ../|', expectedCompletions, expectedResourceRequests: { type: 'folders' } },
{ input: 'cd ../sibling|', expectedCompletions, expectedResourceRequests: { type: 'folders' } },
]
};

View File

@ -84,8 +84,9 @@ export const lsTestSuiteSpec: ISuiteSpec = {
// Relative directories (changes cwd due to /)
{ input: 'ls child/|', expectedCompletions: allOptions, expectedResourceRequests: { type: 'both', cwd: testPaths.cwdChild } },
{ input: 'ls ../|', expectedCompletions: allOptions, expectedResourceRequests: { type: 'both', cwd: testPaths.cwdParent } },
{ input: 'ls ../sibling|', expectedCompletions: allOptions, expectedResourceRequests: { type: 'both', cwd: testPaths.cwdParent } },
// Paths with .. are handled by the completion service to avoid double-navigation (no cwd resolution)
{ input: 'ls ../|', expectedCompletions: allOptions, expectedResourceRequests: { type: 'both' } },
{ input: 'ls ../sibling|', expectedCompletions: allOptions, expectedResourceRequests: { type: 'both' } },
]
};

View File

@ -21,7 +21,7 @@ export interface ITestSpec {
input: string;
expectedResourceRequests?: {
type: 'files' | 'folders' | 'both';
cwd: Uri;
cwd?: Uri;
};
expectedCompletions?: (string | ICompletionResource)[];
}

View File

@ -85,7 +85,7 @@ suite('Terminal Suggest', () => {
let expectedString = testSpec.expectedCompletions ? `[${testSpec.expectedCompletions.map(e => `'${e}'`).join(', ')}]` : '[]';
if (testSpec.expectedResourceRequests) {
expectedString += ` + ${testSpec.expectedResourceRequests.type}`;
if (testSpec.expectedResourceRequests.cwd.fsPath !== testPaths.cwd.fsPath) {
if (testSpec.expectedResourceRequests.cwd && testSpec.expectedResourceRequests.cwd.fsPath !== testPaths.cwd.fsPath) {
expectedString += ` @ ${basename(testSpec.expectedResourceRequests.cwd.fsPath)}/`;
}
}

View File

@ -591,7 +591,7 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo
if (lastWordFolder.length > 0) {
label = addPathRelativePrefix(lastWordFolder + label, resourceOptions, lastWordFolderHasDotPrefix);
}
const parentDir = URI.joinPath(cwd, '..' + resourceOptions.pathSeparator);
const parentDir = URI.joinPath(lastWordFolderResource, '..' + resourceOptions.pathSeparator);
resourceCompletions.push({
label,
provider,

View File

@ -198,6 +198,32 @@ suite('TerminalCompletionService', () => {
], { replacementRange: [1, 3] });
});
test('../| should return parent folder completions', async () => {
// Scenario: cwd is /parent/folder1, sibling is /parent/folder2
// When typing ../, should see contents of /parent/ (folder1 and folder2)
validResources = [
URI.parse('file:///parent/folder1'),
URI.parse('file:///parent'),
];
childResources = [
{ resource: URI.parse('file:///parent/folder1/'), isDirectory: true },
{ resource: URI.parse('file:///parent/folder2/'), isDirectory: true },
];
const resourceOptions: TerminalCompletionResourceOptions = {
cwd: URI.parse('file:///parent/folder1'),
showDirectories: true,
pathSeparator
};
const result = await terminalCompletionService.resolveResources(resourceOptions, '../', 3, provider, capabilities);
assertCompletions(result, [
{ label: '../', detail: '/parent/' },
{ label: '../folder1/', detail: '/parent/folder1/' },
{ label: '../folder2/', detail: '/parent/folder2/' },
{ label: '../../', detail: '/' },
], { replacementRange: [0, 3] });
});
test('cd ./| should return folder completions', async () => {
const resourceOptions: TerminalCompletionResourceOptions = {
cwd: URI.parse('file:///test'),
@ -564,7 +590,8 @@ suite('TerminalCompletionService', () => {
assertCompletions(result, [
{ label: './test/', detail: '/test/test/' },
{ label: './test/inner/', detail: '/test/test/inner/' },
{ label: './test/../', detail: '/' }
// ../` from the viewed folder (/test/test/) goes to /test/, not /
{ label: './test/../', detail: '/test/' }
], { replacementRange: [0, 5] });
});
test('test/| should normalize current and parent folders', async () => {