Lua debugger part 1

This commit is contained in:
kepler155c@gmail.com
2020-05-23 21:44:55 -06:00
parent 2c27787f27
commit cb58a553f5
11 changed files with 923 additions and 349 deletions

View File

@@ -335,92 +335,23 @@ local page = UI.Page {
return UI.SlideOut.eventHandler(self, event) return UI.SlideOut.eventHandler(self, event)
end, end,
}, },
quick_open = UI.SlideOut { quick_open = UI.QuickSelect {
filter_entry = UI.TextEntry { modal = true,
x = 2, y = 2, ex = -2, enable = function() end,
shadowText = 'File name',
accelerators = {
[ 'enter' ] = 'accept',
[ 'up' ] = 'grid_up',
[ 'down' ] = 'grid_down',
},
},
grid = UI.ScrollingGrid {
x = 2, y = 3, ex = -2, ey = -4,
disableHeader = true,
columns = {
{ key = 'name' },
{ key = 'dir', textColor = 'lightGray' },
},
accelerators = {
grid_select = 'accept',
},
},
cancel = UI.Button {
x = -9, y = -2,
text = 'Cancel',
event = 'slide_hide',
},
apply_filter = function(self, filter)
if filter then
filter = filter:lower()
self.grid.sortColumn = 'score'
for _,v in pairs(self.grid.values) do
v.score = -fuzzy(v.lname, filter)
end
else
self.grid.sortColumn = 'lname'
end
self.grid:update()
self.grid:setIndex(1)
end,
show = function(self) show = function(self)
local function recurse(dir) UI.QuickSelect.enable(self)
local files = fs.list(dir) self:focusFirst()
for _,f in ipairs(files) do self:draw()
local fullName = fs.combine(dir, f)
if fs.native.isDir(fullName) then -- skip virtual dirs
if f ~= '.git' then recurse(fullName) end
else
_insert(self.grid.values, {
name = f,
dir = dir,
lname = f:lower(),
fullName = fullName,
})
end
end
end
recurse('')
self:apply_filter()
self.filter_entry:reset()
UI.SlideOut.show(self)
self:addTransition('expandUp', { easing = 'outBounce', ticks = 12 }) self:addTransition('expandUp', { easing = 'outBounce', ticks = 12 })
end, end,
eventHandler = function(self, event) eventHandler = function(self, event)
if event.type == 'grid_up' then if event.type == 'select_cancel' then
self.grid:emit({ type = 'scroll_up' }) self:disable()
elseif event.type == 'select_file' then
elseif event.type == 'grid_down' then self:disable()
self.grid:emit({ type = 'scroll_down' }) actions.process('open', event.file)
elseif event.type == 'accept' then
local sel = self.grid:getSelected()
if sel then
actions.process('open', sel.fullName)
self:hide()
end
elseif event.type == 'text_change' then
self:apply_filter(event.text)
self.grid:draw()
else
return UI.SlideOut.eventHandler(self, event)
end end
return true return UI.QuickSelect.eventHandler(self, event)
end, end,
}, },
completions = UI.SlideOut { completions = UI.SlideOut {

6
debugger/.package Normal file
View File

@@ -0,0 +1,6 @@
{
title = 'Lua Debugger',
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/debugger',
description = [[Lua interactive debugger]],
license = 'MIT',
}

201
debugger/apis/init.lua Normal file
View File

@@ -0,0 +1,201 @@
--[[
some portions from https://github.com/slembcke/debugger.lua
]]
local fs = _G.fs
local dbg = { }
local function hookBreakpoint(info)
if dbg.breakpoints then
for _,v in pairs(dbg.breakpoints) do
if v.line == info.currentline and v.file == info.short_src then
return true
end
end
end
end
local function hookFunction(fn)
return function(info)
return info.func == fn
end
end
local function hookStep()
local co = coroutine.running()
return function()
return co == coroutine.running()
end
end
local function hookStepStacksize(n)
local co = coroutine.running()
local i = 2
while true do
local info = debug.getinfo(i)
if not info then
break
end
i = i + 1
end
return function()
if co == coroutine.running() then
if not debug.getinfo(i - n) then
return true
end
end
end
end
local function hookStepOut()
return hookStepStacksize(1)
end
local function hookStepOver()
return hookStepStacksize(0)
end
local hookEval = function() end
-- Create a table of all the locally accessible variables.
-- Globals are not included when running the locals command
local function local_bindings(offset, stack_inspect_offset)
offset = offset + 1 + stack_inspect_offset -- add this function to the offset
local func = debug.getinfo(offset).func
local bindings = {}
-- Retrieve the upvalues
do local i = 1; while true do
local name, value = debug.getupvalue(func, i)
if not name then break end
bindings[name] = value
i = i + 1
end end
-- Retrieve the locals (overwriting any upvalues)
do local i = 1; while true do
local name, value = debug.getlocal(offset, i)
if not name then break end
bindings[name] = value
i = i + 1
end end
-- Retrieve the varargs (works in Lua 5.2 and LuaJIT)
local varargs = {}
do local i = 1; while true do
local name, value = debug.getlocal(offset, -i)
if not name then break end
varargs[i] = value
i = i + 1
end end
if #varargs > 0 then bindings["..."] = varargs end
return bindings
end
local function get_trace(offset, stack_inspect_offset)
local function format_loc(file, line) return file..":"..line end
local function format_stack_frame_info(info)
local filename = info.source:match("@(.*)")
local source = filename and fs.getName(filename) or info.short_src
local namewhat = (info.namewhat == "" and "chunk at" or info.namewhat)
local name = (info.name and "'"..info.name.."'" or format_loc(source, info.linedefined))
return format_loc(source, info.currentline).." in "..namewhat.." "..name
end
offset = offset + 1 -- add this function to the offset
local t = { }
local i = 0
while true do
local info = debug.getinfo(offset + i)
if not info then break end
t[i] = {
index = i,
current = (i == stack_inspect_offset),
desc = format_stack_frame_info(info),
info = info,
}
i = i + 1
end
return t
end
local inHook = false
local function hook()
local info = debug.getinfo(2)
if info.currentline < 0 then
return
end
if not inHook and hookEval(info) then
inHook = true
local offset = 2 -- the offset from this function to the code being debugged
local inspectOffset = 0
repeat
local done = true
local snapshot = {
info = debug.getinfo(offset + inspectOffset),
locals = local_bindings(offset, inspectOffset),
stack = get_trace(offset, inspectOffset),
}
inspectOffset = 0 -- reset
local cmd, param = dbg.read(snapshot)
if cmd == 's' then
hookEval = hookStep()
elseif cmd == 'n' then
hookEval = hookStepOver()
elseif cmd == 'f' then
hookEval = hookStepOut()
elseif cmd == 'c' then
hookEval = hookBreakpoint
elseif cmd == 'd' then -- detach
debug.sethook()
elseif cmd == 'q' then
os.exit(0)
elseif cmd == 'b' then
dbg.breakpoints = param
done = false
elseif cmd == 'i' then
-- inspect stack at this offset
inspectOffset = param
done = false
end
until done
inHook = false
end
end
debug.sethook(hook, 'l')
-- Expose the debugger's functions
dbg.hook = hook
dbg.exit = function(err) os.exit(err) end
dbg.stopIn = function(fn)
hookEval = hookFunction(fn)
end
dbg.debugger = nil
dbg.read = function(info)
_G._pinfo = info
os.sleep(0) -- this is important ...
dbg.debugger:resume('debugger', 'info', info)
while true do
local _, cmd, args = os.pullEvent('debugger')
if cmd == 'b' then
dbg.breakpoints = args
else
return cmd, args
end
end
end
return dbg

407
debugger/debug.lua Normal file
View File

@@ -0,0 +1,407 @@
local Config = require('opus.config')
local Event = require('opus.event')
local UI = require('opus.ui')
local Util = require('opus.util')
local fs = _G.fs
local getfenv = _G.getfenv
local kernel = _G.kernel
local multishell = _ENV.multishell
local shell = _ENV.shell
local args = { ... }
local filename = shell.resolveProgram(table.remove(args, 1))
if not filename then
error('file not found')
end
local config = Config.load('debugger')
if not config.filename then
config.filename = { }
end
local breakpoints = config.filename
local currentFile
local debugFile, debugLine
local debugger = kernel.getCurrent()
local client
local function startClient()
local env = kernel.makeEnv(_ENV)
local clientId = multishell.openTab(nil, {
env = env,
title = fs.getName(filename):match('([^%.]+)'),
args = args,
fn = function(...)
local dbg = require('debugger')
local fn = loadfile(filename, env)
local cocreate = coroutine.create
env.coroutine = require('opus.util').shallowCopy(coroutine)
env.coroutine.create = function(f, ...)
local co = cocreate(f, ...)
debug.sethook(co, dbg.hook, "l")
return co
end
dbg.debugger = debugger
dbg.breakpoints = breakpoints
dbg.stopIn(fn)
fn(...)
end,
})
client = kernel.find(clientId)
end
local function loadSource(file)
currentFile = file:match('@?(.*)')
local src = { }
local lines = Util.readLines(file:match('@?(.*)'))
if lines then
for i = 1, #lines do
table.insert(src, { line = i, source = lines[i] })
end
end
return src
end
local function message(...)
client:resume('debugger', ...)
end
local page = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = 'Continue', event = 'cmd', cmd = 'c' },
{ text = 'Step', event = 'cmd', cmd = 's' },
{ text = 'Step Over', event = 'cmd', cmd = 'n' },
{ text = 'Step Out', event = 'cmd', cmd = 'f' },
{ text = 'Restart', event = 'restart', width = 9, ex = -1 },
},
},
container = UI.Window {
y = 2, ey = '50%',
locals = UI.ScrollingGrid {
ey = -2,
disableHeader = true,
columns = {
{ heading = 'Key', key = 'name' },
{ heading = 'Value', key = 'value', textColor = 'yellow' },
},
--sortColumn = 'name',
autospace = true,
accelerators = {
grid_select = 'show_variable',
},
},
statusBar = UI.StatusBar {
ex = -7, y = -1,
backgroundColor = 'primary',
textColor = 'white',
},
UI.Button {
y = -1, x = -6,
event = 'open',
text = 'Open',
}
},
tabs = UI.Tabs {
y = '50%',
source = UI.Tab {
title = 'Source',
index = 1,
grid = UI.ScrollingGrid {
disableHeader = true,
columns = {
{ key = 'marker', width = 1 },
{ key = 'line', textColor = 'cyan', width = 4 },
{ heading = 'heading', key = 'source' },
},
getDisplayValues = function(_, row)
for _,v in pairs(breakpoints) do
if v.file == currentFile and v.line == row.line then
return {
marker = '!',
line = row.line,
source = row.source,
}
end
end
return row
end,
getRowTextColor = function(self, row, selected)
return row.line == debugLine and currentFile == debugFile and 'yellow'
or UI.Grid.getRowTextColor(self, row, selected)
end,
eventHandler = function(self, event)
if event.type == 'grid_select' then
self:emit({
type = 'toggle_breakpoint',
file = currentFile,
line = event.selected.line,
})
end
return UI.Grid.eventHandler(self, event)
end,
},
},
stack = UI.Tab {
title = 'Stack',
index = 3,
grid = UI.ScrollingGrid {
columns = {
{ key = 'index', width = 2 },
{ heading = 'heading', key = 'desc' },
},
getRowTextColor = function(self, row, selected)
return row.current and 'yellow'
or UI.Grid.getRowTextColor(self, row, selected)
end,
sortColumn = 'index',
},
eventHandler = function(self, event)
if event.type == 'grid_select' then
message('r', event.selected.index)
else
return UI.Grid.eventHandler(self, event)
end
end,
},
env = UI.Tab {
title = 'Env',
index = 4,
grid = UI.ScrollingGrid {
columns = {
{ heading = 'Key', key = 'name' },
{ heading = 'Value', key = 'value', textColor = 'yellow' },
},
autospace = true,
accelerators = {
grid_select = 'show_variable',
},
sortCompare = function() end,
},
},
breaks = UI.Tab {
title = 'Breakpoints',
index = 2,
menuBar = UI.MenuBar {
buttons = {
{ text = 'Toggle', event = 'toggle' },
{ text = 'Remove', event = 'remove' },
{ text = 'Clear', event = 'clear' },
},
},
grid = UI.ScrollingGrid {
y = 2,
columns = {
{ heading = 'Line', key = 'line', width = 5 },
{ heading = 'Name', key = 'short' },
{ heading = 'Path', key = 'path', textColor = 'lightGray' },
},
values = breakpoints,
autospace = true,
getRowTextColor = function(self, row, selected)
return row.disabled and 'lightGray'
or UI.Grid.getRowTextColor(self, row, selected)
end,
},
eventHandler = function(self, event)
if event.type == 'clear' then
Util.clear(self.grid.values)
self:emit({ type = 'update_breakpoints' })
elseif event.type == 'toggle' then
local bp = self.grid:getSelected()
if bp then
bp.disabled = not bp.disabled
self:emit({ type = 'update_breakpoints' })
end
elseif event.type == 'grid_select' then
self:emit({
type = 'open_file',
file = event.selected.file,
line = event.selected.line,
})
elseif event.type == 'remove' then
local bp = self.grid:getSelected()
if bp then
Util.removeByValue(self.grid.values, bp)
self:emit({ type = 'update_breakpoints' })
end
end
return UI.Tab.eventHandler(self, event)
end,
},
},
quick_open = UI.QuickSelect {
modal = true,
enable = function() end,
show = function(self)
UI.QuickSelect.enable(self)
self:focusFirst()
self:draw()
self:addTransition('expandUp', { easing = 'outBounce', ticks = 12 })
end,
eventHandler = function(self, event)
if event.type == 'select_cancel' then
self:disable()
elseif event.type == 'select_file' then
self.parent:openFile(event.file)
self:disable()
end
return UI.QuickSelect.eventHandler(self, event)
end,
},
openFile = function(self, file, line)
if file ~= currentFile then
local src = loadSource(file)
self.tabs.source.grid:setValues(src)
end
if line then
self.tabs.source.grid:setIndex(#self.tabs.source.grid.values)
self.tabs.source.grid:setIndex(math.max(1, line - 4))
end
self.tabs.source.grid:setIndex(line or 1)
self.tabs:selectTab(self.tabs.source)
if currentFile == debugFile then
self.container.statusBar:setStatus(
string.format('%s : %d', fs.getName(file), debugLine))
else
self.container.statusBar:setStatus(fs.getName(file))
end
self:draw()
end,
eventHandler = function(self, event)
if event.type == 'cmd' then
self.container.statusBar:setStatus('Running...')
message(event.element.cmd)
elseif event.type == 'restart' then
if kernel.find(client.uid) then
client:resume('terminate')
end
startClient()
elseif event.type == 'open' then
self.quick_open:show()
elseif event.type == 'open_file' then
self:openFile(event.file, event.line)
elseif event.type == 'update_breakpoints' then
self.tabs.breaks.grid:update()
self.tabs.breaks.grid:draw()
self.tabs.source.grid:draw()
message('b', breakpoints)
Config.update('debugger', config)
elseif event.type == 'toggle_breakpoint' then
for k,v in pairs(breakpoints) do
if v.file == event.file and v.line == event.line then
table.remove(breakpoints, k)
self:emit({ type = 'update_breakpoints' })
return
end
end
table.insert(breakpoints, {
file = event.file,
line = event.line,
short = fs.getName(event.file),
path = fs.getDir(event.file),
})
self:emit({ type = 'update_breakpoints' })
elseif event.type == 'show_variable' then
if type(event.selected.raw) == 'table' then
if event.selected.children then
event.selected.children = nil
else
event.selected.children = { }
local t = event.selected.raw
for k,v in pairs(t) do
local depth = event.selected.depth or 0
table.insert(event.selected.children,
{ name = (' '):rep(depth + 2) .. k, value = tostring(v), raw = v, depth = depth + 2 })
end
table.sort(event.selected.children, function(a, b) return a.name < b.name end)
end
local t = { }
local function insert(values)
for _,v in pairs(values) do
table.insert(t, v)
if v.children then
insert(v.children)
end
end
end
insert(event.element.orig)
event.element:setValues(t)
event.element:draw()
end
end
return UI.Page.eventHandler(self, event)
end,
enable = function(self)
UI.Page.enable(self)
startClient()
end,
}
Event.on('debugger', function(_, cmd, data)
if cmd == 'info' then
kernel.raise(debugger.uid)
-- local tab
local t = { }
for k,v in pairs(data.locals or { }) do
table.insert(t, { name = k, value = tostring(v), raw = v })
end
table.sort(t, function(a, b) return a.name < b.name end)
page.container.locals:setValues(t)
page.container.locals.orig = Util.shallowCopy(t)
-- env tab
t = { }
for k,v in pairs(getfenv(data.info.func)) do
table.insert(t, { name = k, value = tostring(v), raw = v })
end
page.tabs.env.grid:setValues(t)
page.tabs.env.grid.orig = Util.shallowCopy(t)
debugLine = data.info.currentline
debugFile = data.info.source:match('@?(.*)')
-- source tab
page:openFile(debugFile, debugLine)
-- stack
page.tabs.stack.grid:setValues(data.stack)
page:draw()
page:sync()
end
end)
UI:setPage(page)
UI:start()
message('d')

