Skip to content

Commit 6d84c1c

Browse files
authored
Shorten Merlin diagnostics error (#1513)
* Add ShortenMerlinDiagnostics in `config_data` (and promote) * Upgrade configuration * Change diagnostics representation * Add test * Remove useless `Fiber.return` * Add CHANGES entry * Restore Merlin's recovery
1 parent 056032b commit 6d84c1c

File tree

14 files changed

+393
-40
lines changed

14 files changed

+393
-40
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Make `inlay-hint` for function parameters configurable (#1515)
66
- Add custom `ocamllsp/jumpToTypedHole` to navigate through typed holes (#1516)
77
- Add a code-action for combining pattern cases (just relaying on regex) (#1514)
8+
- Allow (by configuration) shortening of diagnostics (just highlighting the first line) (#1513)
89

910
## Fixes
1011

ocaml-lsp-server/src/config_data.ml

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,78 @@
11
open Import
22
open Import.Json.Conv
33

4+
module ShortenMerlinDiagnostics = struct
5+
type t = { enable : bool [@default false] }
6+
[@@deriving_inline yojson] [@@yojson.allow_extra_fields]
7+
8+
let _ = fun (_ : t) -> ()
9+
10+
let t_of_yojson =
11+
(let _tp_loc = "ocaml-lsp-server/src/config_data.ml.ShortenMerlinDiagnostics.t" in
12+
function
13+
| `Assoc field_yojsons as yojson ->
14+
let enable_field = ref Ppx_yojson_conv_lib.Option.None
15+
and duplicates = ref []
16+
and extra = ref [] in
17+
let rec iter = function
18+
| (field_name, _field_yojson) :: tail ->
19+
(match field_name with
20+
| "enable" ->
21+
(match Ppx_yojson_conv_lib.( ! ) enable_field with
22+
| Ppx_yojson_conv_lib.Option.None ->
23+
let fvalue = bool_of_yojson _field_yojson in
24+
enable_field := Ppx_yojson_conv_lib.Option.Some fvalue
25+
| Ppx_yojson_conv_lib.Option.Some _ ->
26+
duplicates := field_name :: Ppx_yojson_conv_lib.( ! ) duplicates)
27+
| _ -> ());
28+
iter tail
29+
| [] -> ()
30+
in
31+
iter field_yojsons;
32+
(match Ppx_yojson_conv_lib.( ! ) duplicates with
33+
| _ :: _ ->
34+
Ppx_yojson_conv_lib.Yojson_conv_error.record_duplicate_fields
35+
_tp_loc
36+
(Ppx_yojson_conv_lib.( ! ) duplicates)
37+
yojson
38+
| [] ->
39+
(match Ppx_yojson_conv_lib.( ! ) extra with
40+
| _ :: _ ->
41+
Ppx_yojson_conv_lib.Yojson_conv_error.record_extra_fields
42+
_tp_loc
43+
(Ppx_yojson_conv_lib.( ! ) extra)
44+
yojson
45+
| [] ->
46+
let enable_value = Ppx_yojson_conv_lib.( ! ) enable_field in
47+
{ enable =
48+
(match enable_value with
49+
| Ppx_yojson_conv_lib.Option.None -> false
50+
| Ppx_yojson_conv_lib.Option.Some v -> v)
51+
}))
52+
| _ as yojson ->
53+
Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom _tp_loc yojson
54+
: Ppx_yojson_conv_lib.Yojson.Safe.t -> t)
55+
;;
56+
57+
let _ = t_of_yojson
58+
59+
let yojson_of_t =
60+
(function
61+
| { enable = v_enable } ->
62+
let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in
63+
let bnds =
64+
let arg = yojson_of_bool v_enable in
65+
("enable", arg) :: bnds
66+
in
67+
`Assoc bnds
68+
: t -> Ppx_yojson_conv_lib.Yojson.Safe.t)
69+
;;
70+
71+
let _ = yojson_of_t
72+
73+
[@@@end]
74+
end
75+
476
module InlayHints = struct
577
type t =
678
{ hint_pattern_variables : bool [@key "hintPatternVariables"] [@default false]
@@ -565,6 +637,8 @@ type t =
565637
[@key "syntaxDocumentation"] [@default None] [@yojson_drop_default ( = )]
566638
; merlin_jump_code_actions : MerlinJumpCodeActions.t Json.Nullable_option.t
567639
[@key "merlinJumpCodeActions"] [@default None] [@yojson_drop_default ( = )]
640+
; shorten_merlin_diagnostics : ShortenMerlinDiagnostics.t Json.Nullable_option.t
641+
[@key "shortenMerlinDiagnostics"] [@default None] [@yojson_drop_default ( = )]
568642
}
569643
[@@deriving_inline yojson] [@@yojson.allow_extra_fields]
570644

@@ -581,6 +655,7 @@ let t_of_yojson =
581655
and dune_diagnostics_field = ref Ppx_yojson_conv_lib.Option.None
582656
and syntax_documentation_field = ref Ppx_yojson_conv_lib.Option.None
583657
and merlin_jump_code_actions_field = ref Ppx_yojson_conv_lib.Option.None
658+
and shorten_merlin_diagnostics_field = ref Ppx_yojson_conv_lib.Option.None
584659
and duplicates = ref []
585660
and extra = ref [] in
586661
let rec iter = function
@@ -655,6 +730,17 @@ let t_of_yojson =
655730
merlin_jump_code_actions_field := Ppx_yojson_conv_lib.Option.Some fvalue
656731
| Ppx_yojson_conv_lib.Option.Some _ ->
657732
duplicates := field_name :: Ppx_yojson_conv_lib.( ! ) duplicates)
733+
| "shortenMerlinDiagnostics" ->
734+
(match Ppx_yojson_conv_lib.( ! ) shorten_merlin_diagnostics_field with
735+
| Ppx_yojson_conv_lib.Option.None ->
736+
let fvalue =
737+
Json.Nullable_option.t_of_yojson
738+
ShortenMerlinDiagnostics.t_of_yojson
739+
_field_yojson
740+
in
741+
shorten_merlin_diagnostics_field := Ppx_yojson_conv_lib.Option.Some fvalue
742+
| Ppx_yojson_conv_lib.Option.Some _ ->
743+
duplicates := field_name :: Ppx_yojson_conv_lib.( ! ) duplicates)
658744
| _ -> ());
659745
iter tail
660746
| [] -> ()
@@ -680,15 +766,17 @@ let t_of_yojson =
680766
, inlay_hints_value
681767
, dune_diagnostics_value
682768
, syntax_documentation_value
683-
, merlin_jump_code_actions_value )
769+
, merlin_jump_code_actions_value
770+
, shorten_merlin_diagnostics_value )
684771
=
685772
( Ppx_yojson_conv_lib.( ! ) codelens_field
686773
, Ppx_yojson_conv_lib.( ! ) extended_hover_field
687774
, Ppx_yojson_conv_lib.( ! ) standard_hover_field
688775
, Ppx_yojson_conv_lib.( ! ) inlay_hints_field
689776
, Ppx_yojson_conv_lib.( ! ) dune_diagnostics_field
690777
, Ppx_yojson_conv_lib.( ! ) syntax_documentation_field
691-
, Ppx_yojson_conv_lib.( ! ) merlin_jump_code_actions_field )
778+
, Ppx_yojson_conv_lib.( ! ) merlin_jump_code_actions_field
779+
, Ppx_yojson_conv_lib.( ! ) shorten_merlin_diagnostics_field )
692780
in
693781
{ codelens =
694782
(match codelens_value with
@@ -718,6 +806,10 @@ let t_of_yojson =
718806
(match merlin_jump_code_actions_value with
719807
| Ppx_yojson_conv_lib.Option.None -> None
720808
| Ppx_yojson_conv_lib.Option.Some v -> v)
809+
; shorten_merlin_diagnostics =
810+
(match shorten_merlin_diagnostics_value with
811+
| Ppx_yojson_conv_lib.Option.None -> None
812+
| Ppx_yojson_conv_lib.Option.Some v -> v)
721813
}))
722814
| _ as yojson ->
723815
Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom _tp_loc yojson
@@ -735,8 +827,20 @@ let yojson_of_t =
735827
; dune_diagnostics = v_dune_diagnostics
736828
; syntax_documentation = v_syntax_documentation
737829
; merlin_jump_code_actions = v_merlin_jump_code_actions
830+
; shorten_merlin_diagnostics = v_shorten_merlin_diagnostics
738831
} ->
739832
let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in
833+
let bnds =
834+
if None = v_shorten_merlin_diagnostics
835+
then bnds
836+
else (
837+
let arg =
838+
(Json.Nullable_option.yojson_of_t ShortenMerlinDiagnostics.yojson_of_t)
839+
v_shorten_merlin_diagnostics
840+
in
841+
let bnd = "shortenMerlinDiagnostics", arg in
842+
bnd :: bnds)
843+
in
740844
let bnds =
741845
if None = v_merlin_jump_code_actions
742846
then bnds
@@ -829,5 +933,6 @@ let default =
829933
; dune_diagnostics = Some { enable = true }
830934
; syntax_documentation = Some { enable = false }
831935
; merlin_jump_code_actions = Some { enable = false }
936+
; shorten_merlin_diagnostics = Some { enable = false }
832937
}
833938
;;

