editor: Add toggle breadcrumb action (#57970)

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 is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #55576

This PR adds a new `editor: toggle breadcrumb` actions that toggles the
breadcrumbs for the active item without persisting on `settings.json`.
Similar to what `editor: toggle minimap` does.


https://github.com/user-attachments/assets/7179a0c8-6a5e-4716-86b8-abde8aa61f68

A simple test was added to check that using this action doesn't change
the settings, and that changing an unrelated setting keeps the toggled
action as the source of truth for the breadcrumb visibility.

Release Notes:

- Added `editor: toggle breadcrumb` action to toggle breadcrumb of
active item without persisting on `settings.json`
This commit is contained in:
Alvaro Parker authored and GitHub committed 2026-06-01 06:55:52 +00:00
1 parent 735980b158
commit 0bd41d67b9
6 files changed
+117 -7

No files matched your search

+2
View File
@@ -918,6 +918,8 @@ actions!(
AlignSelections,
/// Saves the current location to navigation history.
SaveLocation,
/// Toggles breadcrumbs display.
ToggleBreadcrumb,
]
);
+25
View File
@@ -138,6 +138,31 @@ impl Editor {
}
}
pub fn breadcrumbs_visible(&self) -> bool {
self.breadcrumbs_visibility.visible()
}
fn set_breadcrumbs_visibility(
&mut self,
breadcrumbs_visibility: BreadcrumbsVisibility,
cx: &mut Context<Self>,
) {
if self.breadcrumbs_visibility != breadcrumbs_visibility {
self.breadcrumbs_visibility = breadcrumbs_visibility;
cx.emit(EditorEvent::BreadcrumbsChanged);
cx.notify();
}
}
pub fn toggle_breadcrumb(
&mut self,
_: &ToggleBreadcrumb,
_: &mut Window,
cx: &mut Context<Self>,
) {
self.set_breadcrumbs_visibility(self.breadcrumbs_visibility.toggle_visibility(), cx);
}
pub fn disable_scrollbars_and_minimap(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.set_show_scrollbars(false, cx);
self.set_minimap_visibility(MinimapVisibility::Disabled, window, cx);
+44 -5
View File
@@ -705,6 +705,40 @@ impl MinimapVisibility {
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
struct BreadcrumbsVisibility {
setting_configuration: bool,
toggle_override: bool,
}
impl BreadcrumbsVisibility {
fn from_settings(cx: &App) -> Self {
Self::new(EditorSettings::get_global(cx).toolbar.breadcrumbs)
}
fn new(setting_configuration: bool) -> Self {
Self {
setting_configuration,
toggle_override: false,
}
}
fn settings_visibility(&self) -> bool {
self.setting_configuration
}
fn visible(&self) -> bool {
self.setting_configuration ^ self.toggle_override
}
fn toggle_visibility(&self) -> Self {
Self {
setting_configuration: self.setting_configuration,
toggle_override: !self.toggle_override,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BufferSerialization {
All,
@@ -931,7 +965,7 @@ pub struct Editor {
hovered_cursors: HashMap<HoveredCursor, Task<()>>,
pub show_local_selections: bool,
mode: EditorMode,
show_breadcrumbs: bool,
breadcrumbs_visibility: BreadcrumbsVisibility,
show_gutter: bool,
show_scrollbars: ScrollbarAxes,
minimap_visibility: MinimapVisibility,
@@ -2130,7 +2164,7 @@ impl Editor {
},
minimap_visibility: MinimapVisibility::for_mode(&mode, cx),
offset_content: !matches!(mode, EditorMode::SingleLine),
show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs,
breadcrumbs_visibility: BreadcrumbsVisibility::from_settings(cx),
show_gutter: full_mode,
show_line_numbers: (!full_mode).then_some(false),
use_relative_line_numbers: None,
@@ -9467,12 +9501,17 @@ impl Editor {
self.refresh_inline_values(cx);
let old_cursor_shape = self.cursor_shape;
let old_show_breadcrumbs = self.show_breadcrumbs;
let old_breadcrumbs_visible = self.breadcrumbs_visible();
{
let editor_settings = EditorSettings::get_global(cx);
self.scroll_manager.vertical_scroll_margin = editor_settings.vertical_scroll_margin;
self.show_breadcrumbs = editor_settings.toolbar.breadcrumbs;
if self.breadcrumbs_visibility.settings_visibility()
!= editor_settings.toolbar.breadcrumbs
{
self.breadcrumbs_visibility =
BreadcrumbsVisibility::new(editor_settings.toolbar.breadcrumbs);
}
self.cursor_shape = editor_settings.cursor_shape.unwrap_or_default();
}
@@ -9480,7 +9519,7 @@ impl Editor {
cx.emit(EditorEvent::CursorShapeChanged);
}
if old_show_breadcrumbs != self.show_breadcrumbs {
if old_breadcrumbs_visible != self.breadcrumbs_visible() {
cx.emit(EditorEvent::BreadcrumbsChanged);
}
+44 -1
View File
@@ -70,7 +70,7 @@ use util::{
};
use workspace::{
CloseActiveItem, CloseAllItems, CloseOtherItems, MultiWorkspace, NavigationEntry, OpenOptions,
ViewId,
ToolbarItemLocation, ViewId,
item::{FollowEvent, FollowableItem, Item, ItemHandle, SaveOptions},
register_project_item,
};
@@ -917,6 +917,49 @@ fn test_clone(cx: &mut TestAppContext) {
);
}
#[gpui::test]
fn test_toggle_breadcrumb_does_not_change_settings(cx: &mut TestAppContext) {
init_test(cx, |_| {});
update_test_editor_settings(cx, &|settings| {
settings.toolbar.get_or_insert_default().breadcrumbs = Some(true);
});
let editor = cx.add_window(|window, cx| {
let buffer = MultiBuffer::build_simple("hello", cx);
build_editor(buffer, window, cx)
});
_ = editor.update(cx, |editor, window, cx| {
assert!(EditorSettings::get_global(cx).toolbar.breadcrumbs);
assert_eq!(
editor.breadcrumb_location(cx),
ToolbarItemLocation::PrimaryLeft
);
editor.toggle_breadcrumb(&ToggleBreadcrumb, window, cx);
assert!(EditorSettings::get_global(cx).toolbar.breadcrumbs);
assert_eq!(editor.breadcrumb_location(cx), ToolbarItemLocation::Hidden);
});
// Changing unrelated settings should not affect breadcrumbs visibility.
update_test_editor_settings(cx, &|settings| {
settings.vertical_scroll_margin = Some(4.0);
});
cx.run_until_parked();
_ = editor.update(cx, |editor, window, cx| {
assert!(EditorSettings::get_global(cx).toolbar.breadcrumbs);
assert_eq!(editor.breadcrumb_location(cx), ToolbarItemLocation::Hidden);
editor.toggle_breadcrumb(&ToggleBreadcrumb, window, cx);
assert!(EditorSettings::get_global(cx).toolbar.breadcrumbs);
assert_eq!(
editor.breadcrumb_location(cx),
ToolbarItemLocation::PrimaryLeft
);
});
}
#[gpui::test]
async fn test_navigation_history(cx: &mut TestAppContext) {
init_test(cx, |_| {});
+1
View File
@@ -421,6 +421,7 @@ impl EditorElement {
register_action(editor, window, Editor::open_excerpts_in_split);
register_action(editor, window, Editor::toggle_soft_wrap);
register_action(editor, window, Editor::toggle_tab_bar);
register_action(editor, window, Editor::toggle_breadcrumb);
register_action(editor, window, Editor::toggle_line_numbers);
register_action(editor, window, Editor::toggle_relative_line_numbers);
register_action(editor, window, Editor::toggle_indent_guides);
+1 -1
View File
@@ -1029,7 +1029,7 @@ impl Item for Editor {
}
fn breadcrumb_location(&self, cx: &App) -> ToolbarItemLocation {
if self.show_breadcrumbs && self.buffer().read(cx).is_singleton() {
if self.breadcrumbs_visible() && self.buffer().read(cx).is_singleton() {
ToolbarItemLocation::PrimaryLeft
} else {
ToolbarItemLocation::Hidden