aboutsummaryrefslogtreecommitdiff
path: root/editors
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-05-18 00:53:55 +0100
committerAleksey Kladov <[email protected]>2020-05-19 19:28:27 +0100
commit3dd68c1ba3e72a0959bcdaa46e730a7ae4d9ed4c (patch)
treee7794cbdea1ffb7f45d2d87330b6e15c16c3942b /editors
parent2bf6b16a7f174ea3f581f26f642b4febff0b9ce8 (diff)
Implement client-side of SnippetTextEdit
Diffstat (limited to 'editors')
-rw-r--r--editors/code/src/client.ts48
-rw-r--r--editors/code/src/commands/index.ts34
-rw-r--r--editors/code/src/main.ts1
3 files changed, 81 insertions, 2 deletions
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 2067738ea..fac1a0be3 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -31,7 +31,39 @@ 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 for (const item of values) {
45 if (lc.CodeAction.is(item)) {
46 const action = client.protocol2CodeConverter.asCodeAction(item);
47 if (isSnippetEdit(item)) {
48 action.command = {
49 command: "rust-analyzer.applySnippetWorkspaceEdit",
50 title: "",
51 arguments: [action.edit],
52 };
53 action.edit = undefined;
54 }
55 result.push(action);
56 } else {
57 const command = client.protocol2CodeConverter.asCommand(item);
58 result.push(command);
59 }
60 }
61 return result;
62 },
63 (_error) => undefined
64 );
34 } 65 }
66
35 } as any 67 } as any
36 }; 68 };
37 69
@@ -42,7 +74,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
42 clientOptions, 74 clientOptions,
43 ); 75 );
44 76
45 // To turn on all proposed features use: res.registerProposedFeatures(); 77 // To turn on all proposed features use: client.registerProposedFeatures();
46 // Here we want to enable CallHierarchyFeature and SemanticTokensFeature 78 // Here we want to enable CallHierarchyFeature and SemanticTokensFeature
47 // since they are available on stable. 79 // since they are available on stable.
48 // Note that while these features are stable in vscode their LSP protocol 80 // Note that while these features are stable in vscode their LSP protocol
@@ -58,8 +90,20 @@ class SnippetTextEditFeature implements lc.StaticFeature {
58 fillClientCapabilities(capabilities: lc.ClientCapabilities): void { 90 fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
59 const caps: any = capabilities.experimental ?? {}; 91 const caps: any = capabilities.experimental ?? {};
60 caps.snippetTextEdit = true; 92 caps.snippetTextEdit = true;
61 capabilities.experimental = caps 93 capabilities.experimental = caps;
62 } 94 }
63 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { 95 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
64 } 96 }
65} 97}
98
99function isSnippetEdit(action: lc.CodeAction): boolean {
100 const documentChanges = action.edit?.documentChanges ?? [];
101 for (const edit of documentChanges) {
102 if (lc.TextDocumentEdit.is(edit)) {
103 if (edit.edits.some((indel) => (indel as any).insertTextFormat === lc.InsertTextFormat.Snippet)) {
104 return true;
105 }
106 }
107 }
108 return false;
109}
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index bdb7fc3b0..770d11bd3 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -4,6 +4,7 @@ import * as ra from '../rust-analyzer-api';
4 4
5import { Ctx, Cmd } from '../ctx'; 5import { Ctx, Cmd } from '../ctx';
6import * as sourceChange from '../source_change'; 6import * as sourceChange from '../source_change';
7import { assert } from '../util';
7 8
8export * from './analyzer_status'; 9export * from './analyzer_status';
9export * from './matching_brace'; 10export * from './matching_brace';
@@ -51,3 +52,36 @@ export function selectAndApplySourceChange(ctx: Ctx): Cmd {
51 } 52 }
52 }; 53 };
53} 54}
55
56export function applySnippetWorkspaceEdit(_ctx: Ctx): Cmd {
57 return async (edit: vscode.WorkspaceEdit) => {
58 assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`);
59 const [uri, edits] = edit.entries()[0];
60
61 const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
62 if (!editor) return;
63
64 let editWithSnippet: vscode.TextEdit | undefined = undefined;
65 let lineDelta = 0;
66 await editor.edit((builder) => {
67 for (const indel of edits) {
68 if (indel.newText.indexOf('$0') !== -1) {
69 editWithSnippet = indel;
70 } else {
71 if (!editWithSnippet) {
72 lineDelta = (indel.newText.match(/\n/g) || []).length - (indel.range.end.line - indel.range.start.line);
73 }
74 builder.replace(indel.range, indel.newText);
75 }
76 }
77 });
78 if (editWithSnippet) {
79 const snip = editWithSnippet as vscode.TextEdit;
80 const range = snip.range.with(
81 snip.range.start.with(snip.range.start.line + lineDelta),
82 snip.range.end.with(snip.range.end.line + lineDelta),
83 );
84 await editor.insertSnippet(new vscode.SnippetString(snip.newText), range);
85 }
86 };
87}
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index c015460b8..ac3bb365e 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -91,6 +91,7 @@ export async function activate(context: vscode.ExtensionContext) {
91 ctx.registerCommand('debugSingle', commands.debugSingle); 91 ctx.registerCommand('debugSingle', commands.debugSingle);
92 ctx.registerCommand('showReferences', commands.showReferences); 92 ctx.registerCommand('showReferences', commands.showReferences);
93 ctx.registerCommand('applySourceChange', commands.applySourceChange); 93 ctx.registerCommand('applySourceChange', commands.applySourceChange);
94 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEdit);
94 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); 95 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange);
95 96
96 ctx.pushCleanup(activateTaskProvider(workspaceFolder)); 97 ctx.pushCleanup(activateTaskProvider(workspaceFolder));