local unicode=require("unicode") local kb=require("keyboard") local tty=require("tty") local text=require("text") local computer=require("computer") local keys=kb.keys local core_cursor={} core_cursor.vertical={} function core_cursor.vertical:move(n) local s=math.max(math.min(self.index+n,self.len),0) if s==self.index then return end local echo_cmd=keys.left local from=s+1 local to=self.index if s>self.index then echo_cmd,from,to=keys.right,to+1,s end self.index=s local step=unicode.wlen(unicode.sub(self.data,from,to)) self:echo(echo_cmd,step) end function core_cursor.vertical:update(arg,back) if not arg then self.tails={} self.data="" self.index=0 self.sy=0 self.hindex=0 end local s1=unicode.sub(self.data,1,self.index) local s2=unicode.sub(self.data,self.index+1) if type(arg)=="string"then if back==false then arg,s2=arg..s2,"" else self.index=self.index+unicode.len(arg) self:echo(arg) end self.data=s1 ..arg elseif arg then local has_tail=arg<0 or#s2>0 if arg<0 then if self.index<=0 then return end self:move(arg) s1=unicode.sub(s1,1,-1+arg) else if self.index>=self.len then return end s2=unicode.sub(s2,1+arg) end self.data=s1 if has_tail then self:echo(self.clear) end end self.len=unicode.len(self.data) self:move(back or 0) if#s2>0 then self:update(s2,-unicode.len(s2)) end end function core_cursor.vertical:echo(arg,num) local win=tty.window local gpu=win.gpu if not io.stdin.tty then return end local out=io.stdin.stream if not gpu then return end win.nowrap=self.nowrap if arg==""then local width,x,y=win.width,win.x,win.y if x>width then win.x=((x-1)%width)+1 win.y=y+math.floor(x/width) out:write("") x,y=win.x,win.y end if x<=0 or y<=0 or y>win.height or not gpu then return end return table.pack(select(2,pcall(gpu.get,x+win.dx,y+win.dy))) elseif arg==keys.left then local x=win.x-num local y=win.y while x<1 do x=x+win.width-#(self.tails[win.dy+y-self.sy-1]or"") y=y-1 end win.x,win.y=x,y arg="" elseif arg==keys.right then local x=win.x+num local y=win.y while true do local width=win.width-#(self.tails[win.dy+y-self.sy]or"") if x<=width then break end x=x-width y=y+1 end win.x,win.y=x,y arg="" elseif not arg or arg==true then local char=self.char_at_cursor if(arg==nil and not char)or(arg and not self.blinked)then char=char or self:echo("") if not char[1]then return false end self.blinked=true if not arg then out:write("\0277") char.saved=win.saved gpu.setForeground(char[4]or char[2],not not char[4]) gpu.setBackground(char[5]or char[3],not not char[5]) end out:write("\0277\27[7m"..char[1].."\0278") elseif(arg and self.blinked)or(arg==false and char)then self.blinked=false gpu.set(win.x+win.dx,win.y+win.dy,char[1]) if not arg then win.saved=char.saved out:write("\0278") char=nil end end self.char_at_cursor=char return true end return out:write(arg) end function core_cursor.vertical:handle(name,char,code) if name=="clipboard"then self.cache=nil local newline=char:find("\10")or#char local printable_prefix,remainder=char:sub(1,newline),char:sub(newline+1) self:update(printable_prefix) self:update(remainder,false) elseif name=="touch"or name=="drag"then core_cursor.touch(self,char,code) elseif name=="interrupted"then self:echo("^C\n") return false,name elseif name=="key_down"then local data=self.data local backup_cache=self.cache self.cache=nil local ctrl=kb.isControlDown() if ctrl and code==keys.d then return elseif code==keys.tab then self.cache=backup_cache core_cursor.tab(self) elseif code==keys.enter or code==keys.numpadenter then self:move(self.len) self:update("\n") elseif code==keys.up or code==keys.down then local ni=self.hindex+(code==keys.up and 1 or-1) if ni>=0 and ni<=#self then self[self.hindex]=data self.hindex=ni self:move(self.len) self:update(-self.len) self:update(self[ni]) end elseif code==keys.left or code==keys.back or code==keys.w and ctrl then local value=ctrl and((unicode.sub(data,1,self.index):find("%s[^%s]+%s*$")or 0)-self.index)or-1 if code==keys.left then self:move(value) else self:update(value) end elseif code==keys.right then self:move(ctrl and((data:find("%s[^%s]",self.index+1)or self.len)-self.index)or 1) elseif code==keys.home then self:move(-self.len) elseif code==keys["end"]then self:move(self.len) elseif code==keys.delete then self:update(1) elseif char>=32 then self:update(unicode.char(char)) else self.cache=backup_cache end end return true end core_cursor.vertical.clear="\27[J" function core_cursor.new(base,index) base=base or{} base.super=base.super or index or core_cursor.vertical setmetatable(base,getmetatable(base)or{__index=base.super}) if not base.data then base:update() end return base end function core_cursor.read(cursor) local last=cursor.next or"" cursor.next=nil if#last>0 then cursor:handle("clipboard",last) end local address_check= { key_down=tty.keyboard, clipboard=tty.keyboard, touch=tty.screen, drag=tty.screen, drop=tty.screen } while true do local next_line=cursor.data:find("\10") if next_line then local result=cursor.data:sub(1,next_line) local overflow=cursor.data:sub(next_line+1) local history=text.trim(result) if history~=""and history~=cursor[1]then table.insert(cursor,1,history) cursor[(tonumber(os.getenv("HISTSIZE"))or 10)+1]=nil end cursor[0]=nil cursor:update() cursor.next=overflow return result end cursor:echo() local pack=table.pack(computer.pullSignal(tty.window.blink and.5 or math.huge)) local name=pack[1] cursor:echo(not name) if name then local filter_address=address_check[name] if not filter_address or filter_address()==pack[2]then local ret,why=cursor:handle(name,table.unpack(pack,3,pack.n)) if not ret then return ret,why end end end end end require("package").delay(core_cursor,"/lib/core/full_cursor.lua") return core_cursor