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"
208 # The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
210 # The main method here is `visit` that performs the pretty printing of a `ANode`.
212 # Because some tokens like `TComment` are not stored in the AST,
213 # we visit the AST like traditionnal visitor and also maintain a
214 # pointer to the `current_token` (actually the next one to be printed).
216 # Visited productions are in charges to move the token pointer using methods such as:
218 # * `consume`: print `current_token` and move to the next one
219 # * `skip`: move to the next token without printing the current one
220 # * `skip_to`: move to a specified token skipping all the tokens before
221 # * `catch_up`: consume all the tokens between `current_token` and a specified token
222 # * `finish_line`: consume all the tokens between `current_token` and the end of line
223 class PrettyPrinterVisitor
225 fun pretty
(n
: ANode): Template do
230 current_token
= n
.first_token
232 else if n
isa Token then
235 while p
!= null and not p
isa Prod do
239 current_token
= p
.first_token
243 return tpl
.as(not null)
246 # Pretty print the whole `nmodule` with comments before and after.
247 fun pretty_nmodule
(nmodule
: AModule): Template do
249 nmodule
.parentize_tokens
250 current_token
= nmodule
.location
.file
.first_token
252 catch_up nmodule
.location
.file
.last_token
254 return tpl
.as(not null)
257 # Prepare `self` for a new visit.
267 # Visit `n` if not null.
268 fun visit
(n
: nullable ANode) do
269 if n
== null then return
270 n
.accept_pretty_printer
self
273 # Visit a list of `Anode`.
274 fun visit_list
(n
: nullable ANodes[ANode]) do
275 if n
== null then return
276 n
.accept_pretty_printer
self
279 # Is the node inlinable and can fit on the line.
280 fun can_inline
(n
: nullable ANode): Bool do
281 if n
== null then return true
282 if n
.must_be_inline
then return true
283 if n
.must_be_block
then return false
285 if n
.collect_length
+ current_length
> max_size
then return false
286 # check block is inlinable
287 return n
.is_inlinable
290 # Collect all `TComment` between `from` and `to`.
291 fun collect_comments
(from
: nullable ANode, to
: nullable ANode): Array[TComment] do
292 var res
= new Array[TComment]
293 if from
isa Prod then from
= from
.first_token
294 if to
isa Prod then to
= to
.first_token
295 if from
== null or to
== null then return res
298 if from
isa TComment then res
.add from
299 from
= from
.as(Token).next_token
305 # Token under cursor.
307 # This is the next token to visit.
308 var current_token
: nullable Token = null
310 # Skip the `current_token`.
311 fun skip
do current_token
= current_token
.next_token
313 # Skip `current_token` until the end of line.
314 fun skip_line
do current_token
= current_token
.last_real_token_in_line
316 # Skip `current_token` until `target` is reached.
317 fun skip_to
(target
: nullable Token) do
318 if target
== null then return
319 while current_token
!= target
do skip
322 # Visit `current_token`.
323 fun consume
(token
: String) do
324 assert current_token
.text
== token
328 # Is there token to visit between `current_token` and `target`?
329 fun need_catch_up
(target
: nullable Token): Bool do
330 if target
== null then return false
331 return current_token
!= target
334 # Visit all tokens between `current_token` and `target`.
335 fun catch_up
(target
: nullable ANode) do
336 if target
== null then return
337 if current_token
== null then return
339 if target
isa Token then
341 else if target
isa Prod then
342 token
= target
.first_token
.as(not null)
346 assert current_token
.location
<= token
.location
347 while current_token
!= token
do visit current_token
350 # Visit all tokens between `current_token` and the end of line.
352 if current_token
isa TComment then
357 while current_token
isa TEol do skip
360 # The template under construction.
361 private var tpl
: nullable Template = null
363 # Current indent level.
366 # Size of a tabulation in spaces.
372 # Length of the current line.
373 var current_length
= 0
375 # Length of the previous line.
376 var previous_length
= 0
378 # Is the last line a blank line?
379 fun last_line_is_blank
: Bool do return previous_length
== 0
381 # Add `t` to current template.
382 fun add
(t
: String) do
383 if t
.is_empty
then return
384 while wait_addn
> 0 do
389 current_length
+= t
.length
394 if current_length
== 0 and last_line_is_blank
then return
395 previous_length
= current_length
400 # End of line chars are stored until another char is added.
401 # This avoid empty files with only a '`\n`'.
402 private var wait_addn
= 0
404 # Add `'\t' * indent`.
405 fun addt
do add
"\t" * indent
410 fun visit_recv
(n_expr
: AExpr) do
411 if not n_expr
isa AImplicitSelfExpr then
418 # Base framework redefs
420 redef class ANodes[E
]
421 private fun accept_pretty_printer
(v
: PrettyPrinterVisitor) do
423 var e_can_inline
= v
.can_inline
(e
)
426 if not e_can_inline
then
442 # Start visit of `self` using a `PrettyPrinterVisitor`
443 private fun accept_pretty_printer
(v
: PrettyPrinterVisitor) is abstract
445 # Collect the length (in `Char`) of the node.
446 private fun collect_length
: Int is abstract
448 # Is `self` printable in one line?
449 private fun is_inlinable
: Bool do return true
451 # Force `self` to be rendered as a block.
452 private var force_block
= false
454 # Does `self` have to be rendered as a block?
455 private fun must_be_block
: Bool do return force_block
457 # Force `self` to be rendered as a line.
458 private var force_inline
= false
460 # Does `self` have be rendered as a line?
461 private fun must_be_inline
: Bool do
462 if parent
!= null and parent
.must_be_inline
then return true
466 # Does `self` was written in one line before transformation?
467 private fun was_inline
: Bool is abstract
471 redef fun accept_pretty_printer
(v
) do
473 v
.current_token
= next_token
476 redef fun collect_length
do return text
.length
477 redef fun is_inlinable
do return true
478 redef fun was_inline
do return true
482 redef fun accept_pretty_printer
(v
) do v
.visit first_token
484 # The token where the production really start (skipping ADoc).
485 private fun start_token
: nullable Token do return first_token
487 # Collect all `TComment` contained in the production
488 # (between `start_token` and `end_token`).
489 private fun collect_comments
: Array[TComment] do
490 var res
= new Array[TComment]
491 if start_token
== null or last_token
== null then return res
492 var token
= start_token
494 while token
!= last_token
do
495 if token
isa TComment then res
.add token
496 token
= token
.next_token
502 redef fun collect_length
do
504 if start_token
== null or last_token
== null then return res
505 var token
= start_token
507 while token
!= last_token
do
508 res
+= token
.text
.length
509 token
= token
.next_token
512 res
+= token
.text
.length
516 redef fun was_inline
do
517 return first_token
.location
.line_start
== last_token
.location
.line_end
524 redef fun accept_pretty_printer
(v
) do
535 if is_last_in_group
then v
.addn
549 if next_token
isa TComment and is_first_in_group
then v
.addn
553 var prev_token
= self.prev_token
554 if prev_token
isa TComment and prev_token
.is_inline
and is_last_in_group
then v
.addn
561 # Is `self` part of an `ADoc`?
562 private fun is_adoc
: Bool do return parent
isa ADoc and parent
.parent
!= null
564 # Is `self` part of a licence?
565 private fun is_licence
: Bool do
566 var prev_token
= self.prev_token
568 if prev_token
== null then
570 else if prev_token
isa TComment then
571 return prev_token
.is_licence
577 # Is `self` starts and ends its line?
578 private fun is_inline
: Bool do
579 return self == first_real_token_in_line
and self == last_real_token_in_line
582 # Is `self` an orphan line (blank before and after)?
583 private fun is_orphan
: Bool do
584 return prev_token
isa TEol and
585 (prev_token
.prev_token
isa TEol or prev_token
.prev_token
isa TComment) and
589 # Is `self` the first comment of a group of comment?
590 private fun is_first_in_group
: Bool do return not prev_token
isa TComment
592 # Is `self` the last comment of a group of comments?
593 private fun is_last_in_group
: Bool do return not next_token
isa TComment
597 redef fun accept_pretty_printer
(v
) do for comment
in n_comment
do v
.visit comment
598 redef fun is_inlinable
do return n_comment
.length
<= 1
603 redef class AAnnotations
604 redef fun accept_pretty_printer
(v
) do
608 if v
.can_inline
(self) then
610 for n_item
in n_items
do
612 if n_item
!= n_items
.last
then
617 else if n_items
.length
> 1 then
621 for n_item
in n_items
do
630 if not was_inline
and v
.current_token
isa TKwend then v
.skip
633 redef fun is_inlinable
do
634 if not super then return false
635 for annot
in n_items
do if not annot
.is_inlinable
then return false
640 redef class AAnnotation
641 redef fun accept_pretty_printer
(v
) do
643 if not n_args
.is_empty
then
644 if n_opar
== null then
655 redef class ATypeExpr
656 redef fun accept_pretty_printer
(v
) do v
.visit n_type
662 redef fun accept_pretty_printer
(v
) do
663 v
.catch_up start_token
666 if not n_imports
.is_empty
then
669 for n_import
in n_imports
do
675 if not n_extern_code_blocks
.is_empty
then
678 for n_extern_block
in n_extern_code_blocks
do
679 v
.catch_up n_extern_block
680 v
.visit n_extern_block
682 if n_extern_block
!= n_extern_code_blocks
.last
then v
.addn
685 if not n_classdefs
.is_empty
then v
.addn
688 if not n_classdefs
.is_empty
then
691 for n_classdef
in n_classdefs
do
692 v
.catch_up n_classdef
694 if n_classdef
!= n_classdefs
.last
then v
.addn
702 redef fun start_token
do
703 if n_moduledecl
!= null then return n_moduledecl
.first_token
704 if not n_imports
.is_empty
then return n_imports
.first
.first_token
705 if not n_classdefs
.is_empty
then return n_classdefs
.first
.first_token
709 redef fun is_inlinable
do return false
712 redef class AModuledecl
713 redef fun accept_pretty_printer
(v
) do
719 if n_annotations
!= null then
720 var annot_inline
= v
.can_inline
(n_annotations
)
721 v
.visit n_annotations
723 if not annot_inline
then
724 if v
.current_token
isa TKwend then
738 redef class AModuleName
739 redef fun accept_pretty_printer
(v
) do
740 for path
in n_path
do
749 redef class ANoImport
750 redef fun accept_pretty_printer
(v
) do
759 redef class AStdImport
760 redef fun accept_pretty_printer
(v
) do
761 if not n_visibility
isa APublicVisibility then
776 redef class AClassdef
777 redef fun accept_pretty_printer
(v
) do
778 for n_propdef
in n_propdefs
do
781 if n_propdef
.n_doc
!= null or not v
.can_inline
(n_propdef
) then
782 if n_propdef
!= n_propdefs
.first
then v
.addn
784 if n_propdef
!= n_propdefs
.last
then v
.addn
792 redef class AStdClassdef
793 redef fun accept_pretty_printer
(v
) do
795 var can_inline
= v
.can_inline
(self)
797 if not n_visibility
isa APublicVisibility then
802 if n_kwredef
!= null then
811 if not n_formaldefs
.is_empty
then
813 v
.visit_list n_formaldefs
817 if n_extern_code_block
!= null then
819 v
.visit n_extern_code_block
825 if not n_superclasses
.is_empty
then
826 for n_superclass
in n_superclasses
do
836 for n_superclass
in n_superclasses
do
837 v
.catch_up n_superclass
844 if not n_superclasses
.is_empty
and not n_propdefs
.is_empty
then
859 redef fun is_inlinable
do
860 if not super then return false
861 if not n_propdefs
.is_empty
then return false
862 if n_superclasses
.length
> 1 then return false
863 if not collect_comments
.is_empty
then return false
867 redef fun start_token
do
868 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
869 if n_kwredef
!= null then return n_kwredef
870 return n_classkind
.first_token
874 redef class AAbstractClasskind
875 redef fun accept_pretty_printer
(v
) do
882 redef class AExternClasskind
883 redef fun accept_pretty_printer
(v
) do
890 redef class AFormaldef
891 redef fun accept_pretty_printer
(v
) do
894 if n_type
!= null then
903 redef fun accept_pretty_printer
(v
) do
904 if n_kwnullable
!= null then
911 if not n_types
.is_empty
then
919 redef class ASuperclass
920 redef fun accept_pretty_printer
(v
) do
930 redef fun accept_pretty_printer
(v
) do
934 if not n_visibility
isa APublicVisibility then
939 if n_kwredef
!= null then
945 redef fun start_token
do
946 if n_doc
== null then return super
947 return n_doc
.last_token
.next_token
951 redef class AAttrPropdef
952 redef fun accept_pretty_printer
(v
) do
958 if n_type
!= null then
964 if n_expr
!= null then
971 if n_annotations
!= null then v
.visit n_annotations
976 redef fun first_token
do
977 if n_doc
!= null then return n_doc
.first_token
978 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
979 if n_kwredef
!= null then return n_kwredef
983 redef fun is_inlinable
do return true
986 redef class ATypePropdef
987 redef fun accept_pretty_printer
(v
) do
999 redef fun is_inlinable
do return true
1002 redef class AMethPropdef
1003 redef fun accept_pretty_printer
(v
) do
1004 # TODO: Handle extern annotations
1006 var before
= v
.indent
1007 var can_inline
= v
.can_inline
(self)
1009 if n_kwinit
!= null then v
.visit n_kwinit
1010 if n_kwmeth
!= null then v
.visit n_kwmeth
1011 if n_kwnew
!= null then v
.visit n_kwnew
1013 if not n_methid
== null then
1020 if n_annotations
!= null then
1021 v
.visit n_annotations
1026 if n_extern_calls
!= null or n_extern_code_block
!= null then
1027 if n_annotations
!= null then v
.adds
1028 if n_extern_calls
!= null then v
.visit n_extern_calls
1029 if n_extern_code_block
!= null then v
.visit n_extern_code_block
1032 var n_block
= self.n_block
1034 if n_block
!= null then
1035 while not v
.current_token
isa TKwdo do v
.skip
1036 if n_annotations
!= null then
1037 if v
.can_inline
(n_annotations
) then
1048 if n_block
isa ABlockExpr then
1049 if n_block
.n_expr
.is_empty
then
1050 v
.visit n_block
.n_kwend
1052 v
.visit n_block
.n_expr
.first
1053 v
.current_token
= n_block
.n_kwend
1058 if v
.current_token
isa TKwend then v
.skip
1065 if n_block
isa ABlockExpr then
1066 n_block
.force_block
= true
1068 v
.catch_up n_block
.n_kwend
1077 if n_block
isa ABlockExpr then
1078 v
.visit n_block
.n_kwend
1087 assert v
.indent
== before
1090 # Can be inlined if:
1091 # * block is empty or can be inlined
1092 # * contains no comments
1093 redef fun is_inlinable
do
1094 if not super then return false
1095 if n_annotations
!= null and not n_annotations
.is_inlinable
then return false
1096 if n_block
!= null and not n_block
.is_inlinable
then return false
1097 if n_extern_calls
!= null and not n_extern_calls
.is_inlinable
then return false
1098 if n_extern_code_block
!= null and not n_extern_code_block
.is_inlinable
then return false
1099 if not collect_comments
.is_empty
then return false
1104 redef class AMainMethPropdef
1105 redef fun accept_pretty_printer
(v
) do
1111 redef class ASignature
1112 redef fun accept_pretty_printer
(v
) do
1113 if not n_params
.is_empty
then
1115 v
.visit_list n_params
1119 if n_type
!= null then
1128 redef fun accept_pretty_printer
(v
) do
1131 if n_type
!= null then
1137 if n_dotdotdot
!= null then v
.visit n_dotdotdot
1143 redef class AExternCalls
1144 redef fun accept_pretty_printer
(v
) do
1145 var can_inline
= v
.can_inline
(self)
1150 v
.visit_list n_extern_calls
1155 v
.visit_list n_extern_calls
1162 redef class AFullPropExternCall
1163 redef fun accept_pretty_printer
(v
) do
1170 redef class ALocalPropExternCall
1171 redef fun accept_pretty_printer
(v
) do v
.visit n_methid
1174 redef class AInitPropExternCall
1175 redef fun accept_pretty_printer
(v
) do v
.visit n_type
1178 redef class ACastAsExternCall
1179 redef fun accept_pretty_printer
(v
) do
1189 redef class AAsNullableExternCall
1190 redef fun accept_pretty_printer
(v
) do
1195 v
.visit n_kwnullable
1199 redef class AAsNotNullableExternCall
1200 redef fun accept_pretty_printer
(v
) do
1207 v
.visit n_kwnullable
1211 redef class AExternCodeBlock
1212 redef fun accept_pretty_printer
(v
) do
1213 if n_in_language
!= null then
1214 v
.visit n_in_language
1218 v
.visit n_extern_code_segment
1221 redef fun is_inlinable
do
1222 if not super then return false
1223 return n_extern_code_segment
.is_inlinable
1227 redef class AInLanguage
1228 redef fun accept_pretty_printer
(v
) do
1235 redef class TExternCodeSegment
1236 redef fun accept_pretty_printer
(v
) do
1237 var can_inline
= v
.can_inline
(self)
1242 var text
= text
.substring
(2, text
.length
- 4)
1243 var lines
= text
.r_trim
.split
("\n")
1245 if text
.is_empty
then
1250 if not lines
.first
.trim
.is_empty
then
1258 for line
in lines
do
1267 v
.current_token
= next_token
1271 redef fun is_inlinable
do
1272 if not super then return false
1273 return location
.line_start
== location
.line_end
1279 redef class ABlockExpr
1280 redef fun accept_pretty_printer
(v
) do
1281 var before
= v
.indent
1282 var can_inline
= v
.can_inline
(self)
1284 if can_inline
and not n_expr
.is_empty
then
1285 v
.visit n_expr
.first
1288 for nexpr
in n_expr
do
1289 var expr_inline
= v
.can_inline
(nexpr
)
1290 if not expr_inline
and nexpr
!= n_expr
.first
then v
.addn
1296 if not expr_inline
and nexpr
!= n_expr
.last
then v
.addn
1300 assert v
.indent
== before
1303 redef fun is_inlinable
do
1304 if not super then return false
1305 if not collect_comments
.is_empty
then return false
1307 if not n_expr
.is_empty
then
1308 if n_expr
.length
> 1 then return false
1309 if not n_expr
.first
.is_inlinable
then return false
1317 redef fun accept_pretty_printer
(v
) do
1318 var before
= v
.indent
1319 var can_inline
= v
.can_inline
(self)
1323 if v
.can_inline
(n_expr
) then
1332 # skip comments before `then` token
1333 while not v
.current_token
isa TKwthen do v
.skip
1335 var n_else
= self.n_else
1339 if n_then
!= null then v
.visit n_then
1342 n_else
.force_inline
= true
1347 else if n_then
== null then
1351 v
.skip_to last_token
.last_real_token_in_line
1357 if n_then
!= null then
1358 if n_then
isa ABlockExpr then
1359 n_then
.force_block
= true
1369 while not v
.current_token
isa TKwelse do
1370 v
.consume v
.current_token
.text
1377 if n_else
isa AIfExpr then
1378 n_else
.force_block
= true
1386 if n_else
isa ABlockExpr then
1387 n_else
.force_block
= true
1395 if last_token
isa TKwend then
1396 v
.catch_up last_token
1407 if last_token
.location
>= v
.current_token
.location
then v
.catch_up last_token
1411 if v
.current_token
isa TKwend then v
.skip
1415 assert v
.indent
== before
1418 redef fun is_inlinable
do
1419 if not super then return false
1420 if n_then
!= null and not n_then
.is_inlinable
then return false
1421 var n_else
= self.n_else
1422 if (n_else
isa ABlockExpr and not n_else
.n_expr
.is_empty
) or
1423 (not n_else
isa ABlockExpr and n_else
!= null) then
1426 if not collect_comments
.is_empty
then return false
1430 # Does this `if` statement contains a `else` part?
1431 private fun has_else
(v
: PrettyPrinterVisitor): Bool do
1433 if n_else
== null then return false
1434 var n_kwelse
= collect_kwelse
1435 if n_kwelse
== null then return false
1437 if n_else
isa ABlockExpr then
1438 var comments
: Array[TComment]
1440 if n_then
== null then
1441 comments
= v
.collect_comments
(n_expr
.last_token
, n_else
.last_token
)
1443 comments
= v
.collect_comments
(n_then
.last_token
, n_else
.last_token
)
1446 if not comments
.is_empty
then return true
1447 return not n_else
.n_expr
.is_empty
1453 # Lookup for `else` token in `self`.
1454 private fun collect_kwelse
: nullable TKwelse do
1455 var token
= first_token
1457 while token
!= last_token
do
1458 if token
isa TKwelse then return token
1459 token
= token
.next_token
1466 # Used to factorize work on loops.
1467 private class ALoopHelper
1470 fun loop_block
: nullable ANode is abstract
1471 fun loop_label
: nullable ANode is abstract
1473 fun visit_loop_block
(v
: PrettyPrinterVisitor) do
1474 var n_block
= loop_block
1479 if n_block
isa ABlockExpr then
1480 n_block
.force_block
= true
1482 v
.catch_up n_block
.n_kwend
1485 v
.visit n_block
.n_kwend
1495 if loop_label
!= null then
1501 fun visit_loop_inline
(v
: PrettyPrinterVisitor) do
1502 var n_block
= loop_block
1505 if n_block
isa ABlockExpr then
1506 if n_block
.n_expr
.is_empty
then
1507 v
.visit n_block
.n_kwend
1509 v
.visit n_block
.n_expr
.first
1510 v
.current_token
= n_block
.n_kwend
1515 if v
.current_token
isa TKwend then v
.skip
1518 if loop_label
!= null then
1524 redef fun is_inlinable
do
1525 var n_block
= loop_block
1526 if not super then return false
1527 if n_block
isa ABlockExpr and not n_block
.is_inlinable
then return false
1528 if not collect_comments
.is_empty
then return false
1533 redef class ALoopExpr
1536 redef fun loop_block
do return n_block
1537 redef fun loop_label
do return n_label
1539 redef fun accept_pretty_printer
(v
) do
1540 var can_inline
= v
.can_inline
(self)
1542 if can_inline
then visit_loop_inline v
else visit_loop_block v
1546 redef class AWhileExpr
1549 redef fun loop_block
do return n_block
1550 redef fun loop_label
do return n_label
1552 redef fun accept_pretty_printer
(v
) do
1553 var can_inline
= v
.can_inline
(self)
1559 if can_inline
then visit_loop_inline v
else visit_loop_block v
1566 redef fun loop_block
do return n_block
1567 redef fun loop_label
do return n_label
1569 redef fun accept_pretty_printer
(v
) do
1570 var can_inline
= v
.can_inline
(self)
1572 if can_inline
then visit_loop_inline v
else visit_loop_block v
1576 redef class AForExpr
1579 redef fun loop_block
do return n_block
1580 redef fun loop_label
do return n_label
1582 redef fun accept_pretty_printer
(v
) do
1583 var can_inline
= v
.can_inline
(self)
1587 for n_id
in n_ids
do
1589 if n_id
!= n_ids
.last
then v
.add
", "
1598 if can_inline
then visit_loop_inline v
else visit_loop_block v
1602 redef class ABreakExpr
1603 redef fun accept_pretty_printer
(v
) do
1606 if n_expr
!= null then
1611 if n_label
!= null then
1617 redef fun is_inlinable
do return true
1620 redef class AContinueExpr
1621 redef fun accept_pretty_printer
(v
) do
1622 v
.visit n_kwcontinue
1624 if n_expr
!= null then
1629 if n_label
!= null then
1635 redef fun is_inlinable
do return true
1640 redef class ASendExpr
1641 redef fun is_inlinable
do return true
1644 redef class ACallExpr
1645 redef fun accept_pretty_printer
(v
) do
1646 var can_inline
= v
.can_inline
(self)
1649 if not n_expr
isa AImplicitSelfExpr and not can_inline
then
1657 if not n_args
.n_exprs
.is_empty
then
1658 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1660 if v
.current_token
isa TOpar then v
.skip
1661 v
.visit n_args
.n_exprs
.first
1662 if v
.current_token
isa TCpar then v
.skip
1664 if v
.current_token
isa TOpar then
1670 v
.visit_list n_args
.n_exprs
1671 if v
.current_token
isa TCpar then v
.consume
")"
1676 # Is the call alone on its line?
1677 fun is_stmt
: Bool do return parent
isa ABlockExpr
1680 redef class ACallAssignExpr
1681 redef fun accept_pretty_printer
(v
) do
1685 if not n_args
.n_exprs
.is_empty
then
1687 v
.visit_list n_args
.n_exprs
1698 redef class ACallReassignExpr
1699 redef fun accept_pretty_printer
(v
) do
1703 if not n_args
.n_exprs
.is_empty
then
1705 v
.visit_list n_args
.n_exprs
1716 redef class ABraExpr
1717 redef fun accept_pretty_printer
(v
) do
1720 if not n_args
.n_exprs
.is_empty
then
1722 v
.visit_list n_args
.n_exprs
1728 redef class ABraAssignExpr
1729 redef fun accept_pretty_printer
(v
) do
1732 if not n_args
.n_exprs
.is_empty
then
1734 v
.visit_list n_args
.n_exprs
1745 redef class ABraReassignExpr
1746 redef fun accept_pretty_printer
(v
) do
1749 if not n_args
.n_exprs
.is_empty
then
1751 v
.visit_list n_args
.n_exprs
1762 redef class AAssignMethid
1763 redef fun accept_pretty_printer
(v
) do
1769 redef class ABraMethid
1770 redef fun accept_pretty_printer
(v
) do
1776 redef class ABraassignMethid
1777 redef fun accept_pretty_printer
(v
) do
1784 redef class AInitExpr
1785 redef fun accept_pretty_printer
(v
) do
1786 if not n_expr
isa AImplicitSelfExpr then
1793 if not n_args
.n_exprs
.is_empty
then
1795 v
.visit_list n_args
.n_exprs
1801 redef class ANewExpr
1802 redef fun accept_pretty_printer
(v
) do
1803 var can_inline
= v
.can_inline
(self)
1808 if n_id
!= null then
1811 if not can_inline
then
1820 if not n_args
.n_exprs
.is_empty
then
1822 v
.visit_list n_args
.n_exprs
1827 redef fun is_inlinable
do return true
1832 redef class AAttrExpr
1833 redef fun accept_pretty_printer
(v
) do
1838 redef fun is_inlinable
do return true
1841 redef class AAttrAssignExpr
1842 redef fun accept_pretty_printer
(v
) do
1852 redef class AAttrReassignExpr
1853 redef fun accept_pretty_printer
(v
) do
1865 redef class AVardeclExpr
1866 redef fun accept_pretty_printer
(v
) do
1871 if n_type
!= null then
1877 if n_expr
!= null then
1885 redef fun is_inlinable
do return true
1888 redef class AVarAssignExpr
1889 redef fun accept_pretty_printer
(v
) do
1898 redef class AAssertExpr
1899 redef fun accept_pretty_printer
(v
) do
1900 var can_inline
= v
.can_inline
(self)
1903 if n_id
!= null then
1911 var n_else
= self.n_else
1913 if n_else
!= null then
1923 if n_else
isa ABlockExpr then
1925 n_else
.force_block
= true
1929 v
.visit n_else
.n_kwend
1943 redef fun is_inlinable
do
1944 if not super then return false
1945 if n_else
!= null and not n_else
.is_inlinable
then return false
1950 redef class AReturnExpr
1951 redef fun accept_pretty_printer
(v
) do
1954 if n_expr
!= null then
1961 redef class ASuperExpr
1962 redef fun accept_pretty_printer
(v
) do
1965 if not n_args
.n_exprs
.is_empty
then
1966 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1968 if v
.current_token
isa TOpar then v
.skip
1969 v
.visit n_args
.n_exprs
.first
1970 if v
.current_token
isa TCpar then v
.skip
1972 if v
.current_token
isa TOpar then
1978 v
.visit_list n_args
.n_exprs
1979 if v
.current_token
isa TCpar then v
.consume
")"
1984 # Is the call alone on its line?
1985 fun is_stmt
: Bool do return self.first_token
.is_starting_line
1987 redef fun is_inlinable
do return true
1990 redef class AOnceExpr
1991 redef fun accept_pretty_printer
(v
) do
1997 redef fun is_inlinable
do return true
2000 redef class AAbortExpr
2001 redef fun accept_pretty_printer
(v
) do v
.visit n_kwabort
2002 redef fun is_inlinable
do return true
2005 redef class ANotExpr
2006 redef fun accept_pretty_printer
(v
) do
2013 redef class AAsCastExpr
2014 redef fun accept_pretty_printer
(v
) do
2024 redef class AAsNotnullExpr
2025 redef fun accept_pretty_printer
(v
) do
2039 # Used to factorize work on Or, And, Implies and Binop expressions.
2040 private class ABinOpHelper
2043 fun bin_expr1
: AExpr is abstract
2044 fun bin_expr2
: AExpr is abstract
2047 fun bin_op
: String is abstract
2049 redef fun accept_pretty_printer
(v
) do
2050 var can_inline
= v
.can_inline
(self)
2052 if not can_inline
then
2053 if (self isa ABinopExpr and bin_expr1
isa ABinopExpr) or
2054 (self isa AAndExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr)) or
2055 (self isa AOrExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr))
2057 bin_expr1
.force_block
= true
2077 redef class AAndExpr
2080 redef fun bin_expr1
do return n_expr
2081 redef fun bin_expr2
do return n_expr2
2082 redef fun bin_op
do return "and"
2088 redef fun bin_expr1
do return n_expr
2089 redef fun bin_expr2
do return n_expr2
2090 redef fun bin_op
do return "or"
2093 redef class AImpliesExpr
2096 redef fun bin_expr1
do return n_expr
2097 redef fun bin_expr2
do return n_expr2
2098 redef fun bin_op
do return "implies"
2101 redef class ABinopExpr
2104 redef fun bin_expr1
do return n_expr
2105 redef fun bin_expr2
do return n_expr2
2109 redef fun bin_op
do return "=="
2113 redef fun bin_op
do return ">="
2117 redef fun bin_op
do return ">>"
2121 redef fun bin_op
do return ">"
2125 redef fun bin_op
do return "<="
2129 redef fun bin_op
do return "<<"
2133 redef fun bin_op
do return "<"
2136 redef class AMinusExpr
2137 redef fun bin_op
do return "-"
2141 redef fun bin_op
do return "!="
2144 redef class APercentExpr
2145 redef fun bin_op
do return "%"
2148 redef class APlusExpr
2149 redef fun bin_op
do return "+"
2152 redef class ASlashExpr
2153 redef fun bin_op
do return "/"
2156 redef class AStarExpr
2157 redef fun bin_op
do return "*"
2160 redef class AStarstarExpr
2161 redef fun bin_op
do return "**"
2164 redef class AStarshipExpr
2165 redef fun bin_op
do return "<=>"
2168 redef class AIsaExpr
2169 redef fun accept_pretty_printer
(v
) do
2178 redef class AOrElseExpr
2179 redef fun accept_pretty_printer
(v
) do
2189 redef fun is_inlinable
do return true
2194 redef class AUminusExpr
2195 redef fun accept_pretty_printer
(v
) do
2201 redef class ANullExpr
2202 redef fun accept_pretty_printer
(v
) do v
.visit n_kwnull
2203 redef fun is_inlinable
do return true
2206 redef class AParExpr
2207 redef fun accept_pretty_printer
(v
) do
2214 redef class AArrayExpr
2215 redef fun accept_pretty_printer
(v
) do
2217 v
.visit_list n_exprs
.n_exprs
2222 redef class ACrangeExpr
2223 redef fun accept_pretty_printer
(v
) do
2232 redef class AOrangeExpr
2233 redef fun accept_pretty_printer
(v
) do
2244 redef class AStringFormExpr
2245 redef fun accept_pretty_printer
(v
) do
2246 var can_inline
= v
.can_inline
(self)
2251 var text
= n_string
.text
2254 while i
< text
.length
do
2257 if v
.current_length
>= v
.max_size
and i
<= text
.length
- 3 then
2269 v
.current_token
= n_string
.next_token
2274 redef class ASuperstringExpr
2275 redef fun accept_pretty_printer
(v
) do
2276 for n_expr
in n_exprs
do v
.visit n_expr
2279 redef fun must_be_inline
do
2280 if super then return true
2282 if not n_exprs
.is_empty
then
2283 var first
= n_exprs
.first
2284 return first
isa AStringFormExpr and first
.n_string
.text
.has_prefix
("\"\
"\"")
2291 redef class ToolContext
2292 var opt_dir = new OptionString("Working directory
(default
is '.nitpretty')", "--dir
")
2294 var opt_output = new OptionString("Output name
(default
is pretty
.nit
)", "-o
",
2297 var opt_diff = new OptionBool("Show diff between source
and output
", "--diff
")
2299 var opt_meld = new OptionBool("Show diff between source
and output using meld
",
2302 var opt_check = new OptionBool("Check format of
Nit source files
", "--check
")
2305 # Return result from diff between `file1` and `file2`.
2306 private fun diff(file1, file2: String): String do
2307 var p = new IProcess("diff
", "-u
", file1, file2)
2308 var res = p.read_all
2315 var toolcontext = new ToolContext
2317 toolcontext.option_context.
2318 add_option(toolcontext.opt_dir, toolcontext.opt_output, toolcontext.opt_diff,
2319 toolcontext.opt_meld, toolcontext.opt_check)
2321 toolcontext.tooldescription = "Usage: nitpretty
[OPTION]... <file
.nit
>\n
" +
2322 "Pretty print
Nit code from
Nit source files
."
2324 toolcontext.process_options args
2325 var arguments = toolcontext.option_context.rest
2327 var model = new Model
2328 var mbuilder = new ModelBuilder(model, toolcontext)
2329 var mmodules = mbuilder.parse(arguments)
2332 if mmodules.is_empty then
2333 print "Error: no
module to pretty print
"
2337 if not toolcontext.opt_check.value and mmodules.length > 1 then
2338 print "Error: only
--check option allow multiple modules
"
2342 var dir = toolcontext.opt_dir.value or else ".nitpretty
"
2343 if not dir.file_exists then dir.mkdir
2344 var v = new PrettyPrinterVisitor
2346 for mmodule in mmodules do
2347 if not mbuilder.mmodule2nmodule.has_key(mmodule) then
2348 print " Error: no source file
for module {mmodule}"
2352 var nmodule = mbuilder.mmodule2nmodule[mmodule]
2353 var file = "{dir}/{mmodule.name}.nit
"
2354 var tpl = v.pretty_nmodule(nmodule)
2355 tpl.write_to_file file
2357 if toolcontext.opt_check.value then
2358 var res = diff(nmodule.location.file.filename, file)
2360 if not res.is_empty then
2361 print "Wrong formating
for module {nmodule.location.file.filename}"
2362 toolcontext.info(res, 1)
2364 if toolcontext.opt_meld.value then
2365 sys.system "meld
{nmodule.location.file.filename} {file}"
2368 toolcontext.info("[OK] {nmodule.location.file.filename}", 1)
2372 var out = toolcontext.opt_output.value
2373 if out != null then sys.system "cp
{file} {out}"
2376 if toolcontext.opt_meld.value then
2377 sys.system "meld
{arguments.first} {file}"
2382 if toolcontext.opt_diff.value then
2383 var res = diff(arguments.first, file)
2384 if not res.is_empty then print res
2389 if not toolcontext.opt_quiet.value then tpl.write_to sys.stdout