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 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.53 2005/07/21 15:15:38 eric_mahurin Exp $
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, #scan_partial,
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. It is preferrable to use these position objects
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. A negative +len+ will go in reverse. +hold+ will
276
- # hold the cursor in place. +hold+==+nil+ (not false) will make it delete.
277
- def read(len,hold=false,buffer=new_data)
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 return(
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. +hold+ will
296
- # hold the cursor in place. +hold+==+nil+ (not false) will make it delete.
297
- def read!(reverse=false,hold=false,buffer=new_data)
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 return(
300
+ (v0 = meth.call).nil? and (
304
301
  skip(reverse ? len0 : -len0) if hold
305
- len0.nonzero?&&buffer
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 and return the count. A negative +len+ will go in
312
- # reverse. +hold+ will
313
- # hold the cursor in place. +hold+==+nil+ (not false) will make it delete.
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 return(
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 and return the count. A negative +len+ will go in
332
- # reverse. +hold+ will hold the cursor in place. +hold+==+nil+ (not false)
333
- # will make it delete.
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 return(
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 and return the overwrite count. +hold+ will
348
- # hold the cursor in place. +hold+==+nil+ (not false) will make it insert.
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 return(
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 and return the sequence overwritten.
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=value.class.new)
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 return(
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+ at the cursor. When there is a mismatch, +nil+ is returned
383
- # and the cursor is move back to where it started (unless hold==+nil+:
384
- # cursor position will indeterminate).
385
- # +hold+ will hold the cursor in place even on a match.
386
- def scan(value,reverse=false,hold=false,buffer=value.class.new)
387
- meth = method(reverse ? :read1prev : :read1next)
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
- (!v0.nil? and i += 1 and v==v0) or return(
392
- skip(reverse ? i : -i) if !hold.nil?
393
- nil
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) and
401
- # return that entire sequence. +hold+ will
402
- # hold the cursor in place. +hold+==+nil+ (not false) will make it delete.
403
- def scan_until(value,reverse=false,hold=false,buffer=value.class.new)
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 return(
432
+ (v0 = meth.call).nil? and (
412
433
  skip(reverse ? len0 : -len0) if hold
413
- len0.nonzero?&&buffer
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
- buffer
454
+ if buffer0
455
+ return len0,i
456
+ else
457
+ return buffer
458
+ end
430
459
  end
431
- # scan for +value+ and return as much of the sequence that matched
432
- def scan_partial(value,reverse=false,hold=false,buffer=value.class.new)
433
- meth = method(reverse ? :scan1prev : :scan1next)
434
- i = 0
435
- until (v = value[i]).nil?
436
- v0 = meth.call(v)
437
- !v0.nil? or return(
438
- skip(reverse ? i : -i) if hold
439
- i.nonzero?&&buffer
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
- buffer << v0
442
- i += 1
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
- # modify elements using +lookup+[orignalElement] until that returns nil
448
- def modify(lookup,reverse=false,hold=false,buffer=new_data)
449
- meth = method(reverse ? :modify1prev : :modify1next)
450
- len0 = 0
451
- until (v0 = meth.call(lookup)).nil?
452
- buffer << v0
453
- len0 += 1
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
- # This will return a numeric position. When not +reverse+,
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
- def pos(reverse=false)
463
- reverse ? -(skip!(false,true)||0.0) : (skip!(true,true)||0)
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=nil,&code) # :yield:
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 && (!@positions.empty? || nil)
729
+ @positions && @positions.include?(p) || equal?(p) || false
563
730
  end
564
731
  end
565
- # Discard/close every child #position (+p+=+nil+) or discard (not close)
566
- # the give +p+ (you probably want +p+.close instead).
567
- def position!(p=nil)
568
- if p
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
- position!
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
- position { skip(len);position((len.nonzero?||1.0/len)<0) }
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
- position { skip1next && position(false) }
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
- position { skip1prev && position(true) }
772
+ pos(true) { skip1prev && position(true) }
611
773
  end
612
774
  # Return a new #position for the beginning.
613
775
  def begin
614
- position { skip!(true);position(false) }
776
+ pos(false) { skip!(true);position(false) }
615
777
  end
616
778
  # Return a new #position for the end.
617
779
  def end
618
- position { skip!(false);position(true) }
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(obj)
796
+ def replace(value,reverse=false)
635
797
  clear
636
- write(obj,false,nil)
798
+ write(value,reverse,nil)
637
799
  self
638
800
  end
639
- # Get all of the data and leave the cursor at the end.
640
- def data
641
- skip!(true)
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
- insert1after(v)
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 +index+ (or not
661
- # moved if +nil+).
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.nil? ? read1after : read(len,true)
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.nil? ? delete1after? : read(len,nil)
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. +value+ is written using #write.
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.nil?
679
- write1after!(value)
680
- else
868
+ if len
681
869
  skip(len,nil)
682
- write(value,(len.nonzero?||1.0/len)>=0,nil)
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=+0.0,reverse=false,terminator=nil,&code) # :yield: value
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=+0.0,reverse=false,terminator=nil,&code) # :yield: value
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?