debugger - error handling, ui rework
This commit is contained in:
@@ -1,35 +1,36 @@
|
|||||||
--[[
|
-- this code is loaded into the code being debugged
|
||||||
some portions from https://github.com/slembcke/debugger.lua
|
-- some portions from https://github.com/slembcke/debugger.lua
|
||||||
]]
|
|
||||||
|
|
||||||
local fs = _G.fs
|
local fs = _G.fs
|
||||||
|
|
||||||
local dbg = { }
|
local dbg = { }
|
||||||
|
|
||||||
local function hookBreakpoint(info)
|
local function breakpointHook(info)
|
||||||
if dbg.breakpoints then
|
if dbg.breakpoints then
|
||||||
for _,v in pairs(dbg.breakpoints) do
|
for _,v in pairs(dbg.breakpoints) do
|
||||||
if v.line == info.currentline and v.file == info.short_src and not v.disabled then
|
if v.line == info.currentline and v.file == info.short_src then
|
||||||
return true
|
print(v.line, not v.disabled)
|
||||||
|
return not v.disabled
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function hookFunction(fn)
|
local function functionHook(fn)
|
||||||
return function(info)
|
return function(info)
|
||||||
return info.func == fn
|
return info.func == fn
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function hookStep()
|
local function stepHook()
|
||||||
local co = coroutine.running()
|
local co = coroutine.running()
|
||||||
return function(info)
|
return function(info)
|
||||||
return co == coroutine.running() or hookBreakpoint(info)
|
return co == coroutine.running()
|
||||||
|
or breakpointHook(info)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function hookStepStacksize(n)
|
local function stackSizeHook(n)
|
||||||
local co = coroutine.running()
|
local co = coroutine.running()
|
||||||
local i = 2
|
local i = 2
|
||||||
while true do
|
while true do
|
||||||
@@ -40,27 +41,23 @@ local function hookStepStacksize(n)
|
|||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
return function(info)
|
return function(info)
|
||||||
if co == coroutine.running() then
|
return co == coroutine.running()
|
||||||
if not debug.getinfo(i - n) then
|
and not debug.getinfo(i - n)
|
||||||
return true
|
or breakpointHook(info)
|
||||||
end
|
|
||||||
end
|
|
||||||
return hookBreakpoint(info)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function hookStepOut()
|
local function stepOutHook()
|
||||||
return hookStepStacksize(1)
|
return stackSizeHook(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function hookStepOver()
|
local function stepOverHook()
|
||||||
return hookStepStacksize(0)
|
return stackSizeHook(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
local hookEval = function() end
|
local hookEval = function() end
|
||||||
|
|
||||||
-- Create a table of all the locally accessible variables.
|
-- 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)
|
local function local_bindings(offset, stack_inspect_offset)
|
||||||
offset = offset + 1 + stack_inspect_offset -- add this function to the offset
|
offset = offset + 1 + stack_inspect_offset -- add this function to the offset
|
||||||
local func = debug.getinfo(offset).func
|
local func = debug.getinfo(offset).func
|
||||||
@@ -96,7 +93,7 @@ local function local_bindings(offset, stack_inspect_offset)
|
|||||||
|
|
||||||
local t = { }
|
local t = { }
|
||||||
for k,v in pairs(bindings) do
|
for k,v in pairs(bindings) do
|
||||||
if v.raw ~= nil then
|
if k ~= '(*temporary)' then
|
||||||
v.name = k
|
v.name = k
|
||||||
v.value = tostring(v.raw)
|
v.value = tostring(v.raw)
|
||||||
table.insert(t, v)
|
table.insert(t, v)
|
||||||
@@ -138,45 +135,44 @@ local inHook = false
|
|||||||
|
|
||||||
local function hook()
|
local function hook()
|
||||||
local info = debug.getinfo(2)
|
local info = debug.getinfo(2)
|
||||||
if info.currentline < 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not inHook and hookEval(info) then
|
if not inHook and hookEval(info) then
|
||||||
inHook = true
|
inHook = true
|
||||||
|
|
||||||
local offset = 2 -- the offset from this function to the code being debugged
|
|
||||||
local inspectOffset = 0
|
local inspectOffset = 0
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
local done = true
|
local done = true
|
||||||
local snapshot = {
|
|
||||||
info = debug.getinfo(offset + inspectOffset),
|
|
||||||
locals = local_bindings(offset, inspectOffset),
|
|
||||||
stack = get_trace(offset, inspectOffset),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
local snapshot = {
|
||||||
|
info = debug.getinfo(2 + inspectOffset),
|
||||||
|
locals = local_bindings(2, inspectOffset),
|
||||||
|
stack = get_trace(2, inspectOffset),
|
||||||
|
}
|
||||||
inspectOffset = 0 -- reset
|
inspectOffset = 0 -- reset
|
||||||
|
|
||||||
local cmd, param = dbg.read(snapshot)
|
os.queueEvent('debuggerX', dbg.debugger.uid, snapshot)
|
||||||
|
|
||||||
|
local e, cmd, param
|
||||||
|
repeat
|
||||||
|
e, cmd, param = os.pullEvent('debugger')
|
||||||
|
until e == 'debugger'
|
||||||
|
|
||||||
if cmd == 's' then
|
if cmd == 's' then
|
||||||
hookEval = hookStep()
|
hookEval = stepHook()
|
||||||
elseif cmd == 'n' then
|
elseif cmd == 'n' then
|
||||||
hookEval = hookStepOver()
|
hookEval = stepOverHook()
|
||||||
elseif cmd == 'f' then
|
elseif cmd == 'f' then
|
||||||
hookEval = hookStepOut()
|
hookEval = stepOutHook()
|
||||||
elseif cmd == 'c' then
|
elseif cmd == 'c' then
|
||||||
hookEval = hookBreakpoint
|
hookEval = breakpointHook
|
||||||
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
|
elseif cmd == 'i' then
|
||||||
-- inspect stack at this offset
|
-- get snapshot of stack at this offset
|
||||||
inspectOffset = param
|
inspectOffset = param
|
||||||
done = false
|
done = false
|
||||||
|
else
|
||||||
|
os.sleep(1)
|
||||||
|
done = false
|
||||||
end
|
end
|
||||||
until done
|
until done
|
||||||
|
|
||||||
@@ -184,41 +180,51 @@ local function hook()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local cocreate = coroutine.create
|
function dbg.call(f, ...)
|
||||||
_ENV.coroutine = { }
|
local args = { ... }
|
||||||
for k,v in pairs(_G.coroutine) do
|
return xpcall(
|
||||||
_ENV.coroutine[k] = v
|
function()
|
||||||
end
|
f(table.unpack(args))
|
||||||
_ENV.coroutine.create = function(f, ...)
|
end,
|
||||||
local co = cocreate(f, ...)
|
function(err)
|
||||||
debug.sethook(co, dbg.hook, "l")
|
hookEval = stepHook()
|
||||||
return co
|
|
||||||
|
-- An error has occurred
|
||||||
|
return err
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
_ENV.coroutine = setmetatable({
|
||||||
|
|
||||||
|
create = function(f)
|
||||||
|
local co = _G.coroutine.create(function(...)
|
||||||
|
local r = { dbg.call(f, ...) }
|
||||||
|
|
||||||
|
if not r[1] then
|
||||||
|
error(r[2], -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.unpack(r, 2)
|
||||||
|
end)
|
||||||
|
|
||||||
|
debug.sethook(co, hook, 'l')
|
||||||
|
return co
|
||||||
|
end
|
||||||
|
--[[
|
||||||
|
create = function(f)
|
||||||
|
local co = _G.coroutine.create(f)
|
||||||
|
debug.sethook(co, hook, 'l')
|
||||||
|
return co
|
||||||
|
end
|
||||||
|
]]
|
||||||
|
}, { __index = coroutine })
|
||||||
|
|
||||||
debug.sethook(hook, 'l')
|
debug.sethook(hook, 'l')
|
||||||
|
|
||||||
-- Expose the debugger's functions
|
-- Expose the debugger's functions
|
||||||
dbg.hook = hook
|
|
||||||
dbg.exit = function(err) os.exit(err) end
|
|
||||||
dbg.stopIn = function(fn)
|
dbg.stopIn = function(fn)
|
||||||
hookEval = hookFunction(fn)
|
hookEval = functionHook(fn)
|
||||||
end
|
end
|
||||||
dbg.debugger = nil
|
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
|
return dbg
|
||||||
|
|||||||
@@ -32,12 +32,13 @@ local client
|
|||||||
|
|
||||||
local function startClient()
|
local function startClient()
|
||||||
local env = kernel.makeEnv(_ENV)
|
local env = kernel.makeEnv(_ENV)
|
||||||
|
currentFile = nil
|
||||||
|
|
||||||
local clientId = multishell.openTab(nil, {
|
local clientId = multishell.openTab(nil, {
|
||||||
env = env,
|
env = env,
|
||||||
title = fs.getName(filename):match('([^%.]+)'),
|
title = fs.getName(filename):match('([^%.]+)'),
|
||||||
args = args,
|
args = args,
|
||||||
fn = function(...)
|
fn = function()
|
||||||
local dbg = require('debugger')
|
local dbg = require('debugger')
|
||||||
local fn, msg = loadfile(filename, env)
|
local fn, msg = loadfile(filename, env)
|
||||||
|
|
||||||
@@ -45,10 +46,14 @@ local function startClient()
|
|||||||
error(msg, -1)
|
error(msg, -1)
|
||||||
end
|
end
|
||||||
|
|
||||||
dbg.debugger = debugger
|
-- breakpoint table is shared across processes
|
||||||
dbg.breakpoints = breakpoints
|
dbg.breakpoints = breakpoints
|
||||||
|
dbg.debugger = debugger
|
||||||
dbg.stopIn(fn)
|
dbg.stopIn(fn)
|
||||||
fn(...)
|
local s, m = dbg.call(fn, table.unpack(args))
|
||||||
|
if not s then
|
||||||
|
error(m, -1)
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
client = kernel.find(clientId)
|
client = kernel.find(clientId)
|
||||||
@@ -79,7 +84,7 @@ romFiles:load()
|
|||||||
local function loadSource(file)
|
local function loadSource(file)
|
||||||
currentFile = romFiles:get(file) or file:match('@?(.*)')
|
currentFile = romFiles:get(file) or file:match('@?(.*)')
|
||||||
local src = { }
|
local src = { }
|
||||||
local lines = Util.readLines(currentFile)
|
local lines = Util.readLines(currentFile) or type(file) == 'string' and Util.split(file)
|
||||||
|
|
||||||
if lines then
|
if lines then
|
||||||
for i = 1, #lines do
|
for i = 1, #lines do
|
||||||
@@ -95,199 +100,210 @@ local function message(...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
menuBar = UI.MenuBar {
|
backgroundColor = 'black',
|
||||||
buttons = {
|
|
||||||
{ text = 'Continue', event = 'cmd', cmd = 'c' },
|
|
||||||
{ text = 'Step', event = 'cmd', cmd = 's' },
|
|
||||||
{ text = 'Over', event = 'cmd', cmd = 'n' },
|
|
||||||
{ text = 'Out', event = 'cmd', cmd = 'f' },
|
|
||||||
{ text = 'Restart', event = 'restart', width = 9, ex = -1 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
container = UI.Window {
|
container = UI.Window {
|
||||||
y = 2, ey = '50%',
|
y = 1, ey = '50%',
|
||||||
locals = UI.ScrollingGrid {
|
tabs = UI.Tabs {
|
||||||
ey = -2,
|
ey = -2,
|
||||||
disableHeader = true,
|
unselectedBackgroundColor = 'black',
|
||||||
columns = {
|
|
||||||
{ heading = 'Key', key = 'name' },
|
|
||||||
{ heading = 'Value', key = 'value', textColor = 'yellow' },
|
|
||||||
},
|
|
||||||
autospace = true,
|
|
||||||
accelerators = {
|
|
||||||
grid_select = 'show_variable',
|
|
||||||
},
|
|
||||||
getRowTextColor = function(self, row, selected)
|
|
||||||
return row.type == 'U' and 'cyan'
|
|
||||||
or row.type == 'V' and 'lime'
|
|
||||||
or UI.Grid.getRowTextColor(self, row, selected)
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
statusBar = UI.StatusBar {
|
|
||||||
ex = -7, y = -1,
|
|
||||||
backgroundColor = 'primary',
|
|
||||||
textColor = 'white',
|
|
||||||
},
|
|
||||||
UI.Button {
|
|
||||||
y = -1, x = -6,
|
|
||||||
event = 'open',
|
|
||||||
text = 'Open',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
tabs = UI.Tabs {
|
locals = UI.Tab {
|
||||||
y = '50%',
|
title = 'Locals',
|
||||||
source = UI.Tab {
|
index = 1,
|
||||||
title = 'Source',
|
grid = UI.ScrollingGrid {
|
||||||
index = 1,
|
disableHeader = true,
|
||||||
grid = UI.ScrollingGrid {
|
unfocusedBackgroundSelectedColor = 'black',
|
||||||
disableHeader = true,
|
columns = {
|
||||||
columns = {
|
{ heading = 'Key', key = 'name' },
|
||||||
{ key = 'marker', width = 1 },
|
{ heading = 'Value', key = 'value', textColor = 'yellow' },
|
||||||
{ key = 'line', textColor = 'cyan', width = 4 },
|
},
|
||||||
{ heading = 'heading', key = 'source' },
|
autospace = true,
|
||||||
|
accelerators = {
|
||||||
|
grid_select = 'show_variable',
|
||||||
|
},
|
||||||
|
getRowTextColor = function(self, row, selected)
|
||||||
|
return row.type == 'U' and 'cyan'
|
||||||
|
or row.type == 'V' and 'lime'
|
||||||
|
or UI.Grid.getRowTextColor(self, row, selected)
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
getDisplayValues = function(_, row)
|
},
|
||||||
for _,v in pairs(breakpoints) do
|
|
||||||
if v.file == currentFile and v.line == row.line then
|
stack = UI.Tab {
|
||||||
return {
|
title = 'Stack',
|
||||||
marker = v.disabled and 'x' or '!',
|
index = 3,
|
||||||
line = row.line,
|
grid = UI.ScrollingGrid {
|
||||||
source = row.source,
|
disableHeader = true,
|
||||||
}
|
sortColumn = 'index',
|
||||||
|
unfocusedBackgroundSelectedColor = 'black',
|
||||||
|
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,
|
||||||
|
eventHandler = function(self, event)
|
||||||
|
if event.type == 'grid_select' then
|
||||||
|
message('i', event.selected.index)
|
||||||
|
else
|
||||||
|
return UI.Grid.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
end
|
end,
|
||||||
return row
|
},
|
||||||
end,
|
},
|
||||||
accelerators = {
|
|
||||||
t = 'toggle_enabled'
|
env = UI.Tab {
|
||||||
|
title = 'Env',
|
||||||
|
index = 4,
|
||||||
|
grid = UI.ScrollingGrid {
|
||||||
|
disableHeader = true,
|
||||||
|
autospace = true,
|
||||||
|
unfocusedBackgroundSelectedColor = 'black',
|
||||||
|
columns = {
|
||||||
|
{ heading = 'Key', key = 'name' },
|
||||||
|
{ heading = 'Value', key = 'value', textColor = 'yellow' },
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
values = breakpoints,
|
||||||
|
autospace = true,
|
||||||
|
columns = {
|
||||||
|
{ heading = 'Line', key = 'line', width = 5 },
|
||||||
|
{ heading = 'Name', key = 'short' },
|
||||||
|
{ heading = 'Path', key = 'path', textColor = 'lightGray' },
|
||||||
|
},
|
||||||
|
getRowTextColor = function(self, row, selected)
|
||||||
|
return row.disabled and 'lightGray'
|
||||||
|
or UI.Grid.getRowTextColor(self, row, selected)
|
||||||
|
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)
|
eventHandler = function(self, event)
|
||||||
if event.type == 'grid_select' then
|
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({
|
self:emit({
|
||||||
type = 'toggle_breakpoint',
|
type = 'open_file',
|
||||||
file = currentFile,
|
file = event.selected.file,
|
||||||
line = event.selected.line,
|
line = event.selected.line,
|
||||||
})
|
})
|
||||||
elseif event.type == 'toggle_enabled' then
|
|
||||||
local line = self:getSelected() and self:getSelected().line
|
elseif event.type == 'remove' then
|
||||||
if line then
|
local bp = self.grid:getSelected()
|
||||||
for _,v in pairs(breakpoints) do
|
if bp then
|
||||||
if v.file == currentFile and v.line == line then
|
Util.removeByValue(self.grid.values, bp)
|
||||||
v.disabled = not v.disabled
|
self:emit({ type = 'update_breakpoints' })
|
||||||
self:emit({ type = 'update_breakpoints' })
|
end
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
return UI.Tab.eventHandler(self, event)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
menuBar = UI.MenuBar {
|
||||||
|
y = -1,
|
||||||
|
buttons = {
|
||||||
|
{ text = 'Continue', event = 'cmd', cmd = 'c' },
|
||||||
|
{ text = 'Step', event = 'cmd', cmd = 's' },
|
||||||
|
{ text = 'Over', event = 'cmd', cmd = 'n' },
|
||||||
|
{ text = 'Out', event = 'cmd', cmd = 'f' },
|
||||||
|
{ text = 'Restart', event = 'restart', width = 9, ex = -1 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
source = UI.ScrollingGrid {
|
||||||
|
y = '50%', ey = -2,
|
||||||
|
disableHeader = true,
|
||||||
|
columns = {
|
||||||
|
{ key = 'marker', width = 1 },
|
||||||
|
{ key = 'line', textColor = 'cyan', width = 4 },
|
||||||
|
{ heading = 'heading', key = 'source' },
|
||||||
|
},
|
||||||
|
accelerators = {
|
||||||
|
t = 'toggle_enabled'
|
||||||
|
},
|
||||||
|
getDisplayValues = function(_, row)
|
||||||
|
for _,v in pairs(breakpoints) do
|
||||||
|
if v.file == currentFile and v.line == row.line then
|
||||||
|
return {
|
||||||
|
marker = v.disabled and 'x' or '!',
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
elseif event.type == 'toggle_enabled' then
|
||||||
|
local line = self:getSelected() and self:getSelected().line
|
||||||
|
if line then
|
||||||
|
for _,v in pairs(breakpoints) do
|
||||||
|
if v.file == currentFile and v.line == line then
|
||||||
|
v.disabled = not v.disabled
|
||||||
|
self:emit({ type = 'update_breakpoints' })
|
||||||
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return UI.Grid.eventHandler(self, event)
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
stack = UI.Tab {
|
|
||||||
title = 'Stack',
|
|
||||||
index = 3,
|
|
||||||
grid = UI.ScrollingGrid {
|
|
||||||
disableHeader = true,
|
|
||||||
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('i', 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
|
end
|
||||||
return UI.Tab.eventHandler(self, event)
|
end
|
||||||
end,
|
return UI.Grid.eventHandler(self, event)
|
||||||
},
|
end,
|
||||||
|
},
|
||||||
|
statusBar = UI.StatusBar {
|
||||||
|
ex = -7, y = -1,
|
||||||
|
backgroundColor = 'black',
|
||||||
|
textColor = 'orange',
|
||||||
|
},
|
||||||
|
UI.FlatButton {
|
||||||
|
y = -1, x = -5,
|
||||||
|
textColor = 'orange',
|
||||||
|
event = 'open',
|
||||||
|
text = 'Open',
|
||||||
},
|
},
|
||||||
|
|
||||||
quick_open = UI.QuickSelect {
|
quick_open = UI.QuickSelect {
|
||||||
|
y = '50%',
|
||||||
modal = true,
|
modal = true,
|
||||||
enable = function() end,
|
enable = function() end,
|
||||||
show = function(self)
|
show = function(self)
|
||||||
@@ -310,26 +326,25 @@ local page = UI.Page {
|
|||||||
openFile = function(self, file, line)
|
openFile = function(self, file, line)
|
||||||
if file ~= currentFile then
|
if file ~= currentFile then
|
||||||
local src = loadSource(file)
|
local src = loadSource(file)
|
||||||
self.tabs.source.grid:setValues(src)
|
self.source:setValues(src)
|
||||||
end
|
end
|
||||||
if line then
|
if line then
|
||||||
self.tabs.source.grid:setIndex(#self.tabs.source.grid.values)
|
self.source:setIndex(#self.source.values)
|
||||||
self.tabs.source.grid:setIndex(math.max(1, line - 4))
|
self.source:setIndex(math.max(1, line - 4))
|
||||||
end
|
end
|
||||||
self.tabs.source.grid:setIndex(line or 1)
|
self.source:setIndex(line or 1)
|
||||||
self.tabs:selectTab(self.tabs.source)
|
|
||||||
|
|
||||||
if currentFile == debugFile then
|
if currentFile == debugFile then
|
||||||
self.container.statusBar:setStatus(
|
self.statusBar:setStatus(
|
||||||
string.format('%s : %d', fs.getName(file), debugLine))
|
string.format('%s : %d', fs.getName(file), debugLine))
|
||||||
else
|
else
|
||||||
self.container.statusBar:setStatus(fs.getName(file))
|
self.statusBar:setStatus(fs.getName(file))
|
||||||
end
|
end
|
||||||
self:draw()
|
self:draw()
|
||||||
end,
|
end,
|
||||||
eventHandler = function(self, event)
|
eventHandler = function(self, event)
|
||||||
if event.type == 'cmd' then
|
if event.type == 'cmd' then
|
||||||
self.container.statusBar:setStatus('Running...')
|
self.statusBar:setStatus('Running...')
|
||||||
message(event.element.cmd)
|
message(event.element.cmd)
|
||||||
|
|
||||||
elseif event.type == 'restart' then
|
elseif event.type == 'restart' then
|
||||||
@@ -345,10 +360,9 @@ local page = UI.Page {
|
|||||||
self:openFile(event.file, event.line)
|
self:openFile(event.file, event.line)
|
||||||
|
|
||||||
elseif event.type == 'update_breakpoints' then
|
elseif event.type == 'update_breakpoints' then
|
||||||
self.tabs.breaks.grid:update()
|
self.container.tabs.breaks.grid:update()
|
||||||
self.tabs.breaks.grid:draw()
|
self.container.tabs.breaks.grid:draw()
|
||||||
self.tabs.source.grid:draw()
|
self.source:draw()
|
||||||
message('b', breakpoints)
|
|
||||||
Config.update('debugger', config)
|
Config.update('debugger', config)
|
||||||
|
|
||||||
elseif event.type == 'toggle_breakpoint' then
|
elseif event.type == 'toggle_breakpoint' then
|
||||||
@@ -405,26 +419,22 @@ local page = UI.Page {
|
|||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
Event.on('debugger', function(_, cmd, data)
|
Event.on('debuggerX', function(_, uid, data)
|
||||||
if cmd == 'info' then
|
if uid == debugger.uid then
|
||||||
kernel.raise(debugger.uid)
|
kernel.raise(debugger.uid)
|
||||||
|
|
||||||
-- local tab
|
-- local tab
|
||||||
local t = data.locals
|
table.sort(data.locals, function(a, b) return a.name < b.name end)
|
||||||
-- for k,v in pairs(data.locals or { }) do
|
page.container.tabs.locals.grid:setValues(data.locals)
|
||||||
-- table.insert(t, { name = k, value = tostring(v), raw = v })
|
page.container.tabs.locals.grid.orig = Util.shallowCopy(data.locals)
|
||||||
-- 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
|
-- env tab
|
||||||
t = { }
|
local t = { }
|
||||||
for k,v in pairs(getfenv(data.info.func)) do
|
for k,v in pairs(getfenv(data.info.func)) do
|
||||||
table.insert(t, { name = k, value = tostring(v), raw = v })
|
table.insert(t, { name = k, value = tostring(v), raw = v })
|
||||||
end
|
end
|
||||||
page.tabs.env.grid:setValues(t)
|
page.container.tabs.env.grid:setValues(t)
|
||||||
page.tabs.env.grid.orig = Util.shallowCopy(t)
|
page.container.tabs.env.grid.orig = Util.shallowCopy(t)
|
||||||
|
|
||||||
debugLine = data.info.currentline
|
debugLine = data.info.currentline
|
||||||
debugFile = data.info.source:match('@?(.*)')
|
debugFile = data.info.source:match('@?(.*)')
|
||||||
@@ -433,7 +443,7 @@ Event.on('debugger', function(_, cmd, data)
|
|||||||
page:openFile(debugFile, debugLine)
|
page:openFile(debugFile, debugLine)
|
||||||
|
|
||||||
-- stack
|
-- stack
|
||||||
page.tabs.stack.grid:setValues(data.stack)
|
page.container.tabs.stack.grid:setValues(data.stack)
|
||||||
|
|
||||||
page:draw()
|
page:draw()
|
||||||
page:sync()
|
page:sync()
|
||||||
|
|||||||
@@ -4,16 +4,37 @@ end
|
|||||||
|
|
||||||
local function method(times)
|
local function method(times)
|
||||||
local a = 2
|
local a = 2
|
||||||
-- use step out to return out of method
|
|
||||||
for _ = 1, times do
|
for _ = 1, times do
|
||||||
a = a * a
|
a = a * a
|
||||||
end
|
end
|
||||||
return m2(a)
|
return m2(a)
|
||||||
end
|
end
|
||||||
|
|
||||||
print('before')
|
local chunk = load([[
|
||||||
term.current().clear()
|
local j = 5
|
||||||
print('after')
|
for i = 1, 5 do
|
||||||
|
j = j * i
|
||||||
|
end
|
||||||
|
--table.insert(j, 5)
|
||||||
|
return j]], nil, nil, _ENV)
|
||||||
|
|
||||||
|
local j = chunk()
|
||||||
|
print(j)
|
||||||
|
|
||||||
|
require('opus.util').print(coroutine)
|
||||||
|
local co = coroutine.create(function(args)
|
||||||
|
print('in coroutine')
|
||||||
|
return 'hi'
|
||||||
|
end)
|
||||||
|
|
||||||
|
local _, t = coroutine.resume(co, 'test')
|
||||||
|
while coroutine.status(co) ~= 'dead' do
|
||||||
|
coroutine.resume(co, os.pullEvent())
|
||||||
|
--print('alive')
|
||||||
|
end
|
||||||
|
print(coroutine.status(co))
|
||||||
|
|
||||||
|
print(t)
|
||||||
|
|
||||||
local i = 2
|
local i = 2
|
||||||
print(i)
|
print(i)
|
||||||
@@ -24,4 +45,4 @@ dofile("rom/modules/main/cc/expect.lua")
|
|||||||
print(res)
|
print(res)
|
||||||
print('result: ' .. res)
|
print('result: ' .. res)
|
||||||
|
|
||||||
error('f')
|
table.insert(res, 5)
|
||||||
|
|||||||
Reference in New Issue
Block a user