16
16
17
17
package org .intellij .erlang .debugger .xdebug ;
18
18
19
+ import com .ericsson .otp .erlang .OtpErlangAtom ;
19
20
import com .ericsson .otp .erlang .OtpErlangObject ;
20
21
import com .ericsson .otp .erlang .OtpErlangPid ;
22
+ import com .ericsson .otp .erlang .OtpErlangTuple ;
21
23
import com .intellij .execution .ExecutionException ;
22
24
import com .intellij .execution .configurations .GeneralCommandLine ;
23
25
import com .intellij .execution .process .OSProcessHandler ;
72
74
import static org .intellij .erlang .debugger .ErlangDebuggerLog .LOG ;
73
75
74
76
public class ErlangXDebugProcess extends XDebugProcess implements ErlangDebuggerEventListener {
75
- private final XDebugSession mySession ;
76
77
private final ExecutionEnvironment myExecutionEnvironment ;
77
78
private final ErlangRunningState myRunningState ;
78
79
private final ErlangDebuggerNode myDebuggerNode ;
@@ -83,11 +84,16 @@ public class ErlangXDebugProcess extends XDebugProcess implements ErlangDebugger
83
84
private ConcurrentHashMap <ErlangSourcePosition , XLineBreakpoint <ErlangLineBreakpointProperties >> myPositionToLineBreakpointMap =
84
85
new ConcurrentHashMap <>();
85
86
private XDebuggerEvaluator .XEvaluationCallback myEvalCallback = null ;
87
+ // right after evaluating an expression, the erlang debugger will trigger a new "breakpoint reached" event
88
+ // unfortunately, that means this plugin will try to concurrently render the new state of the bindings as
89
+ // well as the result from the evaluation, which Intellij doesn't seem to handle too well, resulting in
90
+ // the debugging session losing track of what the current frame is... so we just ignore the next "breakpoint
91
+ // reached" event in the same process right after evaluating an expression - better ideas welcome!
92
+ private OtpErlangPid ignoreNextBreakpointReachedEventFor = null ;
86
93
87
94
public ErlangXDebugProcess (@ NotNull XDebugSession session , ExecutionEnvironment env ) throws ExecutionException {
88
95
//TODO add debug build targets and make sure the project is built using them.
89
96
super (session );
90
- mySession = session ;
91
97
92
98
session .setPauseActionSupported (false );
93
99
@@ -124,20 +130,48 @@ public ErlangDebugLocationResolver getLocationResolver() {
124
130
public synchronized void evaluateExpression (@ NotNull String expression ,
125
131
@ NotNull XDebuggerEvaluator .XEvaluationCallback callback ,
126
132
@ NotNull ErlangTraceElement traceElement ) {
127
- // need to pause the debugging session otherwise the callback might get invalidated
128
- mySession .pause ();
129
133
myEvalCallback = callback ;
134
+ // see the comment about ignoreNextBreakpointReachedEventFor
135
+ ignoreNextBreakpointReachedEventFor = myDebuggerNode .getLastSuspendedPid ();
130
136
myDebuggerNode .evaluate (expression , traceElement );
131
137
}
132
138
133
139
@ Override
134
140
public synchronized void handleEvaluationResponse (OtpErlangObject response ) {
135
141
if (myEvalCallback != null ) {
136
- myEvalCallback .evaluated (ErlangXValueFactory .create (response ));
137
- mySession .resume ();
142
+ String error = maybeExtractErrorFromEvaluationResponse (response );
143
+
144
+ if (error == null ) {
145
+ myEvalCallback .evaluated (ErlangXValueFactory .create (response ));
146
+ }
147
+ else {
148
+ myEvalCallback .errorOccurred (error );
149
+ }
138
150
}
139
151
}
140
152
153
+ // Parses the response from an evaluation and determines whether it's an error
154
+ // response or not; if it is an error, formats it as a displayable string, if
155
+ // it's not, just returns null
156
+ private static String maybeExtractErrorFromEvaluationResponse (OtpErlangObject response ) {
157
+ // is it a parsing error?
158
+ if (response instanceof OtpErlangAtom && ((OtpErlangAtom ) response ).atomValue ().equals ("Parse error" )) {
159
+ return "Parse error" ;
160
+ }
161
+
162
+ // is it an uncaught exception?
163
+ if (response instanceof OtpErlangTuple ) {
164
+ OtpErlangObject [] elements = ((OtpErlangTuple ) response ).elements ();
165
+ if (elements .length == 2
166
+ && elements [0 ] instanceof OtpErlangAtom
167
+ && ((OtpErlangAtom ) elements [0 ]).atomValue ().equals ("EXIT" )) {
168
+ return "Uncaught exception: " + elements [1 ];
169
+ }
170
+ }
171
+
172
+ return null ;
173
+ }
174
+
141
175
@ Override
142
176
public void debuggerStarted () {
143
177
getSession ().reportMessage ("Debug process started" , MessageType .INFO );
@@ -177,7 +211,13 @@ public void breakpointIsSet(String module, int line) {
177
211
}
178
212
179
213
@ Override
180
- public void breakpointReached (final OtpErlangPid pid , List <ErlangProcessSnapshot > snapshots ) {
214
+ public synchronized void breakpointReached (final OtpErlangPid pid , List <ErlangProcessSnapshot > snapshots ) {
215
+ if (pid .equals (ignoreNextBreakpointReachedEventFor )) {
216
+ // see the comment about ignoreNextBreakpointReachedEventFor
217
+ ignoreNextBreakpointReachedEventFor = null ;
218
+ return ;
219
+ }
220
+
181
221
ErlangProcessSnapshot processInBreakpoint = ContainerUtil .find (snapshots , erlangProcessSnapshot -> erlangProcessSnapshot .getPid ().equals (pid ));
182
222
assert processInBreakpoint != null ;
183
223
ErlangSourcePosition breakPosition = ErlangSourcePosition .create (myLocationResolver , processInBreakpoint );
0 commit comments