# # This class implements a pretty printing algorithm. It finds line breaks and # nice indentations for grouped structure. # # By default, the class assumes that primitive elements are strings and each # byte in the strings have single column in width. But it can be used for other # situations by giving suitable arguments for some methods: # * newline object and space generation block for PrettyPrint.new # * optional width argument for PrettyPrint#text # * PrettyPrint#breakable # # # There are several candidate uses: # * text formatting using proportional fonts # * multibyte characters which has columns different to number of bytes # * non-string formatting # # # ## Bugs # * Box based formatting? # * Other (better) model/algorithm? # # # Report any bugs at http://bugs.ruby-lang.org # # ## References # Christian Lindig, Strictly Pretty, March 2000, # http://www.st.cs.uni-sb.de/~lindig/papers/#pretty # # Philip Wadler, A prettier printer, March 1998, # http://homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier # # ## Author # Tanaka Akira # class PrettyPrint interface _Output def <<: (String) -> void end # # This is a convenience method which is same as follows: # # begin # q = PrettyPrint.new(output, maxwidth, newline, &genspace) # ... # q.flush # output # end # def self.format: (?untyped output, ?Integer maxwidth, ?String newline, ?^(Integer) -> Integer genspace) { (PrettyPrint) -> untyped } -> _Output # # This is similar to PrettyPrint::format but the result has no breaks. # # `maxwidth`, `newline` and `genspace` are ignored. # # The invocation of `breakable` in the block doesn't break a line and is treated # as just an invocation of `text`. # def self.singleline_format: (?untyped output, ?Integer? maxwidth, ?String? newline, ?^(Integer) -> Integer? genspace) { (PrettyPrint::SingleLine) -> untyped } -> _Output # # Creates a buffer for pretty printing. # # `output` is an output target. If it is not specified, '' is assumed. It should # have a << method which accepts the first argument `obj` of PrettyPrint#text, # the first argument `sep` of PrettyPrint#breakable, the first argument # `newline` of PrettyPrint.new, and the result of a given block for # PrettyPrint.new. # # `maxwidth` specifies maximum line length. If it is not specified, 79 is # assumed. However actual outputs may overflow `maxwidth` if long non-breakable # texts are provided. # # `newline` is used for line breaks. "n" is used if it is not specified. # # The block is used to generate spaces. {|width| ' ' * width} is used if it is # not given. # def initialize: (?untyped output, ?Integer maxwidth, ?String newline, ?^(Integer) -> Integer genspace) -> void # # The output object. # # This defaults to '', and should accept the << method # attr_reader output: _Output # # The maximum width of a line, before it is separated in to a newline # # This defaults to 79, and should be an Integer # attr_reader maxwidth: Integer # # The value that is appended to `output` to add a new line. # # This defaults to "n", and should be String # attr_reader newline: String # # A lambda or Proc, that takes one argument, of an Integer, and returns the # corresponding number of spaces. # # By default this is: # lambda {|n| ' ' * n} # attr_reader genspace: Proc # # The number of spaces to be indented # attr_reader indent: Integer # # The PrettyPrint::GroupQueue of groups in stack to be pretty printed # attr_reader group_queue: PrettyPrint::GroupQueue # # Returns the group most recently added to the stack. # # Contrived example: # out = "" # => "" # q = PrettyPrint.new(out) # => #, @output_width=0, @buffer_width=0, @buffer=[], @group_stack=[#], @group_queue=#]]>, @indent=0> # q.group { # q.text q.current_group.inspect # q.text q.newline # q.group(q.current_group.depth + 1) { # q.text q.current_group.inspect # q.text q.newline # q.group(q.current_group.depth + 1) { # q.text q.current_group.inspect # q.text q.newline # q.group(q.current_group.depth + 1) { # q.text q.current_group.inspect # q.text q.newline # } # } # } # } # => 284 # puts out # # # # # # # # # def current_group: () -> PrettyPrint::Group # # Breaks the buffer into lines that are shorter than #maxwidth # def break_outmost_groups: () -> untyped # # This adds `obj` as a text of `width` columns in width. # # If `width` is not specified, obj.length is used. # def text: (String obj, ?Integer width) -> void # # This is similar to #breakable except the decision to break or not is # determined individually. # # Two #fill_breakable under a group may cause 4 results: (break,break), # (break,non-break), (non-break,break), (non-break,non-break). This is different # to #breakable because two #breakable under a group may cause 2 results: # (break,break), (non-break,non-break). # # The text `sep` is inserted if a line is not broken at this point. # # If `sep` is not specified, " " is used. # # If `width` is not specified, `sep.length` is used. You will have to specify # this when `sep` is a multibyte character, for example. # def fill_breakable: (?String sep, ?Integer width) -> void # # This says "you can break a line here if necessary", and a `width`-column text # `sep` is inserted if a line is not broken at the point. # # If `sep` is not specified, " " is used. # # If `width` is not specified, `sep.length` is used. You will have to specify # this when `sep` is a multibyte character, for example. # def breakable: (?String sep, ?Integer width) -> void # # Groups line break hints added in the block. The line break hints are all to be # used or not. # # If `indent` is specified, the method call is regarded as nested by # nest(indent) { ... }. # # If `open_obj` is specified, `text open_obj, open_width` is called before # grouping. If `close_obj` is specified, `text close_obj, close_width` is called # after grouping. # def group: (?::Integer indent, ?::String open_obj, ?::String close_obj, ?Integer open_width, ?Integer close_width) { () -> untyped } -> Integer # # Takes a block and queues a new group that is indented 1 level further. # def group_sub: () { () -> untyped } -> untyped # # Increases left margin after newline with `indent` for line breaks added in the # block. # def nest: (Integer indent) { () -> untyped } -> void # # outputs buffered data. # def flush: () -> Integer class Text def initialize: () -> void attr_reader width: Integer def output: (untyped `out`, untyped output_width) -> untyped def add: (untyped obj, Integer width) -> void end class Breakable def initialize: (String sep, Integer width, PrettyPrint q) -> void attr_reader obj: String attr_reader width: Integer attr_reader indent: Integer def output: (untyped `out`, Integer output_width) -> untyped end class Group def initialize: (untyped depth) -> void attr_reader depth: untyped attr_reader breakables: Array[PrettyPrint::Breakable] def break: () -> bool def break?: () -> bool def first?: () -> bool end class GroupQueue def initialize: (*untyped groups) -> void def enq: (untyped group) -> void def deq: () -> (PrettyPrint::Group | nil) def delete: (PrettyPrint::Group group) -> void end # # PrettyPrint::SingleLine is used by PrettyPrint.singleline_format # # It is passed to be similar to a PrettyPrint object itself, by responding to: # * #text # * #breakable # * #nest # * #group # * #flush # * #first? # # # but instead, the output has no line breaks # class SingleLine # # Create a PrettyPrint::SingleLine object # # Arguments: # * `output` - String (or similar) to store rendered text. Needs to respond to # '<<' # * `maxwidth` - Argument position expected to be here for compatibility. # This argument is a noop. # # * `newline` - Argument position expected to be here for compatibility. # This argument is a noop. # def initialize: (String | untyped output, ?Integer? maxwidth, ?String? newline) -> void # # Add `obj` to the text to be output. # # `width` argument is here for compatibility. It is a noop argument. # def text: (String obj, ?Integer? width) -> void # # Appends `sep` to the text to be output. By default `sep` is ' ' # # `width` argument is here for compatibility. It is a noop argument. # def breakable: (?String sep, ?Integer? width) -> void def nest: (untyped indent) { () -> untyped } -> void # # Opens a block for grouping objects to be pretty printed. # # Arguments: # * `indent` - noop argument. Present for compatibility. # * `open_obj` - text appended before the &blok. Default is '' # * `close_obj` - text appended after the &blok. Default is '' # * `open_width` - noop argument. Present for compatibility. # * `close_width` - noop argument. Present for compatibility. # def group: (?Integer? indent, ?String open_obj, ?String close_obj, ?Integer? open_width, ?Integer? close_width) { () -> untyped } -> untyped def flush: () -> nil # # This is used as a predicate, and ought to be called first. # def first?: () -> bool end end