react-native-enriched-markdown-scaffold 0.7.4-scaffold.9 → 1.0.2-scaffold.2
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 +886 -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 +189 -13
- 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 +37 -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 +49 -15
- package/android/src/main/java/com/swmansion/enriched/markdown/views/BlockMenuOptions.kt +45 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +396 -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 +36 -9
- 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 +6 -4
- 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 +234 -12
- package/docs/BREAKING_CHANGES.md +129 -0
- package/docs/CODE_HIGHLIGHT.md +133 -0
- package/docs/COPY_OPTIONS.md +47 -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 +284 -75
- package/ios/EnrichedMarkdownText.h +7 -0
- package/ios/EnrichedMarkdownText.mm +101 -73
- 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 +886 -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/ENRMBlockMenu.h +57 -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.h +22 -5
- package/ios/utils/EditMenuUtils.m +53 -9
- 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 +53 -0
- package/ios/views/ENRMCodeBlockContainerView.m +559 -0
- package/ios/views/ENRMMathContainerView.h +16 -0
- package/ios/views/ENRMMathContainerView.m +74 -19
- package/ios/views/TableContainerView.h +22 -0
- package/ios/views/TableContainerView.m +261 -175
- package/jest.d.ts +1 -0
- package/jest.js +5 -0
- package/lib/module/EnrichedMarkdownNativeComponent.ts +73 -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 +74 -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 +37 -5
- 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 +66 -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 +66 -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 +104 -9
- 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 +73 -0
- package/src/EnrichedMarkdownTextInput.tsx +159 -8
- package/src/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
- package/src/EnrichedMarkdownTextNativeComponent.ts +74 -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 +53 -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 +101 -9
- 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
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.input.formatting
|
|
2
|
+
|
|
3
|
+
import com.swmansion.enriched.markdown.input.model.BlockRange
|
|
4
|
+
import com.swmansion.enriched.markdown.input.model.BlockType
|
|
5
|
+
import com.swmansion.enriched.markdown.input.model.MAX_LIST_DEPTH
|
|
6
|
+
import java.util.Collections
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Stores the block-level (paragraph-scoped) ranges for the editor, mirroring
|
|
10
|
+
* [FormattingStore]. Unlike inline ranges, block ranges never overlap: at most
|
|
11
|
+
* one block covers any given paragraph, and ranges are kept normalized to
|
|
12
|
+
* whole-line boundaries.
|
|
13
|
+
*/
|
|
14
|
+
class BlockStore {
|
|
15
|
+
private val ranges = mutableListOf<BlockRange>()
|
|
16
|
+
|
|
17
|
+
val allRanges: List<BlockRange> get() = Collections.unmodifiableList(ranges)
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Returns the block whose paragraph starts exactly at [lineStart], or null.
|
|
21
|
+
* [ranges] is kept sorted by `start`, so this is an O(log n) binary search —
|
|
22
|
+
* the single lookup the view funnels through instead of the linear
|
|
23
|
+
* `allRanges.firstOrNull { it.start == lineStart }` scans it used to repeat.
|
|
24
|
+
* Relies on the one-block-per-paragraph invariant (starts are unique).
|
|
25
|
+
*/
|
|
26
|
+
fun blockStartingAt(lineStart: Int): BlockRange? {
|
|
27
|
+
var lo = 0
|
|
28
|
+
var hi = ranges.size - 1
|
|
29
|
+
while (lo <= hi) {
|
|
30
|
+
val mid = (lo + hi) ushr 1
|
|
31
|
+
val start = ranges[mid].start
|
|
32
|
+
when {
|
|
33
|
+
start < lineStart -> lo = mid + 1
|
|
34
|
+
start > lineStart -> hi = mid - 1
|
|
35
|
+
else -> return ranges[mid]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Incoming ranges are trusted to be non-overlapping and line-scoped — the
|
|
43
|
+
* parser owns that invariant (md4c block structure never overlaps at the same
|
|
44
|
+
* nesting level, and nested containers are not yet mapped). Revisit
|
|
45
|
+
* enforcement here if a container block type (list, blockquote) is added.
|
|
46
|
+
*/
|
|
47
|
+
fun setRanges(newRanges: List<BlockRange>) {
|
|
48
|
+
ranges.clear()
|
|
49
|
+
ranges.addAll(newRanges.sortedBy { it.start })
|
|
50
|
+
recomputeListMetadata()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fun clearAll() {
|
|
54
|
+
ranges.clear()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Sets/replaces the block on every paragraph the given range touches, expanding
|
|
59
|
+
* to whole-line boundaries within [text]. Removes any block previously covering
|
|
60
|
+
* those paragraphs.
|
|
61
|
+
*/
|
|
62
|
+
fun setBlock(
|
|
63
|
+
type: BlockType,
|
|
64
|
+
level: Int,
|
|
65
|
+
paragraphStart: Int,
|
|
66
|
+
paragraphEnd: Int,
|
|
67
|
+
text: CharSequence,
|
|
68
|
+
) {
|
|
69
|
+
val (start, end) = paragraphBounds(paragraphStart, paragraphEnd, text)
|
|
70
|
+
removeBlocksOverlapping(start, end)
|
|
71
|
+
// An anchored block (heading, list item) on an empty line is kept as a zero-length
|
|
72
|
+
// anchor (see adjustForEdit); other blocks need real content.
|
|
73
|
+
if (end < start || (end == start && type !in BlockType.ANCHORED)) return
|
|
74
|
+
|
|
75
|
+
val block = BlockRange(type, start, end, level)
|
|
76
|
+
ranges.add(sortedInsertionIndex(ranges, start), block)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Clears any block on the paragraphs the given range touches (reverting them to
|
|
81
|
+
* the implicit paragraph default).
|
|
82
|
+
*/
|
|
83
|
+
fun removeBlock(
|
|
84
|
+
paragraphStart: Int,
|
|
85
|
+
paragraphEnd: Int,
|
|
86
|
+
text: CharSequence,
|
|
87
|
+
) {
|
|
88
|
+
val (start, end) = paragraphBounds(paragraphStart, paragraphEnd, text)
|
|
89
|
+
removeBlocksOverlapping(start, end)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Shifts/clips block ranges to follow a text edit (see [RangeEditAdjustment]),
|
|
94
|
+
* with anchored-block (heading / list item) persistence layered on top: a block
|
|
95
|
+
* deleted exactly to its end collapses to a zero-length anchor at the edit
|
|
96
|
+
* location (its line survives), and existing anchors shift/keep/drop with their
|
|
97
|
+
* line. The view's prune/normalize pass reconciles anchors against the final text.
|
|
98
|
+
*/
|
|
99
|
+
fun adjustForEdit(
|
|
100
|
+
editLocation: Int,
|
|
101
|
+
deletedLength: Int,
|
|
102
|
+
insertedLength: Int,
|
|
103
|
+
) {
|
|
104
|
+
if (deletedLength == 0 && insertedLength == 0) return
|
|
105
|
+
|
|
106
|
+
val deleteEnd = editLocation + deletedLength
|
|
107
|
+
val delta = insertedLength - deletedLength
|
|
108
|
+
|
|
109
|
+
val anchors = ranges.filter { it.length == 0 && it.type in BlockType.ANCHORED }
|
|
110
|
+
ranges.removeAll { it.length == 0 }
|
|
111
|
+
|
|
112
|
+
// At most one range can end exactly at deleteEnd, so this restores at most
|
|
113
|
+
// one collapsed block.
|
|
114
|
+
val collapsed =
|
|
115
|
+
ranges.firstOrNull {
|
|
116
|
+
it.type in BlockType.ANCHORED && it.start >= editLocation && it.end == deleteEnd
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
RangeEditAdjustment.adjustForEdit(
|
|
120
|
+
ranges,
|
|
121
|
+
editLocation,
|
|
122
|
+
deletedLength,
|
|
123
|
+
insertedLength,
|
|
124
|
+
inheritsReplacementAtStart = { true },
|
|
125
|
+
growsAtStartOnInsert = { true },
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
for (anchor in anchors) {
|
|
129
|
+
when {
|
|
130
|
+
anchor.start <= editLocation -> { /* keeps its position */ }
|
|
131
|
+
|
|
132
|
+
anchor.start >= deleteEnd -> {
|
|
133
|
+
anchor.start += delta
|
|
134
|
+
anchor.end = anchor.start
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
else -> {
|
|
138
|
+
continue // the anchor's line was deleted
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
ranges.add(sortedInsertionIndex(ranges, anchor.start), anchor)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (collapsed != null && collapsed !in ranges) {
|
|
145
|
+
ranges.add(
|
|
146
|
+
sortedInsertionIndex(ranges, editLocation),
|
|
147
|
+
BlockRange(collapsed.type, editLocation, editLocation, collapsed.level),
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Snaps every stored range to the line bounds of its start position.
|
|
154
|
+
* Absorbs edge-typed chars, clips split ranges to first line, drops
|
|
155
|
+
* duplicates. On an empty line an anchored block (heading, list item)
|
|
156
|
+
* persists as a zero-length anchor; any other collapsed range is dropped.
|
|
157
|
+
* List depths are clamped so an item nests at most one level under the
|
|
158
|
+
* previous adjacent item (CommonMark cannot represent orphan nesting).
|
|
159
|
+
* Call after [adjustForEdit] once [text] is final. Idempotent.
|
|
160
|
+
*/
|
|
161
|
+
fun normalizeToLineBounds(text: CharSequence) {
|
|
162
|
+
if (ranges.isEmpty()) return
|
|
163
|
+
|
|
164
|
+
var previousEnd = -1
|
|
165
|
+
val iterator = ranges.listIterator()
|
|
166
|
+
while (iterator.hasNext()) {
|
|
167
|
+
val range = iterator.next()
|
|
168
|
+
val (lineStart, lineEnd) = paragraphBounds(range.start, range.start, text)
|
|
169
|
+
val isEmptyLine = lineEnd == lineStart
|
|
170
|
+
if ((isEmptyLine && range.type !in BlockType.ANCHORED) || lineStart <= previousEnd) {
|
|
171
|
+
// Dedup: drop a range that resolves to an already-claimed paragraph. This is
|
|
172
|
+
// a legitimate self-heal path (a heading anchor and the block it merges into
|
|
173
|
+
// can briefly share a start after a line-join), so it is deliberately silent.
|
|
174
|
+
iterator.remove()
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
range.start = lineStart
|
|
178
|
+
range.end = lineEnd
|
|
179
|
+
previousEnd = lineEnd
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
recomputeListMetadata()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Clamps list depths to valid ancestry (an item nests at most one level under
|
|
187
|
+
* the previous adjacent list item — CommonMark cannot represent orphan nesting)
|
|
188
|
+
* and renumbers ordered items among their adjacent same-depth, same-type run.
|
|
189
|
+
*/
|
|
190
|
+
private fun recomputeListMetadata() {
|
|
191
|
+
var prevEnd = -2
|
|
192
|
+
var prevDepth = -1
|
|
193
|
+
val counters = IntArray(MAX_LIST_DEPTH + 2)
|
|
194
|
+
val counterTypes = arrayOfNulls<BlockType>(MAX_LIST_DEPTH + 2)
|
|
195
|
+
for (range in ranges) {
|
|
196
|
+
if (range.type !in BlockType.LIST_ITEMS) {
|
|
197
|
+
prevDepth = -1
|
|
198
|
+
continue
|
|
199
|
+
}
|
|
200
|
+
val adjacent = prevDepth >= 0 && range.start == prevEnd + 1
|
|
201
|
+
if (!adjacent) {
|
|
202
|
+
counters.fill(0)
|
|
203
|
+
counterTypes.fill(null)
|
|
204
|
+
}
|
|
205
|
+
val maxDepth = if (adjacent) prevDepth + 1 else 0
|
|
206
|
+
if (range.level > maxDepth) range.level = maxDepth
|
|
207
|
+
val depth = range.level
|
|
208
|
+
for (i in depth + 1..MAX_LIST_DEPTH + 1) {
|
|
209
|
+
counters[i] = 0
|
|
210
|
+
counterTypes[i] = null
|
|
211
|
+
}
|
|
212
|
+
if (counterTypes[depth] != range.type) {
|
|
213
|
+
counters[depth] = 0
|
|
214
|
+
counterTypes[depth] = range.type
|
|
215
|
+
}
|
|
216
|
+
counters[depth]++
|
|
217
|
+
range.ordinal = counters[depth]
|
|
218
|
+
prevEnd = range.end
|
|
219
|
+
prevDepth = range.level
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Drops any stored block overlapping `[start, end)`. Blocks never partially
|
|
225
|
+
* overlap, so a touched block is removed wholesale; a zero-length anchor is
|
|
226
|
+
* dropped when it sits within the bounds (so toggle-off clears an empty line).
|
|
227
|
+
*/
|
|
228
|
+
private fun removeBlocksOverlapping(
|
|
229
|
+
start: Int,
|
|
230
|
+
end: Int,
|
|
231
|
+
) {
|
|
232
|
+
ranges.removeAll { (it.end > start && it.start < end) || (it.length == 0 && it.start in start..end) }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Expands a selection to cover whole lines (line-scoped block boundaries). */
|
|
236
|
+
private fun paragraphBounds(
|
|
237
|
+
rangeStart: Int,
|
|
238
|
+
rangeEnd: Int,
|
|
239
|
+
text: CharSequence,
|
|
240
|
+
): Pair<Int, Int> {
|
|
241
|
+
if (text.isEmpty()) return 0 to 0
|
|
242
|
+
|
|
243
|
+
val clampedStart = rangeStart.coerceIn(0, text.length)
|
|
244
|
+
val clampedEnd = rangeEnd.coerceIn(clampedStart, text.length)
|
|
245
|
+
|
|
246
|
+
var lineStart = clampedStart
|
|
247
|
+
while (lineStart > 0 && text[lineStart - 1] != '\n') lineStart--
|
|
248
|
+
|
|
249
|
+
var lineEnd = clampedEnd
|
|
250
|
+
while (lineEnd < text.length && text[lineEnd] != '\n') lineEnd++
|
|
251
|
+
|
|
252
|
+
return lineStart to lineEnd
|
|
253
|
+
}
|
|
254
|
+
}
|
package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt
CHANGED
|
@@ -53,6 +53,55 @@ class FormattingStore {
|
|
|
53
53
|
return if (link != null && start > link.start && start < link.end) Pair(link.end, link.end) else null
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
enum class ToggleResult {
|
|
57
|
+
/** The style was active and has been removed (or is active at the caret). */
|
|
58
|
+
WAS_ACTIVE,
|
|
59
|
+
|
|
60
|
+
/** The style was inactive and has been added (or is inactive at the caret). */
|
|
61
|
+
WAS_INACTIVE,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Toggles [type] on the range `[start, end)`. For a selection (`start < end`),
|
|
66
|
+
* removes the style when active at `start`, otherwise strips [conflictingStyles]
|
|
67
|
+
* and adds it. For a caret (`start == end`), reports the current state without
|
|
68
|
+
* mutating — the caller manages pending-style bookkeeping.
|
|
69
|
+
*/
|
|
70
|
+
fun toggleStyle(
|
|
71
|
+
type: StyleType,
|
|
72
|
+
start: Int,
|
|
73
|
+
end: Int,
|
|
74
|
+
conflictingStyles: Set<StyleType>,
|
|
75
|
+
): ToggleResult {
|
|
76
|
+
val isActive = isStyleActive(type, start)
|
|
77
|
+
if (start < end) {
|
|
78
|
+
if (isActive) {
|
|
79
|
+
removeType(type, start, end)
|
|
80
|
+
} else {
|
|
81
|
+
for (conflict in conflictingStyles) {
|
|
82
|
+
removeType(conflict, start, end)
|
|
83
|
+
}
|
|
84
|
+
addRange(FormattingRange(type, start, end))
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return if (isActive) ToggleResult.WAS_ACTIVE else ToggleResult.WAS_INACTIVE
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Returns true when toggling [type] ON at [position] should be refused
|
|
92
|
+
* because one of the [blockingStyles] is currently active there.
|
|
93
|
+
* Toggling OFF (the style is already active) is never blocked.
|
|
94
|
+
*/
|
|
95
|
+
fun isToggleBlocked(
|
|
96
|
+
type: StyleType,
|
|
97
|
+
position: Int,
|
|
98
|
+
blockingStyles: Set<StyleType>,
|
|
99
|
+
): Boolean {
|
|
100
|
+
if (blockingStyles.isEmpty()) return false
|
|
101
|
+
if (isStyleActive(type, position)) return false
|
|
102
|
+
return blockingStyles.any { isStyleActive(it, position) }
|
|
103
|
+
}
|
|
104
|
+
|
|
56
105
|
fun addRange(newRange: FormattingRange) {
|
|
57
106
|
var mergedStart = newRange.start
|
|
58
107
|
var mergedEnd = newRange.end
|
|
@@ -72,7 +121,7 @@ class FormattingStore {
|
|
|
72
121
|
}
|
|
73
122
|
|
|
74
123
|
val merged = FormattingRange(newRange.type, mergedStart, mergedEnd, newRange.url)
|
|
75
|
-
val insertAt = sortedInsertionIndex(mergedStart)
|
|
124
|
+
val insertAt = sortedInsertionIndex(ranges, mergedStart)
|
|
76
125
|
ranges.add(insertAt, merged)
|
|
77
126
|
}
|
|
78
127
|
|
|
@@ -104,7 +153,7 @@ class FormattingStore {
|
|
|
104
153
|
|
|
105
154
|
// Remainders are fragments of a just-removed range and cannot overlap others.
|
|
106
155
|
for (remainder in remainders) {
|
|
107
|
-
val insertAt = sortedInsertionIndex(remainder.start)
|
|
156
|
+
val insertAt = sortedInsertionIndex(ranges, remainder.start)
|
|
108
157
|
ranges.add(insertAt, remainder)
|
|
109
158
|
}
|
|
110
159
|
}
|
|
@@ -113,99 +162,43 @@ class FormattingStore {
|
|
|
113
162
|
ranges.remove(range)
|
|
114
163
|
}
|
|
115
164
|
|
|
165
|
+
/** Shifts/clips formatting ranges to follow a text edit. See [RangeEditAdjustment]. */
|
|
116
166
|
fun adjustForEdit(
|
|
117
167
|
editLocation: Int,
|
|
118
168
|
deletedLength: Int,
|
|
119
169
|
insertedLength: Int,
|
|
120
170
|
) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
171
|
+
RangeEditAdjustment.adjustForEdit(
|
|
172
|
+
ranges,
|
|
173
|
+
editLocation,
|
|
174
|
+
deletedLength,
|
|
175
|
+
insertedLength,
|
|
176
|
+
inheritsReplacementAtStart = { it.type != StyleType.LINK },
|
|
177
|
+
)
|
|
178
|
+
coalesceAdjacentSameTypeRanges()
|
|
179
|
+
}
|
|
125
180
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
EditOverlap.CLIPPED_END -> {
|
|
145
|
-
val newEnd = editLocation + insertedLength
|
|
146
|
-
val newLength = if (newEnd > range.start) newEnd - range.start else 0
|
|
147
|
-
range.end = range.start + newLength
|
|
148
|
-
if (newLength == 0) indexesToRemove.add(idx)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
EditOverlap.CLIPPED_START -> {
|
|
152
|
-
val charsClipped = deleteEnd - range.start
|
|
153
|
-
val newStart = editLocation + insertedLength
|
|
154
|
-
val oldLength = range.length
|
|
155
|
-
range.start = newStart
|
|
156
|
-
range.end = newStart + oldLength - charsClipped
|
|
157
|
-
if (range.length == 0) indexesToRemove.add(idx)
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
} else {
|
|
161
|
-
when {
|
|
162
|
-
range.start >= editLocation -> {
|
|
163
|
-
range.start += insertedLength
|
|
164
|
-
range.end += insertedLength
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
editLocation > range.start && editLocation < range.end -> {
|
|
168
|
-
range.end += insertedLength
|
|
169
|
-
}
|
|
181
|
+
/**
|
|
182
|
+
* Merge same-type (and same-url) ranges left adjacent or overlapping by an
|
|
183
|
+
* edit — e.g. deleting the space in "**foo** **bar**" leaves two touching
|
|
184
|
+
* bold ranges that would serialize as "**foo****bar**". [addRange] keeps
|
|
185
|
+
* this invariant on insert; the edit path must too.
|
|
186
|
+
*/
|
|
187
|
+
private fun coalesceAdjacentSameTypeRanges() {
|
|
188
|
+
var idx = 0
|
|
189
|
+
while (idx < ranges.size) {
|
|
190
|
+
val current = ranges[idx]
|
|
191
|
+
var next = idx + 1
|
|
192
|
+
while (next < ranges.size && ranges[next].start <= current.end) {
|
|
193
|
+
val candidate = ranges[next]
|
|
194
|
+
if (candidate.type == current.type && candidate.url == current.url) {
|
|
195
|
+
current.end = maxOf(current.end, candidate.end)
|
|
196
|
+
ranges.removeAt(next)
|
|
197
|
+
} else {
|
|
198
|
+
next++
|
|
170
199
|
}
|
|
171
200
|
}
|
|
201
|
+
idx++
|
|
172
202
|
}
|
|
173
|
-
|
|
174
|
-
for (idx in indexesToRemove.reversed()) {
|
|
175
|
-
ranges.removeAt(idx)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
ranges.removeAll { it.length == 0 }
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private fun sortedInsertionIndex(location: Int): Int {
|
|
182
|
-
var index = 0
|
|
183
|
-
for (existing in ranges) {
|
|
184
|
-
if (existing.start > location) break
|
|
185
|
-
index++
|
|
186
|
-
}
|
|
187
|
-
return index
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private enum class EditOverlap {
|
|
191
|
-
BEFORE_EDIT,
|
|
192
|
-
AFTER_EDIT,
|
|
193
|
-
FULLY_DELETED,
|
|
194
|
-
DELETED_INSIDE,
|
|
195
|
-
CLIPPED_END,
|
|
196
|
-
CLIPPED_START,
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
private fun classifyOverlap(
|
|
200
|
-
rangeStart: Int,
|
|
201
|
-
rangeEnd: Int,
|
|
202
|
-
editLocation: Int,
|
|
203
|
-
deleteEnd: Int,
|
|
204
|
-
): EditOverlap {
|
|
205
|
-
if (rangeEnd <= editLocation) return EditOverlap.BEFORE_EDIT
|
|
206
|
-
if (rangeStart >= deleteEnd) return EditOverlap.AFTER_EDIT
|
|
207
|
-
if (rangeStart >= editLocation && rangeEnd <= deleteEnd) return EditOverlap.FULLY_DELETED
|
|
208
|
-
if (rangeStart < editLocation && rangeEnd > deleteEnd) return EditOverlap.DELETED_INSIDE
|
|
209
|
-
return if (rangeStart < editLocation) EditOverlap.CLIPPED_END else EditOverlap.CLIPPED_START
|
|
210
203
|
}
|
|
211
204
|
}
|
package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputFormatter.kt
CHANGED
|
@@ -2,16 +2,23 @@ package com.swmansion.enriched.markdown.input.formatting
|
|
|
2
2
|
|
|
3
3
|
import android.text.Spannable
|
|
4
4
|
import android.text.style.CharacterStyle
|
|
5
|
+
import com.swmansion.enriched.markdown.input.model.BlockRange
|
|
6
|
+
import com.swmansion.enriched.markdown.input.model.BlockType
|
|
5
7
|
import com.swmansion.enriched.markdown.input.model.FormattingRange
|
|
6
8
|
import com.swmansion.enriched.markdown.input.model.InputFormatterStyle
|
|
7
9
|
import com.swmansion.enriched.markdown.input.model.StyleType
|
|
10
|
+
import com.swmansion.enriched.markdown.input.spans.InputListItemSpacingSpan
|
|
11
|
+
import com.swmansion.enriched.markdown.input.styles.BlockHandler
|
|
8
12
|
import com.swmansion.enriched.markdown.input.styles.BoldStyleHandler
|
|
13
|
+
import com.swmansion.enriched.markdown.input.styles.HeadingBlockHandler
|
|
9
14
|
import com.swmansion.enriched.markdown.input.styles.ItalicStyleHandler
|
|
10
15
|
import com.swmansion.enriched.markdown.input.styles.LinkStyleHandler
|
|
16
|
+
import com.swmansion.enriched.markdown.input.styles.OrderedListBlockHandler
|
|
11
17
|
import com.swmansion.enriched.markdown.input.styles.SpoilerStyleHandler
|
|
12
18
|
import com.swmansion.enriched.markdown.input.styles.StrikethroughStyleHandler
|
|
13
19
|
import com.swmansion.enriched.markdown.input.styles.StyleHandler
|
|
14
20
|
import com.swmansion.enriched.markdown.input.styles.UnderlineStyleHandler
|
|
21
|
+
import com.swmansion.enriched.markdown.input.styles.UnorderedListBlockHandler
|
|
15
22
|
|
|
16
23
|
/**
|
|
17
24
|
* Marker interface so we only remove spans we created, leaving
|
|
@@ -19,7 +26,15 @@ import com.swmansion.enriched.markdown.input.styles.UnderlineStyleHandler
|
|
|
19
26
|
*/
|
|
20
27
|
interface MarkdownSpan
|
|
21
28
|
|
|
22
|
-
|
|
29
|
+
/**
|
|
30
|
+
* @property displayDensity px-per-dp of the host display, handed to the list block
|
|
31
|
+
* handlers so they can build density-correct spans without a Context. This is a
|
|
32
|
+
* platform constant, not style data, which is why it lives here rather than in
|
|
33
|
+
* [InputFormatterStyle].
|
|
34
|
+
*/
|
|
35
|
+
class InputFormatter(
|
|
36
|
+
private val displayDensity: Float = 1f,
|
|
37
|
+
) {
|
|
23
38
|
val handlers: Map<StyleType, StyleHandler> =
|
|
24
39
|
mapOf(
|
|
25
40
|
StyleType.BOLD to BoldStyleHandler(),
|
|
@@ -30,6 +45,22 @@ class InputFormatter {
|
|
|
30
45
|
StyleType.SPOILER to SpoilerStyleHandler(),
|
|
31
46
|
)
|
|
32
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Block handlers, keyed by block type. A single [HeadingBlockHandler] serves all
|
|
50
|
+
* six heading levels — it reads the level from the [BlockRange] — so it is mapped
|
|
51
|
+
* under every `HEADING_n` key. One [UnorderedListBlockHandler] serves every list
|
|
52
|
+
* depth (depth lives on the range), so it is mapped under the single list key.
|
|
53
|
+
*/
|
|
54
|
+
val blockHandlers: Map<BlockType, BlockHandler> =
|
|
55
|
+
buildMap {
|
|
56
|
+
val heading = HeadingBlockHandler()
|
|
57
|
+
for (type in BlockType.HEADINGS) put(type, heading)
|
|
58
|
+
put(BlockType.UNORDERED_LIST_ITEM, UnorderedListBlockHandler(displayDensity))
|
|
59
|
+
put(BlockType.ORDERED_LIST_ITEM, OrderedListBlockHandler(displayDensity))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fun handlerForBlock(type: BlockType): BlockHandler? = blockHandlers[type]
|
|
63
|
+
|
|
33
64
|
private var style: InputFormatterStyle? = null
|
|
34
65
|
|
|
35
66
|
fun updateStyle(newStyle: InputFormatterStyle): Boolean {
|
|
@@ -38,16 +69,20 @@ class InputFormatter {
|
|
|
38
69
|
return true
|
|
39
70
|
}
|
|
40
71
|
|
|
72
|
+
fun resolveHeadingFontSizePx(level: Int): Float? = style?.headingStyle(level)?.fontSizePx
|
|
73
|
+
|
|
41
74
|
fun applyFormatting(
|
|
42
75
|
spannable: Spannable,
|
|
43
76
|
ranges: List<FormattingRange>,
|
|
44
77
|
) {
|
|
45
78
|
val currentStyle = style ?: return
|
|
46
79
|
|
|
80
|
+
val blockSpanClasses = blockHandlers.values.flatMap { it.spanClasses() }.toSet()
|
|
47
81
|
val existingSpans =
|
|
48
82
|
spannable
|
|
49
83
|
.getSpans(0, spannable.length, CharacterStyle::class.java)
|
|
50
84
|
.filterIsInstance<MarkdownSpan>()
|
|
85
|
+
.filter { span -> blockSpanClasses.none { it.isInstance(span) } }
|
|
51
86
|
val linkSpanClasses = handlers[StyleType.LINK]?.spanClasses().orEmpty().toSet()
|
|
52
87
|
val existingLinkSpans = existingSpans.filter { span -> linkSpanClasses.any { it.isInstance(span) } }
|
|
53
88
|
for (span in existingLinkSpans) {
|
|
@@ -108,6 +143,79 @@ class InputFormatter {
|
|
|
108
143
|
}
|
|
109
144
|
}
|
|
110
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Applies each block range's paragraph-scoped spans via its handler, after the
|
|
148
|
+
* inline pass. Mirrors [applyFormatting]: removes the block spans we previously
|
|
149
|
+
* set (identified by the registered handlers' [BlockHandler.spanClasses]) and
|
|
150
|
+
* re-applies from the current ranges. With no handlers registered (PR1) both
|
|
151
|
+
* the cleanup and the apply loops are no-ops, so block formatting is inert.
|
|
152
|
+
*/
|
|
153
|
+
fun applyBlockFormatting(
|
|
154
|
+
spannable: Spannable,
|
|
155
|
+
blockRanges: List<BlockRange>,
|
|
156
|
+
) = applyBlockFormatting(spannable, blockRanges, 0, spannable.length)
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Re-stamps block spans, scoped to `[scopeStart, scopeEnd)`. Only the block spans
|
|
160
|
+
* we created that intersect the scope are removed, and only the block ranges
|
|
161
|
+
* intersecting the scope are re-applied — so an edit re-normalizes just the
|
|
162
|
+
* affected line(s) instead of the whole document on every keystroke. Pass the
|
|
163
|
+
* full document range for a wholesale re-apply (import / style change).
|
|
164
|
+
*/
|
|
165
|
+
fun applyBlockFormatting(
|
|
166
|
+
spannable: Spannable,
|
|
167
|
+
blockRanges: List<BlockRange>,
|
|
168
|
+
scopeStart: Int,
|
|
169
|
+
scopeEnd: Int,
|
|
170
|
+
) {
|
|
171
|
+
val currentStyle = style ?: return
|
|
172
|
+
if (blockHandlers.isEmpty()) return
|
|
173
|
+
|
|
174
|
+
val start = scopeStart.coerceIn(0, spannable.length)
|
|
175
|
+
val end = scopeEnd.coerceIn(start, spannable.length)
|
|
176
|
+
|
|
177
|
+
val blockSpanClasses = blockHandlers.values.flatMap { it.spanClasses() }.toSet()
|
|
178
|
+
|
|
179
|
+
val existingBlockSpans =
|
|
180
|
+
spannable
|
|
181
|
+
.getSpans(start, end, Any::class.java)
|
|
182
|
+
.filter { span -> span is MarkdownSpan && blockSpanClasses.any { it.isInstance(span) } }
|
|
183
|
+
for (span in existingBlockSpans) {
|
|
184
|
+
spannable.removeSpan(span)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
for (range in blockRanges) {
|
|
188
|
+
val isAnchor = range.length == 0 && range.type in BlockType.ANCHORED
|
|
189
|
+
if (!isAnchor && (range.start >= range.end || range.start < 0 || range.end > spannable.length)) continue
|
|
190
|
+
if (isAnchor && (range.start < 0 || range.start > spannable.length)) continue
|
|
191
|
+
if (isAnchor) {
|
|
192
|
+
if (range.start < start || range.start > end) continue
|
|
193
|
+
} else if (range.end <= start || range.start >= end) {
|
|
194
|
+
continue
|
|
195
|
+
}
|
|
196
|
+
val handler = blockHandlers[range.type] ?: continue
|
|
197
|
+
|
|
198
|
+
// Extend a zero-length anchor to cover the next character (usually '\n') so
|
|
199
|
+
// the Layout gives the empty line the block's metrics. At the very end of
|
|
200
|
+
// text the span stays zero-length; the view-level paint override handles
|
|
201
|
+
// cursor height for that edge case.
|
|
202
|
+
val anchorEnd = if (isAnchor && range.start < spannable.length) range.start + 1 else range.end
|
|
203
|
+
val flags = if (isAnchor) Spannable.SPAN_INCLUSIVE_INCLUSIVE else Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
204
|
+
for (span in handler.createSpans(range, currentStyle)) {
|
|
205
|
+
// A LineHeightSpan (list-item spacing) must cover only the item's first
|
|
206
|
+
// character so it spaces just the first visual line, not wrapped lines.
|
|
207
|
+
val spanEnd =
|
|
208
|
+
if (span is InputListItemSpacingSpan) {
|
|
209
|
+
(range.start + 1).coerceAtMost(range.end).coerceAtMost(spannable.length)
|
|
210
|
+
} else {
|
|
211
|
+
anchorEnd
|
|
212
|
+
}
|
|
213
|
+
if (span is InputListItemSpacingSpan && spanEnd <= range.start) continue
|
|
214
|
+
spannable.setSpan(span, range.start, spanEnd, flags)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
111
219
|
private data class SpanDescriptor(
|
|
112
220
|
val start: Int,
|
|
113
221
|
val end: Int,
|