From 3a008101420fd7ba5db4cbef82f22b76d70f4222 Mon Sep 17 00:00:00 2001 From: Xin Zhao Date: Fri, 20 Feb 2026 15:38:16 +0800 Subject: [PATCH] editor: Prevent comment extension on REPL separators (#48174) Closes #47691 Release Notes: - N/A *or* Added/Fixed/Improved ... --- crates/editor/src/editor.rs | 31 ++++++++++++++++----- crates/editor/src/editor_tests.rs | 45 +++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index cba09fb543dbb9d17d36ab173510cc368660b9ca..3fa52ed21e68ac432c2a3922c85ed40b61f7621d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -25679,19 +25679,25 @@ fn comment_delimiter_for_newline( let comment_candidate = snapshot .chars_for_range(range.clone()) .skip(num_of_whitespaces) - .take(max_len_of_delimiter) + .take(max_len_of_delimiter + 2) .collect::(); - let (delimiter, trimmed_len) = delimiters + let (delimiter, trimmed_len, is_repl) = delimiters .iter() .filter_map(|delimiter| { let prefix = delimiter.trim_end(); if comment_candidate.starts_with(prefix) { - Some((delimiter, prefix.len())) + let is_repl = if let Some(stripped_comment) = comment_candidate.strip_prefix(prefix) + { + stripped_comment.starts_with(" %%") + } else { + false + }; + Some((delimiter, prefix.len(), is_repl)) } else { None } }) - .max_by_key(|(_, len)| *len)?; + .max_by_key(|(_, len, _)| *len)?; if let Some(BlockCommentConfig { start: block_start, .. @@ -25700,7 +25706,7 @@ fn comment_delimiter_for_newline( let block_start_trimmed = block_start.trim_end(); if block_start_trimmed.starts_with(delimiter.trim_end()) { let line_content = snapshot - .chars_for_range(range) + .chars_for_range(range.clone()) .skip(num_of_whitespaces) .take(block_start_trimmed.len()) .collect::(); @@ -25714,7 +25720,20 @@ fn comment_delimiter_for_newline( let cursor_is_placed_after_comment_marker = num_of_whitespaces + trimmed_len <= start_point.column as usize; if cursor_is_placed_after_comment_marker { - Some(delimiter.clone()) + if !is_repl { + return Some(delimiter.clone()); + } + + let line_content_after_cursor: String = snapshot + .chars_for_range(range) + .skip(start_point.column as usize) + .collect(); + + if line_content_after_cursor.trim().is_empty() { + return None; + } else { + return Some(delimiter.clone()); + } } else { None } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 18a29b2befc84a61d2345de8384c7ebe595df774..bc44038c05c7864966f1c9a012f171022134cdaa 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -3752,6 +3752,51 @@ async fn test_newline_comments_with_multiple_delimiters(cx: &mut TestAppContext) } } +#[gpui::test] +async fn test_newline_comments_repl_separators(cx: &mut TestAppContext) { + init_test(cx, |settings| { + settings.defaults.tab_size = NonZeroU32::new(4) + }); + let language = Arc::new(Language::new( + LanguageConfig { + line_comments: vec!["# ".into()], + ..LanguageConfig::default() + }, + None, + )); + + { + let mut cx = EditorTestContext::new(cx).await; + cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx)); + cx.set_state(indoc! {" + # %%ˇ + "}); + cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx)); + cx.assert_editor_state(indoc! {" + # %% + ˇ + "}); + + cx.set_state(indoc! {" + # %%%%%ˇ + "}); + cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx)); + cx.assert_editor_state(indoc! {" + # %%%%% + ˇ + "}); + + cx.set_state(indoc! {" + # %ˇ% + "}); + cx.update_editor(|e, window, cx| e.newline(&Newline, window, cx)); + cx.assert_editor_state(indoc! {" + # % + # ˇ% + "}); + } +} + #[gpui::test] async fn test_newline_documentation_comments(cx: &mut TestAppContext) { init_test(cx, |settings| {