From c4904884232749842b2e57bb0f9241265292ce23 Mon Sep 17 00:00:00 2001 From: sneeker Date: Wed, 25 May 2022 15:17:54 +0000 Subject: [PATCH] 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. --- lib/suggest.ml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 lib/suggest.ml diff --git a/lib/suggest.ml b/lib/suggest.ml new file mode 100644 index 0000000..3521575 --- /dev/null +++ b/lib/suggest.ml @@ -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)