commonmarker 0.23.10 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1221 -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 +1160 -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 +38 -186
  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.30", 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(rb_options: Option<RHash>) -> Result<comrak::Options, 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
+ }