import model_utils
private class Pager
- var content: String = ""
+ var content = new Buffer
fun add(text: String) do addn("{text}\n")
- fun addn(text: String) do content += text.escape
+ 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
#var mmodules = modelbuilder.parse_and_build(["{dir}/lib/standard/standard.nit"])
var mmodules = mbuilder.parse_and_build([arguments.first])
if mmodules.is_empty then return
- mbuilder.full_propdef_semantic_analysis
assert mmodules.length == 1
self.mainmodule = mmodules.first
end
end
fun welcome do
- print "Welcome in Nit Index.\n"
- print "Loaded modules"
- for m in mbuilder.nmodules do
- print " - {m.mmodule.name}"
+ print "Welcome in the Nit Index."
+ 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 a blank line to exit.\n"
+ print "\nLoaded 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 "\nEnter the module, class or property name you want to look up."
- print "Enter a blank line to exit.\n"
end
fun prompt do
private fun modules_fulldoc(mmodules: List[MModule]) do
var pager = new Pager
for mmodule in mmodules do
- var nmodule = mbuilder.mmodule2nmodule[mmodule]
- pager.add("# module {mmodule.namespace}\n".bold)
- if not mmodule.in_importation.direct_greaters.is_empty then
- pager.add("import ".bold + "{mmodule.in_importation.direct_greaters.join(", ")}\n")
- end
- if not mmodule.in_importation.direct_smallers.is_empty then
- pager.add("known clients: ".bold + "{mmodule.in_importation.direct_smallers.join(", ")}\n")
+ # 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}")
pager.add_rule
- pager.addn(nmodule.n_moduledecl.n_doc.comment.green)
- pager.add_rule
-
- var cats = new HashMap[String, Collection[MClass]]
- cats["introduced classes"] = mmodule.intro_mclasses
- cats["refined classes"] = mmodule.redef_mclasses
- cats["imported classes"] = mmodule.imported_mclasses
-
- for cat, list in cats do
- if not list.is_empty then
- pager.add("\n# {cat}".bold)
- #sort list
- var sorted = new Array[MClass]
- sorted.add_all(list)
- var sorter = new ComparableSorter[MClass]
- sorter.sort(sorted)
- for mclass in sorted do
- var nclass = mbuilder.mclassdef2nclassdef[mclass.intro].as(AStdClassdef)
- pager.add("")
- if not nclass.n_doc == null and not nclass.n_doc.short_comment.is_empty then
- pager.add("\t# {nclass.n_doc.short_comment}")
- end
- if cat == "refined classes" then
- pager.add("\tredef {mclass.short_doc}")
- else
- pager.add("\t{mclass.short_doc}")
- end
- if cat != "introduced classes" then
- pager.add("\t\t" + "introduced in {mmodule.full_name}::{mclass}".gray)
- end
- for mclassdef in mclass.mclassdefs do
- if mclassdef != mclass.intro then
- pager.add("\t\t" + "refined in {mclassdef.namespace}".gray)
- end
- end
+ # 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
- pager.add_rule
+ # 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?
end
pager.render
end
private fun classes_fulldoc(mclasses: List[MClass]) do
var pager = new Pager
for mclass in mclasses do
- var nclass = mbuilder.mclassdef2nclassdef[mclass.intro].as(AStdClassdef)
-
+ # title
pager.add("# {mclass.namespace}\n".bold)
- pager.add("{mclass.short_doc}")
- pager.add_rule
- pager.addn(nclass.n_doc.comment.green)
+ # 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 mclass.in_hierarchy(mainmodule).direct_greaters.length > 1 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)
+ if i < mclass.in_hierarchy(mainmodule).direct_greaters.length -1 then pager.addn(", ")
+ end
+ pager.add("")
+ end
pager.add_rule
+ # 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}")
end
+ pager.add("")
end
- if not mclass.virtual_types.is_empty then
- pager.add("# virtual types".bold)
- for vt in mclass.virtual_types do
- pager.add("")
- vt_fulldoc(pager, vt)
+ # 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]
+
+ for mclassdef in mclass.mclassdefs do
+ for mpropdef in mclassdef.mpropdefs do
+ if mpropdef isa MAttributeDef then continue
+ if mpropdef isa MVirtualTypeDef then cats["virtual types"].add(mpropdef)
+ if mpropdef isa MMethodDef then
+ if mpropdef.mproperty.is_init then
+ cats["constructors"].add(mpropdef)
+ else if mpropdef.is_intro then
+ cats["introduced methods"].add(mpropdef)
+ else
+ cats["refined methods"].add(mpropdef)
+ end
+ end
end
end
- pager.add_rule
-
- var cats = new HashMap[String, Collection[MMethod]]
- cats["constructors"] = mclass.constructors
- cats["introduced methods"] = mclass.intro_methods
- cats["refined methods"] = mclass.redef_methods
- cats["inherited methods"] = mclass.inherited_methods
-
+ # local mproperties
for cat, list in cats do
if not list.is_empty then
#sort list
- var sorted = new Array[MMethod]
+ var sorted = new Array[MPropDef]
sorted.add_all(list)
- var sorter = new ComparableSorter[MMethod]
+ var sorter = new MPropDefNameSorter
sorter.sort(sorted)
- pager.add("\n# {cat}".bold)
- for mprop in sorted do
+ pager.add("# {cat}".bold)
+ for mpropdef in sorted do
pager.add("")
- method_fulldoc(pager, mprop)
+ if mbuilder.mpropdef2npropdef.has_key(mpropdef) then
+ var nprop = mbuilder.mpropdef2npropdef[mpropdef]
+ if not nprop.n_doc == null and not nprop.n_doc.comment.is_empty then
+ for comment in nprop.n_doc.comment do pager.add("\t{comment.green}")
+ else
+ nprop = mbuilder.mpropdef2npropdef[mpropdef.mproperty.intro]
+ if not nprop.n_doc == null and not nprop.n_doc.comment.is_empty then
+ for comment in nprop.n_doc.comment do pager.add("\t{comment.green}")
+ end
+ end
+ end
+ pager.add("\t{mpropdef}")
+ mainmodule.linearize_mpropdefs(mpropdef.mproperty.mpropdefs)
+ var previous_defs = new Array[MPropDef]
+ for def in mpropdef.mproperty.mpropdefs do
+ if def == mpropdef then continue
+ if def.is_intro then continue
+ if mclass.in_hierarchy(mainmodule) < def.mclassdef.mclass then
+ previous_defs.add(def)
+ end
+ end
+ if not mpropdef.is_intro then
+ pager.add("\t\t" + "introduced by {mpropdef.mproperty.intro.mclassdef.namespace.bold}".gray)
+ end
+ if not previous_defs.is_empty then
+ for def in previous_defs do pager.add("\t\t" + "inherited from {def.mclassdef.namespace.bold}".gray)
+ end
end
+ pager.add("")
+ end
+ end
+ # inherited mproperties
+ var inhs = new ArrayMap[MClass, Array[MProperty]]
+ var ancestors = mclass.in_hierarchy(mainmodule).greaters.to_a
+ mainmodule.linearize_mclasses(ancestors)
+ for a in ancestors do
+ if a == mclass then continue
+ for c in a.mclassdefs do
+ for p in c.intro_mproperties do
+ if p.intro_mclassdef == c then
+ if not inhs.has_key(a) then inhs[a] = new Array[MProperty]
+ inhs[a].add(p)
+ end
+ end
+ end
+ end
+ if not inhs.is_empty then
+ pager.add("# inherited properties".bold)
+ for a, ps in inhs do
+ pager.add("\n\tfrom {a.namespace.bold}: {ps.join(", ")}")
end
end
- pager.add_rule
end
pager.render
end
+ private fun mpropdef_fulldoc(pager: Pager, mpropdef: MPropDef) do
+ if mbuilder.mpropdef2npropdef.has_key(mpropdef) then
+ var nprop = mbuilder.mpropdef2npropdef[mpropdef]
+ if not nprop.n_doc == null and not nprop.n_doc.comment.is_empty then
+ for comment in nprop.n_doc.comment do pager.add("\t{comment.green}")
+ end
+ end
+ pager.add("\t{mpropdef}")
+ if not mpropdef.is_intro then
+ pager.add("\t\t" + "introduced in {mpropdef.mproperty.intro_mclassdef.namespace.bold}".gray)
+ end
+ var mpropdefs = mpropdef.mproperty.mpropdefs
+ mainmodule.linearize_mpropdefs(mpropdefs)
+ for mpdef in mpropdefs do
+ if not mpdef.is_intro then
+ pager.add("\t\t" + "refined in {mpdef.mclassdef.namespace.bold}".gray)
+ end
+ end
+ end
+
private fun props_fulldoc(raw_mprops: List[MProperty]) do
var pager = new Pager
# group by module
cats[mclass].add(mprop)
end
#sort groups
- var sorter = new ComparableSorter[MClass]
+ var sorter = new MClassNameSorter
var sorted = new Array[MClass]
sorted.add_all(cats.keys)
sorter.sort(sorted)
for mclass in sorted do
var mprops = cats[mclass]
pager.add("# {mclass.namespace}".bold)
- var sorterp = new ComparableSorter[MProperty]
+ var sorterp = new MPropertyNameSorter
sorterp.sort(mprops)
for mprop in mprops do
- if mprop isa MMethod and mbuilder.mpropdef2npropdef.has_key(mprop.intro) then
- pager.add("")
- method_fulldoc(pager, mprop)
- else if mprop isa MVirtualTypeProp then
- pager.add("")
- vt_fulldoc(pager, mprop)
- end
+ pager.add("")
+ mpropdef_fulldoc(pager, mprop.intro)
end
pager.add_rule
end
end
return matches
end
-
- private fun method_fulldoc(pager: Pager, mprop: MMethod) do
- if mbuilder.mpropdef2npropdef.has_key(mprop.intro) then
- var nprop = mbuilder.mpropdef2npropdef[mprop.intro]
- if not nprop.n_doc == null and not nprop.n_doc.short_comment.is_empty then
- pager.add("\t# {nprop.n_doc.short_comment}")
- end
- if nprop isa AAttrPropdef then
- pager.add("\t{nprop.read_accessor}")
- pager.add("\t{nprop.write_accessor}")
- else if nprop isa AMethPropdef then
- pager.add("\t{nprop}")
- end
- pager.add("\t\t" + "introduced in {mprop.intro_mclassdef.namespace}".gray)
- for mpropdef in mprop.mpropdefs do
- if mpropdef != mprop.intro then
- pager.add("\t\t" + "refined in {mpropdef.mclassdef.namespace}".gray)
- end
- end
- end
- end
-
- private fun vt_fulldoc(pager: Pager, vt: MVirtualTypeProp) do
- pager.add("\t{vt.short_doc}")
- pager.add("\t\t" + "introduced in {vt.intro_mclassdef.namespace}::{vt}".gray)
- for mpropdef in vt.mpropdefs do
- if mpropdef != vt.intro then
- pager.add("\t\t" + "refined in {mpropdef.mclassdef.namespace}".gray)
- end
- end
- end
end
# Printing facilities
redef class MModule
- super Comparable
- redef type OTHER: MModule
- redef fun <(other: OTHER): Bool do return self.name < other.name
+ # prototype of the module
+ # module ownername::name
+ private fun prototype: String do return "module {name}"
+ # namespace of the module
+ # ownername::name
private fun namespace: String do
- return full_name
+ if public_owner == null then
+ return self.name
+ else
+ return "{public_owner.namespace}::{self.name}"
+ end
end
end
redef class MClass
- super Comparable
- redef type OTHER: MClass
- redef fun <(other: OTHER): Bool do return self.name < other.name
-
- redef fun to_s: String do
+ # return the generic signature of the class
+ # [E, F]
+ private fun signature: String do
if arity > 0 then
- return "{name}[{intro.parameter_names.join(", ")}]"
+ return "[{intro.parameter_names.join(", ")}]"
else
- return name
+ return ""
end
end
- private fun short_doc: String do
- var ret = ""
- if is_interface then ret = "interface {ret}"
- if is_enum then ret = "enum {ret}"
- if is_class then ret = "class {ret}"
- if is_abstract then ret = "abstract {ret}"
- if visibility.to_s == "public" then ret = "{ret}{to_s.green}"
- if visibility.to_s == "private" then ret = "{ret}{to_s.red}"
- if visibility.to_s == "protected" then ret = "{ret}{to_s.yellow}"
- if not parents.is_empty then
- ret = "{ret} super {parents.join(", ")}"
- end
- return ret
+ # return the prototype of the class
+ # class name is displayed with colors depending on visibility
+ # abstract interface Foo[E]
+ private fun prototype: String do
+ var res = new Buffer
+ res.append("{kind} ")
+ if visibility.to_s == "public" then res.append("{name}{signature}".bold.green)
+ if visibility.to_s == "private" then res.append("{name}{signature}".bold.red)
+ if visibility.to_s == "protected" then res.append("{name}{signature}".bold.yellow)
+ return res.to_s
end
private fun namespace: String do
- return "{intro_mmodule.public_owner.name}::{name}"
+ return "{intro_mmodule.namespace}::{name}"
end
end
end
redef class MProperty
- super Comparable
- redef type OTHER: MProperty
- redef fun <(other: OTHER): Bool do return self.name < other.name
+ redef fun to_s do
+ if visibility.to_s == "public" then return name.green
+ if visibility.to_s == "private" then return name.red
+ if visibility.to_s == "protected" then return name.yellow
+ return name.bold
+ end
end
-redef class MVirtualTypeProp
- private fun short_doc: String do
- var ret = ""
- if visibility.to_s == "public" then ret = "{to_s.green}: {intro.bound.to_s}"
- if visibility.to_s == "private" then ret = "\t{to_s.red}: {intro.bound.to_s}"
- if visibility.to_s == "protected" then ret = "\t{to_s.yellow}: {intro.bound.to_s}"
- return ret
+redef class MMethodDef
+ redef fun to_s do
+ var res = new Buffer
+ if not is_intro then res.append("redef ")
+ if not mproperty.is_init then res.append("fun ")
+ res.append(mproperty.to_s.bold)
+ if msignature != null then res.append(msignature.to_s)
+ # FIXME: modifiers should be accessible via the model
+ #if self isa ADeferredMethPropdef then ret = "{ret} is abstract"
+ #if self isa AInternMethPropdef then ret = "{ret} is intern"
+ #if self isa AExternMethPropdef then ret = "{ret} is extern"
+ return res.to_s
end
end
-redef class ADoc
- private fun comment: String do
+redef class MVirtualTypeDef
+ redef fun to_s do
var res = new Buffer
- for t in n_comment do
- res.append(t.text.replace("# ", "").replace("#", ""))
+ res.append("type ")
+ res.append(mproperty.to_s.bold)
+ res.append(": {bound.to_s}")
+ return res.to_s
+ end
+end
+
+redef class MSignature
+ redef fun to_s do
+ var res = new Buffer
+ if not mparameters.is_empty then
+ res.append("(")
+ for i in [0..mparameters.length[ do
+ res.append(mparameters[i].to_s)
+ if i < mparameters.length - 1 then res.append(", ")
+ end
+ res.append(")")
+ end
+ if return_mtype != null then
+ res.append(": {return_mtype.to_s}")
end
return res.to_s
end
+end
+
+redef class MParameter
+ redef fun to_s do
+ var res = new Buffer
+ res.append("{name}: {mtype}")
+ if is_vararg then res.append("...")
+ return res.to_s
+ end
+end
+
+redef class MNullableType
+ redef fun to_s do return "nullable {mtype}"
+end
+
+redef class MGenericType
+ redef fun to_s do
+ var res = new Buffer
+ res.append("{mclass.name}[")
+ for i in [0..arguments.length[ do
+ res.append(arguments[i].to_s)
+ if i < arguments.length - 1 then res.append(", ")
+ end
+ res.append("]")
+ return res.to_s
+ end
+end
+
+redef class MParameterType
+ redef fun to_s do return mclass.intro.parameter_names[rank]
+end
+
+redef class MVirtualType
+ redef fun to_s do return mproperty.intro.to_s
+end
+
+redef class ADoc
+ private fun comment: List[String] do
+ var res = new List[String]
+ for t in n_comment do
+ res.add(t.text.replace("\n", ""))
+ end
+ return res
+ end
private fun short_comment: String do
- return n_comment.first.text.replace("# ", "").replace("\n", "")
+ return n_comment.first.text.replace("\n", "")
end
end
end
end
-redef class AMethPropdef
- redef fun to_s do
- var ret = ""
- if not mpropdef.mproperty.is_init then
- ret = "fun "
- end
- if mpropdef.mproperty.visibility.to_s == "public" then ret = "{ret}{mpropdef.mproperty.name.green}"
- if mpropdef.mproperty.visibility.to_s == "private" then ret = "{ret}{mpropdef.mproperty.name.red}"
- if mpropdef.mproperty.visibility.to_s == "protected" then ret = "{ret}{mpropdef.mproperty.name.yellow}"
- if n_signature != null then ret = "{ret}{n_signature.to_s}"
- if n_kwredef != null then ret = "redef {ret}"
- if self isa ADeferredMethPropdef then ret = "{ret} is abstract"
- if self isa AInternMethPropdef then ret = "{ret} is intern"
- if self isa AExternMethPropdef then ret = "{ret} is extern"
- return ret
- end
-end
-
-redef class ASignature
- redef fun to_s do
- #TODO closures
- var ret = ""
- if not n_params.is_empty then
- ret = "{ret}({n_params.join(", ")})"
- end
- if n_type != null then ret += ": {n_type.to_s}"
- return ret
- end
-end
-
-redef class AParam
- redef fun to_s do
- var ret = "{n_id.text}"
- if n_type != null then
- ret = "{ret}: {n_type.to_s}"
- if n_dotdotdot != null then ret = "{ret}..."
- end
- return ret
- end
-end
-
-redef class AType
- redef fun to_s do
- var ret = n_id.text
- if n_kwnullable != null then ret = "nullable {ret}"
- if not n_types.is_empty then ret = "{ret}[{n_types.join(", ")}]"
- return ret
- end
-end
-
# Redef String class to add a function to color the string
redef class String
end
private fun esc: Char do return 27.ascii
- private fun red: String do return add_escape_char("{esc}[1;31m")
- private fun yellow: String do return add_escape_char("{esc}[1;33m")
- private fun green: String do return add_escape_char("{esc}[1;32m")
- private fun blue: String do return add_escape_char("{esc}[1;34m")
- private fun cyan: String do return add_escape_char("{esc}[1;36m")
- private fun gray: String do return add_escape_char("{esc}[30;1m")
+ private fun gray: String do return add_escape_char("{esc}[30m")
+ private fun red: String do return add_escape_char("{esc}[31m")
+ private fun green: String do return add_escape_char("{esc}[32m")
+ private fun yellow: String do return add_escape_char("{esc}[33m")
+ private fun blue: String do return add_escape_char("{esc}[34m")
+ private fun purple: String do return add_escape_char("{esc}[35m")
+ private fun cyan: String do return add_escape_char("{esc}[36m")
+ private fun light_gray: String do return add_escape_char("{esc}[37m")
private fun bold: String do return add_escape_char("{esc}[1m")
private fun underline: String do return add_escape_char("{esc}[4m")