aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/commands/index.ts
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src/commands/index.ts')
-rw-r--r--editors/code/src/commands/index.ts257
1 files changed, 242 insertions, 15 deletions
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index 1ed8258d8..1585912a2 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -4,18 +4,251 @@ import * as ra from '../rust-analyzer-api';
4 4
5import { Ctx, Cmd } from '../ctx'; 5import { Ctx, Cmd } from '../ctx';
6import { applySnippetWorkspaceEdit } from '../snippets'; 6import { applySnippetWorkspaceEdit } from '../snippets';
7import { spawnSync } from 'child_process';
7 8
8export * from './analyzer_status';
9export * from './matching_brace';
10export * from './join_lines';
11export * from './on_enter';
12export * from './parent_module';
13export * from './syntax_tree'; 9export * from './syntax_tree';
14export * from './expand_macro';
15export * from './runnables'; 10export * from './runnables';
16export * from './ssr'; 11
17export * from './server_version'; 12export function analyzerStatus(ctx: Ctx): Cmd {
18export * from './toggle_inlay_hints'; 13 const tdcp = new class implements vscode.TextDocumentContentProvider {
14 readonly uri = vscode.Uri.parse('rust-analyzer-status://status');
15 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
16
17 provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> {
18 if (!vscode.window.activeTextEditor) return '';
19
20 return ctx.client.sendRequest(ra.analyzerStatus, null);
21 }
22
23 get onDidChange(): vscode.Event<vscode.Uri> {
24 return this.eventEmitter.event;
25 }
26 }();
27
28 let poller: NodeJS.Timer | undefined = undefined;
29
30 ctx.pushCleanup(
31 vscode.workspace.registerTextDocumentContentProvider(
32 'rust-analyzer-status',
33 tdcp,
34 ),
35 );
36
37 ctx.pushCleanup({
38 dispose() {
39 if (poller !== undefined) {
40 clearInterval(poller);
41 }
42 },
43 });
44
45 return async () => {
46 if (poller === undefined) {
47 poller = setInterval(() => tdcp.eventEmitter.fire(tdcp.uri), 1000);
48 }
49 const document = await vscode.workspace.openTextDocument(tdcp.uri);
50 return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true);
51 };
52}
53
54export function matchingBrace(ctx: Ctx): Cmd {
55 return async () => {
56 const editor = ctx.activeRustEditor;
57 const client = ctx.client;
58 if (!editor || !client) return;
59
60 const response = await client.sendRequest(ra.matchingBrace, {
61 textDocument: { uri: editor.document.uri.toString() },
62 positions: editor.selections.map(s =>
63 client.code2ProtocolConverter.asPosition(s.active),
64 ),
65 });
66 editor.selections = editor.selections.map((sel, idx) => {
67 const active = client.protocol2CodeConverter.asPosition(
68 response[idx],
69 );
70 const anchor = sel.isEmpty ? active : sel.anchor;
71 return new vscode.Selection(anchor, active);
72 });
73 editor.revealRange(editor.selection);
74 };
75}
76
77export function joinLines(ctx: Ctx): Cmd {
78 return async () => {
79 const editor = ctx.activeRustEditor;
80 const client = ctx.client;
81 if (!editor || !client) return;
82
83 const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, {
84 ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)),
85 textDocument: { uri: editor.document.uri.toString() },
86 });
87 editor.edit((builder) => {
88 client.protocol2CodeConverter.asTextEdits(items).forEach((edit) => {
89 builder.replace(edit.range, edit.newText);
90 });
91 });
92 };
93}
94
95export function onEnter(ctx: Ctx): Cmd {
96 async function handleKeypress() {
97 const editor = ctx.activeRustEditor;
98 const client = ctx.client;
99
100 if (!editor || !client) return false;
101
102 const change = await client.sendRequest(ra.onEnter, {
103 textDocument: { uri: editor.document.uri.toString() },
104 position: client.code2ProtocolConverter.asPosition(
105 editor.selection.active,
106 ),
107 }).catch(_error => {
108 // client.logFailedRequest(OnEnterRequest.type, error);
109 return null;
110 });
111 if (!change) return false;
112
113 const workspaceEdit = client.protocol2CodeConverter.asWorkspaceEdit(change);
114 await applySnippetWorkspaceEdit(workspaceEdit);
115 return true;
116 }
117
118 return async () => {
119 if (await handleKeypress()) return;
120
121 await vscode.commands.executeCommand('default:type', { text: '\n' });
122 };
123}
124
125export function parentModule(ctx: Ctx): Cmd {
126 return async () => {
127 const editor = ctx.activeRustEditor;
128 const client = ctx.client;
129 if (!editor || !client) return;
130
131 const response = await client.sendRequest(ra.parentModule, {
132 textDocument: { uri: editor.document.uri.toString() },
133 position: client.code2ProtocolConverter.asPosition(
134 editor.selection.active,
135 ),
136 });
137 const loc = response[0];
138 if (loc == null) return;
139
140 const uri = client.protocol2CodeConverter.asUri(loc.uri);
141 const range = client.protocol2CodeConverter.asRange(loc.range);
142
143 const doc = await vscode.workspace.openTextDocument(uri);
144 const e = await vscode.window.showTextDocument(doc);
145 e.selection = new vscode.Selection(range.start, range.start);
146 e.revealRange(range, vscode.TextEditorRevealType.InCenter);
147 };
148}
149
150export function ssr(ctx: Ctx): Cmd {
151 return async () => {
152 const client = ctx.client;
153 if (!client) return;
154
155 const options: vscode.InputBoxOptions = {
156 value: "() ==>> ()",
157 prompt: "Enter request, for example 'Foo($a:expr) ==> Foo::new($a)' ",
158 validateInput: async (x: string) => {
159 try {
160 await client.sendRequest(ra.ssr, { query: x, parseOnly: true });
161 } catch (e) {
162 return e.toString();
163 }
164 return null;
165 }
166 };
167 const request = await vscode.window.showInputBox(options);
168 if (!request) return;
169
170 const edit = await client.sendRequest(ra.ssr, { query: request, parseOnly: false });
171
172 await vscode.workspace.applyEdit(client.protocol2CodeConverter.asWorkspaceEdit(edit));
173 };
174}
175
176export function serverVersion(ctx: Ctx): Cmd {
177 return async () => {
178 const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" });
179 const commitHash = stdout.slice(`rust-analyzer `.length).trim();
180 const { releaseTag } = ctx.config.package;
181
182 void vscode.window.showInformationMessage(
183 `rust-analyzer version: ${releaseTag ?? "unreleased"} (${commitHash})`
184 );
185 };
186}
187
188export function toggleInlayHints(ctx: Ctx): Cmd {
189 return async () => {
190 await vscode
191 .workspace
192 .getConfiguration(`${ctx.config.rootSection}.inlayHints`)
193 .update('enable', !ctx.config.inlayHints.enable, vscode.ConfigurationTarget.Workspace);
194 };
195}
196
197// Opens the virtual file that will show the syntax tree
198//
199// The contents of the file come from the `TextDocumentContentProvider`
200export function expandMacro(ctx: Ctx): Cmd {
201 function codeFormat(expanded: ra.ExpandedMacro): string {
202 let result = `// Recursive expansion of ${expanded.name}! macro\n`;
203 result += '// ' + '='.repeat(result.length - 3);
204 result += '\n\n';
205 result += expanded.expansion;
206
207 return result;
208 }
209
210 const tdcp = new class implements vscode.TextDocumentContentProvider {
211 uri = vscode.Uri.parse('rust-analyzer://expandMacro/[EXPANSION].rs');
212 eventEmitter = new vscode.EventEmitter<vscode.Uri>();
213 async provideTextDocumentContent(_uri: vscode.Uri): Promise<string> {
214 const editor = vscode.window.activeTextEditor;
215 const client = ctx.client;
216 if (!editor || !client) return '';
217
218 const position = editor.selection.active;
219
220 const expanded = await client.sendRequest(ra.expandMacro, {
221 textDocument: { uri: editor.document.uri.toString() },
222 position,
223 });
224
225 if (expanded == null) return 'Not available';
226
227 return codeFormat(expanded);
228 }
229
230 get onDidChange(): vscode.Event<vscode.Uri> {
231 return this.eventEmitter.event;
232 }
233 }();
234
235 ctx.pushCleanup(
236 vscode.workspace.registerTextDocumentContentProvider(
237 'rust-analyzer',
238 tdcp,
239 ),
240 );
241
242 return async () => {
243 const document = await vscode.workspace.openTextDocument(tdcp.uri);
244 tdcp.eventEmitter.fire(tdcp.uri);
245 return vscode.window.showTextDocument(
246 document,
247 vscode.ViewColumn.Two,
248 true,
249 );
250 };
251}
19 252
20export function collectGarbage(ctx: Ctx): Cmd { 253export function collectGarbage(ctx: Ctx): Cmd {
21 return async () => ctx.client.sendRequest(ra.collectGarbage, null); 254 return async () => ctx.client.sendRequest(ra.collectGarbage, null);
@@ -35,12 +268,6 @@ export function showReferences(ctx: Ctx): Cmd {
35 }; 268 };
36} 269}
37 270
38export function applySourceChange(ctx: Ctx): Cmd {
39 return async (change: ra.SourceChange) => {
40 await sourceChange.applySourceChange(ctx, change);
41 };
42}
43
44export function applyActionGroup(_ctx: Ctx): Cmd { 271export function applyActionGroup(_ctx: Ctx): Cmd {
45 return async (actions: { label: string; edit: vscode.WorkspaceEdit }[]) => { 272 return async (actions: { label: string; edit: vscode.WorkspaceEdit }[]) => {
46 const selectedAction = await vscode.window.showQuickPick(actions); 273 const selectedAction = await vscode.window.showQuickPick(actions);