react-native-marcus 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +227 -0
- package/RNMarcus.podspec +78 -0
- package/android/build.gradle +220 -0
- package/android/proguard-rules.pro +1 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/cpp/CMakeLists.txt +41 -0
- package/android/src/main/cpp/MarkdownParser.cpp +49 -0
- package/android/src/main/cpp/MarkdownParser.h +56 -0
- package/android/src/main/cpp/OnLoad.cpp +17 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusModule.kt +28 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusPackage.kt +31 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorView.kt +63 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorViewManager.kt +32 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorViewManagerSpec.kt +21 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownFormatter.kt +333 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownParseResult.kt +12 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownParser.kt +89 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownRange.kt +11 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownStyle.kt +81 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownTextWatcher.kt +17 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownUtils.kt +84 -0
- package/android/src/main/java/app/getbullet/marcus/ReactLineHeight.java +29 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundColorSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundSpan.kt +105 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBlockIndentSpan.kt +67 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBoldSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontFamilySpan.kt +28 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontSizeSpan.kt +7 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownForegroundColorSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownGapSpan.kt +57 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownItalicSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownLineHeightSpan.kt +21 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownSpan.kt +7 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownStrikethroughSpan.kt +5 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownUnderlineSpan.kt +5 -0
- package/android/src/main/new_arch/CMakeLists.txt +70 -0
- package/android/src/main/new_arch/RNMarcusSpec.cpp +43 -0
- package/android/src/main/new_arch/RNMarcusSpec.h +36 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorShadowNode.cpp +218 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorShadowNode.h +54 -0
- package/android/src/reactNativeVersionPatch/CustomMountingManager/81/app/getbullet/marcus/CustomFabricUIManager.java +156 -0
- package/android/src/reactNativeVersionPatch/CustomMountingManager/latest/app/getbullet/marcus/CustomFabricUIManager.java +130 -0
- package/android/src/test/java/app/getbullet/marcus/MarkdownFormatterTest.kt +205 -0
- package/android/src/test/java/app/getbullet/marcus/RenderModel.kt +78 -0
- package/android/src/test/resources/render-model.txt +583 -0
- package/apple/MarcusModule.h +8 -0
- package/apple/MarcusModule.mm +26 -0
- package/apple/MarcusRange.h +15 -0
- package/apple/MarcusRange.mm +15 -0
- package/apple/MarcusTextInputDecoratorComponentView.h +12 -0
- package/apple/MarcusTextInputDecoratorComponentView.mm +121 -0
- package/apple/MarcusTextInputDecoratorShadowNode.cpp +251 -0
- package/apple/MarcusTextInputDecoratorShadowNode.h +65 -0
- package/apple/MarcusTextInputDecoratorShadowNode.mm +204 -0
- package/apple/MarcusTextInputDecoratorViewManager.h +8 -0
- package/apple/MarcusTextInputDecoratorViewManager.mm +11 -0
- package/apple/MarkdownAdaptiveImageGlyph.swift +44 -0
- package/apple/MarkdownAttributes.h +31 -0
- package/apple/MarkdownAttributes.m +9 -0
- package/apple/MarkdownBackedTextInputDelegate.swift +117 -0
- package/apple/MarkdownDecorator.swift +316 -0
- package/apple/MarkdownFonts.swift +21 -0
- package/apple/MarkdownFormatter+React.swift +51 -0
- package/apple/MarkdownFormatter.swift +509 -0
- package/apple/MarkdownLog.h +10 -0
- package/apple/MarkdownLog.mm +6 -0
- package/apple/MarkdownParser.swift +200 -0
- package/apple/MarkdownSwiftInterop.h +23 -0
- package/apple/MarkdownTextBackground.swift +17 -0
- package/apple/MarkdownTextFieldObserver.swift +90 -0
- package/apple/MarkdownTextLayoutFragment.swift +205 -0
- package/apple/MarkdownTextLayoutManagerDelegate.swift +84 -0
- package/apple/MarkdownTextStorageDelegate.swift +32 -0
- package/apple/MarkdownTextViewObserver.swift +30 -0
- package/apple/RCTMarcusStyle+Codegen.h +18 -0
- package/apple/RCTMarcusStyle+Codegen.mm +64 -0
- package/apple/RCTMarcusStyle.h +41 -0
- package/apple/RCTMarcusStyle.m +5 -0
- package/apple/RCTMarcusUtils.h +26 -0
- package/apple/RCTMarcusUtils.mm +26 -0
- package/apple/RCTTextInput+AdaptiveImageGlyph.h +16 -0
- package/apple/RCTTextInput+AdaptiveImageGlyph.mm +79 -0
- package/apple/RCTTextInputComponentView+Marcus.h +11 -0
- package/apple/RCTTextInputComponentView+Marcus.mm +79 -0
- package/cpp/MarkdownCxx.modulemap +8 -0
- package/cpp/MarkdownGlobal.cpp +48 -0
- package/cpp/MarkdownGlobal.h +33 -0
- package/cpp/MarkdownWorkletParser.cpp +93 -0
- package/cpp/MarkdownWorkletParser.h +45 -0
- package/cpp/RuntimeDecorator.cpp +39 -0
- package/cpp/RuntimeDecorator.h +14 -0
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorState.h +33 -0
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorViewComponentDescriptor.h +17 -0
- package/lib/commonjs/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
- package/lib/commonjs/MarkdownTextInput.js +99 -0
- package/lib/commonjs/MarkdownTextInput.js.map +1 -0
- package/lib/commonjs/MarkdownTextInput.web.js +618 -0
- package/lib/commonjs/MarkdownTextInput.web.js.map +1 -0
- package/lib/commonjs/NativeMarcusModule.js +9 -0
- package/lib/commonjs/NativeMarcusModule.js.map +1 -0
- package/lib/commonjs/commonTypes.js +2 -0
- package/lib/commonjs/commonTypes.js.map +1 -0
- package/lib/commonjs/global.d.js +2 -0
- package/lib/commonjs/global.d.js.map +1 -0
- package/lib/commonjs/index.js +27 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/parser/index.js +7030 -0
- package/lib/commonjs/parser/index.js.map +7 -0
- package/lib/commonjs/rangeUtils.js +71 -0
- package/lib/commonjs/rangeUtils.js.map +1 -0
- package/lib/commonjs/react-native-web.d.js +2 -0
- package/lib/commonjs/react-native-web.d.js.map +1 -0
- package/lib/commonjs/styleUtils.js +91 -0
- package/lib/commonjs/styleUtils.js.map +1 -0
- package/lib/commonjs/web/InputHistory.js +111 -0
- package/lib/commonjs/web/InputHistory.js.map +1 -0
- package/lib/commonjs/web/MarkdownTextInput.css +72 -0
- package/lib/commonjs/web/utils/blockLayout.js +132 -0
- package/lib/commonjs/web/utils/blockLayout.js.map +1 -0
- package/lib/commonjs/web/utils/blockUtils.js +219 -0
- package/lib/commonjs/web/utils/blockUtils.js.map +1 -0
- package/lib/commonjs/web/utils/browserUtils.js +40 -0
- package/lib/commonjs/web/utils/browserUtils.js.map +1 -0
- package/lib/commonjs/web/utils/cursorUtils.js +157 -0
- package/lib/commonjs/web/utils/cursorUtils.js.map +1 -0
- package/lib/commonjs/web/utils/inputUtils.js +152 -0
- package/lib/commonjs/web/utils/inputUtils.js.map +1 -0
- package/lib/commonjs/web/utils/measureUtils.js +0 -0
- package/lib/commonjs/web/utils/measureUtils.js.map +1 -0
- package/lib/commonjs/web/utils/parserUtils.js +552 -0
- package/lib/commonjs/web/utils/parserUtils.js.map +1 -0
- package/lib/commonjs/web/utils/treeUtils.js +109 -0
- package/lib/commonjs/web/utils/treeUtils.js.map +1 -0
- package/lib/commonjs/web/utils/webStyleUtils.js +59 -0
- package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -0
- package/lib/module/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
- package/lib/module/MarkdownTextInput.js +94 -0
- package/lib/module/MarkdownTextInput.js.map +1 -0
- package/lib/module/MarkdownTextInput.web.js +612 -0
- package/lib/module/MarkdownTextInput.web.js.map +1 -0
- package/lib/module/NativeMarcusModule.js +5 -0
- package/lib/module/NativeMarcusModule.js.map +1 -0
- package/lib/module/commonTypes.js +2 -0
- package/lib/module/commonTypes.js.map +1 -0
- package/lib/module/global.d.js +2 -0
- package/lib/module/global.d.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/parser/index.js +7029 -0
- package/lib/module/parser/index.js.map +7 -0
- package/lib/module/rangeUtils.js +66 -0
- package/lib/module/rangeUtils.js.map +1 -0
- package/lib/module/react-native-web.d.js +2 -0
- package/lib/module/react-native-web.d.js.map +1 -0
- package/lib/module/styleUtils.js +88 -0
- package/lib/module/styleUtils.js.map +1 -0
- package/lib/module/web/InputHistory.js +106 -0
- package/lib/module/web/InputHistory.js.map +1 -0
- package/lib/module/web/MarkdownTextInput.css +72 -0
- package/lib/module/web/utils/blockLayout.js +128 -0
- package/lib/module/web/utils/blockLayout.js.map +1 -0
- package/lib/module/web/utils/blockUtils.js +210 -0
- package/lib/module/web/utils/blockUtils.js.map +1 -0
- package/lib/module/web/utils/browserUtils.js +36 -0
- package/lib/module/web/utils/browserUtils.js.map +1 -0
- package/lib/module/web/utils/cursorUtils.js +150 -0
- package/lib/module/web/utils/cursorUtils.js.map +1 -0
- package/lib/module/web/utils/inputUtils.js +144 -0
- package/lib/module/web/utils/inputUtils.js.map +1 -0
- package/lib/module/web/utils/measureUtils.js +0 -0
- package/lib/module/web/utils/measureUtils.js.map +1 -0
- package/lib/module/web/utils/parserUtils.js +547 -0
- package/lib/module/web/utils/parserUtils.js.map +1 -0
- package/lib/module/web/utils/treeUtils.js +103 -0
- package/lib/module/web/utils/treeUtils.js.map +1 -0
- package/lib/module/web/utils/webStyleUtils.js +54 -0
- package/lib/module/web/utils/webStyleUtils.js.map +1 -0
- package/lib/typescript/commonjs/e2e/checks.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/checks.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/e2e/editing.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/editing.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/e2e/helpers.d.ts +23 -0
- package/lib/typescript/commonjs/e2e/helpers.d.ts.map +1 -0
- package/lib/typescript/commonjs/e2e/smoke.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/smoke.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +63 -0
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts +22 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts +41 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/NativeMarcusModule.d.ts +7 -0
- package/lib/typescript/commonjs/src/NativeMarcusModule.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/commonTypes.d.ts +10 -0
- package/lib/typescript/commonjs/src/commonTypes.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +5 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/parser/index.d.ts +8 -0
- package/lib/typescript/commonjs/src/parser/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts +8 -0
- package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/rangeUtils.d.ts +6 -0
- package/lib/typescript/commonjs/src/rangeUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/styleUtils.d.ts +8 -0
- package/lib/typescript/commonjs/src/styleUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/InputHistory.d.ts +24 -0
- package/lib/typescript/commonjs/src/web/InputHistory.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts +41 -0
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts +33 -0
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/browserUtils.d.ts +7 -0
- package/lib/typescript/commonjs/src/web/utils/browserUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/cursorUtils.d.ts +12 -0
- package/lib/typescript/commonjs/src/web/utils/cursorUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/inputUtils.d.ts +12 -0
- package/lib/typescript/commonjs/src/web/utils/inputUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/measureUtils.d.ts +22 -0
- package/lib/typescript/commonjs/src/web/utils/measureUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts +44 -0
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts +25 -0
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts +7 -0
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts.map +1 -0
- package/lib/typescript/module/e2e/checks.test.d.ts +2 -0
- package/lib/typescript/module/e2e/checks.test.d.ts.map +1 -0
- package/lib/typescript/module/e2e/editing.test.d.ts +2 -0
- package/lib/typescript/module/e2e/editing.test.d.ts.map +1 -0
- package/lib/typescript/module/e2e/helpers.d.ts +23 -0
- package/lib/typescript/module/e2e/helpers.d.ts.map +1 -0
- package/lib/typescript/module/e2e/smoke.test.d.ts +2 -0
- package/lib/typescript/module/e2e/smoke.test.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +63 -0
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownTextInput.d.ts +22 -0
- package/lib/typescript/module/src/MarkdownTextInput.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts +41 -0
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts.map +1 -0
- package/lib/typescript/module/src/NativeMarcusModule.d.ts +7 -0
- package/lib/typescript/module/src/NativeMarcusModule.d.ts.map +1 -0
- package/lib/typescript/module/src/commonTypes.d.ts +10 -0
- package/lib/typescript/module/src/commonTypes.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +5 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -0
- package/lib/typescript/module/src/parser/index.d.ts +8 -0
- package/lib/typescript/module/src/parser/index.d.ts.map +1 -0
- package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts +8 -0
- package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts.map +1 -0
- package/lib/typescript/module/src/rangeUtils.d.ts +6 -0
- package/lib/typescript/module/src/rangeUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/styleUtils.d.ts +8 -0
- package/lib/typescript/module/src/styleUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/InputHistory.d.ts +24 -0
- package/lib/typescript/module/src/web/InputHistory.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts +41 -0
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts +33 -0
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/browserUtils.d.ts +7 -0
- package/lib/typescript/module/src/web/utils/browserUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/cursorUtils.d.ts +12 -0
- package/lib/typescript/module/src/web/utils/cursorUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/inputUtils.d.ts +12 -0
- package/lib/typescript/module/src/web/utils/inputUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/measureUtils.d.ts +22 -0
- package/lib/typescript/module/src/web/utils/measureUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts +44 -0
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts +25 -0
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts +7 -0
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts.map +1 -0
- package/package.json +167 -0
- package/react-native.config.js +10 -0
- package/src/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
- package/src/MarkdownTextInput.tsx +139 -0
- package/src/MarkdownTextInput.web.tsx +958 -0
- package/src/NativeMarcusModule.ts +8 -0
- package/src/commonTypes.ts +30 -0
- package/src/global.d.ts +1 -0
- package/src/index.tsx +4 -0
- package/src/parser/index.ts +556 -0
- package/src/parser/micromark-extension-mentions.ts +82 -0
- package/src/rangeUtils.ts +68 -0
- package/src/react-native-web.d.ts +1 -0
- package/src/styleUtils.ts +100 -0
- package/src/web/InputHistory.ts +137 -0
- package/src/web/MarkdownTextInput.css +72 -0
- package/src/web/utils/blockLayout.ts +163 -0
- package/src/web/utils/blockUtils.ts +245 -0
- package/src/web/utils/browserUtils.ts +41 -0
- package/src/web/utils/cursorUtils.ts +204 -0
- package/src/web/utils/inputUtils.ts +206 -0
- package/src/web/utils/measureUtils.ts +0 -0
- package/src/web/utils/parserUtils.ts +813 -0
- package/src/web/utils/treeUtils.ts +149 -0
- package/src/web/utils/webStyleUtils.ts +69 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
/// Applies markdown ranges to an attributed string as text attributes.
|
|
4
|
+
///
|
|
5
|
+
/// Stateless by design: style and ranges are parameters rather than stored
|
|
6
|
+
/// properties. That is what lets the Fabric measure path call it concurrently
|
|
7
|
+
/// without a lock -- the previous design stored the style and parser id on a
|
|
8
|
+
/// shared object, so a setter on one thread could race a read on another.
|
|
9
|
+
@objc public final class MarkdownFormatter: NSObject {
|
|
10
|
+
|
|
11
|
+
/// Font resolution is injected rather than reached for, so this file carries
|
|
12
|
+
/// no React dependency and can be compiled on its own. The Objective-C entry
|
|
13
|
+
/// point in `MarkdownFormatter+React.swift` supplies the production one.
|
|
14
|
+
public static func format(
|
|
15
|
+
_ attributedString: NSMutableAttributedString,
|
|
16
|
+
defaultTextAttributes: [NSAttributedString.Key: Any],
|
|
17
|
+
ranges: [MarcusRange],
|
|
18
|
+
style: RCTMarcusStyle,
|
|
19
|
+
fonts: MarkdownFontProviding
|
|
20
|
+
) {
|
|
21
|
+
let fullRange = NSRange(location: 0, length: attributedString.length)
|
|
22
|
+
|
|
23
|
+
attributedString.beginEditing()
|
|
24
|
+
|
|
25
|
+
attributedString.setAttributes(defaultTextAttributes, range: fullRange)
|
|
26
|
+
|
|
27
|
+
// Forces the swizzled `_textOf:equals:` into string-only comparison.
|
|
28
|
+
attributedString.addAttribute(.marcusText, value: true, range: fullRange)
|
|
29
|
+
|
|
30
|
+
// Containers arrive per line, outermost first, each preceded by the run of
|
|
31
|
+
// text its own marker takes up on that line. Laying a line out is a walk
|
|
32
|
+
// left to right: reserve a container's gutter, step over its marker, repeat.
|
|
33
|
+
var line = BlockLayout()
|
|
34
|
+
var pendingPrefix: NSRange?
|
|
35
|
+
|
|
36
|
+
for markdownRange in ranges {
|
|
37
|
+
if markdownRange.type == "block-prefix" {
|
|
38
|
+
pendingPrefix = markdownRange.range
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if let gutter = gutter(
|
|
43
|
+
for: markdownRange.type,
|
|
44
|
+
depth: Int(markdownRange.depth),
|
|
45
|
+
style: style
|
|
46
|
+
) {
|
|
47
|
+
line.add(
|
|
48
|
+
markdownRange.type,
|
|
49
|
+
gutter: gutter,
|
|
50
|
+
depth: Int(markdownRange.depth),
|
|
51
|
+
prefix: pendingPrefix,
|
|
52
|
+
to: attributedString,
|
|
53
|
+
range: markdownRange.range,
|
|
54
|
+
defaultTextAttributes: defaultTextAttributes
|
|
55
|
+
)
|
|
56
|
+
pendingPrefix = nil
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
apply(
|
|
61
|
+
markdownRange.type,
|
|
62
|
+
to: attributedString,
|
|
63
|
+
range: markdownRange.range,
|
|
64
|
+
depth: Int(markdownRange.depth),
|
|
65
|
+
style: style,
|
|
66
|
+
fonts: fonts
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
(attributedString.string as NSString).enumerateSubstrings(
|
|
71
|
+
in: fullRange,
|
|
72
|
+
options: [.byLines, .substringNotRequired]
|
|
73
|
+
) { _, _, enclosingRange, _ in
|
|
74
|
+
applyBaselineOffset(to: attributedString, range: enclosingRange)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
attributedString.fixAttributes(in: fullRange)
|
|
78
|
+
|
|
79
|
+
attributedString.endEditing()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// MARK: - Per-range attributes
|
|
83
|
+
|
|
84
|
+
private static func apply(
|
|
85
|
+
_ type: String,
|
|
86
|
+
to attributedString: NSMutableAttributedString,
|
|
87
|
+
range: NSRange,
|
|
88
|
+
depth: Int,
|
|
89
|
+
style: RCTMarcusStyle,
|
|
90
|
+
fonts: MarkdownFontProviding
|
|
91
|
+
) {
|
|
92
|
+
switch type {
|
|
93
|
+
case "bold", "italic", "code", "pre", "heading", "emoji":
|
|
94
|
+
var font =
|
|
95
|
+
attributedString.attribute(
|
|
96
|
+
.font,
|
|
97
|
+
at: range.location,
|
|
98
|
+
effectiveRange: nil
|
|
99
|
+
) as? UIFont
|
|
100
|
+
switch type {
|
|
101
|
+
case "bold":
|
|
102
|
+
font = fonts.update(font, weight: "bold")
|
|
103
|
+
case "italic":
|
|
104
|
+
font = fonts.update(font, style: "italic")
|
|
105
|
+
case "code":
|
|
106
|
+
font = fonts.update(
|
|
107
|
+
font,
|
|
108
|
+
family: style.codeFontFamily,
|
|
109
|
+
size: style.codeFontSize,
|
|
110
|
+
weight: nil
|
|
111
|
+
)
|
|
112
|
+
case "pre":
|
|
113
|
+
font = fonts.update(
|
|
114
|
+
font,
|
|
115
|
+
family: style.preFontFamily,
|
|
116
|
+
size: style.preFontSize,
|
|
117
|
+
weight: nil
|
|
118
|
+
)
|
|
119
|
+
case "heading":
|
|
120
|
+
// Level N is the base size scaled N-1 times, so a single pair of style
|
|
121
|
+
// values covers all six.
|
|
122
|
+
var size = style.headingFontSize
|
|
123
|
+
for _ in 1..<max(depth, 1) {
|
|
124
|
+
size *= style.headingScale
|
|
125
|
+
}
|
|
126
|
+
font = fonts.update(font, family: nil, size: size, weight: "bold")
|
|
127
|
+
case "emoji":
|
|
128
|
+
font = fonts.update(
|
|
129
|
+
font,
|
|
130
|
+
family: style.emojiFontFamily,
|
|
131
|
+
size: style.emojiFontSize,
|
|
132
|
+
weight: nil
|
|
133
|
+
)
|
|
134
|
+
default:
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
attributedString.addAttribute(.font, value: font as Any, range: range)
|
|
138
|
+
default:
|
|
139
|
+
break
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
switch type {
|
|
143
|
+
case "syntax":
|
|
144
|
+
attributedString.addAttribute(
|
|
145
|
+
.foregroundColor,
|
|
146
|
+
value: style.syntaxColor,
|
|
147
|
+
range: range
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
case "strikethrough":
|
|
151
|
+
attributedString.addAttribute(
|
|
152
|
+
.strikethroughStyle,
|
|
153
|
+
value: NSUnderlineStyle.single.rawValue,
|
|
154
|
+
range: range
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
case "code":
|
|
158
|
+
attributedString.addAttribute(
|
|
159
|
+
.foregroundColor,
|
|
160
|
+
value: style.codeColor,
|
|
161
|
+
range: range
|
|
162
|
+
)
|
|
163
|
+
attributedString.addAttribute(
|
|
164
|
+
.backgroundColor,
|
|
165
|
+
value: style.codeBackgroundColor,
|
|
166
|
+
range: range
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
case "mention-here":
|
|
170
|
+
applyMention(
|
|
171
|
+
to: attributedString,
|
|
172
|
+
range: range,
|
|
173
|
+
color: style.mentionHereColor,
|
|
174
|
+
backgroundColor: style.mentionHereBackgroundColor,
|
|
175
|
+
borderRadius: style.mentionHereBorderRadius
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
case "mention-user":
|
|
179
|
+
// TODO: change mention color when it mentions current user
|
|
180
|
+
applyMention(
|
|
181
|
+
to: attributedString,
|
|
182
|
+
range: range,
|
|
183
|
+
color: style.mentionUserColor,
|
|
184
|
+
backgroundColor: style.mentionUserBackgroundColor,
|
|
185
|
+
borderRadius: style.mentionUserBorderRadius
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
case "mention-report":
|
|
189
|
+
applyMention(
|
|
190
|
+
to: attributedString,
|
|
191
|
+
range: range,
|
|
192
|
+
color: style.mentionReportColor,
|
|
193
|
+
backgroundColor: style.mentionReportBackgroundColor,
|
|
194
|
+
borderRadius: style.mentionReportBorderRadius
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
case "link":
|
|
198
|
+
attributedString.addAttribute(
|
|
199
|
+
.underlineStyle,
|
|
200
|
+
value: NSUnderlineStyle.single.rawValue,
|
|
201
|
+
range: range
|
|
202
|
+
)
|
|
203
|
+
attributedString.addAttribute(
|
|
204
|
+
.foregroundColor,
|
|
205
|
+
value: style.linkColor,
|
|
206
|
+
range: range
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
case "pre":
|
|
210
|
+
attributedString.addAttribute(
|
|
211
|
+
.foregroundColor,
|
|
212
|
+
value: style.preColor,
|
|
213
|
+
range: range
|
|
214
|
+
)
|
|
215
|
+
// A fenced block's range starts with the newline after the opening fence;
|
|
216
|
+
// the background should not cover it.
|
|
217
|
+
let text = attributedString.string as NSString
|
|
218
|
+
let rangeForBackground =
|
|
219
|
+
text.character(at: range.location) == unichar(10)
|
|
220
|
+
? NSRange(location: range.location + 1, length: range.length - 1)
|
|
221
|
+
: range
|
|
222
|
+
attributedString.addAttribute(
|
|
223
|
+
.backgroundColor,
|
|
224
|
+
value: style.preBackgroundColor,
|
|
225
|
+
range: rangeForBackground
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
default:
|
|
229
|
+
break
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private static func applyMention(
|
|
234
|
+
to attributedString: NSMutableAttributedString,
|
|
235
|
+
range: NSRange,
|
|
236
|
+
color: UIColor,
|
|
237
|
+
backgroundColor: UIColor,
|
|
238
|
+
borderRadius: CGFloat
|
|
239
|
+
) {
|
|
240
|
+
attributedString.addAttribute(.foregroundColor, value: color, range: range)
|
|
241
|
+
|
|
242
|
+
let textBackground = MarkdownTextBackground()
|
|
243
|
+
textBackground.color = backgroundColor
|
|
244
|
+
textBackground.borderRadius = borderRadius
|
|
245
|
+
attributedString.addAttribute(
|
|
246
|
+
.marcusTextBackground,
|
|
247
|
+
value: textBackground,
|
|
248
|
+
range: range
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// MARK: - Block layout
|
|
253
|
+
|
|
254
|
+
/// The gutter a container reserves for itself, or nil if the type is not one.
|
|
255
|
+
private static func gutter(
|
|
256
|
+
for type: String,
|
|
257
|
+
depth: Int,
|
|
258
|
+
style: RCTMarcusStyle
|
|
259
|
+
) -> CGFloat? {
|
|
260
|
+
switch type {
|
|
261
|
+
case "blockquote":
|
|
262
|
+
return (style.blockquoteMarginLeft + style.blockquoteBorderWidth
|
|
263
|
+
+ style.blockquotePaddingLeft) * CGFloat(depth)
|
|
264
|
+
case "list-ordered":
|
|
265
|
+
return (style.orderedListMarginLeft + style.orderedListPaddingLeft)
|
|
266
|
+
* CGFloat(depth)
|
|
267
|
+
case "list-unordered":
|
|
268
|
+
return (style.unorderedListMarginLeft + style.unorderedListPaddingLeft)
|
|
269
|
+
* CGFloat(depth)
|
|
270
|
+
default:
|
|
271
|
+
return nil
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/// Places the containers of one line, left to right.
|
|
276
|
+
///
|
|
277
|
+
/// Each container reserves a gutter and is then followed by its own marker,
|
|
278
|
+
/// which is text and so has to be stepped over rather than reserved: the
|
|
279
|
+
/// container nested inside it starts after the marker, not in front of it.
|
|
280
|
+
/// That is what puts a quote's ribbons after a list bullet, and a list's
|
|
281
|
+
/// indent after a quote's `>`.
|
|
282
|
+
///
|
|
283
|
+
/// A line continuing a block carries no marker of its own, and reuses the one
|
|
284
|
+
/// that opened it -- otherwise its text, and any ribbon beside it, would sit
|
|
285
|
+
/// at a different offset than the line above and break the block in two.
|
|
286
|
+
private struct BlockLayout {
|
|
287
|
+
/// Last marker seen for each container type, for the lines that continue it.
|
|
288
|
+
private var markers: [String: NSRange] = [:]
|
|
289
|
+
private var lineStart = NSNotFound
|
|
290
|
+
/// Offset reached so far, from the paragraph's own left edge.
|
|
291
|
+
private var offset: CGFloat = 0
|
|
292
|
+
/// Where the line's first marker begins, if it has one. The text starts
|
|
293
|
+
/// there; everything past it is held open with padding instead.
|
|
294
|
+
private var textStart: CGFloat?
|
|
295
|
+
/// Marker of the container placed most recently, if it is on this line and
|
|
296
|
+
/// so has something to pad.
|
|
297
|
+
private var padded: NSRange?
|
|
298
|
+
|
|
299
|
+
mutating func add(
|
|
300
|
+
_ type: String,
|
|
301
|
+
gutter: CGFloat,
|
|
302
|
+
depth: Int,
|
|
303
|
+
prefix: NSRange?,
|
|
304
|
+
to attributedString: NSMutableAttributedString,
|
|
305
|
+
range: NSRange,
|
|
306
|
+
defaultTextAttributes: [NSAttributedString.Key: Any]
|
|
307
|
+
) {
|
|
308
|
+
if range.location != lineStart {
|
|
309
|
+
lineStart = range.location
|
|
310
|
+
offset = MarkdownFormatter.baseIndent(
|
|
311
|
+
of: attributedString,
|
|
312
|
+
at: range.location,
|
|
313
|
+
defaultTextAttributes: defaultTextAttributes
|
|
314
|
+
)
|
|
315
|
+
textStart = nil
|
|
316
|
+
padded = nil
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if let padded {
|
|
320
|
+
MarkdownFormatter.pad(padded, by: gutter, in: attributedString)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if type == "blockquote" {
|
|
324
|
+
attributedString.addAttribute(
|
|
325
|
+
.marcusBlockquoteDepth,
|
|
326
|
+
value: depth,
|
|
327
|
+
range: range
|
|
328
|
+
)
|
|
329
|
+
attributedString.addAttribute(
|
|
330
|
+
.marcusBlockquoteIndent,
|
|
331
|
+
value: offset,
|
|
332
|
+
range: range
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
offset += gutter
|
|
337
|
+
|
|
338
|
+
if let prefix {
|
|
339
|
+
markers[type] = prefix
|
|
340
|
+
if textStart == nil {
|
|
341
|
+
textStart = offset
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if let marker = prefix ?? markers[type] {
|
|
346
|
+
offset += MarkdownFormatter.measure(marker, in: attributedString)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
padded = prefix
|
|
350
|
+
MarkdownFormatter.setIndent(
|
|
351
|
+
firstLine: textStart ?? offset,
|
|
352
|
+
rest: offset,
|
|
353
|
+
of: attributedString,
|
|
354
|
+
range: range,
|
|
355
|
+
defaultTextAttributes: defaultTextAttributes
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// MARK: - Indents
|
|
361
|
+
|
|
362
|
+
private static func baseIndent(
|
|
363
|
+
of attributedString: NSAttributedString,
|
|
364
|
+
at location: Int,
|
|
365
|
+
defaultTextAttributes: [NSAttributedString.Key: Any]
|
|
366
|
+
) -> CGFloat {
|
|
367
|
+
paragraphStyle(
|
|
368
|
+
of: attributedString,
|
|
369
|
+
at: location,
|
|
370
|
+
defaultTextAttributes: defaultTextAttributes
|
|
371
|
+
).headIndent
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private static func setIndent(
|
|
375
|
+
firstLine: CGFloat,
|
|
376
|
+
rest: CGFloat,
|
|
377
|
+
of attributedString: NSMutableAttributedString,
|
|
378
|
+
range: NSRange,
|
|
379
|
+
defaultTextAttributes: [NSAttributedString.Key: Any]
|
|
380
|
+
) {
|
|
381
|
+
let style = paragraphStyle(
|
|
382
|
+
of: attributedString,
|
|
383
|
+
at: range.location,
|
|
384
|
+
defaultTextAttributes: defaultTextAttributes
|
|
385
|
+
)
|
|
386
|
+
style.firstLineHeadIndent = firstLine
|
|
387
|
+
style.headIndent = rest
|
|
388
|
+
|
|
389
|
+
attributedString.addAttribute(.paragraphStyle, value: style, range: range)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private static func paragraphStyle(
|
|
393
|
+
of attributedString: NSAttributedString,
|
|
394
|
+
at location: Int,
|
|
395
|
+
defaultTextAttributes: [NSAttributedString.Key: Any]
|
|
396
|
+
) -> NSMutableParagraphStyle {
|
|
397
|
+
let existing =
|
|
398
|
+
attributedString.attribute(
|
|
399
|
+
.paragraphStyle,
|
|
400
|
+
at: location,
|
|
401
|
+
effectiveRange: nil
|
|
402
|
+
) as? NSParagraphStyle
|
|
403
|
+
?? defaultTextAttributes[.paragraphStyle] as? NSParagraphStyle
|
|
404
|
+
|
|
405
|
+
return (existing?.mutableCopy() as? NSMutableParagraphStyle)
|
|
406
|
+
?? NSMutableParagraphStyle()
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// MARK: - Measurement
|
|
410
|
+
|
|
411
|
+
/// Opens `width` of space after `range` without touching its glyphs, so a
|
|
412
|
+
/// nested container's gutter can sit between one marker and the next.
|
|
413
|
+
private static func pad(
|
|
414
|
+
_ range: NSRange,
|
|
415
|
+
by width: CGFloat,
|
|
416
|
+
in attributedString: NSMutableAttributedString
|
|
417
|
+
) {
|
|
418
|
+
guard width > 0, range.length > 0 else { return }
|
|
419
|
+
|
|
420
|
+
attributedString.addAttribute(
|
|
421
|
+
.kern,
|
|
422
|
+
value: width,
|
|
423
|
+
range: NSRange(location: NSMaxRange(range) - 1, length: 1)
|
|
424
|
+
)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/// Width `range` takes up mid-line, rounded up to a whole point.
|
|
428
|
+
///
|
|
429
|
+
/// A sentinel is appended and its width taken back off again because `size()`
|
|
430
|
+
/// measures the range as a line of its own and drops trailing whitespace --
|
|
431
|
+
/// which a list-item prefix always ends in.
|
|
432
|
+
///
|
|
433
|
+
/// Rounding is what keeps a quote's ribbon in one piece. TextKit snaps every
|
|
434
|
+
/// line's origin to the device pixel grid, so a line indented past a marker
|
|
435
|
+
/// and a line indented past nothing land on different sides of a fractional
|
|
436
|
+
/// width, and the bar drawn beside them breaks by a pixel. A whole point is a
|
|
437
|
+
/// whole number of pixels at every screen scale, so there is nothing left to
|
|
438
|
+
/// snap. Rounding up rather than down also keeps the gap from eating into the
|
|
439
|
+
/// marker it was measured from.
|
|
440
|
+
private static func measure(
|
|
441
|
+
_ range: NSRange,
|
|
442
|
+
in attributedString: NSAttributedString
|
|
443
|
+
) -> CGFloat {
|
|
444
|
+
let text = NSMutableAttributedString(
|
|
445
|
+
attributedString: attributedString.attributedSubstring(from: range)
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
guard text.length > 0 else { return 0 }
|
|
449
|
+
|
|
450
|
+
let full = NSRange(location: 0, length: text.length)
|
|
451
|
+
// Indents belong to the paragraph, not to the run being measured, and the
|
|
452
|
+
// padding opened after the marker is the next container's gutter rather
|
|
453
|
+
// than part of the marker itself.
|
|
454
|
+
text.removeAttribute(.paragraphStyle, range: full)
|
|
455
|
+
text.removeAttribute(.kern, range: full)
|
|
456
|
+
|
|
457
|
+
let sentinel = NSAttributedString(
|
|
458
|
+
string: "|",
|
|
459
|
+
attributes: text.attributes(at: text.length - 1, effectiveRange: nil)
|
|
460
|
+
)
|
|
461
|
+
text.append(sentinel)
|
|
462
|
+
|
|
463
|
+
return (text.size().width - sentinel.size().width).rounded(.up)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// MARK: - Baseline
|
|
467
|
+
|
|
468
|
+
/// Re-centres text vertically within an explicit `lineHeight` after the fonts
|
|
469
|
+
/// above have changed the natural line height.
|
|
470
|
+
private static func applyBaselineOffset(
|
|
471
|
+
to attributedText: NSMutableAttributedString,
|
|
472
|
+
range: NSRange
|
|
473
|
+
) {
|
|
474
|
+
var maximumLineHeight: CGFloat = 0
|
|
475
|
+
attributedText.enumerateAttribute(
|
|
476
|
+
.paragraphStyle,
|
|
477
|
+
in: range,
|
|
478
|
+
options: .longestEffectiveRangeNotRequired
|
|
479
|
+
) { value, _, _ in
|
|
480
|
+
guard let paragraphStyle = value as? NSParagraphStyle else { return }
|
|
481
|
+
maximumLineHeight = max(
|
|
482
|
+
paragraphStyle.maximumLineHeight,
|
|
483
|
+
maximumLineHeight
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// `lineHeight` was not specified, nothing to do.
|
|
488
|
+
guard maximumLineHeight != 0 else { return }
|
|
489
|
+
|
|
490
|
+
var maximumFontLineHeight: CGFloat = 0
|
|
491
|
+
attributedText.enumerateAttribute(
|
|
492
|
+
.font,
|
|
493
|
+
in: range,
|
|
494
|
+
options: .longestEffectiveRangeNotRequired
|
|
495
|
+
) { value, _, _ in
|
|
496
|
+
guard let font = value as? UIFont else { return }
|
|
497
|
+
maximumFontLineHeight = max(font.lineHeight, maximumFontLineHeight)
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
guard maximumLineHeight >= maximumFontLineHeight else { return }
|
|
501
|
+
|
|
502
|
+
let baselineOffset = (maximumLineHeight - maximumFontLineHeight) / 2.0
|
|
503
|
+
attributedText.addAttribute(
|
|
504
|
+
.baselineOffset,
|
|
505
|
+
value: baselineOffset,
|
|
506
|
+
range: range
|
|
507
|
+
)
|
|
508
|
+
}
|
|
509
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
// RCTLogWarn is a variadic C macro, so it cannot be called from Swift. This is
|
|
6
|
+
// the thinnest possible shim over it, kept so parser warnings still reach LogBox.
|
|
7
|
+
FOUNDATION_EXPORT void
|
|
8
|
+
MarkdownLogWarn(NSString *message) NS_SWIFT_NAME(markdownLogWarn(_:));
|
|
9
|
+
|
|
10
|
+
NS_ASSUME_NONNULL_END
|