2
DebugConsole = DebugInstrument:extend
4
-- Property: inputHistory
5
-- A table of previously-entered commands.
8
-- Property: inputHistoryIndex
9
-- Which history entry, if any, we are displaying.
10
inputHistoryIndex = 1,
16
-- The <Text> sprite showing recent lines in the log.
19
-- The <TextInput> that the user types into to enter commands.
22
-- The <Text> sprite that shows a > in front of commands.
24
onNew = function (self)
25
self.title.text = 'Console'
27
self.log = self:add(Text:new{ font = self.font })
28
self.prompt = self:add(Text:new{ font = self.font, text = '>' })
30
local w = self.prompt:getSize()
32
self.lineHeight = self.log._fontObj:getHeight()
34
self.input = self:add(TextInput:new
37
onType = function (self, char)
38
return char ~= debugger.consoleKey
42
-- hijack print function
43
-- this is nasty to debug if it goes wrong, be careful
45
self._oldPrint = print
47
print = function (...)
48
local caller = debug.getinfo(2, 'Sl')
50
if caller.linedefined ~= 0 then
51
self.log.text = self.log.text .. '(' .. caller.short_src .. ':' .. (caller.currentline or caller.linedefined) .. ') '
54
for _, value in pairs{...} do
55
self.log.text = self.log.text .. tostring(value) .. ' '
58
self.log.text = self.log.text .. '\n'
59
self._updateLog = true
63
debugger.unsourcedPrint = function (...)
64
for _, value in pairs{...} do
65
self.log.text = self.log.text .. tostring(value) .. ' '
68
self.log.text = self.log.text .. '\n'
69
self._updateLog = true
73
-- This replaces the default love.errhand() method, displaying
74
-- a stack trace and allowing inspection of the state of things.
76
debugger._handleCrash = function (message)
77
if debugger.crashed then
78
debugger._originalErrhand(message)
82
debugger.crashed = true
83
local print = debugger.unsourcedPrint or print
87
love.mouse.setVisible(true)
89
print('\n' .. string.rep('=', 40) .. '\n')
90
print('Crash, ' .. message)
92
-- print offending source line where possible
94
local crashState = debug.getinfo(3, 'Sl')
96
if string.find(crashState.source, '^@') then
97
print('\n>>> ' .. debugger.sourceLine(string.gsub(crashState.source, '^@', ''), crashState.currentline) .. '\n')
100
-- print or show stack and locals
102
if debugger.showStack then
103
debugger.showStack(5)
105
print(debug.traceback('', 3))
108
if debugger.showLocals then
109
debugger.showLocals(5)
111
-- move locals into global context
112
-- http://www.lua.org/pil/23.1.1.html
117
local name, value = debug.getlocal(4, i)
118
if not name then break end
120
if (not string.find(name, ' ')) then
128
print('\nlocal variables:')
130
-- http://www.lua.org/pil/23.1.1.html
136
local name, value = debug.getlocal(4, i)
137
if not name then break end
139
if (not string.find(name, ' ')) then
140
table.insert(localVars, name)
147
table.sort(localVars)
149
for _, name in pairs(localVars) do
152
if type(_G[name]) == 'string' then
153
val = "'" .. string.gsub(_G[name], "'", "\\'") .. "'"
155
val = tostring(_G[name])
158
print(name .. ': ' .. val)
162
print(string.rep('=', 40) .. '\n')
163
debugger.showConsole()
165
if debugger._miniEventLoop then debugger._miniEventLoop(true) end
170
-- Safely executes a string of code and prints the result.
173
-- code - string code to execute
178
execute = function (self, code)
179
if string.sub(code, 1, 1) == '=' then
180
code = 'debugger.unsourcedPrint (' .. string.sub(code, 2) .. ')'
183
local func, err = loadstring(code)
186
local ok, result = pcall(func)
189
debugger.unsourcedPrint('Error, ' .. tostring(result) .. '\n')
191
debugger.unsourcedPrint('')
194
return tostring(result)
196
debugger.unsourcedPrint('Syntax error, ' .. string.gsub(tostring(err), '^.*:', '') .. '\n')
200
onUpdate = function (self, elapsed)
201
-- update the log contents if output is waiting
203
if self._updateLog then
204
local _, height = self.log:getSize()
205
local linesToDelete = math.ceil((height - self.log.height) / self.lineHeight)
207
if linesToDelete > 0 then
208
self.log.text = string.gsub(self.log.text, '.-\n', '', linesToDelete)
211
_, height = self.log:getSize()
213
self.prompt.y = self.log.y + height
214
self.input.y = self.log.y + height
215
self._updateLog = false
218
-- control keys to jump to different sides and erase everything
220
if the.keys:pressed('ctrl') and the.keys:justPressed('a') then
224
if the.keys:pressed('ctrl') and the.keys:justPressed('e') then
225
self.input.caret = string.len(self.input.text)
228
if the.keys:pressed('ctrl') and the.keys:justPressed('k') then
233
-- up and down arrows cycle through history
235
if the.keys:justPressed('up') and self.inputHistoryIndex > 1 then
236
-- save what the user was in the middle of typing
238
self.inputHistory[self.inputHistoryIndex] = self.input.text
240
self.input.text = self.inputHistory[self.inputHistoryIndex - 1]
241
self.input.caret = string.len(self.input.text)
242
self.inputHistoryIndex = self.inputHistoryIndex - 1
245
if the.keys:justPressed('down') and self.inputHistoryIndex < #self.inputHistory then
246
self.input.text = self.inputHistory[self.inputHistoryIndex + 1]
247
self.input.caret = string.len(self.input.text)
248
self.inputHistoryIndex = self.inputHistoryIndex + 1
253
if the.keys:justPressed('return') then
254
debugger.unsourcedPrint('>' .. self.input.text)
255
self:execute(self.input.text)
256
table.insert(self.inputHistory, self.inputHistoryIndex, self.input.text)
258
while #self.inputHistory > self.inputHistoryIndex do
259
table.remove(self.inputHistory)
262
self.inputHistoryIndex = self.inputHistoryIndex + 1
268
onResize = function (self, x, y, width, height)
269
self.log.x = x + self.spacing
270
self.log.y = y + self.spacing
271
self.log.width = width - self.spacing * 2
272
self.log.height = height - self.spacing * 2
274
self.prompt.x = self.log.x
275
self.input.x = self.prompt.x + self.inputIndent
276
self.input.width = width - self.inputIndent
278
self._updateLog = true