Detailed changes
@@ -2,8 +2,9 @@ use crate::persistence::DebuggerPaneItem;
use crate::session::DebugSession;
use crate::{
ClearAllBreakpoints, Continue, Detach, FocusBreakpointList, FocusConsole, FocusFrames,
- FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, Pause, Restart, StepBack,
- StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints, persistence,
+ FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, Pause, Restart,
+ ShowStackTrace, StepBack, StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints,
+ persistence,
};
use anyhow::{Result, anyhow};
use command_palette_hooks::CommandPaletteFilter;
@@ -67,11 +68,7 @@ pub struct DebugPanel {
}
impl DebugPanel {
- pub fn new(
- workspace: &Workspace,
- _window: &mut Window,
- cx: &mut Context<Workspace>,
- ) -> Entity<Self> {
+ pub fn new(workspace: &Workspace, cx: &mut Context<Workspace>) -> Entity<Self> {
cx.new(|cx| {
let project = workspace.project().clone();
@@ -119,6 +116,7 @@ impl DebugPanel {
TypeId::of::<StepOver>(),
TypeId::of::<StepInto>(),
TypeId::of::<StepOut>(),
+ TypeId::of::<ShowStackTrace>(),
TypeId::of::<editor::actions::DebuggerRunToCursor>(),
TypeId::of::<editor::actions::DebuggerEvaluateSelectedText>(),
];
@@ -170,8 +168,8 @@ impl DebugPanel {
cx: &mut AsyncWindowContext,
) -> Task<Result<Entity<Self>>> {
cx.spawn(async move |cx| {
- workspace.update_in(cx, |workspace, window, cx| {
- let debug_panel = DebugPanel::new(workspace, window, cx);
+ workspace.update(cx, |workspace, cx| {
+ let debug_panel = DebugPanel::new(workspace, cx);
workspace.register_action(|workspace, _: &ClearAllBreakpoints, _, cx| {
workspace.project().read(cx).breakpoint_store().update(
@@ -421,6 +419,7 @@ impl DebugPanel {
pub fn active_session(&self) -> Option<Entity<DebugSession>> {
self.active_session.clone()
}
+
fn close_session(&mut self, entity_id: EntityId, window: &mut Window, cx: &mut Context<Self>) {
let Some(session) = self
.sessions
@@ -999,7 +998,7 @@ impl DebugPanel {
this.go_to_selected_stack_frame(window, cx);
});
});
- self.active_session = Some(session_item);
+ self.active_session = Some(session_item.clone());
cx.notify();
}
@@ -7,14 +7,16 @@ use new_session_modal::NewSessionModal;
use project::debugger::{self, breakpoint_store::SourceBreakpoint};
use session::DebugSession;
use settings::Settings;
+use stack_trace_view::StackTraceView;
use util::maybe;
-use workspace::{ShutdownDebugAdapters, Workspace};
+use workspace::{ItemHandle, ShutdownDebugAdapters, Workspace};
pub mod attach_modal;
pub mod debugger_panel;
mod new_session_modal;
mod persistence;
pub(crate) mod session;
+mod stack_trace_view;
#[cfg(any(test, feature = "test-support"))]
pub mod tests;
@@ -41,6 +43,7 @@ actions!(
FocusModules,
FocusLoadedSources,
FocusTerminal,
+ ShowStackTrace,
]
);
@@ -146,6 +149,38 @@ pub fn init(cx: &mut App) {
})
},
)
+ .register_action(
+ |workspace: &mut Workspace, _: &ShowStackTrace, window, cx| {
+ let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
+ return;
+ };
+
+ if let Some(existing) = workspace.item_of_type::<StackTraceView>(cx) {
+ let is_active = workspace
+ .active_item(cx)
+ .is_some_and(|item| item.item_id() == existing.item_id());
+ workspace.activate_item(&existing, true, !is_active, window, cx);
+ } else {
+ let Some(active_session) = debug_panel.read(cx).active_session() else {
+ return;
+ };
+
+ let project = workspace.project();
+
+ let stack_trace_view = active_session.update(cx, |session, cx| {
+ session.stack_trace_view(project, window, cx).clone()
+ });
+
+ workspace.add_item_to_active_pane(
+ Box::new(stack_trace_view),
+ None,
+ true,
+ window,
+ cx,
+ );
+ }
+ },
+ )
.register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
NewSessionModal::show(workspace, window, cx);
});
@@ -1,6 +1,6 @@
pub mod running;
-use std::sync::OnceLock;
+use std::{cell::OnceCell, sync::OnceLock};
use dap::client::SessionId;
use gpui::{
@@ -17,15 +17,16 @@ use workspace::{
item::{self, Item},
};
-use crate::{debugger_panel::DebugPanel, persistence::SerializedLayout};
+use crate::{StackTraceView, debugger_panel::DebugPanel, persistence::SerializedLayout};
pub struct DebugSession {
remote_id: Option<workspace::ViewId>,
running_state: Entity<RunningState>,
label: OnceLock<SharedString>,
+ stack_trace_view: OnceCell<Entity<StackTraceView>>,
_debug_panel: WeakEntity<DebugPanel>,
_worktree_store: WeakEntity<WorktreeStore>,
- _workspace: WeakEntity<Workspace>,
+ workspace: WeakEntity<Workspace>,
_subscriptions: [Subscription; 1],
}
@@ -66,8 +67,9 @@ impl DebugSession {
running_state,
label: OnceLock::new(),
_debug_panel,
+ stack_trace_view: OnceCell::new(),
_worktree_store: project.read(cx).worktree_store().downgrade(),
- _workspace: workspace,
+ workspace,
})
}
@@ -75,6 +77,32 @@ impl DebugSession {
self.running_state.read(cx).session_id()
}
+ pub(crate) fn stack_trace_view(
+ &mut self,
+ project: &Entity<Project>,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> &Entity<StackTraceView> {
+ let workspace = self.workspace.clone();
+ let running_state = self.running_state.clone();
+
+ self.stack_trace_view.get_or_init(|| {
+ let stackframe_list = running_state.read(cx).stack_frame_list().clone();
+
+ let stack_frame_view = cx.new(|cx| {
+ StackTraceView::new(
+ workspace.clone(),
+ project.clone(),
+ stackframe_list,
+ window,
+ cx,
+ )
+ });
+
+ stack_frame_view
+ })
+ }
+
pub fn session(&self, cx: &App) -> Entity<Session> {
self.running_state.read(cx).session().clone()
}
@@ -1235,8 +1235,7 @@ impl RunningState {
self.stack_frame_list.read(cx).selected_stack_frame_id()
}
- #[cfg(test)]
- pub fn stack_frame_list(&self) -> &Entity<StackFrameList> {
+ pub(crate) fn stack_frame_list(&self) -> &Entity<StackFrameList> {
&self.stack_frame_list
}
@@ -62,7 +62,6 @@ impl Console {
editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx);
editor
});
- let focus_handle = cx.focus_handle();
let this = cx.weak_entity();
let query_bar = cx.new(|cx| {
@@ -77,6 +76,8 @@ impl Console {
editor
});
+ let focus_handle = query_bar.focus_handle(cx);
+
let _subscriptions =
vec![cx.subscribe(&stack_frame_list, Self::handle_stack_frame_list_events)];
@@ -110,6 +111,7 @@ impl Console {
) {
match event {
StackFrameListEvent::SelectedStackFrameChanged(_) => cx.notify(),
+ StackFrameListEvent::BuiltEntries => {}
}
}
@@ -15,13 +15,16 @@ use project::debugger::session::{Session, SessionEvent, StackFrame};
use project::{ProjectItem, ProjectPath};
use ui::{Scrollbar, ScrollbarState, Tooltip, prelude::*};
use util::ResultExt;
-use workspace::Workspace;
+use workspace::{ItemHandle, Workspace};
+
+use crate::StackTraceView;
use super::RunningState;
#[derive(Debug)]
pub enum StackFrameListEvent {
SelectedStackFrameChanged(StackFrameId),
+ BuiltEntries,
}
pub struct StackFrameList {
@@ -101,13 +104,18 @@ impl StackFrameList {
&self.entries
}
- #[cfg(test)]
- pub(crate) fn flatten_entries(&self) -> Vec<dap::StackFrame> {
+ pub(crate) fn flatten_entries(&self, show_collapsed: bool) -> Vec<dap::StackFrame> {
self.entries
.iter()
.flat_map(|frame| match frame {
StackFrameEntry::Normal(frame) => vec![frame.clone()],
- StackFrameEntry::Collapsed(frames) => frames.clone(),
+ StackFrameEntry::Collapsed(frames) => {
+ if show_collapsed {
+ frames.clone()
+ } else {
+ vec![]
+ }
+ }
})
.collect::<Vec<_>>()
}
@@ -136,6 +144,25 @@ impl StackFrameList {
self.selected_stack_frame_id
}
+ pub(crate) fn select_stack_frame_id(
+ &mut self,
+ id: StackFrameId,
+ window: &Window,
+ cx: &mut Context<Self>,
+ ) {
+ if !self.entries.iter().any(|entry| match entry {
+ StackFrameEntry::Normal(entry) => entry.id == id,
+ StackFrameEntry::Collapsed(stack_frames) => {
+ stack_frames.iter().any(|frame| frame.id == id)
+ }
+ }) {
+ return;
+ }
+
+ self.selected_stack_frame_id = Some(id);
+ self.go_to_selected_stack_frame(window, cx);
+ }
+
pub(super) fn schedule_refresh(
&mut self,
select_first: bool,
@@ -206,6 +233,7 @@ impl StackFrameList {
.detach_and_log_err(cx);
}
+ cx.emit(StackFrameListEvent::BuiltEntries);
cx.notify();
}
@@ -255,7 +283,7 @@ impl StackFrameList {
let row = (stack_frame.line.saturating_sub(1)) as u32;
- let Some(abs_path) = self.abs_path_from_stack_frame(&stack_frame) else {
+ let Some(abs_path) = Self::abs_path_from_stack_frame(&stack_frame) else {
return Task::ready(Err(anyhow!("Project path not found")));
};
@@ -294,12 +322,22 @@ impl StackFrameList {
let project_path = buffer.read(cx).project_path(cx).ok_or_else(|| {
anyhow!("Could not select a stack frame for unnamed buffer")
})?;
+
+ let open_preview = !workspace
+ .item_of_type::<StackTraceView>(cx)
+ .map(|viewer| {
+ workspace
+ .active_item(cx)
+ .is_some_and(|item| item.item_id() == viewer.item_id())
+ })
+ .unwrap_or_default();
+
anyhow::Ok(workspace.open_path_preview(
project_path,
None,
- false,
true,
true,
+ open_preview,
window,
cx,
))
@@ -332,7 +370,7 @@ impl StackFrameList {
})
}
- fn abs_path_from_stack_frame(&self, stack_frame: &dap::StackFrame) -> Option<Arc<Path>> {
+ pub(crate) fn abs_path_from_stack_frame(stack_frame: &dap::StackFrame) -> Option<Arc<Path>> {
stack_frame.source.as_ref().and_then(|s| {
s.path
.as_deref()
@@ -302,6 +302,7 @@ impl VariableList {
self.selected_stack_frame_id = Some(*stack_frame_id);
cx.notify();
}
+ StackFrameListEvent::BuiltEntries => {}
}
}
@@ -0,0 +1,453 @@
+use std::any::{Any, TypeId};
+
+use collections::HashMap;
+use dap::StackFrameId;
+use editor::{
+ Anchor, Bias, DebugStackFrameLine, Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer,
+ RowHighlightOptions, ToPoint, scroll::Autoscroll,
+};
+use gpui::{
+ AnyView, App, AppContext, Entity, EventEmitter, Focusable, IntoElement, Render, SharedString,
+ Subscription, Task, WeakEntity, Window,
+};
+use language::{BufferSnapshot, Capability, Point, Selection, SelectionGoal, TreeSitterOptions};
+use project::{Project, ProjectPath};
+use ui::{ActiveTheme as _, Context, ParentElement as _, Styled as _, div};
+use util::ResultExt as _;
+use workspace::{
+ Item, ItemHandle as _, ItemNavHistory, ToolbarItemLocation, Workspace,
+ item::{BreadcrumbText, ItemEvent},
+ searchable::SearchableItemHandle,
+};
+
+use crate::session::running::stack_frame_list::{StackFrameList, StackFrameListEvent};
+use anyhow::Result;
+
+pub(crate) struct StackTraceView {
+ editor: Entity<Editor>,
+ multibuffer: Entity<MultiBuffer>,
+ workspace: WeakEntity<Workspace>,
+ project: Entity<Project>,
+ stack_frame_list: Entity<StackFrameList>,
+ selected_stack_frame_id: Option<StackFrameId>,
+ highlights: Vec<(StackFrameId, Anchor)>,
+ excerpt_for_frames: collections::HashMap<ExcerptId, StackFrameId>,
+ refresh_task: Option<Task<Result<()>>>,
+ _subscription: Option<Subscription>,
+}
+
+impl StackTraceView {
+ pub(crate) fn new(
+ workspace: WeakEntity<Workspace>,
+ project: Entity<Project>,
+ stack_frame_list: Entity<StackFrameList>,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Self {
+ let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
+ let editor = cx.new(|cx| {
+ let mut editor =
+ Editor::for_multibuffer(multibuffer.clone(), Some(project.clone()), window, cx);
+ editor.set_vertical_scroll_margin(5, cx);
+ editor
+ });
+
+ cx.subscribe_in(&editor, window, |this, editor, event, window, cx| {
+ if let EditorEvent::SelectionsChanged { local: true } = event {
+ let excerpt_id = editor.update(cx, |editor, cx| {
+ let position: Point = editor.selections.newest(cx).head();
+
+ editor
+ .snapshot(window, cx)
+ .buffer_snapshot
+ .excerpt_containing(position..position)
+ .map(|excerpt| excerpt.id())
+ });
+
+ if let Some(stack_frame_id) = excerpt_id
+ .and_then(|id| this.excerpt_for_frames.get(&id))
+ .filter(|id| Some(**id) != this.selected_stack_frame_id)
+ {
+ this.stack_frame_list.update(cx, |list, cx| {
+ list.select_stack_frame_id(*stack_frame_id, window, cx);
+ });
+ }
+ }
+ })
+ .detach();
+
+ cx.subscribe_in(
+ &stack_frame_list,
+ window,
+ |this, stack_frame_list, event, window, cx| match event {
+ StackFrameListEvent::BuiltEntries => {
+ this.selected_stack_frame_id =
+ stack_frame_list.read(cx).selected_stack_frame_id();
+ this.update_excerpts(window, cx);
+ }
+ StackFrameListEvent::SelectedStackFrameChanged(selected_frame_id) => {
+ this.selected_stack_frame_id = Some(*selected_frame_id);
+ this.update_highlights(window, cx);
+
+ if let Some(frame_anchor) = this
+ .highlights
+ .iter()
+ .find(|(frame_id, _)| frame_id == selected_frame_id)
+ .map(|highlight| highlight.1)
+ {
+ this.editor.update(cx, |editor, cx| {
+ if frame_anchor.excerpt_id
+ != editor.selections.newest_anchor().head().excerpt_id
+ {
+ let auto_scroll =
+ Some(Autoscroll::center().for_anchor(frame_anchor));
+
+ editor.change_selections(auto_scroll, window, cx, |selections| {
+ let selection_id = selections.new_selection_id();
+
+ let selection = Selection {
+ id: selection_id,
+ start: frame_anchor,
+ end: frame_anchor,
+ goal: SelectionGoal::None,
+ reversed: false,
+ };
+
+ selections.select_anchors(vec![selection]);
+ })
+ }
+ });
+ }
+ }
+ },
+ )
+ .detach();
+
+ let mut this = Self {
+ editor,
+ multibuffer,
+ workspace,
+ project,
+ excerpt_for_frames: HashMap::default(),
+ highlights: Vec::default(),
+ stack_frame_list,
+ selected_stack_frame_id: None,
+ refresh_task: None,
+ _subscription: None,
+ };
+
+ this.update_excerpts(window, cx);
+ this
+ }
+
+ fn update_excerpts(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+ self.refresh_task.take();
+ self.editor.update(cx, |editor, cx| {
+ editor.clear_highlights::<DebugStackFrameLine>(cx)
+ });
+
+ let stack_frames = self
+ .stack_frame_list
+ .update(cx, |list, _| list.flatten_entries(false));
+
+ let frames_to_open: Vec<_> = stack_frames
+ .into_iter()
+ .filter_map(|frame| {
+ Some((
+ frame.id,
+ frame.line as u32 - 1,
+ StackFrameList::abs_path_from_stack_frame(&frame)?,
+ ))
+ })
+ .collect();
+
+ self.multibuffer
+ .update(cx, |multi_buffer, cx| multi_buffer.clear(cx));
+
+ let task = cx.spawn_in(window, async move |this, cx| {
+ let mut to_highlights = Vec::default();
+
+ for (stack_frame_id, line, abs_path) in frames_to_open {
+ let (worktree, relative_path) = this
+ .update(cx, |this, cx| {
+ this.workspace.update(cx, |workspace, cx| {
+ workspace.project().update(cx, |this, cx| {
+ this.find_or_create_worktree(&abs_path, false, cx)
+ })
+ })
+ })??
+ .await?;
+
+ let project_path = ProjectPath {
+ worktree_id: worktree.read_with(cx, |tree, _| tree.id())?,
+ path: relative_path.into(),
+ };
+
+ if let Some(buffer) = this
+ .read_with(cx, |this, _| this.project.clone())?
+ .update(cx, |project, cx| project.open_buffer(project_path, cx))?
+ .await
+ .log_err()
+ {
+ this.update(cx, |this, cx| {
+ this.multibuffer.update(cx, |multi_buffer, cx| {
+ let line_point = Point::new(line, 0);
+ let start_context = Self::heuristic_syntactic_expand(
+ &buffer.read(cx).snapshot(),
+ line_point,
+ );
+
+ // Users will want to see what happened before an active debug line in most cases
+ let range = ExcerptRange {
+ context: start_context..Point::new(line.saturating_add(1), 0),
+ primary: line_point..line_point,
+ };
+ multi_buffer.push_excerpts(buffer.clone(), vec![range], cx);
+
+ let line_anchor =
+ multi_buffer.buffer_point_to_anchor(&buffer, line_point, cx);
+
+ if let Some(line_anchor) = line_anchor {
+ this.excerpt_for_frames
+ .insert(line_anchor.excerpt_id, stack_frame_id);
+ to_highlights.push((stack_frame_id, line_anchor));
+ }
+ });
+ })
+ .ok();
+ }
+ }
+
+ this.update_in(cx, |this, window, cx| {
+ this.highlights = to_highlights;
+ this.update_highlights(window, cx);
+ })
+ .ok();
+
+ anyhow::Ok(())
+ });
+
+ self.refresh_task = Some(task);
+ }
+
+ fn update_highlights(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+ self.editor.update(cx, |editor, _| {
+ editor.clear_row_highlights::<DebugStackFrameLine>()
+ });
+
+ let stack_frames = self
+ .stack_frame_list
+ .update(cx, |session, _| session.flatten_entries(false));
+
+ let active_idx = self
+ .selected_stack_frame_id
+ .and_then(|id| {
+ stack_frames
+ .iter()
+ .enumerate()
+ .find_map(|(idx, frame)| if frame.id == id { Some(idx) } else { None })
+ })
+ .unwrap_or(0);
+
+ self.editor.update(cx, |editor, cx| {
+ let snapshot = editor.snapshot(window, cx).display_snapshot;
+ let first_color = cx.theme().colors().editor_debugger_active_line_background;
+
+ let color = first_color.opacity(0.5);
+
+ let mut is_first = true;
+
+ for (_, highlight) in self.highlights.iter().skip(active_idx) {
+ let position = highlight.to_point(&snapshot.buffer_snapshot);
+ let color = if is_first {
+ is_first = false;
+ first_color
+ } else {
+ color
+ };
+
+ let start = snapshot
+ .buffer_snapshot
+ .clip_point(Point::new(position.row, 0), Bias::Left);
+ let end = start + Point::new(1, 0);
+ let start = snapshot.buffer_snapshot.anchor_before(start);
+ let end = snapshot.buffer_snapshot.anchor_before(end);
+ editor.highlight_rows::<DebugStackFrameLine>(
+ start..end,
+ color,
+ RowHighlightOptions::default(),
+ cx,
+ );
+ }
+ })
+ }
+
+ fn heuristic_syntactic_expand(snapshot: &BufferSnapshot, selected_point: Point) -> Point {
+ let mut text_objects = snapshot.text_object_ranges(
+ selected_point..selected_point,
+ TreeSitterOptions::max_start_depth(4),
+ );
+
+ let mut start_position = text_objects
+ .find(|(_, obj)| matches!(obj, language::TextObject::AroundFunction))
+ .map(|(range, _)| snapshot.offset_to_point(range.start))
+ .map(|point| Point::new(point.row.max(selected_point.row.saturating_sub(8)), 0))
+ .unwrap_or(selected_point);
+
+ if start_position.row == selected_point.row {
+ start_position.row = start_position.row.saturating_sub(1);
+ }
+
+ start_position
+ }
+}
+
+impl Render for StackTraceView {
+ fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
+ div().size_full().child(self.editor.clone())
+ }
+}
+
+impl EventEmitter<EditorEvent> for StackTraceView {}
+impl Focusable for StackTraceView {
+ fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
+ self.editor.focus_handle(cx)
+ }
+}
+
+impl Item for StackTraceView {
+ type Event = EditorEvent;
+
+ fn to_item_events(event: &EditorEvent, f: impl FnMut(ItemEvent)) {
+ Editor::to_item_events(event, f)
+ }
+
+ fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+ self.editor
+ .update(cx, |editor, cx| editor.deactivated(window, cx));
+ }
+
+ fn navigate(
+ &mut self,
+ data: Box<dyn Any>,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> bool {
+ self.editor
+ .update(cx, |editor, cx| editor.navigate(data, window, cx))
+ }
+
+ fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> {
+ Some("Stack Frame Viewer".into())
+ }
+
+ fn tab_content_text(&self, _detail: usize, _: &App) -> SharedString {
+ "Stack Frames".into()
+ }
+
+ fn for_each_project_item(
+ &self,
+ cx: &App,
+ f: &mut dyn FnMut(gpui::EntityId, &dyn project::ProjectItem),
+ ) {
+ self.editor.for_each_project_item(cx, f)
+ }
+
+ fn is_singleton(&self, _: &App) -> bool {
+ false
+ }
+
+ fn set_nav_history(
+ &mut self,
+ nav_history: ItemNavHistory,
+ _: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ self.editor.update(cx, |editor, _| {
+ editor.set_nav_history(Some(nav_history));
+ });
+ }
+
+ fn is_dirty(&self, cx: &App) -> bool {
+ self.multibuffer.read(cx).is_dirty(cx)
+ }
+
+ fn has_deleted_file(&self, cx: &App) -> bool {
+ self.multibuffer.read(cx).has_deleted_file(cx)
+ }
+
+ fn has_conflict(&self, cx: &App) -> bool {
+ self.multibuffer.read(cx).has_conflict(cx)
+ }
+
+ fn can_save(&self, _: &App) -> bool {
+ true
+ }
+
+ fn save(
+ &mut self,
+ format: bool,
+ project: Entity<Project>,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Task<Result<()>> {
+ self.editor.save(format, project, window, cx)
+ }
+
+ fn save_as(
+ &mut self,
+ _: Entity<Project>,
+ _: ProjectPath,
+ _window: &mut Window,
+ _: &mut Context<Self>,
+ ) -> Task<Result<()>> {
+ unreachable!()
+ }
+
+ fn reload(
+ &mut self,
+ project: Entity<Project>,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> Task<Result<()>> {
+ self.editor.reload(project, window, cx)
+ }
+
+ fn act_as_type<'a>(
+ &'a self,
+ type_id: TypeId,
+ self_handle: &'a Entity<Self>,
+ _: &'a App,
+ ) -> Option<AnyView> {
+ if type_id == TypeId::of::<Self>() {
+ Some(self_handle.to_any())
+ } else if type_id == TypeId::of::<Editor>() {
+ Some(self.editor.to_any())
+ } else {
+ None
+ }
+ }
+
+ fn as_searchable(&self, _: &Entity<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+ Some(Box::new(self.editor.clone()))
+ }
+
+ fn breadcrumb_location(&self, _: &App) -> ToolbarItemLocation {
+ ToolbarItemLocation::PrimaryLeft
+ }
+
+ fn breadcrumbs(&self, theme: &theme::Theme, cx: &App) -> Option<Vec<BreadcrumbText>> {
+ self.editor.breadcrumbs(theme, cx)
+ }
+
+ fn added_to_workspace(
+ &mut self,
+ workspace: &mut Workspace,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ self.editor.update(cx, |editor, cx| {
+ editor.added_to_workspace(workspace, window, cx)
+ });
+ }
+}
@@ -190,7 +190,7 @@ 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(), list.selected_stack_frame_id())
+ (list.flatten_entries(true), list.selected_stack_frame_id())
});
assert_eq!(stack_frames, stack_frame_list);
@@ -431,7 +431,7 @@ 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(), list.selected_stack_frame_id())
+ (list.flatten_entries(true), list.selected_stack_frame_id())
});
assert_eq!(Some(1), stack_frame_id);
@@ -1452,7 +1452,7 @@ 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(), list.selected_stack_frame_id())
+ (list.flatten_entries(true), list.selected_stack_frame_id())
});
assert_eq!(Some(1), stack_frame_id);
@@ -1734,7 +1734,7 @@ 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(), list.selected_stack_frame_id())
+ (list.flatten_entries(true), list.selected_stack_frame_id())
});
let variable_list = running_state.variable_list().read(cx);
@@ -1789,7 +1789,7 @@ 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(), list.selected_stack_frame_id())
+ (list.flatten_entries(true), list.selected_stack_frame_id())
});
let variable_list = running_state.variable_list().read(cx);
@@ -289,6 +289,7 @@ impl InlayId {
}
pub enum ActiveDebugLine {}
+pub enum DebugStackFrameLine {}
enum DocumentHighlightRead {}
enum DocumentHighlightWrite {}
enum InputComposition {}
@@ -13880,7 +13881,10 @@ impl Editor {
Default::default(),
cx,
);
- self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
+
+ if self.buffer.read(cx).is_singleton() {
+ self.request_autoscroll(Autoscroll::center().for_anchor(start), cx);
+ }
}
pub fn go_to_definition(
@@ -16886,6 +16890,7 @@ impl Editor {
handled = true;
self.clear_row_highlights::<ActiveDebugLine>();
+
self.go_to_line::<ActiveDebugLine>(
multibuffer_anchor,
Some(cx.theme().colors().editor_debugger_active_line_background),
@@ -17900,9 +17905,7 @@ impl Editor {
let Some(project) = self.project.clone() else {
return;
};
- let Some(buffer) = self.buffer.read(cx).as_singleton() else {
- return;
- };
+
if !self.inline_value_cache.enabled {
let inlays = std::mem::take(&mut self.inline_value_cache.inlays);
self.splice_inlays(&inlays, Vec::new(), cx);
@@ -17920,15 +17923,24 @@ impl Editor {
.ok()?;
let inline_values = editor
- .update(cx, |_, cx| {
+ .update(cx, |editor, cx| {
let Some(current_execution_position) = current_execution_position else {
return Some(Task::ready(Ok(Vec::new())));
};
- // todo(debugger) when introducing multi buffer inline values check execution position's buffer id to make sure the text
- // anchor is in the same buffer
+ let buffer = editor.buffer.read_with(cx, |buffer, cx| {
+ let snapshot = buffer.snapshot(cx);
+
+ let excerpt = snapshot.excerpt_containing(
+ current_execution_position..current_execution_position,
+ )?;
+
+ editor.buffer.read(cx).buffer(excerpt.buffer_id())
+ })?;
+
let range =
buffer.read(cx).anchor_before(0)..current_execution_position.text_anchor;
+
project.inline_values(buffer, range, cx)
})
.ok()