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: image After: image --- 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!(