aboutsummaryrefslogtreecommitdiff
path: root/editors/code
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-05-24 13:40:25 +0100
committerGitHub <[email protected]>2021-05-24 13:40:25 +0100
commit05fc97e31b1d04bf5d5885edd98a1510f0931a62 (patch)
tree27a99294690e75990250b0c1306ef99fad4558fa /editors/code
parent31a19148e967163ea9ebb42e341944be76ce8960 (diff)
parent5c0369b1d0c5351672f2a16e9a0d17beee84bcbe (diff)
Merge #8955
8955: feature: Support standalone Rust files r=matklad a=SomeoneToIgnore ![standalone](https://user-images.githubusercontent.com/2690773/119277037-0b579380-bc26-11eb-8d77-20d46ab4916a.gif) Closes https://github.com/rust-analyzer/rust-analyzer/issues/6388 Caveats: * I've decided to support multiple detached files in the code (anticipating the scratch files), but I found no way to open multiple files in VSCode at once: running `code *.rs` makes the plugin to register in the `vscode.workspace.textDocuments` only the first file, while code actually displays all files later. Apparently what happens is the same as when you have VSCode open at some workplace already and then run `code some_other_file.rs`: it gets opened in the same workspace of the same VSCode with no server to support it. If there's a way to override it, I'd appreciate the pointer. * No way to toggle inlay hints, since the setting is updated for the workspace (which does not exist for a single file opened) > [2021-05-24 00:22:49.100] [exthost] [error] Error: Unable to write to Workspace Settings because no workspace is opened. Please open a workspace first and try again. * No runners/lens to run or check the code are implemented for this mode. In theory, we can detect `rustc`, run it on a file and run the resulting binary, but not sure if worth doing it at this stage. Otherwise imports, hints, completion and other features work. Co-authored-by: Kirill Bulatov <[email protected]>
Diffstat (limited to 'editors/code')
-rw-r--r--editors/code/src/client.ts15
-rw-r--r--editors/code/src/ctx.ts14
-rw-r--r--editors/code/src/main.ts81
3 files changed, 69 insertions, 41 deletions
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 116f41df6..69dbe2535 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.
@@ -31,6 +32,11 @@ export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc
31 const newEnv = Object.assign({}, process.env); 32 const newEnv = Object.assign({}, process.env);
32 Object.assign(newEnv, extraEnv); 33 Object.assign(newEnv, extraEnv);
33 34
35 let cwd = undefined;
36 if (workspace.kind === "Workspace Folder") {
37 cwd = workspace.folder.fsPath;
38 };
39
34 const run: lc.Executable = { 40 const run: lc.Executable = {
35 command: serverPath, 41 command: serverPath,
36 options: { cwd, env: newEnv }, 42 options: { cwd, env: newEnv },
@@ -43,9 +49,14 @@ export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc
43 'Rust Analyzer Language Server Trace', 49 'Rust Analyzer Language Server Trace',
44 ); 50 );
45 51
52 let initializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
53 if (workspace.kind === "Detached Files") {
54 initializationOptions = { "detachedFiles": workspace.files.map(file => file.uri.fsPath), ...initializationOptions };
55 }
56
46 const clientOptions: lc.LanguageClientOptions = { 57 const clientOptions: lc.LanguageClientOptions = {
47 documentSelector: [{ scheme: 'file', language: 'rust' }], 58 documentSelector: [{ scheme: 'file', language: 'rust' }],
48 initializationOptions: vscode.workspace.getConfiguration("rust-analyzer"), 59 initializationOptions,
49 diagnosticCollectionName: "rustc", 60 diagnosticCollectionName: "rustc",
50 traceOutputChannel, 61 traceOutputChannel,
51 middleware: { 62 middleware: {
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index bd023f803..22c5f62a1 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -7,6 +7,16 @@ 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 folder: vscode.Uri;
14 }
15 | {
16 kind: 'Detached Files';
17 files: vscode.TextDocument[];
18 };
19
10export class Ctx { 20export class Ctx {
11 private constructor( 21 private constructor(
12 readonly config: Config, 22 readonly config: Config,
@@ -22,9 +32,9 @@ export class Ctx {
22 config: Config, 32 config: Config,
23 extCtx: vscode.ExtensionContext, 33 extCtx: vscode.ExtensionContext,
24 serverPath: string, 34 serverPath: string,
25 cwd: string, 35 workspace: Workspace,
26 ): Promise<Ctx> { 36 ): Promise<Ctx> {
27 const client = createClient(serverPath, cwd, config.serverExtraEnv); 37 const client = createClient(serverPath, workspace, config.serverExtraEnv);
28 38
29 const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); 39 const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
30 extCtx.subscriptions.push(statusBar); 40 extCtx.subscriptions.push(statusBar);
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index aaedc2431..b735186fe 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 => {
@@ -67,14 +47,52 @@ async function tryActivate(context: vscode.ExtensionContext) {
67 47
68 const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; 48 const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
69 if (workspaceFolder === undefined) { 49 if (workspaceFolder === undefined) {
70 throw new Error("no folder is opened"); 50 const rustDocuments = vscode.workspace.textDocuments.filter(document => isRustDocument(document));
51 if (rustDocuments.length > 0) {
52 ctx = await Ctx.create(config, context, serverPath, { kind: 'Detached Files', files: rustDocuments });
53 } else {
54 throw new Error("no rust files are opened");
55 }
56 } else {
57 // Note: we try to start the server before we activate type hints so that it
58 // registers its `onDidChangeDocument` handler before us.
59 //
60 // This a horribly, horribly wrong way to deal with this problem.
61 ctx = await Ctx.create(config, context, serverPath, { kind: "Workspace Folder", folder: workspaceFolder.uri });
62 ctx.pushCleanup(activateTaskProvider(workspaceFolder, ctx.config));
71 } 63 }
64 await initCommonContext(context, ctx);
65
66 activateInlayHints(ctx);
67 warnAboutExtensionConflicts();
68
69 vscode.workspace.onDidChangeConfiguration(
70 _ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }),
71 null,
72 ctx.subscriptions,
73 );
74}
72 75
73 // Note: we try to start the server before we activate type hints so that it 76async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
74 // registers its `onDidChangeDocument` handler before us. 77 // Register a "dumb" onEnter command for the case where server fails to
78 // start.
79 //
80 // FIXME: refactor command registration code such that commands are
81 // **always** registered, even if the server does not start. Use API like
82 // this perhaps?
75 // 83 //
76 // This a horribly, horribly wrong way to deal with this problem. 84 // ```TypeScript
77 ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath); 85 // registerCommand(
86 // factory: (Ctx) => ((Ctx) => any),
87 // fallback: () => any = () => vscode.window.showErrorMessage(
88 // "rust-analyzer is not available"
89 // ),
90 // )
91 const defaultOnEnter = vscode.commands.registerCommand(
92 'rust-analyzer.onEnter',
93 () => vscode.commands.executeCommand('default:type', { text: '\n' }),
94 );
95 context.subscriptions.push(defaultOnEnter);
78 96
79 await setContextValue(RUST_PROJECT_CONTEXT_NAME, true); 97 await setContextValue(RUST_PROJECT_CONTEXT_NAME, true);
80 98
@@ -134,17 +152,6 @@ async function tryActivate(context: vscode.ExtensionContext) {
134 ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction); 152 ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction);
135 ctx.registerCommand('applyActionGroup', commands.applyActionGroup); 153 ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
136 ctx.registerCommand('gotoLocation', commands.gotoLocation); 154 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} 155}
149 156
150export async function deactivate() { 157export async function deactivate() {