Compare commits
5 Commits
settings-d
...
multi-line
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02a8a7dad2 | ||
|
|
1815b534f0 | ||
|
|
d08dd46186 | ||
|
|
688375619c | ||
|
|
2ea4819127 |
@@ -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,
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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(())
|
||||
})?
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user