From 374d66d39e9f391be3667e8e2ce44c1fa544fb6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sat, 23 Nov 2013 18:10:45 +0100 Subject: [PATCH] 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 --- assets/opencomputers/lua/kernel.lua | 8 +- assets/opencomputers/lua/rom/bin/alias.lua | 4 +- assets/opencomputers/lua/rom/bin/cd.lua | 2 +- assets/opencomputers/lua/rom/bin/pwd.lua | 2 +- assets/opencomputers/lua/rom/bin/sh.lua | 2 +- assets/opencomputers/lua/rom/bin/unalias.lua | 3 +- .../opencomputers/lua/rom/lib/component.lua | 2 +- .../opencomputers/lua/rom/lib/filesystem.lua | 11 +- assets/opencomputers/lua/rom/lib/os.lua | 2 +- assets/opencomputers/lua/rom/lib/shell.lua | 66 ++-- assets/opencomputers/lua/rom/lib/term.lua | 319 +++++++++--------- assets/opencomputers/lua/rom/lib/text.lua | 15 + 12 files changed, 227 insertions(+), 209 deletions(-) diff --git a/assets/opencomputers/lua/kernel.lua b/assets/opencomputers/lua/kernel.lua index f486db3b3..1c68d61b6 100644 --- a/assets/opencomputers/lua/kernel.lua +++ b/assets/opencomputers/lua/kernel.lua @@ -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 = { diff --git a/assets/opencomputers/lua/rom/bin/alias.lua b/assets/opencomputers/lua/rom/bin/alias.lua index 819c67dcb..2847f9802 100644 --- a/assets/opencomputers/lua/rom/bin/alias.lua +++ b/assets/opencomputers/lua/rom/bin/alias.lua @@ -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 diff --git a/assets/opencomputers/lua/rom/bin/cd.lua b/assets/opencomputers/lua/rom/bin/cd.lua index ad9a98326..e3bf3e0d9 100644 --- a/assets/opencomputers/lua/rom/bin/cd.lua +++ b/assets/opencomputers/lua/rom/bin/cd.lua @@ -2,7 +2,7 @@ local args = shell.parse(...) if #args == 0 then print("Usage: cd ") 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 diff --git a/assets/opencomputers/lua/rom/bin/pwd.lua b/assets/opencomputers/lua/rom/bin/pwd.lua index 8e3a4168e..7547c044d 100644 --- a/assets/opencomputers/lua/rom/bin/pwd.lua +++ b/assets/opencomputers/lua/rom/bin/pwd.lua @@ -1 +1 @@ -print(shell.cwd()) \ No newline at end of file +print(shell.getWorkingDirectory()) \ No newline at end of file diff --git a/assets/opencomputers/lua/rom/bin/sh.lua b/assets/opencomputers/lua/rom/bin/sh.lua index 889f7a4d1..9d341142d 100644 --- a/assets/opencomputers/lua/rom/bin/sh.lua +++ b/assets/opencomputers/lua/rom/bin/sh.lua @@ -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 diff --git a/assets/opencomputers/lua/rom/bin/unalias.lua b/assets/opencomputers/lua/rom/bin/unalias.lua index 596c31831..d826c306f 100644 --- a/assets/opencomputers/lua/rom/bin/unalias.lua +++ b/assets/opencomputers/lua/rom/bin/unalias.lua @@ -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 diff --git a/assets/opencomputers/lua/rom/lib/component.lua b/assets/opencomputers/lua/rom/lib/component.lua index dbc71092c..e7317e9af 100644 --- a/assets/opencomputers/lua/rom/lib/component.lua +++ b/assets/opencomputers/lua/rom/lib/component.lua @@ -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 diff --git a/assets/opencomputers/lua/rom/lib/filesystem.lua b/assets/opencomputers/lua/rom/lib/filesystem.lua index 485892405..324964d85 100644 --- a/assets/opencomputers/lua/rom/lib/filesystem.lua +++ b/assets/opencomputers/lua/rom/lib/filesystem.lua @@ -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 diff --git a/assets/opencomputers/lua/rom/lib/os.lua b/assets/opencomputers/lua/rom/lib/os.lua index bd251e352..1c7546ed5 100644 --- a/assets/opencomputers/lua/rom/lib/os.lua +++ b/assets/opencomputers/lua/rom/lib/os.lua @@ -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 = {} diff --git a/assets/opencomputers/lua/rom/lib/shell.lua b/assets/opencomputers/lua/rom/lib/shell.lua index 7e98625a4..3a8334b06 100644 --- a/assets/opencomputers/lua/rom/lib/shell.lua +++ b/assets/opencomputers/lua/rom/lib/shell.lua @@ -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 diff --git a/assets/opencomputers/lua/rom/lib/term.lua b/assets/opencomputers/lua/rom/lib/term.lua index 12baf629d..70caca049 100644 --- a/assets/opencomputers/lua/rom/lib/term.lua +++ b/assets/opencomputers/lua/rom/lib/term.lua @@ -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() diff --git a/assets/opencomputers/lua/rom/lib/text.lua b/assets/opencomputers/lua/rom/lib/text.lua index d93964998..04f35ba15 100644 --- a/assets/opencomputers/lua/rom/lib/text.lua +++ b/assets/opencomputers/lua/rom/lib/text.lua @@ -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 \ No newline at end of file