vim autocomplete: fix `MDoc` used for classes
[nit.git] / src / doc / vim_autocomplete.nit
index bcc9041..edbc126 100644 (file)
@@ -14,8 +14,9 @@
 
 # Generate files used by the Vim plugin to autocomplete with doc
 #
-# There is 3 files generated, each with a different target: modules, classes,
-# and properties. Each line describe a different entity, with 4 values:
+# There is 3 files generated, each with a different target: modules, types,
+# properties and constructors. Each line describe a different entity,
+# with 4 values:
 #
 # 1. Short name to use in autocompletion
 # 2. Full signature
 # 4. Full doc with extra
 #
 # The priority with those files is for them to be analyzed efficiently, for
-# this reason, the data is prepared in advant and some information may be
+# this reason, the data is prepared in advance and some information may be
 # duplicated.
 module vim_autocomplete
 
 import modelbuilder
 import phase
 import modelize::modelize_class
+import model_utils
 
 redef class ToolContext
        # Phase generating the files for the Vim plugin
@@ -50,37 +52,47 @@ redef class MEntity
        private fun field_separator: String do return "#====#"
        private fun line_separator: String do return "#nnnn#"
 
-       private fun write_to_stream(stream: OStream)
+       private fun write_doc(mainmodule: MModule, stream: Writer)
        do
                # 1. Short name for autocompletion
-               stream.write name
+               stream.write complete_name
                stream.write field_separator
 
                # 2. Full signature
-               stream.write name
+               stream.write complete_name
                write_signature_to_stream(stream)
                stream.write field_separator
 
                # 3. Doc synopsis
-               var mdoc = mdoc
+               var mdoc = complete_mdoc
                if mdoc != null then
                        stream.write mdoc.content.first
                end
 
                # 4. Full doc with extra
                stream.write field_separator
+               stream.write "# "
+               stream.write full_name
+               write_signature_to_stream(stream)
                if mdoc != null then
-                       stream.write "# "
-                       stream.write full_name
-                       write_signature_to_stream(stream)
                        for i in 2.times do stream.write line_separator
                        stream.write mdoc.content.join(line_separator)
                end
+               write_extra_doc(mainmodule, stream)
 
                stream.write "\n"
        end
 
-       private fun write_signature_to_stream(stream: OStream) do end
+       private fun write_signature_to_stream(stream: Writer) do end
+
+       # Actual name used in completion
+       private fun complete_name: String do return name
+
+       # Doc to use in completion
+       private fun complete_mdoc: nullable MDoc do return mdoc
+
+       # Extra auto documentation to append to the `stream`
+       private fun write_extra_doc(mainmodule: MModule, stream: Writer) do end
 end
 
 redef class MMethodDef
@@ -103,6 +115,121 @@ redef class MAttributeDef
        end
 end
 
+# Use `MClassDef` as anchor for its constructors only
+redef class MClassDef
+       private var target_constructor: nullable MMethodDef = null
+
+       redef fun complete_name
+       do
+               var target_constructor = target_constructor
+               assert target_constructor != null
+
+               var params
+               var mparameters = mclass.mparameters
+               if not mparameters.is_empty then
+                       params = "[{mparameters.join(", ")}]"
+               else
+                       params = ""
+               end
+
+               if target_constructor.name != "init" and target_constructor.name != "new" then
+                       return name + params + "." + target_constructor.name
+               end
+
+               return name + params
+       end
+
+       redef fun complete_mdoc
+       do
+               var target_constructor = target_constructor
+               assert target_constructor != null
+
+               if target_constructor.name != "init" and target_constructor.name != "new" then
+                       return target_constructor.mdoc
+               end
+
+               return mdoc
+       end
+end
+
+redef class MClassType
+       redef fun write_extra_doc(mainmodule, stream)
+       do
+               # Super classes
+               stream.write line_separator*2
+               stream.write "## Class hierarchy"
+
+               var direct_supers = [for s in mclass.in_hierarchy(mainmodule).direct_greaters do s.name]
+               if not direct_supers.is_empty then
+                       alpha_comparator.sort direct_supers
+                       stream.write line_separator
+                       stream.write "* Direct super classes: "
+                       stream.write direct_supers.join(", ")
+               end
+
+               var supers = [for s in mclass.in_hierarchy(mainmodule).greaters do s.name]
+               supers.remove mclass.name
+               if not supers.is_empty then
+                       alpha_comparator.sort supers
+                       stream.write line_separator
+                       stream.write "* All super classes: "
+                       stream.write supers.join(", ")
+               end
+
+               var direct_subs = [for s in mclass.in_hierarchy(mainmodule).direct_smallers do s.name]
+               if not direct_subs.is_empty then
+                       alpha_comparator.sort direct_subs
+                       stream.write line_separator
+                       stream.write "* Direct sub classes: "
+                       stream.write direct_subs.join(", ")
+               end
+
+               var subs = [for s in mclass.in_hierarchy(mainmodule).smallers do s.name]
+               subs.remove mclass.name
+               if not subs.is_empty then
+                       alpha_comparator.sort subs
+                       stream.write line_separator
+                       stream.write "* All sub classes: "
+                       stream.write subs.join(", ")
+               end
+
+               # List other properties
+               stream.write line_separator*2
+               stream.write "## Properties"
+               stream.write line_separator
+               var props = mclass.all_mproperties(mainmodule, protected_visibility).to_a
+               alpha_comparator.sort props
+               for prop in props do
+                       if mclass.name == "Object" or prop.intro.mclassdef.mclass.name != "Object" then
+
+                               if prop.visibility == public_visibility then
+                                       stream.write "+ "
+                               else stream.write "~ " # protected_visibility
+
+                               if prop isa MMethod then
+                                       if prop.is_init and prop.name != "init" then stream.write "init "
+                                       if prop.is_new and prop.name != "new" then stream.write "new "
+                               end
+
+                               stream.write prop.name
+
+                               if prop isa MMethod then
+                                       stream.write prop.intro.msignature.to_s
+                               end
+
+                               var mdoc = prop.intro.mdoc
+                               if mdoc != null then
+                                       stream.write "  # "
+                                       stream.write mdoc.content.first
+                               end
+                               stream.write line_separator
+                       end
+               end
+       end
+
+       redef fun complete_mdoc do return mclass.intro.mdoc
+end
+
 private class AutocompletePhase
        super Phase
 
