Library used to pretty print Nit code.

Usage:

import parser_util
var tc = new ToolContext
var nmodule = tc.parse_something("class A\nfun   toto :  Int  do   return   5\nend")
var ppv = new PrettyPrinterVisitor
var pmodule = ppv.pretty(nmodule)
assert pmodule.write_to_string == """
class A
\tfun toto: Int do return 5
end"""

See nitpretty tool for more documentation.

Introduced classes

private class ABinOpHelper

nitc :: ABinOpHelper

Used to factorize work on Or, And, Implies and Binop expressions.
private class ALoopHelper

nitc :: ALoopHelper

Used to factorize work on loops.
class PrettyPrinterVisitor

nitc :: PrettyPrinterVisitor

The PrettyPrinterVisitor is used to visit a node and pretty print it.

Redefined classes

redef class AAbortExpr

nitc :: pretty $ AAbortExpr

An abort statement
redef class AAbstractClasskind

nitc :: pretty $ AAbstractClasskind

An abstract class modifier (abstract class)
redef class AAndExpr

nitc :: pretty $ AAndExpr

A and expression
redef class AAnnotation

nitc :: pretty $ AAnnotation

A single annotation
redef class AAnnotations

nitc :: pretty $ AAnnotations

A group of annotation on a node
redef class AArrayExpr

nitc :: pretty $ AArrayExpr

A literal array. eg. [x,y,z]
redef class AAsCastExpr

nitc :: pretty $ AAsCastExpr

A type cast. eg x.as(T)
redef class AAsNotNullableExternCall

nitc :: pretty $ AAsNotNullableExternCall

A single callback declaration on a cast to a non-nullable type
redef class AAsNotnullExpr

nitc :: pretty $ AAsNotnullExpr

A as-not-null cast. eg x.as(not null)
redef class AAsNullableExternCall

nitc :: pretty $ AAsNullableExternCall

A single callback declaration on a cast to a nullable type
redef class AAssertExpr

nitc :: pretty $ AAssertExpr

An assert statement
redef class AAssignMethid

nitc :: pretty $ AAssignMethid

A setter method name with a simple identifier (with a =)
redef class AAttrAssignExpr

nitc :: pretty $ AAttrAssignExpr

The assignment of an attribute. eg x._a=y
redef class AAttrExpr

nitc :: pretty $ AAttrExpr

The read of an attribute. eg x._a
redef class AAttrPropdef

nitc :: pretty $ AAttrPropdef

A definition of an attribute
redef class AAttrReassignExpr

nitc :: pretty $ AAttrReassignExpr

A complex attribute assignment. eg x._a+=y
redef abstract class ABinopExpr

nitc :: pretty $ ABinopExpr

A binary operation on a method
redef class ABlockExpr

nitc :: pretty $ ABlockExpr

A sequence of AExpr (usually statements)
redef class ABraAssignExpr

nitc :: pretty $ ABraAssignExpr

A setter call of the bracket operator. eg x[y,z]=t
redef class ABraExpr

nitc :: pretty $ ABraExpr

A call of the brackets operator. eg x[y,z]
redef class ABraMethid

nitc :: pretty $ ABraMethid

A method name []
redef class ABraReassignExpr

nitc :: pretty $ ABraReassignExpr

A complex setter call of the bracket operator. eg x[y,z]+=t
redef class ABraassignMethid

nitc :: pretty $ ABraassignMethid

A method name []=
redef class ABreakExpr

nitc :: pretty $ ABreakExpr

A break statement.
redef class ACallAssignExpr

nitc :: pretty $ ACallAssignExpr

A setter call with a standard method-name and any number of arguments. eg x.m(y)=z. OR just a simple assignment.
redef class ACallExpr

nitc :: pretty $ ACallExpr

A call with a standard method-name and any number of arguments. eg x.m(y). OR just a simple id
redef class ACallReassignExpr

nitc :: pretty $ ACallReassignExpr

A complex setter call with a standard method-name and any number of arguments. eg x.m(y)+=z. OR just a simple complex assignment.
redef class ACastAsExternCall

nitc :: pretty $ ACastAsExternCall

A single callback declaration on a cast to a given type
redef abstract class AClassdef

nitc :: pretty $ AClassdef

A class definition
redef class AContinueExpr

nitc :: pretty $ AContinueExpr

A continue statement
redef class ACrangeExpr

nitc :: pretty $ ACrangeExpr

A closed literal range. eg [x..y]
redef class ADoExpr

nitc :: pretty $ ADoExpr

A do statement
redef class ADoc

nitc :: pretty $ ADoc

A documentation of a definition
redef class AExternCalls

nitc :: pretty $ AExternCalls

Declaration of callbacks for extern methods
redef class AExternClasskind

nitc :: pretty $ AExternClasskind

An extern class modifier (extern class)
redef class AExternCodeBlock

nitc :: pretty $ AExternCodeBlock

An full extern block
redef class AForExpr

nitc :: pretty $ AForExpr

A for statement
redef class AForGroup

nitc :: pretty $ AForGroup

A collection iterated by a for, its automatic variables and its implicit iterator.
redef class AFormaldef

nitc :: pretty $ AFormaldef

The definition of a formal generic parameter type. eg X: Y
redef class AFullPropExternCall

nitc :: pretty $ AFullPropExternCall

A single callback declaration on a method on an explicit receiver type.
redef class AIfExpr

nitc :: pretty $ AIfExpr

A if statement
redef class AImpliesExpr

nitc :: pretty $ AImpliesExpr

A implies expression
redef class AInLanguage

nitc :: pretty $ AInLanguage

A language declaration for an extern block
redef class AInitExpr

nitc :: pretty $ AInitExpr

A call to the init constructor.
redef class AInitPropExternCall

nitc :: pretty $ AInitPropExternCall

A single callback declaration on a method on a constructor
redef class AIsaExpr

nitc :: pretty $ AIsaExpr

A type-ckeck expression. eg x isa T
redef class ALocalPropExternCall

nitc :: pretty $ ALocalPropExternCall

A single callback declaration on a method on the current receiver
redef class ALoopExpr

nitc :: pretty $ ALoopExpr

A loop statement
redef class AMainMethPropdef

nitc :: pretty $ AMainMethPropdef

The implicit main method
redef class AMethPropdef

nitc :: pretty $ AMethPropdef

A definition of all kind of method (including constructors)
redef class AModule

nitc :: pretty $ AModule

The main node of a Nit source-file
redef class AModuleName

nitc :: pretty $ AModuleName

A possibly fully-qualified module identifier
redef class AModuledecl

nitc :: pretty $ AModuledecl

The declaration of the module with the documentation, name, and annotations
redef class ANewExpr

nitc :: pretty $ ANewExpr

An explicit instantiation. eg new T
redef class ANoImport

nitc :: pretty $ ANoImport

The special import clause of the kernel module. eg import end
redef abstract class ANode

nitc :: pretty $ ANode

Root of the AST class-hierarchy
redef class ANodes[E: ANode]

nitc :: pretty $ ANodes

A sequence of nodes
redef class ANotExpr

nitc :: pretty $ ANotExpr

A not expression
redef class ANullExpr

nitc :: pretty $ ANullExpr

A null literal constant
redef class AOnceExpr

nitc :: pretty $ AOnceExpr

A once expression. eg once x
redef class AOrElseExpr

nitc :: pretty $ AOrElseExpr

A or else expression
redef class AOrExpr

nitc :: pretty $ AOrExpr

A or expression
redef class AOrangeExpr

nitc :: pretty $ AOrangeExpr

