aboutsummaryrefslogtreecommitdiff
path: root/editors
diff options
context:
space:
mode:
Diffstat (limited to 'editors')
-rw-r--r--editors/code/package-lock.json6
-rw-r--r--editors/code/package.json370
-rw-r--r--editors/code/ra_syntax_tree.tmGrammar.json31
-rw-r--r--editors/code/src/client.ts27
-rw-r--r--editors/code/src/commands/runnables.ts4
-rw-r--r--editors/code/src/commands/syntax_tree.ts111
-rw-r--r--editors/code/src/config.ts32
-rw-r--r--editors/code/src/ctx.ts3
-rw-r--r--editors/code/src/highlighting.ts255
-rw-r--r--editors/code/src/main.ts4
-rw-r--r--editors/code/src/status_display.ts2
11 files changed, 323 insertions, 522 deletions
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 955a05066..2d8b432a7 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -1399,9 +1399,9 @@
1399 } 1399 }
1400 }, 1400 },
1401 "rollup": { 1401 "rollup": {
1402 "version": "2.3.1", 1402 "version": "2.3.2",
1403 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.3.1.tgz", 1403 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.3.2.tgz",
1404 "integrity": "sha512-BRjzOauORe+R0U0I6SkMTSG22nYmtztR/TaBl0SvbXgc3VAxBDrZoB6HROiK0S5px1pUBnLnjBkbzmVuwC9Q1Q==", 1404 "integrity": "sha512-p66+fbfaUUOGE84sHXAOgfeaYQMslgAazoQMp//nlR519R61213EPFgrMZa48j31jNacJwexSAR1Q8V/BwGKBA==",
1405 "dev": true, 1405 "dev": true,
1406 "requires": { 1406 "requires": {
1407 "fsevents": "~2.1.2" 1407 "fsevents": "~2.1.2"
diff --git a/editors/code/package.json b/editors/code/package.json
index ba31c4e63..14bdec8c2 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -45,7 +45,7 @@
45 "@typescript-eslint/eslint-plugin": "^2.26.0", 45 "@typescript-eslint/eslint-plugin": "^2.26.0",
46 "@typescript-eslint/parser": "^2.26.0", 46 "@typescript-eslint/parser": "^2.26.0",
47 "eslint": "^6.8.0", 47 "eslint": "^6.8.0",
48 "rollup": "^2.3.1", 48 "rollup": "^2.3.2",
49 "tslib": "^1.11.1", 49 "tslib": "^1.11.1",
50 "typescript": "^3.8.3", 50 "typescript": "^3.8.3",
51 "typescript-formatter": "^7.2.2", 51 "typescript-formatter": "^7.2.2",
@@ -177,91 +177,70 @@
177 "type": "object", 177 "type": "object",
178 "title": "Rust Analyzer", 178 "title": "Rust Analyzer",
179 "properties": { 179 "properties": {
180 "rust-analyzer.highlighting.semanticTokens": { 180 "rust-analyzer.diagnostics.enable": {
181 "type": "boolean", 181 "type": "boolean",
182 "default": false, 182 "default": true,
183 "description": "Use proposed semantic tokens API for syntax highlighting" 183 "markdownDescription": "Whether to show native rust-analyzer diagnostics."
184 },
185 "rust-analyzer.highlightingOn": {
186 "type": "boolean",
187 "default": false,
188 "description": "Highlight Rust code (overrides built-in syntax highlighting)"
189 },
190 "rust-analyzer.rainbowHighlightingOn": {
191 "type": "boolean",
192 "default": false,
193 "description": "When highlighting Rust code, use a unique color per identifier"
194 }, 184 },
195 "rust-analyzer.featureFlags": { 185 "rust-analyzer.lruCapacity": {
196 "type": "object", 186 "type": [
197 "default": {}, 187 "null",
198 "description": "Fine grained feature flags to disable annoying features", 188 "integer"
199 "properties": { 189 ],
200 "lsp.diagnostics": { 190 "default": null,
201 "type": "boolean", 191 "minimum": 0,
202 "markdownDescription": "Whether to show diagnostics from `cargo check`" 192 "exclusiveMinimum": true,
203 }, 193 "description": "Number of syntax trees rust-analyzer keeps in memory."
204 "completion.insertion.add-call-parenthesis": {
205 "type": "boolean",
206 "description": "Whether to add parenthesis when completing functions"
207 },
208 "completion.insertion.add-argument-snippets": {
209 "type": "boolean",
210 "description": "Whether to add argument snippets when completing functions"
211 },
212 "completion.enable-postfix": {
213 "type": "boolean",
214 "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc."
215 },
216 "call-info.full": {
217 "type": "boolean",
218 "description": "Show function name and docs in parameter hints"
219 },
220 "notifications.workspace-loaded": {
221 "type": "boolean",
222 "markdownDescription": "Whether to show `workspace loaded` message"
223 },
224 "notifications.cargo-toml-not-found": {
225 "type": "boolean",
226 "markdownDescription": "Whether to show `can't find Cargo.toml` error message"
227 }
228 }
229 }, 194 },
230 "rust-analyzer.updates.channel": { 195 "rust-analyzer.files.watcher": {
231 "type": "string", 196 "type": "string",
232 "enum": [ 197 "enum": [
233 "stable", 198 "client",
234 "nightly" 199 "notify"
235 ],
236 "default": "stable",
237 "markdownEnumDescriptions": [
238 "`\"stable\"` updates are shipped weekly, they don't contain cutting-edge features from VSCode proposed APIs but have less bugs in general",
239 "`\"nightly\"` updates are shipped daily (extension updates automatically by downloading artifacts directly from GitHub), they contain cutting-edge features and latest bug fixes. These releases help us get your feedback very quickly and speed up rust-analyzer development **drastically**"
240 ], 200 ],
241 "markdownDescription": "Choose `\"nightly\"` updates to get the latest features and bug fixes every day. While `\"stable\"` releases occur weekly and don't contain cutting-edge features from VSCode proposed APIs" 201 "default": "client",
202 "description": "Controls file watching implementation."
242 }, 203 },
243 "rust-analyzer.updates.askBeforeDownload": { 204 "rust-analyzer.files.exclude": {
205 "type": "array",
206 "items": {
207 "type": "string"
208 },
209 "default": [],
210 "description": "Paths to exclude from analysis."
211 },
212 "rust-analyzer.notifications.workspaceLoaded": {
244 "type": "boolean", 213 "type": "boolean",
245 "default": true, 214 "markdownDescription": "Whether to show `workspace loaded` message."
246 "description": "Whether to ask for permission before downloading any files from the Internet"
247 }, 215 },
248 "rust-analyzer.serverPath": { 216 "rust-analyzer.notifications.cargoTomlNotFound": {
249 "type": [ 217 "type": "boolean",
250 "null", 218 "markdownDescription": "Whether to show `can't find Cargo.toml` error message"
251 "string" 219 },
252 ], 220 "rust-analyzer.cargo.noDefaultFeatures": {
253 "default": null, 221 "type": "boolean",
254 "description": "Path to rust-analyzer executable (points to bundled binary by default). If this is set, then \"rust-analyzer.updates.channel\" setting is not used" 222 "default": false,
223 "markdownDescription": "Do not activate the `default` feature"
224 },
225 "rust-analyzer.cargo.allFeatures": {
226 "type": "boolean",
227 "default": true,
228 "description": "Activate all available features"
255 }, 229 },
256 "rust-analyzer.excludeGlobs": { 230 "rust-analyzer.cargo.features": {
257 "type": "array", 231 "type": "array",
258 "items": { 232 "items": {
259 "type": "string" 233 "type": "string"
260 }, 234 },
261 "default": [], 235 "default": [],
262 "description": "Paths to exclude from analysis" 236 "description": "List of features to activate"
237 },
238 "rust-analyzer.cargo.loadOutDirsFromCheck": {
239 "type": "boolean",
240 "default": false,
241 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs"
263 }, 242 },
264 "rust-analyzer.rustfmtArgs": { 243 "rust-analyzer.rustfmt.extraArgs": {
265 "type": "array", 244 "type": "array",
266 "items": { 245 "items": {
267 "type": "string" 246 "type": "string"
@@ -269,64 +248,42 @@
269 "default": [], 248 "default": [],
270 "description": "Additional arguments to rustfmt" 249 "description": "Additional arguments to rustfmt"
271 }, 250 },
272 "rust-analyzer.useClientWatching": { 251 "rust-analyzer.rustfmt.overrideCommand": {
273 "type": "boolean", 252 "type": "array",
274 "default": true, 253 "items": {
275 "description": "client provided file watching instead of notify watching." 254 "type": "string"
255 },
256 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for formatting."
276 }, 257 },
277 "rust-analyzer.cargo-watch.enable": { 258 "rust-analyzer.checkOnSave.enable": {
278 "type": "boolean", 259 "type": "boolean",
279 "default": true, 260 "default": true,
280 "markdownDescription": "Run specified `cargo-watch` command for diagnostics on save" 261 "markdownDescription": "Run specified `cargo check` command for diagnostics on save"
281 }, 262 },
282 "rust-analyzer.cargo-watch.arguments": { 263 "rust-analyzer.checkOnSave.extraArgs": {
283 "type": "array", 264 "type": "array",
284 "items": { 265 "items": {
285 "type": "string" 266 "type": "string"
286 }, 267 },
287 "markdownDescription": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", 268 "markdownDescription": "Extra arguments for `cargo check`",
288 "default": [] 269 "default": []
289 }, 270 },
290 "rust-analyzer.cargo-watch.command": { 271 "rust-analyzer.checkOnSave.command": {
291 "type": "string", 272 "type": "string",
292 "markdownDescription": "`cargo-watch` command. (e.g: `clippy` will run as `cargo watch -x clippy` )", 273 "default": "check",
293 "default": "check" 274 "markdownDescription": "Cargo command to use for `cargo check`"
294 }, 275 },
295 "rust-analyzer.cargo-watch.allTargets": { 276 "rust-analyzer.checkOnSave.overrideCommand": {
296 "type": "boolean", 277 "type": "array",
297 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)", 278 "items": {
298 "default": true 279 "type": "string"
299 }, 280 },
300 "rust-analyzer.trace.server": { 281 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message=format=json` or similar option."
301 "type": "string",
302 "scope": "window",
303 "enum": [
304 "off",
305 "messages",
306 "verbose"
307 ],
308 "enumDescriptions": [
309 "No traces",
310 "Error only",
311 "Full log"
312 ],
313 "default": "off",
314 "description": "Trace requests to the rust-analyzer"
315 }, 282 },
316 "rust-analyzer.trace.extension": { 283 "rust-analyzer.checkOnSave.allTargets": {
317 "description": "Enable logging of VS Code extensions itself",
318 "type": "boolean", 284 "type": "boolean",
319 "default": false 285 "default": true,
320 }, 286 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)"
321 "rust-analyzer.lruCapacity": {
322 "type": [
323 "null",
324 "integer"
325 ],
326 "default": null,
327 "minimum": 0,
328 "exclusiveMinimum": true,
329 "description": "Number of syntax trees rust-analyzer keeps in memory"
330 }, 287 },
331 "rust-analyzer.inlayHints.typeHints": { 288 "rust-analyzer.inlayHints.typeHints": {
332 "type": "boolean", 289 "type": "boolean",
@@ -353,28 +310,76 @@
353 "exclusiveMinimum": true, 310 "exclusiveMinimum": true,
354 "description": "Maximum length for inlay hints" 311 "description": "Maximum length for inlay hints"
355 }, 312 },
356 "rust-analyzer.cargoFeatures.noDefaultFeatures": { 313 "rust-analyzer.completion.addCallParenthesis": {
357 "type": "boolean", 314 "type": "boolean",
358 "default": false, 315 "default": true,
359 "markdownDescription": "Do not activate the `default` feature" 316 "description": "Whether to add parenthesis when completing functions"
360 }, 317 },
361 "rust-analyzer.cargoFeatures.allFeatures": { 318 "rust-analyzer.completion.addCallArgumentSnippets": {
362 "type": "boolean", 319 "type": "boolean",
363 "default": true, 320 "default": true,
364 "description": "Activate all available features" 321 "description": "Whether to add argument snippets when completing functions"
365 }, 322 },
366 "rust-analyzer.cargoFeatures.features": { 323 "rust-analyzer.completion.postfix.enable": {
367 "type": "array", 324 "type": "boolean",
368 "items": { 325 "default": true,
369 "type": "string" 326 "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc."
370 },
371 "default": [],
372 "description": "List of features to activate"
373 }, 327 },
374 "rust-analyzer.cargoFeatures.loadOutDirsFromCheck": { 328 "rust-analyzer.callInfo.full": {
329 "type": "boolean",
330 "description": "Show function name and docs in parameter hints"
331 },
332 "rust-analyzer.highlighting.semanticTokens": {
375 "type": "boolean", 333 "type": "boolean",
376 "default": false, 334 "default": false,
377 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs" 335 "description": "Use proposed semantic tokens API for syntax highlighting"
336 },
337 "rust-analyzer.updates.channel": {
338 "type": "string",
339 "enum": [
340 "stable",
341 "nightly"
342 ],
343 "default": "stable",
344 "markdownEnumDescriptions": [
345 "`\"stable\"` updates are shipped weekly, they don't contain cutting-edge features from VSCode proposed APIs but have less bugs in general",
346 "`\"nightly\"` updates are shipped daily (extension updates automatically by downloading artifacts directly from GitHub), they contain cutting-edge features and latest bug fixes. These releases help us get your feedback very quickly and speed up rust-analyzer development **drastically**"
347 ],
348 "markdownDescription": "Choose `\"nightly\"` updates to get the latest features and bug fixes every day. While `\"stable\"` releases occur weekly and don't contain cutting-edge features from VSCode proposed APIs"
349 },
350 "rust-analyzer.updates.askBeforeDownload": {
351 "type": "boolean",
352 "default": true,
353 "description": "Whether to ask for permission before downloading any files from the Internet"
354 },
355 "rust-analyzer.serverPath": {
356 "type": [
357 "null",
358 "string"
359 ],
360 "default": null,
361 "description": "Path to rust-analyzer executable (points to bundled binary by default). If this is set, then \"rust-analyzer.updates.channel\" setting is not used"
362 },
363 "rust-analyzer.trace.server": {
364 "type": "string",
365 "scope": "window",
366 "enum": [
367 "off",
368 "messages",
369 "verbose"
370 ],
371 "enumDescriptions": [
372 "No traces",
373 "Error only",
374 "Full log"
375 ],
376 "default": "off",
377 "description": "Trace requests to the rust-analyzer"
378 },
379 "rust-analyzer.trace.extension": {
380 "description": "Enable logging of VS Code extensions itself",
381 "type": "boolean",
382 "default": false
378 } 383 }
379 } 384 }
380 }, 385 },
@@ -411,6 +416,21 @@
411 ] 416 ]
412 } 417 }
413 ], 418 ],
419 "languages": [
420 {
421 "id": "ra_syntax_tree",
422 "extensions": [
423 ".rast"
424 ]
425 }
426 ],
427 "grammars": [
428 {
429 "language": "ra_syntax_tree",
430 "scopeName": "source.ra_syntax_tree",
431 "path": "ra_syntax_tree.tmGrammar.json"
432 }
433 ],
414 "problemMatchers": [ 434 "problemMatchers": [
415 { 435 {
416 "name": "rustc", 436 "name": "rustc",
@@ -450,6 +470,15 @@
450 "light": "#747474", 470 "light": "#747474",
451 "highContrast": "#BEBEBE" 471 "highContrast": "#BEBEBE"
452 } 472 }
473 },
474 {
475 "id": "rust_analyzer.syntaxTreeBorder",
476 "description": "Color of the border displayed in the Rust source code for the selected syntax node (see \"Show Syntax Tree\" command)",
477 "defaults": {
478 "dark": "#ffffff",
479 "light": "#b700ff",
480 "highContrast": "#b700ff"
481 }
453 } 482 }
454 ], 483 ],
455 "semanticTokenTypes": [ 484 "semanticTokenTypes": [
@@ -492,65 +521,34 @@
492 "description": "Style for unsafe operations" 521 "description": "Style for unsafe operations"
493 } 522 }
494 ], 523 ],
495 "semanticTokenStyleDefaults": [ 524 "semanticTokenScopes": [
496 { 525 {
497 "selector": "attribute", 526 "language": "rust",
498 "scope": [ 527 "scopes": {
499 "meta.attribute" 528 "attribute": [
500 ] 529 "meta.attribute"
501 }, 530 ],
502 { 531 "builtinType": [
503 "selector": "builtinType", 532 "support.type.primitive"
504 "scope": [ 533 ],
505 "support.type.primitive" 534 "lifetime": [
506 ] 535 "entity.name.lifetime.rust"
507 }, 536 ],
508 { 537 "typeAlias": [
509 "selector": "lifetime", 538 "entity.name.typeAlias"
510 "scope": [ 539 ],
511 "entity.name.lifetime.rust" 540 "union": [
512 ] 541 "entity.name.union"
513 }, 542 ],
514 { 543 "keyword.unsafe": [
515 "selector": "typeAlias", 544 "keyword.other.unsafe"
516 "scope": [ 545 ],
517 "entity.name.typeAlias" 546 "keyword.control": [
518 ] 547 "keyword.control"
519 }, 548 ],
520 { 549 "variable.constant": [
521 "selector": "union", 550 "entity.name.constant"
522 "scope": [ 551 ]
523 "entity.name.union"
524 ]
525 },
526 {
527 "selector": "keyword.unsafe",
528 "scope": [
529 "keyword.other.unsafe"
530 ]
531 },
532 {
533 "selector": "keyword.control",
534 "scope": [
535 "keyword.control"
536 ]
537 },
538 {
539 "selector": "variable.constant",
540 "scope": [
541 "entity.name.constant"
542 ]
543 },
544 {
545 "selector": "*.mutable",
546 "light": {
547 "fontStyle": "underline"
548 },
549 "dark": {
550 "fontStyle": "underline"
551 },
552 "highContrast": {
553 "fontStyle": "underline"
554 } 552 }
555 } 553 }
556 ] 554 ]
diff --git a/editors/code/ra_syntax_tree.tmGrammar.json b/editors/code/ra_syntax_tree.tmGrammar.json
new file mode 100644
index 000000000..0d72a3e36
--- /dev/null
+++ b/editors/code/ra_syntax_tree.tmGrammar.json
@@ -0,0 +1,31 @@
1{
2 "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
3
4 "scopeName": "source.ra_syntax_tree",
5 "patterns": [
6 { "include": "#node_type" },
7 { "include": "#node_range_index" },
8 { "include": "#token_text" }
9 ],
10 "repository": {
11 "node_type": {
12 "match": "^\\s*([A-Z_]+?)@",
13 "captures": {
14 "1": {
15 "name": "entity.name.class"
16 }
17 }
18 },
19 "node_range_index": {
20 "match": "\\d+",
21 "name": "constant.numeric"
22 },
23 "token_text": {
24 "match": "\".+\"",
25 "name": "string"
26 }
27 },
28 "fileTypes": [
29 "rast"
30 ]
31}
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index f909f8db2..3b1d00bca 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -5,31 +5,6 @@ import { Config } from './config';
5import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; 5import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
6import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; 6import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
7 7
8export function configToServerOptions(config: Config) {
9 return {
10 publishDecorations: !config.highlightingSemanticTokens,
11 lruCapacity: config.lruCapacity,
12
13 inlayHintsType: config.inlayHints.typeHints,
14 inlayHintsParameter: config.inlayHints.parameterHints,
15 inlayHintsChaining: config.inlayHints.chainingHints,
16 inlayHintsMaxLength: config.inlayHints.maxLength,
17
18 cargoWatchEnable: config.cargoWatchOptions.enable,
19 cargoWatchArgs: config.cargoWatchOptions.arguments,
20 cargoWatchCommand: config.cargoWatchOptions.command,
21 cargoWatchAllTargets: config.cargoWatchOptions.allTargets,
22
23 excludeGlobs: config.excludeGlobs,
24 useClientWatching: config.useClientWatching,
25 featureFlags: config.featureFlags,
26 withSysroot: config.withSysroot,
27 cargoFeatures: config.cargoFeatures,
28 rustfmtArgs: config.rustfmtArgs,
29 vscodeLldb: vscode.extensions.getExtension("vadimcn.vscode-lldb") != null,
30 };
31}
32
33export async function createClient(config: Config, serverPath: string, cwd: string): Promise<lc.LanguageClient> { 8export async function createClient(config: Config, serverPath: string, cwd: string): Promise<lc.LanguageClient> {
34 // '.' Is the fallback if no folder is open 9 // '.' Is the fallback if no folder is open
35 // TODO?: Workspace folders support Uri's (eg: file://test.txt). 10 // TODO?: Workspace folders support Uri's (eg: file://test.txt).
@@ -49,7 +24,7 @@ export async function createClient(config: Config, serverPath: string, cwd: stri
49 24
50 const clientOptions: lc.LanguageClientOptions = { 25 const clientOptions: lc.LanguageClientOptions = {
51 documentSelector: [{ scheme: 'file', language: 'rust' }], 26 documentSelector: [{ scheme: 'file', language: 'rust' }],
52 initializationOptions: configToServerOptions(config), 27 initializationOptions: vscode.workspace.getConfiguration("rust-analyzer"),
53 traceOutputChannel, 28 traceOutputChannel,
54 middleware: { 29 middleware: {
55 // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576 30 // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index 357155163..2635a1440 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -66,6 +66,10 @@ export function debugSingle(ctx: Ctx): Cmd {
66 return async (config: ra.Runnable) => { 66 return async (config: ra.Runnable) => {
67 const editor = ctx.activeRustEditor; 67 const editor = ctx.activeRustEditor;
68 if (!editor) return; 68 if (!editor) return;
69 if (!vscode.extensions.getExtension("vadimcn.vscode-lldb")) {
70 vscode.window.showErrorMessage("Install `vadimcn.vscode-lldb` extension for debugging");
71 return;
72 }
69 73
70 const debugConfig = { 74 const debugConfig = {
71 type: "lldb", 75 type: "lldb",
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
index 996c7a716..b7a397414 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -15,6 +15,9 @@ export function syntaxTree(ctx: Ctx): Cmd {
15 void new AstInspector(ctx); 15 void new AstInspector(ctx);
16 16
17 ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp)); 17 ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp));
18 ctx.pushCleanup(vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
19 brackets: [["[", ")"]],
20 }));
18 21
19 return async () => { 22 return async () => {
20 const editor = vscode.window.activeTextEditor; 23 const editor = vscode.window.activeTextEditor;
@@ -36,7 +39,7 @@ export function syntaxTree(ctx: Ctx): Cmd {
36} 39}
37 40
38class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { 41class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
39 readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree'); 42 readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree/tree.rast');
40 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); 43 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
41 44
42 45
@@ -79,16 +82,41 @@ class TextDocumentContentProvider implements vscode.TextDocumentContentProvider
79 82
80// FIXME: consider implementing this via the Tree View API? 83// FIXME: consider implementing this via the Tree View API?
81// https://code.visualstudio.com/api/extension-guides/tree-view 84// https://code.visualstudio.com/api/extension-guides/tree-view
82class AstInspector implements vscode.HoverProvider, Disposable { 85class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable {
83 private static readonly astDecorationType = vscode.window.createTextEditorDecorationType({ 86 private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
84 fontStyle: "normal", 87 borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'),
85 border: "#ffffff 1px solid", 88 borderStyle: "solid",
89 borderWidth: "2px",
90
86 }); 91 });
87 private rustEditor: undefined | RustEditor; 92 private rustEditor: undefined | RustEditor;
88 93
94 // Lazy rust token range -> syntax tree file range.
95 private readonly rust2Ast = new Lazy(() => {
96 const astEditor = this.findAstTextEditor();
97 if (!this.rustEditor || !astEditor) return undefined;
98
99 const buf: [vscode.Range, vscode.Range][] = [];
100 for (let i = 0; i < astEditor.document.lineCount; ++i) {
101 const astLine = astEditor.document.lineAt(i);
102
103 // Heuristically look for nodes with quoted text (which are token nodes)
104 const isTokenNode = astLine.text.lastIndexOf('"') >= 0;
105 if (!isTokenNode) continue;
106
107 const rustRange = this.parseRustTextRange(this.rustEditor.document, astLine.text);
108 if (!rustRange) continue;
109
110 buf.push([rustRange, this.findAstNodeRange(astLine)]);
111 }
112 return buf;
113 });
114
89 constructor(ctx: Ctx) { 115 constructor(ctx: Ctx) {
90 ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this)); 116 ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this));
117 ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
91 vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions); 118 vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions);
119 vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
92 vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions); 120 vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions);
93 121
94 ctx.pushCleanup(this); 122 ctx.pushCleanup(this);
@@ -97,6 +125,12 @@ class AstInspector implements vscode.HoverProvider, Disposable {
97 this.setRustEditor(undefined); 125 this.setRustEditor(undefined);
98 } 126 }
99 127
128 private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
129 if (this.rustEditor && event.document.uri.toString() === this.rustEditor.document.uri.toString()) {
130 this.rust2Ast.reset();
131 }
132 }
133
100 private onDidCloseTextDocument(doc: vscode.TextDocument) { 134 private onDidCloseTextDocument(doc: vscode.TextDocument) {
101 if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) { 135 if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) {
102 this.setRustEditor(undefined); 136 this.setRustEditor(undefined);
@@ -104,38 +138,67 @@ class AstInspector implements vscode.HoverProvider, Disposable {
104 } 138 }
105 139
106 private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) { 140 private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) {
107 if (editors.every(suspect => suspect.document.uri.scheme !== AST_FILE_SCHEME)) { 141 if (!this.findAstTextEditor()) {
108 this.setRustEditor(undefined); 142 this.setRustEditor(undefined);
109 return; 143 return;
110 } 144 }
111 this.setRustEditor(editors.find(isRustEditor)); 145 this.setRustEditor(editors.find(isRustEditor));
112 } 146 }
113 147
148 private findAstTextEditor(): undefined | vscode.TextEditor {
149 return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === AST_FILE_SCHEME);
150 }
151
114 private setRustEditor(newRustEditor: undefined | RustEditor) { 152 private setRustEditor(newRustEditor: undefined | RustEditor) {
115 if (newRustEditor !== this.rustEditor) { 153 if (this.rustEditor && this.rustEditor !== newRustEditor) {
116 this.rustEditor?.setDecorations(AstInspector.astDecorationType, []); 154 this.rustEditor.setDecorations(this.astDecorationType, []);
155 this.rust2Ast.reset();
117 } 156 }
118 this.rustEditor = newRustEditor; 157 this.rustEditor = newRustEditor;
119 } 158 }
120 159
160 // additional positional params are omitted
161 provideDefinition(doc: vscode.TextDocument, pos: vscode.Position): vscode.ProviderResult<vscode.DefinitionLink[]> {
162 if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) return;
163
164 const astEditor = this.findAstTextEditor();
165 if (!astEditor) return;
166
167 const rust2AstRanges = this.rust2Ast.get()?.find(([rustRange, _]) => rustRange.contains(pos));
168 if (!rust2AstRanges) return;
169
170 const [rustFileRange, astFileRange] = rust2AstRanges;
171
172 astEditor.revealRange(astFileRange);
173 astEditor.selection = new vscode.Selection(astFileRange.start, astFileRange.end);
174
175 return [{
176 targetRange: astFileRange,
177 targetUri: astEditor.document.uri,
178 originSelectionRange: rustFileRange,
179 targetSelectionRange: astFileRange,
180 }];
181 }
182
183 // additional positional params are omitted
121 provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> { 184 provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> {
122 if (!this.rustEditor) return; 185 if (!this.rustEditor) return;
123 186
124 const astTextLine = doc.lineAt(hoverPosition.line); 187 const astFileLine = doc.lineAt(hoverPosition.line);
125 188
126 const rustTextRange = this.parseRustTextRange(this.rustEditor.document, astTextLine.text); 189 const rustFileRange = this.parseRustTextRange(this.rustEditor.document, astFileLine.text);
127 if (!rustTextRange) return; 190 if (!rustFileRange) return;
128 191
129 this.rustEditor.setDecorations(AstInspector.astDecorationType, [rustTextRange]); 192 this.rustEditor.setDecorations(this.astDecorationType, [rustFileRange]);
130 this.rustEditor.revealRange(rustTextRange); 193 this.rustEditor.revealRange(rustFileRange);
131 194
132 const rustSourceCode = this.rustEditor.document.getText(rustTextRange); 195 const rustSourceCode = this.rustEditor.document.getText(rustFileRange);
133 const astTextRange = this.findAstRange(astTextLine); 196 const astFileRange = this.findAstNodeRange(astFileLine);
134 197
135 return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astTextRange); 198 return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astFileRange);
136 } 199 }
137 200
138 private findAstRange(astLine: vscode.TextLine) { 201 private findAstNodeRange(astLine: vscode.TextLine) {
139 const lineOffset = astLine.range.start; 202 const lineOffset = astLine.range.start;
140 const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex); 203 const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex);
141 const end = lineOffset.translate(undefined, astLine.text.trimEnd().length); 204 const end = lineOffset.translate(undefined, astLine.text.trimEnd().length);
@@ -151,3 +214,17 @@ class AstInspector implements vscode.HoverProvider, Disposable {
151 return new vscode.Range(begin, end); 214 return new vscode.Range(begin, end);
152 } 215 }
153} 216}
217
218class Lazy<T> {
219 val: undefined | T;
220
221 constructor(private readonly compute: () => undefined | T) { }
222
223 get() {
224 return this.val ?? (this.val = this.compute());
225 }
226
227 reset() {
228 this.val = undefined;
229 }
230}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 501997fef..1f45f1de0 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -11,9 +11,8 @@ export class Config {
11 private readonly rootSection = "rust-analyzer"; 11 private readonly rootSection = "rust-analyzer";
12 private readonly requiresReloadOpts = [ 12 private readonly requiresReloadOpts = [
13 "serverPath", 13 "serverPath",
14 "cargoFeatures", 14 "cargo",
15 "excludeGlobs", 15 "files",
16 "useClientWatching",
17 "highlighting", 16 "highlighting",
18 "updates.channel", 17 "updates.channel",
19 ] 18 ]
@@ -71,19 +70,8 @@ export class Config {
71 get channel() { return this.cfg.get<UpdatesChannel>("updates.channel")!; } 70 get channel() { return this.cfg.get<UpdatesChannel>("updates.channel")!; }
72 get askBeforeDownload() { return this.cfg.get<boolean>("updates.askBeforeDownload")!; } 71 get askBeforeDownload() { return this.cfg.get<boolean>("updates.askBeforeDownload")!; }
73 get highlightingSemanticTokens() { return this.cfg.get<boolean>("highlighting.semanticTokens")!; } 72 get highlightingSemanticTokens() { return this.cfg.get<boolean>("highlighting.semanticTokens")!; }
74 get highlightingOn() { return this.cfg.get<boolean>("highlightingOn")!; }
75 get rainbowHighlightingOn() { return this.cfg.get<boolean>("rainbowHighlightingOn")!; }
76 get lruCapacity() { return this.cfg.get<null | number>("lruCapacity")!; }
77 get excludeGlobs() { return this.cfg.get<string[]>("excludeGlobs")!; }
78 get useClientWatching() { return this.cfg.get<boolean>("useClientWatching")!; }
79 get featureFlags() { return this.cfg.get<Record<string, boolean>>("featureFlags")!; }
80 get rustfmtArgs() { return this.cfg.get<string[]>("rustfmtArgs")!; }
81 get loadOutDirsFromCheck() { return this.cfg.get<boolean>("loadOutDirsFromCheck")!; }
82 get traceExtension() { return this.cfg.get<boolean>("trace.extension")!; } 73 get traceExtension() { return this.cfg.get<boolean>("trace.extension")!; }
83 74
84 // for internal use
85 get withSysroot() { return this.cfg.get<boolean>("withSysroot", true)!; }
86
87 get inlayHints() { 75 get inlayHints() {
88 return { 76 return {
89 typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!, 77 typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!,
@@ -93,21 +81,9 @@ export class Config {
93 }; 81 };
94 } 82 }
95 83
96 get cargoWatchOptions() { 84 get checkOnSave() {
97 return {
98 enable: this.cfg.get<boolean>("cargo-watch.enable")!,
99 arguments: this.cfg.get<string[]>("cargo-watch.arguments")!,
100 allTargets: this.cfg.get<boolean>("cargo-watch.allTargets")!,
101 command: this.cfg.get<string>("cargo-watch.command")!,
102 };
103 }
104
105 get cargoFeatures() {
106 return { 85 return {
107 noDefaultFeatures: this.cfg.get<boolean>("cargoFeatures.noDefaultFeatures")!, 86 command: this.cfg.get<string>("checkOnSave.command")!,
108 allFeatures: this.cfg.get<boolean>("cargoFeatures.allFeatures")!,
109 features: this.cfg.get<string[]>("cargoFeatures.features")!,
110 loadOutDirsFromCheck: this.cfg.get<boolean>("cargoFeatures.loadOutDirsFromCheck")!,
111 }; 87 };
112 } 88 }
113} 89}
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 86b5f3629..bd1c3de07 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3 3
4import { Config } from './config'; 4import { Config } from './config';
5import { createClient, configToServerOptions } from './client'; 5import { createClient } from './client';
6import { isRustEditor, RustEditor } from './util'; 6import { isRustEditor, RustEditor } from './util';
7 7
8export class Ctx { 8export class Ctx {
@@ -25,7 +25,6 @@ export class Ctx {
25 const res = new Ctx(config, extCtx, client, serverPath); 25 const res = new Ctx(config, extCtx, client, serverPath);
26 res.pushCleanup(client.start()); 26 res.pushCleanup(client.start());
27 await client.onReady(); 27 await client.onReady();
28 client.onRequest('workspace/configuration', _ => [configToServerOptions(config)]);
29 return res; 28 return res;
30 } 29 }
31 30
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
deleted file mode 100644
index ea2dfc0e3..000000000
--- a/editors/code/src/highlighting.ts
+++ /dev/null
@@ -1,255 +0,0 @@
1import * as vscode from 'vscode';
2import * as ra from './rust-analyzer-api';
3
4import { ColorTheme, TextMateRuleSettings } from './color_theme';
5
6import { Ctx } from './ctx';
7import { sendRequestWithRetry, isRustDocument } from './util';
8
9export function activateHighlighting(ctx: Ctx) {
10 const highlighter = new Highlighter(ctx);
11
12 ctx.client.onNotification(ra.publishDecorations, params => {
13 if (!ctx.config.highlightingOn) return;
14
15 const targetEditor = vscode.window.visibleTextEditors.find(
16 editor => {
17 const unescapedUri = unescape(
18 editor.document.uri.toString(),
19 );
20 // Unescaped URI looks like:
21 // file:///c:/Workspace/ra-test/src/main.rs
22 return unescapedUri === params.uri;
23 },
24 );
25 if (!targetEditor) return;
26
27 highlighter.setHighlights(targetEditor, params.decorations);
28 });
29
30
31 vscode.workspace.onDidChangeConfiguration(
32 _ => highlighter.removeHighlights(),
33 null,
34 ctx.subscriptions,
35 );
36
37 vscode.window.onDidChangeActiveTextEditor(
38 async (editor: vscode.TextEditor | undefined) => {
39 if (!editor || !isRustDocument(editor.document)) return;
40 if (!ctx.config.highlightingOn) return;
41 const client = ctx.client;
42 if (!client) return;
43
44 const decorations = await sendRequestWithRetry(
45 client,
46 ra.decorationsRequest,
47 { uri: editor.document.uri.toString() },
48 );
49 highlighter.setHighlights(editor, decorations);
50 },
51 null,
52 ctx.subscriptions,
53 );
54}
55
56// Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76
57function fancify(seed: string, shade: 'light' | 'dark') {
58 const random = randomU32Numbers(hashString(seed));
59 const randomInt = (min: number, max: number) => {
60 return Math.abs(random()) % (max - min + 1) + min;
61 };
62
63 const h = randomInt(0, 360);
64 const s = randomInt(42, 98);
65 const l = shade === 'light' ? randomInt(15, 40) : randomInt(40, 90);
66 return `hsl(${h},${s}%,${l}%)`;
67}
68
69class Highlighter {
70 private ctx: Ctx;
71 private decorations: Map<
72 string,
73 vscode.TextEditorDecorationType
74 > | null = null;
75
76 constructor(ctx: Ctx) {
77 this.ctx = ctx;
78 }
79
80 public removeHighlights() {
81 if (this.decorations == null) {
82 return;
83 }
84
85 // Decorations are removed when the object is disposed
86 for (const decoration of this.decorations.values()) {
87 decoration.dispose();
88 }
89
90 this.decorations = null;
91 }
92
93 public setHighlights(editor: vscode.TextEditor, highlights: ra.Decoration[]) {
94 const client = this.ctx.client;
95 if (!client) return;
96 // Initialize decorations if necessary
97 //
98 // Note: decoration objects need to be kept around so we can dispose them
99 // if the user disables syntax highlighting
100 if (this.decorations == null) {
101 this.decorations = initDecorations();
102 }
103
104 const byTag: Map<string, vscode.Range[]> = new Map();
105 const colorfulIdents: Map<
106 string,
107 [vscode.Range[], boolean]
108 > = new Map();
109 const rainbowTime = this.ctx.config.rainbowHighlightingOn;
110
111 for (const tag of this.decorations.keys()) {
112 byTag.set(tag, []);
113 }
114
115 for (const d of highlights) {
116 if (!byTag.get(d.tag)) {
117 continue;
118 }
119
120 if (rainbowTime && d.bindingHash) {
121 if (!colorfulIdents.has(d.bindingHash)) {
122 const mut = d.tag.endsWith('.mut');
123 colorfulIdents.set(d.bindingHash, [[], mut]);
124 }
125 colorfulIdents
126 .get(d.bindingHash)![0]
127 .push(
128 client.protocol2CodeConverter.asRange(d.range),
129 );
130 } else {
131 byTag
132 .get(d.tag)!
133 .push(
134 client.protocol2CodeConverter.asRange(d.range),
135 );
136 }
137 }
138
139 for (const tag of byTag.keys()) {
140 const dec = this.decorations.get(
141 tag,
142 ) as vscode.TextEditorDecorationType;
143 const ranges = byTag.get(tag)!;
144 editor.setDecorations(dec, ranges);
145 }
146
147 for (const [hash, [ranges, mut]] of colorfulIdents.entries()) {
148 const textDecoration = mut ? 'underline' : undefined;
149 const dec = vscode.window.createTextEditorDecorationType({
150 light: { color: fancify(hash, 'light'), textDecoration },
151 dark: { color: fancify(hash, 'dark'), textDecoration },
152 });
153 editor.setDecorations(dec, ranges);
154 }
155 }
156}
157
158function initDecorations(): Map<string, vscode.TextEditorDecorationType> {
159 const theme = ColorTheme.load();
160 const res = new Map();
161 TAG_TO_SCOPES.forEach((scopes, tag) => {
162 // We are going to axe this soon, so don't try to detect unknown tags.
163 // Users should switch to the new semantic tokens implementation.
164 if (!scopes) return;
165 const rule = theme.lookup(scopes);
166 const decor = createDecorationFromTextmate(rule);
167 res.set(tag, decor);
168 });
169 return res;
170}
171
172function createDecorationFromTextmate(
173 themeStyle: TextMateRuleSettings,
174): vscode.TextEditorDecorationType {
175 const decorationOptions: vscode.DecorationRenderOptions = {};
176 decorationOptions.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen;
177
178 if (themeStyle.foreground) {
179 decorationOptions.color = themeStyle.foreground;
180 }
181
182 if (themeStyle.background) {
183 decorationOptions.backgroundColor = themeStyle.background;
184 }
185
186 if (themeStyle.fontStyle) {
187 const parts: string[] = themeStyle.fontStyle.split(' ');
188 parts.forEach(part => {
189 switch (part) {
190 case 'italic':
191 decorationOptions.fontStyle = 'italic';
192 break;
193 case 'bold':
194 decorationOptions.fontWeight = 'bold';
195 break;
196 case 'underline':
197 decorationOptions.textDecoration = 'underline';
198 break;
199 default:
200 break;
201 }
202 });
203 }
204 return vscode.window.createTextEditorDecorationType(decorationOptions);
205}
206
207// sync with tags from `syntax_highlighting.rs`.
208const TAG_TO_SCOPES = new Map<string, string[]>([
209 ["field", ["entity.name.field"]],
210 ["function", ["entity.name.function"]],
211 ["module", ["entity.name.module"]],
212 ["constant", ["entity.name.constant"]],
213 ["macro", ["entity.name.macro"]],
214
215 ["variable", ["variable"]],
216 ["variable.mutable", ["variable", "meta.mutable"]],
217
218 ["type", ["entity.name.type"]],
219 ["type.builtin", ["entity.name.type", "support.type.primitive"]],
220 ["type.self", ["entity.name.type.parameter.self"]],
221 ["type.param", ["entity.name.type.parameter", "entity.name.type.param.rust"]],
222 ["type.lifetime", ["entity.name.type.lifetime", "entity.name.lifetime.rust"]],
223
224 ["literal.byte", ["constant.character.byte"]],
225 ["literal.char", ["constant.character.rust"]],
226 ["numeric_literal", ["constant.numeric"]],
227
228 ["comment", ["comment"]],
229 ["string_literal", ["string.quoted"]],
230 ["attribute", ["meta.attribute.rust"]],
231
232 ["keyword", ["keyword"]],
233 ["keyword.unsafe", ["keyword.other.unsafe"]],
234 ["keyword.control", ["keyword.control"]],
235]);
236
237function randomU32Numbers(seed: number) {
238 let random = seed | 0;
239 return () => {
240 random ^= random << 13;
241 random ^= random >> 17;
242 random ^= random << 5;
243 random |= 0;
244 return random;
245 };
246}
247
248function hashString(str: string): number {
249 let res = 0;
250 for (let i = 0; i < str.length; ++i) {
251 const c = str.codePointAt(i)!;
252 res = (res * 31 + c) & ~0;
253 }
254 return res;
255}
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 7ba16120c..4f3b89f44 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -7,7 +7,6 @@ import * as commands from './commands';
7import { activateInlayHints } from './inlay_hints'; 7import { activateInlayHints } from './inlay_hints';
8import { activateStatusDisplay } from './status_display'; 8import { activateStatusDisplay } from './status_display';
9import { Ctx } from './ctx'; 9import { Ctx } from './ctx';
10import { activateHighlighting } from './highlighting';
11import { Config, NIGHTLY_TAG } from './config'; 10import { Config, NIGHTLY_TAG } from './config';
12import { log, assert } from './util'; 11import { log, assert } from './util';
13import { PersistentState } from './persistent_state'; 12import { PersistentState } from './persistent_state';
@@ -97,9 +96,6 @@ export async function activate(context: vscode.ExtensionContext) {
97 96
98 activateStatusDisplay(ctx); 97 activateStatusDisplay(ctx);
99 98
100 if (!ctx.config.highlightingSemanticTokens) {
101 activateHighlighting(ctx);
102 }
103 activateInlayHints(ctx); 99 activateInlayHints(ctx);
104 100
105 vscode.workspace.onDidChangeConfiguration( 101 vscode.workspace.onDidChangeConfiguration(
diff --git a/editors/code/src/status_display.ts b/editors/code/src/status_display.ts
index 0f5f6ef99..f9cadc8a2 100644
--- a/editors/code/src/status_display.ts
+++ b/editors/code/src/status_display.ts
@@ -7,7 +7,7 @@ import { Ctx } from './ctx';
7const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; 7const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
8 8
9export function activateStatusDisplay(ctx: Ctx) { 9export function activateStatusDisplay(ctx: Ctx) {
10 const statusDisplay = new StatusDisplay(ctx.config.cargoWatchOptions.command); 10 const statusDisplay = new StatusDisplay(ctx.config.checkOnSave.command);
11 ctx.pushCleanup(statusDisplay); 11 ctx.pushCleanup(statusDisplay);
12 const client = ctx.client; 12 const client = ctx.client;
13 if (client != null) { 13 if (client != null) {