moved string.trim to text.trim; made some more Lua functions use get/set prefix after all (got too used to dropping it from Scala, but since most callbacks will use this, too, I'd like to keep it uniform); major cleanup of term's readline implementation

This commit is contained in:
Florian Nücke 2013-11-23 18:10:45 +01:00
parent e674f4905b
commit 374d66d39e
12 changed files with 227 additions and 209 deletions

View File

@ -143,13 +143,7 @@ sandbox = {
rep = string.rep,
reverse = string.reverse,
sub = string.sub,
upper = string.upper,
uchar = string.uchar,
trim = function(s) -- from http://lua-users.org/wiki/StringTrim
local from = string.match(s, "^%s*()")
return from > #s and "" or string.match(s, ".*%S", from)
end
upper = string.upper
},
table = {

View File

@ -5,13 +5,13 @@ if args.n == 0 then
print(name, value)
end
elseif #args == 1 then
local value = shell.alias(args[1])
local value = shell.getAlias(args[1])
if value then
print(value)
else
print("no such alias")
end
else
shell.alias(args[1], args[2])
shell.setAlias(args[1], args[2])
print("alias created: " .. args[1] .. " -> " .. args[2])
end

View File

@ -2,7 +2,7 @@ local args = shell.parse(...)
if #args == 0 then
print("Usage: cd <dirname>")
else
local result, reason = shell.cwd(shell.resolve(args[1]))
local result, reason = shell.setWorkingDirectory(shell.resolve(args[1]))
if not result then
print(reason)
end

View File

@ -1 +1 @@
print(shell.cwd())
print(shell.getWorkingDirectory())

View File

@ -27,7 +27,7 @@ while true do
while #history > 10 do
table.remove(history, 1)
end
command = string.trim(command)
command = text.trim(command)
if command == "exit" then
return
elseif command ~= "" then

View File

@ -4,9 +4,10 @@ if #args < 1 then
return
end
local result = shell.alias(args[1], nil)
local result = shell.getAlias(args[1])
if not result then
print("no such alias")
else
shell.setAlias(args[1], nil)
print("alias removed: " .. args[1] .. " -> " .. result)
end

View File

@ -1,7 +1,7 @@
local removing = {}
local primaries = {}
local function tryGetPrimary(self, key)
local function tryGetPrimary(_, key)
if component.isAvailable(key) then
return primaries[key]
end

View File

@ -59,14 +59,15 @@ end
-------------------------------------------------------------------------------
function filesystem.autorun(enabled)
if enabled ~= nil then
checkArg(1, enabled, "boolean")
isAutorunEnabled = enabled
end
function filesystem.isAutorunEnabled(value)
return isAutorunEnabled
end
function filesystem.setAutorunEnabled(value)
checkArg(1, value, "boolean")
isAutorunEnabled = value
end
function filesystem.canonical(path)
local result = table.concat(segments(path), "/")
if unicode.sub(path, 1, 1) == "/" then

View File

@ -11,7 +11,7 @@ os.execute = function(command)
if head == oldHead then -- say no to infinite recursion, live longer
command = nil
else
command = shell.alias(head)
command = shell.getAlias(head)
end
until command == nil
local args = {}

View File

@ -35,7 +35,7 @@ local function findFile(name, ext)
local found, where = findIn("/")
if found then return where end
else
local found, where = findIn(shell.cwd())
local found, where = findIn(shell.getWorkingDirectory())
if found then return where end
for _, p in ipairs(path) do
local found, where = findIn(p)
@ -47,34 +47,35 @@ end
-------------------------------------------------------------------------------
function shell.alias(alias, ...)
function shell.getAlias(alias)
return aliases[alias]
end
function shell.setAlias(alias, value)
checkArg(1, alias, "string")
local result = aliases[alias]
local args = table.pack(...)
if args.n > 0 then
checkArg(2, args[1], "string", "nil")
aliases[alias] = args[1]
end
return result
checkArg(2, value, "string", "nil")
aliases[alias] = value
end
function shell.aliases()
return pairs(aliases)
end
function shell.cwd(dir)
if dir then
checkArg(1, dir, "string")
dir = fs.canonical(dir) .. "/"
if fs.isDirectory(dir) then
cwd = dir
else
return nil, "not a directory"
end
end
function shell.getWorkingDirectory()
return cwd
end
function shell.setWorkingDirectory(dir)
checkArg(1, dir, "string")
dir = fs.canonical(dir) .. "/"
if fs.isDirectory(dir) then
cwd = dir
return true
else
return nil, "not a directory"
end
end
function shell.execute(program, ...)
if type(program) ~= "function" then
local where, reason = shell.resolve(program, "lua")
@ -134,21 +135,20 @@ function shell.parse(...)
return args, options
end
function shell.path(...)
local result = table.concat(path, ":")
local args = table.pack(...)
if args.n > 0 then
checkArg(1, args[1], "string")
path = {}
for p in string:gmatch(args[1], "[^:]") do
p = fs.canonical(string.trim(p))
if unicode.sub(p, 1, 1) ~= "/" then
p = "/" .. p
end
table.insert(path, p)
function shell.getPath()
return table.concat(path, ":")
end
function shell.setPath(value)
checkArg(1, value, "string")
path = {}
for p in string:gmatch(value, "[^:]") do
p = fs.canonical(text.trim(p))
if unicode.sub(p, 1, 1) ~= "/" then
p = "/" .. p
end
table.insert(path, p)
end
return result
end
function shell.resolve(path, ext)
@ -164,7 +164,7 @@ function shell.resolve(path, ext)
if unicode.sub(path, 1, 1) == "/" then
return fs.canonical(path)
else
return fs.concat(shell.cwd(), path)
return fs.concat(shell.getWorkingDirectory(), path)
end
end
end

View File

@ -78,161 +78,177 @@ function term.read(history)
checkArg(1, history, "table", "nil")
history = history or {}
table.insert(history, "")
local current = #history
local start = term.getCursor()
local cursor, scroll = 1, 0
local offsetX = term.getCursor() - 1
local scrollX, scrollY = 0, #history
local function remove()
local x = start - 1 + cursor - scroll
local function getCursor()
local cx, cy = term.getCursor()
return cx - offsetX + scrollX, scrollY
end
local function setCursor(cbx, cby)
local w = component.gpu.getResolution()
component.gpu.copy(x + 1, cursorY, w - x, 1, -1, 0)
local cursor = cursor + (w - x)
local char = unicode.sub(history[current], cursor, cursor)
if unicode.len(char) == 0 then
char = " "
local cx, cy = term.getCursor()
local ncx, ncy = cbx + offsetX - scrollX, cy
-- TODO generalize with y scrolling and make it a helper in the text lib?
scrollY = cby or scrollY
if ncx > w then
local sx = cbx - (w - offsetX)
local dx = math.abs(scrollX - sx)
scrollX = sx
component.gpu.copy(1 + offsetX + dx, cy, w - offsetX - dx, 1, -dx, 0)
local str = unicode.sub(history[scrollY], cbx - (dx - 1), cbx)
str = text.pad(str, dx)
component.gpu.set(1 + (w - dx), cy, str)
end
component.gpu.set(w, cursorY, char)
end
local function render()
local w = component.gpu.getResolution()
local str = unicode.sub(history[current], 1 + scroll, 1 + scroll + w - (start - 1))
str = str .. string.rep(" ", (w - (start - 1)) - unicode.len(str))
component.gpu.set(start, cursorY, str)
end
local function scrollEnd()
local w = component.gpu.getResolution()
cursor = unicode.len(history[current]) + 1
scroll = math.max(0, cursor - (w - (start - 1)))
render()
end
local function scrollLeft()
scroll = scroll - 1
local w = component.gpu.getResolution()
component.gpu.copy(start, cursorY, w - start - 1, 1, 1, 0)
local cursor = w - (start - 1) + scroll
local char = unicode.sub(history[current], cursor, cursor)
if unicode.len(char) == 0 then
char = " "
if ncx < 1 + offsetX then
local sx = cbx - 1
local dx = math.abs(scrollX - sx)
scrollX = sx
component.gpu.copy(1 + offsetX, cy, w - offsetX - dx, 1, dx, 0)
local str = unicode.sub(history[scrollY], cbx, cbx + dx)
str = text.pad(str, dx)
component.gpu.set(1 + offsetX, cy, str)
end
component.gpu.set(1, cursorY, char)
term.setCursor(cbx - scrollX + offsetX, ncy)
end
local function scrollRight()
scroll = scroll + 1
local function redraw()
local cx, cy = term.getCursor()
local bx, by = 1 + scrollX, scrollY
local w = component.gpu.getResolution()
component.gpu.copy(start + 1, cursorY, w - start, 1, -1, 0)
local cursor = w - (start - 1) + scroll
local char = unicode.sub(history[current], cursor, cursor)
if unicode.len(char) == 0 then
char = " "
end
component.gpu.set(w, cursorY, char)
local l = w - offsetX
local str = unicode.sub(history[by], bx, bx + l)
str = text.pad(str, l)
component.gpu.set(1 + offsetX, cy, str)
end
local function update()
local w = component.gpu.getResolution()
local cursor = cursor - 1
local x = start - 1 + cursor - scroll
if cursor < unicode.len(history[current]) then
component.gpu.copy(x, cursorY, w - x, 1, 1, 0)
local function home()
local cbx, cby = getCursor()
setCursor(1, cby)
end
local function ende()
local cbx, cby = getCursor()
setCursor(unicode.len(history[cby]) + 1, cby)
end
local function enter()
local cbx, cby = getCursor()
if cby ~= #history then -- bring entry to front
history[#history] = history[cby]
table.remove(history, cby)
end
end
local function left()
local cbx, cby = getCursor()
if cbx > 1 then
setCursor(cbx - 1, cby)
return true -- for backspace
end
end
local function right(n)
n = n or 1
local cbx, cby = getCursor()
local be = unicode.len(history[cby]) + 1
if cbx < be then
setCursor(math.min(be, cbx + n), cby)
end
end
local function up()
local cbx, cby = getCursor()
if cby > 1 then
setCursor(1, cby - 1)
redraw()
ende()
end
end
local function down()
local cbx, cby = getCursor()
if cby < #history then
setCursor(1, cby + 1)
redraw()
ende()
end
component.gpu.set(x, cursorY, unicode.sub(history[current], cursor, cursor))
end
local function copyIfNecessary()
if current ~= #history then
history[#history] = history[current]
current = #history
local cbx, cby = getCursor()
if cby ~= #history then
history[#history] = history[cby]
setCursor(cbx, #history)
end
end
local function updateCursor()
cursorX = start - 1 + cursor - scroll
if not term.getCursorBlink() then
term.setCursorBlink(true)
local function delete()
copyIfNecessary()
local cbx, cby = getCursor()
if cbx <= unicode.len(history[cby]) then
history[cby] = unicode.sub(history[cby], 1, cbx - 1) ..
unicode.sub(history[cby], cbx + 1)
local cx, cy = term.getCursor()
local w = component.gpu.getResolution()
component.gpu.copy(cx + 1, cy, w - cx, 1, -1, 0)
local br = cbx + (w - cx)
local char = unicode.sub(history[cby], br, br)
if not char or unicode.len(char) == 0 then
char = " "
end
component.gpu.set(w, cy, char)
end
end
local function insert(value)
copyIfNecessary()
local cx, cy = term.getCursor()
local cbx, cby = getCursor()
local w = component.gpu.getResolution()
history[cby] = unicode.sub(history[cby], 1, cbx - 1) ..
value ..
unicode.sub(history[cby], cbx)
local len = unicode.len(value)
local scroll = w - cx - len
if scroll > 0 then
component.gpu.copy(cx, cy, scroll, 1, len, 0)
end
component.gpu.set(cx, cy, value)
right(len)
end
local function onKeyDown(char, code)
local w = component.gpu.getResolution()
local blink = false
term.setCursorBlink(false)
if code == keyboard.keys.back then
if cursor > 1 then
term.setCursorBlink(false)
copyIfNecessary()
history[#history] = unicode.sub(history[#history], 1, cursor - 2) ..
unicode.sub(history[#history], cursor)
cursor = cursor - 1
if cursor - scroll < 1 then
scrollLeft()
end
remove()
end
if left() then delete() end
elseif code == keyboard.keys.delete then
if cursor <= unicode.len(history[current]) then
term.setCursorBlink(false)
copyIfNecessary()
history[#history] = unicode.sub(history[#history], 1, cursor - 1) ..
unicode.sub(history[#history], cursor + 1)
remove()
end
delete()
elseif code == keyboard.keys.left then
if cursor > 1 then
term.setCursorBlink(false)
blink = true
cursor = cursor - 1
if cursor - scroll < 1 then
scrollLeft()
end
end
left()
elseif code == keyboard.keys.right then
if cursor < unicode.len(history[current]) + 1 then
term.setCursorBlink(false)
blink = true
cursor = cursor + 1
if cursor - scroll > w - (start - 1) then
scrollRight()
end
end
right()
elseif code == keyboard.keys.home then
if cursor > 1 then
term.setCursorBlink(false)
blink = true
cursor, scroll = 1, 0
render()
end
home()
elseif code == keyboard.keys["end"] then
if cursor < unicode.len(history[current]) + 1 then
term.setCursorBlink(false)
blink = true
scrollEnd()
end
ende()
elseif code == keyboard.keys.up then
if current > 1 then
term.setCursorBlink(false)
blink = true
current = current - 1
scrollEnd()
end
up()
elseif code == keyboard.keys.down then
if current < #history then
term.setCursorBlink(false)
blink = true
current = current + 1
scrollEnd()
end
down()
elseif code == keyboard.keys.enter then
if current ~= #history then -- bring entry to front
history[#history] = history[current]
table.remove(history, current)
end
enter()
return true, history[#history] .. "\n"
elseif keyboard.isControlDown() then
local cbx, cby = getCursor()
if code == keyboard.keys.d then
if history[current] == "" then
if history[cby] == "" then
history[#history] = ""
return true, nil
end
@ -241,35 +257,26 @@ function term.read(history)
return true, nil
end
elseif not keyboard.isControl(char) then
term.setCursorBlink(false)
copyIfNecessary()
history[#history] = unicode.sub(history[#history], 1, cursor - 1) ..
unicode.char(char) ..
unicode.sub(history[#history], cursor)
cursor = cursor + 1
update()
if cursor - scroll > w - (start - 1) then
scrollRight()
end
end
updateCursor()
if blink then -- immediately show cursor
term.setCursorBlink(true)
insert(unicode.char(char))
end
term.setCursorBlink(true)
term.setCursorBlink(true) -- force toggle to caret
end
local function onClipboard(value)
copyIfNecessary()
term.setCursorBlink(false)
local cbx, cby = getCursor()
local l = value:find("\n", 1, true)
if l then
history[#history] = history[#history] .. unicode.sub(value, 1, l - 1)
render()
return true, history[#history] .. "\n"
history[cby] = unicode.sub(history[cby], 1, cbx - 1)
redraw()
insert(unicode.sub(value, 1, l - 1))
return true, history[cby] .. "\n"
else
history[#history] = history[#history] .. value
scrollEnd()
updateCursor()
insert(value)
term.setCursorBlink(true)
term.setCursorBlink(true) -- force toggle to caret
end
end
@ -283,27 +290,24 @@ function term.read(history)
term.setCursorBlink(true)
while term.isAvailable() do
local ok, event, address, charOrValue, code = pcall(event.pull)
local ok, name, address, charOrValue, code = pcall(event.pull)
if not ok then
cleanup()
error("interrupted", 0)
end
if term.isAvailable() and
if term.isAvailable() and -- may have changed since pull
type(address) == "string" and
component.isPrimary(address)
then
if event == "key_down" then
local done, result = onKeyDown(charOrValue, code)
if done then
cleanup()
return result
end
elseif event == "clipboard" then
local done, result = onClipboard(charOrValue)
if done then
cleanup()
return result
end
local done, result
if name == "key_down" then
done, result = onKeyDown(charOrValue, code)
elseif name == "clipboard" then
done, result = onClipboard(charOrValue)
end
if done then
cleanup()
return result
end
end
end
@ -321,6 +325,9 @@ function term.write(value, wrap)
end
value = text.detab(value)
local w, h = component.gpu.getResolution()
if not w then
return -- gpu lost its screen but the signal wasn't processed yet.
end
local blink = term.getCursorBlink()
term.setCursorBlink(false)
local function checkCursor()

View File

@ -11,4 +11,19 @@ function text.detab(value, tabWidth)
return value:gsub("([^\n]-)\t", rep)
end
function text.pad(value, length)
checkArg(1, value, "string", "nil")
checkArg(2, length, "number")
if not value or unicode.len(value) == 0 then
return string.rep(" ", length)
else
return value .. string.rep(" ", length - unicode.len(value))
end
end
function text.trim(value) -- from http://lua-users.org/wiki/StringTrim
local from = string.match(value, "^%s*()")
return from > #value and "" or string.match(value, ".*%S", from)
end
_G.text = text