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,16 @@ 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 in the same process right after evaluating an expression - better ideas welcome!
92+ private OtpErlangPid ignoreNextBreakpointReachedEventFor = null ;
8693
8794 public ErlangXDebugProcess (@ NotNull XDebugSession session , ExecutionEnvironment env ) throws ExecutionException {
8895 //TODO add debug build targets and make sure the project is built using them.
8996 super (session );
90- mySession = session ;
9197
9298 session .setPauseActionSupported (false );
9399
@@ -124,20 +130,48 @@ public ErlangDebugLocationResolver getLocationResolver() {
124130 public synchronized void evaluateExpression (@ NotNull String expression ,
125131 @ NotNull XDebuggerEvaluator .XEvaluationCallback callback ,
126132 @ NotNull ErlangTraceElement traceElement ) {
127- // need to pause the debugging session otherwise the callback might get invalidated
128- mySession .pause ();
129133 myEvalCallback = callback ;
134+ // see the comment about ignoreNextBreakpointReachedEventFor
135+ ignoreNextBreakpointReachedEventFor = myDebuggerNode .getLastSuspendedPid ();
130136 myDebuggerNode .evaluate (expression , traceElement );
131137 }
132138
133139 @ Override
134140 public synchronized void handleEvaluationResponse (OtpErlangObject response ) {
135141 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+ }
138150 }
139151 }
140152
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+
141175 @ Override
142176 public void debuggerStarted () {
143177 getSession ().reportMessage ("Debug process started" , MessageType .INFO );
@@ -177,7 +211,13 @@ public void breakpointIsSet(String module, int line) {
177211 }
178212
179213 @ 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+
181221 ErlangProcessSnapshot processInBreakpoint = ContainerUtil .find (snapshots , erlangProcessSnapshot -> erlangProcessSnapshot .getPid ().equals (pid ));
182222 assert processInBreakpoint != null ;
183223 ErlangSourcePosition breakPosition = ErlangSourcePosition .create (myLocationResolver , processInBreakpoint );
0 commit comments