Compare commits

...

5 Commits

Author SHA1 Message Date
Piotr Osiewicz
02a8a7dad2 Merge branch 'main' into multi-line-highlight-stack-trace 2025-06-16 12:32:27 +02:00
Piotr Osiewicz
1815b534f0 WIP2 2025-06-09 18:00:41 +02:00
Piotr Osiewicz
d08dd46186 clippy 2025-06-09 13:22:57 +02:00
Piotr Osiewicz
688375619c Merge branch 'main' into multi-line-highlight-stack-trace 2025-06-09 12:48:04 +02:00
Piotr Osiewicz
2ea4819127 wip 2025-06-09 00:16:19 +02:00
11 changed files with 259 additions and 284 deletions

View File

@@ -16632,56 +16632,6 @@ impl Editor {
}
}
// Returns true if the editor handled a go-to-line request
pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
maybe!({
let breakpoint_store = self.breakpoint_store.as_ref()?;
let Some(active_stack_frame) = breakpoint_store.read(cx).active_position().cloned()
else {
self.clear_row_highlights::<ActiveDebugLine>();
return None;
};
let position = active_stack_frame.position;
let buffer_id = position.buffer_id?;
let snapshot = self
.project
.as_ref()?
.read(cx)
.buffer_for_id(buffer_id, cx)?
.read(cx)
.snapshot();
let mut handled = false;
for (id, ExcerptRange { context, .. }) in
self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
{
if context.start.cmp(&position, &snapshot).is_ge()
|| context.end.cmp(&position, &snapshot).is_lt()
{
continue;
}
let snapshot = self.buffer.read(cx).snapshot(cx);
let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
handled = true;
self.clear_row_highlights::<ActiveDebugLine>();
self.go_to_line::<ActiveDebugLine>(
multibuffer_anchor,
Some(cx.theme().colors().editor_debugger_active_line_background),
window,
cx,
);
cx.notify();
}
handled.then_some(())
})
.is_some()
}
pub fn copy_file_name_without_extension(
&mut self,
_: &CopyFileNameWithoutExtension,

View File

@@ -934,6 +934,9 @@ impl DebugPanel {
cx: &mut Context<Self>,
) {
debug_assert!(self.sessions.contains(&session_item));
self.project.read(cx).dap_store().update(cx, |this, cx| {
this.set_active_session(&session_item.read(cx).session(cx), cx);
});
session_item.focus_handle(cx).focus(window);
session_item.update(cx, |this, cx| {
this.running_state().update(cx, |this, cx| {

View File

@@ -1400,7 +1400,9 @@ impl RunningState {
if self.thread_id.is_some_and(|id| id == thread_id) {
return;
}
self.session.update(cx, |this, cx| {
this.set_active_thread(thread_id, cx);
});
self.thread_id = Some(thread_id);
self.stack_frame_list
@@ -1482,18 +1484,6 @@ impl RunningState {
}
pub(crate) fn shutdown(&mut self, cx: &mut Context<Self>) {
self.workspace
.update(cx, |workspace, cx| {
workspace
.project()
.read(cx)
.breakpoint_store()
.update(cx, |store, cx| {
store.remove_active_position(Some(self.session_id), cx)
})
})
.log_err();
self.session.update(cx, |session, cx| {
session.shutdown(cx).detach();
})
@@ -1504,18 +1494,6 @@ impl RunningState {
return;
};
self.workspace
.update(cx, |workspace, cx| {
workspace
.project()
.read(cx)
.breakpoint_store()
.update(cx, |store, cx| {
store.remove_active_position(Some(self.session_id), cx)
})
})
.log_err();
self.session().update(cx, |state, cx| {
state.terminate_threads(Some(vec![thread_id; 1]), cx);
});

View File

@@ -353,22 +353,10 @@ impl StackFrameList {
state.thread_id.context("No selected thread ID found")
})??;
this.workspace.update(cx, |workspace, cx| {
let breakpoint_store = workspace.project().read(cx).breakpoint_store();
breakpoint_store.update(cx, |store, cx| {
store.set_active_position(
ActiveStackFrame {
session_id: this.session.read(cx).session_id(),
thread_id,
stack_frame_id,
path: abs_path,
position,
},
cx,
);
})
})
this.session.update(cx, |this, cx| {
this.set_active_stack_frame(stack_frame_id, cx);
});
Ok(())
})?
})
}

View File

@@ -14,7 +14,7 @@ use dap::{
},
};
use editor::{
ActiveDebugLine, Editor, EditorMode, MultiBuffer,
Editor, EditorMode, MultiBuffer, StackFrameMemberLine,
actions::{self},
};
use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
@@ -1596,7 +1596,8 @@ async fn test_active_debug_line_setting(executor: BackgroundExecutor, cx: &mut T
cx.run_until_parked();
main_editor.update_in(cx, |editor, window, cx| {
let active_debug_lines: Vec<_> = editor.highlighted_rows::<ActiveDebugLine>().collect();
let active_debug_lines: Vec<_> =
editor.highlighted_rows::<StackFrameMemberLine>().collect();
assert_eq!(
active_debug_lines.len(),
@@ -1613,7 +1614,8 @@ async fn test_active_debug_line_setting(executor: BackgroundExecutor, cx: &mut T
});
second_editor.update(cx, |editor, _| {
let active_debug_lines: Vec<_> = editor.highlighted_rows::<ActiveDebugLine>().collect();
let active_debug_lines: Vec<_> =
editor.highlighted_rows::<StackFrameMemberLine>().collect();
assert!(
active_debug_lines.is_empty(),
@@ -1671,7 +1673,8 @@ async fn test_active_debug_line_setting(executor: BackgroundExecutor, cx: &mut T
cx.run_until_parked();
second_editor.update_in(cx, |editor, window, cx| {
let active_debug_lines: Vec<_> = editor.highlighted_rows::<ActiveDebugLine>().collect();
let active_debug_lines: Vec<_> =
editor.highlighted_rows::<StackFrameMemberLine>().collect();
assert_eq!(
active_debug_lines.len(),
@@ -1688,7 +1691,8 @@ async fn test_active_debug_line_setting(executor: BackgroundExecutor, cx: &mut T
});
main_editor.update(cx, |editor, _| {
let active_debug_lines: Vec<_> = editor.highlighted_rows::<ActiveDebugLine>().collect();
let active_debug_lines: Vec<_> =
editor.highlighted_rows::<StackFrameMemberLine>().collect();
assert!(
active_debug_lines.is_empty(),
@@ -1711,7 +1715,8 @@ async fn test_active_debug_line_setting(executor: BackgroundExecutor, cx: &mut T
cx.run_until_parked();
second_editor.update(cx, |editor, _| {
let active_debug_lines: Vec<_> = editor.highlighted_rows::<ActiveDebugLine>().collect();
let active_debug_lines: Vec<_> =
editor.highlighted_rows::<StackFrameMemberLine>().collect();
assert!(
active_debug_lines.is_empty(),
@@ -1720,7 +1725,8 @@ async fn test_active_debug_line_setting(executor: BackgroundExecutor, cx: &mut T
});
main_editor.update(cx, |editor, _| {
let active_debug_lines: Vec<_> = editor.highlighted_rows::<ActiveDebugLine>().collect();
let active_debug_lines: Vec<_> =
editor.highlighted_rows::<StackFrameMemberLine>().collect();
assert!(
active_debug_lines.is_empty(),
@@ -1738,7 +1744,8 @@ async fn test_active_debug_line_setting(executor: BackgroundExecutor, cx: &mut T
shutdown_session.await.unwrap();
main_editor.update(cx, |editor, _| {
let active_debug_lines: Vec<_> = editor.highlighted_rows::<ActiveDebugLine>().collect();
let active_debug_lines: Vec<_> =
editor.highlighted_rows::<StackFrameMemberLine>().collect();
assert!(
active_debug_lines.is_empty(),
@@ -1747,7 +1754,8 @@ async fn test_active_debug_line_setting(executor: BackgroundExecutor, cx: &mut T
});
second_editor.update(cx, |editor, _| {
let active_debug_lines: Vec<_> = editor.highlighted_rows::<ActiveDebugLine>().collect();
let active_debug_lines: Vec<_> =
editor.highlighted_rows::<StackFrameMemberLine>().collect();
assert!(
active_debug_lines.is_empty(),

View File

@@ -344,7 +344,7 @@ async fn test_select_stack_frame(executor: BackgroundExecutor, cx: &mut TestAppC
let snapshot = editor.snapshot(window, cx);
editor
.highlighted_rows::<editor::ActiveDebugLine>()
.highlighted_rows::<editor::StackFrameMemberLine>()
.map(|(range, _)| {
let start = range.start.to_point(&snapshot.buffer_snapshot);
let end = range.end.to_point(&snapshot.buffer_snapshot);
@@ -412,7 +412,7 @@ async fn test_select_stack_frame(executor: BackgroundExecutor, cx: &mut TestAppC
let snapshot = editor.snapshot(window, cx);
editor
.highlighted_rows::<editor::ActiveDebugLine>()
.highlighted_rows::<editor::StackFrameMemberLine>()
.map(|(range, _)| {
let start = range.start.to_point(&snapshot.buffer_snapshot);
let end = range.end.to_point(&snapshot.buffer_snapshot);

View File

@@ -79,6 +79,7 @@ use futures::{
stream::FuturesUnordered,
};
use fuzzy::{StringMatch, StringMatchCandidate};
use project::debugger::dap_store::ActiveSessionEvent;
use ::git::blame::BlameEntry;
use ::git::{Restore, blame::ParsedCommitMessage};
@@ -110,7 +111,7 @@ use itertools::Itertools;
use language::{
AutoindentMode, BracketMatch, BracketPair, Buffer, Capability, CharKind, CodeLabel,
CursorShape, DiagnosticEntry, DiagnosticSourceKind, DiffOptions, DocumentationConfig,
EditPredictionsMode, EditPreview, HighlightedText, IndentKind, IndentSize, Language,
EditPredictionsMode, EditPreview, HighlightedText, IndentKind, IndentSize, Language, LocalFile,
OffsetRangeExt, Point, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions,
WordsQuery,
language_settings::{
@@ -288,7 +289,8 @@ impl InlayId {
}
}
pub enum ActiveDebugLine {}
/// A highlight for a line that's a member of currently visible stack frame.
pub enum StackFrameMemberLine {}
pub enum DebugStackFrameLine {}
enum DocumentHighlightRead {}
enum DocumentHighlightWrite {}
@@ -1838,21 +1840,12 @@ impl Editor {
};
project_subscriptions.push(cx.subscribe_in(
&project.read(cx).breakpoint_store(),
&project.read(cx).dap_store(),
window,
|editor, _, event, window, cx| match event {
BreakpointStoreEvent::ClearDebugLines => {
editor.clear_row_highlights::<ActiveDebugLine>();
editor.refresh_inline_values(cx);
|editor, _, event, window, cx| {
if let ActiveSessionEvent::StackTraceUpdated = event {
editor.update_active_debug_line_highlights(window, cx);
}
BreakpointStoreEvent::SetDebugLine => {
if editor.go_to_active_debug_line(window, cx) {
cx.stop_propagation();
}
editor.refresh_inline_values(cx);
}
_ => {}
},
));
}
@@ -2184,6 +2177,7 @@ impl Editor {
jsx_tag_auto_close::refresh_enabled_in_any_buffer(&mut editor, &buffer, cx);
if full_mode {
editor.update_active_debug_line_highlights(window, cx);
let should_auto_hide_scrollbars = cx.should_auto_hide_scrollbars();
cx.set_global(ScrollbarAutoHide(should_auto_hide_scrollbars));
@@ -2191,8 +2185,6 @@ impl Editor {
editor.start_git_blame_inline(false, window, cx);
}
editor.go_to_active_debug_line(window, cx);
if let Some(buffer) = buffer.read(cx).as_singleton() {
if let Some(project) = editor.project.as_ref() {
let handle = project.update(cx, |project, cx| {
@@ -7556,14 +7548,18 @@ impl Editor {
let Some(breakpoint_store) = self.breakpoint_store.clone() else {
return breakpoint_display_points;
};
let snapshot = self.snapshot(window, cx);
let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
let Some(project) = self.project.as_ref() else {
return breakpoint_display_points;
};
let snapshot = self.snapshot(window, cx);
let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
let active_session = project
.read(cx)
.active_debug_session(cx)
.map(|session| session.read(cx).session_id());
let range = snapshot.display_point_to_point(DisplayPoint::new(range.start, 0), Bias::Left)
..snapshot.display_point_to_point(DisplayPoint::new(range.end, 0), Bias::Right);
@@ -7582,6 +7578,7 @@ impl Editor {
buffer_snapshot.anchor_before(range.start)
..buffer_snapshot.anchor_after(range.end),
),
active_session,
buffer_snapshot,
cx,
);
@@ -10188,7 +10185,10 @@ impl Editor {
.buffer_snapshot
.buffer_id_for_excerpt(breakpoint_position.excerpt_id)
})?;
let active_session_id = project
.read(cx)
.active_debug_session(cx)
.map(|session| session.read(cx).session_id());
let enclosing_excerpt = breakpoint_position.excerpt_id;
let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
let buffer_snapshot = buffer.read(cx).snapshot();
@@ -10210,6 +10210,7 @@ impl Editor {
.breakpoints(
&buffer,
Some(breakpoint_position.text_anchor..anchor_end.text_anchor),
active_session_id,
&buffer_snapshot,
cx,
)
@@ -13513,6 +13514,62 @@ impl Editor {
Ok(())
}
fn update_active_debug_line_highlights(&mut self, window: &mut Window, cx: &mut Context<Self>) {
maybe!({
let project = self.project.as_ref()?.read(cx);
self.clear_row_highlights::<StackFrameMemberLine>();
let highlights_for_active_thread = project.active_debug_session_with_stack(cx)?;
let multi_buffer = self.buffer.read(cx);
let abs_path_to_buffer = multi_buffer
.all_buffers()
.into_iter()
.filter_map(|buffer| {
let path: Arc<Path> = project::File::from_dyn(buffer.read(cx).file())?
.abs_path(cx)
.into();
Some((path, buffer))
})
.collect::<HashMap<_, _>>();
for entry in highlights_for_active_thread.stack.into_iter() {
let Some(buffer) = abs_path_to_buffer.get(&entry.abs_path) else {
continue;
};
let multi_buffer = self.buffer.read(cx);
let Some(start) = multi_buffer.buffer_point_to_anchor(
buffer,
Point {
row: entry.row,
column: 0,
},
cx,
) else {
continue;
};
let end = multi_buffer
.buffer_point_to_anchor(
buffer,
Point {
row: entry.row + 1,
column: 0,
},
cx,
)
.unwrap_or_else(Anchor::max);
self.highlight_rows::<StackFrameMemberLine>(
start..end,
gpui::red(),
RowHighlightOptions::default(),
cx,
);
}
self.refresh_inline_values(cx);
Some(())
});
}
pub fn toggle_comments(
&mut self,
action: &ToggleComments,
@@ -17827,57 +17884,6 @@ impl Editor {
}
}
// Returns true if the editor handled a go-to-line request
pub fn go_to_active_debug_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> bool {
maybe!({
let breakpoint_store = self.breakpoint_store.as_ref()?;
let Some(active_stack_frame) = breakpoint_store.read(cx).active_position().cloned()
else {
self.clear_row_highlights::<ActiveDebugLine>();
return None;
};
let position = active_stack_frame.position;
let buffer_id = position.buffer_id?;
let snapshot = self
.project
.as_ref()?
.read(cx)
.buffer_for_id(buffer_id, cx)?
.read(cx)
.snapshot();
let mut handled = false;
for (id, ExcerptRange { context, .. }) in
self.buffer.read(cx).excerpts_for_buffer(buffer_id, cx)
{
if context.start.cmp(&position, &snapshot).is_ge()
|| context.end.cmp(&position, &snapshot).is_lt()
{
continue;
}
let snapshot = self.buffer.read(cx).snapshot(cx);
let multibuffer_anchor = snapshot.anchor_in_excerpt(id, position)?;
handled = true;
self.clear_row_highlights::<ActiveDebugLine>();
self.go_to_line::<ActiveDebugLine>(
multibuffer_anchor,
Some(cx.theme().colors().editor_debugger_active_line_background),
window,
cx,
);
cx.notify();
}
handled.then_some(())
})
.is_some()
}
pub fn copy_file_name_without_extension(
&mut self,
_: &CopyFileNameWithoutExtension,
@@ -18945,7 +18951,7 @@ impl Editor {
let current_execution_position = self
.highlighted_rows
.get(&TypeId::of::<ActiveDebugLine>())
.get(&TypeId::of::<StackFrameMemberLine>())
.and_then(|lines| lines.last().map(|line| line.range.start));
self.inline_value_cache.refresh_task = cx.spawn(async move |editor, cx| {
@@ -21301,7 +21307,7 @@ impl SemanticsProvider for Entity<Project> {
self.update(cx, |project, cx| {
if project
.active_debug_session(cx)
.is_some_and(|(session, _)| session.read(cx).any_stopped_thread())
.is_some_and(|session| session.read(cx).any_stopped_thread())
{
return true;
}
@@ -21320,9 +21326,9 @@ impl SemanticsProvider for Entity<Project> {
cx: &mut App,
) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {
self.update(cx, |project, cx| {
let (session, active_stack_frame) = project.active_debug_session(cx)?;
let session = project.active_debug_session(cx)?;
Some(project.inline_values(session, active_stack_frame, buffer_handle, range, cx))
Some(project.inline_values(session, buffer_handle, range, cx))
})
}

View File

@@ -157,7 +157,6 @@ pub struct ActiveStackFrame {
pub struct BreakpointStore {
breakpoints: BTreeMap<Arc<Path>, BreakpointsInFile>,
downstream_client: Option<(AnyProtoClient, u64)>,
active_stack_frame: Option<ActiveStackFrame>,
// E.g ssh
mode: BreakpointStoreMode,
}
@@ -175,7 +174,6 @@ impl BreakpointStore {
buffer_store,
}),
downstream_client: None,
active_stack_frame: Default::default(),
}
}
@@ -187,7 +185,6 @@ impl BreakpointStore {
_upstream_project_id: upstream_project_id,
}),
downstream_client: None,
active_stack_frame: Default::default(),
}
}
@@ -607,15 +604,12 @@ impl BreakpointStore {
&'a self,
buffer: &'a Entity<Buffer>,
range: Option<Range<text::Anchor>>,
active_session_id: Option<SessionId>,
buffer_snapshot: &'a BufferSnapshot,
cx: &App,
) -> impl Iterator<Item = (&'a BreakpointWithPosition, Option<BreakpointSessionState>)> + 'a
{
let abs_path = Self::abs_path_from_buffer(buffer, cx);
let active_session_id = self
.active_stack_frame
.as_ref()
.map(|frame| frame.session_id);
abs_path
.and_then(|path| self.breakpoints.get(&path))
.into_iter()
@@ -639,46 +633,6 @@ impl BreakpointStore {
})
}
pub fn active_position(&self) -> Option<&ActiveStackFrame> {
self.active_stack_frame.as_ref()
}
pub fn remove_active_position(
&mut self,
session_id: Option<SessionId>,
cx: &mut Context<Self>,
) {
if let Some(session_id) = session_id {
self.active_stack_frame
.take_if(|active_stack_frame| active_stack_frame.session_id == session_id);
} else {
self.active_stack_frame.take();
}
cx.emit(BreakpointStoreEvent::ClearDebugLines);
cx.notify();
}
pub fn set_active_position(&mut self, position: ActiveStackFrame, cx: &mut Context<Self>) {
if self
.active_stack_frame
.as_ref()
.is_some_and(|active_position| active_position == &position)
{
cx.emit(BreakpointStoreEvent::SetDebugLine);
return;
}
if self.active_stack_frame.is_some() {
cx.emit(BreakpointStoreEvent::ClearDebugLines);
}
self.active_stack_frame = Some(position);
cx.emit(BreakpointStoreEvent::SetDebugLine);
cx.notify();
}
pub fn breakpoint_at_row(
&self,
path: &Path,

View File

@@ -6,16 +6,17 @@ use super::{
};
use crate::{
InlayHint, InlayHintLabel, ProjectEnvironment, ResolveState,
debugger::session::{SourceStackFrameItem, StackFrame},
project_settings::ProjectSettings,
terminals::{SshCommand, wrap_for_ssh},
worktree_store::WorktreeStore,
};
use anyhow::{Context as _, Result, anyhow};
use async_trait::async_trait;
use collections::HashMap;
use collections::{HashMap, IndexSet};
use dap::{
Capabilities, CompletionItem, CompletionsArguments, DapRegistry, DebugRequest,
EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, Source, StackFrameId,
EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, Source,
adapters::{
DapDelegate, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition, TcpArguments,
},
@@ -30,7 +31,9 @@ use futures::{
channel::mpsc::{self, UnboundedSender},
future::{Shared, join_all},
};
use gpui::{App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task};
use gpui::{
App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, Task,
};
use http_client::HttpClient;
use language::{Buffer, LanguageToolchainStore, language_settings::InlayHintKind};
use node_runtime::NodeRuntime;
@@ -66,6 +69,12 @@ pub enum DapStoreEvent {
RemoteHasInitialized,
}
#[derive(Clone, Copy)]
pub enum ActiveSessionEvent {
/// There's a new stack trace, thus all existing highlight are invalidated.
StackTraceUpdated,
}
enum DapStoreMode {
Local(LocalDapStore),
Ssh(SshDapStore),
@@ -92,10 +101,18 @@ pub struct DapStore {
breakpoint_store: Entity<BreakpointStore>,
worktree_store: Entity<WorktreeStore>,
sessions: BTreeMap<SessionId, Entity<Session>>,
active_session: Option<(Entity<Session>, Subscription)>,
next_session_id: u32,
}
impl EventEmitter<DapStoreEvent> for DapStore {}
impl EventEmitter<ActiveSessionEvent> for DapStore {}
#[derive(Clone)]
pub struct ActiveSessionWithStack {
session: Entity<Session>,
pub stack: IndexSet<SourceStackFrameItem>,
}
impl DapStore {
pub fn init(client: &AnyProtoClient, cx: &mut App) {
@@ -173,6 +190,7 @@ impl DapStore {
breakpoint_store,
worktree_store,
sessions: Default::default(),
active_session: None,
}
}
@@ -389,6 +407,12 @@ impl DapStore {
move |this: &mut DapStore, _, event: &SessionStateEvent, cx| match event {
SessionStateEvent::Shutdown => {
this.shutdown_session(session_id, cx).detach_and_log_err(cx);
let session = this
.active_session
.take_if(|(session, _)| session.read(cx).session_id() == session_id);
if session.is_some() {
cx.emit(ActiveSessionEvent::StackTraceUpdated);
}
}
SessionStateEvent::Restart | SessionStateEvent::SpawnChildSession { .. } => {}
SessionStateEvent::Running => {
@@ -574,14 +598,14 @@ impl DapStore {
pub fn resolve_inline_value_locations(
&self,
session: Entity<Session>,
stack_frame_id: StackFrameId,
buffer_handle: Entity<Buffer>,
inline_value_locations: Vec<dap::inline_value::InlineValueLocation>,
cx: &mut Context<Self>,
) -> Task<Result<Vec<InlayHint>>> {
let snapshot = buffer_handle.read(cx).snapshot();
let all_variables = session.read(cx).variables_by_stack_frame_id(stack_frame_id);
let all_variables = session.read(cx).variables_for_active_stack_frame_id();
let id = session.read(cx).active_stack_frame();
fn format_value(mut value: String) -> String {
const LIMIT: usize = 100;
@@ -599,6 +623,9 @@ impl DapStore {
}
cx.spawn(async move |_, cx| {
let Some(stack_frame_id) = id else {
return Ok(vec![]);
};
let mut inlay_hints = Vec::with_capacity(inline_value_locations.len());
for inline_value_location in inline_value_locations.iter() {
let point = snapshot.point_to_point_utf16(language::Point::new(
@@ -828,6 +855,33 @@ impl DapStore {
})
})
}
pub(crate) fn active_session(&self) -> Option<&Entity<Session>> {
self.active_session.as_ref().map(|(session, _)| session)
}
pub fn set_active_session(&mut self, session: &Entity<Session>, cx: &mut Context<Self>) {
self.active_session = Some((
session.clone(),
cx.subscribe(session, |_, session, e: &ActiveSessionEvent, cx| {
cx.emit(*e);
}),
));
cx.notify();
}
pub(crate) fn active_session_with_stack(&self, cx: &App) -> Option<ActiveSessionWithStack> {
self.active_session.as_ref().map(|(session, _)| {
let stack = session
.read(cx)
.stack_frames_with_source_for_active_thread()
.unwrap_or_default();
ActiveSessionWithStack {
stack,
session: session.clone(),
}
})
}
}
#[derive(Clone)]

View File

@@ -1,4 +1,5 @@
use crate::debugger::breakpoint_store::BreakpointSessionState;
use crate::debugger::dap_store::ActiveSessionEvent;
use super::breakpoint_store::{
BreakpointStore, BreakpointStoreEvent, BreakpointUpdatedReason, SourceBreakpoint,
@@ -68,6 +69,13 @@ impl From<u64> for ThreadId {
}
}
#[derive(Clone, Hash, PartialEq, Eq)]
pub struct SourceStackFrameItem {
pub row: u32,
pub is_active: bool,
pub abs_path: Arc<Path>,
}
#[derive(Clone, Debug)]
pub struct StackFrame {
pub dap: dap::StackFrame,
@@ -619,6 +627,8 @@ pub struct Session {
ignore_breakpoints: bool,
exception_breakpoints: BTreeMap<String, (ExceptionBreakpointsFilter, IsEnabled)>,
background_tasks: Vec<Task<()>>,
active_thread_id: Option<ThreadId>,
active_stack_frame: Option<StackFrameId>,
}
trait CacheableCommand: Any + Send + Sync {
@@ -783,6 +793,8 @@ impl Session {
exception_breakpoints: Default::default(),
label,
adapter,
active_stack_frame: None,
active_thread_id: None,
};
this
@@ -1317,9 +1329,6 @@ impl Session {
Events::Continued(event) => {
if event.all_threads_continued.unwrap_or_default() {
self.thread_states.continue_all_threads();
self.breakpoint_store.update(cx, |store, cx| {
store.remove_active_position(Some(self.session_id()), cx)
});
} else {
self.thread_states
.continue_thread(ThreadId(event.thread_id));
@@ -1327,9 +1336,7 @@ impl Session {
// todo(debugger): We should be able to get away with only invalidating generic if all threads were continued
self.invalidate_generic();
}
Events::Exited(_event) => {
self.clear_active_debug_line(cx);
}
Events::Exited(_event) => {}
Events::Terminated(_) => {
self.shutdown(cx).detach();
}
@@ -1692,37 +1699,13 @@ impl Session {
thread_id: ThreadId,
) -> impl FnOnce(&mut Self, Result<T::Response>, &mut Context<Self>) -> Option<T::Response> + 'static
{
move |this, response, cx| match response.log_err() {
Some(response) => {
this.breakpoint_store.update(cx, |store, cx| {
store.remove_active_position(Some(this.session_id()), cx)
});
Some(response)
}
None => {
this.thread_states.stop_thread(thread_id);
cx.notify();
None
}
move |this, response, cx| {
this.thread_states.continue_thread(thread_id);
cx.notify();
response.log_err()
}
}
fn clear_active_debug_line_response(
&mut self,
response: Result<()>,
cx: &mut Context<Session>,
) -> Option<()> {
response.log_err()?;
self.clear_active_debug_line(cx);
Some(())
}
fn clear_active_debug_line(&mut self, cx: &mut Context<Session>) {
self.breakpoint_store.update(cx, |store, cx| {
store.remove_active_position(Some(self.id), cx)
});
}
pub fn pause_thread(&mut self, thread_id: ThreadId, cx: &mut Context<Self>) {
self.request(
PauseCommand {
@@ -1782,7 +1765,7 @@ impl Session {
TerminateCommand {
restart: Some(false),
},
Self::clear_active_debug_line_response,
Self::empty_response,
cx,
)
} else {
@@ -1792,7 +1775,7 @@ impl Session {
terminate_debuggee: Some(true),
suspend_debuggee: Some(false),
},
Self::clear_active_debug_line_response,
Self::empty_response,
cx,
)
};
@@ -2051,6 +2034,55 @@ impl Session {
}
}
pub fn set_active_stack_frame(&mut self, stack_frame: StackFrameId, cx: &mut Context<Self>) {
self.active_stack_frame = Some(stack_frame);
cx.notify();
}
pub fn set_active_thread(&mut self, thread_id: ThreadId, cx: &mut Context<Self>) {
self.active_thread_id = Some(thread_id);
self.active_stack_frame.take();
cx.notify();
}
pub(crate) fn active_stack_frame(&self) -> Option<StackFrameId> {
self.active_stack_frame
}
pub(crate) fn active_thread_id(&self) -> Option<ThreadId> {
self.active_thread_id
}
pub(crate) fn stack_frames_with_source_for_active_thread(
&self,
) -> Option<IndexSet<SourceStackFrameItem>> {
self.active_thread_id.map(|tid| {
let active_stack_frame = self.active_stack_frame;
self.threads
.get(&tid)
.into_iter()
.flat_map(|thread| {
thread.stack_frame_ids.iter().filter_map(|id| {
self.stack_frames.get(id).and_then(|frame| {
let source = frame.dap.source.as_ref()?;
let path: &Path = source.path.as_ref().map(|p| p.as_ref())?;
if !path.is_absolute() {
return None;
}
let mut line = frame.dap.line.checked_sub(1)?;
let row = line.try_into().unwrap_or(u32::MAX);
let is_active = Some(*id) == active_stack_frame;
Some(SourceStackFrameItem {
row,
is_active,
abs_path: Arc::from(path),
})
})
})
})
.collect()
})
}
pub fn scopes(&mut self, stack_frame_id: u64, cx: &mut Context<Self>) -> &[dap::Scope] {
if self.requests.contains_key(&TypeId::of::<ThreadsCommand>())
&& self
@@ -2092,8 +2124,11 @@ impl Session {
.unwrap_or_default()
}
pub fn variables_by_stack_frame_id(&self, stack_frame_id: StackFrameId) -> Vec<dap::Variable> {
let Some(stack_frame) = self.stack_frames.get(&stack_frame_id) else {
pub fn variables_for_active_stack_frame_id(&self) -> Vec<dap::Variable> {
let Some(stack_frame) = self
.active_stack_frame
.and_then(|id| self.stack_frames.get(&id))
else {
return Vec::new();
};
@@ -2277,12 +2312,13 @@ impl Session {
TerminateThreadsCommand {
thread_ids: thread_ids.map(|ids| ids.into_iter().map(|id| id.0).collect()),
},
Self::clear_active_debug_line_response,
|_, response, cx| {
cx.emit(ActiveSessionEvent::StackTraceUpdated);
response.ok()
},
cx,
)
.detach();
} else {
self.shutdown(cx).detach();
}
}
@@ -2290,3 +2326,5 @@ impl Session {
self.thread_states.thread_state(thread_id)
}
}
impl EventEmitter<ActiveSessionEvent> for Session {}

View File

@@ -31,7 +31,7 @@ use git_store::{Repository, RepositoryId};
pub mod search_history;
mod yarn;
use crate::git_store::GitStore;
use crate::{debugger::dap_store::ActiveSessionWithStack, git_store::GitStore};
pub use git_store::{
ConflictRegion, ConflictSet, ConflictSetSnapshot, ConflictSetUpdate,
git_traversal::{ChildEntriesGitIter, GitEntry, GitEntryRef, GitTraversal},
@@ -51,7 +51,7 @@ use collections::{BTreeSet, HashMap, HashSet};
use debounced_delay::DebouncedDelay;
pub use debugger::breakpoint_store::BreakpointWithPosition;
use debugger::{
breakpoint_store::{ActiveStackFrame, BreakpointStore},
breakpoint_store::BreakpointStore,
dap_store::{DapStore, DapStoreEvent},
session::Session,
};
@@ -1678,13 +1678,12 @@ impl Project {
self.breakpoint_store.clone()
}
pub fn active_debug_session(&self, cx: &App) -> Option<(Entity<Session>, ActiveStackFrame)> {
let active_position = self.breakpoint_store.read(cx).active_position()?;
let session = self
.dap_store
.read(cx)
.session_by_id(active_position.session_id)?;
Some((session, active_position.clone()))
pub fn active_debug_session(&self, cx: &App) -> Option<Entity<Session>> {
self.dap_store.read(cx).active_session().cloned()
}
pub fn active_debug_session_with_stack(&self, cx: &App) -> Option<ActiveSessionWithStack> {
self.dap_store.read(cx).active_session_with_stack(cx)
}
pub fn lsp_store(&self) -> Entity<LspStore> {
@@ -3644,7 +3643,6 @@ impl Project {
pub fn inline_values(
&mut self,
session: Entity<Session>,
active_stack_frame: ActiveStackFrame,
buffer_handle: Entity<Buffer>,
range: Range<text::Anchor>,
cx: &mut Context<Self>,
@@ -3679,13 +3677,11 @@ impl Project {
row,
);
let stack_frame_id = active_stack_frame.stack_frame_id;
cx.spawn(async move |this, cx| {
this.update(cx, |project, cx| {
project.dap_store().update(cx, |dap_store, cx| {
dap_store.resolve_inline_value_locations(
session,
stack_frame_id,
buffer_handle,
inline_value_locations,
cx,