123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389(*
* Batteries_help - Calling the help system from the toplevel
* Copyright (C) 2009 David Teller, LIFO, Universite d'Orleans
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version,
* with the special exception on linking described in file LICENSE.
*
* This library 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 GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*)openBatIO(*let debug fmt =
Printf.eprintf fmt*)letdebugfmt=BatPrintf.fprintfBatIO.stdnullfmt(**
{6 Kinds}
*)typekinds=|Values|Types|Topics|Modules|Exns|Modtypes|Classes|Methods|Attributes|Objtypes(** Parse a category name into a topic.*)letkind_of_name=function|"topic"|"language"->SomeTopics|"values"->SomeValues|"types"->SomeTypes|"modules"->SomeModules|"exceptions"|"exns"->SomeExns|"modtypes"|"module_types"->SomeModtypes|"classes"->SomeClasses|"methods"->SomeMethods|"attributes"->SomeAttributes|"class_types"->SomeObjtypes|_->None(**
{6 Tables}
*)typeurl=string(**A kind of string used to represent URLs. Distinguished for documentation purposes.*)typequalified=string(**A kind of string used to represent fully-qualified names.*)typeunqualif=string(**A kind of string used to represent unqualified names, i.e. names without their module.*)typepackage=string(**A kind pf string used to represent help providers.*)typesuggestion={url:url(**The url to open in the browser to visit help on this suggestion.*);spackage:package(**The package which provides the url.*);}typecompletion={qualified:qualified(**A possible qualified name matching the request*);cpackage:package(**The package which provides the completion.*)}typetable={suggestions:(qualified,suggestion)Hashtbl.t(**A map from fully qualified name to suggestions.*);completions:(unqualif,completionlist)Hashtbl.t(**A map from unqualified name to a list of completions.*)}(**
Convert a table of reflists to a table of lists.
*)lettable_of_tablereft=letresult=Hashtbl.create(Hashtbl.lengtht)inHashtbl.iter(funkd->Hashtbl.addresultk(BatRefList.to_listd))t;resultletappend_to_tabletablekv=letfound=tryHashtbl.findtablekwithNot_found->letl=BatRefList.empty()inHashtbl.addtablekl;linBatRefList.pushfoundv(**
{6 Browsing}
*)letbrowsepages=tryList.iter(funpage->debug"Showing %s\n"page.url;ifBatteriesConfig.browsepage.url<>0thenraiseExit)pageswithExit->Printf.eprintf"Sorry, I had a problem communicating with your browser and I couldn't open the manual.\n%!"(**
{6 Loading}
*)(**Extract the unqualified name of a possibly qualified name.
[local_name "a.b.c.d"] produces ["d"]*)letlocal_names=trysnd(BatString.rsplits~by:".")withNot_found->s(**
Load the contents of an index file into hash tables.
*)letload_index~name~index~prefix~suggestions~completions=tryBatEnum.iter(funline->Scanf.sscanfline" %S : %S "(funitemurl->letfull_url=tryignore(BatString.findurl"://");urlwithNot_found->prefix^urlinHashtbl.addsuggestionsitem{spackage=name;url=full_url};(*Add fully qualified name -> url*)letbasename=Filename.basenameiteminletleafname=local_namebasenameinletcompletion={cpackage=name;qualified=item}inappend_to_tablecompletionsbasenamecompletion;ifleafname<>basenamethenappend_to_tablecompletionsleafnamecompletion;debug"Adding manual %S => %S (%S)\n"itemfull_urlname;debug"Adding completion %S => %S (%S)\n"basenameitemname;debug"Adding completion %S => %S (%S)\n"leafnameitemname))(BatFile.lines_ofindex)withe->Printf.eprintf"While initializing the on-line help, error reading index file %S\n%s\n%!"index(Printexc.to_stringe)(** Acquire a table, loading it if it hasn't been loaded yet.
{b Note} This function is thread-unsafe. Don't call it from any thread other than the main thread.
*)letget_table=lettables:(kinds,table)Hashtbl.t=Hashtbl.create16infunkind->tryHashtbl.findtableskindwithNot_found->letroot_dir=BatteriesConfig.documentation_rootinletroot_file=Filename.concatroot_dir"documentation.idex"intryletsuggestions=Hashtbl.create256andcompletions=Hashtbl.create256inBatEnum.iter(funline->tryScanf.sscanfline"%s %s "(funcategoryindex->matchkind_of_namecategorywith|Somekwhenk=kind->letindex=Filename.concatroot_dirindexinlethtml_directory=Filename.dirnameindexinifSys.file_existsindexthenload_index~name:"OCaml Batteries Included"~index~prefix:("file://"^html_directory^"/")~suggestions~completions|_->())with_->()(*At this point, ignore syntax errors, they're probably comments.*))(BatFile.lines_ofroot_file);letresult={suggestions=suggestions;completions=table_of_tablerefcompletions}inHashtbl.addtableskindresult;resultwithe->Printf.eprintf"While initializing the on-line help, error in root doc file %S\n%s\n%!"root_file(Printexc.to_stringe);letresult={suggestions=Hashtbl.create0;completions=Hashtbl.create0}inHashtbl.addtableskindresult;result(**
{6 Searching}
*)(**Print a warning regarding inconsistencies.*)letinconsistencytopicsubject=Printf.eprintf"Configuration issue: the help system promises something about a %s called %S but does not contain anything such. There may be an error with your installation of the documentation.\n"topicsubject(**
Find all the URL of each qualified name from a list of completions.
Qualified names which can't be found in the table are dropped and a warning is printed.
*)letresult_of_completionstablesingularsubject(l:completionlist)=BatList.filter_map(fun{qualified=q;_}->trySome(Hashtbl.findtable.suggestionsq)withNot_found->inconsistencysingularsubject;(*Report internal inconsistency*)None)l(**
Look for a given subject inside one of the manuals
@param singular The singular noun corresponding to this manual. This string is used to display
information regarding where the information may be found.
@param plural The plural noun corresponding to this manual. This string is used to display
information regarding where the information may be found.
@param kind The key corresponding to the manual.
@param subject The subject to search inside a manual.
*)letman_aux~kind~singular~pluralsubject=trylettable=get_tablekindintrymatchHashtbl.findtable.completionssubjectwith|[]->`No_result(*No completion on the subject, report subject not found*)|[{qualified=q;_}]asl->(*Check for inconsistency*)(tryignore(Hashtbl.findtable.suggestionsq);`Suggestions(l,table)withNot_found->inconsistencysingularsubject;`No_result)|l->`Suggestions(l,table)withNot_found->`No_resultwithSys_errore->Printf.printf"Sorry, I had a problem loading the help on %s. Deactivating help on that subject.\n Detailed error message is %s\n"plurale;`No_result(**
Look for a given subject inside one of the manuals and display the results.
@param cmd The command used to invoke this manual. This string is used to suggest further searches.
@param singular The singular noun corresponding to this manual. This string is used to display
information regarding where the information may be found.
@param plural The plural noun corresponding to this manual. This string is used to display
information regarding where the information may be found.
@param kind The key corresponding to the manual.
@param tabs If [true], all matching subjects will be opened, each one in its tab. Otherwise,
a message will allow selecting one subject.
@param subject The subject to search inside a manual.
*)letman~cmd~kind~singular~plural~tabssubject=matchman_aux~kind~singular~pluralsubjectwith`No_result->Printf.printf"Sorry, I don't know any %s named %S.\n%!"singularsubject|`Suggestions(l,table)whentabs->browse(result_of_completionstablesingularsubjectl)|`Suggestions([h],table)->browse(result_of_completionstablesingularsubject[h])|`Suggestions(l,_)->BatPrintf.printf"Several %s exist with name %S. To obtain help on one of them, please use one of\n %a%!"pluralsubject(BatList.print~first:""~sep:"\n "~last:"\n"(funout{qualified=q;_}->BatPrintf.fprintfout" %s %S\n"cmdq))l(**
Look for a given subject across all manuals and display the results.
*)letman_allsources~tabssubject=letfound_something=iftabsthenList.fold_left(funwas_found(*Browse help directly*)(_cmd,kind,singular,plural,_undefined)->matchman_aux~kind~singular~pluralsubjectwith|`No_result->was_found|`Suggestions(l,table)->matchresult_of_completionstablesingularsubjectlwith|[]->false(*Inconsistency*)|l'->let_=browsel'intrue)falsesourceselsematchList.fold_left(fun(((result_as_strings:stringlist)(*The text to display, as a list of strings, one string per kind.*),_one_suggestion(*The latest suggestion -- used only in case there's only one suggestion.*))asacc)(cmd,kind,singular,plural,_undefined)->matchman_aux~kind~singular~pluralsubjectwith|`No_result->acc|`Suggestions([h],table)->letdisplay:string=Printf.sprintf"There's information on %S in %s. To read this information, please use\n %s %S%!"subjectpluralcmdh.qualifiedin(display::result_as_strings,`Browse(h,table,singular))|`Suggestions(l,_)->letdisplay:string=BatPrintf.sprintf2"There's information on %S in %s. To read this information, please use one of\n%a%!"subjectplural(BatList.print~first:""~sep:""~last:""(funout{qualified=q;_}->BatPrintf.fprintfout" %s %S\n"cmdq))lin(display::result_as_strings,`No_browsing))([],`No_result)sourceswith|([],_)->false(*No result*)|([_],`Browse(l,table,singular))->(matchresult_of_completionstablesingularsubject[l]with|[]->false(*Inconsistency*)|l'->let_=browsel'intrue)|(texts,_)->BatPrintf.printf"Several definitions exist for %S.\n%a%!"subject(BatList.print~first:""~sep:"\n"~last:"\n"BatString.print)texts;trueinifnotfound_somethingthenPrintf.printf"Sorry, I don't know anything about %S.\n%!"subject(**
{6 Registration}
*)(** The various functions which may be used to access the manual.*)lethelpers=letsources=[("#man_value",Values,"value","values","a value");("#man_type",Types,"type","types","a type");("#man_topic",Topics,"topic","topics","a topic");("#man_module",Modules,"module","modules","a module");("#man_exception",Exns,"exception","exceptions","an exception");("#man_signature",Modtypes,"signature","signatures","a signature");("#man_class",Classes,"class","classes","a class");("#man_method",Methods,"method","methods","a method");("#man_attribute",Attributes,"attribute","attributes","an attribute");("#man_objtype",Objtypes,"object type","object types","an object type")]in("man",man_allsources~tabs:false)::(List.map(fun(cmd,kind,singular,plural,_undefined)->(String.subcmd1(String.lengthcmd-1),man~cmd~kind~singular~plural~tabs:false))sources)(**Launch the introductory help text.*)lethelp()=BatFile.with_file_in(BatteriesConfig.documentation_root^"/toplevel.help")(funfile->copyfilestdout);flushstdout;;(**Print the signature of a module.*)letprint_modulename=tryletflattened=Str.global_replace(Str.regexp"[^_0-9a-zA-Z]")"__"nameinletphrase=!Toploop.parse_toplevel_phrase(Lexing.from_string(Printf.sprintf"module %s = %s;;"flattenedname))inignore(Toploop.execute_phrasetrueFormat.std_formatterphrase)with_->();;letman=List.assoc"man"helpers(** Initialize the help system (lazily)*)letinit()=try[@alert"-deprecated"](* Toploop.directive_table is deprecated but Toploop.add_directive is only available since OCaml 4.03 *)(*The manual*)List.iter(fun(key,search)->Hashtbl.addToploop.directive_tablekey(Toploop.Directive_stringsearch))helpers;(*Directive #help*)Hashtbl.addToploop.directive_table"help"(Toploop.Directive_nonehelp);(*Directive #browse*)Hashtbl.addToploop.directive_table"browse"(Toploop.Directive_stringprint_module)withe->Printf.printf"Error while initializing help system:\n%s\n%!"(Printexc.to_stringe)