@@ -3514,26 +3514,46 @@ impl Editor {
) {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let tail = self.selections.newest::<usize>(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>) {
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::<usize>(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);
+ }
});
}
}
@@ -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::<f32>::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::<f32>::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::<f32>::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, |_| {});
@@ -35,6 +35,8 @@ pub struct SelectionsCollection {
disjoint: Arc<[Selection<Anchor>]>,
/// A pending selection, such as when the mouse is being dragged
pending: Option<PendingSelection>,
+ 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> {