Skip to content

Commit c547f93

Browse files
committed
gate link to definition behind flag, to gracefully degrade for clients that cannot handle markdown links
1 parent 018033f commit c547f93

File tree

10 files changed

+61
-27
lines changed

10 files changed

+61
-27
lines changed

analysis/src/Cli.ml

+8-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ API examples:
77
./rescript-editor-analysis.exe definition src/MyFile.res 9 3
88
./rescript-editor-analysis.exe typeDefinition src/MyFile.res 9 3
99
./rescript-editor-analysis.exe documentSymbol src/Foo.res
10-
./rescript-editor-analysis.exe hover src/MyFile.res 10 2
10+
./rescript-editor-analysis.exe hover src/MyFile.res 10 2 true
1111
./rescript-editor-analysis.exe references src/MyFile.res 10 2
1212
./rescript-editor-analysis.exe rename src/MyFile.res 10 2 foo
1313
./rescript-editor-analysis.exe diagnosticSyntax src/MyFile.res
@@ -39,9 +39,9 @@ Options:
3939

4040
./rescript-editor-analysis.exe documentSymbol src/MyFile.res
4141

42-
hover: get inferred type for MyFile.res at line 10 column 2:
42+
hover: get inferred type for MyFile.res at line 10 column 2 (supporting markdown links):
4343

44-
./rescript-editor-analysis.exe hover src/MyFile.res 10 2
44+
./rescript-editor-analysis.exe hover src/MyFile.res 10 2 true
4545

4646
references: get all references to item in MyFile.res at line 10 column 2:
4747

@@ -95,10 +95,14 @@ let main () =
9595
~pos:(int_of_string line, int_of_string col)
9696
~debug:false
9797
| [_; "documentSymbol"; path] -> DocumentSymbol.command ~path
98-
| [_; "hover"; path; line; col; currentFile] ->
98+
| [_; "hover"; path; line; col; currentFile; supportsMarkdownLinks] ->
9999
Commands.hover ~path
100100
~pos:(int_of_string line, int_of_string col)
101101
~currentFile ~debug:false
102+
~supportsMarkdownLinks:
103+
(match supportsMarkdownLinks with
104+
| "true" -> true
105+
| _ -> false)
102106
| [_; "inlayHint"; path; line_start; line_end; maxLength] ->
103107
Commands.inlayhint ~path
104108
~pos:(int_of_string line_start, int_of_string line_end)

analysis/src/Commands.ml

+4-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ let codeLens ~path ~debug =
3636
let result = Hint.codeLens ~path ~debug |> Protocol.array in
3737
print_endline result
3838

39-
let hover ~path ~pos ~currentFile ~debug =
39+
let hover ~path ~pos ~currentFile ~debug ~supportsMarkdownLinks =
4040
let result =
4141
match Cmt.fullFromPath ~path with
4242
| None -> Protocol.null
@@ -81,7 +81,7 @@ let hover ~path ~pos ~currentFile ~debug =
8181
in
8282
if skipZero then Protocol.null
8383
else
84-
let hoverText = Hover.newHover ~full locItem in
84+
let hoverText = Hover.newHover ~supportsMarkdownLinks ~full locItem in
8585
match hoverText with
8686
| None -> Protocol.null
8787
| Some s -> Protocol.stringifyHover s))
@@ -341,7 +341,8 @@ let test ~path =
341341
("Hover " ^ path ^ " " ^ string_of_int line ^ ":"
342342
^ string_of_int col);
343343
let currentFile = createCurrentFile () in
344-
hover ~path ~pos:(line, col) ~currentFile ~debug:true;
344+
hover ~supportsMarkdownLinks:true ~path ~pos:(line, col)
345+
~currentFile ~debug:true;
345346
Sys.remove currentFile
346347
| "int" ->
347348
print_endline ("Create Interface " ^ path);

analysis/src/Hover.ml

+14-10
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ let rec showModule ~docstring ~(file : File.t) ~name
6666
| Some {item = Ident path} ->
6767
Some ("Unable to resolve module reference " ^ Path.name path)
6868

