Source file Suggestions.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
open Forester_prelude
open Forester_core
let edit_distance ~cutoff x y =
let len_x, len_y = String.length x, String.length y in
let grid = Array.make_matrix (len_x + 1) (len_y + 1) 0 in
for i = 1 to len_x do
grid.(i).(0) <- i
done;
for j = 1 to len_y do
grid.(0).(j) <- j
done;
for j = 1 to len_y do
for i = 1 to len_x do
let cost = if x.[i - 1] = y.[j - 1] then 0 else 1 in
let k = Int.min (grid.(i - 1).(j) + 1) (grid.(i).(j - 1) + 1) in
grid.(i).(j) <- Int.min k (grid.(i - 1).(j - 1) + cost)
done;
done;
let result = grid.(len_x).(len_y) in
if result > cutoff then None
else
Some result
let suggestions ?prefix ~(cutoff : int) (p : Trie.bwd_path) : ('data, 'tag) Trie.t -> ('data, int) Trie.t =
let compare p d =
edit_distance
~cutoff
(String.concat "" (Bwd.to_list p))
(String.concat "" (Bwd.to_list d))
in
Trie.filter_map ?prefix @@ fun q (data, _) ->
let@ i = Option.bind @@ compare p q in
if i > cutoff then None else Some (data, i)
let suggestions ~visible path =
suggestions ~cutoff: 2 (Bwd.of_list path) visible
|> Trie.to_seq
|> Seq.map (fun (path, (data, distance)) -> (path, data, distance))
|> List.of_seq
|> List.sort (fun (_, _, a) (_, _, b) -> Int.compare a b)
let create_suggestions ~visible path =
let suggestions = suggestions ~visible path in
let =
if List.length suggestions > 0 then
let (path, data, _) = List.hd suggestions in
let location_hint =
match data with
| Syn.Term ({loc = Some loc; _} :: _) ->
begin
match Range.view loc with
| `End_of_file {source; _}
| `Range ({source; _}, _) ->
match Range.title source with
| Some string ->
[Asai.Diagnostic.loctextf "defined in %s" string]
| _ -> []
end
| _ -> []
in
[Asai.Diagnostic.loctextf "Did you mean %a?" Trie.pp_path path] @ location_hint
else []
in
extra_remarks