diff options
Diffstat (limited to 'editors/code/src')
-rw-r--r-- | editors/code/src/commands.ts | 14 | ||||
-rw-r--r-- | editors/code/src/config.ts | 5 | ||||
-rw-r--r-- | editors/code/src/debug.ts | 2 | ||||
-rw-r--r-- | editors/code/src/main.ts | 34 | ||||
-rw-r--r-- | editors/code/src/net.ts | 39 | ||||
-rw-r--r-- | editors/code/src/util.ts | 1 |
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 | ||
356 | export 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 | |||
356 | export function resolveCodeAction(ctx: Ctx): Cmd { | 370 | export 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 @@ | |||
1 | import fetch from "node-fetch"; | 1 | import fetch from "node-fetch"; |
2 | import * as vscode from "vscode"; | 2 | import * as vscode from "vscode"; |
3 | import * as fs from "fs"; | ||
4 | import * as stream from "stream"; | 3 | import * as stream from "stream"; |
4 | import * as crypto from "crypto"; | ||
5 | import * as fs from "fs"; | ||
5 | import * as util from "util"; | 6 | import * as util from "util"; |
7 | import * as path from "path"; | ||
6 | import { log, assert } from "./util"; | 8 | import { log, assert } from "./util"; |
7 | 9 | ||
8 | const pipeline = util.promisify(stream.pipeline); | 10 | const pipeline = util.promisify(stream.pipeline); |
@@ -58,22 +60,29 @@ export interface GithubRelease { | |||
58 | }>; | 60 | }>; |
59 | } | 61 | } |
60 | 62 | ||
63 | interface DownloadOpts { | ||
64 | progressTitle: string; | ||
65 | url: string; | ||
66 | dest: string; | ||
67 | mode?: number; | ||
68 | } | ||
69 | |||
70 | export 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 | ||
62 | export 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); |