Skip to content

Commit

Permalink
Add PrettyPrint to RBS Stdlib
Browse files Browse the repository at this point in the history
  * Added prettyprint.rbs file with definitions and rdoc
    annotations
  * Added the tests
  • Loading branch information
Alain Mauri committed Jan 24, 2021
1 parent deeb903 commit 54bba31
Show file tree
Hide file tree
Showing 2 changed files with 584 additions and 0 deletions.
366 changes: 366 additions & 0 deletions stdlib/prettyprint/0/prettyprint.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,366 @@
# 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 <akr@fsij.org>
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 a Fixnum
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 a Fixnum, 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)
# => #<PrettyPrint:0x82f85c0 @output="", @maxwidth=79, @newline="\n", @genspace=#<Proc:0x82f8368@/home/vbatts/.rvm/rubies/ruby-head/lib/ruby/2.0.0/prettyprint.rb:82 (lambda)>, @output_width=0, @buffer_width=0, @buffer=[], @group_stack=[#<PrettyPrint::Group:0x82f8138 @depth=0, @breakables=[], @break=false>], @group_queue=#<PrettyPrint::GroupQueue:0x82fb7c0 @queue=[[#<PrettyPrint::Group:0x82f8138 @depth=0, @breakables=[], @break=false>]]>, @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
# #<PrettyPrint::Group:0x8354758 @depth=1, @breakables=[], @break=false>
# #<PrettyPrint::Group:0x8354550 @depth=2, @breakables=[], @break=false>
# #<PrettyPrint::Group:0x83541cc @depth=3, @breakables=[], @break=false>
# #<PrettyPrint::Group:0x8347e54 @depth=4, @breakables=[], @break=false>
#
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
# Creates a new text object.
#
# This constructor takes no arguments.
#
# The workflow is to append a PrettyPrint::Text object to the buffer, and
# being able to call the buffer.last() to reference it.
#
# As there are objects, use PrettyPrint::Text#add to include the objects
# and the width to utilized by the String version of this object.
def initialize: () -> void

# The total width of the objects included in this Text object.
attr_reader width: Integer

# Render the String text of the objects that have been added to this Text object.
#
# Output the text to +out+, and increment the width to +output_width+
def output: (untyped `out`, untyped output_width) -> untyped

# Include +obj+ in the objects to be pretty printed, and increment
# this Text object's total width by +width+
def add: (untyped obj, Integer width) -> void
end

class Breakable
# Create a new Breakable object.
#
# Arguments:
# * +sep+ String of the separator
# * +width+ Fixnum width of the +sep+
# * +q+ parent PrettyPrint object, to base from
def initialize: (String sep, Integer width, PrettyPrint q) -> void

# Holds the separator String
#
# The +sep+ argument from ::new
attr_reader obj: String

# The width of +obj+ / +sep+
attr_reader width: Integer

# The number of spaces to indent.
#
# This is inferred from +q+ within PrettyPrint, passed in ::new
attr_reader indent: Integer

# Render the String text of the objects that have been added to this
# Breakable object.
#
# Output the text to +out+, and increment the width to +output_width+
def output: (untyped `out`, Integer output_width) -> untyped
end

class Group
# The Group class is used for making indentation easier.
#
# While this class does neither the breaking into newlines nor indentation,
# it is used in a stack (as well as a queue) within PrettyPrint, to group
# objects.
#
# For information on using groups, see PrettyPrint#group
#
# This class is intended for internal use of the PrettyPrint buffers.
# :nodoc:
# Create a Group object
#
# Arguments:
# * +depth+ - this group's relation to previous groups
def initialize: (untyped depth) -> void

# This group's relation to previous groups
attr_reader depth: untyped

# Array to hold the Breakable objects for this Group
attr_reader breakables: Array[PrettyPrint::Breakable]

# Makes a break for this Group, and returns true
def break: () -> bool

# Boolean of whether this Group has made a break
def break?: () -> bool

# Boolean of whether this Group has been queried for being first
#
# This is used as a predicate, and ought to be called first.
def first?: () -> bool
end

class GroupQueue
# The GroupQueue class is used for managing the queue of Group to be pretty
# printed.
#
# This queue groups the Group objects, based on their depth.
#
# This class is intended for internal use of the PrettyPrint buffers.
# :nodoc:
# Create a GroupQueue object
#
# Arguments:
# * +groups+ - one or more PrettyPrint::Group objects
def initialize: (*untyped groups) -> void

# Enqueue +group+
#
# This does not strictly append the group to the end of the queue,
# but instead adds it in line, base on the +group.depth+
def enq: (untyped group) -> void

# Returns the outer group of the queue
def deq: () -> (PrettyPrint::Group | nil)

# Remote +group+ from this queue
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
Loading

0 comments on commit 54bba31

Please sign in to comment.