package ocamlformat

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file Indent.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
(**************************************************************************)
(*                                                                        *)
(*                              OCamlFormat                               *)
(*                                                                        *)
(*            Copyright (c) Facebook, Inc. and its affiliates.            *)
(*                                                                        *)
(*      This source code is licensed under the MIT license found in       *)
(*      the LICENSE file in the root directory of this source tree.       *)
(*                                                                        *)
(**************************************************************************)

open Migrate_ast

module Ocp_indent = struct
  let run ~source ~in_lines ~on_indent ~init =
    let config, _, _ = IndentConfig.local_default () in
    let output : _ IndentPrinter.output =
      { debug= false
      ; config
      ; in_lines
      ; adaptive= true
      ; indent_empty= false
      ; kind= Numeric on_indent }
    in
    let stream = Nstream.of_string source in
    IndentPrinter.proceed output stream IndentBlock.empty init
end

module Valid_ast = struct
  let loc_of_line loctree locs line =
    let rec aux = function
      | [] -> None
      | (h : Location.t) :: t ->
          if h.loc_start.pos_lnum = line then Some h
          else if h.loc_start.pos_lnum <= line && line <= h.loc_end.pos_lnum
          then
            match Loc_tree.children loctree h with
            | [] -> Some h
            | children -> Some (Option.value (aux children) ~default:h)
          else aux t
    in
    aux locs

  let matching_loc locs locs' loc : Location.t =
    match List.zip locs locs' with
    | Ok assoc -> (
        let equal x y = Location.compare x y = 0 in
        match List.Assoc.find assoc ~equal loc with
        | Some loc -> loc
        | None ->
            impossible "Cannot find matching location in formatted output." )
    | Unequal_lengths ->
        impossible "Cannot match pre-post formatting locations."

  let indent_range fragment ~unformatted:(ast, txt_src)
      ~formatted:(fmted_ast, fmted_src) ~lines ~range =
    let low, high = Range.get range in
    let loctree, locs = Loc_tree.of_ast fragment ast in
    let _, locs' = Loc_tree.of_ast fragment fmted_ast in
    let indent_line i =
      match loc_of_line loctree locs i with
      | Some loc -> (
          let fmted_loc = matching_loc locs locs' loc in
          let fmted_lnum = fmted_loc.loc_start.pos_lnum in
          match Source.find_first_token_on_line fmted_src fmted_lnum with
          | Some (_, loc) -> Some (Position.column loc.loc_start)
          | None -> None )
      | None -> None
    in
    let rec aux ?prev acc idx_ocpi i =
      if i > high then (List.rev acc, idx_ocpi)
      else
        match
          match List.nth lines (i - 1) with
          | Some _ -> (
            match indent_line i with
            | Some indent -> (
              match prev with
              | Some prev_indent when indent = prev_indent -> `Maybe indent
              | _ -> `Ok indent )
            | None -> `Ok 0 )
          | None -> `Ok 0
        with
        | `Ok x -> aux ~prev:x (`Ok x :: acc) idx_ocpi (i + 1)
        | `Maybe x -> aux ~prev:x (`Maybe x :: acc) (i :: idx_ocpi) (i + 1)
    in
    let prev =
      let i = low - 1 in
      match List.nth lines (i - 1) with
      | Some _ -> indent_line i
      | None -> None
    in
    let aux_ret, idx_ocpi = aux ?prev [] [] low in
    let rec on_indent i (aux_ret, res) =
      match aux_ret with
      | [] -> impossible "list expected to contain at least a `Maybe item"
      | `Ok x :: t -> on_indent i (t, x :: res)
      | `Maybe x :: t -> (t, (if i = 0 then x else i) :: res)
    in
    let aux_ret, ret =
      Ocp_indent.run ~source:txt_src ~init:(aux_ret, []) ~on_indent
        ~in_lines:(List.mem idx_ocpi ~equal:Int.equal)
    in
    List.rev
    @@ List.fold_left aux_ret ~init:ret ~f:(fun t -> function
         | `Ok h -> h :: t
         | `Maybe _ -> impossible "all `Maybe were handled by ocp-indent" )
end

module Partial_ast = struct
  let indent_range ~source ~range =
    let low, high = Range.get range in
    List.rev
    @@ Ocp_indent.run ~source ~init:[]
         ~in_lines:(fun i -> low <= i && i <= high)
         ~on_indent:(fun i acc -> i :: acc)
end
OCaml

Innovation. Community. Security.