summaryrefslogtreecommitdiff
path: root/lib/pp.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pp.rb')
-rw-r--r--lib/pp.rb993
1 files changed, 568 insertions, 425 deletions
diff --git a/lib/pp.rb b/lib/pp.rb
index 9babc1e7a9..5fd29a373a 100644
--- a/lib/pp.rb
+++ b/lib/pp.rb
@@ -1,212 +1,293 @@
-# $Id$
+# frozen_string_literal: true
-=begin
-= Pretty-printer for Ruby objects.
-
-== Which seems better?
-
-non-pretty-printed output by (({p})) is:
- #<PP:0x81fedf0 @genspace=#<Proc:0x81feda0>, @group_queue=#<PrettyPrint::GroupQueue:0x81fed3c @queue=[[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], []]>, @buffer=[], @newline="\n", @group_stack=[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], @buffer_width=0, @indent=0, @maxwidth=79, @output_width=2, @output=#<IO:0x8114ee4>>
-
-pretty-printed output by (({pp})) is:
- #<PP:0x81fedf0
- @buffer=[],
- @buffer_width=0,
- @genspace=#<Proc:0x81feda0>,
- @group_queue=
- #<PrettyPrint::GroupQueue:0x81fed3c
- @queue=
- [[#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
- []]>,
- @group_stack=
- [#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
- @indent=0,
- @maxwidth=79,
- @newline="\n",
- @output=#<IO:0x8114ee4>,
- @output_width=2>
-
-I like the latter. If you do too, this library is for you.
-
-== Usage
-
-: pp(obj)
- output ((|obj|)) to (({$>})) in pretty printed format.
-
- It returns (({nil})).
-
-== Output Customization
-To define your customized pretty printing function for your classes,
-redefine a method (({pretty_print(((|pp|)))})) in the class.
-It takes an argument ((|pp|)) which is an instance of the class ((<PP>)).
-The method should use PP#text, PP#breakable, PP#nest, PP#group and
-PP#pp to print the object.
-
-= PP
-== super class
-((<PrettyPrint>))
-
-== class methods
---- PP.pp(obj[, out[, width]])
- outputs ((|obj|)) to ((|out|)) in pretty printed format of
- ((|width|)) columns in width.
-
- If ((|out|)) is ommitted, (({$>})) is assumed.
- If ((|width|)) is ommitted, 79 is assumed.
-
- PP.pp returns ((|out|)).
-
---- PP.singleline_pp(obj[, out])
- outputs ((|obj|)) to ((|out|)) like (({PP.pp})) but with no indent and
- newline.
-
- PP.singleline_pp returns ((|out|)).
-
---- PP.sharing_detection
- returns the sharing detection flag as a boolean value.
- It is false by default.
-
---- PP.sharing_detection = boolean_value
- sets the sharing detection flag.
-
-== methods
---- pp(obj)
- adds ((|obj|)) to the pretty printing buffer
- using Object#pretty_print or Object#pretty_print_cycle.
-
- Object#pretty_print_cycle 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
- line break hints.
-
- This module provides predefined pretty_print() methods for some of
- the most commonly used built-in classes for convenience.
-
---- pretty_print_cycle(pp)
- is a default pretty printing method for general objects that are
- detected as part of a cycle.
-
---- pretty_print_instance_variables
- returns a sorted array of instance variable names.
-
- This method should return an array of names of instance variables as symbols or strings as:
- (({[:@a, :@b]})).
-
---- pretty_print_inspect
- is (({inspect})) implementation using (({pretty_print})).
- If you implement (({pretty_print})), it can be used as follows.
-
- alias inspect pretty_print_inspect
+require 'prettyprint'
-== AUTHOR
-Tanaka Akira <akr@m17n.org>
-=end
+##
+# A pretty-printer for Ruby objects.
+#
+##
+# == What PP Does
+#
+# Standard output by #p returns this:
+# #<PP:0x81fedf0 @genspace=#<Proc:0x81feda0>, @group_queue=#<PrettyPrint::GroupQueue:0x81fed3c @queue=[[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], []]>, @buffer=[], @newline="\n", @group_stack=[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], @buffer_width=0, @indent=0, @maxwidth=79, @output_width=2, @output=#<IO:0x8114ee4>>
+#
+# Pretty-printed output returns this:
+# #<PP:0x81fedf0
+# @buffer=[],
+# @buffer_width=0,
+# @genspace=#<Proc:0x81feda0>,
+# @group_queue=
+# #<PrettyPrint::GroupQueue:0x81fed3c
+# @queue=
+# [[#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
+# []]>,
+# @group_stack=
+# [#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
+# @indent=0,
+# @maxwidth=79,
+# @newline="\n",
+# @output=#<IO:0x8114ee4>,
+# @output_width=2>
+#
+##
+# == Usage
+#
+# pp(obj) #=> obj
+# pp obj #=> obj
+# pp(obj1, obj2, ...) #=> [obj1, obj2, ...]
+# pp() #=> nil
+#
+# Output <tt>obj(s)</tt> to <tt>$></tt> in pretty printed format.
+#
+# It returns <tt>obj(s)</tt>.
+#
+##
+# == Output Customization
+#
+# To define a customized pretty printing function for your classes,
+# redefine method <code>#pretty_print(pp)</code> in the class.
+# Note that <code>require 'pp'</code> is needed before redefining <code>#pretty_print(pp)</code>.
+#
+# <code>#pretty_print</code> takes the +pp+ argument, which is an instance of the PP class.
+# The method uses #text, #breakable, #nest, #group and #pp to print the
+# object.
+#
+##
+# == Pretty-Print JSON
+#
+# To pretty-print JSON refer to JSON#pretty_generate.
+#
+##
+# == Author
+# Tanaka Akira <akr@fsij.org>
-require 'prettyprint'
+class PP < PrettyPrint
-module Kernel
- private
- def pp(*objs)
- objs.each {|obj|
- PP.pp(obj)
- }
- nil
+ # The version string
+ VERSION = "0.6.3"
+
+ # Returns the usable width for +out+.
+ # As the width of +out+:
+ # 1. If +out+ is assigned to a tty device, its width is used.
+ # 2. Otherwise, or it could not get the value, the +COLUMN+
+ # environment variable is assumed to be set to the width.
+ # 3. If +COLUMN+ is not set to a non-zero number, 80 is assumed.
+ #
+ # And finally, returns the above width value - 1.
+ # * This -1 is for Windows command prompt, which moves the cursor to
+ # the next line if it reaches the last column.
+ def PP.width_for(out)
+ begin
+ require 'io/console'
+ _, width = out.winsize
+ rescue LoadError, NoMethodError, SystemCallError
+ end
+ (width || ENV['COLUMNS']&.to_i&.nonzero? || 80) - 1
end
- module_function :pp
-end
-class PP < PrettyPrint
- def PP.pp(obj, out=$>, width=79)
- pp = PP.new(out, width)
- pp.guard_inspect_key {pp.pp obj}
- pp.flush
- #$pp = pp
+ # Outputs +obj+ to +out+ in pretty printed format of
+ # +width+ columns in width.
+ #
+ # If +out+ is omitted, <code>$></code> is assumed.
+ # If +width+ is omitted, the width of +out+ is assumed (see
+ # width_for).
+ #
+ # PP.pp returns +out+.
+ def PP.pp(obj, out=$>, width=width_for(out))
+ q = new(out, width)
+ q.guard_inspect_key {q.pp obj}
+ q.flush
+ #$pp = q
out << "\n"
end
+ # Outputs +obj+ to +out+ like PP.pp but with no indent and
+ # newline.
+ #
+ # PP.singleline_pp returns +out+.
def PP.singleline_pp(obj, out=$>)
- pp = SingleLine.new(out)
- pp.guard_inspect_key {pp.pp obj}
- pp.flush
+ q = SingleLine.new(out)
+ q.guard_inspect_key {q.pp obj}
+ q.flush
out
end
- @sharing_detection = false
- class << self
- attr_accessor :sharing_detection
+ # :stopdoc:
+ def PP.mcall(obj, mod, meth, *args, &block)
+ mod.instance_method(meth).bind_call(obj, *args, &block)
+ end
+ # :startdoc:
+
+ if defined? ::Ractor
+ class << self
+ # Returns the sharing detection flag as a boolean value.
+ # It is false (nil) by default.
+ def sharing_detection
+ Ractor.current[:pp_sharing_detection]
+ end
+ # Sets the sharing detection flag to b.
+ def sharing_detection=(b)
+ Ractor.current[:pp_sharing_detection] = b
+ end
+ end
+ else
+ @sharing_detection = false
+ class << self
+ # Returns the sharing detection flag as a boolean value.
+ # It is false by default.
+ attr_accessor :sharing_detection
+ end
end
+ # Module that defines helper methods for pretty_print.
module PPMethods
- InspectKey = :__inspect_key__
+ # Yields to a block
+ # and preserves the previous set of objects being printed.
def guard_inspect_key
- if Thread.current[InspectKey] == nil
- Thread.current[InspectKey] = []
- end
-
- save = Thread.current[InspectKey]
-
+ recursive_state = Thread.current[:__recursive_key__] ||= {}.compare_by_identity
+ save = recursive_state[:inspect] ||= {}.compare_by_identity
begin
- Thread.current[InspectKey] = []
+ recursive_state[:inspect] = {}.compare_by_identity
yield
ensure
- Thread.current[InspectKey] = save
+ recursive_state[:inspect] = save
end
end
+ # Check whether the object_id +id+ is in the current buffer of objects
+ # to be pretty printed. Used to break cycles in chains of objects to be
+ # pretty printed.
+ def check_inspect_key(id)
+ recursive_state = Thread.current[:__recursive_key__] or return false
+ recursive_state[:inspect]&.include?(id)
+ end
+
+ # Adds the object_id +id+ to the set of objects being pretty printed, so
+ # as to not repeat objects.
+ def push_inspect_key(id)
+ Thread.current[:__recursive_key__][:inspect][id] = true
+ end
+
+ # Removes an object from the set of objects being pretty printed.
+ def pop_inspect_key(id)
+ Thread.current[:__recursive_key__][:inspect].delete id
+ end
+
+ private def guard_inspect(object) # :nodoc:
+ recursive_state = Thread.current[:__recursive_key__]
+
+ if recursive_state&.key?(:inspect)
+ begin
+ push_inspect_key(object)
+ yield
+ ensure
+ pop_inspect_key(object) unless PP.sharing_detection
+ end
+ else
+ guard_inspect_key do
+ push_inspect_key(object)
+ yield
+ end
+ end
+ end
+
+ # Adds +obj+ to the pretty printing buffer
+ # using Object#pretty_print or Object#pretty_print_cycle.
+ #
+ # Object#pretty_print_cycle is used when +obj+ is already
+ # printed, a.k.a the object reference chain has a cycle.
def pp(obj)
- id = obj.__id__
+ # If obj is a Delegator then use the object being delegated to for cycle
+ # detection
+ obj = obj.__getobj__ if defined?(::Delegator) and ::Delegator === obj
- if Thread.current[InspectKey].include? id
+ if check_inspect_key(obj)
group {obj.pretty_print_cycle self}
return
end
- begin
- Thread.current[InspectKey] << id
- group {obj.pretty_print self}
- ensure
- Thread.current[InspectKey].pop unless PP.sharing_detection
+ guard_inspect(obj) do
+ group do
+ obj.pretty_print self
+ rescue NoMethodError
+ text Kernel.instance_method(:inspect).bind_call(obj)
+ end
end
end
- def object_group(obj, &block)
+ # A convenience method which is same as follows:
+ #
+ # group(1, '#<' + obj.class.name, '>') { ... }
+ def object_group(obj, &block) # :yield:
group(1, '#<' + obj.class.name, '>', &block)
end
+ # A convenience method, like object_group, but also reformats the Object's
+ # object_id.
def object_address_group(obj, &block)
- group(1, sprintf('#<%s:0x%x', obj.class.to_s, obj.__id__ * 2), '>', &block)
+ str = Kernel.instance_method(:to_s).bind_call(obj)
+ str.chomp!('>')
+ group(1, str, '>', &block)
end
+ # A convenience method which is same as follows:
+ #
+ # text ','
+ # breakable
def comma_breakable
text ','
breakable
end
+ # Adds a separated list.
+ # The list is separated by comma with breakable space, by default.
+ #
+ # #seplist iterates the +list+ using +iter_method+.
+ # It yields each object to the block given for #seplist.
+ # The procedure +separator_proc+ is called between each yields.
+ #
+ # If the iteration is zero times, +separator_proc+ is not called at all.
+ #
+ # If +separator_proc+ is nil or not given,
+ # +lambda { comma_breakable }+ is used.
+ # If +iter_method+ is not given, :each is used.
+ #
+ # For example, following 3 code fragments has similar effect.
+ #
+ # q.seplist([1,2,3]) {|v| xxx v }
+ #
+ # q.seplist([1,2,3], lambda { q.comma_breakable }, :each) {|v| xxx v }
+ #
+ # xxx 1
+ # q.comma_breakable
+ # xxx 2
+ # q.comma_breakable
+ # xxx 3
+ def seplist(list, sep=nil, iter_method=:each) # :yield: element
+ sep ||= lambda { comma_breakable }
+ first = true
+ kwsplat = EMPTY_KWHASH
+ list.__send__(iter_method) {|*v|
+ if first
+ first = false
+ else
+ sep.call
+ end
+ kwsplat ? yield(*v, **kwsplat) : yield(*v)
+ }
+ end
+ EMPTY_KWHASH = if RUBY_VERSION >= "3.0" # :nodoc:
+ {}.freeze
+ end
+ private_constant :EMPTY_KWHASH
+
+ # A present standard failsafe for pretty printing any given Object
def pp_object(obj)
object_address_group(obj) {
- obj.pretty_print_instance_variables.each {|v|
- v = v.to_s if Symbol === v
- text ',' unless first?
+ seplist(obj.pretty_print_instance_variables, lambda { text ',' }) {|v|
breakable
+ v = v.to_s if Symbol === v
text v
text '='
group(1) {
@@ -217,140 +298,273 @@ class PP < PrettyPrint
}
end
+ # A pretty print for a Hash
def pp_hash(obj)
group(1, '{', '}') {
- obj.each {|k, v|
- comma_breakable unless first?
+ seplist(obj, nil, :each_pair) {|k, v|
group {
- pp k
- text '=>'
- group(1) {
- breakable ''
- pp v
- }
+ pp_hash_pair k, v
}
}
}
end
+
+ if RUBY_VERSION >= '3.4.'
+ # A pretty print for a pair of Hash
+ def pp_hash_pair(k, v)
+ if Symbol === k
+ if k.inspect.match?(%r[\A:["$@!]|[%&*+\-\/<=>@\]^`|~]\z])
+ k = k.to_s.inspect
+ end
+ text "#{k}:"
+ else
+ pp k
+ text ' '
+ text '=>'
+ end
+ group(1) {
+ breakable
+ pp v
+ }
+ end
+ else
+ def pp_hash_pair(k, v)
+ pp k
+ text '=>'
+ group(1) {
+ breakable ''
+ pp v
+ }
+ end
+ end
end
include PPMethods
- class SingleLine < PrettyPrint::SingleLine
+ class SingleLine < PrettyPrint::SingleLine # :nodoc:
include PPMethods
end
- module ObjectMixin
+ module ObjectMixin # :nodoc:
# 1. specific pretty_print
# 2. specific inspect
- # 3. specific to_s if instance variable is empty
- # 4. generic pretty_print
-
- def pretty_print(pp)
- if /\(Kernel\)#/ !~ method(:inspect).inspect
- pp.text self.inspect
- elsif /\(Kernel\)#/ !~ method(:to_s).inspect && instance_variables.empty?
- pp.text self.to_s
+ # 3. generic pretty_print
+
+ # 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
+ # line break hints.
+ #
+ # This module provides predefined #pretty_print methods for some of
+ # the most commonly used built-in classes for convenience.
+ def pretty_print(q)
+ umethod_method = Object.instance_method(:method)
+ begin
+ inspect_method = umethod_method.bind_call(self, :inspect)
+ rescue NameError
+ end
+ if inspect_method && inspect_method.owner != Kernel
+ q.text self.inspect
+ elsif !inspect_method && self.respond_to?(:inspect)
+ q.text self.inspect
else
- pp.pp_object(self)
+ q.pp_object(self)
end
end
- def pretty_print_cycle(pp)
- pp.object_address_group(self) {
- pp.breakable
- pp.text '...'
+ # A default pretty printing method for general objects that are
+ # detected as part of a cycle.
+ def pretty_print_cycle(q)
+ q.object_address_group(self) {
+ q.breakable
+ q.text '...'
}
end
+ # Returns a sorted array of instance variable names.
+ #
+ # This method should return an array of names of instance variables as symbols or strings as:
+ # +[:@a, :@b]+.
def pretty_print_instance_variables
- instance_variables.sort
+ ivars = respond_to?(:instance_variables_to_inspect, true) ? instance_variables_to_inspect || instance_variables : instance_variables
+ ivars.sort
end
+ # Is #inspect implementation using #pretty_print.
+ # If you implement #pretty_print, it can be used as follows.
+ #
+ # alias inspect pretty_print_inspect
+ #
+ # However, doing this requires that every class that #inspect is called on
+ # implement #pretty_print, or a RuntimeError will be raised.
def pretty_print_inspect
- if /\(PP::ObjectMixin\)#/ =~ method(:pretty_print).inspect
- raise "pretty_print is not overriden."
+ if Object.instance_method(:method).bind_call(self, :pretty_print).owner == PP::ObjectMixin
+ raise "pretty_print is not overridden for #{self.class}"
end
- PP.singleline_pp(self, '')
+ PP.singleline_pp(self, ''.dup)
end
end
end
-class Array
- def pretty_print(pp)
- pp.group(1, '[', ']') {
- self.each {|v|
- pp.comma_breakable unless pp.first?
- pp.pp v
+class Array # :nodoc:
+ def pretty_print(q) # :nodoc:
+ q.group(1, '[', ']') {
+ q.seplist(self) {|v|
+ q.pp v
}
}
end
- def pretty_print_cycle(pp)
- pp.text(empty? ? '[]' : '[...]')
+ def pretty_print_cycle(q) # :nodoc:
+ q.text(empty? ? '[]' : '[...]')
end
end
-class Hash
- def pretty_print(pp)
- pp.pp_hash self
+class Hash # :nodoc:
+ def pretty_print(q) # :nodoc:
+ q.pp_hash self
end
- def pretty_print_cycle(pp)
- pp.text(empty? ? '{}' : '{...}')
+ def pretty_print_cycle(q) # :nodoc:
+ q.text(empty? ? '{}' : '{...}')
end
end
-class << ENV
- def pretty_print(pp)
- pp.pp_hash self
+if defined?(Set)
+ if set_pp = Set.instance_method(:initialize).source_location
+ set_pp = !set_pp.first.end_with?("/set.rb") # not defined in set.rb
+ else
+ set_pp = true # defined in C
end
end
+class Set # :nodoc:
+ def pretty_print(pp) # :nodoc:
+ pp.group(1, "#{self.class.name}[", ']') {
+ pp.seplist(self) { |o|
+ pp.pp o
+ }
+ }
+ end
+
+ def pretty_print_cycle(pp) # :nodoc:
+ name = self.class.name
+ pp.text(empty? ? "#{name}[]" : "#{name}[...]")
+ end
+end if set_pp
-class Struct
- def pretty_print(pp)
- pp.object_group(self) {
- self.members.each {|member|
- pp.text "," unless pp.first?
- pp.breakable
- pp.text member.to_s
- pp.text '='
- pp.group(1) {
- pp.breakable ''
- pp.pp self[member]
+class << ENV # :nodoc:
+ def pretty_print(q) # :nodoc:
+ h = {}
+ ENV.keys.sort.each {|k|
+ h[k] = ENV[k]
+ }
+ q.pp_hash h
+ end
+end
+
+class Struct # :nodoc:
+ def pretty_print(q) # :nodoc:
+ q.group(1, sprintf("#<struct %s", PP.mcall(self, Kernel, :class).name), '>') {
+ q.seplist(PP.mcall(self, Struct, :members), lambda { q.text "," }) {|member|
+ q.breakable
+ q.text member.to_s
+ q.text '='
+ q.group(1) {
+ q.breakable ''
+ q.pp self[member]
}
}
}
end
- def pretty_print_cycle(pp)
- pp.text sprintf("#<%s:...>", self.class.name)
+ def pretty_print_cycle(q) # :nodoc:
+ q.text sprintf("#<struct %s:...>", PP.mcall(self, Kernel, :class).name)
end
end
-class Range
- def pretty_print(pp)
- pp.pp self.begin
- pp.breakable ''
- pp.text(self.exclude_end? ? '...' : '..')
- pp.breakable ''
- pp.pp self.end
+verbose, $VERBOSE = $VERBOSE, nil
+begin
+ has_data_define = defined?(Data.define)
+ensure
+ $VERBOSE = verbose
+end
+
+class Data # :nodoc:
+ def pretty_print(q) # :nodoc:
+ class_name = PP.mcall(self, Kernel, :class).name
+ class_name = " #{class_name}" if class_name
+ q.group(1, "#<data#{class_name}", '>') {
+
+ members = PP.mcall(self, Kernel, :class).members
+ values = []
+ members.select! do |member|
+ begin
+ values << __send__(member)
+ true
+ rescue NoMethodError
+ false
+ end
+ end
+
+ q.seplist(members.zip(values), lambda { q.text "," }) {|(member, value)|
+ q.breakable
+ q.text member.to_s
+ q.text '='
+ q.group(1) {
+ q.breakable ''
+ q.pp value
+ }
+ }
+ }
+ end
+
+ def pretty_print_cycle(q) # :nodoc:
+ q.text sprintf("#<data %s:...>", PP.mcall(self, Kernel, :class).name)
+ end
+end if has_data_define
+
+class Range # :nodoc:
+ def pretty_print(q) # :nodoc:
+ begin_nil = self.begin == nil
+ end_nil = self.end == nil
+ q.pp self.begin if !begin_nil || end_nil
+ q.breakable ''
+ q.text(self.exclude_end? ? '...' : '..')
+ q.breakable ''
+ q.pp self.end if !end_nil || begin_nil
end
end
-class File
- class Stat
- def pretty_print(pp)
- require 'etc.so'
- 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 {
+class String # :nodoc:
+ def pretty_print(q) # :nodoc:
+ lines = self.lines
+ if lines.size > 1
+ q.group(0, '', '') do
+ q.seplist(lines, lambda { q.text ' +'; q.breakable }) do |v|
+ q.pp v
+ end
+ end
+ else
+ q.text inspect
+ end
+ end
+end
+
+class File < IO # :nodoc:
+ class Stat # :nodoc:
+ def pretty_print(q) # :nodoc:
+ require 'etc'
+ q.object_group(self) {
+ q.breakable
+ q.text sprintf("dev=0x%x", self.dev); q.comma_breakable
+ q.text "ino="; q.pp self.ino; q.comma_breakable
+ q.group {
m = self.mode
- pp.text sprintf("mode=0%o", m)
- pp.breakable
- pp.text sprintf("(%s %c%c%c%c%c%c%c%c%c)",
+ q.text sprintf("mode=0%o", m)
+ q.breakable
+ q.text sprintf("(%s %c%c%c%c%c%c%c%c%c)",
self.ftype,
(m & 0400 == 0 ? ?- : ?r),
(m & 0200 == 0 ? ?- : ?w),
@@ -365,231 +579,160 @@ class File
(m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
(m & 01000 == 0 ? ?x : ?t)))
}
- pp.comma_breakable
- pp.text "nlink="; pp.pp self.nlink; pp.comma_breakable
- pp.group {
- pp.text "uid="; pp.pp self.uid
+ q.comma_breakable
+ q.text "nlink="; q.pp self.nlink; q.comma_breakable
+ q.group {
+ q.text "uid="; q.pp self.uid
begin
- name = Etc.getpwuid(self.uid).name
- pp.breakable; pp.text "(#{name})"
+ pw = Etc.getpwuid(self.uid)
rescue ArgumentError
end
+ if pw
+ q.breakable; q.text "(#{pw.name})"
+ end
}
- pp.comma_breakable
- pp.group {
- pp.text "gid="; pp.pp self.gid
+ q.comma_breakable
+ q.group {
+ q.text "gid="; q.pp self.gid
begin
- name = Etc.getgrgid(self.gid).name
- pp.breakable; pp.text "(#{name})"
+ gr = Etc.getgrgid(self.gid)
rescue ArgumentError
end
+ if gr
+ q.breakable; q.text "(#{gr.name})"
+ end
}
- 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)
+ q.comma_breakable
+ q.group {
+ q.text sprintf("rdev=0x%x", self.rdev)
+ if self.rdev_major && self.rdev_minor
+ q.breakable
+ q.text sprintf('(%d, %d)', self.rdev_major, self.rdev_minor)
+ end
}
- 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 {
+ q.comma_breakable
+ q.text "size="; q.pp self.size; q.comma_breakable
+ q.text "blksize="; q.pp self.blksize; q.comma_breakable
+ q.text "blocks="; q.pp self.blocks; q.comma_breakable
+ q.group {
t = self.atime
- pp.text "atime="; pp.pp t
- pp.breakable; pp.text "(#{t.tv_sec})"
+ q.text "atime="; q.pp t
+ q.breakable; q.text "(#{t.tv_sec})"
}
- pp.comma_breakable
- pp.group {
+ q.comma_breakable
+ q.group {
t = self.mtime
- pp.text "mtime="; pp.pp t
- pp.breakable; pp.text "(#{t.tv_sec})"
+ q.text "mtime="; q.pp t
+ q.breakable; q.text "(#{t.tv_sec})"
}
- pp.comma_breakable
- pp.group {
+ q.comma_breakable
+ q.group {
t = self.ctime
- pp.text "ctime="; pp.pp t
- pp.breakable; pp.text "(#{t.tv_sec})"
+ q.text "ctime="; q.pp t
+ q.breakable; q.text "(#{t.tv_sec})"
}
}
end
end
end
-class MatchData
- def pretty_print(pp)
- pp.object_group(self) {
- pp.breakable
- 1.upto(self.size) {|i|
- pp.breakable unless pp.first?
- pp.pp self[i-1]
+class MatchData # :nodoc:
+ def pretty_print(q) # :nodoc:
+ nc = []
+ self.regexp.named_captures.each {|name, indexes|
+ indexes.each {|i| nc[i] = name }
+ }
+ q.object_group(self) {
+ q.breakable
+ q.seplist(0...self.size, lambda { q.breakable }) {|i|
+ if i == 0
+ q.pp self[i]
+ else
+ if nc[i]
+ q.text nc[i]
+ else
+ q.pp i
+ end
+ q.text ':'
+ q.pp self[i]
+ end
}
}
end
end
-class Object
+if defined?(RubyVM::AbstractSyntaxTree)
+ class RubyVM::AbstractSyntaxTree::Node # :nodoc:
+ def pretty_print_children(q, names = [])
+ children.zip(names) do |c, n|
+ if n
+ q.breakable
+ q.text "#{n}:"
+ end
+ q.group(2) do
+ q.breakable
+ q.pp c
+ end
+ end
+ end
+
+ def pretty_print(q)
+ q.group(1, "(#{type}@#{first_lineno}:#{first_column}-#{last_lineno}:#{last_column}", ")") {
+ case type
+ when :SCOPE
+ pretty_print_children(q, %w"tbl args body")
+ when :ARGS
+ pretty_print_children(q, %w[pre_num pre_init opt first_post post_num post_init rest kw kwrest block])
+ when :DEFN
+ pretty_print_children(q, %w[mid body])
+ when :ARYPTN
+ pretty_print_children(q, %w[const pre rest post])
+ when :HSHPTN
+ pretty_print_children(q, %w[const kw kwrest])
+ else
+ pretty_print_children(q)
+ end
+ }
+ end
+ end
+end
+
+class Object < BasicObject # :nodoc:
include PP::ObjectMixin
end
[Numeric, Symbol, FalseClass, TrueClass, NilClass, Module].each {|c|
c.class_eval {
- def pretty_print_cycle(pp)
- pp.text inspect
+ def pretty_print_cycle(q)
+ q.text inspect
end
}
}
-if __FILE__ == $0
- require 'test/unit'
-
- class PPTest < Test::Unit::TestCase
- def test_list0123_12
- assert_equal("[0, 1, 2, 3]\n", PP.pp([0,1,2,3], '', 12))
- end
-
- def test_list0123_11
- assert_equal("[0,\n 1,\n 2,\n 3]\n", PP.pp([0,1,2,3], '', 11))
- end
- end
-
- class HasInspect
- def initialize(a)
- @a = a
- end
-
- def inspect
- return "<inspect:#{@a.inspect}>"
- end
- end
-
- class HasPrettyPrint
- def initialize(a)
- @a = a
- end
-
- def pretty_print(pp)
- pp.text "<pretty_print:"
- pp.pp @a
- pp.text ">"
- end
- end
-
- class HasBoth
- def initialize(a)
- @a = a
- end
-
- def inspect
- return "<inspect:#{@a.inspect}>"
- end
-
- def pretty_print(pp)
- pp.text "<pretty_print:"
- pp.pp @a
- pp.text ">"
- end
- end
-
- class PrettyPrintInspect < HasPrettyPrint
- alias inspect pretty_print_inspect
- end
-
- class PrettyPrintInspectWithoutPrettyPrint
- alias inspect pretty_print_inspect
- end
-
- class PPInspectTest < Test::Unit::TestCase
- def test_hasinspect
- a = HasInspect.new(1)
- assert_equal("<inspect:1>\n", PP.pp(a, ''))
- end
-
- def test_hasprettyprint
- a = HasPrettyPrint.new(1)
- assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
- end
-
- def test_hasboth
- a = HasBoth.new(1)
- assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
- end
-
- def test_pretty_print_inspect
- a = PrettyPrintInspect.new(1)
- assert_equal("<pretty_print:1>", a.inspect)
- a = PrettyPrintInspectWithoutPrettyPrint.new
- assert_raises(RuntimeError) { a.inspect }
- end
-
- def test_proc
- a = proc {1}
- assert_equal("#{a.inspect}\n", PP.pp(a, ''))
+[Numeric, FalseClass, TrueClass, Module].each {|c|
+ c.class_eval {
+ def pretty_print(q)
+ q.text inspect
end
+ }
+}
- def test_to_s_with_iv
- a = Object.new
- def a.to_s() "aaa" end
- a.instance_eval { @a = nil }
- result = PP.pp(a, '')
- assert_equal("#{a.inspect}\n", result)
- assert_match(/\A#<Object.*>\n\z/m, result)
- end
-
- def test_to_s_without_iv
- a = Object.new
- def a.to_s() "aaa" end
- result = PP.pp(a, '')
- assert_equal("#{a.inspect}\n", result)
- assert_equal("aaa\n", result)
- end
+module Kernel
+ # Returns a pretty printed object as a string.
+ #
+ # See the PP module for more information.
+ def pretty_inspect
+ PP.pp(self, ''.dup)
end
- class PPCycleTest < Test::Unit::TestCase
- def test_array
- a = []
- a << a
- assert_equal("[[...]]\n", PP.pp(a, ''))
- end
-
- def test_hash
- a = {}
- a[0] = a
- assert_equal("{0=>{...}}\n", PP.pp(a, ''))
- end
-
- S = Struct.new("S", :a, :b)
- def test_struct
- a = S.new(1,2)
- a.b = a
- assert_equal("#<Struct::S a=1, b=#<Struct::S:...>>\n", PP.pp(a, ''))
- end
-
- def test_object
- a = Object.new
- a.instance_eval {@a = a}
- assert_equal(a.inspect + "\n", PP.pp(a, ''))
- end
-
- def test_anonymous
- a = Class.new.new
- assert_equal(a.inspect + "\n", PP.pp(a, ''))
- end
-
- def test_withinspect
- a = []
- a << HasInspect.new(a)
- assert_equal("[<inspect:[...]>]\n", PP.pp(a, ''))
- end
-
- def test_share_nil
- begin
- PP.sharing_detection = true
- a = [nil, nil]
- assert_equal("[nil, nil]\n", PP.pp(a, ''))
- ensure
- PP.sharing_detection = false
- end
- end
+ # prints arguments in pretty form.
+ #
+ # +#pp+ returns argument(s).
+ def pp(*objs)
+ objs.each {|obj|
+ PP.pp(obj)
+ }
+ objs.size <= 1 ? objs.first : objs
end
+ module_function :pp
end