doc/commands: introduce CmdEntityLink command
[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 # Get the link to a MEntity API documentation
97 class CmdEntityLink
98 super CmdEntity
99
100 # The link text to display
101 var text: nullable String = null is optional, writable
102
103 # The link title to display when the link is hovered
104 var title: nullable String = null is optional, writable
105
106 redef fun init_command do
107 var res = super
108 if not res isa CmdSuccess then return res
109 var mentity = self.mentity.as(not null)
110
111 if text == null then
112 text = mentity.name
113 end
114 if title == null then
115 var mdoc = mentity.mdoc_or_fallback
116 if mdoc != null then
117 title = mdoc.synopsis
118 end
119 end
120 return res
121 end
122 end
123
124 # MEntity ancestors command
125 #
126 # Retrieve all the ancestors (direct and indirect) of a MEntity.
127 class CmdAncestors
128 super CmdEntityList
129
130 # Include direct parents in the ancestors list
131 #
132 # Default is `true`.
133 var parents = true is optional, writable
134
135 redef fun init_results do
136 if results != null then return new CmdSuccess
137
138 var res = super
139 if not res isa CmdSuccess then return res
140 var mentity = self.mentity.as(not null)
141
142 var ancestors = mentity.collect_ancestors(view).to_a
143 if parents then
144 results = ancestors
145 return res
146 end
147
148 var parents = mentity.collect_parents(view)
149 var mentities = new HashSet[MEntity]
150 for ancestor in ancestors do
151 if not parents.has(ancestor) then mentities.add ancestor
152 end
153 results = mentities.to_a
154 return res
155 end
156 end
157
158 # MEntity parents command
159 class CmdParents
160 super CmdEntityList
161
162 redef fun init_results do
163 if results != null then return new CmdSuccess
164
165 var res = super
166 if not res isa CmdSuccess then return res
167 var mentity = self.mentity.as(not null)
168
169 results = mentity.collect_parents(view).to_a
170 return res
171 end
172 end
173
174 # MEntity children command
175 class CmdChildren
176 super CmdEntityList
177
178 redef fun init_results do
179 if results != null then return new CmdSuccess
180
181 var res = super
182 if not res isa CmdSuccess then return res
183 var mentity = self.mentity.as(not null)
184
185 results = mentity.collect_children(view).to_a
186 return res
187 end
188 end
189
190 # MEntity descendants command
191 class CmdDescendants
192 super CmdEntityList
193
194 # Include direct children in the descendants list
195 #
196 # Default is `true`.
197 var children = true is optional, writable
198
199 redef fun init_results do
200 if results != null then return new CmdSuccess
201
202 var res = super
203 if not res isa CmdSuccess then return res
204 var mentity = self.mentity.as(not null)
205
206 var descendants = mentity.collect_descendants(view).to_a
207 if children then
208 results = descendants
209 return res
210 end
211
212 var children = mentity.collect_children(view)
213 var mentities = new HashSet[MEntity]
214 for descendant in descendants do
215 if not children.has(descendant) then mentities.add descendant
216 end
217 results = mentities.to_a
218 return res
219 end
220 end
221
222 # Linearization command
223 #
224 # Collects and linearizes definitions about an MEntity.
225 class CmdLinearization
226 super CmdEntityList
227
228 # Same states than `CmdEntity::init_mentity`
229 #
230 # Plus returns `WarningNoLinearization` if no linearization can be computed
231 # from the mentity.
232 redef fun init_results do
233 if results != null then return new CmdSuccess
234
235 var res = super
236 if not res isa CmdSuccess then return res
237 var mentity = self.mentity.as(not null)
238
239 sorter = null
240 results = mentity.collect_linearization(view.mainmodule)
241 if results == null then return new WarningNoLinearization(mentity)
242 return res
243 end
244 end
245
246 # No linearization computed for `mentity`.
247 class WarningNoLinearization
248 super CmdWarning
249
250 # MEntity provided
251 var mentity: MEntity
252
253 redef fun to_s do return "No linearization for `{mentity.full_name}`"
254 end
255
256 # A free text search command
257 class CmdSearch
258 super CmdEntities
259
260 # Free text command string
261 var query: nullable String = null is optional, writable
262
263 # Return states:
264 # * `CmdSuccess`: everything was ok;
265 # * `ErrorNoQuery`: no `query` provided.
266 redef fun init_results do
267 if results != null then return new CmdSuccess
268
269 var res = super
270 if not res isa CmdSuccess then return res
271
272 var query = self.query
273 if query == null then return new ErrorNoQuery
274 sorter = null
275 results = view.find(query)
276 return res
277 end
278 end
279
280 # No query string given
281 class ErrorNoQuery
282 super CmdError
283
284 redef fun to_s do return "Missing search string"
285 end
286
287 # MEntity feature list
288 #
289 # Mostly a list of mentities defined in `mentity`.
290 class CmdFeatures
291 super CmdEntityList
292
293 # Same as `CmdEntity::init_mentity`
294 #
295 # Plus `WarningNoFeatures` if no features are found for `mentity`.
296 redef fun init_results do
297 if results != null then return new CmdSuccess
298
299 var res = super
300 if not res isa CmdSuccess then return res
301 var mentity = self.mentity.as(not null)
302
303 var mentities = new Array[MEntity]
304 if mentity isa MPackage then
305 mentities.add_all mentity.collect_mgroups(view)
306 mentities.add_all mentity.collect_mmodules(view)
307 else if mentity isa MGroup then
308 mentities.add_all mentity.collect_mgroups(view)
309 mentities.add_all mentity.collect_mmodules(view)
310 else if mentity isa MModule then
311 mentities.add_all mentity.collect_local_mclassdefs(view)
312 else if mentity isa MClass then
313 mentities.add_all mentity.collect_intro_mproperties(view)
314 mentities.add_all mentity.collect_redef_mpropdefs(view)
315 else if mentity isa MClassDef then
316 mentities.add_all mentity.collect_intro_mpropdefs(view)
317 mentities.add_all mentity.collect_redef_mpropdefs(view)
318 else if mentity isa MProperty then
319 mentities.add_all mentity.collect_mpropdefs(view)
320 else
321 return new WarningNoFeatures(mentity)
322 end
323 self.results = mentities
324 return res
325 end
326 end
327
328 # TODO remove once the filters/sorters are merged
329 class CmdIntros
330 super CmdEntityList
331
332 redef fun init_results 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_intro_mclasses(view).to_a
341 self.results = mentities
342 else if mentity isa MClass then
343 var mentities = mentity.collect_intro_mproperties(view).to_a
344 self.results = mentities
345 else if mentity isa MClassDef then
346 var mentities = mentity.collect_intro_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 CmdRedefs
358 super CmdEntityList
359
360 redef fun init_command 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 MModule then
368 var mentities = mentity.collect_redef_mclasses(view).to_a
369 self.results = mentities
370 else if mentity isa MClass then
371 var mentities = mentity.collect_redef_mproperties(view).to_a
372 self.results = mentities
373 else if mentity isa MClassDef then
374 var mentities = mentity.collect_redef_mpropdefs(view).to_a
375 view.mainmodule.linearize_mpropdefs(mentities)
376 self.results = mentities
377 else
378 return new WarningNoFeatures(mentity)
379 end
380 return res
381 end
382 end
383
384 # TODO remove once the filters/sorters are merged
385 class CmdAllProps
386 super CmdEntityList
387
388 redef fun init_results do
389 if results != null then return new CmdSuccess
390
391 var res = super
392 if not res isa CmdSuccess then return res
393 var mentity = self.mentity.as(not null)
394
395 if mentity isa MClass then
396 results = mentity.collect_accessible_mproperties(view).to_a
397 else
398 return new WarningNoFeatures(mentity)
399 end
400 return res
401 end
402 end
403
404 # No feature list for `mentity`
405 class WarningNoFeatures
406 super CmdWarning
407
408 # MEntity provided
409 var mentity: MEntity
410
411 redef fun to_s do return "No features for `{mentity.full_name}`"
412 end
413
414 # Abstract command that returns source-code pieces
415 abstract class CmdCode
416 super DocCommand
417
418 autoinit(view, modelbuilder, format)
419
420 # ModelBuilder used to get AST nodes
421 var modelbuilder: ModelBuilder
422
423 # Rendering format
424 #
425 # Set the output format for this piece of code.
426 # Can be "raw", "html" or "ansi".
427 # Default is "raw".
428 #
429 # This format can be different than the format used in the command response.
430 # For example you can choose to render code as HTML inside a JSON object response.
431 # Another example is to render raw format to put into a HTML code tag.
432 var format = "raw" is optional, writable
433
434 # Render `node` depending on the selected `format`
435 fun render_code(node: nullable ANode): nullable Writable do
436 if node == null then return null
437 if format == "html" then
438 var hl = new HtmlightVisitor
439 hl.highlight_node node
440 return hl.html
441 else if format == "ansi" then
442 var hl = new AnsiHighlightVisitor
443 hl.highlight_node node
444 return hl.result
445 end
446 return node.location.text
447 end
448 end
449
450 # Cmd that finds the source code related to an `mentity`
451 class CmdEntityCode
452 super CmdEntity
453 super CmdCode
454
455 autoinit(view, modelbuilder, mentity, mentity_name, format)
456
457 # AST node to return
458 var node: nullable ANode = null is optional, writable
459
460 # Same as `CmdEntity::init_mentity`
461 #
462 # Plus `WarningNoCode` if no code/AST node is found for `mentity`.
463 redef fun init_command do
464 if node != null then return new CmdSuccess
465
466 var res = super
467 if not res isa CmdSuccess then return res
468 var mentity = self.mentity.as(not null)
469
470 if mentity isa MClass then mentity = mentity.intro
471 if mentity isa MProperty then mentity = mentity.intro
472 node = modelbuilder.mentity2node(mentity)
473 if node == null then return new WarningNoCode(mentity)
474 return res
475 end
476 end
477
478 # No code for `mentity`
479 class WarningNoCode
480 super CmdWarning
481
482 # MEntity provided
483 var mentity: MEntity
484
485 redef fun to_s do return "No code for `{mentity.full_name}`"
486 end
487
488 # Model commands
489
490 # A command that returns a list of all mentities in a model
491 class CmdModelEntities
492 super CmdEntities
493
494 # Kind of mentities to be returned.
495 #
496 # Value must be one of "packages", "groups", "modules", "classes", "classdefs",
497 # "properties", "propdefs" or "all".
498 #
499 # Default is "all".
500 var kind = "all" is optional, writable
501
502 # Default limit is `10`
503 redef var limit = 10
504
505 redef fun init_results do
506 if results != null then return new CmdSuccess
507
508 var res = super
509 if not res isa CmdSuccess then return res
510
511 var mentities = new Array[MEntity]
512 if kind == "packages" then
513 mentities = view.mpackages.to_a
514 else if kind == "groups" then
515 mentities = view.mgroups.to_a
516 else if kind == "modules" then
517 mentities = view.mmodules.to_a
518 else if kind == "classes" then
519 mentities = view.mclasses.to_a
520 else if kind == "classdefs" then
521 mentities = view.mclassdefs.to_a
522 else if kind == "properties" then
523 mentities = view.mproperties.to_a
524 else if kind == "propdefs" then
525 mentities = view.mpropdefs.to_a
526 else
527 mentities = view.mentities.to_a
528 end
529 results = mentities
530 return res
531 end
532 end
533
534 # A command that returns a random list of mentities from a model
535 class CmdRandomEntities
536 super CmdModelEntities
537
538 # Always return `CmdSuccess`
539 redef fun init_results do
540 if results != null then return new CmdSuccess
541 var res = super
542 if not res isa CmdSuccess then return res
543 randomize
544 return res
545 end
546
547 # Randomize mentities order
548 fun randomize do
549 var results = self.results
550 if results == null then return
551 results.shuffle
552 end
553 end