Keep folds at cursor open for "fold at level" (#39396)
Andrew Farkas
and
Conrad Irwin
created 2 months ago
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>
Change summary
crates/editor/src/editor.rs | 22 ++++++++
crates/editor/src/editor_tests.rs | 57 ++++++++++++++++++++++++
crates/editor/src/selections_collection.rs | 3 +
3 files changed, 80 insertions(+), 2 deletions(-)
Detailed changes
@@ -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 {
@@ -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,