jon.recoil.org

Source file merlin_codemirror.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
open Code_mirror
open Brr

module Utils = Utils

let ocaml = Jv.get Jv.global "__CM__mllike" |> Stream.Language.of_jv
let ocaml = Stream.Language.define ocaml

module Extensions (Worker : Merlin_client.WORKER) = struct

  module Merlin_client = Merlin_client.Make (Worker)
  type worker = Merlin_client.worker

  let linter worker = fun view ->
    let open Fut.Syntax in
    let doc = Utils.get_full_doc @@ Editor.View.state view in
    let+ result = Merlin_client.query_errors worker doc in
    List.map (fun Protocol.{ kind; loc; main; sub = _; source } ->
      let from = loc.loc_start.pos_cnum in
      let to_ = loc.loc_end.pos_cnum in
      let source = Protocol.report_source_to_string source in
      let severity = match kind with
        | Report_error
        | Report_warning_as_error _
        | Report_alert_as_error _ -> Lint.Diagnostic.Error
        | Report_warning _ -> Lint.Diagnostic.Warning
        | Report_alert _ -> Lint.Diagnostic.Info
      in
      Lint.Diagnostic.create ~source ~from ~to_ ~severity ~message:main ()
    ) result
    |> Array.of_list

  let keywords = List.map
    (fun label ->
      Autocomplete.Completion.create ~label ~type_:"keyword" ())
      [
        "as"; "do"; "else"; "end"; "exception"; "fun"; "functor"; "if"; "in";
        "include"; "let"; "of"; "open"; "rec"; "struct"; "then"; "type"; "val";
        "while"; "with"; "and"; "assert"; "begin"; "class"; "constraint";
        "done"; "downto"; "external"; "function"; "initializer"; "lazy";
        "match"; "method"; "module"; "mutable"; "new"; "nonrec"; "object";
        "private"; "sig"; "to"; "try"; "value"; "virtual"; "when";
      ]

  let merlin_completion worker = fun ctx ->
    let open Fut.Syntax in
    let source = Utils.get_full_doc @@ Autocomplete.Context.state ctx in
    let pos = Autocomplete.Context.pos ctx in
    let+ { from; to_; entries } =
      Merlin_client.query_completions worker source (`Offset pos)
    in
    let options =
      let num_completions = List.length entries in
      List.mapi (fun i Query_protocol.Compl.{ name; desc; _ } ->
        let boost = num_completions - i in
        Autocomplete.Completion.create ~label:name ~detail:desc ~boost ()) entries
    in
    Some (Autocomplete.Result.create ~filter:true ~from ~to_ ~options ())

  let autocomplete worker =
    let override = [
      Autocomplete.Source.from_list keywords;
      Autocomplete.Source.create @@ merlin_completion worker]
  in
    let config = Autocomplete.config () ~override in
    Autocomplete.create ~config ()

  let tooltip_on_hover worker =
    let open Tooltip in
    hover_tooltip @@
    fun ~view ~pos ~side:_ ->
      let open Fut.Syntax in
      let doc = Utils.get_full_doc @@ Editor.View.state view in
      let pos = `Offset pos in
      let+ result = Merlin_client.query_type worker doc pos in
      match result with
      | (loc, `String type_, _)::_ ->
        let create _view =
          let dom = El.div [El.txt' type_] in
          Tooltip_view.create ~dom ()
        in
        let pos = loc.loc_start.pos_cnum in
        let end_ = loc.loc_end.pos_cnum in
        Some (Tooltip.create ~pos ~end_ ~above:true ~arrow:true ~create ())
      | _ -> None

  let linter worker = Lint.create (linter worker)

  let all_extensions worker = [|
    linter worker;
    autocomplete worker;
    tooltip_on_hover worker
  |]
end

module type Config = sig
  val worker_url : string
  val cmis : Protocol.cmis
end

module Make (Config : Config) = struct
  let worker =
    let worker = Merlin_client.make_worker Config.worker_url in
    let _ = Merlin_client.add_cmis worker Config.cmis in
    worker

  open Extensions (Merlin_client.Webworker)

  let autocomplete = autocomplete worker
  let tooltip_on_hover = tooltip_on_hover worker
  let linter = linter worker
  let all_extensions = all_extensions worker
end