@@ -5,9 +5,10 @@ use std::time::Duration;
use anyhow::{Context as _, Result, anyhow};
use dap::StackFrameId;
use gpui::{
- AnyElement, Entity, EventEmitter, FocusHandle, Focusable, ListState, MouseButton, Stateful,
- Subscription, Task, WeakEntity, list,
+ AnyElement, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, ListState, MouseButton,
+ Stateful, Subscription, Task, WeakEntity, list,
};
+use util::debug_panic;
use crate::StackTraceView;
use language::PointUtf16;
@@ -43,6 +44,8 @@ pub struct StackFrameList {
#[derive(Debug, PartialEq, Eq)]
pub enum StackFrameEntry {
Normal(dap::StackFrame),
+ /// Used to indicate that the frame is artificial and is a visual label or separator
+ Label(dap::StackFrame),
Collapsed(Vec<dap::StackFrame>),
}
@@ -99,18 +102,18 @@ impl StackFrameList {
&self.entries
}
- pub(crate) fn flatten_entries(&self, show_collapsed: bool) -> Vec<dap::StackFrame> {
+ pub(crate) fn flatten_entries(
+ &self,
+ show_collapsed: bool,
+ show_labels: bool,
+ ) -> Vec<dap::StackFrame> {
self.entries
.iter()
.flat_map(|frame| match frame {
StackFrameEntry::Normal(frame) => vec![frame.clone()],
- StackFrameEntry::Collapsed(frames) => {
- if show_collapsed {
- frames.clone()
- } else {
- vec![]
- }
- }
+ StackFrameEntry::Label(frame) if show_labels => vec![frame.clone()],
+ StackFrameEntry::Collapsed(frames) if show_collapsed => frames.clone(),
+ _ => vec![],
})
.collect::<Vec<_>>()
}
@@ -176,9 +179,7 @@ impl StackFrameList {
.and_then(|ix| self.entries.get(ix))
.and_then(|entry| match entry {
StackFrameEntry::Normal(stack_frame) => Some(stack_frame.id),
- StackFrameEntry::Collapsed(stack_frames) => {
- stack_frames.first().map(|stack_frame| stack_frame.id)
- }
+ StackFrameEntry::Collapsed(_) | StackFrameEntry::Label(_) => None,
});
let mut entries = Vec::new();
let mut collapsed_entries = Vec::new();
@@ -202,6 +203,9 @@ impl StackFrameList {
Some(dap::StackFramePresentationHint::Deemphasize) => {
collapsed_entries.push(stack_frame.dap.clone());
}
+ Some(dap::StackFramePresentationHint::Label) => {
+ entries.push(StackFrameEntry::Label(stack_frame.dap.clone()));
+ }
_ => {
let collapsed_entries = std::mem::take(&mut collapsed_entries);
if !collapsed_entries.is_empty() {
@@ -234,9 +238,7 @@ impl StackFrameList {
} else if let Some(old_selected_frame_id) = old_selected_frame_id {
let ix = self.entries.iter().position(|entry| match entry {
StackFrameEntry::Normal(frame) => frame.id == old_selected_frame_id,
- StackFrameEntry::Collapsed(frames) => {
- frames.iter().any(|frame| frame.id == old_selected_frame_id)
- }
+ StackFrameEntry::Collapsed(_) | StackFrameEntry::Label(_) => false,
});
self.selected_ix = ix;
}
@@ -256,6 +258,7 @@ impl StackFrameList {
.entries
.iter()
.flat_map(|entry| match entry {
+ StackFrameEntry::Label(stack_frame) => std::slice::from_ref(stack_frame),
StackFrameEntry::Normal(stack_frame) => std::slice::from_ref(stack_frame),
StackFrameEntry::Collapsed(stack_frames) => stack_frames.as_slice(),
})
@@ -380,6 +383,33 @@ impl StackFrameList {
});
}
+ fn render_label_entry(
+ &self,
+ stack_frame: &dap::StackFrame,
+ _cx: &mut Context<Self>,
+ ) -> AnyElement {
+ h_flex()
+ .rounded_md()
+ .justify_between()
+ .w_full()
+ .group("")
+ .id(("label-stack-frame", stack_frame.id))
+ .p_1()
+ .on_any_mouse_down(|_, _, cx| {
+ cx.stop_propagation();
+ })
+ .child(
+ v_flex().justify_center().gap_0p5().child(
+ Label::new(stack_frame.name.clone())
+ .size(LabelSize::Small)
+ .weight(FontWeight::BOLD)
+ .truncate()
+ .color(Color::Info),
+ ),
+ )
+ .into_any()
+ }
+
fn render_normal_entry(
&self,
ix: usize,
@@ -541,6 +571,7 @@ impl StackFrameList {
fn render_entry(&self, ix: usize, cx: &mut Context<Self>) -> AnyElement {
match &self.entries[ix] {
+ StackFrameEntry::Label(stack_frame) => self.render_label_entry(stack_frame, cx),
StackFrameEntry::Normal(stack_frame) => self.render_normal_entry(ix, stack_frame, cx),
StackFrameEntry::Collapsed(stack_frames) => {
self.render_collapsed_entry(ix, stack_frames, cx)
@@ -657,6 +688,9 @@ impl StackFrameList {
self.go_to_stack_frame_inner(stack_frame, window, cx)
.detach_and_log_err(cx)
}
+ StackFrameEntry::Label(_) => {
+ debug_panic!("You should not be able to select a label stack frame")
+ }
StackFrameEntry::Collapsed(_) => self.expand_collapsed_entry(ix),
}
cx.notify();
@@ -148,7 +148,7 @@ impl StackTraceView {
let stack_frames = self
.stack_frame_list
- .read_with(cx, |list, _| list.flatten_entries(false));
+ .read_with(cx, |list, _| list.flatten_entries(false, false));
let frames_to_open: Vec<_> = stack_frames
.into_iter()
@@ -237,7 +237,7 @@ impl StackTraceView {
let stack_frames = self
.stack_frame_list
- .read_with(cx, |session, _| session.flatten_entries(false));
+ .read_with(cx, |session, _| session.flatten_entries(false, false));
let active_idx = self
.selected_stack_frame_id
@@ -191,7 +191,10 @@ async fn test_basic_fetch_initial_scope_and_variables(
running_state.update(cx, |running_state, cx| {
let (stack_frame_list, stack_frame_id) =
running_state.stack_frame_list().update(cx, |list, _| {
- (list.flatten_entries(true), list.opened_stack_frame_id())
+ (
+ list.flatten_entries(true, true),
+ list.opened_stack_frame_id(),
+ )
});
assert_eq!(stack_frames, stack_frame_list);
@@ -432,7 +435,10 @@ async fn test_fetch_variables_for_multiple_scopes(
running_state.update(cx, |running_state, cx| {
let (stack_frame_list, stack_frame_id) =
running_state.stack_frame_list().update(cx, |list, _| {
- (list.flatten_entries(true), list.opened_stack_frame_id())
+ (
+ list.flatten_entries(true, true),
+ list.opened_stack_frame_id(),
+ )
});
assert_eq!(Some(1), stack_frame_id);
@@ -1459,7 +1465,10 @@ async fn test_variable_list_only_sends_requests_when_rendering(
running_state.update(cx, |running_state, cx| {
let (stack_frame_list, stack_frame_id) =
running_state.stack_frame_list().update(cx, |list, _| {
- (list.flatten_entries(true), list.opened_stack_frame_id())
+ (
+ list.flatten_entries(true, true),
+ list.opened_stack_frame_id(),
+ )
});
assert_eq!(Some(1), stack_frame_id);
@@ -1741,7 +1750,10 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame(
running_state.update(cx, |running_state, cx| {
let (stack_frame_list, stack_frame_id) =
running_state.stack_frame_list().update(cx, |list, _| {
- (list.flatten_entries(true), list.opened_stack_frame_id())
+ (
+ list.flatten_entries(true, true),
+ list.opened_stack_frame_id(),
+ )
});
let variable_list = running_state.variable_list().read(cx);
@@ -1796,7 +1808,10 @@ async fn test_it_fetches_scopes_variables_when_you_select_a_stack_frame(
running_state.update(cx, |running_state, cx| {
let (stack_frame_list, stack_frame_id) =
running_state.stack_frame_list().update(cx, |list, _| {
- (list.flatten_entries(true), list.opened_stack_frame_id())
+ (
+ list.flatten_entries(true, true),
+ list.opened_stack_frame_id(),
+ )
});
let variable_list = running_state.variable_list().read(cx);