/
fileOutline.ml
371 lines (341 loc) · 10.1 KB
/
fileOutline.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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
(**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the "hack" directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*)
open Core
open Reordered_argument_collections
open SymbolDefinition
type outline = string SymbolDefinition.t list
let modifiers_of_ast_kinds l =
List.map l begin function
| Ast.Final -> Final
| Ast.Static -> Static
| Ast.Abstract -> Abstract
| Ast.Private -> Private
| Ast.Public -> Public
| Ast.Protected -> Protected
end
let summarize_property class_name kinds var =
let modifiers = modifiers_of_ast_kinds kinds in
let span, (pos, name), _expr_opt = var in
let kind = Property in
let id = get_symbol_id kind (Some class_name) name in
{
kind;
name;
id;
pos;
span;
modifiers;
children = None;
params = None;
docblock = None;
}
let maybe_summarize_property class_name ~skip kinds var =
let _, (_, name), _ = var in
if SSet.mem skip name then [] else [summarize_property class_name kinds var]
let summarize_const class_name ((pos, name), (expr_pos, _)) =
let span = (Pos.btw pos expr_pos) in
let kind = Const in
let id = get_symbol_id kind (Some class_name) name in
{
kind;
name;
id;
pos;
span;
modifiers = [];
children = None;
params = None;
docblock = None;
}
let summarize_abs_const class_name (pos, name) =
let kind = Const in
let id = get_symbol_id kind (Some class_name) name in
{
kind;
name;
id;
pos = pos;
span = pos;
modifiers = [Abstract];
children = None;
params = None;
docblock = None;
}
let modifier_of_fun_kind acc = function
| Ast.FAsync | Ast.FAsyncGenerator -> Async :: acc
| _ -> acc
let summarize_typeconst class_name t =
let pos, name = t.Ast.tconst_name in
let kind = Typeconst in
let id = get_symbol_id kind (Some class_name) name in
{
kind;
name;
id;
pos;
span = t.Ast.tconst_span;
modifiers = if t.Ast.tconst_abstract then [Abstract] else [];
children = None;
params = None;
docblock = None;
}
let summarize_param param =
let pos, name = param.Ast.param_id in
let param_start = Option.value_map param.Ast.param_hint ~f:fst ~default:pos in
let param_end = Option.value_map param.Ast.param_expr ~f:fst ~default:pos in
let modifiers =
modifiers_of_ast_kinds (Option.to_list param.Ast.param_modifier) in
let kind = Param in
let id = get_symbol_id kind None name in
{
kind;
name;
id;
pos;
span = Pos.btw param_start param_end;
children = None;
modifiers;
params = None;
docblock = None;
}
let summarize_method class_name m =
let modifiers = modifier_of_fun_kind [] m.Ast.m_fun_kind in
let modifiers = (modifiers_of_ast_kinds m.Ast.m_kind) @ modifiers in
let params = Some (List.map m.Ast.m_params summarize_param) in
let name = snd m.Ast.m_name in
let kind = Method in
let id = get_symbol_id kind (Some class_name) name in
{
kind;
name;
id;
pos = (fst m.Ast.m_name);
span = m.Ast.m_span;
modifiers;
children = None;
params;
docblock = None;
}
(* Parser synthesizes AST nodes for implicit properties (defined in constructor
* parameter lists. We don't want them to show up in outline view *)
let params_implicit_fields params =
List.filter_map params ~f:begin function
| { Ast.param_modifier = Some _vis; param_id; _ } ->
Some (String_utils.lstrip (snd param_id) "$" )
| _ -> None
end
let class_implicit_fields class_ =
List.concat_map class_.Ast.c_body ~f:begin function
| Ast.Method { Ast.m_name = _, "__construct"; m_params; _ } ->
params_implicit_fields m_params
| _ -> []
end
let summarize_class class_ ~no_children =
let class_name = Utils.strip_ns (snd class_.Ast.c_name) in
let class_name_pos = fst class_.Ast.c_name in
let c_span = class_.Ast.c_span in
let modifiers =
if class_.Ast.c_final then [Final] else []
in
let modifiers = match class_.Ast.c_kind with
| Ast.Cabstract -> Abstract :: modifiers
| _ -> modifiers
in
let children = if no_children then None else begin
let implicit_props = List.fold (class_implicit_fields class_)
~f:SSet.add ~init:SSet.empty
in
Some (List.concat_map class_.Ast.c_body ~f:begin function
| Ast.Method m -> [summarize_method class_name m]
| Ast.ClassVars (kinds, _, vars) ->
List.concat_map vars
~f:(maybe_summarize_property class_name ~skip:implicit_props kinds)
| Ast.XhpAttr (_, var, _, _) ->
maybe_summarize_property class_name ~skip:implicit_props [] var
| Ast.Const (_, cl) -> List.map cl ~f:(summarize_const class_name)
| Ast.AbsConst (_, id) -> [summarize_abs_const class_name id]
| Ast.TypeConst t -> [summarize_typeconst class_name t]
| _ -> []
end)
end in
let kind = match class_.Ast.c_kind with
| Ast.Cinterface -> Interface
| Ast.Ctrait -> Trait
| Ast.Cenum -> Enum
| _ -> Class
in
let name = class_name in
let id = get_symbol_id kind None name in
{
kind;
name;
id;
pos = class_name_pos;
span = c_span;
modifiers;
children;
params = None;
docblock = None;
}
let summarize_fun f =
let modifiers = modifier_of_fun_kind [] f.Ast.f_fun_kind in
let params = Some (List.map f.Ast.f_params summarize_param) in
let kind = Function in
let name = Utils.strip_ns (snd f.Ast.f_name) in
let id = get_symbol_id kind None name in
{
kind;
name;
id;
pos = fst f.Ast.f_name;
span = f.Ast.f_span;
modifiers;
children = None;
params;
docblock = None;
}
let summarize_gconst cst =
let pos = fst cst.Ast.cst_name in
let gconst_start = Option.value_map cst.Ast.cst_type ~f:fst ~default:pos in
let gconst_end = fst cst.Ast.cst_value in
let kind = Const in
let name = Utils.strip_ns (snd cst.Ast.cst_name) in
let id = get_symbol_id kind None name in
{
kind;
name;
id;
pos;
span = Pos.btw gconst_start gconst_end;
modifiers = [];
children = None;
params = None;
docblock = None;
}
let summarize_local name span =
let kind = LocalVar in
let id = get_symbol_id kind None name in
{
kind;
name;
id;
pos = span;
span;
modifiers = [];
children = None;
params = None;
docblock = None;
}
let outline_ast ast =
let outline = List.filter_map ast ~f:begin function
| Ast.Fun f -> Some (summarize_fun f)
| Ast.Class c -> Some (summarize_class c ~no_children:false)
| _ -> None
end in
List.map outline SymbolDefinition.to_absolute
let should_add_docblock = function
| Function| Class | Method | Property | Const | Enum
| Interface | Trait | Typeconst -> true
| LocalVar | Param -> false
let add_def_docblock finder previous_def_line def =
let line = Pos.line def.pos in
let docblock = if should_add_docblock def.kind
then Docblock_finder.find_docblock finder previous_def_line line
else None
in
line, { def with docblock }
let add_docblocks defs comments =
let finder = Docblock_finder.make_docblock_finder comments in
let rec map_def f acc def =
let acc, def = f acc def in
let acc, children = Option.value_map def.children
~f:(fun defs ->
let acc, defs = map_def_list f acc defs in
acc, Some defs)
~default:(acc, None)
in
acc, { def with children }
and map_def_list f acc defs =
let acc, defs = List.fold_left defs
~f:(fun (acc, defs) def ->
let acc, def = map_def f acc def in
acc, def :: defs)
~init:(acc, []) in
acc, List.rev defs
in
snd (map_def_list (add_def_docblock finder) 0 defs)
let outline popt content =
let {Parser_hack.ast; comments; _} =
Parser_hack.program
popt
Relative_path.default
content
~include_line_comments:true
~keep_errors:false
in
let result = outline_ast ast in
add_docblocks result comments
let rec definition_to_json def =
Hh_json.JSON_Object ([
"kind", Hh_json.JSON_String (string_of_kind def.kind);
"name", Hh_json.JSON_String def.name;
"id", Option.value_map def.id
~f:(fun x -> Hh_json.JSON_String x) ~default:Hh_json.JSON_Null;
"position", Pos.json def.pos;
"span", Pos.multiline_json def.span;
"modifiers", Hh_json.JSON_Array
(List.map def.modifiers
(fun x -> Hh_json.JSON_String (string_of_modifier x)));
] @
(Option.value_map def.children
~f:(fun x -> [("children", to_json x)]) ~default:[])
@
(Option.value_map def.params
~f:(fun x -> [("params", to_json x)]) ~default:[])
@
(Option.value_map def.docblock
~f:(fun x -> [("docblock", Hh_json.JSON_String x)]) ~default:[]))
and to_json outline =
Hh_json.JSON_Array begin
List.map outline ~f:definition_to_json
end
let rec print_def ~short_pos indent def =
let
{name; kind; id; pos; span; modifiers; children; params; docblock} = def
in
let print_pos, print_span = if short_pos
then Pos.string_no_file, Pos.multiline_string_no_file
else Pos.string, Pos.multiline_string
in
Printf.printf "%s%s\n" indent name;
Printf.printf "%s kind: %s\n" indent (string_of_kind kind);
Option.iter id (fun id -> Printf.printf "%s id: %s\n" indent id);
Printf.printf "%s position: %s\n" indent (print_pos pos);
Printf.printf "%s span: %s\n" indent (print_span span);
Printf.printf "%s modifiers: " indent;
List.iter modifiers
(fun x -> Printf.printf "%s " (string_of_modifier x));
Printf.printf "\n";
Option.iter params (fun x ->
Printf.printf "%s params:\n" indent;
print ~short_pos (indent ^ " ") x;
);
Option.iter docblock (fun x ->
Printf.printf "%s docblock:\n" indent;
Printf.printf "%s\n" x;
);
Printf.printf "\n";
Option.iter children (fun x ->
print ~short_pos (indent ^ " ") x
);
and print ~short_pos indent defs =
List.iter defs ~f:(print_def ~short_pos indent)
let print_def ?short_pos:(short_pos = false) = print_def ~short_pos
let print ?short_pos:(short_pos = false) = print ~short_pos ""