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,30 @@
|
|
|
1
|
+
type MarkdownType =
|
|
2
|
+
| "bold"
|
|
3
|
+
| "italic"
|
|
4
|
+
| "strikethrough"
|
|
5
|
+
| "emoji"
|
|
6
|
+
| "mention-here"
|
|
7
|
+
| "mention-user"
|
|
8
|
+
| "mention-short"
|
|
9
|
+
| "mention-report"
|
|
10
|
+
| "link"
|
|
11
|
+
| "code"
|
|
12
|
+
| "pre"
|
|
13
|
+
| "blockquote"
|
|
14
|
+
| "list-ordered"
|
|
15
|
+
| "list-unordered"
|
|
16
|
+
| "block-prefix"
|
|
17
|
+
| "heading"
|
|
18
|
+
| "syntax"
|
|
19
|
+
| "inline-image"
|
|
20
|
+
| "codeblock"
|
|
21
|
+
|
|
22
|
+
interface MarkdownRange {
|
|
23
|
+
type: MarkdownType
|
|
24
|
+
start: number
|
|
25
|
+
length: number
|
|
26
|
+
depth?: number
|
|
27
|
+
syntaxType?: "opening" | "closing"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type { MarkdownType, MarkdownRange }
|
package/src/global.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module "*.css"
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { default as MarkdownTextInput, getWorkletRuntime } from "./MarkdownTextInput"
|
|
2
|
+
export type { MarkdownTextInputProps, MarkdownStyle } from "./MarkdownTextInput"
|
|
3
|
+
export type { MarkdownType, MarkdownRange } from "./commonTypes"
|
|
4
|
+
export { parse as parser } from "./parser"
|
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
import emojiRegex from "emoji-regex"
|
|
2
|
+
import { parse, postprocess, preprocess } from "micromark"
|
|
3
|
+
import { gfmAutolinkLiteral } from "micromark-extension-gfm-autolink-literal"
|
|
4
|
+
import { gfmStrikethrough } from "micromark-extension-gfm-strikethrough"
|
|
5
|
+
import type { Token, TokenType } from "micromark-util-types"
|
|
6
|
+
|
|
7
|
+
import type { MarkdownRange, MarkdownType } from "../commonTypes"
|
|
8
|
+
import { mentions } from "./micromark-extension-mentions"
|
|
9
|
+
|
|
10
|
+
const EMOJI_TYPE = "emoji"
|
|
11
|
+
const SYNTAX_TYPE = "syntax"
|
|
12
|
+
|
|
13
|
+
const MAX_DEPTH = 6
|
|
14
|
+
|
|
15
|
+
const TOKENS: {
|
|
16
|
+
syntax: Set<TokenType>
|
|
17
|
+
block: Partial<Record<TokenType, MarkdownType>>
|
|
18
|
+
special: Partial<Record<TokenType, MarkdownType>>
|
|
19
|
+
scope: Partial<Record<TokenType, MarkdownType>>
|
|
20
|
+
content: Partial<Record<TokenType, MarkdownType>>
|
|
21
|
+
link: Partial<Record<TokenType, MarkdownType>>
|
|
22
|
+
} = {
|
|
23
|
+
syntax: new Set([
|
|
24
|
+
// Emphasis.
|
|
25
|
+
"emphasisSequence",
|
|
26
|
+
"strongSequence",
|
|
27
|
+
"strikethroughSequence",
|
|
28
|
+
|
|
29
|
+
// Headings.
|
|
30
|
+
"atxHeadingSequence",
|
|
31
|
+
"setextHeadingLineSequence",
|
|
32
|
+
|
|
33
|
+
// Block containers.
|
|
34
|
+
"blockQuoteMarker",
|
|
35
|
+
|
|
36
|
+
// Lists.
|
|
37
|
+
"listItemMarker",
|
|
38
|
+
"listItemValue",
|
|
39
|
+
|
|
40
|
+
// Thematic breaks.
|
|
41
|
+
"thematicBreakSequence",
|
|
42
|
+
|
|
43
|
+
// Links/images.
|
|
44
|
+
"labelMarker",
|
|
45
|
+
"labelImageMarker",
|
|
46
|
+
"referenceMarker",
|
|
47
|
+
"resourceMarker",
|
|
48
|
+
|
|
49
|
+
// Link/image destinations.
|
|
50
|
+
"resourceDestinationLiteralMarker",
|
|
51
|
+
|
|
52
|
+
// Titles.
|
|
53
|
+
"resourceTitleMarker",
|
|
54
|
+
|
|
55
|
+
// Definitions.
|
|
56
|
+
"definitionLabelMarker",
|
|
57
|
+
"definitionMarker",
|
|
58
|
+
"definitionDestinationLiteralMarker",
|
|
59
|
+
"definitionTitleMarker",
|
|
60
|
+
|
|
61
|
+
// Autolinks.
|
|
62
|
+
"autolinkMarker",
|
|
63
|
+
|
|
64
|
+
// Escapes.
|
|
65
|
+
"escapeMarker",
|
|
66
|
+
|
|
67
|
+
// Hard breaks.
|
|
68
|
+
"hardBreakEscape",
|
|
69
|
+
"hardBreakTrailing",
|
|
70
|
+
|
|
71
|
+
// Code.
|
|
72
|
+
"codeTextSequence",
|
|
73
|
+
"codeFencedFenceSequence",
|
|
74
|
+
|
|
75
|
+
// Character references.
|
|
76
|
+
"characterReferenceMarker",
|
|
77
|
+
"characterReferenceMarkerNumeric",
|
|
78
|
+
"characterReferenceMarkerHexadecimal",
|
|
79
|
+
]),
|
|
80
|
+
block: {
|
|
81
|
+
blockQuote: "blockquote",
|
|
82
|
+
|
|
83
|
+
// Ordered and unordered stay separate types so a renderer can tell them
|
|
84
|
+
// apart. They nest independently: a line inside both carries one range of
|
|
85
|
+
// each, and the indents add up.
|
|
86
|
+
listOrdered: "list-ordered",
|
|
87
|
+
listUnordered: "list-unordered",
|
|
88
|
+
},
|
|
89
|
+
special: {
|
|
90
|
+
codeFenced: "codeblock",
|
|
91
|
+
codeIndented: "codeblock",
|
|
92
|
+
image: "inline-image",
|
|
93
|
+
},
|
|
94
|
+
scope: {
|
|
95
|
+
emphasis: "italic",
|
|
96
|
+
strong: "bold",
|
|
97
|
+
strikethrough: "strikethrough",
|
|
98
|
+
|
|
99
|
+
// Headings are absent: their level is only known once the marker token
|
|
100
|
+
// arrives, so they are emitted from emitHeading() instead.
|
|
101
|
+
},
|
|
102
|
+
content: {
|
|
103
|
+
mention: "mention-user",
|
|
104
|
+
|
|
105
|
+
codeTextData: "code",
|
|
106
|
+
codeFlowValue: "pre",
|
|
107
|
+
|
|
108
|
+
// TODO:
|
|
109
|
+
// htmlFlow: 'html',
|
|
110
|
+
// htmlText: 'html',
|
|
111
|
+
},
|
|
112
|
+
link: {
|
|
113
|
+
autolinkProtocol: "link",
|
|
114
|
+
autolinkEmail: "link",
|
|
115
|
+
|
|
116
|
+
literalAutolinkEmail: "link",
|
|
117
|
+
literalAutolinkHttp: "link",
|
|
118
|
+
literalAutolinkWww: "link",
|
|
119
|
+
|
|
120
|
+
resourceDestinationString: "link",
|
|
121
|
+
definitionDestinationString: "link",
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const BLOCK_PREFIX = "block-prefix"
|
|
126
|
+
|
|
127
|
+
const EMOJI: {
|
|
128
|
+
scan: Set<TokenType>
|
|
129
|
+
exclude: Set<MarkdownType>
|
|
130
|
+
pattern: RegExp
|
|
131
|
+
} = {
|
|
132
|
+
scan: new Set([
|
|
133
|
+
// Normal text.
|
|
134
|
+
"data",
|
|
135
|
+
|
|
136
|
+
// Inline code content.
|
|
137
|
+
"codeTextData",
|
|
138
|
+
|
|
139
|
+
// Fenced/indented code content.
|
|
140
|
+
"codeFlowValue",
|
|
141
|
+
|
|
142
|
+
// HTML source.
|
|
143
|
+
"htmlFlowData",
|
|
144
|
+
"htmlTextData",
|
|
145
|
+
|
|
146
|
+
// Link destinations.
|
|
147
|
+
"resourceDestinationString",
|
|
148
|
+
"definitionDestinationString",
|
|
149
|
+
|
|
150
|
+
// Link/image titles, if present.
|
|
151
|
+
"resourceTitleString",
|
|
152
|
+
"definitionTitleString",
|
|
153
|
+
|
|
154
|
+
// Autolinks.
|
|
155
|
+
"autolinkProtocol",
|
|
156
|
+
"autolinkEmail",
|
|
157
|
+
]),
|
|
158
|
+
exclude: new Set(["bold", "italic", "strikethrough"]),
|
|
159
|
+
pattern: emojiRegex(),
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Bundler shenanigans: when bundling esm export ends up in wrapper function body
|
|
163
|
+
// which is a syntax error so don't export anything at all.
|
|
164
|
+
// Instead declare export so typing still works.
|
|
165
|
+
declare module "./index" {
|
|
166
|
+
export function parse(markdown: string): MarkdownRange[]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
declare global {
|
|
170
|
+
function __parse__micromark(markdown: string): MarkdownRange[]
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
globalThis.__parse__micromark = function (markdown: string): MarkdownRange[] {
|
|
174
|
+
// micromark's preprocessor drops a leading BOM, so every offset it reports is
|
|
175
|
+
// one short of the caller's string -- a document that opens with one had every
|
|
176
|
+
// range shifted left by a character, leaving the last one uncovered. Parse the
|
|
177
|
+
// stripped text and shift the ranges back at the end rather than second-
|
|
178
|
+
// guessing offsets at each of the two dozen places they are read.
|
|
179
|
+
const bomLength = markdown.charCodeAt(0) === 0xfeff ? 1 : 0
|
|
180
|
+
|
|
181
|
+
if (bomLength > 0) {
|
|
182
|
+
markdown = markdown.slice(bomLength)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const extensions = [gfmAutolinkLiteral(), gfmStrikethrough(), mentions()]
|
|
186
|
+
const ranges: MarkdownRange[] = []
|
|
187
|
+
// Counted block containers, innermost last. Never exceeds MAX_DEPTH per type.
|
|
188
|
+
const openBlocks: MarkdownType[] = []
|
|
189
|
+
// One entry per open container, recording whether it made it into openBlocks.
|
|
190
|
+
const countedBlocks: boolean[] = []
|
|
191
|
+
// Heading awaiting the marker that states its level.
|
|
192
|
+
let pendingHeading: Token | null = null
|
|
193
|
+
// The container markers on the line being built, in the order they appear.
|
|
194
|
+
// Positional rather than keyed by type: one type can open more than once on a
|
|
195
|
+
// line, and the occurrences need not be adjacent (`> - > `). Cleared per
|
|
196
|
+
// line -- a container whose marker was on an earlier line is being continued,
|
|
197
|
+
// not opened.
|
|
198
|
+
const markers: { type: MarkdownType; start: number; end: number }[] = []
|
|
199
|
+
const events = postprocess(
|
|
200
|
+
parse({ extensions }).document().write(preprocess()(markdown, "utf-8", true)),
|
|
201
|
+
)
|
|
202
|
+
let lineStart = 0
|
|
203
|
+
|
|
204
|
+
for (const event of events) {
|
|
205
|
+
if (event[0] === "enter") {
|
|
206
|
+
enter(event[1])
|
|
207
|
+
} else {
|
|
208
|
+
exit(event[1])
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
excludeEmoji()
|
|
213
|
+
|
|
214
|
+
if (bomLength > 0) {
|
|
215
|
+
for (const range of ranges) {
|
|
216
|
+
range.start += bomLength
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return ranges
|
|
221
|
+
|
|
222
|
+
function enter(token: Token) {
|
|
223
|
+
if (EMOJI.scan.has(token.type)) {
|
|
224
|
+
emitEmoji(token)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// The heading itself opens before its marker, so it is held until the
|
|
228
|
+
// marker states the level. Both marker types also emit `syntax`, so this
|
|
229
|
+
// falls through rather than returning.
|
|
230
|
+
if (token.type === "atxHeading" || token.type === "setextHeading") {
|
|
231
|
+
pendingHeading = token
|
|
232
|
+
} else if (token.type === "atxHeadingSequence") {
|
|
233
|
+
// ATX level is the run of `#`. A closing run is ignored: the heading has
|
|
234
|
+
// already been emitted by then.
|
|
235
|
+
emitHeading(token.end.offset - token.start.offset)
|
|
236
|
+
} else if (token.type === "setextHeadingLineSequence") {
|
|
237
|
+
emitHeading(markdown.charCodeAt(token.start.offset) === 61 ? 1 : 2)
|
|
238
|
+
} else if (token.type === "listItemPrefix") {
|
|
239
|
+
const list = openBlocks[openBlocks.length - 1]
|
|
240
|
+
|
|
241
|
+
// Past the depth cap the item's own list was never opened, so its marker
|
|
242
|
+
// would be recorded against whatever encloses it.
|
|
243
|
+
if (list === "list-ordered" || list === "list-unordered") {
|
|
244
|
+
markers.push({ type: list, start: token.start.offset, end: token.end.offset })
|
|
245
|
+
}
|
|
246
|
+
} else if (token.type === "blockQuotePrefix") {
|
|
247
|
+
// One per level, so `>>> ` arrives as three markers; flushLine folds a
|
|
248
|
+
// contiguous run back into a single prefix.
|
|
249
|
+
markers.push({
|
|
250
|
+
type: "blockquote",
|
|
251
|
+
start: token.start.offset,
|
|
252
|
+
end: token.end.offset,
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const block = TOKENS.block[token.type]
|
|
257
|
+
|
|
258
|
+
if (block) {
|
|
259
|
+
// Past the cap the container is ignored outright rather than counted and
|
|
260
|
+
// clamped on the way out, so nesting cannot grow the stack or the work
|
|
261
|
+
// flushLine does per line.
|
|
262
|
+
const counted = depthOf(block) < MAX_DEPTH
|
|
263
|
+
countedBlocks.push(counted)
|
|
264
|
+
|
|
265
|
+
if (counted) {
|
|
266
|
+
openBlocks.push(block)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (token.type === "lineEnding" || token.type === "lineEndingBlank") {
|
|
273
|
+
flushLine(token.start.offset)
|
|
274
|
+
|
|
275
|
+
// The token can reach past the break itself: micromark folds the next
|
|
276
|
+
// line's container prefix into it, so `\n>` arrives as one lineEnding.
|
|
277
|
+
// Resuming at token.end would swallow that line, leaving a gap in the
|
|
278
|
+
// quote. Resume just after the break instead.
|
|
279
|
+
let next = token.start.offset
|
|
280
|
+
if (markdown.charCodeAt(next) === 13) {
|
|
281
|
+
next++
|
|
282
|
+
}
|
|
283
|
+
if (markdown.charCodeAt(next) === 10) {
|
|
284
|
+
next++
|
|
285
|
+
}
|
|
286
|
+
lineStart = next > token.start.offset ? next : token.end.offset
|
|
287
|
+
|
|
288
|
+
return
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (TOKENS.special[token.type]) {
|
|
292
|
+
emit(token, TOKENS.special[token.type]!)
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (TOKENS.syntax.has(token.type)) {
|
|
297
|
+
emit(token, SYNTAX_TYPE)
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (TOKENS.scope[token.type]) {
|
|
302
|
+
emit(token, TOKENS.scope[token.type]!)
|
|
303
|
+
return
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (TOKENS.content[token.type]) {
|
|
307
|
+
if (token.type === "codeFlowValue") {
|
|
308
|
+
emitCodeBlock(token, TOKENS.content[token.type]!)
|
|
309
|
+
} else {
|
|
310
|
+
emit(token, TOKENS.content[token.type]!)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (TOKENS.link[token.type]) {
|
|
317
|
+
emit(token, TOKENS.link[token.type]!)
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function exit(token: Token) {
|
|
323
|
+
if (!TOKENS.block[token.type]) {
|
|
324
|
+
return
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
flushLine(token.end.offset)
|
|
328
|
+
|
|
329
|
+
// Ignored containers were never pushed, so only counted ones pop. Skipped
|
|
330
|
+
// ones are always innermost, so this stays balanced.
|
|
331
|
+
if (countedBlocks.pop()) {
|
|
332
|
+
openBlocks.pop()
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function emitHeading(level: number) {
|
|
337
|
+
if (!pendingHeading) {
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
push(
|
|
342
|
+
pendingHeading.start.offset,
|
|
343
|
+
pendingHeading.end.offset,
|
|
344
|
+
"heading",
|
|
345
|
+
level > MAX_DEPTH ? MAX_DEPTH : level,
|
|
346
|
+
)
|
|
347
|
+
pendingHeading = null
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function depthOf(type: MarkdownType) {
|
|
351
|
+
let depth = 0
|
|
352
|
+
|
|
353
|
+
for (let i = 0; i < openBlocks.length; i++) {
|
|
354
|
+
if (openBlocks[i] === type) {
|
|
355
|
+
depth++
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return depth
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Emits the containers the finished line sits in, each preceded by the run of
|
|
364
|
+
* text its own marker occupies there.
|
|
365
|
+
*
|
|
366
|
+
* The markers are real characters that the containers nested inside them are
|
|
367
|
+
* laid out around, and how wide they render is a question only the platform
|
|
368
|
+
* can answer -- so the offsets are handed over and the arithmetic is done
|
|
369
|
+
* where the font is known.
|
|
370
|
+
*
|
|
371
|
+
* This prefix-then-container order is the contract the native formatters read
|
|
372
|
+
* (`apple/MarkdownFormatter.swift`, `android/.../MarkdownFormatter.kt`): each
|
|
373
|
+
* holds the `block-prefix` it just saw and hands it to the next container, so
|
|
374
|
+
* laying out a line is one left-to-right walk. It is deliberately *not* tree
|
|
375
|
+
* order -- a container arrives after the marker it encloses -- so the web
|
|
376
|
+
* builder re-sorts into containment order via `sortRanges`. Reordering the
|
|
377
|
+
* emission to suit web would break the pairing on both native platforms.
|
|
378
|
+
*/
|
|
379
|
+
function flushLine(end: number) {
|
|
380
|
+
if (openBlocks.length > 0 && end > lineStart) {
|
|
381
|
+
let prefixStart = lineStart
|
|
382
|
+
|
|
383
|
+
for (let i = 0; i < openBlocks.length; i++) {
|
|
384
|
+
const type = openBlocks[i]!
|
|
385
|
+
|
|
386
|
+
// Emit each distinct type once, at its first occurrence.
|
|
387
|
+
if (openBlocks.indexOf(type) !== i) {
|
|
388
|
+
continue
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// This type's next marker at or after the walk position. Taking the
|
|
392
|
+
// last one on the line instead -- which is what keying by type alone
|
|
393
|
+
// did -- stretches the prefix across any marker that sits between two
|
|
394
|
+
// occurrences, so `> - > ` gave the quote a `"> - > "` prefix and left
|
|
395
|
+
// the list with none at all.
|
|
396
|
+
const index = markers.findIndex((m) => m.type === type && m.start >= prefixStart)
|
|
397
|
+
|
|
398
|
+
if (index !== -1) {
|
|
399
|
+
// A contiguous run of one type is a single prefix: `>>> ` is three
|
|
400
|
+
// markers but one step over on the way to the content.
|
|
401
|
+
let markerEnd = markers[index]!.end
|
|
402
|
+
|
|
403
|
+
for (let j = index + 1; j < markers.length; j++) {
|
|
404
|
+
const marker = markers[j]!
|
|
405
|
+
|
|
406
|
+
if (marker.type !== type || marker.start !== markerEnd) {
|
|
407
|
+
break
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
markerEnd = marker.end
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
push(prefixStart, markerEnd, BLOCK_PREFIX)
|
|
414
|
+
prefixStart = markerEnd
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
push(lineStart, end, type, depthOf(type))
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (end > lineStart) {
|
|
422
|
+
lineStart = end
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
markers.length = 0
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function emit(token: Token, type: MarkdownType) {
|
|
429
|
+
push(token.start.offset, token.end.offset, type)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function emitCodeBlock(token: Token, type: MarkdownType) {
|
|
433
|
+
let end = token.end.offset
|
|
434
|
+
|
|
435
|
+
if (markdown.charCodeAt(end) === 13) {
|
|
436
|
+
// CRLF
|
|
437
|
+
end += markdown.charCodeAt(end + 1) === 10 ? 2 : 1
|
|
438
|
+
} else if (markdown.charCodeAt(end) === 10) {
|
|
439
|
+
// LF
|
|
440
|
+
end++
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
push(token.start.offset, end, type)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function emitEmoji(token: Token) {
|
|
447
|
+
const start = token.start.offset
|
|
448
|
+
const end = token.end.offset
|
|
449
|
+
|
|
450
|
+
if (start >= end) return
|
|
451
|
+
|
|
452
|
+
const value = markdown.slice(start, end)
|
|
453
|
+
|
|
454
|
+
EMOJI.pattern.lastIndex = 0
|
|
455
|
+
|
|
456
|
+
let match: RegExpExecArray | null = null
|
|
457
|
+
|
|
458
|
+
while ((match = EMOJI.pattern.exec(value)) !== null) {
|
|
459
|
+
const emojiStart = start + match.index
|
|
460
|
+
const emojiEnd = emojiStart + match[0].length
|
|
461
|
+
|
|
462
|
+
push(emojiStart, emojiEnd, EMOJI_TYPE)
|
|
463
|
+
|
|
464
|
+
if (match[0].length === 0) {
|
|
465
|
+
EMOJI.pattern.lastIndex++
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function push(start: number, end: number, type: MarkdownType, depth?: number) {
|
|
471
|
+
if (start >= end) return
|
|
472
|
+
|
|
473
|
+
const previous = ranges[ranges.length - 1]
|
|
474
|
+
|
|
475
|
+
if (
|
|
476
|
+
depth === undefined &&
|
|
477
|
+
// Two containers opening on one line put their prefixes back to back.
|
|
478
|
+
// Merging them would lose which prefix belongs to which container.
|
|
479
|
+
type !== BLOCK_PREFIX &&
|
|
480
|
+
// Adjacent syntax belongs to whatever encloses it, and neighbors often sit in different
|
|
481
|
+
// enclosures: `` >` `` is a quote marker then a code fence, `` ~a~` `` closes a
|
|
482
|
+
// strikethrough then opens one. A merged range would cover both and so straddle the end of
|
|
483
|
+
// the first one's container -- overlapping it without containing it or being contained by
|
|
484
|
+
// it, which is not a tree and renders the shared characters once per range. Runs that do
|
|
485
|
+
// share an enclosure, like `>>`, cost only an extra span.
|
|
486
|
+
type !== SYNTAX_TYPE &&
|
|
487
|
+
previous &&
|
|
488
|
+
previous.type === type &&
|
|
489
|
+
previous.start + previous.length === start
|
|
490
|
+
) {
|
|
491
|
+
previous.length = end - previous.start
|
|
492
|
+
return
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const range: MarkdownRange = {
|
|
496
|
+
start,
|
|
497
|
+
length: end - start,
|
|
498
|
+
type,
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (depth !== undefined) {
|
|
502
|
+
range.depth = depth
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
ranges.push(range)
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function excludeEmoji() {
|
|
509
|
+
if (!ranges.find((r) => r.type === "emoji")) return
|
|
510
|
+
|
|
511
|
+
const result: MarkdownRange[] = []
|
|
512
|
+
|
|
513
|
+
for (const range of ranges) {
|
|
514
|
+
if (!EMOJI.exclude.has(range.type)) {
|
|
515
|
+
result.push(range)
|
|
516
|
+
continue
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const end = range.start + range.length
|
|
520
|
+
let cursor = range.start
|
|
521
|
+
|
|
522
|
+
for (const emoji of ranges) {
|
|
523
|
+
if (emoji.type !== "emoji") continue
|
|
524
|
+
|
|
525
|
+
const emojiStart = emoji.start
|
|
526
|
+
const emojiEnd = emojiStart + emoji.length
|
|
527
|
+
|
|
528
|
+
if (emojiEnd <= cursor) continue
|
|
529
|
+
if (emojiStart >= end) break
|
|
530
|
+
|
|
531
|
+
if (emojiStart > cursor) {
|
|
532
|
+
result.push({
|
|
533
|
+
start: cursor,
|
|
534
|
+
length: emojiStart - cursor,
|
|
535
|
+
type: range.type,
|
|
536
|
+
})
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
cursor = Math.max(cursor, emojiEnd)
|
|
540
|
+
|
|
541
|
+
if (cursor >= end) break
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (cursor < end) {
|
|
545
|
+
result.push({
|
|
546
|
+
start: cursor,
|
|
547
|
+
length: end - cursor,
|
|
548
|
+
type: range.type,
|
|
549
|
+
})
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
ranges.length = 0
|
|
554
|
+
ranges.push(...result)
|
|
555
|
+
}
|
|
556
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Code, Effects, Extension, State } from "micromark-util-types"
|
|
2
|
+
|
|
3
|
+
const LETTER = /^\p{L}$/u
|
|
4
|
+
const WORD = /^[\p{L}\p{M}\p{N}_]$/u
|
|
5
|
+
const ALPHANUMERIC = /^[\p{L}\p{M}\p{N}]$/u
|
|
6
|
+
|
|
7
|
+
declare module "micromark-util-types" {
|
|
8
|
+
interface TokenTypeMap {
|
|
9
|
+
mention: "mention"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const mention = {
|
|
14
|
+
name: "mention",
|
|
15
|
+
tokenize,
|
|
16
|
+
} as const
|
|
17
|
+
|
|
18
|
+
export function mentions(): Extension {
|
|
19
|
+
return {
|
|
20
|
+
text: {
|
|
21
|
+
64: mention, // @
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function tokenize(effects: Effects, ok: State, nok: State): State {
|
|
27
|
+
return start
|
|
28
|
+
|
|
29
|
+
function start(code: Code): State {
|
|
30
|
+
if (code === null) {
|
|
31
|
+
return nok(code)!
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
effects.enter("mention")
|
|
35
|
+
effects.consume(code)
|
|
36
|
+
|
|
37
|
+
return first
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function first(code: Code): State {
|
|
41
|
+
if (code === null || !test(LETTER, code)) {
|
|
42
|
+
return nok(code)!
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
effects.consume(code)
|
|
46
|
+
|
|
47
|
+
return body
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function body(code: Code): State {
|
|
51
|
+
if (code !== null && test(WORD, code)) {
|
|
52
|
+
effects.consume(code)
|
|
53
|
+
return body
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (code === 45) {
|
|
57
|
+
// `-` is allowed only when followed by an alphanumeric character.
|
|
58
|
+
return hyphen
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
effects.exit("mention")
|
|
62
|
+
return ok(code)!
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function hyphen(code: Code): State {
|
|
66
|
+
if (code === null || !test(ALPHANUMERIC, code)) {
|
|
67
|
+
return nok(code)!
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
effects.consume(code)
|
|
71
|
+
|
|
72
|
+
return body
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function test(regex: RegExp, code: number): boolean {
|
|
77
|
+
if (code < 0 || code > 0x10ffff || (code >= 0xd800 && code <= 0xdfff)) {
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return regex.test(String.fromCodePoint(code))
|
|
82
|
+
}
|