2
-- Displays a running log of output that is print()ed, and allows
3
-- interactive execution of Lua This adds a debugger.unsourcedPrint
4
-- function that allows output of text without the usual source
7
DebugConsole = DebugInstrument:extend
9
-- Property: inputHistory
10
-- A table of previously-entered commands.
13
-- Property: inputHistoryIndex
14
-- Which history entry, if any, we are displaying.
15
inputHistoryIndex = 1,
21
-- The <Text> sprite showing recent lines in the log.
24
-- The <TextInput> that the user types into to enter commands.
27
-- The <Text> sprite that shows a > in front of commands.
29
onNew = function (self)
30
self.title.text = 'Console'
32
self.log = self:add(Text:new{ font = self.font })
33
self.prompt = self:add(Text:new{ font = self.font, text = '>' })
35
local w = self.prompt:getSize()
37
self.lineHeight = self.log._fontObj:getHeight()
39
self.input = self:add(TextInput:new
42
onType = function (self, char)
43
return char ~= debugger.consoleKey
47
-- hijack print function
48
-- this is nasty to debug if it goes wrong, be careful
50
self._oldPrint = print
52
print = function (...)
53
local caller = debug.getinfo(2, 'Sl')
55
if caller.linedefined ~= 0 then
56
self.log.text = self.log.text .. '(' .. caller.short_src .. ':' .. (caller.currentline or caller.linedefined) .. ') '
59
for _, value in pairs{...} do
60
self.log.text = self.log.text .. tostring(value) .. ' '
63
self.log.text = self.log.text .. '\n'
64
self._updateLog = true
68
debugger.unsourcedPrint = function (...)
69
for _, value in pairs{...} do
70
self.log.text = self.log.text .. tostring(value) .. ' '
73
self.log.text = self.log.text .. '\n'
74
self._updateLog = true
78
-- This replaces the default love.errhand() method, displaying
79
-- a stack trace and allowing inspection of the state of things.
81
debugger._handleCrash = function (message)
82
if debugger.crashed then
83
debugger._originalErrhand(message)
87
debugger.crashed = true
88
local print = debugger.unsourcedPrint or print
92
love.mouse.setVisible(true)
94
print('\n' .. string.rep('=', 40) .. '\n')
95
print('Crash, ' .. message)
97
-- print offending source line where possible
99
local crashState = debug.getinfo(3, 'Sl')
101
if string.find(crashState.source, '^@') then
102
print('\n>>> ' .. debugger.sourceLine(string.gsub(crashState.source, '^@', ''), crashState.currentline) .. '\n')
105
-- print or show stack and locals
107
if debugger.showStack then
108
debugger.showStack(5)
110
print(debug.traceback('', 3))
113
if debugger.showLocals then
114
debugger.showLocals(5)
116
-- move locals into global context
117
-- http://www.lua.org/pil/23.1.1.html
122
local name, value = debug.getlocal(4, i)
123
if not name then break end
125
if (not string.find(name, ' ')) then
133
print('\nlocal variables:')
135
-- http://www.lua.org/pil/23.1.1.html
141
local name, value = debug.getlocal(4, i)
142
if not name then break end
144
if (not string.find(name, ' ')) then
145
table.insert(localVars, name)
152
table.sort(localVars)
154
for _, name in pairs(localVars) do
157
if type(_G[name]) == 'string' then
158
val = "'" .. string.gsub(_G[name], "'", "\\'") .. "'"
160
val = tostring(_G[name])
163
print(name .. ': ' .. val)
167
print(string.rep('=', 40) .. '\n')
168
debugger.showConsole()
170
if debugger._miniEventLoop then debugger._miniEventLoop(true) end
175
-- Safely executes a string of code and prints the result.
178
-- code - string code to execute
183
execute = function (self, code)
184
if string.sub(code, 1, 1) == '=' then
185
code = 'debugger.unsourcedPrint (' .. string.sub(code, 2) .. ')'
188
local func, err = loadstring(code)
191
local ok, result = pcall(func)
194
debugger.unsourcedPrint('Error, ' .. tostring(result) .. '\n')
196
debugger.unsourcedPrint('')
199
return tostring(result)
201
debugger.unsourcedPrint('Syntax error, ' .. string.gsub(tostring(err), '^.*:', '') .. '\n')
205
onUpdate = function (self, elapsed)
206
-- update the log contents if output is waiting
208
if self._updateLog then
209
local _, height = self.log:getSize()
210
local linesToDelete = math.ceil((height - self.log.height) / self.lineHeight)
212
if linesToDelete > 0 then
213
self.log.text = string.gsub(self.log.text, '.-\n', '', linesToDelete)
216
_, height = self.log:getSize()
218
self.prompt.y = self.log.y + height
219
self.input.y = self.log.y + height
220
self._updateLog = false
223
-- control keys to jump to different sides and erase everything
225
if the.keys:pressed('ctrl') and the.keys:justPressed('a') then
229
if the.keys:pressed('ctrl') and the.keys:justPressed('e') then
230
self.input.caret = string.len(self.input.text)
233
if the.keys:pressed('ctrl') and the.keys:justPressed('k') then
238
-- up and down arrows cycle through history
240
if the.keys:justPressed('up') and self.inputHistoryIndex > 1 then
241
-- save what the user was in the middle of typing
243
self.inputHistory[self.inputHistoryIndex] = self.input.text
245
self.input.text = self.inputHistory[self.inputHistoryIndex - 1]
246
self.input.caret = string.len(self.input.text)
247
self.inputHistoryIndex = self.inputHistoryIndex - 1
250
if the.keys:justPressed('down') and self.inputHistoryIndex < #self.inputHistory then
251
self.input.text = self.inputHistory[self.inputHistoryIndex + 1]
252
self.input.caret = string.len(self.input.text)
253
self.inputHistoryIndex = self.inputHistoryIndex + 1
258
if the.keys:justPressed('return') then
259
debugger.unsourcedPrint('>' .. self.input.text)
260
self:execute(self.input.text)
261
table.insert(self.inputHistory, self.inputHistoryIndex, self.input.text)
263
while #self.inputHistory > self.inputHistoryIndex do
264
table.remove(self.inputHistory)
267
self.inputHistoryIndex = self.inputHistoryIndex + 1
273
onResize = function (self, x, y, width, height)
274
self.log.x = x + self.spacing
275
self.log.y = y + self.spacing
276
self.log.width = width - self.spacing * 2
277
self.log.height = height - self.spacing * 2
279
self.prompt.x = self.log.x
280
self.input.x = self.prompt.x + self.inputIndent
281
self.input.width = width - self.inputIndent
283
self._updateLog = true