/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/console.lua

  • Committer: Josh C
  • Date: 2013-08-25 00:32:21 UTC
  • Revision ID: josh@9ix.org-20130825003221-632jbug07yhqabuo
pickĀ upĀ goals

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
DebugConsole = DebugInstrument:extend
3
 
{
4
 
        -- Property: inputHistory
5
 
        -- A table of previously-entered commands.
6
 
        inputHistory = {},
7
 
 
8
 
        -- Property: inputHistoryIndex
9
 
        -- Which history entry, if any, we are displaying.
10
 
        inputHistoryIndex = 1,
11
 
 
12
 
        width = 'wide',
13
 
        contentHeight = '*',
14
 
 
15
 
        -- Property: log
16
 
        -- The <Text> sprite showing recent lines in the log.
17
 
 
18
 
        -- Property: input
19
 
        -- The <TextInput> that the user types into to enter commands.
20
 
 
21
 
        -- Property: prompt
22
 
        -- The <Text> sprite that shows a > in front of commands.
23
 
 
24
 
        onNew = function (self)
25
 
                self.title.text = 'Console'
26
 
 
27
 
                self.log = self:add(Text:new{ font = self.font })
28
 
                self.prompt = self:add(Text:new{ font = self.font, text = '>' })
29
 
 
30
 
                local w = self.prompt:getSize()
31
 
                self.inputIndent = w
32
 
                self.lineHeight = self.log._fontObj:getHeight()
33
 
 
34
 
                self.input = self:add(TextInput:new
35
 
                {
36
 
                        font = self.font,
37
 
                        onType = function (self, char)
38
 
                                return char ~= debugger.consoleKey
39
 
                        end
40
 
                })
41
 
 
42
 
                -- hijack print function
43
 
                -- this is nasty to debug if it goes wrong, be careful
44
 
 
45
 
                self._oldPrint = print
46
 
 
47
 
                print = function (...)
48
 
                        local caller = debug.getinfo(2, 'Sl')
49
 
 
50
 
                        if caller.linedefined ~= 0 then
51
 
                                self.log.text = self.log.text .. '(' .. caller.short_src .. ':' .. (caller.currentline or caller.linedefined) .. ') '
52
 
                        end
53
 
 
54
 
                        for _, value in pairs{...} do
55
 
                                self.log.text = self.log.text .. tostring(value) .. ' '
56
 
                        end
57
 
 
58
 
                        self.log.text = self.log.text .. '\n'
59
 
                        self._updateLog = true
60
 
                        self._oldPrint(...)
61
 
                end
62
 
 
63
 
                debugger.unsourcedPrint = function (...)
64
 
                        for _, value in pairs{...} do
65
 
                                self.log.text = self.log.text .. tostring(value) .. ' '
66
 
                        end
67
 
 
68
 
                        self.log.text = self.log.text .. '\n'
69
 
                        self._updateLog = true
70
 
                        self._oldPrint(...)
71
 
                end
72
 
 
73
 
                -- This replaces the default love.errhand() method, displaying
74
 
                -- a stack trace and allowing inspection of the state of things.
75
 
 
76
 
                debugger._handleCrash = function (message)
77
 
                        if debugger.crashed then
78
 
                                debugger._originalErrhand(message)
79
 
                                return
80
 
                        end
81
 
 
82
 
                        debugger.crashed = true
83
 
                        local print = debugger.unsourcedPrint or print
84
 
                        debug.sethook()
85
 
                        setmetatable(_G, nil)
86
 
                        love.audio.stop()
87
 
                        love.mouse.setVisible(true)
88
 
 
89
 
                        print('\n' .. string.rep('=', 40) .. '\n')
90
 
                        print('Crash, ' .. message)
91
 
 
92
 
                        -- print offending source line where possible
93
 
 
94
 
                        local crashState = debug.getinfo(3, 'Sl')
95
 
                        
96
 
                        if string.find(crashState.source, '^@') then
97
 
                                print('\n>>> ' .. debugger.sourceLine(string.gsub(crashState.source, '^@', ''), crashState.currentline) .. '\n')
98
 
                        end
99
 
                        
100
 
                        -- print or show stack and locals
101
 
 
102
 
                        if debugger.showStack then
103
 
                                debugger.showStack(5)
104
 
                        else
105
 
                                print(debug.traceback('', 3))
106
 
                        end
107
 
 
108
 
                        if debugger.showLocals then
109
 
                                debugger.showLocals(5)
110
 
 
111
 
                                -- move locals into global context
112
 
                                -- http://www.lua.org/pil/23.1.1.html
113
 
 
114
 
                                local i = 1
115
 
 
116
 
                                while true do
117
 
                                        local name, value = debug.getlocal(4, i)
118
 
                                        if not name then break end
119
 
 
120
 
                                        if (not string.find(name, ' ')) then
121
 
                                                _G[name] = value
122
 
                                        end
123
 
                                         
124
 
                                        i = i + 1
125
 
                                end
126
 
 
127
 
                        else
128
 
                                print('\nlocal variables:')
129
 
 
130
 
                                -- http://www.lua.org/pil/23.1.1.html
131
 
 
132
 
                                local i = 1
133
 
                                local localVars = {}
134
 
 
135
 
                                while true do
136
 
                                        local name, value = debug.getlocal(4, i)
137
 
                                        if not name then break end
138
 
 
139
 
                                        if (not string.find(name, ' ')) then
140
 
                                                table.insert(localVars, name)
141
 
                                                _G[name] = value
142
 
                                        end
143
 
                                         
144
 
                                        i = i + 1
145
 
                                end
146
 
 
147
 
                                table.sort(localVars)
148
 
 
149
 
                                for _, name in pairs(localVars) do
150
 
                                        local val
151
 
 
152
 
                                        if type(_G[name]) == 'string' then
153
 
                                                val = "'" .. string.gsub(_G[name], "'", "\\'") .. "'"
154
 
                                        else
155
 
                                                val = tostring(_G[name])
156
 
                                        end
157
 
 
158
 
                                        print(name .. ': ' .. val)
159
 
                                end
160
 
                        end
161
 
 
162
 
                        print(string.rep('=', 40) .. '\n')
163
 
                        debugger.showConsole()
164
 
 
165
 
                        if debugger._miniEventLoop then debugger._miniEventLoop(true) end
166
 
                end
167
 
        end,
168
 
 
169
 
        -- Method: execute
170
 
        -- Safely executes a string of code and prints the result.
171
 
        --
172
 
        -- Arguments:
173
 
        --              code - string code to execute
174
 
        --
175
 
        -- Returns:
176
 
        --              string result
177
 
 
178
 
        execute = function (self, code)
179
 
                if string.sub(code, 1, 1) == '=' then
180
 
                        code = 'debugger.unsourcedPrint (' .. string.sub(code, 2) .. ')'
181
 
                end
182
 
 
183
 
                local func, err = loadstring(code)
184
 
 
185
 
                if func then
186
 
                        local ok, result = pcall(func)
187
 
 
188
 
                        if not ok then
189
 
                                debugger.unsourcedPrint('Error, ' .. tostring(result) .. '\n')
190
 
                        else
191
 
                                debugger.unsourcedPrint('')
192
 
                        end
193
 
 
194
 
                        return tostring(result)
195
 
                else
196
 
                        debugger.unsourcedPrint('Syntax error, ' .. string.gsub(tostring(err), '^.*:', '') .. '\n')
197
 
                end
198
 
        end,
199
 
 
200
 
        onUpdate = function (self, elapsed)
201
 
                -- update the log contents if output is waiting
202
 
 
203
 
                if self._updateLog then
204
 
                        local _, height = self.log:getSize()
205
 
                        local linesToDelete = math.ceil((height - self.log.height) / self.lineHeight)
206
 
                        
207
 
                        if linesToDelete > 0 then
208
 
                                self.log.text = string.gsub(self.log.text, '.-\n', '', linesToDelete) 
209
 
                        end
210
 
                        
211
 
                        _, height = self.log:getSize()
212
 
 
213
 
                        self.prompt.y = self.log.y + height
214
 
                        self.input.y = self.log.y + height
215
 
                        self._updateLog = false
216
 
                end
217
 
 
218
 
                -- control keys to jump to different sides and erase everything
219
 
 
220
 
                if the.keys:pressed('ctrl') and the.keys:justPressed('a') then
221
 
                        self.input.caret = 0
222
 
                end
223
 
 
224
 
                if the.keys:pressed('ctrl') and the.keys:justPressed('e') then
225
 
                        self.input.caret = string.len(self.input.text)
226
 
                end
227
 
 
228
 
                if the.keys:pressed('ctrl') and the.keys:justPressed('k') then
229
 
                        self.input.caret = 0
230
 
                        self.input.text = ''
231
 
                end
232
 
 
233
 
                -- up and down arrows cycle through history
234
 
 
235
 
                if the.keys:justPressed('up') and self.inputHistoryIndex > 1 then
236
 
                        -- save what the user was in the middle of typing
237
 
 
238
 
                        self.inputHistory[self.inputHistoryIndex] = self.input.text
239
 
 
240
 
                        self.input.text = self.inputHistory[self.inputHistoryIndex - 1]
241
 
                        self.input.caret = string.len(self.input.text)
242
 
                        self.inputHistoryIndex = self.inputHistoryIndex - 1
243
 
                end
244
 
 
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
249
 
                end
250
 
 
251
 
                -- return executes
252
 
 
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)
257
 
 
258
 
                        while #self.inputHistory > self.inputHistoryIndex do
259
 
                                table.remove(self.inputHistory)
260
 
                        end
261
 
 
262
 
                        self.inputHistoryIndex = self.inputHistoryIndex + 1
263
 
                        self.input.text = ''
264
 
                        self.input.caret = 0
265
 
                end
266
 
        end,
267
 
 
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
273
 
 
274
 
                self.prompt.x = self.log.x
275
 
                self.input.x = self.prompt.x + self.inputIndent
276
 
                self.input.width = width - self.inputIndent
277
 
 
278
 
                self._updateLog = true
279
 
        end
280
 
}