migrate from fs to vscode.FileSystem API

This commit is contained in:
wxb1ank 2021-05-23 22:37:10 -04:00
parent 3ca7f61a8d
commit 0448b73646
3 changed files with 35 additions and 44 deletions

View file

@ -1,7 +1,5 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as path from "path";
import * as os from "os"; import * as os from "os";
import { promises as fs, PathLike } from "fs";
import * as commands from './commands'; import * as commands from './commands';
import { activateInlayHints } from './inlay_hints'; import { activateInlayHints } from './inlay_hints';
@ -222,7 +220,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
const artifact = latestNightlyRelease.assets.find(artifact => artifact.name === "rust-analyzer.vsix"); const artifact = latestNightlyRelease.assets.find(artifact => artifact.name === "rust-analyzer.vsix");
assert(!!artifact, `Bad release: ${JSON.stringify(latestNightlyRelease)}`); assert(!!artifact, `Bad release: ${JSON.stringify(latestNightlyRelease)}`);
const dest = path.join(config.globalStorageUri.path, "rust-analyzer.vsix"); const dest = vscode.Uri.joinPath(config.globalStorageUri, "rust-analyzer.vsix");
await downloadWithRetryDialog(state, async () => { await downloadWithRetryDialog(state, async () => {
await download({ await download({
@ -233,8 +231,8 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
}); });
}); });
await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); await vscode.commands.executeCommand("workbench.extensions.installExtension", dest);
await fs.unlink(dest); await vscode.workspace.fs.delete(dest);
await state.updateNightlyReleaseId(latestNightlyRelease.id); await state.updateNightlyReleaseId(latestNightlyRelease.id);
await state.updateLastCheck(now); await state.updateLastCheck(now);
@ -259,7 +257,7 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise<
return path; return path;
} }
async function patchelf(dest: PathLike): Promise<void> { async function patchelf(dest: vscode.Uri): Promise<void> {
await vscode.window.withProgress( await vscode.window.withProgress(
{ {
location: vscode.ProgressLocation.Notification, location: vscode.ProgressLocation.Notification,
@ -279,11 +277,11 @@ async function patchelf(dest: PathLike): Promise<void> {
''; '';
} }
`; `;
const origFile = dest + "-orig"; const origFile = vscode.Uri.file(dest.path + "-orig");
await fs.rename(dest, origFile); await vscode.workspace.fs.rename(dest, origFile);
progress.report({ message: "Patching executable", increment: 20 }); progress.report({ message: "Patching executable", increment: 20 });
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
const handle = exec(`nix-build -E - --argstr srcStr '${origFile}' -o '${dest}'`, const handle = exec(`nix-build -E - --argstr srcStr '${origFile.path}' -o '${dest.path}'`,
(err, stdout, stderr) => { (err, stdout, stderr) => {
if (err != null) { if (err != null) {
reject(Error(stderr)); reject(Error(stderr));
@ -294,7 +292,7 @@ async function patchelf(dest: PathLike): Promise<void> {
handle.stdin?.write(expression); handle.stdin?.write(expression);
handle.stdin?.end(); handle.stdin?.end();
}); });
await fs.unlink(origFile); await vscode.workspace.fs.delete(origFile);
} }
); );
} }
@ -334,20 +332,20 @@ async function getServer(config: Config, state: PersistentState): Promise<string
platform = "x86_64-unknown-linux-musl"; platform = "x86_64-unknown-linux-musl";
} }
const ext = platform.indexOf("-windows-") !== -1 ? ".exe" : ""; const ext = platform.indexOf("-windows-") !== -1 ? ".exe" : "";
const dest = path.join(config.globalStorageUri.path, `rust-analyzer-${platform}${ext}`); const dest = vscode.Uri.joinPath(config.globalStorageUri, `rust-analyzer-${platform}${ext}`);
const exists = await fs.stat(dest).then(() => true, () => false); const exists = await vscode.workspace.fs.stat(dest).then(() => true, () => false);
if (!exists) { if (!exists) {
await state.updateServerVersion(undefined); await state.updateServerVersion(undefined);
} }
if (state.serverVersion === config.package.version) return dest; if (state.serverVersion === config.package.version) return dest.path;
if (config.askBeforeDownload) { if (config.askBeforeDownload) {
const userResponse = await vscode.window.showInformationMessage( const userResponse = await vscode.window.showInformationMessage(
`Language server version ${config.package.version} for rust-analyzer is not installed.`, `Language server version ${config.package.version} for rust-analyzer is not installed.`,
"Download now" "Download now"
); );
if (userResponse !== "Download now") return dest; if (userResponse !== "Download now") return dest.path;
} }
const releaseTag = config.package.releaseTag; const releaseTag = config.package.releaseTag;
@ -374,7 +372,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string
} }
await state.updateServerVersion(config.package.version); await state.updateServerVersion(config.package.version);
return dest; return dest.path;
} }
function serverPath(config: Config): string | null { function serverPath(config: Config): string | null {
@ -383,7 +381,7 @@ function serverPath(config: Config): string | null {
async function isNixOs(): Promise<boolean> { async function isNixOs(): Promise<boolean> {
try { try {
const contents = await fs.readFile("/etc/os-release"); const contents = (await vscode.workspace.fs.readFile(vscode.Uri.file("/etc/os-release"))).toString();
return contents.indexOf("ID=nixos") !== -1; return contents.indexOf("ID=nixos") !== -1;
} catch (e) { } catch (e) {
return false; return false;

View file

@ -73,14 +73,14 @@ export interface GithubRelease {
assets: Array<{ assets: Array<{
name: string; name: string;
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
browser_download_url: string; browser_download_url: vscode.Uri;
}>; }>;
} }
interface DownloadOpts { interface DownloadOpts {
progressTitle: string; progressTitle: string;
url: string; url: vscode.Uri;
dest: string; dest: vscode.Uri;
mode?: number; mode?: number;
gunzip?: boolean; gunzip?: boolean;
httpProxy?: string; httpProxy?: string;
@ -90,9 +90,9 @@ export async function download(opts: DownloadOpts) {
// Put artifact into a temporary file (in the same dir for simplicity) // Put artifact into a temporary file (in the same dir for simplicity)
// to prevent partially downloaded files when user kills vscode // to prevent partially downloaded files when user kills vscode
// This also avoids overwriting running executables // This also avoids overwriting running executables
const dest = path.parse(opts.dest);
const randomHex = crypto.randomBytes(5).toString("hex"); const randomHex = crypto.randomBytes(5).toString("hex");
const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`); const rawDest = path.parse(opts.dest.path);
const tempFilePath = vscode.Uri.joinPath(vscode.Uri.file(rawDest.dir), `${rawDest.name}${randomHex}`);
await vscode.window.withProgress( await vscode.window.withProgress(
{ {
@ -102,7 +102,7 @@ export async function download(opts: DownloadOpts) {
}, },
async (progress, _cancellationToken) => { async (progress, _cancellationToken) => {
let lastPercentage = 0; let lastPercentage = 0;
await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, opts.httpProxy, (readBytes, totalBytes) => { await downloadFile(opts.url, tempFilePath, opts.mode, !!opts.gunzip, opts.httpProxy, (readBytes, totalBytes) => {
const newPercentage = Math.round((readBytes / totalBytes) * 100); const newPercentage = Math.round((readBytes / totalBytes) * 100);
if (newPercentage !== lastPercentage) { if (newPercentage !== lastPercentage) {
progress.report({ progress.report({
@ -116,12 +116,12 @@ export async function download(opts: DownloadOpts) {
} }
); );
await fs.promises.rename(tempFile, opts.dest); await vscode.workspace.fs.rename(tempFilePath, opts.dest);
} }
async function downloadFile( async function downloadFile(
url: string, url: vscode.Uri,
destFilePath: fs.PathLike, destFilePath: vscode.Uri,
mode: number | undefined, mode: number | undefined,
gunzip: boolean, gunzip: boolean,
httpProxy: string | null | undefined, httpProxy: string | null | undefined,
@ -129,15 +129,15 @@ async function downloadFile(
): Promise<void> { ): Promise<void> {
const res = await (() => { const res = await (() => {
if (httpProxy) { if (httpProxy) {
log.debug(`Downloading ${url} via proxy: ${httpProxy}`); log.debug(`Downloading ${url.path} via proxy: ${httpProxy}`);
return fetch(url, { agent: new HttpsProxyAgent(httpProxy) }); return fetch(url.path, { agent: new HttpsProxyAgent(httpProxy) });
} }
return fetch(url); return fetch(url.path);
})(); })();
if (!res.ok) { if (!res.ok) {
log.error("Error", res.status, "while downloading file from", url); log.error("Error", res.status, "while downloading file from", url.path);
log.error({ body: await res.text(), headers: res.headers }); log.error({ body: await res.text(), headers: res.headers });
throw new Error(`Got response ${res.status} when trying to download a file.`); throw new Error(`Got response ${res.status} when trying to download a file.`);
@ -146,7 +146,7 @@ async function downloadFile(
const totalBytes = Number(res.headers.get('content-length')); const totalBytes = Number(res.headers.get('content-length'));
assert(!Number.isNaN(totalBytes), "Sanity check of content-length protocol"); assert(!Number.isNaN(totalBytes), "Sanity check of content-length protocol");
log.debug("Downloading file of", totalBytes, "bytes size from", url, "to", destFilePath); log.debug("Downloading file of", totalBytes, "bytes size from", url.path, "to", destFilePath.path);
let readBytes = 0; let readBytes = 0;
res.body.on("data", (chunk: Buffer) => { res.body.on("data", (chunk: Buffer) => {
@ -154,7 +154,7 @@ async function downloadFile(
onProgress(readBytes, totalBytes); onProgress(readBytes, totalBytes);
}); });
const destFileStream = fs.createWriteStream(destFilePath, { mode }); const destFileStream = fs.createWriteStream(destFilePath.path, { mode });
const srcStream = gunzip ? res.body.pipe(zlib.createGunzip()) : res.body; const srcStream = gunzip ? res.body.pipe(zlib.createGunzip()) : res.body;
await pipeline(srcStream, destFileStream); await pipeline(srcStream, destFileStream);

View file

@ -1,9 +1,8 @@
import * as cp from 'child_process'; import * as cp from 'child_process';
import * as os from 'os'; import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import * as fs from 'fs';
import * as readline from 'readline'; import * as readline from 'readline';
import { OutputChannel } from 'vscode'; import * as vscode from 'vscode';
import { execute, log, memoize } from './util'; import { execute, log, memoize } from './util';
interface CompilationArtifact { interface CompilationArtifact {
@ -19,7 +18,7 @@ export interface ArtifactSpec {
} }
export class Cargo { export class Cargo {
constructor(readonly rootFolder: string, readonly output: OutputChannel) { } constructor(readonly rootFolder: string, readonly output: vscode.OutputChannel) { }
// Made public for testing purposes // Made public for testing purposes
static artifactSpec(args: readonly string[]): ArtifactSpec { static artifactSpec(args: readonly string[]): ArtifactSpec {
@ -158,9 +157,9 @@ export const getPathForExecutable = memoize(
try { try {
// hmm, `os.homedir()` seems to be infallible // hmm, `os.homedir()` seems to be infallible
// it is not mentioned in docs and cannot be infered by the type signature... // it is not mentioned in docs and cannot be infered by the type signature...
const standardPath = path.join(os.homedir(), ".cargo", "bin", executableName); const standardPath = vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".cargo", "bin", executableName);
if (isFile(standardPath)) return standardPath; if (isFile(standardPath.path)) return standardPath.path;
} catch (err) { } catch (err) {
log.error("Failed to read the fs info", err); log.error("Failed to read the fs info", err);
} }
@ -181,12 +180,6 @@ function lookupInPath(exec: string): boolean {
return candidates.some(isFile); return candidates.some(isFile);
} }
function isFile(suspectPath: string): boolean { async function isFile(path: string): Promise<boolean> {
// It is not mentionned in docs, but `statSync()` throws an error when return ((await vscode.workspace.fs.stat(vscode.Uri.file(path))).type & vscode.FileType.File) != 0;
// the path doesn't exist
try {
return fs.statSync(suspectPath).isFile();
} catch {
return false;
}
} }