react-native-marcus 0.1.0
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/LICENSE +201 -0
- package/README.md +227 -0
- package/RNMarcus.podspec +78 -0
- package/android/build.gradle +220 -0
- package/android/proguard-rules.pro +1 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/cpp/CMakeLists.txt +41 -0
- package/android/src/main/cpp/MarkdownParser.cpp +49 -0
- package/android/src/main/cpp/MarkdownParser.h +56 -0
- package/android/src/main/cpp/OnLoad.cpp +17 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusModule.kt +28 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusPackage.kt +31 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorView.kt +63 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorViewManager.kt +32 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorViewManagerSpec.kt +21 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownFormatter.kt +333 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownParseResult.kt +12 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownParser.kt +89 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownRange.kt +11 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownStyle.kt +81 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownTextWatcher.kt +17 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownUtils.kt +84 -0
- package/android/src/main/java/app/getbullet/marcus/ReactLineHeight.java +29 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundColorSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundSpan.kt +105 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBlockIndentSpan.kt +67 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBoldSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontFamilySpan.kt +28 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontSizeSpan.kt +7 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownForegroundColorSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownGapSpan.kt +57 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownItalicSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownLineHeightSpan.kt +21 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownSpan.kt +7 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownStrikethroughSpan.kt +5 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownUnderlineSpan.kt +5 -0
- package/android/src/main/new_arch/CMakeLists.txt +70 -0
- package/android/src/main/new_arch/RNMarcusSpec.cpp +43 -0
- package/android/src/main/new_arch/RNMarcusSpec.h +36 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorShadowNode.cpp +218 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorShadowNode.h +54 -0
- package/android/src/reactNativeVersionPatch/CustomMountingManager/81/app/getbullet/marcus/CustomFabricUIManager.java +156 -0
- package/android/src/reactNativeVersionPatch/CustomMountingManager/latest/app/getbullet/marcus/CustomFabricUIManager.java +130 -0
- package/android/src/test/java/app/getbullet/marcus/MarkdownFormatterTest.kt +205 -0
- package/android/src/test/java/app/getbullet/marcus/RenderModel.kt +78 -0
- package/android/src/test/resources/render-model.txt +583 -0
- package/apple/MarcusModule.h +8 -0
- package/apple/MarcusModule.mm +26 -0
- package/apple/MarcusRange.h +15 -0
- package/apple/MarcusRange.mm +15 -0
- package/apple/MarcusTextInputDecoratorComponentView.h +12 -0
- package/apple/MarcusTextInputDecoratorComponentView.mm +121 -0
- package/apple/MarcusTextInputDecoratorShadowNode.cpp +251 -0
- package/apple/MarcusTextInputDecoratorShadowNode.h +65 -0
- package/apple/MarcusTextInputDecoratorShadowNode.mm +204 -0
- package/apple/MarcusTextInputDecoratorViewManager.h +8 -0
- package/apple/MarcusTextInputDecoratorViewManager.mm +11 -0
- package/apple/MarkdownAdaptiveImageGlyph.swift +44 -0
- package/apple/MarkdownAttributes.h +31 -0
- package/apple/MarkdownAttributes.m +9 -0
- package/apple/MarkdownBackedTextInputDelegate.swift +117 -0
- package/apple/MarkdownDecorator.swift +316 -0
- package/apple/MarkdownFonts.swift +21 -0
- package/apple/MarkdownFormatter+React.swift +51 -0
- package/apple/MarkdownFormatter.swift +509 -0
- package/apple/MarkdownLog.h +10 -0
- package/apple/MarkdownLog.mm +6 -0
- package/apple/MarkdownParser.swift +200 -0
- package/apple/MarkdownSwiftInterop.h +23 -0
- package/apple/MarkdownTextBackground.swift +17 -0
- package/apple/MarkdownTextFieldObserver.swift +90 -0
- package/apple/MarkdownTextLayoutFragment.swift +205 -0
- package/apple/MarkdownTextLayoutManagerDelegate.swift +84 -0
- package/apple/MarkdownTextStorageDelegate.swift +32 -0
- package/apple/MarkdownTextViewObserver.swift +30 -0
- package/apple/RCTMarcusStyle+Codegen.h +18 -0
- package/apple/RCTMarcusStyle+Codegen.mm +64 -0
- package/apple/RCTMarcusStyle.h +41 -0
- package/apple/RCTMarcusStyle.m +5 -0
- package/apple/RCTMarcusUtils.h +26 -0
- package/apple/RCTMarcusUtils.mm +26 -0
- package/apple/RCTTextInput+AdaptiveImageGlyph.h +16 -0
- package/apple/RCTTextInput+AdaptiveImageGlyph.mm +79 -0
- package/apple/RCTTextInputComponentView+Marcus.h +11 -0
- package/apple/RCTTextInputComponentView+Marcus.mm +79 -0
- package/cpp/MarkdownCxx.modulemap +8 -0
- package/cpp/MarkdownGlobal.cpp +48 -0
- package/cpp/MarkdownGlobal.h +33 -0
- package/cpp/MarkdownWorkletParser.cpp +93 -0
- package/cpp/MarkdownWorkletParser.h +45 -0
- package/cpp/RuntimeDecorator.cpp +39 -0
- package/cpp/RuntimeDecorator.h +14 -0
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorState.h +33 -0
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorViewComponentDescriptor.h +17 -0
- package/lib/commonjs/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
- package/lib/commonjs/MarkdownTextInput.js +99 -0
- package/lib/commonjs/MarkdownTextInput.js.map +1 -0
- package/lib/commonjs/MarkdownTextInput.web.js +618 -0
- package/lib/commonjs/MarkdownTextInput.web.js.map +1 -0
- package/lib/commonjs/NativeMarcusModule.js +9 -0
- package/lib/commonjs/NativeMarcusModule.js.map +1 -0
- package/lib/commonjs/commonTypes.js +2 -0
- package/lib/commonjs/commonTypes.js.map +1 -0
- package/lib/commonjs/global.d.js +2 -0
- package/lib/commonjs/global.d.js.map +1 -0
- package/lib/commonjs/index.js +27 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/parser/index.js +7030 -0
- package/lib/commonjs/parser/index.js.map +7 -0
- package/lib/commonjs/rangeUtils.js +71 -0
- package/lib/commonjs/rangeUtils.js.map +1 -0
- package/lib/commonjs/react-native-web.d.js +2 -0
- package/lib/commonjs/react-native-web.d.js.map +1 -0
- package/lib/commonjs/styleUtils.js +91 -0
- package/lib/commonjs/styleUtils.js.map +1 -0
- package/lib/commonjs/web/InputHistory.js +111 -0
- package/lib/commonjs/web/InputHistory.js.map +1 -0
- package/lib/commonjs/web/MarkdownTextInput.css +72 -0
- package/lib/commonjs/web/utils/blockLayout.js +132 -0
- package/lib/commonjs/web/utils/blockLayout.js.map +1 -0
- package/lib/commonjs/web/utils/blockUtils.js +219 -0
- package/lib/commonjs/web/utils/blockUtils.js.map +1 -0
- package/lib/commonjs/web/utils/browserUtils.js +40 -0
- package/lib/commonjs/web/utils/browserUtils.js.map +1 -0
- package/lib/commonjs/web/utils/cursorUtils.js +157 -0
- package/lib/commonjs/web/utils/cursorUtils.js.map +1 -0
- package/lib/commonjs/web/utils/inputUtils.js +152 -0
- package/lib/commonjs/web/utils/inputUtils.js.map +1 -0
- package/lib/commonjs/web/utils/measureUtils.js +0 -0
- package/lib/commonjs/web/utils/measureUtils.js.map +1 -0
- package/lib/commonjs/web/utils/parserUtils.js +552 -0
- package/lib/commonjs/web/utils/parserUtils.js.map +1 -0
- package/lib/commonjs/web/utils/treeUtils.js +109 -0
- package/lib/commonjs/web/utils/treeUtils.js.map +1 -0
- package/lib/commonjs/web/utils/webStyleUtils.js +59 -0
- package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -0
- package/lib/module/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
- package/lib/module/MarkdownTextInput.js +94 -0
- package/lib/module/MarkdownTextInput.js.map +1 -0
- package/lib/module/MarkdownTextInput.web.js +612 -0
- package/lib/module/MarkdownTextInput.web.js.map +1 -0
- package/lib/module/NativeMarcusModule.js +5 -0
- package/lib/module/NativeMarcusModule.js.map +1 -0
- package/lib/module/commonTypes.js +2 -0
- package/lib/module/commonTypes.js.map +1 -0
- package/lib/module/global.d.js +2 -0
- package/lib/module/global.d.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/parser/index.js +7029 -0
- package/lib/module/parser/index.js.map +7 -0
- package/lib/module/rangeUtils.js +66 -0
- package/lib/module/rangeUtils.js.map +1 -0
- package/lib/module/react-native-web.d.js +2 -0
- package/lib/module/react-native-web.d.js.map +1 -0
- package/lib/module/styleUtils.js +88 -0
- package/lib/module/styleUtils.js.map +1 -0
- package/lib/module/web/InputHistory.js +106 -0
- package/lib/module/web/InputHistory.js.map +1 -0
- package/lib/module/web/MarkdownTextInput.css +72 -0
- package/lib/module/web/utils/blockLayout.js +128 -0
- package/lib/module/web/utils/blockLayout.js.map +1 -0
- package/lib/module/web/utils/blockUtils.js +210 -0
- package/lib/module/web/utils/blockUtils.js.map +1 -0
- package/lib/module/web/utils/browserUtils.js +36 -0
- package/lib/module/web/utils/browserUtils.js.map +1 -0
- package/lib/module/web/utils/cursorUtils.js +150 -0
- package/lib/module/web/utils/cursorUtils.js.map +1 -0
- package/lib/module/web/utils/inputUtils.js +144 -0
- package/lib/module/web/utils/inputUtils.js.map +1 -0
- package/lib/module/web/utils/measureUtils.js +0 -0
- package/lib/module/web/utils/measureUtils.js.map +1 -0
- package/lib/module/web/utils/parserUtils.js +547 -0
- package/lib/module/web/utils/parserUtils.js.map +1 -0
- package/lib/module/web/utils/treeUtils.js +103 -0
- package/lib/module/web/utils/treeUtils.js.map +1 -0
- package/lib/module/web/utils/webStyleUtils.js +54 -0
- package/lib/module/web/utils/webStyleUtils.js.map +1 -0
- package/lib/typescript/commonjs/e2e/checks.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/checks.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/e2e/editing.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/editing.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/e2e/helpers.d.ts +23 -0
- package/lib/typescript/commonjs/e2e/helpers.d.ts.map +1 -0
- package/lib/typescript/commonjs/e2e/smoke.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/smoke.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +63 -0
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts +22 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts +41 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/NativeMarcusModule.d.ts +7 -0
- package/lib/typescript/commonjs/src/NativeMarcusModule.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/commonTypes.d.ts +10 -0
- package/lib/typescript/commonjs/src/commonTypes.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +5 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/parser/index.d.ts +8 -0
- package/lib/typescript/commonjs/src/parser/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts +8 -0
- package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/rangeUtils.d.ts +6 -0
- package/lib/typescript/commonjs/src/rangeUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/styleUtils.d.ts +8 -0
- package/lib/typescript/commonjs/src/styleUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/InputHistory.d.ts +24 -0
- package/lib/typescript/commonjs/src/web/InputHistory.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts +41 -0
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts +33 -0
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/browserUtils.d.ts +7 -0
- package/lib/typescript/commonjs/src/web/utils/browserUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/cursorUtils.d.ts +12 -0
- package/lib/typescript/commonjs/src/web/utils/cursorUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/inputUtils.d.ts +12 -0
- package/lib/typescript/commonjs/src/web/utils/inputUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/measureUtils.d.ts +22 -0
- package/lib/typescript/commonjs/src/web/utils/measureUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts +44 -0
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts +25 -0
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts +7 -0
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts.map +1 -0
- package/lib/typescript/module/e2e/checks.test.d.ts +2 -0
- package/lib/typescript/module/e2e/checks.test.d.ts.map +1 -0
- package/lib/typescript/module/e2e/editing.test.d.ts +2 -0
- package/lib/typescript/module/e2e/editing.test.d.ts.map +1 -0
- package/lib/typescript/module/e2e/helpers.d.ts +23 -0
- package/lib/typescript/module/e2e/helpers.d.ts.map +1 -0
- package/lib/typescript/module/e2e/smoke.test.d.ts +2 -0
- package/lib/typescript/module/e2e/smoke.test.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +63 -0
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownTextInput.d.ts +22 -0
- package/lib/typescript/module/src/MarkdownTextInput.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts +41 -0
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts.map +1 -0
- package/lib/typescript/module/src/NativeMarcusModule.d.ts +7 -0
- package/lib/typescript/module/src/NativeMarcusModule.d.ts.map +1 -0
- package/lib/typescript/module/src/commonTypes.d.ts +10 -0
- package/lib/typescript/module/src/commonTypes.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +5 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -0
- package/lib/typescript/module/src/parser/index.d.ts +8 -0
- package/lib/typescript/module/src/parser/index.d.ts.map +1 -0
- package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts +8 -0
- package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts.map +1 -0
- package/lib/typescript/module/src/rangeUtils.d.ts +6 -0
- package/lib/typescript/module/src/rangeUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/styleUtils.d.ts +8 -0
- package/lib/typescript/module/src/styleUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/InputHistory.d.ts +24 -0
- package/lib/typescript/module/src/web/InputHistory.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts +41 -0
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts +33 -0
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/browserUtils.d.ts +7 -0
- package/lib/typescript/module/src/web/utils/browserUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/cursorUtils.d.ts +12 -0
- package/lib/typescript/module/src/web/utils/cursorUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/inputUtils.d.ts +12 -0
- package/lib/typescript/module/src/web/utils/inputUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/measureUtils.d.ts +22 -0
- package/lib/typescript/module/src/web/utils/measureUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts +44 -0
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts +25 -0
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts +7 -0
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts.map +1 -0
- package/package.json +167 -0
- package/react-native.config.js +10 -0
- package/src/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
- package/src/MarkdownTextInput.tsx +139 -0
- package/src/MarkdownTextInput.web.tsx +958 -0
- package/src/NativeMarcusModule.ts +8 -0
- package/src/commonTypes.ts +30 -0
- package/src/global.d.ts +1 -0
- package/src/index.tsx +4 -0
- package/src/parser/index.ts +556 -0
- package/src/parser/micromark-extension-mentions.ts +82 -0
- package/src/rangeUtils.ts +68 -0
- package/src/react-native-web.d.ts +1 -0
- package/src/styleUtils.ts +100 -0
- package/src/web/InputHistory.ts +137 -0
- package/src/web/MarkdownTextInput.css +72 -0
- package/src/web/utils/blockLayout.ts +163 -0
- package/src/web/utils/blockUtils.ts +245 -0
- package/src/web/utils/browserUtils.ts +41 -0
- package/src/web/utils/cursorUtils.ts +204 -0
- package/src/web/utils/inputUtils.ts +206 -0
- package/src/web/utils/measureUtils.ts +0 -0
- package/src/web/utils/parserUtils.ts +813 -0
- package/src/web/utils/treeUtils.ts +149 -0
- package/src/web/utils/webStyleUtils.ts +69 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#import <RNMarcus/RCTMarcusStyle.h>
|
|
2
|
+
|
|
3
|
+
#import <react/renderer/components/RNMarcusSpec/Props.h>
|
|
4
|
+
|
|
5
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
6
|
+
|
|
7
|
+
// Conversion from the codegen props struct lives in a separate category so that
|
|
8
|
+
// `RCTMarcusStyle.h` itself stays free of C++. Only the component view and
|
|
9
|
+
// the shadow node need this header; everything downstream can import the plain
|
|
10
|
+
// one.
|
|
11
|
+
@interface RCTMarcusStyle (Codegen)
|
|
12
|
+
|
|
13
|
+
- (instancetype)initWithStruct:
|
|
14
|
+
(const facebook::react::MarcusTextInputDecoratorViewMarkdownStyleStruct &)style;
|
|
15
|
+
|
|
16
|
+
@end
|
|
17
|
+
|
|
18
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#import <RNMarcus/RCTMarcusStyle+Codegen.h>
|
|
2
|
+
|
|
3
|
+
#import <React/RCTConversions.h>
|
|
4
|
+
|
|
5
|
+
@implementation RCTMarcusStyle (Codegen)
|
|
6
|
+
|
|
7
|
+
- (instancetype)initWithStruct:
|
|
8
|
+
(const facebook::react::MarcusTextInputDecoratorViewMarkdownStyleStruct &)style {
|
|
9
|
+
if (self = [super init]) {
|
|
10
|
+
self.syntaxColor = RCTUIColorFromSharedColor(style.syntax.color);
|
|
11
|
+
|
|
12
|
+
self.linkColor = RCTUIColorFromSharedColor(style.link.color);
|
|
13
|
+
|
|
14
|
+
self.headingFontSize = style.heading.fontSize;
|
|
15
|
+
self.headingScale = style.heading.scale;
|
|
16
|
+
|
|
17
|
+
self.emojiFontSize = style.emoji.fontSize;
|
|
18
|
+
self.emojiFontFamily = RCTNSStringFromString(style.emoji.fontFamily);
|
|
19
|
+
|
|
20
|
+
self.blockquoteBorderColor =
|
|
21
|
+
RCTUIColorFromSharedColor(style.blockquote.borderColor);
|
|
22
|
+
self.blockquoteBorderWidth = style.blockquote.borderWidth;
|
|
23
|
+
self.blockquoteMarginLeft = style.blockquote.marginLeft;
|
|
24
|
+
self.blockquotePaddingLeft = style.blockquote.paddingLeft;
|
|
25
|
+
|
|
26
|
+
self.orderedListMarginLeft = style.orderedList.marginLeft;
|
|
27
|
+
self.orderedListPaddingLeft = style.orderedList.paddingLeft;
|
|
28
|
+
|
|
29
|
+
self.unorderedListMarginLeft = style.unorderedList.marginLeft;
|
|
30
|
+
self.unorderedListPaddingLeft = style.unorderedList.paddingLeft;
|
|
31
|
+
|
|
32
|
+
self.codeFontFamily = RCTNSStringFromString(style.code.fontFamily);
|
|
33
|
+
self.codeFontSize = style.code.fontSize;
|
|
34
|
+
self.codeColor = RCTUIColorFromSharedColor(style.code.color);
|
|
35
|
+
self.codeBackgroundColor =
|
|
36
|
+
RCTUIColorFromSharedColor(style.code.backgroundColor);
|
|
37
|
+
|
|
38
|
+
self.preFontFamily = RCTNSStringFromString(style.pre.fontFamily);
|
|
39
|
+
self.preFontSize = style.pre.fontSize;
|
|
40
|
+
self.preColor = RCTUIColorFromSharedColor(style.pre.color);
|
|
41
|
+
self.preBackgroundColor =
|
|
42
|
+
RCTUIColorFromSharedColor(style.pre.backgroundColor);
|
|
43
|
+
|
|
44
|
+
self.mentionHereColor = RCTUIColorFromSharedColor(style.mentionHere.color);
|
|
45
|
+
self.mentionHereBackgroundColor =
|
|
46
|
+
RCTUIColorFromSharedColor(style.mentionHere.backgroundColor);
|
|
47
|
+
self.mentionHereBorderRadius = style.mentionHere.borderRadius;
|
|
48
|
+
|
|
49
|
+
self.mentionUserColor = RCTUIColorFromSharedColor(style.mentionUser.color);
|
|
50
|
+
self.mentionUserBackgroundColor =
|
|
51
|
+
RCTUIColorFromSharedColor(style.mentionUser.backgroundColor);
|
|
52
|
+
self.mentionUserBorderRadius = style.mentionUser.borderRadius;
|
|
53
|
+
|
|
54
|
+
self.mentionReportColor =
|
|
55
|
+
RCTUIColorFromSharedColor(style.mentionReport.color);
|
|
56
|
+
self.mentionReportBackgroundColor =
|
|
57
|
+
RCTUIColorFromSharedColor(style.mentionReport.backgroundColor);
|
|
58
|
+
self.mentionReportBorderRadius = style.mentionReport.borderRadius;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return self;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#import <UIKit/UIKit.h>
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
@interface RCTMarcusStyle : NSObject
|
|
6
|
+
|
|
7
|
+
@property(nonatomic) UIColor *syntaxColor;
|
|
8
|
+
@property(nonatomic) UIColor *linkColor;
|
|
9
|
+
@property(nonatomic) CGFloat headingFontSize;
|
|
10
|
+
@property(nonatomic) CGFloat headingScale;
|
|
11
|
+
@property(nonatomic) CGFloat emojiFontSize;
|
|
12
|
+
@property(nonatomic) NSString *emojiFontFamily;
|
|
13
|
+
@property(nonatomic) UIColor *blockquoteBorderColor;
|
|
14
|
+
@property(nonatomic) CGFloat blockquoteBorderWidth;
|
|
15
|
+
@property(nonatomic) CGFloat blockquoteMarginLeft;
|
|
16
|
+
@property(nonatomic) CGFloat blockquotePaddingLeft;
|
|
17
|
+
@property(nonatomic) CGFloat orderedListMarginLeft;
|
|
18
|
+
@property(nonatomic) CGFloat orderedListPaddingLeft;
|
|
19
|
+
@property(nonatomic) CGFloat unorderedListMarginLeft;
|
|
20
|
+
@property(nonatomic) CGFloat unorderedListPaddingLeft;
|
|
21
|
+
@property(nonatomic) NSString *codeFontFamily;
|
|
22
|
+
@property(nonatomic) CGFloat codeFontSize;
|
|
23
|
+
@property(nonatomic) UIColor *codeColor;
|
|
24
|
+
@property(nonatomic) UIColor *codeBackgroundColor;
|
|
25
|
+
@property(nonatomic) NSString *preFontFamily;
|
|
26
|
+
@property(nonatomic) CGFloat preFontSize;
|
|
27
|
+
@property(nonatomic) UIColor *preColor;
|
|
28
|
+
@property(nonatomic) UIColor *preBackgroundColor;
|
|
29
|
+
@property(nonatomic) UIColor *mentionHereColor;
|
|
30
|
+
@property(nonatomic) UIColor *mentionHereBackgroundColor;
|
|
31
|
+
@property(nonatomic) CGFloat mentionHereBorderRadius;
|
|
32
|
+
@property(nonatomic) UIColor *mentionUserColor;
|
|
33
|
+
@property(nonatomic) UIColor *mentionUserBackgroundColor;
|
|
34
|
+
@property(nonatomic) CGFloat mentionUserBorderRadius;
|
|
35
|
+
@property(nonatomic) UIColor *mentionReportColor;
|
|
36
|
+
@property(nonatomic) UIColor *mentionReportBackgroundColor;
|
|
37
|
+
@property(nonatomic) CGFloat mentionReportBorderRadius;
|
|
38
|
+
|
|
39
|
+
@end
|
|
40
|
+
|
|
41
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#import <RNMarcus/RCTMarcusStyle.h>
|
|
2
|
+
#import <UIKit/UIKit.h>
|
|
3
|
+
|
|
4
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
5
|
+
|
|
6
|
+
// Parses and formats in one step for the view layer.
|
|
7
|
+
//
|
|
8
|
+
// Parsing is stateful (it owns the parser's memo cache) so it stays here rather
|
|
9
|
+
// than moving to Swift; formatting itself is a stateless Swift call. One
|
|
10
|
+
// instance is owned per text input and only ever touched on the main thread.
|
|
11
|
+
@interface RCTMarcusUtils : NSObject
|
|
12
|
+
|
|
13
|
+
// Nullable: both are assigned after init (props arrive after the view
|
|
14
|
+
// hierarchy is built), and applyMarkdownFormatting: bails out when either is
|
|
15
|
+
// still nil.
|
|
16
|
+
@property(nonatomic, nullable) RCTMarcusStyle *markdownStyle;
|
|
17
|
+
@property(nonatomic, nullable) NSNumber *parserId;
|
|
18
|
+
|
|
19
|
+
- (void)applyMarkdownFormatting:
|
|
20
|
+
(nonnull NSMutableAttributedString *)attributedString
|
|
21
|
+
withDefaultTextAttributes:
|
|
22
|
+
(nonnull NSDictionary<NSAttributedStringKey, id> *)defaultTextAttributes;
|
|
23
|
+
|
|
24
|
+
@end
|
|
25
|
+
|
|
26
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#import <RNMarcus/MarkdownSwiftInterop.h>
|
|
2
|
+
#import <RNMarcus/RCTMarcusUtils.h>
|
|
3
|
+
|
|
4
|
+
@implementation RCTMarcusUtils
|
|
5
|
+
|
|
6
|
+
- (void)applyMarkdownFormatting:
|
|
7
|
+
(nonnull NSMutableAttributedString *)attributedString
|
|
8
|
+
withDefaultTextAttributes:
|
|
9
|
+
(nonnull NSDictionary<NSAttributedStringKey, id> *)defaultTextAttributes {
|
|
10
|
+
// `markdownStyle` and `parserId` may not be set yet: props are applied after
|
|
11
|
+
// the view hierarchy is built.
|
|
12
|
+
if (_markdownStyle == nil || _parserId == nil) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
NSArray<MarcusRange *> *markdownRanges =
|
|
17
|
+
[[MarkdownParser sharedParser] parse:attributedString.string
|
|
18
|
+
withParserId:_parserId];
|
|
19
|
+
|
|
20
|
+
[MarkdownFormatter formatAttributedString:attributedString
|
|
21
|
+
defaultTextAttributes:defaultTextAttributes
|
|
22
|
+
ranges:markdownRanges
|
|
23
|
+
style:_markdownStyle];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#import <React/RCTUITextField.h>
|
|
2
|
+
#import <React/RCTUITextView.h>
|
|
3
|
+
|
|
4
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
5
|
+
|
|
6
|
+
@interface RCTUITextField (AdaptiveImageGlyph)
|
|
7
|
+
- (void)marcus_insertAdaptiveImageGlyph:(id)glyph
|
|
8
|
+
replacementRange:(NSRange)replacementRange;
|
|
9
|
+
@end
|
|
10
|
+
|
|
11
|
+
@interface RCTUITextView (AdaptiveImageGlyph)
|
|
12
|
+
- (void)marcus_insertAdaptiveImageGlyph:(id)glyph
|
|
13
|
+
replacementRange:(NSRange)replacementRange;
|
|
14
|
+
@end
|
|
15
|
+
|
|
16
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#import <RNMarcus/MarkdownSwiftInterop.h>
|
|
2
|
+
#import <RNMarcus/RCTTextInput+AdaptiveImageGlyph.h>
|
|
3
|
+
|
|
4
|
+
#import <objc/runtime.h>
|
|
5
|
+
|
|
6
|
+
// Kept as .mm rather than .m: plain Objective-C translation units in this pod
|
|
7
|
+
// compile with modules enabled, which makes importing any React header build
|
|
8
|
+
// the React clang module -- and that fails against React Native's prebuilt
|
|
9
|
+
// xcframework. Objective-C++ uses textual includes and avoids it.
|
|
10
|
+
|
|
11
|
+
// Installs `marcus_insertAdaptiveImageGlyph:replacementRange:` in place
|
|
12
|
+
// of UIKit's `insertAdaptiveImageGlyph:replacementRange:`. The handler itself
|
|
13
|
+
// is in Swift; only the runtime plumbing has to live here, because Swift cannot
|
|
14
|
+
// define
|
|
15
|
+
// `+load`.
|
|
16
|
+
static void
|
|
17
|
+
RCTMarcusSwizzleAdaptiveImageGlyph(Class targetClass, SEL swizzledSelector) {
|
|
18
|
+
if (@available(iOS 18.0, *)) {
|
|
19
|
+
SEL originalSelector = @selector(insertAdaptiveImageGlyph:
|
|
20
|
+
replacementRange:);
|
|
21
|
+
|
|
22
|
+
if (![targetClass instancesRespondToSelector:originalSelector]) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
Method originalMethod =
|
|
27
|
+
class_getInstanceMethod(targetClass, originalSelector);
|
|
28
|
+
Method swizzledMethod =
|
|
29
|
+
class_getInstanceMethod(targetClass, swizzledSelector);
|
|
30
|
+
if (!originalMethod || !swizzledMethod) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
BOOL didAddMethod = class_addMethod(
|
|
35
|
+
targetClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)
|
|
36
|
+
);
|
|
37
|
+
if (didAddMethod) {
|
|
38
|
+
class_replaceMethod(targetClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
|
|
39
|
+
} else {
|
|
40
|
+
method_exchangeImplementations(originalMethod, swizzledMethod);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@implementation RCTUITextField (AdaptiveImageGlyph)
|
|
46
|
+
|
|
47
|
+
+ (void)load {
|
|
48
|
+
static dispatch_once_t onceToken;
|
|
49
|
+
dispatch_once(&onceToken, ^{
|
|
50
|
+
RCTMarcusSwizzleAdaptiveImageGlyph([self class], @selector(marcus_insertAdaptiveImageGlyph:
|
|
51
|
+
replacementRange:));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
- (void)marcus_insertAdaptiveImageGlyph:(id)glyph
|
|
56
|
+
replacementRange:(NSRange)replacementRange {
|
|
57
|
+
// Deliberately does not call through to the original: the glyph is pasted as
|
|
58
|
+
// a plain image instead of being inserted as an adaptive glyph.
|
|
59
|
+
[MarkdownAdaptiveImageGlyph handlePasteInTextInputView:self glyph:glyph];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@end
|
|
63
|
+
|
|
64
|
+
@implementation RCTUITextView (AdaptiveImageGlyph)
|
|
65
|
+
|
|
66
|
+
+ (void)load {
|
|
67
|
+
static dispatch_once_t onceToken;
|
|
68
|
+
dispatch_once(&onceToken, ^{
|
|
69
|
+
RCTMarcusSwizzleAdaptiveImageGlyph([self class], @selector(marcus_insertAdaptiveImageGlyph:
|
|
70
|
+
replacementRange:));
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
- (void)marcus_insertAdaptiveImageGlyph:(id)glyph
|
|
75
|
+
replacementRange:(NSRange)replacementRange {
|
|
76
|
+
[MarkdownAdaptiveImageGlyph handlePasteInTextInputView:self glyph:glyph];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#import <RNMarcus/MarkdownAttributes.h>
|
|
2
|
+
#import <RNMarcus/MarcusTextInputDecoratorComponentView.h>
|
|
3
|
+
#import <RNMarcus/RCTTextInputComponentView+Marcus.h>
|
|
4
|
+
#import <objc/message.h>
|
|
5
|
+
|
|
6
|
+
@implementation RCTTextInputComponentView (Marcus)
|
|
7
|
+
|
|
8
|
+
- (BOOL)marcus__textOf:(NSAttributedString *)newText
|
|
9
|
+
equals:(NSAttributedString *)oldText {
|
|
10
|
+
__block BOOL isMarkdownTextInput = false;
|
|
11
|
+
[oldText enumerateAttribute:RCTMarcusTextAttributeName
|
|
12
|
+
inRange:NSMakeRange(0, oldText.length)
|
|
13
|
+
options:0
|
|
14
|
+
usingBlock:^(id value, NSRange range, BOOL *stop) {
|
|
15
|
+
if (value) {
|
|
16
|
+
isMarkdownTextInput = true;
|
|
17
|
+
*stop = YES;
|
|
18
|
+
}
|
|
19
|
+
}];
|
|
20
|
+
if (isMarkdownTextInput) {
|
|
21
|
+
return [newText.string isEqualToString:oldText.string];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return [self marcus__textOf:newText equals:oldText];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
- (void)marcus_didAddSubview:(UIView *)subview {
|
|
28
|
+
[self marcus_didAddSubview:subview];
|
|
29
|
+
|
|
30
|
+
// React Native swaps `_backedTextInputView` in place when `multiline`
|
|
31
|
+
// changes: it removes the old view from this component view and adds a new
|
|
32
|
+
// one. That never touches the decorator's own subviews, so its didAddSubview:
|
|
33
|
+
// does not fire and its observers would stay bound to the discarded view --
|
|
34
|
+
// keeping it alive and leaving the new, visible one unformatted.
|
|
35
|
+
UIView *parent = self.superview;
|
|
36
|
+
if ([parent isKindOfClass:[MarcusTextInputDecoratorComponentView class]]) {
|
|
37
|
+
[(MarcusTextInputDecoratorComponentView *)
|
|
38
|
+
parent reattachTextInputObservers];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Installs `swizzledSelector` in place of `originalSelector` on `cls`.
|
|
43
|
+
//
|
|
44
|
+
// method_exchangeImplementations alone is only safe when `cls` implements
|
|
45
|
+
// `originalSelector` itself. For an inherited method -- didAddSubview:, for
|
|
46
|
+
// instance -- class_getInstanceMethod returns the *superclass's* method, and
|
|
47
|
+
// exchanging it rewrites UIView for every view in the app. So the
|
|
48
|
+
// implementation is added to this class first; when that succeeds, the
|
|
49
|
+
// "call the original" selector is pointed at the inherited implementation.
|
|
50
|
+
static void
|
|
51
|
+
RCTMarcusSwizzle(Class cls, SEL originalSelector, SEL swizzledSelector) {
|
|
52
|
+
Method originalMethod = class_getInstanceMethod(cls, originalSelector);
|
|
53
|
+
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector);
|
|
54
|
+
if (originalMethod == NULL || swizzledMethod == NULL) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
BOOL didAddMethod = class_addMethod(cls, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
|
|
59
|
+
if (didAddMethod) {
|
|
60
|
+
class_replaceMethod(cls, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
|
|
61
|
+
} else {
|
|
62
|
+
method_exchangeImplementations(originalMethod, swizzledMethod);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
+ (void)load {
|
|
67
|
+
static dispatch_once_t onceToken;
|
|
68
|
+
dispatch_once(&onceToken, ^{
|
|
69
|
+
Class cls = [self class];
|
|
70
|
+
|
|
71
|
+
// Implemented by this class, so this is a plain exchange.
|
|
72
|
+
RCTMarcusSwizzle(cls, @selector(_textOf:equals:), @selector(marcus__textOf:equals:));
|
|
73
|
+
|
|
74
|
+
// Inherited from UIView, so this must go through class_addMethod.
|
|
75
|
+
RCTMarcusSwizzle(cls, @selector(didAddSubview:), @selector(marcus_didAddSubview:));
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Exposes the pure-C++ parser to Swift. Kept separate from the pod's generated
|
|
2
|
+
// Objective-C umbrella, which Swift parses in Objective-C mode and therefore
|
|
3
|
+
// cannot contain any C++.
|
|
4
|
+
module MarkdownCxx {
|
|
5
|
+
header "MarkdownWorkletParser.h"
|
|
6
|
+
requires cplusplus
|
|
7
|
+
export *
|
|
8
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#include "MarkdownGlobal.h"
|
|
2
|
+
|
|
3
|
+
#include <unordered_map>
|
|
4
|
+
|
|
5
|
+
using namespace facebook;
|
|
6
|
+
|
|
7
|
+
namespace bulletpoint {
|
|
8
|
+
namespace marcus {
|
|
9
|
+
|
|
10
|
+
std::shared_ptr<WorkletRuntime> globalMarkdownWorkletRuntime;
|
|
11
|
+
|
|
12
|
+
void
|
|
13
|
+
setMarkdownRuntime(const std::shared_ptr<WorkletRuntime> &markdownWorkletRuntime) {
|
|
14
|
+
globalMarkdownWorkletRuntime = markdownWorkletRuntime;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
std::shared_ptr<WorkletRuntime>
|
|
18
|
+
getMarkdownRuntime() {
|
|
19
|
+
return globalMarkdownWorkletRuntime;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
std::unordered_map<int, std::shared_ptr<SerializableWorklet>> globalMarkdownShareableWorklets;
|
|
23
|
+
std::mutex globalMarkdownShareableWorkletsMutex;
|
|
24
|
+
int nextParserId = 1;
|
|
25
|
+
|
|
26
|
+
const int
|
|
27
|
+
registerMarkdownWorklet(const std::shared_ptr<SerializableWorklet> &markdownWorklet) {
|
|
28
|
+
assert(markdownWorklet != nullptr);
|
|
29
|
+
auto parserId = nextParserId++;
|
|
30
|
+
std::unique_lock<std::mutex> lock(globalMarkdownShareableWorkletsMutex);
|
|
31
|
+
globalMarkdownShareableWorklets[parserId] = markdownWorklet;
|
|
32
|
+
return parserId;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
void
|
|
36
|
+
unregisterMarkdownWorklet(const int parserId) {
|
|
37
|
+
std::unique_lock<std::mutex> lock(globalMarkdownShareableWorkletsMutex);
|
|
38
|
+
globalMarkdownShareableWorklets.erase(parserId);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
std::shared_ptr<SerializableWorklet>
|
|
42
|
+
getMarkdownWorklet(const int parserId) {
|
|
43
|
+
std::unique_lock<std::mutex> lock(globalMarkdownShareableWorkletsMutex);
|
|
44
|
+
return globalMarkdownShareableWorklets.at(parserId);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
} // namespace marcus
|
|
48
|
+
} // namespace bulletpoint
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <jsi/jsi.h>
|
|
4
|
+
|
|
5
|
+
#include <worklets/WorkletRuntime/WorkletRuntime.h>
|
|
6
|
+
|
|
7
|
+
using namespace facebook;
|
|
8
|
+
using namespace worklets;
|
|
9
|
+
|
|
10
|
+
namespace bulletpoint {
|
|
11
|
+
namespace marcus {
|
|
12
|
+
|
|
13
|
+
void
|
|
14
|
+
setMarkdownRuntime(
|
|
15
|
+
const std::shared_ptr<WorkletRuntime> &markdownWorkletRuntime
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
std::shared_ptr<WorkletRuntime>
|
|
19
|
+
getMarkdownRuntime();
|
|
20
|
+
|
|
21
|
+
const int
|
|
22
|
+
registerMarkdownWorklet(
|
|
23
|
+
const std::shared_ptr<SerializableWorklet> &markdownWorklet
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
void
|
|
27
|
+
unregisterMarkdownWorklet(const int parserId);
|
|
28
|
+
|
|
29
|
+
std::shared_ptr<SerializableWorklet>
|
|
30
|
+
getMarkdownWorklet(const int parserId);
|
|
31
|
+
|
|
32
|
+
} // namespace marcus
|
|
33
|
+
} // namespace bulletpoint
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#include "MarkdownWorkletParser.h"
|
|
2
|
+
|
|
3
|
+
#include "MarkdownGlobal.h"
|
|
4
|
+
|
|
5
|
+
#include <stdexcept>
|
|
6
|
+
#include <string>
|
|
7
|
+
#include <utility>
|
|
8
|
+
|
|
9
|
+
namespace bulletpoint {
|
|
10
|
+
namespace marcus {
|
|
11
|
+
|
|
12
|
+
namespace {
|
|
13
|
+
|
|
14
|
+
bool
|
|
15
|
+
requiresDepth(const std::string &type) {
|
|
16
|
+
return type == "blockquote" || type == "heading" ||
|
|
17
|
+
type == "list-ordered" || type == "list-unordered";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
constexpr int kMaxDepth = 6;
|
|
21
|
+
|
|
22
|
+
} // namespace
|
|
23
|
+
|
|
24
|
+
ParseResult
|
|
25
|
+
parseMarkdown(const std::string &utf8Text, size_t textLengthUtf16, int parserId) {
|
|
26
|
+
const auto &markdownRuntime = getMarkdownRuntime();
|
|
27
|
+
jsi::Runtime &rt = markdownRuntime->getJSIRuntime();
|
|
28
|
+
|
|
29
|
+
std::shared_ptr<SerializableWorklet> markdownWorklet;
|
|
30
|
+
try {
|
|
31
|
+
markdownWorklet = getMarkdownWorklet(parserId);
|
|
32
|
+
} catch (const std::out_of_range &) {
|
|
33
|
+
// Parser was never registered, or was already unregistered.
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const auto &input = jsi::String::createFromUtf8(rt, utf8Text);
|
|
38
|
+
|
|
39
|
+
jsi::Value output;
|
|
40
|
+
try {
|
|
41
|
+
output = markdownRuntime->runGuarded(markdownWorklet, input);
|
|
42
|
+
} catch (const jsi::JSError &) {
|
|
43
|
+
// Skip formatting; runGuarded will have surfaced the error in LogBox.
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
std::vector<MarkdownRange> ranges;
|
|
48
|
+
try {
|
|
49
|
+
const auto &items = output.asObject(rt).asArray(rt);
|
|
50
|
+
const size_t count = items.size(rt);
|
|
51
|
+
ranges.reserve(count);
|
|
52
|
+
|
|
53
|
+
for (size_t i = 0; i < count; ++i) {
|
|
54
|
+
const auto &item = items.getValueAtIndex(rt, i).asObject(rt);
|
|
55
|
+
auto type = item.getProperty(rt, "type").asString(rt).utf8(rt);
|
|
56
|
+
const auto start =
|
|
57
|
+
static_cast<int>(item.getProperty(rt, "start").asNumber());
|
|
58
|
+
const auto length =
|
|
59
|
+
static_cast<int>(item.getProperty(rt, "length").asNumber());
|
|
60
|
+
int depth = 1;
|
|
61
|
+
if (item.hasProperty(rt, "depth")) {
|
|
62
|
+
depth = static_cast<int>(item.getProperty(rt, "depth").asNumber());
|
|
63
|
+
|
|
64
|
+
if (requiresDepth(type) && depth < 1) {
|
|
65
|
+
return {{}, "range of type '" + type + "' has a `depth` of " + std::to_string(depth) + ", which must be at least 1"};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (depth > kMaxDepth) {
|
|
69
|
+
depth = kMaxDepth;
|
|
70
|
+
}
|
|
71
|
+
} else if (requiresDepth(type)) {
|
|
72
|
+
return {{}, "range of type '" + type + "' is missing `depth`"};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// `start` is checked explicitly because the offsets come from JS and a
|
|
76
|
+
// negative one would wrap when converted to an unsigned platform range.
|
|
77
|
+
if (length <= 0 || start < 0 ||
|
|
78
|
+
static_cast<size_t>(start) + static_cast<size_t>(length) >
|
|
79
|
+
textLengthUtf16) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
ranges.push_back(MarkdownRange{std::move(type), start, length, depth});
|
|
84
|
+
}
|
|
85
|
+
} catch (const jsi::JSError &error) {
|
|
86
|
+
return {{}, error.getMessage()};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {std::move(ranges), ""};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
} // namespace marcus
|
|
93
|
+
} // namespace bulletpoint
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstddef>
|
|
4
|
+
#include <string>
|
|
5
|
+
#include <vector>
|
|
6
|
+
|
|
7
|
+
namespace bulletpoint {
|
|
8
|
+
namespace marcus {
|
|
9
|
+
|
|
10
|
+
struct MarkdownRange {
|
|
11
|
+
std::string type;
|
|
12
|
+
int start;
|
|
13
|
+
int length;
|
|
14
|
+
int depth;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
struct ParseResult {
|
|
18
|
+
std::vector<MarkdownRange> ranges;
|
|
19
|
+
|
|
20
|
+
// Non-empty when the worklet returned something that didn't match the
|
|
21
|
+
// expected schema. Reporting is left to the caller so this stays free of any
|
|
22
|
+
// platform logging dependency.
|
|
23
|
+
std::string schemaError;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Runs a registered markdown parser worklet and returns the ranges it produced.
|
|
27
|
+
//
|
|
28
|
+
// Stateless and lock-free on purpose. Running the worklet waits on the markdown
|
|
29
|
+
// runtime, and holding a lock across that wait is what allowed a background
|
|
30
|
+
// parse to block the main thread until iOS killed the app
|
|
31
|
+
// (Expensify/react-native-live-markdown#772). Caching therefore belongs to the
|
|
32
|
+
// caller, which must not hold its cache lock while calling this.
|
|
33
|
+
//
|
|
34
|
+
// `utf8Text` is the text to parse, UTF-8 encoded for the JSI boundary.
|
|
35
|
+
//
|
|
36
|
+
// `textLengthUtf16` is the same text's length in UTF-16 code units. It is
|
|
37
|
+
// passed separately rather than derived from `utf8Text.size()` because the
|
|
38
|
+
// worklet reports range offsets in UTF-16 units, matching JS string indices.
|
|
39
|
+
// For any non-ASCII text the UTF-8 byte count is larger, so validating against
|
|
40
|
+
// it would let out-of-bounds ranges through.
|
|
41
|
+
ParseResult
|
|
42
|
+
parseMarkdown(const std::string &utf8Text, size_t textLengthUtf16, int parserId);
|
|
43
|
+
|
|
44
|
+
} // namespace marcus
|
|
45
|
+
} // namespace bulletpoint
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#include "RuntimeDecorator.h"
|
|
2
|
+
#include "MarkdownGlobal.h"
|
|
3
|
+
|
|
4
|
+
using namespace facebook;
|
|
5
|
+
using namespace worklets;
|
|
6
|
+
|
|
7
|
+
namespace bulletpoint {
|
|
8
|
+
namespace marcus {
|
|
9
|
+
|
|
10
|
+
void
|
|
11
|
+
injectJSIBindings(jsi::Runtime &rt) {
|
|
12
|
+
|
|
13
|
+
rt.global().setProperty(
|
|
14
|
+
rt, "jsi_setMarcusRuntime", jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "jsi_setMarcusRuntime"), 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
15
|
+
setMarkdownRuntime(extractWorkletRuntime(rt, args[0]));
|
|
16
|
+
return jsi::Value::undefined();
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
rt.global().setProperty(
|
|
21
|
+
rt, "jsi_registerMarcusWorklet", jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "jsi_registerMarcusWorklet"), 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
22
|
+
const auto parserId = registerMarkdownWorklet(
|
|
23
|
+
extractSerializableOrThrow<SerializableWorklet>(rt, args[0])
|
|
24
|
+
);
|
|
25
|
+
return jsi::Value(parserId);
|
|
26
|
+
})
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
rt.global().setProperty(
|
|
30
|
+
rt, "jsi_unregisterMarcusWorklet", jsi::Function::createFromHostFunction(rt, jsi::PropNameID::forAscii(rt, "jsi_unregisterMarcusWorklet"), 1, [](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
31
|
+
auto parserId = static_cast<int>(args[0].asNumber());
|
|
32
|
+
unregisterMarkdownWorklet(parserId);
|
|
33
|
+
return jsi::Value::undefined();
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
} // namespace marcus
|
|
39
|
+
} // namespace bulletpoint
|