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.
44 # # This is a licence comment
46 # # Documentation for module `foo`
49 # `ADoc` are documentation comments attached to a `AModule`, `AClassdef`, `APropdef`.
51 # They are printed before the definition with a blank line before and no after
52 # at the same indentation level than the definition.
54 # # Documentation for module `foo`
57 # # Documentation for class `Bar`
59 # # Documentation for method `baz`
63 # `Block comments` are comments composed of one or more line rattached to nothing.
64 # They are displayed with one blank line before and after at current indent level.
71 # `Attached comments` are comments attached to a production.
72 # They are printed as this.
74 # fun foo do # attached comment
77 # `nitpretty` automatically remove multiple blanks between comments:
86 # Productions are automatically inlined when possible.
90 # * the production must be syntactically inlinable
91 # * the inlined production length is less than `PrettyPrinterVisitor::max-size`
92 # * the production do not contains any comments
96 # * There is a blank between the module declaration and its imports
97 # * There is no blank between imports and only one after
98 # * There is a blank between each extern block definition
99 # * There is a blank between each class definition
100 # * There is no blank line at the end of the module
102 # # Documentation for module `foo`
109 # # Documentation for class `Bar`
112 # class Baz end # not a `ADoc` comment
117 # * There is no blank between the class definition and its super-classes declarations
118 # * There is no blank between two inlined property definition
119 # * There is a blank between each block definition
120 # * There no blank line at the end of the class definition
122 # # Documentation for class `Bar`
129 # private fun b do end
136 # Generic types have no espace after or before brackets and are separated by a comma and a space:
138 # class A[E: Type1, F: Type1] do end
142 # * Inlined productions have no blank lines between them
143 # * Block productions have a blank before and after
155 # ### Calls and Binary Ops
157 # Arguments are always printed separated with a comma and a space:
161 # Binary ops are always printed wrapped with spaces:
165 # Calls and binary ops can be splitted to fit the `max-size` constraint.
166 # Breaking priority is given to arguments declaration after the comma.
168 # return foo("aaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbb",
169 # "cccccccccccccccccccccccccc")
171 # Binary ops can also be broken to fit the `max-size` limit:
173 # return "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbbbbb" +
174 # "cccccccccccccccccccccccccc"
183 # The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
185 # The main method here is `visit` that performs the pretty printing of a `ANode`.
187 # Because some tokens like `TComment` are not stored in the AST,
188 # we visit the AST like traditionnal visitor and also maintain a
189 # pointer to the `current_token` (actually the next one to be printed).
191 # Visited productions are in charges to move the token pointer using methods such as:
193 # * `consume`: print `current_token` and move to the next one
194 # * `skip`: move to the next token without printing the current one
195 # * `skip_to`: move to a specified token skipping all the tokens before
196 # * `catch_up`: consume all the tokens between `current_token` and a specified token
197 # * `finish_line`: consume all the tokens between `current_token` and the end of line
198 class PrettyPrinterVisitor
200 fun pretty
(n
: ANode): Template do
205 current_token
= n
.first_token
207 else if n
isa Token then
210 while p
!= null and not p
isa Prod do
214 current_token
= p
.first_token
218 return tpl
.as(not null)
221 # Pretty print the whole `nmodule` with comments before and after.
222 fun pretty_nmodule
(nmodule
: AModule): Template do
224 nmodule
.parentize_tokens
225 current_token
= nmodule
.location
.file
.first_token
227 catch_up nmodule
.location
.file
.last_token
229 return tpl
.as(not null)
232 # Prepare `self` for a new visit.
242 # Visit `n` if not null.
243 fun visit
(n
: nullable ANode) do
244 if n
== null then return
245 n
.accept_pretty_printer
self
248 # Visit a list of `Anode`.
249 fun visit_list
(n
: nullable ANodes[ANode]) do
250 if n
== null then return
251 n
.accept_pretty_printer
self
254 # Is the node inlinable and can fit on the line.
255 fun can_inline
(n
: nullable ANode): Bool do
256 if n
== null then return true
257 if n
.must_be_inline
then return true
258 if n
.must_be_block
then return false
260 if n
.collect_length
+ current_length
> max_size
then return false
261 # check block is inlinable
262 return n
.is_inlinable
265 # Collect all `TComment` between `from` and `to`.
266 fun collect_comments
(from
: nullable ANode, to
: nullable ANode): Array[TComment] do
267 var res
= new Array[TComment]
268 if from
isa Prod then from
= from
.first_token
269 if to
isa Prod then to
= to
.first_token
270 if from
== null or to
== null then return res
273 if from
isa TComment then res
.add from
274 from
= from
.as(Token).next_token
280 # Token under cursor.
282 # This is the next token to visit.
283 var current_token
: nullable Token = null
285 # Skip the `current_token`.
286 fun skip
do current_token
= current_token
.next_token
288 # Skip `current_token` until the end of line.
289 fun skip_line
do current_token
= current_token
.last_real_token_in_line
291 # Skip `current_token` until `target` is reached.
292 fun skip_to
(target
: nullable Token) do
293 if target
== null then return
294 while current_token
!= target
do skip
297 # Visit `current_token`.
298 fun consume
(token
: String) do
299 assert current_token
.text
== token
303 # Is there token to visit between `current_token` and `target`?
304 fun need_catch_up
(target
: nullable Token): Bool do
305 if target
== null then return false
306 return current_token
!= target
309 # Visit all tokens between `current_token` and `target`.
310 fun catch_up
(target
: nullable ANode) do
311 if target
== null then return
312 if current_token
== null then return
314 if target
isa Token then
316 else if target
isa Prod then
317 token
= target
.first_token
.as(not null)
321 assert current_token
.location
<= token
.location
322 while current_token
!= token
do visit current_token
325 # Visit all tokens between `current_token` and the end of line.
327 if current_token
isa TComment then
332 while current_token
isa TEol do skip
335 # The template under construction.
336 private var tpl
: nullable Template = null
338 # Current indent level.
341 # Size of a tabulation in spaces.
347 # Length of the current line.
348 var current_length
= 0
350 # Length of the previous line.
351 var previous_length
= 0
353 # Is the last line a blank line?
354 fun last_line_is_blank
: Bool do return previous_length
== 0
356 # Add `t` to current template.
357 fun add
(t
: String) do
358 if t
.is_empty
then return
359 while wait_addn
> 0 do
364 current_length
+= t
.length
369 if current_length
== 0 and last_line_is_blank
then return
370 previous_length
= current_length
375 # End of line chars are stored until another char is added.
376 # This avoid empty files with only a '`\n`'.
377 private var wait_addn
= 0
379 # Add `'\t' * indent`.
380 fun addt
do add
"\t" * indent
385 fun visit_recv
(n_expr
: AExpr) do
386 if not n_expr
isa AImplicitSelfExpr then
393 # Base framework redefs
395 redef class ANodes[E
]
396 private fun accept_pretty_printer
(v
: PrettyPrinterVisitor) do
398 var e_can_inline
= v
.can_inline
(e
)
401 if not e_can_inline
then
417 # Start visit of `self` using a `PrettyPrinterVisitor`
418 private fun accept_pretty_printer
(v
: PrettyPrinterVisitor) is abstract
420 # Collect the length (in `Char`) of the node.
421 private fun collect_length
: Int is abstract
423 # Is `self` printable in one line?
424 private fun is_inlinable
: Bool do return true
426 # Force `self` to be rendered as a block.
427 private var force_block
= false
429 # Does `self` have to be rendered as a block?
430 private fun must_be_block
: Bool do return force_block
432 # Force `self` to be rendered as a line.
433 private var force_inline
= false
435 # Does `self` have be rendered as a line?
436 private fun must_be_inline
: Bool do
437 if parent
!= null and parent
.must_be_inline
then return true
441 # Does `self` was written in one line before transformation?
442 private fun was_inline
: Bool is abstract
446 redef fun accept_pretty_printer
(v
) do
448 v
.current_token
= next_token
451 redef fun collect_length
do return text
.length
452 redef fun is_inlinable
do return true
453 redef fun was_inline
do return true
457 redef fun accept_pretty_printer
(v
) do v
.visit first_token
459 # The token where the production really start (skipping ADoc).
460 private fun start_token
: nullable Token do return first_token
462 # Collect all `TComment` contained in the production
463 # (between `start_token` and `end_token`).
464 private fun collect_comments
: Array[TComment] do
465 var res
= new Array[TComment]
466 if start_token
== null or last_token
== null then return res
467 var token
= start_token
469 while token
!= last_token
do
470 if token
isa TComment then res
.add token
471 token
= token
.next_token
477 redef fun collect_length
do
479 if start_token
== null or last_token
== null then return res
480 var token
= start_token
482 while token
!= last_token
do
483 res
+= token
.text
.length
484 token
= token
.next_token
487 res
+= token
.text
.length
491 redef fun was_inline
do
492 return first_token
.location
.line_start
== last_token
.location
.line_end
499 redef fun accept_pretty_printer
(v
) do
510 if is_last_in_group
then v
.addn
524 if next_token
isa TComment and is_first_in_group
then v
.addn
528 var prev_token
= self.prev_token
529 if prev_token
isa TComment and prev_token
.is_inline
and is_last_in_group
then v
.addn
536 # Is `self` part of an `ADoc`?
537 private fun is_adoc
: Bool do return parent
isa ADoc and parent
.parent
!= null
539 # Is `self` part of a licence?
540 private fun is_licence
: Bool do
541 var prev_token
= self.prev_token
543 if prev_token
== null then
545 else if prev_token
isa TComment then
546 return prev_token
.is_licence
552 # Is `self` starts and ends its line?
553 private fun is_inline
: Bool do
554 return self == first_real_token_in_line
and self == last_real_token_in_line
557 # Is `self` an orphan line (blank before and after)?
558 private fun is_orphan
: Bool do
559 return prev_token
isa TEol and
560 (prev_token
.prev_token
isa TEol or prev_token
.prev_token
isa TComment) and
564 # Is `self` the first comment of a group of comment?
565 private fun is_first_in_group
: Bool do return not prev_token
isa TComment
567 # Is `self` the last comment of a group of comments?
568 private fun is_last_in_group
: Bool do return not next_token
isa TComment
572 redef fun accept_pretty_printer
(v
) do for comment
in n_comment
do v
.visit comment
573 redef fun is_inlinable
do return n_comment
.length
<= 1
578 redef class AAnnotations
579 redef fun accept_pretty_printer
(v
) do
583 if v
.can_inline
(self) then
585 for n_item
in n_items
do
587 if n_item
!= n_items
.last
then
592 else if n_items
.length
> 1 then
596 for n_item
in n_items
do
605 if not was_inline
and v
.current_token
isa TKwend then v
.skip
608 redef fun is_inlinable
do
609 if not super then return false
610 for annot
in n_items
do if not annot
.is_inlinable
then return false
615 redef class AAnnotation
616 redef fun accept_pretty_printer
(v
) do
618 if not n_args
.is_empty
then
619 if n_opar
== null then
630 redef class ATypeAtArg
631 redef fun accept_pretty_printer
(v
) do v
.visit n_type
634 redef class AExprAtArg
635 redef fun accept_pretty_printer
(v
) do v
.visit n_expr
641 redef fun accept_pretty_printer
(v
) do
642 v
.catch_up start_token
645 if not n_imports
.is_empty
then
648 for n_import
in n_imports
do
654 if not n_extern_code_blocks
.is_empty
then
657 for n_extern_block
in n_extern_code_blocks
do
658 v
.catch_up n_extern_block
659 v
.visit n_extern_block
661 if n_extern_block
!= n_extern_code_blocks
.last
then v
.addn
664 if not n_classdefs
.is_empty
then v
.addn
667 if not n_classdefs
.is_empty
then
670 for n_classdef
in n_classdefs
do
671 v
.catch_up n_classdef
673 if n_classdef
!= n_classdefs
.last
then v
.addn
681 redef fun start_token
do
682 if n_moduledecl
!= null then return n_moduledecl
.first_token
683 if not n_imports
.is_empty
then return n_imports
.first
.first_token
684 if not n_classdefs
.is_empty
then return n_classdefs
.first
.first_token
688 redef fun is_inlinable
do return false
691 redef class AModuledecl
692 redef fun accept_pretty_printer
(v
) do
698 if n_annotations
!= null then
699 var annot_inline
= v
.can_inline
(n_annotations
)
700 v
.visit n_annotations
702 if not annot_inline
then
703 if v
.current_token
isa TKwend then
717 redef class AModuleName
718 redef fun accept_pretty_printer
(v
) do
719 for path
in n_path
do
728 redef class ANoImport
729 redef fun accept_pretty_printer
(v
) do
738 redef class AStdImport
739 redef fun accept_pretty_printer
(v
) do
740 if not n_visibility
isa APublicVisibility then
755 redef class AClassdef
756 redef fun accept_pretty_printer
(v
) do
757 for n_propdef
in n_propdefs
do
760 if n_propdef
.n_doc
!= null or not v
.can_inline
(n_propdef
) then
761 if n_propdef
!= n_propdefs
.first
then v
.addn
763 if n_propdef
!= n_propdefs
.last
then v
.addn
771 redef class AStdClassdef
772 redef fun accept_pretty_printer
(v
) do
774 var can_inline
= v
.can_inline
(self)
776 if not n_visibility
isa APublicVisibility then
781 if n_kwredef
!= null then
790 if not n_formaldefs
.is_empty
then
792 v
.visit_list n_formaldefs
796 if n_extern_code_block
!= null then
798 v
.visit n_extern_code_block
804 if not n_superclasses
.is_empty
then
805 for n_superclass
in n_superclasses
do
815 for n_superclass
in n_superclasses
do
816 v
.catch_up n_superclass
823 if not n_superclasses
.is_empty
and not n_propdefs
.is_empty
then
838 redef fun is_inlinable
do
839 if not super then return false
840 if not n_propdefs
.is_empty
then return false
841 if n_superclasses
.length
> 1 then return false
842 if not collect_comments
.is_empty
then return false
846 redef fun start_token
do
847 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
848 if n_kwredef
!= null then return n_kwredef
849 return n_classkind
.first_token
853 redef class AAbstractClasskind
854 redef fun accept_pretty_printer
(v
) do
861 redef class AExternClasskind
862 redef fun accept_pretty_printer
(v
) do
869 redef class AFormaldef
870 redef fun accept_pretty_printer
(v
) do
873 if n_type
!= null then
882 redef fun accept_pretty_printer
(v
) do
883 if n_kwnullable
!= null then
890 if not n_types
.is_empty
then
898 redef class ASuperclass
899 redef fun accept_pretty_printer
(v
) do
909 redef fun accept_pretty_printer
(v
) do
913 if not n_visibility
isa APublicVisibility then
918 if n_kwredef
!= null then
924 redef fun start_token
do
925 if n_doc
== null then return super
926 return n_doc
.last_token
.next_token
930 redef class AAttrPropdef
931 redef fun accept_pretty_printer
(v
) do
937 if n_type
!= null then
943 if n_expr
!= null then
950 if n_annotations
!= null then v
.visit n_annotations
955 redef fun first_token
do
956 if n_doc
!= null then return n_doc
.first_token
957 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
958 if n_kwredef
!= null then return n_kwredef
962 redef fun is_inlinable
do return true
965 redef class ATypePropdef
966 redef fun accept_pretty_printer
(v
) do
978 redef fun is_inlinable
do return true
981 redef class AMethPropdef
982 redef fun accept_pretty_printer
(v
) do
983 # TODO: Handle extern annotations
985 var before
= v
.indent
986 var can_inline
= v
.can_inline
(self)
988 if n_kwinit
!= null then v
.visit n_kwinit
989 if n_kwmeth
!= null then v
.visit n_kwmeth
990 if n_kwnew
!= null then v
.visit n_kwnew
992 if not n_methid
== null then
999 if n_annotations
!= null then
1000 v
.visit n_annotations
1005 if n_extern_calls
!= null or n_extern_code_block
!= null then
1006 if n_annotations
!= null then v
.adds
1007 if n_extern_calls
!= null then v
.visit n_extern_calls
1008 if n_extern_code_block
!= null then v
.visit n_extern_code_block
1011 var n_block
= self.n_block
1013 if n_block
!= null then
1014 while not v
.current_token
isa TKwdo do v
.skip
1015 if n_annotations
!= null then
1016 if v
.can_inline
(n_annotations
) then
1027 if n_block
isa ABlockExpr then
1028 if n_block
.n_expr
.is_empty
then
1029 v
.visit n_block
.n_kwend
1031 v
.visit n_block
.n_expr
.first
1032 v
.current_token
= n_block
.n_kwend
1037 if v
.current_token
isa TKwend then v
.skip
1044 if n_block
isa ABlockExpr then
1045 n_block
.force_block
= true
1047 v
.catch_up n_block
.n_kwend
1056 if n_block
isa ABlockExpr then
1057 v
.visit n_block
.n_kwend
1066 assert v
.indent
== before
1069 # Can be inlined if:
1070 # * block is empty or can be inlined
1071 # * contains no comments
1072 redef fun is_inlinable
do
1073 if not super then return false
1074 if n_annotations
!= null and not n_annotations
.is_inlinable
then return false
1075 if n_block
!= null and not n_block
.is_inlinable
then return false
1076 if n_extern_calls
!= null and not n_extern_calls
.is_inlinable
then return false
1077 if n_extern_code_block
!= null and not n_extern_code_block
.is_inlinable
then return false
1078 if not collect_comments
.is_empty
then return false
1083 redef class AMainMethPropdef
1084 redef fun accept_pretty_printer
(v
) do
1090 redef class ASignature
1091 redef fun accept_pretty_printer
(v
) do
1092 if not n_params
.is_empty
then
1094 v
.visit_list n_params
1098 if n_type
!= null then
1107 redef fun accept_pretty_printer
(v
) do
1110 if n_type
!= null then
1116 if n_dotdotdot
!= null then v
.visit n_dotdotdot
1122 redef class AExternCalls
1123 redef fun accept_pretty_printer
(v
) do
1124 var can_inline
= v
.can_inline
(self)
1129 v
.visit_list n_extern_calls
1134 v
.visit_list n_extern_calls
1141 redef class AFullPropExternCall
1142 redef fun accept_pretty_printer
(v
) do
1149 redef class ALocalPropExternCall
1150 redef fun accept_pretty_printer
(v
) do v
.visit n_methid
1153 redef class AInitPropExternCall
1154 redef fun accept_pretty_printer
(v
) do v
.visit n_type
1157 redef class ACastAsExternCall
1158 redef fun accept_pretty_printer
(v
) do
1168 redef class AAsNullableExternCall
1169 redef fun accept_pretty_printer
(v
) do
1174 v
.visit n_kwnullable
1178 redef class AAsNotNullableExternCall
1179 redef fun accept_pretty_printer
(v
) do
1186 v
.visit n_kwnullable
1190 redef class AExternCodeBlock
1191 redef fun accept_pretty_printer
(v
) do
1192 if n_in_language
!= null then
1193 v
.visit n_in_language
1197 v
.visit n_extern_code_segment
1200 redef fun is_inlinable
do
1201 if not super then return false
1202 return n_extern_code_segment
.is_inlinable
1206 redef class AInLanguage
1207 redef fun accept_pretty_printer
(v
) do
1214 redef class TExternCodeSegment
1215 redef fun accept_pretty_printer
(v
) do
1216 var can_inline
= v
.can_inline
(self)
1221 var text
= text
.substring
(2, text
.length
- 4)
1222 var lines
= text
.r_trim
.split
("\n")
1224 if text
.is_empty
then
1229 if not lines
.first
.trim
.is_empty
then
1237 for line
in lines
do
1246 v
.current_token
= next_token
1250 redef fun is_inlinable
do
1251 if not super then return false
1252 return location
.line_start
== location
.line_end
1258 redef class ABlockExpr
1259 redef fun accept_pretty_printer
(v
) do
1260 var before
= v
.indent
1261 var can_inline
= v
.can_inline
(self)
1263 if can_inline
and not n_expr
.is_empty
then
1264 v
.visit n_expr
.first
1267 for nexpr
in n_expr
do
1268 var expr_inline
= v
.can_inline
(nexpr
)
1269 if not expr_inline
and nexpr
!= n_expr
.first
then v
.addn
1275 if not expr_inline
and nexpr
!= n_expr
.last
then v
.addn
1279 assert v
.indent
== before
1282 redef fun is_inlinable
do
1283 if not super then return false
1284 if not collect_comments
.is_empty
then return false
1286 if not n_expr
.is_empty
then
1287 if n_expr
.length
> 1 then return false
1288 if not n_expr
.first
.is_inlinable
then return false
1296 redef fun accept_pretty_printer
(v
) do
1297 var before
= v
.indent
1298 var can_inline
= v
.can_inline
(self)
1302 if v
.can_inline
(n_expr
) then
1311 # skip comments before `then` token
1312 while not v
.current_token
isa TKwthen do v
.skip
1314 var n_else
= self.n_else
1318 if n_then
!= null then v
.visit n_then
1321 n_else
.force_inline
= true
1326 else if n_then
== null then
1330 v
.skip_to last_token
.last_real_token_in_line
1336 if n_then
!= null then
1337 if n_then
isa ABlockExpr then
1338 n_then
.force_block
= true
1348 while not v
.current_token
isa TKwelse do
1349 v
.consume v
.current_token
.text
1356 if n_else
isa AIfExpr then
1357 n_else
.force_block
= true
1365 if n_else
isa ABlockExpr then
1366 n_else
.force_block
= true
1374 if last_token
isa TKwend then
1375 v
.catch_up last_token
1386 if last_token
.location
>= v
.current_token
.location
then v
.catch_up last_token
1390 if v
.current_token
isa TKwend then v
.skip
1394 assert v
.indent
== before
1397 redef fun is_inlinable
do
1398 if not super then return false
1399 if n_then
!= null and not n_then
.is_inlinable
then return false
1400 var n_else
= self.n_else
1401 if (n_else
isa ABlockExpr and not n_else
.n_expr
.is_empty
) or
1402 (not n_else
isa ABlockExpr and n_else
!= null) then
1405 if not collect_comments
.is_empty
then return false
1409 # Does this `if` statement contains a `else` part?
1410 private fun has_else
(v
: PrettyPrinterVisitor): Bool do
1412 if n_else
== null then return false
1413 var n_kwelse
= collect_kwelse
1414 if n_kwelse
== null then return false
1416 if n_else
isa ABlockExpr then
1417 var comments
: Array[TComment]
1419 if n_then
== null then
1420 comments
= v
.collect_comments
(n_expr
.last_token
, n_else
.last_token
)
1422 comments
= v
.collect_comments
(n_then
.last_token
, n_else
.last_token
)
1425 if not comments
.is_empty
then return true
1426 return not n_else
.n_expr
.is_empty
1432 # Lookup for `else` token in `self`.
1433 private fun collect_kwelse
: nullable TKwelse do
1434 var token
= first_token
1436 while token
!= last_token
do
1437 if token
isa TKwelse then return token
1438 token
= token
.next_token
1445 # Used to factorize work on loops.
1446 private class ALoopHelper
1449 fun loop_block
: nullable ANode is abstract
1450 fun loop_label
: nullable ANode is abstract
1452 fun visit_loop_block
(v
: PrettyPrinterVisitor) do
1453 var n_block
= loop_block
1458 if n_block
isa ABlockExpr then
1459 n_block
.force_block
= true
1461 v
.catch_up n_block
.n_kwend
1464 v
.visit n_block
.n_kwend
1474 if loop_label
!= null then
1480 fun visit_loop_inline
(v
: PrettyPrinterVisitor) do
1481 var n_block
= loop_block
1484 if n_block
isa ABlockExpr then
1485 if n_block
.n_expr
.is_empty
then
1486 v
.visit n_block
.n_kwend
1488 v
.visit n_block
.n_expr
.first
1489 v
.current_token
= n_block
.n_kwend
1494 if v
.current_token
isa TKwend then v
.skip
1497 if loop_label
!= null then
1503 redef fun is_inlinable
do
1504 var n_block
= loop_block
1505 if not super then return false
1506 if n_block
isa ABlockExpr and not n_block
.is_inlinable
then return false
1507 if not collect_comments
.is_empty
then return false
1512 redef class ALoopExpr
1515 redef fun loop_block
do return n_block
1516 redef fun loop_label
do return n_label
1518 redef fun accept_pretty_printer
(v
) do
1519 var can_inline
= v
.can_inline
(self)
1521 if can_inline
then visit_loop_inline v
else visit_loop_block v
1525 redef class AWhileExpr
1528 redef fun loop_block
do return n_block
1529 redef fun loop_label
do return n_label
1531 redef fun accept_pretty_printer
(v
) do
1532 var can_inline
= v
.can_inline
(self)
1538 if can_inline
then visit_loop_inline v
else visit_loop_block v
1545 redef fun loop_block
do return n_block
1546 redef fun loop_label
do return n_label
1548 redef fun accept_pretty_printer
(v
) do
1549 var can_inline
= v
.can_inline
(self)
1551 if can_inline
then visit_loop_inline v
else visit_loop_block v
1555 redef class AForExpr
1558 redef fun loop_block
do return n_block
1559 redef fun loop_label
do return n_label
1561 redef fun accept_pretty_printer
(v
) do
1562 var can_inline
= v
.can_inline
(self)
1566 for n_id
in n_ids
do
1568 if n_id
!= n_ids
.last
then v
.add
", "
1577 if can_inline
then visit_loop_inline v
else visit_loop_block v
1581 redef class ABreakExpr
1582 redef fun accept_pretty_printer
(v
) do
1585 if n_expr
!= null then
1590 if n_label
!= null then
1596 redef fun is_inlinable
do return true
1599 redef class AContinueExpr
1600 redef fun accept_pretty_printer
(v
) do
1601 v
.visit n_kwcontinue
1603 if n_expr
!= null then
1608 if n_label
!= null then
1614 redef fun is_inlinable
do return true
1619 redef class ASendExpr
1620 redef fun is_inlinable
do return true
1623 redef class ACallExpr
1624 redef fun accept_pretty_printer
(v
) do
1625 var can_inline
= v
.can_inline
(self)
1628 if not n_expr
isa AImplicitSelfExpr and not can_inline
then
1636 if not n_args
.n_exprs
.is_empty
then
1637 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1639 if v
.current_token
isa TOpar then v
.skip
1640 v
.visit n_args
.n_exprs
.first
1641 if v
.current_token
isa TCpar then v
.skip
1643 if v
.current_token
isa TOpar then
1649 v
.visit_list n_args
.n_exprs
1650 if v
.current_token
isa TCpar then v
.consume
")"
1655 # Is the call alone on its line?
1656 fun is_stmt
: Bool do return parent
isa ABlockExpr
1659 redef class ACallAssignExpr
1660 redef fun accept_pretty_printer
(v
) do
1664 if not n_args
.n_exprs
.is_empty
then
1666 v
.visit_list n_args
.n_exprs
1677 redef class ACallReassignExpr
1678 redef fun accept_pretty_printer
(v
) do
1682 if not n_args
.n_exprs
.is_empty
then
1684 v
.visit_list n_args
.n_exprs
1695 redef class ABraExpr
1696 redef fun accept_pretty_printer
(v
) do
1699 if not n_args
.n_exprs
.is_empty
then
1701 v
.visit_list n_args
.n_exprs
1707 redef class ABraAssignExpr
1708 redef fun accept_pretty_printer
(v
) do
1711 if not n_args
.n_exprs
.is_empty
then
1713 v
.visit_list n_args
.n_exprs
1724 redef class ABraReassignExpr
1725 redef fun accept_pretty_printer
(v
) do
1728 if not n_args
.n_exprs
.is_empty
then
1730 v
.visit_list n_args
.n_exprs
1741 redef class AAssignMethid
1742 redef fun accept_pretty_printer
(v
) do
1748 redef class ABraMethid
1749 redef fun accept_pretty_printer
(v
) do
1755 redef class ABraassignMethid
1756 redef fun accept_pretty_printer
(v
) do
1763 redef class AInitExpr
1764 redef fun accept_pretty_printer
(v
) do
1765 if not n_expr
isa AImplicitSelfExpr then
1772 if not n_args
.n_exprs
.is_empty
then
1774 v
.visit_list n_args
.n_exprs
1780 redef class ANewExpr
1781 redef fun accept_pretty_printer
(v
) do
1782 var can_inline
= v
.can_inline
(self)
1787 if n_id
!= null then
1790 if not can_inline
then
1799 if not n_args
.n_exprs
.is_empty
then
1801 v
.visit_list n_args
.n_exprs
1806 redef fun is_inlinable
do return true
1811 redef class AAttrExpr
1812 redef fun accept_pretty_printer
(v
) do
1817 redef fun is_inlinable
do return true
1820 redef class AAttrAssignExpr
1821 redef fun accept_pretty_printer
(v
) do
1831 redef class AAttrReassignExpr
1832 redef fun accept_pretty_printer
(v
) do
1844 redef class AVardeclExpr
1845 redef fun accept_pretty_printer
(v
) do
1850 if n_type
!= null then
1856 if n_expr
!= null then
1864 redef fun is_inlinable
do return true
1867 redef class AVarAssignExpr
1868 redef fun accept_pretty_printer
(v
) do
1877 redef class AAssertExpr
1878 redef fun accept_pretty_printer
(v
) do
1879 var can_inline
= v
.can_inline
(self)
1882 if n_id
!= null then
1890 var n_else
= self.n_else
1892 if n_else
!= null then
1902 if n_else
isa ABlockExpr then
1904 n_else
.force_block
= true
1908 v
.visit n_else
.n_kwend
1922 redef fun is_inlinable
do
1923 if not super then return false
1924 if n_else
!= null and not n_else
.is_inlinable
then return false
1929 redef class AReturnExpr
1930 redef fun accept_pretty_printer
(v
) do
1933 if n_expr
!= null then
1940 redef class ASuperExpr
1941 redef fun accept_pretty_printer
(v
) do
1944 if not n_args
.n_exprs
.is_empty
then
1945 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1947 if v
.current_token
isa TOpar then v
.skip
1948 v
.visit n_args
.n_exprs
.first
1949 if v
.current_token
isa TCpar then v
.skip
1951 if v
.current_token
isa TOpar then
1957 v
.visit_list n_args
.n_exprs
1958 if v
.current_token
isa TCpar then v
.consume
")"
1963 # Is the call alone on its line?
1964 fun is_stmt
: Bool do return self.first_token
.is_starting_line
1966 redef fun is_inlinable
do return true
1969 redef class AOnceExpr
1970 redef fun accept_pretty_printer
(v
) do
1976 redef fun is_inlinable
do return true
1979 redef class AAbortExpr
1980 redef fun accept_pretty_printer
(v
) do v
.visit n_kwabort
1981 redef fun is_inlinable
do return true
1984 redef class ANotExpr
1985 redef fun accept_pretty_printer
(v
) do
1992 redef class AAsCastExpr
1993 redef fun accept_pretty_printer
(v
) do
2003 redef class AAsNotnullExpr
2004 redef fun accept_pretty_printer
(v
) do
2018 # Used to factorize work on Or, And, Implies and Binop expressions.
2019 private class ABinOpHelper
2022 fun bin_expr1
: AExpr is abstract
2023 fun bin_expr2
: AExpr is abstract
2026 fun bin_op
: String is abstract
2028 redef fun accept_pretty_printer
(v
) do
2029 var can_inline
= v
.can_inline
(self)
2031 if not can_inline
then
2032 if (self isa ABinopExpr and bin_expr1
isa ABinopExpr) or
2033 (self isa AAndExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr)) or
2034 (self isa AOrExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr))
2036 bin_expr1
.force_block
= true
2056 redef class AAndExpr
2059 redef fun bin_expr1
do return n_expr
2060 redef fun bin_expr2
do return n_expr2
2061 redef fun bin_op
do return "and"
2067 redef fun bin_expr1
do return n_expr
2068 redef fun bin_expr2
do return n_expr2
2069 redef fun bin_op
do return "or"
2072 redef class AImpliesExpr
2075 redef fun bin_expr1
do return n_expr
2076 redef fun bin_expr2
do return n_expr2
2077 redef fun bin_op
do return "implies"
2080 redef class ABinopExpr
2083 redef fun bin_expr1
do return n_expr
2084 redef fun bin_expr2
do return n_expr2
2088 redef fun bin_op
do return "=="
2092 redef fun bin_op
do return ">="
2096 redef fun bin_op
do return ">>"
2100 redef fun bin_op
do return ">"
2104 redef fun bin_op
do return "<="
2108 redef fun bin_op
do return "<<"
2112 redef fun bin_op
do return "<"
2115 redef class AMinusExpr
2116 redef fun bin_op
do return "-"
2120 redef fun bin_op
do return "!="
2123 redef class APercentExpr
2124 redef fun bin_op
do return "%"
2127 redef class APlusExpr
2128 redef fun bin_op
do return "+"
2131 redef class ASlashExpr
2132 redef fun bin_op
do return "/"
2135 redef class AStarExpr
2136 redef fun bin_op
do return "*"
2139 redef class AStarstarExpr
2140 redef fun bin_op
do return "**"
2143 redef class AStarshipExpr
2144 redef fun bin_op
do return "<=>"
2147 redef class AIsaExpr
2148 redef fun accept_pretty_printer
(v
) do
2157 redef class AOrElseExpr
2158 redef fun accept_pretty_printer
(v
) do
2168 redef fun is_inlinable
do return true
2173 redef class AUminusExpr
2174 redef fun accept_pretty_printer
(v
) do
2180 redef class ANullExpr
2181 redef fun accept_pretty_printer
(v
) do v
.visit n_kwnull
2182 redef fun is_inlinable
do return true
2185 redef class AParExpr
2186 redef fun accept_pretty_printer
(v
) do
2193 redef class AArrayExpr
2194 redef fun accept_pretty_printer
(v
) do
2196 v
.visit_list n_exprs
.n_exprs
2201 redef class ACrangeExpr
2202 redef fun accept_pretty_printer
(v
) do
2211 redef class AOrangeExpr
2212 redef fun accept_pretty_printer
(v
) do
2223 redef class AStringFormExpr
2224 redef fun accept_pretty_printer
(v
) do
2225 var can_inline
= v
.can_inline
(self)
2230 var text
= n_string
.text
2233 while i
< text
.length
do
2236 if v
.current_length
>= v
.max_size
and i
<= text
.length
- 3 then
2248 v
.current_token
= n_string
.next_token
2253 redef class ASuperstringExpr
2254 redef fun accept_pretty_printer
(v
) do
2255 for n_expr
in n_exprs
do v
.visit n_expr
2258 redef fun must_be_inline
do
2259 if super then return true
2261 if not n_exprs
.is_empty
then
2262 var first
= n_exprs
.first
2263 return first
isa AStringFormExpr and first
.n_string
.text
.has_prefix
("\"\
"\"")
2270 redef class ToolContext
2271 var opt_dir = new OptionString("Working directory
(default
is '.nitpretty')", "--dir
")
2273 var opt_output = new OptionString("Output name
(default
is pretty
.nit
)", "-o
",
2276 var opt_diff = new OptionBool("Show diff between source
and output
", "--diff
")
2278 var opt_meld = new OptionBool("Show diff between source
and output using meld
",
2281 var opt_check = new OptionBool("Check format of
Nit source files
", "--check
")
2284 # Return result from diff between `file1` and `file2`.
2285 private fun diff(file1, file2: String): String do
2286 var p = new IProcess("diff
", "-u
", file1, file2)
2287 var res = p.read_all
2294 var toolcontext = new ToolContext
2296 toolcontext.option_context.
2297 add_option(toolcontext.opt_dir, toolcontext.opt_output, toolcontext.opt_diff,
2298 toolcontext.opt_meld, toolcontext.opt_check)
2300 toolcontext.tooldescription = "Usage: nitpretty
[OPTION]... <file
.nit
>\n
" +
2301 "Pretty print
Nit code from
Nit source files
."
2303 toolcontext.process_options args
2304 var arguments = toolcontext.option_context.rest
2306 var model = new Model
2307 var mbuilder = new ModelBuilder(model, toolcontext)
2308 var mmodules = mbuilder.parse(arguments)
2311 if mmodules.is_empty then
2312 print "Error: no
module to pretty print
"
2316 if not toolcontext.opt_check.value and mmodules.length > 1 then
2317 print "Error: only
--check option allow multiple modules
"
2321 var dir = toolcontext.opt_dir.value or else ".nitpretty
"
2322 if not dir.file_exists then dir.mkdir
2323 var v = new PrettyPrinterVisitor
2325 for mmodule in mmodules do
2326 if not mbuilder.mmodule2nmodule.has_key(mmodule) then
2327 print " Error: no source file
for module {mmodule}"
2331 var nmodule = mbuilder.mmodule2nmodule[mmodule]
2332 var file = "{dir}/{mmodule.name}.nit
"
2333 var tpl = v.pretty_nmodule(nmodule)
2334 tpl.write_to_file file
2336 if toolcontext.opt_check.value then
2337 var res = diff(nmodule.location.file.filename, file)
2339 if not res.is_empty then
2340 print "Wrong formating
for module {nmodule.location.file.filename}"
2341 toolcontext.info(res, 1)
2343 if toolcontext.opt_meld.value then
2344 sys.system "meld
{nmodule.location.file.filename} {file}"
2347 toolcontext.info("[OK] {nmodule.location.file.filename}", 1)
2351 var out = toolcontext.opt_output.value
2352 if out != null then sys.system "cp
{file} {out}"
2355 if toolcontext.opt_meld.value then
2356 sys.system "meld
{arguments.first} {file}"
2361 if toolcontext.opt_diff.value then
2362 var res = diff(arguments.first, file)
2363 if not res.is_empty then print res
2368 if not toolcontext.opt_quiet.value then tpl.write_to sys.stdout