summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2002-06-27 12:01:07 +0000
committerakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2002-06-27 12:01:07 +0000
commit571ff1d6b974eab38d4707d41f071a176960704b (patch)
tree14a0d782c0d681a56d6953ce75e58838f4cc2917 /lib
parent48fbacb11ca896024b23ffdb7f5c5e24bbe7456f (diff)
* lib/prettyprint.rb, lib/pp.rb: convenience methods added.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@2602 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/pp.rb166
-rw-r--r--lib/prettyprint.rb385
2 files changed, 293 insertions, 258 deletions
diff --git a/lib/pp.rb b/lib/pp.rb
index cf1f6788d2..0c7e6b3bcc 100644
--- a/lib/pp.rb
+++ b/lib/pp.rb
@@ -88,9 +88,21 @@ PP#pp to print the object.
Object#pretty_print_cycled is used when ((|obj|)) is already
printed, a.k.a the object reference chain has a cycle.
+--- object_group(obj) { ... }
+ is a convenience method which is same as follows:
+
+ group(1, '#<' + obj.class.name, '>') { ... }
+
+--- comma_breakable
+ is a convenience method which is same as follows:
+
+ text ','
+ breakable
+
= Object
--- pretty_print(pp)
is a default pretty printing method for general objects.
+ It calls (({pretty_print_instance_variables})) to list instance variables.
If (({self})) has a customized (redefined) (({inspect})) method,
the result of (({self.inspect})) is used but it obviously has no
@@ -102,6 +114,13 @@ PP#pp to print the object.
--- pretty_print_cycled(pp)
is a default pretty printing method for general objects that are
detected as part of a cycle.
+
+--- pretty_print_instance_variables
+ is a method to list instance variables used by the default implementation
+ of (({pretty_print})).
+
+ This method should return an array of names of instance variables as symbols or strings as:
+ (({[:@a, :@b]})).
=end
require 'prettyprint'
@@ -172,60 +191,45 @@ class PP < PrettyPrint
end
end
+ def object_group(obj, &block)
+ group(1, '#<' + obj.class.name, '>', &block)
+ end
+
+ def comma_breakable
+ text ','
+ breakable
+ end
+
def pp_object(obj)
- group {
- text '#<'
- nest(1) {
- text obj.class.name
- text ':'
- text sprintf('0x%x', obj.__id__)
- first = true
- obj.instance_variables.sort.each {|v|
- if first
- first = false
- else
- text ','
- end
- breakable
- group {
- text v
- text '='
- nest(1) {
- breakable ''
- pp(obj.instance_eval v)
- }
- }
+ object_group(obj) {
+ text sprintf(':0x%x', obj.__id__)
+ obj.pretty_print_instance_variables.each {|v|
+ v = v.to_s if Symbol === v
+ text ',' unless first?
+ breakable
+ text v
+ text '='
+ group(1) {
+ breakable ''
+ pp(obj.instance_eval v)
}
}
- text '>'
}
end
def pp_hash(obj)
- group {
- text '{'
- nest(1) {
- first = true
- obj.each {|k, v|
- if first
- first = false
- else
- text ","
- breakable
- end
- group {
- pp k
- text '=>'
- nest(1) {
- group {
- breakable ''
- pp v
- }
- }
+ group(1, '{', '}') {
+ obj.each {|k, v|
+ comma_breakable unless first?
+ group {
+ pp k
+ text '=>'
+ group(1) {
+ breakable ''
+ pp v
}
}
}
- text '}'
}
end
@@ -271,6 +275,10 @@ class PP < PrettyPrint
pp.breakable
pp.text sprintf("...>")
end
+
+ def pretty_print_instance_variables
+ instance_variables.sort
+ end
end
end
@@ -284,20 +292,12 @@ end
class Array
def pretty_print(pp)
- pp.text "["
- pp.nest(1) {
- first = true
+ pp.group(1, '[', ']') {
self.each {|v|
- if first
- first = false
- else
- pp.text ","
- pp.breakable
- end
+ pp.comma_breakable unless pp.first?
pp.pp v
}
}
- pp.text "]"
end
def pretty_print_cycled(pp)
@@ -323,27 +323,18 @@ end
class Struct
def pretty_print(pp)
- pp.text sprintf("#<%s", self.class.name)
- pp.nest(1) {
- first = true
+ pp.object_group(self) {
self.members.each {|member|
- if first
- first = false
- else
- pp.text ","
- end
+ pp.text "," unless pp.first?
pp.breakable
- pp.group {
- pp.text member.to_s
- pp.text '='
- pp.nest(1) {
- pp.breakable ''
- pp.pp self[member]
- }
+ pp.text member.to_s
+ pp.text '='
+ pp.group(1) {
+ pp.breakable ''
+ pp.pp self[member]
}
}
}
- pp.text ">"
end
def pretty_print_cycled(pp)
@@ -365,12 +356,10 @@ class File
class Stat
def pretty_print(pp)
require 'etc.so'
- pp.nest(1) {
- pp.text "#<"
- pp.text self.class.name
- pp.breakable; pp.text sprintf("dev=0x%x", self.dev); pp.text ','
- pp.breakable; pp.text "ino="; pp.pp self.ino; pp.text ','
+ pp.object_group(self) {
pp.breakable
+ pp.text sprintf("dev=0x%x", self.dev); pp.comma_breakable
+ pp.text "ino="; pp.pp self.ino; pp.comma_breakable
pp.group {
m = self.mode
pp.text sprintf("mode=0%o", m)
@@ -389,10 +378,9 @@ class File
(m & 0002 == 0 ? ?- : ?w),
(m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
(m & 01000 == 0 ? ?x : ?t)))
- pp.text ','
}
- pp.breakable; pp.text "nlink="; pp.pp self.nlink; pp.text ','
- pp.breakable
+ pp.comma_breakable
+ pp.text "nlink="; pp.pp self.nlink; pp.comma_breakable
pp.group {
pp.text "uid="; pp.pp self.uid
begin
@@ -400,9 +388,8 @@ class File
pp.breakable; pp.text "(#{name})"
rescue ArgumentError
end
- pp.text ','
}
- pp.breakable
+ pp.comma_breakable
pp.group {
pp.text "gid="; pp.pp self.gid
begin
@@ -410,39 +397,34 @@ class File
pp.breakable; pp.text "(#{name})"
rescue ArgumentError
end
- pp.text ','
}
- pp.breakable
+ pp.comma_breakable
pp.group {
pp.text sprintf("rdev=0x%x", self.rdev)
pp.breakable
pp.text sprintf('(%d, %d)', self.rdev_major, self.rdev_minor)
- pp.text ','
}
- pp.breakable; pp.text "size="; pp.pp self.size; pp.text ','
- pp.breakable; pp.text "blksize="; pp.pp self.blksize; pp.text ','
- pp.breakable; pp.text "blocks="; pp.pp self.blocks; pp.text ','
- pp.breakable
+ pp.comma_breakable
+ pp.text "size="; pp.pp self.size; pp.comma_breakable
+ pp.text "blksize="; pp.pp self.blksize; pp.comma_breakable
+ pp.text "blocks="; pp.pp self.blocks; pp.comma_breakable
pp.group {
t = self.atime
pp.text "atime="; pp.pp t
pp.breakable; pp.text "(#{t.tv_sec})"
- pp.text ','
}
- pp.breakable
+ pp.comma_breakable
pp.group {
t = self.mtime
pp.text "mtime="; pp.pp t
pp.breakable; pp.text "(#{t.tv_sec})"
- pp.text ','
}
- pp.breakable
+ pp.comma_breakable
pp.group {
t = self.ctime
pp.text "ctime="; pp.pp t
pp.breakable; pp.text "(#{t.tv_sec})"
}
- pp.text ">"
}
end
end
diff --git a/lib/prettyprint.rb b/lib/prettyprint.rb
index 650ffb8482..e1b7e7c59a 100644
--- a/lib/prettyprint.rb
+++ b/lib/prettyprint.rb
@@ -18,16 +18,17 @@ multibyte characters which has columns diffrent to number of bytes,
non-string formatting, etc.
== class methods
---- PrettyPrint.new(output[, maxwidth[, newline]]) [{|width| ...}]
+--- PrettyPrint.new([output[, maxwidth[, newline]]]) [{|width| ...}]
creates a buffer for pretty printing.
- ((|output|)) is a output target. It should have a (({<<})) method
- which accepts
+ ((|output|)) is a 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})).
+ the result of a given block for (({PrettyPrint.new})).
((|maxwidth|)) specifies maximum line length.
If it is not specified, 79 is assumed.
@@ -40,6 +41,16 @@ non-string formatting, etc.
The block is used to generate spaces.
(({{|width| ' ' * width}})) is used if it is not given.
+--- PrettyPrint.format([output[, maxwidth[, newline[, genspace]]]]) {|pp| ...}
+ is a convenience method which is same as follows:
+
+ begin
+ pp = PrettyPrint.format(output, maxwidth, newline, &genspace)
+ ...
+ pp.flush
+ output
+ end
+
== methods
--- text(obj[, width])
adds ((|obj|)) as a text of ((|width|)) columns in width.
@@ -61,16 +72,37 @@ non-string formatting, etc.
increases left margin after newline with ((|indent|)) for line breaks added
in the block.
---- group {...}
+--- group([indent[, open_obj[, close_obj[, open_width[, close_width]]]]]) {...}
groups line break hints added in the block.
The line break hints are all to be breaked 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
+ at first.
+ If ((|close_obj|)) is specified, (({text close_obj, close_width})) is
+ called at last.
+
--- flush
outputs buffered data.
-== Bugs
-* Current API is for minimalists. More useful methods are required.
+--- first?
+ is a predicate to test the call is a first call to (({first?})) with
+ current group.
+ It is useful to format comma separated values as:
+
+ pp.group(1, '[', ']') {
+ xxx.each {|yyy|
+ unless pp.first?
+ pp.text ','
+ pp.breakable
+ end
+ ... pretty printing yyy ...
+ }
+ }
+== Bugs
* Box based formatting? Other (better) model/algorithm?
== References
@@ -83,7 +115,14 @@ Philip Wadler, A prettier printer, March 1998,
=end
class PrettyPrint
- def initialize(output, maxwidth=79, newline="\n", &genspace)
+ def PrettyPrint.format(output='', maxwidth=79, newline="\n", genspace=lambda {|n| ' ' * n})
+ pp = PrettyPrint.new(output, maxwidth, newline, &genspace)
+ yield pp
+ pp.flush
+ output
+ end
+
+ def initialize(output='', maxwidth=79, newline="\n", &genspace)
@output = output
@maxwidth = maxwidth
@newline = newline
@@ -105,18 +144,22 @@ class PrettyPrint
@group_stack.last
end
+ def first?
+ current_group.first?
+ end
+
def break_outmost_groups
while @maxwidth < @output_width + @buffer_width
return unless group = @group_queue.deq
until group.breakables.empty?
- data = @buffer.shift
- @output_width = data.output(@output, @output_width)
- @buffer_width -= data.width
+ data = @buffer.shift
+ @output_width = data.output(@output, @output_width)
+ @buffer_width -= data.width
end
while !@buffer.empty? && Text === @buffer.first
- text = @buffer.shift
- @output_width = text.output(@output, @output_width)
- @buffer_width -= text.width
+ text = @buffer.shift
+ @output_width = text.output(@output, @output_width)
+ @buffer_width -= text.width
end
end
end
@@ -129,7 +172,7 @@ class PrettyPrint
text = @buffer.last
unless Text === text
text = Text.new
- @buffer << text
+ @buffer << text
end
text.add(obj, width)
@buffer_width += width
@@ -137,6 +180,10 @@ class PrettyPrint
end
end
+ def fill_breakable(sep=' ', width=sep.length)
+ group { breakable sep, width }
+ end
+
def breakable(sep=' ', width=sep.length)
group = @group_stack.last
if group.break?
@@ -152,7 +199,17 @@ class PrettyPrint
end
end
- def group
+ def group(indent=0, open_obj='', close_obj='', open_width=open_obj.length, close_width=close_obj.length)
+ text open_obj, open_width
+ group_sub {
+ nest(indent) {
+ yield
+ }
+ }
+ text close_obj, close_width
+ end
+
+ def group_sub
group = Group.new(@group_stack.last.depth + 1)
@group_stack.push group
@group_queue.enq group
@@ -215,13 +272,13 @@ class PrettyPrint
def output(out, output_width)
@group.breakables.shift
if @group.break?
- out << @pp.newline
- out << @pp.genspace.call(@indent)
- @indent
+ out << @pp.newline
+ out << @pp.genspace.call(@indent)
+ @indent
else
- @pp.group_queue.delete @group if @group.breakables.empty?
- out << @obj
- output_width + @width
+ @pp.group_queue.delete @group if @group.breakables.empty?
+ out << @obj
+ output_width + @width
end
end
end
@@ -241,6 +298,15 @@ class PrettyPrint
def break?
@break
end
+
+ def first?
+ if defined? @first
+ false
+ else
+ @first = false
+ true
+ end
+ end
end
class GroupQueue
@@ -257,15 +323,15 @@ class PrettyPrint
def deq
@queue.each {|gs|
- (gs.length-1).downto(0) {|i|
- unless gs[i].breakables.empty?
- group = gs.slice!(i, 1).first
- group.break
- return group
- end
- }
- gs.each {|group| group.break}
- gs.clear
+ (gs.length-1).downto(0) {|i|
+ unless gs[i].breakables.empty?
+ group = gs.slice!(i, 1).first
+ group.break
+ return group
+ end
+ }
+ gs.each {|group| group.break}
+ gs.clear
}
return nil
end
@@ -283,27 +349,29 @@ if __FILE__ == $0
class WadlerExample < RUNIT::TestCase
def setup
@tree = Tree.new("aaaa", Tree.new("bbbbb", Tree.new("ccc"),
- Tree.new("dd")),
- Tree.new("eee"),
- Tree.new("ffff", Tree.new("gg"),
- Tree.new("hhh"),
- Tree.new("ii")))
+ Tree.new("dd")),
+ Tree.new("eee"),
+ Tree.new("ffff", Tree.new("gg"),
+ Tree.new("hhh"),
+ Tree.new("ii")))
end
def hello(width)
- out = ''
- hello = PrettyPrint.new(out, width)
- hello.group {
- hello.group {
- hello.group {
- hello.group {
- hello.text 'hello'
- hello.breakable; hello.text 'a'}
- hello.breakable; hello.text 'b'}
- hello.breakable; hello.text 'c'}
- hello.breakable; hello.text 'd'}
- hello.flush
- out
+ PrettyPrint.format('', width) {|hello|
+ hello.group {
+ hello.group {
+ hello.group {
+ hello.group {
+ hello.text 'hello'
+ hello.breakable; hello.text 'a'
+ }
+ hello.breakable; hello.text 'b'
+ }
+ hello.breakable; hello.text 'c'
+ }
+ hello.breakable; hello.text 'd'
+ }
+ }
end
def test_hello_00_06
@@ -356,11 +424,7 @@ End
end
def tree(width)
- out = ''
- pp = PrettyPrint.new(out, width)
- @tree.show(pp)
- pp.flush
- out
+ PrettyPrint.format('', width) {|pp| @tree.show(pp)}
end
def test_tree_00_19
@@ -405,11 +469,7 @@ End
end
def tree_alt(width)
- out = ''
- pp = PrettyPrint.new(out, width)
- @tree.altshow(pp)
- pp.flush
- out
+ PrettyPrint.format('', width) {|pp| @tree.altshow(pp)}
end
def test_tree_alt_00_18
@@ -469,55 +529,55 @@ End
class Tree
def initialize(string, *children)
@string = string
- @children = children
+ @children = children
end
def show(pp)
- pp.group {
- pp.text @string
- pp.nest(@string.length) {
- unless @children.empty?
- pp.text '['
- pp.nest(1) {
- first = true
- @children.each {|t|
- if first
- first = false
- else
- pp.text ','
- pp.breakable
- end
- t.show(pp)
- }
- }
- pp.text ']'
- end
- }
- }
+ pp.group {
+ pp.text @string
+ pp.nest(@string.length) {
+ unless @children.empty?
+ pp.text '['
+ pp.nest(1) {
+ first = true
+ @children.each {|t|
+ if first
+ first = false
+ else
+ pp.text ','
+ pp.breakable
+ end
+ t.show(pp)
+ }
+ }
+ pp.text ']'
+ end
+ }
+ }
end
def altshow(pp)
- pp.group {
- pp.text @string
- unless @children.empty?
- pp.text '['
- pp.nest(2) {
- pp.breakable
- first = true
- @children.each {|t|
- if first
- first = false
- else
- pp.text ','
- pp.breakable
- end
- t.altshow(pp)
- }
- }
- pp.breakable
- pp.text ']'
- end
- }
+ pp.group {
+ pp.text @string
+ unless @children.empty?
+ pp.text '['
+ pp.nest(2) {
+ pp.breakable
+ first = true
+ @children.each {|t|
+ if first
+ first = false
+ else
+ pp.text ','
+ pp.breakable
+ end
+ t.altshow(pp)
+ }
+ }
+ pp.breakable
+ pp.text ']'
+ end
+ }
end
end
@@ -525,31 +585,29 @@ End
class StrictPrettyExample < RUNIT::TestCase
def prog(width)
- out = ''
- pp = PrettyPrint.new(out, width)
- pp.group {
- pp.group {pp.nest(2) {
- pp.text "if"; pp.breakable;
- pp.group {
- pp.nest(2) {
- pp.group {pp.text "a"; pp.breakable; pp.text "=="}
- pp.breakable; pp.text "b"}}}}
- pp.breakable
- pp.group {pp.nest(2) {
- pp.text "then"; pp.breakable;
- pp.group {
- pp.nest(2) {
- pp.group {pp.text "a"; pp.breakable; pp.text "<<"}
- pp.breakable; pp.text "2"}}}}
- pp.breakable
- pp.group {pp.nest(2) {
- pp.text "else"; pp.breakable;
- pp.group {
- pp.nest(2) {
- pp.group {pp.text "a"; pp.breakable; pp.text "+"}
- pp.breakable; pp.text "b"}}}}}
- pp.flush
- out
+ PrettyPrint.format('', width) {|pp|
+ pp.group {
+ pp.group {pp.nest(2) {
+ pp.text "if"; pp.breakable;
+ pp.group {
+ pp.nest(2) {
+ pp.group {pp.text "a"; pp.breakable; pp.text "=="}
+ pp.breakable; pp.text "b"}}}}
+ pp.breakable
+ pp.group {pp.nest(2) {
+ pp.text "then"; pp.breakable;
+ pp.group {
+ pp.nest(2) {
+ pp.group {pp.text "a"; pp.breakable; pp.text "<<"}
+ pp.breakable; pp.text "2"}}}}
+ pp.breakable
+ pp.group {pp.nest(2) {
+ pp.text "else"; pp.breakable;
+ pp.group {
+ pp.nest(2) {
+ pp.group {pp.text "a"; pp.breakable; pp.text "+"}
+ pp.breakable; pp.text "b"}}}}}
+ }
end
def test_00_04
@@ -672,34 +730,31 @@ End
class TailGroup < RUNIT::TestCase
def test_1
- out = ''
- pp = PrettyPrint.new(out, 10)
- pp.group {
- pp.group {
- pp.text "abc"
- pp.breakable
- pp.text "def"
- }
- pp.group {
- pp.text "ghi"
- pp.breakable
- pp.text "jkl"
- }
+ out = PrettyPrint.format('', 10) {|pp|
+ pp.group {
+ pp.group {
+ pp.text "abc"
+ pp.breakable
+ pp.text "def"
+ }
+ pp.group {
+ pp.text "ghi"
+ pp.breakable
+ pp.text "jkl"
+ }
+ }
}
- pp.flush
assert_equal("abc defghi\njkl", out)
end
end
class NonString < RUNIT::TestCase
def format(width)
- out = []
- pp = PrettyPrint.new(out, width, 'newline') {|n| "#{n} spaces"}
- pp.text(3, 3)
- pp.breakable(1, 1)
- pp.text(3, 3)
- pp.flush
- out
+ PrettyPrint.format([], width, 'newline', lambda {|n| "#{n} spaces"}) {|pp|
+ pp.text(3, 3)
+ pp.breakable(1, 1)
+ pp.text(3, 3)
+ }
end
def test_6
@@ -714,25 +769,23 @@ End
class Fill < RUNIT::TestCase
def format(width)
- out = ''
- pp = PrettyPrint.new(out, width)
- pp.group {
- pp.text 'abc'
- pp.group { pp.breakable }
- pp.text 'def'
- pp.group { pp.breakable }
- pp.text 'ghi'
- pp.group { pp.breakable }
- pp.text 'jkl'
- pp.group { pp.breakable }
- pp.text 'mno'
- pp.group { pp.breakable }
- pp.text 'pqr'
- pp.group { pp.breakable }
- pp.text 'stu'
+ PrettyPrint.format('', width) {|pp|
+ pp.group {
+ pp.text 'abc'
+ pp.fill_breakable
+ pp.text 'def'
+ pp.fill_breakable
+ pp.text 'ghi'
+ pp.fill_breakable
+ pp.text 'jkl'
+ pp.fill_breakable
+ pp.text 'mno'
+ pp.fill_breakable
+ pp.text 'pqr'
+ pp.fill_breakable
+ pp.text 'stu'
+ }
}
- pp.flush
- out
end
def test_00_06