vim autocomplete: rename `write_to_stream` to `write_doc` with more arguments
[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_utils
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 end
49 end
50
51 redef class MEntity
52 private fun field_separator: String do return "#====#"
53 private fun line_separator: String do return "#nnnn#"
54
55 private fun write_doc(mainmodule: MModule, stream: Writer)
56 do
57 # 1. Short name for autocompletion
58 stream.write complete_name
59 stream.write field_separator
60
61 # 2. Full signature
62 stream.write complete_name
63 write_signature_to_stream(stream)
64 stream.write field_separator
65
66 # 3. Doc synopsis
67 var mdoc = complete_mdoc
68 if mdoc != null then
69 stream.write mdoc.content.first
70 end
71
72 # 4. Full doc with extra
73 stream.write field_separator
74 stream.write "# "
75 stream.write full_name
76 write_signature_to_stream(stream)
77 if mdoc != null then
78 for i in 2.times do stream.write line_separator
79 stream.write mdoc.content.join(line_separator)
80 end
81
82 stream.write "\n"
83 end
84
85 private fun write_signature_to_stream(stream: Writer) do end
86
87 # Actual name used in completion
88 private fun complete_name: String do return name
89
90 # Doc to use in completion
91 private fun complete_mdoc: nullable MDoc do return mdoc
92 end
93
94 redef class MMethodDef
95 redef fun write_signature_to_stream(stream)
96 do
97 var msignature = msignature
98 if msignature != null then
99 stream.write msignature.to_s
100 end
101 end
102 end
103
104 redef class MAttributeDef
105 redef fun write_signature_to_stream(stream)
106 do
107 var static_mtype = static_mtype
108 if static_mtype != null then
109 stream.write stream.to_s
110 end
111 end
112 end
113
114 # Use `MClassDef` as anchor for its constructors only
115 redef class MClassDef
116 private var target_constructor: nullable MMethodDef = null
117
118 redef fun complete_name
119 do
120 var target_constructor = target_constructor
121 assert target_constructor != null
122
123 var params
124 var mparameters = mclass.mparameters
125 if not mparameters.is_empty then
126 params = "[{mparameters.join(", ")}]"
127 else
128 params = ""
129 end
130
131 if target_constructor.name != "init" and target_constructor.name != "new" then
132 return name + params + "." + target_constructor.name
133 end
134
135 return name + params
136 end
137
138 redef fun complete_mdoc
139 do
140 var target_constructor = target_constructor
141 assert target_constructor != null
142
143 if target_constructor.name != "init" and target_constructor.name != "new" then
144 return target_constructor.mdoc
145 end
146
147 return mdoc
148 end
149 end
150
151 private class AutocompletePhase
152 super Phase
153
154 redef fun process_mainmodule(mainmodule, given_mmodules)
155 do
156 if not toolcontext.opt_vim_autocomplete.value then return
157
158 var compile_dir = "NIT_VIM_DIR".environ
159 if compile_dir.is_empty then compile_dir = "HOME".environ / ".vim/nit"
160 compile_dir.mkdir
161
162 var modules_stream = new FileWriter.open(compile_dir / "modules.txt")
163 var classes_stream = new FileWriter.open(compile_dir / "classes.txt")
164 var constructors_stream = new FileWriter.open(compile_dir / "constructors.txt")
165 var types_stream = new FileWriter.open(compile_dir / "types.txt")
166 var properties_stream = new FileWriter.open(compile_dir / "properties.txt")
167
168 # Got all known modules
169 var model = mainmodule.model
170 for mmodule in model.mmodules do
171 mmodule.write_doc(mainmodule, modules_stream)
172 end
173
174 # TODO list other modules from the Nit lib
175
176 # Get all known classes
177 for mclass in model.mclasses do
178 if not mainmodule.is_visible(mclass.intro_mmodule, public_visibility) then continue
179 var mclass_intro = mclass.intro
180
181 # Can it be instantiated?
182 if mclass.kind != interface_kind and mclass.kind != abstract_kind then
183
184 for prop in mclass.all_mproperties(mainmodule, public_visibility) do
185 if prop isa MMethod and prop.is_init then
186 mclass_intro.target_constructor = prop.intro
187 mclass_intro.write_doc(mainmodule, constructors_stream)
188 end
189 end
190 mclass_intro.target_constructor = null
191 end
192
193 # Always add to types and classes
194 mclass.mclass_type.write_doc(mainmodule, classes_stream)
195 mclass.mclass_type.write_doc(mainmodule, types_stream)
196 end
197
198 # Get all known properties
199 for mproperty in model.mproperties do
200 var intro_mmodule = mproperty.intro_mclassdef.mmodule
201 if not mainmodule.is_visible(intro_mmodule, public_visibility) then continue
202
203 # Is it a virtual type?
204 if mproperty isa MVirtualTypeProp then
205 mproperty.intro.write_doc(mainmodule, types_stream)
206 continue
207 end
208
209 # Skip properties beginning with @ or _
210 var first_letter = mproperty.name.chars.first
211 if first_letter == '@' or first_letter == '_' then continue
212
213 mproperty.intro.write_doc(mainmodule, properties_stream)
214 end
215
216 # Close streams
217 for stream in [modules_stream, classes_stream, properties_stream,
218 types_stream, constructors_stream] do
219
220 stream.close
221 var error = stream.last_error
222 if error != null then
223 toolcontext.error(null, "Failed to write Vim autocomplete file: {error}")
224 end
225 end
226 end
227 end