ocaml-lsp-server/src/configuration.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,9 @@ let report_dune_diagnostics t =
5555
| Some { enable = true } | None -> true
5656
| Some { enable = false } -> false
5757
;;
58+
59+
let shorten_merlin_diagnostics t =
60+
match t.data.shorten_merlin_diagnostics with
61+
| Some { enable = true } -> true
62+
| Some { enable = false } | None -> false
63+
;;

ocaml-lsp-server/src/configuration.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ val default : unit -> t Fiber.t
99
val wheel : t -> Lev_fiber.Timer.Wheel.t
1010
val update : t -> DidChangeConfigurationParams.t -> t Fiber.t
1111
val report_dune_diagnostics : t -> bool
12+
val shorten_merlin_diagnostics : t -> bool

ocaml-lsp-server/src/diagnostics.ml

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,14 @@ type t =
8686
; related_information : bool
8787
; tags : DiagnosticTag.t list
8888
; mutable report_dune_diagnostics : bool
89+
; mutable shorten_merlin_diagnostics : bool
8990
}
9091

9192
let create
9293
(capabilities : PublishDiagnosticsClientCapabilities.t option)
9394
send
9495
~report_dune_diagnostics
96+
~shorten_merlin_diagnostics
9597
=
9698
let related_information, tags =
9799
match capabilities with
@@ -109,6 +111,7 @@ let create
109111
; related_information
110112
; tags
111113
; report_dune_diagnostics
114+
; shorten_merlin_diagnostics
112115
}
113116
;;
114117

