123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188openImporttypeinline=Doc_types.inline=|Textofstring|Codeofstringtypeblock=Doc_types.block=|Paragraphofinlinelist|Preofstringtypedoc=blocklist(* Parse and print ATD's own "text" format *)moduleText=structletparselocs=try(Doc_lexer.parse_strings:blocklist)withe->failwith(Printf.sprintf"%s:\nInvalid format for doc.text %S:\n%s"(Ast.string_of_locloc)s(Printexc.to_stringe))(*
Escape as little as we can get away with depending on the context:
- always: \ -> \\
- normal text mode: {{ -> \{\{
- code: }} -> \}\}
- pre: }}} -> \}\}\}
*)letescape_text_re=Re.Pcre.regexp{|\{\{\|\\|}letescape_code_re=Re.Pcre.regexp{|\}\}|\\|}letescape_pre_re=Re.Pcre.regexp{|\}\}\}|\\|}letescape_texts=Re.Pcre.substitute~rex:escape_text_re~subst:(function|"{{"->{|\{\{|}|{|\|}->{|\\|}|s->s(* bug *))sletescape_codes=Re.Pcre.substitute~rex:escape_code_re~subst:(function|"}}"->{|\}\}|}|{|\|}->{|\\|}|s->s(* bug *))sletescape_pres=Re.Pcre.substitute~rex:escape_pre_re~subst:(function|"}}}"->{|\}\}\}|}|{|\|}->{|\\|}|s->s(* bug *))sletcompact_whitespace=letrex=Re.Pcre.regexp"(?: \t\r\n)+"infuns->Re.Pcre.substitute~rex~subst:(fun_->" ")s(* - remove leading and trailing whitespace
- turn inner whitespace sequences into a single space *)letnormalize_inlines=s|>String.trim|>compact_whitespaceletconcat_nonemptysepxs=xs|>List.filter((<>)"")|>String.concatsepletprint_inline(x:Doc_types.inline)=matchxwith|Texts->s|>normalize_inline|>escape_text|Codes->matchs|>normalize_inline|>escape_codewith|""->""|s->letfirst_space=ifs.[0]='{'then" "else""inletlast_space=ifs.[String.lengths-1]='}'then" "else""insprintf"{{%s%s%s}}"first_spaceslast_spaceletprint_block(x:Doc_types.block)=matchxwith|Paragraphxs->xs|>List.mapprint_inline|>concat_nonempty" "|Pres->letcontent=escape_presinmatchcontentwith|""->""|s->letfirst_newline=ifs.[0]<>'\n'then"\n"else""inletlast_newline=ifs.[String.lengths-1]<>'\n'then"\n"else""insprintf"{{{%s%s%s}}}"first_newlineslast_newlineletprint_blocksblocks=blocks|>List.mapprint_block|>String.concat"\n\n"endletparse_text=Text.parseletprint_text=Text.print_blocks(*
This must hold all the valid annotations of the form
'<doc ...>'.
*)letannot_schema:Annot.schema=[{section="doc";fields=[Module_head,"text";Type_def,"text";Variant,"text";Field,"text";(* Tolerate but deprecate?
Type_expr, "text"; *)]}]letget_doclocan:docoption=Annot.get_opt_field~parse:(funs->Some(parse_textlocs))~sections:["doc"]~field:"text"an(* Conversion to HTML *)lethtml_escapebufs=String.iter(function'<'->Buffer.add_stringbuf"<"|'>'->Buffer.add_stringbuf">"|'&'->Buffer.add_stringbuf"&"|'"'->Buffer.add_stringbuf"""|c->Buffer.add_charbufc)sletprint_inlinebuf=function|Texts->html_escapebufs|Codes->bprintfbuf"<code>%a</code>"html_escapeslethtml_of_docblocks=letbuf=Buffer.create300inbprintfbuf"\n<div class=\"atd-doc\">\n";List.iter(function|Paragraphl->Buffer.add_stringbuf"<p>\n";List.iter(print_inlinebuf)l;Buffer.add_stringbuf"\n</p>\n"|Pres->Buffer.add_stringbuf"<pre>\n";html_escapebufs;Buffer.add_stringbuf"</pre>\n")blocks;bprintfbuf"\n</div>\n";Buffer.contentsbuf