# 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
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
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
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
+
+ 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
- mclass.intro.write_to_stream classes_stream
+ # 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
var intro_mmodule = mproperty.intro_mclassdef.mmodule
if not mainmodule.is_visible(intro_mmodule, public_visibility) then continue
- mproperty.intro.write_to_stream properties_stream
+ # 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_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}")
+ toolcontext.error(null, "Error: failed to write Vim autocomplete file: {error}.")
end
end
end