react-native-jet-markdown 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +4 -56
  2. package/android/src/main/cpp/JniBindings.cpp +0 -106
  3. package/android/src/main/java/com/jetmarkdown/JetMarkdownNative.kt +0 -79
  4. package/android/src/main/java/com/jetmarkdown/JetMarkdownPackage.kt +1 -2
  5. package/android/src/main/java/com/jetmarkdown/render/spans/MarkdownLineHeightSpan.kt +2 -13
  6. package/cpp/react/override/react/renderer/components/JetMarkdownViewSpec/ComponentDescriptors.h +0 -4
  7. package/cpp/tests/parser_tests.cpp +0 -402
  8. package/cpp/tests/run_tests.sh +0 -3
  9. package/lib/module/index.js +0 -2
  10. package/lib/module/index.js.map +1 -1
  11. package/lib/typescript/src/index.d.ts +1 -3
  12. package/lib/typescript/src/index.d.ts.map +1 -1
  13. package/lib/typescript/src/types.d.ts +0 -127
  14. package/lib/typescript/src/types.d.ts.map +1 -1
  15. package/package.json +2 -5
  16. package/src/index.tsx +0 -9
  17. package/src/types.ts +0 -134
  18. package/android/src/main/java/com/jetmarkdown/editor/EditorSpans.kt +0 -217
  19. package/android/src/main/java/com/jetmarkdown/editor/JetMarkdownEditorManager.kt +0 -224
  20. package/android/src/main/java/com/jetmarkdown/editor/JetMarkdownEditorView.kt +0 -1736
  21. package/cpp/core/AstToMarkdown.cpp +0 -549
  22. package/cpp/core/AstToMarkdown.h +0 -16
  23. package/cpp/core/EditorRuns.cpp +0 -640
  24. package/cpp/core/EditorRuns.h +0 -94
  25. package/cpp/core/EditorText.cpp +0 -15
  26. package/cpp/core/EditorText.h +0 -18
  27. package/cpp/react/JetMarkdownEditorShadowNode.cpp +0 -54
  28. package/cpp/react/JetMarkdownEditorShadowNode.h +0 -55
  29. package/cpp/react/JetMarkdownEditorState.h +0 -35
  30. package/ios/editor/JetMarkdownEditor.h +0 -12
  31. package/ios/editor/JetMarkdownEditor.mm +0 -2002
  32. package/lib/module/JetMarkdownEditor.js +0 -85
  33. package/lib/module/JetMarkdownEditor.js.map +0 -1
  34. package/lib/module/JetMarkdownEditor.native.js +0 -244
  35. package/lib/module/JetMarkdownEditor.native.js.map +0 -1
  36. package/lib/module/JetMarkdownEditorNativeComponent.ts +0 -173
  37. package/lib/module/useJetMarkdownEditor.js +0 -41
  38. package/lib/module/useJetMarkdownEditor.js.map +0 -1
  39. package/lib/typescript/src/JetMarkdownEditor.d.ts +0 -6
  40. package/lib/typescript/src/JetMarkdownEditor.d.ts.map +0 -1
  41. package/lib/typescript/src/JetMarkdownEditor.native.d.ts +0 -6
  42. package/lib/typescript/src/JetMarkdownEditor.native.d.ts.map +0 -1
  43. package/lib/typescript/src/JetMarkdownEditorNativeComponent.d.ts +0 -105
  44. package/lib/typescript/src/JetMarkdownEditorNativeComponent.d.ts.map +0 -1
  45. package/lib/typescript/src/useJetMarkdownEditor.d.ts +0 -36
  46. package/lib/typescript/src/useJetMarkdownEditor.d.ts.map +0 -1
  47. package/src/JetMarkdownEditor.native.tsx +0 -301
  48. package/src/JetMarkdownEditor.tsx +0 -90
  49. package/src/JetMarkdownEditorNativeComponent.ts +0 -173
  50. package/src/useJetMarkdownEditor.ts +0 -50
