aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src')
-rw-r--r--editors/code/src/commands.ts14
-rw-r--r--editors/code/src/config.ts5
-rw-r--r--editors/code/src/debug.ts2
-rw-r--r--editors/code/src/main.ts34
-rw-r--r--editors/code/src/net.ts39
-rw-r--r--editors/code/src/util.ts1
6 files changed, 74 insertions, 21 deletions
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 3e9c3aa0e..48a25495f 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -353,6 +353,20 @@ export function applyActionGroup(_ctx: Ctx): Cmd {
353 }; 353 };
354} 354}
355 355
356export function gotoLocation(ctx: Ctx): Cmd {
357 return async (locationLink: lc.LocationLink) => {
358 const client = ctx.client;
359 if (client) {
360 const uri = client.protocol2CodeConverter.asUri(locationLink.targetUri);
361 let range = client.protocol2CodeConverter.asRange(locationLink.targetSelectionRange);
362 // collapse the range to a cursor position
363 range = range.with({ end: range.start });
364
365 await vscode.window.showTextDocument(uri, { selection: range });
366 }
367 };
368}
369
356export function resolveCodeAction(ctx: Ctx): Cmd { 370export function resolveCodeAction(ctx: Ctx): Cmd {
357 const client = ctx.client; 371 const client = ctx.client;
358 return async (params: ra.ResolveCodeActionParams) => { 372 return async (params: ra.ResolveCodeActionParams) => {
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index d8f0037d4..9591d4fe3 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -117,7 +117,7 @@ export class Config {
117 return { 117 return {
118 engine: this.get<string>("debug.engine"), 118 engine: this.get<string>("debug.engine"),
119 engineSettings: this.get<object>("debug.engineSettings"), 119 engineSettings: this.get<object>("debug.engineSettings"),
120 openUpDebugPane: this.get<boolean>("debug.openUpDebugPane"), 120 openDebugPane: this.get<boolean>("debug.openDebugPane"),
121 sourceFileMap: sourceFileMap 121 sourceFileMap: sourceFileMap
122 }; 122 };
123 } 123 }
@@ -135,6 +135,9 @@ export class Config {
135 return { 135 return {
136 enable: this.get<boolean>("hoverActions.enable"), 136 enable: this.get<boolean>("hoverActions.enable"),
137 implementations: this.get<boolean>("hoverActions.implementations"), 137 implementations: this.get<boolean>("hoverActions.implementations"),
138 run: this.get<boolean>("hoverActions.run"),
139 debug: this.get<boolean>("hoverActions.debug"),
140 gotoTypeDef: this.get<boolean>("hoverActions.gotoTypeDef"),
138 }; 141 };
139 } 142 }
140} 143}
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index a0c9b3ab2..61c12dbe0 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -82,7 +82,7 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
82 } 82 }
83 83
84 debugOutput.clear(); 84 debugOutput.clear();
85 if (ctx.config.debug.openUpDebugPane) { 85 if (ctx.config.debug.openDebugPane) {
86 debugOutput.show(true); 86 debugOutput.show(true);
87 } 87 }
88 88
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 16a94fceb..cdb63b46f 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -41,7 +41,20 @@ export async function activate(context: vscode.ExtensionContext) {
41 41
42 const config = new Config(context); 42 const config = new Config(context);
43 const state = new PersistentState(context.globalState); 43 const state = new PersistentState(context.globalState);
44 const serverPath = await bootstrap(config, state); 44 const serverPath = await bootstrap(config, state).catch(err => {
45 let message = "bootstrap error. ";
46
47 if (err.code === "EBUSY" || err.code === "ETXTBSY") {
48 message += "Other vscode windows might be using rust-analyzer, ";
49 message += "you should close them and reload this window to retry. ";
50 }
51
52 message += 'Open "Help > Toggle Developer Tools > Console" to see the logs ';
53 message += '(enable verbose logs with "rust-analyzer.trace.extension")';
54
55 log.error("Bootstrap error", err);
56 throw new Error(message);
57 });
45 58
46 const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; 59 const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
47 if (workspaceFolder === undefined) { 60 if (workspaceFolder === undefined) {
@@ -99,6 +112,7 @@ export async function activate(context: vscode.ExtensionContext) {
99 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); 112 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
100 ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction); 113 ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction);
101 ctx.registerCommand('applyActionGroup', commands.applyActionGroup); 114 ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
115 ctx.registerCommand('gotoLocation', commands.gotoLocation);
102 116
103 ctx.pushCleanup(activateTaskProvider(workspaceFolder)); 117 ctx.pushCleanup(activateTaskProvider(workspaceFolder));
104 118
@@ -165,7 +179,11 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
165 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 179 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
166 180
167 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix"); 181 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix");
168 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer extension"); 182 await download({
183 url: artifact.browser_download_url,
184 dest,
185 progressTitle: "Downloading rust-analyzer extension",
186 });
169 187
170 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); 188 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest));
171 await fs.unlink(dest); 189 await fs.unlink(dest);
@@ -281,7 +299,17 @@ async function getServer(config: Config, state: PersistentState): Promise<string
281 const artifact = release.assets.find(artifact => artifact.name === binaryName); 299 const artifact = release.assets.find(artifact => artifact.name === binaryName);
282 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 300 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
283 301
284 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 }); 302 // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error.
303 await fs.unlink(dest).catch(err => {
304 if (err.code !== "ENOENT") throw err;
305 });
306
307 await download({
308 url: artifact.browser_download_url,
309 dest,
310 progressTitle: "Downloading rust-analyzer server",
311 mode: 0o755
312 });
285 313
286 // Patching executable if that's NixOS. 314 // Patching executable if that's NixOS.
287 if (await fs.stat("/etc/nixos").then(_ => true).catch(_ => false)) { 315 if (await fs.stat("/etc/nixos").then(_ => true).catch(_ => false)) {
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index 492213937..866092882 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -1,8 +1,10 @@
1import fetch from "node-fetch"; 1import fetch from "node-fetch";
2import * as vscode from "vscode"; 2import * as vscode from "vscode";
3import * as fs from "fs";
4import * as stream from "stream"; 3import * as stream from "stream";
4import * as crypto from "crypto";
5import * as fs from "fs";
5import * as util from "util"; 6import * as util from "util";
7import * as path from "path";
6import { log, assert } from "./util"; 8import { log, assert } from "./util";
7 9
8const pipeline = util.promisify(stream.pipeline); 10const pipeline = util.promisify(stream.pipeline);
@@ -58,22 +60,29 @@ export interface GithubRelease {
58 }>; 60 }>;
59} 61}
60 62
63interface DownloadOpts {
64 progressTitle: string;
65 url: string;
66 dest: string;
67 mode?: number;
68}
69
70export async function download(opts: DownloadOpts) {
71 // Put artifact into a temporary file (in the same dir for simplicity)
72 // to prevent partially downloaded files when user kills vscode
73 const dest = path.parse(opts.dest);
74 const randomHex = crypto.randomBytes(5).toString("hex");
75 const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`);
61 76
62export async function download(
63 downloadUrl: string,
64 destinationPath: string,
65 progressTitle: string,
66 { mode }: { mode?: number } = {},
67) {
68 await vscode.window.withProgress( 77 await vscode.window.withProgress(
69 { 78 {
70 location: vscode.ProgressLocation.Notification, 79 location: vscode.ProgressLocation.Notification,
71 cancellable: false, 80 cancellable: false,
72 title: progressTitle 81 title: opts.progressTitle
73 }, 82 },
74 async (progress, _cancellationToken) => { 83 async (progress, _cancellationToken) => {
75 let lastPercentage = 0; 84 let lastPercentage = 0;
76 await downloadFile(downloadUrl, destinationPath, mode, (readBytes, totalBytes) => { 85 await downloadFile(opts.url, tempFile, opts.mode, (readBytes, totalBytes) => {
77 const newPercentage = (readBytes / totalBytes) * 100; 86 const newPercentage = (readBytes / totalBytes) * 100;
78 progress.report({ 87 progress.report({
79 message: newPercentage.toFixed(0) + "%", 88 message: newPercentage.toFixed(0) + "%",
@@ -84,10 +93,12 @@ export async function download(
84 }); 93 });
85 } 94 }
86 ); 95 );
96
97 await fs.promises.rename(tempFile, opts.dest);
87} 98}
88 99
89/** 100/**
90 * Downloads file from `url` and stores it at `destFilePath` with `destFilePermissions`. 101 * Downloads file from `url` and stores it at `destFilePath` with `mode` (unix permissions).
91 * `onProgress` callback is called on recieveing each chunk of bytes 102 * `onProgress` callback is called on recieveing each chunk of bytes
92 * to track the progress of downloading, it gets the already read and total 103 * to track the progress of downloading, it gets the already read and total
93 * amount of bytes to read as its parameters. 104 * amount of bytes to read as its parameters.
@@ -119,13 +130,11 @@ async function downloadFile(
119 }); 130 });
120 131
121 const destFileStream = fs.createWriteStream(destFilePath, { mode }); 132 const destFileStream = fs.createWriteStream(destFilePath, { mode });
122
123 await pipeline(res.body, destFileStream); 133 await pipeline(res.body, destFileStream);
124 return new Promise<void>(resolve => { 134 await new Promise<void>(resolve => {
125 destFileStream.on("close", resolve); 135 destFileStream.on("close", resolve);
126 destFileStream.destroy(); 136 destFileStream.destroy();
127 137 // This workaround is awaiting to be removed when vscode moves to newer nodejs version:
128 // Details on workaround: https://github.com/rust-analyzer/rust-analyzer/pull/3092#discussion_r378191131 138 // https://github.com/rust-analyzer/rust-analyzer/issues/3167
129 // Issue at nodejs repo: https://github.com/nodejs/node/issues/31776
130 }); 139 });
131} 140}
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index fe3fb71cd..fec4c3295 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -26,7 +26,6 @@ export const log = new class {
26 } 26 }
27 27
28 error(message?: any, ...optionalParams: any[]): void { 28 error(message?: any, ...optionalParams: any[]): void {
29 if (!log.enabled) return;
30 debugger; 29 debugger;
31 // eslint-disable-next-line no-console 30 // eslint-disable-next-line no-console
32 console.error(message, ...optionalParams); 31 console.error(message, ...optionalParams);