use pulldown_cmark::{
Alignment, BlockQuoteKind, CodeBlockKind, Event, LinkType, Options, Parser, Tag, TagEnd,
};
use std::ops::Range;
use crate::{
doc::{Align, Block, BlockKind, Doc, Form, Mark, MarkSpan, QuoteKind, Text},
marks::Marks,
select::Cursor,
};
mod inline;
mod lift;
mod state;
pub use inline::*;
pub use lift::*;
use state::*;
pub(crate) fn split_size(spelled: &str) -> (&str, Option<u32>) {
let Some((name, tail)) = spelled.rsplit_once(char::is_whitespace) else {
return (spelled, None);
};
match tail.parse().ok().filter(|size| *size > 0) {
Some(size) => (name.trim_end(), Some(size)),
None => (spelled, None),
}
}
pub(crate) const OPTIONS: Options = Options::ENABLE_TABLES
.union(Options::ENABLE_STRIKETHROUGH)
.union(Options::ENABLE_TASKLISTS)
.union(Options::ENABLE_GFM);
impl From<BlockQuoteKind> for QuoteKind {
fn from(kind: BlockQuoteKind) -> Self {
match kind {
BlockQuoteKind::Note => Self::Note,
BlockQuoteKind::Tip => Self::Tip,
BlockQuoteKind::Important => Self::Important,
BlockQuoteKind::Warning => Self::Warning,
BlockQuoteKind::Caution => Self::Caution,
}
}
}
pub fn parse(source: &str) -> Doc {
parse_plain(source)
}
pub struct ParsedDoc {
pub doc: Doc,
pub block_ranges: Vec<Range<usize>>,
}
pub fn parse_ranges(source: &str) -> ParsedDoc {
let (doc, starts) = parse_spanned(source);
ParsedDoc {
block_ranges: ranges(&starts, source.len()),
doc,
}
}
impl From<&str> for Doc {
fn from(source: &str) -> Self {
parse(source)
}
}
impl From<&str> for ParsedDoc {
fn from(source: &str) -> Self {
parse_ranges(source)
}
}
impl From<(&str, &Marks)> for Doc {
fn from((source, marks): (&str, &Marks)) -> Self {
parse_with(source, marks)
}
}
fn ranges(starts: &[usize], len: usize) -> Vec<Range<usize>> {
let mut out = Vec::with_capacity(starts.len());
let mut at = 0;
for &start in starts.iter().skip(1) {
let start = start.clamp(at, len);
out.push(at..start);
at = start;
}
if !starts.is_empty() {
out.push(at..len);
}
out
}
pub fn parse_with(source: &str, marks: &Marks) -> Doc {
if marks.is_empty() {
return parse_plain(source);
}
let mut doc = parse_plain(&lift(source, marks));
for block in &mut doc.blocks {
for part in block.parts() {
if let Some(text) = block.text_at_mut(part) {
settle(text, marks);
}
}
}
doc
}
fn parse_plain(source: &str) -> Doc {
parse_spanned(source).0
}
fn parse_spanned(source: &str) -> (Doc, Vec<usize>) {
let mut state = ParseState::default();
for (event, range) in Parser::new_ext(source, OPTIONS).into_offset_iter() {
state.event(event, range);
}
state.doc.renumber();
(state.doc, state.starts)
}
#[derive(Default)]
struct TextBuilder {
text: String,
marks: Vec<MarkSpan>,
open: Vec<usize>,
}
impl TextBuilder {
fn open(&mut self, mark: Mark) {
let ix = self.marks.len();
let at = self.text.len();
self.marks.push(MarkSpan {
range: at..at,
mark,
});
self.open.push(ix);
}
fn is_empty(&self) -> bool {
self.text.is_empty() && self.marks.is_empty()
}
fn close(&mut self) {
if let Some(ix) = self.open.pop() {
self.marks[ix].range.end = self.text.len();
}
}
fn wrap(&mut self, mark: Mark, s: &str) {
let start = self.text.len();
self.text.push_str(s);
self.marks.push(MarkSpan {
range: start..self.text.len(),
mark,
});
}
fn take(&mut self) -> Text {
self.open.clear();
let mut text = normalize(
&std::mem::take(&mut self.text),
&std::mem::take(&mut self.marks),
);
settle_mentions(&mut text);
linkify(&mut text);
text
}
}