1616
1717package org .intellij .erlang .debugger .xdebug ;
1818
19+ import com .ericsson .otp .erlang .OtpErlangAtom ;
1920import com .ericsson .otp .erlang .OtpErlangObject ;
2021import com .ericsson .otp .erlang .OtpErlangPid ;
22+ import com .ericsson .otp .erlang .OtpErlangTuple ;
2123import com .intellij .execution .ExecutionException ;
2224import com .intellij .execution .configurations .GeneralCommandLine ;
2325import com .intellij .execution .process .OSProcessHandler ;
7274import static org .intellij .erlang .debugger .ErlangDebuggerLog .LOG ;
7375
7476public class ErlangXDebugProcess extends XDebugProcess implements ErlangDebuggerEventListener {
75- private final XDebugSession mySession ;
7677 private final ExecutionEnvironment myExecutionEnvironment ;
7778 private final ErlangRunningState myRunningState ;
7879 private final ErlangDebuggerNode myDebuggerNode ;
@@ -83,11 +84,17 @@ public class ErlangXDebugProcess extends XDebugProcess implements ErlangDebugger
8384 private ConcurrentHashMap <ErlangSourcePosition , XLineBreakpoint <ErlangLineBreakpointProperties >> myPositionToLineBreakpointMap =
8485 new ConcurrentHashMap <>();
8586 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 ;
8694
8795 public ErlangXDebugProcess (@ NotNull XDebugSession session , ExecutionEnvironment env ) throws ExecutionException {
8896 //TODO add debug build targets and make sure the project is built using them.
8997 super (session );
90- mySession = session ;
9198
9299 session .setPauseActionSupported (false );
93100
@@ -124,18 +131,46 @@ public ErlangDebugLocationResolver getLocationResolver() {
124131 public synchronized void evaluateExpression (@ NotNull String expression ,
125132 @ NotNull XDebuggerEvaluator .XEvaluationCallback callback ,
126133 @ NotNull ErlangTraceElement traceElement ) {
127- // need to pause the debugging session otherwise the callback might get invalidated
128- mySession .pause ();
129134 myEvalCallback = callback ;
135+ // see the comment about myIgnoreNextBreakpointIfSame
136+ myIgnoreNextBreakpointIfSame = true ;
130137 myDebuggerNode .evaluate (expression , traceElement );
131138 }
132139
133140 @ Override
134141 public synchronized void handleEvaluationResponse (OtpErlangObject response ) {
135142 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" ;
138161 }
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 ;
139174 }
140175
141176 @ Override
@@ -177,9 +212,16 @@ public void breakpointIsSet(String module, int line) {
177212 }
178213
179214 @ Override
180- public void breakpointReached (final OtpErlangPid pid , List <ErlangProcessSnapshot > snapshots ) {
215+ public synchronized void breakpointReached (final OtpErlangPid pid , List <ErlangProcessSnapshot > snapshots ) {
181216 ErlangProcessSnapshot processInBreakpoint = ContainerUtil .find (snapshots , erlangProcessSnapshot -> erlangProcessSnapshot .getPid ().equals (pid ));
182217 assert processInBreakpoint != null ;
218+
219+ if (myIgnoreNextBreakpointIfSame && myLastBreakpointReached .isSameBreakpoint (processInBreakpoint )) {
220+ // see the comment about myLastBreakpointReached
221+ return ;
222+ }
223+
224+ myLastBreakpointReached = processInBreakpoint ;
183225 ErlangSourcePosition breakPosition = ErlangSourcePosition .create (myLocationResolver , processInBreakpoint );
184226 XLineBreakpoint <ErlangLineBreakpointProperties > breakpoint = getLineBreakpoint (breakPosition );
185227 ErlangSuspendContext suspendContext = new ErlangSuspendContext (this , pid , snapshots );
0 commit comments