1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Generate files used by the Vim plugin to autocomplete with doc
17 # There is 3 files generated, each with a different target: modules, types,
18 # properties and constructors. Each line describe a different entity,
21 # 1. Short name to use in autocompletion
24 # 4. Full doc with extra
26 # The priority with those files is for them to be analyzed efficiently, for
27 # this reason, the data is prepared in advance and some information may be
29 module vim_autocomplete
33 import modelize
::modelize_class
34 import model
::model_collect
36 redef class ToolContext
37 # Phase generating the files for the Vim plugin
38 var autocomplete_phase
: Phase = new AutocompletePhase(self, [modelize_class_phase
])
40 # Shall we generate the files for the Vim plugin?
41 var opt_vim_autocomplete
= new OptionBool(
42 "Generate metadata files used by the Vim plugin for autocompletion", "--vim-autocomplete")
47 option_context
.add_option opt_vim_autocomplete
48 opt_vim_autocomplete
.hidden
= true
54 # Get a custom view for vimautocomplete.
55 private fun vim_view
: ModelView do
56 var view
= new ModelView(self)
57 view
.min_visibility
= protected_visibility
63 private fun field_separator
: String do return "#====#"
64 private fun line_separator
: String do return "#nnnn#"
66 private fun write_doc
(mainmodule
: MModule, stream
: Writer)
68 # 1. Short name for autocompletion
69 stream
.write complete_name
70 stream
.write field_separator
73 stream
.write complete_name
74 write_signature_to_stream
(stream
)
75 stream
.write field_separator
78 var mdoc
= complete_mdoc
80 stream
.write mdoc
.content
.first
83 # 4. Full doc with extra
84 stream
.write field_separator
86 stream
.write full_name
87 write_signature_to_stream
(stream
)
89 for i
in 2.times
do stream
.write line_separator
90 stream
.write mdoc
.content
.join
(line_separator
)
93 write_location
(mainmodule
, stream
)
95 write_extra_doc
(mainmodule
, stream
)
100 private fun write_signature_to_stream
(stream
: Writer) do end
102 # Actual name used in completion
103 private fun complete_name
: String do return name
105 # Doc to use in completion
106 private fun complete_mdoc
: nullable MDoc do return mdoc
108 # Extra auto documentation to append to the `stream`
109 private fun write_extra_doc
(mainmodule
: MModule, stream
: Writer) do end
111 # Location (file and line when available) of related declarations
112 private fun write_location
(mainmodule
: MModule, stream
: Writer)
114 for i
in 2.times
do stream
.write line_separator
115 stream
.write
"## Location"
116 stream
.write line_separator
117 stream
.write
"* {location}"
121 redef class MMethodDef
122 redef fun write_signature_to_stream
(stream
)
124 var msignature
= msignature
125 if msignature
!= null then
126 stream
.write msignature
.to_s
130 redef fun write_location
(mainmodule
, stream
)
132 for i
in 2.times
do stream
.write line_separator
133 stream
.write
"## Location of introduction and refinements"
135 # Group locations in the same file
136 var file_to_location
= new MultiHashMap[nullable SourceFile, Location]
137 for c
in mproperty
.mpropdefs
do
138 file_to_location
[c
.location
.file
].add c
.location
141 # Write one file per location
142 for file
, locations
in file_to_location
do
143 var l
= locations
.first
144 stream
.write line_separator
146 if locations
.length
> 1 then stream
.write
" ({locations.length-1} more)"
151 redef class MAttributeDef
152 redef fun write_signature_to_stream
(stream
)
154 var static_mtype
= static_mtype
155 if static_mtype
!= null then
156 stream
.write stream
.to_s
161 # Use `MClassDef` as anchor for its constructors only
162 redef class MClassDef
163 private var target_constructor
: nullable MMethodDef = null
165 redef fun complete_name
167 var target_constructor
= target_constructor
168 assert target_constructor
!= null
171 var mparameters
= mclass
.mparameters
172 if not mparameters
.is_empty
then
173 params
= "[{mparameters.join(", ")}]"
178 if target_constructor
.name
!= "init" and target_constructor
.name
!= "new" then
179 return name
+ params
+ "." + target_constructor
.name
185 redef fun complete_mdoc
187 var target_constructor
= target_constructor
188 assert target_constructor
!= null
190 if target_constructor
.name
!= "init" and target_constructor
.name
!= "new" then
191 return target_constructor
.mdoc
198 redef class MClassType
199 redef fun write_extra_doc
(mainmodule
, stream
)
202 stream
.write line_separator
*2
203 stream
.write
"## Class hierarchy"
205 var direct_supers
= [for s
in mclass
.in_hierarchy
(mainmodule
).direct_greaters
do s
.name
]
206 if not direct_supers
.is_empty
then
207 alpha_comparator
.sort direct_supers
208 stream
.write line_separator
209 stream
.write
"* Direct super classes: "
210 stream
.write direct_supers
.join
(", ")
213 var supers
= [for s
in mclass
.in_hierarchy
(mainmodule
).greaters
do s
.name
]
214 supers
.remove mclass
.name
215 if not supers
.is_empty
then
216 alpha_comparator
.sort supers
217 stream
.write line_separator
218 stream
.write
"* All super classes: "
219 stream
.write supers
.join
(", ")
222 var direct_subs
= [for s
in mclass
.in_hierarchy
(mainmodule
).direct_smallers
do s
.name
]
223 if not direct_subs
.is_empty
then
224 alpha_comparator
.sort direct_subs
225 stream
.write line_separator
226 stream
.write
"* Direct sub classes: "
227 stream
.write direct_subs
.join
(", ")
230 var subs
= [for s
in mclass
.in_hierarchy
(mainmodule
).smallers
do s
.name
]
231 subs
.remove mclass
.name
232 if not subs
.is_empty
then
233 alpha_comparator
.sort subs
234 stream
.write line_separator
235 stream
.write
"* All sub classes: "
236 stream
.write subs
.join
(", ")
239 # List other properties
240 stream
.write line_separator
*2
241 stream
.write
"## Properties"
242 stream
.write line_separator
243 var props
= mclass
.collect_accessible_mproperties
(model
.protected_view
).to_a
244 alpha_comparator
.sort props
246 if mclass
.name
== "Object" or prop
.intro
.mclassdef
.mclass
.name
!= "Object" then
247 prop
.write_synopsis
(mainmodule
, stream
)
252 redef fun complete_mdoc
do return mclass
.intro
.mdoc
254 redef fun write_location
(mainmodule
, stream
)
256 for i
in 2.times
do stream
.write line_separator
257 stream
.write
"## Location of introduction and refinements"
258 for c
in mclass
.mclassdefs
do
259 stream
.write line_separator
260 stream
.write
"* {c.location}"
265 private class AutocompletePhase
268 redef fun process_mainmodule
(mainmodule
, given_mmodules
)
270 if not toolcontext
.opt_vim_autocomplete
.value
then return
272 var compile_dir
= "NIT_VIM_DIR".environ
273 if compile_dir
.is_empty
then compile_dir
= "HOME".environ
/ ".vim/nit"
276 var modules_stream
= new FileWriter.open
(compile_dir
/ "modules.txt")
277 var classes_stream
= new FileWriter.open
(compile_dir
/ "classes.txt")
278 var constructors_stream
= new FileWriter.open
(compile_dir
/ "constructors.txt")
279 var types_stream
= new FileWriter.open
(compile_dir
/ "types.txt")
280 var properties_stream
= new FileWriter.open
(compile_dir
/ "properties.txt")
282 # Got all known modules
283 var model
= mainmodule
.model
284 for mmodule
in model
.mmodules
do
285 mmodule
.write_doc
(mainmodule
, modules_stream
)
288 # TODO list other modules from the Nit lib
290 # Get all known classes
291 for mclass
in model
.mclasses
do
292 if not mainmodule
.is_visible
(mclass
.intro_mmodule
, public_visibility
) then continue
293 var mclass_intro
= mclass
.intro
295 # Can it be instantiated?
296 if mclass
.kind
!= interface_kind
and mclass
.kind
!= abstract_kind
then
298 for prop
in mclass
.collect_accessible_mproperties
(model
.public_view
) do
299 if prop
isa MMethod and prop
.is_init
then
300 mclass_intro
.target_constructor
= prop
.intro
301 mclass_intro
.write_doc
(mainmodule
, constructors_stream
)
304 mclass_intro
.target_constructor
= null
307 # Always add to types and classes
308 mclass
.mclass_type
.write_doc
(mainmodule
, classes_stream
)
309 mclass
.mclass_type
.write_doc
(mainmodule
, types_stream
)
312 # Get all known properties
313 for mproperty
in model
.mproperties
do
314 var intro_mmodule
= mproperty
.intro_mclassdef
.mmodule
315 if not mainmodule
.is_visible
(intro_mmodule
, public_visibility
) then continue
317 # Is it a virtual type?
318 if mproperty
isa MVirtualTypeProp then
319 mproperty
.intro
.write_doc
(mainmodule
, types_stream
)
323 # Skip properties beginning with @ or _
324 var first_letter
= mproperty
.name
.chars
.first
325 if first_letter
== '@' or first_letter
== '_' then continue
327 mproperty
.intro
.write_doc
(mainmodule
, properties_stream
)
331 for stream
in [modules_stream
, classes_stream
, properties_stream
,
332 types_stream
, constructors_stream
] do
335 var error
= stream
.last_error
336 if error
!= null then
337 toolcontext
.error
(null, "Error: failed to write Vim autocomplete file: {error}.")
344 redef fun write_extra_doc
(mainmodule
, stream
)
347 var class_intros
= collect_intro_mclasses
(model
.protected_view
).to_a
348 if class_intros
.not_empty
then
349 alpha_comparator
.sort class_intros
350 stream
.write line_separator
*2
351 stream
.write
"## Introduced classes"
353 for c
in class_intros
do
354 stream
.write line_separator
355 stream
.write
"* {c.name}"
356 var doc
= c
.intro
.mdoc
357 if doc
!= null then stream
.write
": {doc.content.first}"
361 # Introduced properties
362 var prop_intros
= new Array[MPropDef]
363 for c
in mclassdefs
do
364 prop_intros
.add_all c
.collect_intro_mpropdefs
(model
.protected_view
)
367 if prop_intros
.not_empty
then
368 alpha_comparator
.sort prop_intros
369 stream
.write line_separator
*2
370 stream
.write
"## Introduced properties"
371 stream
.write line_separator
373 for p
in prop_intros
do
374 p
.mproperty
.write_synopsis
(mainmodule
, stream
)
380 redef class MProperty
381 private fun write_synopsis
(mainmodule
: MModule, stream
: Writer)
383 if visibility
== public_visibility
then
385 else stream
.write
"~ " # protected_visibility
387 if self isa MMethod then
388 if is_new
and name
!= "new" then
390 else if is_init
and name
!= "init" then
397 if self isa MMethod then
399 assert intro
isa MMethodDef
400 var msignature
= intro
.msignature
401 if msignature
!= null then
402 stream
.write msignature
.to_s
406 var mdoc
= intro
.mdoc
409 stream
.write mdoc
.content
.first
411 stream
.write line_separator