react-native-marcus 0.1.1 → 0.2.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.
Files changed (294) hide show
  1. package/README.md +220 -63
  2. package/RNMarcus.podspec +6 -0
  3. package/android/src/main/java/app/getbullet/marcus/MarcusPackage.kt +2 -1
  4. package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorView.kt +168 -0
  5. package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManager.kt +38 -0
  6. package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManagerSpec.kt +19 -0
  7. package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorView.kt +23 -1
  8. package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPainter.kt +147 -0
  9. package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPass.kt +19 -0
  10. package/android/src/main/java/app/getbullet/marcus/MarkdownCopy.kt +87 -0
  11. package/android/src/main/java/app/getbullet/marcus/MarkdownFormatter.kt +652 -113
  12. package/android/src/main/java/app/getbullet/marcus/MarkdownRanges.kt +47 -0
  13. package/android/src/main/java/app/getbullet/marcus/MarkdownStyle.kt +140 -61
  14. package/android/src/main/java/app/getbullet/marcus/MarkdownUtils.kt +37 -7
  15. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownAbsoluteLineHeightSpan.kt +48 -0
  16. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundSpan.kt +102 -48
  17. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBlockIndentSpan.kt +22 -4
  18. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBulletSpan.kt +78 -0
  19. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpan.kt +177 -0
  20. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontStyleSpan.kt +32 -0
  21. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontWeightSpan.kt +34 -0
  22. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownGapSpan.kt +24 -11
  23. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownLetterSpacingSpan.kt +32 -0
  24. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownTextShadowSpan.kt +32 -0
  25. package/android/src/main/new_arch/CMakeLists.txt +17 -0
  26. package/android/src/main/new_arch/RNMarcusSpec.h +1 -0
  27. package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.cpp +235 -0
  28. package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.h +62 -0
  29. package/android/src/reactNativeVersionPatch/CustomMountingManager/81/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
  30. package/android/src/reactNativeVersionPatch/CustomMountingManager/latest/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
  31. package/android/src/test/java/app/getbullet/marcus/MarkdownCopyTest.kt +69 -0
  32. package/android/src/test/java/app/getbullet/marcus/MarkdownFormatterTest.kt +170 -25
  33. package/android/src/test/java/app/getbullet/marcus/RenderModel.kt +22 -5
  34. package/android/src/test/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpanTest.kt +91 -0
  35. package/android/src/test/resources/render-model.txt +221 -16
  36. package/apple/MarcusSwizzle.h +25 -0
  37. package/apple/MarcusTextDecoratorComponentView.h +22 -0
  38. package/apple/MarcusTextDecoratorComponentView.mm +72 -0
  39. package/apple/MarcusTextDecoratorShadowNode.h +62 -0
  40. package/apple/MarcusTextDecoratorShadowNode.mm +192 -0
  41. package/apple/MarcusTextDecoratorViewManager.h +4 -0
  42. package/apple/MarcusTextDecoratorViewManager.mm +11 -0
  43. package/apple/MarcusTextInputDecoratorComponentView.mm +1 -1
  44. package/apple/MarcusTextInputDecoratorShadowNode.mm +1 -1
  45. package/apple/MarcusTextLayoutManager.h +40 -0
  46. package/apple/MarcusTextLayoutManager.mm +173 -0
  47. package/apple/MarkdownAttributes.h +23 -2
  48. package/apple/MarkdownAttributes.m +6 -0
  49. package/apple/MarkdownCopy.swift +63 -0
  50. package/apple/MarkdownDecorator.swift +2 -2
  51. package/apple/MarkdownFormatter+React.swift +23 -0
  52. package/apple/MarkdownFormatter.swift +869 -148
  53. package/apple/MarkdownParagraphLayoutManager.swift +455 -0
  54. package/apple/MarkdownSwiftInterop.h +0 -1
  55. package/apple/MarkdownTextBackground.swift +59 -1
  56. package/apple/MarkdownTextFieldObserver.swift +2 -2
  57. package/apple/MarkdownTextLayoutFragment.swift +159 -33
  58. package/apple/MarkdownTextLayoutManagerDelegate.swift +67 -3
  59. package/apple/MarkdownTextStorageDelegate.swift +2 -2
  60. package/apple/MarkdownTextViewObserver.swift +2 -2
  61. package/apple/MarkdownUtils.swift +38 -0
  62. package/apple/MarkdownWritingDirection.swift +132 -0
  63. package/apple/RCTMarcusStyle+Codegen.h +115 -9
  64. package/apple/RCTMarcusStyle.h +128 -31
  65. package/apple/RCTMarcusStyle.m +48 -0
  66. package/apple/RCTParagraphComponentView+Marcus.h +17 -0
  67. package/apple/RCTParagraphComponentView+Marcus.mm +38 -0
  68. package/apple/RCTTextInputComponentView+Marcus.mm +1 -25
  69. package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorState.h +37 -0
  70. package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorViewComponentDescriptor.h +17 -0
  71. package/lib/commonjs/MarcusTextDecoratorViewNativeComponent.ts +199 -0
  72. package/lib/commonjs/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
  73. package/lib/commonjs/MarkdownText.js +330 -0
  74. package/lib/commonjs/MarkdownText.js.map +1 -0
  75. package/lib/commonjs/MarkdownText.web.js +529 -0
  76. package/lib/commonjs/MarkdownText.web.js.map +1 -0
  77. package/lib/commonjs/MarkdownTextInput.js +22 -27
  78. package/lib/commonjs/MarkdownTextInput.js.map +1 -1
  79. package/lib/commonjs/MarkdownTextInput.web.js +12 -7
  80. package/lib/commonjs/MarkdownTextInput.web.js.map +1 -1
  81. package/lib/commonjs/atomUtils.js +112 -0
  82. package/lib/commonjs/atomUtils.js.map +1 -0
  83. package/lib/commonjs/childrenUtils.js +36 -0
  84. package/lib/commonjs/childrenUtils.js.map +1 -0
  85. package/lib/commonjs/index.js +8 -7
  86. package/lib/commonjs/index.js.map +1 -1
  87. package/lib/commonjs/math/environment.js +69 -0
  88. package/lib/commonjs/math/environment.js.map +1 -0
  89. package/lib/commonjs/math/index.js +61 -0
  90. package/lib/commonjs/math/index.js.map +1 -0
  91. package/lib/commonjs/math/mathjax.js +548 -0
  92. package/lib/commonjs/math/mathjax.js.map +1 -0
  93. package/lib/commonjs/metro.js +172 -0
  94. package/lib/commonjs/metro.js.map +1 -0
  95. package/lib/commonjs/parser/index.d.ts +3 -0
  96. package/lib/commonjs/parser/index.js +450 -42
  97. package/lib/commonjs/parser/index.js.map +4 -4
  98. package/lib/commonjs/stripUtils.js +743 -0
  99. package/lib/commonjs/stripUtils.js.map +1 -0
  100. package/lib/commonjs/styleUtils.js +194 -23
  101. package/lib/commonjs/styleUtils.js.map +1 -1
  102. package/lib/commonjs/web/MarkdownElements.css +67 -0
  103. package/lib/commonjs/web/MarkdownTextInput.css +9 -7
  104. package/lib/commonjs/web/utils/blockLayout.js +95 -7
  105. package/lib/commonjs/web/utils/blockLayout.js.map +1 -1
  106. package/lib/commonjs/web/utils/blockUtils.js +327 -54
  107. package/lib/commonjs/web/utils/blockUtils.js.map +1 -1
  108. package/lib/commonjs/web/utils/elementUtils.js +80 -0
  109. package/lib/commonjs/web/utils/elementUtils.js.map +1 -0
  110. package/lib/commonjs/web/utils/parserUtils.js +84 -28
  111. package/lib/commonjs/web/utils/parserUtils.js.map +1 -1
  112. package/lib/commonjs/web/utils/treeUtils.js +6 -0
  113. package/lib/commonjs/web/utils/treeUtils.js.map +1 -1
  114. package/lib/commonjs/web/utils/webStyleUtils.js +25 -5
  115. package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -1
  116. package/lib/module/MarcusTextDecoratorViewNativeComponent.ts +199 -0
  117. package/lib/module/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
  118. package/lib/module/MarkdownText.js +325 -0
  119. package/lib/module/MarkdownText.js.map +1 -0
  120. package/lib/module/MarkdownText.web.js +524 -0
  121. package/lib/module/MarkdownText.web.js.map +1 -0
  122. package/lib/module/MarkdownTextInput.js +24 -29
  123. package/lib/module/MarkdownTextInput.js.map +1 -1
  124. package/lib/module/MarkdownTextInput.web.js +12 -7
  125. package/lib/module/MarkdownTextInput.web.js.map +1 -1
  126. package/lib/module/atomUtils.js +106 -0
  127. package/lib/module/atomUtils.js.map +1 -0
  128. package/lib/module/childrenUtils.js +33 -0
  129. package/lib/module/childrenUtils.js.map +1 -0
  130. package/lib/module/index.js +1 -1
  131. package/lib/module/index.js.map +1 -1
  132. package/lib/module/math/environment.js +67 -0
  133. package/lib/module/math/environment.js.map +1 -0
  134. package/lib/module/math/index.js +56 -0
  135. package/lib/module/math/index.js.map +1 -0
  136. package/lib/module/math/mathjax.js +541 -0
  137. package/lib/module/math/mathjax.js.map +1 -0
  138. package/lib/module/metro.js +169 -0
  139. package/lib/module/metro.js.map +1 -0
  140. package/lib/module/parser/index.d.ts +3 -0
  141. package/lib/module/parser/index.js +450 -42
  142. package/lib/module/parser/index.js.map +4 -4
  143. package/lib/module/stripUtils.js +739 -0
  144. package/lib/module/stripUtils.js.map +1 -0
  145. package/lib/module/styleUtils.js +196 -25
  146. package/lib/module/styleUtils.js.map +1 -1
  147. package/lib/module/web/MarkdownElements.css +67 -0
  148. package/lib/module/web/MarkdownTextInput.css +9 -7
  149. package/lib/module/web/utils/blockLayout.js +94 -8
  150. package/lib/module/web/utils/blockLayout.js.map +1 -1
  151. package/lib/module/web/utils/blockUtils.js +328 -56
  152. package/lib/module/web/utils/blockUtils.js.map +1 -1
  153. package/lib/module/web/utils/elementUtils.js +75 -0
  154. package/lib/module/web/utils/elementUtils.js.map +1 -0
  155. package/lib/module/web/utils/parserUtils.js +86 -30
  156. package/lib/module/web/utils/parserUtils.js.map +1 -1
  157. package/lib/module/web/utils/treeUtils.js +6 -0
  158. package/lib/module/web/utils/treeUtils.js.map +1 -1
  159. package/lib/module/web/utils/webStyleUtils.js +25 -5
  160. package/lib/module/web/utils/webStyleUtils.js.map +1 -1
  161. package/lib/typescript/commonjs/e2e/links.test.d.ts +2 -0
  162. package/lib/typescript/commonjs/e2e/links.test.d.ts.map +1 -0
  163. package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
  164. package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
  165. package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
  166. package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
  167. package/lib/typescript/commonjs/src/MarkdownText.d.ts +127 -0
  168. package/lib/typescript/commonjs/src/MarkdownText.d.ts.map +1 -0
  169. package/lib/typescript/commonjs/src/MarkdownText.web.d.ts +126 -0
  170. package/lib/typescript/commonjs/src/MarkdownText.web.d.ts.map +1 -0
  171. package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts +23 -1
  172. package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts.map +1 -1
  173. package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts +26 -4
  174. package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts.map +1 -1
  175. package/lib/typescript/commonjs/src/atomUtils.d.ts +58 -0
  176. package/lib/typescript/commonjs/src/atomUtils.d.ts.map +1 -0
  177. package/lib/typescript/commonjs/src/childrenUtils.d.ts +12 -0
  178. package/lib/typescript/commonjs/src/childrenUtils.d.ts.map +1 -0
  179. package/lib/typescript/commonjs/src/commonTypes.d.ts +39 -3
  180. package/lib/typescript/commonjs/src/commonTypes.d.ts.map +1 -1
  181. package/lib/typescript/commonjs/src/index.d.ts +2 -1
  182. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  183. package/lib/typescript/commonjs/src/math/environment.d.ts +7 -0
  184. package/lib/typescript/commonjs/src/math/environment.d.ts.map +1 -0
  185. package/lib/typescript/commonjs/src/math/index.d.ts +64 -0
  186. package/lib/typescript/commonjs/src/math/index.d.ts.map +1 -0
  187. package/lib/typescript/commonjs/src/math/mathjax.d.ts +63 -0
  188. package/lib/typescript/commonjs/src/math/mathjax.d.ts.map +1 -0
  189. package/lib/typescript/commonjs/src/metro.d.ts +59 -0
  190. package/lib/typescript/commonjs/src/metro.d.ts.map +1 -0
  191. package/lib/typescript/commonjs/src/parser/index.d.ts +3 -3
  192. package/lib/typescript/commonjs/src/parser/index.d.ts.map +1 -1
  193. package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts.map +1 -1
  194. package/lib/typescript/commonjs/src/rangeUtils.d.ts +1 -1
  195. package/lib/typescript/commonjs/src/stripUtils.d.ts +108 -0
  196. package/lib/typescript/commonjs/src/stripUtils.d.ts.map +1 -0
  197. package/lib/typescript/commonjs/src/styleUtils.d.ts +62 -4
  198. package/lib/typescript/commonjs/src/styleUtils.d.ts.map +1 -1
  199. package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts +32 -4
  200. package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts.map +1 -1
  201. package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts +20 -8
  202. package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts.map +1 -1
  203. package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts +37 -0
  204. package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts.map +1 -0
  205. package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts +5 -5
  206. package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts.map +1 -1
  207. package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts +6 -1
  208. package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts.map +1 -1
  209. package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts +2 -2
  210. package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts.map +1 -1
  211. package/lib/typescript/module/e2e/links.test.d.ts +2 -0
  212. package/lib/typescript/module/e2e/links.test.d.ts.map +1 -0
  213. package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
  214. package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
  215. package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
  216. package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
  217. package/lib/typescript/module/src/MarkdownText.d.ts +127 -0
  218. package/lib/typescript/module/src/MarkdownText.d.ts.map +1 -0
  219. package/lib/typescript/module/src/MarkdownText.web.d.ts +126 -0
  220. package/lib/typescript/module/src/MarkdownText.web.d.ts.map +1 -0
  221. package/lib/typescript/module/src/MarkdownTextInput.d.ts +23 -1
  222. package/lib/typescript/module/src/MarkdownTextInput.d.ts.map +1 -1
  223. package/lib/typescript/module/src/MarkdownTextInput.web.d.ts +26 -4
  224. package/lib/typescript/module/src/MarkdownTextInput.web.d.ts.map +1 -1
  225. package/lib/typescript/module/src/atomUtils.d.ts +58 -0
  226. package/lib/typescript/module/src/atomUtils.d.ts.map +1 -0
  227. package/lib/typescript/module/src/childrenUtils.d.ts +12 -0
  228. package/lib/typescript/module/src/childrenUtils.d.ts.map +1 -0
  229. package/lib/typescript/module/src/commonTypes.d.ts +39 -3
  230. package/lib/typescript/module/src/commonTypes.d.ts.map +1 -1
  231. package/lib/typescript/module/src/index.d.ts +2 -1
  232. package/lib/typescript/module/src/index.d.ts.map +1 -1
  233. package/lib/typescript/module/src/math/environment.d.ts +7 -0
  234. package/lib/typescript/module/src/math/environment.d.ts.map +1 -0
  235. package/lib/typescript/module/src/math/index.d.ts +64 -0
  236. package/lib/typescript/module/src/math/index.d.ts.map +1 -0
  237. package/lib/typescript/module/src/math/mathjax.d.ts +63 -0
  238. package/lib/typescript/module/src/math/mathjax.d.ts.map +1 -0
  239. package/lib/typescript/module/src/metro.d.ts +59 -0
  240. package/lib/typescript/module/src/metro.d.ts.map +1 -0
  241. package/lib/typescript/module/src/parser/index.d.ts +3 -3
  242. package/lib/typescript/module/src/parser/index.d.ts.map +1 -1
  243. package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts.map +1 -1
  244. package/lib/typescript/module/src/rangeUtils.d.ts +1 -1
  245. package/lib/typescript/module/src/stripUtils.d.ts +108 -0
  246. package/lib/typescript/module/src/stripUtils.d.ts.map +1 -0
  247. package/lib/typescript/module/src/styleUtils.d.ts +62 -4
  248. package/lib/typescript/module/src/styleUtils.d.ts.map +1 -1
  249. package/lib/typescript/module/src/web/utils/blockLayout.d.ts +32 -4
  250. package/lib/typescript/module/src/web/utils/blockLayout.d.ts.map +1 -1
  251. package/lib/typescript/module/src/web/utils/blockUtils.d.ts +20 -8
  252. package/lib/typescript/module/src/web/utils/blockUtils.d.ts.map +1 -1
  253. package/lib/typescript/module/src/web/utils/elementUtils.d.ts +37 -0
  254. package/lib/typescript/module/src/web/utils/elementUtils.d.ts.map +1 -0
  255. package/lib/typescript/module/src/web/utils/parserUtils.d.ts +5 -5
  256. package/lib/typescript/module/src/web/utils/parserUtils.d.ts.map +1 -1
  257. package/lib/typescript/module/src/web/utils/treeUtils.d.ts +6 -1
  258. package/lib/typescript/module/src/web/utils/treeUtils.d.ts.map +1 -1
  259. package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts +2 -2
  260. package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts.map +1 -1
  261. package/package.json +29 -2
  262. package/react-native.config.js +8 -1
  263. package/src/MarcusTextDecoratorViewNativeComponent.ts +199 -0
  264. package/src/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
  265. package/src/MarkdownText.tsx +472 -0
  266. package/src/MarkdownText.web.tsx +668 -0
  267. package/src/MarkdownTextInput.tsx +42 -38
  268. package/src/MarkdownTextInput.web.tsx +36 -13
  269. package/src/atomUtils.ts +112 -0
  270. package/src/childrenUtils.ts +39 -0
  271. package/src/commonTypes.ts +69 -9
  272. package/src/index.tsx +2 -1
  273. package/src/math/environment.ts +68 -0
  274. package/src/math/index.ts +78 -0
  275. package/src/math/mathjax.ts +604 -0
  276. package/src/metro.ts +238 -0
  277. package/src/parser/index.ts +175 -10
  278. package/src/parser/micromark-extension-mentions.ts +104 -11
  279. package/src/stripUtils.ts +919 -0
  280. package/src/styleUtils.ts +290 -26
  281. package/src/web/MarkdownElements.css +67 -0
  282. package/src/web/MarkdownTextInput.css +9 -7
  283. package/src/web/utils/blockLayout.ts +124 -14
  284. package/src/web/utils/blockUtils.ts +365 -59
  285. package/src/web/utils/elementUtils.ts +87 -0
  286. package/src/web/utils/parserUtils.ts +119 -31
  287. package/src/web/utils/treeUtils.ts +6 -1
  288. package/src/web/utils/webStyleUtils.ts +32 -8
  289. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundColorSpan.kt +0 -6
  290. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBoldSpan.kt +0 -6
  291. package/android/src/main/java/app/getbullet/marcus/spans/MarkdownItalicSpan.kt +0 -6
  292. package/apple/RCTMarcusStyle+Codegen.mm +0 -64
  293. package/apple/RCTMarcusUtils.h +0 -26
  294. package/apple/RCTMarcusUtils.mm +0 -26
