Skip to content

Commit 6162f86

Browse files
committed
complete function snippets
1 parent e7bd5ed commit 6162f86

File tree

6 files changed

+193
-1
lines changed

6 files changed

+193
-1
lines changed

analysis/src/CompletionBackEnd.ml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,42 @@ let rec completeTypedValue (t : SharedTypes.completionType) ~env ~full ~prefix
11171117
~env ();
11181118
]
11191119
else []
1120+
| Some (Tfunction {env; typ; args}) ->
1121+
let mkFnBody ~asSnippet =
1122+
match args with
1123+
| [(Nolabel, argTyp)] when TypeUtils.typeIsUnit argTyp -> "()"
1124+
| [(Nolabel, _)] -> if asSnippet then "${1:v1}" else "v1"
1125+
| _ ->
1126+
let currentUnlabelledIndex = ref 0 in
1127+
let argsText =
1128+
args
1129+
|> List.map (fun ((label, typ) : typedFnArg) ->
1130+
match label with
1131+
| Optional name -> "~" ^ name ^ "=?"
1132+
| Labelled name -> "~" ^ name
1133+
| Nolabel ->
1134+
if TypeUtils.typeIsUnit typ then "()"
1135+
else (
1136+
currentUnlabelledIndex := !currentUnlabelledIndex + 1;
1137+
let num = !currentUnlabelledIndex in
1138+
if asSnippet then
1139+
"${" ^ string_of_int num ^ ":v" ^ string_of_int num ^ "}"
1140+
else "v" ^ string_of_int num))
1141+
|> String.concat ", "
1142+
in
1143+
"(" ^ argsText ^ ")"
1144+
in
1145+
if prefix = "" then
1146+
[
1147+
Completion.createWithSnippet
1148+
~name:(mkFnBody ~asSnippet:false ^ " => {}")
1149+
~insertText:
1150+
(mkFnBody ~asSnippet:!Cfg.supportsSnippets
1151+
^ " => "
1152+
^ if !Cfg.supportsSnippets then "${0:{\\}}" else "{}")
1153+
~sortText:"A" ~kind:(Value typ) ~env ();
1154+
]
1155+
else []
11201156
| _ -> []
11211157

11221158
let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover

analysis/src/SharedTypes.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ let ident l = l |> List.map str |> String.concat "."
44

55
type path = string list
66

7+
type typedFnArg = Asttypes.arg_label * Types.type_expr
8+
79
let pathToString (path : path) = path |> String.concat "."
810

911
module ModulePath = struct
@@ -644,6 +646,11 @@ module Completable = struct
644646
typeExpr: Types.type_expr;
645647
}
646648
| TinlineRecord of {env: QueryEnv.t; fields: field list}
649+
| Tfunction of {
650+
env: QueryEnv.t;
651+
args: typedFnArg list;
652+
typ: Types.type_expr;
653+
}
647654

648655
let toString =
649656
let completionContextToString = function

analysis/src/TypeUtils.ml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ let rec extractType ~env ~package (t : Types.type_expr) =
146146
})
147147
in
148148
Some (Tpolyvariant {env; constructors; typeExpr = t})
149+
| Tarrow _ -> (
150+
match extractFunctionType t ~env ~package with
151+
| args, _tRet when args <> [] -> Some (Tfunction {env; args; typ = t})
152+
| _args, _tRet -> None)
149153
| _ -> None
150154

151155
let findReturnTypeOfFunctionAtLoc loc ~(env : QueryEnv.t) ~full ~debug =
@@ -325,3 +329,13 @@ let getArgs ~env (t : Types.type_expr) ~full =
325329
| _ -> []
326330
in
327331
t |> getArgsLoop ~env ~full ~currentArgumentPosition:0
332+
333+
let typeIsUnit (typ : Types.type_expr) =
334+
match typ.desc with
335+
| Tconstr (Pident id, _typeArgs, _)
336+
| Tlink {desc = Tconstr (Pident id, _typeArgs, _)}
337+
| Tsubst {desc = Tconstr (Pident id, _typeArgs, _)}
338+
| Tpoly ({desc = Tconstr (Pident id, _typeArgs, _)}, [])
339+
when Ident.name id = "unit" ->
340+
true
341+
| _ -> false

analysis/tests/src/CompletionExpressions.res

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,42 @@ let fnTakingInlineRecord = (r: variantWithInlineRecord) => {
138138

139139
// let _ = fnTakingInlineRecord(WithInlineRecord({nestedRecord: {} }))
140140
// ^com
141+
142+
type variant = First | Second(bool)
143+
144+
let fnTakingCallback = (
145+
cb: unit => unit,
146+
cb2: bool => unit,
147+
cb3: ReactEvent.Mouse.t => unit,
148+
cb4: (~on: bool, ~off: bool=?, variant) => int,
149+
cb5: (bool, option<bool>, bool) => unit,
150+
cb6: (~on: bool=?, ~off: bool=?, unit) => int,
151+
) => {
152+
let _ = cb
153+
let _ = cb2
154+
let _ = cb3
155+
let _ = cb4
156+
let _ = cb5
157+
let _ = cb6
158+
}
159+
160+
// fnTakingCallback()
161+
// ^com
162+
163+
// fnTakingCallback(a)
164+
// ^com
165+
166+
// fnTakingCallback(a, )
167+
// ^com
168+
169+
// fnTakingCallback(a, b, )
170+
// ^com
171+
172+
// fnTakingCallback(a, b, c, )
173+
// ^com
174+
175+
// fnTakingCallback(a, b, c, d, )
176+
// ^com
177+
178+
// fnTakingCallback(a, b, c, d, e, )
179+
// ^com

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

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,3 +617,99 @@ Completable: Cexpression CArgument Value[fnTakingInlineRecord]($0)->variantPaylo
617617
"documentation": null
618618
}]
619619

