react-native-nitro-markdown 0.5.1 → 0.5.3
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 +257 -682
- 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/cpp/cpp-adapter.cpp +4 -1
- package/android/src/main/java/com/margelo/nitro/com/nitromarkdown/HybridMarkdownSession.kt +61 -21
- package/android/src/main/java/com/nitromarkdown/NitroMarkdownPackage.kt +6 -18
- package/cpp/bindings/HybridMarkdownParser.cpp +38 -12
- package/cpp/bindings/HybridMarkdownParser.hpp +4 -4
- package/cpp/bindings/HybridMarkdownSession.cpp +2 -0
- package/cpp/core/MD4CParser.cpp +128 -85
- package/cpp/core/MarkdownSessionCore.cpp +2 -0
- package/ios/HybridMarkdownSession.swift +89 -46
- package/lib/commonjs/headless.js +33 -7
- 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 +47 -10
- package/lib/commonjs/markdown.js.map +1 -1
- package/lib/commonjs/renderers/code.js +1 -1
- package/lib/commonjs/renderers/code.js.map +1 -1
- package/lib/commonjs/renderers/image.js +6 -1
- 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 +2 -0
- package/lib/commonjs/renderers/list.js.map +1 -1
- package/lib/commonjs/renderers/math.js +4 -2
- package/lib/commonjs/renderers/math.js.map +1 -1
- 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 +10 -2
- 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 +34 -6
- 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 +48 -11
- package/lib/module/markdown.js.map +1 -1
- package/lib/module/renderers/code.js +1 -1
- package/lib/module/renderers/code.js.map +1 -1
- package/lib/module/renderers/image.js +6 -1
- 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 +2 -0
- package/lib/module/renderers/list.js.map +1 -1
- package/lib/module/renderers/math.js +4 -2
- package/lib/module/renderers/math.js.map +1 -1
- 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 +10 -2
- 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/headless.d.ts +9 -1
- 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 +7 -2
- package/lib/typescript/commonjs/markdown.d.ts.map +1 -1
- package/lib/typescript/commonjs/renderers/code.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.map +1 -1
- package/lib/typescript/commonjs/renderers/math.d.ts.map +1 -1
- 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/headless.d.ts +9 -1
- 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 +7 -2
- package/lib/typescript/module/markdown.d.ts.map +1 -1
- package/lib/typescript/module/renderers/code.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.map +1 -1
- package/lib/typescript/module/renderers/math.d.ts.map +1 -1
- 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/package.json +5 -3
- package/src/headless.ts +57 -7
- package/src/index.ts +16 -2
- package/src/markdown-stream.tsx +1 -0
- package/src/markdown.tsx +98 -31
- package/src/renderers/code.tsx +23 -16
- package/src/renderers/image.tsx +9 -1
- package/src/renderers/link.tsx +8 -2
- package/src/renderers/list.tsx +2 -0
- package/src/renderers/math.tsx +6 -2
- package/src/renderers/table/cell-content.tsx +15 -4
- package/src/renderers/table/index.tsx +15 -3
- package/src/theme.ts +34 -14
- package/src/utils/code-highlight.ts +133 -44
package/android/CMakeLists.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
cmake_minimum_required(VERSION 3.
|
|
1
|
+
cmake_minimum_required(VERSION 3.18.1...3.28)
|
|
2
2
|
project(NitroMarkdown)
|
|
3
3
|
|
|
4
4
|
set(CMAKE_CXX_STANDARD 20)
|
|
@@ -38,3 +38,10 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
|
|
|
38
38
|
# Include Nitro autolinking (adds nitrogen sources, definitions, and links)
|
|
39
39
|
include(${CMAKE_CURRENT_SOURCE_DIR}/../nitrogen/generated/android/NitroMarkdown+autolinking.cmake)
|
|
40
40
|
|
|
41
|
+
# L5: Per-build-type optimization flags. Release builds get full optimization and strip debug
|
|
42
|
+
# symbols from the binary. Debug builds disable optimization for easier debugging.
|
|
43
|
+
target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE
|
|
44
|
+
$<$<CONFIG:Release>:-O3 -DNDEBUG>
|
|
45
|
+
$<$<CONFIG:Debug>:-O0 -g>
|
|
46
|
+
)
|
|
47
|
+
|
package/android/build.gradle
CHANGED
|
@@ -11,7 +11,10 @@ buildscript {
|
|
|
11
11
|
|
|
12
12
|
def reactNativeArchitectures() {
|
|
13
13
|
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
14
|
-
|
|
14
|
+
// L4: Default to 64-bit only. 32-bit ABIs (armeabi-v7a, x86) are excluded from the default
|
|
15
|
+
// because all Android devices on API 24+ support 64-bit, and 32-bit increases APK size with no
|
|
16
|
+
// practical benefit for this library. Override via gradle.properties if needed.
|
|
17
|
+
return value ? value.split(",") : ["arm64-v8a", "x86_64"]
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
def isNewArchitectureEnabled() {
|
|
@@ -44,10 +47,14 @@ android {
|
|
|
44
47
|
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
45
48
|
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
46
49
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
50
|
+
consumerProguardFiles 'consumer-rules.pro'
|
|
47
51
|
|
|
48
52
|
externalNativeBuild {
|
|
49
53
|
cmake {
|
|
50
|
-
|
|
54
|
+
// L3/L5: -fstack-protector-strong replaces -fstack-protector-all (better coverage/perf
|
|
55
|
+
// tradeoff). -Wformat/-Werror=format-security guard against format-string exploits.
|
|
56
|
+
// -D_FORTIFY_SOURCE=2 enables glibc/Bionic source fortification for buffer overflows.
|
|
57
|
+
cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-strong -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2"
|
|
51
58
|
arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
52
59
|
abiFilters (*reactNativeArchitectures())
|
|
53
60
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# react-native-nitro-markdown consumer ProGuard/R8 rules
|
|
2
|
+
# Preserve all Nitro Hybrid Objects and JNI-facing Kotlin classes
|
|
3
|
+
-keep class com.margelo.nitro.com.nitromarkdown.** { *; }
|
|
4
|
+
-keep class com.nitromarkdown.** { *; }
|
|
5
|
+
|
|
6
|
+
# C5: Explicitly preserve nitrogen-generated Func_* wrapper classes accessed via JNI reflection.
|
|
7
|
+
# These are already covered by the wildcard above, but explicit rules guard against future
|
|
8
|
+
# refactors that might narrow the wildcard, and make the intent clear to ProGuard/R8.
|
|
9
|
+
-keep class com.margelo.nitro.com.nitromarkdown.Func_void { *; }
|
|
10
|
+
-keep class com.margelo.nitro.com.nitromarkdown.Func_void_cxx { *; }
|
|
11
|
+
-keep class com.margelo.nitro.com.nitromarkdown.Func_void_java { *; }
|
|
12
|
+
-keep class com.margelo.nitro.com.nitromarkdown.Func_void_double_double { *; }
|
|
13
|
+
-keep class com.margelo.nitro.com.nitromarkdown.Func_void_double_double_cxx { *; }
|
|
14
|
+
-keep class com.margelo.nitro.com.nitromarkdown.Func_void_double_double_java { *; }
|
|
15
|
+
-keepclassmembers class com.margelo.nitro.com.nitromarkdown.Func_** {
|
|
16
|
+
<init>(...);
|
|
17
|
+
void invoke(...);
|
|
18
|
+
*;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Preserve JNI-registered native methods
|
|
22
|
+
-keepclasseswithmembernames class * {
|
|
23
|
+
native <methods>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Preserve Facebook JNI annotations
|
|
27
|
+
-keep @com.facebook.proguard.annotations.DoNotStrip class * { *; }
|
|
28
|
+
-keepclassmembers class * {
|
|
29
|
+
@com.facebook.proguard.annotations.DoNotStrip *;
|
|
30
|
+
}
|
|
31
|
+
-keep @com.facebook.react.bridge.ReactModule class * { *; }
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
NitroMarkdown_compileSdkVersion=34
|
|
2
|
+
# M3: minSdkVersion kept at 24 (Android 7.0) to match the example app's minimum SDK requirement.
|
|
3
|
+
# Bumping to 26 would be preferable for security (full ASLR, etc.) but would break example builds.
|
|
2
4
|
NitroMarkdown_minSdkVersion=24
|
|
3
5
|
NitroMarkdown_targetSdkVersion=34
|
|
4
6
|
NitroMarkdown_ndkVersion=27.1.12297006
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#include <jni.h>
|
|
2
|
+
#include <fbjni/fbjni.h>
|
|
2
3
|
#include "NitroMarkdownOnLoad.hpp"
|
|
3
4
|
|
|
4
5
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
|
|
5
|
-
return
|
|
6
|
+
return facebook::jni::initialize(vm, []() {
|
|
7
|
+
margelo::nitro::Markdown::registerAllNatives();
|
|
8
|
+
});
|
|
6
9
|
}
|
|
7
10
|
|
|
@@ -1,26 +1,38 @@
|
|
|
1
1
|
package com.margelo.nitro.com.nitromarkdown
|
|
2
2
|
|
|
3
|
+
import androidx.annotation.GuardedBy
|
|
4
|
+
|
|
3
5
|
class HybridMarkdownSession : HybridMarkdownSessionSpec() {
|
|
6
|
+
@GuardedBy("lock")
|
|
4
7
|
private var buffer = StringBuilder()
|
|
8
|
+
|
|
9
|
+
@GuardedBy("lock")
|
|
5
10
|
private val listeners = mutableMapOf<Long, (Double, Double) -> Unit>()
|
|
11
|
+
|
|
12
|
+
@GuardedBy("lock")
|
|
6
13
|
private var nextListenerId = 0L
|
|
7
|
-
private val lock = Any()
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
set(value) {
|
|
11
|
-
synchronized(lock) { field = value }
|
|
12
|
-
// No notify for highlighting to avoid flood
|
|
13
|
-
}
|
|
15
|
+
private val lock = Any()
|
|
14
16
|
|
|
17
|
+
@Volatile
|
|
18
|
+
private var isDestroyed = false
|
|
15
19
|
|
|
20
|
+
@GuardedBy("lock")
|
|
21
|
+
override var highlightPosition: Double = 0.0
|
|
22
|
+
get() = synchronized(lock) { field }
|
|
23
|
+
set(value) = synchronized(lock) { field = value }
|
|
24
|
+
// No notify for highlighting to avoid flood
|
|
16
25
|
|
|
17
26
|
override val memorySize: Long
|
|
18
|
-
get() = buffer.length.toLong()
|
|
27
|
+
get() = synchronized(lock) { buffer.length.toLong() }
|
|
19
28
|
|
|
20
29
|
override fun append(chunk: String): Double {
|
|
21
30
|
val from: Int
|
|
22
31
|
val to: Int
|
|
23
32
|
synchronized(lock) {
|
|
33
|
+
if (buffer.length + chunk.length > MAX_BUFFER_SIZE) {
|
|
34
|
+
throw IllegalArgumentException("Buffer size limit exceeded (max ${MAX_BUFFER_SIZE} chars)")
|
|
35
|
+
}
|
|
24
36
|
from = buffer.length
|
|
25
37
|
buffer.append(chunk)
|
|
26
38
|
to = buffer.length
|
|
@@ -50,38 +62,37 @@ class HybridMarkdownSession : HybridMarkdownSessionSpec() {
|
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
override fun getTextRange(from: Double, to: Double): String {
|
|
65
|
+
if (from.isNaN() || to.isNaN() || from < 0.0) return ""
|
|
53
66
|
synchronized(lock) {
|
|
54
|
-
val start = from.
|
|
55
|
-
val end = to.
|
|
67
|
+
val start = from.toLong().coerceIn(0L, buffer.length.toLong()).toInt()
|
|
68
|
+
val end = to.toLong().coerceIn(start.toLong(), buffer.length.toLong()).toInt()
|
|
56
69
|
return buffer.substring(start, end)
|
|
57
70
|
}
|
|
58
71
|
}
|
|
59
72
|
|
|
60
73
|
override fun addListener(listener: (Double, Double) -> Unit): () -> Unit {
|
|
61
|
-
val id: Long
|
|
62
74
|
synchronized(lock) {
|
|
63
|
-
|
|
75
|
+
if (isDestroyed) throw IllegalStateException("HybridMarkdownSession is destroyed")
|
|
76
|
+
val id = nextListenerId++
|
|
64
77
|
listeners[id] = listener
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
synchronized(lock) {
|
|
68
|
-
listeners.remove(id)
|
|
69
|
-
}
|
|
78
|
+
return { synchronized(lock) { listeners.remove(id) } }
|
|
70
79
|
}
|
|
71
80
|
}
|
|
72
81
|
|
|
73
82
|
override fun reset(text: String) {
|
|
74
83
|
synchronized(lock) {
|
|
75
84
|
buffer.replace(0, buffer.length, text)
|
|
85
|
+
highlightPosition = 0.0
|
|
76
86
|
}
|
|
77
87
|
notifyListeners(0.0, text.length.toDouble())
|
|
78
88
|
}
|
|
79
89
|
|
|
80
90
|
override fun replace(from: Double, to: Double, text: String): Double {
|
|
91
|
+
require(from >= 0.0 && to >= from) { "Invalid range: from=$from must be >= 0 and to=$to must be >= from" }
|
|
81
92
|
val newLength: Double
|
|
82
93
|
synchronized(lock) {
|
|
83
|
-
val start = from.
|
|
84
|
-
val end = to.
|
|
94
|
+
val start = from.toLong().coerceIn(0L, buffer.length.toLong()).toInt()
|
|
95
|
+
val end = to.toLong().coerceIn(start.toLong(), buffer.length.toLong()).toInt()
|
|
85
96
|
buffer.replace(start, end, text)
|
|
86
97
|
newLength = buffer.length.toDouble()
|
|
87
98
|
}
|
|
@@ -90,10 +101,39 @@ class HybridMarkdownSession : HybridMarkdownSessionSpec() {
|
|
|
90
101
|
}
|
|
91
102
|
|
|
92
103
|
private fun notifyListeners(from: Double, to: Double) {
|
|
93
|
-
val
|
|
104
|
+
val snapshot: List<(Double, Double) -> Unit>
|
|
94
105
|
synchronized(lock) {
|
|
95
|
-
|
|
106
|
+
snapshot = listeners.values.toList()
|
|
96
107
|
}
|
|
97
|
-
|
|
108
|
+
for (listener in snapshot) {
|
|
109
|
+
try {
|
|
110
|
+
listener(from, to)
|
|
111
|
+
} catch (e: Throwable) {
|
|
112
|
+
android.util.Log.e(TAG, "Listener callback threw an exception", e)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fun onDestroyed() {
|
|
118
|
+
synchronized(lock) {
|
|
119
|
+
isDestroyed = true
|
|
120
|
+
listeners.clear()
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
override fun dispose() {
|
|
125
|
+
onDestroyed()
|
|
126
|
+
super.dispose()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// H6: The C++ generated file (JHybridMarkdownSessionSpec.cpp) is marked DO NOT MODIFY and
|
|
130
|
+
// cannot be edited. However, fbjni (used by Nitro) automatically propagates Java/Kotlin
|
|
131
|
+
// exceptions thrown across the JNI boundary as C++ exceptions. Therefore, an
|
|
132
|
+
// IllegalArgumentException thrown in append() will be rethrown on the C++ side without
|
|
133
|
+
// requiring manual JNI exception checks in the generated code.
|
|
134
|
+
|
|
135
|
+
companion object {
|
|
136
|
+
private const val TAG = "HybridMarkdownSession"
|
|
137
|
+
private const val MAX_BUFFER_SIZE = 10 * 1024 * 1024 // 10 MB
|
|
98
138
|
}
|
|
99
139
|
}
|
|
@@ -1,28 +1,16 @@
|
|
|
1
1
|
package com.nitromarkdown
|
|
2
2
|
|
|
3
|
-
import com.facebook.react.
|
|
3
|
+
import com.facebook.react.ReactPackage
|
|
4
4
|
import com.facebook.react.bridge.NativeModule
|
|
5
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
-
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
7
6
|
import com.facebook.react.uimanager.ViewManager
|
|
8
7
|
import com.margelo.nitro.com.nitromarkdown.NitroMarkdownOnLoad
|
|
9
8
|
|
|
10
|
-
class NitroMarkdownPackage :
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
NitroMarkdownOnLoad.initializeNative()
|
|
14
|
-
}
|
|
9
|
+
class NitroMarkdownPackage : ReactPackage {
|
|
10
|
+
init {
|
|
11
|
+
NitroMarkdownOnLoad.initializeNative()
|
|
15
12
|
}
|
|
16
13
|
|
|
17
|
-
override fun
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
|
22
|
-
return ReactModuleInfoProvider { emptyMap() }
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
26
|
-
return emptyList()
|
|
27
|
-
}
|
|
14
|
+
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> = emptyList()
|
|
15
|
+
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> = emptyList()
|
|
28
16
|
}
|
|
@@ -79,6 +79,37 @@ inline void appendBoolField(std::string& output, const char* key, bool value) {
|
|
|
79
79
|
output += value ? "true" : "false";
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
static constexpr size_t kMaxEstimatedSize = 64 * 1024 * 1024; // 64 MB cap
|
|
83
|
+
|
|
84
|
+
static size_t estimateJsonSize(const std::shared_ptr<InternalMarkdownNode>& node) noexcept {
|
|
85
|
+
if (!node) return 0;
|
|
86
|
+
size_t size = 64; // base overhead per node (type, beg, end, braces)
|
|
87
|
+
auto safeAdd = [](size_t a, size_t b) -> size_t {
|
|
88
|
+
return (b > kMaxEstimatedSize - a) ? kMaxEstimatedSize : a + b;
|
|
89
|
+
};
|
|
90
|
+
if (node->content && size < kMaxEstimatedSize) {
|
|
91
|
+
size = safeAdd(size, node->content->size());
|
|
92
|
+
}
|
|
93
|
+
if (node->href && size < kMaxEstimatedSize) {
|
|
94
|
+
size = safeAdd(size, node->href->size() + 10);
|
|
95
|
+
}
|
|
96
|
+
if (node->title && size < kMaxEstimatedSize) {
|
|
97
|
+
size = safeAdd(size, node->title->size() + 10);
|
|
98
|
+
}
|
|
99
|
+
if (node->alt && size < kMaxEstimatedSize) {
|
|
100
|
+
size = safeAdd(size, node->alt->size() + 8);
|
|
101
|
+
}
|
|
102
|
+
if (node->language && size < kMaxEstimatedSize) {
|
|
103
|
+
size = safeAdd(size, node->language->size() + 12);
|
|
104
|
+
}
|
|
105
|
+
for (const auto& child : node->children) {
|
|
106
|
+
if (size >= kMaxEstimatedSize) break;
|
|
107
|
+
size_t childSize = estimateJsonSize(child);
|
|
108
|
+
size = safeAdd(size, childSize);
|
|
109
|
+
}
|
|
110
|
+
return size;
|
|
111
|
+
}
|
|
112
|
+
|
|
82
113
|
void appendNodeJson(std::string& output, const std::shared_ptr<InternalMarkdownNode>& node) {
|
|
83
114
|
output.push_back('{');
|
|
84
115
|
|
|
@@ -175,10 +206,6 @@ std::string flattenNodeText(const std::shared_ptr<InternalMarkdownNode>& node) {
|
|
|
175
206
|
case NodeType::MathInline:
|
|
176
207
|
case NodeType::HtmlInline:
|
|
177
208
|
return node->content.value_or("");
|
|
178
|
-
case NodeType::CodeBlock:
|
|
179
|
-
case NodeType::MathBlock:
|
|
180
|
-
case NodeType::HtmlBlock:
|
|
181
|
-
return trimCopy(node->content.value_or("")) + "\n\n";
|
|
182
209
|
case NodeType::LineBreak:
|
|
183
210
|
return "\n";
|
|
184
211
|
case NodeType::SoftBreak:
|
|
@@ -201,6 +228,9 @@ std::string flattenNodeText(const std::shared_ptr<InternalMarkdownNode>& node) {
|
|
|
201
228
|
case NodeType::Paragraph:
|
|
202
229
|
case NodeType::Heading:
|
|
203
230
|
case NodeType::Blockquote:
|
|
231
|
+
case NodeType::CodeBlock:
|
|
232
|
+
case NodeType::MathBlock:
|
|
233
|
+
case NodeType::HtmlBlock:
|
|
204
234
|
return trimCopy(childrenText) + "\n\n";
|
|
205
235
|
case NodeType::ListItem:
|
|
206
236
|
case NodeType::TaskListItem:
|
|
@@ -219,10 +249,8 @@ std::string flattenNodeText(const std::shared_ptr<InternalMarkdownNode>& node) {
|
|
|
219
249
|
} // namespace
|
|
220
250
|
|
|
221
251
|
std::string HybridMarkdownParser::parse(const std::string& text) {
|
|
222
|
-
InternalParserOptions opts;
|
|
223
|
-
|
|
224
|
-
opts.math = true;
|
|
225
|
-
|
|
252
|
+
InternalParserOptions opts{.gfm = true, .math = true};
|
|
253
|
+
|
|
226
254
|
auto ast = parser_->parse(text, opts);
|
|
227
255
|
return nodeToJson(ast);
|
|
228
256
|
}
|
|
@@ -237,9 +265,7 @@ std::string HybridMarkdownParser::parseWithOptions(const std::string& text, cons
|
|
|
237
265
|
}
|
|
238
266
|
|
|
239
267
|
std::string HybridMarkdownParser::extractPlainText(const std::string& text) {
|
|
240
|
-
InternalParserOptions opts;
|
|
241
|
-
opts.gfm = true;
|
|
242
|
-
opts.math = true;
|
|
268
|
+
InternalParserOptions opts{.gfm = true, .math = true};
|
|
243
269
|
|
|
244
270
|
auto ast = parser_->parse(text, opts);
|
|
245
271
|
return flattenNodeText(ast);
|
|
@@ -256,7 +282,7 @@ std::string HybridMarkdownParser::extractPlainTextWithOptions(const std::string&
|
|
|
256
282
|
|
|
257
283
|
std::string HybridMarkdownParser::nodeToJson(const std::shared_ptr<InternalMarkdownNode>& node) {
|
|
258
284
|
std::string json;
|
|
259
|
-
json.reserve(
|
|
285
|
+
json.reserve(estimateJsonSize(node));
|
|
260
286
|
appendNodeJson(json, node);
|
|
261
287
|
return json;
|
|
262
288
|
}
|
|
@@ -17,10 +17,10 @@ public:
|
|
|
17
17
|
|
|
18
18
|
~HybridMarkdownParser() override = default;
|
|
19
19
|
|
|
20
|
-
std::string parse(const std::string& text) override;
|
|
21
|
-
std::string parseWithOptions(const std::string& text, const ParserOptions& options) override;
|
|
22
|
-
std::string extractPlainText(const std::string& text) override;
|
|
23
|
-
std::string extractPlainTextWithOptions(const std::string& text, const ParserOptions& options) override;
|
|
20
|
+
[[nodiscard]] std::string parse(const std::string& text) override;
|
|
21
|
+
[[nodiscard]] std::string parseWithOptions(const std::string& text, const ParserOptions& options) override;
|
|
22
|
+
[[nodiscard]] std::string extractPlainText(const std::string& text) override;
|
|
23
|
+
[[nodiscard]] std::string extractPlainTextWithOptions(const std::string& text, const ParserOptions& options) override;
|
|
24
24
|
|
|
25
25
|
private:
|
|
26
26
|
std::unique_ptr<::NitroMarkdown::MD4CParser> parser_;
|