module Html : sig type t val string_of_list : t list -> string type attr val a_class : string list -> attr val code : a:attr list -> t list -> t val span : a:attr list -> t list -> t val div : a:attr list -> t list -> t val txt : string -> t module Unsafe : sig val data : string -> t end end = struct type t = Raw of string | Txt of string | Concat of t list let add_escape_string buf s = (* https://discuss.ocaml.org/t/html-encoding-of-string/4289/4 *) let add = Buffer.add_string buf in let len = String.length s in let max_idx = len - 1 in let flush start i = if start < len then Buffer.add_substring buf s start (i - start) in let rec loop start i = if i > max_idx then flush start i else match String.get s i with | '&' -> escape "&" start i | '<' -> escape "<" start i | '>' -> escape ">" start i | '\'' -> escape "'" start i | '"' -> escape """ start i | '@' -> escape "@" start i | _ -> loop start (i + 1) and escape amperstr start i = flush start i; add amperstr; let next = i + 1 in loop next next in loop 0 0 let to_string t = let buf = Buffer.create 16 in let rec go = function | Raw s -> Buffer.add_string buf s | Txt s -> add_escape_string buf s | Concat xs -> List.iter go xs in go t; Buffer.contents buf let string_of_list lst = to_string (Concat lst) type attr = t let a_class lst = Concat [ Raw "class=\""; Txt (String.concat " " lst); Raw "\"" ] let attrs = function [] -> Concat [] | xs -> Concat (Raw " " :: xs) let block name ~a body = let name = Raw name in Concat [ Raw "<"; name; attrs a; Raw ">"; Concat body; Raw "" ] let code = block "code" let span = block "span" let div = block "div" let txt s = Txt s module Unsafe = struct let data s = Raw s end end let of_strings ~kind ~prefix_name ~name ~rhs ~typedecl_params ~doc = let open Html in let kind = code ~a:[ a_class [ "entry-kind" ] ] [ txt kind ] and typedecl_params = match typedecl_params with | None -> [] | Some p -> [ span ~a: [ a_class [ (* the parameter of the typedecl are highlighted as if part of main entry name. *) "entry-name"; ]; ] [ txt (p ^ " ") ]; ] and prefix_name = match prefix_name with | None -> [] | Some "" -> [] | Some prefix_name -> [ span ~a:[ a_class [ "prefix-name" ] ] [ txt prefix_name ] ] and name = match name with | Some name -> [ span ~a:[ a_class [ "entry-name" ] ] [ txt name ] ] | None -> [] and rhs = match rhs with | None -> [] | Some rhs -> [ code ~a:[ a_class [ "entry-rhs" ] ] [ txt rhs ] ] in Html.string_of_list [ kind; code ~a:[ a_class [ "entry-title" ] ] (typedecl_params @ prefix_name @ name @ rhs); div ~a:[ a_class [ "entry-comment" ] ] [ Unsafe.data doc ]; ] let kind_doc = "doc" let kind_typedecl = "type" let kind_module = "mod" let kind_exception = "exn" let kind_class_type = "class" let kind_class = "class" let kind_method = "meth" let kind_extension_constructor = "cons" let kind_module_type = "sig" let kind_constructor = "cons" let kind_field = "field" let kind_value = "val" let kind_extension = "ext"