Add type annotations to `clientConnect.ml`
[hiphop-php.git] / hphp / hack / src / server / ffpAutocompleteService.ml
blobdda3e862505097735c731a39f97341dfa0694281
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 *)
10 module PositionedSyntax = Full_fidelity_positioned_syntax
11 module SourceText = Full_fidelity_source_text
12 module SyntaxKind = Full_fidelity_syntax_kind
13 module SyntaxTree = Full_fidelity_syntax_tree.WithSyntax(PositionedSyntax)
14 module TokenKind = Full_fidelity_token_kind
16 open Core_kernel
17 open AutocompleteTypes
19 let empty_autocomplete_token = "PLACEHOLDER"
21 let make_keyword_completion (replace_pos:Ide_api_types.range) (keyword_name:string) =
23 res_pos = Pos.none |> Pos.to_absolute;
24 res_replace_pos = replace_pos;
25 res_base_class = None;
26 res_ty = "keyword";
27 res_name = keyword_name;
28 res_kind = Keyword_kind;
29 func_details = None;
32 let handle_empty_autocomplete (pos: File_content.position) file_content =
33 let open File_content in
34 let offset = File_content.get_offset file_content pos in
35 let prev_char = File_content.get_char file_content (offset-1) in
36 let next_char = File_content.get_char file_content offset in
37 let is_whitespace = function ' ' | '\n' | '\r' | '\t' -> true | _ -> false in
38 if is_whitespace prev_char && is_whitespace next_char then
39 let edits = [{range = Some {st = pos; ed = pos}; text = empty_autocomplete_token}] in
40 File_content.edit_file_unsafe file_content edits
41 else
42 file_content
44 let auto_complete
45 (tcopt:TypecheckerOptions.t)
46 (file_content:string)
47 (pos:File_content.position)
48 ~(basic_only:bool)
49 ~(filter_by_token:bool) : result =
50 let open File_content in
51 (* The part of the line from the far left end to the point where the caret is. *)
52 let new_file_content = handle_empty_autocomplete pos file_content in
53 let dummy_path = Relative_path.(create Dummy "<autocomplete>") in
54 let source_text = SourceText.make dummy_path new_file_content in
55 let offset = SourceText.position_to_offset source_text (pos.line, pos.column) in
56 let syntax_tree = SyntaxTree.make source_text in
57 let positioned_tree = SyntaxTree.root syntax_tree in
58 let replace_pos =
59 let syntax = List.hd_exn (PositionedSyntax.parentage positioned_tree offset) in
60 let (start_line, start_col) =
61 SourceText.offset_to_position source_text (PositionedSyntax.start_offset syntax)
63 let (end_line, end_col) =
64 SourceText.offset_to_position source_text (PositionedSyntax.end_offset syntax)
67 Ide_api_types.st = {
68 Ide_api_types.line = start_line;
69 column = start_col;
71 ed = {
72 Ide_api_types.line = end_line;
73 column = end_col;
78 let (context, stub) = FfpAutocompleteContextParser.get_context_and_stub positioned_tree offset in
79 (* If we are running a test, filter the keywords and local variables based on
80 the token we are completing. *)
81 let stub = if file_content <> new_file_content then
82 String_utils.rstrip stub empty_autocomplete_token
83 else
84 stub
86 let filter_results res = List.filter res ~f:begin fun res ->
87 if filter_by_token
88 then String_utils.string_starts_with res.res_name stub
89 else true
90 end in
91 (* Delegate to each type of completion to determine whether or not that
92 type is valid in the current context *)
93 let keyword_completions =
94 FfpAutocompleteKeywords.autocomplete_keyword context
95 |> List.map ~f:(make_keyword_completion replace_pos)
97 let type_based_completions =
98 FfpAutocompleteTypeCheck.run ~context ~file_content ~stub ~pos ~tcopt ~basic_only
100 let global_completions =
101 FfpAutocompleteGlobals.get_globals context stub positioned_tree replace_pos
103 [keyword_completions; type_based_completions; global_completions]
104 |> List.concat_no_order
105 |> filter_results
106 |> List.sort ~compare:(fun a b -> compare a.res_name b.res_name)
107 |> List.remove_consecutive_duplicates ~equal:(fun a b -> a.res_name = b.res_name)