@@ -7,12 +7,23 @@ import com.facebook.react.bridge.ReactContext
7
7
  import com.facebook.react.views.textinput.ReactEditText
8
8
  import com.facebook.react.views.view.ReactViewGroup
9
9
 
10
+ /**
11
+ * Formats the editable of the `TextInput` it wraps, and paints the backgrounds
12
+ * that would otherwise cover the caret.
13
+ */
10
14
  class MarcusTextInputDecoratorView(context: Context) : ReactViewGroup(context) {
11
15
 
12
16
  private var markdownUtils: MarkdownUtils? = null
13
17
  private var reactEditText: ReactEditText? = null
14
18
  private var textWatcher: TextWatcher? = null
15
19
 
20
+ /**
21
+ * Paints the markdown backgrounds before the text does, so that the caret --
22
+ * which Android draws before either -- is not covered by them.
23
+ */
24
+ private val backgroundPass = MarkdownBackgroundPass()
25
+ private val backgroundPainter = MarkdownBackgroundPainter(backgroundPass)
26
+
16
27
  var markdownStyle: MarkdownStyle? = null
17
28
  set(value) {
18
29
  field = value
@@ -32,12 +43,13 @@ class MarcusTextInputDecoratorView(context: Context) : ReactViewGroup(context) {
32
43
 
33
44
  val child = getChildAt(0)
34
45
  if (child is ReactEditText) {
35
- val utils = MarkdownUtils(context as ReactContext, child.paint)
46
+ val utils = MarkdownUtils(context as ReactContext, child.paint, backgroundPass)
36
47
  utils.markdownStyle = markdownStyle
37
48
  utils.parserId = parserId
38
49
  markdownUtils = utils
39
50
  reactEditText = child
40
51
  textWatcher = MarkdownTextWatcher(utils).also { child.addTextChangedListener(it) }
52
+ backgroundPainter.attachTo(child)
41
53
  applyNewStyles()
42
54
  }
43
55
  }
@@ -46,6 +58,7 @@ class MarcusTextInputDecoratorView(context: Context) : ReactViewGroup(context) {
46
58
  super.onDetachedFromWindow()
47
59
  reactEditText?.let { editText ->
48
60
  textWatcher?.let { editText.removeTextChangedListener(it) }
61
+ editText.setCompoundDrawables(null, null, null, null)
49
62
  reactEditText = null
50
63
  textWatcher = null
51
64
  markdownUtils = null
@@ -55,6 +68,15 @@ class MarcusTextInputDecoratorView(context: Context) : ReactViewGroup(context) {
55
68
  fun applyNewStyles() {
56
69
  val editText = reactEditText ?: return
57
70
  val utils = markdownUtils ?: return
71
+
72
+ // Puts the painter back if something else took the compound drawable slot it
73
+ // lives in. Only `inlineImageLeft` does, and it clears all four; checked here
74
+ // rather than per frame, where asking for it would cost a layout pass every
75
+ // time something wrapped our drawable rather than replacing it.
76
+ if (editText.compoundDrawables[3] == null) {
77
+ backgroundPainter.attachTo(editText)
78
+ }
79
+
58
80
  val editable = editText.text
59
81
  if (editable is SpannableStringBuilder) {
60
82
  utils.applyMarkdownFormatting(editable)
@@ -0,0 +1,147 @@
1
+ package app.getbullet.marcus
2
+
3
+ import android.graphics.Canvas
4
+ import android.graphics.ColorFilter
5
+ import android.graphics.PixelFormat
6
+ import android.graphics.Rect
7
+ import android.graphics.drawable.Drawable
8
+ import android.text.Layout
9
+ import android.text.Spanned
10
+ import android.text.style.LineBackgroundSpan
11
+ import android.widget.TextView
12
+ import app.getbullet.marcus.spans.MarkdownSpan
13
+ import java.lang.ref.WeakReference
14
+
15
+ /**
16
+ * Paints the markdown backgrounds before the caret is drawn.
17
+ *
18
+ * A `TextView` draws its caret first and its text layout -- line backgrounds and
19
+ * all -- over the top, so anything opaque behind the text hides the caret.
20
+ * Android's own `BackgroundColorSpan` has the same effect; with a code block's
21
+ * box covering the width of every line it is impossible to miss.
22
+ *
23
+ * The one thing a `TextView` draws earlier, and that can be handed to a view we
24
+ * did not create, is a compound drawable: they are painted at the top of
25
+ * `onDraw`, before the cursor and before the text. So this is a drawable of no
26
+ * size, in the one slot React Native never uses, whose only job is to run the
27
+ * line background spans early. They are the same spans a `Text` paints through
28
+ * the layout -- one implementation, drawn at two different moments -- and
29
+ * [MarkdownBackgroundPass] is what keeps them from painting twice.
30
+ *
31
+ * The line walk mirrors `Layout.drawBackground`, which is not public until API
32
+ * 29 and would paint every line background rather than only ours.
33
+ */
34
+ class MarkdownBackgroundPainter(private val pass: MarkdownBackgroundPass) : Drawable() {
35
+
36
+ private var textView: WeakReference<TextView>? = null
37
+ private val clip = Rect()
38
+
39
+ /**
40
+ * Takes the bottom compound drawable slot: `inlineImageLeft` is the only one
41
+ * React Native sets, and it sets the left.
42
+ */
43
+ fun attachTo(view: TextView) {
44
+ textView = WeakReference(view)
45
+ setBounds(0, 0, 0, 0)
46
+ view.compoundDrawablePadding = 0
47
+ view.setCompoundDrawables(null, null, null, this)
48
+ }
49
+
50
+ override fun draw(canvas: Canvas) {
51
+ val view = textView?.get() ?: return
52
+ val layout = view.layout ?: return
53
+ val text = layout.text as? Spanned ?: return
54
+
55
+ val spans = text.getSpans(0, text.length, LineBackgroundSpan::class.java)
56
+ if (spans.isEmpty()) {
57
+ return
58
+ }
59
+
60
+ canvas.save()
61
+ // From where a compound drawable is drawn to where the text is: the slot is
62
+ // pinned to the view and centred in the space between the compound paddings,
63
+ // while the layout hangs off the padding and scrolls.
64
+ val horizontalSpace = view.width - view.compoundPaddingLeft - view.compoundPaddingRight
65
+ canvas.translate(
66
+ (-view.scrollX - horizontalSpace / 2).toFloat(),
67
+ (view.totalPaddingTop - view.scrollY - view.height + view.paddingBottom).toFloat(),
68
+ )
69
+
70
+ pass.painting = true
71
+ try {
72
+ paint(canvas, layout, text, spans)
73
+ } finally {
74
+ pass.painting = false
75
+ canvas.restore()
76
+ }
77
+ }
78
+
79
+ private fun paint(
80
+ canvas: Canvas,
81
+ layout: Layout,
82
+ text: Spanned,
83
+ spans: Array<out LineBackgroundSpan>
84
+ ) {
85
+ // Only what the frame is about to show: a long document would otherwise pay
86
+ // for every line of it on every draw.
87
+ val firstLine: Int
88
+ val lastLine: Int
89
+ if (canvas.getClipBounds(clip)) {
90
+ firstLine = layout.getLineForVertical(clip.top)
91
+ lastLine = layout.getLineForVertical(clip.bottom)
92
+ } else {
93
+ firstLine = 0
94
+ lastLine = layout.lineCount - 1
95
+ }
96
+
97
+ val paint = layout.paint
98
+ val width = layout.width
99
+ var top = layout.getLineTop(firstLine)
100
+
101
+ for (line in firstLine..lastLine) {
102
+ val start = layout.getLineStart(line)
103
+ val end = layout.getLineEnd(line)
104
+ // The next line's top rather than this line's bottom, so consecutive
105
+ // fills tile through the line spacing exactly as the layout's own pass
106
+ // makes them.
107
+ val bottom = layout.getLineTop(line + 1)
108
+ val baseline = bottom - layout.getLineDescent(line)
109
+
110
+ for (span in spans) {
111
+ // Ours only: anything else is still painted by the layout's own pass,
112
+ // where it belongs, and painting it here as well would double it.
113
+ if (span is MarkdownSpan &&
114
+ text.getSpanStart(span) < end &&
115
+ text.getSpanEnd(span) > start
116
+ ) {
117
+ span.drawBackground(
118
+ canvas,
119
+ paint,
120
+ 0,
121
+ width,
122
+ top,
123
+ baseline,
124
+ bottom,
125
+ text,
126
+ start,
127
+ end,
128
+ line,
129
+ )
130
+ }
131
+ }
132
+
133
+ top = bottom
134
+ }
135
+ }
136
+
137
+ override fun getIntrinsicWidth(): Int = 0
138
+
139
+ override fun getIntrinsicHeight(): Int = 0
140
+
141
+ override fun setAlpha(alpha: Int) = Unit
142
+
143
+ override fun setColorFilter(colorFilter: ColorFilter?) = Unit
144
+
145
+ @Deprecated("Deprecated in Drawable", ReplaceWith("PixelFormat.TRANSLUCENT"))
146
+ override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
147
+ }
@@ -0,0 +1,19 @@
1
+ package app.getbullet.marcus
2
+
3
+ /**
4
+ * Whether the view is painting the markdown backgrounds itself.
5
+ *
6
+ * A `Text` lets the layout paint them, in the pass Android runs for
7
+ * `LineBackgroundSpan`. A `TextInput` cannot: that pass happens after the caret
8
+ * is drawn, so anything opaque in it hides the caret ([[MarkdownBackgroundPainter]]
9
+ * explains the rest). There the painter runs the spans itself, earlier, and this
10
+ * is what tells them which of the two moments they are in -- painting on both
11
+ * would put the background back over the caret.
12
+ *
13
+ * One instance per input. A spannable formatted for a `Text` carries none, and
14
+ * its spans paint whenever they are asked to.
15
+ */
16
+ class MarkdownBackgroundPass {
17
+ /** True only while the painter is driving the spans. */
18
+ var painting = false
19
+ }
@@ -0,0 +1,87 @@
1
+ package app.getbullet.marcus
2
+
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import com.facebook.react.bridge.ReadableType
5
+
6
+ /** One placeholder character, and the markdown it was written as. */
7
+ data class MarkdownSubstitution(val index: Int, val text: String)
8
+
9
+ /**
10
+ * What a selection copies as.
11
+ *
12
+ * A view nested in a `Text` -- a drawn equation, an image -- is one U+FFFC in the spannable the
13
+ * `TextView` lays out, and U+FFFC is what the platform's own copy puts on the clipboard: a message
14
+ * with an equation in it arrives in the next application with a box where the maths was. So the
15
+ * copy is taken over and the placeholders are written back out as the source they stand for.
16
+ *
17
+ * Offsets are the ones JavaScript measured against the stripped text, which is the very string the
18
+ * `TextView` holds -- the ranges are measured against it too -- so a selection's start and end
19
+ * index into the same coordinates with nothing to remap.
20
+ */
21
+ object MarkdownCopy {
22
+
23
+ @JvmStatic
24
+ fun read(array: ReadableArray?): List<MarkdownSubstitution> {
25
+ if (array == null || array.size() == 0) {
26
+ return emptyList()
27
+ }
28
+
29
+ val substitutions = ArrayList<MarkdownSubstitution>(array.size())
30
+
31
+ for (i in 0 until array.size()) {
32
+ if (array.getType(i) != ReadableType.Map) {
33
+ continue
34
+ }
35
+
36
+ val map = array.getMap(i) ?: continue
37
+
38
+ substitutions.add(
39
+ MarkdownSubstitution(index = map.getInt("index"), text = map.getString("text") ?: "")
40
+ )
41
+ }
42
+
43
+ return substitutions
44
+ }
45
+
46
+ /**
47
+ * The text between two offsets with every placeholder in it replaced by its source.
48
+ *
49
+ * Substitutions arrive in text order and each covers exactly one character, so one walk is
50
+ * enough. One that falls outside the selection is skipped rather than clipped: half an equation
51
+ * is not worth pasting, and a selection that stops in the middle of a drawing stops at its edge
52
+ * as far as the `TextView` is concerned anyway.
53
+ */
54
+ fun substitute(
55
+ text: CharSequence,
56
+ start: Int,
57
+ end: Int,
58
+ substitutions: List<MarkdownSubstitution>,
59
+ ): String {
60
+ val from = start.coerceIn(0, text.length)
61
+ val to = end.coerceIn(from, text.length)
62
+ val copied = StringBuilder(to - from)
63
+ var cursor = from
64
+
65
+ for (substitution in substitutions) {
66
+ val index = substitution.index
67
+
68
+ if (index < cursor) {
69
+ continue
70
+ }
71
+
72
+ if (index >= to) {
73
+ break
74
+ }
75
+
76
+ copied.append(text, cursor, index)
77
+ copied.append(substitution.text)
78
+ cursor = index + 1
79
+ }
80
+
81
+ if (cursor < to) {
82
+ copied.append(text, cursor, to)
83
+ }
84
+
85
+ return copied.toString()
86
+ }
87
+ }