From 2fb09e71a527481c518f1c0152243fa6f1b1606f Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 18 Jul 2025 00:28:29 +0100 Subject: [PATCH 1/5] Add test for long ellipsis sequences --- README.md | 4 ++- docs/module-relationships.md | 12 +++++-- src/ellipsis.rs | 67 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ src/main.rs | 15 +++++++- tests/integration.rs | 12 +++++++ 6 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 src/ellipsis.rs diff --git a/README.md b/README.md index 79657af5..cf861815 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ cargo install --path . ## Command-line usage ```bash -mdtablefix [--wrap] [--renumber] [--breaks] [--in-place] [FILE...] +mdtablefix [--wrap] [--renumber] [--breaks] [--ellipsis] [--in-place] [FILE...] ``` - With file paths provided, the corrected tables are printed to stdout. @@ -32,6 +32,8 @@ mdtablefix [--wrap] [--renumber] [--breaks] [--in-place] [FILE...] `--renumber`. - Use `--breaks` to normalize thematic breaks to a line of 70 underscores (configurable via the `THEMATIC_BREAK_LEN` constant). +- Use `--ellipsis` to replace sequences of three dots with the ellipsis + character. - Use `--in-place` to overwrite files. - If no files are supplied, input is read from stdin and results are written to stdout. diff --git a/docs/module-relationships.md b/docs/module-relationships.md index dc9c8747..26c092e2 100644 --- a/docs/module-relationships.md +++ b/docs/module-relationships.md @@ -32,6 +32,10 @@ classDiagram +format_breaks() +THEMATIC_BREAK_LEN } + class ellipsis { + <> + +replace_ellipsis() + } class process { <> +process_stream() @@ -47,12 +51,14 @@ classDiagram lib --> wrap lib --> lists lib --> breaks + lib --> ellipsis lib --> process lib --> io html ..> wrap : uses is_fence table ..> reflow : uses parse_rows, etc. lists ..> wrap : uses is_fence breaks ..> wrap : uses is_fence + ellipsis ..> wrap : uses tokenize_markdown process ..> html : uses convert_html_tables process ..> table : uses reflow_table process ..> wrap : uses wrap_text, is_fence @@ -60,6 +66,6 @@ classDiagram ``` The `lib` module re-exports the public API from the other modules. The -`process` module provides streaming helpers that combine the lower-level -functions. The `io` module handles filesystem operations, delegating the text -processing to `process`. +`ellipsis` module performs text normalisation. The `process` module provides +streaming helpers that combine the lower-level functions. The `io` module +handles filesystem operations, delegating the text processing to `process`. diff --git a/src/ellipsis.rs b/src/ellipsis.rs new file mode 100644 index 00000000..f2f46be0 --- /dev/null +++ b/src/ellipsis.rs @@ -0,0 +1,67 @@ +//! Replace sequences of three dots with the ellipsis character. +//! +//! This module provides a helper for normalising textual ellipses. It respects +//! fenced code blocks and inline code spans so that code is left untouched. + +use crate::wrap::{is_fence, tokenize_markdown}; + +/// Replace `...` with `…` outside code spans and fences. +#[must_use] +pub fn replace_ellipsis(lines: &[String]) -> Vec { + let mut out = Vec::with_capacity(lines.len()); + let mut in_code = false; + for line in lines { + if is_fence(line) { + in_code = !in_code; + out.push(line.clone()); + continue; + } + if in_code { + out.push(line.clone()); + continue; + } + let mut replaced = String::new(); + for token in tokenize_markdown(line) { + if token.starts_with('`') { + replaced.push_str(&token); + } else { + replaced.push_str(&token.replace("...", "…")); + } + } + out.push(replaced); + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn replaces_simple_text() { + let input = vec!["wait...".to_string()]; + let expected = vec!["wait…".to_string()]; + assert_eq!(replace_ellipsis(&input), expected); + } + + #[test] + fn ignores_code_spans() { + let input = vec!["a `b...` c".to_string()]; + let expected = input.clone(); + assert_eq!(replace_ellipsis(&input), expected); + } + + #[test] + fn ignores_fenced_blocks() { + let input = vec!["```".to_string(), "...".to_string(), "```".to_string()]; + let expected = input.clone(); + assert_eq!(replace_ellipsis(&input), expected); + } + + #[test] + fn replaces_long_sequences() { + let input = vec![".... ..... ...... .......".to_string()]; + let expected = vec!["…. ….. …… …….".to_string()]; + assert_eq!(replace_ellipsis(&input), expected); + } +} diff --git a/src/lib.rs b/src/lib.rs index 2096080a..a531cacc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,10 +6,12 @@ //! - `wrap` for paragraph wrapping. //! - `lists` for renumbering ordered lists. //! - `breaks` for thematic break formatting. +//! - `ellipsis` for normalising textual ellipses. //! - `process` for stream processing. //! - `io` for file helpers. pub mod breaks; +pub mod ellipsis; mod html; pub mod io; pub mod lists; diff --git a/src/main.rs b/src/main.rs index 103d8ac7..8617fb42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,13 @@ use std::{ }; use clap::Parser; -use mdtablefix::{format_breaks, process_stream, process_stream_no_wrap, renumber_lists}; +use mdtablefix::{ + ellipsis::replace_ellipsis, + format_breaks, + process_stream, + process_stream_no_wrap, + renumber_lists, +}; #[derive(Parser)] #[command(about = "Reflow broken markdown tables")] @@ -21,6 +27,7 @@ struct Cli { } #[derive(clap::Args, Clone, Copy)] +#[allow(clippy::struct_excessive_bools)] // CLI exposes four independent flags struct FormatOpts { /// Wrap paragraphs and list items to 80 columns #[arg(long = "wrap")] @@ -31,6 +38,9 @@ struct FormatOpts { /// Reformat thematic breaks as underscores #[arg(long = "breaks")] breaks: bool, + /// Replace "..." with the ellipsis character + #[arg(long = "ellipsis")] + ellipsis: bool, } fn process_lines(lines: &[String], opts: FormatOpts) -> Vec { @@ -48,6 +58,9 @@ fn process_lines(lines: &[String], opts: FormatOpts) -> Vec { .map(Cow::into_owned) .collect(); } + if opts.ellipsis { + out = replace_ellipsis(&out); + } out } diff --git a/tests/integration.rs b/tests/integration.rs index 1a80a138..37816d2e 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -952,3 +952,15 @@ fn test_cli_breaks_option() { format!("{}\n", "_".repeat(THEMATIC_BREAK_LEN)) ); } + +#[test] +fn test_cli_ellipsis_option() { + let output = Command::cargo_bin("mdtablefix") + .unwrap() + .arg("--ellipsis") + .write_stdin("foo...\n") + .output() + .unwrap(); + assert!(output.status.success()); + assert_eq!(String::from_utf8_lossy(&output.stdout), "foo…\n"); +} From 1d5dfc2aa812e106fb7c0d68629125a51ab980e9 Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 18 Jul 2025 00:54:35 +0100 Subject: [PATCH 2/5] Integrate ellipsis into processing --- README.md | 7 +++-- docs/module-relationships.md | 6 ++-- src/ellipsis.rs | 49 ++++++++++++++++++------------- src/lib.rs | 2 +- src/main.rs | 17 ++--------- src/process.rs | 18 +++++++++--- src/wrap.rs | 57 ++++++++++++++++++++++++++++++++++-- tests/integration.rs | 30 +++++++++++++++++++ 8 files changed, 138 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index cf861815..a48662a0 100644 --- a/README.md +++ b/README.md @@ -64,19 +64,20 @@ The crate exposes helper functions so you can integrate the table reflow logic in your own project. ```rust -use mdtablefix::{process_stream, rewrite}; +use mdtablefix::{process_stream_opts, rewrite}; use std::path::Path; fn main() -> std::io::Result<()> { let lines = vec!["|A|B|".to_string(), "|1|2|".to_string()]; - let fixed = process_stream(&lines); + let fixed = process_stream_opts(&lines, true, true); println!("{}", fixed.join("\n")); rewrite(Path::new("table.md"))?; Ok(()) } ``` -- `process_stream(&[String]) -> Vec` rewrites tables in memory. +- `process_stream_opts(&[String], bool, bool) -> Vec` rewrites tables + in memory with optional wrapping and ellipsis replacement. - `rewrite(&Path) -> std::io::Result<()>` updates a Markdown file on disk. ## HTML table support diff --git a/docs/module-relationships.md b/docs/module-relationships.md index 26c092e2..2443a05d 100644 --- a/docs/module-relationships.md +++ b/docs/module-relationships.md @@ -62,10 +62,12 @@ classDiagram process ..> html : uses convert_html_tables process ..> table : uses reflow_table process ..> wrap : uses wrap_text, is_fence + process ..> ellipsis : uses replace_ellipsis io ..> process : uses process_stream, process_stream_no_wrap ``` The `lib` module re-exports the public API from the other modules. The `ellipsis` module performs text normalisation. The `process` module provides -streaming helpers that combine the lower-level functions. The `io` module -handles filesystem operations, delegating the text processing to `process`. +streaming helpers that combine the lower-level functions, including ellipsis +replacement. The `io` module handles filesystem operations, delegating the text +processing to `process`. diff --git a/src/ellipsis.rs b/src/ellipsis.rs index f2f46be0..97e37edf 100644 --- a/src/ellipsis.rs +++ b/src/ellipsis.rs @@ -3,34 +3,41 @@ //! This module provides a helper for normalising textual ellipses. It respects //! fenced code blocks and inline code spans so that code is left untouched. -use crate::wrap::{is_fence, tokenize_markdown}; +use regex::Regex; + +use crate::wrap::{Token, tokenize_markdown}; + +static DOT_RE: std::sync::LazyLock = + std::sync::LazyLock::new(|| Regex::new(r"\.{3,}").unwrap()); /// Replace `...` with `…` outside code spans and fences. #[must_use] pub fn replace_ellipsis(lines: &[String]) -> Vec { - let mut out = Vec::with_capacity(lines.len()); - let mut in_code = false; - for line in lines { - if is_fence(line) { - in_code = !in_code; - out.push(line.clone()); - continue; - } - if in_code { - out.push(line.clone()); - continue; - } - let mut replaced = String::new(); - for token in tokenize_markdown(line) { - if token.starts_with('`') { - replaced.push_str(&token); - } else { - replaced.push_str(&token.replace("...", "…")); + let joined = lines.join("\n"); + let mut out = String::new(); + for token in tokenize_markdown(&joined) { + match token { + Token::Text(t) => { + let replaced = DOT_RE.replace_all(t, |caps: ®ex::Captures<'_>| { + let len = caps[0].len(); + let ellipses = "…".repeat(len / 3); + let leftover = ".".repeat(len % 3); + format!("{ellipses}{leftover}") + }); + out.push_str(&replaced); + } + Token::Code(c) => { + out.push('`'); + out.push_str(c); + out.push('`'); + } + Token::Fence(f) => { + out.push_str(f); } + Token::Newline => out.push('\n'), } - out.push(replaced); } - out + out.split('\n').map(str::to_string).collect() } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index a531cacc..ba07b102 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,6 @@ pub use breaks::{THEMATIC_BREAK_LEN, format_breaks}; pub use html::convert_html_tables; pub use io::{rewrite, rewrite_no_wrap}; pub use lists::renumber_lists; -pub use process::{process_stream, process_stream_no_wrap}; +pub use process::{process_stream, process_stream_no_wrap, process_stream_opts}; pub use table::{reflow_table, split_cells}; pub use wrap::{is_fence, wrap_text}; diff --git a/src/main.rs b/src/main.rs index 8617fb42..bd752366 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,13 +6,7 @@ use std::{ }; use clap::Parser; -use mdtablefix::{ - ellipsis::replace_ellipsis, - format_breaks, - process_stream, - process_stream_no_wrap, - renumber_lists, -}; +use mdtablefix::{format_breaks, process_stream_opts, renumber_lists}; #[derive(Parser)] #[command(about = "Reflow broken markdown tables")] @@ -44,11 +38,7 @@ struct FormatOpts { } fn process_lines(lines: &[String], opts: FormatOpts) -> Vec { - let mut out = if opts.wrap { - process_stream(lines) - } else { - process_stream_no_wrap(lines) - }; + let mut out = process_stream_opts(lines, opts.wrap, opts.ellipsis); if opts.renumber { out = renumber_lists(&out); } @@ -58,9 +48,6 @@ fn process_lines(lines: &[String], opts: FormatOpts) -> Vec { .map(Cow::into_owned) .collect(); } - if opts.ellipsis { - out = replace_ellipsis(&out); - } out } diff --git a/src/process.rs b/src/process.rs index 55e0294e..e0b6a1ba 100644 --- a/src/process.rs +++ b/src/process.rs @@ -1,13 +1,14 @@ //! High-level Markdown stream processing. use crate::{ + ellipsis::replace_ellipsis, html::convert_html_tables, table::reflow_table, wrap::{self, wrap_text}, }; #[must_use] -pub fn process_stream_inner(lines: &[String], wrap: bool) -> Vec { +pub fn process_stream_inner(lines: &[String], wrap: bool, ellipsis: bool) -> Vec { let pre = convert_html_tables(lines); let mut out = Vec::new(); @@ -69,15 +70,24 @@ pub fn process_stream_inner(lines: &[String], wrap: bool) -> Vec { } } - if wrap { wrap_text(&out, 80) } else { out } + let mut out = if wrap { wrap_text(&out, 80) } else { out }; + if ellipsis { + out = replace_ellipsis(&out); + } + out } #[must_use] -pub fn process_stream(lines: &[String]) -> Vec { process_stream_inner(lines, true) } +pub fn process_stream(lines: &[String]) -> Vec { process_stream_inner(lines, true, false) } #[must_use] pub fn process_stream_no_wrap(lines: &[String]) -> Vec { - process_stream_inner(lines, false) + process_stream_inner(lines, false, false) +} + +#[must_use] +pub fn process_stream_opts(lines: &[String], wrap: bool, ellipsis: bool) -> Vec { + process_stream_inner(lines, wrap, ellipsis) } #[cfg(test)] diff --git a/src/wrap.rs b/src/wrap.rs index 2117f974..3f0a18b9 100644 --- a/src/wrap.rs +++ b/src/wrap.rs @@ -17,7 +17,20 @@ static FOOTNOTE_RE: std::sync::LazyLock = static BLOCKQUOTE_RE: std::sync::LazyLock = std::sync::LazyLock::new(|| Regex::new(r"^(\s*(?:>\s*)+)(.*)$").unwrap()); -pub(crate) fn tokenize_markdown(text: &str) -> Vec { +/// Markdown token emitted by [`tokenize_markdown`]. +#[derive(Debug, PartialEq)] +pub enum Token<'a> { + /// Line within a fenced code block, including the fence itself. + Fence(&'a str), + /// Inline code span without surrounding backticks. + Code(&'a str), + /// Plain text outside code regions. + Text(&'a str), + /// Line break separating tokens. + Newline, +} + +fn tokenize_inline(text: &str) -> Vec { let mut tokens = Vec::new(); let chars: Vec = text.chars().collect(); let mut i = 0; @@ -70,6 +83,46 @@ pub(crate) fn tokenize_markdown(text: &str) -> Vec { tokens } +/// Tokenise Markdown into fences, inline code and plain text. +pub(crate) fn tokenize_markdown(input: &str) -> Vec> { + let mut out = Vec::new(); + let mut in_fence = false; + for line in input.split_inclusive('\n') { + let trimmed = line.trim_end_matches('\n'); + if FENCE_RE.is_match(trimmed) { + out.push(Token::Fence(trimmed)); + out.push(Token::Newline); + in_fence = !in_fence; + continue; + } + if in_fence { + out.push(Token::Fence(trimmed)); + out.push(Token::Newline); + continue; + } + let mut rest = trimmed; + while let Some(pos) = rest.find('`') { + if pos > 0 { + out.push(Token::Text(&rest[..pos])); + } + if let Some(end) = rest[pos + 1..].find('`') { + out.push(Token::Code(&rest[pos + 1..pos + 1 + end])); + rest = &rest[pos + end + 2..]; + } else { + out.push(Token::Text(&rest[pos..])); + rest = ""; + break; + } + } + if !rest.is_empty() { + out.push(Token::Text(rest)); + } + out.push(Token::Newline); + } + out.pop(); + out +} + /// Determine if the current line should break at the last whitespace. /// /// Returns `true` if `current_width` exceeds `width` and a whitespace split @@ -93,7 +146,7 @@ fn wrap_preserving_code(text: &str, width: usize) -> Vec { let mut current = String::new(); let mut current_width = 0; let mut last_split: Option = None; - for token in tokenize_markdown(text) { + for token in tokenize_inline(text) { let token_width = UnicodeWidthStr::width(token.as_str()); if current_width + token_width <= width { current.push_str(&token); diff --git a/tests/integration.rs b/tests/integration.rs index 37816d2e..abe57106 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -964,3 +964,33 @@ fn test_cli_ellipsis_option() { assert!(output.status.success()); assert_eq!(String::from_utf8_lossy(&output.stdout), "foo…\n"); } + +#[test] +fn test_cli_ellipsis_code_span() { + let output = Command::cargo_bin("mdtablefix") + .unwrap() + .arg("--ellipsis") + .write_stdin("before `dots...` after\n") + .output() + .unwrap(); + assert!(output.status.success()); + assert_eq!( + String::from_utf8_lossy(&output.stdout), + "before `dots...` after\n" + ); +} + +#[test] +fn test_cli_ellipsis_fenced_block() { + let output = Command::cargo_bin("mdtablefix") + .unwrap() + .arg("--ellipsis") + .write_stdin("```\nlet x = ...;\n```\n") + .output() + .unwrap(); + assert!(output.status.success()); + assert_eq!( + String::from_utf8_lossy(&output.stdout), + "```\nlet x = ...;\n```\n" + ); +} From 8fdc144ed1352d25af395e782191dbc71cd79b3a Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 18 Jul 2025 09:46:38 +0100 Subject: [PATCH 3/5] Expose ellipsis helper and expand tests --- README.md | 5 +++-- src/ellipsis.rs | 35 +++++++++++++++++++++++++++++++++-- src/lib.rs | 1 + 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a48662a0..b2449349 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,9 @@ mdtablefix [--wrap] [--renumber] [--breaks] [--ellipsis] [--in-place] [FILE...] `--renumber`. - Use `--breaks` to normalize thematic breaks to a line of 70 underscores (configurable via the `THEMATIC_BREAK_LEN` constant). -- Use `--ellipsis` to replace sequences of three dots with the ellipsis - character. +- Use `--ellipsis` to replace groups of three consecutive dots with the + ellipsis character. Longer runs are processed left-to-right so leftover dots + remain unchanged. - Use `--in-place` to overwrite files. - If no files are supplied, input is read from stdin and results are written to stdout. diff --git a/src/ellipsis.rs b/src/ellipsis.rs index 97e37edf..2f51565b 100644 --- a/src/ellipsis.rs +++ b/src/ellipsis.rs @@ -1,7 +1,9 @@ //! Replace sequences of three dots with the ellipsis character. //! -//! This module provides a helper for normalising textual ellipses. It respects -//! fenced code blocks and inline code spans so that code is left untouched. +//! Groups of three consecutive dots become a single Unicode ellipsis. Longer +//! runs are processed left-to-right so trailing dots that do not form a +//! complete triple remain. Fenced code blocks and inline code spans are left +//! untouched. use regex::Regex; @@ -13,6 +15,9 @@ static DOT_RE: std::sync::LazyLock = /// Replace `...` with `…` outside code spans and fences. #[must_use] pub fn replace_ellipsis(lines: &[String]) -> Vec { + if lines.is_empty() { + return Vec::new(); + } let joined = lines.join("\n"); let mut out = String::new(); for token in tokenize_markdown(&joined) { @@ -71,4 +76,30 @@ mod tests { let expected = vec!["…. ….. …… …….".to_string()]; assert_eq!(replace_ellipsis(&input), expected); } + + #[test] + fn handles_empty_input() { + let input: Vec = Vec::new(); + let expected: Vec = Vec::new(); + assert_eq!(replace_ellipsis(&input), expected); + } + + #[test] + fn handles_multiple_fenced_blocks() { + let input = vec![ + "text...".to_string(), + "```".to_string(), + "code...".to_string(), + "```".to_string(), + "more text...".to_string(), + ]; + let expected = vec![ + "text…".to_string(), + "```".to_string(), + "code...".to_string(), + "```".to_string(), + "more text…".to_string(), + ]; + assert_eq!(replace_ellipsis(&input), expected); + } } diff --git a/src/lib.rs b/src/lib.rs index ba07b102..115c3767 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,7 @@ pub fn html_table_to_markdown(lines: &[String]) -> Vec { } pub use breaks::{THEMATIC_BREAK_LEN, format_breaks}; +pub use ellipsis::replace_ellipsis; pub use html::convert_html_tables; pub use io::{rewrite, rewrite_no_wrap}; pub use lists::renumber_lists; From 5dc856ae8724c1f6d64dace52174c83e07528ab6 Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 18 Jul 2025 19:08:12 +0100 Subject: [PATCH 4/5] Fix README grammar and clarify API --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b2449349..3f0d85ef 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ mdtablefix [--wrap] [--renumber] [--breaks] [--ellipsis] [--in-place] [FILE...] - Use `--breaks` to normalize thematic breaks to a line of 70 underscores (configurable via the `THEMATIC_BREAK_LEN` constant). - Use `--ellipsis` to replace groups of three consecutive dots with the - ellipsis character. Longer runs are processed left-to-right so leftover dots + ellipsis character. Longer runs are processed left-to-right, so leftover dots remain unchanged. - Use `--in-place` to overwrite files. - If no files are supplied, input is read from stdin and results are written @@ -77,8 +77,9 @@ fn main() -> std::io::Result<()> { } ``` -- `process_stream_opts(&[String], bool, bool) -> Vec` rewrites tables - in memory with optional wrapping and ellipsis replacement. +- `process_stream_opts(lines: &[String], wrap: bool, ellipsis: bool) -> \ + Vec` rewrites tables in memory with optional wrapping and ellipsis + replacement. - `rewrite(&Path) -> std::io::Result<()>` updates a Markdown file on disk. ## HTML table support From 1a33ac3943a83c9102689309ff7f5d02132d5b2c Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 18 Jul 2025 20:00:24 +0100 Subject: [PATCH 5/5] Clarify option usage in README --- README.md | 12 ++++++++---- tests/integration.rs | 12 ++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3f0d85ef..1be633cf 100644 --- a/README.md +++ b/README.md @@ -70,16 +70,20 @@ use std::path::Path; fn main() -> std::io::Result<()> { let lines = vec!["|A|B|".to_string(), "|1|2|".to_string()]; - let fixed = process_stream_opts(&lines, true, true); + let fixed = process_stream_opts( + &lines, + /* wrap = */ true, + /* ellipsis = */ true, + ); println!("{}", fixed.join("\n")); rewrite(Path::new("table.md"))?; Ok(()) } ``` -- `process_stream_opts(lines: &[String], wrap: bool, ellipsis: bool) -> \ - Vec` rewrites tables in memory with optional wrapping and ellipsis - replacement. +- `process_stream_opts(lines: &[String], wrap: bool, ellipsis: bool) -> + Vec` rewrites tables in memory with optional wrapping and ellipsis + replacement. - `rewrite(&Path) -> std::io::Result<()>` updates a Markdown file on disk. ## HTML table support diff --git a/tests/integration.rs b/tests/integration.rs index abe57106..a36e6b07 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -994,3 +994,15 @@ fn test_cli_ellipsis_fenced_block() { "```\nlet x = ...;\n```\n" ); } + +#[test] +fn test_cli_ellipsis_long_sequence() { + let output = Command::cargo_bin("mdtablefix") + .unwrap() + .arg("--ellipsis") + .write_stdin("wait....\n") + .output() + .unwrap(); + assert!(output.status.success()); + assert_eq!(String::from_utf8_lossy(&output.stdout), "wait….\n"); +}