This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197// SPDX-License-Identifier: AGPL-3.0-or-later
use std::fmt;
use crate::ast::{Node, ParserFlags};use crate::constants::{MAX_AST_NODES, MAX_LINE_LENGTH};use crate::emoji::EmojiContext;use crate::normalize::{ apply_text_presentation_node, combine_adjacent_text, compact_empty_text_nodes, flatten_top_level_formatting, normalize_nodes,};use crate::text::{self, Line, trim, trim_start};
#[derive(Debug)]pub enum ParseError { Serialize(serde_json::Error),}
impl fmt::Display for ParseError { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Serialize(error) => write!(formatter, "{error}"), } }}
impl std::error::Error for ParseError {}
impl From<serde_json::Error> for ParseError { fn from(value: serde_json::Error) -> Self { Self::Serialize(value) }}
#[derive(Clone, Debug)]pub struct MarkdownParser { flags: u32, emoji_context: EmojiContext, pub(crate) node_count: usize,}
impl MarkdownParser { pub fn new(flags: u32, emoji_context: EmojiContext) -> Self { Self { flags, emoji_context, node_count: 0, } }
pub fn flags(&self) -> u32 { self.flags }
pub fn emoji_context(&self) -> &EmojiContext { &self.emoji_context }
pub fn parse(&mut self, input: &str) -> Result<Vec<Node>, ParseError> { let lines = text::split_lines(input); self.parse_lines(lines) }
pub(crate) fn parse_lines(&mut self, lines: Vec<Line>) -> Result<Vec<Node>, ParseError> { RuntimeState { parser: self, lines, current_line: 0, } .parse() }
pub(crate) fn child_with_flags(&self, flags: u32) -> Self { Self::new(flags, self.emoji_context.clone()) }}
pub(crate) struct RuntimeState<'a> { pub(crate) parser: &'a mut MarkdownParser, pub(crate) lines: Vec<Line>, pub(crate) current_line: usize,}
impl RuntimeState<'_> { fn parse(&mut self) -> Result<Vec<Node>, ParseError> { let mut ast = Vec::new(); if self.lines.is_empty() { return Ok(ast); }
while self.current_line < self.lines.len() && self.parser.node_count <= MAX_AST_NODES { if self.lines[self.current_line].text.len() > MAX_LINE_LENGTH { self.lines[self.current_line].text = text::bounded_line_text(&self.lines[self.current_line].text).to_owned(); } let line = self.lines[self.current_line].text.clone(); let trimmed = trim_start(&line); if trim(trimmed).is_empty() { let blank_count = self.count_blank_lines(); if !ast.is_empty() && self.current_line + blank_count < self.lines.len() { let next_line = self.lines[self.current_line + blank_count].clone(); let next_trimmed = trim_start(&next_line.text); let next_offset = next_line.offset + (next_line.text.len() - next_trimmed.len()); let is_next_heading = crate::block::parse_heading_node(self.parser, next_trimmed, next_offset)? .is_some(); let is_prev_heading = matches!(ast.last(), Some(Node::Heading { .. })); if !is_next_heading && !is_prev_heading { ast.push(Node::Text { content: "\n".repeat(blank_count), }); self.parser.node_count += 1; } } self.current_line += blank_count; continue; } let block = crate::block::parse_block(self)?; if let Some(node) = block.node { ast.push(node); if let Some(extra_nodes) = block.extra_nodes { ast.extend(extra_nodes); } self.current_line = block.new_line_index; self.parser.node_count = block.new_node_count; continue; } self.parse_inline_line(&mut ast)?; self.current_line += 1; } for node in &mut ast { apply_text_presentation_node(node); } if ast.len() > 1 { normalize_nodes(&mut ast, false); flatten_top_level_formatting(&mut ast); combine_adjacent_text(&mut ast, false); } compact_empty_text_nodes(&mut ast); Ok(ast) }
fn count_blank_lines(&self) -> usize { let mut count = 0usize; let mut index = self.current_line; while index < self.lines.len() && trim(&self.lines[index].text).is_empty() { count += 1; index += 1; } count }
fn parse_inline_line(&mut self, ast: &mut Vec<Node>) -> Result<(), ParseError> { let mut text = self.lines[self.current_line].text.clone(); let base_offset = self.lines[self.current_line].offset; let mut consumed = 1usize; while self.current_line + consumed < self.lines.len() { let next_line = &self.lines[self.current_line + consumed].text; let trimmed_next = trim_start(next_line); if crate::block::is_block_start(trimmed_next, self.parser.flags()) || trimmed_next.is_empty() { break; } text.push('\n'); text.push_str(next_line); consumed += 1; } if self.current_line + consumed < self.lines.len() { let next_line = &self.lines[self.current_line + consumed].text; let trimmed_next = trim_start(next_line); let next_is_heading = crate::block::is_heading_start(trimmed_next, self.parser.flags()); let next_is_blockquote = crate::block::is_blockquote_start(trimmed_next, self.parser.flags()); if trimmed_next.is_empty() || (!next_is_heading && !next_is_blockquote) { text.push('\n'); } } let inline_nodes = crate::inline::parse_inline(self.parser, &text, base_offset)?; for node in inline_nodes { ast.push(node); self.parser.node_count += 1; if self.parser.node_count > MAX_AST_NODES { break; } } self.current_line += consumed - 1; Ok(()) }}
#[allow(dead_code)]pub(crate) fn all_flags() -> u32 { ParserFlags::ALL}