Skip to content

Commit b5fb0fa

Browse files
committed
Improving the expression evaluator
* right after evaluating an expression, the erlang debugger will trigger a new "breakpoint reached" event; unfortunately, that means this plugin will try to concurrently render the new state of the bindings as well as the result from the evaluation, which Intellij doesn't seem to handle too well, resulting in the debugging session losing track of what the current frame is... so to fix that we just ignore the next "breakpoint reached" event occuring in an Erlang process right after evaluating an expression if it's the same breakpoint, in the same process. Better ideas welcome! * checking whether the evaluation resulted in an error, and nicely formatting it in if that's the case
1 parent 004db36 commit b5fb0fa

File tree

2 files changed

+58
-7
lines changed

2 files changed

+58
-7
lines changed

src/org/intellij/erlang/debugger/node/ErlangProcessSnapshot.java

+7
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,11 @@ public String getExitReason() {
8181
public List<ErlangTraceElement> getStack() {
8282
return myStack;
8383
}
84+
85+
public boolean isSameBreakpoint(@NotNull ErlangProcessSnapshot snapshot) {
86+
return myPid.equals(snapshot.getPid())
87+
&& myBreakModule != null
88+
&& myBreakModule.equals(snapshot.getBreakModule())
89+
&& myBreakLine == snapshot.getBreakLine();
90+
}
8491
}

src/org/intellij/erlang/debugger/xdebug/ErlangXDebugProcess.java

+51-7
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
package org.intellij.erlang.debugger.xdebug;
1818

19+
import com.ericsson.otp.erlang.OtpErlangAtom;
1920
import com.ericsson.otp.erlang.OtpErlangObject;
2021
import com.ericsson.otp.erlang.OtpErlangPid;
22+
import com.ericsson.otp.erlang.OtpErlangTuple;
2123
import com.intellij.execution.ExecutionException;
2224
import com.intellij.execution.configurations.GeneralCommandLine;
2325
import com.intellij.execution.process.OSProcessHandler;
@@ -72,7 +74,6 @@
7274
import static org.intellij.erlang.debugger.ErlangDebuggerLog.LOG;
7375

7476
public 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,18 @@ 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+
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;
183227
ErlangSourcePosition breakPosition = ErlangSourcePosition.create(myLocationResolver, processInBreakpoint);
184228
XLineBreakpoint<ErlangLineBreakpointProperties> breakpoint = getLineBreakpoint(breakPosition);
185229
ErlangSuspendContext suspendContext = new ErlangSuspendContext(this, pid, snapshots);

0 commit comments

Comments
 (0)