react-native-nitro-markdown 0.5.2 → 0.5.4
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.
- package/README.md +311 -669
- package/android/CMakeLists.txt +8 -1
- package/android/build.gradle +9 -2
- package/android/consumer-rules.pro +31 -0
- package/android/gradle.properties +2 -0
- package/android/src/main/java/com/margelo/nitro/com/nitromarkdown/HybridMarkdownSession.kt +68 -22
- package/android/src/main/java/com/nitromarkdown/NitroMarkdownPackage.kt +6 -18
- package/cpp/bindings/HybridMarkdownParser.cpp +40 -12
- package/cpp/bindings/HybridMarkdownParser.hpp +4 -4
- package/cpp/bindings/HybridMarkdownSession.cpp +2 -0
- package/cpp/core/MD4CParser.cpp +147 -86
- package/cpp/core/MarkdownSessionCore.cpp +2 -0
- package/cpp/core/MarkdownTypes.hpp +1 -1
- package/ios/HybridMarkdownSession.swift +89 -46
- package/lib/commonjs/headless.js +34 -8
- package/lib/commonjs/headless.js.map +1 -1
- package/lib/commonjs/index.js +48 -38
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/markdown-stream.js +1 -1
- package/lib/commonjs/markdown-stream.js.map +1 -1
- package/lib/commonjs/markdown.js +57 -16
- package/lib/commonjs/markdown.js.map +1 -1
- package/lib/commonjs/renderers/blockquote.js +15 -13
- package/lib/commonjs/renderers/blockquote.js.map +1 -1
- package/lib/commonjs/renderers/code.js +58 -54
- package/lib/commonjs/renderers/code.js.map +1 -1
- package/lib/commonjs/renderers/heading.js +48 -46
- package/lib/commonjs/renderers/heading.js.map +1 -1
- package/lib/commonjs/renderers/horizontal-rule.js +10 -8
- package/lib/commonjs/renderers/horizontal-rule.js.map +1 -1
- package/lib/commonjs/renderers/image.js +18 -4
- package/lib/commonjs/renderers/image.js.map +1 -1
- package/lib/commonjs/renderers/link.js +7 -2
- package/lib/commonjs/renderers/link.js.map +1 -1
- package/lib/commonjs/renderers/list.js +75 -68
- package/lib/commonjs/renderers/list.js.map +1 -1
- package/lib/commonjs/renderers/math.js +8 -5
- package/lib/commonjs/renderers/math.js.map +1 -1
- package/lib/commonjs/renderers/paragraph.js +15 -13
- package/lib/commonjs/renderers/paragraph.js.map +1 -1
- package/lib/commonjs/renderers/style-cache.js +14 -0
- package/lib/commonjs/renderers/style-cache.js.map +1 -0
- package/lib/commonjs/renderers/table/cell-content.js +1 -1
- package/lib/commonjs/renderers/table/cell-content.js.map +1 -1
- package/lib/commonjs/renderers/table/index.js +17 -6
- package/lib/commonjs/renderers/table/index.js.map +1 -1
- package/lib/commonjs/theme.js +7 -7
- package/lib/commonjs/theme.js.map +1 -1
- package/lib/commonjs/utils/code-highlight.js +24 -25
- package/lib/commonjs/utils/code-highlight.js.map +1 -1
- package/lib/module/headless.js +35 -7
- package/lib/module/headless.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/markdown-stream.js +1 -1
- package/lib/module/markdown-stream.js.map +1 -1
- package/lib/module/markdown.js +58 -17
- package/lib/module/markdown.js.map +1 -1
- package/lib/module/renderers/blockquote.js +15 -13
- package/lib/module/renderers/blockquote.js.map +1 -1
- package/lib/module/renderers/code.js +58 -54
- package/lib/module/renderers/code.js.map +1 -1
- package/lib/module/renderers/heading.js +48 -46
- package/lib/module/renderers/heading.js.map +1 -1
- package/lib/module/renderers/horizontal-rule.js +10 -8
- package/lib/module/renderers/horizontal-rule.js.map +1 -1
- package/lib/module/renderers/image.js +19 -5
- package/lib/module/renderers/image.js.map +1 -1
- package/lib/module/renderers/link.js +7 -2
- package/lib/module/renderers/link.js.map +1 -1
- package/lib/module/renderers/list.js +75 -68
- package/lib/module/renderers/list.js.map +1 -1
- package/lib/module/renderers/math.js +8 -5
- package/lib/module/renderers/math.js.map +1 -1
- package/lib/module/renderers/paragraph.js +15 -13
- package/lib/module/renderers/paragraph.js.map +1 -1
- package/lib/module/renderers/style-cache.js +10 -0
- package/lib/module/renderers/style-cache.js.map +1 -0
- package/lib/module/renderers/table/cell-content.js +1 -1
- package/lib/module/renderers/table/cell-content.js.map +1 -1
- package/lib/module/renderers/table/index.js +17 -6
- package/lib/module/renderers/table/index.js.map +1 -1
- package/lib/module/theme.js +7 -7
- package/lib/module/theme.js.map +1 -1
- package/lib/module/utils/code-highlight.js +24 -25
- package/lib/module/utils/code-highlight.js.map +1 -1
- package/lib/typescript/commonjs/Markdown.nitro.d.ts +1 -0
- package/lib/typescript/commonjs/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/commonjs/headless.d.ts +10 -2
- package/lib/typescript/commonjs/headless.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +3 -2
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/markdown-stream.d.ts.map +1 -1
- package/lib/typescript/commonjs/markdown.d.ts +8 -3
- package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/blockquote.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/blockquote.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/code.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/heading.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/horizontal-rule.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/image.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/link.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/list.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/list.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/math.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/paragraph.d.ts +1 -1
- package/lib/typescript/commonjs/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/style-cache.d.ts +3 -0
- package/lib/typescript/commonjs/renderers/style-cache.d.ts.map +1 -0
- package/lib/typescript/commonjs/renderers/table/cell-content.d.ts +4 -3
- package/lib/typescript/commonjs/renderers/table/cell-content.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/table/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/code-highlight.d.ts +1 -1
- package/lib/typescript/commonjs/utils/code-highlight.d.ts.map +1 -1
- package/lib/typescript/module/Markdown.nitro.d.ts +1 -0
- package/lib/typescript/module/Markdown.nitro.d.ts.map +1 -1
- package/lib/typescript/module/headless.d.ts +10 -2
- package/lib/typescript/module/headless.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +3 -2
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/markdown-stream.d.ts.map +1 -1
- package/lib/typescript/module/markdown.d.ts +8 -3
- package/lib/typescript/module/markdown.d.ts.map +1 -1
- package/lib/typescript/module/renderers/blockquote.d.ts +1 -1
- package/lib/typescript/module/renderers/blockquote.d.ts.map +1 -1
- package/lib/typescript/module/renderers/code.d.ts.map +1 -1
- package/lib/typescript/module/renderers/heading.d.ts +1 -1
- package/lib/typescript/module/renderers/heading.d.ts.map +1 -1
- package/lib/typescript/module/renderers/horizontal-rule.d.ts +1 -1
- package/lib/typescript/module/renderers/horizontal-rule.d.ts.map +1 -1
- package/lib/typescript/module/renderers/image.d.ts.map +1 -1
- package/lib/typescript/module/renderers/link.d.ts.map +1 -1
- package/lib/typescript/module/renderers/list.d.ts +1 -1
- package/lib/typescript/module/renderers/list.d.ts.map +1 -1
- package/lib/typescript/module/renderers/math.d.ts +1 -1
- package/lib/typescript/module/renderers/math.d.ts.map +1 -1
- package/lib/typescript/module/renderers/paragraph.d.ts +1 -1
- package/lib/typescript/module/renderers/paragraph.d.ts.map +1 -1
- package/lib/typescript/module/renderers/style-cache.d.ts +3 -0
- package/lib/typescript/module/renderers/style-cache.d.ts.map +1 -0
- package/lib/typescript/module/renderers/table/cell-content.d.ts +4 -3
- package/lib/typescript/module/renderers/table/cell-content.d.ts.map +1 -1
- package/lib/typescript/module/renderers/table/index.d.ts.map +1 -1
- package/lib/typescript/module/theme.d.ts.map +1 -1
- package/lib/typescript/module/utils/code-highlight.d.ts +1 -1
- package/lib/typescript/module/utils/code-highlight.d.ts.map +1 -1
- package/nitro.json +12 -3
- package/nitrogen/generated/android/NitroMarkdownOnLoad.cpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void.hpp +2 -2
- package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +2 -2
- package/nitrogen/generated/android/c++/JHybridMarkdownSessionSpec.hpp +2 -2
- package/nitrogen/generated/ios/NitroMarkdown+autolinking.rb +2 -0
- package/nitrogen/generated/shared/c++/ParserOptions.hpp +6 -2
- package/package.json +8 -6
- package/react-native-nitro-markdown.podspec +3 -0
- package/src/Markdown.nitro.ts +1 -0
- package/src/headless.ts +58 -8
- package/src/index.ts +16 -2
- package/src/markdown-stream.tsx +1 -0
- package/src/markdown.tsx +108 -23
- package/src/renderers/blockquote.tsx +22 -17
- package/src/renderers/code.tsx +76 -57
- package/src/renderers/heading.tsx +60 -54
- package/src/renderers/horizontal-rule.tsx +17 -12
- package/src/renderers/image.tsx +24 -5
- package/src/renderers/link.tsx +8 -2
- package/src/renderers/list.tsx +93 -74
- package/src/renderers/math.tsx +14 -5
- package/src/renderers/paragraph.tsx +22 -17
- package/src/renderers/style-cache.ts +14 -0
- package/src/renderers/table/cell-content.tsx +15 -4
- package/src/renderers/table/index.tsx +30 -13
- package/src/theme.ts +34 -14
- package/src/utils/code-highlight.ts +133 -44
package/cpp/core/MD4CParser.cpp
CHANGED
|
@@ -15,6 +15,18 @@ size_t clampInputSize(size_t inputSize) {
|
|
|
15
15
|
}
|
|
16
16
|
return inputSize;
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
// Safe pointer offset calculation — guards against out-of-allocation arithmetic.
|
|
20
|
+
// md4c callbacks receive pointers into the input buffer, so arithmetic is valid
|
|
21
|
+
// as long as the input string is stable. This check catches any edge cases.
|
|
22
|
+
static MD_OFFSET safeOffset(const char* text, const char* base, size_t baseSize) noexcept {
|
|
23
|
+
if (text < base) return 0;
|
|
24
|
+
ptrdiff_t diff = text - base;
|
|
25
|
+
if (diff < 0 || static_cast<size_t>(diff) > baseSize) return 0;
|
|
26
|
+
// Check MD_OFFSET won't truncate
|
|
27
|
+
if (static_cast<size_t>(diff) > static_cast<size_t>(std::numeric_limits<MD_OFFSET>::max())) return 0;
|
|
28
|
+
return static_cast<MD_OFFSET>(diff);
|
|
29
|
+
}
|
|
18
30
|
} // namespace
|
|
19
31
|
|
|
20
32
|
class MD4CParser::Impl {
|
|
@@ -38,13 +50,21 @@ public:
|
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
void flushText() {
|
|
41
|
-
if (!currentText.empty()
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
if (!currentText.empty()) {
|
|
54
|
+
if (!nodeStack.empty()) {
|
|
55
|
+
auto textNode = std::make_shared<MarkdownNode>(NodeType::Text);
|
|
56
|
+
textNode->content = std::move(currentText);
|
|
57
|
+
textNode->beg = currentTextBeg;
|
|
58
|
+
textNode->end = lastTextEnd;
|
|
59
|
+
nodeStack.top()->addChild(std::move(textNode));
|
|
60
|
+
currentText.clear();
|
|
61
|
+
} else {
|
|
62
|
+
#if defined(NITROMARKDOWN_DEBUG) || defined(DEBUG)
|
|
63
|
+
// This indicates a parser state bug - text available but no node to attach it to
|
|
64
|
+
fprintf(stderr, "[NitroMarkdown] Warning: flushText called with empty nodeStack, text dropped: %.50s\n", currentText.c_str());
|
|
65
|
+
#endif
|
|
66
|
+
currentText.clear();
|
|
67
|
+
}
|
|
48
68
|
}
|
|
49
69
|
}
|
|
50
70
|
|
|
@@ -74,17 +94,18 @@ public:
|
|
|
74
94
|
std::string result;
|
|
75
95
|
result.reserve(attr->size);
|
|
76
96
|
|
|
77
|
-
|
|
97
|
+
// md4c invariant: substr_types is terminated by an entry where
|
|
98
|
+
// substr_offsets[i] == attr->size (the sentinel entry). Reading
|
|
99
|
+
// substr_offsets[i+1] is always valid when substr_offsets[i] < attr->size.
|
|
100
|
+
for (unsigned i = 0; attr->substr_offsets[i] < attr->size; i++) {
|
|
78
101
|
size_t start = static_cast<size_t>(attr->substr_offsets[i]);
|
|
79
|
-
size_t end = static_cast<size_t>(attr->substr_offsets[i + 1]);
|
|
102
|
+
size_t end = static_cast<size_t>(attr->substr_offsets[i + 1]); // safe: [i+1] always valid when [i] < size
|
|
80
103
|
|
|
81
|
-
if (end > attr->size) {
|
|
104
|
+
if (end > static_cast<size_t>(attr->size)) {
|
|
82
105
|
end = static_cast<size_t>(attr->size);
|
|
83
106
|
}
|
|
84
|
-
if (start > end) {
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
107
|
|
|
108
|
+
// Append content for all recognised text types
|
|
88
109
|
if (attr->substr_types[i] == MD_TEXT_NORMAL ||
|
|
89
110
|
attr->substr_types[i] == MD_TEXT_ENTITY ||
|
|
90
111
|
attr->substr_types[i] == MD_TEXT_NULLCHAR) {
|
|
@@ -92,12 +113,11 @@ public:
|
|
|
92
113
|
result.append(attr->text + start, end - start);
|
|
93
114
|
}
|
|
94
115
|
}
|
|
95
|
-
|
|
96
|
-
if (end >= attr->size) {
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
116
|
}
|
|
100
117
|
|
|
118
|
+
// Fallback: if all substrings had unrecognised types (should not occur
|
|
119
|
+
// per the md4c spec, but guards against future spec extensions), return
|
|
120
|
+
// the raw attribute text.
|
|
101
121
|
if (result.empty() && attr->size > 0) {
|
|
102
122
|
result.assign(attr->text, attr->size);
|
|
103
123
|
}
|
|
@@ -105,25 +125,27 @@ public:
|
|
|
105
125
|
return result;
|
|
106
126
|
}
|
|
107
127
|
|
|
108
|
-
static int enterBlock(MD_BLOCKTYPE type, void* detail, MD_OFFSET off, void* userdata) {
|
|
128
|
+
static int enterBlock(MD_BLOCKTYPE type, void* detail, MD_OFFSET off, void* userdata) noexcept {
|
|
129
|
+
try {
|
|
109
130
|
auto* impl = static_cast<Impl*>(userdata);
|
|
110
|
-
|
|
131
|
+
if (impl == nullptr) return 1; // Signal error to md4c
|
|
132
|
+
|
|
111
133
|
switch (type) {
|
|
112
134
|
case MD_BLOCK_DOC:
|
|
113
135
|
break;
|
|
114
|
-
|
|
136
|
+
|
|
115
137
|
case MD_BLOCK_QUOTE: {
|
|
116
138
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Blockquote), off);
|
|
117
139
|
break;
|
|
118
140
|
}
|
|
119
|
-
|
|
141
|
+
|
|
120
142
|
case MD_BLOCK_UL: {
|
|
121
143
|
auto node = std::make_shared<MarkdownNode>(NodeType::List);
|
|
122
144
|
node->ordered = false;
|
|
123
145
|
impl->pushNode(node, off);
|
|
124
146
|
break;
|
|
125
147
|
}
|
|
126
|
-
|
|
148
|
+
|
|
127
149
|
case MD_BLOCK_OL: {
|
|
128
150
|
auto* d = static_cast<MD_BLOCK_OL_DETAIL*>(detail);
|
|
129
151
|
auto node = std::make_shared<MarkdownNode>(NodeType::List);
|
|
@@ -132,7 +154,7 @@ public:
|
|
|
132
154
|
impl->pushNode(node, off);
|
|
133
155
|
break;
|
|
134
156
|
}
|
|
135
|
-
|
|
157
|
+
|
|
136
158
|
case MD_BLOCK_LI: {
|
|
137
159
|
auto* d = static_cast<MD_BLOCK_LI_DETAIL*>(detail);
|
|
138
160
|
if (d->is_task) {
|
|
@@ -144,12 +166,12 @@ public:
|
|
|
144
166
|
}
|
|
145
167
|
break;
|
|
146
168
|
}
|
|
147
|
-
|
|
169
|
+
|
|
148
170
|
case MD_BLOCK_HR: {
|
|
149
171
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::HorizontalRule), off);
|
|
150
172
|
break;
|
|
151
173
|
}
|
|
152
|
-
|
|
174
|
+
|
|
153
175
|
case MD_BLOCK_H: {
|
|
154
176
|
auto* d = static_cast<MD_BLOCK_H_DETAIL*>(detail);
|
|
155
177
|
auto node = std::make_shared<MarkdownNode>(NodeType::Heading);
|
|
@@ -157,7 +179,7 @@ public:
|
|
|
157
179
|
impl->pushNode(node, off);
|
|
158
180
|
break;
|
|
159
181
|
}
|
|
160
|
-
|
|
182
|
+
|
|
161
183
|
case MD_BLOCK_CODE: {
|
|
162
184
|
auto* d = static_cast<MD_BLOCK_CODE_DETAIL*>(detail);
|
|
163
185
|
auto node = std::make_shared<MarkdownNode>(NodeType::CodeBlock);
|
|
@@ -167,37 +189,37 @@ public:
|
|
|
167
189
|
impl->pushNode(node, off);
|
|
168
190
|
break;
|
|
169
191
|
}
|
|
170
|
-
|
|
192
|
+
|
|
171
193
|
case MD_BLOCK_HTML: {
|
|
172
194
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::HtmlBlock), off);
|
|
173
195
|
break;
|
|
174
196
|
}
|
|
175
|
-
|
|
197
|
+
|
|
176
198
|
case MD_BLOCK_P: {
|
|
177
199
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Paragraph), off);
|
|
178
200
|
break;
|
|
179
201
|
}
|
|
180
|
-
|
|
202
|
+
|
|
181
203
|
case MD_BLOCK_TABLE: {
|
|
182
204
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Table), off);
|
|
183
205
|
break;
|
|
184
206
|
}
|
|
185
|
-
|
|
207
|
+
|
|
186
208
|
case MD_BLOCK_THEAD: {
|
|
187
209
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::TableHead), off);
|
|
188
210
|
break;
|
|
189
211
|
}
|
|
190
|
-
|
|
212
|
+
|
|
191
213
|
case MD_BLOCK_TBODY: {
|
|
192
214
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::TableBody), off);
|
|
193
215
|
break;
|
|
194
216
|
}
|
|
195
|
-
|
|
217
|
+
|
|
196
218
|
case MD_BLOCK_TR: {
|
|
197
219
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::TableRow), off);
|
|
198
220
|
break;
|
|
199
221
|
}
|
|
200
|
-
|
|
222
|
+
|
|
201
223
|
case MD_BLOCK_TH: {
|
|
202
224
|
auto* d = static_cast<MD_BLOCK_TD_DETAIL*>(detail);
|
|
203
225
|
auto node = std::make_shared<MarkdownNode>(NodeType::TableCell);
|
|
@@ -211,7 +233,7 @@ public:
|
|
|
211
233
|
impl->pushNode(node, off);
|
|
212
234
|
break;
|
|
213
235
|
}
|
|
214
|
-
|
|
236
|
+
|
|
215
237
|
case MD_BLOCK_TD: {
|
|
216
238
|
auto* d = static_cast<MD_BLOCK_TD_DETAIL*>(detail);
|
|
217
239
|
auto node = std::make_shared<MarkdownNode>(NodeType::TableCell);
|
|
@@ -226,14 +248,18 @@ public:
|
|
|
226
248
|
break;
|
|
227
249
|
}
|
|
228
250
|
}
|
|
229
|
-
|
|
251
|
+
|
|
230
252
|
return 0;
|
|
253
|
+
} catch (...) {
|
|
254
|
+
return 1; // Signal error to md4c
|
|
255
|
+
}
|
|
231
256
|
}
|
|
232
257
|
|
|
233
|
-
static int leaveBlock(MD_BLOCKTYPE type, void* detail, MD_OFFSET off, void* userdata) {
|
|
234
|
-
|
|
258
|
+
static int leaveBlock(MD_BLOCKTYPE type, [[maybe_unused]] void* detail, MD_OFFSET off, void* userdata) noexcept {
|
|
259
|
+
try {
|
|
235
260
|
auto* impl = static_cast<Impl*>(userdata);
|
|
236
|
-
|
|
261
|
+
if (impl == nullptr) return 1; // Signal error to md4c
|
|
262
|
+
|
|
237
263
|
switch (type) {
|
|
238
264
|
case MD_BLOCK_DOC:
|
|
239
265
|
impl->root->end = off;
|
|
@@ -245,29 +271,34 @@ public:
|
|
|
245
271
|
impl->popNode(off);
|
|
246
272
|
break;
|
|
247
273
|
}
|
|
248
|
-
|
|
274
|
+
|
|
249
275
|
return 0;
|
|
276
|
+
} catch (...) {
|
|
277
|
+
return 1; // Signal error to md4c
|
|
278
|
+
}
|
|
250
279
|
}
|
|
251
280
|
|
|
252
|
-
static int enterSpan(MD_SPANTYPE type, void* detail, MD_OFFSET off, void* userdata) {
|
|
281
|
+
static int enterSpan(MD_SPANTYPE type, void* detail, MD_OFFSET off, void* userdata) noexcept {
|
|
282
|
+
try {
|
|
253
283
|
auto* impl = static_cast<Impl*>(userdata);
|
|
254
|
-
|
|
284
|
+
if (impl == nullptr) return 1; // Signal error to md4c
|
|
285
|
+
|
|
255
286
|
switch (type) {
|
|
256
287
|
case MD_SPAN_EM: {
|
|
257
288
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Italic), off);
|
|
258
289
|
break;
|
|
259
290
|
}
|
|
260
|
-
|
|
291
|
+
|
|
261
292
|
case MD_SPAN_STRONG: {
|
|
262
293
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Bold), off);
|
|
263
294
|
break;
|
|
264
295
|
}
|
|
265
|
-
|
|
296
|
+
|
|
266
297
|
case MD_SPAN_DEL: {
|
|
267
298
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Strikethrough), off);
|
|
268
299
|
break;
|
|
269
300
|
}
|
|
270
|
-
|
|
301
|
+
|
|
271
302
|
case MD_SPAN_A: {
|
|
272
303
|
auto* d = static_cast<MD_SPAN_A_DETAIL*>(detail);
|
|
273
304
|
auto node = std::make_shared<MarkdownNode>(NodeType::Link);
|
|
@@ -280,7 +311,7 @@ public:
|
|
|
280
311
|
impl->pushNode(node, off);
|
|
281
312
|
break;
|
|
282
313
|
}
|
|
283
|
-
|
|
314
|
+
|
|
284
315
|
case MD_SPAN_IMG: {
|
|
285
316
|
auto* d = static_cast<MD_SPAN_IMG_DETAIL*>(detail);
|
|
286
317
|
auto node = std::make_shared<MarkdownNode>(NodeType::Image);
|
|
@@ -293,40 +324,44 @@ public:
|
|
|
293
324
|
impl->pushNode(node, off);
|
|
294
325
|
break;
|
|
295
326
|
}
|
|
296
|
-
|
|
327
|
+
|
|
297
328
|
case MD_SPAN_CODE: {
|
|
298
329
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::CodeInline), off);
|
|
299
330
|
break;
|
|
300
331
|
}
|
|
301
|
-
|
|
332
|
+
|
|
302
333
|
case MD_SPAN_LATEXMATH: {
|
|
303
334
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::MathInline), off);
|
|
304
335
|
break;
|
|
305
336
|
}
|
|
306
|
-
|
|
337
|
+
|
|
307
338
|
case MD_SPAN_LATEXMATH_DISPLAY: {
|
|
308
339
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::MathBlock), off);
|
|
309
340
|
break;
|
|
310
341
|
}
|
|
311
|
-
|
|
342
|
+
|
|
312
343
|
case MD_SPAN_U: {
|
|
313
344
|
impl->pushNode(std::make_shared<MarkdownNode>(NodeType::Italic), off);
|
|
314
345
|
break;
|
|
315
346
|
}
|
|
316
|
-
|
|
347
|
+
|
|
317
348
|
case MD_SPAN_WIKILINK: {
|
|
318
349
|
auto node = std::make_shared<MarkdownNode>(NodeType::Link);
|
|
319
350
|
impl->pushNode(node, off);
|
|
320
351
|
break;
|
|
321
352
|
}
|
|
322
353
|
}
|
|
323
|
-
|
|
354
|
+
|
|
324
355
|
return 0;
|
|
356
|
+
} catch (...) {
|
|
357
|
+
return 1; // Signal error to md4c
|
|
358
|
+
}
|
|
325
359
|
}
|
|
326
360
|
|
|
327
|
-
static int leaveSpan(MD_SPANTYPE type, void* detail, MD_OFFSET off, void* userdata) {
|
|
328
|
-
|
|
361
|
+
static int leaveSpan(MD_SPANTYPE type, [[maybe_unused]] void* detail, MD_OFFSET off, void* userdata) noexcept {
|
|
362
|
+
try {
|
|
329
363
|
auto* impl = static_cast<Impl*>(userdata);
|
|
364
|
+
if (impl == nullptr) return 1; // Signal error to md4c
|
|
330
365
|
|
|
331
366
|
if (!impl->nodeStack.empty()) {
|
|
332
367
|
auto currentNode = impl->nodeStack.top();
|
|
@@ -349,71 +384,89 @@ public:
|
|
|
349
384
|
|
|
350
385
|
impl->popNode(off);
|
|
351
386
|
return 0;
|
|
387
|
+
} catch (...) {
|
|
388
|
+
return 1; // Signal error to md4c
|
|
389
|
+
}
|
|
352
390
|
}
|
|
353
391
|
|
|
354
|
-
static int text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata) {
|
|
392
|
+
static int text(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata) noexcept {
|
|
393
|
+
try {
|
|
355
394
|
auto* impl = static_cast<Impl*>(userdata);
|
|
395
|
+
if (impl == nullptr) return 1; // Signal error to md4c
|
|
356
396
|
|
|
357
397
|
if (!text || size == 0) return 0;
|
|
358
398
|
|
|
359
399
|
switch (type) {
|
|
360
400
|
case MD_TEXT_NULLCHAR: {
|
|
361
|
-
MD_OFFSET off = impl->
|
|
362
|
-
|
|
363
|
-
if (diff >= 0 && static_cast<size_t>(diff) <= impl->inputTextSize) {
|
|
364
|
-
off = static_cast<MD_OFFSET>(diff);
|
|
365
|
-
}
|
|
401
|
+
MD_OFFSET off = safeOffset(text, impl->inputText, impl->inputTextSize);
|
|
402
|
+
if (off == 0 && text != impl->inputText) off = impl->lastTextEnd;
|
|
366
403
|
if (impl->currentText.empty()) impl->currentTextBeg = off;
|
|
367
404
|
impl->currentText += '\0';
|
|
368
405
|
impl->lastTextEnd = off + 1;
|
|
369
406
|
break;
|
|
370
407
|
}
|
|
371
|
-
|
|
408
|
+
|
|
372
409
|
case MD_TEXT_BR:
|
|
373
410
|
impl->flushText();
|
|
374
|
-
impl->nodeStack.
|
|
375
|
-
|
|
411
|
+
if (!impl->nodeStack.empty()) {
|
|
412
|
+
impl->nodeStack.top()->addChild(
|
|
413
|
+
std::make_shared<MarkdownNode>(NodeType::LineBreak));
|
|
414
|
+
}
|
|
376
415
|
break;
|
|
377
|
-
|
|
416
|
+
|
|
378
417
|
case MD_TEXT_SOFTBR:
|
|
379
418
|
impl->flushText();
|
|
380
|
-
impl->nodeStack.
|
|
381
|
-
|
|
419
|
+
if (!impl->nodeStack.empty()) {
|
|
420
|
+
impl->nodeStack.top()->addChild(
|
|
421
|
+
std::make_shared<MarkdownNode>(NodeType::SoftBreak));
|
|
422
|
+
}
|
|
382
423
|
break;
|
|
383
|
-
|
|
424
|
+
|
|
384
425
|
case MD_TEXT_HTML:
|
|
385
426
|
impl->flushText();
|
|
386
|
-
{
|
|
427
|
+
if (!impl->nodeStack.empty() && text && size > 0) {
|
|
428
|
+
MD_OFFSET off = safeOffset(text, impl->inputText, impl->inputTextSize);
|
|
429
|
+
if (off == 0 && text != impl->inputText) off = impl->lastTextEnd;
|
|
430
|
+
|
|
431
|
+
if (impl->nodeStack.top()->type == NodeType::HtmlBlock) {
|
|
432
|
+
auto htmlBlock = impl->nodeStack.top();
|
|
433
|
+
if (htmlBlock->content.has_value()) {
|
|
434
|
+
htmlBlock->content->append(text, size);
|
|
435
|
+
} else {
|
|
436
|
+
htmlBlock->content = std::string(text, size);
|
|
437
|
+
}
|
|
438
|
+
htmlBlock->end = off + size;
|
|
439
|
+
impl->lastTextEnd = off + size;
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
|
|
387
443
|
auto node = std::make_shared<MarkdownNode>(NodeType::HtmlInline);
|
|
388
444
|
node->content = std::string(text, size);
|
|
445
|
+
node->beg = off;
|
|
446
|
+
node->end = off + size;
|
|
389
447
|
impl->nodeStack.top()->addChild(node);
|
|
448
|
+
impl->lastTextEnd = off + size;
|
|
390
449
|
}
|
|
391
450
|
break;
|
|
392
|
-
|
|
451
|
+
|
|
393
452
|
case MD_TEXT_ENTITY:
|
|
394
453
|
if (text && size > 0) {
|
|
395
|
-
MD_OFFSET off = impl->
|
|
396
|
-
|
|
397
|
-
if (diff >= 0 && static_cast<size_t>(diff) <= impl->inputTextSize) {
|
|
398
|
-
off = static_cast<MD_OFFSET>(diff);
|
|
399
|
-
}
|
|
454
|
+
MD_OFFSET off = safeOffset(text, impl->inputText, impl->inputTextSize);
|
|
455
|
+
if (off == 0 && text != impl->inputText) off = impl->lastTextEnd;
|
|
400
456
|
if (impl->currentText.empty()) impl->currentTextBeg = off;
|
|
401
457
|
impl->currentText.append(text, size);
|
|
402
458
|
impl->lastTextEnd = off + size;
|
|
403
459
|
}
|
|
404
460
|
break;
|
|
405
|
-
|
|
461
|
+
|
|
406
462
|
case MD_TEXT_NORMAL:
|
|
407
463
|
case MD_TEXT_CODE:
|
|
408
464
|
case MD_TEXT_LATEXMATH:
|
|
409
465
|
default: {
|
|
410
466
|
if (text && size > 0) {
|
|
411
|
-
MD_OFFSET off = impl->
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
off = static_cast<MD_OFFSET>(diff);
|
|
415
|
-
}
|
|
416
|
-
|
|
467
|
+
MD_OFFSET off = safeOffset(text, impl->inputText, impl->inputTextSize);
|
|
468
|
+
if (off == 0 && text != impl->inputText) off = impl->lastTextEnd;
|
|
469
|
+
|
|
417
470
|
if (impl->currentText.empty()) {
|
|
418
471
|
impl->currentTextBeg = off;
|
|
419
472
|
}
|
|
@@ -423,8 +476,11 @@ public:
|
|
|
423
476
|
break;
|
|
424
477
|
}
|
|
425
478
|
}
|
|
426
|
-
|
|
479
|
+
|
|
427
480
|
return 0;
|
|
481
|
+
} catch (...) {
|
|
482
|
+
return 1; // Signal error to md4c
|
|
483
|
+
}
|
|
428
484
|
}
|
|
429
485
|
};
|
|
430
486
|
|
|
@@ -438,7 +494,7 @@ std::shared_ptr<MarkdownNode> MD4CParser::parse(const std::string& markdown, con
|
|
|
438
494
|
size_t inputSize = clampInputSize(markdown.size());
|
|
439
495
|
impl_->inputTextSize = inputSize;
|
|
440
496
|
|
|
441
|
-
unsigned int flags = MD_FLAG_NOHTML;
|
|
497
|
+
unsigned int flags = options.html ? 0 : MD_FLAG_NOHTML;
|
|
442
498
|
|
|
443
499
|
if (options.gfm) {
|
|
444
500
|
flags |= MD_FLAG_TABLES;
|
|
@@ -463,10 +519,15 @@ std::shared_ptr<MarkdownNode> MD4CParser::parse(const std::string& markdown, con
|
|
|
463
519
|
nullptr
|
|
464
520
|
};
|
|
465
521
|
|
|
466
|
-
md_parse(markdown.c_str(),
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
522
|
+
int result = md_parse(markdown.c_str(),
|
|
523
|
+
static_cast<MD_SIZE>(inputSize),
|
|
524
|
+
&parser,
|
|
525
|
+
impl_.get());
|
|
526
|
+
if (result != 0) {
|
|
527
|
+
// md_parse failed (callback aborted or runtime error).
|
|
528
|
+
// The AST may be partial but is still a valid tree rooted at Document,
|
|
529
|
+
// so we continue rather than throw — callers can use whatever was parsed.
|
|
530
|
+
}
|
|
470
531
|
|
|
471
532
|
impl_->flushText();
|
|
472
533
|
return impl_->root;
|