aboutsummaryrefslogtreecommitdiff
path: root/editors/code
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code')
-rw-r--r--editors/code/package.json14
-rw-r--r--editors/code/src/client.ts12
-rw-r--r--editors/code/src/config.ts2
-rw-r--r--editors/code/src/ctx.ts13
-rw-r--r--editors/code/src/main.ts84
-rw-r--r--editors/code/src/run.ts4
-rw-r--r--editors/code/src/snippets.ts5
-rw-r--r--editors/code/src/tasks.ts27
8 files changed, 102 insertions, 59 deletions
diff --git a/editors/code/package.json b/editors/code/package.json
index 17d9281ff..42a06e137 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -572,6 +572,11 @@
572 "default": true, 572 "default": true,
573 "type": "boolean" 573 "type": "boolean"
574 }, 574 },
575 "rust-analyzer.completion.autoself.enable": {
576 "markdownDescription": "Toggles the additional completions that automatically show method calls and field accesses\nwith `self` prefixed to them when inside a method.",
577 "default": true,
578 "type": "boolean"
579 },
575 "rust-analyzer.diagnostics.enable": { 580 "rust-analyzer.diagnostics.enable": {
576 "markdownDescription": "Whether to show native rust-analyzer diagnostics.", 581 "markdownDescription": "Whether to show native rust-analyzer diagnostics.",
577 "default": true, 582 "default": true,
@@ -597,7 +602,7 @@
597 "type": "object" 602 "type": "object"
598 }, 603 },
599 "rust-analyzer.diagnostics.warningsAsHint": { 604 "rust-analyzer.diagnostics.warningsAsHint": {
600 "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.", 605 "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.",
601 "default": [], 606 "default": [],
602 "type": "array", 607 "type": "array",
603 "items": { 608 "items": {
@@ -605,7 +610,7 @@
605 } 610 }
606 }, 611 },
607 "rust-analyzer.diagnostics.warningsAsInfo": { 612 "rust-analyzer.diagnostics.warningsAsInfo": {
608 "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.", 613 "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.",
609 "default": [], 614 "default": [],
610 "type": "array", 615 "type": "array",
611 "items": { 616 "items": {
@@ -795,6 +800,11 @@
795 "type": "string" 800 "type": "string"
796 } 801 }
797 }, 802 },
803 "rust-analyzer.rustfmt.enableRangeFormatting": {
804 "markdownDescription": "Enables the use of rustfmt's unstable range formatting command for the\n`textDocument/rangeFormatting` request. The rustfmt option is unstable and only\navailable on a nightly build.",
805 "default": false,
806 "type": "boolean"
807 },
798 "rust-analyzer.workspace.symbol.search.scope": { 808 "rust-analyzer.workspace.symbol.search.scope": {
799 "markdownDescription": "Workspace symbol search scope.", 809 "markdownDescription": "Workspace symbol search scope.",
800 "default": "workspace", 810 "default": "workspace",
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 116f41df6..f13ae07e1 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -4,6 +4,7 @@ import * as ra from '../src/lsp_ext';
4import * as Is from 'vscode-languageclient/lib/common/utils/is'; 4import * as Is from 'vscode-languageclient/lib/common/utils/is';
5import { assert } from './util'; 5import { assert } from './util';
6import { WorkspaceEdit } from 'vscode'; 6import { WorkspaceEdit } from 'vscode';
7import { Workspace } from './ctx';
7 8
8export interface Env { 9export interface Env {
9 [name: string]: string; 10 [name: string]: string;
@@ -23,7 +24,7 @@ function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownStri
23 return result; 24 return result;
24} 25}
25 26
26export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc.LanguageClient { 27export function createClient(serverPath: string, workspace: Workspace, extraEnv: Env): lc.LanguageClient {
27 // '.' Is the fallback if no folder is open 28 // '.' Is the fallback if no folder is open
28 // TODO?: Workspace folders support Uri's (eg: file://test.txt). 29 // TODO?: Workspace folders support Uri's (eg: file://test.txt).
29 // It might be a good idea to test if the uri points to a file. 30 // It might be a good idea to test if the uri points to a file.
@@ -33,7 +34,7 @@ export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc
33 34
34 const run: lc.Executable = { 35 const run: lc.Executable = {
35 command: serverPath, 36 command: serverPath,
36 options: { cwd, env: newEnv }, 37 options: { env: newEnv },
37 }; 38 };
38 const serverOptions: lc.ServerOptions = { 39 const serverOptions: lc.ServerOptions = {
39 run, 40 run,
@@ -43,9 +44,14 @@ export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc
43 'Rust Analyzer Language Server Trace', 44 'Rust Analyzer Language Server Trace',
44 ); 45 );
45 46
47 let initializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
48 if (workspace.kind === "Detached Files") {
49 initializationOptions = { "detachedFiles": workspace.files.map(file => file.uri.fsPath), ...initializationOptions };
50 }
51
46 const clientOptions: lc.LanguageClientOptions = { 52 const clientOptions: lc.LanguageClientOptions = {
47 documentSelector: [{ scheme: 'file', language: 'rust' }], 53 documentSelector: [{ scheme: 'file', language: 'rust' }],
48 initializationOptions: vscode.workspace.getConfiguration("rust-analyzer"), 54 initializationOptions,
49 diagnosticCollectionName: "rustc", 55 diagnosticCollectionName: "rustc",
50 traceOutputChannel, 56 traceOutputChannel,
51 middleware: { 57 middleware: {
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index fbb7a556a..d3d6e631a 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -34,7 +34,7 @@ export class Config {
34 readonly globalStoragePath: string; 34 readonly globalStoragePath: string;
35 35
36 constructor(ctx: vscode.ExtensionContext) { 36 constructor(ctx: vscode.ExtensionContext) {
37 this.globalStoragePath = ctx.globalStorageUri.path; 37 this.globalStoragePath = ctx.globalStorageUri.fsPath;
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/ctx.ts b/editors/code/src/ctx.ts
index bd023f803..cf67dd8cf 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -7,6 +7,15 @@ import { createClient } from './client';
7import { isRustEditor, RustEditor } from './util'; 7import { isRustEditor, RustEditor } from './util';
8import { ServerStatusParams } from './lsp_ext'; 8import { ServerStatusParams } from './lsp_ext';
9 9
10export type Workspace =
11 {
12 kind: 'Workspace Folder';
13 }
14 | {
15 kind: 'Detached Files';
16 files: vscode.TextDocument[];
17 };
18
10export class Ctx { 19export class Ctx {
11 private constructor( 20 private constructor(
12 readonly config: Config, 21 readonly config: Config,
@@ -22,9 +31,9 @@ export class Ctx {
22 config: Config, 31 config: Config,
23 extCtx: vscode.ExtensionContext, 32 extCtx: vscode.ExtensionContext,
24 serverPath: string, 33 serverPath: string,
25 cwd: string, 34 workspace: Workspace,
26 ): Promise<Ctx> { 35 ): Promise<Ctx> {
27 const client = createClient(serverPath, cwd, config.serverExtraEnv); 36 const client = createClient(serverPath, workspace, config.serverExtraEnv);
28 37
29 const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); 38 const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
30 extCtx.subscriptions.push(statusBar); 39 extCtx.subscriptions.push(statusBar);
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index aaedc2431..d26273246 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -7,7 +7,7 @@ import * as commands from './commands';
7import { activateInlayHints } from './inlay_hints'; 7import { activateInlayHints } from './inlay_hints';
8import { Ctx } from './ctx'; 8import { Ctx } from './ctx';
9import { Config } from './config'; 9import { Config } from './config';
10import { log, assert, isValidExecutable } from './util'; 10import { log, assert, isValidExecutable, isRustDocument } from './util';
11import { PersistentState } from './persistent_state'; 11import { PersistentState } from './persistent_state';
12import { fetchRelease, download } from './net'; 12import { fetchRelease, download } from './net';
13import { activateTaskProvider } from './tasks'; 13import { activateTaskProvider } from './tasks';
@@ -28,26 +28,6 @@ export async function activate(context: vscode.ExtensionContext) {
28} 28}
29 29
30async function tryActivate(context: vscode.ExtensionContext) { 30async function tryActivate(context: vscode.ExtensionContext) {
31 // Register a "dumb" onEnter command for the case where server fails to
32 // start.
33 //
34 // FIXME: refactor command registration code such that commands are
35 // **always** registered, even if the server does not start. Use API like
36 // this perhaps?
37 //
38 // ```TypeScript
39 // registerCommand(
40 // factory: (Ctx) => ((Ctx) => any),
41 // fallback: () => any = () => vscode.window.showErrorMessage(
42 // "rust-analyzer is not available"
43 // ),
44 // )
45 const defaultOnEnter = vscode.commands.registerCommand(
46 'rust-analyzer.onEnter',
47 () => vscode.commands.executeCommand('default:type', { text: '\n' }),
48 );
49 context.subscriptions.push(defaultOnEnter);
50
51 const config = new Config(context); 31 const config = new Config(context);
52 const state = new PersistentState(context.globalState); 32 const state = new PersistentState(context.globalState);
53 const serverPath = await bootstrap(config, state).catch(err => { 33 const serverPath = await bootstrap(config, state).catch(err => {
@@ -65,16 +45,53 @@ async function tryActivate(context: vscode.ExtensionContext) {
65 throw new Error(message); 45 throw new Error(message);
66 }); 46 });
67 47
68 const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; 48 if (vscode.workspace.workspaceFolders?.length === 0) {
69 if (workspaceFolder === undefined) { 49 const rustDocuments = vscode.workspace.textDocuments.filter(document => isRustDocument(document));
70 throw new Error("no folder is opened"); 50 if (rustDocuments.length > 0) {
51 ctx = await Ctx.create(config, context, serverPath, { kind: 'Detached Files', files: rustDocuments });
52 } else {
53 throw new Error("no rust files are opened");
54 }
55 } else {
56 // Note: we try to start the server before we activate type hints so that it
57 // registers its `onDidChangeDocument` handler before us.
58 //
59 // This a horribly, horribly wrong way to deal with this problem.
60 ctx = await Ctx.create(config, context, serverPath, { kind: "Workspace Folder" });
61 ctx.pushCleanup(activateTaskProvider(ctx.config));
71 } 62 }
63 await initCommonContext(context, ctx);
64
65 activateInlayHints(ctx);
66 warnAboutExtensionConflicts();
67
68 vscode.workspace.onDidChangeConfiguration(
69 _ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }),
70 null,
71 ctx.subscriptions,
72 );
73}
72 74
73 // Note: we try to start the server before we activate type hints so that it 75async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
74 // registers its `onDidChangeDocument` handler before us. 76 // Register a "dumb" onEnter command for the case where server fails to
77 // start.
78 //
79 // FIXME: refactor command registration code such that commands are
80 // **always** registered, even if the server does not start. Use API like
81 // this perhaps?
75 // 82 //
76 // This a horribly, horribly wrong way to deal with this problem. 83 // ```TypeScript
77 ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath); 84 // registerCommand(
85 // factory: (Ctx) => ((Ctx) => any),
86 // fallback: () => any = () => vscode.window.showErrorMessage(
87 // "rust-analyzer is not available"
88 // ),
89 // )
90 const defaultOnEnter = vscode.commands.registerCommand(
91 'rust-analyzer.onEnter',
92 () => vscode.commands.executeCommand('default:type', { text: '\n' }),
93 );
94 context.subscriptions.push(defaultOnEnter);
78 95
79 await setContextValue(RUST_PROJECT_CONTEXT_NAME, true); 96 await setContextValue(RUST_PROJECT_CONTEXT_NAME, true);
80 97
@@ -134,17 +151,6 @@ async function tryActivate(context: vscode.ExtensionContext) {
134 ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction); 151 ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction);
135 ctx.registerCommand('applyActionGroup', commands.applyActionGroup); 152 ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
136 ctx.registerCommand('gotoLocation', commands.gotoLocation); 153 ctx.registerCommand('gotoLocation', commands.gotoLocation);
137
138 ctx.pushCleanup(activateTaskProvider(workspaceFolder, ctx.config));
139
140 activateInlayHints(ctx);
141 warnAboutExtensionConflicts();
142
143 vscode.workspace.onDidChangeConfiguration(
144 _ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }),
145 null,
146 ctx.subscriptions,
147 );
148} 154}
149 155
150export async function deactivate() { 156export async function deactivate() {
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index 138e3f686..d0be84068 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -142,7 +142,11 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
142 // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion 142 // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
143 const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate() 143 const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate()
144 const cargoTask = await tasks.buildCargoTask(target, definition, runnable.label, args, config.cargoRunner, true); 144 const cargoTask = await tasks.buildCargoTask(target, definition, runnable.label, args, config.cargoRunner, true);
145
145 cargoTask.presentationOptions.clear = true; 146 cargoTask.presentationOptions.clear = true;
147 // Sadly, this doesn't prevent focus stealing if the terminal is currently
148 // hidden, and will become revealed due to task exucution.
149 cargoTask.presentationOptions.focus = false;
146 150
147 return cargoTask; 151 return cargoTask;
148} 152}
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts
index 9561aa345..a409e5296 100644
--- a/editors/code/src/snippets.ts
+++ b/editors/code/src/snippets.ts
@@ -52,10 +52,13 @@ export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vs
52 } else { 52 } else {
53 builder.replace(indel.range, indel.newText); 53 builder.replace(indel.range, indel.newText);
54 } 54 }
55 lineDelta = countLines(indel.newText) - (indel.range.end.line - indel.range.start.line); 55 lineDelta += countLines(indel.newText) - (indel.range.end.line - indel.range.start.line);
56 } 56 }
57 }); 57 });
58 if (selections.length > 0) editor.selections = selections; 58 if (selections.length > 0) editor.selections = selections;
59 if (selections.length === 1) {
60 editor.revealRange(selections[0], vscode.TextEditorRevealType.InCenterIfOutsideViewport);
61 }
59} 62}
60 63
61function parseSnippet(snip: string): [string, [number, number]] | undefined { 64function parseSnippet(snip: string): [string, [number, number]] | undefined {
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
index a3ff15102..694ee1e41 100644
--- a/editors/code/src/tasks.ts
+++ b/editors/code/src/tasks.ts
@@ -17,11 +17,9 @@ export interface CargoTaskDefinition extends vscode.TaskDefinition {
17} 17}
18 18
19class CargoTaskProvider implements vscode.TaskProvider { 19class CargoTaskProvider implements vscode.TaskProvider {
20 private readonly target: vscode.WorkspaceFolder;
21 private readonly config: Config; 20 private readonly config: Config;
22 21
23 constructor(target: vscode.WorkspaceFolder, config: Config) { 22 constructor(config: Config) {
24 this.target = target;
25 this.config = config; 23 this.config = config;
26 } 24 }
27 25
@@ -40,10 +38,12 @@ class CargoTaskProvider implements vscode.TaskProvider {
40 ]; 38 ];
41 39
42 const tasks: vscode.Task[] = []; 40 const tasks: vscode.Task[] = [];
43 for (const def of defs) { 41 for (const workspaceTarget of vscode.workspace.workspaceFolders || []) {
44 const vscodeTask = await buildCargoTask(this.target, { type: TASK_TYPE, command: def.command }, `cargo ${def.command}`, [def.command], this.config.cargoRunner); 42 for (const def of defs) {
45 vscodeTask.group = def.group; 43 const vscodeTask = await buildCargoTask(workspaceTarget, { type: TASK_TYPE, command: def.command }, `cargo ${def.command}`, [def.command], this.config.cargoRunner);
46 tasks.push(vscodeTask); 44 vscodeTask.group = def.group;
45 tasks.push(vscodeTask);
46 }
47 } 47 }
48 48
49 return tasks; 49 return tasks;
@@ -58,14 +58,19 @@ class CargoTaskProvider implements vscode.TaskProvider {
58 58
59 if (definition.type === TASK_TYPE && definition.command) { 59 if (definition.type === TASK_TYPE && definition.command) {
60 const args = [definition.command].concat(definition.args ?? []); 60 const args = [definition.command].concat(definition.args ?? []);
61 61 if (isWorkspaceFolder(task.scope)) {
62 return await buildCargoTask(this.target, definition, task.name, args, this.config.cargoRunner); 62 return await buildCargoTask(task.scope, definition, task.name, args, this.config.cargoRunner);
63 }
63 } 64 }
64 65
65 return undefined; 66 return undefined;
66 } 67 }
67} 68}
68 69
70function isWorkspaceFolder(scope?: any): scope is vscode.WorkspaceFolder {
71 return (scope as vscode.WorkspaceFolder).name !== undefined;
72}
73
69export async function buildCargoTask( 74export async function buildCargoTask(
70 target: vscode.WorkspaceFolder, 75 target: vscode.WorkspaceFolder,
71 definition: CargoTaskDefinition, 76 definition: CargoTaskDefinition,
@@ -119,7 +124,7 @@ export async function buildCargoTask(
119 ); 124 );
120} 125}
121 126
122export function activateTaskProvider(target: vscode.WorkspaceFolder, config: Config): vscode.Disposable { 127export function activateTaskProvider(config: Config): vscode.Disposable {
123 const provider = new CargoTaskProvider(target, config); 128 const provider = new CargoTaskProvider(config);
124 return vscode.tasks.registerTaskProvider(TASK_TYPE, provider); 129 return vscode.tasks.registerTaskProvider(TASK_TYPE, provider);
125} 130}