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,17 @@ 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 after evaluating an expression if it's the same breakpoint - better ideas welcome!
92
+ private ErlangProcessSnapshot myLastBreakpointReached = null ;
93
+ private boolean myIgnoreNextBreakpointIfSame = false ;
86
94
87
95
public ErlangXDebugProcess (@ NotNull XDebugSession session , ExecutionEnvironment env ) throws ExecutionException {
88
96
//TODO add debug build targets and make sure the project is built using them.
89
97
super (session );
90
- mySession = session ;
91
98
92
99
session .setPauseActionSupported (false );
93
100
@@ -124,18 +131,46 @@ public ErlangDebugLocationResolver getLocationResolver() {
124
131
public synchronized void evaluateExpression (@ NotNull String expression ,
125
132
@ NotNull XDebuggerEvaluator .XEvaluationCallback callback ,
126
133
@ NotNull ErlangTraceElement traceElement ) {
127
- // need to pause the debugging session otherwise the callback might get invalidated
128
- mySession .pause ();
129
134
myEvalCallback = callback ;
135
+ // see the comment about myIgnoreNextBreakpointIfSame
136
+ myIgnoreNextBreakpointIfSame = true ;
130
137
myDebuggerNode .evaluate (expression , traceElement );
131
138
}
132
139
133
140
@ Override
134
141
public synchronized void handleEvaluationResponse (OtpErlangObject response ) {
135
142
if (myEvalCallback != null ) {
136
- myEvalCallback .evaluated (ErlangXValueFactory .create (response ));
137
- mySession .resume ();
143
+ String error = maybeExtractErrorFromEvaluationResponse (response );
144
+
145
+ if (error == null ) {
146
+ myEvalCallback .evaluated (ErlangXValueFactory .create (response ));
147
+ }
148
+ else {
149
+ myEvalCallback .errorOccurred (error );
150
+ }
151
+ }
152
+ }
153
+
154
+ // Parses the response from an evaluation and determines whether it's an error
155
+ // response or not; if it is an error, formats it as a displayable string, if
156
+ // it's not, just returns null
157
+ private static String maybeExtractErrorFromEvaluationResponse (OtpErlangObject response ) {
158
+ // is it a parsing error?
159
+ if (response instanceof OtpErlangAtom && ((OtpErlangAtom ) response ).atomValue ().equals ("Parse error" )) {
160
+ return "Parse error" ;
138
161
}
162
+
163
+ // is it an uncaught exception?
164
+ if (response instanceof OtpErlangTuple ) {
165
+ OtpErlangObject [] elements = ((OtpErlangTuple ) response ).elements ();
166
+ if (elements .length == 2
167
+ && elements [0 ] instanceof OtpErlangAtom
168
+ && ((OtpErlangAtom ) elements [0 ]).atomValue ().equals ("EXIT" )) {
169
+ return "Uncaught exception: " + elements [1 ];
170
+ }
171
+ }
172
+
173
+ return null ;
139
174
}
140
175
141
176
@ Override
@@ -177,9 +212,18 @@ public void breakpointIsSet(String module, int line) {
177
212
}
178
213
179
214
@ Override
180
- public void breakpointReached (final OtpErlangPid pid , List <ErlangProcessSnapshot > snapshots ) {
215
+ public synchronized void breakpointReached (final OtpErlangPid pid , List <ErlangProcessSnapshot > snapshots ) {
181
216
ErlangProcessSnapshot processInBreakpoint = ContainerUtil .find (snapshots , erlangProcessSnapshot -> erlangProcessSnapshot .getPid ().equals (pid ));
182
217
assert processInBreakpoint != null ;
218
+
219
+ boolean ignoreIfSame = myIgnoreNextBreakpointIfSame ;
220
+ myIgnoreNextBreakpointIfSame = false ;
221
+ if (ignoreIfSame && myLastBreakpointReached .isSameBreakpoint (processInBreakpoint )) {
222
+ // see the comment about myLastBreakpointReached
223
+ return ;
224
+ }
225
+
226
+ myLastBreakpointReached = processInBreakpoint ;
183
227
ErlangSourcePosition breakPosition = ErlangSourcePosition .create (myLocationResolver , processInBreakpoint );
184
228
XLineBreakpoint <ErlangLineBreakpointProperties > breakpoint = getLineBreakpoint (breakPosition );
185
229
ErlangSuspendContext suspendContext = new ErlangSuspendContext (this , pid , snapshots );
0 commit comments