@@ -1,549 +0,0 @@
1
- #include "AstToMarkdown.h"
2
-
3
- #include <algorithm>
4
- #include <cctype>
5
- #include <string>
6
- #include <vector>
7
-
8
- namespace jetmarkdown {
9
-
10
- namespace {
11
-
12
- // ---------------------------------------------------------------------------
13
- // Inline serialization
14
- // ---------------------------------------------------------------------------
15
-
16
- // Characters that are always escaped inside text runs: everything our
17
- // parser (md4c + the pre-pass + the inline-extensions scanner) can
18
- // interpret mid-line.
19
- bool needsInlineEscape(char c) {
20
- switch (c) {
21
- case '\\':
22
- case '`':
23
- case '*':
24
- case '_':
25
- case '~':
26
- case '|':
27
- case '^':
28
- case '[':
29
- case ']':
30
- case '!':
31
- // '<' opens angle autolinks/raw-HTML-ish constructs; '&' opens
32
- // entities (which the parser materializes as literal characters).
33
- case '<':
34
- case '&':
35
- return true;
36
- default:
37
- return false;
38
- }
39
- }
40
-
41
- // Block-level constructs only bite at the start of a line.
42
- bool needsLineStartEscape(char c) {
43
- switch (c) {
44
- case '#':
45
- case '>':
46
- case '-':
47
- case '+':
48
- case '=':
49
- return true;
50
- default:
51
- return false;
52
- }
53
- }
54
-
55
- struct InlineWriter {
56
- std::string out;
57
- bool atLineStart = true;
58
-
59
- void raw(const std::string& value) {
60
- out += value;
61
- if (!value.empty()) {
62
- atLineStart = value.back() == '\n';
63
- }
64
- }
65
-
66
- void text(const std::string& value) {
67
- for (size_t i = 0; i < value.size(); i++) {
68
- const char c = value[i];
69
- if (c == '\n') {
70
- out += c;
71
- atLineStart = true;
72
- continue;
73
- }
74
- if (atLineStart) {
75
- if (c == ' ') {
76
- // A leading space run would be trimmed by the parser (or, at 4+,
77
- // become an indented code block). One entity space defuses both
78
- // and preserves the run.
79
- out += "&#32;";
80
- atLineStart = false;
81
- continue;
82
- }
83
- if (needsLineStartEscape(c)) {
84
- out += '\\';
85
- } else if (std::isdigit(static_cast<unsigned char>(c))) {
86
- // "12. x" / "12) x" would open an ordered list.
87
- size_t j = i;
88
- while (j < value.size() && std::isdigit(static_cast<unsigned char>(value[j]))) {
89
- j++;
90
- }
91
- if (j < value.size() && (value[j] == '.' || value[j] == ')')) {
92
- out += value.substr(i, j - i);
93
- out += '\\';
94
- out += value[j];
95
- atLineStart = false;
96
- i = j;
97
- continue;
98
- }
99
- }
100
- }
101
- if (needsInlineEscape(c)) {
102
- out += '\\';
103
- }
104
- out += c;
105
- if (c != ' ') {
106
- atLineStart = false;
107
- }
108
- }
109
- }
110
- };
111
-
112
- size_t longestBacktickRun(const std::string& value) {
113
- size_t longest = 0;
114
- size_t run = 0;
115
- for (const char c : value) {
116
- run = c == '`' ? run + 1 : 0;
117
- longest = std::max(longest, run);
118
- }
119
- return longest;
120
- }
121
-
122
- void writeInlineCode(InlineWriter& writer, const std::string& content) {
123
- const std::string fence(longestBacktickRun(content) + 1, '`');
124
- const bool pad = !content.empty() &&
125
- (content.front() == '`' || content.back() == '`' || content.front() == ' ' ||
126
- content.back() == ' ');
127
- writer.raw(fence);
128
- if (pad) {
129
- writer.raw(" ");
130
- }
131
- writer.raw(content);
132
- if (pad) {
133
- writer.raw(" ");
134
- }
135
- writer.raw(fence);
136
- }
137
-
138
- void writeLinkDestination(InlineWriter& writer, const std::string& url) {
139
- const bool needsBrackets =
140
- url.find(' ') != std::string::npos || url.find('(') != std::string::npos ||
141
- url.find(')') != std::string::npos;
142
- if (needsBrackets) {
143
- writer.raw("<" + url + ">");
144
- } else {
145
- writer.raw(url);
146
- }
147
- }
148
-
149
- void writeInlineChildren(InlineWriter& writer, const Node* node);
150
-
151
- // True when the star delimiters about to be written would extend the
152
- // writer's trailing star run to 4+ — a run md4c's delimiter algorithm no
153
- // longer splits correctly (3-star runs like **X***y* are fine).
154
- bool starRunWouldMerge(const InlineWriter& writer, size_t openLen) {
155
- size_t trailing = 0;
156
- for (auto it = writer.out.rbegin(); it != writer.out.rend() && *it == '*'; ++it) {
157
- trailing++;
158
- }
159
- return trailing > 0 && trailing + openLen >= 4;
160
- }
161
-
162
- void writeInlineNode(
163
- InlineWriter& writer, const Node* node, bool nextStartsAlnum) {
164
- switch (node->type) {
165
- case NodeType::Text:
166
- writer.text(node->text);
167
- break;
168
- case NodeType::SoftBreak:
169
- writer.raw("\n");
170
- break;
171
- case NodeType::HardBreak:
172
- writer.raw("\\\n");
173
- break;
174
- case NodeType::Bold:
175
- case NodeType::Italic: {
176
- // Stars by default: '_' cannot open/close emphasis intraword, and
177
- // md4c resolves 3-star adjacency (**X***y*) correctly. Only when the
178
- // open would fuse into a 4+ star run — which md4c fails to split —
179
- // fall back to underscores, and only where flanking permits (the open
180
- // side is a star, i.e. punctuation; the close side must not touch a
181
- // word character).
182
- const size_t openLen = node->type == NodeType::Bold ? 2 : 1;
183
- const bool underscore =
184
- starRunWouldMerge(writer, openLen) && !nextStartsAlnum;
185
- const char* delimiter = node->type == NodeType::Bold
186
- ? (underscore ? "__" : "**")
187
- : (underscore ? "_" : "*");
188
- writer.raw(delimiter);
189
- writeInlineChildren(writer, node);
190
- writer.raw(delimiter);
191
- break;
192
- }
193
- case NodeType::Strikethrough:
194
- writer.raw("~~");
195
- writeInlineChildren(writer, node);
196
- writer.raw("~~");
197
- break;
198
- case NodeType::Spoiler:
199
- writer.raw("||");
200
- writeInlineChildren(writer, node);
201
- writer.raw("||");
202
- break;
203
- case NodeType::Superscript: {
204
- // The caret-pair form cannot contain whitespace; multi-word content
205
- // uses the Reddit paren form — which in turn ends at the first ")",
206
- // unescapably. Content that needs both is unrepresentable: emit it
207
- // unmarked rather than corrupt the round trip.
208
- InlineWriter content;
209
- content.atLineStart = false;
210
- writeInlineChildren(content, node);
211
- const bool hasWhitespace =
212
- content.out.find_first_of(" \t") != std::string::npos;
213
- const bool hasParen = content.out.find(')') != std::string::npos;
214
- if (!hasWhitespace) {
215
- writer.raw("^");
216
- writer.raw(content.out);
217
- writer.raw("^");
218
- } else if (!hasParen) {
219
- writer.raw("^(");
220
- writer.raw(content.out);
221
- writer.raw(")");
222
- } else {
223
- writer.raw(content.out);
224
- }
225
- break;
226
- }
227
- case NodeType::Subscript:
228
- writer.raw("~");
229
- writeInlineChildren(writer, node);
230
- writer.raw("~");
231
- break;
232
- case NodeType::InlineCode:
233
- writeInlineCode(writer, node->text);
234
- break;
235
- case NodeType::Link:
236
- // A link whose visible text IS its destination re-parses as a
237
- // permissive autolink, so emit the bare URL instead of the noisy
238
- // [url](url) form.
239
- if (node->children.size() == 1 &&
240
- node->children[0]->type == NodeType::Text &&
241
- node->children[0]->text == node->url &&
242
- (node->url.rfind("http://", 0) == 0 ||
243
- node->url.rfind("https://", 0) == 0)) {
244
- writer.raw(node->url);
245
- break;
246
- }
247
- writer.raw("[");
248
- writeInlineChildren(writer, node);
249
- writer.raw("](");
250
- writeLinkDestination(writer, node->url);
251
- writer.raw(")");
252
- break;
253
- case NodeType::Image:
254
- writer.raw("![");
255
- writer.text(node->text);
256
- writer.raw("](");
257
- writeLinkDestination(writer, node->url);
258
- writer.raw(")");
259
- break;
260
- default:
261
- writeInlineChildren(writer, node);
262
- break;
263
- }
264
- }
265
-
266
- void writeInlineChildren(InlineWriter& writer, const Node* node) {
267
- const auto& kids = node->children;
268
- for (size_t i = 0; i < kids.size(); i++) {
269
- // Underscore-fallback lookahead: does a word character immediately
270
- // follow this child? Only Text siblings can supply one.
271
- bool nextStartsAlnum = false;
272
- if (i + 1 < kids.size() && kids[i + 1]->type == NodeType::Text &&
273
- !kids[i + 1]->text.empty()) {
274
- nextStartsAlnum =
275
- std::isalnum(static_cast<unsigned char>(kids[i + 1]->text.front())) != 0 ||
276
- static_cast<unsigned char>(kids[i + 1]->text.front()) >= 0x80;
277
- }
278
- writeInlineNode(writer, kids[i], nextStartsAlnum);
279
- }
280
- }
281
-
282
- std::string inlineMarkdown(const Node* node) {
283
- InlineWriter writer;
284
- writeInlineChildren(writer, node);
285
- return writer.out;
286
- }
287
-
288
- std::vector<std::string> splitLines(const std::string& value) {
289
- std::vector<std::string> lines;
290
- std::string current;
291
- for (const char c : value) {
292
- if (c == '\n') {
293
- lines.push_back(current);
294
- current.clear();
295
- } else {
296
- current += c;
297
- }
298
- }
299
- lines.push_back(current);
300
- return lines;
301
- }
302
-
303
- // ---------------------------------------------------------------------------
304
- // Block serialization: every block renders to lines; containers prefix them.
305
- // ---------------------------------------------------------------------------
306
-
307
- std::vector<std::string> blockLines(const Node* node);
308
-
309
- bool isBlockNode(NodeType type) {
310
- switch (type) {
311
- case NodeType::Paragraph:
312
- case NodeType::Heading:
313
- case NodeType::BlockQuote:
314
- case NodeType::CodeBlock:
315
- case NodeType::List:
316
- case NodeType::Table:
317
- case NodeType::ThematicBreak:
318
- return true;
319
- default:
320
- return false;
321
- }
322
- }
323
-
324
- // Tight list items carry inline nodes directly (no paragraph wrapper);
325
- // loose items carry blocks. Group runs of inline children into one
326
- // paragraph-like run, blocks get blank-line separation.
327
- std::vector<std::string> listItemLines(const Node* item) {
328
- std::vector<std::string> lines;
329
- InlineWriter inlineRun;
330
- bool wroteAny = false;
331
- bool lastWasInlineRun = false;
332
-
333
- auto appendLines = [&lines, &wroteAny](std::vector<std::string> next, bool blankBefore) {
334
- if (wroteAny && blankBefore) {
335
- lines.emplace_back();
336
- }
337
- for (auto& line : next) {
338
- lines.push_back(std::move(line));
339
- }
340
- wroteAny = true;
341
- };
342
-
343
- auto flushInline = [&inlineRun, &appendLines, &lastWasInlineRun]() {
344
- if (inlineRun.out.empty()) {
345
- return;
346
- }
347
- appendLines(splitLines(inlineRun.out), true);
348
- inlineRun = InlineWriter{};
349
- lastWasInlineRun = true;
350
- };
351
-
352
- for (const Node* child : item->children) {
353
- if (isBlockNode(child->type)) {
354
- flushInline();
355
- // No blank line after a tight item's inline run: it would turn the
356
- // list loose and wrap the run in a paragraph on re-parse.
357
- appendLines(blockLines(child), !lastWasInlineRun);
358
- lastWasInlineRun = false;
359
- } else {
360
- writeInlineNode(inlineRun, child, false);
361
- }
362
- }
363
- flushInline();
364
-
365
- if (lines.empty()) {
366
- lines.emplace_back();
367
- }
368
- return lines;
369
- }
370
-
371
- std::vector<std::string> childBlockLines(const Node* node) {
372
- std::vector<std::string> lines;
373
- bool first = true;
374
- for (const Node* child : node->children) {
375
- if (!first) {
376
- lines.emplace_back();
377
- }
378
- first = false;
379
- auto childLines = blockLines(child);
380
- lines.insert(lines.end(), childLines.begin(), childLines.end());
381
- }
382
- return lines;
383
- }
384
-
385
- std::vector<std::string> codeBlockLines(const Node* node) {
386
- std::string content = node->text;
387
- if (!content.empty() && content.back() == '\n') {
388
- content.pop_back();
389
- }
390
- const size_t fenceLength = std::max<size_t>(3, longestBacktickRun(content) + 1);
391
- const std::string fence(fenceLength, '`');
392
-
393
- std::vector<std::string> lines;
394
- lines.push_back(fence + node->url);
395
- for (auto& line : splitLines(content)) {
396
- lines.push_back(std::move(line));
397
- }
398
- lines.push_back(fence);
399
- return lines;
400
- }
401
-
402
- std::vector<std::string> listLines(const Node* node) {
403
- std::vector<std::string> lines;
404
- int index = node->startIndex;
405
- for (const Node* item : node->children) {
406
- if (item->type != NodeType::ListItem) {
407
- continue;
408
- }
409
-
410
- const std::string marker =
411
- node->ordered ? std::to_string(index) + ". " : std::string("- ");
412
- const std::string indent(marker.size(), ' ');
413
-
414
- auto itemLines = listItemLines(item);
415
- bool firstLine = true;
416
- for (auto& line : itemLines) {
417
- if (firstLine) {
418
- lines.push_back(marker + line);
419
- firstLine = false;
420
- } else if (line.empty()) {
421
- lines.emplace_back();
422
- } else {
423
- lines.push_back(indent + line);
424
- }
425
- }
426
- index++;
427
- }
428
- return lines;
429
- }
430
-
431
- std::string tableCellMarkdown(const Node* cell) {
432
- InlineWriter writer;
433
- writer.atLineStart = false;
434
- writeInlineChildren(writer, cell);
435
- return writer.out;
436
- }
437
-
438
- std::vector<std::string> tableLines(const Node* node) {
439
- std::vector<std::string> lines;
440
- const Node* headerRow = nullptr;
441
- std::vector<const Node*> bodyRows;
442
- for (const Node* row : node->children) {
443
- if (row->type != NodeType::TableRow) {
444
- continue;
445
- }
446
- if (row->level == 1 && headerRow == nullptr) {
447
- headerRow = row;
448
- } else {
449
- bodyRows.push_back(row);
450
- }
451
- }
452
- if (headerRow == nullptr) {
453
- return lines;
454
- }
455
-
456
- auto rowLine = [](const Node* row) {
457
- std::string line = "|";
458
- for (const Node* cell : row->children) {
459
- line += " " + tableCellMarkdown(cell) + " |";
460
- }
461
- return line;
462
- };
463
-
464
- lines.push_back(rowLine(headerRow));
465
-
466
- std::string separator = "|";
467
- for (const Node* cell : headerRow->children) {
468
- switch (static_cast<CellAlign>(cell->level)) {
469
- case CellAlign::Left:
470
- separator += " :-- |";
471
- break;
472
- case CellAlign::Center:
473
- separator += " :-: |";
474
- break;
475
- case CellAlign::Right:
476
- separator += " --: |";
477
- break;
478
- case CellAlign::Default:
479
- separator += " --- |";
480
- break;
481
- }
482
- }
483
- lines.push_back(separator);
484
-
485
- for (const Node* row : bodyRows) {
486
- lines.push_back(rowLine(row));
487
- }
488
- return lines;
489
- }
490
-
491
- std::vector<std::string> blockLines(const Node* node) {
492
- switch (node->type) {
493
- case NodeType::Paragraph:
494
- return splitLines(inlineMarkdown(node));
495
-
496
- case NodeType::Heading: {
497
- const int level = std::clamp<int>(node->level, 1, 6);
498
- InlineWriter writer;
499
- writer.atLineStart = false;
500
- writeInlineChildren(writer, node);
501
- return {std::string(level, '#') + " " + writer.out};
502
- }
503
-
504
- case NodeType::BlockQuote: {
505
- std::vector<std::string> lines;
506
- for (auto& line : childBlockLines(node)) {
507
- lines.push_back(line.empty() ? ">" : "> " + line);
508
- }
509
- return lines;
510
- }
511
-
512
- case NodeType::CodeBlock:
513
- return codeBlockLines(node);
514
-
515
- case NodeType::List:
516
- return listLines(node);
517
-
518
- case NodeType::Table:
519
- return tableLines(node);
520
-
521
- case NodeType::ThematicBreak:
522
- return {"---"};
523
-
524
- default:
525
- return splitLines(inlineMarkdown(node));
526
- }
527
- }
528
-
529
- } // namespace
530
-
531
- std::string astToMarkdown(const Node* root) {
532
- if (root == nullptr) {
533
- return "";
534
- }
535
- const auto lines = childBlockLines(root);
536
- std::string out;
537
- for (size_t i = 0; i < lines.size(); i++) {
538
- if (i > 0) {
539
- out += '\n';
540
- }
541
- out += lines[i];
542
- }
543
- if (!out.empty()) {
544
- out += '\n';
545
- }
546
- return out;
547
- }
548
-
549
- } // namespace jetmarkdown
@@ -1,16 +0,0 @@
1
- #pragma once
2
-
3
- #include <string>
4
-
5
- #include "Ast.h"
6
-
7
- namespace jetmarkdown {
8
-
9
- // Serializes an AST back to markdown text such that re-parsing the output
10
- // yields an identical AST (the round-trip law, covered by golden tests).
11
- // Inline text is escaped so literal characters survive: parse(serialize(x))
12
- // == x even when the original spelling differs (e.g. Reddit ^word
13
- // superscripts normalize to ^word^).
14
- std::string astToMarkdown(const Node* root);
15
-
16
- } // namespace jetmarkdown