gitlab-glfm-markdown 0.0.28-x86_64-darwin → 0.0.30-x86_64-darwin

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5cb63aead83dc5200db35df1620042ce03c57e6ac8e353aad8650cecb78c51c8
4
- data.tar.gz: 3ce29549555f4ad8a1f0551eab726a192548ec30e687b5b975dac502169ce6c4
3
+ metadata.gz: abcc42d894a05f51058f3eb3dffa3dea2bbd56443c0a404cb1d828cc7fe2a52c
4
+ data.tar.gz: 1179ff284552a99fb9834d7158e43806b398b23b5fc3e50ce0a0e56a2bee9149
5
5
  SHA512:
6
- metadata.gz: 569aafedfd5e5c666835c16376188955551922af91aa74684b2319ddf57c9f151a7ef3a69cf8dd3e4ca3bf6e962be7c5cab079922d096992cff03019aa2b78b5
7
- data.tar.gz: 911a589d5686171863b624f8ad7411ddea3618507c6ad76e496619fab24f9000e8b6e58574b32f9fd7b5e4eca55a345805eae748d2445d41f76025adc0afa52b
6
+ metadata.gz: d69ce9afafa104bbc2ddcaee97a0aaed86505c99b3e97a0caeb41ffdbf735df21f723cea1cee7ec081bdcb8dd1fcb95937d1a6fd9a56ec3471458c0133454e76
7
+ data.tar.gz: 51abd246fa56b42ffcd3fce1c9f6beb877aa9c71968a9820f0e9f57223c8ce97bad5643b1755a7a040116f157b2f8ed166f5886e555eba524eca4c127016297d
data/Cargo.lock CHANGED
@@ -181,9 +181,9 @@ dependencies = [
181
181
 
182
182
  [[package]]
183
183
  name = "clap"
184
- version = "4.5.27"
184
+ version = "4.4.18"
185
185
  source = "registry+https://github.com/rust-lang/crates.io-index"
186
- checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796"
186
+ checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
187
187
  dependencies = [
188
188
  "clap_builder",
189
189
  "clap_derive",
@@ -191,9 +191,9 @@ dependencies = [
191
191
 
192
192
  [[package]]
193
193
  name = "clap_builder"
194
- version = "4.5.27"
194
+ version = "4.4.18"
195
195
  source = "registry+https://github.com/rust-lang/crates.io-index"
196
- checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
196
+ checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
197
197
  dependencies = [
198
198
  "anstream",
199
199
  "anstyle",
@@ -203,9 +203,9 @@ dependencies = [
203
203
 
204
204
  [[package]]
205
205
  name = "clap_derive"
206
- version = "4.5.24"
206
+ version = "4.4.7"
207
207
  source = "registry+https://github.com/rust-lang/crates.io-index"
208
- checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
208
+ checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
209
209
  dependencies = [
210
210
  "heck",
211
211
  "proc-macro2",
@@ -215,9 +215,9 @@ dependencies = [
215
215
 
216
216
  [[package]]
217
217
  name = "clap_lex"
218
- version = "0.7.4"
218
+ version = "0.6.0"
219
219
  source = "registry+https://github.com/rust-lang/crates.io-index"
220
- checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
220
+ checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
221
221
 
222
222
  [[package]]
223
223
  name = "colorchoice"
@@ -227,9 +227,9 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
227
227
 
228
228
  [[package]]
229
229
  name = "comrak"
230
- version = "0.36.0"
230
+ version = "0.39.0"
231
231
  source = "registry+https://github.com/rust-lang/crates.io-index"
232
- checksum = "5afa2702ef2fecc5bd7ca605f37e875a6be3fc8138c4633e711a945b70351550"
232
+ checksum = "d5c834ca54c5a20588b358f34d1533b4b498ddb5fd979cec6b22d0e8867a2449"
233
233
  dependencies = [
234
234
  "caseless",
235
235
  "emojis",
@@ -320,12 +320,14 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
320
320
 
321
321
  [[package]]
322
322
  name = "glfm_markdown"
323
- version = "0.0.28"
323
+ version = "0.0.30"
324
324
  dependencies = [
325
325
  "clap",
326
326
  "comrak",
327
+ "lazy_static",
327
328
  "magnus",
328
329
  "rb-sys",
330
+ "regex",
329
331
  ]
330
332
 
331
333
  [[package]]
@@ -342,9 +344,9 @@ checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
342
344
 
343
345
  [[package]]
344
346
  name = "heck"
345
- version = "0.5.0"
347
+ version = "0.4.1"
346
348
  source = "registry+https://github.com/rust-lang/crates.io-index"
347
- checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
349
+ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
348
350
 
349
351
  [[package]]
350
352
  name = "indexmap"
@@ -729,9 +731,9 @@ dependencies = [
729
731
 
730
732
  [[package]]
731
733
  name = "strsim"
732
- version = "0.11.1"
734
+ version = "0.10.0"
733
735
  source = "registry+https://github.com/rust-lang/crates.io-index"
734
- checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
736
+ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
735
737
 
736
738
  [[package]]
737
739
  name = "syn"
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.36.0`_.
8
+ _Currently using `comrak 0.39.0`_.
9
9
 
10
10
  This project is still in constant flux, so interfaces and functionality can change at any time.
11
11
 
@@ -32,42 +32,43 @@ GLFMMarkdown.to_html('# header', options: { sourcepos: true })
32
32
 
33
33
  ### Options
34
34
 
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
- | `math_code` | Enables `math code` extension, using math code syntax |
53
- | `math_dollars` | Enables `math dollars` extension, using math dollar syntax |
54
- | `multiline_block_quotes` | Enable the `multiline-block-quotes` extension |
55
- | `relaxed_autolinks` | Enable relaxing of autolink parsing, allowing links to be recognized when in brackets |
56
- | `relaxed_tasklist_character` | Enable relaxing which character is allowed in tasklists |
57
- | `sourcepos` | Include source mappings in HTML attributes |
58
- | `experimental_inline_sourcepos` | Include inline sourcepos in HTML output, which is known to have issues |
59
- | `smart` | Use smart punctuation |
60
- | `spoiler` | Enable the `spoiler` extension - use double vertical bars |
61
- | `strikethrough` | Enable the `strikethrough` extension |
62
- | `superscript` | Enable the `superscript` extension |
63
- | `table` | Enable the `table` extension |
64
- | `tagfilter` | Enable the `tagfilter` extension |
65
- | `tasklist` | Enable the `tasklist` extension |
66
- | `underline` | Enables the `underline` extension - use double underscores |
67
- | `unsafe` | Allow raw HTML and dangerous URLs |
68
- | `wikilinks_title_after_pipe` | Enable the `wikilinks_title_after_pipe` extension |
69
- | `wikilinks_title_before_pipe` | Enable the `wikilinks_title_before_pipe` extension |
70
- | `debug` | Show debug information |
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
+ | `math_code` | Enables `math code` extension, using math code syntax |
53
+ | `math_dollars` | Enables `math dollars` extension, using math dollar syntax |
54
+ | `multiline_block_quotes` | Enable the `multiline-block-quotes` extension |
55
+ | `default_html` | Disables any custom HTML, and returns default HTML from `comrak` |
56
+ | `placeholder_detection` | Detect placeholder variables in the format `%{placeholder}` |
57
+ | `relaxed_autolinks` | Enable relaxing of autolink parsing, allowing links to be recognized when in brackets |
58
+ | `relaxed_tasklist_character` | Enable relaxing which character is allowed in tasklists |
59
+ | `sourcepos` | Include source mappings in HTML attributes |
60
+ | `smart` | Use smart punctuation |
61
+ | `spoiler` | Enable the `spoiler` extension - use double vertical bars |
62
+ | `strikethrough` | Enable the `strikethrough` extension |
63
+ | `superscript` | Enable the `superscript` extension |
64
+ | `table` | Enable the `table` extension |
65
+ | `tagfilter` | Enable the `tagfilter` extension |
66
+ | `tasklist` | Enable the `tasklist` extension |
67
+ | `underline` | Enables the `underline` extension - use double underscores |
68
+ | `unsafe` | Allow raw HTML and dangerous URLs |
69
+ | `wikilinks_title_after_pipe` | Enable the `wikilinks_title_after_pipe` extension |
70
+ | `wikilinks_title_before_pipe` | Enable the `wikilinks_title_before_pipe` extension |
71
+ | `debug` | Show debug information |
71
72
 
72
73
  ## Dingus / Demo
73
74
 
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "glfm_markdown"
3
- version = "0.0.28"
3
+ version = "0.0.30"
4
4
  edition = "2021"
5
5
  authors = ["digitalmoksha <bwalker@gitlab.com>"]
6
6
  description = "GitLab Flavored Markdown parser and formatter. 100% CommonMark-compatible. Experimental."
@@ -14,10 +14,12 @@ name = "glfm_markdown"
14
14
  required-features = ["cli"]
15
15
 
16
16
  [dependencies]
17
- clap = { version = "4.0", optional = true, features = ["derive", "string"] }
18
- comrak = { version = "0.36.0", default-features = false, features = ["shortcodes"] }
17
+ clap = { version = "=4.4.18", optional = true, features = ["derive", "string"] }
18
+ comrak = { version = "0.39.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
+ regex = "1.11.1"
22
+ lazy_static = "1.5.0"
21
23
 
22
24
  [features]
23
25
  cli = ["clap", "comrak/syntect"]
@@ -1,4 +1,16 @@
1
- #[derive(Debug)]
1
+ use comrak::html::{ChildRendering, Context};
2
+ use comrak::nodes::{AstNode, NodeValue};
3
+ use comrak::{create_formatter, html, parse_document, Arena, Plugins};
4
+ use lazy_static::lazy_static;
5
+ use regex::Regex;
6
+ use std::io;
7
+ use std::io::{BufWriter, Write};
8
+
9
+ lazy_static! {
10
+ static ref PLACEHOLDER_REGEX: Regex = Regex::new(r"%(\{|%7B)(\w{1,30})(}|%7D)").unwrap();
11
+ }
12
+
13
+ #[derive(Debug, Clone)]
2
14
  pub struct RenderOptions {
3
15
  pub alerts: bool,
4
16
  pub autolink: bool,
@@ -24,7 +36,6 @@ pub struct RenderOptions {
24
36
  pub relaxed_autolinks: bool,
25
37
  pub relaxed_tasklist_character: bool,
26
38
  pub sourcepos: bool,
27
- pub experimental_inline_sourcepos: bool,
28
39
  pub smart: bool,
29
40
  pub spoiler: bool,
30
41
  pub strikethrough: bool,
@@ -39,57 +50,277 @@ pub struct RenderOptions {
39
50
  pub wikilinks_title_after_pipe: bool,
40
51
  pub wikilinks_title_before_pipe: bool,
41
52
 
53
+ /// Only use default comrak HTML formatting
54
+ pub default_html: bool,
55
+
56
+ /// Detect and mark potential placeholder variables, which
57
+ /// have the format `%{PLACEHOLDER}`
58
+ pub placeholder_detection: bool,
59
+
42
60
  pub debug: bool,
43
61
  }
44
62
 
63
+ pub struct RenderUserData {
64
+ pub default_html: bool,
65
+ pub placeholder_detection: bool,
66
+ pub debug: bool,
67
+ }
68
+
69
+ impl From<&RenderOptions> for RenderUserData {
70
+ fn from(options: &RenderOptions) -> Self {
71
+ RenderUserData {
72
+ default_html: options.default_html,
73
+ placeholder_detection: options.placeholder_detection,
74
+ debug: options.debug,
75
+ }
76
+ }
77
+ }
78
+
79
+ impl From<&RenderOptions> for comrak::Options<'_> {
80
+ fn from(options: &RenderOptions) -> Self {
81
+ let mut comrak_options = comrak::Options::default();
82
+
83
+ comrak_options.extension.alerts = options.alerts;
84
+ comrak_options.extension.autolink = options.autolink;
85
+ comrak_options.extension.description_lists = options.description_lists;
86
+ comrak_options.extension.footnotes = options.footnotes;
87
+ // comrak_options.extension.front_matter_delimiter = options.front_matter_delimiter;
88
+ comrak_options.extension.greentext = options.greentext;
89
+ comrak_options.extension.header_ids = options.header_ids.clone();
90
+ comrak_options.extension.math_code = options.math_code;
91
+ comrak_options.extension.math_dollars = options.math_dollars;
92
+ comrak_options.extension.multiline_block_quotes = options.multiline_block_quotes;
93
+ comrak_options.extension.shortcodes = options.gemojis;
94
+ comrak_options.extension.spoiler = options.spoiler;
95
+ comrak_options.extension.strikethrough = options.strikethrough;
96
+ comrak_options.extension.subscript = options.subscript;
97
+ comrak_options.extension.superscript = options.superscript;
98
+ comrak_options.extension.table = options.table;
99
+ comrak_options.extension.tagfilter = options.tagfilter;
100
+ comrak_options.extension.tasklist = options.tasklist;
101
+ comrak_options.extension.underline = options.underline;
102
+ comrak_options.extension.wikilinks_title_after_pipe = options.wikilinks_title_after_pipe;
103
+ comrak_options.extension.wikilinks_title_before_pipe = options.wikilinks_title_before_pipe;
104
+
105
+ comrak_options.render.escape = options.escape;
106
+ comrak_options.render.escaped_char_spans = options.escaped_char_spans;
107
+ comrak_options.render.figure_with_caption = options.figure_with_caption;
108
+ comrak_options.render.full_info_string = options.full_info_string;
109
+ comrak_options.render.gfm_quirks = options.gfm_quirks;
110
+ comrak_options.render.github_pre_lang = options.github_pre_lang;
111
+ comrak_options.render.hardbreaks = options.hardbreaks;
112
+ comrak_options.render.ignore_empty_links = options.ignore_empty_links;
113
+ comrak_options.render.ignore_setext = options.ignore_setext;
114
+ comrak_options.render.sourcepos = options.sourcepos;
115
+ // comrak_options.render.syntax_highlighting = options.syntax_highlighting;
116
+
117
+ comrak_options.render.unsafe_ = options.unsafe_;
118
+
119
+ // comrak_options.parse.default_info_string = options.default_info_string;
120
+ comrak_options.parse.relaxed_autolinks = options.relaxed_autolinks;
121
+ comrak_options.parse.relaxed_tasklist_matching = options.relaxed_tasklist_character;
122
+ comrak_options.parse.smart = options.smart;
123
+
124
+ comrak_options
125
+ }
126
+ }
127
+
45
128
  pub fn render(text: String, options: RenderOptions) -> String {
46
- render_comrak(text, options)
129
+ render_with_plugins(text, options, &comrak::Plugins::default())
130
+ }
131
+
132
+ fn render_with_plugins(text: String, render_options: RenderOptions, plugins: &Plugins) -> String {
133
+ let user_data = RenderUserData::from(&render_options);
134
+ let options = comrak::Options::from(&render_options);
135
+
136
+ if user_data.default_html {
137
+ return comrak::markdown_to_html_with_plugins(&text, &options, plugins);
138
+ }
139
+
140
+ let arena = Arena::new();
141
+ let root = parse_document(&arena, &text, &options);
142
+ let mut bw = BufWriter::new(Vec::new());
143
+
144
+ CustomFormatter::format_document_with_plugins(root, &options, &mut bw, plugins, user_data)
145
+ .unwrap();
146
+ String::from_utf8(bw.into_inner().unwrap()).unwrap()
47
147
  }
48
148
 
49
- fn render_comrak(text: String, options: RenderOptions) -> String {
50
- let mut comrak_options = comrak::ComrakOptions::default();
51
-
52
- comrak_options.extension.alerts = options.alerts;
53
- comrak_options.extension.autolink = options.autolink;
54
- comrak_options.extension.description_lists = options.description_lists;
55
- comrak_options.extension.footnotes = options.footnotes;
56
- // comrak_options.extension.front_matter_delimiter = options.front_matter_delimiter;
57
- comrak_options.extension.greentext = options.greentext;
58
- comrak_options.extension.header_ids = options.header_ids;
59
- comrak_options.extension.math_code = options.math_code;
60
- comrak_options.extension.math_dollars = options.math_dollars;
61
- comrak_options.extension.multiline_block_quotes = options.multiline_block_quotes;
62
- comrak_options.extension.shortcodes = options.gemojis;
63
- comrak_options.extension.spoiler = options.spoiler;
64
- comrak_options.extension.strikethrough = options.strikethrough;
65
- comrak_options.extension.subscript = options.subscript;
66
- comrak_options.extension.superscript = options.superscript;
67
- comrak_options.extension.table = options.table;
68
- comrak_options.extension.tagfilter = options.tagfilter;
69
- comrak_options.extension.tasklist = options.tasklist;
70
- comrak_options.extension.underline = options.underline;
71
- comrak_options.extension.wikilinks_title_after_pipe = options.wikilinks_title_after_pipe;
72
- comrak_options.extension.wikilinks_title_before_pipe = options.wikilinks_title_before_pipe;
73
-
74
- comrak_options.render.escape = options.escape;
75
- comrak_options.render.escaped_char_spans = options.escaped_char_spans;
76
- comrak_options.render.figure_with_caption = options.figure_with_caption;
77
- comrak_options.render.full_info_string = options.full_info_string;
78
- comrak_options.render.gfm_quirks = options.gfm_quirks;
79
- comrak_options.render.github_pre_lang = options.github_pre_lang;
80
- comrak_options.render.hardbreaks = options.hardbreaks;
81
- comrak_options.render.ignore_empty_links = options.ignore_empty_links;
82
- comrak_options.render.ignore_setext = options.ignore_setext;
83
- comrak_options.render.sourcepos = options.sourcepos;
84
- comrak_options.render.experimental_inline_sourcepos = options.experimental_inline_sourcepos;
85
- // comrak_options.render.syntax_highlighting = options.syntax_highlighting;
86
-
87
- comrak_options.render.unsafe_ = options.unsafe_;
88
-
89
- // comrak_options.parse.default_info_string = options.default_info_string;
90
- comrak_options.parse.relaxed_autolinks = options.relaxed_autolinks;
91
- comrak_options.parse.relaxed_tasklist_matching = options.relaxed_tasklist_character;
92
- comrak_options.parse.smart = options.smart;
93
-
94
- comrak::markdown_to_html(&text, &comrak_options)
149
+ // The important thing to remember is that this overrides the default behavior of the
150
+ // specified nodes. If we do override a node, then it's our responsibility to ensure that
151
+ // any changes in the `comrak` code for those nodes is backported to here, such as when
152
+ // `figcaption` support was added.
153
+ // One idea to limit that would be having the ability to specify attributes that would
154
+ // be inserted when a node is rendered. That would allow us to (in many cases) just
155
+ // inject the changes we need. Such a feature would need to be added to `comrak`.
156
+ create_formatter!(CustomFormatter<RenderUserData>, {
157
+ NodeValue::Text(_) => |context, node, entering| {
158
+ return render_text(context, node, entering);
159
+ },
160
+ NodeValue::Link(_) => |context, node, entering| {
161
+ return render_link(context, node, entering);
162
+ },
163
+ NodeValue::Image(_) => |context, node, entering| {
164
+ return render_image(context, node, entering);
165
+ }
166
+ });
167
+
168
+ fn render_image<'a>(
169
+ context: &mut Context<RenderUserData>,
170
+ node: &'a AstNode<'a>,
171
+ entering: bool,
172
+ ) -> io::Result<ChildRendering> {
173
+ let NodeValue::Image(ref nl) = node.data.borrow().value else {
174
+ panic!("Attempt to render invalid node as image")
175
+ };
176
+
177
+ if !(context.user.placeholder_detection && PLACEHOLDER_REGEX.is_match(nl.url.as_str())) {
178
+ return html::format_node_default(context, node, entering);
179
+ }
180
+
181
+ if entering {
182
+ if context.options.render.figure_with_caption {
183
+ context.write_all(b"<figure>")?;
184
+ }
185
+ context.write_all(b"<img")?;
186
+ html::render_sourcepos(context, node)?;
187
+ context.write_all(b" src=\"")?;
188
+ let url = nl.url.as_bytes();
189
+ if context.options.render.unsafe_ || !html::dangerous_url(url) {
190
+ if let Some(rewriter) = &context.options.extension.image_url_rewriter {
191
+ context.escape_href(rewriter.to_html(&nl.url).as_bytes())?;
192
+ } else {
193
+ context.escape_href(url)?;
194
+ }
195
+ }
196
+
197
+ context.write_all(b"\"")?;
198
+
199
+ if PLACEHOLDER_REGEX.is_match(nl.url.as_str()) {
200
+ context.write_all(b" data-placeholder")?;
201
+ }
202
+
203
+ context.write_all(b" alt=\"")?;
204
+
205
+ return Ok(ChildRendering::Plain);
206
+ } else {
207
+ if !nl.title.is_empty() {
208
+ context.write_all(b"\" title=\"")?;
209
+ context.escape(nl.title.as_bytes())?;
210
+ }
211
+ context.write_all(b"\" />")?;
212
+ if context.options.render.figure_with_caption {
213
+ if !nl.title.is_empty() {
214
+ context.write_all(b"<figcaption>")?;
215
+ context.escape(nl.title.as_bytes())?;
216
+ context.write_all(b"</figcaption>")?;
217
+ }
218
+ context.write_all(b"</figure>")?;
219
+ };
220
+ }
221
+
222
+ Ok(ChildRendering::HTML)
223
+ }
224
+
225
+ fn render_link<'a>(
226
+ context: &mut Context<RenderUserData>,
227
+ node: &'a AstNode<'a>,
228
+ entering: bool,
229
+ ) -> io::Result<ChildRendering> {
230
+ let NodeValue::Link(ref nl) = node.data.borrow().value else {
231
+ panic!("Attempt to render invalid node as link")
232
+ };
233
+
234
+ if !(context.user.placeholder_detection && PLACEHOLDER_REGEX.is_match(nl.url.as_str())) {
235
+ return html::format_node_default(context, node, entering);
236
+ }
237
+
238
+ let parent_node = node.parent();
239
+
240
+ if !context.options.parse.relaxed_autolinks
241
+ || (parent_node.is_none()
242
+ || !matches!(
243
+ parent_node.unwrap().data.borrow().value,
244
+ NodeValue::Link(..)
245
+ ))
246
+ {
247
+ if entering {
248
+ context.write_all(b"<a")?;
249
+ html::render_sourcepos(context, node)?;
250
+ context.write_all(b" href=\"")?;
251
+ let url = nl.url.as_bytes();
252
+ if context.options.render.unsafe_ || !html::dangerous_url(url) {
253
+ if let Some(rewriter) = &context.options.extension.link_url_rewriter {
254
+ context.escape_href(rewriter.to_html(&nl.url).as_bytes())?;
255
+ } else {
256
+ context.escape_href(url)?;
257
+ }
258
+ }
259
+ context.write_all(b"\"")?;
260
+
261
+ if !nl.title.is_empty() {
262
+ context.write_all(b" title=\"")?;
263
+ context.escape(nl.title.as_bytes())?;
264
+ }
265
+
266
+ if PLACEHOLDER_REGEX.is_match(nl.url.as_str()) {
267
+ context.write_all(b" data-placeholder")?;
268
+ }
269
+
270
+ context.write_all(b">")?;
271
+ } else {
272
+ context.write_all(b"</a>")?;
273
+ }
274
+ }
275
+
276
+ Ok(ChildRendering::HTML)
277
+ }
278
+
279
+ fn render_text<'a>(
280
+ context: &mut Context<RenderUserData>,
281
+ node: &'a AstNode<'a>,
282
+ entering: bool,
283
+ ) -> io::Result<ChildRendering> {
284
+ let NodeValue::Text(ref literal) = node.data.borrow().value else {
285
+ panic!("Attempt to render invalid node as text")
286
+ };
287
+
288
+ if !(context.user.placeholder_detection && PLACEHOLDER_REGEX.is_match(literal)) {
289
+ return html::format_node_default(context, node, entering);
290
+ }
291
+
292
+ // Don't currently support placeholders in the text inside links or images.
293
+ // If the text has an underscore in it, then the parser will not combine
294
+ // the multiple text nodes in `comrak`'s `postprocess_text_nodes`, breaking up
295
+ // the placeholder into multiple text nodes.
296
+ // For example, `[%{a_b}](link)`.
297
+ let parent = node.parent().unwrap();
298
+ if matches!(
299
+ parent.data.borrow().value,
300
+ NodeValue::Link(_) | NodeValue::Image(_)
301
+ ) {
302
+ return html::format_node_default(context, node, entering);
303
+ }
304
+
305
+ if entering {
306
+ let mut cursor: usize = 0;
307
+
308
+ for mat in PLACEHOLDER_REGEX.find_iter(literal) {
309
+ if mat.start() > cursor {
310
+ context.escape(literal[cursor..mat.start()].as_bytes())?;
311
+ }
312
+
313
+ context.write_all(b"<span data-placeholder>")?;
314
+ context.escape(literal[mat.start()..mat.end()].as_bytes())?;
315
+ context.write_all(b"</span>")?;
316
+
317
+ cursor = mat.end();
318
+ }
319
+
320
+ if cursor < literal.len() {
321
+ context.escape(literal[cursor..literal.len()].as_bytes())?;
322
+ }
323
+ }
324
+
325
+ Ok(ChildRendering::HTML)
95
326
  }
@@ -39,7 +39,6 @@ pub fn render_to_html_rs(text: String, options: RHash) -> String {
39
39
  relaxed_autolinks: get_bool_opt("relaxed_autolinks", options),
40
40
  relaxed_tasklist_character: get_bool_opt("relaxed_tasklist_character", options),
41
41
  sourcepos: get_bool_opt("sourcepos", options),
42
- experimental_inline_sourcepos: get_bool_opt("experimental_inline_sourcepos", options),
43
42
  smart: get_bool_opt("smart", options),
44
43
  spoiler: get_bool_opt("spoiler", options),
45
44
  strikethrough: get_bool_opt("strikethrough", options),
@@ -54,6 +53,8 @@ pub fn render_to_html_rs(text: String, options: RHash) -> String {
54
53
  wikilinks_title_after_pipe: get_bool_opt("wikilinks_title_after_pipe", options),
55
54
  wikilinks_title_before_pipe: get_bool_opt("wikilinks_title_before_pipe", options),
56
55
 
56
+ default_html: get_bool_opt("default_html", options),
57
+ placeholder_detection: get_bool_opt("placeholder_detection", options),
57
58
  debug: get_bool_opt("debug", options),
58
59
  };
59
60
 
@@ -32,6 +32,10 @@ struct Args {
32
32
  #[arg(long)]
33
33
  escaped_char_spans: bool,
34
34
 
35
+ /// Render the image as a figure element with the title as its caption
36
+ #[arg(long)]
37
+ figure_with_caption: bool,
38
+
35
39
  /// Enable 'footnotes' extension
36
40
  #[arg(long)]
37
41
  footnotes: bool,
@@ -106,10 +110,6 @@ struct Args {
106
110
  #[arg(long)]
107
111
  sourcepos: bool,
108
112
 
109
- /// Include inline sourcepos in HTML output, which is known to have issues.
110
- #[arg(long)]
111
- experimental_inline_sourcepos: bool,
112
-
113
113
  /// Use smart punctuation
114
114
  #[arg(long)]
115
115
  smart: bool,
@@ -162,6 +162,15 @@ struct Args {
162
162
  #[arg(long)]
163
163
  wikilinks_title_before_pipe: bool,
164
164
 
165
+ /// Only use default comrak HTML formatting
166
+ #[arg(long)]
167
+ default_html: bool,
168
+
169
+ /// Detect and marks potential placeholder variables, which
170
+ /// have the format `%{PLACEHOLDER}`
171
+ #[arg(long)]
172
+ placeholder_detection: bool,
173
+
165
174
  /// Show debug information
166
175
  #[arg(long)]
167
176
  debug: bool,
@@ -188,6 +197,7 @@ fn main() {
188
197
  description_lists: cli.description_lists,
189
198
  escape: cli.escape,
190
199
  escaped_char_spans: cli.escaped_char_spans,
200
+ figure_with_caption: cli.figure_with_caption,
191
201
  footnotes: cli.footnotes,
192
202
  // front_matter_delimiter:
193
203
  full_info_string: cli.full_info_string,
@@ -205,7 +215,6 @@ fn main() {
205
215
  relaxed_autolinks: cli.relaxed_autolinks,
206
216
  relaxed_tasklist_character: cli.relaxed_tasklist_character,
207
217
  sourcepos: cli.sourcepos,
208
- experimental_inline_sourcepos: cli.experimental_inline_sourcepos,
209
218
  smart: cli.smart,
210
219
  spoiler: cli.spoiler,
211
220
  strikethrough: cli.strikethrough,
@@ -219,6 +228,9 @@ fn main() {
219
228
  unsafe_: cli.unsafe_,
220
229
  wikilinks_title_after_pipe: cli.wikilinks_title_after_pipe,
221
230
  wikilinks_title_before_pipe: cli.wikilinks_title_before_pipe,
231
+
232
+ default_html: cli.default_html,
233
+ placeholder_detection: cli.placeholder_detection,
222
234
  debug: cli.debug,
223
235
  };
224
236
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GLFMMarkdown
4
- VERSION = '0.0.28'
4
+ VERSION = '0.0.30'
5
5
  end
data/lib/glfm_markdown.rb CHANGED
@@ -19,7 +19,6 @@ module GLFMMarkdown
19
19
  multiline_block_quotes: true,
20
20
  relaxed_autolinks: false,
21
21
  sourcepos: true,
22
- experimental_inline_sourcepos: true,
23
22
  smart: false,
24
23
  strikethrough: true,
25
24
  table: true,
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.28
4
+ version: 0.0.30
5
5
  platform: x86_64-darwin
6
6
  authors:
7
7
  - Brett Walker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-04 00:00:00.000000000 Z
11
+ date: 2025-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rb_sys