aboutsummaryrefslogtreecommitdiff
path: root/editors
diff options
context:
space:
mode:
Diffstat (limited to 'editors')
-rw-r--r--editors/code/package.json29
-rw-r--r--editors/code/src/client.ts11
-rw-r--r--editors/code/src/commands.ts55
-rw-r--r--editors/code/src/config.ts7
-rw-r--r--editors/code/src/ctx.ts2
-rw-r--r--editors/code/src/lsp_ext.ts1
-rw-r--r--editors/code/src/main.ts27
7 files changed, 115 insertions, 17 deletions
diff --git a/editors/code/package.json b/editors/code/package.json
index 13749a084..3e6ebd7ed 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -104,6 +104,11 @@
104 "category": "Rust Analyzer" 104 "category": "Rust Analyzer"
105 }, 105 },
106 { 106 {
107 "command": "rust-analyzer.viewHir",
108 "title": "View Hir",
109 "category": "Rust Analyzer"
110 },
111 {
107 "command": "rust-analyzer.expandMacro", 112 "command": "rust-analyzer.expandMacro",
108 "title": "Expand macro recursively", 113 "title": "Expand macro recursively",
109 "category": "Rust Analyzer" 114 "category": "Rust Analyzer"
@@ -275,7 +280,7 @@
275 "default": true, 280 "default": true,
276 "description": "Whether to ask for permission before downloading any files from the Internet." 281 "description": "Whether to ask for permission before downloading any files from the Internet."
277 }, 282 },
278 "rust-analyzer.serverPath": { 283 "rust-analyzer.server.path": {
279 "type": [ 284 "type": [
280 "null", 285 "null",
281 "string" 286 "string"
@@ -283,6 +288,14 @@
283 "default": null, 288 "default": null,
284 "markdownDescription": "Path to rust-analyzer executable (points to bundled binary by default). If this is set, then `#rust-analyzer.updates.channel#` setting is not used" 289 "markdownDescription": "Path to rust-analyzer executable (points to bundled binary by default). If this is set, then `#rust-analyzer.updates.channel#` setting is not used"
285 }, 290 },
291 "rust-analyzer.server.extraEnv": {
292 "type": [
293 "null",
294 "object"
295 ],
296 "default": null,
297 "markdownDescription": "Extra environment variables that will be passed to the rust-analyzer executable. Useful for passing e.g. `RA_LOG` for debugging."
298 },
286 "rust-analyzer.trace.server": { 299 "rust-analyzer.trace.server": {
287 "type": "string", 300 "type": "string",
288 "scope": "window", 301 "scope": "window",
@@ -336,7 +349,7 @@
336 "default": {}, 349 "default": {},
337 "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`" 350 "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`"
338 }, 351 },
339 "rust-analyzer.assist.importMergeBehaviour": { 352 "rust-analyzer.assist.importMergeBehavior": {
340 "markdownDescription": "The strategy to use when inserting new imports or merging imports.", 353 "markdownDescription": "The strategy to use when inserting new imports or merging imports.",
341 "default": "full", 354 "default": "full",
342 "type": "string", 355 "type": "string",
@@ -650,6 +663,14 @@
650 "default": false, 663 "default": false,
651 "type": "boolean" 664 "type": "boolean"
652 }, 665 },
666 "rust-analyzer.procMacro.server": {
667 "markdownDescription": "Internal config, path to proc-macro server executable (typically, this is rust-analyzer itself, but we override this in tests).",
668 "default": null,
669 "type": [
670 "null",
671 "string"
672 ]
673 },
653 "rust-analyzer.runnables.overrideCargo": { 674 "rust-analyzer.runnables.overrideCargo": {
654 "markdownDescription": "Command to be executed instead of 'cargo' for runnables.", 675 "markdownDescription": "Command to be executed instead of 'cargo' for runnables.",
655 "default": null, 676 "default": null,
@@ -999,6 +1020,10 @@
999 "when": "inRustProject" 1020 "when": "inRustProject"
1000 }, 1021 },
1001 { 1022 {
1023 "command": "rust-analyzer.viewHir",
1024 "when": "inRustProject"
1025 },
1026 {
1002 "command": "rust-analyzer.expandMacro", 1027 "command": "rust-analyzer.expandMacro",
1003 "when": "inRustProject" 1028 "when": "inRustProject"
1004 }, 1029 },
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 63ab82dde..539e487ec 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -6,6 +6,10 @@ import { DocumentSemanticsTokensSignature, DocumentSemanticsTokensEditsSignature
6import { assert } from './util'; 6import { assert } from './util';
7import { WorkspaceEdit } from 'vscode'; 7import { WorkspaceEdit } from 'vscode';
8 8
9export interface Env {
10 [name: string]: string;
11}
12
9function renderCommand(cmd: ra.CommandLink) { 13function renderCommand(cmd: ra.CommandLink) {
10 return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; 14 return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`;
11} 15}
@@ -27,14 +31,17 @@ async function semanticHighlightingWorkaround<R, F extends (...args: any[]) => v
27 return res; 31 return res;
28} 32}
29 33
30export function createClient(serverPath: string, cwd: string): lc.LanguageClient { 34export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc.LanguageClient {
31 // '.' Is the fallback if no folder is open 35 // '.' Is the fallback if no folder is open
32 // TODO?: Workspace folders support Uri's (eg: file://test.txt). 36 // TODO?: Workspace folders support Uri's (eg: file://test.txt).
33 // It might be a good idea to test if the uri points to a file. 37 // It might be a good idea to test if the uri points to a file.
34 38
39 const newEnv = Object.assign({}, process.env);
40 Object.assign(newEnv, extraEnv);
41
35 const run: lc.Executable = { 42 const run: lc.Executable = {
36 command: serverPath, 43 command: serverPath,
37 options: { cwd }, 44 options: { cwd, env: newEnv },
38 }; 45 };
39 const serverOptions: lc.ServerOptions = { 46 const serverOptions: lc.ServerOptions = {
40 run, 47 run,
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index b12e134ca..c1c9f9754 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -340,6 +340,61 @@ export function syntaxTree(ctx: Ctx): Cmd {
340 }; 340 };
341} 341}
342 342
343// Opens the virtual file that will show the HIR of the function containing the cursor position
344//
345// The contents of the file come from the `TextDocumentContentProvider`
346export function viewHir(ctx: Ctx): Cmd {
347 const tdcp = new class implements vscode.TextDocumentContentProvider {
348 readonly uri = vscode.Uri.parse('rust-analyzer://viewHir/hir.txt');
349 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
350 constructor() {
351 vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
352 vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
353 }
354
355 private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
356 if (isRustDocument(event.document)) {
357 // We need to order this after language server updates, but there's no API for that.
358 // Hence, good old sleep().
359 void sleep(10).then(() => this.eventEmitter.fire(this.uri));
360 }
361 }
362 private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
363 if (editor && isRustEditor(editor)) {
364 this.eventEmitter.fire(this.uri);
365 }
366 }
367
368 provideTextDocumentContent(_uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
369 const rustEditor = ctx.activeRustEditor;
370 const client = ctx.client;
371 if (!rustEditor || !client) return '';
372
373 const params = {
374 textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(rustEditor.document),
375 position: client.code2ProtocolConverter.asPosition(
376 rustEditor.selection.active,
377 ),
378 };
379 return client.sendRequest(ra.viewHir, params, ct);
380 }
381
382 get onDidChange(): vscode.Event<vscode.Uri> {
383 return this.eventEmitter.event;
384 }
385 };
386
387 ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
388
389 return async () => {
390 const document = await vscode.workspace.openTextDocument(tdcp.uri);
391 tdcp.eventEmitter.fire(tdcp.uri);
392 void await vscode.window.showTextDocument(document, {
393 viewColumn: vscode.ViewColumn.Two,
394 preserveFocus: true
395 });
396 };
397}
343 398
344// Opens the virtual file that will show the syntax tree 399// Opens the virtual file that will show the syntax tree
345// 400//
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 848e92af9..ebe4de1ea 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -1,4 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import { Env } from './client';
2import { log } from "./util"; 3import { log } from "./util";
3 4
4export type UpdatesChannel = "stable" | "nightly"; 5export type UpdatesChannel = "stable" | "nightly";
@@ -13,6 +14,7 @@ export class Config {
13 readonly rootSection = "rust-analyzer"; 14 readonly rootSection = "rust-analyzer";
14 private readonly requiresReloadOpts = [ 15 private readonly requiresReloadOpts = [
15 "serverPath", 16 "serverPath",
17 "server",
16 "cargo", 18 "cargo",
17 "procMacro", 19 "procMacro",
18 "files", 20 "files",
@@ -91,7 +93,10 @@ export class Config {
91 return this.cfg.get<T>(path)!; 93 return this.cfg.get<T>(path)!;
92 } 94 }
93 95
94 get serverPath() { return this.get<null | string>("serverPath"); } 96 get serverPath() {
97 return this.get<null | string>("server.path") ?? this.get<null | string>("serverPath");
98 }
99 get serverExtraEnv() { return this.get<Env | null>("server.extraEnv") ?? {}; }
95 get channel() { return this.get<UpdatesChannel>("updates.channel"); } 100 get channel() { return this.get<UpdatesChannel>("updates.channel"); }
96 get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); } 101 get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); }
97 get traceExtension() { return this.get<boolean>("trace.extension"); } 102 get traceExtension() { return this.get<boolean>("trace.extension"); }
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index d39864d33..e7585184b 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -24,7 +24,7 @@ export class Ctx {
24 serverPath: string, 24 serverPath: string,
25 cwd: string, 25 cwd: string,
26 ): Promise<Ctx> { 26 ): Promise<Ctx> {
27 const client = createClient(serverPath, cwd); 27 const client = createClient(serverPath, cwd, config.serverExtraEnv);
28 28
29 const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); 29 const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
30 extCtx.subscriptions.push(statusBar); 30 extCtx.subscriptions.push(statusBar);
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 5e877ce65..d21a3db86 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -24,6 +24,7 @@ export interface SyntaxTreeParams {
24} 24}
25export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>("rust-analyzer/syntaxTree"); 25export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>("rust-analyzer/syntaxTree");
26 26
27export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>("rust-analyzer/viewHir");
27 28
28export interface ExpandMacroParams { 29export interface ExpandMacroParams {
29 textDocument: lc.TextDocumentIdentifier; 30 textDocument: lc.TextDocumentIdentifier;
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 282240d84..694da9409 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -105,6 +105,7 @@ async function tryActivate(context: vscode.ExtensionContext) {
105 ctx.registerCommand('joinLines', commands.joinLines); 105 ctx.registerCommand('joinLines', commands.joinLines);
106 ctx.registerCommand('parentModule', commands.parentModule); 106 ctx.registerCommand('parentModule', commands.parentModule);
107 ctx.registerCommand('syntaxTree', commands.syntaxTree); 107 ctx.registerCommand('syntaxTree', commands.syntaxTree);
108 ctx.registerCommand('viewHir', commands.viewHir);
108 ctx.registerCommand('expandMacro', commands.expandMacro); 109 ctx.registerCommand('expandMacro', commands.expandMacro);
109 ctx.registerCommand('run', commands.run); 110 ctx.registerCommand('run', commands.run);
110 ctx.registerCommand('debug', commands.debug); 111 ctx.registerCommand('debug', commands.debug);
@@ -166,6 +167,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
166 } 167 }
167 return; 168 return;
168 }; 169 };
170 if (serverPath(config) !== null) return;
169 171
170 const now = Date.now(); 172 const now = Date.now();
171 if (config.package.releaseTag === NIGHTLY_TAG) { 173 if (config.package.releaseTag === NIGHTLY_TAG) {
@@ -277,7 +279,7 @@ async function patchelf(dest: PathLike): Promise<void> {
277} 279}
278 280
279async function getServer(config: Config, state: PersistentState): Promise<string | undefined> { 281async function getServer(config: Config, state: PersistentState): Promise<string | undefined> {
280 const explicitPath = process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath; 282 const explicitPath = serverPath(config);
281 if (explicitPath) { 283 if (explicitPath) {
282 if (explicitPath.startsWith("~/")) { 284 if (explicitPath.startsWith("~/")) {
283 return os.homedir() + explicitPath.slice("~".length); 285 return os.homedir() + explicitPath.slice("~".length);
@@ -286,16 +288,15 @@ async function getServer(config: Config, state: PersistentState): Promise<string
286 }; 288 };
287 if (config.package.releaseTag === null) return "rust-analyzer"; 289 if (config.package.releaseTag === null) return "rust-analyzer";
288 290
289 let platform: string | undefined; 291 const platforms: { [key: string]: string } = {
290 if ((process.arch === "x64" || process.arch === "ia32") && process.platform === "win32") { 292 "ia32 win32": "x86_64-pc-windows-msvc",
291 platform = "x86_64-pc-windows-msvc"; 293 "x64 win32": "x86_64-pc-windows-msvc",
292 } else if (process.arch === "x64" && process.platform === "linux") { 294 "x64 linux": "x86_64-unknown-linux-gnu",
293 platform = "x86_64-unknown-linux-gnu"; 295 "x64 darwin": "x86_64-apple-darwin",
294 } else if (process.arch === "x64" && process.platform === "darwin") { 296 "arm64 win32": "aarch64-pc-windows-msvc",
295 platform = "x86_64-apple-darwin"; 297 "arm64 darwin": "aarch64-apple-darwin",
296 } else if (process.arch === "arm64" && process.platform === "darwin") { 298 };
297 platform = "aarch64-apple-darwin"; 299 const platform = platforms[`${process.arch} ${process.platform}`];
298 }
299 if (platform === undefined) { 300 if (platform === undefined) {
300 vscode.window.showErrorMessage( 301 vscode.window.showErrorMessage(
301 "Unfortunately we don't ship binaries for your platform yet. " + 302 "Unfortunately we don't ship binaries for your platform yet. " +
@@ -351,6 +352,10 @@ async function getServer(config: Config, state: PersistentState): Promise<string
351 return dest; 352 return dest;
352} 353}
353 354
355function serverPath(config: Config): string | null {
356 return process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath;
357}
358
354async function isNixOs(): Promise<boolean> { 359async function isNixOs(): Promise<boolean> {
355 try { 360 try {
356 const contents = await fs.readFile("/etc/os-release"); 361 const contents = await fs.readFile("/etc/os-release");