123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438(**************************************************************************)(* *)(* Copyright 2011 Jun Furuse *)(* Copyright 2013 OCamlPro *)(* *)(* All rights reserved.This file is distributed under the terms of the *)(* GNU Lesser General Public License version 2.1 with linking *)(* exception. *)(* *)(* TypeRex is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* Lesser GNU General Public License for more details. *)(* *)(**************************************************************************)openCompattypethreechoices=Always|Never|Autotypet={i_base:int;i_type:int;i_in:int;i_with:int;i_match_clause:int;i_ppx_stritem_ext:int;i_max_indent:intoption;i_strict_with:threechoices;i_strict_else:threechoices;i_strict_comments:bool;i_align_ops:bool;i_align_params:threechoices;}letdefault={i_base=2;i_type=2;i_in=0;i_with=0;i_match_clause=2;i_ppx_stritem_ext=2;i_max_indent=Some4;i_strict_with=Never;i_strict_else=Always;i_strict_comments=false;i_align_ops=true;i_align_params=Auto;}letpresets=["apprentice",{i_base=2;i_type=4;i_in=2;i_with=2;i_match_clause=4;i_ppx_stritem_ext=2;i_max_indent=None;i_strict_with=Never;i_strict_else=Always;i_strict_comments=false;i_align_ops=true;i_align_params=Always};"normal",default;"JaneStreet",{i_base=2;i_type=2;i_in=0;i_with=0;i_match_clause=2;i_ppx_stritem_ext=2;i_max_indent=Some2;i_strict_with=Auto;i_strict_else=Always;i_strict_comments=true;i_align_ops=true;i_align_params=Always};]letthreechoices_of_string=function|"always"->Always|"never"->Never|"auto"->Auto|_->failwith"threechoices_of_string"letstring_of_threechoices=function|Always->"always"|Never->"never"|Auto->"auto"letintoption_of_string=function|"none"|"None"->None|n->trySome(int_of_stringn)withFailure_->failwith"intoption_of_string"letstring_of_intoption=function|Somen->string_of_intn|None->"none"letto_string?(sep=",")indent=Printf.sprintf"base = %d%s\
type = %d%s\
in = %d%s\
with = %d%s\
match_clause = %d%s\
ppx_stritem_ext = %d%s\
max_indent = %s%s\
strict_with = %s%s\
strict_else = %s%s\
strict_comments = %b%s\
align_ops = %b%s\
align_params = %s"indent.i_basesepindent.i_typesepindent.i_insepindent.i_withsepindent.i_match_clausesepindent.i_ppx_stritem_extsep(string_of_intoptionindent.i_max_indent)sep(string_of_threechoicesindent.i_strict_with)sep(string_of_threechoicesindent.i_strict_else)sepindent.i_strict_commentssepindent.i_align_opssep(string_of_threechoicesindent.i_align_params)letset?(extra=fun_->None)tvar_namevalue=trymatchvar_namewith|"base"->{twithi_base=int_of_stringvalue}|"type"->{twithi_type=int_of_stringvalue}|"in"->{twithi_in=int_of_stringvalue}|"with"->{twithi_with=int_of_stringvalue}|"match_clause"->{twithi_match_clause=int_of_stringvalue}|"ppx_stritem_ext"->{twithi_ppx_stritem_ext=int_of_stringvalue}|"max_indent"->{twithi_max_indent=intoption_of_stringvalue}|"strict_with"->{twithi_strict_with=threechoices_of_stringvalue}|"strict_else"->{twithi_strict_else=threechoices_of_stringvalue}|"with_never"->(* backwards compat, don't document *){twithi_strict_with=ifbool_of_stringvaluethenAlwayselseNever}|"strict_comments"->{twithi_strict_comments=bool_of_stringvalue}|"align_ops"->{twithi_align_ops=bool_of_stringvalue}|"align_params"->{twithi_align_params=threechoices_of_stringvalue}|var_name->matchextravar_namewith|Somef->fvalue;t|None->lete=Printf.sprintf"unknown configuration key %S"var_nameinraise(Invalid_argumente)with|Failure"int_of_string"->lete=Printf.sprintf"%s should be an integer, not %S"var_namevalueinraise(Invalid_argumente)|Failure"bool_of_string"->lete=Printf.sprintf"%s should be either \"true\" or \"false\", not %S"var_namevalueinraise(Invalid_argumente)|Failure"threechoices_of_string"->lete=Printf.sprintf"%s should be either \"always\", \"never\" or \"auto\", not %S"var_namevalueinraise(Invalid_argumente)|Failure"intoption_of_string"->lete=Printf.sprintf"%s should be either an integer or \"none\", not %S"var_namevalueinraise(Invalid_argumente)letupdate_from_string?extraindents=List.fold_left(funindents->matchUtil.string_split'='swith|[]|[""]->indent|[var;value]->set?extraindent(String.trimvar)(String.trimvalue)|[preset]->(tryList.assoc(String.trimpreset)presetswithNot_found->lete=Printf.sprintf"unknown preset %S"presetinraise(Invalid_argumente))|_->lete=Printf.sprintf"wrong \"param=value\" pair in %S"sinraise(Invalid_argumente))indent(Util.string_split_chars",\n"s)(* Remember to also document the template configuration file ../.ocp-indent *)typeman_block=[`Sofstring|`Pofstring|`Preofstring|`Iofstring*string|`Noblank|`Blocksofman_blocklist]letman=letoption_namenamekinddefault=Printf.sprintf"$(b,%s)=%s (default=%s)"namekinddefaultinletpres=List.fold_right(funlineacc->leti=ref0andline=Bytes.copylineinwhile!i<Bytes.lengthline&&Bytes.getline(!i)=' 'doBytes.setline(!i)'\xa0';incridone;letline=Bytes.to_stringlinein`Pline::(ifacc=[]then[]else`Noblank::acc))(List.mapBytes.of_string(Util.string_split'\n's))[]in[`P"A configuration definition is a list of bindings in the form \
$(i,NAME=VALUE) or of $(i,PRESET), separated by commas or newlines";`P"Syntax: $(b,[PRESET,]VAR=VALUE[,VAR=VALUE...])"]@`I(option_name"base""INT"(string_of_intdefault.i_base),"Indentation used when none of the following options applies.")::pre" let foo =\n\
\ $(b,..)bar"@`I(option_name"type""INT"(string_of_intdefault.i_type),"Indentation for type definitions.")::pre" type t =\n\
\ $(b,..)int"@`I(option_name"in""INT"(string_of_intdefault.i_in),"Indentation after `let ... in', unless followed by another `let'.")::pre" let foo = () in\n\
\ $(b,..)bar"@`I(option_name"with""INT"(string_of_intdefault.i_with),"Indentation after `match ... with', `try ... with' or `function'.")::pre" match foo with\n\
\ $(b,..)| _ -> bar"@`I(option_name"match_clause""INT"(string_of_intdefault.i_match_clause),"Indentation for clauses inside a pattern-match (after arrows).")::pre" match foo with\n\
\ | _ ->\n\
\ $(b,..)bar"@`I(option_name"ppx_stritem_ext""INT"(string_of_intdefault.i_ppx_stritem_ext),"Indentation for items inside a [%%id ... ] extension node).")::pre" [%% id.id\n\
\ $(b,..)let x = 3\
\ ]"@`I(option_name"max_indent""<INT|none>"(string_of_intoptiondefault.i_max_indent),"When nesting expressions on the same line, their indentations are \
stacked in some cases so that they remain correct if you close them \
one per line. However, this can lead to large indentations in complex \
code, so this parameter sets a maximum indentation. Note that it \
only affects indentation after function arrows and opening parens at \
the ends of lines.")::pre" let f = g (h (i (fun x ->\n\
\ $(b,....)x)\n\
\ )\n\
\ )"@`I(option_name"strict_with""<always|never|auto>"(string_of_threechoicesdefault.i_strict_with),"If `never', match bars are indented, superseding `with', \
whenever `match with' doesn't start its line.\n\
If `auto', there are exceptions for constructs like \
`begin match with'.\n\
If `always', `with' is always strictly respected, and additionally \
applies to variant types definition, for consistency.")::pre" Example with `strict_with=$(b,never),with=0':\n\
\ begin match foo with\n\
\ $(b,..)| _ -> bar\n\
\ end"@`I(option_name"strict_else""<always|never|auto>"(string_of_threechoicesdefault.i_strict_else),"If `always', indent after the `else' keyword normally, like after \
`then'.\n\
If `auto', indent after `else' unless in a few \
\"unclosable\" cases (`let .... in', `match', etc.).\n\
If `never', the `else' keyword won't indent when followed \
by a newline.")::pre" Example with `strict_else=$(b,auto)':\n\
\ if cond then\n\
\ foo\n\
\ else\n\
\ $(b,let) x = bar in\n\
\ baz"@`I(option_name"strict_comments""BOOL"(string_of_booldefault.i_strict_comments),"In-comment indentation is normally preserved, as long as it respects \
the left margin or the comments starts with a newline. Setting this \
to `true' forces alignment within comments. Lines starting with `*' \
are always aligned")::[]@`I(option_name"align_ops""BOOL"(string_of_booldefault.i_align_ops),"Toggles preference of column-alignment over line indentation for most \
of the common operators and after mid-line opening parentheses.")::pre" Example with `align_ops=$(b,true)':\n\
\ let f x = x\n\
\ + y\n\
\ \n\
\ Example with `align_ops=$(b,false)':\n\
\ let f x = x\n\
\ + y"@`I(option_name"align_params""<always|never|auto>"(string_of_threechoicesdefault.i_align_params),"If `never', function parameters are indented one level from the \
line of the function. \
If `always', they are aligned from the column of the function. \
if `auto', alignment is chosen over indentation in a few cases, e.g. \
after match arrows")::pre" Example with `align_params=$(b,never)':\n\
\ match foo with\n\
\ | _ -> some_fun\n\
\ $(b,..)parameter\n\
\ \n\
\ Example with `align_params=$(b,always)' or `$(b,auto)':\n\
\ match foo with\n\
\ | _ -> some_fun\n\
\ $(b,..)parameter"@[`P"Available presets are `normal', the default, `apprentice' which may \
make some aspects of the syntax more obvious for beginners, and \
`JaneStreet'."]letsavetfile=tryletoc=open_outfileinoutput_stringoc(to_string~sep:"\n"t);output_charoc'\n';truewithSys_error_->Printf.eprintf"ocp-indent warning: could not open %S for writing configuration.\n%!"file;falseletsyntax_extsyntax_list_refdynlink_list_ref=function|"syntax"->Some(funsyntaxes->List.iter(funsyn->(* if List.mem syn (IndentExt.available ()) then *)syntax_list_ref:=syn::!syntax_list_ref(* else *)(* let e = Printf.sprintf "unknown syntax extension %S" syn in *)(* raise (Invalid_argument e) *))(Util.string_split' 'syntaxes))|"load"->Some(funpkgs->List.iter(funs->letdl=ifFilename.check_suffixs".cmo"||Filename.check_suffixs".cma"||Filename.check_suffixs".cmxs"then`Modselse`Pkgsindynlink_list_ref:=dl::!dynlink_list_ref)(Util.string_split' 'pkgs))|_->Noneletload?(indent=default)file=tryletic=open_infileinletcontents=letb=Buffer.create512intrywhiletruedolets=input_lineicinletn=tryString.indexs'#'withNot_found->String.lengthsinBuffer.add_substringbs0n;Buffer.add_charb'\n'done;assertfalsewithEnd_of_file->close_inic;Buffer.contentsbinletexts=ref[]inletdynlink=ref[]inlett=update_from_string~extra:(syntax_extextsdynlink)indentcontentsint,!exts,!dynlinkwith|Sys_error_->Printf.eprintf"ocp-indent warning: could not open %S for reading configuration.\n%!"file;indent,[],[]|Invalid_argumenterr->Printf.eprintf"ocp-indent warning: error in configuration file %S:\n%s\n%!"fileerr;default,[],[]letconf_file_name=".ocp-indent"letrecfind_conf_filepath=let(/)=Filename.concatinifSys.file_exists(path/conf_file_name)thenSome(path/conf_file_name)elseletpath=ifFilename.is_relativepaththenSys.getcwd()/pathelsepathinletparent=Filename.dirnamepathinifparent<>paththenfind_conf_fileparentelseNoneletlocal_default?(path=Sys.getcwd())()=letconf=defaultinletconf,syn,dlink=trylet(/)=Filename.concatinletxdg_path=(matchSys.getenv"XDG_CONFIG_HOME"with|""->(Sys.getenv"HOME")/".config"|exceptionNot_found->(Sys.getenv"HOME")/".config"|x->x)/"ocp"/"ocp-indent.conf"inifSys.file_existsxdg_paththenload~indent:confxdg_pathelseletlegacy_path=(Sys.getenv"HOME")/".ocp"/"ocp-indent.conf"inifSys.file_existslegacy_paththenload~indent:conflegacy_pathelseconf,[],[]withNot_found->conf,[],[]inletconf,syn,dlink=matchfind_conf_filepathwith|Somec->letconf,syn1,dlink1=load~indent:confcinconf,syn1@syn,dlink1@dlink|None->conf,syn,dlinkinletconf=tryupdate_from_stringconf(Sys.getenv("OCP_INDENT_CONFIG"))with|Not_found->conf|Invalid_argument_->prerr_endline"Warning: invalid $OCP_INDENT_CONFIG";confinconf,syn,dlink