Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file Fmt.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357(**************************************************************************)(* *)(* OCamlFormat *)(* *)(* Copyright (c) Facebook, Inc. and its affiliates. *)(* *)(* This source code is licensed under the MIT license found in *)(* the LICENSE file in the root directory of this source tree. *)(* *)(**************************************************************************)(** Formatting combinators *)moduleFormat=Format_(** Define the core type and minimal combinators.
Other higher level functions like [fmt_if] or [list_pn] are implemented
on top of this. The goal is to be able to modify the underlying
abstraction without modifying too many places in the [Fmt] module. *)moduleT:sigtypetval($):t->t->t(** Sequence *)valwith_pp:(Format.formatter->unit)->t(** Use an arbitrary pretty-printing function *)valprotect:t->on_error:(exn->unit)->t(** Exception handler *)vallazy_:(unit->t)->t(** Defer the evaluation of some side effects until formatting happens.
This can matter if for example a list of [t] is built, and then only
some of them end up being displayed. Using [lazy_] ensures that only
side effects for the displayed elements have run.
See [tests_lazy] in [Test_fmt]. *)valeval:Format.formatter->t->unit(** Main function to evaluate a term using an actual formatter. *)end=structtypet=(Format.formatter->unit)Staged.tlet($)fg=letf=Staged.unstagefinletg=Staged.unstageginStaged.stage(funx->fx;gx)letwith_ppf=Staged.stagefletevalfsf=letf=Staged.unstagefinffsletprotectt~on_error=lett=Staged.unstagetinStaged.stage(funfs->trytfswithexn->Format.pp_print_flushfs();on_errorexn)letlazy_f=Staged.stage(funfs->letk=Staged.unstage(f())inkfs)endincludeTtypes=(unit,Format.formatter,unit)formattypesp=Blank|Cut|Space|Breakofint*intlet(>$)fgx=f$gxletset_marginn=with_pp(funfs->Format.pp_set_geometryfs~max_indent:n~margin:(n+1))letmax_indent=refNoneletset_max_indentn=with_pp(fun_->max_indent:=Somen)(** Debug of formatting -------------------------------------------------*)letpp_color_kcolor_codekfs=letc=Format.sprintf"\x1B[%dm"inFormat.fprintffs"@<0>%s%t@<0>%s"(ccolor_code)k(c0)(** Break hints and format strings --------------------------------------*)letbreakno=with_pp(funfs->Format.pp_print_breakfsno)letcbreak~fits~breaks=with_pp(funfs->Format.pp_print_custom_breakfs~fits~breaks)letnoop=with_pp(fun_->())letsequencel=letrecgollen=matchlwith|[]->noop|[x]->x|l->leta_len=len/2inletb_len=len-a_leninleta,b=List.split_nla_leningoaa_len$gobb_leningol(List.lengthl)letfmtf=with_pp(funfs->Format.fprintffsf)(** Primitive types -----------------------------------------------------*)letcharc=with_pp(funfs->Format.pp_print_charfsc)letutf8_lengths=Uuseg_string.fold_utf_8`Grapheme_cluster(funn_->n+1)0sletstr_asns=with_pp(funfs->Format.pp_print_asfsns)letstrs=ifString.is_emptysthennoopelsestr_as(utf8_lengths)sletsp=function|Blank->char' '|Cut->fmt"@,"|Space->fmt"@ "|Break(x,y)->breakxy(** Primitive containers ------------------------------------------------*)letoptof=Option.value_map~default:noop~foletlist_pnx1Npp=matchx1Nwith|[]->noop|[x1]->lazy_(fun()->pp~prev:Nonex1~next:None)|x1::(x2::_asx2N)->letl=letrecaux(prev,acc)=function|[]->acc|[xI]->aux(xI,(Someprev,xI,None)::acc)[]|xI::(xJ::_asxJN)->aux(xI,(Someprev,xI,SomexJ)::acc)xJNinaux(x1,[(None,x1,Somex2)])x2NinList.rev_mapl~f:(fun(prev,x,next)->lazy_(fun()->pp~prevx~next))|>sequenceletlist_flxspp=list_pnxs(fun~prevx~next->pp~first:(Option.is_noneprev)~last:(Option.is_nonenext)x)letlist_klsepf=list_fll(fun~first:_~lastx->fx$iflastthennoopelsesep)letlistxsseppp=list_kxs(fmtsep)pp(** Conditional formatting ----------------------------------------------*)letfmt_if_kcndx=ifcndthenxelsenoopletfmt_ifcndf=fmt_if_kcnd(fmtf)letfmt_or_kcndtf=ifcndthentelsefletfmt_orcndtf=fmt_or_kcnd(fmtt)(fmtf)letfmt_opto=Option.valueo~default:noop(** Conditional on immediately following a line break -------------------*)letif_newlines=with_pp(funfs->Format.pp_print_string_if_newlinefss)letbreak_unless_newlineno=with_pp(funfs->Format.pp_print_or_newlinefsno"""")(** Conditional on breaking of enclosing box ----------------------------*)typebehavior=Fit|Breakletfits_or_breaks~levelfitsnspacesoffsetbreaks=with_pp(funfs->Format.pp_print_fits_or_breaksfs~levelfitsnspacesoffsetbreaks)letfits_breaks?force?(hint=(0,Int.min_value))?(level=0)fitsbreaks=letnspaces,offset=hintinmatchforcewith|SomeFit->strfits|SomeBreak->fmt_if_k(offset>=0)(breaknspacesoffset)$strbreaks|None->fits_or_breaks~levelfitsnspacesoffsetbreaksletfits_breaks_if?force?hint?levelcndfitsbreaks=fmt_if_kcnd(fits_breaks?force?hint?levelfitsbreaks)(** Wrapping ------------------------------------------------------------*)letwrap_if_kcndpresufk=fmt_if_kcndpre$k$fmt_if_kcndsufletwrap_kx=wrap_if_ktruexletwrap_ifcndpresuf=wrap_if_kcnd(fmtpre)(fmtsuf)andwrappresuf=wrap_k(fmtpre)(fmtsuf)letwrap_if_fits_orcndpresufk=ifcndthenwrap_k(strpre)(strsuf)kelsefits_breakspre""$k$fits_breakssuf""letwrap_fits_breaks_if?(space=true)(c:Conf.t)cndpresufk=match(c.fmt_opts.indicate_multiline_delimiters,space)with|`No,false->wrap_if_kcnd(strpre)(strsuf)k|`Space,_|`No,true->fits_breaks_ifcndpre(pre^" ")$k$fits_breaks_ifcndsuf~hint:(1,0)suf|`Closing_on_separate_line,_->fits_breaks_ifcndpre(pre^" ")$k$fits_breaks_ifcndsuf~hint:(1000,0)sufletwrap_fits_breaks?(space=true)confx=wrap_fits_breaks_if~spaceconftruex(** Boxes ---------------------------------------------------------------*)letbox_debug_enabled=reffalseletwith_box_debugk=letg=!box_debug_enabledinwith_pp(fun_->box_debug_enabled:=true)$k$with_pp(fun_->box_debug_enabled:=g)letbox_depth=ref0(* Numeric part of the ANSI escape sequence for colors *)letbox_depth_colors=[|32;33;94;31;35;36|]letbox_depth_color()=box_depth_colors.(!box_depth%Array.lengthbox_depth_colors)letdebug_box_open?namebox_kindnfs=if!box_debug_enabledthen(letname=matchnamewith|Somes->Format.sprintf"%s:%s"box_kinds|None->box_kindinletopenning=ifn=0thennameelseFormat.sprintf"%s<%d"nameninpp_color_k(box_depth_color())(funfs->Format.fprintffs"@<0>[@<0>%s@<0>>"openning)fs;Int.incrbox_depth)letdebug_box_closefs=if!box_debug_enabledthenif!box_depth=0then(* mismatched close, red background *)pp_color_k41(funfs->Format.fprintffs"@<0>]")fselse(Int.decrbox_depth;pp_color_k(box_depth_color())(funfs->Format.fprintffs"@<0>]")fs)letapply_max_indentn=Option.value_map!max_indent~f:(minn)~default:nletopen_box?namen=with_pp(funfs->letn=apply_max_indentnindebug_box_open?name"b"nfs;Format.pp_open_boxfsn)andopen_vbox?namen=with_pp(funfs->letn=apply_max_indentnindebug_box_open?name"v"nfs;Format.pp_open_vboxfsn)andopen_hvbox?namen=with_pp(funfs->letn=apply_max_indentnindebug_box_open?name"hv"nfs;Format.pp_open_hvboxfsn)andopen_hovbox?namen=with_pp(funfs->letn=apply_max_indentnindebug_box_open?name"hov"nfs;Format.pp_open_hovboxfsn)andclose_box=with_pp(funfs->debug_box_closefs;Format.pp_close_boxfs())(** Wrapping boxes ------------------------------------------------------*)letcbox?namen=wrap_k(open_box?namen)close_boxandvbox?namen=wrap_k(open_vbox?namen)close_boxandhvbox?namen=wrap_k(open_hvbox?namen)close_boxandhovbox?namen=wrap_k(open_hovbox?namen)close_boxandcbox_if?namecndn=wrap_if_kcnd(open_box?namen)close_boxandvbox_if?namecndn=wrap_if_kcnd(open_vbox?namen)close_boxandhvbox_if?namecndn=wrap_if_kcnd(open_hvbox?namen)close_boxandhovbox_if?namecndn=wrap_if_kcnd(open_hovbox?namen)close_box(** Text filling --------------------------------------------------------*)letfill_text?(epi="")text=assert(not(String.is_emptytext));letfmt_lineline=letwords=List.filter~f:(Fn.nonString.is_empty)(String.split_on_charsline~on:['\t';'\n';'\011';'\012';'\r';' '])inlistwords"@ "strinletlines=List.remove_consecutive_duplicates~equal:(funxy->String.is_emptyx&&String.is_emptyy)(String.split(String.rstriptext)~on:'\n')inletpro=ifString.starts_with_whitespacetextthen" "else""inletepi=ifString.lengthtext>1&&String.ends_with_whitespacetextthen" "^epielseepiinstrpro$hvbox0(hovbox0(list_pnlines(fun~prev:_curr~next->fmt_linecurr$matchnextwith|SomestrwhenString.for_allstr~f:Char.is_whitespace->close_box$fmt"\n@,"$open_hovbox0|Some_whennot(String.is_emptycurr)->fmt"@ "|_->noop)$strepi))typecode_formatter=string->tor_error