-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathbuffer.jl
219 lines (192 loc) · 5.64 KB
/
buffer.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
module Buffer
import REPL.LineEdit as LE
export VimBuffer, mode, VimMode, normal_mode, insert_mode, testbuf, readall, freeze,
BufferRecord, chars, peek_left, peek_right, peek_two_right, read_left, read_right
@enum VimMode begin
normal_mode
insert_mode
# visual
end
function VimMode(s::AbstractString)
return if s == "i"
insert_mode
elseif s == "n"
normal_mode
else
normal_mode
end
end
VimMode(::Nothing) = normal_mode
VimMode(vm::VimMode) = vm
"""
Generate a buffer from s, but place its position where the pipe operator occurs in `s`
match | as the position of the buffer
and match |i| as "insert mode", |n| as "normal mode".
Defaults to normal mode.
"""
function testbuf(s::AbstractString)::VimBuffer
# issue with testbuf!
m = match(r"(.*?)\|(?:([ni])\|)?(.*)"s, s)
if m === nothing
throw(ArgumentError("could not construct VimBuffer with string \"$s\"\n Expecting a string with a pipe `|` indicating cursor position"))
end
(a, mode, b) = (m[1], m[2], m[3])
buf = IOBuffer(; read=true, write=true, append=true)
cursor_index = write(buf, a)
after_cursor = write(buf, b)
@debug "creating testbuf" from=s start=a mode endd=b
# @debug "creating testbuf" cursor_index after_cursor
seek(buf, cursor_index)
vim_mode = VimMode(mode)
return VimBuffer(buf, vim_mode)
end
struct VimBuffer <: IO
buf::IOBuffer
mode::VimMode
end
VimBuffer() = VimBuffer(IOBuffer(), normal_mode)
mode(vb::VimBuffer) = vb.mode
# TODO modify VimBuffer operations to operate on Characters rather than bytes
# Status: Removed uses of
# - [ ] length()
# - [ ] skip() in favor of skipchars, or else ensure skip operatoes on characters only
Base.position(vb::VimBuffer) = position(vb.buf)
Base.seek(vb::VimBuffer, n) = seek(vb.buf, n)
Base.mark(vb::VimBuffer) = mark(vb.buf)
Base.peek(vb::VimBuffer, ::Type{T}) where {T} = peek(vb.buf, T)
Base.peek(vb::VimBuffer) = peek(vb.buf)
Base.reset(vb::VimBuffer) = reset(vb.buf)
Base.read(vb::VimBuffer, ::Type{Char}) = read(vb.buf, Char)
Base.read(vb::VimBuffer, ::Type{String}) = read(vb.buf, String)
Base.take!(vb::VimBuffer) = take!(vb.buf)
Base.eof(vb::VimBuffer) = eof(vb.buf)
Base.skip(vb::VimBuffer, o) = skip(vb.buf, o)
Base.truncate(vb::VimBuffer, n::Integer) = truncate(vb.buf, n)
Base.write(vb::VimBuffer, x::AbstractString) = write(vb.buf, x)
Base.write(vb::VimBuffer, x::Union{SubString{String},String}) = write(vb.buf, x)
Base.copy(vb::VimBuffer) = VimBuffer(copy(vb.buf), vb.mode)
"""
Read 1 valid UTF-8 character left of the current position and leave the buffer in the same position.
"""
function peek_left(buf::IO)::Union{Char,Nothing}
origin = position(buf)
while position(buf) > 0
skip(buf, -1)
c = peek(buf, Char)
# skip(buf, -1)
if isvalid(c)
seek(buf, origin)
return c
end
end
seek(buf, origin)
return nothing
end
"""
Read 1 valid UTF-8 character left of the current position.
Place the cursor before the char that is read, if any.
"""
function read_left(buf::IO)::Union{Char,Nothing}
origin = position(buf)
while position(buf) > 0
skip(buf, -1)
c = peek(buf, Char)
# skip(buf, -1)
if isvalid(c)
return c
end
end
seek(buf, origin)
return nothing
end
"""
Read 1 valid UTF-8 character right of the current position and leave the buffer in the same position.
"""
function peek_right(buf::IO)::Union{Char,Nothing}
origin = position(buf)
while !eof(buf)
c = read(buf, Char)
if isvalid(c)
seek(buf, origin)
return c
end
end
seek(buf, origin)
return nothing
end
"""
Read up to 2 valid UTF-8 character right of the current position and leave the buffer in the same position.
Returns a tuple with each successful character (or nothing for a character not read successfully)
"""
function peek_two_right(buf::IO)
origin = position(buf)
c1 = read_right(buf)
c2 = read_right(buf)
seek(buf, origin)
return (c1, c2)
end
"""
Read 1 valid UTF-8 character right of the current position.
Place the cursor after the char that is read, if any.
"""
function read_right(buf::IO)::Union{Char,Nothing}
origin = position(buf)
while !eof(buf)
c = read(buf, Char)
if isvalid(c)
return c
end
end
seek(buf, origin)
return nothing
end
function chars(vb::VimBuffer)::Vector{Char}
collect(String(take!(copy(vb))))
end
function Base.getproperty(vb::VimBuffer, sym::Symbol)
if sym === :size
return vb.buf.size
else # fallback to getfield
return getfield(vb, sym)
end
end
function Base.show(io::IO, buf::VimBuffer)
# read all of vb into a string
# reconstruct the "mode" style string, e.g.
# "this is|i| a buffer in insert mode"
pos = position(buf)
text = String(take!(copy(buf)))
# cs = chars(buf)
s = if length(text) <= 0
""
else
# seekstart(buf)
# read(buf, String)
end
# seek(buf, pos)
a = text[begin:thisind(text, pos)]
b = text[nextind(text, pos):end]
mode = if buf.mode == insert_mode
"i"
elseif buf.mode == normal_mode
"n"
end
out = a * "|$mode|" * b
print(io, " VimBuffer(\"$out\")")
end
function Base.show(io::IO, ::MIME"text/plain", vb::VimBuffer)
show(io, vb)
end
"""
Two VimBuffers are equal if their contents and position are equal
"""
function Base.:(==)(buf1::VimBuffer, buf2::VimBuffer)
a = IOBuffer()
b = IOBuffer()
show(a, buf1)
show(b, buf2)
seekstart(a)
seekstart(b)
return read(a, String) == read(b, String)
end
end