123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198(**************************************************************************)(* *)(* 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. *)(* *)(**************************************************************************)openMigrate_astmoduleT=structtypet=|Commentof{txt:string;loc:Location.t}|Docstringof{txt:string;loc:Location.t}letloc(Comment{loc;_}|Docstring{loc;_})=loclettxt(Comment{txt;_}|Docstring{txt;_})=txtletcreate_commenttxtloc=Comment{txt;loc}letcreate_docstringtxtloc=Docstring{txt;loc}letis_docstring=functionComment_->false|Docstring_->trueletcompare=Poly.compareletsexp_of_tcmt=letkind,txt,loc=matchcmtwith|Comment{txt;loc}->("comment",txt,loc)|Docstring{txt;loc}->("docstring",txt,loc)inSexp.List[Sexp.Atomkind;Sexp.Atomtxt;Sexp.Atom(Format.asprintf"%a"Migrate_ast.Location.fmtloc)]endincludeTincludeComparator.Make(T)typeerror={kind:[`Addedoft|`Modifiedoft*t|`Droppedoft];cmt_kind:[`Comment|`Doc_comment]}letpp_errorfs{kind;cmt_kind}=letpp_cmtfsx=matchcmt_kindwith|`Doc_comment->Format.fprintffs"(** %s *)"(txtx)|`Comment->Format.fprintffs"(* %s *)"(txtx)inlets_kind=matchcmt_kindwith|`Doc_comment->"doc-comment"|`Comment->"comment"inmatchkindwith|`Addedx->Format.fprintffs"%!@{<loc>%a@}:@,@{<error>Error@}: %s %a added.\n%!"Location.print_loc(locx)s_kindpp_cmtx|`Droppedx->Format.fprintffs"%!@{<loc>%a@}:@,@{<error>Error@}: %s %a dropped.\n%!"Location.print_loc(locx)s_kindpp_cmtx|`Modified(x,y)->(Format.fprintffs"%!@{<loc>%a@}:@,\
@{<error>Error@}: formatting of %s is unstable.\n\
\ before: %a\n\
\ after: %a\n\
%!"Location.print_loc(locx)s_kindpp_cmtxpp_cmty;matchcmt_kindwith|`Comment->()|`Doc_comment->Format.fprintffs"Please tighten up this comment in the source or disable the \
formatting using the option --no-parse-docstrings.\n\
%!")typepos=Before|Within|Aftertypedecoded_kind=|Verbatimofstring|Docofstring|Normalofstring|Codeofstring|Asterisk_prefixedofstringlisttypedecoded={prefix:string;suffix:string;kind:decoded_kind}(** [~content_offset] indicates at which column the body of the comment
starts (1-indexed). [~max_idnent] indicates the maximum amount of
indentation to trim. *)letunindent_lines?(max_indent=Stdlib.max_int)~content_offsetfirst_linetl_lines=lettl_indent=List.fold_left~init:max_indent~f:(funaccs->Option.value_map~default:acc~f:(minacc)(String.indent_of_lines))tl_linesin(* The indentation of the first line must account for the location of the
comment opening. Don't account for the first line if it's empty.
[fl_trim] is the number of characters to remove from the first line. *)letfl_trim,fl_indent=matchString.indent_of_linefirst_linewith|Somei->(max0(mini(tl_indent-content_offset)),i+content_offset-1)|None->(String.lengthfirst_line,max_indent)inletmin_indent=mintl_indentfl_indentinletfirst_line=String.drop_prefixfirst_linefl_triminfirst_line::List.map~f:(funs->String.drop_prefixsmin_indent)tl_linesletunindent_lines?max_indent~content_offsettxt=matchString.split~on:'\n'txtwith|[]->[]|hd::tl->unindent_lines?max_indent~content_offsethdtlletis_all_whitespaces=String.for_alls~f:Char.is_whitespaceletsplit_asterisk_prefixed=letprefix="*"inletdrop_prefixs=String.drop_prefixs(String.lengthprefix)inletreclines_are_asterisk_prefixed=function|[]->true(* Allow the last line to be empty *)|[last]whenis_all_whitespacelast->true|hd::tl->String.is_prefixhd~prefix&&lines_are_asterisk_prefixedtlinfunction(* Check whether the second line is not empty to avoid matching a comment
with no asterisks. *)|fst_line::(snd_line::_astl)whenlines_are_asterisk_prefixedtl&¬(is_all_whitespacesnd_line)->Some(fst_line::List.maptl~f:drop_prefix)|_->Noneletmk?(prefix="")?(suffix="")kind={prefix;suffix;kind}letdecode_commenttxtloc=lettxt=(* Windows compatibility *)letf=function'\r'->false|_->trueinString.filtertxt~finletopn_offset=let{Lexing.pos_cnum;pos_bol;_}=loc.Location.loc_startinpos_cnum-pos_bol+1inifString.lengthtxt>=2thenmatchtxt.[0]with|'$'whennot(Char.is_whitespacetxt.[1])->mk(Verbatimtxt)|'$'->letdollar_suf=Char.equaltxt.[String.lengthtxt-1]'$'inletsuffix=ifdollar_sufthen"$"else""inletcode=letlen=String.lengthtxt-ifdollar_sufthen2else1inString.sub~pos:1~lentxtinmk~prefix:"$"~suffix(Codecode)|'='->mk(Verbatimtxt)|_whenis_all_whitespacetxt->mk(Verbatim" ")(* Make sure not to format to [(**)]. *)|_->(letlines=letcontent_offset=opn_offset+2inunindent_lines~content_offsettxtinmatchsplit_asterisk_prefixedlineswith|Somedeprefixed_lines->mk(Asterisk_prefixeddeprefixed_lines)|None->mk(Normaltxt))elsematchtxtwith(* "(**)" is not parsed as a docstring but as a regular comment
containing '*' and would be rewritten as "(***)" *)|"*"whenLocation.widthloc=4->mk(Verbatim"")|("*"|"$")astxt->mk(Verbatimtxt)|"\n"|" "->mk(Verbatim" ")|_->mk(Normaltxt)letdecode_docstring_loc=function|""->mk(Verbatim"")|("*"|"$")astxt->mk(Verbatimtxt)|"\n"|" "->mk(Verbatim" ")|txt->mk~prefix:"*"(Doctxt)letdecode=function|Comment{txt;loc}->decode_commenttxtloc|Docstring{txt;loc}->decode_docstringloctxt