diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d1e19a20e9..0616815e0e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -106,8 +106,8 @@ use itertools::Itertools; use language::{ AutoindentMode, BracketMatch, BracketPair, Buffer, Capability, CharKind, CodeLabel, CursorShape, Diagnostic, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, - IndentKind, IndentSize, Language, OffsetRangeExt, Point, Selection, SelectionGoal, TextObject, - TransactionId, TreeSitterOptions, WordsQuery, + IndentKind, IndentSize, Language, OffsetRangeExt, OriginalIndentColumn, Point, Selection, + SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery, language_settings::{ self, InlayHintSettings, RewrapBehavior, WordsCompletionMode, all_language_settings, language_settings, @@ -1067,6 +1067,8 @@ pub struct ClipboardSelection { pub is_entire_line: bool, /// The indentation of the first line when this content was originally copied. pub first_line_indent: u32, + /// The indentation of the second line when this content was originally copied. + pub second_line_indent: u32, } // selections, scroll behavior, was newest selection reversed @@ -2572,7 +2574,7 @@ impl Editor { pub fn edit_with_block_indent( &mut self, edits: I, - original_indent_columns: Vec>, + original_indent_columns: Vec>, cx: &mut Context, ) where I: IntoIterator, T)>, @@ -9816,6 +9818,9 @@ impl Editor { first_line_indent: buffer .indent_size_for_line(MultiBufferRow(selection.start.row)) .len, + second_line_indent: buffer + .indent_size_for_line(MultiBufferRow(selection.start.row + 1)) + .len, }); } } @@ -9939,6 +9944,9 @@ impl Editor { first_line_indent: buffer .indent_size_for_line(MultiBufferRow(trimmed_range.start.row)) .len, + second_line_indent: buffer + .indent_size_for_line(MultiBufferRow(trimmed_range.start.row + 1)) + .len, }); } } @@ -9970,7 +9978,10 @@ impl Editor { let all_selections_were_entire_line = clipboard_selections.iter().all(|s| s.is_entire_line); let first_selection_indent_column = - clipboard_selections.first().map(|s| s.first_line_indent); + clipboard_selections.first().map(|s| OriginalIndentColumn { + first_line: s.first_line_indent, + second_line: s.second_line_indent, + }); if clipboard_selections.len() != old_selections.len() { clipboard_selections.drain(..); } @@ -9995,7 +10006,10 @@ impl Editor { to_insert = &clipboard_text[start_offset..end_offset]; entire_line = clipboard_selection.is_entire_line; start_offset = end_offset + 1; - original_indent_column = Some(clipboard_selection.first_line_indent); + original_indent_column = Some(OriginalIndentColumn { + first_line: clipboard_selection.first_line_indent, + second_line: clipboard_selection.second_line_indent, + }); } else { to_insert = clipboard_text.as_str(); entire_line = all_selections_were_entire_line; diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 8761d2adbc..54ba970cde 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -403,19 +403,26 @@ pub enum AutoindentMode { /// Apply the same indentation adjustment to all of the lines /// in a given insertion. Block { - /// The original indentation column of the first line of each + /// The original indentation column of the first and second line of each /// insertion, if it has been copied. /// /// Knowing this makes it possible to preserve the relative indentation /// of every line in the insertion from when it was copied. - /// - /// 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_indent_columns: Vec>, + original_indent_columns: Vec>, }, } +#[derive(Copy, Clone, Debug)] +pub struct OriginalIndentColumn { + /// If the original indent column first line 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` + pub first_line: u32, + /// In certain cases like '\n' where we don't want to edit the indentation of first line, + /// we use the second line as a reference to find auto-indentation. + pub second_line: u32, +} + #[derive(Clone)] struct AutoindentRequest { before_edit: BufferSnapshot, @@ -434,7 +441,7 @@ struct AutoindentRequestEntry { /// since the edit was made. first_line_is_new: bool, indent_size: IndentSize, - original_indent_column: Option, + original_indent: Option, } #[derive(Debug)] @@ -1616,7 +1623,7 @@ impl Buffer { let old_row = position.to_point(&request.before_edit).row; old_to_new_rows.insert(old_row, new_row); } - row_ranges.push((new_row..new_end_row, entry.original_indent_column)); + row_ranges.push((new_row..new_end_row, entry.original_indent)); } // Build a map containing the suggested indentation for each of the edited lines @@ -1668,7 +1675,7 @@ impl Buffer { // 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 (row_range, original_indent_column) in row_ranges { + for (row_range, original_indent) in row_ranges { let new_edited_row_range = if request.is_block_mode { row_range.start..row_range.start + 1 } else { @@ -1699,6 +1706,8 @@ impl Buffer { }) .with_delta(suggestion.delta, language_indent_size); + dbg!(&indent_sizes, &suggested_indent); + if old_suggestions.get(&new_row).map_or( true, |(old_indentation, was_within_error)| { @@ -1714,8 +1723,7 @@ impl Buffer { } } - if let (true, Some(original_indent_column)) = - (request.is_block_mode, original_indent_column) + if let (true, Some(original_indent)) = (request.is_block_mode, original_indent) { let new_indent = if let Some((indent, _)) = indent_sizes.get(&row_range.start) { @@ -1723,7 +1731,8 @@ impl Buffer { } else { snapshot.indent_size_for_line(row_range.start) }; - let delta = new_indent.len as i64 - original_indent_column as i64; + + let delta = new_indent.len as i64 - original_indent as i64; if delta != 0 { for row in row_range.skip(1) { indent_sizes.entry(row).or_insert_with(|| { @@ -2245,16 +2254,23 @@ impl Buffer { first_line_is_new = true; } - let mut original_indent_column = None; + let mut original_indent = None; if let AutoindentMode::Block { original_indent_columns, } = &mode { - original_indent_column = Some( + original_indent = Some( original_indent_columns .get(ix) .copied() .flatten() + .map(|original_indent_column| { + if new_text.starts_with('\n') { + original_indent_column.second_line + } else { + original_indent_column.first_line + } + }) .unwrap_or_else(|| { indent_size_for_text( new_text[range_of_insertion_to_indent.clone()].chars(), @@ -2271,7 +2287,7 @@ impl Buffer { AutoindentRequestEntry { first_line_is_new, - original_indent_column, + original_indent, indent_size: before_edit.language_indent_size_at(range.start, cx), range: self.anchor_before(new_start + range_of_insertion_to_indent.start) ..self.anchor_after(new_start + range_of_insertion_to_indent.end), @@ -2319,7 +2335,7 @@ impl Buffer { range: before_edit.anchor_before(range.start)..before_edit.anchor_after(range.end), first_line_is_new: true, indent_size: before_edit.language_indent_size_at(range.start, cx), - original_indent_column: None, + original_indent: None, }) .collect(); self.autoindent_requests.push(Arc::new(AutoindentRequest { diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 76316b2640..134f9df30e 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -1646,7 +1646,10 @@ 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![Some(4)]; + let original_indent_columns = vec![Some(OriginalIndentColumn { + first_line: 4, + second_line: 8, + })]; let inserted_text = r#" " c @@ -1825,7 +1828,20 @@ 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_indent_columns: vec![Some(0), Some(0), Some(0)], + original_indent_columns: vec![ + Some(OriginalIndentColumn { + first_line: 0, + second_line: 0, + }), + Some(OriginalIndentColumn { + first_line: 0, + second_line: 0, + }), + Some(OriginalIndentColumn { + first_line: 0, + second_line: 0, + }), + ], }), cx, ); diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index c2f2e84e10..1aa2c2fb78 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -18,9 +18,9 @@ use itertools::Itertools; use language::{ AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharClassifier, CharKind, Chunk, CursorShape, DiagnosticEntry, DiskState, File, IndentSize, Language, - LanguageScope, OffsetRangeExt, OffsetUtf16, Outline, OutlineItem, Point, PointUtf16, Selection, - TextDimension, TextObject, ToOffset as _, ToPoint as _, TransactionId, TreeSitterOptions, - Unclipped, + LanguageScope, OffsetRangeExt, OffsetUtf16, OriginalIndentColumn, Outline, OutlineItem, Point, + PointUtf16, Selection, TextDimension, TextObject, ToOffset as _, ToPoint as _, TransactionId, + TreeSitterOptions, Unclipped, language_settings::{IndentGuideSettings, LanguageSettings, language_settings}, }; @@ -535,7 +535,7 @@ struct BufferEdit { range: Range, new_text: Arc, is_insertion: bool, - original_indent_column: Option, + original_indent_column: Option, excerpt_id: ExcerptId, } @@ -898,7 +898,7 @@ impl MultiBuffer { &self, edits: Vec<(Range, Arc)>, snapshot: &MultiBufferSnapshot, - original_indent_columns: &[Option], + original_indent_columns: &[Option], ) -> (HashMap>, Vec) { let mut buffer_edits: HashMap> = Default::default(); let mut edited_excerpt_ids = Vec::new(); diff --git a/crates/vim/src/normal/paste.rs b/crates/vim/src/normal/paste.rs index 3f3e27c457..028b2725f8 100644 --- a/crates/vim/src/normal/paste.rs +++ b/crates/vim/src/normal/paste.rs @@ -1,6 +1,6 @@ use editor::{DisplayPoint, RowExt, display_map::ToDisplayPoint, movement, scroll::Autoscroll}; use gpui::{Context, Window, impl_actions}; -use language::{Bias, SelectionGoal}; +use language::{Bias, OriginalIndentColumn, SelectionGoal}; use schemars::JsonSchema; use serde::Deserialize; use std::cmp; @@ -85,7 +85,10 @@ impl Vim { clipboard_selections.as_ref().and_then(|zed_selections| { zed_selections .first() - .map(|selection| selection.first_line_indent) + .map(|selection| OriginalIndentColumn { + first_line: selection.first_line_indent, + second_line: selection.second_line_indent, + }) }); let before = action.before || vim.mode == Mode::VisualLine; @@ -101,7 +104,13 @@ impl Vim { 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.first_line_indent)) + ( + text, + Some(OriginalIndentColumn { + first_line: clipboard_selection.first_line_indent, + second_line: clipboard_selection.second_line_indent, + }), + ) } else { ("".to_string(), first_selection_indent_column) } diff --git a/crates/vim/src/normal/yank.rs b/crates/vim/src/normal/yank.rs index 0ec19f654b..b9c61795ec 100644 --- a/crates/vim/src/normal/yank.rs +++ b/crates/vim/src/normal/yank.rs @@ -185,6 +185,9 @@ impl Vim { len: text.len() - initial_len, is_entire_line: kind.linewise(), first_line_indent: buffer.indent_size_for_line(MultiBufferRow(start.row)).len, + second_line_indent: buffer + .indent_size_for_line(MultiBufferRow(start.row + 1)) + .len, }); } }