620+
Complete src/CompletionExpressions.res 159:20
621+
posCursor:[159:20] posNoWhite:[159:19] Found expr:[159:3->159:21]
622+
Pexp_apply ...[159:3->159:19] (...[159:20->159:21])
623+
Completable: Cexpression CArgument Value[fnTakingCallback]($0)
624+
[{
625+
"label": "() => {}",
626+
"kind": 12,
627+
"tags": [],
628+
"detail": "unit => unit",
629+
"documentation": null,
630+
"sortText": "A",
631+
"insertText": "() => ${0:{\\}}",
632+
"insertTextFormat": 2
633+
}]
634+
635+
Complete src/CompletionExpressions.res 162:21
636+
posCursor:[162:21] posNoWhite:[162:20] Found expr:[162:3->162:22]
637+
Pexp_apply ...[162:3->162:19] (...[162:20->162:21])
638+
Completable: Cexpression CArgument Value[fnTakingCallback]($0)=a
639+
[]
640+
641+
Complete src/CompletionExpressions.res 165:22
642+
posCursor:[165:22] posNoWhite:[165:21] Found expr:[165:3->165:24]
643+
Pexp_apply ...[165:3->165:19] (...[165:20->165:21])
644+
Completable: Cexpression CArgument Value[fnTakingCallback]($1)
645+
[{
646+
"label": "v1 => {}",
647+
"kind": 12,
648+
"tags": [],
649+
"detail": "bool => unit",
650+
"documentation": null,
651+
"sortText": "A",
652+
"insertText": "${1:v1} => ${0:{\\}}",
653+
"insertTextFormat": 2
654+
}]
655+
656+
Complete src/CompletionExpressions.res 168:25
657+
posCursor:[168:25] posNoWhite:[168:24] Found expr:[168:3->168:27]
658+
Pexp_apply ...[168:3->168:19] (...[168:20->168:21], ...[168:23->168:24])
659+
Completable: Cexpression CArgument Value[fnTakingCallback]($2)
660+
[{
661+
"label": "v1 => {}",
662+
"kind": 12,
663+
"tags": [],
664+
"detail": "ReactEvent.Mouse.t => unit",
665+
"documentation": null,
666+
"sortText": "A",
667+
"insertText": "${1:v1} => ${0:{\\}}",
668+
"insertTextFormat": 2
669+
}]
670+
671+
Complete src/CompletionExpressions.res 171:29
672+
posCursor:[171:29] posNoWhite:[171:27] Found expr:[171:3->171:30]
673+
Pexp_apply ...[171:3->171:19] (...[171:20->171:21], ...[171:23->171:24], ...[171:26->171:27])
674+
Completable: Cexpression CArgument Value[fnTakingCallback]($3)
675+
[{
676+
"label": "(~on, ~off=?, v1) => {}",
677+
"kind": 12,
678+
"tags": [],
679+
"detail": "(~on: bool, ~off: bool=?, variant) => int",
680+
"documentation": null,
681+
"sortText": "A",
682+
"insertText": "(~on, ~off=?, ${1:v1}) => ${0:{\\}}",
683+
"insertTextFormat": 2
684+
}]
685+
686+
Complete src/CompletionExpressions.res 174:32
687+
posCursor:[174:32] posNoWhite:[174:30] Found expr:[174:3->174:33]
688+
Pexp_apply ...[174:3->174:19] (...[174:20->174:21], ...[174:23->174:24], ...[174:26->174:27], ...[174:29->174:30])
689+
Completable: Cexpression CArgument Value[fnTakingCallback]($4)
690+
[{
691+
"label": "(v1, v2, v3) => {}",
692+
"kind": 12,
693+
"tags": [],
694+
"detail": "(bool, option<bool>, bool) => unit",
695+
"documentation": null,
696+
"sortText": "A",
697+
"insertText": "(${1:v1}, ${2:v2}, ${3:v3}) => ${0:{\\}}",
698+
"insertTextFormat": 2
699+
}]
700+
701+
Complete src/CompletionExpressions.res 177:34
702+
posCursor:[177:34] posNoWhite:[177:33] Found expr:[177:3->177:36]
703+
Pexp_apply ...[177:3->177:19] (...[177:20->177:21], ...[177:23->177:24], ...[177:26->177:27], ...[177:29->177:30], ...[177:32->177:33])
704+
Completable: Cexpression CArgument Value[fnTakingCallback]($5)
705+
[{
706+
"label": "(~on=?, ~off=?, ()) => {}",
707+
"kind": 12,
708+
"tags": [],
709+
"detail": "(~on: bool=?, ~off: bool=?, unit) => int",
710+
"documentation": null,
711+
"sortText": "A",
712+
"insertText": "(~on=?, ~off=?, ()) => ${0:{\\}}",
713+
"insertTextFormat": 2
714+
}]
715+

server/src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ function onMessage(msg: p.Message) {
11191119
renameProvider: { prepareProvider: true },
11201120
documentSymbolProvider: true,
11211121
completionProvider: {
1122-
triggerCharacters: [".", ">", "@", "~", '"', "="],
1122+
triggerCharacters: [".", ">", "@", "~", '"', "=", "("],
11231123
},
11241124
semanticTokensProvider: {
11251125
legend: {

0 commit comments

Comments
 (0)