28
debugger/example.lua Normal file
View File

@@ -0,0 +1,28 @@
local function m2(a)
return a
end
local function method(times)
local a = 2
-- use step out to return out of method
for _ = 1, times do
a = a * a
end
return m2(a)
end
print('before')
-- breakpoint
--dbg()
print('after')
local i = 2
print(i)
local res = method(i)
dofile("rom/modules/main/cc/expect.lua")
print(res)
print('result: ' .. res)
error('f')

View File

@@ -19,9 +19,9 @@ local SIGC = 'LZWC'
local basedictcompress = {} local basedictcompress = {}
local basedictdecompress = {} local basedictdecompress = {}
for i = 0, 255 do for i = 0, 255 do
local ic, iic = char(i), char(i, 0) local ic, iic = char(i), char(i, 0)
basedictcompress[ic] = iic basedictcompress[ic] = iic
basedictdecompress[iic] = ic basedictdecompress[iic] = ic
end end
local native = { open = fs.open } local native = { open = fs.open }
@@ -29,123 +29,123 @@ local enabled = false
local filters = { } local filters = { }
local function dictAddA(str, dict, a, b) local function dictAddA(str, dict, a, b)
if a >= 256 then if a >= 256 then
a, b = 0, b+1 a, b = 0, b+1
if b >= 256 then if b >= 256 then
dict = {} dict = {}
b = 1 b = 1
end end
end end
dict[str] = char(a,b) dict[str] = char(a,b)
a = a+1 a = a+1
return dict, a, b return dict, a, b
end end
local function compress(input) local function compress(input)
if type(input) ~= "string" then if type(input) ~= "string" then
error ("string expected, got "..type(input)) error ("string expected, got "..type(input))
end end
local len = #input local len = #input
if len <= 1 then if len <= 1 then
return input return input
end end
local dict = {} local dict = {}
local a, b = 0, 1 local a, b = 0, 1
local result = { SIGC } local result = { SIGC }
local resultlen = 1 local resultlen = 1
local n = 2 local n = 2
local word = "" local word = ""
for i = 1, len do for i = 1, len do
local c = sub(input, i, i) local c = sub(input, i, i)
local wc = word..c local wc = word..c
if not (basedictcompress[wc] or dict[wc]) then if not (basedictcompress[wc] or dict[wc]) then
local write = basedictcompress[word] or dict[word] local write = basedictcompress[word] or dict[word]
if not write then if not write then
error "algorithm error, could not fetch word" error "algorithm error, could not fetch word"
end end
result[n] = write result[n] = write
resultlen = resultlen + #write resultlen = resultlen + #write
n = n+1 n = n+1
if len <= resultlen then if len <= resultlen then
return input return input
end end
dict, a, b = dictAddA(wc, dict, a, b) dict, a, b = dictAddA(wc, dict, a, b)
word = c word = c
else else
word = wc word = wc
end end
end end
result[n] = basedictcompress[word] or dict[word] result[n] = basedictcompress[word] or dict[word]
resultlen = resultlen+#result[n] resultlen = resultlen+#result[n]
if len <= resultlen then if len <= resultlen then
return input return input
end end
return tconcat(result) return tconcat(result)
end end
local function dictAddB(str, dict, a, b) local function dictAddB(str, dict, a, b)
if a >= 256 then if a >= 256 then
a, b = 0, b+1 a, b = 0, b+1
if b >= 256 then if b >= 256 then
dict = {} dict = {}
b = 1 b = 1
end end
end end
dict[char(a,b)] = str dict[char(a,b)] = str
a = a+1 a = a+1
return dict, a, b return dict, a, b
end end
local function decompress(input) local function decompress(input)
if type(input) ~= "string" then if type(input) ~= "string" then
error( "string expected, got "..type(input)) error( "string expected, got "..type(input))
end end
if #input <= 1 then if #input <= 1 then
return input return input
end end
local control = sub(input, 1, 4) local control = sub(input, 1, 4)
if control ~= SIGC then if control ~= SIGC then
return input return input
end end
input = sub(input, 5) input = sub(input, 5)
local len = #input local len = #input
if len < 2 then if len < 2 then
error("invalid input - not a compressed string") error("invalid input - not a compressed string")
end end
local dict = {} local dict = {}
local a, b = 0, 1 local a, b = 0, 1
local result = {} local result = {}
local n = 1 local n = 1
local last = sub(input, 1, 2) local last = sub(input, 1, 2)
result[n] = basedictdecompress[last] or dict[last] result[n] = basedictdecompress[last] or dict[last]
n = n+1 n = n+1
for i = 3, len, 2 do for i = 3, len, 2 do
local code = sub(input, i, i+1) local code = sub(input, i, i+1)
local lastStr = basedictdecompress[last] or dict[last] local lastStr = basedictdecompress[last] or dict[last]
if not lastStr then if not lastStr then
error( "could not find last from dict. Invalid input?") error( "could not find last from dict. Invalid input?")
end end
local toAdd = basedictdecompress[code] or dict[code] local toAdd = basedictdecompress[code] or dict[code]
if toAdd then if toAdd then
result[n] = toAdd result[n] = toAdd
n = n+1 n = n+1
dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b) dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b)
else else
local tmp = lastStr..sub(lastStr, 1, 1) local tmp = lastStr..sub(lastStr, 1, 1)
result[n] = tmp result[n] = tmp
n = n+1 n = n+1
dict, a, b = dictAddB(tmp, dict, a, b) dict, a, b = dictAddB(tmp, dict, a, b)
end end
last = code last = code
end end
return tconcat(result) return tconcat(result)
end end
local function split(str, pattern) local function split(str, pattern)
@@ -157,39 +157,39 @@ local function split(str, pattern)
end end
local function matchesFilter(fname) local function matchesFilter(fname)
if not fname:find('lzwfs') then -- don't compress anything with lzwfs in name (sigh) if not fname:find('lzwfs') then -- don't compress anything with lzwfs in name (sigh)
for _, filter in pairs(filters) do for _, filter in pairs(filters) do
if fname:match(filter) then if fname:match(filter) then
return true return true
end end
end end
end end
end end
function fs.open(fname, flags) function fs.open(fname, flags)
if not enabled then if not enabled then
return native.open(fname, flags) return native.open(fname, flags)
end end
if flags == 'r' then if flags == 'r' then
local f, err = native.open(fname, 'rb') local f, err = native.open(fname, 'rb')
if not f then if not f then
return f, err return f, err
end end
local ctr = 0 local ctr = 0
local lines local lines
return { return {
read = function() read = function()
if not lines then
lines = decompress(f.readAll())
end
ctr = ctr + 1
return lines:sub(ctr, ctr)
end,
readLine = function()
if not lines then if not lines then
lines = split(decompress(f.readAll())) lines = decompress(f.readAll())
end
ctr = ctr + 1
return lines:sub(ctr, ctr)
end,
readLine = function()
if not lines then
lines = split(decompress(f.readAll()))
end end
ctr = ctr + 1 ctr = ctr + 1
return lines[ctr] return lines[ctr]
@@ -197,61 +197,61 @@ function fs.open(fname, flags)
readAll = function() readAll = function()
return decompress(f.readAll()) return decompress(f.readAll())
end, end,
close = function() close = function()
f.close() f.close()
end,
}
elseif flags == 'w' or flags == 'a' then
if not matchesFilter(fs.combine(fname, '')) then
return native.open(fname, flags)
end
local c = { }
if flags == 'a' then
local f = fs.open(fname, 'r')
if f then
tinsert(c, f.readAll())
f.close()
end
end
local f, err = native.open(fname, 'wb')
if not f then
return f, err
end
return {
write = function(str)
tinsert(c, str)
end,
writeLine = function(str)
tinsert(c, str)
tinsert(c, '\n')
end,
flush = function()
-- this isn't gonna work...
-- f.write(compress(tconcat(c)))
f.flush();
end,
close = function()
f.write(compress(tconcat(c)))
f.close()
end, end,
} }
end elseif flags == 'w' or flags == 'a' then
if not matchesFilter(fs.combine(fname, '')) then
return native.open(fname, flags)
end
return native.open(fname, flags) local c = { }
if flags == 'a' then
local f = fs.open(fname, 'r')
if f then
tinsert(c, f.readAll())
f.close()
end
end
local f, err = native.open(fname, 'wb')
if not f then
return f, err
end
return {
write = function(str)
tinsert(c, str)
end,
writeLine = function(str)
tinsert(c, str)
tinsert(c, '\n')
end,
flush = function()
-- this isn't gonna work...
-- f.write(compress(tconcat(c)))
f.flush();
end,
close = function()
f.write(compress(tconcat(c)))
f.close()
end,
}
end
return native.open(fname, flags)
end end
function fs.option(category, action, option) function fs.option(category, action, option)
if category == 'compression' then if category == 'compression' then
if action == 'enabled' then if action == 'enabled' then
enabled = option enabled = option
elseif action == 'filters' then elseif action == 'filters' then
filters = option filters = option
end end
end end
end end
print('lzwfs started') print('lzwfs started')

