react-native-marcus 0.1.0 → 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
@@ -1,7 +1,10 @@
1
1
  package app.getbullet.marcus
2
2
 
3
3
  import android.text.SpannableStringBuilder
4
+ import android.text.Spanned
4
5
  import android.text.TextPaint
6
+ import android.text.style.AbsoluteSizeSpan
7
+ import app.getbullet.marcus.spans.MarkdownCodeBlockSpan
5
8
  import com.facebook.react.bridge.JavaOnlyMap
6
9
  import com.facebook.react.bridge.ReadableMap
7
10
  import com.facebook.react.uimanager.DisplayMetricsHolder
@@ -9,6 +12,8 @@ import java.io.File
9
12
  import org.json.JSONArray
10
13
  import org.json.JSONObject
11
14
  import org.junit.Assert.assertEquals
15
+ import org.junit.Assert.assertFalse
16
+ import org.junit.Assert.assertTrue
12
17
  import org.junit.Before
13
18
  import org.junit.Test
14
19
  import org.junit.runner.RunWith
@@ -26,6 +31,8 @@ import org.robolectric.annotation.Config
26
31
  * so the Android and iOS outputs stay diffable against each other in a single
27
32
  * comparison. Regenerate with `-Dmarcus.updateBaselines=true`.
28
33
  */
34
+ private val INDENT = Regex("""indent\((-?\d+),""")
35
+
29
36
  @RunWith(RobolectricTestRunner::class)
30
37
  @Config(sdk = [34])
