Skip to main content

emphasis_span

Function emphasis_span 

Source
pub fn emphasis_span(
    text: &str,
    before: Option<char>,
    after: Option<char>,
) -> Option<Range<usize>>
Expand description

The part of a styled run that emphasis delimiters (*, **, ~~) can wrap, given the characters the run lands between, or None when no part can.

before is the last character already written, after the first one the next piece will write (None at either end of the inline content). The text outside the returned range is written as-is, outside the delimiters.

CommonMark reads a delimiter as emphasis only when it flanks the text (ยง6.2): it may not touch whitespace on its inside, and when its inside is punctuation its outside must be whitespace, punctuation, or the end of the line. So:

  • leading and trailing whitespace always stays outside โ€” **word ** is not emphasis;
  • leading or trailing punctuation stays outside too, with any whitespace behind it, when the run touches a letter or digit on that side โ€” an italic ", s" after 32 written as 32*, s* shows its asterisks, written as 32, *s* it does not;
  • a run that is nothing but whitespace, or nothing but punctuation between words, has nothing to wrap.

Punctuation that does not touch a word stays inside (*i.e.,*, *(h)*), so a run is not reshaped unless it has to be.

text may already be escaped: a backslash escape (\*) is one unit and is never split across a delimiter. Only escapes of ASCII punctuation are units, as in CommonMark.

use unparser_shared::markdown::emphasis_span;

let wrap = |text: &str, before, after| {
    emphasis_span(text, before, after).map_or(text.to_string(), |r| {
        format!("{}*{}*{}", &text[..r.start], &text[r.clone()], &text[r.end..])
    })
};
assert_eq!(wrap(", s", Some('2'), Some(' ')), ", *s*");
assert_eq!(wrap("word ", None, Some('x')), "*word* ");
assert_eq!(wrap(",", Some('8'), Some('a')), ",");
assert_eq!(wrap("(h)", Some(' '), None), "*(h)*");