f2686e613cf01cf0d9344a0db9e7b7d955e61457
[nit.git] / src / doc / doc_phases / doc_structure.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 # Composes the DocComposite tree of a DocPage and organizes its content.
16 module doc_structure
17
18 import doc_concerns
19 import modelize
20
21 # StructurePhase populates the DocPage content with section and article.
22 #
23 # This phase only applies structure.
24 # The content of the structure is choosen by the rendering phases.
25 class StructurePhase
26 super DocPhase
27
28 # Used to sort ConcernsTree by rank.
29 private var concerns_sorter = new MConcernRankSorter
30
31 # Used to sort ConcernsTree by name.
32 private var name_sorter = new MEntityNameSorter
33
34 # Populates the given DocModel.
35 redef fun apply do
36 for page in doc.pages.values do page.apply_structure(self, doc)
37 end
38 end
39
40 redef class DocPage
41
42 # Populates `self` with structure elements like DocComposite ones.
43 #
44 # See `StructurePhase`.
45 fun apply_structure(v: StructurePhase, doc: DocModel) do end
46 end
47
48 redef class OverviewPage
49 redef fun apply_structure(v, doc) do
50 var article = new HomeArticle("home.article", "Home")
51 root.add_child article
52 # Packages list
53 var mpackages = doc.model.mpackages.to_a
54 var sorter = new MConcernRankSorter
55 sorter.sort mpackages
56 var section = new DocSection("packages.section", "Packages")
57 for mpackage in mpackages do
58 section.add_child new DefinitionArticle("{mpackage.nitdoc_id}.definition", null, mpackage)
59 end
60 article.add_child section
61 end
62 end
63
64 redef class SearchPage
65 redef fun apply_structure(v, doc) do
66 var mmodules = doc.mmodules.to_a
67 v.name_sorter.sort(mmodules)
68 var mclasses = doc.mclasses.to_a
69 v.name_sorter.sort(mclasses)
70 var mprops = doc.mproperties.to_a
71 v.name_sorter.sort(mprops)
72 root.add_child new IndexArticle("index.article", null, mmodules, mclasses, mprops)
73 end
74 end
75
76 redef class MGroupPage
77 redef fun apply_structure(v, doc) do
78 var section = new MEntitySection("{mentity.nitdoc_name}.section", null, mentity)
79 root.add_child section
80 if mentity.is_root then
81 section.add_child new IntroArticle("{mentity.mpackage.nitdoc_id}.intro", null, mentity.mpackage)
82 else
83 section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", null, mentity)
84 end
85 var concerns = self.concerns
86 if concerns == null or concerns.is_empty then return
87 # FIXME avoid diff
88 mentity.mpackage.booster_rank = -1000
89 mentity.booster_rank = -1000
90 concerns.sort_with(v.concerns_sorter)
91 mentity.mpackage.booster_rank = 0
92 mentity.booster_rank = 0
93 section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", null, mentity, concerns)
94 for mentity in concerns do
95 var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", null, mentity)
96 if mentity isa MModule then
97 ssection.add_child new DefinitionArticle("{mentity.nitdoc_id}.definition", null, mentity)
98 end
99 section.add_child ssection
100 end
101 end
102 end
103
104 redef class MModulePage
105 redef fun apply_structure(v, doc) do
106 var section = new MEntitySection("{mentity.nitdoc_name}.section", null, mentity)
107 root.add_child section
108 section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", null, mentity)
109 var concerns = self.concerns
110 if concerns == null or concerns.is_empty then return
111 # FIXME avoid diff
112 mentity.mgroup.mpackage.booster_rank = -1000
113 mentity.mgroup.booster_rank = -1000
114 mentity.booster_rank = -1000
115 concerns.sort_with(v.concerns_sorter)
116 mentity.mgroup.mpackage.booster_rank = 0
117 mentity.mgroup.booster_rank = 0
118 mentity.booster_rank = 0
119 section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", null, mentity, concerns)
120 # reference list
121 for mentity in concerns do
122 var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", null, mentity)
123 if mentity isa MModule then
124 var mclasses = mclasses_for_mmodule(mentity).to_a
125 v.name_sorter.sort(mclasses)
126 for mclass in mclasses do
127 var article = new DefinitionListArticle(
128 "{mclass.intro.nitdoc_id}.definition-list", null, mclass)
129 var mclassdefs = mclassdefs_for(mclass).to_a
130 if not mclassdefs.has(mclass.intro) then
131 article.add_child(new DefinitionArticle(
132 "{mclass.intro.nitdoc_id}.definition", null, mclass.intro))
133 end
134 doc.mainmodule.linearize_mclassdefs(mclassdefs)
135 for mclassdef in mclassdefs do
136 article.add_child(new DefinitionArticle(
137 "{mclassdef.nitdoc_id}.definition", null, mclassdef))
138 end
139 ssection.add_child article
140 end
141 end
142 section.add_child ssection
143 end
144 end
145
146 # Filters `self.mclassses` by intro `mmodule`.
147 private fun mclasses_for_mmodule(mmodule: MModule): Set[MClass] do
148 var mclasses = new HashSet[MClass]
149 for mclass in self.mclasses do
150 if mclass.intro.mmodule == mmodule then
151 mclasses.add mclass
152 end
153 end
154 return mclasses
155 end
156
157 # Filters `self.mclassdefs` by `mclass`.
158 private fun mclassdefs_for(mclass: MClass): Set[MClassDef] do
159 var mclassdefs = new HashSet[MClassDef]
160 for mclassdef in self.mclassdefs do
161 if mclassdef.mclass == mclass then
162 mclassdefs.add mclassdef
163 end
164 end
165 return mclassdefs
166 end
167 end
168
169 redef class MClassPage
170 redef fun apply_structure(v, doc) do
171 var section = new MEntitySection("{mentity.nitdoc_name}.section", null, mentity)
172 root.add_child section
173 section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", null, mentity)
174 var concerns = self.concerns
175 if concerns == null or concerns.is_empty then return
176 # FIXME diff hack
177 mentity.intro_mmodule.mgroup.mpackage.booster_rank = -1000
178 mentity.intro_mmodule.mgroup.booster_rank = -1000
179 mentity.intro_mmodule.booster_rank = -1000
180 concerns.sort_with(v.concerns_sorter)
181 mentity.intro_mmodule.mgroup.mpackage.booster_rank = 0
182 mentity.intro_mmodule.mgroup.booster_rank = 0
183 mentity.intro_mmodule.booster_rank = 0
184 var constructors = new DocSection("{mentity.nitdoc_id}.constructors", "Constructors")
185 var minit = mentity.root_init
186 if minit != null then
187 constructors.add_child new DefinitionArticle("{minit.nitdoc_id}.definition", null, minit)
188 end
189 section.add_child constructors
190 section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", null, mentity, concerns)
191 for mentity in concerns do
192 var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", null, mentity)
193 if mentity isa MModule then
194 var mprops = mproperties_for(mentity)
195 var by_kind = new PropertiesByKind.with_elements(mprops)
196 for group in by_kind.groups do
197 v.name_sorter.sort(group)
198 for mprop in group do
199 for mpropdef in mpropdefs_for(mprop, mentity) do
200 if mpropdef isa MMethodDef and mpropdef.mproperty.is_init then
201 if mpropdef == minit then continue
202 constructors.add_child new DefinitionArticle(
203 "{mpropdef.nitdoc_id}.definition", null, mpropdef)
204 else
205 ssection.add_child new DefinitionArticle(
206 "{mpropdef.nitdoc_id}.definition", null, mpropdef)
207 end
208 end
209 end
210 end
211 end
212 section.add_child ssection
213 end
214 end
215
216 # Filters `self.mpropdefs` by `mmodule`.
217 #
218 # FIXME diff hack
219 private fun mproperties_for(mmodule: MModule): Set[MProperty] do
220 var mprops = new HashSet[MProperty]
221 for mpropdef in self.mpropdefs do
222 if mpropdef.mclassdef.mmodule == mmodule then
223 mprops.add mpropdef.mproperty
224 end
225 end
226 return mprops
227 end
228
229 # Filters `self.mpropdefs` by `mproperty`.
230 #
231 # FIXME diff hack
232 private fun mpropdefs_for(mproperty: MProperty, mmodule: MModule): Set[MPropDef] do
233 var mpropdefs = new HashSet[MPropDef]
234 for mpropdef in self.mpropdefs do
235 if mpropdef.mproperty == mproperty and
236 mpropdef.mclassdef.mmodule == mmodule then
237 mpropdefs.add mpropdef
238 end
239 end
240 return mpropdefs
241 end
242 end
243
244 redef class MPropertyPage
245 redef fun apply_structure(v, doc) do
246 var section = new MEntitySection("{mentity.nitdoc_name}.section", null, mentity)
247 root.add_child section
248 section.add_child new IntroArticle("{mentity.nitdoc_id}.intro", null, mentity)
249 var concerns = self.concerns
250 if concerns == null or concerns.is_empty then return
251 # FIXME diff hack
252 mentity.intro.mclassdef.mmodule.mgroup.mpackage.booster_rank = -1000
253 mentity.intro.mclassdef.mmodule.mgroup.booster_rank = -1000
254 mentity.intro.mclassdef.mmodule.booster_rank = -1000
255 concerns.sort_with(v.concerns_sorter)
256 mentity.intro.mclassdef.mmodule.mgroup.mpackage.booster_rank = 0
257 mentity.intro.mclassdef.mmodule.mgroup.booster_rank = 0
258 mentity.intro.mclassdef.mmodule.booster_rank = 0
259 section.add_child new ConcernsArticle("{mentity.nitdoc_id}.concerns", null, mentity, concerns)
260 for mentity in concerns do
261 var ssection = new ConcernSection("{mentity.nitdoc_id}.concern", null, mentity)
262 if mentity isa MModule then
263 # Add mproperties
264 var mpropdefs = mpropdefs_for(mentity).to_a
265 v.name_sorter.sort(mpropdefs)
266 for mpropdef in mpropdefs do
267 ssection.add_child new DefinitionArticle(
268 "{mpropdef.nitdoc_id}.definition", null, mpropdef)
269 end
270 end
271 section.add_child ssection
272 end
273 end
274
275 # Filters `self.mpropdefs` by `mmodule`.
276 private fun mpropdefs_for(mmodule: MModule): Set[MPropDef] do
277 var mpropdefs = new HashSet[MPropDef]
278 for mpropdef in self.mpropdefs do
279 if mpropdef.mclassdef.mmodule == mmodule then
280 mpropdefs.add mpropdef
281 end
282 end
283 return mpropdefs
284 end
285 end
286
287 # A group of sections that can be displayed together in a tab.
288 #
289 # Display the first child and hide less relevant data in other panels.
290 class TabbedGroup
291 super DocSection
292 end
293
294 # A group of sections that can be displayed together in a tab panel.
295 class PanelGroup
296 super DocSection
297 end
298
299 # A DocComposite element about a MEntity.
300 class MEntityComposite
301 super DocComposite
302
303 redef fun title do return mentity.nitdoc_name
304
305 # MEntity documented by this page element.
306 var mentity: MEntity
307 end
308
309 # A Section about a Concern.
310 #
311 # Those sections are used to build the page summary.
312 class ConcernSection
313 super MEntityComposite
314 super DocSection
315
316 redef fun is_toc_hidden do return is_hidden
317 end
318
319 # An article about a Mentity.
320 #
321 # Used to display textual content about a MEntity.
322 abstract class MEntityArticle
323 super MEntityComposite
324 super DocArticle
325 end
326
327 # An article that displays a list of mentities.
328 class MEntitiesListArticle
329 super DocArticle
330
331 # MEntities to display.
332 var mentities: Array[MEntity]
333
334 redef fun is_hidden do return mentities.is_empty
335 end
336
337
338 # A section about a Mentity.
339 #
340 # Used to regroup content about a MEntity.
341 class MEntitySection
342 super MEntityComposite
343 super DocSection
344 end
345
346 # An introduction article about a MEntity.
347 #
348 # Used at the top of a documentation page to introduce the documented MEntity.
349 class IntroArticle
350 super MEntityComposite
351 super DocArticle
352
353 redef var is_hidden = false
354 redef var is_toc_hidden = true
355 end
356
357 # An article that display a ConcernsTreee as a list.
358 class ConcernsArticle
359 super MEntityArticle
360
361 # Concerns to list in this article.
362 var concerns: ConcernsTree
363
364 redef fun is_hidden do return concerns.is_empty
365 end
366
367 # An article that displays a list of definition belonging to a MEntity.
368 class DefinitionListArticle
369 super TabbedGroup
370 super MEntityArticle
371 end
372
373 # An article that display the definition text of a MEntity.
374 class DefinitionArticle
375 super MEntityArticle
376
377 redef var is_hidden = false
378 end
379
380 # The main package article.
381 class HomeArticle
382 super DocArticle
383 end
384
385 # An article that display an index of mmodules, mclasses and mproperties.
386 class IndexArticle
387 super DocArticle
388
389 # List of mmodules to display.
390 var mmodules: Array[MModule]
391
392 # List of mclasses to display.
393 var mclasses: Array[MClass]
394
395 # List of mproperties to display.
396 var mprops: Array[MProperty]
397
398 redef fun is_hidden do
399 return mmodules.is_empty and mclasses.is_empty and mprops.is_empty
400 end
401 end
402
403 # Concerns ranking
404
405 # Sort MConcerns based on the module importation hierarchy ranking
406 # see also: `MConcern::concern_rank` and `MConcern::booster_rank`
407 #
408 # Comparison is made with the formula:
409 #
410 # ~~~nitish
411 # a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_ran
412 # ~~~
413 #
414 # If both `a` and `b` have the same ranking,
415 # ordering is based on lexicographic comparison of `a.name` and `b.name`
416 class MConcernRankSorter
417 super Comparator
418 redef type COMPARED: MConcern
419
420 redef fun compare(a, b) do
421 if a.concern_rank == b.concern_rank then
422 return a.name <=> b.name
423 end
424 return a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_rank
425 end
426 end
427
428 redef class MConcern
429
430 # Boost a MConcern rank
431 # see: `MConcernRankSorter`
432 # Use a positive booster to push down a result in the list
433 # A negative booster can be used to push up the result
434 var booster_rank: Int = 0 is writable
435
436 # Concern ranking used for ordering
437 # see: `MConcernRankSorter`
438 # Rank can be positive or negative
439 fun concern_rank: Int is abstract
440 end
441
442 redef class MPackage
443 redef var concern_rank is lazy do
444 var max = 0
445 for mgroup in mgroups do
446 var mmax = mgroup.concern_rank
447 if mmax > max then max = mmax
448 end
449 return max + 1
450 end
451 end
452
453 redef class MGroup
454 redef var concern_rank is lazy do
455 var max = 0
456 for mmodule in mmodules do
457 var mmax = mmodule.concern_rank
458 if mmax > max then max = mmax
459 end
460 return max + 1
461 end
462 end
463
464 redef class MModule
465 redef var concern_rank is lazy do
466 var max = 0
467 for p in in_importation.direct_greaters do
468 var pmax = p.concern_rank
469 if pmax > max then max = pmax
470 end
471 return max + 1
472 end
473 end