Skip to content

Commit cb88ad9

Browse files
committed
Add completion for throw keyword
1 parent a631b38 commit cb88ad9

File tree

5 files changed

+192
-0
lines changed

5 files changed

+192
-0
lines changed

analysis/src/CompletionBackEnd.ml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,72 @@ let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope
756756
findAllCompletions ~env ~prefix ~exact ~namesUsed ~completionContext
757757
| None -> []))
758758

759+
(* Collect exception constructor names from cmt infos. *)
760+
let exceptions_from_cmt_infos (infos : Cmt_format.cmt_infos) :
761+
(string * bool) list =
762+
let by_name : (string, bool) Hashtbl.t = Hashtbl.create 16 in
763+
let add_ext (ext : Typedtree.extension_constructor) : unit =
764+
let name = ext.ext_name.txt in
765+
let hasArgs =
766+
match ext.ext_kind with
767+
| Text_decl (Cstr_tuple args, _ret) -> args <> []
768+
| Text_decl (Cstr_record fields, _ret) -> fields <> []
769+
| Text_rebind _ -> true
770+
in
771+
let prev =
772+
match Hashtbl.find_opt by_name name with
773+
| Some b -> b
774+
| None -> false
775+
in
776+
Hashtbl.replace by_name name (prev || hasArgs)
777+
in
778+
let rec of_structure_items (items : Typedtree.structure_item list) : unit =
779+
match items with
780+
| [] -> ()
781+
| item :: rest ->
782+
(match item.str_desc with
783+
| Tstr_exception ext -> add_ext ext
784+
| _ -> ());
785+
of_structure_items rest
786+
in
787+
let rec of_signature_items (items : Typedtree.signature_item list) : unit =
788+
match items with
789+
| [] -> ()
790+
| item :: rest ->
791+
(match item.sig_desc with
792+
| Tsig_exception ext -> add_ext ext
793+
| _ -> ());
794+
of_signature_items rest
795+
in
796+
let of_parts (parts : Cmt_format.binary_part array) : unit =
797+
Array.iter
798+
(function
799+
| Cmt_format.Partial_structure s -> of_structure_items s.str_items
800+
| Partial_structure_item si -> of_structure_items [si]
801+
| Partial_signature s -> of_signature_items s.sig_items
802+
| Partial_signature_item si -> of_signature_items [si]
803+
| _ -> ())
804+
parts
805+
in
806+
(match infos.cmt_annots with
807+
| Cmt_format.Implementation s -> of_structure_items s.str_items
808+
| Interface s -> of_signature_items s.sig_items
809+
| Partial_implementation parts -> of_parts parts
810+
| Partial_interface parts -> of_parts parts
811+
| _ -> ());
812+
Hashtbl.fold (fun name hasArgs acc -> (name, hasArgs) :: acc) by_name []
813+
814+
(* Predefined Stdlib/Pervasives exceptions. *)
815+
let predefined_exceptions : (string * bool) list =
816+
[
817+
("Not_found", true);
818+
("Invalid_argument", true);
819+
("Assert_failure", true);
820+
("Failure", true);
821+
("Match_failure", true);
822+
("Division_by_zero", false);
823+
]
824+
759825
(** Completions intended for piping, from a completion path. *)
760826
let completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom ~opens ~pos
761827
~scope ~debug ~prefix ~env ~rawOpens ~full completionPath =
@@ -1010,6 +1076,33 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10101076
| Some (Tpromise (env, typ), _env) ->
10111077
[Completion.create "dummy" ~env ~kind:(Completion.Value typ)]
10121078
| _ -> [])
1079+
| CPId {path = ["throw"]; completionContext = Value; loc = _} ->
1080+
let exn_typ = Ctype.newconstr Predef.path_exn [] in
1081+
let names_from_cmt =
1082+
let moduleName = env.file.moduleName in
1083+
match Hashtbl.find_opt full.package.pathsForModule moduleName with
1084+
| None -> []
1085+
| Some paths -> (
1086+
let uri = getUri paths in
1087+
let cmt_path = getCmtPath ~uri paths in
1088+
match Shared.tryReadCmt cmt_path with
1089+
| None -> []
1090+
| Some infos -> exceptions_from_cmt_infos infos)
1091+
in
1092+
let all = names_from_cmt @ predefined_exceptions in
1093+
all
1094+
|> List.map (fun (name, hasArgs) ->
1095+
let insertText =
1096+
if hasArgs then Printf.sprintf "(%s($0))" name
1097+
else Printf.sprintf "(%s)" name
1098+
in
1099+
let isBuiltin = List.mem (name, hasArgs) predefined_exceptions in
1100+
let detail =
1101+
if isBuiltin then "Built-in Exception"
1102+
else "User-defined Exception"
1103+
in
1104+
Completion.create name ~env ~kind:(Completion.Value exn_typ)
1105+
~includesSnippets:true ~insertText ~detail)
10131106
| CPId {path; completionContext; loc} ->
10141107
if Debug.verbose () then print_endline "[ctx_path]--> CPId";
10151108
(* Looks up the type of an identifier.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
exception MyCustomThingToThrow(string)
2+
exception NoArgsToThrow
3+
4+
// let x = () => throw
5+
// ^com

tests/analysis_tests/tests/src/expected/Completion.res.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,6 +2147,16 @@ Path T
21472147
"modulePath": "TableclothMap",
21482148
"filePath": "src/Completion.res"
21492149
}
2150+
}, {
2151+
"label": "Throw",
2152+
"kind": 9,
2153+
"tags": [],
2154+
"detail": "module Throw",
2155+
"documentation": null,
2156+
"data": {
2157+
"modulePath": "Throw",
2158+
"filePath": "src/Completion.res"
2159+
}
21502160
}, {
21512161
"label": "TypeArgCtx",
21522162
"kind": 9,

tests/analysis_tests/tests/src/expected/CompletionJsxProps.res.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ Path CompletionSupport.TestComponent.make
9797
"modulePath": "TableclothMap",
9898
"filePath": "src/CompletionJsxProps.res"
9999
}
100+
}, {
101+
"label": "Throw",
102+
"kind": 9,
103+
"tags": [],
104+
"detail": "module Throw",
105+
"documentation": null,
106+
"data": {
107+
"modulePath": "Throw",
108+
"filePath": "src/CompletionJsxProps.res"
109+
}
100110
}, {
101111
"label": "TypeArgCtx",
102112
"kind": 9,
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
Complete src/Throw.res 3:22
2+
posCursor:[3:22] posNoWhite:[3:21] Found expr:[3:11->3:22]
3+
posCursor:[3:22] posNoWhite:[3:21] Found expr:[3:17->3:22]
4+
Pexp_ident throw:[3:17->3:22]
5+
Completable: Cpath Value[throw]
6+
Package opens Stdlib.place holder Pervasives.JsxModules.place holder
7+
Resolved opens 1 Stdlib
8+
ContextPath Value[throw]
9+
[{
10+
"label": "MyCustomThingToThrow",
11+
"kind": 12,
12+
"tags": [],
13+
"detail": "User-defined Exception",
14+
"documentation": null,
15+
"insertText": "(MyCustomThingToThrow($0))",
16+
"insertTextFormat": 2
17+
}, {
18+
"label": "NoArgsToThrow",
19+
"kind": 12,
20+
"tags": [],
21+
"detail": "User-defined Exception",
22+
"documentation": null,
23+
"insertText": "(NoArgsToThrow)",
24+
"insertTextFormat": 2
25+
}, {
26+
"label": "Not_found",
27+
"kind": 12,
28+
"tags": [],
29+
"detail": "Built-in Exception",
30+
"documentation": null,
31+
"insertText": "(Not_found($0))",
32+
"insertTextFormat": 2
33+
}, {
34+
"label": "Invalid_argument",
35+
"kind": 12,
36+
"tags": [],
37+
"detail": "Built-in Exception",
38+
"documentation": null,
39+
"insertText": "(Invalid_argument($0))",
40+
"insertTextFormat": 2
41+
}, {
42+
"label": "Assert_failure",
43+
"kind": 12,
44+
"tags": [],
45+
"detail": "Built-in Exception",
46+
"documentation": null,
47+
"insertText": "(Assert_failure($0))",
48+
"insertTextFormat": 2
49+
}, {
50+
"label": "Failure",
51+
"kind": 12,
52+
"tags": [],
53+
"detail": "Built-in Exception",
54+
"documentation": null,
55+
"insertText": "(Failure($0))",
56+
"insertTextFormat": 2
57+
}, {
58+
"label": "Match_failure",
59+
"kind": 12,
60+
"tags": [],
61+
"detail": "Built-in Exception",
62+
"documentation": null,
63+
"insertText": "(Match_failure($0))",
64+
"insertTextFormat": 2
65+
}, {
66+
"label": "Division_by_zero",
67+
"kind": 12,
68+
"tags": [],
69+
"detail": "Built-in Exception",
70+
"documentation": null,
71+
"insertText": "(Division_by_zero)",
72+
"insertTextFormat": 2
73+
}]
74+

0 commit comments

Comments
 (0)