@@ -1493,8 +1493,26 @@ impl Editor {
cx: &mut ViewContext<Self>,
) {
self.start_transaction(cx);
- self.select_to_previous_word_boundary(&SelectToPreviousWordBoundary, cx);
- self.backspace(&Backspace, cx);
+ let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
+ let mut selections = self.selections(cx.as_ref()).to_vec();
+ {
+ let buffer = self.buffer.read(cx);
+ for selection in &mut selections {
+ let range = selection.point_range(buffer);
+ if range.start == range.end {
+ let head = selection.head().to_display_point(&display_map, Bias::Left);
+ let cursor = display_map.anchor_before(
+ movement::prev_word_boundary(&display_map, head).unwrap(),
+ Bias::Right,
+ );
+ selection.set_head(&buffer, cursor);
+ selection.goal = SelectionGoal::None;
+ }
+ }
+ }
+
+ self.update_selections(selections, true, cx);
+ self.insert(&Insert(String::new()), cx);
self.end_transaction(cx);
}
@@ -1545,8 +1563,26 @@ impl Editor {
cx: &mut ViewContext<Self>,
) {
self.start_transaction(cx);
- self.select_to_next_word_boundary(&SelectToNextWordBoundary, cx);
- self.delete(&Delete, cx);
+ let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
+ let mut selections = self.selections(cx.as_ref()).to_vec();
+ {
+ let buffer = self.buffer.read(cx);
+ for selection in &mut selections {
+ let range = selection.point_range(buffer);
+ if range.start == range.end {
+ let head = selection.head().to_display_point(&display_map, Bias::Left);
+ let cursor = display_map.anchor_before(
+ movement::next_word_boundary(&display_map, head).unwrap(),
+ Bias::Right,
+ );
+ selection.set_head(&buffer, cursor);
+ selection.goal = SelectionGoal::None;
+ }
+ }
+ }
+
+ self.update_selections(selections, true, cx);
+ self.insert(&Insert(String::new()), cx);
self.end_transaction(cx);
}
@@ -3226,24 +3262,13 @@ mod tests {
);
});
- view.update(cx, |view, cx| {
- view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
- assert_eq!(
- view.selection_ranges(cx),
- &[
- DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
- DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
- ]
- );
- });
-
view.update(cx, |view, cx| {
view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
assert_eq!(
view.selection_ranges(cx),
&[
DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
- DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24),
+ DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
]
);
});
@@ -3270,24 +3295,13 @@ mod tests {
);
});
- view.update(cx, |view, cx| {
- view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
- assert_eq!(
- view.selection_ranges(cx),
- &[
- DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4),
- DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
- ]
- );
- });
-
view.update(cx, |view, cx| {
view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
assert_eq!(
view.selection_ranges(cx),
&[
DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7),
- DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
+ DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
]
);
});
@@ -3298,7 +3312,7 @@ mod tests {
view.selection_ranges(cx),
&[
DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
- DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2),
+ DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
]
);
});
@@ -3310,7 +3324,7 @@ mod tests {
view.selection_ranges(cx),
&[
DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9),
- DisplayPoint::new(2, 3)..DisplayPoint::new(2, 2),
+ DisplayPoint::new(2, 4)..DisplayPoint::new(2, 3),
]
);
});
@@ -3321,7 +3335,7 @@ mod tests {
view.selection_ranges(cx),
&[
DisplayPoint::new(0, 10)..DisplayPoint::new(0, 7),
- DisplayPoint::new(2, 3)..DisplayPoint::new(2, 0),
+ DisplayPoint::new(2, 4)..DisplayPoint::new(2, 2),
]
);
});
@@ -3332,37 +3346,7 @@ mod tests {
view.selection_ranges(cx),
&[
DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9),
- DisplayPoint::new(2, 3)..DisplayPoint::new(2, 2),
- ]
- );
- });
-
- view.update(cx, |view, cx| {
- view.delete_to_next_word_boundary(&DeleteToNextWordBoundary, cx);
- assert_eq!(
- view.display_text(cx),
- "use std::s::{foo, bar}\n\n {az.qux()}"
- );
- assert_eq!(
- view.selection_ranges(cx),
- &[
- DisplayPoint::new(0, 10)..DisplayPoint::new(0, 10),
- DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
- ]
- );
- });
-
- view.update(cx, |view, cx| {
- view.delete_to_previous_word_boundary(&DeleteToPreviousWordBoundary, cx);
- assert_eq!(
- view.display_text(cx),
- "use std::::{foo, bar}\n\n az.qux()}"
- );
- assert_eq!(
- view.selection_ranges(cx),
- &[
- DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
- DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2),
+ DisplayPoint::new(2, 4)..DisplayPoint::new(2, 3),
]
);
});
@@ -3418,11 +3402,52 @@ mod tests {
view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
assert_eq!(
view.selection_ranges(cx),
- &[DisplayPoint::new(1, 15)..DisplayPoint::new(1, 15)]
+ &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
);
});
}
+ #[gpui::test]
+ fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
+ let buffer = cx.add_model(|cx| Buffer::new(0, "one two three four", cx));
+ let settings = settings::test(&cx).1;
+ let (_, view) = cx.add_window(Default::default(), |cx| {
+ build_editor(buffer.clone(), settings, cx)
+ });
+
+ view.update(cx, |view, cx| {
+ view.select_display_ranges(
+ &[
+ // an empty selection - the preceding word fragment is deleted
+ DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
+ // characters selected - they are deleted
+ DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
+ ],
+ cx,
+ )
+ .unwrap();
+ view.delete_to_previous_word_boundary(&DeleteToPreviousWordBoundary, cx);
+ });
+
+ assert_eq!(buffer.read(cx).text(), "e two te four");
+
+ view.update(cx, |view, cx| {
+ view.select_display_ranges(
+ &[
+ // an empty selection - the following word fragment is deleted
+ DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
+ // characters selected - they are deleted
+ DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
+ ],
+ cx,
+ )
+ .unwrap();
+ view.delete_to_next_word_boundary(&DeleteToNextWordBoundary, cx);
+ });
+
+ assert_eq!(buffer.read(cx).text(), "e t te our");
+ }
+
#[gpui::test]
fn test_backspace(cx: &mut gpui::MutableAppContext) {
let buffer = cx.add_model(|cx| {
@@ -101,7 +101,10 @@ pub fn line_end(map: &DisplayMapSnapshot, point: DisplayPoint) -> Result<Display
Ok(map.clip_point(line_end, Bias::Left))
}
-pub fn prev_word_boundary(map: &DisplayMapSnapshot, point: DisplayPoint) -> Result<DisplayPoint> {
+pub fn prev_word_boundary(
+ map: &DisplayMapSnapshot,
+ mut point: DisplayPoint,
+) -> Result<DisplayPoint> {
let mut line_start = 0;
if point.row() > 0 {
if let Some(indent) = map.soft_wrap_indent(point.row() - 1) {
@@ -111,39 +114,52 @@ pub fn prev_word_boundary(map: &DisplayMapSnapshot, point: DisplayPoint) -> Resu
if point.column() == line_start {
if point.row() == 0 {
- Ok(DisplayPoint::new(0, 0))
+ return Ok(DisplayPoint::new(0, 0));
} else {
let row = point.row() - 1;
- Ok(map.clip_point(DisplayPoint::new(row, map.line_len(row)), Bias::Left))
+ point = map.clip_point(DisplayPoint::new(row, map.line_len(row)), Bias::Left);
}
- } else {
- let mut boundary = DisplayPoint::new(point.row(), 0);
- let mut column = 0;
- let mut prev_c = None;
- for c in map.chars_at(DisplayPoint::new(point.row(), 0)) {
- if column >= point.column() {
- break;
- }
+ }
- if prev_c.is_none() || char_kind(prev_c.unwrap()) != char_kind(c) {
- *boundary.column_mut() = column;
- }
+ let mut boundary = DisplayPoint::new(point.row(), 0);
+ let mut column = 0;
+ let mut prev_char_kind = CharKind::Newline;
+ for c in map.chars_at(DisplayPoint::new(point.row(), 0)) {
+ if column >= point.column() {
+ break;
+ }
- prev_c = Some(c);
- column += c.len_utf8() as u32;
+ let char_kind = char_kind(c);
+ if char_kind != prev_char_kind
+ && char_kind != CharKind::Whitespace
+ && char_kind != CharKind::Newline
+ {
+ *boundary.column_mut() = column;
}
- Ok(boundary)
+
+ prev_char_kind = char_kind;
+ column += c.len_utf8() as u32;
}
+ Ok(boundary)
}
pub fn next_word_boundary(
map: &DisplayMapSnapshot,
mut point: DisplayPoint,
) -> Result<DisplayPoint> {
- let mut prev_c = None;
+ let mut prev_char_kind = None;
for c in map.chars_at(point) {
- if prev_c.is_some() && (c == '\n' || char_kind(prev_c.unwrap()) != char_kind(c)) {
- break;
+ let char_kind = char_kind(c);
+ if let Some(prev_char_kind) = prev_char_kind {
+ if c == '\n' {
+ break;
+ }
+ if prev_char_kind != char_kind
+ && prev_char_kind != CharKind::Whitespace
+ && prev_char_kind != CharKind::Newline
+ {
+ break;
+ }
}
if c == '\n' {
@@ -152,7 +168,7 @@ pub fn next_word_boundary(
} else {
*point.column_mut() += c.len_utf8() as u32;
}
- prev_c = Some(c);
+ prev_char_kind = Some(char_kind);
}
Ok(point)
}
@@ -192,7 +208,7 @@ mod tests {
.unwrap();
let font_size = 14.0;
- let buffer = cx.add_model(|cx| Buffer::new(0, "a bcΔ defγ", cx));
+ let buffer = cx.add_model(|cx| Buffer::new(0, "a bcΔ defγ hi—jk", cx));
let display_map =
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx));
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
@@ -202,7 +218,7 @@ mod tests {
);
assert_eq!(
prev_word_boundary(&snapshot, DisplayPoint::new(0, 7)).unwrap(),
- DisplayPoint::new(0, 6)
+ DisplayPoint::new(0, 2)
);
assert_eq!(
prev_word_boundary(&snapshot, DisplayPoint::new(0, 6)).unwrap(),
@@ -210,7 +226,7 @@ mod tests {
);
assert_eq!(
prev_word_boundary(&snapshot, DisplayPoint::new(0, 2)).unwrap(),
- DisplayPoint::new(0, 1)
+ DisplayPoint::new(0, 0)
);
assert_eq!(
prev_word_boundary(&snapshot, DisplayPoint::new(0, 1)).unwrap(),
@@ -223,7 +239,7 @@ mod tests {
);
assert_eq!(
next_word_boundary(&snapshot, DisplayPoint::new(0, 1)).unwrap(),
- DisplayPoint::new(0, 2)
+ DisplayPoint::new(0, 6)
);
assert_eq!(
next_word_boundary(&snapshot, DisplayPoint::new(0, 2)).unwrap(),
@@ -231,7 +247,7 @@ mod tests {
);
assert_eq!(
next_word_boundary(&snapshot, DisplayPoint::new(0, 6)).unwrap(),
- DisplayPoint::new(0, 7)
+ DisplayPoint::new(0, 12)
);
assert_eq!(
next_word_boundary(&snapshot, DisplayPoint::new(0, 7)).unwrap(),