Detailed changes
@@ -2638,11 +2638,25 @@ impl Editor {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
for selection in &mut selections {
if selection.is_empty() {
- let head = selection.head().to_display_point(&display_map);
- let cursor = movement::left(&display_map, head)
- .unwrap()
- .to_point(&display_map);
- selection.set_head(cursor);
+ let old_head = selection.head();
+ let (buffer, line_buffer_range) = display_map
+ .buffer_snapshot
+ .buffer_line_for_row(old_head.row)
+ .unwrap();
+ let indent_column = buffer.indent_column_for_line(line_buffer_range.start.row);
+ let mut new_head =
+ movement::left(&display_map, old_head.to_display_point(&display_map))
+ .unwrap()
+ .to_point(&display_map);
+ if old_head.column <= indent_column && old_head.column > 0 {
+ let indent = buffer.indent_size();
+ new_head = cmp::min(
+ new_head,
+ Point::new(old_head.row, ((old_head.column - 1) / indent) * indent),
+ );
+ }
+
+ selection.set_head(new_head);
selection.goal = SelectionGoal::None;
}
}
@@ -7153,14 +7167,13 @@ mod tests {
#[gpui::test]
fn test_backspace(cx: &mut gpui::MutableAppContext) {
- let buffer =
- MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
let settings = Settings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| {
- build_editor(buffer.clone(), settings, cx)
+ build_editor(MultiBuffer::build_simple("", cx), settings, cx)
});
view.update(cx, |view, cx| {
+ view.set_text("one two three\nfour five six\nseven eight nine\nten\n", cx);
view.select_display_ranges(
&[
// an empty selection - the preceding character is deleted
@@ -7173,12 +7186,28 @@ mod tests {
cx,
);
view.backspace(&Backspace, cx);
- });
+ assert_eq!(view.text(cx), "oe two three\nfou five six\nseven ten\n");
- assert_eq!(
- buffer.read(cx).read(cx).text(),
- "oe two three\nfou five six\nseven ten\n"
- );
+ view.set_text(" one\n two\n three\n four", cx);
+ view.select_display_ranges(
+ &[
+ // cursors at the the end of leading indent - last indent is deleted
+ DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4),
+ DisplayPoint::new(1, 8)..DisplayPoint::new(1, 8),
+ // cursors inside leading indent - overlapping indent deletions are coalesced
+ DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
+ DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
+ DisplayPoint::new(2, 6)..DisplayPoint::new(2, 6),
+ // cursor at the beginning of a line - preceding newline is deleted
+ DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
+ // selection inside leading indent - only the selected character is deleted
+ DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3),
+ ],
+ cx,
+ );
+ view.backspace(&Backspace, cx);
+ assert_eq!(view.text(cx), "one\n two\n three four");
+ });
}
#[gpui::test]
@@ -1657,7 +1657,7 @@ impl MultiBufferSnapshot {
}
}
- fn buffer_line_for_row(&self, row: u32) -> Option<(&BufferSnapshot, Range<Point>)> {
+ pub fn buffer_line_for_row(&self, row: u32) -> Option<(&BufferSnapshot, Range<Point>)> {
let mut cursor = self.excerpts.cursor::<Point>();
cursor.seek(&Point::new(row, 0), Bias::Right, &());
if let Some(excerpt) = cursor.item() {
@@ -47,9 +47,6 @@ lazy_static! {
static ref QUERY_CURSORS: Mutex<Vec<QueryCursor>> = Default::default();
}
-// TODO - Make this configurable
-const INDENT_SIZE: u32 = 4;
-
pub struct Buffer {
text: TextBuffer,
file: Option<Box<dyn File>>,
@@ -70,6 +67,7 @@ pub struct Buffer {
file_update_count: usize,
completion_triggers: Vec<String>,
deferred_ops: OperationQueue<Operation>,
+ indent_size: u32,
}
pub struct BufferSnapshot {
@@ -81,9 +79,9 @@ pub struct BufferSnapshot {
file_update_count: usize,
remote_selections: TreeMap<ReplicaId, SelectionSet>,
selections_update_count: usize,
- is_parsing: bool,
language: Option<Arc<Language>>,
parse_count: usize,
+ indent_size: u32,
}
#[derive(Clone, Debug)]
@@ -416,6 +414,8 @@ impl Buffer {
file_update_count: 0,
completion_triggers: Default::default(),
deferred_ops: OperationQueue::new(),
+ // TODO: make this configurable
+ indent_size: 4,
}
}
@@ -428,10 +428,10 @@ impl Buffer {
diagnostics: self.diagnostics.clone(),
diagnostics_update_count: self.diagnostics_update_count,
file_update_count: self.file_update_count,
- is_parsing: self.parsing_in_background,
language: self.language.clone(),
parse_count: self.parse_count,
selections_update_count: self.selections_update_count,
+ indent_size: self.indent_size,
}
}
@@ -768,7 +768,11 @@ impl Buffer {
.before_edit
.indent_column_for_line(suggestion.basis_row)
});
- let delta = if suggestion.indent { INDENT_SIZE } else { 0 };
+ let delta = if suggestion.indent {
+ snapshot.indent_size
+ } else {
+ 0
+ };
old_suggestions.insert(
*old_to_new_rows.get(&old_row).unwrap(),
indentation_basis + delta,
@@ -787,7 +791,11 @@ impl Buffer {
.into_iter()
.flatten();
for (new_row, suggestion) in new_edited_row_range.zip(suggestions) {
- let delta = if suggestion.indent { INDENT_SIZE } else { 0 };
+ let delta = if suggestion.indent {
+ snapshot.indent_size
+ } else {
+ 0
+ };
let new_indentation = indent_columns
.get(&suggestion.basis_row)
.copied()
@@ -819,7 +827,11 @@ impl Buffer {
.into_iter()
.flatten();
for (row, suggestion) in inserted_row_range.zip(suggestions) {
- let delta = if suggestion.indent { INDENT_SIZE } else { 0 };
+ let delta = if suggestion.indent {
+ snapshot.indent_size
+ } else {
+ 0
+ };
let new_indentation = indent_columns
.get(&suggestion.basis_row)
.copied()
@@ -1868,6 +1880,10 @@ impl BufferSnapshot {
pub fn file_update_count(&self) -> usize {
self.file_update_count
}
+
+ pub fn indent_size(&self) -> u32 {
+ self.indent_size
+ }
}
impl Clone for BufferSnapshot {
@@ -1881,9 +1897,9 @@ impl Clone for BufferSnapshot {
selections_update_count: self.selections_update_count,
diagnostics_update_count: self.diagnostics_update_count,
file_update_count: self.file_update_count,
- is_parsing: self.is_parsing,
language: self.language.clone(),
parse_count: self.parse_count,
+ indent_size: self.indent_size,
}
}
}