current_token = nmodule.location.file.first_token
visit nmodule
catch_up nmodule.location.file.last_token
- tpl.add "\n"
+ if skip_empty then tpl.add "\n"
return tpl.as(not null)
end
n.accept_pretty_printer self
end
- # Visit a list of `Anode`.
+ # Visit a list of arguments `ANode` with optional parentheses
+ fun visit_args(n: nullable ANodes[ANode]) do
+ if n == null or n.is_empty then return
+ if current_token isa TOpar then
+ consume "("
+ else
+ adds
+ end
+
+ visit_list n
+ if current_token isa TCpar then consume ")"
+ end
+
+ # Visit a list of `ANode`.
fun visit_list(n: nullable ANodes[ANode]) do
if n == null then return
n.accept_pretty_printer self
# Is the node inlinable and can fit on the line.
fun can_inline(n: nullable ANode): Bool do
if n == null then return true
+ if no_inline and n.location.line_start != n.location.line_end then return false
if n.must_be_inline then return true
if n.must_be_block then return false
# check length
- if n.collect_length + current_length > max_size then return false
+ if max_size > 0 and n.collect_length + current_length > max_size then return false
# check block is inlinable
return n.is_inlinable
end
# Skip `current_token` until `target` is reached.
fun skip_to(target: nullable Token) do
if target == null then return
- while current_token != target do skip
+ while current_token != null and current_token != target do skip
+ if current_token == null then
+ target.debug("Looked for, but not found :(")
+ abort
+ end
+ end
+
+ # Consume comments and end of lines if any
+ fun consume_comments do
+ while current_token isa TEol or current_token isa TComment do visit current_token
end
# Visit `current_token`.
fun consume(token: String) do
- assert current_token.text == token
+ consume_comments
+ if current_token.text == token then else current_token.debug("Got `{current_token.text}`; expected `{token}`.")
visit current_token
end
else
abort
end
- assert current_token.location <= token.location
+ if current_token.location > token.location then return
while current_token != token do visit current_token
end
visit current_token
end
- while current_token isa TEol do skip
+ while current_token isa TEol do visit(current_token)
end
# The template under construction.
var tab_size = 8
# Max line size.
- var max_size = 80
+ # 0 (or negative) to disable.
+ var max_size = 80 is writable
# Length of the current line.
var current_length = 0
if current_length == 0 and last_line_is_blank then return
previous_length = current_length
current_length = 0
+ if skip_empty then wait_addn += 1
+ end
+
+ # Perform `addn` even if not `skip_empty`.
+ fun forcen do
+ if current_length == 0 and last_line_is_blank then return
+ previous_length = current_length
+ current_length = 0
wait_addn += 1
end
consume "."
end
end
+
+ # Do we break string literals that are too long?
+ var break_strings = false is public writable
+
+ # Do we force `do` to be on the same line as the method signature?
+ var inline_do = false is public writable
+
+ # Do we force the deletion of empty lines?
+ var skip_empty = false is public writable
+
+ # Disable automatic inlining.
+ var no_inline = false is writable
end
# Base framework redefs
for e in self do
var e_can_inline = v.can_inline(e)
+ if v.current_token isa TComma then v.skip
+
if e != first then
if not e_can_inline then
v.add ","
- v.addn
- v.addt
+ v.forcen
+ v.indent += 1
v.addt
+ v.indent -= 1
else
v.add ", "
end
redef fun was_inline do return true
end
+redef class TEol
+ redef fun accept_pretty_printer(v) do
+ if v.skip_empty then
+ super
+ else
+ v.add text
+ v.current_token = next_token
+ end
+ end
+end
+
redef class Prod
redef fun accept_pretty_printer(v) do v.visit first_token
end
redef fun was_inline do
- return first_token.location.line_start == last_token.location.line_end
+ return start_token.location.line_start == last_token.location.line_end
end
end
if is_adoc then
v.addt
super
- v.addn
+ v.forcen
return
end
if is_licence then
super
- v.addn
+ v.forcen
if is_last_in_group then v.addn
return
end
v.addn
v.addt
super
- v.addn
+ v.forcen
v.addn
return
end
if next_token isa TComment and is_first_in_group then v.addn
v.addt
super
- v.addn
+ v.forcen
var prev_token = self.prev_token
if prev_token isa TComment and prev_token.is_inline and is_last_in_group then v.addn
return
end
super
+ if not v.skip_empty then v.forcen
end
# Is `self` part of an `ADoc`?
redef fun accept_pretty_printer(v) do
v.adds
v.consume "is"
-
if v.can_inline(self) then
v.adds
for n_item in n_items do
v.add ", "
end
end
- v.finish_line
- else if n_items.length > 1 then
- v.addn
+ if not was_inline then
+ v.finish_line
+ if v.current_token isa TKwend then v.skip
+ end
+ else
+ v.forcen
v.indent += 1
-
for n_item in n_items do
v.addt
v.visit n_item
v.finish_line
- v.addn
+ if n_item != n_items.last then
+ if was_inline then
+ v.forcen
+ else
+ v.addn
+ end
+ end
end
-
v.indent -= 1
end
- if not was_inline and v.current_token isa TKwend then v.skip
end
redef fun is_inlinable do
redef class AAnnotation
redef fun accept_pretty_printer(v) do
- v.visit n_atid
- if not n_args.is_empty then
- if n_opar == null then
- v.adds
- else
- v.visit n_opar
- end
- v.visit_list n_args
- v.visit n_cpar
+ if n_visibility != null and not n_visibility isa APublicVisibility then
+ v.visit n_visibility
+ v.adds
end
+ v.visit n_atid
+ v.visit_args n_args
end
end
v.visit n_moduledecl
if not n_imports.is_empty then
- v.addn
+ if v.skip_empty then v.addn
for n_import in n_imports do
v.catch_up n_import
end
if not n_classdefs.is_empty then
- v.addn
+ if v.skip_empty then v.addn
for n_classdef in n_classdefs do
v.catch_up n_classdef
end
v.finish_line
- v.addn
+ if v.skip_empty then v.addn
end
end
v.adds
v.visit n_kwend
v.finish_line
- v.addn
+ if v.skip_empty then v.addn
end
end
v.adds
v.visit n_name
v.finish_line
- v.addn
+ if v.skip_empty then v.addn
end
end
v.catch_up n_propdef
if n_propdef.n_doc != null or not v.can_inline(n_propdef) then
- if n_propdef != n_propdefs.first then v.addn
+ if v.skip_empty and n_propdef != n_propdefs.first then v.addn
v.visit n_propdef
- if n_propdef != n_propdefs.last then v.addn
+ if v.skip_empty and n_propdef != n_propdefs.last then v.addn
else
v.visit n_propdef
end
if can_inline then
v.adds
- if not n_superclasses.is_empty then
- for n_superclass in n_superclasses do
+ if not n_propdefs.is_empty then
+ for n_superclass in n_propdefs do
v.visit n_superclass
v.adds
end
end
else
v.finish_line
- v.addn
+ if v.skip_empty then v.addn
v.indent += 1
- for n_superclass in n_superclasses do
- v.catch_up n_superclass
- v.addt
- v.visit n_superclass
- v.finish_line
- v.addn
- end
-
- if not n_superclasses.is_empty and not n_propdefs.is_empty then
- v.addn
- end
-
super
v.catch_up n_kwend
v.indent -= 1
v.visit n_kwend
v.finish_line
- v.addn
+ if v.skip_empty then v.addn
assert v.indent == 0
end
redef fun is_inlinable do
if not super then return false
- if not n_propdefs.is_empty then return false
- if n_superclasses.length > 1 then return false
+ # FIXME: repair pretty-printing one-liner classes
+ if n_propdefs.length > 0 then return false
+ #if n_propdefs.length == 1 and not n_propdefs.first isa ASuperPropdef then return false
if not collect_comments.is_empty then return false
return true
end
end
end
-redef class ASuperclass
- redef fun accept_pretty_printer(v) do
- v.visit n_kwsuper
- v.adds
- v.visit n_type
- end
-end
-
# Properties
redef class APropdef
v.visit n_doc
v.addt
- if not n_visibility isa APublicVisibility then
+ if not n_visibility isa nullable APublicVisibility then
v.visit n_visibility
v.adds
end
end
end
+ # Factorize annotations visit for all APropdef.
+ #
+ # Return true if annotations were inlined.
+ fun visit_annotations(v: PrettyPrinterVisitor, n_annotations: nullable AAnnotations): Bool do
+ var res = v.can_inline(n_annotations)
+ if n_annotations != null then v.visit n_annotations
+ return res
+ end
+
+ # Factorize block visit for APropdefs.
+ #
+ # Were annotations printed inline? If so, we need to print the block differently.
+ fun visit_block(v: PrettyPrinterVisitor, n_block: nullable AExpr, annot_inline: Bool) do
+ # var can_inline = v.can_inline(n_block)
+ if n_block == null then return
+ if n_annotations != null and not annot_inline then
+ v.forcen
+ v.addt
+ end
+ if v.inline_do then
+ while not v.current_token isa TKwdo do v.skip
+ end
+ var token = v.current_token
+ var do_inline = true
+ loop
+ if token isa TEol then
+ v.skip
+ if not v.can_inline(n_block) then
+ v.forcen
+ v.addt
+ do_inline = false
+ end
+ end
+ token = v.current_token
+ if token isa TKwdo then break
+ end
+ if annot_inline and do_inline then v.adds
+ v.consume "do"
+
+ if v.can_inline(n_block) and do_inline then
+ v.adds
+
+ if n_block isa ABlockExpr then
+ if n_block.n_expr.is_empty then
+ v.visit n_block.n_kwend
+ else
+ v.visit n_block.n_expr.first
+ v.current_token = n_block.n_kwend
+ v.skip
+ end
+ else
+ v.visit n_block
+ if v.current_token isa TKwend then v.skip
+ end
+ else
+ v.finish_line
+ if was_inline then
+ v.forcen
+ else
+ v.addn
+ end
+ v.indent += 1
+
+ if n_block isa ABlockExpr then
+ n_block.force_block = true
+ v.visit n_block
+ v.catch_up n_block.n_kwend
+ else
+ v.addt
+ v.visit n_block
+ v.forcen
+ end
+
+ v.indent -= 1
+ v.addt
+ if n_block isa ABlockExpr then
+ v.visit n_block.n_kwend
+ else
+ v.add "end"
+ end
+ end
+ v.finish_line
+ end
+
redef fun start_token do
if n_doc == null then return super
return n_doc.last_token.next_token
v.visit n_expr
end
- if n_annotations != null then v.visit n_annotations
+ var annot_inline = visit_annotations(v, n_annotations)
+ visit_block(v, n_block, annot_inline)
v.finish_line
v.addn
end
v.consume ":"
v.adds
v.visit n_type
+ visit_annotations(v, n_annotations)
v.finish_line
v.addn
end
# TODO: Handle extern annotations
var before = v.indent
- var can_inline = v.can_inline(self)
super
if n_kwinit != null then v.visit n_kwinit
if n_kwmeth != null then v.visit n_kwmeth
v.visit n_signature
- if n_annotations != null then
- v.visit n_annotations
- else
- v.adds
- end
+ var annot_inline = visit_annotations(v, n_annotations)
if n_extern_calls != null or n_extern_code_block != null then
- if n_annotations != null then v.adds
+ v.adds
if n_extern_calls != null then v.visit n_extern_calls
if n_extern_code_block != null then v.visit n_extern_code_block
end
- var n_block = self.n_block
-
- if n_block != null then
- while not v.current_token isa TKwdo do v.skip
- if n_annotations != null then
- if v.can_inline(n_annotations) then
- v.adds
- else
- v.addt
- end
- end
- v.consume "do"
-
- if can_inline then
- v.adds
-
- if n_block isa ABlockExpr then
- if n_block.n_expr.is_empty then
- v.visit n_block.n_kwend
- else
- v.visit n_block.n_expr.first
- v.current_token = n_block.n_kwend
- v.skip
- end
- else
- v.visit n_block
- if v.current_token isa TKwend then v.skip
- end
- else
- v.finish_line
- v.addn
- v.indent += 1
-
- if n_block isa ABlockExpr then
- n_block.force_block = true
- v.visit n_block
- v.catch_up n_block.n_kwend
- else
- v.addt
- v.visit n_block
- v.addn
- end
-
- v.indent -= 1
- v.addt
- if n_block isa ABlockExpr then
- v.visit n_block.n_kwend
- else
- v.add "end"
- end
- end
- v.finish_line
- end
-
+ visit_block(v, n_block, annot_inline)
v.addn
assert v.indent == before
end
redef class AMainMethPropdef
redef fun accept_pretty_printer(v) do
v.visit n_block
+ if v.skip_empty then v.addn
+ end
+end
+
+redef class ASuperPropdef
+ redef fun accept_pretty_printer(v) do
+ super
+ v.visit n_kwsuper
+ v.adds
+ v.visit n_type
+ visit_annotations(v, n_annotations)
+ v.finish_line
v.addn
end
+
+ redef fun is_inlinable do return true
end
redef class ASignature
v.adds
v.visit_list n_extern_calls
else
- v.addn
- v.addt
+ v.forcen
+ v.indent += 1
v.addt
+ v.indent -= 1
v.visit_list n_extern_calls
end
v.add "`\{"
if not lines.first.trim.is_empty then
- v.addn
+ v.forcen
lines.first.l_trim
v.indent += 1
v.addt
for line in lines do
v.add line.r_trim
- v.addn
+ v.forcen
end
v.addt
v.adds
else
v.visit n_expr
- v.addn
+ v.forcen
v.addt
end
else if n_then == null then
v.add "end"
end
-
v.skip_to last_token.last_real_token_in_line
else
v.finish_line
- v.addn
+ if was_inline then
+ v.forcen
+ else if not v.skip_empty and n_then != null and
+ n_then.was_inline and
+ n_then.location.line_end == location.line_start then
+ v.forcen # Xymus fucking syntax
+ else
+ v.addn
+ end
v.indent += 1
if n_then != null then
else
v.addt
v.visit n_then
- v.addn
+ if n_then.was_inline then
+ v.forcen
+ else
+ v.addn
+ end
end
end
- if has_else(v) then
- while not v.current_token isa TKwelse do
- v.consume v.current_token.text
- end
+ v.consume_comments
+
+ # FIXME: for some unknown reasons, has_else can be true even if
+ # there is no `else` keyword but a `end` instead.
+ if has_else(v) and v.current_token isa TKwelse then
v.indent -= 1
v.addt
v.visit n_else
else
v.finish_line
- v.addn
+ if was_inline then
+ v.forcen
+ else
+ v.addn
+ end
v.indent += 1
if n_else isa ABlockExpr then
else
v.addt
v.visit n_else
- v.addn
+ if n_else.was_inline then
+ v.forcen
+ else
+ v.addn
+ end
end
if last_token isa TKwend then
v.visit_recv n_expr
if not n_expr isa AImplicitSelfExpr and not can_inline then
- v.addn
- v.addt
+ v.forcen
v.addt
end
v.visit n_args.n_exprs.first
if v.current_token isa TCpar then v.skip
else
- if v.current_token isa TOpar then
- v.consume "("
- else
- v.adds
- end
-
- v.visit_list n_args.n_exprs
- if v.current_token isa TCpar then v.consume ")"
+ v.visit_args n_args.n_exprs
end
end
end
end
v.visit n_kwinit
-
- if not n_args.n_exprs.is_empty then
- v.consume "("
- v.visit_list n_args.n_exprs
- v.consume ")"
- end
+ v.visit_args n_args.n_exprs
end
end
v.consume "."
if not can_inline then
- v.addn
- v.addt
+ v.forcen
+ v.indent += 1
v.addt
+ v.indent -= 1
end
v.visit n_id
end
- if not n_args.n_exprs.is_empty then
- v.consume "("
- v.visit_list n_args.n_exprs
- v.consume ")"
- end
+ v.visit_args n_args.n_exprs
end
redef fun is_inlinable do return true
v.visit n_else
else
v.addn
+ v.indent += 1
if n_else isa ABlockExpr then
- v.indent += 1
n_else.force_block = true
v.visit n_else
v.indent -= 1
v.addt
v.visit n_else.n_kwend
else
- v.indent += 1
v.addt
v.visit n_else
- v.addn
+ v.forcen
v.indent -= 1
v.addt
v.add "end"
v.visit n_args.n_exprs.first
if v.current_token isa TCpar then v.skip
else
- if v.current_token isa TOpar then
- v.consume "("
- else
- v.adds
- end
-
- v.visit_list n_args.n_exprs
- if v.current_token isa TCpar then v.consume ")"
+ v.visit_args n_args.n_exprs
end
end
end
v.adds
v.visit bin_expr2
else
- v.addn
- v.addt
+ v.forcen
+ v.indent += 1
v.addt
+ v.indent -= 1
v.visit bin_expr2
end
end
redef fun bin_expr1 do return n_expr
redef fun bin_expr2 do return n_expr2
-end
-
-redef class AEqExpr
- redef fun bin_op do return "=="
-end
-
-redef class AGeExpr
- redef fun bin_op do return ">="
-end
-
-redef class AGgExpr
- redef fun bin_op do return ">>"
-end
-
-redef class AGtExpr
- redef fun bin_op do return ">"
-end
-
-redef class ALeExpr
- redef fun bin_op do return "<="
-end
-
-redef class ALlExpr
- redef fun bin_op do return "<<"
-end
-
-redef class ALtExpr
- redef fun bin_op do return "<"
-end
-
-redef class AMinusExpr
- redef fun bin_op do return "-"
-end
-
-redef class ANeExpr
- redef fun bin_op do return "!="
-end
-
-redef class APercentExpr
- redef fun bin_op do return "%"
-end
-
-redef class APlusExpr
- redef fun bin_op do return "+"
-end
-
-redef class ASlashExpr
- redef fun bin_op do return "/"
-end
-
-redef class AStarExpr
- redef fun bin_op do return "*"
-end
-
-redef class AStarstarExpr
- redef fun bin_op do return "**"
-end
-
-redef class AStarshipExpr
- redef fun bin_op do return "<=>"
+ redef fun bin_op do return operator
end
redef class AIsaExpr
# Syntax
-redef class AUminusExpr
+redef class AUnaryopExpr
redef fun accept_pretty_printer(v) do
- v.consume "-"
+ v.visit n_op
v.visit n_expr
end
end
redef fun accept_pretty_printer(v) do
v.consume "["
v.visit_list n_exprs
+ if n_type != null then
+ v.consume ":"
+ v.adds
+ v.visit n_type
+ end
v.consume "]"
end
end
redef class AStringFormExpr
redef fun accept_pretty_printer(v) do
- var can_inline = v.can_inline(self)
-
- if can_inline then
+ if not v.break_strings then
+ # n_string.force_inline = true
+ v.visit n_string
+ return
+ end
+ if v.can_inline(self) then
+ n_string.force_inline = true
v.visit n_string
else
var text = n_string.text
while i < text.length do
v.add text[i].to_s
- if v.current_length >= v.max_size and i <= text.length - 3 then
+ if v.max_size > 0 and v.current_length >= v.max_size and i <= text.length - 3 then
v.add "\" +"
- v.addn
+ if was_inline then
+ v.forcen
+ else
+ v.addn
+ end
v.indent += 1
v.addt
v.indent -= 1
redef class ASuperstringExpr
redef fun accept_pretty_printer(v) do
- for n_expr in n_exprs do v.visit n_expr
+ for n_expr in n_exprs do
+ if not v.break_strings then
+ n_expr.force_inline = true
+ end
+ v.visit n_expr
+ end
end
redef fun must_be_inline do