commonmarker 0.23.10 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Cargo.lock +1221 -0
- data/Cargo.toml +7 -0
- data/README.md +233 -172
- data/ext/commonmarker/Cargo.toml +20 -0
- data/ext/commonmarker/extconf.rb +3 -6
- data/ext/commonmarker/src/lib.rs +103 -0
- data/ext/commonmarker/src/node.rs +1160 -0
- data/ext/commonmarker/src/options.rs +216 -0
- data/ext/commonmarker/src/plugins/syntax_highlighting.rs +166 -0
- data/ext/commonmarker/src/plugins.rs +6 -0
- data/ext/commonmarker/src/utils.rs +8 -0
- data/lib/commonmarker/config.rb +91 -40
- data/lib/commonmarker/constants.rb +7 -0
- data/lib/commonmarker/extension.rb +14 -0
- data/lib/commonmarker/node/ast.rb +8 -0
- data/lib/commonmarker/node/inspect.rb +14 -4
- data/lib/commonmarker/node.rb +29 -47
- data/lib/commonmarker/renderer.rb +1 -127
- data/lib/commonmarker/utils.rb +22 -0
- data/lib/commonmarker/version.rb +2 -2
- data/lib/commonmarker.rb +27 -25
- metadata +38 -186
- data/Rakefile +0 -109
- data/bin/commonmarker +0 -118
- data/commonmarker.gemspec +0 -38
- data/ext/commonmarker/arena.c +0 -104
- data/ext/commonmarker/autolink.c +0 -508
- data/ext/commonmarker/autolink.h +0 -8
- data/ext/commonmarker/blocks.c +0 -1622
- data/ext/commonmarker/buffer.c +0 -278
- data/ext/commonmarker/buffer.h +0 -116
- data/ext/commonmarker/case_fold_switch.inc +0 -4327
- data/ext/commonmarker/chunk.h +0 -135
- data/ext/commonmarker/cmark-gfm-core-extensions.h +0 -54
- data/ext/commonmarker/cmark-gfm-extension_api.h +0 -737
- data/ext/commonmarker/cmark-gfm-extensions_export.h +0 -42
- data/ext/commonmarker/cmark-gfm.h +0 -833
- data/ext/commonmarker/cmark-gfm_export.h +0 -42
- data/ext/commonmarker/cmark-gfm_version.h +0 -7
- data/ext/commonmarker/cmark.c +0 -55
- data/ext/commonmarker/cmark_ctype.c +0 -44
- data/ext/commonmarker/cmark_ctype.h +0 -33
- data/ext/commonmarker/commonmark.c +0 -514
- data/ext/commonmarker/commonmarker.c +0 -1308
- data/ext/commonmarker/commonmarker.h +0 -16
- data/ext/commonmarker/config.h +0 -76
- data/ext/commonmarker/core-extensions.c +0 -27
- data/ext/commonmarker/entities.inc +0 -2138
- data/ext/commonmarker/ext_scanners.c +0 -879
- data/ext/commonmarker/ext_scanners.h +0 -24
- data/ext/commonmarker/footnotes.c +0 -63
- data/ext/commonmarker/footnotes.h +0 -27
- data/ext/commonmarker/houdini.h +0 -57
- data/ext/commonmarker/houdini_href_e.c +0 -100
- data/ext/commonmarker/houdini_html_e.c +0 -66
- data/ext/commonmarker/houdini_html_u.c +0 -149
- data/ext/commonmarker/html.c +0 -502
- data/ext/commonmarker/html.h +0 -27
- data/ext/commonmarker/inlines.c +0 -1788
- data/ext/commonmarker/inlines.h +0 -29
- data/ext/commonmarker/iterator.c +0 -159
- data/ext/commonmarker/iterator.h +0 -26
- data/ext/commonmarker/latex.c +0 -468
- data/ext/commonmarker/linked_list.c +0 -37
- data/ext/commonmarker/man.c +0 -274
- data/ext/commonmarker/map.c +0 -129
- data/ext/commonmarker/map.h +0 -44
- data/ext/commonmarker/node.c +0 -1045
- data/ext/commonmarker/node.h +0 -167
- data/ext/commonmarker/parser.h +0 -59
- data/ext/commonmarker/plaintext.c +0 -218
- data/ext/commonmarker/plugin.c +0 -36
- data/ext/commonmarker/plugin.h +0 -34
- data/ext/commonmarker/references.c +0 -43
- data/ext/commonmarker/references.h +0 -26
- data/ext/commonmarker/registry.c +0 -63
- data/ext/commonmarker/registry.h +0 -24
- data/ext/commonmarker/render.c +0 -213
- data/ext/commonmarker/render.h +0 -62
- data/ext/commonmarker/scanners.c +0 -14056
- data/ext/commonmarker/scanners.h +0 -70
- data/ext/commonmarker/scanners.re +0 -341
- data/ext/commonmarker/strikethrough.c +0 -167
- data/ext/commonmarker/strikethrough.h +0 -9
- data/ext/commonmarker/syntax_extension.c +0 -149
- data/ext/commonmarker/syntax_extension.h +0 -34
- data/ext/commonmarker/table.c +0 -917
- data/ext/commonmarker/table.h +0 -12
- data/ext/commonmarker/tagfilter.c +0 -60
- data/ext/commonmarker/tagfilter.h +0 -8
- data/ext/commonmarker/tasklist.c +0 -156
- data/ext/commonmarker/tasklist.h +0 -8
- data/ext/commonmarker/utf8.c +0 -317
- data/ext/commonmarker/utf8.h +0 -35
- data/ext/commonmarker/xml.c +0 -182
- data/lib/commonmarker/renderer/html_renderer.rb +0 -256
data/Cargo.toml
ADDED
data/README.md
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
#
|
1
|
+
# Commonmarker
|
2
2
|
|
3
|
-
|
3
|
+
Ruby wrapper for Rust's [comrak](https://github.com/kivikakk/comrak) crate.
|
4
4
|
|
5
|
-
|
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
|
-
|
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 `
|
30
|
+
Call `to_html` on a string to convert it to HTML:
|
29
31
|
|
30
|
-
```
|
32
|
+
```ruby
|
31
33
|
require 'commonmarker'
|
32
|
-
|
33
|
-
|
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 `
|
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
|
-
```
|
46
|
+
```ruby
|
43
47
|
require 'commonmarker'
|
44
48
|
|
45
|
-
doc =
|
46
|
-
|
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, :
|
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
|
-
|
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
|
92
|
+
- `each` will iterate on a node's direct children, but no further.
|
61
93
|
|
62
|
-
```
|
94
|
+
```ruby
|
63
95
|
require 'commonmarker'
|
64
96
|
|
65
|
-
# parse
|
66
|
-
doc =
|
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
|
-
|
118
|
+
#### Example: Converting a document back into raw CommonMark
|
96
119
|
|
97
|
-
You can
|
120
|
+
You can use `to_commonmark` on a node to render it as raw text:
|
98
121
|
|
99
|
-
```
|
100
|
-
|
101
|
-
def initialize
|
102
|
-
super
|
103
|
-
@headerid = 1
|
104
|
-
end
|
122
|
+
```ruby
|
123
|
+
require 'commonmarker'
|
105
124
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
116
|
-
|
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
|
-
|
142
|
+
### Options
|
127
143
|
|
128
|
-
|
144
|
+
Commonmarker accepts the same parse, render, and extensions options that comrak does, as a hash dictionary with symbol keys:
|
129
145
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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
|
-
|
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
|
-
|
171
|
-
CommonMarker.render_html("\"'Shelob' is my name.\"", [:HARDBREAKS, :SOURCEPOS])
|
172
|
-
```
|
155
|
+
### Parse options
|
173
156
|
|
174
|
-
|
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
|
-
|
164
|
+
### Render options
|
177
165
|
|
178
|
-
|
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
|
-
|
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
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
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
|
-
|
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
|
-
|
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;">"hello"
|
247
|
+
# </span><span style="font-weight:bold;color:#a71d5d;">end
|
248
|
+
# </span>
|
249
|
+
# </code></pre>
|
250
|
+
````
|
191
251
|
|
192
|
-
|
252
|
+
By default, the plugin uses the `"base16-ocean.dark"` theme to syntax highlight code.
|
193
253
|
|
194
|
-
|
254
|
+
To disable this plugin, set the value to `nil`:
|
195
255
|
|
196
|
-
|
197
|
-
|
198
|
-
|
256
|
+
````ruby
|
257
|
+
code = <<~CODE
|
258
|
+
```ruby
|
259
|
+
def hello
|
260
|
+
puts "hello"
|
261
|
+
end
|
262
|
+
```
|
263
|
+
CODE
|
199
264
|
|
200
|
-
|
201
|
-
```
|
265
|
+
Commonmarker.to_html(code, plugins: { syntax_highlighter: nil })
|
202
266
|
|
203
|
-
|
267
|
+
# <pre lang="ruby"><code>def hello
|
268
|
+
# puts "hello"
|
269
|
+
# end
|
270
|
+
# </code></pre>
|
271
|
+
````
|
204
272
|
|
205
|
-
|
273
|
+
To output CSS classes instead of `style` attributes, set the `theme` key to `""`:
|
206
274
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
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
|
-
|
283
|
+
Commonmarker.to_html(code, plugins: { syntax_highlighter: { theme: "" } })
|
224
284
|
|
225
|
-
|
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">"</span>hello<span class="punctuation definition string end ruby">"</span></span>
|
287
|
+
# <span class="keyword control ruby">end</span>\n</span></code></pre>
|
288
|
+
````
|
226
289
|
|
227
|
-
|
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
|
-
|
292
|
+
```ruby
|
293
|
+
Commonmarker.to_html(code, plugins: { syntax_highlighter: { theme: "Monokai", path: "./themes" } })
|
232
294
|
```
|
233
295
|
|
234
|
-
|
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
|
-
|
247
|
-
puts(doc.to_commonmark)
|
298
|
+
Commonmarker can currently only generate output in one format: HTML.
|
248
299
|
|
249
|
-
|
250
|
-
2. I continue the list.
|
300
|
+
### HTML
|
251
301
|
|
252
|
-
|
302
|
+
```ruby
|
303
|
+
puts Commonmarker.to_html('*Hello* world!')
|
253
304
|
|
254
|
-
|
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
|
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
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
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"]
|
data/ext/commonmarker/extconf.rb
CHANGED
@@ -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
|
+
}
|