@@ -113,24 +240,41 @@ private class AutocompletePhase
                var compile_dir = "NIT_VIM_DIR".environ
                if compile_dir.is_empty then compile_dir = "HOME".environ / ".vim/nit"
                compile_dir.mkdir
-               var modules_stream = new OFStream.open(compile_dir / "modules.txt")
-               var classes_stream = new OFStream.open(compile_dir / "classes.txt")
-               var properties_stream = new OFStream.open(compile_dir / "properties.txt")
+
+               var modules_stream = new FileWriter.open(compile_dir / "modules.txt")
+               var classes_stream = new FileWriter.open(compile_dir / "classes.txt")
+               var constructors_stream = new FileWriter.open(compile_dir / "constructors.txt")
+               var types_stream = new FileWriter.open(compile_dir / "types.txt")
+               var properties_stream = new FileWriter.open(compile_dir / "properties.txt")
 
                # Got all known modules
                var model = mainmodule.model
                for mmodule in model.mmodules do
-                       mmodule.write_to_stream modules_stream
+                       mmodule.write_doc(mainmodule, modules_stream)
                end
 
                # TODO list other modules from the Nit lib
-               # TODO list submodules
 
                # Get all known classes
                for mclass in model.mclasses do
                        if not mainmodule.is_visible(mclass.intro_mmodule, public_visibility) then continue
+                       var mclass_intro = mclass.intro
+
+                       # Can it be instantiated?
+                       if mclass.kind != interface_kind and mclass.kind != abstract_kind then
 
-                       mclass.intro.write_to_stream classes_stream
+                               for prop in mclass.all_mproperties(mainmodule, public_visibility) do
+                                       if prop isa MMethod and prop.is_init then
+                                               mclass_intro.target_constructor = prop.intro
+                                               mclass_intro.write_doc(mainmodule, constructors_stream)
+                                       end
+                               end
+                               mclass_intro.target_constructor = null
+                       end
+
+                       # Always add to types and classes
+                       mclass.mclass_type.write_doc(mainmodule, classes_stream)
+                       mclass.mclass_type.write_doc(mainmodule, types_stream)
                end
 
                # Get all known properties
@@ -138,17 +282,24 @@ private class AutocompletePhase
                        var intro_mmodule = mproperty.intro_mclassdef.mmodule
                        if not mainmodule.is_visible(intro_mmodule, public_visibility) then continue
 
+                       # Is it a virtual type?
+                       if mproperty isa MVirtualTypeProp then
+                               mproperty.intro.write_doc(mainmodule, types_stream)
+                               continue
+                       end
+
                        # Skip properties beginning with @ or _
                        var first_letter = mproperty.name.chars.first
                        if first_letter == '@' or first_letter == '_' then continue
 
-                       mproperty.intro.write_to_stream properties_stream
+                       mproperty.intro.write_doc(mainmodule, properties_stream)
                end
 
                # Close streams
-               for stream in [modules_stream, classes_stream, properties_stream] do
-                       stream.close
+               for stream in [modules_stream, classes_stream, properties_stream,
+                       types_stream, constructors_stream] do
 
+                       stream.close
                        var error = stream.last_error
                        if error != null then
                                toolcontext.error(null, "Failed to write Vim autocomplete file: {error}")