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.rb
CHANGED
@@ -1,385 +1,544 @@
|
|
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.53 2005/07/21 15:15:38 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
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
require 'weakrefset'
|
9
|
+
|
10
|
+
=begin rdoc
|
11
|
+
An object in this Cursor class can be best thought of as a cursor in a text
|
12
|
+
editor. Many of the same operations apply - insert, delete, replace, copy,
|
13
|
+
paste, move, goto begin/end, mark position, goto mark, etc. Unlike a
|
14
|
+
text editor, this class can operate on variety of data, not just characters
|
15
|
+
and strings. It is up to the derived classes to deal with what type of data
|
16
|
+
is stored (i.e. characters, arbitrary array objects) and how it is stored (in
|
17
|
+
an Array, String, IO, linked list, mapping to another Cursor, etc). The
|
18
|
+
minimal a derived class has to implement to be able to get all of the features
|
19
|
+
below is inserting and deleting an element (both directions). Various categories
|
20
|
+
of methods are described in the paragraphs below.
|
21
|
+
|
22
|
+
The single element methods are listed below. The suffix Next/Prev means operate
|
23
|
+
after/before the cursor and move the cursor in the same direction. The suffix After/Before
|
24
|
+
means operate after/before the cursor and leave the cursor in place. The ? suffix
|
25
|
+
looks what is being written over or deleted and returns it instead of +true+
|
26
|
+
(the reads are like ? versions of skips). The ! suffix forces the operation by
|
27
|
+
doing an insert instead of overwrite at the beginning/end. When any of these
|
28
|
+
operations fail (at beginning/end or a mismatch) +nil+ is returned.
|
29
|
+
|
30
|
+
#read1next, #read1prev, #read1after, #read1before,
|
31
|
+
#skip1next, #skip1prev, #skip1after, #skip1before,
|
32
|
+
#write1next, #write1prev, #write1after, #write1before,
|
33
|
+
#write1next!, #write1prev!, #write1after!, #write1before!,
|
34
|
+
#write1next?, #write1prev?, #write1after?, #write1before?,
|
35
|
+
#delete1after, #delete1before, #delete1after?, #delete1before?,
|
36
|
+
#insert1before, #insert1after,
|
37
|
+
#scan1next, #scan1prev,
|
38
|
+
#modify1next, #modify1prev,
|
39
|
+
|
40
|
+
Basic methods that operate on element sequences are listed below. These are
|
41
|
+
all bidirectional, can hold the cursor in place, and some may be able to
|
42
|
+
delete/insert. The ! suffix operates
|
43
|
+
until the beginning/end. The element sequences
|
44
|
+
are passed/returned in Array/String like things. The methods these sequences
|
45
|
+
need are #<< and/or #[].
|
46
|
+
#new_data controls the default sequence returned (but another object that
|
47
|
+
responds to #<< can be passed in instead). Sequences to be written/scanned
|
48
|
+
with need to respond to #[] (scanning requires this return something responding
|
49
|
+
to #==).
|
50
|
+
|
51
|
+
#read, #read!, #skip, #skip!,
|
52
|
+
#write, #write?,
|
53
|
+
#scan, #scan_until, #scan_partial,
|
54
|
+
#modify
|
55
|
+
|
56
|
+
The methods below deal with numeric positions (a pos) to represent the cursor location.
|
57
|
+
A non-negative number represents the number of elements from the beginning.
|
58
|
+
A negative number (including -0.0) represents the location relative to the
|
59
|
+
end. Also included in this list below is #prop for manipulating properities
|
60
|
+
for the current cursor location. One typical use is for tracking line and
|
61
|
+
column numbers.
|
62
|
+
|
63
|
+
#pos, #pos=, #to_i, #prop, #to_s,
|
64
|
+
|
65
|
+
The position methods below use a Cursor object (Cursor::Position in the base class) to
|
66
|
+
hold the position rather than simply a numeric position. These
|
67
|
+
position objects hold a #pos and whatever is
|
68
|
+
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).
|
79
|
+
|
80
|
+
#position, #position=, #position?, #position!,
|
81
|
+
#close, #closed?
|
82
|
+
|
83
|
+
Queries about current position relative to another #position: #<=>, #-
|
84
|
+
|
85
|
+
Return a remote #position: #+, #succ, #pred, #begin, #end
|
86
|
+
|
87
|
+
Access the entire collection: #empty?, #length/#size, #data, #replace
|
88
|
+
|
89
|
+
Random access: #[]/#slice, #slice!, #[]=, #<<, #>>
|
90
|
+
|
91
|
+
Enumerable: #each, #collect!/#map!
|
92
|
+
|
93
|
+
The best way to get examples/demos of the methods and the classes is to use the
|
94
|
+
cursor/test_cursors.rb and cursor/test_circulars.rb scripts.
|
95
|
+
|
96
|
+
=end
|
15
97
|
class Cursor
|
16
98
|
|
17
99
|
include Comparable
|
18
100
|
include Enumerable
|
19
101
|
|
20
|
-
#
|
21
|
-
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# Delete instead of retrieve elements (used with get)
|
29
|
-
Delete = 8
|
30
|
-
# Insert instead of replace elements (used with put)
|
31
|
-
Insert = 8
|
32
|
-
# Put a single element instead of a sequence (needed when it looks like a sequence)
|
33
|
-
Single = 16
|
34
|
-
# Operate on what follows the cursor and then move foward
|
35
|
-
Next = 0
|
36
|
-
# Operate on what precedes the cursor and then move backward
|
37
|
-
Prev = Reverse
|
38
|
-
# Operate on what follows the cursor and hold position
|
39
|
-
After = Hold
|
40
|
-
# Operate on what precedes the cursor and hold position
|
41
|
-
Before = Reverse|Hold
|
42
|
-
# #pos for the beginning (positive zero)
|
43
|
-
Begin = +0
|
44
|
-
# #pos for the end (negative zero)
|
45
|
-
End = -0.1
|
46
|
-
# #get +len+ for getting the rest of the elements
|
47
|
-
Rest = +1.0/0
|
48
|
-
|
102
|
+
# Create a new cursor from another cursor. Only single element methods and
|
103
|
+
# #new_data are
|
104
|
+
# delegated to the other cursor. Other methods are based on those single
|
105
|
+
# element methods.
|
106
|
+
def initialize(cursor)
|
107
|
+
@cursor = cursor
|
108
|
+
end
|
109
|
+
|
49
110
|
protected
|
50
|
-
#
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
def
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
flags ^= Hold
|
74
|
-
flags ^= Reverse
|
75
|
-
end
|
76
|
-
_insert1(value,flags)
|
77
|
-
(flags&Ignore).nonzero? ? true : value
|
78
|
-
end
|
79
|
-
# Put an element at the cursor. The observed bits in +flags+ are +Reverse+
|
80
|
-
# (put before instead of after the cursor), +Hold+ (don't move the
|
81
|
-
# cursor), and +Read+ (read and return what is being replaced instead of
|
82
|
-
# just returning +true+). When the cursor is at the end (or beginning),
|
83
|
-
# either nil or the code block result is returned (see #get).
|
84
|
-
# The value is inserted at that point when that happens.
|
85
|
-
def _put1(value,flags,&more) # :yield: l=1
|
86
|
-
value0 = _delete1(flags^Read,&more)
|
87
|
-
if (flags&Hold).nonzero?
|
88
|
-
flags ^= Hold
|
89
|
-
flags ^= Reverse
|
90
|
-
end
|
91
|
-
_insert1(value,flags)
|
92
|
-
value0
|
111
|
+
# adjust positions after a deletion
|
112
|
+
def _adjust_delete(len=1,reverse=false)
|
113
|
+
i = pos(false)
|
114
|
+
ret = nil
|
115
|
+
@positions.each { |p| ret = p.__send__(:_deletion,i,len,reverse,ret) }
|
116
|
+
end
|
117
|
+
# adjust positions before an insertion
|
118
|
+
def _adjust_insert(len=1)
|
119
|
+
i = pos(false)
|
120
|
+
ret = nil
|
121
|
+
@positions.each { |p| ret = p.__send__(:_insertion,i,len,ret) }
|
122
|
+
end
|
123
|
+
def _delete1after?
|
124
|
+
@cursor.delete1after?
|
125
|
+
end
|
126
|
+
def _delete1before?
|
127
|
+
@cursor.delete1before?
|
128
|
+
end
|
129
|
+
def _insert1before(v)
|
130
|
+
@cursor.insert1before(v)
|
131
|
+
end
|
132
|
+
def _insert1after(v)
|
133
|
+
@cursor.insert1after(v)
|
93
134
|
end
|
94
135
|
|
95
136
|
public
|
96
|
-
#
|
97
|
-
def
|
98
|
-
@
|
99
|
-
end
|
100
|
-
#
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
137
|
+
# read next element
|
138
|
+
def read1next
|
139
|
+
@cursor.read1next
|
140
|
+
end
|
141
|
+
# read previous element
|
142
|
+
def read1prev
|
143
|
+
@cursor.read1prev
|
144
|
+
end
|
145
|
+
# read element after the cursor
|
146
|
+
def read1after
|
147
|
+
@cursor.read1after
|
148
|
+
end
|
149
|
+
# read element before the cursor
|
150
|
+
def read1before
|
151
|
+
@cursor.read1before
|
152
|
+
end
|
153
|
+
# skip next element and return true if succeeded
|
154
|
+
def skip1next
|
155
|
+
@cursor.skip1next
|
156
|
+
end
|
157
|
+
# skip previous element and return true if succeeded
|
158
|
+
def skip1prev
|
159
|
+
@cursor.skip1prev
|
160
|
+
end
|
161
|
+
# check if there is an element after the cursor
|
162
|
+
def skip1after
|
163
|
+
@cursor.skip1after
|
164
|
+
end
|
165
|
+
# check if there is an element before the cursor
|
166
|
+
def skip1before
|
167
|
+
@cursor.skip1before
|
168
|
+
end
|
169
|
+
# delete element after and return true if succeeded
|
170
|
+
def delete1after
|
171
|
+
v0 = @cursor.delete1after
|
172
|
+
v0 && @positions && _adjust_delete
|
173
|
+
v0
|
174
|
+
end
|
175
|
+
# delete element before cursor and return true if succeeded
|
176
|
+
def delete1before
|
177
|
+
v0 = @cursor.delete1before
|
178
|
+
v0 && @positions && _adjust_delete
|
179
|
+
v0
|
180
|
+
end
|
181
|
+
# delete element after cursor and return what is was
|
182
|
+
def delete1after?
|
183
|
+
v0 = @cursor.delete1after?
|
184
|
+
v0.nil? || @positions && _adjust_delete
|
185
|
+
v0
|
186
|
+
end
|
187
|
+
# delete element before cursor and return what is was
|
188
|
+
def delete1before?
|
189
|
+
v0 = @cursor.delete1before?
|
190
|
+
v0.nil? || @positions && _adjust_delete
|
191
|
+
v0
|
192
|
+
end
|
193
|
+
# write over next element and return true if successful
|
194
|
+
def write1next(v)
|
195
|
+
@cursor.write1next(v)
|
196
|
+
end
|
197
|
+
# write over previous element and return true if successful
|
198
|
+
def write1prev(v)
|
199
|
+
@cursor.write1prev(v)
|
200
|
+
end
|
201
|
+
# write over element after the cursor and return true if successful
|
202
|
+
def write1after(v)
|
203
|
+
@cursor.write1after(v)
|
204
|
+
end
|
205
|
+
# write over element before the cursor and return true if successful
|
206
|
+
def write1before(v)
|
207
|
+
@cursor.write1before(v)
|
208
|
+
end
|
209
|
+
# write next element or insert before if at end (returns true)
|
210
|
+
def write1next!(v)
|
211
|
+
write1next(v) || insert1before(v)
|
212
|
+
end
|
213
|
+
# write previous element or insert after if at beginning (returns true)
|
214
|
+
def write1prev!(v)
|
215
|
+
write1prev(v) || insert1after(v)
|
216
|
+
end
|
217
|
+
# write element after the cursor or insert after if at end (returns true)
|
218
|
+
def write1after!(v)
|
219
|
+
write1after(v) || insert1after(v)
|
220
|
+
end
|
221
|
+
# write element before the cursor or insert before if at beginning (returns true)
|
222
|
+
def write1before!(v)
|
223
|
+
write1before(v) || insert1before(v)
|
224
|
+
end
|
225
|
+
# write over next element and return what was overwritten
|
226
|
+
def write1next?(v)
|
227
|
+
@cursor.write1next?(v)
|
228
|
+
end
|
229
|
+
# write over previous element and return what was overwritten
|
230
|
+
def write1prev?(v)
|
231
|
+
@cursor.write1prev?(v)
|
232
|
+
end
|
233
|
+
# write over element after the cursor and return what was overwritten
|
234
|
+
def write1after?(v)
|
235
|
+
@cursor.write1after?(v)
|
236
|
+
end
|
237
|
+
# write over element before the cursor and return what was overwritten
|
238
|
+
def write1before?(v)
|
239
|
+
@cursor.write1before?(v)
|
240
|
+
end
|
241
|
+
# insert element before cursor (returns true)
|
242
|
+
def insert1before(v)
|
243
|
+
@positions && _adjust_insert
|
244
|
+
@cursor.insert1before(v)
|
245
|
+
end
|
246
|
+
# insert element after cursor (returns true)
|
247
|
+
def insert1after(v)
|
248
|
+
@positions && _adjust_insert
|
249
|
+
@cursor.insert1after(v)
|
250
|
+
end
|
251
|
+
# scan next element using == and return it when matched or nil (and go back) when mismatched
|
252
|
+
def scan1next(v)
|
253
|
+
@cursor.scan1next(v)
|
254
|
+
end
|
255
|
+
# scan previous element using == and return it when matched or nil (and go back) when mismatched
|
256
|
+
def scan1prev(v)
|
257
|
+
@cursor.scan1prev(v)
|
258
|
+
end
|
259
|
+
# modify next element using +lookup+.[] and return the original
|
260
|
+
def modify1next(lookup)
|
261
|
+
@cursor.modify1next(lookup)
|
262
|
+
end
|
263
|
+
# modify previous element using +lookup+.[] and return the original
|
264
|
+
def modify1prev(lookup)
|
265
|
+
@cursor.modify1prev(lookup)
|
105
266
|
end
|
106
|
-
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
#
|
115
|
-
#
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
267
|
+
|
268
|
+
# Return an empty object used for returning a sequence of elements.
|
269
|
+
# The only method required of this object is << (append to the sequence).
|
270
|
+
# Typically this object is determined by the #new_data of the cursor given
|
271
|
+
# to new or is the same class of the data (i.e. String/Array) given to new.
|
272
|
+
def new_data
|
273
|
+
@cursor.new_data
|
274
|
+
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)
|
278
|
+
reverse = len<0
|
279
|
+
len = len.abs
|
280
|
+
meth = method(hold.nil? ?
|
281
|
+
(reverse ? :delete1before? : :delete1after?) :
|
282
|
+
(reverse ? :read1prev : :read1next) )
|
283
|
+
len0 = 0
|
284
|
+
while len>len0
|
285
|
+
(v0 = meth.call).nil? and return(
|
286
|
+
skip(reverse ? len0 : -len0) if hold
|
287
|
+
len0.nonzero?&&buffer
|
288
|
+
)
|
289
|
+
buffer << v0
|
290
|
+
len0 += 1
|
291
|
+
end
|
292
|
+
skip(reverse ? len0 : -len0) if hold
|
293
|
+
buffer
|
294
|
+
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)
|
298
|
+
meth = method(hold.nil? ?
|
299
|
+
(reverse ? :delete1before? : :delete1after?) :
|
300
|
+
(reverse ? :read1prev : :read1next) )
|
301
|
+
len0 = 0
|
302
|
+
loop do
|
303
|
+
(v0 = meth.call).nil? and return(
|
304
|
+
skip(reverse ? len0 : -len0) if hold
|
305
|
+
len0.nonzero?&&buffer
|
306
|
+
)
|
307
|
+
buffer << v0
|
308
|
+
len0 += 1
|
309
|
+
end
|
310
|
+
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.
|
314
|
+
def skip(len,hold=false)
|
315
|
+
reverse = len<0
|
316
|
+
len = len.abs
|
317
|
+
meth = method(hold.nil? ?
|
318
|
+
(reverse ? :delete1before : :delete1after) :
|
319
|
+
(reverse ? :skip1prev : :skip1next) )
|
320
|
+
len0 = 0
|
321
|
+
while len>len0
|
322
|
+
meth.call or return(
|
323
|
+
skip(reverse ? len0 : -len0) if hold
|
324
|
+
len0.nonzero?
|
325
|
+
)
|
326
|
+
len0 += 1
|
327
|
+
end
|
328
|
+
skip(reverse ? len0 : -len0) if hold
|
329
|
+
len0
|
330
|
+
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.
|
334
|
+
def skip!(reverse=false,hold=false)
|
335
|
+
meth = method(hold.nil? ?
|
336
|
+
(reverse ? :delete1before : :delete1after) :
|
337
|
+
(reverse ? :skip1prev : :skip1next) )
|
338
|
+
len0 = 0
|
339
|
+
loop do
|
340
|
+
meth.call or return(
|
341
|
+
skip(reverse ? len0 : -len0) if hold
|
342
|
+
len0.nonzero?
|
343
|
+
)
|
344
|
+
len0 += 1
|
345
|
+
end
|
346
|
+
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.
|
349
|
+
# +overwrite_only+ will prevent insertion when the cursor is at the beginning/end.
|
350
|
+
def write(value,reverse=false,hold=false,overwrite_only=false)
|
351
|
+
meth = method(
|
352
|
+
hold.nil? ? (reverse ? :insert1after : :insert1before) :
|
353
|
+
overwrite_only ? (reverse ? :write1prev : :write1next) :
|
354
|
+
(reverse ? :write1prev! : :write1next!) )
|
355
|
+
i = 0
|
356
|
+
until (v = value[i]).nil?
|
357
|
+
meth.call(v) or return(
|
358
|
+
skip(reverse ? i : -i) if hold
|
359
|
+
i.nonzero?
|
360
|
+
)
|
361
|
+
i += 1
|
362
|
+
end
|
363
|
+
skip(reverse ? i : -i) if hold
|
364
|
+
i
|
365
|
+
end
|
366
|
+
# overwrite a sequence of elements and return the sequence overwritten.
|
367
|
+
# +hold+ will hold the cursor in place.
|
368
|
+
def write?(value,reverse=false,hold=false,buffer=value.class.new)
|
369
|
+
meth = method(reverse ? :write1prev? : :write1next?)
|
370
|
+
i = 0
|
371
|
+
until (v = value[i]).nil?
|
372
|
+
(v0 = meth.call(v)).nil? and return(
|
373
|
+
skip(reverse ? i : -i) if hold
|
374
|
+
i.nonzero? && buffer
|
375
|
+
)
|
376
|
+
buffer << v0
|
377
|
+
i += 1
|
378
|
+
end
|
379
|
+
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
|
+
i = 0
|
389
|
+
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
|
394
|
+
)
|
395
|
+
buffer << v0
|
396
|
+
end
|
397
|
+
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)
|
404
|
+
meth = method(hold.nil? ?
|
405
|
+
(reverse ? :delete1before? : :delete1after?) :
|
406
|
+
(reverse ? :read1prev : :read1next) )
|
407
|
+
buffer2 = []
|
408
|
+
len0 = 0
|
409
|
+
i = 0
|
410
|
+
until (v = value[i]).nil?
|
411
|
+
(v0 = meth.call).nil? and return(
|
412
|
+
skip(reverse ? len0 : -len0) if hold
|
413
|
+
len0.nonzero?&&buffer
|
414
|
+
)
|
415
|
+
buffer << v0
|
416
|
+
buffer2 << v0
|
417
|
+
len0 += 1
|
418
|
+
i += 1
|
419
|
+
if v!=v0
|
420
|
+
begin
|
421
|
+
buffer2.shift
|
422
|
+
i -= 1
|
423
|
+
end until i.times { |j| value[j]==buffer2[j] or (v=nil;break) }
|
424
|
+
v ||= value[i]
|
425
|
+
redo
|
426
|
+
end
|
427
|
+
end
|
428
|
+
skip(reverse ? len0 : -len0) if hold
|
429
|
+
buffer
|
430
|
+
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
|
440
|
+
)
|
441
|
+
buffer << v0
|
442
|
+
i += 1
|
192
443
|
end
|
444
|
+
skip(reverse ? i : -i) if hold
|
445
|
+
buffer
|
193
446
|
end
|
194
|
-
#
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
#
|
202
|
-
# When the cursor is at the end (or beginning),
|
203
|
-
# either nil or the code block result is returned (see #get).
|
204
|
-
# The value is inserted at that point when that happens.
|
205
|
-
def put(value,flags=Next,&more) # :yield: l
|
206
|
-
if (flags&Single).nonzero? or not data_class>=value.class
|
207
|
-
if (flags&Insert).nonzero?
|
208
|
-
if (flags&Hold).nonzero?
|
209
|
-
flags ^= Hold
|
210
|
-
flags ^= Reverse
|
211
|
-
end
|
212
|
-
return(_insert1(value,flags,&more))
|
213
|
-
else
|
214
|
-
return(_put1(value,flags,&more))
|
215
|
-
end
|
216
|
-
end
|
217
|
-
if (flags&Insert).nonzero?
|
218
|
-
flags |= Single
|
219
|
-
if (flags&Hold).nonzero?
|
220
|
-
flags ^= Hold
|
221
|
-
flags ^= Reverse
|
222
|
-
end
|
223
|
-
if (flags&Reverse).nonzero?
|
224
|
-
start = value.size-1
|
225
|
-
finish = 0
|
226
|
-
step = -1
|
227
|
-
else
|
228
|
-
start = 0
|
229
|
-
finish = value.size-1
|
230
|
-
step = +1
|
231
|
-
end
|
232
|
-
start.step(finish,step) do |i|
|
233
|
-
_insert1(value[i],flags)
|
234
|
-
end
|
235
|
-
nil
|
236
|
-
elsif (flags&Hold).nonzero?
|
237
|
-
flags ^= Hold
|
238
|
-
position { put(value,flags,&more) }
|
239
|
-
else
|
240
|
-
flags |= Single
|
241
|
-
if (flags&Reverse).nonzero?
|
242
|
-
start = value.size-1
|
243
|
-
finish = 0
|
244
|
-
step = -1
|
245
|
-
else
|
246
|
-
start = 0
|
247
|
-
finish = value.size-1
|
248
|
-
step = +1
|
249
|
-
end
|
250
|
-
if (flags&Read).nonzero?
|
251
|
-
value0 = data_class.new
|
252
|
-
replacing = true
|
253
|
-
start.step(finish,step) do |i|
|
254
|
-
v = _put1(value[i],flags) { |l|
|
255
|
-
replacing = more&&more[l]
|
256
|
-
}
|
257
|
-
value0 << v if replacing
|
258
|
-
end
|
259
|
-
if value0.size.zero?
|
260
|
-
nil
|
261
|
-
elsif step<0
|
262
|
-
value0.reverse
|
263
|
-
else
|
264
|
-
value0
|
265
|
-
end
|
266
|
-
else
|
267
|
-
len0 = 0
|
268
|
-
replacing = true
|
269
|
-
start.step(finish,step) do |i|
|
270
|
-
_put1(value[i],flags) { |l|
|
271
|
-
replacing = more&&more[l]
|
272
|
-
}
|
273
|
-
len0 += 1 if replacing
|
274
|
-
end
|
275
|
-
if len0.zero?
|
276
|
-
nil
|
277
|
-
else
|
278
|
-
len0
|
279
|
-
end
|
280
|
-
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
|
281
454
|
end
|
282
|
-
|
455
|
+
skip(reverse ? len0 : -len0) if hold
|
456
|
+
buffer
|
457
|
+
end
|
458
|
+
|
283
459
|
# This will return a numeric position. When not +reverse+,
|
284
460
|
# this numeric position is the number of elements from the beginning (0 is at the beginning). With
|
285
|
-
# +reverse+ it is negative and the number of elements from the end (-0.
|
286
|
-
def pos(reverse=false
|
287
|
-
|
288
|
-
ref = reverse ? End : Begin
|
289
|
-
p = get(Rest,flags^Reverse) or return(ref)
|
290
|
-
get(p,flags)==p or raise(IndexError,"couldn't get back to where we were: #{p}")
|
291
|
-
p = reverse ? -p : p
|
292
|
-
p+ref.to_i
|
461
|
+
# +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)
|
293
464
|
end
|
294
465
|
# Returns #pos.to_i
|
295
466
|
def to_i
|
296
467
|
pos.to_i
|
297
468
|
end
|
298
|
-
#
|
299
|
-
def
|
300
|
-
|
301
|
-
p
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
469
|
+
# Set #pos to be +p+. When +p+ is negative, it is set from the end.
|
470
|
+
def pos=(p)
|
471
|
+
#skip!((p.nonzero?||1.0/p)>=0)
|
472
|
+
#p.to_i.abs==skip(p) or raise(IndexError,"invalid pos=#{p}")
|
473
|
+
len = p-pos((p.nonzero?||1.0/p)<0)
|
474
|
+
len.to_i.abs==skip(len) or raise(IndexError,"invalid pos=#{p}")
|
475
|
+
p
|
476
|
+
end
|
477
|
+
# Get (no +value+) and set cursor properties. Normally, +name+
|
478
|
+
# should be a symbol. If +name+ is +nil+, it wil get/set using a hash
|
479
|
+
# representing all of the properties.
|
480
|
+
def prop(name=nil,*value) # :args: (name[,value])
|
481
|
+
if name.nil?
|
482
|
+
if value.size.zero?
|
483
|
+
@prop&&@prop.clone
|
484
|
+
else
|
485
|
+
if (value = value[0]).nil?
|
486
|
+
@prop&&remove_instance_variable(:@prop)
|
487
|
+
else
|
488
|
+
(@prop||={}).replace(value)
|
489
|
+
end
|
490
|
+
end
|
309
491
|
else
|
310
|
-
|
492
|
+
if value.size.zero?
|
493
|
+
@prop&&@prop[name]
|
494
|
+
else
|
495
|
+
(@prop||={})[name] = value[0]
|
496
|
+
end
|
311
497
|
end
|
312
|
-
s
|
313
498
|
end
|
314
|
-
#
|
315
|
-
def
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
#
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
# hold the #position rather than simply a numeric position that #pos and
|
343
|
-
# #pos= use. These Cursor::Position objects hold a #pos and whatever is
|
344
|
-
# in #prop. For now, the #pos in these objects do not adjust based on
|
345
|
-
# insertions and deletions, but that may change. So don't count
|
346
|
-
# Without +p+ or a code block, #position returns one of these objects to
|
347
|
-
# represent the current location of the cursor. With +p+ or a code block,
|
348
|
-
# the current #position is saved, the #position is set to +p+ using
|
349
|
-
# #position= (if +p+), the code block is executed (or the current #position
|
350
|
-
# is found with no code block), and the #position is returned to where it
|
351
|
-
# was saved (using #position=). The return value is the result of the code
|
352
|
-
# block or the #position if that was executed instead.
|
353
|
-
def position(p=nil,&code) # :yield:
|
354
|
-
if code or p
|
355
|
-
start = position
|
356
|
-
self.position = p if p
|
357
|
-
ret = code ? code[] : position
|
358
|
-
self.position = start
|
359
|
-
start.close
|
360
|
-
ret
|
499
|
+
# Returns "pos=#pos.to_s" followed by what's in #prop in a reasonable format.
|
500
|
+
def to_s
|
501
|
+
"pos=#{pos.to_s}" +
|
502
|
+
(prop||{}).collect { |k,v| "#{k}=#{v}" }.join(" ")
|
503
|
+
end
|
504
|
+
|
505
|
+
# Without a code block, #position returns one of these objects to
|
506
|
+
# represent the current location of the cursor. When +reverse+, it will
|
507
|
+
# anchor the #position to the element after. Otherwise it will anchor it
|
508
|
+
# to the element before. This anchoring is used when an insertion occurs at
|
509
|
+
# that point affecting how this position is adjusted.
|
510
|
+
#
|
511
|
+
# With a code block, it saves the #position (also using +reverse+),
|
512
|
+
# executes the code block, and returns the #position to where it
|
513
|
+
# started. The return value is the result of the code block.
|
514
|
+
def position(reverse=false,&code) # :yield:
|
515
|
+
p = Position.new(self,reverse)
|
516
|
+
(@positions||=WeakRefSet.new) << p
|
517
|
+
if code
|
518
|
+
begin
|
519
|
+
code[]
|
520
|
+
ensure
|
521
|
+
begin
|
522
|
+
self.position = p
|
523
|
+
ensure
|
524
|
+
p.close
|
525
|
+
end
|
526
|
+
end
|
361
527
|
else
|
362
|
-
p = Position.new(self,pos,prop)
|
363
|
-
@positions << (p.object_id >> 1)
|
364
|
-
ObjectSpace.define_finalizer(p,method(:_finalizer))
|
365
528
|
p
|
366
529
|
end
|
367
530
|
end
|
368
|
-
# Set the position to +p
|
531
|
+
# Set the position to +p+ (from #position).
|
369
532
|
def position=(p)
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
self.prop = p.prop
|
374
|
-
else
|
375
|
-
self.pos = p
|
376
|
-
end
|
533
|
+
self.pos = p.pos(nil)
|
534
|
+
self.prop(nil,p.prop)
|
535
|
+
nil
|
377
536
|
end
|
378
537
|
# Without a code block, this queries whether a particular #position +p+ is
|
379
538
|
# valid (is a child) or determines if there is any outstanding #position
|
380
539
|
# (when +p+=+nil+).
|
381
540
|
#
|
382
|
-
# With a code block, it
|
541
|
+
# With a code block, it saves the #position (passing +p+ as the anchoring flag)
|
383
542
|
# and executes the code. If the result of the code is false/nil, it will
|
384
543
|
# return to the original #position (intially saved). Otherwise it will stay where the code
|
385
544
|
# left it. The result of the code block is returned. This is useful when
|
@@ -387,37 +546,30 @@ class Cursor
|
|
387
546
|
# else for a fail/mismatch.
|
388
547
|
def position?(p=nil,&code) # :yield:
|
389
548
|
if code
|
390
|
-
start = position
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
return(true) if p.object_id==self.object_id
|
400
|
-
return(nil)
|
401
|
-
end
|
402
|
-
if p.closed?
|
403
|
-
@positions.slice!(i)
|
404
|
-
nil
|
405
|
-
else
|
406
|
-
true
|
549
|
+
start = position(p)
|
550
|
+
begin
|
551
|
+
ret = code[]
|
552
|
+
ensure
|
553
|
+
begin
|
554
|
+
self.position = start if not ret
|
555
|
+
ensure
|
556
|
+
start.close
|
557
|
+
end
|
407
558
|
end
|
559
|
+
elsif p
|
560
|
+
@positions && @positions.include?(p) || equal?(p) || nil
|
408
561
|
else
|
409
|
-
|
410
|
-
return(true) if not ObjectSpace._id2ref(@positions[0] << 1).closed?
|
411
|
-
@positions.shift
|
412
|
-
end
|
562
|
+
@positions && (!@positions.empty? || nil)
|
413
563
|
end
|
414
564
|
end
|
415
|
-
#
|
565
|
+
# Discard/close every child #position (+p+=+nil+) or discard (not close)
|
566
|
+
# the give +p+ (you probably want +p+.close instead).
|
416
567
|
def position!(p=nil)
|
417
568
|
if p
|
418
|
-
p
|
419
|
-
|
420
|
-
@positions.
|
569
|
+
@positions.delete(p)
|
570
|
+
elsif @positions
|
571
|
+
@positions.each { |p| p.close }
|
572
|
+
remove_instance_variable(:@positions)
|
421
573
|
end
|
422
574
|
self
|
423
575
|
end
|
@@ -425,833 +577,164 @@ class Cursor
|
|
425
577
|
def close
|
426
578
|
position!
|
427
579
|
# this should make just about any operation fail
|
428
|
-
instance_variables.each { |v|
|
580
|
+
instance_variables.each { |v| instance_variable_set(v,nil) }
|
429
581
|
nil
|
430
582
|
end
|
431
583
|
# Is the cursor closed?
|
432
584
|
def closed?
|
433
|
-
instance_variables.
|
585
|
+
not instance_variables.find { |v| !instance_variable_get(v).nil? }
|
434
586
|
end
|
435
|
-
|
436
|
-
|
437
|
-
not get(nil,(reverse ? Before : After)|Ignore)
|
438
|
-
end
|
439
|
-
alias eof eof?
|
440
|
-
# Compare +other+ (from #pos or #position) to the current position. +1
|
587
|
+
|
588
|
+
# Compare +other+ (a #position) to the current position. +1
|
441
589
|
# for the self is after, -1 for self being before, and 0 for it being at
|
442
590
|
# same location.
|
443
591
|
def <=>(other)
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
other.respond_to?:zero? or return(nil)
|
449
|
-
pos(other<0).to_i<=>other.to_i
|
450
|
-
end
|
451
|
-
alias === ==
|
452
|
-
# If +other+ is from #position, this will return the distance (number
|
453
|
-
# or elements) from +other+ to +self+. This can be +, -, or 0.
|
454
|
-
# Otherwise, a new #position is returned that is decreased by this amount
|
455
|
-
# (using #get).
|
592
|
+
position?(other) and pos<=>other.pos
|
593
|
+
end
|
594
|
+
# Return the distance (number
|
595
|
+
# or elements) from +other+ (a #position) to +self+. This can be +, -, or 0.
|
456
596
|
def -(other)
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
597
|
+
pos-other.pos
|
598
|
+
end
|
599
|
+
# Returns a new #position increased by +len+ (positive or negative).
|
600
|
+
def +(len)
|
601
|
+
position { skip(len);position((len.nonzero?||1.0/len)<0) }
|
602
|
+
end
|
603
|
+
|
604
|
+
# Return a new #position for next cursor location or +nil+ if we are at the end.
|
605
|
+
def succ
|
606
|
+
position { skip1next && position(false) }
|
607
|
+
end
|
608
|
+
# Return a new #position for previous cursor location or +nil+ if we are at the beginning.
|
609
|
+
def pred
|
610
|
+
position { skip1prev && position(true) }
|
611
|
+
end
|
612
|
+
# Return a new #position for the beginning.
|
613
|
+
def begin
|
614
|
+
position { skip!(true);position(false) }
|
615
|
+
end
|
616
|
+
# Return a new #position for the end.
|
617
|
+
def end
|
618
|
+
position { skip!(false);position(true) }
|
464
619
|
end
|
465
|
-
# Returns a new #position increased by +other+ (using #get).
|
466
|
-
def +(other)
|
467
|
-
position { get(other,Next|Ignore);position }
|
468
|
-
end
|
469
|
-
# Returns a Reversed cursor.
|
470
|
-
def reverse
|
471
|
-
Reversed.new(self)
|
472
|
-
end
|
473
|
-
alias -@ reverse
|
474
|
-
# Increments the cursor by +len+ (same +len+ of #get)
|
475
|
-
# Returns +nil+ at the end and self otherwise.
|
476
|
-
def succ!(len=nil); get(len,Next|Ignore) && self; end
|
477
|
-
# Decrements the cursor by +len+ (same +len+ of #get)
|
478
|
-
# Returns +nil+ at the beginning and self otherwise.
|
479
|
-
def pred!(len=nil); get(len,Prev|Ignore) && self; end
|
480
|
-
# Similar to #succ! except a new #position is returned instead of
|
481
|
-
# modifying the current.
|
482
|
-
def succ(len=nil); position { get(len,Next|Ignore) && position }; end
|
483
|
-
# Similar to #pred! except a new #position is returned instead of
|
484
|
-
# modifying the current.
|
485
|
-
def pred(len=nil); position { get(len,Prev|Ignore) && position }; end
|
486
|
-
# Go to the beginning
|
487
|
-
def begin!; get(Rest,Prev|Ignore); self; end
|
488
|
-
# Go to the end
|
489
|
-
def end!; get(Rest,Next|Ignore); self; end
|
490
|
-
# Similar to #first! except a new #position is returned instead of
|
491
|
-
# modifying the current.
|
492
|
-
def begin; position { get(Rest,Prev|Ignore);position }; end
|
493
|
-
# Similar to #last! except a new #position is returned instead of
|
494
|
-
# modifying the current.
|
495
|
-
def end; position { get(Rest,Next|Ignore);position }; end
|
496
620
|
# Returns the number of elements.
|
497
621
|
def size
|
498
|
-
|
499
|
-
l0 = position { get(Rest,Prev|Ignore) } || 0
|
500
|
-
l0+l1
|
622
|
+
(skip!(true,true)||0)+(skip!(false,true)||0)
|
501
623
|
end
|
502
624
|
alias length size
|
503
625
|
# Determines whether there is anything before or after the cursor.
|
504
626
|
def empty?
|
505
|
-
|
506
|
-
get(nil,Before|Ignore) and return(false)
|
507
|
-
true
|
627
|
+
skip1after.nil? && skip1before.nil?
|
508
628
|
end
|
509
|
-
# Removes all elements and returns the number removed
|
629
|
+
# Removes all elements and returns the number removed.
|
510
630
|
def clear
|
511
|
-
(
|
631
|
+
(skip!(false,nil)||0)+(skip!(true,nil)||0)
|
512
632
|
end
|
513
|
-
#
|
514
|
-
# use #position= +obj+. self is returned.
|
633
|
+
# Replace the all of the data and return self.
|
515
634
|
def replace(obj)
|
516
|
-
|
517
|
-
|
518
|
-
put(obj)
|
519
|
-
else
|
520
|
-
self.position = obj
|
521
|
-
end
|
635
|
+
clear
|
636
|
+
write(obj,false,nil)
|
522
637
|
self
|
523
638
|
end
|
524
|
-
# Get all of the data
|
639
|
+
# Get all of the data and leave the cursor at the end.
|
525
640
|
def data
|
526
|
-
|
527
|
-
|
641
|
+
skip!(true)
|
642
|
+
read!(false)
|
528
643
|
end
|
529
|
-
# Appends a single element at the end and returns self
|
530
|
-
def << (
|
531
|
-
|
532
|
-
|
644
|
+
# Appends a single element at the end and returns self.
|
645
|
+
def << (v)
|
646
|
+
skip!(false)
|
647
|
+
insert1before(v)
|
533
648
|
self
|
534
649
|
end
|
535
|
-
# Prepends a single element at the beginning and returns self
|
536
|
-
def >> (
|
537
|
-
|
538
|
-
|
650
|
+
# Prepends a single element at the beginning and returns self.
|
651
|
+
def >> (v)
|
652
|
+
skip!(true)
|
653
|
+
insert1after(v)
|
539
654
|
self
|
540
655
|
end
|
541
|
-
|
542
|
-
def first(len=nil)
|
543
|
-
get(Rest,Prev|Ignore)
|
544
|
-
get(len,Next)
|
545
|
-
end
|
546
|
-
# like Array#last, except it takes a +len+
|
547
|
-
def last(len=nil)
|
548
|
-
get(Rest,Next|Ignore)
|
549
|
-
get(len,Prev)
|
550
|
-
end
|
551
|
-
protected
|
552
|
-
def _index(index,len,flags) # :nodoc:
|
553
|
-
if index.respond_to?:exclude_end?
|
554
|
-
if index.first.respond_to?:zero? and (index.first<0)!=(index.last<0)
|
555
|
-
len = (size-index.last)-abs(index.first)
|
556
|
-
else
|
557
|
-
len = index.last-index.first
|
558
|
-
end
|
559
|
-
if index.exclude_end?
|
560
|
-
len = (len<0) ? len.floor : len.ceil
|
561
|
-
else
|
562
|
-
len = (len<0) ? len.to_i-1 : len.to_i+1
|
563
|
-
end
|
564
|
-
index = index.first
|
565
|
-
index += len if (flags&Reverse).nonzero?
|
566
|
-
end
|
567
|
-
self.position = index if index
|
568
|
-
[len,flags]
|
569
|
-
end
|
570
|
-
public
|
656
|
+
|
571
657
|
# Provides random access for the cursor like what is in Array/String.
|
572
|
-
# +index+ can be +nil+ (start at the current location)
|
573
|
-
#
|
574
|
-
#
|
575
|
-
#
|
576
|
-
|
577
|
-
|
578
|
-
len
|
579
|
-
get(len,flags)
|
658
|
+
# +index+ can be +nil+ (start at the current location) or a numeric (for #pos=).
|
659
|
+
# +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+).
|
662
|
+
def slice(index=nil,len=nil)
|
663
|
+
self.pos = index if index
|
664
|
+
len.nil? ? read1after : read(len,true)
|
580
665
|
end
|
581
666
|
alias [] slice
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
# Random access store like in Array/String. Accepts the same enhancements
|
591
|
-
# for index, len, and flags as #slice does.
|
592
|
-
def []=(*args) # :args: (index=nil,len=nil,flags=After,value)
|
667
|
+
# Like #slice except the element(s) are deleted.
|
668
|
+
def slice!(index=nil,len=nil)
|
669
|
+
self.pos = index if index
|
670
|
+
len.nil? ? delete1after? : read(len,nil)
|
671
|
+
end
|
672
|
+
# 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.
|
674
|
+
def []=(*args) # :args: (index=nil,len=nil,value)
|
593
675
|
value = args.slice!(-1)
|
594
|
-
index,len
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
ret = put(value,flags|Single)
|
599
|
-
else
|
600
|
-
flags &= ~Single
|
601
|
-
ret = get(len,flags^Ignore|Delete)
|
602
|
-
put(value,flags|Insert)
|
603
|
-
end
|
604
|
-
if (flags&Read).nonzero?
|
605
|
-
ret
|
606
|
-
else
|
607
|
-
value
|
608
|
-
end
|
609
|
-
end
|
610
|
-
# copies data from one place to another. The default from and to indices are
|
611
|
-
# nil which is the current cursor location. All of the options of #slice
|
612
|
-
# are available for the indices, +len+, and flags.
|
613
|
-
def copy(from_index=nil,to_index=nil,len=nil,from_flags=After,to_flags=After)
|
614
|
-
if not to_index
|
615
|
-
value = position { self[from_index,len,from_flags] }
|
616
|
-
self[to_index,len,to_flags] = value
|
617
|
-
elsif not from_index
|
618
|
-
value = self[from_index,len,from_flags]
|
619
|
-
position { self[to_index,len,to_flags] = value }
|
676
|
+
index,len = *args
|
677
|
+
self.pos = index if index
|
678
|
+
if len.nil?
|
679
|
+
write1after!(value)
|
620
680
|
else
|
621
|
-
|
681
|
+
skip(len,nil)
|
682
|
+
write(value,(len.nonzero?||1.0/len)>=0,nil)
|
622
683
|
end
|
684
|
+
value
|
623
685
|
end
|
624
|
-
|
625
|
-
#
|
626
|
-
#
|
627
|
-
#
|
628
|
-
|
629
|
-
|
630
|
-
continue = true
|
631
|
-
loop do
|
632
|
-
value = get(len,flags) { continue = nil }
|
633
|
-
if not continue
|
634
|
-
code[value] if len and value
|
635
|
-
break
|
636
|
-
end
|
637
|
-
code[value]
|
638
|
-
end
|
639
|
-
end
|
640
|
-
# Same as #each except go in Reverse.
|
641
|
-
def reverse_each(index=End,len=nil,flags=Prev,&code) # :yield: value
|
642
|
-
each(index,len,flags,&code)
|
643
|
-
end
|
644
|
-
# Performs an in-place collect! (like Array#collect!). Accepts the same enhancements
|
645
|
-
# for index, len, and flags as #slice does.
|
646
|
-
# The cursor will be left at the end (or beginning if the +Reverse+ flags bit is use).
|
647
|
-
# nil is returned (or the break value if the code does a break).
|
648
|
-
def collect!(index=Begin,len=nil,flags=Next,&code) # :yield: value
|
649
|
-
len,flags = _index(index,len,flags)
|
650
|
-
continue = true
|
651
|
-
loop do
|
652
|
-
value = get(len,flags|Delete) { continue = nil }
|
653
|
-
if not continue
|
654
|
-
if len and value
|
655
|
-
value = code[value]
|
656
|
-
put(value,flags|Insert) if value
|
657
|
-
end
|
658
|
-
break
|
659
|
-
end
|
660
|
-
value = code[value]
|
661
|
-
put(value,flags|Insert|(len ? 0 : Single)) if value || !len
|
662
|
-
end
|
663
|
-
end
|
664
|
-
alias map! collect!
|
665
|
-
# Performs an in-place reject! (like Array#reject!). Accepts the same enhancements
|
666
|
-
# for index, len, and flags as #slice does.
|
667
|
-
# The cursor will be left at the end (or beginning if the +Reverse+ flags bit is use).
|
686
|
+
|
687
|
+
# Performs each just to make this class Enumerable. +index+ can be +nil+ or
|
688
|
+
# a #pos like in #slice. If +reverse+ each will move in reverse. If a
|
689
|
+
# +terminator+ is specified, each iteration will get a sequence using
|
690
|
+
# #scan_until(+terminator+) instead of using a single element.
|
691
|
+
# The cursor will be left at the end (or beginning if +reverse+).
|
668
692
|
# nil is returned (or the break value if the code does a break).
|
669
|
-
def
|
670
|
-
|
693
|
+
def each(index=+0.0,reverse=false,terminator=nil,&code) # :yield: value
|
694
|
+
self.pos = index if index
|
671
695
|
continue = true
|
672
|
-
|
673
|
-
loop do
|
674
|
-
value = get(len,flags) { continue = nil }
|
675
|
-
if not continue
|
676
|
-
if len and value
|
677
|
-
if code[value]
|
678
|
-
get(len,flags^Reverse|Delete|Ignore)
|
679
|
-
ret = self
|
680
|
-
end
|
681
|
-
end
|
682
|
-
break
|
683
|
-
end
|
684
|
-
if code[value]
|
685
|
-
get(len,flags^Reverse|Delete|Ignore)
|
686
|
-
ret = self
|
687
|
-
end
|
688
|
-
end
|
689
|
-
ret
|
690
|
-
end
|
691
|
-
|
692
|
-
|
693
|
-
# Objects in this class are mainly used to simply mark/remember the location
|
694
|
-
# of a parent cursor. But, this class also has the fully functionality of the
|
695
|
-
# parent. When this child want to do an operation, it uses the parent to
|
696
|
-
# do it and returns the parent to where it was. Derived classes where the
|
697
|
-
# underlying data is random access may be able to implement this class to
|
698
|
-
# directly access the data rather than go through the parent.
|
699
|
-
class Position < Cursor
|
700
|
-
def initialize(parent,pos,prop)
|
701
|
-
@parent = parent
|
702
|
-
@pos,self.prop = pos,prop
|
703
|
-
end
|
704
|
-
def data_class
|
705
|
-
@parent.data_class
|
706
|
-
end
|
707
|
-
protected
|
708
|
-
def _pos(&code) # :nodoc:
|
709
|
-
start = @parent.pos,@parent.prop
|
710
|
-
@parent.pos,@parent.prop = @pos,self.prop
|
711
|
-
ret = code[]
|
712
|
-
@pos,self.prop = @parent.pos,@parent.prop
|
713
|
-
@parent.pos,@parent.prop = start
|
714
|
-
ret
|
715
|
-
end
|
716
|
-
public
|
717
|
-
def get(len=nil,flags=Next,&more) # :yield: l
|
718
|
-
_pos { @parent.get(len,flags,&more) }
|
719
|
-
end
|
720
|
-
def put(value,flags=Next,&more) # :yield: l
|
721
|
-
_pos { @parent.put(value,flags,&more) }
|
722
|
-
end
|
723
|
-
def pos(reverse=false)
|
696
|
+
if terminator.nil?
|
724
697
|
if reverse
|
725
|
-
|
726
|
-
|
727
|
-
@pos
|
728
|
-
end
|
729
|
-
end
|
730
|
-
def position(p=nil,&code) # :yield:
|
731
|
-
if p or code
|
732
|
-
super(p,&code)
|
733
|
-
else
|
734
|
-
_pos { @parent.position }
|
735
|
-
end
|
736
|
-
end
|
737
|
-
def position?(p=nil,&code) # :yield:
|
738
|
-
if code
|
739
|
-
super(p,&code)
|
740
|
-
else
|
741
|
-
@parent.position?(p)
|
742
|
-
end
|
743
|
-
end
|
744
|
-
def position!(p=nil)
|
745
|
-
if p
|
746
|
-
@parent.position!(p)
|
747
|
-
else
|
748
|
-
nil
|
749
|
-
end
|
750
|
-
end
|
751
|
-
def close
|
752
|
-
parent = @parent
|
753
|
-
super()
|
754
|
-
parent.position?(self)
|
755
|
-
end
|
756
|
-
end
|
757
|
-
|
758
|
-
# This class can be used to reverse the direction of operations on a given
|
759
|
-
# cursor. It operates on the given cursor directly moving it around.
|
760
|
-
class Reversed < Cursor
|
761
|
-
def initialize(cursor)
|
762
|
-
super()
|
763
|
-
@cursor = cursor
|
764
|
-
end
|
765
|
-
def data_class
|
766
|
-
@cursor.data_class
|
767
|
-
end
|
768
|
-
def get(len=nil,flags=Next,&more) # :yield: l
|
769
|
-
@cursor.get(len,flags^Reverse)
|
770
|
-
end
|
771
|
-
def put(value,flags=Next,&more) # :yield: l
|
772
|
-
@cursor.put(value,flags^Reverse,&more)
|
773
|
-
end
|
774
|
-
def pos(reverse=false,&code) # :yield:
|
775
|
-
if code
|
776
|
-
super(!reverse)
|
777
|
-
else
|
778
|
-
p = @cursor.pos(!reverse)
|
779
|
-
p = -p+Begin+End
|
780
|
-
end
|
781
|
-
end
|
782
|
-
def pos=(p)
|
783
|
-
reverse = p<Begin
|
784
|
-
p = -p+Begin+End
|
785
|
-
p = (@cursor.pos = p)
|
786
|
-
p = -p+Begin+End
|
787
|
-
end
|
788
|
-
end
|
789
|
-
|
790
|
-
# This class is used to test the base class by overriding as few methods
|
791
|
-
# as possible.
|
792
|
-
class Test < Cursor
|
793
|
-
def initialize(data=[],index=0)
|
794
|
-
super()
|
795
|
-
@data = data
|
796
|
-
@pos = index
|
797
|
-
@data_class = (@data.respond_to?:data_class) ?
|
798
|
-
@data.data_class : @data.class
|
799
|
-
end
|
800
|
-
def data_class
|
801
|
-
@data_class
|
802
|
-
end
|
803
|
-
def _delete1(flags,&more) # :yield: l=1
|
804
|
-
if (flags&Reverse).nonzero?
|
805
|
-
@pos>0 && (@pos -= 1)
|
806
|
-
else
|
807
|
-
@pos<@data.size
|
808
|
-
end or return(
|
809
|
-
if ret = more&&more[1]
|
810
|
-
(flags&Ignore).nonzero? ? true : ret[0]
|
811
|
-
end
|
812
|
-
)
|
813
|
-
if (flags&Ignore).nonzero?
|
814
|
-
@data[@pos,1] = @data_class.new
|
815
|
-
1
|
816
|
-
else
|
817
|
-
@data.slice!(@pos)
|
818
|
-
end
|
819
|
-
end
|
820
|
-
def _insert1(value,flags)
|
821
|
-
@data[@pos,0] = (@data_class.new << value)
|
822
|
-
@pos += 1 if (flags&Reverse).zero?
|
823
|
-
end
|
824
|
-
end
|
825
|
-
|
826
|
-
# This class puts a cursor on an Array or String.
|
827
|
-
class Indexed < Cursor
|
828
|
-
def initialize(data=[],index=0)
|
829
|
-
super()
|
830
|
-
@data = data
|
831
|
-
@pos = index
|
832
|
-
@data_class = (@data.respond_to?:data_class) ?
|
833
|
-
@data.data_class : @data.class
|
834
|
-
end
|
835
|
-
def data_class
|
836
|
-
@data_class
|
837
|
-
end
|
838
|
-
def _delete1(flags,&more) # :yield: l=1
|
839
|
-
if (flags&Reverse).nonzero?
|
840
|
-
@pos>0 && @pos -= 1
|
841
|
-
else
|
842
|
-
@pos<@data.size
|
843
|
-
end or return(
|
844
|
-
if ret = more&&more[1]
|
845
|
-
(flags&Ignore).nonzero? ? true : ret[0]
|
698
|
+
while v0 = read1prev
|
699
|
+
code[v0]
|
846
700
|
end
|
847
|
-
)
|
848
|
-
if (flags&Ignore).nonzero?
|
849
|
-
@data[@pos,1] = @data_class.new
|
850
|
-
true
|
851
701
|
else
|
852
|
-
|
853
|
-
|
854
|
-
end
|
855
|
-
def _insert1(value,flags)
|
856
|
-
@data[@pos,0] = (@data_class.new << value)
|
857
|
-
@pos += 1 if (flags&Reverse).zero?
|
858
|
-
end
|
859
|
-
end
|
860
|
-
|
861
|
-
# This class treats an IO (or StringIO) as a Cursor. An IO is already
|
862
|
-
# like a Cursor, but doesn't have as robust an interface. Deleting and
|
863
|
-
# inserting is a slow/painful process.
|
864
|
-
class IO < Cursor
|
865
|
-
def initialize(io=StringIO.new)
|
866
|
-
super()
|
867
|
-
@io = io
|
868
|
-
end
|
869
|
-
def data_class
|
870
|
-
String
|
871
|
-
end
|
872
|
-
def _delete1(flags,&more) # :yield: l=1
|
873
|
-
if (flags&Reverse).nonzero?
|
874
|
-
begin
|
875
|
-
@io.seek(-1,::IO::SEEK_CUR)
|
876
|
-
ret = @io.getc
|
877
|
-
rescue
|
878
|
-
ret = nil
|
702
|
+
while v0 = read1next
|
703
|
+
code[v0]
|
879
704
|
end
|
880
|
-
else
|
881
|
-
ret = @io.getc
|
882
|
-
end or return(
|
883
|
-
if ret = more&&more[1]
|
884
|
-
(flags&Ignore).nonzero? ? true : ret[0]
|
885
|
-
end
|
886
|
-
)
|
887
|
-
ret = true if (flags&Ignore).nonzero?
|
888
|
-
after = 0
|
889
|
-
while c = @io.getc
|
890
|
-
after += 1
|
891
|
-
@io.seek(-2,::IO::SEEK_CUR)
|
892
|
-
@io.putc(c)
|
893
|
-
@io.seek(+1,::IO::SEEK_CUR)
|
894
705
|
end
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
ret
|
899
|
-
end
|
900
|
-
def _insert1(value,flags)
|
901
|
-
after = 0
|
902
|
-
while c = @io.getc
|
903
|
-
after += 1
|
904
|
-
@io.seek(-1,::IO::SEEK_CUR)
|
905
|
-
@io.putc(value)
|
906
|
-
value = c
|
907
|
-
end
|
908
|
-
@io.putc(value)
|
909
|
-
after += 1 if (flags&Reverse).nonzero?
|
910
|
-
@io.seek(-after,::IO::SEEK_CUR) if after>0
|
911
|
-
nil
|
912
|
-
end
|
913
|
-
def _get1(flags,&more) # :yield: l=1
|
914
|
-
if (flags&Reverse).nonzero?
|
915
|
-
begin
|
916
|
-
@io.seek(-1,::IO::SEEK_CUR)
|
917
|
-
rescue
|
918
|
-
return(
|
919
|
-
if ret = more&&more[1]
|
920
|
-
if (flags&Hold).nonzero?
|
921
|
-
flags ^= Hold
|
922
|
-
flags ^= Reverse
|
923
|
-
end
|
924
|
-
_insert(flags,ret=ret[0])
|
925
|
-
(flags&Ignore).nonzero? ? true : ret
|
926
|
-
end
|
927
|
-
)
|
928
|
-
end
|
929
|
-
if (flags&Ignore).nonzero?
|
930
|
-
@io.seek(+1,::IO::SEEK_CUR) if (flags&Hold).nonzero?
|
931
|
-
ret = true
|
932
|
-
else
|
933
|
-
ret = @io.getc
|
934
|
-
@io.seek(-1,::IO::SEEK_CUR) if (flags&Hold).zero?
|
935
|
-
end
|
936
|
-
else
|
937
|
-
if not ret = @io.getc
|
938
|
-
return(
|
939
|
-
if ret = more&&more[1]
|
940
|
-
@io.putc(ret=ret[0])
|
941
|
-
@io.seek(-1,::IO::SEEK_CUR) if (flags&Hold).nonzero?
|
942
|
-
(flags&Ignore).nonzero? ? true : ret
|
943
|
-
end
|
944
|
-
)
|
945
|
-
end
|
946
|
-
@io.ungetc(ret) if (flags&Hold).nonzero?
|
947
|
-
ret = true if (flags&Ignore).nonzero?
|
948
|
-
end
|
949
|
-
ret
|
950
|
-
end
|
951
|
-
def _put1(value,flags,&more) # :yield: l=1
|
952
|
-
if (flags&Reverse).nonzero?
|
953
|
-
begin
|
954
|
-
@io.seek(-1,::IO::SEEK_CUR)
|
955
|
-
rescue
|
956
|
-
if ret = more&&more[1]
|
957
|
-
ret = (flags&Read).zero? ? true : ret[0]
|
958
|
-
end
|
959
|
-
if (flags&Hold).nonzero?
|
960
|
-
flags ^= Hold
|
961
|
-
flags ^= Reverse
|
962
|
-
end
|
963
|
-
_insert1(value,flags)
|
964
|
-
return(ret)
|
965
|
-
end
|
966
|
-
if (flags&Read).nonzero?
|
967
|
-
ret = @io.getc
|
968
|
-
@io.ungetc(ret)
|
969
|
-
else
|
970
|
-
ret = true
|
971
|
-
end
|
972
|
-
@io.putc(value)
|
973
|
-
if (flags&Hold).zero?
|
974
|
-
@io.seek(-1,::IO::SEEK_CUR)
|
975
|
-
end
|
976
|
-
else
|
977
|
-
if (flags&Read).nonzero?
|
978
|
-
if not ret = @io.getc
|
979
|
-
if ret = more&&more[1]
|
980
|
-
ret = ret[0]
|
981
|
-
end
|
982
|
-
else
|
983
|
-
@io.ungetc(ret)
|
984
|
-
end
|
985
|
-
elsif @io.eof?
|
986
|
-
ret = more&&more[1]&&true
|
987
|
-
else
|
988
|
-
ret = true
|
989
|
-
end
|
990
|
-
@io.putc(value)
|
991
|
-
@io.seek(-1,::IO::SEEK_CUR) if (flags&Hold).nonzero?
|
992
|
-
end
|
993
|
-
ret
|
994
|
-
end
|
995
|
-
def close
|
996
|
-
@io.close
|
997
|
-
super()
|
998
|
-
end
|
999
|
-
end
|
1000
|
-
|
1001
|
-
# This Cursor class uses an Array/String for data before the cursor
|
1002
|
-
# and another one for data after the cursor. The result of this is that
|
1003
|
-
# data is only deleted/inserted at the end of these 2. Because of that
|
1004
|
-
# most operations have similar expense and are linear with respect to the
|
1005
|
-
# number of elements being moved, inserted, deleted, replaced, etc.
|
1006
|
-
class Buffer < Cursor
|
1007
|
-
def initialize(before=[],after=before.class.new)
|
1008
|
-
super()
|
1009
|
-
@before = before
|
1010
|
-
@after = after
|
1011
|
-
@data_class = (@before.respond_to?:data_class) ?
|
1012
|
-
@before.data_class : @before.class
|
1013
|
-
end
|
1014
|
-
def data_class
|
1015
|
-
@data_class
|
1016
|
-
end
|
1017
|
-
def _delete1(flags,&more) # :yield: l=1
|
1018
|
-
data = (flags&Reverse).nonzero? ? @before : @after
|
1019
|
-
data.size>0 or return(
|
1020
|
-
if ret = more&&more[1]
|
1021
|
-
(flags&Ignore).nonzero? ? true : ret[0]
|
1022
|
-
end
|
1023
|
-
)
|
1024
|
-
if (flags&Ignore).nonzero?
|
1025
|
-
data[-1,1] = @data_class.new
|
1026
|
-
true
|
1027
|
-
else
|
1028
|
-
data.slice!(-1)
|
1029
|
-
end
|
1030
|
-
end
|
1031
|
-
def _insert1(value,flags)
|
1032
|
-
data = (flags&Reverse).nonzero? ? @after : @before
|
1033
|
-
data << value
|
1034
|
-
end
|
1035
|
-
end
|
1036
|
-
|
1037
|
-
# This Cursor class implements a circular buffer (not very efficient now). The beginning and end
|
1038
|
-
# are treated to be the current position. #pos/#position and friends really
|
1039
|
-
# don't make sense here (at least right now).
|
1040
|
-
class Circular < Cursor
|
1041
|
-
def initialize(data=[])
|
1042
|
-
super()
|
1043
|
-
@data = data
|
1044
|
-
@data_class = (@data.respond_to?:data_class) ?
|
1045
|
-
@data.data_class : @data.class
|
1046
|
-
end
|
1047
|
-
def data_class
|
1048
|
-
@data_class
|
1049
|
-
end
|
1050
|
-
def _delete1(flags,&more) # :yield: l=1
|
1051
|
-
@data.size>0 or return(
|
1052
|
-
if ret = more&&more[1]
|
1053
|
-
(flags&Ignore).nonzero? ? true : ret[0]
|
1054
|
-
end
|
1055
|
-
)
|
1056
|
-
index = (flags&Reverse).nonzero? ? -1 : 0
|
1057
|
-
if (flags&Ignore).nonzero?
|
1058
|
-
@data[index,1] = @data_class.new
|
1059
|
-
true
|
1060
|
-
else
|
1061
|
-
@data.slice!(index)
|
1062
|
-
end
|
1063
|
-
end
|
1064
|
-
def _insert1(value,flags)
|
1065
|
-
if (flags&Reverse).nonzero?
|
1066
|
-
@data[0,0] = (@data_class.new << value)
|
1067
|
-
else
|
1068
|
-
@data << value
|
1069
|
-
end
|
1070
|
-
end
|
1071
|
-
def get(len=nil,flags=Next,&more) # :yield: l
|
1072
|
-
if len==Rest
|
1073
|
-
nil
|
1074
|
-
else
|
1075
|
-
super(len,flags,&more)
|
1076
|
-
end
|
1077
|
-
end
|
1078
|
-
def size
|
1079
|
-
@data.size
|
1080
|
-
end
|
1081
|
-
alias length size
|
1082
|
-
def clear
|
1083
|
-
@data.replace(@data_class.new)
|
1084
|
-
end
|
1085
|
-
def data
|
1086
|
-
@data.clone()
|
1087
|
-
end
|
1088
|
-
def replace(obj)
|
1089
|
-
if @data_class>=obj.class
|
1090
|
-
@data.replace(obj)
|
1091
|
-
else
|
1092
|
-
super(obj)
|
706
|
+
else
|
707
|
+
while v0 = scan_until(terminator,reverse)
|
708
|
+
code[v0]
|
1093
709
|
end
|
1094
|
-
self
|
1095
710
|
end
|
1096
|
-
|
1097
711
|
end
|
1098
|
-
|
1099
|
-
#
|
1100
|
-
#
|
1101
|
-
#
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
class Buffered < Cursor
|
1110
|
-
def initialize(input,output=nil,buffer=Buffer.new((input||output).data_class.new))
|
1111
|
-
@input = input
|
1112
|
-
@output = output
|
1113
|
-
@buffer = buffer
|
1114
|
-
@offset = 0
|
1115
|
-
super()
|
1116
|
-
end
|
1117
|
-
def data_class
|
1118
|
-
@buffer.data_class
|
1119
|
-
end
|
1120
|
-
def get(len=nil,flags=Next,&more) # :yield: l
|
1121
|
-
value = @buffer.get(len,flags) { |l|
|
1122
|
-
if (flags&Reverse).nonzero?
|
1123
|
-
if @offset.zero?
|
1124
|
-
more&&more[l]
|
1125
|
-
else
|
1126
|
-
v = @output.get(l,Prev,&more)
|
1127
|
-
@offset -= v.size if v
|
1128
|
-
v
|
1129
|
-
end
|
1130
|
-
elsif @input
|
1131
|
-
@input.get(l,Next,&more)
|
1132
|
-
else
|
1133
|
-
more&&more[l]
|
1134
|
-
end
|
1135
|
-
}
|
1136
|
-
if not position?
|
1137
|
-
@offset = @buffer.pos+@offset
|
1138
|
-
if output_value = @buffer.get(Rest,Prev|Delete)
|
1139
|
-
@output.put(output_value,Next) if @output
|
1140
|
-
end
|
1141
|
-
end
|
1142
|
-
value
|
1143
|
-
end
|
1144
|
-
def put(value,flags=Next,&more) # :yield: l
|
1145
|
-
value0 = @buffer.put(value,flags) { |l|
|
1146
|
-
if (flags&Reverse).nonzero?
|
1147
|
-
if @offset.zero?
|
1148
|
-
more&&more[l]
|
1149
|
-
else
|
1150
|
-
v = @output.get(l,Prev,&more)
|
1151
|
-
@offset -= v.size if v
|
1152
|
-
v
|
1153
|
-
end
|
1154
|
-
elsif @input
|
1155
|
-
@input.get(l,Next,&more)
|
1156
|
-
else
|
1157
|
-
more&&more[l]
|
1158
|
-
end
|
1159
|
-
}
|
1160
|
-
if not position?
|
1161
|
-
@offset = @buffer.pos+@offset
|
1162
|
-
if output_value = @buffer.get(Rest,Prev|Delete)
|
1163
|
-
@output.put(output_value,Next) if @output
|
712
|
+
# Performs an in-place collect! (like Array#collect!).
|
713
|
+
# When the code returns +nil+ the element/sequence will be deleted.
|
714
|
+
# Accepts the same enhancements as #each does.
|
715
|
+
# 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
|
717
|
+
self.pos = index if index
|
718
|
+
continue = true
|
719
|
+
if terminator.nil?
|
720
|
+
if reverse
|
721
|
+
while v0 = delete1before
|
722
|
+
(v = code[v0]).nil? or insert1after(v)
|
1164
723
|
end
|
1165
|
-
end
|
1166
|
-
value0
|
1167
|
-
end
|
1168
|
-
def pos(reverse=false,&code) # :yield:
|
1169
|
-
if code or reverse
|
1170
|
-
super(reverse,&code)
|
1171
724
|
else
|
1172
|
-
|
1173
|
-
|
1174
|
-
end
|
1175
|
-
def pos=(p)
|
1176
|
-
if p<Begin
|
1177
|
-
super(p)
|
1178
|
-
else
|
1179
|
-
p = p-@offset
|
1180
|
-
if p<Begin
|
1181
|
-
@buffer.pos = Begin
|
1182
|
-
get(-p,Prev|Ignore)
|
1183
|
-
@offset
|
1184
|
-
else
|
1185
|
-
(@buffer.pos = p)+@offset
|
725
|
+
while v0 = delete1after
|
726
|
+
(v = code[v0]).nil? or insert1before(v)
|
1186
727
|
end
|
1187
728
|
end
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
@output.put(value,Next)
|
1192
|
-
value = @buffer.get(Rest,Next|Delete) and @output and
|
1193
|
-
@output.put(value,Next)
|
1194
|
-
super()
|
1195
|
-
end
|
1196
|
-
end
|
1197
|
-
|
1198
|
-
# This class tracks the current line and column. Both line and column
|
1199
|
-
# start at 0. When a newline is found, the line is incremented and the
|
1200
|
-
# column is reset. With other characters, the column is incremented.
|
1201
|
-
# The line and column numbers are only tracked when reading forward, but
|
1202
|
-
# using #position (and friends) will hold the line and column.
|
1203
|
-
class LineColumnNumbered < Cursor
|
1204
|
-
# Structure stored in the #prop attribute. line and column numbers
|
1205
|
-
# are accessed through #prop.line and #prop.column.
|
1206
|
-
LineColumn = Struct.new("LineColumn",:line,:column)
|
1207
|
-
# Create a new Cursor from another that tracks line and column.
|
1208
|
-
# +newline+ can be any valid argument for String#index (or the
|
1209
|
-
# index method for whatever the underlying #data_class is). For example,
|
1210
|
-
# +newline+=+/\n|\r(?!\n)/+ could be used for unix, mac, dos/cpm style
|
1211
|
-
# newlines.
|
1212
|
-
def initialize(cursor,newline="\n"[0])
|
1213
|
-
@cursor = cursor
|
1214
|
-
@newline = newline
|
1215
|
-
self.prop = LineColumn.new(0,0)
|
1216
|
-
super()
|
1217
|
-
end
|
1218
|
-
def get(len=nil,flags=Next,&more) # :yield: l
|
1219
|
-
ret = @cursor.get(len,flags,&more)
|
1220
|
-
if (flags&Ignore).zero? and (flags&Reverse).zero? and ret
|
1221
|
-
if not len
|
1222
|
-
if ret==@newline
|
1223
|
-
prop.line += 1
|
1224
|
-
prop.column = 0
|
1225
|
-
else
|
1226
|
-
prop.column += 1
|
1227
|
-
end
|
1228
|
-
else
|
1229
|
-
i0 = 0
|
1230
|
-
begin
|
1231
|
-
while i = index(@newline,i0)
|
1232
|
-
prop.line += 1
|
1233
|
-
prop.column = 0
|
1234
|
-
i0 = i+1
|
1235
|
-
end
|
1236
|
-
rescue
|
1237
|
-
#index with 2 args not supported
|
1238
|
-
i = i0
|
1239
|
-
while i<ret.size
|
1240
|
-
if @newline==ret[i]
|
1241
|
-
prop.line += 1
|
1242
|
-
prop.column = 0
|
1243
|
-
i0 = i+1
|
1244
|
-
end
|
1245
|
-
i += 1
|
1246
|
-
end
|
1247
|
-
end
|
1248
|
-
prop.column += ret.size-i0
|
1249
|
-
end
|
729
|
+
else
|
730
|
+
while v0 = scan_until(terminator,reverse,nil)
|
731
|
+
(v = code[v0]).nil? or write(v,reverse,nil)
|
1250
732
|
end
|
1251
|
-
ret
|
1252
733
|
end
|
1253
734
|
end
|
1254
|
-
|
735
|
+
alias map! collect!
|
1255
736
|
end
|
1256
737
|
|
738
|
+
require 'cursor/position'
|
739
|
+
|
1257
740
|
|