Skip to content

Commit d34d381

Browse files
authored
feat(lsp): Explicit type annotation code action (#2125)
1 parent cb8ef42 commit d34d381

File tree

9 files changed

+201
-10
lines changed

9 files changed

+201
-10
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
open Grain;
2+
open Compile;
3+
open Grain_parsing;
4+
open Grain_utils;
5+
open Grain_typed;
6+
open Grain_diagnostics;
7+
open Sourcetree;
8+
open Lsp_types;
9+
10+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#definitionParams
11+
module RequestParams = {
12+
[@deriving yojson({strict: false})]
13+
type code_action_context = {diagnostics: list(Protocol.diagnostic)};
14+
15+
[@deriving yojson({strict: false})]
16+
type t = {
17+
[@key "textDocument"]
18+
text_document: Protocol.text_document_identifier,
19+
range: Protocol.range,
20+
context: code_action_context,
21+
};
22+
};
23+
24+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#locationLink
25+
module ResponseResult = {
26+
[@deriving yojson]
27+
type code_action = {
28+
title: string,
29+
kind: string,
30+
edit: Protocol.workspace_edit,
31+
};
32+
33+
[@deriving yojson]
34+
type t = option(list(code_action));
35+
};
36+
37+
let send_no_result = (~id: Protocol.message_id) => {
38+
Protocol.response(~id, `Null);
39+
};
40+
41+
let explicit_type_annotation = (range, uri, type_str) => {
42+
ResponseResult.{
43+
title: "Annotate type",
44+
kind: "annotate-type",
45+
edit: {
46+
document_changes: [
47+
{
48+
text_document: {
49+
uri,
50+
version: None,
51+
},
52+
edits: [{range, new_text: ": " ++ type_str}],
53+
},
54+
],
55+
},
56+
};
57+
};
58+
59+
let send_code_actions =
60+
(id: Protocol.message_id, code_actions: list(ResponseResult.code_action)) => {
61+
Protocol.response(~id, ResponseResult.to_yojson(Some(code_actions)));
62+
};
63+
64+
let process_explicit_type_annotation = (uri, results: list(Sourcetree.node)) => {
65+
switch (results) {
66+
| [Pattern({pattern})]
67+
| [
68+
Pattern({pattern: {pat_desc: TPatAlias({pat_desc: TPatAny}, _, _)}}),
69+
Pattern({pattern}),
70+
..._,
71+
]
72+
when pattern.pat_extra == [] =>
73+
let loc = {...pattern.pat_loc, loc_start: pattern.pat_loc.loc_end};
74+
let type_str = Printtyp.string_of_type_scheme(pattern.pat_type);
75+
Some(explicit_type_annotation(Utils.loc_to_range(loc), uri, type_str));
76+
| _ => None
77+
};
78+
};
79+
80+
let process =
81+
(
82+
~id: Protocol.message_id,
83+
~compiled_code: Hashtbl.t(Protocol.uri, Lsp_types.code),
84+
~documents: Hashtbl.t(Protocol.uri, string),
85+
params: RequestParams.t,
86+
) => {
87+
switch (Hashtbl.find_opt(compiled_code, params.text_document.uri)) {
88+
| None => send_no_result(~id)
89+
| Some({program, sourcetree}) =>
90+
let results = Sourcetree.query(params.range.range_start, sourcetree);
91+
92+
let code_actions =
93+
List.filter_map(
94+
x => x,
95+
[
96+
process_explicit_type_annotation(params.text_document.uri, results),
97+
],
98+
);
99+
100+
switch (code_actions) {
101+
| [] => send_no_result(~id)
102+
| _ => send_code_actions(id, code_actions)
103+
};
104+
};
105+
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
open Grain_typed;
2+
3+
//https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#documentFormattingParams
4+
module RequestParams: {
5+
[@deriving yojson({strict: false})]
6+
type t;
7+
};
8+
9+
//https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit
10+
module ResponseResult: {
11+
[@deriving yojson]
12+
type t;
13+
};
14+
15+
let process:
16+
(
17+
~id: Protocol.message_id,
18+
~compiled_code: Hashtbl.t(Protocol.uri, Lsp_types.code),
19+
~documents: Hashtbl.t(Protocol.uri, string),
20+
RequestParams.t
21+
) =>
22+
unit;

compiler/src/language_server/driver.re

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ let process = msg => {
4949
| Goto(id, goto_request_type, params) when is_initialized^ =>
5050
Goto.process(~id, ~compiled_code, ~documents, goto_request_type, params);
5151
Reading;
52+
| CodeAction(id, params) when is_initialized^ =>
53+
Code_action.process(~id, ~compiled_code, ~documents, params);
54+
Reading;
5255
| SetTrace(trace_value) =>
5356
Trace.set_level(trace_value);
5457
Reading;

compiler/src/language_server/formatting.re

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,9 @@ module RequestParams = {
2525
};
2626
};
2727

28-
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit
2928
module ResponseResult = {
3029
[@deriving yojson]
31-
type text_edit = {
32-
range: Protocol.range,
33-
newText: string,
34-
};
35-
36-
[@deriving yojson]
37-
type t = option(list(text_edit));
30+
type t = option(list(Protocol.text_edit));
3831
};
3932

4033
let process =
@@ -76,7 +69,8 @@ let process =
7669
},
7770
};
7871

79-
let res: ResponseResult.t = Some([{range, newText: formatted_code}]);
72+
let res: ResponseResult.t =
73+
Some([{range, new_text: formatted_code}]);
8074
Protocol.response(~id, ResponseResult.to_yojson(res));
8175
}) {
8276
| exn =>

compiler/src/language_server/initialize.re

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ module ResponseResult = {
7575
type_definition_provider: true,
7676
references_provider: false,
7777
document_symbol_provider: false,
78-
code_action_provider: false,
78+
code_action_provider: true,
7979
code_lens_provider: {
8080
resolve_provider: true,
8181
},

compiler/src/language_server/message.re

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type t =
1111
| TextDocumentInlayHint(Protocol.message_id, Inlayhint.RequestParams.t)
1212
| Formatting(Protocol.message_id, Formatting.RequestParams.t)
1313
| Goto(Protocol.message_id, Goto.goto_request_type, Goto.RequestParams.t)
14+
| CodeAction(Protocol.message_id, Code_action.RequestParams.t)
1415
| SetTrace(Protocol.trace_value)
1516
| Unsupported
1617
| Error(string);
@@ -76,6 +77,11 @@ let of_request = (msg: Protocol.request_message): t => {
7677
| Ok(params) => Goto(id, TypeDefinition, params)
7778
| Error(msg) => Error(msg)
7879
}
80+
| {method: "textDocument/codeAction", id: Some(id), params: Some(params)} =>
81+
switch (Code_action.RequestParams.of_yojson(params)) {
82+
| Ok(params) => CodeAction(id, params)
83+
| Error(msg) => Error(msg)
84+
}
7985
| {method: "$/setTrace", params: Some(params)} =>
8086
switch (Trace.RequestParams.of_yojson(params)) {
8187
| Ok(params) => SetTrace(params.value)

compiler/src/language_server/message.rei

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type t =
99
| TextDocumentInlayHint(Protocol.message_id, Inlayhint.RequestParams.t)
1010
| Formatting(Protocol.message_id, Formatting.RequestParams.t)
1111
| Goto(Protocol.message_id, Goto.goto_request_type, Goto.RequestParams.t)
12+
| CodeAction(Protocol.message_id, Code_action.RequestParams.t)
1213
| SetTrace(Protocol.trace_value)
1314
| Unsupported
1415
| Error(string);

compiler/src/language_server/protocol.re

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ type text_document_position_params = {
116116
position,
117117
};
118118

119+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#optionalVersionedTextDocumentIdentifier
120+
[@deriving yojson]
121+
type optional_versioned_text_document_identifier = {
122+
uri,
123+
version: option(int),
124+
};
125+
119126
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentSyncKind
120127
[@deriving (enum, yojson)]
121128
type text_document_sync_kind =
@@ -216,6 +223,29 @@ type notification_message = {
216223
params: Yojson.Safe.t,
217224
};
218225

226+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit
227+
[@deriving yojson({strict: false})]
228+
type text_edit = {
229+
range,
230+
[@key "newText"]
231+
new_text: string,
232+
};
233+
234+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentEdit
235+
[@deriving yojson({strict: false})]
236+
type text_document_edit = {
237+
[@key "textDocument"]
238+
text_document: optional_versioned_text_document_identifier,
239+
edits: list(text_edit),
240+
};
241+
242+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspaceEdit
243+
[@deriving yojson({strict: false})]
244+
type workspace_edit = {
245+
[@key "documentChanges"]
246+
document_changes: list(text_document_edit),
247+
};
248+
219249
let version: version = "2.0";
220250

221251
let header_prefix = "Content-Length: ";

compiler/src/language_server/protocol.rei

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ type versioned_text_document_identifier = {
122122
version: int,
123123
};
124124

125+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#optionalVersionedTextDocumentIdentifier
126+
[@deriving yojson({strict: false})]
127+
type optional_versioned_text_document_identifier = {
128+
uri,
129+
version: option(int),
130+
};
131+
125132
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#errorCodes
126133
[@deriving (enum, yojson)]
127134
type error_code =
@@ -170,6 +177,29 @@ type notification_message = {
170177
params: Yojson.Safe.t,
171178
};
172179

180+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit
181+
[@deriving yojson({strict: false})]
182+
type text_edit = {
183+
range,
184+
[@key "newText"]
185+
new_text: string,
186+
};
187+
188+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentEdit
189+
[@deriving yojson({strict: false})]
190+
type text_document_edit = {
191+
[@key "textDocument"]
192+
text_document: optional_versioned_text_document_identifier,
193+
edits: list(text_edit),
194+
};
195+
196+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspaceEdit
197+
[@deriving yojson({strict: false})]
198+
type workspace_edit = {
199+
[@key "documentChanges"]
200+
document_changes: list(text_document_edit),
201+
};
202+
173203
//https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#definitionClientCapabilities
174204
[@deriving yojson({strict: false})]
175205
type definition_client_capabilities = {

0 commit comments

Comments
 (0)