react-native-enriched-markdown 0.1.0 → 0.1.1
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 +20 -0
- package/README.md +479 -0
- package/ReactNativeEnrichedMarkdown.podspec +27 -0
- package/android/build.gradle +101 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +39 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +21 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ComponentDescriptors.cpp +22 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ComponentDescriptors.h +24 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +24 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +25 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +57 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +1164 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.cpp +17 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/ShadowNodes.h +32 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/States.cpp +16 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/States.h +20 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/baseline-prof.txt +65 -0
- package/android/src/main/cpp/jni-adapter.cpp +203 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +153 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +119 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextPackage.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +165 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkPressEvent.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +48 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +166 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +89 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +105 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeRenderer.kt +35 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/DocumentRenderer.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/EmphasisRenderer.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +54 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ImageRenderer.kt +52 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LineBreakRenderer.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +28 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +105 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +58 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +99 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ParagraphRenderer.kt +66 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/Renderer.kt +95 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +85 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/StrongRenderer.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/TextRenderer.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ThematicBreakRenderer.kt +44 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +136 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +135 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +180 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +196 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +34 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/HeadingSpan.kt +38 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +320 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LineHeightSpan.kt +36 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +37 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/MarginBottomSpan.kt +76 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +87 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +37 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/TextSpan.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ThematicBreakSpan.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BaseBlockStyle.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +48 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +51 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeStyle.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/EmphasisStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/HeadingStyle.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineImageStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkStyle.kt +19 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +54 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ParagraphStyle.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StrongStyle.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +180 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleParser.kt +75 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ThematicBreakStyle.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/AsyncDrawable.kt +91 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/HTMLGenerator.kt +809 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/MarkdownExtractor.kt +365 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/SelectionActionMode.kt +139 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/Utils.kt +181 -0
- package/android/src/main/jni/CMakeLists.txt +82 -0
- package/android/src/main/jni/EnrichedMarkdownTextSpec.cpp +21 -0
- package/android/src/main/jni/EnrichedMarkdownTextSpec.h +25 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextComponentDescriptor.h +29 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextMeasurementManager.cpp +45 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextMeasurementManager.h +21 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +33 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +49 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +9 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +25 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +19 -0
- package/cpp/md4c/md4c.c +6492 -0
- package/cpp/md4c/md4c.h +402 -0
- package/cpp/parser/MD4CParser.cpp +314 -0
- package/cpp/parser/MD4CParser.hpp +23 -0
- package/cpp/parser/MarkdownASTNode.hpp +49 -0
- package/ios/EnrichedMarkdownText.h +18 -0
- package/ios/EnrichedMarkdownText.mm +1074 -0
- package/ios/attachments/ImageAttachment.h +23 -0
- package/ios/attachments/ImageAttachment.m +185 -0
- package/ios/attachments/ThematicBreakAttachment.h +15 -0
- package/ios/attachments/ThematicBreakAttachment.m +33 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ComponentDescriptors.cpp +22 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ComponentDescriptors.h +24 -0
- package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.cpp +24 -0
- package/ios/generated/EnrichedMarkdownTextSpec/EventEmitters.h +25 -0
- package/ios/generated/EnrichedMarkdownTextSpec/Props.cpp +57 -0
- package/ios/generated/EnrichedMarkdownTextSpec/Props.h +1164 -0
- package/ios/generated/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +20 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ShadowNodes.cpp +17 -0
- package/ios/generated/EnrichedMarkdownTextSpec/ShadowNodes.h +32 -0
- package/ios/generated/EnrichedMarkdownTextSpec/States.cpp +16 -0
- package/ios/generated/EnrichedMarkdownTextSpec/States.h +20 -0
- package/ios/internals/EnrichedMarkdownTextComponentDescriptor.h +19 -0
- package/ios/internals/EnrichedMarkdownTextShadowNode.h +43 -0
- package/ios/internals/EnrichedMarkdownTextShadowNode.mm +85 -0
- package/ios/internals/EnrichedMarkdownTextState.h +24 -0
- package/ios/parser/MarkdownASTNode.h +33 -0
- package/ios/parser/MarkdownASTNode.m +32 -0
- package/ios/parser/MarkdownParser.h +8 -0
- package/ios/parser/MarkdownParser.mm +13 -0
- package/ios/parser/MarkdownParserBridge.mm +110 -0
- package/ios/renderer/AttributedRenderer.h +9 -0
- package/ios/renderer/AttributedRenderer.m +119 -0
- package/ios/renderer/BlockquoteRenderer.h +7 -0
- package/ios/renderer/BlockquoteRenderer.m +159 -0
- package/ios/renderer/CodeBlockRenderer.h +10 -0
- package/ios/renderer/CodeBlockRenderer.m +89 -0
- package/ios/renderer/CodeRenderer.h +10 -0
- package/ios/renderer/CodeRenderer.m +60 -0
- package/ios/renderer/EmphasisRenderer.h +6 -0
- package/ios/renderer/EmphasisRenderer.m +96 -0
- package/ios/renderer/HeadingRenderer.h +7 -0
- package/ios/renderer/HeadingRenderer.m +98 -0
- package/ios/renderer/ImageRenderer.h +12 -0
- package/ios/renderer/ImageRenderer.m +62 -0
- package/ios/renderer/LinkRenderer.h +7 -0
- package/ios/renderer/LinkRenderer.m +69 -0
- package/ios/renderer/ListItemRenderer.h +16 -0
- package/ios/renderer/ListItemRenderer.m +91 -0
- package/ios/renderer/ListRenderer.h +13 -0
- package/ios/renderer/ListRenderer.m +67 -0
- package/ios/renderer/NodeRenderer.h +8 -0
- package/ios/renderer/ParagraphRenderer.h +7 -0
- package/ios/renderer/ParagraphRenderer.m +69 -0
- package/ios/renderer/RenderContext.h +88 -0
- package/ios/renderer/RenderContext.m +248 -0
- package/ios/renderer/RendererFactory.h +12 -0
- package/ios/renderer/RendererFactory.m +110 -0
- package/ios/renderer/StrongRenderer.h +6 -0
- package/ios/renderer/StrongRenderer.m +83 -0
- package/ios/renderer/TextRenderer.h +6 -0
- package/ios/renderer/TextRenderer.m +16 -0
- package/ios/renderer/ThematicBreakRenderer.h +5 -0
- package/ios/renderer/ThematicBreakRenderer.m +53 -0
- package/ios/styles/StyleConfig.h +228 -0
- package/ios/styles/StyleConfig.mm +1467 -0
- package/ios/utils/BlockquoteBorder.h +20 -0
- package/ios/utils/BlockquoteBorder.m +92 -0
- package/ios/utils/CodeBackground.h +19 -0
- package/ios/utils/CodeBackground.m +191 -0
- package/ios/utils/CodeBlockBackground.h +17 -0
- package/ios/utils/CodeBlockBackground.m +87 -0
- package/ios/utils/EditMenuUtils.h +22 -0
- package/ios/utils/EditMenuUtils.m +118 -0
- package/ios/utils/FontUtils.h +20 -0
- package/ios/utils/FontUtils.m +13 -0
- package/ios/utils/HTMLGenerator.h +20 -0
- package/ios/utils/HTMLGenerator.m +779 -0
- package/ios/utils/LastElementUtils.h +53 -0
- package/ios/utils/ListMarkerDrawer.h +15 -0
- package/ios/utils/ListMarkerDrawer.m +127 -0
- package/ios/utils/MarkdownExtractor.h +17 -0
- package/ios/utils/MarkdownExtractor.m +295 -0
- package/ios/utils/ParagraphStyleUtils.h +13 -0
- package/ios/utils/ParagraphStyleUtils.m +56 -0
- package/ios/utils/PasteboardUtils.h +36 -0
- package/ios/utils/PasteboardUtils.m +134 -0
- package/ios/utils/RTFExportUtils.h +24 -0
- package/ios/utils/RTFExportUtils.m +297 -0
- package/ios/utils/RuntimeKeys.h +38 -0
- package/ios/utils/RuntimeKeys.m +11 -0
- package/ios/utils/TextViewLayoutManager.h +14 -0
- package/ios/utils/TextViewLayoutManager.mm +113 -0
- package/lib/module/EnrichedMarkdownText.js +34 -0
- package/lib/module/EnrichedMarkdownText.js.map +1 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +130 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/normalizeMarkdownStyle.js +340 -0
- package/lib/module/normalizeMarkdownStyle.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/EnrichedMarkdownText.d.ts +101 -0
- package/lib/typescript/src/EnrichedMarkdownText.d.ts.map +1 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +111 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts +6 -0
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -0
- package/package.json +186 -1
- package/react-native.config.js +13 -0
- package/src/EnrichedMarkdownText.tsx +152 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +130 -0
- package/src/index.tsx +7 -0
- package/src/normalizeMarkdownStyle.ts +377 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
3
|
+
*
|
|
4
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
5
|
+
* once the code is regenerated.
|
|
6
|
+
*
|
|
7
|
+
* @generated by codegen project: GenerateComponentHObjCpp.js
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#import <Foundation/Foundation.h>
|
|
11
|
+
#import <React/RCTDefines.h>
|
|
12
|
+
#import <React/RCTLog.h>
|
|
13
|
+
|
|
14
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
15
|
+
|
|
16
|
+
@protocol RCTEnrichedMarkdownTextViewProtocol <NSObject>
|
|
17
|
+
|
|
18
|
+
@end
|
|
19
|
+
|
|
20
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
4
|
+
*
|
|
5
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
6
|
+
* once the code is regenerated.
|
|
7
|
+
*
|
|
8
|
+
* @generated by codegen project: GenerateShadowNodeCpp.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#include "ShadowNodes.h"
|
|
12
|
+
|
|
13
|
+
namespace facebook::react {
|
|
14
|
+
|
|
15
|
+
extern const char EnrichedMarkdownTextComponentName[] = "EnrichedMarkdownText";
|
|
16
|
+
|
|
17
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
4
|
+
*
|
|
5
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
6
|
+
* once the code is regenerated.
|
|
7
|
+
*
|
|
8
|
+
* @generated by codegen project: GenerateShadowNodeH.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#pragma once
|
|
12
|
+
|
|
13
|
+
#include "EventEmitters.h"
|
|
14
|
+
#include "Props.h"
|
|
15
|
+
#include "States.h"
|
|
16
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
17
|
+
#include <jsi/jsi.h>
|
|
18
|
+
|
|
19
|
+
namespace facebook::react {
|
|
20
|
+
|
|
21
|
+
JSI_EXPORT extern const char EnrichedMarkdownTextComponentName[];
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* `ShadowNode` for <EnrichedMarkdownText> component.
|
|
25
|
+
*/
|
|
26
|
+
using EnrichedMarkdownTextShadowNode = ConcreteViewShadowNode<
|
|
27
|
+
EnrichedMarkdownTextComponentName,
|
|
28
|
+
EnrichedMarkdownTextProps,
|
|
29
|
+
EnrichedMarkdownTextEventEmitter,
|
|
30
|
+
EnrichedMarkdownTextState>;
|
|
31
|
+
|
|
32
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
4
|
+
*
|
|
5
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
6
|
+
* once the code is regenerated.
|
|
7
|
+
*
|
|
8
|
+
* @generated by codegen project: GenerateStateCpp.js
|
|
9
|
+
*/
|
|
10
|
+
#include "States.h"
|
|
11
|
+
|
|
12
|
+
namespace facebook::react {
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
|
|
3
|
+
*
|
|
4
|
+
* Do not edit this file as changes may cause incorrect behavior and will be lost
|
|
5
|
+
* once the code is regenerated.
|
|
6
|
+
*
|
|
7
|
+
* @generated by codegen project: GenerateStateH.js
|
|
8
|
+
*/
|
|
9
|
+
#pragma once
|
|
10
|
+
|
|
11
|
+
#include <react/renderer/core/StateData.h>
|
|
12
|
+
#ifdef RN_SERIALIZABLE_STATE
|
|
13
|
+
#include <folly/dynamic.h>
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
namespace facebook::react {
|
|
17
|
+
|
|
18
|
+
using EnrichedMarkdownTextState = StateData;
|
|
19
|
+
|
|
20
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "EnrichedMarkdownTextShadowNode.h"
|
|
3
|
+
#include <react/debug/react_native_assert.h>
|
|
4
|
+
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
|
5
|
+
|
|
6
|
+
namespace facebook::react {
|
|
7
|
+
|
|
8
|
+
class EnrichedMarkdownTextComponentDescriptor final
|
|
9
|
+
: public ConcreteComponentDescriptor<EnrichedMarkdownTextShadowNode> {
|
|
10
|
+
public:
|
|
11
|
+
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
|
|
12
|
+
|
|
13
|
+
void adopt(ShadowNode &shadowNode) const override {
|
|
14
|
+
react_native_assert(dynamic_cast<EnrichedMarkdownTextShadowNode *>(&shadowNode));
|
|
15
|
+
ConcreteComponentDescriptor::adopt(shadowNode);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include <ReactNativeEnrichedMarkdown/EnrichedMarkdownTextState.h>
|
|
3
|
+
#include <ReactNativeEnrichedMarkdown/EventEmitters.h>
|
|
4
|
+
#include <ReactNativeEnrichedMarkdown/Props.h>
|
|
5
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
6
|
+
#include <react/renderer/core/LayoutConstraints.h>
|
|
7
|
+
|
|
8
|
+
namespace facebook::react {
|
|
9
|
+
|
|
10
|
+
JSI_EXPORT extern const char EnrichedMarkdownTextComponentName[];
|
|
11
|
+
|
|
12
|
+
/// ShadowNode implementing measureContent for automatic height calculation.
|
|
13
|
+
class EnrichedMarkdownTextShadowNode
|
|
14
|
+
: public ConcreteViewShadowNode<EnrichedMarkdownTextComponentName, EnrichedMarkdownTextProps,
|
|
15
|
+
EnrichedMarkdownTextEventEmitter, EnrichedMarkdownTextState> {
|
|
16
|
+
public:
|
|
17
|
+
using ConcreteViewShadowNode::ConcreteViewShadowNode;
|
|
18
|
+
|
|
19
|
+
EnrichedMarkdownTextShadowNode(const ShadowNodeFragment &fragment, const ShadowNodeFamily::Shared &family,
|
|
20
|
+
ShadowNodeTraits traits);
|
|
21
|
+
|
|
22
|
+
EnrichedMarkdownTextShadowNode(const ShadowNode &sourceShadowNode, const ShadowNodeFragment &fragment);
|
|
23
|
+
|
|
24
|
+
void dirtyLayoutIfNeeded();
|
|
25
|
+
|
|
26
|
+
Size measureContent(const LayoutContext &layoutContext, const LayoutConstraints &layoutConstraints) const override;
|
|
27
|
+
|
|
28
|
+
static ShadowNodeTraits BaseTraits()
|
|
29
|
+
{
|
|
30
|
+
auto traits = ConcreteViewShadowNode::BaseTraits();
|
|
31
|
+
traits.set(ShadowNodeTraits::Trait::LeafYogaNode);
|
|
32
|
+
traits.set(ShadowNodeTraits::Trait::MeasurableYogaNode);
|
|
33
|
+
return traits;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private:
|
|
37
|
+
int localHeightRecalculationCounter_{0};
|
|
38
|
+
|
|
39
|
+
// Creates mock view off-screen for initial measurement when real view doesn't exist
|
|
40
|
+
id setupMockEnrichedMarkdownText_(CGFloat width) const;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#import "EnrichedMarkdownTextShadowNode.h"
|
|
2
|
+
#import "EnrichedMarkdownText.h"
|
|
3
|
+
#import <react/utils/ManagedObjectWrapper.h>
|
|
4
|
+
#import <yoga/Yoga.h>
|
|
5
|
+
|
|
6
|
+
namespace facebook::react {
|
|
7
|
+
|
|
8
|
+
extern const char EnrichedMarkdownTextComponentName[] = "EnrichedMarkdownText";
|
|
9
|
+
|
|
10
|
+
EnrichedMarkdownTextShadowNode::EnrichedMarkdownTextShadowNode(const ShadowNodeFragment &fragment,
|
|
11
|
+
const ShadowNodeFamily::Shared &family,
|
|
12
|
+
ShadowNodeTraits traits)
|
|
13
|
+
: ConcreteViewShadowNode(fragment, family, traits)
|
|
14
|
+
{
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
EnrichedMarkdownTextShadowNode::EnrichedMarkdownTextShadowNode(const ShadowNode &sourceShadowNode,
|
|
18
|
+
const ShadowNodeFragment &fragment)
|
|
19
|
+
: ConcreteViewShadowNode(sourceShadowNode, fragment)
|
|
20
|
+
{
|
|
21
|
+
dirtyLayoutIfNeeded();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
void EnrichedMarkdownTextShadowNode::dirtyLayoutIfNeeded()
|
|
25
|
+
{
|
|
26
|
+
const auto state = this->getStateData();
|
|
27
|
+
const int receivedCounter = state.getHeightRecalculationCounter();
|
|
28
|
+
|
|
29
|
+
if (receivedCounter > localHeightRecalculationCounter_) {
|
|
30
|
+
localHeightRecalculationCounter_ = receivedCounter;
|
|
31
|
+
YGNodeMarkDirty(&yogaNode_);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// Creates a mock view off-screen to measure content when real view isn't ready yet.
|
|
36
|
+
id EnrichedMarkdownTextShadowNode::setupMockEnrichedMarkdownText_(CGFloat width) const
|
|
37
|
+
{
|
|
38
|
+
EnrichedMarkdownText *mockView = [[EnrichedMarkdownText alloc] initWithFrame:CGRectMake(20000, 20000, width, 1000)];
|
|
39
|
+
|
|
40
|
+
const auto props = this->getProps();
|
|
41
|
+
[mockView updateProps:props oldProps:nullptr];
|
|
42
|
+
|
|
43
|
+
// Render markdown synchronously for accurate measurement
|
|
44
|
+
const auto &typedProps = *std::static_pointer_cast<const EnrichedMarkdownTextProps>(props);
|
|
45
|
+
if (!typedProps.markdown.empty()) {
|
|
46
|
+
NSString *markdown = [NSString stringWithUTF8String:typedProps.markdown.c_str()];
|
|
47
|
+
[mockView renderMarkdownSynchronously:markdown];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return mockView;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Size EnrichedMarkdownTextShadowNode::measureContent(const LayoutContext &layoutContext,
|
|
54
|
+
const LayoutConstraints &layoutConstraints) const
|
|
55
|
+
{
|
|
56
|
+
CGFloat maxWidth = layoutConstraints.maximumSize.width;
|
|
57
|
+
CGFloat maxHeight = layoutConstraints.maximumSize.height;
|
|
58
|
+
|
|
59
|
+
RCTInternalGenericWeakWrapper *weakWrapper =
|
|
60
|
+
(RCTInternalGenericWeakWrapper *)unwrapManagedObject(getStateData().getComponentViewRef());
|
|
61
|
+
EnrichedMarkdownText *view = weakWrapper ? (EnrichedMarkdownText *)weakWrapper.object : nil;
|
|
62
|
+
|
|
63
|
+
__block CGSize size;
|
|
64
|
+
|
|
65
|
+
// Measure on main thread (required for UIKit)
|
|
66
|
+
void (^measureBlock)(void) = ^{
|
|
67
|
+
if (view) {
|
|
68
|
+
size = [view measureSize:maxWidth];
|
|
69
|
+
} else {
|
|
70
|
+
// No view yet - create mock view for accurate initial measurement
|
|
71
|
+
EnrichedMarkdownText *mockView = setupMockEnrichedMarkdownText_(maxWidth);
|
|
72
|
+
size = [mockView measureSize:maxWidth];
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
if ([NSThread isMainThread]) {
|
|
77
|
+
measureBlock();
|
|
78
|
+
} else {
|
|
79
|
+
dispatch_sync(dispatch_get_main_queue(), measureBlock);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {size.width, MIN(size.height, maxHeight)};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include <memory>
|
|
3
|
+
|
|
4
|
+
namespace facebook::react {
|
|
5
|
+
|
|
6
|
+
/// Counter triggers layout recalculation, viewRef allows shadow node to access view for measurement.
|
|
7
|
+
class EnrichedMarkdownTextState {
|
|
8
|
+
public:
|
|
9
|
+
EnrichedMarkdownTextState() = default;
|
|
10
|
+
EnrichedMarkdownTextState(int counter, std::shared_ptr<void> ref) : counter_(counter), viewRef_(ref) {}
|
|
11
|
+
|
|
12
|
+
int getHeightRecalculationCounter() const {
|
|
13
|
+
return counter_;
|
|
14
|
+
}
|
|
15
|
+
std::shared_ptr<void> getComponentViewRef() const {
|
|
16
|
+
return viewRef_;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private:
|
|
20
|
+
int counter_{0};
|
|
21
|
+
std::shared_ptr<void> viewRef_{nullptr};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
|
|
3
|
+
typedef NS_ENUM(NSInteger, MarkdownNodeType) {
|
|
4
|
+
MarkdownNodeTypeDocument,
|
|
5
|
+
MarkdownNodeTypeParagraph,
|
|
6
|
+
MarkdownNodeTypeText,
|
|
7
|
+
MarkdownNodeTypeLink,
|
|
8
|
+
MarkdownNodeTypeHeading,
|
|
9
|
+
MarkdownNodeTypeLineBreak,
|
|
10
|
+
MarkdownNodeTypeStrong,
|
|
11
|
+
MarkdownNodeTypeEmphasis,
|
|
12
|
+
MarkdownNodeTypeCode,
|
|
13
|
+
MarkdownNodeTypeImage,
|
|
14
|
+
MarkdownNodeTypeBlockquote,
|
|
15
|
+
MarkdownNodeTypeUnorderedList,
|
|
16
|
+
MarkdownNodeTypeOrderedList,
|
|
17
|
+
MarkdownNodeTypeListItem,
|
|
18
|
+
MarkdownNodeTypeCodeBlock,
|
|
19
|
+
MarkdownNodeTypeThematicBreak
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
@interface MarkdownASTNode : NSObject
|
|
23
|
+
|
|
24
|
+
@property (nonatomic, assign) MarkdownNodeType type;
|
|
25
|
+
@property (nonatomic, strong) NSString *content;
|
|
26
|
+
@property (nonatomic, strong) NSMutableDictionary *attributes;
|
|
27
|
+
@property (nonatomic, strong) NSMutableArray<MarkdownASTNode *> *children;
|
|
28
|
+
|
|
29
|
+
- (instancetype)initWithType:(MarkdownNodeType)type;
|
|
30
|
+
- (void)addChild:(MarkdownASTNode *)child;
|
|
31
|
+
- (void)setAttribute:(NSString *)key value:(NSString *)value;
|
|
32
|
+
|
|
33
|
+
@end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#import "MarkdownASTNode.h"
|
|
2
|
+
|
|
3
|
+
@implementation MarkdownASTNode
|
|
4
|
+
|
|
5
|
+
- (instancetype)initWithType:(MarkdownNodeType)type
|
|
6
|
+
{
|
|
7
|
+
if (self = [super init]) {
|
|
8
|
+
_type = type;
|
|
9
|
+
_content = nil;
|
|
10
|
+
_attributes = [[NSMutableDictionary alloc] init];
|
|
11
|
+
_children = [[NSMutableArray alloc] init];
|
|
12
|
+
}
|
|
13
|
+
return self;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
- (void)addChild:(MarkdownASTNode *)child
|
|
17
|
+
{
|
|
18
|
+
[_children addObject:child];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
- (void)setAttribute:(NSString *)key value:(NSString *)value
|
|
22
|
+
{
|
|
23
|
+
_attributes[key] = value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
- (NSString *)description
|
|
27
|
+
{
|
|
28
|
+
return [NSString stringWithFormat:@"MarkdownASTNode(type=%ld, content=%@, children=%lu)", (long)_type, _content,
|
|
29
|
+
(unsigned long)_children.count];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#import "MarkdownParser.h"
|
|
2
|
+
#import "MarkdownASTNode.h"
|
|
3
|
+
|
|
4
|
+
extern MarkdownASTNode *parseMarkdownWithCppParser(NSString *markdown);
|
|
5
|
+
|
|
6
|
+
@implementation MarkdownParser
|
|
7
|
+
|
|
8
|
+
- (MarkdownASTNode *)parseMarkdown:(NSString *)markdown
|
|
9
|
+
{
|
|
10
|
+
return parseMarkdownWithCppParser(markdown);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#include "MD4CParser.hpp"
|
|
2
|
+
#import "MarkdownASTNode.h"
|
|
3
|
+
#include "MarkdownASTNode.hpp"
|
|
4
|
+
|
|
5
|
+
// Convert C++ AST node to Objective-C AST node
|
|
6
|
+
static MarkdownASTNode *convertCppASTToObjC(std::shared_ptr<Markdown::MarkdownASTNode> cppNode)
|
|
7
|
+
{
|
|
8
|
+
if (!cppNode) {
|
|
9
|
+
return [[MarkdownASTNode alloc] initWithType:MarkdownNodeTypeDocument];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Convert C++ NodeType enum to Objective-C MarkdownNodeType
|
|
13
|
+
MarkdownNodeType objcType;
|
|
14
|
+
switch (cppNode->type) {
|
|
15
|
+
case Markdown::NodeType::Document:
|
|
16
|
+
objcType = MarkdownNodeTypeDocument;
|
|
17
|
+
break;
|
|
18
|
+
case Markdown::NodeType::Paragraph:
|
|
19
|
+
objcType = MarkdownNodeTypeParagraph;
|
|
20
|
+
break;
|
|
21
|
+
case Markdown::NodeType::Text:
|
|
22
|
+
objcType = MarkdownNodeTypeText;
|
|
23
|
+
break;
|
|
24
|
+
case Markdown::NodeType::Link:
|
|
25
|
+
objcType = MarkdownNodeTypeLink;
|
|
26
|
+
break;
|
|
27
|
+
case Markdown::NodeType::Heading:
|
|
28
|
+
objcType = MarkdownNodeTypeHeading;
|
|
29
|
+
break;
|
|
30
|
+
case Markdown::NodeType::LineBreak:
|
|
31
|
+
objcType = MarkdownNodeTypeLineBreak;
|
|
32
|
+
break;
|
|
33
|
+
case Markdown::NodeType::Strong:
|
|
34
|
+
objcType = MarkdownNodeTypeStrong;
|
|
35
|
+
break;
|
|
36
|
+
case Markdown::NodeType::Emphasis:
|
|
37
|
+
objcType = MarkdownNodeTypeEmphasis;
|
|
38
|
+
break;
|
|
39
|
+
case Markdown::NodeType::Code:
|
|
40
|
+
objcType = MarkdownNodeTypeCode;
|
|
41
|
+
break;
|
|
42
|
+
case Markdown::NodeType::Image:
|
|
43
|
+
objcType = MarkdownNodeTypeImage;
|
|
44
|
+
break;
|
|
45
|
+
case Markdown::NodeType::Blockquote:
|
|
46
|
+
objcType = MarkdownNodeTypeBlockquote;
|
|
47
|
+
break;
|
|
48
|
+
case Markdown::NodeType::UnorderedList:
|
|
49
|
+
objcType = MarkdownNodeTypeUnorderedList;
|
|
50
|
+
break;
|
|
51
|
+
case Markdown::NodeType::OrderedList:
|
|
52
|
+
objcType = MarkdownNodeTypeOrderedList;
|
|
53
|
+
break;
|
|
54
|
+
case Markdown::NodeType::ListItem:
|
|
55
|
+
objcType = MarkdownNodeTypeListItem;
|
|
56
|
+
break;
|
|
57
|
+
case Markdown::NodeType::CodeBlock:
|
|
58
|
+
objcType = MarkdownNodeTypeCodeBlock;
|
|
59
|
+
break;
|
|
60
|
+
case Markdown::NodeType::ThematicBreak:
|
|
61
|
+
objcType = MarkdownNodeTypeThematicBreak;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
MarkdownASTNode *objcNode = [[MarkdownASTNode alloc] initWithType:objcType];
|
|
66
|
+
|
|
67
|
+
// Convert content
|
|
68
|
+
if (!cppNode->content.empty()) {
|
|
69
|
+
objcNode.content = [NSString stringWithUTF8String:cppNode->content.c_str()];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Convert attributes
|
|
73
|
+
for (const auto &[key, value] : cppNode->attributes) {
|
|
74
|
+
NSString *objcKey = [NSString stringWithUTF8String:key.c_str()];
|
|
75
|
+
NSString *objcValue = [NSString stringWithUTF8String:value.c_str()];
|
|
76
|
+
[objcNode setAttribute:objcKey value:objcValue];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Convert children recursively
|
|
80
|
+
for (const auto &child : cppNode->children) {
|
|
81
|
+
MarkdownASTNode *objcChild = convertCppASTToObjC(child);
|
|
82
|
+
[objcNode addChild:objcChild];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return objcNode;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Public function to parse markdown using C++ parser and convert to Objective-C AST
|
|
89
|
+
MarkdownASTNode *parseMarkdownWithCppParser(NSString *markdown)
|
|
90
|
+
{
|
|
91
|
+
if (markdown.length == 0) {
|
|
92
|
+
return [[MarkdownASTNode alloc] initWithType:MarkdownNodeTypeDocument];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Convert NSString to std::string
|
|
96
|
+
const char *utf8String = [markdown UTF8String];
|
|
97
|
+
if (!utf8String) {
|
|
98
|
+
NSLog(@"MarkdownParserBridge: Failed to convert markdown to UTF-8");
|
|
99
|
+
return [[MarkdownASTNode alloc] initWithType:MarkdownNodeTypeDocument];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
std::string cppMarkdown(utf8String);
|
|
103
|
+
|
|
104
|
+
// Parse using C++ parser
|
|
105
|
+
Markdown::MD4CParser parser;
|
|
106
|
+
auto cppAST = parser.parse(cppMarkdown);
|
|
107
|
+
|
|
108
|
+
// Convert C++ AST to Objective-C AST
|
|
109
|
+
return convertCppASTToObjC(cppAST);
|
|
110
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
|
|
3
|
+
@class MarkdownASTNode;
|
|
4
|
+
@class RenderContext;
|
|
5
|
+
|
|
6
|
+
@interface AttributedRenderer : NSObject
|
|
7
|
+
- (instancetype)initWithConfig:(id)config;
|
|
8
|
+
- (NSMutableAttributedString *)renderRoot:(MarkdownASTNode *)root context:(RenderContext *)context;
|
|
9
|
+
@end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#import "AttributedRenderer.h"
|
|
2
|
+
#import "CodeBlockBackground.h"
|
|
3
|
+
#import "LastElementUtils.h"
|
|
4
|
+
#import "MarkdownASTNode.h"
|
|
5
|
+
#import "NodeRenderer.h"
|
|
6
|
+
#import "RenderContext.h"
|
|
7
|
+
#import "RendererFactory.h"
|
|
8
|
+
#import "StyleConfig.h"
|
|
9
|
+
|
|
10
|
+
@implementation AttributedRenderer {
|
|
11
|
+
StyleConfig *_config;
|
|
12
|
+
RendererFactory *_rendererFactory;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
- (instancetype)initWithConfig:(StyleConfig *)config
|
|
16
|
+
{
|
|
17
|
+
self = [super init];
|
|
18
|
+
if (self) {
|
|
19
|
+
_config = config;
|
|
20
|
+
_rendererFactory = [[RendererFactory alloc] initWithConfig:config];
|
|
21
|
+
}
|
|
22
|
+
return self;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Entry point for rendering a Markdown AST.
|
|
27
|
+
* Sets the baseline global style and initiates the recursive traversal.
|
|
28
|
+
*/
|
|
29
|
+
- (NSMutableAttributedString *)renderRoot:(MarkdownASTNode *)root context:(RenderContext *)context
|
|
30
|
+
{
|
|
31
|
+
if (!root)
|
|
32
|
+
return [[NSMutableAttributedString alloc] init];
|
|
33
|
+
|
|
34
|
+
// 1. Establish the global baseline style.
|
|
35
|
+
// This ensures that leaf nodes (like Text) have valid attributes if they appear at the root.
|
|
36
|
+
[context setBlockStyle:BlockTypeParagraph font:_config.paragraphFont color:_config.paragraphColor headingLevel:0];
|
|
37
|
+
|
|
38
|
+
NSMutableAttributedString *output = [[NSMutableAttributedString alloc] init];
|
|
39
|
+
|
|
40
|
+
// 2. Iterate through root children.
|
|
41
|
+
// We skip the 'Root' node itself as it is a container, not a renderable element.
|
|
42
|
+
for (MarkdownASTNode *node in root.children) {
|
|
43
|
+
[self renderNodeRecursive:node into:output context:context];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 3. Remove trailing paragraph spacing from last block element
|
|
47
|
+
[self removeTrailingSpacing:output];
|
|
48
|
+
|
|
49
|
+
// 4. Cleanup global state to prevent side effects in subsequent renders.
|
|
50
|
+
[context clearBlockStyle];
|
|
51
|
+
|
|
52
|
+
return output;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// Removes trailing margin spacing while preserving code block padding
|
|
56
|
+
- (void)removeTrailingSpacing:(NSMutableAttributedString *)output
|
|
57
|
+
{
|
|
58
|
+
if (output.length == 0)
|
|
59
|
+
return;
|
|
60
|
+
|
|
61
|
+
NSRange lastContent = [output.string rangeOfCharacterFromSet:[[NSCharacterSet newlineCharacterSet] invertedSet]
|
|
62
|
+
options:NSBackwardsSearch];
|
|
63
|
+
if (lastContent.location == NSNotFound)
|
|
64
|
+
return;
|
|
65
|
+
|
|
66
|
+
if (isLastElementCodeBlock(output)) {
|
|
67
|
+
// Code block: preserve bottom padding, only trim external margin
|
|
68
|
+
NSRange codeBlockRange;
|
|
69
|
+
[output attribute:CodeBlockAttributeName atIndex:lastContent.location effectiveRange:&codeBlockRange];
|
|
70
|
+
NSUInteger codeBlockEnd = NSMaxRange(codeBlockRange);
|
|
71
|
+
if (codeBlockEnd < output.length) {
|
|
72
|
+
[output deleteCharactersInRange:NSMakeRange(codeBlockEnd, output.length - codeBlockEnd)];
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
// Other elements: trim trailing newlines and zero all spacing
|
|
76
|
+
[output deleteCharactersInRange:NSMakeRange(NSMaxRange(lastContent), output.length - NSMaxRange(lastContent))];
|
|
77
|
+
|
|
78
|
+
NSRange range;
|
|
79
|
+
NSParagraphStyle *style = [output attribute:NSParagraphStyleAttributeName
|
|
80
|
+
atIndex:lastContent.location
|
|
81
|
+
effectiveRange:&range];
|
|
82
|
+
if (style) {
|
|
83
|
+
NSMutableParagraphStyle *fixed = [style mutableCopy];
|
|
84
|
+
fixed.paragraphSpacing = 0;
|
|
85
|
+
fixed.paragraphSpacingBefore = 0;
|
|
86
|
+
// For images: zero line spacing to eliminate baseline gaps
|
|
87
|
+
if (isLastElementImage(output)) {
|
|
88
|
+
fixed.lineSpacing = 0;
|
|
89
|
+
}
|
|
90
|
+
[output addAttribute:NSParagraphStyleAttributeName value:fixed range:range];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Orchestrates the recursive traversal of the AST.
|
|
97
|
+
* If a specialized renderer exists for a node type, it takes full control.
|
|
98
|
+
*/
|
|
99
|
+
- (void)renderNodeRecursive:(MarkdownASTNode *)node
|
|
100
|
+
into:(NSMutableAttributedString *)out
|
|
101
|
+
context:(RenderContext *)context
|
|
102
|
+
{
|
|
103
|
+
if (!node)
|
|
104
|
+
return;
|
|
105
|
+
|
|
106
|
+
id<NodeRenderer> renderer = [_rendererFactory rendererForNodeType:node.type];
|
|
107
|
+
|
|
108
|
+
if (renderer) {
|
|
109
|
+
// Specialized renderers (e.g., Strong, Link, Heading) handle their own sub-trees.
|
|
110
|
+
[renderer renderNode:node into:out context:context];
|
|
111
|
+
} else {
|
|
112
|
+
// Fallback: Default to deep-first traversal for unhandled container nodes.
|
|
113
|
+
for (MarkdownASTNode *child in node.children) {
|
|
114
|
+
[self renderNodeRecursive:child into:out context:context];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@end
|