cursor 0.6 → 0.8
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 +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
|
+
|