displayOutput.ml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. open Globals
  2. open Ast
  3. open Common
  4. open Filename
  5. open Timer
  6. open DisplayTypes.DisplayMode
  7. open DisplayTypes.CompletionResultKind
  8. open CompletionItem
  9. open CompletionClassField
  10. open CompletionEnumField
  11. open ClassFieldOrigin
  12. open DisplayException
  13. open Type
  14. open Display
  15. open DisplayTypes
  16. open CompletionModuleType
  17. open Typecore
  18. open Genjson
  19. open CompilationContext
  20. open DisplayProcessingGlobals
  21. (* Old XML stuff *)
  22. let htmlescape s =
  23. let s = String.concat "&" (ExtString.String.nsplit s "&") in
  24. let s = String.concat "&lt;" (ExtString.String.nsplit s "<") in
  25. let s = String.concat "&gt;" (ExtString.String.nsplit s ">") in
  26. let s = String.concat "&quot;" (ExtString.String.nsplit s "\"") in
  27. s
  28. let get_timer_fields start_time =
  29. let tot = ref 0. in
  30. Hashtbl.iter (fun _ t -> tot := !tot +. t.total) Timer.htimers;
  31. let fields = [("@TOTAL", Printf.sprintf "%.3fs" (get_time() -. start_time))] in
  32. if !tot > 0. then
  33. Hashtbl.fold (fun _ t acc ->
  34. ((String.concat "." t.id),(Printf.sprintf "%.3fs (%.0f%%)" t.total (t.total *. 100. /. !tot))) :: acc
  35. ) Timer.htimers fields
  36. else
  37. fields
  38. let print_keywords () =
  39. let b = Buffer.create 0 in
  40. Buffer.add_string b "<list>\n";
  41. Hashtbl.iter (fun k _ ->
  42. Buffer.add_string b (Printf.sprintf "<i n=\"%s\"></i>\n" k)
  43. ) Lexer.keywords;
  44. Buffer.add_string b "</list>\n";
  45. Buffer.contents b
  46. let print_fields fields =
  47. let b = Buffer.create 0 in
  48. Buffer.add_string b "<list>\n";
  49. let convert k = match k.ci_kind with
  50. | ITClassField({field = cf}) | ITEnumAbstractField(_,{field = cf}) ->
  51. let kind = match cf.cf_kind with
  52. | Method _ -> "method"
  53. | Var _ -> "var"
  54. in
  55. kind,cf.cf_name,s_type (print_context()) cf.cf_type,cf.cf_doc
  56. | ITEnumField ef ->
  57. let ef = ef.efield in
  58. let kind = match follow ef.ef_type with
  59. | TFun _ -> "method"
  60. | _ -> "var"
  61. in
  62. kind,ef.ef_name,s_type (print_context()) ef.ef_type,ef.ef_doc
  63. | ITType(cm,_) ->
  64. let path = CompletionItem.CompletionModuleType.get_path cm in
  65. "type",snd path,s_type_path path,None
  66. | ITPackage(path,_) -> "package",snd path,"",None
  67. | ITModule path -> "type",snd path,"",None
  68. | ITMetadata meta ->
  69. let s,(doc,_),_ = Meta.get_info meta in
  70. "metadata","@" ^ s,"",doc_from_string doc
  71. | ITTimer(name,value) -> "timer",name,"",doc_from_string value
  72. | ITLiteral s ->
  73. let t = match k.ci_type with None -> t_dynamic | Some (t,_) -> t in
  74. "literal",s,s_type (print_context()) t,None
  75. | ITLocal v -> "local",v.v_name,s_type (print_context()) v.v_type,None
  76. | ITKeyword kwd -> "keyword",Ast.s_keyword kwd,"",None
  77. | ITExpression _ | ITAnonymous _ | ITTypeParameter _ | ITDefine _ -> die "" __LOC__
  78. in
  79. let fields = List.sort (fun k1 k2 -> compare (legacy_sort k1) (legacy_sort k2)) fields in
  80. let fields = List.map convert fields in
  81. List.iter (fun(k,n,t,d) ->
  82. let d = match d with None -> "" | Some d -> gen_doc_text d in
  83. Buffer.add_string b (Printf.sprintf "<i n=\"%s\" k=\"%s\"><t>%s</t><d>%s</d></i>\n" n k (htmlescape t) (htmlescape d))
  84. ) fields;
  85. Buffer.add_string b "</list>\n";
  86. Buffer.contents b
  87. let maybe_print_doc d_opt =
  88. Option.map_default (fun d -> Printf.sprintf " d=\"%s\"" (htmlescape (gen_doc_text d))) "" d_opt
  89. let print_toplevel il =
  90. let b = Buffer.create 0 in
  91. Buffer.add_string b "<il>\n";
  92. let s_type t = htmlescape (s_type (print_context()) t) in
  93. let s_doc d = maybe_print_doc d in
  94. let identifiers = Hashtbl.create 0 in
  95. let check_ident s =
  96. if Hashtbl.mem identifiers s then false
  97. else begin
  98. Hashtbl.add identifiers s true;
  99. true
  100. end
  101. in
  102. List.iter (fun id -> match id.ci_kind with
  103. | ITLocal v ->
  104. if check_ident v.v_name then Buffer.add_string b (Printf.sprintf "<i k=\"local\" t=\"%s\">%s</i>\n" (s_type v.v_type) v.v_name);
  105. | ITClassField({field = cf;scope = CFSMember}) ->
  106. if check_ident cf.cf_name then Buffer.add_string b (Printf.sprintf "<i k=\"member\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
  107. | ITClassField({field = cf;scope = (CFSStatic | CFSConstructor)}) ->
  108. if check_ident cf.cf_name then Buffer.add_string b (Printf.sprintf "<i k=\"static\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
  109. | ITEnumField ef ->
  110. let ef = ef.efield in
  111. if check_ident ef.ef_name then Buffer.add_string b (Printf.sprintf "<i k=\"enum\" t=\"%s\"%s>%s</i>\n" (s_type ef.ef_type) (s_doc ef.ef_doc) ef.ef_name);
  112. | ITEnumAbstractField(a,cf) ->
  113. let cf = cf.field in
  114. if check_ident cf.cf_name then Buffer.add_string b (Printf.sprintf "<i k=\"enumabstract\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
  115. | ITType(cm,_) ->
  116. let path = CompletionItem.CompletionModuleType.get_path cm in
  117. Buffer.add_string b (Printf.sprintf "<i k=\"type\" p=\"%s\"%s>%s</i>\n" (s_type_path path) ("") cm.name);
  118. | ITPackage(path,_) ->
  119. Buffer.add_string b (Printf.sprintf "<i k=\"package\">%s</i>\n" (snd path))
  120. | ITLiteral s ->
  121. Buffer.add_string b (Printf.sprintf "<i k=\"literal\">%s</i>\n" s)
  122. | ITTimer(s,_) ->
  123. Buffer.add_string b (Printf.sprintf "<i k=\"timer\">%s</i>\n" s)
  124. | ITTypeParameter c ->
  125. Buffer.add_string b (Printf.sprintf "<i k=\"type\" p=\"%s\"%s>%s</i>\n" (s_type_path c.cl_path) ("") (snd c.cl_path));
  126. | ITMetadata _ | ITModule _ | ITKeyword _ | ITAnonymous _ | ITExpression _ | ITDefine _ ->
  127. (* compat: don't add *)
  128. ()
  129. ) il;
  130. Buffer.add_string b "</il>";
  131. Buffer.contents b
  132. let print_type t p doc =
  133. let b = Buffer.create 0 in
  134. if p = null_pos then
  135. Buffer.add_string b "<type"
  136. else begin
  137. let error_printer file line = Printf.sprintf "%s:%d:" (Path.get_full_path file) line in
  138. let epos = Lexer.get_error_pos error_printer p in
  139. Buffer.add_string b ("<type p=\"" ^ (htmlescape epos) ^ "\"")
  140. end;
  141. Buffer.add_string b (maybe_print_doc doc);
  142. Buffer.add_string b ">\n";
  143. Buffer.add_string b (htmlescape (s_type (print_context()) t));
  144. Buffer.add_string b "\n</type>\n";
  145. Buffer.contents b
  146. let print_signatures tl =
  147. let b = Buffer.create 0 in
  148. List.iter (fun (((args,ret),_),doc) ->
  149. Buffer.add_string b "<type";
  150. Option.may (fun d -> Buffer.add_string b (Printf.sprintf " d=\"%s\"" (htmlescape (gen_doc_text d)))) doc;
  151. Buffer.add_string b ">\n";
  152. Buffer.add_string b (htmlescape (s_type (print_context()) (TFun(args,ret))));
  153. Buffer.add_string b "\n</type>\n";
  154. ) tl;
  155. Buffer.contents b
  156. let print_positions pl =
  157. let b = Buffer.create 0 in
  158. let error_printer file line = Printf.sprintf "%s:%d:" (Path.get_real_path file) line in
  159. Buffer.add_string b "<list>\n";
  160. List.iter (fun p ->
  161. let epos = Lexer.get_error_pos error_printer p in
  162. Buffer.add_string b "<pos>";
  163. Buffer.add_string b epos;
  164. Buffer.add_string b "</pos>\n";
  165. ) pl;
  166. Buffer.add_string b "</list>";
  167. Buffer.contents b
  168. (* New JSON stuff *)
  169. open Json
  170. let print_signature tl display_arg =
  171. let st = s_type (print_context()) in
  172. let s_arg (n,o,t) = Printf.sprintf "%s%s:%s" (if o then "?" else "") n (st t) in
  173. let s_fun args ret = Printf.sprintf "(%s):%s" (String.concat ", " (List.map s_arg args)) (st ret) in
  174. let siginf = List.map (fun (((args,ret),_),doc) ->
  175. let label = s_fun args ret in
  176. let parameters =
  177. List.map (fun arg ->
  178. let label = s_arg arg in
  179. JObject [
  180. "label",JString label
  181. ]
  182. ) args
  183. in
  184. let js = [
  185. "label",JString label;
  186. "parameters",JArray parameters;
  187. ] in
  188. JObject (match doc with None -> js | Some d -> ("documentation",JString (gen_doc_text d)) :: js)
  189. ) tl in
  190. let jo = JObject [
  191. "signatures",JArray siginf;
  192. "activeParameter",JInt (arg_index tl 0 display_arg);
  193. "activeSignature",JInt 0;
  194. ] in
  195. string_of_json jo
  196. (* Mode processing *)
  197. let find_doc t =
  198. let doc = match follow t with
  199. | TAnon an ->
  200. begin match !(an.a_status) with
  201. | Statics c -> c.cl_doc
  202. | EnumStatics en -> en.e_doc
  203. | AbstractStatics a -> a.a_doc
  204. | _ -> None
  205. end
  206. | _ ->
  207. None
  208. in
  209. doc
  210. let handle_syntax_completion com kind subj =
  211. let open Parser in
  212. let l,kind = match kind with
  213. | SCClassRelation ->
  214. [Extends;Implements],CRTypeRelation
  215. | SCInterfaceRelation ->
  216. [Extends],CRTypeRelation
  217. | SCComment ->
  218. [],CRTypeRelation
  219. | SCTypeDecl mode ->
  220. let in_import_hx = Filename.basename subj.s_insert_pos.pfile = "import.hx" in
  221. let l = if in_import_hx then [] else [Private;Extern;Class;Interface;Enum;Abstract;Typedef;Final] in
  222. let l = match mode with
  223. | TCBeforePackage -> Package :: Import :: Using :: l
  224. | TCAfterImport -> Import :: Using :: l
  225. | TCAfterType -> l
  226. in
  227. l,CRTypeDecl
  228. | SCAfterTypeFlag flags ->
  229. let l = [Class;Interface] in
  230. let l = if List.mem DPrivate flags then l else Private :: l in
  231. let l = if List.mem DExtern flags then l else Extern :: l in
  232. let l = if List.mem DFinal flags then l else
  233. Final :: Enum :: Abstract :: Typedef :: l
  234. in
  235. l,CRTypeDecl
  236. in
  237. match l with
  238. | [] ->
  239. ()
  240. | _ ->
  241. let l = List.map make_ci_keyword l in
  242. match com.Common.json_out with
  243. | None ->
  244. let b = Buffer.create 0 in
  245. Buffer.add_string b "<il>\n";
  246. List.iter (fun item -> match item.ci_kind with
  247. | ITKeyword kwd -> Buffer.add_string b (Printf.sprintf "<i k=\"keyword\">%s</i>" (s_keyword kwd));
  248. | _ -> die "" __LOC__
  249. ) l;
  250. Buffer.add_string b "</il>";
  251. let s = Buffer.contents b in
  252. raise (Completion s)
  253. | Some api ->
  254. let ctx = Genjson.create_context ~jsonrpc:api.jsonrpc GMFull in
  255. api.send_result(fields_to_json ctx l kind subj)
  256. let handle_display_exception_old ctx dex = match dex with
  257. | DisplayPackage pack ->
  258. DisplayPosition.display_position#reset;
  259. raise (Completion (String.concat "." pack))
  260. | DisplayFields r ->
  261. DisplayPosition.display_position#reset;
  262. let fields = if !Timer.measure_times then begin
  263. Timer.close_times();
  264. (List.map (fun (name,value) ->
  265. CompletionItem.make_ci_timer ("@TIME " ^ name) value
  266. ) (get_timer_fields !Helper.start_time)) @ r.fitems
  267. end else
  268. r.fitems
  269. in
  270. let s = match r.fkind with
  271. | CRToplevel _
  272. | CRTypeHint
  273. | CRExtends
  274. | CRImplements
  275. | CRStructExtension _
  276. | CRImport
  277. | CRUsing
  278. | CRNew
  279. | CRPattern _
  280. | CRTypeRelation
  281. | CRTypeDecl ->
  282. print_toplevel fields
  283. | CRField _
  284. | CRStructureField
  285. | CRMetadata
  286. | CROverride ->
  287. print_fields fields
  288. in
  289. raise (Completion s)
  290. | DisplayHover ({hitem = {CompletionItem.ci_type = Some (t,_)}} as hover) ->
  291. DisplayPosition.display_position#reset;
  292. let doc = CompletionItem.get_documentation hover.hitem in
  293. raise (Completion (print_type t hover.hpos doc))
  294. | DisplaySignatures (signatures,_,display_arg,_) ->
  295. DisplayPosition.display_position#reset;
  296. if ctx.com.display.dms_kind = DMSignature then
  297. raise (Completion (print_signature signatures display_arg))
  298. else
  299. raise (Completion (print_signatures signatures))
  300. | DisplayPositions pl ->
  301. DisplayPosition.display_position#reset;
  302. raise (Completion (print_positions pl))
  303. | ModuleSymbols s | Metadata s ->
  304. DisplayPosition.display_position#reset;
  305. raise (Completion s)
  306. | DisplayHover _ | DisplayNoResult ->
  307. raise (Completion "")
  308. let handle_display_exception_json ctx dex api =
  309. match dex with
  310. | DisplayHover _ | DisplayPositions _ | DisplayFields _ | DisplayPackage _ | DisplaySignatures _ ->
  311. DisplayPosition.display_position#reset;
  312. let ctx = DisplayJson.create_json_context api.jsonrpc (match dex with DisplayFields _ -> true | _ -> false) in
  313. api.send_result (DisplayException.to_json ctx dex)
  314. | DisplayNoResult ->
  315. api.send_result JNull
  316. | _ ->
  317. handle_display_exception_old ctx dex
  318. let handle_display_exception ctx dex = match ctx.com.json_out with
  319. | Some api ->
  320. handle_display_exception_json ctx dex api
  321. | None ->
  322. handle_display_exception_old ctx dex
  323. let handle_type_path_exception ctx p c is_import pos =
  324. let open DisplayTypes.CompletionResultKind in
  325. let com = ctx.com in
  326. let fields =
  327. try begin match c with
  328. | None ->
  329. DisplayPath.TypePathHandler.complete_type_path com p
  330. | Some (c,cur_package) ->
  331. let ctx = Typer.create com in
  332. DisplayPath.TypePathHandler.complete_type_path_inner ctx p c cur_package is_import
  333. end with Common.Abort(msg,p) ->
  334. error ctx msg p;
  335. None
  336. in
  337. begin match ctx.com.json_out,fields with
  338. | None,None ->
  339. ()
  340. | None,Some fields ->
  341. raise (Completion (print_fields fields))
  342. | Some api,None when is_legacy_completion com ->
  343. api.send_result JNull
  344. | Some api,fields ->
  345. let fields = Option.default [] fields in
  346. let ctx = DisplayJson.create_json_context api.jsonrpc false in
  347. let path = match List.rev p with
  348. | name :: pack -> List.rev pack,name
  349. | [] -> [],""
  350. in
  351. let kind = CRField ((CompletionItem.make_ci_module path,pos,None,None)) in
  352. api.send_result (DisplayException.fields_to_json ctx fields kind (DisplayTypes.make_subject None pos));
  353. end
  354. let emit_diagnostics com =
  355. let dctx = Diagnostics.run com in
  356. let s = Json.string_of_json (DiagnosticsPrinter.json_of_diagnostics com dctx) in
  357. DisplayPosition.display_position#reset;
  358. raise (Completion s)
  359. let emit_statistics tctx =
  360. let stats = Statistics.collect_statistics tctx [SFFile (DisplayPosition.display_position#get).pfile] true in
  361. let s = Statistics.Printer.print_statistics stats in
  362. raise (Completion s)