motion-pp 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +39 -0
- data/lib/motion-pp.rb +8 -0
- data/lib/project/pp.rb +522 -0
- data/lib/project/prettyprint.rb +377 -0
- metadata +62 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: caa4bcca8c651eedbb0430317c69d57bbd55f883
|
4
|
+
data.tar.gz: cde85487575c7bbee2b5ef32adfa03c01d87685f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 194d490d84c67c8fa2f463c2f0103ccf765d64ab67ba5830d1305f9691bfae7095376b9610eb4f3a5aac2ea46710684c1dd0854c8ffdf6eb1b7c4a79dbac2c07
|
7
|
+
data.tar.gz: 936c5a73a877bb4bf8a718242e1dbf44f16d210dcfeef7fc1297dd14355919e7dd07600c8e5e73a684d6c25d1d90adfdcf7361c6b54a6b881798d0ca4ee52b1b
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# motion-pp
|
2
|
+
|
3
|
+
Pretty-printer for RubyMotion objects.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'motion-pp'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ [sudo] gem install motion-pp
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
|
22
|
+
This gem can be used in Rakefile of your RubyMotion project by requiring.
|
23
|
+
|
24
|
+
require 'motion-pp'
|
25
|
+
|
26
|
+
|
27
|
+
And then, you would write the code, like:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
ary = Array.new(5) { {:foobar => :baz} }
|
31
|
+
pp ary
|
32
|
+
#=> [{:foobar=>:baz},
|
33
|
+
{:foobar=>:baz},
|
34
|
+
{:foobar=>:baz},
|
35
|
+
{:foobar=>:baz},
|
36
|
+
{:foobar=>:baz}]
|
37
|
+
```
|
38
|
+
|
39
|
+
This library provides the same APIs as standard Ruby. You could see the APIs references at http://ruby-doc.org/stdlib-1.9.3/libdoc/pp/rdoc/
|
data/lib/motion-pp.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
unless defined?(Motion::Project::Config)
|
2
|
+
raise "This file must be required within a RubyMotion project Rakefile."
|
3
|
+
end
|
4
|
+
|
5
|
+
lib_dir_path = File.dirname(File.expand_path(__FILE__))
|
6
|
+
Motion::Project::App.setup do |app|
|
7
|
+
app.files.unshift(Dir.glob(File.join(lib_dir_path, "project/**/*.rb")))
|
8
|
+
end
|
data/lib/project/pp.rb
ADDED
@@ -0,0 +1,522 @@
|
|
1
|
+
# == Pretty-printer for Ruby objects.
|
2
|
+
#
|
3
|
+
# = Which seems better?
|
4
|
+
#
|
5
|
+
# non-pretty-printed output by #p is:
|
6
|
+
# #<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>>
|
7
|
+
#
|
8
|
+
# pretty-printed output by #pp is:
|
9
|
+
# #<PP:0x81fedf0
|
10
|
+
# @buffer=[],
|
11
|
+
# @buffer_width=0,
|
12
|
+
# @genspace=#<Proc:0x81feda0>,
|
13
|
+
# @group_queue=
|
14
|
+
# #<PrettyPrint::GroupQueue:0x81fed3c
|
15
|
+
# @queue=
|
16
|
+
# [[#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
|
17
|
+
# []]>,
|
18
|
+
# @group_stack=
|
19
|
+
# [#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
|
20
|
+
# @indent=0,
|
21
|
+
# @maxwidth=79,
|
22
|
+
# @newline="\n",
|
23
|
+
# @output=#<IO:0x8114ee4>,
|
24
|
+
# @output_width=2>
|
25
|
+
#
|
26
|
+
# I like the latter. If you do too, this library is for you.
|
27
|
+
#
|
28
|
+
# = Usage
|
29
|
+
#
|
30
|
+
# pp(obj)
|
31
|
+
#
|
32
|
+
# output +obj+ to +$>+ in pretty printed format.
|
33
|
+
#
|
34
|
+
# It returns +nil+.
|
35
|
+
#
|
36
|
+
# = Output Customization
|
37
|
+
# To define your customized pretty printing function for your classes,
|
38
|
+
# redefine a method #pretty_print(+pp+) in the class.
|
39
|
+
# It takes an argument +pp+ which is an instance of the class PP.
|
40
|
+
# The method should use PP#text, PP#breakable, PP#nest, PP#group and
|
41
|
+
# PP#pp to print the object.
|
42
|
+
#
|
43
|
+
# = Author
|
44
|
+
# Tanaka Akira <akr@m17n.org>
|
45
|
+
|
46
|
+
module Kernel
|
47
|
+
# returns a pretty printed object as a string.
|
48
|
+
def pretty_inspect
|
49
|
+
PP.pp(self, '')
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
# prints arguments in pretty form.
|
54
|
+
#
|
55
|
+
# pp returns argument(s).
|
56
|
+
def pp(*objs) # :doc:
|
57
|
+
objs.each {|obj|
|
58
|
+
PP.pp(obj)
|
59
|
+
}
|
60
|
+
objs.size <= 1 ? objs.first : objs
|
61
|
+
end
|
62
|
+
module_function :pp
|
63
|
+
end
|
64
|
+
|
65
|
+
class PP < PrettyPrint
|
66
|
+
# Outputs +obj+ to +out+ in pretty printed format of
|
67
|
+
# +width+ columns in width.
|
68
|
+
#
|
69
|
+
# If +out+ is omitted, +$>+ is assumed.
|
70
|
+
# If +width+ is omitted, 79 is assumed.
|
71
|
+
#
|
72
|
+
# PP.pp returns +out+.
|
73
|
+
def PP.pp(obj, out=$>, width=79)
|
74
|
+
q = PP.new(out, width)
|
75
|
+
q.guard_inspect_key {q.pp obj}
|
76
|
+
q.flush
|
77
|
+
#$pp = q
|
78
|
+
out << "\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
# Outputs +obj+ to +out+ like PP.pp but with no indent and
|
82
|
+
# newline.
|
83
|
+
#
|
84
|
+
# PP.singleline_pp returns +out+.
|
85
|
+
def PP.singleline_pp(obj, out=$>)
|
86
|
+
q = SingleLine.new(out)
|
87
|
+
q.guard_inspect_key {q.pp obj}
|
88
|
+
q.flush
|
89
|
+
out
|
90
|
+
end
|
91
|
+
|
92
|
+
# :stopdoc:
|
93
|
+
def PP.mcall(obj, mod, meth, *args, &block)
|
94
|
+
mod.instance_method(meth).bind(obj).call(*args, &block)
|
95
|
+
end
|
96
|
+
# :startdoc:
|
97
|
+
|
98
|
+
@sharing_detection = false
|
99
|
+
class << self
|
100
|
+
# Returns the sharing detection flag as a boolean value.
|
101
|
+
# It is false by default.
|
102
|
+
attr_accessor :sharing_detection
|
103
|
+
end
|
104
|
+
|
105
|
+
module PPMethods
|
106
|
+
def guard_inspect_key
|
107
|
+
if Thread.current[:__recursive_key__] == nil
|
108
|
+
Thread.current[:__recursive_key__] = {}.untrust
|
109
|
+
end
|
110
|
+
|
111
|
+
if Thread.current[:__recursive_key__][:inspect] == nil
|
112
|
+
Thread.current[:__recursive_key__][:inspect] = {}.untrust
|
113
|
+
end
|
114
|
+
|
115
|
+
save = Thread.current[:__recursive_key__][:inspect]
|
116
|
+
|
117
|
+
begin
|
118
|
+
Thread.current[:__recursive_key__][:inspect] = {}.untrust
|
119
|
+
yield
|
120
|
+
ensure
|
121
|
+
Thread.current[:__recursive_key__][:inspect] = save
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def check_inspect_key(id)
|
126
|
+
Thread.current[:__recursive_key__] &&
|
127
|
+
Thread.current[:__recursive_key__][:inspect] &&
|
128
|
+
Thread.current[:__recursive_key__][:inspect].include?(id)
|
129
|
+
end
|
130
|
+
def push_inspect_key(id)
|
131
|
+
Thread.current[:__recursive_key__][:inspect][id] = true
|
132
|
+
end
|
133
|
+
def pop_inspect_key(id)
|
134
|
+
Thread.current[:__recursive_key__][:inspect].delete id
|
135
|
+
end
|
136
|
+
|
137
|
+
# Adds +obj+ to the pretty printing buffer
|
138
|
+
# using Object#pretty_print or Object#pretty_print_cycle.
|
139
|
+
#
|
140
|
+
# Object#pretty_print_cycle is used when +obj+ is already
|
141
|
+
# printed, a.k.a the object reference chain has a cycle.
|
142
|
+
def pp(obj)
|
143
|
+
id = obj.object_id
|
144
|
+
|
145
|
+
if check_inspect_key(id)
|
146
|
+
group {obj.pretty_print_cycle self}
|
147
|
+
return
|
148
|
+
end
|
149
|
+
|
150
|
+
begin
|
151
|
+
push_inspect_key(id)
|
152
|
+
group {obj.pretty_print self}
|
153
|
+
ensure
|
154
|
+
pop_inspect_key(id) unless PP.sharing_detection
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# A convenience method which is same as follows:
|
159
|
+
#
|
160
|
+
# group(1, '#<' + obj.class.name, '>') { ... }
|
161
|
+
def object_group(obj, &block) # :yield:
|
162
|
+
group(1, '#<' + obj.class.name, '>', &block)
|
163
|
+
end
|
164
|
+
|
165
|
+
PointerMask = (1 << ([""].pack("p").size * 8)) - 1
|
166
|
+
|
167
|
+
case Object.new.inspect
|
168
|
+
when /\A\#<Object:0x([0-9a-f]+)>\z/
|
169
|
+
PointerFormat = "%0#{$1.length}x"
|
170
|
+
else
|
171
|
+
PointerFormat = "%x"
|
172
|
+
end
|
173
|
+
|
174
|
+
def object_address_group(obj, &block)
|
175
|
+
id = PointerFormat % (obj.object_id * 2 & PointerMask)
|
176
|
+
group(1, "\#<#{obj.class}:0x#{id}", '>', &block)
|
177
|
+
end
|
178
|
+
|
179
|
+
# A convenience method which is same as follows:
|
180
|
+
#
|
181
|
+
# text ','
|
182
|
+
# breakable
|
183
|
+
def comma_breakable
|
184
|
+
text ','
|
185
|
+
breakable
|
186
|
+
end
|
187
|
+
|
188
|
+
# Adds a separated list.
|
189
|
+
# The list is separated by comma with breakable space, by default.
|
190
|
+
#
|
191
|
+
# #seplist iterates the +list+ using +iter_method+.
|
192
|
+
# It yields each object to the block given for #seplist.
|
193
|
+
# The procedure +separator_proc+ is called between each yields.
|
194
|
+
#
|
195
|
+
# If the iteration is zero times, +separator_proc+ is not called at all.
|
196
|
+
#
|
197
|
+
# If +separator_proc+ is nil or not given,
|
198
|
+
# +lambda { comma_breakable }+ is used.
|
199
|
+
# If +iter_method+ is not given, :each is used.
|
200
|
+
#
|
201
|
+
# For example, following 3 code fragments has similar effect.
|
202
|
+
#
|
203
|
+
# q.seplist([1,2,3]) {|v| xxx v }
|
204
|
+
#
|
205
|
+
# q.seplist([1,2,3], lambda { q.comma_breakable }, :each) {|v| xxx v }
|
206
|
+
#
|
207
|
+
# xxx 1
|
208
|
+
# q.comma_breakable
|
209
|
+
# xxx 2
|
210
|
+
# q.comma_breakable
|
211
|
+
# xxx 3
|
212
|
+
def seplist(list, sep=nil, iter_method=:each) # :yield: element
|
213
|
+
sep ||= lambda { comma_breakable }
|
214
|
+
first = true
|
215
|
+
list.__send__(iter_method) {|*v|
|
216
|
+
if first
|
217
|
+
first = false
|
218
|
+
else
|
219
|
+
sep.call
|
220
|
+
end
|
221
|
+
yield(*v)
|
222
|
+
}
|
223
|
+
end
|
224
|
+
|
225
|
+
def pp_object(obj)
|
226
|
+
object_address_group(obj) {
|
227
|
+
seplist(obj.pretty_print_instance_variables, lambda { text ',' }) {|v|
|
228
|
+
breakable
|
229
|
+
v = v.to_s if Symbol === v
|
230
|
+
text v
|
231
|
+
text '='
|
232
|
+
group(1) {
|
233
|
+
breakable ''
|
234
|
+
pp(obj.instance_eval(v))
|
235
|
+
}
|
236
|
+
}
|
237
|
+
}
|
238
|
+
end
|
239
|
+
|
240
|
+
def pp_hash(obj)
|
241
|
+
group(1, '{', '}') {
|
242
|
+
seplist(obj, nil, :each_pair) {|k, v|
|
243
|
+
group {
|
244
|
+
pp k
|
245
|
+
text '=>'
|
246
|
+
group(1) {
|
247
|
+
breakable ''
|
248
|
+
pp v
|
249
|
+
}
|
250
|
+
}
|
251
|
+
}
|
252
|
+
}
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
include PPMethods
|
257
|
+
|
258
|
+
class SingleLine < PrettyPrint::SingleLine
|
259
|
+
include PPMethods
|
260
|
+
end
|
261
|
+
|
262
|
+
module ObjectMixin
|
263
|
+
# 1. specific pretty_print
|
264
|
+
# 2. specific inspect
|
265
|
+
# 3. specific to_s
|
266
|
+
# 4. generic pretty_print
|
267
|
+
|
268
|
+
# A default pretty printing method for general objects.
|
269
|
+
# It calls #pretty_print_instance_variables to list instance variables.
|
270
|
+
#
|
271
|
+
# If +self+ has a customized (redefined) #inspect method,
|
272
|
+
# the result of self.inspect is used but it obviously has no
|
273
|
+
# line break hints.
|
274
|
+
#
|
275
|
+
# This module provides predefined #pretty_print methods for some of
|
276
|
+
# the most commonly used built-in classes for convenience.
|
277
|
+
def pretty_print(q)
|
278
|
+
method_method = Object.instance_method(:method).bind(self)
|
279
|
+
begin
|
280
|
+
inspect_method = method_method.call(:inspect)
|
281
|
+
rescue NameError
|
282
|
+
end
|
283
|
+
begin
|
284
|
+
to_s_method = method_method.call(:to_s)
|
285
|
+
rescue NameError
|
286
|
+
end
|
287
|
+
if inspect_method && /\(Kernel\)#/ !~ inspect_method.inspect
|
288
|
+
q.text self.inspect
|
289
|
+
elsif !inspect_method && self.respond_to?(:inspect)
|
290
|
+
q.text self.inspect
|
291
|
+
elsif to_s_method && /\(Kernel\)#/ !~ to_s_method.inspect
|
292
|
+
q.text self.to_s
|
293
|
+
elsif !to_s_method && self.respond_to?(:to_s)
|
294
|
+
q.text self.to_s
|
295
|
+
else
|
296
|
+
q.pp_object(self)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# A default pretty printing method for general objects that are
|
301
|
+
# detected as part of a cycle.
|
302
|
+
def pretty_print_cycle(q)
|
303
|
+
q.object_address_group(self) {
|
304
|
+
q.breakable
|
305
|
+
q.text '...'
|
306
|
+
}
|
307
|
+
end
|
308
|
+
|
309
|
+
# Returns a sorted array of instance variable names.
|
310
|
+
#
|
311
|
+
# This method should return an array of names of instance variables as symbols or strings as:
|
312
|
+
# +[:@a, :@b]+.
|
313
|
+
def pretty_print_instance_variables
|
314
|
+
instance_variables.sort
|
315
|
+
end
|
316
|
+
|
317
|
+
# Is #inspect implementation using #pretty_print.
|
318
|
+
# If you implement #pretty_print, it can be used as follows.
|
319
|
+
#
|
320
|
+
# alias inspect pretty_print_inspect
|
321
|
+
#
|
322
|
+
# However, doing this requires that every class that #inspect is called on
|
323
|
+
# implement #pretty_print, or a RuntimeError will be raised.
|
324
|
+
def pretty_print_inspect
|
325
|
+
if /\(PP::ObjectMixin\)#/ =~ Object.instance_method(:method).bind(self).call(:pretty_print).inspect
|
326
|
+
raise "pretty_print is not overridden for #{self.class}"
|
327
|
+
end
|
328
|
+
PP.singleline_pp(self, '')
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
class Array
|
334
|
+
def pretty_print(q)
|
335
|
+
q.group(1, '[', ']') {
|
336
|
+
q.seplist(self) {|v|
|
337
|
+
q.pp v
|
338
|
+
}
|
339
|
+
}
|
340
|
+
end
|
341
|
+
|
342
|
+
def pretty_print_cycle(q)
|
343
|
+
q.text(empty? ? '[]' : '[...]')
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
class Hash
|
348
|
+
def pretty_print(q)
|
349
|
+
q.pp_hash self
|
350
|
+
end
|
351
|
+
|
352
|
+
def pretty_print_cycle(q)
|
353
|
+
q.text(empty? ? '{}' : '{...}')
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
class << ENV
|
358
|
+
def pretty_print(q)
|
359
|
+
h = {}
|
360
|
+
ENV.keys.sort.each {|k|
|
361
|
+
h[k] = ENV[k]
|
362
|
+
}
|
363
|
+
q.pp_hash h
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
class Struct
|
368
|
+
def pretty_print(q)
|
369
|
+
q.group(1, sprintf("#<struct %s", PP.mcall(self, Kernel, :class).name), '>') {
|
370
|
+
q.seplist(PP.mcall(self, Struct, :members), lambda { q.text "," }) {|member|
|
371
|
+
q.breakable
|
372
|
+
q.text member.to_s
|
373
|
+
q.text '='
|
374
|
+
q.group(1) {
|
375
|
+
q.breakable ''
|
376
|
+
q.pp self[member]
|
377
|
+
}
|
378
|
+
}
|
379
|
+
}
|
380
|
+
end
|
381
|
+
|
382
|
+
def pretty_print_cycle(q)
|
383
|
+
q.text sprintf("#<struct %s:...>", PP.mcall(self, Kernel, :class).name)
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
class Range
|
388
|
+
def pretty_print(q)
|
389
|
+
q.pp self.begin
|
390
|
+
q.breakable ''
|
391
|
+
q.text(self.exclude_end? ? '...' : '..')
|
392
|
+
q.breakable ''
|
393
|
+
q.pp self.end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
class File
|
398
|
+
class Stat
|
399
|
+
def pretty_print(q)
|
400
|
+
require 'etc'
|
401
|
+
q.object_group(self) {
|
402
|
+
q.breakable
|
403
|
+
q.text sprintf("dev=0x%x", self.dev); q.comma_breakable
|
404
|
+
q.text "ino="; q.pp self.ino; q.comma_breakable
|
405
|
+
q.group {
|
406
|
+
m = self.mode
|
407
|
+
q.text sprintf("mode=0%o", m)
|
408
|
+
q.breakable
|
409
|
+
q.text sprintf("(%s %c%c%c%c%c%c%c%c%c)",
|
410
|
+
self.ftype,
|
411
|
+
(m & 0400 == 0 ? ?- : ?r),
|
412
|
+
(m & 0200 == 0 ? ?- : ?w),
|
413
|
+
(m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
|
414
|
+
(m & 04000 == 0 ? ?x : ?s)),
|
415
|
+
(m & 0040 == 0 ? ?- : ?r),
|
416
|
+
(m & 0020 == 0 ? ?- : ?w),
|
417
|
+
(m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
|
418
|
+
(m & 02000 == 0 ? ?x : ?s)),
|
419
|
+
(m & 0004 == 0 ? ?- : ?r),
|
420
|
+
(m & 0002 == 0 ? ?- : ?w),
|
421
|
+
(m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
|
422
|
+
(m & 01000 == 0 ? ?x : ?t)))
|
423
|
+
}
|
424
|
+
q.comma_breakable
|
425
|
+
q.text "nlink="; q.pp self.nlink; q.comma_breakable
|
426
|
+
q.group {
|
427
|
+
q.text "uid="; q.pp self.uid
|
428
|
+
begin
|
429
|
+
pw = Etc.getpwuid(self.uid)
|
430
|
+
rescue ArgumentError
|
431
|
+
end
|
432
|
+
if pw
|
433
|
+
q.breakable; q.text "(#{pw.name})"
|
434
|
+
end
|
435
|
+
}
|
436
|
+
q.comma_breakable
|
437
|
+
q.group {
|
438
|
+
q.text "gid="; q.pp self.gid
|
439
|
+
begin
|
440
|
+
gr = Etc.getgrgid(self.gid)
|
441
|
+
rescue ArgumentError
|
442
|
+
end
|
443
|
+
if gr
|
444
|
+
q.breakable; q.text "(#{gr.name})"
|
445
|
+
end
|
446
|
+
}
|
447
|
+
q.comma_breakable
|
448
|
+
q.group {
|
449
|
+
q.text sprintf("rdev=0x%x", self.rdev)
|
450
|
+
q.breakable
|
451
|
+
q.text sprintf('(%d, %d)', self.rdev_major, self.rdev_minor)
|
452
|
+
}
|
453
|
+
q.comma_breakable
|
454
|
+
q.text "size="; q.pp self.size; q.comma_breakable
|
455
|
+
q.text "blksize="; q.pp self.blksize; q.comma_breakable
|
456
|
+
q.text "blocks="; q.pp self.blocks; q.comma_breakable
|
457
|
+
q.group {
|
458
|
+
t = self.atime
|
459
|
+
q.text "atime="; q.pp t
|
460
|
+
q.breakable; q.text "(#{t.tv_sec})"
|
461
|
+
}
|
462
|
+
q.comma_breakable
|
463
|
+
q.group {
|
464
|
+
t = self.mtime
|
465
|
+
q.text "mtime="; q.pp t
|
466
|
+
q.breakable; q.text "(#{t.tv_sec})"
|
467
|
+
}
|
468
|
+
q.comma_breakable
|
469
|
+
q.group {
|
470
|
+
t = self.ctime
|
471
|
+
q.text "ctime="; q.pp t
|
472
|
+
q.breakable; q.text "(#{t.tv_sec})"
|
473
|
+
}
|
474
|
+
}
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
class MatchData
|
480
|
+
def pretty_print(q)
|
481
|
+
nc = []
|
482
|
+
self.regexp.named_captures.each {|name, indexes|
|
483
|
+
indexes.each {|i| nc[i] = name }
|
484
|
+
}
|
485
|
+
q.object_group(self) {
|
486
|
+
q.breakable
|
487
|
+
q.seplist(0...self.size, lambda { q.breakable }) {|i|
|
488
|
+
if i == 0
|
489
|
+
q.pp self[i]
|
490
|
+
else
|
491
|
+
if nc[i]
|
492
|
+
q.text nc[i]
|
493
|
+
else
|
494
|
+
q.pp i
|
495
|
+
end
|
496
|
+
q.text ':'
|
497
|
+
q.pp self[i]
|
498
|
+
end
|
499
|
+
}
|
500
|
+
}
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
class Object
|
505
|
+
include PP::ObjectMixin
|
506
|
+
end
|
507
|
+
|
508
|
+
[Numeric, Symbol, FalseClass, TrueClass, NilClass, Module].each {|c|
|
509
|
+
c.class_eval {
|
510
|
+
def pretty_print_cycle(q)
|
511
|
+
q.text inspect
|
512
|
+
end
|
513
|
+
}
|
514
|
+
}
|
515
|
+
|
516
|
+
[Numeric, FalseClass, TrueClass, Module].each {|c|
|
517
|
+
c.class_eval {
|
518
|
+
def pretty_print(q)
|
519
|
+
q.text inspect
|
520
|
+
end
|
521
|
+
}
|
522
|
+
}
|
@@ -0,0 +1,377 @@
|
|
1
|
+
# This class implements a pretty printing algorithm. It finds line breaks and
|
2
|
+
# nice indentations for grouped structure.
|
3
|
+
#
|
4
|
+
# By default, the class assumes that primitive elements are strings and each
|
5
|
+
# byte in the strings have single column in width. But it can be used for
|
6
|
+
# other situations by giving suitable arguments for some methods:
|
7
|
+
# * newline object and space generation block for PrettyPrint.new
|
8
|
+
# * optional width argument for PrettyPrint#text
|
9
|
+
# * PrettyPrint#breakable
|
10
|
+
#
|
11
|
+
# There are several candidate uses:
|
12
|
+
# * text formatting using proportional fonts
|
13
|
+
# * multibyte characters which has columns different to number of bytes
|
14
|
+
# * non-string formatting
|
15
|
+
#
|
16
|
+
# == Bugs
|
17
|
+
# * Box based formatting?
|
18
|
+
# * Other (better) model/algorithm?
|
19
|
+
#
|
20
|
+
# == References
|
21
|
+
# Christian Lindig, Strictly Pretty, March 2000,
|
22
|
+
# http://www.st.cs.uni-sb.de/~lindig/papers/#pretty
|
23
|
+
#
|
24
|
+
# Philip Wadler, A prettier printer, March 1998,
|
25
|
+
# http://homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier
|
26
|
+
#
|
27
|
+
# == Author
|
28
|
+
# Tanaka Akira <akr@m17n.org>
|
29
|
+
#
|
30
|
+
class PrettyPrint
|
31
|
+
|
32
|
+
# This is a convenience method which is same as follows:
|
33
|
+
#
|
34
|
+
# begin
|
35
|
+
# q = PrettyPrint.new(output, maxwidth, newline, &genspace)
|
36
|
+
# ...
|
37
|
+
# q.flush
|
38
|
+
# output
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
def PrettyPrint.format(output='', maxwidth=79, newline="\n", genspace=lambda {|n| ' ' * n})
|
42
|
+
q = PrettyPrint.new(output, maxwidth, newline, &genspace)
|
43
|
+
yield q
|
44
|
+
q.flush
|
45
|
+
output
|
46
|
+
end
|
47
|
+
|
48
|
+
# This is similar to PrettyPrint::format but the result has no breaks.
|
49
|
+
#
|
50
|
+
# +maxwidth+, +newline+ and +genspace+ are ignored.
|
51
|
+
#
|
52
|
+
# The invocation of +breakable+ in the block doesn't break a line and is
|
53
|
+
# treated as just an invocation of +text+.
|
54
|
+
#
|
55
|
+
def PrettyPrint.singleline_format(output='', maxwidth=nil, newline=nil, genspace=nil)
|
56
|
+
q = SingleLine.new(output)
|
57
|
+
yield q
|
58
|
+
output
|
59
|
+
end
|
60
|
+
|
61
|
+
# Creates a buffer for pretty printing.
|
62
|
+
#
|
63
|
+
# +output+ is an output target. If it is not specified, '' is assumed. It
|
64
|
+
# should have a << method which accepts the first argument +obj+ of
|
65
|
+
# PrettyPrint#text, the first argument +sep+ of PrettyPrint#breakable, the
|
66
|
+
# first argument +newline+ of PrettyPrint.new, and the result of a given
|
67
|
+
# block for PrettyPrint.new.
|
68
|
+
#
|
69
|
+
# +maxwidth+ specifies maximum line length. If it is not specified, 79 is
|
70
|
+
# assumed. However actual outputs may overflow +maxwidth+ if long
|
71
|
+
# non-breakable texts are provided.
|
72
|
+
#
|
73
|
+
# +newline+ is used for line breaks. "\n" is used if it is not specified.
|
74
|
+
#
|
75
|
+
# The block is used to generate spaces. {|width| ' ' * width} is used if it
|
76
|
+
# is not given.
|
77
|
+
#
|
78
|
+
def initialize(output='', maxwidth=79, newline="\n", &genspace)
|
79
|
+
@output = output
|
80
|
+
@maxwidth = maxwidth
|
81
|
+
@newline = newline
|
82
|
+
@genspace = genspace || lambda {|n| ' ' * n}
|
83
|
+
|
84
|
+
@output_width = 0
|
85
|
+
@buffer_width = 0
|
86
|
+
@buffer = []
|
87
|
+
|
88
|
+
root_group = Group.new(0)
|
89
|
+
@group_stack = [root_group]
|
90
|
+
@group_queue = GroupQueue.new(root_group)
|
91
|
+
@indent = 0
|
92
|
+
end
|
93
|
+
attr_reader :output, :maxwidth, :newline, :genspace
|
94
|
+
attr_reader :indent, :group_queue
|
95
|
+
|
96
|
+
def current_group
|
97
|
+
@group_stack.last
|
98
|
+
end
|
99
|
+
|
100
|
+
# first? is a predicate to test the call is a first call to first? with
|
101
|
+
# current group.
|
102
|
+
#
|
103
|
+
# It is useful to format comma separated values as:
|
104
|
+
#
|
105
|
+
# q.group(1, '[', ']') {
|
106
|
+
# xxx.each {|yyy|
|
107
|
+
# unless q.first?
|
108
|
+
# q.text ','
|
109
|
+
# q.breakable
|
110
|
+
# end
|
111
|
+
# ... pretty printing yyy ...
|
112
|
+
# }
|
113
|
+
# }
|
114
|
+
#
|
115
|
+
# first? is obsoleted in 1.8.2.
|
116
|
+
#
|
117
|
+
def first?
|
118
|
+
warn "PrettyPrint#first? is obsoleted at 1.8.2."
|
119
|
+
current_group.first?
|
120
|
+
end
|
121
|
+
|
122
|
+
def break_outmost_groups
|
123
|
+
while @maxwidth < @output_width + @buffer_width
|
124
|
+
return unless group = @group_queue.deq
|
125
|
+
until group.breakables.empty?
|
126
|
+
data = @buffer.shift
|
127
|
+
@output_width = data.output(@output, @output_width)
|
128
|
+
@buffer_width -= data.width
|
129
|
+
end
|
130
|
+
while !@buffer.empty? && Text === @buffer.first
|
131
|
+
text = @buffer.shift
|
132
|
+
@output_width = text.output(@output, @output_width)
|
133
|
+
@buffer_width -= text.width
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# This adds +obj+ as a text of +width+ columns in width.
|
139
|
+
#
|
140
|
+
# If +width+ is not specified, obj.length is used.
|
141
|
+
#
|
142
|
+
def text(obj, width=obj.length)
|
143
|
+
if @buffer.empty?
|
144
|
+
@output << obj
|
145
|
+
@output_width += width
|
146
|
+
else
|
147
|
+
text = @buffer.last
|
148
|
+
unless Text === text
|
149
|
+
text = Text.new
|
150
|
+
@buffer << text
|
151
|
+
end
|
152
|
+
text.add(obj, width)
|
153
|
+
@buffer_width += width
|
154
|
+
break_outmost_groups
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def fill_breakable(sep=' ', width=sep.length)
|
159
|
+
group { breakable sep, width }
|
160
|
+
end
|
161
|
+
|
162
|
+
# This tells "you can break a line here if necessary", and a +width+\-column
|
163
|
+
# text +sep+ is inserted if a line is not broken at the point.
|
164
|
+
#
|
165
|
+
# If +sep+ is not specified, " " is used.
|
166
|
+
#
|
167
|
+
# If +width+ is not specified, +sep.length+ is used. You will have to
|
168
|
+
# specify this when +sep+ is a multibyte character, for example.
|
169
|
+
#
|
170
|
+
def breakable(sep=' ', width=sep.length)
|
171
|
+
group = @group_stack.last
|
172
|
+
if group.break?
|
173
|
+
flush
|
174
|
+
@output << @newline
|
175
|
+
@output << @genspace.call(@indent)
|
176
|
+
@output_width = @indent
|
177
|
+
@buffer_width = 0
|
178
|
+
else
|
179
|
+
@buffer << Breakable.new(sep, width, self)
|
180
|
+
@buffer_width += width
|
181
|
+
break_outmost_groups
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Groups line break hints added in the block. The line break hints are all
|
186
|
+
# to be used or not.
|
187
|
+
#
|
188
|
+
# If +indent+ is specified, the method call is regarded as nested by
|
189
|
+
# nest(indent) { ... }.
|
190
|
+
#
|
191
|
+
# If +open_obj+ is specified, <tt>text open_obj, open_width</tt> is called
|
192
|
+
# before grouping. If +close_obj+ is specified, <tt>text close_obj,
|
193
|
+
# close_width</tt> is called after grouping.
|
194
|
+
#
|
195
|
+
def group(indent=0, open_obj='', close_obj='', open_width=open_obj.length, close_width=close_obj.length)
|
196
|
+
text open_obj, open_width
|
197
|
+
group_sub {
|
198
|
+
nest(indent) {
|
199
|
+
yield
|
200
|
+
}
|
201
|
+
}
|
202
|
+
text close_obj, close_width
|
203
|
+
end
|
204
|
+
|
205
|
+
def group_sub
|
206
|
+
group = Group.new(@group_stack.last.depth + 1)
|
207
|
+
@group_stack.push group
|
208
|
+
@group_queue.enq group
|
209
|
+
begin
|
210
|
+
yield
|
211
|
+
ensure
|
212
|
+
@group_stack.pop
|
213
|
+
if group.breakables.empty?
|
214
|
+
@group_queue.delete group
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Increases left margin after newline with +indent+ for line breaks added in
|
220
|
+
# the block.
|
221
|
+
#
|
222
|
+
def nest(indent)
|
223
|
+
@indent += indent
|
224
|
+
begin
|
225
|
+
yield
|
226
|
+
ensure
|
227
|
+
@indent -= indent
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# outputs buffered data.
|
232
|
+
#
|
233
|
+
def flush
|
234
|
+
@buffer.each {|data|
|
235
|
+
@output_width = data.output(@output, @output_width)
|
236
|
+
}
|
237
|
+
@buffer.clear
|
238
|
+
@buffer_width = 0
|
239
|
+
end
|
240
|
+
|
241
|
+
class Text
|
242
|
+
def initialize
|
243
|
+
@objs = []
|
244
|
+
@width = 0
|
245
|
+
end
|
246
|
+
attr_reader :width
|
247
|
+
|
248
|
+
def output(out, output_width)
|
249
|
+
@objs.each {|obj| out << obj}
|
250
|
+
output_width + @width
|
251
|
+
end
|
252
|
+
|
253
|
+
def add(obj, width)
|
254
|
+
@objs << obj
|
255
|
+
@width += width
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
class Breakable
|
260
|
+
def initialize(sep, width, q)
|
261
|
+
@obj = sep
|
262
|
+
@width = width
|
263
|
+
@pp = q
|
264
|
+
@indent = q.indent
|
265
|
+
@group = q.current_group
|
266
|
+
@group.breakables.push self
|
267
|
+
end
|
268
|
+
attr_reader :obj, :width, :indent
|
269
|
+
|
270
|
+
def output(out, output_width)
|
271
|
+
@group.breakables.shift
|
272
|
+
if @group.break?
|
273
|
+
out << @pp.newline
|
274
|
+
out << @pp.genspace.call(@indent)
|
275
|
+
@indent
|
276
|
+
else
|
277
|
+
@pp.group_queue.delete @group if @group.breakables.empty?
|
278
|
+
out << @obj
|
279
|
+
output_width + @width
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
class Group
|
285
|
+
def initialize(depth)
|
286
|
+
@depth = depth
|
287
|
+
@breakables = []
|
288
|
+
@break = false
|
289
|
+
end
|
290
|
+
attr_reader :depth, :breakables
|
291
|
+
|
292
|
+
def break
|
293
|
+
@break = true
|
294
|
+
end
|
295
|
+
|
296
|
+
def break?
|
297
|
+
@break
|
298
|
+
end
|
299
|
+
|
300
|
+
def first?
|
301
|
+
if defined? @first
|
302
|
+
false
|
303
|
+
else
|
304
|
+
@first = false
|
305
|
+
true
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
class GroupQueue
|
311
|
+
def initialize(*groups)
|
312
|
+
@queue = []
|
313
|
+
groups.each {|g| enq g}
|
314
|
+
end
|
315
|
+
|
316
|
+
def enq(group)
|
317
|
+
depth = group.depth
|
318
|
+
@queue << [] until depth < @queue.length
|
319
|
+
@queue[depth] << group
|
320
|
+
end
|
321
|
+
|
322
|
+
def deq
|
323
|
+
@queue.each {|gs|
|
324
|
+
(gs.length-1).downto(0) {|i|
|
325
|
+
unless gs[i].breakables.empty?
|
326
|
+
group = gs.slice!(i, 1).first
|
327
|
+
group.break
|
328
|
+
return group
|
329
|
+
end
|
330
|
+
}
|
331
|
+
gs.each {|group| group.break}
|
332
|
+
gs.clear
|
333
|
+
}
|
334
|
+
return nil
|
335
|
+
end
|
336
|
+
|
337
|
+
def delete(group)
|
338
|
+
@queue[group.depth].delete(group)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
class SingleLine
|
343
|
+
def initialize(output, maxwidth=nil, newline=nil)
|
344
|
+
@output = output
|
345
|
+
@first = [true]
|
346
|
+
end
|
347
|
+
|
348
|
+
def text(obj, width=nil)
|
349
|
+
@output << obj
|
350
|
+
end
|
351
|
+
|
352
|
+
def breakable(sep=' ', width=nil)
|
353
|
+
@output << sep
|
354
|
+
end
|
355
|
+
|
356
|
+
def nest(indent)
|
357
|
+
yield
|
358
|
+
end
|
359
|
+
|
360
|
+
def group(indent=nil, open_obj='', close_obj='', open_width=nil, close_width=nil)
|
361
|
+
@first.push true
|
362
|
+
@output << open_obj
|
363
|
+
yield
|
364
|
+
@output << close_obj
|
365
|
+
@first.pop
|
366
|
+
end
|
367
|
+
|
368
|
+
def flush
|
369
|
+
end
|
370
|
+
|
371
|
+
def first?
|
372
|
+
result = @first[-1]
|
373
|
+
@first[-1] = false
|
374
|
+
result
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: motion-pp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Watson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-11-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Pretty-printer for RubyMotion objects.
|
28
|
+
email:
|
29
|
+
- watson1978@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- README.md
|
35
|
+
- lib/motion-pp.rb
|
36
|
+
- lib/project/pp.rb
|
37
|
+
- lib/project/prettyprint.rb
|
38
|
+
homepage: https://github.com/Watson1978/motion-pp
|
39
|
+
licenses:
|
40
|
+
- Ruby
|
41
|
+
metadata: {}
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 2.1.5
|
59
|
+
signing_key:
|
60
|
+
specification_version: 4
|
61
|
+
summary: Pretty-printer for RubyMotion objects.
|
62
|
+
test_files: []
|