diff --git a/crates/activity_indicator/src/activity_indicator.rs b/crates/activity_indicator/src/activity_indicator.rs index 3730b5d21a..02ad100df8 100644 --- a/crates/activity_indicator/src/activity_indicator.rs +++ b/crates/activity_indicator/src/activity_indicator.rs @@ -82,6 +82,7 @@ impl ActivityIndicator { buffer.update(cx, |buffer, cx| { buffer.edit( [(0..0, format!("Language server error: {}\n\n", lsp_name))], + None, cx, ); }); diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 9471967eee..1f3ccef0be 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -842,8 +842,8 @@ async fn test_propagate_saves_and_fs_changes( .update(cx_c, |p, cx| p.open_buffer((worktree_id, "file1"), cx)) .await .unwrap(); - buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], cx)); - buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], cx)); + buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "i-am-b, ")], None, cx)); + buffer_c.update(cx_c, |buf, cx| buf.edit([(0..0, "i-am-c, ")], None, cx)); // Open and edit that buffer as the host. let buffer_a = project_a @@ -855,7 +855,7 @@ async fn test_propagate_saves_and_fs_changes( .condition(cx_a, |buf, _| buf.text() == "i-am-c, i-am-b, ") .await; buffer_a.update(cx_a, |buf, cx| { - buf.edit([(buf.len()..buf.len(), "i-am-a")], cx) + buf.edit([(buf.len()..buf.len(), "i-am-a")], None, cx) }); // Wait for edits to propagate @@ -871,7 +871,7 @@ async fn test_propagate_saves_and_fs_changes( // Edit the buffer as the host and concurrently save as guest B. let save_b = buffer_b.update(cx_b, |buf, cx| buf.save(cx)); - buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], cx)); + buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "hi-a, ")], None, cx)); save_b.await.unwrap(); assert_eq!( client_a.fs.load("/a/file1".as_ref()).await.unwrap(), @@ -1237,7 +1237,7 @@ async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut T .await .unwrap(); - buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], cx)); + buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "world ")], None, cx)); buffer_b.read_with(cx_b, |buf, _| { assert!(buf.is_dirty()); assert!(!buf.has_conflict()); @@ -1251,7 +1251,7 @@ async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut T assert!(!buf.has_conflict()); }); - buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], cx)); + buffer_b.update(cx_b, |buf, cx| buf.edit([(0..0, "hello ")], None, cx)); buffer_b.read_with(cx_b, |buf, _| { assert!(buf.is_dirty()); assert!(!buf.has_conflict()); @@ -1342,9 +1342,9 @@ async fn test_editing_while_guest_opens_buffer( // Edit the buffer as client A while client B is still opening it. cx_b.background().simulate_random_delay().await; - buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], cx)); + buffer_a.update(cx_a, |buf, cx| buf.edit([(0..0, "X")], None, cx)); cx_b.background().simulate_random_delay().await; - buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], cx)); + buffer_a.update(cx_a, |buf, cx| buf.edit([(1..1, "Y")], None, cx)); let text = buffer_a.read_with(cx_a, |buf, _| buf.text()); let buffer_b = buffer_b.await.unwrap(); @@ -1882,8 +1882,8 @@ async fn test_reloading_buffer_manually(cx_a: &mut TestAppContext, cx_b: &mut Te .await .unwrap(); buffer_b.update(cx_b, |buffer, cx| { - buffer.edit([(4..7, "six")], cx); - buffer.edit([(10..11, "6")], cx); + buffer.edit([(4..7, "six")], None, cx); + buffer.edit([(10..11, "6")], None, cx); assert_eq!(buffer.text(), "let six = 6;"); assert!(buffer.is_dirty()); assert!(!buffer.has_conflict()); @@ -2964,7 +2964,7 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T ); rename.editor.update(cx, |rename_editor, cx| { rename_editor.buffer().update(cx, |rename_buffer, cx| { - rename_buffer.edit([(0..3, "THREE")], cx); + rename_buffer.edit([(0..3, "THREE")], None, cx); }); }); }); diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 6f50da6a7d..9b12df60d9 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -897,7 +897,7 @@ pub mod tests { let ix = snapshot.buffer_snapshot.text().find("seven").unwrap(); buffer.update(cx, |buffer, cx| { - buffer.edit([(ix..ix, "and ")], cx); + buffer.edit([(ix..ix, "and ")], None, cx); }); let snapshot = map.update(cx, |map, cx| map.snapshot(cx)); @@ -936,6 +936,7 @@ pub mod tests { (Point::new(1, 1)..Point::new(1, 1), "\t"), (Point::new(2, 1)..Point::new(2, 1), "\t"), ], + None, cx, ) }); diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index ed0df25d69..5237901170 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -1164,7 +1164,7 @@ mod tests { // Insert a line break, separating two block decorations into separate lines. let buffer_snapshot = buffer.update(cx, |buffer, cx| { - buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], cx); + buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx); buffer.snapshot(cx) }); diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 95c3abb252..a6e5536d15 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -1240,6 +1240,7 @@ mod tests { (Point::new(0, 0)..Point::new(0, 1), "123"), (Point::new(2, 3)..Point::new(2, 3), "123"), ], + None, cx, ); buffer.snapshot(cx) @@ -1262,7 +1263,7 @@ mod tests { ); let buffer_snapshot = buffer.update(cx, |buffer, cx| { - buffer.edit([(Point::new(2, 6)..Point::new(4, 3), "456")], cx); + buffer.edit([(Point::new(2, 6)..Point::new(4, 3), "456")], None, cx); buffer.snapshot(cx) }); let (snapshot4, _) = map.read(buffer_snapshot.clone(), subscription.consume().into_inner()); @@ -1318,7 +1319,7 @@ mod tests { // Edit within one of the folds. let buffer_snapshot = buffer.update(cx, |buffer, cx| { - buffer.edit([(0..1, "12345")], cx); + buffer.edit([(0..1, "12345")], None, cx); buffer.snapshot(cx) }); let (snapshot, _) = @@ -1360,7 +1361,7 @@ mod tests { assert_eq!(snapshot.text(), "aa…cccc\nd…eeeee"); let buffer_snapshot = buffer.update(cx, |buffer, cx| { - buffer.edit([(Point::new(2, 2)..Point::new(3, 1), "")], cx); + buffer.edit([(Point::new(2, 2)..Point::new(3, 1), "")], None, cx); buffer.snapshot(cx) }); let (snapshot, _) = map.read(buffer_snapshot.clone(), subscription.consume().into_inner()); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index f570fb2fb6..4543aa3b27 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -38,9 +38,9 @@ use hover_popover::{hide_hover, HoverState}; pub use items::MAX_TAB_TITLE_LEN; pub use language::{char_kind, CharKind}; use language::{ - BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticSeverity, - IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Point, Selection, SelectionGoal, - TransactionId, + AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, + DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Point, + Selection, SelectionGoal, TransactionId, }; use link_go_to_definition::LinkGoToDefinitionState; pub use multi_buffer::{ @@ -1464,7 +1464,8 @@ impl Editor { S: ToOffset, T: Into>, { - self.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx)); + self.buffer + .update(cx, |buffer, cx| buffer.edit(edits, None, cx)); } pub fn edit_with_autoindent(&mut self, edits: I, cx: &mut ViewContext) @@ -1473,8 +1474,9 @@ impl Editor { S: ToOffset, T: Into>, { - self.buffer - .update(cx, |buffer, cx| buffer.edit_with_autoindent(edits, cx)); + self.buffer.update(cx, |buffer, cx| { + buffer.edit(edits, Some(AutoindentMode::Independent), cx) + }); } fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext) { @@ -1887,9 +1889,7 @@ impl Editor { .unzip() }; - this.buffer.update(cx, |buffer, cx| { - buffer.edit_with_autoindent(edits, cx); - }); + this.edit_with_autoindent(edits, cx); let buffer = this.buffer.read(cx).snapshot(cx); let new_selections = selection_fixup_info .into_iter() @@ -1922,10 +1922,11 @@ impl Editor { }) .collect::>() }; - buffer.edit_with_autoindent( + buffer.edit( old_selections .iter() .map(|s| (s.start..s.end, text.clone())), + Some(AutoindentMode::Block), cx, ); anchors @@ -1986,6 +1987,7 @@ impl Editor { (s.end.clone()..s.end.clone(), pair_end.clone()), ] }), + None, cx, ); }); @@ -2061,6 +2063,7 @@ impl Editor { selection_ranges .iter() .map(|range| (range.clone(), pair_end.clone())), + None, cx, ); snapshot = buffer.snapshot(cx); @@ -2363,8 +2366,11 @@ impl Editor { this.insert_snippet(&ranges, snippet, cx).log_err(); } else { this.buffer.update(cx, |buffer, cx| { - buffer - .edit_with_autoindent(ranges.iter().map(|range| (range.clone(), text)), cx); + buffer.edit( + ranges.iter().map(|range| (range.clone(), text)), + Some(AutoindentMode::Block), + cx, + ); }); } }); @@ -2725,11 +2731,12 @@ impl Editor { ) -> Result<()> { let tabstops = self.buffer.update(cx, |buffer, cx| { let snippet_text: Arc = snippet.text.clone().into(); - buffer.edit_with_autoindent( + buffer.edit( insertion_ranges .iter() .cloned() .map(|range| (range, snippet_text.clone())), + Some(AutoindentMode::Independent), cx, ); @@ -2933,7 +2940,11 @@ impl Editor { let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size); IndentSize::spaces(chars_to_next_tab_stop) }; - buffer.edit([(cursor..cursor, tab_size.chars().collect::())], cx); + buffer.edit( + [(cursor..cursor, tab_size.chars().collect::())], + None, + cx, + ); cursor.column += tab_size.len; selection.start = cursor; selection.end = cursor; @@ -3006,6 +3017,7 @@ impl Editor { row_start..row_start, indent_delta.chars().collect::(), )], + None, cx, ); @@ -3080,6 +3092,7 @@ impl Editor { deletion_ranges .into_iter() .map(|range| (range, empty_str.clone())), + None, cx, ); }); @@ -3145,6 +3158,7 @@ impl Editor { edit_ranges .into_iter() .map(|range| (range, empty_str.clone())), + None, cx, ); buffer.snapshot(cx) @@ -3202,7 +3216,7 @@ impl Editor { self.transact(cx, |this, cx| { this.buffer.update(cx, |buffer, cx| { - buffer.edit(edits, cx); + buffer.edit(edits, None, cx); }); this.request_autoscroll(Autoscroll::Fit, cx); @@ -3311,7 +3325,7 @@ impl Editor { this.unfold_ranges(unfold_ranges, true, cx); this.buffer.update(cx, |buffer, cx| { for (range, text) in edits { - buffer.edit([(range, text)], cx); + buffer.edit([(range, text)], None, cx); } }); this.fold_ranges(refold_ranges, cx); @@ -3416,7 +3430,7 @@ impl Editor { this.unfold_ranges(unfold_ranges, true, cx); this.buffer.update(cx, |buffer, cx| { for (range, text) in edits { - buffer.edit([(range, text)], cx); + buffer.edit([(range, text)], None, cx); } }); this.fold_ranges(refold_ranges, cx); @@ -3467,7 +3481,8 @@ impl Editor { }); edits }); - this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx)); + this.buffer + .update(cx, |buffer, cx| buffer.edit(edits, None, cx)); let selections = this.selections.all::(cx); this.change_selections(Some(Autoscroll::Fit), cx, |s| { s.select(selections); @@ -3597,7 +3612,7 @@ impl Editor { edits.push((range, to_insert)); } drop(snapshot); - buffer.edit_with_autoindent(edits, cx); + buffer.edit(edits, Some(AutoindentMode::Block), cx); }); let selections = this.selections.all::(cx); @@ -4432,6 +4447,7 @@ impl Editor { .iter() .cloned() .map(|range| (range, empty_str.clone())), + None, cx, ); } else { @@ -4441,7 +4457,7 @@ impl Editor { let position = Point::new(range.start.row, min_column); (position..position, full_comment_prefix.clone()) }); - buffer.edit(edits, cx); + buffer.edit(edits, None, cx); } } } @@ -4875,9 +4891,9 @@ impl Editor { editor.override_text_style = Some(Box::new(move |style| old_highlight_id.style(&style.syntax))); } - editor - .buffer - .update(cx, |buffer, cx| buffer.edit([(0..0, old_name.clone())], cx)); + editor.buffer.update(cx, |buffer, cx| { + buffer.edit([(0..0, old_name.clone())], None, cx) + }); editor.select_all(&SelectAll, cx); editor }); @@ -6658,8 +6674,8 @@ mod tests { // Simulate an edit in another editor buffer.update(cx, |buffer, cx| { buffer.start_transaction_at(now, cx); - buffer.edit([(0..1, "a")], cx); - buffer.edit([(1..1, "b")], cx); + buffer.edit([(0..1, "a")], None, cx); + buffer.edit([(1..1, "b")], None, cx); buffer.end_transaction_at(now, cx); }); @@ -7200,6 +7216,7 @@ mod tests { (Point::new(1, 0)..Point::new(1, 0), "\t"), (Point::new(1, 1)..Point::new(1, 1), "\t"), ], + None, cx, ); }); @@ -7836,6 +7853,7 @@ mod tests { (Point::new(1, 2)..Point::new(3, 0), ""), (Point::new(4, 2)..Point::new(6, 0), ""), ], + None, cx, ); assert_eq!( @@ -7894,7 +7912,7 @@ mod tests { // Edit the buffer directly, deleting ranges surrounding the editor's selections buffer.update(cx, |buffer, cx| { - buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx); + buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx); assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent()); }); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index fa48cabf05..a12285dbbe 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -7,9 +7,9 @@ use collections::{Bound, HashMap, HashSet}; use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task}; pub use language::Completion; use language::{ - char_kind, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, DiagnosticEntry, Event, File, - IndentSize, Language, OffsetRangeExt, Outline, OutlineItem, Selection, ToOffset as _, - ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, + char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, + DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, Outline, OutlineItem, + Selection, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, }; use smallvec::SmallVec; use std::{ @@ -302,28 +302,10 @@ impl MultiBuffer { self.read(cx).symbols_containing(offset, theme) } - pub fn edit(&mut self, edits: I, cx: &mut ModelContext) - where - I: IntoIterator, T)>, - S: ToOffset, - T: Into>, - { - self.edit_internal(edits, false, cx) - } - - pub fn edit_with_autoindent(&mut self, edits: I, cx: &mut ModelContext) - where - I: IntoIterator, T)>, - S: ToOffset, - T: Into>, - { - self.edit_internal(edits, true, cx) - } - - pub fn edit_internal( + pub fn edit( &mut self, edits: I, - autoindent: bool, + autoindent_mode: Option, cx: &mut ModelContext, ) where I: IntoIterator, T)>, @@ -345,11 +327,7 @@ impl MultiBuffer { if let Some(buffer) = self.as_singleton() { return buffer.update(cx, |buffer, cx| { - if autoindent { - buffer.edit_with_autoindent(edits, cx); - } else { - buffer.edit(edits, cx); - } + buffer.edit(edits, autoindent_mode, cx); }); } @@ -464,13 +442,8 @@ impl MultiBuffer { } } - if autoindent { - buffer.edit_with_autoindent(deletions, cx); - buffer.edit_with_autoindent(insertions, cx); - } else { - buffer.edit(deletions, cx); - buffer.edit(insertions, cx); - } + buffer.edit(deletions, autoindent_mode, cx); + buffer.edit(insertions, autoindent_mode, cx); }) } } @@ -1386,7 +1359,7 @@ impl MultiBuffer { log::info!("mutating multi-buffer with {:?}", edits); drop(snapshot); - self.edit(edits, cx); + self.edit(edits, None, cx); } pub fn randomly_edit_excerpts( @@ -3224,7 +3197,7 @@ mod tests { .collect::>() ); - buffer.update(cx, |buffer, cx| buffer.edit([(1..3, "XXX\n")], cx)); + buffer.update(cx, |buffer, cx| buffer.edit([(1..3, "XXX\n")], None, cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), buffer.read(cx).text()); @@ -3247,11 +3220,11 @@ mod tests { let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), "a"); - guest_buffer.update(cx, |buffer, cx| buffer.edit([(1..1, "b")], cx)); + guest_buffer.update(cx, |buffer, cx| buffer.edit([(1..1, "b")], None, cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), "ab"); - guest_buffer.update(cx, |buffer, cx| buffer.edit([(2..2, "c")], cx)); + guest_buffer.update(cx, |buffer, cx| buffer.edit([(2..2, "c")], None, cx)); let snapshot = multibuffer.read(cx).snapshot(cx); assert_eq!(snapshot.text(), "abc"); } @@ -3392,6 +3365,7 @@ mod tests { (Point::new(0, 0)..Point::new(0, 0), text), (Point::new(2, 1)..Point::new(2, 3), text), ], + None, cx, ); }); @@ -3529,8 +3503,8 @@ mod tests { let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx)); let old_snapshot = multibuffer.read(cx).snapshot(cx); buffer.update(cx, |buffer, cx| { - buffer.edit([(0..0, "X")], cx); - buffer.edit([(5..5, "Y")], cx); + buffer.edit([(0..0, "X")], None, cx); + buffer.edit([(5..5, "Y")], None, cx); }); let new_snapshot = multibuffer.read(cx).snapshot(cx); @@ -3577,12 +3551,12 @@ mod tests { assert_eq!(Anchor::max().to_offset(&old_snapshot), 10); buffer_1.update(cx, |buffer, cx| { - buffer.edit([(0..0, "W")], cx); - buffer.edit([(5..5, "X")], cx); + buffer.edit([(0..0, "W")], None, cx); + buffer.edit([(5..5, "X")], None, cx); }); buffer_2.update(cx, |buffer, cx| { - buffer.edit([(0..0, "Y")], cx); - buffer.edit([(6..6, "Z")], cx); + buffer.edit([(0..0, "Y")], None, cx); + buffer.edit([(6..6, "Z")], None, cx); }); let new_snapshot = multibuffer.read(cx).snapshot(cx); @@ -3611,7 +3585,7 @@ mod tests { // Create an insertion id in buffer 1 that doesn't exist in buffer 2. // Add an excerpt from buffer 1 that spans this new insertion. - buffer_1.update(cx, |buffer, cx| buffer.edit([(4..4, "123")], cx)); + buffer_1.update(cx, |buffer, cx| buffer.edit([(4..4, "123")], None, cx)); let excerpt_id_1 = multibuffer.update(cx, |multibuffer, cx| { multibuffer .push_excerpts( @@ -4184,6 +4158,7 @@ mod tests { (Point::new(0, 0)..Point::new(0, 0), "A"), (Point::new(1, 0)..Point::new(1, 0), "A"), ], + None, cx, ); multibuffer.edit( @@ -4191,6 +4166,7 @@ mod tests { (Point::new(0, 1)..Point::new(0, 1), "B"), (Point::new(1, 1)..Point::new(1, 1), "B"), ], + None, cx, ); multibuffer.end_transaction_at(now, cx); @@ -4199,19 +4175,19 @@ mod tests { // Edit buffer 1 through the multibuffer now += 2 * group_interval; multibuffer.start_transaction_at(now, cx); - multibuffer.edit([(2..2, "C")], cx); + multibuffer.edit([(2..2, "C")], None, cx); multibuffer.end_transaction_at(now, cx); assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678"); // Edit buffer 1 independently buffer_1.update(cx, |buffer_1, cx| { buffer_1.start_transaction_at(now); - buffer_1.edit([(3..3, "D")], cx); + buffer_1.edit([(3..3, "D")], None, cx); buffer_1.end_transaction_at(now, cx); now += 2 * group_interval; buffer_1.start_transaction_at(now); - buffer_1.edit([(4..4, "E")], cx); + buffer_1.edit([(4..4, "E")], None, cx); buffer_1.end_transaction_at(now, cx); }); assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678"); @@ -4252,7 +4228,7 @@ mod tests { // Redo stack gets cleared after an edit. now += 2 * group_interval; multibuffer.start_transaction_at(now, cx); - multibuffer.edit([(0..0, "X")], cx); + multibuffer.edit([(0..0, "X")], None, cx); multibuffer.end_transaction_at(now, cx); assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678"); multibuffer.redo(cx); diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 0dc8165bfb..6e9f368e77 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -16,6 +16,7 @@ test-support = [ "text/test-support", "tree-sitter-rust", "tree-sitter-typescript", + "settings/test-support", "util/test-support", ] @@ -57,6 +58,7 @@ collections = { path = "../collections", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } lsp = { path = "../lsp", features = ["test-support"] } text = { path = "../text", features = ["test-support"] } +settings = { path = "../settings", features = ["test-support"] } util = { path = "../util", features = ["test-support"] } ctor = "0.1" env_logger = "0.9" diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 4b0d2566c3..8f0f7d40ec 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -229,11 +229,18 @@ struct SyntaxTree { version: clock::Global, } +#[derive(Clone, Copy)] +pub enum AutoindentMode { + Block, + Independent, +} + #[derive(Clone)] struct AutoindentRequest { before_edit: BufferSnapshot, entries: Vec, indent_size: IndentSize, + mode: AutoindentMode, } #[derive(Clone)] @@ -852,8 +859,12 @@ impl Buffer { // At this point, old_suggestions contains the suggested indentation for all edited lines // with respect to the state of the buffer before the edit, but keyed by the row for these // lines after the edits were applied. + let new_edited_row_ranges = contiguous_ranges( - row_ranges.iter().map(|range| range.start), + row_ranges.iter().flat_map(|range| match request.mode { + AutoindentMode::Block => range.start..range.start + 1, + AutoindentMode::Independent => range.clone(), + }), max_rows_between_yields, ); for new_edited_row_range in new_edited_row_ranges { @@ -883,26 +894,32 @@ impl Buffer { yield_now().await; } - for row_range in row_ranges { - if row_range.len() > 1 { - if let Some(new_indent_size) = indent_sizes.get(&row_range.start).copied() { - let old_indent_size = snapshot.indent_size_for_line(row_range.start); - if new_indent_size.kind == old_indent_size.kind { - let delta = new_indent_size.len as i64 - old_indent_size.len as i64; - if delta != 0 { - for row in row_range.skip(1) { - indent_sizes.entry(row).or_insert_with(|| { - let mut size = snapshot.indent_size_for_line(row); - if size.kind == new_indent_size.kind { - if delta > 0 { - size.len += delta as u32; - } else if delta < 0 { - size.len = - size.len.saturating_sub(-delta as u32); + if matches!(request.mode, AutoindentMode::Block) { + for row_range in row_ranges { + if row_range.len() > 1 { + if let Some(new_indent_size) = + indent_sizes.get(&row_range.start).copied() + { + let old_indent_size = + snapshot.indent_size_for_line(row_range.start); + if new_indent_size.kind == old_indent_size.kind { + let delta = + new_indent_size.len as i64 - old_indent_size.len as i64; + if delta != 0 { + for row in row_range.skip(1) { + indent_sizes.entry(row).or_insert_with(|| { + let mut size = snapshot.indent_size_for_line(row); + if size.kind == new_indent_size.kind { + if delta > 0 { + size.len += delta as u32; + } else if delta < 0 { + size.len = + size.len.saturating_sub(-delta as u32); + } } - } - size - }); + size + }); + } } } } @@ -948,6 +965,7 @@ impl Buffer { .take((size.len - current_size.len) as usize) .collect::(), )], + None, cx, ); } else if size.len < current_size.len { @@ -956,6 +974,7 @@ impl Buffer { Point::new(row, 0)..Point::new(row, current_size.len - size.len), "", )], + None, cx, ); } @@ -993,7 +1012,7 @@ impl Buffer { match tag { ChangeTag::Equal => offset += len, ChangeTag::Delete => { - self.edit([(range, "")], cx); + self.edit([(range, "")], None, cx); } ChangeTag::Insert => { self.edit( @@ -1002,6 +1021,7 @@ impl Buffer { &diff.new_text[range.start - diff.start_offset ..range.end - diff.start_offset], )], + None, cx, ); offset += len; @@ -1138,46 +1158,13 @@ impl Buffer { where T: Into>, { - self.edit_internal([(0..self.len(), text)], None, cx) + self.edit([(0..self.len(), text)], None, cx) } pub fn edit( &mut self, edits_iter: I, - cx: &mut ModelContext, - ) -> Option - where - I: IntoIterator, T)>, - S: ToOffset, - T: Into>, - { - self.edit_internal(edits_iter, None, cx) - } - - pub fn edit_with_autoindent( - &mut self, - edits_iter: I, - cx: &mut ModelContext, - ) -> Option - where - I: IntoIterator, T)>, - S: ToOffset, - T: Into>, - { - let language_name = self.language().map(|language| language.name()); - let settings = cx.global::(); - let indent_size = if settings.hard_tabs(language_name.as_deref()) { - IndentSize::tab() - } else { - IndentSize::spaces(settings.tab_size(language_name.as_deref()).get()) - }; - self.edit_internal(edits_iter, Some(indent_size), cx) - } - - pub fn edit_internal( - &mut self, - edits_iter: I, - autoindent_size: Option, + autoindent_mode: Option, cx: &mut ModelContext, ) -> Option where @@ -1212,16 +1199,21 @@ impl Buffer { self.start_transaction(); self.pending_autoindent.take(); - let autoindent_request = self - .language - .as_ref() - .and_then(|_| autoindent_size) - .map(|autoindent_size| (self.snapshot(), autoindent_size)); + let autoindent_request = autoindent_mode + .and_then(|mode| self.language.as_ref().map(|_| (self.snapshot(), mode))); let edit_operation = self.text.edit(edits.iter().cloned()); let edit_id = edit_operation.local_timestamp(); - if let Some((before_edit, size)) = autoindent_request { + if let Some((before_edit, mode)) = autoindent_request { + let language_name = self.language().map(|language| language.name()); + let settings = cx.global::(); + let indent_size = if settings.hard_tabs(language_name.as_deref()) { + IndentSize::tab() + } else { + IndentSize::spaces(settings.tab_size(language_name.as_deref()).get()) + }; + let mut delta = 0isize; let entries = edits .into_iter() @@ -1248,8 +1240,11 @@ impl Buffer { range_of_insertion_to_indent.start += 1; first_line_is_new = true; } - if new_text[range_of_insertion_to_indent.clone()].ends_with('\n') { - range_of_insertion_to_indent.end -= 1; + + if matches!(mode, AutoindentMode::Block) { + if new_text[range_of_insertion_to_indent.clone()].ends_with('\n') { + range_of_insertion_to_indent.end -= 1; + } } AutoindentRequestEntry { @@ -1263,7 +1258,8 @@ impl Buffer { self.autoindent_requests.push(Arc::new(AutoindentRequest { before_edit, entries, - indent_size: size, + indent_size, + mode, })); } @@ -1550,7 +1546,7 @@ impl Buffer { edits.push((range, new_text)); } log::info!("mutating buffer {} with {:?}", self.replica_id(), edits); - self.edit(edits, cx); + self.edit(edits, None, cx); } pub fn randomly_undo_redo(&mut self, rng: &mut impl rand::Rng, cx: &mut ModelContext) { diff --git a/crates/language/src/tests.rs b/crates/language/src/tests.rs index 0a6fb2ce02..acfaf5980d 100644 --- a/crates/language/src/tests.rs +++ b/crates/language/src/tests.rs @@ -33,8 +33,12 @@ fn test_line_endings(cx: &mut gpui::MutableAppContext) { assert_eq!(buffer.line_ending(), LineEnding::Windows); buffer.check_invariants(); - buffer.edit_with_autoindent([(buffer.len()..buffer.len(), "\r\nfour")], cx); - buffer.edit([(0..0, "zero\r\n")], cx); + buffer.edit( + [(buffer.len()..buffer.len(), "\r\nfour")], + Some(AutoindentMode::Independent), + cx, + ); + buffer.edit([(0..0, "zero\r\n")], None, cx); assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour"); assert_eq!(buffer.line_ending(), LineEnding::Windows); buffer.check_invariants(); @@ -114,7 +118,7 @@ fn test_edit_events(cx: &mut gpui::MutableAppContext) { // An edit emits an edited event, followed by a dirty changed event, // since the buffer was previously in a clean state. - buffer.edit([(2..4, "XYZ")], cx); + buffer.edit([(2..4, "XYZ")], None, cx); // An empty transaction does not emit any events. buffer.start_transaction(); @@ -123,8 +127,8 @@ fn test_edit_events(cx: &mut gpui::MutableAppContext) { // A transaction containing two edits emits one edited event. now += Duration::from_secs(1); buffer.start_transaction_at(now); - buffer.edit([(5..5, "u")], cx); - buffer.edit([(6..6, "w")], cx); + buffer.edit([(5..5, "u")], None, cx); + buffer.edit([(6..6, "w")], None, cx); buffer.end_transaction_at(now, cx); // Undoing a transaction emits one edited event. @@ -224,11 +228,11 @@ async fn test_reparse(cx: &mut gpui::TestAppContext) { buf.start_transaction(); let offset = buf.text().find(")").unwrap(); - buf.edit([(offset..offset, "b: C")], cx); + buf.edit([(offset..offset, "b: C")], None, cx); assert!(!buf.is_parsing()); let offset = buf.text().find("}").unwrap(); - buf.edit([(offset..offset, " d; ")], cx); + buf.edit([(offset..offset, " d; ")], None, cx); assert!(!buf.is_parsing()); buf.end_transaction(cx); @@ -253,19 +257,19 @@ async fn test_reparse(cx: &mut gpui::TestAppContext) { // * add a turbofish to the method call buffer.update(cx, |buf, cx| { let offset = buf.text().find(";").unwrap(); - buf.edit([(offset..offset, ".e")], cx); + buf.edit([(offset..offset, ".e")], None, cx); assert_eq!(buf.text(), "fn a(b: C) { d.e; }"); assert!(buf.is_parsing()); }); buffer.update(cx, |buf, cx| { let offset = buf.text().find(";").unwrap(); - buf.edit([(offset..offset, "(f)")], cx); + buf.edit([(offset..offset, "(f)")], None, cx); assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }"); assert!(buf.is_parsing()); }); buffer.update(cx, |buf, cx| { let offset = buf.text().find("(f)").unwrap(); - buf.edit([(offset..offset, "::")], cx); + buf.edit([(offset..offset, "::")], None, cx); assert_eq!(buf.text(), "fn a(b: C) { d.e::(f); }"); assert!(buf.is_parsing()); }); @@ -626,20 +630,32 @@ fn test_autoindent_with_soft_tabs(cx: &mut MutableAppContext) { let text = "fn a() {}"; let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx); - buffer.edit_with_autoindent([(8..8, "\n\n")], cx); + buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::Independent), cx); assert_eq!(buffer.text(), "fn a() {\n \n}"); - buffer.edit_with_autoindent([(Point::new(1, 4)..Point::new(1, 4), "b()\n")], cx); + buffer.edit( + [(Point::new(1, 4)..Point::new(1, 4), "b()\n")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!(buffer.text(), "fn a() {\n b()\n \n}"); // Create a field expression on a new line, causing that line // to be indented. - buffer.edit_with_autoindent([(Point::new(2, 4)..Point::new(2, 4), ".c")], cx); + buffer.edit( + [(Point::new(2, 4)..Point::new(2, 4), ".c")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}"); // Remove the dot so that the line is no longer a field expression, // causing the line to be outdented. - buffer.edit_with_autoindent([(Point::new(2, 8)..Point::new(2, 9), "")], cx); + buffer.edit( + [(Point::new(2, 8)..Point::new(2, 9), "")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!(buffer.text(), "fn a() {\n b()\n c\n}"); buffer @@ -656,20 +672,32 @@ fn test_autoindent_with_hard_tabs(cx: &mut MutableAppContext) { let text = "fn a() {}"; let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx); - buffer.edit_with_autoindent([(8..8, "\n\n")], cx); + buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::Independent), cx); assert_eq!(buffer.text(), "fn a() {\n\t\n}"); - buffer.edit_with_autoindent([(Point::new(1, 1)..Point::new(1, 1), "b()\n")], cx); + buffer.edit( + [(Point::new(1, 1)..Point::new(1, 1), "b()\n")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}"); // Create a field expression on a new line, causing that line // to be indented. - buffer.edit_with_autoindent([(Point::new(2, 1)..Point::new(2, 1), ".c")], cx); + buffer.edit( + [(Point::new(2, 1)..Point::new(2, 1), ".c")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}"); // Remove the dot so that the line is no longer a field expression, // causing the line to be outdented. - buffer.edit_with_autoindent([(Point::new(2, 2)..Point::new(2, 3), "")], cx); + buffer.edit( + [(Point::new(2, 2)..Point::new(2, 3), "")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}"); buffer @@ -694,11 +722,12 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta // Lines 2 and 3 don't match the indentation suggestion. When editing these lines, // their indentation is not adjusted. - buffer.edit_with_autoindent( + buffer.edit( [ (empty(Point::new(1, 1)), "()"), (empty(Point::new(2, 1)), "()"), ], + Some(AutoindentMode::Independent), cx, ); assert_eq!( @@ -714,11 +743,12 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta // When appending new content after these lines, the indentation is based on the // preceding lines' actual indentation. - buffer.edit_with_autoindent( + buffer.edit( [ (empty(Point::new(1, 1)), "\n.f\n.g"), (empty(Point::new(2, 1)), "\n.f\n.g"), ], + Some(AutoindentMode::Independent), cx, ); assert_eq!( @@ -751,7 +781,11 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx); // Delete a closing curly brace changes the suggested indent for the line. - buffer.edit_with_autoindent([(Point::new(3, 4)..Point::new(3, 5), "")], cx); + buffer.edit( + [(Point::new(3, 4)..Point::new(3, 5), "")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!( buffer.text(), " @@ -767,7 +801,11 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut Muta ); // Manually editing the leading whitespace - buffer.edit_with_autoindent([(Point::new(3, 0)..Point::new(3, 12), "")], cx); + buffer.edit( + [(Point::new(3, 0)..Point::new(3, 12), "")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!( buffer.text(), " @@ -795,7 +833,7 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppConte let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx); - buffer.edit_with_autoindent([(5..5, "\nb")], cx); + buffer.edit([(5..5, "\nb")], Some(AutoindentMode::Independent), cx); assert_eq!( buffer.text(), " @@ -807,7 +845,11 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppConte // The indentation suggestion changed because `@end` node (a close paren) // is now at the beginning of the line. - buffer.edit_with_autoindent([(Point::new(1, 4)..Point::new(1, 5), "")], cx); + buffer.edit( + [(Point::new(1, 4)..Point::new(1, 5), "")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!( buffer.text(), " @@ -827,7 +869,11 @@ fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut MutableAppContext) { cx.add_model(|cx| { let text = "a\nb"; let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx); - buffer.edit_with_autoindent([(0..1, "\n"), (2..3, "\n")], cx); + buffer.edit( + [(0..1, "\n"), (2..3, "\n")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!(buffer.text(), "\n\n\n"); buffer }); @@ -848,8 +894,9 @@ fn test_autoindent_multi_line_insertion(cx: &mut MutableAppContext) { .unindent(); let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx); - buffer.edit_with_autoindent( + buffer.edit( [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")], + Some(AutoindentMode::Independent), cx, ); assert_eq!( @@ -896,8 +943,9 @@ fn test_autoindent_preserves_relative_indentation_in_multi_line_insertion( .unindent(); // insert at the beginning of a line - buffer.edit_with_autoindent( + buffer.edit( [(Point::new(2, 0)..Point::new(2, 0), pasted_text.clone())], + Some(AutoindentMode::Block), cx, ); assert_eq!( @@ -941,7 +989,11 @@ fn test_autoindent_language_without_indents_query(cx: &mut MutableAppContext) { )), cx, ); - buffer.edit_with_autoindent([(Point::new(3, 0)..Point::new(3, 0), "\n")], cx); + buffer.edit( + [(Point::new(3, 0)..Point::new(3, 0), "\n")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!( buffer.text(), " @@ -963,18 +1015,18 @@ fn test_serialization(cx: &mut gpui::MutableAppContext) { let buffer1 = cx.add_model(|cx| { let mut buffer = Buffer::new(0, "abc", cx); - buffer.edit([(3..3, "D")], cx); + buffer.edit([(3..3, "D")], None, cx); now += Duration::from_secs(1); buffer.start_transaction_at(now); - buffer.edit([(4..4, "E")], cx); + buffer.edit([(4..4, "E")], None, cx); buffer.end_transaction_at(now, cx); assert_eq!(buffer.text(), "abcDE"); buffer.undo(cx); assert_eq!(buffer.text(), "abcD"); - buffer.edit([(4..4, "F")], cx); + buffer.edit([(4..4, "F")], None, cx); assert_eq!(buffer.text(), "abcDF"); buffer }); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 29106b5889..898dbb5a2f 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -3168,7 +3168,7 @@ impl Project { buffer.finalize_last_transaction(); buffer.start_transaction(); for (range, text) in edits { - buffer.edit([(range, text)], cx); + buffer.edit([(range, text)], None, cx); } if buffer.end_transaction(cx).is_some() { let transaction = buffer.finalize_last_transaction().unwrap().clone(); @@ -3663,7 +3663,7 @@ impl Project { buffer.finalize_last_transaction(); buffer.start_transaction(); for (range, text) in edits { - buffer.edit([(range, text)], cx); + buffer.edit([(range, text)], None, cx); } let transaction = if buffer.end_transaction(cx).is_some() { let transaction = buffer.finalize_last_transaction().unwrap().clone(); @@ -4023,7 +4023,7 @@ impl Project { buffer.finalize_last_transaction(); buffer.start_transaction(); for (range, text) in edits { - buffer.edit([(range, text)], cx); + buffer.edit([(range, text)], None, cx); } let transaction = if buffer.end_transaction(cx).is_some() { let transaction = buffer.finalize_last_transaction().unwrap().clone(); diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index e36bd2c75a..4c5e9ef8e1 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -169,7 +169,7 @@ async fn test_managing_language_servers( }); // Edit a buffer. The changes are reported to the language server. - rust_buffer.update(cx, |buffer, cx| buffer.edit([(16..16, "2")], cx)); + rust_buffer.update(cx, |buffer, cx| buffer.edit([(16..16, "2")], None, cx)); assert_eq!( fake_rust_server .receive_notification::() @@ -226,8 +226,10 @@ async fn test_managing_language_servers( }); // Changes are reported only to servers matching the buffer's language. - toml_buffer.update(cx, |buffer, cx| buffer.edit([(5..5, "23")], cx)); - rust_buffer2.update(cx, |buffer, cx| buffer.edit([(0..0, "let x = 1;")], cx)); + toml_buffer.update(cx, |buffer, cx| buffer.edit([(5..5, "23")], None, cx)); + rust_buffer2.update(cx, |buffer, cx| { + buffer.edit([(0..0, "let x = 1;")], None, cx) + }); assert_eq!( fake_rust_server .receive_notification::() @@ -348,7 +350,7 @@ async fn test_managing_language_servers( }); // The renamed file's version resets after changing language server. - rust_buffer2.update(cx, |buffer, cx| buffer.edit([(0..0, "// ")], cx)); + rust_buffer2.update(cx, |buffer, cx| buffer.edit([(0..0, "// ")], None, cx)); assert_eq!( fake_json_server .receive_notification::() @@ -972,7 +974,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { .await; // Edit the buffer, moving the content down - buffer.update(cx, |buffer, cx| buffer.edit([(0..0, "\n\n")], cx)); + buffer.update(cx, |buffer, cx| buffer.edit([(0..0, "\n\n")], None, cx)); let change_notification_1 = fake_server .receive_notification::() .await; @@ -1137,9 +1139,13 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { // Keep editing the buffer and ensure disk-based diagnostics get translated according to the // changes since the last save. buffer.update(cx, |buffer, cx| { - buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], cx); - buffer.edit([(Point::new(2, 8)..Point::new(2, 10), "(x: usize)")], cx); - buffer.edit([(Point::new(3, 10)..Point::new(3, 10), "xxx")], cx); + buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], None, cx); + buffer.edit( + [(Point::new(2, 8)..Point::new(2, 10), "(x: usize)")], + None, + cx, + ); + buffer.edit([(Point::new(3, 10)..Point::new(3, 10), "xxx")], None, cx); }); let change_notification_2 = fake_server .receive_notification::() @@ -1330,6 +1336,7 @@ async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) { Point::new(0, 0)..Point::new(0, 0), "// above first function\n", )], + None, cx, ); buffer.edit( @@ -1337,6 +1344,7 @@ async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) { Point::new(2, 0)..Point::new(2, 0), " // inside first function\n", )], + None, cx, ); buffer.edit( @@ -1344,6 +1352,7 @@ async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) { Point::new(6, 4)..Point::new(6, 4), "// inside second function ", )], + None, cx, ); @@ -1405,7 +1414,7 @@ async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) { buffer.update(cx, |buffer, cx| { for (range, new_text) in edits { - buffer.edit([(range, new_text)], cx); + buffer.edit([(range, new_text)], None, cx); } assert_eq!( buffer.text(), @@ -1517,7 +1526,7 @@ async fn test_edits_from_lsp_with_edits_on_adjacent_lines(cx: &mut gpui::TestApp ); for (range, new_text) in edits { - buffer.edit([(range, new_text)], cx); + buffer.edit([(range, new_text)], None, cx); } assert_eq!( buffer.text(), @@ -1620,7 +1629,7 @@ async fn test_invalid_edits_from_lsp(cx: &mut gpui::TestAppContext) { ); for (range, new_text) in edits { - buffer.edit([(range, new_text)], cx); + buffer.edit([(range, new_text)], None, cx); } assert_eq!( buffer.text(), @@ -2025,7 +2034,7 @@ async fn test_save_file(cx: &mut gpui::TestAppContext) { buffer .update(cx, |buffer, cx| { assert_eq!(buffer.text(), "the old contents"); - buffer.edit([(0..0, "a line of text.\n".repeat(10 * 1024))], cx); + buffer.edit([(0..0, "a line of text.\n".repeat(10 * 1024))], None, cx); buffer.save(cx) }) .await @@ -2053,7 +2062,7 @@ async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) { .unwrap(); buffer .update(cx, |buffer, cx| { - buffer.edit([(0..0, "a line of text.\n".repeat(10 * 1024))], cx); + buffer.edit([(0..0, "a line of text.\n".repeat(10 * 1024))], None, cx); buffer.save(cx) }) .await @@ -2073,7 +2082,7 @@ async fn test_save_as(cx: &mut gpui::TestAppContext) { project.create_buffer("", None, cx).unwrap() }); buffer.update(cx, |buffer, cx| { - buffer.edit([(0..0, "abc")], cx); + buffer.edit([(0..0, "abc")], None, cx); assert!(buffer.is_dirty()); assert!(!buffer.has_conflict()); }); @@ -2329,7 +2338,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { assert!(!buffer.is_dirty()); assert!(events.borrow().is_empty()); - buffer.edit([(1..2, "")], cx); + buffer.edit([(1..2, "")], None, cx); }); // after the first edit, the buffer is dirty, and emits a dirtied event. @@ -2356,8 +2365,8 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { assert_eq!(*events.borrow(), &[language::Event::Saved]); events.borrow_mut().clear(); - buffer.edit([(1..1, "B")], cx); - buffer.edit([(2..2, "D")], cx); + buffer.edit([(1..1, "B")], None, cx); + buffer.edit([(2..2, "D")], None, cx); }); // after editing again, the buffer is dirty, and emits another dirty event. @@ -2376,7 +2385,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { // After restoring the buffer to its previously-saved state, // the buffer is not considered dirty anymore. - buffer.edit([(1..3, "")], cx); + buffer.edit([(1..3, "")], None, cx); assert!(buffer.text() == "ac"); assert!(!buffer.is_dirty()); }); @@ -2427,7 +2436,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { }); buffer3.update(cx, |buffer, cx| { - buffer.edit([(0..0, "x")], cx); + buffer.edit([(0..0, "x")], None, cx); }); events.borrow_mut().clear(); fs.remove_file("/dir/file3".as_ref(), Default::default()) @@ -2495,7 +2504,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) { // Modify the buffer buffer.update(cx, |buffer, cx| { - buffer.edit([(0..0, " ")], cx); + buffer.edit([(0..0, " ")], None, cx); assert!(buffer.is_dirty()); assert!(!buffer.has_conflict()); }); @@ -2986,7 +2995,7 @@ async fn test_search(cx: &mut gpui::TestAppContext) { .unwrap(); buffer_4.update(cx, |buffer, cx| { let text = "two::TWO"; - buffer.edit([(20..28, text), (31..43, text)], cx); + buffer.edit([(20..28, text), (31..43, text)], None, cx); }); assert_eq!( diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 529da6f7b6..52631e71b4 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -260,7 +260,7 @@ impl BufferSearchBar { self.query_editor.update(cx, |query_editor, cx| { query_editor.buffer().update(cx, |query_buffer, cx| { let len = query_buffer.len(cx); - query_buffer.edit([(0..len, query)], cx); + query_buffer.edit([(0..len, query)], None, cx); }); }); } diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 65909702ba..8ec0fea9a9 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -13,7 +13,7 @@ use change::init as change_init; use collections::HashSet; use editor::{Autoscroll, Bias, ClipboardSelection, DisplayPoint}; use gpui::{actions, MutableAppContext, ViewContext}; -use language::{Point, SelectionGoal}; +use language::{AutoindentMode, Point, SelectionGoal}; use workspace::Workspace; use self::{change::change_over, delete::delete_over, yank::yank_over}; @@ -278,7 +278,7 @@ fn paste(_: &mut Workspace, _: &Paste, cx: &mut ViewContext) { } } drop(snapshot); - buffer.edit_with_autoindent(edits, cx); + buffer.edit(edits, Some(AutoindentMode::Independent), cx); }); editor.change_selections(Some(Autoscroll::Fit), cx, |s| { diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index fedb999cad..a81ac1c455 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use collections::HashMap; use editor::{display_map::ToDisplayPoint, Autoscroll, Bias, ClipboardSelection}; use gpui::{actions, MutableAppContext, ViewContext}; -use language::SelectionGoal; +use language::{AutoindentMode, SelectionGoal}; use workspace::Workspace; use crate::{motion::Motion, state::Mode, utils::copy_selections_content, Vim}; @@ -254,7 +254,7 @@ pub fn paste(_: &mut Workspace, _: &VisualPaste, cx: &mut ViewContext } } drop(snapshot); - buffer.edit_with_autoindent(edits, cx); + buffer.edit(edits, Some(AutoindentMode::Independent), cx); }); editor.change_selections(Some(Autoscroll::Fit), cx, |s| { diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index 71fde5bd5e..074f51bc8f 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -249,7 +249,7 @@ impl super::LspAdapter for CLspAdapter { #[cfg(test)] mod tests { use gpui::MutableAppContext; - use language::Buffer; + use language::{AutoindentMode, Buffer}; use settings::Settings; use std::sync::Arc; @@ -265,21 +265,25 @@ mod tests { let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(language), cx); // empty function - buffer.edit_with_autoindent([(0..0, "int main() {}")], cx); + buffer.edit([(0..0, "int main() {}")], None, cx); // indent inside braces let ix = buffer.len() - 1; - buffer.edit_with_autoindent([(ix..ix, "\n\n")], cx); + buffer.edit([(ix..ix, "\n\n")], Some(AutoindentMode::Independent), cx); assert_eq!(buffer.text(), "int main() {\n \n}"); // indent body of single-statement if statement let ix = buffer.len() - 2; - buffer.edit_with_autoindent([(ix..ix, "if (a)\nb;")], cx); + buffer.edit( + [(ix..ix, "if (a)\nb;")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!(buffer.text(), "int main() {\n if (a)\n b;\n}"); // indent inside field expression let ix = buffer.len() - 3; - buffer.edit_with_autoindent([(ix..ix, "\n.c")], cx); + buffer.edit([(ix..ix, "\n.c")], Some(AutoindentMode::Independent), cx); assert_eq!(buffer.text(), "int main() {\n if (a)\n b\n .c;\n}"); buffer diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs index 644d9ece40..f76b4e0fba 100644 --- a/crates/zed/src/languages/python.rs +++ b/crates/zed/src/languages/python.rs @@ -147,7 +147,7 @@ impl LspAdapter for PythonLspAdapter { #[cfg(test)] mod tests { use gpui::{ModelContext, MutableAppContext}; - use language::Buffer; + use language::{AutoindentMode, Buffer}; use settings::Settings; use std::sync::Arc; @@ -163,7 +163,7 @@ mod tests { let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(language), cx); let append = |buffer: &mut Buffer, text: &str, cx: &mut ModelContext| { let ix = buffer.len(); - buffer.edit_with_autoindent([(ix..ix, text)], cx); + buffer.edit([(ix..ix, text)], Some(AutoindentMode::Independent), cx); }; // indent after "def():" @@ -207,7 +207,11 @@ mod tests { // dedent the closing paren if it is shifted to the beginning of the line let argument_ix = buffer.text().find("1").unwrap(); - buffer.edit_with_autoindent([(argument_ix..argument_ix + 1, "")], cx); + buffer.edit( + [(argument_ix..argument_ix + 1, "")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!( buffer.text(), "def a():\n \n if a:\n b()\n else:\n foo(\n )" @@ -222,7 +226,11 @@ mod tests { // manually outdent the last line let end_whitespace_ix = buffer.len() - 4; - buffer.edit_with_autoindent([(end_whitespace_ix..buffer.len(), "")], cx); + buffer.edit( + [(end_whitespace_ix..buffer.len(), "")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!( buffer.text(), "def a():\n \n if a:\n b()\n else:\n foo(\n )\n" @@ -236,7 +244,7 @@ mod tests { ); // reset to a simple if statement - buffer.edit([(0..buffer.len(), "if a:\n b(\n )")], cx); + buffer.edit([(0..buffer.len(), "if a:\n b(\n )")], None, cx); // dedent "else" on the line after a closing paren append(&mut buffer, "\n else:\n", cx); diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 856986b1eb..8e804f0c7f 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -444,29 +444,29 @@ mod tests { // indent between braces buffer.set_text("fn a() {}", cx); let ix = buffer.len() - 1; - buffer.edit_with_autoindent([(ix..ix, "\n\n")], cx); + buffer.edit([(ix..ix, "\n\n")], Some(AutoindentMode::Independent), cx); assert_eq!(buffer.text(), "fn a() {\n \n}"); // indent between braces, even after empty lines buffer.set_text("fn a() {\n\n\n}", cx); let ix = buffer.len() - 2; - buffer.edit_with_autoindent([(ix..ix, "\n")], cx); + buffer.edit([(ix..ix, "\n")], Some(AutoindentMode::Independent), cx); assert_eq!(buffer.text(), "fn a() {\n\n\n \n}"); // indent a line that continues a field expression buffer.set_text("fn a() {\n \n}", cx); let ix = buffer.len() - 2; - buffer.edit_with_autoindent([(ix..ix, "b\n.c")], cx); + buffer.edit([(ix..ix, "b\n.c")], Some(AutoindentMode::Independent), cx); assert_eq!(buffer.text(), "fn a() {\n b\n .c\n}"); // indent further lines that continue the field expression, even after empty lines let ix = buffer.len() - 2; - buffer.edit_with_autoindent([(ix..ix, "\n\n.d")], cx); + buffer.edit([(ix..ix, "\n\n.d")], Some(AutoindentMode::Independent), cx); assert_eq!(buffer.text(), "fn a() {\n b\n .c\n \n .d\n}"); // dedent the line after the field expression let ix = buffer.len() - 2; - buffer.edit_with_autoindent([(ix..ix, ";\ne")], cx); + buffer.edit([(ix..ix, ";\ne")], Some(AutoindentMode::Independent), cx); assert_eq!( buffer.text(), "fn a() {\n b\n .c\n \n .d;\n e\n}" @@ -475,17 +475,21 @@ mod tests { // indent inside a struct within a call buffer.set_text("const a: B = c(D {});", cx); let ix = buffer.len() - 3; - buffer.edit_with_autoindent([(ix..ix, "\n\n")], cx); + buffer.edit([(ix..ix, "\n\n")], Some(AutoindentMode::Independent), cx); assert_eq!(buffer.text(), "const a: B = c(D {\n \n});"); // indent further inside a nested call let ix = buffer.len() - 4; - buffer.edit_with_autoindent([(ix..ix, "e: f(\n\n)")], cx); + buffer.edit( + [(ix..ix, "e: f(\n\n)")], + Some(AutoindentMode::Independent), + cx, + ); assert_eq!(buffer.text(), "const a: B = c(D {\n e: f(\n \n )\n});"); // keep that indent after an empty line let ix = buffer.len() - 8; - buffer.edit_with_autoindent([(ix..ix, "\n")], cx); + buffer.edit([(ix..ix, "\n")], Some(AutoindentMode::Independent), cx); assert_eq!( buffer.text(), "const a: B = c(D {\n e: f(\n \n \n )\n});"