commonmarker 0.23.10 → 2.8.0

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.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1164 -0
  3. data/Cargo.toml +12 -0
  4. data/README.md +252 -176
  5. data/ext/commonmarker/Cargo.toml +21 -0
  6. data/ext/commonmarker/build.rs +9 -0
  7. data/ext/commonmarker/extconf.rb +3 -6
  8. data/ext/commonmarker/src/lib.rs +168 -0
  9. data/ext/commonmarker/src/node.rs +1263 -0
  10. data/ext/commonmarker/src/options.rs +241 -0
  11. data/ext/commonmarker/src/plugins/syntax_highlighting.rs +171 -0
  12. data/ext/commonmarker/src/plugins.rs +6 -0
  13. data/ext/commonmarker/src/utils.rs +5 -0
  14. data/lib/commonmarker/config.rb +103 -40
  15. data/lib/commonmarker/constants.rb +7 -0
  16. data/lib/commonmarker/extension.rb +14 -0
  17. data/lib/commonmarker/node/ast.rb +8 -0
  18. data/lib/commonmarker/node/inspect.rb +14 -4
  19. data/lib/commonmarker/node.rb +29 -47
  20. data/lib/commonmarker/renderer.rb +1 -127
  21. data/lib/commonmarker/utils.rb +22 -0
  22. data/lib/commonmarker/version.rb +2 -2
  23. data/lib/commonmarker.rb +27 -25
  24. metadata +38 -187
  25. data/Rakefile +0 -109
  26. data/bin/commonmarker +0 -118
  27. data/commonmarker.gemspec +0 -38
  28. data/ext/commonmarker/arena.c +0 -104
  29. data/ext/commonmarker/autolink.c +0 -508
  30. data/ext/commonmarker/autolink.h +0 -8
  31. data/ext/commonmarker/blocks.c +0 -1622
  32. data/ext/commonmarker/buffer.c +0 -278
  33. data/ext/commonmarker/buffer.h +0 -116
  34. data/ext/commonmarker/case_fold_switch.inc +0 -4327
  35. data/ext/commonmarker/chunk.h +0 -135
  36. data/ext/commonmarker/cmark-gfm-core-extensions.h +0 -54
  37. data/ext/commonmarker/cmark-gfm-extension_api.h +0 -737
  38. data/ext/commonmarker/cmark-gfm-extensions_export.h +0 -42
  39. data/ext/commonmarker/cmark-gfm.h +0 -833
  40. data/ext/commonmarker/cmark-gfm_export.h +0 -42
  41. data/ext/commonmarker/cmark-gfm_version.h +0 -7
  42. data/ext/commonmarker/cmark.c +0 -55
  43. data/ext/commonmarker/cmark_ctype.c +0 -44
  44. data/ext/commonmarker/cmark_ctype.h +0 -33
  45. data/ext/commonmarker/commonmark.c +0 -514
  46. data/ext/commonmarker/commonmarker.c +0 -1308
  47. data/ext/commonmarker/commonmarker.h +0 -16
  48. data/ext/commonmarker/config.h +0 -76
  49. data/ext/commonmarker/core-extensions.c +0 -27
  50. data/ext/commonmarker/entities.inc +0 -2138
  51. data/ext/commonmarker/ext_scanners.c +0 -879
  52. data/ext/commonmarker/ext_scanners.h +0 -24
  53. data/ext/commonmarker/footnotes.c +0 -63
  54. data/ext/commonmarker/footnotes.h +0 -27
  55. data/ext/commonmarker/houdini.h +0 -57
  56. data/ext/commonmarker/houdini_href_e.c +0 -100
  57. data/ext/commonmarker/houdini_html_e.c +0 -66
  58. data/ext/commonmarker/houdini_html_u.c +0 -149
  59. data/ext/commonmarker/html.c +0 -502
  60. data/ext/commonmarker/html.h +0 -27
  61. data/ext/commonmarker/inlines.c +0 -1788
  62. data/ext/commonmarker/inlines.h +0 -29
  63. data/ext/commonmarker/iterator.c +0 -159
  64. data/ext/commonmarker/iterator.h +0 -26
  65. data/ext/commonmarker/latex.c +0 -468
  66. data/ext/commonmarker/linked_list.c +0 -37
  67. data/ext/commonmarker/man.c +0 -274
  68. data/ext/commonmarker/map.c +0 -129
  69. data/ext/commonmarker/map.h +0 -44
  70. data/ext/commonmarker/node.c +0 -1045
  71. data/ext/commonmarker/node.h +0 -167
  72. data/ext/commonmarker/parser.h +0 -59
  73. data/ext/commonmarker/plaintext.c +0 -218
  74. data/ext/commonmarker/plugin.c +0 -36
  75. data/ext/commonmarker/plugin.h +0 -34
  76. data/ext/commonmarker/references.c +0 -43
  77. data/ext/commonmarker/references.h +0 -26
  78. data/ext/commonmarker/registry.c +0 -63
  79. data/ext/commonmarker/registry.h +0 -24
  80. data/ext/commonmarker/render.c +0 -213
  81. data/ext/commonmarker/render.h +0 -62
  82. data/ext/commonmarker/scanners.c +0 -14056
  83. data/ext/commonmarker/scanners.h +0 -70
  84. data/ext/commonmarker/scanners.re +0 -341
  85. data/ext/commonmarker/strikethrough.c +0 -167
  86. data/ext/commonmarker/strikethrough.h +0 -9
  87. data/ext/commonmarker/syntax_extension.c +0 -149
  88. data/ext/commonmarker/syntax_extension.h +0 -34
  89. data/ext/commonmarker/table.c +0 -917
  90. data/ext/commonmarker/table.h +0 -12
  91. data/ext/commonmarker/tagfilter.c +0 -60
  92. data/ext/commonmarker/tagfilter.h +0 -8
  93. data/ext/commonmarker/tasklist.c +0 -156
  94. data/ext/commonmarker/tasklist.h +0 -8
  95. data/ext/commonmarker/utf8.c +0 -317
  96. data/ext/commonmarker/utf8.h +0 -35
  97. data/ext/commonmarker/xml.c +0 -182
  98. data/lib/commonmarker/renderer/html_renderer.rb +0 -256
