1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
use super::{cmark_resume_one_event, fmt, Borrow, Event, Options, Range, State};
/// Serialize a stream of [pulldown-cmark-Events][Event] while preserving the escape characters in `source`.
/// Each input [Event] is accompanied by an optional [Range] that maps it back to the `source` string.
///
/// Different from [`cmark_resume_with_options`](super::cmark_resume_with_options), which always escape
/// Markdown special characters like `#` or `[`, this function only escapes a special character if
/// it is escaped in `source`.
///
/// 1. **source**
/// * Markdown source from which `event_and_ranges` are created.
/// 1. **event_and_ranges**
/// * An iterator over [`Event`]-range pairs, for example as returned by [`pulldown_cmark::OffsetIter`].
/// Must match what's provided in `source`.
/// 1. **formatter**
/// * A format writer, can be a `String`.
/// 1. **state**
/// * The optional initial state of the serialization, useful when the operation should be resumed.
/// 1. **options**
/// * Customize the appearance of the serialization. All otherwise magic values are contained
/// here.
///
/// *Returns* the [`State`] of the serialization on success. You can use it as initial state in the
/// next call if you are halting event serialization.
/// *Errors* are only happening if the underlying buffer fails, which is unlikely.
pub fn cmark_resume_with_source_range_and_options<'a, I, E, F>(
event_and_ranges: I,
source: &'a str,
mut formatter: F,
state: Option<State<'a>>,
options: Options<'_>,
) -> Result<State<'a>, fmt::Error>
where
I: Iterator<Item = (E, Option<Range<usize>>)>,
E: Borrow<Event<'a>>,
F: fmt::Write,
{
let mut state = state.unwrap_or_default();
for (event, range) in event_and_ranges {
let update_event_end_index = !matches!(*event.borrow(), Event::Start(_));
let prevent_escape_leading_special_characters = match (&range, event.borrow()) {
// IMPORTANT: Any changes that allow anything other than `Text`
// breaks the assumption below.
(Some(range), Event::Text(_)) => {
range.start <= state.last_event_end_index ||
// Some source characters are not captured,
// so check the previous character.
source.as_bytes().get(range.start.saturating_sub(1)) != Some(&b'\\')
}
_ => false,
} && !state.is_in_code_block;
if prevent_escape_leading_special_characters {
// Hack to not escape leading special characters.
state.is_in_code_block = true;
}
cmark_resume_one_event(event, &mut formatter, &mut state, &options)?;
if prevent_escape_leading_special_characters {
// Assumption: this case only happens when `event` is `Text`,
// so `state.is_in_code_block` should not be changed to `true`.
// Also, `state.is_in_code_block` was `false`.
state.is_in_code_block = false;
}
if let (true, Some(range)) = (update_event_end_index, range) {
state.last_event_end_index = range.end;
}
}
Ok(state)
}
/// As [`cmark_resume_with_source_range_and_options`], but with default [`Options`].
pub fn cmark_resume_with_source_range<'a, I, E, F>(
event_and_ranges: I,
source: &'a str,
formatter: F,
state: Option<State<'a>>,
) -> Result<State<'a>, fmt::Error>
where
I: Iterator<Item = (E, Option<Range<usize>>)>,
E: Borrow<Event<'a>>,
F: fmt::Write,
{
cmark_resume_with_source_range_and_options(event_and_ranges, source, formatter, state, Options::default())
}
/// As [`cmark_resume_with_source_range_and_options`], but with the [`State`] finalized.
pub fn cmark_with_source_range_and_options<'a, I, E, F>(
event_and_ranges: I,
source: &'a str,
mut formatter: F,
options: Options<'_>,
) -> Result<State<'a>, fmt::Error>
where
I: Iterator<Item = (E, Option<Range<usize>>)>,
E: Borrow<Event<'a>>,
F: fmt::Write,
{
let state = cmark_resume_with_source_range_and_options(
event_and_ranges,
source,
&mut formatter,
Default::default(),
options,
)?;
state.finalize(formatter)
}
/// As [`cmark_with_source_range_and_options`], but with default [`Options`].
pub fn cmark_with_source_range<'a, I, E, F>(
event_and_ranges: I,
source: &'a str,
mut formatter: F,
) -> Result<State<'a>, fmt::Error>
where
I: Iterator<Item = (E, Option<Range<usize>>)>,
E: Borrow<Event<'a>>,
F: fmt::Write,
{
cmark_with_source_range_and_options(event_and_ranges, source, &mut formatter, Default::default())
}