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 no_inline
and n
.location
.line_start
!= n
.location
.line_end
then return false
125 if n
.must_be_inline
then return true
126 if n
.must_be_block
then return false
128 if max_size
> 0 and n
.collect_length
+ current_length
> max_size
then return false
129 # check block is inlinable
130 return n
.is_inlinable
133 # Collect all `TComment` between `from` and `to`.
134 fun collect_comments
(from
: nullable ANode, to
: nullable ANode): Array[TComment] do
135 var res
= new Array[TComment]
136 if from
isa Prod then from
= from
.first_token
137 if to
isa Prod then to
= to
.first_token
138 if from
== null or to
== null then return res
141 if from
isa TComment then res
.add from
142 from
= from
.as(Token).next_token
148 # Token under cursor.
150 # This is the next token to visit.
151 var current_token
: nullable Token = null
153 # Skip the `current_token`.
154 fun skip
do current_token
= current_token
.next_token
156 # Skip `current_token` until the end of line.
157 fun skip_line
do current_token
= current_token
.last_real_token_in_line
159 # Skip `current_token` until `target` is reached.
160 fun skip_to
(target
: nullable Token) do
161 if target
== null then return
162 while current_token
!= null and current_token
!= target
do skip
163 if current_token
== null then
164 target
.debug
("Looked for, but not found :(")
169 # Consume comments and end of lines if any
170 fun consume_comments
do
171 while current_token
isa TEol or current_token
isa TComment do visit current_token
174 # Visit `current_token`.
175 fun consume
(token
: String) do
177 if current_token
.text
== token
then else current_token
.debug
("Got `{current_token.text}`; expected `{token}`.")
181 # Is there token to visit between `current_token` and `target`?
182 fun need_catch_up
(target
: nullable Token): Bool do
183 if target
== null then return false
184 return current_token
!= target
187 # Visit all tokens between `current_token` and `target`.
188 fun catch_up
(target
: nullable ANode) do
189 if target
== null then return
190 if current_token
== null then return
192 if target
isa Token then
194 else if target
isa Prod then
195 token
= target
.first_token
.as(not null)
199 if current_token
.location
> token
.location
then return
200 while current_token
!= token
do visit current_token
203 # Visit all tokens between `current_token` and the end of line.
205 if current_token
isa TComment then
210 while current_token
isa TEol do visit
(current_token
)
213 # The template under construction.
214 private var tpl
: nullable Template = null
216 # Current indent level.
219 # Size of a tabulation in spaces.
223 # 0 (or negative) to disable.
224 var max_size
= 80 is writable
226 # Length of the current line.
227 var current_length
= 0
229 # Length of the previous line.
230 var previous_length
= 0
232 # Is the last line a blank line?
233 fun last_line_is_blank
: Bool do return previous_length
== 0
235 # Add `t` to current template.
236 fun add
(t
: String) do
237 if t
.is_empty
then return
238 while wait_addn
> 0 do
243 current_length
+= t
.length
248 if current_length
== 0 and last_line_is_blank
then return
249 previous_length
= current_length
251 if skip_empty
then wait_addn
+= 1
254 # Perform `addn` even if not `skip_empty`.
256 if current_length
== 0 and last_line_is_blank
then return
257 previous_length
= current_length
262 # End of line chars are stored until another char is added.
263 # This avoid empty files with only a '`\n`'.
264 private var wait_addn
= 0
266 # Add `'\t' * indent`.
267 fun addt
do add
"\t" * indent
272 # Visit explicit receiver, implicit self will be ignored.
273 fun visit_recv
(n_expr
: AExpr) do
274 if not n_expr
isa AImplicitSelfExpr then
280 # Do we break string literals that are too long?
281 var break_strings
= false is public
writable
283 # Do we force `do` to be on the same line as the method signature?
284 var inline_do
= false is public
writable
286 # Do we force the deletion of empty lines?
287 var skip_empty
= false is public
writable
289 # Disable automatic inlining.
290 var no_inline
= false is writable
293 # Base framework redefs
295 redef class ANodes[E
]
296 private fun accept_pretty_printer
(v
: PrettyPrinterVisitor) do
298 var e_can_inline
= v
.can_inline
(e
)
300 if v
.current_token
isa TComma then v
.skip
303 if not e_can_inline
then
320 # Start visit of `self` using a `PrettyPrinterVisitor`
321 private fun accept_pretty_printer
(v
: PrettyPrinterVisitor) is abstract
323 # Collect the length (in `Char`) of the node.
324 private fun collect_length
: Int is abstract
326 # Is `self` printable in one line?
327 private fun is_inlinable
: Bool do return true
329 # Force `self` to be rendered as a block.
330 private var force_block
= false
332 # Does `self` have to be rendered as a block?
333 private fun must_be_block
: Bool do return force_block
335 # Force `self` to be rendered as a line.
336 private var force_inline
= false
338 # Does `self` have be rendered as a line?
339 private fun must_be_inline
: Bool do
340 if parent
!= null and parent
.must_be_inline
then return true
344 # Does `self` was written in one line before transformation?
345 private fun was_inline
: Bool is abstract
349 redef fun accept_pretty_printer
(v
) do
351 v
.current_token
= next_token
354 redef fun collect_length
do return text
.length
355 redef fun is_inlinable
do return true
356 redef fun was_inline
do return true
360 redef fun accept_pretty_printer
(v
) do
365 v
.current_token
= next_token
371 redef fun accept_pretty_printer
(v
) do v
.visit first_token
373 # The token where the production really start (skipping ADoc).
374 private fun start_token
: nullable Token do return first_token
376 # Collect all `TComment` contained in the production
377 # (between `start_token` and `end_token`).
378 private fun collect_comments
: Array[TComment] do
379 var res
= new Array[TComment]
380 if start_token
== null or last_token
== null then return res
381 var token
= start_token
383 while token
!= last_token
do
384 if token
isa TComment then res
.add token
385 token
= token
.next_token
391 redef fun collect_length
do
393 if start_token
== null or last_token
== null then return res
394 var token
= start_token
396 while token
!= last_token
do
397 res
+= token
.text
.length
398 token
= token
.next_token
401 res
+= token
.text
.length
405 redef fun was_inline
do
406 return start_token
.location
.line_start
== last_token
.location
.line_end
413 redef fun accept_pretty_printer
(v
) do
424 if is_last_in_group
then v
.addn
438 if next_token
isa TComment and is_first_in_group
then v
.addn
442 var prev_token
= self.prev_token
443 if prev_token
isa TComment and prev_token
.is_inline
and is_last_in_group
then v
.addn
448 if not v
.skip_empty
then v
.forcen
451 # Is `self` part of an `ADoc`?
452 private fun is_adoc
: Bool do return parent
isa ADoc and parent
.parent
!= null
454 # Is `self` part of a licence?
455 private fun is_licence
: Bool do
456 var prev_token
= self.prev_token
458 if prev_token
== null then
460 else if prev_token
isa TComment then
461 return prev_token
.is_licence
467 # Is `self` starts and ends its line?
468 private fun is_inline
: Bool do
469 return self == first_real_token_in_line
and self == last_real_token_in_line
472 # Is `self` an orphan line (blank before and after)?
473 private fun is_orphan
: Bool do
474 return prev_token
isa TEol and
475 (prev_token
.prev_token
isa TEol or prev_token
.prev_token
isa TComment) and
479 # Is `self` the first comment of a group of comment?
480 private fun is_first_in_group
: Bool do return not prev_token
isa TComment
482 # Is `self` the last comment of a group of comments?
483 private fun is_last_in_group
: Bool do return not next_token
isa TComment
487 redef fun accept_pretty_printer
(v
) do for comment
in n_comment
do v
.visit comment
488 redef fun is_inlinable
do return n_comment
.length
<= 1
493 redef class AAnnotations
494 redef fun accept_pretty_printer
(v
) do
497 if v
.can_inline
(self) then
499 for n_item
in n_items
do
501 if n_item
!= n_items
.last
then
505 if not was_inline
then
507 if v
.current_token
isa TKwend then v
.skip
512 for n_item
in n_items
do
516 if n_item
!= n_items
.last
then
528 redef fun is_inlinable
do
529 if not super then return false
530 for annot
in n_items
do if not annot
.is_inlinable
then return false
535 redef class AAnnotation
536 redef fun accept_pretty_printer
(v
) do
537 if n_visibility
!= null and not n_visibility
isa APublicVisibility then
546 redef class ATypeExpr
547 redef fun accept_pretty_printer
(v
) do v
.visit n_type
553 redef fun accept_pretty_printer
(v
) do
554 v
.catch_up start_token
557 if not n_imports
.is_empty
then
558 if v
.skip_empty
then v
.addn
560 for n_import
in n_imports
do
566 if not n_extern_code_blocks
.is_empty
then
569 for n_extern_block
in n_extern_code_blocks
do
570 v
.catch_up n_extern_block
571 v
.visit n_extern_block
573 if n_extern_block
!= n_extern_code_blocks
.last
then v
.addn
576 if not n_classdefs
.is_empty
then v
.addn
579 if not n_classdefs
.is_empty
then
580 if v
.skip_empty
then v
.addn
582 for n_classdef
in n_classdefs
do
583 v
.catch_up n_classdef
585 if n_classdef
!= n_classdefs
.last
then v
.addn
593 redef fun start_token
do
594 if n_moduledecl
!= null then return n_moduledecl
.first_token
595 if not n_imports
.is_empty
then return n_imports
.first
.first_token
596 if not n_classdefs
.is_empty
then return n_classdefs
.first
.first_token
600 redef fun is_inlinable
do return false
603 redef class AModuledecl
604 redef fun accept_pretty_printer
(v
) do
610 if n_annotations
!= null then
611 var annot_inline
= v
.can_inline
(n_annotations
)
612 v
.visit n_annotations
614 if not annot_inline
then
615 if v
.current_token
isa TKwend then
625 if v
.skip_empty
then v
.addn
629 redef class AModuleName
630 redef fun accept_pretty_printer
(v
) do
631 for path
in n_path
do
640 redef class ANoImport
641 redef fun accept_pretty_printer
(v
) do
646 if v
.skip_empty
then v
.addn
650 redef class AStdImport
651 redef fun accept_pretty_printer
(v
) do
652 if not n_visibility
isa APublicVisibility then
661 if v
.skip_empty
then v
.addn
667 redef class AClassdef
668 redef fun accept_pretty_printer
(v
) do
669 for n_propdef
in n_propdefs
do
672 if n_propdef
.n_doc
!= null or not v
.can_inline
(n_propdef
) then
673 if v
.skip_empty
and n_propdef
!= n_propdefs
.first
then v
.addn
675 if v
.skip_empty
and n_propdef
!= n_propdefs
.last
then v
.addn
683 redef class AStdClassdef
684 redef fun accept_pretty_printer
(v
) do
686 var can_inline
= v
.can_inline
(self)
688 if not n_visibility
isa APublicVisibility then
693 if n_kwredef
!= null then
702 if not n_formaldefs
.is_empty
then
704 v
.visit_list n_formaldefs
708 if n_extern_code_block
!= null then
710 v
.visit n_extern_code_block
716 if not n_propdefs
.is_empty
then
717 for n_superclass
in n_propdefs
do
724 if v
.skip_empty
then v
.addn
734 if v
.skip_empty
then v
.addn
738 redef fun is_inlinable
do
739 if not super then return false
740 # FIXME: repair pretty-printing one-liner classes
741 if n_propdefs
.length
> 0 then return false
742 #if n_propdefs.length == 1 and not n_propdefs.first isa ASuperPropdef then return false
743 if not collect_comments
.is_empty
then return false
747 redef fun start_token
do
748 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
749 if n_kwredef
!= null then return n_kwredef
750 return n_classkind
.first_token
754 redef class AAbstractClasskind
755 redef fun accept_pretty_printer
(v
) do
762 redef class AExternClasskind
763 redef fun accept_pretty_printer
(v
) do
770 redef class AFormaldef
771 redef fun accept_pretty_printer
(v
) do
774 if n_type
!= null then
783 redef fun accept_pretty_printer
(v
) do
784 if n_kwnullable
!= null then
791 if not n_types
.is_empty
then
802 redef fun accept_pretty_printer
(v
) do
806 if not n_visibility
isa nullable APublicVisibility then
811 if n_kwredef
!= null then
817 # Factorize annotations visit for all APropdef.
819 # Return true if annotations were inlined.
820 fun visit_annotations
(v
: PrettyPrinterVisitor, n_annotations
: nullable AAnnotations): Bool do
821 var res
= v
.can_inline
(n_annotations
)
822 if n_annotations
!= null then v
.visit n_annotations
826 # Factorize block visit for APropdefs.
828 # Were annotations printed inline? If so, we need to print the block differently.
829 fun visit_block
(v
: PrettyPrinterVisitor, n_block
: nullable AExpr, annot_inline
: Bool) do
830 # var can_inline = v.can_inline(n_block)
831 if n_block
== null then return
832 if n_annotations
!= null and not annot_inline
then
837 while not v
.current_token
isa TKwdo do v
.skip
839 var token
= v
.current_token
842 if token
isa TEol then
844 if not v
.can_inline
(n_block
) then
850 token
= v
.current_token
851 if token
isa TKwdo then break
853 if annot_inline
and do_inline
then v
.adds
856 if v
.can_inline
(n_block
) and do_inline
then
859 if n_block
isa ABlockExpr then
860 if n_block
.n_expr
.is_empty
then
861 v
.visit n_block
.n_kwend
863 v
.visit n_block
.n_expr
.first
864 v
.current_token
= n_block
.n_kwend
869 if v
.current_token
isa TKwend then v
.skip
880 if n_block
isa ABlockExpr then
881 n_block
.force_block
= true
883 v
.catch_up n_block
.n_kwend
892 if n_block
isa ABlockExpr then
893 v
.visit n_block
.n_kwend
901 redef fun start_token
do
902 if n_doc
== null then return super
903 return n_doc
.last_token
.next_token
907 redef class AAttrPropdef
908 redef fun accept_pretty_printer
(v
) do
914 if n_type
!= null then
920 if n_expr
!= null then
927 var annot_inline
= visit_annotations
(v
, n_annotations
)
928 visit_block
(v
, n_block
, annot_inline
)
933 redef fun first_token
do
934 if n_doc
!= null then return n_doc
.first_token
935 if not n_visibility
isa APublicVisibility then return n_visibility
.first_token
936 if n_kwredef
!= null then return n_kwredef
940 redef fun is_inlinable
do return true
943 redef class ATypePropdef
944 redef fun accept_pretty_printer
(v
) do
952 visit_annotations
(v
, n_annotations
)
957 redef fun is_inlinable
do return true
960 redef class AMethPropdef
961 redef fun accept_pretty_printer
(v
) do
962 # TODO: Handle extern annotations
964 var before
= v
.indent
966 if n_kwinit
!= null then v
.visit n_kwinit
967 if n_kwmeth
!= null then v
.visit n_kwmeth
968 if n_kwnew
!= null then v
.visit n_kwnew
970 if not n_methid
== null then
977 var annot_inline
= visit_annotations
(v
, n_annotations
)
979 if n_extern_calls
!= null or n_extern_code_block
!= null then
981 if n_extern_calls
!= null then v
.visit n_extern_calls
982 if n_extern_code_block
!= null then v
.visit n_extern_code_block
985 visit_block
(v
, n_block
, annot_inline
)
987 assert v
.indent
== before
991 # * block is empty or can be inlined
992 # * contains no comments
993 redef fun is_inlinable
do
994 if not super then return false
995 if n_annotations
!= null and not n_annotations
.is_inlinable
then return false
996 if n_block
!= null and not n_block
.is_inlinable
then return false
997 if n_extern_calls
!= null and not n_extern_calls
.is_inlinable
then return false
998 if n_extern_code_block
!= null and not n_extern_code_block
.is_inlinable
then return false
999 if not collect_comments
.is_empty
then return false
1004 redef class AMainMethPropdef
1005 redef fun accept_pretty_printer
(v
) do
1007 if v
.skip_empty
then v
.addn
1011 redef class ASuperPropdef
1012 redef fun accept_pretty_printer
(v
) do
1017 visit_annotations
(v
, n_annotations
)
1022 redef fun is_inlinable
do return true
1025 redef class ASignature
1026 redef fun accept_pretty_printer
(v
) do
1027 if not n_params
.is_empty
then
1029 v
.visit_list n_params
1033 if n_type
!= null then
1042 redef fun accept_pretty_printer
(v
) do
1045 if n_type
!= null then
1051 if n_dotdotdot
!= null then v
.visit n_dotdotdot
1057 redef class AExternCalls
1058 redef fun accept_pretty_printer
(v
) do
1059 var can_inline
= v
.can_inline
(self)
1064 v
.visit_list n_extern_calls
1070 v
.visit_list n_extern_calls
1077 redef class AFullPropExternCall
1078 redef fun accept_pretty_printer
(v
) do
1085 redef class ALocalPropExternCall
1086 redef fun accept_pretty_printer
(v
) do v
.visit n_methid
1089 redef class AInitPropExternCall
1090 redef fun accept_pretty_printer
(v
) do v
.visit n_type
1093 redef class ACastAsExternCall
1094 redef fun accept_pretty_printer
(v
) do
1104 redef class AAsNullableExternCall
1105 redef fun accept_pretty_printer
(v
) do
1110 v
.visit n_kwnullable
1114 redef class AAsNotNullableExternCall
1115 redef fun accept_pretty_printer
(v
) do
1122 v
.visit n_kwnullable
1126 redef class AExternCodeBlock
1127 redef fun accept_pretty_printer
(v
) do
1128 if n_in_language
!= null then
1129 v
.visit n_in_language
1133 v
.visit n_extern_code_segment
1136 redef fun is_inlinable
do
1137 if not super then return false
1138 return n_extern_code_segment
.is_inlinable
1142 redef class AInLanguage
1143 redef fun accept_pretty_printer
(v
) do
1150 redef class TExternCodeSegment
1151 redef fun accept_pretty_printer
(v
) do
1152 var can_inline
= v
.can_inline
(self)
1157 var text
= text
.substring
(2, text
.length
- 4)
1158 var lines
= text
.r_trim
.split
("\n")
1160 if text
.is_empty
then
1165 if not lines
.first
.trim
.is_empty
then
1173 for line
in lines
do
1182 v
.current_token
= next_token
1186 redef fun is_inlinable
do
1187 if not super then return false
1188 return location
.line_start
== location
.line_end
1194 redef class ABlockExpr
1195 redef fun accept_pretty_printer
(v
) do
1196 var before
= v
.indent
1197 var can_inline
= v
.can_inline
(self)
1199 if can_inline
and not n_expr
.is_empty
then
1200 v
.visit n_expr
.first
1203 for nexpr
in n_expr
do
1204 var expr_inline
= v
.can_inline
(nexpr
)
1205 if not expr_inline
and nexpr
!= n_expr
.first
then v
.addn
1211 if not expr_inline
and nexpr
!= n_expr
.last
then v
.addn
1215 assert v
.indent
== before
1218 redef fun is_inlinable
do
1219 if not super then return false
1220 if not collect_comments
.is_empty
then return false
1222 if not n_expr
.is_empty
then
1223 if n_expr
.length
> 1 then return false
1224 if not n_expr
.first
.is_inlinable
then return false
1232 redef fun accept_pretty_printer
(v
) do
1233 var before
= v
.indent
1234 var can_inline
= v
.can_inline
(self)
1238 if v
.can_inline
(n_expr
) then
1247 # skip comments before `then` token
1248 while not v
.current_token
isa TKwthen do v
.skip
1250 var n_else
= self.n_else
1254 if n_then
!= null then v
.visit n_then
1257 n_else
.force_inline
= true
1262 else if n_then
== null then
1265 v
.skip_to last_token
.last_real_token_in_line
1270 else if not v
.skip_empty
and n_then
!= null and
1271 n_then
.was_inline
and
1272 n_then
.location
.line_end
== location
.line_start
then
1273 v
.forcen
# Xymus fucking syntax
1279 if n_then
!= null then
1280 if n_then
isa ABlockExpr then
1281 n_then
.force_block
= true
1286 if n_then
.was_inline
then
1296 # FIXME: for some unknown reasons, has_else can be true even if
1297 # there is no `else` keyword but a `end` instead.
1298 if has_else
(v
) and v
.current_token
isa TKwelse then
1304 if n_else
isa AIfExpr then
1305 n_else
.force_block
= true
1317 if n_else
isa ABlockExpr then
1318 n_else
.force_block
= true
1323 if n_else
.was_inline
then
1330 if last_token
isa TKwend then
1331 v
.catch_up last_token
1342 if last_token
.location
>= v
.current_token
.location
then v
.catch_up last_token
1346 if v
.current_token
isa TKwend then v
.skip
1350 assert v
.indent
== before
1353 redef fun is_inlinable
do
1354 if not super then return false
1355 if n_then
!= null and not n_then
.is_inlinable
then return false
1356 var n_else
= self.n_else
1357 if (n_else
isa ABlockExpr and not n_else
.n_expr
.is_empty
) or
1358 (not n_else
isa ABlockExpr and n_else
!= null) then
1361 if not collect_comments
.is_empty
then return false
1365 # Does this `if` statement contains a `else` part?
1366 private fun has_else
(v
: PrettyPrinterVisitor): Bool do
1368 if n_else
== null then return false
1369 var n_kwelse
= collect_kwelse
1370 if n_kwelse
== null then return false
1372 if n_else
isa ABlockExpr then
1373 var comments
: Array[TComment]
1375 if n_then
== null then
1376 comments
= v
.collect_comments
(n_expr
.last_token
, n_else
.last_token
)
1378 comments
= v
.collect_comments
(n_then
.last_token
, n_else
.last_token
)
1381 if not comments
.is_empty
then return true
1382 return not n_else
.n_expr
.is_empty
1388 # Lookup for `else` token in `self`.
1389 private fun collect_kwelse
: nullable TKwelse do
1390 var token
= first_token
1392 while token
!= last_token
do
1393 if token
isa TKwelse then return token
1394 token
= token
.next_token
1401 # Used to factorize work on loops.
1402 private class ALoopHelper
1405 fun loop_block
: nullable ANode is abstract
1406 fun loop_label
: nullable ANode is abstract
1408 fun visit_loop_block
(v
: PrettyPrinterVisitor) do
1409 var n_block
= loop_block
1414 if n_block
isa ABlockExpr then
1415 n_block
.force_block
= true
1417 v
.catch_up n_block
.n_kwend
1420 v
.visit n_block
.n_kwend
1430 if loop_label
!= null then
1436 fun visit_loop_inline
(v
: PrettyPrinterVisitor) do
1437 var n_block
= loop_block
1440 if n_block
isa ABlockExpr then
1441 if n_block
.n_expr
.is_empty
then
1442 v
.visit n_block
.n_kwend
1444 v
.visit n_block
.n_expr
.first
1445 v
.current_token
= n_block
.n_kwend
1450 if v
.current_token
isa TKwend then v
.skip
1453 if loop_label
!= null then
1459 redef fun is_inlinable
do
1460 var n_block
= loop_block
1461 if not super then return false
1462 if n_block
isa ABlockExpr and not n_block
.is_inlinable
then return false
1463 if not collect_comments
.is_empty
then return false
1468 redef class ALoopExpr
1471 redef fun loop_block
do return n_block
1472 redef fun loop_label
do return n_label
1474 redef fun accept_pretty_printer
(v
) do
1475 var can_inline
= v
.can_inline
(self)
1477 if can_inline
then visit_loop_inline v
else visit_loop_block v
1481 redef class AWhileExpr
1484 redef fun loop_block
do return n_block
1485 redef fun loop_label
do return n_label
1487 redef fun accept_pretty_printer
(v
) do
1488 var can_inline
= v
.can_inline
(self)
1494 if can_inline
then visit_loop_inline v
else visit_loop_block v
1501 redef fun loop_block
do return n_block
1502 redef fun loop_label
do return n_label
1504 redef fun accept_pretty_printer
(v
) do
1505 var can_inline
= v
.can_inline
(self)
1507 if can_inline
then visit_loop_inline v
else visit_loop_block v
1511 redef class AForExpr
1514 redef fun loop_block
do return n_block
1515 redef fun loop_label
do return n_label
1517 redef fun accept_pretty_printer
(v
) do
1518 var can_inline
= v
.can_inline
(self)
1522 for n_id
in n_ids
do
1524 if n_id
!= n_ids
.last
then v
.add
", "
1533 if can_inline
then visit_loop_inline v
else visit_loop_block v
1537 redef class ABreakExpr
1538 redef fun accept_pretty_printer
(v
) do
1541 if n_expr
!= null then
1546 if n_label
!= null then
1552 redef fun is_inlinable
do return true
1555 redef class AContinueExpr
1556 redef fun accept_pretty_printer
(v
) do
1557 v
.visit n_kwcontinue
1559 if n_expr
!= null then
1564 if n_label
!= null then
1570 redef fun is_inlinable
do return true
1575 redef class ASendExpr
1576 redef fun is_inlinable
do return true
1579 redef class ACallExpr
1580 redef fun accept_pretty_printer
(v
) do
1581 var can_inline
= v
.can_inline
(self)
1584 if not n_expr
isa AImplicitSelfExpr and not can_inline
then
1591 if not n_args
.n_exprs
.is_empty
then
1592 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1594 if v
.current_token
isa TOpar then v
.skip
1595 v
.visit n_args
.n_exprs
.first
1596 if v
.current_token
isa TCpar then v
.skip
1598 v
.visit_args n_args
.n_exprs
1603 # Is the call alone on its line?
1604 fun is_stmt
: Bool do return parent
isa ABlockExpr
1607 redef class ACallAssignExpr
1608 redef fun accept_pretty_printer
(v
) do
1612 if not n_args
.n_exprs
.is_empty
then
1614 v
.visit_list n_args
.n_exprs
1625 redef class ACallReassignExpr
1626 redef fun accept_pretty_printer
(v
) do
1630 if not n_args
.n_exprs
.is_empty
then
1632 v
.visit_list n_args
.n_exprs
1643 redef class ABraExpr
1644 redef fun accept_pretty_printer
(v
) do
1647 if not n_args
.n_exprs
.is_empty
then
1649 v
.visit_list n_args
.n_exprs
1655 redef class ABraAssignExpr
1656 redef fun accept_pretty_printer
(v
) do
1659 if not n_args
.n_exprs
.is_empty
then
1661 v
.visit_list n_args
.n_exprs
1672 redef class ABraReassignExpr
1673 redef fun accept_pretty_printer
(v
) do
1676 if not n_args
.n_exprs
.is_empty
then
1678 v
.visit_list n_args
.n_exprs
1689 redef class AAssignMethid
1690 redef fun accept_pretty_printer
(v
) do
1696 redef class ABraMethid
1697 redef fun accept_pretty_printer
(v
) do
1703 redef class ABraassignMethid
1704 redef fun accept_pretty_printer
(v
) do
1711 redef class AInitExpr
1712 redef fun accept_pretty_printer
(v
) do
1713 if not n_expr
isa AImplicitSelfExpr then
1719 v
.visit_args n_args
.n_exprs
1723 redef class ANewExpr
1724 redef fun accept_pretty_printer
(v
) do
1725 var can_inline
= v
.can_inline
(self)
1730 if n_qid
!= null then
1733 if not can_inline
then
1743 v
.visit_args n_args
.n_exprs
1746 redef fun is_inlinable
do return true
1751 redef class AAttrExpr
1752 redef fun accept_pretty_printer
(v
) do
1757 redef fun is_inlinable
do return true
1760 redef class AAttrAssignExpr
1761 redef fun accept_pretty_printer
(v
) do
1771 redef class AAttrReassignExpr
1772 redef fun accept_pretty_printer
(v
) do
1784 redef class AVardeclExpr
1785 redef fun accept_pretty_printer
(v
) do
1790 if n_type
!= null then
1796 if n_expr
!= null then
1804 redef fun is_inlinable
do return true
1807 redef class AVarAssignExpr
1808 redef fun accept_pretty_printer
(v
) do
1817 redef class AAssertExpr
1818 redef fun accept_pretty_printer
(v
) do
1819 var can_inline
= v
.can_inline
(self)
1822 if n_id
!= null then
1830 var n_else
= self.n_else
1832 if n_else
!= null then
1843 if n_else
isa ABlockExpr then
1844 n_else
.force_block
= true
1848 v
.visit n_else
.n_kwend
1861 redef fun is_inlinable
do
1862 if not super then return false
1863 if n_else
!= null and not n_else
.is_inlinable
then return false
1868 redef class AReturnExpr
1869 redef fun accept_pretty_printer
(v
) do
1872 if n_expr
!= null then
1879 redef class ASuperExpr
1880 redef fun accept_pretty_printer
(v
) do
1883 if not n_args
.n_exprs
.is_empty
then
1884 if is_stmt
and n_args
.n_exprs
.length
== 1 then
1886 if v
.current_token
isa TOpar then v
.skip
1887 v
.visit n_args
.n_exprs
.first
1888 if v
.current_token
isa TCpar then v
.skip
1890 v
.visit_args n_args
.n_exprs
1895 # Is the call alone on its line?
1896 fun is_stmt
: Bool do return self.first_token
.is_starting_line
1898 redef fun is_inlinable
do return true
1901 redef class AOnceExpr
1902 redef fun accept_pretty_printer
(v
) do
1908 redef fun is_inlinable
do return true
1911 redef class AAbortExpr
1912 redef fun accept_pretty_printer
(v
) do v
.visit n_kwabort
1913 redef fun is_inlinable
do return true
1916 redef class ANotExpr
1917 redef fun accept_pretty_printer
(v
) do
1924 redef class AAsCastExpr
1925 redef fun accept_pretty_printer
(v
) do
1935 redef class AAsNotnullExpr
1936 redef fun accept_pretty_printer
(v
) do
1950 # Used to factorize work on Or, And, Implies and Binop expressions.
1951 private class ABinOpHelper
1954 fun bin_expr1
: AExpr is abstract
1955 fun bin_expr2
: AExpr is abstract
1958 fun bin_op
: String is abstract
1960 redef fun accept_pretty_printer
(v
) do
1961 var can_inline
= v
.can_inline
(self)
1963 if not can_inline
then
1964 if (self isa ABinopExpr and bin_expr1
isa ABinopExpr) or
1965 (self isa AAndExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr)) or
1966 (self isa AOrExpr and (bin_expr1
isa AAndExpr or bin_expr1
isa AOrExpr))
1968 bin_expr1
.force_block
= true
1989 redef class AAndExpr
1992 redef fun bin_expr1
do return n_expr
1993 redef fun bin_expr2
do return n_expr2
1994 redef fun bin_op
do return "and"
2000 redef fun bin_expr1
do return n_expr
2001 redef fun bin_expr2
do return n_expr2
2002 redef fun bin_op
do return "or"
2005 redef class AImpliesExpr
2008 redef fun bin_expr1
do return n_expr
2009 redef fun bin_expr2
do return n_expr2
2010 redef fun bin_op
do return "implies"
2013 redef class ABinopExpr
2016 redef fun bin_expr1
do return n_expr
2017 redef fun bin_expr2
do return n_expr2
2018 redef fun bin_op
do return operator
2021 redef class AIsaExpr
2022 redef fun accept_pretty_printer
(v
) do
2031 redef class AOrElseExpr
2032 redef fun accept_pretty_printer
(v
) do
2042 redef fun is_inlinable
do return true
2047 redef class AUnaryopExpr
2048 redef fun accept_pretty_printer
(v
) do
2054 redef class ANullExpr
2055 redef fun accept_pretty_printer
(v
) do v
.visit n_kwnull
2056 redef fun is_inlinable
do return true
2059 redef class AParExpr
2060 redef fun accept_pretty_printer
(v
) do
2067 redef class AArrayExpr
2068 redef fun accept_pretty_printer
(v
) do
2070 v
.visit_list n_exprs
2071 if n_type
!= null then
2080 redef class ACrangeExpr
2081 redef fun accept_pretty_printer
(v
) do
2090 redef class AOrangeExpr
2091 redef fun accept_pretty_printer
(v
) do
2102 redef class AStringFormExpr
2103 redef fun accept_pretty_printer
(v
) do
2104 if not v
.break_strings
then
2105 # n_string.force_inline = true
2109 if v
.can_inline
(self) then
2110 n_string
.force_inline
= true
2113 var text
= n_string
.text
2116 while i
< text
.length
do
2119 if v
.max_size
> 0 and v
.current_length
>= v
.max_size
and i
<= text
.length
- 3 then
2135 v
.current_token
= n_string
.next_token
2140 redef class ASuperstringExpr
2141 redef fun accept_pretty_printer
(v
) do
2142 for n_expr
in n_exprs
do
2143 if not v
.break_strings
then
2144 n_expr
.force_inline
= true
2150 redef fun must_be_inline
do
2151 if super then return true
2153 if not n_exprs
.is_empty
then
2154 var first
= n_exprs
.first
2155 return first
isa AStringFormExpr and first
.n_string
.text
.has_prefix
("\"\
"\"")