nitpretty: fixes documentation warnings.
[nit.git] / src / nitpretty.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 # `nitpretty` is a tool able to pretty print Nit files.
16 #
17 # Usage:
18 #
19 # nitpretty source.nit
20 #
21 # Main options:
22 #
23 # * `-o res.nit` output result into `res.nit`
24 # * `--diff` show diff between `source` and `res`
25 # * `--meld` open diff with `meld`
26 # * `--check` check the format of multiple source files
27 # * `--check --meld` perform `--check` and open `meld` for each difference
28 #
29 # ## Specification
30 #
31 # The specification of the pretty printing is described here.
32 #
33 # * Default indentation level is one `'\t'` character and
34 # is increased by one for each indentation level.
35 # * Default line max-size is 80.
36 #
37 # ### Comments
38 #
39 # There is many categories of comments:
40 #
41 # `Licence comments` are attached to the top of the file
42 # no blank line before, one after.
43 #
44 # ~~~nitish
45 # # This is a licence comment
46 #
47 # # Documentation for module `foo`
48 # module foo
49 # ~~~
50 #
51 # `ADoc` are documentation comments attached to a `AModule`, `AClassdef`, `APropdef`.
52 #
53 # They are printed before the definition with a blank line before and no after
54 # at the same indentation level than the definition.
55 #
56 # ~~~nitish
57 # # Documentation for module `foo`
58 # module foo
59 #
60 # # Documentation for class `Bar`
61 # class Bar
62 # # Documentation for method `baz`
63 # fun baz do end
64 # end
65 # ~~~
66 #
67 # `Block comments` are comments composed of one or more line rattached to nothing.
68 # They are displayed with one blank line before and after at current indent level.
69 #
70 # ~~~nitish
71 # <blank>
72 # # block
73 # # comment
74 # <blank>
75 # ~~~
76 #
77 # `Attached comments` are comments attached to a production.
78 # They are printed as this.
79 #
80 # ~~~nitish
81 # fun foo do # attached comment
82 # end
83 # ~~~
84 #
85 # `nitpretty` automatically remove multiple blanks between comments:
86 #
87 # ~~~nitish
88 # # Licence
89 # # ...
90 # <blank>
91 # # Block comment
92 # ~~~
93 #
94 # ### Inlining
95 #
96 # Productions are automatically inlined when possible.
97 #
98 # Conditions:
99 #
100 # * the production must be syntactically inlinable
101 # * the inlined production length is less than `PrettyPrinterVisitor::max-size`
102 # * the production do not contains any comments
103 #
104 # ### Modules
105 #
106 # * There is a blank between the module declaration and its imports
107 # * There is no blank between imports and only one after
108 # * There is a blank between each extern block definition
109 # * There is a blank between each class definition
110 # * There is no blank line at the end of the module
111 #
112 # ~~~nitish
113 # # Documentation for module `foo`
114 # module foo
115 #
116 # import a
117 # # import b
118 # import c
119 #
120 # # Documentation for class `Bar`
121 # class Bar end
122 #
123 # class Baz end # not a `ADoc` comment
124 # ~~~
125 #
126 #
127 # ### Classes
128 #
129 # * There is no blank between the class definition and its super-classes declarations
130 # * There is no blank between two inlined property definition
131 # * There is a blank between each block definition
132 # * There no blank line at the end of the class definition
133 #
134 # ~~~nitish
135 # # Documentation for class `Bar`
136 # class Bar end
137 #
138 # class Baz
139 # super Bar
140 #
141 # fun a is abstract
142 # private fun b do end
143 #
144 # fun c do
145 # # ...
146 # end
147 # end
148 # ~~~
149 #
150 # Generic types have no space after or before brackets and are separated by a comma and a space:
151 #
152 # ~~~nitish
153 # class A[E: Type1, F: Type1] end
154 # ~~~
155 #
156 # ### Blocks
157 #
158 # * Inlined productions have no blank lines between them
159 # * Block productions have a blank before and after
160 #
161 # ~~~nitish
162 # var a = 10
163 # var b = 0
164 #
165 # if a > b then
166 # # is positive
167 # print "positive"
168 # end
169 #
170 # print "end"
171 # ~~~
172 #
173 # ### Calls and Binary Ops
174 #
175 # Arguments are always printed separated with a comma and a space:
176 #
177 # ~~~nitish
178 # foo(a, b, c)
179 # ~~~
180 #
181 # Binary ops are always printed wrapped with spaces:
182 #
183 # ~~~nitish
184 # var c = 1 + 2
185 # ~~~
186 #
187 # Calls and binary ops can be splitted to fit the `max-size` constraint.
188 # Breaking priority is given to arguments declaration after the comma.
189 #
190 # ~~~nitish
191 # return foo("aaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbb",
192 # "cccccccccccccccccccccccccc")
193 # ~~~
194 #
195 # Binary ops can also be broken to fit the `max-size` limit:
196 #
197 # ~~~nitish
198 # return "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbbbbb" +
199 # "cccccccccccccccccccccccccc"
200 # ~~~
201 module nitpretty
202
203 import pretty
204
205 redef class ToolContext
206 # The working directory used to store temp files.
207 var opt_dir = new OptionString("Working directory (default is '.nitpretty')", "--dir")
208
209 # Output pretty printed code with this filename.
210 var opt_output = new OptionString("Output name (default is pretty.nit)", "-o",
211 "--output")
212
213 # Show diff between source and pretty printed code.
214 var opt_diff = new OptionBool("Show diff between source and output", "--diff")
215
216 # Show diff between source and pretty printed code using meld.
217 var opt_meld = new OptionBool("Show diff between source and output using meld",
218 "--meld")
219
220 # Check formatting instead of pretty printing.
221 #
222 # This option create a tempory pretty printed file then check if
223 # the output of the diff command on the source file and the pretty
224 # printed one is empty.
225 var opt_check = new OptionBool("Check format of Nit source files", "--check")
226 end
227
228 # Return result from diff between `file1` and `file2`.
229 private fun diff(file1, file2: String): String do
230 var p = new IProcess("diff", "-u", file1, file2)
231 var res = p.read_all
232 p.wait
233 p.close
234 return res
235 end
236
237 # process options
238 var toolcontext = new ToolContext
239
240 toolcontext.option_context.
241 add_option(toolcontext.opt_dir, toolcontext.opt_output, toolcontext.opt_diff,
242 toolcontext.opt_meld, toolcontext.opt_check)
243
244 toolcontext.tooldescription = "Usage: nitpretty [OPTION]... <file.nit>\n" +
245 "Pretty print Nit code from Nit source files."
246
247 toolcontext.process_options args
248 var arguments = toolcontext.option_context.rest
249 # build model
250 var model = new Model
251 var mbuilder = new ModelBuilder(model, toolcontext)
252 var mmodules = mbuilder.parse(arguments)
253 mbuilder.run_phases
254
255 if mmodules.is_empty then
256 print "Error: no module to pretty print"
257 return
258 end
259
260 if not toolcontext.opt_check.value and mmodules.length > 1 then
261 print "Error: only --check option allow multiple modules"
262 return
263 end
264
265 var dir = toolcontext.opt_dir.value or else ".nitpretty"
266 if not dir.file_exists then dir.mkdir
267 var v = new PrettyPrinterVisitor
268
269 for mmodule in mmodules do
270 if not mbuilder.mmodule2nmodule.has_key(mmodule) then
271 print " Error: no source file for module {mmodule}"
272 return
273 end
274
275 var nmodule = mbuilder.mmodule2nmodule[mmodule]
276 var file = "{dir}/{mmodule.name}.nit"
277 var tpl = v.pretty_nmodule(nmodule)
278 tpl.write_to_file file
279
280 if toolcontext.opt_check.value then
281 var res = diff(nmodule.location.file.filename, file)
282
283 if not res.is_empty then
284 print "Wrong formating for module {nmodule.location.file.filename}"
285 toolcontext.info(res, 1)
286
287 if toolcontext.opt_meld.value then
288 sys.system "meld {nmodule.location.file.filename} {file}"
289 end
290 else
291 toolcontext.info("[OK] {nmodule.location.file.filename}", 1)
292 end
293 else
294 # write to file
295 var out = toolcontext.opt_output.value
296 if out != null then sys.system "cp {file} {out}"
297
298 # open in meld
299 if toolcontext.opt_meld.value then
300 sys.system "meld {arguments.first} {file}"
301 return
302 end
303
304 # show diff
305 if toolcontext.opt_diff.value then
306 var res = diff(arguments.first, file)
307 if not res.is_empty then print res
308 return
309 end
310
311 # show pretty
312 if not toolcontext.opt_quiet.value then tpl.write_to sys.stdout
313 end
314 end