diff options
Diffstat (limited to 'editors/code/src/client.ts')
-rw-r--r-- | editors/code/src/client.ts | 98 |
1 files changed, 93 insertions, 5 deletions
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index cffdcf11a..d64f9a3f9 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -31,24 +31,112 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
31 | const res = await next(document, token); | 31 | const res = await next(document, token); |
32 | if (res === undefined) throw new Error('busy'); | 32 | if (res === undefined) throw new Error('busy'); |
33 | return res; | 33 | return res; |
34 | }, | ||
35 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { | ||
36 | const params: lc.CodeActionParams = { | ||
37 | textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), | ||
38 | range: client.code2ProtocolConverter.asRange(range), | ||
39 | context: client.code2ProtocolConverter.asCodeActionContext(context) | ||
40 | }; | ||
41 | return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => { | ||
42 | if (values === null) return undefined; | ||
43 | const result: (vscode.CodeAction | vscode.Command)[] = []; | ||
44 | const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>(); | ||
45 | for (const item of values) { | ||
46 | if (lc.CodeAction.is(item)) { | ||
47 | const action = client.protocol2CodeConverter.asCodeAction(item); | ||
48 | const group = actionGroup(item); | ||
49 | if (isSnippetEdit(item) || group) { | ||
50 | action.command = { | ||
51 | command: "rust-analyzer.applySnippetWorkspaceEdit", | ||
52 | title: "", | ||
53 | arguments: [action.edit], | ||
54 | }; | ||
55 | action.edit = undefined; | ||
56 | } | ||
57 | |||
58 | if (group) { | ||
59 | let entry = groups.get(group); | ||
60 | if (!entry) { | ||
61 | entry = { index: result.length, items: [] }; | ||
62 | groups.set(group, entry); | ||
63 | result.push(action); | ||
64 | } | ||
65 | entry.items.push(action); | ||
66 | } else { | ||
67 | result.push(action); | ||
68 | } | ||
69 | } else { | ||
70 | const command = client.protocol2CodeConverter.asCommand(item); | ||
71 | result.push(command); | ||
72 | } | ||
73 | } | ||
74 | for (const [group, { index, items }] of groups) { | ||
75 | if (items.length === 1) { | ||
76 | result[index] = items[0]; | ||
77 | } else { | ||
78 | const action = new vscode.CodeAction(group); | ||
79 | action.command = { | ||
80 | command: "rust-analyzer.applyActionGroup", | ||
81 | title: "", | ||
82 | arguments: [items.map((item) => { | ||
83 | return { label: item.title, edit: item.command!!.arguments!![0] }; | ||
84 | })], | ||
85 | }; | ||
86 | result[index] = action; | ||
87 | } | ||
88 | } | ||
89 | return result; | ||
90 | }, | ||
91 | (_error) => undefined | ||
92 | ); | ||
34 | } | 93 | } |
94 | |||
35 | } as any | 95 | } as any |
36 | }; | 96 | }; |
37 | 97 | ||
38 | const res = new lc.LanguageClient( | 98 | const client = new lc.LanguageClient( |
39 | 'rust-analyzer', | 99 | 'rust-analyzer', |
40 | 'Rust Analyzer Language Server', | 100 | 'Rust Analyzer Language Server', |
41 | serverOptions, | 101 | serverOptions, |
42 | clientOptions, | 102 | clientOptions, |
43 | ); | 103 | ); |
44 | 104 | ||
45 | // To turn on all proposed features use: res.registerProposedFeatures(); | 105 | // To turn on all proposed features use: client.registerProposedFeatures(); |
46 | // Here we want to enable CallHierarchyFeature and SemanticTokensFeature | 106 | // Here we want to enable CallHierarchyFeature and SemanticTokensFeature |
47 | // since they are available on stable. | 107 | // since they are available on stable. |
48 | // Note that while these features are stable in vscode their LSP protocol | 108 | // Note that while these features are stable in vscode their LSP protocol |
49 | // implementations are still in the "proposed" category for 3.16. | 109 | // implementations are still in the "proposed" category for 3.16. |
50 | res.registerFeature(new CallHierarchyFeature(res)); | 110 | client.registerFeature(new CallHierarchyFeature(client)); |
51 | res.registerFeature(new SemanticTokensFeature(res)); | 111 | client.registerFeature(new SemanticTokensFeature(client)); |
112 | client.registerFeature(new ExperimentalFeatures()); | ||
113 | |||
114 | return client; | ||
115 | } | ||
116 | |||
117 | class ExperimentalFeatures implements lc.StaticFeature { | ||
118 | fillClientCapabilities(capabilities: lc.ClientCapabilities): void { | ||
119 | const caps: any = capabilities.experimental ?? {}; | ||
120 | caps.snippetTextEdit = true; | ||
121 | caps.codeActionGroup = true; | ||
122 | capabilities.experimental = caps; | ||
123 | } | ||
124 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { | ||
125 | } | ||
126 | } | ||
127 | |||
128 | function isSnippetEdit(action: lc.CodeAction): boolean { | ||
129 | const documentChanges = action.edit?.documentChanges ?? []; | ||
130 | for (const edit of documentChanges) { | ||
131 | if (lc.TextDocumentEdit.is(edit)) { | ||
132 | if (edit.edits.some((indel) => (indel as any).insertTextFormat === lc.InsertTextFormat.Snippet)) { | ||
133 | return true; | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | return false; | ||
138 | } | ||
52 | 139 | ||
53 | return res; | 140 | function actionGroup(action: lc.CodeAction): string | undefined { |
141 | return (action as any).group; | ||
54 | } | 142 | } |