@@ -13,6 +13,13 @@ class PythonDebuggerView extends View
13
13
debuggedFileArgs : []
14
14
backendDebuggerPath : null
15
15
backendDebuggerName : " atom_pdb.py"
16
+ flagActionStarted : false
17
+ flagVarsNeedUpdate : false
18
+ flagCallstackNeedsUpdate : false
19
+ # 0 - normal output, 1 - print variables, 2 - print call stack
20
+ currentState : 0
21
+ varScrollTop : 0
22
+ callStackScrollTop : 0
16
23
17
24
getCurrentFilePath : ->
18
25
editor = atom .workspace .getActivePaneItem ()
@@ -32,42 +39,90 @@ class PythonDebuggerView extends View
32
39
@ subview " commandEntryView" , new TextEditorView
33
40
mini : true ,
34
41
placeholderText : " > Enter debugger commands here"
35
- @ button outlet : " breakpointBtn" , click : " toggleBreak" , class : " btn" , =>
36
- @ span " break point"
37
- @ button class : " btn" , =>
38
- @ span " "
39
- @ button outlet : " runBtn" , click : " runApp" , class : " btn" , =>
40
- @ span " run"
41
- @ button outlet : " stopBtn" , click : " stopApp" , class : " btn" , =>
42
- @ span " stop"
43
- @ button class : " btn" , =>
44
- @ span " "
45
- @ button outlet : " stepOverBtn" , click : " stepOverBtnPressed" , class : " btn" , =>
46
- @ span " next"
47
- @ button outlet : " stepInBtn" , click : " stepInBtnPressed" , class : " btn" , =>
48
- @ span " step"
49
- @ button outlet : " varBtn" , click : " varBtnPressed" , class : " btn" , =>
50
- @ span " variables"
51
- @ button class : " btn" , =>
52
- @ span " "
53
- @ button outlet : " returnBtn" , click : " returnBtnPressed" , class : " btn" , =>
54
- @ span " return"
55
- @ button outlet : " continueBtn" , click : " continueBtnPressed" , class : " btn" , =>
56
- @ span " continue"
57
- @ button class : " btn" , =>
58
- @ span " "
59
- @ button outlet : " upBtn" , click : " upBtnPressed" , class : " btn" , =>
60
- @ span " up"
61
- @ button outlet : " callstackBtn" , click : " callstackBtnPressed" , class : " btn" , =>
62
- @ span " callstack"
63
- @ button outlet : " downBtn" , click : " downBtnPressed" , class : " btn" , =>
64
- @ span " down"
65
- @ button class : " btn" , =>
66
- @ span " "
67
- @ button outlet : " clearBtn" , click : " clearOutput" , class : " btn" , =>
68
- @ span " clear"
69
- @ div class : " panel-body" , outlet : " outputContainer" , =>
70
- @ pre class : " command-output" , outlet : " output"
42
+ @ div class : " btn-toolbar" , =>
43
+ @ div class : " btn-group" , =>
44
+ @ button outlet : " breakpointBtn" , click : " toggleBreak" , class : " btn" , =>
45
+ @ span " break point"
46
+ @ div class : " btn-group" , =>
47
+ @ button outlet : " runBtn" , click : " runApp" , class : " btn" , =>
48
+ @ span " run"
49
+ @ button outlet : " stopBtn" , click : " stopApp" , class : " btn" , =>
50
+ @ span " stop"
51
+ @ div class : " btn-group" , =>
52
+ @ button outlet : " stepOverBtn" , click : " stepOverBtnPressed" , class : " btn" , =>
53
+ @ span " next"
54
+ @ button outlet : " stepInBtn" , click : " stepInBtnPressed" , class : " btn" , =>
55
+ @ span " step"
56
+ @ button outlet : " returnBtn" , click : " returnBtnPressed" , class : " btn" , =>
57
+ @ span " return"
58
+ @ button outlet : " continueBtn" , click : " continueBtnPressed" , class : " btn" , =>
59
+ @ span " continue"
60
+ @ div class : " btn-group" , =>
61
+ @ button outlet : " upBtn" , click : " upBtnPressed" , class : " btn" , =>
62
+ @ span " up"
63
+ @ button outlet : " downBtn" , click : " downBtnPressed" , class : " btn" , =>
64
+ @ span " down"
65
+ @ div class : " btn-group" , =>
66
+ @ button outlet : " clearBtn" , click : " clearOutput" , class : " btn" , =>
67
+ @ span " clear"
68
+ @ input class : " input-checkbox" , type : " checkbox" , id : " ck_input" , outlet : " showInput" , click : " toggleInput"
69
+ @ label class : " label" , for : " ck_input" , =>
70
+ @ span " Input"
71
+ @ input class : " input-checkbox" , type : " checkbox" , id : " ck_vars" , outlet : " showVars" , click : " toggleVars"
72
+ @ label class : " label" , for : " ck_vars" , =>
73
+ @ span " Variables"
74
+ @ input class : " input-checkbox" , type : " checkbox" , id : " ck_callstack" , outlet : " showCallstack" , click : " toggleCallstack"
75
+ @ label class : " label" , for : " ck_callstack" , =>
76
+ @ span " Call stack"
77
+ @ div class : " block" , outlet : " bottomPane" , =>
78
+ @ div class : " inline-block panel" , id : " outputPane" , outlet : " outputPane" , =>
79
+ @ pre class : " command-output" , outlet : " output"
80
+ @ div class : " inline-block panel" , id : " variablesPane" , outlet : " variablesPane" , =>
81
+ @ pre class : " command-output" , outlet : " variables"
82
+ @ div class : " inline-block panel" , id : " callstackPane" , outlet : " callstackPane" , =>
83
+ @ pre class : " command-output" , outlet : " callstack"
84
+
85
+ toggleInput : ->
86
+ if @backendDebugger
87
+ @argsEntryView .hide ()
88
+ if @showInput .prop (' checked' )
89
+ @commandEntryView .show ()
90
+ else
91
+ @commandEntryView .hide ()
92
+ else
93
+ if @showInput .prop (' checked' )
94
+ @argsEntryView .show ()
95
+ else
96
+ @argsEntryView .hide ()
97
+ @commandEntryView .hide ()
98
+
99
+ toggleVars : ->
100
+ @ togglePanes ()
101
+
102
+ toggleCallstack : ->
103
+ @ togglePanes ()
104
+
105
+ togglePanes : ->
106
+ n = 1
107
+ if @showVars .prop (' checked' )
108
+ @variablesPane .show ()
109
+ n = n+ 1
110
+ else
111
+ @variablesPane .hide ()
112
+ if @showCallstack .prop (' checked' )
113
+ @callstackPane .show ()
114
+ n = n+ 1
115
+ else
116
+ @callstackPane .hide ()
117
+ width = ' ' + (100 / n)+ ' %'
118
+ @outputPane .css (' width' , width)
119
+ if @showVars .prop (' checked' )
120
+ @variablesPane .css (' width' , width)
121
+ if @showCallstack .prop (' checked' )
122
+ @callstackPane .css (' width' , width)
123
+ # the following statements are used to update the information in the variables/callstack
124
+ @ setFlags ()
125
+ @backendDebugger ? .stdin .write (" print 'display option changed.'\n " )
71
126
72
127
toggleBreak : ->
73
128
editor = atom .workspace .getActiveTextEditor ()
@@ -81,30 +136,54 @@ class PythonDebuggerView extends View
81
136
for breakpoint in @breakpointStore .breakpoints
82
137
@output .append (breakpoint .toCommand () + " \n " )
83
138
84
- upBtnPressed : ->
85
- @output .empty ()
86
- @backendDebugger ? .stdin .write (" up\n bt\n " )
87
-
88
- callstackBtnPressed : ->
89
- @output .empty ()
90
- @backendDebugger ? .stdin .write (" bt\n " )
91
-
92
- downBtnPressed : ->
93
- @output .empty ()
94
- @backendDebugger ? .stdin .write (" down\n bt\n " )
95
-
96
139
stepOverBtnPressed : ->
140
+ @ setFlags ()
97
141
@backendDebugger ? .stdin .write (" n\n " )
98
142
99
143
stepInBtnPressed : ->
144
+ @ setFlags ()
100
145
@backendDebugger ? .stdin .write (" s\n " )
101
146
102
147
continueBtnPressed : ->
148
+ @ setFlags ()
103
149
@backendDebugger ? .stdin .write (" c\n " )
104
150
105
151
returnBtnPressed : ->
152
+ @ setFlags ()
106
153
@backendDebugger ? .stdin .write (" r\n " )
107
154
155
+ upBtnPressed : ->
156
+ @ setFlags ()
157
+ @backendDebugger ? .stdin .write (" up\n " )
158
+
159
+ downBtnPressed : ->
160
+ @ setFlags ()
161
+ @backendDebugger ? .stdin .write (" down\n " )
162
+
163
+ printVars : ->
164
+ @variables .empty ()
165
+ @backendDebugger ? .stdin .write (" print ('@{variables_start}')\n " )
166
+ @backendDebugger ? .stdin .write (" for (__k, __v) in [(__k, __v) for __k, __v in globals().items() if not __k.startswith('__')]: print __k, '=', __v\n " )
167
+ @backendDebugger ? .stdin .write (" print '-------------'\n " )
168
+ @backendDebugger ? .stdin .write (" for (__k, __v) in [(__k, __v) for __k, __v in locals().items() if __k != 'self' and not __k.startswith('__')]: print __k, '=', __v\n " )
169
+ @backendDebugger ? .stdin .write (" for (__k, __v) in [(__k, __v) for __k, __v in (self.__dict__ if 'self' in locals().keys() else {}).items()]: print 'self.{0}'.format(__k), '=', __v\n " )
170
+ @backendDebugger ? .stdin .write (" print ('@{variables_end}')\n " )
171
+
172
+ printCallstack : ->
173
+ @callstack .empty ()
174
+ @backendDebugger ? .stdin .write (" print ('@{callstack_start}')\n " )
175
+ @backendDebugger ? .stdin .write (" bt\n " )
176
+ @backendDebugger ? .stdin .write (" print ('@{callstack_end}')\n " )
177
+
178
+ setFlags : ->
179
+ @flagActionStarted = true
180
+ if @showVars .prop (' checked' )
181
+ @varScrollTop = @variables .prop (' scrollTop' )
182
+ @flagVarsNeedUpdate = true
183
+ if @showCallstack .prop (' checked' )
184
+ @callStackScrollTop = @callstack .prop (' scrollTop' )
185
+ @flagCallstackNeedsUpdate = true
186
+
108
187
workspacePath : ->
109
188
editor = atom .workspace .getActiveTextEditor ()
110
189
activePath = editor .getPath ()
@@ -120,41 +199,52 @@ class PythonDebuggerView extends View
120
199
if @ pathsNotSet ()
121
200
@ askForPaths ()
122
201
return
202
+ @ setFlags ()
123
203
@ runBackendDebugger ()
204
+ @ toggleInput ()
124
205
125
- varBtnPressed : ->
126
- @output .empty ()
206
+ highlightLineInEditor : (fileName , lineNumber ) ->
207
+ if lineNumber && fileName
208
+ lineNumber = parseInt (lineNumber)
209
+ editor = atom .workspace .getActiveTextEditor ()
210
+ if fileName .toLowerCase () == editor .getPath ().toLowerCase ()
211
+ position = Point (lineNumber- 1 , 0 )
212
+ editor .setCursorBufferPosition (position)
213
+ editor .unfoldBufferRow (lineNumber)
214
+ editor .scrollToBufferPosition (position)
215
+ else
216
+ options = {initialLine : lineNumber- 1 , initialColumn : 0 }
217
+ atom .workspace .open (fileName, options) if fs .existsSync (fileName)
218
+ # TODO: add decoration to current line?
127
219
128
- @backendDebugger ? .stdin .write (" for (__k, __v) in [(__k, __v) for __k, __v in globals().items() if not __k.startswith('__')]: print __k, '=', __v\n " )
129
- @backendDebugger ? .stdin .write (" print '-------------'\n " )
130
- @backendDebugger ? .stdin .write (" for (__k, __v) in [(__k, __v) for __k, __v in locals().items() if __k != 'self' and not __k.startswith('__')]: print __k, '=', __v\n " )
131
- @backendDebugger ? .stdin .write (" for (__k, __v) in [(__k, __v) for __k, __v in (self.__dict__ if 'self' in locals().keys() else {}).items()]: print 'self.{0}'.format(__k), '=', __v\n " )
132
-
133
-
220
+ processNormalOutput : (data_str ) ->
134
221
135
- # Extract the file name and line number output by the debugger.
136
- processDebuggerOutput : (data ) ->
137
- data_str = data .toString ().trim ()
138
222
lineNumber = null
139
223
fileName = null
140
- call_stack_str = " Call stack: \n "
141
224
142
- m = / [^ -] > (. * [. ] py)[(] ([0-9 ] * )[)] . * / .exec (data_str)
143
- if m
144
- [fileName , lineNumber ] = [m[1 ], m[2 ]]
145
- `
146
- re = / [\n ] (>* )[ \t ] * (. * [. ] py)[(] ([0-9 ] * )[)] ([^ \n ] * )[\n ] ([^ \n ] * )/ gi ;
147
- while ((match = re .exec (data_str)))
148
- {
149
- if (match[1 ].includes (' >' ))
150
- call_stack_str += ' --> ' ;
151
- else
152
- call_stack_str += ' ' ;
153
- call_stack_str += match[5 ].replace (" ->" , " " ) + " in " + match[4 ] + " @ " + match [2 ] + " : " + match[3 ] + " \n " ;
154
- }
155
- `
156
- data_str = call_stack_str
157
-
225
+ # print the action_end string
226
+ if @flagActionStarted
227
+ @backendDebugger ? .stdin .write (" print ('@{action_end}')\n " )
228
+ @flagActionStarted = false
229
+
230
+ # detect predefined flag strings
231
+ isActionEnd = data_str .includes (' @{action_end}' )
232
+ isVarsStart = data_str .includes (' @{variables_start}' )
233
+ isCallstackStart = data_str .includes (' @{callstack_start}' )
234
+
235
+ # variables print started
236
+ if isVarsStart
237
+ @currentState = 1
238
+ @ processVariables (data_str)
239
+ return
240
+
241
+ # call stack print started
242
+ if isCallstackStart
243
+ @currentState = 2
244
+ @ processCallstack (data_str)
245
+ return
246
+
247
+ # handle normal output
158
248
[data_str , tail ] = data_str .split (" line:: " )
159
249
if tail
160
250
[lineNumber , tail ] = tail .split (" \n " )
@@ -167,20 +257,72 @@ class PythonDebuggerView extends View
167
257
fileName = fileName .trim () if fileName
168
258
fileName = null if fileName == " <string>"
169
259
260
+ # highlight the current line
170
261
if lineNumber && fileName
171
- lineNumber = parseInt (lineNumber)
172
- editor = atom .workspace .getActiveTextEditor ()
173
- if fileName .toLowerCase () == editor .getPath ().toLowerCase ()
174
- position = Point (lineNumber- 1 , 0 )
175
- editor .setCursorBufferPosition (position)
176
- editor .unfoldBufferRow (lineNumber)
177
- editor .scrollToBufferPosition (position)
262
+ @ highlightLineInEditor (fileName, lineNumber)
263
+
264
+ # print the output
265
+ @ addOutput (data_str .trim ().replace (' @{action_end}' , ' ' ))
266
+
267
+ # if action end, trigger the follow up actions
268
+ if isActionEnd
269
+ if @flagVarsNeedUpdate
270
+ @ printVars ()
271
+ @flagVarsNeedUpdate = false
178
272
else
179
- options = {initialLine : lineNumber- 1 , initialColumn : 0 }
180
- atom .workspace .open (fileName, options) if fs .existsSync (fileName)
181
- # TODO: add decoration to current line?
273
+ if @flagCallstackNeedsUpdate
274
+ @ printCallstack ()
275
+ @flagCallstackNeedsUpdate = false
276
+
277
+ processVariables : (data_str ) ->
278
+ isVarsEnd = data_str .includes (' @{variables_end}' )
279
+ for line in data_str .split ' \n '
280
+ if ! line .includes (" @{variable" )
281
+ @variables .append (@ createOutputNode (line))
282
+ @variables .append (' \n ' )
283
+ if isVarsEnd
284
+ @variables .prop (' scrollTop' , @varScrollTop )
285
+ @currentState = 0
286
+ if @flagCallstackNeedsUpdate
287
+ @ printCallstack ()
288
+ @flagCallstackNeedsUpdate = false
289
+
290
+ processCallstack : (data_str ) ->
291
+ lineNumber = null
292
+ fileName = null
293
+ isCallstackEnd = data_str .includes (' @{callstack_end}' )
294
+ m = / [^ -] > (. * [. ] py)[(] ([0-9 ] * )[)] . * / .exec (data_str)
295
+ if m
296
+ [fileName , lineNumber ] = [m[1 ], m[2 ]]
297
+ callstack_pre = @callstack
298
+ `
299
+ re = / [\n ] (>* )[ \t ] * (. * [. ] py)[(] ([0-9 ] * )[)] ([^ \n ] * )[\n ] ([^ \n ] * )/ gi ;
300
+ while ((match = re .exec (data_str)))
301
+ {
302
+ if (match[5 ].includes (' exec cmd in globals, locals' )) continue ;
303
+ if (match[1 ].includes (' >' ))
304
+ item = " <b><u>" + match[5 ].replace (" ->" , " " )+ " </u></b>" ;
305
+ else
306
+ item = match[5 ].replace (" ->" , " " );
307
+ callstack_pre .append (item);
308
+ callstack_pre .append (' \n ' );
309
+ }
310
+ `
311
+ if lineNumber && fileName
312
+ @ highlightLineInEditor (fileName, lineNumber)
313
+ if isCallstackEnd
314
+ @currentState = 0
315
+ @callstack .prop (' scrollTop' , @callStackScrollTop )
182
316
183
- @ addOutput (data_str .trim ())
317
+ # Extract the file name and line number output by the debugger.
318
+ processDebuggerOutput : (data ) ->
319
+ data_str = data .toString ().trim ()
320
+ if @currentState == 1
321
+ @ processVariables (data_str)
322
+ else if @currentState == 2
323
+ @ processCallstack (data_str)
324
+ else
325
+ @ processNormalOutput (data_str)
184
326
185
327
runBackendDebugger : ->
186
328
args = [path .join (@backendDebuggerPath , @backendDebuggerName )]
@@ -208,6 +350,7 @@ class PythonDebuggerView extends View
208
350
@backendDebugger ? .stdin .write (" \n exit()\n " )
209
351
@backendDebugger = null
210
352
console .log " debugger stopped"
353
+ @ toggleInput ()
211
354
212
355
clearOutput : ->
213
356
@output .empty ()
@@ -234,6 +377,8 @@ class PythonDebuggerView extends View
234
377
@breakpointStore = breakpointStore
235
378
@debuggedFileName = @ getCurrentFilePath ()
236
379
@backendDebuggerPath = @ getDebuggerPath ()
380
+ @ toggleInput ()
381
+ @ togglePanes ()
237
382
@ addOutput (" Welcome to Python Debugger for Atom!" )
238
383
@ addOutput (" The file being debugged is: " + @debuggedFileName )
239
384
@ askForPaths ()
0 commit comments