Skip to content

Commit 1411a6a

Browse files
committed
basic snippet support in completions
1 parent 793d195 commit 1411a6a

File tree

7 files changed

+116
-28
lines changed

7 files changed

+116
-28
lines changed

analysis/src/Cfg.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let supportsSnippets = ref false

analysis/src/Cli.ml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ let help =
33
**Private CLI For rescript-vscode usage only**
44

55
API examples:
6-
./rescript-editor-analysis.exe completion src/MyFile.res 0 4 currentContent.res
6+
./rescript-editor-analysis.exe completion src/MyFile.res 0 4 currentContent.res true
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
@@ -86,7 +86,11 @@ Options:
8686

8787
let main () =
8888
match Array.to_list Sys.argv with
89-
| [_; "completion"; path; line; col; currentFile] ->
89+
| [_; "completion"; path; line; col; currentFile; supportsSnippets] ->
90+
(Cfg.supportsSnippets :=
91+
match supportsSnippets with
92+
| "true" -> true
93+
| _ -> false);
9094
Commands.completion ~debug:false ~path
9195
~pos:(int_of_string line, int_of_string col)
9296
~currentFile
@@ -143,7 +147,9 @@ let main () =
143147
(Json.escape (CreateInterface.command ~path ~cmiFile))
144148
| [_; "format"; path] ->
145149
Printf.printf "\"%s\"" (Json.escape (Commands.format ~path))
146-
| [_; "test"; path] -> Commands.test ~path
150+
| [_; "test"; path] ->
151+
Cfg.supportsSnippets := true;
152+
Commands.test ~path
147153
| args when List.mem "-h" args || List.mem "--help" args -> prerr_endline help
148154
| _ ->
149155
prerr_endline help;

analysis/src/CompletionBackEnd.ml

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,12 +1144,29 @@ let mkItem ~name ~kind ~detail ~deprecated ~docstring =
11441144
documentation =
11451145
(if docContent = "" then None
11461146
else Some {kind = "markdown"; value = docContent});
1147+
sortText = None;
1148+
insertText = None;
1149+
insertTextFormat = None;
11471150
}
11481151

1149-
let completionToItem {Completion.name; deprecated; docstring; kind} =
1150-
mkItem ~name
1151-
~kind:(Completion.kindToInt kind)
1152-
~deprecated ~detail:(detail name kind) ~docstring
1152+
let completionToItem
1153+
{
1154+
Completion.name;
1155+
deprecated;
1156+
docstring;
1157+
kind;
1158+
sortText;
1159+
insertText;
1160+
insertTextFormat;
1161+
} =
1162+
let item =
1163+
mkItem ~name
1164+
~kind:(Completion.kindToInt kind)
1165+
~deprecated ~detail:(detail name kind) ~docstring
1166+
in
1167+
if !Cfg.supportsSnippets then
1168+
{item with sortText; insertText; insertTextFormat}
1169+
else item
11531170

11541171
let completionsGetTypeEnv = function
11551172
| {Completion.kind = Value typ; env} :: _ -> Some (typ, env)
@@ -1554,9 +1571,9 @@ let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix
15541571
Completion.create ~name:"None"
15551572
~kind:(Label (t |> Shared.typeToString))
15561573
~env;
1557-
Completion.create ~name:"Some(_)"
1574+
Completion.createWithSnippet ~name:"Some(_)"
15581575
~kind:(Label (t |> Shared.typeToString))
1559-
~env;
1576+
~env ~insertText:"Some(${1:_})" ();
15601577
]
15611578
|> filterItems ~prefix
15621579
| _ -> []

analysis/src/Protocol.ml

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,22 @@ type signatureHelp = {
3333
activeParameter: int option;
3434
}
3535

