Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 43 additions & 3 deletions editors/vscode/client/ConfigService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,30 @@ export class ConfigService implements IDisposable {
settingsBinary = this.removeWindowsLeadingSlash(settingsBinary);
}

return settingsBinary;
if (process.platform !== "win32" && settingsBinary.endsWith(".exe")) {
// on non-Windows, remove `.exe` extension if present
settingsBinary = settingsBinary.slice(0, -4);
}

try {
await workspace.fs.stat(Uri.file(settingsBinary));
return settingsBinary;
} catch {}

// on Windows, also check for `.exe` extension (bun uses `.exe` for its binaries)
if (process.platform === "win32") {
if (!settingsBinary.endsWith(".exe")) {
settingsBinary += ".exe";
}

try {
await workspace.fs.stat(Uri.file(settingsBinary));
return settingsBinary;
} catch {}
}

// no valid binary found
return undefined;
}

/**
Expand Down Expand Up @@ -178,15 +201,32 @@ export class ConfigService implements IDisposable {
// not found, continue to glob search
}

// on Windows, also check for `.exe` extension
if (process.platform === "win32") {
const binPathExe = `${binPath}.exe`;
try {
await workspace.fs.stat(Uri.file(binPathExe));
return binPathExe;
} catch {
// not found, continue to glob search
}
}

const cts = new CancellationTokenSource();
setTimeout(() => cts.cancel(), 10000); // cancel after 10 seconds

try {
// bun package manager uses `.exe` extension on Windows
// search for both with and without `.exe` extension
const extension = process.platform === "win32" ? "{,.exe}" : "";
// fallback: search with glob
// maybe use `tinyglobby` later for better performance, VSCode can be slow on globbing large projects.
const files = await workspace.findFiles(
// search up to 3 levels deep
new RelativePattern(workspacePath, `{*/,*/*,*/*/*}/node_modules/.bin/${binaryName}`),
// search up to 3 levels deep for the binary path
new RelativePattern(
workspacePath,
`{*/,*/*,*/*/*}/node_modules/.bin/${binaryName}${extension}`,
),
undefined,
1,
cts.token,
Expand Down
52 changes: 46 additions & 6 deletions editors/vscode/tests/unit/ConfigService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,62 @@ suite('ConfigService', () => {
return workspace_path;
};

const createWorkspaceFolderFileUri = async (relativePath: string) => {
const workspace_path = getWorkspaceFolderPlatformSafe();
const path = process.platform === 'win32'
? `${workspace_path}\\${relativePath}`
: `${workspace_path}/${relativePath}`;

await workspace.fs.writeFile(
WORKSPACE_FOLDER.uri.with({ path }),
new Uint8Array(),
);
}

const deleteWorkspaceFolderFileUri = async (relativePath: string) => {
const workspace_path = getWorkspaceFolderPlatformSafe();
const path = process.platform === 'win32'
? `${workspace_path}\\${relativePath}`
: `${workspace_path}/${relativePath}`;

await workspace.fs.delete(
WORKSPACE_FOLDER.uri.with({ path }),
);
}

suite('getOxfmtServerBinPath', () => {
test('resolves relative server path with workspace folder', async () => {
const service = new ConfigService();
const workspace_path = getWorkspaceFolderPlatformSafe();
const nonDefinedServerPath = await service.getOxfmtServerBinPath();

await createWorkspaceFolderFileUri('absolute/oxfmt');
await createWorkspaceFolderFileUri('relative/oxfmt');

strictEqual(nonDefinedServerPath, undefined);

await conf.update('path.oxfmt', '/absolute/oxfmt');
await conf.update('path.oxfmt', `${workspace_path}/absolute/oxfmt`);
const absoluteServerPath = await service.getOxfmtServerBinPath();

strictEqual(absoluteServerPath, '/absolute/oxfmt');
strictEqual(absoluteServerPath, `${workspace_path}/absolute/oxfmt`);

await conf.update('path.oxfmt', './relative/oxfmt');
const relativeServerPath = await service.getOxfmtServerBinPath();

const workspace_path = getWorkspaceFolderPlatformSafe();
strictEqual(relativeServerPath, `${workspace_path}/relative/oxfmt`);

await deleteWorkspaceFolderFileUri('absolute/oxfmt');
await deleteWorkspaceFolderFileUri('relative/oxfmt');
});

test('returns undefined for unsafe server path', async () => {
await createWorkspaceFolderFileUri('../unsafe/oxfmt');
const service = new ConfigService();
await conf.update('path.oxfmt', '../unsafe/oxfmt');
const unsafeServerPath = await service.getOxfmtServerBinPath();

strictEqual(unsafeServerPath, undefined);
await deleteWorkspaceFolderFileUri('../unsafe/oxfmt');
});

test('returns backslashes path on Windows', async () => {
Expand All @@ -74,27 +105,36 @@ suite('ConfigService', () => {
test('resolves relative server path with workspace folder', async () => {
const service = new ConfigService();
const nonDefinedServerPath = await service.getOxlintServerBinPath();
const workspace_path = getWorkspaceFolderPlatformSafe();

await createWorkspaceFolderFileUri('absolute/oxlint');
await createWorkspaceFolderFileUri('relative/oxlint');

strictEqual(nonDefinedServerPath, undefined);

await conf.update('path.oxlint', '/absolute/oxlint');
await conf.update('path.oxlint', `${workspace_path}/absolute/oxlint`);
const absoluteServerPath = await service.getOxlintServerBinPath();

strictEqual(absoluteServerPath, '/absolute/oxlint');
strictEqual(absoluteServerPath, `${workspace_path}/absolute/oxlint`);

await conf.update('path.oxlint', './relative/oxlint');
const relativeServerPath = await service.getOxlintServerBinPath();

const workspace_path = getWorkspaceFolderPlatformSafe();
strictEqual(relativeServerPath, `${workspace_path}/relative/oxlint`);


await deleteWorkspaceFolderFileUri('absolute/oxlint');
await deleteWorkspaceFolderFileUri('relative/oxlint');
});

test('returns undefined for unsafe server path', async () => {
await createWorkspaceFolderFileUri('../unsafe/oxlint');
const service = new ConfigService();
await conf.update('path.oxlint', '../unsafe/oxlint');
const unsafeServerPath = await service.getOxlintServerBinPath();

strictEqual(unsafeServerPath, undefined);
await deleteWorkspaceFolderFileUri('../unsafe/oxlint');
});

test('returns backslashes path on Windows', async () => {
Expand Down
Loading