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 self.path = path 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) if ed.buffers[buffer] then buffer = ed.buffers[buffer] end if type(buffer) == "string" then nb = ed.newBuffer() nb:load(buffer) buffer = nb end if type(buffer) ~= "table" then buffer = ed.newBuffer() buffer[1] = "" end 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