displayException.ml 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. open Globals
  2. open Ast
  3. open DisplayTypes
  4. open CompletionItem
  5. open Type
  6. open Genjson
  7. exception DisplayException of display_exception_kind
  8. let raise_module_symbols s = raise (DisplayException(ModuleSymbols s))
  9. let raise_metadata s = raise (DisplayException(Metadata s))
  10. let raise_signatures l isig iarg kind = raise (DisplayException(DisplaySignatures((l,isig,iarg,kind))))
  11. let raise_hover item expected p = raise (DisplayException(DisplayHover({hitem = item;hpos = p;hexpected = expected})))
  12. let raise_positions pl = raise (DisplayException(DisplayPositions pl))
  13. let raise_fields ckl cr subj = raise (DisplayException(DisplayFields({fitems = ckl;fkind = cr;fsubject = subj})))
  14. let raise_package sl = raise (DisplayException(DisplayPackage sl))
  15. (* global state *)
  16. let last_completion_result = ref (Array.make 0 (CompletionItem.make (ITModule ([],"")) None))
  17. let last_completion_pos = ref None
  18. let max_completion_items = ref 0
  19. let filter_somehow ctx items kind subj =
  20. let subject = match subj.s_name with
  21. | None -> ""
  22. | Some name-> ExtString.String.lowercase name
  23. in
  24. let subject_length = ExtString.String.length subject in
  25. let determine_cost s =
  26. let get_initial_cost o =
  27. if o = 0 then
  28. 0 (* Term starts with subject - perfect *)
  29. else begin
  30. (* Consider `.` as anchors and determine distance from closest one. Penalize starting distance by factor 2. *)
  31. try
  32. let last_anchor = ExtString.String.rindex_from s o '.' in
  33. (o - (last_anchor + 1)) * 2
  34. with Not_found ->
  35. o * 2
  36. end
  37. in
  38. let index_from o c =
  39. let rec loop o cost =
  40. let c' = s.[o] in
  41. if c' = c then
  42. o,cost
  43. else
  44. loop (o + 1) (cost + 3) (* Holes are bad, penalize by 3. *)
  45. in
  46. loop o 0
  47. in
  48. let rec loop i o cost =
  49. if i < subject_length then begin
  50. let o',new_cost = index_from o subject.[i] in
  51. loop (i + 1) o' (cost + new_cost)
  52. end else
  53. cost + (if o = ExtString.String.length s - 1 then 0 else 1) (* Slightly penalize for not-exact matches. *)
  54. in
  55. if subject_length = 0 then
  56. 0
  57. else try
  58. let o = ExtString.String.index s subject.[0] in
  59. loop 1 o (get_initial_cost o);
  60. with Not_found | Invalid_argument _ ->
  61. -1
  62. in
  63. let rec loop acc items index =
  64. match items with
  65. | item :: items ->
  66. let name = ExtString.String.lowercase (get_filter_name item) in
  67. let cost = determine_cost name in
  68. let acc = if cost >= 0 then
  69. (item,index,cost) :: acc
  70. else
  71. acc
  72. in
  73. loop acc items (index + 1)
  74. | [] ->
  75. acc
  76. in
  77. let acc = loop [] items 0 in
  78. let acc = if subject_length = 0 then
  79. List.rev acc
  80. else
  81. List.sort (fun (_,_,cost1) (_,_,cost2) ->
  82. compare cost1 cost2
  83. ) acc
  84. in
  85. let ret = DynArray.create () in
  86. let rec loop acc_types = match acc_types with
  87. | (item,index,_) :: acc_types when DynArray.length ret < !max_completion_items ->
  88. DynArray.add ret (CompletionItem.to_json ctx (Some index) item);
  89. loop acc_types
  90. | _ ->
  91. ()
  92. in
  93. loop acc;
  94. DynArray.to_list ret,DynArray.length ret
  95. let patch_completion_subject subj =
  96. let p = Parser.cut_pos_at_display subj.s_insert_pos in
  97. match subj.s_name with
  98. | Some name ->
  99. let delta = p.pmax - p.pmin in
  100. let name = if delta > 0 && delta < ExtString.String.length name then
  101. ExtString.String.sub name 0 delta
  102. else
  103. name
  104. in
  105. {subj with s_name = Some name;s_insert_pos = p}
  106. | None ->
  107. {subj with s_insert_pos = p}
  108. let fields_to_json ctx fields kind subj =
  109. last_completion_result := Array.of_list fields;
  110. let needs_filtering = !max_completion_items > 0 && Array.length !last_completion_result > !max_completion_items in
  111. (* let p_before = subj.s_insert_pos in *)
  112. let subj = patch_completion_subject subj in
  113. let ja,num_items = if needs_filtering then
  114. filter_somehow ctx fields kind subj
  115. else
  116. List.mapi (fun i item -> CompletionItem.to_json ctx (Some i) item) fields,Array.length !last_completion_result
  117. in
  118. let did_filter = num_items = !max_completion_items in
  119. last_completion_pos := if did_filter then Some subj.s_start_pos else None;
  120. let filter_string = (match subj.s_name with None -> "" | Some name -> name) in
  121. (* print_endline (Printf.sprintf "FIELDS OUTPUT:\n\tfilter_string: %s\n\t num items: %i\n\t start: %s\n\t position: %s\n\t before cut: %s"
  122. filter_string
  123. num_items
  124. (Printer.s_pos subj.s_start_pos)
  125. (Printer.s_pos subj.s_insert_pos)
  126. (Printer.s_pos p_before)
  127. ); *)
  128. let fl =
  129. ("items",jarray ja) ::
  130. ("isIncomplete",jbool did_filter) ::
  131. ("mode",CompletionResultKind.to_json ctx kind) ::
  132. ("filterString",jstring filter_string) ::
  133. ("replaceRange",generate_pos_as_range subj.s_insert_pos) ::
  134. []
  135. in
  136. jobject fl
  137. let arg_index signatures signature_index param_index =
  138. try
  139. let args,_ = fst (fst (List.nth signatures signature_index)) in
  140. let rec loop args index =
  141. match args with
  142. | [] -> param_index
  143. | [_,_,t] when index < param_index && ExtType.is_rest (follow t) -> index
  144. | arg :: _ when index = param_index -> param_index
  145. | _ :: args -> loop args (index + 1)
  146. in
  147. loop args 0
  148. with Invalid_argument _ ->
  149. param_index
  150. let to_json ctx de =
  151. match de with
  152. | ModuleSymbols _
  153. | Metadata _ -> die "" __LOC__
  154. | DisplaySignatures(sigs,isig,iarg,kind) ->
  155. (* We always want full info for signatures *)
  156. let ctx = Genjson.create_context GMFull in
  157. let fsig ((_,signature),doc) =
  158. let fl = CompletionType.generate_function' ctx signature in
  159. let fl = (match doc with None -> fl | Some d -> ("documentation",jstring (gen_doc_text d)) :: fl) in
  160. jobject fl
  161. in
  162. let sigkind = match kind with
  163. | SKCall -> 0
  164. | SKArrayAccess -> 1
  165. in
  166. jobject [
  167. "activeSignature",jint isig;
  168. "activeParameter",jint (arg_index sigs isig iarg);
  169. "signatures",jlist fsig sigs;
  170. "kind",jint sigkind;
  171. ]
  172. | DisplayHover hover ->
  173. let named_source_kind = function
  174. | WithType.FunctionArgument name -> (0, name)
  175. | WithType.StructureField name -> (1, name)
  176. | _ -> die "" __LOC__
  177. in
  178. let ctx = Genjson.create_context GMFull in
  179. let generate_name kind =
  180. let i,si = named_source_kind kind in
  181. jobject [
  182. "name",jstring si.si_name;
  183. "kind",jint i;
  184. "doc",(match si.si_doc with None -> jnull | Some s -> jstring s);
  185. ]
  186. in
  187. let expected = match hover.hexpected with
  188. | Some(WithType.WithType(t,src)) ->
  189. jobject (("type",generate_type ctx t)
  190. :: (match src with
  191. | None -> []
  192. | Some ImplicitReturn -> []
  193. | Some src -> [
  194. "name",generate_name src;
  195. ])
  196. )
  197. | Some(Value(Some ((FunctionArgument name | StructureField name) as src))) ->
  198. jobject [
  199. "name",generate_name src;
  200. ]
  201. | _ -> jnull
  202. in
  203. jobject [
  204. "documentation",jopt jstring (gen_doc_text_opt (CompletionItem.get_documentation hover.hitem));
  205. "range",generate_pos_as_range hover.hpos;
  206. "item",CompletionItem.to_json ctx None hover.hitem;
  207. "expected",expected;
  208. ]
  209. | DisplayPositions pl ->
  210. jarray (List.map generate_pos_as_location pl)
  211. | DisplayFields r ->
  212. fields_to_json ctx r.fitems r.fkind r.fsubject
  213. | DisplayPackage pack ->
  214. jarray (List.map jstring pack)
  215. | DisplayNoResult ->
  216. jnull