diff --git a/crates/typstyle-core/src/config.rs b/crates/typstyle-core/src/config.rs index 40a7741b..0e8aad59 100644 --- a/crates/typstyle-core/src/config.rs +++ b/crates/typstyle-core/src/config.rs @@ -16,6 +16,13 @@ pub struct Config { /// When `true`, text in markup will be wrapped to fit within `max_width`. /// Implies `collapse_markup_spaces`. pub wrap_text: bool, + + /// When `true`, doc comments (`/// ...`) will be formatted as markup according to style rules. + pub format_doc_comments: bool, + /// When `true`, doc comments will be wrapped to fit within `doc_comment_width`. + pub wrap_doc_comments: bool, + /// The maximum width for contents of doc comments. + pub doc_comment_width: usize, } impl Default for Config { @@ -27,6 +34,9 @@ impl Default for Config { reorder_import_items: true, collapse_markup_spaces: false, wrap_text: false, + format_doc_comments: false, + wrap_doc_comments: false, + doc_comment_width: 80, } } } diff --git a/crates/typstyle-core/src/pretty/doc_comment.rs b/crates/typstyle-core/src/pretty/doc_comment.rs new file mode 100644 index 00000000..46b683a2 --- /dev/null +++ b/crates/typstyle-core/src/pretty/doc_comment.rs @@ -0,0 +1,43 @@ +use itertools::Itertools; +use typst_syntax::{Source, SyntaxNode}; + +use crate::{ + pretty::{prelude::*, Context, PrettyPrinter}, + Config, Typstyle, +}; + +impl<'a> PrettyPrinter<'a> { + pub(super) fn format_doc_comments( + &'a self, + ctx: Context, + doc_comment_nodes: Vec<&'a SyntaxNode>, + ) -> ArenaDoc<'a> { + let text = doc_comment_nodes + .iter() + .map(|&it| &it.text()[3..]) + .join("\n"); + let source = Source::detached(text); + + let config = Config { + wrap_text: self.config.wrap_text | self.config.wrap_doc_comments, + max_width: self.config.doc_comment_width, + ..self.config + }; + + let Ok(formatted) = Typstyle::new(config).format_source(source).render() else { + // Fall back to original formatting + return self.arena.intersperse( + doc_comment_nodes + .into_iter() + .map(|node| self.convert_comment(ctx, node)), + self.arena.hardline(), + ); + }; + self.arena.intersperse( + formatted + .lines() + .map(|line| self.arena.text(format!("/// {line}"))), + self.arena.hardline(), + ) + } +} diff --git a/crates/typstyle-core/src/pretty/markup.rs b/crates/typstyle-core/src/pretty/markup.rs index b001c9a6..8a401c00 100644 --- a/crates/typstyle-core/src/pretty/markup.rs +++ b/crates/typstyle-core/src/pretty/markup.rs @@ -197,6 +197,8 @@ impl<'a> PrettyPrinter<'a> { let repr = collect_markup_repr(markup); let body = if self.config.wrap_text && scope != MarkupScope::InlineItem { self.convert_markup_body_reflow(ctx, &repr) + } else if self.config.format_doc_comments { + self.convert_markup_body_with_doc_comments_formatted(ctx, &repr) } else { self.convert_markup_body(ctx, &repr) }; @@ -294,6 +296,67 @@ impl<'a> PrettyPrinter<'a> { doc } + fn convert_markup_body_with_doc_comments_formatted( + &'a self, + ctx: Context, + repr: &MarkupRepr<'a>, + ) -> ArenaDoc<'a> { + let mut doc = self.arena.nil(); + let mut doc_comments = vec![]; + for (i, line) in repr.lines.iter().enumerate() { + let &MarkupLine { + ref nodes, + breaks, + mixed_text, + } = line; + if nodes.len() == 1 + && nodes[0].kind() == SyntaxKind::LineComment + && nodes[0].text().starts_with("///") + { + doc_comments.push(nodes[0]); + continue; + } + if !doc_comments.is_empty() { + doc += self.format_doc_comments(ctx, std::mem::take(&mut doc_comments)); + // SAFETY: i > 0 due to non-empty doc_comments. + let last_line = &repr.lines[i - 1]; + if last_line.breaks > 0 { + doc += self.arena.hardline().repeat(last_line.breaks); + } + } + for node in nodes.iter() { + doc += if node.kind() == SyntaxKind::Space { + self.convert_space_untyped(ctx, node) + } else if let Some(text) = node.cast::() { + self.convert_text(text) + } else if let Some(expr) = node.cast::() { + let ctx = if mixed_text { + ctx.suppress_breaks() + } else { + ctx + }; + self.convert_expr(ctx, expr) + } else if is_comment_node(node) { + self.convert_comment(ctx, node) + } else { + // can be Hash, Semicolon, Shebang + self.convert_trivia_untyped(node) + }; + } + if breaks > 0 { + doc += self.arena.hardline().repeat(breaks); + } + } + if !doc_comments.is_empty() { + doc += self.format_doc_comments(ctx, std::mem::take(&mut doc_comments)); + let last_line = &repr.lines.last().expect("lines should not be empty"); + if last_line.breaks > 0 { + doc += self.arena.hardline().repeat(last_line.breaks); + } + } + doc + } + /// With text-wrapping enabled, spaces may turn to linebreaks, and linebreaks may turn to spaces, if safe. fn convert_markup_body_reflow(&'a self, ctx: Context, repr: &MarkupRepr<'a>) -> ArenaDoc<'a> { /// For NOT space -> soft-line: \ diff --git a/crates/typstyle-core/src/pretty/mod.rs b/crates/typstyle-core/src/pretty/mod.rs index 01d1e331..54866c12 100644 --- a/crates/typstyle-core/src/pretty/mod.rs +++ b/crates/typstyle-core/src/pretty/mod.rs @@ -8,6 +8,7 @@ mod code_flow; mod code_list; mod code_misc; mod comment; +mod doc_comment; mod func_call; mod import; mod layout; diff --git a/crates/typstyle/src/cli.rs b/crates/typstyle/src/cli.rs index c4a3f964..732eb63b 100644 --- a/crates/typstyle/src/cli.rs +++ b/crates/typstyle/src/cli.rs @@ -72,28 +72,30 @@ pub struct StyleArgs { long, visible_short_alias = 'c', visible_alias = "column", - default_value_t = 80, - global = true + default_value_t = 80 )] pub line_width: usize, /// Number of spaces per indentation level. - #[arg( - short = 't', - long, - visible_alias = "tab-width", - default_value_t = 2, - global = true - )] + #[arg(short = 't', long, visible_alias = "tab-width", default_value_t = 2)] pub indent_width: usize, /// Disable alphabetical reordering of import items. - #[arg(long, default_value_t = false, global = true)] + #[arg(long, default_value_t = false)] pub no_reorder_import_items: bool, /// Wrap text in markup to fit within the line width. Implies `--collapse-spaces`. - #[arg(long, default_value_t = false, global = true)] + #[arg(long, default_value_t = false)] pub wrap_text: bool, + + #[arg(long, default_value_t = false)] + pub format_doc_comments: bool, + + #[arg(long, default_value_t = false)] + pub wrap_doc_comments: bool, + + #[arg(long, default_value_t = 80)] + pub doc_comment_width: usize, } #[derive(Args)] diff --git a/crates/typstyle/src/fmt.rs b/crates/typstyle/src/fmt.rs index 779b7251..0e941114 100644 --- a/crates/typstyle/src/fmt.rs +++ b/crates/typstyle/src/fmt.rs @@ -50,6 +50,9 @@ impl StyleArgs { tab_spaces: self.indent_width, reorder_import_items: !self.no_reorder_import_items, wrap_text: self.wrap_text, + format_doc_comments: self.format_doc_comments, + wrap_doc_comments: self.format_doc_comments && self.wrap_doc_comments, + doc_comment_width: self.doc_comment_width, ..Default::default() } } diff --git a/playground/src/Playground.tsx b/playground/src/Playground.tsx index ecbee19f..7d65e651 100644 --- a/playground/src/Playground.tsx +++ b/playground/src/Playground.tsx @@ -100,7 +100,7 @@ function Playground() { const optionsPanel = (
-
+
-
+
+
-
+
-
+
-
+
+
+ + + setFormatOptions((prev) => ({ + ...prev, + formatDocComments: e.target.checked, + })) + } + /> +
+ +
+ + + setFormatOptions((prev) => ({ + ...prev, + wrapDocComments: e.target.checked, + })) + } + /> +
+ +
+ +
+ + setFormatOptions((prev) => ({ + ...prev, + docCommentWidth: Number.parseInt(e.target.value), + })) + } + /> +
+
+ diff --git a/playground/src/utils/formatter.ts b/playground/src/utils/formatter.ts index eb273ce9..8b42c06c 100644 --- a/playground/src/utils/formatter.ts +++ b/playground/src/utils/formatter.ts @@ -1,4 +1,4 @@ -import * as typstyle from "typstyle-wasm"; +import type * as typstyle from "typstyle-wasm"; export interface FormatOptions { lineWidth: number; @@ -6,6 +6,9 @@ export interface FormatOptions { collapseMarkupSpaces: boolean; reorderImportItems: boolean; wrapText: boolean; + formatDocComments: boolean; + wrapDocComments: boolean; + docCommentWidth: number; } // Default format style options @@ -15,6 +18,9 @@ export const DEFAULT_FORMAT_OPTIONS: FormatOptions = { collapseMarkupSpaces: false, reorderImportItems: true, wrapText: false, + formatDocComments: false, + wrapDocComments: false, + docCommentWidth: 80, }; /** @@ -44,5 +50,8 @@ export function formatOptionsToConfig( collapse_markup_spaces: options.collapseMarkupSpaces, reorder_import_items: options.reorderImportItems, wrap_text: options.wrapText, + format_doc_comments: options.formatDocComments, + wrap_doc_comments: options.wrapDocComments, + doc_comment_width: options.docCommentWidth, }; } diff --git a/tests/fixtures/unit/doc/blocks.typ b/tests/fixtures/unit/doc/blocks.typ new file mode 100644 index 00000000..105dc6cc --- /dev/null +++ b/tests/fixtures/unit/doc/blocks.typ @@ -0,0 +1,154 @@ +/// typstyle: format_doc_comments +// Test cases for doc comments inside various block types + +/// Doc comments in content blocks with code: `#for i in range(3){i}` +#let content_block = [ + /// Doc comment inside content block + /// Should be handled correctly + Some content here + + /// Another doc comment in content + More content + + /// Trailing +] + +/// Doc comments inside code blocks: +#let code_with_docs = { + /// This doc comment is inside a code block + /// It should be formatted properly + let x = 5 + + /// Another doc comment inside the same block + /// With multiple lines + let y = 10 + + x + y + + /// Trailing +} + +/// Doc comments in nested blocks: +#let nested_blocks = { + /// Outer doc comment in code block + let outer = [ + /// Doc comment inside content block which is inside code block + /// This tests deep nesting + Content here + ] + + /// Another doc comment in the code block + outer +} + +/// Doc comments in function definitions: +#let documented_function = ( + /// Doc comment for parameter inside function + x, + y +) => { + /// Doc comment inside function body + /// Explains the implementation + x + y +} + +/// Doc comments in function parameters: +#let func_with_param_docs = ( + /// Parameter x: the input value + /// Should be a positive number + x, + /// Parameter y: the multiplier + y: 1, + /// Rest parameters documentation + ..args +) => { + /// Function body documentation + /// Describes the implementation + x * y +} + +/// Doc comments in loops: +#let loop_with_docs = { + /// Documentation before the loop + for i in range(3) { + /// Doc comment inside loop body + /// Executed on each iteration + [Item #i] + } +} + +/// Doc comments in while loops: +#let while_with_docs = { + /// Counter initialization documentation + let count = 0 + + /// While loop documentation + while count < 3 { + /// Loop body documentation + count += 1 + } + + count +} + +/// Doc comments in conditional expressions: +#let conditional_with_docs = { + /// Condition documentation + let condition = true + + /// If-else documentation + if condition { + /// True branch documentation + /// Multiple lines in true case + "condition is true" + } else { + /// False branch documentation + "condition is false" + } +} + +/// Doc comments in match expressions: +#let match_with_docs = { + /// Value to match documentation + let value = 1 + + /// Match expression documentation + if value == 1 { + /// Case 1 documentation + "one" + } else if value == 2 { + /// Case 2 documentation + "two" + } else { + /// Default case documentation + "other" + } +} + +/// Doc comments in nested function calls: +#let nested_calls_with_docs = { + /// Outer function call documentation + calc.max( + /// First argument documentation + 10, + /// Second argument documentation + /// With multiple lines + 20 + ) +} + +/// Doc comments in show rules: +#show heading: it => { + /// Show rule documentation + /// Explains the heading transformation + text(weight: "bold")[#it.body] +} + +/// Doc comments in set rules context: +#let set_rule_context = { + /// Documentation for the set rule + set text(size: 12pt) + + /// Content after set rule + [Some text content] +} diff --git a/tests/fixtures/unit/doc/edge-cases.typ b/tests/fixtures/unit/doc/edge-cases.typ new file mode 100644 index 00000000..de6dfb9c --- /dev/null +++ b/tests/fixtures/unit/doc/edge-cases.typ @@ -0,0 +1,63 @@ +/// typstyle: format_doc_comments +// Test cases for edge cases in doc comment formatting + +/// Doc comment with mixed indentation and spacing: +/// Extra spaces at the beginning +/// And varying indentation levels +/// Back to normal +/// Deep indentation here +#let mixed_indentation = 1 + +/// Doc comment with empty lines and whitespace: +/// +/// Line with content after empty line +/// +/// +/// Multiple empty lines above #(1) +/// +/// Line with trailing spaces #(2) +/// +#let whitespace_handling = 2 + +/// +/// Doc comment starting with empty line (1) +#let starts_with_empty = 3 + +/// Doc comment ending with empty line (1) +/// +#let ends_with_empty = 4 + +/// Multiple spaces between words should be handled +/// Tabs and mixed whitespace characters +/// Various spacing patterns throughout +#let multiple_spaces = 5 + +/// Very long line that should be wrapped when wrap_doc_comments is enabled and the line exceeds the doc_comment_width setting +#let long_line = 6 + +/// Doc comment with nested doc-like patterns: +/// This looks like /// a doc comment but isn't +/// Also this: ///not a real doc comment +/// But this is normal text +#let pseudo_nested = 7 + +/// Mixed comment types: #(1) +/// Regular doc comment line #(2) +// Regular comment (not doc) #(3) +/// Back to doc comment #(4) +/// /// Nested doc comment #(5) +// Another regular comment #(6) +/// Final doc comment line #(7) +#let mixed_comments = 8 + +/// Doc comment with /// inline doc patterns #(1) +/// Text with #(0) /// embedded patterns that /// should be handled #(2) +/// Properly without /// breaking formatting #(3) +#let inline_doc_patterns = 9 + +/// Doc comment with line breaks #(1) + +/// after paragraph breaks #(2) +#let with_breaks = 10 + +/// Trailing doc comment diff --git a/tests/fixtures/unit/doc/general.typ b/tests/fixtures/unit/doc/general.typ new file mode 100644 index 00000000..a34ad174 --- /dev/null +++ b/tests/fixtures/unit/doc/general.typ @@ -0,0 +1,24 @@ +/// typstyle: format_doc_comments wrap_doc_comments +// Test cases for doc comment formatting - markup parsing and grouping + +/// This is a simple doc comment with valid markup +#let simple = 1 + +/// This has valid markup: *bold* and `code` and _emphasis_ +#let valid_markup = 2 + +/// Doc comment with code expressions: #(1+2) and #let x=5 +/// Also has badly formatted code: #(a+b*c/d) and #calc.pow(2,3) +#let with_code_expressions = 3 + +/// This has invalid markup: *unclosed strong +/// and broken `code span +/// Should fallback to plain text formatting +#let invalid_markup = 4 + +/// Code with function calls: #text(size:12pt)[Hello] and #rect(width:100pt,height:50pt) +/// Should be formatted properly +#let with_function_calls = 6 + +/// Very long comment that should be wrapped when wrap_doc_comments is enabled and exceeds doc_comment_width +#let long_comment = 7 diff --git a/tests/fixtures/unit/doc/nested.typ b/tests/fixtures/unit/doc/nested.typ new file mode 100644 index 00000000..f57a55bb --- /dev/null +++ b/tests/fixtures/unit/doc/nested.typ @@ -0,0 +1,67 @@ +/// typstyle: format_doc_comments +// Test cases for nested doc comments + +/// Simple nested doc comment: +/// /// This is nested level 1 +/// Back to normal level +#let simple_nested = 1 + +/// Multiple levels of nesting: +/// /// Level 1 nested +/// /// /// Level 2 nested +/// /// /// /// Level 3 nested +/// /// Back to level 1 +/// Back to normal level +#let deep_nested = 2 + +/// Mixed nesting patterns: +/// Normal doc comment +/// /// Nested comment +/// Normal again +/// /// /// Double nested +/// /// Back to single nested +/// Normal conclusion +#let mixed_nesting = 3 + +/// Nested with empty lines: +/// /// Nested comment +/// +/// ///Another nested after empty line +/// +/// /// Final nested comment +#let nested_with_empty = 4 + +/// Irregular nesting patterns: +/// ///Nested without space +/// /// Nested with space +/// /// Nested with extra spaces +/// //// Four slashes +/// ///// Five slashes +#let irregular_nesting = 5 + +/// Nested comments in consecutive blocks: +/// /// First nested +/// /// Second nested +/// Normal comment +#let nested_in_consecutive = 6 + +/// Another consecutive block with nesting: +/// Normal comment +/// /// Nested comment +/// /// Another nested +#let another_nested_consecutive = 7 + +/// Complex nesting scenario: +/// This is a normal doc comment line +/// /// This is nested level 1 +/// /// /// This is nested level 2 +/// /// /// Another line at level 2 +/// /// Back to level 1 +/// /// /// Deep again to level 2 +/// /// /// /// Even deeper to level 3 +/// /// /// Back to level 2 +/// /// Back to level 1 +/// Back to normal level +/// /// One more nested +/// Final normal line +#let complex_nesting = 8 diff --git a/tests/fixtures/unit/doc/snap/blocks.typ-0.snap b/tests/fixtures/unit/doc/snap/blocks.typ-0.snap new file mode 100644 index 00000000..6a4b5dc1 --- /dev/null +++ b/tests/fixtures/unit/doc/snap/blocks.typ-0.snap @@ -0,0 +1,182 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/blocks.typ +--- +/// typstyle: format_doc_comments +// Test cases for doc comments inside various block types + +/// Doc comments in content blocks with code: `#for i in range(3){i}` +#let content_block = [ + /// Doc comment inside content block + /// Should be handled correctly + Some content here + + /// Another doc comment in content + More content + + /// Trailing +] + +/// Doc comments inside code blocks: +#let code_with_docs = { + /// This doc comment is inside a code block + /// It should be formatted properly + let x = 5 + + /// Another doc comment inside the same block + /// With multiple lines + let y = 10 + + ( + x + + y + ) + + /// Trailing +} + +/// Doc comments in nested blocks: +#let nested_blocks = { + /// Outer doc comment in code block + let outer = [ + /// Doc comment inside content block which is inside code block + /// This tests deep nesting + Content here + ] + + /// Another doc comment in the code block + outer +} + +/// Doc comments in function definitions: +#let documented_function = ( + /// Doc comment for parameter inside function + x, + y, +) => { + /// Doc comment inside function body + /// Explains the implementation + ( + x + + y + ) +} + +/// Doc comments in function parameters: +#let func_with_param_docs = ( + /// Parameter x: the input value + /// Should be a positive number + x, + /// Parameter y: the multiplier + y: 1, + /// Rest parameters documentation + ..args, +) => { + /// Function body documentation + /// Describes the implementation + ( + x + * y + ) +} + +/// Doc comments in loops: +#let loop_with_docs = { + /// Documentation before the loop + for i in range( + 3, + ) { + /// Doc comment inside loop body + /// Executed on each iteration + [Item #i] + } +} + +/// Doc comments in while loops: +#let while_with_docs = { + /// Counter initialization documentation + let count = 0 + + /// While loop documentation + while ( + count + < 3 + ) { + /// Loop body documentation + count += 1 + } + + count +} + +/// Doc comments in conditional expressions: +#let conditional_with_docs = { + /// Condition documentation + let condition = true + + /// If-else documentation + if condition { + /// True branch documentation + /// Multiple lines in true case + "condition is true" + } else { + /// False branch documentation + "condition is false" + } +} + +/// Doc comments in match expressions: +#let match_with_docs = { + /// Value to match documentation + let value = 1 + + /// Match expression documentation + if ( + value + == 1 + ) { + /// Case 1 documentation + "one" + } else if ( + value + == 2 + ) { + /// Case 2 documentation + "two" + } else { + /// Default case documentation + "other" + } +} + +/// Doc comments in nested function calls: +#let nested_calls_with_docs = { + /// Outer function call documentation + calc.max( + /// First argument documentation + 10, + /// Second argument documentation + /// With multiple lines + 20, + ) +} + +/// Doc comments in show rules: +#show heading: it => { + /// Show rule documentation + /// Explains the heading transformation + text( + weight: "bold", + )[#it.body] +} + +/// Doc comments in set rules context: +#let set_rule_context = { + /// Documentation for the set rule + set text( + size: 12pt, + ) + + /// Content after set rule + [Some text content] +} diff --git a/tests/fixtures/unit/doc/snap/blocks.typ-120.snap b/tests/fixtures/unit/doc/snap/blocks.typ-120.snap new file mode 100644 index 00000000..25e24972 --- /dev/null +++ b/tests/fixtures/unit/doc/snap/blocks.typ-120.snap @@ -0,0 +1,158 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/blocks.typ +--- +/// typstyle: format_doc_comments +// Test cases for doc comments inside various block types + +/// Doc comments in content blocks with code: `#for i in range(3){i}` +#let content_block = [ + /// Doc comment inside content block + /// Should be handled correctly + Some content here + + /// Another doc comment in content + More content + + /// Trailing +] + +/// Doc comments inside code blocks: +#let code_with_docs = { + /// This doc comment is inside a code block + /// It should be formatted properly + let x = 5 + + /// Another doc comment inside the same block + /// With multiple lines + let y = 10 + + x + y + + /// Trailing +} + +/// Doc comments in nested blocks: +#let nested_blocks = { + /// Outer doc comment in code block + let outer = [ + /// Doc comment inside content block which is inside code block + /// This tests deep nesting + Content here + ] + + /// Another doc comment in the code block + outer +} + +/// Doc comments in function definitions: +#let documented_function = ( + /// Doc comment for parameter inside function + x, + y, +) => { + /// Doc comment inside function body + /// Explains the implementation + x + y +} + +/// Doc comments in function parameters: +#let func_with_param_docs = ( + /// Parameter x: the input value + /// Should be a positive number + x, + /// Parameter y: the multiplier + y: 1, + /// Rest parameters documentation + ..args, +) => { + /// Function body documentation + /// Describes the implementation + x * y +} + +/// Doc comments in loops: +#let loop_with_docs = { + /// Documentation before the loop + for i in range(3) { + /// Doc comment inside loop body + /// Executed on each iteration + [Item #i] + } +} + +/// Doc comments in while loops: +#let while_with_docs = { + /// Counter initialization documentation + let count = 0 + + /// While loop documentation + while count < 3 { + /// Loop body documentation + count += 1 + } + + count +} + +/// Doc comments in conditional expressions: +#let conditional_with_docs = { + /// Condition documentation + let condition = true + + /// If-else documentation + if condition { + /// True branch documentation + /// Multiple lines in true case + "condition is true" + } else { + /// False branch documentation + "condition is false" + } +} + +/// Doc comments in match expressions: +#let match_with_docs = { + /// Value to match documentation + let value = 1 + + /// Match expression documentation + if value == 1 { + /// Case 1 documentation + "one" + } else if value == 2 { + /// Case 2 documentation + "two" + } else { + /// Default case documentation + "other" + } +} + +/// Doc comments in nested function calls: +#let nested_calls_with_docs = { + /// Outer function call documentation + calc.max( + /// First argument documentation + 10, + /// Second argument documentation + /// With multiple lines + 20, + ) +} + +/// Doc comments in show rules: +#show heading: it => { + /// Show rule documentation + /// Explains the heading transformation + text(weight: "bold")[#it.body] +} + +/// Doc comments in set rules context: +#let set_rule_context = { + /// Documentation for the set rule + set text(size: 12pt) + + /// Content after set rule + [Some text content] +} diff --git a/tests/fixtures/unit/doc/snap/blocks.typ-40.snap b/tests/fixtures/unit/doc/snap/blocks.typ-40.snap new file mode 100644 index 00000000..25e24972 --- /dev/null +++ b/tests/fixtures/unit/doc/snap/blocks.typ-40.snap @@ -0,0 +1,158 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/blocks.typ +--- +/// typstyle: format_doc_comments +// Test cases for doc comments inside various block types + +/// Doc comments in content blocks with code: `#for i in range(3){i}` +#let content_block = [ + /// Doc comment inside content block + /// Should be handled correctly + Some content here + + /// Another doc comment in content + More content + + /// Trailing +] + +/// Doc comments inside code blocks: +#let code_with_docs = { + /// This doc comment is inside a code block + /// It should be formatted properly + let x = 5 + + /// Another doc comment inside the same block + /// With multiple lines + let y = 10 + + x + y + + /// Trailing +} + +/// Doc comments in nested blocks: +#let nested_blocks = { + /// Outer doc comment in code block + let outer = [ + /// Doc comment inside content block which is inside code block + /// This tests deep nesting + Content here + ] + + /// Another doc comment in the code block + outer +} + +/// Doc comments in function definitions: +#let documented_function = ( + /// Doc comment for parameter inside function + x, + y, +) => { + /// Doc comment inside function body + /// Explains the implementation + x + y +} + +/// Doc comments in function parameters: +#let func_with_param_docs = ( + /// Parameter x: the input value + /// Should be a positive number + x, + /// Parameter y: the multiplier + y: 1, + /// Rest parameters documentation + ..args, +) => { + /// Function body documentation + /// Describes the implementation + x * y +} + +/// Doc comments in loops: +#let loop_with_docs = { + /// Documentation before the loop + for i in range(3) { + /// Doc comment inside loop body + /// Executed on each iteration + [Item #i] + } +} + +/// Doc comments in while loops: +#let while_with_docs = { + /// Counter initialization documentation + let count = 0 + + /// While loop documentation + while count < 3 { + /// Loop body documentation + count += 1 + } + + count +} + +/// Doc comments in conditional expressions: +#let conditional_with_docs = { + /// Condition documentation + let condition = true + + /// If-else documentation + if condition { + /// True branch documentation + /// Multiple lines in true case + "condition is true" + } else { + /// False branch documentation + "condition is false" + } +} + +/// Doc comments in match expressions: +#let match_with_docs = { + /// Value to match documentation + let value = 1 + + /// Match expression documentation + if value == 1 { + /// Case 1 documentation + "one" + } else if value == 2 { + /// Case 2 documentation + "two" + } else { + /// Default case documentation + "other" + } +} + +/// Doc comments in nested function calls: +#let nested_calls_with_docs = { + /// Outer function call documentation + calc.max( + /// First argument documentation + 10, + /// Second argument documentation + /// With multiple lines + 20, + ) +} + +/// Doc comments in show rules: +#show heading: it => { + /// Show rule documentation + /// Explains the heading transformation + text(weight: "bold")[#it.body] +} + +/// Doc comments in set rules context: +#let set_rule_context = { + /// Documentation for the set rule + set text(size: 12pt) + + /// Content after set rule + [Some text content] +} diff --git a/tests/fixtures/unit/doc/snap/blocks.typ-80.snap b/tests/fixtures/unit/doc/snap/blocks.typ-80.snap new file mode 100644 index 00000000..25e24972 --- /dev/null +++ b/tests/fixtures/unit/doc/snap/blocks.typ-80.snap @@ -0,0 +1,158 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/blocks.typ +--- +/// typstyle: format_doc_comments +// Test cases for doc comments inside various block types + +/// Doc comments in content blocks with code: `#for i in range(3){i}` +#let content_block = [ + /// Doc comment inside content block + /// Should be handled correctly + Some content here + + /// Another doc comment in content + More content + + /// Trailing +] + +/// Doc comments inside code blocks: +#let code_with_docs = { + /// This doc comment is inside a code block + /// It should be formatted properly + let x = 5 + + /// Another doc comment inside the same block + /// With multiple lines + let y = 10 + + x + y + + /// Trailing +} + +/// Doc comments in nested blocks: +#let nested_blocks = { + /// Outer doc comment in code block + let outer = [ + /// Doc comment inside content block which is inside code block + /// This tests deep nesting + Content here + ] + + /// Another doc comment in the code block + outer +} + +/// Doc comments in function definitions: +#let documented_function = ( + /// Doc comment for parameter inside function + x, + y, +) => { + /// Doc comment inside function body + /// Explains the implementation + x + y +} + +/// Doc comments in function parameters: +#let func_with_param_docs = ( + /// Parameter x: the input value + /// Should be a positive number + x, + /// Parameter y: the multiplier + y: 1, + /// Rest parameters documentation + ..args, +) => { + /// Function body documentation + /// Describes the implementation + x * y +} + +/// Doc comments in loops: +#let loop_with_docs = { + /// Documentation before the loop + for i in range(3) { + /// Doc comment inside loop body + /// Executed on each iteration + [Item #i] + } +} + +/// Doc comments in while loops: +#let while_with_docs = { + /// Counter initialization documentation + let count = 0 + + /// While loop documentation + while count < 3 { + /// Loop body documentation + count += 1 + } + + count +} + +/// Doc comments in conditional expressions: +#let conditional_with_docs = { + /// Condition documentation + let condition = true + + /// If-else documentation + if condition { + /// True branch documentation + /// Multiple lines in true case + "condition is true" + } else { + /// False branch documentation + "condition is false" + } +} + +/// Doc comments in match expressions: +#let match_with_docs = { + /// Value to match documentation + let value = 1 + + /// Match expression documentation + if value == 1 { + /// Case 1 documentation + "one" + } else if value == 2 { + /// Case 2 documentation + "two" + } else { + /// Default case documentation + "other" + } +} + +/// Doc comments in nested function calls: +#let nested_calls_with_docs = { + /// Outer function call documentation + calc.max( + /// First argument documentation + 10, + /// Second argument documentation + /// With multiple lines + 20, + ) +} + +/// Doc comments in show rules: +#show heading: it => { + /// Show rule documentation + /// Explains the heading transformation + text(weight: "bold")[#it.body] +} + +/// Doc comments in set rules context: +#let set_rule_context = { + /// Documentation for the set rule + set text(size: 12pt) + + /// Content after set rule + [Some text content] +} diff --git a/tests/fixtures/unit/doc/snap/edge-cases.typ-0.snap b/tests/fixtures/unit/doc/snap/edge-cases.typ-0.snap new file mode 100644 index 00000000..5404b560 --- /dev/null +++ b/tests/fixtures/unit/doc/snap/edge-cases.typ-0.snap @@ -0,0 +1,64 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/edge-cases.typ +--- +/// typstyle: format_doc_comments +// Test cases for edge cases in doc comment formatting + +/// Doc comment with mixed indentation and spacing: +/// Extra spaces at the beginning +/// And varying indentation levels +/// Back to normal +/// Deep indentation here +#let mixed_indentation = 1 + +/// Doc comment with empty lines and whitespace: +/// +/// Line with content after empty line +/// +/// +/// Multiple empty lines above #1 +/// +/// Line with trailing spaces #2 +#let whitespace_handling = 2 + +/// +/// Doc comment starting with empty line (1) +#let starts_with_empty = 3 + +/// Doc comment ending with empty line (1) +#let ends_with_empty = 4 + +/// Multiple spaces between words should be handled +/// Tabs and mixed whitespace characters +/// Various spacing patterns throughout +#let multiple_spaces = 5 + +/// Very long line that should be wrapped when wrap_doc_comments is enabled and the line exceeds the doc_comment_width setting +#let long_line = 6 + +/// Doc comment with nested doc-like patterns: +/// This looks like /// a doc comment but isn't +/// Also this: ///not a real doc comment +/// But this is normal text +#let pseudo_nested = 7 + +/// Mixed comment types: #1 +/// Regular doc comment line #2 +// Regular comment (not doc) #(3) +/// Back to doc comment #4 +/// /// Nested doc comment #5 +// Another regular comment #(6) +/// Final doc comment line #7 +#let mixed_comments = 8 + +/// Doc comment with /// inline doc patterns #(1) +/// Text with #0 /// embedded patterns that /// should be handled #(2) +/// Properly without /// breaking formatting #(3) +#let inline_doc_patterns = 9 + +/// Doc comment with line breaks #1 +/// after paragraph breaks #2 +#let with_breaks = 10 + +/// Trailing doc comment diff --git a/tests/fixtures/unit/doc/snap/edge-cases.typ-120.snap b/tests/fixtures/unit/doc/snap/edge-cases.typ-120.snap new file mode 100644 index 00000000..5404b560 --- /dev/null +++ b/tests/fixtures/unit/doc/snap/edge-cases.typ-120.snap @@ -0,0 +1,64 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/edge-cases.typ +--- +/// typstyle: format_doc_comments +// Test cases for edge cases in doc comment formatting + +/// Doc comment with mixed indentation and spacing: +/// Extra spaces at the beginning +/// And varying indentation levels +/// Back to normal +/// Deep indentation here +#let mixed_indentation = 1 + +/// Doc comment with empty lines and whitespace: +/// +/// Line with content after empty line +/// +/// +/// Multiple empty lines above #1 +/// +/// Line with trailing spaces #2 +#let whitespace_handling = 2 + +/// +/// Doc comment starting with empty line (1) +#let starts_with_empty = 3 + +/// Doc comment ending with empty line (1) +#let ends_with_empty = 4 + +/// Multiple spaces between words should be handled +/// Tabs and mixed whitespace characters +/// Various spacing patterns throughout +#let multiple_spaces = 5 + +/// Very long line that should be wrapped when wrap_doc_comments is enabled and the line exceeds the doc_comment_width setting +#let long_line = 6 + +/// Doc comment with nested doc-like patterns: +/// This looks like /// a doc comment but isn't +/// Also this: ///not a real doc comment +/// But this is normal text +#let pseudo_nested = 7 + +/// Mixed comment types: #1 +/// Regular doc comment line #2 +// Regular comment (not doc) #(3) +/// Back to doc comment #4 +/// /// Nested doc comment #5 +// Another regular comment #(6) +/// Final doc comment line #7 +#let mixed_comments = 8 + +/// Doc comment with /// inline doc patterns #(1) +/// Text with #0 /// embedded patterns that /// should be handled #(2) +/// Properly without /// breaking formatting #(3) +#let inline_doc_patterns = 9 + +/// Doc comment with line breaks #1 +/// after paragraph breaks #2 +#let with_breaks = 10 + +/// Trailing doc comment diff --git a/tests/fixtures/unit/doc/snap/edge-cases.typ-40.snap b/tests/fixtures/unit/doc/snap/edge-cases.typ-40.snap new file mode 100644 index 00000000..5404b560 --- /dev/null +++ b/tests/fixtures/unit/doc/snap/edge-cases.typ-40.snap @@ -0,0 +1,64 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/edge-cases.typ +--- +/// typstyle: format_doc_comments +// Test cases for edge cases in doc comment formatting + +/// Doc comment with mixed indentation and spacing: +/// Extra spaces at the beginning +/// And varying indentation levels +/// Back to normal +/// Deep indentation here +#let mixed_indentation = 1 + +/// Doc comment with empty lines and whitespace: +/// +/// Line with content after empty line +/// +/// +/// Multiple empty lines above #1 +/// +/// Line with trailing spaces #2 +#let whitespace_handling = 2 + +/// +/// Doc comment starting with empty line (1) +#let starts_with_empty = 3 + +/// Doc comment ending with empty line (1) +#let ends_with_empty = 4 + +/// Multiple spaces between words should be handled +/// Tabs and mixed whitespace characters +/// Various spacing patterns throughout +#let multiple_spaces = 5 + +/// Very long line that should be wrapped when wrap_doc_comments is enabled and the line exceeds the doc_comment_width setting +#let long_line = 6 + +/// Doc comment with nested doc-like patterns: +/// This looks like /// a doc comment but isn't +/// Also this: ///not a real doc comment +/// But this is normal text +#let pseudo_nested = 7 + +/// Mixed comment types: #1 +/// Regular doc comment line #2 +// Regular comment (not doc) #(3) +/// Back to doc comment #4 +/// /// Nested doc comment #5 +// Another regular comment #(6) +/// Final doc comment line #7 +#let mixed_comments = 8 + +/// Doc comment with /// inline doc patterns #(1) +/// Text with #0 /// embedded patterns that /// should be handled #(2) +/// Properly without /// breaking formatting #(3) +#let inline_doc_patterns = 9 + +/// Doc comment with line breaks #1 +/// after paragraph breaks #2 +#let with_breaks = 10 + +/// Trailing doc comment diff --git a/tests/fixtures/unit/doc/snap/edge-cases.typ-80.snap b/tests/fixtures/unit/doc/snap/edge-cases.typ-80.snap new file mode 100644 index 00000000..5404b560 --- /dev/null +++ b/tests/fixtures/unit/doc/snap/edge-cases.typ-80.snap @@ -0,0 +1,64 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/edge-cases.typ +--- +/// typstyle: format_doc_comments +// Test cases for edge cases in doc comment formatting + +/// Doc comment with mixed indentation and spacing: +/// Extra spaces at the beginning +/// And varying indentation levels +/// Back to normal +/// Deep indentation here +#let mixed_indentation = 1 + +/// Doc comment with empty lines and whitespace: +/// +/// Line with content after empty line +/// +/// +/// Multiple empty lines above #1 +/// +/// Line with trailing spaces #2 +#let whitespace_handling = 2 + +/// +/// Doc comment starting with empty line (1) +#let starts_with_empty = 3 + +/// Doc comment ending with empty line (1) +#let ends_with_empty = 4 + +/// Multiple spaces between words should be handled +/// Tabs and mixed whitespace characters +/// Various spacing patterns throughout +#let multiple_spaces = 5 + +/// Very long line that should be wrapped when wrap_doc_comments is enabled and the line exceeds the doc_comment_width setting +#let long_line = 6 + +/// Doc comment with nested doc-like patterns: +/// This looks like /// a doc comment but isn't +/// Also this: ///not a real doc comment +/// But this is normal text +#let pseudo_nested = 7 + +/// Mixed comment types: #1 +/// Regular doc comment line #2 +// Regular comment (not doc) #(3) +/// Back to doc comment #4 +/// /// Nested doc comment #5 +// Another regular comment #(6) +/// Final doc comment line #7 +#let mixed_comments = 8 + +/// Doc comment with /// inline doc patterns #(1) +/// Text with #0 /// embedded patterns that /// should be handled #(2) +/// Properly without /// breaking formatting #(3) +#let inline_doc_patterns = 9 + +/// Doc comment with line breaks #1 +/// after paragraph breaks #2 +#let with_breaks = 10 + +/// Trailing doc comment diff --git a/tests/fixtures/unit/doc/snap/general.typ-0.snap b/tests/fixtures/unit/doc/snap/general.typ-0.snap new file mode 100644 index 00000000..7efa2bf8 --- /dev/null +++ b/tests/fixtures/unit/doc/snap/general.typ-0.snap @@ -0,0 +1,98 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/general.typ +--- +/// typstyle: +/// format_doc_comments +/// wrap_doc_comments +// Test cases for doc comment formatting - markup parsing and grouping + +/// This +/// is +/// a +/// simple +/// doc +/// comment +/// with +/// valid +/// markup +#let simple = 1 + +/// This +/// has +/// valid +/// markup: +/// *bold* +/// and +/// `code` +/// and +/// _emphasis_ +#let valid_markup = 2 + +/// Doc +/// comment +/// with +/// code +/// expressions: +/// #( +/// 1 +/// + 2 +/// ) +/// and +/// #let x = 5 +/// Also +/// has +/// badly +/// formatted +/// code: +/// #( +/// a +/// + b +/// * c +/// / d +/// ) +/// and +/// #calc.pow( +/// 2, +/// 3, +/// ) +#let with_code_expressions = 3 + +/// This has invalid markup: *unclosed strong +/// and broken `code span +/// Should fallback to plain text formatting +#let invalid_markup = 4 + +/// Code +/// with +/// function +/// calls: +/// #text( +/// size: 12pt, +/// )[Hello] +/// and +/// #rect( +/// width: 100pt, +/// height: 50pt, +/// ) +/// Should +/// be +/// formatted +/// properly +#let with_function_calls = 6 + +/// Very +/// long +/// comment +/// that +/// should +/// be +/// wrapped +/// when +/// wrap_doc_comments +/// is +/// enabled +/// and +/// exceeds +/// doc_comment_width +#let long_comment = 7 diff --git a/tests/fixtures/unit/doc/snap/general.typ-120.snap b/tests/fixtures/unit/doc/snap/general.typ-120.snap new file mode 100644 index 00000000..9f6396fa --- /dev/null +++ b/tests/fixtures/unit/doc/snap/general.typ-120.snap @@ -0,0 +1,27 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/general.typ +--- +/// typstyle: format_doc_comments wrap_doc_comments +// Test cases for doc comment formatting - markup parsing and grouping + +/// This is a simple doc comment with valid markup +#let simple = 1 + +/// This has valid markup: *bold* and `code` and _emphasis_ +#let valid_markup = 2 + +/// Doc comment with code expressions: #(1 + 2) and #let x = 5 Also has badly formatted code: #(a + b * c / d) and +/// #calc.pow(2, 3) +#let with_code_expressions = 3 + +/// This has invalid markup: *unclosed strong +/// and broken `code span +/// Should fallback to plain text formatting +#let invalid_markup = 4 + +/// Code with function calls: #text(size: 12pt)[Hello] and #rect(width: 100pt, height: 50pt) Should be formatted properly +#let with_function_calls = 6 + +/// Very long comment that should be wrapped when wrap_doc_comments is enabled and exceeds doc_comment_width +#let long_comment = 7 diff --git a/tests/fixtures/unit/doc/snap/general.typ-40.snap b/tests/fixtures/unit/doc/snap/general.typ-40.snap new file mode 100644 index 00000000..2c9b10cb --- /dev/null +++ b/tests/fixtures/unit/doc/snap/general.typ-40.snap @@ -0,0 +1,40 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/general.typ +--- +/// typstyle: format_doc_comments +/// wrap_doc_comments +// Test cases for doc comment formatting - markup parsing and grouping + +/// This is a simple doc comment with valid +/// markup +#let simple = 1 + +/// This has valid markup: *bold* and `code` +/// and _emphasis_ +#let valid_markup = 2 + +/// Doc comment with code expressions: #( +/// 1 + 2 +/// ) and #let x = 5 Also has badly +/// formatted code: #(a + b * c / d) and +/// #calc.pow(2, 3) +#let with_code_expressions = 3 + +/// This has invalid markup: *unclosed strong +/// and broken `code span +/// Should fallback to plain text formatting +#let invalid_markup = 4 + +/// Code with function calls: #text( +/// size: 12pt, +/// )[Hello] and #rect( +/// width: 100pt, +/// height: 50pt, +/// ) Should be formatted properly +#let with_function_calls = 6 + +/// Very long comment that should be wrapped +/// when wrap_doc_comments is enabled and +/// exceeds doc_comment_width +#let long_comment = 7 diff --git a/tests/fixtures/unit/doc/snap/general.typ-80.snap b/tests/fixtures/unit/doc/snap/general.typ-80.snap new file mode 100644 index 00000000..40d6555a --- /dev/null +++ b/tests/fixtures/unit/doc/snap/general.typ-80.snap @@ -0,0 +1,31 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/general.typ +--- +/// typstyle: format_doc_comments wrap_doc_comments +// Test cases for doc comment formatting - markup parsing and grouping + +/// This is a simple doc comment with valid markup +#let simple = 1 + +/// This has valid markup: *bold* and `code` and _emphasis_ +#let valid_markup = 2 + +/// Doc comment with code expressions: #(1 + 2) and #let x = 5 Also has badly +/// formatted code: #(a + b * c / d) and #calc.pow(2, 3) +#let with_code_expressions = 3 + +/// This has invalid markup: *unclosed strong +/// and broken `code span +/// Should fallback to plain text formatting +#let invalid_markup = 4 + +/// Code with function calls: #text(size: 12pt)[Hello] and #rect( +/// width: 100pt, +/// height: 50pt, +/// ) Should be formatted properly +#let with_function_calls = 6 + +/// Very long comment that should be wrapped when wrap_doc_comments is enabled and +/// exceeds doc_comment_width +#let long_comment = 7 diff --git a/tests/fixtures/unit/doc/snap/nested.typ-0.snap b/tests/fixtures/unit/doc/snap/nested.typ-0.snap new file mode 100644 index 00000000..6545861c --- /dev/null +++ b/tests/fixtures/unit/doc/snap/nested.typ-0.snap @@ -0,0 +1,69 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/nested.typ +--- +/// typstyle: format_doc_comments +// Test cases for nested doc comments + +/// Simple nested doc comment: +/// /// This is nested level 1 +/// Back to normal level +#let simple_nested = 1 + +/// Multiple levels of nesting: +/// /// Level 1 nested +/// /// /// Level 2 nested +/// /// /// /// Level 3 nested +/// /// Back to level 1 +/// Back to normal level +#let deep_nested = 2 + +/// Mixed nesting patterns: +/// Normal doc comment +/// /// Nested comment +/// Normal again +/// /// /// Double nested +/// /// Back to single nested +/// Normal conclusion +#let mixed_nesting = 3 + +/// Nested with empty lines: +/// /// Nested comment +/// /// Another nested after empty line +/// /// Final nested comment +#let nested_with_empty = 4 + +/// Irregular nesting patterns: +/// ///Nested without space +/// /// Nested with space +/// /// Nested with extra spaces +/// //// Four slashes +/// ///// Five slashes +#let irregular_nesting = 5 + +/// Nested comments in consecutive blocks: +/// /// First nested +/// /// Second nested +/// Normal comment +#let nested_in_consecutive = 6 + +/// Another consecutive block with nesting: +/// Normal comment +/// /// Nested comment +/// /// Another nested +#let another_nested_consecutive = 7 + +/// Complex nesting scenario: +/// This is a normal doc comment line +/// /// This is nested level 1 +/// /// /// This is nested level 2 +/// /// /// Another line at level 2 +/// /// Back to level 1 +/// /// /// Deep again to level 2 +/// /// /// /// Even deeper to level 3 +/// /// /// Back to level 2 +/// /// Back to level 1 +/// Back to normal level +/// /// One more nested +/// Final normal line +#let complex_nesting = 8 diff --git a/tests/fixtures/unit/doc/snap/nested.typ-120.snap b/tests/fixtures/unit/doc/snap/nested.typ-120.snap new file mode 100644 index 00000000..6545861c --- /dev/null +++ b/tests/fixtures/unit/doc/snap/nested.typ-120.snap @@ -0,0 +1,69 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/nested.typ +--- +/// typstyle: format_doc_comments +// Test cases for nested doc comments + +/// Simple nested doc comment: +/// /// This is nested level 1 +/// Back to normal level +#let simple_nested = 1 + +/// Multiple levels of nesting: +/// /// Level 1 nested +/// /// /// Level 2 nested +/// /// /// /// Level 3 nested +/// /// Back to level 1 +/// Back to normal level +#let deep_nested = 2 + +/// Mixed nesting patterns: +/// Normal doc comment +/// /// Nested comment +/// Normal again +/// /// /// Double nested +/// /// Back to single nested +/// Normal conclusion +#let mixed_nesting = 3 + +/// Nested with empty lines: +/// /// Nested comment +/// /// Another nested after empty line +/// /// Final nested comment +#let nested_with_empty = 4 + +/// Irregular nesting patterns: +/// ///Nested without space +/// /// Nested with space +/// /// Nested with extra spaces +/// //// Four slashes +/// ///// Five slashes +#let irregular_nesting = 5 + +/// Nested comments in consecutive blocks: +/// /// First nested +/// /// Second nested +/// Normal comment +#let nested_in_consecutive = 6 + +/// Another consecutive block with nesting: +/// Normal comment +/// /// Nested comment +/// /// Another nested +#let another_nested_consecutive = 7 + +/// Complex nesting scenario: +/// This is a normal doc comment line +/// /// This is nested level 1 +/// /// /// This is nested level 2 +/// /// /// Another line at level 2 +/// /// Back to level 1 +/// /// /// Deep again to level 2 +/// /// /// /// Even deeper to level 3 +/// /// /// Back to level 2 +/// /// Back to level 1 +/// Back to normal level +/// /// One more nested +/// Final normal line +#let complex_nesting = 8 diff --git a/tests/fixtures/unit/doc/snap/nested.typ-40.snap b/tests/fixtures/unit/doc/snap/nested.typ-40.snap new file mode 100644 index 00000000..6545861c --- /dev/null +++ b/tests/fixtures/unit/doc/snap/nested.typ-40.snap @@ -0,0 +1,69 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/nested.typ +--- +/// typstyle: format_doc_comments +// Test cases for nested doc comments + +/// Simple nested doc comment: +/// /// This is nested level 1 +/// Back to normal level +#let simple_nested = 1 + +/// Multiple levels of nesting: +/// /// Level 1 nested +/// /// /// Level 2 nested +/// /// /// /// Level 3 nested +/// /// Back to level 1 +/// Back to normal level +#let deep_nested = 2 + +/// Mixed nesting patterns: +/// Normal doc comment +/// /// Nested comment +/// Normal again +/// /// /// Double nested +/// /// Back to single nested +/// Normal conclusion +#let mixed_nesting = 3 + +/// Nested with empty lines: +/// /// Nested comment +/// /// Another nested after empty line +/// /// Final nested comment +#let nested_with_empty = 4 + +/// Irregular nesting patterns: +/// ///Nested without space +/// /// Nested with space +/// /// Nested with extra spaces +/// //// Four slashes +/// ///// Five slashes +#let irregular_nesting = 5 + +/// Nested comments in consecutive blocks: +/// /// First nested +/// /// Second nested +/// Normal comment +#let nested_in_consecutive = 6 + +/// Another consecutive block with nesting: +/// Normal comment +/// /// Nested comment +/// /// Another nested +#let another_nested_consecutive = 7 + +/// Complex nesting scenario: +/// This is a normal doc comment line +/// /// This is nested level 1 +/// /// /// This is nested level 2 +/// /// /// Another line at level 2 +/// /// Back to level 1 +/// /// /// Deep again to level 2 +/// /// /// /// Even deeper to level 3 +/// /// /// Back to level 2 +/// /// Back to level 1 +/// Back to normal level +/// /// One more nested +/// Final normal line +#let complex_nesting = 8 diff --git a/tests/fixtures/unit/doc/snap/nested.typ-80.snap b/tests/fixtures/unit/doc/snap/nested.typ-80.snap new file mode 100644 index 00000000..6545861c --- /dev/null +++ b/tests/fixtures/unit/doc/snap/nested.typ-80.snap @@ -0,0 +1,69 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/nested.typ +--- +/// typstyle: format_doc_comments +// Test cases for nested doc comments + +/// Simple nested doc comment: +/// /// This is nested level 1 +/// Back to normal level +#let simple_nested = 1 + +/// Multiple levels of nesting: +/// /// Level 1 nested +/// /// /// Level 2 nested +/// /// /// /// Level 3 nested +/// /// Back to level 1 +/// Back to normal level +#let deep_nested = 2 + +/// Mixed nesting patterns: +/// Normal doc comment +/// /// Nested comment +/// Normal again +/// /// /// Double nested +/// /// Back to single nested +/// Normal conclusion +#let mixed_nesting = 3 + +/// Nested with empty lines: +/// /// Nested comment +/// /// Another nested after empty line +/// /// Final nested comment +#let nested_with_empty = 4 + +/// Irregular nesting patterns: +/// ///Nested without space +/// /// Nested with space +/// /// Nested with extra spaces +/// //// Four slashes +/// ///// Five slashes +#let irregular_nesting = 5 + +/// Nested comments in consecutive blocks: +/// /// First nested +/// /// Second nested +/// Normal comment +#let nested_in_consecutive = 6 + +/// Another consecutive block with nesting: +/// Normal comment +/// /// Nested comment +/// /// Another nested +#let another_nested_consecutive = 7 + +/// Complex nesting scenario: +/// This is a normal doc comment line +/// /// This is nested level 1 +/// /// /// This is nested level 2 +/// /// /// Another line at level 2 +/// /// Back to level 1 +/// /// /// Deep again to level 2 +/// /// /// /// Even deeper to level 3 +/// /// /// Back to level 2 +/// /// Back to level 1 +/// Back to normal level +/// /// One more nested +/// Final normal line +#let complex_nesting = 8 diff --git a/tests/fixtures/unit/doc/snap/wrapping.typ-0.snap b/tests/fixtures/unit/doc/snap/wrapping.typ-0.snap new file mode 100644 index 00000000..da809c22 --- /dev/null +++ b/tests/fixtures/unit/doc/snap/wrapping.typ-0.snap @@ -0,0 +1,103 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/wrapping.typ +--- +/// typstyle: +/// format_doc_comments +/// wrap_doc_comments +// Test cases for doc comment wrapping behavior + +/// Short +/// comment +/// that +/// should +/// not +/// wrap +#let short = 1 + +/// This +/// is +/// a +/// moderately +/// long +/// comment +/// that +/// might +/// wrap +/// depending +/// on +/// the +/// doc_comment_width +/// setting +#let moderate = 2 + +/// This +/// is +/// an +/// extremely +/// long +/// comment +/// that +/// will +/// definitely +/// need +/// to +/// be +/// wrapped +/// when +/// wrap_doc_comments +/// is +/// enabled +/// because +/// it +/// contains +/// far +/// too +/// much +/// text +/// to +/// fit +/// on +/// a +/// single +/// line +#let very_long = 3 + +/// Comment +/// with +/// long +/// words: +/// Antidisestablishmentarianism +/// and +/// Floccinaucinihilipilification +/// These +/// unbreakable +/// words +/// need +/// special +/// handling +#let long_words = 4 + +/// Comment +/// with +/// multiple +/// sentences. +/// This +/// is +/// the +/// first +/// sentence. +/// This +/// is +/// the +/// second +/// sentence. +/// This +/// is +/// the +/// third +/// sentence +/// that +/// demonstrates +/// wrapping. +#let multiple_sentences = 5 diff --git a/tests/fixtures/unit/doc/snap/wrapping.typ-120.snap b/tests/fixtures/unit/doc/snap/wrapping.typ-120.snap new file mode 100644 index 00000000..0297ed9e --- /dev/null +++ b/tests/fixtures/unit/doc/snap/wrapping.typ-120.snap @@ -0,0 +1,24 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/wrapping.typ +--- +/// typstyle: format_doc_comments wrap_doc_comments +// Test cases for doc comment wrapping behavior + +/// Short comment that should not wrap +#let short = 1 + +/// This is a moderately long comment that might wrap depending on the doc_comment_width setting +#let moderate = 2 + +/// This is an extremely long comment that will definitely need to be wrapped when wrap_doc_comments is enabled because it +/// contains far too much text to fit on a single line +#let very_long = 3 + +/// Comment with long words: Antidisestablishmentarianism and Floccinaucinihilipilification These unbreakable words need +/// special handling +#let long_words = 4 + +/// Comment with multiple sentences. This is the first sentence. This is the second sentence. This is the third sentence +/// that demonstrates wrapping. +#let multiple_sentences = 5 diff --git a/tests/fixtures/unit/doc/snap/wrapping.typ-40.snap b/tests/fixtures/unit/doc/snap/wrapping.typ-40.snap new file mode 100644 index 00000000..5fa3015b --- /dev/null +++ b/tests/fixtures/unit/doc/snap/wrapping.typ-40.snap @@ -0,0 +1,34 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/wrapping.typ +--- +/// typstyle: format_doc_comments +/// wrap_doc_comments +// Test cases for doc comment wrapping behavior + +/// Short comment that should not wrap +#let short = 1 + +/// This is a moderately long comment that +/// might wrap depending on the +/// doc_comment_width setting +#let moderate = 2 + +/// This is an extremely long comment that +/// will definitely need to be wrapped when +/// wrap_doc_comments is enabled because it +/// contains far too much text to fit on a +/// single line +#let very_long = 3 + +/// Comment with long words: +/// Antidisestablishmentarianism and +/// Floccinaucinihilipilification These +/// unbreakable words need special handling +#let long_words = 4 + +/// Comment with multiple sentences. This is +/// the first sentence. This is the second +/// sentence. This is the third sentence +/// that demonstrates wrapping. +#let multiple_sentences = 5 diff --git a/tests/fixtures/unit/doc/snap/wrapping.typ-80.snap b/tests/fixtures/unit/doc/snap/wrapping.typ-80.snap new file mode 100644 index 00000000..64c7b506 --- /dev/null +++ b/tests/fixtures/unit/doc/snap/wrapping.typ-80.snap @@ -0,0 +1,26 @@ +--- +source: tests/src/unit.rs +input_file: tests/fixtures/unit/doc/wrapping.typ +--- +/// typstyle: format_doc_comments wrap_doc_comments +// Test cases for doc comment wrapping behavior + +/// Short comment that should not wrap +#let short = 1 + +/// This is a moderately long comment that might wrap depending on the +/// doc_comment_width setting +#let moderate = 2 + +/// This is an extremely long comment that will definitely need to be wrapped when +/// wrap_doc_comments is enabled because it contains far too much text to fit on a +/// single line +#let very_long = 3 + +/// Comment with long words: Antidisestablishmentarianism and +/// Floccinaucinihilipilification These unbreakable words need special handling +#let long_words = 4 + +/// Comment with multiple sentences. This is the first sentence. This is the second +/// sentence. This is the third sentence that demonstrates wrapping. +#let multiple_sentences = 5 diff --git a/tests/fixtures/unit/doc/wrapping.typ b/tests/fixtures/unit/doc/wrapping.typ new file mode 100644 index 00000000..09b40d89 --- /dev/null +++ b/tests/fixtures/unit/doc/wrapping.typ @@ -0,0 +1,18 @@ +/// typstyle: format_doc_comments wrap_doc_comments +// Test cases for doc comment wrapping behavior + +/// Short comment that should not wrap +#let short = 1 + +/// This is a moderately long comment that might wrap depending on the doc_comment_width setting +#let moderate = 2 + +/// This is an extremely long comment that will definitely need to be wrapped when wrap_doc_comments is enabled because it contains far too much text to fit on a single line +#let very_long = 3 + +/// Comment with long words: Antidisestablishmentarianism and Floccinaucinihilipilification +/// These unbreakable words need special handling +#let long_words = 4 + +/// Comment with multiple sentences. This is the first sentence. This is the second sentence. This is the third sentence that demonstrates wrapping. +#let multiple_sentences = 5 diff --git a/tests/src/common/directive.rs b/tests/src/common/directive.rs index 53d73a55..8013dd6d 100644 --- a/tests/src/common/directive.rs +++ b/tests/src/common/directive.rs @@ -80,6 +80,19 @@ pub fn parse_directives(content: &str) -> Result { .with_context(|| format!("Invalid tab_spaces value: {v}"))?; } } + "format_doc_comments" => { + config.format_doc_comments = value != Some("false"); + } + "wrap_doc_comments" => { + config.wrap_doc_comments = value != Some("false"); + } + "doc_comment_width" => { + if let Some(v) = value { + config.doc_comment_width = v + .parse() + .with_context(|| format!("Invalid doc_comment_width value: {v}"))?; + } + } _ => bail!("unknown directive: {key}"), } Ok(()) diff --git a/tests/src/unit.rs b/tests/src/unit.rs index 749bf3db..2929db7e 100644 --- a/tests/src/unit.rs +++ b/tests/src/unit.rs @@ -3,7 +3,7 @@ use std::{env, error::Error, fs, path::Path}; use insta::internals::Content; use libtest_mimic::{Failed, Trial}; use typst_syntax::Source; -use typstyle_core::Typstyle; +use typstyle_core::{Config, Typstyle}; use crate::common::{fixtures_dir, read_source_with_options}; @@ -103,7 +103,6 @@ pub fn collect_tests() -> Result, Box> { fn check_snapshot(path: &Path, width: usize) -> Result<(), Failed> { let (source, opt) = read_source_with_options(path)?; - let mut cfg = opt.config; let mut settings = insta::Settings::clone_current(); settings.set_prepend_module_to_snapshot(false); @@ -118,8 +117,8 @@ fn check_snapshot(path: &Path, width: usize) -> Result<(), Failed> { if source.root().erroneous() { insta::assert_snapshot!(snap_name, ""); } else { - cfg.max_width = width; - let mut formatted = Typstyle::new(cfg).format_source(source).render().unwrap(); + let t = Typstyle::new(config_width(opt.config, width)); + let mut formatted = t.format_source(source).render().unwrap(); if formatted.starts_with('\n') { formatted.insert_str(0, "// DUMMY\n"); } @@ -135,13 +134,11 @@ fn check_snapshot(path: &Path, width: usize) -> Result<(), Failed> { fn check_convergence(path: &Path, width: usize) -> Result<(), Failed> { let (source, opt) = read_source_with_options(path)?; - let mut cfg = opt.config; if source.root().erroneous() { return Ok(()); } - cfg.max_width = width; - let t = Typstyle::new(cfg); + let t = Typstyle::new(config_width(opt.config, width)); let mut first_pass = t.format_source(source).render()?; for i in 0..=opt.relax_convergence { let new_source = Source::detached(&first_pass); @@ -177,13 +174,11 @@ fn check_output_consistency(path: &Path, width: usize) -> Result<(), Failed> { use typstyle_consistency::{ErrorSink, FormattedSources, FormatterHarness}; let (source, opt) = read_source_with_options(path)?; - let mut cfg = opt.config; if source.root().erroneous() { return Ok(()); } - cfg.max_width = width; - let t = Typstyle::new(cfg); + let t = Typstyle::new(config_width(opt.config, width)); let mut err_sink = ErrorSink::new(format!("consistency {}", path.display())); @@ -209,3 +204,9 @@ fn check_output_consistency(path: &Path, width: usize) -> Result<(), Failed> { Err(err_sink.into()) } } + +fn config_width(mut config: Config, max_width: usize) -> Config { + config.max_width = max_width; + config.doc_comment_width = max_width; + config +}