cursor 0.8 → 0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/cursor.rb +321 -131
- data/cursor/buffered.rb +3 -3
- data/cursor/circular.rb +3 -1
- data/cursor/io.rb +19 -14
- data/cursor/position.rb +14 -16
- data/cursor/reversed.rb +3 -2
- data/cursor/split.rb +3 -3
- data/cursor/test.rb +101 -12
- data/cursor/test_circulars.rb +5 -5
- data/cursor/test_cursors.rb +6 -5
- data/cursor/usenext/position.rb +5 -3
- data/duck.rb +28 -13
- metadata +2 -3
- data/regexp_cursor.rb +0 -50
data/cursor.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/bin/env ruby
|
2
2
|
# = cursor.rb - external iterators with capabilities like a text editor cursor
|
3
|
-
# $Id: cursor.rb,v 1.
|
3
|
+
# $Id: cursor.rb,v 1.58 2005/10/14 00:22:58 eric_mahurin Exp $
|
4
4
|
# Author:: Eric Mahurin (Eric under Mahurin at yahoo dot com)
|
5
5
|
# License:: Ruby license
|
6
6
|
# Home:: http://rubyforge.org/projects/cursor
|
@@ -50,7 +50,8 @@ to #==).
|
|
50
50
|
|
51
51
|
#read, #read!, #skip, #skip!,
|
52
52
|
#write, #write?,
|
53
|
-
#scan, #scan_until
|
53
|
+
#scan, #scan_until
|
54
|
+
#scan_pattern, #scan_pattern_while, #scan_pattern_until
|
54
55
|
#modify
|
55
56
|
|
56
57
|
The methods below deal with numeric positions (a pos) to represent the cursor location.
|
@@ -60,22 +61,13 @@ end. Also included in this list below is #prop for manipulating properities
|
|
60
61
|
for the current cursor location. One typical use is for tracking line and
|
61
62
|
column numbers.
|
62
63
|
|
63
|
-
#pos, #pos=, #to_i, #prop, #to_s,
|
64
|
+
#pos, #pos=, #pos?, #to_i, #prop, #to_s,
|
64
65
|
|
65
66
|
The position methods below use a Cursor object (Cursor::Position in the base class) to
|
66
67
|
hold the position rather than simply a numeric position. These
|
67
68
|
position objects hold a #pos and whatever is
|
68
69
|
in #prop. Also, the #pos in these objects adjust based on
|
69
|
-
insertions and deletions.
|
70
|
-
over numeric positions because
|
71
|
-
a) derived classes may have a more efficient means of holding a position (i.e. linked list could hold a node reference),
|
72
|
-
b) they may trigger things like buffering in derived classes,
|
73
|
-
c) they hold what's in #prop,
|
74
|
-
d) they adjust with insert/delete,
|
75
|
-
e) when the numeric positions can't be implemented these methods still may, and
|
76
|
-
f) the covenience of saving and possibly restoring position
|
77
|
-
before/after executing a code block is available (should be implementable before
|
78
|
-
any of the others).
|
70
|
+
insertions and deletions.
|
79
71
|
|
80
72
|
#position, #position=, #position?, #position!,
|
81
73
|
#close, #closed?
|
@@ -86,7 +78,7 @@ Return a remote #position: #+, #succ, #pred, #begin, #end
|
|
86
78
|
|
87
79
|
Access the entire collection: #empty?, #length/#size, #data, #replace
|
88
80
|
|
89
|
-
Random access: #[]/#slice, #slice!, #[]=, #<<,
|
81
|
+
Random access: #[]/#slice, #slice!, #[]=, #<<, #>>, #concat, #prepend
|
90
82
|
|
91
83
|
Enumerable: #each, #collect!/#map!
|
92
84
|
|
@@ -272,9 +264,12 @@ class Cursor
|
|
272
264
|
def new_data
|
273
265
|
@cursor.new_data
|
274
266
|
end
|
275
|
-
# read +len+ elements.
|
276
|
-
#
|
277
|
-
|
267
|
+
# read +len+ elements.
|
268
|
+
# A negative +len+ will go in reverse.
|
269
|
+
# +hold+ will hold the cursor in place.
|
270
|
+
# +hold+.nil? will make it delete.
|
271
|
+
def read(len,hold=false,buffer=nil)
|
272
|
+
buffer0 = buffer or buffer = new_data
|
278
273
|
reverse = len<0
|
279
274
|
len = len.abs
|
280
275
|
meth = method(hold.nil? ?
|
@@ -282,35 +277,39 @@ class Cursor
|
|
282
277
|
(reverse ? :read1prev : :read1next) )
|
283
278
|
len0 = 0
|
284
279
|
while len>len0
|
285
|
-
(v0 = meth.call).nil? and
|
280
|
+
(v0 = meth.call).nil? and (
|
286
281
|
skip(reverse ? len0 : -len0) if hold
|
287
|
-
len0.nonzero?&&buffer
|
282
|
+
return len0.nonzero?&&(buffer0 ? len0 : buffer)
|
288
283
|
)
|
289
284
|
buffer << v0
|
290
285
|
len0 += 1
|
291
286
|
end
|
292
287
|
skip(reverse ? len0 : -len0) if hold
|
293
|
-
buffer
|
288
|
+
buffer0 ? len0 : buffer
|
294
289
|
end
|
295
|
-
# read the remaining elements.
|
296
|
-
# hold the cursor in place.
|
297
|
-
|
290
|
+
# read the remaining elements.
|
291
|
+
# +hold+ will hold the cursor in place.
|
292
|
+
# +hold+.nil? will make it delete.
|
293
|
+
def read!(reverse=false,hold=false,buffer=nil)
|
294
|
+
buffer0 = buffer or buffer = new_data
|
298
295
|
meth = method(hold.nil? ?
|
299
296
|
(reverse ? :delete1before? : :delete1after?) :
|
300
297
|
(reverse ? :read1prev : :read1next) )
|
301
298
|
len0 = 0
|
302
299
|
loop do
|
303
|
-
(v0 = meth.call).nil? and
|
300
|
+
(v0 = meth.call).nil? and (
|
304
301
|
skip(reverse ? len0 : -len0) if hold
|
305
|
-
len0.nonzero
|
302
|
+
return len0.nonzero? && (buffer0 ? len0 : buffer)
|
306
303
|
)
|
307
304
|
buffer << v0
|
308
305
|
len0 += 1
|
309
306
|
end
|
310
307
|
end
|
311
|
-
# skip +len+ elements
|
312
|
-
#
|
313
|
-
# hold the cursor in place.
|
308
|
+
# skip +len+ elements.
|
309
|
+
# A negative +len+ will go in reverse.
|
310
|
+
# +hold+ will hold the cursor in place.
|
311
|
+
# +hold+.nil? will make it delete.
|
312
|
+
# The amount skipped is returned (<+len+ if reached beginning/end).
|
314
313
|
def skip(len,hold=false)
|
315
314
|
reverse = len<0
|
316
315
|
len = len.abs
|
@@ -319,34 +318,41 @@ class Cursor
|
|
319
318
|
(reverse ? :skip1prev : :skip1next) )
|
320
319
|
len0 = 0
|
321
320
|
while len>len0
|
322
|
-
meth.call or
|
321
|
+
meth.call or (
|
323
322
|
skip(reverse ? len0 : -len0) if hold
|
324
|
-
len0.nonzero?
|
323
|
+
return len0.nonzero?
|
325
324
|
)
|
326
325
|
len0 += 1
|
327
326
|
end
|
328
327
|
skip(reverse ? len0 : -len0) if hold
|
329
328
|
len0
|
330
329
|
end
|
331
|
-
# skip the remaining elements
|
332
|
-
#
|
333
|
-
# will
|
330
|
+
# skip the remaining elements.
|
331
|
+
# A negative +len+ will go in reverse.
|
332
|
+
# +hold+ will hold the cursor in place.
|
333
|
+
# +hold+.nil? will make it delete.
|
334
|
+
# The amount skipped is returned.
|
334
335
|
def skip!(reverse=false,hold=false)
|
335
336
|
meth = method(hold.nil? ?
|
336
337
|
(reverse ? :delete1before : :delete1after) :
|
337
338
|
(reverse ? :skip1prev : :skip1next) )
|
338
339
|
len0 = 0
|
339
340
|
loop do
|
340
|
-
meth.call or
|
341
|
+
meth.call or (
|
341
342
|
skip(reverse ? len0 : -len0) if hold
|
342
|
-
len0.nonzero?
|
343
|
+
return len0.nonzero?
|
343
344
|
)
|
344
345
|
len0 += 1
|
345
346
|
end
|
346
347
|
end
|
347
|
-
# write a sequence of elements
|
348
|
-
# hold the cursor in place.
|
348
|
+
# write a sequence of elements (+value+).
|
349
|
+
# +hold+ will hold the cursor in place.
|
350
|
+
# +hold+.nil? will make it insert.
|
349
351
|
# +overwrite_only+ will prevent insertion when the cursor is at the beginning/end.
|
352
|
+
# When the entire +value+ is written, the length is returned.
|
353
|
+
# When nothing of the +value+ could be written, +nil+ is returned.
|
354
|
+
# When a portion of +value+ is written, the negative of the length written
|
355
|
+
# is returned.
|
350
356
|
def write(value,reverse=false,hold=false,overwrite_only=false)
|
351
357
|
meth = method(
|
352
358
|
hold.nil? ? (reverse ? :insert1after : :insert1before) :
|
@@ -354,53 +360,68 @@ class Cursor
|
|
354
360
|
(reverse ? :write1prev! : :write1next!) )
|
355
361
|
i = 0
|
356
362
|
until (v = value[i]).nil?
|
357
|
-
meth.call(v) or
|
363
|
+
meth.call(v) or (
|
358
364
|
skip(reverse ? i : -i) if hold
|
359
|
-
i.nonzero?
|
365
|
+
return (-i).nonzero?
|
360
366
|
)
|
361
367
|
i += 1
|
362
368
|
end
|
363
369
|
skip(reverse ? i : -i) if hold
|
364
370
|
i
|
365
371
|
end
|
366
|
-
# overwrite a sequence of elements
|
372
|
+
# overwrite a sequence of elements while reading what is overwritten.
|
373
|
+
# and return the sequence overwritten.
|
367
374
|
# +hold+ will hold the cursor in place.
|
368
|
-
def write?(value,reverse=false,hold=false,buffer=
|
375
|
+
def write?(value,reverse=false,hold=false,buffer=nil)
|
376
|
+
buffer0 = buffer or buffer = new_data
|
369
377
|
meth = method(reverse ? :write1prev? : :write1next?)
|
370
378
|
i = 0
|
371
379
|
until (v = value[i]).nil?
|
372
|
-
(v0 = meth.call(v)).nil? and
|
380
|
+
(v0 = meth.call(v)).nil? and (
|
373
381
|
skip(reverse ? i : -i) if hold
|
374
|
-
i.nonzero? && buffer
|
382
|
+
return i.nonzero? && (buffer0 ? -i : buffer)
|
375
383
|
)
|
376
384
|
buffer << v0
|
377
385
|
i += 1
|
378
386
|
end
|
379
387
|
skip(reverse ? i : -i) if hold
|
380
|
-
buffer
|
381
|
-
end
|
382
|
-
# scan for +value
|
383
|
-
# and the cursor
|
384
|
-
#
|
385
|
-
# +
|
386
|
-
|
387
|
-
|
388
|
+
buffer0 ? i : buffer
|
389
|
+
end
|
390
|
+
# scan for the sequence +value+. Will terminate on the first mismatching
|
391
|
+
# element and leave the cursor at that mismatched element.
|
392
|
+
# Without +buffer+, the matching sequence found will be returned.
|
393
|
+
# With +buffer+, an integer will be returned where its absolute value is the
|
394
|
+
# number of elements matched. Positive means the entire sequence matched
|
395
|
+
# and negative means only part of the sequence matched.
|
396
|
+
# If there is a mismatch on the very first element, +nil+ is returned
|
397
|
+
# (regardless of +buffer+).
|
398
|
+
def scan(value,reverse=false,hold=false,buffer=nil)
|
399
|
+
buffer0 = buffer or buffer = new_data
|
400
|
+
meth = method(reverse ? :scan1prev : :scan1next)
|
388
401
|
i = 0
|
389
402
|
until (v = value[i]).nil?
|
390
|
-
v0 = meth.call
|
391
|
-
|
392
|
-
skip(reverse ? i : -i) if
|
393
|
-
|
403
|
+
v0 = meth.call(v)
|
404
|
+
!v0.nil? or (
|
405
|
+
skip(reverse ? i : -i) if hold
|
406
|
+
return i.nonzero? && (buffer0 ? -i : buffer)
|
394
407
|
)
|
395
408
|
buffer << v0
|
409
|
+
i += 1
|
396
410
|
end
|
397
411
|
skip(reverse ? i : -i) if hold
|
398
|
-
buffer
|
399
|
-
end
|
400
|
-
# scan for +value+ until it is found (or the end is reached)
|
401
|
-
#
|
402
|
-
#
|
403
|
-
|
412
|
+
buffer0 ? i : buffer
|
413
|
+
end
|
414
|
+
# scan for +value+ until it is found (or the beginning/end is reached).
|
415
|
+
# +hold+.nil? will make it delete instead of read.
|
416
|
+
# Without +buffer+, sequence (including what +value+ matched) will be
|
417
|
+
# returned (or +nil+ if started at the beginning/end).
|
418
|
+
# With +buffer+, the length of the sequence scanned and the final index of
|
419
|
+
# +value+ is returned. If the cursor started at the beginnning/end,
|
420
|
+
# +nil+ will be returned in place of the sequence length. If the entire
|
421
|
+
# +value+ is not found (beginning/end reached), the index will be <=0 and
|
422
|
+
# its absolute value is the index of the mismatch.
|
423
|
+
def scan_until(value,reverse=false,hold=false,buffer=nil)
|
424
|
+
buffer0 = buffer or buffer = new_data
|
404
425
|
meth = method(hold.nil? ?
|
405
426
|
(reverse ? :delete1before? : :delete1after?) :
|
406
427
|
(reverse ? :read1prev : :read1next) )
|
@@ -408,9 +429,13 @@ class Cursor
|
|
408
429
|
len0 = 0
|
409
430
|
i = 0
|
410
431
|
until (v = value[i]).nil?
|
411
|
-
(v0 = meth.call).nil? and
|
432
|
+
(v0 = meth.call).nil? and (
|
412
433
|
skip(reverse ? len0 : -len0) if hold
|
413
|
-
|
434
|
+
if buffer0
|
435
|
+
return len0.nonzero?,-i
|
436
|
+
else
|
437
|
+
return len0.nonzero? && buffer
|
438
|
+
end
|
414
439
|
)
|
415
440
|
buffer << v0
|
416
441
|
buffer2 << v0
|
@@ -426,41 +451,166 @@ class Cursor
|
|
426
451
|
end
|
427
452
|
end
|
428
453
|
skip(reverse ? len0 : -len0) if hold
|
429
|
-
|
454
|
+
if buffer0
|
455
|
+
return len0,i
|
456
|
+
else
|
457
|
+
return buffer
|
458
|
+
end
|
430
459
|
end
|
431
|
-
# scan for +
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
460
|
+
# scan for +pattern+ at the cursor.
|
461
|
+
# +pattern+ should #match no more than than +len+ (< 0 goes in reverse) elements.
|
462
|
+
# Although +pattern+ is implicitly anchored to the current cursor location
|
463
|
+
# (\A) the current implementation is more efficient with the anchor explicit.
|
464
|
+
# +pattern+ should not use any end anchors (\Z or $).
|
465
|
+
# +pattern+.match should return a MatchData like object that needs
|
466
|
+
# to respond to #offset(0).
|
467
|
+
# An array of the what matched and this MatchData object is returned
|
468
|
+
# (or nil upon mismatch).
|
469
|
+
# #post_match isn't valid in this MatchData object.
|
470
|
+
# The cursor is advanced to right after the match unless there is mismatch or
|
471
|
+
# the cursor is requested to +hold+ position.
|
472
|
+
def scan_pattern(pattern,len=1,hold=false,buffer=nil)
|
473
|
+
reverse = len<0
|
474
|
+
matchbuffer = new_data
|
475
|
+
matchbuffer_len = read(len,false,matchbuffer) or return
|
476
|
+
(matchdata = pattern.match(matchbuffer) and
|
477
|
+
(b,e = matchdata.offset(0);b.zero?)) or (
|
478
|
+
skip(reverse ? +matchbuffer_len : -matchbuffer_len)
|
479
|
+
return
|
480
|
+
)
|
481
|
+
back_len = matchbuffer_len-e
|
482
|
+
matchbuffer.slice!(e,back_len)
|
483
|
+
back_len += e if hold
|
484
|
+
skip(reverse ? +back_len : -back_len)
|
485
|
+
if buffer
|
486
|
+
buffer.concat(matchbuffer)
|
487
|
+
matchdata
|
488
|
+
else
|
489
|
+
matchbuffer
|
490
|
+
end
|
491
|
+
end
|
492
|
+
# scan until +pattern+ is found.
|
493
|
+
# +pattern+ should need no more than +len+ (<0 goes in reverse) elements
|
494
|
+
# to determine a match.
|
495
|
+
# Initially, a sequence of +init+*+len+ elements is read to be matched.
|
496
|
+
# Until the +pattern+ matches the buffer, more data is read and tried to be
|
497
|
+
# matched with +pattern+.
|
498
|
+
# +pattern+.match should return a MatchData like object that needs
|
499
|
+
# to respond to #offset(0).
|
500
|
+
# An array of the prematch, what matched, and this
|
501
|
+
# MatchData object is returned (or nil upon mismatch).
|
502
|
+
# #post_match and #pre_match aren't valid in this MatchData object.
|
503
|
+
# The cursor is advanced to right after the match unless there is mismatch or
|
504
|
+
# the cursor is requested to +hold+ position.
|
505
|
+
def scan_pattern_until(pattern,len=1,hold=false,buffer=nil,init=16)
|
506
|
+
buffer0 = buffer or buffer = new_data
|
507
|
+
reverse = len<0
|
508
|
+
read_len = len*init
|
509
|
+
len = -len if reverse
|
510
|
+
matchbuffer = new_data
|
511
|
+
buffer_len = 0
|
512
|
+
matchbuffer_len = 0
|
513
|
+
read_len1 = read(read_len,false,matchbuffer) or (
|
514
|
+
if buffer0
|
515
|
+
return nil,nil,nil
|
516
|
+
else
|
517
|
+
return nil
|
518
|
+
end
|
519
|
+
)
|
520
|
+
matchbuffer_len += read_len1
|
521
|
+
until matchdata = pattern.match(matchbuffer)
|
522
|
+
matchbuffer_len>=len and (
|
523
|
+
buffer.concat(matchbuffer.slice!(0,matchbuffer_len-len+1))
|
524
|
+
buffer_len += matchbuffer_len-len+1
|
525
|
+
matchbuffer_len = len-1
|
526
|
+
read_len *= 2
|
527
|
+
read_len1 = read(read_len,false,matchbuffer)) or (
|
528
|
+
buffer.concat(matchbuffer)
|
529
|
+
buffer_len += matchbuffer_len
|
530
|
+
skip(reverse ? +buffer_len : -buffer_len) if hold
|
531
|
+
if buffer0
|
532
|
+
return buffer_len,nil,nil
|
533
|
+
else
|
534
|
+
return buffer
|
535
|
+
end
|
440
536
|
)
|
441
|
-
|
442
|
-
|
537
|
+
p self
|
538
|
+
matchbuffer_len += read_len1
|
539
|
+
end
|
540
|
+
b,e = matchdata.offset(0)
|
541
|
+
back_len = matchbuffer_len-e
|
542
|
+
matchbuffer.slice!(e,back_len)
|
543
|
+
buffer.concat(matchbuffer)
|
544
|
+
buffer_len += e
|
545
|
+
back_len += buffer_len if hold
|
546
|
+
skip(reverse ? +back_len : -back_len)
|
547
|
+
if buffer0
|
548
|
+
return buffer_len-(e-b),buffer_len,matchdata
|
549
|
+
else
|
550
|
+
return buffer
|
443
551
|
end
|
444
|
-
skip(reverse ? i : -i) if hold
|
445
|
-
buffer
|
446
552
|
end
|
447
|
-
#
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
553
|
+
# scan while +pattern+ matches more data.
|
554
|
+
# +pattern+ should be a zero or more ("*") loop with the maximum match
|
555
|
+
# length per iteration being +len+ (< 0 goes in reverse).
|
556
|
+
# +pattern+ should not use any anchors (\A, \Z, or $).
|
557
|
+
# Initially, a sequence of +init+*+len+ elements is read to be matched.
|
558
|
+
# As long as the pattern matches more data, more data is read to be matched
|
559
|
+
# with the pattern.
|
560
|
+
# +pattern+.match should return a MatchData like object that needs
|
561
|
+
# to respond to #offset(0).
|
562
|
+
# An array of the what matched and this
|
563
|
+
# MatchData object is returned (or nil for a mismatch or empty match).
|
564
|
+
# #post_match, #pre_match, everything related to #[0] aren't valid in this
|
565
|
+
# MatchData object.
|
566
|
+
# The cursor is advanced to right after the match unless there is mismatch or
|
567
|
+
# the cursor is requested to +hold+ position.
|
568
|
+
def scan_pattern_while(pattern,len=1,hold=false,buffer=nil,init=16)
|
569
|
+
buffer0 = buffer or buffer = new_data
|
570
|
+
reverse = len<0
|
571
|
+
read_len = len*init
|
572
|
+
len = -len if reverse
|
573
|
+
matchbuffer = new_data
|
574
|
+
buffer_len = 0
|
575
|
+
matchbuffer_len = 0
|
576
|
+
while matchbuffer_len<len and
|
577
|
+
read_len1 = read(read_len,false,matchbuffer) and
|
578
|
+
matchbuffer_len += read_len1 and
|
579
|
+
matchdata = pattern.match(matchbuffer) and
|
580
|
+
(b,e = matchdata.offset(0);b.zero? and e.nonzero?)
|
581
|
+
buffer.concat(matchbuffer.slice!(0,e))
|
582
|
+
buffer_len += e
|
583
|
+
matchbuffer_len -= e
|
584
|
+
read_len *= 2
|
585
|
+
end
|
586
|
+
back_len = matchbuffer_len+(hold ? buffer_len : 0)
|
587
|
+
skip(reverse ? +back_len : -back_len)
|
588
|
+
if buffer0
|
589
|
+
buffer_len.nonzero?
|
590
|
+
else
|
591
|
+
buffer_len.nonzero? and buffer
|
454
592
|
end
|
455
|
-
skip(reverse ? len0 : -len0) if hold
|
456
|
-
buffer
|
457
593
|
end
|
458
594
|
|
459
|
-
#
|
595
|
+
# Without a block, this will return a numeric position. When not +reverse+,
|
460
596
|
# this numeric position is the number of elements from the beginning (0 is at the beginning). With
|
461
597
|
# +reverse+ it is negative and the number of elements from the end (-0.0 is at the end).
|
462
|
-
|
463
|
-
|
598
|
+
# With a code block, it will execute some code and come back to where the
|
599
|
+
# cursor started. The result of the block will be returned.
|
600
|
+
# Within the block, no operations that insert or delete elements
|
601
|
+
# before (or after if +reverse+) the starting cursor location should be executed.
|
602
|
+
# The cursor will be left in an unknown location if that happens.
|
603
|
+
def pos(reverse=false,&code) # :yield:
|
604
|
+
p = reverse ? -(skip!(false,true)||0.0) : (skip!(true,true)||0)
|
605
|
+
if code
|
606
|
+
begin
|
607
|
+
code[]
|
608
|
+
ensure
|
609
|
+
self.pos = p
|
610
|
+
end
|
611
|
+
else
|
612
|
+
p
|
613
|
+
end
|
464
614
|
end
|
465
615
|
# Returns #pos.to_i
|
466
616
|
def to_i
|
@@ -474,6 +624,23 @@ class Cursor
|
|
474
624
|
len.to_i.abs==skip(len) or raise(IndexError,"invalid pos=#{p}")
|
475
625
|
p
|
476
626
|
end
|
627
|
+
# Without a block, this checks to see if p is a valid numeric position.
|
628
|
+
# With a block, it will execute the block and come back to where the cursor
|
629
|
+
# started if the result of the block was +false+/+nil+. The result of the
|
630
|
+
# block will be returned. The restrictions on the #pos block also apply here.
|
631
|
+
def pos?(p=false,&code) # :yield:
|
632
|
+
if code
|
633
|
+
p = pos(p)
|
634
|
+
begin
|
635
|
+
ret = code[]
|
636
|
+
ensure
|
637
|
+
self.pos = p if !ret
|
638
|
+
end
|
639
|
+
else
|
640
|
+
len = p-pos((p.nonzero?||1.0/p)<0)
|
641
|
+
len.to_i.abs==skip(len,true)
|
642
|
+
end
|
643
|
+
end
|
477
644
|
# Get (no +value+) and set cursor properties. Normally, +name+
|
478
645
|
# should be a symbol. If +name+ is +nil+, it wil get/set using a hash
|
479
646
|
# representing all of the properties.
|
@@ -506,7 +673,9 @@ class Cursor
|
|
506
673
|
# represent the current location of the cursor. When +reverse+, it will
|
507
674
|
# anchor the #position to the element after. Otherwise it will anchor it
|
508
675
|
# to the element before. This anchoring is used when an insertion occurs at
|
509
|
-
# that point affecting how this position is adjusted.
|
676
|
+
# that point affecting how this position is adjusted. If the element that a
|
677
|
+
# #position is anchored to is deleted, that #position may become invalid
|
678
|
+
# or have an unknown behavior.
|
510
679
|
#
|
511
680
|
# With a code block, it saves the #position (also using +reverse+),
|
512
681
|
# executes the code block, and returns the #position to where it
|
@@ -544,7 +713,7 @@ class Cursor
|
|
544
713
|
# left it. The result of the code block is returned. This is useful when
|
545
714
|
# you want the cursor to stay for a pass/match, and return to try something
|
546
715
|
# else for a fail/mismatch.
|
547
|
-
def position?(p=
|
716
|
+
def position?(p=false,&code) # :yield:
|
548
717
|
if code
|
549
718
|
start = position(p)
|
550
719
|
begin
|
@@ -556,26 +725,19 @@ class Cursor
|
|
556
725
|
start.close
|
557
726
|
end
|
558
727
|
end
|
559
|
-
elsif p
|
560
|
-
@positions && @positions.include?(p) || equal?(p) || nil
|
561
728
|
else
|
562
|
-
@positions &&
|
729
|
+
@positions && @positions.include?(p) || equal?(p) || false
|
563
730
|
end
|
564
731
|
end
|
565
|
-
#
|
566
|
-
#
|
567
|
-
def
|
568
|
-
|
569
|
-
@positions.delete(p)
|
570
|
-
elsif @positions
|
571
|
-
@positions.each { |p| p.close }
|
572
|
-
remove_instance_variable(:@positions)
|
573
|
-
end
|
732
|
+
# Delete +p+ from the list of children (from #position).
|
733
|
+
# Should only be used by child #position.
|
734
|
+
def _delete_position(p) # :nodoc:
|
735
|
+
@positions.delete(p)
|
574
736
|
self
|
575
737
|
end
|
576
|
-
# Close the cursor. This will also close every child #position.
|
738
|
+
# Close the cursor. This will also close/invalidate every child #position.
|
577
739
|
def close
|
578
|
-
|
740
|
+
@positions and @positions.each { |p| p.close }
|
579
741
|
# this should make just about any operation fail
|
580
742
|
instance_variables.each { |v| instance_variable_set(v,nil) }
|
581
743
|
nil
|
@@ -598,24 +760,24 @@ class Cursor
|
|
598
760
|
end
|
599
761
|
# Returns a new #position increased by +len+ (positive or negative).
|
600
762
|
def +(len)
|
601
|
-
|
763
|
+
pos { skip(len);position((len.nonzero?||1.0/len)<0) }
|
602
764
|
end
|
603
765
|
|
604
766
|
# Return a new #position for next cursor location or +nil+ if we are at the end.
|
605
767
|
def succ
|
606
|
-
|
768
|
+
pos(false) { skip1next && position(false) }
|
607
769
|
end
|
608
770
|
# Return a new #position for previous cursor location or +nil+ if we are at the beginning.
|
609
771
|
def pred
|
610
|
-
|
772
|
+
pos(true) { skip1prev && position(true) }
|
611
773
|
end
|
612
774
|
# Return a new #position for the beginning.
|
613
775
|
def begin
|
614
|
-
|
776
|
+
pos(false) { skip!(true);position(false) }
|
615
777
|
end
|
616
778
|
# Return a new #position for the end.
|
617
779
|
def end
|
618
|
-
|
780
|
+
pos(true) { skip!(false);position(true) }
|
619
781
|
end
|
620
782
|
# Returns the number of elements.
|
621
783
|
def size
|
@@ -631,55 +793,83 @@ class Cursor
|
|
631
793
|
(skip!(false,nil)||0)+(skip!(true,nil)||0)
|
632
794
|
end
|
633
795
|
# Replace the all of the data and return self.
|
634
|
-
def replace(
|
796
|
+
def replace(value,reverse=false)
|
635
797
|
clear
|
636
|
-
write(
|
798
|
+
write(value,reverse,nil)
|
637
799
|
self
|
638
800
|
end
|
639
|
-
# Get all of the data
|
640
|
-
def data
|
641
|
-
skip!(
|
642
|
-
read!(false)
|
801
|
+
# Get all of the data.
|
802
|
+
def data(reverse=false)
|
803
|
+
pos { skip!(!reverse);read!(reverse) }
|
643
804
|
end
|
644
805
|
# Appends a single element at the end and returns self.
|
645
806
|
def << (v)
|
646
|
-
skip!(false)
|
647
|
-
insert1before(v)
|
807
|
+
pos(false) { skip!(false);write1next!(v) }
|
648
808
|
self
|
649
809
|
end
|
650
810
|
# Prepends a single element at the beginning and returns self.
|
651
811
|
def >> (v)
|
652
|
-
skip!(true)
|
653
|
-
|
812
|
+
pos(true) { skip!(true);write1prev!(v) }
|
813
|
+
self
|
814
|
+
end
|
815
|
+
# Appends a sequence to the end and returns self.
|
816
|
+
def concat(value)
|
817
|
+
pos(false) { skip!(false);write(value,false) }
|
818
|
+
self
|
819
|
+
end
|
820
|
+
# Prepends a sequence to the beginning and returns self.
|
821
|
+
def prepend(value)
|
822
|
+
pos(true) { skip!(true);write(value,true) }
|
823
|
+
self
|
824
|
+
end
|
825
|
+
def push(v)
|
826
|
+
pos(false) { skip!(false);write1next!(v) }
|
827
|
+
self
|
828
|
+
end
|
829
|
+
def unshift(v)
|
830
|
+
pos(true) { skip!(true);write1prev!(v) }
|
654
831
|
self
|
655
832
|
end
|
833
|
+
def pop
|
834
|
+
pos(false) { skip!(false);delete1before? }
|
835
|
+
end
|
836
|
+
def shift
|
837
|
+
pos(true) { skip!(true);delete1after? }
|
838
|
+
end
|
839
|
+
def first
|
840
|
+
pos(true) { skip!(true);read1next }
|
841
|
+
end
|
842
|
+
def last
|
843
|
+
pos(false) { skip!(false);read1prev }
|
844
|
+
end
|
656
845
|
|
657
846
|
# Provides random access for the cursor like what is in Array/String.
|
658
847
|
# +index+ can be +nil+ (start at the current location) or a numeric (for #pos=).
|
659
848
|
# +len+ can be +nil+ (get a single element) or the number of elements to
|
660
|
-
# #read (positive or negative). The cursor is left at
|
661
|
-
#
|
849
|
+
# #read (positive or negative). The cursor is left at where the access
|
850
|
+
# started (+index+==nil : no chnage, or +index).
|
662
851
|
def slice(index=nil,len=nil)
|
663
852
|
self.pos = index if index
|
664
|
-
len
|
853
|
+
len ? read(len,true) : read1after
|
665
854
|
end
|
666
855
|
alias [] slice
|
667
856
|
# Like #slice except the element(s) are deleted.
|
668
857
|
def slice!(index=nil,len=nil)
|
669
858
|
self.pos = index if index
|
670
|
-
len
|
859
|
+
len ? read(len,nil) : delete1after?
|
671
860
|
end
|
672
861
|
# Similar to #slice except data is written. +index+ and +len+ have the
|
673
|
-
# same meaning as they do in #slice. +
|
862
|
+
# same meaning as they do in #slice. +len+ elements are deleted and +value+
|
863
|
+
# is inserted.
|
674
864
|
def []=(*args) # :args: (index=nil,len=nil,value)
|
675
865
|
value = args.slice!(-1)
|
676
866
|
index,len = *args
|
677
867
|
self.pos = index if index
|
678
|
-
if len
|
679
|
-
write1after!(value)
|
680
|
-
else
|
868
|
+
if len
|
681
869
|
skip(len,nil)
|
682
|
-
write(value,(len.nonzero?||1.0/len)
|
870
|
+
write(value,(len.nonzero?||1.0/len)<0,nil)
|
871
|
+
else
|
872
|
+
write1after!(value)
|
683
873
|
end
|
684
874
|
value
|
685
875
|
end
|
@@ -690,7 +880,7 @@ class Cursor
|
|
690
880
|
# #scan_until(+terminator+) instead of using a single element.
|
691
881
|
# The cursor will be left at the end (or beginning if +reverse+).
|
692
882
|
# nil is returned (or the break value if the code does a break).
|
693
|
-
def each(index
|
883
|
+
def each(index=0,reverse=false,terminator=nil,&code) # :yield: value
|
694
884
|
self.pos = index if index
|
695
885
|
continue = true
|
696
886
|
if terminator.nil?
|
@@ -713,7 +903,7 @@ class Cursor
|
|
713
903
|
# When the code returns +nil+ the element/sequence will be deleted.
|
714
904
|
# Accepts the same enhancements as #each does.
|
715
905
|
# nil is returned (or the break value if the code does a break).
|
716
|
-
def collect!(index
|
906
|
+
def collect!(index=0,reverse=false,terminator=nil,&code) # :yield: value
|
717
907
|
self.pos = index if index
|
718
908
|
continue = true
|
719
909
|
if terminator.nil?
|