From aa12eb6b5567031f5ac2b19ab15ea5daf2607a45 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 4 Feb 2022 09:21:13 -0700 Subject: [PATCH 1/3] Span across soft-wraps when triple clicking a line Co-Authored-By: Antonio Scandurra --- crates/editor/src/editor.rs | 52 +++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index da6ea878acb61b8411d6ef8fffc6c52f9d5fca7e..8916b8ab52f42d7d2b2c625ef855ab0f8f4f572f 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -968,15 +968,16 @@ impl Editor { mode = SelectMode::Word(start.clone()..end.clone()); } 3 => { - let position = display_map.clip_point(position, Bias::Left); - let line_start = movement::line_beginning(&display_map, position, false); - let mut next_line_start = line_start.clone(); - *next_line_start.row_mut() += 1; - *next_line_start.column_mut() = 0; - next_line_start = display_map.clip_point(next_line_start, Bias::Right); - - start = buffer.anchor_before(line_start.to_point(&display_map)); - end = buffer.anchor_before(next_line_start.to_point(&display_map)); + let position = display_map + .clip_point(position, Bias::Left) + .to_point(&display_map); + let line_start = display_map.prev_line_boundary(position).0; + let next_line_start = buffer.clip_point( + display_map.next_line_boundary(position).0 + Point::new(1, 0), + Bias::Left, + ); + start = buffer.anchor_before(line_start); + end = buffer.anchor_before(next_line_start); mode = SelectMode::Line(start.clone()..end.clone()); } _ => { @@ -1082,26 +1083,27 @@ impl Editor { } } SelectMode::Line(original_range) => { - let original_display_range = original_range.start.to_display_point(&display_map) - ..original_range.end.to_display_point(&display_map); - let original_buffer_range = original_display_range.start.to_point(&display_map) - ..original_display_range.end.to_point(&display_map); - let line_start = movement::line_beginning(&display_map, position, false); - let mut next_line_start = line_start.clone(); - *next_line_start.row_mut() += 1; - *next_line_start.column_mut() = 0; - next_line_start = display_map.clip_point(next_line_start, Bias::Right); - - if line_start < original_display_range.start { - head = line_start.to_point(&display_map); + let original_range = original_range.to_point(&display_map.buffer_snapshot); + + let position = display_map + .clip_point(position, Bias::Left) + .to_point(&display_map); + let line_start = display_map.prev_line_boundary(position).0; + let next_line_start = buffer.clip_point( + display_map.next_line_boundary(position).0 + Point::new(1, 0), + Bias::Left, + ); + + if line_start < original_range.start { + head = line_start } else { - head = next_line_start.to_point(&display_map); + head = next_line_start } - if head <= original_buffer_range.start { - tail = original_buffer_range.end; + if head <= original_range.start { + tail = original_range.end; } else { - tail = original_buffer_range.start; + tail = original_range.start; } } SelectMode::All => { From 90a780a604d7a5b0ce177eed9635b230d3aa6bf1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 4 Feb 2022 09:52:50 -0700 Subject: [PATCH 2/3] Improve moving to start or end of soft-wrapped lines When moving to end, stop at the soft-wrap line boundary first, then move to the end of the hard line. Vice versa when moving to the beginning. Co-Authored-By: Antonio Scandurra --- crates/editor/src/movement.rs | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 7eee1e627cb4567e8d0e22b9bca7d294148695f1..b67f147ca69075004ab4b57ad487b0387706d518 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -1,6 +1,7 @@ use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint}; use crate::{char_kind, CharKind, ToPoint}; use anyhow::Result; +use language::Point; use std::ops::Range; pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> Result { @@ -93,20 +94,37 @@ pub fn down( pub fn line_beginning( map: &DisplaySnapshot, - point: DisplayPoint, + display_point: DisplayPoint, toggle_indent: bool, ) -> DisplayPoint { - let (indent, is_blank) = map.line_indent(point.row()); - if toggle_indent && !is_blank && point.column() != indent { - DisplayPoint::new(point.row(), indent) + let point = display_point.to_point(map); + let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right); + let indent_start = Point::new( + point.row, + map.buffer_snapshot.indent_column_for_line(point.row), + ) + .to_display_point(map); + let line_start = map.prev_line_boundary(point).1; + + if display_point != soft_line_start { + soft_line_start + } else if toggle_indent && display_point != indent_start { + indent_start } else { - DisplayPoint::new(point.row(), 0) + line_start } } -pub fn line_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { - let line_end = DisplayPoint::new(point.row(), map.line_len(point.row())); - map.clip_point(line_end, Bias::Left) +pub fn line_end(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint { + let soft_line_end = map.clip_point( + DisplayPoint::new(display_point.row(), map.line_len(display_point.row())), + Bias::Left, + ); + if display_point == soft_line_end { + map.next_line_boundary(display_point.to_point(map)).1 + } else { + soft_line_end + } } pub fn prev_word_boundary(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint { From c38fe473cd4410552a05083e6894311ef4150624 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Fri, 4 Feb 2022 10:10:30 -0700 Subject: [PATCH 3/3] Delete to hard line boundary when deleting to start/end of line Co-Authored-By: Antonio Scandurra --- crates/editor/src/editor.rs | 26 +++++++++++++++----------- crates/editor/src/movement.rs | 19 ++++++++++++------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8916b8ab52f42d7d2b2c625ef855ab0f8f4f572f..bd8ab3d9f87e052b3b2fc488d7c44dc625ff50a1 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -101,7 +101,7 @@ action!(SelectRight); action!(SelectToPreviousWordBoundary); action!(SelectToNextWordBoundary); action!(SelectToBeginningOfLine, bool); -action!(SelectToEndOfLine); +action!(SelectToEndOfLine, bool); action!(SelectToBeginning); action!(SelectToEnd); action!(SelectAll); @@ -212,8 +212,8 @@ pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec, ) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let mut selections = self.local_selections::(cx); for selection in &mut selections { let head = selection.head().to_display_point(&display_map); - let new_head = movement::line_beginning(&display_map, head, *toggle_indent); + let new_head = movement::line_beginning(&display_map, head, *stop_at_soft_boundaries); selection.set_head(new_head.to_point(&display_map)); selection.goal = SelectionGoal::None; } @@ -2956,7 +2956,7 @@ impl Editor { { for selection in &mut selections { let head = selection.head().to_display_point(&display_map); - let new_head = movement::line_end(&display_map, head); + let new_head = movement::line_end(&display_map, head, true); let anchor = new_head.to_point(&display_map); selection.start = anchor.clone(); selection.end = anchor; @@ -2967,12 +2967,16 @@ impl Editor { self.update_selections(selections, Some(Autoscroll::Fit), cx); } - pub fn select_to_end_of_line(&mut self, _: &SelectToEndOfLine, cx: &mut ViewContext) { + pub fn select_to_end_of_line( + &mut self, + SelectToEndOfLine(stop_at_soft_boundaries): &SelectToEndOfLine, + cx: &mut ViewContext, + ) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let mut selections = self.local_selections::(cx); for selection in &mut selections { let head = selection.head().to_display_point(&display_map); - let new_head = movement::line_end(&display_map, head); + let new_head = movement::line_end(&display_map, head, *stop_at_soft_boundaries); selection.set_head(new_head.to_point(&display_map)); selection.goal = SelectionGoal::None; } @@ -2981,14 +2985,14 @@ impl Editor { pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext) { self.start_transaction(cx); - self.select_to_end_of_line(&SelectToEndOfLine, cx); + self.select_to_end_of_line(&SelectToEndOfLine(false), cx); self.delete(&Delete, cx); self.end_transaction(cx); } pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext) { self.start_transaction(cx); - self.select_to_end_of_line(&SelectToEndOfLine, cx); + self.select_to_end_of_line(&SelectToEndOfLine(false), cx); self.cut(&Cut, cx); self.end_transaction(cx); } @@ -5673,7 +5677,7 @@ mod tests { }); view.update(cx, |view, cx| { - view.select_to_end_of_line(&SelectToEndOfLine, cx); + view.select_to_end_of_line(&SelectToEndOfLine(true), cx); assert_eq!( view.selected_display_ranges(cx), &[ diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index b67f147ca69075004ab4b57ad487b0387706d518..287ad34dc87e6687e7041f027b93ed205621ef99 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -95,7 +95,7 @@ pub fn down( pub fn line_beginning( map: &DisplaySnapshot, display_point: DisplayPoint, - toggle_indent: bool, + stop_at_soft_boundaries: bool, ) -> DisplayPoint { let point = display_point.to_point(map); let soft_line_start = map.clip_point(DisplayPoint::new(display_point.row(), 0), Bias::Right); @@ -106,24 +106,29 @@ pub fn line_beginning( .to_display_point(map); let line_start = map.prev_line_boundary(point).1; - if display_point != soft_line_start { + if stop_at_soft_boundaries && soft_line_start > indent_start && display_point != soft_line_start + { soft_line_start - } else if toggle_indent && display_point != indent_start { + } else if stop_at_soft_boundaries && display_point != indent_start { indent_start } else { line_start } } -pub fn line_end(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint { +pub fn line_end( + map: &DisplaySnapshot, + display_point: DisplayPoint, + stop_at_soft_boundaries: bool, +) -> DisplayPoint { let soft_line_end = map.clip_point( DisplayPoint::new(display_point.row(), map.line_len(display_point.row())), Bias::Left, ); - if display_point == soft_line_end { - map.next_line_boundary(display_point.to_point(map)).1 - } else { + if stop_at_soft_boundaries && display_point != soft_line_end { soft_line_end + } else { + map.next_line_boundary(display_point.to_point(map)).1 } }