@@ -286,6 +289,74 @@ let extract_related_errors uri raw_message =
286289
| _ -> raw_message, None
287290
;;
288291

292+
let first_n_lines_of_range (range : Range.t) n =
293+
if range.end_.line - range.start.line < n
294+
then range
295+
else (
296+
let start = Position.create ~character:range.start.character ~line:range.start.line
297+
and end_ = Position.create ~character:0 ~line:(range.start.line + n) in
298+
Range.create ~start ~end_)
299+
;;
300+
301+
let error_to_diagnostics ~diagnostics ~merlin error =
302+
let doc = Document.Merlin.to_doc merlin in
303+
let create_diagnostic = Diagnostic.create ~source:ocamllsp_source in
304+
let uri = Document.uri doc in
305+
let loc = Loc.loc_of_report error in
306+
let original_range = Range.of_loc loc in
307+
let range =
308+
if diagnostics.shorten_merlin_diagnostics
309+
then first_n_lines_of_range original_range 1
310+
else original_range
311+
in
312+
let severity =
313+
match error.source with
314+
| Warning -> DiagnosticSeverity.Warning
315+
| _ -> DiagnosticSeverity.Error
316+
in
317+
let make_message ppf m = String.trim (Format.asprintf "%a@." ppf m) in
318+
let message = make_message Loc.print_main error in
319+
let message, related_information =
320+
match diagnostics.related_information with
321+
| false -> message, None
322+
| true ->
323+
(match error.sub with
324+
| [] -> extract_related_errors uri message
325+
| _ :: _ ->
326+
( message
327+
, Some
328+
(List.map error.sub ~f:(fun (sub : Loc.msg) ->
329+
let location =
330+
let range = Range.of_loc sub.loc in
331+
Location.create ~range ~uri
332+
in
333+
let message = make_message Loc.print_sub_msg sub in
334+
DiagnosticRelatedInformation.create ~location ~message)) ))
335+
in
336+
let maybe_extra_range_information =
337+
match diagnostics.shorten_merlin_diagnostics with
338+
| false -> None
339+
| true ->
340+
let start_location = Location.create ~range:original_range ~uri in
341+
Some
342+
[ DiagnosticRelatedInformation.create
343+
~location:start_location
344+
~message:"Original error span"
345+
]
346+
in
347+
let relatedInformation =
348+
Option.merge maybe_extra_range_information related_information ~f:( @ )
349+
in
350+
let tags = tags_of_message diagnostics ~src:`Merlin message in
351+
create_diagnostic
352+
?tags
353+
?relatedInformation
354+
~range
355+
~message:(`String message)
356+
~severity
357+
()
358+
;;
359+
289360
let merlin_diagnostics diagnostics merlin =
290361
let doc = Document.Merlin.to_doc merlin in
291362
let uri = Document.uri doc in
@@ -307,41 +378,7 @@ let merlin_diagnostics diagnostics merlin =
307378
[ create_diagnostic ~range:Range.first_line ~message () ]
308379
| errors ->
309380
let merlin_diagnostics =
310-
List.rev_map errors ~f:(fun (error : Loc.error) ->
311-
let loc = Loc.loc_of_report error in
312-
let range = Range.of_loc loc in
313-
let severity =
314-
match error.source with
315-
| Warning -> DiagnosticSeverity.Warning
316-
| _ -> DiagnosticSeverity.Error
317-
in
318-
let make_message ppf m = String.trim (Format.asprintf "%a@." ppf m) in
319-
let message = make_message Loc.print_main error in
320-
let message, relatedInformation =
321-
match diagnostics.related_information with
322-
| false -> message, None
323-
| true ->
324-
(match error.sub with
325-
| [] -> extract_related_errors uri message
326-
| _ :: _ ->
327-
( message
328-
, Some
329-
(List.map error.sub ~f:(fun (sub : Loc.msg) ->
330-
let location =
331-
let range = Range.of_loc sub.loc in
332-
Location.create ~range ~uri
333-
in
334-
let message = make_message Loc.print_sub_msg sub in
335-
DiagnosticRelatedInformation.create ~location ~message)) ))
336-
in
337-
let tags = tags_of_message diagnostics ~src:`Merlin message in
338-
create_diagnostic
339-
?tags
340-
?relatedInformation
341-
~range
342-
~message:(`String message)
343-
~severity
344-
())
381+
List.rev_map errors ~f:(error_to_diagnostics ~diagnostics ~merlin)
345382
in
346383
let holes_as_err_diags =
347384
Query_commands.dispatch pipeline Holes
@@ -369,8 +406,6 @@ let merlin_diagnostics diagnostics merlin =
369406
;;
370407

