Skip to content

Commit

Permalink
add text wrapping
Browse files Browse the repository at this point in the history
Adds a `--wrap_width <n>` for markdown formatting.

This required big changes to `output.rs`. It also inspired even more
changes: I should rework it to more of a stream model, where I just keep
track of the indentations, and treat newlines as special. But for now,
this works.

resolves #192
  • Loading branch information
yshavit authored Jan 18, 2025
1 parent 12a98e6 commit 2ee9505
Show file tree
Hide file tree
Showing 13 changed files with 1,190 additions and 229 deletions.
41 changes: 40 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "mdq"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Select specific elements in a Markdown document"
description = "Select specific elements in a Markdown document"
repository = "https://github.com/yshavit/mdq"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand All @@ -15,6 +15,7 @@ paste = "1.0"
regex = "1.10.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1.0"
termsize = "0.1.9"

[dev-dependencies]
indoc = "2"
Expand Down
31 changes: 30 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::fmt_md::ReferencePlacement;
use crate::link_transform::LinkTransform;
use clap::{Parser, ValueEnum};
use clap::error::ErrorKind;
use clap::{CommandFactory, Parser, ValueEnum};
use std::borrow::Cow;
use std::fmt::{Display, Formatter};

Expand Down Expand Up @@ -28,6 +29,15 @@ pub struct Cli {
#[arg(long, short, default_value_t = OutputFormat::Markdown)]
pub(crate) output: OutputFormat,

/// The number of characters to wrap text at. This is only valid when the output format is
/// markdown.
///
/// Certain elements (like section headings and link definitions) will never be wrapped, and the
/// wrapping will never break a word; it will only ever be along existing whitespace. In
/// particular, this means the wrapping will never add hyphens, and it will never break URLs.
#[arg(long)]
pub(crate) wrap_width: Option<usize>,

#[arg(
short = ' ',
hide = true,
Expand Down Expand Up @@ -64,6 +74,25 @@ impl Cli {
},
}
}

pub fn extra_validation(&self) -> bool {
match self.output {
OutputFormat::Json => {
if matches!(self.wrap_width, Some(_)) {
let _ = Cli::command()
.error(
ErrorKind::ArgumentConflict,
"Can't set text width with JSON output format",
)
.print();
false
} else {
true
}
}
OutputFormat::Markdown | OutputFormat::Md => true,
}
}
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
Expand Down
32 changes: 19 additions & 13 deletions src/fmt_md.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,15 @@ impl<'s, 'md> MdWriterState<'s, 'md> {
}
MdElemRef::Section(Section { depth, title, body }) => {
out.with_block(Block::Plain, |out| {
for _ in 0..*depth {
out.write_str("#");
}
if !title.is_empty() {
out.write_str(" ");
self.inlines_writer.write_line(out, title);
}
out.without_wrapping(|out| {
for _ in 0..*depth {
out.write_str("#");
}
if !title.is_empty() {
out.write_str(" ");
self.inlines_writer.write_line(out, title);
}
});
});
self.write_md(out, Self::doc_iter(body), false);
self.write_link_refs_as_needed(out);
Expand All @@ -136,7 +138,9 @@ impl<'s, 'md> MdWriterState<'s, 'md> {
}
MdElemRef::ThematicBreak => {
if !prev_was_thematic_break {
out.with_block(Block::Plain, |out| out.write_str(" -----"));
out.with_block(Block::Plain, |out| {
out.without_wrapping(|out| out.write_str(" -----"));
});
}
self.prev_was_thematic_break = true;
}
Expand Down Expand Up @@ -440,9 +444,11 @@ impl<'s, 'md> MdWriterState<'s, 'md> {
LinkLabel::Text(identifier) => out.write_str(identifier.deref()),
LinkLabel::Inline(text) => self.inlines_writer.write_line(out, text),
}
out.write_str("]: ");
out.write_str(&link_def.url);
self.inlines_writer.write_url_title(out, &link_def.title);
out.without_wrapping(|out| {
out.write_str("]: ");
out.write_str(&link_def.url);
self.inlines_writer.write_url_title(out, &link_def.title);
});
newline(out);
}
}
Expand All @@ -463,7 +469,7 @@ impl<'s, 'md> MdWriterState<'s, 'md> {
}

fn line_to_string(&mut self, line: &'md Line) -> String {
let mut out = Output::new(String::with_capacity(line.len() * 10)); // rough guess
let mut out = Output::without_text_wrapping(String::with_capacity(line.len() * 10)); // rough guess
self.inlines_writer.write_line(&mut out, line);
out.take_underlying().unwrap()
}
Expand Down Expand Up @@ -2184,7 +2190,7 @@ pub mod tests {
let (ctx, nodes) = inputs;
nodes.iter().for_each(|n| VARIANTS_CHECKER.see(n));

let mut out = Output::new(String::default());
let mut out = Output::without_text_wrapping(String::default());
write_md(options, &mut out, &ctx, nodes.into_iter());
let actual = out.take_underlying().unwrap();
assert_eq!(&actual, expect);
Expand Down
Loading

0 comments on commit 2ee9505

Please sign in to comment.