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 ATypeExpr
631 redef fun accept_pretty_printer
(v
) do v
.visit n_type
637 redef fun accept_pretty_printer
(v
) do
638 v
.catch_up start_token
641 if not n_imports
.is_empty
then
644 for n_import
in n_imports
do
650 if not n_extern_code_blocks
.is_empty
then
653 for n_extern_block
in n_extern_code_blocks
do
654 v
.catch_up n_extern_block
655 v
.visit n_extern_block
657 if n_extern_block
!= n_extern_code_blocks
.last
then v
.addn
660 if not n_classdefs
.is_empty
then v
.addn
663 if not n_classdefs
.is_empty
then
666 for n_classdef
in n_classdefs
do
667 v
.catch_up n_classdef
669 if n_classdef
!= n_classdefs
.last
then v
.addn
677 redef fun start_token
do
678 if n_moduledecl
!= null then return n_moduledecl
.first_token
679 if not n_imports
.is_empty
then return n_imports
.first
.first_token
680 if not n_classdefs
.is_empty
then return n_classdefs
.first
.first_token
684 redef fun is_inlinable
do return false
687 redef class AModuledecl
688 redef fun accept_pretty_printer
(v
) do
694 if n_annotations
!= null then
695 var annot_inline
= v
.can_inline
(n_annotations
)
696 v
.visit n_annotations
698 if not annot_inline
then
699 if v
.current_token
isa TKwend then
713 redef class AModuleName
714 redef fun accept_pretty_printer
(v
) do
715 for path
in n_path
do
724 redef class ANoImport
725 redef fun accept_pretty_printer
(v
) do
734 redef class AStdImport
735 redef fun accept_pretty_printer
(v
) do
736 if not n_visibility
isa APublicVisibility then
751 redef class AClassdef
752 redef fun accept_pretty_printer
(v
) do
753 for n_propdef
in n_propdefs
do
756 if n_propdef
.n_doc
!= null or not v
.can_inline
(n_propdef
) then
757 if n_propdef
!= n_propdefs
.first
then v
.addn
759 if n_propdef
!= n_propdefs
.last
then v
.addn
767 redef class AStdClassdef
768 redef fun accept_pretty_printer
(v
) do
770 var can_inline
= v
.can_inline
(self)
772 if not n_visibility
isa APublicVisibility then
777 if n_kwredef
!= null then
786 if not n_formaldefs
.is_empty
then
788 v
.visit_list n_formaldefs
792 if n_extern_code_block
!= null then
794 v
.visit n_extern_code_block
800 if not n_superclasses
.is_empty
then
801 for n_superclass
in n_superclasses
do
811 for n_superclass
in n_superclasses
do
812 v
.catch_up n_superclass
819 if not n_superclasses
.is_empty
and not n_propdefs
.is_empty
then
834 redef fun is_inlinable
do
835 if not super then return false
836 if not n_propdefs
.is_empty
then return false
837 if n_superclasses
.length
> 1 then return false
838 if not collect_comments
.is_empty
then return false
842 redef fun start_token
do
843 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
844 if n_kwredef
!= null then return n_kwredef
845 return n_classkind
.first_token
849 redef class AAbstractClasskind
850 redef fun accept_pretty_printer
(v
) do
857 redef class AExternClasskind
858 redef fun accept_pretty_printer
(v
) do
865 redef class AFormaldef
866 redef fun accept_pretty_printer
(v
) do
869 if n_type
!= null then
878 redef fun accept_pretty_printer
(v
) do
879 if n_kwnullable
!= null then
886 if not n_types
.is_empty
then
894 redef class ASuperclass
895 redef fun accept_pretty_printer
(v
) do
905 redef fun accept_pretty_printer
(v
) do
909 if not n_visibility
isa APublicVisibility then
914 if n_kwredef
!= null then
920 redef fun start_token
do
921 if n_doc
== null then return super
922 return n_doc
.last_token
.next_token
926 redef class AAttrPropdef
927 redef fun accept_pretty_printer
(v
) do
933 if n_type
!= null then
939 if n_expr
!= null then
946 if n_annotations
!= null then v
.visit n_annotations
951 redef fun first_token
do
952 if n_doc
!= null then return n_doc
.first_token
953 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
954 if n_kwredef
!= null then return n_kwredef
958 redef fun is_inlinable
do return true
961 redef class ATypePropdef
962 redef fun accept_pretty_printer
(v
) do
974 redef fun is_inlinable
do return true
977 redef class AMethPropdef
978 redef fun accept_pretty_printer
(v
) do
979 # TODO: Handle extern annotations
981 var before
= v
.indent
982 var can_inline
= v
.can_inline
(self)
984 if n_kwinit
!= null then v
.visit n_kwinit
985 if n_kwmeth
!= null then v
.visit n_kwmeth
986 if n_kwnew
!= null then v
.visit n_kwnew
988 if not n_methid
== null then
995 if n_annotations
!= null then
996 v
.visit n_annotations
1001 if n_extern_calls
!= null or n_extern_code_block
!= null then
1002 if n_annotations
!= null then v
.adds
1003 if n_extern_calls
!= null then v
.visit n_extern_calls
1004 if n_extern_code_block
!= null then v
.visit n_extern_code_block
1007 var n_block
= self.n_block
1009 if n_block
!= null then
1010 while not v
.current_token
isa TKwdo do v
.skip
1011 if n_annotations
!= null then
1012 if v
.can_inline
(n_annotations
) then
1023 if n_block
isa ABlockExpr then
1024 if n_block
.n_expr
.is_empty
then
1025 v
.visit n_block
.n_kwend
1027 v
.visit n_block
.n_expr
.first
1028 v
.current_token
= n_block
.n_kwend
1033 if v
.current_token
isa TKwend then v
.skip
1040 if n_block
isa ABlockExpr then
1041 n_block
.force_block
= true
1043 v
.catch_up n_block
.n_kwend
1052 if n_block
isa ABlockExpr then
1053 v
.visit n_block
.n_kwend
1062 assert v
.indent
== before
1065 # Can be inlined if:
1066 # * block is empty or can be inlined
1067 # * contains no comments
1068 redef fun is_inlinable
do
1069 if not super then return false
1070 if n_annotations
!= null and not n_annotations
.is_inlinable
then return false
1071 if n_block
!= null and not n_block
.is_inlinable
then return false
1072 if n_extern_calls
!= null and not n_extern_calls
.is_inlinable
then return false
1073 if n_extern_code_block
!= null and not n_extern_code_block
.is_inlinable
then return false
1074 if not collect_comments
.is_empty
then return false
1079 redef class AMainMethPropdef
1080 redef fun accept_pretty_printer
(v
) do
1086 redef class ASignature
1087 redef fun accept_pretty_printer
(v
) do
1088 if not n_params
.is_empty
then
1090 v
.visit_list n_params
1094 if n_type
!= null then
1103 redef fun accept_pretty_printer
(v
) do
1106 if n_type
!= null then
1112 if n_dotdotdot
!= null then v
.visit n_dotdotdot
1118 redef class AExternCalls
1119 redef fun accept_pretty_printer
(v
) do
1120 var can_inline
= v
.can_inline
(self)
1125 v
.visit_list n_extern_calls
1130 v
.visit_list n_extern_calls
1137 redef class AFullPropExternCall
1138 redef fun accept_pretty_printer
(v
) do
1145 redef class ALocalPropExternCall
1146 redef fun accept_pretty_printer
(v
) do v
.visit n_methid
1149 redef class AInitPropExternCall
1150 redef fun accept_pretty_printer
(v
) do v
.visit n_type
1153 redef class ACastAsExternCall
1154 redef fun accept_pretty_printer
(v
) do
1164 redef class AAsNullableExternCall
1165 redef fun accept_pretty_printer
(v
) do
1170 v
.visit n_kwnullable
1174 redef class AAsNotNullableExternCall
1175 redef fun accept_pretty_printer
(v
) do
1182 v
.visit n_kwnullable
1186 redef class AExternCodeBlock
1187 redef fun accept_pretty_printer
(v
) do
1188 if n_in_language
!= null then
1189 v
.visit n_in_language
1193 v
.visit n_extern_code_segment
1196 redef fun is_inlinable
do
1197 if not super then return false
1198 return n_extern_code_segment
.is_inlinable
1202 redef class AInLanguage
1203 redef fun accept_pretty_printer
(v
) do
1210 redef class TExternCodeSegment
1211 redef fun accept_pretty_printer
(v
) do
1212 var can_inline
= v
.can_inline
(self)
1217 var text
= text
.substring
(2, text
.length
- 4)
1218 var lines
= text
.r_trim
.split
("\n")
1220 if text
.is_empty
then
1225 if not lines
.first
.trim
.is_empty
then
1233 for line
in lines
do
1242 v
.current_token
= next_token
1246 redef fun is_inlinable
do
1247 if not super then return false
1248 return location
.line_start
== location
.line_end
1254 redef class ABlockExpr
1255 redef fun accept_pretty_printer
(v
) do
1256 var before
= v
.indent
1257 var can_inline
= v
.can_inline
(self)
1259 if can_inline
and not n_expr
.is_empty
then
1260 v
.visit n_expr
.first
1263 for nexpr
in n_expr
do
1264 var expr_inline
= v
.can_inline
(nexpr
)
1265 if not expr_inline
and nexpr
!= n_expr
.first
then v
.addn
1271 if not expr_inline
and nexpr
!= n_expr
.last
then v
.addn
1275 assert v
.indent
== before
1278 redef fun is_inlinable
do
1279 if not super then return false
1280 if not collect_comments
.is_empty
then return false
1282 if not n_expr
.is_empty
then
1283 if n_expr
.length
> 1 then return false
1284 if not n_expr
.first
.is_inlinable
then return false
1292 redef fun accept_pretty_printer
(v
) do
1293 var before
= v
.indent
1294 var can_inline
= v
.can_inline
(self)
1298 if v
.can_inline
(n_expr
) then
1307 # skip comments before `then` token
1308 while not v
.current_token
isa TKwthen do v
.skip
1310 var n_else
= self.n_else
1314 if n_then
!= null then v
.visit n_then
1317 n_else
.force_inline
= true
1322 else if n_then
== null then
1326 v
.skip_to last_token
.last_real_token_in_line
1332 if n_then
!= null then
1333 if n_then
isa ABlockExpr then
1334 n_then
.force_block
= true
1344 while not v
.current_token
isa TKwelse do
1345 v
.consume v
.current_token
.text
1352 if n_else
isa AIfExpr then
1353 n_else
.force_block
= true
1361 if n_else
isa ABlockExpr then
1362 n_else
.force_block
= true
1370 if last_token
isa TKwend then
1371 v
.catch_up last_token
1382 if last_token
.location
>= v
.current_token
.location
then v
.catch_up last_token
1386 if v
.current_token
isa TKwend then v
.skip
1390 assert v
.indent
== before
1393 redef fun is_inlinable
do
1394 if not super then return false
1395 if n_then
!= null and not n_then
.is_inlinable
then return false
1396 var n_else
= self.n_else
1397 if (n_else
isa ABlockExpr and not n_else
.n_expr
.is_empty
) or
1398 (not n_else
isa ABlockExpr and n_else
!= null) then
1401 if not collect_comments
.is_empty
then return false
1405 # Does this `if` statement contains a `else` part?
1406 private fun has_else
(v
: PrettyPrinterVisitor): Bool do
1408 if n_else
== null then return false
1409 var n_kwelse
= collect_kwelse
1410 if n_kwelse
== null then return false
1412 if n_else
isa ABlockExpr then
1413 var comments
: Array[TComment]
1415 if n_then
== null then
1416 comments
= v
.collect_comments
(n_expr
.last_token
, n_else
.last_token
)
1418 comments
= v
.collect_comments
(n_then
.last_token
, n_else
.last_token
)
1421 if not comments
.is_empty
then return true
1422 return not n_else
.n_expr
.is_empty
1428 # Lookup for `else` token in `self`.
1429 private fun collect_kwelse
: nullable TKwelse do
1430 var token
= first_token
1432 while token
!= last_token
do
1433 if token
isa TKwelse then return token
1434 token
= token
.next_token
1441 # Used to factorize work on loops.
1442 private class ALoopHelper
1445 fun loop_block
: nullable ANode is abstract
1446 fun loop_label
: nullable ANode is abstract
1448 fun visit_loop_block
(v
: PrettyPrinterVisitor) do
1449 var n_block
= loop_block
1454 if n_block
isa ABlockExpr then
1455 n_block
.force_block
= true
1457 v
.catch_up n_block
.n_kwend
1460 v
.visit n_block
.n_kwend
1470 if loop_label
!= null then
1476 fun visit_loop_inline
(v
: PrettyPrinterVisitor) do
1477 var n_block
= loop_block
1480 if n_block
isa ABlockExpr then
1481 if n_block
.n_expr
.is_empty
then
1482 v
.visit n_block
.n_kwend
1484 v
.visit n_block
.n_expr
.first
1485 v
.current_token
= n_block
.n_kwend
1490 if v
.current_token
isa TKwend then v
.skip
1493 if loop_label
!= null then
1499 redef fun is_inlinable
do
1500 var n_block
= loop_block
1501 if not super then return false
1502 if n_block
isa ABlockExpr and not n_block
.is_inlinable
then return false
1503 if not collect_comments
.is_empty
then return false
1508 redef class ALoopExpr
1511 redef fun loop_block
do return n_block
1512 redef fun loop_label
do return n_label
1514 redef fun accept_pretty_printer
(v
) do
1515 var can_inline
= v
.can_inline
(self)
1517 if can_inline
then visit_loop_inline v
else visit_loop_block v
1521 redef class AWhileExpr
1524 redef fun loop_block
do return n_block
1525 redef fun loop_label
do return n_label
1527 redef fun accept_pretty_printer
(v
) do
1528 var can_inline
= v
.can_inline
(self)
1534 if can_inline
then visit_loop_inline v
else visit_loop_block v
1541 redef fun loop_block
do return n_block
1542 redef fun loop_label
do return n_label
1544 redef fun accept_pretty_printer
(v
) do
1545 var can_inline
= v
.can_inline
(self)
1547 if can_inline
then visit_loop_inline v
else visit_loop_block v
1551 redef class AForExpr
1554 redef fun loop_block
do return n_block
1555 redef fun loop_label
do return n_label
1557 redef fun accept_pretty_printer
(v
) do
1558 var can_inline
= v
.can_inline
(self)
1562 for n_id
in n_ids
do
1564 if n_id
!= n_ids
.last
then v
.add
", "
1573 if can_inline
then visit_loop_inline v
else visit_loop_block v
1577 redef class ABreakExpr
1578 redef fun accept_pretty_printer
(v
) do
1581 if n_expr
!= null then
1586 if n_label
!= null then
1592 redef fun is_inlinable
do return true
1595 redef class AContinueExpr
1596 redef fun accept_pretty_printer
(v
) do
1597 v
.visit n_kwcontinue
1599 if n_expr
!= null then
1604 if n_label
!= null then
1610 redef fun is_inlinable
do return true
1615 redef class ASendExpr
1616 redef fun is_inlinable
do return true
1619 redef class ACallExpr
1620 redef fun accept_pretty_printer
(v
) do
1621 var can_inline
= v
.can_inline
(self)
1624 if not n_expr
isa AImplicitSelfExpr and not can_inline
then
1632 if not n_args
.n_exprs
.is_empty
then
1633 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1635 if v
.current_token
isa TOpar then v
.skip
1636 v
.visit n_args
.n_exprs
.first
1637 if v
.current_token
isa TCpar then v
.skip
1639 if v
.current_token
isa TOpar then
1645 v
.visit_list n_args
.n_exprs
1646 if v
.current_token
isa TCpar then v
.consume
")"
1651 # Is the call alone on its line?
1652 fun is_stmt
: Bool do return parent
isa ABlockExpr
1655 redef class ACallAssignExpr
1656 redef fun accept_pretty_printer
(v
) do
1660 if not n_args
.n_exprs
.is_empty
then
1662 v
.visit_list n_args
.n_exprs
1673 redef class ACallReassignExpr
1674 redef fun accept_pretty_printer
(v
) do
1678 if not n_args
.n_exprs
.is_empty
then
1680 v
.visit_list n_args
.n_exprs
1691 redef class ABraExpr
1692 redef fun accept_pretty_printer
(v
) do
1695 if not n_args
.n_exprs
.is_empty
then
1697 v
.visit_list n_args
.n_exprs
1703 redef class ABraAssignExpr
1704 redef fun accept_pretty_printer
(v
) do
1707 if not n_args
.n_exprs
.is_empty
then
1709 v
.visit_list n_args
.n_exprs
1720 redef class ABraReassignExpr
1721 redef fun accept_pretty_printer
(v
) do
1724 if not n_args
.n_exprs
.is_empty
then
1726 v
.visit_list n_args
.n_exprs
1737 redef class AAssignMethid
1738 redef fun accept_pretty_printer
(v
) do
1744 redef class ABraMethid
1745 redef fun accept_pretty_printer
(v
) do
1751 redef class ABraassignMethid
1752 redef fun accept_pretty_printer
(v
) do
1759 redef class AInitExpr
1760 redef fun accept_pretty_printer
(v
) do
1761 if not n_expr
isa AImplicitSelfExpr then
1768 if not n_args
.n_exprs
.is_empty
then
1770 v
.visit_list n_args
.n_exprs
1776 redef class ANewExpr
1777 redef fun accept_pretty_printer
(v
) do
1778 var can_inline
= v
.can_inline
(self)
1783 if n_id
!= null then
1786 if not can_inline
then
1795 if not n_args
.n_exprs
.is_empty
then
1797 v
.visit_list n_args
.n_exprs
1802 redef fun is_inlinable
do return true
1807 redef class AAttrExpr
1808 redef fun accept_pretty_printer
(v
) do
1813 redef fun is_inlinable
do return true
1816 redef class AAttrAssignExpr
1817 redef fun accept_pretty_printer
(v
) do
1827 redef class AAttrReassignExpr
1828 redef fun accept_pretty_printer
(v
) do
1840 redef class AVardeclExpr
1841 redef fun accept_pretty_printer
(v
) do
1846 if n_type
!= null then
1852 if n_expr
!= null then
1860 redef fun is_inlinable
do return true
1863 redef class AVarAssignExpr
1864 redef fun accept_pretty_printer
(v
) do
1873 redef class AAssertExpr
1874 redef fun accept_pretty_printer
(v
) do
1875 var can_inline
= v
.can_inline
(self)
1878 if n_id
!= null then
1886 var n_else
= self.n_else
1888 if n_else
!= null then
1898 if n_else
isa ABlockExpr then
1900 n_else
.force_block
= true
1904 v
.visit n_else
.n_kwend
1918 redef fun is_inlinable
do
1919 if not super then return false
1920 if n_else
!= null and not n_else
.is_inlinable
then return false
1925 redef class AReturnExpr
1926 redef fun accept_pretty_printer
(v
) do
1929 if n_expr
!= null then
1936 redef class ASuperExpr
1937 redef fun accept_pretty_printer
(v
) do
1940 if not n_args
.n_exprs
.is_empty
then
1941 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1943 if v
.current_token
isa TOpar then v
.skip
1944 v
.visit n_args
.n_exprs
.first
1945 if v
.current_token
isa TCpar then v
.skip
1947 if v
.current_token
isa TOpar then
1953 v
.visit_list n_args
.n_exprs
1954 if v
.current_token
isa TCpar then v
.consume
")"
1959 # Is the call alone on its line?
1960 fun is_stmt
: Bool do return self.first_token
.is_starting_line
1962 redef fun is_inlinable
do return true
1965 redef class AOnceExpr
1966 redef fun accept_pretty_printer
(v
) do
1972 redef fun is_inlinable
do return true
1975 redef class AAbortExpr
1976 redef fun accept_pretty_printer
(v
) do v
.visit n_kwabort
1977 redef fun is_inlinable
do return true
1980 redef class ANotExpr
1981 redef fun accept_pretty_printer
(v
) do
1988 redef class AAsCastExpr
1989 redef fun accept_pretty_printer
(v
) do
1999 redef class AAsNotnullExpr
2000 redef fun accept_pretty_printer
(v
) do
2014 # Used to factorize work on Or, And, Implies and Binop expressions.
2015 private class ABinOpHelper
2018 fun bin_expr1
: AExpr is abstract
2019 fun bin_expr2
: AExpr is abstract
2022 fun bin_op
: String is abstract
2024 redef fun accept_pretty_printer
(v
) do
2025 var can_inline
= v
.can_inline
(self)
2027 if not can_inline
then
2028 if (self isa ABinopExpr and bin_expr1
isa ABinopExpr) or
2029 (self isa AAndExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr)) or
2030 (self isa AOrExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr))
2032 bin_expr1
.force_block
= true
2052 redef class AAndExpr
2055 redef fun bin_expr1
do return n_expr
2056 redef fun bin_expr2
do return n_expr2
2057 redef fun bin_op
do return "and"
2063 redef fun bin_expr1
do return n_expr
2064 redef fun bin_expr2
do return n_expr2
2065 redef fun bin_op
do return "or"
2068 redef class AImpliesExpr
2071 redef fun bin_expr1
do return n_expr
2072 redef fun bin_expr2
do return n_expr2
2073 redef fun bin_op
do return "implies"
2076 redef class ABinopExpr
2079 redef fun bin_expr1
do return n_expr
2080 redef fun bin_expr2
do return n_expr2
2084 redef fun bin_op
do return "=="
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 "<"
2111 redef class AMinusExpr
2112 redef fun bin_op
do return "-"
2116 redef fun bin_op
do return "!="
2119 redef class APercentExpr
2120 redef fun bin_op
do return "%"
2123 redef class APlusExpr
2124 redef fun bin_op
do return "+"
2127 redef class ASlashExpr
2128 redef fun bin_op
do return "/"
2131 redef class AStarExpr
2132 redef fun bin_op
do return "*"
2135 redef class AStarstarExpr
2136 redef fun bin_op
do return "**"
2139 redef class AStarshipExpr
2140 redef fun bin_op
do return "<=>"
2143 redef class AIsaExpr
2144 redef fun accept_pretty_printer
(v
) do
2153 redef class AOrElseExpr
2154 redef fun accept_pretty_printer
(v
) do
2164 redef fun is_inlinable
do return true
2169 redef class AUminusExpr
2170 redef fun accept_pretty_printer
(v
) do
2176 redef class ANullExpr
2177 redef fun accept_pretty_printer
(v
) do v
.visit n_kwnull
2178 redef fun is_inlinable
do return true
2181 redef class AParExpr
2182 redef fun accept_pretty_printer
(v
) do
2189 redef class AArrayExpr
2190 redef fun accept_pretty_printer
(v
) do
2192 v
.visit_list n_exprs
.n_exprs
2197 redef class ACrangeExpr
2198 redef fun accept_pretty_printer
(v
) do
2207 redef class AOrangeExpr
2208 redef fun accept_pretty_printer
(v
) do
2219 redef class AStringFormExpr
2220 redef fun accept_pretty_printer
(v
) do
2221 var can_inline
= v
.can_inline
(self)
2226 var text
= n_string
.text
2229 while i
< text
.length
do
2232 if v
.current_length
>= v
.max_size
and i
<= text
.length
- 3 then
2244 v
.current_token
= n_string
.next_token
2249 redef class ASuperstringExpr
2250 redef fun accept_pretty_printer
(v
) do
2251 for n_expr
in n_exprs
do v
.visit n_expr
2254 redef fun must_be_inline
do
2255 if super then return true
2257 if not n_exprs
.is_empty
then
2258 var first
= n_exprs
.first
2259 return first
isa AStringFormExpr and first
.n_string
.text
.has_prefix
("\"\
"\"")
2266 redef class ToolContext
2267 var opt_dir = new OptionString("Working directory
(default
is '.nitpretty')", "--dir
")
2269 var opt_output = new OptionString("Output name
(default
is pretty
.nit
)", "-o
",
2272 var opt_diff = new OptionBool("Show diff between source
and output
", "--diff
")
2274 var opt_meld = new OptionBool("Show diff between source
and output using meld
",
2277 var opt_check = new OptionBool("Check format of
Nit source files
", "--check
")
2280 # Return result from diff between `file1` and `file2`.
2281 private fun diff(file1, file2: String): String do
2282 var p = new IProcess("diff
", "-u
", file1, file2)
2283 var res = p.read_all
2290 var toolcontext = new ToolContext
2292 toolcontext.option_context.
2293 add_option(toolcontext.opt_dir, toolcontext.opt_output, toolcontext.opt_diff,
2294 toolcontext.opt_meld, toolcontext.opt_check)
2296 toolcontext.tooldescription = "Usage: nitpretty
[OPTION]... <file
.nit
>\n
" +
2297 "Pretty print
Nit code from
Nit source files
."
2299 toolcontext.process_options args
2300 var arguments = toolcontext.option_context.rest
2302 var model = new Model
2303 var mbuilder = new ModelBuilder(model, toolcontext)
2304 var mmodules = mbuilder.parse(arguments)
2307 if mmodules.is_empty then
2308 print "Error: no
module to pretty print
"
2312 if not toolcontext.opt_check.value and mmodules.length > 1 then
2313 print "Error: only
--check option allow multiple modules
"
2317 var dir = toolcontext.opt_dir.value or else ".nitpretty
"
2318 if not dir.file_exists then dir.mkdir
2319 var v = new PrettyPrinterVisitor
2321 for mmodule in mmodules do
2322 if not mbuilder.mmodule2nmodule.has_key(mmodule) then
2323 print " Error: no source file
for module {mmodule}"
2327 var nmodule = mbuilder.mmodule2nmodule[mmodule]
2328 var file = "{dir}/{mmodule.name}.nit
"
2329 var tpl = v.pretty_nmodule(nmodule)
2330 tpl.write_to_file file
2332 if toolcontext.opt_check.value then
2333 var res = diff(nmodule.location.file.filename, file)
2335 if not res.is_empty then
2336 print "Wrong formating
for module {nmodule.location.file.filename}"
2337 toolcontext.info(res, 1)
2339 if toolcontext.opt_meld.value then
2340 sys.system "meld
{nmodule.location.file.filename} {file}"
2343 toolcontext.info("[OK] {nmodule.location.file.filename}", 1)
2347 var out = toolcontext.opt_output.value
2348 if out != null then sys.system "cp
{file} {out}"
2351 if toolcontext.opt_meld.value then
2352 sys.system "meld
{arguments.first} {file}"
2357 if toolcontext.opt_diff.value then
2358 var res = diff(arguments.first, file)
2359 if not res.is_empty then print res
2364 if not toolcontext.opt_quiet.value then tpl.write_to sys.stdout