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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
type loc = Cmarkit.Textloc.t
let loc_of_ploc loc (idx, idx') =
let open Cmarkit.Textloc in
let file = file loc in
let first_line = first_line loc in
let last_line = first_line in
let first_byte = first_byte loc + idx in
let last_byte = first_byte + idx' - idx - 1 in
v ~file ~first_line ~last_line ~first_byte ~last_byte
type t =
| DuplicateID of { id : string; occurrences : loc list }
| MissingFile of { file : string; error_msg : string; locs : loc list }
| WrongType of { loc_reason : loc; loc_block : loc; expected_type : string }
| ParsingError of { action : string; msg : string; loc : loc }
| ParsingWarnor of { warnor : Actions_arguments.W.warnor; loc : loc }
| MissingID of { id : string; loc : loc }
| UnknownAttribute of { attr : string; loc : loc }
| General of {
code : string;
msg : string;
labels : (string * loc) list;
notes : string list;
}
let pp ppf = function
| DuplicateID id ->
Format.fprintf ppf "ID '%s' has already been given at %a." id.id
(Fmt.list Cmarkit.Textloc.pp_ocaml)
id.occurrences
| MissingFile s ->
Format.fprintf ppf "Missing file: %s, considering it as an URL. (%s)"
s.file s.error_msg
| WrongType { loc_reason = _; loc_block = _; expected_type } ->
Format.fprintf ppf "Wrong type: expected type '%s'" expected_type
| ParsingError { action; msg; loc = _ } ->
Format.fprintf ppf
"Parsing of the arguments of actions '%s' failed with '%s'" action msg
| ParsingWarnor
{ warnor = UnusedArgument { action_name; argument_name; _ }; loc = _ } ->
Format.fprintf ppf "Action '%s' does not accept argument '%s'" action_name
argument_name
| ParsingWarnor { warnor = Parsing_failure { msg; loc = _ }; loc = _ } ->
Format.fprintf ppf "Action argument parsing failure: %s" msg
| MissingID { id; loc = _ } ->
Format.fprintf ppf "Id '%s' could not be found" id
| General { msg; labels = _; notes = _; code = _ } ->
Format.fprintf ppf "%s" msg
| UnknownAttribute { attr; loc = _ } ->
Format.fprintf ppf
"Attribute '%s' is neither a standard HTML attribute nor a slipshow \
specific one"
attr
let with_range source_map loc f =
let open Grace in
let range (loc : loc) =
let source = source_map (Cmarkit.Textloc.file loc) in
let start = Cmarkit.Textloc.first_byte loc in
let stop = Cmarkit.Textloc.last_byte loc + 1 in
Range.create ~source (Byte_index.of_int start) (Byte_index.of_int stop)
in
try
let range = range loc in
Some (f ~range)
with _ -> None
let to_grace source_map error =
let open Grace in
let with_range = with_range source_map in
match error with
| DuplicateID { id; occurrences } ->
let labels =
List.filter_map
(fun occ -> with_range occ @@ Diagnostic.Label.primaryf "")
occurrences
in
Some
(Diagnostic.createf ~labels Warning "ID %s is assigned multiple times"
id)
| MissingFile { file; error_msg; locs } ->
let labels =
List.filter_map
(fun loc -> with_range loc @@ Diagnostic.Label.primaryf "")
locs
in
Some
(Diagnostic.createf ~labels Warning "file '%s' could not be read: %s"
file error_msg)
| WrongType { loc_reason; loc_block; expected_type } ->
let labels =
List.filter_map Fun.id
[
with_range loc_reason
@@ Diagnostic.Label.primaryf "This expects the id of a %s"
expected_type;
with_range loc_block
@@ Diagnostic.Label.primaryf "This is not a %s" expected_type;
]
in
Some (Diagnostic.createf ~labels Warning "Wrong type")
| ParsingError { action; msg; loc } ->
let labels =
List.filter_map Fun.id
[ with_range loc @@ Diagnostic.Label.primaryf "%s" msg ]
in
Some
(Diagnostic.createf ~labels Warning
"Action %s arguments could not be parsed" action)
| ParsingWarnor
{
warnor =
UnusedArgument
{ action_name; argument_name; loc = parse_loc; possible_arguments };
loc;
} ->
let loc = loc_of_ploc loc parse_loc in
let labels =
List.filter_map Fun.id
[
with_range loc
@@ Diagnostic.Label.primaryf
"Action '%s' does not take argument '%s'" action_name
argument_name;
]
in
let notes =
match possible_arguments with
| [] ->
[
Diagnostic.Message.createf "'%s' accepts no arguments" action_name;
]
| _ ->
[
Diagnostic.Message.createf "'%s' accepts arguments '%s'"
action_name
(String.concat "', '" possible_arguments);
]
in
Some (Diagnostic.createf ~labels ~notes Warning "Invalid action argument")
| ParsingWarnor { warnor = Parsing_failure { msg; loc = parse_loc }; loc } ->
let loc = loc_of_ploc loc parse_loc in
let labels =
List.filter_map Fun.id
[ with_range loc @@ Diagnostic.Label.primaryf "%s" msg ]
in
Some (Diagnostic.createf ~labels Warning "Failed to parse")
| MissingID { id; loc } ->
let labels =
List.filter_map Fun.id
[
with_range loc
@@ Diagnostic.Label.primaryf
"This should be an ID present in the document";
]
in
Some
(Diagnostic.createf ~labels Warning "No element with id '%s' was found"
id)
| General { msg; labels; notes; code = _ } ->
let labels =
List.filter_map
(fun (msg, loc) ->
with_range loc @@ Diagnostic.Label.primaryf "%s" msg)
labels
in
let notes =
List.map (fun msg -> Diagnostic.Message.createf "%s" msg) notes
in
Some (Diagnostic.createf ~labels ~notes Warning "%s" msg)
| UnknownAttribute { attr; loc } ->
let labels =
List.filter_map Fun.id
[ with_range loc @@ Diagnostic.Label.primaryf "" ]
in
Some
(Diagnostic.createf ~labels Warning "Non standard attribute: '%s'" attr)
let errors_acc = ref []
let add x = errors_acc := x :: !errors_acc
let with_ f =
let old_errors = !errors_acc in
errors_acc := [];
let clean_up () =
let errors = !errors_acc in
errors_acc := old_errors;
errors
in
try
let res = f () in
(res, clean_up ())
with exn ->
let _ = clean_up () in
raise exn
let to_code = function
| DuplicateID _ -> "DupID"
| MissingFile _ -> "FSError"
| WrongType _ -> "WrongType"
| ParsingError _ -> "ActionParsing"
| ParsingWarnor _ -> "ActionParsing"
| MissingID _ -> "IDNotFound"
| UnknownAttribute _ -> "UnknownAttribute"
| General { code; _ } -> code
let report_no_src fmt x =
let msg = Format.asprintf "%a" pp x in
let msg = Grace.Diagnostic.createf ~labels:[] ~code:x Warning "%s" msg in
Format.fprintf fmt "%a@.@."
(Grace_ansi_renderer.pp_diagnostic ?config:None ~code_to_string:to_code)
msg