2 * Copyright (c) 2017, Facebook, Inc.
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the "hack" directory of this source tree.
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
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
;
27 res_name
= keyword_name
;
28 res_kind
= Keyword_kind
;
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
45 (tcopt
:TypecheckerOptions.t
)
47 (pos
:File_content.position
)
49 ~
(filter_by_token
:bool)
50 ~
(env
: SearchUtils.local_tracking_env
): result
=
51 let open File_content
in
52 (* The part of the line from the far left end to the point where the caret is. *)
53 let new_file_content = handle_empty_autocomplete pos file_content
in
54 let dummy_path = Relative_path.(create Dummy
"<autocomplete>") in
55 let source_text = SourceText.make
dummy_path new_file_content in
56 let offset = SourceText.position_to_offset
source_text (pos
.line
, pos
.column
) in
57 let syntax_tree = SyntaxTree.make
source_text in
58 let positioned_tree = SyntaxTree.root
syntax_tree in
60 let syntax = List.hd_exn
(PositionedSyntax.parentage
positioned_tree offset) in
61 let (start_line
, start_col
) =
62 SourceText.offset_to_position
source_text (PositionedSyntax.start_offset
syntax)
64 let (end_line
, end_col
) =
65 SourceText.offset_to_position
source_text (PositionedSyntax.end_offset
syntax)
69 Ide_api_types.line
= start_line
;
73 Ide_api_types.line
= end_line
;
79 let (context
, stub
) = FfpAutocompleteContextParser.get_context_and_stub
positioned_tree offset in
80 (* If we are running a test, filter the keywords and local variables based on
81 the token we are completing. *)
82 let stub = if file_content
<> new_file_content then
83 String_utils.rstrip
stub empty_autocomplete_token
87 let filter_results res
= List.filter res ~f
:begin fun res
->
89 then String_utils.string_starts_with res
.res_name
stub
92 (* Delegate to each type of completion to determine whether or not that
93 type is valid in the current context *)
94 let keyword_completions =
95 FfpAutocompleteKeywords.autocomplete_keyword context
96 |> List.map ~f
:(make_keyword_completion replace_pos)
98 let type_based_completions =
99 FfpAutocompleteTypeCheck.run ~context ~file_content ~
stub ~pos ~tcopt ~basic_only ~env
101 let global_completions =
102 FfpAutocompleteGlobals.get_globals context
stub positioned_tree replace_pos
104 [keyword_completions; type_based_completions; global_completions]
105 |> List.concat_no_order
107 |> List.sort ~compare
:(fun a b
-> compare a
.res_name b
.res_name
)
108 |> List.remove_consecutive_duplicates ~equal
:(fun a b
-> a
.res_name
= b
.res_name
)