@fairyhunter13/opentui-core 0.1.114 → 0.1.115

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 (591) hide show
  1. package/3d/SpriteResourceManager.d.ts +74 -0
  2. package/3d/SpriteUtils.d.ts +13 -0
  3. package/3d/TextureUtils.d.ts +24 -0
  4. package/3d/ThreeRenderable.d.ts +40 -0
  5. package/3d/WGPURenderer.d.ts +61 -0
  6. package/3d/animation/ExplodingSpriteEffect.d.ts +71 -0
  7. package/3d/animation/PhysicsExplodingSpriteEffect.d.ts +76 -0
  8. package/3d/animation/SpriteAnimator.d.ts +124 -0
  9. package/3d/animation/SpriteParticleGenerator.d.ts +62 -0
  10. package/3d/canvas.d.ts +44 -0
  11. package/3d/index.d.ts +12 -0
  12. package/3d/physics/PlanckPhysicsAdapter.d.ts +19 -0
  13. package/3d/physics/RapierPhysicsAdapter.d.ts +19 -0
  14. package/3d/physics/physics-interface.d.ts +27 -0
  15. package/3d.d.ts +2 -0
  16. package/3d.js +34041 -0
  17. package/3d.js.map +155 -0
  18. package/LICENSE +21 -0
  19. package/NativeSpanFeed.d.ts +41 -0
  20. package/Renderable.d.ts +334 -0
  21. package/animation/Timeline.d.ts +126 -0
  22. package/ansi.d.ts +13 -0
  23. package/buffer.d.ts +111 -0
  24. package/console.d.ts +144 -0
  25. package/edit-buffer.d.ts +98 -0
  26. package/editor-view.d.ts +73 -0
  27. package/index-j4m38kjn.js +411 -0
  28. package/index-j4m38kjn.js.map +10 -0
  29. package/index-tse8gzh0.js +20614 -0
  30. package/index-tse8gzh0.js.map +67 -0
  31. package/index-vv2jcd4r.js +12299 -0
  32. package/index-vv2jcd4r.js.map +42 -0
  33. package/index.d.ts +23 -0
  34. package/index.js +478 -0
  35. package/index.js.map +9 -0
  36. package/lib/KeyHandler.d.ts +61 -0
  37. package/lib/RGBA.d.ts +25 -0
  38. package/lib/ascii.font.d.ts +508 -0
  39. package/lib/border.d.ts +51 -0
  40. package/lib/bunfs.d.ts +7 -0
  41. package/lib/clipboard.d.ts +17 -0
  42. package/lib/clock.d.ts +15 -0
  43. package/lib/data-paths.d.ts +26 -0
  44. package/lib/debounce.d.ts +42 -0
  45. package/lib/detect-links.d.ts +6 -0
  46. package/lib/env.d.ts +42 -0
  47. package/lib/extmarks-history.d.ts +17 -0
  48. package/lib/extmarks.d.ts +89 -0
  49. package/lib/hast-styled-text.d.ts +17 -0
  50. package/lib/index.d.ts +21 -0
  51. package/lib/keymapping.d.ts +25 -0
  52. package/lib/objects-in-viewport.d.ts +24 -0
  53. package/lib/output.capture.d.ts +24 -0
  54. package/lib/parse.keypress-kitty.d.ts +2 -0
  55. package/lib/parse.keypress.d.ts +26 -0
  56. package/lib/parse.mouse.d.ts +30 -0
  57. package/lib/paste.d.ts +7 -0
  58. package/lib/queue.d.ts +15 -0
  59. package/lib/renderable.validations.d.ts +12 -0
  60. package/lib/scroll-acceleration.d.ts +43 -0
  61. package/lib/selection.d.ts +63 -0
  62. package/lib/singleton.d.ts +7 -0
  63. package/lib/stdin-parser.d.ts +87 -0
  64. package/lib/styled-text.d.ts +63 -0
  65. package/lib/terminal-capability-detection.d.ts +30 -0
  66. package/lib/terminal-palette.d.ts +50 -0
  67. package/lib/tree-sitter/assets/update.d.ts +11 -0
  68. package/lib/tree-sitter/client.d.ts +47 -0
  69. package/lib/tree-sitter/default-parsers.d.ts +2 -0
  70. package/lib/tree-sitter/download-utils.d.ts +21 -0
  71. package/lib/tree-sitter/index.d.ts +8 -0
  72. package/lib/tree-sitter/parser.worker.d.ts +1 -0
  73. package/lib/tree-sitter/parsers-config.d.ts +53 -0
  74. package/lib/tree-sitter/resolve-ft.d.ts +5 -0
  75. package/lib/tree-sitter/types.d.ts +82 -0
  76. package/lib/tree-sitter-styled-text.d.ts +14 -0
  77. package/lib/validate-dir-name.d.ts +1 -0
  78. package/lib/yoga.options.d.ts +32 -0
  79. package/package.json +50 -62
  80. package/parser.worker.js +899 -0
  81. package/parser.worker.js.map +12 -0
  82. package/plugins/core-slot.d.ts +72 -0
  83. package/plugins/registry.d.ts +42 -0
  84. package/plugins/types.d.ts +34 -0
  85. package/post/effects.d.ts +147 -0
  86. package/post/filters.d.ts +65 -0
  87. package/post/matrices.d.ts +20 -0
  88. package/renderables/ASCIIFont.d.ts +52 -0
  89. package/renderables/Box.d.ts +81 -0
  90. package/renderables/Code.d.ts +78 -0
  91. package/renderables/Diff.d.ts +142 -0
  92. package/renderables/EditBufferRenderable.d.ts +237 -0
  93. package/renderables/FrameBuffer.d.ts +16 -0
  94. package/renderables/Input.d.ts +67 -0
  95. package/renderables/LineNumberRenderable.d.ts +78 -0
  96. package/renderables/Markdown.d.ts +185 -0
  97. package/renderables/ScrollBar.d.ts +77 -0
  98. package/renderables/ScrollBox.d.ts +124 -0
  99. package/renderables/Select.d.ts +115 -0
  100. package/renderables/Slider.d.ts +47 -0
  101. package/renderables/TabSelect.d.ts +96 -0
  102. package/renderables/Text.d.ts +36 -0
  103. package/renderables/TextBufferRenderable.d.ts +105 -0
  104. package/renderables/TextNode.d.ts +91 -0
  105. package/renderables/TextTable.d.ts +140 -0
  106. package/renderables/Textarea.d.ts +63 -0
  107. package/renderables/TimeToFirstDraw.d.ts +24 -0
  108. package/renderables/__tests__/renderable-test-utils.d.ts +12 -0
  109. package/renderables/composition/VRenderable.d.ts +16 -0
  110. package/renderables/composition/constructs.d.ts +35 -0
  111. package/renderables/composition/vnode.d.ts +46 -0
  112. package/renderables/index.d.ts +23 -0
  113. package/renderables/markdown-parser.d.ts +10 -0
  114. package/renderer.d.ts +419 -0
  115. package/runtime-plugin-support.d.ts +3 -0
  116. package/runtime-plugin-support.js +29 -0
  117. package/runtime-plugin-support.js.map +10 -0
  118. package/runtime-plugin.d.ts +16 -0
  119. package/runtime-plugin.js +16 -0
  120. package/runtime-plugin.js.map +9 -0
  121. package/syntax-style.d.ts +54 -0
  122. package/testing/manual-clock.d.ts +17 -0
  123. package/testing/mock-keys.d.ts +81 -0
  124. package/testing/mock-mouse.d.ts +38 -0
  125. package/testing/mock-tree-sitter-client.d.ts +23 -0
  126. package/testing/spy.d.ts +7 -0
  127. package/testing/test-recorder.d.ts +61 -0
  128. package/testing/test-renderer.d.ts +23 -0
  129. package/testing.d.ts +6 -0
  130. package/testing.js +697 -0
  131. package/testing.js.map +15 -0
  132. package/text-buffer-view.d.ts +42 -0
  133. package/text-buffer.d.ts +67 -0
  134. package/types.d.ts +139 -0
  135. package/utils.d.ts +14 -0
  136. package/zig-structs.d.ts +155 -0
  137. package/zig.d.ts +353 -0
  138. package/dev/keypress-debug-renderer.ts +0 -148
  139. package/dev/keypress-debug.ts +0 -43
  140. package/dev/print-env-vars.ts +0 -32
  141. package/dev/test-tmux-graphics-334.sh +0 -68
  142. package/dev/thai-debug-test.ts +0 -68
  143. package/docs/development.md +0 -144
  144. package/scripts/build.ts +0 -400
  145. package/scripts/publish.ts +0 -60
  146. package/src/3d/SpriteResourceManager.ts +0 -286
  147. package/src/3d/SpriteUtils.ts +0 -70
  148. package/src/3d/TextureUtils.ts +0 -196
  149. package/src/3d/ThreeRenderable.ts +0 -197
  150. package/src/3d/WGPURenderer.ts +0 -294
  151. package/src/3d/animation/ExplodingSpriteEffect.ts +0 -513
  152. package/src/3d/animation/PhysicsExplodingSpriteEffect.ts +0 -429
  153. package/src/3d/animation/SpriteAnimator.ts +0 -633
  154. package/src/3d/animation/SpriteParticleGenerator.ts +0 -435
  155. package/src/3d/canvas.ts +0 -464
  156. package/src/3d/index.ts +0 -12
  157. package/src/3d/physics/PlanckPhysicsAdapter.ts +0 -72
  158. package/src/3d/physics/RapierPhysicsAdapter.ts +0 -66
  159. package/src/3d/physics/physics-interface.ts +0 -31
  160. package/src/3d/shaders/supersampling.wgsl +0 -201
  161. package/src/3d.ts +0 -3
  162. package/src/NativeSpanFeed.ts +0 -300
  163. package/src/Renderable.ts +0 -1704
  164. package/src/__snapshots__/buffer.test.ts.snap +0 -28
  165. package/src/animation/Timeline.test.ts +0 -2709
  166. package/src/animation/Timeline.ts +0 -598
  167. package/src/ansi.ts +0 -18
  168. package/src/benchmark/attenuation-benchmark.ts +0 -81
  169. package/src/benchmark/colormatrix-benchmark.ts +0 -128
  170. package/src/benchmark/gain-benchmark.ts +0 -80
  171. package/src/benchmark/latest-all-bench-run.json +0 -707
  172. package/src/benchmark/latest-async-bench-run.json +0 -336
  173. package/src/benchmark/latest-default-bench-run.json +0 -657
  174. package/src/benchmark/latest-large-bench-run.json +0 -707
  175. package/src/benchmark/latest-quick-bench-run.json +0 -207
  176. package/src/benchmark/markdown-benchmark.ts +0 -1796
  177. package/src/benchmark/native-span-feed-async-benchmark.ts +0 -355
  178. package/src/benchmark/native-span-feed-benchmark.md +0 -56
  179. package/src/benchmark/native-span-feed-benchmark.ts +0 -596
  180. package/src/benchmark/native-span-feed-compare.ts +0 -280
  181. package/src/benchmark/renderer-benchmark.ts +0 -754
  182. package/src/benchmark/text-table-benchmark.ts +0 -948
  183. package/src/buffer.test.ts +0 -291
  184. package/src/buffer.ts +0 -554
  185. package/src/console.test.ts +0 -612
  186. package/src/console.ts +0 -1254
  187. package/src/edit-buffer.test.ts +0 -1769
  188. package/src/edit-buffer.ts +0 -411
  189. package/src/editor-view.test.ts +0 -1032
  190. package/src/editor-view.ts +0 -284
  191. package/src/examples/ascii-font-selection-demo.ts +0 -245
  192. package/src/examples/assets/Water_2_M_Normal.jpg +0 -0
  193. package/src/examples/assets/concrete.png +0 -0
  194. package/src/examples/assets/crate.png +0 -0
  195. package/src/examples/assets/crate_emissive.png +0 -0
  196. package/src/examples/assets/forrest_background.png +0 -0
  197. package/src/examples/assets/hast-example.json +0 -1018
  198. package/src/examples/assets/heart.png +0 -0
  199. package/src/examples/assets/main_char_heavy_attack.png +0 -0
  200. package/src/examples/assets/main_char_idle.png +0 -0
  201. package/src/examples/assets/main_char_jump_end.png +0 -0
  202. package/src/examples/assets/main_char_jump_landing.png +0 -0
  203. package/src/examples/assets/main_char_jump_start.png +0 -0
  204. package/src/examples/assets/main_char_run_loop.png +0 -0
  205. package/src/examples/assets/roughness_map.jpg +0 -0
  206. package/src/examples/build.ts +0 -115
  207. package/src/examples/code-demo.ts +0 -924
  208. package/src/examples/console-demo.ts +0 -358
  209. package/src/examples/core-plugin-slots-demo.ts +0 -759
  210. package/src/examples/diff-demo.ts +0 -701
  211. package/src/examples/draggable-three-demo.ts +0 -259
  212. package/src/examples/editor-demo.ts +0 -322
  213. package/src/examples/extmarks-demo.ts +0 -196
  214. package/src/examples/focus-restore-demo.ts +0 -310
  215. package/src/examples/fonts.ts +0 -245
  216. package/src/examples/fractal-shader-demo.ts +0 -268
  217. package/src/examples/framebuffer-demo.ts +0 -674
  218. package/src/examples/full-unicode-demo.ts +0 -241
  219. package/src/examples/golden-star-demo.ts +0 -933
  220. package/src/examples/grayscale-buffer-demo.ts +0 -249
  221. package/src/examples/hast-syntax-highlighting-demo.ts +0 -129
  222. package/src/examples/index.ts +0 -926
  223. package/src/examples/input-demo.ts +0 -377
  224. package/src/examples/input-select-layout-demo.ts +0 -425
  225. package/src/examples/install.sh +0 -143
  226. package/src/examples/keypress-debug-demo.ts +0 -452
  227. package/src/examples/lib/HexList.ts +0 -122
  228. package/src/examples/lib/PaletteGrid.ts +0 -125
  229. package/src/examples/lib/standalone-keys.ts +0 -25
  230. package/src/examples/lib/tab-controller.ts +0 -243
  231. package/src/examples/lights-phong-demo.ts +0 -290
  232. package/src/examples/link-demo.ts +0 -220
  233. package/src/examples/live-state-demo.ts +0 -480
  234. package/src/examples/markdown-demo.ts +0 -725
  235. package/src/examples/mouse-interaction-demo.ts +0 -428
  236. package/src/examples/nested-zindex-demo.ts +0 -357
  237. package/src/examples/opacity-example.ts +0 -235
  238. package/src/examples/opentui-demo.ts +0 -1057
  239. package/src/examples/physx-planck-2d-demo.ts +0 -623
  240. package/src/examples/physx-rapier-2d-demo.ts +0 -655
  241. package/src/examples/relative-positioning-demo.ts +0 -323
  242. package/src/examples/scroll-example.ts +0 -214
  243. package/src/examples/scrollbox-mouse-test.ts +0 -112
  244. package/src/examples/scrollbox-overlay-hit-test.ts +0 -206
  245. package/src/examples/select-demo.ts +0 -237
  246. package/src/examples/shader-cube-demo.ts +0 -1015
  247. package/src/examples/simple-layout-example.ts +0 -591
  248. package/src/examples/slider-demo.ts +0 -617
  249. package/src/examples/split-mode-demo.ts +0 -453
  250. package/src/examples/sprite-animation-demo.ts +0 -443
  251. package/src/examples/sprite-particle-generator-demo.ts +0 -486
  252. package/src/examples/static-sprite-demo.ts +0 -193
  253. package/src/examples/sticky-scroll-example.ts +0 -308
  254. package/src/examples/styled-text-demo.ts +0 -282
  255. package/src/examples/tab-select-demo.ts +0 -219
  256. package/src/examples/terminal-title.ts +0 -29
  257. package/src/examples/terminal.ts +0 -305
  258. package/src/examples/text-node-demo.ts +0 -416
  259. package/src/examples/text-selection-demo.ts +0 -377
  260. package/src/examples/text-table-demo.ts +0 -503
  261. package/src/examples/text-truncation-demo.ts +0 -481
  262. package/src/examples/text-wrap.ts +0 -757
  263. package/src/examples/texture-loading-demo.ts +0 -259
  264. package/src/examples/timeline-example.ts +0 -670
  265. package/src/examples/transparency-demo.ts +0 -400
  266. package/src/examples/vnode-composition-demo.ts +0 -404
  267. package/src/examples/wide-grapheme-overlay-demo.ts +0 -280
  268. package/src/index.ts +0 -24
  269. package/src/lib/KeyHandler.integration.test.ts +0 -292
  270. package/src/lib/KeyHandler.stopPropagation.test.ts +0 -289
  271. package/src/lib/KeyHandler.test.ts +0 -662
  272. package/src/lib/KeyHandler.ts +0 -222
  273. package/src/lib/RGBA.test.ts +0 -984
  274. package/src/lib/RGBA.ts +0 -204
  275. package/src/lib/ascii.font.ts +0 -330
  276. package/src/lib/border.test.ts +0 -83
  277. package/src/lib/border.ts +0 -170
  278. package/src/lib/bunfs.test.ts +0 -27
  279. package/src/lib/bunfs.ts +0 -18
  280. package/src/lib/clipboard.test.ts +0 -41
  281. package/src/lib/clipboard.ts +0 -47
  282. package/src/lib/clock.ts +0 -35
  283. package/src/lib/data-paths.test.ts +0 -133
  284. package/src/lib/data-paths.ts +0 -109
  285. package/src/lib/debounce.ts +0 -106
  286. package/src/lib/detect-links.test.ts +0 -98
  287. package/src/lib/detect-links.ts +0 -56
  288. package/src/lib/env.test.ts +0 -228
  289. package/src/lib/env.ts +0 -209
  290. package/src/lib/extmarks-history.ts +0 -51
  291. package/src/lib/extmarks-multiwidth.test.ts +0 -322
  292. package/src/lib/extmarks.test.ts +0 -3457
  293. package/src/lib/extmarks.ts +0 -843
  294. package/src/lib/fonts/block.json +0 -405
  295. package/src/lib/fonts/grid.json +0 -265
  296. package/src/lib/fonts/huge.json +0 -741
  297. package/src/lib/fonts/pallet.json +0 -314
  298. package/src/lib/fonts/shade.json +0 -591
  299. package/src/lib/fonts/slick.json +0 -321
  300. package/src/lib/fonts/tiny.json +0 -69
  301. package/src/lib/hast-styled-text.ts +0 -59
  302. package/src/lib/index.ts +0 -21
  303. package/src/lib/keymapping.test.ts +0 -317
  304. package/src/lib/keymapping.ts +0 -115
  305. package/src/lib/objects-in-viewport.test.ts +0 -787
  306. package/src/lib/objects-in-viewport.ts +0 -153
  307. package/src/lib/output.capture.ts +0 -58
  308. package/src/lib/parse.keypress-kitty.protocol.test.ts +0 -340
  309. package/src/lib/parse.keypress-kitty.test.ts +0 -663
  310. package/src/lib/parse.keypress-kitty.ts +0 -439
  311. package/src/lib/parse.keypress.test.ts +0 -1849
  312. package/src/lib/parse.keypress.ts +0 -397
  313. package/src/lib/parse.mouse.test.ts +0 -552
  314. package/src/lib/parse.mouse.ts +0 -232
  315. package/src/lib/paste.ts +0 -16
  316. package/src/lib/queue.ts +0 -65
  317. package/src/lib/renderable.validations.test.ts +0 -87
  318. package/src/lib/renderable.validations.ts +0 -83
  319. package/src/lib/scroll-acceleration.ts +0 -98
  320. package/src/lib/selection.ts +0 -240
  321. package/src/lib/singleton.ts +0 -28
  322. package/src/lib/stdin-parser.test.ts +0 -2290
  323. package/src/lib/stdin-parser.ts +0 -1810
  324. package/src/lib/styled-text.ts +0 -178
  325. package/src/lib/terminal-capability-detection.test.ts +0 -202
  326. package/src/lib/terminal-capability-detection.ts +0 -79
  327. package/src/lib/terminal-palette.test.ts +0 -878
  328. package/src/lib/terminal-palette.ts +0 -383
  329. package/src/lib/tree-sitter/assets/README.md +0 -118
  330. package/src/lib/tree-sitter/assets/update.ts +0 -334
  331. package/src/lib/tree-sitter/assets.d.ts +0 -9
  332. package/src/lib/tree-sitter/cache.test.ts +0 -273
  333. package/src/lib/tree-sitter/client.test.ts +0 -1165
  334. package/src/lib/tree-sitter/client.ts +0 -607
  335. package/src/lib/tree-sitter/default-parsers.ts +0 -86
  336. package/src/lib/tree-sitter/download-utils.ts +0 -148
  337. package/src/lib/tree-sitter/index.ts +0 -28
  338. package/src/lib/tree-sitter/parser.worker.ts +0 -1042
  339. package/src/lib/tree-sitter/parsers-config.ts +0 -81
  340. package/src/lib/tree-sitter/resolve-ft.test.ts +0 -55
  341. package/src/lib/tree-sitter/resolve-ft.ts +0 -189
  342. package/src/lib/tree-sitter/types.ts +0 -82
  343. package/src/lib/tree-sitter-styled-text.test.ts +0 -1253
  344. package/src/lib/tree-sitter-styled-text.ts +0 -306
  345. package/src/lib/validate-dir-name.ts +0 -55
  346. package/src/lib/yoga.options.test.ts +0 -628
  347. package/src/lib/yoga.options.ts +0 -346
  348. package/src/plugins/core-slot.ts +0 -579
  349. package/src/plugins/registry.ts +0 -402
  350. package/src/plugins/types.ts +0 -46
  351. package/src/post/effects.ts +0 -930
  352. package/src/post/filters.ts +0 -489
  353. package/src/post/matrices.ts +0 -288
  354. package/src/renderables/ASCIIFont.ts +0 -219
  355. package/src/renderables/Box.test.ts +0 -205
  356. package/src/renderables/Box.ts +0 -326
  357. package/src/renderables/Code.test.ts +0 -2062
  358. package/src/renderables/Code.ts +0 -357
  359. package/src/renderables/Diff.regression.test.ts +0 -226
  360. package/src/renderables/Diff.test.ts +0 -3101
  361. package/src/renderables/Diff.ts +0 -1211
  362. package/src/renderables/EditBufferRenderable.test.ts +0 -288
  363. package/src/renderables/EditBufferRenderable.ts +0 -1166
  364. package/src/renderables/FrameBuffer.ts +0 -47
  365. package/src/renderables/Input.test.ts +0 -1228
  366. package/src/renderables/Input.ts +0 -247
  367. package/src/renderables/LineNumberRenderable.ts +0 -724
  368. package/src/renderables/Markdown.ts +0 -1393
  369. package/src/renderables/ScrollBar.ts +0 -422
  370. package/src/renderables/ScrollBox.ts +0 -883
  371. package/src/renderables/Select.test.ts +0 -1033
  372. package/src/renderables/Select.ts +0 -524
  373. package/src/renderables/Slider.test.ts +0 -456
  374. package/src/renderables/Slider.ts +0 -342
  375. package/src/renderables/TabSelect.test.ts +0 -197
  376. package/src/renderables/TabSelect.ts +0 -455
  377. package/src/renderables/Text.selection-buffer.test.ts +0 -123
  378. package/src/renderables/Text.test.ts +0 -2660
  379. package/src/renderables/Text.ts +0 -147
  380. package/src/renderables/TextBufferRenderable.ts +0 -518
  381. package/src/renderables/TextNode.test.ts +0 -1058
  382. package/src/renderables/TextNode.ts +0 -325
  383. package/src/renderables/TextTable.test.ts +0 -1421
  384. package/src/renderables/TextTable.ts +0 -1344
  385. package/src/renderables/Textarea.ts +0 -430
  386. package/src/renderables/TimeToFirstDraw.ts +0 -89
  387. package/src/renderables/__snapshots__/Code.test.ts.snap +0 -13
  388. package/src/renderables/__snapshots__/Diff.test.ts.snap +0 -785
  389. package/src/renderables/__snapshots__/Text.test.ts.snap +0 -421
  390. package/src/renderables/__snapshots__/TextTable.test.ts.snap +0 -215
  391. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +0 -144
  392. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +0 -816
  393. package/src/renderables/__tests__/LineNumberRenderable.test.ts +0 -1865
  394. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +0 -85
  395. package/src/renderables/__tests__/Markdown.code-colors.test.ts +0 -242
  396. package/src/renderables/__tests__/Markdown.test.ts +0 -2518
  397. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +0 -87
  398. package/src/renderables/__tests__/Textarea.buffer.test.ts +0 -682
  399. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +0 -675
  400. package/src/renderables/__tests__/Textarea.editing.test.ts +0 -2041
  401. package/src/renderables/__tests__/Textarea.error-handling.test.ts +0 -35
  402. package/src/renderables/__tests__/Textarea.events.test.ts +0 -738
  403. package/src/renderables/__tests__/Textarea.highlights.test.ts +0 -590
  404. package/src/renderables/__tests__/Textarea.keybinding.test.ts +0 -3149
  405. package/src/renderables/__tests__/Textarea.paste.test.ts +0 -357
  406. package/src/renderables/__tests__/Textarea.rendering.test.ts +0 -1866
  407. package/src/renderables/__tests__/Textarea.scroll.test.ts +0 -733
  408. package/src/renderables/__tests__/Textarea.selection.test.ts +0 -1590
  409. package/src/renderables/__tests__/Textarea.stress.test.ts +0 -670
  410. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +0 -383
  411. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +0 -310
  412. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +0 -221
  413. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +0 -89
  414. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +0 -457
  415. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +0 -158
  416. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +0 -387
  417. package/src/renderables/__tests__/markdown-parser.test.ts +0 -217
  418. package/src/renderables/__tests__/renderable-test-utils.ts +0 -60
  419. package/src/renderables/composition/README.md +0 -8
  420. package/src/renderables/composition/VRenderable.ts +0 -32
  421. package/src/renderables/composition/constructs.ts +0 -127
  422. package/src/renderables/composition/vnode.ts +0 -289
  423. package/src/renderables/index.ts +0 -23
  424. package/src/renderables/markdown-parser.ts +0 -66
  425. package/src/renderer.ts +0 -2681
  426. package/src/runtime-plugin-support.ts +0 -39
  427. package/src/runtime-plugin.ts +0 -615
  428. package/src/syntax-style.test.ts +0 -841
  429. package/src/syntax-style.ts +0 -257
  430. package/src/testing/README.md +0 -210
  431. package/src/testing/capture-spans.test.ts +0 -194
  432. package/src/testing/integration.test.ts +0 -276
  433. package/src/testing/manual-clock.ts +0 -117
  434. package/src/testing/mock-keys.test.ts +0 -1378
  435. package/src/testing/mock-keys.ts +0 -457
  436. package/src/testing/mock-mouse.test.ts +0 -218
  437. package/src/testing/mock-mouse.ts +0 -247
  438. package/src/testing/mock-tree-sitter-client.ts +0 -73
  439. package/src/testing/spy.ts +0 -13
  440. package/src/testing/test-recorder.test.ts +0 -415
  441. package/src/testing/test-recorder.ts +0 -145
  442. package/src/testing/test-renderer.ts +0 -132
  443. package/src/testing.ts +0 -7
  444. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +0 -481
  445. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +0 -19
  446. package/src/tests/__snapshots__/scrollbox.test.ts.snap +0 -29
  447. package/src/tests/absolute-positioning.snapshot.test.ts +0 -638
  448. package/src/tests/allocator-stats.test.ts +0 -38
  449. package/src/tests/destroy-during-render.test.ts +0 -200
  450. package/src/tests/destroy-on-exit.fixture.ts +0 -36
  451. package/src/tests/destroy-on-exit.test.ts +0 -41
  452. package/src/tests/hover-cursor.test.ts +0 -98
  453. package/src/tests/native-span-feed-async.test.ts +0 -173
  454. package/src/tests/native-span-feed-close.test.ts +0 -120
  455. package/src/tests/native-span-feed-coverage.test.ts +0 -227
  456. package/src/tests/native-span-feed-edge-cases.test.ts +0 -352
  457. package/src/tests/native-span-feed-use-after-free.test.ts +0 -45
  458. package/src/tests/opacity.test.ts +0 -123
  459. package/src/tests/renderable.snapshot.test.ts +0 -524
  460. package/src/tests/renderable.test.ts +0 -1281
  461. package/src/tests/renderer.clock.test.ts +0 -158
  462. package/src/tests/renderer.console-startup.test.ts +0 -185
  463. package/src/tests/renderer.control.test.ts +0 -425
  464. package/src/tests/renderer.core-slot-binding.test.ts +0 -952
  465. package/src/tests/renderer.cursor.test.ts +0 -26
  466. package/src/tests/renderer.destroy-during-render.test.ts +0 -147
  467. package/src/tests/renderer.focus-restore.test.ts +0 -257
  468. package/src/tests/renderer.focus.test.ts +0 -294
  469. package/src/tests/renderer.idle.test.ts +0 -219
  470. package/src/tests/renderer.input.test.ts +0 -2237
  471. package/src/tests/renderer.kitty-flags.test.ts +0 -195
  472. package/src/tests/renderer.mouse.test.ts +0 -1274
  473. package/src/tests/renderer.palette.test.ts +0 -629
  474. package/src/tests/renderer.selection.test.ts +0 -49
  475. package/src/tests/renderer.slot-registry.test.ts +0 -684
  476. package/src/tests/renderer.useMouse.test.ts +0 -47
  477. package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +0 -76
  478. package/src/tests/runtime-plugin-node-modules-mjs.fixture.ts +0 -43
  479. package/src/tests/runtime-plugin-node-modules-no-bare-rewrite.fixture.ts +0 -67
  480. package/src/tests/runtime-plugin-node-modules-package-type-cache.fixture.ts +0 -72
  481. package/src/tests/runtime-plugin-node-modules-runtime-specifier.fixture.ts +0 -44
  482. package/src/tests/runtime-plugin-node-modules-scoped-package-bare-rewrite.fixture.ts +0 -85
  483. package/src/tests/runtime-plugin-path-alias.fixture.ts +0 -43
  484. package/src/tests/runtime-plugin-resolve-roots.fixture.ts +0 -65
  485. package/src/tests/runtime-plugin-support.fixture.ts +0 -11
  486. package/src/tests/runtime-plugin-support.test.ts +0 -19
  487. package/src/tests/runtime-plugin-windows-file-url.fixture.ts +0 -30
  488. package/src/tests/runtime-plugin.fixture.ts +0 -40
  489. package/src/tests/runtime-plugin.test.ts +0 -354
  490. package/src/tests/scrollbox-culling-bug.test.ts +0 -114
  491. package/src/tests/scrollbox-hitgrid-resize.test.ts +0 -136
  492. package/src/tests/scrollbox-hitgrid.test.ts +0 -909
  493. package/src/tests/scrollbox.test.ts +0 -1530
  494. package/src/tests/wrap-resize-perf.test.ts +0 -276
  495. package/src/tests/yoga-setters.test.ts +0 -921
  496. package/src/text-buffer-view.test.ts +0 -705
  497. package/src/text-buffer-view.ts +0 -189
  498. package/src/text-buffer.test.ts +0 -347
  499. package/src/text-buffer.ts +0 -250
  500. package/src/types.ts +0 -161
  501. package/src/utils.ts +0 -88
  502. package/src/zig/ansi.zig +0 -268
  503. package/src/zig/bench/README.md +0 -50
  504. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +0 -887
  505. package/src/zig/bench/edit-buffer_bench.zig +0 -476
  506. package/src/zig/bench/native-span-feed_bench.zig +0 -100
  507. package/src/zig/bench/rope-markers_bench.zig +0 -713
  508. package/src/zig/bench/rope_bench.zig +0 -514
  509. package/src/zig/bench/styled-text_bench.zig +0 -470
  510. package/src/zig/bench/text-buffer-coords_bench.zig +0 -362
  511. package/src/zig/bench/text-buffer-view_bench.zig +0 -459
  512. package/src/zig/bench/text-chunk-graphemes_bench.zig +0 -273
  513. package/src/zig/bench/utf8_bench.zig +0 -799
  514. package/src/zig/bench-utils.zig +0 -431
  515. package/src/zig/bench.zig +0 -217
  516. package/src/zig/buffer-methods.zig +0 -211
  517. package/src/zig/buffer.zig +0 -2281
  518. package/src/zig/build.zig +0 -289
  519. package/src/zig/build.zig.zon +0 -16
  520. package/src/zig/edit-buffer.zig +0 -825
  521. package/src/zig/editor-view.zig +0 -802
  522. package/src/zig/event-bus.zig +0 -13
  523. package/src/zig/event-emitter.zig +0 -65
  524. package/src/zig/file-logger.zig +0 -92
  525. package/src/zig/grapheme.zig +0 -599
  526. package/src/zig/lib.zig +0 -1854
  527. package/src/zig/link.zig +0 -333
  528. package/src/zig/logger.zig +0 -43
  529. package/src/zig/mem-registry.zig +0 -125
  530. package/src/zig/native-span-feed-bench-lib.zig +0 -7
  531. package/src/zig/native-span-feed.zig +0 -708
  532. package/src/zig/renderer.zig +0 -1393
  533. package/src/zig/rope.zig +0 -1220
  534. package/src/zig/syntax-style.zig +0 -161
  535. package/src/zig/terminal.zig +0 -987
  536. package/src/zig/test.zig +0 -72
  537. package/src/zig/tests/README.md +0 -18
  538. package/src/zig/tests/buffer-methods_test.zig +0 -1109
  539. package/src/zig/tests/buffer_test.zig +0 -2557
  540. package/src/zig/tests/edit-buffer-history_test.zig +0 -271
  541. package/src/zig/tests/edit-buffer_test.zig +0 -1689
  542. package/src/zig/tests/editor-view_test.zig +0 -3299
  543. package/src/zig/tests/event-emitter_test.zig +0 -249
  544. package/src/zig/tests/grapheme_test.zig +0 -1304
  545. package/src/zig/tests/link_test.zig +0 -190
  546. package/src/zig/tests/mem-registry_test.zig +0 -473
  547. package/src/zig/tests/memory_leak_regression_test.zig +0 -159
  548. package/src/zig/tests/native-span-feed_test.zig +0 -1264
  549. package/src/zig/tests/renderer_test.zig +0 -1017
  550. package/src/zig/tests/rope-nested_test.zig +0 -712
  551. package/src/zig/tests/rope_fuzz_test.zig +0 -238
  552. package/src/zig/tests/rope_test.zig +0 -2362
  553. package/src/zig/tests/segment-merge.test.zig +0 -148
  554. package/src/zig/tests/syntax-style_test.zig +0 -557
  555. package/src/zig/tests/terminal_test.zig +0 -754
  556. package/src/zig/tests/text-buffer-drawing_test.zig +0 -3237
  557. package/src/zig/tests/text-buffer-highlights_test.zig +0 -666
  558. package/src/zig/tests/text-buffer-iterators_test.zig +0 -776
  559. package/src/zig/tests/text-buffer-segment_test.zig +0 -320
  560. package/src/zig/tests/text-buffer-selection_test.zig +0 -1035
  561. package/src/zig/tests/text-buffer-selection_viewport_test.zig +0 -358
  562. package/src/zig/tests/text-buffer-view_test.zig +0 -3649
  563. package/src/zig/tests/text-buffer_test.zig +0 -2191
  564. package/src/zig/tests/unicode-width-map.zon +0 -3909
  565. package/src/zig/tests/utf8_no_zwj_test.zig +0 -260
  566. package/src/zig/tests/utf8_test.zig +0 -4057
  567. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +0 -267
  568. package/src/zig/tests/utf8_wcwidth_test.zig +0 -357
  569. package/src/zig/tests/word-wrap-editing_test.zig +0 -498
  570. package/src/zig/tests/wrap-cache-perf_test.zig +0 -113
  571. package/src/zig/text-buffer-iterators.zig +0 -499
  572. package/src/zig/text-buffer-segment.zig +0 -404
  573. package/src/zig/text-buffer-view.zig +0 -1371
  574. package/src/zig/text-buffer.zig +0 -1180
  575. package/src/zig/utf8.zig +0 -1948
  576. package/src/zig/utils.zig +0 -9
  577. package/src/zig-structs.ts +0 -261
  578. package/src/zig.ts +0 -3884
  579. package/tsconfig.build.json +0 -24
  580. package/tsconfig.json +0 -27
  581. /package/{src/lib/tree-sitter/assets → assets}/javascript/highlights.scm +0 -0
  582. /package/{src/lib/tree-sitter/assets → assets}/javascript/tree-sitter-javascript.wasm +0 -0
  583. /package/{src/lib/tree-sitter/assets → assets}/markdown/highlights.scm +0 -0
  584. /package/{src/lib/tree-sitter/assets → assets}/markdown/injections.scm +0 -0
  585. /package/{src/lib/tree-sitter/assets → assets}/markdown/tree-sitter-markdown.wasm +0 -0
  586. /package/{src/lib/tree-sitter/assets → assets}/markdown_inline/highlights.scm +0 -0
  587. /package/{src/lib/tree-sitter/assets → assets}/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  588. /package/{src/lib/tree-sitter/assets → assets}/typescript/highlights.scm +0 -0
  589. /package/{src/lib/tree-sitter/assets → assets}/typescript/tree-sitter-typescript.wasm +0 -0
  590. /package/{src/lib/tree-sitter/assets → assets}/zig/highlights.scm +0 -0
  591. /package/{src/lib/tree-sitter/assets → assets}/zig/tree-sitter-zig.wasm +0 -0
