Source file loc.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
(** Source locations: byte-offset ranges in the original input.

    Every {!Token.t} and {!Component.t} carries one, letting downstream
    validators report errors with precise positions and recover ranges for
    editor integrations. *)

type meta_level = [ `None | `Locs | `Full ]

let default_meta_level : meta_level = `Full

type t = { start_pos : int; end_pos : int }

let v ~start_pos ~end_pos = { start_pos; end_pos }
let dummy = { start_pos = 0; end_pos = 0 }

let union a b =
  { start_pos = min a.start_pos b.start_pos; end_pos = max a.end_pos b.end_pos }

let pp : t Pp.t =
 fun ctx { start_pos; end_pos } ->
  Pp.char ctx '[';
  Pp.string ctx (string_of_int start_pos);
  Pp.char ctx '-';
  Pp.string ctx (string_of_int end_pos);
  Pp.char ctx ']'

let to_string t = Pp.to_string pp t

module Path = struct
  type step = Mem of string | Nth of int | Label of string
  type t = step list

  let empty = []
  let push step t = t @ [ step ]

  let rec last = function
    | [] -> None
    | [ step ] -> Some step
    | _ :: rest -> last rest

  let to_list t = t
  let of_labels labels = List.map (fun label -> Label label) labels

  let string_of_step = function
    | Mem s -> s
    | Nth n -> "[" ^ string_of_int n ^ "]"
    | Label s -> s

  let to_labels t = List.map string_of_step t
  let pp : t Pp.t = fun ctx t -> Pp.string ctx (String.concat "/" (to_labels t))
end

module Context = struct
  type snippet = { text : string; marker_pos : int; marker_len : int }

  type nonrec t = {
    path : Path.t;
    loc : t;
    sort : Sort.t;
    snippet : snippet option;
  }

  let push step t = { t with path = Path.push step t.path }
end

let snippet ?(window = 40) source loc =
  let len = String.length source in
  let pos = max 0 (min len loc.start_pos) in
  let end_pos = max pos (min len loc.end_pos) in
  let start_pos = max 0 (pos - window) in
  let stop_pos = min len (end_pos + window) in
  let text = String.sub source start_pos (stop_pos - start_pos) in
  let marker_pos = pos - start_pos in
  let marker_len = max 1 (end_pos - pos) in
  let marker_len = min marker_len (String.length text - marker_pos) in
  { Context.text; marker_pos; marker_len }