Lua debugger part 1
This commit is contained in:
@@ -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
6
debugger/.package
Normal 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
201
debugger/apis/init.lua
Normal 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
407
debugger/debug.lua
Normal 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
28
debugger/example.lua
Normal 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')
|
||||||
354
lzwfs/lzwfs.lua
354
lzwfs/lzwfs.lua
@@ -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')
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
Reference in New Issue
Block a user