1
debugger = debugger or {}
3
-- Property: debugger.consoleKey
4
-- What key toggles visibility. By default, this is the tab key.
5
debugger.consoleKey = 'tab'
7
-- Property: debugger.crashed
8
-- Records whether the app has crashed.
9
debugger.crashed = false
11
-- internal property: debugger._sourceCache
12
-- A cache of source files used by debugger.sourceLine().
13
debugger._sourceCache = {}
15
-- Function: debugger.init()
16
-- Adds debug instruments to the app. Must be called *after*
17
-- an app has started running, i.e. in its <App.onRun> event
21
-- Instrument classes to add, defaults to all available
26
debugger.init = function()
27
debugger.console = Group:new
31
instruments = { narrow = Group:new(), wide = Group:new() },
32
widths = { wide = 0.7, narrow = 0.3 },
35
_instrumentHeights = {},
37
onNew = function (self)
38
self:add(self.instruments.narrow)
39
self:add(self.instruments.wide)
42
update = function (self, elapsed)
43
if the.keys:justPressed(debugger.consoleKey) then
45
debugger.hideConsole()
47
debugger.showConsole()
51
for _, listener in pairs(self.listeners) do
55
if debugger.console.visible then
56
for spr, height in pairs(debugger.console._instrumentHeights) do
57
if height ~= spr.contentHeight then
58
debugger._resizeInstruments()
62
Group.update(self, elapsed)
67
the.app.meta:add(debugger.console)
69
debugger.addInstrument(DebugStepper:new())
70
debugger.addInstrument(DebugLocals:new())
71
debugger.addInstrument(DebugStack:new())
72
debugger.addInstrument(DebugPerformance:new())
73
debugger.addInstrument(DebugWatch:new())
74
debugger.addInstrument(DebugShortcuts:new())
75
debugger.addInstrument(DebugConsole:new())
78
-- Function: debugger.showConsole()
79
-- Makes the console visible.
87
debugger.showConsole = function()
88
debugger.console.visible = true
91
-- Function: debugger.hideConsole
92
-- Makes the console invisible. If the app has crashed,
93
-- this has no effect.
101
debugger.hideConsole = function()
102
if not debugger.crashed then
103
debugger.console.visible = false
107
-- Function: debugger.addInstrument
108
-- Adds an instrument to the console, creating a container and
112
-- instrument - <Group> enclosing the entire instrument
117
debugger.addInstrument = function (instrument)
118
local console = debugger.console
119
assert(instrument.width == 'narrow' or instrument.width == 'wide',
120
"debug instrument's width property must be either 'wide' or 'narrow'")
122
console.instruments[instrument.width]:add(instrument)
123
debugger._resizeInstruments()
126
-- Function: debugger.addListener
127
-- Adds a function that will be called on every frame,
128
-- regardless of whether the console is visible.
131
-- listener - function
136
debugger.addListener = function (func)
137
table.insert(debugger.console.listeners, func)
140
-- Function: debugger.reload
141
-- Resets the entire app and forces all code to be reloaded from
142
-- on disk. via https://love2d.org/forums/viewtopic.php?f=3&t=7965
150
debugger.reload = function()
153
-- create local references to needed variables
154
-- because we're about to blow the global scope away
156
local initialGlobals = debugger._initialGlobals
157
local initialPackages = debugger._initialPackages
159
-- reset global scope
161
for key, _ in pairs(_G) do
162
_G[key] = initialGlobals[key]
165
-- reload main file and restart
167
for key, _ in pairs(package.loaded) do
168
if not initialPackages[key] then
169
package.loaded[key] = nil
177
-- Function: debugger.sourceLine
178
-- Retrieves a line of source code. If the source file cannot
179
-- be opened, then this returns '(source not available)'. If
180
-- the line doesn't exist in the file (e.g. you ask for line 200
181
-- of a 100-line file), this returns nil.
184
-- file - filename of the source code
185
-- line - line number to retrieve
188
-- string source or '(source not available')
190
debugger.sourceLine = function (file, line)
192
if not debugger._sourceCache[file] then
193
debugger._sourceCache[file] = {}
195
for line in love.filesystem.lines(file) do
196
table.insert(debugger._sourceCache[file], line)
200
return debugger._sourceCache[file][line]
202
return '(source not available)'
206
debugger._resizeInstruments = function()
207
local console = debugger.console
211
local x = console.spacing
212
local y = console.spacing
213
local width = the.app.width * console.widths.wide
214
local expandables = {}
216
for _, spr in pairs(console.instruments.wide.sprites) do
218
local height = spr:totalHeight()
219
console._instrumentHeights[spr] = spr.contentHeight
221
if height == '*' then
222
table.insert(expandables, spr)
224
spr:resize(x, y, width - console.spacing, height)
225
y = y + height + console.spacing
230
if #expandables > 0 then
231
local height = (the.app.height - y) / #expandables
233
for i, spr in ipairs(expandables) do
234
spr:resize(x, y + height * (i - 1), width - console.spacing, height - console.spacing)
238
-- narrow instruments
242
width = the.app.width * console.widths.narrow
245
for _, spr in pairs(console.instruments.narrow.sprites) do
247
local height = spr:totalHeight()
248
console._instrumentHeights[spr] = spr.contentHeight
250
if height == '*' then
251
table.insert(expandables, spr)
253
spr:resize(x, y, width - 2 * console.spacing, height)
254
y = y + height + console.spacing
259
if #expandables > 0 then
260
local height = (the.app.height - y) / #expandables
262
for i, spr in ipairs(expandables) do
263
spr:resize(x, y + height * (i - 1), width - console.spacing, height - console.spacing)
268
-- internal function: debugger._miniEventLoop
269
-- This replicates the entire LOVE event loop, but only updates/draws
270
-- the debug console, keyboard, and mouse. This is so that after a crash or
271
-- during a break, drawing and updates still continue to happen.
274
-- forever - run indefinitely, or just for a single frame?
277
-- whether a quit event was detected, and the caller
278
-- should take an action based on this
280
debugger._miniEventLoop = function (forever)
282
local quitNow = false
288
for e, a, b, c, d in love.event.poll() do
290
if not love.quit or not love.quit() then
296
love.handlers[e](a, b, c, d)
302
elapsed = love.timer.getDelta()
305
the.keys:startFrame(elapsed)
306
the.mouse:startFrame(elapsed)
307
debugger.console:startFrame(elapsed)
308
the.keys:update(elapsed)
309
the.mouse:update(elapsed)
310
debugger.console:update(elapsed)
311
the.keys:endFrame(elapsed)
312
the.mouse:endFrame(elapsed)
313
debugger.console:endFrame(elapsed)
315
if the.keys:pressed('escape') then
316
if not love.quit or not love.quit() then
321
if love.graphics then
322
love.graphics.clear()
324
debugger.console:draw()
328
if love.timer then love.timer.sleep(0.03) end
329
if love.graphics then love.graphics.present() end