Source file completion_spec.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
open! Import
module Hint = struct
type 'reentrant t =
| File
| Values of string list
| Reentrant of 'reentrant
let replace_reentrants_with_indices t acc =
match t with
| Reentrant _ -> Reentrant acc, acc + 1
| Values v -> Values v, acc
| File -> File, acc
;;
let all_reentrants = function
| Reentrant r -> [ r ]
| _ -> []
;;
end
module Named_arg = struct
type 'reentrant t =
{ names : Name.t Nonempty_list.t
; has_param : bool
; hint : 'reentrant Hint.t option
}
let first_name { names; _ } = Nonempty_list.hd names
let to_patterns_with_dashes { names; _ } =
Nonempty_list.map names ~f:Name.to_string_with_dashes
|> Shell_dsl.Case_pattern.of_strings
;;
let replace_reentrants_with_indices t acc =
match t.hint with
| Some hint ->
let hint, acc = Hint.replace_reentrants_with_indices hint acc in
{ t with hint = Some hint }, acc
| None -> { t with hint = None }, acc
;;
let all_reentrants t =
match t.hint with
| Some hint -> Hint.all_reentrants hint
| None -> []
;;
end
module Positional_args_hints = struct
type 'reentrant t =
{ finite_args : 'reentrant Hint.t option list
; repeated_arg : [ `No_hint | `Hint of 'reentrant Hint.t ] option
}
let empty = { finite_args = []; repeated_arg = None }
let replace_reentrants_with_indices t acc =
let finite_args, acc =
List.fold_left t.finite_args ~init:([], acc) ~f:(fun (hints, acc) ->
function
| Some hint ->
let hint, acc = Hint.replace_reentrants_with_indices hint acc in
Some hint :: hints, acc
| None -> None :: hints, acc)
in
let finite_args = List.rev finite_args in
let repeated_arg, acc =
match t.repeated_arg with
| Some (`Hint hint) ->
let hint, acc = Hint.replace_reentrants_with_indices hint acc in
Some (`Hint hint), acc
| Some `No_hint -> Some `No_hint, acc
| None -> None, acc
in
{ finite_args; repeated_arg }, acc
;;
let all_reentrants t =
let from_finite_args =
List.concat_map t.finite_args ~f:(function
| Some hint -> Hint.all_reentrants hint
| None -> [])
in
let from_repeated_arg =
match t.repeated_arg with
| Some (`Hint hint) -> Hint.all_reentrants hint
| _ -> []
in
from_finite_args @ from_repeated_arg
;;
end
module Parser_spec = struct
type 'reentrant t =
{ named_args : 'reentrant Named_arg.t list
; positional_args_hints : 'reentrant Positional_args_hints.t
}
let empty = { named_args = []; positional_args_hints = Positional_args_hints.empty }
let replace_reentrants_with_indices t acc =
let named_args, acc =
List.fold_left t.named_args ~init:([], acc) ~f:(fun (named_args, acc) named_arg ->
let named_arg, acc = Named_arg.replace_reentrants_with_indices named_arg acc in
named_arg :: named_args, acc)
in
let named_args = List.rev named_args in
let positional_args_hints, acc =
Positional_args_hints.replace_reentrants_with_indices t.positional_args_hints acc
in
{ named_args; positional_args_hints }, acc
;;
let all_reentrants t =
let from_named_args = List.concat_map t.named_args ~f:Named_arg.all_reentrants in
let from_positional_args_hints =
Positional_args_hints.all_reentrants t.positional_args_hints
in
from_named_args @ from_positional_args_hints
;;
let all_short_names_with_dashes_sorted { named_args; _ } =
List.concat_map named_args ~f:(fun { Named_arg.names; _ } ->
Nonempty_list.to_list names
|> List.filter_map ~f:(fun name ->
if Name.is_short name then Some (Name.to_string_with_dashes name) else None))
|> List.sort ~cmp:String.compare
;;
let all_long_names_with_dashes_sorted { named_args; _ } =
List.concat_map named_args ~f:(fun { Named_arg.names; _ } ->
Nonempty_list.to_list names
|> List.filter_map ~f:(fun name ->
if Name.is_long name then Some (Name.to_string_with_dashes name) else None))
|> List.sort ~cmp:String.compare
;;
let all_names_with_dashes_sorted { named_args; _ } =
List.concat_map named_args ~f:(fun { Named_arg.names; _ } ->
Nonempty_list.to_list names |> List.map ~f:Name.to_string_with_dashes)
|> List.sort ~cmp:String.compare
;;
end
type 'reentrant t =
{ parser_spec : 'reentrant Parser_spec.t
; subcommands : 'reentrant subcommand list
}
and 'reentrant subcommand =
{ name : string
; spec : 'reentrant t
}
let empty = { parser_spec = Parser_spec.empty; subcommands = [] }
let rec replace_reentrants_with_indices t acc =
let parser_spec, acc = Parser_spec.replace_reentrants_with_indices t.parser_spec acc in
let subcommands, acc =
List.fold_left
t.subcommands
~init:([], acc)
~f:(fun (subcommands, acc) { name; spec } ->
let spec, acc = replace_reentrants_with_indices spec acc in
{ name; spec } :: subcommands, acc)
in
let subcommands = List.rev subcommands in
{ parser_spec; subcommands }, acc
;;
let replace_reentrants_with_indices t = replace_reentrants_with_indices t 0 |> fst
let rec all_reentrants t =
let from_parser_spec = Parser_spec.all_reentrants t.parser_spec in
let from_subcommands =
List.concat_map t.subcommands ~f:(fun { spec; _ } -> all_reentrants spec)
in
from_parser_spec @ from_subcommands
;;