1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # `nitpretty` is a tool able to pretty print Nit files.
19 # nitpretty source.nit
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
31 # The specification of the pretty printing is described here.
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.
39 # There is many categories of comments:
41 # `Licence comments` are attached to the top of the file
42 # no blank line before, one after.
45 # # This is a licence comment
47 # # Documentation for module `foo`
51 # `ADoc` are documentation comments attached to a `AModule`, `AClassdef`, `APropdef`.
53 # They are printed before the definition with a blank line before and no after
54 # at the same indentation level than the definition.
57 # # Documentation for module `foo`
60 # # Documentation for class `Bar`
62 # # Documentation for method `baz`
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.
77 # `Attached comments` are comments attached to a production.
78 # They are printed as this.
81 # fun foo do # attached comment
85 # `nitpretty` automatically remove multiple blanks between comments:
96 # Productions are automatically inlined when possible.
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
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
113 # # Documentation for module `foo`
120 # # Documentation for class `Bar`
123 # class Baz end # not a `ADoc` comment
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
135 # # Documentation for class `Bar`
142 # private fun b do end
150 # Generic types have no space after or before brackets and are separated by a comma and a space:
153 # class A[E: Type1, F: Type1] end
158 # * Inlined productions have no blank lines between them
159 # * Block productions have a blank before and after
173 # ### Calls and Binary Ops
175 # Arguments are always printed separated with a comma and a space:
181 # Binary ops are always printed wrapped with spaces:
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.
191 # return foo("aaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbb",
192 # "cccccccccccccccccccccccccc")
195 # Binary ops can also be broken to fit the `max-size` limit:
198 # return "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbbbbb" +
199 # "cccccccccccccccccccccccccc"
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")
209 # Output pretty printed code with this filename.
210 var opt_output
= new OptionString("Output name (default is pretty.nit)", "-o",
213 # Show diff between source and pretty printed code.
214 var opt_diff
= new OptionBool("Show diff between source and output", "--diff")
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",
220 # Check formatting instead of pretty printing.
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")
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
)
238 var toolcontext
= new ToolContext
240 toolcontext
.option_context
.
241 add_option
(toolcontext
.opt_dir
, toolcontext
.opt_output
, toolcontext
.opt_diff
,
242 toolcontext
.opt_meld
, toolcontext
.opt_check
)
244 toolcontext
.tooldescription
= "Usage: nitpretty [OPTION]... <file.nit>\n" +
245 "Pretty print Nit code from Nit source files."
247 toolcontext
.process_options args
248 var arguments
= toolcontext
.option_context
.rest
250 var model
= new Model
251 var mbuilder
= new ModelBuilder(model
, toolcontext
)
252 var mmodules
= mbuilder
.parse
(arguments
)
255 if mmodules
.is_empty
then
256 print
"Error: no module to pretty print"
260 if not toolcontext
.opt_check
.value
and mmodules
.length
> 1 then
261 print
"Error: only --check option allow multiple modules"
265 var dir
= toolcontext
.opt_dir
.value
or else ".nitpretty"
266 if not dir
.file_exists
then dir
.mkdir
267 var v
= new PrettyPrinterVisitor
269 for mmodule
in mmodules
do
270 if not mbuilder
.mmodule2nmodule
.has_key
(mmodule
) then
271 print
" Error: no source file for module {mmodule}"
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
280 if toolcontext
.opt_check
.value
then
281 var res
= diff
(nmodule
.location
.file
.filename
, file
)
283 if not res
.is_empty
then
284 print
"Wrong formating for module {nmodule.location.file.filename}"
285 toolcontext
.info
(res
, 1)
287 if toolcontext
.opt_meld
.value
then
288 sys
.system
"meld {nmodule.location.file.filename} {file}"
291 toolcontext
.info
("[OK] {nmodule.location.file.filename}", 1)
295 var out
= toolcontext
.opt_output
.value
296 if out
!= null then sys
.system
"cp {file} {out}"
299 if toolcontext
.opt_meld
.value
then
300 sys
.system
"meld {arguments.first} {file}"
305 if toolcontext
.opt_diff
.value
then
306 var res
= diff
(arguments
.first
, file
)
307 if not res
.is_empty
then print res
312 if not toolcontext
.opt_quiet
.value
then tpl
.write_to sys
.stdout