1 # frozen_string_literal: true
6 # A pretty-printer for Ruby objects.
11 # Standard output by #p returns this:
12 # #<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>>
14 # Pretty-printed output returns this:
18 # @genspace=#<Proc:0x81feda0>,
20 # #<PrettyPrint::GroupQueue:0x81fed3c
22 # [[#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
25 # [#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
29 # @output=#<IO:0x8114ee4>,
37 # pp(obj1, obj2, ...) #=> [obj1, obj2, ...]
40 # Output <tt>obj(s)</tt> to <tt>$></tt> in pretty printed format.
42 # It returns <tt>obj(s)</tt>.
45 # == Output Customization
47 # To define a customized pretty printing function for your classes,
48 # redefine method <code>#pretty_print(pp)</code> in the class.
49 # Note that <code>require 'pp'</code> is needed before redefining <code>#pretty_print(pp)</code>.
51 # <code>#pretty_print</code> takes the +pp+ argument, which is an instance of the PP class.
52 # The method uses #text, #breakable, #nest, #group and #pp to print the
56 # == Pretty-Print JSON
58 # To pretty-print JSON refer to JSON#pretty_generate.
62 # Tanaka Akira <akr@fsij.org>
64 class PP < PrettyPrint
68 # Returns the usable width for +out+.
69 # As the width of +out+:
70 # 1. If +out+ is assigned to a tty device, its width is used.
71 # 2. Otherwise, or it could not get the value, the +COLUMN+
72 # environment variable is assumed to be set to the width.
73 # 3. If +COLUMN+ is not set to a non-zero number, 80 is assumed.
75 # And finally, returns the above width value - 1.
76 # * This -1 is for Windows command prompt, which moves the cursor to
77 # the next line if it reaches the last column.
81 _, width = out.winsize
82 rescue LoadError, NoMethodError, SystemCallError
84 (width || ENV['COLUMNS']&.to_i&.nonzero? || 80) - 1
87 # Outputs +obj+ to +out+ in pretty printed format of
88 # +width+ columns in width.
90 # If +out+ is omitted, <code>$></code> is assumed.
91 # If +width+ is omitted, the width of +out+ is assumed (see
94 # PP.pp returns +out+.
95 def PP.pp(obj, out=$>, width=width_for(out))
97 q.guard_inspect_key {q.pp obj}
103 # Outputs +obj+ to +out+ like PP.pp but with no indent and
106 # PP.singleline_pp returns +out+.
107 def PP.singleline_pp(obj, out=$>)
108 q = SingleLine.new(out)
109 q.guard_inspect_key {q.pp obj}
115 def PP.mcall(obj, mod, meth, *args, &block)
116 mod.instance_method(meth).bind_call(obj, *args, &block)
122 # Returns the sharing detection flag as a boolean value.
123 # It is false (nil) by default.
124 def sharing_detection
125 Ractor.current[:pp_sharing_detection]
127 # Sets the sharing detection flag to b.
128 def sharing_detection=(b)
129 Ractor.current[:pp_sharing_detection] = b
133 @sharing_detection = false
135 # Returns the sharing detection flag as a boolean value.
136 # It is false by default.
137 attr_accessor :sharing_detection
144 # and preserves the previous set of objects being printed.
145 def guard_inspect_key
146 if Thread.current[:__recursive_key__] == nil
147 Thread.current[:__recursive_key__] = {}.compare_by_identity
150 if Thread.current[:__recursive_key__][:inspect] == nil
151 Thread.current[:__recursive_key__][:inspect] = {}.compare_by_identity
154 save = Thread.current[:__recursive_key__][:inspect]
157 Thread.current[:__recursive_key__][:inspect] = {}.compare_by_identity
160 Thread.current[:__recursive_key__][:inspect] = save
164 # Check whether the object_id +id+ is in the current buffer of objects
165 # to be pretty printed. Used to break cycles in chains of objects to be
167 def check_inspect_key(id)
168 Thread.current[:__recursive_key__] &&
169 Thread.current[:__recursive_key__][:inspect] &&
170 Thread.current[:__recursive_key__][:inspect].include?(id)
173 # Adds the object_id +id+ to the set of objects being pretty printed, so
174 # as to not repeat objects.
175 def push_inspect_key(id)
176 Thread.current[:__recursive_key__][:inspect][id] = true
179 # Removes an object from the set of objects being pretty printed.
180 def pop_inspect_key(id)
181 Thread.current[:__recursive_key__][:inspect].delete id
184 # Adds +obj+ to the pretty printing buffer
185 # using Object#pretty_print or Object#pretty_print_cycle.
187 # Object#pretty_print_cycle is used when +obj+ is already
188 # printed, a.k.a the object reference chain has a cycle.
190 # If obj is a Delegator then use the object being delegated to for cycle
192 obj = obj.__getobj__ if defined?(::Delegator) and obj.is_a?(::Delegator)
194 if check_inspect_key(obj)
195 group {obj.pretty_print_cycle self}
200 push_inspect_key(obj)
201 group {obj.pretty_print self}
203 pop_inspect_key(obj) unless PP.sharing_detection
207 # A convenience method which is same as follows:
209 # group(1, '#<' + obj.class.name, '>') { ... }
210 def object_group(obj, &block) # :yield:
211 group(1, '#<' + obj.class.name, '>', &block)
214 # A convenience method, like object_group, but also reformats the Object's
216 def object_address_group(obj, &block)
217 str = Kernel.instance_method(:to_s).bind_call(obj)
219 group(1, str, '>', &block)
222 # A convenience method which is same as follows:
231 # Adds a separated list.
232 # The list is separated by comma with breakable space, by default.
234 # #seplist iterates the +list+ using +iter_method+.
235 # It yields each object to the block given for #seplist.
236 # The procedure +separator_proc+ is called between each yields.
238 # If the iteration is zero times, +separator_proc+ is not called at all.
240 # If +separator_proc+ is nil or not given,
241 # +lambda { comma_breakable }+ is used.
242 # If +iter_method+ is not given, :each is used.
244 # For example, following 3 code fragments has similar effect.
246 # q.seplist([1,2,3]) {|v| xxx v }
248 # q.seplist([1,2,3], lambda { q.comma_breakable }, :each) {|v| xxx v }
255 def seplist(list, sep=nil, iter_method=:each) # :yield: element
256 sep ||= lambda { comma_breakable }
258 list.__send__(iter_method) {|*v|
264 RUBY_VERSION >= "3.0" ? yield(*v, **{}) : yield(*v)
268 # A present standard failsafe for pretty printing any given Object
270 object_address_group(obj) {
271 seplist(obj.pretty_print_instance_variables, lambda { text ',' }) {|v|
273 v = v.to_s if Symbol === v
278 pp(obj.instance_eval(v))
284 # A pretty print for a Hash
287 seplist(obj, nil, :each_pair) {|k, v|
295 # A pretty print for a pair of Hash
296 def pp_hash_pair(k, v)
308 class SingleLine < PrettyPrint::SingleLine # :nodoc:
312 module ObjectMixin # :nodoc:
313 # 1. specific pretty_print
314 # 2. specific inspect
315 # 3. generic pretty_print
317 # A default pretty printing method for general objects.
318 # It calls #pretty_print_instance_variables to list instance variables.
320 # If +self+ has a customized (redefined) #inspect method,
321 # the result of self.inspect is used but it obviously has no
324 # This module provides predefined #pretty_print methods for some of
325 # the most commonly used built-in classes for convenience.
327 umethod_method = Object.instance_method(:method)
329 inspect_method = umethod_method.bind_call(self, :inspect)
332 if inspect_method && inspect_method.owner != Kernel
334 elsif !inspect_method && self.respond_to?(:inspect)
341 # A default pretty printing method for general objects that are
342 # detected as part of a cycle.
343 def pretty_print_cycle(q)
344 q.object_address_group(self) {
350 # Returns a sorted array of instance variable names.
352 # This method should return an array of names of instance variables as symbols or strings as:
354 def pretty_print_instance_variables
355 instance_variables.sort
358 # Is #inspect implementation using #pretty_print.
359 # If you implement #pretty_print, it can be used as follows.
361 # alias inspect pretty_print_inspect
363 # However, doing this requires that every class that #inspect is called on
364 # implement #pretty_print, or a RuntimeError will be raised.
365 def pretty_print_inspect
366 if Object.instance_method(:method).bind_call(self, :pretty_print).owner == PP::ObjectMixin
367 raise "pretty_print is not overridden for #{self.class}"
369 PP.singleline_pp(self, ''.dup)
374 class Array # :nodoc:
375 def pretty_print(q) # :nodoc:
376 q.group(1, '[', ']') {
383 def pretty_print_cycle(q) # :nodoc:
384 q.text(empty? ? '[]' : '[...]')
389 def pretty_print(q) # :nodoc:
393 def pretty_print_cycle(q) # :nodoc:
394 q.text(empty? ? '{}' : '{...}')
398 class << ENV # :nodoc:
399 def pretty_print(q) # :nodoc:
401 ENV.keys.sort.each {|k|
408 class Struct # :nodoc:
409 def pretty_print(q) # :nodoc:
410 q.group(1, sprintf("#<struct %s", PP.mcall(self, Kernel, :class).name), '>') {
411 q.seplist(PP.mcall(self, Struct, :members), lambda { q.text "," }) {|member|
423 def pretty_print_cycle(q) # :nodoc:
424 q.text sprintf("#<struct %s:...>", PP.mcall(self, Kernel, :class).name)
429 def pretty_print(q) # :nodoc:
430 class_name = PP.mcall(self, Kernel, :class).name
431 class_name = " #{class_name}" if class_name
432 q.group(1, "#<data#{class_name}", '>') {
433 q.seplist(PP.mcall(self, Kernel, :class).members, lambda { q.text "," }) {|member|
439 q.pp public_send(member)
445 def pretty_print_cycle(q) # :nodoc:
446 q.text sprintf("#<data %s:...>", PP.mcall(self, Kernel, :class).name)
448 end if defined?(Data.define)
450 class Range # :nodoc:
451 def pretty_print(q) # :nodoc:
452 q.pp self.begin if self.begin
454 q.text(self.exclude_end? ? '...' : '..')
456 q.pp self.end if self.end
460 class String # :nodoc:
461 def pretty_print(q) # :nodoc:
464 q.group(0, '', '') do
465 q.seplist(lines, lambda { q.text ' +'; q.breakable }) do |v|
475 class File < IO # :nodoc:
477 def pretty_print(q) # :nodoc:
479 q.object_group(self) {
481 q.text sprintf("dev=0x%x", self.dev); q.comma_breakable
482 q.text "ino="; q.pp self.ino; q.comma_breakable
485 q.text sprintf("mode=0%o", m)
487 q.text sprintf("(%s %c%c%c%c%c%c%c%c%c)",
489 (m & 0400 == 0 ? ?- : ?r),
490 (m & 0200 == 0 ? ?- : ?w),
491 (m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
492 (m & 04000 == 0 ? ?x : ?s)),
493 (m & 0040 == 0 ? ?- : ?r),
494 (m & 0020 == 0 ? ?- : ?w),
495 (m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
496 (m & 02000 == 0 ? ?x : ?s)),
497 (m & 0004 == 0 ? ?- : ?r),
498 (m & 0002 == 0 ? ?- : ?w),
499 (m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
500 (m & 01000 == 0 ? ?x : ?t)))
503 q.text "nlink="; q.pp self.nlink; q.comma_breakable
505 q.text "uid="; q.pp self.uid
507 pw = Etc.getpwuid(self.uid)
511 q.breakable; q.text "(#{pw.name})"
516 q.text "gid="; q.pp self.gid
518 gr = Etc.getgrgid(self.gid)
522 q.breakable; q.text "(#{gr.name})"
527 q.text sprintf("rdev=0x%x", self.rdev)
528 if self.rdev_major && self.rdev_minor
530 q.text sprintf('(%d, %d)', self.rdev_major, self.rdev_minor)
534 q.text "size="; q.pp self.size; q.comma_breakable
535 q.text "blksize="; q.pp self.blksize; q.comma_breakable
536 q.text "blocks="; q.pp self.blocks; q.comma_breakable
539 q.text "atime="; q.pp t
540 q.breakable; q.text "(#{t.tv_sec})"
545 q.text "mtime="; q.pp t
546 q.breakable; q.text "(#{t.tv_sec})"
551 q.text "ctime="; q.pp t
552 q.breakable; q.text "(#{t.tv_sec})"
559 class MatchData # :nodoc:
560 def pretty_print(q) # :nodoc:
562 self.regexp.named_captures.each {|name, indexes|
563 indexes.each {|i| nc[i] = name }
565 q.object_group(self) {
567 q.seplist(0...self.size, lambda { q.breakable }) {|i|
584 if defined?(RubyVM::AbstractSyntaxTree)
585 class RubyVM::AbstractSyntaxTree::Node
586 def pretty_print_children(q, names = [])
587 children.zip(names) do |c, n|
600 q.group(1, "(#{type}@#{first_lineno}:#{first_column}-#{last_lineno}:#{last_column}", ")") {
603 pretty_print_children(q, %w"tbl args body")
605 pretty_print_children(q, %w[pre_num pre_init opt first_post post_num post_init rest kw kwrest block])
607 pretty_print_children(q, %w[mid body])
609 pretty_print_children(q, %w[const pre rest post])
611 pretty_print_children(q, %w[const kw kwrest])
613 pretty_print_children(q)
620 class Object < BasicObject # :nodoc:
621 include PP::ObjectMixin
624 [Numeric, Symbol, FalseClass, TrueClass, NilClass, Module].each {|c|
626 def pretty_print_cycle(q)
632 [Numeric, FalseClass, TrueClass, Module].each {|c|
641 # Returns a pretty printed object as a string.
643 # See the PP module for more information.
648 # prints arguments in pretty form.
650 # pp returns argument(s).
655 objs.size <= 1 ? objs.first : objs