package reason

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

Source file reason_heuristics.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
open Ppxlib

let is_punned_labelled_expression e lbl =
  match e.pexp_desc with
  | Pexp_ident { txt; _ }
  | Pexp_constraint ({ pexp_desc = Pexp_ident { txt; _ }; _ }, _)
  | Pexp_coerce ({ pexp_desc = Pexp_ident { txt; _ }; _ }, _, _) ->
    Reason_syntax_util.parse_lid lbl = txt
  | _ -> false

(* We manually check the length of `Thing.map(foo, bar, baz`, * in
   `Thing.map(foo, bar, baz, (a) => doStuff(a))` * because Easyformat doesn't
   have a hook to change printing when a list breaks * * we check if all
   arguments aside from the final one are either strings or identifiers, * where
   the sum of the string contents and identifier names are less than the print
   width *)
let funAppCallbackExceedsWidth ~printWidth ~args ~funExpr () =
  let funLen =
    match funExpr.pexp_desc with
    | Pexp_ident ident ->
      let identList = Longident.flatten_exn ident.txt in
      let lengthOfDots = List.length identList - 1 in
      let len =
        List.fold_left
          (fun acc curr -> acc + String.length curr)
          lengthOfDots
          identList
      in
      len
    | _ -> -1
  in
  (* eats an argument & substract its length from the printWidth * as soon as
     the print width reaches a sub-zero value, * we know the print width is
     exceeded & returns *)
  let rec aux len = function
    | _ when len < 0 -> true
    | [] -> false
    | arg :: args ->
      (match arg with
      | label, ({ pexp_desc = Pexp_ident ident; _ } as e) ->
        let identLen =
          List.fold_left
            (fun acc curr -> acc + String.length curr)
            len
            (Longident.flatten_exn ident.txt)
        in
        (match label with
        | Nolabel -> aux (len - identLen) args
        | Labelled s when is_punned_labelled_expression e s ->
          aux (len - (identLen + 1)) args
        | Labelled s -> aux (len - (identLen + 2 + String.length s)) args
        | Optional s -> aux (len - (identLen + 3 + String.length s)) args)
      | label, { pexp_desc = Pexp_constant (Pconst_string (str, _, _)); _ } ->
        let strLen = String.length str in
        (match label with
        | Nolabel -> aux (len - strLen) args
        | Labelled s -> aux (len - (strLen + 2 + String.length s)) args
        | Optional s -> aux (len - (strLen + 3 + String.length s)) args)
      | _ ->
        (* if we encounter a non-string or non-identifier argument exit *)
        true)
  in
  aux (printWidth - funLen) args

(* * Whether or not an identiier is small enough to justify omitting the *
   trailing comma for single identifier patterns. For single identifier *
   patterns, usually the identifier is not "far right" in the document, and * is
   one of the last things to require breaking. We can omit the trailing comma *
   in these cases because it likely will never render anyways and therefore the
   * space taken up by the trailing comma doesn't disrupt wrapping length
   calculations. * * For example, the `X` hardly ever benefits from a trailing
   comma. * | X(y) => *)
let singleTokenPatternOmmitTrail txt = String.length txt < 4

(* Indicates whether an expression can be printed with the uncurried * dot
   notation. At the moment uncurried function application & definition * only
   makes sense in the context of a Pexp_apply or Pexp_fun * * Examples: * [@bs]
   add(2, 3); -> add(. 2, 3); (* Pexp_apply *) * setTimeout([@bs] () =>
   Js.log("hola"), 1000); (* Pexp_fun *) * -> setTimeout((.) => Js.log("hola"),
   1000); *)
let bsExprCanBeUncurried expr =
  match Parsetree.(expr.pexp_desc) with
  | Pexp_function _ | Pexp_apply _ -> true
  | _ -> false

let isUnderscoreIdent expr =
  match Parsetree.(expr.pexp_desc) with
  | Pexp_ident { txt = Lident "_"; _ } -> true
  | _ -> false

let isPipeFirst e =
  match Parsetree.(e.pexp_desc) with
  | Pexp_ident { txt = Longident.Lident "|."; _ } -> true
  | Pexp_apply
      ({ pexp_desc = Pexp_ident { txt = Longident.Lident "|."; _ }; _ }, _) ->
    true
  | _ -> false

let isUnderscoreApplication expr =
  match expr with
  | { pexp_attributes = []
    ; pexp_desc =
        Pexp_function
          ( { pparam_desc =
                Pparam_val
                  ( Nolabel
                  , None
                  , { ppat_desc = Ppat_var { txt = "__x"; _ }
                    ; ppat_attributes = []
                    ; _
                    } )
            ; _
            }
            :: _
          , _
          , _ )
    ; _
    } ->
    true
  | _ -> false

(* <div> {items->Belt.Array.map(ReasonReact.string)->ReasonReact.array} </div>;
 * An application with pipe first inside jsx children requires special treatment.
 * Jsx children don't allow expression application, hence we need the braces
 * preserved in this case. *)
let isPipeFirstWithNonSimpleJSXChild e =
  match Parsetree.(e.pexp_desc) with
  | Pexp_apply
      ( { pexp_desc = Pexp_ident { txt = Longident.Lident "|."; _ }; _ }
      , [ (Nolabel, { pexp_desc = Pexp_apply _; _ }); _ ] ) ->
    true
  (* Handle <div> {url->a(b, _)} </div>;
   * underscore sugar needs protection *)
  | Pexp_apply
      ( { pexp_desc = Pexp_ident { txt = Longident.Lident "|."; _ }; _ }
      , [ _; (Nolabel, fe) ] )
    when isUnderscoreApplication fe ->
    true
  | _ -> false
OCaml

Innovation. Community. Security.