cursor 0.8 → 0.9
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.
- 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?
|