View File

@@ -6,11 +6,11 @@ local CONFIG = 'usr/config/lzwfs'
local config = { } local config = { }
if fs.exists(CONFIG) then if fs.exists(CONFIG) then
local f = fs.open(CONFIG, 'r') local f = fs.open(CONFIG, 'r')
if f then if f then
config = textutils.unserialize(f.readAll()) config = textutils.unserialize(f.readAll())
f.close() f.close()
end end
end end
os.run(_ENV, '/packages/lzwfs/lzwfs.lua') os.run(_ENV, '/packages/lzwfs/lzwfs.lua')

View File

@@ -3,7 +3,7 @@ local shell = _ENV.shell
if not fs.exists('.mbs') then if not fs.exists('.mbs') then
print('Installing MBS') print('Installing MBS')
shell.run('mbs download') --shell.run('mbs download')
end end
print('Initializing MBS') print('Initializing MBS')
shell.run('mbs startup') --shell.run('mbs startup')

View File

@@ -2,24 +2,24 @@ local fs = _G.fs
local shell = _ENV.shell local shell = _ENV.shell
local function recurse(path) local function recurse(path)
if fs.isDir(path) then if fs.isDir(path) then
for _, v in pairs(fs.listEx(path)) do for _, v in pairs(fs.listEx(path)) do
if not v.isReadOnly then if not v.isReadOnly then
recurse(fs.combine(path, v.name)) recurse(fs.combine(path, v.name))
end end
end end
elseif path:match('%.lua$') and not fs.isReadOnly(path) then elseif path:match('%.lua$') and not fs.isReadOnly(path) then
local sz = fs.getSize(path) local sz = fs.getSize(path)
shell.run('minify.lua minify ' .. path) shell.run('minify.lua minify ' .. path)
print(string.format('%s : %.2f%%', path, (sz - fs.getSize(path)) / sz * 100)) print(string.format('%s : %.2f%%', path, (sz - fs.getSize(path)) / sz * 100))
end end
end end
local path = ({ ... })[1] or error('Syntax: minifyDir PATH') local path = ({ ... })[1] or error('Syntax: minifyDir PATH')
path = fs.combine(path, '') path = fs.combine(path, '')
if not fs.isDir(path) then if not fs.isDir(path) then
error('Invalid path') error('Invalid path')
end end
recurse(path) recurse(path)

