Skip to content

Commit a7e97a2

Browse files
committed
feature: communicate dialects READER to merlin
If dialect defined (merlin_reader) thenwe configure merlin with it. Signed-off-by: Andrey Popp <8mayday@gmail.com>
1 parent 7c9c6cb commit a7e97a2

File tree

11 files changed

+181
-40
lines changed

11 files changed

+181
-40
lines changed

src/dune_rules/dialect.ml

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ module File_kind = struct
1010
; extension : string
1111
; preprocess : (Loc.t * Action.t) option
1212
; format : (Loc.t * Action.t * string list) option
13+
; merlin_reader : (Loc.t * string list) option
1314
}
1415

15-
let encode { kind; extension; preprocess; format } =
16+
let encode { kind; extension; preprocess; format; merlin_reader } =
1617
let open Dune_lang.Encoder in
1718
let kind =
1819
string
@@ -28,16 +29,18 @@ module File_kind = struct
2829
[ field "extension" string extension
2930
; field_o "preprocess" Action.encode (Option.map ~f:snd preprocess)
3031
; field_o "format" Action.encode (Option.map ~f:(fun (_, x, _) -> x) format)
32+
; field_o "merlin_reader" (list string) (Option.map ~f:snd merlin_reader)
3133
])
3234
;;
3335

34-
let to_dyn { kind; extension; preprocess; format } =
36+
let to_dyn { kind; extension; preprocess; format; merlin_reader } =
3537
let open Dyn in
3638
record
3739
[ "kind", Ml_kind.to_dyn kind
3840
; "extension", string extension
3941
; "preprocess", option (fun (_, x) -> Action.to_dyn x) preprocess
4042
; "format", option (fun (_, x, y) -> pair Action.to_dyn (list string) (x, y)) format
43+
; "merlin_reader", option (fun (_, x) -> list string x) merlin_reader
4144
]
4245
;;
4346
end
@@ -78,13 +81,17 @@ let decode =
7881
field_o
7982
"format"
8083
(map ~f:(fun (loc, x) -> loc, x, []) (located Action.decode_dune_file))
84+
and+ merlin_reader =
85+
field_o
86+
"merlin_reader"
87+
(Dune_lang.Syntax.since Stanza.syntax (3, 11) >>> located (repeat1 string))
8188
and+ syntax_ver = Syntax.get_exn Stanza.syntax in
8289
let ver = 3, 9 in
8390
if syntax_ver < ver && Option.is_some (String.index_from extension 1 '.')
8491
then (
8592
let what = "the possibility of defining extensions containing periods" in
8693
Syntax.Error.since loc Stanza.syntax ver ~what);
87-
{ File_kind.kind; extension; preprocess; format }
94+
{ File_kind.kind; extension; preprocess; format; merlin_reader }
8895
in
8996
fields
9097
(let+ name = field "name" string
@@ -130,6 +137,13 @@ let format { file_kinds; _ } ml_kind =
130137
x.format
131138
;;
132139

140+
let merlin_reader { file_kinds; _ } ml_kind =
141+
let open Option.O in
142+
let* x = Ml_kind.Dict.get file_kinds ml_kind in
143+
let+ _, merlin_reader = x.merlin_reader in
144+
x.extension, merlin_reader
145+
;;
146+
133147
let ocaml =
134148
let format kind =
135149
let flag_of_kind = function
@@ -154,6 +168,7 @@ let ocaml =
154168
( Loc.none
155169
, format kind
156170
, [ ".ocamlformat"; ".ocamlformat-ignore"; ".ocamlformat-enable" ] )
171+
; merlin_reader = None
157172
}
158173
in
159174
let intf = Some (file_kind Ml_kind.Intf ".mli") in
@@ -179,6 +194,7 @@ let reason =
179194
; extension
180195
; preprocess = Some (Loc.none, preprocess)
181196
; format = Some (Loc.none, format, [])
197+
; merlin_reader = None
182198
}
183199
in
184200
let intf = Some (file_kind Ml_kind.Intf ".rei") in
@@ -207,6 +223,7 @@ let rescript =
207223
; extension
208224
; preprocess = Some (Loc.none, preprocess)
209225
; format = Some (Loc.none, format, [])
226+
; merlin_reader = None
210227
}
211228
in
212229
let intf = Some (file_kind Ml_kind.Intf ".resi") in
@@ -227,43 +244,61 @@ module DB = struct
227244
type t =
228245
{ by_name : dialect String.Map.t
229246
; by_extension : dialect String.Map.t
230-
; mutable extensions_for_merlin : string option Ml_kind.Dict.t list option
247+
; for_merlin : for_merlin Lazy.t
248+
}
249+
250+
and for_merlin =
251+
{ extensions : string option Ml_kind.Dict.t list
252+
; readers : string list String.Map.t
231253
}
232254

