vim_autocomplete: remove ModelView dependency
[nit.git] / src / doc / commands / commands_base.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 # Documentation commands
16 #
17 # A DocCommand returns data about a model, an entity or a piece of documentation.
18 #
19 # Each command assumes a different goal like getting the comment of an entity,
20 # getting a list of packages, getting an UML class diagram etc.
21 #
22 # Commands are used by documentation tools to build up documentation ressources
23 # like Nitweb, Nitx, Nitdoc or documentation cards within READMEs.
24 module commands_base
25
26 import model::model_index
27 import catalog
28
29 # Documentation command
30 #
31 # An abstract command that works on a Model.
32 #
33 # Since they are used by a wide variety of clients, initialization of DocCommands
34 # works in two steps.
35 #
36 # First, you pass the data you already have to the command at init:
37 # ~~~nitish
38 # var c1 = new CmdEntity(view, mentity_name = "Array")
39 # var c2 = new CmdEntity(view, mentity = my_entity)
40 # ~~~
41 #
42 # Then, you call `init_command` to initialize the missing field from the stub data:
43 # ~~~nitish
44 # var r1 = c1.init_command
45 # assert c1.mentity != null
46 # assert r1 isa CmdSuccess
47 #
48 # var r2 = c2.init_command
49 # assert c2.mentity_name != null
50 # assert r2 isa CmdSuccess
51 # ~~~
52 #
53 # See `init_command` for more details about the returned statuses.
54 abstract class DocCommand
55
56 # Model to retrieve data for
57 var model: Model
58
59 # ModelFilter to apply if any
60 var filter: nullable ModelFilter
61
62 # Initialize the command
63 #
64 # Returns a command message that gives the status of the command initialization.
65 #
66 # There is 3 categories of messages:
67 # * `CmdSuccess`: when the command that initialized correctly;
68 # * `CmdError`: when the command cannot be initialized;
69 # * `CmdWarning`: when something is wrong with the command but a result still can be produced.
70 #
71 # Warnings are generally used to distinguish empty list or mdoc from no data at all.
72 fun init_command: CmdMessage do return new CmdSuccess
73 end
74
75 # Command message
76 #
77 # A message returned by a command.
78 # Messages are used to inform the client of the command initialization status and results.
79 # Mostly, messages are used to check if a command is in an error state.
80 abstract class CmdMessage
81 end
82
83 # Command Success
84 #
85 # Returned when the command was performed without any error or warning.
86 class CmdSuccess
87 super CmdMessage
88 end
89
90 # Command Error
91 #
92 # Command errors are returned when the command cannot provide results because
93 # of a problem on the user-end (i.e. Bad command name, MEntity not found etc.).
94 abstract class CmdError
95 super CmdMessage
96 end
97
98 # Command Warning
99 #
100 # Command warnings are returned when the command cannot provide results because
101 # of a problem on the model-end (i.e. No documentation for a MEntity, no code etc.)
102 abstract class CmdWarning
103 super CmdMessage
104 end
105
106 # Basic commands
107
108 # A command about a MEntity
109 class CmdEntity
110 super DocCommand
111
112 # MEntity this command is about
113 #
114 # Alternatively you can provide a `mentity_name`.
115 var mentity: nullable MEntity = null is optional, writable
116
117 # Name of the mentity this command is about
118 #
119 # Alternatively you can directly provide the `mentity`.
120 var mentity_name: nullable String = null is optional, writable
121
122 # Initialize the command mentity.
123 #
124 # If not already set, tries to find the `mentity` from the `mentity_name`.
125 #
126 # This function try to match `mentity_name` both as a `full_name` and
127 # `name`.
128 #
129 # Return states:
130 # * `CmdSuccess`: everything was ok;
131 # * `ErrorMEntityNoName`: no `mentity` and no `mentity_name` provided;
132 # * `ErrorMEntityNotFound`: no mentity for `mentity_name`;
133 # * `ErrorMEntityConflict`: `mentity_name` was a non-qualified name that
134 # returns more than one MEntity.
135 fun init_mentity: CmdMessage do
136 if mentity != null then
137 if mentity_name == null then mentity_name = mentity.as(not null).full_name
138 return new CmdSuccess
139 end
140
141 var mentity_name = self.mentity_name
142 if mentity_name == null or mentity_name.is_empty then return new ErrorMEntityNoName
143
144 mentity = model.mentity_by_full_name(mentity_name)
145 if mentity == null then
146 var mentities = model.mentities_by_name(mentity_name)
147 if mentities.is_empty then
148 var suggest = model.find(mentity_name, 3)
149 return new ErrorMEntityNotFound(mentity_name, suggest)
150 else if mentities.length > 1 then
151 return new ErrorMEntityConflict(mentity_name, mentities)
152 end
153 mentity = mentities.first
154 end
155 return new CmdSuccess
156 end
157
158 # See `init_mentity`.
159 redef fun init_command do return init_mentity
160 end
161
162 # No MEntity name provided
163 class ErrorMEntityNoName
164 super CmdError
165 redef fun to_s do return "No entity name provided"
166 end
167
168 # No MEntity matching `mentity_name`
169 class ErrorMEntityNotFound
170 super CmdError
171
172 # MEntity name provided
173 var mentity_name: String
174
175 # Suggestions matching the `mentity_name`.
176 var suggestions: Array[MEntity]
177
178 redef fun to_s do
179 var res = new Buffer
180 res.append "No entity for `{mentity_name}`.\n"
181 res.append "Did you mean: "
182 for mentity in suggestions do
183 res.append " `{mentity.full_name}`"
184 if mentity != suggestions.last then res.append ","
185 end
186 return res.write_to_string
187 end
188 end
189
190 # Multiple MEntities matching `mentity_name`
191 class ErrorMEntityConflict
192 super CmdError
193
194 # MEntity name provided
195 var mentity_name: String
196
197 # Conflicts for `mentity_name`
198 var conflicts: Array[MEntity]
199
200 redef fun to_s do
201 var res = new Buffer
202 res.append "Multiple entities for `{mentity_name}`:"
203 for mentity in conflicts do
204 res.append " `{mentity.full_name}`"
205 if mentity != conflicts.last then res.append ","
206 end
207 return res.write_to_string
208 end
209 end
210
211 # A command that returns a list of results
212 abstract class CmdList
213 super DocCommand
214
215 # Type of result
216 type ITEM: Object
217
218 # Limit the items in the list
219 var limit: nullable Int = null is optional, writable
220
221 # Page to display
222 var page: nullable Int = null is optional, writable
223
224 # Total number of ret
225 var count: nullable Int = null is optional, writable
226
227 # Total number of pages
228 var max: nullable Int = null is optional, writable
229
230 # Comparator used to sort the list
231 var sorter: nullable Comparator = null is writable
232
233 # Items in the list
234 var results: nullable Array[ITEM] = null is writable
235
236 # `init_command` is used to factorize the sorting and pagination of results
237 #
238 # See `init_results` for the result list initialization.
239 redef fun init_command do
240 var res = super
241 if not res isa CmdSuccess then return res
242 res = init_results
243 if not res isa CmdSuccess then return res
244 sort
245 paginate
246 return res
247 end
248
249 # Initialize the `results` list
250 #
251 # This method must be redefined by CmdList subclasses.
252 fun init_results: CmdMessage do return new CmdSuccess
253
254 # Sort `mentities` with `sorter`
255 fun sort do
256 var results = self.results
257 if results == null then return
258 var sorter = self.sorter
259 if sorter == null then return
260 sorter.sort(results)
261 end
262
263 # Paginate the results
264 #
265 # This methods keeps only a subset of `results` depending on the current `page` and the
266 # number of elements to return set by `limit`.
267 #
268 # The `count` can be specified when `results` does not contain all the results.
269 # For example when the results are already limited from a DB statement.
270 fun paginate do
271 var results = self.results
272 if results == null then return
273
274 var limit = self.limit
275 if limit == null then return
276
277 var page = self.page
278 if page == null or page <= 0 then page = 1
279
280 var count = self.count
281 if count == null then count = results.length
282
283 var max = count / limit
284 if max == 0 then
285 page = 1
286 max = 1
287 else if page > max then
288 page = max
289 end
290
291 var lstart = (page - 1) * limit
292 var lend = limit
293 if lstart + lend > count then lend = count - lstart
294 self.results = results.subarray(lstart, lend)
295 self.max = max
296 self.limit = limit
297 self.page = page
298 self.count = count
299 end
300 end
301
302 # A list of mentities
303 abstract class CmdEntities
304 super CmdList
305
306 redef type ITEM: MEntity
307
308 redef var sorter = new MEntityNameSorter
309 end
310
311 # A command about a MEntity that returns a list of mentities
312 abstract class CmdEntityList
313 super CmdEntity
314 super CmdEntities
315
316 autoinit(model, filter, mentity, mentity_name, limit, page, count, max)
317
318 redef fun init_command do
319 var res = init_mentity
320 if not res isa CmdSuccess then return res
321 res = init_results
322 if not res isa CmdSuccess then return res
323 sort
324 paginate
325 return res
326 end
327 end