#[derive(Debug, Clone)]
pub struct FmtOptions {
wrap: Wrap,
wrap_strategy: WrapStrategy,
italic: ItalicStyle,
strong: StrongStyle,
list_marker: ListMarkerStyle,
list_continuation_indent: ListContinuationIndent,
ordered_list: OrderedListStyle,
table: TableStyle,
trailing_newline: TrailingNewline,
end_of_line: EndOfLine,
exclude_globs: Vec<String>,
link_def_placement: Placement,
link_def_style: LinkDefStyle,
footnote_placement: Placement,
preserve_frontmatter: bool,
thematic_break_style: ThematicStyle,
math: MathOptions,
heading_attrs: HeadingAttrsStyle,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum HeadingAttrsStyle {
#[default]
Preserve,
Canonicalise,
}
#[derive(Copy, Clone, Debug, Default)]
pub struct MathOptions {
pub normalise: bool,
pub render: MathRender,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum MathRender {
#[default]
None,
CommonmarkKatex,
Dollar,
}
impl FmtOptions {
#[must_use]
pub fn wrap(&self) -> Wrap {
self.wrap
}
#[must_use]
pub fn wrap_strategy(&self) -> WrapStrategy {
self.wrap_strategy
}
#[must_use]
pub fn italic(&self) -> ItalicStyle {
self.italic
}
#[must_use]
pub fn strong(&self) -> StrongStyle {
self.strong
}
#[must_use]
pub fn list_marker(&self) -> ListMarkerStyle {
self.list_marker
}
#[must_use]
pub fn list_continuation_indent(&self) -> ListContinuationIndent {
self.list_continuation_indent
}
#[must_use]
pub fn ordered_list(&self) -> OrderedListStyle {
self.ordered_list
}
#[must_use]
pub fn table(&self) -> TableStyle {
self.table
}
#[must_use]
pub fn trailing_newline(&self) -> TrailingNewline {
self.trailing_newline
}
#[must_use]
pub fn end_of_line(&self) -> EndOfLine {
self.end_of_line
}
#[must_use]
pub fn exclude_globs(&self) -> &[String] {
&self.exclude_globs
}
#[must_use]
pub fn link_def_placement(&self) -> Placement {
self.link_def_placement
}
#[must_use]
pub fn link_def_style(&self) -> LinkDefStyle {
self.link_def_style
}
#[must_use]
pub fn footnote_placement(&self) -> Placement {
self.footnote_placement
}
#[must_use]
pub fn preserve_frontmatter(&self) -> bool {
self.preserve_frontmatter
}
#[must_use]
pub fn thematic_break_style(&self) -> ThematicStyle {
self.thematic_break_style
}
#[must_use]
pub fn math(&self) -> MathOptions {
self.math
}
#[must_use]
pub fn with_math(mut self, math: MathOptions) -> Self {
self.math = math;
self
}
#[must_use]
pub fn with_math_render(mut self, render: MathRender) -> Self {
self.math.render = render;
self
}
#[must_use]
pub fn heading_attrs(&self) -> HeadingAttrsStyle {
self.heading_attrs
}
#[must_use]
pub fn with_heading_attrs(mut self, style: HeadingAttrsStyle) -> Self {
self.heading_attrs = style;
self
}
#[must_use]
pub fn resolve_italic(&self, source_delim: u8) -> u8 {
match self.italic {
ItalicStyle::Asterisk => b'*',
ItalicStyle::Underscore => b'_',
ItalicStyle::Preserve => source_delim,
}
}
#[must_use]
pub fn with_wrap(mut self, wrap: Wrap) -> Self {
self.wrap = wrap;
self
}
#[must_use]
pub fn with_wrap_strategy(mut self, strategy: WrapStrategy) -> Self {
self.wrap_strategy = strategy;
self
}
#[must_use]
pub fn with_italic(mut self, italic: ItalicStyle) -> Self {
self.italic = italic;
self
}
#[must_use]
pub fn with_strong(mut self, strong: StrongStyle) -> Self {
self.strong = strong;
self
}
#[must_use]
pub fn with_list_marker(mut self, list_marker: ListMarkerStyle) -> Self {
self.list_marker = list_marker;
self
}
#[must_use]
pub fn with_list_continuation_indent(mut self, indent: ListContinuationIndent) -> Self {
self.list_continuation_indent = indent;
self
}
#[must_use]
pub fn with_ordered_list(mut self, ordered_list: OrderedListStyle) -> Self {
self.ordered_list = ordered_list;
self
}
#[must_use]
pub fn with_table(mut self, table: TableStyle) -> Self {
self.table = table;
self
}
#[must_use]
pub fn with_thematic_break(mut self, thematic_break: ThematicStyle) -> Self {
self.thematic_break_style = thematic_break;
self
}
#[must_use]
pub fn with_link_def_style(mut self, link_def_style: LinkDefStyle) -> Self {
self.link_def_style = link_def_style;
self
}
#[must_use]
pub fn resolve_list_marker(&self, source_marker: u8) -> u8 {
match self.list_marker {
ListMarkerStyle::Dash => b'-',
ListMarkerStyle::Asterisk => b'*',
ListMarkerStyle::Plus => b'+',
ListMarkerStyle::Preserve => source_marker,
}
}
#[must_use]
pub(crate) fn italic_target_byte(&self) -> Option<u8> {
match self.italic {
ItalicStyle::Asterisk => Some(b'*'),
ItalicStyle::Underscore => Some(b'_'),
ItalicStyle::Preserve => None,
}
}
#[must_use]
pub(crate) fn strong_target_byte(&self) -> Option<u8> {
match self.strong {
StrongStyle::Asterisk => Some(b'*'),
StrongStyle::Underscore => Some(b'_'),
StrongStyle::Preserve => None,
}
}
#[must_use]
pub(crate) fn list_marker_target_byte(&self) -> Option<u8> {
match self.list_marker {
ListMarkerStyle::Dash => Some(b'-'),
ListMarkerStyle::Asterisk => Some(b'*'),
ListMarkerStyle::Plus => Some(b'+'),
ListMarkerStyle::Preserve => None,
}
}
#[must_use]
pub(crate) fn ordered_list_target(&self) -> Option<OrderedListStyle> {
match self.ordered_list {
OrderedListStyle::Consistent | OrderedListStyle::One => Some(self.ordered_list),
OrderedListStyle::Preserve => None,
}
}
#[must_use]
pub(crate) fn thematic_target(&self) -> Option<ThematicStyle> {
match self.thematic_break_style {
ThematicStyle::Dash | ThematicStyle::Asterisk | ThematicStyle::Underscore | ThematicStyle::Underscore70 => {
Some(self.thematic_break_style)
}
ThematicStyle::Preserve => None,
}
}
#[must_use]
pub(crate) fn should_normalise_tables(&self) -> bool {
!matches!(self.table, TableStyle::Preserve)
}
#[must_use]
pub(crate) fn has_non_table_canonicalisation(&self) -> bool {
self.italic_target_byte().is_some()
|| self.strong_target_byte().is_some()
|| self.list_marker_target_byte().is_some()
|| self.thematic_target().is_some()
|| self.ordered_list_target().is_some()
|| self.link_def_target().is_some()
|| matches!(self.heading_attrs, HeadingAttrsStyle::Canonicalise)
|| matches!(self.math.render, MathRender::Dollar)
|| self.math.normalise
|| !self.preserve_frontmatter
}
#[must_use]
pub(crate) fn link_def_target(&self) -> Option<LinkDefStyle> {
match self.link_def_style {
LinkDefStyle::Bare => Some(LinkDefStyle::Bare),
LinkDefStyle::Angle => Some(LinkDefStyle::Angle),
LinkDefStyle::Preserve => None,
}
}
#[must_use]
pub(crate) fn has_any_canonicalisation(&self) -> bool {
self.should_normalise_tables() || self.has_non_table_canonicalisation()
}
#[must_use]
pub fn with_trailing_newline(mut self, trailing_newline: TrailingNewline) -> Self {
self.trailing_newline = trailing_newline;
self
}
#[must_use]
pub fn with_end_of_line(mut self, end_of_line: EndOfLine) -> Self {
self.end_of_line = end_of_line;
self
}
#[must_use]
pub fn with_exclude_globs(mut self, exclude_globs: Vec<String>) -> Self {
self.exclude_globs = exclude_globs;
self
}
#[must_use]
pub fn with_link_def_placement(mut self, placement: Placement) -> Self {
self.link_def_placement = placement;
self
}
#[must_use]
pub fn with_footnote_placement(mut self, placement: Placement) -> Self {
self.footnote_placement = placement;
self
}
#[must_use]
pub fn with_preserve_frontmatter(mut self, preserve_frontmatter: bool) -> Self {
self.preserve_frontmatter = preserve_frontmatter;
self
}
}
impl Default for FmtOptions {
fn default() -> Self {
Self {
wrap: Wrap::Keep,
wrap_strategy: WrapStrategy::Stable,
italic: ItalicStyle::Preserve,
strong: StrongStyle::Preserve,
list_marker: ListMarkerStyle::Preserve,
list_continuation_indent: ListContinuationIndent::MarkerWidth,
ordered_list: OrderedListStyle::Preserve,
table: TableStyle::Compact,
trailing_newline: TrailingNewline::Preserve,
end_of_line: EndOfLine::Lf,
exclude_globs: Vec::new(),
link_def_placement: Placement::End,
link_def_style: LinkDefStyle::Preserve,
footnote_placement: Placement::Preserve,
preserve_frontmatter: true,
thematic_break_style: ThematicStyle::Preserve,
math: MathOptions::default(),
heading_attrs: HeadingAttrsStyle::default(),
}
}
}
impl FmtOptions {
#[must_use]
pub fn mdformat() -> Self {
Self::default()
.with_wrap_strategy(WrapStrategy::Stable)
.with_list_marker(ListMarkerStyle::Dash)
.with_list_continuation_indent(ListContinuationIndent::FourSpace)
.with_ordered_list(OrderedListStyle::One)
.with_thematic_break(ThematicStyle::Underscore70)
.with_table(TableStyle::Align)
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum TrailingNewline {
#[default]
Preserve,
Strip,
Ensure,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Placement {
End,
Preserve,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LinkDefStyle {
Bare,
Angle,
Preserve,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Wrap {
Keep,
No,
At(u32),
}
impl Wrap {
#[must_use]
pub fn columns(self) -> u32 {
match self {
Self::Keep | Self::No => u32::MAX,
Self::At(n) => n,
}
}
#[must_use]
pub fn shrink(self, n: u32) -> Self {
match self {
Self::At(c) => Self::At(c.saturating_sub(n).max(1)),
Self::Keep | Self::No => self,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum WrapStrategy {
Stable,
Balanced,
}
impl WrapStrategy {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::Stable => "stable",
Self::Balanced => "balanced",
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ItalicStyle {
Asterisk,
Underscore,
Preserve,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum StrongStyle {
Asterisk,
Underscore,
Preserve,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ListMarkerStyle {
Dash,
Asterisk,
Plus,
Preserve,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ListContinuationIndent {
MarkerWidth,
FourSpace,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum OrderedListStyle {
One,
Consistent,
Preserve,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ThematicStyle {
Dash,
Asterisk,
Underscore,
Underscore70,
Preserve,
}
impl ThematicStyle {
#[must_use]
pub fn as_byte(self) -> Option<u8> {
match self {
Self::Dash => Some(b'-'),
Self::Asterisk => Some(b'*'),
Self::Underscore | Self::Underscore70 => Some(b'_'),
Self::Preserve => None,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TableStyle {
Compact,
Align,
Preserve,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EndOfLine {
Lf,
Crlf,
Keep,
}