233255
let fold { by_name; _ } = String.Map.fold by_name
234256

235257
let empty =
236258
{ by_name = String.Map.empty
237259
; by_extension = String.Map.empty
238-
; extensions_for_merlin = None
260+
; for_merlin = lazy { extensions = []; readers = String.Map.empty }
239261
}
240262
;;
241263

242-
let set_extensions_for_merlin t =
243-
let v =
244-
fold t ~init:[] ~f:(fun d s ->
245-
let impl = extension d Ml_kind.Impl in
246-
let intf = extension d Ml_kind.Intf in
247-
if (* Only include dialects with no preprocessing and skip default file
248-
extensions *)
249-
preprocess d Ml_kind.Impl <> None
250-
|| preprocess d Ml_kind.Intf <> None
251-
|| (impl = extension ocaml Ml_kind.Impl && intf = extension ocaml Ml_kind.Intf)
252-
then s
253-
else { Ml_kind.Dict.impl; intf } :: s)
264+
let compute_for_merlin by_name =
265+
let extensions =
266+
String.Map.fold by_name ~init:[] ~f:(fun d s ->
267+
let ext_for kind =
268+
let ext = extension d kind in
269+
if ext = extension ocaml kind
270+
then (* this is standard dialect, exclude *) None
271+
else if merlin_reader d kind <> None
272+
then (* we have merlin reader defined, it will handle these files *) ext
273+
else if preprocess d kind <> None
274+
then (* we have preprocessor defined *) None
275+
else ext
276+
in
277+
let impl = ext_for Ml_kind.Impl in
278+
let intf = ext_for Ml_kind.Intf in
279+
match impl, intf with
280+
| None, None -> s
281+
| _ -> { Ml_kind.Dict.impl; intf } :: s)
254282
|> List.sort ~compare:(Ml_kind.Dict.compare (Option.compare String.compare))
255283
in
256-
t.extensions_for_merlin <- Some v;
257-
v
284+
let readers =
285+
String.Map.fold by_name ~init:String.Map.empty ~f:(fun d s ->
286+
let add kind s =
287+
match merlin_reader d kind with
288+
| None -> s
289+
| Some (extension, reader) ->
290+
(* Ok to use [add_exn] here as we are validating below in [add]
291+
function that we have extensions registered only once. *)
292+
String.Map.add_exn s extension reader
293+
in
294+
s |> add Ml_kind.Impl |> add Ml_kind.Intf)
295+
in
296+
{ extensions; readers }
258297
;;
259298

260-
let extensions_for_merlin t =
261-
match t.extensions_for_merlin with
262-
| Some s -> s
263-
| None -> set_extensions_for_merlin t
264-
;;
299+
let for_merlin t = Lazy.force t.for_merlin
265300

266-
let add { by_name; by_extension; extensions_for_merlin = _ } ~loc dialect =
301+
let add { by_name; by_extension; for_merlin = _ } ~loc dialect =
267302
let by_name =
268303
match String.Map.add by_name dialect.name dialect with
269304
| Ok by_name -> by_name
@@ -287,7 +322,7 @@ module DB = struct
287322
let by_extension =
288323
add_ext (add_ext by_extension dialect.file_kinds.intf) dialect.file_kinds.impl
289324
in
290-
{ by_name; by_extension; extensions_for_merlin = None }
325+
{ by_name; by_extension; for_merlin = lazy (compute_for_merlin by_name) }
291326
;;
292327

