Keep folds at cursor open for "fold at level" (#39396)

Closes #39308

Also fixes a possible bug in `apply_selected_diff_hunks()` caused by
reversed selections.

Release Notes:

- Fixed "editor: fold at level" closing regions containing selections

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
Andrew Farkas
2025-10-03 15:06:46 +00:00
committed by GitHub
co-authored by Conrad Irwin
parent fd8bae9b72
commit 9aeb617a89
3 changed files with 80 additions and 2 deletions
+20 -2
View File
@@ -18126,6 +18126,13 @@ impl Editor {
let mut to_fold = Vec::new();
let mut stack = vec![(0, snapshot.max_row().0, 1)];
let row_ranges_to_keep: Vec<Range<u32>> = self
.selections
.all::<Point>(cx)
.into_iter()
.map(|sel| sel.start.row..sel.end.row)
.collect();
while let Some((mut start_row, end_row, current_level)) = stack.pop() {
while start_row < end_row {
match self
@@ -18139,7 +18146,13 @@ impl Editor {
if current_level < fold_at_level {
stack.push((nested_start_row, nested_end_row, current_level + 1));
} else if current_level == fold_at_level {
to_fold.push(crease);
// Fold iff there is no selection completely contained within the fold region
if !row_ranges_to_keep.iter().any(|selection| {
selection.end >= nested_start_row
&& selection.start <= nested_end_row
}) {
to_fold.push(crease);
}
}
start_row = nested_end_row + 1;
@@ -18846,7 +18859,12 @@ impl Editor {
) {
self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx);
let snapshot = self.snapshot(window, cx);
let hunks = snapshot.hunks_for_ranges(self.selections.ranges(cx));
let hunks = snapshot.hunks_for_ranges(
self.selections
.all(cx)
.into_iter()
.map(|selection| selection.range()),
);
let mut ranges_by_buffer = HashMap::default();
self.transact(window, cx, |editor, _window, cx| {
for hunk in hunks {
+57
View File
@@ -1256,6 +1256,63 @@ fn test_fold_at_level(cx: &mut TestAppContext) {
editor.display_text(cx),
editor.buffer.read(cx).read(cx).text()
);
let (_, positions) = marked_text_ranges(
&"
class Foo:
# Hello!
def a():
print(1)
def b():
p«riˇ»nt(2)
class Bar:
# World!
def a():
«ˇprint(1)
def b():
print(2)»
"
.unindent(),
true,
);
editor.change_selections(SelectionEffects::default(), window, cx, |s| {
s.select_ranges(positions)
});
editor.fold_at_level(&FoldAtLevel(2), window, cx);
assert_eq!(
editor.display_text(cx),
"
class Foo:
# Hello!
def a():
def b():
print(2)
class Bar:
# World!
def a():
print(1)
def b():
print(2)
"
.unindent(),
);
});
}
@@ -332,6 +332,9 @@ impl SelectionsCollection {
self.all(cx).last().unwrap().clone()
}
/// Returns a list of (potentially backwards!) ranges representing the selections.
/// Useful for test assertions, but prefer `.all()` instead.
#[cfg(any(test, feature = "test-support"))]
pub fn ranges<D: TextDimension + Ord + Sub<D, Output = D>>(
&self,
cx: &mut App,