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.
@@ -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: []