aboutsummaryrefslogtreecommitdiff
path: root/editors/code
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code')
-rw-r--r--editors/code/package.json6
-rw-r--r--editors/code/src/config.ts4
-rw-r--r--editors/code/src/main.ts34
-rw-r--r--editors/code/src/net.ts32
-rw-r--r--editors/code/src/toolchain.ts21
5 files changed, 49 insertions, 48 deletions
diff --git a/editors/code/package.json b/editors/code/package.json
index 9565c6edb..1fac700be 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -313,7 +313,7 @@
313 }, 313 },
314 "rust-analyzer.updates.askBeforeDownload": { 314 "rust-analyzer.updates.askBeforeDownload": {
315 "type": "boolean", 315 "type": "boolean",
316 "default": true, 316 "default": false,
317 "description": "Whether to ask for permission before downloading any files from the Internet." 317 "description": "Whether to ask for permission before downloading any files from the Internet."
318 }, 318 },
319 "rust-analyzer.server.path": { 319 "rust-analyzer.server.path": {
@@ -389,7 +389,7 @@
389 "default": {}, 389 "default": {},
390 "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`" 390 "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`"
391 }, 391 },
392 "$generated-start": false, 392 "$generated-start": {},
393 "rust-analyzer.assist.importGranularity": { 393 "rust-analyzer.assist.importGranularity": {
394 "markdownDescription": "How imports should be grouped into use statements.", 394 "markdownDescription": "How imports should be grouped into use statements.",
395 "default": "crate", 395 "default": "crate",
@@ -841,7 +841,7 @@
841 "Search for all symbols kinds" 841 "Search for all symbols kinds"
842 ] 842 ]
843 }, 843 },
844 "$generated-end": false 844 "$generated-end": {}
845 } 845 }
846 }, 846 },
847 "problemPatterns": [ 847 "problemPatterns": [
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 1f1fe59a4..2277eeb7e 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -31,10 +31,10 @@ export class Config {
31 enableProposedApi: boolean | undefined; 31 enableProposedApi: boolean | undefined;
32 } = vscode.extensions.getExtension(this.extensionId)!.packageJSON; 32 } = vscode.extensions.getExtension(this.extensionId)!.packageJSON;
33 33
34 readonly globalStoragePath: string; 34 readonly globalStorageUri: vscode.Uri;
35 35
36 constructor(ctx: vscode.ExtensionContext) { 36 constructor(ctx: vscode.ExtensionContext) {
37 this.globalStoragePath = ctx.globalStorageUri.fsPath; 37 this.globalStorageUri = ctx.globalStorageUri;
38 vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, ctx.subscriptions); 38 vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, ctx.subscriptions);
39 this.refreshLogging(); 39 this.refreshLogging();
40 } 40 }
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 74ee28d24..15f2151ad 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -1,7 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as path from "path";
3import * as os from "os"; 2import * as os from "os";
4import { promises as fs, PathLike } from "fs";
5 3
6import * as commands from './commands'; 4import * as commands from './commands';
7import { activateInlayHints } from './inlay_hints'; 5import { activateInlayHints } from './inlay_hints';
@@ -160,7 +158,7 @@ export async function deactivate() {
160} 158}
161 159
162async function bootstrap(config: Config, state: PersistentState): Promise<string> { 160async function bootstrap(config: Config, state: PersistentState): Promise<string> {
163 await fs.mkdir(config.globalStoragePath, { recursive: true }); 161 await vscode.workspace.fs.createDirectory(config.globalStorageUri).then();
164 162
165 if (!config.currentExtensionIsNightly) { 163 if (!config.currentExtensionIsNightly) {
166 await state.updateNightlyReleaseId(undefined); 164 await state.updateNightlyReleaseId(undefined);
@@ -222,7 +220,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
222 const artifact = latestNightlyRelease.assets.find(artifact => artifact.name === "rust-analyzer.vsix"); 220 const artifact = latestNightlyRelease.assets.find(artifact => artifact.name === "rust-analyzer.vsix");
223 assert(!!artifact, `Bad release: ${JSON.stringify(latestNightlyRelease)}`); 221 assert(!!artifact, `Bad release: ${JSON.stringify(latestNightlyRelease)}`);
224 222
225 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix"); 223 const dest = vscode.Uri.joinPath(config.globalStorageUri, "rust-analyzer.vsix");
226 224
227 await downloadWithRetryDialog(state, async () => { 225 await downloadWithRetryDialog(state, async () => {
228 await download({ 226 await download({
@@ -233,8 +231,8 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
233 }); 231 });
234 }); 232 });
235 233
236 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); 234 await vscode.commands.executeCommand("workbench.extensions.installExtension", dest);
237 await fs.unlink(dest); 235 await vscode.workspace.fs.delete(dest);
238 236
239 await state.updateNightlyReleaseId(latestNightlyRelease.id); 237 await state.updateNightlyReleaseId(latestNightlyRelease.id);
240 await state.updateLastCheck(now); 238 await state.updateLastCheck(now);
@@ -259,7 +257,7 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise<
259 return path; 257 return path;
260} 258}
261 259
262async function patchelf(dest: PathLike): Promise<void> { 260async function patchelf(dest: vscode.Uri): Promise<void> {
263 await vscode.window.withProgress( 261 await vscode.window.withProgress(
264 { 262 {
265 location: vscode.ProgressLocation.Notification, 263 location: vscode.ProgressLocation.Notification,
@@ -279,11 +277,11 @@ async function patchelf(dest: PathLike): Promise<void> {
279 ''; 277 '';
280 } 278 }
281 `; 279 `;
282 const origFile = dest + "-orig"; 280 const origFile = vscode.Uri.file(dest.fsPath + "-orig");
283 await fs.rename(dest, origFile); 281 await vscode.workspace.fs.rename(dest, origFile);
284 progress.report({ message: "Patching executable", increment: 20 }); 282 progress.report({ message: "Patching executable", increment: 20 });
285 await new Promise((resolve, reject) => { 283 await new Promise((resolve, reject) => {
286 const handle = exec(`nix-build -E - --argstr srcStr '${origFile}' -o '${dest}'`, 284 const handle = exec(`nix-build -E - --argstr srcStr '${origFile.fsPath}' -o '${dest.fsPath}'`,
287 (err, stdout, stderr) => { 285 (err, stdout, stderr) => {
288 if (err != null) { 286 if (err != null) {
289 reject(Error(stderr)); 287 reject(Error(stderr));
@@ -294,7 +292,7 @@ async function patchelf(dest: PathLike): Promise<void> {
294 handle.stdin?.write(expression); 292 handle.stdin?.write(expression);
295 handle.stdin?.end(); 293 handle.stdin?.end();
296 }); 294 });
297 await fs.unlink(origFile); 295 await vscode.workspace.fs.delete(origFile);
298 } 296 }
299 ); 297 );
300} 298}
@@ -334,20 +332,20 @@ async function getServer(config: Config, state: PersistentState): Promise<string
334 platform = "x86_64-unknown-linux-musl"; 332 platform = "x86_64-unknown-linux-musl";
335 } 333 }
336 const ext = platform.indexOf("-windows-") !== -1 ? ".exe" : ""; 334 const ext = platform.indexOf("-windows-") !== -1 ? ".exe" : "";
337 const dest = path.join(config.globalStoragePath, `rust-analyzer-${platform}${ext}`); 335 const dest = vscode.Uri.joinPath(config.globalStorageUri, `rust-analyzer-${platform}${ext}`);
338 const exists = await fs.stat(dest).then(() => true, () => false); 336 const exists = await vscode.workspace.fs.stat(dest).then(() => true, () => false);
339 if (!exists) { 337 if (!exists) {
340 await state.updateServerVersion(undefined); 338 await state.updateServerVersion(undefined);
341 } 339 }
342 340
343 if (state.serverVersion === config.package.version) return dest; 341 if (state.serverVersion === config.package.version) return dest.fsPath;
344 342
345 if (config.askBeforeDownload) { 343 if (config.askBeforeDownload) {
346 const userResponse = await vscode.window.showInformationMessage( 344 const userResponse = await vscode.window.showInformationMessage(
347 `Language server version ${config.package.version} for rust-analyzer is not installed.`, 345 `Language server version ${config.package.version} for rust-analyzer is not installed.`,
348 "Download now" 346 "Download now"
349 ); 347 );
350 if (userResponse !== "Download now") return dest; 348 if (userResponse !== "Download now") return dest.fsPath;
351 } 349 }
352 350
353 const releaseTag = config.package.releaseTag; 351 const releaseTag = config.package.releaseTag;
@@ -374,7 +372,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string
374 } 372 }
375 373
376 await state.updateServerVersion(config.package.version); 374 await state.updateServerVersion(config.package.version);
377 return dest; 375 return dest.fsPath;
378} 376}
379 377
380function serverPath(config: Config): string | null { 378function serverPath(config: Config): string | null {
@@ -383,9 +381,9 @@ function serverPath(config: Config): string | null {
383 381
384async function isNixOs(): Promise<boolean> { 382async function isNixOs(): Promise<boolean> {
385 try { 383 try {
386 const contents = await fs.readFile("/etc/os-release"); 384 const contents = (await vscode.workspace.fs.readFile(vscode.Uri.file("/etc/os-release"))).toString();
387 return contents.indexOf("ID=nixos") !== -1; 385 return contents.indexOf("ID=nixos") !== -1;
388 } catch (e) { 386 } catch {
389 return false; 387 return false;
390 } 388 }
391} 389}
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index 07ebc615c..722dab756 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -73,14 +73,14 @@ export interface GithubRelease {
73 assets: Array<{ 73 assets: Array<{
74 name: string; 74 name: string;
75 // eslint-disable-next-line camelcase 75 // eslint-disable-next-line camelcase
76 browser_download_url: string; 76 browser_download_url: vscode.Uri;
77 }>; 77 }>;
78} 78}
79 79
80interface DownloadOpts { 80interface DownloadOpts {
81 progressTitle: string; 81 progressTitle: string;
82 url: string; 82 url: vscode.Uri;
83 dest: string; 83 dest: vscode.Uri;
84 mode?: number; 84 mode?: number;
85 gunzip?: boolean; 85 gunzip?: boolean;
86 httpProxy?: string; 86 httpProxy?: string;
@@ -90,9 +90,9 @@ export async function download(opts: DownloadOpts) {
90 // Put artifact into a temporary file (in the same dir for simplicity) 90 // Put artifact into a temporary file (in the same dir for simplicity)
91 // to prevent partially downloaded files when user kills vscode 91 // to prevent partially downloaded files when user kills vscode
92 // This also avoids overwriting running executables 92 // This also avoids overwriting running executables
93 const dest = path.parse(opts.dest);
94 const randomHex = crypto.randomBytes(5).toString("hex"); 93 const randomHex = crypto.randomBytes(5).toString("hex");
95 const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`); 94 const rawDest = path.parse(opts.dest.fsPath);
95 const tempFilePath = vscode.Uri.joinPath(vscode.Uri.file(rawDest.dir), `${rawDest.name}${randomHex}`);
96 96
97 await vscode.window.withProgress( 97 await vscode.window.withProgress(
98 { 98 {
@@ -102,7 +102,7 @@ export async function download(opts: DownloadOpts) {
102 }, 102 },
103 async (progress, _cancellationToken) => { 103 async (progress, _cancellationToken) => {
104 let lastPercentage = 0; 104 let lastPercentage = 0;
105 await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, opts.httpProxy, (readBytes, totalBytes) => { 105 await downloadFile(opts.url, tempFilePath, opts.mode, !!opts.gunzip, opts.httpProxy, (readBytes, totalBytes) => {
106 const newPercentage = Math.round((readBytes / totalBytes) * 100); 106 const newPercentage = Math.round((readBytes / totalBytes) * 100);
107 if (newPercentage !== lastPercentage) { 107 if (newPercentage !== lastPercentage) {
108 progress.report({ 108 progress.report({
@@ -116,28 +116,30 @@ export async function download(opts: DownloadOpts) {
116 } 116 }
117 ); 117 );
118 118
119 await fs.promises.rename(tempFile, opts.dest); 119 await vscode.workspace.fs.rename(tempFilePath, opts.dest, { overwrite: true });
120} 120}
121 121
122async function downloadFile( 122async function downloadFile(
123 url: string, 123 url: vscode.Uri,
124 destFilePath: fs.PathLike, 124 destFilePath: vscode.Uri,
125 mode: number | undefined, 125 mode: number | undefined,
126 gunzip: boolean, 126 gunzip: boolean,
127 httpProxy: string | null | undefined, 127 httpProxy: string | null | undefined,
128 onProgress: (readBytes: number, totalBytes: number) => void 128 onProgress: (readBytes: number, totalBytes: number) => void
129): Promise<void> { 129): Promise<void> {
130 const urlString = url.toString();
131
130 const res = await (() => { 132 const res = await (() => {
131 if (httpProxy) { 133 if (httpProxy) {
132 log.debug(`Downloading ${url} via proxy: ${httpProxy}`); 134 log.debug(`Downloading ${urlString} via proxy: ${httpProxy}`);
133 return fetch(url, { agent: new HttpsProxyAgent(httpProxy) }); 135 return fetch(urlString, { agent: new HttpsProxyAgent(httpProxy) });
134 } 136 }
135 137
136 return fetch(url); 138 return fetch(urlString);
137 })(); 139 })();
138 140
139 if (!res.ok) { 141 if (!res.ok) {
140 log.error("Error", res.status, "while downloading file from", url); 142 log.error("Error", res.status, "while downloading file from", urlString);
141 log.error({ body: await res.text(), headers: res.headers }); 143 log.error({ body: await res.text(), headers: res.headers });
142 144
143 throw new Error(`Got response ${res.status} when trying to download a file.`); 145 throw new Error(`Got response ${res.status} when trying to download a file.`);
@@ -146,7 +148,7 @@ async function downloadFile(
146 const totalBytes = Number(res.headers.get('content-length')); 148 const totalBytes = Number(res.headers.get('content-length'));
147 assert(!Number.isNaN(totalBytes), "Sanity check of content-length protocol"); 149 assert(!Number.isNaN(totalBytes), "Sanity check of content-length protocol");
148 150
149 log.debug("Downloading file of", totalBytes, "bytes size from", url, "to", destFilePath); 151 log.debug("Downloading file of", totalBytes, "bytes size from", urlString, "to", destFilePath.fsPath);
150 152
151 let readBytes = 0; 153 let readBytes = 0;
152 res.body.on("data", (chunk: Buffer) => { 154 res.body.on("data", (chunk: Buffer) => {
@@ -154,7 +156,7 @@ async function downloadFile(
154 onProgress(readBytes, totalBytes); 156 onProgress(readBytes, totalBytes);
155 }); 157 });
156 158
157 const destFileStream = fs.createWriteStream(destFilePath, { mode }); 159 const destFileStream = fs.createWriteStream(destFilePath.fsPath, { mode });
158 const srcStream = gunzip ? res.body.pipe(zlib.createGunzip()) : res.body; 160 const srcStream = gunzip ? res.body.pipe(zlib.createGunzip()) : res.body;
159 161
160 await pipeline(srcStream, destFileStream); 162 await pipeline(srcStream, destFileStream);
diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts
index 68826c478..355dd76fe 100644
--- a/editors/code/src/toolchain.ts
+++ b/editors/code/src/toolchain.ts
@@ -1,9 +1,8 @@
1import * as cp from 'child_process'; 1import * as cp from 'child_process';
2import * as os from 'os'; 2import * as os from 'os';
3import * as path from 'path'; 3import * as path from 'path';
4import * as fs from 'fs';
5import * as readline from 'readline'; 4import * as readline from 'readline';
6import { OutputChannel } from 'vscode'; 5import * as vscode from 'vscode';
7import { execute, log, memoize } from './util'; 6import { execute, log, memoize } from './util';
8 7
9interface CompilationArtifact { 8interface CompilationArtifact {
@@ -19,7 +18,7 @@ export interface ArtifactSpec {
19} 18}
20 19
21export class Cargo { 20export class Cargo {
22 constructor(readonly rootFolder: string, readonly output: OutputChannel) { } 21 constructor(readonly rootFolder: string, readonly output: vscode.OutputChannel) { }
23 22
24 // Made public for testing purposes 23 // Made public for testing purposes
25 static artifactSpec(args: readonly string[]): ArtifactSpec { 24 static artifactSpec(args: readonly string[]): ArtifactSpec {
@@ -158,9 +157,9 @@ export const getPathForExecutable = memoize(
158 try { 157 try {
159 // hmm, `os.homedir()` seems to be infallible 158 // hmm, `os.homedir()` seems to be infallible
160 // it is not mentioned in docs and cannot be infered by the type signature... 159 // it is not mentioned in docs and cannot be infered by the type signature...
161 const standardPath = path.join(os.homedir(), ".cargo", "bin", executableName); 160 const standardPath = vscode.Uri.joinPath(vscode.Uri.file(os.homedir()), ".cargo", "bin", executableName);
162 161
163 if (isFile(standardPath)) return standardPath; 162 if (isFileAtUri(standardPath)) return standardPath.fsPath;
164 } catch (err) { 163 } catch (err) {
165 log.error("Failed to read the fs info", err); 164 log.error("Failed to read the fs info", err);
166 } 165 }
@@ -178,14 +177,16 @@ function lookupInPath(exec: string): boolean {
178 : [candidate]; 177 : [candidate];
179 }); 178 });
180 179
181 return candidates.some(isFile); 180 return candidates.some(isFileAtPath);
182} 181}
183 182
184function isFile(suspectPath: string): boolean { 183async function isFileAtPath(path: string): Promise<boolean> {
185 // It is not mentionned in docs, but `statSync()` throws an error when 184 return isFileAtUri(vscode.Uri.file(path));
186 // the path doesn't exist 185}
186
187async function isFileAtUri(uri: vscode.Uri): Promise<boolean> {
187 try { 188 try {
188 return fs.statSync(suspectPath).isFile(); 189 return ((await vscode.workspace.fs.stat(uri)).type & vscode.FileType.File) !== 0;
189 } catch { 190 } catch {
190 return false; 191 return false;
191 } 192 }