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