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