motion-pp 1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|