@xincli/opentui-core 0.4.6 → 0.4.7

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