[debugger] fix stack frames folding, IDEA-340584

There were some problems with folding/hiding,
please see test changes.

^IDEA-344033 fixed

GitOrigin-RevId: cc004b02094381a5ce85155c699c2f31b63a82d6
This commit is contained in:
Vladimir Parfinenko
2024-01-31 16:11:48 +01:00
committed by intellij-monorepo-bot
parent dff6b3aaff
commit 40f60bd16e
10 changed files with 94 additions and 37 deletions

View File

@@ -265,17 +265,39 @@ public class JavaExecutionStack extends XExecutionStack {
return myAdded <= StackFrameProxyImpl.FRAMES_BATCH_MAX ? Priority.NORMAL : Priority.LOW;
}
private void flushHiddenFrames() {
if (!XFramesView.shouldFoldHiddenFrames()) return;
if (myHiddenCount > 0) {
assert myFirstHiddenFrame != null;
var placeholder = new XFramesView.HiddenStackFramesItem(myHiddenCount, myFirstHiddenFrame);
myContainer.addStackFrames(Collections.singletonList(placeholder), false);
myHiddenCount = 0;
myFirstHiddenFrame = null;
}
}
private void rememberHiddenFrame(XStackFrame frame) {
if (!XFramesView.shouldFoldHiddenFrames()) return;
if (myHiddenCount == 0) {
myFirstHiddenFrame = frame;
}
myHiddenCount++;
}
private void addStackFrames(List<XStackFrame> frames, boolean last) {
flushHiddenFrames();
myContainer.addStackFrames(frames, last);
}
private boolean addFrameIfNeeded(XStackFrame frame, boolean last) {
if (++myAdded > mySkip) {
if (myHiddenCount > 0) {
assert myFirstHiddenFrame != null;
myContainer.addStackFrames(XFramesView.createHiddenFramePlaceholder(myHiddenCount, myFirstHiddenFrame), false);
}
myContainer.addStackFrames(Collections.singletonList(frame), last);
addStackFrames(Collections.singletonList(frame), last);
return true;
}
if (last) {
myContainer.addStackFrames(Collections.emptyList(), true);
addStackFrames(Collections.emptyList(), true);
}
return false;
}
@@ -307,18 +329,13 @@ public class JavaExecutionStack extends XExecutionStack {
if (frame instanceof JavaStackFrame) {
((JavaStackFrame)frame).getDescriptor().updateRepresentationNoNotify(null, () -> {
// repaint on icon change
myContainer.addStackFrames(Collections.emptyList(), !myStackFramesIterator.hasNext());
addStackFrames(Collections.emptyList(), !myStackFramesIterator.hasNext());
});
}
addFrameIfNeeded(frame, false);
myHiddenCount = 0;
myFirstHiddenFrame = null;
}
else {
if (myHiddenCount == 0) {
myFirstHiddenFrame = frame;
}
myHiddenCount++;
rememberHiddenFrame(frame);
}
}
@@ -350,7 +367,7 @@ public class JavaExecutionStack extends XExecutionStack {
appendRelatedStack(suspendContext, myAsyncStack.subList(myAddedAsync, myAsyncStack.size()));
}
else {
myContainer.addStackFrames(Collections.emptyList(), true);
addStackFrames(Collections.emptyList(), true);
}
}
@@ -383,18 +400,29 @@ public class JavaExecutionStack extends XExecutionStack {
XStackFrame newFrame = stackFrame.createFrame(myDebugProcess);
if (newFrame != null) {
if (showFrame(newFrame)) {
StackFrameItem.setWithSeparator(newFrame, mySeparator);
if (mySeparator) {
flushHiddenFrames();
StackFrameItem.setWithSeparator(newFrame);
}
if (addFrameIfNeeded(newFrame, false)) {
// No need to propagate the separator further, because it was added.
mySeparator = false;
}
myHiddenCount = 0;
myFirstHiddenFrame = null;
}
else if (XFramesView.shouldFoldHiddenFrames()) {
if (mySeparator) {
flushHiddenFrames();
// The separator for this hidden frame will be used as the placeholder separator.
StackFrameItem.setWithSeparator(newFrame);
mySeparator = false;
}
rememberHiddenFrame(newFrame);
}
else {
if (myHiddenCount == 0) {
myFirstHiddenFrame = newFrame;
if (!mySeparator && StackFrameItem.hasSeparatorAbove(newFrame)) {
// Frame has a separator, but it wasn't added; we need to propagate the separator further.
mySeparator = true;
}
myHiddenCount++;
}
}
schedule(suspendContext, null, myAsyncStack, mySeparator);

View File

@@ -57,7 +57,9 @@ class StackFrameList extends XDebuggerFramesList {
}
else {
XStackFrame frame = frameInfo.createFrame(myDebugProcess);
StackFrameItem.setWithSeparator(frame, separator);
if (separator) {
StackFrameItem.setWithSeparator(frame);
}
DebuggerUIUtil.invokeLater(() -> getModel().add(frame));
separator = false;
}

View File

@@ -228,9 +228,14 @@ public class StackFrameItem {
return new CapturedStackFrame(debugProcess, this);
}
public static void setWithSeparator(XStackFrame frame, boolean withSeparator) {
if (frame instanceof CapturedStackFrame) {
((CapturedStackFrame)frame).setWithSeparator(withSeparator);
public static boolean hasSeparatorAbove(XStackFrame frame) {
return frame instanceof XDebuggerFramesList.ItemWithSeparatorAbove frameWithSeparator &&
frameWithSeparator.hasSeparatorAbove();
}
public static void setWithSeparator(XStackFrame frame) {
if (frame instanceof XDebuggerFramesList.ItemWithSeparatorAbove frameWithSeparator) {
frameWithSeparator.setWithSeparator(true);
}
}
@@ -328,6 +333,7 @@ public class StackFrameItem {
return myWithSeparator;
}
@Override
public void setWithSeparator(boolean withSeparator) {
myWithSeparator = withSeparator;
}

View File

@@ -230,8 +230,9 @@ memory.instances.close.text=Close
threads.list.threads.not.available=Threads are not available
data.views.configurable.panel.title=Editor
hide.library.frames.tooltip=Hide Frames from Libraries
fold.library.frames.tooltip=Fold Frames from Libraries
show.all.frames.tooltip=Show All Frames
label.hidden.frames={0,number} hidden {0,choice, 1#frame|2#frames}
label.folded.frames={0,number} hidden {0,choice, 1#frame|2#frames}
options.kotlin.attribute.descriptor.inline.stack.frames=Inline stack frames
options.java.attribute.descriptor.breakpoint.line=Breakpoint line
options.java.attribute.descriptor.execution.point=Execution point

View File

@@ -6,6 +6,7 @@ import com.intellij.openapi.actionSystem.*;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerBundle;
import com.intellij.xdebugger.impl.XDebuggerUtilImpl;
import com.intellij.xdebugger.impl.frame.XFramesView;
import com.intellij.xdebugger.impl.settings.XDebuggerSettingManagerImpl;
import org.jetbrains.annotations.NotNull;
@@ -43,7 +44,11 @@ final class ShowLibraryFramesAction extends ToggleAction {
if (Boolean.TRUE.equals(isSupported)) {
presentation.setVisible(true);
final boolean shouldShow = !Toggleable.isSelected(presentation);
presentation.setText(XDebuggerBundle.message(shouldShow ? "hide.library.frames.tooltip" : "show.all.frames.tooltip"));
presentation.setText(XDebuggerBundle.message(shouldShow
? (XFramesView.shouldFoldHiddenFrames()
? "fold.library.frames.tooltip"
: "hide.library.frames.tooltip")
: "show.all.frames.tooltip"));
}
else {
presentation.setVisible(false);

View File

@@ -426,6 +426,9 @@ public class XDebuggerFramesList extends DebuggerFramesList implements DataProvi
public interface ItemWithSeparatorAbove {
boolean hasSeparatorAbove();
@NlsContexts.Separator String getCaptionAboveOf();
default void setWithSeparator(boolean withSeparator) {
}
}
public interface ItemWithCustomBackgroundColor {

View File

@@ -725,20 +725,25 @@ public final class XFramesView extends XDebugView {
}
}
private static class HiddenStackFramesItem extends XStackFrame implements XDebuggerFramesList.ItemWithCustomBackgroundColor,
XDebuggerFramesList.ItemWithSeparatorAbove {
/**
* Synthetic frame which encapsulates hidden library frames as a single fold.
*
* @see #shouldFoldHiddenFrames()
*/
public static class HiddenStackFramesItem extends XStackFrame implements XDebuggerFramesList.ItemWithCustomBackgroundColor,
XDebuggerFramesList.ItemWithSeparatorAbove {
private final int hiddenFrameCount;
private final @NotNull XStackFrame firstHiddenFrame;
private HiddenStackFramesItem(int hiddenFrameCount, @NotNull XStackFrame firstHiddenFrame) {
public HiddenStackFramesItem(int hiddenFrameCount, @NotNull XStackFrame firstHiddenFrame) {
this.hiddenFrameCount = hiddenFrameCount;
this.firstHiddenFrame = firstHiddenFrame;
}
@Override
public void customizePresentation(@NotNull ColoredTextContainer component) {
component.append(XDebuggerBundle.message("label.hidden.frames", hiddenFrameCount), SimpleTextAttributes.GRAYED_ATTRIBUTES);
component.append(XDebuggerBundle.message("label.folded.frames", hiddenFrameCount), SimpleTextAttributes.GRAYED_ATTRIBUTES);
component.setIcon(EmptyIcon.ICON_16);
}
@@ -764,10 +769,12 @@ public final class XFramesView extends XDebugView {
}
}
public static List<XStackFrame> createHiddenFramePlaceholder(int hiddenFrameCount, @NotNull XStackFrame firstHiddenFrame) {
return Registry.is("debugger.library.frames.fold.instead.of.hide")
? Collections.singletonList(new HiddenStackFramesItem(hiddenFrameCount, firstHiddenFrame))
: Collections.emptyList();
/**
* Whether hidden frames are folded into a single-item placeholder.
* Otherwise, they completely disappear.
*/
public static boolean shouldFoldHiddenFrames() {
return Registry.is("debugger.library.frames.fold.instead.of.hide");
}

View File

@@ -29,7 +29,7 @@ class CreationCoroutineStackFrameItem(
return debugProcess.invokeInManagerThread {
val frame = debugProcess.findFirstFrame() ?: return@invokeInManagerThread null
val position = location.findPosition(debugProcess)
CreationCoroutineStackFrame(frame, position, first, location)
CreationCoroutineStackFrame(frame, position, withSepartor = first, location)
}
}
}

View File

@@ -44,7 +44,7 @@ class CoroutinePreflightFrame(
class CreationCoroutineStackFrame(
frame: StackFrameProxyImpl,
sourcePosition: XSourcePosition?,
val first: Boolean,
private var withSepartor: Boolean,
location: Location? = frame.safeLocation()
) : CoroutineStackFrame(frame, sourcePosition, emptyList(), false, location), XDebuggerFramesList.ItemWithSeparatorAbove {
@@ -52,7 +52,11 @@ class CreationCoroutineStackFrame(
KotlinDebuggerCoroutinesBundle.message("coroutine.dump.creation.trace")
override fun hasSeparatorAbove() =
first
withSepartor
override fun setWithSeparator(withSeparator: Boolean) {
this.withSepartor = withSeparator
}
}
open class CoroutineStackFrame(

View File

@@ -8,6 +8,7 @@ Thread stack trace:
CoroutineInfo: 1 coroutine RUNNING
CoroutineStackFrame main:!LINE_NUMBER!, SuspendFunStackTraceHiddenKt (continuation)
()
Coroutine Creation Stack Trace
CreationCoroutineStackFrame main:!LINE_NUMBER!, SuspendFunStackTraceHiddenKt (continuation)
($result, this)
CreationCoroutineStackFrame FRAME:main:-1, SuspendFunStackTraceHiddenKt (continuation)