Generate files used by the Vim plugin to autocomplete with doc

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
  3. Doc synopsis
  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 advance and some information may be duplicated.

Introduced classes

Redefined classes

redef class MAttributeDef

nitc :: vim_autocomplete $ MAttributeDef

A local definition of an attribute
redef class MClassDef

nitc :: vim_autocomplete $ MClassDef

Use MClassDef as anchor for its constructors only
redef class MClassType

nitc :: vim_autocomplete $ MClassType

A type based on a class.
redef abstract class MEntity

nitc :: vim_autocomplete $ MEntity

A named and possibly documented entity in the model.
redef class MMethodDef

nitc :: vim_autocomplete $ MMethodDef

A local definition of a method
redef class MModule

nitc :: vim_autocomplete $ MModule

A Nit module is usually associated with a Nit source file.
redef abstract class MProperty

nitc :: vim_autocomplete $ MProperty

A service (global property) that generalize method, attribute, etc.
redef class ToolContext

nitc :: vim_autocomplete $ ToolContext

Global context for tools

All class definitions

redef class MAttributeDef

nitc :: vim_autocomplete $ MAttributeDef

A local definition of an attribute
redef class MClassDef

nitc :: vim_autocomplete $ MClassDef

Use MClassDef as anchor for its constructors only
redef class MClassType

nitc :: vim_autocomplete $ MClassType

A type based on a class.
redef abstract class MEntity

nitc :: vim_autocomplete $ MEntity

A named and possibly documented entity in the model.
redef class MMethodDef

nitc :: vim_autocomplete $ MMethodDef

A local definition of a method
redef class MModule

nitc :: vim_autocomplete $ MModule

A Nit module is usually associated with a Nit source file.
redef abstract class MProperty

nitc :: vim_autocomplete $ MProperty

A service (global property) that generalize method, attribute, etc.
redef class ToolContext

nitc :: vim_autocomplete $ ToolContext

Global context for tools
package_diagram nitc::vim_autocomplete vim_autocomplete nitc::model_collect model_collect nitc::vim_autocomplete->nitc::model_collect nitc::model_filters model_filters nitc::model_collect->nitc::model_filters ...nitc::model_filters ... ...nitc::model_filters->nitc::model_filters nitc::nitpick nitpick nitc::nitpick->nitc::vim_autocomplete a_star-m a_star-m a_star-m->nitc::nitpick a_star-m... ... a_star-m...->a_star-m

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module annotation

nitc :: annotation

Management and utilities on annotations
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module console

console :: console

Defines some ANSI Terminal Control Escape Sequences.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module digraph

graph :: digraph

Implementation of directed graphs, also called digraphs.
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module ini

ini :: ini

Read and write INI configuration files
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module lexer

nitc :: lexer

Lexer and its tokens.
module lexer_work

nitc :: lexer_work

Internal algorithm and data structures for the Nit lexer
module list

core :: list

This module handle double linked lists
module literal

nitc :: literal

Parsing of literal values in the abstract syntax tree.
module loader

nitc :: loader

Loading of Nit source files
module location

nitc :: location

Nit source-file and locations in source-file
module math

core :: math

Mathematical operations
module mdoc

nitc :: mdoc

Documentation of model entities
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module mmodule

nitc :: mmodule

modules and module hierarchies in the metamodel
module mmodule_data

nitc :: mmodule_data

Define and retrieve data in modules
module model

nitc :: model

Classes, types and properties
module model_base

nitc :: model_base

The abstract concept of model and related common things
module model_examples

nitc :: model_examples

Examples for Model entities
module modelbuilder_base

nitc :: modelbuilder_base

Load nit source files and build the associated model
module modelize_class

nitc :: modelize_class

Analysis and verification of class definitions to instantiate model element
module modelize_property

nitc :: modelize_property

Analysis and verification of property definitions to instantiate model element
module more_collections

more_collections :: more_collections

Highly specific, but useful, collections-related classes.
module mpackage

nitc :: mpackage

Modelisation of a Nit package
module native

core :: native

Native structures for text and bytes
module nitpm_shared

nitc :: nitpm_shared

Services related to the Nit package manager
module numeric

core :: numeric

Advanced services for Numeric types
module opts

opts :: opts

Management of options on the command line
module ordered_tree

ordered_tree :: ordered_tree

Manipulation and presentation of ordered trees.
module parse_annotations

nitc :: parse_annotations

Simple annotation parsing
module parser

nitc :: parser

Parser.
module parser_nodes

nitc :: parser_nodes

AST nodes of the Nit language
module parser_prod

nitc :: parser_prod

Production AST nodes full definition.
module parser_work

nitc :: parser_work

Internal algorithm and data structures for the Nit parser
module phase

nitc :: phase

Phases of the processing of nit programs
module poset

poset :: poset

Pre order sets and partial order set (ie hierarchies)
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module tables

nitc :: tables

Module that interfaces the parsing tables.
module template

template :: template

Basic template system
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module toolcontext

nitc :: toolcontext

Common command-line tool infrastructure than handle options and error messages
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O
module version

nitc :: version

This file was generated by git-gen-version.sh

Parents

module model_collect

nitc :: model_collect

Collect things from the model.

Children

module nitpick

nitc :: nitpick

A program that collect potential style and code issues

Descendants

module a_star-m

a_star-m

# Generate files used by the Vim plugin to autocomplete with doc
#
# 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
# 3. Doc synopsis
# 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 advance and some information may be
# duplicated.
module vim_autocomplete

import modelbuilder
import phase
import modelize::modelize_class
import model::model_collect

