Skip to content

Commit 3cb862a

Browse files
marcusrobertsMarcus Robertsospencerphated
authored
feat(grainlsp): Implement goto definition (#1787)
* Initial goto definition support * go to all the definitions * Cleaned up a utility function * Clean up a debug function * Format code * Update compiler/src/language_server/definition.re Co-authored-by: Blaine Bublitz <blaine.bublitz@gmail.com> * Update compiler/src/language_server/definition.re Co-authored-by: Blaine Bublitz <blaine.bublitz@gmail.com> * Fix formatting * Refactored utility functions out of Protocol * Remove unused opens --------- Co-authored-by: Marcus Roberts <marcus@marcusr.com> Co-authored-by: Oscar Spencer <oscar@grain-lang.org> Co-authored-by: Blaine Bublitz <blaine.bublitz@gmail.com>
1 parent e52c807 commit 3cb862a

File tree

15 files changed

+394
-92
lines changed

15 files changed

+394
-92
lines changed

compiler/src/language_server/code_file.re

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ let warning_to_diagnostic =
4848
};
4949

5050
let compile_source = (uri, source) => {
51-
let filename = Protocol.uri_to_filename(uri);
51+
let filename = Utils.uri_to_filename(uri);
5252

5353
Trace.log("Compiling " ++ filename);
5454

@@ -120,7 +120,7 @@ let compile_source = (uri, source) => {
120120
related_information: [
121121
{
122122
location: {
123-
uri: Protocol.filename_to_uri(file),
123+
uri: Utils.filename_to_uri(file),
124124
range: file_range,
125125
},
126126
message: e.msg,
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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 t = {
14+
[@key "textDocument"]
15+
text_document: Protocol.text_document_identifier,
16+
position: Protocol.position,
17+
};
18+
};
19+
20+
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#locationLink
21+
module ResponseResult = {
22+
[@deriving yojson]
23+
type t = {
24+
[@key "originSelectionRange"]
25+
origin_selection_range: Protocol.range,
26+
[@key "targetUri"]
27+
target_uri: Protocol.uri,
28+
[@key "targetRange"]
29+
target_range: Protocol.range,
30+
[@key "targetSelectionRange"]
31+
target_selection_range: Protocol.range,
32+
};
33+
};
34+
35+
let send_no_result = (~id: Protocol.message_id) => {
36+
Protocol.response(~id, `Null);
37+
};
38+
39+
let send_definition =
40+
(
41+
~id: Protocol.message_id,
42+
~range: Protocol.range,
43+
~target_uri: Protocol.uri,
44+
target_range: Protocol.range,
45+
) => {
46+
Protocol.response(
47+
~id,
48+
ResponseResult.to_yojson({
49+
origin_selection_range: range,
50+
target_uri,
51+
target_range,
52+
target_selection_range: target_range,
53+
}),
54+
);
55+
};
56+
57+
let process =
58+
(
59+
~id: Protocol.message_id,
60+
~compiled_code: Hashtbl.t(Protocol.uri, Lsp_types.code),
61+
~documents: Hashtbl.t(Protocol.uri, string),
62+
params: RequestParams.t,
63+
) => {
64+
switch (Hashtbl.find_opt(compiled_code, params.text_document.uri)) {
65+
| None => send_no_result(~id)
66+
| Some({program, sourcetree}) =>
67+
let results = Sourcetree.query(params.position, sourcetree);
68+
69+
switch (results) {
70+
| [Value({definition}), ..._]
71+
| [Pattern({definition}), ..._]
72+
| [Type({definition}), ..._]
73+
| [Declaration({definition}), ..._]
74+
| [Module({definition}), ..._] =>
75+
switch (definition) {
76+
| None => send_no_result(~id)
77+
| Some(loc) =>
78+
let uri = Utils.filename_to_uri(loc.loc_start.pos_fname);
79+
80+
send_definition(
81+
~id,
82+
~range=Utils.loc_to_range(loc),
83+
~target_uri=uri,
84+
Utils.loc_to_range(loc),
85+
);
86+
}
87+
| _ => send_no_result(~id)
88+
};
89+
};
90+
};
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/#definitionParams
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/#location
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
@@ -46,6 +46,9 @@ let process = msg => {
4646
| Formatting(id, params) when is_initialized^ =>
4747
Formatting.process(~id, ~compiled_code, ~documents, params);
4848
Reading;
49+
| Definition(id, params) when is_initialized^ =>
50+
Definition.process(~id, ~compiled_code, ~documents, params);
51+
Reading;
4952
| SetTrace(trace_value) =>
5053
Trace.set_level(trace_value);
5154
Reading;

compiler/src/language_server/formatting.re

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ let process =
5252
code: InvalidParams,
5353
message:
5454
"The document is not available on the server: "
55-
++ Protocol.uri_to_filename(params.text_document.uri),
55+
++ Utils.uri_to_filename(params.text_document.uri),
5656
},
5757
)
5858
| Some(compiled_code) =>
@@ -86,7 +86,7 @@ let process =
8686
code: RequestFailed,
8787
message:
8888
"Failed to format the document: "
89-
++ Protocol.uri_to_filename(params.text_document.uri),
89+
++ Utils.uri_to_filename(params.text_document.uri),
9090
},
9191
)
9292
}
@@ -101,7 +101,7 @@ let process =
101101
code: RequestFailed,
102102
message:
103103
"Reached an invalid compilation state when compiling: "
104-
++ Protocol.uri_to_filename(params.text_document.uri),
104+
++ Utils.uri_to_filename(params.text_document.uri),
105105
},
106106
)
107107
}

compiler/src/language_server/hover.re

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,6 @@ module ResponseResult = {
3232
};
3333
};
3434

35-
let loc_to_range = (pos: Location.t): Protocol.range => {
36-
let (_, startline, startchar, _) =
37-
Locations.get_raw_pos_info(pos.loc_start);
38-
let (_, endline, endchar) =
39-
Grain_parsing.Location.get_pos_info(pos.loc_end);
40-
41-
{
42-
range_start: {
43-
line: startline - 1,
44-
character: startchar,
45-
},
46-
range_end: {
47-
line: endline - 1,
48-
character: endchar,
49-
},
50-
};
51-
};
52-
5335
// We need to use the "grain-type" markdown syntax to have correct coloring on hover items
5436
let grain_type_code_block = Markdown.code_block(~syntax="grain-type");
5537
// Used for module hovers
@@ -155,20 +137,32 @@ let process =
155137
| Some({program, sourcetree}) =>
156138
let results = Sourcetree.query(params.position, sourcetree);
157139
switch (results) {
158-
| [Value(env, ty, loc), ..._] =>
159-
send_hover(~id, ~range=loc_to_range(loc), value_lens(env, ty))
160-
| [Pattern(pat), ..._] =>
161-
send_hover(~id, ~range=loc_to_range(pat.pat_loc), pattern_lens(pat))
162-
| [Type(ty), ..._] =>
163-
send_hover(~id, ~range=loc_to_range(ty.ctyp_loc), type_lens(ty))
164-
| [Declaration(ident, decl, loc), ..._] =>
140+
| [Value({env, value_type, loc}), ..._] =>
141+
send_hover(
142+
~id,
143+
~range=Utils.loc_to_range(loc),
144+
value_lens(env, value_type),
145+
)
146+
| [Pattern({pattern}), ..._] =>
147+
send_hover(
148+
~id,
149+
~range=Utils.loc_to_range(pattern.pat_loc),
150+
pattern_lens(pattern),
151+
)
152+
| [Type({core_type}), ..._] =>
153+
send_hover(
154+
~id,
155+
~range=Utils.loc_to_range(core_type.ctyp_loc),
156+
type_lens(core_type),
157+
)
158+
| [Declaration({ident, decl, loc}), ..._] =>
165159
send_hover(
166160
~id,
167-
~range=loc_to_range(loc),
161+
~range=Utils.loc_to_range(loc),
168162
declaration_lens(ident, decl),
169163
)
170-
| [Module(path, decl, loc), ..._] =>
171-
send_hover(~id, ~range=loc_to_range(loc), module_lens(decl))
164+
| [Module({path, decl, loc}), ..._] =>
165+
send_hover(~id, ~range=Utils.loc_to_range(loc), module_lens(decl))
172166
| _ => send_no_result(~id)
173167
};
174168
};

compiler/src/language_server/initialize.re

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ module ResponseResult = {
4242
[@key "hoverProvider"]
4343
hover_provider: bool,
4444
[@key "definitionProvider"]
45-
definition_provider: bool,
45+
definition_provider: Protocol.definition_client_capabilities,
4646
[@key "typeDefinitionProvider"]
4747
type_definition_provider: bool,
4848
[@key "referencesProvider"]
@@ -69,7 +69,9 @@ module ResponseResult = {
6969
document_formatting_provider: true,
7070
text_document_sync: Full,
7171
hover_provider: true,
72-
definition_provider: false, // disabled until we can resolve the external module location
72+
definition_provider: {
73+
link_support: true,
74+
},
7375
type_definition_provider: false,
7476
references_provider: false,
7577
document_symbol_provider: false,

compiler/src/language_server/inlayhint.re

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ let find_hints = program => {
4343
let enter_toplevel_stmt = (stmt: toplevel_stmt) => {
4444
switch (stmt.ttop_desc) {
4545
| TTopInclude(inc) =>
46-
let path = inc.tinc_path;
4746
let name = Path.name(inc.tinc_path);
4847

4948
let stmt_loc = stmt.ttop_loc;

compiler/src/language_server/message.re

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ type t =
1010
| TextDocumentDidChange(Protocol.uri, Code_file.DidChange.RequestParams.t)
1111
| TextDocumentInlayHint(Protocol.message_id, Inlayhint.RequestParams.t)
1212
| Formatting(Protocol.message_id, Formatting.RequestParams.t)
13+
| Definition(Protocol.message_id, Definition.RequestParams.t)
1314
| SetTrace(Protocol.trace_value)
1415
| Unsupported
1516
| Error(string);
@@ -61,6 +62,11 @@ let of_request = (msg: Protocol.request_message): t => {
6162
| Ok(params) => Formatting(id, params)
6263
| Error(msg) => Error(msg)
6364
}
65+
| {method: "textDocument/definition", id: Some(id), params: Some(params)} =>
66+
switch (Definition.RequestParams.of_yojson(params)) {
67+
| Ok(params) => Definition(id, params)
68+
| Error(msg) => Error(msg)
69+
}
6470
| {method: "$/setTrace", params: Some(params)} =>
6571
switch (Trace.RequestParams.of_yojson(params)) {
6672
| 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
@@ -8,6 +8,7 @@ type t =
88
| TextDocumentDidChange(Protocol.uri, Code_file.DidChange.RequestParams.t)
99
| TextDocumentInlayHint(Protocol.message_id, Inlayhint.RequestParams.t)
1010
| Formatting(Protocol.message_id, Formatting.RequestParams.t)
11+
| Definition(Protocol.message_id, Definition.RequestParams.t)
1112
| SetTrace(Protocol.trace_value)
1213
| Unsupported
1314
| Error(string);

0 commit comments

Comments
 (0)