0846e852801426e42171aa96fea40ecfe9b69e3a
[nit.git] / src / doc / vim_autocomplete.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # Generate files used by the Vim plugin to autocomplete with doc
16 #
17 # There is 3 files generated, each with a different target: modules, types,
18 # properties and constructors. Each line describe a different entity,
19 # with 4 values:
20 #
21 # 1. Short name to use in autocompletion
22 # 2. Full signature
23 # 3. Doc synopsis
24 # 4. Full doc with extra
25 #
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
28 # duplicated.
29 module vim_autocomplete
30
31 import modelbuilder
32 import phase
33 import modelize::modelize_class
34 import model::model_collect
35
36 redef class ToolContext
37 # Phase generating the files for the Vim plugin
38 var autocomplete_phase: Phase = new AutocompletePhase(self, [modelize_class_phase])
39
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")
43
44 init
45 do
46 super
47 option_context.add_option opt_vim_autocomplete
48 opt_vim_autocomplete.hidden = true
49 end
50 end
51
52 redef class Model
53
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
58 return view
59 end
60 end
61
62 redef class MEntity
63 private fun field_separator: String do return "#====#"
64 private fun line_separator: String do return "#nnnn#"
65
66 private fun write_doc(mainmodule: MModule, stream: Writer)
67 do
68 # 1. Short name for autocompletion
69 stream.write complete_name
70 stream.write field_separator
71
72 # 2. Full signature
73 stream.write complete_name
74 write_signature_to_stream(stream)
75 stream.write field_separator
76
77 # 3. Doc synopsis
78 var mdoc = complete_mdoc
79 if mdoc != null then
80 stream.write mdoc.content.first
81 end
82
83 # 4. Full doc with extra
84 stream.write field_separator
85 stream.write "# "
86 stream.write full_name
87 write_signature_to_stream(stream)
88 if mdoc != null then
89 for i in 2.times do stream.write line_separator
90 stream.write mdoc.content.join(line_separator)
91 end
92
93 write_location(mainmodule, stream)
94
95 write_extra_doc(mainmodule, stream)
96
97 stream.write "\n"
98 end
99
100 private fun write_signature_to_stream(stream: Writer) do end
101
102 # Actual name used in completion
103 private fun complete_name: String do return name
104
105 # Doc to use in completion
106 private fun complete_mdoc: nullable MDoc do return mdoc
107
108 # Extra auto documentation to append to the `stream`
109 private fun write_extra_doc(mainmodule: MModule, stream: Writer) do end
110
111 # Location (file and line when available) of related declarations
112 private fun write_location(mainmodule: MModule, stream: Writer)
113 do
114 for i in 2.times do stream.write line_separator
115 stream.write "## Location"
116 stream.write line_separator
117 stream.write "* {location}"
118 end
119 end
120
121 redef class MMethodDef
122 redef fun write_signature_to_stream(stream)
123 do
124 var msignature = msignature
125 if msignature != null then
126 stream.write msignature.to_s
127 end
128 end
129
130 redef fun write_location(mainmodule, stream)
131 do
132 for i in 2.times do stream.write line_separator
133 stream.write "## Location of introduction and refinements"
134
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
139 end
140
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
145 stream.write "* {l}"
146 if locations.length > 1 then stream.write " ({locations.length-1} more)"
147 end
148 end
149 end
150
151 redef class MAttributeDef
152 redef fun write_signature_to_stream(stream)
153 do
154 var static_mtype = static_mtype
155 if static_mtype != null then
156 stream.write stream.to_s
157 end
158 end
159 end
160
161 # Use `MClassDef` as anchor for its constructors only
162 redef class MClassDef
163 private var target_constructor: nullable MMethodDef = null
164
165 redef fun complete_name
166 do
167 var target_constructor = target_constructor
168 assert target_constructor != null
169
170 var params
171 var mparameters = mclass.mparameters
172 if not mparameters.is_empty then
173 params = "[{mparameters.join(", ")}]"
174 else
175 params = ""
176 end
177
178 if target_constructor.name != "init" and target_constructor.name != "new" then
179 return name + params + "." + target_constructor.name
180 end
181
182 return name + params
183 end
184
185 redef fun complete_mdoc
186 do
187 var target_constructor = target_constructor
188 assert target_constructor != null
189
190 if target_constructor.name != "init" and target_constructor.name != "new" then
191 return target_constructor.mdoc
192 end
193
194 return mdoc
195 end
196 end
197
198 redef class MClassType
199 redef fun write_extra_doc(mainmodule, stream)
200 do
201 # Super classes
202 stream.write line_separator*2
203 stream.write "## Class hierarchy"
204
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(", ")
211 end
212
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(", ")
220 end
221
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(", ")
228 end
229
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(", ")
237 end
238
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
245 for prop in props do
246 if mclass.name == "Object" or prop.intro.mclassdef.mclass.name != "Object" then
247 prop.write_synopsis(mainmodule, stream)
248 end
249 end
250 end
251
252 redef fun complete_mdoc do return mclass.intro.mdoc
253
254 redef fun write_location(mainmodule, stream)
255 do
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}"
261 end
262 end
263 end
264
265 private class AutocompletePhase
266 super Phase
267
268 redef fun process_mainmodule(mainmodule, given_mmodules)
269 do
270 if not toolcontext.opt_vim_autocomplete.value then return
271
272 var compile_dir = "NIT_VIM_DIR".environ
273 if compile_dir.is_empty then compile_dir = "HOME".environ / ".vim/nit"
274 compile_dir.mkdir
275
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")
281
282 # Got all known modules
283 var model = mainmodule.model
284 for mmodule in model.mmodules do
285 mmodule.write_doc(mainmodule, modules_stream)
286 end
287
288 # TODO list other modules from the Nit lib
289
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
294
295 # Can it be instantiated?
296 if mclass.kind != interface_kind and mclass.kind != abstract_kind then
297
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)
302 end
303 end
304 mclass_intro.target_constructor = null
305 end
306
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)
310 end
311
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
316
317 # Is it a virtual type?
318 if mproperty isa MVirtualTypeProp then
319 mproperty.intro.write_doc(mainmodule, types_stream)
320 continue
321 end
322
323 # Skip properties beginning with @ or _
324 var first_letter = mproperty.name.chars.first
325 if first_letter == '@' or first_letter == '_' then continue
326
327 mproperty.intro.write_doc(mainmodule, properties_stream)
328 end
329
330 # Close streams
331 for stream in [modules_stream, classes_stream, properties_stream,
332 types_stream, constructors_stream] do
333
334 stream.close
335 var error = stream.last_error
336 if error != null then
337 toolcontext.error(null, "Error: failed to write Vim autocomplete file: {error}.")
338 end
339 end
340 end
341 end
342
343 redef class MModule
344 redef fun write_extra_doc(mainmodule, stream)
345 do
346 # Introduced classes
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"
352
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}"
358 end
359 end
360
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)
365 end
366
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
372
373 for p in prop_intros do
374 p.mproperty.write_synopsis(mainmodule, stream)
375 end
376 end
377 end
378 end
379
380 redef class MProperty
381 private fun write_synopsis(mainmodule: MModule, stream: Writer)
382 do
383 if visibility == public_visibility then
384 stream.write "+ "
385 else stream.write "~ " # protected_visibility
386
387 if self isa MMethod then
388 if is_new and name != "new" then
389 stream.write "new "
390 else if is_init and name != "init" then
391 stream.write "init "
392 end
393 end
394
395 stream.write name
396
397 if self isa MMethod then
398 var intro = intro
399 assert intro isa MMethodDef
400 var msignature = intro.msignature
401 if msignature != null then
402 stream.write msignature.to_s
403 end
404 end
405
406 var mdoc = intro.mdoc
407 if mdoc != null then
408 stream.write " # "
409 stream.write mdoc.content.first
410 end
411 stream.write line_separator
412 end
413 end