@xincli/opentui-core 0.4.6

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 (476) hide show
  1. package/README.md +63 -0
  2. package/dev/keypress-debug-renderer.ts +148 -0
  3. package/dev/keypress-debug.ts +43 -0
  4. package/dev/print-env-vars.ts +32 -0
  5. package/dev/test-tmux-graphics-334.sh +68 -0
  6. package/dev/test-tmux-theme-queries.sh +151 -0
  7. package/dev/thai-debug-test.ts +68 -0
  8. package/dev/theme-mode.ts +290 -0
  9. package/docs/development.md +144 -0
  10. package/package.json +102 -0
  11. package/prebuilt/aarch64-android/libopentui.so +0 -0
  12. package/scripts/build-android.ts +226 -0
  13. package/scripts/build-native-termux.sh +525 -0
  14. package/scripts/build.ts +530 -0
  15. package/scripts/dist-test.ts +312 -0
  16. package/scripts/package-prebuilt.ts +110 -0
  17. package/scripts/publish.ts +60 -0
  18. package/scripts/test-node-hook.mjs +16 -0
  19. package/scripts/test-node.ts +241 -0
  20. package/scripts/vendor-deps.sh +73 -0
  21. package/src/NativeSpanFeed.ts +375 -0
  22. package/src/Renderable.ts +1878 -0
  23. package/src/__snapshots__/buffer.test.ts.snap +28 -0
  24. package/src/animation/Timeline.test.ts +2709 -0
  25. package/src/animation/Timeline.ts +598 -0
  26. package/src/ansi.ts +22 -0
  27. package/src/audio.ts +479 -0
  28. package/src/benchmark/attenuation-benchmark.ts +81 -0
  29. package/src/benchmark/audio-playback-benchmark.ts +344 -0
  30. package/src/benchmark/box-draw-benchmark.ts +1042 -0
  31. package/src/benchmark/colormatrix-benchmark.ts +123 -0
  32. package/src/benchmark/gain-benchmark.ts +80 -0
  33. package/src/benchmark/latest-all-bench-run.json +707 -0
  34. package/src/benchmark/latest-async-bench-run.json +336 -0
  35. package/src/benchmark/latest-default-bench-run.json +657 -0
  36. package/src/benchmark/latest-large-bench-run.json +707 -0
  37. package/src/benchmark/latest-quick-bench-run.json +207 -0
  38. package/src/benchmark/layout-benchmark.ts +2547 -0
  39. package/src/benchmark/markdown-benchmark.ts +1796 -0
  40. package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
  41. package/src/benchmark/native-span-feed-benchmark.md +56 -0
  42. package/src/benchmark/native-span-feed-benchmark.ts +596 -0
  43. package/src/benchmark/native-span-feed-compare.ts +280 -0
  44. package/src/benchmark/render-traversal-benchmark.ts +928 -0
  45. package/src/benchmark/text-buffer-render-benchmark.ts +874 -0
  46. package/src/benchmark/text-table-benchmark.ts +948 -0
  47. package/src/buffer.test.ts +292 -0
  48. package/src/buffer.ts +564 -0
  49. package/src/console.test.ts +629 -0
  50. package/src/console.ts +1253 -0
  51. package/src/edit-buffer.test.ts +1852 -0
  52. package/src/edit-buffer.ts +417 -0
  53. package/src/editor-view.test.ts +1056 -0
  54. package/src/editor-view.ts +290 -0
  55. package/src/index.ts +25 -0
  56. package/src/lib/KeyHandler.integration.test.ts +292 -0
  57. package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
  58. package/src/lib/KeyHandler.test.ts +662 -0
  59. package/src/lib/KeyHandler.ts +222 -0
  60. package/src/lib/RGBA.test.ts +60 -0
  61. package/src/lib/RGBA.ts +343 -0
  62. package/src/lib/ascii.font.ts +330 -0
  63. package/src/lib/border.test.ts +83 -0
  64. package/src/lib/border.ts +170 -0
  65. package/src/lib/bunfs.test.ts +27 -0
  66. package/src/lib/bunfs.ts +18 -0
  67. package/src/lib/clipboard.test.ts +63 -0
  68. package/src/lib/clipboard.ts +41 -0
  69. package/src/lib/clock.ts +35 -0
  70. package/src/lib/data-paths.test.ts +133 -0
  71. package/src/lib/data-paths.ts +109 -0
  72. package/src/lib/debounce.ts +106 -0
  73. package/src/lib/detect-links.test.ts +98 -0
  74. package/src/lib/detect-links.ts +56 -0
  75. package/src/lib/env.test.ts +228 -0
  76. package/src/lib/env.ts +209 -0
  77. package/src/lib/extmarks-history.ts +51 -0
  78. package/src/lib/extmarks-multiwidth.test.ts +324 -0
  79. package/src/lib/extmarks.test.ts +3547 -0
  80. package/src/lib/extmarks.ts +861 -0
  81. package/src/lib/fonts/block.json +405 -0
  82. package/src/lib/fonts/grid.json +265 -0
  83. package/src/lib/fonts/huge.json +741 -0
  84. package/src/lib/fonts/pallet.json +314 -0
  85. package/src/lib/fonts/shade.json +591 -0
  86. package/src/lib/fonts/slick.json +321 -0
  87. package/src/lib/fonts/tiny.json +69 -0
  88. package/src/lib/hast-styled-text.ts +59 -0
  89. package/src/lib/index.ts +22 -0
  90. package/src/lib/keybinding.internal.test.ts +342 -0
  91. package/src/lib/keybinding.internal.ts +182 -0
  92. package/src/lib/objects-in-viewport.test.ts +789 -0
  93. package/src/lib/objects-in-viewport.ts +153 -0
  94. package/src/lib/output.capture.ts +58 -0
  95. package/src/lib/parse.keypress-kitty.protocol.test.ts +347 -0
  96. package/src/lib/parse.keypress-kitty.test.ts +712 -0
  97. package/src/lib/parse.keypress-kitty.ts +470 -0
  98. package/src/lib/parse.keypress.test.ts +2020 -0
  99. package/src/lib/parse.keypress.ts +480 -0
  100. package/src/lib/parse.mouse.test.ts +552 -0
  101. package/src/lib/parse.mouse.ts +232 -0
  102. package/src/lib/paste.ts +18 -0
  103. package/src/lib/queue.ts +65 -0
  104. package/src/lib/render-geometry.ts +36 -0
  105. package/src/lib/renderable.validations.test.ts +87 -0
  106. package/src/lib/renderable.validations.ts +83 -0
  107. package/src/lib/scroll-acceleration.ts +98 -0
  108. package/src/lib/selection.ts +261 -0
  109. package/src/lib/singleton.ts +34 -0
  110. package/src/lib/stdin-parser.test.ts +2439 -0
  111. package/src/lib/stdin-parser.ts +2007 -0
  112. package/src/lib/styled-text.ts +178 -0
  113. package/src/lib/terminal-capability-detection.test.ts +226 -0
  114. package/src/lib/terminal-capability-detection.ts +98 -0
  115. package/src/lib/terminal-palette.test.ts +954 -0
  116. package/src/lib/terminal-palette.ts +441 -0
  117. package/src/lib/tree-sitter/assets/README.md +119 -0
  118. package/src/lib/tree-sitter/assets/javascript/highlights.scm +205 -0
  119. package/src/lib/tree-sitter/assets/javascript/tree-sitter-javascript.wasm +0 -0
  120. package/src/lib/tree-sitter/assets/markdown/highlights.scm +150 -0
  121. package/src/lib/tree-sitter/assets/markdown/injections.scm +27 -0
  122. package/src/lib/tree-sitter/assets/markdown/tree-sitter-markdown.wasm +0 -0
  123. package/src/lib/tree-sitter/assets/markdown_inline/highlights.scm +115 -0
  124. package/src/lib/tree-sitter/assets/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
  125. package/src/lib/tree-sitter/assets/typescript/highlights.scm +604 -0
  126. package/src/lib/tree-sitter/assets/typescript/tree-sitter-typescript.wasm +0 -0
  127. package/src/lib/tree-sitter/assets/update.ts +360 -0
  128. package/src/lib/tree-sitter/assets/zig/highlights.scm +284 -0
  129. package/src/lib/tree-sitter/assets/zig/tree-sitter-zig.wasm +0 -0
  130. package/src/lib/tree-sitter/assets.d.ts +9 -0
  131. package/src/lib/tree-sitter/cache.test.ts +291 -0
  132. package/src/lib/tree-sitter/client.test.ts +1434 -0
  133. package/src/lib/tree-sitter/client.ts +847 -0
  134. package/src/lib/tree-sitter/default-parsers.ts +133 -0
  135. package/src/lib/tree-sitter/download-utils.ts +148 -0
  136. package/src/lib/tree-sitter/index.ts +75 -0
  137. package/src/lib/tree-sitter/parser.worker.ts +1098 -0
  138. package/src/lib/tree-sitter/parsers-config.ts +81 -0
  139. package/src/lib/tree-sitter/resolve-ft.test.ts +55 -0
  140. package/src/lib/tree-sitter/resolve-ft.ts +189 -0
  141. package/src/lib/tree-sitter/types.ts +132 -0
  142. package/src/lib/tree-sitter/update-assets.ts +10 -0
  143. package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
  144. package/src/lib/tree-sitter-styled-text.ts +314 -0
  145. package/src/lib/validate-dir-name.ts +55 -0
  146. package/src/lib/yoga.options.test.ts +628 -0
  147. package/src/lib/yoga.options.ts +346 -0
  148. package/src/native-event-worker-repro.test.ts +48 -0
  149. package/src/native-event-worker-repro.worker.ts +9 -0
  150. package/src/native-handle.test.ts +221 -0
  151. package/src/platform/ffi.test.ts +516 -0
  152. package/src/platform/ffi.ts +653 -0
  153. package/src/platform/runtime.test.ts +52 -0
  154. package/src/platform/runtime.ts +121 -0
  155. package/src/platform/test.ts +45 -0
  156. package/src/platform/worker-handler-cleanup.fixture.ts +12 -0
  157. package/src/platform/worker-onmessage-startup.fixture.ts +10 -0
  158. package/src/platform/worker-startup.fixture.ts +9 -0
  159. package/src/platform/worker.node-test.ts +153 -0
  160. package/src/platform/worker.ts +425 -0
  161. package/src/plugins/core-slot.ts +579 -0
  162. package/src/plugins/registry.ts +402 -0
  163. package/src/plugins/types.ts +46 -0
  164. package/src/post/effects.ts +948 -0
  165. package/src/post/filters.ts +512 -0
  166. package/src/post/matrices.ts +288 -0
  167. package/src/renderables/ASCIIFont.ts +219 -0
  168. package/src/renderables/Box.test.ts +474 -0
  169. package/src/renderables/Box.ts +353 -0
  170. package/src/renderables/Code.test.ts +2388 -0
  171. package/src/renderables/Code.ts +555 -0
  172. package/src/renderables/Diff.regression.test.ts +381 -0
  173. package/src/renderables/Diff.test.ts +3212 -0
  174. package/src/renderables/Diff.ts +1278 -0
  175. package/src/renderables/EditBufferRenderable.test.ts +288 -0
  176. package/src/renderables/EditBufferRenderable.ts +1152 -0
  177. package/src/renderables/FrameBuffer.ts +47 -0
  178. package/src/renderables/Input.test.ts +1310 -0
  179. package/src/renderables/Input.ts +273 -0
  180. package/src/renderables/LineNumberRenderable.ts +725 -0
  181. package/src/renderables/Markdown.ts +2144 -0
  182. package/src/renderables/ScrollBar.ts +423 -0
  183. package/src/renderables/ScrollBox.ts +903 -0
  184. package/src/renderables/Select.test.ts +1134 -0
  185. package/src/renderables/Select.ts +535 -0
  186. package/src/renderables/Slider.test.ts +456 -0
  187. package/src/renderables/Slider.ts +342 -0
  188. package/src/renderables/TabSelect.test.ts +238 -0
  189. package/src/renderables/TabSelect.ts +447 -0
  190. package/src/renderables/Text.selection-buffer.test.ts +118 -0
  191. package/src/renderables/Text.test.ts +2662 -0
  192. package/src/renderables/Text.ts +147 -0
  193. package/src/renderables/TextBufferRenderable.ts +504 -0
  194. package/src/renderables/TextNode.test.ts +1137 -0
  195. package/src/renderables/TextNode.ts +379 -0
  196. package/src/renderables/TextTable.test.ts +1411 -0
  197. package/src/renderables/TextTable.ts +1434 -0
  198. package/src/renderables/Textarea.ts +425 -0
  199. package/src/renderables/TimeToFirstDraw.ts +89 -0
  200. package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
  201. package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
  202. package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
  203. package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
  204. package/src/renderables/__tests__/Code.test.ts +55 -0
  205. package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
  206. package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +827 -0
  207. package/src/renderables/__tests__/LineNumberRenderable.test.ts +1906 -0
  208. package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
  209. package/src/renderables/__tests__/Markdown.code-colors.test.ts +288 -0
  210. package/src/renderables/__tests__/Markdown.test.ts +4175 -0
  211. package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
  212. package/src/renderables/__tests__/Textarea.buffer.test.ts +683 -0
  213. package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
  214. package/src/renderables/__tests__/Textarea.editing.test.ts +2120 -0
  215. package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
  216. package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
  217. package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
  218. package/src/renderables/__tests__/Textarea.keybinding.test.ts +3275 -0
  219. package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
  220. package/src/renderables/__tests__/Textarea.rendering.test.ts +1927 -0
  221. package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
  222. package/src/renderables/__tests__/Textarea.selection.test.ts +1589 -0
  223. package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
  224. package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
  225. package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
  226. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
  227. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
  228. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
  229. package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
  230. package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
  231. package/src/renderables/__tests__/markdown-parser.test.ts +241 -0
  232. package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
  233. package/src/renderables/composition/README.md +8 -0
  234. package/src/renderables/composition/VRenderable.ts +32 -0
  235. package/src/renderables/composition/constructs.ts +127 -0
  236. package/src/renderables/composition/vnode.ts +277 -0
  237. package/src/renderables/index.ts +23 -0
  238. package/src/renderables/markdown-parser.ts +79 -0
  239. package/src/renderer-theme-mode.ts +199 -0
  240. package/src/renderer.ts +4975 -0
  241. package/src/runtime-plugin-support-configure.ts +63 -0
  242. package/src/runtime-plugin-support.ts +13 -0
  243. package/src/runtime-plugin.ts +626 -0
  244. package/src/specs/terminal-startup.md +64 -0
  245. package/src/syntax-style.test.ts +841 -0
  246. package/src/syntax-style.ts +274 -0
  247. package/src/testing/README.md +219 -0
  248. package/src/testing/bun-test-node.ts +880 -0
  249. package/src/testing/capture-spans.test.ts +216 -0
  250. package/src/testing/integration.test.ts +276 -0
  251. package/src/testing/manual-clock.ts +117 -0
  252. package/src/testing/mock-keys.test.ts +1378 -0
  253. package/src/testing/mock-keys.ts +457 -0
  254. package/src/testing/mock-mouse.test.ts +218 -0
  255. package/src/testing/mock-mouse.ts +247 -0
  256. package/src/testing/mock-tree-sitter-client.ts +81 -0
  257. package/src/testing/spy.ts +13 -0
  258. package/src/testing/terminal-capabilities.ts +47 -0
  259. package/src/testing/test-recorder.test.ts +410 -0
  260. package/src/testing/test-recorder.ts +133 -0
  261. package/src/testing/test-renderer.ts +375 -0
  262. package/src/testing/test-renderer.wait.test.ts +278 -0
  263. package/src/testing/test-streams.ts +31 -0
  264. package/src/testing.ts +9 -0
  265. package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
  266. package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
  267. package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
  268. package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
  269. package/src/tests/allocator-stats.test.ts +53 -0
  270. package/src/tests/audio.test.ts +303 -0
  271. package/src/tests/destroy-during-render.test.ts +200 -0
  272. package/src/tests/destroy-on-exit.fixture.ts +36 -0
  273. package/src/tests/destroy-on-exit.test.ts +62 -0
  274. package/src/tests/ffi-borrowed-pointer-callsites.test.ts +153 -0
  275. package/src/tests/hover-cursor.test.ts +98 -0
  276. package/src/tests/native-backed-measurement-lifecycle.test.ts +133 -0
  277. package/src/tests/native-backed-measurement-parity.test.ts +322 -0
  278. package/src/tests/native-span-feed-async.test.ts +173 -0
  279. package/src/tests/native-span-feed-close.test.ts +120 -0
  280. package/src/tests/native-span-feed-coverage.test.ts +227 -0
  281. package/src/tests/native-span-feed-edge-cases.test.ts +384 -0
  282. package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
  283. package/src/tests/opacity.test.ts +123 -0
  284. package/src/tests/renderable.snapshot.test.ts +524 -0
  285. package/src/tests/renderable.test.ts +1851 -0
  286. package/src/tests/renderer.clock.test.ts +270 -0
  287. package/src/tests/renderer.console-startup.test.ts +2151 -0
  288. package/src/tests/renderer.control.test.ts +458 -0
  289. package/src/tests/renderer.core-slot-binding.test.ts +962 -0
  290. package/src/tests/renderer.cursor.test.ts +26 -0
  291. package/src/tests/renderer.custom-stdout.test.ts +1112 -0
  292. package/src/tests/renderer.destroy-during-render.test.ts +147 -0
  293. package/src/tests/renderer.focus-restore.test.ts +257 -0
  294. package/src/tests/renderer.focus.test.ts +329 -0
  295. package/src/tests/renderer.idle.test.ts +219 -0
  296. package/src/tests/renderer.input.test.ts +2615 -0
  297. package/src/tests/renderer.kitty-flags.test.ts +195 -0
  298. package/src/tests/renderer.lifecycle.test.ts +34 -0
  299. package/src/tests/renderer.mouse.test.ts +1315 -0
  300. package/src/tests/renderer.notifications.test.ts +27 -0
  301. package/src/tests/renderer.palette.test.ts +1199 -0
  302. package/src/tests/renderer.remote.test.ts +247 -0
  303. package/src/tests/renderer.render-stats.test.ts +146 -0
  304. package/src/tests/renderer.scrollback-surface.test.ts +474 -0
  305. package/src/tests/renderer.selection.test.ts +138 -0
  306. package/src/tests/renderer.slot-registry.test.ts +684 -0
  307. package/src/tests/renderer.tracker.test.ts +202 -0
  308. package/src/tests/renderer.useMouse.test.ts +83 -0
  309. package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +76 -0
  310. package/src/tests/runtime-plugin-node-modules-import-like-string.fixture.ts +48 -0
  311. package/src/tests/runtime-plugin-node-modules-mjs.fixture.ts +43 -0
  312. package/src/tests/runtime-plugin-node-modules-no-bare-rewrite.fixture.ts +67 -0
  313. package/src/tests/runtime-plugin-node-modules-package-type-cache.fixture.ts +72 -0
  314. package/src/tests/runtime-plugin-node-modules-runtime-specifier.fixture.ts +44 -0
  315. package/src/tests/runtime-plugin-node-modules-scoped-package-bare-rewrite.fixture.ts +85 -0
  316. package/src/tests/runtime-plugin-path-alias.fixture.ts +43 -0
  317. package/src/tests/runtime-plugin-resolve-roots.fixture.ts +65 -0
  318. package/src/tests/runtime-plugin-support-configure.fixture.ts +31 -0
  319. package/src/tests/runtime-plugin-support-late-addition.fixture.ts +17 -0
  320. package/src/tests/runtime-plugin-support.fixture.ts +11 -0
  321. package/src/tests/runtime-plugin-support.test.ts +54 -0
  322. package/src/tests/runtime-plugin-windows-file-url.fixture.ts +30 -0
  323. package/src/tests/runtime-plugin.fixture.ts +40 -0
  324. package/src/tests/runtime-plugin.test.ts +369 -0
  325. package/src/tests/scrollbox-culling-bug.test.ts +114 -0
  326. package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
  327. package/src/tests/scrollbox-hitgrid.test.ts +909 -0
  328. package/src/tests/scrollbox.test.ts +1895 -0
  329. package/src/tests/wrap-resize-perf.test.ts +276 -0
  330. package/src/tests/yoga-api-coverage.test.ts +409 -0
  331. package/src/tests/yoga-callback-stress.test.ts +122 -0
  332. package/src/tests/yoga-ffi.test.ts +117 -0
  333. package/src/tests/yoga-setters.test.ts +921 -0
  334. package/src/tests/yoga-upstream/YGAlignBaselineTest.test.ts +124 -0
  335. package/src/tests/yoga-upstream/YGComputedBorderTest.test.ts +27 -0
  336. package/src/tests/yoga-upstream/YGComputedMarginTest.test.ts +27 -0
  337. package/src/tests/yoga-upstream/YGComputedPaddingTest.test.ts +27 -0
  338. package/src/tests/yoga-upstream/YGDirtiedTest.test.ts +157 -0
  339. package/src/tests/yoga-upstream/YGErrataTest.test.ts +40 -0
  340. package/src/tests/yoga-upstream/YGFlexBasisAuto.test.ts +23 -0
  341. package/src/tests/yoga-upstream/YGHasNewLayout.test.ts +81 -0
  342. package/src/tests/yoga-upstream/YGMeasureCacheTest.test.ts +31 -0
  343. package/src/tests/yoga-upstream/YGMeasureTest.test.ts +70 -0
  344. package/src/tests/yoga-upstream/generated/YGAbsolutePositionTest.test.ts +1864 -0
  345. package/src/tests/yoga-upstream/generated/YGAlignContentTest.test.ts +5829 -0
  346. package/src/tests/yoga-upstream/generated/YGAlignItemsTest.test.ts +2444 -0
  347. package/src/tests/yoga-upstream/generated/YGAlignSelfTest.test.ts +293 -0
  348. package/src/tests/yoga-upstream/generated/YGAndroidNewsFeed.test.ts +315 -0
  349. package/src/tests/yoga-upstream/generated/YGAspectRatioTest.test.ts +231 -0
  350. package/src/tests/yoga-upstream/generated/YGAutoTest.test.ts +347 -0
  351. package/src/tests/yoga-upstream/generated/YGBorderTest.test.ts +256 -0
  352. package/src/tests/yoga-upstream/generated/YGBoxSizingTest.test.ts +2747 -0
  353. package/src/tests/yoga-upstream/generated/YGDimensionTest.test.ts +129 -0
  354. package/src/tests/yoga-upstream/generated/YGDisplayContentsTest.test.ts +109 -0
  355. package/src/tests/yoga-upstream/generated/YGDisplayTest.test.ts +1097 -0
  356. package/src/tests/yoga-upstream/generated/YGFlexDirectionTest.test.ts +4562 -0
  357. package/src/tests/yoga-upstream/generated/YGFlexTest.test.ts +677 -0
  358. package/src/tests/yoga-upstream/generated/YGFlexWrapTest.test.ts +2071 -0
  359. package/src/tests/yoga-upstream/generated/YGGapTest.test.ts +3303 -0
  360. package/src/tests/yoga-upstream/generated/YGIntrinsicSizeTest.test.ts +582 -0
  361. package/src/tests/yoga-upstream/generated/YGJustifyContentTest.test.ts +2150 -0
  362. package/src/tests/yoga-upstream/generated/YGMarginTest.test.ts +1963 -0
  363. package/src/tests/yoga-upstream/generated/YGMinMaxDimensionTest.test.ts +1446 -0
  364. package/src/tests/yoga-upstream/generated/YGPaddingTest.test.ts +354 -0
  365. package/src/tests/yoga-upstream/generated/YGPercentageTest.test.ts +1760 -0
  366. package/src/tests/yoga-upstream/generated/YGRoundingTest.test.ts +1161 -0
  367. package/src/tests/yoga-upstream/generated/YGSizeOverflowTest.test.ts +207 -0
  368. package/src/tests/yoga-upstream/generated/YGStaticPositionTest.test.ts +6338 -0
  369. package/src/tests/yoga-upstream/tools/MeasureCounter.ts +54 -0
  370. package/src/tests/yoga-upstream/tools/utils.ts +81 -0
  371. package/src/text-buffer-view.test.ts +722 -0
  372. package/src/text-buffer-view.ts +193 -0
  373. package/src/text-buffer.test.ts +512 -0
  374. package/src/text-buffer.ts +250 -0
  375. package/src/types.ts +199 -0
  376. package/src/utils.ts +88 -0
  377. package/src/yoga.ts +1146 -0
  378. package/src/zig/ansi.zig +502 -0
  379. package/src/zig/audio.zig +1206 -0
  380. package/src/zig/bench/README.md +50 -0
  381. package/src/zig/bench/buffer-color-blending_bench.zig +350 -0
  382. package/src/zig/bench/buffer-draw-box_bench.zig +786 -0
  383. package/src/zig/bench/buffer-draw-text-buffer_bench.zig +894 -0
  384. package/src/zig/bench/edit-buffer_bench.zig +476 -0
  385. package/src/zig/bench/editor-view_bench.zig +169 -0
  386. package/src/zig/bench/native-span-feed_bench.zig +107 -0
  387. package/src/zig/bench/rope-markers_bench.zig +713 -0
  388. package/src/zig/bench/rope_bench.zig +514 -0
  389. package/src/zig/bench/styled-text_bench.zig +476 -0
  390. package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
  391. package/src/zig/bench/text-buffer-view_bench.zig +459 -0
  392. package/src/zig/bench/text-chunk-graphemes_bench.zig +272 -0
  393. package/src/zig/bench/utf8_bench.zig +799 -0
  394. package/src/zig/bench-utils.zig +431 -0
  395. package/src/zig/bench.zig +223 -0
  396. package/src/zig/buffer-methods.zig +215 -0
  397. package/src/zig/buffer.zig +2515 -0
  398. package/src/zig/build.zig +700 -0
  399. package/src/zig/build.zig.zon +19 -0
  400. package/src/zig/edit-buffer.zig +900 -0
  401. package/src/zig/editor-view.zig +812 -0
  402. package/src/zig/event-bus.zig +28 -0
  403. package/src/zig/event-emitter.zig +65 -0
  404. package/src/zig/file-logger.zig +92 -0
  405. package/src/zig/grapheme.zig +838 -0
  406. package/src/zig/handles.zig +267 -0
  407. package/src/zig/lib.zig +2813 -0
  408. package/src/zig/link.zig +349 -0
  409. package/src/zig/logger.zig +43 -0
  410. package/src/zig/mem-registry.zig +126 -0
  411. package/src/zig/miniaudio_shim.c +21 -0
  412. package/src/zig/native-renderable.zig +112 -0
  413. package/src/zig/native-span-feed-bench-lib.zig +7 -0
  414. package/src/zig/native-span-feed.zig +747 -0
  415. package/src/zig/renderer-output.zig +677 -0
  416. package/src/zig/renderer.zig +2106 -0
  417. package/src/zig/rope.zig +1220 -0
  418. package/src/zig/split-scrollback.zig +88 -0
  419. package/src/zig/syntax-style.zig +174 -0
  420. package/src/zig/terminal.zig +1652 -0
  421. package/src/zig/test.zig +82 -0
  422. package/src/zig/tests/README.md +18 -0
  423. package/src/zig/tests/ansi_test.zig +18 -0
  424. package/src/zig/tests/audio_test.zig +439 -0
  425. package/src/zig/tests/buffer-methods_test.zig +1111 -0
  426. package/src/zig/tests/buffer_test.zig +2926 -0
  427. package/src/zig/tests/edit-buffer-history_test.zig +299 -0
  428. package/src/zig/tests/edit-buffer_test.zig +1688 -0
  429. package/src/zig/tests/editor-view_test.zig +3454 -0
  430. package/src/zig/tests/event-emitter_test.zig +249 -0
  431. package/src/zig/tests/grapheme_test.zig +1329 -0
  432. package/src/zig/tests/handles_test.zig +123 -0
  433. package/src/zig/tests/link_test.zig +220 -0
  434. package/src/zig/tests/mem-registry_test.zig +473 -0
  435. package/src/zig/tests/memory_leak_regression_test.zig +234 -0
  436. package/src/zig/tests/native-renderable_test.zig +16 -0
  437. package/src/zig/tests/native-span-feed_test.zig +1266 -0
  438. package/src/zig/tests/renderer_test.zig +2604 -0
  439. package/src/zig/tests/rope-nested_test.zig +712 -0
  440. package/src/zig/tests/rope_fuzz_test.zig +238 -0
  441. package/src/zig/tests/rope_test.zig +2362 -0
  442. package/src/zig/tests/segment-merge.test.zig +148 -0
  443. package/src/zig/tests/split-scrollback_test.zig +70 -0
  444. package/src/zig/tests/syntax-style_test.zig +558 -0
  445. package/src/zig/tests/terminal_test.zig +1489 -0
  446. package/src/zig/tests/test-renderer.zig +126 -0
  447. package/src/zig/tests/text-buffer-drawing_test.zig +3291 -0
  448. package/src/zig/tests/text-buffer-highlights_test.zig +706 -0
  449. package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
  450. package/src/zig/tests/text-buffer-segment_test.zig +320 -0
  451. package/src/zig/tests/text-buffer-selection_test.zig +1056 -0
  452. package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
  453. package/src/zig/tests/text-buffer-view_test.zig +3672 -0
  454. package/src/zig/tests/text-buffer_test.zig +2191 -0
  455. package/src/zig/tests/unicode-width-map.zon +3909 -0
  456. package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
  457. package/src/zig/tests/utf8_test.zig +4060 -0
  458. package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
  459. package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
  460. package/src/zig/tests/word-wrap-editing_test.zig +498 -0
  461. package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
  462. package/src/zig/tests/yoga_test.zig +65 -0
  463. package/src/zig/text-buffer-iterators.zig +496 -0
  464. package/src/zig/text-buffer-segment.zig +403 -0
  465. package/src/zig/text-buffer-view.zig +1428 -0
  466. package/src/zig/text-buffer.zig +1272 -0
  467. package/src/zig/utf8.zig +1954 -0
  468. package/src/zig/utils.zig +12 -0
  469. package/src/zig/uucode-stub.zig +76 -0
  470. package/src/zig/vendor/miniaudio/miniaudio.h +93468 -0
  471. package/src/zig/yoga.zig +658 -0
  472. package/src/zig-structs.ts +381 -0
  473. package/src/zig.ts +5260 -0
  474. package/tsconfig.build.json +17 -0
  475. package/tsconfig.json +27 -0
  476. package/tsconfig.node-test.json +138 -0
