diff --git a/Cargo.lock b/Cargo.lock index 8b8821bba573..ec9af66f2b14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -934,6 +934,42 @@ dependencies = [ "tracing", ] +[[package]] +name = "biome_markdown_factory" +version = "0.0.1" +dependencies = [ + "biome_markdown_syntax", + "biome_rowan", +] + +[[package]] +name = "biome_markdown_parser" +version = "0.0.1" +dependencies = [ + "biome_console", + "biome_diagnostics", + "biome_markdown_factory", + "biome_markdown_syntax", + "biome_parser", + "biome_rowan", + "biome_test_utils", + "biome_unicode_table", + "insta", + "quickcheck", + "quickcheck_macros", + "tracing", + "unicode-bom", +] + +[[package]] +name = "biome_markdown_syntax" +version = "0.0.1" +dependencies = [ + "biome_rowan", + "schemars", + "serde", +] + [[package]] name = "biome_markup" version = "0.5.7" diff --git a/Cargo.toml b/Cargo.toml index bc3b9231a4f5..45431422d7c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,6 +132,9 @@ biome_json_factory = { version = "0.5.7", path = "./crates/biome_json_ biome_json_formatter = { version = "0.5.7", path = "./crates/biome_json_formatter" } biome_json_parser = { version = "0.5.7", path = "./crates/biome_json_parser" } biome_json_syntax = { version = "0.5.7", path = "./crates/biome_json_syntax" } +biome_markdown_factory = { version = "0.0.1", path = "./crates/biome_markdown_factory" } +biome_markdown_parser = { version = "0.0.1", path = "./crates/biome_markdown_parser" } +biome_markdown_syntax = { version = "0.0.1", path = "./crates/biome_markdown_syntax" } biome_yaml_factory = { version = "0.0.1", path = "./crates/biome_yaml_factory" } biome_yaml_parser = { version = "0.0.1", path = "./crates/biome_yaml_parser" } biome_yaml_syntax = { version = "0.0.1", path = "./crates/biome_yaml_syntax" } diff --git a/crates/biome_markdown_factory/Cargo.toml b/crates/biome_markdown_factory/Cargo.toml new file mode 100644 index 000000000000..73fc2ac80592 --- /dev/null +++ b/crates/biome_markdown_factory/Cargo.toml @@ -0,0 +1,20 @@ +[package] +authors.workspace = true +categories.workspace = true +description = "Utilities to create Markdown AST for biome_markdown_parser" +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "biome_markdown_factory" +repository.workspace = true +version = "0.0.1" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +biome_markdown_syntax = { workspace = true } +biome_rowan = { workspace = true } + +[lints] +workspace = true diff --git a/crates/biome_markdown_factory/src/generated.rs b/crates/biome_markdown_factory/src/generated.rs new file mode 100644 index 000000000000..52ee5e0dd8a0 --- /dev/null +++ b/crates/biome_markdown_factory/src/generated.rs @@ -0,0 +1,6 @@ +#[rustfmt::skip] +pub(super) mod syntax_factory; +#[rustfmt::skip] +pub mod node_factory; + +pub use syntax_factory::MarkdownSyntaxFactory; diff --git a/crates/biome_markdown_factory/src/generated/node_factory.rs b/crates/biome_markdown_factory/src/generated/node_factory.rs new file mode 100644 index 000000000000..1f5faff07ed8 --- /dev/null +++ b/crates/biome_markdown_factory/src/generated/node_factory.rs @@ -0,0 +1,341 @@ +//! Generated file, do not edit by hand, see `xtask/codegen` + +#![allow(clippy::redundant_closure)] +#![allow(clippy::too_many_arguments)] +use biome_markdown_syntax::{ + MarkdownSyntaxElement as SyntaxElement, MarkdownSyntaxNode as SyntaxNode, + MarkdownSyntaxToken as SyntaxToken, *, +}; +use biome_rowan::AstNode; +pub fn markdown_break_block(value_token: SyntaxToken) -> MarkdownBreakBlock { + MarkdownBreakBlock::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_BREAK_BLOCK, + [Some(SyntaxElement::Token(value_token))], + )) +} +pub fn markdown_bullet_list_item( + markdown_bullet_list: MarkdownBulletList, +) -> MarkdownBulletListItem { + MarkdownBulletListItem::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_BULLET_LIST_ITEM, + [Some(SyntaxElement::Node( + markdown_bullet_list.into_syntax(), + ))], + )) +} +pub fn markdown_document( + value: MarkdownBlockList, + eof_token: SyntaxToken, +) -> MarkdownDocumentBuilder { + MarkdownDocumentBuilder { + value, + eof_token, + bom_token: None, + } +} +pub struct MarkdownDocumentBuilder { + value: MarkdownBlockList, + eof_token: SyntaxToken, + bom_token: Option, +} +impl MarkdownDocumentBuilder { + pub fn with_bom_token(mut self, bom_token: SyntaxToken) -> Self { + self.bom_token = Some(bom_token); + self + } + pub fn build(self) -> MarkdownDocument { + MarkdownDocument::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_DOCUMENT, + [ + self.bom_token.map(|token| SyntaxElement::Token(token)), + Some(SyntaxElement::Node(self.value.into_syntax())), + Some(SyntaxElement::Token(self.eof_token)), + ], + )) + } +} +pub fn markdown_fenced_code_block(markdown_textual: MarkdownTextual) -> MarkdownFencedCodeBlock { + MarkdownFencedCodeBlock::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_FENCED_CODE_BLOCK, + [Some(SyntaxElement::Node(markdown_textual.into_syntax()))], + )) +} +pub fn markdown_hard_line(value_token: SyntaxToken) -> MarkdownHardLine { + MarkdownHardLine::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_HARD_LINE, + [Some(SyntaxElement::Token(value_token))], + )) +} +pub fn markdown_hash(hash_token: SyntaxToken) -> MarkdownHash { + MarkdownHash::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_HASH, + [Some(SyntaxElement::Token(hash_token))], + )) +} +pub fn markdown_header(before: MarkdownHashList, after: MarkdownHashList) -> MarkdownHeaderBuilder { + MarkdownHeaderBuilder { + before, + after, + markdown_paragraph: None, + } +} +pub struct MarkdownHeaderBuilder { + before: MarkdownHashList, + after: MarkdownHashList, + markdown_paragraph: Option, +} +impl MarkdownHeaderBuilder { + pub fn with_markdown_paragraph(mut self, markdown_paragraph: MarkdownParagraph) -> Self { + self.markdown_paragraph = Some(markdown_paragraph); + self + } + pub fn build(self) -> MarkdownHeader { + MarkdownHeader::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_HEADER, + [ + Some(SyntaxElement::Node(self.before.into_syntax())), + self.markdown_paragraph + .map(|token| SyntaxElement::Node(token.into_syntax())), + Some(SyntaxElement::Node(self.after.into_syntax())), + ], + )) + } +} +pub fn markdown_html_block(markdown_textual: MarkdownTextual) -> MarkdownHtmlBlock { + MarkdownHtmlBlock::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_HTML_BLOCK, + [Some(SyntaxElement::Node(markdown_textual.into_syntax()))], + )) +} +pub fn markdown_indent(value_token: SyntaxToken) -> MarkdownIndent { + MarkdownIndent::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_INDENT, + [Some(SyntaxElement::Token(value_token))], + )) +} +pub fn markdown_indent_code_block(markdown_textual: MarkdownTextual) -> MarkdownIndentCodeBlock { + MarkdownIndentCodeBlock::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_INDENT_CODE_BLOCK, + [Some(SyntaxElement::Node(markdown_textual.into_syntax()))], + )) +} +pub fn markdown_inline_code(markdown_textual: MarkdownTextual) -> MarkdownInlineCode { + MarkdownInlineCode::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_INLINE_CODE, + [Some(SyntaxElement::Node(markdown_textual.into_syntax()))], + )) +} +pub fn markdown_inline_emphasis(markdown_textual: MarkdownTextual) -> MarkdownInlineEmphasis { + MarkdownInlineEmphasis::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_INLINE_EMPHASIS, + [Some(SyntaxElement::Node(markdown_textual.into_syntax()))], + )) +} +pub fn markdown_inline_image( + alt: MarkdownTextual, + src: MarkdownTextual, +) -> MarkdownInlineImageBuilder { + MarkdownInlineImageBuilder { + alt, + src, + title: None, + } +} +pub struct MarkdownInlineImageBuilder { + alt: MarkdownTextual, + src: MarkdownTextual, + title: Option, +} +impl MarkdownInlineImageBuilder { + pub fn with_title(mut self, title: MarkdownTextual) -> Self { + self.title = Some(title); + self + } + pub fn build(self) -> MarkdownInlineImage { + MarkdownInlineImage::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_INLINE_IMAGE, + [ + Some(SyntaxElement::Node(self.alt.into_syntax())), + Some(SyntaxElement::Node(self.src.into_syntax())), + self.title + .map(|token| SyntaxElement::Node(token.into_syntax())), + ], + )) + } +} +pub fn markdown_inline_link( + label: MarkdownTextual, + url: MarkdownTextual, +) -> MarkdownInlineLinkBuilder { + MarkdownInlineLinkBuilder { + label, + url, + title: None, + } +} +pub struct MarkdownInlineLinkBuilder { + label: MarkdownTextual, + url: MarkdownTextual, + title: Option, +} +impl MarkdownInlineLinkBuilder { + pub fn with_title(mut self, title: MarkdownTextual) -> Self { + self.title = Some(title); + self + } + pub fn build(self) -> MarkdownInlineLink { + MarkdownInlineLink::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_INLINE_LINK, + [ + Some(SyntaxElement::Node(self.label.into_syntax())), + Some(SyntaxElement::Node(self.url.into_syntax())), + self.title + .map(|token| SyntaxElement::Node(token.into_syntax())), + ], + )) + } +} +pub fn markdown_link_block( + label: MarkdownTextual, + url: MarkdownTextual, +) -> MarkdownLinkBlockBuilder { + MarkdownLinkBlockBuilder { + label, + url, + title: None, + } +} +pub struct MarkdownLinkBlockBuilder { + label: MarkdownTextual, + url: MarkdownTextual, + title: Option, +} +impl MarkdownLinkBlockBuilder { + pub fn with_title(mut self, title: MarkdownTextual) -> Self { + self.title = Some(title); + self + } + pub fn build(self) -> MarkdownLinkBlock { + MarkdownLinkBlock::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_LINK_BLOCK, + [ + Some(SyntaxElement::Node(self.label.into_syntax())), + Some(SyntaxElement::Node(self.url.into_syntax())), + self.title + .map(|token| SyntaxElement::Node(token.into_syntax())), + ], + )) + } +} +pub fn markdown_order_list_item(markdown_bullet_list: MarkdownBulletList) -> MarkdownOrderListItem { + MarkdownOrderListItem::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_ORDER_LIST_ITEM, + [Some(SyntaxElement::Node( + markdown_bullet_list.into_syntax(), + ))], + )) +} +pub fn markdown_paragraph( + markdown_paragraph_item_list: MarkdownParagraphItemList, +) -> MarkdownParagraph { + MarkdownParagraph::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_PARAGRAPH, + [Some(SyntaxElement::Node( + markdown_paragraph_item_list.into_syntax(), + ))], + )) +} +pub fn markdown_quote(any_markdown_block: AnyMarkdownBlock) -> MarkdownQuote { + MarkdownQuote::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_QUOTE, + [Some(SyntaxElement::Node(any_markdown_block.into_syntax()))], + )) +} +pub fn markdown_setext_header(markdown_paragraph: MarkdownParagraph) -> MarkdownSetextHeader { + MarkdownSetextHeader::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_SETEXT_HEADER, + [Some(SyntaxElement::Node(markdown_paragraph.into_syntax()))], + )) +} +pub fn markdown_soft_break(value_token: SyntaxToken) -> MarkdownSoftBreak { + MarkdownSoftBreak::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_SOFT_BREAK, + [Some(SyntaxElement::Token(value_token))], + )) +} +pub fn markdown_textual(value_token: SyntaxToken) -> MarkdownTextual { + MarkdownTextual::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_TEXTUAL, + [Some(SyntaxElement::Token(value_token))], + )) +} +pub fn markdown_block_list(items: I) -> MarkdownBlockList +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + MarkdownBlockList::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_BLOCK_LIST, + items + .into_iter() + .map(|item| Some(item.into_syntax().into())), + )) +} +pub fn markdown_bullet_list(items: I) -> MarkdownBulletList +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + MarkdownBulletList::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_BULLET_LIST, + items + .into_iter() + .map(|item| Some(item.into_syntax().into())), + )) +} +pub fn markdown_hash_list(items: I) -> MarkdownHashList +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + MarkdownHashList::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_HASH_LIST, + items + .into_iter() + .map(|item| Some(item.into_syntax().into())), + )) +} +pub fn markdown_order_list(items: I) -> MarkdownOrderList +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + MarkdownOrderList::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_ORDER_LIST, + items + .into_iter() + .map(|item| Some(item.into_syntax().into())), + )) +} +pub fn markdown_paragraph_item_list(items: I) -> MarkdownParagraphItemList +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + MarkdownParagraphItemList::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_PARAGRAPH_ITEM_LIST, + items + .into_iter() + .map(|item| Some(item.into_syntax().into())), + )) +} +pub fn markdown_bogus(slots: I) -> MarkdownBogus +where + I: IntoIterator>, + I::IntoIter: ExactSizeIterator, +{ + MarkdownBogus::unwrap_cast(SyntaxNode::new_detached( + MarkdownSyntaxKind::MARKDOWN_BOGUS, + slots, + )) +} diff --git a/crates/biome_markdown_factory/src/generated/syntax_factory.rs b/crates/biome_markdown_factory/src/generated/syntax_factory.rs new file mode 100644 index 000000000000..0a1b19d1aa50 --- /dev/null +++ b/crates/biome_markdown_factory/src/generated/syntax_factory.rs @@ -0,0 +1,505 @@ +//! Generated file, do not edit by hand, see `xtask/codegen` + +use biome_markdown_syntax::{MarkdownSyntaxKind, MarkdownSyntaxKind::*, T, *}; +use biome_rowan::{ + AstNode, ParsedChildren, RawNodeSlots, RawSyntaxNode, SyntaxFactory, SyntaxKind, +}; +#[derive(Debug)] +pub struct MarkdownSyntaxFactory; +impl SyntaxFactory for MarkdownSyntaxFactory { + type Kind = MarkdownSyntaxKind; + #[allow(unused_mut)] + fn make_syntax( + kind: Self::Kind, + children: ParsedChildren, + ) -> RawSyntaxNode { + match kind { + MARKDOWN_BOGUS => RawSyntaxNode::new(kind, children.into_iter().map(Some)), + MARKDOWN_BREAK_BLOCK => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if element.kind() == MARKDOWN_BREAK_BLOCK_LITERAL { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_BREAK_BLOCK.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_BREAK_BLOCK, children) + } + MARKDOWN_BULLET_LIST_ITEM => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownBulletList::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_BULLET_LIST_ITEM.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_BULLET_LIST_ITEM, children) + } + MARKDOWN_DOCUMENT => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if element.kind() == T![UNICODE_BOM] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if MarkdownBlockList::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if element.kind() == T![EOF] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_DOCUMENT.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_DOCUMENT, children) + } + MARKDOWN_FENCED_CODE_BLOCK => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_FENCED_CODE_BLOCK.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_FENCED_CODE_BLOCK, children) + } + MARKDOWN_HARD_LINE => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if element.kind() == MARKDOWN_HARD_LINE_LITERAL { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_HARD_LINE.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_HARD_LINE, children) + } + MARKDOWN_HASH => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if element.kind() == T ! [#] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_HASH.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_HASH, children) + } + MARKDOWN_HEADER => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownHashList::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if MarkdownParagraph::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if MarkdownHashList::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_HEADER.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_HEADER, children) + } + MARKDOWN_HTML_BLOCK => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_HTML_BLOCK.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_HTML_BLOCK, children) + } + MARKDOWN_INDENT => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if element.kind() == MARKDOWN_INDENT_CHUNK_LITERAL { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_INDENT.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_INDENT, children) + } + MARKDOWN_INDENT_CODE_BLOCK => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_INDENT_CODE_BLOCK.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_INDENT_CODE_BLOCK, children) + } + MARKDOWN_INLINE_CODE => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_INLINE_CODE.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_INLINE_CODE, children) + } + MARKDOWN_INLINE_EMPHASIS => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_INLINE_EMPHASIS.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_INLINE_EMPHASIS, children) + } + MARKDOWN_INLINE_IMAGE => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_INLINE_IMAGE.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_INLINE_IMAGE, children) + } + MARKDOWN_INLINE_LINK => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_INLINE_LINK.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_INLINE_LINK, children) + } + MARKDOWN_LINK_BLOCK => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if MarkdownTextual::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_LINK_BLOCK.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_LINK_BLOCK, children) + } + MARKDOWN_ORDER_LIST_ITEM => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownBulletList::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_ORDER_LIST_ITEM.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_ORDER_LIST_ITEM, children) + } + MARKDOWN_PARAGRAPH => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownParagraphItemList::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_PARAGRAPH.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_PARAGRAPH, children) + } + MARKDOWN_QUOTE => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if AnyMarkdownBlock::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_QUOTE.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_QUOTE, children) + } + MARKDOWN_SETEXT_HEADER => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if MarkdownParagraph::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_SETEXT_HEADER.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_SETEXT_HEADER, children) + } + MARKDOWN_SOFT_BREAK => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if element.kind() == MARKDOWN_SOFT_BREAK_LITERAL { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_SOFT_BREAK.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_SOFT_BREAK, children) + } + MARKDOWN_TEXTUAL => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if element.kind() == MARKDOWN_TEXTUAL_LITERAL { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + MARKDOWN_TEXTUAL.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(MARKDOWN_TEXTUAL, children) + } + MARKDOWN_BLOCK_LIST => { + Self::make_node_list_syntax(kind, children, AnyMarkdownBlock::can_cast) + } + MARKDOWN_BULLET_LIST => { + Self::make_node_list_syntax(kind, children, AnyCodeBlock::can_cast) + } + MARKDOWN_HASH_LIST => { + Self::make_node_list_syntax(kind, children, MarkdownHash::can_cast) + } + MARKDOWN_ORDER_LIST => { + Self::make_node_list_syntax(kind, children, AnyCodeBlock::can_cast) + } + MARKDOWN_PARAGRAPH_ITEM_LIST => { + Self::make_node_list_syntax(kind, children, AnyMarkdownInline::can_cast) + } + _ => unreachable!("Is {:?} a token?", kind), + } + } +} diff --git a/crates/biome_markdown_factory/src/lib.rs b/crates/biome_markdown_factory/src/lib.rs new file mode 100644 index 000000000000..5eb79128205c --- /dev/null +++ b/crates/biome_markdown_factory/src/lib.rs @@ -0,0 +1,13 @@ +use biome_markdown_syntax::MarkdownLanguage; +use biome_rowan::TreeBuilder; + +mod generated; +pub use crate::generated::MarkdownSyntaxFactory; + +// Re-exported for tests +#[doc(hidden)] +pub use biome_markdown_syntax as syntax; + +pub type DemoSyntaxTreeBuilder = TreeBuilder<'static, MarkdownLanguage, MarkdownSyntaxFactory>; + +pub mod make; diff --git a/crates/biome_markdown_factory/src/make.rs b/crates/biome_markdown_factory/src/make.rs new file mode 100644 index 000000000000..3b57c15562d3 --- /dev/null +++ b/crates/biome_markdown_factory/src/make.rs @@ -0,0 +1 @@ +pub use crate::generated::node_factory::*; diff --git a/crates/biome_markdown_parser/Cargo.toml b/crates/biome_markdown_parser/Cargo.toml new file mode 100644 index 000000000000..8b2b1893f6ef --- /dev/null +++ b/crates/biome_markdown_parser/Cargo.toml @@ -0,0 +1,35 @@ +[package] +authors.workspace = true +categories.workspace = true +description = "Biome's Markdown parser" +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "biome_markdown_parser" +repository.workspace = true +version = "0.0.1" + +[dependencies] +biome_console = { workspace = true } +biome_diagnostics = { workspace = true } +biome_markdown_factory = { workspace = true } +biome_markdown_syntax = { workspace = true } +biome_parser = { workspace = true } +biome_rowan = { workspace = true } +biome_test_utils = { workspace = true } +biome_unicode_table = { workspace = true } +tracing = { workspace = true } +unicode-bom = { workspace = true } + +[dev-dependencies] +insta = { workspace = true } +quickcheck = { workspace = true } +quickcheck_macros = { workspace = true } + +# cargo-workspaces metadata +[package.metadata.workspaces] +independent = true + +[lints] +workspace = true diff --git a/crates/biome_markdown_parser/src/lexer/mod.rs b/crates/biome_markdown_parser/src/lexer/mod.rs new file mode 100644 index 000000000000..8f9e2ac4c8f2 --- /dev/null +++ b/crates/biome_markdown_parser/src/lexer/mod.rs @@ -0,0 +1,375 @@ +//! An extremely fast, lookup table based, JSON lexer which yields SyntaxKind tokens used by the rome-json parser. + +#[rustfmt::skip] +mod tests; + +use biome_markdown_syntax::MarkdownSyntaxKind::*; +use biome_markdown_syntax::{MarkdownSyntaxKind, T}; +use biome_parser::diagnostic::ParseDiagnostic; +use biome_parser::lexer::{ + LexContext, Lexer, LexerCheckpoint, LexerWithCheckpoint, ReLexer, TokenFlags, +}; +use biome_rowan::TextSize; +use biome_unicode_table::{lookup_byte, Dispatch::*}; + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub enum MarkdownLexContext { + #[default] + Regular, +} + +impl LexContext for MarkdownLexContext { + /// Returns true if this is [MarkdownLexContext::Regular] + fn is_regular(&self) -> bool { + matches!(self, MarkdownLexContext::Regular) + } +} + +/// Context in which the [MarkdownLexContext]'s current should be re-lexed. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum MarkdownReLexContext { + #[allow(dead_code)] + Regular, + // UnicodeRange, +} + +/// An extremely fast, lookup table based, lossless Markdown lexer +#[derive(Debug)] +pub(crate) struct MarkdownLexer<'src> { + /// Source text + source: &'src str, + + /// The start byte position in the source text of the next token. + position: usize, + + /// `true` if there has been a line break between the last non-trivia token and the next non-trivia token. + after_newline: bool, + + /// If the source starts with a Unicode BOM, this is the number of bytes for that token. + unicode_bom_length: usize, + + /// Byte offset of the current token from the start of the source. + /// + /// The range of the current token can be computed by `self.position - self.current_start` + current_start: TextSize, + + /// The kind of the current token + current_kind: MarkdownSyntaxKind, + + /// Flags for the current token + current_flags: TokenFlags, + + diagnostics: Vec, +} + +impl<'src> Lexer<'src> for MarkdownLexer<'src> { + const NEWLINE: Self::Kind = NEWLINE; + + const WHITESPACE: Self::Kind = WHITESPACE; + type Kind = MarkdownSyntaxKind; + type LexContext = MarkdownLexContext; + type ReLexContext = MarkdownReLexContext; + + fn source(&self) -> &'src str { + self.source + } + + fn current(&self) -> Self::Kind { + self.current_kind + } + + fn position(&self) -> usize { + self.position + } + + fn current_start(&self) -> TextSize { + self.current_start + } + + fn push_diagnostic(&mut self, diagnostic: ParseDiagnostic) { + self.diagnostics.push(diagnostic); + } + + fn next_token(&mut self, _context: Self::LexContext) -> Self::Kind { + self.current_start = self.text_position(); + self.current_flags = TokenFlags::empty(); + + let kind = match self.current_byte() { + Some(current) => self.consume_token(current), + None => EOF, + }; + self.current_kind = kind; + + kind + } + + fn has_preceding_line_break(&self) -> bool { + self.current_flags.has_preceding_line_break() + } + + fn has_unicode_escape(&self) -> bool { + self.current_flags.has_unicode_escape() + } + + fn rewind(&mut self, checkpoint: LexerCheckpoint) { + let LexerCheckpoint { + position, + current_start, + current_flags, + current_kind, + after_line_break, + unicode_bom_length, + diagnostics_pos, + } = checkpoint; + + let new_pos = u32::from(position) as usize; + + self.position = new_pos; + self.current_kind = current_kind; + self.current_start = current_start; + self.current_flags = current_flags; + self.after_newline = after_line_break; + self.unicode_bom_length = unicode_bom_length; + self.diagnostics.truncate(diagnostics_pos as usize); + } + + fn finish(self) -> Vec { + self.diagnostics + } + + fn current_flags(&self) -> TokenFlags { + self.current_flags + } + + #[inline] + fn advance_char_unchecked(&mut self) { + let c = self.current_char_unchecked(); + self.position += c.len_utf8(); + } + + /// Advances the current position by `n` bytes. + #[inline] + fn advance(&mut self, n: usize) { + self.position += n; + } +} + +impl<'src> MarkdownLexer<'src> { + /// Make a new lexer from a str, this is safe because strs are valid utf8 + pub fn from_str(source: &'src str) -> Self { + Self { + source, + after_newline: false, + unicode_bom_length: 0, + current_kind: TOMBSTONE, + current_start: TextSize::from(0), + current_flags: TokenFlags::empty(), + position: 0, + diagnostics: vec![], + } + } + + pub(crate) fn consume_token(&mut self, current: u8) -> MarkdownSyntaxKind { + let dispatched = lookup_byte(current); + match dispatched { + WHS => self.consume_newline_or_whitespace(), + MUL => self.consume_byte(T![*]), + MIN => self.consume_byte(T![-]), + IDT => self.consume_byte(T![_]), + _ => self.consume_textual(), + } + } + + fn text_position(&self) -> TextSize { + TextSize::try_from(self.position).expect("Input to be smaller than 4 GB") + } + + /// Bumps the current byte and creates a lexed token of the passed in kind + #[allow(dead_code)] + fn eat_byte(&mut self, tok: MarkdownSyntaxKind) -> MarkdownSyntaxKind { + self.advance(1); + tok + } + /// Returns the byte at position `self.position + offset` or `None` if it is out of bounds. + #[inline] + fn byte_at(&self, offset: usize) -> Option { + self.source() + .as_bytes() + .get(self.position() + offset) + .copied() + } + + /// Peeks at the next byte + #[inline] + fn peek_byte(&self) -> Option { + self.byte_at(1) + } + + /// Consume one newline or all whitespace until a non-whitespace or a newline is found. + /// + /// ## Safety + /// Must be called at a valid UT8 char boundary + fn consume_newline_or_whitespace(&mut self) -> MarkdownSyntaxKind { + match self.current_byte() { + Some(b'\n' | b'\r') => self.consume_newline(), + Some(b' ') => self.consume_whitespace(), + Some(b'\t') => self.consume_tab(), + _ => self.consume_textual(), + } + } + + /// Consume just one newline/line break. + /// + /// ## Safety + /// Must be called at a valid UT8 char boundary + fn consume_newline(&mut self) -> MarkdownSyntaxKind { + self.assert_at_char_boundary(); + + match self.current_byte() { + Some(b'\n') => { + self.advance(1); + } + Some(b'\r') => { + if self.peek_byte() == Some(b'\n') { + self.advance(2) + } else { + self.advance(1) + } + } + _ => unreachable!(), + } + NEWLINE + } + + /// Consumes all whitespace until a non-whitespace or a newline is found. + /// + /// ## Safety + /// Must be called at a valid UT8 char boundary + fn consume_whitespace(&mut self) -> MarkdownSyntaxKind { + self.assert_at_char_boundary(); + while let Some(b' ') = self.current_byte() { + self.advance(1); + } + + WHITESPACE + } + + fn consume_tab(&mut self) -> MarkdownSyntaxKind { + self.assert_at_char_boundary(); + + if matches!(self.current_byte(), Some(b'\t')) { + self.advance(1) + } + TAB + } + + /// Get the UTF8 char which starts at the current byte + /// + /// ## Safety + /// Must be called at a valid UT8 char boundary + fn current_char_unchecked(&self) -> char { + // Precautionary measure for making sure the unsafe code below does not read over memory boundary + debug_assert!(!self.is_eof()); + self.assert_at_char_boundary(); + + // Safety: We know this is safe because we require the input to the lexer to be valid utf8 and we always call this when we are at a char + let string = unsafe { + std::str::from_utf8_unchecked(self.source.as_bytes().get_unchecked(self.position..)) + }; + let chr = if let Some(chr) = string.chars().next() { + chr + } else { + // Safety: we always call this when we are at a valid char, so this branch is completely unreachable + unsafe { + core::hint::unreachable_unchecked(); + } + }; + + chr + } + + /// Gets the current byte. + /// + /// ## Returns + /// The current byte if the lexer isn't at the end of the file. + #[inline] + fn current_byte(&self) -> Option { + if self.is_eof() { + None + } else { + Some(self.source.as_bytes()[self.position]) + } + } + + /// Asserts that the lexer is at a UTF8 char boundary + #[inline] + fn assert_at_char_boundary(&self) { + debug_assert!(self.source.is_char_boundary(self.position)); + } + + /// Advances the current position by `n` bytes. + #[inline] + fn advance(&mut self, n: usize) { + self.position += n; + } + + /// Returns `true` if the parser is at or passed the end of the file. + #[inline] + fn is_eof(&self) -> bool { + self.position >= self.source.len() + } + + #[inline] + fn consume_textual(&mut self) -> MarkdownSyntaxKind { + self.assert_at_char_boundary(); + + let char = self.current_char_unchecked(); + self.advance(char.len_utf8()); + + MarkdownSyntaxKind::MARKDOWN_TEXTUAL_LITERAL + } + + /// Bumps the current byte and creates a lexed token of the passed in kind + fn consume_byte(&mut self, tok: MarkdownSyntaxKind) -> MarkdownSyntaxKind { + self.advance(1); + tok + } +} + +impl<'src> ReLexer<'src> for MarkdownLexer<'src> { + fn re_lex(&mut self, context: Self::ReLexContext) -> Self::Kind { + let old_position = self.position; + self.position = u32::from(self.current_start) as usize; + + let re_lexed_kind = match self.current_byte() { + Some(current) => match context { + MarkdownReLexContext::Regular => self.consume_token(current), + // MarkdownReLexContext::UnicodeRange => self.consume_unicode_range_token(current), + }, + None => EOF, + }; + + if self.current() == re_lexed_kind { + // Didn't re-lex anything. Return existing token again + self.position = old_position; + } else { + self.current_kind = re_lexed_kind; + } + + re_lexed_kind + } +} + +impl<'src> LexerWithCheckpoint<'src> for MarkdownLexer<'src> { + fn checkpoint(&self) -> LexerCheckpoint { + LexerCheckpoint { + position: TextSize::from(self.position as u32), + current_start: self.current_start, + current_flags: self.current_flags, + current_kind: self.current_kind, + after_line_break: self.after_newline, + unicode_bom_length: self.unicode_bom_length, + diagnostics_pos: self.diagnostics.len() as u32, + } + } +} diff --git a/crates/biome_markdown_parser/src/lexer/tests.rs b/crates/biome_markdown_parser/src/lexer/tests.rs new file mode 100644 index 000000000000..9ac625263f32 --- /dev/null +++ b/crates/biome_markdown_parser/src/lexer/tests.rs @@ -0,0 +1,116 @@ +#![cfg(test)] +#![allow(unused_mut, unused_variables, unused_assignments)] + +use biome_parser::lexer::Lexer; +use biome_markdown_syntax::MarkdownSyntaxKind::*; +use crate::lexer::MarkdownLexContext; +use super::{MarkdownLexer, TextSize}; +use quickcheck_macros::quickcheck; +use std::sync::mpsc::channel; +use std::thread; +use std::time::Duration; + +// Assert the result of lexing a piece of source code, +// and make sure the tokens yielded are fully lossless and the source can be reconstructed from only the tokens +macro_rules! assert_lex { + ($src:expr, $($kind:ident:$len:expr $(,)?)*) => {{ + let mut lexer = MarkdownLexer::from_str($src); + let mut idx = 0; + let mut tok_idx = TextSize::default(); + + let mut new_str = String::with_capacity($src.len()); + let mut tokens = vec![]; + + while lexer.next_token(MarkdownLexContext::default()) != EOF { + tokens.push((lexer.current(), lexer.current_range())); + } + + $( + assert_eq!( + tokens[idx].0, + biome_markdown_syntax::MarkdownSyntaxKind::$kind, + "expected token kind {}, but found {:?}", + stringify!($kind), + tokens[idx].0, + ); + + assert_eq!( + tokens[idx].1.len(), + TextSize::from($len), + "expected token length of {}, but found {:?} for token {:?}", + $len, + tokens[idx].1.len(), + tokens[idx].0, + ); + + new_str.push_str(&$src[tokens[idx].1]); + tok_idx += tokens[idx].1.len(); + + idx += 1; + )* + + if idx < tokens.len() { + panic!( + "expected {} tokens but lexer returned {}, first unexpected token is '{:?}'", + idx, + tokens.len(), + tokens[idx].0 + ); + } else { + assert_eq!(idx, tokens.len()); + } + + assert_eq!($src, new_str, "Failed to reconstruct input"); + }}; +} + +// This is for testing if the lexer is truly lossless +// It parses random strings and puts them back together with the produced tokens and compares +#[quickcheck] +fn losslessness(string: String) -> bool { + // using an mpsc channel allows us to spawn a thread and spawn the lexer there, then if + // it takes more than 2 seconds we panic because it is 100% infinite recursion + let cloned = string.clone(); + let (sender, receiver) = channel(); + thread::spawn(move || { + let mut lexer = MarkdownLexer::from_str(&cloned); + let mut tokens = vec![]; + + while lexer.next_token(MarkdownLexContext::default()) != EOF { + tokens.push(lexer.current_range()); + } + + sender + .send(tokens) + .expect("Could not send tokens to receiver"); + }); + let token_ranges = receiver + .recv_timeout(Duration::from_secs(2)) + .unwrap_or_else(|_| panic!("Lexer is infinitely recursing with this code: ->{string}<-")); + + let mut new_str = String::with_capacity(string.len()); + let mut idx = TextSize::from(0); + + for range in token_ranges { + new_str.push_str(&string[range]); + idx += range.len(); + } + + string == new_str +} + +#[test] +fn empty() { + assert_lex! { + "", + } +} + +#[test] +fn textual() { + assert_lex! { + "+", + MARKDOWN_TEXTUAL_LITERAL:1, + } +} + diff --git a/crates/biome_markdown_parser/src/lib.rs b/crates/biome_markdown_parser/src/lib.rs new file mode 100644 index 000000000000..245014f971d2 --- /dev/null +++ b/crates/biome_markdown_parser/src/lib.rs @@ -0,0 +1,77 @@ +use biome_markdown_factory::MarkdownSyntaxFactory; +use biome_markdown_syntax::{MarkdownDocument, MarkdownLanguage, MarkdownSyntaxNode}; +use biome_parser::{prelude::ParseDiagnostic, tree_sink::LosslessTreeSink}; +use biome_rowan::{AstNode, NodeCache}; +use parser::MarkdownParser; +use syntax::parse_document; + +mod lexer; +mod parser; +mod syntax; +mod token_source; + +pub(crate) type MarkdownLosslessTreeSink<'source> = + LosslessTreeSink<'source, MarkdownLanguage, MarkdownSyntaxFactory>; + +pub fn parse_markdown(source: &str) -> MarkdownParse { + let mut cache = NodeCache::default(); + parse_markdown_with_cache(source, &mut cache) +} + +pub fn parse_markdown_with_cache(source: &str, cache: &mut NodeCache) -> MarkdownParse { + tracing::debug_span!("Parsing phase").in_scope(move || { + let mut parser = MarkdownParser::new(source); + + parse_document(&mut parser); + + let (events, diagnostics, trivia) = parser.finish(); + + let mut tree_sink = MarkdownLosslessTreeSink::with_cache(source, &trivia, cache); + biome_parser::event::process(&mut tree_sink, events, diagnostics); + let (green, diagnostics) = tree_sink.finish(); + + MarkdownParse::new(green, diagnostics) + }) +} + +/// A utility struct for managing the result of a parser job +#[derive(Debug)] +pub struct MarkdownParse { + root: MarkdownSyntaxNode, + diagnostics: Vec, +} + +impl MarkdownParse { + pub fn new(root: MarkdownSyntaxNode, diagnostics: Vec) -> MarkdownParse { + MarkdownParse { root, diagnostics } + } + + pub fn syntax(&self) -> MarkdownSyntaxNode { + self.root.clone() + } + + /// Get the diagnostics which occurred when parsing + pub fn diagnostics(&self) -> &[ParseDiagnostic] { + &self.diagnostics + } + + /// Get the diagnostics which occurred when parsing + pub fn into_diagnostics(self) -> Vec { + self.diagnostics + } + + /// Returns [true] if the parser encountered some errors during the parsing. + pub fn has_errors(&self) -> bool { + self.diagnostics + .iter() + .any(|diagnostic| diagnostic.is_error()) + } + + /// Convert this parse result into a typed AST node. + /// + /// # Panics + /// Panics if the node represented by this parse result mismatches. + pub fn tree(&self) -> MarkdownDocument { + MarkdownDocument::unwrap_cast(self.syntax()) + } +} diff --git a/crates/biome_markdown_parser/src/parser.rs b/crates/biome_markdown_parser/src/parser.rs new file mode 100644 index 000000000000..fe8a620f5d86 --- /dev/null +++ b/crates/biome_markdown_parser/src/parser.rs @@ -0,0 +1,78 @@ +use biome_markdown_syntax::MarkdownSyntaxKind; +use biome_parser::event::Event; +use biome_parser::prelude::*; +use biome_parser::token_source::Trivia; +use biome_parser::ParserContext; +use biome_parser::{diagnostic::merge_diagnostics, ParserContextCheckpoint}; + +use crate::token_source::{MarkdownTokenSource, MarkdownTokenSourceCheckpoint}; + +pub(crate) struct MarkdownParser<'source> { + context: ParserContext, + source: MarkdownTokenSource<'source>, +} + +impl<'source> MarkdownParser<'source> { + pub fn new(source: &'source str) -> Self { + Self { + context: ParserContext::default(), + source: MarkdownTokenSource::from_str(source), + } + } + #[allow(dead_code)] + pub fn checkpoint(&self) -> MarkdownParserCheckpoint { + MarkdownParserCheckpoint { + context: self.context.checkpoint(), + source: self.source.checkpoint(), + } + } + #[allow(dead_code)] + pub fn rewind(&mut self, checkpoint: MarkdownParserCheckpoint) { + let MarkdownParserCheckpoint { context, source } = checkpoint; + + self.context.rewind(context); + self.source.rewind(source); + } + + pub fn finish( + self, + ) -> ( + Vec>, + Vec, + Vec, + ) { + let (trivia, lexer_diagnostics) = self.source.finish(); + let (events, parse_diagnostics) = self.context.finish(); + + let diagnostics = merge_diagnostics(lexer_diagnostics, parse_diagnostics); + + (events, diagnostics, trivia) + } +} + +impl<'source> Parser for MarkdownParser<'source> { + type Kind = MarkdownSyntaxKind; + type Source = MarkdownTokenSource<'source>; + + fn context(&self) -> &ParserContext { + &self.context + } + + fn context_mut(&mut self) -> &mut ParserContext { + &mut self.context + } + + fn source(&self) -> &Self::Source { + &self.source + } + + fn source_mut(&mut self) -> &mut Self::Source { + &mut self.source + } +} + +#[allow(dead_code)] +pub struct MarkdownParserCheckpoint { + pub(super) context: ParserContextCheckpoint, + pub(super) source: MarkdownTokenSourceCheckpoint, +} diff --git a/crates/biome_markdown_parser/src/syntax.rs b/crates/biome_markdown_parser/src/syntax.rs new file mode 100644 index 000000000000..ee2d9dfb14c8 --- /dev/null +++ b/crates/biome_markdown_parser/src/syntax.rs @@ -0,0 +1,33 @@ +use biome_markdown_syntax::{kind::MarkdownSyntaxKind::*, T}; +use biome_parser::Parser; + +use crate::MarkdownParser; + +pub(crate) fn parse_document(p: &mut MarkdownParser) { + let m = p.start(); + parse_block_list(p); + m.complete(p, MARKDOWN_DOCUMENT); +} + +pub(crate) fn parse_block_list(p: &mut MarkdownParser) { + let m = p.start(); + + while !p.at(T![EOF]) { + parse_any_block(p); + } + m.complete(p, MARKDOWN_BLOCK_LIST); +} + +pub(crate) fn parse_any_block(p: &mut MarkdownParser) { + if at_break_block(p) { + parse_break_block(p); + } +} + +pub(crate) fn at_break_block(p: &mut MarkdownParser) -> bool { + p.at(MARKDOWN_BREAK_BLOCK_LITERAL) +} + +pub(crate) fn parse_break_block(_p: &mut MarkdownParser) { + todo!() +} diff --git a/crates/biome_markdown_parser/src/token_source.rs b/crates/biome_markdown_parser/src/token_source.rs new file mode 100644 index 000000000000..6781e2684d70 --- /dev/null +++ b/crates/biome_markdown_parser/src/token_source.rs @@ -0,0 +1,149 @@ +use crate::lexer::{MarkdownLexContext, MarkdownLexer, MarkdownReLexContext}; +use biome_markdown_syntax::MarkdownSyntaxKind; +use biome_markdown_syntax::MarkdownSyntaxKind::EOF; +use biome_parser::lexer::BufferedLexer; +use biome_parser::prelude::{BumpWithContext, TokenSource}; +use biome_parser::token_source::{TokenSourceWithBufferedLexer, Trivia}; +use biome_parser::{diagnostic::ParseDiagnostic, token_source::TokenSourceCheckpoint}; +use biome_rowan::{TextRange, TriviaPieceKind}; + +pub(crate) struct MarkdownTokenSource<'source> { + lexer: BufferedLexer>, + + /// List of the skipped trivia. Needed to construct the CST and compute the non-trivia token offsets. + pub(super) trivia_list: Vec, +} + +#[allow(dead_code)] +pub(crate) type MarkdownTokenSourceCheckpoint = TokenSourceCheckpoint; + +impl<'source> MarkdownTokenSource<'source> { + /// Creates a new token source. + pub(crate) fn new( + lexer: BufferedLexer>, + ) -> MarkdownTokenSource<'source> { + MarkdownTokenSource { + lexer, + trivia_list: vec![], + } + } + + /// Creates a new token source for the given string + pub fn from_str(source: &'source str) -> Self { + let lexer = MarkdownLexer::from_str(source); + + let buffered = BufferedLexer::new(lexer); + let mut source = MarkdownTokenSource::new(buffered); + + source.next_non_trivia_token(MarkdownLexContext::default(), true); + source + } + + fn next_non_trivia_token(&mut self, context: MarkdownLexContext, first_token: bool) { + let mut trailing = !first_token; + + loop { + let kind = self.lexer.next_token(context); + + let trivia_kind = TriviaPieceKind::try_from(kind); + + match trivia_kind { + Err(_) => { + // Not trivia + break; + } + Ok(trivia_kind) => { + if trivia_kind.is_newline() { + trailing = false; + } + + self.trivia_list + .push(Trivia::new(trivia_kind, self.current_range(), trailing)); + } + } + } + } + #[allow(dead_code)] + pub fn re_lex(&mut self, mode: MarkdownReLexContext) -> MarkdownSyntaxKind { + self.lexer.re_lex(mode) + } + + /// Creates a checkpoint to which it can later return using [Self::rewind]. + #[allow(dead_code)] + pub fn checkpoint(&self) -> MarkdownTokenSourceCheckpoint { + MarkdownTokenSourceCheckpoint { + trivia_len: self.trivia_list.len() as u32, + lexer_checkpoint: self.lexer.checkpoint(), + } + } + + /// Restores the token source to a previous state + #[allow(dead_code)] + pub fn rewind(&mut self, checkpoint: MarkdownTokenSourceCheckpoint) { + assert!(self.trivia_list.len() >= checkpoint.trivia_len as usize); + self.trivia_list.truncate(checkpoint.trivia_len as usize); + self.lexer.rewind(checkpoint.lexer_checkpoint); + } +} + +impl<'source> TokenSource for MarkdownTokenSource<'source> { + type Kind = MarkdownSyntaxKind; + + fn current(&self) -> Self::Kind { + self.lexer.current() + } + + fn current_range(&self) -> TextRange { + self.lexer.current_range() + } + + fn text(&self) -> &str { + self.lexer.source() + } + + fn has_preceding_line_break(&self) -> bool { + self.lexer.has_preceding_line_break() + } + + fn bump(&mut self) { + self.bump_with_context(MarkdownLexContext::Regular) + } + + fn skip_as_trivia(&mut self) { + self.skip_as_trivia_with_context(MarkdownLexContext::Regular) + } + + fn finish(self) -> (Vec, Vec) { + (self.trivia_list, self.lexer.finish()) + } +} + +impl<'source> BumpWithContext for MarkdownTokenSource<'source> { + type Context = MarkdownLexContext; + + fn bump_with_context(&mut self, context: Self::Context) { + if self.current() != EOF { + self.next_non_trivia_token(context, false); + } + } + + fn skip_as_trivia_with_context(&mut self, context: Self::Context) { + if self.current() != EOF { + self.trivia_list.push(Trivia::new( + TriviaPieceKind::Skipped, + self.current_range(), + false, + )); + + self.next_non_trivia_token(context, true) + } + } +} + +impl<'source> TokenSourceWithBufferedLexer> + for MarkdownTokenSource<'source> +{ + fn lexer(&mut self) -> &mut BufferedLexer> { + &mut self.lexer + } +} diff --git a/crates/biome_markdown_parser/tests/spec_test.rs b/crates/biome_markdown_parser/tests/spec_test.rs new file mode 100644 index 000000000000..307f9bc0d578 --- /dev/null +++ b/crates/biome_markdown_parser/tests/spec_test.rs @@ -0,0 +1,135 @@ +use biome_console::fmt::{Formatter, Termcolor}; +use biome_console::markup; +use biome_diagnostics::display::PrintDiagnostic; +use biome_diagnostics::termcolor; +use biome_diagnostics::DiagnosticExt; +use biome_markdown_parser::parse_markdown; +use biome_rowan::SyntaxKind; +use biome_test_utils::has_bogus_nodes_or_empty_slots; +use std::fmt::Write; +use std::fs; +use std::path::Path; + +#[derive(Copy, Clone)] +pub enum ExpectedOutcome { + Pass, + Fail, + Undefined, +} + +pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_str: &str) { + let outcome = match outcome_str { + "ok" => ExpectedOutcome::Pass, + "error" => ExpectedOutcome::Fail, + "undefined" => ExpectedOutcome::Undefined, + _ => panic!("Invalid expected outcome {outcome_str}"), + }; + + let test_case_path = Path::new(test_case); + + let file_name = test_case_path + .file_name() + .expect("Expected test to have a file name") + .to_str() + .expect("File name to be valid UTF8"); + + let content = fs::read_to_string(test_case_path) + .expect("Expected test path to be a readable file in UTF8 encoding"); + + let parsed = parse_markdown(&content); + let formatted_ast = format!("{:#?}", parsed.tree()); + + let mut snapshot = String::new(); + writeln!(snapshot, "\n## Input\n\n```\n{content}\n```\n\n").unwrap(); + + writeln!( + snapshot, + r#"## AST + +``` +{formatted_ast} +``` + +## CST + +``` +{:#?} +``` +"#, + parsed.syntax() + ) + .unwrap(); + + let diagnostics = parsed.diagnostics(); + if !diagnostics.is_empty() { + let mut diagnostics_buffer = termcolor::Buffer::no_color(); + + let termcolor = &mut Termcolor(&mut diagnostics_buffer); + let mut formatter = Formatter::new(termcolor); + + for diagnostic in diagnostics { + let error = diagnostic + .clone() + .with_file_path(file_name) + .with_file_source_code(&content); + formatter + .write_markup(markup! { + {PrintDiagnostic::verbose(&error)} + }) + .expect("failed to emit diagnostic"); + } + + let formatted_diagnostics = + std::str::from_utf8(diagnostics_buffer.as_slice()).expect("non utf8 in error buffer"); + + if matches!(outcome, ExpectedOutcome::Pass) { + panic!("Expected no errors to be present in a test case that is expected to pass but the following diagnostics are present:\n{formatted_diagnostics}") + } + + writeln!(snapshot, "## Diagnostics\n\n```").unwrap(); + snapshot.write_str(formatted_diagnostics).unwrap(); + + writeln!(snapshot, "```\n").unwrap(); + } + + match outcome { + ExpectedOutcome::Pass => { + let missing_required = formatted_ast.contains("missing (required)"); + if missing_required + || parsed + .syntax() + .descendants() + .any(|node| node.kind().is_bogus()) + { + panic!("Parsed tree of a 'OK' test case should not contain any missing required children or bogus nodes"); + } + } + ExpectedOutcome::Fail => { + if parsed.diagnostics().is_empty() { + panic!("Failing test must have diagnostics"); + } + } + _ => {} + } + + insta::with_settings!({ + prepend_module_to_snapshot => false, + snapshot_path => &test_directory, + }, { + insta::assert_snapshot!(file_name, snapshot); + }); +} + +#[ignore] +#[test] +pub fn quick_test() { + let code = r#" + "#; + + let root = parse_markdown(code); + let syntax = root.syntax(); + dbg!(&syntax, root.diagnostics(), root.has_errors()); + if has_bogus_nodes_or_empty_slots(&syntax) { + panic!("modified tree has bogus nodes or empty slots:\n{syntax:#?} \n\n {syntax}") + } +} diff --git a/crates/biome_markdown_syntax/Cargo.toml b/crates/biome_markdown_syntax/Cargo.toml new file mode 100644 index 000000000000..4bce253cad07 --- /dev/null +++ b/crates/biome_markdown_syntax/Cargo.toml @@ -0,0 +1,24 @@ +[package] +authors.workspace = true +categories.workspace = true +description = "SyntaxKind and common rowan definitions for biome_markdown_parser" +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "biome_markdown_syntax" +repository.workspace = true +version = "0.0.1" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +biome_rowan = { workspace = true, features = ["serde"] } +schemars = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"] } + +[features] +schema = ["biome_rowan/serde", "schemars"] + +[lints] +workspace = true diff --git a/crates/biome_markdown_syntax/src/generated.rs b/crates/biome_markdown_syntax/src/generated.rs new file mode 100644 index 000000000000..e62bfed1de4e --- /dev/null +++ b/crates/biome_markdown_syntax/src/generated.rs @@ -0,0 +1,9 @@ +#[rustfmt::skip] +pub(super) mod nodes; +#[rustfmt::skip] +pub mod macros; +#[macro_use] +pub mod kind; + +pub use kind::*; +pub use nodes::*; diff --git a/crates/biome_markdown_syntax/src/generated/kind.rs b/crates/biome_markdown_syntax/src/generated/kind.rs new file mode 100644 index 000000000000..daa86205cc97 --- /dev/null +++ b/crates/biome_markdown_syntax/src/generated/kind.rs @@ -0,0 +1,137 @@ +//! Generated file, do not edit by hand, see `xtask/codegen` + +#![allow(clippy::all)] +#![allow(bad_style, missing_docs, unreachable_pub)] +#[doc = r" The kind of syntax node, e.g. `IDENT`, `FUNCTION_KW`, or `FOR_STMT`."] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[repr(u16)] +pub enum MarkdownSyntaxKind { + #[doc(hidden)] + TOMBSTONE, + #[doc = r" Marks the end of the file. May have trivia attached"] + EOF, + #[doc = r" Any Unicode BOM character that may be present at the start of"] + #[doc = r" a file."] + UNICODE_BOM, + L_ANGLE, + R_ANGLE, + L_PAREN, + R_PAREN, + L_BRACK, + R_BRACK, + SLASH, + EQ, + BANG, + MINUS, + STAR, + BACKTICK, + TILDE, + WHITESPACE3, + UNDERSCORE, + HASH, + FALSE_KW, + MARKDOWN_HARD_LINE_LITERAL, + MARKDOWN_SOFT_BREAK_LITERAL, + MARKDOWN_TEXTUAL_LITERAL, + MARKDOWN_STRING_LITERAL, + MARKDOWN_INDENT_CHUNK_LITERAL, + MARKDOWN_BREAK_BLOCK_LITERAL, + NEWLINE, + WHITESPACE, + TAB, + BOGUS, + MARKDOWN_BOGUS, + MARKDOWN_DOCUMENT, + MARKDOWN_BLOCK_LIST, + MARKDOWN_HASH_LIST, + MARKDOWN_HASH, + MARKDOWN_HEADER, + MARKDOWN_INDENT_CODE_BLOCK, + MARKDOWN_FENCED_CODE_BLOCK, + MARKDOWN_HTML_BLOCK, + MARKDOWN_LINK_BLOCK, + MARKDOWN_QUOTE, + MARKDOWN_ORDER_LIST_ITEM, + MARKDOWN_BULLET_LIST_ITEM, + MARKDOWN_BULLET_LIST, + MARKDOWN_ORDER_LIST, + MARKDOWN_PARAGRAPH, + MARKDOWN_PARAGRAPH_ITEM_LIST, + MARKDOWN_INLINE_CODE, + MARKDOWN_INLINE_EMPHASIS, + MARKDOWN_INLINE_LINK, + MARKDOWN_INLINE_IMAGE, + MARKDOWN_HARD_LINE, + MARKDOWN_SOFT_BREAK, + MARKDOWN_TEXTUAL, + MARKDOWN_SETEXT_HEADER, + MARKDOWN_STRING, + MARKDOWN_INDENT, + MARKDOWN_BREAK_BLOCK, + #[doc(hidden)] + __LAST, +} +use self::MarkdownSyntaxKind::*; +impl MarkdownSyntaxKind { + pub const fn is_punct(self) -> bool { + match self { + L_ANGLE | R_ANGLE | L_PAREN | R_PAREN | L_BRACK | R_BRACK | SLASH | EQ | BANG + | MINUS | STAR | BACKTICK | TILDE | WHITESPACE3 | UNDERSCORE | HASH => true, + _ => false, + } + } + pub const fn is_literal(self) -> bool { + match self { + MARKDOWN_HARD_LINE_LITERAL + | MARKDOWN_SOFT_BREAK_LITERAL + | MARKDOWN_TEXTUAL_LITERAL + | MARKDOWN_STRING_LITERAL + | MARKDOWN_INDENT_CHUNK_LITERAL + | MARKDOWN_BREAK_BLOCK_LITERAL => true, + _ => false, + } + } + pub const fn is_list(self) -> bool { + match self { + MARKDOWN_BLOCK_LIST + | MARKDOWN_HASH_LIST + | MARKDOWN_BULLET_LIST + | MARKDOWN_ORDER_LIST + | MARKDOWN_PARAGRAPH_ITEM_LIST => true, + _ => false, + } + } + pub fn from_keyword(ident: &str) -> Option { + let kw = match ident { + "false" => FALSE_KW, + _ => return None, + }; + Some(kw) + } + pub const fn to_string(&self) -> Option<&'static str> { + let tok = match self { + L_ANGLE => "<", + R_ANGLE => ">", + L_PAREN => "(", + R_PAREN => ")", + L_BRACK => "[", + R_BRACK => "]", + SLASH => "/", + EQ => "=", + BANG => "!", + MINUS => "-", + STAR => "*", + BACKTICK => "`", + TILDE => "~", + WHITESPACE3 => " ", + UNDERSCORE => "_", + HASH => "#", + FALSE_KW => "false", + _ => return None, + }; + Some(tok) + } +} +#[doc = r" Utility macro for creating a SyntaxKind through simple macro syntax"] +#[macro_export] +macro_rules ! T { [<] => { $ crate :: MarkdownSyntaxKind :: L_ANGLE } ; [>] => { $ crate :: MarkdownSyntaxKind :: R_ANGLE } ; ['('] => { $ crate :: MarkdownSyntaxKind :: L_PAREN } ; [')'] => { $ crate :: MarkdownSyntaxKind :: R_PAREN } ; ['['] => { $ crate :: MarkdownSyntaxKind :: L_BRACK } ; [']'] => { $ crate :: MarkdownSyntaxKind :: R_BRACK } ; [/] => { $ crate :: MarkdownSyntaxKind :: SLASH } ; [=] => { $ crate :: MarkdownSyntaxKind :: EQ } ; [!] => { $ crate :: MarkdownSyntaxKind :: BANG } ; [-] => { $ crate :: MarkdownSyntaxKind :: MINUS } ; [*] => { $ crate :: MarkdownSyntaxKind :: STAR } ; ['`'] => { $ crate :: MarkdownSyntaxKind :: BACKTICK } ; [~] => { $ crate :: MarkdownSyntaxKind :: TILDE } ; [ ] => { $ crate :: MarkdownSyntaxKind :: WHITESPACE3 } ; [_] => { $ crate :: MarkdownSyntaxKind :: UNDERSCORE } ; [#] => { $ crate :: MarkdownSyntaxKind :: HASH } ; [false] => { $ crate :: MarkdownSyntaxKind :: FALSE_KW } ; [ident] => { $ crate :: MarkdownSyntaxKind :: IDENT } ; [EOF] => { $ crate :: MarkdownSyntaxKind :: EOF } ; [UNICODE_BOM] => { $ crate :: MarkdownSyntaxKind :: UNICODE_BOM } ; [#] => { $ crate :: MarkdownSyntaxKind :: HASH } ; } diff --git a/crates/biome_markdown_syntax/src/generated/macros.rs b/crates/biome_markdown_syntax/src/generated/macros.rs new file mode 100644 index 000000000000..e4e449f341af --- /dev/null +++ b/crates/biome_markdown_syntax/src/generated/macros.rs @@ -0,0 +1,133 @@ +//! Generated file, do not edit by hand, see `xtask/codegen` + +#[doc = r" Reconstruct an AstNode from a SyntaxNode"] +#[doc = r""] +#[doc = r" This macros performs a match over the [kind](biome_rowan::SyntaxNode::kind)"] +#[doc = r" of the provided [biome_rowan::SyntaxNode] and constructs the appropriate"] +#[doc = r" AstNode type for it, then execute the provided expression over it."] +#[doc = r""] +#[doc = r" # Examples"] +#[doc = r""] +#[doc = r" ```ignore"] +#[doc = r" map_syntax_node!(syntax_node, node => node.format())"] +#[doc = r" ```"] +#[macro_export] +macro_rules! map_syntax_node { + ($ node : expr , $ pattern : pat => $ body : expr) => { + match $node { + node => match $crate::MarkdownSyntaxNode::kind(&node) { + $crate::MarkdownSyntaxKind::MARKDOWN_BREAK_BLOCK => { + let $pattern = unsafe { $crate::MarkdownBreakBlock::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_BULLET_LIST_ITEM => { + let $pattern = unsafe { $crate::MarkdownBulletListItem::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_DOCUMENT => { + let $pattern = unsafe { $crate::MarkdownDocument::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_FENCED_CODE_BLOCK => { + let $pattern = unsafe { $crate::MarkdownFencedCodeBlock::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_HARD_LINE => { + let $pattern = unsafe { $crate::MarkdownHardLine::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_HASH => { + let $pattern = unsafe { $crate::MarkdownHash::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_HEADER => { + let $pattern = unsafe { $crate::MarkdownHeader::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_HTML_BLOCK => { + let $pattern = unsafe { $crate::MarkdownHtmlBlock::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_INDENT => { + let $pattern = unsafe { $crate::MarkdownIndent::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_INDENT_CODE_BLOCK => { + let $pattern = unsafe { $crate::MarkdownIndentCodeBlock::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_INLINE_CODE => { + let $pattern = unsafe { $crate::MarkdownInlineCode::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_INLINE_EMPHASIS => { + let $pattern = unsafe { $crate::MarkdownInlineEmphasis::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_INLINE_IMAGE => { + let $pattern = unsafe { $crate::MarkdownInlineImage::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_INLINE_LINK => { + let $pattern = unsafe { $crate::MarkdownInlineLink::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_LINK_BLOCK => { + let $pattern = unsafe { $crate::MarkdownLinkBlock::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_ORDER_LIST_ITEM => { + let $pattern = unsafe { $crate::MarkdownOrderListItem::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_PARAGRAPH => { + let $pattern = unsafe { $crate::MarkdownParagraph::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_QUOTE => { + let $pattern = unsafe { $crate::MarkdownQuote::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_SETEXT_HEADER => { + let $pattern = unsafe { $crate::MarkdownSetextHeader::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_SOFT_BREAK => { + let $pattern = unsafe { $crate::MarkdownSoftBreak::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_TEXTUAL => { + let $pattern = unsafe { $crate::MarkdownTextual::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_BOGUS => { + let $pattern = unsafe { $crate::MarkdownBogus::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_BLOCK_LIST => { + let $pattern = unsafe { $crate::MarkdownBlockList::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_BULLET_LIST => { + let $pattern = unsafe { $crate::MarkdownBulletList::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_HASH_LIST => { + let $pattern = unsafe { $crate::MarkdownHashList::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_ORDER_LIST => { + let $pattern = unsafe { $crate::MarkdownOrderList::new_unchecked(node) }; + $body + } + $crate::MarkdownSyntaxKind::MARKDOWN_PARAGRAPH_ITEM_LIST => { + let $pattern = + unsafe { $crate::MarkdownParagraphItemList::new_unchecked(node) }; + $body + } + _ => unreachable!(), + }, + } + }; +} +pub(crate) use map_syntax_node; diff --git a/crates/biome_markdown_syntax/src/generated/nodes.rs b/crates/biome_markdown_syntax/src/generated/nodes.rs new file mode 100644 index 000000000000..bd2e9199227a --- /dev/null +++ b/crates/biome_markdown_syntax/src/generated/nodes.rs @@ -0,0 +1,2934 @@ +//! Generated file, do not edit by hand, see `xtask/codegen` + +#![allow(clippy::enum_variant_names)] +#![allow(clippy::match_like_matches_macro)] +use crate::{ + macros::map_syntax_node, + MarkdownLanguage as Language, MarkdownSyntaxElement as SyntaxElement, + MarkdownSyntaxElementChildren as SyntaxElementChildren, + MarkdownSyntaxKind::{self as SyntaxKind, *}, + MarkdownSyntaxList as SyntaxList, MarkdownSyntaxNode as SyntaxNode, + MarkdownSyntaxToken as SyntaxToken, +}; +use biome_rowan::{support, AstNode, RawSyntaxKind, SyntaxKindSet, SyntaxResult}; +#[allow(unused)] +use biome_rowan::{ + AstNodeList, AstNodeListIterator, AstNodeSlotMap, AstSeparatedList, + AstSeparatedListNodesIterator, +}; +use serde::ser::SerializeSeq; +use serde::{Serialize, Serializer}; +use std::fmt::{Debug, Formatter}; +#[doc = r" Sentinel value indicating a missing element in a dynamic node, where"] +#[doc = r" the slots are not statically known."] +#[allow(dead_code)] +pub(crate) const SLOT_MAP_EMPTY_VALUE: u8 = u8::MAX; +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownBreakBlock { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownBreakBlock { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownBreakBlockFields { + MarkdownBreakBlockFields { + value_token: self.value_token(), + } + } + pub fn value_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownBreakBlock { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownBreakBlockFields { + pub value_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownBulletListItem { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownBulletListItem { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownBulletListItemFields { + MarkdownBulletListItemFields { + markdown_bullet_list: self.markdown_bullet_list(), + } + } + pub fn markdown_bullet_list(&self) -> MarkdownBulletList { + support::list(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownBulletListItem { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownBulletListItemFields { + pub markdown_bullet_list: MarkdownBulletList, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownDocument { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownDocument { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownDocumentFields { + MarkdownDocumentFields { + bom_token: self.bom_token(), + value: self.value(), + eof_token: self.eof_token(), + } + } + pub fn bom_token(&self) -> Option { + support::token(&self.syntax, 0usize) + } + pub fn value(&self) -> MarkdownBlockList { + support::list(&self.syntax, 1usize) + } + pub fn eof_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 2usize) + } +} +impl Serialize for MarkdownDocument { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownDocumentFields { + pub bom_token: Option, + pub value: MarkdownBlockList, + pub eof_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownFencedCodeBlock { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownFencedCodeBlock { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownFencedCodeBlockFields { + MarkdownFencedCodeBlockFields { + markdown_textual: self.markdown_textual(), + } + } + pub fn markdown_textual(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownFencedCodeBlock { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownFencedCodeBlockFields { + pub markdown_textual: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownHardLine { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownHardLine { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownHardLineFields { + MarkdownHardLineFields { + value_token: self.value_token(), + } + } + pub fn value_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownHardLine { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownHardLineFields { + pub value_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownHash { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownHash { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownHashFields { + MarkdownHashFields { + hash_token: self.hash_token(), + } + } + pub fn hash_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownHash { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownHashFields { + pub hash_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownHeader { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownHeader { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownHeaderFields { + MarkdownHeaderFields { + before: self.before(), + markdown_paragraph: self.markdown_paragraph(), + after: self.after(), + } + } + pub fn before(&self) -> MarkdownHashList { + support::list(&self.syntax, 0usize) + } + pub fn markdown_paragraph(&self) -> Option { + support::node(&self.syntax, 1usize) + } + pub fn after(&self) -> MarkdownHashList { + support::list(&self.syntax, 2usize) + } +} +impl Serialize for MarkdownHeader { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownHeaderFields { + pub before: MarkdownHashList, + pub markdown_paragraph: Option, + pub after: MarkdownHashList, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownHtmlBlock { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownHtmlBlock { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownHtmlBlockFields { + MarkdownHtmlBlockFields { + markdown_textual: self.markdown_textual(), + } + } + pub fn markdown_textual(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownHtmlBlock { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownHtmlBlockFields { + pub markdown_textual: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownIndent { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownIndent { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownIndentFields { + MarkdownIndentFields { + value_token: self.value_token(), + } + } + pub fn value_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownIndent { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownIndentFields { + pub value_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownIndentCodeBlock { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownIndentCodeBlock { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownIndentCodeBlockFields { + MarkdownIndentCodeBlockFields { + markdown_textual: self.markdown_textual(), + } + } + pub fn markdown_textual(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownIndentCodeBlock { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownIndentCodeBlockFields { + pub markdown_textual: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownInlineCode { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownInlineCode { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownInlineCodeFields { + MarkdownInlineCodeFields { + markdown_textual: self.markdown_textual(), + } + } + pub fn markdown_textual(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownInlineCode { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownInlineCodeFields { + pub markdown_textual: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownInlineEmphasis { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownInlineEmphasis { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownInlineEmphasisFields { + MarkdownInlineEmphasisFields { + markdown_textual: self.markdown_textual(), + } + } + pub fn markdown_textual(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownInlineEmphasis { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownInlineEmphasisFields { + pub markdown_textual: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownInlineImage { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownInlineImage { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownInlineImageFields { + MarkdownInlineImageFields { + alt: self.alt(), + src: self.src(), + title: self.title(), + } + } + pub fn alt(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } + pub fn src(&self) -> SyntaxResult { + support::required_node(&self.syntax, 1usize) + } + pub fn title(&self) -> Option { + support::node(&self.syntax, 2usize) + } +} +impl Serialize for MarkdownInlineImage { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownInlineImageFields { + pub alt: SyntaxResult, + pub src: SyntaxResult, + pub title: Option, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownInlineLink { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownInlineLink { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownInlineLinkFields { + MarkdownInlineLinkFields { + label: self.label(), + url: self.url(), + title: self.title(), + } + } + pub fn label(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } + pub fn url(&self) -> SyntaxResult { + support::required_node(&self.syntax, 1usize) + } + pub fn title(&self) -> Option { + support::node(&self.syntax, 2usize) + } +} +impl Serialize for MarkdownInlineLink { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownInlineLinkFields { + pub label: SyntaxResult, + pub url: SyntaxResult, + pub title: Option, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownLinkBlock { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownLinkBlock { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownLinkBlockFields { + MarkdownLinkBlockFields { + label: self.label(), + url: self.url(), + title: self.title(), + } + } + pub fn label(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } + pub fn url(&self) -> SyntaxResult { + support::required_node(&self.syntax, 1usize) + } + pub fn title(&self) -> Option { + support::node(&self.syntax, 2usize) + } +} +impl Serialize for MarkdownLinkBlock { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownLinkBlockFields { + pub label: SyntaxResult, + pub url: SyntaxResult, + pub title: Option, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownOrderListItem { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownOrderListItem { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownOrderListItemFields { + MarkdownOrderListItemFields { + markdown_bullet_list: self.markdown_bullet_list(), + } + } + pub fn markdown_bullet_list(&self) -> MarkdownBulletList { + support::list(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownOrderListItem { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownOrderListItemFields { + pub markdown_bullet_list: MarkdownBulletList, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownParagraph { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownParagraph { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownParagraphFields { + MarkdownParagraphFields { + markdown_paragraph_item_list: self.markdown_paragraph_item_list(), + } + } + pub fn markdown_paragraph_item_list(&self) -> MarkdownParagraphItemList { + support::list(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownParagraph { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownParagraphFields { + pub markdown_paragraph_item_list: MarkdownParagraphItemList, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownQuote { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownQuote { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownQuoteFields { + MarkdownQuoteFields { + any_markdown_block: self.any_markdown_block(), + } + } + pub fn any_markdown_block(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownQuote { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownQuoteFields { + pub any_markdown_block: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownSetextHeader { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownSetextHeader { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownSetextHeaderFields { + MarkdownSetextHeaderFields { + markdown_paragraph: self.markdown_paragraph(), + } + } + pub fn markdown_paragraph(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownSetextHeader { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownSetextHeaderFields { + pub markdown_paragraph: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownSoftBreak { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownSoftBreak { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownSoftBreakFields { + MarkdownSoftBreakFields { + value_token: self.value_token(), + } + } + pub fn value_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownSoftBreak { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownSoftBreakFields { + pub value_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MarkdownTextual { + pub(crate) syntax: SyntaxNode, +} +impl MarkdownTextual { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> MarkdownTextualFields { + MarkdownTextualFields { + value_token: self.value_token(), + } + } + pub fn value_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } +} +impl Serialize for MarkdownTextual { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[derive(Serialize)] +pub struct MarkdownTextualFields { + pub value_token: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyCodeBlock { + MarkdownFencedCodeBlock(MarkdownFencedCodeBlock), + MarkdownIndentCodeBlock(MarkdownIndentCodeBlock), +} +impl AnyCodeBlock { + pub fn as_markdown_fenced_code_block(&self) -> Option<&MarkdownFencedCodeBlock> { + match &self { + AnyCodeBlock::MarkdownFencedCodeBlock(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_indent_code_block(&self) -> Option<&MarkdownIndentCodeBlock> { + match &self { + AnyCodeBlock::MarkdownIndentCodeBlock(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyContainerBlock { + MarkdownBulletListItem(MarkdownBulletListItem), + MarkdownOrderListItem(MarkdownOrderListItem), + MarkdownQuote(MarkdownQuote), +} +impl AnyContainerBlock { + pub fn as_markdown_bullet_list_item(&self) -> Option<&MarkdownBulletListItem> { + match &self { + AnyContainerBlock::MarkdownBulletListItem(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_order_list_item(&self) -> Option<&MarkdownOrderListItem> { + match &self { + AnyContainerBlock::MarkdownOrderListItem(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_quote(&self) -> Option<&MarkdownQuote> { + match &self { + AnyContainerBlock::MarkdownQuote(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyLeafBlock { + AnyCodeBlock(AnyCodeBlock), + MarkdownBreakBlock(MarkdownBreakBlock), + MarkdownHeader(MarkdownHeader), + MarkdownHtmlBlock(MarkdownHtmlBlock), + MarkdownLinkBlock(MarkdownLinkBlock), + MarkdownParagraph(MarkdownParagraph), + MarkdownSetextHeader(MarkdownSetextHeader), +} +impl AnyLeafBlock { + pub fn as_any_code_block(&self) -> Option<&AnyCodeBlock> { + match &self { + AnyLeafBlock::AnyCodeBlock(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_break_block(&self) -> Option<&MarkdownBreakBlock> { + match &self { + AnyLeafBlock::MarkdownBreakBlock(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_header(&self) -> Option<&MarkdownHeader> { + match &self { + AnyLeafBlock::MarkdownHeader(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_html_block(&self) -> Option<&MarkdownHtmlBlock> { + match &self { + AnyLeafBlock::MarkdownHtmlBlock(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_link_block(&self) -> Option<&MarkdownLinkBlock> { + match &self { + AnyLeafBlock::MarkdownLinkBlock(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_paragraph(&self) -> Option<&MarkdownParagraph> { + match &self { + AnyLeafBlock::MarkdownParagraph(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_setext_header(&self) -> Option<&MarkdownSetextHeader> { + match &self { + AnyLeafBlock::MarkdownSetextHeader(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyMarkdownBlock { + AnyContainerBlock(AnyContainerBlock), + AnyLeafBlock(AnyLeafBlock), +} +impl AnyMarkdownBlock { + pub fn as_any_container_block(&self) -> Option<&AnyContainerBlock> { + match &self { + AnyMarkdownBlock::AnyContainerBlock(item) => Some(item), + _ => None, + } + } + pub fn as_any_leaf_block(&self) -> Option<&AnyLeafBlock> { + match &self { + AnyMarkdownBlock::AnyLeafBlock(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub enum AnyMarkdownInline { + MarkdownHardLine(MarkdownHardLine), + MarkdownHtmlBlock(MarkdownHtmlBlock), + MarkdownInlineCode(MarkdownInlineCode), + MarkdownInlineEmphasis(MarkdownInlineEmphasis), + MarkdownInlineImage(MarkdownInlineImage), + MarkdownInlineLink(MarkdownInlineLink), + MarkdownSoftBreak(MarkdownSoftBreak), + MarkdownTextual(MarkdownTextual), +} +impl AnyMarkdownInline { + pub fn as_markdown_hard_line(&self) -> Option<&MarkdownHardLine> { + match &self { + AnyMarkdownInline::MarkdownHardLine(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_html_block(&self) -> Option<&MarkdownHtmlBlock> { + match &self { + AnyMarkdownInline::MarkdownHtmlBlock(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_inline_code(&self) -> Option<&MarkdownInlineCode> { + match &self { + AnyMarkdownInline::MarkdownInlineCode(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_inline_emphasis(&self) -> Option<&MarkdownInlineEmphasis> { + match &self { + AnyMarkdownInline::MarkdownInlineEmphasis(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_inline_image(&self) -> Option<&MarkdownInlineImage> { + match &self { + AnyMarkdownInline::MarkdownInlineImage(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_inline_link(&self) -> Option<&MarkdownInlineLink> { + match &self { + AnyMarkdownInline::MarkdownInlineLink(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_soft_break(&self) -> Option<&MarkdownSoftBreak> { + match &self { + AnyMarkdownInline::MarkdownSoftBreak(item) => Some(item), + _ => None, + } + } + pub fn as_markdown_textual(&self) -> Option<&MarkdownTextual> { + match &self { + AnyMarkdownInline::MarkdownTextual(item) => Some(item), + _ => None, + } + } +} +impl AstNode for MarkdownBreakBlock { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_BREAK_BLOCK as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_BREAK_BLOCK + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownBreakBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownBreakBlock") + .field( + "value_token", + &support::DebugSyntaxResult(self.value_token()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownBreakBlock) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownBreakBlock) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownBulletListItem { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_BULLET_LIST_ITEM as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_BULLET_LIST_ITEM + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownBulletListItem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownBulletListItem") + .field("markdown_bullet_list", &self.markdown_bullet_list()) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownBulletListItem) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownBulletListItem) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownDocument { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_DOCUMENT as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_DOCUMENT + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownDocument { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownDocument") + .field( + "bom_token", + &support::DebugOptionalElement(self.bom_token()), + ) + .field("value", &self.value()) + .field("eof_token", &support::DebugSyntaxResult(self.eof_token())) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownDocument) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownDocument) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownFencedCodeBlock { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_FENCED_CODE_BLOCK as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_FENCED_CODE_BLOCK + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownFencedCodeBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownFencedCodeBlock") + .field( + "markdown_textual", + &support::DebugSyntaxResult(self.markdown_textual()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownFencedCodeBlock) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownFencedCodeBlock) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownHardLine { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_HARD_LINE as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_HARD_LINE + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownHardLine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownHardLine") + .field( + "value_token", + &support::DebugSyntaxResult(self.value_token()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownHardLine) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownHardLine) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownHash { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_HASH as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_HASH + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownHash") + .field("hash_token", &support::DebugSyntaxResult(self.hash_token())) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownHash) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownHash) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownHeader { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_HEADER as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_HEADER + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownHeader { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownHeader") + .field("before", &self.before()) + .field( + "markdown_paragraph", + &support::DebugOptionalElement(self.markdown_paragraph()), + ) + .field("after", &self.after()) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownHeader) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownHeader) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownHtmlBlock { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_HTML_BLOCK as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_HTML_BLOCK + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownHtmlBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownHtmlBlock") + .field( + "markdown_textual", + &support::DebugSyntaxResult(self.markdown_textual()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownHtmlBlock) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownHtmlBlock) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownIndent { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_INDENT as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_INDENT + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownIndent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownIndent") + .field( + "value_token", + &support::DebugSyntaxResult(self.value_token()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownIndent) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownIndent) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownIndentCodeBlock { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_INDENT_CODE_BLOCK as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_INDENT_CODE_BLOCK + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownIndentCodeBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownIndentCodeBlock") + .field( + "markdown_textual", + &support::DebugSyntaxResult(self.markdown_textual()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownIndentCodeBlock) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownIndentCodeBlock) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownInlineCode { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_INLINE_CODE as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_INLINE_CODE + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownInlineCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownInlineCode") + .field( + "markdown_textual", + &support::DebugSyntaxResult(self.markdown_textual()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownInlineCode) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownInlineCode) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownInlineEmphasis { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_INLINE_EMPHASIS as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_INLINE_EMPHASIS + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownInlineEmphasis { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownInlineEmphasis") + .field( + "markdown_textual", + &support::DebugSyntaxResult(self.markdown_textual()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownInlineEmphasis) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownInlineEmphasis) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownInlineImage { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_INLINE_IMAGE as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_INLINE_IMAGE + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownInlineImage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownInlineImage") + .field("alt", &support::DebugSyntaxResult(self.alt())) + .field("src", &support::DebugSyntaxResult(self.src())) + .field("title", &support::DebugOptionalElement(self.title())) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownInlineImage) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownInlineImage) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownInlineLink { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_INLINE_LINK as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_INLINE_LINK + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownInlineLink { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownInlineLink") + .field("label", &support::DebugSyntaxResult(self.label())) + .field("url", &support::DebugSyntaxResult(self.url())) + .field("title", &support::DebugOptionalElement(self.title())) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownInlineLink) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownInlineLink) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownLinkBlock { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_LINK_BLOCK as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_LINK_BLOCK + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownLinkBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownLinkBlock") + .field("label", &support::DebugSyntaxResult(self.label())) + .field("url", &support::DebugSyntaxResult(self.url())) + .field("title", &support::DebugOptionalElement(self.title())) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownLinkBlock) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownLinkBlock) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownOrderListItem { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_ORDER_LIST_ITEM as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_ORDER_LIST_ITEM + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownOrderListItem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownOrderListItem") + .field("markdown_bullet_list", &self.markdown_bullet_list()) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownOrderListItem) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownOrderListItem) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownParagraph { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_PARAGRAPH as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_PARAGRAPH + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownParagraph { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownParagraph") + .field( + "markdown_paragraph_item_list", + &self.markdown_paragraph_item_list(), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownParagraph) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownParagraph) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownQuote { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_QUOTE as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_QUOTE + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownQuote { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownQuote") + .field( + "any_markdown_block", + &support::DebugSyntaxResult(self.any_markdown_block()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownQuote) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownQuote) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownSetextHeader { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_SETEXT_HEADER as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_SETEXT_HEADER + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownSetextHeader { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownSetextHeader") + .field( + "markdown_paragraph", + &support::DebugSyntaxResult(self.markdown_paragraph()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownSetextHeader) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownSetextHeader) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownSoftBreak { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_SOFT_BREAK as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_SOFT_BREAK + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownSoftBreak { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownSoftBreak") + .field( + "value_token", + &support::DebugSyntaxResult(self.value_token()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownSoftBreak) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownSoftBreak) -> SyntaxElement { + n.syntax.into() + } +} +impl AstNode for MarkdownTextual { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_TEXTUAL as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_TEXTUAL + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownTextual { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownTextual") + .field( + "value_token", + &support::DebugSyntaxResult(self.value_token()), + ) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownTextual) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownTextual) -> SyntaxElement { + n.syntax.into() + } +} +impl From for AnyCodeBlock { + fn from(node: MarkdownFencedCodeBlock) -> AnyCodeBlock { + AnyCodeBlock::MarkdownFencedCodeBlock(node) + } +} +impl From for AnyCodeBlock { + fn from(node: MarkdownIndentCodeBlock) -> AnyCodeBlock { + AnyCodeBlock::MarkdownIndentCodeBlock(node) + } +} +impl AstNode for AnyCodeBlock { + type Language = Language; + const KIND_SET: SyntaxKindSet = + MarkdownFencedCodeBlock::KIND_SET.union(MarkdownIndentCodeBlock::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + MARKDOWN_FENCED_CODE_BLOCK | MARKDOWN_INDENT_CODE_BLOCK + ) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + MARKDOWN_FENCED_CODE_BLOCK => { + AnyCodeBlock::MarkdownFencedCodeBlock(MarkdownFencedCodeBlock { syntax }) + } + MARKDOWN_INDENT_CODE_BLOCK => { + AnyCodeBlock::MarkdownIndentCodeBlock(MarkdownIndentCodeBlock { syntax }) + } + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + AnyCodeBlock::MarkdownFencedCodeBlock(it) => &it.syntax, + AnyCodeBlock::MarkdownIndentCodeBlock(it) => &it.syntax, + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + AnyCodeBlock::MarkdownFencedCodeBlock(it) => it.syntax, + AnyCodeBlock::MarkdownIndentCodeBlock(it) => it.syntax, + } + } +} +impl std::fmt::Debug for AnyCodeBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AnyCodeBlock::MarkdownFencedCodeBlock(it) => std::fmt::Debug::fmt(it, f), + AnyCodeBlock::MarkdownIndentCodeBlock(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyCodeBlock) -> SyntaxNode { + match n { + AnyCodeBlock::MarkdownFencedCodeBlock(it) => it.into(), + AnyCodeBlock::MarkdownIndentCodeBlock(it) => it.into(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyCodeBlock) -> SyntaxElement { + let node: SyntaxNode = n.into(); + node.into() + } +} +impl From for AnyContainerBlock { + fn from(node: MarkdownBulletListItem) -> AnyContainerBlock { + AnyContainerBlock::MarkdownBulletListItem(node) + } +} +impl From for AnyContainerBlock { + fn from(node: MarkdownOrderListItem) -> AnyContainerBlock { + AnyContainerBlock::MarkdownOrderListItem(node) + } +} +impl From for AnyContainerBlock { + fn from(node: MarkdownQuote) -> AnyContainerBlock { + AnyContainerBlock::MarkdownQuote(node) + } +} +impl AstNode for AnyContainerBlock { + type Language = Language; + const KIND_SET: SyntaxKindSet = MarkdownBulletListItem::KIND_SET + .union(MarkdownOrderListItem::KIND_SET) + .union(MarkdownQuote::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + MARKDOWN_BULLET_LIST_ITEM | MARKDOWN_ORDER_LIST_ITEM | MARKDOWN_QUOTE + ) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + MARKDOWN_BULLET_LIST_ITEM => { + AnyContainerBlock::MarkdownBulletListItem(MarkdownBulletListItem { syntax }) + } + MARKDOWN_ORDER_LIST_ITEM => { + AnyContainerBlock::MarkdownOrderListItem(MarkdownOrderListItem { syntax }) + } + MARKDOWN_QUOTE => AnyContainerBlock::MarkdownQuote(MarkdownQuote { syntax }), + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + AnyContainerBlock::MarkdownBulletListItem(it) => &it.syntax, + AnyContainerBlock::MarkdownOrderListItem(it) => &it.syntax, + AnyContainerBlock::MarkdownQuote(it) => &it.syntax, + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + AnyContainerBlock::MarkdownBulletListItem(it) => it.syntax, + AnyContainerBlock::MarkdownOrderListItem(it) => it.syntax, + AnyContainerBlock::MarkdownQuote(it) => it.syntax, + } + } +} +impl std::fmt::Debug for AnyContainerBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AnyContainerBlock::MarkdownBulletListItem(it) => std::fmt::Debug::fmt(it, f), + AnyContainerBlock::MarkdownOrderListItem(it) => std::fmt::Debug::fmt(it, f), + AnyContainerBlock::MarkdownQuote(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyContainerBlock) -> SyntaxNode { + match n { + AnyContainerBlock::MarkdownBulletListItem(it) => it.into(), + AnyContainerBlock::MarkdownOrderListItem(it) => it.into(), + AnyContainerBlock::MarkdownQuote(it) => it.into(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyContainerBlock) -> SyntaxElement { + let node: SyntaxNode = n.into(); + node.into() + } +} +impl From for AnyLeafBlock { + fn from(node: MarkdownBreakBlock) -> AnyLeafBlock { + AnyLeafBlock::MarkdownBreakBlock(node) + } +} +impl From for AnyLeafBlock { + fn from(node: MarkdownHeader) -> AnyLeafBlock { + AnyLeafBlock::MarkdownHeader(node) + } +} +impl From for AnyLeafBlock { + fn from(node: MarkdownHtmlBlock) -> AnyLeafBlock { + AnyLeafBlock::MarkdownHtmlBlock(node) + } +} +impl From for AnyLeafBlock { + fn from(node: MarkdownLinkBlock) -> AnyLeafBlock { + AnyLeafBlock::MarkdownLinkBlock(node) + } +} +impl From for AnyLeafBlock { + fn from(node: MarkdownParagraph) -> AnyLeafBlock { + AnyLeafBlock::MarkdownParagraph(node) + } +} +impl From for AnyLeafBlock { + fn from(node: MarkdownSetextHeader) -> AnyLeafBlock { + AnyLeafBlock::MarkdownSetextHeader(node) + } +} +impl AstNode for AnyLeafBlock { + type Language = Language; + const KIND_SET: SyntaxKindSet = AnyCodeBlock::KIND_SET + .union(MarkdownBreakBlock::KIND_SET) + .union(MarkdownHeader::KIND_SET) + .union(MarkdownHtmlBlock::KIND_SET) + .union(MarkdownLinkBlock::KIND_SET) + .union(MarkdownParagraph::KIND_SET) + .union(MarkdownSetextHeader::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + MARKDOWN_BREAK_BLOCK + | MARKDOWN_HEADER + | MARKDOWN_HTML_BLOCK + | MARKDOWN_LINK_BLOCK + | MARKDOWN_PARAGRAPH + | MARKDOWN_SETEXT_HEADER => true, + k if AnyCodeBlock::can_cast(k) => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + MARKDOWN_BREAK_BLOCK => AnyLeafBlock::MarkdownBreakBlock(MarkdownBreakBlock { syntax }), + MARKDOWN_HEADER => AnyLeafBlock::MarkdownHeader(MarkdownHeader { syntax }), + MARKDOWN_HTML_BLOCK => AnyLeafBlock::MarkdownHtmlBlock(MarkdownHtmlBlock { syntax }), + MARKDOWN_LINK_BLOCK => AnyLeafBlock::MarkdownLinkBlock(MarkdownLinkBlock { syntax }), + MARKDOWN_PARAGRAPH => AnyLeafBlock::MarkdownParagraph(MarkdownParagraph { syntax }), + MARKDOWN_SETEXT_HEADER => { + AnyLeafBlock::MarkdownSetextHeader(MarkdownSetextHeader { syntax }) + } + _ => { + if let Some(any_code_block) = AnyCodeBlock::cast(syntax) { + return Some(AnyLeafBlock::AnyCodeBlock(any_code_block)); + } + return None; + } + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + AnyLeafBlock::MarkdownBreakBlock(it) => &it.syntax, + AnyLeafBlock::MarkdownHeader(it) => &it.syntax, + AnyLeafBlock::MarkdownHtmlBlock(it) => &it.syntax, + AnyLeafBlock::MarkdownLinkBlock(it) => &it.syntax, + AnyLeafBlock::MarkdownParagraph(it) => &it.syntax, + AnyLeafBlock::MarkdownSetextHeader(it) => &it.syntax, + AnyLeafBlock::AnyCodeBlock(it) => it.syntax(), + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + AnyLeafBlock::MarkdownBreakBlock(it) => it.syntax, + AnyLeafBlock::MarkdownHeader(it) => it.syntax, + AnyLeafBlock::MarkdownHtmlBlock(it) => it.syntax, + AnyLeafBlock::MarkdownLinkBlock(it) => it.syntax, + AnyLeafBlock::MarkdownParagraph(it) => it.syntax, + AnyLeafBlock::MarkdownSetextHeader(it) => it.syntax, + AnyLeafBlock::AnyCodeBlock(it) => it.into_syntax(), + } + } +} +impl std::fmt::Debug for AnyLeafBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AnyLeafBlock::AnyCodeBlock(it) => std::fmt::Debug::fmt(it, f), + AnyLeafBlock::MarkdownBreakBlock(it) => std::fmt::Debug::fmt(it, f), + AnyLeafBlock::MarkdownHeader(it) => std::fmt::Debug::fmt(it, f), + AnyLeafBlock::MarkdownHtmlBlock(it) => std::fmt::Debug::fmt(it, f), + AnyLeafBlock::MarkdownLinkBlock(it) => std::fmt::Debug::fmt(it, f), + AnyLeafBlock::MarkdownParagraph(it) => std::fmt::Debug::fmt(it, f), + AnyLeafBlock::MarkdownSetextHeader(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyLeafBlock) -> SyntaxNode { + match n { + AnyLeafBlock::AnyCodeBlock(it) => it.into(), + AnyLeafBlock::MarkdownBreakBlock(it) => it.into(), + AnyLeafBlock::MarkdownHeader(it) => it.into(), + AnyLeafBlock::MarkdownHtmlBlock(it) => it.into(), + AnyLeafBlock::MarkdownLinkBlock(it) => it.into(), + AnyLeafBlock::MarkdownParagraph(it) => it.into(), + AnyLeafBlock::MarkdownSetextHeader(it) => it.into(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyLeafBlock) -> SyntaxElement { + let node: SyntaxNode = n.into(); + node.into() + } +} +impl AstNode for AnyMarkdownBlock { + type Language = Language; + const KIND_SET: SyntaxKindSet = + AnyContainerBlock::KIND_SET.union(AnyLeafBlock::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + k if AnyContainerBlock::can_cast(k) => true, + k if AnyLeafBlock::can_cast(k) => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + let syntax = match AnyContainerBlock::try_cast(syntax) { + Ok(any_container_block) => { + return Some(AnyMarkdownBlock::AnyContainerBlock(any_container_block)); + } + Err(syntax) => syntax, + }; + if let Some(any_leaf_block) = AnyLeafBlock::cast(syntax) { + return Some(AnyMarkdownBlock::AnyLeafBlock(any_leaf_block)); + } + None + } + fn syntax(&self) -> &SyntaxNode { + match self { + AnyMarkdownBlock::AnyContainerBlock(it) => it.syntax(), + AnyMarkdownBlock::AnyLeafBlock(it) => it.syntax(), + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + AnyMarkdownBlock::AnyContainerBlock(it) => it.into_syntax(), + AnyMarkdownBlock::AnyLeafBlock(it) => it.into_syntax(), + } + } +} +impl std::fmt::Debug for AnyMarkdownBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AnyMarkdownBlock::AnyContainerBlock(it) => std::fmt::Debug::fmt(it, f), + AnyMarkdownBlock::AnyLeafBlock(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyMarkdownBlock) -> SyntaxNode { + match n { + AnyMarkdownBlock::AnyContainerBlock(it) => it.into(), + AnyMarkdownBlock::AnyLeafBlock(it) => it.into(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyMarkdownBlock) -> SyntaxElement { + let node: SyntaxNode = n.into(); + node.into() + } +} +impl From for AnyMarkdownInline { + fn from(node: MarkdownHardLine) -> AnyMarkdownInline { + AnyMarkdownInline::MarkdownHardLine(node) + } +} +impl From for AnyMarkdownInline { + fn from(node: MarkdownHtmlBlock) -> AnyMarkdownInline { + AnyMarkdownInline::MarkdownHtmlBlock(node) + } +} +impl From for AnyMarkdownInline { + fn from(node: MarkdownInlineCode) -> AnyMarkdownInline { + AnyMarkdownInline::MarkdownInlineCode(node) + } +} +impl From for AnyMarkdownInline { + fn from(node: MarkdownInlineEmphasis) -> AnyMarkdownInline { + AnyMarkdownInline::MarkdownInlineEmphasis(node) + } +} +impl From for AnyMarkdownInline { + fn from(node: MarkdownInlineImage) -> AnyMarkdownInline { + AnyMarkdownInline::MarkdownInlineImage(node) + } +} +impl From for AnyMarkdownInline { + fn from(node: MarkdownInlineLink) -> AnyMarkdownInline { + AnyMarkdownInline::MarkdownInlineLink(node) + } +} +impl From for AnyMarkdownInline { + fn from(node: MarkdownSoftBreak) -> AnyMarkdownInline { + AnyMarkdownInline::MarkdownSoftBreak(node) + } +} +impl From for AnyMarkdownInline { + fn from(node: MarkdownTextual) -> AnyMarkdownInline { + AnyMarkdownInline::MarkdownTextual(node) + } +} +impl AstNode for AnyMarkdownInline { + type Language = Language; + const KIND_SET: SyntaxKindSet = MarkdownHardLine::KIND_SET + .union(MarkdownHtmlBlock::KIND_SET) + .union(MarkdownInlineCode::KIND_SET) + .union(MarkdownInlineEmphasis::KIND_SET) + .union(MarkdownInlineImage::KIND_SET) + .union(MarkdownInlineLink::KIND_SET) + .union(MarkdownSoftBreak::KIND_SET) + .union(MarkdownTextual::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + MARKDOWN_HARD_LINE + | MARKDOWN_HTML_BLOCK + | MARKDOWN_INLINE_CODE + | MARKDOWN_INLINE_EMPHASIS + | MARKDOWN_INLINE_IMAGE + | MARKDOWN_INLINE_LINK + | MARKDOWN_SOFT_BREAK + | MARKDOWN_TEXTUAL + ) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + MARKDOWN_HARD_LINE => AnyMarkdownInline::MarkdownHardLine(MarkdownHardLine { syntax }), + MARKDOWN_HTML_BLOCK => { + AnyMarkdownInline::MarkdownHtmlBlock(MarkdownHtmlBlock { syntax }) + } + MARKDOWN_INLINE_CODE => { + AnyMarkdownInline::MarkdownInlineCode(MarkdownInlineCode { syntax }) + } + MARKDOWN_INLINE_EMPHASIS => { + AnyMarkdownInline::MarkdownInlineEmphasis(MarkdownInlineEmphasis { syntax }) + } + MARKDOWN_INLINE_IMAGE => { + AnyMarkdownInline::MarkdownInlineImage(MarkdownInlineImage { syntax }) + } + MARKDOWN_INLINE_LINK => { + AnyMarkdownInline::MarkdownInlineLink(MarkdownInlineLink { syntax }) + } + MARKDOWN_SOFT_BREAK => { + AnyMarkdownInline::MarkdownSoftBreak(MarkdownSoftBreak { syntax }) + } + MARKDOWN_TEXTUAL => AnyMarkdownInline::MarkdownTextual(MarkdownTextual { syntax }), + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + AnyMarkdownInline::MarkdownHardLine(it) => &it.syntax, + AnyMarkdownInline::MarkdownHtmlBlock(it) => &it.syntax, + AnyMarkdownInline::MarkdownInlineCode(it) => &it.syntax, + AnyMarkdownInline::MarkdownInlineEmphasis(it) => &it.syntax, + AnyMarkdownInline::MarkdownInlineImage(it) => &it.syntax, + AnyMarkdownInline::MarkdownInlineLink(it) => &it.syntax, + AnyMarkdownInline::MarkdownSoftBreak(it) => &it.syntax, + AnyMarkdownInline::MarkdownTextual(it) => &it.syntax, + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + AnyMarkdownInline::MarkdownHardLine(it) => it.syntax, + AnyMarkdownInline::MarkdownHtmlBlock(it) => it.syntax, + AnyMarkdownInline::MarkdownInlineCode(it) => it.syntax, + AnyMarkdownInline::MarkdownInlineEmphasis(it) => it.syntax, + AnyMarkdownInline::MarkdownInlineImage(it) => it.syntax, + AnyMarkdownInline::MarkdownInlineLink(it) => it.syntax, + AnyMarkdownInline::MarkdownSoftBreak(it) => it.syntax, + AnyMarkdownInline::MarkdownTextual(it) => it.syntax, + } + } +} +impl std::fmt::Debug for AnyMarkdownInline { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AnyMarkdownInline::MarkdownHardLine(it) => std::fmt::Debug::fmt(it, f), + AnyMarkdownInline::MarkdownHtmlBlock(it) => std::fmt::Debug::fmt(it, f), + AnyMarkdownInline::MarkdownInlineCode(it) => std::fmt::Debug::fmt(it, f), + AnyMarkdownInline::MarkdownInlineEmphasis(it) => std::fmt::Debug::fmt(it, f), + AnyMarkdownInline::MarkdownInlineImage(it) => std::fmt::Debug::fmt(it, f), + AnyMarkdownInline::MarkdownInlineLink(it) => std::fmt::Debug::fmt(it, f), + AnyMarkdownInline::MarkdownSoftBreak(it) => std::fmt::Debug::fmt(it, f), + AnyMarkdownInline::MarkdownTextual(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyMarkdownInline) -> SyntaxNode { + match n { + AnyMarkdownInline::MarkdownHardLine(it) => it.into(), + AnyMarkdownInline::MarkdownHtmlBlock(it) => it.into(), + AnyMarkdownInline::MarkdownInlineCode(it) => it.into(), + AnyMarkdownInline::MarkdownInlineEmphasis(it) => it.into(), + AnyMarkdownInline::MarkdownInlineImage(it) => it.into(), + AnyMarkdownInline::MarkdownInlineLink(it) => it.into(), + AnyMarkdownInline::MarkdownSoftBreak(it) => it.into(), + AnyMarkdownInline::MarkdownTextual(it) => it.into(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyMarkdownInline) -> SyntaxElement { + let node: SyntaxNode = n.into(); + node.into() + } +} +impl std::fmt::Display for AnyCodeBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AnyContainerBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AnyLeafBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AnyMarkdownBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AnyMarkdownInline { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownBreakBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownBulletListItem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownDocument { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownFencedCodeBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownHardLine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownHash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownHeader { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownHtmlBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownIndent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownIndentCodeBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownInlineCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownInlineEmphasis { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownInlineImage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownInlineLink { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownLinkBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownOrderListItem { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownParagraph { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownQuote { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownSetextHeader { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownSoftBreak { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for MarkdownTextual { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +#[derive(Clone, PartialEq, Eq, Hash, Serialize)] +pub struct MarkdownBogus { + syntax: SyntaxNode, +} +impl MarkdownBogus { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn items(&self) -> SyntaxElementChildren { + support::elements(&self.syntax) + } +} +impl AstNode for MarkdownBogus { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_BOGUS as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_BOGUS + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for MarkdownBogus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MarkdownBogus") + .field("items", &DebugSyntaxElementChildren(self.items())) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: MarkdownBogus) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: MarkdownBogus) -> SyntaxElement { + n.syntax.into() + } +} +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct MarkdownBlockList { + syntax_list: SyntaxList, +} +impl MarkdownBlockList { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { + syntax_list: syntax.into_list(), + } + } +} +impl AstNode for MarkdownBlockList { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_BLOCK_LIST as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_BLOCK_LIST + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(MarkdownBlockList { + syntax_list: syntax.into_list(), + }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + self.syntax_list.node() + } + fn into_syntax(self) -> SyntaxNode { + self.syntax_list.into_node() + } +} +impl Serialize for MarkdownBlockList { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + for e in self.iter() { + seq.serialize_element(&e)?; + } + seq.end() + } +} +impl AstNodeList for MarkdownBlockList { + type Language = Language; + type Node = AnyMarkdownBlock; + fn syntax_list(&self) -> &SyntaxList { + &self.syntax_list + } + fn into_syntax_list(self) -> SyntaxList { + self.syntax_list + } +} +impl Debug for MarkdownBlockList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("MarkdownBlockList ")?; + f.debug_list().entries(self.iter()).finish() + } +} +impl IntoIterator for &MarkdownBlockList { + type Item = AnyMarkdownBlock; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +impl IntoIterator for MarkdownBlockList { + type Item = AnyMarkdownBlock; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct MarkdownBulletList { + syntax_list: SyntaxList, +} +impl MarkdownBulletList { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { + syntax_list: syntax.into_list(), + } + } +} +impl AstNode for MarkdownBulletList { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_BULLET_LIST as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_BULLET_LIST + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(MarkdownBulletList { + syntax_list: syntax.into_list(), + }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + self.syntax_list.node() + } + fn into_syntax(self) -> SyntaxNode { + self.syntax_list.into_node() + } +} +impl Serialize for MarkdownBulletList { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + for e in self.iter() { + seq.serialize_element(&e)?; + } + seq.end() + } +} +impl AstNodeList for MarkdownBulletList { + type Language = Language; + type Node = AnyCodeBlock; + fn syntax_list(&self) -> &SyntaxList { + &self.syntax_list + } + fn into_syntax_list(self) -> SyntaxList { + self.syntax_list + } +} +impl Debug for MarkdownBulletList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("MarkdownBulletList ")?; + f.debug_list().entries(self.iter()).finish() + } +} +impl IntoIterator for &MarkdownBulletList { + type Item = AnyCodeBlock; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +impl IntoIterator for MarkdownBulletList { + type Item = AnyCodeBlock; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct MarkdownHashList { + syntax_list: SyntaxList, +} +impl MarkdownHashList { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { + syntax_list: syntax.into_list(), + } + } +} +impl AstNode for MarkdownHashList { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_HASH_LIST as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_HASH_LIST + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(MarkdownHashList { + syntax_list: syntax.into_list(), + }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + self.syntax_list.node() + } + fn into_syntax(self) -> SyntaxNode { + self.syntax_list.into_node() + } +} +impl Serialize for MarkdownHashList { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + for e in self.iter() { + seq.serialize_element(&e)?; + } + seq.end() + } +} +impl AstNodeList for MarkdownHashList { + type Language = Language; + type Node = MarkdownHash; + fn syntax_list(&self) -> &SyntaxList { + &self.syntax_list + } + fn into_syntax_list(self) -> SyntaxList { + self.syntax_list + } +} +impl Debug for MarkdownHashList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("MarkdownHashList ")?; + f.debug_list().entries(self.iter()).finish() + } +} +impl IntoIterator for &MarkdownHashList { + type Item = MarkdownHash; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +impl IntoIterator for MarkdownHashList { + type Item = MarkdownHash; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct MarkdownOrderList { + syntax_list: SyntaxList, +} +impl MarkdownOrderList { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { + syntax_list: syntax.into_list(), + } + } +} +impl AstNode for MarkdownOrderList { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_ORDER_LIST as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_ORDER_LIST + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(MarkdownOrderList { + syntax_list: syntax.into_list(), + }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + self.syntax_list.node() + } + fn into_syntax(self) -> SyntaxNode { + self.syntax_list.into_node() + } +} +impl Serialize for MarkdownOrderList { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + for e in self.iter() { + seq.serialize_element(&e)?; + } + seq.end() + } +} +impl AstNodeList for MarkdownOrderList { + type Language = Language; + type Node = AnyCodeBlock; + fn syntax_list(&self) -> &SyntaxList { + &self.syntax_list + } + fn into_syntax_list(self) -> SyntaxList { + self.syntax_list + } +} +impl Debug for MarkdownOrderList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("MarkdownOrderList ")?; + f.debug_list().entries(self.iter()).finish() + } +} +impl IntoIterator for &MarkdownOrderList { + type Item = AnyCodeBlock; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +impl IntoIterator for MarkdownOrderList { + type Item = AnyCodeBlock; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct MarkdownParagraphItemList { + syntax_list: SyntaxList, +} +impl MarkdownParagraphItemList { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { + syntax_list: syntax.into_list(), + } + } +} +impl AstNode for MarkdownParagraphItemList { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(MARKDOWN_PARAGRAPH_ITEM_LIST as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == MARKDOWN_PARAGRAPH_ITEM_LIST + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(MarkdownParagraphItemList { + syntax_list: syntax.into_list(), + }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + self.syntax_list.node() + } + fn into_syntax(self) -> SyntaxNode { + self.syntax_list.into_node() + } +} +impl Serialize for MarkdownParagraphItemList { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + for e in self.iter() { + seq.serialize_element(&e)?; + } + seq.end() + } +} +impl AstNodeList for MarkdownParagraphItemList { + type Language = Language; + type Node = AnyMarkdownInline; + fn syntax_list(&self) -> &SyntaxList { + &self.syntax_list + } + fn into_syntax_list(self) -> SyntaxList { + self.syntax_list + } +} +impl Debug for MarkdownParagraphItemList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("MarkdownParagraphItemList ")?; + f.debug_list().entries(self.iter()).finish() + } +} +impl IntoIterator for &MarkdownParagraphItemList { + type Item = AnyMarkdownInline; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +impl IntoIterator for MarkdownParagraphItemList { + type Item = AnyMarkdownInline; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +#[derive(Clone)] +pub struct DebugSyntaxElementChildren(pub SyntaxElementChildren); +impl Debug for DebugSyntaxElementChildren { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list() + .entries(self.clone().0.map(DebugSyntaxElement)) + .finish() + } +} +struct DebugSyntaxElement(SyntaxElement); +impl Debug for DebugSyntaxElement { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match &self.0 { + SyntaxElement::Node(node) => { + map_syntax_node ! (node . clone () , node => std :: fmt :: Debug :: fmt (& node , f)) + } + SyntaxElement::Token(token) => Debug::fmt(token, f), + } + } +} diff --git a/crates/biome_markdown_syntax/src/generated/nodes_mut.rs b/crates/biome_markdown_syntax/src/generated/nodes_mut.rs new file mode 100644 index 000000000000..c6b3cea23b8a --- /dev/null +++ b/crates/biome_markdown_syntax/src/generated/nodes_mut.rs @@ -0,0 +1,233 @@ +//! Generated file, do not edit by hand, see `xtask/codegen` + +use crate::{generated::nodes::*, MarkdownSyntaxToken as SyntaxToken}; +use biome_rowan::AstNode; +use std::iter::once; +impl MarkdownBreakBlock { + pub fn with_value_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } +} +impl MarkdownBulletListItem { + pub fn with_markdown_bullet_list(self, element: MarkdownBulletList) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl MarkdownDocument { + pub fn with_bom_token(self, element: Option) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(element.map(|element| element.into()))), + ) + } + pub fn with_value(self, element: MarkdownBlockList) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_eof_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into()))), + ) + } +} +impl MarkdownFencedCodeBlock { + pub fn with_markdown_textual(self, element: MarkdownTextual) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl MarkdownHardLine { + pub fn with_value_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } +} +impl MarkdownHash { + pub fn with_hash_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } +} +impl MarkdownHeader { + pub fn with_before(self, element: MarkdownHashList) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_markdown_paragraph(self, element: Option) -> Self { + Self::unwrap_cast(self.syntax.splice_slots( + 1usize..=1usize, + once(element.map(|element| element.into_syntax().into())), + )) + } + pub fn with_after(self, element: MarkdownHashList) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl MarkdownHtmlBlock { + pub fn with_markdown_textual(self, element: MarkdownTextual) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl MarkdownIndent { + pub fn with_value_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } +} +impl MarkdownIndentCodeBlock { + pub fn with_markdown_textual(self, element: MarkdownTextual) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl MarkdownInlineCode { + pub fn with_markdown_textual(self, element: MarkdownTextual) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl MarkdownInlineEmphasis { + pub fn with_markdown_textual(self, element: MarkdownTextual) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl MarkdownInlineImage { + pub fn with_alt(self, element: MarkdownTextual) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_src(self, element: MarkdownTextual) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_title(self, element: Option) -> Self { + Self::unwrap_cast(self.syntax.splice_slots( + 2usize..=2usize, + once(element.map(|element| element.into_syntax().into())), + )) + } +} +impl MarkdownInlineLink { + pub fn with_label(self, element: MarkdownTextual) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_url(self, element: MarkdownTextual) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_title(self, element: Option) -> Self { + Self::unwrap_cast(self.syntax.splice_slots( + 2usize..=2usize, + once(element.map(|element| element.into_syntax().into())), + )) + } +} +impl MarkdownLinkBlock { + pub fn with_label(self, element: MarkdownTextual) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_url(self, element: MarkdownTextual) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_title(self, element: Option) -> Self { + Self::unwrap_cast(self.syntax.splice_slots( + 2usize..=2usize, + once(element.map(|element| element.into_syntax().into())), + )) + } +} +impl MarkdownOrderListItem { + pub fn with_markdown_bullet_list(self, element: MarkdownBulletList) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl MarkdownParagraph { + pub fn with_markdown_paragraph_item_list(self, element: MarkdownParagraphItemList) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl MarkdownQuote { + pub fn with_any_markdown_block(self, element: AnyMarkdownBlock) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl MarkdownSetextHeader { + pub fn with_markdown_paragraph(self, element: MarkdownParagraph) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl MarkdownSoftBreak { + pub fn with_value_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } +} +impl MarkdownTextual { + pub fn with_value_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } +} diff --git a/crates/biome_markdown_syntax/src/lib.rs b/crates/biome_markdown_syntax/src/lib.rs new file mode 100644 index 000000000000..68e716eb0e05 --- /dev/null +++ b/crates/biome_markdown_syntax/src/lib.rs @@ -0,0 +1,68 @@ +#[macro_use] +mod generated; +mod syntax_node; + +pub use self::generated::*; +use biome_rowan::{RawSyntaxKind, SyntaxKind, TriviaPieceKind}; +pub use syntax_node::*; + +impl From for MarkdownSyntaxKind { + fn from(d: u16) -> MarkdownSyntaxKind { + assert!(d <= (MarkdownSyntaxKind::__LAST as u16)); + unsafe { std::mem::transmute::(d) } + } +} + +impl biome_rowan::SyntaxKind for MarkdownSyntaxKind { + const TOMBSTONE: Self = MarkdownSyntaxKind::TOMBSTONE; + + const EOF: Self = MarkdownSyntaxKind::EOF; + + fn is_bogus(&self) -> bool { + matches!(self, MarkdownSyntaxKind::MARKDOWN_BOGUS) + } + + fn to_bogus(&self) -> Self { + Self::MARKDOWN_BOGUS + } + + fn to_raw(&self) -> biome_rowan::RawSyntaxKind { + RawSyntaxKind(*self as u16) + } + + fn from_raw(raw: biome_rowan::RawSyntaxKind) -> Self { + Self::from(raw.0) + } + + fn is_root(&self) -> bool { + todo!() + } + + fn is_list(&self) -> bool { + MarkdownSyntaxKind::is_list(*self) + } + + fn is_trivia(self) -> bool { + matches!(self, MarkdownSyntaxKind::NEWLINE) + } + + fn to_string(&self) -> Option<&'static str> { + MarkdownSyntaxKind::to_string(self) + } +} + +impl TryFrom for TriviaPieceKind { + type Error = (); + + fn try_from(value: MarkdownSyntaxKind) -> Result { + if value.is_trivia() { + match value { + MarkdownSyntaxKind::NEWLINE => Ok(TriviaPieceKind::Newline), + MarkdownSyntaxKind::WHITESPACE => Ok(TriviaPieceKind::Whitespace), + _ => unreachable!("Not Trivia"), + } + } else { + Err(()) + } + } +} diff --git a/crates/biome_markdown_syntax/src/syntax_node.rs b/crates/biome_markdown_syntax/src/syntax_node.rs new file mode 100644 index 000000000000..3a18372d3840 --- /dev/null +++ b/crates/biome_markdown_syntax/src/syntax_node.rs @@ -0,0 +1,24 @@ +//! This module defines the Concrete Syntax Tree used by Biome. +//! +//! The tree is entirely lossless, whitespace, comments, and errors are preserved. +//! It also provides traversal methods including parent, children, and siblings of nodes. +//! +//! This is a simple wrapper around the `rowan` crate which does most of the heavy lifting and is language agnostic. + +use crate::{MarkdownDocument, MarkdownSyntaxKind}; +use biome_rowan::Language; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +pub struct MarkdownLanguage; + +impl Language for MarkdownLanguage { + type Kind = MarkdownSyntaxKind; + type Root = MarkdownDocument; +} + +pub type MarkdownSyntaxNode = biome_rowan::SyntaxNode; +pub type MarkdownSyntaxToken = biome_rowan::SyntaxToken; +pub type MarkdownSyntaxElement = biome_rowan::SyntaxElement; +pub type MarkdownSyntaxNodeChildren = biome_rowan::SyntaxNodeChildren; +pub type MarkdownSyntaxElementChildren = biome_rowan::SyntaxElementChildren; +pub type MarkdownSyntaxList = biome_rowan::SyntaxList; diff --git a/xtask/codegen/markdown.ungram b/xtask/codegen/markdown.ungram new file mode 100644 index 000000000000..2bad3f2a348f --- /dev/null +++ b/xtask/codegen/markdown.ungram @@ -0,0 +1,124 @@ +// Markdown Un-Grammar. +// +// This grammar specifies the structure of Rust's concrete syntax tree. +// It does not specify parsing rules (ambiguities, precedence, etc are out of scope). +// Tokens are processed -- contextual keywords are recognised, compound operators glued. +// +// Legend: +// +// // -- comment +// Name = -- non-terminal definition +// 'ident' -- token (terminal) +// A B -- sequence +// A | B -- alternation +// A* -- zero or more repetition +// (A (',' A)* ','?) -- repetition of node A separated by ',' and allowing a trailing comma +// (A (',' A)*) -- repetition of node A separated by ',' without a trailing comma +// A? -- zero or one repetition +// (A) -- same as A +// label:A -- suggested name for field of AST node + +// NOTES +// +// - SyntaxNode, SyntaxToken and SyntaxElement will be stripped from the codegen +// - Bogus nodes are special nodes used to keep track of broken code; they are +// not part of the grammar but they will appear inside the green tree + + +/////////////// +// BOGUS NODES +/////////////// +// SyntaxElement is a generic data structure that is meant to track nodes and tokens +// in cases where we care about both types +// +// As Bogus* node will need to yield both tokens and nodes without discrimination, +// and their children will need to yield nodes and tokens as well. +// For this reason, SyntaxElement = SyntaxElement + +SyntaxElement = SyntaxElement + +MarkdownBogus = SyntaxElement* + +MarkdownDocument = + bom: 'UNICODE_BOM'? + value: MarkdownBlockList + eof: 'EOF' + +MarkdownBlockList = AnyMarkdownBlock* + +AnyMarkdownBlock = AnyLeafBlock | AnyContainerBlock + +AnyLeafBlock = MarkdownBreakBlock | MarkdownHeader | MarkdownSetextHeader + | AnyCodeBlock | MarkdownHtmlBlock | MarkdownLinkBlock | MarkdownParagraph + +AnyContainerBlock = MarkdownQuote | MarkdownBulletListItem | MarkdownOrderListItem + + + +// h1..h6 +MarkdownHeader = before:MarkdownHashList MarkdownParagraph? after:MarkdownHashList + +MarkdownHashList = MarkdownHash* + +MarkdownHash = '#' + + +// setext title +// foo +// === +// bar +// --- +MarkdownSetextHeader = MarkdownParagraph + +// indented code blocks & fenced code blocks +AnyCodeBlock = MarkdownIndentCodeBlock | MarkdownFencedCodeBlock + +// [ ] xxx +MarkdownIndentCodeBlock = MarkdownTextual +// ~~~ +// xxx +// ~~~ +MarkdownFencedCodeBlock = MarkdownTextual + +// html block +MarkdownHtmlBlock = MarkdownTextual + +MarkdownLinkBlock = label: MarkdownTextual + url: MarkdownTextual + title: MarkdownTextual? + +MarkdownQuote = AnyMarkdownBlock + +MarkdownBulletListItem = MarkdownBulletList +MarkdownOrderListItem = MarkdownBulletList + + +MarkdownBulletList = AnyCodeBlock* +MarkdownOrderList = AnyCodeBlock* + + +MarkdownParagraph = MarkdownParagraphItemList + +MarkdownParagraphItemList = AnyMarkdownInline* + +AnyMarkdownInline = MarkdownInlineCode | MarkdownInlineEmphasis | MarkdownInlineLink | MarkdownInlineImage + | MarkdownHtmlBlock | MarkdownHardLine | MarkdownSoftBreak + | MarkdownTextual +MarkdownInlineCode = MarkdownTextual +MarkdownInlineEmphasis = MarkdownTextual +MarkdownInlineLink = label: MarkdownTextual + url: MarkdownTextual + title: MarkdownTextual? + +MarkdownInlineImage = alt: MarkdownTextual + src: MarkdownTextual + title: MarkdownTextual? + +MarkdownHardLine = value: 'markdown_hard_line_literal' +MarkdownSoftBreak = value: 'markdown_soft_break_literal' +MarkdownTextual = value: 'markdown_textual_literal' +MarkdownIndent = value: 'markdown_indent_chunk_literal' +// *** +// --- +// ___ +MarkdownBreakBlock = value: 'markdown_break_block_literal' \ No newline at end of file diff --git a/xtask/codegen/src/formatter.rs b/xtask/codegen/src/formatter.rs index ca7cdc5f50fa..65ca8638848b 100644 --- a/xtask/codegen/src/formatter.rs +++ b/xtask/codegen/src/formatter.rs @@ -781,6 +781,7 @@ fn get_node_concept( _ if name.ends_with("Value") => NodeConcept::Value, _ => NodeConcept::Auxiliary, }, + LanguageKind::Markdown => NodeConcept::Auxiliary, LanguageKind::Css => match name { _ if name.ends_with("AtRule") => NodeConcept::Statement, _ if name.ends_with("Selector") => NodeConcept::Selector, @@ -890,6 +891,7 @@ impl LanguageKind { LanguageKind::Grit => "GritFormatter", LanguageKind::Html => "HtmlFormatter", LanguageKind::Yaml => "YamlFormatter", + LanguageKind::Markdown => "DemoFormatter", }; Ident::new(name, Span::call_site()) @@ -904,6 +906,7 @@ impl LanguageKind { LanguageKind::Grit => "GritFormatContext", LanguageKind::Html => "HtmlFormatContext", LanguageKind::Yaml => "YamlFormatContext", + LanguageKind::Markdown => "DemoFormatterContext", }; Ident::new(name, Span::call_site()) diff --git a/xtask/codegen/src/generate_syntax_kinds.rs b/xtask/codegen/src/generate_syntax_kinds.rs index adf376a9c257..19c8068a64ef 100644 --- a/xtask/codegen/src/generate_syntax_kinds.rs +++ b/xtask/codegen/src/generate_syntax_kinds.rs @@ -133,6 +133,18 @@ pub fn generate_syntax_kinds(grammar: KindsSrc, language_kind: LanguageKind) -> } } } + LanguageKind::Markdown => { + quote! { + pub const fn to_string(&self) -> Option<&'static str> { + let tok = match self { + #(#punctuation => #punctuation_strings,)* + #(#full_keywords => #all_keyword_to_strings,)* + _ => return None, + }; + Some(tok) + } + } + } LanguageKind::Grit => { quote! { pub const fn to_string(&self) -> Option<&'static str> { diff --git a/xtask/codegen/src/language_kind.rs b/xtask/codegen/src/language_kind.rs index 63da4c4d3307..7de12e90e371 100644 --- a/xtask/codegen/src/language_kind.rs +++ b/xtask/codegen/src/language_kind.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use crate::css_kinds_src::CSS_KINDS_SRC; use crate::graphql_kind_src::GRAPHQL_KINDS_SRC; use crate::grit_kinds_src::GRIT_KINDS_SRC; @@ -5,13 +7,22 @@ use crate::html_kinds_src::HTML_KINDS_SRC; use crate::js_kinds_src::JS_KINDS_SRC; use crate::json_kinds_src::JSON_KINDS_SRC; use crate::kind_src::KindsSrc; +use crate::markdown_kinds_src::MARKDOWN_KINDS_SRC; use crate::yaml_kinds_src::YAML_KINDS_SRC; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; -use std::str::FromStr; -pub const LANGUAGE_PREFIXES: [&str; 9] = [ - "js_", "ts_", "jsx_", "tsx_", "css_", "json_", "grit_", "html_", "yaml_", +pub const LANGUAGE_PREFIXES: [&str; 10] = [ + "js_", + "ts_", + "jsx_", + "tsx_", + "css_", + "json_", + "grit_", + "html_", + "yaml_", + "markdown_", ]; #[derive(Debug, Eq, Copy, Clone, PartialEq)] @@ -23,6 +34,7 @@ pub enum LanguageKind { Grit, Html, Yaml, + Markdown, } impl std::fmt::Display for LanguageKind { @@ -35,11 +47,12 @@ impl std::fmt::Display for LanguageKind { LanguageKind::Grit => write!(f, "grit"), LanguageKind::Html => write!(f, "html"), LanguageKind::Yaml => write!(f, "yaml"), + LanguageKind::Markdown => write!(f, "markdown"), } } } -pub const ALL_LANGUAGE_KIND: [LanguageKind; 7] = [ +pub const ALL_LANGUAGE_KIND: [LanguageKind; 8] = [ LanguageKind::Js, LanguageKind::Css, LanguageKind::Json, @@ -47,6 +60,7 @@ pub const ALL_LANGUAGE_KIND: [LanguageKind; 7] = [ LanguageKind::Grit, LanguageKind::Html, LanguageKind::Yaml, + LanguageKind::Markdown, ]; impl FromStr for LanguageKind { @@ -61,8 +75,9 @@ impl FromStr for LanguageKind { "grit" => Ok(LanguageKind::Grit), "html" => Ok(LanguageKind::Html), "yaml" => Ok(LanguageKind::Yaml), + "markdown" => Ok(LanguageKind::Markdown), _ => Err(format!( - "Language {kind} not supported, please use: `js`, `css`, `json`, `grit`, `graphql`, `html` or `yaml`" + "Language {kind} not supported, please use: `js`, `css`, `json`, `grit`, `graphql`, `html`, `yaml` or `markdown`" )), } } @@ -98,7 +113,7 @@ macro_rules! define_language_kind_functions { } impl LanguageKind { - define_language_kind_functions!([Js, Css, Json, Graphql, Grit, Html, Yaml]); + define_language_kind_functions!([Js, Css, Json, Graphql, Grit, Html, Yaml, Markdown]); pub(crate) fn syntax_crate_ident(&self) -> Ident { Ident::new(self.syntax_crate_name().as_str(), Span::call_site()) @@ -125,6 +140,7 @@ impl LanguageKind { LanguageKind::Grit => GRIT_KINDS_SRC, LanguageKind::Html => HTML_KINDS_SRC, LanguageKind::Yaml => YAML_KINDS_SRC, + LanguageKind::Markdown => MARKDOWN_KINDS_SRC, } } @@ -137,6 +153,7 @@ impl LanguageKind { LanguageKind::Grit => include_str!("../gritql.ungram"), LanguageKind::Html => include_str!("../html.ungram"), LanguageKind::Yaml => include_str!("../yaml.ungram"), + LanguageKind::Markdown => include_str!("../markdown.ungram"), } } } diff --git a/xtask/codegen/src/lib.rs b/xtask/codegen/src/lib.rs index 630f14bc22ed..34a2bb100f19 100644 --- a/xtask/codegen/src/lib.rs +++ b/xtask/codegen/src/lib.rs @@ -15,6 +15,7 @@ mod graphql_kind_src; mod grit_kinds_src; mod js_kinds_src; mod json_kinds_src; +mod markdown_kinds_src; mod yaml_kinds_src; mod generate_crate; diff --git a/xtask/codegen/src/markdown_kinds_src.rs b/xtask/codegen/src/markdown_kinds_src.rs new file mode 100644 index 000000000000..af99e83527ae --- /dev/null +++ b/xtask/codegen/src/markdown_kinds_src.rs @@ -0,0 +1,66 @@ +use crate::kind_src::KindsSrc; + +// 对Token进行分组,生成is_xx 方法 +pub const MARKDOWN_KINDS_SRC: KindsSrc = KindsSrc { + punct: &[ + ("<", "L_ANGLE"), + (">", "R_ANGLE"), + ("(", "L_PAREN"), + (")", "R_PAREN"), + ("[", "L_BRACK"), + ("]", "R_BRACK"), + ("/", "SLASH"), + ("=", "EQ"), + ("!", "BANG"), + ("-", "MINUS"), + ("*", "STAR"), + ("`", "BACKTICK"), + ("~", "TILDE"), + (" ", "WHITESPACE3"), + ("_", "UNDERSCORE"), + ("#", "HASH"), + ], + keywords: &["false"], + literals: &[ + "MARKDOWN_HARD_LINE_LITERAL", + "MARKDOWN_SOFT_BREAK_LITERAL", + "MARKDOWN_TEXTUAL_LITERAL", + "MARKDOWN_STRING_LITERAL", + "MARKDOWN_INDENT_CHUNK_LITERAL", + "MARKDOWN_BREAK_BLOCK_LITERAL", + ], + tokens: &["NEWLINE", "WHITESPACE", "TAB"], + nodes: &[ + // Bogus nodes + "BOGUS", + "MARKDOWN_BOGUS", + // node + "MARKDOWN_DOCUMENT", + "MARKDOWN_BLOCK_LIST", + "MARKDOWN_HASH_LIST", + "MARKDOWN_HASH", + "MARKDOWN_HEADER", + "MARKDOWN_INDENT_CODE_BLOCK", + "MARKDOWN_FENCED_CODE_BLOCK", + "MARKDOWN_HTML_BLOCK", + "MARKDOWN_LINK_BLOCK", + "MARKDOWN_QUOTE", + "MARKDOWN_ORDER_LIST_ITEM", + "MARKDOWN_BULLET_LIST_ITEM", + "MARKDOWN_BULLET_LIST", + "MARKDOWN_ORDER_LIST", + "MARKDOWN_PARAGRAPH", + "MARKDOWN_PARAGRAPH_ITEM_LIST", + "MARKDOWN_INLINE_CODE", + "MARKDOWN_INLINE_EMPHASIS", + "MARKDOWN_INLINE_LINK", + "MARKDOWN_INLINE_IMAGE", + "MARKDOWN_HARD_LINE", + "MARKDOWN_SOFT_BREAK", + "MARKDOWN_TEXTUAL", + "MARKDOWN_SETEXT_HEADER", + "MARKDOWN_STRING", + "MARKDOWN_INDENT", + "MARKDOWN_BREAK_BLOCK", + ], +};