-
Notifications
You must be signed in to change notification settings - Fork 463
/
Copy pathUtils.ml
268 lines (236 loc) · 7.72 KB
/
Utils.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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
(**
* `startsWith(string, prefix)`
* true if the string starts with the prefix
*)
let startsWith s prefix =
if prefix = "" then true
else
let p = String.length prefix in
p <= String.length s && String.sub s 0 p = prefix
let endsWith s suffix =
if suffix = "" then true
else
let p = String.length suffix in
let l = String.length s in
p <= String.length s && String.sub s (l - p) p = suffix
let isFirstCharUppercase s =
String.length s > 0 && Char.equal s.[0] (Char.uppercase_ascii s.[0])
let cmtPosToPosition {Lexing.pos_lnum; pos_cnum; pos_bol} =
Protocol.{line = pos_lnum - 1; character = pos_cnum - pos_bol}
let cmtLocToRange {Location.loc_start; loc_end} =
Protocol.{start = cmtPosToPosition loc_start; end_ = cmtPosToPosition loc_end}
let endOfLocation loc length =
let open Location in
{
loc with
loc_start = {loc.loc_end with pos_cnum = loc.loc_end.pos_cnum - length};
}
let chopLocationEnd loc length =
let open Location in
{
loc with
loc_end = {loc.loc_end with pos_cnum = loc.loc_end.pos_cnum - length};
}
(** An optional List.find *)
let rec find fn items =
match items with
| [] -> None
| one :: rest -> (
match fn one with
| None -> find fn rest
| Some x -> Some x)
let filterMap f =
let rec aux accu = function
| [] -> List.rev accu
| x :: l -> (
match f x with
| None -> aux accu l
| Some v -> aux (v :: accu) l)
in
aux []
let dumpPath path = Str.global_replace (Str.regexp_string "\\") "/" path
let isUncurriedInternal path = startsWith (Path.name path) "Js.Fn.arity"
let flattenLongIdent ?(jsx = false) ?(cutAtOffset = None) lid =
let extendPath s path =
match path with
| "" :: _ -> path
| _ -> s :: path
in
let rec loop lid =
match lid with
| Longident.Lident txt -> ([txt], String.length txt)
| Ldot (lid, txt) ->
let path, offset = loop lid in
if Some offset = cutAtOffset then (extendPath "" path, offset + 1)
else if jsx && txt = "createElement" then (path, offset)
else if txt = "_" then (extendPath "" path, offset + 1)
else (extendPath txt path, offset + 1 + String.length txt)
| Lapply _ -> ([], 0)
in
let path, _ = loop lid in
List.rev path
let identifyPexp pexp =
match pexp with
| Parsetree.Pexp_ident _ -> "Pexp_ident"
| Pexp_constant _ -> "Pexp_constant"
| Pexp_let _ -> "Pexp_let"
| Pexp_fun _ -> "Pexp_fun"
| Pexp_apply _ -> "Pexp_apply"
| Pexp_match _ -> "Pexp_match"
| Pexp_try _ -> "Pexp_try"
| Pexp_tuple _ -> "Pexp_tuple"
| Pexp_construct _ -> "Pexp_construct"
| Pexp_variant _ -> "Pexp_variant"
| Pexp_record _ -> "Pexp_record"
| Pexp_field _ -> "Pexp_field"
| Pexp_setfield _ -> "Pexp_setfield"
| Pexp_array _ -> "Pexp_array"
| Pexp_ifthenelse _ -> "Pexp_ifthenelse"
| Pexp_sequence _ -> "Pexp_sequence"
| Pexp_while _ -> "Pexp_while"
| Pexp_for _ -> "Pexp_for"
| Pexp_constraint _ -> "Pexp_constraint"
| Pexp_coerce _ -> "Pexp_coerce"
| Pexp_send _ -> "Pexp_send"
| Pexp_letmodule _ -> "Pexp_letmodule"
| Pexp_letexception _ -> "Pexp_letexception"
| Pexp_assert _ -> "Pexp_assert"
| Pexp_lazy _ -> "Pexp_lazy"
| Pexp_newtype _ -> "Pexp_newtype"
| Pexp_pack _ -> "Pexp_pack"
| Pexp_extension _ -> "Pexp_extension"
| Pexp_open _ -> "Pexp_open"
let identifyPpat pat =
match pat with
| Parsetree.Ppat_any -> "Ppat_any"
| Ppat_var _ -> "Ppat_var"
| Ppat_alias _ -> "Ppat_alias"
| Ppat_constant _ -> "Ppat_constant"
| Ppat_interval _ -> "Ppat_interval"
| Ppat_tuple _ -> "Ppat_tuple"
| Ppat_construct _ -> "Ppat_construct"
| Ppat_variant _ -> "Ppat_variant"
| Ppat_record _ -> "Ppat_record"
| Ppat_array _ -> "Ppat_array"
| Ppat_or _ -> "Ppat_or"
| Ppat_constraint _ -> "Ppat_constraint"
| Ppat_type _ -> "Ppat_type"
| Ppat_lazy _ -> "Ppat_lazy"
| Ppat_unpack _ -> "Ppat_unpack"
| Ppat_exception _ -> "Ppat_exception"
| Ppat_extension _ -> "Ppat_extension"
| Ppat_open _ -> "Ppat_open"
let rec skipWhite text i =
if i < 0 then 0
else
match text.[i] with
| ' ' | '\n' | '\r' | '\t' -> skipWhite text (i - 1)
| _ -> i
let hasBraces attributes =
attributes |> List.exists (fun (loc, _) -> loc.Location.txt = "res.braces")
let rec unwrapIfOption (t : Types.type_expr) =
match t.desc with
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> unwrapIfOption t1
| Tconstr (Path.Pident {name = "option"}, [unwrappedType], _) -> unwrappedType
| _ -> t
let isJsxComponent (vb : Parsetree.value_binding) =
vb.pvb_attributes
|> List.exists (function
| {Location.txt = "react.component" | "jsx.component"}, _payload -> true
| _ -> false)
let checkName name ~prefix ~exact =
if exact then name = prefix else startsWith name prefix
let rec getUnqualifiedName txt =
match txt with
| Longident.Lident fieldName -> fieldName
| Ldot (t, _) -> getUnqualifiedName t
| _ -> ""
let indent n text =
let spaces = String.make n ' ' in
let len = String.length text in
let text =
if len != 0 && text.[len - 1] = '\n' then String.sub text 0 (len - 1)
else text
in
let lines = String.split_on_char '\n' text in
match lines with
| [] -> ""
| [line] -> line
| line :: lines ->
line ^ "\n"
^ (lines |> List.map (fun line -> spaces ^ line) |> String.concat "\n")
let mkPosition (pos : Pos.t) =
let line, character = pos in
{Protocol.line; character}
let rangeOfLoc (loc : Location.t) =
let start = loc |> Loc.start |> mkPosition in
let end_ = loc |> Loc.end_ |> mkPosition in
{Protocol.start; end_}
let rec expandPath (path : Path.t) =
match path with
| Pident id -> [Ident.name id]
| Pdot (p, s, _) -> s :: expandPath p
| Papply _ -> []
module Option = struct
let flatMap f o =
match o with
| None -> None
| Some v -> f v
end
let rec lastElements list =
match list with
| ([_; _] | [_] | []) as res -> res
| _ :: tl -> lastElements tl
let lowercaseFirstChar s =
if String.length s = 0 then s
else String.mapi (fun i c -> if i = 0 then Char.lowercase_ascii c else c) s
let cutAfterDash s =
match String.index s '-' with
| n -> ( try String.sub s 0 n with Invalid_argument _ -> s)
| exception Not_found -> s
let fileNameHasUnallowedChars s =
let regexp = Str.regexp "[^A-Za-z0-9_]" in
try
ignore (Str.search_forward regexp s 0);
true
with Not_found -> false
(* Flattens any namespace in the provided path.
Example:
Globals-RescriptBun.URL.t (which is an illegal path because of the namespace) becomes:
RescriptBun.Globals.URL.t
*)
let rec flattenAnyNamespaceInPath path =
match path with
| [] -> []
| head :: tail ->
if String.contains head '-' then
let parts = String.split_on_char '-' head in
(* Namespaces are in reverse order, so "URL-RescriptBun" where RescriptBun is the namespace. *)
(parts |> List.rev) @ flattenAnyNamespaceInPath tail
else head :: flattenAnyNamespaceInPath tail
let printMaybeExoticIdent ?(allowUident = false) txt =
let len = String.length txt in
let rec loop i =
if i == len then txt
else if i == 0 then
match String.unsafe_get txt i with
| 'A' .. 'Z' when allowUident -> loop (i + 1)
| 'a' .. 'z' | '_' -> loop (i + 1)
| _ -> "\"" ^ txt ^ "\""
else
match String.unsafe_get txt i with
| 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' | '\'' | '_' -> loop (i + 1)
| _ -> "\"" ^ txt ^ "\""
in
if Res_token.is_keyword_txt txt then "\"" ^ txt ^ "\"" else loop 0
let findPackageJson root =
let path = Uri.toPath root in
let rec loop path =
if path = "/" then None
else if Files.exists (Filename.concat path "package.json") then
Some (Filename.concat path "package.json")
else
let parent = Filename.dirname path in
if parent = path then (* reached root *) None else loop parent
in
loop path