diff --git a/extensions/terminal-suggest/src/helpers/executable.ts b/extensions/terminal-suggest/src/helpers/executable.ts index 32c6a3d05a1..20f936fb5b1 100644 --- a/extensions/terminal-suggest/src/helpers/executable.ts +++ b/extensions/terminal-suggest/src/helpers/executable.ts @@ -6,9 +6,10 @@ import { osIsWindows } from './os'; import * as fs from 'fs/promises'; -export async function isExecutable(filePath: string): Promise { +export async function isExecutable(filePath: string, configuredWindowsExecutableExtensions?: Object): Promise { if (osIsWindows()) { - return windowsExecutableExtensions.find(ext => filePath.endsWith(ext)) !== undefined; + const resolvedWindowsExecutableExtensions = resolveWindowsExecutableExtensions(configuredWindowsExecutableExtensions); + return resolvedWindowsExecutableExtensions.find(ext => filePath.endsWith(ext)) !== undefined; } try { const stats = await fs.stat(filePath); @@ -19,7 +20,23 @@ export async function isExecutable(filePath: string): Promise { return false; } } -const windowsExecutableExtensions: string[] = [ + +function resolveWindowsExecutableExtensions(configuredWindowsExecutableExtensions?: Object): string[] { + const resolvedWindowsExecutableExtensions: string[] = windowsDefaultExecutableExtensions; + const excluded = new Set(); + if (configuredWindowsExecutableExtensions) { + for (const [key, value] of Object.entries(configuredWindowsExecutableExtensions)) { + if (value === true) { + resolvedWindowsExecutableExtensions.push(key); + } else { + excluded.add(key); + } + } + } + return Array.from(new Set(resolvedWindowsExecutableExtensions)).filter(ext => !excluded.has(ext)); +} + +export const windowsDefaultExecutableExtensions: string[] = [ '.exe', // Executable file '.bat', // Batch file '.cmd', // Command script diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 4e4913c78ba..662df64816d 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -15,6 +15,8 @@ import { isExecutable } from './helpers/executable'; const isWindows = osIsWindows(); let cachedAvailableCommandsPath: string | undefined; +let cachedWindowsExecutableExtensions: Object | undefined; +const cachedWindowsExecutableExtensionsSettingId = 'terminal.integrated.suggest.windowsExecutableExtensions'; let cachedAvailableCommands: Set | undefined; const cachedBuiltinCommands: Map = new Map(); @@ -110,8 +112,18 @@ export async function activate(context: vscode.ExtensionContext) { return result.items; } }, '/', '\\')); -} + if (isWindows) { + cachedWindowsExecutableExtensions = vscode.workspace.getConfiguration(cachedWindowsExecutableExtensionsSettingId); + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(cachedWindowsExecutableExtensionsSettingId)) { + cachedWindowsExecutableExtensions = vscode.workspace.getConfiguration(cachedWindowsExecutableExtensionsSettingId); + cachedAvailableCommands = undefined; + cachedAvailableCommandsPath = undefined; + } + })); + } +} /** * Adjusts the current working directory based on a given prefix if it is a folder. @@ -221,7 +233,7 @@ async function getCommandsInPath(env: { [key: string]: string | undefined } = pr const files = await vscode.workspace.fs.readDirectory(fileResource); for (const [file, fileType] of files) { const formattedPath = getFriendlyFilePath(vscode.Uri.joinPath(fileResource, file), pathSeparator); - if (!labels.has(file) && fileType !== vscode.FileType.Unknown && fileType !== vscode.FileType.Directory && await isExecutable(formattedPath)) { + if (!labels.has(file) && fileType !== vscode.FileType.Unknown && fileType !== vscode.FileType.Directory && await isExecutable(formattedPath), cachedWindowsExecutableExtensions) { executables.add({ label: file, path: formattedPath }); labels.add(file); } diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.ts index ac7e94ead6a..6b2dd061810 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.ts @@ -15,9 +15,29 @@ export const enum TerminalSuggestSettingId { RunOnEnter = 'terminal.integrated.suggest.runOnEnter', BuiltinCompletions = 'terminal.integrated.suggest.builtinCompletions', EnableExtensionCompletions = 'terminal.integrated.suggest.enableExtensionCompletions', + WindowsExecutableExtensions = 'terminal.integrated.suggest.windowsExecutableExtensions', Providers = 'terminal.integrated.suggest.providers', } +export const windowsDefaultExecutableExtensions: string[] = [ + 'exe', // Executable file + 'bat', // Batch file + 'cmd', // Command script + 'com', // Command file + + 'msi', // Windows Installer package + + 'ps1', // PowerShell script + + 'vbs', // VBScript file + 'js', // JScript file + 'jar', // Java Archive (requires Java runtime) + 'py', // Python script (requires Python interpreter) + 'rb', // Ruby script (requires Ruby interpreter) + 'pl', // Perl script (requires Perl interpreter) + 'sh', // Shell script (via WSL or third-party tools) +]; + export const terminalSuggestConfigSection = 'terminal.integrated.suggest'; export interface ITerminalSuggestConfiguration { @@ -106,4 +126,15 @@ export const terminalSuggestConfiguration: IStringDictionary `- ${extension}`).join('\n'), + ), + type: 'object', + default: {}, + tags: ['experimental'], + } }; + +