Merge: doc: fixed some typos and other misc. corrections
[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 MEntity
53 private fun field_separator: String do return "#====#"
54 private fun line_separator: String do return "#nnnn#"
55
56 private fun write_doc(model: Model, mainmodule: MModule, stream: Writer)
57 do
58 # 1. Short name for autocompletion
59 stream.write complete_name
60 stream.write field_separator
61
62 # 2. Full signature
63 stream.write complete_name
64 write_signature_to_stream(stream)
65 stream.write field_separator
66
67 # 3. Doc synopsis
68 var mdoc = complete_mdoc
69 if mdoc != null then
70 stream.write mdoc.content.first
71 end
72
73 # 4. Full doc with extra
74 stream.write field_separator
75 stream.write "# "
76 stream.write full_name
77 write_signature_to_stream(stream)
78 if mdoc != null then
79 for i in 2.times do stream.write line_separator
80 stream.write mdoc.content.join(line_separator)
81 end
82
83 write_location(mainmodule, stream)
84
85 write_extra_doc(model, mainmodule, stream)
86
87 stream.write "\n"
88 end
89
90 private fun write_signature_to_stream(stream: Writer) do end
91
92 # Actual name used in completion
93 private fun complete_name: String do return name
94
95 # Doc to use in completion
96 private fun complete_mdoc: nullable MDoc do return mdoc
97
98 # Extra auto documentation to append to the `stream`
99 private fun write_extra_doc(model: Model, mainmodule: MModule, stream: Writer) do end
100
101 # Location (file and line when available) of related declarations
102 private fun write_location(mainmodule: MModule, stream: Writer)
103 do
104 for i in 2.times do stream.write line_separator
105 stream.write "## Location"
106 stream.write line_separator
107 stream.write "* {location}"
108 end
109 end
110
111 redef class MMethodDef
112 redef fun write_signature_to_stream(stream)
113 do
114 var msignature = msignature
115 if msignature != null then
116 stream.write msignature.to_s
117 end
118 end
119
120 redef fun write_location(mainmodule, stream)
121 do
122 for i in 2.times do stream.write line_separator
123 stream.write "## Location of introduction and refinements"
124
125 # Group locations in the same file
126 var file_to_location = new MultiHashMap[nullable SourceFile, Location]
127 for c in mproperty.mpropdefs do
128 file_to_location[c.location.file].add c.location
129 end
130
131 # Write one file per location
132 for file, locations in file_to_location do
133 var l = locations.first
134 stream.write line_separator
135 stream.write "* {l}"
136 if locations.length > 1 then stream.write " ({locations.length-1} more)"
137 end
138 end
139 end
140
141 redef class MAttributeDef
142 redef fun write_signature_to_stream(stream)
143 do
144 var static_mtype = static_mtype
145 if static_mtype != null then
146 stream.write stream.to_s
147 end
148 end
149 end
150
151 # Use `MClassDef` as anchor for its constructors only
152 redef class MClassDef
153 private var target_constructor: nullable MMethodDef = null
154
155 redef fun complete_name
156 do
157 var target_constructor = target_constructor
158 assert target_constructor != null
159
160 var params
161 var mparameters = mclass.mparameters
162 if not mparameters.is_empty then
163 params = "[{mparameters.join(", ")}]"
164 else
165 params = ""
166 end
167
168 if target_constructor.name != "init" and target_constructor.name != "new" then
169 return name + params + "." + target_constructor.name
170 end
171
172 return name + params
173 end
174
175 redef fun complete_mdoc
176 do
177 var target_constructor = target_constructor
178 assert target_constructor != null
179
180 if target_constructor.name != "init" and target_constructor.name != "new" then
181 return target_constructor.mdoc
182 end
183
184 return mdoc
185 end
186 end
187
188 redef class MClassType
189 redef fun write_extra_doc(model, mainmodule, stream)
190 do
191 # Super classes
192 stream.write line_separator*2
193 stream.write "## Class hierarchy"
194
195 var direct_supers = [for s in mclass.in_hierarchy(mainmodule).direct_greaters do s.name]
196 if not direct_supers.is_empty then
197 alpha_comparator.sort direct_supers
198 stream.write line_separator
199 stream.write "* Direct super classes: "
200 stream.write direct_supers.join(", ")
201 end
202
203 var supers = [for s in mclass.in_hierarchy(mainmodule).greaters do s.name]
204 supers.remove mclass.name
205 if not supers.is_empty then
206 alpha_comparator.sort supers
207 stream.write line_separator
208 stream.write "* All super classes: "
209 stream.write supers.join(", ")
210 end
211
212 var direct_subs = [for s in mclass.in_hierarchy(mainmodule).direct_smallers do s.name]
213 if not direct_subs.is_empty then
214 alpha_comparator.sort direct_subs
215 stream.write line_separator
216 stream.write "* Direct sub classes: "
217 stream.write direct_subs.join(", ")
218 end
219
220 var subs = [for s in mclass.in_hierarchy(mainmodule).smallers do s.name]
221 subs.remove mclass.name
222 if not subs.is_empty then
223 alpha_comparator.sort subs
224 stream.write line_separator
225 stream.write "* All sub classes: "
226 stream.write subs.join(", ")
227 end
228
229 # List other properties
230 stream.write line_separator*2
231 stream.write "## Properties"
232 stream.write line_separator
233 var props = mclass.collect_accessible_mproperties(mainmodule).to_a
234 alpha_comparator.sort props
235 for prop in props do
236 if mclass.name == "Object" or prop.intro.mclassdef.mclass.name != "Object" then
237 prop.write_synopsis(mainmodule, stream)
238 end
239 end
240 end
241
242 redef fun complete_mdoc do return mclass.intro.mdoc
243
244 redef fun write_location(mainmodule, stream)
245 do
246 for i in 2.times do stream.write line_separator
247 stream.write "## Location of introduction and refinements"
248 for c in mclass.mclassdefs do
249 stream.write line_separator
250 stream.write "* {c.location}"
251 end
252 end
253 end
254
255 private class AutocompletePhase
256 super Phase
257
258 redef fun process_mainmodule(mainmodule, given_mmodules)
259 do
260 if not toolcontext.opt_vim_autocomplete.value then return
261
262 var compile_dir = "NIT_VIM_DIR".environ
263 if compile_dir.is_empty then compile_dir = "HOME".environ / ".vim/nit"
264 compile_dir.mkdir
265
266 var modules_stream = new FileWriter.open(compile_dir / "modules.txt")
267 var classes_stream = new FileWriter.open(compile_dir / "classes.txt")
268 var constructors_stream = new FileWriter.open(compile_dir / "constructors.txt")
269 var types_stream = new FileWriter.open(compile_dir / "types.txt")
270 var properties_stream = new FileWriter.open(compile_dir / "properties.txt")
271
272 # Got all known modules
273 var model = mainmodule.model
274 for mmodule in model.mmodules do
275 mmodule.write_doc(model, mainmodule, modules_stream)
276 end
277
278 # TODO list other modules from the Nit lib
279
280 # Get all known classes
281 for mclass in model.mclasses do
282 if not mainmodule.is_visible(mclass.intro_mmodule, public_visibility) then continue
283 var mclass_intro = mclass.intro
284
285 # Can it be instantiated?
286 if mclass.kind != interface_kind and mclass.kind != abstract_kind then
287
288 for prop in mclass.collect_accessible_mproperties(mainmodule) do
289 if prop isa MMethod and prop.is_init then
290 mclass_intro.target_constructor = prop.intro
291 mclass_intro.write_doc(model, mainmodule, constructors_stream)
292 end
293 end
294 mclass_intro.target_constructor = null
295 end
296
297 # Always add to types and classes
298 mclass.mclass_type.write_doc(model, mainmodule, classes_stream)
299 mclass.mclass_type.write_doc(model, mainmodule, types_stream)
300 end
301
302 # Get all known properties
303 for mproperty in model.mproperties do
304 var intro_mmodule = mproperty.intro_mclassdef.mmodule
305 if not mainmodule.is_visible(intro_mmodule, public_visibility) then continue
306
307 # Is it a virtual type?
308 if mproperty isa MVirtualTypeProp then
309 mproperty.intro.write_doc(model, mainmodule, types_stream)
310 continue
311 end
312
313 # Skip properties beginning with @ or _
314 var first_letter = mproperty.name.chars.first
315 if first_letter == '@' or first_letter == '_' then continue
316
317 mproperty.intro.write_doc(model, mainmodule, properties_stream)
318 end
319
320 # Close streams
321 for stream in [modules_stream, classes_stream, properties_stream,
322 types_stream, constructors_stream] do
323
324 stream.close
325 var error = stream.last_error
326 if error != null then
327 toolcontext.error(null, "Error: failed to write Vim autocomplete file: {error}.")
328 end
329 end
330 end
331 end
332
333 redef class MModule
334 redef fun write_extra_doc(model, mainmodule, stream)
335 do
336 # Introduced classes
337 var class_intros = collect_intro_mclasses.to_a
338 if class_intros.not_empty then
339 alpha_comparator.sort class_intros
340 stream.write line_separator*2
341 stream.write "## Introduced classes"
342
343 for c in class_intros do
344 stream.write line_separator
345 stream.write "* {c.name}"
346 var doc = c.intro.mdoc
347 if doc != null then stream.write ": {doc.content.first}"
348 end
349 end
350
351 # Introduced properties
352 var prop_intros = new Array[MPropDef]
353 for c in mclassdefs do
354 prop_intros.add_all c.collect_intro_mpropdefs
355 end
356
357 if prop_intros.not_empty then
358 alpha_comparator.sort prop_intros
359 stream.write line_separator*2
360 stream.write "## Introduced properties"
361 stream.write line_separator
362
363 for p in prop_intros do
364 p.mproperty.write_synopsis(mainmodule, stream)
365 end
366 end
367 end
368 end
369
370 redef class MProperty
371 private fun write_synopsis(mainmodule: MModule, stream: Writer)
372 do
373 if visibility == public_visibility then
374 stream.write "+ "
375 else stream.write "~ " # protected_visibility
376
377 if self isa MMethod then
378 if is_new and name != "new" then
379 stream.write "new "
380 else if is_init and name != "init" then
381 stream.write "init "
382 end
383 end
384
385 stream.write name
386
387 if self isa MMethod then
388 var intro = intro
389 assert intro isa MMethodDef
390 var msignature = intro.msignature
391 if msignature != null then
392 stream.write msignature.to_s
393 end
394 end
395
396 var mdoc = intro.mdoc
397 if mdoc != null then
398 stream.write " # "
399 stream.write mdoc.content.first
400 end
401 stream.write line_separator
402 end
403 end