An open literal range. eg [x..y[
redef class AParExpr

nitc :: pretty $ AParExpr

A simple parenthesis. eg (x)
redef class AParam

nitc :: pretty $ AParam

A parameter definition in a signature. eg x:X
redef abstract class APropdef

nitc :: pretty $ APropdef

The definition of a property
redef class AReturnExpr

nitc :: pretty $ AReturnExpr

A return statement. eg return x
redef abstract class ASendExpr

nitc :: pretty $ ASendExpr

A polymorphic invocation of a method
redef class ASignature

nitc :: pretty $ ASignature

A signature in a method definition. eg (x,y:X,z:Z):T
redef class AStdClassdef

nitc :: pretty $ AStdClassdef

A standard class definition with a name, superclasses and properties
redef class AStdImport

nitc :: pretty $ AStdImport

A standard import clause. eg import x
redef abstract class AStringFormExpr

nitc :: pretty $ AStringFormExpr

A string literal
redef class ASuperExpr

nitc :: pretty $ ASuperExpr

A call to super. OR a call of a super-constructor
redef class ASuperPropdef

nitc :: pretty $ ASuperPropdef

A super-class. eg super X
redef class ASuperstringExpr

nitc :: pretty $ ASuperstringExpr

A superstring literal. eg "a{x}b{y}c"
redef class AType

nitc :: pretty $ AType

A static type. eg nullable X[Y]
redef class ATypeExpr

nitc :: pretty $ ATypeExpr

A special expression that encapsulates a static type
redef class ATypePropdef

nitc :: pretty $ ATypePropdef

A definition of a virtual type
redef abstract class AUnaryopExpr

nitc :: pretty $ AUnaryopExpr

A unary operation on a method
redef class AVarAssignExpr

nitc :: pretty $ AVarAssignExpr

A local variable simple assignment access
redef class AVardeclExpr

nitc :: pretty $ AVardeclExpr

A declaration of a local variable. eg var x: X = y
redef class AWhileExpr

nitc :: pretty $ AWhileExpr

A while statement
redef abstract class Prod

nitc :: pretty $ Prod

Ancestor of all productions
redef class TComment

nitc :: pretty $ TComment

Token of a line of comments
redef class TEol

nitc :: pretty $ TEol

Token of end of line (basically \n)
redef class TExternCodeSegment

nitc :: pretty $ TExternCodeSegment

A extern code block
redef abstract class Token

nitc :: pretty $ Token

Ancestor of all tokens

All class definitions

redef class AAbortExpr

nitc :: pretty $ AAbortExpr

An abort statement
redef class AAbstractClasskind

nitc :: pretty $ AAbstractClasskind

An abstract class modifier (abstract class)
redef class AAndExpr

nitc :: pretty $ AAndExpr

A and expression
redef class AAnnotation

nitc :: pretty $ AAnnotation

A single annotation
redef class AAnnotations

nitc :: pretty $ AAnnotations

A group of annotation on a node
redef class AArrayExpr

nitc :: pretty $ AArrayExpr

A literal array. eg. [x,y,z]
redef class AAsCastExpr

nitc :: pretty $ AAsCastExpr

A type cast. eg x.as(T)
redef class AAsNotNullableExternCall

nitc :: pretty $ AAsNotNullableExternCall

A single callback declaration on a cast to a non-nullable type
redef class AAsNotnullExpr

nitc :: pretty $ AAsNotnullExpr

A as-not-null cast. eg x.as(not null)
redef class AAsNullableExternCall

nitc :: pretty $ AAsNullableExternCall

A single callback declaration on a cast to a nullable type
redef class AAssertExpr

nitc :: pretty $ AAssertExpr

An assert statement
redef class AAssignMethid

nitc :: pretty $ AAssignMethid

A setter method name with a simple identifier (with a =)
redef class AAttrAssignExpr

nitc :: pretty $ AAttrAssignExpr

The assignment of an attribute. eg x._a=y
redef class AAttrExpr

nitc :: pretty $ AAttrExpr

The read of an attribute. eg x._a
redef class AAttrPropdef

nitc :: pretty $ AAttrPropdef

A definition of an attribute
redef class AAttrReassignExpr

nitc :: pretty $ AAttrReassignExpr

A complex attribute assignment. eg x._a+=y
private class ABinOpHelper

nitc $ ABinOpHelper

Used to factorize work on Or, And, Implies and Binop expressions.
redef abstract class ABinopExpr

nitc :: pretty $ ABinopExpr

A binary operation on a method
redef class ABlockExpr

nitc :: pretty $ ABlockExpr

A sequence of AExpr (usually statements)
redef class ABraAssignExpr

nitc :: pretty $ ABraAssignExpr

A setter call of the bracket operator. eg x[y,z]=t
redef class ABraExpr

nitc :: pretty $ ABraExpr

A call of the brackets operator. eg x[y,z]
redef class ABraMethid

nitc :: pretty $ ABraMethid

A method name []
redef class ABraReassignExpr

nitc :: pretty $ ABraReassignExpr

A complex setter call of the bracket operator. eg x[y,z]+=t
redef class ABraassignMethid

nitc :: pretty $ ABraassignMethid

A method name []=
redef class ABreakExpr

nitc :: pretty $ ABreakExpr

A break statement.
redef class ACallAssignExpr

nitc :: pretty $ ACallAssignExpr

A setter call with a standard method-name and any number of arguments. eg x.m(y)=z. OR just a simple assignment.
redef class ACallExpr

nitc :: pretty $ ACallExpr

A call with a standard method-name and any number of arguments. eg x.m(y). OR just a simple id
redef class ACallReassignExpr

nitc :: pretty $ ACallReassignExpr

A complex setter call with a standard method-name and any number of arguments. eg x.m(y)+=z. OR just a simple complex assignment.
redef class ACastAsExternCall

nitc :: pretty $ ACastAsExternCall

A single callback declaration on a cast to a given type
redef abstract class AClassdef

nitc :: pretty $ AClassdef

A class definition
redef class AContinueExpr

nitc :: pretty $ AContinueExpr

A continue statement
redef class ACrangeExpr

nitc :: pretty $ ACrangeExpr

A closed literal range. eg [x..y]
redef class ADoExpr

nitc :: pretty $ ADoExpr

A do statement
redef class ADoc

nitc :: pretty $ ADoc

A documentation of a definition
redef class AExternCalls

nitc :: pretty $ AExternCalls

Declaration of callbacks for extern methods
redef class AExternClasskind

nitc :: pretty $ AExternClasskind

An extern class modifier (extern class)
redef class AExternCodeBlock

nitc :: pretty $ AExternCodeBlock

An full extern block
redef class AForExpr

nitc :: pretty $ AForExpr

A for statement
redef class AForGroup

nitc :: pretty $ AForGroup

A collection iterated by a for, its automatic variables and its implicit iterator.
redef class AFormaldef

nitc :: pretty $ AFormaldef

The definition of a formal generic parameter type. eg X: Y
redef class AFullPropExternCall

nitc :: pretty $ AFullPropExternCall

A single callback declaration on a method on an explicit receiver type.
redef class AIfExpr

nitc :: pretty $ AIfExpr

A if statement
redef class AImpliesExpr

nitc :: pretty $ AImpliesExpr

A implies expression
redef class AInLanguage

nitc :: pretty $ AInLanguage

A language declaration for an extern block
redef class AInitExpr

nitc :: pretty $ AInitExpr

A call to the init constructor.
redef class AInitPropExternCall

nitc :: pretty $ AInitPropExternCall

A single callback declaration on a method on a constructor
redef class AIsaExpr

nitc :: pretty $ AIsaExpr

A type-ckeck expression. eg x isa T
redef class ALocalPropExternCall

nitc :: pretty $ ALocalPropExternCall

A single callback declaration on a method on the current receiver
redef class ALoopExpr

nitc :: pretty $ ALoopExpr

A loop statement
private class ALoopHelper

nitc $ ALoopHelper

Used to factorize work on loops.
redef class AMainMethPropdef

nitc :: pretty $ AMainMethPropdef

The implicit main method
redef class AMethPropdef

nitc :: pretty $ AMethPropdef

A definition of all kind of method (including constructors)
redef class AModule

nitc :: pretty $ AModule

The main node of a Nit source-file
redef class AModuleName

nitc :: pretty $ AModuleName

A possibly fully-qualified module identifier
redef class AModuledecl

nitc :: pretty $ AModuledecl

The declaration of the module with the documentation, name, and annotations
redef class ANewExpr

nitc :: pretty $ ANewExpr

An explicit instantiation. eg new T
redef class ANoImport

nitc :: pretty $ ANoImport

The special import clause of the kernel module. eg import end
redef abstract class ANode

nitc :: pretty $ ANode

Root of the AST class-hierarchy
redef class ANodes[E: ANode]

nitc :: pretty $ ANodes

A sequence of nodes
redef class ANotExpr

nitc :: pretty $ ANotExpr

A not expression
redef class ANullExpr

nitc :: pretty $ ANullExpr

A null literal constant
redef class AOnceExpr

nitc :: pretty $ AOnceExpr

A once expression. eg once x
redef class AOrElseExpr

nitc :: pretty $ AOrElseExpr

A or else expression
redef class AOrExpr

nitc :: pretty $ AOrExpr

A or expression
redef class AOrangeExpr

nitc :: pretty $ AOrangeExpr

An open literal range. eg [x..y[
redef class AParExpr

nitc :: pretty $ AParExpr

A simple parenthesis. eg (x)
redef class AParam

nitc :: pretty $ AParam

A parameter definition in a signature. eg x:X
redef abstract class APropdef

nitc :: pretty $ APropdef

The definition of a property
redef class AReturnExpr

nitc :: pretty $ AReturnExpr

A return statement. eg return x
redef abstract class ASendExpr

nitc :: pretty $ ASendExpr

A polymorphic invocation of a method
redef class ASignature

nitc :: pretty $ ASignature

A signature in a method definition. eg (x,y:X,z:Z):T
redef class AStdClassdef

nitc :: pretty $ AStdClassdef

A standard class definition with a name, superclasses and properties
redef class AStdImport

nitc :: pretty $ AStdImport

A standard import clause. eg import x
redef abstract class AStringFormExpr

nitc :: pretty $ AStringFormExpr

A string literal
redef class ASuperExpr

nitc :: pretty $ ASuperExpr

A call to super. OR a call of a super-constructor
redef class ASuperPropdef

nitc :: pretty $ ASuperPropdef

A super-class. eg super X
redef class ASuperstringExpr

nitc :: pretty $ ASuperstringExpr

A superstring literal. eg "a{x}b{y}c"
redef class AType

nitc :: pretty $ AType

A static type. eg nullable X[Y]
redef class ATypeExpr

nitc :: pretty $ ATypeExpr

A special expression that encapsulates a static type
redef class ATypePropdef

nitc :: pretty $ ATypePropdef

A definition of a virtual type
redef abstract class AUnaryopExpr

nitc :: pretty $ AUnaryopExpr

A unary operation on a method
redef class AVarAssignExpr

nitc :: pretty $ AVarAssignExpr

A local variable simple assignment access
redef class AVardeclExpr

nitc :: pretty $ AVardeclExpr

A declaration of a local variable. eg var x: X = y
redef class AWhileExpr

nitc :: pretty $ AWhileExpr

A while statement
class PrettyPrinterVisitor

nitc $ PrettyPrinterVisitor

The PrettyPrinterVisitor is used to visit a node and pretty print it.
redef abstract class Prod

nitc :: pretty $ Prod

Ancestor of all productions
redef class TComment

nitc :: pretty $ TComment

Token of a line of comments
redef class TEol

nitc :: pretty $ TEol

Token of end of line (basically \n)
redef class TExternCodeSegment

nitc :: pretty $ TExternCodeSegment

A extern code block
redef abstract class Token

nitc :: pretty $ Token

Ancestor of all tokens
package_diagram nitc::pretty pretty nitc::modelbuilder modelbuilder nitc::pretty->nitc::modelbuilder nitc::astutil astutil nitc::pretty->nitc::astutil nitc::loader loader nitc::modelbuilder->nitc::loader nitc::phase phase nitc::modelbuilder->nitc::phase nitc\>parser\> parser nitc::astutil->nitc\>parser\> html html nitc::astutil->html ...nitc::loader ... ...nitc::loader->nitc::loader ...nitc::phase ... ...nitc::phase->nitc::phase ...nitc\>parser\> ... ...nitc\>parser\>->nitc\>parser\> ...html ... ...html->html nitc::nitpretty nitpretty nitc::nitpretty->nitc::pretty a_star-m a_star-m a_star-m->nitc::nitpretty a_star-m... ... a_star-m...->a_star-m

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module console

console :: console

Defines some ANSI Terminal Control Escape Sequences.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module digraph

graph :: digraph

Implementation of directed graphs, also called digraphs.
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module html

html :: html

HTML output facilities
module ini

ini :: ini

Read and write INI configuration files
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module lexer

nitc :: lexer

Lexer and its tokens.
module lexer_work

nitc :: lexer_work

Internal algorithm and data structures for the Nit lexer
module list

core :: list

This module handle double linked lists
module loader

nitc :: loader

Loading of Nit source files
module location

nitc :: location

Nit source-file and locations in source-file
module math

core :: math

Mathematical operations
module mdoc

nitc :: mdoc

Documentation of model entities
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module mmodule

nitc :: mmodule

modules and module hierarchies in the metamodel
module model

nitc :: model

Classes, types and properties
module model_base

nitc :: model_base

The abstract concept of model and related common things
module modelbuilder_base

nitc :: modelbuilder_base

Load nit source files and build the associated model
module more_collections

more_collections :: more_collections

Highly specific, but useful, collections-related classes.
module mpackage

nitc :: mpackage

Modelisation of a Nit package
module native

core :: native

Native structures for text and bytes
module nitpm_shared

nitc :: nitpm_shared

Services related to the Nit package manager
module numeric

core :: numeric

Advanced services for Numeric types
module opts

opts :: opts

Management of options on the command line
module ordered_tree

ordered_tree :: ordered_tree

Manipulation and presentation of ordered trees.
module parser

nitc :: parser

Parser.
module parser_nodes

nitc :: parser_nodes

AST nodes of the Nit language
module parser_prod

nitc :: parser_prod

Production AST nodes full definition.
module parser_work

nitc :: parser_work

Internal algorithm and data structures for the Nit parser
module phase

nitc :: phase

Phases of the processing of nit programs
module poset

poset :: poset

Pre order sets and partial order set (ie hierarchies)
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module tables

nitc :: tables

Module that interfaces the parsing tables.
module template

template :: template

Basic template system
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module toolcontext

nitc :: toolcontext

Common command-line tool infrastructure than handle options and error messages
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O
module version

nitc :: version

This file was generated by git-gen-version.sh

Parents

module astutil

nitc :: astutil

Additional features on Nit AST

Children

module nitpretty

nitc :: nitpretty

Descendants

module a_star-m

a_star-m

# Library used to pretty print Nit code.
#
# Usage:
#
#     import parser_util
#     var tc = new ToolContext
#     var nmodule = tc.parse_something("class A\nfun   toto :  Int  do   return   5\nend")
#     var ppv = new PrettyPrinterVisitor
#     var pmodule = ppv.pretty(nmodule)
#     assert pmodule.write_to_string == """
#     class A
#     \tfun toto: Int do return 5
#     end"""
#
# See `nitpretty` tool for more documentation.
module pretty

import template
import toolcontext
import modelbuilder
import astutil

# The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
#
# The main method here is `visit` that performs the pretty printing of a `ANode`.
#
# Because some tokens like `TComment` are not stored in the AST,
# we visit the AST like traditionnal visitor and also maintain a
# pointer to the `current_token` (actually the next one to be printed).
#
# Visited productions are in charges to move the token pointer using methods such as:
#
# * `consume`: print `current_token` and move to the next one
# * `skip`: move to the next token without printing the current one
# * `skip_to`: move to a specified token skipping all the tokens before
# * `catch_up`: consume all the tokens between `current_token` and a specified token
# * `finish_line`: consume all the tokens between `current_token` and the end of line
class PrettyPrinterVisitor
	# Pretty print `n`.
	fun pretty(n: ANode): Template do
		clear
		n.parentize_tokens

		if n isa Prod then
			current_token = n.first_token
			visit n
		else if n isa Token then
			var p = n.parent

			while p != null and not p isa Prod do
				p = p.parent
			end

			current_token = p.first_token
			visit p
		end

		return tpl.as(not null)
	end

	# Pretty print the whole `nmodule` with comments before and after.
	fun pretty_nmodule(nmodule: AModule): Template do
		clear
		nmodule.parentize_tokens
		current_token = nmodule.location.file.first_token
		visit nmodule
		catch_up nmodule.location.file.last_token
		if skip_empty then tpl.add "\n"
		return tpl.as(not null)
	end

	# Prepare `self` for a new visit.
	private fun clear do
		tpl = new Template
		current_token = null
		indent = 0
		current_length = 0
		previous_length = 0
		wait_addn = 0
	end

	# Visit `n` if not null.
	fun visit(n: nullable ANode) do
		if n == null then return
		n.accept_pretty_printer self
	end

	# 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
	end

	# 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 max_size > 0 and n.collect_length + current_length > max_size then return false
		# check block is inlinable
		return n.is_inlinable
	end

	# Collect all `TComment` between `from` and `to`.
	fun collect_comments(from: nullable ANode, to: nullable ANode): Array[TComment] do
		var res = new Array[TComment]
		if from isa Prod then from = from.first_token
		if to isa Prod then to = to.first_token
		if from == null or to == null then return res

		while from != to do
			if from isa TComment then res.add from
			from = from.as(Token).next_token
		end

		return res
	end

	# Token under cursor.
	#
	# This is the next token to visit.
	var current_token: nullable Token = null

	# Skip the `current_token`.
	fun skip do current_token = current_token.next_token

	# Skip `current_token` until the end of line.
	fun skip_line do current_token = current_token.last_real_token_in_line

	# Skip `current_token` until `target` is reached.
	fun skip_to(target: nullable Token) do
		if target == null then return
		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
		consume_comments
		if current_token.text == token then else current_token.debug("Got `{current_token.text}`; expected `{token}`.")
		visit current_token
	end

	# Is there token to visit between `current_token` and `target`?
	fun need_catch_up(target: nullable Token): Bool do
		if target == null then return false
		return current_token != target
	end

	# Visit all tokens between `current_token` and `target`.
	fun catch_up(target: nullable ANode) do
		if target == null then return
		if current_token == null then return
		var token: Token
		if target isa Token then
			token = target
		else if target isa Prod then
			token = target.first_token.as(not null)
		else
			abort
		end
		if current_token.location > token.location then return
		while current_token != token do visit current_token
	end

	# Visit all tokens between `current_token` and the end of line.
	fun finish_line do
		if current_token isa TComment then
			adds
			visit current_token
		end

		while current_token isa TEol do visit(current_token)
	end

	# The template under construction.
	private var tpl: nullable Template = null

	# Current indent level.
	var indent = 0

	# Size of a tabulation in spaces.
	var tab_size = 8

	# Max line size.
	# 0 (or negative) to disable.
	var max_size = 80 is writable

	# Length of the current line.
	var current_length = 0

	# Length of the previous line.
	var previous_length = 0

	# Is the last line a blank line?
	fun last_line_is_blank: Bool do return previous_length == 0

	# Add `t` to current template.
	fun add(t: String) do
		if t.is_empty then return
		while wait_addn > 0 do
			tpl.add "\n"
			wait_addn -= 1
		end
		tpl.add t
		current_length += t.length
	end

	# Add a `'\n'`.
	fun addn do
		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

	# End of line chars are stored until another char is added.
	# This avoid empty files with only a '`\n`'.
	private var wait_addn = 0

	# Add `'\t' * indent`.
	fun addt do add "\t" * indent

	# Add a space.
	fun adds do add " "

	# Visit explicit receiver, implicit self will be ignored.
	fun visit_recv(n_expr: AExpr) do
		if not n_expr isa AImplicitSelfExpr then
			visit n_expr
			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

redef class ANodes[E]
	private fun accept_pretty_printer(v: PrettyPrinterVisitor) do
		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.forcen
					v.indent += 1
					v.addt
					v.indent -= 1
				else
					v.add ", "
				end
			end

			v.visit e
		end
	end
end

redef class ANode
	# Start visit of `self` using a `PrettyPrinterVisitor`
	private fun accept_pretty_printer(v: PrettyPrinterVisitor) is abstract

	# Collect the length (in `Char`) of the node.
	private fun collect_length: Int is abstract

	# Is `self` printable in one line?
	private fun is_inlinable: Bool do return true

	# Force `self` to be rendered as a block.
	private var force_block = false

	# Does `self` have to be rendered as a block?
	private fun must_be_block: Bool do return force_block

	# Force `self` to be rendered as a line.
	private var force_inline = false

	# Does `self` have be rendered as a line?
	private fun must_be_inline: Bool do
		if parent != null and parent.must_be_inline then return true
		return force_inline
	end

	# Does `self` was written in one line before transformation?
	private fun was_inline: Bool is abstract
end

redef class Token
	redef fun accept_pretty_printer(v) do
		v.add text.trim
		v.current_token = next_token
	end

	redef fun collect_length do return text.length
	redef fun is_inlinable do return true
	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

	# The token where the production really start (skipping ADoc).
	private fun start_token: nullable Token do return first_token

	# Collect all `TComment` contained in the production
	# (between `start_token` and `end_token`).
	private fun collect_comments: Array[TComment] do
		var res = new Array[TComment]
		if start_token == null or last_token == null then return res
		var token = start_token

		while token != last_token do
			if token isa TComment then res.add token
			token = token.next_token
		end

		return res
	end

	redef fun collect_length do
		var res = 0
		if start_token == null or last_token == null then return res
		var token = start_token

		while token != last_token do
			res += token.text.length
			token = token.next_token
		end

		res += token.text.length
		return res
	end

	redef fun was_inline do
		return start_token.location.line_start == last_token.location.line_end
	end
end

# Comments

redef class TComment
	redef fun accept_pretty_printer(v) do
		if is_adoc then
			v.addt
			super
			v.forcen
			return
		end

		if is_licence then
			super
			v.forcen
			if is_last_in_group then v.addn
			return
		end

		if is_orphan then
			v.addn
			v.addt
			super
			v.forcen
			v.addn
			return
		end

		if is_inline then
			if next_token isa TComment and is_first_in_group then v.addn
			v.addt
			super
			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`?
	private fun is_adoc: Bool do return parent isa ADoc and parent.parent != null

	# Is `self` part of a licence?
	private fun is_licence: Bool do
		var prev_token = self.prev_token

		if prev_token == null then
			return true
		else if prev_token isa TComment then
			return prev_token.is_licence
		else
			return false
		end
	end

	# Is `self` starts and ends its line?
	private fun is_inline: Bool do
		return self == first_real_token_in_line and self == last_real_token_in_line
	end

	# Is `self` an orphan line (blank before and after)?
	private fun is_orphan: Bool do
		return prev_token isa TEol and
		   (prev_token.prev_token isa TEol or prev_token.prev_token isa TComment) and
		   next_token isa TEol
	end

	# Is `self` the first comment of a group of comment?
	private fun is_first_in_group: Bool do return not prev_token isa TComment

	# Is `self` the last comment of a group of comments?
	private fun is_last_in_group: Bool do return not next_token isa TComment
end

redef class ADoc
	redef fun accept_pretty_printer(v) do for comment in n_comment do v.visit comment
	redef fun is_inlinable do return n_comment.length <= 1
end

# Annotations

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
				v.visit n_item
				if n_item != n_items.last then
					v.add ", "
				end
			end
			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
				if n_item != n_items.last then
					if was_inline then
						v.forcen
					else
						v.addn
					end
				end
			end
			v.indent -= 1
		end
	end

	redef fun is_inlinable do
		if not super then return false
		for annot in n_items do if not annot.is_inlinable then return false
		return true
	end
end

redef class AAnnotation
	redef fun accept_pretty_printer(v) do
		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

redef class ATypeExpr
	redef fun accept_pretty_printer(v) do v.visit n_type
end

# Modules

redef class AModule
	redef fun accept_pretty_printer(v) do
		v.catch_up start_token
		v.visit n_moduledecl

		if not n_imports.is_empty then
			if v.skip_empty then v.addn

			for n_import in n_imports do
				v.catch_up n_import
				v.visit n_import
			end
		end

		if not n_extern_code_blocks.is_empty then
			v.addn

			for n_extern_block in n_extern_code_blocks do
				v.catch_up n_extern_block
				v.visit n_extern_block
				v.addn
				if n_extern_block != n_extern_code_blocks.last then v.addn
			end

			if not n_classdefs.is_empty then v.addn
		end

		if not n_classdefs.is_empty then
			if v.skip_empty then v.addn

			for n_classdef in n_classdefs do
				v.catch_up n_classdef
				v.visit n_classdef
				if n_classdef != n_classdefs.last then v.addn
			end
		end

		assert v.indent == 0
	end

	# Skip doc if any.
	redef fun start_token do
		if n_moduledecl != null then return n_moduledecl.first_token
		if not n_imports.is_empty then return n_imports.first.first_token
		if not n_classdefs.is_empty then return n_classdefs.first.first_token
		return first_token
	end

	redef fun is_inlinable do return false
end

redef class AModuledecl
	redef fun accept_pretty_printer(v) do
		v.visit n_doc
		v.visit n_kwmodule
		v.adds
		v.visit n_name

		if n_annotations != null then
			var annot_inline = v.can_inline(n_annotations)
			v.visit n_annotations

			if not annot_inline then
				if v.current_token isa TKwend then
					v.consume "end"
					v.finish_line
				else
					v.add "end"
				end
			end
		end

		v.finish_line
		if v.skip_empty then v.addn
	end
end

redef class AModuleName
	redef fun accept_pretty_printer(v) do
		for path in n_path do
			v.visit path
			v.add "::"
		end

		v.visit n_id
	end
end

redef class ANoImport
	redef fun accept_pretty_printer(v) do
		v.visit n_kwimport
		v.adds
		v.visit n_kwend
		v.finish_line
		if v.skip_empty then v.addn
	end
end

redef class AStdImport
	redef fun accept_pretty_printer(v) do
		if not n_visibility isa APublicVisibility then
			v.visit n_visibility
			v.adds
		end

		v.visit n_kwimport
		v.adds
		v.visit n_name
		v.finish_line
		if v.skip_empty then v.addn
	end
end

# Classes

redef class AClassdef
	redef fun accept_pretty_printer(v) do
		for n_propdef in n_propdefs do
			v.catch_up n_propdef

			if n_propdef.n_doc != null or not v.can_inline(n_propdef) then
				if v.skip_empty and n_propdef != n_propdefs.first then v.addn
				v.visit n_propdef
				if v.skip_empty and n_propdef != n_propdefs.last then v.addn
			else
				v.visit n_propdef
			end
		end
	end
end

redef class AStdClassdef
	redef fun accept_pretty_printer(v) do
		v.visit n_doc
		var can_inline = v.can_inline(self)

		if not n_visibility isa APublicVisibility then
			v.visit n_visibility
			v.adds
		end

		if n_kwredef != null then
			v.visit n_kwredef
			v.adds
		end

		v.visit n_classkind
		v.adds
		v.visit n_qid

		if not n_formaldefs.is_empty then
			v.consume "["
			v.visit_list n_formaldefs
			v.consume "]"
		end

		if n_extern_code_block != null then
			v.adds
			v.visit n_extern_code_block
		end

		if can_inline then
			v.adds

			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
			if v.skip_empty then v.addn
			v.indent += 1

			super
			v.catch_up n_kwend
			v.indent -= 1
		end

		v.visit n_kwend
		v.finish_line
		if v.skip_empty then v.addn
		assert v.indent == 0
	end

	redef fun is_inlinable do
		if not super 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

	redef fun start_token do
		if not n_visibility isa APublicVisibility then return n_visibility.first_token
		if n_kwredef != null then return n_kwredef
		return n_classkind.first_token
	end
end

redef class AAbstractClasskind
	redef fun accept_pretty_printer(v) do
		v.visit n_kwabstract
		v.adds
		v.visit n_kwclass
	end
end

redef class AExternClasskind
	redef fun accept_pretty_printer(v) do
		v.visit n_kwextern
		v.adds
		v.visit n_kwclass
	end
end

redef class AFormaldef
	redef fun accept_pretty_printer(v) do
		v.visit n_id

		if n_type != null then
			v.consume ":"
			v.adds
			v.visit n_type
		end
	end
end

redef class AType
	redef fun accept_pretty_printer(v) do
		if n_kwnullable != null then
			v.visit n_kwnullable
			v.adds
		end

		v.visit n_qid

		if not n_types.is_empty then
			v.consume "["
			v.visit_list n_types
			v.consume "]"
		end
	end
end

# Properties

redef class APropdef
	redef fun accept_pretty_printer(v) do
		v.visit n_doc
		v.addt

		if not n_visibility isa nullable APublicVisibility then
			v.visit n_visibility
			v.adds
		end

		if n_kwredef != null then
			v.visit n_kwredef
			v.adds
		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
	end
end

redef class AAttrPropdef
	redef fun accept_pretty_printer(v) do
		super
		v.visit n_kwvar
		v.adds
		v.visit n_id2

		if n_type != null then
			v.consume ":"
			v.adds
			v.visit n_type
		end

		if n_expr != null then
			v.adds
			v.consume "="
			v.adds
			v.visit n_expr
		end

		var annot_inline = visit_annotations(v, n_annotations)
		visit_block(v, n_block, annot_inline)
		v.finish_line
		v.addn
	end

	redef fun first_token do
		if n_doc != null then return n_doc.first_token
		if not n_visibility isa APublicVisibility then return n_visibility.first_token
		if n_kwredef != null then return n_kwredef
		return n_kwvar
	end

	redef fun is_inlinable do return true
end

redef class ATypePropdef
	redef fun accept_pretty_printer(v) do
		super
		v.visit n_kwtype
		v.adds
		v.visit n_qid
		v.consume ":"
		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 AMethPropdef
	redef fun accept_pretty_printer(v) do
		#  TODO: Handle extern annotations

		var before = v.indent
		super
		if n_kwinit != null then v.visit n_kwinit
		if n_kwmeth != null then v.visit n_kwmeth
		if n_kwnew != null then v.visit n_kwnew

		if not n_methid == null then
			v.adds
			v.visit n_methid
		end

		v.visit n_signature

		var annot_inline = visit_annotations(v, n_annotations)

		if n_extern_calls != null or n_extern_code_block != null then
			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

		visit_block(v, n_block, annot_inline)
		v.addn
		assert v.indent == before
	end

	# Can be inlined if:
	# * block is empty or can be inlined
	# * contains no comments
	redef fun is_inlinable do
		if not super then return false
		if n_annotations != null and not n_annotations.is_inlinable then return false
		if n_block != null and not n_block.is_inlinable then return false
		if n_extern_calls != null and not n_extern_calls.is_inlinable then return false
		if n_extern_code_block != null and not n_extern_code_block.is_inlinable then return false
		if not collect_comments.is_empty then return false
		return true
	end
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
	redef fun accept_pretty_printer(v) do
		if not n_params.is_empty then
			v.consume "("
			v.visit_list n_params
			v.consume ")"
		end

		if n_type != null then
			v.consume ":"
			v.adds
			v.visit n_type
		end
	end
end

redef class AParam
	redef fun accept_pretty_printer(v) do
		v.visit n_id

		if n_type != null then
			v.consume ":"
			v.adds
			v.visit n_type
		end

		if n_dotdotdot != null then v.visit n_dotdotdot
	end
end

# Extern

redef class AExternCalls
	redef fun accept_pretty_printer(v) do
		var can_inline = v.can_inline(self)
		v.visit n_kwimport

		if can_inline then
			v.adds
			v.visit_list n_extern_calls
		else
			v.forcen
			v.indent += 1
			v.addt
			v.indent -= 1
			v.visit_list n_extern_calls
		end

		v.adds
	end
end

redef class AFullPropExternCall
	redef fun accept_pretty_printer(v) do
		v.visit n_type
		v.visit n_dot
		v.visit n_methid
	end
end

redef class ALocalPropExternCall
	redef fun accept_pretty_printer(v) do v.visit n_methid
end

redef class AInitPropExternCall
	redef fun accept_pretty_printer(v) do v.visit n_type
end

redef class ACastAsExternCall
	redef fun accept_pretty_printer(v) do
		v.visit n_from_type
		v.visit n_dot
		v.visit n_kwas
		v.consume "("
		v.visit n_to_type
		v.consume ")"
	end
end

redef class AAsNullableExternCall
	redef fun accept_pretty_printer(v) do
		v.visit n_type
		v.consume "."
		v.visit n_kwas
		v.adds
		v.visit n_kwnullable
	end
end

redef class AAsNotNullableExternCall
	redef fun accept_pretty_printer(v) do
		v.visit n_type
		v.consume "."
		v.visit n_kwas
		v.adds
		v.visit n_kwnot
		v.adds
		v.visit n_kwnullable
	end
end

redef class AExternCodeBlock
	redef fun accept_pretty_printer(v) do
		if n_in_language != null then
			v.visit n_in_language
			v.adds
		end

		v.visit n_extern_code_segment
	end

	redef fun is_inlinable do
		if not super then return false
		return n_extern_code_segment.is_inlinable
	end
end

redef class AInLanguage
	redef fun accept_pretty_printer(v) do
		v.visit n_kwin
		v.adds
		v.visit n_string
	end
end

redef class TExternCodeSegment
	redef fun accept_pretty_printer(v) do
		var can_inline = v.can_inline(self)

		if can_inline then
			super
		else
			var text = text.substring(2, text.length - 4)
			var lines = text.r_trim.split("\n")

			if text.is_empty then
				v.add "`\{`\}"
			else
				v.add "`\{"

				if not lines.first.trim.is_empty then
					v.forcen
					lines.first.l_trim
					v.indent += 1
					v.addt
					v.indent -= 1
				end

				for line in lines do
					v.add line.r_trim
					v.forcen
				end

				v.addt
				v.add "`\}"
			end

			v.current_token = next_token
		end
	end

	redef fun is_inlinable do
		if not super then return false
		return location.line_start == location.line_end
	end
end

# Blocks

redef class ABlockExpr
	redef fun accept_pretty_printer(v) do
		var before = v.indent
		var can_inline = v.can_inline(self)

		if can_inline and not n_expr.is_empty then
			v.visit n_expr.first
			v.finish_line
		else
			for nexpr in n_expr do
				var expr_inline = v.can_inline(nexpr)
				if not expr_inline and nexpr != n_expr.first then v.addn
				v.catch_up nexpr
				v.addt
				v.visit nexpr
				v.finish_line
				v.addn
				if not expr_inline and nexpr != n_expr.last then v.addn
			end
		end

		assert v.indent == before
	end

	redef fun is_inlinable do
		if not super then return false
		if not collect_comments.is_empty then return false

		if not n_expr.is_empty then
			if n_expr.length > 1 then return false
			if not n_expr.first.is_inlinable then return false
		end

		return true
	end
end

redef class AIfExpr
	redef fun accept_pretty_printer(v) do
		var before = v.indent
		var can_inline = v.can_inline(self)
		v.visit n_kwif
		v.adds

		if v.can_inline(n_expr) then
			v.visit n_expr
			v.adds
		else
			v.visit n_expr
			v.forcen
			v.addt
		end

		# skip comments before `then` token
		while not v.current_token isa TKwthen do v.skip
		v.consume "then"
		var n_else = self.n_else

		if can_inline then
			v.adds
			if n_then != null then v.visit n_then

			if has_else(v) then
				n_else.force_inline = true
				v.adds
				v.consume "else"
				v.adds
				v.visit n_else
			else if n_then == null then
				v.add "end"
			end
			v.skip_to last_token.last_real_token_in_line
		else
			v.finish_line
			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
				if n_then isa ABlockExpr then
					n_then.force_block = true
					v.visit n_then
				else
					v.addt
					v.visit n_then
					if n_then.was_inline then
						v.forcen
					else
						v.addn
					end
				end
			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.consume "else"

				if n_else isa AIfExpr then
					n_else.force_block = true
					v.adds
					v.visit n_else
				else
					v.finish_line
					if was_inline then
						v.forcen
					else
						v.addn
					end
					v.indent += 1

					if n_else isa ABlockExpr then
						n_else.force_block = true
						v.visit n_else
					else
						v.addt
						v.visit n_else
						if n_else.was_inline then
							v.forcen
						else
							v.addn
						end
					end

					if last_token isa TKwend then
						v.catch_up last_token
						v.indent -= 1
						v.addt
						v.consume "end"
					else
						v.indent -= 1
						v.addt
						v.add "end"
					end
				end
			else
				if last_token.location >= v.current_token.location then v.catch_up last_token
				v.indent -= 1
				v.addt
				v.add "end"
				if v.current_token isa TKwend then v.skip
			end
		end

		assert v.indent == before
	end

	redef fun is_inlinable do
		if not super then return false
		if n_then != null and not n_then.is_inlinable then return false
		var n_else = self.n_else
		if (n_else isa ABlockExpr and not n_else.n_expr.is_empty) or
		   (not n_else isa ABlockExpr and n_else != null) then
			return false
		end
		if not collect_comments.is_empty then return false
		return true
	end

	# Does this `if` statement contains a `else` part?
	private fun has_else(v: PrettyPrinterVisitor): Bool do
		var n_else = n_else
		if n_else == null then return false
		var n_kwelse = collect_kwelse
		if n_kwelse == null then return false

		if n_else isa ABlockExpr then
			var comments: Array[TComment]

			if n_then == null then
				comments = v.collect_comments(n_expr.last_token, n_else.last_token)
			else
				comments = v.collect_comments(n_then.last_token, n_else.last_token)
			end

			if not comments.is_empty then return true
			return not n_else.n_expr.is_empty
		end

		return true
	end

	# Lookup for `else` token in `self`.
	private fun collect_kwelse: nullable TKwelse do
		var token = first_token

		while token != last_token do
			if token isa TKwelse then return token
			token = token.next_token
		end

		return null
	end
end

# Used to factorize work on loops.
private class ALoopHelper
	super AExpr

	fun loop_block: nullable ANode is abstract
	fun loop_label: nullable ANode is abstract

	fun visit_loop_block(v: PrettyPrinterVisitor) do
		var n_block = loop_block
		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
			v.indent -= 1
			v.addt
			v.visit n_block.n_kwend
		else
			v.addt
			v.visit n_block
			v.addn
			v.indent -= 1
			v.addt
			v.add "end"
		end

		if loop_label != null then
			v.adds
			v.visit loop_label
		end
	end

	fun visit_loop_inline(v: PrettyPrinterVisitor) do
		var n_block = loop_block
		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

		if loop_label != null then
			v.adds
			v.visit loop_label
		end
	end

	redef fun is_inlinable do
		var n_block = loop_block
		if not super then return false
		if n_block isa ABlockExpr and not n_block.is_inlinable then return false
		if not collect_comments.is_empty then return false
		return true
	end
end

redef class ALoopExpr
	super ALoopHelper

	redef fun loop_block do return n_block
	redef fun loop_label do return n_label

	redef fun accept_pretty_printer(v) do
		var can_inline = v.can_inline(self)
		v.visit n_kwloop
		if can_inline then visit_loop_inline v else visit_loop_block v
	end
end

redef class AWhileExpr
	super ALoopHelper

	redef fun loop_block do return n_block
	redef fun loop_label do return n_label

	redef fun accept_pretty_printer(v) do
		var can_inline = v.can_inline(self)
		v.visit n_kwwhile
		v.adds
		v.visit n_expr
		v.adds
		v.visit n_kwdo
		if can_inline then visit_loop_inline v else visit_loop_block v
	end
end

redef class ADoExpr
	super ALoopHelper

	redef fun loop_block do return n_block
	redef fun loop_label do return n_label

	redef fun accept_pretty_printer(v) do
		var can_inline = v.can_inline(self)
		v.visit n_kwdo
		if can_inline then visit_loop_inline v else visit_loop_block v
	end
end

redef class AForExpr
	super ALoopHelper

	redef fun loop_block do return n_block
	redef fun loop_label do return n_label

	redef fun accept_pretty_printer(v) do
		var can_inline = v.can_inline(self)
		v.visit n_kwfor
		v.adds

		for n_group in n_groups do
			v.visit n_group
			if n_group != n_groups.last then v.add ", "

		end

		v.adds
		v.visit n_kwdo
		if can_inline then visit_loop_inline v else visit_loop_block v
	end
end

redef class AForGroup
	redef fun accept_pretty_printer(v) do
		for n_id in n_ids do
			v.visit n_id
			if n_id != n_ids.last then v.add ", "
		end

		v.adds
		v.consume "in"
		v.adds
		v.visit n_expr
	end
end

redef class ABreakExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_kwbreak

		if n_expr != null then
			v.adds
			v.visit n_expr
		end

		if n_label != null then
			v.adds
			v.visit n_label
		end
	end

	redef fun is_inlinable do return true
end

redef class AContinueExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_kwcontinue

		if n_expr != null then
			v.adds
			v.visit n_expr
		end

		if n_label != null then
			v.adds
			v.visit n_label
		end
	end

	redef fun is_inlinable do return true
end

# Calls

redef class ASendExpr
	redef fun is_inlinable do return true
end

redef class ACallExpr
	redef fun accept_pretty_printer(v) do
		var can_inline = v.can_inline(self)
		v.visit_recv n_expr

		if not n_expr isa AImplicitSelfExpr and not can_inline then
			v.forcen
			v.addt
		end

		v.visit n_qid

		if not n_args.n_exprs.is_empty then
			if is_stmt and n_args.n_exprs.length == 1 then
				v.adds
				if v.current_token isa TOpar then v.skip
				v.visit n_args.n_exprs.first
				if v.current_token isa TCpar then v.skip
			else
				v.visit_args n_args.n_exprs
			end
		end
	end

	# Is the call alone on its line?
	fun is_stmt: Bool do return parent isa ABlockExpr
end

redef class ACallAssignExpr
	redef fun accept_pretty_printer(v) do
		v.visit_recv n_expr
		v.visit n_qid

		if not n_args.n_exprs.is_empty then
			v.consume "("
			v.visit_list n_args.n_exprs
			v.consume ")"
		end

		v.adds
		v.consume "="
		v.adds
		v.visit n_value
	end
end

redef class ACallReassignExpr
	redef fun accept_pretty_printer(v) do
		v.visit_recv n_expr
		v.visit n_qid

		if not n_args.n_exprs.is_empty then
			v.consume "("
			v.visit_list n_args.n_exprs
			v.consume ")"
		end

		v.adds
		v.visit n_assign_op
		v.adds
		v.visit n_value
	end
end

redef class ABraExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_expr

		if not n_args.n_exprs.is_empty then
			v.consume "["
			v.visit_list n_args.n_exprs
			v.consume "]"
		end
	end
end

redef class ABraAssignExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_expr

		if not n_args.n_exprs.is_empty then
			v.consume "["
			v.visit_list n_args.n_exprs
			v.consume "]"
		end

		v.adds
		v.visit n_assign
		v.adds
		v.visit n_value
	end
end

redef class ABraReassignExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_expr

		if not n_args.n_exprs.is_empty then
			v.consume "["
			v.visit_list n_args.n_exprs
			v.consume "]"
		end

		v.adds
		v.visit n_assign_op
		v.adds
		v.visit n_value
	end
end

redef class AAssignMethid
	redef fun accept_pretty_printer(v) do
		v.visit n_id
		v.visit n_assign
	end
end

redef class ABraMethid
	redef fun accept_pretty_printer(v) do
		v.visit n_obra
		v.visit n_cbra
	end
end

redef class ABraassignMethid
	redef fun accept_pretty_printer(v) do
		v.visit n_obra
		v.visit n_cbra
		v.visit n_assign
	end
end

redef class AInitExpr
	redef fun accept_pretty_printer(v) do
		if not n_expr isa AImplicitSelfExpr then
			v.visit n_expr
			v.consume "."
		end

		v.visit n_kwinit
		v.visit_args n_args.n_exprs
	end
end

redef class ANewExpr
	redef fun accept_pretty_printer(v) do
		var can_inline = v.can_inline(self)
		v.visit n_kwnew
		v.adds
		v.visit n_type

		if n_qid != null then
			v.consume "."

			if not can_inline then
				v.forcen
				v.indent += 1
				v.addt
				v.indent -= 1
			end

			v.visit n_qid
		end

		v.visit_args n_args.n_exprs
	end

	redef fun is_inlinable do return true
end

# Attributes

redef class AAttrExpr
	redef fun accept_pretty_printer(v) do
		v.visit_recv n_expr
		v.visit n_id
	end

	redef fun is_inlinable do return true
end

redef class AAttrAssignExpr
	redef fun accept_pretty_printer(v) do
		v.visit_recv n_expr
		v.visit n_id
		v.adds
		v.visit n_assign
		v.adds
		v.visit n_value
	end
end

redef class AAttrReassignExpr
	redef fun accept_pretty_printer(v) do
		v.visit_recv n_expr
		v.visit n_id
		v.adds
		v.visit n_assign_op
		v.adds
		v.visit n_value
	end
end

# Exprs

redef class AVardeclExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_kwvar
		v.adds
		v.visit n_id

		if n_type != null then
			v.consume ":"
			v.adds
			v.visit n_type
		end

		if n_expr != null then
			v.adds
			v.consume "="
			v.adds
			v.visit n_expr
		end
	end

	redef fun is_inlinable do return true
end

redef class AVarAssignExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_id
		v.adds
		v.visit n_assign
		v.adds
		v.visit n_value
	end
end

redef class AAssertExpr
	redef fun accept_pretty_printer(v) do
		var can_inline = v.can_inline(self)
		v.visit n_kwassert

		if n_id != null then
			v.adds
			v.visit n_id
			v.consume ":"
		end

		v.adds
		v.visit n_expr
		var n_else = self.n_else

		if n_else != null then
			v.adds
			v.consume "else"

			if can_inline then
				v.adds
				v.visit n_else
			else
				v.addn
				v.indent += 1

				if n_else isa ABlockExpr then
					n_else.force_block = true
					v.visit n_else
					v.indent -= 1
					v.addt
					v.visit n_else.n_kwend
				else
					v.addt
					v.visit n_else
					v.forcen
					v.indent -= 1
					v.addt
					v.add "end"
				end
			end
		end
	end

	redef fun is_inlinable do
		if not super then return false
		if n_else != null and not n_else.is_inlinable then return false
		return true
	end
end

redef class AReturnExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_kwreturn

		if n_expr != null then
			v.adds
			v.visit n_expr
		end
	end
end

redef class ASuperExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_kwsuper

		if not n_args.n_exprs.is_empty then
			if is_stmt and n_args.n_exprs.length == 1 then
				v.adds
				if v.current_token isa TOpar then v.skip
				v.visit n_args.n_exprs.first
				if v.current_token isa TCpar then v.skip
			else
				v.visit_args n_args.n_exprs
			end
		end
	end

	# Is the call alone on its line?
	fun is_stmt: Bool do return self.first_token.is_starting_line

	redef fun is_inlinable do return true
end

redef class AOnceExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_kwonce
		v.adds
		v.visit n_expr
	end

	redef fun is_inlinable do return true
end

redef class AAbortExpr
	redef fun accept_pretty_printer(v) do v.visit n_kwabort
	redef fun is_inlinable do return true
end

redef class ANotExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_kwnot
		v.adds
		v.visit n_expr
	end
end

redef class AAsCastExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_expr
		v.consume "."
		v.visit n_kwas
		v.visit n_opar
		v.visit n_type
		v.visit n_cpar
	end
end

redef class AAsNotnullExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_expr
		v.consume "."
		v.visit n_kwas
		v.visit n_opar
		v.visit n_kwnot
		v.adds
		v.visit n_kwnull
		v.visit n_cpar
	end
end

# Binops

# Used to factorize work on Or, And, Implies and Binop expressions.
private class ABinOpHelper
	super AExpr

	fun bin_expr1: AExpr is abstract
	fun bin_expr2: AExpr is abstract

	# Operator string
	fun bin_op: String is abstract

	redef fun accept_pretty_printer(v) do
		var can_inline = v.can_inline(self)

		if not can_inline then
			if (self isa ABinopExpr and bin_expr1 isa ABinopExpr) or
			   (self isa AAndExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr)) or
			   (self isa AOrExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr))
			then
				bin_expr1.force_block = true
			end
		end

		v.visit bin_expr1
		v.adds
		v.consume bin_op

		if can_inline then
			v.adds
			v.visit bin_expr2
		else
			v.forcen
			v.indent += 1
			v.addt
			v.indent -= 1
			v.visit bin_expr2
		end
	end
end

redef class AAndExpr
	super ABinOpHelper

	redef fun bin_expr1 do return n_expr
	redef fun bin_expr2 do return n_expr2
	redef fun bin_op do return "and"
end

redef class AOrExpr
	super ABinOpHelper

	redef fun bin_expr1 do return n_expr
	redef fun bin_expr2 do return n_expr2
	redef fun bin_op do return "or"
end

redef class AImpliesExpr
	super ABinOpHelper

	redef fun bin_expr1 do return n_expr
	redef fun bin_expr2 do return n_expr2
	redef fun bin_op do return "implies"
end

redef class ABinopExpr
	super ABinOpHelper

	redef fun bin_expr1 do return n_expr
	redef fun bin_expr2 do return n_expr2
	redef fun bin_op do return operator
end

redef class AIsaExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_expr
		v.adds
		v.consume "isa"
		v.adds
		v.visit n_type
	end
end

redef class AOrElseExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_expr
		v.adds
		v.consume "or"
		v.adds
		v.consume "else"
		v.adds
		v.visit n_expr2
	end

	redef fun is_inlinable do return true
end

# Syntax

redef class AUnaryopExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_op
		v.visit n_expr
	end
end

redef class ANullExpr
	redef fun accept_pretty_printer(v) do v.visit n_kwnull
	redef fun is_inlinable do return true
end

redef class AParExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_opar
		v.visit n_expr
		v.visit n_cpar
	end
end

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

redef class ACrangeExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_obra
		v.visit n_expr
		v.consume ".."
		v.visit n_expr2
		v.visit n_cbra
	end
end

redef class AOrangeExpr
	redef fun accept_pretty_printer(v) do
		v.visit n_obra
		v.visit n_expr
		v.consume ".."
		v.visit n_expr2
		v.visit n_cbra
	end
end

# Strings

redef class AStringFormExpr
	redef fun accept_pretty_printer(v) do
		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
			var i = 0

			while i < text.length do
				v.add text[i].to_s

				if v.max_size > 0 and v.current_length >= v.max_size and i <= text.length - 3 then
					v.add "\" +"
					if was_inline then
						v.forcen
					else
						v.addn
					end
					v.indent += 1
					v.addt
					v.indent -= 1
					v.add "\""
				end

				i += 1
			end

			v.current_token = n_string.next_token
		end
	end
end

redef class ASuperstringExpr
	redef fun accept_pretty_printer(v) do
		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
		if super then return true

		if not n_exprs.is_empty then
			var first = n_exprs.first
			return first isa AStringFormExpr and first.n_string.text.has_prefix("\"\"\"")
		end

		return false
	end
end
src/pretty.nit:15,1--2171,3