@@ -0,0 +1,683 @@
1
+ import { describe, expect, it, beforeEach, afterEach } from "bun:test"
2
+ import { createTestRenderer, type TestRenderer, type MockInput } from "../../testing/test-renderer.js"
3
+ import { stringWidth } from "../../platform/runtime.js"
4
+ import { createTextareaRenderable } from "./renderable-test-utils.js"
5
+
6
+ let currentRenderer: TestRenderer
7
+ let renderOnce: () => Promise<void>
8
+ let currentMockInput: MockInput
9
+
10
+ describe("Textarea - Buffer Tests", () => {
11
+ beforeEach(async () => {
12
+ ;({
13
+ renderer: currentRenderer,
14
+ renderOnce,
15
+ mockInput: currentMockInput,
16
+ } = await createTestRenderer({
17
+ width: 80,
18
+ height: 24,
19
+ }))
20
+ })
21
+
22
+ afterEach(() => {
23
+ currentRenderer.destroy()
24
+ })
25
+
26
+ describe("getTextRange", () => {
27
+ it("should get text range by display-width offsets", async () => {
28
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
29
+ initialValue: "Hello, World!\nThis is line 2.",
30
+ width: 40,
31
+ height: 10,
32
+ })
33
+
34
+ const range1 = editor.getTextRange(0, 5)
35
+ expect(range1).toBe("Hello")
36
+
37
+ const range2 = editor.getTextRange(7, 12)
38
+ expect(range2).toBe("World")
39
+
40
+ const range3 = editor.getTextRange(0, 13)
41
+ expect(range3).toBe("Hello, World!")
42
+
43
+ const range4 = editor.getTextRange(14, 21)
44
+ expect(range4).toBe("This is")
45
+ })
46
+
47
+ it("should get text range by row/col coordinates", async () => {
48
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
49
+ initialValue: "Hello, World!\nThis is line 2.",
50
+ width: 40,
51
+ height: 10,
52
+ })
53
+
54
+ const range1 = editor.getTextRangeByCoords(0, 0, 0, 5)
55
+ expect(range1).toBe("Hello")
56
+
57
+ const range2 = editor.getTextRangeByCoords(0, 7, 0, 12)
58
+ expect(range2).toBe("World")
59
+
60
+ const range3 = editor.getTextRangeByCoords(1, 0, 1, 7)
61
+ expect(range3).toBe("This is")
62
+
63
+ const range4 = editor.getTextRangeByCoords(0, 0, 1, 7)
64
+ expect(range4).toBe("Hello, World!\nThis is")
65
+ })
66
+
67
+ it("should handle empty ranges with getTextRangeByCoords", async () => {
68
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
69
+ initialValue: "Hello, World!",
70
+ width: 40,
71
+ height: 10,
72
+ })
73
+
74
+ const rangeEmpty = editor.getTextRangeByCoords(0, 5, 0, 5)
75
+ expect(rangeEmpty).toBe("")
76
+
77
+ const rangeInvalid = editor.getTextRangeByCoords(0, 10, 0, 5)
78
+ expect(rangeInvalid).toBe("")
79
+ })
80
+
81
+ it("should handle ranges spanning multiple lines with getTextRangeByCoords", async () => {
82
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
83
+ initialValue: "Line 1\nLine 2\nLine 3",
84
+ width: 40,
85
+ height: 10,
86
+ })
87
+
88
+ const range1 = editor.getTextRangeByCoords(0, 5, 1, 4)
89
+ expect(range1).toBe("1\nLine")
90
+
91
+ const range2 = editor.getTextRangeByCoords(0, 0, 2, 6)
92
+ expect(range2).toBe("Line 1\nLine 2\nLine 3")
93
+
94
+ const range3 = editor.getTextRangeByCoords(1, 0, 2, 6)
95
+ expect(range3).toBe("Line 2\nLine 3")
96
+ })
97
+
98
+ it("should handle Unicode characters with getTextRangeByCoords", async () => {
99
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
100
+ initialValue: "Hello 🌟 World",
101
+ width: 40,
102
+ height: 10,
103
+ })
104
+
105
+ const range1 = editor.getTextRangeByCoords(0, 0, 0, 6)
106
+ expect(range1).toBe("Hello ")
107
+
108
+ const range2 = editor.getTextRangeByCoords(0, 6, 0, 8)
109
+ expect(range2).toBe("🌟")
110
+
111
+ const range3 = editor.getTextRangeByCoords(0, 8, 0, 14)
112
+ expect(range3).toBe(" World")
113
+ })
114
+
115
+ it("should handle CJK characters with getTextRangeByCoords", async () => {
116
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
117
+ initialValue: "Hello 世界",
118
+ width: 40,
119
+ height: 10,
120
+ })
121
+
122
+ const range1 = editor.getTextRangeByCoords(0, 0, 0, 6)
123
+ expect(range1).toBe("Hello ")
124
+
125
+ const range2 = editor.getTextRangeByCoords(0, 6, 0, 10)
126
+ expect(range2).toBe("世界")
127
+ })
128
+
129
+ it("should get text range by coords after editing operations", async () => {
130
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
131
+ initialValue: "ABC\nDEF",
132
+ width: 40,
133
+ height: 10,
134
+ })
135
+
136
+ editor.focus()
137
+
138
+ const range1 = editor.getTextRangeByCoords(0, 0, 1, 3)
139
+ expect(range1).toBe("ABC\nDEF")
140
+
141
+ editor.gotoLine(1)
142
+ currentMockInput.pressBackspace()
143
+ expect(editor.plainText).toBe("ABCDEF")
144
+
145
+ const range2 = editor.getTextRangeByCoords(0, 1, 0, 5)
146
+ expect(range2).toBe("BCDE")
147
+
148
+ const range3 = editor.getTextRangeByCoords(0, 0, 0, 6)
149
+ expect(range3).toBe("ABCDEF")
150
+ })
151
+
152
+ it("should handle out-of-bounds coordinates with getTextRangeByCoords", async () => {
153
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
154
+ initialValue: "Short",
155
+ width: 40,
156
+ height: 10,
157
+ })
158
+
159
+ const range1 = editor.getTextRangeByCoords(10, 0, 20, 0)
160
+ expect(range1).toBe("")
161
+
162
+ const range2 = editor.getTextRangeByCoords(0, 0, 0, 5)
163
+ expect(range2).toBe("Short")
164
+
165
+ const range3 = editor.getTextRangeByCoords(0, 100, 0, 200)
166
+ expect(range3).toBe("")
167
+ })
168
+
169
+ it("should match offset-based and coords-based methods", async () => {
170
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
171
+ initialValue: "Line 1\nLine 2\nLine 3",
172
+ width: 40,
173
+ height: 10,
174
+ })
175
+
176
+ const offsetBased = editor.getTextRange(0, 6)
177
+ const coordsBased = editor.getTextRangeByCoords(0, 0, 0, 6)
178
+ expect(coordsBased).toBe(offsetBased)
179
+ expect(coordsBased).toBe("Line 1")
180
+
181
+ const offsetBased2 = editor.getTextRange(7, 13)
182
+ const coordsBased2 = editor.getTextRangeByCoords(1, 0, 1, 6)
183
+ expect(coordsBased2).toBe(offsetBased2)
184
+ expect(coordsBased2).toBe("Line 2")
185
+
186
+ const offsetBased3 = editor.getTextRange(5, 12)
187
+ const coordsBased3 = editor.getTextRangeByCoords(0, 5, 1, 5)
188
+ expect(coordsBased3).toBe(offsetBased3)
189
+ expect(coordsBased3).toBe("1\nLine ")
190
+ })
191
+
192
+ it("should handle empty ranges", async () => {
193
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
194
+ initialValue: "Hello, World!",
195
+ width: 40,
196
+ height: 10,
197
+ })
198
+
199
+ const rangeEmpty = editor.getTextRange(5, 5)
200
+ expect(rangeEmpty).toBe("")
201
+
202
+ const rangeInvalid = editor.getTextRange(10, 5)
203
+ expect(rangeInvalid).toBe("")
204
+ })
205
+
206
+ it("should handle ranges spanning multiple lines", async () => {
207
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
208
+ initialValue: "Line 1\nLine 2\nLine 3",
209
+ width: 40,
210
+ height: 10,
211
+ })
212
+
213
+ const range1 = editor.getTextRange(0, 13)
214
+ expect(range1).toBe("Line 1\nLine 2")
215
+
216
+ const range2 = editor.getTextRange(5, 12)
217
+ expect(range2).toBe("1\nLine ")
218
+ })
219
+
220
+ it("should handle Unicode characters in ranges", async () => {
221
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
222
+ initialValue: "Hello 🌟 World",
223
+ width: 40,
224
+ height: 10,
225
+ })
226
+
227
+ const range1 = editor.getTextRange(0, 6)
228
+ expect(range1).toBe("Hello ")
229
+
230
+ const range2 = editor.getTextRange(6, 8)
231
+ expect(range2).toBe("🌟")
232
+
233
+ const range3 = editor.getTextRange(8, 14)
234
+ expect(range3).toBe(" World")
235
+ })
236
+
237
+ it("should handle CJK characters in ranges", async () => {
238
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
239
+ initialValue: "Hello 世界",
240
+ width: 40,
241
+ height: 10,
242
+ })
243
+
244
+ const range1 = editor.getTextRange(0, 6)
245
+ expect(range1).toBe("Hello ")
246
+
247
+ const range2 = editor.getTextRange(6, 10)
248
+ expect(range2).toBe("世界")
249
+ })
250
+
251
+ it("should get text range after editing operations", async () => {
252
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
253
+ initialValue: "ABC",
254
+ width: 40,
255
+ height: 10,
256
+ })
257
+
258
+ editor.focus()
259
+
260
+ editor.gotoLine(9999)
261
+ editor.insertText("DEF")
262
+ expect(editor.plainText).toBe("ABCDEF")
263
+
264
+ const range1 = editor.getTextRange(0, 6)
265
+ expect(range1).toBe("ABCDEF")
266
+
267
+ const range2 = editor.getTextRange(0, 3)
268
+ expect(range2).toBe("ABC")
269
+
270
+ const range3 = editor.getTextRange(3, 6)
271
+ expect(range3).toBe("DEF")
272
+ })
273
+
274
+ it("should get text range across chunk boundaries after line joins", async () => {
275
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
276
+ initialValue: "ABC\nDEF",
277
+ width: 40,
278
+ height: 10,
279
+ })
280
+
281
+ editor.focus()
282
+ editor.gotoLine(1)
283
+
284
+ currentMockInput.pressBackspace()
285
+ expect(editor.plainText).toBe("ABCDEF")
286
+
287
+ const range1 = editor.getTextRange(1, 5)
288
+ expect(range1).toBe("BCDE")
289
+
290
+ const range2 = editor.getTextRange(0, 6)
291
+ expect(range2).toBe("ABCDEF")
292
+ })
293
+
294
+ it("should handle range at buffer boundaries", async () => {
295
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
296
+ initialValue: "Test",
297
+ width: 40,
298
+ height: 10,
299
+ })
300
+
301
+ const range1 = editor.getTextRange(0, 2)
302
+ expect(range1).toBe("Te")
303
+
304
+ const range2 = editor.getTextRange(2, 4)
305
+ expect(range2).toBe("st")
306
+
307
+ const range3 = editor.getTextRange(0, 4)
308
+ expect(range3).toBe("Test")
309
+ })
310
+
311
+ it("should return empty string for out-of-bounds ranges", async () => {
312
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
313
+ initialValue: "Short",
314
+ width: 40,
315
+ height: 10,
316
+ })
317
+
318
+ const range1 = editor.getTextRange(100, 200)
319
+ expect(range1).toBe("")
320
+
321
+ const range2 = editor.getTextRange(0, 1000)
322
+ expect(range2).toBe("Short")
323
+ })
324
+ })
325
+
326
+ describe("Visual Cursor with Offset", () => {
327
+ it("should have visualCursor with offset property", async () => {
328
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
329
+ initialValue: "",
330
+ width: 40,
331
+ height: 10,
332
+ })
333
+
334
+ editor.focus()
335
+
336
+ const visualCursor = editor.visualCursor
337
+ expect(visualCursor).not.toBe(null)
338
+ expect(visualCursor!.offset).toBeDefined()
339
+ expect(visualCursor!.offset).toBe(0)
340
+ })
341
+
342
+ it("should update offset after inserting text", async () => {
343
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
344
+ initialValue: "",
345
+ width: 40,
346
+ height: 10,
347
+ })
348
+
349
+ editor.focus()
350
+
351
+ editor.insertText("Hello")
352
+
353
+ const visualCursor = editor.visualCursor
354
+ expect(visualCursor).not.toBe(null)
355
+ expect(visualCursor!.offset).toBe(5)
356
+ })
357
+
358
+ it("should update offset correctly for multi-line content", async () => {
359
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
360
+ initialValue: "ABC\nDEF",
361
+ width: 40,
362
+ height: 10,
363
+ })
364
+
365
+ editor.focus()
366
+
367
+ // Cursor at start
368
+ let visualCursor = editor.visualCursor
369
+ expect(visualCursor!.offset).toBe(0)
370
+
371
+ // Move to end of first line
372
+ for (let i = 0; i < 3; i++) {
373
+ editor.moveCursorRight()
374
+ }
375
+ visualCursor = editor.visualCursor
376
+ expect(visualCursor!.offset).toBe(3)
377
+
378
+ // Move to second line (across newline)
379
+ editor.moveCursorRight()
380
+ visualCursor = editor.visualCursor
381
+ expect(visualCursor!.offset).toBe(4)
382
+ expect(visualCursor!.logicalRow).toBe(1)
383
+ expect(visualCursor!.logicalCol).toBe(0)
384
+
385
+ // Move to end of second line
386
+ for (let i = 0; i < 3; i++) {
387
+ editor.moveCursorRight()
388
+ }
389
+ visualCursor = editor.visualCursor
390
+ expect(visualCursor!.offset).toBe(7)
391
+ })
392
+
393
+ it("should set cursor by offset", async () => {
394
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
395
+ initialValue: "Hello World",
396
+ width: 40,
397
+ height: 10,
398
+ })
399
+
400
+ editor.focus()
401
+
402
+ // Set cursor to offset 6 (after "Hello ")
403
+ editor.editBuffer.setCursorByOffset(6)
404
+
405
+ const visualCursor = editor.visualCursor
406
+ expect(visualCursor).not.toBe(null)
407
+ expect(visualCursor!.offset).toBe(6)
408
+ expect(visualCursor!.logicalRow).toBe(0)
409
+ expect(visualCursor!.logicalCol).toBe(6)
410
+
411
+ // Set cursor to offset 2
412
+ editor.editBuffer.setCursorByOffset(2)
413
+
414
+ const newVisualCursor = editor.visualCursor
415
+ expect(newVisualCursor).not.toBe(null)
416
+ expect(newVisualCursor!.offset).toBe(2)
417
+ expect(newVisualCursor!.logicalRow).toBe(0)
418
+ expect(newVisualCursor!.logicalCol).toBe(2)
419
+ })
420
+
421
+ it("should set cursor by offset in multi-line content", async () => {
422
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
423
+ initialValue: "Line1\nLine2\nLine3",
424
+ width: 40,
425
+ height: 10,
426
+ })
427
+
428
+ editor.focus()
429
+
430
+ // Set cursor to offset 6 (start of "Line2")
431
+ editor.editBuffer.setCursorByOffset(6)
432
+
433
+ const visualCursor = editor.visualCursor
434
+ expect(visualCursor).not.toBe(null)
435
+ expect(visualCursor!.offset).toBe(6)
436
+ expect(visualCursor!.logicalRow).toBe(1)
437
+ expect(visualCursor!.logicalCol).toBe(0)
438
+
439
+ // Set cursor to offset 8 (L[i]ne2, at 'n')
440
+ editor.editBuffer.setCursorByOffset(8)
441
+
442
+ const newVisualCursor = editor.visualCursor
443
+ expect(newVisualCursor).not.toBe(null)
444
+ expect(newVisualCursor!.offset).toBe(8)
445
+ expect(newVisualCursor!.logicalRow).toBe(1)
446
+ expect(newVisualCursor!.logicalCol).toBe(2)
447
+ })
448
+
449
+ it("should maintain offset consistency when using editorView.setCursorByOffset", async () => {
450
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
451
+ initialValue: "ABCDEF",
452
+ width: 40,
453
+ height: 10,
454
+ })
455
+
456
+ editor.focus()
457
+
458
+ // Use editorView instead of editBuffer
459
+ editor.editorView.setCursorByOffset(3)
460
+
461
+ const visualCursor = editor.visualCursor
462
+ expect(visualCursor).not.toBe(null)
463
+ expect(visualCursor!.offset).toBe(3)
464
+ expect(visualCursor!.logicalRow).toBe(0)
465
+ expect(visualCursor!.logicalCol).toBe(3)
466
+ })
467
+
468
+ it("should set cursor to end of content using cursorOffset setter and stringWidth", async () => {
469
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
470
+ initialValue: "",
471
+ width: 40,
472
+ height: 10,
473
+ })
474
+
475
+ editor.focus()
476
+
477
+ const content = "Hello World"
478
+ editor.setText(content)
479
+ editor.cursorOffset = stringWidth(content)
480
+
481
+ const visualCursor = editor.visualCursor
482
+ expect(visualCursor).not.toBe(null)
483
+ expect(visualCursor!.offset).toBe(stringWidth(content))
484
+ expect(visualCursor!.logicalRow).toBe(0)
485
+ expect(visualCursor!.logicalCol).toBe(content.length)
486
+ expect(visualCursor!.visualCol).toBe(content.length)
487
+
488
+ // Verify cursor is at the end
489
+ expect(editor.cursorOffset).toBe(11)
490
+ expect(editor.plainText).toBe("Hello World")
491
+ })
492
+ })
493
+
494
+ describe("EditBufferRenderable Methods", () => {
495
+ describe("deleteRange", () => {
496
+ it("should delete range within a single line", async () => {
497
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
498
+ initialValue: "Hello World",
499
+ width: 40,
500
+ height: 10,
501
+ })
502
+
503
+ editor.deleteRange(0, 6, 0, 11)
504
+ await renderOnce()
505
+
506
+ expect(editor.plainText).toBe("Hello ")
507
+ })
508
+
509
+ it("should delete range across multiple lines", async () => {
510
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
511
+ initialValue: "Line 1\nLine 2\nLine 3",
512
+ width: 40,
513
+ height: 10,
514
+ })
515
+
516
+ editor.deleteRange(0, 5, 2, 5)
517
+ await renderOnce()
518
+
519
+ expect(editor.plainText).toBe("Line 3")
520
+ })
521
+
522
+ it("should delete entire line", async () => {
523
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
524
+ initialValue: "First\nSecond\nThird",
525
+ width: 40,
526
+ height: 10,
527
+ })
528
+
529
+ editor.deleteRange(1, 0, 1, 6)
530
+ await renderOnce()
531
+
532
+ expect(editor.plainText).toBe("First\n\nThird")
533
+ })
534
+
535
+ it("should mark yoga node as dirty and request render", async () => {
536
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
537
+ initialValue: "Test text",
538
+ width: 40,
539
+ height: 10,
540
+ })
541
+
542
+ const initialHeight = editor.height
543
+ editor.deleteRange(0, 0, 0, 5)
544
+ await renderOnce()
545
+
546
+ expect(editor.plainText).toBe("text")
547
+ })
548
+
549
+ it("should handle empty range deletion", async () => {
550
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
551
+ initialValue: "Hello",
552
+ width: 40,
553
+ height: 10,
554
+ })
555
+
556
+ editor.deleteRange(0, 2, 0, 2)
557
+ await renderOnce()
558
+
559
+ expect(editor.plainText).toBe("Hello")
560
+ })
561
+ })
562
+
563
+ describe("insertText", () => {
564
+ it("should insert text at cursor position", async () => {
565
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
566
+ initialValue: "Hello",
567
+ width: 40,
568
+ height: 10,
569
+ })
570
+
571
+ editor.insertText(" World")
572
+ await renderOnce()
573
+
574
+ expect(editor.plainText).toBe(" WorldHello")
575
+ })
576
+
577
+ it("should insert text in middle of content", async () => {
578
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
579
+ initialValue: "HelloWorld",
580
+ width: 40,
581
+ height: 10,
582
+ })
583
+
584
+ editor.editBuffer.setCursor(0, 5)
585
+ editor.insertText(" ")
586
+ await renderOnce()
587
+
588
+ expect(editor.plainText).toBe("Hello World")
589
+ })
590
+
591
+ it("should insert multiline text", async () => {
592
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
593
+ initialValue: "Start",
594
+ width: 40,
595
+ height: 10,
596
+ })
597
+
598
+ editor.editBuffer.setCursor(0, 5)
599
+ editor.insertText("\nEnd")
600
+ await renderOnce()
601
+
602
+ expect(editor.plainText).toBe("Start\nEnd")
603
+ })
604
+
605
+ it("should mark yoga node as dirty and request render", async () => {
606
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
607
+ initialValue: "",
608
+ width: 40,
609
+ height: 10,
610
+ })
611
+
612
+ editor.insertText("Test")
613
+ await renderOnce()
614
+
615
+ expect(editor.plainText).toBe("Test")
616
+ })
617
+
618
+ it("should insert multiline text and update content", async () => {
619
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
620
+ initialValue: "Line 1",
621
+ width: 40,
622
+ height: 10,
623
+ })
624
+
625
+ editor.editBuffer.setCursor(0, 6)
626
+ editor.insertText("\nLine 2\nLine 3")
627
+ await renderOnce()
628
+
629
+ expect(editor.plainText).toBe("Line 1\nLine 2\nLine 3")
630
+ expect(editor.logicalCursor.row).toBe(2)
631
+ })
632
+ })
633
+
634
+ describe("Combined deleteRange and insertText", () => {
635
+ it("should replace text by deleting range then inserting", async () => {
636
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
637
+ initialValue: "Hello World",
638
+ width: 40,
639
+ height: 10,
640
+ })
641
+
642
+ editor.deleteRange(0, 6, 0, 11)
643
+ editor.editBuffer.setCursor(0, 6)
644
+ editor.insertText("Friend")
645
+ await renderOnce()
646
+
647
+ expect(editor.plainText).toBe("Hello Friend")
648
+ })
649
+
650
+ it("should handle complex editing operations", async () => {
651
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
652
+ initialValue: "Line 1\nLine 2\nLine 3",
653
+ width: 40,
654
+ height: 10,
655
+ })
656
+
657
+ editor.deleteRange(1, 0, 1, 6)
658
+ editor.editBuffer.setCursor(1, 0)
659
+ editor.insertText("Modified")
660
+ await renderOnce()
661
+
662
+ expect(editor.plainText).toBe("Line 1\nModified\nLine 3")
663
+ })
664
+
665
+ it("should work after multiple operations", async () => {
666
+ const { textarea: editor } = await createTextareaRenderable(currentRenderer, renderOnce, {
667
+ initialValue: "Start",
668
+ width: 40,
669
+ height: 10,
670
+ })
671
+
672
+ editor.editBuffer.setCursor(0, 5)
673
+ editor.insertText(" Middle")
674
+ editor.editBuffer.setCursor(0, 12)
675
+ editor.insertText(" End")
676
+ editor.deleteRange(0, 0, 0, 5)
677
+ await renderOnce()
678
+
679
+ expect(editor.plainText).toBe(" Middle End")
680
+ })
681
+ })
682
+ })
683
+ })