@@ -13,6 +13,13 @@ class PythonDebuggerView extends View
1313 debuggedFileArgs : []
1414 backendDebuggerPath : null
1515 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
1623
1724 getCurrentFilePath : ->
1825 editor = atom .workspace .getActivePaneItem ()
@@ -32,42 +39,90 @@ class PythonDebuggerView extends View
3239 @ subview " commandEntryView" , new TextEditorView
3340 mini : true ,
3441 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 " )
71126
72127 toggleBreak : ->
73128 editor = atom .workspace .getActiveTextEditor ()
@@ -81,30 +136,54 @@ class PythonDebuggerView extends View
81136 for breakpoint in @breakpointStore .breakpoints
82137 @output .append (breakpoint .toCommand () + " \n " )
83138
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-
96139 stepOverBtnPressed : ->
140+ @ setFlags ()
97141 @backendDebugger ? .stdin .write (" n\n " )
98142
99143 stepInBtnPressed : ->
144+ @ setFlags ()
100145 @backendDebugger ? .stdin .write (" s\n " )
101146
102147 continueBtnPressed : ->
148+ @ setFlags ()
103149 @backendDebugger ? .stdin .write (" c\n " )
104150
105151 returnBtnPressed : ->
152+ @ setFlags ()
106153 @backendDebugger ? .stdin .write (" r\n " )
107154
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+
108187 workspacePath : ->
109188 editor = atom .workspace .getActiveTextEditor ()
110189 activePath = editor .getPath ()
@@ -120,41 +199,52 @@ class PythonDebuggerView extends View
120199 if @ pathsNotSet ()
121200 @ askForPaths ()
122201 return
202+ @ setFlags ()
123203 @ runBackendDebugger ()
204+ @ toggleInput ()
124205
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?
127219
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 ) ->
134221
135- # Extract the file name and line number output by the debugger.
136- processDebuggerOutput : (data ) ->
137- data_str = data .toString ().trim ()
138222 lineNumber = null
139223 fileName = null
140- call_stack_str = " Call stack: \n "
141224
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
158248 [data_str , tail ] = data_str .split (" line:: " )
159249 if tail
160250 [lineNumber , tail ] = tail .split (" \n " )
@@ -167,20 +257,72 @@ class PythonDebuggerView extends View
167257 fileName = fileName .trim () if fileName
168258 fileName = null if fileName == " <string>"
169259
260+ # highlight the current line
170261 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
178272 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 )
182316
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)
184326
185327 runBackendDebugger : ->
186328 args = [path .join (@backendDebuggerPath , @backendDebuggerName )]
@@ -208,6 +350,7 @@ class PythonDebuggerView extends View
208350 @backendDebugger ? .stdin .write (" \n exit()\n " )
209351 @backendDebugger = null
210352 console .log " debugger stopped"
353+ @ toggleInput ()
211354
212355 clearOutput : ->
213356 @output .empty ()
@@ -234,6 +377,8 @@ class PythonDebuggerView extends View
234377 @breakpointStore = breakpointStore
235378 @debuggedFileName = @ getCurrentFilePath ()
236379 @backendDebuggerPath = @ getDebuggerPath ()
380+ @ toggleInput ()
381+ @ togglePanes ()
237382 @ addOutput (" Welcome to Python Debugger for Atom!" )
238383 @ addOutput (" The file being debugged is: " + @debuggedFileName )
239384 @ askForPaths ()
0 commit comments