diff --git a/extensions/terminal-suggest/src/completions/gh.ts b/extensions/terminal-suggest/src/completions/gh.ts index 0c06cc35d7f..ea163beeb7f 100644 --- a/extensions/terminal-suggest/src/completions/gh.ts +++ b/extensions/terminal-suggest/src/completions/gh.ts @@ -50,7 +50,7 @@ const postProcessRemoteBranches: Fig.Generator["postProcess"] = (out) => { return { name, description: "Branch", - icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.Branch}`, + icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.ScmBranch}`, priority: 75, }; }); diff --git a/extensions/terminal-suggest/src/completions/git.ts b/extensions/terminal-suggest/src/completions/git.ts index 13c7739407a..7c0f74fb843 100644 --- a/extensions/terminal-suggest/src/completions/git.ts +++ b/extensions/terminal-suggest/src/completions/git.ts @@ -89,7 +89,7 @@ const postProcessBranches = name: branch.replace("*", "").trim(), description: "Current branch", priority: 100, - icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.Branch}` + icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.ScmBranch}` }; } else if (parts[0] === "+") { // Branch checked out in another worktree. @@ -112,7 +112,7 @@ const postProcessBranches = return { name, description, - icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.Branch}`, + icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.ScmBranch}`, priority: 75, }; }) @@ -148,7 +148,7 @@ export const gitGenerators = { return lines.map((line) => { return { name: line.substring(0, hashLength), - icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.Commit}`, + icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.ScmCommit}`, description: line.substring(descriptionStart), }; }); @@ -194,7 +194,7 @@ export const gitGenerators = { return output.split("\n").map((line) => { return { name: line.substring(0, 7), - icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.Commit}`, + icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.ScmCommit}`, description: line.substring(7), }; }); @@ -217,7 +217,7 @@ export const gitGenerators = { // account for conventional commit messages name: file.split(":").slice(2).join(":"), insertValue: file.split(":")[0], - icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.Stash}`, + icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.ScmStash}`, }; }); }, @@ -329,7 +329,7 @@ export const gitGenerators = { return Object.keys(remoteURLs).map((remote) => { return { name: remote, - icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.Remote}`, + icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.ScmRemote}`, description: "Remote", }; }); @@ -347,7 +347,7 @@ export const gitGenerators = { postProcess: function (output) { return output.split("\n").map((tag) => ({ name: tag, - icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.Tag}` + icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.ScmTag}` })); }, } satisfies Fig.Generator, @@ -8117,7 +8117,7 @@ const completionSpec: Fig.Spec = { { name: "-", description: "Switch to the last used branch", - icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.Branch}` + icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.ScmBranch}` }, { name: "--", @@ -9283,7 +9283,7 @@ const completionSpec: Fig.Spec = { { name: "-", description: "Switch to the last used branch", - icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.Branch}` + icon: `vscode://icon?type=${vscode.TerminalCompletionItemKind.ScmBranch}` }, ], }, diff --git a/extensions/terminal-suggest/src/fig/figInterface.ts b/extensions/terminal-suggest/src/fig/figInterface.ts index 35a8e12747b..387eaa340ef 100644 --- a/extensions/terminal-suggest/src/fig/figInterface.ts +++ b/extensions/terminal-suggest/src/fig/figInterface.ts @@ -20,8 +20,8 @@ import { asArray, availableSpecs } from '../terminalSuggestMain'; import { IFigExecuteExternals } from './execute'; export interface IFigSpecSuggestionsResult { - filesRequested: boolean; - foldersRequested: boolean; + showFiles: boolean; + showFolders: boolean; fileExtensions?: string[]; hasCurrentArg: boolean; items: vscode.TerminalCompletionItem[]; @@ -29,7 +29,7 @@ export interface IFigSpecSuggestionsResult { export async function getFigSuggestions( specs: Fig.Spec[], - terminalContext: { commandLine: string; cursorPosition: number }, + terminalContext: { commandLine: string; cursorIndex: number }, availableCommands: ICompletionResource[], currentCommandAndArgString: string, tokenType: TokenType, @@ -40,8 +40,8 @@ export async function getFigSuggestions( token?: vscode.CancellationToken, ): Promise { const result: IFigSpecSuggestionsResult = { - filesRequested: false, - foldersRequested: false, + showFiles: false, + showFolders: false, hasCurrentArg: false, items: [], }; @@ -75,7 +75,7 @@ export async function getFigSuggestions( if (availableCommand.kind !== vscode.TerminalCompletionItemKind.Alias) { const description = getFixSuggestionDescription(spec); result.items.push(createCompletionItem( - terminalContext.cursorPosition, + terminalContext.cursorIndex, currentCommandAndArgString, { label: { label: specLabel, description }, @@ -106,8 +106,8 @@ export async function getFigSuggestions( const completionItemResult = await getFigSpecSuggestions(actualSpec, terminalContext, currentCommandAndArgString, shellIntegrationCwd, env, name, executeExternals, token); result.hasCurrentArg ||= !!completionItemResult?.hasCurrentArg; if (completionItemResult) { - result.filesRequested ||= completionItemResult.filesRequested; - result.foldersRequested ||= completionItemResult.foldersRequested; + result.showFiles ||= completionItemResult.showFiles; + result.showFolders ||= completionItemResult.showFolders; result.fileExtensions ||= completionItemResult.fileExtensions; if (completionItemResult.items) { result.items = result.items.concat(completionItemResult.items); @@ -120,7 +120,7 @@ export async function getFigSuggestions( async function getFigSpecSuggestions( spec: Fig.Spec, - terminalContext: { commandLine: string; cursorPosition: number }, + terminalContext: { commandLine: string; cursorIndex: number }, prefix: string, shellIntegrationCwd: vscode.Uri | undefined, env: Record, @@ -128,11 +128,11 @@ async function getFigSpecSuggestions( executeExternals: IFigExecuteExternals, token?: vscode.CancellationToken, ): Promise { - let filesRequested = false; - let foldersRequested = false; + let showFiles = false; + let showFolders = false; let fileExtensions: string[] | undefined; - const command = getCommand(terminalContext.commandLine, {}, terminalContext.cursorPosition); + const command = getCommand(terminalContext.commandLine, {}, terminalContext.cursorIndex); if (!command || !shellIntegrationCwd) { return; } @@ -153,14 +153,14 @@ async function getFigSpecSuggestions( } if (completionItemResult) { - filesRequested = completionItemResult.filesRequested; - foldersRequested = completionItemResult.foldersRequested; + showFiles = completionItemResult.showFiles; + showFolders = completionItemResult.showFolders; fileExtensions = completionItemResult.fileExtensions; } return { - filesRequested, - foldersRequested, + showFiles: showFiles, + showFolders: showFolders, fileExtensions, hasCurrentArg: !!parsedArguments.currentArg, items, @@ -173,14 +173,14 @@ export async function collectCompletionItemResult( command: Command, parsedArguments: ArgumentParserResult, prefix: string, - terminalContext: { commandLine: string; cursorPosition: number }, + terminalContext: { commandLine: string; cursorIndex: number }, shellIntegrationCwd: vscode.Uri | undefined, env: Record, items: vscode.TerminalCompletionItem[], executeExternals: IFigExecuteExternals -): Promise<{ filesRequested: boolean; foldersRequested: boolean; fileExtensions: string[] | undefined } | undefined> { - let filesRequested = false; - let foldersRequested = false; +): Promise<{ showFiles: boolean; showFolders: boolean; fileExtensions: string[] | undefined } | undefined> { + let showFiles = false; + let showFolders = false; let fileExtensions: string[] | undefined; const addSuggestions = async (specArgs: SpecArg[] | Record | undefined, kind: vscode.TerminalCompletionItemKind, parsedArguments?: ArgumentParserResult) => { @@ -188,7 +188,7 @@ export async function collectCompletionItemResult( const generators = parsedArguments.currentArg.generators; const initialFigState: FigState = { buffer: terminalContext.commandLine, - cursorLocation: terminalContext.cursorPosition, + cursorLocation: terminalContext.cursorIndex, cwd: shellIntegrationCwd?.fsPath ?? null, processUserIsIn: null, sshContextString: null, @@ -222,12 +222,12 @@ export async function collectCompletionItemResult( for (const generatorResult of generatorResults) { for (const item of (await generatorResult?.request) ?? []) { if (item.type === 'file') { - filesRequested = true; - foldersRequested = true; + showFiles = true; + showFolders = true; fileExtensions = item._internal?.fileExtensions as string[] | undefined; } if (item.type === 'folder') { - foldersRequested = true; + showFolders = true; } if (!item.name) { @@ -239,7 +239,7 @@ export async function collectCompletionItemResult( } for (const label of suggestionLabels) { items.push(createCompletionItem( - terminalContext.cursorPosition, + terminalContext.cursorIndex, prefix, { label }, item.displayName, @@ -256,16 +256,16 @@ export async function collectCompletionItemResult( const templates = Array.isArray(generator.template) ? generator.template : [generator.template]; for (const template of templates) { if (template === 'filepaths') { - filesRequested = true; + showFiles = true; } else if (template === 'folders') { - foldersRequested = true; + showFolders = true; } } } } } if (!specArgs) { - return { filesRequested, foldersRequested }; + return { showFiles, showFolders }; } const flagsToExclude = kind === vscode.TerminalCompletionItemKind.Flag ? parsedArguments?.passedOptions.map(option => option.name).flat() : undefined; @@ -304,7 +304,7 @@ export async function collectCompletionItemResult( items.push( createCompletionItem( - terminalContext.cursorPosition, + terminalContext.cursorIndex, prefix, { label: detail ? { label, detail } : label @@ -343,7 +343,7 @@ export async function collectCompletionItemResult( await addSuggestions(parsedArguments.completionObj.options, vscode.TerminalCompletionItemKind.Flag, parsedArguments); } - return { filesRequested, foldersRequested, fileExtensions }; + return { showFiles, showFolders, fileExtensions }; } function convertEnvRecordToArray(env: Record): EnvironmentVariable[] { @@ -372,11 +372,11 @@ export function getFigSuggestionLabel(spec: Fig.Spec | Fig.Arg | Fig.Suggestion function convertIconToKind(icon: string | undefined): vscode.TerminalCompletionItemKind | undefined { switch (icon) { - case 'vscode://icon?type=10': return vscode.TerminalCompletionItemKind.Commit; - case 'vscode://icon?type=11': return vscode.TerminalCompletionItemKind.Branch; - case 'vscode://icon?type=12': return vscode.TerminalCompletionItemKind.Tag; - case 'vscode://icon?type=13': return vscode.TerminalCompletionItemKind.Stash; - case 'vscode://icon?type=14': return vscode.TerminalCompletionItemKind.Remote; + case 'vscode://icon?type=10': return vscode.TerminalCompletionItemKind.ScmCommit; + case 'vscode://icon?type=11': return vscode.TerminalCompletionItemKind.ScmBranch; + case 'vscode://icon?type=12': return vscode.TerminalCompletionItemKind.ScmTag; + case 'vscode://icon?type=13': return vscode.TerminalCompletionItemKind.ScmStash; + case 'vscode://icon?type=14': return vscode.TerminalCompletionItemKind.ScmRemote; case 'vscode://icon?type=15': return vscode.TerminalCompletionItemKind.PullRequest; case 'vscode://icon?type=16': return vscode.TerminalCompletionItemKind.PullRequestDone; default: return undefined; diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 9db6122f729..45805d9def4 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -274,7 +274,7 @@ export async function activate(context: vscode.ExtensionContext) { } // Order is important here, add shell globals first so they are prioritized over path commands const commands = [...shellGlobals, ...commandsInPath.completionResources]; - const currentCommandString = getCurrentCommandAndArgs(terminalContext.commandLine, terminalContext.cursorPosition, terminalShellType); + const currentCommandString = getCurrentCommandAndArgs(terminalContext.commandLine, terminalContext.cursorIndex, terminalShellType); const pathSeparator = isWindows ? '\\' : '/'; const tokenType = getTokenType(terminalContext, terminalShellType); const result = await Promise.race([ @@ -305,11 +305,11 @@ export async function activate(context: vscode.ExtensionContext) { } const cwd = result.cwd ?? terminal.shellIntegration?.cwd; - if (cwd && (result.filesRequested || result.foldersRequested)) { + if (cwd && (result.showFiles || result.showFolders)) { const globPattern = createFileGlobPattern(result.fileExtensions); return new vscode.TerminalCompletionList(result.items, { - filesRequested: result.filesRequested, - foldersRequested: result.foldersRequested, + showFiles: result.showFiles, + showDirectories: result.showFolders, globPattern, cwd, }); @@ -370,7 +370,7 @@ export async function resolveCwdFromCurrentCommandString(currentCommandString: s // Retrurns the string that represents the current command and its arguments up to the cursor position. // Uses shell specific separators to determine the current command and its arguments. -export function getCurrentCommandAndArgs(commandLine: string, cursorPosition: number, shellType: TerminalShellType | undefined): string { +export function getCurrentCommandAndArgs(commandLine: string, cursorIndex: number, shellType: TerminalShellType | undefined): string { // Return an empty string if the command line is empty after trimming if (commandLine.trim() === '') { @@ -378,12 +378,12 @@ export function getCurrentCommandAndArgs(commandLine: string, cursorPosition: nu } // Check if cursor is not at the end and there's non-whitespace after the cursor - if (cursorPosition < commandLine.length && /\S/.test(commandLine[cursorPosition])) { + if (cursorIndex < commandLine.length && /\S/.test(commandLine[cursorIndex])) { return ''; } // Extract the part of the line up to the cursor position - const beforeCursor = commandLine.slice(0, cursorPosition); + const beforeCursor = commandLine.slice(0, cursorIndex); const resetChars = shellType ? shellTypeResetChars.get(shellType) ?? defaultShellTypeResetChars : defaultShellTypeResetChars; // Find the last reset character before the cursor @@ -419,10 +419,10 @@ export async function getCompletionItemsFromSpecs( name: string, token?: vscode.CancellationToken, executeExternals?: IFigExecuteExternals, -): Promise<{ items: vscode.TerminalCompletionItem[]; filesRequested: boolean; foldersRequested: boolean; fileExtensions?: string[]; cwd?: vscode.Uri }> { +): Promise<{ items: vscode.TerminalCompletionItem[]; showFiles: boolean; showFolders: boolean; fileExtensions?: string[]; cwd?: vscode.Uri }> { let items: vscode.TerminalCompletionItem[] = []; - let filesRequested = false; - let foldersRequested = false; + let showFiles = false; + let showFolders = false; let hasCurrentArg = false; let fileExtensions: string[] | undefined; @@ -455,8 +455,8 @@ export async function getCompletionItemsFromSpecs( const result = await getFigSuggestions(specs, terminalContext, availableCommands, currentCommandString, tokenType, shellIntegrationCwd, env, name, executeExternalsWithFallback, token); if (result) { hasCurrentArg ||= result.hasCurrentArg; - filesRequested ||= result.filesRequested; - foldersRequested ||= result.foldersRequested; + showFiles ||= result.showFiles; + showFolders ||= result.showFolders; fileExtensions = result.fileExtensions; if (result.items) { items = items.concat(result.items); @@ -472,7 +472,7 @@ export async function getCompletionItemsFromSpecs( const labelWithoutExtension = isWindows ? commandTextLabel.replace(/\.[^ ]+$/, '') : commandTextLabel; if (!labels.has(labelWithoutExtension)) { items.push(createCompletionItem( - terminalContext.cursorPosition, + terminalContext.cursorIndex, currentCommandString, command, command.detail, @@ -491,23 +491,23 @@ export async function getCompletionItemsFromSpecs( existingItem.detail ??= command.detail; } } - filesRequested = true; - foldersRequested = true; + showFiles = true; + showFolders = true; } // For arguments when no fig suggestions are found these are fallback suggestions - else if (!items.length && !filesRequested && !foldersRequested && !hasCurrentArg) { + else if (!items.length && !showFiles && !showFolders && !hasCurrentArg) { if (terminalContext.allowFallbackCompletions) { - filesRequested = true; - foldersRequested = true; + showFiles = true; + showFolders = true; } } let cwd: vscode.Uri | undefined; - if (shellIntegrationCwd && (filesRequested || foldersRequested)) { + if (shellIntegrationCwd && (showFiles || showFolders)) { cwd = await resolveCwdFromCurrentCommandString(currentCommandString, shellIntegrationCwd); } - return { items, filesRequested, foldersRequested, fileExtensions, cwd }; + return { items, showFiles: showFiles, showFolders: showFolders, fileExtensions, cwd }; } function getEnvAsRecord(shellIntegrationEnv: ITerminalEnvironment): Record { @@ -564,7 +564,7 @@ export function sanitizeProcessEnvironment(env: Record, ...prese }); } -function createFileGlobPattern(fileExtensions?: string[]): vscode.GlobPattern | undefined { +function createFileGlobPattern(fileExtensions?: string[]): string | undefined { if (!fileExtensions || fileExtensions.length === 0) { return undefined; } diff --git a/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts b/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts index 5f3fb96a72c..e60099c6089 100644 --- a/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts +++ b/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts @@ -91,11 +91,11 @@ suite('Terminal Suggest', () => { } test(`'${testSpec.input}' -> ${expectedString}`, async () => { const commandLine = testSpec.input.split('|')[0]; - const cursorPosition = testSpec.input.indexOf('|'); - const currentCommandString = getCurrentCommandAndArgs(commandLine, cursorPosition, undefined); - const filesRequested = testSpec.expectedResourceRequests?.type === 'files' || testSpec.expectedResourceRequests?.type === 'both'; - const foldersRequested = testSpec.expectedResourceRequests?.type === 'folders' || testSpec.expectedResourceRequests?.type === 'both'; - const terminalContext = { commandLine, cursorPosition, allowFallbackCompletions: true }; + const cursorIndex = testSpec.input.indexOf('|'); + const currentCommandString = getCurrentCommandAndArgs(commandLine, cursorIndex, undefined); + const showFiles = testSpec.expectedResourceRequests?.type === 'files' || testSpec.expectedResourceRequests?.type === 'both'; + const showFolders = testSpec.expectedResourceRequests?.type === 'folders' || testSpec.expectedResourceRequests?.type === 'both'; + const terminalContext = { commandLine, cursorIndex, allowFallbackCompletions: true }; const result = await getCompletionItemsFromSpecs( completionSpecs, terminalContext, @@ -118,8 +118,8 @@ suite('Terminal Suggest', () => { }).sort(), (testSpec.expectedCompletions ?? []).sort() ); - strictEqual(result.filesRequested, filesRequested, 'Files requested different than expected, got: ' + result.filesRequested); - strictEqual(result.foldersRequested, foldersRequested, 'Folders requested different than expected, got: ' + result.foldersRequested); + strictEqual(result.showFiles, showFiles, 'Show files different than expected, got: ' + result.showFiles); + strictEqual(result.showFolders, showFolders, 'Show folders different than expected, got: ' + result.showFolders); if (testSpec.expectedResourceRequests?.cwd) { strictEqual(result.cwd?.fsPath, testSpec.expectedResourceRequests.cwd.fsPath, 'Non matching cwd'); } diff --git a/extensions/terminal-suggest/src/test/tokens.test.ts b/extensions/terminal-suggest/src/test/tokens.test.ts index fb3ea702bab..dc583a28b08 100644 --- a/extensions/terminal-suggest/src/test/tokens.test.ts +++ b/extensions/terminal-suggest/src/test/tokens.test.ts @@ -10,91 +10,91 @@ import { TerminalShellType } from '../terminalSuggestMain'; suite('Terminal Suggest', () => { test('simple command', () => { - strictEqual(getTokenType({ commandLine: 'echo', cursorPosition: 'echo'.length }, undefined), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo', cursorIndex: 'echo'.length }, undefined), TokenType.Command); }); test('simple argument', () => { - strictEqual(getTokenType({ commandLine: 'echo hello', cursorPosition: 'echo hello'.length }, undefined), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello', cursorIndex: 'echo hello'.length }, undefined), TokenType.Argument); }); test('simple command, cursor mid text', () => { - strictEqual(getTokenType({ commandLine: 'echo hello', cursorPosition: 'echo'.length }, undefined), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello', cursorIndex: 'echo'.length }, undefined), TokenType.Command); }); test('simple argument, cursor mid text', () => { - strictEqual(getTokenType({ commandLine: 'echo hello', cursorPosition: 'echo hel'.length }, undefined), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello', cursorIndex: 'echo hel'.length }, undefined), TokenType.Argument); }); suite('reset to command', () => { test('|', () => { - strictEqual(getTokenType({ commandLine: 'echo hello | ', cursorPosition: 'echo hello | '.length }, undefined), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello | ', cursorIndex: 'echo hello | '.length }, undefined), TokenType.Command); }); test(';', () => { - strictEqual(getTokenType({ commandLine: 'echo hello; ', cursorPosition: 'echo hello; '.length }, undefined), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello; ', cursorIndex: 'echo hello; '.length }, undefined), TokenType.Command); }); test('&&', () => { - strictEqual(getTokenType({ commandLine: 'echo hello && ', cursorPosition: 'echo hello && '.length }, undefined), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello && ', cursorIndex: 'echo hello && '.length }, undefined), TokenType.Command); }); test('||', () => { - strictEqual(getTokenType({ commandLine: 'echo hello || ', cursorPosition: 'echo hello || '.length }, undefined), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello || ', cursorIndex: 'echo hello || '.length }, undefined), TokenType.Command); }); }); suite('pwsh', () => { test('simple command', () => { - strictEqual(getTokenType({ commandLine: 'Write-Host', cursorPosition: 'Write-Host'.length }, TerminalShellType.PowerShell), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'Write-Host', cursorIndex: 'Write-Host'.length }, TerminalShellType.PowerShell), TokenType.Command); }); test('simple argument', () => { - strictEqual(getTokenType({ commandLine: 'Write-Host hello', cursorPosition: 'Write-Host hello'.length }, TerminalShellType.PowerShell), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'Write-Host hello', cursorIndex: 'Write-Host hello'.length }, TerminalShellType.PowerShell), TokenType.Argument); }); test('reset char', () => { - strictEqual(getTokenType({ commandLine: `Write-Host hello -and `, cursorPosition: `Write-Host hello -and `.length }, TerminalShellType.PowerShell), TokenType.Command); + strictEqual(getTokenType({ commandLine: `Write-Host hello -and `, cursorIndex: `Write-Host hello -and `.length }, TerminalShellType.PowerShell), TokenType.Command); }); test('arguments after reset char', () => { - strictEqual(getTokenType({ commandLine: `Write-Host hello -and $true `, cursorPosition: `Write-Host hello -and $true `.length }, TerminalShellType.PowerShell), TokenType.Argument); + strictEqual(getTokenType({ commandLine: `Write-Host hello -and $true `, cursorIndex: `Write-Host hello -and $true `.length }, TerminalShellType.PowerShell), TokenType.Argument); }); test('; reset char', () => { - strictEqual(getTokenType({ commandLine: `Write-Host hello; `, cursorPosition: `Write-Host hello; `.length }, TerminalShellType.PowerShell), TokenType.Command); + strictEqual(getTokenType({ commandLine: `Write-Host hello; `, cursorIndex: `Write-Host hello; `.length }, TerminalShellType.PowerShell), TokenType.Command); }); suite('multiple commands on the line', () => { test('multiple commands, cursor at end', () => { - strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorPosition: 'echo hello && ech'.length }, undefined), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorIndex: 'echo hello && ech'.length }, undefined), TokenType.Command); // Bash - strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorPosition: 'echo hello && ech'.length }, TerminalShellType.Bash), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorIndex: 'echo hello && ech'.length }, TerminalShellType.Bash), TokenType.Command); // Zsh - strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorPosition: 'echo hello && ech'.length }, TerminalShellType.Zsh), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorIndex: 'echo hello && ech'.length }, TerminalShellType.Zsh), TokenType.Command); // Fish (use ';' as separator) - strictEqual(getTokenType({ commandLine: 'echo hello; echo world', cursorPosition: 'echo hello; ech'.length }, TerminalShellType.Fish), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello; echo world', cursorIndex: 'echo hello; ech'.length }, TerminalShellType.Fish), TokenType.Command); // PowerShell (use ';' as separator) - strictEqual(getTokenType({ commandLine: 'echo hello; echo world', cursorPosition: 'echo hello; ech'.length }, TerminalShellType.PowerShell), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello; echo world', cursorIndex: 'echo hello; ech'.length }, TerminalShellType.PowerShell), TokenType.Command); }); test('multiple commands, cursor mid text', () => { - strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorPosition: 'echo hello && echo w'.length }, undefined), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorIndex: 'echo hello && echo w'.length }, undefined), TokenType.Argument); // Bash - strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorPosition: 'echo hello && echo w'.length }, TerminalShellType.Bash), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorIndex: 'echo hello && echo w'.length }, TerminalShellType.Bash), TokenType.Argument); // Zsh - strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorPosition: 'echo hello && echo w'.length }, TerminalShellType.Zsh), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world', cursorIndex: 'echo hello && echo w'.length }, TerminalShellType.Zsh), TokenType.Argument); // Fish (use ';' as separator) - strictEqual(getTokenType({ commandLine: 'echo hello; echo world', cursorPosition: 'echo hello; echo w'.length }, TerminalShellType.Fish), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello; echo world', cursorIndex: 'echo hello; echo w'.length }, TerminalShellType.Fish), TokenType.Argument); // PowerShell (use ';' as separator) - strictEqual(getTokenType({ commandLine: 'echo hello; echo world', cursorPosition: 'echo hello; echo w'.length }, TerminalShellType.PowerShell), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello; echo world', cursorIndex: 'echo hello; echo w'.length }, TerminalShellType.PowerShell), TokenType.Argument); }); test('multiple commands, cursor at end with reset char', () => { - strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorPosition: 'echo hello && echo world; '.length }, undefined), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorIndex: 'echo hello && echo world; '.length }, undefined), TokenType.Command); // Bash - strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorPosition: 'echo hello && echo world; '.length }, TerminalShellType.Bash), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorIndex: 'echo hello && echo world; '.length }, TerminalShellType.Bash), TokenType.Command); // Zsh - strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorPosition: 'echo hello && echo world; '.length }, TerminalShellType.Zsh), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorIndex: 'echo hello && echo world; '.length }, TerminalShellType.Zsh), TokenType.Command); // Fish (use ';' as separator) - strictEqual(getTokenType({ commandLine: 'echo hello; echo world; ', cursorPosition: 'echo hello; echo world; '.length }, TerminalShellType.Fish), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello; echo world; ', cursorIndex: 'echo hello; echo world; '.length }, TerminalShellType.Fish), TokenType.Command); // PowerShell (use ';' as separator) - strictEqual(getTokenType({ commandLine: 'echo hello; echo world; ', cursorPosition: 'echo hello; echo world; '.length }, TerminalShellType.PowerShell), TokenType.Command); + strictEqual(getTokenType({ commandLine: 'echo hello; echo world; ', cursorIndex: 'echo hello; echo world; '.length }, TerminalShellType.PowerShell), TokenType.Command); }); test('multiple commands, cursor mid text with reset char', () => { - strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorPosition: 'echo hello && echo worl'.length }, undefined), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorIndex: 'echo hello && echo worl'.length }, undefined), TokenType.Argument); // Bash - strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorPosition: 'echo hello && echo worl'.length }, TerminalShellType.Bash), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorIndex: 'echo hello && echo worl'.length }, TerminalShellType.Bash), TokenType.Argument); // Zsh - strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorPosition: 'echo hello && echo worl'.length }, TerminalShellType.Zsh), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello && echo world; ', cursorIndex: 'echo hello && echo worl'.length }, TerminalShellType.Zsh), TokenType.Argument); // Fish (use ';' as separator) - strictEqual(getTokenType({ commandLine: 'echo hello; echo world; ', cursorPosition: 'echo hello; echo worl'.length }, TerminalShellType.Fish), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello; echo world; ', cursorIndex: 'echo hello; echo worl'.length }, TerminalShellType.Fish), TokenType.Argument); // PowerShell (use ';' as separator) - strictEqual(getTokenType({ commandLine: 'echo hello; echo world; ', cursorPosition: 'echo hello; echo worl'.length }, TerminalShellType.PowerShell), TokenType.Argument); + strictEqual(getTokenType({ commandLine: 'echo hello; echo world; ', cursorIndex: 'echo hello; echo worl'.length }, TerminalShellType.PowerShell), TokenType.Argument); }); }); }); diff --git a/extensions/terminal-suggest/src/tokens.ts b/extensions/terminal-suggest/src/tokens.ts index cadab15ba64..11a23f7ad43 100644 --- a/extensions/terminal-suggest/src/tokens.ts +++ b/extensions/terminal-suggest/src/tokens.ts @@ -19,9 +19,9 @@ export const shellTypeResetChars = new Map([ export const defaultShellTypeResetChars = shellTypeResetChars.get(TerminalShellType.Bash)!; -export function getTokenType(ctx: { commandLine: string; cursorPosition: number }, shellType: TerminalShellType | undefined): TokenType { +export function getTokenType(ctx: { commandLine: string; cursorIndex: number }, shellType: TerminalShellType | undefined): TokenType { const commandLine = ctx.commandLine; - const cursorPosition = ctx.cursorPosition; + const cursorPosition = ctx.cursorIndex; const commandResetChars = shellType === undefined ? defaultShellTypeResetChars : shellTypeResetChars.get(shellType) ?? defaultShellTypeResetChars; // Check for reset char before the current word diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 64afd9475cf..533e27a5d6e 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -278,18 +278,18 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._completionProviders.set(id, this._terminalCompletionService.registerTerminalCompletionProvider(extensionIdentifier, id, { id, provideCompletions: async (commandLine, cursorPosition, allowFallbackCompletions, token) => { - const completions = await this._proxy.$provideTerminalCompletions(id, { commandLine, cursorPosition, allowFallbackCompletions }, token); + const completions = await this._proxy.$provideTerminalCompletions(id, { commandLine, cursorIndex: cursorPosition, allowFallbackCompletions }, token); if (!completions) { return undefined; } - if (completions.resourceRequestConfig) { - const { cwd, globPattern, ...rest } = completions.resourceRequestConfig; + if (completions.resourceOptions) { + const { cwd, globPattern, ...rest } = completions.resourceOptions; return { items: completions.items?.map(c => ({ provider: `ext:${id}`, ...c, })), - resourceRequestConfig: { + resourceOptions: { ...rest, cwd, globPattern diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index f7ca67e2a19..7177c83cdc0 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -2476,7 +2476,7 @@ export interface ITerminalCommandDto { export interface ITerminalCompletionContextDto { commandLine: string; - cursorPosition: number; + cursorIndex: number; allowFallbackCompletions: boolean; } @@ -2508,7 +2508,7 @@ export class TerminalCompletionListDto TerminalCompletionItemDto.from(i)), - resourceRequestConfig: completions.resourceRequestConfig ? TerminalResourceRequestConfig.from(completions.resourceRequestConfig, pathSeparator) : undefined, + resourceOptions: completions.resourceOptions ? TerminalCompletionResourceOptions.from(completions.resourceOptions, pathSeparator) : undefined, }; } } -export namespace TerminalResourceRequestConfig { - export function from(resourceRequestConfig: vscode.TerminalResourceRequestConfig, pathSeparator: string): extHostProtocol.TerminalResourceRequestConfigDto { +export namespace TerminalCompletionResourceOptions { + export function from(resourceOptions: vscode.TerminalCompletionResourceOptions, pathSeparator: string): extHostProtocol.TerminalCompletionResourceOptionsDto { return { - ...resourceRequestConfig, + ...resourceOptions, pathSeparator, - cwd: resourceRequestConfig.cwd, - globPattern: GlobPattern.from(resourceRequestConfig.globPattern) ?? undefined + cwd: resourceOptions.cwd, + globPattern: GlobPattern.from(resourceOptions.globPattern) ?? undefined }; } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index ff5bf7abeb4..ca7116aba8b 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -929,11 +929,11 @@ export enum TerminalCompletionItemKind { Flag = 7, SymbolicLinkFile = 8, SymbolicLinkFolder = 9, - Commit = 10, - Branch = 11, - Tag = 12, - Stash = 13, - Remote = 14, + ScmCommit = 10, + ScmBranch = 11, + ScmTag = 12, + ScmStash = 13, + ScmRemote = 14, PullRequest = 15, PullRequestDone = 16, } @@ -971,7 +971,7 @@ export class TerminalCompletionList { /** * Resources should be shown in the completions list */ - resourceRequestConfig?: TerminalResourceRequestConfig; + resourceOptions?: TerminalCompletionResourceOptions; /** * The completion items. @@ -47,15 +47,15 @@ export class TerminalCompletionList { * @param items The completion items. * @param isIncomplete The list is not complete. */ - constructor(items?: ITerminalCompletion[], resourceRequestConfig?: TerminalResourceRequestConfig) { + constructor(items?: ITerminalCompletion[], resourceOptions?: TerminalCompletionResourceOptions) { this.items = items; - this.resourceRequestConfig = resourceRequestConfig; + this.resourceOptions = resourceOptions; } } -export interface TerminalResourceRequestConfig { - filesRequested?: boolean; - foldersRequested?: boolean; +export interface TerminalCompletionResourceOptions { + showFiles?: boolean; + showFolders?: boolean; globPattern?: string | IRelativePattern; cwd: UriComponents; pathSeparator: string; @@ -223,8 +223,8 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo if (Array.isArray(completions)) { return completionItems; } - if (completions.resourceRequestConfig) { - const resourceCompletions = await this.resolveResources(completions.resourceRequestConfig, promptValue, cursorPosition, `core:path:ext:${provider.id}`, capabilities, shellType); + if (completions.resourceOptions) { + const resourceCompletions = await this.resolveResources(completions.resourceOptions, promptValue, cursorPosition, `core:path:ext:${provider.id}`, capabilities, shellType); this._logService.trace(`TerminalCompletionService#_collectCompletions dedupe`); if (resourceCompletions) { const labels = new Set(completionItems.map(c => c.label)); @@ -245,22 +245,22 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo return results.filter(result => !!result).flat(); } - async resolveResources(resourceRequestConfig: TerminalResourceRequestConfig, promptValue: string, cursorPosition: number, provider: string, capabilities: ITerminalCapabilityStore, shellType?: TerminalShellType): Promise { + async resolveResources(resourceOptions: TerminalCompletionResourceOptions, promptValue: string, cursorPosition: number, provider: string, capabilities: ITerminalCapabilityStore, shellType?: TerminalShellType): Promise { this._logService.trace(`TerminalCompletionService#resolveResources`); - const useWindowsStylePath = resourceRequestConfig.pathSeparator === '\\'; + const useWindowsStylePath = resourceOptions.pathSeparator === '\\'; if (useWindowsStylePath) { // for tests, make sure the right path separator is used - promptValue = promptValue.replaceAll(/[\\/]/g, resourceRequestConfig.pathSeparator); + promptValue = promptValue.replaceAll(/[\\/]/g, resourceOptions.pathSeparator); } // Files requested implies folders requested since the file could be in any folder. We could // provide diagnostics when a folder is provided where a file is expected. - const foldersRequested = (resourceRequestConfig.foldersRequested || resourceRequestConfig.filesRequested) ?? false; - const filesRequested = resourceRequestConfig.filesRequested ?? false; - const globPattern = resourceRequestConfig.globPattern ?? undefined; + const showFolders = (resourceOptions.showFolders || resourceOptions.showFiles) ?? false; + const showFiles = resourceOptions.showFiles ?? false; + const globPattern = resourceOptions.globPattern ?? undefined; - if (!foldersRequested && !filesRequested) { + if (!showFolders && !showFiles) { return; } @@ -288,7 +288,7 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo } lastSlashIndex = Math.max(lastBackslashIndex, lastWord.lastIndexOf('/')); } else { - lastSlashIndex = lastWord.lastIndexOf(resourceRequestConfig.pathSeparator); + lastSlashIndex = lastWord.lastIndexOf(resourceOptions.pathSeparator); } // The _complete_ folder of the last word. For example if the last word is `./src/file`, @@ -304,9 +304,9 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo let lastWordFolderResource: URI | string | undefined; const lastWordFolderHasDotPrefix = !!lastWordFolder.match(/^\.\.?[\\\/]/); const lastWordFolderHasTildePrefix = !!lastWordFolder.match(/^~[\\\/]?/); - const isAbsolutePath = getIsAbsolutePath(shellType, resourceRequestConfig.pathSeparator, lastWordFolder, useWindowsStylePath); + const isAbsolutePath = getIsAbsolutePath(shellType, resourceOptions.pathSeparator, lastWordFolder, useWindowsStylePath); const type = lastWordFolderHasTildePrefix ? 'tilde' : isAbsolutePath ? 'absolute' : 'relative'; - const cwd = URI.revive(resourceRequestConfig.cwd); + const cwd = URI.revive(resourceOptions.cwd); switch (type) { case 'tilde': { @@ -374,7 +374,7 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo // - (tilde) `~/|` -> `~/` // - (tilde) `~/src/|` -> `~/src/` this._logService.trace(`TerminalCompletionService#resolveResources cwd`); - if (foldersRequested) { + if (showFolders) { let label: string; switch (type) { case 'tilde': { @@ -388,7 +388,7 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo case 'relative': { label = '.'; if (lastWordFolder.length > 0) { - label = addPathRelativePrefix(lastWordFolder, resourceRequestConfig, lastWordFolderHasDotPrefix); + label = addPathRelativePrefix(lastWordFolder, resourceOptions, lastWordFolderHasDotPrefix); } break; } @@ -397,7 +397,7 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo label, provider, kind: TerminalCompletionItemKind.Folder, - detail: getFriendlyPath(this._labelService, lastWordFolderResource, resourceRequestConfig.pathSeparator, TerminalCompletionItemKind.Folder, shellType), + detail: getFriendlyPath(this._labelService, lastWordFolderResource, resourceOptions.pathSeparator, TerminalCompletionItemKind.Folder, shellType), replacementIndex: cursorPosition - lastWord.length, replacementLength: lastWord.length }); @@ -412,13 +412,13 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo await Promise.all(stat.children.map(child => (async () => { let kind: TerminalCompletionItemKind | undefined; let detail: string | undefined = undefined; - if (foldersRequested && child.isDirectory) { + if (showFolders && child.isDirectory) { if (child.isSymbolicLink) { kind = TerminalCompletionItemKind.SymbolicLinkFolder; } else { kind = TerminalCompletionItemKind.Folder; } - } else if (filesRequested && child.isFile) { + } else if (showFiles && child.isFile) { if (child.isSymbolicLink) { kind = TerminalCompletionItemKind.SymbolicLinkFile; } else { @@ -430,18 +430,18 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo } let label = lastWordFolder; - if (label.length > 0 && !label.endsWith(resourceRequestConfig.pathSeparator)) { - label += resourceRequestConfig.pathSeparator; + if (label.length > 0 && !label.endsWith(resourceOptions.pathSeparator)) { + label += resourceOptions.pathSeparator; } label += child.name; if (type === 'relative') { - label = addPathRelativePrefix(label, resourceRequestConfig, lastWordFolderHasDotPrefix); + label = addPathRelativePrefix(label, resourceOptions, lastWordFolderHasDotPrefix); } - if (child.isDirectory && !label.endsWith(resourceRequestConfig.pathSeparator)) { - label += resourceRequestConfig.pathSeparator; + if (child.isDirectory && !label.endsWith(resourceOptions.pathSeparator)) { + label += resourceOptions.pathSeparator; } - label = escapeTerminalCompletionLabel(label, shellType, resourceRequestConfig.pathSeparator); + label = escapeTerminalCompletionLabel(label, shellType, resourceOptions.pathSeparator); if (child.isFile && globPattern) { const filePath = child.resource.fsPath; @@ -456,7 +456,7 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo try { const realpath = await this._fileService.realpath(child.resource); if (realpath && !isEqual(child.resource, realpath)) { - detail = `${getFriendlyPath(this._labelService, child.resource, resourceRequestConfig.pathSeparator, kind, shellType)} -> ${getFriendlyPath(this._labelService, realpath, resourceRequestConfig.pathSeparator, kind, shellType)}`; + detail = `${getFriendlyPath(this._labelService, child.resource, resourceOptions.pathSeparator, kind, shellType)} -> ${getFriendlyPath(this._labelService, realpath, resourceOptions.pathSeparator, kind, shellType)}`; } } catch (error) { // Ignore errors resolving symlink targets - they may be dangling links @@ -467,7 +467,7 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo label, provider, kind, - detail: detail ?? getFriendlyPath(this._labelService, child.resource, resourceRequestConfig.pathSeparator, kind, shellType), + detail: detail ?? getFriendlyPath(this._labelService, child.resource, resourceOptions.pathSeparator, kind, shellType), replacementIndex: cursorPosition - lastWord.length, replacementLength: lastWord.length }); @@ -477,7 +477,7 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo // // - (relative) `|` -> `/foo/vscode` (CDPATH has /foo which contains vscode folder) this._logService.trace(`TerminalCompletionService#resolveResources CDPATH`); - if (type === 'relative' && foldersRequested) { + if (type === 'relative' && showFolders) { if (promptValue.startsWith('cd ')) { const config = this._configurationService.getValue(TerminalSuggestSettingId.CdPath); if (config === 'absolute' || config === 'relative') { @@ -498,9 +498,9 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo ? basename(child.resource.fsPath) : shellType === WindowsShellType.GitBash ? windowsToGitBashPath(child.resource.fsPath) - : getFriendlyPath(this._labelService, child.resource, resourceRequestConfig.pathSeparator, kind, shellType); + : getFriendlyPath(this._labelService, child.resource, resourceOptions.pathSeparator, kind, shellType); const detail = useRelative - ? `CDPATH ${getFriendlyPath(this._labelService, child.resource, resourceRequestConfig.pathSeparator, kind, shellType)}` + ? `CDPATH ${getFriendlyPath(this._labelService, child.resource, resourceOptions.pathSeparator, kind, shellType)}` : `CDPATH`; resourceCompletions.push({ label, @@ -524,17 +524,17 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo // - (relative) `|` -> `../` // - (relative) `./src/|` -> `./src/../` this._logService.trace(`TerminalCompletionService#resolveResources parent dir`); - if (type === 'relative' && foldersRequested) { - let label = `..${resourceRequestConfig.pathSeparator}`; + if (type === 'relative' && showFolders) { + let label = `..${resourceOptions.pathSeparator}`; if (lastWordFolder.length > 0) { - label = addPathRelativePrefix(lastWordFolder + label, resourceRequestConfig, lastWordFolderHasDotPrefix); + label = addPathRelativePrefix(lastWordFolder + label, resourceOptions, lastWordFolderHasDotPrefix); } - const parentDir = URI.joinPath(cwd, '..' + resourceRequestConfig.pathSeparator); + const parentDir = URI.joinPath(cwd, '..' + resourceOptions.pathSeparator); resourceCompletions.push({ label, provider, kind: TerminalCompletionItemKind.Folder, - detail: getFriendlyPath(this._labelService, parentDir, resourceRequestConfig.pathSeparator, TerminalCompletionItemKind.Folder, shellType), + detail: getFriendlyPath(this._labelService, parentDir, resourceOptions.pathSeparator, TerminalCompletionItemKind.Folder, shellType), replacementIndex: cursorPosition - lastWord.length, replacementLength: lastWord.length }); @@ -560,7 +560,7 @@ export class TerminalCompletionService extends Disposable implements ITerminalCo label: '~', provider, kind: TerminalCompletionItemKind.Folder, - detail: typeof homeResource === 'string' ? homeResource : getFriendlyPath(this._labelService, homeResource, resourceRequestConfig.pathSeparator, TerminalCompletionItemKind.Folder, shellType), + detail: typeof homeResource === 'string' ? homeResource : getFriendlyPath(this._labelService, homeResource, resourceOptions.pathSeparator, TerminalCompletionItemKind.Folder, shellType), replacementIndex: cursorPosition - lastWord.length, replacementLength: lastWord.length }); @@ -597,12 +597,12 @@ function getFriendlyPath(labelService: ILabelService, uri: URI, pathSeparator: s * Normalize suggestion to add a ./ prefix to the start of the path if there isn't one already. We * may want to change this behavior in the future to go with whatever format the user has. */ -function addPathRelativePrefix(text: string, resourceRequestConfig: Pick, lastWordFolderHasDotPrefix: boolean): string { +function addPathRelativePrefix(text: string, resourceOptions: Pick, lastWordFolderHasDotPrefix: boolean): string { if (!lastWordFolderHasDotPrefix) { - if (text.startsWith(resourceRequestConfig.pathSeparator)) { + if (text.startsWith(resourceOptions.pathSeparator)) { return `.${text}`; } - return `.${resourceRequestConfig.pathSeparator}${text}`; + return `.${resourceOptions.pathSeparator}${text}`; } return text; } diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts b/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts index 5094ba0acae..27a681094ae 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalCompletionService.test.ts @@ -5,7 +5,7 @@ import { URI } from '../../../../../../base/common/uri.js'; import { IFileService, IFileStatWithMetadata, IResolveMetadataFileOptions } from '../../../../../../platform/files/common/files.js'; -import { TerminalCompletionService, TerminalResourceRequestConfig, type ITerminalCompletionProvider } from '../../browser/terminalCompletionService.js'; +import { TerminalCompletionService, TerminalCompletionResourceOptions, type ITerminalCompletionProvider } from '../../browser/terminalCompletionService.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; import assert, { fail } from 'assert'; import { isWindows, type IProcessEnvironment } from '../../../../../../base/common/platform.js'; @@ -147,13 +147,13 @@ suite('TerminalCompletionService', () => { }); suite('resolveResources should return undefined', () => { - test('if neither filesRequested nor foldersRequested are true', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + test('if neither showFiles nor showFolders are true', async () => { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), pathSeparator }; validResources = [URI.parse('file:///test')]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ', 3, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, 'cd ', 3, provider, capabilities); assert(!result); }); }); @@ -168,12 +168,12 @@ suite('TerminalCompletionService', () => { }); test('| should return root-level completions', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, + showFolders: true, pathSeparator }; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, '', 1, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, '', 1, provider, capabilities); assertCompletions(result, [ { label: '.', detail: '/test/' }, @@ -184,12 +184,12 @@ suite('TerminalCompletionService', () => { }); test('./| should return folder completions', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, + showFolders: true, pathSeparator }; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, './', 3, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, './', 3, provider, capabilities); assertCompletions(result, [ { label: './', detail: '/test/' }, @@ -199,12 +199,12 @@ suite('TerminalCompletionService', () => { }); test('cd ./| should return folder completions', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, + showFolders: true, pathSeparator }; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ./', 5, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, 'cd ./', 5, provider, capabilities); assertCompletions(result, [ { label: './', detail: '/test/' }, @@ -213,12 +213,12 @@ suite('TerminalCompletionService', () => { ], { replacementIndex: 3, replacementLength: 2 }); }); test('cd ./f| should return folder completions', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, + showFolders: true, pathSeparator }; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ./f', 6, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, 'cd ./f', 6, provider, capabilities); assertCompletions(result, [ { label: './', detail: '/test/' }, @@ -240,13 +240,13 @@ suite('TerminalCompletionService', () => { }); test('./| should handle hidden files and folders', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, - filesRequested: true, + showFolders: true, + showFiles: true, pathSeparator }; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, './', 2, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, './', 2, provider, capabilities); assertCompletions(result, [ { label: './', detail: '/test/' }, @@ -259,13 +259,13 @@ suite('TerminalCompletionService', () => { }); test('./h| should handle hidden files and folders', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, - filesRequested: true, + showFolders: true, + showFiles: true, pathSeparator }; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, './h', 3, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, './h', 3, provider, capabilities); assertCompletions(result, [ { label: './', detail: '/test/' }, @@ -279,7 +279,7 @@ suite('TerminalCompletionService', () => { }); suite('~ -> $HOME', () => { - let resourceRequestConfig: TerminalResourceRequestConfig; + let resourceOptions: TerminalCompletionResourceOptions; let shellEnvDetection: ShellEnvDetectionCapability; setup(() => { @@ -290,10 +290,10 @@ suite('TerminalCompletionService', () => { }, true); capabilities.add(TerminalCapability.ShellEnvDetection, shellEnvDetection); - resourceRequestConfig = { + resourceOptions = { cwd: URI.parse('file:///test/folder1'),// Updated to reflect home directory - filesRequested: true, - foldersRequested: true, + showFiles: true, + showFolders: true, pathSeparator }; validResources = [ @@ -312,20 +312,20 @@ suite('TerminalCompletionService', () => { }); test('~| should return completion for ~', async () => { - assertPartialCompletionsExist(await terminalCompletionService.resolveResources(resourceRequestConfig, '~', 1, provider, capabilities), [ + assertPartialCompletionsExist(await terminalCompletionService.resolveResources(resourceOptions, '~', 1, provider, capabilities), [ { label: '~', detail: '/home/' }, ], { replacementIndex: 0, replacementLength: 1 }); }); test('~/| should return folder completions relative to $HOME', async () => { - assertCompletions(await terminalCompletionService.resolveResources(resourceRequestConfig, '~/', 2, provider, capabilities), [ + assertCompletions(await terminalCompletionService.resolveResources(resourceOptions, '~/', 2, provider, capabilities), [ { label: '~/', detail: '/home/' }, { label: '~/vscode/', detail: '/home/vscode/' }, ], { replacementIndex: 0, replacementLength: 2 }); }); test('~/vscode/| should return folder completions relative to $HOME/vscode', async () => { - assertCompletions(await terminalCompletionService.resolveResources(resourceRequestConfig, '~/vscode/', 9, provider, capabilities), [ + assertCompletions(await terminalCompletionService.resolveResources(resourceOptions, '~/vscode/', 9, provider, capabilities), [ { label: '~/vscode/', detail: '/home/vscode/' }, { label: '~/vscode/foo/', detail: '/home/vscode/foo/' }, { label: '~/vscode/bar.txt', detail: '/home/vscode/bar.txt', kind: TerminalCompletionItemKind.File }, @@ -341,9 +341,9 @@ suite('TerminalCompletionService', () => { if (isWindows) { test('C:/Foo/| absolute paths on Windows', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///C:'), - foldersRequested: true, + showFolders: true, pathSeparator }; validResources = [URI.parse('file:///C:/Foo')]; @@ -351,7 +351,7 @@ suite('TerminalCompletionService', () => { { resource: URI.parse('file:///C:/Foo/Bar'), isDirectory: true, isFile: false }, { resource: URI.parse('file:///C:/Foo/Baz.txt'), isDirectory: false, isFile: true } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'C:/Foo/', 7, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, 'C:/Foo/', 7, provider, capabilities); assertCompletions(result, [ { label: 'C:/Foo/', detail: 'C:/Foo/' }, @@ -359,16 +359,16 @@ suite('TerminalCompletionService', () => { ], { replacementIndex: 0, replacementLength: 7 }); }); test('c:/foo/| case insensitivity on Windows', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///c:'), - foldersRequested: true, + showFolders: true, pathSeparator }; validResources = [URI.parse('file:///c:/foo')]; childResources = [ { resource: URI.parse('file:///c:/foo/Bar'), isDirectory: true, isFile: false } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'c:/foo/', 7, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, 'c:/foo/', 7, provider, capabilities); assertCompletions(result, [ // Note that the detail is normalizes drive letters to capital case intentionally @@ -378,9 +378,9 @@ suite('TerminalCompletionService', () => { }); } else { test('/foo/| absolute paths NOT on Windows', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///'), - foldersRequested: true, + showFolders: true, pathSeparator }; validResources = [URI.parse('file:///foo')]; @@ -388,7 +388,7 @@ suite('TerminalCompletionService', () => { { resource: URI.parse('file:///foo/Bar'), isDirectory: true, isFile: false }, { resource: URI.parse('file:///foo/Baz.txt'), isDirectory: false, isFile: true } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, '/foo/', 5, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, '/foo/', 5, provider, capabilities); assertCompletions(result, [ { label: '/foo/', detail: '/foo/' }, @@ -399,9 +399,9 @@ suite('TerminalCompletionService', () => { if (isWindows) { test('.\\folder | Case insensitivity should resolve correctly on Windows', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///C:/test'), - foldersRequested: true, + showFolders: true, pathSeparator: '\\' }; @@ -411,7 +411,7 @@ suite('TerminalCompletionService', () => { { resource: URI.parse('file:///C:/test/anotherFolder/'), isDirectory: true } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, '.\\folder', 8, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, '.\\folder', 8, provider, capabilities); assertCompletions(result, [ { label: '.\\', detail: 'C:\\test\\' }, @@ -422,9 +422,9 @@ suite('TerminalCompletionService', () => { }); } else { test('./folder | Case sensitivity should resolve correctly on Mac/Unix', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, + showFolders: true, pathSeparator: '/' }; validResources = [URI.parse('file:///test')]; @@ -433,7 +433,7 @@ suite('TerminalCompletionService', () => { { resource: URI.parse('file:///test/foldera/'), isDirectory: true } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, './folder', 8, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, './folder', 8, provider, capabilities); assertCompletions(result, [ { label: './', detail: '/test/' }, @@ -445,9 +445,9 @@ suite('TerminalCompletionService', () => { } test('| Empty input should resolve to current directory', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, + showFolders: true, pathSeparator }; validResources = [URI.parse('file:///test')]; @@ -455,7 +455,7 @@ suite('TerminalCompletionService', () => { { resource: URI.parse('file:///test/folder1/'), isDirectory: true }, { resource: URI.parse('file:///test/folder2/'), isDirectory: true } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, '', 0, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, '', 0, provider, capabilities); assertCompletions(result, [ { label: '.', detail: '/test/' }, @@ -467,9 +467,9 @@ suite('TerminalCompletionService', () => { }); test('./| should handle large directories with many results gracefully', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, + showFolders: true, pathSeparator }; validResources = [URI.parse('file:///test')]; @@ -477,7 +477,7 @@ suite('TerminalCompletionService', () => { resource: URI.parse(`file:///test/folder${i}/`), isDirectory: true })); - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, './', 2, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, './', 2, provider, capabilities); assert(result); // includes the 1000 folders + ./ and ./../ @@ -487,9 +487,9 @@ suite('TerminalCompletionService', () => { }); test('./folder| should include current folder with trailing / is missing', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, + showFolders: true, pathSeparator }; validResources = [URI.parse('file:///test')]; @@ -497,7 +497,7 @@ suite('TerminalCompletionService', () => { { resource: URI.parse('file:///test/folder1/'), isDirectory: true }, { resource: URI.parse('file:///test/folder2/'), isDirectory: true } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, './folder1', 10, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, './folder1', 10, provider, capabilities); assertCompletions(result, [ { label: './', detail: '/test/' }, @@ -508,9 +508,9 @@ suite('TerminalCompletionService', () => { }); test('folder/| should normalize current and parent folders', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, + showFolders: true, pathSeparator }; validResources = [ @@ -523,7 +523,7 @@ suite('TerminalCompletionService', () => { { resource: URI.parse('file:///test/folder1/'), isDirectory: true }, { resource: URI.parse('file:///test/folder2/'), isDirectory: true } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'test/', 5, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, 'test/', 5, provider, capabilities); assertCompletions(result, [ { label: './test/', detail: '/test/' }, @@ -551,13 +551,13 @@ suite('TerminalCompletionService', () => { test('cd | should show paths from $CDPATH (relative)', async () => { configurationService.setUserConfiguration('terminal.integrated.suggest.cdPath', 'relative'); - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, - filesRequested: true, + showFolders: true, + showFiles: true, pathSeparator }; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ', 3, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, 'cd ', 3, provider, capabilities); assertPartialCompletionsExist(result, [ { label: 'folder1', detail: 'CDPATH /cdpath_value/folder1/' }, @@ -566,13 +566,13 @@ suite('TerminalCompletionService', () => { test('cd | should show paths from $CDPATH (absolute)', async () => { configurationService.setUserConfiguration('terminal.integrated.suggest.cdPath', 'absolute'); - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, - filesRequested: true, + showFolders: true, + showFiles: true, pathSeparator }; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ', 3, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, 'cd ', 3, provider, capabilities); assertPartialCompletionsExist(result, [ { label: '/cdpath_value/folder1/', detail: 'CDPATH' }, @@ -602,13 +602,13 @@ suite('TerminalCompletionService', () => { { resource: URI.parse(`${uriPathPrefix}cdpath2_value/inner_dir/file1.txt`), isFile: true }, ]; - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse(`${uriPathPrefix}test`), - foldersRequested: true, - filesRequested: true, + showFolders: true, + showFiles: true, pathSeparator }; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'cd ', 3, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, 'cd ', 3, provider, capabilities); const finalPrefix = isWindows ? 'C:\\' : '/'; assertPartialCompletionsExist(result, [ @@ -637,10 +637,10 @@ suite('TerminalCompletionService', () => { }); test('resolveResources with c:/ style absolute path for Git Bash', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.file('C:\\Users\\foo'), - foldersRequested: true, - filesRequested: true, + showFolders: true, + showFiles: true, pathSeparator: '/' }; validResources = [ @@ -652,7 +652,7 @@ suite('TerminalCompletionService', () => { { resource: URI.file('C:\\Users\\foo\\bar'), isDirectory: true, isFile: false }, { resource: URI.file('C:\\Users\\foo\\baz.txt'), isFile: true } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'C:/Users/foo/', 13, provider, capabilities, WindowsShellType.GitBash); + const result = await terminalCompletionService.resolveResources(resourceOptions, 'C:/Users/foo/', 13, provider, capabilities, WindowsShellType.GitBash); assertCompletions(result, [ { label: 'C:/Users/foo/', detail: 'C:\\Users\\foo\\' }, { label: 'C:/Users/foo/bar/', detail: 'C:\\Users\\foo\\bar\\' }, @@ -660,10 +660,10 @@ suite('TerminalCompletionService', () => { ], { replacementIndex: 0, replacementLength: 13 }, '/'); }); test('resolveResources with cwd as Windows path (relative)', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.file('C:\\Users\\foo'), - foldersRequested: true, - filesRequested: true, + showFolders: true, + showFiles: true, pathSeparator: '/' }; validResources = [ @@ -675,7 +675,7 @@ suite('TerminalCompletionService', () => { { resource: URI.file('C:\\Users\\foo\\bar'), isDirectory: true }, { resource: URI.file('C:\\Users\\foo\\baz.txt'), isFile: true } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, './', 2, provider, capabilities, WindowsShellType.GitBash); + const result = await terminalCompletionService.resolveResources(resourceOptions, './', 2, provider, capabilities, WindowsShellType.GitBash); assertCompletions(result, [ { label: './', detail: 'C:\\Users\\foo\\' }, { label: './bar/', detail: 'C:\\Users\\foo\\bar\\' }, @@ -685,10 +685,10 @@ suite('TerminalCompletionService', () => { }); test('resolveResources with cwd as Windows path (absolute)', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.file('C:\\Users\\foo'), - foldersRequested: true, - filesRequested: true, + showFolders: true, + showFiles: true, pathSeparator: '/' }; validResources = [ @@ -700,7 +700,7 @@ suite('TerminalCompletionService', () => { { resource: URI.file('C:\\Users\\foo\\bar'), isDirectory: true }, { resource: URI.file('C:\\Users\\foo\\baz.txt'), isFile: true } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, '/c/Users/foo/', 13, provider, capabilities, WindowsShellType.GitBash); + const result = await terminalCompletionService.resolveResources(resourceOptions, '/c/Users/foo/', 13, provider, capabilities, WindowsShellType.GitBash); assertCompletions(result, [ { label: '/c/Users/foo/', detail: 'C:\\Users\\foo\\' }, { label: '/c/Users/foo/bar/', detail: 'C:\\Users\\foo\\bar\\' }, @@ -712,11 +712,11 @@ suite('TerminalCompletionService', () => { if (!isWindows) { suite('symlink support', () => { test('should include symlink target information in completions', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), pathSeparator, - filesRequested: true, - foldersRequested: true + showFiles: true, + showFolders: true }; validResources = [URI.parse('file:///test')]; @@ -729,7 +729,7 @@ suite('TerminalCompletionService', () => { { resource: URI.parse('file:///test/regular-folder'), isDirectory: true }, ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, 'ls ', 3, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, 'ls ', 3, provider, capabilities); // Find the symlink completion const symlinkFileCompletion = result?.find(c => c.label === './symlink-file'); @@ -741,10 +741,10 @@ suite('TerminalCompletionService', () => { } suite('completion label escaping', () => { test('| should escape special characters in file/folder names for POSIX shells', async () => { - const resourceRequestConfig: TerminalResourceRequestConfig = { + const resourceOptions: TerminalCompletionResourceOptions = { cwd: URI.parse('file:///test'), - foldersRequested: true, - filesRequested: true, + showFolders: true, + showFiles: true, pathSeparator }; validResources = [URI.parse('file:///test')]; @@ -754,7 +754,7 @@ suite('TerminalCompletionService', () => { { resource: URI.parse('file:///test/!special$chars&/'), isDirectory: true }, { resource: URI.parse('file:///test/!special$chars2&'), isFile: true } ]; - const result = await terminalCompletionService.resolveResources(resourceRequestConfig, '', 0, provider, capabilities); + const result = await terminalCompletionService.resolveResources(resourceOptions, '', 0, provider, capabilities); assertCompletions(result, [ { label: '.', detail: '/test/' }, diff --git a/src/vscode-dts/vscode.proposed.terminalCompletionProvider.d.ts b/src/vscode-dts/vscode.proposed.terminalCompletionProvider.d.ts index 37152b7145f..4b3836f7a16 100644 --- a/src/vscode-dts/vscode.proposed.terminalCompletionProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.terminalCompletionProvider.d.ts @@ -93,17 +93,29 @@ declare module 'vscode' { Method = 2, Alias = 3, Argument = 4, + /** + * An option, for example in `code --locale`, `--locale` is the option + */ Option = 5, + /** + * The value of an option, for example in `code --locale en-US`, `en-US` is the option value + */ OptionValue = 6, + /** + * A flag, for example in `git commit --amend"`, `--amend` is the flag + */ Flag = 7, SymbolicLinkFile = 8, SymbolicLinkFolder = 9, - Commit = 10, - Branch = 11, - Tag = 12, - Stash = 13, - Remote = 14, + ScmCommit = 10, + ScmBranch = 11, + ScmTag = 12, + ScmStash = 13, + ScmRemote = 14, PullRequest = 15, + /** + * The pull request has been closed + */ PullRequestDone = 16, } @@ -118,16 +130,16 @@ declare module 'vscode' { /** * The complete terminal command line. */ - commandLine: string; + readonly commandLine: string; /** * The index of the cursor in the command line. */ - cursorPosition: number; + readonly cursorIndex: number; /** - * Whether completions should be provided when it is not clear to what type of completion is - * well known. + * Whether completions should be provided when none are explicitly suggested. This will display + * fallback suggestions like files and folders. */ - allowFallbackCompletions: boolean; + readonly allowFallbackCompletions: boolean; } export namespace window { @@ -155,14 +167,14 @@ declare module 'vscode' { * @example Create a completion list that requests files for the terminal cwd * const list = new TerminalCompletionList([ * { label: 'ls', replacementIndex: 0, replacementLength: 0, kind: TerminalCompletionItemKind.Method } - * ], { filesRequested: true, cwd: Uri.file('/home/user') }); + * ], { showFiles: true, cwd: Uri.file('/home/user') }); */ export class TerminalCompletionList { /** * Resources that should be shown in the completions list for the cwd of the terminal. */ - resourceRequestConfig?: TerminalResourceRequestConfig; + resourceOptions?: TerminalCompletionResourceOptions; /** * The completion items. @@ -173,9 +185,9 @@ declare module 'vscode' { * Creates a new completion list. * * @param items The completion items. - * @param resourceRequestConfig Indicates which resources should be shown as completions for the cwd of the terminal. + * @param resourceOptions Indicates which resources should be shown as completions for the cwd of the terminal. */ - constructor(items?: T[], resourceRequestConfig?: TerminalResourceRequestConfig); + constructor(items: T[], resourceOptions?: TerminalCompletionResourceOptions); } @@ -185,19 +197,19 @@ declare module 'vscode' { * When a provider indicates that it wants file/folder resources, the terminal will surface completions for files and * folders that match {@link globPattern} from the provided {@link cwd}. */ - export interface TerminalResourceRequestConfig { + export interface TerminalCompletionResourceOptions { /** * Show files as completion items. */ - filesRequested?: boolean; + showFiles?: boolean; /** * Show folders as completion items. */ - foldersRequested?: boolean; + showDirectories?: boolean; /** - * A {@link GlobPattern glob pattern} that controls which files suggest should surface. + * A glob pattern string that controls which files suggest should surface. Note that this will only apply if {@param showFiles} or {@param showDirectories} is set to true. */ - globPattern?: GlobPattern; + globPattern?: string; /** * The cwd from which to request resources. */