Skip to content

Commit 12fa839

Browse files
authored
implement regex-replace for all selectors
This uses the new flat_inlines (#402) to replace all of the string-matching that the selectors had previous had, with a new regex-replace functionality. Resolves #376.
1 parent 989c417 commit 12fa839

File tree

14 files changed

+1481
-627
lines changed

14 files changed

+1481
-627
lines changed

Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,3 @@ lazy_static = "1.4.0"
3030
indoc = "2"
3131
serde = { version = "1", features = ["derive"] }
3232
toml = "0.8"
33-
34-
[lints.rust]
35-
dead_code = "allow" # TODO remove this; it's a temporary crutch for the phased commits of #376

src/md_elem/flat_inlines.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::md_elem::tree::elem::Span;
22
use crate::md_elem::tree::elem::{Autolink, AutolinkStyle, Image, Link, StandardLink};
33
use crate::md_elem::tree::elem::{FootnoteId, Inline, LinkDefinition, SpanVariant, Text, TextVariant};
4+
use crate::output::{inlines_to_plain_string, FootnoteToString, InlineToStringOpts};
45
use std::cmp::Ordering;
56
use std::iter::Peekable;
67
use std::ops::Range;
@@ -482,7 +483,12 @@ fn flatten_inlines(inlines: impl IntoIterator<Item = Inline>, text: &mut String)
482483
// range starts outside this inline). Rather than describing a complex situation to the user, we'll just
483484
// prohibit them.
484485
let start_pos = text.len();
485-
let content = String::from("1"); // TODO
486+
let content = inlines_to_plain_string(
487+
&[&other],
488+
InlineToStringOpts {
489+
footnotes: FootnoteToString::OnlyFootnoteId,
490+
},
491+
);
486492
text.push_str(&content);
487493
let length = content.len();
488494

src/md_elem/tree.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1442,7 +1442,7 @@ macro_rules! m_node {
14421442

14431443
// Recursive case: A::B<tail> -> A::B(B<tail>)
14441444
($head:ident :: $next:ident $(:: $($tail:ident)::*)? $( $( <$last_variant:ident> )? { $($args:tt)* })? ) => {
1445-
$head::$next( m_node!($next $(:: $($tail)::*)? $( $( <$last_variant> )? { $($args)* })?) )
1445+
$head::$next( crate::md_elem::m_node!($next $(:: $($tail)::*)? $( $( <$last_variant> )? { $($args)* })?) )
14461446
};
14471447
}
14481448
pub(crate) use m_node;

src/output/fmt_plain_str.rs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,48 @@
11
use crate::md_elem::elem::*;
22
use std::borrow::Borrow;
33

4-
pub(crate) fn inlines_to_plain_string<N: Borrow<Inline>>(inlines: &[N]) -> String {
4+
#[derive(Default, Copy, Clone)]
5+
pub(crate) struct InlineToStringOpts {
6+
pub(crate) footnotes: FootnoteToString,
7+
}
8+
9+
#[derive(Default, Copy, Clone)]
10+
pub(crate) enum FootnoteToString {
11+
/// Includes all the surrounding Markdown. If the footnote id is `"1"`, this will render as `[^1]`.
12+
#[default]
13+
IncludeMarkdown,
14+
15+
/// Includes only the footnote id. If the footnote id is `"1"`, this will render as `1`.
16+
OnlyFootnoteId,
17+
}
18+
19+
pub(crate) fn inlines_to_plain_string<N: Borrow<Inline>>(inlines: &[N], opts: InlineToStringOpts) -> String {
520
let mut result = String::with_capacity(inlines.len() * 5); // random guess
6-
build_inlines(&mut result, inlines);
21+
build_inlines(&mut result, inlines, opts);
722
result
823
}
924

10-
fn build_inlines<N: Borrow<Inline>>(out: &mut String, inlines: &[N]) {
25+
fn build_inlines<N: Borrow<Inline>>(out: &mut String, inlines: &[N], opts: InlineToStringOpts) {
1126
for inline in inlines {
12-
build_inline(out, inline.borrow());
27+
build_inline(out, inline.borrow(), opts);
1328
}
1429
}
1530

16-
fn build_inline(out: &mut String, elem: &Inline) {
31+
fn build_inline(out: &mut String, elem: &Inline, opts: InlineToStringOpts) {
1732
match elem {
18-
Inline::Span(Span { children, .. }) => build_inlines(out, children),
33+
Inline::Span(Span { children, .. }) => build_inlines(out, children, opts),
1934
Inline::Text(Text { value, .. }) => out.push_str(value),
20-
Inline::Link(Link::Standard(standard_link)) => build_inlines(out, &standard_link.display),
35+
Inline::Link(Link::Standard(standard_link)) => build_inlines(out, &standard_link.display, opts),
2136
Inline::Link(Link::Autolink(autolink)) => out.push_str(&autolink.url),
2237
Inline::Image(Image { alt, .. }) => out.push_str(alt),
2338
Inline::Footnote(footnote) => {
24-
out.push_str("[^");
39+
if matches!(opts.footnotes, FootnoteToString::IncludeMarkdown) {
40+
out.push_str("[^");
41+
}
2542
out.push_str(footnote.as_str());
26-
out.push(']');
43+
if matches!(opts.footnotes, FootnoteToString::IncludeMarkdown) {
44+
out.push(']');
45+
}
2746
}
2847
}
2948
}
@@ -70,7 +89,7 @@ mod tests {
7089
unwrap!(&md_elems[0], MdElem::Paragraph(contents));
7190
unwrap!(&contents.body[1], inline @ Inline::Text(_));
7291
VARIANTS_CHECKER.see(inline);
73-
let actual = inlines_to_plain_string(&contents.body);
92+
let actual = inlines_to_plain_string(&contents.body, Default::default());
7493
assert_eq!(&actual, "Hello <foo> world");
7594
}
7695

@@ -119,7 +138,7 @@ mod tests {
119138
let md_elems = MdDoc::parse(md, &options).unwrap().roots;
120139
unwrap!(&md_elems[0], MdElem::Paragraph(p));
121140
p.body.iter().for_each(|inline| VARIANTS_CHECKER.see(inline));
122-
let actual = inlines_to_plain_string(&p.body);
141+
let actual = inlines_to_plain_string(&p.body, Default::default());
123142
assert_eq!(&actual, expect);
124143
}
125144
}

src/select/match_replace.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,45 @@ pub struct MatchReplace {
55
pub matcher: Matcher,
66
pub replacement: Option<String>,
77
}
8+
9+
#[cfg(test)]
10+
mod test_utils {
11+
use super::*;
12+
use crate::select::{Matcher, Regex};
13+
14+
impl MatchReplace {
15+
pub(crate) fn match_any() -> MatchReplace {
16+
Self::build(|b| b)
17+
}
18+
19+
pub(crate) fn build(f: impl FnOnce(&mut MatchReplaceBuilder) -> &mut MatchReplaceBuilder) -> MatchReplace {
20+
let mut builder = MatchReplaceBuilder {
21+
matcher: None,
22+
replacement: None,
23+
};
24+
f(&mut builder);
25+
MatchReplace {
26+
matcher: builder.matcher.unwrap_or(Matcher::Any { explicit: false }),
27+
replacement: builder.replacement,
28+
}
29+
}
30+
}
31+
32+
pub(crate) struct MatchReplaceBuilder {
33+
matcher: Option<Matcher>,
34+
replacement: Option<String>,
35+
}
36+
37+
impl MatchReplaceBuilder {
38+
pub(crate) fn match_regex(&mut self, pattern: impl AsRef<str>) -> &mut Self {
39+
let re = fancy_regex::Regex::new(pattern.as_ref()).expect("test error: bad regex");
40+
self.matcher = Some(Matcher::Regex(Regex { re }));
41+
self
42+
}
43+
44+
pub(crate) fn replacement(&mut self, replacement: impl Into<String>) -> &mut Self {
45+
self.replacement = Some(replacement.into());
46+
self
47+
}
48+
}
49+
}

src/select/match_selector.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,11 @@ where
2424
}
2525
}
2626
}
27+
28+
pub(crate) fn make_select_result<T: Into<MdElem>>(item: T, matched_any: bool) -> Select {
29+
if matched_any {
30+
Select::Hit(vec![item.into()])
31+
} else {
32+
Select::Miss(item.into())
33+
}
34+
}

0 commit comments

Comments
 (0)