nitdoc: move `DocComposite::id` to `doc_base`
[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("article:home")
51 root.add_child article
52 # Projects list
53 var mprojects = doc.model.mprojects.to_a
54 var sorter = new MConcernRankSorter
55 sorter.sort mprojects
56 var section = new ProjectsSection("section:projects")
57 for mproject in mprojects do
58 section.add_child new DefinitionArticle("article:{mproject.nitdoc_id}.definition", mproject)
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("article:index", 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("section:{mentity.nitdoc_name}", mentity)
79 root.add_child section
80 if mentity.is_root then
81 section.add_child new IntroArticle("article:{mentity.mproject.nitdoc_id}.intro", mentity.mproject)
82 else
83 section.add_child new IntroArticle("article:{mentity.nitdoc_id}.intro", mentity)
84 end
85 var concerns = self.concerns
86 if concerns == null or concerns.is_empty then return
87 # FIXME avoid diff
88 mentity.mproject.booster_rank = -1000
89 mentity.booster_rank = -1000
90 concerns.sort_with(v.concerns_sorter)
91 mentity.mproject.booster_rank = 0
92 mentity.booster_rank = 0
93 section.add_child new ConcernsArticle("article:{mentity.nitdoc_id}.concerns", mentity, concerns)
94 for mentity in concerns do
95 var ssection = new ConcernSection("concern:{mentity.nitdoc_id}", mentity)
96 if mentity isa MModule then
97 ssection.add_child new DefinitionArticle("article:{mentity.nitdoc_id}.definition", 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("section:{mentity.nitdoc_name}", mentity)
107 root.add_child section
108 section.add_child new IntroArticle("article:{mentity.nitdoc_id}.intro", mentity)
109 var concerns = self.concerns
110 if concerns == null or concerns.is_empty then return
111 # FIXME avoid diff
112 mentity.mgroup.mproject.booster_rank = -1000
113 mentity.mgroup.booster_rank = -1000
114 mentity.booster_rank = -1000
115 concerns.sort_with(v.concerns_sorter)
116 mentity.mgroup.mproject.booster_rank = 0
117 mentity.mgroup.booster_rank = 0
118 mentity.booster_rank = 0
119 section.add_child new ConcernsArticle("article:{mentity.nitdoc_id}.concerns", mentity, concerns)
120 # reference list
121 for mentity in concerns do
122 var ssection = new ConcernSection("concern:{mentity.nitdoc_id}", 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 "article:{mclass.intro.nitdoc_id}.definition-list", mclass)
129 var mclassdefs = mclassdefs_for(mclass).to_a
130 if not mclassdefs.has(mclass.intro) then
131 article.add_child(new DefinitionArticle(
132 "article:{mclass.intro.nitdoc_id}.definition", mclass.intro))
133 end
134 doc.mainmodule.linearize_mclassdefs(mclassdefs)
135 for mclassdef in mclassdefs do
136 article.add_child(new DefinitionArticle(
137 "article:{mclassdef.nitdoc_id}.definition", 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("section:{mentity.nitdoc_name}", mentity)
172 root.add_child section
173 section.add_child new IntroArticle("article:{mentity.nitdoc_id}.intro", 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.mproject.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.mproject.booster_rank = 0
182 mentity.intro_mmodule.mgroup.booster_rank = 0
183 mentity.intro_mmodule.booster_rank = 0
184 var constructors = new ConstructorsSection(
185 "article:{mentity.nitdoc_id}.constructors", mentity)
186 var minit = mentity.root_init
187 if minit != null then
188 constructors.add_child new DefinitionArticle("article:{minit.nitdoc_id}.definition", minit)
189 end
190 section.add_child constructors
191 section.add_child new ConcernsArticle("article:{mentity.nitdoc_id}.concerns", mentity, concerns)
192 for mentity in concerns do
193 var ssection = new ConcernSection("concern:{mentity.nitdoc_id}", mentity)
194 if mentity isa MModule then
195 var mprops = mproperties_for(mentity)
196 var by_kind = new PropertiesByKind.with_elements(mprops)
197 for group in by_kind.groups do
198 v.name_sorter.sort(group)
199 for mprop in group do
200 for mpropdef in mpropdefs_for(mprop, mentity) do
201 if mpropdef isa MMethodDef and mpropdef.mproperty.is_init then
202 if mpropdef == minit then continue
203 constructors.add_child new DefinitionArticle(
204 "article:{mpropdef.nitdoc_id}.definition", mpropdef)
205 else
206 ssection.add_child new DefinitionArticle(
207 "article:{mpropdef.nitdoc_id}.definition", mpropdef)
208 end
209 end
210 end
211 end
212 end
213 section.add_child ssection
214 end
215 end
216
217 # Filters `self.mpropdefs` by `mmodule`.
218 #
219 # FIXME diff hack
220 private fun mproperties_for(mmodule: MModule): Set[MProperty] do
221 var mprops = new HashSet[MProperty]
222 for mpropdef in self.mpropdefs do
223 if mpropdef.mclassdef.mmodule == mmodule then
224 mprops.add mpropdef.mproperty
225 end
226 end
227 return mprops
228 end
229
230 # Filters `self.mpropdefs` by `mproperty`.
231 #
232 # FIXME diff hack
233 private fun mpropdefs_for(mproperty: MProperty, mmodule: MModule): Set[MPropDef] do
234 var mpropdefs = new HashSet[MPropDef]
235 for mpropdef in self.mpropdefs do
236 if mpropdef.mproperty == mproperty and
237 mpropdef.mclassdef.mmodule == mmodule then
238 mpropdefs.add mpropdef
239 end
240 end
241 return mpropdefs
242 end
243 end
244
245 redef class MPropertyPage
246 redef fun apply_structure(v, doc) do
247 var section = new MEntitySection("section:{mentity.nitdoc_name}", mentity)
248 root.add_child section
249 section.add_child new IntroArticle("article:{mentity.nitdoc_id}.intro", mentity)
250 var concerns = self.concerns
251 if concerns == null or concerns.is_empty then return
252 # FIXME diff hack
253 mentity.intro.mclassdef.mmodule.mgroup.mproject.booster_rank = -1000
254 mentity.intro.mclassdef.mmodule.mgroup.booster_rank = -1000
255 mentity.intro.mclassdef.mmodule.booster_rank = -1000
256 concerns.sort_with(v.concerns_sorter)
257 mentity.intro.mclassdef.mmodule.mgroup.mproject.booster_rank = 0
258 mentity.intro.mclassdef.mmodule.mgroup.booster_rank = 0
259 mentity.intro.mclassdef.mmodule.booster_rank = 0
260 section.add_child new ConcernsArticle("article:{mentity.nitdoc_id}.concerns", mentity, concerns)
261 for mentity in concerns do
262 var ssection = new ConcernSection("concern:{mentity.nitdoc_id}", mentity)
263 if mentity isa MModule then
264 # Add mproperties
265 var mpropdefs = mpropdefs_for(mentity).to_a
266 v.name_sorter.sort(mpropdefs)
267 for mpropdef in mpropdefs do
268 ssection.add_child new DefinitionArticle(
269 "article:{mpropdef.nitdoc_id}.definition", mpropdef)
270 end
271 end
272 section.add_child ssection
273 end
274 end
275
276 # Filters `self.mpropdefs` by `mmodule`.
277 private fun mpropdefs_for(mmodule: MModule): Set[MPropDef] do
278 var mpropdefs = new HashSet[MPropDef]
279 for mpropdef in self.mpropdefs do
280 if mpropdef.mclassdef.mmodule == mmodule then
281 mpropdefs.add mpropdef
282 end
283 end
284 return mpropdefs
285 end
286 end
287
288 # A group of sections that can be displayed together in a tab.
289 #
290 # Display the first child and hide less relevant data in other panels.
291 class TabbedGroup
292 super DocSection
293 end
294
295 # A group of sections that can be displayed together in a tab panel.
296 class PanelGroup
297 super DocSection
298
299 # The title of this group.
300 var group_title: String
301 end
302
303 # A DocComposite element about a MEntity.
304 class MEntityComposite
305 super DocComposite
306
307 # MEntity documented by this page element.
308 var mentity: MEntity
309 end
310
311 # A list of constructors.
312 class ConstructorsSection
313 super MEntitySection
314 end
315
316 # A Section about a Concern.
317 #
318 # Those sections are used to build the page summary.
319 class ConcernSection
320 super MEntityComposite
321 super DocSection
322 end
323
324 # An article about a Mentity.
325 #
326 # Used to display textual content about a MEntity.
327 abstract class MEntityArticle
328 super MEntityComposite
329 super DocArticle
330 end
331
332 # A section about a Mentity.
333 #
334 # Used to regroup content about a MEntity.
335 class MEntitySection
336 super MEntityComposite
337 super DocSection
338 end
339
340 # An introduction article about a MEntity.
341 #
342 # Used at the top of a documentation page to introduce the documented MEntity.
343 class IntroArticle
344 super MEntityComposite
345 super DocArticle
346 end
347
348 # An article that display a ConcernsTreee as a list.
349 class ConcernsArticle
350 super MEntityArticle
351
352 # Concerns to list in this article.
353 var concerns: ConcernsTree
354 end
355
356 # An article that displaus a list of definition belonging to a MEntity.
357 class DefinitionListArticle
358 super TabbedGroup
359 super MEntityArticle
360 end
361
362 # An article that display the definition text of a MEntity.
363 class DefinitionArticle
364 super MEntityArticle
365 end
366
367 # The main project article.
368 class HomeArticle
369 super DocArticle
370 end
371
372 # The project list.
373 class ProjectsSection
374 super DocArticle
375 end
376
377 # An article that display an index of mmodules, mclasses and mproperties.
378 class IndexArticle
379 super DocArticle
380
381 # List of mmodules to display.
382 var mmodules: Array[MModule]
383
384 # List of mclasses to display.
385 var mclasses: Array[MClass]
386
387 # List of mproperties to display.
388 var mprops: Array[MProperty]
389 end
390
391 # Concerns ranking
392
393 # Sort MConcerns based on the module importation hierarchy ranking
394 # see also: `MConcern::concern_rank` and `MConcern::booster_rank`
395 #
396 # Comparison is made with the formula:
397 #
398 # ~~~nitish
399 # a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_ran
400 # ~~~
401 #
402 # If both `a` and `b` have the same ranking,
403 # ordering is based on lexicographic comparison of `a.name` and `b.name`
404 class MConcernRankSorter
405 super Comparator
406 redef type COMPARED: MConcern
407
408 redef fun compare(a, b) do
409 if a.concern_rank == b.concern_rank then
410 return a.name <=> b.name
411 end
412 return a.concern_rank + a.booster_rank <=> b.concern_rank + b.booster_rank
413 end
414 end
415
416 redef class MConcern
417
418 # Boost a MConcern rank
419 # see: `MConcernRankSorter`
420 # Use a positive booster to push down a result in the list
421 # A negative booster can be used to push up the result
422 var booster_rank: Int = 0 is writable
423
424 # Concern ranking used for ordering
425 # see: `MConcernRankSorter`
426 # Rank can be positive or negative
427 fun concern_rank: Int is abstract
428 end
429
430 redef class MProject
431 redef var concern_rank is lazy do
432 var max = 0
433 for mgroup in mgroups do
434 var mmax = mgroup.concern_rank
435 if mmax > max then max = mmax
436 end
437 return max + 1
438 end
439 end
440
441 redef class MGroup
442 redef var concern_rank is lazy do
443 var max = 0
444 for mmodule in mmodules do
445 var mmax = mmodule.concern_rank
446 if mmax > max then max = mmax
447 end
448 return max + 1
449 end
450 end
451
452 redef class MModule
453 redef var concern_rank is lazy do
454 var max = 0
455 for p in in_importation.direct_greaters do
456 var pmax = p.concern_rank
457 if pmax > max then max = pmax
458 end
459 return max + 1
460 end
461 end