Unified symbol-to-docblock server command
[hiphop-php.git] / hphp / hack / src / server / serverHover.ml
blob4d37ebc2f8813e099ff4ce520d4c101900dcbd8d
1 (**
2 * Copyright (c) 2015, 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 open Core_kernel
11 open HoverService
13 let symbols_at (file, line, char) tcopt =
14 let contents = match file with
15 | ServerCommandTypes.FileName file_name ->
16 let relative_path = Relative_path.(create Root file_name) in
17 File_provider.get_contents relative_path
18 | ServerCommandTypes.FileContent content -> Some content
20 match contents with
21 | None -> []
22 | Some contents -> ServerIdentifyFunction.go contents line char tcopt
24 let type_at (file, line, char) tcopt naming_table =
25 let tcopt = {
26 tcopt with
27 GlobalOptions.tco_dynamic_view = ServerDynamicView.dynamic_view_on ();
28 } in
29 let _, tast = ServerIdeUtils.check_file_input tcopt naming_table file in
30 let env_ty_opt = ServerInferType.type_at_pos tast line char in
31 Option.map env_ty_opt ~f:(fun (env, ty) -> env, Tast_expand.expand_ty env ty)
33 (** When we get a Class occurrence and a Method occurrence, that means that the
34 user is hovering over an invocation of the constructor, and would therefore only
35 want to see information about the constructor, rather than getting both the
36 class and constructor back in the hover. *)
37 let filter_class_and_constructor results =
38 let result_is_constructor result =
39 SymbolOccurrence.is_constructor (fst result) in
40 let result_is_class result =
41 SymbolOccurrence.is_class (fst result) in
42 let has_class = List.exists results ~f:result_is_class in
43 let has_constructor = List.exists results ~f:result_is_constructor in
44 if has_class && has_constructor
45 then List.filter results ~f:result_is_constructor
46 else results
48 let make_hover_doc_block ~basic_only file occurrence def_opt =
49 match def_opt with
50 | Some def ->
51 let base_class_name = SymbolOccurrence.enclosing_class occurrence in
52 ServerDocblockAt.go_def ~def ~base_class_name ~file ~basic_only
53 |> Option.to_list
54 | None -> []
56 let make_hover_return_type env_and_ty occurrence =
57 let open SymbolOccurrence in
58 let open Typing_defs in
59 match occurrence, env_and_ty with
60 | { type_ = Function | Method _; _ }, Some (env, (_, Tfun ft)) ->
61 [Printf.sprintf "Return type: `%s`" (Tast_env.print_ty env ft.ft_ret)]
62 | _ -> []
64 let make_hover_full_name env_and_ty occurrence def_opt =
65 let open SymbolOccurrence in
66 let open Typing_defs in
67 match occurrence, env_and_ty with
68 | { type_ = Method _; _ }, _
69 | { type_ = Property _ | ClassConst _; _ }, Some (_, (_, Tfun _)) ->
70 let name = match def_opt with
71 | Some def -> def.SymbolDefinition.full_name
72 | None -> occurrence.name
74 [Printf.sprintf "Full name: `%s`" (Utils.strip_ns name)]
75 | _ -> []
77 let make_hover_info env_and_ty file (occurrence, def_opt) ~basic_only =
78 let open SymbolOccurrence in
79 let open Typing_defs in
80 let snippet = match occurrence, env_and_ty with
81 | { name; _ }, None -> Utils.strip_ns name
82 | { type_ = Method (classname, name); _ }, Some (env, ty)
83 when name = Naming_special_names.Members.__construct ->
84 let snippet_opt =
85 let open Option.Monad_infix in
86 Decl_provider.get_class classname
87 >>= fun c -> fst (Decl_provider.Class.construct c)
88 >>| fun elt ->
89 let ty = Lazy.force_val elt.ce_type in
90 Tast_env.print_ty_with_identity env ty occurrence def_opt
92 begin match snippet_opt with
93 | Some s -> s
94 | None -> Tast_env.print_ty_with_identity env ty occurrence def_opt
95 end
96 | occurrence, Some (env, ty) -> Tast_env.print_ty_with_identity env ty occurrence def_opt
98 let addendum = List.concat [
99 make_hover_doc_block file occurrence def_opt ~basic_only;
100 make_hover_return_type env_and_ty occurrence;
101 make_hover_full_name env_and_ty occurrence def_opt;
104 HoverService.{ snippet; addendum; pos = Some occurrence.SymbolOccurrence.pos }
106 let go env (file, line, char) ~basic_only =
107 let position = (file, line, char) in
108 let ServerEnv.{ tcopt; naming_table; _ } = env in
109 let identities = symbols_at position tcopt in
110 let env_and_ty = type_at position tcopt naming_table in
111 (* There are legitimate cases where we expect to have no identities returned,
112 so just format the type. *)
113 match identities with
114 | [] ->
115 begin match env_and_ty with
116 | Some (env, ty) ->
117 [{ snippet = Tast_env.print_ty env ty; addendum = []; pos = None }]
118 | None -> []
120 | identities ->
121 identities
122 |> filter_class_and_constructor
123 |> List.map ~f:(make_hover_info env_and_ty file ~basic_only)
124 |> List.remove_consecutive_duplicates ~equal:(=)