diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 34232ebda32..7c375d4ba70 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -241,6 +241,7 @@ export async function getCompletionItemsFromSpecs( const precedingText = terminalContext.commandLine.slice(0, terminalContext.cursorPosition + 1); + let specificItemsProvided = false; for (const spec of specs) { const specLabels = getLabel(spec); @@ -273,6 +274,7 @@ export async function getCompletionItemsFromSpecs( items.push(...argsCompletionResult.items); filesRequested ||= argsCompletionResult.filesRequested; foldersRequested ||= argsCompletionResult.foldersRequested; + specificItemsProvided ||= argsCompletionResult.items.length > 0; } if (!argsCompletionResult?.items.length) { // Arg completions are more specific, only get options if those are not provided. @@ -281,15 +283,13 @@ export async function getCompletionItemsFromSpecs( items.push(...optionsCompletionResult.items); filesRequested ||= optionsCompletionResult.filesRequested; foldersRequested ||= optionsCompletionResult.foldersRequested; + specificItemsProvided ||= optionsCompletionResult.items.length > 0; } } } } - const shouldShowResourceCompletions = - (!terminalContext.commandLine.trim() || !items.length) && - !filesRequested && - !foldersRequested; + if (tokenType === TokenType.Command) { // Include builitin/available commands in the results @@ -299,11 +299,17 @@ export async function getCompletionItemsFromSpecs( items.push(createCompletionItem(terminalContext.cursorPosition, prefix, command, command.detail)); } } - } - - if (shouldShowResourceCompletions) { filesRequested = true; foldersRequested = true; + } else { + const shouldShowResourceCompletions = + !specificItemsProvided && + !filesRequested && + !foldersRequested; + if (shouldShowResourceCompletions) { + filesRequested = true; + foldersRequested = true; + } } let cwd: vscode.Uri | undefined; diff --git a/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts b/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts index 030e9d0db99..68a2abbb256 100644 --- a/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts +++ b/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts @@ -46,7 +46,8 @@ function createCodeTestSpecs(executable: string): ITestSpec2[] { const typingTests: ITestSpec2[] = []; for (let i = 1; i < executable.length; i++) { - typingTests.push({ input: `${executable.slice(0, i)}|`, expectedCompletions: [executable] }); + const input = `${executable.slice(0, i)}|`; + typingTests.push({ input, expectedCompletions: [executable], expectedResourceRequests: input.endsWith(' ') ? undefined : { type: 'both', cwd: testCwd } }); } return [ @@ -85,6 +86,9 @@ const testSpecs2: ISuiteSpec[] = [ completionSpecs: [], availableCommands: [], testSpecs: [ + { input: '|', expectedCompletions: [], expectedResourceRequests: { type: 'both', cwd: testCwd } }, + { input: '|.', expectedCompletions: [], expectedResourceRequests: { type: 'both', cwd: testCwd } }, + { input: '|./', expectedCompletions: [], expectedResourceRequests: { type: 'both', cwd: testCwd } }, { input: 'fakecommand |', expectedCompletions: [], expectedResourceRequests: { type: 'both', cwd: testCwd } }, ] }, @@ -93,11 +97,13 @@ const testSpecs2: ISuiteSpec[] = [ completionSpecs: cdSpec, availableCommands: 'cd', testSpecs: [ + // Typing a path + { input: '.|', expectedCompletions: ['cd'], expectedResourceRequests: { type: 'both', cwd: testCwd } }, + { input: './|', expectedCompletions: ['cd'], expectedResourceRequests: { type: 'both', cwd: testCwd } }, + { input: './.|', expectedCompletions: ['cd'], expectedResourceRequests: { type: 'both', cwd: testCwd } }, // Typing the command - // TODO: Shouldn't this also request file resources that contain "c" as you can run a file as a command? - { input: 'c|', expectedCompletions: ['cd'] }, - // TODO: Shouldn't this also request file resources that contain "cd" as you can run a file as a command? - { input: 'cd|', expectedCompletions: ['cd'] }, + { input: 'c|', expectedCompletions: ['cd'], expectedResourceRequests: { type: 'both', cwd: testCwd } }, + { input: 'cd|', expectedCompletions: ['cd'], expectedResourceRequests: { type: 'both', cwd: testCwd } }, // Basic arguments { input: 'cd |', expectedCompletions: ['~', '-'], expectedResourceRequests: { type: 'folders', cwd: testCwd } }, diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index f9aabbce56c..3cb325d21a7 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -435,26 +435,30 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement }); this.modelListeners.add(this.modelService.onModelRemoved(modelEvent => { - let markerChanged: IDisposable | undefined = - Event.debounce(this.markerService.onMarkerChanged, (last: readonly URI[] | undefined, e: readonly URI[]) => { - return (last ?? []).concat(e); - }, 500, false, true)(async (markerEvent) => { - markerChanged?.dispose(); + let markerChanged: IDisposable | undefined = Event.debounce( + this.markerService.onMarkerChanged, + (last: readonly URI[] | undefined, e: readonly URI[]) => (last ?? []).concat(e), + 500, + false, + true + )(async (markerEvent: readonly URI[]) => { + if (!markerEvent || !markerEvent.includes(modelEvent.uri) || (this.markerService.read({ resource: modelEvent.uri }).length !== 0)) { + return; + } + const oldLines = Array.from(this.lines); + for (const line of oldLines) { + await this.processLineInternal(line); + } + }); + + this._register(markerChanged); // Ensures markerChanged is tracked and disposed of properly + + setTimeout(() => { + if (markerChanged) { + const _markerChanged = markerChanged; markerChanged = undefined; - if (!markerEvent || !markerEvent.includes(modelEvent.uri) || (this.markerService.read({ resource: modelEvent.uri }).length !== 0)) { - return; - } - const oldLines = Array.from(this.lines); - for (const line of oldLines) { - await this.processLineInternal(line); - } - }); - setTimeout(async () => { - // Calling dispose below can trigger the debounce event (via flushOnListenerRemove), so we - // have to unset markerChanged first to make sure the handler above doesn't dispose it again. - const _markerChanged = markerChanged; - markerChanged = undefined; - _markerChanged?.dispose(); + _markerChanged.dispose(); + } }, 600); })); }