Add editor::ToggleFoldAll action (#34317)

In multibuffers adds the ability to alt-click to fold/unfold all
excepts. In singleton buffers it adds the ability to toggle back and
forth between `editor::FoldAll` and `editor::UnfoldAll`.

Bind it in your keymap with:

```json
  {
    "context": "Editor && (mode == full || multibuffer)",
    "bindings": {
      "cmd-k cmd-o": "editor::ToggleFoldAll"
    }
  },
```

<img width="253" height="99" alt="Screenshot 2025-07-11 at 17 04 25"
src="https://github.com/user-attachments/assets/94de8275-d2ee-4cf8-a46c-a698ccdb60e3"
/>

Release Notes:

- Add ability to fold all excerpts in a multibuffer (alt-click) and in
singleton buffers `editor::ToggleFoldAll`
This commit is contained in:
Peter Tripp 2025-07-14 09:23:51 -04:00 committed by GitHub
parent c6a6db9754
commit a2f5c47e2d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 65 additions and 9 deletions

View file

@ -410,6 +410,8 @@ actions!(
ToggleFold,
/// Toggles recursive folding at the current position.
ToggleFoldRecursive,
/// Toggles all folds in a buffer or all excerpts in multibuffer.
ToggleFoldAll,
/// Formats the entire document.
Format,
/// Formats only the selected text.

View file

@ -17075,6 +17075,46 @@ impl Editor {
}
}
pub fn toggle_fold_all(
&mut self,
_: &actions::ToggleFoldAll,
window: &mut Window,
cx: &mut Context<Self>,
) {
if self.buffer.read(cx).is_singleton() {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let has_folds = display_map
.folds_in_range(0..display_map.buffer_snapshot.len())
.next()
.is_some();
if has_folds {
self.unfold_all(&actions::UnfoldAll, window, cx);
} else {
self.fold_all(&actions::FoldAll, window, cx);
}
} else {
let buffer_ids = self.buffer.read(cx).excerpt_buffer_ids();
let should_unfold = buffer_ids
.iter()
.any(|buffer_id| self.is_buffer_folded(*buffer_id, cx));
self.toggle_fold_multiple_buffers = cx.spawn_in(window, async move |editor, cx| {
editor
.update_in(cx, |editor, _, cx| {
for buffer_id in buffer_ids {
if should_unfold {
editor.unfold_buffer(buffer_id, cx);
} else {
editor.fold_buffer(buffer_id, cx);
}
}
})
.ok();
});
}
}
fn fold_at_level(
&mut self,
fold_at: &FoldAtLevel,

View file

@ -9,7 +9,7 @@ use crate::{
LineUp, MAX_LINE_LEN, MINIMAP_FONT_SIZE, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, OpenExcerpts,
PageDown, PageUp, PhantomBreakpointIndicator, Point, RowExt, RowRangeExt, SelectPhase,
SelectedTextHighlight, Selection, SelectionDragState, SoftWrap, StickyHeaderExcerpt, ToPoint,
ToggleFold,
ToggleFold, ToggleFoldAll,
code_context_menus::{CodeActionsMenu, MENU_ASIDE_MAX_WIDTH, MENU_ASIDE_MIN_WIDTH, MENU_GAP},
display_map::{
Block, BlockContext, BlockStyle, ChunkRendererId, DisplaySnapshot, EditorMargins,
@ -416,6 +416,7 @@ impl EditorElement {
register_action(editor, window, Editor::fold_recursive);
register_action(editor, window, Editor::toggle_fold);
register_action(editor, window, Editor::toggle_fold_recursive);
register_action(editor, window, Editor::toggle_fold_all);
register_action(editor, window, Editor::unfold_lines);
register_action(editor, window, Editor::unfold_recursive);
register_action(editor, window, Editor::unfold_all);
@ -3620,24 +3621,37 @@ impl EditorElement {
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
Tooltip::with_meta_in(
"Toggle Excerpt Fold",
&ToggleFold,
Some(&ToggleFold),
"Alt+click to toggle all",
&focus_handle,
window,
cx,
)
}
})
.on_click(move |_, _, cx| {
if is_folded {
.on_click(move |event, window, cx| {
if event.modifiers().alt {
// Alt+click toggles all buffers
editor.update(cx, |editor, cx| {
editor.unfold_buffer(buffer_id, cx);
editor.toggle_fold_all(
&ToggleFoldAll,
window,
cx,
);
});
} else {
editor.update(cx, |editor, cx| {
editor.fold_buffer(buffer_id, cx);
});
// Regular click toggles single buffer
if is_folded {
editor.update(cx, |editor, cx| {
editor.unfold_buffer(buffer_id, cx);
});
} else {
editor.update(cx, |editor, cx| {
editor.fold_buffer(buffer_id, cx);
});
}
}
}),
),