aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/commands
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src/commands')
-rw-r--r--editors/code/src/commands/analyzer_status.ts74
-rw-r--r--editors/code/src/commands/apply_source_change.ts54
-rw-r--r--editors/code/src/commands/cargo_watch.ts264
-rw-r--r--editors/code/src/commands/expand_macro.ts103
-rw-r--r--editors/code/src/commands/index.ts66
-rw-r--r--editors/code/src/commands/inlay_hints.ts115
-rw-r--r--editors/code/src/commands/join_lines.ts46
-rw-r--r--editors/code/src/commands/line_buffer.ts16
-rw-r--r--editors/code/src/commands/matching_brace.ts58
-rw-r--r--editors/code/src/commands/on_enter.ts53
-rw-r--r--editors/code/src/commands/parent_module.ts55
-rw-r--r--editors/code/src/commands/runnables.ts214
-rw-r--r--editors/code/src/commands/syntaxTree.ts76
-rw-r--r--editors/code/src/commands/syntax_tree.ts104
-rw-r--r--editors/code/src/commands/watch_status.ts63
15 files changed, 408 insertions, 953 deletions
diff --git a/editors/code/src/commands/analyzer_status.ts b/editors/code/src/commands/analyzer_status.ts
index 2777ced24..cfe7d1af0 100644
--- a/editors/code/src/commands/analyzer_status.ts
+++ b/editors/code/src/commands/analyzer_status.ts
@@ -1,45 +1,20 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import { Server } from '../server';
3 2
4const statusUri = vscode.Uri.parse('rust-analyzer-status://status'); 3import { Ctx, Cmd } from '../ctx';
5
6export class TextDocumentContentProvider
7 implements vscode.TextDocumentContentProvider {
8 public eventEmitter = new vscode.EventEmitter<vscode.Uri>();
9 public syntaxTree: string = 'Not available';
10
11 public provideTextDocumentContent(
12 _uri: vscode.Uri,
13 ): vscode.ProviderResult<string> {
14 const editor = vscode.window.activeTextEditor;
15 if (editor == null) {
16 return '';
17 }
18 return Server.client.sendRequest<string>(
19 'rust-analyzer/analyzerStatus',
20 null,
21 );
22 }
23
24 get onDidChange(): vscode.Event<vscode.Uri> {
25 return this.eventEmitter.event;
26 }
27}
28
29let poller: NodeJS.Timer | null = null;
30 4
31// Shows status of rust-analyzer (for debugging) 5// Shows status of rust-analyzer (for debugging)
6export function analyzerStatus(ctx: Ctx): Cmd {
7 let poller: NodeJS.Timer | null = null;
8 const tdcp = new TextDocumentContentProvider(ctx);
32 9
33export function makeCommand(context: vscode.ExtensionContext) { 10 ctx.pushCleanup(
34 const textDocumentContentProvider = new TextDocumentContentProvider();
35 context.subscriptions.push(
36 vscode.workspace.registerTextDocumentContentProvider( 11 vscode.workspace.registerTextDocumentContentProvider(
37 'rust-analyzer-status', 12 'rust-analyzer-status',
38 textDocumentContentProvider, 13 tdcp,
39 ), 14 ),
40 ); 15 );
41 16
42 context.subscriptions.push({ 17 ctx.pushCleanup({
43 dispose() { 18 dispose() {
44 if (poller != null) { 19 if (poller != null) {
45 clearInterval(poller); 20 clearInterval(poller);
@@ -49,12 +24,9 @@ export function makeCommand(context: vscode.ExtensionContext) {
49 24
50 return async function handle() { 25 return async function handle() {
51 if (poller == null) { 26 if (poller == null) {
52 poller = setInterval( 27 poller = setInterval(() => tdcp.eventEmitter.fire(tdcp.uri), 1000);
53 () => textDocumentContentProvider.eventEmitter.fire(statusUri),
54 1000,
55 );
56 } 28 }
57 const document = await vscode.workspace.openTextDocument(statusUri); 29 const document = await vscode.workspace.openTextDocument(tdcp.uri);
58 return vscode.window.showTextDocument( 30 return vscode.window.showTextDocument(
59 document, 31 document,
60 vscode.ViewColumn.Two, 32 vscode.ViewColumn.Two,
@@ -62,3 +34,31 @@ export function makeCommand(context: vscode.ExtensionContext) {
62 ); 34 );
63 }; 35 };
64} 36}
37
38class TextDocumentContentProvider
39 implements vscode.TextDocumentContentProvider {
40 private ctx: Ctx;
41 uri = vscode.Uri.parse('rust-analyzer-status://status');
42 eventEmitter = new vscode.EventEmitter<vscode.Uri>();
43
44 constructor(ctx: Ctx) {
45 this.ctx = ctx;
46 }
47
48 provideTextDocumentContent(
49 _uri: vscode.Uri,
50 ): vscode.ProviderResult<string> {
51 const editor = vscode.window.activeTextEditor;
52 const client = this.ctx.client;
53 if (!editor || !client) return '';
54
55 return client.sendRequest<string>(
56 'rust-analyzer/analyzerStatus',
57 null,
58 );
59 }
60
61 get onDidChange(): vscode.Event<vscode.Uri> {
62 return this.eventEmitter.event;
63 }
64}
diff --git a/editors/code/src/commands/apply_source_change.ts b/editors/code/src/commands/apply_source_change.ts
deleted file mode 100644
index 8167398b1..000000000
--- a/editors/code/src/commands/apply_source_change.ts
+++ /dev/null
@@ -1,54 +0,0 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3
4import { Server } from '../server';
5
6export interface SourceChange {
7 label: string;
8 workspaceEdit: lc.WorkspaceEdit;
9 cursorPosition?: lc.TextDocumentPositionParams;
10}
11
12export async function handle(change: SourceChange) {
13 const wsEdit = Server.client.protocol2CodeConverter.asWorkspaceEdit(
14 change.workspaceEdit,
15 );
16 let created;
17 let moved;
18 if (change.workspaceEdit.documentChanges) {
19 for (const docChange of change.workspaceEdit.documentChanges) {
20 if (lc.CreateFile.is(docChange)) {
21 created = docChange.uri;
22 } else if (lc.RenameFile.is(docChange)) {
23 moved = docChange.newUri;
24 }
25 }
26 }
27 const toOpen = created || moved;
28 const toReveal = change.cursorPosition;
29 await vscode.workspace.applyEdit(wsEdit);
30 if (toOpen) {
31 const toOpenUri = vscode.Uri.parse(toOpen);
32 const doc = await vscode.workspace.openTextDocument(toOpenUri);
33 await vscode.window.showTextDocument(doc);
34 } else if (toReveal) {
35 const uri = Server.client.protocol2CodeConverter.asUri(
36 toReveal.textDocument.uri,
37 );
38 const position = Server.client.protocol2CodeConverter.asPosition(
39 toReveal.position,
40 );
41 const editor = vscode.window.activeTextEditor;
42 if (!editor || editor.document.uri.toString() !== uri.toString()) {
43 return;
44 }
45 if (!editor.selection.isEmpty) {
46 return;
47 }
48 editor.selection = new vscode.Selection(position, position);
49 editor.revealRange(
50 new vscode.Range(position, position),
51 vscode.TextEditorRevealType.Default,
52 );
53 }
54}
diff --git a/editors/code/src/commands/cargo_watch.ts b/editors/code/src/commands/cargo_watch.ts
deleted file mode 100644
index ac62bdd48..000000000
--- a/editors/code/src/commands/cargo_watch.ts
+++ /dev/null
@@ -1,264 +0,0 @@
1import * as child_process from 'child_process';
2import * as path from 'path';
3import * as vscode from 'vscode';
4
5import { Server } from '../server';
6import { terminate } from '../utils/processes';
7import { LineBuffer } from './line_buffer';
8import { StatusDisplay } from './watch_status';
9
10import {
11 mapRustDiagnosticToVsCode,
12 RustDiagnostic,
13} from '../utils/diagnostics/rust';
14import SuggestedFixCollection from '../utils/diagnostics/SuggestedFixCollection';
15import { areDiagnosticsEqual } from '../utils/diagnostics/vscode';
16
17export async function registerCargoWatchProvider(
18 subscriptions: vscode.Disposable[],
19): Promise<CargoWatchProvider | undefined> {
20 let cargoExists = false;
21
22 // Check if the working directory is valid cargo root path
23 const cargoTomlPath = path.join(vscode.workspace.rootPath!, 'Cargo.toml');
24 const cargoTomlUri = vscode.Uri.file(cargoTomlPath);
25 const cargoTomlFileInfo = await vscode.workspace.fs.stat(cargoTomlUri);
26
27 if (cargoTomlFileInfo) {
28 cargoExists = true;
29 }
30
31 if (!cargoExists) {
32 vscode.window.showErrorMessage(
33 `Couldn\'t find \'Cargo.toml\' at ${cargoTomlPath}`,
34 );
35 return;
36 }
37
38 const provider = new CargoWatchProvider();
39 subscriptions.push(provider);
40 return provider;
41}
42
43export class CargoWatchProvider implements vscode.Disposable {
44 private readonly diagnosticCollection: vscode.DiagnosticCollection;
45 private readonly statusDisplay: StatusDisplay;
46 private readonly outputChannel: vscode.OutputChannel;
47
48 private suggestedFixCollection: SuggestedFixCollection;
49 private codeActionDispose: vscode.Disposable;
50
51 private cargoProcess?: child_process.ChildProcess;
52
53 constructor() {
54 this.diagnosticCollection = vscode.languages.createDiagnosticCollection(
55 'rustc',
56 );
57 this.statusDisplay = new StatusDisplay(
58 Server.config.cargoWatchOptions.command,
59 );
60 this.outputChannel = vscode.window.createOutputChannel(
61 'Cargo Watch Trace',
62 );
63
64 // Track `rustc`'s suggested fixes so we can convert them to code actions
65 this.suggestedFixCollection = new SuggestedFixCollection();
66 this.codeActionDispose = vscode.languages.registerCodeActionsProvider(
67 [{ scheme: 'file', language: 'rust' }],
68 this.suggestedFixCollection,
69 {
70 providedCodeActionKinds:
71 SuggestedFixCollection.PROVIDED_CODE_ACTION_KINDS,
72 },
73 );
74 }
75
76 public start() {
77 if (this.cargoProcess) {
78 vscode.window.showInformationMessage(
79 'Cargo Watch is already running',
80 );
81 return;
82 }
83
84 let args =
85 Server.config.cargoWatchOptions.command + ' --message-format json';
86 if (Server.config.cargoWatchOptions.allTargets) {
87 args += ' --all-targets';
88 }
89 if (Server.config.cargoWatchOptions.command.length > 0) {
90 // Excape the double quote string:
91 args += ' ' + Server.config.cargoWatchOptions.arguments;
92 }
93 // Windows handles arguments differently than the unix-likes, so we need to wrap the args in double quotes
94 if (process.platform === 'win32') {
95 args = '"' + args + '"';
96 }
97
98 const ignoreFlags = Server.config.cargoWatchOptions.ignore.reduce(
99 (flags, pattern) => [...flags, '--ignore', pattern],
100 [] as string[],
101 );
102
103 // Start the cargo watch with json message
104 this.cargoProcess = child_process.spawn(
105 'cargo',
106 ['watch', '-x', args, ...ignoreFlags],
107 {
108 stdio: ['ignore', 'pipe', 'pipe'],
109 cwd: vscode.workspace.rootPath,
110 windowsVerbatimArguments: true,
111 },
112 );
113
114 if (!this.cargoProcess) {
115 vscode.window.showErrorMessage('Cargo Watch failed to start');
116 return;
117 }
118
119 const stdoutData = new LineBuffer();
120 this.cargoProcess.stdout?.on('data', (s: string) => {
121 stdoutData.processOutput(s, line => {
122 this.logInfo(line);
123 try {
124 this.parseLine(line);
125 } catch (err) {
126 this.logError(`Failed to parse: ${err}, content : ${line}`);
127 }
128 });
129 });
130
131 const stderrData = new LineBuffer();
132 this.cargoProcess.stderr?.on('data', (s: string) => {
133 stderrData.processOutput(s, line => {
134 this.logError('Error on cargo-watch : {\n' + line + '}\n');
135 });
136 });
137
138 this.cargoProcess.on('error', (err: Error) => {
139 this.logError(
140 'Error on cargo-watch process : {\n' + err.message + '}\n',
141 );
142 });
143
144 this.logInfo('cargo-watch started.');
145 }
146
147 public stop() {
148 if (this.cargoProcess) {
149 this.cargoProcess.kill();
150 terminate(this.cargoProcess);
151 this.cargoProcess = undefined;
152 } else {
153 vscode.window.showInformationMessage('Cargo Watch is not running');
154 }
155 }
156
157 public dispose(): void {
158 this.stop();
159
160 this.diagnosticCollection.clear();
161 this.diagnosticCollection.dispose();
162 this.outputChannel.dispose();
163 this.statusDisplay.dispose();
164 this.codeActionDispose.dispose();
165 }
166
167 private logInfo(line: string) {
168 if (Server.config.cargoWatchOptions.trace === 'verbose') {
169 this.outputChannel.append(line);
170 }
171 }
172
173 private logError(line: string) {
174 if (
175 Server.config.cargoWatchOptions.trace === 'error' ||
176 Server.config.cargoWatchOptions.trace === 'verbose'
177 ) {
178 this.outputChannel.append(line);
179 }
180 }
181
182 private parseLine(line: string) {
183 if (line.startsWith('[Running')) {
184 this.diagnosticCollection.clear();
185 this.suggestedFixCollection.clear();
186 this.statusDisplay.show();
187 }
188
189 if (line.startsWith('[Finished running')) {
190 this.statusDisplay.hide();
191 }
192
193 interface CargoArtifact {
194 reason: string;
195 package_id: string;
196 }
197
198 // https://github.com/rust-lang/cargo/blob/master/src/cargo/util/machine_message.rs
199 interface CargoMessage {
200 reason: string;
201 package_id: string;
202 message: RustDiagnostic;
203 }
204
205 // cargo-watch itself output non json format
206 // Ignore these lines
207 let data: CargoMessage;
208 try {
209 data = JSON.parse(line.trim());
210 } catch (error) {
211 this.logError(`Fail to parse to json : { ${error} }`);
212 return;
213 }
214
215 if (data.reason === 'compiler-artifact') {
216 const msg = data as CargoArtifact;
217
218 // The format of the package_id is "{name} {version} ({source_id})",
219 // https://github.com/rust-lang/cargo/blob/37ad03f86e895bb80b474c1c088322634f4725f5/src/cargo/core/package_id.rs#L53
220 this.statusDisplay.packageName = msg.package_id.split(' ')[0];
221 } else if (data.reason === 'compiler-message') {
222 const msg = data.message as RustDiagnostic;
223
224 const mapResult = mapRustDiagnosticToVsCode(msg);
225 if (!mapResult) {
226 return;
227 }
228
229 const { location, diagnostic, suggestedFixes } = mapResult;
230 const fileUri = location.uri;
231
232 const diagnostics: vscode.Diagnostic[] = [
233 ...(this.diagnosticCollection!.get(fileUri) || []),
234 ];
235
236 // If we're building multiple targets it's possible we've already seen this diagnostic
237 const isDuplicate = diagnostics.some(d =>
238 areDiagnosticsEqual(d, diagnostic),
239 );
240 if (isDuplicate) {
241 return;
242 }
243
244 diagnostics.push(diagnostic);
245 this.diagnosticCollection!.set(fileUri, diagnostics);
246
247 if (suggestedFixes.length) {
248 for (const suggestedFix of suggestedFixes) {
249 this.suggestedFixCollection.addSuggestedFixForDiagnostic(
250 suggestedFix,
251 diagnostic,
252 );
253 }
254
255 // Have VsCode query us for the code actions
256 vscode.commands.executeCommand(
257 'vscode.executeCodeActionProvider',
258 fileUri,
259 diagnostic.range,
260 );
261 }
262 }
263 }
264}
diff --git a/editors/code/src/commands/expand_macro.ts b/editors/code/src/commands/expand_macro.ts
index 17c78280a..dcdde78af 100644
--- a/editors/code/src/commands/expand_macro.ts
+++ b/editors/code/src/commands/expand_macro.ts
@@ -1,60 +1,23 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import { Position, TextDocumentIdentifier } from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import { Server } from '../server';
4 3
5export const expandMacroUri = vscode.Uri.parse( 4import { Ctx, Cmd } from '../ctx';
6 'rust-analyzer://expandMacro/[EXPANSION].rs',
7);
8
9export class ExpandMacroContentProvider
10 implements vscode.TextDocumentContentProvider {
11 public eventEmitter = new vscode.EventEmitter<vscode.Uri>();
12
13 public provideTextDocumentContent(
14 _uri: vscode.Uri,
15 ): vscode.ProviderResult<string> {
16 async function handle() {
17 const editor = vscode.window.activeTextEditor;
18 if (editor == null) {
19 return '';
20 }
21
22 const position = editor.selection.active;
23 const request: MacroExpandParams = {
24 textDocument: { uri: editor.document.uri.toString() },
25 position,
26 };
27 const expanded = await Server.client.sendRequest<ExpandedMacro>(
28 'rust-analyzer/expandMacro',
29 request,
30 );
31
32 if (expanded == null) {
33 return 'Not available';
34 }
35
36 return code_format(expanded);
37 }
38
39 return handle();
40 }
41
42 get onDidChange(): vscode.Event<vscode.Uri> {
43 return this.eventEmitter.event;
44 }
45}
46 5
47// Opens the virtual file that will show the syntax tree 6// Opens the virtual file that will show the syntax tree
48// 7//
49// The contents of the file come from the `TextDocumentContentProvider` 8// The contents of the file come from the `TextDocumentContentProvider`
50export function createHandle(provider: ExpandMacroContentProvider) { 9export function expandMacro(ctx: Ctx): Cmd {
51 return async () => { 10 const tdcp = new TextDocumentContentProvider(ctx);
52 const uri = expandMacroUri; 11 ctx.pushCleanup(
53 12 vscode.workspace.registerTextDocumentContentProvider(
54 const document = await vscode.workspace.openTextDocument(uri); 13 'rust-analyzer',
55 14 tdcp,
56 provider.eventEmitter.fire(uri); 15 ),
16 );
57 17
18 return async () => {
19 const document = await vscode.workspace.openTextDocument(tdcp.uri);
20 tdcp.eventEmitter.fire(tdcp.uri);
58 return vscode.window.showTextDocument( 21 return vscode.window.showTextDocument(
59 document, 22 document,
60 vscode.ViewColumn.Two, 23 vscode.ViewColumn.Two,
@@ -63,11 +26,6 @@ export function createHandle(provider: ExpandMacroContentProvider) {
63 }; 26 };
64} 27}
65 28
66interface MacroExpandParams {
67 textDocument: TextDocumentIdentifier;
68 position: Position;
69}
70
71interface ExpandedMacro { 29interface ExpandedMacro {
72 name: string; 30 name: string;
73 expansion: string; 31 expansion: string;
@@ -81,3 +39,38 @@ function code_format(expanded: ExpandedMacro): string {
81 39
82 return result; 40 return result;
83} 41}
42
43class TextDocumentContentProvider
44 implements vscode.TextDocumentContentProvider {
45 private ctx: Ctx;
46 uri = vscode.Uri.parse('rust-analyzer://expandMacro/[EXPANSION].rs');
47 eventEmitter = new vscode.EventEmitter<vscode.Uri>();
48
49 constructor(ctx: Ctx) {
50 this.ctx = ctx;
51 }
52
53 async provideTextDocumentContent(_uri: vscode.Uri): Promise<string> {
54 const editor = vscode.window.activeTextEditor;
55 const client = this.ctx.client;
56 if (!editor || !client) return '';
57
58 const position = editor.selection.active;
59 const request: lc.TextDocumentPositionParams = {
60 textDocument: { uri: editor.document.uri.toString() },
61 position,
62 };
63 const expanded = await client.sendRequest<ExpandedMacro>(
64 'rust-analyzer/expandMacro',
65 request,
66 );
67
68 if (expanded == null) return 'Not available';
69
70 return code_format(expanded);
71 }
72
73 get onDidChange(): vscode.Event<vscode.Uri> {
74 return this.eventEmitter.event;
75 }
76}
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index 13a696758..9a1697dcb 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -1,23 +1,63 @@
1import * as analyzerStatus from './analyzer_status'; 1import * as vscode from 'vscode';
2import * as applySourceChange from './apply_source_change'; 2import * as lc from 'vscode-languageclient';
3import * as expandMacro from './expand_macro'; 3
4import * as inlayHints from './inlay_hints'; 4import { Ctx, Cmd } from '../ctx';
5import * as joinLines from './join_lines'; 5import * as sourceChange from '../source_change';
6import * as matchingBrace from './matching_brace'; 6
7import * as onEnter from './on_enter'; 7import { analyzerStatus } from './analyzer_status';
8import * as parentModule from './parent_module'; 8import { matchingBrace } from './matching_brace';
9import * as runnables from './runnables'; 9import { joinLines } from './join_lines';
10import * as syntaxTree from './syntaxTree'; 10import { onEnter } from './on_enter';
11import { parentModule } from './parent_module';
12import { syntaxTree } from './syntax_tree';
13import { expandMacro } from './expand_macro';
14import { run, runSingle } from './runnables';
15
16function collectGarbage(ctx: Ctx): Cmd {
17 return async () => {
18 ctx.client?.sendRequest<null>('rust-analyzer/collectGarbage', null);
19 };
20}
21
22function showReferences(ctx: Ctx): Cmd {
23 return (uri: string, position: lc.Position, locations: lc.Location[]) => {
24 let client = ctx.client;
25 if (client) {
26 vscode.commands.executeCommand(
27 'editor.action.showReferences',
28 vscode.Uri.parse(uri),
29 client.protocol2CodeConverter.asPosition(position),
30 locations.map(client.protocol2CodeConverter.asLocation),
31 );
32 }
33 };
34}
35
36function applySourceChange(ctx: Ctx): Cmd {
37 return async (change: sourceChange.SourceChange) => {
38 sourceChange.applySourceChange(ctx, change);
39 };
40}
41
42function reload(ctx: Ctx): Cmd {
43 return async () => {
44 vscode.window.showInformationMessage('Reloading rust-analyzer...');
45 await ctx.restartServer();
46 };
47}
11 48
12export { 49export {
13 analyzerStatus, 50 analyzerStatus,
14 applySourceChange,
15 expandMacro, 51 expandMacro,
16 joinLines, 52 joinLines,
17 matchingBrace, 53 matchingBrace,
18 parentModule, 54 parentModule,
19 runnables,
20 syntaxTree, 55 syntaxTree,
21 onEnter, 56 onEnter,
22 inlayHints, 57 collectGarbage,
58 run,
59 runSingle,
60 showReferences,
61 applySourceChange,
62 reload
23}; 63};
diff --git a/editors/code/src/commands/inlay_hints.ts b/editors/code/src/commands/inlay_hints.ts
deleted file mode 100644
index ac7dcce60..000000000
--- a/editors/code/src/commands/inlay_hints.ts
+++ /dev/null
@@ -1,115 +0,0 @@
1import * as vscode from 'vscode';
2import { Range, TextDocumentChangeEvent, TextEditor } from 'vscode';
3import { TextDocumentIdentifier } from 'vscode-languageclient';
4import { Server } from '../server';
5
6interface InlayHintsParams {
7 textDocument: TextDocumentIdentifier;
8}
9
10interface InlayHint {
11 range: Range;
12 kind: string;
13 label: string;
14}
15
16const typeHintDecorationType = vscode.window.createTextEditorDecorationType({
17 after: {
18 color: new vscode.ThemeColor('ralsp.inlayHint'),
19 },
20});
21
22export class HintsUpdater {
23 private displayHints = true;
24
25 public async toggleHintsDisplay(displayHints: boolean): Promise<void> {
26 if (this.displayHints !== displayHints) {
27 this.displayHints = displayHints;
28 return this.refreshVisibleEditorsHints(
29 displayHints ? undefined : [],
30 );
31 }
32 }
33
34 public async refreshHintsForVisibleEditors(
35 cause?: TextDocumentChangeEvent,
36 ): Promise<void> {
37 if (!this.displayHints) {
38 return;
39 }
40 if (
41 cause !== undefined &&
42 (cause.contentChanges.length === 0 ||
43 !this.isRustDocument(cause.document))
44 ) {
45 return;
46 }
47 return this.refreshVisibleEditorsHints();
48 }
49
50 private async refreshVisibleEditorsHints(
51 newDecorations?: vscode.DecorationOptions[],
52 ) {
53 const promises: Array<Promise<void>> = [];
54
55 for (const rustEditor of vscode.window.visibleTextEditors.filter(
56 editor => this.isRustDocument(editor.document),
57 )) {
58 if (newDecorations !== undefined) {
59 promises.push(
60 Promise.resolve(
61 rustEditor.setDecorations(
62 typeHintDecorationType,
63 newDecorations,
64 ),
65 ),
66 );
67 } else {
68 promises.push(this.updateDecorationsFromServer(rustEditor));
69 }
70 }
71
72 for (const promise of promises) {
73 await promise;
74 }
75 }
76
77 private isRustDocument(document: vscode.TextDocument): boolean {
78 return document && document.languageId === 'rust';
79 }
80
81 private async updateDecorationsFromServer(
82 editor: TextEditor,
83 ): Promise<void> {
84 const newHints = await this.queryHints(editor.document.uri.toString());
85 if (newHints !== null) {
86 const newDecorations = newHints.map(hint => ({
87 range: hint.range,
88 renderOptions: {
89 after: {
90 contentText: `: ${hint.label}`,
91 },
92 },
93 }));
94 return editor.setDecorations(
95 typeHintDecorationType,
96 newDecorations,
97 );
98 }
99 }
100
101 private async queryHints(documentUri: string): Promise<InlayHint[] | null> {
102 const request: InlayHintsParams = {
103 textDocument: { uri: documentUri },
104 };
105 const client = Server.client;
106 return client
107 .onReady()
108 .then(() =>
109 client.sendRequest<InlayHint[] | null>(
110 'rust-analyzer/inlayHints',
111 request,
112 ),
113 );
114 }
115}
diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts
index 134ddc801..7b08c3255 100644
--- a/editors/code/src/commands/join_lines.ts
+++ b/editors/code/src/commands/join_lines.ts
@@ -1,29 +1,27 @@
1import * as vscode from 'vscode'; 1import * as lc from 'vscode-languageclient';
2 2
3import { Range, TextDocumentIdentifier } from 'vscode-languageclient'; 3import { Ctx, Cmd } from '../ctx';
4import { Server } from '../server'; 4import { applySourceChange, SourceChange } from '../source_change';
5import {
6 handle as applySourceChange,
7 SourceChange,
8} from './apply_source_change';
9 5
10interface JoinLinesParams { 6export function joinLines(ctx: Ctx): Cmd {
11 textDocument: TextDocumentIdentifier; 7 return async () => {
12 range: Range; 8 const editor = ctx.activeRustEditor;
13} 9 const client = ctx.client;
10 if (!editor || !client) return;
14 11
15export async function handle() { 12 const request: JoinLinesParams = {
16 const editor = vscode.window.activeTextEditor; 13 range: client.code2ProtocolConverter.asRange(editor.selection),
17 if (editor == null || editor.document.languageId !== 'rust') { 14 textDocument: { uri: editor.document.uri.toString() },
18 return; 15 };
19 } 16 const change = await client.sendRequest<SourceChange>(
20 const request: JoinLinesParams = { 17 'rust-analyzer/joinLines',
21 range: Server.client.code2ProtocolConverter.asRange(editor.selection), 18 request,
22 textDocument: { uri: editor.document.uri.toString() }, 19 );
20 await applySourceChange(ctx, change);
23 }; 21 };
24 const change = await Server.client.sendRequest<SourceChange>( 22}
25 'rust-analyzer/joinLines', 23
26 request, 24interface JoinLinesParams {
27 ); 25 textDocument: lc.TextDocumentIdentifier;
28 await applySourceChange(change); 26 range: lc.Range;
29} 27}
diff --git a/editors/code/src/commands/line_buffer.ts b/editors/code/src/commands/line_buffer.ts
deleted file mode 100644
index fb5b9f7f2..000000000
--- a/editors/code/src/commands/line_buffer.ts
+++ /dev/null
@@ -1,16 +0,0 @@
1export class LineBuffer {
2 private outBuffer: string = '';
3
4 public processOutput(chunk: string, cb: (line: string) => void) {
5 this.outBuffer += chunk;
6 let eolIndex = this.outBuffer.indexOf('\n');
7 while (eolIndex >= 0) {
8 // line includes the EOL
9 const line = this.outBuffer.slice(0, eolIndex + 1);
10 cb(line);
11 this.outBuffer = this.outBuffer.slice(eolIndex + 1);
12
13 eolIndex = this.outBuffer.indexOf('\n');
14 }
15 }
16}
diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts
index 364208cc7..7c58bb7e7 100644
--- a/editors/code/src/commands/matching_brace.ts
+++ b/editors/code/src/commands/matching_brace.ts
@@ -1,34 +1,36 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
2 3
3import { Position, TextDocumentIdentifier } from 'vscode-languageclient'; 4import { Ctx, Cmd } from '../ctx';
4import { Server } from '../server';
5 5
6interface FindMatchingBraceParams { 6export function matchingBrace(ctx: Ctx): Cmd {
7 textDocument: TextDocumentIdentifier; 7 return async () => {
8 offsets: Position[]; 8 const editor = ctx.activeRustEditor;
9} 9 const client = ctx.client;
10 if (!editor || !client) return;
10 11
11export async function handle() { 12 const request: FindMatchingBraceParams = {
12 const editor = vscode.window.activeTextEditor; 13 textDocument: { uri: editor.document.uri.toString() },
13 if (editor == null || editor.document.languageId !== 'rust') { 14 offsets: editor.selections.map(s =>
14 return; 15 client.code2ProtocolConverter.asPosition(s.active),
15 } 16 ),
16 const request: FindMatchingBraceParams = { 17 };
17 textDocument: { uri: editor.document.uri.toString() }, 18 const response = await client.sendRequest<lc.Position[]>(
18 offsets: editor.selections.map(s => { 19 'rust-analyzer/findMatchingBrace',
19 return Server.client.code2ProtocolConverter.asPosition(s.active); 20 request,
20 }),
21 };
22 const response = await Server.client.sendRequest<Position[]>(
23 'rust-analyzer/findMatchingBrace',
24 request,
25 );
26 editor.selections = editor.selections.map((sel, idx) => {
27 const active = Server.client.protocol2CodeConverter.asPosition(
28 response[idx],
29 ); 21 );
30 const anchor = sel.isEmpty ? active : sel.anchor; 22 editor.selections = editor.selections.map((sel, idx) => {
31 return new vscode.Selection(anchor, active); 23 const active = client.protocol2CodeConverter.asPosition(
32 }); 24 response[idx],
33 editor.revealRange(editor.selection); 25 );
26 const anchor = sel.isEmpty ? active : sel.anchor;
27 return new vscode.Selection(anchor, active);
28 });
29 editor.revealRange(editor.selection);
30 };
31}
32
33interface FindMatchingBraceParams {
34 textDocument: lc.TextDocumentIdentifier;
35 offsets: lc.Position[];
34} 36}
diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts
index 772c64b3c..6f61883cd 100644
--- a/editors/code/src/commands/on_enter.ts
+++ b/editors/code/src/commands/on_enter.ts
@@ -1,33 +1,28 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 1import * as lc from 'vscode-languageclient';
3import { Server } from '../server';
4import {
5 handle as applySourceChange,
6 SourceChange,
7} from './apply_source_change';
8 2
9export async function handle(event: { text: string }): Promise<boolean> { 3import { applySourceChange, SourceChange } from '../source_change';
10 const editor = vscode.window.activeTextEditor; 4import { Cmd, Ctx } from '../ctx';
11 if ( 5
12 editor == null || 6export function onEnter(ctx: Ctx): Cmd {
13 editor.document.languageId !== 'rust' || 7 return async (event: { text: string }) => {
14 event.text !== '\n' 8 const editor = ctx.activeRustEditor;
15 ) { 9 const client = ctx.client;
16 return false; 10 if (!editor || event.text !== '\n') return false;
17 } 11 if (!client) return false;
18 const request: lc.TextDocumentPositionParams = { 12
19 textDocument: { uri: editor.document.uri.toString() }, 13 const request: lc.TextDocumentPositionParams = {
20 position: Server.client.code2ProtocolConverter.asPosition( 14 textDocument: { uri: editor.document.uri.toString() },
21 editor.selection.active, 15 position: client.code2ProtocolConverter.asPosition(
22 ), 16 editor.selection.active,
17 ),
18 };
19 const change = await client.sendRequest<undefined | SourceChange>(
20 'rust-analyzer/onEnter',
21 request,
22 );
23 if (!change) return false;
24
25 await applySourceChange(ctx, change);
26 return true;
23 }; 27 };
24 const change = await Server.client.sendRequest<undefined | SourceChange>(
25 'rust-analyzer/onEnter',
26 request,
27 );
28 if (!change) {
29 return false;
30 }
31 await applySourceChange(change);
32 return true;
33} 28}
diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts
index ad49e1bdb..bf40b4021 100644
--- a/editors/code/src/commands/parent_module.ts
+++ b/editors/code/src/commands/parent_module.ts
@@ -1,32 +1,33 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2
3import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
4import { Server } from '../server';
5 3
6export async function handle() { 4import { Ctx, Cmd } from '../ctx';
7 const editor = vscode.window.activeTextEditor; 5
8 if (editor == null || editor.document.languageId !== 'rust') { 6export function parentModule(ctx: Ctx): Cmd {
9 return; 7 return async () => {
10 } 8 const editor = ctx.activeRustEditor;
11 const request: lc.TextDocumentPositionParams = { 9 const client = ctx.client;
12 textDocument: { uri: editor.document.uri.toString() }, 10 if (!editor || !client) return;
13 position: Server.client.code2ProtocolConverter.asPosition(
14 editor.selection.active,
15 ),
16 };
17 const response = await Server.client.sendRequest<lc.Location[]>(
18 'rust-analyzer/parentModule',
19 request,
20 );
21 const loc = response[0];
22 if (loc == null) {
23 return;
24 }
25 const uri = Server.client.protocol2CodeConverter.asUri(loc.uri);
26 const range = Server.client.protocol2CodeConverter.asRange(loc.range);
27 11
28 const doc = await vscode.workspace.openTextDocument(uri); 12 const request: lc.TextDocumentPositionParams = {
29 const e = await vscode.window.showTextDocument(doc); 13 textDocument: { uri: editor.document.uri.toString() },
30 e.selection = new vscode.Selection(range.start, range.start); 14 position: client.code2ProtocolConverter.asPosition(
31 e.revealRange(range, vscode.TextEditorRevealType.InCenter); 15 editor.selection.active,
16 ),
17 };
18 const response = await client.sendRequest<lc.Location[]>(
19 'rust-analyzer/parentModule',
20 request,
21 );
22 const loc = response[0];
23 if (loc == null) return;
24
25 const uri = client.protocol2CodeConverter.asUri(loc.uri);
26 const range = client.protocol2CodeConverter.asRange(loc.range);
27
28 const doc = await vscode.workspace.openTextDocument(uri);
29 const e = await vscode.window.showTextDocument(doc);
30 e.selection = new vscode.Selection(range.start, range.start);
31 e.revealRange(range, vscode.TextEditorRevealType.InCenter);
32 };
32} 33}
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index cf980e257..7919997ce 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -1,11 +1,68 @@
1import * as child_process from 'child_process';
2
3import * as util from 'util';
4import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
5import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
6 3
7import { Server } from '../server'; 4import { Ctx, Cmd } from '../ctx';
8import { CargoWatchProvider, registerCargoWatchProvider } from './cargo_watch'; 5
6export function run(ctx: Ctx): Cmd {
7 let prevRunnable: RunnableQuickPick | undefined;
8
9 return async () => {
10 const editor = ctx.activeRustEditor;
11 const client = ctx.client;
12 if (!editor || !client) return;
13
14 const textDocument: lc.TextDocumentIdentifier = {
15 uri: editor.document.uri.toString(),
16 };
17 const params: RunnablesParams = {
18 textDocument,
19 position: client.code2ProtocolConverter.asPosition(
20 editor.selection.active,
21 ),
22 };
23 const runnables = await client.sendRequest<Runnable[]>(
24 'rust-analyzer/runnables',
25 params,
26 );
27 const items: RunnableQuickPick[] = [];
28 if (prevRunnable) {
29 items.push(prevRunnable);
30 }
31 for (const r of runnables) {
32 if (
33 prevRunnable &&
34 JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)
35 ) {
36 continue;
37 }
38 items.push(new RunnableQuickPick(r));
39 }
40 const item = await vscode.window.showQuickPick(items);
41 if (!item) return;
42
43 item.detail = 'rerun';
44 prevRunnable = item;
45 const task = createTask(item.runnable);
46 return await vscode.tasks.executeTask(task);
47 };
48}
49
50export function runSingle(ctx: Ctx): Cmd {
51 return async (runnable: Runnable) => {
52 const editor = ctx.activeRustEditor;
53 if (!editor) return;
54
55 const task = createTask(runnable);
56 task.group = vscode.TaskGroup.Build;
57 task.presentationOptions = {
58 reveal: vscode.TaskRevealKind.Always,
59 panel: vscode.TaskPanelKind.Dedicated,
60 clear: true,
61 };
62
63 return vscode.tasks.executeTask(task);
64 };
65}
9 66
10interface RunnablesParams { 67interface RunnablesParams {
11 textDocument: lc.TextDocumentIdentifier; 68 textDocument: lc.TextDocumentIdentifier;
@@ -71,150 +128,3 @@ function createTask(spec: Runnable): vscode.Task {
71 t.presentationOptions.clear = true; 128 t.presentationOptions.clear = true;
72 return t; 129 return t;
73} 130}
74
75let prevRunnable: RunnableQuickPick | undefined;
76export async function handle(): Promise<vscode.TaskExecution | undefined> {
77 const editor = vscode.window.activeTextEditor;
78 if (editor == null || editor.document.languageId !== 'rust') {
79 return;
80 }
81 const textDocument: lc.TextDocumentIdentifier = {
82 uri: editor.document.uri.toString(),
83 };
84 const params: RunnablesParams = {
85 textDocument,
86 position: Server.client.code2ProtocolConverter.asPosition(
87 editor.selection.active,
88 ),
89 };
90 const runnables = await Server.client.sendRequest<Runnable[]>(
91 'rust-analyzer/runnables',
92 params,
93 );
94 const items: RunnableQuickPick[] = [];
95 if (prevRunnable) {
96 items.push(prevRunnable);
97 }
98 for (const r of runnables) {
99 if (
100 prevRunnable &&
101 JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)
102 ) {
103 continue;
104 }
105 items.push(new RunnableQuickPick(r));
106 }
107 const item = await vscode.window.showQuickPick(items);
108 if (!item) {
109 return;
110 }
111
112 item.detail = 'rerun';
113 prevRunnable = item;
114 const task = createTask(item.runnable);
115 return await vscode.tasks.executeTask(task);
116}
117
118export async function handleSingle(runnable: Runnable) {
119 const editor = vscode.window.activeTextEditor;
120 if (editor == null || editor.document.languageId !== 'rust') {
121 return;
122 }
123
124 const task = createTask(runnable);
125 task.group = vscode.TaskGroup.Build;
126 task.presentationOptions = {
127 reveal: vscode.TaskRevealKind.Always,
128 panel: vscode.TaskPanelKind.Dedicated,
129 clear: true,
130 };
131
132 return vscode.tasks.executeTask(task);
133}
134
135/**
136 * Interactively asks the user whether we should run `cargo check` in order to
137 * provide inline diagnostics; the user is met with a series of dialog boxes
138 * that, when accepted, allow us to `cargo install cargo-watch` and then run it.
139 */
140export async function interactivelyStartCargoWatch(
141 context: vscode.ExtensionContext,
142): Promise<CargoWatchProvider | undefined> {
143 if (Server.config.cargoWatchOptions.enableOnStartup === 'disabled') {
144 return;
145 }
146
147 if (Server.config.cargoWatchOptions.enableOnStartup === 'ask') {
148 const watch = await vscode.window.showInformationMessage(
149 'Start watching changes with cargo? (Executes `cargo watch`, provides inline diagnostics)',
150 'yes',
151 'no',
152 );
153 if (watch !== 'yes') {
154 return;
155 }
156 }
157
158 return startCargoWatch(context);
159}
160
161export async function startCargoWatch(
162 context: vscode.ExtensionContext,
163): Promise<CargoWatchProvider | undefined> {
164 const execPromise = util.promisify(child_process.exec);
165
166 const { stderr, code = 0 } = await execPromise(
167 'cargo watch --version',
168 ).catch(e => e);
169
170 if (stderr.includes('no such subcommand: `watch`')) {
171 const msg =
172 'The `cargo-watch` subcommand is not installed. Install? (takes ~1-2 minutes)';
173 const install = await vscode.window.showInformationMessage(
174 msg,
175 'yes',
176 'no',
177 );
178 if (install !== 'yes') {
179 return;
180 }
181
182 const label = 'install-cargo-watch';
183 const taskFinished = new Promise((resolve, _reject) => {
184 const disposable = vscode.tasks.onDidEndTask(({ execution }) => {
185 if (execution.task.name === label) {
186 disposable.dispose();
187 resolve();
188 }
189 });
190 });
191
192 vscode.tasks.executeTask(
193 createTask({
194 label,
195 bin: 'cargo',
196 args: ['install', 'cargo-watch'],
197 env: {},
198 }),
199 );
200 await taskFinished;
201 const output = await execPromise('cargo watch --version').catch(e => e);
202 if (output.stderr !== '') {
203 vscode.window.showErrorMessage(
204 `Couldn't install \`cargo-\`watch: ${output.stderr}`,
205 );
206 return;
207 }
208 } else if (code !== 0) {
209 vscode.window.showErrorMessage(
210 `\`cargo watch\` failed with ${code}: ${stderr}`,
211 );
212 return;
213 }
214
215 const provider = await registerCargoWatchProvider(context.subscriptions);
216 if (provider) {
217 provider.start();
218 }
219 return provider;
220}
diff --git a/editors/code/src/commands/syntaxTree.ts b/editors/code/src/commands/syntaxTree.ts
deleted file mode 100644
index 89a80550c..000000000
--- a/editors/code/src/commands/syntaxTree.ts
+++ /dev/null
@@ -1,76 +0,0 @@
1import * as vscode from 'vscode';
2import { Range, TextDocumentIdentifier } from 'vscode-languageclient';
3
4import { Server } from '../server';
5
6export const syntaxTreeUri = vscode.Uri.parse('rust-analyzer://syntaxtree');
7
8export class SyntaxTreeContentProvider
9 implements vscode.TextDocumentContentProvider {
10 public eventEmitter = new vscode.EventEmitter<vscode.Uri>();
11 public syntaxTree: string = 'Not available';
12
13 public provideTextDocumentContent(
14 uri: vscode.Uri,
15 ): vscode.ProviderResult<string> {
16 const editor = vscode.window.activeTextEditor;
17 if (editor == null) {
18 return '';
19 }
20
21 let range: Range | undefined;
22
23 // When the range based query is enabled we take the range of the selection
24 if (uri.query === 'range=true') {
25 range = editor.selection.isEmpty
26 ? undefined
27 : Server.client.code2ProtocolConverter.asRange(
28 editor.selection,
29 );
30 }
31
32 const request: SyntaxTreeParams = {
33 textDocument: { uri: editor.document.uri.toString() },
34 range,
35 };
36 return Server.client.sendRequest<SyntaxTreeResult>(
37 'rust-analyzer/syntaxTree',
38 request,
39 );
40 }
41
42 get onDidChange(): vscode.Event<vscode.Uri> {
43 return this.eventEmitter.event;
44 }
45}
46
47interface SyntaxTreeParams {
48 textDocument: TextDocumentIdentifier;
49 range?: Range;
50}
51
52type SyntaxTreeResult = string;
53
54// Opens the virtual file that will show the syntax tree
55//
56// The contents of the file come from the `TextDocumentContentProvider`
57export function createHandle(provider: SyntaxTreeContentProvider) {
58 return async () => {
59 const editor = vscode.window.activeTextEditor;
60 const rangeEnabled = !!(editor && !editor.selection.isEmpty);
61
62 const uri = rangeEnabled
63 ? vscode.Uri.parse(`${syntaxTreeUri.toString()}?range=true`)
64 : syntaxTreeUri;
65
66 const document = await vscode.workspace.openTextDocument(uri);
67
68 provider.eventEmitter.fire(uri);
69
70 return vscode.window.showTextDocument(
71 document,
72 vscode.ViewColumn.Two,
73 true,
74 );
75 };
76}
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
new file mode 100644
index 000000000..02ea9f166
--- /dev/null
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -0,0 +1,104 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3
4import { Ctx, Cmd } from '../ctx';
5
6// Opens the virtual file that will show the syntax tree
7//
8// The contents of the file come from the `TextDocumentContentProvider`
9export function syntaxTree(ctx: Ctx): Cmd {
10 const tdcp = new TextDocumentContentProvider(ctx);
11
12 ctx.pushCleanup(
13 vscode.workspace.registerTextDocumentContentProvider(
14 'rust-analyzer',
15 tdcp,
16 ),
17 );
18
19 vscode.workspace.onDidChangeTextDocument(
20 (event: vscode.TextDocumentChangeEvent) => {
21 const doc = event.document;
22 if (doc.languageId !== 'rust') return;
23 afterLs(() => tdcp.eventEmitter.fire(tdcp.uri));
24 },
25 ctx.subscriptions,
26 );
27
28 vscode.window.onDidChangeActiveTextEditor(
29 (editor: vscode.TextEditor | undefined) => {
30 if (!editor || editor.document.languageId !== 'rust') return;
31 tdcp.eventEmitter.fire(tdcp.uri);
32 },
33 ctx.subscriptions,
34 );
35
36 return async () => {
37 const editor = vscode.window.activeTextEditor;
38 const rangeEnabled = !!(editor && !editor.selection.isEmpty);
39
40 const uri = rangeEnabled
41 ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`)
42 : tdcp.uri;
43
44 const document = await vscode.workspace.openTextDocument(uri);
45
46 tdcp.eventEmitter.fire(uri);
47
48 return vscode.window.showTextDocument(
49 document,
50 vscode.ViewColumn.Two,
51 true,
52 );
53 };
54}
55
56// We need to order this after LS updates, but there's no API for that.
57// Hence, good old setTimeout.
58function afterLs(f: () => any) {
59 setTimeout(f, 10);
60}
61
62interface SyntaxTreeParams {
63 textDocument: lc.TextDocumentIdentifier;
64 range?: lc.Range;
65}
66
67class TextDocumentContentProvider
68 implements vscode.TextDocumentContentProvider {
69 private ctx: Ctx;
70 uri = vscode.Uri.parse('rust-analyzer://syntaxtree');
71 eventEmitter = new vscode.EventEmitter<vscode.Uri>();
72
73 constructor(ctx: Ctx) {
74 this.ctx = ctx;
75 }
76
77 provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult<string> {
78 const editor = vscode.window.activeTextEditor;
79 const client = this.ctx.client;
80 if (!editor || !client) return '';
81
82 let range: lc.Range | undefined;
83
84 // When the range based query is enabled we take the range of the selection
85 if (uri.query === 'range=true') {
86 range = editor.selection.isEmpty
87 ? undefined
88 : client.code2ProtocolConverter.asRange(editor.selection);
89 }
90
91 const request: SyntaxTreeParams = {
92 textDocument: { uri: editor.document.uri.toString() },
93 range,
94 };
95 return client.sendRequest<string>(
96 'rust-analyzer/syntaxTree',
97 request,
98 );
99 }
100
101 get onDidChange(): vscode.Event<vscode.Uri> {
102 return this.eventEmitter.event;
103 }
104}
diff --git a/editors/code/src/commands/watch_status.ts b/editors/code/src/commands/watch_status.ts
deleted file mode 100644
index 8d64394c7..000000000
--- a/editors/code/src/commands/watch_status.ts
+++ /dev/null
@@ -1,63 +0,0 @@
1import * as vscode from 'vscode';
2
3const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
4
5export class StatusDisplay implements vscode.Disposable {
6 public packageName?: string;
7
8 private i = 0;
9 private statusBarItem: vscode.StatusBarItem;
10 private command: string;
11 private timer?: NodeJS.Timeout;
12
13 constructor(command: string) {
14 this.statusBarItem = vscode.window.createStatusBarItem(
15 vscode.StatusBarAlignment.Left,
16 10,
17 );
18 this.command = command;
19 this.statusBarItem.hide();
20 }
21
22 public show() {
23 this.packageName = undefined;
24
25 this.timer =
26 this.timer ||
27 setInterval(() => {
28 if (this.packageName) {
29 this.statusBarItem!.text = `cargo ${this.command} [${
30 this.packageName
31 }] ${this.frame()}`;
32 } else {
33 this.statusBarItem!.text = `cargo ${
34 this.command
35 } ${this.frame()}`;
36 }
37 }, 300);
38
39 this.statusBarItem.show();
40 }
41
42 public hide() {
43 if (this.timer) {
44 clearInterval(this.timer);
45 this.timer = undefined;
46 }
47
48 this.statusBarItem.hide();
49 }
50
51 public dispose() {
52 if (this.timer) {
53 clearInterval(this.timer);
54 this.timer = undefined;
55 }
56
57 this.statusBarItem.dispose();
58 }
59
60 private frame() {
61 return spinnerFrames[(this.i = ++this.i % spinnerFrames.length)];
62 }
63}