@fairyhunter13/opentui-core 0.1.112 → 0.1.114
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.
- package/dev/keypress-debug-renderer.ts +148 -0
- package/dev/keypress-debug.ts +43 -0
- package/dev/print-env-vars.ts +32 -0
- package/dev/test-tmux-graphics-334.sh +68 -0
- package/dev/thai-debug-test.ts +68 -0
- package/docs/development.md +144 -0
- package/package.json +63 -51
- package/scripts/build.ts +400 -0
- package/scripts/publish.ts +60 -0
- package/src/3d/SpriteResourceManager.ts +286 -0
- package/src/3d/SpriteUtils.ts +70 -0
- package/src/3d/TextureUtils.ts +196 -0
- package/src/3d/ThreeRenderable.ts +197 -0
- package/src/3d/WGPURenderer.ts +294 -0
- package/src/3d/animation/ExplodingSpriteEffect.ts +513 -0
- package/src/3d/animation/PhysicsExplodingSpriteEffect.ts +429 -0
- package/src/3d/animation/SpriteAnimator.ts +633 -0
- package/src/3d/animation/SpriteParticleGenerator.ts +435 -0
- package/src/3d/canvas.ts +464 -0
- package/src/3d/index.ts +12 -0
- package/src/3d/physics/PlanckPhysicsAdapter.ts +72 -0
- package/src/3d/physics/RapierPhysicsAdapter.ts +66 -0
- package/src/3d/physics/physics-interface.ts +31 -0
- package/src/3d/shaders/supersampling.wgsl +201 -0
- package/src/3d.ts +3 -0
- package/src/NativeSpanFeed.ts +300 -0
- package/src/Renderable.ts +1704 -0
- package/src/__snapshots__/buffer.test.ts.snap +28 -0
- package/src/animation/Timeline.test.ts +2709 -0
- package/src/animation/Timeline.ts +598 -0
- package/src/ansi.ts +18 -0
- package/src/benchmark/attenuation-benchmark.ts +81 -0
- package/src/benchmark/colormatrix-benchmark.ts +128 -0
- package/src/benchmark/gain-benchmark.ts +80 -0
- package/src/benchmark/latest-all-bench-run.json +707 -0
- package/src/benchmark/latest-async-bench-run.json +336 -0
- package/src/benchmark/latest-default-bench-run.json +657 -0
- package/src/benchmark/latest-large-bench-run.json +707 -0
- package/src/benchmark/latest-quick-bench-run.json +207 -0
- package/src/benchmark/markdown-benchmark.ts +1796 -0
- package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
- package/src/benchmark/native-span-feed-benchmark.md +56 -0
- package/src/benchmark/native-span-feed-benchmark.ts +596 -0
- package/src/benchmark/native-span-feed-compare.ts +280 -0
- package/src/benchmark/renderer-benchmark.ts +754 -0
- package/src/benchmark/text-table-benchmark.ts +948 -0
- package/src/buffer.test.ts +291 -0
- package/src/buffer.ts +554 -0
- package/src/console.test.ts +612 -0
- package/src/console.ts +1254 -0
- package/src/edit-buffer.test.ts +1769 -0
- package/src/edit-buffer.ts +411 -0
- package/src/editor-view.test.ts +1032 -0
- package/src/editor-view.ts +284 -0
- package/src/examples/ascii-font-selection-demo.ts +245 -0
- package/src/examples/assets/Water_2_M_Normal.jpg +0 -0
- package/src/examples/assets/concrete.png +0 -0
- package/src/examples/assets/crate.png +0 -0
- package/src/examples/assets/crate_emissive.png +0 -0
- package/src/examples/assets/forrest_background.png +0 -0
- package/src/examples/assets/hast-example.json +1018 -0
- package/src/examples/assets/heart.png +0 -0
- package/src/examples/assets/main_char_heavy_attack.png +0 -0
- package/src/examples/assets/main_char_idle.png +0 -0
- package/src/examples/assets/main_char_jump_end.png +0 -0
- package/src/examples/assets/main_char_jump_landing.png +0 -0
- package/src/examples/assets/main_char_jump_start.png +0 -0
- package/src/examples/assets/main_char_run_loop.png +0 -0
- package/src/examples/assets/roughness_map.jpg +0 -0
- package/src/examples/build.ts +115 -0
- package/src/examples/code-demo.ts +924 -0
- package/src/examples/console-demo.ts +358 -0
- package/src/examples/core-plugin-slots-demo.ts +759 -0
- package/src/examples/diff-demo.ts +701 -0
- package/src/examples/draggable-three-demo.ts +259 -0
- package/src/examples/editor-demo.ts +322 -0
- package/src/examples/extmarks-demo.ts +196 -0
- package/src/examples/focus-restore-demo.ts +310 -0
- package/src/examples/fonts.ts +245 -0
- package/src/examples/fractal-shader-demo.ts +268 -0
- package/src/examples/framebuffer-demo.ts +674 -0
- package/src/examples/full-unicode-demo.ts +241 -0
- package/src/examples/golden-star-demo.ts +933 -0
- package/src/examples/grayscale-buffer-demo.ts +249 -0
- package/src/examples/hast-syntax-highlighting-demo.ts +129 -0
- package/src/examples/index.ts +926 -0
- package/src/examples/input-demo.ts +377 -0
- package/src/examples/input-select-layout-demo.ts +425 -0
- package/src/examples/install.sh +143 -0
- package/src/examples/keypress-debug-demo.ts +452 -0
- package/src/examples/lib/HexList.ts +122 -0
- package/src/examples/lib/PaletteGrid.ts +125 -0
- package/src/examples/lib/standalone-keys.ts +25 -0
- package/src/examples/lib/tab-controller.ts +243 -0
- package/src/examples/lights-phong-demo.ts +290 -0
- package/src/examples/link-demo.ts +220 -0
- package/src/examples/live-state-demo.ts +480 -0
- package/src/examples/markdown-demo.ts +725 -0
- package/src/examples/mouse-interaction-demo.ts +428 -0
- package/src/examples/nested-zindex-demo.ts +357 -0
- package/src/examples/opacity-example.ts +235 -0
- package/src/examples/opentui-demo.ts +1057 -0
- package/src/examples/physx-planck-2d-demo.ts +623 -0
- package/src/examples/physx-rapier-2d-demo.ts +655 -0
- package/src/examples/relative-positioning-demo.ts +323 -0
- package/src/examples/scroll-example.ts +214 -0
- package/src/examples/scrollbox-mouse-test.ts +112 -0
- package/src/examples/scrollbox-overlay-hit-test.ts +206 -0
- package/src/examples/select-demo.ts +237 -0
- package/src/examples/shader-cube-demo.ts +1015 -0
- package/src/examples/simple-layout-example.ts +591 -0
- package/src/examples/slider-demo.ts +617 -0
- package/src/examples/split-mode-demo.ts +453 -0
- package/src/examples/sprite-animation-demo.ts +443 -0
- package/src/examples/sprite-particle-generator-demo.ts +486 -0
- package/src/examples/static-sprite-demo.ts +193 -0
- package/src/examples/sticky-scroll-example.ts +308 -0
- package/src/examples/styled-text-demo.ts +282 -0
- package/src/examples/tab-select-demo.ts +219 -0
- package/src/examples/terminal-title.ts +29 -0
- package/src/examples/terminal.ts +305 -0
- package/src/examples/text-node-demo.ts +416 -0
- package/src/examples/text-selection-demo.ts +377 -0
- package/src/examples/text-table-demo.ts +503 -0
- package/src/examples/text-truncation-demo.ts +481 -0
- package/src/examples/text-wrap.ts +757 -0
- package/src/examples/texture-loading-demo.ts +259 -0
- package/src/examples/timeline-example.ts +670 -0
- package/src/examples/transparency-demo.ts +400 -0
- package/src/examples/vnode-composition-demo.ts +404 -0
- package/src/examples/wide-grapheme-overlay-demo.ts +280 -0
- package/src/index.ts +24 -0
- package/src/lib/KeyHandler.integration.test.ts +292 -0
- package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
- package/src/lib/KeyHandler.test.ts +662 -0
- package/src/lib/KeyHandler.ts +222 -0
- package/src/lib/RGBA.test.ts +984 -0
- package/src/lib/RGBA.ts +204 -0
- package/src/lib/ascii.font.ts +330 -0
- package/src/lib/border.test.ts +83 -0
- package/src/lib/border.ts +170 -0
- package/src/lib/bunfs.test.ts +27 -0
- package/src/lib/bunfs.ts +18 -0
- package/src/lib/clipboard.test.ts +41 -0
- package/src/lib/clipboard.ts +47 -0
- package/src/lib/clock.ts +35 -0
- package/src/lib/data-paths.test.ts +133 -0
- package/src/lib/data-paths.ts +109 -0
- package/src/lib/debounce.ts +106 -0
- package/src/lib/detect-links.test.ts +98 -0
- package/src/lib/detect-links.ts +56 -0
- package/src/lib/env.test.ts +228 -0
- package/src/lib/env.ts +209 -0
- package/src/lib/extmarks-history.ts +51 -0
- package/src/lib/extmarks-multiwidth.test.ts +322 -0
- package/src/lib/extmarks.test.ts +3457 -0
- package/src/lib/extmarks.ts +843 -0
- package/src/lib/fonts/block.json +405 -0
- package/src/lib/fonts/grid.json +265 -0
- package/src/lib/fonts/huge.json +741 -0
- package/src/lib/fonts/pallet.json +314 -0
- package/src/lib/fonts/shade.json +591 -0
- package/src/lib/fonts/slick.json +321 -0
- package/src/lib/fonts/tiny.json +69 -0
- package/src/lib/hast-styled-text.ts +59 -0
- package/src/lib/index.ts +21 -0
- package/src/lib/keymapping.test.ts +317 -0
- package/src/lib/keymapping.ts +115 -0
- package/src/lib/objects-in-viewport.test.ts +787 -0
- package/src/lib/objects-in-viewport.ts +153 -0
- package/src/lib/output.capture.ts +58 -0
- package/src/lib/parse.keypress-kitty.protocol.test.ts +340 -0
- package/src/lib/parse.keypress-kitty.test.ts +663 -0
- package/src/lib/parse.keypress-kitty.ts +439 -0
- package/src/lib/parse.keypress.test.ts +1849 -0
- package/src/lib/parse.keypress.ts +397 -0
- package/src/lib/parse.mouse.test.ts +552 -0
- package/src/lib/parse.mouse.ts +232 -0
- package/src/lib/paste.ts +16 -0
- package/src/lib/queue.ts +65 -0
- package/src/lib/renderable.validations.test.ts +87 -0
- package/src/lib/renderable.validations.ts +83 -0
- package/src/lib/scroll-acceleration.ts +98 -0
- package/src/lib/selection.ts +240 -0
- package/src/lib/singleton.ts +28 -0
- package/src/lib/stdin-parser.test.ts +2290 -0
- package/src/lib/stdin-parser.ts +1810 -0
- package/src/lib/styled-text.ts +178 -0
- package/src/lib/terminal-capability-detection.test.ts +202 -0
- package/src/lib/terminal-capability-detection.ts +79 -0
- package/src/lib/terminal-palette.test.ts +878 -0
- package/src/lib/terminal-palette.ts +383 -0
- package/src/lib/tree-sitter/assets/README.md +118 -0
- package/src/lib/tree-sitter/assets/update.ts +334 -0
- package/src/lib/tree-sitter/assets.d.ts +9 -0
- package/src/lib/tree-sitter/cache.test.ts +273 -0
- package/src/lib/tree-sitter/client.test.ts +1165 -0
- package/src/lib/tree-sitter/client.ts +607 -0
- package/src/lib/tree-sitter/default-parsers.ts +86 -0
- package/src/lib/tree-sitter/download-utils.ts +148 -0
- package/src/lib/tree-sitter/index.ts +28 -0
- package/src/lib/tree-sitter/parser.worker.ts +1042 -0
- package/src/lib/tree-sitter/parsers-config.ts +81 -0
- package/src/lib/tree-sitter/resolve-ft.test.ts +55 -0
- package/src/lib/tree-sitter/resolve-ft.ts +189 -0
- package/src/lib/tree-sitter/types.ts +82 -0
- package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
- package/src/lib/tree-sitter-styled-text.ts +306 -0
- package/src/lib/validate-dir-name.ts +55 -0
- package/src/lib/yoga.options.test.ts +628 -0
- package/src/lib/yoga.options.ts +346 -0
- package/src/plugins/core-slot.ts +579 -0
- package/src/plugins/registry.ts +402 -0
- package/src/plugins/types.ts +46 -0
- package/src/post/effects.ts +930 -0
- package/src/post/filters.ts +489 -0
- package/src/post/matrices.ts +288 -0
- package/src/renderables/ASCIIFont.ts +219 -0
- package/src/renderables/Box.test.ts +205 -0
- package/src/renderables/Box.ts +326 -0
- package/src/renderables/Code.test.ts +2062 -0
- package/src/renderables/Code.ts +357 -0
- package/src/renderables/Diff.regression.test.ts +226 -0
- package/src/renderables/Diff.test.ts +3101 -0
- package/src/renderables/Diff.ts +1211 -0
- package/src/renderables/EditBufferRenderable.test.ts +288 -0
- package/src/renderables/EditBufferRenderable.ts +1166 -0
- package/src/renderables/FrameBuffer.ts +47 -0
- package/src/renderables/Input.test.ts +1228 -0
- package/src/renderables/Input.ts +247 -0
- package/src/renderables/LineNumberRenderable.ts +724 -0
- package/src/renderables/Markdown.ts +1393 -0
- package/src/renderables/ScrollBar.ts +422 -0
- package/src/renderables/ScrollBox.ts +883 -0
- package/src/renderables/Select.test.ts +1033 -0
- package/src/renderables/Select.ts +524 -0
- package/src/renderables/Slider.test.ts +456 -0
- package/src/renderables/Slider.ts +342 -0
- package/src/renderables/TabSelect.test.ts +197 -0
- package/src/renderables/TabSelect.ts +455 -0
- package/src/renderables/Text.selection-buffer.test.ts +123 -0
- package/src/renderables/Text.test.ts +2660 -0
- package/src/renderables/Text.ts +147 -0
- package/src/renderables/TextBufferRenderable.ts +518 -0
- package/src/renderables/TextNode.test.ts +1058 -0
- package/src/renderables/TextNode.ts +325 -0
- package/src/renderables/TextTable.test.ts +1421 -0
- package/src/renderables/TextTable.ts +1344 -0
- package/src/renderables/Textarea.ts +430 -0
- package/src/renderables/TimeToFirstDraw.ts +89 -0
- package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
- package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
- package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
- package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
- package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
- package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +816 -0
- package/src/renderables/__tests__/LineNumberRenderable.test.ts +1865 -0
- package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
- package/src/renderables/__tests__/Markdown.code-colors.test.ts +242 -0
- package/src/renderables/__tests__/Markdown.test.ts +2518 -0
- package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
- package/src/renderables/__tests__/Textarea.buffer.test.ts +682 -0
- package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
- package/src/renderables/__tests__/Textarea.editing.test.ts +2041 -0
- package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
- package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
- package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
- package/src/renderables/__tests__/Textarea.keybinding.test.ts +3149 -0
- package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
- package/src/renderables/__tests__/Textarea.rendering.test.ts +1866 -0
- package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
- package/src/renderables/__tests__/Textarea.selection.test.ts +1590 -0
- package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
- package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
- package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
- package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
- package/src/renderables/__tests__/markdown-parser.test.ts +217 -0
- package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
- package/src/renderables/composition/README.md +8 -0
- package/src/renderables/composition/VRenderable.ts +32 -0
- package/src/renderables/composition/constructs.ts +127 -0
- package/src/renderables/composition/vnode.ts +289 -0
- package/src/renderables/index.ts +23 -0
- package/src/renderables/markdown-parser.ts +66 -0
- package/src/renderer.ts +2681 -0
- package/src/runtime-plugin-support.ts +39 -0
- package/src/runtime-plugin.ts +615 -0
- package/src/syntax-style.test.ts +841 -0
- package/src/syntax-style.ts +257 -0
- package/src/testing/README.md +210 -0
- package/src/testing/capture-spans.test.ts +194 -0
- package/src/testing/integration.test.ts +276 -0
- package/src/testing/manual-clock.ts +117 -0
- package/src/testing/mock-keys.test.ts +1378 -0
- package/src/testing/mock-keys.ts +457 -0
- package/src/testing/mock-mouse.test.ts +218 -0
- package/src/testing/mock-mouse.ts +247 -0
- package/src/testing/mock-tree-sitter-client.ts +73 -0
- package/src/testing/spy.ts +13 -0
- package/src/testing/test-recorder.test.ts +415 -0
- package/src/testing/test-recorder.ts +145 -0
- package/src/testing/test-renderer.ts +132 -0
- package/src/testing.ts +7 -0
- package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
- package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
- package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
- package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
- package/src/tests/allocator-stats.test.ts +38 -0
- package/src/tests/destroy-during-render.test.ts +200 -0
- package/src/tests/destroy-on-exit.fixture.ts +36 -0
- package/src/tests/destroy-on-exit.test.ts +41 -0
- package/src/tests/hover-cursor.test.ts +98 -0
- package/src/tests/native-span-feed-async.test.ts +173 -0
- package/src/tests/native-span-feed-close.test.ts +120 -0
- package/src/tests/native-span-feed-coverage.test.ts +227 -0
- package/src/tests/native-span-feed-edge-cases.test.ts +352 -0
- package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
- package/src/tests/opacity.test.ts +123 -0
- package/src/tests/renderable.snapshot.test.ts +524 -0
- package/src/tests/renderable.test.ts +1281 -0
- package/src/tests/renderer.clock.test.ts +158 -0
- package/src/tests/renderer.console-startup.test.ts +185 -0
- package/src/tests/renderer.control.test.ts +425 -0
- package/src/tests/renderer.core-slot-binding.test.ts +952 -0
- package/src/tests/renderer.cursor.test.ts +26 -0
- package/src/tests/renderer.destroy-during-render.test.ts +147 -0
- package/src/tests/renderer.focus-restore.test.ts +257 -0
- package/src/tests/renderer.focus.test.ts +294 -0
- package/src/tests/renderer.idle.test.ts +219 -0
- package/src/tests/renderer.input.test.ts +2237 -0
- package/src/tests/renderer.kitty-flags.test.ts +195 -0
- package/src/tests/renderer.mouse.test.ts +1274 -0
- package/src/tests/renderer.palette.test.ts +629 -0
- package/src/tests/renderer.selection.test.ts +49 -0
- package/src/tests/renderer.slot-registry.test.ts +684 -0
- package/src/tests/renderer.useMouse.test.ts +47 -0
- package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +76 -0
- package/src/tests/runtime-plugin-node-modules-mjs.fixture.ts +43 -0
- package/src/tests/runtime-plugin-node-modules-no-bare-rewrite.fixture.ts +67 -0
- package/src/tests/runtime-plugin-node-modules-package-type-cache.fixture.ts +72 -0
- package/src/tests/runtime-plugin-node-modules-runtime-specifier.fixture.ts +44 -0
- package/src/tests/runtime-plugin-node-modules-scoped-package-bare-rewrite.fixture.ts +85 -0
- package/src/tests/runtime-plugin-path-alias.fixture.ts +43 -0
- package/src/tests/runtime-plugin-resolve-roots.fixture.ts +65 -0
- package/src/tests/runtime-plugin-support.fixture.ts +11 -0
- package/src/tests/runtime-plugin-support.test.ts +19 -0
- package/src/tests/runtime-plugin-windows-file-url.fixture.ts +30 -0
- package/src/tests/runtime-plugin.fixture.ts +40 -0
- package/src/tests/runtime-plugin.test.ts +354 -0
- package/src/tests/scrollbox-culling-bug.test.ts +114 -0
- package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
- package/src/tests/scrollbox-hitgrid.test.ts +909 -0
- package/src/tests/scrollbox.test.ts +1530 -0
- package/src/tests/wrap-resize-perf.test.ts +276 -0
- package/src/tests/yoga-setters.test.ts +921 -0
- package/src/text-buffer-view.test.ts +705 -0
- package/src/text-buffer-view.ts +189 -0
- package/src/text-buffer.test.ts +347 -0
- package/src/text-buffer.ts +250 -0
- package/src/types.ts +161 -0
- package/src/utils.ts +88 -0
- package/src/zig/ansi.zig +268 -0
- package/src/zig/bench/README.md +50 -0
- package/src/zig/bench/buffer-draw-text-buffer_bench.zig +887 -0
- package/src/zig/bench/edit-buffer_bench.zig +476 -0
- package/src/zig/bench/native-span-feed_bench.zig +100 -0
- package/src/zig/bench/rope-markers_bench.zig +713 -0
- package/src/zig/bench/rope_bench.zig +514 -0
- package/src/zig/bench/styled-text_bench.zig +470 -0
- package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
- package/src/zig/bench/text-buffer-view_bench.zig +459 -0
- package/src/zig/bench/text-chunk-graphemes_bench.zig +273 -0
- package/src/zig/bench/utf8_bench.zig +799 -0
- package/src/zig/bench-utils.zig +431 -0
- package/src/zig/bench.zig +217 -0
- package/src/zig/buffer-methods.zig +211 -0
- package/src/zig/buffer.zig +2281 -0
- package/src/zig/build.zig +289 -0
- package/src/zig/build.zig.zon +16 -0
- package/src/zig/edit-buffer.zig +825 -0
- package/src/zig/editor-view.zig +802 -0
- package/src/zig/event-bus.zig +13 -0
- package/src/zig/event-emitter.zig +65 -0
- package/src/zig/file-logger.zig +92 -0
- package/src/zig/grapheme.zig +599 -0
- package/src/zig/lib.zig +1854 -0
- package/src/zig/link.zig +333 -0
- package/src/zig/logger.zig +43 -0
- package/src/zig/mem-registry.zig +125 -0
- package/src/zig/native-span-feed-bench-lib.zig +7 -0
- package/src/zig/native-span-feed.zig +708 -0
- package/src/zig/renderer.zig +1393 -0
- package/src/zig/rope.zig +1220 -0
- package/src/zig/syntax-style.zig +161 -0
- package/src/zig/terminal.zig +987 -0
- package/src/zig/test.zig +72 -0
- package/src/zig/tests/README.md +18 -0
- package/src/zig/tests/buffer-methods_test.zig +1109 -0
- package/src/zig/tests/buffer_test.zig +2557 -0
- package/src/zig/tests/edit-buffer-history_test.zig +271 -0
- package/src/zig/tests/edit-buffer_test.zig +1689 -0
- package/src/zig/tests/editor-view_test.zig +3299 -0
- package/src/zig/tests/event-emitter_test.zig +249 -0
- package/src/zig/tests/grapheme_test.zig +1304 -0
- package/src/zig/tests/link_test.zig +190 -0
- package/src/zig/tests/mem-registry_test.zig +473 -0
- package/src/zig/tests/memory_leak_regression_test.zig +159 -0
- package/src/zig/tests/native-span-feed_test.zig +1264 -0
- package/src/zig/tests/renderer_test.zig +1017 -0
- package/src/zig/tests/rope-nested_test.zig +712 -0
- package/src/zig/tests/rope_fuzz_test.zig +238 -0
- package/src/zig/tests/rope_test.zig +2362 -0
- package/src/zig/tests/segment-merge.test.zig +148 -0
- package/src/zig/tests/syntax-style_test.zig +557 -0
- package/src/zig/tests/terminal_test.zig +754 -0
- package/src/zig/tests/text-buffer-drawing_test.zig +3237 -0
- package/src/zig/tests/text-buffer-highlights_test.zig +666 -0
- package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
- package/src/zig/tests/text-buffer-segment_test.zig +320 -0
- package/src/zig/tests/text-buffer-selection_test.zig +1035 -0
- package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
- package/src/zig/tests/text-buffer-view_test.zig +3649 -0
- package/src/zig/tests/text-buffer_test.zig +2191 -0
- package/src/zig/tests/unicode-width-map.zon +3909 -0
- package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
- package/src/zig/tests/utf8_test.zig +4057 -0
- package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
- package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
- package/src/zig/tests/word-wrap-editing_test.zig +498 -0
- package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
- package/src/zig/text-buffer-iterators.zig +499 -0
- package/src/zig/text-buffer-segment.zig +404 -0
- package/src/zig/text-buffer-view.zig +1371 -0
- package/src/zig/text-buffer.zig +1180 -0
- package/src/zig/utf8.zig +1948 -0
- package/src/zig/utils.zig +9 -0
- package/src/zig-structs.ts +261 -0
- package/src/zig.ts +3884 -0
- package/tsconfig.build.json +24 -0
- package/tsconfig.json +27 -0
- package/3d/SpriteResourceManager.d.ts +0 -74
- package/3d/SpriteUtils.d.ts +0 -13
- package/3d/TextureUtils.d.ts +0 -24
- package/3d/ThreeRenderable.d.ts +0 -40
- package/3d/WGPURenderer.d.ts +0 -61
- package/3d/animation/ExplodingSpriteEffect.d.ts +0 -71
- package/3d/animation/PhysicsExplodingSpriteEffect.d.ts +0 -76
- package/3d/animation/SpriteAnimator.d.ts +0 -124
- package/3d/animation/SpriteParticleGenerator.d.ts +0 -62
- package/3d/canvas.d.ts +0 -44
- package/3d/index.d.ts +0 -12
- package/3d/physics/PlanckPhysicsAdapter.d.ts +0 -19
- package/3d/physics/RapierPhysicsAdapter.d.ts +0 -19
- package/3d/physics/physics-interface.d.ts +0 -27
- package/3d.d.ts +0 -2
- package/3d.js +0 -34041
- package/3d.js.map +0 -155
- package/LICENSE +0 -21
- package/NativeSpanFeed.d.ts +0 -41
- package/Renderable.d.ts +0 -334
- package/animation/Timeline.d.ts +0 -126
- package/ansi.d.ts +0 -13
- package/buffer.d.ts +0 -111
- package/console.d.ts +0 -144
- package/edit-buffer.d.ts +0 -98
- package/editor-view.d.ts +0 -73
- package/index-8fks7yv1.js +0 -411
- package/index-8fks7yv1.js.map +0 -10
- package/index-egy5e2rs.js +0 -12267
- package/index-egy5e2rs.js.map +0 -42
- package/index-tse8gzh0.js +0 -20614
- package/index-tse8gzh0.js.map +0 -67
- package/index.d.ts +0 -23
- package/index.js +0 -478
- package/index.js.map +0 -9
- package/lib/KeyHandler.d.ts +0 -61
- package/lib/RGBA.d.ts +0 -25
- package/lib/ascii.font.d.ts +0 -508
- package/lib/border.d.ts +0 -51
- package/lib/bunfs.d.ts +0 -7
- package/lib/clipboard.d.ts +0 -17
- package/lib/clock.d.ts +0 -15
- package/lib/data-paths.d.ts +0 -26
- package/lib/debounce.d.ts +0 -42
- package/lib/detect-links.d.ts +0 -6
- package/lib/env.d.ts +0 -42
- package/lib/extmarks-history.d.ts +0 -17
- package/lib/extmarks.d.ts +0 -89
- package/lib/hast-styled-text.d.ts +0 -17
- package/lib/index.d.ts +0 -21
- package/lib/keymapping.d.ts +0 -25
- package/lib/objects-in-viewport.d.ts +0 -24
- package/lib/output.capture.d.ts +0 -24
- package/lib/parse.keypress-kitty.d.ts +0 -2
- package/lib/parse.keypress.d.ts +0 -26
- package/lib/parse.mouse.d.ts +0 -30
- package/lib/paste.d.ts +0 -7
- package/lib/queue.d.ts +0 -15
- package/lib/renderable.validations.d.ts +0 -12
- package/lib/scroll-acceleration.d.ts +0 -43
- package/lib/selection.d.ts +0 -63
- package/lib/singleton.d.ts +0 -7
- package/lib/stdin-parser.d.ts +0 -87
- package/lib/styled-text.d.ts +0 -63
- package/lib/terminal-capability-detection.d.ts +0 -30
- package/lib/terminal-palette.d.ts +0 -50
- package/lib/tree-sitter/assets/update.d.ts +0 -11
- package/lib/tree-sitter/client.d.ts +0 -47
- package/lib/tree-sitter/default-parsers.d.ts +0 -2
- package/lib/tree-sitter/download-utils.d.ts +0 -21
- package/lib/tree-sitter/index.d.ts +0 -8
- package/lib/tree-sitter/parser.worker.d.ts +0 -1
- package/lib/tree-sitter/parsers-config.d.ts +0 -53
- package/lib/tree-sitter/resolve-ft.d.ts +0 -5
- package/lib/tree-sitter/types.d.ts +0 -82
- package/lib/tree-sitter-styled-text.d.ts +0 -14
- package/lib/validate-dir-name.d.ts +0 -1
- package/lib/yoga.options.d.ts +0 -32
- package/parser.worker.js +0 -899
- package/parser.worker.js.map +0 -12
- package/plugins/core-slot.d.ts +0 -72
- package/plugins/registry.d.ts +0 -42
- package/plugins/types.d.ts +0 -34
- package/post/effects.d.ts +0 -147
- package/post/filters.d.ts +0 -65
- package/post/matrices.d.ts +0 -20
- package/renderables/ASCIIFont.d.ts +0 -52
- package/renderables/Box.d.ts +0 -81
- package/renderables/Code.d.ts +0 -78
- package/renderables/Diff.d.ts +0 -142
- package/renderables/EditBufferRenderable.d.ts +0 -237
- package/renderables/FrameBuffer.d.ts +0 -16
- package/renderables/Input.d.ts +0 -67
- package/renderables/LineNumberRenderable.d.ts +0 -78
- package/renderables/Markdown.d.ts +0 -185
- package/renderables/ScrollBar.d.ts +0 -77
- package/renderables/ScrollBox.d.ts +0 -124
- package/renderables/Select.d.ts +0 -115
- package/renderables/Slider.d.ts +0 -47
- package/renderables/TabSelect.d.ts +0 -96
- package/renderables/Text.d.ts +0 -36
- package/renderables/TextBufferRenderable.d.ts +0 -105
- package/renderables/TextNode.d.ts +0 -91
- package/renderables/TextTable.d.ts +0 -140
- package/renderables/Textarea.d.ts +0 -63
- package/renderables/TimeToFirstDraw.d.ts +0 -24
- package/renderables/__tests__/renderable-test-utils.d.ts +0 -12
- package/renderables/composition/VRenderable.d.ts +0 -16
- package/renderables/composition/constructs.d.ts +0 -35
- package/renderables/composition/vnode.d.ts +0 -46
- package/renderables/index.d.ts +0 -23
- package/renderables/markdown-parser.d.ts +0 -10
- package/renderer.d.ts +0 -419
- package/runtime-plugin-support.d.ts +0 -3
- package/runtime-plugin-support.js +0 -29
- package/runtime-plugin-support.js.map +0 -10
- package/runtime-plugin.d.ts +0 -16
- package/runtime-plugin.js +0 -16
- package/runtime-plugin.js.map +0 -9
- package/syntax-style.d.ts +0 -54
- package/testing/manual-clock.d.ts +0 -17
- package/testing/mock-keys.d.ts +0 -81
- package/testing/mock-mouse.d.ts +0 -38
- package/testing/mock-tree-sitter-client.d.ts +0 -23
- package/testing/spy.d.ts +0 -7
- package/testing/test-recorder.d.ts +0 -61
- package/testing/test-renderer.d.ts +0 -23
- package/testing.d.ts +0 -6
- package/testing.js +0 -697
- package/testing.js.map +0 -15
- package/text-buffer-view.d.ts +0 -42
- package/text-buffer.d.ts +0 -67
- package/types.d.ts +0 -139
- package/utils.d.ts +0 -14
- package/zig-structs.d.ts +0 -155
- package/zig.d.ts +0 -353
- /package/{assets → src/lib/tree-sitter/assets}/javascript/highlights.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/javascript/tree-sitter-javascript.wasm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/markdown/highlights.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/markdown/injections.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/markdown/tree-sitter-markdown.wasm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/highlights.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/typescript/highlights.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/typescript/tree-sitter-typescript.wasm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/zig/highlights.scm +0 -0
- /package/{assets → src/lib/tree-sitter/assets}/zig/tree-sitter-zig.wasm +0 -0
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const Allocator = std.mem.Allocator;
|
|
3
|
+
const tb = @import("text-buffer.zig");
|
|
4
|
+
const iter_mod = @import("text-buffer-iterators.zig");
|
|
5
|
+
const seg_mod = @import("text-buffer-segment.zig");
|
|
6
|
+
const gp = @import("grapheme.zig");
|
|
7
|
+
const link = @import("link.zig");
|
|
8
|
+
|
|
9
|
+
const utf8 = @import("utf8.zig");
|
|
10
|
+
const event_emitter = @import("event-emitter.zig");
|
|
11
|
+
const event_bus = @import("event-bus.zig");
|
|
12
|
+
|
|
13
|
+
const UnifiedTextBuffer = tb.UnifiedTextBuffer;
|
|
14
|
+
const TextChunk = seg_mod.TextChunk;
|
|
15
|
+
const Segment = seg_mod.Segment;
|
|
16
|
+
const UnifiedRope = seg_mod.UnifiedRope;
|
|
17
|
+
|
|
18
|
+
var global_edit_buffer_id: u16 = 0;
|
|
19
|
+
|
|
20
|
+
pub const EditBufferError = error{
|
|
21
|
+
OutOfMemory,
|
|
22
|
+
InvalidCursor,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
pub const EditBufferEvent = enum {
|
|
26
|
+
cursorChanged,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/// Cursor position (row, col in display-width coordinates)
|
|
30
|
+
pub const Cursor = struct {
|
|
31
|
+
row: u32,
|
|
32
|
+
col: u32,
|
|
33
|
+
desired_col: u32 = 0,
|
|
34
|
+
offset: u32 = 0, // Global display-width offset from buffer start
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const CursorCoords = struct { row: u32, col: u32 };
|
|
38
|
+
|
|
39
|
+
const AddBuffer = struct {
|
|
40
|
+
mem_id: u8,
|
|
41
|
+
ptr: [*]u8,
|
|
42
|
+
len: usize,
|
|
43
|
+
cap: usize,
|
|
44
|
+
allocator: Allocator,
|
|
45
|
+
|
|
46
|
+
fn init(allocator: Allocator, text_buffer: *UnifiedTextBuffer, initial_cap: usize) !AddBuffer {
|
|
47
|
+
const mem = try allocator.alloc(u8, initial_cap);
|
|
48
|
+
const mem_id = try text_buffer.registerMemBuffer(mem, true);
|
|
49
|
+
|
|
50
|
+
return .{
|
|
51
|
+
.mem_id = mem_id,
|
|
52
|
+
.ptr = mem.ptr,
|
|
53
|
+
.len = 0,
|
|
54
|
+
.cap = mem.len,
|
|
55
|
+
.allocator = allocator,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fn ensureCapacity(self: *AddBuffer, text_buffer: *UnifiedTextBuffer, need: usize) !void {
|
|
60
|
+
if (self.len + need <= self.cap) return;
|
|
61
|
+
|
|
62
|
+
// TODO: Create a new buffer, register the new buffer and use the new mem_id for subsequent inserts
|
|
63
|
+
const new_cap = @max(self.cap * 2, self.len + need);
|
|
64
|
+
const new_mem = try self.allocator.alloc(u8, new_cap);
|
|
65
|
+
const new_mem_id = try text_buffer.registerMemBuffer(new_mem, true);
|
|
66
|
+
self.mem_id = new_mem_id;
|
|
67
|
+
self.ptr = new_mem.ptr;
|
|
68
|
+
self.len = 0;
|
|
69
|
+
self.cap = new_mem.len;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn append(self: *AddBuffer, bytes: []const u8) struct { mem_id: u8, start: u32, end: u32 } {
|
|
73
|
+
std.debug.assert(self.len + bytes.len <= self.cap);
|
|
74
|
+
const start: u32 = @intCast(self.len);
|
|
75
|
+
|
|
76
|
+
const dest_slice = self.ptr[0..self.cap];
|
|
77
|
+
@memcpy(dest_slice[self.len .. self.len + bytes.len], bytes);
|
|
78
|
+
|
|
79
|
+
self.len += bytes.len;
|
|
80
|
+
const end: u32 = @intCast(self.len);
|
|
81
|
+
return .{ .mem_id = self.mem_id, .start = start, .end = end };
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
pub const EditBuffer = struct {
|
|
86
|
+
id: u16,
|
|
87
|
+
tb: *UnifiedTextBuffer,
|
|
88
|
+
add_buffer: AddBuffer,
|
|
89
|
+
cursors: std.ArrayListUnmanaged(Cursor),
|
|
90
|
+
allocator: Allocator,
|
|
91
|
+
events: event_emitter.EventEmitter(EditBufferEvent),
|
|
92
|
+
segment_splitter: UnifiedRope.Node.LeafSplitFn,
|
|
93
|
+
|
|
94
|
+
pub fn init(
|
|
95
|
+
allocator: Allocator,
|
|
96
|
+
pool: *gp.GraphemePool,
|
|
97
|
+
link_pool: *link.LinkPool,
|
|
98
|
+
width_method: utf8.WidthMethod,
|
|
99
|
+
) !*EditBuffer {
|
|
100
|
+
const self = try allocator.create(EditBuffer);
|
|
101
|
+
errdefer allocator.destroy(self);
|
|
102
|
+
|
|
103
|
+
const text_buffer = try UnifiedTextBuffer.init(allocator, pool, link_pool, width_method);
|
|
104
|
+
errdefer text_buffer.deinit();
|
|
105
|
+
|
|
106
|
+
const add_buffer = try AddBuffer.init(allocator, text_buffer, 65536);
|
|
107
|
+
errdefer {}
|
|
108
|
+
|
|
109
|
+
var cursors: std.ArrayListUnmanaged(Cursor) = .{};
|
|
110
|
+
errdefer cursors.deinit(allocator);
|
|
111
|
+
|
|
112
|
+
try cursors.append(allocator, .{ .row = 0, .col = 0 });
|
|
113
|
+
|
|
114
|
+
const buffer_id = global_edit_buffer_id;
|
|
115
|
+
global_edit_buffer_id += 1;
|
|
116
|
+
|
|
117
|
+
self.* = .{
|
|
118
|
+
.id = buffer_id,
|
|
119
|
+
.tb = text_buffer,
|
|
120
|
+
.add_buffer = add_buffer,
|
|
121
|
+
.cursors = cursors,
|
|
122
|
+
.allocator = allocator,
|
|
123
|
+
.events = event_emitter.EventEmitter(EditBufferEvent).init(allocator),
|
|
124
|
+
.segment_splitter = .{ .ctx = self, .splitFn = splitSegmentCallback },
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return self;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
pub fn deinit(self: *EditBuffer) void {
|
|
131
|
+
// Registry owns all AddBuffer memory, don't free it manually
|
|
132
|
+
self.events.deinit();
|
|
133
|
+
self.tb.deinit();
|
|
134
|
+
self.cursors.deinit(self.allocator);
|
|
135
|
+
self.allocator.destroy(self);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
pub fn getId(self: *const EditBuffer) u16 {
|
|
139
|
+
return self.id;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fn emitNativeEvent(self: *const EditBuffer, event_name: []const u8) void {
|
|
143
|
+
var id_bytes: [2]u8 = undefined;
|
|
144
|
+
std.mem.writeInt(u16, &id_bytes, self.id, .little);
|
|
145
|
+
|
|
146
|
+
const full_name = std.fmt.allocPrint(self.allocator, "eb_{s}", .{event_name}) catch return;
|
|
147
|
+
defer self.allocator.free(full_name);
|
|
148
|
+
|
|
149
|
+
event_bus.emit(full_name, &id_bytes);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
pub fn getTextBuffer(self: *EditBuffer) *UnifiedTextBuffer {
|
|
153
|
+
return self.tb;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
pub fn getCursor(self: *const EditBuffer, idx: usize) ?Cursor {
|
|
157
|
+
if (idx >= self.cursors.items.len) return null;
|
|
158
|
+
return self.cursors.items[idx];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
pub fn getPrimaryCursor(self: *const EditBuffer) Cursor {
|
|
162
|
+
if (self.cursors.items.len == 0) return .{ .row = 0, .col = 0 };
|
|
163
|
+
return self.cursors.items[0];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
pub fn setCursor(self: *EditBuffer, row: u32, col: u32) !void {
|
|
167
|
+
const line_count = self.tb.lineCount();
|
|
168
|
+
const clamped_row = @min(row, line_count -| 1);
|
|
169
|
+
|
|
170
|
+
const line_width = iter_mod.lineWidthAt(self.tb.rope(), clamped_row);
|
|
171
|
+
const clamped_col = @min(col, line_width);
|
|
172
|
+
|
|
173
|
+
const offset = iter_mod.coordsToOffset(self.tb.rope(), clamped_row, clamped_col) orelse 0;
|
|
174
|
+
|
|
175
|
+
if (self.cursors.items.len == 0) {
|
|
176
|
+
try self.cursors.append(self.allocator, .{ .row = clamped_row, .col = clamped_col, .desired_col = clamped_col, .offset = offset });
|
|
177
|
+
} else {
|
|
178
|
+
self.cursors.items[0] = .{ .row = clamped_row, .col = clamped_col, .desired_col = clamped_col, .offset = offset };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
self.events.emit(.cursorChanged);
|
|
182
|
+
self.emitNativeEvent("cursor-changed");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
pub fn setCursorByOffset(self: *EditBuffer, offset: u32) !void {
|
|
186
|
+
const coords = iter_mod.offsetToCoords(self.tb.rope(), offset) orelse iter_mod.Coords{ .row = 0, .col = 0 };
|
|
187
|
+
try self.setCursor(coords.row, coords.col);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
fn ensureAddCapacity(self: *EditBuffer, need: usize) !void {
|
|
191
|
+
try self.add_buffer.ensureCapacity(self.tb, need);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/// TODO: This method should live in text-buffer-segment.zig and the Rope should take it as comptime param
|
|
195
|
+
fn splitChunkAtWeight(
|
|
196
|
+
self: *EditBuffer,
|
|
197
|
+
chunk: *const TextChunk,
|
|
198
|
+
weight: u32,
|
|
199
|
+
) error{ OutOfBounds, OutOfMemory }!struct { left: TextChunk, right: TextChunk } {
|
|
200
|
+
const chunk_weight = chunk.width;
|
|
201
|
+
|
|
202
|
+
if (weight == 0) {
|
|
203
|
+
return .{
|
|
204
|
+
.left = TextChunk{ .mem_id = 0, .byte_start = 0, .byte_end = 0, .width = 0 },
|
|
205
|
+
.right = chunk.*,
|
|
206
|
+
};
|
|
207
|
+
} else if (weight >= chunk_weight) {
|
|
208
|
+
return .{
|
|
209
|
+
.left = chunk.*,
|
|
210
|
+
.right = TextChunk{ .mem_id = 0, .byte_start = 0, .byte_end = 0, .width = 0 },
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const chunk_bytes = chunk.getBytes(self.tb.memRegistry());
|
|
215
|
+
const is_ascii_only = (chunk.flags & TextChunk.Flags.ASCII_ONLY) != 0;
|
|
216
|
+
|
|
217
|
+
const result = utf8.findPosByWidth(chunk_bytes, weight, self.tb.tabWidth(), is_ascii_only, false, self.tb.widthMethod());
|
|
218
|
+
const split_byte_offset = result.byte_offset;
|
|
219
|
+
|
|
220
|
+
const left_chunk = self.tb.createChunk(
|
|
221
|
+
chunk.mem_id,
|
|
222
|
+
chunk.byte_start,
|
|
223
|
+
chunk.byte_start + split_byte_offset,
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const right_chunk = self.tb.createChunk(
|
|
227
|
+
chunk.mem_id,
|
|
228
|
+
chunk.byte_start + split_byte_offset,
|
|
229
|
+
chunk.byte_end,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
return .{ .left = left_chunk, .right = right_chunk };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
fn splitSegmentCallback(
|
|
236
|
+
ctx: ?*anyopaque,
|
|
237
|
+
allocator: Allocator,
|
|
238
|
+
leaf: *const Segment,
|
|
239
|
+
weight_in_leaf: u32,
|
|
240
|
+
) error{ OutOfBounds, OutOfMemory }!UnifiedRope.Node.LeafSplitResult {
|
|
241
|
+
_ = allocator;
|
|
242
|
+
const edit_buf = @as(*EditBuffer, @ptrCast(@alignCast(ctx.?)));
|
|
243
|
+
|
|
244
|
+
if (leaf.asText()) |chunk| {
|
|
245
|
+
const result = try edit_buf.splitChunkAtWeight(chunk, weight_in_leaf);
|
|
246
|
+
return .{
|
|
247
|
+
.left = Segment{ .text = result.left },
|
|
248
|
+
.right = Segment{ .text = result.right },
|
|
249
|
+
};
|
|
250
|
+
} else {
|
|
251
|
+
return .{
|
|
252
|
+
.left = Segment{ .brk = {} },
|
|
253
|
+
.right = Segment{ .brk = {} },
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
pub fn insertText(self: *EditBuffer, bytes: []const u8) !void {
|
|
259
|
+
if (bytes.len == 0) return;
|
|
260
|
+
if (self.cursors.items.len == 0) return;
|
|
261
|
+
|
|
262
|
+
try self.autoStoreUndo();
|
|
263
|
+
|
|
264
|
+
const cursor = self.cursors.items[0];
|
|
265
|
+
|
|
266
|
+
try self.ensureAddCapacity(bytes.len);
|
|
267
|
+
|
|
268
|
+
const insert_offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row, cursor.col) orelse return EditBufferError.InvalidCursor;
|
|
269
|
+
|
|
270
|
+
const chunk_ref = self.add_buffer.append(bytes);
|
|
271
|
+
const base_mem_id = chunk_ref.mem_id;
|
|
272
|
+
const base_start = chunk_ref.start;
|
|
273
|
+
|
|
274
|
+
var result = try self.tb.textToSegments(self.allocator, bytes, base_mem_id, base_start, false);
|
|
275
|
+
defer result.segments.deinit(result.allocator);
|
|
276
|
+
|
|
277
|
+
const inserted_width = result.total_width;
|
|
278
|
+
|
|
279
|
+
// Calculate width after last break
|
|
280
|
+
var width_after_last_break: u32 = 0;
|
|
281
|
+
var num_breaks: usize = 0;
|
|
282
|
+
for (result.segments.items) |seg| {
|
|
283
|
+
if (seg.isBreak()) {
|
|
284
|
+
num_breaks += 1;
|
|
285
|
+
width_after_last_break = 0;
|
|
286
|
+
} else if (seg.asText()) |chunk| {
|
|
287
|
+
width_after_last_break += chunk.width;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (result.segments.items.len > 0) {
|
|
292
|
+
try self.tb.rope().insertSliceByWeight(insert_offset, result.segments.items, &self.segment_splitter);
|
|
293
|
+
}
|
|
294
|
+
if (num_breaks > 0) {
|
|
295
|
+
const new_row = cursor.row + @as(u32, @intCast(num_breaks));
|
|
296
|
+
const new_col = width_after_last_break;
|
|
297
|
+
const new_offset = iter_mod.coordsToOffset(self.tb.rope(), new_row, new_col) orelse 0;
|
|
298
|
+
self.cursors.items[0] = .{
|
|
299
|
+
.row = new_row,
|
|
300
|
+
.col = new_col,
|
|
301
|
+
.desired_col = new_col,
|
|
302
|
+
.offset = new_offset,
|
|
303
|
+
};
|
|
304
|
+
} else {
|
|
305
|
+
const new_col = cursor.col + inserted_width;
|
|
306
|
+
const new_offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row, new_col) orelse 0;
|
|
307
|
+
self.cursors.items[0] = .{
|
|
308
|
+
.row = cursor.row,
|
|
309
|
+
.col = new_col,
|
|
310
|
+
.desired_col = new_col,
|
|
311
|
+
.offset = new_offset,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
self.tb.markViewsDirty();
|
|
316
|
+
self.events.emit(.cursorChanged);
|
|
317
|
+
self.emitNativeEvent("cursor-changed");
|
|
318
|
+
self.emitNativeEvent("content-changed");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
pub fn deleteRange(self: *EditBuffer, start_cursor: Cursor, end_cursor: Cursor) !void {
|
|
322
|
+
var start = start_cursor;
|
|
323
|
+
var end = end_cursor;
|
|
324
|
+
if (start.row > end.row or (start.row == end.row and start.col > end.col)) {
|
|
325
|
+
const temp = start;
|
|
326
|
+
start = end;
|
|
327
|
+
end = temp;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (start.row == end.row and start.col == end.col) return;
|
|
331
|
+
|
|
332
|
+
try self.autoStoreUndo();
|
|
333
|
+
|
|
334
|
+
const start_offset = iter_mod.coordsToOffset(self.tb.rope(), start.row, start.col) orelse return EditBufferError.InvalidCursor;
|
|
335
|
+
const end_offset = iter_mod.coordsToOffset(self.tb.rope(), end.row, end.col) orelse return EditBufferError.InvalidCursor;
|
|
336
|
+
|
|
337
|
+
if (start_offset >= end_offset) return;
|
|
338
|
+
|
|
339
|
+
try self.tb.rope().deleteRangeByWeight(start_offset, end_offset, &self.segment_splitter);
|
|
340
|
+
|
|
341
|
+
self.tb.markViewsDirty();
|
|
342
|
+
|
|
343
|
+
if (self.cursors.items.len > 0) {
|
|
344
|
+
const line_count = self.tb.lineCount();
|
|
345
|
+
const clamped_row = if (start.row >= line_count) line_count -| 1 else start.row;
|
|
346
|
+
const line_width = if (line_count > 0) iter_mod.lineWidthAt(self.tb.rope(), clamped_row) else 0;
|
|
347
|
+
const clamped_col = @min(start.col, line_width);
|
|
348
|
+
const offset = iter_mod.coordsToOffset(self.tb.rope(), clamped_row, clamped_col) orelse 0;
|
|
349
|
+
|
|
350
|
+
self.cursors.items[0] = .{ .row = clamped_row, .col = clamped_col, .desired_col = clamped_col, .offset = offset };
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
self.events.emit(.cursorChanged);
|
|
354
|
+
self.emitNativeEvent("cursor-changed");
|
|
355
|
+
self.emitNativeEvent("content-changed");
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
pub fn backspace(self: *EditBuffer) !void {
|
|
359
|
+
if (self.cursors.items.len == 0) return;
|
|
360
|
+
const cursor = self.cursors.items[0];
|
|
361
|
+
|
|
362
|
+
if (cursor.row == 0 and cursor.col == 0) return;
|
|
363
|
+
|
|
364
|
+
if (cursor.col == 0) {
|
|
365
|
+
if (cursor.row > 0) {
|
|
366
|
+
const prev_line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row - 1);
|
|
367
|
+
try self.deleteRange(
|
|
368
|
+
.{ .row = cursor.row - 1, .col = prev_line_width },
|
|
369
|
+
.{ .row = cursor.row, .col = 0 },
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
const prev_grapheme_width = self.tb.getPrevGraphemeWidth(cursor.row, cursor.col);
|
|
374
|
+
if (prev_grapheme_width == 0) return; // Nothing to delete
|
|
375
|
+
|
|
376
|
+
const target_col = cursor.col - prev_grapheme_width;
|
|
377
|
+
try self.deleteRange(
|
|
378
|
+
.{ .row = cursor.row, .col = target_col },
|
|
379
|
+
.{ .row = cursor.row, .col = cursor.col },
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// deleteRange already checks for placeholder insertion
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
pub fn deleteForward(self: *EditBuffer) !void {
|
|
387
|
+
if (self.cursors.items.len == 0) return;
|
|
388
|
+
const cursor = self.cursors.items[0];
|
|
389
|
+
|
|
390
|
+
try self.autoStoreUndo();
|
|
391
|
+
|
|
392
|
+
const line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row);
|
|
393
|
+
const line_count = self.tb.lineCount();
|
|
394
|
+
|
|
395
|
+
if (cursor.col >= line_width) {
|
|
396
|
+
if (cursor.row + 1 < line_count) {
|
|
397
|
+
try self.deleteRange(
|
|
398
|
+
.{ .row = cursor.row, .col = line_width },
|
|
399
|
+
.{ .row = cursor.row + 1, .col = 0 },
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
const grapheme_width = self.tb.getGraphemeWidthAt(cursor.row, cursor.col);
|
|
404
|
+
if (grapheme_width > 0) {
|
|
405
|
+
try self.deleteRange(
|
|
406
|
+
.{ .row = cursor.row, .col = cursor.col },
|
|
407
|
+
.{ .row = cursor.row, .col = cursor.col + grapheme_width },
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
pub fn moveLeft(self: *EditBuffer) void {
|
|
414
|
+
if (self.cursors.items.len == 0) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const cursor = &self.cursors.items[0];
|
|
418
|
+
|
|
419
|
+
if (cursor.col > 0) {
|
|
420
|
+
const prev_width = self.tb.getPrevGraphemeWidth(cursor.row, cursor.col);
|
|
421
|
+
cursor.col -= prev_width;
|
|
422
|
+
} else if (cursor.row > 0) {
|
|
423
|
+
cursor.row -= 1;
|
|
424
|
+
const line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row);
|
|
425
|
+
cursor.col = line_width;
|
|
426
|
+
}
|
|
427
|
+
cursor.desired_col = cursor.col;
|
|
428
|
+
cursor.offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row, cursor.col) orelse 0;
|
|
429
|
+
|
|
430
|
+
self.events.emit(.cursorChanged);
|
|
431
|
+
self.emitNativeEvent("cursor-changed");
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
pub fn moveRight(self: *EditBuffer) void {
|
|
435
|
+
if (self.cursors.items.len == 0) return;
|
|
436
|
+
const cursor = &self.cursors.items[0];
|
|
437
|
+
|
|
438
|
+
const line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row);
|
|
439
|
+
const line_count = self.tb.getLineCount();
|
|
440
|
+
|
|
441
|
+
if (cursor.col < line_width) {
|
|
442
|
+
const grapheme_width = self.tb.getGraphemeWidthAt(cursor.row, cursor.col);
|
|
443
|
+
cursor.col += grapheme_width;
|
|
444
|
+
} else if (cursor.row + 1 < line_count) {
|
|
445
|
+
cursor.row += 1;
|
|
446
|
+
cursor.col = 0;
|
|
447
|
+
}
|
|
448
|
+
cursor.desired_col = cursor.col;
|
|
449
|
+
cursor.offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row, cursor.col) orelse 0;
|
|
450
|
+
|
|
451
|
+
self.events.emit(.cursorChanged);
|
|
452
|
+
self.emitNativeEvent("cursor-changed");
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
pub fn moveUp(self: *EditBuffer) void {
|
|
456
|
+
if (self.cursors.items.len == 0) return;
|
|
457
|
+
const cursor = &self.cursors.items[0];
|
|
458
|
+
|
|
459
|
+
if (cursor.row > 0) {
|
|
460
|
+
if (cursor.desired_col == 0) {
|
|
461
|
+
cursor.desired_col = cursor.col;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
cursor.row -= 1;
|
|
465
|
+
|
|
466
|
+
const line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row);
|
|
467
|
+
|
|
468
|
+
cursor.col = @min(cursor.desired_col, line_width);
|
|
469
|
+
cursor.offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row, cursor.col) orelse 0;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
self.events.emit(.cursorChanged);
|
|
473
|
+
self.emitNativeEvent("cursor-changed");
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
pub fn moveDown(self: *EditBuffer) void {
|
|
477
|
+
if (self.cursors.items.len == 0) return;
|
|
478
|
+
const cursor = &self.cursors.items[0];
|
|
479
|
+
|
|
480
|
+
const line_count = self.tb.getLineCount();
|
|
481
|
+
if (cursor.row + 1 < line_count) {
|
|
482
|
+
if (cursor.desired_col == 0) {
|
|
483
|
+
cursor.desired_col = cursor.col;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
cursor.row += 1;
|
|
487
|
+
|
|
488
|
+
const line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row);
|
|
489
|
+
|
|
490
|
+
cursor.col = @min(cursor.desired_col, line_width);
|
|
491
|
+
cursor.offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row, cursor.col) orelse 0;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
self.events.emit(.cursorChanged);
|
|
495
|
+
self.emitNativeEvent("cursor-changed");
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/// Set text and completely reset the buffer state (clears history, resets add_buffer)
|
|
499
|
+
pub fn setText(self: *EditBuffer, text: []const u8) !void {
|
|
500
|
+
const owned_text = try self.allocator.dupe(u8, text);
|
|
501
|
+
const mem_id = try self.tb.registerMemBuffer(owned_text, true);
|
|
502
|
+
try self.setTextFromMemId(mem_id);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/// Set text from memory ID and completely reset the buffer state (clears history, resets add_buffer)
|
|
506
|
+
pub fn setTextFromMemId(self: *EditBuffer, mem_id: u8) !void {
|
|
507
|
+
self.tb.rope().clear_history();
|
|
508
|
+
self.add_buffer.len = 0;
|
|
509
|
+
|
|
510
|
+
try self.tb.setTextFromMemId(mem_id);
|
|
511
|
+
try self.setCursor(0, 0);
|
|
512
|
+
|
|
513
|
+
self.emitNativeEvent("content-changed");
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/// Replace text while preserving undo history (creates an undo point)
|
|
517
|
+
pub fn replaceText(self: *EditBuffer, text: []const u8) !void {
|
|
518
|
+
const owned_text = try self.allocator.dupe(u8, text);
|
|
519
|
+
const mem_id = try self.tb.registerMemBuffer(owned_text, true);
|
|
520
|
+
try self.replaceTextFromMemId(mem_id);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/// Replace text from memory ID while preserving undo history (creates an undo point)
|
|
524
|
+
pub fn replaceTextFromMemId(self: *EditBuffer, mem_id: u8) !void {
|
|
525
|
+
try self.autoStoreUndo();
|
|
526
|
+
|
|
527
|
+
try self.tb.setTextFromMemId(mem_id);
|
|
528
|
+
try self.setCursor(0, 0);
|
|
529
|
+
|
|
530
|
+
self.emitNativeEvent("content-changed");
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
pub fn getText(self: *EditBuffer, out_buffer: []u8) usize {
|
|
534
|
+
return self.tb.getPlainTextIntoBuffer(out_buffer);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
pub fn deleteLine(self: *EditBuffer) !void {
|
|
538
|
+
const cursor = self.getPrimaryCursor();
|
|
539
|
+
const line_count = self.tb.lineCount();
|
|
540
|
+
|
|
541
|
+
if (cursor.row >= line_count) return;
|
|
542
|
+
|
|
543
|
+
if (cursor.row + 1 < line_count) {
|
|
544
|
+
try self.deleteRange(
|
|
545
|
+
.{ .row = cursor.row, .col = 0 },
|
|
546
|
+
.{ .row = cursor.row + 1, .col = 0 },
|
|
547
|
+
);
|
|
548
|
+
} else if (cursor.row > 0) {
|
|
549
|
+
const prev_line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row - 1);
|
|
550
|
+
const curr_line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row);
|
|
551
|
+
|
|
552
|
+
try self.deleteRange(
|
|
553
|
+
.{ .row = cursor.row - 1, .col = prev_line_width },
|
|
554
|
+
.{ .row = cursor.row, .col = curr_line_width },
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
self.tb.markViewsDirty();
|
|
558
|
+
|
|
559
|
+
const new_row = cursor.row - 1;
|
|
560
|
+
const new_col = prev_line_width;
|
|
561
|
+
const new_offset = iter_mod.coordsToOffset(self.tb.rope(), new_row, new_col) orelse 0;
|
|
562
|
+
self.cursors.items[0] = .{ .row = new_row, .col = new_col, .desired_col = new_col, .offset = new_offset };
|
|
563
|
+
self.events.emit(.cursorChanged);
|
|
564
|
+
self.emitNativeEvent("cursor-changed");
|
|
565
|
+
} else {
|
|
566
|
+
const line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row);
|
|
567
|
+
if (line_width > 0) {
|
|
568
|
+
try self.deleteRange(
|
|
569
|
+
.{ .row = cursor.row, .col = 0 },
|
|
570
|
+
.{ .row = cursor.row, .col = line_width },
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
pub fn gotoLine(self: *EditBuffer, line: u32) !void {
|
|
577
|
+
const line_count = self.tb.lineCount();
|
|
578
|
+
const target_line = @min(line, line_count -| 1);
|
|
579
|
+
|
|
580
|
+
if (line >= line_count) {
|
|
581
|
+
const last_line_width = iter_mod.lineWidthAt(self.tb.rope(), target_line);
|
|
582
|
+
try self.setCursor(target_line, last_line_width);
|
|
583
|
+
} else {
|
|
584
|
+
try self.setCursor(target_line, 0);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
pub fn getCursorPosition(self: *const EditBuffer) struct { line: u32, visual_col: u32, offset: u32 } {
|
|
589
|
+
const cursor = self.getPrimaryCursor();
|
|
590
|
+
|
|
591
|
+
return .{
|
|
592
|
+
.line = cursor.row,
|
|
593
|
+
.visual_col = cursor.col,
|
|
594
|
+
.offset = cursor.offset,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
pub fn debugLogRope(self: *const EditBuffer) void {
|
|
599
|
+
self.tb.debugLogRope();
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
fn autoStoreUndo(self: *EditBuffer) !void {
|
|
603
|
+
try self.tb.rope().store_undo("edit");
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
pub fn undo(self: *EditBuffer) ![]const u8 {
|
|
607
|
+
const prev_meta = try self.tb.rope().undo("current");
|
|
608
|
+
|
|
609
|
+
const cursor = self.getPrimaryCursor();
|
|
610
|
+
try self.setCursor(cursor.row, cursor.col);
|
|
611
|
+
|
|
612
|
+
self.tb.markViewsDirty();
|
|
613
|
+
self.events.emit(.cursorChanged);
|
|
614
|
+
self.emitNativeEvent("cursorChanged");
|
|
615
|
+
|
|
616
|
+
return prev_meta;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
pub fn redo(self: *EditBuffer) ![]const u8 {
|
|
620
|
+
const next_meta = try self.tb.rope().redo();
|
|
621
|
+
|
|
622
|
+
const cursor = self.getPrimaryCursor();
|
|
623
|
+
try self.setCursor(cursor.row, cursor.col);
|
|
624
|
+
|
|
625
|
+
self.tb.markViewsDirty();
|
|
626
|
+
self.events.emit(.cursorChanged);
|
|
627
|
+
self.emitNativeEvent("cursorChanged");
|
|
628
|
+
|
|
629
|
+
return next_meta;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
pub fn canUndo(self: *const EditBuffer) bool {
|
|
633
|
+
return self.tb.rope().can_undo();
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
pub fn canRedo(self: *const EditBuffer) bool {
|
|
637
|
+
return self.tb.rope().can_redo();
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
pub fn clearHistory(self: *EditBuffer) void {
|
|
641
|
+
self.tb.rope().clear_history();
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
pub fn clear(self: *EditBuffer) !void {
|
|
645
|
+
self.tb.clear();
|
|
646
|
+
try self.setCursor(0, 0);
|
|
647
|
+
self.emitNativeEvent("content-changed");
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
pub fn getNextWordBoundary(self: *EditBuffer) Cursor {
|
|
651
|
+
if (self.cursors.items.len == 0) return .{ .row = 0, .col = 0 };
|
|
652
|
+
const cursor = self.cursors.items[0];
|
|
653
|
+
|
|
654
|
+
const line_count = self.tb.lineCount();
|
|
655
|
+
if (cursor.row >= line_count) return cursor;
|
|
656
|
+
|
|
657
|
+
const line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row);
|
|
658
|
+
|
|
659
|
+
const linestart = self.tb.rope().getMarker(.linestart, cursor.row) orelse return cursor;
|
|
660
|
+
var seg_idx = linestart.leaf_index + 1;
|
|
661
|
+
var cols_before: u32 = 0;
|
|
662
|
+
var passed_cursor = false;
|
|
663
|
+
|
|
664
|
+
while (seg_idx < self.tb.rope().count()) : (seg_idx += 1) {
|
|
665
|
+
const seg = self.tb.rope().get(seg_idx) orelse break;
|
|
666
|
+
if (seg.isBreak() or seg.isLineStart()) break;
|
|
667
|
+
if (seg.asText()) |chunk| {
|
|
668
|
+
const next_cols = cols_before + chunk.width;
|
|
669
|
+
|
|
670
|
+
// Check this chunk if cursor is within it OR if we've already passed the cursor
|
|
671
|
+
if (cursor.col < next_cols or passed_cursor) {
|
|
672
|
+
const wrap_offsets = self.tb.getWrapOffsetsFor(chunk) catch {
|
|
673
|
+
cols_before = next_cols;
|
|
674
|
+
passed_cursor = true;
|
|
675
|
+
continue;
|
|
676
|
+
};
|
|
677
|
+
const is_ascii_only = (chunk.flags & TextChunk.Flags.ASCII_ONLY) != 0;
|
|
678
|
+
const graphemes: []const seg_mod.GraphemeInfo = if (is_ascii_only)
|
|
679
|
+
&[_]seg_mod.GraphemeInfo{}
|
|
680
|
+
else
|
|
681
|
+
chunk.getGraphemes(self.tb.memRegistry(), self.tb.getAllocator(), self.tb.tabWidth(), self.tb.widthMethod()) catch &[_]seg_mod.GraphemeInfo{};
|
|
682
|
+
var grapheme_idx: usize = 0;
|
|
683
|
+
var col_delta: i64 = 0;
|
|
684
|
+
|
|
685
|
+
// For chunks containing or after the cursor, find the first break after cursor position
|
|
686
|
+
const local_cursor_col = if (cursor.col > cols_before) cursor.col - cols_before else 0;
|
|
687
|
+
|
|
688
|
+
for (wrap_offsets) |wrap_break| {
|
|
689
|
+
const break_info = iter_mod.charOffsetToColumn(wrap_break.char_offset, graphemes, &grapheme_idx, &col_delta);
|
|
690
|
+
const break_col = break_info.col;
|
|
691
|
+
|
|
692
|
+
// If we've passed the cursor chunk, any break is valid
|
|
693
|
+
// If we're in the cursor chunk, break must be after cursor position
|
|
694
|
+
if (passed_cursor or break_col > local_cursor_col) {
|
|
695
|
+
// break_col points at the break grapheme start.
|
|
696
|
+
// Adding width moves the cursor to the boundary after it.
|
|
697
|
+
const target_col = cols_before + break_col + break_info.width;
|
|
698
|
+
if (target_col <= line_width) {
|
|
699
|
+
const offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row, target_col) orelse cursor.offset;
|
|
700
|
+
return .{ .row = cursor.row, .col = target_col, .desired_col = target_col, .offset = offset };
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// A boundary at the cursor can still be the next word step
|
|
705
|
+
// for script-transition cases like "a日", "日a", or "丽abc".
|
|
706
|
+
// Only accept it when the boundary starts on a word codepoint.
|
|
707
|
+
if (!passed_cursor and break_col == local_cursor_col) {
|
|
708
|
+
const break_byte_offset: usize = @intCast(wrap_break.byte_offset);
|
|
709
|
+
const chunk_bytes = chunk.getBytes(self.tb.memRegistry());
|
|
710
|
+
if (break_byte_offset < chunk_bytes.len) {
|
|
711
|
+
const break_cp = utf8.decodeUtf8Unchecked(chunk_bytes, break_byte_offset).cp;
|
|
712
|
+
if (utf8.isWordCodepoint(break_cp)) {
|
|
713
|
+
const target_col = cols_before + break_col + break_info.width;
|
|
714
|
+
if (target_col <= line_width) {
|
|
715
|
+
const offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row, target_col) orelse cursor.offset;
|
|
716
|
+
return .{ .row = cursor.row, .col = target_col, .desired_col = target_col, .offset = offset };
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Mark that we've processed/passed the cursor position
|
|
724
|
+
passed_cursor = true;
|
|
725
|
+
}
|
|
726
|
+
cols_before = next_cols;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
if (cursor.row + 1 < line_count) {
|
|
731
|
+
const offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row + 1, 0) orelse cursor.offset;
|
|
732
|
+
return .{ .row = cursor.row + 1, .col = 0, .desired_col = 0, .offset = offset };
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row, line_width) orelse cursor.offset;
|
|
736
|
+
return .{ .row = cursor.row, .col = line_width, .desired_col = line_width, .offset = offset };
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
pub fn getPrevWordBoundary(self: *EditBuffer) Cursor {
|
|
740
|
+
if (self.cursors.items.len == 0) return .{ .row = 0, .col = 0 };
|
|
741
|
+
const cursor = self.cursors.items[0];
|
|
742
|
+
|
|
743
|
+
if (cursor.row == 0 and cursor.col == 0) return cursor;
|
|
744
|
+
|
|
745
|
+
const linestart = self.tb.rope().getMarker(.linestart, cursor.row) orelse return cursor;
|
|
746
|
+
var seg_idx = linestart.leaf_index + 1;
|
|
747
|
+
var cols_before: u32 = 0;
|
|
748
|
+
var last_boundary: ?u32 = null;
|
|
749
|
+
|
|
750
|
+
while (seg_idx < self.tb.rope().count()) : (seg_idx += 1) {
|
|
751
|
+
const seg = self.tb.rope().get(seg_idx) orelse break;
|
|
752
|
+
if (seg.isBreak() or seg.isLineStart()) break;
|
|
753
|
+
if (seg.asText()) |chunk| {
|
|
754
|
+
const next_cols = cols_before + chunk.width;
|
|
755
|
+
|
|
756
|
+
const wrap_offsets = self.tb.getWrapOffsetsFor(chunk) catch {
|
|
757
|
+
cols_before = next_cols;
|
|
758
|
+
continue;
|
|
759
|
+
};
|
|
760
|
+
const is_ascii_only = (chunk.flags & TextChunk.Flags.ASCII_ONLY) != 0;
|
|
761
|
+
const graphemes: []const seg_mod.GraphemeInfo = if (is_ascii_only)
|
|
762
|
+
&[_]seg_mod.GraphemeInfo{}
|
|
763
|
+
else
|
|
764
|
+
chunk.getGraphemes(self.tb.memRegistry(), self.tb.getAllocator(), self.tb.tabWidth(), self.tb.widthMethod()) catch &[_]seg_mod.GraphemeInfo{};
|
|
765
|
+
var grapheme_idx: usize = 0;
|
|
766
|
+
var col_delta: i64 = 0;
|
|
767
|
+
|
|
768
|
+
for (wrap_offsets) |wrap_break| {
|
|
769
|
+
const break_info = iter_mod.charOffsetToColumn(wrap_break.char_offset, graphemes, &grapheme_idx, &col_delta);
|
|
770
|
+
// break_info follows the same convention as getNextWordBoundary:
|
|
771
|
+
// use break start + grapheme width to land after the break grapheme.
|
|
772
|
+
const boundary_col = cols_before + break_info.col + break_info.width;
|
|
773
|
+
if (boundary_col < cursor.col) {
|
|
774
|
+
last_boundary = boundary_col;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
cols_before = next_cols;
|
|
779
|
+
if (cursor.col <= cols_before) break;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
if (last_boundary) |boundary_col| {
|
|
784
|
+
const offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row, boundary_col) orelse cursor.offset;
|
|
785
|
+
return .{ .row = cursor.row, .col = boundary_col, .desired_col = boundary_col, .offset = offset };
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (cursor.row > 0) {
|
|
789
|
+
const prev_line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row - 1);
|
|
790
|
+
const offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row - 1, prev_line_width) orelse cursor.offset;
|
|
791
|
+
return .{ .row = cursor.row - 1, .col = prev_line_width, .desired_col = prev_line_width, .offset = offset };
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
return .{ .row = 0, .col = 0, .desired_col = 0, .offset = 0 };
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
pub fn getEOL(self: *EditBuffer) Cursor {
|
|
798
|
+
if (self.cursors.items.len == 0) return .{ .row = 0, .col = 0 };
|
|
799
|
+
const cursor = self.cursors.items[0];
|
|
800
|
+
|
|
801
|
+
const line_count = self.tb.lineCount();
|
|
802
|
+
if (cursor.row >= line_count) return cursor;
|
|
803
|
+
|
|
804
|
+
const line_width = iter_mod.lineWidthAt(self.tb.rope(), cursor.row);
|
|
805
|
+
const offset = iter_mod.coordsToOffset(self.tb.rope(), cursor.row, line_width) orelse cursor.offset;
|
|
806
|
+
|
|
807
|
+
return .{ .row = cursor.row, .col = line_width, .desired_col = line_width, .offset = offset };
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/// Get text within a range of display-width offsets
|
|
811
|
+
/// Automatically snaps to grapheme boundaries:
|
|
812
|
+
/// - start_offset excludes graphemes that start before it
|
|
813
|
+
/// - end_offset includes graphemes that start before it
|
|
814
|
+
/// Returns number of bytes written to out_buffer
|
|
815
|
+
pub fn getTextRange(self: *EditBuffer, start_offset: u32, end_offset: u32, out_buffer: []u8) !usize {
|
|
816
|
+
return self.tb.getTextRange(start_offset, end_offset, out_buffer);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/// Get text within a range specified by row/col coordinates
|
|
820
|
+
/// Automatically snaps to grapheme boundaries:
|
|
821
|
+
/// Returns number of bytes written to out_buffer
|
|
822
|
+
pub fn getTextRangeByCoords(self: *EditBuffer, start_row: u32, start_col: u32, end_row: u32, end_col: u32, out_buffer: []u8) usize {
|
|
823
|
+
return self.tb.getTextRangeByCoords(start_row, start_col, end_row, end_col, out_buffer);
|
|
824
|
+
}
|
|
825
|
+
};
|