Source file type_search.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
open Std
let sherlodoc_type_of env typ =
let open Merlin_sherlodoc in
let rec aux typ =
match Types.get_desc typ with
| Types.Tvar None -> Type_parsed.Wildcard
| Types.Tvar (Some ty) -> Type_parsed.Tyvar ty
| Types.Ttuple elts -> Type_parsed.tuple @@ List.map ~f:aux elts
| Types.Tarrow (_, a, b, _) -> Type_parsed.Arrow (aux a, aux b)
| Types.Tconstr (p, args, _) ->
let p = Printtyp.rewrite_double_underscore_paths env p in
let name = Format.asprintf "%a" Printtyp.path p in
Type_parsed.Tycon (name, List.map ~f:aux args)
| _ -> Type_parsed.Unhandled
in
typ |> aux |> Type_expr.normalize_type_parameters
let make_constructible path desc =
let holes =
match Types.get_desc desc with
| Types.Tarrow (l, _, b, _) ->
let rec aux acc t =
match Types.get_desc t with
| Types.Tarrow (l, _, b, _) -> aux (acc ^ with_label l) b
| _ -> acc
and with_label l =
match l with
| Ocaml_parsing.Asttypes.Nolabel -> " _"
| Labelled s -> " ~" ^ s ^ ":_"
| Optional _ -> ""
in
aux (with_label l) b
| _ -> ""
in
path ^ holes
let doc_to_option = function
| `Builtin doc | `Found doc -> Some doc
| _ -> None
let get_doc ~config ~env ~local_defs ~ ~pos name =
Locate.get_doc ~config ~env ~local_defs ~comments ~pos (`User_input name)
|> doc_to_option
let compare_result Query_protocol.{ cost = cost_a; name = a; doc = doc_a; _ }
Query_protocol.{ cost = cost_b; name = b; doc = doc_b; _ } =
let c = Int.compare cost_a cost_b in
if Int.equal c 0 then
let c = Int.compare (String.length a) (String.length b) in
match (c, doc_a, doc_b) with
| 0, Some _, None -> 1
| 0, None, Some _ -> -1
| 0, Some doc_a, Some doc_b ->
let c = Int.compare (String.length doc_a) (String.length doc_b) in
if Int.equal 0 c then String.compare a b else c
| 0, None, None -> String.compare a b
| _ -> c
else c
let compute_value query env _ path desc acc =
let open Merlin_sherlodoc in
let d = desc.Types.val_type in
let typ = sherlodoc_type_of env d in
let name =
Printtyp.wrap_printing_env env @@ fun () ->
let path = Printtyp.rewrite_double_underscore_paths env path in
Format.asprintf "%a" Printtyp.path path
in
let cost = Query.distance_for query ~path:name typ in
if cost >= 1000 then acc
else
let doc = None in
let loc = desc.Types.val_loc in
let typ = desc.Types.val_type in
let constructible = make_constructible name d in
Query_protocol.{ cost; name; typ; loc; doc; constructible } :: acc
let compute_values query env lident acc =
Env.fold_values (compute_value query env) lident env acc
let values_from_module query env lident acc =
let rec aux acc lident =
match Env.find_module_by_name lident env with
| exception _ -> acc
| _ ->
let acc = compute_values query env (Some lident) acc in
Env.fold_modules
(fun name _ mdl acc ->
match mdl.Types.md_type with
| Types.Mty_alias _ -> acc
| _ ->
let lident = Longident.Ldot (lident, name) in
aux acc lident)
(Some lident) env acc
in
aux acc lident
let run ?(limit = 100) ~env ~query ~modules () =
let init = compute_values query env None [] in
modules
|> List.fold_left ~init ~f:(fun acc name ->
let lident = Longident.Lident name in
values_from_module query env lident acc)
|> List.sort ~cmp:compare_result
|> List.take_n limit
let classify_query query =
let query = String.trim query in
match query.[0] with
| '+' | '-' -> `Polarity query
| _ -> `By_type query
| exception Invalid_argument _ -> `Polarity query