/ld27

To get this branch, use:
bzr branch /bzr/ld27
35 by Josh C
cluke009 zoetrope + my spritebatch changes
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