X-Git-Url: http://nitlanguage.org diff --git a/src/pretty.nit b/src/pretty.nit index 3b9f7ef..e4d848d 100644 --- a/src/pretty.nit +++ b/src/pretty.nit @@ -79,7 +79,7 @@ class PrettyPrinterVisitor 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 @@ -99,7 +99,20 @@ class PrettyPrinterVisitor 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 @@ -108,10 +121,11 @@ class PrettyPrinterVisitor # 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 @@ -145,12 +159,22 @@ class PrettyPrinterVisitor # 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 @@ -172,7 +196,7 @@ class PrettyPrinterVisitor 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 @@ -183,7 +207,7 @@ class PrettyPrinterVisitor 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. @@ -196,7 +220,8 @@ class PrettyPrinterVisitor 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 @@ -223,6 +248,14 @@ class PrettyPrinterVisitor 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 @@ -243,6 +276,18 @@ class PrettyPrinterVisitor 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 @@ -252,12 +297,15 @@ redef class ANodes[E] 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 @@ -308,6 +356,17 @@ redef class Token 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 @@ -344,7 +403,7 @@ redef class Prod 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 @@ -355,13 +414,13 @@ redef class TComment 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 @@ -370,7 +429,7 @@ redef class TComment v.addn v.addt super - v.addn + v.forcen v.addn return end @@ -379,13 +438,14 @@ redef class TComment 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`? @@ -434,7 +494,6 @@ redef class AAnnotations 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 @@ -443,21 +502,27 @@ redef class AAnnotations 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 @@ -469,16 +534,12 @@ end 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 @@ -494,7 +555,7 @@ redef class AModule 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 @@ -516,7 +577,7 @@ redef class AModule 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 @@ -561,7 +622,7 @@ redef class AModuledecl end v.finish_line - v.addn + if v.skip_empty then v.addn end end @@ -582,7 +643,7 @@ redef class ANoImport v.adds v.visit n_kwend v.finish_line - v.addn + if v.skip_empty then v.addn end end @@ -597,7 +658,7 @@ redef class AStdImport v.adds v.visit n_name v.finish_line - v.addn + if v.skip_empty then v.addn end end @@ -609,9 +670,9 @@ redef class AClassdef 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 @@ -652,29 +713,17 @@ redef class AStdClassdef 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 @@ -682,14 +731,15 @@ redef class AStdClassdef 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 @@ -746,14 +796,6 @@ redef class AType 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 @@ -761,7 +803,7 @@ 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 @@ -772,6 +814,90 @@ redef class APropdef 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 @@ -798,7 +924,8 @@ redef class AAttrPropdef 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 @@ -822,6 +949,7 @@ redef class ATypePropdef v.consume ":" v.adds v.visit n_type + visit_annotations(v, n_annotations) v.finish_line v.addn end @@ -834,7 +962,6 @@ redef class AMethPropdef # 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 @@ -847,72 +974,15 @@ redef class AMethPropdef 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 - end - - v.finish_line + visit_block(v, n_block, annot_inline) v.addn assert v.indent == before end @@ -934,8 +1004,22 @@ 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 @@ -979,9 +1063,10 @@ redef class AExternCalls 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 @@ -1078,7 +1163,7 @@ redef class TExternCodeSegment v.add "`\{" if not lines.first.trim.is_empty then - v.addn + v.forcen lines.first.l_trim v.indent += 1 v.addt @@ -1087,7 +1172,7 @@ redef class TExternCodeSegment for line in lines do v.add line.r_trim - v.addn + v.forcen end v.addt @@ -1155,7 +1240,7 @@ redef class AIfExpr v.adds else v.visit n_expr - v.addn + v.forcen v.addt end @@ -1177,11 +1262,18 @@ redef class AIfExpr 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 @@ -1191,14 +1283,19 @@ redef class AIfExpr 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 @@ -1210,7 +1307,11 @@ redef class AIfExpr 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 @@ -1219,7 +1320,11 @@ redef class AIfExpr 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 @@ -1477,8 +1582,7 @@ redef class ACallExpr 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 @@ -1491,14 +1595,7 @@ redef class ACallExpr 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 @@ -1619,12 +1716,7 @@ redef class AInitExpr 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 @@ -1639,19 +1731,16 @@ redef class ANewExpr 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 @@ -1749,19 +1838,18 @@ redef class AAssertExpr 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" @@ -1799,14 +1887,7 @@ redef class ASuperExpr 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 @@ -1896,9 +1977,10 @@ private class ABinOpHelper 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 @@ -1933,66 +2015,7 @@ redef class ABinopExpr 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 @@ -2021,9 +2044,9 @@ end # 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 @@ -2045,6 +2068,11 @@ redef class AArrayExpr 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 @@ -2073,9 +2101,13 @@ 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 @@ -2084,9 +2116,13 @@ redef class AStringFormExpr 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 @@ -2103,7 +2139,12 @@ end 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