293328
let of_list dialects = List.fold_left ~f:(add ~loc:Loc.none) ~init:empty dialects

src/dune_rules/dialect.mli

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,14 @@ module DB : sig
4242
val find_by_name : t -> string -> dialect option
4343
val find_by_extension : t -> string -> (dialect * Ml_kind.t) option
4444
val fold : t -> init:'a -> f:(dialect -> 'a -> 'a) -> 'a
45-
val extensions_for_merlin : t -> string option Ml_kind.Dict.t list
4645
val to_dyn : t -> Dyn.t
4746
val builtin : t
4847
val is_default : t -> bool
48+
49+
type for_merlin =
50+
{ extensions : string option Ml_kind.Dict.t list
51+
; readers : string list String.Map.t
52+
}
53+
54+
val for_merlin : t -> for_merlin
4955
end

src/dune_rules/merlin/merlin.ml

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,16 @@ module Processed = struct
7373
type module_config =
7474
{ opens : Module_name.t list
7575
; module_ : Module.t
76+
; reader : string list option
7677
}
7778

78-
let dyn_of_module_config { opens; module_ } =
79+
let dyn_of_module_config { opens; module_; reader } =
7980
let open Dyn in
80-
record [ "opens", list Module_name.to_dyn opens; "module_", Module.to_dyn module_ ]
81+
record
82+
[ "opens", list Module_name.to_dyn opens
83+
; "module_", Module.to_dyn module_
84+
; "reader", option (list string) reader
85+
]
8186
;;
8287

8388
(* ...but modules can have different preprocessing specifications*)
@@ -150,7 +155,11 @@ module Processed = struct
150155
| None, None -> None
151156
;;
152157

153-
let to_sexp ~opens ~pp { stdlib_dir; obj_dirs; src_dirs; flags; extensions; melc_flags }
158+
let to_sexp
159+
~opens
160+
~pp
161+
~reader
162+
{ stdlib_dir; obj_dirs; src_dirs; flags; extensions; melc_flags }
154163
=
155164
let make_directive tag value = Sexp.List [ Atom tag; value ] in
156165
let make_directive_of_path tag path =
@@ -201,8 +210,16 @@ module Processed = struct
201210
let+ impl, intf = get_ext x in
202211
make_directive "SUFFIX" (Sexp.Atom (Printf.sprintf "%s %s" impl intf)))
203212
in
213+
let reader =
214+
match reader with
215+
| Some reader ->
216+
[ make_directive "READER" (Sexp.List (List.map ~f:(fun r -> Sexp.Atom r) reader))
217+
]
218+
| None -> []
219+
in
204220
Sexp.List
205-
(List.concat [ stdlib_dir; exclude_query_dir; obj_dirs; src_dirs; flags; suffixes ])
221+
(List.concat
222+
[ stdlib_dir; exclude_query_dir; obj_dirs; src_dirs; flags; suffixes; reader ])
206223
;;
207224

208225
let quote_for_dot_merlin s =
@@ -251,7 +268,7 @@ module Processed = struct
251268
(* We only match the first part of the filename : foo.ml -> foo foo.cppo.ml
252269
-> foo *)
253270
let open Option.O in
254-
let+ { module_; opens } =
271+
let+ { module_; opens; reader } =
255272
let find file =
256273
match Path.Build.Map.find per_module_config file with
257274
| Some _ as s -> s
@@ -262,25 +279,32 @@ module Processed = struct
262279
| None -> Copy_line_directive.DB.follow_while file ~f:find
263280
in
264281
let pp = Module_name.Per_item.get pp_config (Module.name module_) in
265-
to_sexp ~opens ~pp config
282+
to_sexp ~opens ~pp ~reader config
266283
;;
267284

