commonmarker 0.23.10 → 2.0.2

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1158 -0
  3. data/Cargo.toml +7 -0
  4. data/README.md +233 -172
  5. data/ext/commonmarker/Cargo.toml +20 -0
  6. data/ext/commonmarker/extconf.rb +3 -6
  7. data/ext/commonmarker/src/lib.rs +103 -0
  8. data/ext/commonmarker/src/node.rs +1167 -0
  9. data/ext/commonmarker/src/options.rs +216 -0
  10. data/ext/commonmarker/src/plugins/syntax_highlighting.rs +166 -0
  11. data/ext/commonmarker/src/plugins.rs +6 -0
  12. data/ext/commonmarker/src/utils.rs +8 -0
  13. data/lib/commonmarker/config.rb +91 -40
  14. data/lib/commonmarker/constants.rb +7 -0
  15. data/lib/commonmarker/extension.rb +14 -0
  16. data/lib/commonmarker/node/ast.rb +8 -0
  17. data/lib/commonmarker/node/inspect.rb +14 -4
  18. data/lib/commonmarker/node.rb +29 -47
  19. data/lib/commonmarker/renderer.rb +1 -127
  20. data/lib/commonmarker/utils.rb +22 -0
  21. data/lib/commonmarker/version.rb +2 -2
  22. data/lib/commonmarker.rb +27 -25
  23. metadata +43 -191
  24. data/Rakefile +0 -109
  25. data/bin/commonmarker +0 -118
  26. data/commonmarker.gemspec +0 -38
  27. data/ext/commonmarker/arena.c +0 -104
  28. data/ext/commonmarker/autolink.c +0 -508
  29. data/ext/commonmarker/autolink.h +0 -8
  30. data/ext/commonmarker/blocks.c +0 -1622
  31. data/ext/commonmarker/buffer.c +0 -278
  32. data/ext/commonmarker/buffer.h +0 -116
  33. data/ext/commonmarker/case_fold_switch.inc +0 -4327
  34. data/ext/commonmarker/chunk.h +0 -135
  35. data/ext/commonmarker/cmark-gfm-core-extensions.h +0 -54
  36. data/ext/commonmarker/cmark-gfm-extension_api.h +0 -737
  37. data/ext/commonmarker/cmark-gfm-extensions_export.h +0 -42
  38. data/ext/commonmarker/cmark-gfm.h +0 -833
  39. data/ext/commonmarker/cmark-gfm_export.h +0 -42
  40. data/ext/commonmarker/cmark-gfm_version.h +0 -7
  41. data/ext/commonmarker/cmark.c +0 -55
  42. data/ext/commonmarker/cmark_ctype.c +0 -44
  43. data/ext/commonmarker/cmark_ctype.h +0 -33
  44. data/ext/commonmarker/commonmark.c +0 -514
  45. data/ext/commonmarker/commonmarker.c +0 -1308
  46. data/ext/commonmarker/commonmarker.h +0 -16
  47. data/ext/commonmarker/config.h +0 -76
  48. data/ext/commonmarker/core-extensions.c +0 -27
  49. data/ext/commonmarker/entities.inc +0 -2138
  50. data/ext/commonmarker/ext_scanners.c +0 -879
  51. data/ext/commonmarker/ext_scanners.h +0 -24
  52. data/ext/commonmarker/footnotes.c +0 -63
  53. data/ext/commonmarker/footnotes.h +0 -27
  54. data/ext/commonmarker/houdini.h +0 -57
  55. data/ext/commonmarker/houdini_href_e.c +0 -100
  56. data/ext/commonmarker/houdini_html_e.c +0 -66
  57. data/ext/commonmarker/houdini_html_u.c +0 -149
  58. data/ext/commonmarker/html.c +0 -502
  59. data/ext/commonmarker/html.h +0 -27
  60. data/ext/commonmarker/inlines.c +0 -1788
  61. data/ext/commonmarker/inlines.h +0 -29
  62. data/ext/commonmarker/iterator.c +0 -159
  63. data/ext/commonmarker/iterator.h +0 -26
  64. data/ext/commonmarker/latex.c +0 -468
  65. data/ext/commonmarker/linked_list.c +0 -37
  66. data/ext/commonmarker/man.c +0 -274
  67. data/ext/commonmarker/map.c +0 -129
  68. data/ext/commonmarker/map.h +0 -44
  69. data/ext/commonmarker/node.c +0 -1045
  70. data/ext/commonmarker/node.h +0 -167
  71. data/ext/commonmarker/parser.h +0 -59
  72. data/ext/commonmarker/plaintext.c +0 -218
  73. data/ext/commonmarker/plugin.c +0 -36
  74. data/ext/commonmarker/plugin.h +0 -34
  75. data/ext/commonmarker/references.c +0 -43
  76. data/ext/commonmarker/references.h +0 -26
  77. data/ext/commonmarker/registry.c +0 -63
  78. data/ext/commonmarker/registry.h +0 -24
  79. data/ext/commonmarker/render.c +0 -213
  80. data/ext/commonmarker/render.h +0 -62
  81. data/ext/commonmarker/scanners.c +0 -14056
  82. data/ext/commonmarker/scanners.h +0 -70
  83. data/ext/commonmarker/scanners.re +0 -341
  84. data/ext/commonmarker/strikethrough.c +0 -167
  85. data/ext/commonmarker/strikethrough.h +0 -9
  86. data/ext/commonmarker/syntax_extension.c +0 -149
  87. data/ext/commonmarker/syntax_extension.h +0 -34
  88. data/ext/commonmarker/table.c +0 -917
  89. data/ext/commonmarker/table.h +0 -12
  90. data/ext/commonmarker/tagfilter.c +0 -60
  91. data/ext/commonmarker/tagfilter.h +0 -8
  92. data/ext/commonmarker/tasklist.c +0 -156
  93. data/ext/commonmarker/tasklist.h +0 -8
  94. data/ext/commonmarker/utf8.c +0 -317
  95. data/ext/commonmarker/utf8.h +0 -35
  96. data/ext/commonmarker/xml.c +0 -182
  97. data/lib/commonmarker/renderer/html_renderer.rb +0 -256
