···88 let doc = Utils.get_full_doc @@ Editor.View.state view in
99 let+ result = Merlin_client.query_errors worker doc in
1010 List.map (fun Protocol.{ kind; loc; main; sub = _; source } ->
1111- let from = loc.loc_start.pos_cnum in
1212- let to_ = loc.loc_end.pos_cnum in
1313- let source = Protocol.report_source_to_string source in
1414- let severity = match kind with
1515- | Report_error
1616- | Report_warning_as_error _
1717- | Report_alert_as_error _ -> Lint.Diagnostic.Error
1818- | Report_warning _ -> Lint.Diagnostic.Warning
1919- | Report_alert _ -> Lint.Diagnostic.Info
2020- in
2121- Lint.Diagnostic.create ~source ~from ~to_ ~severity ~message:main ()
2222- ) result
1111+ let from = loc.loc_start.pos_cnum in
1212+ let to_ = loc.loc_end.pos_cnum in
1313+ let source = Protocol.report_source_to_string source in
1414+ let severity = match kind with
1515+ | Report_error
1616+ | Report_warning_as_error _
1717+ | Report_alert_as_error _ -> Lint.Diagnostic.Error
1818+ | Report_warning _ -> Lint.Diagnostic.Warning
1919+ | Report_alert _ -> Lint.Diagnostic.Info
2020+ in
2121+ Lint.Diagnostic.create ~source ~from ~to_ ~severity ~message:main ()
2222+ ) result
2323 |> Array.of_list
24242525- let keywords = List.map
2626- (fun label -> Autocomplete.Completion.create ~label ~type_:"keyword" ())
2727- [
2828- "as"; "do"; "else"; "end"; "exception"; "fun"; "functor"; "if"; "in";
2929- "include"; "let"; "of"; "open"; "rec"; "struct"; "then"; "type"; "val";
3030- "while"; "with"; "and"; "assert"; "begin"; "class"; "constraint";
3131- "done"; "downto"; "external"; "function"; "initializer"; "lazy";
3232- "match"; "method"; "module"; "mutable"; "new"; "nonrec"; "object";
3333- "private"; "sig"; "to"; "try"; "value"; "virtual"; "when";
3434- ]
2525+let keywords = List.map
2626+ (fun label -> Autocomplete.Completion.create ~label ~type_:"keyword" ())
2727+ [
2828+ "as"; "do"; "else"; "end"; "exception"; "fun"; "functor"; "if"; "in";
2929+ "include"; "let"; "of"; "open"; "rec"; "struct"; "then"; "type"; "val";
3030+ "while"; "with"; "and"; "assert"; "begin"; "class"; "constraint";
3131+ "done"; "downto"; "external"; "function"; "initializer"; "lazy";
3232+ "match"; "method"; "module"; "mutable"; "new"; "nonrec"; "object";
3333+ "private"; "sig"; "to"; "try"; "value"; "virtual"; "when";
3434+ ]
35353636let merlin_completion worker = fun ctx ->
3737 let open Fut.Syntax in
···4040 let+ { from; to_; entries } =
4141 Merlin_client.query_completions worker source (`Offset pos)
4242 in
4343- let options = List.map (fun Query_protocol.Compl.{ name; desc; _ } ->
4444- Autocomplete.Completion.create ~label:name ~detail:desc ()
4545- ) entries in
4343+ let options =
4444+ List.map (fun Query_protocol.Compl.{ name; desc; _ } ->
4545+ Autocomplete.Completion.create ~label:name ~detail:desc ()) entries
4646+ in
4647 Some (Autocomplete.Result.create ~from ~to_ ~options ())
47484849let autocomplete worker =
4949- let config = Autocomplete.(config ()
5050- ~override:[Source.create @@ merlin_completion worker; Source.from_list keywords])
5151- in
5050+ let override = [
5151+ Autocomplete.Source.create @@ merlin_completion worker;
5252+ Autocomplete.Source.from_list keywords]
5353+in
5454+ let config = Autocomplete.config () ~override in
5255 Autocomplete.create ~config ()
53565457let tooltip_on_hover worker =
5558 let open Tooltip in
5656- hover_tooltip
5757- (fun ~view ~pos ~side:_ ->
5959+ hover_tooltip @@
6060+ fun ~view ~pos ~side:_ ->
5861 let open Fut.Syntax in
5962 let doc = Utils.get_full_doc @@ Editor.View.state view in
6063 let pos = `Offset pos in
···6871 let pos = loc.loc_start.pos_cnum in
6972 let end_ = loc.loc_end.pos_cnum in
7073 Some (Tooltip.create ~pos ~end_ ~above:true ~arrow:true ~create ())
7171- | _ -> None)
7474+ | _ -> None
72757376let ocaml = Jv.get Jv.global "__CM__mllike" |> Stream.Language.of_jv
7477let ocaml = Stream.Language.define ocaml
75787679module Make (Config : sig val worker_url : string end) = struct
7780 let worker = Merlin_client.make_worker Config.worker_url
7878-7981 let autocomplete = autocomplete worker
8082 let tooltip_on_hover = tooltip_on_hover worker
8181-8283 let linter = Lint.create (linter worker)
83848485 let all_extensions = [|
8585- ocaml;
8686 linter;
8787 autocomplete;
8888 tooltip_on_hover
+21
src/extension/merlin_codemirror.mli
···11+module Utils : sig
22+ val get_el_by_id : string -> Brr.El.t
33+ val get_full_doc : Code_mirror.Editor.State.t -> string
44+end
55+66+val ocaml : Code_mirror.Extension.t
77+(** An extension providing OCaml syntax highlighting *)
88+99+module Make : functor (Config : sig val worker_url : string end) -> sig
1010+ val autocomplete : Code_mirror.Extension.t
1111+ (** An extension providing completions when typing *)
1212+1313+ val tooltip_on_hover : Code_mirror.Extension.t
1414+ (** An extension providing type-information when hovering code *)
1515+1616+ val linter : Code_mirror.Extension.t
1717+ (** An extension that highlights errors and warnings in the code *)
1818+1919+ val all_extensions : Code_mirror.Extension.t array
2020+ (** All the Merlin-specific extensions (does not include [ocaml]) *)
2121+end
···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
+8-10
src/worker/merlin_worker.ml
src/worker/worker.ml
···44open Merlin_kernel
55module Location = Ocaml_parsing.Location
6677-(* Load the CMIs into the pseudo file-system *)
88-(* This add roughly 3mo to the final script. These could be loaded dynamically
99-after the worker *)
1010-let () = List.iter ~f:(fun (path, content) ->
1111- let name = Filename.(concat "/static/stdlib" (basename path)) in
1212- Js_of_ocaml.Sys_js.create_file ~name ~content
1313- ) Static_files.stdlib_cmis
1414-157let config =
168 let initial = Mconfig.initial in
179 { initial with
···193185 let res = Marshal.to_bytes res [] in
194186 Brr_webworkers.Worker.G.post res
195187196196-let () = Jv.(set global "onmessage" @@ Jv.repr on_message)
197188198198-(* let () = Console.(log [dump (); dump_config ()]) *)
189189+let run () =
190190+ (* Load the CMIs into the pseudo file-system *)
191191+ (* This add roughly 3mo to the final script. These could be loaded dynamically
192192+ after the worker *)
193193+ List.iter Static_files.stdlib_cmis ~f:(fun (path, content) ->
194194+ let name = Filename.(concat "/static/stdlib" (basename path)) in
195195+ Js_of_ocaml.Sys_js.create_file ~name ~content);
196196+ Jv.(set global "onmessage" @@ Jv.repr on_message)