Detailed changes
@@ -108,6 +108,8 @@
// Whether or not to remove any trailing whitespace from lines of a buffer
// before saving it.
"remove_trailing_whitespace_on_save": true,
+ // Whether to start a new line with a comment when a previous line is a comment as well.
+ "extend_comment_on_newline": true,
// Whether or not to ensure there's a single newline at the end of a buffer
// when saving it.
"ensure_final_newline_on_save": true,
@@ -2169,8 +2169,8 @@ impl Editor {
self.transact(cx, |this, cx| {
let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
let selections = this.selections.all::<usize>(cx);
-
- let buffer = this.buffer.read(cx).snapshot(cx);
+ let multi_buffer = this.buffer.read(cx);
+ let buffer = multi_buffer.snapshot(cx);
selections
.iter()
.map(|selection| {
@@ -2181,70 +2181,74 @@ impl Editor {
let end = selection.end;
let is_cursor = start == end;
let language_scope = buffer.language_scope_at(start);
- let (comment_delimiter, insert_extra_newline) =
- if let Some(language) = &language_scope {
- let leading_whitespace_len = buffer
- .reversed_chars_at(start)
- .take_while(|c| c.is_whitespace() && *c != '\n')
- .map(|c| c.len_utf8())
- .sum::<usize>();
-
- let trailing_whitespace_len = buffer
- .chars_at(end)
- .take_while(|c| c.is_whitespace() && *c != '\n')
- .map(|c| c.len_utf8())
- .sum::<usize>();
-
- let insert_extra_newline =
- language.brackets().any(|(pair, enabled)| {
- let pair_start = pair.start.trim_end();
- let pair_end = pair.end.trim_start();
-
- enabled
- && pair.newline
- && buffer.contains_str_at(
- end + trailing_whitespace_len,
- pair_end,
- )
- && buffer.contains_str_at(
- (start - leading_whitespace_len)
- .saturating_sub(pair_start.len()),
- pair_start,
- )
- });
- // Comment extension on newline is allowed only for cursor selections
- let comment_delimiter =
- language.line_comment_prefix().filter(|_| is_cursor);
- let comment_delimiter = if let Some(delimiter) = comment_delimiter {
- buffer
- .buffer_line_for_row(start_point.row)
- .is_some_and(|(snapshot, range)| {
- let mut index_of_first_non_whitespace = 0;
- let line_starts_with_comment = snapshot
- .chars_for_range(range)
- .skip_while(|c| {
- let should_skip = c.is_whitespace();
- if should_skip {
- index_of_first_non_whitespace += 1;
- }
- should_skip
- })
- .take(delimiter.len())
- .eq(delimiter.chars());
- let cursor_is_placed_after_comment_marker =
- index_of_first_non_whitespace + delimiter.len()
- <= start_point.column as usize;
- line_starts_with_comment
- && cursor_is_placed_after_comment_marker
- })
- .then(|| delimiter.clone())
- } else {
- None
- };
- (comment_delimiter, insert_extra_newline)
+ let (comment_delimiter, insert_extra_newline) = if let Some(language) =
+ &language_scope
+ {
+ let leading_whitespace_len = buffer
+ .reversed_chars_at(start)
+ .take_while(|c| c.is_whitespace() && *c != '\n')
+ .map(|c| c.len_utf8())
+ .sum::<usize>();
+
+ let trailing_whitespace_len = buffer
+ .chars_at(end)
+ .take_while(|c| c.is_whitespace() && *c != '\n')
+ .map(|c| c.len_utf8())
+ .sum::<usize>();
+
+ let insert_extra_newline =
+ language.brackets().any(|(pair, enabled)| {
+ let pair_start = pair.start.trim_end();
+ let pair_end = pair.end.trim_start();
+
+ enabled
+ && pair.newline
+ && buffer.contains_str_at(
+ end + trailing_whitespace_len,
+ pair_end,
+ )
+ && buffer.contains_str_at(
+ (start - leading_whitespace_len)
+ .saturating_sub(pair_start.len()),
+ pair_start,
+ )
+ });
+ // Comment extension on newline is allowed only for cursor selections
+ let comment_delimiter = language.line_comment_prefix().filter(|_| {
+ let is_comment_extension_enabled =
+ multi_buffer.settings_at(0, cx).extend_comment_on_newline;
+ is_cursor && is_comment_extension_enabled
+ });
+ let comment_delimiter = if let Some(delimiter) = comment_delimiter {
+ buffer
+ .buffer_line_for_row(start_point.row)
+ .is_some_and(|(snapshot, range)| {
+ let mut index_of_first_non_whitespace = 0;
+ let line_starts_with_comment = snapshot
+ .chars_for_range(range)
+ .skip_while(|c| {
+ let should_skip = c.is_whitespace();
+ if should_skip {
+ index_of_first_non_whitespace += 1;
+ }
+ should_skip
+ })
+ .take(delimiter.len())
+ .eq(delimiter.chars());
+ let cursor_is_placed_after_comment_marker =
+ index_of_first_non_whitespace + delimiter.len()
+ <= start_point.column as usize;
+ line_starts_with_comment
+ && cursor_is_placed_after_comment_marker
+ })
+ .then(|| delimiter.clone())
} else {
- (None, false)
+ None
};
+ (comment_delimiter, insert_extra_newline)
+ } else {
+ (None, false)
+ };
let capacity_for_delimiter = comment_delimiter
.as_deref()
@@ -1732,27 +1732,41 @@ async fn test_newline_comments(cx: &mut gpui::TestAppContext) {
},
None,
));
-
- let mut cx = EditorTestContext::new(cx).await;
- cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
- cx.set_state(indoc! {"
+ {
+ let mut cx = EditorTestContext::new(cx).await;
+ cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
+ cx.set_state(indoc! {"
// Fooˇ
"});
- cx.update_editor(|e, cx| e.newline(&Newline, cx));
- cx.assert_editor_state(indoc! {"
+ cx.update_editor(|e, cx| e.newline(&Newline, cx));
+ cx.assert_editor_state(indoc! {"
// Foo
//ˇ
"});
- // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
- cx.set_state(indoc! {"
+ // Ensure that if cursor is before the comment start, we do not actually insert a comment prefix.
+ cx.set_state(indoc! {"
ˇ// Foo
"});
- cx.update_editor(|e, cx| e.newline(&Newline, cx));
- cx.assert_editor_state(indoc! {"
+ cx.update_editor(|e, cx| e.newline(&Newline, cx));
+ cx.assert_editor_state(indoc! {"
ˇ// Foo
"});
+ }
+ // Ensure that comment continuations can be disabled.
+ update_test_settings(cx, |settings| {
+ settings.defaults.extend_comment_on_newline = Some(false);
+ });
+ let mut cx = EditorTestContext::new(cx).await;
+ cx.set_state(indoc! {"
+ // Fooˇ
+ "});
+ cx.update_editor(|e, cx| e.newline(&Newline, cx));
+ cx.assert_editor_state(indoc! {"
+ // Foo
+ ˇ
+ "});
}
#[gpui::test]
@@ -51,6 +51,7 @@ pub struct LanguageSettings {
pub enable_language_server: bool,
pub show_copilot_suggestions: bool,
pub show_whitespaces: ShowWhitespaceSetting,
+ pub extend_comment_on_newline: bool,
}
#[derive(Clone, Debug, Default)]
@@ -95,6 +96,8 @@ pub struct LanguageSettingsContent {
pub show_copilot_suggestions: Option<bool>,
#[serde(default)]
pub show_whitespaces: Option<ShowWhitespaceSetting>,
+ #[serde(default)]
+ pub extend_comment_on_newline: Option<bool>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
@@ -340,7 +343,10 @@ fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent
src.show_copilot_suggestions,
);
merge(&mut settings.show_whitespaces, src.show_whitespaces);
-
+ merge(
+ &mut settings.extend_comment_on_newline,
+ src.extend_comment_on_newline,
+ );
fn merge<T>(target: &mut T, value: Option<T>) {
if let Some(value) = value {
*target = value;