data/Cargo.toml ADDED
@@ -0,0 +1,7 @@
1
+ # This Cargo.toml is here to let externals tools (IDEs, etc.) know that this is
2
+ # a Rust project. Your extensions depedencies should be added to the Cargo.toml
3
+ # in the ext/ directory.
4
+
5
+ [workspace]
6
+ members = ["ext/commonmarker"]
7
+ resolver = "2"
data/README.md CHANGED
@@ -1,11 +1,13 @@
1
- # CommonMarker
1
+ # Commonmarker
2
2
 
3
- ![Build Status](https://github.com/gjtorikian/commonmarker/workflows/CI/badge.svg) [![Gem Version](https://badge.fury.io/rb/commonmarker.svg)](http://badge.fury.io/rb/commonmarker)
3
+ Ruby wrapper for Rust's [comrak](https://github.com/kivikakk/comrak) crate.
4
4
 
5
- Ruby wrapper for [libcmark-gfm](https://github.com/github/cmark),
6
- GitHub's fork of the reference parser for CommonMark. It passes all of the C tests, and is therefore spec-complete. It also includes extensions to the CommonMark spec as documented in the [GitHub Flavored Markdown spec](http://github.github.com/gfm/), such as support for tables, strikethroughs, and autolinking.
5
+ It passes all of the CommonMark test suite, and is therefore spec-complete. It also includes extensions to the CommonMark spec as documented in the [GitHub Flavored Markdown spec](http://github.github.com/gfm/), such as support for tables, strikethroughs, and autolinking.
7
6
 
8
- For more information on available extensions, see [the documentation below](#extensions).
7
+ > [!NOTE]
8
+ > By default, several extensions not in any spec have been enabled, for the sake of end user convenience when generating HTML.
9
+ >
10
+ > For more information on the available options and extensions, see [the documentation below](#options-and-plugins).
9
11
 
10
12
  ## Installation
11
13
 
@@ -25,45 +27,75 @@ Or install it yourself as:
25
27
 
26
28
  ### Converting to HTML
27
29
 
28
- Call `render_html` on a string to convert it to HTML:
30
+ Call `to_html` on a string to convert it to HTML:
29
31
 
30
- ``` ruby
32
+ ```ruby
31
33
  require 'commonmarker'
32
- CommonMarker.render_html('Hi *there*', :DEFAULT)
33
- # <p>Hi <em>there</em></p>\n
34
+ Commonmarker.to_html('"Hi *there*"', options: {
35
+ parse: { smart: true }
36
+ })
37
+ # => <p>“Hi <em>there</em>”</p>\n
34
38
  ```
35
39
 
36
- The second argument is optional--[see below](#options) for more information.
40
+ (The second argument is optional--[see below](#options-and-plugins) for more information.)
37
41
 
38
42
  ### Generating a document
39
43
 
40
- You can also parse a string to receive a `Document` node. You can then print that node to HTML, iterate over the children, and other fun node stuff. For example:
44
+ You can also parse a string to receive a `:document` node. You can then print that node to HTML, iterate over the children, and do other fun node stuff. For example:
41
45
 
42
- ``` ruby
46
+ ```ruby
43
47
  require 'commonmarker'
44
48
 
45
- doc = CommonMarker.render_doc('*Hello* world', :DEFAULT)
46
- puts(doc.to_html) # <p>Hi <em>there</em></p>\n
49
+ doc = Commonmarker.parse("*Hello* world", options: {
50
+ parse: { smart: true }
51
+ })
52
+ puts(doc.to_html) # => <p><em>Hello</em> world</p>\n
47
53
 
48
54
  doc.walk do |node|
49
- puts node.type # [:document, :paragraph, :text, :emph, :text]
55
+ puts node.type # => [:document, :paragraph, :emph, :text, :text]
50
56
  end
51
57
  ```
52
58
 
53
- The second argument is optional--[see below](#options) for more information.
59
+ (The second argument is optional--[see below](#options-and-plugins) for more information.)
60
+
61
+ When it comes to modifying the document, you can perform the following operations:
62
+
63
+ - `insert_before`
64
+ - `insert_after`
65
+ - `prepend_child`
66
+ - `append_child`
67
+ - `delete`
68
+
69
+ You can also get the source position of a node by calling `source_position`:
70
+
71
+ ```ruby
72
+ doc = Commonmarker.parse("*Hello* world")
73
+ puts doc.first_child.first_child.source_position
74
+ # => {:start_line=>1, :start_column=>1, :end_line=>1, :end_column=>7}
75
+ ```
76
+
77
+ You can also modify the following attributes:
54
78
 
55
- #### Example: walking the AST
79
+ - `url`
80
+ - `title`
81
+ - `header_level`
82
+ - `list_type`
83
+ - `list_start`
84
+ - `list_tight`
85
+ - `fence_info`
86
+
87
+ #### Example: Walking the AST
56
88
 
57
89
  You can use `walk` or `each` to iterate over nodes:
58
90
 
59
91
  - `walk` will iterate on a node and recursively iterate on a node's children.
60
- - `each` will iterate on a node and its children, but no further.
92
+ - `each` will iterate on a node's direct children, but no further.
61
93
 
62
- ``` ruby
94
+ ```ruby
63
95
  require 'commonmarker'
64
96
 
65
- # parse the files specified on the command line
66
- doc = CommonMarker.render_doc("# The site\n\n [GitHub](https://www.github.com)")
97
+ # parse some string
98
+ doc = Commonmarker.parse("# The site\n\n [GitHub](https://www.github.com)")
67
99
 
68
100
  # Walk tree and print out URLs for links
69
101
  doc.walk do |node|
@@ -71,17 +103,7 @@ doc.walk do |node|
71
103
  printf("URL = %s\n", node.url)
72
104
  end
73
105
  end
74
-
75
- # Capitalize all regular text in headers
76
- doc.walk do |node|
77
- if node.type == :header
78
- node.each do |subnode|
79
- if subnode.type == :text
80
- subnode.string_content = subnode.string_content.upcase
81
- end
82
- end
83
- end
84
- end
106
+ # => URL = https://www.github.com
85
107
 
86
108
  # Transform links to regular text
87
109
  doc.walk do |node|
@@ -90,169 +112,197 @@ doc.walk do |node|
90
112
  node.delete
91
113
  end
92
114
  end
115
+ # => <h1><a href=\"#the-site\"></a>The site</h1>\n<p>GitHub</p>\n
93
116
  ```
94
117
 
95
- ### Creating a custom renderer
118
+ #### Example: Converting a document back into raw CommonMark
96
119
 
97
- You can also derive a class from CommonMarker's `HtmlRenderer` class. This produces slower output, but is far more customizable. For example:
120
+ You can use `to_commonmark` on a node to render it as raw text:
98
121
 
99
- ``` ruby
100
- class MyHtmlRenderer < CommonMarker::HtmlRenderer
101
- def initialize
102
- super
103
- @headerid = 1
104
- end
122
+ ```ruby
123
+ require 'commonmarker'
105
124
 
106
- def header(node)
107
- block do
108
- out("<h", node.header_level, " id=\"", @headerid, "\">",
109
- :children, "</h", node.header_level, ">")
110
- @headerid += 1
111
- end
125
+ # parse some string
126
+ doc = Commonmarker.parse("# The site\n\n [GitHub](https://www.github.com)")
127
+
128
+ # Transform links to regular text
129
+ doc.walk do |node|
130
+ if node.type == :link
131
+ node.insert_before(node.first_child)
132
+ node.delete
112
133
  end
113
134
  end
114
135
 
115
- myrenderer = MyHtmlRenderer.new
116
- puts myrenderer.render(doc)
117
-
118
- # Print any warnings to STDERR
119
- renderer.warnings.each do |w|
120
- STDERR.write("#{w}\n")
121
- end
136
+ doc.to_commonmark
137
+ # => # The site\n\nGitHub\n
122
138
  ```
123
139
 
124
- ## Options
140
+ ## Options and plugins
125
141
 
126
- CommonMarker accepts the same options that CMark does, as symbols. Note that there is a distinction in CMark for "parse" options and "render" options, which are represented in the tables below.
142
+ ### Options
127
143
 
128
- ### Parse options
144
+ Commonmarker accepts the same parse, render, and extensions options that comrak does, as a hash dictionary with symbol keys:
129
145
 
130
- | Name | Description
131
- | ----------------------------- | -----------
132
- | `:DEFAULT` | The default parsing system.
133
- | `:SOURCEPOS` | Include source position in nodes
134
- | `:UNSAFE` | Allow raw/custom HTML and unsafe links.
135
- | `:VALIDATE_UTF8` | Replace illegal sequences with the replacement character `U+FFFD`.
136
- | `:SMART` | Use smart punctuation (curly quotes, etc.).
137
- | `:LIBERAL_HTML_TAG` | Support liberal parsing of inline HTML tags.
138
- | `:FOOTNOTES` | Parse footnotes.
139
- | `:STRIKETHROUGH_DOUBLE_TILDE` | Parse strikethroughs by double tildes (compatibility with [redcarpet](https://github.com/vmg/redcarpet))
140
-
141
- ### Render options
142
-
143
- | Name | Description |
144
- | ------------------ | ----------- |
145
- | `:DEFAULT` | The default rendering system. |
146
- | `:SOURCEPOS` | Include source position in rendered HTML. |
147
- | `:HARDBREAKS` | Treat `\n` as hardbreaks (by adding `<br/>`). |
148
- | `:UNSAFE` | Allow raw/custom HTML and unsafe links. |
149
- | `:NOBREAKS` | Translate `\n` in the source to a single whitespace. |
150
- | `:VALIDATE_UTF8` | Replace illegal sequences with the replacement character `U+FFFD`. |
151
- | `:SMART` | Use smart punctuation (curly quotes, etc.). |
152
- | `:GITHUB_PRE_LANG` | Use GitHub-style `<pre lang>` for fenced code blocks. |
153
- | `:LIBERAL_HTML_TAG` | Support liberal parsing of inline HTML tags. |
154
- | `:FOOTNOTES` | Render footnotes. |
155
- | `:STRIKETHROUGH_DOUBLE_TILDE` | Parse strikethroughs by double tildes (compatibility with [redcarpet](https://github.com/vmg/redcarpet)) |
156
- | `:TABLE_PREFER_STYLE_ATTRIBUTES` | Use `style` insted of `align` for table cells. |
157
- | `:FULL_INFO_STRING` | Include full info strings of code blocks in separate attribute. |
158
-
159
- ### Passing options
160
-
161
- To apply a single option, pass it in as a symbol argument:
162
-
163
- ``` ruby
164
- CommonMarker.render_doc("\"Hello,\" said the spider.", :SMART)
165
- # <p>“Hello,” said the spider.</p>\n
146
+ ```ruby
147
+ Commonmarker.to_html('"Hi *there*"', options:{
148
+ parse: { smart: true },
149
+ render: { hardbreaks: false}
150
+ })
166
151
  ```
167
152
 
168
- To have multiple options applied, pass in an array of symbols:
153
+ Note that there is a distinction in comrak for "parse" options and "render" options, which are represented in the tables below. As well, if you wish to disable any-non boolean option, pass in `nil`.
169
154
 
170
- ``` ruby
171
- CommonMarker.render_html("\"'Shelob' is my name.\"", [:HARDBREAKS, :SOURCEPOS])
172
- ```
155
+ ### Parse options
173
156
 
174
- For more information on these options, see [the CMark documentation](https://git.io/v7nh1).
157
+ | Name | Description | Default |
158
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
159
+ | `smart` | Punctuation (quotes, full-stops and hyphens) are converted into 'smart' punctuation. | `false` |
160
+ | `default_info_string` | The default info string for fenced code blocks. | `""` |
161
+ | `relaxed_tasklist_matching` | Enables relaxing of the tasklist extension matching, allowing any non-space to be used for the "checked" state instead of only `x` and `X`. | `false` |
162
+ | `relaxed_autolinks` | Enable relaxing of the autolink extension parsing, allowing links to be recognized when in brackets, as well as permitting any url scheme. | `false` |
175
163
 
176
- ## Extensions
164
+ ### Render options
177
165
 
178
- Both `render_html` and `render_doc` take an optional third argument defining the extensions you want enabled as your CommonMark document is being processed. The documentation for these extensions are [defined in this spec](https://github.github.com/gfm/), and the rationale is provided [in this blog post](https://githubengineering.com/a-formal-spec-for-github-markdown/).
166
+ | Name | Description | Default |
167
+ | -------------------- | ------------------------------------------------------------------------------------------------------ | ------- |
168
+ | `hardbreaks` | [Soft line breaks](http://spec.commonmark.org/0.27/#soft-line-breaks) translate into hard line breaks. | `true` |
169
+ | `github_pre_lang` | GitHub-style `<pre lang="xyz">` is used for fenced code blocks with info tags. | `true` |
170
+ | `full_info_string` | Gives info string data after a space in a `data-meta` attribute on code blocks. | `false` |
171
+ | `width` | The wrap column when outputting CommonMark. | `80` |
172
+ | `unsafe` | Allow rendering of raw HTML and potentially dangerous links. | `false` |
173
+ | `escape` | Escape raw HTML instead of clobbering it. | `false` |
174
+ | `sourcepos` | Include source position attribute in HTML and XML output. | `false` |
175
+ | `escaped_char_spans` | Wrap escaped characters in span tags. | `true` |
176
+ | `ignore_setext` | Ignores setext-style headings. | `false` |
177
+ | `ignore_empty_links` | Ignores empty links, leaving the Markdown text in place. | `false` |
178
+ | `gfm_quirks` | Outputs HTML with GFM-style quirks; namely, not nesting `<strong>` inlines. | `false` |
179
+ | `prefer_fenced` | Always output fenced code blocks, even where an indented one could be used. | `false` |
180
+
181
+ As well, there are several extensions which you can toggle in the same manner:
179
182
 
180
- The available extensions are:
183
+ ```ruby
184
+ Commonmarker.to_html('"Hi *there*"', options: {
185
+ extension: { footnotes: true, description_lists: true },
186
+ render: { hardbreaks: false }
187
+ })
188
+ ```
181
189
 
182
- * `:table` - This provides support for tables.
183
- * `:tasklist` - This provides support for task list items.
184
- * `:strikethrough` - This provides support for strikethroughs.
185
- * `:autolink` - This provides support for automatically converting URLs to anchor tags.
186
- * `:tagfilter` - This escapes [several "unsafe" HTML tags](https://github.github.com/gfm/#disallowed-raw-html-extension-), causing them to not have any effect.
190
+ ### Extension options
191
+
192
+ | Name | Description | Default |
193
+ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------- |
194
+ | `strikethrough` | Enables the [strikethrough extension](https://github.github.com/gfm/#strikethrough-extension-) from the GFM spec. | `true` |
195
+ | `tagfilter` | Enables the [tagfilter extension](https://github.github.com/gfm/#disallowed-raw-html-extension-) from the GFM spec. | `true` |
196
+ | `table` | Enables the [table extension](https://github.github.com/gfm/#tables-extension-) from the GFM spec. | `true` |
197
+ | `autolink` | Enables the [autolink extension](https://github.github.com/gfm/#autolinks-extension-) from the GFM spec. | `true` |
198
+ | `tasklist` | Enables the [task list extension](https://github.github.com/gfm/#task-list-items-extension-) from the GFM spec. | `true` |
199
+ | `superscript` | Enables the superscript Comrak extension. | `false` |
200
+ | `header_ids` | Enables the header IDs Comrak extension. from the GFM spec. | `""` |
201
+ | `footnotes` | Enables the footnotes extension per `cmark-gfm`. | `false` |
202
+ | `description_lists` | Enables the description lists extension. | `false` |
203
+ | `front_matter_delimiter` | Enables the front matter extension. | `""` |
204
+ | `multiline_block_quotes` | Enables the multiline block quotes extension. | `false` |
205
+ | `math_dollars`, `math_code` | Enables the math extension. | `false` |
206
+ | `shortcodes` | Enables the shortcodes extension. | `true` |
207
+ | `wikilinks_title_before_pipe` | Enables the wikilinks extension, placing the title before the dividing pipe. | `false` |
208
+ | `wikilinks_title_after_pipe` | Enables the shortcodes extension, placing the title after the dividing pipe. | `false` |
209
+ | `underline` | Enables the underline extension. | `false` |
210
+ | `spoiler` | Enables the spoiler extension. | `false` |
211
+ | `greentext` | Enables the greentext extension. | `false` |
212
+
213
+ For more information on these options, see [the comrak documentation](https://github.com/kivikakk/comrak#usage).
214
+
215
+ ### Plugins
216
+
217
+ In addition to the possibilities provided by generic CommonMark rendering, Commonmarker also supports plugins as a means of
218
+ providing further niceties.
219
+
220
+ #### Syntax Highlighter Plugin
221
+
222
+ The library comes with [a set of pre-existing themes](https://docs.rs/syntect/5.0.0/syntect/highlighting/struct.ThemeSet.html#implementations) for highlighting code:
223
+
224
+ - `"base16-ocean.dark"`
225
+ - `"base16-eighties.dark"`
226
+ - `"base16-mocha.dark"`
227
+ - `"base16-ocean.light"`
228
+ - `"InspiredGitHub"`
229
+ - `"Solarized (dark)"`
230
+ - `"Solarized (light)"`
231
+
232
+ ````ruby
233
+ code = <<~CODE
234
+ ```ruby
235
+ def hello
236
+ puts "hello"
237
+ end
238
+ ```
239
+ CODE
187
240
 
188
- ## Output formats
241
+ # pass in a theme name from a pre-existing set
242
+ puts Commonmarker.to_html(code, plugins: { syntax_highlighter: { theme: "InspiredGitHub" } })
189
243
 
190
- Like CMark, CommonMarker can generate output in several formats: HTML, XML, plaintext, and commonmark are currently supported.
244
+ # <pre style="background-color:#ffffff;" lang="ruby"><code>
245
+ # <span style="font-weight:bold;color:#a71d5d;">def </span><span style="font-weight:bold;color:#795da3;">hello
246
+ # </span><span style="color:#62a35c;">puts </span><span style="color:#183691;">&quot;hello&quot;
247
+ # </span><span style="font-weight:bold;color:#a71d5d;">end
248
+ # </span>
249
+ # </code></pre>
250
+ ````
191
251
 
192
- ### HTML
252
+ By default, the plugin uses the `"base16-ocean.dark"` theme to syntax highlight code.
193
253
 
194
- The default output format, HTML, will be generated when calling `to_html` or using `--to=html` on the command line.
254
+ To disable this plugin, set the value to `nil`:
195
255
 
196
- ```ruby
197
- doc = CommonMarker.render_doc('*Hello* world!', :DEFAULT)
198
- puts(doc.to_html)
256
+ ````ruby
257
+ code = <<~CODE
258
+ ```ruby
259
+ def hello
260
+ puts "hello"
261
+ end
262
+ ```
263
+ CODE
199
264
 
200
- <p><em>Hello</em> world!</p>
201
- ```
265
+ Commonmarker.to_html(code, plugins: { syntax_highlighter: nil })
202
266
 
203
- ### XML
267
+ # <pre lang="ruby"><code>def hello
268
+ # puts &quot;hello&quot;
269
+ # end
270
+ # </code></pre>
271
+ ````
204
272
 
205
- XML will be generated when calling `to_xml` or using `--to=xml` on the command line.
273
+ To output CSS classes instead of `style` attributes, set the `theme` key to `""`:
206
274
 
207
- ```ruby
208
- doc = CommonMarker.render_doc('*Hello* world!', :DEFAULT)
209
- puts(doc.to_xml)
210
-
211
- <?xml version="1.0" encoding="UTF-8"?>
212
- <!DOCTYPE document SYSTEM "CommonMark.dtd">
213
- <document xmlns="http://commonmark.org/xml/1.0">
214
- <paragraph>
215
- <emph>
216
- <text xml:space="preserve">Hello</text>
217
- </emph>
218
- <text xml:space="preserve"> world!</text>
219
- </paragraph>
220
- </document>
221
- ```
275
+ ````ruby
276
+ code = <<~CODE
277
+ ```ruby
278
+ def hello
279
+ puts "hello"
280
+ end
281
+ CODE
222
282
 
223
- ### Plaintext
283
+ Commonmarker.to_html(code, plugins: { syntax_highlighter: { theme: "" } })
224
284
 
225
- Plaintext will be generated when calling `to_plaintext` or using `--to=plaintext` on the command line.
285
+ # <pre class="syntax-highlighting"><code><span class="source ruby"><span class="meta function ruby"><span class="keyword control def ruby">def</span></span><span class="meta function ruby"> # <span class="entity name function ruby">hello</span></span>
286
+ # <span class="support function builtin ruby">puts</span> <span class="string quoted double ruby"><span class="punctuation definition string begin ruby">&quot;</span>hello<span class="punctuation definition string end ruby">&quot;</span></span>
287
+ # <span class="keyword control ruby">end</span>\n</span></code></pre>
288
+ ````
226
289
 
227
- ```ruby
228
- doc = CommonMarker.render_doc('*Hello* world!', :DEFAULT)
229
- puts(doc.to_plaintext)
290
+ To use a custom theme, you can provide a `path` to a directory containing `.tmtheme` files to load:
230
291
 
231
- Hello world!
292
+ ```ruby
293
+ Commonmarker.to_html(code, plugins: { syntax_highlighter: { theme: "Monokai", path: "./themes" } })
232
294
  ```
233
295
 
234
- ### Commonmark
235
-
236
- Commonmark will be generated when calling `to_commonmark` or using `--to=commonmark` on the command line.
237
-
238
- ``` ruby
239
- text = <<-TEXT
240
- 1. I am a numeric list.
241
- 2. I continue the list.
242
- * Suddenly, an unordered list!
243
- * What fun!
244
- TEXT
296
+ ## Output formats
245
297
 
246
- doc = CommonMarker.render_doc(text, :DEFAULT)
247
- puts(doc.to_commonmark)
298
+ Commonmarker can currently only generate output in one format: HTML.
248
299
 
249
- 1. I am a numeric list.
250
- 2. I continue the list.
300
+ ### HTML
251
301
 
252
- <!-- end list -->
302
+ ```ruby
303
+ puts Commonmarker.to_html('*Hello* world!')
253
304
 
254
- - Suddenly, an unordered list\!
255
- - What fun\!
305
+ # <p><em>Hello</em> world!</p>
256
306
  ```
257
307
 
258
308
  ## Developing locally
@@ -264,25 +314,36 @@ script/bootstrap
264
314
  bundle exec rake compile
265
315
  ```
266
316
 
267
- If there were no errors, you're done! Otherwise, make sure to follow the CMark dependency instructions.
317
+ If there were no errors, you're done! Otherwise, make sure to follow the comrak dependency instructions.
268
318
 
269
319
  ## Benchmarks
270
320
 
271
- Some rough benchmarks:
272
-
273
321
  ```
274
- $ bundle exec rake benchmark
275
-
276
- input size = 11063727 bytes
277
-
278
- redcarpet
279
- 0.070000 0.020000 0.090000 ( 0.079641)
280
- github-markdown
281
- 0.070000 0.010000 0.080000 ( 0.083535)
282
- commonmarker with to_html
283
- 0.100000 0.010000 0.110000 ( 0.111947)
284
- commonmarker with ruby HtmlRenderer
285
- 1.830000 0.030000 1.860000 ( 1.866203)
286
- kramdown
287
- 4.610000 0.070000 4.680000 ( 4.678398)
322
+ bundle exec rake benchmark
323
+ input size = 11064832 bytes
324
+
325
+ ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
326
+ Warming up --------------------------------------
327
+ Markly.render_html 1.000 i/100ms
328
+ Markly::Node#to_html 1.000 i/100ms
329
+ Commonmarker.to_html 1.000 i/100ms
330
+ Commonmarker::Node.to_html
331
+ 1.000 i/100ms
332
+ Kramdown::Document#to_html
333
+ 1.000 i/100ms
334
+ Calculating -------------------------------------
335
+ Markly.render_html 15.606 (±25.6%) i/s - 71.000 in 5.047132s
336
+ Markly::Node#to_html 15.692 (±25.5%) i/s - 72.000 in 5.095810s
337
+ Commonmarker.to_html 4.482 (± 0.0%) i/s - 23.000 in 5.137680s
338
+ Commonmarker::Node.to_html
339
+ 5.092 (±19.6%) i/s - 25.000 in 5.072220s
340
+ Kramdown::Document#to_html
341
+ 0.379 (± 0.0%) i/s - 2.000 in 5.277770s
342
+
343
+ Comparison:
344
+ Markly::Node#to_html: 15.7 i/s
345
+ Markly.render_html: 15.6 i/s - same-ish: difference falls within error
346
+ Commonmarker::Node.to_html: 5.1 i/s - 3.08x slower
347
+ Commonmarker.to_html: 4.5 i/s - 3.50x slower
348
+ Kramdown::Document#to_html: 0.4 i/s - 41.40x slower
288
349
  ```
@@ -0,0 +1,20 @@
1
+ [package]
2
+ name = "commonmarker"
3
+ version = "1.0.0"
4
+ edition = "2021"
5
+ rust-version = "1.75.0"
6
+ publish = false
7
+
8
+ [dependencies]
9
+ magnus = { version = "0.7", features = ["rb-sys"] }
10
+ rb-sys = { version = "*", default-features = false, features = [
11
+ "stable-api-compiled-fallback",
12
+ ] }
13
+ comrak = { version = "0.32", features = ["shortcodes"] }
14
+ syntect = { version = "5.2", features = ["plist-load"] }
15
+ typed-arena = "2.0"
16
+ rctree = "0.6"
17
+
18
+ [lib]
19
+ name = "commonmarker"
20
+ crate-type = ["cdylib"]
@@ -1,7 +1,4 @@
1
- # frozen_string_literal: true
1
+ require "mkmf"
2
+ require "rb_sys/mkmf"
2
3
 
3
- require 'mkmf'
4
-
5
- $CFLAGS << ' -std=c99'
6
-
7
- create_makefile('commonmarker/commonmarker')
4
+ create_rust_makefile("commonmarker/commonmarker")
@@ -0,0 +1,103 @@
1
+ extern crate core;
2
+
3
+ use comrak::{markdown_to_html_with_plugins, parse_document, ComrakOptions};
4
+ use magnus::{define_module, function, r_hash::ForEach, scan_args, Error, RHash, Symbol, Value};
5
+ use node::CommonmarkerNode;
6
+ use plugins::syntax_highlighting::construct_syntax_highlighter_from_plugin;
7
+
8
+ mod options;
9
+ use options::iterate_options_hash;
10
+
11
+ mod plugins;
12
+
13
+ use typed_arena::Arena;
14
+
15
+ mod node;
16
+ mod utils;
17
+
18
+ pub const EMPTY_STR: &str = "";
19
+
20
+ fn commonmark_parse(args: &[Value]) -> Result<CommonmarkerNode, magnus::Error> {
21
+ let args = scan_args::scan_args::<_, (), (), (), _, ()>(args)?;
22
+ let (rb_commonmark,): (String,) = args.required;
23
+
24
+ let kwargs =
25
+ scan_args::get_kwargs::<_, (), (Option<RHash>,), ()>(args.keywords, &[], &["options"])?;
26
+ let (rb_options,) = kwargs.optional;
27
+
28
+ let mut comrak_options = ComrakOptions::default();
29
+
30
+ if let Some(rb_options) = rb_options {
31
+ rb_options.foreach(|key: Symbol, value: RHash| {
32
+ iterate_options_hash(&mut comrak_options, key, value)?;
33
+ Ok(ForEach::Continue)
34
+ })?;
35
+ }
36
+
37
+ let arena = Arena::new();
38
+ let root = parse_document(&arena, &rb_commonmark, &comrak_options);
39
+
40
+ CommonmarkerNode::new_from_comrak_node(root)
41
+ }
42
+
43
+ fn commonmark_to_html(args: &[Value]) -> Result<String, magnus::Error> {
44
+ let args = scan_args::scan_args::<_, (), (), (), _, ()>(args)?;
45
+ let (rb_commonmark,): (String,) = args.required;
46
+
47
+ let kwargs = scan_args::get_kwargs::<_, (), (Option<RHash>, Option<RHash>), ()>(
48
+ args.keywords,
49
+ &[],
50
+ &["options", "plugins"],
51
+ )?;
52
+ let (rb_options, rb_plugins) = kwargs.optional;
53
+
54
+ let comrak_options = match format_options(rb_options) {
55
+ Ok(options) => options,
56
+ Err(err) => return Err(err),
57
+ };
58
+
59
+ let mut comrak_plugins = comrak::Plugins::default();
60
+
61
+ let syntect_adapter = match construct_syntax_highlighter_from_plugin(rb_plugins) {
62
+ Ok(Some(adapter)) => Some(adapter),
63
+ Ok(None) => None,
64
+ Err(err) => return Err(err),
65
+ };
66
+
67
+ match syntect_adapter {
68
+ Some(ref adapter) => comrak_plugins.render.codefence_syntax_highlighter = Some(adapter),
69
+ None => comrak_plugins.render.codefence_syntax_highlighter = None,
70
+ }
71
+
72
+ Ok(markdown_to_html_with_plugins(
73
+ &rb_commonmark,
74
+ &comrak_options,
75
+ &comrak_plugins,
76
+ ))
77
+ }
78
+
79
+ fn format_options<'c>(rb_options: Option<RHash>) -> Result<comrak::Options<'c>, magnus::Error> {
80
+ let mut comrak_options = ComrakOptions::default();
81
+
82
+ if let Some(rb_options) = rb_options {
83
+ rb_options.foreach(|key: Symbol, value: RHash| {
84
+ iterate_options_hash(&mut comrak_options, key, value)?;
85
+ Ok(ForEach::Continue)
86
+ })?;
87
+ }
88
+
89
+ Ok(comrak_options)
90
+ }
91
+
92
+ #[magnus::init]
93
+ fn init() -> Result<(), Error> {
94
+ let m_commonmarker = define_module("Commonmarker")?;
95
+
96
+ m_commonmarker.define_module_function("commonmark_parse", function!(commonmark_parse, -1))?;
97
+ m_commonmarker
98
+ .define_module_function("commonmark_to_html", function!(commonmark_to_html, -1))?;
99
+
100
+ node::init(m_commonmarker).expect("cannot define Commonmarker::Node class");
101
+
102
+ Ok(())
103
+ }