Markdown parser fork with extended syntax for personal use.
1//! Label start (image) occurs in the [text][] content type.
2//!
3//! ## Grammar
4//!
5//! Label start (image) forms with the following BNF
6//! (<small>see [construct][crate::construct] for character groups</small>):
7//!
8//! ```bnf
9//! label_start_image ::= '!' '['
10//! ```
11//!
12//! ## HTML
13//!
14//! Label start (image) does not, on its own, relate to anything in HTML.
15//! When matched with a [label end][label_end], they together relate to the
16//! `<img>` element in HTML.
17//! See [*§ 4.8.3 The `img` element*][html_img] in the HTML spec for more info.
18//! Without an end, the characters (`![`) are output.
19//!
20//! ## Tokens
21//!
22//! * [`LabelImage`][Name::LabelImage]
23//! * [`LabelImageMarker`][Name::LabelImageMarker]
24//! * [`LabelMarker`][Name::LabelMarker]
25//!
26//! ## References
27//!
28//! * [`label-start-image.js` in `micromark`](https://github.com/micromark/micromark/blob/main/packages/micromark-core-commonmark/dev/lib/label-start-image.js)
29//! * [*§ 6.4 Images* in `CommonMark`](https://spec.commonmark.org/0.31/#images)
30//!
31//! [text]: crate::construct::text
32//! [label_end]: crate::construct::label_end
33//! [html_img]: https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element
34
35use crate::event::Name;
36use crate::resolve::Name as ResolveName;
37use crate::state::{Name as StateName, State};
38use crate::tokenizer::{LabelKind, LabelStart, Tokenizer};
39
40/// Start of label (image) start.
41///
42/// ```markdown
43/// > | a ![b] c
44/// ^
45/// ```
46pub fn start(tokenizer: &mut Tokenizer) -> State {
47 if tokenizer.parse_state.options.constructs.label_start_image && tokenizer.current == Some(b'!')
48 {
49 tokenizer.enter(Name::LabelImage);
50 tokenizer.enter(Name::LabelImageMarker);
51 tokenizer.consume();
52 tokenizer.exit(Name::LabelImageMarker);
53 State::Next(StateName::LabelStartImageOpen)
54 } else {
55 State::Nok
56 }
57}
58
59/// After `!`, at `[`.
60///
61/// ```markdown
62/// > | a ![b] c
63/// ^
64/// ```
65pub fn open(tokenizer: &mut Tokenizer) -> State {
66 match tokenizer.current {
67 Some(b'[') => {
68 tokenizer.enter(Name::LabelMarker);
69 tokenizer.consume();
70 tokenizer.exit(Name::LabelMarker);
71 tokenizer.exit(Name::LabelImage);
72 State::Next(StateName::LabelStartImageAfter)
73 }
74 _ => State::Nok,
75 }
76}
77
78/// After `![`.
79///
80/// ```markdown
81/// > | a ![b] c
82/// ^
83/// ```
84///
85/// This is needed in because, when GFM footnotes are enabled, images never
86/// form when started with a `^`.
87/// Instead, links form:
88///
89/// ```markdown
90/// 
91///
92/// ![^a][b]
93///
94/// [b]: c
95/// ```
96///
97/// ```html
98/// <p>!<a href=\"b\">^a</a></p>
99/// <p>!<a href=\"c\">^a</a></p>
100/// ```
101pub fn after(tokenizer: &mut Tokenizer) -> State {
102 if tokenizer
103 .parse_state
104 .options
105 .constructs
106 .gfm_label_start_footnote
107 && tokenizer.current == Some(b'^')
108 {
109 State::Nok
110 } else {
111 tokenizer.tokenize_state.label_starts.push(LabelStart {
112 kind: LabelKind::Image,
113 start: (tokenizer.events.len() - 6, tokenizer.events.len() - 1),
114 inactive: false,
115 });
116 tokenizer.register_resolver_before(ResolveName::Label);
117 State::Ok
118 }
119}