diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-05-24 13:40:25 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-05-24 13:40:25 +0100 |
commit | 05fc97e31b1d04bf5d5885edd98a1510f0931a62 (patch) | |
tree | 27a99294690e75990250b0c1306ef99fad4558fa /editors | |
parent | 31a19148e967163ea9ebb42e341944be76ce8960 (diff) | |
parent | 5c0369b1d0c5351672f2a16e9a0d17beee84bcbe (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')
-rw-r--r-- | editors/code/src/client.ts | 15 | ||||
-rw-r--r-- | editors/code/src/ctx.ts | 14 | ||||
-rw-r--r-- | editors/code/src/main.ts | 81 |
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'; | |||
4 | import * as Is from 'vscode-languageclient/lib/common/utils/is'; | 4 | import * as Is from 'vscode-languageclient/lib/common/utils/is'; |
5 | import { assert } from './util'; | 5 | import { assert } from './util'; |
6 | import { WorkspaceEdit } from 'vscode'; | 6 | import { WorkspaceEdit } from 'vscode'; |
7 | import { Workspace } from './ctx'; | ||
7 | 8 | ||
8 | export interface Env { | 9 | export 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 | ||
26 | export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc.LanguageClient { | 27 | export 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'; | |||
7 | import { isRustEditor, RustEditor } from './util'; | 7 | import { isRustEditor, RustEditor } from './util'; |
8 | import { ServerStatusParams } from './lsp_ext'; | 8 | import { ServerStatusParams } from './lsp_ext'; |
9 | 9 | ||
10 | export type Workspace = | ||
11 | { | ||
12 | kind: 'Workspace Folder'; | ||
13 | folder: vscode.Uri; | ||
14 | } | ||
15 | | { | ||
16 | kind: 'Detached Files'; | ||
17 | files: vscode.TextDocument[]; | ||
18 | }; | ||
19 | |||
10 | export class Ctx { | 20 | export 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'; | |||
7 | import { activateInlayHints } from './inlay_hints'; | 7 | import { activateInlayHints } from './inlay_hints'; |
8 | import { Ctx } from './ctx'; | 8 | import { Ctx } from './ctx'; |
9 | import { Config } from './config'; | 9 | import { Config } from './config'; |
10 | import { log, assert, isValidExecutable } from './util'; | 10 | import { log, assert, isValidExecutable, isRustDocument } from './util'; |
11 | import { PersistentState } from './persistent_state'; | 11 | import { PersistentState } from './persistent_state'; |
12 | import { fetchRelease, download } from './net'; | 12 | import { fetchRelease, download } from './net'; |
13 | import { activateTaskProvider } from './tasks'; | 13 | import { activateTaskProvider } from './tasks'; |
@@ -28,26 +28,6 @@ export async function activate(context: vscode.ExtensionContext) { | |||
28 | } | 28 | } |
29 | 29 | ||
30 | async function tryActivate(context: vscode.ExtensionContext) { | 30 | async 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 | 76 | async 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 | ||
150 | export async function deactivate() { | 157 | export async function deactivate() { |