From 47df871eccb1a4d097d4ec138d459e1e48e5b202 Mon Sep 17 00:00:00 2001
From: Casey Avila <53847249+caseyavila@users.noreply.github.com>
Date: Fri, 28 Aug 2026 18:59:38 +0000
Subject: [PATCH] debugger: Fix breakpoint list not showing breakpoints in
single files (#63348)
# Objective
- Currently there is a bug where breakpoints in single files (not in
folder/worktree) are not shown in the debug panel breakpoint list. They
visually make the breakpoint list empty
- This is because we try to create a `RelPath` from `breakpoint.path`,
which is an absolute path
## Solution
- Create the fallback directory string in a valid way
- Because breakpoint directories can now be absolute, the first part of
the path isn't always the worktree root, so we can't blindly skip this
in the UI code anymore
- Instead, only prepend the worktree root if there multiple worktrees,
matching behavior of other UI elements. This simplifies the code overall
## Testing
- Manually tested
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
Before:
After:
---
Release Notes:
- Fixed breakpoint list not showing breakpoints in single files
---
.../src/session/running/breakpoint_list.rs | 47 ++++++++-----------
1 file changed, 19 insertions(+), 28 deletions(-)
diff --git a/crates/debugger_ui/src/session/running/breakpoint_list.rs b/crates/debugger_ui/src/session/running/breakpoint_list.rs
index 32e22215a2..95d11441f3 100644
--- a/crates/debugger_ui/src/session/running/breakpoint_list.rs
+++ b/crates/debugger_ui/src/session/running/breakpoint_list.rs
@@ -1,9 +1,4 @@
-use std::{
- ops::Range,
- path::{Path, PathBuf},
- sync::Arc,
- time::Duration,
-};
+use std::{ops::Range, path::Path, sync::Arc, time::Duration};
use dap::{Capabilities, ExceptionBreakpointsFilter, adapters::DebugAdapterName};
use db::kvp::KeyValueStore;
@@ -27,7 +22,7 @@ use ui::{
Divider, DividerColor, FluentBuilder as _, Indicator, IntoElement, ListItem, Render,
ScrollAxes, StatefulInteractiveElement, Tooltip, WithScrollbar, prelude::*,
};
-use util::rel_path::RelPath;
+use util::paths::PathExt;
use workspace::Workspace;
use zed_actions::{ToggleEnableBreakpoint, UnsetBreakpoint};
@@ -668,7 +663,7 @@ impl Render for BreakpointList {
fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl ui::IntoElement {
let breakpoints = self.breakpoint_store.read(cx).all_source_breakpoints(cx);
self.breakpoints.clear();
- let path_style = self.worktree_store.read(cx).path_style();
+ let multiple_worktrees = self.worktree_store.read(cx).visible_worktrees(cx).count() > 1;
let weak = cx.weak_entity();
let breakpoints = breakpoints.into_iter().flat_map(|(path, mut breakpoints)| {
let relative_worktree_path = self
@@ -676,23 +671,26 @@ impl Render for BreakpointList {
.read(cx)
.find_worktree(&path, cx)
.and_then(|(worktree, relative_path)| {
- worktree
- .read(cx)
- .is_visible()
- .then(|| worktree.read(cx).root_name().join(&relative_path))
+ worktree.read(cx).is_visible().then(|| {
+ if multiple_worktrees {
+ worktree.read(cx).root_name().join(&relative_path)
+ } else {
+ relative_path.to_rel_path_buf()
+ }
+ })
});
breakpoints.sort_by_key(|breakpoint| breakpoint.row);
let weak = weak.clone();
breakpoints.into_iter().filter_map(move |breakpoint| {
debug_assert_eq!(&path, &breakpoint.path);
let file_name = breakpoint.path.file_name()?;
- let breakpoint_path = RelPath::new(&breakpoint.path, path_style).ok();
let dir = relative_worktree_path
.as_deref()
- .or(breakpoint_path.as_deref())?
+ .map(|rel_path| rel_path.as_std_path())
+ .unwrap_or(&breakpoint.path.as_ref().compact())
.parent()
- .map(|parent| SharedString::from(parent.display(path_style).to_string()));
+ .map(|parent| SharedString::from(parent.display().to_string()));
let name = file_name
.to_str()
.map(ToOwned::to_owned)
@@ -928,19 +926,12 @@ impl LineBreakpoint {
.size(LabelSize::Small)
.line_height_style(ui::LineHeightStyle::UiLabel),
)
- .children(self.dir.as_ref().and_then(|dir| {
- let path_without_root = Path::new(dir.as_ref())
- .components()
- .skip(1)
- .collect::();
- path_without_root.components().next()?;
- Some(
- Label::new(path_without_root.to_string_lossy().into_owned())
- .color(Color::Muted)
- .size(LabelSize::Small)
- .line_height_style(ui::LineHeightStyle::UiLabel)
- .truncate(),
- )
+ .children(self.dir.as_ref().map(|dir| {
+ Label::new(dir)
+ .color(Color::Muted)
+ .size(LabelSize::Small)
+ .line_height_style(ui::LineHeightStyle::UiLabel)
+ .truncate()
}))
.when_some(self.dir.as_ref(), |this, parent_dir| {
this.tooltip(Tooltip::text(format!(