diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index e1ec4d5f81bdfc4cb631b1779621dc5b45d4f0e8..b3379981ca333bc4ab8b41b1643b305c3ed716f1 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3514,26 +3514,46 @@ impl Editor { ) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let tail = self.selections.newest::(cx).tail(); + let click_count = click_count.max(match self.selections.select_mode() { + SelectMode::Character => 1, + SelectMode::Word(_) => 2, + SelectMode::Line(_) => 3, + SelectMode::All => 4, + }); self.begin_selection(position, false, click_count, window, cx); - let position = position.to_offset(&display_map, Bias::Left); let tail_anchor = display_map.buffer_snapshot().anchor_before(tail); + let current_selection = match self.selections.select_mode() { + SelectMode::Character | SelectMode::All => tail_anchor..tail_anchor, + SelectMode::Word(range) | SelectMode::Line(range) => range.clone(), + }; + let mut pending_selection = self .selections .pending_anchor() .cloned() .expect("extend_selection not called with pending selection"); - if position >= tail { - pending_selection.start = tail_anchor; - } else { - pending_selection.end = tail_anchor; + + if pending_selection + .start + .cmp(¤t_selection.start, display_map.buffer_snapshot()) + == Ordering::Greater + { + pending_selection.start = current_selection.start; + } + if pending_selection + .end + .cmp(¤t_selection.end, display_map.buffer_snapshot()) + == Ordering::Less + { + pending_selection.end = current_selection.end; pending_selection.reversed = true; } let mut pending_mode = self.selections.pending_mode().unwrap(); match &mut pending_mode { - SelectMode::Word(range) | SelectMode::Line(range) => *range = tail_anchor..tail_anchor, + SelectMode::Word(range) | SelectMode::Line(range) => *range = current_selection, _ => {} } @@ -3544,7 +3564,8 @@ impl Editor { }; self.change_selections(effects, window, cx, |s| { - s.set_pending(pending_selection.clone(), pending_mode) + s.set_pending(pending_selection.clone(), pending_mode); + s.set_is_extending(true); }); } @@ -3813,11 +3834,16 @@ impl Editor { fn end_selection(&mut self, window: &mut Window, cx: &mut Context) { self.columnar_selection_state.take(); - if self.selections.pending_anchor().is_some() { + if let Some(pending_mode) = self.selections.pending_mode() { let selections = self.selections.all::(cx); self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.select(selections); s.clear_pending(); + if s.is_extending() { + s.set_is_extending(false); + } else { + s.set_select_mode(pending_mode); + } }); } } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 24fd007ac0ec068075210ce9aa59c8a760497736..f6a0406fecf957a836ed0cd701319f5ab7f50a23 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -619,6 +619,93 @@ fn test_movement_actions_with_pending_selection(cx: &mut TestAppContext) { }); } +#[gpui::test] +fn test_extending_selection(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + let editor = cx.add_window(|window, cx| { + let buffer = MultiBuffer::build_simple("aaa bbb ccc ddd eee", cx); + build_editor(buffer, window, cx) + }); + + _ = editor.update(cx, |editor, window, cx| { + editor.begin_selection(DisplayPoint::new(DisplayRow(0), 5), false, 1, window, cx); + editor.end_selection(window, cx); + assert_eq!( + editor.selections.display_ranges(cx), + [DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 5)] + ); + + editor.extend_selection(DisplayPoint::new(DisplayRow(0), 10), 1, window, cx); + editor.end_selection(window, cx); + assert_eq!( + editor.selections.display_ranges(cx), + [DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 10)] + ); + + editor.extend_selection(DisplayPoint::new(DisplayRow(0), 10), 1, window, cx); + editor.end_selection(window, cx); + editor.extend_selection(DisplayPoint::new(DisplayRow(0), 10), 2, window, cx); + assert_eq!( + editor.selections.display_ranges(cx), + [DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 11)] + ); + + editor.update_selection( + DisplayPoint::new(DisplayRow(0), 1), + 0, + gpui::Point::::default(), + window, + cx, + ); + editor.end_selection(window, cx); + assert_eq!( + editor.selections.display_ranges(cx), + [DisplayPoint::new(DisplayRow(0), 5)..DisplayPoint::new(DisplayRow(0), 0)] + ); + + editor.begin_selection(DisplayPoint::new(DisplayRow(0), 5), true, 1, window, cx); + editor.end_selection(window, cx); + editor.begin_selection(DisplayPoint::new(DisplayRow(0), 5), true, 2, window, cx); + editor.end_selection(window, cx); + assert_eq!( + editor.selections.display_ranges(cx), + [DisplayPoint::new(DisplayRow(0), 4)..DisplayPoint::new(DisplayRow(0), 7)] + ); + + editor.extend_selection(DisplayPoint::new(DisplayRow(0), 10), 1, window, cx); + assert_eq!( + editor.selections.display_ranges(cx), + [DisplayPoint::new(DisplayRow(0), 4)..DisplayPoint::new(DisplayRow(0), 11)] + ); + + editor.update_selection( + DisplayPoint::new(DisplayRow(0), 6), + 0, + gpui::Point::::default(), + window, + cx, + ); + assert_eq!( + editor.selections.display_ranges(cx), + [DisplayPoint::new(DisplayRow(0), 4)..DisplayPoint::new(DisplayRow(0), 7)] + ); + + editor.update_selection( + DisplayPoint::new(DisplayRow(0), 1), + 0, + gpui::Point::::default(), + window, + cx, + ); + editor.end_selection(window, cx); + assert_eq!( + editor.selections.display_ranges(cx), + [DisplayPoint::new(DisplayRow(0), 7)..DisplayPoint::new(DisplayRow(0), 0)] + ); + }); +} + #[gpui::test] fn test_clone(cx: &mut TestAppContext) { init_test(cx, |_| {}); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index df966d4b8611630d6415030ecb623298f56e421e..054240ab73ba5ac36499e1a1f2e66f8dae06503c 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -681,6 +681,7 @@ impl EditorElement { .drag_and_drop_selection .enabled && click_count == 1 + && !modifiers.shift { let newest_anchor = editor.selections.newest_anchor(); let snapshot = editor.snapshot(window, cx); diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 5ab6d25eb9abcfa0846a176f83e2f2620245bb47..acdece23d9996ae397a6934db43917bf12cecf37 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -35,6 +35,8 @@ pub struct SelectionsCollection { disjoint: Arc<[Selection]>, /// A pending selection, such as when the mouse is being dragged pending: Option, + select_mode: SelectMode, + is_extending: bool, } impl SelectionsCollection { @@ -55,6 +57,8 @@ impl SelectionsCollection { }, mode: SelectMode::Character, }), + select_mode: SelectMode::Character, + is_extending: false, } } @@ -456,6 +460,22 @@ impl SelectionsCollection { pub fn set_line_mode(&mut self, line_mode: bool) { self.line_mode = line_mode; } + + pub fn select_mode(&self) -> &SelectMode { + &self.select_mode + } + + pub fn set_select_mode(&mut self, select_mode: SelectMode) { + self.select_mode = select_mode; + } + + pub fn is_extending(&self) -> bool { + self.is_extending + } + + pub fn set_is_extending(&mut self, is_extending: bool) { + self.is_extending = is_extending; + } } pub struct MutableSelectionsCollection<'a> {