ni: First introduction
authorAlexandre Terrasa <alexandre@moz-code.org>
Wed, 19 Jun 2013 18:28:45 +0000 (14:28 -0400)
committerAlexandre Terrasa <alexandre@moz-code.org>
Wed, 19 Jun 2013 18:28:45 +0000 (14:28 -0400)
ni is now able to display index for modules and classes

Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

src/ni.nit [new file with mode: 0644]

diff --git a/src/ni.nit b/src/ni.nit
new file mode 100644 (file)
index 0000000..d60e33c
--- /dev/null
@@ -0,0 +1,411 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2008 Jean Privat <jean@pryen.org>
+#
+# 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