123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116openCommon(*****************************************************************************)(* The data structure *)(*****************************************************************************)typeoutline_node={stars:string;title:string;before_first_children:stringlist;}typeoutline=outline_nodeCommon2.tree2letoutline_default_regexp="^\\(\\*+\\)[ ]*\\(.*\\)"letroot_stars=""letroot_title="__ROOT__"(*****************************************************************************)(* Helpers, accessors *)(*****************************************************************************)letis_root_nodenode=String.lengthnode.stars=0&&node.title=root_titleletextract_outline_line?(outline_regexp=outline_default_regexp)s=ifs=~outline_regexpthenmatched2selsefailwith(spf"line does not match regexp: %s vs %s"soutline_regexp)(*****************************************************************************)(* Loading, saving *)(*****************************************************************************)(* Similar to parenthesizd expression parsing, or ifdef parsing as
* in parsing_hacks, but a little different cos don't have the
* end delimiter in most cases. The end delimiter is in fact
* the start of a new header or the end of the file.
*)letparse_outline?(outline_regexp=outline_default_regexp)file=letxs=Common.catfilein(* just differentiate outline lines from regular lines *)letheaders_or_not=xs|>List.map(funs->ifs=~outline_regexpthenlet(stars,line)=extract_outline_line~outline_regexpsinLeft(String.lengthstars,stars,line)elseRights)inletroot=(0,root_stars,root_title)in(* pack the Right with each appropriate Left *)letheaders=letrecaux(acc_right,outline)xs=matchxswith|[]->[(outline,List.revacc_right)]|x::xs->(matchxwith|Rightregular->aux(regular::acc_right,outline)xs|Leftoutline2->(outline,List.revacc_right)::aux([],outline2)xs)inaux([],root)headers_or_notin(* build the tree *)lettrees=letrecaux_outlinexs=matchxswith|[]->[]|x::xs->let((lvl,stars,title),before_first_children)=xinlet(children,rest)=xs|>Common2.span(funx2->let((lvl2,_,_),_)=x2inlvl2>lvl)inletnode={stars=stars;title=title;before_first_children=before_first_children;}inletchildren_trees=aux_outlinechildrenin(Common2.Tree(node,children_trees))::aux_outlinerestinaux_outlineheadersinmatchtreeswith|[root]->root|_->failwith"wierd, multiple roots"letwrite_outlineoutlinefile=Common.with_open_outfilefile(fun(pr_no_nl,_chan)->letprs=pr_no_nl(s^"\n")inoutline|>Common2.tree2_iter(funnode->ifnot(is_root_nodenode)thenpr(node.stars^node.title);node.before_first_children|>List.iterpr;);)