Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Manual resolution of deno command on Windows #367

Merged
merged 1 commit into from
Mar 14, 2021
Merged
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
62 changes: 61 additions & 1 deletion client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import {
import type { Settings } from "./interfaces";
import { DenoTextDocumentContentProvider, SCHEME } from "./content_provider";
import { DenoDebugConfigurationProvider } from "./debug_config_provider";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as vscode from "vscode";
import {
Executable,
Expand Down Expand Up @@ -78,7 +81,8 @@ export async function activate(
context: vscode.ExtensionContext,
): Promise<void> {
const command =
vscode.workspace.getConfiguration("deno").get<string>("path") || "deno";
vscode.workspace.getConfiguration("deno").get<string>("path") ||
await getDefaultDenoCommand();
Copy link
Member Author

@dsherret dsherret Mar 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: I think leading logical operators help break this up better (dprint default). The above almost looks like a separate statement.

const command =
  vscode.workspace.getConfiguration("deno").get<string>("path")
  || await getDefaultDenoCommand();

const run: Executable = {
command,
args: ["lsp"],
Expand Down Expand Up @@ -218,3 +222,59 @@ function createRegisterCommand(
);
};
}

function getDefaultDenoCommand() {
switch (os.platform()) {
case "win32":
return getDenoWindowsPath();
default:
return Promise.resolve("deno");
}

async function getDenoWindowsPath() {
// Adapted from https://github.com/npm/node-which/blob/master/which.js
// Within vscode it will do `require("child_process").spawn("deno")`,
// which will prioritize "deno.exe" on the path instead of a possible
// higher precedence non-exe executable. This is a problem because, for
// example, version managers may have a `deno.bat` shim on the path. To
// ensure the resolution of the `deno` command matches what occurs on the
// command line, attempt to manually resolve the file path (issue #361).
const denoCmd = "deno";
// deno-lint-ignore no-undef
const pathExtValue = process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM";
// deno-lint-ignore no-undef
const pathValue = process.env.PATH ?? "";
const pathExtItems = splitEnvValue(pathExtValue);
const pathFolderPaths = splitEnvValue(pathValue);

for (const pathFolderPath of pathFolderPaths) {
for (const pathExtItem of pathExtItems) {
const cmdFilePath = path.join(pathFolderPath, denoCmd + pathExtItem);
if (await fileExists(cmdFilePath)) {
return cmdFilePath;
}
}
}

// nothing found; return back command
return denoCmd;

function splitEnvValue(value: string) {
return value
.split(";")
.map((item) => item.trim())
.filter((item) => item.length > 0);
}
}

function fileExists(executableFilePath: string) {
return new Promise((resolve) => {
fs.stat(executableFilePath, (err, stat) => {
resolve(err == null && stat.isFile());
});
}).catch(() => {
// ignore all errors
return false;
});
}
}