diff --git a/Cargo.lock b/Cargo.lock index 9de445fd..ec101507 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4745,6 +4745,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", + "textwrap", "thiserror", "walkdir", "weaver_common", diff --git a/crates/weaver_forge/Cargo.toml b/crates/weaver_forge/Cargo.toml index 05cc34a9..105731cc 100644 --- a/crates/weaver_forge/Cargo.toml +++ b/crates/weaver_forge/Cargo.toml @@ -29,6 +29,7 @@ jaq-syn = "1.1.0" indexmap = "2.6.0" regex = "1.11.1" markdown = "=1.0.0-alpha.21" +textwrap = "0.16.1" itertools.workspace = true thiserror.workspace = true diff --git a/crates/weaver_forge/src/config.rs b/crates/weaver_forge/src/config.rs index c14a1604..d94a7a79 100644 --- a/crates/weaver_forge/src/config.rs +++ b/crates/weaver_forge/src/config.rs @@ -322,6 +322,8 @@ pub struct CommentFormat { /// Flag to enforce trailing dots on the comment content. #[serde(default = "default_bool::")] pub enforce_trailing_dots: bool, + /// The maximum number of characters in a line. + pub(crate) line_length: Option, } /// The type of indentation to use for the comment. diff --git a/crates/weaver_forge/src/extensions/code.rs b/crates/weaver_forge/src/extensions/code.rs index 8b6fdbe0..be39de41 100644 --- a/crates/weaver_forge/src/extensions/code.rs +++ b/crates/weaver_forge/src/extensions/code.rs @@ -9,6 +9,7 @@ use crate::formats::markdown::MarkdownRenderer; use minijinja::value::{Kwargs, ValueKind}; use minijinja::{Environment, ErrorKind, Value}; use std::collections::HashMap; +use std::usize; /// Add code-oriented filters to the environment. /// @@ -176,26 +177,43 @@ pub(crate) fn comment( .map(|v: String| v) .unwrap_or_else(|_| comment_format.footer.clone().unwrap_or("".to_owned())); + // Configure text-wrap algorithm to appropriately segment lines. + let subsequent_indent = format!("{indent}{prefix}"); + // If there is a header, then we need to indent the first line. + let initial_indent = if header.is_empty() { + &prefix + } else { + &subsequent_indent + }; + let wrap_options = + textwrap::Options::new(comment_format.line_length.unwrap_or(usize::MAX)) + .initial_indent(&initial_indent) + .subsequent_indent(&subsequent_indent) + .wrap_algorithm(textwrap::WrapAlgorithm::FirstFit) + .break_words(false); + // Wrap the comment as configured. + comment = textwrap::fill(&comment, wrap_options); + + // The textwrap will leave empty lines, which we want to fill with the prefix. let mut new_comment = String::new(); for line in comment.lines() { if !new_comment.is_empty() { new_comment.push('\n'); } - if header.is_empty() && new_comment.is_empty() { - // For the first line we don't add the indentation - new_comment.push_str(&format!("{}{}", prefix, line)); + if line.is_empty() && !new_comment.is_empty() { + new_comment.push_str(&format!("{indent}{prefix}")); } else { - new_comment.push_str(&format!("{}{}{}", indent, prefix, line)); + new_comment.push_str(line); } } comment = new_comment; + // Add header + footer to the comment. if !header.is_empty() { comment = format!("{}\n{}", header, comment); } - if !footer.is_empty() { - comment = format!("{}\n{}{}", comment, indent, footer); + comment = format!("{}\n{}{}", comment.trim_end(), indent, footer); } // Remove all trailing spaces from the comment @@ -285,6 +303,7 @@ mod tests { trim: true, remove_trailing_dots: true, enforce_trailing_dots: false, + line_length: None, }, )] .into_iter() @@ -462,6 +481,7 @@ it's RECOMMENDED to: trim: true, remove_trailing_dots: true, enforce_trailing_dots: false, + line_length: None, }, )] .into_iter() @@ -523,6 +543,7 @@ it's RECOMMENDED to: remove_trailing_dots: true, enforce_trailing_dots: false, indent_type: Default::default(), + line_length: None, }, )] .into_iter() @@ -589,6 +610,7 @@ it's RECOMMENDED to: remove_trailing_dots: false, enforce_trailing_dots: true, indent_type: Default::default(), + line_length: None, }, )] .into_iter() diff --git a/crates/weaver_forge/src/formats/html.rs b/crates/weaver_forge/src/formats/html.rs index cd6fed43..ce502388 100644 --- a/crates/weaver_forge/src/formats/html.rs +++ b/crates/weaver_forge/src/formats/html.rs @@ -351,6 +351,7 @@ mod tests { trim: true, remove_trailing_dots: true, enforce_trailing_dots: false, + line_length: None, }, )] .into_iter() diff --git a/crates/weaver_forge/src/formats/markdown.rs b/crates/weaver_forge/src/formats/markdown.rs index 3dea59f3..b7cf42fb 100644 --- a/crates/weaver_forge/src/formats/markdown.rs +++ b/crates/weaver_forge/src/formats/markdown.rs @@ -412,6 +412,7 @@ mod tests { trim: true, remove_trailing_dots: true, enforce_trailing_dots: false, + line_length: None, }, )] .into_iter() @@ -517,6 +518,7 @@ it's RECOMMENDED to: remove_trailing_dots: true, indent_type: Default::default(), enforce_trailing_dots: false, + line_length: None, }, )] .into_iter() @@ -557,6 +559,7 @@ The file \[extension\] extracted \[from] the `url.full`, excluding the leading d remove_trailing_dots: true, indent_type: Default::default(), enforce_trailing_dots: false, + line_length: None, }, )] .into_iter()