69-
let newHover ~full:{file; package} locItem =
69+
let newHover ~full:{file; package} ~supportsMarkdownLinks locItem =
7070
match locItem.locType with
7171
| TypeDefinition (name, decl, _stamp) ->
7272
let typeDef = Shared.declToString name decl in
@@ -186,16 +186,20 @@ let newHover ~full:{file; package} locItem =
186186
| None -> None
187187
| Some (typString, extentLoc, env) ->
188188
let startLine, startCol = Pos.ofLexing extentLoc.loc_start in
189+
let linkToTypeDefinitionStr =
190+
if supportsMarkdownLinks then
191+
"\nGo to: "
192+
^ makeGotoCommand
193+
{
194+
label = "Type definition";
195+
file = Uri.toString env.file.uri;
196+
startPos = {line = startLine; character = startCol};
197+
}
198+
else ""
199+
in
189200
Some
190-
(Shared.markdownSpacing ^ codeBlock typString ^ "\n"
191-
^ "Go to: "
192-
^ makeGotoCommand
193-
{
194-
label = "Type definition";
195-
file = Uri.toString env.file.uri;
196-
startPos = {line = startLine; character = startCol};
197-
}
198-
^ "\n\n---\n"))
201+
(Shared.markdownSpacing ^ codeBlock typString
202+
^ linkToTypeDefinitionStr ^ "\n\n---\n"))
199203
in
200204
let typeString = typeString :: typeDefinitions |> String.concat "\n\n" in
201205
(typeString, docstring)
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
Hover src/Auto.res 2:13
2-
{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n---\n\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"}
2+
{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"}
33

analysis/tests/src/expected/Definition.res.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Hover src/Definition.res 14:14
88
{"contents": "```rescript\n('a => 'b, list<'a>) => list<'b>\n```\n\n [List.map f [a1; ...; an]] applies function [f] to [a1, ..., an],\n and builds the list [[f a1; ...; f an]]\n with the results returned by [f]. Not tail-recursive. "}
99

1010
Hover src/Definition.res 18:14
11-
{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n---\n\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"}
11+
{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n\n```\n \n```\n```rescript\ntype Belt.List.t<'a> = list<'a>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22belt_List.mli%22%2C34%2C0%5D)\n\n\n Returns a new list with `f` applied to each element of `someList`.\n\n ```res example\n list{1, 2}->Belt.List.map(x => x + 1) // list{3, 4}\n ```\n"}
1212

1313
Hover src/Definition.res 23:3
1414
{"contents": "```rescript\n(. int, int) => int\n```"}

analysis/tests/src/expected/Div.res.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Hover src/Div.res 0:10
22
getLocItem #3: heuristic for <div>
3-
{"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array<React.element>,\n) => React.element\n```\n\n\n```\n \n```\n```rescript\ntype ReactDOMRe.domProps = ReactDOM.Props.domProps\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22ReactDOMRe.res%22%2C57%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n\n---\n"}
3+
{"contents": "```rescript\n(\n string,\n ~props: ReactDOMRe.domProps=?,\n array<React.element>,\n) => React.element\n```\n\n\n```\n \n```\n```rescript\ntype ReactDOMRe.domProps = ReactDOM.Props.domProps\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22ReactDOMRe.res%22%2C57%2C2%5D)\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)"}
44

55
Complete src/Div.res 3:17
66
posCursor:[3:17] posNoWhite:[3:16] Found expr:[3:4->3:17]

analysis/tests/src/expected/Fragment.res.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Hover src/Fragment.res 6:19
22
getLocItem #4: heuristic for </Comp> within fragments: take make as makeProps does not work
33
the type is not great but jump to definition works
4-
{"contents": "```rescript\nReact.component<{\"children\": React.element}>\n```\n\n\n```\n \n```\n```rescript\ntype React.component<'props> = componentLike<\n 'props,\n element,\n>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)\n\n---\n"}
4+
{"contents": "```rescript\nReact.component<{\"children\": React.element}>\n```\n\n\n```\n \n```\n```rescript\ntype React.component<'props> = componentLike<\n 'props,\n element,\n>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C12%2C0%5D)"}
55

66
Hover src/Fragment.res 9:56
77
Nothing at that position. Now trying to use completion.

analysis/tests/src/expected/Hover.res.txt

+6-6
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ Hover src/Hover.res 106:21
7575
{"contents": "```rescript\nint\n```"}
7676

7777
Hover src/Hover.res 116:16
78-
{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)\n\n---\n"}
78+
{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)"}
7979

8080
Hover src/Hover.res 119:25
81-
{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)\n\n---\n"}
81+
{"contents": "```rescript\nAA.cond<[< #str(string)]> => AA.cond<[< #str(string)]>\n```\n\n\n```\n \n```\n```rescript\ntype AA.cond<'a> = 'a\n constraint 'a = [< #str(string)]\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C110%2C2%5D)"}
8282

8383
Hover src/Hover.res 122:3
8484
Nothing at that position. Now trying to use completion.
@@ -105,10 +105,10 @@ Hover src/Hover.res 148:6
105105
{"contents": "```rescript\nint\n```\n\n doc comment 2 "}
106106

107107
Hover src/Hover.res 165:23
108-
{"contents": "```rescript\nfoo<bar>\n```\n\n\n```\n \n```\n```rescript\ntype foo<'a> = {content: 'a, zzz: string}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C161%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype bar = {age: int}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C162%2C2%5D)\n\n---\n"}
108+
{"contents": "```rescript\nfoo<bar>\n```\n\n\n```\n \n```\n```rescript\ntype foo<'a> = {content: 'a, zzz: string}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C161%2C2%5D)\n\n\n```\n \n```\n```rescript\ntype bar = {age: int}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C162%2C2%5D)"}
109109

110110
Hover src/Hover.res 167:22
111-
{"contents": "```rescript\nfoobar\n```\n\n\n```\n \n```\n```rescript\ntype foobar = foo<bar>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C163%2C2%5D)\n\n---\n"}
111+
{"contents": "```rescript\nfoobar\n```\n\n\n```\n \n```\n```rescript\ntype foobar = foo<bar>\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C163%2C2%5D)"}
112112

113113
Complete src/Hover.res 170:16
114114
posCursor:[170:16] posNoWhite:[170:15] Found expr:[170:5->170:16]
@@ -159,8 +159,8 @@ Completable: Cpath Value[y2].content.""
159159
}]
160160

161161
Hover src/Hover.res 197:4
162-
{"contents": "```rescript\nCompV4.props<int, string> => React.element\n```\n\n\n```\n \n```\n```rescript\ntype CompV4.props<'n, 's> = {?n: 'n, s: 's}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C190%2C2%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)\n\n---\n"}
162+
{"contents": "```rescript\nCompV4.props<int, string> => React.element\n```\n\n\n```\n \n```\n```rescript\ntype CompV4.props<'n, 's> = {?n: 'n, s: 's}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C190%2C2%5D)\n\n\n```\n \n```\n```rescript\ntype React.element\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22React.res%22%2C0%2C0%5D)"}
163163

164164
Hover src/Hover.res 202:16
165-
{"contents": "```rescript\nuseR\n```\n\n\n```\n \n```\n```rescript\ntype useR = {x: int, y: list<option<r<float>>>}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C200%2C0%5D)\n\n---\n\n\n\n```\n \n```\n```rescript\ntype r<'a> = {i: 'a, f: float}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C101%2C0%5D)\n\n---\n"}
165+
{"contents": "```rescript\nuseR\n```\n\n\n```\n \n```\n```rescript\ntype useR = {x: int, y: list<option<r<float>>>}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C200%2C0%5D)\n\n\n```\n \n```\n```rescript\ntype r<'a> = {i: 'a, f: float}\n```\nGo to: [Type definition](command:rescript-vscode.go_to_location?%5B%22Hover.res%22%2C101%2C0%5D)"}
166166

client/src/extension.ts

+6
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ export function activate(context: ExtensionContext) {
106106
// We'll leave it like this for now, but might be worth revisiting later on.
107107
initializationOptions: {
108108
extensionConfiguration: workspace.getConfiguration("rescript.settings"),
109+
110+
// Keep this in sync with the `extensionClientCapabilities` type in the
111+
// server.
112+
extensionClientCapabilities: {
113+
supportsMarkdownLinks: true,
114+
},
109115
},
110116
outputChannel,
111117
markdown: {

server/src/server.ts

+19
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ interface extensionConfiguration {
3535
binaryPath: string | null;
3636
}
3737

38+
// This holds client capabilities specific to our extension, and not necessarily
39+
// related to the LS protocol. It's for enabling/disabling features that might
40+
// work in one client, like VSCode, but perhaps not in others, like vim.
41+
export interface extensionClientCapabilities {
42+
supportsMarkdownLinks?: boolean | null;
43+
}
44+
let extensionClientCapabilities: extensionClientCapabilities = {};
45+
3846
// All values here are temporary, and will be overridden as the server is
3947
// initialized, and the current config is received from the client.
4048
let extensionConfiguration: extensionConfiguration = {
@@ -436,6 +444,7 @@ function hover(msg: p.RequestMessage) {
436444
params.position.line,
437445
params.position.character,
438446
tmpname,
447+
Boolean(extensionClientCapabilities.supportsMarkdownLinks),
439448
],
440449
msg
441450
);
@@ -1070,6 +1079,16 @@ function onMessage(msg: p.Message) {
10701079
extensionConfiguration = initialConfiguration;
10711080
}
10721081

1082+
// These are static configuration options the client can set to enable certain
1083+
let extensionClientCapabilitiesFromClient = initParams
1084+
.initializationOptions?.extensionClientCapabilities as
1085+
| extensionClientCapabilities
1086+
| undefined;
1087+
1088+
if (extensionClientCapabilitiesFromClient != null) {
1089+
extensionClientCapabilities = extensionClientCapabilitiesFromClient;
1090+
}
1091+
10731092
// send the list of features we support
10741093
let result: p.InitializeResult = {
10751094
// This tells the client: "hey, we support the following operations".

0 commit comments

Comments
 (0)