diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 8927f9efdd..0c0bfb0d25 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1,6 +1,6 @@ use super::{ DisplayPoint, DisplayRow, Editor, EditorMode, EditorSettings, EditorStyle, Input, Scroll, - Select, SelectPhase, Snapshot, MAX_LINE_LEN, + Select, SelectMode, SelectPhase, Snapshot, MAX_LINE_LEN, }; use clock::ReplicaId; use gpui::{ @@ -56,6 +56,7 @@ impl EditorElement { &self, position: Vector2F, cmd: bool, + count: usize, layout: &mut LayoutState, paint: &mut PaintState, cx: &mut EventContext, @@ -63,7 +64,17 @@ impl EditorElement { if paint.text_bounds.contains_point(position) { let snapshot = self.snapshot(cx.app); let position = paint.point_for_position(&snapshot, layout, position); - cx.dispatch_action(Select(SelectPhase::Begin { position, add: cmd })); + let mode = match count { + 1 => SelectMode::Character, + 2 => SelectMode::Word, + 3 => SelectMode::Line, + _ => SelectMode::All, + }; + cx.dispatch_action(Select(SelectPhase::Begin { + position, + add: cmd, + mode, + })); true } else { false @@ -841,9 +852,11 @@ impl Element for EditorElement { ) -> bool { if let (Some(layout), Some(paint)) = (layout, paint) { match event { - Event::LeftMouseDown { position, cmd } => { - self.mouse_down(*position, *cmd, layout, paint, cx) - } + Event::LeftMouseDown { + position, + cmd, + count, + } => self.mouse_down(*position, *cmd, *count, layout, paint, cx), Event::LeftMouseUp { position } => self.mouse_up(*position, cx), Event::LeftMouseDragged { position } => { self.mouse_dragged(*position, layout, paint, cx) diff --git a/crates/editor/src/lib.rs b/crates/editor/src/lib.rs index b54ea6989d..55f46c5d4a 100644 --- a/crates/editor/src/lib.rs +++ b/crates/editor/src/lib.rs @@ -274,6 +274,7 @@ pub enum SelectPhase { Begin { position: DisplayPoint, add: bool, + mode: SelectMode, }, Update { position: DisplayPoint, @@ -282,6 +283,14 @@ pub enum SelectPhase { End, } +#[derive(Copy, Clone, Debug)] +pub enum SelectMode { + Character, + Word, + Line, + All, +} + #[derive(Copy, Clone, PartialEq, Eq)] pub enum EditorMode { SingleLine, @@ -300,7 +309,7 @@ pub struct Editor { buffer: ModelHandle, display_map: ModelHandle, selection_set_id: SelectionSetId, - pending_selection: Option>, + pending_selection: Option, next_selection_id: usize, add_selections_state: Option, autoclose_stack: Vec, @@ -327,6 +336,11 @@ pub struct Snapshot { scroll_top_anchor: Anchor, } +struct PendingSelection { + selection: Selection, + mode: SelectMode, +} + struct AddSelectionsState { above: bool, stack: Vec, @@ -627,7 +641,11 @@ impl Editor { fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext) { match phase { - SelectPhase::Begin { position, add } => self.begin_selection(*position, *add, cx), + SelectPhase::Begin { + position, + add, + mode, + } => self.begin_selection(*position, *add, *mode, cx), SelectPhase::Update { position, scroll_position, @@ -636,7 +654,13 @@ impl Editor { } } - fn begin_selection(&mut self, position: DisplayPoint, add: bool, cx: &mut ViewContext) { + fn begin_selection( + &mut self, + position: DisplayPoint, + add: bool, + mode: SelectMode, + cx: &mut ViewContext, + ) { if !self.focused { cx.focus_self(); cx.emit(Event::Activate); @@ -644,11 +668,28 @@ impl Editor { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = self.buffer.read(cx); - let cursor = buffer.anchor_before(position.to_point(&display_map)); + let start; + let end; + match mode { + SelectMode::Character => { + start = buffer.anchor_before(position.to_point(&display_map)); + end = start.clone(); + } + SelectMode::Word => { + let range = movement::surrounding_word(&display_map, position); + start = buffer.anchor_before(range.start.to_point(&display_map)); + end = buffer.anchor_before(range.end.to_point(&display_map)); + } + SelectMode::Line => todo!(), + SelectMode::All => { + start = buffer.anchor_before(0); + end = buffer.anchor_before(buffer.len()); + } + } let selection = Selection { id: post_inc(&mut self.next_selection_id), - start: cursor.clone(), - end: cursor, + start, + end, reversed: false, goal: SelectionGoal::None, }; @@ -656,7 +697,7 @@ impl Editor { if !add { self.update_selections::(Vec::new(), false, cx); } - self.pending_selection = Some(selection); + self.pending_selection = Some(PendingSelection { selection, mode }); cx.notify(); } @@ -668,21 +709,34 @@ impl Editor { cx: &mut ViewContext, ) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - if let Some(pending_selection) = self.pending_selection.as_mut() { + if let Some(PendingSelection { selection, mode }) = self.pending_selection.as_mut() { let buffer = self.buffer.read(cx); - let cursor = buffer.anchor_before(position.to_point(&display_map)); - if cursor.cmp(&pending_selection.tail(), buffer).unwrap() < Ordering::Equal { - if !pending_selection.reversed { - pending_selection.end = pending_selection.start.clone(); - pending_selection.reversed = true; + let cursor = match mode { + SelectMode::Character => buffer.anchor_before(position.to_point(&display_map)), + SelectMode::Word => { + let word_range = movement::surrounding_word(&display_map, position); + if word_range.start < selection.start.to_display_point(&display_map) { + buffer.anchor_before(word_range.start.to_point(&display_map)) + } else { + buffer.anchor_before(word_range.end.to_point(&display_map)) + } } - pending_selection.start = cursor; + SelectMode::Line => todo!(), + SelectMode::All => selection.head(), + }; + + if cursor.cmp(&selection.tail(), buffer).unwrap() < Ordering::Equal { + if !selection.reversed { + selection.end = selection.start.clone(); + selection.reversed = true; + } + selection.start = cursor; } else { - if pending_selection.reversed { - pending_selection.start = pending_selection.end.clone(); - pending_selection.reversed = false; + if selection.reversed { + selection.start = selection.end.clone(); + selection.reversed = false; } - pending_selection.end = cursor; + selection.end = cursor; } } else { log::error!("update_selection dispatched with no pending selection"); @@ -707,17 +761,17 @@ impl Editor { pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext) { if self.active_diagnostics.is_some() { self.dismiss_diagnostics(cx); - } else if let Some(pending_selection) = self.pending_selection.take() { + } else if let Some(PendingSelection { selection, .. }) = self.pending_selection.take() { let buffer = self.buffer.read(cx); - let pending_selection = Selection { - id: pending_selection.id, - start: pending_selection.start.to_point(buffer), - end: pending_selection.end.to_point(buffer), - reversed: pending_selection.reversed, - goal: pending_selection.goal, + let selection = Selection { + id: selection.id, + start: selection.start.to_point(buffer), + end: selection.end.to_point(buffer), + reversed: selection.reversed, + goal: selection.goal, }; if self.selections::(cx).next().is_none() { - self.update_selections(vec![pending_selection], true, cx); + self.update_selections(vec![selection], true, cx); } } else { let mut oldest_selection = self.oldest_selection::(cx); @@ -1735,8 +1789,7 @@ impl Editor { let mut selections = self.selections::(cx).collect::>(); for selection in &mut selections { let head = selection.head().to_display_point(&display_map); - let new_head = movement::prev_word_boundary(&display_map, head).unwrap(); - let cursor = new_head.to_point(&display_map); + let cursor = movement::prev_word_boundary(&display_map, head).to_point(&display_map); selection.start = cursor.clone(); selection.end = cursor; selection.reversed = false; @@ -1754,8 +1807,7 @@ impl Editor { let mut selections = self.selections::(cx).collect::>(); for selection in &mut selections { let head = selection.head().to_display_point(&display_map); - let new_head = movement::prev_word_boundary(&display_map, head).unwrap(); - let cursor = new_head.to_point(&display_map); + let cursor = movement::prev_word_boundary(&display_map, head).to_point(&display_map); selection.set_head(cursor); selection.goal = SelectionGoal::None; } @@ -1773,8 +1825,8 @@ impl Editor { for selection in &mut selections { if selection.is_empty() { let head = selection.head().to_display_point(&display_map); - let new_head = movement::prev_word_boundary(&display_map, head).unwrap(); - let cursor = new_head.to_point(&display_map); + let cursor = + movement::prev_word_boundary(&display_map, head).to_point(&display_map); selection.set_head(cursor); selection.goal = SelectionGoal::None; } @@ -1793,8 +1845,7 @@ impl Editor { let mut selections = self.selections::(cx).collect::>(); for selection in &mut selections { let head = selection.head().to_display_point(&display_map); - let new_head = movement::next_word_boundary(&display_map, head).unwrap(); - let cursor = new_head.to_point(&display_map); + let cursor = movement::next_word_boundary(&display_map, head).to_point(&display_map); selection.start = cursor; selection.end = cursor; selection.reversed = false; @@ -1812,8 +1863,7 @@ impl Editor { let mut selections = self.selections::(cx).collect::>(); for selection in &mut selections { let head = selection.head().to_display_point(&display_map); - let new_head = movement::next_word_boundary(&display_map, head).unwrap(); - let cursor = new_head.to_point(&display_map); + let cursor = movement::next_word_boundary(&display_map, head).to_point(&display_map); selection.set_head(cursor); selection.goal = SelectionGoal::None; } @@ -1831,8 +1881,8 @@ impl Editor { for selection in &mut selections { if selection.is_empty() { let head = selection.head().to_display_point(&display_map); - let new_head = movement::next_word_boundary(&display_map, head).unwrap(); - let cursor = new_head.to_point(&display_map); + let cursor = + movement::next_word_boundary(&display_map, head).to_point(&display_map); selection.set_head(cursor); selection.goal = SelectionGoal::None; } @@ -2463,9 +2513,9 @@ impl Editor { let start_index = self.selection_insertion_index(&selections, start); let pending_selection = if set_id == self.selection_set_id { self.pending_selection.as_ref().and_then(|pending| { - let mut selection_start = pending.start.to_display_point(&display_map); - let mut selection_end = pending.end.to_display_point(&display_map); - if pending.reversed { + let mut selection_start = pending.selection.start.to_display_point(&display_map); + let mut selection_end = pending.selection.end.to_display_point(&display_map); + if pending.selection.reversed { mem::swap(&mut selection_start, &mut selection_end); } if selection_start <= range.end || selection_end <= range.end { @@ -2535,12 +2585,12 @@ impl Editor { D: 'a + TextDimension<'a>, { let buffer = self.buffer.read(cx); - self.pending_selection.as_ref().map(|selection| Selection { - id: selection.id, - start: selection.start.summary::(buffer), - end: selection.end.summary::(buffer), - reversed: selection.reversed, - goal: selection.goal, + self.pending_selection.as_ref().map(|pending| Selection { + id: pending.selection.id, + start: pending.selection.start.summary::(buffer), + end: pending.selection.end.summary::(buffer), + reversed: pending.selection.reversed, + goal: pending.selection.goal, }) } @@ -3148,7 +3198,7 @@ mod tests { cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); editor.update(cx, |view, cx| { - view.begin_selection(DisplayPoint::new(2, 2), false, cx); + view.begin_selection(DisplayPoint::new(2, 2), false, SelectMode::Character, cx); }); assert_eq!( @@ -3185,7 +3235,7 @@ mod tests { ); editor.update(cx, |view, cx| { - view.begin_selection(DisplayPoint::new(3, 3), true, cx); + view.begin_selection(DisplayPoint::new(3, 3), true, SelectMode::Character, cx); view.update_selection(DisplayPoint::new(0, 0), Vector2F::zero(), cx); }); @@ -3214,7 +3264,7 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { - view.begin_selection(DisplayPoint::new(2, 2), false, cx); + view.begin_selection(DisplayPoint::new(2, 2), false, SelectMode::Character, cx); assert_eq!( view.selection_ranges(cx), [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)] @@ -3246,11 +3296,11 @@ mod tests { let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx)); view.update(cx, |view, cx| { - view.begin_selection(DisplayPoint::new(3, 4), false, cx); + view.begin_selection(DisplayPoint::new(3, 4), false, SelectMode::Character, cx); view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), cx); view.end_selection(cx); - view.begin_selection(DisplayPoint::new(0, 1), true, cx); + view.begin_selection(DisplayPoint::new(0, 1), true, SelectMode::Character, cx); view.update_selection(DisplayPoint::new(0, 3), Vector2F::zero(), cx); view.end_selection(cx); assert_eq!(