123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282moduleEasy_format=Reason_easy_formattypebreak_criterion=|Never|IfNeed|Always(* Always_rec not only will break, it will break recursively up to the root *)|Always_rec(* Modeling separators: Special ability to render the final separator
distinctly. This is so we can replace them when they do/don't occur next to
newlines.
If sepLeft:true { final item1 sep item2 sep item3 }
If sepLeft:false { item1 sep item2 sep item3 final } *)(* You can't determine the final separator unless you specify a separator *)typeseparator=|NoSep|Sepofstring|SepFinalofstring*string(** * Module concerning info to correctly interleave whitespace above a layout
node. *)moduleWhitespaceRegion=structtypet={(* range of the region *)range:Reason_location.Range.t;(* inserted comments into the whitespace region *)comments:Reason_comment.tlist;(* amount of newlines to be interleaved *)newlines:int}letmake~range~newlines()={range;comments=[];newlines}letnewlinest=t.newlinesletranget=t.rangeletcommentst=t.commentsletaddCommenttcomment={twithcomments=comment::t.comments}letmodifyNewlinestnewNewlines={twithnewlines=newNewlines}end(** * These represent "intent to format" the AST, with some parts being
annotated * with original source location. The benefit of tracking this in
an * intermediate structure, is that we can then interleave comments
throughout * the tree before generating the final representation. That
prevents the * formatting code from having to thread comments everywhere. *
* The final representation is rendered using Easy_format. *)typet=|SourceMapofLocation.t*t(* a layout with location info *)|Sequenceofconfig*tlist|Labelof(Easy_format.t->Easy_format.t->Easy_format.t)*t*t|EasyofEasy_format.t(* Extra variant representing "intent to interleave whitespace" above a
* layout node. Why the extra representation?
* Since comments get interleaved after formatting the ast,
* the inserting of actual newlines has to happen after the comments
* have been formatted/inserted. *)|WhitespaceofWhitespaceRegion.t*tandconfig={break:break_criterion;(* Break setting that becomes activated if a comment becomes interleaved into
* this list. Typically, if not specified, the behavior from [break] will be
* used.
*)wrap:string*string;inline:bool*bool;sep:separator;indent:int;sepLeft:bool;preSpace:bool;(* Really means space_after_separator *)postSpace:bool;pad:bool*bool;(* A function, because the system might rearrange your previous settings, and
* a function allows you to not be locked into some configuration that is made
* out of date by the formatting system (suppose it removes the separator
* token etc.) Having a function allows you to instruct our formatter how to
* extend the "freshest" notion of the list config when comments are
* interleaved. *)listConfigIfCommentsInterleaved:(config->config)option;(* Formatting to use if an item in a list had an end-of-line comment
appended *)listConfigIfEolCommentsInterleaved:(config->config)option}letstring_of_easy=function|Easy_format.Atom(s,_)->s|Easy_format.List(_,_)->"list"|Easy_format.Label(_,_)->"label"|Easy_format.Custom_->"custom"letindent_moreindent=" "^indentletdump_easyppfeasy=letprintffmt=Format.fprintfppffmtinletrectraverseindent=function|Easy_format.Atom(s,_)->printf"%s Atom:'%s'\n"indents|Easy_format.List((opening,sep,closing,config),items)->letbreak=matchconfig.wrap_bodywith|`No_breaks->"No_breaks"|`Wrap_atoms->"Wrap_atoms"|`Never_wrap->"Never_wrap"|`Force_breaks->"Force_breaks"|`Force_breaks_rec->"Force_breaks_rec"|`Always_wrap->"Always_wrap"inprintf"%s List: open %s close %s sep %s break %s \n"indentopeningclosingsepbreak;let_=List.map(traverse(indent_moreindent))itemsin()|Easy_format.Label((left,config),right)->letbreak=matchconfig.label_breakwith|`Never->"Never"|`Always_rec->"Always_rec"|`Auto->"Auto"|`Always->"Always"inprintf"%s Label (break = %s): \n"indentbreak;printf" %s left \n"indent;letindent'=indent_moreindentintraverseindent'left;printf" %s right \n"indent;traverseindent'right|Easy_format.Custom_->printf"custom \n"intraverse""easyletdumpppflayout=letprintffmt=Format.fprintfppffmtinletrectraverseindent=function|SourceMap(loc,layout)->printf"%s SourceMap [(%d:%d)-(%d:%d)]\n"indentloc.loc_start.Lexing.pos_lnum(loc.loc_start.Lexing.pos_cnum-loc.loc_start.Lexing.pos_bol)loc.loc_end.Lexing.pos_lnum(loc.loc_end.Lexing.pos_cnum-loc.loc_end.Lexing.pos_bol);traverse(indent_moreindent)layout|Sequence(config,layout_list)->letbreak=matchconfig.breakwith|Never->"Never"|IfNeed->"if need"|Always->"Always"|Always_rec->"Always_rec"inletsep=matchconfig.sepwith|NoSep->"NoSep"|Seps->"Sep '"^s^"'"|SepFinal(s,finalSep)->"SepFinal ('"^s^"', '"^finalSep^"')"inprintf"%s Sequence of %d, sep: %s, stick_to_left: %s break: %s\n"indent(List.lengthlayout_list)sep(string_of_boolconfig.sepLeft)break;List.iter(traverse(indent_moreindent))layout_list|Label(_,left,right)->printf"%s Label: \n"indent;printf" %s left \n"indent;letindent'=indent_more(indent_moreindent)intraverseindent'left;printf" %s right \n"indent;traverseindent'right|Easye->printf"%s Easy: '%s' \n"indent(string_of_easye)|Whitespace(region,sublayout)->printf" %s Whitespace (%d) [%d %d]:\n"indentregion.newlinesregion.range.lnum_startregion.range.lnum_end;traverse(indent_moreindent)sublayoutintraverse""layoutletsource_map?(loc=Location.none)layout=ifloc=Location.nonethenlayoutelseSourceMap(loc,layout)letdefault_list_settings={Easy_format.space_after_opening=false;space_after_separator=false;space_before_separator=false;separators_stick_left=true;space_before_closing=false;stick_to_label=true;align_closing=true;wrap_body=`No_breaks;indent_body=0;list_style=Some"list";opening_style=None;body_style=None;separator_style=None;closing_style=None}leteasy_settings_from_config{break;wrap;inline;indent;preSpace;postSpace;pad;sep;_}=(* TODO: Stop handling separators in Easy_format since we handle most of them
before Easy_format anyways. There's just some that we still rely on
Easy_format for. Easy_format's sep wasn't powerful enough. *)letopn,cls=wrapinletpadOpn,padCls=padinletinlineStart,inlineEnd=inlineinletsepStr=matchsepwithNoSep->""|Seps|SepFinal(s,_)->sin(opn,sepStr,cls,{default_list_settingswithEasy_format.wrap_body=(matchbreakwith|Never->`No_breaks(* Yes, `Never_wrap is a horrible name - really means "if needed". *)|IfNeed->`Never_wrap|Always->`Force_breaks|Always_rec->`Force_breaks_rec);indent_body=indent;space_after_separator=postSpace;space_before_separator=preSpace;space_after_opening=padOpn;space_before_closing=padCls;stick_to_label=inlineStart;align_closing=notinlineEnd})letto_easy_formatlayout=letrectraverse=function|Sequence(config,sublayouts)->letitems=List.maptraversesublayoutsinEasy_format.List(easy_settings_from_configconfig,items)|Label(labelFormatter,left,right)->labelFormatter(traverseleft)(traverseright)|SourceMap(_,subLayout)->traversesubLayout|Easye->e|Whitespace(_,subLayout)->traversesubLayoutintraverselayout(** [getLocFromLayout] recursively takes the unioned location of its children, *
and returns the max one *)letget_locationlayout=letunionloc1loc2=matchloc1,loc2with|None,_->loc2|_,None->loc1|Someloc1,Someloc2->Some{loc1withLocation.loc_end=loc2.Location.loc_end}inletrectraverse=function|Sequence(_,subLayouts)->letlocs=List.maptraversesubLayoutsinList.fold_leftunionNonelocs|Label(_,left,right)->union(traverseleft)(traverseright)|SourceMap(loc,_)->Someloc|Whitespace(_,sub)->traversesub|_->Noneintraverselayoutletis_before~locationlayout=matchget_locationlayoutwith|None->true|Someloc->Reason_syntax_util.location_is_beforeloclocationletcontains_locationlayout~location=matchget_locationlayoutwith|None->false|Somelayout_loc->Reason_syntax_util.location_containslayout_loclocation