31
38
  class MarkdownFormatterTest {
@@ -81,12 +88,12 @@ class MarkdownFormatterTest {
81
88
  }
82
89
 
83
90
  @Test
84
- fun `applies bold across the whole emphasis span`() {
91
+ fun `applies bold across the whole strong span`() {
85
92
  val ssb = SpannableStringBuilder("**bold**")
86
93
  formatter.format(
87
94
  ssb,
88
95
  listOf(
89
- MarkdownRange("bold", 0, 8, 0),
96
+ MarkdownRange("strong", 0, 8, 0),
90
97
  MarkdownRange("syntax", 0, 2, 0),
91
98
  MarkdownRange("syntax", 6, 2, 0),
92
99
  ),
@@ -104,6 +111,85 @@ class MarkdownFormatterTest {
104
111
  )
105
112
  }
106
113
 
114
+ // The ranges a display is handed for `- one` and `1. one`: the syntax is gone,
115
+ // and what is left of it is the marker that stands for the bullet or numbers
116
+ // the item.
117
+ private val bulletRanges = listOf(
118
+ MarkdownRange("syntax", 0, 1, 0),
119
+ MarkdownRange("block-prefix", 0, 2, 0),
120
+ MarkdownRange("list-unordered", 0, 5, 1),
121
+ )
122
+
123
+ private val numberRanges = listOf(
124
+ MarkdownRange("syntax", 0, 1, 0),
125
+ MarkdownRange("syntax", 1, 1, 0),
126
+ MarkdownRange("block-prefix", 0, 3, 0),
127
+ MarkdownRange("list-ordered", 0, 6, 1),
128
+ )
129
+
130
+ @Test
131
+ fun `draws a bullet in place of an unordered marker`() {
132
+ val model = format("- one", bulletRanges, display = true)
133
+
134
+ assertTrue(model, model.contains("bullet"))
135
+ }
136
+
137
+ @Test
138
+ fun `scales an ordered marker to its own size`() {
139
+ val model = format("1. one", numberRanges, display = true)
140
+
141
+ // 16 * orderedListMarkerScale, over the `1.` and nothing else.
142
+ assertTrue(model, model.contains("font-size(12)"))
143
+ assertFalse(model, model.contains("bullet"))
144
+ }
145
+
146
+ @Test
147
+ fun `shows the marker an input was typed with`() {
148
+ val bullet = format("- one", bulletRanges, display = false)
149
+ val number = format("1. one", numberRanges, display = false)
150
+
151
+ assertFalse(bullet, bullet.contains("bullet"))
152
+ assertFalse(number, number.contains("font-size("))
153
+ }
154
+
155
+ @Test
156
+ fun `indents past the bullet rather than past the marker`() {
157
+ // The circle is 16 * unorderedListMarkerScale wide and holds a marker's
158
+ // padding either side of it, so the text starts further in than the `-` it
159
+ // was written with would have put it.
160
+ val shown = indentOf(format("- one", bulletRanges, display = false))
161
+ val drawn = indentOf(format("- one", bulletRanges, display = true))
162
+
163
+ assertTrue("$drawn is not past $shown", drawn > shown)
164
+ }
165
+
166
+ @Test
167
+ fun `takes the base font from the spannable when there is no view to ask`() {
168
+ // The measure pass runs before there is a view, so the size the text will be
169
+ // drawn at is only on the spannable. Reading it there is what keeps the
170
+ // bullet the same size in both passes -- and the paragraph the same height.
171
+ val measured = SpannableStringBuilder("- one")
172
+ measured.setSpan(AbsoluteSizeSpan(16, false), 0, measured.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
173
+ MarkdownFormatter(RuntimeEnvironment.getApplication().assets, null, true)
174
+ .format(measured, bulletRanges, style, null)
175
+
176
+ assertEquals(
177
+ indentOf(format("- one", bulletRanges, display = true)),
178
+ indentOf(RenderModel.dump("- one", measured)),
179
+ )
180
+ }
181
+
182
+ @Test
183
+ fun `takes a box's direction from the code inside it`() {
184
+ assertTrue(codeBlock("```\nكود\n```").rightToLeft)
185
+ assertFalse(codeBlock("```\nconst a = 1\n```").rightToLeft)
186
+ // The whole block rather than its first line: a fence is punctuation and
187
+ // decides nothing, and it is typed in an input and stripped from a display,
188
+ // so a rule that stopped at the first line would answer differently in the
189
+ // two. `MarkdownFormatterTests.swift` pins the same three cases on iOS.
190
+ assertTrue(codeBlock("\nكود\n").rightToLeft)
191
+ }
192
+
107
193
  @Test
108
194
  fun `leaves plain text unspanned`() {
109
195
  val ssb = SpannableStringBuilder("no markdown here")
@@ -112,8 +198,38 @@ class MarkdownFormatterTest {
112
198
  assertEquals("(no spans)", RenderModel.dump("no markdown here", ssb))
113
199
  }
114
200
 
201
+ /** The box the formatter puts behind a whole string taken as one block. */
202
+ private fun codeBlock(markdown: String): MarkdownCodeBlockSpan {
203
+ val ssb = SpannableStringBuilder(markdown)
204
+ formatter.format(
205
+ ssb,
206
+ listOf(MarkdownRange("codeblock", 0, markdown.length, 0)),
207
+ style,
208
+ textPaint(),
209
+ )
210
+
211
+ return ssb.getSpans(0, ssb.length, MarkdownCodeBlockSpan::class.java).single()
212
+ }
213
+
115
214
  private fun textPaint() = TextPaint().apply { textSize = 16f }
116
215
 
216
+ private fun format(markdown: String, ranges: List<MarkdownRange>, display: Boolean): String {
217
+ val ssb = SpannableStringBuilder(markdown)
218
+ MarkdownFormatter(RuntimeEnvironment.getApplication().assets, null, display)
219
+ .format(ssb, ranges, style, textPaint())
220
+
221
+ return RenderModel.dump(markdown, ssb)
222
+ }
223
+
224
+ /** The leading margin the first `indent(first,rest)` in a dump reports. */
225
+ private fun indentOf(model: String): Int {
226
+ val indent = model.lineSequence()
227
+ .mapNotNull { INDENT.find(it) }
228
+ .firstOrNull()
229
+
230
+ return requireNotNull(indent) { "no indent in\n$model" }.groupValues[1].toInt()
231
+ }
232
+
117
233
  private fun rangesOf(array: JSONArray): List<MarkdownRange> =
118
234
  (0 until array.length()).map { i ->
119
235
  val range = array.getJSONObject(i)
@@ -131,6 +247,12 @@ class MarkdownFormatterTest {
131
247
  }.bufferedReader().readText()
132
248
 
133
249
  /** The same style the web harness pins, so the dumps are comparable. */
250
+ /**
251
+ * The style the decorators are handed: the shipped defaults, already resolved
252
+ * the way `mergeMarkdownStyleWithDefault` resolves them before one is sent --
253
+ * the per-axis lengths filled in from the single `padding` and `margin`, and
254
+ * each list's marker filled in from `syntax`.
255
+ */
134
256
  private fun styleMap(): ReadableMap {
135
257
  fun group(vararg entries: Pair<String, Any>): JavaOnlyMap = JavaOnlyMap().apply {
136
258
  entries.forEach { (key, value) ->
@@ -138,26 +260,51 @@ class MarkdownFormatterTest {
138
260
  is Int -> putInt(key, value)
139
261
  is Double -> putDouble(key, value)
140
262
  is String -> putString(key, value)
263
+ is JavaOnlyMap -> putMap(key, value)
141
264
  }
142
265
  }
143
266
  }
144
267
 
268
+ val marker = group("color" to 0xFF808080.toInt())
269
+
145
270
  return JavaOnlyMap().apply {
146
271
  putMap("syntax", group("color" to 0xFF808080.toInt()))
147
- putMap("link", group("color" to 0xFF0000FF.toInt()))
148
- putMap("heading", group("fontSize" to 38.0, "scale" to 0.85))
272
+ putMap("link", group("color" to 0xFF0000FF.toInt(), "textDecorationLine" to "underline"))
273
+ putMap("strong", group("fontWeight" to "bold"))
274
+ putMap("emphasis", group("fontStyle" to "italic"))
275
+ putMap("strikethrough", group("textDecorationLine" to "line-through"))
276
+ putMap("heading", group("fontSize" to 38.0, "fontWeight" to "bold", "scale" to 0.85))
149
277
  putMap("emoji", group("fontSize" to 16.0, "fontFamily" to "System"))
150
278
  putMap(
151
279
  "blockquote",
152
280
  group(
153
281
  "borderColor" to 0xFF808080.toInt(),
154
282
  "borderWidth" to 6.0,
155
- "marginLeft" to 6.0,
156
- "paddingLeft" to 6.0,
283
+ "borderRadius" to 0.0,
284
+ "marginStart" to 6.0,
285
+ "paddingStart" to 6.0,
286
+ ),
287
+ )
288
+ putMap(
289
+ "orderedList",
290
+ group(
291
+ "marginStart" to 6.0,
292
+ "paddingStart" to 18.0,
293
+ "markerScale" to 0.8,
294
+ "markerPadding" to 2.0,
295
+ "marker" to marker,
296
+ ),
297
+ )
298
+ putMap(
299
+ "unorderedList",
300
+ group(
301
+ "marginStart" to 6.0,
302
+ "paddingStart" to 18.0,
303
+ "markerScale" to 0.3,
304
+ "markerPadding" to 2.0,
305
+ "marker" to marker,
157
306
  ),
158
307
  )
159
- putMap("orderedList", group("marginLeft" to 6.0, "paddingLeft" to 18.0))
160
- putMap("unorderedList", group("marginLeft" to 6.0, "paddingLeft" to 18.0))
161
308
  putMap(
162
309
  "code",
163
310
  group(
@@ -165,6 +312,11 @@ class MarkdownFormatterTest {
165
312
  "fontSize" to 16.0,
166
313
  "color" to 0xFF000000.toInt(),
167
314
  "backgroundColor" to 0xFFD3D3D3.toInt(),
315
+ "borderRadius" to 4.0,
316
+ "paddingHorizontal" to 2.0,
317
+ "paddingVertical" to 2.0,
318
+ "marginHorizontal" to 2.0,
319
+ "marginVertical" to 2.0,
168
320
  ),
169
321
  )
170
322
  putMap(
@@ -174,30 +326,23 @@ class MarkdownFormatterTest {
174
326
  "fontSize" to 16.0,
175
327
  "color" to 0xFF000000.toInt(),
176
328
  "backgroundColor" to 0xFFD3D3D3.toInt(),
329
+ "borderRadius" to 4.0,
330
+ "paddingHorizontal" to 8.0,
331
+ "paddingVertical" to 8.0,
332
+ "marginHorizontal" to 4.0,
333
+ "marginVertical" to 4.0,
177
334
  ),
178
335
  )
179
336
  putMap(
180
- "mentionHere",
181
- group(
182
- "color" to 0xFF008000.toInt(),
183
- "backgroundColor" to 0xFF00FF00.toInt(),
184
- "borderRadius" to 5.0,
185
- ),
186
- )
187
- putMap(
188
- "mentionUser",
337
+ "mention",
189
338
  group(
190
339
  "color" to 0xFF0000FF.toInt(),
191
340
  "backgroundColor" to 0xFF00FFFF.toInt(),
192
341
  "borderRadius" to 5.0,
193
- ),
194
- )
195
- putMap(
196
- "mentionReport",
197
- group(
198
- "color" to 0xFFFF0000.toInt(),
199
- "backgroundColor" to 0xFFFFC0CB.toInt(),
200
- "borderRadius" to 5.0,
342
+ "paddingHorizontal" to 2.0,
343
+ "paddingVertical" to 2.0,
344
+ "marginHorizontal" to 0.0,
345
+ "marginVertical" to 0.0,
201
346
  ),
202
347
  )
203
348
  }
@@ -6,13 +6,18 @@ import android.text.style.AbsoluteSizeSpan
6
6
  import android.text.style.BackgroundColorSpan
7
7
  import android.text.style.ForegroundColorSpan
8
8
  import android.text.style.LeadingMarginSpan
9
+ import app.getbullet.marcus.spans.MarkdownAbsoluteLineHeightSpan
9
10
  import app.getbullet.marcus.spans.MarkdownBackgroundSpan
10
- import app.getbullet.marcus.spans.MarkdownBoldSpan
11
+ import app.getbullet.marcus.spans.MarkdownBulletSpan
12
+ import app.getbullet.marcus.spans.MarkdownCodeBlockSpan
11
13
  import app.getbullet.marcus.spans.MarkdownFontFamilySpan
14
+ import app.getbullet.marcus.spans.MarkdownFontStyleSpan
15
+ import app.getbullet.marcus.spans.MarkdownFontWeightSpan
12
16
  import app.getbullet.marcus.spans.MarkdownGapSpan
13
- import app.getbullet.marcus.spans.MarkdownItalicSpan
17
+ import app.getbullet.marcus.spans.MarkdownLetterSpacingSpan
14
18
  import app.getbullet.marcus.spans.MarkdownLineHeightSpan
15
19
  import app.getbullet.marcus.spans.MarkdownStrikethroughSpan
20
+ import app.getbullet.marcus.spans.MarkdownTextShadowSpan
16
21
  import app.getbullet.marcus.spans.MarkdownUnderlineSpan
17
22
 
18
23
  /**
@@ -51,15 +56,27 @@ object RenderModel {
51
56
 
52
57
  /** The shared attribute name for a span, or null for spans we do not model. */
53
58
  private fun describe(span: Any): String? = when (span) {
54
- is MarkdownBoldSpan -> "bold"
55
- is MarkdownItalicSpan -> "italic"
59
+ // Named by what the span resolves to rather than by its class, so the two
60
+ // weights the model already had a word for keep it: iOS reports a bold run
61
+ // as `bold` whether the style asked for `bold` or for `700`.
62
+ is MarkdownFontWeightSpan ->
63
+ if (span.weight >= 700) "bold" else "weight(${span.weight})"
64
+ is MarkdownFontStyleSpan -> if (span.italic) "italic" else "upright"
56
65
  is MarkdownStrikethroughSpan -> "strikethrough"
57
66
  is MarkdownUnderlineSpan -> "underline"
58
67
  is MarkdownFontFamilySpan -> "font-family"
59
- is MarkdownLineHeightSpan -> "line-height"
68
+ is MarkdownLetterSpacingSpan -> "letter-spacing"
69
+ is MarkdownTextShadowSpan -> "shadow"
70
+ is MarkdownLineHeightSpan, is MarkdownAbsoluteLineHeightSpan -> "line-height"
60
71
  // The rounded rectangle drawn behind a mention, not a plain background
61
72
  // colour -- iOS models the same thing as `MarkdownTextBackground`.
62
73
  is MarkdownBackgroundSpan -> "background-shape"
74
+ // The box behind a whole code block, which iOS models as the
75
+ // `MarkdownCodeBlock` it stores under `.marcusCodeBlock`.
76
+ is MarkdownCodeBlockSpan -> "code-block"
77
+ // The circle drawn in place of an unordered item's marker, which iOS models
78
+ // as the `MarkdownListBullet` it stores under `.marcusListBullet`.
79
+ is MarkdownBulletSpan -> "bullet"
63
80
  // The space held open after a marker so the next container's gutter lands
64
81
  // between the two.
65
82
  is MarkdownGapSpan -> "gap"
@@ -0,0 +1,91 @@
1
+ package app.getbullet.marcus.spans
2
+
3
+ import android.graphics.Bitmap
4
+ import android.graphics.Canvas
5
+ import android.graphics.Color
6
+ import android.graphics.Paint
7
+ import android.graphics.Path
8
+ import android.graphics.RectF
9
+ import org.junit.Assert.assertEquals
10
+ import org.junit.Test
11
+ import org.junit.runner.RunWith
12
+ import org.robolectric.RobolectricTestRunner
13
+ import org.robolectric.annotation.Config
14
+
15
+ /**
16
+ * Where a code block's box lands, which is the one thing about it that a render
17
+ * model cannot show: the span is handed the line's two sides and decides between
18
+ * them itself.
19
+ *
20
+ * The path it draws is captured rather than the pixels it fills, so the numbers
21
+ * asserted here are the ones the span computed. iOS proves the same geometry the
22
+ * other way round, by counting coloured columns, in
23
+ * `MarkdownParagraphLayoutManagerTests` -- there the shapes are drawn straight
24
+ * onto the context with no path to intercept.
25
+ */
26
+ @RunWith(RobolectricTestRunner::class)
27
+ @Config(sdk = [34])
28
+ class MarkdownCodeBlockSpanTest {
29
+
30
+ @Test
31
+ fun `boxes a left-to-right block from the left edge`() {
32
+ val bounds = box(rightToLeft = false)
33
+
34
+ assertEquals(LEADING_INSET, bounds.left, TOLERANCE)
35
+ assertEquals(WIDTH - MARGIN, bounds.right, TOLERANCE)
36
+ }
37
+
38
+ @Test
39
+ fun `boxes a right-to-left block from the right edge`() {
40
+ val bounds = box(rightToLeft = true)
41
+
42
+ // The padded edge is now the right one, and the bare margin is all that is
43
+ // left on the far side -- the mirror of the case above, on the same numbers.
44
+ assertEquals(MARGIN, bounds.left, TOLERANCE)
45
+ assertEquals(WIDTH - LEADING_INSET, bounds.right, TOLERANCE)
46
+ }
47
+
48
+ /** One line's fill, for a block whose lines start [LEADING_INSET] in. */
49
+ private fun box(rightToLeft: Boolean): RectF {
50
+ val span = MarkdownCodeBlockSpan(
51
+ backgroundColor = Color.RED,
52
+ borderRadius = 0f,
53
+ paddingVertical = 0f,
54
+ marginHorizontal = MARGIN,
55
+ marginVertical = 0f,
56
+ leadingInset = LEADING_INSET,
57
+ rightToLeft = rightToLeft,
58
+ blockStart = 0,
59
+ blockEnd = TEXT.length,
60
+ )
61
+
62
+ val canvas = PathRecordingCanvas()
63
+ span.drawBackground(
64
+ canvas, Paint(), 0, WIDTH.toInt(), 0, 16, 20, TEXT, 0, TEXT.length, 0,
65
+ )
66
+
67
+ return requireNotNull(canvas.bounds) { "the span drew nothing" }
68
+ }
69
+
70
+ /**
71
+ * A canvas that keeps the last path filled on it and paints none of it, so a
72
+ * rect can be read back without a bitmap to search.
73
+ */
74
+ private class PathRecordingCanvas :
75
+ Canvas(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)) {
76
+
77
+ var bounds: RectF? = null
78
+
79
+ override fun drawPath(path: Path, paint: Paint) {
80
+ bounds = RectF().also { path.computeBounds(it, true) }
81
+ }
82
+ }
83
+
84
+ private companion object {
85
+ const val WIDTH = 300f
86
+ const val MARGIN = 4f
87
+ const val LEADING_INSET = 22f
88
+ const val TOLERANCE = 0.5f
89
+ const val TEXT = "كود"
90
+ }
91
+ }