123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997(*
* uTop_complete.ml
* ----------------
* Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
* Licence : BSD3
*
* This file is a part of utop.
*)[@@@warning"-9-27-32"]openTypesopenLTerm_read_lineopenUTop_compatopenUTop_tokenmoduleString_set=Set.Make(String)moduleString_map=Map.Make(String)letset_of_list=List.fold_left (funsetx->String_set.addxset)String_set.empty(* +-----------------------------------------------------------------+
| Utils |
+-----------------------------------------------------------------+ *)(* Transform a non-empty list of strings into a long-identifier. *)letlongident_of_list=function|[]->invalid_arg "UTop_complete.longident_of_list"|component::rest->letrecloopacc=function|[]->acc|component::rest->loop(Longident.Ldot(acc,component))restinloop(Longident.Lidentcomponent)rest(* Check whether an identifier is a valid one. *)letis_valid_identifierid=id<>""&&(matchid.[0]with|'A'..'Z'|'a'..'z'|'_'->true|_->false)letaddidset=ifis_valid_identifieridthen String_set.addidsetelsesetletlookup_envfxenv =trySome(fxenv)with Not_found|Env.Error_->None(* +-----------------------------------------------------------------+
| Parsing |
+-----------------------------------------------------------------+ *)(* The following functions takes a list of tokens in reverse order. *)typevalue_or_field=Value|Field(* Either a value,or a record field. *)(* Parse something of the form [M1.M2. ... .Mn.id] or
[field.M1.M2. ... .Mn.id] *)letparse_longidenttokens=letrecloopacctokens =matchtokenswith|(Symbol".",_):: (Uidentid,_)::tokens->loop(id::acc)tokens|(Symbol".",_)::(Lident id,_)::tokens->(Field,matchaccwith|[]->None|l->Some(longident_of_listl))|_->(Value,matchaccwith|[]->None|l->Some(longident_of_listl))inmatchtokenswith|((Comment(_,false)|String(_,false)|Quotation(_,false)),_)::_->(* An unterminated command, string, or quotation. *)None|((Uidentid|Lidentid),{idx1=start})::tokens->(* An identifier. *)letkind,path=loop[]tokensinSome (kind,path,start,id)|(Blanks,{idx2 =stop })::tokens->(* Some blanks at the end. *)letkind,path=loop[]tokensinSome (kind,path,stop,"")|(_,{idx2 =stop})::_->(* Otherwise complete after the last token. *)letkind,path=loop[]tokensinSome (kind,path,stop,"")|[]->None(* Parse something of the form [M1.M2. ... .Mn.id#m1#m2# ... #mp#m] *)letparse_methodtokens=(* Collect [M1.M2. ... .Mn.id] and returns the corresponding
longidentifier. *)letrecloop_uidentsacctokens=matchtokenswith|(Symbol".",_):: (Uidentid,_)::tokens->loop_uidents(id::acc)tokens|_->longident_of_listaccin(* Collect [m1#m2# ... #mp] *)letrecloop_methodsacctokens=matchtokenswith|(Lidentmeth,_)::(Symbol"#",_)::tokens->loop_methods(meth::acc)tokens|(Lidentid,_)::tokens->Some(loop_uidents[id]tokens,acc)|_->Noneinmatchtokenswith|(Lidentmeth,{idx1 =start})::(Symbol"#",_)::tokens->beginmatchloop_methods[]tokenswith|None ->None|Some(path,meths)->Some(path,meths,start,meth)end|(Symbol"#",{idx2=stop})::tokens|(Blanks,{idx2=stop})::(Symbol"#",_)::tokens->beginmatchloop_methods[]tokenswith|None ->None|Some(path,meths)->Some(path,meths,stop,"")end|_->Nonetypelabel_kind=Required|Optional(* Kind of labels: required or optional. *)typefun_or_new=Fun|New(* Either a functionapplication, either an object creation. *)(* Parse something of the form [M1.M2. ... .Mn.id#m1#m2# ... #mp expr1 ... exprq ~label]
or [new M1.M2. ... .Mn.id expr1 ... exprq ~label] *)letparse_labeltokens=(* Collect [M1.M2. ... .Mn] *)letrecloop_uidentsacc_uidentsacc_methodstokens=matchtokenswith|(Lident"new",_)::_->Some(New,longident_of_listacc_uidents,acc_methods)|((Lidentid|Uidentid),_)::_whenString_set.memid!UTop.keywords->Some(Fun,longident_of_list acc_uidents,acc_methods)|(Symbol".",_)::(Uidentid,_)::tokens->loop_uidents(id::acc_uidents)acc_methods tokens|(Symbol ("~"|"?"|":"|"."|"#"|"!"|"`"),_)::tokens->searchtokens|(Symbol")",_)::tokens ->skiptokens"("[]|(Symbol"}",_)::tokens->skiptokens"{"[]|(Symbol"]",_)::tokens->skiptokens"["[]|(Symbol_,_):: _->Some(Fun,longident_of_listacc_uidents,acc_methods)|[]->Some(Fun,longident_of_listacc_uidents,acc_methods)|_->searchtokensandloop_methodsacctokens=matchtokenswith|((Lidentid|Uident id),_)::_whenString_set.memid!UTop.keywords->None|(Symbol ("~"|"?"|":"|"."|"#"|"!"|"`"),_)::tokens->searchtokens|(Symbol")",_)::tokens ->skiptokens"("[]|(Symbol"}",_)::tokens->skiptokens"{"[]|(Symbol"]",_)::tokens->skiptokens"["[]|(Symbol_,_):: _->None|(Lidentid,_)::(Symbol"#",_)::tokens->loop_methods(id::acc)tokens|(Lidentid,_)::tokens->loop_uidents[id]acctokens|[]->None|_->searchtokensandsearchtokens=match tokenswith|((Lidentid|Uident id),_)::_whenString_set.memid!UTop.keywords->None|(Symbol ("~"|"?"|":"|"."|"#"|"!"|"`"),_)::tokens->searchtokens|(Symbol")",_)::tokens ->skiptokens"("[]|(Symbol"}",_)::tokens->skiptokens"{"[]|(Symbol"]",_)::tokens->skiptokens"["[]|(Symbol_,_):: _->None|(Lidentid,_)::(Symbol"#",_)::tokens->loop_methods[id]tokens|(Lidentid,_)::tokens->loop_uidents[id][]tokens|_::tokens->searchtokens|[]->Noneandskiptokenstopstack=matchtokenswith|(Symbolsymbol,_)::tokenswhensymbol=top->beginmatchstackwith|[]->searchtokens|top::stack ->skiptokenstopstackend|(Symbol")",_)::tokens->skiptokens"("(top::stack)|(Symbol "}",_)::tokens ->skiptokens"{"(top::stack)|(Symbol "]",_)::tokens ->skiptokens"["(top::stack)|_:: tokens->skiptokenstopstack|[]->Noneinmatchtokenswith|(Lidentlabel,{idx1 =start})::(Symbol"~",_)::tokens->beginmatchsearchtokenswith|None->None|Some(kind,id,meths)->Some(kind,id,meths,Required,start,label)end|(Symbol"~",{idx2=stop})::tokens->beginmatchsearchtokenswith|None->None|Some(kind,id,meths)->Some(kind,id,meths,Required,stop,"")end|(Lidentlabel,{idx1=start})::(Symbol"?",_)::tokens->beginmatchsearchtokenswith|None->None|Some(kind,id,meths)->Some(kind,id,meths,Optional,start,label)end|(Symbol"?",{idx2=stop})::tokens->beginmatchsearchtokenswith|None->None|Some(kind,id,meths)->Some(kind,id,meths,Optional,stop,"")end|_->None(* +-----------------------------------------------------------------+
| Directive listing |
+-----------------------------------------------------------------+ *)letlist_directivesphrase_terminator=String_map.bindings(List.fold_left(funmapdir->letsuffix=matchtoploop_get_directivedirwith|Some(Toploop.Directive_none_)->phrase_terminator|Some(Toploop.Directive_string_)->" \""|Some(Toploop.Directive_bool_|Toploop.Directive_int_|Toploop.Directive_ident_)->" "|None->assertfalseinString_map.adddirsuffixmap)String_map.empty(toploop_all_directive_names()))(* +-----------------------------------------------------------------+
| File listing |
+-----------------------------------------------------------------+ *)typefile_kind=Directory|Fileletbasenamename=letname'=Filename.basenamenameinifname'="."&¬(Zed_utf8.ends_withname".")then""elsename'letadd_filesfilteraccdir=Array.fold_left(fun map name->letabsolute_name=Filename.concatdirnameiniftrySys.is_directoryabsolute_namewithSys_error_->falsethenString_map.add(Filename.concatname"")DirectorymapelseiffilternamethenString_map.addnameFilemapelsemap)acc(trySys.readdirdirwithSys_error_->[||])letlist_directoriesdir=String_set.elements(Array.fold_left(funsetname->letabsolute_name=Filename.concatdirnameiniftrySys.is_directory absolute_namewithSys_error_->falsethenString_set.addnamesetelseset)String_set.empty(trySys.readdir(ifdir=""thenFilename.current_dir_nameelsedir)withSys_error_->[||]))let path()=letpath_separator=matchSys.os_typewith|"Unix"|"Cygwin"-> ':'|"Win32"->';'|_->assertfalseinletsplitstrsep=letrecsplit_recpos=ifpos>=String.length strthen[]elsebeginmatchtrySome(String.index_fromstrpossep)withNot_found ->Nonewith|Somenewpos->String.substrpos (newpos-pos)::split_rec (newpos+1)|None->[String.substrpos(String.lengthstr-pos)]endinsplit_rec 0intrysplit(Sys.getenv"PATH")path_separatorwithNot_found->[](* +-----------------------------------------------------------------+
| Names listing |
+-----------------------------------------------------------------+ *)modulePath_map=Map.Make(structtypet=Path.tletcompare =compareend)moduleLongident_map=Map.Make(structtypet=Longident.tletcompare=compareend)(* All names accessible without a path. *)letglobal_names=refNoneletglobal_names_revised=refNone(* All names accessible with a path, by path. *)letlocal_names_by_path=refPath_map.empty(* All names accessible with a path, by long identifier. *)letlocal_names_by_longident=refLongident_map.empty(* All record fields accessible without a path. *)letglobal_fields=refNone(* All record fields accessible with a path, by path. *)letlocal_fields_by_path=refPath_map.empty(* All record fields accessible with a path, by long identifier. *)letlocal_fields_by_longident=refLongident_map.empty(* All visible modules according to Config.load_path. *)letvisible_modules=refNoneletreset()=visible_modules :=None;global_names:=None;global_names_revised:= None;local_names_by_path:=Path_map.empty;local_names_by_longident:=Longident_map.empty;global_fields:=None;local_fields_by_path:= Path_map.empty;local_fields_by_longident:=Longident_map.emptyletget_cachedvarf=match!varwith|Somex->x|None ->letx=f()invar:=Somex;x(* List all visible modules.*)letvisible_modules()=get_cachedvisible_modules(fun()->List.fold_left(funaccdir->tryArray.fold_left(funaccfname->ifFilename.check_suffixfname".cmi"thenString_set.add(String.capitalize_ascii(Filename.chop_suffixfname".cmi"))accelseacc)acc(Sys.readdir(ifdir=""thenFilename.current_dir_nameelsedir))withSys_error _->acc)String_set.empty@@Load_path.get_paths())letfield_name{ld_id=id}=Ident.nameidletconstructor_name{cd_id=id}=Ident.nameidletadd_fields_of_typedeclacc =matchdecl.type_kind with|Type_variant_->acc|Type_record(fields,_)->List.fold_left(funaccfield->add(field_namefield)acc)accfields|Type_abstract->acc|Type_open->accletadd_names_of_typedeclacc =matchdecl.type_kind with#ifOCAML_VERSION>=(4,13,0)|Type_variant(constructors,_)->#else|Type_variantconstructors->#endifList.fold_left(funacccstr->add(constructor_name cstr)acc)accconstructors|Type_record(fields,_)->List.fold_left(funaccfield->add(field_namefield)acc)accfields|Type_abstract->acc|Type_open->accletpath_of_mty_alias=function|Mty_aliaspath->path|_->assertfalseletrecnames_of_module_type=function|Mty_signaturedecls ->List.fold_left(funaccdecl->matchdeclwith|Sig_value(id,_,_)|Sig_typext(id,_,_,_)|Sig_module(id,_,_,_,_)|Sig_modtype(id,_,_)|Sig_class(id,_,_,_)|Sig_class_type(id,_,_,_)->add(Ident.nameid)acc|Sig_type(id,decl,_,_)->add_names_of_typedecl(add(Ident.nameid)acc))String_set.emptydecls|Mty_identpath->beginmatchlookup_envEnv.find_modtypepath!Toploop.toplevel_envwith|Some{mtd_type=None}->String_set.empty|Some{mtd_type=Somemodule_type}->names_of_module_typemodule_type|None->String_set.emptyend|Mty_alias_asmty_alias->beginletpath=path_of_mty_aliasmty_aliasinmatchlookup_envEnv.find_modulepath!Toploop.toplevel_envwith|None->String_set.empty|Some{md_type=module_type}->names_of_module_typemodule_typeend|_->String_set.emptyletrecfields_of_module_type=function|Mty_signaturedecls->List.fold_left(funaccdecl->matchdeclwith|Sig_value_|Sig_typext_|Sig_module_|Sig_modtype_|Sig_class_|Sig_class_type_->acc|Sig_type(_,decl,_,_)->add_fields_of_typedeclacc)String_set.emptydecls|Mty_identpath->beginmatchlookup_envEnv.find_modtypepath!Toploop.toplevel_envwith|Some{mtd_type=None}->String_set.empty|Some{mtd_type=Somemodule_type}->fields_of_module_typemodule_type|None->String_set.emptyend|Mty_alias_asmty_alias->beginletpath=path_of_mty_aliasmty_aliasinmatchlookup_envEnv.find_modulepath!Toploop.toplevel_envwith|None->String_set.empty|Some{md_type=module_type}->fields_of_module_typemodule_typeend|_->String_set.emptyletfind_modulepathenv=(Env.find_modulepathenv).md_typeletnames_of_module longident=tryLongident_map.findlongident!local_names_by_longidentwithNot_found->matchlookup_envEnv.find_module_by_namelongident!Toploop.toplevel_envwith|Some(path,{md_type;_})->let names=names_of_module_typemd_typeinlocal_names_by_path:=Path_map.addpathnames!local_names_by_path;local_names_by_longident:=Longident_map.addlongidentnames!local_names_by_longident;names|None->local_names_by_longident :=Longident_map.addlongidentString_set.empty!local_names_by_longident;String_set.emptyletfields_of_modulelongident=tryLongident_map.findlongident!local_fields_by_longidentwithNot_found->matchlookup_envEnv.find_module_by_namelongident!Toploop.toplevel_envwith|Some(path,{md_type;_})->let fields=fields_of_module_typemd_typeinlocal_fields_by_path:=Path_map.addpathfields!local_fields_by_path;local_fields_by_longident:=Longident_map.addlongidentfields!local_fields_by_longident;fields|None->local_fields_by_longident :=Longident_map.addlongidentString_set.empty!local_fields_by_longident;String_set.emptyletlist_global_names()=letrecloopacc=function|Env.Env_empty->acc|Env.Env_value_unbound_-> acc|Env.Env_module_unbound_->acc|Env.Env_value(summary,id,_)->loop(add(Ident.nameid)acc)summary|Env.Env_type(summary,id,decl)->loop(add_names_of_typedecl(add(Ident.nameid)acc))summary|Env.Env_extension(summary,id,_)->loop(add(Ident.nameid)acc)summary|Env.Env_module(summary,id,_,_)->loop(add(Ident.nameid)acc)summary|Env.Env_modtype(summary,id,_)->loop(add(Ident.nameid)acc)summary|Env.Env_class(summary,id,_)->loop(add(Ident.nameid)acc)summary|Env.Env_cltype(summary,id,_)->loop(add(Ident.nameid)acc)summary|Env.Env_functor_arg(summary,id)->loop(add(Ident.name id)acc)summary|Env.Env_persistent (summary,id)->loop(add(Ident.name id)acc)summary|Env.Env_constraints (summary,_)->loopaccsummary|Env.Env_copy_typessummary->loopaccsummary|Env.Env_open(summary,path)->matchtrySome(Path_map.findpath!local_names_by_path)withNot_found ->Nonewith|Somenames->loop(String_set.unionaccnames)summary|None->matchlookup_envfind_modulepath!Toploop.toplevel_envwith|Somemodule_type ->letnames =names_of_module_typemodule_typeinlocal_names_by_path :=Path_map.addpathnames!local_names_by_path;loop(String_set.unionaccnames)summary|None ->local_names_by_path:=Path_map.addpathString_set.empty !local_names_by_path;loopaccsummaryin(* Add names ofthe environment: *)letacc=loopString_set.empty(Env.summary!Toploop.toplevel_env)in(* Add accessible modules: *)String_set.unionacc(visible_modules())letglobal_names()=get_cachedglobal_nameslist_global_namesletreplacexyset=ifString_set.memxsetthenString_set.addy(String_set.removexset)elsesetletlist_global_fields()=letrecloopacc=function|Env.Env_empty->acc|Env.Env_value_unbound_-> acc|Env.Env_module_unbound_->acc|Env.Env_value(summary,id,_)->loop(add(Ident.nameid)acc)summary|Env.Env_type(summary,id,decl)->loop(add_fields_of_typedecl(add(Ident.nameid)acc))summary|Env.Env_extension(summary,id,_)->loop(add(Ident.nameid)acc)summary|Env.Env_module(summary,id,_,_)->loop(add(Ident.nameid)acc)summary|Env.Env_functor_arg(summary,id)->loop(add(Ident.name id)acc)summary|Env.Env_modtype(summary,id,_)->loop(add(Ident.nameid)acc)summary|Env.Env_class(summary,id,_)->loop(add(Ident.nameid)acc)summary|Env.Env_cltype(summary,id,_)->loop(add(Ident.nameid)acc)summary|Env.Env_persistent (summary,id)->loop(add(Ident.name id)acc)summary|Env.Env_constraints (summary,_)->loopaccsummary|Env.Env_copy_typessummary->loopaccsummary|Env.Env_open(summary,path)->matchtrySome(Path_map.findpath!local_fields_by_path)withNot_found->Nonewith|Somefields->loop(String_set.unionaccfields)summary|None ->matchlookup_envfind_modulepath!Toploop.toplevel_envwith|Somemodule_type ->letfields =fields_of_module_typemodule_typeinlocal_fields_by_path :=Path_map.addpathfields!local_fields_by_path;loop(String_set.unionaccfields)summary|None ->local_fields_by_path:=Path_map.addpathString_set.empty !local_fields_by_path;loopaccsummaryin(* Add fields of the environment: *)letacc=loopString_set.empty(Env.summary!Toploop.toplevel_env)in(* Add accessible modules: *)String_set.unionacc(visible_modules())letglobal_fields()=get_cachedglobal_fieldslist_global_fields(* +-----------------------------------------------------------------+
| Listing methods |
+-----------------------------------------------------------------+ *)letrecfind_methodmethtype_expr=matchget_desc type_expr with|Tlinktype_expr->find_methodmethtype_expr|Tobject(type_expr,_)->find_methodmethtype_expr|Tfield(name,_,type_expr,rest)->ifname=meththenSometype_exprelsefind_method methrest|Tpoly(type_expr,_)->find_methodmethtype_expr|Tconstr(path,_,_)->beginmatchlookup_envEnv.find_typepath!Toploop.toplevel_envwith|None|Some{type_manifest=None}->None|Some{type_manifest=Sometype_expr}->find_methodmethtype_exprend|_->Noneletrecmethods_of_typeacctype_expr=matchget_desc type_expr with|Tlinktype_expr->methods_of_typeacctype_expr|Tobject(type_expr,_)->methods_of_typeacctype_expr|Tfield(name,_,_,rest)->methods_of_type(addnameacc)rest|Tpoly(type_expr,_)->methods_of_typeacctype_expr|Tconstr(path,_,_)->beginmatchlookup_envEnv.find_typepath!Toploop.toplevel_envwith|None|Some{type_manifest=None}->acc|Some{type_manifest=Sometype_expr}->methods_of_typeacctype_exprend|_->accletrecfind_objectmethstype_expr=matchmethswith|[]->Sometype_expr|meth::meths->matchfind_methodmethtype_exprwith|Sometype_expr ->find_objectmethstype_expr|None->Noneletmethods_of_objectlongidentmeths=matchlookup_envEnv.find_value_by_namelongident!Toploop.toplevel_envwith|None->[]|Some(path,{val_type=type_expr})->matchfind_objectmethstype_exprwith|None->[]|Sometype_expr->String_set.elements(methods_of_typeString_set.emptytype_expr)(* +-----------------------------------------------------------------+
| Listing labels |
+-----------------------------------------------------------------+ *)letreclabels_of_typeacctype_expr=matchget_desc type_expr with|Tlinkte->labels_of_typeaccte|Tpoly(te,_)->labels_of_typeaccte|Tarrow(label,_,te,_)->(matchlabelwith|Nolabel->labels_of_typeaccte|Optionallabel->labels_of_type(String_map.addlabelOptionalacc)te|Labelledlabel->labels_of_type(String_map.addlabelRequiredacc)te)|Tconstr(path,_,_)->beginmatchlookup_envEnv.find_typepath!Toploop.toplevel_envwith|None|Some{type_manifest=None}->String_map.bindingsacc|Some{type_manifest=Sometype_expr}->labels_of_typeacctype_exprend|_->String_map.bindingsaccletlabels_of_functionlongidentmeths=matchlookup_envEnv.find_value_by_namelongident!Toploop.toplevel_envwith|None->[]|Some(path,{val_type=type_expr})->matchfind_objectmethstype_exprwith|None->[]|Sometype_expr->labels_of_typeString_map.emptytype_exprletlabels_of_newclasslongident=matchlookup_envEnv.find_class_by_name longident!Toploop.toplevel_envwith|None->[]|Some(path,{cty_new=None})->[]|Some(path,{cty_new=Sometype_expr})->labels_of_typeString_map.emptytype_expr(* +-----------------------------------------------------------------+
| Tokens processing |
+-----------------------------------------------------------------+ *)(* Filter blanks and comments except for the last token. *)letfiltertokens=letrecauxacc=function|[]->acc|[((Blanks|Comment(_,true)),loc)]->(Blanks,loc)::acc|((Blanks|Comment(_,true)),_)::rest->auxaccrest|x::rest ->aux(x::acc)restinList.rev (aux[]tokens)(* Reverse and filter blanks and comments except for the last
token. *)letrecrev_filteracctokens=matchtokenswith|[]->acc|[((Blanks|Comment(_,true)),loc)]->(Blanks,loc)::acc|((Blanks|Comment(_,true)),_)::rest->rev_filteraccrest|x::rest->rev_filter(x::acc)rest(* Find the current context. *)letrecfind_contexttokens=function|[]->Some (rev_filter[]tokens)|[(Quotation(items,false),_)]->find_context_in_quotationitems|_::rest->find_contexttokensrestandfind_context_in_quotation=function|[]->None|[(Quot_anti{a_closing=None;a_contents=tokens},_)]->find_contexttokenstokens|_::rest->find_context_in_quotationrest(* +-----------------------------------------------------------------+
| Completion |
+-----------------------------------------------------------------+ *)letcomplete~phrase_terminator~input=lettrue_name,false_name=("true","false")inlettokens =UTop_lexer.lex_stringinputin(* Filter blanks and comments. *)lettokens=filtertokensinmatchtokenswith(* Completion on directive names. *)|[(Symbol"#",{idx2=stop})]|[(Symbol"#",_);(Blanks,{idx2=stop})]->(stop,list_directivesphrase_terminator)|[(Symbol"#",_);((Lidentsrc|Uidentsrc),{idx1=start})]->(start,lookup_assocsrc(list_directivesphrase_terminator))(* Complete with ";;" when possible. *)|[(Symbol"#",_);((Lident_|Uident_),_);(String(_,true),{idx2=stop})]|[(Symbol"#",_);((Lident_|Uident_),_);(String(_,true),_);(Blanks,{idx2=stop})]->(stop,[(phrase_terminator,"")])|[(Symbol"#",_);((Lident_|Uident_),_);(String(_,true),_);(Symbolsym,{idx1=start})]->ifZed_utf8.starts_withphrase_terminatorsymthen(start,[(phrase_terminator,"")])else(0,[])(* Completion on #require. *)|[(Symbol"#",_);(Lident"require",_);(String(tlen,false),loc)]->letpkg=String.subinput(loc.ofs1+tlen)(String.lengthinput -loc.ofs1-tlen)inletpkgs =lookuppkg(Fl_package_base.list_packages())in(loc.idx1+1,List.map(funpkg->(pkg,"\""^phrase_terminator))(List.sortcomparepkgs))|[(Symbol"#",_);(Lident "typeof",_);(String(tlen,false),loc)]->letprefix=String.subinput(loc.ofs1+tlen)(String.lengthinput -loc.ofs1-tlen)inbeginmatch Parse.longident(Lexing.from_stringprefix)with|Longident.Ldot(lident,last_prefix)->letset=names_of_module lidentinletcompls=lookuplast_prefix(String_set.elementsset)inletstart=loc.idx1 +1+(String.lengthprefix-String.lengthlast_prefix)in(start,List.map(funw->(w,""))compls)|_->letset=global_names()inlet compls=lookupprefix(String_set.elementsset)in(loc.idx1+1,List.map(funw->(w,""))compls)end(* Completion on #load. *)|[(Symbol"#",_);(Lident("load"|"load_rec"),_);(String(tlen,false),loc)]->letfile=String.subinput(loc.ofs1+tlen)(String.lengthinput -loc.ofs1-tlen)inletfilter name=Filename.check_suffixname".cma"||Filename.check_suffixname".cmo"inletmap=ifFilename.is_relativefilethenletdir=Filename.dirnamefileinList.fold_left(funaccd->add_filesfilteracc(Filename.concatddir))String_map.empty(Filename.current_dir_name::(Load_path.get_paths()))elseadd_filesfilterString_map.empty(Filename.dirnamefile)inletlist =String_map.bindingsmapinletname=basenamefileinletresult=lookup_assocnamelistin(loc.idx2-Zed_utf8.lengthname,List.map(function (w,Directory)->(w,"")|(w,File)->(w,"\""^phrase_terminator))result)(* Completion on #ppx. *)|[(Symbol"#",_);(Lident("ppx"),_);(String(tlen,false),loc)]->letfile=String.subinput(loc.ofs1+tlen)(String.lengthinput -loc.ofs1-tlen)inletfilter ~dir_okname=tryUnix.accessname[Unix.X_OK];letkind=(Unix.statname).Unix.st_kindinlet basename=Filename.basenamenamein(kind=Unix.S_REG &&String.lengthbasename>=4&&String.subbasename04="ppx_")||(dir_ok&&kind=Unix.S_DIR)with Unix.Unix_error_->falseinletmap=ifFilename.dirnamefile="."&¬(Filename.is_implicit file)thenletdir=Filename.dirnamefileinadd_files(filter~dir_ok:true)String_map.emptydirelseList.fold_left(funaccdir->add_files(funname->filter~dir_ok:false(Filename.concatdirname))accdir)String_map.empty(path())inletlist=String_map.bindingsmapinletname=basenamefileinletresult=lookup_assocnamelistin(loc.idx2-Zed_utf8.lengthname,List.map(function (w,Directory)->(w,"")|(w,File)->(w,"\""^phrase_terminator))result)(* Completion on #use and #mod_use *)|[(Symbol"#",_);(Lident"use",_);(String(tlen,false),loc)]|[(Symbol"#",_);(Lident"mod_use",_);(String(tlen,false),loc)]->letfile=String.subinput(loc.ofs1+tlen)(String.lengthinput -loc.ofs1-tlen)inletfilter name=matchtrySome(String.rindexname'.')withNot_found->Nonewith|None->true|Someidx->letext=String.subname(idx+1)(String.lengthname-(idx+1))inext="ml"inletmap=ifFilename.is_relativefilethenletdir=Filename.dirnamefileinList.fold_left(funaccd->add_filesfilteracc(Filename.concatddir))String_map.empty(Filename.current_dir_name::(Load_path.get_paths()))elseadd_filesfilterString_map.empty(Filename.dirnamefile)inletlist =String_map.bindingsmapinletname=basenamefileinletresult=lookup_assocnamelistin(loc.idx2-Zed_utf8.lengthname,List.map(function (w,Directory)->(w,"")|(w,File)->(w,"\""^phrase_terminator))result)(* Completion on #directory and #cd. *)|[(Symbol"#",_);(Lident("cd"|"directory"),_);(String(tlen,false),loc)]->letfile=String.subinput(loc.ofs1+tlen)(String.lengthinput -loc.ofs1-tlen)inletlist =list_directories(Filename.dirnamefile)inletname=basename fileinletresult=lookupnamelistin(loc.idx2-Zed_utf8.lengthname,List.map(functiondir->(dir,""))result)(* Generic completion on directives. *)|[(Symbol"#",_);((Lidentdir|Uidentdir),_);(Blanks,{idx2=stop})]->(stop,matchtoploop_get_directivedirwith|Some(Toploop.Directive_none_)->[(phrase_terminator,"")]|Some(Toploop.Directive_string_)->[(" \"","")]|Some(Toploop.Directive_bool_)->[(true_name,phrase_terminator);(false_name,phrase_terminator)]|Some(Toploop.Directive_int _)->[]|Some(Toploop.Directive_ident_)->List.map(funw->(w,""))(String_set.elements(global_names()))|None->[])|(Symbol"#",_)::((Lidentdir|Uidentdir),_)::tokens->beginmatchtoploop_get_directivedirwith|Some(Toploop.Directive_none_)->(0,[])|Some(Toploop.Directive_string_)->(0,[])|Some(Toploop.Directive_bool_)->beginmatchtokenswith|[(Lident id,{idx1=start})]->(start,lookup_associd[(true_name,phrase_terminator);(false_name,phrase_terminator)])|_->(0,[])end|Some(Toploop.Directive_int_)->(0,[])|Some(Toploop.Directive_ident_)->beginmatchparse_longident(List.revtokens)with|Some(Value,None,start,id)->(start,List.map(funw->(w,""))(lookupid(String_set.elements(global_names ()))))|Some(Value,Somelongident,start,id)->(start,List.map(funw->(w,""))(lookupid(String_set.elements(names_of_module longident))))|_->(0,[])end|None->(0,[])end(* Completion on identifiers. *)|_->matchfind_contexttokenstokenswith|None->(0,[])|Some[]->(0,List.map(funw->(w,""))(String_set.elements(String_set.union!UTop.keywords(global_names()))))|Sometokens->matchparse_methodtokenswith|Some(longident,meths,start,meth)->(start,List.map(funw->(w,""))(lookupmeth(methods_of_objectlongidentmeths)))|None->matchparse_labeltokenswith|Some (Fun,longident,meths,Optional,start,label)->(start,List.map(fun(w,kind)->(w,":"))(lookup_assoc label (List.filter(function(w,Optional)->true |(w,Required)->false)(labels_of_functionlongidentmeths))))|Some (Fun,longident,meths,Required,start,label)->(start,List.map(fun(w,kind)->(w,":"))(lookup_assoc label (labels_of_functionlongidentmeths)))|Some (New,longident,meths,Optional,start,label)->(start,List.map(fun(w,kind)->(w,":"))(lookup_assoc label (List.filter(function(w,Optional)->true |(w,Required)->false)(labels_of_newclasslongident))))|Some (New,longident,meths,Required,start,label)->(start,List.map(fun(w,kind)->(w,":"))(lookup_assoc label (labels_of_newclasslongident)))|None ->matchparse_longidenttokenswith|None->(0,[])|Some(Value,None,start,id)->(start,List.map(funw->(w,""))(lookupid(String_set.elements(String_set.union!UTop.keywords(global_names())))))|Some(Value,Somelongident,start,id)->(start,List.map(funw->(w,""))(lookupid(String_set.elements(names_of_module longident))))|Some(Field,None,start,id)->(start,List.map(funw->(w,""))(lookupid(String_set.elements(global_fields ()))))|Some(Field,Somelongident,start,id)->(start,List.map(funw->(w,""))(lookupid(String_set.elements(fields_of_module longident))))letcomplete~phrase_terminator~input=try(complete~phrase_terminator~input:int*(string*string)list)with Cmi_format.Error_->(0,[])