Merge: contracts: fix usage of contract with `--erasure`
[nit.git] / src / doc / commands / commands_model.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 # Doc commands about a Model or a MEntity
16 #
17 # This module defines several commands to retrieve data about a Model and MEntities.
18 module commands_model
19
20 import commands_base
21 import modelize
22
23 # Retrieve the MDoc related to a MEntity
24 class CmdComment
25 super CmdEntity
26
27 # Allow fallback
28 #
29 # If `true`, the command uses `mdoc_or_fallback`.
30 # Default is `true`.
31 var fallback = true is optional, writable
32
33 # Retrieve the full documentation
34 #
35 # If `true`, retrieves the full documentation.
36 # If `false`, retrieves only the synopsis.
37 # Default is `true`.
38 #
39 # Since the rendering the final string (md, html...) depends on the kind of
40 # client, the handling of this option is delegated to submodules.
41 var full_doc = true is optional, writable
42
43 # Format to render the comment
44 #
45 # Can be one of `raw`, `html` or `md`.
46 # Default is `raw`.
47 var format = "raw" is optional, writable
48
49 # MDoc to return
50 var mdoc: nullable MDoc = null is optional, writable
51
52 # Same states than `CmdEntity::init_mentity`
53 #
54 # Plus returns `WarningNoMDoc` if no MDoc was found for the MEntity.
55 redef fun init_command do
56 var res = super
57 if not res isa CmdSuccess then return res
58 var mentity = self.mentity.as(not null)
59
60 if mdoc == null then
61 mdoc = if fallback then mentity.mdoc_or_fallback else mentity.mdoc
62 end
63 if mdoc == null then return new WarningNoMDoc(mentity)
64 return res
65 end
66
67 # Render `mdoc` depending on `full_doc` and `format`
68 fun render_comment: nullable Writable do
69 var mdoc = self.mdoc
70 if mdoc == null then return null
71
72 if full_doc then return mdoc.documentation
73 return mdoc.synopsis
74 end
75 end
76
77 # No MDoc for `mentity`
78 class WarningNoMDoc
79 super CmdWarning
80
81 # MEntity provided
82 var mentity: MEntity
83
84 redef fun to_s do return "No documentation for `{mentity.full_name}`."
85 end
86
87 # Get the link to a MEntity API documentation
88 class CmdEntityLink
89 super CmdEntity
90
91 # The link text to display
92 var text: nullable String = null is optional, writable
93
94 # The link title to display when the link is hovered
95 var title: nullable String = null is optional, writable
96
97 redef fun init_command do
98 var res = super
99 if not res isa CmdSuccess then return res
100 var mentity = self.mentity.as(not null)
101
102 if text == null then
103 text = mentity.name
104 end
105 if title == null then
106 var mdoc = mentity.mdoc_or_fallback
107 if mdoc != null then
108 title = mdoc.synopsis
109 end
110 end
111 return res
112 end
113 end
114
115 # An abstract inheritance command
116 #
117 # For things like ancestors, parents, children and descendants.
118 abstract class CmdInheritance
119 super CmdEntityList
120
121 autoinit(model, mainmodule, filter, mentity, mentity_name, limit, page, count, max)
122
123 # Mainmodule for class linearization
124 var mainmodule: MModule
125 end
126
127 # MEntity ancestors command
128 #
129 # Retrieve all the ancestors (direct and indirect) of a MEntity.
130 class CmdAncestors
131 super CmdInheritance
132
133 # Include direct parents in the ancestors list
134 #
135 # Default is `true`.
136 var parents = true is optional, writable
137
138 redef fun init_results do
139 if results != null then return new CmdSuccess
140
141 var res = super
142 if not res isa CmdSuccess then return res
143 var mentity = self.mentity.as(not null)
144
145 var ancestors = mentity.collect_ancestors(mainmodule, filter).to_a
146 if parents then
147 results = ancestors
148 return res
149 end
150
151 var parents = mentity.collect_parents(mainmodule, filter)
152 var mentities = new HashSet[MEntity]
153 for ancestor in ancestors do
154 if not parents.has(ancestor) then mentities.add ancestor
155 end
156 results = mentities.to_a
157 return res
158 end
159 end
160
161 # MEntity parents command
162 class CmdParents
163 super CmdInheritance
164
165 redef fun init_results do
166 if results != null then return new CmdSuccess
167
168 var res = super
169 if not res isa CmdSuccess then return res
170 var mentity = self.mentity.as(not null)
171
172 results = mentity.collect_parents(mainmodule, filter).to_a
173 return res
174 end
175 end
176
177 # MEntity children command
178 class CmdChildren
179 super CmdInheritance
180
181 redef fun init_results do
182 if results != null then return new CmdSuccess
183
184 var res = super
185 if not res isa CmdSuccess then return res
186 var mentity = self.mentity.as(not null)
187
188 results = mentity.collect_children(mainmodule, filter).to_a
189 return res
190 end
191 end
192
193 # MEntity descendants command
194 class CmdDescendants
195 super CmdInheritance
196
197 # Include direct children in the descendants list
198 #
199 # Default is `true`.
200 var children = true is optional, writable
201
202 redef fun init_results do
203 if results != null then return new CmdSuccess
204
205 var res = super
206 if not res isa CmdSuccess then return res
207 var mentity = self.mentity.as(not null)
208
209 var descendants = mentity.collect_descendants(mainmodule, filter).to_a
210 if children then
211 results = descendants
212 return res
213 end
214
215 var children = mentity.collect_children(mainmodule, filter)
216 var mentities = new HashSet[MEntity]
217 for descendant in descendants do
218 if not children.has(descendant) then mentities.add descendant
219 end
220 results = mentities.to_a
221 return res
222 end
223 end
224
225 # Linearization command
226 #
227 # Collects and linearizes definitions about an MEntity.
228 class CmdLinearization
229 super CmdInheritance
230
231 # Same states than `CmdEntity::init_mentity`
232 #
233 # Plus returns `WarningNoLinearization` if no linearization can be computed
234 # from the mentity.
235 redef fun init_results do
236 if results != null then return new CmdSuccess
237
238 var res = super
239 if not res isa CmdSuccess then return res
240 var mentity = self.mentity.as(not null)
241
242 sorter = null
243 results = mentity.collect_linearization(mainmodule)
244 if results == null then return new WarningNoLinearization(mentity)
245 return res
246 end
247 end
248
249 # No linearization computed for `mentity`.
250 class WarningNoLinearization
251 super CmdWarning
252
253 # MEntity provided
254 var mentity: MEntity
255
256 redef fun to_s do return "No linearization for `{mentity.full_name}`"
257 end
258
259 # A free text search command
260 class CmdSearch
261 super CmdEntities
262
263 # Free text command string
264 var query: nullable String = null is optional, writable
265
266 # Return states:
267 # * `CmdSuccess`: everything was ok;
268 # * `ErrorNoQuery`: no `query` provided.
269 redef fun init_results do
270 if results != null then return new CmdSuccess
271
272 var res = super
273 if not res isa CmdSuccess then return res
274
275 var query = self.query
276 if query == null then return new ErrorNoQuery
277 sorter = null
278 results = model.find(query)
279 return res
280 end
281 end
282
283 # No query string given
284 class ErrorNoQuery
285 super CmdError
286
287 redef fun to_s do return "Missing search string"
288 end
289
290 # MEntity feature list
291 #
292 # Mostly a list of mentities defined in `mentity`.
293 class CmdFeatures
294 super CmdEntityList
295
296 # Same as `CmdEntity::init_mentity`
297 #
298 # Plus `WarningNoFeatures` if no features are found for `mentity`.
299 redef fun init_results do
300 if results != null then return new CmdSuccess
301
302 var res = super
303 if not res isa CmdSuccess then return res
304 var mentity = self.mentity.as(not null)
305
306 var mentities = new Array[MEntity]
307 if mentity isa MPackage then
308 mentities.add_all mentity.collect_mgroups(filter)
309 mentities.add_all mentity.collect_mmodules(filter)
310 else if mentity isa MGroup then
311 mentities.add_all mentity.collect_mgroups(filter)
312 mentities.add_all mentity.collect_mmodules(filter)
313 else if mentity isa MModule then
314 mentities.add_all mentity.collect_local_mclassdefs(filter)
315 else if mentity isa MClass then
316 mentities.add_all mentity.collect_intro_mproperties(filter)
317 mentities.add_all mentity.collect_redef_mpropdefs(filter)
318 else if mentity isa MClassDef then
319 mentities.add_all mentity.collect_intro_mpropdefs(filter)
320 mentities.add_all mentity.collect_redef_mpropdefs(filter)
321 else if mentity isa MProperty then
322 mentities.add_all mentity.collect_mpropdefs(filter)
323 else
324 return new WarningNoFeatures(mentity)
325 end
326 self.results = mentities
327 return res
328 end
329 end
330
331 # TODO remove once the filters/sorters are merged
332 class CmdIntros
333 super CmdInheritance
334
335 redef fun init_results do
336 if results != null then return new CmdSuccess
337
338 var res = super
339 if not res isa CmdSuccess then return res
340 var mentity = self.mentity.as(not null)
341
342 if mentity isa MModule then
343 var mentities = mentity.collect_intro_mclasses(filter).to_a
344 self.results = mentities
345 else if mentity isa MClass then
346 var mentities = mentity.collect_intro_mproperties(filter).to_a
347 self.results = mentities
348 else if mentity isa MClassDef then
349 var mentities = mentity.collect_intro_mpropdefs(filter).to_a
350 mainmodule.linearize_mpropdefs(mentities)
351 self.results = mentities
352 else
353 return new WarningNoFeatures(mentity)
354 end
355 return res
356 end
357 end
358
359 # TODO remove once the filters/sorters are merged
360 class CmdRedefs
361 super CmdInheritance
362
363 redef fun init_results do
364 if results != null then return new CmdSuccess
365
366 var res = super
367 if not res isa CmdSuccess then return res
368 var mentity = self.mentity.as(not null)
369
370 if mentity isa MModule then
371 var mentities = mentity.collect_redef_mclassdefs(filter).to_a
372 self.results = mentities
373 else if mentity isa MClass then
374 var mentities = mentity.collect_redef_mpropdefs(filter).to_a
375 self.results = mentities
376 else if mentity isa MClassDef then
377 var mentities = mentity.collect_redef_mpropdefs(filter).to_a
378 mainmodule.linearize_mpropdefs(mentities)
379 self.results = mentities
380 else
381 return new WarningNoFeatures(mentity)
382 end
383 return res
384 end
385 end
386
387 # TODO remove once the filters/sorters are merged
388 class CmdAllProps
389 super CmdInheritance
390
391 redef fun init_results do
392 if results != null then return new CmdSuccess
393
394 var res = super
395 if not res isa CmdSuccess then return res
396 var mentity = self.mentity.as(not null)
397
398 if mentity isa MClass then
399 results = mentity.collect_accessible_mproperties(mainmodule, filter).to_a
400 else
401 return new WarningNoFeatures(mentity)
402 end
403 return res
404 end
405 end
406
407 # No feature list for `mentity`
408 class WarningNoFeatures
409 super CmdWarning
410
411 # MEntity provided
412 var mentity: MEntity
413
414 redef fun to_s do return "No features for `{mentity.full_name}`"
415 end
416
417 # Abstract command that returns source-code pieces
418 abstract class CmdCode
419 super DocCommand
420
421 autoinit(model, filter, node, format)
422
423 # AST node to display code from
424 var node: nullable ANode = null is optional, writable
425
426 # Rendering format
427 #
428 # Set the output format for this piece of code.
429 # Can be "raw", "html" or "ansi".
430 # Default is "raw".
431 #
432 # This format can be different than the format used in the command response.
433 # For example you can choose to render code as HTML inside a JSON object response.
434 # Another example is to render raw format to put into a HTML code tag.
435 var format = "raw" is optional, writable
436
437 # Render `node` depending on the selected `format`
438 fun render_code(node: ANode): Writable do
439 return node.location.text
440 end
441 end
442
443 # Cmd that finds the source code related to an `mentity`
444 class CmdEntityCode
445 super CmdEntity
446 super CmdCode
447
448 autoinit(model, modelbuilder, filter, mentity, mentity_name, format)
449
450 # ModelBuilder used to get AST nodes from entities
451 var modelbuilder: ModelBuilder
452
453 # Same as `CmdEntity::init_mentity`
454 #
455 # Plus `WarningNoCode` if no code/AST node is found for `mentity`.
456 redef fun init_command do
457 if node != null then return new CmdSuccess
458
459 var res = super
460 if not res isa CmdSuccess then return res
461 var mentity = self.mentity.as(not null)
462
463 if mentity isa MClass then mentity = mentity.intro
464 if mentity isa MProperty then mentity = mentity.intro
465 node = modelbuilder.mentity2node(mentity)
466 if node == null then return new WarningNoCode(mentity)
467 return res
468 end
469 end
470
471 # No code for `mentity`
472 class WarningNoCode
473 super CmdWarning
474
475 # MEntity provided
476 var mentity: MEntity
477
478 redef fun to_s do return "No code for `{mentity.full_name}`"
479 end
480
481 # Model commands
482
483 # A command that returns a list of all mentities in a model
484 class CmdModelEntities
485 super CmdEntities
486
487 # Kind of mentities to be returned.
488 #
489 # Value must be one of "packages", "groups", "modules", "classes", "classdefs",
490 # "properties", "propdefs" or "all".
491 #
492 # Default is "all".
493 var kind = "all" is optional, writable
494
495 # Default limit is `10`
496 redef var limit = 10
497
498 redef fun init_results do
499 if results != null then return new CmdSuccess
500
501 var res = super
502 if not res isa CmdSuccess then return res
503
504 var mentities = new Array[MEntity]
505 if kind == "packages" then
506 mentities = model.collect_mpackages(filter).to_a
507 else if kind == "groups" then
508 mentities = model.collect_mgroups(filter).to_a
509 else if kind == "modules" then
510 mentities = model.collect_mmodules(filter).to_a
511 else if kind == "classes" then
512 mentities = model.collect_mclasses(filter).to_a
513 else if kind == "classdefs" then
514 mentities = model.collect_mclassdefs(filter).to_a
515 else if kind == "properties" then
516 mentities = model.collect_mproperties(filter).to_a
517 else if kind == "propdefs" then
518 mentities = model.collect_mpropdefs(filter).to_a
519 else
520 mentities = model.collect_mentities(filter).to_a
521 end
522 results = mentities
523 return res
524 end
525 end
526
527 # A command that returns a random list of mentities from a model
528 class CmdRandomEntities
529 super CmdModelEntities
530
531 # Always return `CmdSuccess`
532 redef fun init_results do
533 if results != null then return new CmdSuccess
534 var res = super
535 if not res isa CmdSuccess then return res
536 randomize
537 return res
538 end
539
540 # Randomize mentities order
541 fun randomize do
542 var results = self.results
543 if results == null then return
544 results.shuffle
545 end
546 end