diff --git a/Cargo.lock b/Cargo.lock index c8eea441fdb68316e6ebdf0d4ecfdbcdcc456848..4436594b8b0b1c660215538c7450657825795aaa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6230,6 +6230,7 @@ dependencies = [ "lsp", "parking_lot", "postage", + "pretty_assertions", "pulldown-cmark 0.12.1", "rand 0.8.5", "regex", diff --git a/crates/assistant/src/patch.rs b/crates/assistant/src/patch.rs index 8e6430f5349b10d1f6cb2f47cc2efd21d72370bb..13b719f5c60b27da464e1220fada46b7d2a9b372 100644 --- a/crates/assistant/src/patch.rs +++ b/crates/assistant/src/patch.rs @@ -717,7 +717,6 @@ mod tests { ); // Ensure InsertBefore merges correctly with Update of the same text - assert_edits( " fn foo() { @@ -782,6 +781,90 @@ mod tests { .unindent(), cx, ); + + // Correctly indent new text when replacing multiple adjacent indented blocks. + assert_edits( + " + impl Numbers { + fn one() { + 1 + } + + fn two() { + 2 + } + + fn three() { + 3 + } + } + " + .unindent(), + vec![ + AssistantEditKind::Update { + old_text: " + fn one() { + 1 + } + " + .unindent(), + new_text: " + fn one() { + 101 + } + " + .unindent(), + description: "pick better number".into(), + }, + AssistantEditKind::Update { + old_text: " + fn two() { + 2 + } + " + .unindent(), + new_text: " + fn two() { + 102 + } + " + .unindent(), + description: "pick better number".into(), + }, + AssistantEditKind::Update { + old_text: " + fn three() { + 3 + } + " + .unindent(), + new_text: " + fn three() { + 103 + } + " + .unindent(), + description: "pick better number".into(), + }, + ], + " + impl Numbers { + fn one() { + 101 + } + + fn two() { + 102 + } + + fn three() { + 103 + } + } + " + .unindent(), + cx, + ); } #[track_caller] diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index b117b9682bb11616cab8e70a8130abf26b1d5eca..41285d8222e1a3ad25f910460f55f2873b7bf091 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -71,6 +71,7 @@ env_logger.workspace = true gpui = { workspace = true, features = ["test-support"] } indoc.workspace = true lsp = { workspace = true, features = ["test-support"] } +pretty_assertions.workspace = true rand.workspace = true settings = { workspace = true, features = ["test-support"] } text = { workspace = true, features = ["test-support"] } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 13c07e014ceda8c9d3898922d6042f4109b3fe00..26fb620ac1498fff82630fb2d4f3abde5007623b 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -442,7 +442,7 @@ struct AutoindentRequest { is_block_mode: bool, } -#[derive(Clone)] +#[derive(Debug, Clone)] struct AutoindentRequestEntry { /// A range of the buffer whose indentation should be adjusted. range: Range, @@ -1420,24 +1420,17 @@ impl Buffer { yield_now().await; } - // In block mode, only compute indentation suggestions for the first line - // of each insertion. Otherwise, compute suggestions for every inserted line. - let new_edited_row_ranges = contiguous_ranges( - row_ranges.iter().flat_map(|(range, _)| { - if request.is_block_mode { - range.start..range.start + 1 - } else { - range.clone() - } - }), - max_rows_between_yields, - ); - // Compute new suggestions for each line, but only include them in the result // if they differ from the old suggestion for that line. let mut language_indent_sizes = language_indent_sizes_by_new_row.iter().peekable(); let mut language_indent_size = IndentSize::default(); - for new_edited_row_range in new_edited_row_ranges { + for (row_range, original_indent_column) in row_ranges { + let new_edited_row_range = if request.is_block_mode { + row_range.start..row_range.start + 1 + } else { + row_range.clone() + }; + let suggestions = snapshot .suggest_autoindents(new_edited_row_range.clone()) .into_iter() @@ -1471,22 +1464,9 @@ impl Buffer { } } } - yield_now().await; - } - // For each block of inserted text, adjust the indentation of the remaining - // lines of the block by the same amount as the first line was adjusted. - if request.is_block_mode { - for (row_range, original_indent_column) in - row_ranges - .into_iter() - .filter_map(|(range, original_indent_column)| { - if range.len() > 1 { - Some((range, original_indent_column?)) - } else { - None - } - }) + if let (true, Some(original_indent_column)) = + (request.is_block_mode, original_indent_column) { let new_indent = indent_sizes .get(&row_range.start) @@ -1511,6 +1491,8 @@ impl Buffer { } } } + + yield_now().await; } } diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index be6fb4f631f2d139dd36e7f199824ae012518323..9d2385e919139a173791eddb7c5c24f72b8a115a 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -1658,6 +1658,69 @@ fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContex }); } +#[gpui::test] +fn test_autoindent_block_mode_multiple_adjacent_ranges(cx: &mut AppContext) { + init_settings(cx, |_| {}); + + cx.new_model(|cx| { + let (text, ranges_to_replace) = marked_text_ranges( + &" + mod numbers { + «fn one() { + 1 + } + » + «fn two() { + 2 + } + » + «fn three() { + 3 + } + »} + " + .unindent(), + false, + ); + + let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx); + + buffer.edit( + [ + (ranges_to_replace[0].clone(), "fn one() {\n 101\n}\n"), + (ranges_to_replace[1].clone(), "fn two() {\n 102\n}\n"), + (ranges_to_replace[2].clone(), "fn three() {\n 103\n}\n"), + ], + Some(AutoindentMode::Block { + original_indent_columns: vec![0, 0, 0], + }), + cx, + ); + + pretty_assertions::assert_eq!( + buffer.text(), + " + mod numbers { + fn one() { + 101 + } + + fn two() { + 102 + } + + fn three() { + 103 + } + } + " + .unindent() + ); + + buffer + }); +} + #[gpui::test] fn test_autoindent_language_without_indents_query(cx: &mut AppContext) { init_settings(cx, |_| {});