From 0ec15d6b02f4c0775c11e0f5dc3be6959ce7a75a Mon Sep 17 00:00:00 2001 From: Alex Ozer Date: Tue, 4 Mar 2025 10:55:27 -0500 Subject: [PATCH] Fix soft_wrap setting not applying to buffers starting with a different language (#25880) Closes #22999 # Problem Currently, the default soft wrap mode of an editor is determined by reading the language-specific settings of the language _at offset zero_ in the editor's (multi)buffer. While this provides a way to pick a single soft wrap mode for a multi-language multibuffer, it's a bad choice for a single-buffer multibuffer that begins with a different embedded language. For example, Markdown with frontmatter: ```markdown --- my_front_matter --- # Hello World ``` Setting this in config: ```json "languages": { "Markdown": { "soft_wrap": "bounded" } }, ``` Will not soft wrap the Markdown file as the language at offset zero is YAML. # Solution Instead of using the language at offset zero, use the language of the first buffer in the multibuffer (the buffer at offset zero). This gives better behavior for single-buffer editors, and a similar default for multi-language multibuffers as before. # Testing All existing `editor` crate tests pass, but I would appreciate any guidance for where best to add additional testing. Release Notes: - Fixed soft_wrap setting not applying to buffers starting with a different language --------- Co-authored-by: Kirill Bulatov --- crates/editor/src/editor.rs | 45 ++++++++++++++----------- crates/editor/src/element.rs | 2 +- crates/multi_buffer/src/multi_buffer.rs | 40 ++++++++++++++++++++-- crates/vim/src/normal/paste.rs | 2 +- 4 files changed, 64 insertions(+), 25 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8ce48e036c7284c21d4127444cdbfd7be7dec0b3..b1887c45e3881dba01f85867675cdbdc1ea5eb07 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2877,7 +2877,7 @@ impl Editor { } if let Some(bracket_pair) = bracket_pair { - let snapshot_settings = snapshot.settings_at(selection.start, cx); + let snapshot_settings = snapshot.language_settings_at(selection.start, cx); let autoclose = self.use_autoclose && snapshot_settings.use_autoclose; let auto_surround = self.use_auto_surround && snapshot_settings.use_auto_surround; @@ -2942,7 +2942,7 @@ impl Editor { } let always_treat_brackets_as_autoclosed = snapshot - .settings_at(selection.start, cx) + .language_settings_at(selection.start, cx) .always_treat_brackets_as_autoclosed; if always_treat_brackets_as_autoclosed && is_bracket_pair_end @@ -3225,7 +3225,7 @@ impl Editor { return None; } - if !multi_buffer.settings_at(0, cx).extend_comment_on_newline { + if !multi_buffer.language_settings(cx).extend_comment_on_newline { return None; } @@ -3554,7 +3554,7 @@ impl Editor { } let always_treat_brackets_as_autoclosed = buffer - .settings_at(selection.start, cx) + .language_settings_at(selection.start, cx) .always_treat_brackets_as_autoclosed; if !always_treat_brackets_as_autoclosed { @@ -7328,7 +7328,7 @@ impl Editor { } // Otherwise, insert a hard or soft tab. - let settings = buffer.settings_at(cursor, cx); + let settings = buffer.language_settings_at(cursor, cx); let tab_size = if settings.hard_tabs { IndentSize::tab() } else { @@ -7392,7 +7392,7 @@ impl Editor { delta_for_start_row: u32, cx: &App, ) -> u32 { - let settings = buffer.settings_at(selection.start, cx); + let settings = buffer.language_settings_at(selection.start, cx); let tab_size = settings.tab_size.get(); let indent_kind = if settings.hard_tabs { IndentKind::Tab @@ -7472,7 +7472,7 @@ impl Editor { let buffer = self.buffer.read(cx); let snapshot = buffer.snapshot(cx); for selection in &selections { - let settings = buffer.settings_at(selection.start, cx); + let settings = buffer.language_settings_at(selection.start, cx); let tab_size = settings.tab_size.get(); let mut rows = selection.spanned_rows(false, &display_map); @@ -8484,7 +8484,7 @@ impl Editor { continue; } - let tab_size = buffer.settings_at(selection.head(), cx).tab_size; + let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size; // Since not all lines in the selection may be at the same indent // level, choose the indent size that is the most common between all @@ -8530,7 +8530,7 @@ impl Editor { inside_comment = true; } - let language_settings = buffer.settings_at(selection.head(), cx); + let language_settings = buffer.language_settings_at(selection.head(), cx); let allow_rewrap_based_on_language = match language_settings.allow_rewrap { RewrapBehavior::InComments => inside_comment, RewrapBehavior::InSelections => !selection.is_empty(), @@ -8586,7 +8586,7 @@ impl Editor { }; let wrap_column = buffer - .settings_at(Point::new(start_row, 0), cx) + .language_settings_at(Point::new(start_row, 0), cx) .preferred_line_length as usize; let wrapped_text = wrap_with_prefix( line_prefix, @@ -8772,8 +8772,9 @@ impl Editor { this.buffer.update(cx, |buffer, cx| { let snapshot = buffer.read(cx); - auto_indent_on_paste = - snapshot.settings_at(cursor_offset, cx).auto_indent_on_paste; + auto_indent_on_paste = snapshot + .language_settings_at(cursor_offset, cx) + .auto_indent_on_paste; let mut start_offset = 0; let mut edits = Vec::new(); @@ -14082,12 +14083,16 @@ impl Editor { return wrap_guides; } - let settings = self.buffer.read(cx).settings_at(0, cx); + let settings = self.buffer.read(cx).language_settings(cx); if settings.show_wrap_guides { - if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) { - wrap_guides.push((soft_wrap as usize, true)); - } else if let SoftWrap::Bounded(soft_wrap) = self.soft_wrap_mode(cx) { - wrap_guides.push((soft_wrap as usize, true)); + match self.soft_wrap_mode(cx) { + SoftWrap::Column(soft_wrap) => { + wrap_guides.push((soft_wrap as usize, true)); + } + SoftWrap::Bounded(soft_wrap) => { + wrap_guides.push((soft_wrap as usize, true)); + } + SoftWrap::GitDiff | SoftWrap::None | SoftWrap::EditorWidth => {} } wrap_guides.extend(settings.wrap_guides.iter().map(|guide| (*guide, false))) } @@ -14096,7 +14101,7 @@ impl Editor { } pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap { - let settings = self.buffer.read(cx).settings_at(0, cx); + let settings = self.buffer.read(cx).language_settings(cx); let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap); match mode { language_settings::SoftWrap::PreferLine | language_settings::SoftWrap::None => { @@ -14195,7 +14200,7 @@ impl Editor { let currently_enabled = self.should_show_indent_guides().unwrap_or_else(|| { self.buffer .read(cx) - .settings_at(0, cx) + .language_settings(cx) .indent_guides .enabled }); @@ -15844,7 +15849,7 @@ impl Editor { let copilot_enabled_for_language = self .buffer .read(cx) - .settings_at(0, cx) + .language_settings(cx) .show_edit_predictions; let project = project.read(cx); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 41e563cc19748cb718130d788785b42fe9253002..4cf2c7a75d5de7b64062e7482de218f6f307d397 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -4685,7 +4685,7 @@ impl EditorElement { .read(cx) .buffer .read(cx) - .settings_at(0, cx) + .language_settings(cx) .show_whitespaces; for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() { diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 964342ba1bd73d143139b5cc0f1e9248af02cc07..a7350736b910cc0cb07c8c0b0694041e495fb81e 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -2307,7 +2307,27 @@ impl MultiBuffer { .and_then(|(buffer, offset)| buffer.read(cx).language_at(offset)) } - pub fn settings_at<'a, T: ToOffset>(&self, point: T, cx: &'a App) -> Cow<'a, LanguageSettings> { + pub fn language_settings<'a>(&'a self, cx: &'a App) -> Cow<'a, LanguageSettings> { + let buffer_id = self + .snapshot + .borrow() + .excerpts + .first() + .map(|excerpt| excerpt.buffer.remote_id()); + buffer_id + .and_then(|buffer_id| self.buffer(buffer_id)) + .map(|buffer| { + let buffer = buffer.read(cx); + language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx) + }) + .unwrap_or_else(move || self.language_settings_at(0, cx)) + } + + pub fn language_settings_at<'a, T: ToOffset>( + &'a self, + point: T, + cx: &'a App, + ) -> Cow<'a, LanguageSettings> { let mut language = None; let mut file = None; if let Some((buffer, offset)) = self.point_to_buffer_offset(point, cx) { @@ -4259,7 +4279,7 @@ impl MultiBufferSnapshot { pub fn indent_and_comment_for_line(&self, row: MultiBufferRow, cx: &App) -> String { let mut indent = self.indent_size_for_line(row).chars().collect::(); - if self.settings_at(0, cx).extend_comment_on_newline { + if self.language_settings(cx).extend_comment_on_newline { if let Some(language_scope) = self.language_scope_at(Point::new(row.0, 0)) { let delimiters = language_scope.line_comment_prefixes(); for delimiter in delimiters { @@ -5592,7 +5612,21 @@ impl MultiBufferSnapshot { .and_then(|(buffer, offset)| buffer.language_at(offset)) } - pub fn settings_at<'a, T: ToOffset>( + fn language_settings<'a>(&'a self, cx: &'a App) -> Cow<'a, LanguageSettings> { + self.excerpts + .first() + .map(|excerpt| &excerpt.buffer) + .map(|buffer| { + language_settings( + buffer.language().map(|language| language.name()), + buffer.file(), + cx, + ) + }) + .unwrap_or_else(move || self.language_settings_at(0, cx)) + } + + pub fn language_settings_at<'a, T: ToOffset>( &'a self, point: T, cx: &'a App, diff --git a/crates/vim/src/normal/paste.rs b/crates/vim/src/normal/paste.rs index a3fc7a960ef508f325d9cec4ea3b4bc38288b35e..7050d5c60438bf954f8c44897ca6caa6757c454b 100644 --- a/crates/vim/src/normal/paste.rs +++ b/crates/vim/src/normal/paste.rs @@ -160,7 +160,7 @@ impl Vim { .buffer() .read(cx) .snapshot(cx) - .settings_at(cursor_offset, cx) + .language_settings_at(cursor_offset, cx) .auto_indent_on_paste { editor.edit_with_block_indent(edits, original_start_columns, cx);