···11+open Merlin_utils
22+open Brr
33+open Std
44+open Merlin_kernel
55+module Location = Ocaml_parsing.Location
66+77+module Make () : sig end = struct
88+99+(* Load the CMIs into the pseudo file-system *)
1010+(* This add roughly 3mo to the final script. These could be loaded dynamically
1111+after the worker *)
1212+let () = List.iter ~f:(fun (path, content) ->
1313+ let name = Filename.(concat "/static/stdlib" (basename path)) in
1414+ Js_of_ocaml.Sys_js.create_file ~name ~content
1515+ ) Static_files.stdlib_cmis
1616+1717+let config =
1818+ let initial = Mconfig.initial in
1919+ { initial with
2020+ merlin = { initial.merlin with
2121+ stdlib = Some "/static/stdlib" }}
2222+2323+let make_pipeline source =
2424+ Mpipeline.make config source
2525+2626+let dispatch source query =
2727+ let pipeline = make_pipeline source in
2828+ Mpipeline.with_pipeline pipeline @@ fun () -> (
2929+ Query_commands.dispatch pipeline query
3030+ )
3131+3232+3333+module Completion = struct
3434+ (* Prefixing code from ocaml-lsp-server *)
3535+ let rfindi =
3636+ let rec loop s ~f i =
3737+ if i < 0 then
3838+ None
3939+ else if f (String.unsafe_get s i) then
4040+ Some i
4141+ else
4242+ loop s ~f (i - 1)
4343+ in
4444+ fun ?from s ~f ->
4545+ let from =
4646+ let len = String.length s in
4747+ match from with
4848+ | None -> len - 1
4949+ | Some i ->
5050+ if i > len - 1 then
5151+ raise @@ Invalid_argument "rfindi: invalid from"
5252+ else
5353+ i
5454+ in
5555+ loop s ~f from
5656+ let lsplit2 s ~on =
5757+ match String.index_opt s on with
5858+ | None -> None
5959+ | Some i ->
6060+ let open String in
6161+ Some (sub s ~pos:0 ~len:i, sub s ~pos:(i + 1) ~len:(length s - i - 1))
6262+6363+ (** @see <https://ocaml.org/manual/lex.html> reference *)
6464+ let prefix_of_position ?(short_path = false) source position =
6565+ match Msource.text source with
6666+ | "" -> ""
6767+ | text ->
6868+ let from =
6969+ let (`Offset index) = Msource.get_offset source position in
7070+ min (String.length text - 1) (index - 1)
7171+ in
7272+ let pos =
7373+ let should_terminate = ref false in
7474+ let has_seen_dot = ref false in
7575+ let is_prefix_char c =
7676+ if !should_terminate then
7777+ false
7878+ else
7979+ match c with
8080+ | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '\'' | '_'
8181+ (* Infix function characters *)
8282+ | '$' | '&' | '*' | '+' | '-' | '/' | '=' | '>'
8383+ | '@' | '^' | '!' | '?' | '%' | '<' | ':' | '~' | '#' ->
8484+ true
8585+ | '`' ->
8686+ if !has_seen_dot then
8787+ false
8888+ else (
8989+ should_terminate := true;
9090+ true
9191+ ) | '.' ->
9292+ has_seen_dot := true;
9393+ not short_path
9494+ | _ -> false
9595+ in
9696+ rfindi text ~from ~f:(fun c -> not (is_prefix_char c))
9797+ in
9898+ let pos =
9999+ match pos with
100100+ | None -> 0
101101+ | Some pos -> pos + 1
102102+ in
103103+ let len = from - pos + 1 in
104104+ let reconstructed_prefix = String.sub text ~pos ~len in
105105+ (* if we reconstructed [~f:ignore] or [?f:ignore], we should take only
106106+ [ignore], so: *)
107107+ if
108108+ String.is_prefixed ~by:"~" reconstructed_prefix
109109+ || String.is_prefixed ~by:"?" reconstructed_prefix
110110+ then
111111+ match lsplit2 reconstructed_prefix ~on:':' with
112112+ | Some (_, s) -> s
113113+ | None -> reconstructed_prefix
114114+ else
115115+ reconstructed_prefix
116116+117117+118118+ let at_pos source position =
119119+ let prefix = prefix_of_position source position in
120120+ let `Offset to_ = Msource.get_offset source position in
121121+ let from =
122122+ to_ - String.length (prefix_of_position ~short_path:true source position)
123123+ in
124124+125125+ Console.(log ["Prefix:";prefix]);
126126+ if prefix = "" then
127127+ None
128128+ else
129129+ let query = Query_protocol.Complete_prefix (prefix, position, [], true, true)
130130+ in
131131+ Some (from, to_, dispatch source query)
132132+end
133133+(*
134134+let dump () =
135135+ let query = Query_protocol.Dump [`String "paths"] in
136136+ dispatch (Msource.make "") query *)
137137+138138+(* let dump_config () =
139139+ let pipeline = make_pipeline (Msource.make "") in
140140+ Mpipeline.with_pipeline pipeline @@ fun () ->
141141+ Mconfig.dump (Mpipeline.final_config pipeline)
142142+ |> Json.pretty_to_string *)
143143+144144+let on_message e =
145145+ let marshaled_message = Brr_io.Message.Ev.data e in
146146+ let action : Protocol.action =
147147+ Marshal.from_bytes marshaled_message 0
148148+ in
149149+ Console.(log ["w: Received message:"; action]);
150150+ let res =
151151+ match action with
152152+ | Complete_prefix (source, position) ->
153153+ let source = Msource.make source in
154154+ begin match Completion.at_pos source position with
155155+ | Some (from, to_, compl) ->
156156+ let entries = compl.entries in
157157+ Protocol.Completions { from; to_; entries; }
158158+ | None ->
159159+ Protocol.Completions { from = 0; to_ = 0; entries = []; }
160160+ end
161161+ | Type_enclosing (source, position) ->
162162+ let source = Msource.make source in
163163+ let query = Query_protocol.Type_enclosing (None, position, None) in
164164+ Protocol.Typed_enclosings (dispatch source query)
165165+ | Protocol.All_errors source ->
166166+ Console.(log ["w: Query errors"]);
167167+ let source = Msource.make source in
168168+ let query = Query_protocol.Errors {
169169+ lexing = true;
170170+ parsing = true;
171171+ typing = true;
172172+ }
173173+ in
174174+ let errors =
175175+ dispatch source query
176176+ |> List.map ~f:(fun (Location.{kind; main=_ ; sub; source} as error) ->
177177+ let of_sub sub =
178178+ Location.print_sub_msg Format.str_formatter sub;
179179+ String.trim (Format.flush_str_formatter ())
180180+ in
181181+ let loc = Location.loc_of_report error in
182182+ let main =
183183+ Format.asprintf "@[%a@]" Location.print_main error |> String.trim
184184+ in
185185+ Protocol.{
186186+ kind;
187187+ loc;
188188+ main;
189189+ sub = List.map ~f:of_sub sub;
190190+ source;
191191+ })
192192+ in
193193+ Protocol.Errors errors
194194+ in
195195+ let res = Marshal.to_bytes res [] in
196196+ Brr_webworkers.Worker.G.post res
197197+198198+let () = Jv.(set global "onmessage" @@ Jv.repr on_message)
199199+200200+(* let () = Console.(log [dump (); dump_config ()]) *)
201201+end