Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit daa1be4

Browse filesBrowse files
committed
Add support for custom block quote admonition kinds
This adds a callback handler that can parse any arbitrary block quote kind, as long as square brackets are correctly nested. You can choose your own data type for the payload, as long as it can be copied, and it should cover obsidion's stuff.
1 parent 4564d43 commit daa1be4
Copy full SHA for daa1be4

File tree

Expand file treeCollapse file tree

7 files changed

+482
-145
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

7 files changed

+482
-145
lines changed
Open diff view settings
Collapse file

‎pulldown-cmark/src/firstpass.rs‎

Copy file name to clipboardExpand all lines: pulldown-cmark/src/firstpass.rs
+2-32Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -229,43 +229,13 @@ impl<'a, 'b> FirstPass<'a, 'b> {
229229
return after_marker_index + n;
230230
}
231231
} else if line_start.scan_blockquote_marker() {
232-
let kind = if self.options.contains(Options::ENABLE_GFM) {
233-
line_start.scan_blockquote_tag()
234-
} else {
235-
None
236-
};
237232
self.finish_list(start_ix);
238233
self.tree.append(Item {
239234
start: container_start,
240235
end: 0, // will get set later
241-
body: ItemBody::BlockQuote(kind),
236+
body: ItemBody::BlockQuote,
242237
});
243238
self.tree.push();
244-
if kind.is_some() {
245-
// blockquote tag leaves us at the end of the line
246-
// we need to scan through all the container syntax for the next line
247-
// and break out if we can't re-scan all of them
248-
let ix = start_ix + line_start.bytes_scanned();
249-
let mut lazy_line_start = LineStart::new(&bytes[ix..]);
250-
let current_container =
251-
scan_containers(&self.tree, &mut lazy_line_start, self.options)
252-
== self.tree.spine_len();
253-
if !lazy_line_start.scan_space(4)
254-
&& self.scan_paragraph_interrupt(
255-
&bytes[ix + lazy_line_start.bytes_scanned()..],
256-
current_container,
257-
)
258-
{
259-
return ix;
260-
} else {
261-
// blockquote tags act as if they were nested in a paragraph
262-
// so you can lazily continue the imaginary paragraph off of them
263-
line_start = lazy_line_start;
264-
line_start.scan_all_space();
265-
start_ix = ix;
266-
break;
267-
}
268-
}
269239
} else {
270240
line_start = save;
271241
break;
@@ -277,7 +247,7 @@ impl<'a, 'b> FirstPass<'a, 'b> {
277247
if let Some(n) = scan_blank_line(&bytes[ix..]) {
278248
if let Some(node_ix) = self.tree.peek_up() {
279249
match &mut self.tree[node_ix].item.body {
280-
ItemBody::BlockQuote(..) => (),
250+
ItemBody::BlockQuote => (),
281251
ItemBody::ListItem(indent) | ItemBody::DefinitionListDefinition(indent)
282252
if self.begin_list_item.is_some() =>
283253
{
Collapse file

‎pulldown-cmark/src/lib.rs‎

Copy file name to clipboardExpand all lines: pulldown-cmark/src/lib.rs
+131-18Lines changed: 131 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ mod tree;
9797
use std::fmt::Display;
9898

9999
pub use crate::parse::{
100-
BrokenLink, BrokenLinkCallback, DefaultBrokenLinkCallback, OffsetIter, Parser, RefDefs,
100+
AdmonitionTagCallback, BrokenLink, BrokenLinkCallback, DefaultBrokenLinkCallback,
101+
DisableAdmonitionTagCallback, GfmAdmonitionTagCallback, OffsetIter, Parser, RefDefs,
101102
};
102103
pub use crate::strings::{CowStr, InlineStr};
103104
pub use crate::utils::*;
@@ -151,7 +152,7 @@ pub enum MetadataBlockKind {
151152
/// Tags for elements that can contain other elements.
152153
#[derive(Clone, Debug, PartialEq)]
153154
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
154-
pub enum Tag<'a> {
155+
pub enum Tag<'a, Admonition = BlockQuoteKind> {
155156
/// A paragraph of text and other inline elements.
156157
Paragraph,
157158

@@ -171,15 +172,15 @@ pub enum Tag<'a> {
171172

172173
/// A block quote.
173174
///
174-
/// The `BlockQuoteKind` is only parsed & populated with [`Options::ENABLE_GFM`], `None` otherwise.
175+
/// The `BlockQuoteKind` is only parsed & populated with [`Options::ENABLE_BLOCK_QUOTE_ADMONITIONS`], `None` otherwise.
175176
///
176177
/// ```markdown
177178
/// > regular quote
178179
///
179180
/// > [!NOTE]
180181
/// > note quote
181182
/// ```
182-
BlockQuote(Option<BlockQuoteKind>),
183+
BlockQuote(Option<Admonition>),
183184
/// A code block.
184185
CodeBlock(CodeBlockKind<'a>),
185186

@@ -287,8 +288,8 @@ pub enum Tag<'a> {
287288
MetadataBlock(MetadataBlockKind),
288289
}
289290

290-
impl<'a> Tag<'a> {
291-
pub fn to_end(&self) -> TagEnd {
291+
impl<'a, Admonition: Clone + Copy + Eq + PartialEq> Tag<'a, Admonition> {
292+
pub fn to_end(&self) -> TagEnd<Admonition> {
292293
match self {
293294
Tag::Paragraph => TagEnd::Paragraph,
294295
Tag::Heading { level, .. } => TagEnd::Heading(*level),
@@ -316,7 +317,7 @@ impl<'a> Tag<'a> {
316317
}
317318
}
318319

319-
pub fn into_static(self) -> Tag<'static> {
320+
pub fn into_static(self) -> Tag<'static, Admonition> {
320321
match self {
321322
Tag::Paragraph => Tag::Paragraph,
322323
Tag::Heading {
@@ -381,11 +382,11 @@ impl<'a> Tag<'a> {
381382
/// The end of a `Tag`.
382383
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
383384
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
384-
pub enum TagEnd {
385+
pub enum TagEnd<Admonition = BlockQuoteKind> {
385386
Paragraph,
386387
Heading(HeadingLevel),
387388

388-
BlockQuote(Option<BlockQuoteKind>),
389+
BlockQuote(Option<Admonition>),
389390
CodeBlock,
390391

391392
HtmlBlock,
@@ -521,14 +522,14 @@ impl LinkType {
521522
/// have been visited.
522523
#[derive(Clone, Debug, PartialEq)]
523524
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
524-
pub enum Event<'a> {
525+
pub enum Event<'a, Admonition = BlockQuoteKind> {
525526
/// Start of a tagged element. Events that are yielded after this event
526527
/// and before its corresponding `End` event are inside this element.
527528
/// Start and end events are guaranteed to be balanced.
528529
#[cfg_attr(feature = "serde", serde(borrow))]
529-
Start(Tag<'a>),
530+
Start(Tag<'a, Admonition>),
530531
/// End of a tagged element.
531-
End(TagEnd),
532+
End(TagEnd<Admonition>),
532533
/// A text node.
533534
///
534535
/// All text, outside and inside [`Tag`]s.
@@ -611,8 +612,8 @@ pub enum Event<'a> {
611612
TaskListMarker(bool),
612613
}
613614

614-
impl<'a> Event<'a> {
615-
pub fn into_static(self) -> Event<'static> {
615+
impl<'a, Admonition: Clone + Copy + Eq + PartialEq> Event<'a, Admonition> {
616+
pub fn into_static(self) -> Event<'static, Admonition> {
616617
match self {
617618
Event::Start(t) => Event::Start(t.into_static()),
618619
Event::End(e) => Event::End(e),
@@ -631,6 +632,120 @@ impl<'a> Event<'a> {
631632
}
632633
}
633634

635+
impl<'a, Admonition> Event<'a, Admonition> {
636+
pub fn map_admonition<A2, F: FnOnce(Admonition) -> A2>(self, f: F) -> Event<'a, A2> {
637+
match self {
638+
Event::Start(t) => Event::Start(t.map_admonition(f)),
639+
Event::End(e) => Event::End(e.map_admonition(f)),
640+
Event::Text(s) => Event::Text(s),
641+
Event::Code(s) => Event::Code(s),
642+
Event::InlineMath(s) => Event::InlineMath(s),
643+
Event::DisplayMath(s) => Event::DisplayMath(s),
644+
Event::Html(s) => Event::Html(s),
645+
Event::InlineHtml(s) => Event::InlineHtml(s),
646+
Event::FootnoteReference(s) => Event::FootnoteReference(s),
647+
Event::SoftBreak => Event::SoftBreak,
648+
Event::HardBreak => Event::HardBreak,
649+
Event::Rule => Event::Rule,
650+
Event::TaskListMarker(b) => Event::TaskListMarker(b),
651+
}
652+
}
653+
}
654+
655+
impl<'a, Admonition> Tag<'a, Admonition> {
656+
pub fn map_admonition<A2, F: FnOnce(Admonition) -> A2>(self, f: F) -> Tag<'a, A2> {
657+
match self {
658+
Tag::BlockQuote(Some(a)) => Tag::BlockQuote(Some(f(a))),
659+
Tag::BlockQuote(None) => Tag::BlockQuote(None),
660+
Tag::Paragraph => Tag::Paragraph,
661+
Tag::Heading {
662+
level,
663+
id,
664+
classes,
665+
attrs,
666+
} => Tag::Heading {
667+
level,
668+
id,
669+
classes,
670+
attrs,
671+
},
672+
Tag::CodeBlock(code_block_kind) => Tag::CodeBlock(code_block_kind),
673+
Tag::HtmlBlock => Tag::HtmlBlock,
674+
Tag::List(k) => Tag::List(k),
675+
Tag::Item => Tag::Item,
676+
Tag::FootnoteDefinition(cow_str) => Tag::FootnoteDefinition(cow_str),
677+
Tag::DefinitionList => Tag::DefinitionList,
678+
Tag::DefinitionListTitle => Tag::DefinitionListTitle,
679+
Tag::DefinitionListDefinition => Tag::DefinitionListDefinition,
680+
Tag::Table(alignments) => Tag::Table(alignments),
681+
Tag::TableHead => Tag::TableHead,
682+
Tag::TableRow => Tag::TableRow,
683+
Tag::TableCell => Tag::TableCell,
684+
Tag::Emphasis => Tag::Emphasis,
685+
Tag::Strong => Tag::Strong,
686+
Tag::Strikethrough => Tag::Strikethrough,
687+
Tag::Superscript => Tag::Superscript,
688+
Tag::Subscript => Tag::Subscript,
689+
Tag::Link {
690+
link_type,
691+
dest_url,
692+
title,
693+
id,
694+
} => Tag::Link {
695+
link_type,
696+
dest_url,
697+
title,
698+
id,
699+
},
700+
Tag::Image {
701+
link_type,
702+
dest_url,
703+
title,
704+
id,
705+
} => Tag::Image {
706+
link_type,
707+
dest_url,
708+
title,
709+
id,
710+
},
711+
Tag::MetadataBlock(metadata_block_kind) => Tag::MetadataBlock(metadata_block_kind),
712+
}
713+
}
714+
}
715+
716+
impl<Admonition> TagEnd<Admonition> {
717+
pub fn map_admonition<A2, F: FnOnce(Admonition) -> A2>(self, f: F) -> TagEnd<A2> {
718+
match self {
719+
TagEnd::BlockQuote(Some(a)) => TagEnd::BlockQuote(Some(f(a))),
720+
TagEnd::BlockQuote(None) => TagEnd::BlockQuote(None),
721+
TagEnd::Paragraph => TagEnd::Paragraph,
722+
TagEnd::Heading(heading_level) => TagEnd::Heading(heading_level),
723+
TagEnd::CodeBlock => TagEnd::CodeBlock,
724+
TagEnd::HtmlBlock => TagEnd::HtmlBlock,
725+
TagEnd::List(k) => TagEnd::List(k),
726+
TagEnd::Item => TagEnd::Item,
727+
TagEnd::FootnoteDefinition => TagEnd::FootnoteDefinition,
728+
TagEnd::DefinitionList => TagEnd::DefinitionList,
729+
TagEnd::DefinitionListTitle => TagEnd::DefinitionListTitle,
730+
TagEnd::DefinitionListDefinition => TagEnd::DefinitionListDefinition,
731+
TagEnd::Table => TagEnd::Table,
732+
TagEnd::TableHead => TagEnd::TableHead,
733+
TagEnd::TableRow => TagEnd::TableRow,
734+
TagEnd::TableCell => TagEnd::TableCell,
735+
TagEnd::Emphasis => TagEnd::Emphasis,
736+
TagEnd::Strong => TagEnd::Strong,
737+
TagEnd::Strikethrough => TagEnd::Strikethrough,
738+
TagEnd::Superscript => TagEnd::Superscript,
739+
TagEnd::Subscript => TagEnd::Subscript,
740+
TagEnd::Link => TagEnd::Link,
741+
TagEnd::Image => TagEnd::Image,
742+
TagEnd::MetadataBlock(metadata_block_kind) => {
743+
TagEnd::MetadataBlock(metadata_block_kind)
744+
}
745+
}
746+
}
747+
}
748+
634749
/// Table column text alignment.
635750
#[derive(Copy, Clone, Debug, PartialEq)]
636751
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -716,10 +831,8 @@ bitflags::bitflags! {
716831
/// With this feature enabled, two events `Event::InlineMath` and `Event::DisplayMath`
717832
/// are emitted that conventionally contain TeX formulas.
718833
const ENABLE_MATH = 1 << 10;
719-
/// Misc GitHub Flavored Markdown features not supported in CommonMark.
720-
/// The following features are currently behind this tag:
721-
/// - Blockquote tags ([!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION]).
722-
const ENABLE_GFM = 1 << 11;
834+
/// Block quote admonition syntax from GitHub Flavored Markdown.
835+
const ENABLE_BLOCK_QUOTE_ADMONITIONS = 1 << 11;
723836
/// Commonmark-HS-Extensions compatible definition lists.
724837
///
725838
/// ```markdown
Collapse file

‎pulldown-cmark/src/main.rs‎

Copy file name to clipboardExpand all lines: pulldown-cmark/src/main.rs
+7-3Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,11 @@ pub fn main() -> std::io::Result<()> {
100100
"reject-broken-links",
101101
"fail if input file has broken links",
102102
);
103-
opts.optflag("G", "enable-gfm", "enable misc GFM features");
103+
opts.optflag(
104+
"G",
105+
"enable-block-quote-admonitions",
106+
"enable > `[!NOTE]`-style syntax",
107+
);
104108
opts.optflag(
105109
"D",
106110
"enable-definition-list",
@@ -154,8 +158,8 @@ pub fn main() -> std::io::Result<()> {
154158
opts.insert(Options::ENABLE_YAML_STYLE_METADATA_BLOCKS);
155159
opts.insert(Options::ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS);
156160
}
157-
if matches.opt_present("enable-gfm") {
158-
opts.insert(Options::ENABLE_GFM);
161+
if matches.opt_present("enable-block-quote-admonitions") {
162+
opts.insert(Options::ENABLE_BLOCK_QUOTE_ADMONITIONS);
159163
}
160164
if matches.opt_present("enable-definition-list") {
161165
opts.insert(Options::ENABLE_DEFINITION_LIST);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.