Add type annotations to `clientConnect.ml`
[hiphop-php.git] / hphp / hack / src / server / ffpAutocompleteKeywords.ml
blob3a53a684498f9fe5ec9ebd47a13e96b7237d2fc1
1 (**
2 * Copyright (c) 2017, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
8 *)
11 * FUTURE IMPROVEMENTS:
12 * - Order suggestions by how likely they are to be what the programmer
13 * wishes to do, not just what is valid
16 open FfpAutocompleteContextParser
17 open FfpAutocompleteContextParser.Container
18 open FfpAutocompleteContextParser.Predecessor
19 open FfpAutocompleteContextParser.ContextPredicates
20 open Core_kernel
22 (* Each keyword completion object has a list of keywords and a function that
23 takes a context and returns whether or not the list of keywords is valid
24 in that context. *)
25 type keyword_completion = {
26 keywords: string list;
27 is_valid_in_context: context -> bool;
30 let abstract_keyword = {
31 keywords = ["abstract"];
32 is_valid_in_context = begin fun context ->
33 (* Abstract class *)
34 is_top_level_statement_valid context
35 || (* Abstract method *)
36 is_class_body_declaration_valid context ||
37 is_trait_body_declaration_valid context
38 end;
41 let final_keyword = {
42 keywords = ["final"];
43 is_valid_in_context = begin fun context ->
44 (* Final class *)
45 (context.predecessor = TopLevelDeclaration ||
46 context.predecessor = KeywordAbstract)
48 is_top_level_statement_valid context
49 || (* Final method *)
50 is_class_body_declaration_valid context
52 is_trait_body_declaration_valid context
53 || (* Final after other modifiers *)
54 context.closest_parent_container = ClassBody &&
55 (context.predecessor = KeywordStatic ||
56 context.predecessor = VisibilityModifier)
57 end;
60 let implements_keyword = {
61 keywords = ["implements"];
62 is_valid_in_context = begin fun context ->
63 (* Class implements interface *)
64 (context.closest_parent_container = ClassHeader ||
65 context.closest_parent_container = ClassBody) &&
66 (context.predecessor = ClassName ||
67 context.predecessor = ExtendsList)
68 || (* "require implements" inside a trait *)
69 context.closest_parent_container = TraitBody &&
70 context.predecessor = KeywordRequire
71 end;
74 let extends_keyword = {
75 keywords = ["extends"];
76 is_valid_in_context = begin fun context ->
77 (context.closest_parent_container = InterfaceHeader ||
78 context.closest_parent_container = InterfaceBody ||
79 context.closest_parent_container = ClassHeader ||
80 context.closest_parent_container = ClassBody) &&
81 context.predecessor = ClassName
82 || (* Inside trait/interface body *)
83 (context.closest_parent_container = TraitBody ||
84 context.closest_parent_container = InterfaceBody) &&
85 context.predecessor = KeywordRequire
86 end;
89 let visibility_modifiers = {
90 keywords = ["public"; "protected"; "private"];
91 is_valid_in_context = begin fun context ->
92 is_class_body_declaration_valid context
94 is_trait_body_declaration_valid context
96 context.closest_parent_container = ClassBody &&
97 context.predecessor = KeywordFinal
98 end;
101 let interface_visibility_modifiers = {
102 keywords = ["public"];
103 is_valid_in_context = is_interface_body_declaration_valid
106 let static_keyword = {
107 keywords = ["static"];
108 is_valid_in_context = begin fun context ->
109 is_class_body_declaration_valid context ||
110 is_interface_body_declaration_valid context ||
111 is_trait_body_declaration_valid context
113 (context.closest_parent_container = ClassBody ||
114 context.closest_parent_container = InterfaceBody ||
115 context.closest_parent_container = TraitBody) &&
116 context.predecessor = VisibilityModifier
117 end;
120 let async_keyword = {
121 keywords = ["async"];
122 is_valid_in_context = begin fun context ->
123 (* Async method *)
124 is_class_body_declaration_valid context ||
125 is_trait_body_declaration_valid context
126 || (* Async method after modifiers *)
127 (context.closest_parent_container = ClassBody ||
128 context.closest_parent_container = TraitBody) &&
129 (context.predecessor = VisibilityModifier ||
130 context.predecessor = KeywordFinal ||
131 context.predecessor = KeywordStatic )
132 || (* Async top level function *)
133 is_top_level_statement_valid context
134 || (* Async lambda *)
135 is_expression_valid context
136 end;
139 let const_keyword = {
140 keywords = ["const"];
141 is_valid_in_context = begin fun context ->
142 is_class_body_declaration_valid context ||
143 is_interface_body_declaration_valid context
144 end;
147 let use_keyword = {
148 keywords = ["use"];
149 is_valid_in_context = begin fun context ->
150 (* use <trait> inside body *)
151 is_class_body_declaration_valid context
152 || (* use <namespace> at top level *)
153 is_top_level_statement_valid context
154 (* TODO: "use" for closures *)
155 end;
158 let function_keyword = {
159 keywords = ["function"];
160 is_valid_in_context = begin fun context ->
161 (* Class Method *)
162 (* "function" is not valid without a visibility modifier, but we still suggest it here since a
163 user may wish to write the function before adding the modifier. *)
164 is_class_body_declaration_valid context ||
165 is_interface_body_declaration_valid context ||
166 is_trait_body_declaration_valid context
167 || (* Class method, after modifiers *)
168 (context.closest_parent_container = ClassBody ||
169 context.closest_parent_container = InterfaceBody ||
170 context.closest_parent_container = TraitBody ||
171 context.closest_parent_container = FunctionHeader) &&
172 (context.predecessor = VisibilityModifier ||
173 context.predecessor = KeywordAsync ||
174 context.predecessor = KeywordStatic ||
175 context.predecessor = KeywordFinal)
176 || (* Top level function *)
177 is_top_level_statement_valid context
178 || (* Top level async function *)
179 context.closest_parent_container = TopLevel &&
180 context.predecessor = KeywordAsync
181 end;
184 let class_keyword = {
185 keywords = ["class"];
186 is_valid_in_context = begin fun context ->
187 is_top_level_statement_valid context
189 context.closest_parent_container = ClassHeader &&
190 (context.predecessor = KeywordAbstract ||
191 context.predecessor = KeywordFinal)
192 end;
195 let interface_keyword = {
196 keywords = ["interface"];
197 is_valid_in_context = begin fun context ->
198 is_top_level_statement_valid context
199 end;
202 let require_constraint_keyword = {
203 keywords = ["require"];
204 is_valid_in_context = begin fun context ->
205 (* Require inside trait body or interface body *)
206 (context.closest_parent_container = TraitBody ||
207 context.closest_parent_container = InterfaceBody) &&
208 (context.predecessor = TokenLeftBrace ||
209 context.predecessor = ClassBodyDeclaration)
210 end;
214 let declaration_keywords = {
215 keywords = ["enum"; "require"; "include"; "require_once"; "include_once";
216 "namespace"; "newtype"; "trait"; "type"];
217 is_valid_in_context = begin fun context ->
218 is_top_level_statement_valid context
219 end;
222 let void_keyword = {
223 keywords = ["void"];
224 is_valid_in_context = begin fun context ->
225 context.closest_parent_container = FunctionHeader &&
226 (context.predecessor = TokenColon ||
227 context.predecessor = TokenLessThan)
228 end;
231 let noreturn_keyword = {
232 keywords = ["noreturn"];
233 is_valid_in_context = begin fun context ->
234 (context.closest_parent_container = ClassBody ||
235 context.closest_parent_container = FunctionHeader) &&
236 context.predecessor = TokenColon
237 end;
240 let primitive_types = {
241 keywords = ["array"; "arraykey"; "bool"; "classname"; "darray"; "float"; "int"; "mixed"; "num";
242 "string"; "resource"; "varray"];
243 is_valid_in_context = is_type_valid
246 let this_type_keyword = {
247 keywords = ["this"; "?this"];
248 is_valid_in_context = begin fun context ->
249 context.closest_parent_container = FunctionHeader &&
250 context.predecessor = TokenColon &&
251 context.inside_class_body
252 end;
255 let loop_body_keywords = {
256 keywords = ["continue"; "break"];
257 is_valid_in_context = begin fun context ->
258 context.inside_loop_body &&
259 is_at_beginning_of_new_statement context
260 end;
263 let switch_body_keywords = {
264 keywords = ["case"; "default"; "break"];
265 is_valid_in_context = begin fun context ->
266 context.inside_switch_body &&
267 is_at_beginning_of_new_statement context
268 end;
271 let async_func_body_keywords = {
272 keywords = ["await"];
273 is_valid_in_context = begin fun context ->
274 context.inside_async_function &&
275 (context.closest_parent_container = CompoundStatement ||
276 context.closest_parent_container = AssignmentExpression)
277 end;
281 * TODO: Figure out what exactly a postfix expression is and when one is valid
282 * or more importantly, invalid.
284 let postfix_expressions = {
285 keywords = ["clone"; "new"];
286 is_valid_in_context = begin fun context ->
287 is_expression_valid context
288 end;
291 let general_statements = {
292 keywords = ["if"; "do"; "while"; "for"; "foreach"; "try"; "return"; "throw";
293 "switch"; "yield"; "echo"];
294 is_valid_in_context = begin fun context ->
295 is_at_beginning_of_new_statement context
296 end;
299 let if_after_else = {
300 keywords = ["if"];
301 is_valid_in_context = begin fun context ->
302 context.predecessor = KeywordElse &&
303 (context.closest_parent_container = CompoundStatement ||
304 context.closest_parent_container = IfStatement)
305 end;
308 let if_trailing_keywords = {
309 keywords = ["else"; "else if"];
310 is_valid_in_context = begin fun context ->
311 context.predecessor = IfWithoutElse &&
312 context.closest_parent_container = CompoundStatement
313 end;
316 let try_trailing_keywords = {
317 keywords = ["catch"; "finally"];
318 is_valid_in_context = begin fun context ->
319 context.predecessor = TryWithoutFinally &&
320 context.closest_parent_container = CompoundStatement
321 end;
324 let primary_expressions = {
325 keywords = ["tuple"; "shape"; "null"];
326 is_valid_in_context = begin fun context ->
327 is_expression_valid context
328 end;
331 let scope_resolution_qualifiers = {
332 keywords = ["self"; "parent"; "static"];
333 is_valid_in_context = begin fun context ->
334 is_expression_valid context
335 end;
338 let keyword_matches: keyword_completion list = [
339 abstract_keyword;
340 async_keyword;
341 async_func_body_keywords;
342 class_keyword;
343 const_keyword;
344 declaration_keywords;
345 extends_keyword;
346 final_keyword;
347 function_keyword;
348 general_statements;
349 if_after_else;
350 if_trailing_keywords;
351 implements_keyword;
352 interface_keyword;
353 interface_visibility_modifiers;
354 loop_body_keywords;
355 noreturn_keyword;
356 postfix_expressions;
357 primary_expressions;
358 primitive_types;
359 require_constraint_keyword;
360 scope_resolution_qualifiers;
361 static_keyword;
362 switch_body_keywords;
363 this_type_keyword;
364 try_trailing_keywords;
365 use_keyword;
366 visibility_modifiers;
367 void_keyword;
370 let autocomplete_keyword (context:context) : string list =
371 let check_keyword_match { keywords; is_valid_in_context } =
372 Option.some_if (is_valid_in_context context) keywords
374 keyword_matches
375 |> List.filter_map ~f:check_keyword_match
376 |> List.concat