371408
let set_report_dune_diagnostics t ~report_dune_diagnostics =
372-
let open Fiber.O in
373-
let* () = Fiber.return () in
374409
if t.report_dune_diagnostics = report_dune_diagnostics
375410
then Fiber.return ()
376411
else (
@@ -380,3 +415,14 @@ let set_report_dune_diagnostics t ~report_dune_diagnostics =
380415
t.dirty_uris <- Uri_set.add t.dirty_uris uri));
381416
send t `All)
382417
;;
418+
419+
let set_shorten_merlin_diagnostics t ~shorten_merlin_diagnostics =
420+
if t.shorten_merlin_diagnostics = shorten_merlin_diagnostics
421+
then Fiber.return ()
422+
else (
423+
t.shorten_merlin_diagnostics <- shorten_merlin_diagnostics;
424+
Table.iter t.dune ~f:(fun per_dune ->
425+
Table.iter per_dune ~f:(fun (uri, _diagnostic) ->
426+
t.dirty_uris <- Uri_set.add t.dirty_uris uri));
427+
send t `All)
428+
;;

ocaml-lsp-server/src/diagnostics.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ val create
99
: PublishDiagnosticsClientCapabilities.t option
1010
-> (PublishDiagnosticsParams.t list -> unit Fiber.t)
1111
-> report_dune_diagnostics:bool
12+
-> shorten_merlin_diagnostics:bool
1213
-> t
1314

1415
val send : t -> [ `All | `One of Uri.t ] -> unit Fiber.t
@@ -37,6 +38,7 @@ val tags_of_message
3738

3839
val merlin_diagnostics : t -> Document.Merlin.t -> unit Fiber.t
3940
val set_report_dune_diagnostics : t -> report_dune_diagnostics:bool -> unit Fiber.t
41+
val set_shorten_merlin_diagnostics : t -> shorten_merlin_diagnostics:bool -> unit Fiber.t
4042

4143
(** Exposed for testing *)
4244

0 commit comments

Comments
 (0)