Skip to content

Commit f1551d5

Browse files
committed
Add a custom "Output_sink" module for emitting strings
OCaml doesn't have a good abstraction over writing text to generic output (file, stdout, to memory). As a result, we often end up using Format.formatter as an arbitrary output source. This is fine in some use-cases. However, Format is designed around pretty-printing values, rather than being high-performance. We see this particularly when emitting HTML, with the format machinery contributing a significant amount of time. This change adds a new Output_sink module, which effectively exposes a single "write" method. We switch the HTML module over to this. This provides a significant performance boost. For a CC:T doc-gen this reduces allocations and time taken by ~15%.
1 parent 43ee16c commit f1551d5

28 files changed

+311
-232
lines changed

src/bin/cli/dune

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
illuaminate.lint
2020
illuaminate.config
2121
illuaminateConfigFormat
22-
illuaminate.html
2322
illuaminate.minify
2423
illuaminate.pattern
2524
illuaminate.doc_emit

src/bin/cli/illuaminate_cli.ml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,7 @@ let doc_gen path =
104104
let to_abs path = Fpath.to_string (to_abs' path) in
105105

106106
(* Write a HTML doc to a file. *)
107-
let emit_doc node out =
108-
let fmt = Format.formatter_of_out_channel out in
109-
Html.Default.emit_doc fmt node; Format.pp_print_flush fmt ()
110-
in
107+
let emit_doc node out = Output_sink.with_output_stream out @@ fun out -> Html.emit_doc out node in
111108

112109
(* Resolve the path to the logo, copying it into the output directory if needed. *)
113110
let resolve_logo ~data ~destination logo =
@@ -199,7 +196,7 @@ let doc_gen path =
199196
|> CCIO.with_out ~flags:[ Open_creat; Open_trunc; Open_binary ] (Fpath.to_string path) );
200197

201198
let path = Fpath.(destination / "index.html") in
202-
Option.fold ~none:Html.Default.nil ~some:(parse_index ~options:(options Fun.id)) index
199+
Option.fold ~none:Html.nil ~some:(parse_index ~options:(options Fun.id)) index
203200
|> E.Html.emit_index ~options:(options Fun.id) ~pages
204201
|> emit_doc
205202
|> CCIO.with_out ~flags:[ Open_creat; Open_trunc; Open_binary ] (Fpath.to_string path);

src/doc_emit/dune

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
illuaminate
1010
illuaminate.core
1111
illuaminate.data
12-
illuaminate.html
1312
illuaminate.parser
1413
illuaminate.semantics
1514
markup

src/doc_emit/html_basic.ml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ let reference_link ~options:{ resolve; _ } : Reference.resolved -> string option
99
| External { url = None; _ } -> None
1010
| Unknown _ -> None
1111

12-
let show_list ?(tag = "h3") ?(expandable = false) ?(expand = true) title = function
13-
| [] -> Html.Default.nil
12+
let show_list ?(tag = "h3") ?(expandable = false) ?(expand = true) title =
13+
let open Illuaminate.Html in
14+
function
15+
| [] -> nil
1416
| xs ->
15-
let open Html.Default in
1617
[ create_node ~tag
1718
~children:[ str title ]
1819
~attributes:

src/doc_emit/html_highlight.ml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ let transform_ref ~options ((r : M.Reference.t), t) =
2121
| _ -> None
2222

2323
let emit ~options ~data ~input visit tree =
24-
let open Html.Default in
24+
let open Illuaminate.Html in
2525
(* TODO: Emit a true HTML node. Not sure how to do that elegantly though - we'd probably need to
2626
use a visitor within Emit instead. *)
2727
let res = Buffer.create (String.length input) in
@@ -49,7 +49,11 @@ let emit ~options ~data ~input visit tree =
4949
in
5050
("title", desc) :: attrs
5151
in
52-
Format.asprintf "<a%a>" Html.Emitters.attrs attrs
52+
let module Sink = Illuaminate.Output_sink in
53+
Sink.with_to_str @@ fun out ->
54+
Sink.write out "<a";
55+
Illuaminate.Html.emit_attrs out attrs;
56+
Sink.write out ">"
5357
| None ->
5458
stack := false :: xs;
5559
""
@@ -117,7 +121,7 @@ let do_lua ~options:({ Html_options.data; _ } as options) input =
117121
| Error _, (lazy (Ok tree)) ->
118122
let data = resolve tree in
119123
(emit ~options ~data ~input Emit.program tree, Some `Stmt)
120-
| Error _, (lazy (Error _)) -> (Html.Default.str input, None)
124+
| Error _, (lazy (Error _)) -> (Illuaminate.Html.str input, None)
121125

122126
let lua ~options input = do_lua ~options input |> fst
123127

@@ -129,6 +133,6 @@ let lua_block ?(attrs = []) ~options input =
129133
| Some `Expr -> Some "expr"
130134
| Some `Stmt -> Some "stmt"
131135
in
132-
Html.Default.create_node ~tag:"pre"
136+
Illuaminate.Html.create_node ~tag:"pre"
133137
~attributes:(("class", Some "highlight") :: ("data-lua-kind", kind) :: attrs)
134138
~children:[ highlighted ] ()

src/doc_emit/html_highlight.mli

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(** Highlight a Lua string, rendering it as HTML *)
2-
val lua : options:Html_options.t -> string -> Html.Default.node
2+
val lua : options:Html_options.t -> string -> Illuaminate.Html.node_
33

44
val lua_block :
5-
?attrs:(string * string option) list -> options:Html_options.t -> string -> Html.Default.node
5+
?attrs:(string * string option) list -> options:Html_options.t -> string -> Illuaminate.Html.node_

src/doc_emit/html_loader.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
let load_file ~options path =
2-
let open Html.Default in
2+
let open Illuaminate.Html in
33
match CCIO.File.read (Fpath.to_string path) with
44
| Error msg ->
55
Format.asprintf "Cannot open documentation index '%a' (%s)\n%!" Fpath.pp path msg

src/doc_emit/html_loader.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
(** Load a file, converting it to a HTML node depending on the file's type. *)
2-
val load_file : options:Html_options.t -> Fpath.t -> (Html.Default.node, string) result
2+
val load_file : options:Html_options.t -> Fpath.t -> (Illuaminate.Html.node_, string) result

src/doc_emit/html_main.re

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
open Html.Default;
1+
open Illuaminate.Html;
22
open Html_basic;
33
open Html_md;
44
open Html_value;

src/doc_emit/html_main.rei

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ type page_list :=
77

88
/** Emit an index file from a list of page. */
99
let emit_index:
10-
(~options: Html_options.t, ~pages: page_list, Html.Default.node) =>
11-
Html.Default.node;
10+
(~options: Html_options.t, ~pages: page_list, Illuaminate.Html.node_) =>
11+
Illuaminate.Html.node_;
1212

1313
/** Emit a single page. */
1414
let emit_page:
1515
(~options: Html_options.t, ~pages: page_list, documented(page)) =>
16-
Html.Default.node;
16+
Illuaminate.Html.node_;

0 commit comments

Comments
 (0)