123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- use pulldown_cmark::{Event, Parser, Tag};
- use std::ops::Range;
- #[derive(Debug)]
- pub struct IgnoreBlocks {
- ignore: Vec<Range<usize>>,
- }
- impl IgnoreBlocks {
- pub fn new(s: &str) -> IgnoreBlocks {
- let mut ignore = Vec::new();
- let mut parser = Parser::new(s).into_offset_iter();
- while let Some((event, range)) = parser.next() {
- if let Event::Start(Tag::CodeBlock(_)) = event {
- let start = range.start;
- while let Some((event, range)) = parser.next() {
- if let Event::End(Tag::CodeBlock(_)) = event {
- ignore.push(start..range.end);
- break;
- }
- }
- } else if let Event::Start(Tag::BlockQuote) = event {
- let start = range.start;
- let mut count = 1;
- while let Some((event, range)) = parser.next() {
- if let Event::Start(Tag::BlockQuote) = event {
- count += 1;
- } else if let Event::End(Tag::BlockQuote) = event {
- count -= 1;
- if count == 0 {
- ignore.push(start..range.end);
- break;
- }
- }
- }
- } else if let Event::Code(_) = event {
- ignore.push(range);
- }
- }
- IgnoreBlocks { ignore }
- }
- pub fn overlaps_ignore(&self, region: Range<usize>) -> Option<Range<usize>> {
- for ignore in &self.ignore {
- // See https://stackoverflow.com/questions/3269434.
- if ignore.start <= region.end && region.start <= ignore.end {
- return Some(ignore.clone());
- }
- }
- None
- }
- }
- #[cfg(test)]
- #[derive(Debug, PartialEq, Eq)]
- enum Ignore<'a> {
- Yes(&'a str),
- No(&'a str),
- }
- #[cfg(test)]
- fn bodies(s: &str) -> Vec<Ignore<'_>> {
- let mut bodies = Vec::new();
- let cbs = IgnoreBlocks::new(s);
- let mut previous = 0..0;
- for range in &cbs.ignore {
- let range = range.clone();
- if previous.end != range.start {
- bodies.push(Ignore::No(&s[previous.end..range.start]));
- }
- bodies.push(Ignore::Yes(&s[range.clone()]));
- previous = range.clone();
- }
- if let Some(range) = cbs.ignore.last() {
- if range.end != s.len() {
- bodies.push(Ignore::No(&s[range.end..]));
- }
- }
- bodies
- }
- #[test]
- fn cbs_1() {
- assert_eq!(
- bodies("`hey you`bar me too"),
- [Ignore::Yes("`hey you`"), Ignore::No("bar me too")]
- );
- }
- #[test]
- fn cbs_2() {
- assert_eq!(
- bodies("`hey you` <b>me too</b>"),
- [Ignore::Yes("`hey you`"), Ignore::No(" <b>me too</b>")]
- );
- }
- #[test]
- fn cbs_3() {
- assert_eq!(
- bodies(r"`hey you\` <b>`me too</b>"),
- [Ignore::Yes(r"`hey you\`"), Ignore::No(" <b>`me too</b>")]
- );
- }
- #[test]
- fn cbs_4() {
- assert_eq!(
- bodies(
- "
- ```language_spec
- testing
- ```
- nope
- "
- ),
- [
- Ignore::No("\n"),
- Ignore::Yes("```language_spec\ntesting\n```"),
- Ignore::No("\n\nnope\n")
- ],
- );
- }
- #[test]
- fn cbs_5() {
- assert_eq!(
- bodies(concat!(
- "
- ``` tag_after_space
- testing
- ```",
- " "
- )),
- [
- Ignore::No("\n"),
- Ignore::Yes("``` tag_after_space\ntesting\n``` "),
- ],
- );
- }
- #[test]
- fn cbs_6() {
- assert_eq!(
- bodies(
- "
- this is indented
- this is indented too
- "
- ),
- [
- Ignore::No("\n "),
- Ignore::Yes("this is indented\n this is indented too\n"),
- ],
- );
- }
- #[test]
- fn cbs_7() {
- assert_eq!(
- bodies(
- "
- ```
- testing unclosed
- "
- ),
- [Ignore::No("\n"), Ignore::Yes("```\ntesting unclosed\n"),],
- );
- }
- #[test]
- fn cbs_8() {
- assert_eq!(
- bodies("`one` not `two`"),
- [
- Ignore::Yes("`one`"),
- Ignore::No(" not "),
- Ignore::Yes("`two`")
- ]
- );
- }
- #[test]
- fn cbs_9() {
- assert_eq!(
- bodies(
- "
- some text
- > testing citations
- still in citation
- more text
- "
- ),
- [
- Ignore::No("\nsome text\n"),
- Ignore::Yes("> testing citations\nstill in citation\n"),
- Ignore::No("\nmore text\n")
- ],
- );
- }
- #[test]
- fn cbs_10() {
- assert_eq!(
- bodies(
- "
- # abc
- > multiline
- > citation
- lorem ipsum
- "
- ),
- [
- Ignore::No("\n# abc\n\n"),
- Ignore::Yes("> multiline\n> citation\n"),
- Ignore::No("\nlorem ipsum\n")
- ],
- );
- }
- #[test]
- fn cbs_11() {
- assert_eq!(
- bodies(
- "
- > some
- > > nested
- > citations
- "
- ),
- [
- Ignore::No("\n"),
- Ignore::Yes("> some\n> > nested\n> citations\n"),
- ],
- );
- }
|