-import model_utils
-import modelize_property
-
-private class Pager
- var content = new Buffer
- fun add(text: String) do addn("{text}\n")
- fun addn(text: String) do content.append(text.escape)
- fun add_rule do add("\n---\n")
- fun render do sys.system("echo \"{content}\" | pager -r")
-end
-
-# Main class of the nit index tool
-# NitIndex build the model using the toolcontext argument
-# then wait for query on std in to display documentation
-class NitIndex
- private var toolcontext: ToolContext
- private var model: Model
- private var mbuilder: ModelBuilder
- private var mainmodule: MModule
- private var arguments: Array[String]
-
- init(toolcontext: ToolContext) do
- # We need a model to collect stufs
- self.toolcontext = toolcontext
- self.toolcontext.option_context.options.clear
- self.arguments = toolcontext.option_context.rest
-
- if arguments.is_empty or arguments.length > 2 then
- print "usage: ni path/to/module.nit [expression]"
- toolcontext.option_context.usage
- exit(1)
- end
-
- model = new Model
- mbuilder = new ModelBuilder(model, toolcontext)
-
- # Here we load an process std modules
- #var dir = "NIT_DIR".environ
- #var mmodules = modelbuilder.parse_and_build(["{dir}/lib/standard/standard.nit"])
- var mmodules = mbuilder.parse([arguments.first])
- if mmodules.is_empty then return
- mbuilder.run_phases
- assert mmodules.length == 1
- self.mainmodule = mmodules.first
- end
-
- fun start do
- if arguments.length == 1 then
- welcome
- prompt
- else
- seek(arguments[1])
- end
- end
-
- fun welcome do
- print "Welcome in the Nit Index."
- print ""
- print "Loaded modules:"
- var mmodules = new Array[MModule]
- mmodules.add_all(model.mmodules)
- var sorter = new MModuleNameSorter
- sorter.sort(mmodules)
- for m in mmodules do
- print "\t{m.name}"
- end
- print ""
- help
- end
-
- fun help do
- print "\nCommands:"
- print "\tname\t\tlookup module, class and property with the corresponding 'name'"
- print "\tparam: Type\tlookup methods using the corresponding 'Type' as parameter"
- print "\treturn: Type\tlookup methods returning the corresponding 'Type'"
- print "\tEnter ':q' to exit"
- print "\tEnter ':h' to display this help message"
- print ""
- end
-
- fun prompt do
- printn ">> "
- seek(stdin.read_line)
- end
-
- fun seek(entry: String) do
- if entry.is_empty then
- prompt
- return
- end
- if entry == ":h" then
- help
- prompt
- return
- end
- if entry == ":q" then exit(0)
- var pager = new Pager
- # seek return types
- if entry.has_prefix("return:") then
- var ret = entry.split_with(":")[1].replace(" ", "")
- var matches = seek_returns(ret)
- props_fulldoc(pager, matches)
- else if entry.has_prefix("param:") then
- var param = entry.split_with(":")[1].replace(" ", "")
- var matches = seek_params(param)
- props_fulldoc(pager, matches)
- else
- # seek for modules
- var mmatches = new List[MModule]
- for m in model.mmodules do
- if m.name == entry then mmatches.add(m)
- end
- if not mmatches.is_empty then modules_fulldoc(pager, mmatches)
- # seek for classes
- var cmatches = new List[MClass]
- for c in model.mclasses do
- if c.name == entry then cmatches.add(c)
- end
- if not cmatches.is_empty then classes_fulldoc(pager, cmatches)
- # seek for properties
- var matches = new List[MProperty]
- for p in model.mproperties do
- if p.name == entry then matches.add(p)
- end
- if not matches.is_empty then props_fulldoc(pager, matches)
- end
- # no matches
- if pager.content.is_empty then
- print "Nothing known about '{entry}', type ':h' for help"
- else
- pager.render
- end
- if arguments.length == 1 then prompt
- end
-
- private fun modules_fulldoc(pager: Pager, mmodules: List[MModule]) do
- for mmodule in mmodules do
- # name and prototype
- pager.add("# {mmodule.namespace}\n".bold)
- # comment
- if mbuilder.mmodule2nmodule.has_key(mmodule) then
- var nmodule = mbuilder.mmodule2nmodule[mmodule]
- if not nmodule.n_moduledecl.n_doc == null then
- for comment in nmodule.n_moduledecl.n_doc.comment do pager.add(comment.green)
- end
- end
- pager.add("{mmodule.prototype}\n")
- # imports
- var msorter = new MModuleNameSorter
- var ms = mmodule.in_importation.greaters.to_a
- if ms.length > 1 then
- msorter.sort(ms)
- pager.add("## imported modules".bold)
- pager.addn("\t")
- for i in [0..ms.length[ do
- if ms[i] == mmodule then continue
- pager.addn(ms[i].name)
- if i < ms.length - 1 then pager.addn(", ")
- end
- pager.add("\n")
- end
- # clients
- ms = mmodule.in_importation.smallers.to_a
- if ms.length > 1 then
- msorter.sort(ms)
- pager.add("## known modules".bold)
- pager.addn("\t")
- for i in [0..ms.length[ do
- if ms[i] == mmodule then continue
- pager.addn(ms[i].name)
- if i < ms.length - 1 then pager.addn(", ")
- end
- pager.add("\n")
- end
- # local classes and interfaces
- var sorter = new MClassDefNameSorter
- var intro_mclassdefs = new Array[MClassDef]
- var redef_mclassdefs = new Array[MClassDef]
- for mclassdef in mmodule.mclassdefs do
- if mclassdef.is_intro then
- intro_mclassdefs.add(mclassdef)
- else
- redef_mclassdefs.add(mclassdef)
- end
- end
- # intro
- if not intro_mclassdefs.is_empty then
- sorter.sort(intro_mclassdefs)
- pager.add("\n## introduced classes".bold)
- for mclassdef in intro_mclassdefs do
- pager.add("")
- var nclass = mbuilder.mclassdef2nclassdef[mclassdef]
- if nclass isa AStdClassdef and not nclass.n_doc == null and not nclass.n_doc.short_comment.is_empty then
- pager.add("\t{nclass.n_doc.short_comment.green}")
- end
- pager.add("\t{mclassdef.mclass.prototype}")
- #TODO add redefs?
- end
- end
- # redefs
- if not redef_mclassdefs.is_empty then
- sorter.sort(redef_mclassdefs)
- pager.add("\n## refined classes".bold)
- for mclassdef in redef_mclassdefs do
- pager.add("")
- #TODO intro comment?
- var nclass = mbuilder.mclassdef2nclassdef[mclassdef]
- if nclass isa AStdClassdef and not nclass.n_doc == null and not nclass.n_doc.short_comment.is_empty then
- pager.add("\t# {nclass.n_doc.short_comment.green}")
- end
- pager.add("\t{mclassdef.mclass.prototype}")
- pager.add("\t\t" + "introduced in {mclassdef.mclass.intro.mmodule.namespace.bold}".gray)
- for odef in mclassdef.mclass.mclassdefs do
- if odef.is_intro or odef == mclassdef or mclassdef.mmodule == mmodule then continue
- pager.add("\t\t" + "refined in {mclassdef.mmodule.namespace.bold}".gray)
- end
- end
- end
- #TODO add inherited classes?
- pager.add_rule
- end
- end
-
- private fun classes_fulldoc(pager: Pager, mclasses: List[MClass]) do
- for mclass in mclasses do
- # title
- pager.add("# {mclass.namespace}\n".bold)
- # comment
- if mbuilder.mclassdef2nclassdef.has_key(mclass.intro) then
- var nclass = mbuilder.mclassdef2nclassdef[mclass.intro]
- if nclass isa AStdClassdef and not nclass.n_doc == null then
- for comment in nclass.n_doc.comment do pager.add(comment.green)
- end
- end
- pager.addn("{mclass.prototype}")
- if not mclass.in_hierarchy(mainmodule).direct_greaters.is_empty then
- var supers = mclass.in_hierarchy(mainmodule).direct_greaters.to_a
- pager.addn(" super ")
- for i in [0..supers.length[ do
- if supers[i] == mclass then continue
- pager.addn("{supers[i].name}{supers[i].signature}")
- if i < mclass.in_hierarchy(mainmodule).direct_greaters.length -1 then pager.addn(", ")
- end
- end
- pager.add("\n")
- # formal types
- if not mclass.parameter_types.is_empty then
- pager.add("## formal types".bold)
- for ft, bound in mclass.parameter_types do
- pager.add("")
- pager.add("\t{ft.to_s.green}: {bound.to_console}")
- end
- pager.add("")
- end
- # get properties
- var cats = new ArrayMap[String, Set[MPropDef]]
- cats["virtual types"] = new HashSet[MPropDef]
- cats["constructors"] = new HashSet[MPropDef]
- cats["introduced methods"] = new HashSet[MPropDef]
- cats["refined methods"] = new HashSet[MPropDef]