feat(suggest): rank near-miss names by edit distance

Levenshtein with a cutoff, returning the closest valid name at a position, or
nothing when no candidate is close enough.
This commit is contained in:
milner committed 2022-05-25 15:17:54 +00:00
1 parent 4040f5a404
commit d182750a9f
1 file changed
+33
+33
View File
@@ -0,0 +1,33 @@
let distance a b =
let la = String.length a and lb = String.length b in
if la = 0 then lb
else if lb = 0 then la
else begin
let prev = Array.init (lb + 1) (fun j -> j) in
let cur = Array.make (lb + 1) 0 in
for i = 1 to la do
cur.(0) <- i;
for j = 1 to lb do
let cost = if a.[i - 1] = b.[j - 1] then 0 else 1 in
cur.(j) <- min (min (cur.(j - 1) + 1) (prev.(j) + 1)) (prev.(j - 1) + cost)
done;
Array.blit cur 0 prev 0 (lb + 1)
done;
prev.(lb)
end
let closest word candidates =
let limit = 1 + String.length word / 3 in
List.fold_left
(fun best c ->
let d = distance word c in
match best with
| Some (_, bd) when bd <= d -> best
| _ -> if d <= limit then Some (c, d) else best)
None candidates
|> Option.map fst
let hint word candidates =
match closest word candidates with
| Some c -> Printf.sprintf "did you mean %S?" c
| None -> Printf.sprintf "expected one of: %s" (String.concat ", " candidates)