9533722516b65f1a1756282da33104ca94e3504d
[nit.git] / src / doc / commands / commands_main.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 module commands_main
16
17 import doc::commands::commands_model
18
19 # Cmd that finds the mains of an `mentity`
20 class CmdMains
21 super CmdEntityList
22
23 redef fun init_results do
24 if results != null then return new CmdSuccess
25
26 var res = super
27 if not res isa CmdSuccess then return res
28 var mentity = self.mentity.as(not null)
29 var mentities = new Array[MEntity]
30
31 if mentity isa MPackage then
32 for mmodule in mentity.collect_all_mmodules(filter) do
33 if mmodule_has_main(mmodule) then mentities.add mmodule
34 end
35 else if mentity isa MGroup then
36 for mmodule in mentity.collect_all_mmodules(filter) do
37 if mmodule_has_main(mmodule) then mentities.add mmodule
38 end
39 else if mentity isa MModule then
40 if mmodule_has_main(mentity) then mentities.add mentity
41 else
42 return new WarningNoMains(mentity)
43 end
44
45 if mentities.is_empty then return new WarningNoMains(mentity)
46
47 self.results = mentities
48 return res
49 end
50
51 # Does `mmodule` has a `main` method?
52 private fun mmodule_has_main(mmodule: MModule): Bool do
53 for mclassdef in mmodule.collect_redef_mclassdefs(filter) do
54 if mclassdef.name != "Sys" then continue
55 for mpropdef in mclassdef.collect_redef_mpropdefs(filter) do
56 if mpropdef.name == "main" then return true
57 end
58 end
59 return false
60 end
61 end
62
63 # No tests for `mentity`
64 class WarningNoMains
65 super CmdWarning
66
67 # MEntity provided
68 var mentity: MEntity
69
70 redef fun to_s do return "No main found for `{mentity.full_name}`"
71 end
72
73 # Cmd that finds the nitc command related to an `mentity`
74 class CmdMainCompile
75 super CmdEntity
76
77 var file: nullable SourceFile = null
78
79 var command: nullable String is lazy do
80 var path = test_path(file)
81 if path == null then return null
82 return "nitc {path}"
83 end
84
85 redef fun init_command do
86 var res = super
87 if not res isa CmdSuccess then return res
88 var mentity = self.mentity.as(not null)
89
90 if mentity isa MModule then
91 file = mmodule_main(mentity)
92 if file == null then return new WarningNoMain(mentity)
93 else
94 return new WarningNoMain(mentity)
95 end
96 return res
97 end
98
99 # Does `mmodule` has a `main` method?
100 private fun mmodule_main(mmodule: MModule): nullable SourceFile do
101 for mclassdef in mmodule.mclassdefs do
102 if mclassdef.name != "Sys" then continue
103 for mpropdef in mclassdef.mpropdefs do
104 if mpropdef.name == "main" then return mmodule.location.file
105 end
106 end
107 return null
108 end
109
110 # Return the sourcefile path
111 #
112 # This method exists for the only purpose to be redefined by nitunit tests
113 # to avoid path diffs.
114 private fun test_path(file: nullable SourceFile): nullable String do
115 if file == null then return null
116 var base_path = mentity.as(MModule).mpackage.as(not null).location.file.as(not null).filename
117 return file.filename.replace(base_path, "")
118 end
119 end
120
121 # No tests for `mentity`
122 class WarningNoMain
123 super CmdWarning
124
125 # MEntity provided
126 var mentity: MEntity
127
128 redef fun to_s do return "No main to compile for `{mentity.full_name}`"
129 end
130
131 # Cmd that finds the nitunit command related to an `mentity`
132 class CmdTesting
133 super CmdEntityList
134
135 var command: nullable String is lazy do
136 var results = self.results
137 if results == null then return null
138
139 var tpl = new Template
140 tpl.add "nitunit"
141 for result in results do
142 var path = test_path(result)
143 if path == null then continue
144 tpl.add " {path}"
145 end
146 return tpl.write_to_string
147 end
148
149 redef fun init_results do
150 if results != null then return new CmdSuccess
151
152 var res = super
153 if not res isa CmdSuccess then return res
154 var mentity = self.mentity.as(not null)
155
156 var mentities = new Array[MEntity]
157 if not mentity isa MPackage then return new WarningNoTest(mentity)
158
159 for mgroup in mentity.collect_all_mgroups(filter) do
160 if mgroup.is_test then
161 mentities.add mgroup
162 continue
163 end
164 for mmodule in mgroup.collect_mmodules(filter) do
165 if mmodule.is_test then mentities.add mmodule
166 end
167 end
168
169 if mentities.is_empty then return new WarningNoTest(mentity)
170
171 self.results = mentities
172 return res
173 end
174
175 # Return the mentity path
176 #
177 # This method exists for the only purpose to be redefined by nitunit tests
178 # to avoid path diffs.
179 private fun test_path(mentity: MEntity): nullable String do
180 var file = mentity.location.file
181 if file == null then return null
182 var base_path = self.mentity.as(not null).location.file.as(not null).filename
183 return file.filename.replace(base_path, "")
184 end
185 end
186
187 # No tests for `mentity`
188 class WarningNoTest
189 super CmdWarning
190
191 # MEntity provided
192 var mentity: MEntity
193
194 redef fun to_s do return "No nitunit files for `{mentity.full_name}`"
195 end
196
197 # Cmd that finds the man file related to an `mentity`
198 class CmdManFile
199 super CmdEntity
200
201 # Man file
202 var file: nullable String = null
203
204 redef fun init_command do
205 var res = super
206 if not res isa CmdSuccess then return res
207 var mentity = self.mentity.as(not null)
208
209 var mpackage = null
210 if mentity isa MPackage then
211 mpackage = mentity
212 else if mentity isa MGroup then
213 mpackage = mentity.mpackage
214 else if mentity isa MModule then
215 mpackage = mentity.mpackage
216 end
217
218 if mpackage == null then return new WarningNoManFile(mentity)
219
220 var source_file = mpackage.location.file
221 if source_file == null then return new WarningNoManFile(mentity)
222
223 var man_dir = source_file.filename / "man"
224 if not man_dir.file_exists then return new WarningNoManFile(mentity)
225
226 var man_file = null
227 for file in man_dir.files do
228 if not file.has_prefix(mentity.name) then continue
229 man_file = man_dir / file
230 end
231 if man_file == null then return new WarningNoManFile(mentity)
232
233 self.file = man_file
234
235 return res
236 end
237 end
238
239 # No man file for `mentity`
240 class WarningNoManFile
241 super CmdWarning
242
243 # MEntity provided
244 var mentity: MEntity
245
246 redef fun to_s do return "No man file for `{mentity.full_name}`"
247 end
248
249 class CmdManSynopsis
250 super CmdManFile
251
252 # Synopsis string extracted from man
253 var synopsis: nullable String
254
255 redef fun init_command do
256 var res = super
257 if not res isa CmdSuccess then return res
258 var mentity = self.mentity.as(not null)
259 var file = self.file.as(not null)
260
261 var lines = file.to_path.read_lines
262 var in_synopsis = false
263 for line in lines do
264 if in_synopsis and line.has_prefix(mentity.name) then
265 synopsis = line
266 break
267 end
268 if line != "# SYNOPSIS" then continue
269 in_synopsis = true
270 end
271
272 if synopsis == null then return new WarningNoManSynopsis(mentity)
273
274 return res
275 end
276 end
277
278 # No synopsis found in the man file for `mentity`
279 class WarningNoManSynopsis
280 super CmdWarning
281
282 # MEntity provided
283 var mentity: MEntity
284
285 redef fun to_s do return "No synopsis found in the man file for `{mentity.full_name}`"
286 end
287
288 class CmdManOptions
289 super CmdManFile
290
291 # Options description
292 var options: nullable ArrayMap[String, String]
293
294 redef fun init_command do
295 var res = super
296 if not res isa CmdSuccess then return res
297 var mentity = self.mentity.as(not null)
298 var file = self.file.as(not null)
299
300 var options = new ArrayMap[String, String]
301
302 var lines = file.to_path.read_lines
303 var in_options = false
304 for i in [0 .. lines.length[ do
305 var line = lines[i]
306 if line == "# OPTIONS" then
307 in_options = true
308 else if in_options and line.has_prefix("### ") then
309 var opt = line.substring(4, line.length).trim.replace("`", "")
310 var desc = ""
311 if i < lines.length - 1 then
312 desc = lines[i + 1].trim
313 end
314 options[opt] = desc
315 else if line.has_prefix("# ") then
316 in_options = false
317 end
318 end
319
320 if options.is_empty then return new WarningNoManOptions(mentity)
321 self.options = options
322
323 return res
324 end
325 end
326
327 # No options description found in the man file for `mentity`
328 class WarningNoManOptions
329 super CmdWarning
330
331 # MEntity provided
332 var mentity: MEntity
333
334 redef fun to_s do return "No options description found in the man file for `{mentity.full_name}`"
335 end