2020-04-11 13:49:44 -04:00
|
|
|
local ed = package.loaded.ed or {}
|
|
|
|
ed.bfunc = ed.bfunc or {}
|
|
|
|
ed.ifunc = ed.ifunc or setmetatable({},{__index=ed.bfunc})
|
|
|
|
ed.buffers = ed.buffers or {}
|
|
|
|
|
|
|
|
function ed.bfunc:load(fpath)
|
|
|
|
local f = io.open(fpath,"rb")
|
|
|
|
if not f then return false, "unable to open file" end
|
|
|
|
for line in f:lines() do
|
|
|
|
self[#self+1] = line
|
|
|
|
end
|
|
|
|
f:close()
|
|
|
|
self.path = fpath
|
|
|
|
return true, #self
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.bfunc:save(fpath)
|
|
|
|
local path = fpath or self.path
|
|
|
|
if not path then return false, "no path" end
|
2020-08-20 20:28:51 -04:00
|
|
|
self.path = path
|
2020-04-11 13:49:44 -04:00
|
|
|
local f = io.open(path,"wb")
|
|
|
|
if not f then return false, "unable to open file" end
|
|
|
|
for k,v in ipairs(self) do
|
|
|
|
f:write(v.."\n")
|
|
|
|
end
|
|
|
|
f:close()
|
|
|
|
self.dirty = false
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.bfunc:close(discard)
|
|
|
|
if not discard and self.dirty then
|
|
|
|
local saved = self:save()
|
|
|
|
if not saved then return false, "unable to save buffer" end
|
|
|
|
end
|
|
|
|
for k,v in pairs(ed.buffers) do
|
|
|
|
if v == self then
|
|
|
|
table.remove(ed.buffers,k)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false, "unable to find buffer handle"
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.bfunc:range(start,finish)
|
|
|
|
start, finish = math.max(1,tonumber(start) or 1), math.min(#self,tonumber(finish) or #self)
|
|
|
|
local rt = {}
|
|
|
|
for i = start, finish do
|
|
|
|
rt[#rt+1] = self[i]
|
|
|
|
end
|
|
|
|
return rt
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.bfunc:checkCursor()
|
|
|
|
self.y = math.max(self.y,1)
|
|
|
|
self.y = math.min(self.y,#self)
|
|
|
|
self.x = math.max(self.x,1)
|
|
|
|
self.x = math.min(self.x,math.max(self[self.y]:len()+1,1))
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.ifunc.buffers()
|
|
|
|
for k,v in pairs(ed.buffers) do
|
|
|
|
print(string.format("\27[31m%4i\27[0m %s",k,v.path or "?"))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.ifunc.help()
|
|
|
|
print("Available commands:")
|
|
|
|
for k,v in pairs(ed.bfunc) do
|
|
|
|
print(k)
|
|
|
|
end
|
|
|
|
for k,v in pairs(ed.ifunc) do
|
|
|
|
print(k)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.ifunc:list(start,finish)
|
|
|
|
start, finish = math.max(1,tonumber(start) or 1), math.min(#self,tonumber(finish) or #self)
|
|
|
|
local lt = self:range(start,finish)
|
|
|
|
for k,v in pairs(lt) do
|
|
|
|
print(string.format("\27[31m%4d\27[0m %s",k+start-1,v))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.ifunc:insert(p,o)
|
|
|
|
ed.ifunc.pointer(self,p)
|
|
|
|
while true do
|
|
|
|
io.write(string.format("\27[31m%4d\27[0m ",self.y + (o or 0)))
|
|
|
|
local line = io.read()
|
|
|
|
if line == "." then break end
|
|
|
|
table.insert(self,self.y + (o or 0),line)
|
|
|
|
self.dirty = true
|
|
|
|
self.y = self.y + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.ifunc:append(p)
|
|
|
|
ed.ifunc.insert(self,p,1)
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.ifunc:pointer(n)
|
|
|
|
self.y = math.max(1,tonumber(n) or 1)
|
|
|
|
self.y = math.min(#self,self.y)
|
|
|
|
print(self.y)
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.newBuffer()
|
|
|
|
local nb = setmetatable({},{__index=ed.bfunc})
|
|
|
|
nb.x,nb.y = 1, 1
|
|
|
|
ed.buffers[#ed.buffers+1] = nb
|
|
|
|
return nb
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.open(buffer)
|
2020-08-20 20:28:51 -04:00
|
|
|
local bpath = buffer
|
2020-04-11 13:49:44 -04:00
|
|
|
if ed.buffers[buffer] then
|
|
|
|
buffer = ed.buffers[buffer]
|
|
|
|
end
|
|
|
|
if type(buffer) == "string" then
|
|
|
|
nb = ed.newBuffer()
|
|
|
|
nb:load(buffer)
|
|
|
|
buffer = nb
|
|
|
|
end
|
2020-08-20 20:28:51 -04:00
|
|
|
if type(buffer) ~= "table" then buffer = ed.newBuffer() end
|
|
|
|
buffer[1] = buffer[1] or ""
|
2020-08-20 20:37:19 -04:00
|
|
|
buffer.path = buffer.path or "/"..((bpath:sub(1,1) == "/" and table.concat(fs.segments(path),"/")) or table.concat(fs.segments(os.getenv("PWD").."/"..bpath,"/")))
|
2020-04-11 13:49:44 -04:00
|
|
|
return buffer
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.interactive(buffer)
|
|
|
|
buffer=ed.open(buffer)
|
|
|
|
while true do
|
|
|
|
io.write("\27[34mced:\27[0m ")
|
|
|
|
local line = io.read()
|
|
|
|
local words = {}
|
|
|
|
for word in line:gmatch("[^%s]+") do
|
|
|
|
words[#words+1] = word
|
|
|
|
end
|
|
|
|
local cmd = table.remove(words,1)
|
|
|
|
if ed.ifunc[cmd] then
|
|
|
|
print(ed.ifunc[cmd](buffer,table.unpack(words)))
|
|
|
|
elseif cmd == "quit" then
|
|
|
|
break
|
|
|
|
else
|
|
|
|
print("Unknown command.")
|
|
|
|
end
|
|
|
|
if cmd == "close" then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function ed.visual(buffer)
|
|
|
|
buffer=ed.open(buffer)
|
|
|
|
local mx, my = 40, 13
|
|
|
|
local cx,cy = math.max(1,buffer.x-mx//2), math.max(1,buffer.y-my//2)
|
|
|
|
local ox, oy
|
|
|
|
local mode = "c"
|
|
|
|
local mult, multstr = 1, ""
|
|
|
|
local resized = false
|
|
|
|
io.write("\27[999;999H\27[6n")
|
|
|
|
local function drawBuffer(force)
|
|
|
|
if cx ~= ox or cy ~= oy or force then
|
|
|
|
io.write("\27[2J\27[H")
|
|
|
|
for i = cy, cy+my do
|
|
|
|
print(string.format("\27[31m%4i \27[0m%s",i,(buffer[i] or "\27[36m~"):sub(cx,cx+mx-6)))
|
|
|
|
end
|
|
|
|
elseif mode == "i" then
|
|
|
|
print(string.format("\27[2K\27[999D\27[31m%4i \27[0m%s",buffer.y,(buffer[buffer.y] or "\27[36m~"):sub(cx,cx+mx-6)))
|
|
|
|
end
|
|
|
|
io.write(string.format("\27[1;%iH\27[0;36;%im\27[2K[%s] ced visual: %i,%i/%i, %iK free %i",my+2,(mode == "c" and 7) or 0, mode, buffer.x, buffer.y, #buffer, computer.freeMemory()//1024,mult))
|
|
|
|
io.write(string.format("\27[%i;%iH\27[0m",buffer.x+6-cx,buffer.y-cy+1))
|
|
|
|
end
|
|
|
|
os.setTimeout(0.0005)
|
|
|
|
while true do
|
|
|
|
drawBuffer()
|
|
|
|
ox, oy = cx, cy
|
|
|
|
io.write("\27[100;101m")
|
|
|
|
local c=io.read(1)
|
|
|
|
if c == "\27" then
|
|
|
|
local b = ""
|
|
|
|
repeat
|
|
|
|
c=io.read(1)
|
|
|
|
b=b..c
|
|
|
|
until c:match("%a")
|
|
|
|
dprint(b)
|
|
|
|
local nx, ny = b:match("%[(%d+);(%d+)")
|
|
|
|
if nx and ny then
|
|
|
|
mx, my = math.max(mx, tonumber(nx)), math.max(my, tonumber(ny)-2)
|
|
|
|
drawBuffer(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if mode == "c" then
|
|
|
|
if c == "q" then
|
|
|
|
io.write("\27[0m\27[2J\27[H")
|
|
|
|
break
|
|
|
|
elseif c == "h" then
|
|
|
|
buffer.x = buffer.x - mult
|
|
|
|
elseif c == "l" then
|
|
|
|
buffer.x = buffer.x + mult
|
|
|
|
elseif c == "j" then
|
|
|
|
buffer.y = buffer.y + mult
|
|
|
|
elseif c == "k" then
|
|
|
|
buffer.y = buffer.y - mult
|
|
|
|
elseif c == "d" then
|
|
|
|
for i = 1, mult do
|
|
|
|
table.remove(buffer,buffer.y)
|
|
|
|
end
|
|
|
|
drawBuffer(true)
|
|
|
|
elseif c == "i" or c == "\t" then
|
|
|
|
mode = "i"
|
|
|
|
elseif c == "a" then
|
|
|
|
buffer.x = buffer.x + 1
|
|
|
|
mode = "i"
|
|
|
|
elseif c == ":" then
|
|
|
|
io.write("\27[1;999H\27[2K")
|
|
|
|
ed.interactive(buffer)
|
|
|
|
drawBuffer(true)
|
|
|
|
elseif c == "w" then
|
|
|
|
buffer:save()
|
|
|
|
elseif c:match("%d") then
|
|
|
|
multstr = multstr .. c
|
|
|
|
mult = tonumber(multstr)
|
|
|
|
end
|
|
|
|
if c:match("%D") then
|
|
|
|
multstr = ""
|
|
|
|
mult = 1
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if c == "\t" then
|
|
|
|
mode = "c"
|
|
|
|
elseif c == "\8" and buffer.x == 1 and buffer.y > 1 then
|
|
|
|
local lblen = buffer[buffer.y-1]:len()
|
|
|
|
buffer[buffer.y-1] = buffer[buffer.y-1] .. table.remove(buffer,buffer.y)
|
|
|
|
buffer.x, buffer.y = lblen+1, buffer.y - 1
|
|
|
|
drawBuffer(true)
|
|
|
|
elseif c == "\8" then
|
|
|
|
buffer[buffer.y] = buffer[buffer.y]:sub(1,buffer.x - 2)..buffer[buffer.y]:sub(buffer.x)
|
|
|
|
buffer.x = buffer.x - 1
|
|
|
|
else
|
|
|
|
buffer[buffer.y] = buffer[buffer.y]:sub(1,buffer.x-1)..c..buffer[buffer.y]:sub(buffer.x)
|
|
|
|
buffer.x = buffer.x + 1
|
|
|
|
local fh,sh = buffer[buffer.y]:match("(.*)\n(.*)")
|
|
|
|
if fh and sh then
|
|
|
|
buffer[buffer.y] = fh
|
|
|
|
table.insert(buffer,buffer.y+1,sh)
|
|
|
|
buffer.x, buffer.y = 1, buffer.y + 1
|
|
|
|
drawBuffer(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
buffer:checkCursor()
|
|
|
|
local ax, amx = buffer.x + 5, mx - 5
|
|
|
|
if cy + my < buffer.y + 3 then
|
|
|
|
cy = math.min(#buffer-my,buffer.y + 6 - my)
|
|
|
|
end
|
|
|
|
if cy + 3 > buffer.y then
|
|
|
|
cy = math.max(1,buffer.y - 6)
|
|
|
|
end
|
|
|
|
if buffer.x + 5 > cx + mx - 4 then
|
|
|
|
cx = math.max(1,(buffer.x + 6) - (mx - 6))
|
|
|
|
end
|
|
|
|
if buffer.x < cx + 3 then
|
|
|
|
cx = math.max(1,buffer.x - 6)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return ed
|