36+
(* https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#insertTextFormat *)
37+
type insertTextFormat = PlainText | Snippet
38+
39+
let insertTextFormatToInt f =
40+
match f with
41+
| PlainText -> 1
42+
| Snippet -> 2
43+
3644
type completionItem = {
3745
label: string;
3846
kind: int;
3947
tags: int list;
4048
detail: string;
49+
sortText: string option;
50+
insertTextFormat: insertTextFormat option;
51+
insertText: string option;
4152
documentation: markupContent option;
4253
}
4354

@@ -86,21 +97,45 @@ let stringifyRange r =
8697
let stringifyMarkupContent (m : markupContent) =
8798
Printf.sprintf {|{"kind": "%s", "value": "%s"}|} m.kind (Json.escape m.value)
8899

100+
(** None values are not emitted in the output. *)
101+
let stringifyObject properties =
102+
{|{
103+
|}
104+
^ (properties
105+
|> List.filter_map (fun (key, value) ->
106+
match value with
107+
| None -> None
108+
| Some v -> Some (Printf.sprintf {| "%s": %s|} key v))
109+
|> String.concat ",\n")
110+
^ "\n }"
111+
112+
let wrapInQuotes s = "\"" ^ s ^ "\""
113+
114+
let optWrapInQuotes s =
115+
match s with
116+
| None -> None
117+
| Some s -> Some (wrapInQuotes s)
118+
89119
let stringifyCompletionItem c =
90-
Printf.sprintf
91-
{|{
92-
"label": "%s",
93-
"kind": %i,
94-
"tags": %s,
95-
"detail": "%s",
96-
"documentation": %s
97-
}|}
98-
(Json.escape c.label) c.kind
99-
(c.tags |> List.map string_of_int |> array)
100-
(Json.escape c.detail)
101-
(match c.documentation with
102-
| None -> null
103-
| Some doc -> stringifyMarkupContent doc)
120+
stringifyObject
121+
[
122+
("label", Some (wrapInQuotes (Json.escape c.label)));
123+
("kind", Some (string_of_int c.kind));
124+
("tags", Some (c.tags |> List.map string_of_int |> array));
125+
("detail", Some (wrapInQuotes (Json.escape c.detail)));
126+
( "documentation",
127+
Some
128+
(match c.documentation with
129+
| None -> null
130+
| Some doc -> stringifyMarkupContent doc) );
131+
("sortText", optWrapInQuotes c.sortText);
132+
("insertText", optWrapInQuotes c.insertText);
133+
( "insertTextFormat",
134+
match c.insertTextFormat with
135+
| None -> None
136+
| Some insertTextFormat ->
137+
Some (Printf.sprintf "%i" (insertTextFormatToInt insertTextFormat)) );
138+
]
104139

105140
let stringifyHover value =
106141
Printf.sprintf {|{"contents": %s}|}

analysis/src/SharedTypes.ml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,14 +292,38 @@ module Completion = struct
292292

293293
type t = {
294294
name: string;
295+
sortText: string option;
296+
insertText: string option;
297+
insertTextFormat: Protocol.insertTextFormat option;
295298
env: QueryEnv.t;
296299
deprecated: string option;
297300
docstring: string list;
298301
kind: kind;
299302
}
300303

301304
let create ~name ~kind ~env =
302-
{name; env; deprecated = None; docstring = []; kind}
305+
{
306+
name;
307+
env;
308+
deprecated = None;
309+
docstring = [];
310+
kind;
311+
sortText = None;
312+
insertText = None;
313+
insertTextFormat = None;
314+
}
315+
316+
let createWithSnippet ~name ?insertText ~kind ~env ?sortText () =
317+
{
318+
name;
319+
env;
320+
deprecated = None;
321+
docstring = [];
322+
kind;
323+
sortText;
324+
insertText;
325+
insertTextFormat = Some Protocol.Snippet;
326+
}
303327

304328
(* https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion *)
305329
(* https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItemKind *)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ Completable: Cargument Value[someFnTakingVariant]($0=S)
137137
"kind": 4,
138138
"tags": [],
139139
"detail": "someVariant",
140-
"documentation": null
140+
"documentation": null,
141+
"insertText": "Some(${1:_})",
142+
"insertTextFormat": 2
141143
}]
142144

143145
Complete src/CompletionFunctionArguments.res 60:44

server/src/server.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,7 @@ function completion(msg: p.RequestMessage) {
680680
params.position.line,
681681
params.position.character,
682682
tmpname,
683+
"true",
683684
],
684685
msg
685686
);
@@ -946,7 +947,7 @@ function createInterface(msg: p.RequestMessage): p.Message {
946947
jsonrpc: c.jsonrpcVersion,
947948
id: msg.id,
948949
result: {
949-
uri: utils.pathToURI(resiPath)
950+
uri: utils.pathToURI(resiPath),
950951
},
951952
};
952953
return response;
@@ -1013,7 +1014,7 @@ function openCompiledFile(msg: p.RequestMessage): p.Message {
10131014
id: msg.id,
10141015
result: {
10151016
uri: utils.pathToURI(compiledFilePath.result),
1016-
}
1017+
},
10171018
};
10181019

10191020
return response;
@@ -1111,7 +1112,9 @@ function onMessage(msg: p.Message) {
11111112
codeActionProvider: true,
11121113
renameProvider: { prepareProvider: true },
11131114
documentSymbolProvider: true,
1114-
completionProvider: { triggerCharacters: [".", ">", "@", "~", '"', "="] },
1115+
completionProvider: {
1116+
triggerCharacters: [".", ">", "@", "~", '"', "="],
1117+
},
11151118
semanticTokensProvider: {
11161119
legend: {
11171120
tokenTypes: [

0 commit comments

Comments
 (0)