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 # Library used to pretty print Nit code.
20 # var tc = new ToolContext
21 # var nmodule = tc.parse_something("class A\nfun toto : Int do return 5\nend")
22 # var ppv = new PrettyPrinterVisitor
23 # var pmodule = ppv.pretty(nmodule)
24 # assert pmodule.write_to_string == """
26 # \tfun toto: Int do return 5
29 # See `nitpretty` tool for more documentation.
37 # The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
39 # The main method here is `visit` that performs the pretty printing of a `ANode`.
41 # Because some tokens like `TComment` are not stored in the AST,
42 # we visit the AST like traditionnal visitor and also maintain a
43 # pointer to the `current_token` (actually the next one to be printed).
45 # Visited productions are in charges to move the token pointer using methods such as:
47 # * `consume`: print `current_token` and move to the next one
48 # * `skip`: move to the next token without printing the current one
49 # * `skip_to`: move to a specified token skipping all the tokens before
50 # * `catch_up`: consume all the tokens between `current_token` and a specified token
51 # * `finish_line`: consume all the tokens between `current_token` and the end of line
52 class PrettyPrinterVisitor
54 fun pretty
(n
: ANode): Template do
59 current_token
= n
.first_token
61 else if n
isa Token then
64 while p
!= null and not p
isa Prod do
68 current_token
= p
.first_token
72 return tpl
.as(not null)
75 # Pretty print the whole `nmodule` with comments before and after.
76 fun pretty_nmodule
(nmodule
: AModule): Template do
78 nmodule
.parentize_tokens
79 current_token
= nmodule
.location
.file
.first_token
81 catch_up nmodule
.location
.file
.last_token
82 if skip_empty
then tpl
.add
"\n"
83 return tpl
.as(not null)
86 # Prepare `self` for a new visit.
96 # Visit `n` if not null.
97 fun visit
(n
: nullable ANode) do
98 if n
== null then return
99 n
.accept_pretty_printer
self
102 # Visit a list of arguments `ANode` with optional parentheses
103 fun visit_args
(n
: nullable ANodes[ANode]) do
104 if n
== null or n
.is_empty
then return
105 if current_token
isa TOpar then
112 if current_token
isa TCpar then consume
")"
115 # Visit a list of `ANode`.
116 fun visit_list
(n
: nullable ANodes[ANode]) do
117 if n
== null then return
118 n
.accept_pretty_printer
self
121 # Is the node inlinable and can fit on the line.
122 fun can_inline
(n
: nullable ANode): Bool do
123 if n
== null then return true
124 if n
.must_be_inline
then return true
125 if n
.must_be_block
then return false
127 if max_size
> 0 and n
.collect_length
+ current_length
> max_size
then return false
128 # check block is inlinable
129 return n
.is_inlinable
132 # Collect all `TComment` between `from` and `to`.
133 fun collect_comments
(from
: nullable ANode, to
: nullable ANode): Array[TComment] do
134 var res
= new Array[TComment]
135 if from
isa Prod then from
= from
.first_token
136 if to
isa Prod then to
= to
.first_token
137 if from
== null or to
== null then return res
140 if from
isa TComment then res
.add from
141 from
= from
.as(Token).next_token
147 # Token under cursor.
149 # This is the next token to visit.
150 var current_token
: nullable Token = null
152 # Skip the `current_token`.
153 fun skip
do current_token
= current_token
.next_token
155 # Skip `current_token` until the end of line.
156 fun skip_line
do current_token
= current_token
.last_real_token_in_line
158 # Skip `current_token` until `target` is reached.
159 fun skip_to
(target
: nullable Token) do
160 if target
== null then return
161 while current_token
!= null and current_token
!= target
do skip
162 if current_token
== null then
163 target
.debug
("Looked for, but not found :(")
168 # Consume comments and end of lines if any
169 fun consume_comments
do
170 while current_token
isa TEol or current_token
isa TComment do visit current_token
173 # Visit `current_token`.
174 fun consume
(token
: String) do
176 if current_token
.text
== token
then else current_token
.debug
("Got `{current_token.text}`; expected `{token}`.")
180 # Is there token to visit between `current_token` and `target`?
181 fun need_catch_up
(target
: nullable Token): Bool do
182 if target
== null then return false
183 return current_token
!= target
186 # Visit all tokens between `current_token` and `target`.
187 fun catch_up
(target
: nullable ANode) do
188 if target
== null then return
189 if current_token
== null then return
191 if target
isa Token then
193 else if target
isa Prod then
194 token
= target
.first_token
.as(not null)
198 if current_token
.location
> token
.location
then return
199 while current_token
!= token
do visit current_token
202 # Visit all tokens between `current_token` and the end of line.
204 if current_token
isa TComment then
209 while current_token
isa TEol do visit
(current_token
)
212 # The template under construction.
213 private var tpl
: nullable Template = null
215 # Current indent level.
218 # Size of a tabulation in spaces.
222 # 0 (or negative) to disable.
223 var max_size
= 80 is writable
225 # Length of the current line.
226 var current_length
= 0
228 # Length of the previous line.
229 var previous_length
= 0
231 # Is the last line a blank line?
232 fun last_line_is_blank
: Bool do return previous_length
== 0
234 # Add `t` to current template.
235 fun add
(t
: String) do
236 if t
.is_empty
then return
237 while wait_addn
> 0 do
242 current_length
+= t
.length
247 if current_length
== 0 and last_line_is_blank
then return
248 previous_length
= current_length
250 if skip_empty
then wait_addn
+= 1
253 # Perform `addn` even if not `skip_empty`.
255 if current_length
== 0 and last_line_is_blank
then return
256 previous_length
= current_length
261 # End of line chars are stored until another char is added.
262 # This avoid empty files with only a '`\n`'.
263 private var wait_addn
= 0
265 # Add `'\t' * indent`.
266 fun addt
do add
"\t" * indent
271 # Visit explicit receiver, implicit self will be ignored.
272 fun visit_recv
(n_expr
: AExpr) do
273 if not n_expr
isa AImplicitSelfExpr then
279 # Do we break string literals that are too long?
280 var break_strings
= false is public
writable
282 # Do we force `do` to be on the same line as the method signature?
283 var inline_do
= false is public
writable
285 # Do we force the deletion of empty lines?
286 var skip_empty
= false is public
writable
289 # Base framework redefs
291 redef class ANodes[E
]
292 private fun accept_pretty_printer
(v
: PrettyPrinterVisitor) do
294 var e_can_inline
= v
.can_inline
(e
)
296 if v
.current_token
isa TComma then v
.skip
299 if not e_can_inline
then
316 # Start visit of `self` using a `PrettyPrinterVisitor`
317 private fun accept_pretty_printer
(v
: PrettyPrinterVisitor) is abstract
319 # Collect the length (in `Char`) of the node.
320 private fun collect_length
: Int is abstract
322 # Is `self` printable in one line?
323 private fun is_inlinable
: Bool do return true
325 # Force `self` to be rendered as a block.
326 private var force_block
= false
328 # Does `self` have to be rendered as a block?
329 private fun must_be_block
: Bool do return force_block
331 # Force `self` to be rendered as a line.
332 private var force_inline
= false
334 # Does `self` have be rendered as a line?
335 private fun must_be_inline
: Bool do
336 if parent
!= null and parent
.must_be_inline
then return true
340 # Does `self` was written in one line before transformation?
341 private fun was_inline
: Bool is abstract
345 redef fun accept_pretty_printer
(v
) do
347 v
.current_token
= next_token
350 redef fun collect_length
do return text
.length
351 redef fun is_inlinable
do return true
352 redef fun was_inline
do return true
356 redef fun accept_pretty_printer
(v
) do
361 v
.current_token
= next_token
367 redef fun accept_pretty_printer
(v
) do v
.visit first_token
369 # The token where the production really start (skipping ADoc).
370 private fun start_token
: nullable Token do return first_token
372 # Collect all `TComment` contained in the production
373 # (between `start_token` and `end_token`).
374 private fun collect_comments
: Array[TComment] do
375 var res
= new Array[TComment]
376 if start_token
== null or last_token
== null then return res
377 var token
= start_token
379 while token
!= last_token
do
380 if token
isa TComment then res
.add token
381 token
= token
.next_token
387 redef fun collect_length
do
389 if start_token
== null or last_token
== null then return res
390 var token
= start_token
392 while token
!= last_token
do
393 res
+= token
.text
.length
394 token
= token
.next_token
397 res
+= token
.text
.length
401 redef fun was_inline
do
402 return start_token
.location
.line_start
== last_token
.location
.line_end
409 redef fun accept_pretty_printer
(v
) do
420 if is_last_in_group
then v
.addn
434 if next_token
isa TComment and is_first_in_group
then v
.addn
438 var prev_token
= self.prev_token
439 if prev_token
isa TComment and prev_token
.is_inline
and is_last_in_group
then v
.addn
444 if not v
.skip_empty
then v
.forcen
447 # Is `self` part of an `ADoc`?
448 private fun is_adoc
: Bool do return parent
isa ADoc and parent
.parent
!= null
450 # Is `self` part of a licence?
451 private fun is_licence
: Bool do
452 var prev_token
= self.prev_token
454 if prev_token
== null then
456 else if prev_token
isa TComment then
457 return prev_token
.is_licence
463 # Is `self` starts and ends its line?
464 private fun is_inline
: Bool do
465 return self == first_real_token_in_line
and self == last_real_token_in_line
468 # Is `self` an orphan line (blank before and after)?
469 private fun is_orphan
: Bool do
470 return prev_token
isa TEol and
471 (prev_token
.prev_token
isa TEol or prev_token
.prev_token
isa TComment) and
475 # Is `self` the first comment of a group of comment?
476 private fun is_first_in_group
: Bool do return not prev_token
isa TComment
478 # Is `self` the last comment of a group of comments?
479 private fun is_last_in_group
: Bool do return not next_token
isa TComment
483 redef fun accept_pretty_printer
(v
) do for comment
in n_comment
do v
.visit comment
484 redef fun is_inlinable
do return n_comment
.length
<= 1
489 redef class AAnnotations
490 redef fun accept_pretty_printer
(v
) do
493 if v
.can_inline
(self) then
495 for n_item
in n_items
do
497 if n_item
!= n_items
.last
then
501 if not was_inline
then
503 if v
.current_token
isa TKwend then v
.skip
508 for n_item
in n_items
do
512 if n_item
!= n_items
.last
then
524 redef fun is_inlinable
do
525 if not super then return false
526 for annot
in n_items
do if not annot
.is_inlinable
then return false
531 redef class AAnnotation
532 redef fun accept_pretty_printer
(v
) do
533 if n_visibility
!= null and not n_visibility
isa APublicVisibility then
542 redef class ATypeExpr
543 redef fun accept_pretty_printer
(v
) do v
.visit n_type
549 redef fun accept_pretty_printer
(v
) do
550 v
.catch_up start_token
553 if not n_imports
.is_empty
then
554 if v
.skip_empty
then v
.addn
556 for n_import
in n_imports
do
562 if not n_extern_code_blocks
.is_empty
then
565 for n_extern_block
in n_extern_code_blocks
do
566 v
.catch_up n_extern_block
567 v
.visit n_extern_block
569 if n_extern_block
!= n_extern_code_blocks
.last
then v
.addn
572 if not n_classdefs
.is_empty
then v
.addn
575 if not n_classdefs
.is_empty
then
576 if v
.skip_empty
then v
.addn
578 for n_classdef
in n_classdefs
do
579 v
.catch_up n_classdef
581 if n_classdef
!= n_classdefs
.last
then v
.addn
589 redef fun start_token
do
590 if n_moduledecl
!= null then return n_moduledecl
.first_token
591 if not n_imports
.is_empty
then return n_imports
.first
.first_token
592 if not n_classdefs
.is_empty
then return n_classdefs
.first
.first_token
596 redef fun is_inlinable
do return false
599 redef class AModuledecl
600 redef fun accept_pretty_printer
(v
) do
606 if n_annotations
!= null then
607 var annot_inline
= v
.can_inline
(n_annotations
)
608 v
.visit n_annotations
610 if not annot_inline
then
611 if v
.current_token
isa TKwend then
621 if v
.skip_empty
then v
.addn
625 redef class AModuleName
626 redef fun accept_pretty_printer
(v
) do
627 for path
in n_path
do
636 redef class ANoImport
637 redef fun accept_pretty_printer
(v
) do
642 if v
.skip_empty
then v
.addn
646 redef class AStdImport
647 redef fun accept_pretty_printer
(v
) do
648 if not n_visibility
isa APublicVisibility then
657 if v
.skip_empty
then v
.addn
663 redef class AClassdef
664 redef fun accept_pretty_printer
(v
) do
665 for n_propdef
in n_propdefs
do
668 if n_propdef
.n_doc
!= null or not v
.can_inline
(n_propdef
) then
669 if v
.skip_empty
and n_propdef
!= n_propdefs
.first
then v
.addn
671 if v
.skip_empty
and n_propdef
!= n_propdefs
.last
then v
.addn
679 redef class AStdClassdef
680 redef fun accept_pretty_printer
(v
) do
682 var can_inline
= v
.can_inline
(self)
684 if not n_visibility
isa APublicVisibility then
689 if n_kwredef
!= null then
698 if not n_formaldefs
.is_empty
then
700 v
.visit_list n_formaldefs
704 if n_extern_code_block
!= null then
706 v
.visit n_extern_code_block
712 if not n_propdefs
.is_empty
then
713 for n_superclass
in n_propdefs
do
720 if v
.skip_empty
then v
.addn
730 if v
.skip_empty
then v
.addn
734 redef fun is_inlinable
do
735 if not super then return false
736 # FIXME: repair pretty-printing one-liner classes
737 if n_propdefs
.length
> 0 then return false
738 #if n_propdefs.length == 1 and not n_propdefs.first isa ASuperPropdef then return false
739 if not collect_comments
.is_empty
then return false
743 redef fun start_token
do
744 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
745 if n_kwredef
!= null then return n_kwredef
746 return n_classkind
.first_token
750 redef class AAbstractClasskind
751 redef fun accept_pretty_printer
(v
) do
758 redef class AExternClasskind
759 redef fun accept_pretty_printer
(v
) do
766 redef class AFormaldef
767 redef fun accept_pretty_printer
(v
) do
770 if n_type
!= null then
779 redef fun accept_pretty_printer
(v
) do
780 if n_kwnullable
!= null then
787 if not n_types
.is_empty
then
798 redef fun accept_pretty_printer
(v
) do
802 if not n_visibility
isa nullable APublicVisibility then
807 if n_kwredef
!= null then
813 # Factorize annotations visit for all APropdef.
815 # Return true if annotations were inlined.
816 fun visit_annotations
(v
: PrettyPrinterVisitor, n_annotations
: nullable AAnnotations): Bool do
817 var res
= v
.can_inline
(n_annotations
)
818 if n_annotations
!= null then v
.visit n_annotations
822 # Factorize block visit for APropdefs.
824 # Were annotations printed inline? If so, we need to print the block differently.
825 fun visit_block
(v
: PrettyPrinterVisitor, n_block
: nullable AExpr, annot_inline
: Bool) do
826 # var can_inline = v.can_inline(n_block)
827 if n_block
== null then return
828 if n_annotations
!= null and not annot_inline
then
833 while not v
.current_token
isa TKwdo do v
.skip
835 var token
= v
.current_token
838 if token
isa TEol then
840 if not v
.can_inline
(n_block
) then
846 token
= v
.current_token
847 if token
isa TKwdo then break
849 if annot_inline
and do_inline
then v
.adds
852 if v
.can_inline
(n_block
) and do_inline
then
855 if n_block
isa ABlockExpr then
856 if n_block
.n_expr
.is_empty
then
857 v
.visit n_block
.n_kwend
859 v
.visit n_block
.n_expr
.first
860 v
.current_token
= n_block
.n_kwend
865 if v
.current_token
isa TKwend then v
.skip
876 if n_block
isa ABlockExpr then
877 n_block
.force_block
= true
879 v
.catch_up n_block
.n_kwend
888 if n_block
isa ABlockExpr then
889 v
.visit n_block
.n_kwend
897 redef fun start_token
do
898 if n_doc
== null then return super
899 return n_doc
.last_token
.next_token
903 redef class AAttrPropdef
904 redef fun accept_pretty_printer
(v
) do
910 if n_type
!= null then
916 if n_expr
!= null then
923 var annot_inline
= visit_annotations
(v
, n_annotations
)
924 visit_block
(v
, n_block
, annot_inline
)
929 redef fun first_token
do
930 if n_doc
!= null then return n_doc
.first_token
931 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
932 if n_kwredef
!= null then return n_kwredef
936 redef fun is_inlinable
do return true
939 redef class ATypePropdef
940 redef fun accept_pretty_printer
(v
) do
948 visit_annotations
(v
, n_annotations
)
953 redef fun is_inlinable
do return true
956 redef class AMethPropdef
957 redef fun accept_pretty_printer
(v
) do
958 # TODO: Handle extern annotations
960 var before
= v
.indent
962 if n_kwinit
!= null then v
.visit n_kwinit
963 if n_kwmeth
!= null then v
.visit n_kwmeth
964 if n_kwnew
!= null then v
.visit n_kwnew
966 if not n_methid
== null then
973 var annot_inline
= visit_annotations
(v
, n_annotations
)
975 if n_extern_calls
!= null or n_extern_code_block
!= null then
977 if n_extern_calls
!= null then v
.visit n_extern_calls
978 if n_extern_code_block
!= null then v
.visit n_extern_code_block
981 visit_block
(v
, n_block
, annot_inline
)
983 assert v
.indent
== before
987 # * block is empty or can be inlined
988 # * contains no comments
989 redef fun is_inlinable
do
990 if not super then return false
991 if n_annotations
!= null and not n_annotations
.is_inlinable
then return false
992 if n_block
!= null and not n_block
.is_inlinable
then return false
993 if n_extern_calls
!= null and not n_extern_calls
.is_inlinable
then return false
994 if n_extern_code_block
!= null and not n_extern_code_block
.is_inlinable
then return false
995 if not collect_comments
.is_empty
then return false
1000 redef class AMainMethPropdef
1001 redef fun accept_pretty_printer
(v
) do
1003 if v
.skip_empty
then v
.addn
1007 redef class ASuperPropdef
1008 redef fun accept_pretty_printer
(v
) do
1013 visit_annotations
(v
, n_annotations
)
1018 redef fun is_inlinable
do return true
1021 redef class ASignature
1022 redef fun accept_pretty_printer
(v
) do
1023 if not n_params
.is_empty
then
1025 v
.visit_list n_params
1029 if n_type
!= null then
1038 redef fun accept_pretty_printer
(v
) do
1041 if n_type
!= null then
1047 if n_dotdotdot
!= null then v
.visit n_dotdotdot
1053 redef class AExternCalls
1054 redef fun accept_pretty_printer
(v
) do
1055 var can_inline
= v
.can_inline
(self)
1060 v
.visit_list n_extern_calls
1066 v
.visit_list n_extern_calls
1073 redef class AFullPropExternCall
1074 redef fun accept_pretty_printer
(v
) do
1081 redef class ALocalPropExternCall
1082 redef fun accept_pretty_printer
(v
) do v
.visit n_methid
1085 redef class AInitPropExternCall
1086 redef fun accept_pretty_printer
(v
) do v
.visit n_type
1089 redef class ACastAsExternCall
1090 redef fun accept_pretty_printer
(v
) do
1100 redef class AAsNullableExternCall
1101 redef fun accept_pretty_printer
(v
) do
1106 v
.visit n_kwnullable
1110 redef class AAsNotNullableExternCall
1111 redef fun accept_pretty_printer
(v
) do
1118 v
.visit n_kwnullable
1122 redef class AExternCodeBlock
1123 redef fun accept_pretty_printer
(v
) do
1124 if n_in_language
!= null then
1125 v
.visit n_in_language
1129 v
.visit n_extern_code_segment
1132 redef fun is_inlinable
do
1133 if not super then return false
1134 return n_extern_code_segment
.is_inlinable
1138 redef class AInLanguage
1139 redef fun accept_pretty_printer
(v
) do
1146 redef class TExternCodeSegment
1147 redef fun accept_pretty_printer
(v
) do
1148 var can_inline
= v
.can_inline
(self)
1153 var text
= text
.substring
(2, text
.length
- 4)
1154 var lines
= text
.r_trim
.split
("\n")
1156 if text
.is_empty
then
1161 if not lines
.first
.trim
.is_empty
then
1169 for line
in lines
do
1178 v
.current_token
= next_token
1182 redef fun is_inlinable
do
1183 if not super then return false
1184 return location
.line_start
== location
.line_end
1190 redef class ABlockExpr
1191 redef fun accept_pretty_printer
(v
) do
1192 var before
= v
.indent
1193 var can_inline
= v
.can_inline
(self)
1195 if can_inline
and not n_expr
.is_empty
then
1196 v
.visit n_expr
.first
1199 for nexpr
in n_expr
do
1200 var expr_inline
= v
.can_inline
(nexpr
)
1201 if not expr_inline
and nexpr
!= n_expr
.first
then v
.addn
1207 if not expr_inline
and nexpr
!= n_expr
.last
then v
.addn
1211 assert v
.indent
== before
1214 redef fun is_inlinable
do
1215 if not super then return false
1216 if not collect_comments
.is_empty
then return false
1218 if not n_expr
.is_empty
then
1219 if n_expr
.length
> 1 then return false
1220 if not n_expr
.first
.is_inlinable
then return false
1228 redef fun accept_pretty_printer
(v
) do
1229 var before
= v
.indent
1230 var can_inline
= v
.can_inline
(self)
1234 if v
.can_inline
(n_expr
) then
1243 # skip comments before `then` token
1244 while not v
.current_token
isa TKwthen do v
.skip
1246 var n_else
= self.n_else
1250 if n_then
!= null then v
.visit n_then
1253 n_else
.force_inline
= true
1258 else if n_then
== null then
1261 v
.skip_to last_token
.last_real_token_in_line
1266 else if not v
.skip_empty
and n_then
!= null and
1267 n_then
.was_inline
and
1268 n_then
.location
.line_end
== location
.line_start
then
1269 v
.forcen
# Xymus fucking syntax
1275 if n_then
!= null then
1276 if n_then
isa ABlockExpr then
1277 n_then
.force_block
= true
1282 if n_then
.was_inline
then
1292 # FIXME: for some unknown reasons, has_else can be true even if
1293 # there is no `else` keyword but a `end` instead.
1294 if has_else
(v
) and v
.current_token
isa TKwelse then
1300 if n_else
isa AIfExpr then
1301 n_else
.force_block
= true
1313 if n_else
isa ABlockExpr then
1314 n_else
.force_block
= true
1319 if n_else
.was_inline
then
1326 if last_token
isa TKwend then
1327 v
.catch_up last_token
1338 if last_token
.location
>= v
.current_token
.location
then v
.catch_up last_token
1342 if v
.current_token
isa TKwend then v
.skip
1346 assert v
.indent
== before
1349 redef fun is_inlinable
do
1350 if not super then return false
1351 if n_then
!= null and not n_then
.is_inlinable
then return false
1352 var n_else
= self.n_else
1353 if (n_else
isa ABlockExpr and not n_else
.n_expr
.is_empty
) or
1354 (not n_else
isa ABlockExpr and n_else
!= null) then
1357 if not collect_comments
.is_empty
then return false
1361 # Does this `if` statement contains a `else` part?
1362 private fun has_else
(v
: PrettyPrinterVisitor): Bool do
1364 if n_else
== null then return false
1365 var n_kwelse
= collect_kwelse
1366 if n_kwelse
== null then return false
1368 if n_else
isa ABlockExpr then
1369 var comments
: Array[TComment]
1371 if n_then
== null then
1372 comments
= v
.collect_comments
(n_expr
.last_token
, n_else
.last_token
)
1374 comments
= v
.collect_comments
(n_then
.last_token
, n_else
.last_token
)
1377 if not comments
.is_empty
then return true
1378 return not n_else
.n_expr
.is_empty
1384 # Lookup for `else` token in `self`.
1385 private fun collect_kwelse
: nullable TKwelse do
1386 var token
= first_token
1388 while token
!= last_token
do
1389 if token
isa TKwelse then return token
1390 token
= token
.next_token
1397 # Used to factorize work on loops.
1398 private class ALoopHelper
1401 fun loop_block
: nullable ANode is abstract
1402 fun loop_label
: nullable ANode is abstract
1404 fun visit_loop_block
(v
: PrettyPrinterVisitor) do
1405 var n_block
= loop_block
1410 if n_block
isa ABlockExpr then
1411 n_block
.force_block
= true
1413 v
.catch_up n_block
.n_kwend
1416 v
.visit n_block
.n_kwend
1426 if loop_label
!= null then
1432 fun visit_loop_inline
(v
: PrettyPrinterVisitor) do
1433 var n_block
= loop_block
1436 if n_block
isa ABlockExpr then
1437 if n_block
.n_expr
.is_empty
then
1438 v
.visit n_block
.n_kwend
1440 v
.visit n_block
.n_expr
.first
1441 v
.current_token
= n_block
.n_kwend
1446 if v
.current_token
isa TKwend then v
.skip
1449 if loop_label
!= null then
1455 redef fun is_inlinable
do
1456 var n_block
= loop_block
1457 if not super then return false
1458 if n_block
isa ABlockExpr and not n_block
.is_inlinable
then return false
1459 if not collect_comments
.is_empty
then return false
1464 redef class ALoopExpr
1467 redef fun loop_block
do return n_block
1468 redef fun loop_label
do return n_label
1470 redef fun accept_pretty_printer
(v
) do
1471 var can_inline
= v
.can_inline
(self)
1473 if can_inline
then visit_loop_inline v
else visit_loop_block v
1477 redef class AWhileExpr
1480 redef fun loop_block
do return n_block
1481 redef fun loop_label
do return n_label
1483 redef fun accept_pretty_printer
(v
) do
1484 var can_inline
= v
.can_inline
(self)
1490 if can_inline
then visit_loop_inline v
else visit_loop_block v
1497 redef fun loop_block
do return n_block
1498 redef fun loop_label
do return n_label
1500 redef fun accept_pretty_printer
(v
) do
1501 var can_inline
= v
.can_inline
(self)
1503 if can_inline
then visit_loop_inline v
else visit_loop_block v
1507 redef class AForExpr
1510 redef fun loop_block
do return n_block
1511 redef fun loop_label
do return n_label
1513 redef fun accept_pretty_printer
(v
) do
1514 var can_inline
= v
.can_inline
(self)
1518 for n_id
in n_ids
do
1520 if n_id
!= n_ids
.last
then v
.add
", "
1529 if can_inline
then visit_loop_inline v
else visit_loop_block v
1533 redef class ABreakExpr
1534 redef fun accept_pretty_printer
(v
) do
1537 if n_expr
!= null then
1542 if n_label
!= null then
1548 redef fun is_inlinable
do return true
1551 redef class AContinueExpr
1552 redef fun accept_pretty_printer
(v
) do
1553 v
.visit n_kwcontinue
1555 if n_expr
!= null then
1560 if n_label
!= null then
1566 redef fun is_inlinable
do return true
1571 redef class ASendExpr
1572 redef fun is_inlinable
do return true
1575 redef class ACallExpr
1576 redef fun accept_pretty_printer
(v
) do
1577 var can_inline
= v
.can_inline
(self)
1580 if not n_expr
isa AImplicitSelfExpr and not can_inline
then
1587 if not n_args
.n_exprs
.is_empty
then
1588 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1590 if v
.current_token
isa TOpar then v
.skip
1591 v
.visit n_args
.n_exprs
.first
1592 if v
.current_token
isa TCpar then v
.skip
1594 v
.visit_args n_args
.n_exprs
1599 # Is the call alone on its line?
1600 fun is_stmt
: Bool do return parent
isa ABlockExpr
1603 redef class ACallAssignExpr
1604 redef fun accept_pretty_printer
(v
) do
1608 if not n_args
.n_exprs
.is_empty
then
1610 v
.visit_list n_args
.n_exprs
1621 redef class ACallReassignExpr
1622 redef fun accept_pretty_printer
(v
) do
1626 if not n_args
.n_exprs
.is_empty
then
1628 v
.visit_list n_args
.n_exprs
1639 redef class ABraExpr
1640 redef fun accept_pretty_printer
(v
) do
1643 if not n_args
.n_exprs
.is_empty
then
1645 v
.visit_list n_args
.n_exprs
1651 redef class ABraAssignExpr
1652 redef fun accept_pretty_printer
(v
) do
1655 if not n_args
.n_exprs
.is_empty
then
1657 v
.visit_list n_args
.n_exprs
1668 redef class ABraReassignExpr
1669 redef fun accept_pretty_printer
(v
) do
1672 if not n_args
.n_exprs
.is_empty
then
1674 v
.visit_list n_args
.n_exprs
1685 redef class AAssignMethid
1686 redef fun accept_pretty_printer
(v
) do
1692 redef class ABraMethid
1693 redef fun accept_pretty_printer
(v
) do
1699 redef class ABraassignMethid
1700 redef fun accept_pretty_printer
(v
) do
1707 redef class AInitExpr
1708 redef fun accept_pretty_printer
(v
) do
1709 if not n_expr
isa AImplicitSelfExpr then
1715 v
.visit_args n_args
.n_exprs
1719 redef class ANewExpr
1720 redef fun accept_pretty_printer
(v
) do
1721 var can_inline
= v
.can_inline
(self)
1726 if n_id
!= null then
1729 if not can_inline
then
1739 v
.visit_args n_args
.n_exprs
1742 redef fun is_inlinable
do return true
1747 redef class AAttrExpr
1748 redef fun accept_pretty_printer
(v
) do
1753 redef fun is_inlinable
do return true
1756 redef class AAttrAssignExpr
1757 redef fun accept_pretty_printer
(v
) do
1767 redef class AAttrReassignExpr
1768 redef fun accept_pretty_printer
(v
) do
1780 redef class AVardeclExpr
1781 redef fun accept_pretty_printer
(v
) do
1786 if n_type
!= null then
1792 if n_expr
!= null then
1800 redef fun is_inlinable
do return true
1803 redef class AVarAssignExpr
1804 redef fun accept_pretty_printer
(v
) do
1813 redef class AAssertExpr
1814 redef fun accept_pretty_printer
(v
) do
1815 var can_inline
= v
.can_inline
(self)
1818 if n_id
!= null then
1826 var n_else
= self.n_else
1828 if n_else
!= null then
1839 if n_else
isa ABlockExpr then
1840 n_else
.force_block
= true
1844 v
.visit n_else
.n_kwend
1857 redef fun is_inlinable
do
1858 if not super then return false
1859 if n_else
!= null and not n_else
.is_inlinable
then return false
1864 redef class AReturnExpr
1865 redef fun accept_pretty_printer
(v
) do
1868 if n_expr
!= null then
1875 redef class ASuperExpr
1876 redef fun accept_pretty_printer
(v
) do
1879 if not n_args
.n_exprs
.is_empty
then
1880 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1882 if v
.current_token
isa TOpar then v
.skip
1883 v
.visit n_args
.n_exprs
.first
1884 if v
.current_token
isa TCpar then v
.skip
1886 v
.visit_args n_args
.n_exprs
1891 # Is the call alone on its line?
1892 fun is_stmt
: Bool do return self.first_token
.is_starting_line
1894 redef fun is_inlinable
do return true
1897 redef class AOnceExpr
1898 redef fun accept_pretty_printer
(v
) do
1904 redef fun is_inlinable
do return true
1907 redef class AAbortExpr
1908 redef fun accept_pretty_printer
(v
) do v
.visit n_kwabort
1909 redef fun is_inlinable
do return true
1912 redef class ANotExpr
1913 redef fun accept_pretty_printer
(v
) do
1920 redef class AAsCastExpr
1921 redef fun accept_pretty_printer
(v
) do
1931 redef class AAsNotnullExpr
1932 redef fun accept_pretty_printer
(v
) do
1946 # Used to factorize work on Or, And, Implies and Binop expressions.
1947 private class ABinOpHelper
1950 fun bin_expr1
: AExpr is abstract
1951 fun bin_expr2
: AExpr is abstract
1954 fun bin_op
: String is abstract
1956 redef fun accept_pretty_printer
(v
) do
1957 var can_inline
= v
.can_inline
(self)
1959 if not can_inline
then
1960 if (self isa ABinopExpr and bin_expr1
isa ABinopExpr) or
1961 (self isa AAndExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr)) or
1962 (self isa AOrExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr))
1964 bin_expr1
.force_block
= true
1985 redef class AAndExpr
1988 redef fun bin_expr1
do return n_expr
1989 redef fun bin_expr2
do return n_expr2
1990 redef fun bin_op
do return "and"
1996 redef fun bin_expr1
do return n_expr
1997 redef fun bin_expr2
do return n_expr2
1998 redef fun bin_op
do return "or"
2001 redef class AImpliesExpr
2004 redef fun bin_expr1
do return n_expr
2005 redef fun bin_expr2
do return n_expr2
2006 redef fun bin_op
do return "implies"
2009 redef class ABinopExpr
2012 redef fun bin_expr1
do return n_expr
2013 redef fun bin_expr2
do return n_expr2
2014 redef fun bin_op
do return operator
2017 redef class AIsaExpr
2018 redef fun accept_pretty_printer
(v
) do
2027 redef class AOrElseExpr
2028 redef fun accept_pretty_printer
(v
) do
2038 redef fun is_inlinable
do return true
2043 redef class AUplusExpr
2044 redef fun accept_pretty_printer
(v
) do
2050 redef class AUminusExpr
2051 redef fun accept_pretty_printer
(v
) do
2057 redef class ANullExpr
2058 redef fun accept_pretty_printer
(v
) do v
.visit n_kwnull
2059 redef fun is_inlinable
do return true
2062 redef class AParExpr
2063 redef fun accept_pretty_printer
(v
) do
2070 redef class AArrayExpr
2071 redef fun accept_pretty_printer
(v
) do
2073 v
.visit_list n_exprs
2074 if n_type
!= null then
2083 redef class ACrangeExpr
2084 redef fun accept_pretty_printer
(v
) do
2093 redef class AOrangeExpr
2094 redef fun accept_pretty_printer
(v
) do
2105 redef class AStringFormExpr
2106 redef fun accept_pretty_printer
(v
) do
2107 if not v
.break_strings
then
2108 # n_string.force_inline = true
2112 if v
.can_inline
(self) then
2113 n_string
.force_inline
= true
2116 var text
= n_string
.text
2119 while i
< text
.length
do
2122 if v
.max_size
> 0 and v
.current_length
>= v
.max_size
and i
<= text
.length
- 3 then
2138 v
.current_token
= n_string
.next_token
2143 redef class ASuperstringExpr
2144 redef fun accept_pretty_printer
(v
) do
2145 for n_expr
in n_exprs
do
2146 if not v
.break_strings
then
2147 n_expr
.force_inline
= true
2153 redef fun must_be_inline
do
2154 if super then return true
2156 if not n_exprs
.is_empty
then
2157 var first
= n_exprs
.first
2158 return first
isa AStringFormExpr and first
.n_string
.text
.has_prefix
("\"\
"\"")