/ld27

To get this branch, use:
bzr branch http://9ix.org/bzr/ld27

« back to all changes in this revision

Viewing changes to zoetrope/debug/debugger.lua

  • Committer: Josh C
  • Date: 2014-07-06 22:51:43 UTC
  • Revision ID: josh@9ix.org-20140706225143-q72jn0va7v4ssvyy
ignore lovebird, inspect

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
debugger = debugger or {}
 
2
 
 
3
-- Property: debugger.consoleKey
 
4
-- What key toggles visibility. By default, this is the tab key.
 
5
debugger.consoleKey = 'tab'
 
6
 
 
7
-- Property: debugger.crashed
 
8
-- Records whether the app has crashed.
 
9
debugger.crashed = false
 
10
 
 
11
-- internal property: debugger._sourceCache
 
12
-- A cache of source files used by debugger.sourceLine().
 
13
debugger._sourceCache = {}
 
14
 
 
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
 
18
-- handler.
 
19
--
 
20
-- Arguments:
 
21
--              Instrument classes to add, defaults to all available
 
22
--
 
23
-- Returns:
 
24
--              nothing
 
25
 
 
26
debugger.init = function()
 
27
        debugger.console = Group:new
 
28
        {
 
29
                visible = false,
 
30
                spacing = 10,
 
31
                instruments = { narrow = Group:new(), wide = Group:new() },
 
32
                widths = { wide = 0.7, narrow = 0.3 },
 
33
                listeners = {},
 
34
 
 
35
                _instrumentHeights = {},
 
36
 
 
37
                onNew = function (self)
 
38
                        self:add(self.instruments.narrow)
 
39
                        self:add(self.instruments.wide)
 
40
                end,
 
41
 
 
42
                update = function (self, elapsed)
 
43
                        if the.keys:justPressed(debugger.consoleKey) then
 
44
                                if self.visible then
 
45
                                        debugger.hideConsole()
 
46
                                else
 
47
                                        debugger.showConsole()
 
48
                                end
 
49
                        end
 
50
 
 
51
                        for _, listener in pairs(self.listeners) do
 
52
                                listener()
 
53
                        end
 
54
 
 
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()
 
59
                                        end
 
60
                                end
 
61
 
 
62
                                Group.update(self, elapsed)
 
63
                        end
 
64
                end
 
65
        }
 
66
 
 
67
        the.app.meta:add(debugger.console)
 
68
 
 
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())
 
76
end
 
77
 
 
78
-- Function: debugger.showConsole()
 
79
-- Makes the console visible.
 
80
--
 
81
-- Arguments:
 
82
--              none
 
83
--
 
84
-- Returns:
 
85
--              nothing
 
86
 
 
87
debugger.showConsole = function()
 
88
        debugger.console.visible = true
 
89
end
 
90
 
 
91
-- Function: debugger.hideConsole
 
92
-- Makes the console invisible. If the app has crashed,
 
93
-- this has no effect.
 
94
--
 
95
-- Arguments:
 
96
--              none
 
97
--
 
98
-- Returns:
 
99
--              nothing
 
100
 
 
101
debugger.hideConsole = function()
 
102
        if not debugger.crashed then
 
103
                debugger.console.visible = false
 
104
        end
 
105
end
 
106
 
 
107
-- Function: debugger.addInstrument
 
108
-- Adds an instrument to the console, creating a container and
 
109
-- tab to select it.
 
110
--
 
111
-- Arguments:
 
112
--              instrument - <Group> enclosing the entire instrument
 
113
--
 
114
-- Returns:
 
115
--              nothing
 
116
 
 
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'")
 
121
 
 
122
        console.instruments[instrument.width]:add(instrument)
 
123
        debugger._resizeInstruments()
 
124
end
 
125
 
 
126
-- Function: debugger.addListener
 
127
-- Adds a function that will be called on every frame,
 
128
-- regardless of whether the console is visible.
 
129
--
 
130
-- Arguments:
 
131
--              listener - function
 
132
--
 
133
-- Returns:
 
134
--              nothing
 
135
 
 
136
debugger.addListener = function (func)
 
137
        table.insert(debugger.console.listeners, func)
 
138
end
 
139
 
 
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
 
143
-- 
 
144
-- Arguments:
 
145
--              none
 
146
--
 
147
-- Returns:
 
148
--              nothing
 
149
 
 
150
debugger.reload = function()
 
151
        love.audio.stop()
 
152
 
 
153
        -- create local references to needed variables
 
154
        -- because we're about to blow the global scope away
 
155
 
 
156
        local initialGlobals = debugger._initialGlobals
 
157
        local initialPackages = debugger._initialPackages
 
158
        
 
159
        -- reset global scope
 
160
 
 
161
        for key, _ in pairs(_G) do
 
162
                _G[key] = initialGlobals[key]
 
163
        end
 
164
 
 
165
        -- reload main file and restart
 
166
 
 
167
        for key, _ in pairs(package.loaded) do
 
