Allow users to toggle ignore breakpoints (#62)
* Add allow users to ignore breakpoints * Add different colors for both states * Add source name for breakpoints * Move ignore breakpoints to dap store * Change icon instead of color * Add action for ignore breakpoints for a client * Remove spacing * Fix compile error * Return task instead of detaching itself
This commit is contained in:
1
assets/icons/debug_ignore_breakpoints.svg
Normal file
1
assets/icons/debug_ignore_breakpoints.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-off"><path d="m2 2 20 20"/><path d="M8.35 2.69A10 10 0 0 1 21.3 15.65"/><path d="M19.08 19.08A10 10 0 1 1 4.92 4.92"/></svg>
|
||||
|
After Width: | Height: | Size: 334 B |
@@ -32,6 +32,7 @@ use workspace::{
|
||||
};
|
||||
use workspace::{
|
||||
pane, Continue, Disconnect, Pane, Pause, Restart, Start, StepInto, StepOut, StepOver, Stop,
|
||||
ToggleIgnoreBreakpoints,
|
||||
};
|
||||
|
||||
pub enum DebugPanelEvent {
|
||||
@@ -174,6 +175,7 @@ impl DebugPanel {
|
||||
TypeId::of::<Disconnect>(),
|
||||
TypeId::of::<Pause>(),
|
||||
TypeId::of::<Restart>(),
|
||||
TypeId::of::<ToggleIgnoreBreakpoints>(),
|
||||
];
|
||||
|
||||
if has_active_session {
|
||||
|
||||
@@ -477,6 +477,18 @@ impl DebugPanelItem {
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn toggle_ignore_breakpoints(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.project().update(cx, |project, cx| {
|
||||
project
|
||||
.toggle_ignore_breakpoints(&self.client_id, cx)
|
||||
.detach_and_log_err(cx);
|
||||
})
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<DebugPanelItemEvent> for DebugPanelItem {}
|
||||
@@ -633,6 +645,25 @@ impl Render for DebugPanelItem {
|
||||
|| thread_status == ThreadStatus::Ended,
|
||||
)
|
||||
.tooltip(move |cx| Tooltip::text("Disconnect", cx)),
|
||||
)
|
||||
.child(
|
||||
IconButton::new(
|
||||
"debug-ignore-breakpoints",
|
||||
if self.dap_store.read(cx).ignore_breakpoints(&self.client_id) {
|
||||
IconName::DebugIgnoreBreakpoints
|
||||
} else {
|
||||
IconName::DebugBreakpoint
|
||||
},
|
||||
)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
this.toggle_ignore_breakpoints(cx);
|
||||
}))
|
||||
.disabled(
|
||||
thread_status == ThreadStatus::Exited
|
||||
|| thread_status == ThreadStatus::Ended,
|
||||
)
|
||||
.tooltip(move |cx| Tooltip::text("Ignore breakpoints", cx)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
|
||||
@@ -5,7 +5,7 @@ use settings::Settings;
|
||||
use ui::ViewContext;
|
||||
use workspace::{
|
||||
Continue, Pause, Restart, ShutdownDebugAdapters, Start, StepInto, StepOut, StepOver, Stop,
|
||||
Workspace,
|
||||
ToggleIgnoreBreakpoints, Workspace,
|
||||
};
|
||||
|
||||
mod attach_modal;
|
||||
@@ -102,6 +102,19 @@ pub fn init(cx: &mut AppContext) {
|
||||
active_item.update(cx, |item, cx| item.restart_client(cx))
|
||||
});
|
||||
})
|
||||
.register_action(
|
||||
|workspace: &mut Workspace, _: &ToggleIgnoreBreakpoints, cx| {
|
||||
let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
|
||||
|
||||
debug_panel.update(cx, |panel, cx| {
|
||||
let Some(active_item) = panel.active_debug_panel_item(cx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
active_item.update(cx, |item, cx| item.toggle_ignore_breakpoints(cx))
|
||||
});
|
||||
},
|
||||
)
|
||||
.register_action(|workspace: &mut Workspace, _: &Pause, cx| {
|
||||
let debug_panel = workspace.panel::<DebugPanel>(cx).unwrap();
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ pub struct DebugPosition {
|
||||
pub struct DapStore {
|
||||
next_client_id: AtomicUsize,
|
||||
delegate: DapAdapterDelegate,
|
||||
ignore_breakpoints: HashSet<DebugAdapterClientId>,
|
||||
breakpoints: BTreeMap<ProjectPath, HashSet<Breakpoint>>,
|
||||
active_debug_line: Option<(ProjectPath, DebugPosition)>,
|
||||
capabilities: HashMap<DebugAdapterClientId, Capabilities>,
|
||||
@@ -103,6 +104,7 @@ impl DapStore {
|
||||
breakpoints: Default::default(),
|
||||
capabilities: HashMap::default(),
|
||||
next_client_id: Default::default(),
|
||||
ignore_breakpoints: Default::default(),
|
||||
delegate: DapAdapterDelegate::new(
|
||||
http_client.clone(),
|
||||
node_runtime.clone(),
|
||||
@@ -192,6 +194,16 @@ impl DapStore {
|
||||
&self.breakpoints
|
||||
}
|
||||
|
||||
pub fn ignore_breakpoints(&self, client_id: &DebugAdapterClientId) -> bool {
|
||||
self.ignore_breakpoints.contains(client_id)
|
||||
}
|
||||
|
||||
pub fn toggle_ignore_breakpoints(&mut self, client_id: &DebugAdapterClientId) {
|
||||
if !self.ignore_breakpoints.remove(client_id) {
|
||||
self.ignore_breakpoints.insert(*client_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn breakpoint_at_row(
|
||||
&self,
|
||||
row: u32,
|
||||
@@ -1024,6 +1036,7 @@ impl DapStore {
|
||||
|
||||
cx.emit(DapStoreEvent::DebugClientStopped(*client_id));
|
||||
|
||||
self.ignore_breakpoints.remove(client_id);
|
||||
let capabilities = self.capabilities.remove(client_id);
|
||||
|
||||
cx.background_executor().spawn(async move {
|
||||
@@ -1059,7 +1072,7 @@ impl DapStore {
|
||||
buffer_snapshot: BufferSnapshot,
|
||||
edit_action: BreakpointEditAction,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
) -> Task<Result<()>> {
|
||||
let breakpoint_set = self.breakpoints.entry(project_path.clone()).or_default();
|
||||
|
||||
match edit_action {
|
||||
@@ -1077,7 +1090,6 @@ impl DapStore {
|
||||
cx.notify();
|
||||
|
||||
self.send_changed_breakpoints(project_path, buffer_path, buffer_snapshot, cx)
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn send_breakpoints(
|
||||
@@ -1085,6 +1097,7 @@ impl DapStore {
|
||||
client_id: &DebugAdapterClientId,
|
||||
absolute_file_path: Arc<Path>,
|
||||
mut breakpoints: Vec<SourceBreakpoint>,
|
||||
ignore: bool,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let Some(client) = self.client_by_id(client_id) else {
|
||||
@@ -1100,7 +1113,9 @@ impl DapStore {
|
||||
.request::<SetBreakpoints>(SetBreakpointsArguments {
|
||||
source: Source {
|
||||
path: Some(String::from(absolute_file_path.to_string_lossy())),
|
||||
name: None,
|
||||
name: absolute_file_path
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy().to_string()),
|
||||
source_reference: None,
|
||||
presentation_hint: None,
|
||||
origin: None,
|
||||
@@ -1108,8 +1123,8 @@ impl DapStore {
|
||||
adapter_data: None,
|
||||
checksums: None,
|
||||
},
|
||||
breakpoints: Some(breakpoints),
|
||||
source_modified: None,
|
||||
breakpoints: Some(if ignore { Vec::default() } else { breakpoints }),
|
||||
source_modified: Some(false),
|
||||
lines: None,
|
||||
})
|
||||
.await?;
|
||||
@@ -1124,15 +1139,15 @@ impl DapStore {
|
||||
buffer_path: PathBuf,
|
||||
buffer_snapshot: BufferSnapshot,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<()> {
|
||||
) -> Task<Result<()>> {
|
||||
let clients = self.running_clients().collect::<Vec<_>>();
|
||||
|
||||
if clients.is_empty() {
|
||||
return Task::ready(());
|
||||
return Task::ready(Ok(()));
|
||||
}
|
||||
|
||||
let Some(breakpoints) = self.breakpoints.get(project_path) else {
|
||||
return Task::ready(());
|
||||
return Task::ready(Ok(()));
|
||||
};
|
||||
|
||||
let source_breakpoints = breakpoints
|
||||
@@ -1146,12 +1161,15 @@ impl DapStore {
|
||||
&client.id(),
|
||||
Arc::from(buffer_path.clone()),
|
||||
source_breakpoints.clone(),
|
||||
self.ignore_breakpoints(&client.id()),
|
||||
cx,
|
||||
))
|
||||
}
|
||||
|
||||
cx.background_executor().spawn(async move {
|
||||
futures::future::join_all(tasks).await;
|
||||
futures::future::try_join_all(tasks).await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1243,7 +1243,13 @@ impl Project {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
tasks.push(self.dap_store.update(cx, |store, cx| {
|
||||
store.send_breakpoints(client_id, abs_path, source_breakpoints, cx)
|
||||
store.send_breakpoints(
|
||||
client_id,
|
||||
abs_path,
|
||||
source_breakpoints,
|
||||
store.ignore_breakpoints(client_id),
|
||||
cx,
|
||||
)
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1337,6 +1343,57 @@ impl Project {
|
||||
result
|
||||
}
|
||||
|
||||
pub fn toggle_ignore_breakpoints(
|
||||
&self,
|
||||
client_id: &DebugAdapterClientId,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let tasks = self.dap_store.update(cx, |store, cx| {
|
||||
store.toggle_ignore_breakpoints(client_id);
|
||||
|
||||
let mut tasks = Vec::new();
|
||||
|
||||
for (project_path, breakpoints) in store.breakpoints() {
|
||||
let Some((buffer, buffer_path)) = maybe!({
|
||||
let buffer = self
|
||||
.buffer_store
|
||||
.read_with(cx, |store, cx| store.get_by_path(project_path, cx))?;
|
||||
|
||||
let buffer = buffer.read(cx);
|
||||
let project_path = buffer.project_path(cx)?;
|
||||
let worktree = self.worktree_for_id(project_path.clone().worktree_id, cx)?;
|
||||
Some((
|
||||
buffer,
|
||||
worktree.read(cx).absolutize(&project_path.path).ok()?,
|
||||
))
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
tasks.push(
|
||||
store.send_breakpoints(
|
||||
client_id,
|
||||
Arc::from(buffer_path),
|
||||
breakpoints
|
||||
.into_iter()
|
||||
.map(|breakpoint| breakpoint.to_source_breakpoint(buffer))
|
||||
.collect::<Vec<_>>(),
|
||||
store.ignore_breakpoints(client_id),
|
||||
cx,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
tasks
|
||||
});
|
||||
|
||||
cx.background_executor().spawn(async move {
|
||||
try_join_all(tasks).await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Sends updated breakpoint information of one file to all active debug adapters
|
||||
///
|
||||
/// This function is called whenever a breakpoint is toggled, and it doesn't need
|
||||
@@ -1365,14 +1422,16 @@ impl Project {
|
||||
};
|
||||
|
||||
self.dap_store.update(cx, |store, cx| {
|
||||
store.toggle_breakpoint_for_buffer(
|
||||
&project_path,
|
||||
breakpoint,
|
||||
buffer_path,
|
||||
buffer.read(cx).snapshot(),
|
||||
edit_action,
|
||||
cx,
|
||||
);
|
||||
store
|
||||
.toggle_breakpoint_for_buffer(
|
||||
&project_path,
|
||||
breakpoint,
|
||||
buffer_path,
|
||||
buffer.read(cx).snapshot(),
|
||||
edit_action,
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -173,6 +173,7 @@ pub enum IconName {
|
||||
TextSnippet,
|
||||
Dash,
|
||||
DebugBreakpoint,
|
||||
DebugIgnoreBreakpoints,
|
||||
DebugPause,
|
||||
DebugContinue,
|
||||
DebugStepOver,
|
||||
|
||||
@@ -130,7 +130,18 @@ actions!(assistant, [ShowConfiguration]);
|
||||
|
||||
actions!(
|
||||
debugger,
|
||||
[Start, Continue, Disconnect, Pause, Restart, StepInto, StepOver, StepOut, Stop]
|
||||
[
|
||||
Start,
|
||||
Continue,
|
||||
Disconnect,
|
||||
Pause,
|
||||
Restart,
|
||||
StepInto,
|
||||
StepOver,
|
||||
StepOut,
|
||||
Stop,
|
||||
ToggleIgnoreBreakpoints
|
||||
]
|
||||
);
|
||||
|
||||
actions!(
|
||||
|
||||
Reference in New Issue
Block a user