gitlab-glfm-markdown 0.0.38-aarch64-linux-gnu → 0.0.40-aarch64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,366 +0,0 @@
1
- use std::fmt::{self, Write};
2
-
3
- use comrak::html::{render_sourcepos, ChildRendering, Context};
4
- use comrak::nodes::{AstNode, ListType, NodeValue};
5
- use comrak::{create_formatter, html};
6
- use lazy_static::lazy_static;
7
- use regex::Regex;
8
-
9
- // TODO: Use std::sync:LazyLock once we're on 1.80+.
10
- // https://doc.rust-lang.org/std/sync/struct.LazyLock.html
11
- lazy_static! {
12
- static ref PLACEHOLDER_REGEX: Regex = Regex::new(r"%(\{|%7B)(\w{1,30})(}|%7D)").unwrap();
13
- }
14
-
15
- pub struct RenderUserData {
16
- pub default_html: bool,
17
- pub inapplicable_tasks: bool,
18
- pub placeholder_detection: bool,
19
- pub only_escape_chars: Option<Vec<char>>,
20
- pub debug: bool,
21
- }
22
-
23
- // The important thing to remember is that this overrides the default behavior of the
24
- // specified nodes. If we do override a node, then it's our responsibility to ensure that
25
- // any changes in the `comrak` code for those nodes is backported to here, such as when
26
- // `figcaption` support was added.
27
- // One idea to limit that would be having the ability to specify attributes that would
28
- // be inserted when a node is rendered. That would allow us to (in many cases) just
29
- // inject the changes we need. Such a feature would need to be added to `comrak`.
30
- create_formatter!(CustomFormatter<RenderUserData>, {
31
- NodeValue::Text(_) => |context, node, entering| {
32
- return render_text(context, node, entering);
33
- },
34
- NodeValue::Link(_) => |context, node, entering| {
35
- return render_link(context, node, entering);
36
- },
37
- NodeValue::Image(_) => |context, node, entering| {
38
- return render_image(context, node, entering);
39
- },
40
- NodeValue::List(_) => |context, node, entering| {
41
- return render_list(context, node, entering);
42
- },
43
- NodeValue::TaskItem(_) => |context, node, entering| {
44
- return render_task_item(context, node, entering);
45
- },
46
- NodeValue::Escaped => |context, node, entering| {
47
- return render_escaped(context, node, entering);
48
- },
49
- });
50
-
51
- fn render_image<'a>(
52
- context: &mut Context<RenderUserData>,
53
- node: &'a AstNode<'a>,
54
- entering: bool,
55
- ) -> Result<ChildRendering, fmt::Error> {
56
- let NodeValue::Image(ref nl) = node.data.borrow().value else {
57
- unreachable!()
58
- };
59
-
60
- if !(context.user.placeholder_detection && PLACEHOLDER_REGEX.is_match(&nl.url)) {
61
- return html::format_node_default(context, node, entering);
62
- }
63
-
64
- if entering {
65
- if context.options.render.figure_with_caption {
66
- context.write_str("<figure>")?;
67
- }
68
- context.write_str("<img")?;
69
- html::render_sourcepos(context, node)?;
70
- context.write_str(" src=\"")?;
71
- if context.options.render.r#unsafe || !html::dangerous_url(&nl.url) {
72
- if let Some(rewriter) = &context.options.extension.image_url_rewriter {
73
- context.escape_href(&rewriter.to_html(&nl.url))?;
74
- } else {
75
- context.escape_href(&nl.url)?;
76
- }
77
- }
78
-
79
- context.write_str("\"")?;
80
-
81
- if PLACEHOLDER_REGEX.is_match(&nl.url) {
82
- context.write_str(" data-placeholder")?;
83
- }
84
-
85
- context.write_str(" alt=\"")?;
86
-
87
- return Ok(ChildRendering::Plain);
88
- } else {
89
- if !nl.title.is_empty() {
90
- context.write_str("\" title=\"")?;
91
- context.escape(&nl.title)?;
92
- }
93
- context.write_str("\" />")?;
94
- if context.options.render.figure_with_caption {
95
- if !nl.title.is_empty() {
96
- context.write_str("<figcaption>")?;
97
- context.escape(&nl.title)?;
98
- context.write_str("</figcaption>")?;
99
- }
100
- context.write_str("</figure>")?;
101
- };
102
- }
103
-
104
- Ok(ChildRendering::HTML)
105
- }
106
-
107
- fn render_link<'a>(
108
- context: &mut Context<RenderUserData>,
109
- node: &'a AstNode<'a>,
110
- entering: bool,
111
- ) -> Result<ChildRendering, fmt::Error> {
112
- let NodeValue::Link(ref nl) = node.data.borrow().value else {
113
- unreachable!()
114
- };
115
-
116
- if !(context.user.placeholder_detection && PLACEHOLDER_REGEX.is_match(&nl.url)) {
117
- return html::format_node_default(context, node, entering);
118
- }
119
-
120
- let parent_node = node.parent();
121
-
122
- if !context.options.parse.relaxed_autolinks
123
- || (parent_node.is_none()
124
- || !matches!(
125
- parent_node.unwrap().data.borrow().value,
126
- NodeValue::Link(..)
127
- ))
128
- {
129
- if entering {
130
- context.write_str("<a")?;
131
- html::render_sourcepos(context, node)?;
132
- context.write_str(" href=\"")?;
133
- if context.options.render.r#unsafe || !html::dangerous_url(&nl.url) {
134
- if let Some(rewriter) = &context.options.extension.link_url_rewriter {
135
- context.escape_href(&rewriter.to_html(&nl.url))?;
136
- } else {
137
- context.escape_href(&nl.url)?;
138
- }
139
- }
140
- context.write_str("\"")?;
141
-
142
- if !nl.title.is_empty() {
143
- context.write_str(" title=\"")?;
144
- context.escape(&nl.title)?;
145
- }
146
-
147
- if PLACEHOLDER_REGEX.is_match(&nl.url) {
148
- context.write_str(" data-placeholder")?;
149
- }
150
-
151
- context.write_str(">")?;
152
- } else {
153
- context.write_str("</a>")?;
154
- }
155
- }
156
-
157
- Ok(ChildRendering::HTML)
158
- }
159
-
160
- // Overridden to use class `task-list` instead of `contains-task-list`
161
- // to align with GitLab class usage
162
- fn render_list<'a>(
163
- context: &mut Context<RenderUserData>,
164
- node: &'a AstNode<'a>,
165
- entering: bool,
166
- ) -> Result<ChildRendering, fmt::Error> {
167
- if !entering || !context.options.render.tasklist_classes {
168
- return html::format_node_default(context, node, entering);
169
- }
170
-
171
- let NodeValue::List(ref nl) = node.data.borrow().value else {
172
- unreachable!()
173
- };
174
-
175
- context.cr()?;
176
- match nl.list_type {
177
- ListType::Bullet => {
178
- context.write_str("<ul")?;
179
- if nl.is_task_list {
180
- context.write_str(" class=\"task-list\"")?;
181
- }
182
- html::render_sourcepos(context, node)?;
183
- context.write_str(">\n")?;
184
- }
185
- ListType::Ordered => {
186
- context.write_str("<ol")?;
187
- if nl.is_task_list {
188
- context.write_str(" class=\"task-list\"")?;
189
- }
190
- html::render_sourcepos(context, node)?;
191
- if nl.start == 1 {
192
- context.write_str(">\n")?;
193
- } else {
194
- writeln!(context, " start=\"{}\">", nl.start)?;
195
- }
196
- }
197
- }
198
-
199
- Ok(ChildRendering::HTML)
200
- }
201
-
202
- // Overridden to detect inapplicable task list items
203
- fn render_task_item<'a>(
204
- context: &mut Context<RenderUserData>,
205
- node: &'a AstNode<'a>,
206
- entering: bool,
207
- ) -> Result<ChildRendering, fmt::Error> {
208
- if !context.user.inapplicable_tasks {
209
- return html::format_node_default(context, node, entering);
210
- }
211
-
212
- let NodeValue::TaskItem(symbol) = node.data.borrow().value else {
213
- unreachable!()
214
- };
215
-
216
- if symbol.is_none() || matches!(symbol, Some('x' | 'X')) {
217
- return html::format_node_default(context, node, entering);
218
- }
219
-
220
- if entering {
221
- // Handle an inapplicable task symbol.
222
- if matches!(symbol, Some('~')) {
223
- context.cr()?;
224
- context.write_str("<li")?;
225
- context.write_str(" class=\"inapplicable")?;
226
-
227
- if context.options.render.tasklist_classes {
228
- context.write_str(" task-list-item")?;
229
- }
230
- context.write_str("\"")?;
231
-
232
- html::render_sourcepos(context, node)?;
233
- context.write_str(">")?;
234
- context.write_str("<input type=\"checkbox\"")?;
235
-
236
- if context.options.render.tasklist_classes {
237
- context.write_str(" class=\"task-list-item-checkbox\"")?;
238
- }
239
-
240
- context.write_str(" data-inapplicable disabled=\"\"> ")?;
241
- } else {
242
- // Don't allow unsupported symbols to render a checkbox
243
- context.cr()?;
244
- context.write_str("<li")?;
245
-
246
- if context.options.render.tasklist_classes {
247
- context.write_str(" class=\"task-list-item\"")?;
248
- }
249
-
250
- html::render_sourcepos(context, node)?;
251
- context.write_str(">")?;
252
- context.write_str("[")?;
253
- context.escape(&symbol.unwrap().to_string())?;
254
- context.write_str("] ")?;
255
- }
256
- } else {
257
- context.write_str("</li>\n")?;
258
- }
259
-
260
- Ok(ChildRendering::HTML)
261
- }
262
-
263
- fn render_text<'a>(
264
- context: &mut Context<RenderUserData>,
265
- node: &'a AstNode<'a>,
266
- entering: bool,
267
- ) -> Result<ChildRendering, fmt::Error> {
268
- let NodeValue::Text(ref literal) = node.data.borrow().value else {
269
- unreachable!()
270
- };
271
-
272
- if !(context.user.placeholder_detection && PLACEHOLDER_REGEX.is_match(literal)) {
273
- return html::format_node_default(context, node, entering);
274
- }
275
-
276
- // Don't currently support placeholders in the text inside links or images.
277
- // If the text has an underscore in it, then the parser will not combine
278
- // the multiple text nodes in `comrak`'s `postprocess_text_nodes`, breaking up
279
- // the placeholder into multiple text nodes.
280
- // For example, `[%{a_b}](link)`.
281
- let parent = node.parent().unwrap();
282
- if matches!(
283
- parent.data.borrow().value,
284
- NodeValue::Link(_) | NodeValue::Image(_)
285
- ) {
286
- return html::format_node_default(context, node, entering);
287
- }
288
-
289
- if entering {
290
- let mut cursor: usize = 0;
291
-
292
- for mat in PLACEHOLDER_REGEX.find_iter(literal) {
293
- if mat.start() > cursor {
294
- context.escape(&literal[cursor..mat.start()])?;
295
- }
296
-
297
- context.write_str("<span data-placeholder>")?;
298
- context.escape(&literal[mat.start()..mat.end()])?;
299
- context.write_str("</span>")?;
300
-
301
- cursor = mat.end();
302
- }
303
-
304
- if cursor < literal.len() {
305
- context.escape(&literal[cursor..literal.len()])?;
306
- }
307
- }
308
-
309
- Ok(ChildRendering::HTML)
310
- }
311
-
312
- fn render_escaped<'a>(
313
- context: &mut Context<RenderUserData>,
314
- node: &'a AstNode<'a>,
315
- entering: bool,
316
- ) -> Result<ChildRendering, fmt::Error> {
317
- if !context.options.render.escaped_char_spans {
318
- return Ok(ChildRendering::HTML);
319
- }
320
-
321
- if context.user.only_escape_chars.is_none()
322
- || with_node_text_content(node, false, |content| {
323
- if content.len() != 1 {
324
- return false;
325
- }
326
- let c = content.chars().next().unwrap();
327
- context
328
- .user
329
- .only_escape_chars
330
- .as_ref()
331
- .unwrap()
332
- .contains(&c)
333
- })
334
- {
335
- if entering {
336
- context.write_str("<span data-escaped-char")?;
337
- render_sourcepos(context, node)?;
338
- context.write_str(">")?;
339
- } else {
340
- context.write_str("</span>")?;
341
- }
342
- }
343
-
344
- Ok(ChildRendering::HTML)
345
- }
346
-
347
- /// If the given node has a single text child, apply a function to the text content
348
- /// of that node. Otherwise, return the given default value.
349
- fn with_node_text_content<'a, U, F>(node: &'a AstNode<'a>, default: U, f: F) -> U
350
- where
351
- F: FnOnce(&str) -> U,
352
- {
353
- let Some(child) = node.first_child() else {
354
- return default;
355
- };
356
- let Some(last_child) = node.last_child() else {
357
- return default;
358
- };
359
- if !child.same_node(last_child) {
360
- return default;
361
- }
362
- let NodeValue::Text(ref text) = child.data.borrow().value else {
363
- return default;
364
- };
365
- f(text)
366
- }
Binary file