File: postprocess.ml

package info (click to toggle)
ocaml-markup 1.0.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,340 kB
  • sloc: ml: 15,131; makefile: 89
file content (347 lines) | stat: -rw-r--r-- 9,624 bytes parent folder | download
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
(* This file is part of Markup.ml, released under the MIT license. See
   LICENSE.md for details, or visit https://github.com/aantron/markup.ml. *)

open Soup

let (|>) x f = f x

type transform =
  | Rename of string
  | TableOfContents
  | UpTo of string
  | Class of string
  | WithType of string
  | Meta of (string * string option)

let transforms =
  ["Markup.html",
    [Rename "index.html"; TableOfContents; Class "index";
     Meta ("Markup.ml - Error-recovering HTML and XML parsers for OCaml",
           Some ("Streaming, error-recovering, standards-based HTML(5) and " ^
                 "XML parsers with an interface designed for ease of use."))];

   "Markup.Error.html",
     [UpTo "index.html"; Meta ("Error - Markup.ml", None)];

   "Markup.Encoding.html",
     [UpTo "index.html"; Meta ("Encoding - Markup.ml", None)];

   "Markup.Ns.html",
     [UpTo "index.html"; Meta ("Ns - Markup.ml", None)];

   "Markup.ASYNCHRONOUS.html",
     [UpTo "index.html"; Class "asynchronous";
      Meta ("ASYNCHRONOUS - Markup.ml", None)];

   "Markup_lwt.html",
     [UpTo "index.html"; Class "asynchronous"; WithType "Lwt";
      Meta ("Markup_lwt - Markup.ml", None)];

   "Markup_lwt_unix.html",
     [UpTo "index.html"; Meta ("Markup_lwt_unix - Markup.ml", None)];

   "Markup.ASYNCHRONOUS.Encoding.html",
     [UpTo "Markup.ASYNCHRONOUS.html";
      Meta ("ASYNCHRONOUS.Encoding - Markup.ml", None)]]

let rec find_map f = function
  | [] -> None
  | x::l ->
    match f x with
    | None -> find_map f l
    | Some _ as v -> v

let lookup f file =
  try transforms |> List.assoc file |> find_map f
  with Not_found -> None

let should_rename file =
  lookup (function
    | Rename name -> Some name
    | _ -> None)
    file

let new_name file =
  match should_rename file with
  | None -> file
  | Some name -> name

let should_make_toc file =
  try
    transforms
    |> List.assoc file
    |> List.mem TableOfContents
  with Not_found -> false

let should_make_up file =
  lookup (function
    | UpTo name -> Some name
    | _ -> None)
    file

let should_add_class file =
  lookup (function
    | Class name -> Some name
    | _ -> None)
    file

let should_add_with_type file =
  lookup (function
    | WithType name -> Some name
    | _ -> None)
    file

let html_directory = "doc/html"
let read_output_file name = Filename.concat html_directory name |> read_file
let write_output_file name text =
  write_file (Filename.concat html_directory name) text

let read_fragment name = Filename.concat "doc" name |> read_file

let clean_up_head soup name =
  soup $$ "head link:not([rel=stylesheet])" |> iter delete;

  let address = "http://aantron.github.io/markup.ml" in

  let canonical =
    match new_name name with
    | "index.html" -> address
    | name -> address ^ "/" ^ name
  in

  let title, description =
    let result =
      lookup (function
        | Meta v -> Some v
        | _ -> None)
        name
    in
    match result with
    | None -> failwith ("no metadata for " ^ name)
    | Some v -> v
  in

  let meta_content =
    "<title>" ^ title ^ "</title>\n<link rel='canonical' href='" ^
    canonical ^ "'>\n" ^ "<meta name='author' content='Anton Bachin'>\n" ^
    "<meta name='viewport' content='width=device-width'>"
  in

  let meta_content =
    match description with
    | None -> meta_content
    | Some text ->
      meta_content ^ "\n<meta name='description' content='" ^ text ^ "'>"
  in

  soup $ "title" |> delete;
  meta_content |> parse |> children |> iter (append_child (soup $ "head"))

let clean_up_header soup =
  soup $ ".navbar" |> delete;
  soup $ "hr" |> delete;
  read_fragment "header.html" |> parse |> replace (soup $ "h1");
  read_fragment "footer.html" |> parse |> append_child (soup $ "body")

let clean_up_content soup =
  soup $$ "body > br" |> iter delete;
  soup $$ "a:contains(\"..\")" |> iter unwrap;

  begin match soup $? "table.indextable" with
  | None -> ()
  | Some table ->
    table |> R.previous_element |> delete
  end;

  soup $$ "a[href]" |> iter (fun a ->
    let link = R.attribute "href" a in
    let prefix = "Markup.html" in
    if String.length link >= String.length prefix &&
       String.sub link 0 (String.length prefix) = prefix then
      let suffix =
        String.sub link (String.length prefix)
          (String.length link - String.length prefix)
      in
      set_attribute "href" ("index.html" ^ suffix) a);

  soup $$ "a:not(.protect):contains(\"Markup.\")" |> iter (fun a ->
    match a $? ".constructor" with
    | None ->
      let text = R.leaf_text a in
      let prefix = "Markup." in
      let text =
        String.sub text (String.length prefix)
          (String.length text - String.length prefix)
      in
      clear a;
      create_text text |> append_child a

    | Some element ->
      delete element;
      let inner_html =
        a $ ".code" |> children |> fold (fun s n -> s ^ (to_string n)) "" in
      let inner_html = String.sub inner_html 1 (String.length inner_html - 1) in
      a $ ".code" |> clear;
      inner_html |> parse |> children |> iter (append_child (a $ ".code")));

  soup $$ "pre"
  |> filter (fun e -> e $? ".type" <> None)
  |> filter (fun e -> e $? "br" <> None)
  |> filter (fun e -> e $? "+ .info" <> None)
  |> iter (fun e -> e $ "+ .info" |> add_class "multiline-member");

  let rec reassemble_lists () =
    match soup $? "ul + ul" with
    | None -> ()
    | Some ul ->
      let ul = R.previous_element ul in
      let rec consume () =
        match ul $? "+ ul" with
        | None -> ()
        | Some ul' ->
          R.child_element ul' |> append_child ul;
          delete ul';
          consume ()
      in
      consume ();
      reassemble_lists ()
  in
  reassemble_lists ();

  soup $$ "ul" |> iter (fun ul -> ul |> R.previous_element |> delete);

  soup $$ "pre > .type"
  |> filter (fun e -> e $? "br" <> None)
  |> iter (fun e ->
    create_text "       " |> prepend_child e;
    create_element "br" |> prepend_child e);

  let uncolor class_ content =
    soup $$ ("span." ^ class_)
    |> filter at_most_one_child
    |> filter (fun e -> leaf_text e = Some content)
    |> iter unwrap
  in

  uncolor "constructor" "Error";
  uncolor "constructor" "Encoding";
  uncolor "constructor" "Markup";
  uncolor "constructor" "Markup_lwt";
  uncolor "constructor" "Markup_lwt_unix";
  uncolor "constructor" "Markup_async";
  uncolor "constructor" "ASYNCHRONOUS";
  uncolor "constructor" "Pervasives";
  uncolor "constructor" "Lwt_io";
  uncolor "keyword" "false";
  uncolor "keyword" "parser";

  soup $$ "span[id]" |> iter (fun span ->
    set_name "a" span;
    set_attribute "href" ("#" ^ (R.attribute "id" span)) span);

  soup $$ "h2[id]" |> iter (fun h2 ->
    let href = "#" ^ (R.attribute "id" h2) in
    let a =
      create_element
        ~attributes:["href", href] ~inner_text:(R.leaf_text h2) "a";
    in
    clear h2;
    append_child h2 a)

let add_with_type soup type_name =
  let extra =
    " <span class='keyword'>with type</span> 'a io = 'a " ^
    "<span class='constructor'>" ^ type_name ^ "</span>.t"
  in

  parse extra |> children
  |> iter (append_child (soup $ "pre:contains(\"ASYNCHRONOUS\")"))

let add_table_of_contents soup =
  let sections =
    soup $$ "h2"
    |> to_list
    |> List.map (fun h2 -> R.id h2, R.leaf_text h2)
  in

  let toc = create_element ~class_:"toc" "div" in
  create_element ~inner_text:"Module contents" "p" |> append_child toc;
  let links = create_element ~class_:"links" "div" in
  append_child toc links;

  ("", "[Top]")::sections |> List.iter (fun (id, title) ->
    create_element ~attributes:["href", "#" ^ id] ~inner_text:title "a"
    |> append_child links;
    create_element "br" |> append_child links);

  create_element "br" |> insert_after (toc $ "a");

  create_element "br" |> append_child toc;
  create_element "br" |> append_child toc;

  create_element
    ~attributes:["href", "https://github.com/aantron/markup.ml"]
    ~classes:["github"; "hide-narrow"] ~inner_text:"GitHub"
    "a"
  |> append_child toc;

  toc $ "a" |> set_attribute "class" "hide-narrow";

  append_child (soup $ ".info") toc

let add_up_link soup to_ =
  let toc =
    match soup $? ".toc" with
    | Some element -> element
    | None ->
      let toc = create_element ~class_:"toc" "div" in
      append_child (soup $ ".info") toc;
      toc
  in

  let container = create_element ~class_:"hide-narrow" "div" in
  create_element ~inner_text:"[Up]" ~attributes:["href", to_] "a"
  |> append_child container;
  create_element "br" |> append_child container;
  create_element "br" |> append_child container;

  container |> prepend_child toc

let () =
  html_directory
  |> Sys.readdir
  |> Array.to_list
  |> List.filter (fun f -> Filename.check_suffix f ".html")
  |> List.iter begin fun file ->
    let soup = file |> read_output_file |> parse in

    clean_up_head soup file;

    clean_up_header soup;
    clean_up_content soup;

    begin match should_add_with_type file with
    | None -> ()
    | Some type_name -> add_with_type soup type_name
    end;

    if should_make_toc file then
      add_table_of_contents soup;

    begin match should_make_up file with
    | None -> ()
    | Some target -> add_up_link soup target
    end;

    begin match should_add_class file with
    | None -> ()
    | Some class_ -> soup $ "body" |> add_class class_
    end;

    begin match should_rename file with
    | None -> soup |> to_string |> write_output_file file
    | Some name ->
      Sys.remove (Filename.concat html_directory file);
      soup |> to_string |> write_output_file name
    end
  end