From f60a60b19450661d135863a2fee4be8c29bd8506 Mon Sep 17 00:00:00 2001 From: Clayton Carter Date: Sun, 13 Jul 2025 05:45:25 -0400 Subject: [PATCH 1/4] feat: support rewrapping in block comments Closes #19794 Closes #18221 --- crates/editor/src/editor.rs | 22 ++++-- crates/editor/src/editor_tests.rs | 112 ++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 5 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index cbee9021ed..f1516e30d2 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -11752,11 +11752,23 @@ impl Editor { let (comment_prefix, rewrap_prefix) = if let Some(language_scope) = &language_scope { let indent_end = Point::new(row, indent.len); - let comment_prefix = language_scope - .line_comment_prefixes() - .iter() - .find(|prefix| buffer.contains_str_at(indent_end, prefix)) - .map(|prefix| prefix.to_string()); + + let start_point = selection.start.to_point(&buffer); + let is_within_block_comment = buffer + .language_scope_at(start_point) + .is_some_and(|scope| scope.override_name() == Some("comment")); + let comment_prefix = if is_within_block_comment { + language_scope + .documentation() + .map(|doc_config| doc_config.prefix.to_string()) + } else { + language_scope + .line_comment_prefixes() + .iter() + .find(|prefix| buffer.contains_str_at(indent_end, prefix)) + .map(|prefix| prefix.to_string()) + }; + let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row))); let line_text_after_indent = buffer .text_for_range(indent_end..line_end) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 4421869703..a5bc581a00 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -5759,6 +5759,118 @@ async fn test_rewrap(cx: &mut TestAppContext) { } } +#[gpui::test] +async fn test_rewrap_block_comments(cx: &mut TestAppContext) { + init_test(cx, |settings| { + settings.languages.0.extend([( + "Rust".into(), + LanguageSettingsContent { + allow_rewrap: Some(language_settings::RewrapBehavior::InComments), + ..Default::default() + }, + )]) + }); + + let mut cx = EditorTestContext::new(cx).await; + + let language_with_doc_comments = Arc::new( + Language::new( + LanguageConfig { + name: "Rust".into(), + line_comments: vec!["// ".into()], + block_comment: Some(("/*".into(), "*/".into())), + documentation: Some(DocumentationConfig { + start: "/**".into(), + end: "*/".into(), + prefix: "* ".into(), + tab_size: NonZeroU32::new(1).unwrap(), + }), + + ..LanguageConfig::default() + }, + Some(tree_sitter_rust::LANGUAGE.into()), + ) + .with_override_query("[(block_comment)] @comment.inclusive") + .unwrap(), + ); + + // regular block comment + assert_rewrap( + indoc! {" + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + * Praesent semper egestas tellus id dignissim. + */ + "}, + indoc! {" + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit + * purus, a ornare lacus gravida vitae. Praesent semper egestas tellus id + * dignissim. + */ + "}, + language_with_doc_comments.clone(), + &mut cx, + ); + + // block comment still respects paragraph bounds + assert_rewrap( + indoc! {" + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + * Praesent semper egestas tellus id dignissim. + * + * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + * Praesent semper egestas tellus id dignissim. + */ + "}, + indoc! {" + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit + * purus, a ornare lacus gravida vitae. Praesent semper egestas tellus id + * dignissim. + * + * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + * Praesent semper egestas tellus id dignissim. + */ + "}, + language_with_doc_comments.clone(), + &mut cx, + ); + + // documentation comment + assert_rewrap( + indoc! {" + /** + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + * Praesent semper egestas tellus id dignissim. + */ + "}, + indoc! {" + /** + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit + * purus, a ornare lacus gravida vitae. Praesent semper egestas tellus id + * dignissim. + */ + "}, + language_with_doc_comments.clone(), + &mut cx, + ); + + #[track_caller] + fn assert_rewrap( + unwrapped_text: &str, + wrapped_text: &str, + language: Arc, + cx: &mut EditorTestContext, + ) { + cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx)); + cx.set_state(unwrapped_text); + cx.update_editor(|e, window, cx| e.rewrap(&Rewrap, window, cx)); + cx.assert_editor_state(wrapped_text); + } +} + #[gpui::test] async fn test_hard_wrap(cx: &mut TestAppContext) { init_test(cx, |_| {}); From b11bf9108bbe312ec10be44f7f49d5ea266fe666 Mon Sep 17 00:00:00 2001 From: Clayton Carter Date: Wed, 23 Jul 2025 22:17:32 -0400 Subject: [PATCH 2/4] fixup: work with inline delimiters --- crates/editor/src/editor.rs | 225 ++++++++++++++++++++++-------- crates/editor/src/editor_tests.rs | 176 +++++++++++++++++++---- 2 files changed, 310 insertions(+), 91 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index f1516e30d2..f5b60483e7 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -11731,6 +11731,18 @@ impl Editor { let buffer = self.buffer.read(cx).snapshot(cx); let selections = self.selections.all::(cx); + #[derive(Clone, Debug, PartialEq)] + enum CommentFormat { + /// single line comment, with prefix for line + Line(String), + /// single line within a block comment, with prefix for line + BlockLine(String), + /// a single line of a block comment that includes the initial delimiter + BlockCommentWithStart(BlockCommentConfig), + /// a single line of a block comment that includes the ending delimiter + BlockCommentWithEnd(BlockCommentConfig), + } + // Split selections to respect paragraph, indent, and comment prefix boundaries. let wrap_ranges = selections.into_iter().flat_map(|selection| { let mut non_blank_rows_iter = (selection.start.row..=selection.end.row) @@ -11747,49 +11759,75 @@ impl Editor { let language_scope = buffer.language_scope_at(selection.head()); let indent_and_prefix_for_row = - |row: u32| -> (IndentSize, Option, Option) { + |row: u32| -> (IndentSize, Option, Option) { let indent = buffer.indent_size_for_line(MultiBufferRow(row)); - let (comment_prefix, rewrap_prefix) = - if let Some(language_scope) = &language_scope { - let indent_end = Point::new(row, indent.len); + let (comment_prefix, rewrap_prefix) = if let Some(language_scope) = + &language_scope + { + let indent_end = Point::new(row, indent.len); + let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row))); + let line_text_after_indent = buffer + .text_for_range(indent_end..line_end) + .collect::(); - let start_point = selection.start.to_point(&buffer); - let is_within_block_comment = buffer - .language_scope_at(start_point) - .is_some_and(|scope| scope.override_name() == Some("comment")); - let comment_prefix = if is_within_block_comment { - language_scope - .documentation() - .map(|doc_config| doc_config.prefix.to_string()) - } else { - language_scope + let is_within_comment_override = buffer + .language_scope_at(indent_end) + .is_some_and(|scope| scope.override_name() == Some("comment")); + let comment_delimiters = if is_within_comment_override { + // we are within a comment syntax node, but we don't + // yet know what kind of comment: block, doc or line + match ( + language_scope.documentation_comment(), + language_scope.block_comment(), + ) { + (Some(config), _) | (_, Some(config)) + if buffer.contains_str_at(indent_end, &config.start) => + { + Some(CommentFormat::BlockCommentWithStart(config.clone())) + } + (Some(config), _) | (_, Some(config)) + if line_text_after_indent.ends_with(config.end.as_ref()) => + { + Some(CommentFormat::BlockCommentWithEnd(config.clone())) + } + (Some(config), _) | (_, Some(config)) + if buffer.contains_str_at(indent_end, &config.prefix) => + { + Some(CommentFormat::BlockLine(config.prefix.to_string())) + } + (_, _) => language_scope .line_comment_prefixes() .iter() .find(|prefix| buffer.contains_str_at(indent_end, prefix)) - .map(|prefix| prefix.to_string()) - }; - - let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row))); - let line_text_after_indent = buffer - .text_for_range(indent_end..line_end) - .collect::(); - let rewrap_prefix = language_scope - .rewrap_prefixes() - .iter() - .find_map(|prefix_regex| { - prefix_regex.find(&line_text_after_indent).map(|mat| { - if mat.start() == 0 { - Some(mat.as_str().to_string()) - } else { - None - } - }) - }) - .flatten(); - (comment_prefix, rewrap_prefix) + .map(|prefix| CommentFormat::Line(prefix.to_string())), + } } else { - (None, None) + // we not in an overridden comment node, but we may + // be within a non-overridden line comment node + language_scope + .line_comment_prefixes() + .iter() + .find(|prefix| buffer.contains_str_at(indent_end, prefix)) + .map(|prefix| CommentFormat::Line(prefix.to_string())) }; + + let rewrap_prefix = language_scope + .rewrap_prefixes() + .iter() + .find_map(|prefix_regex| { + prefix_regex.find(&line_text_after_indent).map(|mat| { + if mat.start() == 0 { + Some(mat.as_str().to_string()) + } else { + None + } + }) + }) + .flatten(); + (comment_delimiters, rewrap_prefix) + } else { + (None, None) + }; (indent, comment_prefix, rewrap_prefix) }; @@ -11800,22 +11838,22 @@ impl Editor { let mut prev_row = first_row; let ( mut current_range_indent, - mut current_range_comment_prefix, + mut current_range_comment_delimiters, mut current_range_rewrap_prefix, ) = indent_and_prefix_for_row(first_row); for row in non_blank_rows_iter.skip(1) { let has_paragraph_break = row > prev_row + 1; - let (row_indent, row_comment_prefix, row_rewrap_prefix) = + let (row_indent, row_comment_delimiters, row_rewrap_prefix) = indent_and_prefix_for_row(row); let has_indent_change = row_indent != current_range_indent; - let has_comment_change = row_comment_prefix != current_range_comment_prefix; + let has_comment_change = row_comment_delimiters != current_range_comment_delimiters; let has_boundary_change = has_comment_change || row_rewrap_prefix.is_some() - || (has_indent_change && current_range_comment_prefix.is_some()); + || (has_indent_change && current_range_comment_delimiters.is_some()); if has_paragraph_break || has_boundary_change { ranges.push(( @@ -11823,13 +11861,13 @@ impl Editor { Point::new(current_range_start, 0) ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))), current_range_indent, - current_range_comment_prefix.clone(), + current_range_comment_delimiters.clone(), current_range_rewrap_prefix.clone(), from_empty_selection, )); current_range_start = row; current_range_indent = row_indent; - current_range_comment_prefix = row_comment_prefix; + current_range_comment_delimiters = row_comment_delimiters; current_range_rewrap_prefix = row_rewrap_prefix; } prev_row = row; @@ -11840,7 +11878,7 @@ impl Editor { Point::new(current_range_start, 0) ..Point::new(prev_row, buffer.line_len(MultiBufferRow(prev_row))), current_range_indent, - current_range_comment_prefix, + current_range_comment_delimiters, current_range_rewrap_prefix, from_empty_selection, )); @@ -11854,7 +11892,7 @@ impl Editor { for ( language_settings, wrap_range, - indent_size, + mut indent_size, comment_prefix, rewrap_prefix, from_empty_selection, @@ -11874,16 +11912,26 @@ impl Editor { let tab_size = language_settings.tab_size; + let (line_prefix, inside_comment) = match &comment_prefix { + Some(CommentFormat::Line(prefix) | CommentFormat::BlockLine(prefix)) => { + (Some(prefix.as_str()), true) + } + Some(CommentFormat::BlockCommentWithEnd(BlockCommentConfig { prefix, .. })) => { + (Some(prefix.as_ref()), true) + } + Some(CommentFormat::BlockCommentWithStart(BlockCommentConfig { + start: _, + end: _, + prefix, + tab_size, + })) => { + indent_size.len += tab_size; + (Some(prefix.as_ref()), true) + } + None => (None, false), + }; let indent_prefix = indent_size.chars().collect::(); - let mut line_prefix = indent_prefix.clone(); - let mut inside_comment = false; - if let Some(prefix) = &comment_prefix { - line_prefix.push_str(prefix); - inside_comment = true; - } - if let Some(prefix) = &rewrap_prefix { - line_prefix.push_str(prefix); - } + let line_prefix = format!("{indent_prefix}{}", line_prefix.unwrap_or("")); let allow_rewrap_based_on_language = match language_settings.allow_rewrap { RewrapBehavior::InComments => inside_comment, @@ -11928,13 +11976,55 @@ impl Editor { let start_offset = start.to_offset(&buffer); let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row))); let selection_text = buffer.text_for_range(start..end).collect::(); + let mut first_line_delimiter = None; + let mut last_line_delimiter = None; let Some(lines_without_prefixes) = selection_text .lines() .enumerate() .map(|(ix, line)| { - let line_trimmed = line.trim_start(); + let line_trimmed = line.trim(); if rewrap_prefix.is_some() && ix > 0 { Ok(line_trimmed) + } else if let Some( + CommentFormat::BlockCommentWithStart(BlockCommentConfig { + start, + prefix, + end, + tab_size, + }) + | CommentFormat::BlockCommentWithEnd(BlockCommentConfig { + start, + prefix, + end, + tab_size, + }), + ) = &comment_prefix + { + let line_trimmed = line_trimmed + .strip_prefix(start.as_ref()) + .map(|s| { + let mut indent_size = indent_size.clone(); + indent_size.len -= tab_size; + let indent_prefix: String = indent_size.chars().collect(); + first_line_delimiter = Some((indent_prefix, start)); + s.trim_start() + }) + .unwrap_or(line_trimmed); + let line_trimmed = line_trimmed + .strip_suffix(end.as_ref()) + .map(|s| { + last_line_delimiter = Some(end); + s.trim_end() + }) + .unwrap_or(line_trimmed); + let line_trimmed = line_trimmed + .strip_prefix(prefix.as_ref()) + .unwrap_or(line_trimmed); + Ok(line_trimmed) + } else if let Some(CommentFormat::BlockLine(prefix)) = &comment_prefix { + line_trimmed.strip_prefix(prefix).with_context(|| { + format!("line did not start with prefix {prefix:?}: {line:?}") + }) } else { line_trimmed .strip_prefix(&line_prefix.trim_start()) @@ -11961,14 +12051,25 @@ impl Editor { line_prefix.clone() }; - let wrapped_text = wrap_with_prefix( - line_prefix, - subsequent_lines_prefix, - lines_without_prefixes.join("\n"), - wrap_column, - tab_size, - options.preserve_existing_whitespace, - ); + let wrapped_text = { + let mut wrapped_text = wrap_with_prefix( + line_prefix, + subsequent_lines_prefix, + lines_without_prefixes.join("\n"), + wrap_column, + tab_size, + options.preserve_existing_whitespace, + ); + + if let Some((indent, delimiter)) = first_line_delimiter { + wrapped_text = format!("{indent}{delimiter}\n{wrapped_text}"); + } + if let Some(last_line) = last_line_delimiter { + wrapped_text = format!("{wrapped_text}\n{indent_prefix}{last_line}"); + } + + wrapped_text + }; // TODO: should always use char-based diff while still supporting cursor behavior that // matches vim. diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index a5bc581a00..cd42e59dae 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -5436,14 +5436,18 @@ async fn test_rewrap(cx: &mut TestAppContext) { }, None, )); - let rust_language = Arc::new(Language::new( - LanguageConfig { - name: "Rust".into(), - line_comments: vec!["// ".into(), "/// ".into()], - ..LanguageConfig::default() - }, - Some(tree_sitter_rust::LANGUAGE.into()), - )); + let rust_language = Arc::new( + Language::new( + LanguageConfig { + name: "Rust".into(), + line_comments: vec!["// ".into(), "/// ".into()], + ..LanguageConfig::default() + }, + Some(tree_sitter_rust::LANGUAGE.into()), + ) + .with_override_query("[(line_comment)(block_comment)] @comment.inclusive") + .unwrap(), + ); let plaintext_language = Arc::new(Language::new( LanguageConfig { @@ -5773,24 +5777,29 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { let mut cx = EditorTestContext::new(cx).await; - let language_with_doc_comments = Arc::new( + let rust_lang = Arc::new( Language::new( LanguageConfig { name: "Rust".into(), line_comments: vec!["// ".into()], - block_comment: Some(("/*".into(), "*/".into())), - documentation: Some(DocumentationConfig { + block_comment: Some(BlockCommentConfig { + start: "/*".into(), + end: "*/".into(), + prefix: "* ".into(), + tab_size: 1, + }), + documentation_comment: Some(BlockCommentConfig { start: "/**".into(), end: "*/".into(), prefix: "* ".into(), - tab_size: NonZeroU32::new(1).unwrap(), + tab_size: 1, }), ..LanguageConfig::default() }, Some(tree_sitter_rust::LANGUAGE.into()), ) - .with_override_query("[(block_comment)] @comment.inclusive") + .with_override_query("[(line_comment) (block_comment)] @comment.inclusive") .unwrap(), ); @@ -5799,61 +5808,170 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { indoc! {" /* *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. - * Praesent semper egestas tellus id dignissim. */ + /*ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. */ "}, indoc! {" /* *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - * purus, a ornare lacus gravida vitae. Praesent semper egestas tellus id - * dignissim. + * purus, a ornare lacus gravida vitae. + */ + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit + * purus, a ornare lacus gravida vitae. */ "}, - language_with_doc_comments.clone(), + rust_lang.clone(), &mut cx, ); - // block comment still respects paragraph bounds + // indent is respected + assert_rewrap( + indoc! {" + {} + /*ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ + "}, + indoc! {" + {} + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + */ + "}, + rust_lang.clone(), + &mut cx, + ); + + // short block comments with inline delimiters + assert_rewrap( + indoc! {" + /*ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ + /*ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + */ + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ + "}, + indoc! {" + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + */ + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + */ + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + */ + "}, + rust_lang.clone(), + &mut cx, + ); + + // multiline block comment with inline start/end delimiters + assert_rewrap( + indoc! {" + /*ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Vivamus mollis elit purus, a ornare lacus gravida vitae. */ + "}, + indoc! {" + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit + * purus, a ornare lacus gravida vitae. + */ + "}, + rust_lang.clone(), + &mut cx, + ); + + // block comment rewrap still respects paragraph bounds assert_rewrap( indoc! {" /* *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. - * Praesent semper egestas tellus id dignissim. * * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. - * Praesent semper egestas tellus id dignissim. */ "}, indoc! {" /* *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - * purus, a ornare lacus gravida vitae. Praesent semper egestas tellus id - * dignissim. + * purus, a ornare lacus gravida vitae. * * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. - * Praesent semper egestas tellus id dignissim. */ "}, - language_with_doc_comments.clone(), + rust_lang.clone(), &mut cx, ); - // documentation comment + // documentation comments assert_rewrap( indoc! {" + /**ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. */ /** *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. - * Praesent semper egestas tellus id dignissim. */ "}, indoc! {" /** *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - * purus, a ornare lacus gravida vitae. Praesent semper egestas tellus id - * dignissim. + * purus, a ornare lacus gravida vitae. + */ + /** + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit + * purus, a ornare lacus gravida vitae. */ "}, - language_with_doc_comments.clone(), + rust_lang.clone(), + &mut cx, + ); + + // different, adjacent comments + assert_rewrap( + indoc! {" + /** + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + */ + /*ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. */ + //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + "}, + indoc! {" + /** + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit + * purus, a ornare lacus gravida vitae. + */ + /* + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit + * purus, a ornare lacus gravida vitae. + */ + //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit + // purus, a ornare lacus gravida vitae. + "}, + rust_lang.clone(), + &mut cx, + ); + + // TODO these are unhandled edge cases; not correct, just documenting known issues + assert_rewrap( + indoc! {" + /* + //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + */ + /* + //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ + /*ˇ Lorem ipsum dolor sit amet */ /* consectetur adipiscing elit. */ + "}, + indoc! {" + /* + //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit + // purus, a ornare lacus gravida vitae. + */ + /* + * //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + */ + /* + *ˇ Lorem ipsum dolor sit amet */ /* consectetur adipiscing elit. + */ + "}, + rust_lang.clone(), &mut cx, ); From 619a142e3d904c8a76ee3633a3c2b0af68a2140f Mon Sep 17 00:00:00 2001 From: Clayton Carter Date: Wed, 13 Aug 2025 22:46:56 -0400 Subject: [PATCH 3/4] fixup: use shorter test fixtures --- crates/editor/src/editor_tests.rs | 85 +++++++++++++++++-------------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index cd42e59dae..1484c9d55c 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -5770,6 +5770,7 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { "Rust".into(), LanguageSettingsContent { allow_rewrap: Some(language_settings::RewrapBehavior::InComments), + preferred_line_length: Some(40), ..Default::default() }, )]) @@ -5807,18 +5808,18 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { assert_rewrap( indoc! {" /* - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ - /*ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. */ + /*ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ "}, indoc! {" /* - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - * purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ /* - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - * purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ "}, rust_lang.clone(), @@ -5834,7 +5835,8 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { indoc! {" {} /* - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ "}, rust_lang.clone(), @@ -5852,13 +5854,16 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { "}, indoc! {" /* - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ /* - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ /* - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ "}, rust_lang.clone(), @@ -5868,13 +5873,13 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { // multiline block comment with inline start/end delimiters assert_rewrap( indoc! {" - /*ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. - * Vivamus mollis elit purus, a ornare lacus gravida vitae. */ + /*ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ "}, indoc! {" /* - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - * purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ "}, rust_lang.clone(), @@ -5885,17 +5890,17 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { assert_rewrap( indoc! {" /* - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + * Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ "}, indoc! {" /* - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - * purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + * Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ "}, rust_lang.clone(), @@ -5905,19 +5910,19 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { // documentation comments assert_rewrap( indoc! {" - /**ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. */ + /**ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ /** - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ "}, indoc! {" /** - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - * purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ /** - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - * purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ "}, rust_lang.clone(), @@ -5928,22 +5933,22 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { assert_rewrap( indoc! {" /** - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ - /*ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. */ - //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + /*ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ + //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. "}, indoc! {" /** - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - * purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ /* - *ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - * purus, a ornare lacus gravida vitae. + *ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ - //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - // purus, a ornare lacus gravida vitae. + //ˇ Lorem ipsum dolor sit amet, + // consectetur adipiscing elit. "}, rust_lang.clone(), &mut cx, @@ -5953,7 +5958,7 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { assert_rewrap( indoc! {" /* - //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae. + //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ /* //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ @@ -5961,14 +5966,16 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { "}, indoc! {" /* - //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit - // purus, a ornare lacus gravida vitae. + //ˇ Lorem ipsum dolor sit amet, + // consectetur adipiscing elit. */ /* - * //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * //ˇ Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ /* - *ˇ Lorem ipsum dolor sit amet */ /* consectetur adipiscing elit. + *ˇ Lorem ipsum dolor sit amet */ /* + * consectetur adipiscing elit. */ "}, rust_lang.clone(), From e246758be406af77a88830085567f732c0ac116a Mon Sep 17 00:00:00 2001 From: Clayton Carter Date: Thu, 14 Aug 2025 09:36:15 -0400 Subject: [PATCH 4/4] fixup: work on rewrapping block comments in selections --- crates/editor/src/editor.rs | 2 +- crates/editor/src/editor_tests.rs | 173 ++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 1 deletion(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index f5b60483e7..f0d9ff8f28 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -12003,7 +12003,7 @@ impl Editor { let line_trimmed = line_trimmed .strip_prefix(start.as_ref()) .map(|s| { - let mut indent_size = indent_size.clone(); + let mut indent_size = indent_size; indent_size.len -= tab_size; let indent_prefix: String = indent_size.chars().collect(); first_line_delimiter = Some((indent_prefix, start)); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 1484c9d55c..7adfe87f84 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -5954,6 +5954,164 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { &mut cx, ); + // selection w/ single short block comment + assert_rewrap( + indoc! {" + «/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ˇ» + "}, + indoc! {" + «/* + * Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. + */ˇ» + "}, + rust_lang.clone(), + &mut cx, + ); + + // rewrapping a single comment w/ abutting comments + assert_rewrap( + indoc! {" + /* ˇLorem ipsum dolor sit amet, consectetur adipiscing elit. */ + /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ + "}, + indoc! {" + /* + * ˇLorem ipsum dolor sit amet, + * consectetur adipiscing elit. + */ + /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ + "}, + rust_lang.clone(), + &mut cx, + ); + + // selection w/ non-abutting short block comments + assert_rewrap( + indoc! {" + «/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ + + /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ˇ» + "}, + indoc! {" + «/* + * Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. + */ + + /* + * Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. + */ˇ» + "}, + rust_lang.clone(), + &mut cx, + ); + + // selection of multiline block comments + assert_rewrap( + indoc! {" + «/* Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. */ˇ» + "}, + indoc! {" + «/* + * Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. + */ˇ» + "}, + rust_lang.clone(), + &mut cx, + ); + + // partial selection of multiline block comments + assert_rewrap( + indoc! {" + «/* Lorem ipsum dolor sit amet,ˇ» + * consectetur adipiscing elit. */ + /* Lorem ipsum dolor sit amet, + «* consectetur adipiscing elit. */ˇ» + "}, + indoc! {" + «/* + * Lorem ipsum dolor sit amet,ˇ» + * consectetur adipiscing elit. */ + /* Lorem ipsum dolor sit amet, + «* consectetur adipiscing elit. + */ˇ» + "}, + rust_lang.clone(), + &mut cx, + ); + + // selection w/ abutting short block comments + // FIXME unhandled edge case; not correct, just documenting known issues + // should not be combined; should rewrap as 2 comments + assert_rewrap( + indoc! {" + «/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ + /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ˇ» + "}, + // desired behavior: + // indoc! {" + // «/* + // * Lorem ipsum dolor sit amet, + // * consectetur adipiscing elit. + // */ + // /* + // * Lorem ipsum dolor sit amet, + // * consectetur adipiscing elit. + // */ˇ» + // "}, + // actual behaviour: + indoc! {" + «/* + * Lorem ipsum dolor sit amet, + * consectetur adipiscing elit. Lorem + * ipsum dolor sit amet, consectetur + * adipiscing elit. + */ˇ» + "}, + rust_lang.clone(), + &mut cx, + ); + + // FIXME same as above, but with delimiters on separate line + // assert_rewrap( + // indoc! {" + // «/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. + // */ + // /* + // * Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ˇ» + // "}, + // // desired: + // // indoc! {" + // // «/* + // // * Lorem ipsum dolor sit amet, + // // * consectetur adipiscing elit. + // // */ + // // /* + // // * Lorem ipsum dolor sit amet, + // // * consectetur adipiscing elit. + // // */ˇ» + // // "}, + // // actual: (but with trailing w/s on the empty lines) + // indoc! {" + // «/* + // * Lorem ipsum dolor sit amet, + // * consectetur adipiscing elit. + // * + // */ + // /* + // * + // * Lorem ipsum dolor sit amet, + // * consectetur adipiscing elit. + // */ˇ» + // "}, + // rust_lang.clone(), + // &mut cx, + // ); + // TODO these are unhandled edge cases; not correct, just documenting known issues assert_rewrap( indoc! {" @@ -5964,6 +6122,21 @@ async fn test_rewrap_block_comments(cx: &mut TestAppContext) { //ˇ Lorem ipsum dolor sit amet, consectetur adipiscing elit. */ /*ˇ Lorem ipsum dolor sit amet */ /* consectetur adipiscing elit. */ "}, + // desired: + // indoc! {" + // /* + // *ˇ Lorem ipsum dolor sit amet, + // * consectetur adipiscing elit. + // */ + // /* + // *ˇ Lorem ipsum dolor sit amet, + // * consectetur adipiscing elit. + // */ + // /* + // *ˇ Lorem ipsum dolor sit amet + // */ /* consectetur adipiscing elit. */ + // "}, + // actual: indoc! {" /* //ˇ Lorem ipsum dolor sit amet,