react-native-enriched-markdown-scaffold 0.7.4-scaffold.9 → 1.0.2-scaffold.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/README.md +54 -8
- package/ReactNativeEnrichedMarkdown.podspec +82 -73
- package/android/build.gradle +186 -1
- package/android/consumer-rules.pro +1 -1
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +9 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +3 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerDelegate.java +24 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +8 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +9 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +3 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +75 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +63 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +35 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +838 -23
- package/android/src/main/baseline-prof.txt +1 -1
- package/android/src/main/cpp/jni-adapter.cpp +74 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +175 -11
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +50 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +48 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +10 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +42 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +82 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyPressEvent.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +54 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +901 -462
- package/android/src/main/java/com/swmansion/enriched/markdown/input/MarkdownClipboard.kt +42 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/BlockEditCoordinator.kt +196 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/ClipboardCoordinator.kt +73 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditContext.kt +19 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditPipeline.kt +248 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditSession.kt +52 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/InputConnectionWrapper.kt +101 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/LinkCoordinator.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MarkdownTextWatcher.kt +3 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MentionCoordinator.kt +118 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnChangeStateEvent.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnContextMenuItemPressEvent.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnInputKeyPressEvent.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnPasteEvent.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockStore.kt +254 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +80 -87
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputFormatter.kt +109 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +116 -22
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/MarkdownSerializer.kt +167 -38
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/RangeEditAdjustment.kt +149 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt +66 -5
- package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +4 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockRange.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockType.kt +48 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/FormattingRange.kt +9 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/InputFormatterStyle.kt +39 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathAtom.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +1 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MutableRangeBounds.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputBulletSpan.kt +82 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputHeadingSpan.kt +58 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputListItemSpacingSpan.kt +37 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputOrderedListMarkerSpan.kt +63 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/BlockHandler.kt +54 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/HeadingBlockHandler.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/OrderedListBlockHandler.kt +38 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/UnorderedListBlockHandler.kt +37 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +8 -26
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +9 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +100 -12
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +16 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +74 -18
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +43 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +9 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SoftBreakRenderer.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +30 -27
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +174 -24
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +27 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +13 -16
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +1 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +1 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +182 -46
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListItemSpacingSpan.kt +33 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListMarkerAnchorSpan.kt +38 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +2 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +1 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/TaskListSpan.kt +3 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +3 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +7 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +4 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +13 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/TableStyle.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockHighlighter.kt +90 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockNode.kt +57 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/FeatureFlags.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownSegment.kt +9 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +33 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/ReadableMapExtensions.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/RenderedSegment.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/SegmentSignature.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/StreamingMarkdownFilter.kt +83 -5
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/serialization/MarkdownASTSerializer.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +74 -44
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/PasteTextUtils.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageCache.kt +26 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageDownloader.kt +22 -9
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/LocalImageLoader.kt +161 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/extensions/TextPaintExtensions.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/CheckboxTouchHelper.kt +7 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/TaskListTapUtils.kt +41 -21
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/span/BlockSpacing.kt +15 -8
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLongPressMovementMethod.kt +58 -27
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +19 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +388 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/views/ContextMenuPopup.kt +9 -6
- package/android/src/main/java/com/swmansion/enriched/markdown/views/CopyGlyph.kt +101 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +32 -5
- package/android/src/main/jni/CMakeLists.txt +53 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.cpp +10 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.h +11 -2
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +10 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +12 -2
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +9 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +24 -0
- package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +16 -0
- package/android/src/math/java/com/swmansion/enriched/markdown/input/math/MathChipControllerImpl.kt +21 -4
- package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +7 -2
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
- package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +2 -0
- package/cpp/highlight/CodeBlockHighlighter.cpp +284 -0
- package/cpp/highlight/CodeBlockHighlighter.hpp +66 -0
- package/cpp/highlight/CodeBlockLanguages.cpp +138 -0
- package/cpp/highlight/CodeBlockLanguages.hpp +25 -0
- package/cpp/highlight/HighlightGrammars.hpp +28 -0
- package/cpp/highlight/code_highlight_podspec.rb +211 -0
- package/cpp/highlight/gen-registry.mjs +155 -0
- package/cpp/highlight/grammar-versions.json +159 -0
- package/cpp/highlight/vendor/generated/generated_queries.h +1665 -0
- package/cpp/highlight/vendor/generated/generated_registry.cpp +59 -0
- package/cpp/parser/MD4CParser.cpp +173 -61
- package/cpp/parser/MD4CParser.hpp +1 -0
- package/cpp/parser/MarkdownASTNode.hpp +2 -1
- package/cpp/wasm/ASTSerializer.cpp +2 -0
- package/cpp/wasm/build.sh +1 -0
- package/cpp/wasm/md4c_wasm.cpp +3 -1
- package/docs/ACCESSIBILITY.md +14 -2
- package/docs/API_REFERENCE.md +222 -8
- package/docs/BREAKING_CHANGES.md +129 -0
- package/docs/CODE_HIGHLIGHT.md +133 -0
- package/docs/COPY_OPTIONS.md +21 -3
- package/docs/ELEMENTS_STRUCTURE.md +52 -4
- package/docs/IMAGE_CACHING.md +30 -0
- package/docs/INPUT.md +108 -11
- package/docs/LATEX_MATH.md +87 -45
- package/docs/MACOS.md +1 -1
- package/docs/MARKDOWN_STREAMING.md +27 -1
- package/docs/NATIVE_ASSETS.md +127 -0
- package/docs/RTL.md +1 -1
- package/docs/STYLES.md +46 -6
- package/docs/TESTING.md +112 -0
- package/docs/TEXT.md +16 -6
- package/docs/WEB.md +3 -1
- package/ios/EnrichedMarkdown.h +7 -0
- package/ios/EnrichedMarkdown.mm +262 -67
- package/ios/EnrichedMarkdownText.h +7 -0
- package/ios/EnrichedMarkdownText.mm +97 -68
- package/ios/attachments/ENRMImageAttachment.h +9 -0
- package/ios/attachments/ENRMImageAttachment.m +143 -40
- package/ios/attachments/ENRMMathInlineAttachment.h +2 -0
- package/ios/attachments/ENRMMathInlineAttachment.m +48 -81
- package/ios/attachments/ENRMMathInlineAttachmentShared.h +2 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +75 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +63 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +35 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +838 -23
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +131 -0
- package/ios/input/ENRMAutoLinkDetector.h +2 -1
- package/ios/input/ENRMAutoLinkDetector.mm +79 -41
- package/ios/input/ENRMBlockEditCoordinator.h +71 -0
- package/ios/input/ENRMBlockEditCoordinator.mm +195 -0
- package/ios/input/ENRMBlockRange.h +28 -0
- package/ios/input/ENRMBlockRange.mm +30 -0
- package/ios/input/ENRMBlockStore.h +56 -0
- package/ios/input/ENRMBlockStore.mm +275 -0
- package/ios/input/ENRMClipboardCoordinator.h +45 -0
- package/ios/input/ENRMClipboardCoordinator.mm +120 -0
- package/ios/input/ENRMDetectorPipeline.h +2 -1
- package/ios/input/ENRMEditPipeline.h +76 -0
- package/ios/input/ENRMEditPipeline.mm +228 -0
- package/ios/input/ENRMEditSession.h +36 -0
- package/ios/input/ENRMEditSession.mm +63 -0
- package/ios/input/ENRMFormattingStore.h +17 -0
- package/ios/input/ENRMFormattingStore.mm +95 -105
- package/ios/input/ENRMInputBlockType.h +91 -0
- package/ios/input/ENRMInputBlockType.mm +5 -0
- package/ios/input/ENRMInputDecorationDrawer.h +18 -0
- package/ios/input/ENRMInputEventEmitter.h +56 -0
- package/ios/input/ENRMInputEventEmitter.mm +308 -0
- package/ios/input/ENRMInputFormatter.h +29 -69
- package/ios/input/ENRMInputFormatter.mm +216 -124
- package/ios/input/ENRMInputFormatterStyle.h +99 -0
- package/ios/input/ENRMInputFormatterStyle.mm +188 -0
- package/ios/input/ENRMInputLayoutManager.h +9 -0
- package/ios/input/ENRMInputLayoutManager.mm +30 -0
- package/ios/input/ENRMInputListMarkerDrawer.h +32 -0
- package/ios/input/ENRMInputListMarkerDrawer.mm +426 -0
- package/ios/input/ENRMInputParser.h +5 -6
- package/ios/input/ENRMInputParser.mm +461 -178
- package/ios/input/ENRMInputRemend.mm +2 -2
- package/ios/input/ENRMInputStyleStateBuilder.h +40 -0
- package/ios/input/ENRMInputStyleStateBuilder.mm +60 -0
- package/ios/input/ENRMInputTextView.mm +103 -8
- package/ios/input/ENRMInputTypingAttributesController.h +42 -0
- package/ios/input/ENRMInputTypingAttributesController.mm +203 -0
- package/ios/input/ENRMLinkCoordinator.h +41 -0
- package/ios/input/ENRMLinkCoordinator.mm +89 -0
- package/ios/input/ENRMMarkdownSerializer.h +29 -5
- package/ios/input/ENRMMarkdownSerializer.mm +170 -48
- package/ios/input/ENRMMathAtom.h +26 -0
- package/ios/input/ENRMMathAtom.mm +12 -0
- package/ios/input/ENRMMentionCoordinator.h +45 -0
- package/ios/input/ENRMMentionCoordinator.mm +167 -0
- package/ios/input/ENRMRangeEditAdjustment.h +38 -0
- package/ios/input/ENRMRangeEditAdjustment.mm +91 -0
- package/ios/input/ENRMRangeStoreUtils.h +39 -0
- package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +6 -6
- package/ios/input/EnrichedMarkdownTextInput+Internal.h +11 -3
- package/ios/input/EnrichedMarkdownTextInput.h +7 -1
- package/ios/input/EnrichedMarkdownTextInput.mm +1089 -1023
- package/ios/input/InputStylePropsUtils.h +109 -68
- package/ios/input/internals/EnrichedMarkdownTextInputShadowNode.mm +2 -1
- package/ios/input/styles/ENRMBlockHandler.h +46 -0
- package/ios/input/styles/ENRMHeadingBlockHandler.h +15 -0
- package/ios/input/styles/ENRMHeadingBlockHandler.mm +38 -0
- package/ios/input/styles/ENRMOrderedListBlockHandler.h +14 -0
- package/ios/input/styles/ENRMOrderedListBlockHandler.mm +48 -0
- package/ios/input/styles/ENRMUnorderedListBlockHandler.h +15 -0
- package/ios/input/styles/ENRMUnorderedListBlockHandler.mm +51 -0
- package/ios/internals/ENRMViewFreeMeasurement.h +331 -0
- package/ios/internals/EnrichedMarkdownShadowNode.h +1 -3
- package/ios/internals/EnrichedMarkdownShadowNode.mm +16 -20
- package/ios/internals/EnrichedMarkdownTextShadowNode.h +1 -3
- package/ios/internals/EnrichedMarkdownTextShadowNode.mm +15 -20
- package/ios/internals/MeasurementCache.h +33 -7
- package/ios/internals/ShadowMeasurementUtils.h +63 -58
- package/ios/math/ENRMMathChipController.h +74 -0
- package/ios/math/ENRMMathChipController.mm +476 -0
- package/ios/math/ENRMRaTeXBridge.swift +4 -1
- package/ios/parser/ENRMMarkdownParser.h +1 -8
- package/ios/parser/ENRMMarkdownParser.mm +2 -11
- package/ios/parser/MarkdownASTNode.h +2 -1
- package/ios/parser/MarkdownParserBridge.mm +4 -0
- package/ios/renderer/AttributedRenderer.m +20 -2
- package/ios/renderer/BaseRenderer.h +17 -2
- package/ios/renderer/BaseRenderer.m +30 -0
- package/ios/renderer/BlockquoteRenderer.h +1 -1
- package/ios/renderer/BlockquoteRenderer.m +144 -35
- package/ios/renderer/CodeBlockRenderer.h +1 -1
- package/ios/renderer/CodeBlockRenderer.m +27 -23
- package/ios/renderer/CodeRenderer.h +1 -1
- package/ios/renderer/CodeRenderer.m +3 -1
- package/ios/renderer/ENRMImageRenderer.h +1 -1
- package/ios/renderer/ENRMImageRenderer.m +5 -1
- package/ios/renderer/ENRMMathInlineRenderer.h +1 -1
- package/ios/renderer/ENRMMathInlineRenderer.m +5 -1
- package/ios/renderer/ENRMSpoilerRenderer.h +1 -1
- package/ios/renderer/ENRMSpoilerRenderer.m +3 -1
- package/ios/renderer/EmphasisRenderer.h +1 -1
- package/ios/renderer/EmphasisRenderer.m +3 -1
- package/ios/renderer/HeadingRenderer.h +1 -1
- package/ios/renderer/HeadingRenderer.m +13 -6
- package/ios/renderer/HighlightRenderer.h +1 -1
- package/ios/renderer/HighlightRenderer.m +3 -1
- package/ios/renderer/LinkRenderer.h +1 -1
- package/ios/renderer/LinkRenderer.m +3 -1
- package/ios/renderer/ListItemRenderer.h +13 -1
- package/ios/renderer/ListItemRenderer.m +102 -44
- package/ios/renderer/ListRenderer.h +2 -2
- package/ios/renderer/ListRenderer.m +63 -25
- package/ios/renderer/ParagraphRenderer.h +1 -1
- package/ios/renderer/ParagraphRenderer.m +16 -8
- package/ios/renderer/RenderContext.h +10 -0
- package/ios/renderer/RenderContext.m +44 -0
- package/ios/renderer/RendererFactory.m +14 -0
- package/ios/renderer/StrikethroughRenderer.h +1 -1
- package/ios/renderer/StrikethroughRenderer.m +3 -1
- package/ios/renderer/StrongRenderer.h +1 -1
- package/ios/renderer/StrongRenderer.m +3 -1
- package/ios/renderer/SubscriptRenderer.h +1 -1
- package/ios/renderer/SubscriptRenderer.m +3 -1
- package/ios/renderer/SuperscriptRenderer.h +1 -1
- package/ios/renderer/SuperscriptRenderer.m +3 -1
- package/ios/renderer/TextRenderer.h +2 -2
- package/ios/renderer/TextRenderer.m +3 -1
- package/ios/renderer/ThematicBreakRenderer.h +1 -1
- package/ios/renderer/ThematicBreakRenderer.m +3 -1
- package/ios/renderer/UnderlineRenderer.h +1 -1
- package/ios/renderer/UnderlineRenderer.m +3 -1
- package/ios/styles/StyleConfig.h +22 -0
- package/ios/styles/StyleConfig.mm +132 -0
- package/ios/utils/BlockquoteBorder.h +6 -0
- package/ios/utils/BlockquoteBorder.m +249 -52
- package/ios/utils/CodeBlockBackground.m +16 -2
- package/ios/utils/ENRMAtomicSize.h +65 -0
- package/ios/utils/ENRMCodeBlockContent.h +37 -0
- package/ios/utils/ENRMCodeBlockContent.mm +79 -0
- package/ios/utils/ENRMCodeBlockHighlighter.h +37 -0
- package/ios/utils/ENRMCodeBlockHighlighter.mm +51 -0
- package/ios/utils/ENRMFeatureFlags.h +7 -0
- package/ios/utils/ENRMImageDownloader.h +11 -1
- package/ios/utils/ENRMImageDownloader.m +60 -16
- package/ios/utils/ENRMLocalImageLoader.h +31 -0
- package/ios/utils/ENRMLocalImageLoader.m +68 -0
- package/ios/utils/ENRMMathFallback.h +26 -0
- package/ios/utils/ENRMMathFallback.m +18 -0
- package/ios/utils/ENRMTextViewSetup.h +35 -17
- package/ios/utils/EditMenuUtils.m +22 -1
- package/ios/utils/ImageRequestHeaderUtils.h +35 -0
- package/ios/utils/LastElementUtils.h +1 -0
- package/ios/utils/ListMarkerDrawer.m +29 -42
- package/ios/utils/MarkdownASTSerializer.m +4 -0
- package/ios/utils/ParagraphStyleUtils.h +5 -0
- package/ios/utils/ParagraphStyleUtils.m +77 -17
- package/ios/utils/RenderedMarkdownSegment.h +13 -1
- package/ios/utils/RenderedMarkdownSegment.m +20 -0
- package/ios/utils/SegmentRenderer.m +17 -4
- package/ios/utils/StreamingMarkdownFilter.h +11 -1
- package/ios/utils/StreamingMarkdownFilter.m +98 -2
- package/ios/utils/StylePropsUtils.h +63 -0
- package/ios/utils/TaskListTapUtils.m +24 -0
- package/ios/views/ENRMCodeBlockContainerView.h +46 -0
- package/ios/views/ENRMCodeBlockContainerView.m +566 -0
- package/ios/views/ENRMMathContainerView.h +12 -0
- package/ios/views/ENRMMathContainerView.m +71 -1
- package/ios/views/TableContainerView.h +18 -0
- package/ios/views/TableContainerView.m +259 -159
- package/jest.d.ts +1 -0
- package/jest.js +5 -0
- package/lib/module/EnrichedMarkdownNativeComponent.ts +65 -0
- package/lib/module/EnrichedMarkdownTextInput.js +84 -5
- package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
- package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +66 -0
- package/lib/module/headingDefaults.js +34 -0
- package/lib/module/headingDefaults.js.map +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/jest/index.js +191 -0
- package/lib/module/jest/index.js.map +1 -0
- package/lib/module/native/EnrichedMarkdownText.js +30 -4
- package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.js +81 -19
- package/lib/module/normalizeMarkdownStyle.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.web.js +56 -18
- package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
- package/lib/module/normalizeMarkdownTextInputStyle.js +62 -15
- package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
- package/lib/module/normalizeMenuItem.js +1 -1
- package/lib/module/utils/textInputState.js +26 -0
- package/lib/module/utils/textInputState.js.map +1 -0
- package/lib/module/web/EnrichedMarkdownText.js +9 -5
- package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/web/parseMarkdown.js +4 -3
- package/lib/module/web/parseMarkdown.js.map +1 -1
- package/lib/module/web/renderers/InlineRenderers.js +6 -0
- package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
- package/lib/module/web/renderers/ListRenderers.js +7 -2
- package/lib/module/web/renderers/ListRenderers.js.map +1 -1
- package/lib/module/web/renderers/index.js +7 -4
- package/lib/module/web/renderers/index.js.map +1 -1
- package/lib/module/web/styles.js +83 -6
- package/lib/module/web/styles.js.map +1 -1
- package/lib/module/web/utils.js +1 -0
- package/lib/module/web/utils.js.map +1 -1
- package/lib/module/web/wasm/md4c.js +0 -0
- package/lib/module/web/wasm/md4c.js.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +62 -0
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +94 -9
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +69 -5
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +62 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/headingDefaults.d.ts +9 -0
- package/lib/typescript/src/headingDefaults.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.web.d.ts +1 -1
- package/lib/typescript/src/index.web.d.ts.map +1 -1
- package/lib/typescript/src/jest/index.d.ts +6 -0
- package/lib/typescript/src/jest/index.d.ts.map +1 -0
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +3 -3
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts +19 -5
- package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyle.d.ts +72 -0
- package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +27 -0
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts +65 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +19 -0
- package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
- package/lib/typescript/src/types/events.d.ts +19 -0
- package/lib/typescript/src/types/events.d.ts.map +1 -1
- package/lib/typescript/src/utils/textInputState.d.ts +24 -0
- package/lib/typescript/src/utils/textInputState.d.ts.map +1 -0
- package/lib/typescript/src/web/EnrichedMarkdownText.d.ts +1 -1
- package/lib/typescript/src/web/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/web/parseMarkdown.d.ts +1 -1
- package/lib/typescript/src/web/parseMarkdown.d.ts.map +1 -1
- package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
- package/lib/typescript/src/web/renderers/ListRenderers.d.ts.map +1 -1
- package/lib/typescript/src/web/renderers/index.d.ts +2 -1
- package/lib/typescript/src/web/renderers/index.d.ts.map +1 -1
- package/lib/typescript/src/web/styles.d.ts +2 -1
- package/lib/typescript/src/web/styles.d.ts.map +1 -1
- package/lib/typescript/src/web/types.d.ts +6 -2
- package/lib/typescript/src/web/types.d.ts.map +1 -1
- package/lib/typescript/src/web/utils.d.ts.map +1 -1
- package/package.json +36 -9
- package/postinstall.mjs +131 -0
- package/ratex-version.json +21 -0
- package/src/EnrichedMarkdownNativeComponent.ts +65 -0
- package/src/EnrichedMarkdownTextInput.tsx +159 -8
- package/src/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
- package/src/EnrichedMarkdownTextNativeComponent.ts +66 -0
- package/src/headingDefaults.ts +21 -0
- package/src/index.tsx +9 -0
- package/src/index.web.tsx +1 -0
- package/src/jest/index.tsx +215 -0
- package/src/native/EnrichedMarkdownText.tsx +40 -2
- package/src/normalizeMarkdownStyle.ts +102 -16
- package/src/normalizeMarkdownStyle.web.ts +63 -15
- package/src/normalizeMarkdownTextInputStyle.ts +110 -35
- package/src/normalizeMenuItem.ts +1 -1
- package/src/types/MarkdownStyle.ts +74 -0
- package/src/types/MarkdownStyleInternal.ts +39 -0
- package/src/types/MarkdownTextProps.ts +65 -0
- package/src/types/MarkdownTextProps.web.ts +19 -0
- package/src/types/events.ts +21 -0
- package/src/utils/textInputState.ts +35 -0
- package/src/web/EnrichedMarkdownText.tsx +16 -2
- package/src/web/parseMarkdown.ts +6 -2
- package/src/web/renderers/InlineRenderers.tsx +5 -0
- package/src/web/renderers/ListRenderers.tsx +14 -2
- package/src/web/renderers/index.tsx +6 -2
- package/src/web/styles.ts +89 -5
- package/src/web/types.ts +6 -1
- package/src/web/utils.ts +1 -0
- package/src/web/wasm/md4c.js +0 -0
- package/vendor-grammars.mjs +511 -0
- package/vendor-ratex.mjs +173 -0
- package/app.plugin.js +0 -2
- package/plugin/build/withAndroidMath.js +0 -19
- package/plugin/build/withIosMath.js +0 -28
- package/plugin/build/withReactNativeEnrichedMarkdown.js +0 -11
- package/plugin/src/withAndroidMath.ts +0 -24
- package/plugin/src/withIosMath.ts +0 -35
- package/plugin/src/withReactNativeEnrichedMarkdown.ts +0 -17
- package/plugin/tsconfig.build.json +0 -16
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
#import "ENRMInputParser.h"
|
|
2
2
|
#import "ENRMBlockSyntaxSentinel.h"
|
|
3
|
+
#import "ENRMFeatureFlags.h"
|
|
3
4
|
#import "ENRMFormattingRange.h"
|
|
4
5
|
#import "ENRMInputRemend.h"
|
|
5
6
|
#include "md4c.h"
|
|
7
|
+
#include <algorithm>
|
|
6
8
|
#include <string>
|
|
7
9
|
#include <vector>
|
|
8
10
|
|
|
9
11
|
@interface ENRMParseResult ()
|
|
10
12
|
@property (nonatomic, strong, readwrite) NSString *plainText;
|
|
11
13
|
@property (nonatomic, strong, readwrite) NSArray<ENRMFormattingRange *> *formattingRanges;
|
|
14
|
+
@property (nonatomic, strong, readwrite) NSArray<ENRMBlockRange *> *blockRanges;
|
|
12
15
|
@property (nonatomic, strong, readwrite) NSArray<ENRMMathAtom *> *mathAtoms;
|
|
13
16
|
@end
|
|
14
17
|
|
|
@@ -20,9 +23,6 @@
|
|
|
20
23
|
- (ENRMParseResult *)parseToPlainTextAndRanges:(NSString *)markdown remend:(BOOL)remend;
|
|
21
24
|
@end
|
|
22
25
|
|
|
23
|
-
@implementation ENRMMathAtom
|
|
24
|
-
@end
|
|
25
|
-
|
|
26
26
|
namespace {
|
|
27
27
|
|
|
28
28
|
static const size_t kByteOffsetUnset = SIZE_MAX;
|
|
@@ -50,6 +50,46 @@ static bool isSupportedSpan(MD_SPANTYPE md4cType, ENRMInputStyleType &outStyleTy
|
|
|
50
50
|
return false;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
// Block-type mapping mirrors kSupportedSpans for the inline pipeline. A block
|
|
54
|
+
// handler extends recognition by adding its md4c block here and, if leveled,
|
|
55
|
+
// reading its detail in resolveBlockLevel below. MD_BLOCK_P maps to the
|
|
56
|
+
// implicit Paragraph default and produces no stored block range.
|
|
57
|
+
struct BlockTypeMapping {
|
|
58
|
+
MD_BLOCKTYPE md4cType;
|
|
59
|
+
ENRMInputBlockType blockType;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
static const BlockTypeMapping kSupportedBlocks[] = {
|
|
63
|
+
{MD_BLOCK_P, ENRMInputBlockTypeParagraph},
|
|
64
|
+
// MD_BLOCK_H maps to a representative heading type; onEnterBlock resolves the
|
|
65
|
+
// level (via resolveBlockLevel) and rewrites the type to the level-specific
|
|
66
|
+
// ENRMInputBlockTypeHeadingN.
|
|
67
|
+
{MD_BLOCK_H, ENRMInputBlockTypeHeading1},
|
|
68
|
+
};
|
|
69
|
+
static const size_t kSupportedBlockCount = sizeof(kSupportedBlocks) / sizeof(kSupportedBlocks[0]);
|
|
70
|
+
|
|
71
|
+
static bool isSupportedBlock(MD_BLOCKTYPE md4cType, ENRMInputBlockType &outBlockType)
|
|
72
|
+
{
|
|
73
|
+
for (size_t index = 0; index < kSupportedBlockCount; index++) {
|
|
74
|
+
if (kSupportedBlocks[index].md4cType == md4cType) {
|
|
75
|
+
outBlockType = kSupportedBlocks[index].blockType;
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Per-block integer payload (heading level, list depth). md4c exposes this in
|
|
83
|
+
// the block's MD_BLOCK_*_DETAIL struct. Headings read MD_BLOCK_H_DETAIL.level
|
|
84
|
+
// (1-6); other blocks have no level and return 0.
|
|
85
|
+
static NSInteger resolveBlockLevel(MD_BLOCKTYPE blockType, void *detail)
|
|
86
|
+
{
|
|
87
|
+
if (blockType == MD_BLOCK_H && detail) {
|
|
88
|
+
return (NSInteger)(static_cast<MD_BLOCK_H_DETAIL *>(detail)->level);
|
|
89
|
+
}
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
53
93
|
struct InlineSpanInfo {
|
|
54
94
|
ENRMInputStyleType type;
|
|
55
95
|
size_t openingDelimiterByteOffset;
|
|
@@ -58,11 +98,38 @@ struct InlineSpanInfo {
|
|
|
58
98
|
std::string linkURL;
|
|
59
99
|
};
|
|
60
100
|
|
|
101
|
+
struct BlockInfo {
|
|
102
|
+
ENRMInputBlockType type;
|
|
103
|
+
NSInteger level;
|
|
104
|
+
size_t contentStartByteOffset = kByteOffsetUnset;
|
|
105
|
+
size_t contentEndByteOffset = kByteOffsetUnset;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// A `$...$` / `$$...$$` run. Unlike an inline span, math is not styled text: the
|
|
109
|
+
// whole formula collapses to one atomic character in the plain text, so only the
|
|
110
|
+
// raw syntax extent and the LaTeX source matter. The extent is derived from the
|
|
111
|
+
// content bytes plus the delimiter width rather than from `lastTextEnd`, which
|
|
112
|
+
// would be wrong for math opening a span (e.g. "**$x$**", where no text has been
|
|
113
|
+
// emitted since the bold marker).
|
|
61
114
|
struct MathSpanInfo {
|
|
62
115
|
bool displayMode;
|
|
63
|
-
size_t
|
|
116
|
+
size_t contentStartByteOffset = kByteOffsetUnset;
|
|
64
117
|
size_t contentEndByteOffset = kByteOffsetUnset;
|
|
65
118
|
std::string latex;
|
|
119
|
+
|
|
120
|
+
size_t delimiterLength() const
|
|
121
|
+
{
|
|
122
|
+
return displayMode ? 2 : 1;
|
|
123
|
+
}
|
|
124
|
+
size_t syntaxStartByteOffset() const
|
|
125
|
+
{
|
|
126
|
+
size_t delimiter = delimiterLength();
|
|
127
|
+
return contentStartByteOffset >= delimiter ? contentStartByteOffset - delimiter : 0;
|
|
128
|
+
}
|
|
129
|
+
size_t syntaxEndByteOffset() const
|
|
130
|
+
{
|
|
131
|
+
return contentEndByteOffset + delimiterLength();
|
|
132
|
+
}
|
|
66
133
|
};
|
|
67
134
|
|
|
68
135
|
struct ParseContext {
|
|
@@ -71,9 +138,16 @@ struct ParseContext {
|
|
|
71
138
|
size_t originalLength;
|
|
72
139
|
std::vector<InlineSpanInfo> openStack;
|
|
73
140
|
std::vector<InlineSpanInfo> resolved;
|
|
141
|
+
std::vector<BlockInfo> openBlockStack;
|
|
142
|
+
std::vector<BlockInfo> resolvedBlocks;
|
|
74
143
|
std::vector<MathSpanInfo> openMathStack;
|
|
75
144
|
std::vector<MathSpanInfo> resolvedMath;
|
|
145
|
+
std::vector<size_t> textStartOffsets;
|
|
76
146
|
size_t lastTextEnd = 0;
|
|
147
|
+
// Open list containers, innermost last (true = ordered). An item's depth is
|
|
148
|
+
// the stack size - 1 and its type the innermost container's; md4c carries no
|
|
149
|
+
// per-item depth, so both derive from the open UL/OL ancestors.
|
|
150
|
+
std::vector<bool> listContainerStack;
|
|
77
151
|
};
|
|
78
152
|
|
|
79
153
|
static std::vector<NSUInteger> buildByteToUTF16Map(const char *utf8, size_t byteLength)
|
|
@@ -114,55 +188,119 @@ static inline NSUInteger mapByteOffset(const std::vector<NSUInteger> &map, size_
|
|
|
114
188
|
return map[std::min(offset, maxOffset)];
|
|
115
189
|
}
|
|
116
190
|
|
|
117
|
-
static const
|
|
118
|
-
[ENRMInputStyleTypeStrong] = 2, [ENRMInputStyleTypeEmphasis] = 1, [ENRMInputStyleTypeUnderline] = 1,
|
|
119
|
-
[ENRMInputStyleTypeStrikethrough] = 2, [ENRMInputStyleTypeSpoiler] = 2,
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
static size_t closingDelimiterEndByte(const InlineSpanInfo &span, const char *utf8, size_t bufferLength)
|
|
191
|
+
static size_t closingDelimiterEndByte(const ParseContext &context, const InlineSpanInfo &span)
|
|
123
192
|
{
|
|
124
193
|
size_t position = span.contentEndByteOffset;
|
|
125
194
|
|
|
126
195
|
if (span.type == ENRMInputStyleTypeLink) {
|
|
127
|
-
while (position < bufferLength &&
|
|
196
|
+
while (position < context.bufferLength && context.buffer[position] != ')') {
|
|
128
197
|
position++;
|
|
129
198
|
}
|
|
130
|
-
return (position < bufferLength) ? position + 1 : position;
|
|
199
|
+
return (position < context.bufferLength) ? position + 1 : position;
|
|
131
200
|
}
|
|
132
201
|
|
|
133
|
-
|
|
202
|
+
// Markdown delimiter bytes occupy the gap between content end and the next emitted text run
|
|
203
|
+
// (md4c never emits syntax bytes as text). Anchoring here — rather than adding a
|
|
204
|
+
// fixed per-type length — keeps stacked closers of co-terminating spans (e.g.
|
|
205
|
+
// "***", "**~~x~~**") from overlapping and leaking a trailing marker.
|
|
206
|
+
auto next = std::lower_bound(context.textStartOffsets.begin(), context.textStartOffsets.end(), position);
|
|
207
|
+
return (next != context.textStartOffsets.end()) ? *next : context.bufferLength;
|
|
134
208
|
}
|
|
135
209
|
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
// if those elements are added in the future.
|
|
143
|
-
//
|
|
144
|
-
// A proper fix would advance lastTextEnd here (or retroactively adjust
|
|
145
|
-
// openingDelimiterByteOffset for spans opened since the last block
|
|
146
|
-
// transition). md4c's enter_block doesn't provide a byte offset, so this
|
|
147
|
-
// likely requires tracking a "new block" flag and correcting span offsets
|
|
148
|
-
// in onText when the first text inside a new block arrives.
|
|
149
|
-
static int onEnterBlock(MD_BLOCKTYPE, void *, void *)
|
|
210
|
+
// Block tracking. md4c's enter_block gives no byte offset, so a block's content
|
|
211
|
+
// range is bounded by the text spans it encloses: onText records the first/last
|
|
212
|
+
// text offset into the currently open block(s). On leave_block the block is
|
|
213
|
+
// resolved with whatever text range it accumulated; empty blocks (no text) are
|
|
214
|
+
// discarded later when building results.
|
|
215
|
+
static int onEnterBlock(MD_BLOCKTYPE blockType, void *detail, void *userdata)
|
|
150
216
|
{
|
|
217
|
+
auto *context = static_cast<ParseContext *>(userdata);
|
|
218
|
+
|
|
219
|
+
// List nesting is tracked by the container stack, not stored as its own
|
|
220
|
+
// block: the UL/OL container only frames the items that read it.
|
|
221
|
+
if (blockType == MD_BLOCK_UL || blockType == MD_BLOCK_OL) {
|
|
222
|
+
context->listContainerStack.push_back(blockType == MD_BLOCK_OL);
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Tag the item itself, not its inner paragraph: md4c emits MD_BLOCK_P inside
|
|
227
|
+
// items only for *loose* lists, so a tight list has no paragraph to tag. The
|
|
228
|
+
// item's range is clipped back to its own first line when building results.
|
|
229
|
+
if (blockType == MD_BLOCK_LI) {
|
|
230
|
+
BlockInfo blockInfo;
|
|
231
|
+
BOOL ordered = !context->listContainerStack.empty() && context->listContainerStack.back();
|
|
232
|
+
blockInfo.type = ordered ? ENRMInputBlockTypeOrderedListItem : ENRMInputBlockTypeUnorderedListItem;
|
|
233
|
+
// Clamp to the shared max depth (matches Android's parser): markdown can
|
|
234
|
+
// nest deeper than the editor represents, and downstream metadata passes
|
|
235
|
+
// size their counters by kENRMMaxListDepth.
|
|
236
|
+
blockInfo.level = context->listContainerStack.empty()
|
|
237
|
+
? 0
|
|
238
|
+
: MIN((NSInteger)context->listContainerStack.size() - 1, kENRMMaxListDepth);
|
|
239
|
+
context->openBlockStack.push_back(blockInfo);
|
|
240
|
+
return 0;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
ENRMInputBlockType mappedType;
|
|
244
|
+
if (!isSupportedBlock(blockType, mappedType)) {
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
BlockInfo blockInfo;
|
|
249
|
+
blockInfo.level = resolveBlockLevel(blockType, detail);
|
|
250
|
+
// Headings share one md4c block type but split into six ENRMInputBlockTypes by
|
|
251
|
+
// level; map the resolved level onto the concrete heading type.
|
|
252
|
+
blockInfo.type = (blockType == MD_BLOCK_H) ? ENRMBlockTypeForHeadingLevel(blockInfo.level) : mappedType;
|
|
253
|
+
context->openBlockStack.push_back(blockInfo);
|
|
151
254
|
return 0;
|
|
152
255
|
}
|
|
153
|
-
|
|
256
|
+
|
|
257
|
+
static int onLeaveBlock(MD_BLOCKTYPE blockType, void *, void *userdata)
|
|
154
258
|
{
|
|
259
|
+
auto *context = static_cast<ParseContext *>(userdata);
|
|
260
|
+
|
|
261
|
+
if (blockType == MD_BLOCK_UL || blockType == MD_BLOCK_OL) {
|
|
262
|
+
if (!context->listContainerStack.empty()) {
|
|
263
|
+
context->listContainerStack.pop_back();
|
|
264
|
+
}
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// List items are tagged in onEnterBlock (not via isSupportedBlock); resolve
|
|
269
|
+
// them here the same way as supported blocks.
|
|
270
|
+
if (blockType == MD_BLOCK_LI) {
|
|
271
|
+
if (!context->openBlockStack.empty()) {
|
|
272
|
+
context->resolvedBlocks.push_back(context->openBlockStack.back());
|
|
273
|
+
context->openBlockStack.pop_back();
|
|
274
|
+
}
|
|
275
|
+
return 0;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
ENRMInputBlockType mappedType;
|
|
279
|
+
if (!isSupportedBlock(blockType, mappedType)) {
|
|
280
|
+
return 0;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (context->openBlockStack.empty()) {
|
|
284
|
+
return 0;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
context->resolvedBlocks.push_back(context->openBlockStack.back());
|
|
288
|
+
context->openBlockStack.pop_back();
|
|
155
289
|
return 0;
|
|
156
290
|
}
|
|
157
291
|
|
|
292
|
+
static inline bool isMathSpan(MD_SPANTYPE spanType)
|
|
293
|
+
{
|
|
294
|
+
return spanType == MD_SPAN_LATEXMATH || spanType == MD_SPAN_LATEXMATH_DISPLAY;
|
|
295
|
+
}
|
|
296
|
+
|
|
158
297
|
static int onEnterSpan(MD_SPANTYPE spanType, void *detail, void *userdata)
|
|
159
298
|
{
|
|
160
299
|
auto *context = static_cast<ParseContext *>(userdata);
|
|
161
300
|
|
|
162
|
-
if (spanType
|
|
301
|
+
if (isMathSpan(spanType)) {
|
|
163
302
|
MathSpanInfo mathInfo;
|
|
164
303
|
mathInfo.displayMode = (spanType == MD_SPAN_LATEXMATH_DISPLAY);
|
|
165
|
-
mathInfo.openingDelimiterByteOffset = context->lastTextEnd;
|
|
166
304
|
context->openMathStack.push_back(mathInfo);
|
|
167
305
|
return 0;
|
|
168
306
|
}
|
|
@@ -191,7 +329,7 @@ static int onLeaveSpan(MD_SPANTYPE spanType, void *, void *userdata)
|
|
|
191
329
|
{
|
|
192
330
|
auto *context = static_cast<ParseContext *>(userdata);
|
|
193
331
|
|
|
194
|
-
if (spanType
|
|
332
|
+
if (isMathSpan(spanType)) {
|
|
195
333
|
if (context->openMathStack.empty()) {
|
|
196
334
|
return 0;
|
|
197
335
|
}
|
|
@@ -214,25 +352,56 @@ static int onLeaveSpan(MD_SPANTYPE spanType, void *, void *userdata)
|
|
|
214
352
|
return 0;
|
|
215
353
|
}
|
|
216
354
|
|
|
217
|
-
static int onText(MD_TEXTTYPE
|
|
355
|
+
static int onText(MD_TEXTTYPE, const MD_CHAR *text, MD_SIZE size, void *userdata)
|
|
218
356
|
{
|
|
219
357
|
if (!text || size == 0) {
|
|
220
358
|
return 0;
|
|
221
359
|
}
|
|
222
360
|
auto *context = static_cast<ParseContext *>(userdata);
|
|
223
361
|
|
|
224
|
-
// Inside
|
|
225
|
-
//
|
|
362
|
+
// Inside `$...$` md4c emits the LaTeX source as text. It is never document
|
|
363
|
+
// text — the whole formula becomes one placeholder character — so it is
|
|
364
|
+
// collected here and never appended to the plain text. What the enclosing
|
|
365
|
+
// span/block does see is the formula's full *syntax* extent (delimiters
|
|
366
|
+
// included): without that a heading holding only math ("# $x$") would resolve
|
|
367
|
+
// with no content range at all and lose its block prefix.
|
|
226
368
|
if (!context->openMathStack.empty()) {
|
|
227
|
-
(
|
|
228
|
-
|
|
229
|
-
|
|
369
|
+
MathSpanInfo &math = context->openMathStack.back();
|
|
370
|
+
math.latex.append(text, size);
|
|
371
|
+
|
|
230
372
|
bool insideBuffer = (text >= context->buffer && text < context->buffer + context->bufferLength);
|
|
231
|
-
if (insideBuffer) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
373
|
+
if (!insideBuffer) {
|
|
374
|
+
// A synthetic token (soft break inside display math) carries no offset;
|
|
375
|
+
// its characters still belong to the formula, collected above.
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
size_t textStart = text - context->buffer;
|
|
380
|
+
size_t textEnd = textStart + size;
|
|
381
|
+
bool isFirstChunk = (math.contentStartByteOffset == kByteOffsetUnset);
|
|
382
|
+
if (isFirstChunk) {
|
|
383
|
+
math.contentStartByteOffset = textStart;
|
|
384
|
+
}
|
|
385
|
+
math.contentEndByteOffset = textEnd;
|
|
386
|
+
|
|
387
|
+
size_t syntaxStart = math.syntaxStartByteOffset();
|
|
388
|
+
size_t syntaxEnd = math.syntaxEndByteOffset();
|
|
389
|
+
if (isFirstChunk) {
|
|
390
|
+
context->textStartOffsets.push_back(syntaxStart);
|
|
391
|
+
}
|
|
392
|
+
for (auto &openSpan : context->openStack) {
|
|
393
|
+
if (openSpan.contentStartByteOffset == kByteOffsetUnset) {
|
|
394
|
+
openSpan.contentStartByteOffset = syntaxStart;
|
|
395
|
+
}
|
|
396
|
+
openSpan.contentEndByteOffset = syntaxEnd;
|
|
235
397
|
}
|
|
398
|
+
for (auto &openBlock : context->openBlockStack) {
|
|
399
|
+
if (openBlock.contentStartByteOffset == kByteOffsetUnset) {
|
|
400
|
+
openBlock.contentStartByteOffset = syntaxStart;
|
|
401
|
+
}
|
|
402
|
+
openBlock.contentEndByteOffset = syntaxEnd;
|
|
403
|
+
}
|
|
404
|
+
context->lastTextEnd = syntaxEnd;
|
|
236
405
|
return 0;
|
|
237
406
|
}
|
|
238
407
|
|
|
@@ -240,10 +409,9 @@ static int onText(MD_TEXTTYPE textType, const MD_CHAR *text, MD_SIZE size, void
|
|
|
240
409
|
// (e.g. MD_TEXT_SOFTBR, MD_TEXT_BR use a string literal "\n").
|
|
241
410
|
// Pointer arithmetic against context->buffer would underflow, corrupting offsets.
|
|
242
411
|
//
|
|
243
|
-
//
|
|
244
|
-
// newlines.
|
|
245
|
-
//
|
|
246
|
-
// syntax range. See the related TODO above onEnterBlock.
|
|
412
|
+
// Skipping out-of-buffer tokens means lastTextEnd doesn't advance past
|
|
413
|
+
// synthetic newlines. Inline-span offsets stay correct because spans never
|
|
414
|
+
// straddle a synthetic break; block offsets are derived from real text below.
|
|
247
415
|
bool insideBuffer = (text >= context->buffer && text < context->buffer + context->bufferLength);
|
|
248
416
|
if (!insideBuffer) {
|
|
249
417
|
return 0;
|
|
@@ -251,6 +419,7 @@ static int onText(MD_TEXTTYPE textType, const MD_CHAR *text, MD_SIZE size, void
|
|
|
251
419
|
|
|
252
420
|
size_t textStart = text - context->buffer;
|
|
253
421
|
size_t textEnd = textStart + size;
|
|
422
|
+
context->textStartOffsets.push_back(textStart);
|
|
254
423
|
|
|
255
424
|
for (auto &openSpan : context->openStack) {
|
|
256
425
|
if (openSpan.contentStartByteOffset == kByteOffsetUnset) {
|
|
@@ -258,10 +427,26 @@ static int onText(MD_TEXTTYPE textType, const MD_CHAR *text, MD_SIZE size, void
|
|
|
258
427
|
}
|
|
259
428
|
openSpan.contentEndByteOffset = textEnd;
|
|
260
429
|
}
|
|
430
|
+
for (auto &openBlock : context->openBlockStack) {
|
|
431
|
+
if (openBlock.contentStartByteOffset == kByteOffsetUnset) {
|
|
432
|
+
openBlock.contentStartByteOffset = textStart;
|
|
433
|
+
}
|
|
434
|
+
openBlock.contentEndByteOffset = textEnd;
|
|
435
|
+
}
|
|
261
436
|
context->lastTextEnd = textEnd;
|
|
262
437
|
return 0;
|
|
263
438
|
}
|
|
264
439
|
|
|
440
|
+
// The one place math recognition is switched off in a math-disabled build: with
|
|
441
|
+
// the flag clear md4c never opens a LATEXMATH span, so no atom is produced and
|
|
442
|
+
// "$x$" stays literal text — mirroring Android, where `Md4cFlags.latexMath`
|
|
443
|
+
// defaults from `FeatureFlags.IS_MATH_ENABLED`.
|
|
444
|
+
#if ENRICHED_MARKDOWN_MATH
|
|
445
|
+
static const unsigned kMathSpanFlags = MD_FLAG_LATEXMATHSPANS;
|
|
446
|
+
#else
|
|
447
|
+
static const unsigned kMathSpanFlags = 0;
|
|
448
|
+
#endif
|
|
449
|
+
|
|
265
450
|
static bool runMd4cParse(NSString *markdown, ParseContext &context, bool remend)
|
|
266
451
|
{
|
|
267
452
|
NSString *completed = remend ? ENRMInputRemendComplete(markdown) : markdown;
|
|
@@ -275,7 +460,7 @@ static bool runMd4cParse(NSString *markdown, ParseContext &context, bool remend)
|
|
|
275
460
|
|
|
276
461
|
MD_PARSER parser = {
|
|
277
462
|
.abi_version = 0,
|
|
278
|
-
.flags = MD_FLAG_NOHTML | MD_FLAG_UNDERLINE | MD_FLAG_STRIKETHROUGH | MD_FLAG_SPOILERS |
|
|
463
|
+
.flags = MD_FLAG_NOHTML | MD_FLAG_UNDERLINE | MD_FLAG_STRIKETHROUGH | MD_FLAG_SPOILERS | kMathSpanFlags,
|
|
279
464
|
.enter_block = onEnterBlock,
|
|
280
465
|
.leave_block = onLeaveBlock,
|
|
281
466
|
.enter_span = onEnterSpan,
|
|
@@ -288,25 +473,12 @@ static bool runMd4cParse(NSString *markdown, ParseContext &context, bool remend)
|
|
|
288
473
|
return md_parse(completedUTF8, (MD_SIZE)completedLength, &parser, &context) == 0;
|
|
289
474
|
}
|
|
290
475
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
476
|
+
// Builds inline styled ranges (raw-markdown UTF-16 coords) from a completed
|
|
477
|
+
// parse. Split out so parseToPlainTextAndRanges: can derive inline and block
|
|
478
|
+
// ranges from ONE md4c run instead of parsing twice.
|
|
479
|
+
static NSArray<ENRMInputStyledRange *> *styledRangesFromContext(const ParseContext &context,
|
|
480
|
+
const std::vector<NSUInteger> &byteMap)
|
|
296
481
|
{
|
|
297
|
-
if (markdown.length == 0) {
|
|
298
|
-
return @[];
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
ParseContext context;
|
|
302
|
-
if (!runMd4cParse(markdown, context, remend)) {
|
|
303
|
-
return @[];
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
out.mathSpans = context.resolvedMath;
|
|
307
|
-
|
|
308
|
-
auto byteMap = buildByteToUTF16Map(context.buffer, context.bufferLength);
|
|
309
|
-
|
|
310
482
|
NSMutableArray<ENRMInputStyledRange *> *results = [NSMutableArray arrayWithCapacity:context.resolved.size()];
|
|
311
483
|
|
|
312
484
|
for (const auto &spanInfo : context.resolved) {
|
|
@@ -329,8 +501,7 @@ static NSArray<ENRMInputStyledRange *> *runParseInternal(NSString *markdown, Par
|
|
|
329
501
|
NSUInteger openStart = mapByteOffset(byteMap, spanInfo.openingDelimiterByteOffset, context.bufferLength);
|
|
330
502
|
NSRange openingRange = NSMakeRange(openStart, contentStart - openStart);
|
|
331
503
|
|
|
332
|
-
size_t closingEndByte =
|
|
333
|
-
std::min(closingDelimiterEndByte(spanInfo, context.buffer, context.bufferLength), context.bufferLength);
|
|
504
|
+
size_t closingEndByte = std::min(closingDelimiterEndByte(context, spanInfo), context.bufferLength);
|
|
334
505
|
NSUInteger closingStart = mapByteOffset(byteMap, spanInfo.contentEndByteOffset, context.bufferLength);
|
|
335
506
|
NSUInteger closingEnd = mapByteOffset(byteMap, closingEndByte, context.bufferLength);
|
|
336
507
|
NSRange closingRange = NSMakeRange(closingStart, closingEnd - closingStart);
|
|
@@ -354,14 +525,121 @@ static NSArray<ENRMInputStyledRange *> *runParseInternal(NSString *markdown, Par
|
|
|
354
525
|
return results;
|
|
355
526
|
}
|
|
356
527
|
|
|
528
|
+
// Builds block-level ranges (raw-markdown UTF-16 coords) from the same
|
|
529
|
+
// completed parse, mirroring styledRangesFromContext for inline spans.
|
|
530
|
+
// Paragraph blocks (the implicit default) are omitted — only blocks a handler
|
|
531
|
+
// claims are returned.
|
|
532
|
+
static NSArray<ENRMBlockRange *> *blockRangesFromContext(const ParseContext &context,
|
|
533
|
+
const std::vector<NSUInteger> &byteMap, NSString *markdown)
|
|
534
|
+
{
|
|
535
|
+
NSMutableArray<ENRMBlockRange *> *results = [NSMutableArray arrayWithCapacity:context.resolvedBlocks.size()];
|
|
536
|
+
|
|
537
|
+
for (const auto &blockInfo : context.resolvedBlocks) {
|
|
538
|
+
if (blockInfo.type == ENRMInputBlockTypeParagraph) {
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
if (blockInfo.contentStartByteOffset == kByteOffsetUnset || blockInfo.contentEndByteOffset == kByteOffsetUnset ||
|
|
542
|
+
blockInfo.contentStartByteOffset > context.originalLength) {
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
NSUInteger contentStart = mapByteOffset(byteMap, blockInfo.contentStartByteOffset, context.bufferLength);
|
|
547
|
+
NSUInteger contentEnd = mapByteOffset(byteMap, blockInfo.contentEndByteOffset, context.bufferLength);
|
|
548
|
+
if (contentEnd <= contentStart) {
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// A list item accumulates all text in its subtree, so a parent item's range
|
|
553
|
+
// runs through its nested sublist. Clip each item to its own first line so
|
|
554
|
+
// nested items keep their own (deeper) depth rather than being overwritten
|
|
555
|
+
// by the parent's range. Input list items are single-line.
|
|
556
|
+
if (ENRMBlockTypeIsListItem(blockInfo.type) && contentEnd <= markdown.length) {
|
|
557
|
+
NSRange newline = [markdown rangeOfString:@"\n"
|
|
558
|
+
options:0
|
|
559
|
+
range:NSMakeRange(contentStart, contentEnd - contentStart)];
|
|
560
|
+
if (newline.location != NSNotFound) {
|
|
561
|
+
contentEnd = newline.location;
|
|
562
|
+
}
|
|
563
|
+
if (contentEnd <= contentStart) {
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
[results addObject:[ENRMBlockRange rangeWithType:blockInfo.type
|
|
569
|
+
range:NSMakeRange(contentStart, contentEnd - contentStart)
|
|
570
|
+
level:blockInfo.level]];
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return results;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// A resolved math chip in raw-markdown UTF-16 coords: the full `$...$` extent to
|
|
577
|
+
// replace, plus what to replace it with.
|
|
578
|
+
struct MathChip {
|
|
579
|
+
NSRange rawRange;
|
|
580
|
+
NSString *latex;
|
|
581
|
+
BOOL displayMode;
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
// Builds math chips from the same completed parse, mirroring the two functions
|
|
585
|
+
// above. A formula whose closing delimiter exists only because ENRMInputRemend
|
|
586
|
+
// completed the buffer is dropped: while "$x" is still being typed it must stay
|
|
587
|
+
// literal text, the same way an unclosed "**" does (see `isComplete`).
|
|
588
|
+
static std::vector<MathChip> mathChipsFromContext(const ParseContext &context, const std::vector<NSUInteger> &byteMap,
|
|
589
|
+
NSUInteger rawLength)
|
|
590
|
+
{
|
|
591
|
+
std::vector<MathChip> chips;
|
|
592
|
+
chips.reserve(context.resolvedMath.size());
|
|
593
|
+
|
|
594
|
+
for (const auto &math : context.resolvedMath) {
|
|
595
|
+
// No content bytes at all ("$$"): not a formula, leave it as text.
|
|
596
|
+
if (math.contentStartByteOffset == kByteOffsetUnset || math.contentEndByteOffset == kByteOffsetUnset) {
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
size_t syntaxEnd = math.syntaxEndByteOffset();
|
|
600
|
+
if (syntaxEnd > context.originalLength) {
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
NSUInteger start = mapByteOffset(byteMap, math.syntaxStartByteOffset(), context.bufferLength);
|
|
605
|
+
NSUInteger end = mapByteOffset(byteMap, syntaxEnd, context.bufferLength);
|
|
606
|
+
if (end <= start || end > rawLength) {
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
MathChip chip;
|
|
611
|
+
chip.rawRange = NSMakeRange(start, end - start);
|
|
612
|
+
chip.latex = [[NSString alloc] initWithBytes:math.latex.data()
|
|
613
|
+
length:math.latex.size()
|
|
614
|
+
encoding:NSUTF8StringEncoding]
|
|
615
|
+
?: @"";
|
|
616
|
+
chip.displayMode = math.displayMode ? YES : NO;
|
|
617
|
+
chips.push_back(chip);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
std::sort(chips.begin(), chips.end(), [](const MathChip &first, const MathChip &second) {
|
|
621
|
+
return first.rawRange.location < second.rawRange.location;
|
|
622
|
+
});
|
|
623
|
+
return chips;
|
|
624
|
+
}
|
|
625
|
+
|
|
357
626
|
} // namespace
|
|
358
627
|
|
|
359
628
|
@implementation ENRMInputParser
|
|
360
629
|
|
|
361
630
|
- (NSArray<ENRMInputStyledRange *> *)parse:(NSString *)markdown
|
|
362
631
|
{
|
|
363
|
-
|
|
364
|
-
|
|
632
|
+
if (markdown.length == 0) {
|
|
633
|
+
return @[];
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
ParseContext context;
|
|
637
|
+
if (!runMd4cParse(markdown, context, /* remend */ true)) {
|
|
638
|
+
return @[];
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
auto byteMap = buildByteToUTF16Map(context.buffer, context.bufferLength);
|
|
642
|
+
return styledRangesFromContext(context, byteMap);
|
|
365
643
|
}
|
|
366
644
|
|
|
367
645
|
- (ENRMParseResult *)parseToPlainTextAndRanges:(NSString *)markdown
|
|
@@ -375,13 +653,19 @@ static NSArray<ENRMInputStyledRange *> *runParseInternal(NSString *markdown, Par
|
|
|
375
653
|
return [self parseToPlainTextAndRanges:markdown];
|
|
376
654
|
}
|
|
377
655
|
|
|
656
|
+
ENRMParseResult *result = [[ENRMParseResult alloc] init];
|
|
657
|
+
// Block ranges stay empty on every path below: with block syntax off there are
|
|
658
|
+
// no blocks. Under the sentinel md4c sees one paragraph, which yields no range, so
|
|
659
|
+
// this is a guard rather than a fix — a range that did leak would reach the block
|
|
660
|
+
// store and the serializer would prefix it, breaking the byte-for-byte round trip.
|
|
661
|
+
result.blockRanges = @[];
|
|
662
|
+
|
|
378
663
|
NSString *canonical = ENRMBlockSyntaxCanonicalize(markdown);
|
|
379
664
|
if (canonical.length == 0) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
return empty;
|
|
665
|
+
result.plainText = @"";
|
|
666
|
+
result.formattingRanges = @[];
|
|
667
|
+
result.mathAtoms = @[];
|
|
668
|
+
return result;
|
|
385
669
|
}
|
|
386
670
|
|
|
387
671
|
// Remending is skipped too: its speculative closing delimiters are the other
|
|
@@ -389,8 +673,6 @@ static NSArray<ENRMInputStyledRange *> *runParseInternal(NSString *markdown, Par
|
|
|
389
673
|
ENRMParseResult *encoded = [self parseToPlainTextAndRanges:ENRMBlockSyntaxEncode(canonical) remend:NO];
|
|
390
674
|
|
|
391
675
|
NSString *decoded = ENRMBlockSyntaxDecode(encoded.plainText);
|
|
392
|
-
ENRMParseResult *result = [[ENRMParseResult alloc] init];
|
|
393
|
-
|
|
394
676
|
if (decoded == nil) {
|
|
395
677
|
result.plainText = canonical;
|
|
396
678
|
result.formattingRanges = @[];
|
|
@@ -438,58 +720,26 @@ static NSArray<ENRMInputStyledRange *> *runParseInternal(NSString *markdown, Par
|
|
|
438
720
|
if (markdown.length == 0) {
|
|
439
721
|
parseResult.plainText = @"";
|
|
440
722
|
parseResult.formattingRanges = @[];
|
|
723
|
+
parseResult.blockRanges = @[];
|
|
441
724
|
parseResult.mathAtoms = @[];
|
|
442
725
|
return parseResult;
|
|
443
726
|
}
|
|
444
727
|
|
|
445
|
-
|
|
446
|
-
NSArray<ENRMInputStyledRange *> *styledRanges = runParseInternal(markdown, parsedSpans, remend);
|
|
447
|
-
|
|
448
|
-
// Compute byte map + raw byte ranges for math spans so we can strip the
|
|
449
|
-
// entire `$...$` (or `$$...$$`) syntax and inject a single `` at each.
|
|
450
|
-
size_t rawByteLength = [markdown lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
|
451
|
-
const char *rawUTF8 = [markdown UTF8String];
|
|
452
|
-
std::vector<NSUInteger> rawByteMap = buildByteToUTF16Map(rawUTF8, rawByteLength);
|
|
728
|
+
NSUInteger rawLength = markdown.length;
|
|
453
729
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
730
|
+
// One md4c run feeds all three pipelines: inline styled ranges, block ranges
|
|
731
|
+
// and math chips are derived from the same ParseContext.
|
|
732
|
+
ParseContext context;
|
|
733
|
+
NSArray<ENRMInputStyledRange *> *styledRanges = @[];
|
|
734
|
+
NSArray<ENRMBlockRange *> *rawBlockRanges = @[];
|
|
459
735
|
std::vector<MathChip> mathChips;
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
if (closingByteEnd > rawByteLength)
|
|
466
|
-
closingByteEnd = rawByteLength;
|
|
467
|
-
if (mathInfo.openingDelimiterByteOffset > rawByteLength)
|
|
468
|
-
continue;
|
|
469
|
-
if (closingByteEnd > rawByteLength)
|
|
470
|
-
continue;
|
|
471
|
-
|
|
472
|
-
NSUInteger rawStart = rawByteMap[mathInfo.openingDelimiterByteOffset];
|
|
473
|
-
NSUInteger rawEnd = rawByteMap[closingByteEnd];
|
|
474
|
-
if (rawEnd <= rawStart)
|
|
475
|
-
continue;
|
|
476
|
-
|
|
477
|
-
MathChip chip;
|
|
478
|
-
chip.rawSyntaxRange = NSMakeRange(rawStart, rawEnd - rawStart);
|
|
479
|
-
chip.latex = [[NSString alloc] initWithBytes:mathInfo.latex.data()
|
|
480
|
-
length:mathInfo.latex.size()
|
|
481
|
-
encoding:NSUTF8StringEncoding]
|
|
482
|
-
?: @"";
|
|
483
|
-
chip.displayMode = mathInfo.displayMode ? YES : NO;
|
|
484
|
-
mathChips.push_back(chip);
|
|
736
|
+
if (runMd4cParse(markdown, context, remend)) {
|
|
737
|
+
auto byteMap = buildByteToUTF16Map(context.buffer, context.bufferLength);
|
|
738
|
+
styledRanges = styledRangesFromContext(context, byteMap);
|
|
739
|
+
rawBlockRanges = blockRangesFromContext(context, byteMap, markdown);
|
|
740
|
+
mathChips = mathChipsFromContext(context, byteMap, rawLength);
|
|
485
741
|
}
|
|
486
742
|
|
|
487
|
-
// Sort math chips by raw position so we process left-to-right.
|
|
488
|
-
std::sort(mathChips.begin(), mathChips.end(),
|
|
489
|
-
[](const MathChip &a, const MathChip &b) { return a.rawSyntaxRange.location < b.rawSyntaxRange.location; });
|
|
490
|
-
|
|
491
|
-
NSUInteger rawLength = markdown.length;
|
|
492
|
-
|
|
493
743
|
NSMutableIndexSet *syntaxIndexes = [NSMutableIndexSet indexSet];
|
|
494
744
|
for (ENRMInputStyledRange *styledRange in styledRanges) {
|
|
495
745
|
if (!styledRange.isComplete)
|
|
@@ -505,19 +755,28 @@ static NSArray<ENRMInputStyledRange *> *runParseInternal(NSString *markdown, Par
|
|
|
505
755
|
}
|
|
506
756
|
}
|
|
507
757
|
|
|
508
|
-
//
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
758
|
+
// Strip block markers (e.g. "# ") from plain text — same as inline delimiters.
|
|
759
|
+
// Without this the marker survives and the serializer doubles it ("# # ").
|
|
760
|
+
for (ENRMBlockRange *rawBlock in rawBlockRanges) {
|
|
761
|
+
NSUInteger contentStart = rawBlock.range.location;
|
|
762
|
+
if (contentStart == 0 || contentStart > rawLength) {
|
|
763
|
+
continue;
|
|
513
764
|
}
|
|
514
|
-
|
|
515
|
-
|
|
765
|
+
NSUInteger lineStart = contentStart;
|
|
766
|
+
while (lineStart > 0) {
|
|
767
|
+
unichar previous = [markdown characterAtIndex:lineStart - 1];
|
|
768
|
+
if (previous == '\n' || previous == '\r') {
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
lineStart--;
|
|
772
|
+
}
|
|
773
|
+
if (contentStart > lineStart) {
|
|
774
|
+
[syntaxIndexes addIndexesInRange:NSMakeRange(lineStart, contentStart - lineStart)];
|
|
516
775
|
}
|
|
517
776
|
}
|
|
518
777
|
|
|
519
778
|
// Strip \n/\r from syntax ranges — newlines are structural content, not
|
|
520
|
-
// markdown syntax
|
|
779
|
+
// markdown syntax, and must survive into the plain text.
|
|
521
780
|
NSMutableIndexSet *newlineIndexes = [NSMutableIndexSet indexSet];
|
|
522
781
|
[syntaxIndexes enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *stop) {
|
|
523
782
|
if (index < rawLength) {
|
|
@@ -529,32 +788,73 @@ static NSArray<ENRMInputStyledRange *> *runParseInternal(NSString *markdown, Par
|
|
|
529
788
|
}];
|
|
530
789
|
[syntaxIndexes removeIndexes:newlineIndexes];
|
|
531
790
|
|
|
791
|
+
// Build the plain text and the raw→plain map in one pass. Three kinds of raw
|
|
792
|
+
// index: a math chip's extent collapses to one placeholder character, a syntax
|
|
793
|
+
// index is dropped, everything else is copied verbatim.
|
|
794
|
+
//
|
|
795
|
+
// The math substitution happens *here*, not as an insertion afterwards, and
|
|
796
|
+
// that is the whole point: `rawToPlainMap` stays the single authority, so the
|
|
797
|
+
// inline ranges AND the block ranges below both map through it unchanged.
|
|
798
|
+
// Shifting only one of the two is the silent failure this ordering prevents —
|
|
799
|
+
// one placeholder is n raw characters, so every block boundary after a chip
|
|
800
|
+
// would land a line off and "# "/"- "/"1. " would prefix the wrong line.
|
|
532
801
|
NSMutableString *plainText = [NSMutableString stringWithCapacity:rawLength];
|
|
533
|
-
|
|
802
|
+
std::vector<NSUInteger> rawToPlainMap(rawLength + 1, 0);
|
|
803
|
+
NSMutableArray<ENRMMathAtom *> *mathAtoms = [NSMutableArray arrayWithCapacity:mathChips.size()];
|
|
804
|
+
|
|
805
|
+
NSUInteger plainPosition = 0;
|
|
806
|
+
NSUInteger verbatimStart = 0;
|
|
807
|
+
size_t nextChip = 0;
|
|
534
808
|
|
|
535
|
-
|
|
536
|
-
|
|
809
|
+
// Copies the pending verbatim run [from, to) into the plain text.
|
|
810
|
+
auto flushVerbatim = [&](NSUInteger from, NSUInteger to) {
|
|
811
|
+
if (to <= from) {
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
[plainText appendString:[markdown substringWithRange:NSMakeRange(from, to - from)]];
|
|
815
|
+
for (NSUInteger rawIndex = from; rawIndex < to; rawIndex++) {
|
|
816
|
+
rawToPlainMap[rawIndex] = plainPosition++;
|
|
817
|
+
}
|
|
818
|
+
};
|
|
537
819
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
820
|
+
NSUInteger rawIndex = 0;
|
|
821
|
+
while (rawIndex < rawLength) {
|
|
822
|
+
if (nextChip < mathChips.size() && mathChips[nextChip].rawRange.location == rawIndex) {
|
|
823
|
+
const MathChip &chip = mathChips[nextChip];
|
|
824
|
+
NSUInteger chipEnd = MIN(NSMaxRange(chip.rawRange), rawLength);
|
|
825
|
+
flushVerbatim(verbatimStart, rawIndex);
|
|
826
|
+
|
|
827
|
+
ENRMMathAtom *atom = [[ENRMMathAtom alloc] init];
|
|
828
|
+
atom.position = plainPosition;
|
|
829
|
+
atom.latex = chip.latex;
|
|
830
|
+
atom.displayMode = chip.displayMode;
|
|
831
|
+
[mathAtoms addObject:atom];
|
|
832
|
+
|
|
833
|
+
[plainText appendString:ENRMObjectReplacementString()];
|
|
834
|
+
// Every character of the formula — delimiters included — collapses onto the
|
|
835
|
+
// placeholder, so a range that starts inside it starts at the chip.
|
|
836
|
+
for (NSUInteger index = rawIndex; index < chipEnd; index++) {
|
|
837
|
+
rawToPlainMap[index] = plainPosition;
|
|
544
838
|
}
|
|
839
|
+
plainPosition++;
|
|
840
|
+
|
|
841
|
+
rawIndex = chipEnd;
|
|
842
|
+
verbatimStart = rawIndex;
|
|
843
|
+
nextChip++;
|
|
844
|
+
continue;
|
|
545
845
|
}
|
|
546
|
-
|
|
846
|
+
|
|
847
|
+
if ([syntaxIndexes containsIndex:rawIndex]) {
|
|
848
|
+
flushVerbatim(verbatimStart, rawIndex);
|
|
547
849
|
rawToPlainMap[rawIndex] = plainPosition;
|
|
850
|
+
rawIndex++;
|
|
851
|
+
verbatimStart = rawIndex;
|
|
852
|
+
continue;
|
|
548
853
|
}
|
|
549
|
-
previousEnd = NSMaxRange(syntaxRange);
|
|
550
|
-
}];
|
|
551
854
|
|
|
552
|
-
|
|
553
|
-
[plainText appendString:[markdown substringFromIndex:previousEnd]];
|
|
554
|
-
for (NSUInteger rawIndex = previousEnd; rawIndex < rawLength; rawIndex++) {
|
|
555
|
-
rawToPlainMap[rawIndex] = plainPosition++;
|
|
556
|
-
}
|
|
855
|
+
rawIndex++;
|
|
557
856
|
}
|
|
857
|
+
flushVerbatim(verbatimStart, rawLength);
|
|
558
858
|
rawToPlainMap[rawLength] = plainPosition;
|
|
559
859
|
|
|
560
860
|
NSMutableArray<ENRMFormattingRange *> *formattingRanges = [NSMutableArray arrayWithCapacity:styledRanges.count];
|
|
@@ -582,46 +882,29 @@ static NSArray<ENRMInputStyledRange *> *runParseInternal(NSString *markdown, Par
|
|
|
582
882
|
[formattingRanges addObject:formattingRange];
|
|
583
883
|
}
|
|
584
884
|
|
|
585
|
-
//
|
|
586
|
-
//
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
885
|
+
// Map block content ranges (raw-markdown coords) onto plain-text coords. The
|
|
886
|
+
// block's marker syntax was stripped above, so the content range maps cleanly
|
|
887
|
+
// onto the post-strip text and the block no longer covers the marker.
|
|
888
|
+
NSMutableArray<ENRMBlockRange *> *blockRanges = [NSMutableArray arrayWithCapacity:rawBlockRanges.count];
|
|
889
|
+
for (ENRMBlockRange *rawBlock in rawBlockRanges) {
|
|
890
|
+
NSUInteger contentStart = rawBlock.range.location;
|
|
891
|
+
NSUInteger contentEnd = NSMaxRange(rawBlock.range);
|
|
892
|
+
if (contentStart > rawLength || contentEnd > rawLength)
|
|
893
|
+
continue;
|
|
894
|
+
|
|
895
|
+
NSUInteger plainStart = rawToPlainMap[contentStart];
|
|
896
|
+
NSUInteger plainEnd = rawToPlainMap[contentEnd];
|
|
897
|
+
if (plainEnd <= plainStart)
|
|
594
898
|
continue;
|
|
595
|
-
NSUInteger basePlain = rawToPlainMap[rawStart];
|
|
596
|
-
NSUInteger insertAt = basePlain + insertionsSoFar;
|
|
597
|
-
if (insertAt > plainText.length)
|
|
598
|
-
insertAt = plainText.length;
|
|
599
|
-
[plainText insertString:attachmentString atIndex:insertAt];
|
|
600
|
-
|
|
601
|
-
ENRMMathAtom *atom = [[ENRMMathAtom alloc] init];
|
|
602
|
-
atom.position = insertAt;
|
|
603
|
-
atom.latex = chip.latex ?: @"";
|
|
604
|
-
atom.displayMode = chip.displayMode;
|
|
605
|
-
[mathAtoms addObject:atom];
|
|
606
|
-
|
|
607
|
-
// Shift existing formatting ranges that start at or after this insertion.
|
|
608
|
-
for (NSUInteger idx = 0; idx < formattingRanges.count; idx++) {
|
|
609
|
-
ENRMFormattingRange *fr = formattingRanges[idx];
|
|
610
|
-
NSRange r = fr.range;
|
|
611
|
-
if (r.location >= insertAt) {
|
|
612
|
-
r.location += 1;
|
|
613
|
-
formattingRanges[idx] = [ENRMFormattingRange rangeWithType:fr.type range:r url:fr.url];
|
|
614
|
-
} else if (NSMaxRange(r) > insertAt) {
|
|
615
|
-
r.length += 1;
|
|
616
|
-
formattingRanges[idx] = [ENRMFormattingRange rangeWithType:fr.type range:r url:fr.url];
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
899
|
|
|
620
|
-
|
|
900
|
+
[blockRanges addObject:[ENRMBlockRange rangeWithType:rawBlock.type
|
|
901
|
+
range:NSMakeRange(plainStart, plainEnd - plainStart)
|
|
902
|
+
level:rawBlock.level]];
|
|
621
903
|
}
|
|
622
904
|
|
|
623
905
|
parseResult.plainText = plainText;
|
|
624
906
|
parseResult.formattingRanges = formattingRanges;
|
|
907
|
+
parseResult.blockRanges = blockRanges;
|
|
625
908
|
parseResult.mathAtoms = mathAtoms;
|
|
626
909
|
return parseResult;
|
|
627
910
|
}
|