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.
@@ -0,0 +1,105 @@
1
+ # $Id: position.rb,v 1.2 2005/07/18 14:46:28 eric_mahurin Exp $
2
+
3
+ require 'cursor/circular'
4
+
5
+ class Cursor
6
+ class Circular
7
+ class Position < Cursor::Position # :nodoc:
8
+ def initialize(parent,reverse=false)
9
+ @parent = parent
10
+ @anchor_after = reverse
11
+ @pos = @parent.__send__(:_pos)
12
+ if @anchor_after
13
+ if @pos==@parent.size
14
+ if @pos.zero?
15
+ @anchor_after = false
16
+ else
17
+ @pos = 0
18
+ end
19
+ end
20
+ else
21
+ @pos = @parent.size if @pos.zero?
22
+ end
23
+ prop(nil,@parent.prop)
24
+ end
25
+ def position=(p)
26
+ self._pos = p.__send__(:_pos,nil)
27
+ self.prop(nil,p.prop)
28
+ nil
29
+ end
30
+ def pos(reverse=false)
31
+ reverse ? -0.0 : 0
32
+ end
33
+ def pos=(p)
34
+ skip(p)
35
+ p
36
+ end
37
+ protected
38
+ def _pos(reverse=false)
39
+ if reverse.nil? ? @anchor_after : reverse
40
+ (@pos.to_i-@parent.size).nonzero? || -0.0
41
+ else
42
+ @pos
43
+ end
44
+ end
45
+ def _pos=(p)
46
+ if (p.nonzero?||1.0/p)<0
47
+ @anchor_after = true
48
+ @pos = p.to_i
49
+ if @pos.zero?
50
+ @anchor_after = false if @parent.size.zero?
51
+ else
52
+ @pos += @parent.size
53
+ end
54
+ else
55
+ @anchor_after = false
56
+ @pos = p.nonzero? || @parent.size
57
+ end
58
+ p
59
+ end
60
+ def _deletion(pos,len=1,reverse=false,size=nil)
61
+ if @pos==pos
62
+ @anchor_after = false
63
+ @pos = (size ||= @parent.size) if @pos.zero?
64
+ elsif @pos>pos
65
+ @pos -= len
66
+ if @pos<pos
67
+ @pos = pos
68
+ if @anchor_after==reverse
69
+ @anchor_after = !reverse
70
+ if @anchor_after
71
+ if @pos==(size ||= @parent.size)
72
+ if @pos.zero?
73
+ @anchor_after = false
74
+ else
75
+ @pos = 0
76
+ end
77
+ end
78
+ else
79
+ @pos = (size ||= @parent.size) if @pos.zero?
80
+ end
81
+ end
82
+ elsif @pos==pos && !@anchor_after
83
+ @anchor_after = true
84
+ if @pos==(size ||= @parent.size)
85
+ if @pos.zero?
86
+ @anchor_after = false
87
+ else
88
+ @pos = 0
89
+ end
90
+ end
91
+ end
92
+ end
93
+ size
94
+ end
95
+ def _insertion(pos,len=1,dummy=nil)
96
+ if @pos>=pos+((@anchor_after||@pos.zero?) ? 0 : 1)
97
+ @pos += len
98
+ end
99
+ nil
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+
@@ -0,0 +1,48 @@
1
+ # $Id: shifting.rb,v 1.4 2005/07/21 15:15:38 eric_mahurin Exp $
2
+
3
+ require 'cursor/circular'
4
+ require 'cursor/usedeleteinsert'
5
+
6
+ class Cursor
7
+ class Circular
8
+ # This Cursor class puts a cursor at the ends of an Array/String (or something
9
+ # like those). Assuming insertions/deletions are efficient at both ends of that
10
+ # structure, all insertions/deletions at the cursor will be also.
11
+ class Shifting < Circular
12
+ include UseDeleteInsert
13
+ def initialize(data=[],pos0=0)
14
+ @data = data
15
+ @pos0 = pos0
16
+ end
17
+ # :stopdoc:
18
+ def new_data
19
+ @data.class.new
20
+ end
21
+ protected
22
+ def _delete1after?
23
+ @pos0 = (@data.size.nonzero?||return) if @pos0.zero?
24
+ @pos0 -= 1
25
+ @data.slice!(0)
26
+ end
27
+ def _delete1before?
28
+ @pos0 = 0 if @pos0==@data.size
29
+ @data.slice!(-1)
30
+ end
31
+ def _insert1before(v)
32
+ @data << v
33
+ true
34
+ end
35
+ def _insert1after(v)
36
+ @data[0,0] = (@data.class.new << v)
37
+ @pos0 += 1
38
+ true
39
+ end
40
+ def _pos(reverse=false)
41
+ reverse ? -(@pos0.nonzero?||0.0) : @data.size-@pos0
42
+ end
43
+ # :startdoc:
44
+ end
45
+ end
46
+ end
47
+
48
+
@@ -0,0 +1,106 @@
1
+ # $Id: split.rb,v 1.3 2005/07/18 14:46:28 eric_mahurin Exp $
2
+
3
+ require 'cursor/circular'
4
+ require 'cursor/usedeleteinsert'
5
+
6
+ class Cursor
7
+ class Circular
8
+ # This Cursor class uses an Array/String for data before the cursor
9
+ # and another one for data after the cursor. The result of this is that
10
+ # data is only deleted/inserted at the end of these 2. Because of that
11
+ # most operations have similar expense and are linear with respect to the
12
+ # number of elements being moved, inserted, deleted, replaced, etc.
13
+ class Split < Circular
14
+ include UseDeleteInsert
15
+ def initialize(before=[],after=before.class.new)
16
+ @before = before
17
+ @after = after
18
+ @middle = before.class.new
19
+ @middle_start = nil
20
+ @middle_before = false
21
+ @size = @before.size+@after.size
22
+ @pos0 = @after.size
23
+ end
24
+ # :stopdoc:
25
+ def new_data
26
+ @before.class.new
27
+ end
28
+ protected
29
+ def _delete1after?
30
+ @pos0 = (@size.nonzero?||return) if @pos0.zero?
31
+ @pos0 -= 1
32
+ @size -= 1
33
+ v = @after.slice!(-1)
34
+ if v.nil?
35
+ if !@middle_start
36
+ @before,@middle = @middle,@before
37
+ @middle_before = true
38
+ @middle_start = 0
39
+ end
40
+ if @middle_before
41
+ v = @middle[@middle_start]
42
+ @middle_start += 1
43
+ else
44
+ v = @middle.slice!(-1)
45
+ end
46
+ if @middle_start>=@middle.size
47
+ @middle.replace(@middle.class.new)
48
+ @middle_start = nil
49
+ end
50
+ end
51
+ v
52
+ end
53
+ def _delete1before?
54
+ if @pos0==@size
55
+ return if @size.zero?
56
+ @pos0 = 0
57
+ end
58
+ @size -= 1
59
+ v = @before.slice!(-1)
60
+ if v.nil?
61
+ if !@middle_start
62
+ @after,@middle = @middle,@after
63
+ @middle_before = false
64
+ @middle_start = 0
65
+ end
66
+ if !@middle_before
67
+ v = @middle[@middle_start]
68
+ @middle_start += 1
69
+ else
70
+ v = @middle.slice!(-1)
71
+ end
72
+ if @middle_start>=@middle.size
73
+ @middle.replace(@middle.class.new)
74
+ @middle_start = nil
75
+ end
76
+ end
77
+ v
78
+ end
79
+ def _insert1before(v)
80
+ @size += 1
81
+ if !@middle_before and @middle_start and @middle_start.nonzero?
82
+ @middle[@middle_start -= 1] = v
83
+ else
84
+ @before << v
85
+ end
86
+ true
87
+ end
88
+ def _insert1after(v)
89
+ @pos0 += 1
90
+ @size += 1
91
+ if @middle_before and @middle_start and @middle_start.nonzero?
92
+ @middle[@middle_start -= 1] = v
93
+ else
94
+ @after << v
95
+ end
96
+ true
97
+ end
98
+ def _pos(reverse=false)
99
+ reverse ? -(@pos0.nonzero?||0.0) : @size-@pos0
100
+ end
101
+ # :startdoc:
102
+ end
103
+ end
104
+ end
105
+
106
+
@@ -0,0 +1,52 @@
1
+ # $Id: indexed.rb,v 1.7 2005/07/21 15:15:38 eric_mahurin Exp $
2
+
3
+ require 'cursor'
4
+ require 'cursor/usedeleteinsert'
5
+
6
+ class Cursor
7
+ # This class puts a cursor on an Array or String.
8
+ class Indexed < Cursor
9
+ include UseDeleteInsert
10
+ def initialize(data=[],pos=0)
11
+ @data = data
12
+ @pos = pos
13
+ end
14
+ # :stopdoc:
15
+ def new_data
16
+ @data.class.new
17
+ end
18
+ protected
19
+ def _delete1after?
20
+ @data.slice!(@pos)
21
+ end
22
+ def _delete1before?
23
+ @pos==0 ? nil : @data.slice!(@pos-=1)
24
+ end
25
+ def _insert1before(v)
26
+ @data[@pos,0] = (new_data << v)
27
+ @pos += 1
28
+ true
29
+ end
30
+ def _insert1after(v)
31
+ @data[@pos,0] = (new_data << v)
32
+ true
33
+ end
34
+ # :startdoc:
35
+ end
36
+ end
37
+
38
+ class Array
39
+ # convert an array to a cursor starting at +pos+
40
+ def to_cursor(pos=0)
41
+ Cursor::Indexed.new(self,pos)
42
+ end
43
+ end
44
+
45
+ class String
46
+ # convert a string to a cursor starting at +pos+
47
+ def to_cursor(pos=0)
48
+ Cursor::Indexed.new(self,pos)
49
+ end
50
+ end
51
+
52
+
@@ -0,0 +1,342 @@
1
+ # $Id: io.rb,v 1.15 2005/07/21 15:15:38 eric_mahurin Exp $
2
+
3
+ require 'cursor'
4
+ require 'cursor/usenext'
5
+
6
+ class Cursor
7
+ # This class treats an IO (or StringIO) as a Cursor. An IO is already
8
+ # like a Cursor, but doesn't have as robust an interface. Keep in mind that deleting and
9
+ # inserting is a slow/painful process.
10
+ class IO < Cursor
11
+ include UseNext
12
+ def initialize(io)
13
+ @io = io
14
+ end
15
+ # :stopdoc:
16
+ def new_data
17
+ String.new
18
+ end
19
+ protected
20
+ def _delete1after?
21
+ ret = @io.getc or return(nil)
22
+ after = 0
23
+ while c = @io.getc
24
+ after += 1
25
+ @io.seek(-2,::IO::SEEK_CUR)
26
+ @io.putc(c)
27
+ @io.seek(+1,::IO::SEEK_CUR)
28
+ end
29
+ @io.seek(-1,::IO::SEEK_CUR)
30
+ @io.truncate(@io.pos)
31
+ @io.seek(-after,::IO::SEEK_CUR)
32
+ ret
33
+ end
34
+ def _insert1before(v)
35
+ after = 0
36
+ while c = @io.getc
37
+ after += 1
38
+ @io.seek(-1,::IO::SEEK_CUR)
39
+ @io.putc(v)
40
+ v = c
41
+ end
42
+ @io.putc(v)
43
+ @io.seek(-after,::IO::SEEK_CUR)
44
+ true
45
+ end
46
+ public
47
+ def read1next
48
+ @io.getc
49
+ end
50
+ def write1next(v)
51
+ @io.eof? ? nil : (@io.putc(v);true)
52
+ end
53
+ def write1next!(v)
54
+ @io.putc(v)
55
+ true
56
+ end
57
+ def skip1prev
58
+ @io.pos.nonzero? && (@io.seek(-1,::IO::SEEK_CUR);true)
59
+ end
60
+ def read1after
61
+ v0 = @io.getc
62
+ v0 && @io.ungetc(v0)
63
+ v0
64
+ end
65
+ def write1after!(v)
66
+ @io.putc(v)
67
+ @io.seek(-1,::IO::SEEK_CUR)
68
+ true
69
+ end
70
+ def skip1next
71
+ @io.getc ? true : nil
72
+ end
73
+ def skip1after
74
+ @io.eof? ? nil : true
75
+ end
76
+ def skip1before
77
+ @io.pos.zero? ? nil : true
78
+ end
79
+ def read(len,hold=false,buffer=nil)
80
+ len.nonzero? or return(buffer||"")
81
+ reverse = len<0
82
+ len = len.abs
83
+ if reverse
84
+ p0 = @io.pos
85
+ len = p0.nonzero? or return if (p0-len)<0
86
+ @io.seek(-len,::IO::SEEK_CUR)
87
+ buffer1 = @io.read(len)
88
+ if hold.nil?
89
+ buffer2 = @io.read(nil)
90
+ len2 = buffer2.size
91
+ @io.seek(-(len+len2),::IO::SEEK_CUR)
92
+ @io.write(buffer2)
93
+ @io.truncate(@io.pos)
94
+ @io.seek(-len2,::IO::SEEK_CUR)
95
+ @positions && _adjust_delete(len,reverse)
96
+ elsif !hold
97
+ @io.seek(-len,::IO::SEEK_CUR)
98
+ end
99
+ buffer1.reverse!
100
+ else
101
+ buffer1 = @io.read(len) or return
102
+ len = buffer1.size
103
+ if hold.nil?
104
+ buffer2 = @io.read(nil)
105
+ len2 = buffer2.size
106
+ @io.seek(-(len+len2),::IO::SEEK_CUR)
107
+ @io.write(buffer2)
108
+ @io.truncate(@io.pos)
109
+ @io.seek(-len2,::IO::SEEK_CUR)
110
+ @positions && _adjust_delete(len,reverse)
111
+ elsif hold
112
+ @io.seek(-len,::IO::SEEK_CUR)
113
+ end
114
+ end
115
+ if buffer
116
+ if String===buffer
117
+ buffer.concat(buffer1)
118
+ else
119
+ buffer1.each_byte { |c| buffer << c }
120
+ end
121
+ else
122
+ buffer = buffer1
123
+ end
124
+ buffer
125
+ end
126
+ def read!(reverse=false,hold=false,buffer=new_data)
127
+ if reverse
128
+ len = @io.pos.nonzero? or return
129
+ @io.seek(0,::IO::SEEK_SET)
130
+ #@io.pos = 0 # BUGGY in v1.8.2
131
+ buffer1 = @io.read(len) or return
132
+ if hold.nil?
133
+ buffer2 = @io.read(nil)
134
+ len2 = buffer2.size
135
+ @io.seek(-(len+len2),::IO::SEEK_CUR)
136
+ @io.write(buffer2)
137
+ @io.truncate(@io.pos)
138
+ @io.seek(-len2,::IO::SEEK_CUR)
139
+ @positions && _adjust_delete(len,reverse)
140
+ elsif !hold
141
+ @io.seek(-len,::IO::SEEK_CUR)
142
+ end
143
+ buffer1.reverse!
144
+ else
145
+ buffer1 = @io.read(nil)
146
+ len = buffer1.size.nonzero? or return
147
+ if hold.nil?
148
+ @io.seek(-len,::IO::SEEK_CUR)
149
+ @io.truncate(@io.pos)
150
+ @positions && _adjust_delete(len,reverse)
151
+ elsif hold
152
+ @io.seek(-len,::IO::SEEK_CUR)
153
+ end
154
+ end
155
+ if buffer
156
+ if String===buffer
157
+ buffer.concat(buffer1)
158
+ else
159
+ buffer1.each_byte { |c| buffer << c }
160
+ end
161
+ else
162
+ buffer = buffer1
163
+ end
164
+ buffer
165
+ end
166
+ def skip(len,hold=false)
167
+ len.nonzero? or return(0)
168
+ reverse = len<0
169
+ len = len.abs
170
+ if reverse
171
+ p0 = @io.pos
172
+ len = p0.nonzero? or return if (p0-len)<0
173
+ if hold.nil?
174
+ buffer2 = @io.read(nil)
175
+ len2 = buffer2.size
176
+ @io.seek(-(len+len2),::IO::SEEK_CUR)
177
+ @io.write(buffer2)
178
+ @io.truncate(@io.pos)
179
+ @io.seek(-len2,::IO::SEEK_CUR)
180
+ @positions && _adjust_delete(len,reverse)
181
+ elsif !hold
182
+ @io.seek(-len,::IO::SEEK_CUR)
183
+ end
184
+ else
185
+ @io.seek(len,::IO::SEEK_CUR)
186
+ if @io.eof?
187
+ p0 = @io.pos
188
+ @io.seek(0,::IO::SEEK_END)
189
+ len -= p0-@io.pos
190
+ len.nonzero? or return
191
+ end
192
+ if hold.nil?
193
+ buffer2 = @io.read(nil)
194
+ len2 = buffer2.size
195
+ @io.seek(-(len+len2),::IO::SEEK_CUR)
196
+ @io.write(buffer2)
197
+ @io.truncate(@io.pos)
198
+ @io.seek(-len2,::IO::SEEK_CUR)
199
+ @positions && _adjust_delete(len,reverse)
200
+ elsif hold
201
+ @io.seek(-len,::IO::SEEK_CUR)
202
+ end
203
+ end
204
+ len
205
+ end
206
+ def skip!(reverse=false,hold=false)
207
+ if reverse
208
+ len = @io.pos.nonzero? or return
209
+ if hold.nil?
210
+ buffer2 = @io.read(nil)
211
+ len2 = buffer2.size
212
+ @io.seek(-(len+len2),::IO::SEEK_CUR)
213
+ @io.write(buffer2)
214
+ @io.truncate(@io.pos)
215
+ @io.seek(-len2,::IO::SEEK_CUR)
216
+ @positions && _adjust_delete(len,reverse)
217
+ elsif !hold
218
+ @io.seek(0,::IO::SEEK_SET)
219
+ end
220
+ else
221
+ p0 = @io.pos
222
+ @io.seek(0,::IO::SEEK_END)
223
+ len = @io.pos-p0
224
+ len.nonzero? or return
225
+ @io.seek(-len,::IO::SEEK_CUR) if hold!=false
226
+ if hold.nil?
227
+ @io.truncate(@io.pos)
228
+ @positions && _adjust_delete(len,reverse)
229
+ end
230
+ end
231
+ len
232
+ end
233
+ def write(value,reverse=false,hold=false,overwrite_only=false)
234
+ if String===value
235
+ value1 = reverse ? value.reverse : value
236
+ len = value.size
237
+ else
238
+ value1 = ""
239
+ len = 0;
240
+ until (v = value[len]).nil?
241
+ value1 << v
242
+ len += 1
243
+ end
244
+ value1 = value1.reverse if reverse
245
+ end
246
+ if reverse
247
+ if hold.nil?
248
+ @positions && _adjust_insert(len)
249
+ buffer2 = @io.read(nil)
250
+ len2 = buffer2.size
251
+ @io.seek(-len2,::IO::SEEK_CUR)
252
+ @io.write(value1)
253
+ @io.write(buffer2)
254
+ @io.seek(-(len+len2),::IO::SEEK_CUR)
255
+ else
256
+ len_overwrite = @io.pos
257
+ len_insert = len-len_overwrite
258
+ if len_insert<=0
259
+ @io.seek(-len,::IO::SEEK_CUR)
260
+ @io.write(value1)
261
+ else
262
+ @io.seek(-len_overwrite,::IO::SEEK_CUR)
263
+ if overwrite_only
264
+ len = len_overwrite.nonzero? or return
265
+ else
266
+ @positions && _adjust_insert(len_insert)
267
+ buffer2 = @io.read(nil)
268
+ len2 = buffer2.size
269
+ @io.seek(-len2,::IO::SEEK_CUR)
270
+ @io.write(value1[0,len_insert])
271
+ @io.write(buffer2)
272
+ @io.seek(-len2,::IO::SEEK_CUR)
273
+ end
274
+ @io.write(value1[len_insert,len_overwrite])
275
+
276
+ end
277
+ @io.seek(-len,::IO::SEEK_CUR) if !hold
278
+ end
279
+ else
280
+ if hold.nil?
281
+ @positions && _adjust_insert(len)
282
+ buffer2 = @io.read(nil)
283
+ len2 = buffer2.size
284
+ @io.seek(-len2,::IO::SEEK_CUR)
285
+ @io.write(value1)
286
+ @io.write(buffer2)
287
+ @io.seek(-len2,::IO::SEEK_CUR)
288
+ else
289
+ if overwrite_only
290
+ len.nonzero? or return(0)
291
+ @io.seek(len,::IO::SEEK_CUR)
292
+ if @io.eof?
293
+ p0 = @io.pos
294
+ @io.seek(0,::IO::SEEK_END)
295
+ len -= p0-@io.pos
296
+ len.nonzero? or return
297
+ value1 = value1[0,len]
298
+ end
299
+ @io.seek(-len,::IO::SEEK_CUR)
300
+ end
301
+ @io.write(value1)
302
+ @io.seek(-len,::IO::SEEK_CUR) if hold
303
+ end
304
+ end
305
+ len
306
+ end
307
+ def scan_until(value,reverse=false,hold=false,buffer=nil)
308
+ if String===value && !reverse && hold==false && !buffer && !value.empty?
309
+ @io.gets(value)
310
+ else
311
+ super(value,reverse,hold,buffer||value.class.new)
312
+ end
313
+ end
314
+ def pos=(p)
315
+ @io.seek(p,(p.nonzero?||1.0/p)<0 ? ::IO::SEEK_END : ::IO::SEEK_SET)
316
+ p
317
+ end
318
+
319
+ public
320
+ def close
321
+ @io.close
322
+ super
323
+ end
324
+ # :startdoc:
325
+ end
326
+ end
327
+
328
+ class IO
329
+ # convert an IO to a cursor
330
+ def to_cursor
331
+ Cursor::IO.new(self)
332
+ end
333
+ end
334
+
335
+ class StringIO
336
+ # convert an IO to a cursor
337
+ def to_cursor
338
+ Cursor::IO.new(self)
339
+ end
340
+ end
341
+
342
+