Skip to content

Commit

Permalink
add inlines_to_plain_string
Browse files Browse the repository at this point in the history
Also, get rid of `mod fmt_json`. The actual to-json is something I
should think about harder and do right, or not at all. The to-plaintext
helper is now in fmt_str.

I had originally added the json formatting as a quick way of getting
some output, before I had good markdown rendering. I don't need that
shim anymore.
  • Loading branch information
yshavit authored Jun 16, 2024
1 parent 17812cb commit b282f64
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 204 deletions.
180 changes: 0 additions & 180 deletions src/fmt_json.rs

This file was deleted.

142 changes: 142 additions & 0 deletions src/fmt_str.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use crate::tree::{Inline, InlineVariant};
use std::borrow::Borrow;

pub fn inlines_to_plain_string<N: Borrow<Inline>>(inlines: &[N]) -> String {
let mut result = String::with_capacity(inlines.len() * 10); // random guess
build_inlines(&mut result, inlines);

result
}

fn build_inlines<N: Borrow<Inline>>(out: &mut String, inlines: &[N]) {
for inline in inlines {
build_inline(out, inline.borrow());
}
}

fn build_inline(out: &mut String, elem: &Inline) {
match elem {
Inline::Span { children, .. } => build_inlines(out, children),
Inline::Text { variant, value, .. } => {
if !matches!(variant, InlineVariant::Html) {
out.push_str(value)
}
}
Inline::Link { text, .. } => build_inlines(out, text),
Inline::Image { alt, .. } => out.push_str(alt),
Inline::Footnote(footnote) => {
out.push_str("[^");
out.push_str(&footnote.label);
out.push(']');
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;

use crate::tree::{Inline, InlineVariant, MdqNode, SpanVariant};
use crate::unwrap;
use crate::utils_for_test::VariantsChecker;
use lazy_static::lazy_static;
use markdown::ParseOptions;

lazy_static! {
static ref VARIANTS_CHECKER: VariantsChecker<Inline> = crate::new_variants_checker!(Inline {
Span { variant: SpanVariant::Delete, .. },
Span { variant: SpanVariant::Emphasis, .. },
Span { variant: SpanVariant::Strong, .. },
Text { variant: InlineVariant::Text, .. },
Text { variant: InlineVariant::Code, .. },
Text { variant: InlineVariant::Math, .. },
Text { variant: InlineVariant::Html, .. },
Link { .. },
Image { .. },
Footnote(_),
});
}

#[test]
fn spans() {
check("_hello world_", "hello world");
check("**hello world**", "hello world");
check("~~hello world~~", "hello world");
}

#[test]
fn texts() {
check("hello world", "hello world");
check("`hello world`", "hello world");
check("$hello world$", "hello world");
// html is covered separately, since it isn't wrapped in a paragraph: see issue #34
}

#[test]
fn text_html() {
let node = markdown::to_mdast("<foo>", &ParseOptions::gfm()).unwrap();
let mdq_node: MdqNode = node.try_into().unwrap();
unwrap!(mdq_node, MdqNode::Root(root));
unwrap!(&root.body[0], MdqNode::Inline(inline));
VARIANTS_CHECKER.see(inline);
let actual = inlines_to_plain_string(&[inline]);
assert_eq!(&actual, "");
}

#[test]
fn links() {
check("[foo](https://example.com)", "foo");
check("[foo _with emphasis_](https://example.com)", "foo with emphasis");
check(
indoc! {r#"
[foo][1]
[1]: https://example.com"#},
"foo",
)
}

#[test]
fn images() {
check("![foo](https://example.com)", "foo");
check("![foo _with emphasis_](https://example.com)", "foo with emphasis"); // md is ignored in alt
check(
indoc! {r#"
![foo][1]
[1]: https://example.com"#},
"foo",
)
}

#[test]
fn footnote() {
check(
indoc! {r#"
[^1]
[^1]: my footnote"#},
"[^1]",
)
}

#[test]
fn all_variants_checked() {
VARIANTS_CHECKER.wait_for_all();
}

/// Because this is such simple functionality, we're just going to do a simple end-to-end test from original
/// markdown to plain text.
fn check(md: &str, expect: &str) {
let mut options = ParseOptions::gfm();
options.constructs.math_text = true;
let node = markdown::to_mdast(md, &options).unwrap();
let mdq_node: MdqNode = node.try_into().unwrap();
unwrap!(mdq_node, MdqNode::Root(root));
unwrap!(&root.body[0], MdqNode::Paragraph(p));
p.body.iter().for_each(|inline| VARIANTS_CHECKER.see(inline));
let actual = inlines_to_plain_string(&p.body);
assert_eq!(&actual, expect);
}
}
6 changes: 1 addition & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ use std::io;
use std::io::{stdin, Read};
use std::string::ToString;

use crate::fmt_json::TextOnly;
use crate::fmt_md::MdOptions;
use crate::output::Stream;
use crate::tree::MdqNode;

mod fmt_json;
mod fmt_md;
mod fmt_str;
mod output;
mod select;
mod str_utils;
Expand All @@ -32,9 +31,6 @@ fn main() {

let found = selector.find(&mdq);

let jsons = fmt_json::nodes_to_json::<_, TextOnly>(&found);
println!("{}", jsons);
out.write_str("\n\n=======================================\n\n");
fmt_md::write_md(&MdOptions::default(), &mut out, &found);
out.write_str("\n");
}
8 changes: 2 additions & 6 deletions src/select.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::fmt_json;
use crate::fmt_str::inlines_to_plain_string;
use crate::tree::*;

#[allow(dead_code)]
Expand Down Expand Up @@ -69,7 +69,7 @@ impl Selector {
MdqNode::Root(Root { body }) => SelectResult::Recurse(body),
MdqNode::Header(Header { title, body, .. }) => {
if let Selector::Heading(matcher) = self {
if matcher.matches(&Self::line_to_string(title)) {
if matcher.matches(&inlines_to_plain_string(title)) {
SelectResult::Found(body.iter().map(|elem| elem).collect())
} else {
SelectResult::Recurse(body)
Expand Down Expand Up @@ -145,8 +145,4 @@ impl Selector {
fn find_in_children<'a>(&'a self, children: &'a Vec<MdqNode>) -> Vec<&MdqNode> {
children.iter().flat_map(|elem| self.find(elem)).collect()
}

fn line_to_string(line: &[Inline]) -> String {
fmt_json::TextOnly::line_to_string(line)
}
}
Loading

0 comments on commit b282f64

Please sign in to comment.