268285
let print_file path =
269286
match load_file path with
270287
| Error msg -> Printf.eprintf "%s\n" msg
271288
| Ok { per_module_config; pp_config; config } ->
272-
let pp_one { module_; opens } =
289+
let pp_one name sexp =
273290
let open Pp.O in
291+
Pp.vbox (Pp.text name) ++ Pp.newline ++ Pp.vbox (Sexp.pp sexp)
292+
in
293+
let pp_module { module_; opens; reader } =
274294
let name = Module.name module_ in
275295
let pp = Module_name.Per_item.get pp_config name in
276-
let sexp = to_sexp ~opens ~pp config in
277-
Pp.vbox (Pp.text (Module_name.to_string name))
278-
++ Pp.newline
279-
++ Pp.vbox (Sexp.pp sexp)
296+
let sexps =
297+
List.map (Module.sources module_) ~f:(fun path ->
298+
Path.basename path, to_sexp ~opens ~pp ~reader config)
299+
|> List.sort ~compare:(fun (a, _) (b, _) -> String.compare a b)
300+
in
301+
match sexps with
302+
| [ (_, sexp) ] -> pp_one (Module_name.to_string name) sexp
303+
| many -> Pp.concat_map ~sep:Pp.cut ~f:(fun (name, sexp) -> pp_one name sexp) many
280304
in
281305
let pp =
282306
Path.Build.Map.values per_module_config
283-
|> Pp.concat_map ~sep:Pp.cut ~f:pp_one
307+
|> Pp.concat_map ~sep:Pp.cut ~f:pp_module
284308
|> Pp.vbox
285309
in
286310
Format.printf "%a@." Pp.to_fmt pp
@@ -361,6 +385,7 @@ module Unprocessed = struct
361385
; source_dirs : Path.Source.Set.t
362386
; objs_dirs : Path.Set.t
363387
; extensions : string option Ml_kind.Dict.t list
388+
; readers : string list String.Map.t
364389
; mode : Lib_mode.t
365390
}
366391

@@ -399,7 +424,7 @@ module Unprocessed = struct
399424
Path.Set.singleton @@ obj_dir_of_lib `Private mode (Obj_dir.of_local obj_dir)
400425
in
401426
let flags = Ocaml_flags.get flags mode in
402-
let extensions = Dialect.DB.extensions_for_merlin dialects in
427+
let { Dialect.DB.extensions; readers } = Dialect.DB.for_merlin dialects in
403428
let config =
404429
{ stdlib_dir
405430
; mode
@@ -410,6 +435,7 @@ module Unprocessed = struct
410435
; source_dirs
411436
; objs_dirs
412437
; extensions
438+
; readers
413439
}
414440
in
415441
{ ident; config; modules = source_modules }
@@ -515,6 +541,7 @@ module Unprocessed = struct
515541
; config =
516542
{ stdlib_dir
517543
; extensions
544+
; readers
518545
; flags
519546
; objs_dirs
520547
; source_dirs
@@ -610,6 +637,7 @@ module Unprocessed = struct
610637
let config =
611638
{ Processed.module_ = Module.set_pp m None
612639
; opens = Modules.alias_for modules m |> List.map ~f:Module.name
640+
; reader = String.Map.find readers (Path.Build.extension src)
613641
}
614642
in
615643
(src, config) :: acc))
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
(lang dune 3.11)
2+
3+
(using melange 0.1)
4+
5+
(dialect
6+
(name mlx)
7+
(implementation
8+
(extension mlx)
9+
(preprocess (run cat %{input-file}))
10+
(merlin_reader mlx)))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
(executable
2+
(name x))

test/blackbox-tests/test-cases/merlin/dialect.t/exe/x.mlx

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
(library
2+
(name x))

test/blackbox-tests/test-cases/merlin/dialect.t/lib/x.mlx

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(library
2+
(modes melange)
3+
(name x_mel))

test/blackbox-tests/test-cases/merlin/dialect.t/melange/x_mel.mlx

Whitespace-only changes.

0 commit comments

Comments
 (0)