@@ -0,0 +1,241 @@
1
+ use std::borrow::Cow;
2
+
3
+ use magnus::TryConvert;
4
+ use magnus::{r_hash::ForEach, RHash, Symbol, Value};
5
+
6
+ use crate::utils::try_convert_string;
7
+
8
+ const PARSE_SMART: &str = "smart";
9
+ const PARSE_DEFAULT_INFO_STRING: &str = "default_info_string";
10
+ const PARSE_RELAXED_TASKLIST_MATCHING: &str = "relaxed_tasklist_matching";
11
+ const PARSE_RELAXED_AUTOLINKS: &str = "relaxed_autolinks";
12
+ const PARSE_LEAVE_FOOTNOTE_DEFINITIONS: &str = "leave_footnote_definitions";
13
+ const PARSE_SOURCEPOS_CHARS: &str = "sourcepos_chars";
14
+
15
+ pub fn iterate_parse_options(comrak_options: &mut comrak::options::Parse, options_hash: RHash) {
16
+ options_hash
17
+ .foreach(|key: Symbol, value: Value| {
18
+ match key.name()? {
19
+ Cow::Borrowed(PARSE_SMART) => {
20
+ comrak_options.smart = TryConvert::try_convert(value)?;
21
+ }
22
+ Cow::Borrowed(PARSE_DEFAULT_INFO_STRING) => {
23
+ comrak_options.default_info_string = try_convert_string(value);
24
+ }
25
+ Cow::Borrowed(PARSE_RELAXED_TASKLIST_MATCHING) => {
26
+ comrak_options.relaxed_tasklist_matching = TryConvert::try_convert(value)?;
27
+ }
28
+ Cow::Borrowed(RENDER_IGNORE_SETEXT) => {
29
+ comrak_options.ignore_setext = TryConvert::try_convert(value)?;
30
+ }
31
+ Cow::Borrowed(PARSE_RELAXED_AUTOLINKS) => {
32
+ comrak_options.relaxed_autolinks = TryConvert::try_convert(value)?;
33
+ }
34
+ Cow::Borrowed(PARSE_LEAVE_FOOTNOTE_DEFINITIONS) => {
35
+ comrak_options.leave_footnote_definitions = TryConvert::try_convert(value)?;
36
+ }
37
+ Cow::Borrowed(PARSE_SOURCEPOS_CHARS) => {
38
+ comrak_options.sourcepos_chars = TryConvert::try_convert(value)?;
39
+ }
40
+ _ => {}
41
+ }
42
+ Ok(ForEach::Continue)
43
+ })
44
+ .unwrap();
45
+ }
46
+
47
+ const RENDER_HARDBREAKS: &str = "hardbreaks";
48
+ const RENDER_GITHUB_PRE_LANG: &str = "github_pre_lang";
49
+ const RENDER_FULL_INFO_STRING: &str = "full_info_string";
50
+ const RENDER_WIDTH: &str = "width";
51
+ const RENDER_UNSAFE: &str = "unsafe";
52
+ const RENDER_ESCAPE: &str = "escape";
53
+ const RENDER_SOURCEPOS: &str = "sourcepos";
54
+ const RENDER_ESCAPED_CHAR_SPANS: &str = "escaped_char_spans";
55
+ const RENDER_IGNORE_SETEXT: &str = "ignore_setext";
56
+ const RENDER_IGNORE_EMPTY_LINKS: &str = "ignore_empty_links";
57
+ const RENDER_GFM_QUIRKS: &str = "gfm_quirks";
58
+ const RENDER_PREFER_FENCED: &str = "prefer_fenced";
59
+ const RENDER_TASKLIST_CLASSES: &str = "tasklist_classes";
60
+ const RENDER_COMPACT_HTML: &str = "compact_html";
61
+
62
+ pub fn iterate_render_options(comrak_options: &mut comrak::options::Render, options_hash: RHash) {
63
+ options_hash
64
+ .foreach(|key: Symbol, value: Value| {
65
+ match key.name()? {
66
+ Cow::Borrowed(RENDER_HARDBREAKS) => {
67
+ comrak_options.hardbreaks = TryConvert::try_convert(value)?;
68
+ }
69
+ Cow::Borrowed(RENDER_GITHUB_PRE_LANG) => {
70
+ comrak_options.github_pre_lang = TryConvert::try_convert(value)?;
71
+ }
72
+ Cow::Borrowed(RENDER_FULL_INFO_STRING) => {
73
+ comrak_options.full_info_string = TryConvert::try_convert(value)?;
74
+ }
75
+ Cow::Borrowed(RENDER_WIDTH) => {
76
+ comrak_options.width = TryConvert::try_convert(value)?;
77
+ }
78
+ Cow::Borrowed(RENDER_UNSAFE) => {
79
+ comrak_options.r#unsafe = TryConvert::try_convert(value)?;
80
+ }
81
+ Cow::Borrowed(RENDER_ESCAPE) => {
82
+ comrak_options.escape = TryConvert::try_convert(value)?;
83
+ }
84
+ Cow::Borrowed(RENDER_SOURCEPOS) => {
85
+ comrak_options.sourcepos = TryConvert::try_convert(value)?;
86
+ }
87
+ Cow::Borrowed(RENDER_ESCAPED_CHAR_SPANS) => {
88
+ comrak_options.escaped_char_spans = TryConvert::try_convert(value)?;
89
+ }
90
+ Cow::Borrowed(RENDER_IGNORE_EMPTY_LINKS) => {
91
+ comrak_options.ignore_empty_links = TryConvert::try_convert(value)?;
92
+ }
93
+ Cow::Borrowed(RENDER_GFM_QUIRKS) => {
94
+ comrak_options.gfm_quirks = TryConvert::try_convert(value)?;
95
+ }
96
+ Cow::Borrowed(RENDER_PREFER_FENCED) => {
97
+ comrak_options.prefer_fenced = TryConvert::try_convert(value)?;
98
+ }
99
+ Cow::Borrowed(RENDER_TASKLIST_CLASSES) => {
100
+ comrak_options.tasklist_classes = TryConvert::try_convert(value)?;
101
+ }
102
+ Cow::Borrowed(RENDER_COMPACT_HTML) => {
103
+ comrak_options.compact_html = TryConvert::try_convert(value)?;
104
+ }
105
+ _ => {}
106
+ }
107
+ Ok(ForEach::Continue)
108
+ })
109
+ .unwrap();
110
+ }
111
+
112
+ const EXTENSION_STRIKETHROUGH: &str = "strikethrough";
113
+ const EXTENSION_TAGFILTER: &str = "tagfilter";
114
+ const EXTENSION_TABLE: &str = "table";
115
+ const EXTENSION_AUTOLINK: &str = "autolink";
116
+ const EXTENSION_TASKLIST: &str = "tasklist";
117
+ const EXTENSION_SUPERSCRIPT: &str = "superscript";
118
+ const EXTENSION_HEADER_IDS: &str = "header_ids";
119
+ const EXTENSION_HEADER_ID_PREFIX_IN_HREF: &str = "header_id_prefix_in_href";
120
+ const EXTENSION_FOOTNOTES: &str = "footnotes";
121
+ const EXTENSION_INLINE_FOOTNOTES: &str = "inline_footnotes";
122
+ const EXTENSION_DESCRIPTION_LISTS: &str = "description_lists";
123
+ const EXTENSION_FRONT_MATTER_DELIMITER: &str = "front_matter_delimiter";
124
+ const EXTENSION_MULTILINE_BLOCK_QUOTES: &str = "multiline_block_quotes";
125
+ const EXTENSION_MATH_DOLLARS: &str = "math_dollars";
126
+ const EXTENSION_MATH_CODE: &str = "math_code";
127
+ const EXTENSION_SHORTCODES: &str = "shortcodes";
128
+ const EXTENSION_WIKILINKS_TITLE_AFTER_PIPE: &str = "wikilinks_title_after_pipe";
129
+ const EXTENSION_WIKILINKS_TITLE_BEFORE_PIPE: &str = "wikilinks_title_before_pipe";
130
+ const EXTENSION_UNDERLINE: &str = "underline";
131
+ const EXTENSION_SPOILER: &str = "spoiler";
132
+ const EXTENSION_GREENTEXT: &str = "greentext";
133
+ const EXTENSION_SUBSCRIPT: &str = "subscript";
134
+ const EXTENSION_SUBTEXT: &str = "subtext";
135
+ const EXTENSION_ALERTS: &str = "alerts";
136
+ const EXTENSION_CJK_FRIENDLY_EMPHASIS: &str = "cjk_friendly_emphasis";
137
+ const EXTENSION_HIGHLIGHT: &str = "highlight";
138
+ const EXTENSION_INSERT: &str = "insert";
139
+ const EXTENSION_BLOCK_DIRECTIVE: &str = "block_directive";
140
+
141
+ pub fn iterate_extension_options(
142
+ comrak_options: &mut comrak::options::Extension,
143
+ options_hash: RHash,
144
+ ) {
145
+ options_hash
146
+ .foreach(|key: Symbol, value: Value| {
147
+ match key.name()? {
148
+ Cow::Borrowed(EXTENSION_STRIKETHROUGH) => {
149
+ comrak_options.strikethrough = TryConvert::try_convert(value)?;
150
+ }
151
+ Cow::Borrowed(EXTENSION_TAGFILTER) => {
152
+ comrak_options.tagfilter = TryConvert::try_convert(value)?;
153
+ }
154
+ Cow::Borrowed(EXTENSION_TABLE) => {
155
+ comrak_options.table = TryConvert::try_convert(value)?;
156
+ }
157
+ Cow::Borrowed(EXTENSION_AUTOLINK) => {
158
+ comrak_options.autolink = TryConvert::try_convert(value)?;
159
+ }
160
+ Cow::Borrowed(EXTENSION_TASKLIST) => {
161
+ comrak_options.tasklist = TryConvert::try_convert(value)?;
162
+ }
163
+ Cow::Borrowed(EXTENSION_SUPERSCRIPT) => {
164
+ comrak_options.superscript = TryConvert::try_convert(value)?;
165
+ }
166
+ Cow::Borrowed(EXTENSION_HEADER_IDS) => {
167
+ comrak_options.header_id_prefix = try_convert_string(value);
168
+ }
169
+ Cow::Borrowed(EXTENSION_HEADER_ID_PREFIX_IN_HREF) => {
170
+ comrak_options.header_id_prefix_in_href = TryConvert::try_convert(value)?;
171
+ }
172
+ Cow::Borrowed(EXTENSION_FOOTNOTES) => {
173
+ comrak_options.footnotes = TryConvert::try_convert(value)?;
174
+ }
175
+ Cow::Borrowed(EXTENSION_INLINE_FOOTNOTES) => {
176
+ comrak_options.inline_footnotes = TryConvert::try_convert(value)?;
177
+ }
178
+ Cow::Borrowed(EXTENSION_DESCRIPTION_LISTS) => {
179
+ comrak_options.description_lists = TryConvert::try_convert(value)?;
180
+ }
181
+ Cow::Borrowed(EXTENSION_FRONT_MATTER_DELIMITER) => {
182
+ if let Some(option) = try_convert_string(value) {
183
+ if !option.is_empty() {
184
+ comrak_options.front_matter_delimiter = Some(option);
185
+ }
186
+ }
187
+ }
188
+ Cow::Borrowed(EXTENSION_MULTILINE_BLOCK_QUOTES) => {
189
+ comrak_options.multiline_block_quotes = TryConvert::try_convert(value)?;
190
+ }
191
+ Cow::Borrowed(EXTENSION_MATH_DOLLARS) => {
192
+ comrak_options.math_dollars = TryConvert::try_convert(value)?;
193
+ }
194
+ Cow::Borrowed(EXTENSION_MATH_CODE) => {
195
+ comrak_options.math_code = TryConvert::try_convert(value)?;
196
+ }
197
+ Cow::Borrowed(EXTENSION_SHORTCODES) => {
198
+ comrak_options.shortcodes = TryConvert::try_convert(value)?;
199
+ }
200
+ Cow::Borrowed(EXTENSION_WIKILINKS_TITLE_AFTER_PIPE) => {
201
+ comrak_options.wikilinks_title_after_pipe = TryConvert::try_convert(value)?;
202
+ }
203
+ Cow::Borrowed(EXTENSION_WIKILINKS_TITLE_BEFORE_PIPE) => {
204
+ comrak_options.wikilinks_title_before_pipe = TryConvert::try_convert(value)?;
205
+ }
206
+ Cow::Borrowed(EXTENSION_UNDERLINE) => {
207
+ comrak_options.underline = TryConvert::try_convert(value)?;
208
+ }
209
+ Cow::Borrowed(EXTENSION_SPOILER) => {
210
+ comrak_options.spoiler = TryConvert::try_convert(value)?;
211
+ }
212
+ Cow::Borrowed(EXTENSION_GREENTEXT) => {
213
+ comrak_options.greentext = TryConvert::try_convert(value)?;
214
+ }
215
+ Cow::Borrowed(EXTENSION_SUBSCRIPT) => {
216
+ comrak_options.subscript = TryConvert::try_convert(value)?;
217
+ }
218
+ Cow::Borrowed(EXTENSION_SUBTEXT) => {
219
+ comrak_options.subtext = TryConvert::try_convert(value)?;
220
+ }
221
+ Cow::Borrowed(EXTENSION_ALERTS) => {
222
+ comrak_options.alerts = TryConvert::try_convert(value)?;
223
+ }
224
+ Cow::Borrowed(EXTENSION_CJK_FRIENDLY_EMPHASIS) => {
225
+ comrak_options.cjk_friendly_emphasis = TryConvert::try_convert(value)?;
226
+ }
227
+ Cow::Borrowed(EXTENSION_HIGHLIGHT) => {
228
+ comrak_options.highlight = TryConvert::try_convert(value)?;
229
+ }
230
+ Cow::Borrowed(EXTENSION_INSERT) => {
231
+ comrak_options.insert = TryConvert::try_convert(value)?;
232
+ }
233
+ Cow::Borrowed(EXTENSION_BLOCK_DIRECTIVE) => {
234
+ comrak_options.block_directive = TryConvert::try_convert(value)?;
235
+ }
236
+ _ => {}
237
+ }
238
+ Ok(ForEach::Continue)
239
+ })
240
+ .unwrap();
241
+ }
@@ -0,0 +1,171 @@
1
+ use std::path::PathBuf;
2
+
3
+ use comrak::plugins::syntect::{SyntectAdapter, SyntectAdapterBuilder};
4
+
5
+ use magnus::value::ReprValue;
6
+ use magnus::{RHash, Ruby, TryConvert, Value};
7
+ use syntect::highlighting::ThemeSet;
8
+
9
+ use crate::EMPTY_STR;
10
+
11
+ pub fn construct_syntax_highlighter_from_plugin(
12
+ ruby: &Ruby,
13
+ rb_plugins: Option<RHash>,
14
+ ) -> Result<Option<SyntectAdapter>, magnus::Error> {
15
+ match rb_plugins {
16
+ None => Ok(None),
17
+ Some(rb_plugins) => {
18
+ let theme = match rb_plugins.get(ruby.to_symbol(super::SYNTAX_HIGHLIGHTER_PLUGIN)) {
19
+ Some(syntax_highlighter_options) => {
20
+ match fetch_syntax_highlighter_theme(ruby, syntax_highlighter_options) {
21
+ Ok(theme) => theme,
22
+ Err(e) => {
23
+ return Err(e);
24
+ }
25
+ }
26
+ }
27
+ None => None, // no `syntax_highlighter:` defined
28
+ };
29
+
30
+ let adapter: SyntectAdapter;
31
+
32
+ match theme {
33
+ None => Ok(None),
34
+ Some(theme) => {
35
+ if theme.is_empty() {
36
+ // no theme? uss css classes
37
+ adapter = SyntectAdapter::new(None);
38
+ Ok(Some(adapter))
39
+ } else {
40
+ let path = match rb_plugins
41
+ .get(ruby.to_symbol(super::SYNTAX_HIGHLIGHTER_PLUGIN))
42
+ {
43
+ Some(syntax_highlighter_options) => {
44
+ fetch_syntax_highlighter_path(ruby, syntax_highlighter_options)?
45
+ }
46
+ None => PathBuf::from("".to_string()), // no `syntax_highlighter:` defined
47
+ };
48
+
49
+ if path.exists() {
50
+ if !path.is_dir() {
51
+ return Err(magnus::Error::new(
52
+ ruby.exception_arg_error(),
53
+ "`path` needs to be a directory",
54
+ ));
55
+ }
56
+
57
+ let builder = SyntectAdapterBuilder::new();
58
+ let mut ts = ThemeSet::load_defaults();
59
+
60
+ match ts.add_from_folder(&path) {
61
+ Ok(_) => {}
62
+ Err(e) => {
63
+ return Err(magnus::Error::new(
64
+ ruby.exception_arg_error(),
65
+ format!("failed to load theme set from path: {e}"),
66
+ ));
67
+ }
68
+ }
69
+
70
+ // check if the theme exists in the dir
71
+ match ts.themes.get(&theme) {
72
+ Some(theme) => theme,
73
+ None => {
74
+ return Err(magnus::Error::new(
75
+ ruby.exception_arg_error(),
76
+ format!("theme `{}` does not exist", theme),
77
+ ));
78
+ }
79
+ };
80
+
81
+ adapter = builder.theme_set(ts).theme(&theme).build();
82
+
83
+ Ok(Some(adapter))
84
+ } else {
85
+ // no path? default theme lookup
86
+ ThemeSet::load_defaults()
87
+ .themes
88
+ .get(&theme)
89
+ .ok_or_else(|| {
90
+ magnus::Error::new(
91
+ ruby.exception_arg_error(),
92
+ format!("theme `{}` does not exist", theme),
93
+ )
94
+ })?;
95
+ adapter = SyntectAdapter::new(Some(&theme));
96
+ Ok(Some(adapter))
97
+ }
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+
105
+ fn fetch_syntax_highlighter_theme(
106
+ ruby: &Ruby,
107
+ value: Value,
108
+ ) -> Result<Option<String>, magnus::Error> {
109
+ if value.is_nil() {
110
+ // `syntax_highlighter: nil`
111
+ return Ok(None);
112
+ }
113
+
114
+ let syntax_highlighter_plugin: RHash = match TryConvert::try_convert(value) {
115
+ Ok(plugin) => plugin, // `syntax_highlighter: { theme: "<something>" }`
116
+ Err(e) => {
117
+ // not a hash!
118
+ return Err(e);
119
+ }
120
+ };
121
+
122
+ if syntax_highlighter_plugin.is_nil() || syntax_highlighter_plugin.is_empty() {
123
+ return Err(magnus::Error::new(
124
+ ruby.exception_type_error(),
125
+ "theme cannot be blank hash",
126
+ ));
127
+ }
128
+
129
+ let theme_key = ruby.to_symbol(super::SYNTAX_HIGHLIGHTER_PLUGIN_THEME_KEY);
130
+
131
+ match syntax_highlighter_plugin.get(theme_key) {
132
+ Some(theme) => {
133
+ if theme.is_nil() {
134
+ return Err(magnus::Error::new(
135
+ ruby.exception_type_error(),
136
+ "theme cannot be nil",
137
+ ));
138
+ }
139
+ Ok(TryConvert::try_convert(theme)?)
140
+ }
141
+ None => {
142
+ // `syntax_highlighter: { theme: nil }`
143
+ Ok(None)
144
+ }
145
+ }
146
+ }
147
+
148
+ fn fetch_syntax_highlighter_path(ruby: &Ruby, value: Value) -> Result<PathBuf, magnus::Error> {
149
+ if value.is_nil() {
150
+ // `syntax_highlighter: nil`
151
+ return Ok(PathBuf::from(EMPTY_STR));
152
+ }
153
+
154
+ let syntax_highlighter_plugin: RHash = TryConvert::try_convert(value)?;
155
+ let path_key = ruby.to_symbol(super::SYNTAX_HIGHLIGHTER_PLUGIN_PATH_KEY);
156
+
157
+ match syntax_highlighter_plugin.get(path_key) {
158
+ Some(path) => {
159
+ if path.is_nil() {
160
+ // `syntax_highlighter: { path: nil }`
161
+ return Ok(PathBuf::from(EMPTY_STR));
162
+ }
163
+ let val: String = TryConvert::try_convert(path)?;
164
+ Ok(PathBuf::from(val))
165
+ }
166
+ None => {
167
+ // `syntax_highlighter: { }`
168
+ Ok(PathBuf::from(EMPTY_STR))
169
+ }
170
+ }
171
+ }
@@ -0,0 +1,6 @@
1
+ pub mod syntax_highlighting;
2
+
3
+ pub const SYNTAX_HIGHLIGHTER_PLUGIN: &str = "syntax_highlighter";
4
+
5
+ pub const SYNTAX_HIGHLIGHTER_PLUGIN_THEME_KEY: &str = "theme";
6
+ pub const SYNTAX_HIGHLIGHTER_PLUGIN_PATH_KEY: &str = "path";
@@ -0,0 +1,5 @@
1
+ use magnus::{TryConvert, Value};
2
+
3
+ pub fn try_convert_string(value: Value) -> Option<String> {
4
+ TryConvert::try_convert(value).ok()
5
+ }
@@ -1,54 +1,117 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module CommonMarker
4
- # For Ruby::Enum, these must be classes, not modules
3
+ module Commonmarker
5
4
  module Config
6
- # See https://github.com/github/cmark-gfm/blob/master/src/cmark-gfm.h#L673
7
- OPTS = {
5
+ # For details, see
6
+ # https://github.com/kivikakk/comrak/blob/162ef9354deb2c9b4a4e05be495aa372ba5bb696/src/main.rs#L201
7
+ OPTIONS = {
8
8
  parse: {
9
- DEFAULT: 0,
10
- SOURCEPOS: (1 << 1),
11
- UNSAFE: (1 << 17),
12
- VALIDATE_UTF8: (1 << 9),
13
- SMART: (1 << 10),
14
- LIBERAL_HTML_TAG: (1 << 12),
15
- FOOTNOTES: (1 << 13),
16
- STRIKETHROUGH_DOUBLE_TILDE: (1 << 14),
9
+ smart: false,
10
+ default_info_string: "",
11
+ relaxed_tasklist_matching: false,
12
+ relaxed_autolinks: false,
13
+ leave_footnote_definitions: false,
14
+ ignore_setext: false,
15
+ sourcepos_chars: false,
17
16
  }.freeze,
18
17
  render: {
19
- DEFAULT: 0,
20
- SOURCEPOS: (1 << 1),
21
- HARDBREAKS: (1 << 2),
22
- UNSAFE: (1 << 17),
23
- NOBREAKS: (1 << 4),
24
- VALIDATE_UTF8: (1 << 9),
25
- SMART: (1 << 10),
26
- GITHUB_PRE_LANG: (1 << 11),
27
- LIBERAL_HTML_TAG: (1 << 12),
28
- FOOTNOTES: (1 << 13),
29
- STRIKETHROUGH_DOUBLE_TILDE: (1 << 14),
30
- TABLE_PREFER_STYLE_ATTRIBUTES: (1 << 15),
31
- FULL_INFO_STRING: (1 << 16),
18
+ hardbreaks: true,
19
+ github_pre_lang: true,
20
+ full_info_string: false,
21
+ width: 80,
22
+ unsafe: false,
23
+ escape: false,
24
+ sourcepos: false,
25
+ escaped_char_spans: true,
26
+ ignore_empty_links: false,
27
+ gfm_quirks: false,
28
+ prefer_fenced: false,
29
+ tasklist_classes: false,
30
+ compact_html: false,
32
31
  }.freeze,
33
- format: [:html, :xml, :commonmark, :plaintext].freeze,
32
+ extension: {
33
+ strikethrough: true,
34
+ tagfilter: true,
35
+ table: true,
36
+ autolink: true,
37
+ tasklist: true,
38
+ superscript: false,
39
+ header_ids: "",
40
+ header_id_prefix_in_href: false,
41
+ footnotes: false,
42
+ inline_footnotes: false,
43
+ description_lists: false,
44
+ front_matter_delimiter: "",
45
+ multiline_block_quotes: false,
46
+ math_dollars: false,
47
+ math_code: false,
48
+ shortcodes: true,
49
+ wikilinks_title_before_pipe: false,
50
+ wikilinks_title_after_pipe: false,
51
+ underline: false,
52
+ spoiler: false,
53
+ greentext: false,
54
+ subscript: false,
55
+ subtext: false,
56
+ alerts: false,
57
+ cjk_friendly_emphasis: false,
58
+ highlight: false,
59
+ insert: false,
60
+ block_directive: false,
61
+ }.freeze,
62
+ format: [:html].freeze,
34
63
  }.freeze
35
64
 
65
+ PLUGINS = {
66
+ syntax_highlighter: {
67
+ theme: "base16-ocean.dark",
68
+ path: "",
69
+ },
70
+ }
71
+
36
72
  class << self
37
- def process_options(option, type)
38
- case option
39
- when Symbol
40
- OPTS.fetch(type).fetch(option)
41
- when Array
42
- raise TypeError if option.none?
43
-
44
- # neckbearding around. the map will both check the opts and then bitwise-OR it
45
- OPTS.fetch(type).fetch_values(*option).inject(0, :|)
46
- else
47
- raise TypeError, "option type must be a valid symbol or array of symbols within the #{name}::OPTS[:#{type}] context"
73
+ include Commonmarker::Utils
74
+
75
+ def process_options(options)
76
+ {
77
+ parse: process_parse_options(options[:parse].dup),
78
+ render: process_render_options(options[:render].dup),
79
+ extension: process_extension_options(options[:extension].dup),
80
+ }
81
+ end
82
+
83
+ def process_plugins(plugins)
84
+ {
85
+ syntax_highlighter: process_syntax_highlighter_plugin(plugins&.fetch(:syntax_highlighter, nil)),
86
+ }
87
+ end
88
+ end
89
+
90
+ [:parse, :render, :extension].each do |type|
91
+ define_singleton_method :"process_#{type}_options" do |options|
92
+ Commonmarker::Config::OPTIONS[type].each_with_object({}) do |(key, value), hash|
93
+ if options.nil? || !options.key?(key) # option not provided, use the default
94
+ hash[key] = value
95
+ next
96
+ end
97
+
98
+ if options[key].nil? # # option explicitly not included, remove it
99
+ options.delete(key)
100
+ next
101
+ end
102
+
103
+ hash[key] = fetch_kv(options, key, value, type)
48
104
  end
49
- rescue KeyError => e
50
- raise TypeError, "option ':#{e.key}' does not exist for #{name}::OPTS[:#{type}]"
51
105
  end
52
- end
106
+ end
107
+
108
+ define_singleton_method :process_syntax_highlighter_plugin do |options|
109
+ return if options.nil? # plugin explicitly nil, remove it
110
+
111
+ raise TypeError, "Expected a Hash for syntax_highlighter plugin, got #{options.class}" unless options.is_a?(Hash)
112
+ raise TypeError, "Expected a Hash for syntax_highlighter plugin, got nothing" if options.empty?
113
+
114
+ Commonmarker::Config::PLUGINS[:syntax_highlighter].merge(options)
115
+ end
53
116
  end
54
117
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commonmarker
4
+ module Constants
5
+ BOOLS = [true, false].freeze
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ # native precompiled gems package shared libraries in <gem_dir>/lib/commonmarker/<ruby_version>
5
+ # load the precompiled extension file
6
+ ruby_version = /\d+\.\d+/.match(RUBY_VERSION)
7
+ require_relative "#{ruby_version}/commonmarker"
8
+ rescue LoadError
9
+ # fall back to the extension compiled upon installation.
10
+ # use "require" instead of "require_relative" because non-native gems will place C extension files
11
+ # in Gem::BasicSpecification#extension_dir after compilation (during normal installation), which
12
+ # is in $LOAD_PATH but not necessarily relative to this file (see nokogiri#2300)
13
+ require "commonmarker/commonmarker"
14
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commonmarker
4
+ class Node
5
+ class Ast
6
+ end
7
+ end
8
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "pp"
4
4
 
5
- module CommonMarker
5
+ module Commonmarker
6
6
  class Node
7
7
  module Inspect
8
8
  PP_INDENT_SIZE = 2
@@ -16,9 +16,19 @@ module CommonMarker
16
16
  printer.group(PP_INDENT_SIZE, "#<#{self.class}(#{type}):", ">") do
17
17
  printer.breakable
18
18
 
19
- attrs = [:sourcepos, :string_content, :url, :title, :header_level, :list_type, :list_start, :list_tight, :fence_info].map do |name|
19
+ attrs = [
20
+ :source_position,
21
+ :string_content,
22
+ :url,
23
+ :title,
24
+ :header_level,
25
+ :list_type,
26
+ :list_start,
27
+ :list_tight,
28
+ :fence_info,
29
+ ].filter_map do |name|
20
30
  [name, __send__(name)]
21
- rescue NodeError
31
+ rescue StandardError
22
32
  nil
23
33
  end.compact
24
34
 
@@ -34,7 +44,7 @@ module CommonMarker
34
44
  node = first_child
35
45
  while node
36
46
  children << node
37
- node = node.next
47
+ node = node.next_sibling
38
48
  end
39
49
  printer.text("children=")
40
50
  printer.pp(children)