check if windows file is executable (#238142)

fix #237596
This commit is contained in:
Megan Rogge
2025-01-17 11:20:10 -06:00
committed by GitHub
parent bb655894c2
commit f1b4bb8d76
4 changed files with 58 additions and 26 deletions

View File

@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as os from 'os';
import * as fs from 'fs/promises';
import * as path from 'path';
import { ExecOptionsWithStringEncoding, execSync } from 'child_process';
@@ -11,7 +10,10 @@ import { upstreamSpecs } from './constants';
import codeCompletionSpec from './completions/code';
import cdSpec from './completions/cd';
import codeInsidersCompletionSpec from './completions/code-insiders';
import { osIsWindows } from './helpers/os';
import { isExecutable } from './helpers/executable';
const isWindows = osIsWindows();
let cachedAvailableCommandsPath: string | undefined;
let cachedAvailableCommands: Set<string> | undefined;
const cachedBuiltinCommands: Map<string, string[] | undefined> = new Map();
@@ -101,7 +103,7 @@ export async function activate(context: vscode.ExtensionContext) {
const result = await getCompletionItemsFromSpecs(availableSpecs, terminalContext, commands, prefix, terminal.shellIntegration?.cwd, token);
if (result.cwd && (result.filesRequested || result.foldersRequested)) {
// const cwd = resolveCwdFromPrefix(prefix, terminal.shellIntegration?.cwd) ?? terminal.shellIntegration?.cwd;
return new vscode.TerminalCompletionList(result.items, { filesRequested: result.filesRequested, foldersRequested: result.foldersRequested, cwd: result.cwd, pathSeparator: osIsWindows() ? '\\' : '/' });
return new vscode.TerminalCompletionList(result.items, { filesRequested: result.filesRequested, foldersRequested: result.foldersRequested, cwd: result.cwd, pathSeparator: isWindows ? '\\' : '/' });
}
return result.items;
}
@@ -123,7 +125,7 @@ export async function resolveCwdFromPrefix(prefix: string, currentCwd?: vscode.U
// Get the nearest folder path from the prefix. This ignores everything after the `/` as
// they are what triggers changes in the directory.
let lastSlashIndex: number;
if (osIsWindows()) {
if (isWindows) {
// TODO: This support is very basic, ideally the slashes supported would depend upon the
// shell type. For example git bash under Windows does not allow using \ as a path
// separator.
@@ -179,28 +181,10 @@ function createCompletionItem(cursorPosition: number, prefix: string, label: str
};
}
async function isExecutable(filePath: string): Promise<boolean> {
// Windows doesn't have the concept of an executable bit and running any
// file is possible. We considered using $PATHEXT here but since it's mostly
// there for legacy reasons and it would be easier and more intuitive to add
// a setting if needed instead.
if (osIsWindows()) {
return true;
}
try {
const stats = await fs.stat(filePath);
// On macOS/Linux, check if the executable bit is set
return (stats.mode & 0o100) !== 0;
} catch (error) {
// If the file does not exist or cannot be accessed, it's not executable
return false;
}
}
async function getCommandsInPath(env: { [key: string]: string | undefined } = process.env): Promise<Set<string> | undefined> {
// Get PATH value
let pathValue: string | undefined;
if (osIsWindows()) {
if (isWindows) {
const caseSensitivePathKey = Object.keys(env).find(key => key.toLowerCase() === 'path');
if (caseSensitivePathKey) {
pathValue = env[caseSensitivePathKey];
@@ -218,7 +202,6 @@ async function getCommandsInPath(env: { [key: string]: string | undefined } = pr
}
// Extract executables from PATH
const isWindows = osIsWindows();
const paths = pathValue.split(isWindows ? ';' : ':');
const pathSeparator = isWindows ? '\\' : '/';
const executables = new Set<string>();
@@ -484,9 +467,7 @@ function getCompletionItemsFromArgs(args: Fig.SingleOrArray<Fig.Arg> | undefined
return { items, filesRequested, foldersRequested };
}
function osIsWindows(): boolean {
return os.platform() === 'win32';
}
function getFirstCommand(commandLine: string): string | undefined {
const wordsOnLine = commandLine.split(' ');