168
                if not initialPackages[key] then
 
169
                        package.loaded[key] = nil
 
170
                end
 
171
        end
 
172
 
 
173
        require('main')
 
174
        love.load()
 
175
end
 
176
 
 
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.
 
182
--
 
183
-- Arguments:
 
184
--              file - filename of the source code
 
185
--              line - line number to retrieve
 
186
--
 
187
-- Returns:
 
188
--              string source or '(source not available')
 
189
 
 
190
debugger.sourceLine = function (file, line)
 
191
        if file then
 
192
                if not debugger._sourceCache[file] then
 
193
                        debugger._sourceCache[file] = {}
 
194
 
 
195
                        for line in love.filesystem.lines(file) do
 
196
                                table.insert(debugger._sourceCache[file], line)
 
197
                        end
 
198
                end
 
199
 
 
200
                return debugger._sourceCache[file][line]
 
201
        else
 
202
                return '(source not available)'
 
203
        end
 
204
end
 
205
 
 
206
debugger._resizeInstruments = function()
 
207
        local console = debugger.console
 
208
 
 
209
        -- wide instruments
 
210
 
 
211
        local x = console.spacing
 
212
        local y = console.spacing
 
213
        local width = the.app.width * console.widths.wide
 
214
        local expandables = {}
 
215
        
 
216
        for _, spr in pairs(console.instruments.wide.sprites) do
 
217
                if spr.visible then
 
218
                        local height = spr:totalHeight()
 
219
                        console._instrumentHeights[spr] = spr.contentHeight
 
220
 
 
221
                        if height == '*' then
 
222
                                table.insert(expandables, spr)
 
223
                        else
 
224
                                spr:resize(x, y, width - console.spacing, height)
 
225
                                y = y + height + console.spacing
 
226
                        end
 
227
                end
 
228
        end
 
229
 
 
230
        if #expandables > 0 then
 
231
                local height = (the.app.height - y) / #expandables
 
232
 
 
233
                for i, spr in ipairs(expandables) do
 
234
                        spr:resize(x, y + height * (i - 1), width - console.spacing, height - console.spacing)
 
235
                end
 
236
        end
 
237
 
 
238
        -- narrow instruments
 
239
 
 
240
        x = x + width
 
241
        y = console.spacing
 
242
        width = the.app.width * console.widths.narrow
 
243
        expandables = {}
 
244
 
 
245
        for _, spr in pairs(console.instruments.narrow.sprites) do
 
246
                if spr.visible then
 
247
                        local height = spr:totalHeight()
 
248
                        console._instrumentHeights[spr] = spr.contentHeight
 
249
 
 
250
                        if height == '*' then
 
251
                                table.insert(expandables, spr)
 
252
                        else
 
253
                                spr:resize(x, y, width - 2 * console.spacing, height)
 
254
                                y = y + height + console.spacing
 
255
                        end
 
256
                end
 
257
        end
 
258
 
 
259
        if #expandables > 0 then
 
260
                local height = (the.app.height - y) / #expandables
 
261
 
 
262
                for i, spr in ipairs(expandables) do
 
263
                        spr:resize(x, y + height * (i - 1), width - console.spacing, height - console.spacing)
 
264
                end
 
265
        end
 
266
end
 
267
 
 
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.
 
272
--
 
273
-- Arguments:
 
274
--              forever - run indefinitely, or just for a single frame?
 
275
--
 
276
-- Returns:
 
277
--              whether a quit event was detected, and the caller
 
278
--              should take an action based on this
 
279
 
 
280
debugger._miniEventLoop = function (forever)
 
281
        local elapsed = 0
 
282
        local quitNow = false
 
283
 
 
284
        repeat
 
285
                if love.event then
 
286
                        love.event.pump()
 
287
                        
 
288
                        for e, a, b, c, d in love.event.poll() do
 
289
                                if e == 'quit' then
 
290
                                        if not love.quit or not love.quit() then
 
291
                                                quitNow = true
 
292
                                                forever = false
 
293
                                        end
 
294
                                end
 
295
 
 
296
                                love.handlers[e](a, b, c, d)
 
297
                        end
 
298
                end
 
299
 
 
300
                if love.timer then
 
301
                        love.timer.step()
 
302
                        elapsed = love.timer.getDelta()
 
303
                end
 
304
 
 
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)
 
314
 
 
315
                if the.keys:pressed('escape') then
 
316
                        if not love.quit or not love.quit() then
 
317
                                love.event.quit()
 
318
                        end
 
319
                end
 
320
 
 
321
                if love.graphics then
 
322
                        love.graphics.clear()
 
323
                        if love.draw then
 
324
                                debugger.console:draw()
 
325
                        end
 
326
                end
 
327
 
 
328
                if love.timer then love.timer.sleep(0.03) end
 
329
                if love.graphics then love.graphics.present() end
 
330
        until not forever 
 
331
 
 
332
        return quitNow
 
333
end