From 6da48b94dec2bbf2ae4b8faf73da3a6db146f1cd Mon Sep 17 00:00:00 2001 From: Alexandre Terrasa Date: Wed, 19 Jun 2013 14:28:45 -0400 Subject: [PATCH] ni: First introduction ni is now able to display index for modules and classes Signed-off-by: Alexandre Terrasa --- src/ni.nit | 411 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100644 src/ni.nit diff --git a/src/ni.nit b/src/ni.nit new file mode 100644 index 0000000..d60e33c --- /dev/null +++ b/src/ni.nit @@ -0,0 +1,411 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2008 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module ni + +import toolcontext +import exprbuilder + +private class Pager + var content: String = "" + fun add(text: String) do addn("{text}\n") + fun addn(text: String) do content += text.escape + fun add_rule do add("\n---\n") + fun render do sys.system("echo \"{content}\" | pager -r") +end + +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.arguments = toolcontext.option_context.rest + + if 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_and_build([arguments.first]) + if mmodules.is_empty then return + mbuilder.full_propdef_semantic_analysis + 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 Nit Index.\n" + print "Loaded modules" + for m in mbuilder.nmodules do + print " - {m.mmodule.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 + printn ">> " + seek(stdin.read_line) + end + + fun seek(entry: String) do + # quitting? + if entry.is_empty then exit(0) + + var flag = false + for n in mbuilder.nmodules do + var m = n.mmodule + if m.name == entry then + flag = true + module_fulldoc(n) + end + end + for c in model.mclasses do + if c.name == entry then + if not mbuilder.mclassdef2nclassdef[c.intro] isa AStdClassdef then continue + flag = true + doc_class(c) + end + end + var matches = new List[MProperty] + for p in model.mproperties do + if p.name == entry then + flag = true + matches.add(p) + end + end + #if not matches.is_empty then doc_properties + + if not flag then print "Nothing known about '{entry}'" + if arguments.length == 1 then prompt + end + + private fun module_fulldoc(nmodule: AModule) do + var mmodule = nmodule.mmodule + var pager = new Pager + pager.add("# module {mmodule.name}".bold) + pager.add("") + pager.add("import {mmodule.in_importation.direct_greaters.join(", ")}") + pager.add_rule + pager.addn(mmodule.comment(nmodule).green) + pager.add_rule + if not mmodule.intro_mclasses.is_empty then + pager.add("# introduced classes\n".bold) + for mclass in mmodule.intro_mclasses do + mclass_shortdoc(mclass, pager) + pager.add("") + end + end + if not mmodule.redef_mclasses.is_empty then + pager.add("# refined classes\n".bold) + for mclass in mmodule.redef_mclasses do + mclass_shortdoc(mclass, pager) + pager.add("") + end + end + pager.add_rule + #TODO get mclass from nested modules + pager.render + end + + fun doc_class(mclass: MClass) do + var nclass = mbuilder.mclassdef2nclassdef[mclass.intro].as(AStdClassdef) + var pager = new Pager + pager.add("# {mclass.intro_mmodule.public_owner.name}::{mclass.name}".bold) + pager.add("") + # full signature + pager.addn("{mclass.visibility.to_s} ") + if mclass.is_abstract then pager.addn("abstract ") + if mclass.is_interface then pager.addn("interface ") + if mclass.is_enum then pager.addn("enum ") + if mclass.is_class then pager.addn("class ") + pager.addn("{mclass.ni_name} ") + pager.add("super {mclass.in_hierarchy(mainmodule).direct_greaters.join(", ")}") + pager.add_rule + # comment + pager.addn(mclass.comment(nclass).green) + pager.add_rule + # formal and virtual types + #TODO VT + pager.add_rule + # properties + if not mclass.constructors.is_empty then + pager.add("# constructors\n".bold) + display_methods(pager, mclass.constructors) + end + if not mclass.intro_methods.is_empty then + pager.add("# methods\n".bold) + display_methods(pager, mclass.intro_methods) + end + if not mclass.inherited_methods.is_empty then + pager.add("# inherited methods\n".bold) + display_methods(pager, mclass.inherited_methods) + end + pager.add_rule + pager.render + #TODO check inheritance methods + end + + private fun mclass_shortdoc(mclass: MClass, pager: Pager) do + var nclass = mbuilder.mclassdef2nclassdef[mclass.intro].as(AStdClassdef) + + if not mclass.short_comment(nclass).is_empty then + pager.add("\t# {mclass.short_comment(nclass)}") + end + pager.addn("\t") + if mclass.is_abstract then + pager.addn("abstract ") + end + if mclass.is_interface then + pager.addn("interface ") + else if mclass.is_enum then + pager.addn("enum ") + else + pager.addn("class ") + end + if mclass.visibility.to_s == "public" then + pager.addn("{mclass.ni_name} ".green) + else if mclass.visibility.to_s == "private" then + pager.addn("{mclass.ni_name} ".red) + else + pager.addn("{mclass.ni_name} ".yellow) + end + pager.addn("super {mclass.in_hierarchy(mainmodule).direct_greaters.join(", ")}\n") + end + + private fun display_methods(pager: Pager, methods: Set[MMethod]) do + for mmethod in methods do + #TODO verifier cast + var nmethod = mbuilder.mpropdef2npropdef[mmethod.intro].as(AMethPropdef) + if not mmethod.short_comment(nmethod).is_empty then + pager.add("\t# {mmethod.short_comment(nmethod)}") + end + #TODO replace Ft by names + #TODO fun ou init + pager.addn("\tfun ") + if mmethod.visibility.to_s == "public" then pager.addn(mmethod.name.green) + if mmethod.visibility.to_s == "private" then pager.addn(mmethod.name.red) + if mmethod.visibility.to_s == "protected" then pager.addn(mmethod.name.yellow) + + pager.addn("{mmethod.intro.msignature.to_s} ") + #pager.addn("{nmethod.collect_text} ") + if nmethod isa ADeferredMethPropdef then pager.addn("is abstract ") + if nmethod isa AInternMethPropdef then pager.addn("is intern ") + if nmethod isa AExternMethPropdef then pager.addn("is extern ") + #TODO si non local ajouter namespace + pager.add("\n") + end + end +end + +redef class MModule + # Get the list of mclasses refined in self + private fun redef_mclasses: Set[MClass] do + var mclasses = new HashSet[MClass] + for c in mclassdefs do + if not c.is_intro and not mclasses.has(c.mclass) then + mclasses.add(c.mclass) + end + end + return mclasses + end + + private fun comment(nmodule: AModule): String do + var ret = "" + for t in nmodule.n_moduledecl.n_doc.n_comment do + ret += "{t.text.replace("# ", "")}" + end + return ret + end +end + +redef class MClass + + private fun ni_name: String do + if arity > 0 then + return "{name}[{intro.parameter_names.join(", ")}]" + else + return name + end + end + + # Get the list of class constructors + private fun constructors: Set[MMethod] do + var res = new HashSet[MMethod] + for mclassdef in mclassdefs do + for mpropdef in mclassdef.mpropdefs do + if mpropdef isa MMethodDef then + if mpropdef.mproperty.is_init then res.add(mpropdef.mproperty) + end + end + end + return res + end + + # Get the list of intro methods + private fun intro_methods: Set[MMethod] do + var res = new HashSet[MMethod] + for mclassdef in mclassdefs do + for mpropdef in mclassdef.mpropdefs do + if mpropdef isa MMethodDef then + if mpropdef.is_intro and not mpropdef.mproperty.is_init then res.add(mpropdef.mproperty) + end + end + end + return res + end + + # Get the list of inherited methods + private fun inherited_methods: Set[MMethod] do + var res = new HashSet[MMethod] + for mclassdef in mclassdefs do + for mpropdef in mclassdef.mpropdefs do + if mpropdef isa MMethodDef then + if not mpropdef.is_intro and not mpropdef.mproperty.is_init then res.add(mpropdef.mproperty) + end + end + end + return res + end + + private fun comment(nclass: AStdClassdef): String do + var ret = "" + if nclass.n_doc != null then + for t in nclass.n_doc.n_comment do + var txt = t.text.replace("# ", "") + txt = txt.replace("#", "") + ret += "{txt}" + end + end + return ret + end + + private fun short_comment(nclass: AStdClassdef): String do + var ret = "" + if nclass.n_doc != null then + var txt = nclass.n_doc.n_comment.first.text + txt = txt.replace("# ", "") + txt = txt.replace("\n", "") + ret += txt + end + return ret + end + + private fun is_class: Bool do + return self.kind == concrete_kind or self.kind == abstract_kind + end + + private fun is_interface: Bool do + return self.kind == interface_kind + end + + private fun is_enum: Bool do + return self.kind == enum_kind + end + + private fun is_abstract: Bool do + return self.kind == abstract_kind + end +end + +redef class MMethod + private fun short_comment(nmethod: AMethPropdef): String do + var ret = "" + if nmethod.n_doc != null then + var txt = nmethod.n_doc.n_comment.first.text + txt = txt.replace("# ", "") + txt = txt.replace("\n", "") + ret += txt + end + return ret + end +end + +# Redef String class to add a function to color the string +redef class String + + private fun add_escape_char(escapechar: String): String do + return "{escapechar}{self}\\033[0m" + 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 bold: String do return add_escape_char("{esc}[1m") + private fun underline: String do return add_escape_char("{esc}[4m") + + private fun escape: String + do + var b = new Buffer + for c in self do + if c == '\n' then + b.append("\\n") + else if c == '\0' then + b.append("\\0") + else if c == '"' then + b.append("\\\"") + else if c == '\\' then + b.append("\\\\") + else if c == '`' then + b.append("'") + else if c.ascii < 32 then + b.append("\\{c.ascii.to_base(8, false)}") + else + b.add(c) + end + end + return b.to_s + end +end + +# Create a tool context to handle options and paths +var toolcontext = new ToolContext +toolcontext.process_options + +# Here we launch the nit index +var ni = new NitIndex(toolcontext) +ni.start + +# TODO seek methods +# TODO bornes sur les signatures génériques +# TODO lister les methods qui retournent un certain type +# TODO lister les methods qui utilisent un certain type +# TODO lister les sous-types connus d'une type +# TODO lister les prop adaptés à un type générique -- 1.7.9.5