aboutsummaryrefslogtreecommitdiff
path: root/editors/code
diff options
context:
space:
mode:
authornmio <[email protected]>2020-02-25 18:01:59 +0000
committernmio <[email protected]>2020-02-25 18:01:59 +0000
commit39bd3b2bd719289a8dd03fb52d5060cdb10f9169 (patch)
tree4b627f15fcbb454aa09845ba8b456823cbc791b8 /editors/code
parent34e3ef61bd25c635721066c1f881d7f041366a0a (diff)
parentd3040c0deba8266044029a6479a1c12c28e72750 (diff)
Merge branch 'master' of https://github.com/rust-analyzer/rust-analyzer into find-cargo-toml-up-the-fs
Diffstat (limited to 'editors/code')
-rw-r--r--editors/code/.eslintrc.js3
-rw-r--r--editors/code/package-lock.json6
-rw-r--r--editors/code/package.json2
-rw-r--r--editors/code/src/commands/analyzer_status.ts6
-rw-r--r--editors/code/src/commands/expand_macro.ts18
-rw-r--r--editors/code/src/commands/index.ts9
-rw-r--r--editors/code/src/commands/join_lines.ts17
-rw-r--r--editors/code/src/commands/matching_brace.ts15
-rw-r--r--editors/code/src/commands/on_enter.ts21
-rw-r--r--editors/code/src/commands/parent_module.ts10
-rw-r--r--editors/code/src/commands/runnables.ts29
-rw-r--r--editors/code/src/commands/ssr.ts16
-rw-r--r--editors/code/src/commands/syntax_tree.ts27
-rw-r--r--editors/code/src/highlighting.ts60
-rw-r--r--editors/code/src/inlay_hints.ts31
-rw-r--r--editors/code/src/installation/fetch_artifact_release_info.ts58
-rw-r--r--editors/code/src/installation/server.ts2
-rw-r--r--editors/code/src/rust-analyzer-api.ts117
-rw-r--r--editors/code/src/source_change.ts9
-rw-r--r--editors/code/src/util.ts16
20 files changed, 249 insertions, 223 deletions
diff --git a/editors/code/.eslintrc.js b/editors/code/.eslintrc.js
index 16f18ab2c..c6bf410f4 100644
--- a/editors/code/.eslintrc.js
+++ b/editors/code/.eslintrc.js
@@ -32,6 +32,7 @@ module.exports = {
32 "@typescript-eslint/semi": [ 32 "@typescript-eslint/semi": [
33 "error", 33 "error",
34 "always" 34 "always"
35 ] 35 ],
36 "@typescript-eslint/no-unnecessary-type-assertion": "error"
36 } 37 }
37}; 38};
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 76ef2aae4..0288a468e 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -1575,9 +1575,9 @@
1575 } 1575 }
1576 }, 1576 },
1577 "typescript": { 1577 "typescript": {
1578 "version": "3.7.5", 1578 "version": "3.8.2",
1579 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", 1579 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz",
1580 "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", 1580 "integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==",
1581 "dev": true 1581 "dev": true
1582 }, 1582 },
1583 "typescript-formatter": { 1583 "typescript-formatter": {
diff --git a/editors/code/package.json b/editors/code/package.json
index 9ef6c6983..dff535fcd 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -42,7 +42,7 @@
42 "eslint": "^6.8.0", 42 "eslint": "^6.8.0",
43 "rollup": "^1.31.1", 43 "rollup": "^1.31.1",
44 "tslib": "^1.10.0", 44 "tslib": "^1.10.0",
45 "typescript": "^3.7.5", 45 "typescript": "^3.8.2",
46 "typescript-formatter": "^7.2.2", 46 "typescript-formatter": "^7.2.2",
47 "vsce": "^1.73.0" 47 "vsce": "^1.73.0"
48 }, 48 },
diff --git a/editors/code/src/commands/analyzer_status.ts b/editors/code/src/commands/analyzer_status.ts
index 6631e8db7..1c6ea399b 100644
--- a/editors/code/src/commands/analyzer_status.ts
+++ b/editors/code/src/commands/analyzer_status.ts
@@ -1,5 +1,6 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2 2
3import * as ra from '../rust-analyzer-api';
3import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
4 5
5// Shows status of rust-analyzer (for debugging) 6// Shows status of rust-analyzer (for debugging)
@@ -50,10 +51,7 @@ class TextDocumentContentProvider
50 const client = this.ctx.client; 51 const client = this.ctx.client;
51 if (!editor || !client) return ''; 52 if (!editor || !client) return '';
52 53
53 return client.sendRequest<string>( 54 return client.sendRequest(ra.analyzerStatus, null);
54 'rust-analyzer/analyzerStatus',
55 null,
56 );
57 } 55 }
58 56
59 get onDidChange(): vscode.Event<vscode.Uri> { 57 get onDidChange(): vscode.Event<vscode.Uri> {
diff --git a/editors/code/src/commands/expand_macro.ts b/editors/code/src/commands/expand_macro.ts
index edec9bbc1..23f2ef1d5 100644
--- a/editors/code/src/commands/expand_macro.ts
+++ b/editors/code/src/commands/expand_macro.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
5 5
@@ -26,12 +26,7 @@ export function expandMacro(ctx: Ctx): Cmd {
26 }; 26 };
27} 27}
28 28
29interface ExpandedMacro { 29function codeFormat(expanded: ra.ExpandedMacro): string {
30 name: string;
31 expansion: string;
32}
33
34function codeFormat(expanded: ExpandedMacro): string {
35 let result = `// Recursive expansion of ${expanded.name}! macro\n`; 30 let result = `// Recursive expansion of ${expanded.name}! macro\n`;
36 result += '// ' + '='.repeat(result.length - 3); 31 result += '// ' + '='.repeat(result.length - 3);
37 result += '\n\n'; 32 result += '\n\n';
@@ -54,14 +49,11 @@ class TextDocumentContentProvider
54 if (!editor || !client) return ''; 49 if (!editor || !client) return '';
55 50
56 const position = editor.selection.active; 51 const position = editor.selection.active;
57 const request: lc.TextDocumentPositionParams = { 52
53 const expanded = await client.sendRequest(ra.expandMacro, {
58 textDocument: { uri: editor.document.uri.toString() }, 54 textDocument: { uri: editor.document.uri.toString() },
59 position, 55 position,
60 }; 56 });
61 const expanded = await client.sendRequest<ExpandedMacro>(
62 'rust-analyzer/expandMacro',
63 request,
64 );
65 57
66 if (expanded == null) return 'Not available'; 58 if (expanded == null) return 'Not available';
67 59
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index 839245f48..bdb7fc3b0 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -1,5 +1,6 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as ra from '../rust-analyzer-api';
3 4
4import { Ctx, Cmd } from '../ctx'; 5import { Ctx, Cmd } from '../ctx';
5import * as sourceChange from '../source_change'; 6import * as sourceChange from '../source_change';
@@ -16,9 +17,7 @@ export * from './ssr';
16export * from './server_version'; 17export * from './server_version';
17 18
18export function collectGarbage(ctx: Ctx): Cmd { 19export function collectGarbage(ctx: Ctx): Cmd {
19 return async () => { 20 return async () => ctx.client.sendRequest(ra.collectGarbage, null);
20 await ctx.client?.sendRequest<null>('rust-analyzer/collectGarbage', null);
21 };
22} 21}
23 22
24export function showReferences(ctx: Ctx): Cmd { 23export function showReferences(ctx: Ctx): Cmd {
@@ -36,13 +35,13 @@ export function showReferences(ctx: Ctx): Cmd {
36} 35}
37 36
38export function applySourceChange(ctx: Ctx): Cmd { 37export function applySourceChange(ctx: Ctx): Cmd {
39 return async (change: sourceChange.SourceChange) => { 38 return async (change: ra.SourceChange) => {
40 await sourceChange.applySourceChange(ctx, change); 39 await sourceChange.applySourceChange(ctx, change);
41 }; 40 };
42} 41}
43 42
44export function selectAndApplySourceChange(ctx: Ctx): Cmd { 43export function selectAndApplySourceChange(ctx: Ctx): Cmd {
45 return async (changes: sourceChange.SourceChange[]) => { 44 return async (changes: ra.SourceChange[]) => {
46 if (changes.length === 1) { 45 if (changes.length === 1) {
47 await sourceChange.applySourceChange(ctx, changes[0]); 46 await sourceChange.applySourceChange(ctx, changes[0]);
48 } else if (changes.length > 0) { 47 } else if (changes.length > 0) {
diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts
index 7b08c3255..de0614653 100644
--- a/editors/code/src/commands/join_lines.ts
+++ b/editors/code/src/commands/join_lines.ts
@@ -1,7 +1,7 @@
1import * as lc from 'vscode-languageclient'; 1import * as ra from '../rust-analyzer-api';
2 2
3import { Ctx, Cmd } from '../ctx'; 3import { Ctx, Cmd } from '../ctx';
4import { applySourceChange, SourceChange } from '../source_change'; 4import { applySourceChange } from '../source_change';
5 5
6export function joinLines(ctx: Ctx): Cmd { 6export function joinLines(ctx: Ctx): Cmd {
7 return async () => { 7 return async () => {
@@ -9,19 +9,10 @@ export function joinLines(ctx: Ctx): Cmd {
9 const client = ctx.client; 9 const client = ctx.client;
10 if (!editor || !client) return; 10 if (!editor || !client) return;
11 11
12 const request: JoinLinesParams = { 12 const change = await client.sendRequest(ra.joinLines, {
13 range: client.code2ProtocolConverter.asRange(editor.selection), 13 range: client.code2ProtocolConverter.asRange(editor.selection),
14 textDocument: { uri: editor.document.uri.toString() }, 14 textDocument: { uri: editor.document.uri.toString() },
15 }; 15 });
16 const change = await client.sendRequest<SourceChange>(
17 'rust-analyzer/joinLines',
18 request,
19 );
20 await applySourceChange(ctx, change); 16 await applySourceChange(ctx, change);
21 }; 17 };
22} 18}
23
24interface JoinLinesParams {
25 textDocument: lc.TextDocumentIdentifier;
26 range: lc.Range;
27}
diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts
index 7c58bb7e7..a60776e2d 100644
--- a/editors/code/src/commands/matching_brace.ts
+++ b/editors/code/src/commands/matching_brace.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
5 5
@@ -9,16 +9,12 @@ export function matchingBrace(ctx: Ctx): Cmd {
9 const client = ctx.client; 9 const client = ctx.client;
10 if (!editor || !client) return; 10 if (!editor || !client) return;
11 11
12 const request: FindMatchingBraceParams = { 12 const response = await client.sendRequest(ra.findMatchingBrace, {
13 textDocument: { uri: editor.document.uri.toString() }, 13 textDocument: { uri: editor.document.uri.toString() },
14 offsets: editor.selections.map(s => 14 offsets: editor.selections.map(s =>
15 client.code2ProtocolConverter.asPosition(s.active), 15 client.code2ProtocolConverter.asPosition(s.active),
16 ), 16 ),
17 }; 17 });
18 const response = await client.sendRequest<lc.Position[]>(
19 'rust-analyzer/findMatchingBrace',
20 request,
21 );
22 editor.selections = editor.selections.map((sel, idx) => { 18 editor.selections = editor.selections.map((sel, idx) => {
23 const active = client.protocol2CodeConverter.asPosition( 19 const active = client.protocol2CodeConverter.asPosition(
24 response[idx], 20 response[idx],
@@ -29,8 +25,3 @@ export function matchingBrace(ctx: Ctx): Cmd {
29 editor.revealRange(editor.selection); 25 editor.revealRange(editor.selection);
30 }; 26 };
31} 27}
32
33interface FindMatchingBraceParams {
34 textDocument: lc.TextDocumentIdentifier;
35 offsets: lc.Position[];
36}
diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts
index 27ae8ec23..285849db7 100644
--- a/editors/code/src/commands/on_enter.ts
+++ b/editors/code/src/commands/on_enter.ts
@@ -1,7 +1,7 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { applySourceChange, SourceChange } from '../source_change'; 4import { applySourceChange } from '../source_change';
5import { Cmd, Ctx } from '../ctx'; 5import { Cmd, Ctx } from '../ctx';
6 6
7async function handleKeypress(ctx: Ctx) { 7async function handleKeypress(ctx: Ctx) {
@@ -10,22 +10,15 @@ async function handleKeypress(ctx: Ctx) {
10 10
11 if (!editor || !client) return false; 11 if (!editor || !client) return false;
12 12
13 const request: lc.TextDocumentPositionParams = { 13 const change = await client.sendRequest(ra.onEnter, {
14 textDocument: { uri: editor.document.uri.toString() }, 14 textDocument: { uri: editor.document.uri.toString() },
15 position: client.code2ProtocolConverter.asPosition( 15 position: client.code2ProtocolConverter.asPosition(
16 editor.selection.active, 16 editor.selection.active,
17 ), 17 ),
18 }; 18 }).catch(_error => {
19 const change = await client.sendRequest<undefined | SourceChange>( 19 // client.logFailedRequest(OnEnterRequest.type, error);
20 'rust-analyzer/onEnter', 20 return null;
21 request, 21 });
22 ).catch(
23 (_error: any) => {
24 // FIXME: switch to the more modern (?) typed request infrastructure
25 // client.logFailedRequest(OnEnterRequest.type, error);
26 return Promise.resolve(null);
27 }
28 );
29 if (!change) return false; 22 if (!change) return false;
30 23
31 await applySourceChange(ctx, change); 24 await applySourceChange(ctx, change);
diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts
index bf40b4021..8f78ddd71 100644
--- a/editors/code/src/commands/parent_module.ts
+++ b/editors/code/src/commands/parent_module.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
5 5
@@ -9,16 +9,12 @@ export function parentModule(ctx: Ctx): Cmd {
9 const client = ctx.client; 9 const client = ctx.client;
10 if (!editor || !client) return; 10 if (!editor || !client) return;
11 11
12 const request: lc.TextDocumentPositionParams = { 12 const response = await client.sendRequest(ra.parentModule, {
13 textDocument: { uri: editor.document.uri.toString() }, 13 textDocument: { uri: editor.document.uri.toString() },
14 position: client.code2ProtocolConverter.asPosition( 14 position: client.code2ProtocolConverter.asPosition(
15 editor.selection.active, 15 editor.selection.active,
16 ), 16 ),
17 }; 17 });
18 const response = await client.sendRequest<lc.Location[]>(
19 'rust-analyzer/parentModule',
20 request,
21 );
22 const loc = response[0]; 18 const loc = response[0];
23 if (loc == null) return; 19 if (loc == null) return;
24 20
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index 7919997ce..06b513466 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -1,5 +1,6 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as ra from '../rust-analyzer-api';
3 4
4import { Ctx, Cmd } from '../ctx'; 5import { Ctx, Cmd } from '../ctx';
5 6
@@ -14,16 +15,13 @@ export function run(ctx: Ctx): Cmd {
14 const textDocument: lc.TextDocumentIdentifier = { 15 const textDocument: lc.TextDocumentIdentifier = {
15 uri: editor.document.uri.toString(), 16 uri: editor.document.uri.toString(),
16 }; 17 };
17 const params: RunnablesParams = { 18
19 const runnables = await client.sendRequest(ra.runnables, {
18 textDocument, 20 textDocument,
19 position: client.code2ProtocolConverter.asPosition( 21 position: client.code2ProtocolConverter.asPosition(
20 editor.selection.active, 22 editor.selection.active,
21 ), 23 ),
22 }; 24 });
23 const runnables = await client.sendRequest<Runnable[]>(
24 'rust-analyzer/runnables',
25 params,
26 );
27 const items: RunnableQuickPick[] = []; 25 const items: RunnableQuickPick[] = [];
28 if (prevRunnable) { 26 if (prevRunnable) {
29 items.push(prevRunnable); 27 items.push(prevRunnable);
@@ -48,7 +46,7 @@ export function run(ctx: Ctx): Cmd {
48} 46}
49 47
50export function runSingle(ctx: Ctx): Cmd { 48export function runSingle(ctx: Ctx): Cmd {
51 return async (runnable: Runnable) => { 49 return async (runnable: ra.Runnable) => {
52 const editor = ctx.activeRustEditor; 50 const editor = ctx.activeRustEditor;
53 if (!editor) return; 51 if (!editor) return;
54 52
@@ -64,26 +62,13 @@ export function runSingle(ctx: Ctx): Cmd {
64 }; 62 };
65} 63}
66 64
67interface RunnablesParams {
68 textDocument: lc.TextDocumentIdentifier;
69 position?: lc.Position;
70}
71
72interface Runnable {
73 label: string;
74 bin: string;
75 args: string[];
76 env: { [index: string]: string };
77 cwd?: string;
78}
79
80class RunnableQuickPick implements vscode.QuickPickItem { 65class RunnableQuickPick implements vscode.QuickPickItem {
81 public label: string; 66 public label: string;
82 public description?: string | undefined; 67 public description?: string | undefined;
83 public detail?: string | undefined; 68 public detail?: string | undefined;
84 public picked?: boolean | undefined; 69 public picked?: boolean | undefined;
85 70
86 constructor(public runnable: Runnable) { 71 constructor(public runnable: ra.Runnable) {
87 this.label = runnable.label; 72 this.label = runnable.label;
88 } 73 }
89} 74}
@@ -96,7 +81,7 @@ interface CargoTaskDefinition extends vscode.TaskDefinition {
96 env?: { [key: string]: string }; 81 env?: { [key: string]: string };
97} 82}
98 83
99function createTask(spec: Runnable): vscode.Task { 84function createTask(spec: ra.Runnable): vscode.Task {
100 const TASK_SOURCE = 'Rust'; 85 const TASK_SOURCE = 'Rust';
101 const definition: CargoTaskDefinition = { 86 const definition: CargoTaskDefinition = {
102 type: 'cargo', 87 type: 'cargo',
diff --git a/editors/code/src/commands/ssr.ts b/editors/code/src/commands/ssr.ts
index 9b814612a..eee48c693 100644
--- a/editors/code/src/commands/ssr.ts
+++ b/editors/code/src/commands/ssr.ts
@@ -1,6 +1,8 @@
1import { Ctx, Cmd } from '../ctx';
2import { applySourceChange, SourceChange } from '../source_change';
3import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as ra from "../rust-analyzer-api";
3
4import { Ctx, Cmd } from '../ctx';
5import { applySourceChange } from '../source_change';
4 6
5export function ssr(ctx: Ctx): Cmd { 7export function ssr(ctx: Ctx): Cmd {
6 return async () => { 8 return async () => {
@@ -21,16 +23,8 @@ export function ssr(ctx: Ctx): Cmd {
21 23
22 if (!request) return; 24 if (!request) return;
23 25
24 const ssrRequest: SsrRequest = { arg: request }; 26 const change = await client.sendRequest(ra.ssr, { arg: request });
25 const change = await client.sendRequest<SourceChange>(
26 'rust-analyzer/ssr',
27 ssrRequest,
28 );
29 27
30 await applySourceChange(ctx, change); 28 await applySourceChange(ctx, change);
31 }; 29 };
32} 30}
33
34interface SsrRequest {
35 arg: string;
36}
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
index 2887c96c8..7218bfb90 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd } from '../ctx';
5 5
@@ -61,13 +61,8 @@ function afterLs(f: () => void) {
61 setTimeout(f, 10); 61 setTimeout(f, 10);
62} 62}
63 63
64interface SyntaxTreeParams {
65 textDocument: lc.TextDocumentIdentifier;
66 range?: lc.Range;
67}
68 64
69class TextDocumentContentProvider 65class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
70 implements vscode.TextDocumentContentProvider {
71 uri = vscode.Uri.parse('rust-analyzer://syntaxtree'); 66 uri = vscode.Uri.parse('rust-analyzer://syntaxtree');
72 eventEmitter = new vscode.EventEmitter<vscode.Uri>(); 67 eventEmitter = new vscode.EventEmitter<vscode.Uri>();
73 68
@@ -79,23 +74,15 @@ class TextDocumentContentProvider
79 const client = this.ctx.client; 74 const client = this.ctx.client;
80 if (!editor || !client) return ''; 75 if (!editor || !client) return '';
81 76
82 let range: lc.Range | undefined;
83
84 // When the range based query is enabled we take the range of the selection 77 // When the range based query is enabled we take the range of the selection
85 if (uri.query === 'range=true') { 78 const range = uri.query === 'range=true' && !editor.selection.isEmpty
86 range = editor.selection.isEmpty 79 ? client.code2ProtocolConverter.asRange(editor.selection)
87 ? undefined 80 : null;
88 : client.code2ProtocolConverter.asRange(editor.selection);
89 }
90 81
91 const request: SyntaxTreeParams = { 82 return client.sendRequest(ra.syntaxTree, {
92 textDocument: { uri: editor.document.uri.toString() }, 83 textDocument: { uri: editor.document.uri.toString() },
93 range, 84 range,
94 }; 85 });
95 return client.sendRequest<string>(
96 'rust-analyzer/syntaxTree',
97 request,
98 );
99 } 86 }
100 87
101 get onDidChange(): vscode.Event<vscode.Uri> { 88 get onDidChange(): vscode.Event<vscode.Uri> {
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
index 77b4a1a68..3e0cbdc56 100644
--- a/editors/code/src/highlighting.ts
+++ b/editors/code/src/highlighting.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from './rust-analyzer-api';
3 3
4import { ColorTheme, TextMateRuleSettings } from './color_theme'; 4import { ColorTheme, TextMateRuleSettings } from './color_theme';
5 5
@@ -8,29 +8,25 @@ import { sendRequestWithRetry } from './util';
8 8
9export function activateHighlighting(ctx: Ctx) { 9export function activateHighlighting(ctx: Ctx) {
10 const highlighter = new Highlighter(ctx); 10 const highlighter = new Highlighter(ctx);
11 const client = ctx.client;
12 if (client != null) {
13 client.onNotification(
14 'rust-analyzer/publishDecorations',
15 (params: PublishDecorationsParams) => {
16 if (!ctx.config.highlightingOn) return;
17
18 const targetEditor = vscode.window.visibleTextEditors.find(
19 editor => {
20 const unescapedUri = unescape(
21 editor.document.uri.toString(),
22 );
23 // Unescaped URI looks like:
24 // file:///c:/Workspace/ra-test/src/main.rs
25 return unescapedUri === params.uri;
26 },
27 );
28 if (!targetEditor) return;
29 11
30 highlighter.setHighlights(targetEditor, params.decorations); 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;
31 }, 23 },
32 ); 24 );
33 } 25 if (!targetEditor) return;
26
27 highlighter.setHighlights(targetEditor, params.decorations);
28 });
29
34 30
35 vscode.workspace.onDidChangeConfiguration( 31 vscode.workspace.onDidChangeConfiguration(
36 _ => highlighter.removeHighlights(), 32 _ => highlighter.removeHighlights(),
@@ -45,13 +41,10 @@ export function activateHighlighting(ctx: Ctx) {
45 const client = ctx.client; 41 const client = ctx.client;
46 if (!client) return; 42 if (!client) return;
47 43
48 const params: lc.TextDocumentIdentifier = { 44 const decorations = await sendRequestWithRetry(
49 uri: editor.document.uri.toString(),
50 };
51 const decorations = await sendRequestWithRetry<Decoration[]>(
52 client, 45 client,
53 'rust-analyzer/decorationsRequest', 46 ra.decorationsRequest,
54 params, 47 { uri: editor.document.uri.toString() },
55 ); 48 );
56 highlighter.setHighlights(editor, decorations); 49 highlighter.setHighlights(editor, decorations);
57 }, 50 },
@@ -60,17 +53,6 @@ export function activateHighlighting(ctx: Ctx) {
60 ); 53 );
61} 54}
62 55
63interface PublishDecorationsParams {
64 uri: string;
65 decorations: Decoration[];
66}
67
68interface Decoration {
69 range: lc.Range;
70 tag: string;
71 bindingHash?: string;
72}
73
74// Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76 56// Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76
75function fancify(seed: string, shade: 'light' | 'dark') { 57function fancify(seed: string, shade: 'light' | 'dark') {
76 const random = randomU32Numbers(hashString(seed)); 58 const random = randomU32Numbers(hashString(seed));
@@ -108,7 +90,7 @@ class Highlighter {
108 this.decorations = null; 90 this.decorations = null;
109 } 91 }
110 92
111 public setHighlights(editor: vscode.TextEditor, highlights: Decoration[]) { 93 public setHighlights(editor: vscode.TextEditor, highlights: ra.Decoration[]) {
112 const client = this.ctx.client; 94 const client = this.ctx.client;
113 if (!client) return; 95 if (!client) return;
114 // Initialize decorations if necessary 96 // Initialize decorations if necessary
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index 5f9229efb..5951cf1b4 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -1,5 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as ra from './rust-analyzer-api';
3 3
4import { Ctx } from './ctx'; 4import { Ctx } from './ctx';
5import { log, sendRequestWithRetry } from './util'; 5import { log, sendRequestWithRetry } from './util';
@@ -39,16 +39,6 @@ export function activateInlayHints(ctx: Ctx) {
39 void hintsUpdater.setEnabled(ctx.config.displayInlayHints); 39 void hintsUpdater.setEnabled(ctx.config.displayInlayHints);
40} 40}
41 41
42interface InlayHintsParams {
43 textDocument: lc.TextDocumentIdentifier;
44}
45
46interface InlayHint {
47 range: vscode.Range;
48 kind: "TypeHint" | "ParameterHint";
49 label: string;
50}
51
52const typeHintDecorationType = vscode.window.createTextEditorDecorationType({ 42const typeHintDecorationType = vscode.window.createTextEditorDecorationType({
53 after: { 43 after: {
54 color: new vscode.ThemeColor('rust_analyzer.inlayHint'), 44 color: new vscode.ThemeColor('rust_analyzer.inlayHint'),
@@ -107,9 +97,9 @@ class HintsUpdater {
107 if (newHints == null) return; 97 if (newHints == null) return;
108 98
109 const newTypeDecorations = newHints 99 const newTypeDecorations = newHints
110 .filter(hint => hint.kind === 'TypeHint') 100 .filter(hint => hint.kind === ra.InlayKind.TypeHint)
111 .map(hint => ({ 101 .map(hint => ({
112 range: hint.range, 102 range: this.ctx.client.protocol2CodeConverter.asRange(hint.range),
113 renderOptions: { 103 renderOptions: {
114 after: { 104 after: {
115 contentText: `: ${hint.label}`, 105 contentText: `: ${hint.label}`,
@@ -119,9 +109,9 @@ class HintsUpdater {
119 this.setTypeDecorations(editor, newTypeDecorations); 109 this.setTypeDecorations(editor, newTypeDecorations);
120 110
121 const newParameterDecorations = newHints 111 const newParameterDecorations = newHints
122 .filter(hint => hint.kind === 'ParameterHint') 112 .filter(hint => hint.kind === ra.InlayKind.ParameterHint)
123 .map(hint => ({ 113 .map(hint => ({
124 range: hint.range, 114 range: this.ctx.client.protocol2CodeConverter.asRange(hint.range),
125 renderOptions: { 115 renderOptions: {
126 before: { 116 before: {
127 contentText: `${hint.label}: `, 117 contentText: `${hint.label}: `,
@@ -151,20 +141,15 @@ class HintsUpdater {
151 ); 141 );
152 } 142 }
153 143
154 private async queryHints(documentUri: string): Promise<InlayHint[] | null> { 144 private async queryHints(documentUri: string): Promise<ra.InlayHint[] | null> {
155 this.pending.get(documentUri)?.cancel(); 145 this.pending.get(documentUri)?.cancel();
156 146
157 const tokenSource = new vscode.CancellationTokenSource(); 147 const tokenSource = new vscode.CancellationTokenSource();
158 this.pending.set(documentUri, tokenSource); 148 this.pending.set(documentUri, tokenSource);
159 149
160 const request: InlayHintsParams = { textDocument: { uri: documentUri } }; 150 const request = { textDocument: { uri: documentUri } };
161 151
162 return sendRequestWithRetry<InlayHint[]>( 152 return sendRequestWithRetry(this.ctx.client, ra.inlayHints, request, tokenSource.token)
163 this.ctx.client,
164 'rust-analyzer/inlayHints',
165 request,
166 tokenSource.token
167 )
168 .catch(_ => null) 153 .catch(_ => null)
169 .finally(() => { 154 .finally(() => {
170 if (!tokenSource.token.isCancellationRequested) { 155 if (!tokenSource.token.isCancellationRequested) {
diff --git a/editors/code/src/installation/fetch_artifact_release_info.ts b/editors/code/src/installation/fetch_artifact_release_info.ts
index 1b6fc8d48..b1b5a3485 100644
--- a/editors/code/src/installation/fetch_artifact_release_info.ts
+++ b/editors/code/src/installation/fetch_artifact_release_info.ts
@@ -4,41 +4,61 @@ import { log } from "../util";
4 4
5const GITHUB_API_ENDPOINT_URL = "https://api.github.com"; 5const GITHUB_API_ENDPOINT_URL = "https://api.github.com";
6 6
7
8/** 7/**
9 * Fetches the release with `releaseTag` (or just latest release when not specified) 8 * Fetches the release with `releaseTag` from GitHub `repo` and
10 * from GitHub `repo` and returns metadata about `artifactFileName` shipped with 9 * returns metadata about `artifactFileName` shipped with
11 * this release or `null` if no such artifact was published. 10 * this release.
11 *
12 * @throws Error upon network failure or if no such repository, release, or artifact exists.
12 */ 13 */
13export async function fetchArtifactReleaseInfo( 14export async function fetchArtifactReleaseInfo(
14 repo: GithubRepo, artifactFileName: string, releaseTag?: string 15 repo: GithubRepo,
15): Promise<null | ArtifactReleaseInfo> { 16 artifactFileName: string,
17 releaseTag: string
18): Promise<ArtifactReleaseInfo> {
16 19
17 const repoOwner = encodeURIComponent(repo.owner); 20 const repoOwner = encodeURIComponent(repo.owner);
18 const repoName = encodeURIComponent(repo.name); 21 const repoName = encodeURIComponent(repo.name);
19 22
20 const apiEndpointPath = releaseTag 23 const apiEndpointPath = `/repos/${repoOwner}/${repoName}/releases/tags/${releaseTag}`;
21 ? `/repos/${repoOwner}/${repoName}/releases/tags/${releaseTag}`
22 : `/repos/${repoOwner}/${repoName}/releases/latest`;
23 24
24 const requestUrl = GITHUB_API_ENDPOINT_URL + apiEndpointPath; 25 const requestUrl = GITHUB_API_ENDPOINT_URL + apiEndpointPath;
25 26
26 // We skip runtime type checks for simplicity (here we cast from `any` to `GithubRelease`)
27
28 log.debug("Issuing request for released artifacts metadata to", requestUrl); 27 log.debug("Issuing request for released artifacts metadata to", requestUrl);
29 28
30 // FIXME: handle non-ok response 29 const response = await fetch(requestUrl, { headers: { Accept: "application/vnd.github.v3+json" } });
31 const response: GithubRelease = await fetch(requestUrl, {
32 headers: { Accept: "application/vnd.github.v3+json" }
33 })
34 .then(res => res.json());
35 30
36 const artifact = response.assets.find(artifact => artifact.name === artifactFileName); 31 if (!response.ok) {
32 log.error("Error fetching artifact release info", {
33 requestUrl,
34 releaseTag,
35 artifactFileName,
36 response: {
37 headers: response.headers,
38 status: response.status,
39 body: await response.text(),
40 }
41 });
42
43 throw new Error(
44 `Got response ${response.status} when trying to fetch ` +
45 `"${artifactFileName}" artifact release info for ${releaseTag} release`
46 );
47 }
37 48
38 if (!artifact) return null; 49 // We skip runtime type checks for simplicity (here we cast from `any` to `GithubRelease`)
50 const release: GithubRelease = await response.json();
51
52 const artifact = release.assets.find(artifact => artifact.name === artifactFileName);
53
54 if (!artifact) {
55 throw new Error(
56 `Artifact ${artifactFileName} was not found in ${release.name} release!`
57 );
58 }
39 59
40 return { 60 return {
41 releaseName: response.name, 61 releaseName: release.name,
42 downloadUrl: artifact.browser_download_url 62 downloadUrl: artifact.browser_download_url
43 }; 63 };
44 64
diff --git a/editors/code/src/installation/server.ts b/editors/code/src/installation/server.ts
index 9de257dd5..cb5e56844 100644
--- a/editors/code/src/installation/server.ts
+++ b/editors/code/src/installation/server.ts
@@ -63,7 +63,7 @@ export async function ensureServerBinary(source: null | BinarySource): Promise<n
63 63
64async function downloadServer(source: BinarySource.GithubRelease): Promise<boolean> { 64async function downloadServer(source: BinarySource.GithubRelease): Promise<boolean> {
65 try { 65 try {
66 const releaseInfo = (await fetchArtifactReleaseInfo(source.repo, source.file, source.version))!; 66 const releaseInfo = await fetchArtifactReleaseInfo(source.repo, source.file, source.version);
67 67
68 await downloadArtifact(releaseInfo, source.file, source.dir, "language server"); 68 await downloadArtifact(releaseInfo, source.file, source.dir, "language server");
69 await setServerVersion(source.storage, releaseInfo.releaseName); 69 await setServerVersion(source.storage, releaseInfo.releaseName);
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
new file mode 100644
index 000000000..c5a010e94
--- /dev/null
+++ b/editors/code/src/rust-analyzer-api.ts
@@ -0,0 +1,117 @@
1/**
2 * This file mirrors `crates/rust-analyzer/src/req.rs` declarations.
3 */
4
5import * as lc from "vscode-languageclient";
6
7type Option<T> = null | T;
8type Vec<T> = T[];
9type FxHashMap<K extends PropertyKey, V> = Record<K, V>;
10
11function request<TParams, TResult>(method: string) {
12 return new lc.RequestType<TParams, TResult, unknown>(`rust-analyzer/${method}`);
13}
14function notification<TParam>(method: string) {
15 return new lc.NotificationType<TParam>(method);
16}
17
18
19export const analyzerStatus = request<null, string>("analyzerStatus");
20
21
22export const collectGarbage = request<null, null>("collectGarbage");
23
24
25export interface SyntaxTreeParams {
26 textDocument: lc.TextDocumentIdentifier;
27 range: Option<lc.Range>;
28}
29export const syntaxTree = request<SyntaxTreeParams, string>("syntaxTree");
30
31
32export interface ExpandMacroParams {
33 textDocument: lc.TextDocumentIdentifier;
34 position: Option<lc.Position>;
35}
36export interface ExpandedMacro {
37 name: string;
38 expansion: string;
39}
40export const expandMacro = request<ExpandMacroParams, Option<ExpandedMacro>>("expandMacro");
41
42
43export interface FindMatchingBraceParams {
44 textDocument: lc.TextDocumentIdentifier;
45 offsets: Vec<lc.Position>;
46}
47export const findMatchingBrace = request<FindMatchingBraceParams, Vec<lc.Position>>("findMatchingBrace");
48
49
50export interface PublishDecorationsParams {
51 uri: string;
52 decorations: Vec<Decoration>;
53}
54export interface Decoration {
55 range: lc.Range;
56 tag: string;
57 bindingHash: Option<string>;
58}
59export const decorationsRequest = request<lc.TextDocumentIdentifier, Vec<Decoration>>("decorationsRequest");
60
61
62export const parentModule = request<lc.TextDocumentPositionParams, Vec<lc.Location>>("parentModule");
63
64
65export interface JoinLinesParams {
66 textDocument: lc.TextDocumentIdentifier;
67 range: lc.Range;
68}
69export const joinLines = request<JoinLinesParams, SourceChange>("joinLines");
70
71
72export const onEnter = request<lc.TextDocumentPositionParams, Option<SourceChange>>("onEnter");
73
74export interface RunnablesParams {
75 textDocument: lc.TextDocumentIdentifier;
76 position: Option<lc.Position>;
77}
78export interface Runnable {
79 range: lc.Range;
80 label: string;
81 bin: string;
82 args: Vec<string>;
83 env: FxHashMap<string, string>;
84 cwd: Option<string>;
85}
86export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables");
87
88
89export const enum InlayKind {
90 TypeHint = "TypeHint",
91 ParameterHint = "ParameterHint",
92}
93export interface InlayHint {
94 range: lc.Range;
95 kind: InlayKind;
96 label: string;
97}
98export interface InlayHintsParams {
99 textDocument: lc.TextDocumentIdentifier;
100}
101export const inlayHints = request<InlayHintsParams, Vec<InlayHint>>("inlayHints");
102
103
104export interface SsrParams {
105 arg: string;
106}
107export const ssr = request<SsrParams, SourceChange>("ssr");
108
109
110export const publishDecorations = notification<PublishDecorationsParams>("publishDecorations");
111
112
113export interface SourceChange {
114 label: string;
115 workspaceEdit: lc.WorkspaceEdit;
116 cursorPosition: Option<lc.TextDocumentPositionParams>;
117}
diff --git a/editors/code/src/source_change.ts b/editors/code/src/source_change.ts
index a336269ba..399a150c6 100644
--- a/editors/code/src/source_change.ts
+++ b/editors/code/src/source_change.ts
@@ -1,15 +1,10 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as ra from './rust-analyzer-api';
3 4
4import { Ctx } from './ctx'; 5import { Ctx } from './ctx';
5 6
6export interface SourceChange { 7export async function applySourceChange(ctx: Ctx, change: ra.SourceChange) {
7 label: string;
8 workspaceEdit: lc.WorkspaceEdit;
9 cursorPosition?: lc.TextDocumentPositionParams;
10}
11
12export async function applySourceChange(ctx: Ctx, change: SourceChange) {
13 const client = ctx.client; 8 const client = ctx.client;
14 if (!client) return; 9 if (!client) return;
15 10
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 2f18f85a3..68c2a94d0 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -20,21 +20,21 @@ export const log = {
20 } 20 }
21}; 21};
22 22
23export async function sendRequestWithRetry<R>( 23export async function sendRequestWithRetry<TParam, TRet>(
24 client: lc.LanguageClient, 24 client: lc.LanguageClient,
25 method: string, 25 reqType: lc.RequestType<TParam, TRet, unknown>,
26 param: unknown, 26 param: TParam,
27 token?: vscode.CancellationToken, 27 token?: vscode.CancellationToken,
28): Promise<R> { 28): Promise<TRet> {
29 for (const delay of [2, 4, 6, 8, 10, null]) { 29 for (const delay of [2, 4, 6, 8, 10, null]) {
30 try { 30 try {
31 return await (token 31 return await (token
32 ? client.sendRequest(method, param, token) 32 ? client.sendRequest(reqType, param, token)
33 : client.sendRequest(method, param) 33 : client.sendRequest(reqType, param)
34 ); 34 );
35 } catch (error) { 35 } catch (error) {
36 if (delay === null) { 36 if (delay === null) {
37 log.error("LSP request timed out", { method, param, error }); 37 log.error("LSP request timed out", { method: reqType.method, param, error });
38 throw error; 38 throw error;
39 } 39 }
40 40
@@ -43,7 +43,7 @@ export async function sendRequestWithRetry<R>(
43 } 43 }
44 44
45 if (error.code !== lc.ErrorCodes.ContentModified) { 45 if (error.code !== lc.ErrorCodes.ContentModified) {
46 log.error("LSP request failed", { method, param, error }); 46 log.error("LSP request failed", { method: reqType.method, param, error });
47 throw error; 47 throw error;
48 } 48 }
49 49