diff --git a/crates/assistant_context_editor/src/patch.rs b/crates/assistant_context_editor/src/patch.rs index ca652655f8..e46c420fc5 100644 --- a/crates/assistant_context_editor/src/patch.rs +++ b/crates/assistant_context_editor/src/patch.rs @@ -140,7 +140,7 @@ impl ResolvedPatch { buffer.edit( edits, Some(AutoindentMode::Block { - original_start_columns: Vec::new(), + original_indent_columns: Vec::new(), }), cx, ); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 346fc08bf0..2ad253f40e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1012,8 +1012,8 @@ pub struct ClipboardSelection { pub len: usize, /// Whether this was a full-line selection. pub is_entire_line: bool, - /// The column where this selection originally started. - pub start_column: u32, + /// The indentation of the first line when this content was originally copied. + pub first_line_indent: u32, } #[derive(Debug)] @@ -2354,7 +2354,7 @@ impl Editor { pub fn edit_with_block_indent( &mut self, edits: I, - original_start_columns: Vec, + original_indent_columns: Vec>, cx: &mut Context, ) where I: IntoIterator, T)>, @@ -2369,7 +2369,7 @@ impl Editor { buffer.edit( edits, Some(AutoindentMode::Block { - original_start_columns, + original_indent_columns, }), cx, ) @@ -3480,7 +3480,7 @@ impl Editor { pub fn insert(&mut self, text: &str, window: &mut Window, cx: &mut Context) { let autoindent = text.is_empty().not().then(|| AutoindentMode::Block { - original_start_columns: Vec::new(), + original_indent_columns: Vec::new(), }); self.insert_with_autoindent_mode(text, autoindent, window, cx); } @@ -8704,7 +8704,9 @@ impl Editor { clipboard_selections.push(ClipboardSelection { len, is_entire_line, - start_column: selection.start.column, + first_line_indent: buffer + .indent_size_for_line(MultiBufferRow(selection.start.row)) + .len, }); } } @@ -8783,7 +8785,7 @@ impl Editor { clipboard_selections.push(ClipboardSelection { len, is_entire_line, - start_column: start.column, + first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len, }); } } @@ -8813,8 +8815,8 @@ impl Editor { let old_selections = this.selections.all::(cx); let all_selections_were_entire_line = clipboard_selections.iter().all(|s| s.is_entire_line); - let first_selection_start_column = - clipboard_selections.first().map(|s| s.start_column); + let first_selection_indent_column = + clipboard_selections.first().map(|s| s.first_line_indent); if clipboard_selections.len() != old_selections.len() { clipboard_selections.drain(..); } @@ -8829,21 +8831,21 @@ impl Editor { let mut start_offset = 0; let mut edits = Vec::new(); - let mut original_start_columns = Vec::new(); + let mut original_indent_columns = Vec::new(); for (ix, selection) in old_selections.iter().enumerate() { let to_insert; let entire_line; - let original_start_column; + let original_indent_column; if let Some(clipboard_selection) = clipboard_selections.get(ix) { let end_offset = start_offset + clipboard_selection.len; to_insert = &clipboard_text[start_offset..end_offset]; entire_line = clipboard_selection.is_entire_line; start_offset = end_offset + 1; - original_start_column = Some(clipboard_selection.start_column); + original_indent_column = Some(clipboard_selection.first_line_indent); } else { to_insert = clipboard_text.as_str(); entire_line = all_selections_were_entire_line; - original_start_column = first_selection_start_column + original_indent_column = first_selection_indent_column } // If the corresponding selection was empty when this slice of the @@ -8859,7 +8861,7 @@ impl Editor { }; edits.push((range, to_insert)); - original_start_columns.extend(original_start_column); + original_indent_columns.push(original_indent_column); } drop(snapshot); @@ -8867,7 +8869,7 @@ impl Editor { edits, if auto_indent_on_paste { Some(AutoindentMode::Block { - original_start_columns, + original_indent_columns, }) } else { None diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 214fda66a8..02da8d38ff 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -4931,6 +4931,34 @@ async fn test_paste_multiline(cx: &mut TestAppContext) { ) ); "}); + + // Copy an indented block, starting mid-line + cx.set_state(indoc! {" + const a: B = ( + c(), + somethin«g( + e, + f + )ˇ» + ); + "}); + cx.update_editor(|e, window, cx| e.copy(&Copy, window, cx)); + + // Paste it on a line with a lower indent level + cx.update_editor(|e, window, cx| e.move_to_end(&Default::default(), window, cx)); + cx.update_editor(|e, window, cx| e.paste(&Paste, window, cx)); + cx.assert_editor_state(indoc! {" + const a: B = ( + c(), + something( + e, + f + ) + ); + g( + e, + f + )ˇ"}); } #[gpui::test] diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 9c1279e5fe..907b594ec5 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -401,17 +401,16 @@ pub enum AutoindentMode { /// Apply the same indentation adjustment to all of the lines /// in a given insertion. Block { - /// The original start column of each insertion, if it was - /// copied from elsewhere. + /// The original indentation column of the first line of each + /// insertion, if it has been copied. /// - /// Knowing this start column makes it possible to preserve the - /// relative indentation of every line in the insertion from - /// when it was copied. + /// Knowing this makes it possible to preserve the relative indentation + /// of every line in the insertion from when it was copied. /// - /// If the start column is `a`, and the first line of insertion + /// If the original indent column is `a`, and the first line of insertion /// is then auto-indented to column `b`, then every other line of /// the insertion will be auto-indented to column `b - a` - original_start_columns: Vec, + original_indent_columns: Vec>, }, } @@ -2206,15 +2205,20 @@ impl Buffer { let mut original_indent_column = None; if let AutoindentMode::Block { - original_start_columns, + original_indent_columns, } = &mode { original_indent_column = Some( - original_start_columns.get(ix).copied().unwrap_or(0) - + indent_size_for_text( - new_text[range_of_insertion_to_indent.clone()].chars(), - ) - .len, + original_indent_columns + .get(ix) + .copied() + .flatten() + .unwrap_or_else(|| { + indent_size_for_text( + new_text[range_of_insertion_to_indent.clone()].chars(), + ) + .len + }), ); // Avoid auto-indenting the line after the edit. diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index d7cd2659b3..eef74b4cee 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -1643,7 +1643,7 @@ fn test_autoindent_block_mode(cx: &mut App) { // indent level, but the indentation of the first line was not included in // the copied text. This information is retained in the // 'original_indent_columns' vector. - let original_indent_columns = vec![4]; + let original_indent_columns = vec![Some(4)]; let inserted_text = r#" " c @@ -1658,7 +1658,7 @@ fn test_autoindent_block_mode(cx: &mut App) { buffer.edit( [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())], Some(AutoindentMode::Block { - original_start_columns: original_indent_columns.clone(), + original_indent_columns: original_indent_columns.clone(), }), cx, ); @@ -1686,7 +1686,7 @@ fn test_autoindent_block_mode(cx: &mut App) { buffer.edit( [(Point::new(2, 8)..Point::new(2, 8), inserted_text)], Some(AutoindentMode::Block { - original_start_columns: original_indent_columns.clone(), + original_indent_columns: original_indent_columns.clone(), }), cx, ); @@ -1735,7 +1735,7 @@ fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut App) { buffer.edit( [(Point::new(2, 0)..Point::new(2, 0), inserted_text)], Some(AutoindentMode::Block { - original_start_columns: original_indent_columns.clone(), + original_indent_columns: original_indent_columns.clone(), }), cx, ); @@ -1766,7 +1766,7 @@ fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut App) { buffer.edit( [(Point::new(2, 12)..Point::new(2, 12), inserted_text)], Some(AutoindentMode::Block { - original_start_columns: Vec::new(), + original_indent_columns: Vec::new(), }), cx, ); @@ -1822,7 +1822,7 @@ fn test_autoindent_block_mode_multiple_adjacent_ranges(cx: &mut App) { (ranges_to_replace[2].clone(), "fn three() {\n 103\n}\n"), ], Some(AutoindentMode::Block { - original_start_columns: vec![0, 0, 0], + original_indent_columns: vec![Some(0), Some(0), Some(0)], }), cx, ); diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 8f15be2308..16dbd3330e 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -495,7 +495,7 @@ struct BufferEdit { range: Range, new_text: Arc, is_insertion: bool, - original_start_column: u32, + original_indent_column: Option, excerpt_id: ExcerptId, } @@ -751,15 +751,15 @@ impl MultiBuffer { return; } - let original_start_columns = match &mut autoindent_mode { + let original_indent_columns = match &mut autoindent_mode { Some(AutoindentMode::Block { - original_start_columns, - }) => mem::take(original_start_columns), + original_indent_columns, + }) => mem::take(original_indent_columns), _ => Default::default(), }; let (buffer_edits, edited_excerpt_ids) = - this.convert_edits_to_buffer_edits(edits, &snapshot, &original_start_columns); + this.convert_edits_to_buffer_edits(edits, &snapshot, &original_indent_columns); drop(snapshot); let mut buffer_ids = Vec::new(); @@ -778,7 +778,7 @@ impl MultiBuffer { mut range, mut new_text, mut is_insertion, - original_start_column: original_indent_column, + original_indent_column, excerpt_id, }) = edits.next() { @@ -821,7 +821,7 @@ impl MultiBuffer { let deletion_autoindent_mode = if let Some(AutoindentMode::Block { .. }) = autoindent_mode { Some(AutoindentMode::Block { - original_start_columns: Default::default(), + original_indent_columns: Default::default(), }) } else { autoindent_mode.clone() @@ -829,7 +829,7 @@ impl MultiBuffer { let insertion_autoindent_mode = if let Some(AutoindentMode::Block { .. }) = autoindent_mode { Some(AutoindentMode::Block { - original_start_columns: original_indent_columns, + original_indent_columns, }) } else { autoindent_mode.clone() @@ -851,13 +851,13 @@ impl MultiBuffer { &self, edits: Vec<(Range, Arc)>, snapshot: &MultiBufferSnapshot, - original_start_columns: &[u32], + original_indent_columns: &[Option], ) -> (HashMap>, Vec) { let mut buffer_edits: HashMap> = Default::default(); let mut edited_excerpt_ids = Vec::new(); let mut cursor = snapshot.cursor::(); for (ix, (range, new_text)) in edits.into_iter().enumerate() { - let original_start_column = original_start_columns.get(ix).copied().unwrap_or(0); + let original_indent_column = original_indent_columns.get(ix).copied().flatten(); cursor.seek(&range.start); let mut start_region = cursor.region().expect("start offset out of bounds"); @@ -908,7 +908,7 @@ impl MultiBuffer { range: buffer_start..buffer_end, new_text, is_insertion: true, - original_start_column, + original_indent_column, excerpt_id: start_region.excerpt.id, }); } @@ -924,7 +924,7 @@ impl MultiBuffer { range: start_excerpt_range, new_text: new_text.clone(), is_insertion: true, - original_start_column, + original_indent_column, excerpt_id: start_region.excerpt.id, }); } @@ -937,7 +937,7 @@ impl MultiBuffer { range: end_excerpt_range, new_text: new_text.clone(), is_insertion: false, - original_start_column, + original_indent_column, excerpt_id: end_region.excerpt.id, }); } @@ -957,7 +957,7 @@ impl MultiBuffer { range: region.buffer_range, new_text: new_text.clone(), is_insertion: false, - original_start_column, + original_indent_column, excerpt_id: region.excerpt.id, }); } diff --git a/crates/vim/src/normal/paste.rs b/crates/vim/src/normal/paste.rs index 7050d5c604..7dd5c93de3 100644 --- a/crates/vim/src/normal/paste.rs +++ b/crates/vim/src/normal/paste.rs @@ -81,32 +81,32 @@ impl Vim { } } - let first_selection_start_column = + let first_selection_indent_column = clipboard_selections.as_ref().and_then(|zed_selections| { zed_selections .first() - .map(|selection| selection.start_column) + .map(|selection| selection.first_line_indent) }); let before = action.before || vim.mode == Mode::VisualLine; let mut edits = Vec::new(); let mut new_selections = Vec::new(); - let mut original_start_columns = Vec::new(); + let mut original_indent_columns = Vec::new(); let mut start_offset = 0; for (ix, (selection, preserve)) in selections_to_process.iter().enumerate() { - let (mut to_insert, original_start_column) = + let (mut to_insert, original_indent_column) = if let Some(clipboard_selections) = &clipboard_selections { if let Some(clipboard_selection) = clipboard_selections.get(ix) { let end_offset = start_offset + clipboard_selection.len; let text = text[start_offset..end_offset].to_string(); start_offset = end_offset + 1; - (text, Some(clipboard_selection.start_column)) + (text, Some(clipboard_selection.first_line_indent)) } else { - ("".to_string(), first_selection_start_column) + ("".to_string(), first_selection_indent_column) } } else { - (text.to_string(), first_selection_start_column) + (text.to_string(), first_selection_indent_column) }; let line_mode = to_insert.ends_with('\n'); let is_multiline = to_insert.contains('\n'); @@ -152,7 +152,7 @@ impl Vim { new_selections.push((anchor, line_mode, is_multiline)); } edits.push((point_range, to_insert.repeat(count))); - original_start_columns.extend(original_start_column); + original_indent_columns.push(original_indent_column); } let cursor_offset = editor.selections.last::(cx).head(); @@ -163,7 +163,7 @@ impl Vim { .language_settings_at(cursor_offset, cx) .auto_indent_on_paste { - editor.edit_with_block_indent(edits, original_start_columns, cx); + editor.edit_with_block_indent(edits, original_indent_columns, cx); } else { editor.edit(edits, cx); } diff --git a/crates/vim/src/normal/yank.rs b/crates/vim/src/normal/yank.rs index 09551a35cd..e00fb8f8f4 100644 --- a/crates/vim/src/normal/yank.rs +++ b/crates/vim/src/normal/yank.rs @@ -188,7 +188,7 @@ impl Vim { clipboard_selections.push(ClipboardSelection { len: text.len() - initial_len, is_entire_line: linewise, - start_column: start.column, + first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len, }); } }