mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-04-12 00:37:09 +00:00
446 lines
No EOL
12 KiB
Lua
446 lines
No EOL
12 KiB
Lua
local term = require("term")
|
|
local timer = require("timer")
|
|
os.print = print
|
|
|
|
package.path = "/lib/?.lua;/lib/?/init.lua;" .. package.path
|
|
|
|
local event = require("event")
|
|
local colors = require("colors")
|
|
local expect = require("expect").expect
|
|
|
|
function timer.sleep(n)
|
|
local timerId = timer.start(n)
|
|
repeat
|
|
local _, par = coroutine.yield("timer")
|
|
until par == timerId
|
|
end
|
|
|
|
function write(sText)
|
|
expect(1, sText, "string", "number")
|
|
|
|
local w, h = term.getSize()
|
|
local x, y = term.getPos()
|
|
|
|
local nLinesPrinted = 0
|
|
local function newLine()
|
|
if y + 1 <= h then
|
|
term.setPos(1, y + 1)
|
|
else
|
|
term.setPos(1, h)
|
|
term.scroll(1)
|
|
end
|
|
x, y = term.getPos()
|
|
nLinesPrinted = nLinesPrinted + 1
|
|
end
|
|
|
|
-- Print the line with proper word wrapping
|
|
sText = tostring(sText)
|
|
while #sText > 0 do
|
|
local whitespace = string.match(sText, "^[ \t]+")
|
|
if whitespace then
|
|
-- Print whitespace
|
|
term.write(whitespace)
|
|
x, y = term.getPos()
|
|
sText = string.sub(sText, #whitespace + 1)
|
|
end
|
|
|
|
local newline = string.match(sText, "^\n")
|
|
if newline then
|
|
-- Print newlines
|
|
newLine()
|
|
sText = string.sub(sText, 2)
|
|
end
|
|
|
|
local text = string.match(sText, "^[^ \t\n]+")
|
|
if text then
|
|
sText = string.sub(sText, #text + 1)
|
|
if #text > w then
|
|
-- Print a multiline word
|
|
while #text > 0 do
|
|
if x > w then
|
|
newLine()
|
|
end
|
|
term.write(text)
|
|
text = string.sub(text, w - x + 2)
|
|
x, y = term.getPos()
|
|
end
|
|
else
|
|
-- Print a word normally
|
|
if x + #text - 1 > w then
|
|
newLine()
|
|
end
|
|
term.write(text)
|
|
x, y = term.getPos()
|
|
end
|
|
end
|
|
end
|
|
|
|
return nLinesPrinted
|
|
end
|
|
|
|
function print(...)
|
|
local nLinesPrinted = 0
|
|
local nLimit = select("#", ...)
|
|
for n = 1, nLimit do
|
|
local s = tostring(select(n, ...))
|
|
if n < nLimit then
|
|
s = s .. "\t"
|
|
end
|
|
nLinesPrinted = nLinesPrinted + write(s)
|
|
end
|
|
nLinesPrinted = nLinesPrinted + write("\n")
|
|
return nLinesPrinted
|
|
end
|
|
|
|
function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
|
expect(1, _sReplaceChar, "string", "nil")
|
|
expect(2, _tHistory, "table", "nil")
|
|
expect(3, _fnComplete, "function", "nil")
|
|
expect(4, _sDefault, "string", "nil")
|
|
|
|
term.setBlink(true)
|
|
|
|
local sLine
|
|
if type(_sDefault) == "string" then
|
|
sLine = _sDefault
|
|
else
|
|
sLine = ""
|
|
end
|
|
local nHistoryPos
|
|
local nPos, nScroll = #sLine, 0
|
|
if _sReplaceChar then
|
|
_sReplaceChar = string.sub(_sReplaceChar, 1, 1)
|
|
end
|
|
|
|
local tCompletions
|
|
local nCompletion
|
|
local function recomplete()
|
|
if _fnComplete and nPos == #sLine then
|
|
tCompletions = _fnComplete(sLine)
|
|
if tCompletions and #tCompletions > 0 then
|
|
nCompletion = 1
|
|
else
|
|
nCompletion = nil
|
|
end
|
|
else
|
|
tCompletions = nil
|
|
nCompletion = nil
|
|
end
|
|
end
|
|
|
|
local function uncomplete()
|
|
tCompletions = nil
|
|
nCompletion = nil
|
|
end
|
|
|
|
local w = term.getSize()
|
|
local sx = term.getPos()
|
|
|
|
local function redraw(_bClear)
|
|
local cursor_pos = nPos - nScroll
|
|
if sx + cursor_pos >= w then
|
|
-- We've moved beyond the RHS, ensure we're on the edge.
|
|
nScroll = sx + nPos - w
|
|
elseif cursor_pos < 0 then
|
|
-- We've moved beyond the LHS, ensure we're on the edge.
|
|
nScroll = nPos
|
|
end
|
|
|
|
local _, cy = term.getPos()
|
|
term.setPos(sx, cy)
|
|
local sReplace = _bClear and " " or _sReplaceChar
|
|
if sReplace then
|
|
term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0)))
|
|
else
|
|
term.write(string.sub(sLine, nScroll + 1))
|
|
end
|
|
|
|
if nCompletion then
|
|
local sCompletion = tCompletions[nCompletion]
|
|
local oldText, oldBg
|
|
if not _bClear then
|
|
oldText = term.getForeground()
|
|
oldBg = term.getBackground()
|
|
term.setForeground(colors.white)
|
|
term.setBackground(colors.gray)
|
|
end
|
|
if sReplace then
|
|
term.write(string.rep(sReplace, #sCompletion))
|
|
else
|
|
term.write(sCompletion)
|
|
end
|
|
if not _bClear then
|
|
term.setForeground(oldText)
|
|
term.setBackground(oldBg)
|
|
end
|
|
end
|
|
|
|
term.setPos(sx + nPos - nScroll, cy)
|
|
end
|
|
|
|
local function clear()
|
|
redraw(true)
|
|
end
|
|
|
|
recomplete()
|
|
redraw()
|
|
|
|
local function acceptCompletion()
|
|
if nCompletion then
|
|
-- Clear
|
|
clear()
|
|
|
|
-- Find the common prefix of all the other suggestions which start with the same letter as the current one
|
|
local sCompletion = tCompletions[nCompletion]
|
|
sLine = sLine .. sCompletion
|
|
nPos = #sLine
|
|
|
|
-- Redraw
|
|
recomplete()
|
|
redraw()
|
|
end
|
|
end
|
|
while true do
|
|
local sEvent, param, param1, param2 = event.pull()
|
|
if sEvent == "char" then
|
|
-- Typed key
|
|
clear()
|
|
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
|
nPos = nPos + 1
|
|
recomplete()
|
|
redraw()
|
|
|
|
elseif sEvent == "paste" then
|
|
-- Pasted text
|
|
clear()
|
|
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
|
nPos = nPos + #param
|
|
recomplete()
|
|
redraw()
|
|
|
|
elseif sEvent == "key_down" then
|
|
if param1 == "enter" then
|
|
-- Enter/Numpad Enter
|
|
if nCompletion then
|
|
clear()
|
|
uncomplete()
|
|
redraw()
|
|
end
|
|
break
|
|
|
|
elseif param1 == "left" then
|
|
-- Left
|
|
if nPos > 0 then
|
|
clear()
|
|
nPos = nPos - 1
|
|
recomplete()
|
|
redraw()
|
|
end
|
|
|
|
elseif param1 == "right" then
|
|
-- Right
|
|
if nPos < #sLine then
|
|
-- Move right
|
|
clear()
|
|
nPos = nPos + 1
|
|
recomplete()
|
|
redraw()
|
|
else
|
|
-- Accept autocomplete
|
|
acceptCompletion()
|
|
end
|
|
|
|
elseif param1 == "up" or param1 == "down" then
|
|
-- Up or down
|
|
if nCompletion then
|
|
-- Cycle completions
|
|
clear()
|
|
if param == "up" then
|
|
nCompletion = nCompletion - 1
|
|
if nCompletion < 1 then
|
|
nCompletion = #tCompletions
|
|
end
|
|
elseif param == "down" then
|
|
nCompletion = nCompletion + 1
|
|
if nCompletion > #tCompletions then
|
|
nCompletion = 1
|
|
end
|
|
end
|
|
redraw()
|
|
|
|
elseif _tHistory then
|
|
-- Cycle history
|
|
clear()
|
|
if param1 == "up" then
|
|
-- Up
|
|
if nHistoryPos == nil then
|
|
if #_tHistory > 0 then
|
|
nHistoryPos = #_tHistory
|
|
end
|
|
elseif nHistoryPos > 1 then
|
|
nHistoryPos = nHistoryPos - 1
|
|
end
|
|
else
|
|
-- Down
|
|
if nHistoryPos == #_tHistory then
|
|
nHistoryPos = nil
|
|
elseif nHistoryPos ~= nil then
|
|
nHistoryPos = nHistoryPos + 1
|
|
end
|
|
end
|
|
if nHistoryPos then
|
|
sLine = _tHistory[nHistoryPos]
|
|
nPos, nScroll = #sLine, 0
|
|
else
|
|
sLine = ""
|
|
nPos, nScroll = 0, 0
|
|
end
|
|
uncomplete()
|
|
redraw()
|
|
|
|
end
|
|
|
|
elseif param1 == "back" then
|
|
-- Backspace
|
|
if nPos > 0 then
|
|
clear()
|
|
sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1)
|
|
nPos = nPos - 1
|
|
if nScroll > 0 then nScroll = nScroll - 1 end
|
|
recomplete()
|
|
redraw()
|
|
end
|
|
|
|
elseif param1 == "home" then
|
|
-- Home
|
|
if nPos > 0 then
|
|
clear()
|
|
nPos = 0
|
|
recomplete()
|
|
redraw()
|
|
end
|
|
|
|
elseif param1 == "delete" then
|
|
-- Delete
|
|
if nPos < #sLine then
|
|
clear()
|
|
sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
|
|
recomplete()
|
|
redraw()
|
|
end
|
|
|
|
elseif param1 == "end" then
|
|
-- End
|
|
if nPos < #sLine then
|
|
clear()
|
|
nPos = #sLine
|
|
recomplete()
|
|
redraw()
|
|
end
|
|
|
|
elseif param1 == "tab" then
|
|
-- Tab (accept autocomplete)
|
|
acceptCompletion()
|
|
|
|
end
|
|
|
|
elseif sEvent == "mouse_down" or sEvent == "mouse_drag" and param == 1 then
|
|
local _, cy = term.getPos()
|
|
if param1 >= sx and param1 <= w and param2 == cy then
|
|
-- Ensure we don't scroll beyond the current line
|
|
nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine)
|
|
redraw()
|
|
end
|
|
|
|
elseif sEvent == "term_resize" then
|
|
-- Terminal resized
|
|
w = term.getSize()
|
|
redraw()
|
|
|
|
end
|
|
end
|
|
|
|
local _, cy = term.getPos()
|
|
term.setBlink(false)
|
|
term.setPos(w + 1, cy)
|
|
print()
|
|
|
|
return sLine
|
|
end
|
|
|
|
|
|
function printError(...)
|
|
local r, g, b = term.getForeground()
|
|
term.setForeground(colors.red)
|
|
print(...)
|
|
term.setForeground(r, g, b)
|
|
end
|
|
|
|
local tEnv = {
|
|
["_echo"] = function(...)
|
|
return ...
|
|
end,
|
|
p = function(t, sv)
|
|
for k, v in pairs(t) do
|
|
if sv then
|
|
print(k, v)
|
|
else
|
|
print(k, type(v))
|
|
end
|
|
end
|
|
end,
|
|
fs = require("fs"),
|
|
}
|
|
setmetatable(tEnv, { __index = _ENV })
|
|
|
|
local tCommandHistory = {}
|
|
|
|
term.setSize(51, 19)
|
|
|
|
term.setForeground(colors.yellow)
|
|
term.setBackground(colors.black)
|
|
term.clear()
|
|
term.setPos(1,1)
|
|
print("Lua prompt")
|
|
while true do
|
|
term.setForeground(colors.yellow)
|
|
write("> ")
|
|
term.setForeground(colors.white)
|
|
local s = read(nil, tCommandHistory)
|
|
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
|
|
table.insert(tCommandHistory, s)
|
|
end
|
|
|
|
local nForcePrint = 0
|
|
local func, e = load(s, "=lua", "t", tEnv)
|
|
local func2 = load("return _echo(" .. s .. ");", "=lua", "t", tEnv)
|
|
|
|
if not func then
|
|
if func2 then
|
|
func = func2
|
|
e = nil
|
|
nForcePrint = 1
|
|
end
|
|
else
|
|
if func2 then
|
|
func = func2
|
|
end
|
|
end
|
|
|
|
if func then
|
|
local tResults = table.pack(pcall(func))
|
|
if tResults[1] then
|
|
local n = 1
|
|
while n < tResults.n or n <= nForcePrint do
|
|
local value = tResults[n + 1]
|
|
print(tostring(value))
|
|
n = n + 1
|
|
end
|
|
else
|
|
printError(tResults[2])
|
|
end
|
|
else
|
|
printError(e)
|
|
end
|
|
|
|
end |