redef class ToolContext
	# Phase generating the files for the Vim plugin
	var autocomplete_phase: Phase = new AutocompletePhase(self, [modelize_class_phase])

	# Shall we generate the files for the Vim plugin?
	var opt_vim_autocomplete = new OptionBool(
		"Generate metadata files used by the Vim plugin for autocompletion", "--vim-autocomplete")

	init
	do
		super
		option_context.add_option opt_vim_autocomplete
		opt_vim_autocomplete.hidden = true
	end
end

redef class MEntity
	private fun field_separator: String do return "#====#"
	private fun line_separator: String do return "#nnnn#"

	private fun write_doc(model: Model, mainmodule: MModule, stream: Writer)
	do
		# 1. Short name for autocompletion
		stream.write complete_name
		stream.write field_separator

		# 2. Full signature
		stream.write complete_name
		write_signature_to_stream(stream)
		stream.write field_separator

		# 3. Doc synopsis
		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
			for i in 2.times do stream.write line_separator
			stream.write mdoc.content.join(line_separator)
		end

		write_location(mainmodule, stream)

		write_extra_doc(model, mainmodule, stream)

		stream.write "\n"
	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(model: Model, mainmodule: MModule, stream: Writer) do end

	# Location (file and line when available) of related declarations
	private fun write_location(mainmodule: MModule, stream: Writer)
	do
		for i in 2.times do stream.write line_separator
		stream.write "## Location"
		stream.write line_separator
		stream.write "* {location}"
	end
end

redef class MMethodDef
	redef fun write_signature_to_stream(stream)
	do
		var msignature = msignature
		if msignature != null then
			stream.write msignature.to_s
		end
	end

	redef fun write_location(mainmodule, stream)
	do
		for i in 2.times do stream.write line_separator
		stream.write "## Location of introduction and refinements"

		# Group locations in the same file
		var file_to_location = new MultiHashMap[nullable SourceFile, Location]
		for c in mproperty.mpropdefs do
			file_to_location[c.location.file].add c.location
		end

		# Write one file per location
		for file, locations in file_to_location do
			var l = locations.first
			stream.write line_separator
			stream.write "* {l}"
			if locations.length > 1 then stream.write " ({locations.length-1} more)"
		end
	end
end

redef class MAttributeDef
	redef fun write_signature_to_stream(stream)
	do
		var static_mtype = static_mtype
		if static_mtype != null then
			stream.write stream.to_s
		end
	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(model, 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.collect_accessible_mproperties(mainmodule).to_a
		alpha_comparator.sort props
		for prop in props do
			if mclass.name == "Object" or prop.intro.mclassdef.mclass.name != "Object" then
				prop.write_synopsis(mainmodule, stream)
			end
		end
	end

	redef fun complete_mdoc do return mclass.intro.mdoc

	redef fun write_location(mainmodule, stream)
	do
		for i in 2.times do stream.write line_separator
		stream.write "## Location of introduction and refinements"
		for c in mclass.mclassdefs do
			stream.write line_separator
			stream.write "* {c.location}"
		end
	end
end

private class AutocompletePhase
	super Phase

	redef fun process_mainmodule(mainmodule, given_mmodules)
	do
		if not toolcontext.opt_vim_autocomplete.value then return

		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 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_doc(model, mainmodule, modules_stream)
		end

		# TODO list other modules from the Nit lib

		# 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.collect_accessible_mproperties(mainmodule) do
					if prop isa MMethod and prop.is_init then
						mclass_intro.target_constructor = prop.intro
						mclass_intro.write_doc(model, mainmodule, constructors_stream)
					end
				end
				mclass_intro.target_constructor = null
			end

			# Always add to types and classes
			mclass.mclass_type.write_doc(model, mainmodule, classes_stream)
			mclass.mclass_type.write_doc(model, mainmodule, types_stream)
		end

		# Get all known properties
		for mproperty in model.mproperties do
			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(model, 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(model, mainmodule, properties_stream)
		end

		# Close streams
		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, "Error: failed to write Vim autocomplete file: {error}.")
			end
		end
	end
end

redef class MModule
	redef fun write_extra_doc(model, mainmodule, stream)
	do
		# Introduced classes
		var class_intros = collect_intro_mclasses.to_a
		if class_intros.not_empty then
			alpha_comparator.sort class_intros
			stream.write line_separator*2
			stream.write "## Introduced classes"

			for c in class_intros do
				stream.write line_separator
				stream.write "* {c.name}"
				var doc = c.intro.mdoc
				if doc != null then stream.write ": {doc.content.first}"
			end
		end

		# Introduced properties
		var prop_intros = new Array[MPropDef]
		for c in mclassdefs do
			prop_intros.add_all c.collect_intro_mpropdefs
		end

		if prop_intros.not_empty then
			alpha_comparator.sort prop_intros
			stream.write line_separator*2
			stream.write "## Introduced properties"
			stream.write line_separator

			for p in prop_intros do
				p.mproperty.write_synopsis(mainmodule, stream)
			end
		end
	end
end

redef class MProperty
	private fun write_synopsis(mainmodule: MModule, stream: Writer)
	do
		if visibility == public_visibility then
			stream.write "+ "
		else stream.write "~ " # protected_visibility

		if self isa MMethod then
			if is_new and name != "new" then
				stream.write "new "
			else if is_init and name != "init" then
				stream.write "init "
			end
		end

		stream.write name

		if self isa MMethod then
			var intro = intro
			assert intro isa MMethodDef
			var msignature = intro.msignature
			if msignature != null then
				stream.write msignature.to_s
			end
		end

		var mdoc = intro.mdoc
		if mdoc != null then
			stream.write "  # "
			stream.write mdoc.content.first
		end
		stream.write line_separator
	end
end
src/doc/vim_autocomplete.nit:15,1--403,3