commonmarker 2.10.0 → 2.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Cargo.lock +83 -40
- data/README.md +2 -2
- data/ext/commonmarker/Cargo.toml +3 -4
- data/ext/commonmarker/src/lib.rs +12 -2
- data/ext/commonmarker/src/nesting.rs +71 -0
- data/ext/commonmarker/src/node.rs +80 -87
- data/ext/commonmarker/src/options.rs +0 -4
- data/ext/commonmarker/src/plugins/syntax_highlighting.rs +9 -5
- data/ext/commonmarker/src/rctree.rs +841 -0
- data/lib/commonmarker/config.rb +1 -1
- data/lib/commonmarker/errors.rb +7 -0
- data/lib/commonmarker/node.rb +17 -4
- data/lib/commonmarker/version.rb +1 -1
- data/lib/commonmarker.rb +1 -0
- metadata +5 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
use crate::rctree::Node;
|
|
1
2
|
use comrak::arena_tree::Node as ComrakNode;
|
|
2
3
|
use comrak::nodes::{
|
|
3
4
|
AlertType, Ast as ComrakAst, AstNode as ComrakAstNode, ListDelimType, ListType, NodeAlert,
|
|
@@ -8,7 +9,6 @@ use comrak::nodes::{
|
|
|
8
9
|
};
|
|
9
10
|
use magnus::{function, method, scan_args, Module, Object, RHash, RModule, Symbol, Value};
|
|
10
11
|
use magnus::{RArray, Ruby};
|
|
11
|
-
use rctree::Node;
|
|
12
12
|
use typed_arena::Arena;
|
|
13
13
|
|
|
14
14
|
use std::cell::RefCell;
|
|
@@ -599,42 +599,50 @@ impl CommonmarkerNode {
|
|
|
599
599
|
})
|
|
600
600
|
}
|
|
601
601
|
|
|
602
|
+
/// Copies a comrak tree into a Ruby-visible tree, failing if any node sits
|
|
603
|
+
/// more than `max_nesting_depth` levels below the root.
|
|
602
604
|
pub fn new_from_comrak_node<'a>(
|
|
605
|
+
ruby: &Ruby,
|
|
603
606
|
comrak_root_node: &'a ComrakAstNode<'a>,
|
|
607
|
+
max_nesting_depth: usize,
|
|
604
608
|
) -> Result<CommonmarkerNode, magnus::Error> {
|
|
605
|
-
let
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
609
|
+
let commonmarker_root_node = Node::new(CommonmarkerAst {
|
|
610
|
+
data: comrak_root_node.data.borrow().clone(),
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
// Depth-first walk with an explicit stack instead of recursion, so that
|
|
614
|
+
// deeply nested input (e.g. thousands of nested blockquotes) cannot
|
|
615
|
+
// overflow the native stack. Each entry pairs an iterator over a comrak
|
|
616
|
+
// node's remaining children with the copy of that node the children get
|
|
617
|
+
// appended to.
|
|
618
|
+
let mut stack = vec![(comrak_root_node.children(), commonmarker_root_node.clone())];
|
|
619
|
+
|
|
620
|
+
loop {
|
|
621
|
+
// The stack holds one entry per ancestor, so its length is the
|
|
622
|
+
// depth of the next child produced.
|
|
623
|
+
let depth = stack.len();
|
|
624
|
+
let Some((children, parent)) = stack.last_mut() else {
|
|
625
|
+
break;
|
|
626
|
+
};
|
|
627
|
+
let Some(comrak_child) = children.next() else {
|
|
628
|
+
stack.pop();
|
|
629
|
+
continue;
|
|
613
630
|
};
|
|
614
631
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
Ok(_) => {}
|
|
618
|
-
Err(e) => {
|
|
619
|
-
panic!("cannot append node: {}", e);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
632
|
+
if depth > max_nesting_depth {
|
|
633
|
+
return Err(crate::nesting::exceeded_error(ruby, max_nesting_depth));
|
|
622
634
|
}
|
|
623
635
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
};
|
|
630
|
-
|
|
631
|
-
for child in comrak_root_node.children() {
|
|
632
|
-
let new_child = iter_nodes(child);
|
|
633
|
-
|
|
634
|
-
commonmarker_root_node.append_child_node(&new_child)?;
|
|
636
|
+
let node = Node::new(CommonmarkerAst {
|
|
637
|
+
data: comrak_child.data.borrow().clone(),
|
|
638
|
+
});
|
|
639
|
+
parent.append(node.clone());
|
|
640
|
+
stack.push((comrak_child.children(), node));
|
|
635
641
|
}
|
|
636
642
|
|
|
637
|
-
Ok(
|
|
643
|
+
Ok(CommonmarkerNode {
|
|
644
|
+
inner: commonmarker_root_node,
|
|
645
|
+
})
|
|
638
646
|
}
|
|
639
647
|
|
|
640
648
|
fn type_to_symbol(ruby: &Ruby, rb_self: &Self) -> Symbol {
|
|
@@ -1127,6 +1135,39 @@ impl CommonmarkerNode {
|
|
|
1127
1135
|
}
|
|
1128
1136
|
}
|
|
1129
1137
|
|
|
1138
|
+
/// Copies this node and its descendants into `arena` as a comrak tree so that
|
|
1139
|
+
/// comrak's formatters can render it.
|
|
1140
|
+
///
|
|
1141
|
+
/// Depth-first walk with an explicit stack instead of recursion, so that
|
|
1142
|
+
/// deeply nested input cannot overflow the native stack. Each entry pairs an
|
|
1143
|
+
/// iterator over a node's remaining children with the copy of that node the
|
|
1144
|
+
/// children get appended to.
|
|
1145
|
+
fn to_comrak_node<'a>(&self, arena: &'a Arena<ComrakAstNode<'a>>) -> &'a ComrakAstNode<'a> {
|
|
1146
|
+
let comrak_root_node: &'a ComrakAstNode<'a> = arena.alloc(ComrakNode::new(RefCell::new(
|
|
1147
|
+
self.inner.borrow().data.clone(),
|
|
1148
|
+
)));
|
|
1149
|
+
|
|
1150
|
+
let mut stack = vec![(self.inner.children(), comrak_root_node)];
|
|
1151
|
+
|
|
1152
|
+
loop {
|
|
1153
|
+
let Some((children, parent)) = stack.last_mut() else {
|
|
1154
|
+
break;
|
|
1155
|
+
};
|
|
1156
|
+
let parent: &'a ComrakAstNode<'a> = parent;
|
|
1157
|
+
let Some(child) = children.next() else {
|
|
1158
|
+
stack.pop();
|
|
1159
|
+
continue;
|
|
1160
|
+
};
|
|
1161
|
+
|
|
1162
|
+
let comrak_node: &'a ComrakAstNode<'a> =
|
|
1163
|
+
arena.alloc(ComrakNode::new(RefCell::new(child.borrow().data.clone())));
|
|
1164
|
+
parent.append(comrak_node);
|
|
1165
|
+
stack.push((child.children(), comrak_node));
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
comrak_root_node
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1130
1171
|
fn to_html(ruby: &Ruby, rb_self: &Self, args: &[Value]) -> Result<String, magnus::Error> {
|
|
1131
1172
|
let args = scan_args::scan_args::<(), (), (), (), _, ()>(args)?;
|
|
1132
1173
|
|
|
@@ -1171,42 +1212,18 @@ impl CommonmarkerNode {
|
|
|
1171
1212
|
};
|
|
1172
1213
|
|
|
1173
1214
|
match syntect_adapter {
|
|
1174
|
-
Some(ref adapter) =>
|
|
1215
|
+
Some(ref adapter) => {
|
|
1216
|
+
comrak_plugins.render.codefence_syntax_highlighter = Some(adapter.as_ref())
|
|
1217
|
+
}
|
|
1175
1218
|
None => comrak_plugins.render.codefence_syntax_highlighter = None,
|
|
1176
1219
|
}
|
|
1177
1220
|
|
|
1178
1221
|
let arena: Arena<ComrakAstNode> = Arena::new();
|
|
1179
|
-
|
|
1180
|
-
arena: &'a Arena<comrak::arena_tree::Node<'a, RefCell<ComrakAst>>>,
|
|
1181
|
-
node: &CommonmarkerNode,
|
|
1182
|
-
) -> &'a comrak::arena_tree::Node<'a, std::cell::RefCell<comrak::nodes::Ast>> {
|
|
1183
|
-
let comrak_node: &'a mut ComrakAstNode = arena.alloc(ComrakNode::new(RefCell::new(
|
|
1184
|
-
node.inner.borrow().data.clone(),
|
|
1185
|
-
)));
|
|
1186
|
-
|
|
1187
|
-
for c in node.inner.children() {
|
|
1188
|
-
let child = CommonmarkerNode { inner: c };
|
|
1189
|
-
let child_node = iter_nodes(arena, &child);
|
|
1190
|
-
comrak_node.append(child_node);
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
comrak_node
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
let comrak_root_node: ComrakNode<RefCell<ComrakAst>> =
|
|
1197
|
-
ComrakNode::new(RefCell::new(rb_self.inner.borrow().data.clone()));
|
|
1198
|
-
|
|
1199
|
-
for c in rb_self.inner.children() {
|
|
1200
|
-
let child = CommonmarkerNode { inner: c };
|
|
1201
|
-
|
|
1202
|
-
let new_child = iter_nodes(&arena, &child);
|
|
1203
|
-
|
|
1204
|
-
comrak_root_node.append(new_child);
|
|
1205
|
-
}
|
|
1222
|
+
let comrak_root_node = rb_self.to_comrak_node(&arena);
|
|
1206
1223
|
|
|
1207
1224
|
let mut output = String::new();
|
|
1208
1225
|
match comrak::format_html_with_plugins(
|
|
1209
|
-
|
|
1226
|
+
comrak_root_node,
|
|
1210
1227
|
&comrak_options,
|
|
1211
1228
|
&mut output,
|
|
1212
1229
|
&comrak_plugins,
|
|
@@ -1267,42 +1284,18 @@ impl CommonmarkerNode {
|
|
|
1267
1284
|
};
|
|
1268
1285
|
|
|
1269
1286
|
match syntect_adapter {
|
|
1270
|
-
Some(ref adapter) =>
|
|
1287
|
+
Some(ref adapter) => {
|
|
1288
|
+
comrak_plugins.render.codefence_syntax_highlighter = Some(adapter.as_ref())
|
|
1289
|
+
}
|
|
1271
1290
|
None => comrak_plugins.render.codefence_syntax_highlighter = None,
|
|
1272
1291
|
}
|
|
1273
1292
|
|
|
1274
1293
|
let arena: Arena<ComrakAstNode> = Arena::new();
|
|
1275
|
-
|
|
1276
|
-
arena: &'a Arena<comrak::arena_tree::Node<'a, RefCell<ComrakAst>>>,
|
|
1277
|
-
node: &CommonmarkerNode,
|
|
1278
|
-
) -> &'a comrak::arena_tree::Node<'a, std::cell::RefCell<comrak::nodes::Ast>> {
|
|
1279
|
-
let comrak_node: &'a mut ComrakAstNode = arena.alloc(ComrakNode::new(RefCell::new(
|
|
1280
|
-
node.inner.borrow().data.clone(),
|
|
1281
|
-
)));
|
|
1282
|
-
|
|
1283
|
-
for c in node.inner.children() {
|
|
1284
|
-
let child = CommonmarkerNode { inner: c };
|
|
1285
|
-
let child_node = iter_nodes(arena, &child);
|
|
1286
|
-
comrak_node.append(child_node);
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
comrak_node
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
let comrak_root_node: ComrakNode<RefCell<ComrakAst>> =
|
|
1293
|
-
ComrakNode::new(RefCell::new(rb_self.inner.borrow().data.clone()));
|
|
1294
|
-
|
|
1295
|
-
for c in rb_self.inner.children() {
|
|
1296
|
-
let child = CommonmarkerNode { inner: c };
|
|
1297
|
-
|
|
1298
|
-
let new_child = iter_nodes(&arena, &child);
|
|
1299
|
-
|
|
1300
|
-
comrak_root_node.append(new_child);
|
|
1301
|
-
}
|
|
1294
|
+
let comrak_root_node = rb_self.to_comrak_node(&arena);
|
|
1302
1295
|
|
|
1303
1296
|
let mut output = String::new();
|
|
1304
1297
|
match comrak::format_commonmark_with_plugins(
|
|
1305
|
-
|
|
1298
|
+
comrak_root_node,
|
|
1306
1299
|
&comrak_options,
|
|
1307
1300
|
&mut output,
|
|
1308
1301
|
&comrak_plugins,
|
|
@@ -1311,7 +1304,7 @@ impl CommonmarkerNode {
|
|
|
1311
1304
|
Err(e) => {
|
|
1312
1305
|
return Err(magnus::Error::new(
|
|
1313
1306
|
ruby.exception_runtime_error(),
|
|
1314
|
-
format!("cannot convert into
|
|
1307
|
+
format!("cannot convert into commonmark: {}", e),
|
|
1315
1308
|
));
|
|
1316
1309
|
}
|
|
1317
1310
|
}
|
|
@@ -117,7 +117,6 @@ pub fn iterate_render_options(comrak_options: &mut comrak::options::Render, opti
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
const EXTENSION_STRIKETHROUGH: &str = "strikethrough";
|
|
120
|
-
const EXTENSION_TAGFILTER: &str = "tagfilter";
|
|
121
120
|
const EXTENSION_TABLE: &str = "table";
|
|
122
121
|
const EXTENSION_AUTOLINK: &str = "autolink";
|
|
123
122
|
const EXTENSION_TASKLIST: &str = "tasklist";
|
|
@@ -156,9 +155,6 @@ pub fn iterate_extension_options(
|
|
|
156
155
|
Cow::Borrowed(EXTENSION_STRIKETHROUGH) => {
|
|
157
156
|
comrak_options.strikethrough = TryConvert::try_convert(value)?;
|
|
158
157
|
}
|
|
159
|
-
Cow::Borrowed(EXTENSION_TAGFILTER) => {
|
|
160
|
-
comrak_options.tagfilter = TryConvert::try_convert(value)?;
|
|
161
|
-
}
|
|
162
158
|
Cow::Borrowed(EXTENSION_TABLE) => {
|
|
163
159
|
comrak_options.table = TryConvert::try_convert(value)?;
|
|
164
160
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
use std::path::PathBuf;
|
|
2
|
+
use std::sync::{Arc, OnceLock};
|
|
2
3
|
|
|
3
4
|
use comrak::plugins::syntect::{SyntectAdapter, SyntectAdapterBuilder};
|
|
4
5
|
|
|
@@ -11,7 +12,7 @@ use crate::EMPTY_STR;
|
|
|
11
12
|
pub fn construct_syntax_highlighter_from_plugin(
|
|
12
13
|
ruby: &Ruby,
|
|
13
14
|
rb_plugins: Option<RHash>,
|
|
14
|
-
) -> Result<Option<SyntectAdapter
|
|
15
|
+
) -> Result<Option<Arc<SyntectAdapter>>, magnus::Error> {
|
|
15
16
|
match rb_plugins {
|
|
16
17
|
None => Ok(None),
|
|
17
18
|
Some(rb_plugins) => {
|
|
@@ -34,8 +35,11 @@ pub fn construct_syntax_highlighter_from_plugin(
|
|
|
34
35
|
Some(theme) => {
|
|
35
36
|
if theme.is_empty() {
|
|
36
37
|
// no theme? uss css classes
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
// Avoid reloading syntax sets and recompiling regexes for every CSS-mode render.
|
|
39
|
+
static CSS_ADAPTER: OnceLock<Arc<SyntectAdapter>> = OnceLock::new();
|
|
40
|
+
Ok(Some(Arc::clone(
|
|
41
|
+
CSS_ADAPTER.get_or_init(|| Arc::new(SyntectAdapter::new(None))),
|
|
42
|
+
)))
|
|
39
43
|
} else {
|
|
40
44
|
let path = match rb_plugins
|
|
41
45
|
.get(ruby.to_symbol(super::SYNTAX_HIGHLIGHTER_PLUGIN))
|
|
@@ -80,7 +84,7 @@ pub fn construct_syntax_highlighter_from_plugin(
|
|
|
80
84
|
|
|
81
85
|
adapter = builder.theme_set(ts).theme(&theme).build();
|
|
82
86
|
|
|
83
|
-
Ok(Some(adapter))
|
|
87
|
+
Ok(Some(Arc::new(adapter)))
|
|
84
88
|
} else {
|
|
85
89
|
// no path? default theme lookup
|
|
86
90
|
ThemeSet::load_defaults()
|
|
@@ -93,7 +97,7 @@ pub fn construct_syntax_highlighter_from_plugin(
|
|
|
93
97
|
)
|
|
94
98
|
})?;
|
|
95
99
|
adapter = SyntectAdapter::new(Some(&theme));
|
|
96
|
-
Ok(Some(adapter))
|
|
100
|
+
Ok(Some(Arc::new(adapter)))
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
103
|
}
|