View File

@@ -16,12 +16,12 @@ UI:configure('Equipment', ...)
local equipment = device.neuralInterface.getEquipment() local equipment = device.neuralInterface.getEquipment()
local slots = { local slots = {
'primary', 'primary',
'offhand', 'offhand',
'boots', 'boots',
'leggings', 'leggings',
'chest', 'chest',
'helmet', 'helmet',
} }
local page = UI.Page { local page = UI.Page {
@@ -34,29 +34,29 @@ local page = UI.Page {
grid = UI.Grid { grid = UI.Grid {
y = 2, y = 2,
columns = { columns = {
{ heading = 'Slot', key = 'index', width = 7 }, { heading = 'Slot', key = 'index', width = 7 },
{ heading = 'Name', key = 'displayName' }, { heading = 'Name', key = 'displayName' },
{ heading = 'Count', key = 'count', width = 5, align = 'right' }, { heading = 'Count', key = 'count', width = 5, align = 'right' },
}, },
sortColumn = 'index', sortColumn = 'index',
accelerators = { accelerators = {
grid_select = 'show_detail', grid_select = 'show_detail',
}, },
getDisplayValues = function(_, row) getDisplayValues = function(_, row)
row = Util.shallowCopy(row) row = Util.shallowCopy(row)
if row.name then if row.name then
local item = itemDB:get( local item = itemDB:get(
table.concat({ row.name, row.damage, row.nbtHash }, ':'), table.concat({ row.name, row.damage, row.nbtHash }, ':'),
function() function()
return equipment.getItemMeta(row.index) return equipment.getItemMeta(row.index)
end) end)
row.displayName = item.displayName row.displayName = item.displayName
else else
row.displayName = 'empty' row.displayName = 'empty'
end end
row.index = slots[row.index] row.index = slots[row.index]
return row return row
end, end,
}, },
accelerators = { accelerators = {
[ 'control-q' ] = 'quit', [ 'control-q' ] = 'quit',
@@ -77,58 +77,58 @@ local page = UI.Page {
accelerators = { accelerators = {
grid_select = 'inspect', grid_select = 'inspect',
}, },
}, },
show = function(self, slot) show = function(self, slot)
local detail = equipment.getItemMeta(slot.index) local detail = equipment.getItemMeta(slot.index)
local t = { } local t = { }
for k,v in pairs(detail) do for k,v in pairs(detail) do
table.insert(t, { table.insert(t, {
name = k, name = k,
value = v, value = v,
}) })
end end
self.grid:setValues(t) self.grid:setValues(t)
self.grid:setIndex(1) self.grid:setIndex(1)
UI.SlideOut.show(self) UI.SlideOut.show(self)
end, end,
}, },
enable = function(self) enable = function(self)
self:refresh() self:refresh()
UI.Page.enable(self) UI.Page.enable(self)
end, end,
refresh = function(self) refresh = function(self)
local t = { } local t = { }
local list = equipment.list() local list = equipment.list()
for i = 1, equipment.size() do for i = 1, equipment.size() do
local v = list[i] or { } local v = list[i] or { }
v.index = i v.index = i
table.insert(t, v) table.insert(t, v)
end end
self.grid:setValues(t) self.grid:setValues(t)
self.grid:draw() self.grid:draw()
end, end,
eventHandler = function(self, event) eventHandler = function(self, event)
if event.type == 'quit' then if event.type == 'quit' then
UI:quit() UI:quit()
elseif event.type == 'show_detail' then elseif event.type == 'show_detail' then
if event.selected.name then if event.selected.name then
self.detail:show(event.selected) self.detail:show(event.selected)
end end
elseif event.type == 'drop' then elseif event.type == 'drop' then
local selected = self.grid:getSelected() local selected = self.grid:getSelected()
equipment.drop(selected.index) equipment.drop(selected.index)
self:refresh() self:refresh()
elseif event.type == 'suck' then elseif event.type == 'suck' then
local selected = self.grid:getSelected() local selected = self.grid:getSelected()
equipment.suck(selected.index) equipment.suck(selected.index)
self:refresh() self:refresh()
end end
UI.Page.eventHandler(self, event) UI.Page.eventHandler(self, event)
end, end,
} }
Event.onInterval(1, function() Event.onInterval(1, function()

View File

@@ -4,6 +4,7 @@
[ 'ccemux' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/ccemux/.package', [ 'ccemux' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/ccemux/.package',
[ 'common' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/common/.package', [ 'common' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/common/.package',
[ 'core' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/core/.package', [ 'core' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/core/.package',
[ 'debugger' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/debugger/.package',
[ 'farms' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/farms/.package', [ 'farms' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/farms/.package',
-- [ 'forestry' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/forestry/.package', -- [ 'forestry' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/forestry/.package',
[ 'games' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/games/.package', [ 'games' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/games/.package',
@@ -11,7 +12,7 @@
[ 'gps' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/gps/.package', [ 'gps' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/gps/.package',
[ 'lfs' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/lfs/.package', [ 'lfs' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/lfs/.package',
[ 'lzwfs' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/lzwfs/.package', [ 'lzwfs' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/lzwfs/.package',
[ 'mbs' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/mbs/.package', -- [ 'mbs' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/mbs/.package',
[ 'milo' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/milo/.package', [ 'milo' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/milo/.package',
[ 'miloApps' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/miloApps/.package', [ 'miloApps' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/miloApps/.package',
[ 'miners' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/miners/.package', [ 'miners' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/miners/.package',