Merge: introduce nit_env.sh to setup the shell environement
[nit.git] / src / doc / doc_phases / doc_extract.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 # Extract data mentities of Model that will be documented.
16 #
17 # ExtractionPhase populates the DocModel that is the base for all other phases.
18 # No DocPages are created at this level.
19 #
20 # TODO build a model instead?
21 module doc_extract
22
23 import doc_base
24
25 redef class ToolContext
26
27 # Do not generate documentation for attributes.
28 var opt_no_attributes = new OptionBool("Ignore the attributes", "--no-attributes")
29
30 # Do not generate documentation for private properties.
31 var opt_private = new OptionBool("Also generate private API", "--private")
32
33 redef init do
34 super
35 option_context.add_option(opt_no_attributes, opt_private)
36 end
37
38 # Minimum visibility displayed.
39 #
40 # See `opt_private`.
41 var min_visibility: MVisibility is lazy do
42 if opt_private.value then return none_visibility
43 return protected_visibility
44 end
45 end
46
47 # ExtractionPhase populates the DocModel.
48 class ExtractionPhase
49 super DocPhase
50
51 private var new_model: Model is noinit
52
53 # Populates the given DocModel.
54 redef fun apply do
55 doc.populate(self)
56 end
57
58 # Should we exclude this `mpackage` from the documentation?
59 fun ignore_mentity(mentity: MEntity): Bool do
60 if mentity isa MModule then
61 return mentity.is_fictive or mentity.is_test_suite
62 else if mentity isa MClass then
63 return mentity.visibility < ctx.min_visibility
64 else if mentity isa MClassDef then
65 return ignore_mentity(mentity.mclass)
66 else if mentity isa MProperty then
67 return ignore_mentity(mentity.intro_mclassdef) or
68 mentity.visibility < ctx.min_visibility or
69 (ctx.opt_no_attributes.value and mentity isa MAttribute) or
70 mentity isa MInnerClass
71 else if mentity isa MPropDef then
72 return ignore_mentity(mentity.mclassdef) or
73 ignore_mentity(mentity.mproperty)
74 end
75 return false
76 end
77 end
78
79 # TODO Should I rebuild a new Model from filtered data?
80 redef class DocModel
81
82 # MPackages that will be documented.
83 var mpackages = new HashSet[MPackage]
84
85 # MGroups that will be documented.
86 var mgroups = new HashSet[MGroup]
87
88 # MModules that will be documented.
89 var mmodules = new HashSet[MModule]
90
91 # MClasses that will be documented.
92 var mclasses = new HashSet[MClass]
93
94 # MClassDefs that will be documented.
95 var mclassdefs = new HashSet[MClassDef]
96
97 # MProperties that will be documented.
98 var mproperties = new HashSet[MProperty]
99
100 # MPropdefs that will be documented.
101 var mpropdefs = new HashSet[MPropDef]
102
103 # Populate `self` from internal `model`.
104 fun populate(v: ExtractionPhase) do
105 populate_mpackages(v)
106 populate_mclasses(v)
107 populate_mproperties(v)
108 end
109
110 # Populates the `mpackages` set.
111 private fun populate_mpackages(v: ExtractionPhase) do
112 for mpackage in model.mpackages do
113 if v.ignore_mentity(mpackage) then continue
114 self.mpackages.add mpackage
115 for mgroup in mpackage.mgroups do
116 if v.ignore_mentity(mgroup) then continue
117 self.mgroups.add mgroup
118 for mmodule in mgroup.mmodules do
119 if v.ignore_mentity(mmodule) then continue
120 self.mmodules.add mmodule
121 end
122 end
123 end
124 end
125
126 # Populates the `mclasses` set.
127 private fun populate_mclasses(v: ExtractionPhase) do
128 for mclass in model.mclasses do
129 if v.ignore_mentity(mclass) then continue
130 self.mclasses.add mclass
131 for mclassdef in mclass.mclassdefs do
132 if v.ignore_mentity(mclassdef) then continue
133 self.mclassdefs.add mclassdef
134 end
135 end
136 end
137
138 # Populates the `mproperties` set.
139 private fun populate_mproperties(v: ExtractionPhase) do
140 for mproperty in model.mproperties do
141 if v.ignore_mentity(mproperty) then continue
142 self.mproperties.add mproperty
143 for mpropdef in mproperty.mpropdefs do
144 if v.ignore_mentity(mpropdef) then continue
145 self.mpropdefs.add mpropdef
146 end
147 end
148 end
149
150 # Lists all MEntities in the model.
151 #
152 # FIXME invalidate cache if `self` is modified.
153 var mentities: Collection[MEntity] is lazy do
154 var res = new HashSet[MEntity]
155 res.add_all mpackages
156 res.add_all mgroups
157 res.add_all mmodules
158 res.add_all mclasses
159 res.add_all mclassdefs
160 res.add_all mproperties
161 res.add_all mpropdefs
162 return res
163 end
164
165 # Searches MEntities that match `name`.
166 fun mentities_by_name(name: String): Array[MEntity] do
167 var res = new Array[MEntity]
168 for mentity in mentities do
169 if mentity.name != name then continue
170 res.add mentity
171 end
172 return res
173 end
174
175 # Looks up a MEntity by its `namespace`.
176 #
177 # Usefull when `mentities_by_name` by return conflicts.
178 #
179 # Path can be the shortest possible to disambiguise like `Class::property`.
180 # In case of larger conflicts, a more complex namespace can be given like
181 # `package::module::Class::prop`.
182 fun mentities_by_namespace(namespace: String): Array[MEntity] do
183 var res = new Array[MEntity]
184 for mentity in mentities do
185 mentity.mentities_by_namespace(namespace, res)
186 end
187 return res
188 end
189 end
190
191 redef class MEntity
192 # Looks up a MEntity by its `namespace` from `self`.
193 private fun mentities_by_namespace(namespace: String, res: Array[MEntity]) do end
194
195 private fun lookup_in(mentities: Collection[MEntity], namespace: String, res: Array[MEntity]) do
196 var parts = namespace.split_once_on("::")
197 var name = parts.shift
198 for mentity in mentities do
199 if mentity.name != name then continue
200 if parts.is_empty then
201 res.add mentity
202 else
203 mentity.mentities_by_namespace(parts.first, res)
204 end
205 end
206 end
207 end
208
209 redef class MPackage
210 redef fun mentities_by_namespace(namespace, res) do lookup_in(mgroups, namespace, res)
211 end
212
213 redef class MGroup
214 redef fun mentities_by_namespace(namespace, res) do lookup_in(mmodules, namespace, res)
215 end
216
217 redef class MModule
218 redef fun mentities_by_namespace(namespace, res) do lookup_in(mclassdefs, namespace, res)
219 end
220
221 redef class MClassDef
222 redef fun mentities_by_namespace(namespace, res) do lookup_in(mpropdefs, namespace, res)
223 end