diff --git a/sys/apis/injector.lua b/sys/apis/injector.lua index 7877a4c..17e27d5 100644 --- a/sys/apis/injector.lua +++ b/sys/apis/injector.lua @@ -23,8 +23,6 @@ table.insert(luaPaths, 5, '/sys/apis/?.lua') table.insert(luaPaths, 6, '/sys/apis/?/init.lua') local DEFAULT_PATH = table.concat(luaPaths, ';') -local DEFAULT_BRANCH = _ENV.OPUS_BRANCH or _G.OPUS_BRANCH or 'develop-1.8' -local DEFAULT_UPATH = GIT_URL .. '/kepler155c/opus/' .. DEFAULT_BRANCH .. '/sys/apis' local fs = _G.fs local http = _G.http @@ -32,7 +30,7 @@ local os = _G.os local string = _G.string if not http._patched then - -- fix broken http get + -- fix broken http get (http.get is not coroutine safe) local syncLocks = { } local function sync(obj, fn) @@ -110,6 +108,12 @@ return function(env) end if fs.exists(sPath) and not fs.isDir(sPath) then return loadfile(sPath, env) + elseif sPath:match("^(https?:)") then + print('loading ' .. sPath) + local c = loadUrl(sPath) + if c then + return load(c, modname, nil, env) + end end end end @@ -138,24 +142,9 @@ return function(env) end end - local function urlSearcher(modname) - local fname = modname:gsub('%.', '/') .. '.lua' - - if fname:sub(1, 1) ~= '/' then - for entry in string.gmatch(env.package.upath, "[^;]+") do - local url = entry .. '/' .. fname - local c = loadUrl(url) - if c then - return load(c, modname, nil, env) - end - end - end - end - -- place package and require function into env env.package = { path = env.LUA_PATH or _G.LUA_PATH or DEFAULT_PATH, - upath = env.LUA_UPATH or _G.LUA_UPATH or DEFAULT_UPATH, config = '/\n:\n?\n!\n-', preload = { }, loaded = { @@ -172,7 +161,6 @@ return function(env) pathSearcher, pastebinSearcher, gitSearcher, - urlSearcher, } } diff --git a/sys/apis/terminal.lua b/sys/apis/terminal.lua index fc9f49f..5d2ccdd 100644 --- a/sys/apis/terminal.lua +++ b/sys/apis/terminal.lua @@ -28,6 +28,8 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) }) canvas.offy = 0 + win.canvas = canvas + local function update() if isVisible then canvas:render(parent) @@ -98,6 +100,16 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) end win.setTextColour = win.setTextColor + function win.getPaletteColor(n) + return parent.getPaletteColor(n) + end + win.getPaletteColour = win.getPaletteColor + + function win.setPaletteColor(n, r, g, b) + return parent.setPaletteColor(n, r, g, b) + end + win.setPaletteColour = win.setPaletteColor + function win.setBackgroundColor(c) bg = c end diff --git a/sys/apis/trace.lua b/sys/apis/trace.lua new file mode 100644 index 0000000..78cc535 --- /dev/null +++ b/sys/apis/trace.lua @@ -0,0 +1,97 @@ +-- stack trace by SquidDev (MIT License) +-- https://raw.githubusercontent.com/SquidDev-CC/mbs/master/lib/stack_trace.lua + +local type = type +local debug_traceback = type(debug) == "table" and type(debug.traceback) == "function" and debug.traceback + +local function traceback(x) + -- Attempt to detect error() and error("xyz", 0). + -- This probably means they're erroring the program intentionally and so we + -- shouldn't display anything. + if x == nil or (type(x) == "string" and not x:find(":%d+:")) then + return x + end + + if debug_traceback then + -- The parens are important, as they prevent a tail call occuring, meaning + -- the stack level is preserved. This ensures the code behaves identically + -- on LuaJ and PUC Lua. + return (debug_traceback(tostring(x), 2)) + else + local level = 3 + local out = { tostring(x), "stack traceback:" } + while true do + local _, msg = pcall(error, "", level) + if msg == "" then break end + + out[#out + 1] = " " .. msg + level = level + 1 + end + + return table.concat(out, "\n") + end +end + +local function trim_traceback(target, marker) + local ttarget, tmarker = {}, {} + for line in target:gmatch("([^\n]*)\n?") do ttarget[#ttarget + 1] = line end + for line in marker:gmatch("([^\n]*)\n?") do tmarker[#tmarker + 1] = line end + + -- Trim identical suffixes + local t_len, m_len = #ttarget, #tmarker + while t_len >= 3 and ttarget[t_len] == tmarker[m_len] do + table.remove(ttarget, t_len) + t_len, m_len = t_len - 1, m_len - 1 + end + + -- Trim elements from this file and xpcall invocations + while t_len >= 1 and ttarget[t_len]:find("^\tstack_trace%.lua:%d+:") or + ttarget[t_len] == "\t[C]: in function 'xpcall'" or ttarget[t_len] == " xpcall: " do + table.remove(ttarget, t_len) + t_len = t_len - 1 + end + + return ttarget +end + +--- Run a function with +return function (fn, ...) + -- So this is rather grim: we need to get the full traceback and current one and remove + -- the common prefix + local trace + local args = { ... } + + -- xpcall in Lua 5.1 does not accept parameters + -- which is not ideal + local res = table.pack(xpcall(function() + return fn(table.unpack(args)) + end, traceback)) + + if not res[1] then + trace = traceback("trace.lua:1:") + end + local ok, err = res[1], res[2] + + if not ok and err ~= nil then + trace = trim_traceback(err, trace) + + -- Find the position where the stack traceback actually starts + local trace_starts + for i = #trace, 1, -1 do + if trace[i] == "stack traceback:" then trace_starts = i; break end + end + + -- If this traceback is more than 15 elements long, keep the first 9, last 5 + -- and put an ellipsis between the rest + local max = 12 + if trace_starts and #trace - trace_starts > max then + local keep_starts = trace_starts + 9 + for i = #trace - trace_starts - max, 0, -1 do table.remove(trace, keep_starts + i) end + table.insert(trace, keep_starts, " ...") + end + + return false, table.concat(trace, "\n") + end + + return table.unpack(res, 1, res.n) +end diff --git a/sys/apis/ui/canvas.lua b/sys/apis/ui/canvas.lua index 931e9bd..3fbc15d 100644 --- a/sys/apis/ui/canvas.lua +++ b/sys/apis/ui/canvas.lua @@ -246,17 +246,17 @@ function Canvas:blitClipped(device, offset) end function Canvas:redraw(device) ---[[ +-- self:dirty() +-- self:render(device) if #self.layers > 0 then for _,layer in pairs(self.layers) do self:punch(layer) end self:blitClipped(device) else - self:blit(device) + self:renderLayers(device) end self:clean() -]] end function Canvas:isDirty() @@ -384,61 +384,4 @@ function Canvas:applyPalette(palette) self.palette = palette end -function Canvas.convertWindow(win, parent, wx, wy) - local w, h = win.getSize() - - win.canvas = Canvas({ - x = wx, - y = wy, - width = w, - height = h, - isColor = win.isColor(), - }) - - function win.clear() - win.canvas:clear(win.getBackgroundColor(), win.getTextColor()) - end - - function win.clearLine() - local _, y = win.getCursorPos() - win.canvas:write(1, - y, - _rep(' ', win.canvas.width), - win.getBackgroundColor(), - win.getTextColor()) - end - - function win.write(str) - local x, y = win.getCursorPos() - win.canvas:write(x, - y, - str, - win.getBackgroundColor(), - win.getTextColor()) - win.setCursorPos(x + #str, y) - end - - function win.blit(text, fg, bg) - local x, y = win.getCursorPos() - win.canvas:blit(x, y, text, bg, fg) - end - - function win.redraw() - win.canvas:redraw(parent) - end - - function win.scroll(n) - table.insert(win.canvas.lines, table.remove(win.canvas.lines, 1)) - win.canvas.lines[#win.canvas.lines].text = _rep(' ', win.canvas.width) - win.canvas:dirty() - end - - function win.reposition(x, y, width, height) - win.canvas.x, win.canvas.y = x, y - win.canvas:resize(width or win.canvas.width, height or win.canvas.height) - end - - win.clear() -end - return Canvas diff --git a/sys/apps/cedit.lua b/sys/apps/cedit.lua index d1c1295..8032688 100644 --- a/sys/apps/cedit.lua +++ b/sys/apps/cedit.lua @@ -8,6 +8,10 @@ if not args[1] then error('Syntax: cedit ') end +if not _G.http.websocket then + error('Requires CC: Tweaked') +end + if not _G.cloud_catcher then print('Paste key: ') local key = read() diff --git a/sys/apps/cshell.lua b/sys/apps/cshell.lua index 9d54e84..7bc6af6 100644 --- a/sys/apps/cshell.lua +++ b/sys/apps/cshell.lua @@ -1,5 +1,9 @@ -local read = _G.read -local shell = _ENV.shell +local read = _G.read +local shell = _ENV.shell + +if not _G.http.websocket then + error('Requires CC: Tweaked') +end if not _G.cloud_catcher then print('Paste key: ') diff --git a/sys/apps/package.lua b/sys/apps/package.lua index b377134..45016cd 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -50,21 +50,13 @@ local function install(name, isUpdate) name)) local packageDir = fs.combine('packages', name) - local method = args[2] or 'local' - if method == 'remote' then - Util.writeTable(packageDir .. '/.install', { - mount = string.format('%s gitfs %s', packageDir, manifest.repository), - }) - Util.writeTable(fs.combine(packageDir, '.package'), manifest) - else - local list = Git.list(manifest.repository) - local showProgress = progress(Util.size(list)) - for path, entry in pairs(list) do - Util.download(entry.url, fs.combine(packageDir, path)) - showProgress() - end + + local list = Git.list(manifest.repository) + local showProgress = progress(Util.size(list)) + for path, entry in pairs(list) do + Util.download(entry.url, fs.combine(packageDir, path)) + showProgress() end - return end if action == 'list' then diff --git a/sys/apps/shell b/sys/apps/shell index 3877073..d9da2e2 100644 --- a/sys/apps/shell +++ b/sys/apps/shell @@ -65,7 +65,7 @@ local function run(env, ...) end if isUrl then - tProgramStack[#tProgramStack + 1] = path:match("^https?://([^/:]+:?[0-9]*/?.*)$") + tProgramStack[#tProgramStack + 1] = path -- path:match("^https?://([^/:]+:?[0-9]*/?.*)$") else tProgramStack[#tProgramStack + 1] = path end diff --git a/sys/apps/system/path.lua b/sys/apps/system/path.lua index f2a7c98..7e3abff 100644 --- a/sys/apps/system/path.lua +++ b/sys/apps/system/path.lua @@ -2,47 +2,102 @@ local Config = require('config') local UI = require('ui') local Util = require('util') -local pathTab = UI.Tab { +local tab = UI.Tab { tabTitle = 'Path', description = 'Set the shell path', tabClose = true, entry = UI.TextEntry { x = 2, y = 2, ex = -2, limit = 256, - value = Config.load('shell').path, - shadowText = 'enter system path', + shadowText = 'enter new path', accelerators = { enter = 'update_path', }, + help = 'add a new path', }, grid = UI.Grid { - y = 4, + y = 4, ey = -3, disableHeader = true, columns = { { key = 'value' } }, autospace = true, + sort = 'index', + help = 'double-click to remove, shift-arrow to move', + accelerators = { + delete = 'remove', + }, }, + statusBar = UI.StatusBar { }, + accelerators = { + [ 'shift-up' ] = 'move_up', + [ 'shift-down' ] = 'move_down', + }, } -function pathTab.grid:draw() - self.values = { } - local env = Config.load('shell') - for _,v in ipairs(Util.split(env.path, '(.-):')) do - table.insert(self.values, { value = v }) - end - self:update() - UI.Grid.draw(self) +function tab:updateList(path) + self.grid.values = { } + for k,v in ipairs(Util.split(path, '(.-):')) do + table.insert(self.grid.values, { index = k, value = v }) + end + self.grid:update() end -function pathTab:eventHandler(event) - if event.type == 'update_path' then - local env = Config.load('shell') - env.path = self.entry.value - Config.update('shell', env) - self.grid:setIndex(self.grid:getIndex()) - self.grid:draw() - self:emit({ type = 'success_message', message = 'reboot to take effect' }) +function tab:enable() + local env = Config.load('shell') + self:updateList(env.path) + UI.Tab.enable(self) +end + +function tab:save() + local t = { } + for _, v in ipairs(self.grid.values) do + table.insert(t, v.value) + end + local env = Config.load('shell') + env.path = table.concat(t, ':') + self:updateList(env.path) + Config.update('shell', env) +end + +function tab:eventHandler(event) + if event.type == 'update_path' then + table.insert(self.grid.values, { + value = self.entry.value, + }) + self:save() + self.entry:reset() + self.entry:draw() + self.grid:draw() return true - end + + elseif event.type == 'grid_select' or event.type == 'remove' then + local selected = self.grid:getSelected() + if selected then + table.remove(self.grid.values, selected.index) + self:save() + self.grid:draw() + end + + elseif event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + + elseif event.type == 'move_up' then + local entry = self.grid:getSelected() + if entry.index > 1 then + table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index - 1) + self:save() + self.grid:draw() + end + + elseif event.type == 'move_down' then + local entry = self.grid:getSelected() + if entry.index < #self.grid.values then + table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index + 1) + self:save() + self.grid:draw() + end + end end -return pathTab +return tab diff --git a/sys/apps/system/requires.lua b/sys/apps/system/requires.lua new file mode 100644 index 0000000..46eaed8 --- /dev/null +++ b/sys/apps/system/requires.lua @@ -0,0 +1,103 @@ +local Config = require('config') +local UI = require('ui') +local Util = require('util') + +local tab = UI.Tab { + tabTitle = 'Requires', + description = 'Require path', + tabClose = true, + entry = UI.TextEntry { + x = 2, y = 2, ex = -2, + limit = 256, + shadowText = 'Enter new require path', + accelerators = { + enter = 'update_path', + }, + help = 'add a new path', + }, + grid = UI.Grid { + y = 4, ey = -3, + disableHeader = true, + columns = { { key = 'value' } }, + autospace = true, + sort = 'index', + help = 'double-click to remove, shift-arrow to move', + accelerators = { + delete = 'remove', + }, + }, + statusBar = UI.StatusBar { }, + accelerators = { + [ 'shift-up' ] = 'move_up', + [ 'shift-down' ] = 'move_down', + }, +} + +function tab:updateList(lua_path) + self.grid.values = { } + for k,v in ipairs(Util.split(lua_path, '(.-);')) do + table.insert(self.grid.values, { index = k, value = v }) + end + self.grid:update() +end + +function tab:enable() + local env = Config.load('shell') + self:updateList(env.lua_path) + UI.Tab.enable(self) +end + +function tab:save() + local t = { } + for _, v in ipairs(self.grid.values) do + table.insert(t, v.value) + end + local env = Config.load('shell') + env.lua_path = table.concat(t, ';') + self:updateList(env.lua_path) + Config.update('shell', env) +end + +function tab:eventHandler(event) + if event.type == 'update_path' then + table.insert(self.grid.values, { + value = self.entry.value, + }) + self:save() + self.entry:reset() + self.entry:draw() + self.grid:draw() + return true + + elseif event.type == 'grid_select' or event.type == 'remove' then + local selected = self.grid:getSelected() + if selected then + table.remove(self.grid.values, selected.index) + self:save() + self.grid:draw() + end + + elseif event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + + elseif event.type == 'move_up' then + local entry = self.grid:getSelected() + if entry.index > 1 then + table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index - 1) + self:save() + self.grid:draw() + end + + elseif event.type == 'move_down' then + local entry = self.grid:getSelected() + if entry.index < #self.grid.values then + table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index + 1) + self:save() + self.grid:draw() + end + end +end + +return tab diff --git a/sys/boot/opus.boot b/sys/boot/opus.boot index 8c5c413..2f64980 100644 --- a/sys/boot/opus.boot +++ b/sys/boot/opus.boot @@ -48,7 +48,16 @@ if fs.exists('sys/apis/injector.lua') then _G.requireInjector = run('sys/apis/injector.lua') else -- not local, run the file system directly from git + if package and package.path then + package.path = package.path .. ';' .. BASE .. '/sys/apis' + else + sandboxEnv.package = { + path = BASE .. '/sys/apis' + } + end + _G.requireInjector = runUrl('sys/apis/injector.lua') + runUrl('sys/extensions/2.vfs.lua') -- install file system diff --git a/sys/extensions/4.user.lua b/sys/extensions/4.user.lua index 722ec9a..49f8583 100644 --- a/sys/extensions/4.user.lua +++ b/sys/extensions/4.user.lua @@ -10,11 +10,9 @@ if not fs.exists('usr/autorun') then fs.makeDir('usr/autorun') end -local lua_path = package.path - -- TODO: Temporary local upgrade = Util.readTable('usr/config/shell') -if upgrade and not upgrade.upgraded then +if upgrade and not upgrade.upgraded or upgrade.upgraded ~= 1 then fs.delete('usr/config/shell') end @@ -22,8 +20,8 @@ if not fs.exists('usr/config/shell') then Util.writeTable('usr/config/shell', { aliases = shell.aliases(), path = 'usr/apps', - lua_path = lua_path, - upgraded = true, + lua_path = package.path, + upgraded = 1, }) end @@ -44,8 +42,7 @@ for _, v in pairs(Util.split(shell.path(), '(.-):')) do end shell.setPath(table.concat(path, ':')) --- TODO: replace when stable (old lua path is now incorrect) --- _G.LUA_PATH = config.lua_path -_G.LUA_PATH = package.path + +_G.LUA_PATH = config.lua_path fs.loadTab('usr/config/fstab') diff --git a/sys/extensions/6.packages.lua b/sys/extensions/6.packages.lua index 9510e35..b8f6340 100644 --- a/sys/extensions/6.packages.lua +++ b/sys/extensions/6.packages.lua @@ -10,30 +10,14 @@ if not fs.exists('usr/config/packages') then end local appPaths = Util.split(shell.path(), '(.-):') -local luaPaths = Util.split(_G.LUA_PATH, '(.-);') local helpPaths = Util.split(help.path(), '(.-):') table.insert(helpPaths, '/sys/help') -local function addEntry(t, e, n) - for _,v in ipairs(t) do - if v == e then - return true - end - end - table.insert(t, n or 1, e) -end - for name in pairs(Packages:installed()) do local packageDir = fs.combine('packages', name) - if fs.exists(fs.combine(packageDir, '.install')) then - local install = Util.readTable(fs.combine(packageDir, '.install')) - if install and install.mount then - fs.mount(table.unpack(Util.matches(install.mount))) - end - end - addEntry(appPaths, packageDir) + table.insert(appPaths, 1, packageDir) local apiPath = fs.combine(fs.combine('packages', name), 'apis') if fs.exists(apiPath) then fs.mount(fs.combine('sys/apis', name), 'linkfs', apiPath) @@ -47,4 +31,3 @@ end help.setPath(table.concat(helpPaths, ':')) shell.setPath(table.concat(appPaths, ':')) -_G.LUA_PATH = table.concat(luaPaths, ';') diff --git a/sys/extensions/7.multishell.lua b/sys/extensions/7.multishell.lua index 4da07e8..40129fc 100644 --- a/sys/extensions/7.multishell.lua +++ b/sys/extensions/7.multishell.lua @@ -2,6 +2,7 @@ _G.requireInjector(_ENV) local Config = require('config') local Packages = require('packages') +local trace = require('trace') local Util = require('util') local colors = _G.colors @@ -104,6 +105,15 @@ function multishell.launch( tProgramEnv, sProgramPath, ... ) }) end +local function xprun(env, path, ...) + setmetatable(env, { __index = _G }) + local fn, m = loadfile(path, env) + if fn then + return trace(fn, ...) + end + return fn, m +end + function multishell.openTab(tab) if not tab.title and tab.path then tab.title = fs.getName(tab.path):match('([^%.]+)') @@ -120,7 +130,7 @@ function multishell.openTab(tab) if tab.fn then result, err = Util.runFunction(routine.env, tab.fn, table.unpack(tab.args or { } )) elseif tab.path then - result, err = Util.run(routine.env, tab.path, table.unpack(tab.args or { } )) + result, err = xprun(routine.env, tab.path, table.unpack(tab.args or { } )) else err = 'multishell: invalid tab' end