react-native-marcus 0.1.0 → 0.2.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/README.md +220 -63
- package/RNMarcus.podspec +6 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusPackage.kt +2 -1
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorView.kt +168 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManager.kt +38 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManagerSpec.kt +19 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorView.kt +23 -1
- package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPainter.kt +147 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPass.kt +19 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownCopy.kt +87 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownFormatter.kt +652 -113
- package/android/src/main/java/app/getbullet/marcus/MarkdownRanges.kt +47 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownStyle.kt +140 -61
- package/android/src/main/java/app/getbullet/marcus/MarkdownUtils.kt +37 -7
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownAbsoluteLineHeightSpan.kt +48 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundSpan.kt +102 -48
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBlockIndentSpan.kt +22 -4
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBulletSpan.kt +78 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpan.kt +177 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontStyleSpan.kt +32 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontWeightSpan.kt +34 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownGapSpan.kt +24 -11
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownLetterSpacingSpan.kt +32 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownTextShadowSpan.kt +32 -0
- package/android/src/main/new_arch/CMakeLists.txt +17 -0
- package/android/src/main/new_arch/RNMarcusSpec.h +1 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.cpp +235 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.h +62 -0
- package/android/src/reactNativeVersionPatch/CustomMountingManager/81/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
- package/android/src/reactNativeVersionPatch/CustomMountingManager/latest/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
- package/android/src/test/java/app/getbullet/marcus/MarkdownCopyTest.kt +69 -0
- package/android/src/test/java/app/getbullet/marcus/MarkdownFormatterTest.kt +170 -25
- package/android/src/test/java/app/getbullet/marcus/RenderModel.kt +22 -5
- package/android/src/test/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpanTest.kt +91 -0
- package/android/src/test/resources/render-model.txt +221 -16
- package/apple/MarcusSwizzle.h +25 -0
- package/apple/MarcusTextDecoratorComponentView.h +22 -0
- package/apple/MarcusTextDecoratorComponentView.mm +72 -0
- package/apple/MarcusTextDecoratorShadowNode.h +62 -0
- package/apple/MarcusTextDecoratorShadowNode.mm +192 -0
- package/apple/MarcusTextDecoratorViewManager.h +4 -0
- package/apple/MarcusTextDecoratorViewManager.mm +11 -0
- package/apple/MarcusTextInputDecoratorComponentView.mm +1 -1
- package/apple/MarcusTextInputDecoratorShadowNode.mm +1 -1
- package/apple/MarcusTextLayoutManager.h +40 -0
- package/apple/MarcusTextLayoutManager.mm +173 -0
- package/apple/MarkdownAttributes.h +23 -2
- package/apple/MarkdownAttributes.m +6 -0
- package/apple/MarkdownCopy.swift +63 -0
- package/apple/MarkdownDecorator.swift +2 -2
- package/apple/MarkdownFormatter+React.swift +23 -0
- package/apple/MarkdownFormatter.swift +869 -148
- package/apple/MarkdownParagraphLayoutManager.swift +455 -0
- package/apple/MarkdownSwiftInterop.h +0 -1
- package/apple/MarkdownTextBackground.swift +59 -1
- package/apple/MarkdownTextFieldObserver.swift +2 -2
- package/apple/MarkdownTextLayoutFragment.swift +159 -33
- package/apple/MarkdownTextLayoutManagerDelegate.swift +67 -3
- package/apple/MarkdownTextStorageDelegate.swift +2 -2
- package/apple/MarkdownTextViewObserver.swift +2 -2
- package/apple/MarkdownUtils.swift +38 -0
- package/apple/MarkdownWritingDirection.swift +132 -0
- package/apple/RCTMarcusStyle+Codegen.h +115 -9
- package/apple/RCTMarcusStyle.h +128 -31
- package/apple/RCTMarcusStyle.m +48 -0
- package/apple/RCTParagraphComponentView+Marcus.h +17 -0
- package/apple/RCTParagraphComponentView+Marcus.mm +38 -0
- package/apple/RCTTextInputComponentView+Marcus.mm +1 -25
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorState.h +37 -0
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorViewComponentDescriptor.h +17 -0
- package/lib/commonjs/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/lib/commonjs/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/lib/commonjs/MarkdownText.js +330 -0
- package/lib/commonjs/MarkdownText.js.map +1 -0
- package/lib/commonjs/MarkdownText.web.js +529 -0
- package/lib/commonjs/MarkdownText.web.js.map +1 -0
- package/lib/commonjs/MarkdownTextInput.js +22 -27
- package/lib/commonjs/MarkdownTextInput.js.map +1 -1
- package/lib/commonjs/MarkdownTextInput.web.js +12 -7
- package/lib/commonjs/MarkdownTextInput.web.js.map +1 -1
- package/lib/commonjs/atomUtils.js +112 -0
- package/lib/commonjs/atomUtils.js.map +1 -0
- package/lib/commonjs/childrenUtils.js +36 -0
- package/lib/commonjs/childrenUtils.js.map +1 -0
- package/lib/commonjs/index.js +8 -7
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/math/environment.js +69 -0
- package/lib/commonjs/math/environment.js.map +1 -0
- package/lib/commonjs/math/index.js +61 -0
- package/lib/commonjs/math/index.js.map +1 -0
- package/lib/commonjs/math/mathjax.js +548 -0
- package/lib/commonjs/math/mathjax.js.map +1 -0
- package/lib/commonjs/metro.js +172 -0
- package/lib/commonjs/metro.js.map +1 -0
- package/lib/commonjs/parser/index.d.ts +3 -0
- package/lib/commonjs/parser/index.js +450 -42
- package/lib/commonjs/parser/index.js.map +4 -4
- package/lib/commonjs/stripUtils.js +743 -0
- package/lib/commonjs/stripUtils.js.map +1 -0
- package/lib/commonjs/styleUtils.js +194 -23
- package/lib/commonjs/styleUtils.js.map +1 -1
- package/lib/commonjs/web/MarkdownElements.css +67 -0
- package/lib/commonjs/web/MarkdownTextInput.css +9 -7
- package/lib/commonjs/web/utils/blockLayout.js +95 -7
- package/lib/commonjs/web/utils/blockLayout.js.map +1 -1
- package/lib/commonjs/web/utils/blockUtils.js +327 -54
- package/lib/commonjs/web/utils/blockUtils.js.map +1 -1
- package/lib/commonjs/web/utils/elementUtils.js +80 -0
- package/lib/commonjs/web/utils/elementUtils.js.map +1 -0
- package/lib/commonjs/web/utils/parserUtils.js +84 -28
- package/lib/commonjs/web/utils/parserUtils.js.map +1 -1
- package/lib/commonjs/web/utils/treeUtils.js +6 -0
- package/lib/commonjs/web/utils/treeUtils.js.map +1 -1
- package/lib/commonjs/web/utils/webStyleUtils.js +25 -5
- package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -1
- package/lib/module/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/lib/module/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/lib/module/MarkdownText.js +325 -0
- package/lib/module/MarkdownText.js.map +1 -0
- package/lib/module/MarkdownText.web.js +524 -0
- package/lib/module/MarkdownText.web.js.map +1 -0
- package/lib/module/MarkdownTextInput.js +24 -29
- package/lib/module/MarkdownTextInput.js.map +1 -1
- package/lib/module/MarkdownTextInput.web.js +12 -7
- package/lib/module/MarkdownTextInput.web.js.map +1 -1
- package/lib/module/atomUtils.js +106 -0
- package/lib/module/atomUtils.js.map +1 -0
- package/lib/module/childrenUtils.js +33 -0
- package/lib/module/childrenUtils.js.map +1 -0
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/math/environment.js +67 -0
- package/lib/module/math/environment.js.map +1 -0
- package/lib/module/math/index.js +56 -0
- package/lib/module/math/index.js.map +1 -0
- package/lib/module/math/mathjax.js +541 -0
- package/lib/module/math/mathjax.js.map +1 -0
- package/lib/module/metro.js +169 -0
- package/lib/module/metro.js.map +1 -0
- package/lib/module/parser/index.d.ts +3 -0
- package/lib/module/parser/index.js +450 -42
- package/lib/module/parser/index.js.map +4 -4
- package/lib/module/stripUtils.js +739 -0
- package/lib/module/stripUtils.js.map +1 -0
- package/lib/module/styleUtils.js +196 -25
- package/lib/module/styleUtils.js.map +1 -1
- package/lib/module/web/MarkdownElements.css +67 -0
- package/lib/module/web/MarkdownTextInput.css +9 -7
- package/lib/module/web/utils/blockLayout.js +94 -8
- package/lib/module/web/utils/blockLayout.js.map +1 -1
- package/lib/module/web/utils/blockUtils.js +328 -56
- package/lib/module/web/utils/blockUtils.js.map +1 -1
- package/lib/module/web/utils/elementUtils.js +75 -0
- package/lib/module/web/utils/elementUtils.js.map +1 -0
- package/lib/module/web/utils/parserUtils.js +86 -30
- package/lib/module/web/utils/parserUtils.js.map +1 -1
- package/lib/module/web/utils/treeUtils.js +6 -0
- package/lib/module/web/utils/treeUtils.js.map +1 -1
- package/lib/module/web/utils/webStyleUtils.js +25 -5
- package/lib/module/web/utils/webStyleUtils.js.map +1 -1
- package/lib/typescript/commonjs/e2e/links.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/links.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
- package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/MarkdownText.d.ts +127 -0
- package/lib/typescript/commonjs/src/MarkdownText.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownText.web.d.ts +126 -0
- package/lib/typescript/commonjs/src/MarkdownText.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts +23 -1
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts +26 -4
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/atomUtils.d.ts +58 -0
- package/lib/typescript/commonjs/src/atomUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/childrenUtils.d.ts +12 -0
- package/lib/typescript/commonjs/src/childrenUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/commonTypes.d.ts +39 -3
- package/lib/typescript/commonjs/src/commonTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +2 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/math/environment.d.ts +7 -0
- package/lib/typescript/commonjs/src/math/environment.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/math/index.d.ts +64 -0
- package/lib/typescript/commonjs/src/math/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/math/mathjax.d.ts +63 -0
- package/lib/typescript/commonjs/src/math/mathjax.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/metro.d.ts +59 -0
- package/lib/typescript/commonjs/src/metro.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/parser/index.d.ts +3 -3
- package/lib/typescript/commonjs/src/parser/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/rangeUtils.d.ts +1 -1
- package/lib/typescript/commonjs/src/stripUtils.d.ts +108 -0
- package/lib/typescript/commonjs/src/stripUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/styleUtils.d.ts +62 -4
- package/lib/typescript/commonjs/src/styleUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts +32 -4
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts +20 -8
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts +37 -0
- package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts +5 -5
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts +6 -1
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts +2 -2
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts.map +1 -1
- package/lib/typescript/module/e2e/links.test.d.ts +2 -0
- package/lib/typescript/module/e2e/links.test.d.ts.map +1 -0
- package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
- package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/MarkdownText.d.ts +127 -0
- package/lib/typescript/module/src/MarkdownText.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownText.web.d.ts +126 -0
- package/lib/typescript/module/src/MarkdownText.web.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownTextInput.d.ts +23 -1
- package/lib/typescript/module/src/MarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts +26 -4
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts.map +1 -1
- package/lib/typescript/module/src/atomUtils.d.ts +58 -0
- package/lib/typescript/module/src/atomUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/childrenUtils.d.ts +12 -0
- package/lib/typescript/module/src/childrenUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/commonTypes.d.ts +39 -3
- package/lib/typescript/module/src/commonTypes.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +2 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/math/environment.d.ts +7 -0
- package/lib/typescript/module/src/math/environment.d.ts.map +1 -0
- package/lib/typescript/module/src/math/index.d.ts +64 -0
- package/lib/typescript/module/src/math/index.d.ts.map +1 -0
- package/lib/typescript/module/src/math/mathjax.d.ts +63 -0
- package/lib/typescript/module/src/math/mathjax.d.ts.map +1 -0
- package/lib/typescript/module/src/metro.d.ts +59 -0
- package/lib/typescript/module/src/metro.d.ts.map +1 -0
- package/lib/typescript/module/src/parser/index.d.ts +3 -3
- package/lib/typescript/module/src/parser/index.d.ts.map +1 -1
- package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts.map +1 -1
- package/lib/typescript/module/src/rangeUtils.d.ts +1 -1
- package/lib/typescript/module/src/stripUtils.d.ts +108 -0
- package/lib/typescript/module/src/stripUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/styleUtils.d.ts +62 -4
- package/lib/typescript/module/src/styleUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts +32 -4
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts +20 -8
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/elementUtils.d.ts +37 -0
- package/lib/typescript/module/src/web/utils/elementUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts +5 -5
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts +6 -1
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts +2 -2
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts.map +1 -1
- package/package.json +29 -2
- package/react-native.config.js +8 -1
- package/src/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/src/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/src/MarkdownText.tsx +472 -0
- package/src/MarkdownText.web.tsx +668 -0
- package/src/MarkdownTextInput.tsx +42 -38
- package/src/MarkdownTextInput.web.tsx +36 -13
- package/src/atomUtils.ts +112 -0
- package/src/childrenUtils.ts +39 -0
- package/src/commonTypes.ts +69 -9
- package/src/index.tsx +2 -1
- package/src/math/environment.ts +68 -0
- package/src/math/index.ts +78 -0
- package/src/math/mathjax.ts +604 -0
- package/src/metro.ts +238 -0
- package/src/parser/index.ts +175 -10
- package/src/parser/micromark-extension-mentions.ts +104 -11
- package/src/stripUtils.ts +919 -0
- package/src/styleUtils.ts +290 -26
- package/src/web/MarkdownElements.css +67 -0
- package/src/web/MarkdownTextInput.css +9 -7
- package/src/web/utils/blockLayout.ts +124 -14
- package/src/web/utils/blockUtils.ts +365 -59
- package/src/web/utils/elementUtils.ts +87 -0
- package/src/web/utils/parserUtils.ts +119 -31
- package/src/web/utils/treeUtils.ts +6 -1
- package/src/web/utils/webStyleUtils.ts +32 -8
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundColorSpan.kt +0 -6
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBoldSpan.kt +0 -6
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownItalicSpan.kt +0 -6
- package/apple/RCTMarcusStyle+Codegen.mm +0 -64
- package/apple/RCTMarcusUtils.h +0 -26
- package/apple/RCTMarcusUtils.mm +0 -26
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#import <RNMarcus/MarcusTextDecoratorShadowNode.h>
|
|
2
|
+
|
|
3
|
+
#import <RNMarcus/MarcusTextLayoutManager.h>
|
|
4
|
+
#import <RNMarcus/RCTMarcusStyle+Codegen.h>
|
|
5
|
+
|
|
6
|
+
#import <react/renderer/components/text/ParagraphShadowNode.h>
|
|
7
|
+
#import <react/renderer/core/ComponentDescriptor.h>
|
|
8
|
+
|
|
9
|
+
namespace facebook {
|
|
10
|
+
namespace react {
|
|
11
|
+
|
|
12
|
+
extern const char MarcusTextDecoratorViewComponentName[] =
|
|
13
|
+
"MarcusTextDecoratorView";
|
|
14
|
+
|
|
15
|
+
MarcusTextDecoratorShadowNode::MarcusTextDecoratorShadowNode(
|
|
16
|
+
ShadowNodeFragment const &fragment,
|
|
17
|
+
ShadowNodeFamily::Shared const &family,
|
|
18
|
+
ShadowNodeTraits traits
|
|
19
|
+
)
|
|
20
|
+
: ConcreteViewShadowNode(fragment, family, traits) {
|
|
21
|
+
initialize();
|
|
22
|
+
createTextLayoutManager();
|
|
23
|
+
makeChildNodeMutable();
|
|
24
|
+
|
|
25
|
+
if (fragment.children) {
|
|
26
|
+
overwriteTextLayoutManager();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
MarcusTextDecoratorShadowNode::MarcusTextDecoratorShadowNode(
|
|
31
|
+
ShadowNode const &sourceShadowNode,
|
|
32
|
+
ShadowNodeFragment const &fragment
|
|
33
|
+
)
|
|
34
|
+
: ConcreteViewShadowNode(sourceShadowNode, fragment) {
|
|
35
|
+
initialize();
|
|
36
|
+
|
|
37
|
+
const auto &sourceDecorator =
|
|
38
|
+
static_cast<const MarcusTextDecoratorShadowNode &>(sourceShadowNode);
|
|
39
|
+
textLayoutManager_ = sourceDecorator.textLayoutManager_;
|
|
40
|
+
|
|
41
|
+
// Fabric hands a clone the very same props object whenever nothing about the
|
|
42
|
+
// props changed, so pointer identity is both exact and cheap here. Codegen
|
|
43
|
+
// only emits `operator==` for prop structs under RN_SERIALIZABLE_STATE, which
|
|
44
|
+
// iOS does not define, and hand-written equality over the style would rot the
|
|
45
|
+
// moment a field is added.
|
|
46
|
+
if (textLayoutManager_ == nullptr ||
|
|
47
|
+
getProps().get() != sourceDecorator.getProps().get()) {
|
|
48
|
+
createTextLayoutManager();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
makeChildNodeMutable();
|
|
52
|
+
|
|
53
|
+
if (fragment.children) {
|
|
54
|
+
overwriteTextLayoutManager();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
void
|
|
59
|
+
MarcusTextDecoratorShadowNode::initialize() {
|
|
60
|
+
// Setting display: contents style results in ForceFlattenView trait being set
|
|
61
|
+
// on the shadow node. This trait causes the node not to have a host view. By
|
|
62
|
+
// removing the trait, it's possible to force RN to create a host view, layout
|
|
63
|
+
// of which can then be customized.
|
|
64
|
+
ShadowNode::traits_.unset(ShadowNodeTraits::ForceFlattenView);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void
|
|
68
|
+
MarcusTextDecoratorShadowNode::makeChildNodeMutable() {
|
|
69
|
+
// When the decorator is cloned and has a child node, the child node should be
|
|
70
|
+
// cloned as well to ensure it is mutable. Cloning it also runs the paragraph's
|
|
71
|
+
// own component descriptor, which resets the layout manager to the shared one
|
|
72
|
+
// -- hence overwriteTextLayoutManager() always following this, never the other
|
|
73
|
+
// way around.
|
|
74
|
+
const auto &children = getChildren();
|
|
75
|
+
if (!children.empty()) {
|
|
76
|
+
react_native_assert(
|
|
77
|
+
children.size() == 1 &&
|
|
78
|
+
"MarcusTextDecoratorView received more than one child"
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const auto clonedChild = children[0]->clone({});
|
|
82
|
+
replaceChild(*children[0], clonedChild);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
void
|
|
87
|
+
MarcusTextDecoratorShadowNode::createTextLayoutManager() {
|
|
88
|
+
const auto &props = getConcreteProps();
|
|
89
|
+
|
|
90
|
+
NSMutableArray<MarcusRange *> *ranges =
|
|
91
|
+
[NSMutableArray arrayWithCapacity:props.ranges.size()];
|
|
92
|
+
|
|
93
|
+
for (const auto &range : props.ranges) {
|
|
94
|
+
[ranges addObject:[[MarcusRange alloc]
|
|
95
|
+
initWithType:RCTNSStringFromString(range.type)
|
|
96
|
+
range:NSMakeRange(range.start, range.length)
|
|
97
|
+
depth:static_cast<NSUInteger>(range.depth)]];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
textLayoutManager_ = std::make_shared<const MarcusTextLayoutManager>(
|
|
101
|
+
getContextContainer(),
|
|
102
|
+
ranges,
|
|
103
|
+
RCTMarcusStyleFromStruct(props.markdownStyle)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void
|
|
108
|
+
MarcusTextDecoratorShadowNode::overwriteTextLayoutManager() {
|
|
109
|
+
const auto &children = getChildren();
|
|
110
|
+
if (children.empty()) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
react_native_assert(
|
|
114
|
+
children.size() == 1 &&
|
|
115
|
+
"MarcusTextDecoratorView received more than one child"
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const auto child =
|
|
119
|
+
std::dynamic_pointer_cast<const ParagraphShadowNode>(children[0]);
|
|
120
|
+
|
|
121
|
+
react_native_assert(
|
|
122
|
+
child != nullptr &&
|
|
123
|
+
"MarcusTextDecoratorView received child other than a Text"
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
if (child == nullptr) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
child->ensureUnsealed();
|
|
131
|
+
|
|
132
|
+
const auto mutableChild =
|
|
133
|
+
std::const_pointer_cast<ParagraphShadowNode>(child);
|
|
134
|
+
mutableChild->setTextLayoutManager(textLayoutManager_);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
void
|
|
138
|
+
MarcusTextDecoratorShadowNode::appendChild(
|
|
139
|
+
const std::shared_ptr<const ShadowNode> &child
|
|
140
|
+
) {
|
|
141
|
+
YogaLayoutableShadowNode::appendChild(child);
|
|
142
|
+
|
|
143
|
+
overwriteTextLayoutManager();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
void
|
|
147
|
+
MarcusTextDecoratorShadowNode::replaceChild(
|
|
148
|
+
const ShadowNode &oldChild,
|
|
149
|
+
const std::shared_ptr<const ShadowNode> &newChild,
|
|
150
|
+
size_t suggestedIndex
|
|
151
|
+
) {
|
|
152
|
+
YogaLayoutableShadowNode::replaceChild(oldChild, newChild, suggestedIndex);
|
|
153
|
+
|
|
154
|
+
overwriteTextLayoutManager();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
void
|
|
158
|
+
MarcusTextDecoratorShadowNode::layout(LayoutContext layoutContext) {
|
|
159
|
+
YogaLayoutableShadowNode::layout(layoutContext);
|
|
160
|
+
|
|
161
|
+
const auto &children = getChildren();
|
|
162
|
+
react_native_assert(
|
|
163
|
+
children.size() == 1 &&
|
|
164
|
+
"MarcusTextDecoratorView didn't receive exactly one child"
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
if (children.size() != 1) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
auto child =
|
|
172
|
+
std::static_pointer_cast<const YogaLayoutableShadowNode>(children[0]);
|
|
173
|
+
child->ensureUnsealed();
|
|
174
|
+
auto mutableChild = std::const_pointer_cast<YogaLayoutableShadowNode>(child);
|
|
175
|
+
|
|
176
|
+
// Since nodes with display: contents are skipped during layout, they have
|
|
177
|
+
// zero-layout. To properly display the view, assign the layout metrics from
|
|
178
|
+
// the child (the paragraph, which was calculated by Yoga) to the decorator
|
|
179
|
+
// view.
|
|
180
|
+
auto childMetrics = child->getLayoutMetrics();
|
|
181
|
+
setLayoutMetrics(childMetrics); // makes a copy
|
|
182
|
+
|
|
183
|
+
// Then, it's also needed to update the metrics on the child as the position
|
|
184
|
+
// is relative to the parent, which was just moved above. By zeroing the
|
|
185
|
+
// origin, the child is effectively moved to the same position it was before
|
|
186
|
+
// the manipulation here.
|
|
187
|
+
childMetrics.frame.origin = Point{};
|
|
188
|
+
mutableChild->setLayoutMetrics(childMetrics);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
} // namespace react
|
|
192
|
+
} // namespace facebook
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#import <RNMarcus/MarcusTextDecoratorViewManager.h>
|
|
2
|
+
|
|
3
|
+
@implementation MarcusTextDecoratorViewManager
|
|
4
|
+
|
|
5
|
+
RCT_EXPORT_MODULE(MarcusTextDecoratorView)
|
|
6
|
+
|
|
7
|
+
RCT_EXPORT_VIEW_PROPERTY(markdownStyle, NSDictionary)
|
|
8
|
+
|
|
9
|
+
RCT_EXPORT_VIEW_PROPERTY(ranges, NSArray)
|
|
10
|
+
|
|
11
|
+
@end
|
|
@@ -95,7 +95,7 @@ using namespace facebook::react;
|
|
|
95
95
|
// whenever the props object is recreated -- but hand-written equality over
|
|
96
96
|
// ~27 fields would rot silently the moment a style prop is added.
|
|
97
97
|
_decorator.markdownStyle =
|
|
98
|
-
|
|
98
|
+
RCTMarcusStyleFromStruct(newViewProps.markdownStyle);
|
|
99
99
|
|
|
100
100
|
[_decorator applyNewStyles];
|
|
101
101
|
|
|
@@ -128,7 +128,7 @@ MarcusTextInputDecoratorShadowNode::
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
RCTMarcusStyle *markdownStyle =
|
|
131
|
-
|
|
131
|
+
RCTMarcusStyleFromStruct(decoratorProps.markdownStyle);
|
|
132
132
|
NSNumber *parserId = [NSNumber numberWithInt:decoratorProps.parserId];
|
|
133
133
|
|
|
134
134
|
// convert the attibuted string stored in state to
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <Foundation/Foundation.h>
|
|
4
|
+
|
|
5
|
+
#import <RNMarcus/MarcusRange.h>
|
|
6
|
+
#import <RNMarcus/RCTMarcusStyle.h>
|
|
7
|
+
|
|
8
|
+
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
|
|
9
|
+
|
|
10
|
+
#include <memory>
|
|
11
|
+
|
|
12
|
+
namespace facebook {
|
|
13
|
+
namespace react {
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* A `TextLayoutManager` that formats before it lays out.
|
|
17
|
+
*
|
|
18
|
+
* Everything `RCTParagraphComponentView` does with text -- measuring it,
|
|
19
|
+
* drawing it, hit-testing it, enumerating its lines -- goes through the
|
|
20
|
+
* `RCTTextLayoutManager` this owns, and each of those paths converts the
|
|
21
|
+
* shadow tree's `AttributedString` into an `NSAttributedString` first. That
|
|
22
|
+
* conversion is the only place markdown has to be applied, which is why the
|
|
23
|
+
* `Text` decorator needs none of the measure-callback surgery the `TextInput`
|
|
24
|
+
* one does: handing the child paragraph one of these covers every path at once.
|
|
25
|
+
*
|
|
26
|
+
* Immutable once constructed. The decorator builds a new one when its props
|
|
27
|
+
* change rather than mutating this, so the commit thread is never writing style
|
|
28
|
+
* or ranges while the main thread draws with them.
|
|
29
|
+
*/
|
|
30
|
+
class MarcusTextLayoutManager : public TextLayoutManager {
|
|
31
|
+
public:
|
|
32
|
+
MarcusTextLayoutManager(
|
|
33
|
+
const std::shared_ptr<const ContextContainer> &contextContainer,
|
|
34
|
+
NSArray<MarcusRange *> *ranges,
|
|
35
|
+
RCTMarcusStyle *markdownStyle
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
} // namespace react
|
|
40
|
+
} // namespace facebook
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#import <RNMarcus/MarcusTextLayoutManager.h>
|
|
2
|
+
#import <RNMarcus/MarkdownSwiftInterop.h>
|
|
3
|
+
|
|
4
|
+
#import <react/debug/react_native_assert.h>
|
|
5
|
+
#import <react/renderer/textlayoutmanager/RCTTextLayoutManager.h>
|
|
6
|
+
#import <react/utils/ManagedObjectWrapper.h>
|
|
7
|
+
|
|
8
|
+
using namespace facebook::react;
|
|
9
|
+
|
|
10
|
+
// The one seam every text path funnels through. It is private to
|
|
11
|
+
// RCTTextLayoutManager, so it is redeclared here to be overridden; the
|
|
12
|
+
// alternative is reimplementing five public methods that each need
|
|
13
|
+
// `_textStorageAndLayoutManagerWithAttributesString:`, which is private too.
|
|
14
|
+
//
|
|
15
|
+
// If React Native renames it the override silently stops being called, so the
|
|
16
|
+
// subclass asserts on the selector at construction rather than letting
|
|
17
|
+
// formatting quietly disappear.
|
|
18
|
+
@interface RCTTextLayoutManager (Marcus)
|
|
19
|
+
- (NSAttributedString *)
|
|
20
|
+
_nsAttributedStringFromAttributedString:(facebook::react::AttributedString)attributedString;
|
|
21
|
+
- (NSTextStorage *)
|
|
22
|
+
_textStorageAndLayoutManagerWithAttributesString:(NSAttributedString *)attributedString
|
|
23
|
+
paragraphAttributes:(facebook::react::ParagraphAttributes)paragraphAttributes
|
|
24
|
+
size:(CGSize)size;
|
|
25
|
+
@end
|
|
26
|
+
|
|
27
|
+
@interface MarcusRCTTextLayoutManager : RCTTextLayoutManager
|
|
28
|
+
|
|
29
|
+
@property(nonatomic, copy) NSArray<MarcusRange *> *ranges;
|
|
30
|
+
@property(nonatomic, strong) RCTMarcusStyle *markdownStyle;
|
|
31
|
+
|
|
32
|
+
@end
|
|
33
|
+
|
|
34
|
+
@implementation MarcusRCTTextLayoutManager
|
|
35
|
+
|
|
36
|
+
- (NSAttributedString *)
|
|
37
|
+
_nsAttributedStringFromAttributedString:(AttributedString)attributedString {
|
|
38
|
+
NSAttributedString *unformatted =
|
|
39
|
+
[super _nsAttributedStringFromAttributedString:attributedString];
|
|
40
|
+
|
|
41
|
+
if (_markdownStyle == nil || unformatted.length == 0) {
|
|
42
|
+
return unformatted;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Superclass caches what it returns, so the copy is not optional.
|
|
46
|
+
NSMutableAttributedString *formatted =
|
|
47
|
+
[[NSMutableAttributedString alloc] initWithAttributedString:unformatted];
|
|
48
|
+
|
|
49
|
+
// Unlike the text input there is no separate notion of default attributes: a
|
|
50
|
+
// paragraph's own attributes are already on the string, fragment by fragment,
|
|
51
|
+
// and the formatter is asked to leave them alone. What it still needs is a
|
|
52
|
+
// paragraph style to build indents from, which is what these supply.
|
|
53
|
+
NSDictionary<NSAttributedStringKey, id> *defaultTextAttributes =
|
|
54
|
+
[unformatted attributesAtIndex:0 effectiveRange:NULL];
|
|
55
|
+
|
|
56
|
+
[MarkdownFormatter formatParagraph:formatted
|
|
57
|
+
defaultTextAttributes:defaultTextAttributes
|
|
58
|
+
ranges:_ranges
|
|
59
|
+
style:_markdownStyle];
|
|
60
|
+
|
|
61
|
+
return formatted;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// The second seam, for the two shapes that are not attributes at all. Every
|
|
65
|
+
// path builds its layout here, and swapping in a subclass is the only way to
|
|
66
|
+
// reach the background drawing pass -- `drawAttributedString:` takes the layout
|
|
67
|
+
// manager this returns and never asks what class it is.
|
|
68
|
+
//
|
|
69
|
+
// Super is called rather than reimplemented so that whatever else React Native
|
|
70
|
+
// does to a text container -- line breaking, font scaling, baseline offset --
|
|
71
|
+
// keeps happening, and keeps happening the same way for measurement as for
|
|
72
|
+
// drawing.
|
|
73
|
+
- (NSTextStorage *)
|
|
74
|
+
_textStorageAndLayoutManagerWithAttributesString:(NSAttributedString *)attributedString
|
|
75
|
+
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
|
|
76
|
+
size:(CGSize)size {
|
|
77
|
+
NSTextStorage *textStorage =
|
|
78
|
+
[super _textStorageAndLayoutManagerWithAttributesString:attributedString
|
|
79
|
+
paragraphAttributes:paragraphAttributes
|
|
80
|
+
size:size];
|
|
81
|
+
|
|
82
|
+
if (_markdownStyle == nil || textStorage.layoutManagers.count != 1) {
|
|
83
|
+
return textStorage;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
NSLayoutManager *original = textStorage.layoutManagers.firstObject;
|
|
87
|
+
|
|
88
|
+
if (original.textContainers.count != 1 ||
|
|
89
|
+
[original isKindOfClass:[MarkdownParagraphLayoutManager class]]) {
|
|
90
|
+
return textStorage;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
NSTextContainer *textContainer = original.textContainers.firstObject;
|
|
94
|
+
MarkdownParagraphLayoutManager *replacement =
|
|
95
|
+
[MarkdownParagraphLayoutManager new];
|
|
96
|
+
replacement.usesFontLeading = original.usesFontLeading;
|
|
97
|
+
replacement.markdownStyle = _markdownStyle;
|
|
98
|
+
|
|
99
|
+
[original removeTextContainerAtIndex:0];
|
|
100
|
+
[replacement addTextContainer:textContainer];
|
|
101
|
+
[textStorage addLayoutManager:replacement];
|
|
102
|
+
[textStorage removeLayoutManager:original];
|
|
103
|
+
|
|
104
|
+
return textStorage;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Where a press lands.
|
|
109
|
+
*
|
|
110
|
+
* The touch arrives in the paragraph view's own coordinates -- padding
|
|
111
|
+
* included -- while the layout it is measured against starts at the content
|
|
112
|
+
* frame, so on a `Text` with any padding at all React Native looks up the
|
|
113
|
+
* character that many points down and to the right of the one under the
|
|
114
|
+
* finger. With 8pt of padding and a 19pt line that is the bottom half of every
|
|
115
|
+
* line pressing the line below it, which is most of what makes a link feel
|
|
116
|
+
* like it has to be aimed at.
|
|
117
|
+
*
|
|
118
|
+
* Nothing else needs the untranslated point: this is the only method that
|
|
119
|
+
* takes one.
|
|
120
|
+
*/
|
|
121
|
+
- (std::shared_ptr<const EventEmitter>)
|
|
122
|
+
getEventEmitterWithAttributeString:(AttributedString)attributedString
|
|
123
|
+
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
|
|
124
|
+
frame:(CGRect)frame
|
|
125
|
+
atPoint:(CGPoint)point {
|
|
126
|
+
CGPoint inContent =
|
|
127
|
+
CGPointMake(point.x - frame.origin.x, point.y - frame.origin.y);
|
|
128
|
+
|
|
129
|
+
return [super getEventEmitterWithAttributeString:attributedString
|
|
130
|
+
paragraphAttributes:paragraphAttributes
|
|
131
|
+
frame:frame
|
|
132
|
+
atPoint:inContent];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@end
|
|
136
|
+
|
|
137
|
+
namespace facebook {
|
|
138
|
+
namespace react {
|
|
139
|
+
|
|
140
|
+
MarcusTextLayoutManager::MarcusTextLayoutManager(
|
|
141
|
+
const std::shared_ptr<const ContextContainer> &contextContainer,
|
|
142
|
+
NSArray<MarcusRange *> *ranges,
|
|
143
|
+
RCTMarcusStyle *markdownStyle
|
|
144
|
+
)
|
|
145
|
+
: TextLayoutManager(contextContainer) {
|
|
146
|
+
react_native_assert(
|
|
147
|
+
[RCTTextLayoutManager instancesRespondToSelector:
|
|
148
|
+
@selector(_nsAttributedStringFromAttributedString:)] &&
|
|
149
|
+
"RCTTextLayoutManager no longer converts attributed strings through "
|
|
150
|
+
"-_nsAttributedStringFromAttributedString:, so markdown formatting would "
|
|
151
|
+
"never be applied"
|
|
152
|
+
);
|
|
153
|
+
react_native_assert(
|
|
154
|
+
[RCTTextLayoutManager
|
|
155
|
+
instancesRespondToSelector:
|
|
156
|
+
@selector(_textStorageAndLayoutManagerWithAttributesString:paragraphAttributes:size:)] &&
|
|
157
|
+
"RCTTextLayoutManager no longer builds its layout through "
|
|
158
|
+
"-_textStorageAndLayoutManagerWithAttributesString:paragraphAttributes:size:, "
|
|
159
|
+
"so blockquote ribbons and mention pills would never be drawn"
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
MarcusRCTTextLayoutManager *manager = [MarcusRCTTextLayoutManager new];
|
|
163
|
+
manager.ranges = ranges;
|
|
164
|
+
manager.markdownStyle = markdownStyle;
|
|
165
|
+
|
|
166
|
+
// Replaces the plain one the base constructor just made. Every method on
|
|
167
|
+
// TextLayoutManager reads this member rather than a type, so none of them
|
|
168
|
+
// need to be virtual for the subclass to take effect.
|
|
169
|
+
nativeTextLayoutManager_ = wrapManagedObject(manager);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
} // namespace react
|
|
173
|
+
} // namespace facebook
|
|
@@ -16,16 +16,37 @@ FOUNDATION_EXPORT const
|
|
|
16
16
|
FOUNDATION_EXPORT const NSAttributedStringKey
|
|
17
17
|
RCTMarcusTextBackgroundAttributeName NS_SWIFT_NAME(marcusTextBackground);
|
|
18
18
|
|
|
19
|
+
// Carries a MarkdownCodeBlock over a fenced or indented block, drawn as one box
|
|
20
|
+
// behind the whole thing rather than as a background behind its glyphs.
|
|
21
|
+
FOUNDATION_EXPORT const NSAttributedStringKey
|
|
22
|
+
RCTMarcusCodeBlockAttributeName NS_SWIFT_NAME(marcusCodeBlock);
|
|
23
|
+
|
|
24
|
+
// Carries a MarkdownListBullet over the marker character of an unordered list
|
|
25
|
+
// item, whose glyph is left undrawn so that the circle can be drawn in its
|
|
26
|
+
// place. Only a display ever carries it: an input shows the marker you typed.
|
|
27
|
+
FOUNDATION_EXPORT const NSAttributedStringKey
|
|
28
|
+
RCTMarcusListBulletAttributeName NS_SWIFT_NAME(marcusListBullet);
|
|
29
|
+
|
|
19
30
|
// Blockquote nesting level, used to draw the ribbons.
|
|
20
31
|
FOUNDATION_EXPORT const NSAttributedStringKey
|
|
21
32
|
RCTMarcusBlockquoteDepthAttributeName NS_SWIFT_NAME(marcusBlockquoteDepth);
|
|
22
33
|
|
|
23
|
-
// Distance from the
|
|
34
|
+
// Distance from the line's leading edge to the blockquote's own box: the
|
|
24
35
|
// indent the line already carried when the blockquote was applied, plus any
|
|
25
36
|
// room left for a list marker drawn in front of the ribbons. They are drawn
|
|
26
37
|
// from there rather than from the text origin, which also moves with containers
|
|
27
|
-
// nested inside the quote (a list, say).
|
|
38
|
+
// nested inside the quote (a list, say). Leading rather than left, because a
|
|
39
|
+
// head indent is: which edge it is measured from is `marcusRightToLeft`.
|
|
28
40
|
FOUNDATION_EXPORT const NSAttributedStringKey
|
|
29
41
|
RCTMarcusBlockquoteIndentAttributeName NS_SWIFT_NAME(marcusBlockquoteIndent);
|
|
30
42
|
|
|
43
|
+
// Set over a line that reads right to left, and only over such a line: left to
|
|
44
|
+
// right is the absence of it. The shapes drawn beside the text -- a quote's
|
|
45
|
+
// ribbons, a code block's box -- are placed from the edge the line begins at,
|
|
46
|
+
// and nothing else in the string says which edge that is. UIKit resolves
|
|
47
|
+
// `NSWritingDirectionNatural` while laying out and keeps the answer to itself,
|
|
48
|
+
// so `MarkdownWritingDirection` works out the same one while formatting.
|
|
49
|
+
FOUNDATION_EXPORT const NSAttributedStringKey
|
|
50
|
+
RCTMarcusRightToLeftAttributeName NS_SWIFT_NAME(marcusRightToLeft);
|
|
51
|
+
|
|
31
52
|
NS_ASSUME_NONNULL_END
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
const NSAttributedStringKey RCTMarcusTextAttributeName = @"RCTMarcusText";
|
|
4
4
|
const NSAttributedStringKey RCTMarcusTextBackgroundAttributeName =
|
|
5
5
|
@"RCTMarcusTextBackground";
|
|
6
|
+
const NSAttributedStringKey RCTMarcusCodeBlockAttributeName =
|
|
7
|
+
@"RCTMarcusCodeBlock";
|
|
8
|
+
const NSAttributedStringKey RCTMarcusListBulletAttributeName =
|
|
9
|
+
@"RCTMarcusListBullet";
|
|
6
10
|
const NSAttributedStringKey RCTMarcusBlockquoteDepthAttributeName =
|
|
7
11
|
@"RCTMarcusBlockquoteDepth";
|
|
8
12
|
const NSAttributedStringKey RCTMarcusBlockquoteIndentAttributeName =
|
|
9
13
|
@"RCTMarcusBlockquoteIndent";
|
|
14
|
+
const NSAttributedStringKey RCTMarcusRightToLeftAttributeName =
|
|
15
|
+
@"RCTMarcusRightToLeft";
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// What a copied paragraph puts on the pasteboard.
|
|
4
|
+
///
|
|
5
|
+
/// A view nested in a `Text` -- a drawn equation, an image -- is one U+FFFC in
|
|
6
|
+
/// the string TextKit lays out, and U+FFFC is what the platform's own copy
|
|
7
|
+
/// writes: a message with an equation in it arrives in the next application
|
|
8
|
+
/// with an empty box where the maths was. So each of those characters is
|
|
9
|
+
/// written back out as the markdown it stands for.
|
|
10
|
+
///
|
|
11
|
+
/// The offsets come from JavaScript, measured against the stripped text. That
|
|
12
|
+
/// is the same string the ranges are measured against and the same one React
|
|
13
|
+
/// Native laid out -- the strip left behind exactly the one character an
|
|
14
|
+
/// attachment costs -- so there is nothing to remap here.
|
|
15
|
+
///
|
|
16
|
+
/// The whole paragraph, rather than a selection of it: React Native's `Text`
|
|
17
|
+
/// has no partial selection on iOS, and `copy:` is sent for the lot. Android
|
|
18
|
+
/// takes a range because there it can be given one.
|
|
19
|
+
@objc public final class MarkdownCopy: NSObject {
|
|
20
|
+
|
|
21
|
+
/// The paragraph with every placeholder replaced by its source.
|
|
22
|
+
///
|
|
23
|
+
/// The two arrays are one list said in the only shape that crosses into
|
|
24
|
+
/// Objective-C: `indices[i]` is where `sources[i]` goes. They arrive in text
|
|
25
|
+
/// order and each covers exactly one character, so one walk is enough.
|
|
26
|
+
///
|
|
27
|
+
/// - Parameters:
|
|
28
|
+
/// - text: the string the layout was made from.
|
|
29
|
+
/// - indices: where each placeholder is, in UTF-16 offsets.
|
|
30
|
+
/// - sources: the markdown each placeholder was written as. An empty one
|
|
31
|
+
/// removes the placeholder, which is how an image copies as nothing.
|
|
32
|
+
@objc(substituteInto:indices:sources:)
|
|
33
|
+
public static func substitute(_ text: String, indices: [Int], sources: [String]) -> String {
|
|
34
|
+
// Indexed as an `NSString` rather than a `String`: the offsets count UTF-16
|
|
35
|
+
// code units, which is what the text layout counts, while `String` counts
|
|
36
|
+
// characters a reader would recognise as one. The two disagree over every
|
|
37
|
+
// emoji in the paragraph, and the placeholder after one would land inside a
|
|
38
|
+
// surrogate pair.
|
|
39
|
+
let string = text as NSString
|
|
40
|
+
let length = string.length
|
|
41
|
+
var copied = ""
|
|
42
|
+
var cursor = 0
|
|
43
|
+
|
|
44
|
+
for (index, source) in zip(indices, sources) {
|
|
45
|
+
// Skipped rather than trusted: the props and the string that was laid out
|
|
46
|
+
// are two sides of one commit, and a paragraph mounted between them is the
|
|
47
|
+
// one frame where they can disagree.
|
|
48
|
+
guard index >= cursor, index < length else {
|
|
49
|
+
continue
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
copied += string.substring(with: NSRange(location: cursor, length: index - cursor))
|
|
53
|
+
copied += source
|
|
54
|
+
cursor = index + 1
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if cursor < length {
|
|
58
|
+
copied += string.substring(from: cursor)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return copied
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -10,7 +10,7 @@ import UIKit
|
|
|
10
10
|
/// implemented in Swift.
|
|
11
11
|
@objc public final class MarkdownDecorator: NSObject {
|
|
12
12
|
|
|
13
|
-
private var markdownUtils =
|
|
13
|
+
private var markdownUtils = MarkdownUtils()
|
|
14
14
|
|
|
15
15
|
private weak var textView: RCTUITextView?
|
|
16
16
|
private weak var textField: RCTUITextField?
|
|
@@ -44,7 +44,7 @@ import UIKit
|
|
|
44
44
|
|
|
45
45
|
/// Drops all per-mount state so the view can be recycled.
|
|
46
46
|
@objc public func reset() {
|
|
47
|
-
markdownUtils =
|
|
47
|
+
markdownUtils = MarkdownUtils()
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// MARK: - Attach / detach
|
|
@@ -48,4 +48,27 @@ extension MarkdownFormatter {
|
|
|
48
48
|
fonts: MarkdownReactFonts()
|
|
49
49
|
)
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
/// The paragraph entry point. Same formatting, except that whatever the
|
|
53
|
+
/// shadow tree already put on the string -- nested `Text` attributes, inline
|
|
54
|
+
/// view attachments -- is left where it is, and that the list markers are
|
|
55
|
+
/// rendered rather than shown: this is the finished text rather than the text
|
|
56
|
+
/// being written.
|
|
57
|
+
@objc(formatParagraph:defaultTextAttributes:ranges:style:)
|
|
58
|
+
public static func formatParagraph(
|
|
59
|
+
_ attributedString: NSMutableAttributedString,
|
|
60
|
+
defaultTextAttributes: [NSAttributedString.Key: Any],
|
|
61
|
+
ranges: [MarcusRange],
|
|
62
|
+
style: RCTMarcusStyle
|
|
63
|
+
) {
|
|
64
|
+
format(
|
|
65
|
+
attributedString,
|
|
66
|
+
defaultTextAttributes: defaultTextAttributes,
|
|
67
|
+
ranges: ranges,
|
|
68
|
+
style: style,
|
|
69
|
+
fonts: MarkdownReactFonts(),
|
|
70
|
+
resetAttributes: false,
|
|
71
|
+
display: true
|
|
72
|
+
)
|
|
73
|
+
}
|
|
51
74
|
}
|