diff --git a/src/Tiny_httpd_html.ml b/src/Tiny_httpd_html.ml
index 56163f9b..2ebf6b1b 100644
--- a/src/Tiny_httpd_html.ml
+++ b/src/Tiny_httpd_html.ml
@@ -6,10 +6,10 @@ include Tiny_httpd_html_
@param top if true, add DOCTYPE at the beginning. The top element should then
be a "html" tag. *)
let to_string ?(top=false) (self:elt) : string =
- let buf = Buffer.create 256 in
- if top then Printf.bprintf buf "\n";
- self buf;
- Buffer.contents buf
+ let out = Out.create () in
+ if top then Out.add_string out "\n";
+ self out;
+ Out.to_string out
let to_string_top = to_string ~top:true
diff --git a/src/gen/gentags.ml b/src/gen/gentags.ml
index db723a57..a2c8a461 100644
--- a/src/gen/gentags.ml
+++ b/src/gen/gentags.ml
@@ -278,34 +278,74 @@ let attrs = [
]
let prelude = {|
-type attribute = string * string
-type elt = Buffer.t -> unit
-type void = attribute list -> elt
-type nary = attribute list -> elt list -> elt
-type nary' = attribute list -> (Buffer.t -> unit) -> elt
+(** Output for HTML combinators.
-let str_escape (buf:Buffer.t) (s:string) : unit =
+ This output type is used to produce a string reasonably efficiently from
+ a tree of combinators. *)
+module Out : sig
+ type t
+ val create : unit -> t
+ val clear : t -> unit
+ val add_char : t -> char -> unit
+ val add_string : t -> string -> unit
+ val to_string : t -> string
+end = struct
+ include Buffer
+ let create () = Buffer.create 256
+ let to_string = contents
+end
+
+type attribute = string * string
+(** An attribute, i.e. a key/value pair *)
+
+type elt = Out.t -> unit
+(** A html element. It is represented by its output function, so we
+ can directly print it. *)
+
+type void = ?if_:bool -> attribute list -> elt
+(** Element without children. *)
+
+type nary = ?if_:bool -> attribute list -> elt list -> elt
+(** Element with children, represented as a list.
+ @param if_ if false, do not print anything (default true) *)
+
+type nary' = ?if_:bool -> attribute list -> (Out.t -> unit) -> elt
+(** Element with children, represented as a continuation.
+ @param if_ if false, do not print anything (default true) *)
+
+(** Escape string so it can be safely embedded in HTML text. *)
+let str_escape (out:Out.t) (s:string) : unit =
String.iter (function
- | '<' -> Buffer.add_string buf "<"
- | '>' -> Buffer.add_string buf ">"
- | '&' -> Buffer.add_string buf "&"
- | '"' -> Buffer.add_string buf """
- | '\'' -> Buffer.add_string buf "'"
- | c -> Buffer.add_char buf c)
+ | '<' -> Out.add_string out "<"
+ | '>' -> Out.add_string out ">"
+ | '&' -> Out.add_string out "&"
+ | '"' -> Out.add_string out """
+ | '\'' -> Out.add_string out "'"
+ | c -> Out.add_char out c)
s
(** Print the value part of an attribute *)
-let attr_escape buf (s:string) =
- Buffer.add_char buf '"';
- str_escape buf s;
- Buffer.add_char buf '"'
+let attr_escape (out:Out.t) (s:string) =
+ Out.add_char out '"';
+ str_escape out s;
+ Out.add_char out '"'
+
+(** Output a list of attributes. *)
+let write_attrs (out:Out.t ) (l:attribute list) : unit =
+ List.iter
+ (fun (k,v) ->
+ Out.add_char out ' ';
+ Out.add_string out k;
+ Out.add_char out '=';
+ attr_escape out v)
+ l
(** Emit a string value, which will be escaped. *)
-let txt (txt:string) : elt = fun buf -> str_escape buf txt
+let txt (txt:string) : elt = fun out -> str_escape out txt
(** Emit raw HTML. Caution, this can lead to injection vulnerabilities,
never use with text that comes from untrusted users. *)
-let raw_html (s:string) : elt = fun buf -> Buffer.add_string buf s
+let raw_html (s:string) : elt = fun out -> Out.add_string out s
|}
let oname = function
@@ -324,10 +364,11 @@ let emit_void name =
let oname = oname name in
pf "(** tag %S, see {{:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/%s} mdn} *)\n"
name name;
- pf "let %s : void = fun attrs buf ->\n" oname;
- pf " Buffer.add_string buf \"<%s\";\n" name;
- pf " List.iter (fun (k,v) -> Printf.bprintf buf \" %%s=%%a\" k attr_escape v) attrs;\n";
- pf " Buffer.add_string buf \"/>\"";
+ pf "let %s : void = fun ?(if_=true) attrs out ->\n" oname;
+ pf " if if_ then (\n";
+ pf " Out.add_string out \"<%s\";\n" name;
+ pf " write_attrs out attrs;\n";
+ pf " Out.add_string out \"/>\")";
pf "\n\n";
()
@@ -336,24 +377,26 @@ let emit_normal name =
pf "(** tag %S, see {{:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/%s} mdn} *)\n"
name name;
- pf "let %s : nary = fun attrs sub buf ->\n" oname;
- pf " Buffer.add_string buf \"<%s\";\n" name;
- pf " List.iter (fun (k,v) -> Printf.bprintf buf \" %%s=%%a\" k attr_escape v) attrs;\n";
- pf " Buffer.add_string buf \">\\n\";\n";
- pf " List.iter (fun sub -> sub buf; Buffer.add_char buf '\\n') sub;\n";
- pf " Buffer.add_string buf \"%s>\\n\"" name;
+ pf "let %s : nary = fun ?(if_=true) attrs sub out ->\n" oname;
+ pf " if if_ then (\n";
+ pf " Out.add_string out \"<%s\";\n" name;
+ pf " write_attrs out attrs;\n";
+ pf " Out.add_string out \">\\n\";\n";
+ pf " List.iter (fun sub -> sub out; Out.add_char out '\\n') sub;\n";
+ pf " Out.add_string out \"%s>\\n\")" name;
pf "\n\n";
(* block version *)
let oname = oname ^ "'" in
pf "(** tag %S, see {{:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/%s} mdn} *)\n"
name name;
- pf "let %s : nary' = fun attrs f buf ->\n" oname;
- pf " Buffer.add_string buf \"<%s\";\n" name;
- pf " List.iter (fun (k,v) -> Printf.bprintf buf \" %%s=%%a\" k attr_escape v) attrs;\n";
- pf " Buffer.add_string buf \">\\n\";\n";
- pf " f buf;\n";
- pf " Buffer.add_string buf \"%s>\\n\"" name;
+ pf "let %s : nary' = fun ?(if_=true) attrs f out ->\n" oname;
+ pf " if if_ then (\n";
+ pf " Out.add_string out \"<%s\";\n" name;
+ pf " write_attrs out attrs;\n";
+ pf " Out.add_string out \">\\n\";\n";
+ pf " f out;\n";
+ pf " Out.add_string out \"%s>\\n\")" name;
pf "\n\n";