gitlab-glfm-markdown 0.0.32 → 0.0.33
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.
- checksums.yaml +4 -4
- data/Cargo.lock +3 -3
- data/README.md +40 -38
- data/ext/glfm_markdown/Cargo.toml +2 -2
- data/ext/glfm_markdown/src/glfm.rs +118 -1
- data/ext/glfm_markdown/src/lib.rs +2 -0
- data/ext/glfm_markdown/src/main.rs +12 -0
- data/lib/glfm_markdown/version.rb +1 -1
- data/lib/glfm_markdown.rb +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb8448f904c8d824f877414066a5958e72eda7bd405af187c8093ffb88c1763c
|
4
|
+
data.tar.gz: 26244206863034e94f575db9678c4a97bae736ce8010fc49c849142d8648fdfc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf82fe75c037f4a8e22015f4bcddb1fd45d9ebdb08640322a0770b16a1d4cedae41008c318213e2ed2292dfbf240102fcf09484f0b20abf34c48685f6cd6bbd5
|
7
|
+
data.tar.gz: 7dcee4806c5acecf184bad92b2cdfc980a114cc84ab05bb31a22db34c51b3f059cd6268fc65f13a9f73a15eec7c95c537870054d420bb9ba77751ab9eb95ff6e
|
data/Cargo.lock
CHANGED
@@ -227,9 +227,9 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
|
227
227
|
|
228
228
|
[[package]]
|
229
229
|
name = "comrak"
|
230
|
-
version = "0.
|
230
|
+
version = "0.40.0"
|
231
231
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
232
|
-
checksum = "
|
232
|
+
checksum = "32c3278f396e5707769a68bc0943e9b8f84a172836b173827810918279621747"
|
233
233
|
dependencies = [
|
234
234
|
"caseless",
|
235
235
|
"emojis",
|
@@ -320,7 +320,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|
320
320
|
|
321
321
|
[[package]]
|
322
322
|
name = "glfm_markdown"
|
323
|
-
version = "0.0.
|
323
|
+
version = "0.0.33"
|
324
324
|
dependencies = [
|
325
325
|
"clap",
|
326
326
|
"comrak",
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Implements GLFM (as used by GitLab) using the Rust-based markdown parser [comrak](https://github.com/kivikakk/comrak)
|
7
7
|
and providing a Ruby interface.\
|
8
|
-
_Currently using `comrak 0.
|
8
|
+
_Currently using `comrak 0.40.0`_.
|
9
9
|
|
10
10
|
This project is still in constant flux, so interfaces and functionality can change at any time.
|
11
11
|
|
@@ -32,43 +32,45 @@ GLFMMarkdown.to_html('# header', options: { sourcepos: true })
|
|
32
32
|
|
33
33
|
### Options
|
34
34
|
|
35
|
-
| Option name
|
36
|
-
|
37
|
-
| `autolink`
|
38
|
-
| `description_lists`
|
39
|
-
| `escape`
|
40
|
-
| `escape_char_spans`
|
41
|
-
| `figure_with_caption`
|
42
|
-
| `footnotes`
|
43
|
-
| `full_info_string`
|
44
|
-
| `gemojis`
|
45
|
-
| `gfm_quirks`
|
46
|
-
| `github_pre_lang`
|
47
|
-
| `greentext`
|
48
|
-
| `hardbreaks`
|
49
|
-
| `header_ids <PREFIX>`
|
50
|
-
| `ignore_empty_links`
|
51
|
-
| `ignore_setext`
|
52
|
-
| `
|
53
|
-
| `
|
54
|
-
| `
|
55
|
-
| `
|
56
|
-
| `
|
57
|
-
| `
|
58
|
-
| `
|
59
|
-
| `
|
60
|
-
| `
|
61
|
-
| `
|
62
|
-
| `
|
63
|
-
| `
|
64
|
-
| `
|
65
|
-
| `
|
66
|
-
| `
|
67
|
-
| `
|
68
|
-
| `
|
69
|
-
| `
|
70
|
-
| `
|
71
|
-
| `
|
35
|
+
| Option name | Description |
|
36
|
+
|-------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
37
|
+
| `autolink` | Enable the `autolink` extension |
|
38
|
+
| `description_lists` | Enable the `description-lists` extension |
|
39
|
+
| `escape` | Escape raw HTML instead of clobbering it |
|
40
|
+
| `escape_char_spans` | Wrap escaped characters in a `<span>` to allow any post-processing to recognize them |
|
41
|
+
| `figure_with_caption` | Render the image as a figure element with the title as its caption |
|
42
|
+
| `footnotes` | Enable the `footnotes` extension |
|
43
|
+
| `full_info_string` | Enable full info strings for code blocks |
|
44
|
+
| `gemojis` | Enable the `gemojis` extensions - translate gemojis into UTF-8 characters |
|
45
|
+
| `gfm_quirks` | Enables GFM-style quirks in output HTML, such as not nesting <strong> tags |
|
46
|
+
| `github_pre_lang` | Use GitHub-style `<pre lang>` for code blocks |
|
47
|
+
| `greentext` | Enable the `greentext` extension - requires at least one space after a `>` character to generate a blockquote, and restarts blockquote nesting across unique lines of input |
|
48
|
+
| `hardbreaks` | Treat newlines as hard line breaks |
|
49
|
+
| `header_ids <PREFIX>` | Enable the `header-id` extension, with the given ID prefix |
|
50
|
+
| `ignore_empty_links` | Ignore empty links in input |
|
51
|
+
| `ignore_setext` | Ignore setext headings in input |
|
52
|
+
| `inapplicable_tasks` | Allow inapplicable tasks items. Must also set the `relaxed_tasklist_character` option. |
|
53
|
+
| `math_code` | Enables `math code` extension, using math code syntax |
|
54
|
+
| `math_dollars` | Enables `math dollars` extension, using math dollar syntax |
|
55
|
+
| `multiline_block_quotes` | Enable the `multiline-block-quotes` extension |
|
56
|
+
| `default_html` | Disables any custom HTML, and returns default HTML from `comrak` |
|
57
|
+
| `placeholder_detection` | Detect placeholder variables in the format `%{placeholder}` |
|
58
|
+
| `relaxed_autolinks` | Enable relaxing of autolink parsing, allowing links to be recognized when in brackets |
|
59
|
+
| `relaxed_tasklist_character` | Enable relaxing which character is allowed in tasklists |
|
60
|
+
| `sourcepos` | Include source mappings in HTML attributes |
|
61
|
+
| `smart` | Use smart punctuation |
|
62
|
+
| `spoiler` | Enable the `spoiler` extension - use double vertical bars |
|
63
|
+
| `strikethrough` | Enable the `strikethrough` extension |
|
64
|
+
| `superscript` | Enable the `superscript` extension |
|
65
|
+
| `table` | Enable the `table` extension |
|
66
|
+
| `tagfilter` | Enable the `tagfilter` extension |
|
67
|
+
| `tasklist` | Enable the `tasklist` extension |
|
68
|
+
| `tasklist_classes` | Output classes on tasklist elements so that they can be styled with CSS |
|
69
|
+
| `underline` | Enables the `underline` extension - use double underscores |
|
70
|
+
| `unsafe` | Allow raw HTML and dangerous URLs |
|
71
|
+
| `wikilinks_title_after_pipe` | Enable the `wikilinks_title_after_pipe` extension |
|
72
|
+
| `wikilinks_title_before_pipe` | Enable the `wikilinks_title_before_pipe` extension |
|
73
|
+
| `debug` | Show debug information |
|
72
74
|
|
73
75
|
## Dingus / Demo
|
74
76
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[package]
|
2
2
|
name = "glfm_markdown"
|
3
|
-
version = "0.0.
|
3
|
+
version = "0.0.33"
|
4
4
|
edition = "2021"
|
5
5
|
authors = ["digitalmoksha <bwalker@gitlab.com>"]
|
6
6
|
description = "GitLab Flavored Markdown parser and formatter. 100% CommonMark-compatible. Experimental."
|
@@ -15,7 +15,7 @@ required-features = ["cli"]
|
|
15
15
|
|
16
16
|
[dependencies]
|
17
17
|
clap = { version = "=4.4.18", optional = true, features = ["derive", "string"] }
|
18
|
-
comrak = { version = "0.
|
18
|
+
comrak = { version = "0.40.0", default-features = false, features = ["shortcodes"] }
|
19
19
|
magnus = "0.6.2"
|
20
20
|
rb-sys = { version = "0.9.86", default-features = false, features = ["stable-api-compiled-fallback"] }
|
21
21
|
regex = "1.11.1"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
use comrak::html::{ChildRendering, Context};
|
2
|
-
use comrak::nodes::{AstNode, NodeValue};
|
2
|
+
use comrak::nodes::{AstNode, ListType, NodeValue};
|
3
3
|
use comrak::{create_formatter, html, parse_document, Arena, Plugins};
|
4
4
|
use lazy_static::lazy_static;
|
5
5
|
use regex::Regex;
|
@@ -45,14 +45,20 @@ pub struct RenderOptions {
|
|
45
45
|
pub table: bool,
|
46
46
|
pub tagfilter: bool,
|
47
47
|
pub tasklist: bool,
|
48
|
+
pub tasklist_classes: bool,
|
48
49
|
pub underline: bool,
|
49
50
|
pub unsafe_: bool,
|
50
51
|
pub wikilinks_title_after_pipe: bool,
|
51
52
|
pub wikilinks_title_before_pipe: bool,
|
52
53
|
|
54
|
+
/// GLFM specific options
|
55
|
+
|
53
56
|
/// Only use default comrak HTML formatting
|
54
57
|
pub default_html: bool,
|
55
58
|
|
59
|
+
/// Detect inapplicable tasks (`- [~]`)
|
60
|
+
pub inapplicable_tasks: bool,
|
61
|
+
|
56
62
|
/// Detect and mark potential placeholder variables, which
|
57
63
|
/// have the format `%{PLACEHOLDER}`
|
58
64
|
pub placeholder_detection: bool,
|
@@ -62,6 +68,7 @@ pub struct RenderOptions {
|
|
62
68
|
|
63
69
|
pub struct RenderUserData {
|
64
70
|
pub default_html: bool,
|
71
|
+
pub inapplicable_tasks: bool,
|
65
72
|
pub placeholder_detection: bool,
|
66
73
|
pub debug: bool,
|
67
74
|
}
|
@@ -70,6 +77,7 @@ impl From<&RenderOptions> for RenderUserData {
|
|
70
77
|
fn from(options: &RenderOptions) -> Self {
|
71
78
|
RenderUserData {
|
72
79
|
default_html: options.default_html,
|
80
|
+
inapplicable_tasks: options.inapplicable_tasks,
|
73
81
|
placeholder_detection: options.placeholder_detection,
|
74
82
|
debug: options.debug,
|
75
83
|
}
|
@@ -112,6 +120,7 @@ impl From<&RenderOptions> for comrak::Options<'_> {
|
|
112
120
|
comrak_options.render.ignore_empty_links = options.ignore_empty_links;
|
113
121
|
comrak_options.render.ignore_setext = options.ignore_setext;
|
114
122
|
comrak_options.render.sourcepos = options.sourcepos;
|
123
|
+
comrak_options.render.tasklist_classes = options.tasklist_classes;
|
115
124
|
// comrak_options.render.syntax_highlighting = options.syntax_highlighting;
|
116
125
|
|
117
126
|
comrak_options.render.unsafe_ = options.unsafe_;
|
@@ -162,6 +171,12 @@ create_formatter!(CustomFormatter<RenderUserData>, {
|
|
162
171
|
},
|
163
172
|
NodeValue::Image(_) => |context, node, entering| {
|
164
173
|
return render_image(context, node, entering);
|
174
|
+
},
|
175
|
+
NodeValue::List(_) => |context, node, entering| {
|
176
|
+
return render_list(context, node, entering);
|
177
|
+
},
|
178
|
+
NodeValue::TaskItem(_) => |context, node, entering| {
|
179
|
+
return render_task_item(context, node, entering);
|
165
180
|
}
|
166
181
|
});
|
167
182
|
|
@@ -276,6 +291,108 @@ fn render_link<'a>(
|
|
276
291
|
Ok(ChildRendering::HTML)
|
277
292
|
}
|
278
293
|
|
294
|
+
// Overridden to use class `task-list` instead of `contains-task-list`
|
295
|
+
// to align with GitLab class usage
|
296
|
+
fn render_list<'a>(
|
297
|
+
context: &mut Context<RenderUserData>,
|
298
|
+
node: &'a AstNode<'a>,
|
299
|
+
entering: bool,
|
300
|
+
) -> io::Result<ChildRendering> {
|
301
|
+
if !entering || !context.options.render.tasklist_classes {
|
302
|
+
return html::format_node_default(context, node, entering);
|
303
|
+
}
|
304
|
+
|
305
|
+
let NodeValue::List(ref nl) = node.data.borrow().value else {
|
306
|
+
panic!("Attempt to render invalid node as list")
|
307
|
+
};
|
308
|
+
|
309
|
+
context.cr()?;
|
310
|
+
match nl.list_type {
|
311
|
+
ListType::Bullet => {
|
312
|
+
context.write_all(b"<ul")?;
|
313
|
+
if nl.is_task_list {
|
314
|
+
context.write_all(b" class=\"task-list\"")?;
|
315
|
+
}
|
316
|
+
html::render_sourcepos(context, node)?;
|
317
|
+
context.write_all(b">\n")?;
|
318
|
+
}
|
319
|
+
ListType::Ordered => {
|
320
|
+
context.write_all(b"<ol")?;
|
321
|
+
if nl.is_task_list {
|
322
|
+
context.write_all(b" class=\"task-list\"")?;
|
323
|
+
}
|
324
|
+
html::render_sourcepos(context, node)?;
|
325
|
+
if nl.start == 1 {
|
326
|
+
context.write_all(b">\n")?;
|
327
|
+
} else {
|
328
|
+
writeln!(context, " start=\"{}\">", nl.start)?;
|
329
|
+
}
|
330
|
+
}
|
331
|
+
}
|
332
|
+
|
333
|
+
Ok(ChildRendering::HTML)
|
334
|
+
}
|
335
|
+
|
336
|
+
// Overridden to detect inapplicable task list items
|
337
|
+
fn render_task_item<'a>(
|
338
|
+
context: &mut Context<RenderUserData>,
|
339
|
+
node: &'a AstNode<'a>,
|
340
|
+
entering: bool,
|
341
|
+
) -> io::Result<ChildRendering> {
|
342
|
+
if !context.user.inapplicable_tasks {
|
343
|
+
return html::format_node_default(context, node, entering);
|
344
|
+
}
|
345
|
+
|
346
|
+
let NodeValue::TaskItem(symbol) = node.data.borrow().value else {
|
347
|
+
panic!("Attempt to render invalid node as task item")
|
348
|
+
};
|
349
|
+
|
350
|
+
if symbol.is_none() || matches!(symbol, Some('x' | 'X')) {
|
351
|
+
return html::format_node_default(context, node, entering);
|
352
|
+
}
|
353
|
+
|
354
|
+
if entering {
|
355
|
+
// Handle an inapplicable task symbol.
|
356
|
+
if matches!(symbol, Some('~')) {
|
357
|
+
context.cr()?;
|
358
|
+
context.write_all(b"<li")?;
|
359
|
+
context.write_all(b" class=\"inapplicable")?;
|
360
|
+
|
361
|
+
if context.options.render.tasklist_classes {
|
362
|
+
context.write_all(b" task-list-item")?;
|
363
|
+
}
|
364
|
+
context.write_all(b"\"")?;
|
365
|
+
|
366
|
+
html::render_sourcepos(context, node)?;
|
367
|
+
context.write_all(b">")?;
|
368
|
+
context.write_all(b"<input type=\"checkbox\"")?;
|
369
|
+
|
370
|
+
if context.options.render.tasklist_classes {
|
371
|
+
context.write_all(b" class=\"task-list-item-checkbox\"")?;
|
372
|
+
}
|
373
|
+
|
374
|
+
context.write_all(b" data-inapplicable disabled=\"\"> ")?;
|
375
|
+
} else {
|
376
|
+
// Don't allow unsupported symbols to render a checkbox
|
377
|
+
context.cr()?;
|
378
|
+
context.write_all(b"<li")?;
|
379
|
+
|
380
|
+
if context.options.render.tasklist_classes {
|
381
|
+
context.write_all(b" class=\"task-list-item\"")?;
|
382
|
+
}
|
383
|
+
|
384
|
+
html::render_sourcepos(context, node)?;
|
385
|
+
context.write_all(b">")?;
|
386
|
+
context.write_all(b"[")?;
|
387
|
+
context.escape(symbol.unwrap().to_string().as_bytes())?;
|
388
|
+
context.write_all(b"] ")?;
|
389
|
+
}
|
390
|
+
} else {
|
391
|
+
context.write_all(b"</li>\n")?;
|
392
|
+
}
|
393
|
+
|
394
|
+
Ok(ChildRendering::HTML)
|
395
|
+
}
|
279
396
|
fn render_text<'a>(
|
280
397
|
context: &mut Context<RenderUserData>,
|
281
398
|
node: &'a AstNode<'a>,
|
@@ -33,6 +33,7 @@ pub fn render_to_html_rs(text: String, options: RHash) -> String {
|
|
33
33
|
header_ids: get_string_opt("header_ids", options),
|
34
34
|
ignore_empty_links: get_bool_opt("ignore_empty_links", options),
|
35
35
|
ignore_setext: get_bool_opt("ignore_setext", options),
|
36
|
+
inapplicable_tasks: get_bool_opt("inapplicable_tasks", options),
|
36
37
|
math_code: get_bool_opt("math_code", options),
|
37
38
|
math_dollars: get_bool_opt("math_dollars", options),
|
38
39
|
multiline_block_quotes: get_bool_opt("multiline_block_quotes", options),
|
@@ -48,6 +49,7 @@ pub fn render_to_html_rs(text: String, options: RHash) -> String {
|
|
48
49
|
table: get_bool_opt("table", options),
|
49
50
|
tagfilter: get_bool_opt("tagfilter", options),
|
50
51
|
tasklist: get_bool_opt("tasklist", options),
|
52
|
+
tasklist_classes: get_bool_opt("tasklist_classes", options),
|
51
53
|
underline: get_bool_opt("underline", options),
|
52
54
|
unsafe_: get_bool_opt("unsafe", options),
|
53
55
|
wikilinks_title_after_pipe: get_bool_opt("wikilinks_title_after_pipe", options),
|
@@ -146,6 +146,10 @@ struct Args {
|
|
146
146
|
#[arg(long)]
|
147
147
|
tasklist: bool,
|
148
148
|
|
149
|
+
/// Output classes on tasklist elements so that they can be styled with CSS
|
150
|
+
#[arg(long)]
|
151
|
+
tasklist_clases: bool,
|
152
|
+
|
149
153
|
/// Enables underlines using double underscores
|
150
154
|
#[arg(long)]
|
151
155
|
underline: bool,
|
@@ -166,6 +170,12 @@ struct Args {
|
|
166
170
|
#[arg(long)]
|
167
171
|
default_html: bool,
|
168
172
|
|
173
|
+
/// GLFM specific options
|
174
|
+
|
175
|
+
/// Detect inapplicable tasks (`- [~]`)
|
176
|
+
#[arg(long)]
|
177
|
+
inapplicable_tasks: bool,
|
178
|
+
|
169
179
|
/// Detect and marks potential placeholder variables, which
|
170
180
|
/// have the format `%{PLACEHOLDER}`
|
171
181
|
#[arg(long)]
|
@@ -209,6 +219,7 @@ fn main() {
|
|
209
219
|
header_ids: cli.header_ids,
|
210
220
|
ignore_empty_links: cli.ignore_empty_links,
|
211
221
|
ignore_setext: cli.ignore_setext,
|
222
|
+
inapplicable_tasks: cli.inapplicable_tasks,
|
212
223
|
math_code: cli.math_code,
|
213
224
|
math_dollars: cli.math_dollars,
|
214
225
|
multiline_block_quotes: cli.multiline_block_quotes,
|
@@ -224,6 +235,7 @@ fn main() {
|
|
224
235
|
table: cli.table,
|
225
236
|
tagfilter: cli.tagfilter,
|
226
237
|
tasklist: cli.tasklist,
|
238
|
+
tasklist_classes: cli.tasklist_classes,
|
227
239
|
underline: cli.underline,
|
228
240
|
unsafe_: cli.unsafe_,
|
229
241
|
wikilinks_title_after_pipe: cli.wikilinks_title_after_pipe,
|
data/lib/glfm_markdown.rb
CHANGED
@@ -14,6 +14,7 @@ module GLFMMarkdown
|
|
14
14
|
gfm_quirks: true,
|
15
15
|
github_pre_lang: false,
|
16
16
|
hardbreaks: false,
|
17
|
+
inapplicable_tasks: false,
|
17
18
|
math_code: false,
|
18
19
|
math_dollars: false,
|
19
20
|
multiline_block_quotes: true,
|
@@ -24,6 +25,7 @@ module GLFMMarkdown
|
|
24
25
|
table: true,
|
25
26
|
tagfilter: false,
|
26
27
|
tasklist: true,
|
28
|
+
tasklist_classes: true,
|
27
29
|
unsafe: true,
|
28
30
|
|
29
31
|
debug: false
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab-glfm-markdown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.33
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Walker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-07-
|
11
|
+
date: 2025-07-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rb_sys
|