cursor 0.6 → 0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/cursor.rb +626 -1143
- data/cursor/buffered.rb +89 -0
- data/cursor/circular.rb +245 -0
- data/cursor/circular/indexed.rb +51 -0
- data/cursor/circular/linked.rb +98 -0
- data/cursor/circular/position.rb +105 -0
- data/cursor/circular/shifting.rb +48 -0
- data/cursor/circular/split.rb +106 -0
- data/cursor/indexed.rb +52 -0
- data/cursor/io.rb +342 -0
- data/cursor/lined.rb +49 -0
- data/cursor/linked.rb +62 -0
- data/cursor/position.rb +145 -0
- data/cursor/reversed.rb +122 -0
- data/cursor/shifting.rb +40 -0
- data/cursor/split.rb +45 -0
- data/cursor/test.rb +519 -0
- data/cursor/test_circulars.rb +87 -0
- data/cursor/test_cursors.rb +112 -0
- data/cursor/usedeleteinsert.rb +150 -0
- data/cursor/usenext.rb +145 -0
- data/cursor/usenext/position.rb +71 -0
- data/cursor/useposition.rb +42 -0
- data/cursor/usereadwrite.rb +126 -0
- data/duck.rb +31 -0
- data/regexp_cursor.rb +50 -0
- data/weakrefset.rb +130 -0
- metadata +35 -7
data/cursor/lined.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# $Id: lined.rb,v 1.5 2005/07/01 22:06:45 eric_mahurin Exp $
|
2
|
+
|
3
|
+
require 'cursor'
|
4
|
+
require 'cursor/usedeleteinsert'
|
5
|
+
|
6
|
+
class Cursor
|
7
|
+
# This class tracks the current line and column. Both line and column
|
8
|
+
# start at 0. When a newline is found, the line is incremented and the
|
9
|
+
# column is reset. With other characters, the column is incremented.
|
10
|
+
# The line and column numbers are only tracked when reading forward, but
|
11
|
+
# using #position (and friends) will hold the line and column.
|
12
|
+
class Lined < Cursor
|
13
|
+
include UseDeleteInsert
|
14
|
+
# Create a new Cursor from another that tracks line and column.
|
15
|
+
# +newline+ can be any valid argument for String#index. For example,
|
16
|
+
# +newline+=+/\n|\r(?!\n)/+ could be used for unix, mac, dos/cpm style
|
17
|
+
# newlines.
|
18
|
+
def initialize(cursor,newline=?\n)
|
19
|
+
@cursor = cursor
|
20
|
+
@newline = newline
|
21
|
+
prop(:line,0)
|
22
|
+
prop(:column,0)
|
23
|
+
end
|
24
|
+
# :stopdoc:
|
25
|
+
protected
|
26
|
+
def _delete1after?
|
27
|
+
@cursor.delete1after?
|
28
|
+
end
|
29
|
+
def _delete1before?
|
30
|
+
@cursor.delete1before?
|
31
|
+
end
|
32
|
+
def _insert1before(v)
|
33
|
+
if @newline==v
|
34
|
+
prop(:line,prop(:line)+1)
|
35
|
+
prop(:column,0)
|
36
|
+
else
|
37
|
+
prop(:column,prop(:column)+1)
|
38
|
+
end
|
39
|
+
@cursor.insert1before(v)
|
40
|
+
end
|
41
|
+
def _insert1after(v)
|
42
|
+
@cursor.insert1after(v)
|
43
|
+
end
|
44
|
+
public
|
45
|
+
# :startdoc:
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
data/cursor/linked.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# $Id: linked.rb,v 1.3 2005/07/21 15:15:38 eric_mahurin Exp $
|
2
|
+
|
3
|
+
require 'cursor'
|
4
|
+
require 'cursor/usedeleteinsert'
|
5
|
+
|
6
|
+
class Cursor
|
7
|
+
# Double-linked list
|
8
|
+
class Linked < Cursor
|
9
|
+
include UseDeleteInsert
|
10
|
+
def initialize(before=[],after=before.class.new)
|
11
|
+
@next = nil
|
12
|
+
@prev = nil
|
13
|
+
@data_class = before.class
|
14
|
+
write(before,false,nil)
|
15
|
+
write(after,true,nil)
|
16
|
+
end
|
17
|
+
# :stopdoc:
|
18
|
+
def new_data
|
19
|
+
@data_class.new
|
20
|
+
end
|
21
|
+
protected
|
22
|
+
def _delete1after?
|
23
|
+
if @next
|
24
|
+
begin
|
25
|
+
@next[1]
|
26
|
+
ensure
|
27
|
+
@next = @next[2]
|
28
|
+
@next[0] = @prev if @next
|
29
|
+
@prev[2] = @next if @prev
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
def _delete1before?
|
34
|
+
if @prev
|
35
|
+
begin
|
36
|
+
@prev[1]
|
37
|
+
ensure
|
38
|
+
@prev = @prev[0]
|
39
|
+
@next[0] = @prev if @next
|
40
|
+
@prev[2] = @next if @prev
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
def _insert1before(v)
|
45
|
+
node = [@prev,v,@next]
|
46
|
+
@prev[2] = node if @prev
|
47
|
+
@next[0] = node if @next
|
48
|
+
@prev = node
|
49
|
+
true
|
50
|
+
end
|
51
|
+
def _insert1after(v)
|
52
|
+
node = [@prev,v,@next]
|
53
|
+
@prev[2] = node if @prev
|
54
|
+
@next[0] = node if @next
|
55
|
+
@next = node
|
56
|
+
true
|
57
|
+
end
|
58
|
+
# :startdoc:
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
data/cursor/position.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# $Id: position.rb,v 1.18 2005/07/18 14:46:28 eric_mahurin Exp $
|
2
|
+
|
3
|
+
require 'cursor'
|
4
|
+
|
5
|
+
class Cursor
|
6
|
+
# Objects in this class are mainly used to simply mark/remember the location
|
7
|
+
# of a parent cursor. But, this class also has the fully functionality of the
|
8
|
+
# parent. When this child wants to do an operation, it uses the parent to
|
9
|
+
# do it and returns the parent to where it was.
|
10
|
+
class Position < Cursor # :nodoc:
|
11
|
+
def initialize(parent,reverse=false)
|
12
|
+
@parent = parent
|
13
|
+
@anchor_after = reverse
|
14
|
+
@pos = @parent.__send__(:pos)
|
15
|
+
prop(nil,@parent.prop)
|
16
|
+
end
|
17
|
+
def new_data
|
18
|
+
@parent.new_data
|
19
|
+
end
|
20
|
+
public
|
21
|
+
localMethods = %w(
|
22
|
+
prop pos pos= to_s to_i position position= position? position!
|
23
|
+
close closed? each collect! map!)
|
24
|
+
stableMethods = %w(
|
25
|
+
read1after read1before skip1after skip1before
|
26
|
+
write1after write1before write1after? write1before?
|
27
|
+
position pos to_i begin end begin? end? succ pred + - <=>
|
28
|
+
)
|
29
|
+
stableMethods = %w()
|
30
|
+
anywhereMethods = %w(
|
31
|
+
replace clear empty? size length begin end position! close closed?
|
32
|
+
)
|
33
|
+
anywhereMethods = %w()
|
34
|
+
selfMethods = %w(
|
35
|
+
>> << begin! end! succ! pred! replace
|
36
|
+
)
|
37
|
+
repositionMethods = Cursor.public_instance_methods(false)
|
38
|
+
repositionMethods -= anywhereMethods
|
39
|
+
repositionMethods -= localMethods
|
40
|
+
repositionMethods.each { |m|
|
41
|
+
and_self = selfMethods.include?(m) ? " and self" : ""
|
42
|
+
update = stableMethods.include?(m) ? "" : "
|
43
|
+
ensure
|
44
|
+
self.position = @parent
|
45
|
+
"
|
46
|
+
eval("def #{m}(*args,&block)
|
47
|
+
@parent.position {
|
48
|
+
@parent.position = self
|
49
|
+
begin
|
50
|
+
@parent.__send__(#{m.to_sym.inspect},*args,&block)
|
51
|
+
#{update}
|
52
|
+
end
|
53
|
+
}#{and_self}
|
54
|
+
end")
|
55
|
+
}
|
56
|
+
anywhereMethods -= localMethods
|
57
|
+
anywhereMethods.each { |m|
|
58
|
+
and_self = selfMethods.include?(m) ? " and self" : ""
|
59
|
+
eval("def #{m}(*args,&block)
|
60
|
+
@parent.__send__(#{m.to_sym.inspect},*args,&block)#{and_self}
|
61
|
+
end")
|
62
|
+
}
|
63
|
+
|
64
|
+
def pos(reverse=false)
|
65
|
+
if reverse.nil? ? @anchor_after : reverse
|
66
|
+
(@pos.to_i-@parent.size).nonzero? || -0.0
|
67
|
+
else
|
68
|
+
@pos
|
69
|
+
end
|
70
|
+
end
|
71
|
+
def pos=(p)
|
72
|
+
if (p.nonzero?||1.0/p)<0
|
73
|
+
@anchor_after = true
|
74
|
+
@pos = p.to_i+@parent.size
|
75
|
+
p
|
76
|
+
else
|
77
|
+
@anchor_after = false
|
78
|
+
@pos = p
|
79
|
+
end
|
80
|
+
end
|
81
|
+
def position(reverse=false,&code) # :yield:
|
82
|
+
p = @parent.position {
|
83
|
+
@parent.position = self
|
84
|
+
@parent.position(reverse)
|
85
|
+
}
|
86
|
+
if code
|
87
|
+
begin
|
88
|
+
code[]
|
89
|
+
ensure
|
90
|
+
begin
|
91
|
+
self.position = p
|
92
|
+
ensure
|
93
|
+
p.close
|
94
|
+
end
|
95
|
+
end
|
96
|
+
else
|
97
|
+
p
|
98
|
+
end
|
99
|
+
end
|
100
|
+
def position?(p=nil,&code) # :yield:
|
101
|
+
if code
|
102
|
+
super
|
103
|
+
elsif p
|
104
|
+
@parent.position?(p)
|
105
|
+
else
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
def position!(p=nil)
|
110
|
+
if p
|
111
|
+
@parent.position!(p)
|
112
|
+
self
|
113
|
+
else
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
def close
|
118
|
+
@parent.position!(self)
|
119
|
+
super
|
120
|
+
end
|
121
|
+
protected
|
122
|
+
def _deletion(pos,len=1,reverse=false,dummy=nil)
|
123
|
+
if @pos==pos
|
124
|
+
@anchor_after = false
|
125
|
+
elsif @pos>pos
|
126
|
+
@pos -= len
|
127
|
+
if @pos<pos
|
128
|
+
@pos = pos
|
129
|
+
@anchor_after = !reverse
|
130
|
+
elsif @pos==pos
|
131
|
+
@anchor_after = true
|
132
|
+
end
|
133
|
+
end
|
134
|
+
nil
|
135
|
+
end
|
136
|
+
def _insertion(pos,len=1,dummmy=nil)
|
137
|
+
if @pos>=pos+(@anchor_after ? 0 : 1)
|
138
|
+
@pos += len
|
139
|
+
end
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
data/cursor/reversed.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
# $Id: reversed.rb,v 1.10 2005/07/21 15:15:38 eric_mahurin Exp $
|
2
|
+
|
3
|
+
require 'cursor'
|
4
|
+
|
5
|
+
class Cursor
|
6
|
+
# This class can be used to reverse the direction of operations on a given
|
7
|
+
# cursor. It operates on the given cursor directly moving it around.
|
8
|
+
class Reversed < Cursor
|
9
|
+
def initialize(cursor)
|
10
|
+
@cursor = cursor
|
11
|
+
end
|
12
|
+
# :stopdoc:
|
13
|
+
def new_data
|
14
|
+
@cursor.new_data
|
15
|
+
end
|
16
|
+
def read1next
|
17
|
+
@cursor.read1prev
|
18
|
+
end
|
19
|
+
def read1prev
|
20
|
+
@cursor.read1next
|
21
|
+
end
|
22
|
+
def read1after
|
23
|
+
@cursor.read1before
|
24
|
+
end
|
25
|
+
def read1before
|
26
|
+
@cursor.read1after
|
27
|
+
end
|
28
|
+
def skip1next
|
29
|
+
@cursor.skip1prev
|
30
|
+
end
|
31
|
+
def skip1prev
|
32
|
+
@cursor.skip1next
|
33
|
+
end
|
34
|
+
def skip1after
|
35
|
+
@cursor.skip1before
|
36
|
+
end
|
37
|
+
def skip1before
|
38
|
+
@cursor.skip1after
|
39
|
+
end
|
40
|
+
def delete1after
|
41
|
+
v0 = @cursor.delete1before
|
42
|
+
v0 && @positions && _adjust_delete
|
43
|
+
v0
|
44
|
+
end
|
45
|
+
def delete1before
|
46
|
+
v0 = @cursor.delete1after
|
47
|
+
v0 && @positions && _adjust_delete
|
48
|
+
v0
|
49
|
+
end
|
50
|
+
def delete1after?
|
51
|
+
v0 = @cursor.delete1before?
|
52
|
+
v0.nil? || @positions && _adjust_delete
|
53
|
+
v0
|
54
|
+
end
|
55
|
+
def delete1before?
|
56
|
+
v0 = @cursor.delete1after?
|
57
|
+
v0.nil? || @positions && _adjust_delete
|
58
|
+
v0
|
59
|
+
end
|
60
|
+
def write1next(v)
|
61
|
+
@cursor.write1prev(v)
|
62
|
+
end
|
63
|
+
def write1prev(v)
|
64
|
+
@cursor.write1next(v)
|
65
|
+
end
|
66
|
+
def write1after(v)
|
67
|
+
@cursor.write1before(v)
|
68
|
+
end
|
69
|
+
def write1before(v)
|
70
|
+
@cursor.write1after(v)
|
71
|
+
end
|
72
|
+
def write1next?(v)
|
73
|
+
@cursor.write1prev?(v)
|
74
|
+
end
|
75
|
+
def write1prev?(v)
|
76
|
+
@cursor.write1next?(v)
|
77
|
+
end
|
78
|
+
def write1after?(v)
|
79
|
+
@cursor.write1before?(v)
|
80
|
+
end
|
81
|
+
def write1before?(v)
|
82
|
+
@cursor.write1after?(v)
|
83
|
+
end
|
84
|
+
def insert1before(v)
|
85
|
+
@positions && _adjust_insert
|
86
|
+
@cursor.insert1after(v)
|
87
|
+
end
|
88
|
+
def insert1after(v)
|
89
|
+
@positions && _adjust_insert
|
90
|
+
@cursor.insert1before(v)
|
91
|
+
end
|
92
|
+
def scan1next(v)
|
93
|
+
@cursor.scan1prev(v)
|
94
|
+
end
|
95
|
+
def scan1prev(v)
|
96
|
+
@cursor.scan1next(v)
|
97
|
+
end
|
98
|
+
def modify1next(lookup)
|
99
|
+
@cursor.modify1prev(lookup)
|
100
|
+
end
|
101
|
+
def modify1prev(lookup)
|
102
|
+
@cursor.modify1next(lookup)
|
103
|
+
end
|
104
|
+
def pos(reverse=false) # :yield:
|
105
|
+
-(@cursor.pos(!reverse).nonzero? or return(reverse ? -0.0 : 0))
|
106
|
+
end
|
107
|
+
def pos=(p)
|
108
|
+
if p.zero?
|
109
|
+
@cursor.pos = ((1.0/p)<0) ? 0 : -0.0
|
110
|
+
else
|
111
|
+
@cursor.pos = -p
|
112
|
+
end
|
113
|
+
p
|
114
|
+
end
|
115
|
+
def closed?
|
116
|
+
super or @cursor.closed?
|
117
|
+
end
|
118
|
+
# :startdoc:
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
|
data/cursor/shifting.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# $Id: shifting.rb,v 1.3 2005/07/21 15:15:38 eric_mahurin Exp $
|
2
|
+
|
3
|
+
require 'cursor'
|
4
|
+
require 'cursor/usedeleteinsert'
|
5
|
+
|
6
|
+
class Cursor
|
7
|
+
# This Cursor class puts a cursor at the ends of an Array/String (or something
|
8
|
+
# like those). Assuming insertions/deletions are efficient at both ends of that
|
9
|
+
# structure, all insertions/deletions at the cursor will be also.
|
10
|
+
class Shifting < Cursor
|
11
|
+
include UseDeleteInsert
|
12
|
+
def initialize(data=[],pos0=0)
|
13
|
+
@data = data
|
14
|
+
@pos0 = pos0
|
15
|
+
end
|
16
|
+
# :stopdoc:
|
17
|
+
def new_data
|
18
|
+
@data.class.new
|
19
|
+
end
|
20
|
+
protected
|
21
|
+
def _delete1after?
|
22
|
+
@pos0.nonzero? && (@pos0 -= 1) && @data.slice!(0)
|
23
|
+
end
|
24
|
+
def _delete1before?
|
25
|
+
@pos0==@data.size ? nil : @data.slice!(-1)
|
26
|
+
end
|
27
|
+
def _insert1before(v)
|
28
|
+
@data << v
|
29
|
+
true
|
30
|
+
end
|
31
|
+
def _insert1after(v)
|
32
|
+
@data[0,0] = (@data.class.new << v)
|
33
|
+
@pos0 += 1
|
34
|
+
true
|
35
|
+
end
|
36
|
+
# :startdoc:
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
data/cursor/split.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# $Id: split.rb,v 1.3 2005/07/21 15:15:38 eric_mahurin Exp $
|
2
|
+
|
3
|
+
require 'cursor'
|
4
|
+
require 'cursor/usedeleteinsert'
|
5
|
+
|
6
|
+
class Cursor
|
7
|
+
# This Cursor class uses an Array/String for data before the cursor
|
8
|
+
# and another one for data after the cursor. The result of this is that
|
9
|
+
# data is only deleted/inserted at the end of these 2. Because of that
|
10
|
+
# most operations have similar expense and are linear with respect to the
|
11
|
+
# number of elements being moved, inserted, deleted, replaced, etc.
|
12
|
+
class Split < Cursor
|
13
|
+
include UseDeleteInsert
|
14
|
+
def initialize(before=[],after=before.class.new)
|
15
|
+
@before = before
|
16
|
+
@after = after
|
17
|
+
end
|
18
|
+
# :stopdoc:
|
19
|
+
def new_data
|
20
|
+
@before.class.new
|
21
|
+
end
|
22
|
+
protected
|
23
|
+
def _delete1after?
|
24
|
+
@after.slice!(-1)
|
25
|
+
end
|
26
|
+
def _delete1before?
|
27
|
+
@before.slice!(-1)
|
28
|
+
end
|
29
|
+
def _insert1before(v)
|
30
|
+
@before << v
|
31
|
+
true
|
32
|
+
end
|
33
|
+
def _insert1after(v)
|
34
|
+
@after << v
|
35
|
+
true
|
36
|
+
end
|
37
|
+
public
|
38
|
+
def pos(reverse=false)
|
39
|
+
reverse ? -(@after.size.nonzero?||0.0) : @before.size
|
40
|
+
end
|
41
|
+
# :startdoc:
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|