@@ -1,3237 +0,0 @@
1
- const std = @import("std");
2
- const text_buffer = @import("../text-buffer.zig");
3
- const text_buffer_view = @import("../text-buffer-view.zig");
4
- const buffer = @import("../buffer.zig");
5
- const gp = @import("../grapheme.zig");
6
- const link = @import("../link.zig");
7
- const ss = @import("../syntax-style.zig");
8
-
9
- const TextBuffer = text_buffer.TextBuffer;
10
- const TextBufferView = text_buffer_view.TextBufferView;
11
- const OptimizedBuffer = buffer.OptimizedBuffer;
12
- const RGBA = text_buffer.RGBA;
13
- const WrapMode = text_buffer.WrapMode;
14
- const StyledChunk = text_buffer.StyledChunk;
15
-
16
- test "drawTextBuffer - simple single line text" {
17
- const pool = gp.initGlobalPool(std.testing.allocator);
18
- defer gp.deinitGlobalPool();
19
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
20
- defer link.deinitGlobalLinkPool();
21
-
22
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
23
- defer tb.deinit();
24
-
25
- var view = try TextBufferView.init(std.testing.allocator, tb);
26
- defer view.deinit();
27
-
28
- try tb.setText("Hello World");
29
-
30
- var opt_buffer = try OptimizedBuffer.init(
31
- std.testing.allocator,
32
- 20,
33
- 5,
34
- .{ .pool = pool, .width_method = .unicode },
35
- );
36
- defer opt_buffer.deinit();
37
-
38
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
39
- try opt_buffer.drawTextBuffer(view, 0, 0);
40
-
41
- var out_buffer: [100]u8 = undefined;
42
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
43
- const result = out_buffer[0..written];
44
-
45
- try std.testing.expect(std.mem.startsWith(u8, result, "Hello World"));
46
- }
47
-
48
- test "drawTextBuffer - empty text buffer" {
49
- const pool = gp.initGlobalPool(std.testing.allocator);
50
- defer gp.deinitGlobalPool();
51
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
52
- defer link.deinitGlobalLinkPool();
53
-
54
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
55
- defer tb.deinit();
56
-
57
- var view = try TextBufferView.init(std.testing.allocator, tb);
58
- defer view.deinit();
59
-
60
- try tb.setText("");
61
-
62
- var opt_buffer = try OptimizedBuffer.init(
63
- std.testing.allocator,
64
- 20,
65
- 5,
66
- .{ .pool = pool, .width_method = .unicode },
67
- );
68
- defer opt_buffer.deinit();
69
-
70
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
71
- try opt_buffer.drawTextBuffer(view, 0, 0);
72
- }
73
-
74
- test "drawTextBuffer - multiple lines without wrapping" {
75
- const pool = gp.initGlobalPool(std.testing.allocator);
76
- defer gp.deinitGlobalPool();
77
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
78
- defer link.deinitGlobalLinkPool();
79
-
80
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
81
- defer tb.deinit();
82
-
83
- var view = try TextBufferView.init(std.testing.allocator, tb);
84
- defer view.deinit();
85
-
86
- try tb.setText("Line 1\nLine 2\nLine 3");
87
-
88
- var opt_buffer = try OptimizedBuffer.init(
89
- std.testing.allocator,
90
- 20,
91
- 10,
92
- .{ .pool = pool, .width_method = .unicode },
93
- );
94
- defer opt_buffer.deinit();
95
-
96
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
97
- try opt_buffer.drawTextBuffer(view, 0, 0);
98
-
99
- const virtual_lines = view.getVirtualLines();
100
- try std.testing.expect(virtual_lines.len == 3);
101
- }
102
-
103
- test "drawTextBuffer - text wrapping at word boundaries" {
104
- const pool = gp.initGlobalPool(std.testing.allocator);
105
- defer gp.deinitGlobalPool();
106
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
107
- defer link.deinitGlobalLinkPool();
108
-
109
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
110
- defer tb.deinit();
111
-
112
- var view = try TextBufferView.init(std.testing.allocator, tb);
113
- defer view.deinit();
114
-
115
- try tb.setText("This is a long line that should wrap at word boundaries");
116
- view.setWrapMode(.word);
117
- view.setWrapWidth(15);
118
- view.updateVirtualLines();
119
-
120
- var opt_buffer = try OptimizedBuffer.init(
121
- std.testing.allocator,
122
- 15,
123
- 10,
124
- .{ .pool = pool, .width_method = .unicode },
125
- );
126
- defer opt_buffer.deinit();
127
-
128
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
129
- try opt_buffer.drawTextBuffer(view, 0, 0);
130
-
131
- const virtual_lines = view.getVirtualLines();
132
- try std.testing.expect(virtual_lines.len > 1);
133
- }
134
-
135
- test "drawTextBuffer - text wrapping at character boundaries" {
136
- const pool = gp.initGlobalPool(std.testing.allocator);
137
- defer gp.deinitGlobalPool();
138
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
139
- defer link.deinitGlobalLinkPool();
140
-
141
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
142
- defer tb.deinit();
143
-
144
- var view = try TextBufferView.init(std.testing.allocator, tb);
145
- defer view.deinit();
146
-
147
- try tb.setText("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
148
- view.setWrapMode(.char);
149
- view.setWrapWidth(10);
150
- view.updateVirtualLines();
151
-
152
- var opt_buffer = try OptimizedBuffer.init(
153
- std.testing.allocator,
154
- 10,
155
- 10,
156
- .{ .pool = pool, .width_method = .unicode },
157
- );
158
- defer opt_buffer.deinit();
159
-
160
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
161
- try opt_buffer.drawTextBuffer(view, 0, 0);
162
-
163
- const virtual_lines = view.getVirtualLines();
164
- try std.testing.expect(virtual_lines.len == 4);
165
- }
166
-
167
- test "drawTextBuffer - no wrapping with none mode" {
168
- const pool = gp.initGlobalPool(std.testing.allocator);
169
- defer gp.deinitGlobalPool();
170
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
171
- defer link.deinitGlobalLinkPool();
172
-
173
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
174
- defer tb.deinit();
175
-
176
- var view = try TextBufferView.init(std.testing.allocator, tb);
177
- defer view.deinit();
178
-
179
- try tb.setText("This is a very long line that extends beyond the buffer width");
180
- view.setWrapMode(.word);
181
- view.setWrapWidth(null);
182
- view.updateVirtualLines();
183
-
184
- var opt_buffer = try OptimizedBuffer.init(
185
- std.testing.allocator,
186
- 20,
187
- 5,
188
- .{ .pool = pool, .width_method = .unicode },
189
- );
190
- defer opt_buffer.deinit();
191
-
192
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
193
- try opt_buffer.drawTextBuffer(view, 0, 0);
194
-
195
- const virtual_lines = view.getVirtualLines();
196
- try std.testing.expect(virtual_lines.len == 1);
197
- }
198
-
199
- test "drawTextBuffer - wrapped text with multiple lines" {
200
- const pool = gp.initGlobalPool(std.testing.allocator);
201
- defer gp.deinitGlobalPool();
202
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
203
- defer link.deinitGlobalLinkPool();
204
-
205
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
206
- defer tb.deinit();
207
-
208
- var view = try TextBufferView.init(std.testing.allocator, tb);
209
- defer view.deinit();
210
-
211
- try tb.setText("First long line that wraps\nSecond long line that also wraps\nThird line");
212
- view.setWrapMode(.word);
213
- view.setWrapWidth(15);
214
- view.updateVirtualLines();
215
-
216
- var opt_buffer = try OptimizedBuffer.init(
217
- std.testing.allocator,
218
- 15,
219
- 15,
220
- .{ .pool = pool, .width_method = .unicode },
221
- );
222
- defer opt_buffer.deinit();
223
-
224
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
225
- try opt_buffer.drawTextBuffer(view, 0, 0);
226
-
227
- const virtual_lines = view.getVirtualLines();
228
- try std.testing.expect(virtual_lines.len >= 3);
229
- }
230
-
231
- test "drawTextBuffer - unicode characters with wrapping" {
232
- const pool = gp.initGlobalPool(std.testing.allocator);
233
- defer gp.deinitGlobalPool();
234
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
235
- defer link.deinitGlobalLinkPool();
236
-
237
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
238
- defer tb.deinit();
239
-
240
- var view = try TextBufferView.init(std.testing.allocator, tb);
241
- defer view.deinit();
242
-
243
- try tb.setText("Hello 世界 🌟 Test wrapping");
244
- view.setWrapMode(.word);
245
- view.setWrapWidth(15);
246
- view.updateVirtualLines();
247
-
248
- var opt_buffer = try OptimizedBuffer.init(
249
- std.testing.allocator,
250
- 15,
251
- 10,
252
- .{ .pool = pool, .width_method = .unicode },
253
- );
254
- defer opt_buffer.deinit();
255
-
256
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
257
- try opt_buffer.drawTextBuffer(view, 0, 0);
258
-
259
- const virtual_lines = view.getVirtualLines();
260
- try std.testing.expect(virtual_lines.len > 0);
261
- }
262
-
263
- test "drawTextBuffer - wrapping preserves wide characters" {
264
- const pool = gp.initGlobalPool(std.testing.allocator);
265
- defer gp.deinitGlobalPool();
266
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
267
- defer link.deinitGlobalLinkPool();
268
-
269
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
270
- defer tb.deinit();
271
-
272
- var view = try TextBufferView.init(std.testing.allocator, tb);
273
- defer view.deinit();
274
-
275
- try tb.setText("測試測試測試測試測試");
276
- view.setWrapMode(.char);
277
- view.setWrapWidth(10);
278
- view.updateVirtualLines();
279
-
280
- var opt_buffer = try OptimizedBuffer.init(
281
- std.testing.allocator,
282
- 10,
283
- 10,
284
- .{ .pool = pool, .width_method = .unicode },
285
- );
286
- defer opt_buffer.deinit();
287
-
288
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
289
- try opt_buffer.drawTextBuffer(view, 0, 0);
290
-
291
- const virtual_lines = view.getVirtualLines();
292
- try std.testing.expect(virtual_lines.len > 1);
293
- }
294
-
295
- test "drawTextBuffer - word wrap does not split multi-byte UTF-8 characters" {
296
- const pool = gp.initGlobalPool(std.testing.allocator);
297
- defer gp.deinitGlobalPool();
298
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
299
- defer link.deinitGlobalLinkPool();
300
-
301
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
302
- defer tb.deinit();
303
-
304
- var view = try TextBufferView.init(std.testing.allocator, tb);
305
- defer view.deinit();
306
-
307
- try tb.setText("🌟 Unicode test: こんにちは世界 Hello World 你好世界");
308
- view.setWrapMode(.word);
309
- view.setWrapWidth(35);
310
- view.updateVirtualLines();
311
-
312
- const vlines = view.getVirtualLines();
313
-
314
- for (vlines) |vline| {
315
- var line_buffer: [200]u8 = undefined;
316
- const line_start_offset = vline.col_offset;
317
- const line_end_offset = line_start_offset + vline.width_cols;
318
- const extracted = tb.getTextRange(line_start_offset, line_end_offset, &line_buffer);
319
-
320
- const is_valid_utf8 = std.unicode.utf8ValidateSlice(line_buffer[0..extracted]);
321
- try std.testing.expect(is_valid_utf8);
322
- }
323
-
324
- try std.testing.expect(vlines.len == 2);
325
-
326
- var full_buffer: [200]u8 = undefined;
327
- const line0_len = tb.getTextRange(vlines[0].col_offset, vlines[0].col_offset + vlines[0].width_cols, &full_buffer);
328
- const line0_text = full_buffer[0..line0_len];
329
-
330
- const line1_len = tb.getTextRange(vlines[1].col_offset, vlines[1].col_offset + vlines[1].width_cols, &full_buffer);
331
- const line1_text = full_buffer[0..line1_len];
332
-
333
- const line0_ends_with_kai = std.mem.endsWith(u8, line0_text, "界");
334
- const line1_starts_with_kai = std.mem.startsWith(u8, line1_text, "界");
335
-
336
- try std.testing.expect(!(line0_ends_with_kai and line1_starts_with_kai));
337
- }
338
-
339
- test "drawTextBuffer - wrapped text with offset position" {
340
- const pool = gp.initGlobalPool(std.testing.allocator);
341
- defer gp.deinitGlobalPool();
342
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
343
- defer link.deinitGlobalLinkPool();
344
-
345
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
346
- defer tb.deinit();
347
-
348
- var view = try TextBufferView.init(std.testing.allocator, tb);
349
- defer view.deinit();
350
-
351
- try tb.setText("Short line that wraps nicely");
352
- view.setWrapMode(.word);
353
- view.setWrapWidth(10);
354
- view.updateVirtualLines();
355
-
356
- var opt_buffer = try OptimizedBuffer.init(
357
- std.testing.allocator,
358
- 20,
359
- 20,
360
- .{ .pool = pool, .width_method = .unicode },
361
- );
362
- defer opt_buffer.deinit();
363
-
364
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
365
- try opt_buffer.drawTextBuffer(view, 5, 5);
366
-
367
- const cell = opt_buffer.get(5, 5);
368
- try std.testing.expect(cell != null);
369
- try std.testing.expect(cell.?.char != 32);
370
- }
371
-
372
- test "drawTextBuffer - clipping with scrolled view" {
373
- const pool = gp.initGlobalPool(std.testing.allocator);
374
- defer gp.deinitGlobalPool();
375
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
376
- defer link.deinitGlobalLinkPool();
377
-
378
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
379
- defer tb.deinit();
380
-
381
- var view = try TextBufferView.init(std.testing.allocator, tb);
382
- defer view.deinit();
383
-
384
- try tb.setText("Line 1\nLine 2\nLine 3\nLine 4");
385
-
386
- var opt_buffer = try OptimizedBuffer.init(
387
- std.testing.allocator,
388
- 20,
389
- 5,
390
- .{ .pool = pool, .width_method = .unicode },
391
- );
392
- defer opt_buffer.deinit();
393
-
394
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
395
- try opt_buffer.drawTextBuffer(view, 0, 0);
396
-
397
- const virtual_lines = view.getVirtualLines();
398
- try std.testing.expect(virtual_lines.len >= 4);
399
- }
400
-
401
- test "drawTextBuffer - wrapping with very narrow width" {
402
- const pool = gp.initGlobalPool(std.testing.allocator);
403
- defer gp.deinitGlobalPool();
404
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
405
- defer link.deinitGlobalLinkPool();
406
-
407
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
408
- defer tb.deinit();
409
-
410
- var view = try TextBufferView.init(std.testing.allocator, tb);
411
- defer view.deinit();
412
-
413
- try tb.setText("Hello");
414
- view.setWrapMode(.char);
415
- view.setWrapWidth(3);
416
- view.updateVirtualLines();
417
-
418
- var opt_buffer = try OptimizedBuffer.init(
419
- std.testing.allocator,
420
- 3,
421
- 10,
422
- .{ .pool = pool, .width_method = .unicode },
423
- );
424
- defer opt_buffer.deinit();
425
-
426
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
427
- try opt_buffer.drawTextBuffer(view, 0, 0);
428
-
429
- const virtual_lines = view.getVirtualLines();
430
- try std.testing.expect(virtual_lines.len == 2);
431
- }
432
-
433
- test "drawTextBuffer - word wrap doesn't break mid-word" {
434
- const pool = gp.initGlobalPool(std.testing.allocator);
435
- defer gp.deinitGlobalPool();
436
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
437
- defer link.deinitGlobalLinkPool();
438
-
439
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
440
- defer tb.deinit();
441
-
442
- var view = try TextBufferView.init(std.testing.allocator, tb);
443
- defer view.deinit();
444
-
445
- try tb.setText("Hello World");
446
- view.setWrapMode(.word);
447
- view.setWrapWidth(8);
448
- view.updateVirtualLines();
449
-
450
- var opt_buffer = try OptimizedBuffer.init(
451
- std.testing.allocator,
452
- 8,
453
- 5,
454
- .{ .pool = pool, .width_method = .unicode },
455
- );
456
- defer opt_buffer.deinit();
457
-
458
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
459
- try opt_buffer.drawTextBuffer(view, 0, 0);
460
-
461
- const virtual_lines = view.getVirtualLines();
462
- try std.testing.expect(virtual_lines.len == 2);
463
- }
464
-
465
- test "drawTextBuffer - empty lines render correctly" {
466
- const pool = gp.initGlobalPool(std.testing.allocator);
467
- defer gp.deinitGlobalPool();
468
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
469
- defer link.deinitGlobalLinkPool();
470
-
471
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
472
- defer tb.deinit();
473
-
474
- var view = try TextBufferView.init(std.testing.allocator, tb);
475
- defer view.deinit();
476
-
477
- try tb.setText("Line 1\n\nLine 3");
478
-
479
- var opt_buffer = try OptimizedBuffer.init(
480
- std.testing.allocator,
481
- 20,
482
- 10,
483
- .{ .pool = pool, .width_method = .unicode },
484
- );
485
- defer opt_buffer.deinit();
486
-
487
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
488
- try opt_buffer.drawTextBuffer(view, 0, 0);
489
-
490
- const virtual_lines = view.getVirtualLines();
491
- try std.testing.expect(virtual_lines.len == 3);
492
- }
493
-
494
- test "drawTextBuffer - wrapping with tabs" {
495
- const pool = gp.initGlobalPool(std.testing.allocator);
496
- defer gp.deinitGlobalPool();
497
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
498
- defer link.deinitGlobalLinkPool();
499
-
500
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
501
- defer tb.deinit();
502
-
503
- var view = try TextBufferView.init(std.testing.allocator, tb);
504
- defer view.deinit();
505
-
506
- try tb.setText("Hello\tWorld\tTest");
507
- view.setWrapMode(.word);
508
- view.setWrapWidth(15);
509
- view.updateVirtualLines();
510
-
511
- var opt_buffer = try OptimizedBuffer.init(
512
- std.testing.allocator,
513
- 15,
514
- 10,
515
- .{ .pool = pool, .width_method = .unicode },
516
- );
517
- defer opt_buffer.deinit();
518
-
519
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
520
- try opt_buffer.drawTextBuffer(view, 0, 0);
521
- }
522
-
523
- test "drawTextBuffer - very long unwrapped line clipping" {
524
- const pool = gp.initGlobalPool(std.testing.allocator);
525
- defer gp.deinitGlobalPool();
526
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
527
- defer link.deinitGlobalLinkPool();
528
-
529
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
530
- defer tb.deinit();
531
-
532
- var view = try TextBufferView.init(std.testing.allocator, tb);
533
- defer view.deinit();
534
-
535
- var long_text: std.ArrayListUnmanaged(u8) = .{};
536
- defer long_text.deinit(std.testing.allocator);
537
- try long_text.appendNTimes(std.testing.allocator, 'A', 200);
538
-
539
- try tb.setText(long_text.items);
540
- view.setWrapMode(.word);
541
- view.setWrapWidth(null);
542
-
543
- var opt_buffer = try OptimizedBuffer.init(
544
- std.testing.allocator,
545
- 20,
546
- 5,
547
- .{ .pool = pool, .width_method = .unicode },
548
- );
549
- defer opt_buffer.deinit();
550
-
551
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
552
- try opt_buffer.drawTextBuffer(view, 0, 0);
553
-
554
- const virtual_lines = view.getVirtualLines();
555
- try std.testing.expect(virtual_lines.len == 1);
556
- }
557
-
558
- test "drawTextBuffer - wrap mode transitions" {
559
- const pool = gp.initGlobalPool(std.testing.allocator);
560
- defer gp.deinitGlobalPool();
561
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
562
- defer link.deinitGlobalLinkPool();
563
-
564
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
565
- defer tb.deinit();
566
-
567
- var view = try TextBufferView.init(std.testing.allocator, tb);
568
- defer view.deinit();
569
-
570
- try tb.setText("This is a test line for wrapping");
571
-
572
- view.setWrapMode(.word);
573
- view.setWrapWidth(null);
574
- view.updateVirtualLines();
575
- const no_wrap_lines = view.getVirtualLines().len;
576
-
577
- view.setWrapMode(.char);
578
- view.setWrapWidth(10);
579
- view.updateVirtualLines();
580
- const char_lines = view.getVirtualLines().len;
581
-
582
- view.setWrapMode(.word);
583
- view.setWrapWidth(10);
584
- view.updateVirtualLines();
585
- const word_lines = view.getVirtualLines().len;
586
-
587
- try std.testing.expect(no_wrap_lines == 1);
588
- try std.testing.expect(char_lines > 1);
589
- try std.testing.expect(word_lines > 1);
590
- }
591
-
592
- test "drawTextBuffer - changing wrap width updates virtual lines" {
593
- const pool = gp.initGlobalPool(std.testing.allocator);
594
- defer gp.deinitGlobalPool();
595
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
596
- defer link.deinitGlobalLinkPool();
597
-
598
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
599
- defer tb.deinit();
600
-
601
- var view = try TextBufferView.init(std.testing.allocator, tb);
602
- defer view.deinit();
603
-
604
- try tb.setText("AAAAAAAAAAAAAAAAAAAAAAAAAAAA");
605
-
606
- view.setWrapMode(.char);
607
- view.setWrapWidth(10);
608
- view.updateVirtualLines();
609
- const lines_10 = view.getVirtualLines().len;
610
-
611
- view.setWrapWidth(20);
612
- view.updateVirtualLines();
613
- const lines_20 = view.getVirtualLines().len;
614
-
615
- view.setWrapWidth(5);
616
- view.updateVirtualLines();
617
- const lines_5 = view.getVirtualLines().len;
618
-
619
- try std.testing.expect(lines_10 > lines_20);
620
- try std.testing.expect(lines_5 > lines_10);
621
- }
622
-
623
- test "drawTextBuffer - wrapping with mixed ASCII and Unicode" {
624
- const pool = gp.initGlobalPool(std.testing.allocator);
625
- defer gp.deinitGlobalPool();
626
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
627
- defer link.deinitGlobalLinkPool();
628
-
629
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
630
- defer tb.deinit();
631
-
632
- var view = try TextBufferView.init(std.testing.allocator, tb);
633
- defer view.deinit();
634
-
635
- try tb.setText("ABC測試DEF試験GHI");
636
- view.setWrapMode(.char);
637
- view.setWrapWidth(10);
638
- view.updateVirtualLines();
639
-
640
- var opt_buffer = try OptimizedBuffer.init(
641
- std.testing.allocator,
642
- 10,
643
- 10,
644
- .{ .pool = pool, .width_method = .unicode },
645
- );
646
- defer opt_buffer.deinit();
647
-
648
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
649
- try opt_buffer.drawTextBuffer(view, 0, 0);
650
-
651
- const virtual_lines = view.getVirtualLines();
652
- try std.testing.expect(virtual_lines.len > 1);
653
- }
654
-
655
- test "setStyledText - basic rendering with single chunk" {
656
- const pool = gp.initGlobalPool(std.testing.allocator);
657
- defer gp.deinitGlobalPool();
658
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
659
- defer link.deinitGlobalLinkPool();
660
-
661
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
662
- defer tb.deinit();
663
-
664
- const style = try ss.SyntaxStyle.init(std.testing.allocator);
665
- defer style.deinit();
666
- tb.setSyntaxStyle(style);
667
-
668
- const text = "Hello World";
669
- const fg_color = [4]f32{ 1.0, 1.0, 1.0, 1.0 };
670
-
671
- const chunks = [_]StyledChunk{.{
672
- .text_ptr = text.ptr,
673
- .text_len = text.len,
674
- .fg_ptr = @ptrCast(&fg_color),
675
- .bg_ptr = null,
676
- .attributes = 0,
677
- }};
678
-
679
- try tb.setStyledText(&chunks);
680
-
681
- var out_buffer: [100]u8 = undefined;
682
- const written = tb.getPlainTextIntoBuffer(&out_buffer);
683
- const result = out_buffer[0..written];
684
-
685
- try std.testing.expectEqualStrings("Hello World", result);
686
- }
687
-
688
- test "setStyledText - multiple chunks render correctly" {
689
- const pool = gp.initGlobalPool(std.testing.allocator);
690
- defer gp.deinitGlobalPool();
691
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
692
- defer link.deinitGlobalLinkPool();
693
-
694
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
695
- defer tb.deinit();
696
-
697
- const style = try ss.SyntaxStyle.init(std.testing.allocator);
698
- defer style.deinit();
699
- tb.setSyntaxStyle(style);
700
-
701
- const text0 = "Hello ";
702
- const text1 = "World";
703
- const fg_color = [4]f32{ 1.0, 1.0, 1.0, 1.0 };
704
-
705
- const chunks = [_]StyledChunk{
706
- .{ .text_ptr = text0.ptr, .text_len = text0.len, .fg_ptr = @ptrCast(&fg_color), .bg_ptr = null, .attributes = 0 },
707
- .{ .text_ptr = text1.ptr, .text_len = text1.len, .fg_ptr = @ptrCast(&fg_color), .bg_ptr = null, .attributes = 0 },
708
- };
709
-
710
- try tb.setStyledText(&chunks);
711
-
712
- var out_buffer: [100]u8 = undefined;
713
- const written = tb.getPlainTextIntoBuffer(&out_buffer);
714
- const result = out_buffer[0..written];
715
-
716
- try std.testing.expectEqualStrings("Hello World", result);
717
- }
718
-
719
- // Viewport Tests
720
-
721
- test "viewport - basic vertical scrolling limits returned lines" {
722
- const pool = gp.initGlobalPool(std.testing.allocator);
723
- defer gp.deinitGlobalPool();
724
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
725
- defer link.deinitGlobalLinkPool();
726
-
727
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
728
- defer tb.deinit();
729
-
730
- var view = try TextBufferView.init(std.testing.allocator, tb);
731
- defer view.deinit();
732
-
733
- try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8\nLine 9");
734
-
735
- view.setViewport(.{ .x = 0, .y = 2, .width = 20, .height = 5 });
736
-
737
- const visible_lines = view.getVirtualLines();
738
-
739
- try std.testing.expectEqual(@as(usize, 5), visible_lines.len);
740
- try std.testing.expectEqual(@as(usize, 2), visible_lines[0].source_line);
741
- try std.testing.expectEqual(@as(usize, 6), visible_lines[4].source_line);
742
- }
743
-
744
- test "viewport - vertical scrolling at start boundary" {
745
- const pool = gp.initGlobalPool(std.testing.allocator);
746
- defer gp.deinitGlobalPool();
747
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
748
- defer link.deinitGlobalLinkPool();
749
-
750
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
751
- defer tb.deinit();
752
-
753
- var view = try TextBufferView.init(std.testing.allocator, tb);
754
- defer view.deinit();
755
-
756
- try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4");
757
-
758
- view.setViewport(.{ .x = 0, .y = 0, .width = 20, .height = 3 });
759
-
760
- const visible_lines = view.getVirtualLines();
761
-
762
- try std.testing.expectEqual(@as(usize, 3), visible_lines.len);
763
- try std.testing.expectEqual(@as(usize, 0), visible_lines[0].source_line);
764
- try std.testing.expectEqual(@as(usize, 2), visible_lines[2].source_line);
765
- }
766
-
767
- test "viewport - vertical scrolling at end boundary" {
768
- const pool = gp.initGlobalPool(std.testing.allocator);
769
- defer gp.deinitGlobalPool();
770
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
771
- defer link.deinitGlobalLinkPool();
772
-
773
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
774
- defer tb.deinit();
775
-
776
- var view = try TextBufferView.init(std.testing.allocator, tb);
777
- defer view.deinit();
778
-
779
- try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4");
780
-
781
- view.setViewport(.{ .x = 0, .y = 3, .width = 20, .height = 3 });
782
-
783
- const visible_lines = view.getVirtualLines();
784
-
785
- try std.testing.expectEqual(@as(usize, 2), visible_lines.len);
786
- try std.testing.expectEqual(@as(usize, 3), visible_lines[0].source_line);
787
- try std.testing.expectEqual(@as(usize, 4), visible_lines[1].source_line);
788
- }
789
-
790
- test "viewport - vertical scrolling beyond content" {
791
- const pool = gp.initGlobalPool(std.testing.allocator);
792
- defer gp.deinitGlobalPool();
793
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
794
- defer link.deinitGlobalLinkPool();
795
-
796
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
797
- defer tb.deinit();
798
-
799
- var view = try TextBufferView.init(std.testing.allocator, tb);
800
- defer view.deinit();
801
-
802
- try tb.setText("Line 0\nLine 1\nLine 2");
803
-
804
- view.setViewport(.{ .x = 0, .y = 10, .width = 20, .height = 5 });
805
-
806
- const visible_lines = view.getVirtualLines();
807
-
808
- try std.testing.expectEqual(@as(usize, 0), visible_lines.len);
809
- }
810
-
811
- test "viewport - with wrapping vertical scrolling" {
812
- const pool = gp.initGlobalPool(std.testing.allocator);
813
- defer gp.deinitGlobalPool();
814
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
815
- defer link.deinitGlobalLinkPool();
816
-
817
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
818
- defer tb.deinit();
819
-
820
- var view = try TextBufferView.init(std.testing.allocator, tb);
821
- defer view.deinit();
822
-
823
- try tb.setText("This is a long line that will wrap\nShort\nAnother long line that wraps");
824
-
825
- view.setWrapMode(.word);
826
- view.setWrapWidth(15);
827
- view.updateVirtualLines();
828
-
829
- const total_vlines = view.getVirtualLineCount();
830
- try std.testing.expect(total_vlines > 3);
831
-
832
- view.setViewport(.{ .x = 0, .y = 2, .width = 15, .height = 3 });
833
-
834
- const visible_lines = view.getVirtualLines();
835
-
836
- try std.testing.expectEqual(@as(usize, 3), visible_lines.len);
837
- }
838
-
839
- test "viewport - getCachedLineInfo returns only viewport lines" {
840
- const pool = gp.initGlobalPool(std.testing.allocator);
841
- defer gp.deinitGlobalPool();
842
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
843
- defer link.deinitGlobalLinkPool();
844
-
845
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
846
- defer tb.deinit();
847
-
848
- var view = try TextBufferView.init(std.testing.allocator, tb);
849
- defer view.deinit();
850
-
851
- try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5");
852
-
853
- view.setViewport(.{ .x = 0, .y = 1, .width = 20, .height = 3 });
854
-
855
- const line_info = view.getCachedLineInfo();
856
-
857
- try std.testing.expectEqual(@as(usize, 3), line_info.line_start_cols.len);
858
- try std.testing.expectEqual(@as(usize, 3), line_info.line_width_cols.len);
859
- }
860
-
861
- test "viewport - changing viewport updates returned lines" {
862
- const pool = gp.initGlobalPool(std.testing.allocator);
863
- defer gp.deinitGlobalPool();
864
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
865
- defer link.deinitGlobalLinkPool();
866
-
867
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
868
- defer tb.deinit();
869
-
870
- var view = try TextBufferView.init(std.testing.allocator, tb);
871
- defer view.deinit();
872
-
873
- try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5");
874
-
875
- view.setViewport(.{ .x = 0, .y = 0, .width = 20, .height = 2 });
876
- const lines1 = view.getVirtualLines();
877
- try std.testing.expectEqual(@as(usize, 2), lines1.len);
878
- try std.testing.expectEqual(@as(usize, 0), lines1[0].source_line);
879
-
880
- view.setViewport(.{ .x = 0, .y = 3, .width = 20, .height = 2 });
881
- const lines2 = view.getVirtualLines();
882
- try std.testing.expectEqual(@as(usize, 2), lines2.len);
883
- try std.testing.expectEqual(@as(usize, 3), lines2[0].source_line);
884
-
885
- view.setViewport(.{ .x = 0, .y = 1, .width = 20, .height = 4 });
886
- const lines3 = view.getVirtualLines();
887
- try std.testing.expectEqual(@as(usize, 4), lines3.len);
888
- try std.testing.expectEqual(@as(usize, 1), lines3[0].source_line);
889
- }
890
-
891
- test "viewport - null viewport returns all lines" {
892
- const pool = gp.initGlobalPool(std.testing.allocator);
893
- defer gp.deinitGlobalPool();
894
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
895
- defer link.deinitGlobalLinkPool();
896
-
897
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
898
- defer tb.deinit();
899
-
900
- var view = try TextBufferView.init(std.testing.allocator, tb);
901
- defer view.deinit();
902
-
903
- try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4");
904
-
905
- const all_lines = view.getVirtualLines();
906
- try std.testing.expectEqual(@as(usize, 5), all_lines.len);
907
-
908
- view.setViewport(.{ .x = 0, .y = 1, .width = 20, .height = 2 });
909
- const viewport_lines = view.getVirtualLines();
910
- try std.testing.expectEqual(@as(usize, 2), viewport_lines.len);
911
-
912
- view.setViewport(null);
913
- const all_lines_again = view.getVirtualLines();
914
- try std.testing.expectEqual(@as(usize, 5), all_lines_again.len);
915
- }
916
-
917
- test "viewport - setViewportSize convenience method" {
918
- const pool = gp.initGlobalPool(std.testing.allocator);
919
- defer gp.deinitGlobalPool();
920
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
921
- defer link.deinitGlobalLinkPool();
922
-
923
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
924
- defer tb.deinit();
925
-
926
- var view = try TextBufferView.init(std.testing.allocator, tb);
927
- defer view.deinit();
928
-
929
- try tb.setText("Line 0\nLine 1\nLine 2\nLine 3");
930
-
931
- view.setViewportSize(20, 2);
932
- const vp1 = view.getViewport().?;
933
- try std.testing.expectEqual(@as(u32, 0), vp1.x);
934
- try std.testing.expectEqual(@as(u32, 0), vp1.y);
935
- try std.testing.expectEqual(@as(u32, 20), vp1.width);
936
- try std.testing.expectEqual(@as(u32, 2), vp1.height);
937
-
938
- view.setViewport(.{ .x = 5, .y = 1, .width = 20, .height = 2 });
939
-
940
- view.setViewportSize(30, 3);
941
- const vp2 = view.getViewport().?;
942
- try std.testing.expectEqual(@as(u32, 5), vp2.x);
943
- try std.testing.expectEqual(@as(u32, 1), vp2.y);
944
- try std.testing.expectEqual(@as(u32, 30), vp2.width);
945
- try std.testing.expectEqual(@as(u32, 3), vp2.height);
946
- }
947
-
948
- test "viewport - stores horizontal offset value with no wrapping" {
949
- const pool = gp.initGlobalPool(std.testing.allocator);
950
- defer gp.deinitGlobalPool();
951
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
952
- defer link.deinitGlobalLinkPool();
953
-
954
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
955
- defer tb.deinit();
956
-
957
- var view = try TextBufferView.init(std.testing.allocator, tb);
958
- defer view.deinit();
959
-
960
- try tb.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
961
-
962
- view.setWrapMode(.none);
963
- view.setWrapWidth(null);
964
-
965
- view.setViewport(.{ .x = 5, .y = 0, .width = 10, .height = 1 });
966
-
967
- const vp = view.getViewport().?;
968
- try std.testing.expectEqual(@as(u32, 5), vp.x);
969
- try std.testing.expectEqual(@as(u32, 0), vp.y);
970
- try std.testing.expectEqual(@as(u32, 10), vp.width);
971
- try std.testing.expectEqual(@as(u32, 1), vp.height);
972
-
973
- const lines = view.getVirtualLines();
974
- try std.testing.expectEqual(@as(usize, 1), lines.len);
975
- }
976
-
977
- test "viewport - preserves horizontal offset when changing vertical (no wrap)" {
978
- const pool = gp.initGlobalPool(std.testing.allocator);
979
- defer gp.deinitGlobalPool();
980
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
981
- defer link.deinitGlobalLinkPool();
982
-
983
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
984
- defer tb.deinit();
985
-
986
- var view = try TextBufferView.init(std.testing.allocator, tb);
987
- defer view.deinit();
988
-
989
- try tb.setText("ABCDEFGHIJ\nKLMNOPQRST\nUVWXYZ1234");
990
-
991
- view.setWrapMode(.none);
992
- view.setWrapWidth(null);
993
-
994
- view.setViewport(.{ .x = 3, .y = 0, .width = 8, .height = 2 });
995
-
996
- var vp = view.getViewport().?;
997
- try std.testing.expectEqual(@as(u32, 3), vp.x);
998
- try std.testing.expectEqual(@as(u32, 0), vp.y);
999
-
1000
- view.setViewport(.{ .x = 3, .y = 1, .width = 8, .height = 2 });
1001
-
1002
- vp = view.getViewport().?;
1003
- try std.testing.expectEqual(@as(u32, 3), vp.x);
1004
- try std.testing.expectEqual(@as(u32, 1), vp.y);
1005
-
1006
- const visible_lines = view.getVirtualLines();
1007
- try std.testing.expectEqual(@as(usize, 2), visible_lines.len);
1008
- try std.testing.expectEqual(@as(usize, 1), visible_lines[0].source_line);
1009
- }
1010
-
1011
- test "viewport - can set large horizontal offset (no wrap)" {
1012
- const pool = gp.initGlobalPool(std.testing.allocator);
1013
- defer gp.deinitGlobalPool();
1014
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1015
- defer link.deinitGlobalLinkPool();
1016
-
1017
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1018
- defer tb.deinit();
1019
-
1020
- var view = try TextBufferView.init(std.testing.allocator, tb);
1021
- defer view.deinit();
1022
-
1023
- try tb.setText("Short\nLonger line here\nTiny");
1024
-
1025
- view.setWrapMode(.none);
1026
- view.setWrapWidth(null);
1027
-
1028
- view.setViewport(.{ .x = 10, .y = 0, .width = 10, .height = 3 });
1029
-
1030
- const vp = view.getViewport().?;
1031
- try std.testing.expectEqual(@as(u32, 10), vp.x);
1032
-
1033
- const visible_lines = view.getVirtualLines();
1034
- try std.testing.expectEqual(@as(usize, 3), visible_lines.len);
1035
- }
1036
-
1037
- test "viewport - horizontal and vertical offset combined (no wrap)" {
1038
- const pool = gp.initGlobalPool(std.testing.allocator);
1039
- defer gp.deinitGlobalPool();
1040
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1041
- defer link.deinitGlobalLinkPool();
1042
-
1043
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1044
- defer tb.deinit();
1045
-
1046
- var view = try TextBufferView.init(std.testing.allocator, tb);
1047
- defer view.deinit();
1048
-
1049
- try tb.setText("Line 0: ABCDEFGHIJ\nLine 1: KLMNOPQRST\nLine 2: UVWXYZ1234\nLine 3: 567890ABCD");
1050
-
1051
- view.setWrapMode(.none);
1052
- view.setWrapWidth(null);
1053
-
1054
- view.setViewport(.{ .x = 8, .y = 1, .width = 15, .height = 2 });
1055
-
1056
- const vp = view.getViewport().?;
1057
- try std.testing.expectEqual(@as(u32, 8), vp.x);
1058
- try std.testing.expectEqual(@as(u32, 1), vp.y);
1059
-
1060
- const visible_lines = view.getVirtualLines();
1061
- try std.testing.expectEqual(@as(usize, 2), visible_lines.len);
1062
- try std.testing.expectEqual(@as(usize, 1), visible_lines[0].source_line);
1063
- try std.testing.expectEqual(@as(usize, 2), visible_lines[1].source_line);
1064
- }
1065
-
1066
- test "viewport - horizontal scrolling only for no-wrap mode" {
1067
- const pool = gp.initGlobalPool(std.testing.allocator);
1068
- defer gp.deinitGlobalPool();
1069
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1070
- defer link.deinitGlobalLinkPool();
1071
-
1072
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1073
- defer tb.deinit();
1074
-
1075
- var view = try TextBufferView.init(std.testing.allocator, tb);
1076
- defer view.deinit();
1077
-
1078
- const long_text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1079
- try tb.setText(long_text);
1080
-
1081
- view.setWrapMode(.none);
1082
- view.setWrapWidth(null);
1083
- view.setViewport(.{ .x = 10, .y = 0, .width = 15, .height = 1 });
1084
- view.updateVirtualLines();
1085
-
1086
- var vp = view.getViewport().?;
1087
- try std.testing.expectEqual(@as(u32, 10), vp.x);
1088
-
1089
- var lines = view.getVirtualLines();
1090
- try std.testing.expectEqual(@as(usize, 1), lines.len);
1091
-
1092
- view.setWrapMode(.char);
1093
- view.setViewport(.{ .x = 10, .y = 0, .width = 15, .height = 5 });
1094
- view.updateVirtualLines();
1095
-
1096
- vp = view.getViewport().?;
1097
- try std.testing.expectEqual(@as(u32, 10), vp.x);
1098
-
1099
- lines = view.getVirtualLines();
1100
- try std.testing.expect(lines.len > 1);
1101
- }
1102
-
1103
- test "viewport - horizontal offset irrelevant with wrapping enabled" {
1104
- const pool = gp.initGlobalPool(std.testing.allocator);
1105
- defer gp.deinitGlobalPool();
1106
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1107
- defer link.deinitGlobalLinkPool();
1108
-
1109
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1110
- defer tb.deinit();
1111
-
1112
- var view = try TextBufferView.init(std.testing.allocator, tb);
1113
- defer view.deinit();
1114
-
1115
- try tb.setText("This is a very long line that will wrap into multiple virtual lines");
1116
-
1117
- view.setWrapMode(.word);
1118
- view.setWrapWidth(20);
1119
- view.updateVirtualLines();
1120
-
1121
- const total_vlines = view.getVirtualLineCount();
1122
- try std.testing.expect(total_vlines > 1);
1123
-
1124
- view.setViewport(.{ .x = 5, .y = 1, .width = 15, .height = 2 });
1125
-
1126
- const vp = view.getViewport().?;
1127
- try std.testing.expectEqual(@as(u32, 5), vp.x);
1128
- try std.testing.expectEqual(@as(u32, 1), vp.y);
1129
- try std.testing.expectEqual(@as(u32, 15), vp.width);
1130
-
1131
- const visible_lines = view.getVirtualLines();
1132
- try std.testing.expectEqual(@as(usize, 2), visible_lines.len);
1133
- }
1134
-
1135
- test "viewport - zero width or height" {
1136
- const pool = gp.initGlobalPool(std.testing.allocator);
1137
- defer gp.deinitGlobalPool();
1138
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1139
- defer link.deinitGlobalLinkPool();
1140
-
1141
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1142
- defer tb.deinit();
1143
-
1144
- var view = try TextBufferView.init(std.testing.allocator, tb);
1145
- defer view.deinit();
1146
-
1147
- try tb.setText("Line 0\nLine 1\nLine 2");
1148
-
1149
- view.setViewport(.{ .x = 0, .y = 0, .width = 20, .height = 0 });
1150
- const lines1 = view.getVirtualLines();
1151
- try std.testing.expectEqual(@as(usize, 0), lines1.len);
1152
-
1153
- view.setViewport(.{ .x = 0, .y = 0, .width = 0, .height = 2 });
1154
- const lines2 = view.getVirtualLines();
1155
- try std.testing.expectEqual(@as(usize, 2), lines2.len);
1156
- }
1157
-
1158
- test "viewport - viewport sets wrap width automatically" {
1159
- const pool = gp.initGlobalPool(std.testing.allocator);
1160
- defer gp.deinitGlobalPool();
1161
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1162
- defer link.deinitGlobalLinkPool();
1163
-
1164
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1165
- defer tb.deinit();
1166
-
1167
- var view = try TextBufferView.init(std.testing.allocator, tb);
1168
- defer view.deinit();
1169
-
1170
- try tb.setText("AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD");
1171
-
1172
- view.setWrapMode(.char);
1173
-
1174
- view.setViewport(.{ .x = 0, .y = 0, .width = 10, .height = 5 });
1175
- view.updateVirtualLines();
1176
-
1177
- const vline_count_10 = view.getVirtualLineCount();
1178
-
1179
- view.setViewport(.{ .x = 0, .y = 0, .width = 20, .height = 5 });
1180
- view.updateVirtualLines();
1181
-
1182
- const vline_count_20 = view.getVirtualLineCount();
1183
-
1184
- try std.testing.expect(vline_count_10 > vline_count_20);
1185
- }
1186
-
1187
- test "viewport - moving viewport dynamically (no wrap)" {
1188
- const pool = gp.initGlobalPool(std.testing.allocator);
1189
- defer gp.deinitGlobalPool();
1190
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1191
- defer link.deinitGlobalLinkPool();
1192
-
1193
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1194
- defer tb.deinit();
1195
-
1196
- var view = try TextBufferView.init(std.testing.allocator, tb);
1197
- defer view.deinit();
1198
-
1199
- try tb.setText("0123456789\nABCDEFGHIJ\nKLMNOPQRST\nUVWXYZ!@#$");
1200
-
1201
- view.setWrapMode(.none);
1202
- view.setWrapWidth(null);
1203
-
1204
- view.setViewport(.{ .x = 0, .y = 0, .width = 5, .height = 2 });
1205
- var vp = view.getViewport().?;
1206
- try std.testing.expectEqual(@as(u32, 0), vp.x);
1207
- try std.testing.expectEqual(@as(u32, 0), vp.y);
1208
- const lines1 = view.getVirtualLines();
1209
- try std.testing.expectEqual(@as(usize, 2), lines1.len);
1210
- try std.testing.expectEqual(@as(usize, 0), lines1[0].source_line);
1211
-
1212
- view.setViewport(.{ .x = 0, .y = 1, .width = 5, .height = 2 });
1213
- vp = view.getViewport().?;
1214
- try std.testing.expectEqual(@as(u32, 0), vp.x);
1215
- try std.testing.expectEqual(@as(u32, 1), vp.y);
1216
- const lines2 = view.getVirtualLines();
1217
- try std.testing.expectEqual(@as(usize, 2), lines2.len);
1218
- try std.testing.expectEqual(@as(usize, 1), lines2[0].source_line);
1219
-
1220
- view.setViewport(.{ .x = 3, .y = 1, .width = 5, .height = 2 });
1221
- vp = view.getViewport().?;
1222
- try std.testing.expectEqual(@as(u32, 3), vp.x);
1223
- try std.testing.expectEqual(@as(u32, 1), vp.y);
1224
- const lines3 = view.getVirtualLines();
1225
- try std.testing.expectEqual(@as(usize, 2), lines3.len);
1226
-
1227
- view.setViewport(.{ .x = 5, .y = 2, .width = 5, .height = 2 });
1228
- vp = view.getViewport().?;
1229
- try std.testing.expectEqual(@as(u32, 5), vp.x);
1230
- try std.testing.expectEqual(@as(u32, 2), vp.y);
1231
- const lines4 = view.getVirtualLines();
1232
- try std.testing.expectEqual(@as(usize, 2), lines4.len);
1233
- try std.testing.expectEqual(@as(usize, 2), lines4[0].source_line);
1234
- }
1235
-
1236
- test "loadFile - loads and renders file correctly" {
1237
- const pool = gp.initGlobalPool(std.testing.allocator);
1238
- defer gp.deinitGlobalPool();
1239
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1240
- defer link.deinitGlobalLinkPool();
1241
-
1242
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1243
- defer tb.deinit();
1244
-
1245
- var view = try TextBufferView.init(std.testing.allocator, tb);
1246
- defer view.deinit();
1247
-
1248
- const test_content = "ABC\nDEF";
1249
- const tmpdir = std.testing.tmpDir(.{});
1250
- var tmp = tmpdir;
1251
- defer tmp.cleanup();
1252
-
1253
- const file = try tmp.dir.createFile("test.txt", .{});
1254
- try file.writeAll(test_content);
1255
- file.close();
1256
-
1257
- const dir_path = try tmp.dir.realpathAlloc(std.testing.allocator, ".");
1258
- defer std.testing.allocator.free(dir_path);
1259
-
1260
- const file_path = try std.fs.path.join(std.testing.allocator, &[_][]const u8{ dir_path, "test.txt" });
1261
- defer std.testing.allocator.free(file_path);
1262
-
1263
- try tb.loadFile(file_path);
1264
-
1265
- const line_count = tb.getLineCount();
1266
- try std.testing.expectEqual(@as(u32, 2), line_count);
1267
-
1268
- const char_count = tb.getLength();
1269
- try std.testing.expectEqual(@as(u32, 6), char_count);
1270
-
1271
- var opt_buffer = try OptimizedBuffer.init(
1272
- std.testing.allocator,
1273
- 20,
1274
- 5,
1275
- .{ .pool = pool, .width_method = .unicode },
1276
- );
1277
- defer opt_buffer.deinit();
1278
-
1279
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1280
- try opt_buffer.drawTextBuffer(view, 0, 0);
1281
-
1282
- var render_buffer: [200]u8 = undefined;
1283
- const render_written = try opt_buffer.writeResolvedChars(&render_buffer, false);
1284
- const render_result = render_buffer[0..render_written];
1285
-
1286
- try std.testing.expect(std.mem.startsWith(u8, render_result, "ABC"));
1287
- }
1288
-
1289
- test "drawTextBuffer - horizontal viewport offset renders correctly without wrapping" {
1290
- const pool = gp.initGlobalPool(std.testing.allocator);
1291
- defer gp.deinitGlobalPool();
1292
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1293
- defer link.deinitGlobalLinkPool();
1294
-
1295
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1296
- defer tb.deinit();
1297
-
1298
- var view = try TextBufferView.init(std.testing.allocator, tb);
1299
- defer view.deinit();
1300
-
1301
- try tb.setText("0123456789ABCDEFGHIJ");
1302
-
1303
- view.setWrapMode(.none);
1304
- view.setWrapWidth(null);
1305
- view.setViewport(.{ .x = 5, .y = 0, .width = 10, .height = 1 });
1306
-
1307
- var opt_buffer = try OptimizedBuffer.init(
1308
- std.testing.allocator,
1309
- 10,
1310
- 1,
1311
- .{ .pool = pool, .width_method = .unicode },
1312
- );
1313
- defer opt_buffer.deinit();
1314
-
1315
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1316
- try opt_buffer.drawTextBuffer(view, 0, 0);
1317
-
1318
- var out_buffer: [100]u8 = undefined;
1319
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
1320
- const result = out_buffer[0..written];
1321
-
1322
- try std.testing.expect(std.mem.startsWith(u8, result, "56789ABCDE"));
1323
- }
1324
-
1325
- test "drawTextBuffer - horizontal viewport offset with multiple lines" {
1326
- const pool = gp.initGlobalPool(std.testing.allocator);
1327
- defer gp.deinitGlobalPool();
1328
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1329
- defer link.deinitGlobalLinkPool();
1330
-
1331
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1332
- defer tb.deinit();
1333
-
1334
- var view = try TextBufferView.init(std.testing.allocator, tb);
1335
- defer view.deinit();
1336
-
1337
- try tb.setText("ABCDEFGHIJKLMNO\n0123456789!@#$%\nXYZ[\\]^_`{|}~");
1338
-
1339
- view.setWrapMode(.none);
1340
- view.setWrapWidth(null);
1341
- view.setViewport(.{ .x = 3, .y = 0, .width = 8, .height = 3 });
1342
-
1343
- var opt_buffer = try OptimizedBuffer.init(
1344
- std.testing.allocator,
1345
- 8,
1346
- 3,
1347
- .{ .pool = pool, .width_method = .unicode },
1348
- );
1349
- defer opt_buffer.deinit();
1350
-
1351
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1352
- try opt_buffer.drawTextBuffer(view, 0, 0);
1353
-
1354
- var out_buffer: [100]u8 = undefined;
1355
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
1356
- const result = out_buffer[0..written];
1357
-
1358
- try std.testing.expect(std.mem.indexOf(u8, result, "DEFGHIJK") != null);
1359
- try std.testing.expect(std.mem.indexOf(u8, result, "3456789!") != null);
1360
- try std.testing.expect(std.mem.indexOf(u8, result, "[\\]^_`{|") != null);
1361
- }
1362
-
1363
- test "drawTextBuffer - combined horizontal and vertical viewport offsets" {
1364
- const pool = gp.initGlobalPool(std.testing.allocator);
1365
- defer gp.deinitGlobalPool();
1366
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1367
- defer link.deinitGlobalLinkPool();
1368
-
1369
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1370
- defer tb.deinit();
1371
-
1372
- var view = try TextBufferView.init(std.testing.allocator, tb);
1373
- defer view.deinit();
1374
-
1375
- try tb.setText("Line0ABCDEFGHIJ\nLine1KLMNOPQRST\nLine2UVWXYZ0123\nLine3456789!@#$");
1376
-
1377
- view.setWrapMode(.none);
1378
- view.setWrapWidth(null);
1379
- view.setViewport(.{ .x = 5, .y = 1, .width = 10, .height = 2 });
1380
-
1381
- var opt_buffer = try OptimizedBuffer.init(
1382
- std.testing.allocator,
1383
- 10,
1384
- 2,
1385
- .{ .pool = pool, .width_method = .unicode },
1386
- );
1387
- defer opt_buffer.deinit();
1388
-
1389
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1390
- try opt_buffer.drawTextBuffer(view, 0, 0);
1391
-
1392
- var out_buffer: [100]u8 = undefined;
1393
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
1394
- const result = out_buffer[0..written];
1395
-
1396
- try std.testing.expect(std.mem.indexOf(u8, result, "KLMNOPQRST") != null);
1397
- try std.testing.expect(std.mem.indexOf(u8, result, "UVWXYZ0123") != null);
1398
- }
1399
-
1400
- test "drawTextBuffer - horizontal viewport stops rendering at viewport width" {
1401
- const pool = gp.initGlobalPool(std.testing.allocator);
1402
- defer gp.deinitGlobalPool();
1403
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1404
- defer link.deinitGlobalLinkPool();
1405
-
1406
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1407
- defer tb.deinit();
1408
-
1409
- var view = try TextBufferView.init(std.testing.allocator, tb);
1410
- defer view.deinit();
1411
-
1412
- try tb.setText("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
1413
-
1414
- view.setWrapMode(.none);
1415
- view.setWrapWidth(null);
1416
- view.setViewport(.{ .x = 5, .y = 0, .width = 10, .height = 1 });
1417
-
1418
- var opt_buffer = try OptimizedBuffer.init(
1419
- std.testing.allocator,
1420
- 10,
1421
- 1,
1422
- .{ .pool = pool, .width_method = .unicode },
1423
- );
1424
- defer opt_buffer.deinit();
1425
-
1426
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1427
- try opt_buffer.drawTextBuffer(view, 0, 0);
1428
-
1429
- var out_buffer: [100]u8 = undefined;
1430
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
1431
- const result = out_buffer[0..written];
1432
-
1433
- try std.testing.expectEqualStrings("56789ABCDE", result[0..10]);
1434
-
1435
- const cell_9 = opt_buffer.get(9, 0);
1436
- try std.testing.expect(cell_9 != null);
1437
- try std.testing.expectEqual(@as(u32, 'E'), cell_9.?.char);
1438
- }
1439
-
1440
- test "drawTextBuffer - horizontal viewport with small buffer renders only viewport width" {
1441
- const pool = gp.initGlobalPool(std.testing.allocator);
1442
- defer gp.deinitGlobalPool();
1443
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1444
- defer link.deinitGlobalLinkPool();
1445
-
1446
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1447
- defer tb.deinit();
1448
-
1449
- var view = try TextBufferView.init(std.testing.allocator, tb);
1450
- defer view.deinit();
1451
-
1452
- try tb.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
1453
-
1454
- view.setWrapMode(.none);
1455
- view.setWrapWidth(null);
1456
- view.setViewport(.{ .x = 10, .y = 0, .width = 5, .height = 1 });
1457
-
1458
- var opt_buffer = try OptimizedBuffer.init(
1459
- std.testing.allocator,
1460
- 20,
1461
- 1,
1462
- .{ .pool = pool, .width_method = .unicode },
1463
- );
1464
- defer opt_buffer.deinit();
1465
-
1466
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1467
- try opt_buffer.drawTextBuffer(view, 0, 0);
1468
-
1469
- const cell_0 = opt_buffer.get(0, 0);
1470
- try std.testing.expect(cell_0 != null);
1471
- try std.testing.expectEqual(@as(u32, 'K'), cell_0.?.char);
1472
-
1473
- const cell_4 = opt_buffer.get(4, 0);
1474
- try std.testing.expect(cell_4 != null);
1475
- try std.testing.expectEqual(@as(u32, 'O'), cell_4.?.char);
1476
-
1477
- const cell_5 = opt_buffer.get(5, 0);
1478
- try std.testing.expect(cell_5 != null);
1479
- try std.testing.expectEqual(@as(u32, 32), cell_5.?.char);
1480
-
1481
- const cell_6 = opt_buffer.get(6, 0);
1482
- try std.testing.expect(cell_6 != null);
1483
- try std.testing.expectEqual(@as(u32, 32), cell_6.?.char);
1484
- }
1485
-
1486
- test "drawTextBuffer - horizontal viewport width limits rendering (efficiency test)" {
1487
- const pool = gp.initGlobalPool(std.testing.allocator);
1488
- defer gp.deinitGlobalPool();
1489
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1490
- defer link.deinitGlobalLinkPool();
1491
-
1492
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1493
- defer tb.deinit();
1494
-
1495
- var view = try TextBufferView.init(std.testing.allocator, tb);
1496
- defer view.deinit();
1497
-
1498
- var long_line: std.ArrayListUnmanaged(u8) = .{};
1499
- defer long_line.deinit(std.testing.allocator);
1500
- try long_line.appendNTimes(std.testing.allocator, 'A', 1000);
1501
-
1502
- try tb.setText(long_line.items);
1503
-
1504
- view.setWrapMode(.none);
1505
- view.setWrapWidth(null);
1506
- view.setViewport(.{ .x = 100, .y = 0, .width = 10, .height = 1 });
1507
-
1508
- var opt_buffer = try OptimizedBuffer.init(
1509
- std.testing.allocator,
1510
- 50,
1511
- 1,
1512
- .{ .pool = pool, .width_method = .unicode },
1513
- );
1514
- defer opt_buffer.deinit();
1515
-
1516
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1517
- try opt_buffer.drawTextBuffer(view, 0, 0);
1518
-
1519
- var non_space_count: u32 = 0;
1520
- var i: u32 = 0;
1521
- while (i < 50) : (i += 1) {
1522
- if (opt_buffer.get(i, 0)) |cell| {
1523
- if (cell.char == 'A') {
1524
- non_space_count += 1;
1525
- }
1526
- }
1527
- }
1528
-
1529
- try std.testing.expectEqual(@as(u32, 10), non_space_count);
1530
- }
1531
-
1532
- test "drawTextBuffer - overwriting wide grapheme with ASCII leaves no ghost chars" {
1533
- const pool = gp.initGlobalPool(std.testing.allocator);
1534
- defer gp.deinitGlobalPool();
1535
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1536
- defer link.deinitGlobalLinkPool();
1537
-
1538
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1539
- defer tb.deinit();
1540
-
1541
- var view = try TextBufferView.init(std.testing.allocator, tb);
1542
- defer view.deinit();
1543
-
1544
- var opt_buffer = try OptimizedBuffer.init(
1545
- std.testing.allocator,
1546
- 20,
1547
- 5,
1548
- .{ .pool = pool, .width_method = .unicode },
1549
- );
1550
- defer opt_buffer.deinit();
1551
-
1552
- try tb.setText("世界");
1553
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1554
- try opt_buffer.drawTextBuffer(view, 0, 0);
1555
-
1556
- const first_cell = opt_buffer.get(0, 0) orelse unreachable;
1557
- try std.testing.expect(gp.isGraphemeChar(first_cell.char));
1558
- try std.testing.expectEqual(@as(u32, 2), gp.encodedCharWidth(first_cell.char));
1559
-
1560
- const second_cell = opt_buffer.get(1, 0) orelse unreachable;
1561
- try std.testing.expect(gp.isContinuationChar(second_cell.char));
1562
-
1563
- try tb.setText("ABC");
1564
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1565
- try opt_buffer.drawTextBuffer(view, 0, 0);
1566
-
1567
- const cell_a = opt_buffer.get(0, 0) orelse unreachable;
1568
- try std.testing.expectEqual(@as(u32, 'A'), cell_a.char);
1569
- try std.testing.expect(!gp.isGraphemeChar(cell_a.char));
1570
- try std.testing.expect(!gp.isContinuationChar(cell_a.char));
1571
-
1572
- const cell_b = opt_buffer.get(1, 0) orelse unreachable;
1573
- try std.testing.expectEqual(@as(u32, 'B'), cell_b.char);
1574
- try std.testing.expect(!gp.isGraphemeChar(cell_b.char));
1575
- try std.testing.expect(!gp.isContinuationChar(cell_b.char));
1576
-
1577
- const cell_c = opt_buffer.get(2, 0) orelse unreachable;
1578
- try std.testing.expectEqual(@as(u32, 'C'), cell_c.char);
1579
- try std.testing.expect(!gp.isGraphemeChar(cell_c.char));
1580
- try std.testing.expect(!gp.isContinuationChar(cell_c.char));
1581
-
1582
- var out_buffer: [100]u8 = undefined;
1583
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
1584
- const result = out_buffer[0..written];
1585
- try std.testing.expect(std.mem.startsWith(u8, result, "ABC"));
1586
- }
1587
-
1588
- test "drawTextBuffer - syntax style destroy does not crash" {
1589
- const pool = gp.initGlobalPool(std.testing.allocator);
1590
- defer gp.deinitGlobalPool();
1591
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1592
- defer link.deinitGlobalLinkPool();
1593
-
1594
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1595
- defer tb.deinit();
1596
-
1597
- var view = try TextBufferView.init(std.testing.allocator, tb);
1598
- defer view.deinit();
1599
-
1600
- var style = try ss.SyntaxStyle.init(std.testing.allocator);
1601
- tb.setSyntaxStyle(style);
1602
-
1603
- const style_id = try style.registerStyle("test", .{ 1.0, 0.0, 0.0, 1.0 }, null, 0);
1604
- try tb.setText("Hello World");
1605
- try tb.addHighlightByCharRange(0, 5, style_id, 1, 0);
1606
-
1607
- var opt_buffer = try OptimizedBuffer.init(
1608
- std.testing.allocator,
1609
- 20,
1610
- 5,
1611
- .{ .pool = pool, .width_method = .unicode },
1612
- );
1613
- defer opt_buffer.deinit();
1614
-
1615
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1616
- try opt_buffer.drawTextBuffer(view, 0, 0);
1617
-
1618
- var out_buffer: [100]u8 = undefined;
1619
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
1620
- const result = out_buffer[0..written];
1621
- try std.testing.expect(std.mem.startsWith(u8, result, "Hello World"));
1622
-
1623
- style.deinit();
1624
-
1625
- try std.testing.expect(tb.getSyntaxStyle() == null);
1626
-
1627
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1628
- try opt_buffer.drawTextBuffer(view, 0, 0);
1629
-
1630
- const written2 = try opt_buffer.writeResolvedChars(&out_buffer, false);
1631
- const result2 = out_buffer[0..written2];
1632
- try std.testing.expect(std.mem.startsWith(u8, result2, "Hello World"));
1633
- }
1634
-
1635
- test "drawTextBuffer - tabs are rendered as spaces (empty cells)" {
1636
- const pool = gp.initGlobalPool(std.testing.allocator);
1637
- defer gp.deinitGlobalPool();
1638
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1639
- defer link.deinitGlobalLinkPool();
1640
-
1641
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1642
- defer tb.deinit();
1643
-
1644
- var view = try TextBufferView.init(std.testing.allocator, tb);
1645
- defer view.deinit();
1646
-
1647
- tb.setTabWidth(4);
1648
-
1649
- try tb.setText("A\tB");
1650
-
1651
- var opt_buffer = try OptimizedBuffer.init(
1652
- std.testing.allocator,
1653
- 20,
1654
- 5,
1655
- .{ .pool = pool, .width_method = .unicode },
1656
- );
1657
- defer opt_buffer.deinit();
1658
-
1659
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1660
- try opt_buffer.drawTextBuffer(view, 0, 0);
1661
-
1662
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
1663
- try std.testing.expectEqual(@as(u32, 'A'), cell_0.char);
1664
-
1665
- const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
1666
- try std.testing.expectEqual(@as(u32, 32), cell_1.char);
1667
-
1668
- const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
1669
- try std.testing.expectEqual(@as(u32, 32), cell_2.char);
1670
-
1671
- const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
1672
- try std.testing.expectEqual(@as(u32, 32), cell_3.char);
1673
-
1674
- const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
1675
- try std.testing.expectEqual(@as(u32, 32), cell_4.char);
1676
-
1677
- // With static tabs: A at col 0, tab takes 4 cols (1-4), B at col 5
1678
- const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
1679
- try std.testing.expectEqual(@as(u32, 'B'), cell_5.char);
1680
- }
1681
-
1682
- test "drawTextBuffer - tab indicator renders with correct color" {
1683
- const pool = gp.initGlobalPool(std.testing.allocator);
1684
- defer gp.deinitGlobalPool();
1685
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1686
- defer link.deinitGlobalLinkPool();
1687
-
1688
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1689
- defer tb.deinit();
1690
-
1691
- var view = try TextBufferView.init(std.testing.allocator, tb);
1692
- defer view.deinit();
1693
-
1694
- tb.setTabWidth(4);
1695
- try tb.setText("A\tB");
1696
-
1697
- view.setTabIndicator(@as(u32, '→'));
1698
- view.setTabIndicatorColor(RGBA{ 0.25, 0.25, 0.25, 1.0 });
1699
-
1700
- var opt_buffer = try OptimizedBuffer.init(
1701
- std.testing.allocator,
1702
- 20,
1703
- 5,
1704
- .{ .pool = pool, .width_method = .unicode },
1705
- );
1706
- defer opt_buffer.deinit();
1707
-
1708
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1709
- try opt_buffer.drawTextBuffer(view, 0, 0);
1710
-
1711
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
1712
- try std.testing.expectEqual(@as(u32, 'A'), cell_0.char);
1713
-
1714
- const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
1715
- try std.testing.expectEqual(@as(u32, '→'), cell_1.char);
1716
- try std.testing.expectEqual(@as(f32, 0.25), cell_1.fg[0]);
1717
- try std.testing.expectEqual(@as(f32, 0.25), cell_1.fg[1]);
1718
- try std.testing.expectEqual(@as(f32, 0.25), cell_1.fg[2]);
1719
-
1720
- const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
1721
- try std.testing.expectEqual(@as(u32, 32), cell_2.char);
1722
-
1723
- const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
1724
- try std.testing.expectEqual(@as(u32, 32), cell_3.char);
1725
-
1726
- const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
1727
- try std.testing.expectEqual(@as(u32, 32), cell_4.char);
1728
-
1729
- // With static tabs: A at col 0, tab takes 4 cols (1-4), B at col 5
1730
- const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
1731
- try std.testing.expectEqual(@as(u32, 'B'), cell_5.char);
1732
- }
1733
-
1734
- test "drawTextBuffer - tab without indicator renders as spaces" {
1735
- const pool = gp.initGlobalPool(std.testing.allocator);
1736
- defer gp.deinitGlobalPool();
1737
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1738
- defer link.deinitGlobalLinkPool();
1739
-
1740
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1741
- defer tb.deinit();
1742
-
1743
- var view = try TextBufferView.init(std.testing.allocator, tb);
1744
- defer view.deinit();
1745
-
1746
- tb.setTabWidth(4);
1747
- try tb.setText("A\tB");
1748
-
1749
- var opt_buffer = try OptimizedBuffer.init(
1750
- std.testing.allocator,
1751
- 20,
1752
- 5,
1753
- .{ .pool = pool, .width_method = .unicode },
1754
- );
1755
- defer opt_buffer.deinit();
1756
-
1757
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1758
- try opt_buffer.drawTextBuffer(view, 0, 0);
1759
-
1760
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
1761
- try std.testing.expectEqual(@as(u32, 'A'), cell_0.char);
1762
-
1763
- const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
1764
- try std.testing.expectEqual(@as(u32, 32), cell_1.char);
1765
-
1766
- const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
1767
- try std.testing.expectEqual(@as(u32, 32), cell_2.char);
1768
-
1769
- const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
1770
- try std.testing.expectEqual(@as(u32, 32), cell_3.char);
1771
-
1772
- const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
1773
- try std.testing.expectEqual(@as(u32, 32), cell_4.char);
1774
-
1775
- // With static tabs: A at col 0, tab takes 4 cols (1-4), B at col 5
1776
- const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
1777
- try std.testing.expectEqual(@as(u32, 'B'), cell_5.char);
1778
- }
1779
-
1780
- test "drawTextBuffer - mixed ASCII and Unicode with emoji renders completely" {
1781
- const pool = gp.initGlobalPool(std.testing.allocator);
1782
- defer gp.deinitGlobalPool();
1783
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1784
- defer link.deinitGlobalLinkPool();
1785
-
1786
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1787
- defer tb.deinit();
1788
-
1789
- var view = try TextBufferView.init(std.testing.allocator, tb);
1790
- defer view.deinit();
1791
-
1792
- try tb.setText("- ✅ All 881 native tests passs");
1793
-
1794
- var opt_buffer = try OptimizedBuffer.init(
1795
- std.testing.allocator,
1796
- 50,
1797
- 5,
1798
- .{ .pool = pool, .width_method = .unicode },
1799
- );
1800
- defer opt_buffer.deinit();
1801
-
1802
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1803
- try opt_buffer.drawTextBuffer(view, 0, 0);
1804
-
1805
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
1806
- try std.testing.expectEqual(@as(u32, '-'), cell_0.char);
1807
-
1808
- const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
1809
- try std.testing.expectEqual(@as(u32, ' '), cell_1.char);
1810
-
1811
- const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
1812
- try std.testing.expect(gp.isGraphemeChar(cell_2.char));
1813
- const width_2 = gp.encodedCharWidth(cell_2.char);
1814
- try std.testing.expectEqual(@as(u32, 2), width_2);
1815
-
1816
- const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
1817
- try std.testing.expect(gp.isContinuationChar(cell_3.char));
1818
-
1819
- const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
1820
- try std.testing.expectEqual(@as(u32, ' '), cell_4.char);
1821
-
1822
- const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
1823
- try std.testing.expectEqual(@as(u32, 'A'), cell_5.char);
1824
-
1825
- const cell_6 = opt_buffer.get(6, 0) orelse unreachable;
1826
- try std.testing.expectEqual(@as(u32, 'l'), cell_6.char);
1827
-
1828
- const cell_7 = opt_buffer.get(7, 0) orelse unreachable;
1829
- try std.testing.expectEqual(@as(u32, 'l'), cell_7.char);
1830
-
1831
- const cell_8 = opt_buffer.get(8, 0) orelse unreachable;
1832
- try std.testing.expectEqual(@as(u32, ' '), cell_8.char);
1833
-
1834
- const cell_9 = opt_buffer.get(9, 0) orelse unreachable;
1835
- try std.testing.expectEqual(@as(u32, '8'), cell_9.char);
1836
-
1837
- const cell_10 = opt_buffer.get(10, 0) orelse unreachable;
1838
- try std.testing.expectEqual(@as(u32, '8'), cell_10.char);
1839
-
1840
- const cell_11 = opt_buffer.get(11, 0) orelse unreachable;
1841
- try std.testing.expectEqual(@as(u32, '1'), cell_11.char);
1842
-
1843
- const cell_12 = opt_buffer.get(12, 0) orelse unreachable;
1844
- try std.testing.expectEqual(@as(u32, ' '), cell_12.char);
1845
-
1846
- const cell_13 = opt_buffer.get(13, 0) orelse unreachable;
1847
- try std.testing.expectEqual(@as(u32, 'n'), cell_13.char);
1848
-
1849
- const cell_14 = opt_buffer.get(14, 0) orelse unreachable;
1850
- try std.testing.expectEqual(@as(u32, 'a'), cell_14.char);
1851
-
1852
- const cell_15 = opt_buffer.get(15, 0) orelse unreachable;
1853
- try std.testing.expectEqual(@as(u32, 't'), cell_15.char);
1854
-
1855
- const cell_16 = opt_buffer.get(16, 0) orelse unreachable;
1856
- try std.testing.expectEqual(@as(u32, 'i'), cell_16.char);
1857
-
1858
- const cell_17 = opt_buffer.get(17, 0) orelse unreachable;
1859
- try std.testing.expectEqual(@as(u32, 'v'), cell_17.char);
1860
-
1861
- const cell_18 = opt_buffer.get(18, 0) orelse unreachable;
1862
- try std.testing.expectEqual(@as(u32, 'e'), cell_18.char);
1863
-
1864
- const cell_19 = opt_buffer.get(19, 0) orelse unreachable;
1865
- try std.testing.expectEqual(@as(u32, ' '), cell_19.char);
1866
-
1867
- const cell_20 = opt_buffer.get(20, 0) orelse unreachable;
1868
- try std.testing.expectEqual(@as(u32, 't'), cell_20.char);
1869
-
1870
- const cell_21 = opt_buffer.get(21, 0) orelse unreachable;
1871
- try std.testing.expectEqual(@as(u32, 'e'), cell_21.char);
1872
-
1873
- const cell_22 = opt_buffer.get(22, 0) orelse unreachable;
1874
- try std.testing.expectEqual(@as(u32, 's'), cell_22.char);
1875
-
1876
- const cell_23 = opt_buffer.get(23, 0) orelse unreachable;
1877
- try std.testing.expectEqual(@as(u32, 't'), cell_23.char);
1878
-
1879
- const cell_24 = opt_buffer.get(24, 0) orelse unreachable;
1880
- try std.testing.expectEqual(@as(u32, 's'), cell_24.char);
1881
-
1882
- const cell_25 = opt_buffer.get(25, 0) orelse unreachable;
1883
- try std.testing.expectEqual(@as(u32, ' '), cell_25.char);
1884
-
1885
- const cell_26 = opt_buffer.get(26, 0) orelse unreachable;
1886
- try std.testing.expectEqual(@as(u32, 'p'), cell_26.char);
1887
-
1888
- const cell_27 = opt_buffer.get(27, 0) orelse unreachable;
1889
- try std.testing.expectEqual(@as(u32, 'a'), cell_27.char);
1890
-
1891
- const cell_28 = opt_buffer.get(28, 0) orelse unreachable;
1892
- try std.testing.expectEqual(@as(u32, 's'), cell_28.char);
1893
-
1894
- const cell_29 = opt_buffer.get(29, 0) orelse unreachable;
1895
- try std.testing.expectEqual(@as(u32, 's'), cell_29.char);
1896
-
1897
- const cell_30 = opt_buffer.get(30, 0) orelse unreachable;
1898
- try std.testing.expectEqual(@as(u32, 's'), cell_30.char);
1899
-
1900
- var out_buffer: [500]u8 = undefined;
1901
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
1902
- const result = out_buffer[0..written];
1903
-
1904
- try std.testing.expect(std.mem.indexOf(u8, result, "- ✅ All 881 native tests passs") != null);
1905
-
1906
- const plain_text = tb.getPlainTextIntoBuffer(&out_buffer);
1907
- const plain_result = out_buffer[0..plain_text];
1908
- try std.testing.expectEqualStrings("- ✅ All 881 native tests passs", plain_result);
1909
- }
1910
-
1911
- test "viewport width = 31 exactly - last character rendering" {
1912
- const pool = gp.initGlobalPool(std.testing.allocator);
1913
- defer gp.deinitGlobalPool();
1914
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1915
- defer link.deinitGlobalLinkPool();
1916
-
1917
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1918
- defer tb.deinit();
1919
-
1920
- var view = try TextBufferView.init(std.testing.allocator, tb);
1921
- defer view.deinit();
1922
-
1923
- try tb.setText("- ✅ All 881 native tests passs");
1924
-
1925
- // Set viewport width to EXACTLY 31 (the display width needed)
1926
- view.setViewport(text_buffer_view.Viewport{ .x = 0, .y = 0, .width = 31, .height = 1 });
1927
-
1928
- var opt_buffer = try OptimizedBuffer.init(
1929
- std.testing.allocator,
1930
- 50,
1931
- 5,
1932
- .{ .pool = pool, .width_method = .unicode },
1933
- );
1934
- defer opt_buffer.deinit();
1935
-
1936
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1937
- try opt_buffer.drawTextBuffer(view, 0, 0);
1938
-
1939
- // BUG CHECK: The last 's' at cell 30 should be present
1940
- const cell_30 = opt_buffer.get(30, 0);
1941
- if (cell_30) |c| {
1942
- try std.testing.expectEqual(@as(u32, 's'), c.char);
1943
- } else {
1944
- return error.TestFailed;
1945
- }
1946
- }
1947
-
1948
- test "drawTextBuffer - complex multilingual text with diverse scripts and emojis" {
1949
- const pool = gp.initGlobalPool(std.testing.allocator);
1950
- defer gp.deinitGlobalPool();
1951
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
1952
- defer link.deinitGlobalLinkPool();
1953
-
1954
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
1955
- defer tb.deinit();
1956
-
1957
- var view = try TextBufferView.init(std.testing.allocator, tb);
1958
- defer view.deinit();
1959
-
1960
- const text =
1961
- \\# The Celestial Journey of संस्कृति 🌟🔮✨
1962
- \\In the beginning, there was नमस्ते 🙏 and the ancient wisdom of the ॐ symbol echoing through dimensions. The travelers 🧑‍🚀👨‍🚀👩‍🚀 embarked on their quest through the cosmos, guided by the mysterious རྒྱ་མཚོ and the luminous 🌈🦄🧚‍♀️ beings of light. They encountered the great देवनागरी scribes who wrote in flowing अक्षर characters, documenting everything in their sacred texts 📜📖✍️.
1963
- \\## Chapter प्रथम: The Eastern Gardens 🏯🎋🌸
1964
- \\The journey led them to the mystical lands where 漢字 (kanji) danced with ひらがな and カタカナ across ancient scrolls 📯🎴🎎. In the gardens of Seoul, they found 한글 inscriptions speaking of 사랑 (love) and 평화 (peace) 💝🕊️☮️. The monks meditated under the bodhi tree 🧘‍♂️🌳, contemplating the nature of धर्म while drinking matcha 🍵 and eating 餃子 dumplings 🥟.
1965
- \\Strange creatures emerged from the mist: 🦥🦦🦧🦨🦩🦚🦜🦝🦞🦟. They spoke in riddles about the प्राचीन (ancient) ways and the नवीन (new) paths forward. "भविष्य में क्या है?" they asked, while the ໂຫຍ່າກເຈົ້າ whispered secrets in Lao script 🤫🗣️💬.
1966
- \\## The संगम (Confluence) of Scripts 🌊📝🎭
1967
- \\At the great confluence, they witnessed the merger of བོད་ཡིག (Tibetan), ગુજરાતી (Gujarati), and தமிழ் (Tamil) scripts flowing together like rivers 🏞️🌊💧. The scholars debated about ਪੰਜਾਬੀ philosophy while juggling 🤹‍♂️🎪🎨 colorful orbs that represented different తెలుగు concepts.
1968
- \\The marketplace buzzed with activity 🏪🛒💰: merchants sold বাংলা spices 🌶️🧄🧅, ಕನ್ನಡ silks 🧵👘, and മലയാളം handicrafts 🎨🖼️. Children played with toys shaped like 🦖🦕🐉🐲 while their parents bargained using ancient ଓଡ଼ିଆ numerals and gestures 🤝🤲👐.
1969
- \\## The Festival of ๑๐๐ Lanterns 🏮🎆🎇
1970
- \\During the grand festival, they lit exactly ๑๐๐ (100 in Thai numerals) lanterns 🏮🕯️💡 that floated into the night sky like ascending ความหวัง (hopes). The celebration featured dancers 💃🕺🩰 performing classical moves from भरतनाट्यम tradition, their मुद्रा hand gestures telling stories of प्रेम and वीरता.
1971
- \\Musicians played unusual instruments: the 🎻🎺🎷🎸🪕🪘 ensemble created harmonies that resonated with the वेद chants and མཆོད་རྟེན bells 🔔⛩️. The audience sat mesmerized 😵‍💫🤯✨, some sipping on bubble tea 🧋 while others enjoyed मिठाई sweets 🍬🍭🧁.
1972
- \\## The འཕྲུལ་དེབ (Machine) Age Arrives ⚙️🤖🦾
1973
- \\As modernity crept in, the ancient འཁོར་ལོ (wheel) gave way to 🚗🚕🚙🚌🚎 vehicles and eventually to 🚀🛸🛰️ spacecraft. The યુવાન (youth) learned to code in Python 🐍💻⌨️, but still honored their గురువు (teachers) who taught them the old ways of ज्ञान acquisition 🧠📚🎓.
1974
- \\The সমাজ (society) transformed: robots 🤖🦾🦿 worked alongside humans 👨‍💼👩‍💼👨‍🔬👩‍🔬, and AI learned to read སྐད (languages) from across the planet 🌍🌎🌏. Yet somehow, the essence of मानवता remained intact, preserved in the கவிதை (poetry) and the ກາບແກ້ວ stories passed down through generations 👴👵👨‍👩‍👧‍👦.
1975
- \\## The Final ಅಧ್ಯಾಯ (Chapter) 🌅🌄🌠
1976
- \\As the sun set over the പർവ്വതങ്ങൾ (mountains) 🏔️⛰️🗻, our travelers realized that every script, every symbol—from ا to ㄱ to অ to अ—represented not just sounds, but entire civilizations' worth of विचार (thoughts) and ಕನಸು (dreams) 💭💤🌌.
1977
- \\They gathered around the final campfire 🔥🏕️, sharing stories in ภาษา (languages) both ancient and new. Someone brought out a guitar 🎸 and started singing in ગીત form, while others prepared ආහාර (food) 🍛🍲🥘 seasoned with love ❤️💕💖 and memories 📸🎞️📹.
1978
- \\And so they learned that whether written in দেবনাগরী, 中文, 한글, or ไทย, the human experience transcends boundaries 🌐🤝🌈. The weird emojis 🦩🧿🪬🫀🫁🧠 and complex scripts were all part of the same beautiful བསྟན་པ (teaching): that diversity is our greatest strength 💪✊🙌.
1979
- \\The end. समाप्त. 끝. จบ. முடிவு. ముగింపు. সমাপ্তি. ഒടുക്കം. ಅಂತ್ಯ. અંત. 🎬🎭🎪✨🌟⭐
1980
- \\
1981
- ;
1982
-
1983
- try tb.setText(text);
1984
-
1985
- // Test with word wrapping
1986
- view.setWrapMode(.word);
1987
- view.setWrapWidth(80);
1988
- view.updateVirtualLines();
1989
-
1990
- var opt_buffer = try OptimizedBuffer.init(
1991
- std.testing.allocator,
1992
- 80,
1993
- 100,
1994
- .{ .pool = pool, .width_method = .unicode },
1995
- );
1996
- defer opt_buffer.deinit();
1997
-
1998
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
1999
- try opt_buffer.drawTextBuffer(view, 0, 0);
2000
-
2001
- // Verify the text buffer can handle complex multilingual content
2002
- const virtual_lines = view.getVirtualLines();
2003
- try std.testing.expect(virtual_lines.len > 0);
2004
-
2005
- // Test that we can get the plain text back
2006
- var plain_buffer: [10000]u8 = undefined;
2007
- const plain_len = tb.getPlainTextIntoBuffer(&plain_buffer);
2008
- const plain_text = plain_buffer[0..plain_len];
2009
-
2010
- // Verify some key multilingual content is present
2011
- try std.testing.expect(std.mem.indexOf(u8, plain_text, "संस्कृति") != null);
2012
- try std.testing.expect(std.mem.indexOf(u8, plain_text, "नमस्ते") != null);
2013
- try std.testing.expect(std.mem.indexOf(u8, plain_text, "漢字") != null);
2014
- try std.testing.expect(std.mem.indexOf(u8, plain_text, "한글") != null);
2015
- try std.testing.expect(std.mem.indexOf(u8, plain_text, "தமிழ்") != null);
2016
- try std.testing.expect(std.mem.indexOf(u8, plain_text, "বাংলা") != null);
2017
- try std.testing.expect(std.mem.indexOf(u8, plain_text, "ಕನ್ನಡ") != null);
2018
- try std.testing.expect(std.mem.indexOf(u8, plain_text, "മലയാളം") != null);
2019
- try std.testing.expect(std.mem.indexOf(u8, plain_text, "🌟") != null);
2020
- try std.testing.expect(std.mem.indexOf(u8, plain_text, "🙏") != null);
2021
-
2022
- // Test with no wrapping
2023
- view.setWrapMode(.none);
2024
- view.setWrapWidth(null);
2025
- view.updateVirtualLines();
2026
-
2027
- const no_wrap_lines = view.getVirtualLines();
2028
- // Should have one line per actual newline in the text
2029
- try std.testing.expect(no_wrap_lines.len > 10);
2030
-
2031
- // Test with character wrapping on narrow width
2032
- view.setWrapMode(.char);
2033
- view.setWrapWidth(40);
2034
- view.updateVirtualLines();
2035
-
2036
- const char_wrap_lines = view.getVirtualLines();
2037
- // Should wrap into many more lines
2038
- try std.testing.expect(char_wrap_lines.len > virtual_lines.len);
2039
-
2040
- // Test viewport scrolling through the content
2041
- view.setWrapMode(.word);
2042
- view.setWrapWidth(80);
2043
- view.setViewport(.{ .x = 0, .y = 10, .width = 80, .height = 20 });
2044
- view.updateVirtualLines();
2045
-
2046
- const viewport_lines = view.getVirtualLines();
2047
- try std.testing.expect(viewport_lines.len <= 20);
2048
-
2049
- // Verify rendering doesn't crash with complex emoji sequences
2050
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2051
- try opt_buffer.drawTextBuffer(view, 0, 0);
2052
-
2053
- // Test that line count is reasonable
2054
- const line_count = tb.getLineCount();
2055
- try std.testing.expect(line_count > 15);
2056
- }
2057
-
2058
- test "setStyledText - highlight positioning with Unicode text" {
2059
- const pool = gp.initGlobalPool(std.testing.allocator);
2060
- defer gp.deinitGlobalPool();
2061
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
2062
- defer link.deinitGlobalLinkPool();
2063
-
2064
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
2065
- defer tb.deinit();
2066
-
2067
- var view = try TextBufferView.init(std.testing.allocator, tb);
2068
- defer view.deinit();
2069
-
2070
- const style = try ss.SyntaxStyle.init(std.testing.allocator);
2071
- defer style.deinit();
2072
- tb.setSyntaxStyle(style);
2073
-
2074
- // Text: "Say नमस्ते please."
2075
- // Layout: "Say " (4 cols) + "नमस्ते" (4 cols) + " " (1 col) + "please" (6 cols) + "." (1 col)
2076
- // We highlight "please" with a green background to verify correct positioning
2077
- const text_part1 = "Say ";
2078
- const text_part2 = "नमस्ते";
2079
- const text_part3 = " ";
2080
- const text_part4 = "please";
2081
- const text_part5 = ".";
2082
-
2083
- const fg_normal = [4]f32{ 1.0, 1.0, 1.0, 1.0 };
2084
- const bg_highlight = [4]f32{ 0.0, 1.0, 0.0, 1.0 }; // Green background
2085
-
2086
- const chunks = [_]StyledChunk{
2087
- .{ .text_ptr = text_part1.ptr, .text_len = text_part1.len, .fg_ptr = @ptrCast(&fg_normal), .bg_ptr = null, .attributes = 0 },
2088
- .{ .text_ptr = text_part2.ptr, .text_len = text_part2.len, .fg_ptr = @ptrCast(&fg_normal), .bg_ptr = null, .attributes = 0 },
2089
- .{ .text_ptr = text_part3.ptr, .text_len = text_part3.len, .fg_ptr = @ptrCast(&fg_normal), .bg_ptr = null, .attributes = 0 },
2090
- .{ .text_ptr = text_part4.ptr, .text_len = text_part4.len, .fg_ptr = @ptrCast(&fg_normal), .bg_ptr = @ptrCast(&bg_highlight), .attributes = 0 },
2091
- .{ .text_ptr = text_part5.ptr, .text_len = text_part5.len, .fg_ptr = @ptrCast(&fg_normal), .bg_ptr = null, .attributes = 0 },
2092
- };
2093
-
2094
- try tb.setStyledText(&chunks);
2095
-
2096
- // Verify the text content
2097
- var out_buffer: [100]u8 = undefined;
2098
- const written = tb.getPlainTextIntoBuffer(&out_buffer);
2099
- const result = out_buffer[0..written];
2100
- try std.testing.expectEqualStrings("Say नमस्ते please.", result);
2101
-
2102
- // Calculate expected positions using measureText
2103
- const part1_width = tb.measureText(text_part1);
2104
- const part2_width = tb.measureText(text_part2);
2105
- const part3_width = tb.measureText(text_part3);
2106
- const please_start_col = part1_width + part2_width + part3_width;
2107
-
2108
- // Render to buffer and check colors
2109
- var opt_buffer = try OptimizedBuffer.init(
2110
- std.testing.allocator,
2111
- 30,
2112
- 5,
2113
- .{ .pool = pool, .width_method = .unicode },
2114
- );
2115
- defer opt_buffer.deinit();
2116
-
2117
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2118
- try opt_buffer.drawTextBuffer(view, 0, 0);
2119
-
2120
- // Check that "please" (6 characters) all have the green background
2121
- const epsilon: f32 = 0.01;
2122
- var i: u32 = 0;
2123
- while (i < 6) : (i += 1) {
2124
- const cell_col = please_start_col + i;
2125
- const cell = opt_buffer.get(cell_col, 0) orelse return error.TestFailed;
2126
-
2127
- // Verify green background (R=0, G=1, B=0)
2128
- try std.testing.expect(@abs(cell.bg[0] - 0.0) < epsilon);
2129
- try std.testing.expect(@abs(cell.bg[1] - 1.0) < epsilon);
2130
- try std.testing.expect(@abs(cell.bg[2] - 0.0) < epsilon);
2131
- }
2132
-
2133
- // Check that text before "please" does NOT have green background
2134
- i = 0;
2135
- while (i < please_start_col) : (i += 1) {
2136
- const cell = opt_buffer.get(i, 0) orelse unreachable;
2137
- const has_green_bg = @abs(cell.bg[1] - 1.0) < epsilon and @abs(cell.bg[0] - 0.0) < epsilon;
2138
- try std.testing.expect(!has_green_bg);
2139
- }
2140
-
2141
- // Check that "." after "please" does NOT have green background
2142
- const period_col = please_start_col + 6;
2143
- const period_cell = opt_buffer.get(period_col, 0) orelse unreachable;
2144
- const has_green_bg = @abs(period_cell.bg[1] - 1.0) < epsilon and @abs(period_cell.bg[0] - 0.0) < epsilon;
2145
- try std.testing.expect(!has_green_bg);
2146
- }
2147
-
2148
- test "drawTextBuffer - multiple syntax highlights with various horizontal viewport offsets" {
2149
- const pool = gp.initGlobalPool(std.testing.allocator);
2150
- defer gp.deinitGlobalPool();
2151
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
2152
- defer link.deinitGlobalLinkPool();
2153
-
2154
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
2155
- defer tb.deinit();
2156
-
2157
- var view = try TextBufferView.init(std.testing.allocator, tb);
2158
- defer view.deinit();
2159
-
2160
- const style = try ss.SyntaxStyle.init(std.testing.allocator);
2161
- defer style.deinit();
2162
- tb.setSyntaxStyle(style);
2163
-
2164
- // Register different color styles
2165
- const red_style = try style.registerStyle("red", RGBA{ 1.0, 0.0, 0.0, 1.0 }, null, 0);
2166
- const green_style = try style.registerStyle("green", RGBA{ 0.0, 1.0, 0.0, 1.0 }, null, 0);
2167
- const blue_style = try style.registerStyle("blue", RGBA{ 0.0, 0.0, 1.0, 1.0 }, null, 0);
2168
- const yellow_style = try style.registerStyle("yellow", RGBA{ 1.0, 1.0, 0.0, 1.0 }, null, 0);
2169
-
2170
- // Text: "const x = function(y) { return y * 2; }"
2171
- const test_text = "const x = function(y) { return y * 2; }";
2172
- // Positions (0-indexed):
2173
- // "const" is at 0-5 (exclusive end, so 0,1,2,3,4)
2174
- // "function" is at 10-18 (chars 10-17)
2175
- // "return" is at 24-30 (chars 24-29)
2176
- // "2" is at 35-36 (char 35)
2177
-
2178
- try tb.setText(test_text);
2179
-
2180
- try tb.addHighlightByCharRange(0, 5, red_style, 1, 0); // "const"
2181
- try tb.addHighlightByCharRange(10, 18, green_style, 1, 0); // "function"
2182
- try tb.addHighlightByCharRange(24, 30, blue_style, 1, 0); // "return"
2183
- try tb.addHighlightByCharRange(35, 36, yellow_style, 1, 0); // "2"
2184
-
2185
- view.setWrapMode(.none);
2186
- view.setWrapWidth(null);
2187
-
2188
- const epsilon: f32 = 0.01;
2189
-
2190
- // Test 1: Viewport at x=0 (no scroll)
2191
- {
2192
- view.setViewport(.{ .x = 0, .y = 0, .width = 40, .height = 1 });
2193
- var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 40, 1, .{ .pool = pool, .width_method = .unicode });
2194
- defer opt_buffer.deinit();
2195
-
2196
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2197
- try opt_buffer.drawTextBuffer(view, 0, 0);
2198
-
2199
- // Check "const" is red
2200
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
2201
- try std.testing.expectEqual(@as(u32, 'c'), cell_0.char);
2202
- try std.testing.expect(@abs(cell_0.fg[0] - 1.0) < epsilon); // Red
2203
-
2204
- const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
2205
- try std.testing.expectEqual(@as(u32, 't'), cell_4.char);
2206
- try std.testing.expect(@abs(cell_4.fg[0] - 1.0) < epsilon); // Red
2207
-
2208
- // Check "function" is green
2209
- const cell_10 = opt_buffer.get(10, 0) orelse unreachable;
2210
- try std.testing.expectEqual(@as(u32, 'f'), cell_10.char);
2211
- try std.testing.expect(@abs(cell_10.fg[1] - 1.0) < epsilon); // Green
2212
-
2213
- const cell_17 = opt_buffer.get(17, 0) orelse unreachable;
2214
- try std.testing.expectEqual(@as(u32, 'n'), cell_17.char);
2215
- try std.testing.expect(@abs(cell_17.fg[1] - 1.0) < epsilon); // Green
2216
- }
2217
-
2218
- // Test 2: Viewport scrolled to x=3 (showing "st x = fun...")
2219
- {
2220
- view.setViewport(.{ .x = 3, .y = 0, .width = 20, .height = 1 });
2221
- var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 20, 1, .{ .pool = pool, .width_method = .unicode });
2222
- defer opt_buffer.deinit();
2223
-
2224
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2225
- try opt_buffer.drawTextBuffer(view, 0, 0);
2226
-
2227
- // Buffer shows characters 3-22 from source: "st x = function(y) {"
2228
- // Position 0: 's' (source 3) - should be RED (part of "const" 0-5)
2229
- // Position 1: 't' (source 4) - should be RED (part of "const" 0-5)
2230
- // Position 2: ' ' (source 5) - NOT red (outside "const")
2231
- // Position 7: 'f' (source 10) - should be GREEN (start of "function" 10-18)
2232
- // Position 14: 'n' (source 17) - should be GREEN (part of "function")
2233
-
2234
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
2235
- try std.testing.expectEqual(@as(u32, 's'), cell_0.char);
2236
- try std.testing.expect(@abs(cell_0.fg[0] - 1.0) < epsilon); // Red
2237
- try std.testing.expect(@abs(cell_0.fg[1] - 0.0) < epsilon);
2238
- try std.testing.expect(@abs(cell_0.fg[2] - 0.0) < epsilon);
2239
-
2240
- const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
2241
- try std.testing.expectEqual(@as(u32, 't'), cell_1.char);
2242
- try std.testing.expect(@abs(cell_1.fg[0] - 1.0) < epsilon); // Red
2243
- try std.testing.expect(@abs(cell_1.fg[1] - 0.0) < epsilon);
2244
- try std.testing.expect(@abs(cell_1.fg[2] - 0.0) < epsilon);
2245
-
2246
- const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
2247
- try std.testing.expectEqual(@as(u32, ' '), cell_2.char);
2248
- try std.testing.expect(@abs(cell_2.fg[0] - 1.0) < epsilon); // White (default)
2249
- try std.testing.expect(@abs(cell_2.fg[1] - 1.0) < epsilon);
2250
- try std.testing.expect(@abs(cell_2.fg[2] - 1.0) < epsilon);
2251
-
2252
- const cell_7 = opt_buffer.get(7, 0) orelse unreachable;
2253
- try std.testing.expectEqual(@as(u32, 'f'), cell_7.char);
2254
- try std.testing.expect(@abs(cell_7.fg[0] - 0.0) < epsilon); // Green
2255
- try std.testing.expect(@abs(cell_7.fg[1] - 1.0) < epsilon);
2256
- try std.testing.expect(@abs(cell_7.fg[2] - 0.0) < epsilon);
2257
-
2258
- const cell_14 = opt_buffer.get(14, 0) orelse unreachable;
2259
- try std.testing.expectEqual(@as(u32, 'n'), cell_14.char);
2260
- try std.testing.expect(@abs(cell_14.fg[0] - 0.0) < epsilon); // Green
2261
- try std.testing.expect(@abs(cell_14.fg[1] - 1.0) < epsilon);
2262
- try std.testing.expect(@abs(cell_14.fg[2] - 0.0) < epsilon);
2263
- }
2264
-
2265
- // Test 4: Viewport scrolled to x=30 (showing "y * 2; }" based on 40 char text)
2266
- {
2267
- view.setViewport(.{ .x = 30, .y = 0, .width = 20, .height = 1 });
2268
- var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 20, 1, .{ .pool = pool, .width_method = .unicode });
2269
- defer opt_buffer.deinit();
2270
-
2271
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2272
- try opt_buffer.drawTextBuffer(view, 0, 0);
2273
-
2274
- // Actual rendering shows: " y * 2; }"
2275
- // Source chars 30-38 are shown
2276
- // Position 0: ' ' (source 30) - white
2277
- // Position 5: '2' (source 35) - should be YELLOW (highlighted 35-36)
2278
-
2279
- const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
2280
- try std.testing.expectEqual(@as(u32, '2'), cell_5.char);
2281
- try std.testing.expect(@abs(cell_5.fg[0] - 1.0) < epsilon); // Yellow
2282
- try std.testing.expect(@abs(cell_5.fg[1] - 1.0) < epsilon);
2283
- try std.testing.expect(@abs(cell_5.fg[2] - 0.0) < epsilon);
2284
- }
2285
- }
2286
-
2287
- test "drawTextBuffer - syntax highlighting with horizontal viewport offset" {
2288
- const pool = gp.initGlobalPool(std.testing.allocator);
2289
- defer gp.deinitGlobalPool();
2290
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
2291
- defer link.deinitGlobalLinkPool();
2292
-
2293
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
2294
- defer tb.deinit();
2295
-
2296
- var view = try TextBufferView.init(std.testing.allocator, tb);
2297
- defer view.deinit();
2298
-
2299
- const style = try ss.SyntaxStyle.init(std.testing.allocator);
2300
- defer style.deinit();
2301
- tb.setSyntaxStyle(style);
2302
-
2303
- // Register a red style
2304
- const red_style_id = try style.registerStyle("keyword", RGBA{ 1.0, 0.0, 0.0, 1.0 }, null, 0);
2305
-
2306
- // Text: "const x = 1"
2307
- // Highlight "const" (characters 0-5) in red
2308
- try tb.setText("const x = 1");
2309
- try tb.addHighlightByCharRange(0, 5, red_style_id, 1, 0);
2310
-
2311
- // Set viewport to skip first 3 characters, showing "st x = 1"
2312
- view.setWrapMode(.none);
2313
- view.setWrapWidth(null);
2314
- view.setViewport(.{ .x = 3, .y = 0, .width = 10, .height = 1 });
2315
-
2316
- var opt_buffer = try OptimizedBuffer.init(
2317
- std.testing.allocator,
2318
- 10,
2319
- 1,
2320
- .{ .pool = pool, .width_method = .unicode },
2321
- );
2322
- defer opt_buffer.deinit();
2323
-
2324
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2325
- try opt_buffer.drawTextBuffer(view, 0, 0);
2326
-
2327
- const epsilon: f32 = 0.01;
2328
- const red_fg = RGBA{ 1.0, 0.0, 0.0, 1.0 };
2329
-
2330
- // Check that 's' at buffer position 0 is RED
2331
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
2332
- try std.testing.expectEqual(@as(u32, 's'), cell_0.char);
2333
- const is_red_0 = @abs(cell_0.fg[0] - red_fg[0]) < epsilon and
2334
- @abs(cell_0.fg[1] - red_fg[1]) < epsilon and
2335
- @abs(cell_0.fg[2] - red_fg[2]) < epsilon;
2336
- try std.testing.expect(is_red_0);
2337
-
2338
- // Check that 't' at buffer position 1 is RED
2339
- const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
2340
- try std.testing.expectEqual(@as(u32, 't'), cell_1.char);
2341
- const is_red_1 = @abs(cell_1.fg[0] - red_fg[0]) < epsilon and
2342
- @abs(cell_1.fg[1] - red_fg[1]) < epsilon and
2343
- @abs(cell_1.fg[2] - red_fg[2]) < epsilon;
2344
- try std.testing.expect(is_red_1);
2345
-
2346
- // Check that ' ' at buffer position 2 is NOT RED
2347
- const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
2348
- try std.testing.expectEqual(@as(u32, ' '), cell_2.char);
2349
- const is_red_2 = @abs(cell_2.fg[0] - red_fg[0]) < epsilon and
2350
- @abs(cell_2.fg[1] - red_fg[1]) < epsilon and
2351
- @abs(cell_2.fg[2] - red_fg[2]) < epsilon;
2352
- try std.testing.expect(!is_red_2);
2353
-
2354
- // Check that 'x' at buffer position 3 is NOT RED
2355
- const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
2356
- try std.testing.expectEqual(@as(u32, 'x'), cell_3.char);
2357
- const is_red_3 = @abs(cell_3.fg[0] - red_fg[0]) < epsilon and
2358
- @abs(cell_3.fg[1] - red_fg[1]) < epsilon and
2359
- @abs(cell_3.fg[2] - red_fg[2]) < epsilon;
2360
- try std.testing.expect(!is_red_3);
2361
- }
2362
-
2363
- test "drawTextBuffer - setStyledText with multiple colors and horizontal scrolling" {
2364
- const pool = gp.initGlobalPool(std.testing.allocator);
2365
- defer gp.deinitGlobalPool();
2366
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
2367
- defer link.deinitGlobalLinkPool();
2368
-
2369
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
2370
- defer tb.deinit();
2371
-
2372
- var view = try TextBufferView.init(std.testing.allocator, tb);
2373
- defer view.deinit();
2374
-
2375
- const style = try ss.SyntaxStyle.init(std.testing.allocator);
2376
- defer style.deinit();
2377
- tb.setSyntaxStyle(style);
2378
-
2379
- // Simulate what code renderable does with setStyledText
2380
- // Text will be: "const x = function(y) { return y * 2; }"
2381
- // But split into colored chunks like syntax highlighting
2382
-
2383
- const chunk1_text = "const";
2384
- const chunk2_text = " x = ";
2385
- const chunk3_text = "function";
2386
- const chunk4_text = "(y) { ";
2387
- const chunk5_text = "return";
2388
- const chunk6_text = " y * ";
2389
- const chunk7_text = "2";
2390
- const chunk8_text = "; }";
2391
-
2392
- const red_color = [4]f32{ 1.0, 0.0, 0.0, 1.0 };
2393
- const white_color = [4]f32{ 1.0, 1.0, 1.0, 1.0 };
2394
- const green_color = [4]f32{ 0.0, 1.0, 0.0, 1.0 };
2395
- const blue_color = [4]f32{ 0.0, 0.0, 1.0, 1.0 };
2396
- const yellow_color = [4]f32{ 1.0, 1.0, 0.0, 1.0 };
2397
-
2398
- const chunks = [_]StyledChunk{
2399
- .{ .text_ptr = chunk1_text.ptr, .text_len = chunk1_text.len, .fg_ptr = @ptrCast(&red_color), .bg_ptr = null, .attributes = 0 },
2400
- .{ .text_ptr = chunk2_text.ptr, .text_len = chunk2_text.len, .fg_ptr = @ptrCast(&white_color), .bg_ptr = null, .attributes = 0 },
2401
- .{ .text_ptr = chunk3_text.ptr, .text_len = chunk3_text.len, .fg_ptr = @ptrCast(&green_color), .bg_ptr = null, .attributes = 0 },
2402
- .{ .text_ptr = chunk4_text.ptr, .text_len = chunk4_text.len, .fg_ptr = @ptrCast(&white_color), .bg_ptr = null, .attributes = 0 },
2403
- .{ .text_ptr = chunk5_text.ptr, .text_len = chunk5_text.len, .fg_ptr = @ptrCast(&blue_color), .bg_ptr = null, .attributes = 0 },
2404
- .{ .text_ptr = chunk6_text.ptr, .text_len = chunk6_text.len, .fg_ptr = @ptrCast(&white_color), .bg_ptr = null, .attributes = 0 },
2405
- .{ .text_ptr = chunk7_text.ptr, .text_len = chunk7_text.len, .fg_ptr = @ptrCast(&yellow_color), .bg_ptr = null, .attributes = 0 },
2406
- .{ .text_ptr = chunk8_text.ptr, .text_len = chunk8_text.len, .fg_ptr = @ptrCast(&white_color), .bg_ptr = null, .attributes = 0 },
2407
- };
2408
-
2409
- try tb.setStyledText(&chunks);
2410
-
2411
- view.setWrapMode(.none);
2412
- view.setWrapWidth(null);
2413
-
2414
- const epsilon: f32 = 0.01;
2415
-
2416
- // Helper to check if color matches
2417
- const isRed = struct {
2418
- fn check(fg: RGBA, eps: f32) bool {
2419
- return @abs(fg[0] - 1.0) < eps and @abs(fg[1] - 0.0) < eps and @abs(fg[2] - 0.0) < eps;
2420
- }
2421
- }.check;
2422
-
2423
- const isGreen = struct {
2424
- fn check(fg: RGBA, eps: f32) bool {
2425
- return @abs(fg[0] - 0.0) < eps and @abs(fg[1] - 1.0) < eps and @abs(fg[2] - 0.0) < eps;
2426
- }
2427
- }.check;
2428
-
2429
- const isBlue = struct {
2430
- fn check(fg: RGBA, eps: f32) bool {
2431
- return @abs(fg[0] - 0.0) < eps and @abs(fg[1] - 0.0) < eps and @abs(fg[2] - 1.0) < eps;
2432
- }
2433
- }.check;
2434
-
2435
- const isYellow = struct {
2436
- fn check(fg: RGBA, eps: f32) bool {
2437
- return @abs(fg[0] - 1.0) < eps and @abs(fg[1] - 1.0) < eps and @abs(fg[2] - 0.0) < eps;
2438
- }
2439
- }.check;
2440
-
2441
- const isWhite = struct {
2442
- fn check(fg: RGBA, eps: f32) bool {
2443
- return @abs(fg[0] - 1.0) < eps and @abs(fg[1] - 1.0) < eps and @abs(fg[2] - 1.0) < eps;
2444
- }
2445
- }.check;
2446
-
2447
- // Test at x=0 (no scroll)
2448
- {
2449
- view.setViewport(.{ .x = 0, .y = 0, .width = 40, .height = 1 });
2450
- var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 40, 1, .{ .pool = pool, .width_method = .unicode });
2451
- defer opt_buffer.deinit();
2452
-
2453
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2454
- try opt_buffer.drawTextBuffer(view, 0, 0);
2455
-
2456
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable; // 'c' from "const"
2457
- try std.testing.expectEqual(@as(u32, 'c'), cell_0.char);
2458
- try std.testing.expect(isRed(cell_0.fg, epsilon));
2459
-
2460
- const cell_10 = opt_buffer.get(10, 0) orelse unreachable; // 'f' from "function"
2461
- try std.testing.expectEqual(@as(u32, 'f'), cell_10.char);
2462
- try std.testing.expect(isGreen(cell_10.fg, epsilon));
2463
- }
2464
-
2465
- // Test at x=5 (scrolled past "const")
2466
- {
2467
- view.setViewport(.{ .x = 5, .y = 0, .width = 20, .height = 1 });
2468
- var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 20, 1, .{ .pool = pool, .width_method = .unicode });
2469
- defer opt_buffer.deinit();
2470
-
2471
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2472
- try opt_buffer.drawTextBuffer(view, 0, 0);
2473
-
2474
- // At x=5, showing chars 5-24: " x = function(y) { "
2475
- // Position 0: ' ' (source 5) - should be white
2476
- // Position 5: 'f' (source 10) - should be GREEN
2477
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
2478
- try std.testing.expectEqual(@as(u32, ' '), cell_0.char);
2479
- try std.testing.expect(isWhite(cell_0.fg, epsilon));
2480
-
2481
- const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
2482
- try std.testing.expectEqual(@as(u32, 'f'), cell_5.char);
2483
- try std.testing.expect(isGreen(cell_5.fg, epsilon));
2484
- }
2485
-
2486
- // Test at x=15 (in middle of "function")
2487
- {
2488
- view.setViewport(.{ .x = 15, .y = 0, .width = 20, .height = 1 });
2489
- var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 20, 1, .{ .pool = pool, .width_method = .unicode });
2490
- defer opt_buffer.deinit();
2491
-
2492
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2493
- try opt_buffer.drawTextBuffer(view, 0, 0);
2494
-
2495
- // At x=15, showing chars 15-34: "ion(y) { return y * "
2496
- // "const x = function..."
2497
- // 0123456789012345678...
2498
- // Position 0: 'i' (source 15) - should be GREEN (part of "function" 10-18)
2499
- // Position 1: 'o' (source 16) - should be GREEN
2500
- // Position 2: 'n' (source 17) - should be GREEN
2501
- // Position 3: '(' (source 18) - should be WHITE (end of "function")
2502
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
2503
- try std.testing.expectEqual(@as(u32, 'i'), cell_0.char);
2504
- try std.testing.expect(isGreen(cell_0.fg, epsilon));
2505
-
2506
- const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
2507
- try std.testing.expectEqual(@as(u32, 'o'), cell_1.char);
2508
- try std.testing.expect(isGreen(cell_1.fg, epsilon));
2509
-
2510
- const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
2511
- try std.testing.expectEqual(@as(u32, 'n'), cell_2.char);
2512
- try std.testing.expect(isGreen(cell_2.fg, epsilon));
2513
-
2514
- const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
2515
- try std.testing.expectEqual(@as(u32, '('), cell_3.char);
2516
- try std.testing.expect(isWhite(cell_3.fg, epsilon));
2517
-
2518
- // Position 9: 'r' (source 24) - should be BLUE (start of "return" 24-30)
2519
- const cell_9 = opt_buffer.get(9, 0) orelse unreachable;
2520
- try std.testing.expectEqual(@as(u32, 'r'), cell_9.char);
2521
- try std.testing.expect(isBlue(cell_9.fg, epsilon));
2522
- }
2523
-
2524
- // Test at x=25 (past "return")
2525
- {
2526
- view.setViewport(.{ .x = 25, .y = 0, .width = 20, .height = 1 });
2527
- var opt_buffer = try OptimizedBuffer.init(std.testing.allocator, 20, 1, .{ .pool = pool, .width_method = .unicode });
2528
- defer opt_buffer.deinit();
2529
-
2530
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2531
- try opt_buffer.drawTextBuffer(view, 0, 0);
2532
-
2533
- // At x=25, showing chars 25-44: "eturn y * 2; }"
2534
- // Position 0: 'e' (source 25) - should be BLUE (part of "return" 24-30)
2535
- // Position 4: 'n' (source 29) - should be BLUE
2536
- // Position 5: ' ' (source 30) - should be WHITE (end of "return")
2537
- // Position 10: '2' (source 35) - should be YELLOW
2538
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
2539
- try std.testing.expectEqual(@as(u32, 'e'), cell_0.char);
2540
- try std.testing.expect(isBlue(cell_0.fg, epsilon));
2541
-
2542
- const cell_4 = opt_buffer.get(4, 0) orelse unreachable;
2543
- try std.testing.expectEqual(@as(u32, 'n'), cell_4.char);
2544
- try std.testing.expect(isBlue(cell_4.fg, epsilon));
2545
-
2546
- const cell_5 = opt_buffer.get(5, 0) orelse unreachable;
2547
- try std.testing.expectEqual(@as(u32, ' '), cell_5.char);
2548
- try std.testing.expect(isWhite(cell_5.fg, epsilon));
2549
-
2550
- const cell_10 = opt_buffer.get(10, 0) orelse unreachable;
2551
- try std.testing.expectEqual(@as(u32, '2'), cell_10.char);
2552
- try std.testing.expect(isYellow(cell_10.fg, epsilon));
2553
- }
2554
- }
2555
-
2556
- test "drawTextBuffer - selection with horizontal viewport offset" {
2557
- const pool = gp.initGlobalPool(std.testing.allocator);
2558
- defer gp.deinitGlobalPool();
2559
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
2560
- defer link.deinitGlobalLinkPool();
2561
-
2562
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
2563
- defer tb.deinit();
2564
-
2565
- var view = try TextBufferView.init(std.testing.allocator, tb);
2566
- defer view.deinit();
2567
-
2568
- // Text: "0123456789ABCDEFGHIJ"
2569
- // We'll set viewport to x=5, showing "56789ABCDE"
2570
- // Then we'll select characters 7-12 (which are "789AB")
2571
- // Expected: in the rendered buffer, "789AB" should be highlighted
2572
- try tb.setText("0123456789ABCDEFGHIJ");
2573
-
2574
- view.setWrapMode(.none);
2575
- view.setWrapWidth(null);
2576
- view.setViewport(.{ .x = 5, .y = 0, .width = 10, .height = 1 });
2577
-
2578
- // Select characters at positions 7-12 in the original text ("789AB")
2579
- view.setSelection(7, 12, RGBA{ 1.0, 1.0, 0.0, 1.0 }, RGBA{ 0.0, 0.0, 0.0, 1.0 });
2580
-
2581
- var opt_buffer = try OptimizedBuffer.init(
2582
- std.testing.allocator,
2583
- 10,
2584
- 1,
2585
- .{ .pool = pool, .width_method = .unicode },
2586
- );
2587
- defer opt_buffer.deinit();
2588
-
2589
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2590
- try opt_buffer.drawTextBuffer(view, 0, 0);
2591
-
2592
- // The viewport shows positions 5-14 of the text
2593
- // Characters 7-11 (0-indexed) should be highlighted
2594
- // In the buffer:
2595
- // Position 0: '5' - not highlighted
2596
- // Position 1: '6' - not highlighted
2597
- // Position 2: '7' - HIGHLIGHTED (char pos 7)
2598
- // Position 3: '8' - HIGHLIGHTED (char pos 8)
2599
- // Position 4: '9' - HIGHLIGHTED (char pos 9)
2600
- // Position 5: 'A' - HIGHLIGHTED (char pos 10)
2601
- // Position 6: 'B' - HIGHLIGHTED (char pos 11)
2602
- // Position 7: 'C' - not highlighted (char pos 12, selection end is exclusive)
2603
- // Position 8: 'D' - not highlighted
2604
- // Position 9: 'E' - not highlighted
2605
-
2606
- const epsilon: f32 = 0.01;
2607
- const yellow_bg = RGBA{ 1.0, 1.0, 0.0, 1.0 };
2608
-
2609
- // Check non-highlighted cells
2610
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
2611
- try std.testing.expectEqual(@as(u32, '5'), cell_0.char);
2612
- const has_yellow_0 = @abs(cell_0.bg[0] - yellow_bg[0]) < epsilon and
2613
- @abs(cell_0.bg[1] - yellow_bg[1]) < epsilon and
2614
- @abs(cell_0.bg[2] - yellow_bg[2]) < epsilon;
2615
- try std.testing.expect(!has_yellow_0);
2616
-
2617
- const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
2618
- try std.testing.expectEqual(@as(u32, '6'), cell_1.char);
2619
- const has_yellow_1 = @abs(cell_1.bg[0] - yellow_bg[0]) < epsilon and
2620
- @abs(cell_1.bg[1] - yellow_bg[1]) < epsilon and
2621
- @abs(cell_1.bg[2] - yellow_bg[2]) < epsilon;
2622
- try std.testing.expect(!has_yellow_1);
2623
-
2624
- // Check highlighted cells
2625
- const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
2626
- try std.testing.expectEqual(@as(u32, '7'), cell_2.char);
2627
- const has_yellow_2 = @abs(cell_2.bg[0] - yellow_bg[0]) < epsilon and
2628
- @abs(cell_2.bg[1] - yellow_bg[1]) < epsilon and
2629
- @abs(cell_2.bg[2] - yellow_bg[2]) < epsilon;
2630
- try std.testing.expect(has_yellow_2);
2631
-
2632
- const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
2633
- try std.testing.expectEqual(@as(u32, '8'), cell_3.char);
2634
- const has_yellow_3 = @abs(cell_3.bg[0] - yellow_bg[0]) < epsilon and
2635
- @abs(cell_3.bg[1] - yellow_bg[1]) < epsilon and
2636
- @abs(cell_3.bg[2] - yellow_bg[2]) < epsilon;
2637
- try std.testing.expect(has_yellow_3);
2638
-
2639
- const cell_6 = opt_buffer.get(6, 0) orelse unreachable;
2640
- try std.testing.expectEqual(@as(u32, 'B'), cell_6.char);
2641
- const has_yellow_6 = @abs(cell_6.bg[0] - yellow_bg[0]) < epsilon and
2642
- @abs(cell_6.bg[1] - yellow_bg[1]) < epsilon and
2643
- @abs(cell_6.bg[2] - yellow_bg[2]) < epsilon;
2644
- try std.testing.expect(has_yellow_6);
2645
-
2646
- // Check cells after selection
2647
- const cell_7 = opt_buffer.get(7, 0) orelse unreachable;
2648
- try std.testing.expectEqual(@as(u32, 'C'), cell_7.char);
2649
- const has_yellow_7 = @abs(cell_7.bg[0] - yellow_bg[0]) < epsilon and
2650
- @abs(cell_7.bg[1] - yellow_bg[1]) < epsilon and
2651
- @abs(cell_7.bg[2] - yellow_bg[2]) < epsilon;
2652
- try std.testing.expect(!has_yellow_7);
2653
- }
2654
-
2655
- test "drawTextBuffer - syntax highlight respects truncation" {
2656
- const pool = gp.initGlobalPool(std.testing.allocator);
2657
- defer gp.deinitGlobalPool();
2658
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
2659
- defer link.deinitGlobalLinkPool();
2660
-
2661
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
2662
- defer tb.deinit();
2663
-
2664
- var view = try TextBufferView.init(std.testing.allocator, tb);
2665
- defer view.deinit();
2666
-
2667
- const style = try ss.SyntaxStyle.init(std.testing.allocator);
2668
- defer style.deinit();
2669
- tb.setSyntaxStyle(style);
2670
-
2671
- const red_style = try style.registerStyle("red", RGBA{ 1.0, 0.0, 0.0, 1.0 }, null, 0);
2672
- const green_style = try style.registerStyle("green", RGBA{ 0.0, 1.0, 0.0, 1.0 }, null, 0);
2673
-
2674
- try tb.setText("0123456789ABCDEFGHIJ");
2675
- try tb.addHighlightByCharRange(4, 7, red_style, 1, 0); // highlight "456"
2676
- try tb.addHighlightByCharRange(16, 20, green_style, 1, 0); // highlight "GHIJ"
2677
-
2678
- view.setWrapMode(.none);
2679
- view.setWrapWidth(null);
2680
- view.setTruncate(true);
2681
- view.setViewport(.{ .x = 0, .y = 0, .width = 10, .height = 1 });
2682
-
2683
- var opt_buffer = try OptimizedBuffer.init(
2684
- std.testing.allocator,
2685
- 10,
2686
- 1,
2687
- .{ .pool = pool, .width_method = .unicode },
2688
- );
2689
- defer opt_buffer.deinit();
2690
-
2691
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2692
- try opt_buffer.drawTextBuffer(view, 0, 0);
2693
-
2694
- const epsilon: f32 = 0.01;
2695
-
2696
- const prefix_cell = opt_buffer.get(1, 0) orelse unreachable;
2697
- try std.testing.expectEqual(@as(u32, '1'), prefix_cell.char);
2698
- try std.testing.expect(@abs(prefix_cell.fg[0] - 1.0) < epsilon);
2699
- try std.testing.expect(@abs(prefix_cell.fg[1] - 1.0) < epsilon);
2700
- try std.testing.expect(@abs(prefix_cell.fg[2] - 1.0) < epsilon);
2701
-
2702
- const ellipsis_cell = opt_buffer.get(3, 0) orelse unreachable;
2703
- try std.testing.expectEqual(@as(u32, '.'), ellipsis_cell.char);
2704
- try std.testing.expect(@abs(ellipsis_cell.fg[0] - 1.0) < epsilon);
2705
- try std.testing.expect(@abs(ellipsis_cell.fg[1] - 1.0) < epsilon);
2706
- try std.testing.expect(@abs(ellipsis_cell.fg[2] - 1.0) < epsilon);
2707
-
2708
- const suffix_cell = opt_buffer.get(6, 0) orelse unreachable;
2709
- try std.testing.expectEqual(@as(u32, 'G'), suffix_cell.char);
2710
- try std.testing.expect(@abs(suffix_cell.fg[0] - 0.0) < epsilon);
2711
- try std.testing.expect(@abs(suffix_cell.fg[1] - 1.0) < epsilon);
2712
- try std.testing.expect(@abs(suffix_cell.fg[2] - 0.0) < epsilon);
2713
- }
2714
-
2715
- test "drawTextBuffer - highlight spanning ellipsis continues on suffix" {
2716
- const pool = gp.initGlobalPool(std.testing.allocator);
2717
- defer gp.deinitGlobalPool();
2718
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
2719
- defer link.deinitGlobalLinkPool();
2720
-
2721
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
2722
- defer tb.deinit();
2723
-
2724
- var view = try TextBufferView.init(std.testing.allocator, tb);
2725
- defer view.deinit();
2726
-
2727
- const style = try ss.SyntaxStyle.init(std.testing.allocator);
2728
- defer style.deinit();
2729
- tb.setSyntaxStyle(style);
2730
-
2731
- const magenta_style = try style.registerStyle("magenta", RGBA{ 1.0, 0.0, 1.0, 1.0 }, null, 0);
2732
- const green_style = try style.registerStyle("green", RGBA{ 0.0, 1.0, 0.0, 1.0 }, null, 0);
2733
-
2734
- try tb.setText("0123456789ABCDEFGHIJ");
2735
- try tb.addHighlightByCharRange(2, 18, magenta_style, 1, 0); // spans through ellipsis
2736
- try tb.addHighlightByCharRange(18, 20, green_style, 2, 0); // suffix highlight
2737
-
2738
- view.setWrapMode(.none);
2739
- view.setWrapWidth(null);
2740
- view.setTruncate(true);
2741
- view.setViewport(.{ .x = 0, .y = 0, .width = 10, .height = 1 });
2742
-
2743
- var opt_buffer = try OptimizedBuffer.init(
2744
- std.testing.allocator,
2745
- 10,
2746
- 1,
2747
- .{ .pool = pool, .width_method = .unicode },
2748
- );
2749
- defer opt_buffer.deinit();
2750
-
2751
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2752
- try opt_buffer.drawTextBuffer(view, 0, 0);
2753
-
2754
- const epsilon: f32 = 0.01;
2755
-
2756
- const ellipsis_cell = opt_buffer.get(3, 0) orelse unreachable;
2757
- try std.testing.expectEqual(@as(u32, '.'), ellipsis_cell.char);
2758
- try std.testing.expect(@abs(ellipsis_cell.fg[0] - 1.0) < epsilon);
2759
- try std.testing.expect(@abs(ellipsis_cell.fg[1] - 1.0) < epsilon);
2760
- try std.testing.expect(@abs(ellipsis_cell.fg[2] - 1.0) < epsilon);
2761
-
2762
- const suffix_magenta = opt_buffer.get(6, 0) orelse unreachable;
2763
- try std.testing.expectEqual(@as(u32, 'G'), suffix_magenta.char);
2764
- try std.testing.expect(@abs(suffix_magenta.fg[0] - 1.0) < epsilon);
2765
- try std.testing.expect(@abs(suffix_magenta.fg[1] - 0.0) < epsilon);
2766
- try std.testing.expect(@abs(suffix_magenta.fg[2] - 1.0) < epsilon);
2767
-
2768
- const suffix_green = opt_buffer.get(8, 0) orelse unreachable;
2769
- try std.testing.expectEqual(@as(u32, 'I'), suffix_green.char);
2770
- try std.testing.expect(@abs(suffix_green.fg[0] - 0.0) < epsilon);
2771
- try std.testing.expect(@abs(suffix_green.fg[1] - 1.0) < epsilon);
2772
- try std.testing.expect(@abs(suffix_green.fg[2] - 0.0) < epsilon);
2773
- }
2774
-
2775
- test "drawTextBuffer - selection respects truncation" {
2776
- const pool = gp.initGlobalPool(std.testing.allocator);
2777
- defer gp.deinitGlobalPool();
2778
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
2779
- defer link.deinitGlobalLinkPool();
2780
-
2781
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
2782
- defer tb.deinit();
2783
-
2784
- var view = try TextBufferView.init(std.testing.allocator, tb);
2785
- defer view.deinit();
2786
-
2787
- // Text: "0123456789ABCDEFGHIJ" (len 20)
2788
- // With width 10, truncation should render: "012...GHIJ"
2789
- try tb.setText("0123456789ABCDEFGHIJ");
2790
-
2791
- view.setWrapMode(.none);
2792
- view.setWrapWidth(null);
2793
- view.setTruncate(true);
2794
- view.setViewport(.{ .x = 0, .y = 0, .width = 10, .height = 1 });
2795
-
2796
- // Select across the ellipsis and suffix
2797
- view.setSelection(2, 19, RGBA{ 1.0, 1.0, 0.0, 1.0 }, RGBA{ 0.0, 0.0, 0.0, 1.0 });
2798
-
2799
- var opt_buffer = try OptimizedBuffer.init(
2800
- std.testing.allocator,
2801
- 10,
2802
- 1,
2803
- .{ .pool = pool, .width_method = .unicode },
2804
- );
2805
- defer opt_buffer.deinit();
2806
-
2807
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2808
- try opt_buffer.drawTextBuffer(view, 0, 0);
2809
-
2810
- const epsilon: f32 = 0.01;
2811
- const yellow_bg = RGBA{ 1.0, 1.0, 0.0, 1.0 };
2812
-
2813
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
2814
- try std.testing.expectEqual(@as(u32, '0'), cell_0.char);
2815
- const has_yellow_0 = @abs(cell_0.bg[0] - yellow_bg[0]) < epsilon and
2816
- @abs(cell_0.bg[1] - yellow_bg[1]) < epsilon and
2817
- @abs(cell_0.bg[2] - yellow_bg[2]) < epsilon;
2818
- try std.testing.expect(!has_yellow_0);
2819
-
2820
- const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
2821
- try std.testing.expectEqual(@as(u32, '.'), cell_3.char);
2822
- const has_yellow_3 = @abs(cell_3.bg[0] - yellow_bg[0]) < epsilon and
2823
- @abs(cell_3.bg[1] - yellow_bg[1]) < epsilon and
2824
- @abs(cell_3.bg[2] - yellow_bg[2]) < epsilon;
2825
- try std.testing.expect(has_yellow_3);
2826
-
2827
- const cell_6 = opt_buffer.get(6, 0) orelse unreachable;
2828
- try std.testing.expectEqual(@as(u32, 'G'), cell_6.char);
2829
- const has_yellow_6 = @abs(cell_6.bg[0] - yellow_bg[0]) < epsilon and
2830
- @abs(cell_6.bg[1] - yellow_bg[1]) < epsilon and
2831
- @abs(cell_6.bg[2] - yellow_bg[2]) < epsilon;
2832
- try std.testing.expect(has_yellow_6);
2833
-
2834
- const cell_8 = opt_buffer.get(8, 0) orelse unreachable;
2835
- try std.testing.expectEqual(@as(u32, 'I'), cell_8.char);
2836
- const has_yellow_8 = @abs(cell_8.bg[0] - yellow_bg[0]) < epsilon and
2837
- @abs(cell_8.bg[1] - yellow_bg[1]) < epsilon and
2838
- @abs(cell_8.bg[2] - yellow_bg[2]) < epsilon;
2839
- try std.testing.expect(has_yellow_8);
2840
-
2841
- const cell_9 = opt_buffer.get(9, 0) orelse unreachable;
2842
- try std.testing.expectEqual(@as(u32, 'J'), cell_9.char);
2843
- const has_yellow_9 = @abs(cell_9.bg[0] - yellow_bg[0]) < epsilon and
2844
- @abs(cell_9.bg[1] - yellow_bg[1]) < epsilon and
2845
- @abs(cell_9.bg[2] - yellow_bg[2]) < epsilon;
2846
- try std.testing.expect(!has_yellow_9);
2847
- }
2848
-
2849
- test "drawTextBuffer - truncation selection does not overshoot multiline" {
2850
- const pool = gp.initGlobalPool(std.testing.allocator);
2851
- defer gp.deinitGlobalPool();
2852
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
2853
- defer link.deinitGlobalLinkPool();
2854
-
2855
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
2856
- defer tb.deinit();
2857
-
2858
- var view = try TextBufferView.init(std.testing.allocator, tb);
2859
- defer view.deinit();
2860
-
2861
- try tb.setText(
2862
- "abcdefghijABCDEFGHIJ\n" ++
2863
- "klmnopqrstKLMNOPQRST",
2864
- );
2865
-
2866
- view.setWrapMode(.none);
2867
- view.setWrapWidth(null);
2868
- view.setTruncate(true);
2869
- view.setViewport(.{ .x = 0, .y = 0, .width = 10, .height = 2 });
2870
-
2871
- // Select from line 1 col 2 through line 2 col 5 (exclusive)
2872
- view.setSelection(2, 26, RGBA{ 1.0, 1.0, 0.0, 1.0 }, RGBA{ 0.0, 0.0, 0.0, 1.0 });
2873
-
2874
- var opt_buffer = try OptimizedBuffer.init(
2875
- std.testing.allocator,
2876
- 10,
2877
- 2,
2878
- .{ .pool = pool, .width_method = .unicode },
2879
- );
2880
- defer opt_buffer.deinit();
2881
-
2882
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2883
- try opt_buffer.drawTextBuffer(view, 0, 0);
2884
-
2885
- const epsilon: f32 = 0.01;
2886
- const yellow_bg = RGBA{ 1.0, 1.0, 0.0, 1.0 };
2887
-
2888
- const line2_cell_0 = opt_buffer.get(0, 1) orelse unreachable;
2889
- try std.testing.expectEqual(@as(u32, 'k'), line2_cell_0.char);
2890
- const has_yellow_line2_0 = @abs(line2_cell_0.bg[0] - yellow_bg[0]) < epsilon and
2891
- @abs(line2_cell_0.bg[1] - yellow_bg[1]) < epsilon and
2892
- @abs(line2_cell_0.bg[2] - yellow_bg[2]) < epsilon;
2893
- try std.testing.expect(has_yellow_line2_0);
2894
-
2895
- const line2_cell_2 = opt_buffer.get(2, 1) orelse unreachable;
2896
- try std.testing.expectEqual(@as(u32, 'm'), line2_cell_2.char);
2897
- const has_yellow_line2_2 = @abs(line2_cell_2.bg[0] - yellow_bg[0]) < epsilon and
2898
- @abs(line2_cell_2.bg[1] - yellow_bg[1]) < epsilon and
2899
- @abs(line2_cell_2.bg[2] - yellow_bg[2]) < epsilon;
2900
- try std.testing.expect(has_yellow_line2_2);
2901
-
2902
- const line2_cell_6 = opt_buffer.get(6, 1) orelse unreachable;
2903
- try std.testing.expectEqual(@as(u32, 'Q'), line2_cell_6.char);
2904
- const has_yellow_line2_6 = @abs(line2_cell_6.bg[0] - yellow_bg[0]) < epsilon and
2905
- @abs(line2_cell_6.bg[1] - yellow_bg[1]) < epsilon and
2906
- @abs(line2_cell_6.bg[2] - yellow_bg[2]) < epsilon;
2907
- try std.testing.expect(!has_yellow_line2_6);
2908
- }
2909
-
2910
- test "drawTextBuffer - Chinese text with wrapping no stray bytes" {
2911
- const pool = gp.initGlobalPool(std.testing.allocator);
2912
- defer gp.deinitGlobalPool();
2913
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
2914
- defer link.deinitGlobalLinkPool();
2915
-
2916
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
2917
- defer tb.deinit();
2918
-
2919
- var view = try TextBufferView.init(std.testing.allocator, tb);
2920
- defer view.deinit();
2921
-
2922
- const text =
2923
- \\前后端分离 - TypeScript逻辑 + Go TUI界面
2924
- \\组件化设计 - 基于tview的可复用组件
2925
- \\渐进式交互 - 逐步披露避免信息过载
2926
- \\智能上下文 - 基于项目状态动态生成问题
2927
- \\丰富的问题类型 - 支持6种不同的交互形式
2928
- \\完整的验证 - 实时输入验证和错误处理
2929
- ;
2930
-
2931
- try tb.setText(text);
2932
-
2933
- // Try word wrapping with a width that might split multibyte chars
2934
- view.setWrapMode(.word);
2935
- view.setWrapWidth(35);
2936
- view.updateVirtualLines();
2937
-
2938
- var opt_buffer = try OptimizedBuffer.init(
2939
- std.testing.allocator,
2940
- 40,
2941
- 20,
2942
- .{ .pool = pool, .width_method = .unicode },
2943
- );
2944
- defer opt_buffer.deinit();
2945
-
2946
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
2947
- try opt_buffer.drawTextBuffer(view, 0, 0);
2948
-
2949
- // Write the rendered buffer to check for stray bytes
2950
- var out_buffer: [2000]u8 = undefined;
2951
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
2952
- const result = out_buffer[0..written];
2953
-
2954
- // Verify the output is valid UTF-8
2955
- try std.testing.expect(std.unicode.utf8ValidateSlice(result));
2956
-
2957
- // Verify that the original text is contained in the output (with possible spaces/newlines from wrapping)
2958
- try std.testing.expect(std.mem.indexOf(u8, result, "完整的验证") != null);
2959
- try std.testing.expect(std.mem.indexOf(u8, result, "实时输入验证和错误处理") != null);
2960
-
2961
- // Check specific problematic line - should NOT contain stray bytes
2962
- // The line should be present correctly (possibly wrapped with spaces)
2963
- // But there should be NO stray å character or partial UTF-8 sequences
2964
- try std.testing.expect(std.mem.indexOf(u8, result, "å式") == null); // This should NOT appear
2965
- try std.testing.expect(std.mem.indexOf(u8, result, "å") == null); // No stray partial bytes
2966
-
2967
- // Verify the problematic characters appear correctly
2968
- try std.testing.expect(std.mem.indexOf(u8, result, "形式") != null);
2969
- }
2970
-
2971
- test "drawTextBuffer - Chinese text WITHOUT wrapping no duplicate chunks" {
2972
- const pool = gp.initGlobalPool(std.testing.allocator);
2973
- defer gp.deinitGlobalPool();
2974
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
2975
- defer link.deinitGlobalLinkPool();
2976
-
2977
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
2978
- defer tb.deinit();
2979
-
2980
- var view = try TextBufferView.init(std.testing.allocator, tb);
2981
- defer view.deinit();
2982
-
2983
- const text =
2984
- \\前后端分离 - TypeScript逻辑 + Go TUI界面
2985
- \\组件化设计 - 基于tview的可复用组件
2986
- \\渐进式交互 - 逐步披露避免信息过载
2987
- \\智能上下文 - 基于项目状态动态生成问题
2988
- \\丰富的问题类型 - 支持6种不同的交互形式
2989
- \\完整的验证 - 实时输入验证和错误处理
2990
- ;
2991
-
2992
- try tb.setText(text);
2993
-
2994
- // Word wrap mode but with wide width so nothing actually wraps
2995
- view.setWrapMode(.word);
2996
- view.setWrapWidth(80);
2997
- view.updateVirtualLines();
2998
-
2999
- const vlines = view.getVirtualLines();
3000
-
3001
- // Check each virtual line - should have exactly ONE chunk when width is large enough
3002
- for (vlines) |vline| {
3003
- // Each line should have exactly ONE chunk when not actually wrapping
3004
- try std.testing.expectEqual(@as(usize, 1), vline.chunks.items.len);
3005
- }
3006
-
3007
- var opt_buffer = try OptimizedBuffer.init(
3008
- std.testing.allocator,
3009
- 80,
3010
- 10,
3011
- .{ .pool = pool, .width_method = .unicode },
3012
- );
3013
- defer opt_buffer.deinit();
3014
-
3015
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
3016
- try opt_buffer.drawTextBuffer(view, 0, 0);
3017
-
3018
- // Write the rendered buffer
3019
- var out_buffer: [2000]u8 = undefined;
3020
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
3021
- const result = out_buffer[0..written];
3022
-
3023
- // Verify the output is valid UTF-8
3024
- try std.testing.expect(std.unicode.utf8ValidateSlice(result));
3025
-
3026
- // Should NOT contain stray bytes
3027
- try std.testing.expect(std.mem.indexOf(u8, result, "å") == null);
3028
-
3029
- // All text should be present
3030
- try std.testing.expect(std.mem.indexOf(u8, result, "完整的验证 - 实时输入验证和错误处理") != null);
3031
- }
3032
-
3033
- test "drawTextBuffer - Chinese text with CHAR wrapping no stray bytes" {
3034
- const pool = gp.initGlobalPool(std.testing.allocator);
3035
- defer gp.deinitGlobalPool();
3036
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
3037
- defer link.deinitGlobalLinkPool();
3038
-
3039
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
3040
- defer tb.deinit();
3041
-
3042
- var view = try TextBufferView.init(std.testing.allocator, tb);
3043
- defer view.deinit();
3044
-
3045
- const text =
3046
- \\前后端分离 - TypeScript逻辑 + Go TUI界面
3047
- \\组件化设计 - 基于tview的可复用组件
3048
- \\渐进式交互 - 逐步披露避免信息过载
3049
- \\智能上下文 - 基于项目状态动态生成问题
3050
- \\丰富的问题类型 - 支持6种不同的交互形式
3051
- \\完整的验证 - 实时输入验证和错误处理
3052
- ;
3053
-
3054
- try tb.setText(text);
3055
-
3056
- // Char wrapping with a width that might split multibyte chars
3057
- view.setWrapMode(.char);
3058
- view.setWrapWidth(35);
3059
- view.updateVirtualLines();
3060
-
3061
- var opt_buffer = try OptimizedBuffer.init(
3062
- std.testing.allocator,
3063
- 35,
3064
- 20,
3065
- .{ .pool = pool, .width_method = .unicode },
3066
- );
3067
- defer opt_buffer.deinit();
3068
-
3069
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
3070
- try opt_buffer.drawTextBuffer(view, 0, 0);
3071
-
3072
- // Write the rendered buffer to check for stray bytes
3073
- var out_buffer: [2000]u8 = undefined;
3074
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
3075
- const result = out_buffer[0..written];
3076
-
3077
- // Verify the output is valid UTF-8
3078
- try std.testing.expect(std.unicode.utf8ValidateSlice(result));
3079
-
3080
- // Should NOT contain stray bytes
3081
- try std.testing.expect(std.mem.indexOf(u8, result, "å") == null);
3082
-
3083
- // Verify the problematic characters appear correctly
3084
- try std.testing.expect(std.mem.indexOf(u8, result, "形式") != null);
3085
- try std.testing.expect(std.mem.indexOf(u8, result, "完整的验证") != null);
3086
- }
3087
-
3088
- test "drawTextBuffer - word wrap CJK mixed text without break points" {
3089
- const pool = gp.initGlobalPool(std.testing.allocator);
3090
- defer gp.deinitGlobalPool();
3091
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
3092
- defer link.deinitGlobalLinkPool();
3093
-
3094
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
3095
- defer tb.deinit();
3096
-
3097
- var view = try TextBufferView.init(std.testing.allocator, tb);
3098
- defer view.deinit();
3099
-
3100
- try tb.setText("한글,English,中文,日本語,混合,Test,測試,テスト,가나다,ABC,一二三,あいう,라마바,DEF,四五六,えおか");
3101
-
3102
- view.setWrapMode(.word);
3103
- view.setWrapWidth(20);
3104
- view.updateVirtualLines();
3105
-
3106
- var opt_buffer = try OptimizedBuffer.init(
3107
- std.testing.allocator,
3108
- 30,
3109
- 20,
3110
- .{ .pool = pool, .width_method = .unicode },
3111
- );
3112
- defer opt_buffer.deinit();
3113
-
3114
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
3115
- try opt_buffer.drawTextBuffer(view, 0, 0);
3116
-
3117
- var out_buffer: [1000]u8 = undefined;
3118
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
3119
- const result = out_buffer[0..written];
3120
-
3121
- const vlines = view.getVirtualLines();
3122
- try std.testing.expect(vlines.len > 1);
3123
-
3124
- var y: u32 = 0;
3125
- while (y < vlines.len) : (y += 1) {
3126
- const first_cell = opt_buffer.get(0, y);
3127
- if (first_cell) |cell| {
3128
- try std.testing.expect(!gp.isContinuationChar(cell.char));
3129
- }
3130
- }
3131
-
3132
- try std.testing.expect(std.unicode.utf8ValidateSlice(result));
3133
- }
3134
-
3135
- test "drawTextBuffer - word wrap CJK text preserves UTF-8 boundaries" {
3136
- const pool = gp.initGlobalPool(std.testing.allocator);
3137
- defer gp.deinitGlobalPool();
3138
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
3139
- defer link.deinitGlobalLinkPool();
3140
-
3141
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
3142
- defer tb.deinit();
3143
-
3144
- var view = try TextBufferView.init(std.testing.allocator, tb);
3145
- defer view.deinit();
3146
-
3147
- try tb.setText("한글,English,中文,日本語,混合,Test,測試,テスト,가나다,ABC,一二三,あいう,라마바,DEF,四五六,えおか");
3148
-
3149
- view.setWrapMode(.word);
3150
- view.setWrapWidth(20);
3151
- view.updateVirtualLines();
3152
-
3153
- var opt_buffer = try OptimizedBuffer.init(
3154
- std.testing.allocator,
3155
- 30,
3156
- 20,
3157
- .{ .pool = pool, .width_method = .unicode },
3158
- );
3159
- defer opt_buffer.deinit();
3160
-
3161
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
3162
- try opt_buffer.drawTextBuffer(view, 0, 0);
3163
-
3164
- var out_buffer: [1000]u8 = undefined;
3165
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
3166
- const result = out_buffer[0..written];
3167
-
3168
- try std.testing.expect(std.unicode.utf8ValidateSlice(result));
3169
- try std.testing.expect(std.mem.indexOf(u8, result, "ä") == null);
3170
-
3171
- var i: usize = 0;
3172
- while (i < result.len) : (i += 1) {
3173
- if (result[i] == 0xE4) {
3174
- if (i + 1 >= result.len) {
3175
- return error.TestFailed;
3176
- }
3177
- const next_byte = result[i + 1];
3178
- if (next_byte < 0x80 or next_byte > 0xBF) {
3179
- return error.TestFailed;
3180
- }
3181
- }
3182
- }
3183
-
3184
- const vlines = view.getVirtualLines();
3185
- var y: u32 = 0;
3186
- while (y < vlines.len) : (y += 1) {
3187
- const first_cell = opt_buffer.get(0, y);
3188
- if (first_cell) |cell| {
3189
- try std.testing.expect(!gp.isContinuationChar(cell.char));
3190
- }
3191
- }
3192
- }
3193
-
3194
- test "drawTextBuffer - Thai ว่ grapheme in quotes occupies one cell" {
3195
- const pool = gp.initGlobalPool(std.testing.allocator);
3196
- defer gp.deinitGlobalPool();
3197
- const link_pool = link.initGlobalLinkPool(std.testing.allocator);
3198
- defer link.deinitGlobalLinkPool();
3199
-
3200
- var tb = try TextBuffer.init(std.testing.allocator, pool, link_pool, .unicode);
3201
- defer tb.deinit();
3202
-
3203
- var view = try TextBufferView.init(std.testing.allocator, tb);
3204
- defer view.deinit();
3205
-
3206
- try tb.setText("\"ว่\"");
3207
-
3208
- var opt_buffer = try OptimizedBuffer.init(
3209
- std.testing.allocator,
3210
- 10,
3211
- 1,
3212
- .{ .pool = pool, .width_method = .unicode },
3213
- );
3214
- defer opt_buffer.deinit();
3215
-
3216
- try opt_buffer.clear(.{ 0.0, 0.0, 0.0, 1.0 }, 32);
3217
- try opt_buffer.drawTextBuffer(view, 0, 0);
3218
-
3219
- const cell_0 = opt_buffer.get(0, 0) orelse unreachable;
3220
- try std.testing.expectEqual(@as(u32, '"'), cell_0.char);
3221
-
3222
- const cell_1 = opt_buffer.get(1, 0) orelse unreachable;
3223
- try std.testing.expect(cell_1.char != ' ');
3224
- try std.testing.expect(cell_1.char != '"');
3225
-
3226
- const cell_2 = opt_buffer.get(2, 0) orelse unreachable;
3227
- try std.testing.expectEqual(@as(u32, '"'), cell_2.char);
3228
-
3229
- const cell_3 = opt_buffer.get(3, 0) orelse unreachable;
3230
- try std.testing.expectEqual(@as(u32, ' '), cell_3.char);
3231
-
3232
- var out_buffer: [100]u8 = undefined;
3233
- const written = try opt_buffer.writeResolvedChars(&out_buffer, false);
3234
- const result = out_buffer[0..written];
3235
-
3236
- try std.testing.expect(std.mem.indexOf(u8, result, "\"ว่\"") != null);
3237
- }