diff options
Diffstat (limited to 'editors/code/src')
-rw-r--r-- | editors/code/src/client.ts | 23 | ||||
-rw-r--r-- | editors/code/src/commands.ts | 32 | ||||
-rw-r--r-- | editors/code/src/lsp_ext.ts | 12 | ||||
-rw-r--r-- | editors/code/src/main.ts | 1 | ||||
-rw-r--r-- | editors/code/src/snippets.ts | 23 |
5 files changed, 66 insertions, 25 deletions
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index d032b45b7..63ab82dde 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -4,6 +4,7 @@ import * as ra from '../src/lsp_ext'; | |||
4 | import * as Is from 'vscode-languageclient/lib/common/utils/is'; | 4 | import * as Is from 'vscode-languageclient/lib/common/utils/is'; |
5 | import { DocumentSemanticsTokensSignature, DocumentSemanticsTokensEditsSignature, DocumentRangeSemanticTokensSignature } from 'vscode-languageclient/lib/common/semanticTokens'; | 5 | import { DocumentSemanticsTokensSignature, DocumentSemanticsTokensEditsSignature, DocumentRangeSemanticTokensSignature } from 'vscode-languageclient/lib/common/semanticTokens'; |
6 | import { assert } from './util'; | 6 | import { assert } from './util'; |
7 | import { WorkspaceEdit } from 'vscode'; | ||
7 | 8 | ||
8 | function renderCommand(cmd: ra.CommandLink) { | 9 | function renderCommand(cmd: ra.CommandLink) { |
9 | return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; | 10 | return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; |
@@ -75,8 +76,8 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
75 | return Promise.resolve(null); | 76 | return Promise.resolve(null); |
76 | }); | 77 | }); |
77 | }, | 78 | }, |
78 | // Using custom handling of CodeActions where each code action is resolved lazily | 79 | // Using custom handling of CodeActions to support action groups and snippet edits. |
79 | // That's why we are not waiting for any command or edits | 80 | // Note that this means we have to re-implement lazy edit resolving ourselves as well. |
80 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { | 81 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { |
81 | const params: lc.CodeActionParams = { | 82 | const params: lc.CodeActionParams = { |
82 | textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), | 83 | textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), |
@@ -99,16 +100,15 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
99 | const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind); | 100 | const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind); |
100 | const action = new vscode.CodeAction(item.title, kind); | 101 | const action = new vscode.CodeAction(item.title, kind); |
101 | const group = (item as any).group; | 102 | const group = (item as any).group; |
102 | const id = (item as any).id; | ||
103 | const resolveParams: ra.ResolveCodeActionParams = { | ||
104 | id: id, | ||
105 | codeActionParams: params | ||
106 | }; | ||
107 | action.command = { | 103 | action.command = { |
108 | command: "rust-analyzer.resolveCodeAction", | 104 | command: "rust-analyzer.resolveCodeAction", |
109 | title: item.title, | 105 | title: item.title, |
110 | arguments: [resolveParams], | 106 | arguments: [item], |
111 | }; | 107 | }; |
108 | |||
109 | // Set a dummy edit, so that VS Code doesn't try to resolve this. | ||
110 | action.edit = new WorkspaceEdit(); | ||
111 | |||
112 | if (group) { | 112 | if (group) { |
113 | let entry = groups.get(group); | 113 | let entry = groups.get(group); |
114 | if (!entry) { | 114 | if (!entry) { |
@@ -134,6 +134,10 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
134 | return { label: item.title, arguments: item.command!!.arguments!![0] }; | 134 | return { label: item.title, arguments: item.command!!.arguments!![0] }; |
135 | })], | 135 | })], |
136 | }; | 136 | }; |
137 | |||
138 | // Set a dummy edit, so that VS Code doesn't try to resolve this. | ||
139 | action.edit = new WorkspaceEdit(); | ||
140 | |||
137 | result[index] = action; | 141 | result[index] = action; |
138 | } | 142 | } |
139 | } | 143 | } |
@@ -164,13 +168,14 @@ class ExperimentalFeatures implements lc.StaticFeature { | |||
164 | const caps: any = capabilities.experimental ?? {}; | 168 | const caps: any = capabilities.experimental ?? {}; |
165 | caps.snippetTextEdit = true; | 169 | caps.snippetTextEdit = true; |
166 | caps.codeActionGroup = true; | 170 | caps.codeActionGroup = true; |
167 | caps.resolveCodeAction = true; | ||
168 | caps.hoverActions = true; | 171 | caps.hoverActions = true; |
169 | caps.statusNotification = true; | 172 | caps.statusNotification = true; |
170 | capabilities.experimental = caps; | 173 | capabilities.experimental = caps; |
171 | } | 174 | } |
172 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { | 175 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { |
173 | } | 176 | } |
177 | dispose(): void { | ||
178 | } | ||
174 | } | 179 | } |
175 | 180 | ||
176 | function isCodeActionWithoutEditsAndCommands(value: any): boolean { | 181 | function isCodeActionWithoutEditsAndCommands(value: any): boolean { |
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 22509e874..92bc4d7f7 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -188,6 +188,27 @@ export function parentModule(ctx: Ctx): Cmd { | |||
188 | }; | 188 | }; |
189 | } | 189 | } |
190 | 190 | ||
191 | export function openCargoToml(ctx: Ctx): Cmd { | ||
192 | return async () => { | ||
193 | const editor = ctx.activeRustEditor; | ||
194 | const client = ctx.client; | ||
195 | if (!editor || !client) return; | ||
196 | |||
197 | const response = await client.sendRequest(ra.openCargoToml, { | ||
198 | textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), | ||
199 | }); | ||
200 | if (!response) return; | ||
201 | |||
202 | const uri = client.protocol2CodeConverter.asUri(response.uri); | ||
203 | const range = client.protocol2CodeConverter.asRange(response.range); | ||
204 | |||
205 | const doc = await vscode.workspace.openTextDocument(uri); | ||
206 | const e = await vscode.window.showTextDocument(doc); | ||
207 | e.selection = new vscode.Selection(range.start, range.start); | ||
208 | e.revealRange(range, vscode.TextEditorRevealType.InCenter); | ||
209 | }; | ||
210 | } | ||
211 | |||
191 | export function ssr(ctx: Ctx): Cmd { | 212 | export function ssr(ctx: Ctx): Cmd { |
192 | return async () => { | 213 | return async () => { |
193 | const editor = vscode.window.activeTextEditor; | 214 | const editor = vscode.window.activeTextEditor; |
@@ -395,7 +416,7 @@ export function showReferences(ctx: Ctx): Cmd { | |||
395 | } | 416 | } |
396 | 417 | ||
397 | export function applyActionGroup(_ctx: Ctx): Cmd { | 418 | export function applyActionGroup(_ctx: Ctx): Cmd { |
398 | return async (actions: { label: string; arguments: ra.ResolveCodeActionParams }[]) => { | 419 | return async (actions: { label: string; arguments: lc.CodeAction }[]) => { |
399 | const selectedAction = await vscode.window.showQuickPick(actions); | 420 | const selectedAction = await vscode.window.showQuickPick(actions); |
400 | if (!selectedAction) return; | 421 | if (!selectedAction) return; |
401 | vscode.commands.executeCommand( | 422 | vscode.commands.executeCommand( |
@@ -442,12 +463,13 @@ export function openDocs(ctx: Ctx): Cmd { | |||
442 | 463 | ||
443 | export function resolveCodeAction(ctx: Ctx): Cmd { | 464 | export function resolveCodeAction(ctx: Ctx): Cmd { |
444 | const client = ctx.client; | 465 | const client = ctx.client; |
445 | return async (params: ra.ResolveCodeActionParams) => { | 466 | return async (params: lc.CodeAction) => { |
446 | const item: lc.WorkspaceEdit = await client.sendRequest(ra.resolveCodeAction, params); | 467 | params.command = undefined; |
447 | if (!item) { | 468 | const item = await client.sendRequest(lc.CodeActionResolveRequest.type, params); |
469 | if (!item.edit) { | ||
448 | return; | 470 | return; |
449 | } | 471 | } |
450 | const edit = client.protocol2CodeConverter.asWorkspaceEdit(item); | 472 | const edit = client.protocol2CodeConverter.asWorkspaceEdit(item.edit); |
451 | await applySnippetWorkspaceEdit(edit); | 473 | await applySnippetWorkspaceEdit(edit); |
452 | }; | 474 | }; |
453 | } | 475 | } |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index fc8e120b3..5e877ce65 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -43,12 +43,6 @@ export const matchingBrace = new lc.RequestType<MatchingBraceParams, lc.Position | |||
43 | 43 | ||
44 | export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule"); | 44 | export const parentModule = new lc.RequestType<lc.TextDocumentPositionParams, lc.LocationLink[], void>("experimental/parentModule"); |
45 | 45 | ||
46 | export interface ResolveCodeActionParams { | ||
47 | id: string; | ||
48 | codeActionParams: lc.CodeActionParams; | ||
49 | } | ||
50 | export const resolveCodeAction = new lc.RequestType<ResolveCodeActionParams, lc.WorkspaceEdit, unknown>('experimental/resolveCodeAction'); | ||
51 | |||
52 | export interface JoinLinesParams { | 46 | export interface JoinLinesParams { |
53 | textDocument: lc.TextDocumentIdentifier; | 47 | textDocument: lc.TextDocumentIdentifier; |
54 | ranges: lc.Range[]; | 48 | ranges: lc.Range[]; |
@@ -120,3 +114,9 @@ export interface CommandLinkGroup { | |||
120 | } | 114 | } |
121 | 115 | ||
122 | export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, string | void, void>('experimental/externalDocs'); | 116 | export const openDocs = new lc.RequestType<lc.TextDocumentPositionParams, string | void, void>('experimental/externalDocs'); |
117 | |||
118 | export const openCargoToml = new lc.RequestType<OpenCargoTomlParams, lc.Location, void>("experimental/openCargoToml"); | ||
119 | |||
120 | export interface OpenCargoTomlParams { | ||
121 | textDocument: lc.TextDocumentIdentifier; | ||
122 | } | ||
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 09543e348..2f3dde8ac 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -111,6 +111,7 @@ async function tryActivate(context: vscode.ExtensionContext) { | |||
111 | ctx.registerCommand('debug', commands.debug); | 111 | ctx.registerCommand('debug', commands.debug); |
112 | ctx.registerCommand('newDebugConfig', commands.newDebugConfig); | 112 | ctx.registerCommand('newDebugConfig', commands.newDebugConfig); |
113 | ctx.registerCommand('openDocs', commands.openDocs); | 113 | ctx.registerCommand('openDocs', commands.openDocs); |
114 | ctx.registerCommand('openCargoToml', commands.openCargoToml); | ||
114 | 115 | ||
115 | defaultOnEnter.dispose(); | 116 | defaultOnEnter.dispose(); |
116 | ctx.registerCommand('onEnter', commands.onEnter); | 117 | ctx.registerCommand('onEnter', commands.onEnter); |
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts index 258b49982..fee736e7d 100644 --- a/editors/code/src/snippets.ts +++ b/editors/code/src/snippets.ts | |||
@@ -3,16 +3,29 @@ import * as vscode from 'vscode'; | |||
3 | import { assert } from './util'; | 3 | import { assert } from './util'; |
4 | 4 | ||
5 | export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) { | 5 | export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) { |
6 | assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); | 6 | if (edit.entries().length === 1) { |
7 | const [uri, edits] = edit.entries()[0]; | 7 | const [uri, edits] = edit.entries()[0]; |
8 | const editor = await editorFromUri(uri); | ||
9 | if (editor) await applySnippetTextEdits(editor, edits); | ||
10 | return; | ||
11 | } | ||
12 | for (const [uri, edits] of edit.entries()) { | ||
13 | const editor = await editorFromUri(uri); | ||
14 | if (editor) await editor.edit((builder) => { | ||
15 | for (const indel of edits) { | ||
16 | assert(!parseSnippet(indel.newText), `bad ws edit: snippet received with multiple edits: ${JSON.stringify(edit)}`); | ||
17 | builder.replace(indel.range, indel.newText); | ||
18 | } | ||
19 | }); | ||
20 | } | ||
21 | } | ||
8 | 22 | ||
23 | async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undefined> { | ||
9 | if (vscode.window.activeTextEditor?.document.uri !== uri) { | 24 | if (vscode.window.activeTextEditor?.document.uri !== uri) { |
10 | // `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed | 25 | // `vscode.window.visibleTextEditors` only contains editors whose contents are being displayed |
11 | await vscode.window.showTextDocument(uri, {}); | 26 | await vscode.window.showTextDocument(uri, {}); |
12 | } | 27 | } |
13 | const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); | 28 | return vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); |
14 | if (!editor) return; | ||
15 | await applySnippetTextEdits(editor, edits); | ||
16 | } | 29 | } |
17 | 30 | ||
18 | export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) { | 31 | export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) { |