motion-pp 1.0

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