@fairyhunter13/opentui-core 0.1.113 → 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 +62 -53
- 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-9vwc3fg6.js +0 -12260
- package/index-9vwc3fg6.js.map +0 -42
- package/index-dcj62y8t.js +0 -20614
- package/index-dcj62y8t.js.map +0 -67
- package/index-f7n39gpy.js +0 -411
- package/index-f7n39gpy.js.map +0 -10
- 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,1344 @@
|
|
|
1
|
+
import { MeasureMode } from "yoga-layout"
|
|
2
|
+
import { type RenderableOptions, Renderable } from "../Renderable.js"
|
|
3
|
+
import type { OptimizedBuffer } from "../buffer.js"
|
|
4
|
+
import { type BorderStyle, BorderCharArrays, parseBorderStyle } from "../lib/border.js"
|
|
5
|
+
import { convertGlobalToLocalSelection, type Selection, type LocalSelectionBounds } from "../lib/selection.js"
|
|
6
|
+
import { StyledText, stringToStyledText } from "../lib/styled-text.js"
|
|
7
|
+
import { RGBA, parseColor, type ColorInput } from "../lib/RGBA.js"
|
|
8
|
+
import { SyntaxStyle } from "../syntax-style.js"
|
|
9
|
+
import { type TextChunk, TextBuffer } from "../text-buffer.js"
|
|
10
|
+
import { TextBufferView } from "../text-buffer-view.js"
|
|
11
|
+
import type { RenderContext } from "../types.js"
|
|
12
|
+
|
|
13
|
+
// Large sentinel height for text measurement. The Zig measure path currently
|
|
14
|
+
// ignores height, but we pass an effectively unbounded value so if height-aware
|
|
15
|
+
// measuring is introduced later, table sizing remains stable.
|
|
16
|
+
const MEASURE_HEIGHT = 10_000
|
|
17
|
+
|
|
18
|
+
export type TextTableCellContent = TextChunk[] | null | undefined
|
|
19
|
+
export type TextTableContent = TextTableCellContent[][]
|
|
20
|
+
export type TextTableColumnWidthMode = "content" | "full"
|
|
21
|
+
export type TextTableColumnFitter = "proportional" | "balanced"
|
|
22
|
+
|
|
23
|
+
interface ResolvedTableBorderLayout {
|
|
24
|
+
left: boolean
|
|
25
|
+
right: boolean
|
|
26
|
+
top: boolean
|
|
27
|
+
bottom: boolean
|
|
28
|
+
innerVertical: boolean
|
|
29
|
+
innerHorizontal: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface TextTableCellState {
|
|
33
|
+
textBuffer: TextBuffer
|
|
34
|
+
textBufferView: TextBufferView
|
|
35
|
+
syntaxStyle: SyntaxStyle
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface TextTableLayout {
|
|
39
|
+
columnWidths: number[]
|
|
40
|
+
rowHeights: number[]
|
|
41
|
+
columnOffsets: number[]
|
|
42
|
+
rowOffsets: number[]
|
|
43
|
+
columnOffsetsI32: Int32Array
|
|
44
|
+
rowOffsetsI32: Int32Array
|
|
45
|
+
tableWidth: number
|
|
46
|
+
tableHeight: number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface CellPosition {
|
|
50
|
+
rowIdx: number
|
|
51
|
+
colIdx: number
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface RowRange {
|
|
55
|
+
firstRow: number
|
|
56
|
+
lastRow: number
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type TableSelectionMode = "single-cell" | "column-locked" | "grid"
|
|
60
|
+
|
|
61
|
+
interface SelectionResolution {
|
|
62
|
+
mode: TableSelectionMode
|
|
63
|
+
anchorCell: CellPosition | null
|
|
64
|
+
anchorColumn: number | null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface CellSelectionCoords {
|
|
68
|
+
anchorX: number
|
|
69
|
+
anchorY: number
|
|
70
|
+
focusX: number
|
|
71
|
+
focusY: number
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface TextTableOptions extends RenderableOptions<TextTableRenderable> {
|
|
75
|
+
content?: TextTableContent
|
|
76
|
+
wrapMode?: "none" | "char" | "word"
|
|
77
|
+
columnWidthMode?: TextTableColumnWidthMode
|
|
78
|
+
columnFitter?: TextTableColumnFitter
|
|
79
|
+
cellPadding?: number
|
|
80
|
+
showBorders?: boolean
|
|
81
|
+
border?: boolean
|
|
82
|
+
outerBorder?: boolean
|
|
83
|
+
selectable?: boolean
|
|
84
|
+
selectionBg?: ColorInput
|
|
85
|
+
selectionFg?: ColorInput
|
|
86
|
+
borderStyle?: BorderStyle
|
|
87
|
+
borderColor?: ColorInput
|
|
88
|
+
borderBackgroundColor?: ColorInput
|
|
89
|
+
backgroundColor?: ColorInput
|
|
90
|
+
fg?: ColorInput
|
|
91
|
+
bg?: ColorInput
|
|
92
|
+
attributes?: number
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class TextTableRenderable extends Renderable {
|
|
96
|
+
private _content: TextTableContent
|
|
97
|
+
private _wrapMode: "none" | "char" | "word"
|
|
98
|
+
private _columnWidthMode: TextTableColumnWidthMode
|
|
99
|
+
private _columnFitter: TextTableColumnFitter
|
|
100
|
+
private _cellPadding: number
|
|
101
|
+
private _showBorders: boolean
|
|
102
|
+
private _border: boolean
|
|
103
|
+
private _outerBorder: boolean
|
|
104
|
+
private _hasExplicitOuterBorder: boolean
|
|
105
|
+
private _borderStyle: BorderStyle
|
|
106
|
+
private _borderColor: RGBA
|
|
107
|
+
private _borderBackgroundColor: RGBA
|
|
108
|
+
private _backgroundColor: RGBA
|
|
109
|
+
private _defaultFg: RGBA
|
|
110
|
+
private _defaultBg: RGBA
|
|
111
|
+
private _defaultAttributes: number
|
|
112
|
+
private _selectionBg: RGBA | undefined
|
|
113
|
+
private _selectionFg: RGBA | undefined
|
|
114
|
+
private _lastLocalSelection: LocalSelectionBounds | null = null
|
|
115
|
+
private _lastSelectionMode: TableSelectionMode | null = null
|
|
116
|
+
|
|
117
|
+
private _cells: TextTableCellState[][] = []
|
|
118
|
+
private _prevCellContent: TextTableCellContent[][] = []
|
|
119
|
+
private _rowCount: number = 0
|
|
120
|
+
private _columnCount: number = 0
|
|
121
|
+
|
|
122
|
+
private _layout: TextTableLayout = this.createEmptyLayout()
|
|
123
|
+
private _layoutDirty: boolean = true
|
|
124
|
+
private _rasterDirty: boolean = true
|
|
125
|
+
|
|
126
|
+
private _cachedMeasureLayout: TextTableLayout | null = null
|
|
127
|
+
private _cachedMeasureWidth: number | undefined = undefined
|
|
128
|
+
|
|
129
|
+
private readonly _defaultOptions = {
|
|
130
|
+
content: [] as TextTableContent,
|
|
131
|
+
wrapMode: "word" as "none" | "char" | "word",
|
|
132
|
+
columnWidthMode: "full" as TextTableColumnWidthMode,
|
|
133
|
+
columnFitter: "proportional" as TextTableColumnFitter,
|
|
134
|
+
cellPadding: 0,
|
|
135
|
+
showBorders: true,
|
|
136
|
+
border: true,
|
|
137
|
+
outerBorder: true,
|
|
138
|
+
selectable: true,
|
|
139
|
+
selectionBg: undefined as ColorInput | undefined,
|
|
140
|
+
selectionFg: undefined as ColorInput | undefined,
|
|
141
|
+
borderStyle: "single" as BorderStyle,
|
|
142
|
+
borderColor: "#FFFFFF",
|
|
143
|
+
borderBackgroundColor: "transparent",
|
|
144
|
+
backgroundColor: "transparent",
|
|
145
|
+
fg: "#FFFFFF",
|
|
146
|
+
bg: "transparent",
|
|
147
|
+
attributes: 0,
|
|
148
|
+
} satisfies Partial<TextTableOptions>
|
|
149
|
+
|
|
150
|
+
constructor(ctx: RenderContext, options: TextTableOptions = {}) {
|
|
151
|
+
super(ctx, { ...options, flexShrink: options.flexShrink ?? 0, buffered: true })
|
|
152
|
+
|
|
153
|
+
this._content = options.content ?? this._defaultOptions.content
|
|
154
|
+
this._wrapMode = options.wrapMode ?? this._defaultOptions.wrapMode
|
|
155
|
+
this._columnWidthMode = options.columnWidthMode ?? this._defaultOptions.columnWidthMode
|
|
156
|
+
this._columnFitter = this.resolveColumnFitter(options.columnFitter)
|
|
157
|
+
this._cellPadding = this.resolveCellPadding(options.cellPadding)
|
|
158
|
+
this._showBorders = options.showBorders ?? this._defaultOptions.showBorders
|
|
159
|
+
this._border = options.border ?? this._defaultOptions.border
|
|
160
|
+
this._hasExplicitOuterBorder = options.outerBorder !== undefined
|
|
161
|
+
this._outerBorder = options.outerBorder ?? this._border
|
|
162
|
+
this.selectable = options.selectable ?? this._defaultOptions.selectable
|
|
163
|
+
this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : undefined
|
|
164
|
+
this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : undefined
|
|
165
|
+
this._borderStyle = parseBorderStyle(options.borderStyle, this._defaultOptions.borderStyle)
|
|
166
|
+
this._borderColor = parseColor(options.borderColor ?? this._defaultOptions.borderColor)
|
|
167
|
+
this._borderBackgroundColor = parseColor(
|
|
168
|
+
options.borderBackgroundColor ?? this._defaultOptions.borderBackgroundColor,
|
|
169
|
+
)
|
|
170
|
+
this._backgroundColor = parseColor(options.backgroundColor ?? this._defaultOptions.backgroundColor)
|
|
171
|
+
this._defaultFg = parseColor(options.fg ?? this._defaultOptions.fg)
|
|
172
|
+
this._defaultBg = parseColor(options.bg ?? this._defaultOptions.bg)
|
|
173
|
+
this._defaultAttributes = options.attributes ?? this._defaultOptions.attributes
|
|
174
|
+
|
|
175
|
+
this.setupMeasureFunc()
|
|
176
|
+
this.rebuildCells()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public get content(): TextTableContent {
|
|
180
|
+
return this._content
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public set content(value: TextTableContent) {
|
|
184
|
+
this._content = value ?? []
|
|
185
|
+
this.rebuildCells()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
public get wrapMode(): "none" | "char" | "word" {
|
|
189
|
+
return this._wrapMode
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public set wrapMode(value: "none" | "char" | "word") {
|
|
193
|
+
if (this._wrapMode === value) return
|
|
194
|
+
this._wrapMode = value
|
|
195
|
+
for (const row of this._cells) {
|
|
196
|
+
for (const cell of row) {
|
|
197
|
+
cell.textBufferView.setWrapMode(value)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
this.invalidateLayoutAndRaster()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
public get columnWidthMode(): TextTableColumnWidthMode {
|
|
204
|
+
return this._columnWidthMode
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public set columnWidthMode(value: TextTableColumnWidthMode) {
|
|
208
|
+
if (this._columnWidthMode === value) return
|
|
209
|
+
this._columnWidthMode = value
|
|
210
|
+
this.invalidateLayoutAndRaster()
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
public get columnFitter(): TextTableColumnFitter {
|
|
214
|
+
return this._columnFitter
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
public set columnFitter(value: TextTableColumnFitter) {
|
|
218
|
+
const next = this.resolveColumnFitter(value)
|
|
219
|
+
if (this._columnFitter === next) return
|
|
220
|
+
this._columnFitter = next
|
|
221
|
+
this.invalidateLayoutAndRaster()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
public get cellPadding(): number {
|
|
225
|
+
return this._cellPadding
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
public set cellPadding(value: number) {
|
|
229
|
+
const next = this.resolveCellPadding(value)
|
|
230
|
+
if (this._cellPadding === next) return
|
|
231
|
+
this._cellPadding = next
|
|
232
|
+
this.invalidateLayoutAndRaster()
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
public get showBorders(): boolean {
|
|
236
|
+
return this._showBorders
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
public set showBorders(value: boolean) {
|
|
240
|
+
if (this._showBorders === value) return
|
|
241
|
+
this._showBorders = value
|
|
242
|
+
this.invalidateRasterOnly()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public get outerBorder(): boolean {
|
|
246
|
+
return this._outerBorder
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
public set outerBorder(value: boolean) {
|
|
250
|
+
if (this._outerBorder === value) return
|
|
251
|
+
|
|
252
|
+
this._hasExplicitOuterBorder = true
|
|
253
|
+
this._outerBorder = value
|
|
254
|
+
this.invalidateLayoutAndRaster()
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
public get border(): boolean {
|
|
258
|
+
return this._border
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
public set border(value: boolean) {
|
|
262
|
+
if (this._border === value) return
|
|
263
|
+
|
|
264
|
+
this._border = value
|
|
265
|
+
|
|
266
|
+
if (!this._hasExplicitOuterBorder) {
|
|
267
|
+
this._outerBorder = value
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
this.invalidateLayoutAndRaster()
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
public get borderStyle(): BorderStyle {
|
|
274
|
+
return this._borderStyle
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
public set borderStyle(value: BorderStyle) {
|
|
278
|
+
const next = parseBorderStyle(value, this._defaultOptions.borderStyle)
|
|
279
|
+
if (this._borderStyle === next) return
|
|
280
|
+
this._borderStyle = next
|
|
281
|
+
this.invalidateRasterOnly()
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
public get borderColor(): RGBA {
|
|
285
|
+
return this._borderColor
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
public set borderColor(value: ColorInput) {
|
|
289
|
+
const next = parseColor(value)
|
|
290
|
+
if (this._borderColor === next) return
|
|
291
|
+
this._borderColor = next
|
|
292
|
+
this.invalidateRasterOnly()
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
public override shouldStartSelection(x: number, y: number): boolean {
|
|
296
|
+
if (!this.selectable) return false
|
|
297
|
+
|
|
298
|
+
this.ensureLayoutReady()
|
|
299
|
+
|
|
300
|
+
const localX = x - this.x
|
|
301
|
+
const localY = y - this.y
|
|
302
|
+
return this.getCellAtLocalPosition(localX, localY) !== null
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
public override onSelectionChanged(selection: Selection | null): boolean {
|
|
306
|
+
this.ensureLayoutReady()
|
|
307
|
+
|
|
308
|
+
const previousLocalSelection = this._lastLocalSelection
|
|
309
|
+
const localSelection = convertGlobalToLocalSelection(selection, this.x, this.y)
|
|
310
|
+
this._lastLocalSelection = localSelection
|
|
311
|
+
const dirtyRows = this.getDirtySelectionRowRange(previousLocalSelection, localSelection)
|
|
312
|
+
|
|
313
|
+
if (!localSelection?.isActive) {
|
|
314
|
+
this.resetCellSelections()
|
|
315
|
+
this._lastSelectionMode = null
|
|
316
|
+
} else {
|
|
317
|
+
this.applySelectionToCells(localSelection, selection?.isStart ?? false)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (dirtyRows !== null) {
|
|
321
|
+
this.redrawSelectionRows(dirtyRows.firstRow, dirtyRows.lastRow)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return this.hasSelection()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
public override hasSelection(): boolean {
|
|
328
|
+
for (const row of this._cells) {
|
|
329
|
+
for (const cell of row) {
|
|
330
|
+
if (cell.textBufferView.hasSelection()) {
|
|
331
|
+
return true
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return false
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
public getSelection(): { start: number; end: number } | null {
|
|
340
|
+
for (const row of this._cells) {
|
|
341
|
+
for (const cell of row) {
|
|
342
|
+
const selection = cell.textBufferView.getSelection()
|
|
343
|
+
if (selection) {
|
|
344
|
+
return selection
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return null
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
public override getSelectedText(): string {
|
|
353
|
+
const selectedRows: string[] = []
|
|
354
|
+
|
|
355
|
+
for (let rowIdx = 0; rowIdx < this._rowCount; rowIdx++) {
|
|
356
|
+
const rowSelections: string[] = []
|
|
357
|
+
|
|
358
|
+
for (let colIdx = 0; colIdx < this._columnCount; colIdx++) {
|
|
359
|
+
const cell = this._cells[rowIdx]?.[colIdx]
|
|
360
|
+
if (!cell || !cell.textBufferView.hasSelection()) continue
|
|
361
|
+
|
|
362
|
+
const selectedText = cell.textBufferView.getSelectedText()
|
|
363
|
+
if (selectedText.length > 0) {
|
|
364
|
+
rowSelections.push(selectedText)
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (rowSelections.length > 0) {
|
|
369
|
+
selectedRows.push(rowSelections.join("\t"))
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return selectedRows.join("\n")
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
protected override onResize(width: number, height: number): void {
|
|
377
|
+
this.invalidateLayoutAndRaster(false)
|
|
378
|
+
super.onResize(width, height)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
protected override renderSelf(buffer: OptimizedBuffer): void {
|
|
382
|
+
if (!this.visible || this.isDestroyed) return
|
|
383
|
+
|
|
384
|
+
if (this._layoutDirty) {
|
|
385
|
+
this.rebuildLayoutForCurrentWidth()
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (!this._rasterDirty) return
|
|
389
|
+
|
|
390
|
+
buffer.clear(this._backgroundColor)
|
|
391
|
+
|
|
392
|
+
if (this._rowCount === 0 || this._columnCount === 0) {
|
|
393
|
+
this._rasterDirty = false
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
this.drawBorders(buffer)
|
|
398
|
+
this.drawCells(buffer)
|
|
399
|
+
|
|
400
|
+
this._rasterDirty = false
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
protected override destroySelf(): void {
|
|
404
|
+
this.destroyCells()
|
|
405
|
+
super.destroySelf()
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private setupMeasureFunc(): void {
|
|
409
|
+
const measureFunc = (
|
|
410
|
+
width: number,
|
|
411
|
+
widthMode: MeasureMode,
|
|
412
|
+
_height: number,
|
|
413
|
+
_heightMode: MeasureMode,
|
|
414
|
+
): { width: number; height: number } => {
|
|
415
|
+
const hasWidthConstraint = widthMode !== MeasureMode.Undefined && Number.isFinite(width)
|
|
416
|
+
const rawWidthConstraint = hasWidthConstraint ? Math.max(1, Math.floor(width)) : undefined
|
|
417
|
+
const widthConstraint = this.resolveLayoutWidthConstraint(rawWidthConstraint)
|
|
418
|
+
const measuredLayout = this.computeLayout(widthConstraint)
|
|
419
|
+
this._cachedMeasureLayout = measuredLayout
|
|
420
|
+
this._cachedMeasureWidth = widthConstraint
|
|
421
|
+
|
|
422
|
+
let measuredWidth = measuredLayout.tableWidth > 0 ? measuredLayout.tableWidth : 1
|
|
423
|
+
let measuredHeight = measuredLayout.tableHeight > 0 ? measuredLayout.tableHeight : 1
|
|
424
|
+
|
|
425
|
+
if (widthMode === MeasureMode.AtMost && rawWidthConstraint !== undefined && this._positionType !== "absolute") {
|
|
426
|
+
measuredWidth = Math.min(rawWidthConstraint, measuredWidth)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Keep intrinsic height even under AtMost constraints. Clamping here can under-report
|
|
430
|
+
// content height during Yoga measure passes and leave parent scroll extents stale.
|
|
431
|
+
return {
|
|
432
|
+
width: measuredWidth,
|
|
433
|
+
height: measuredHeight,
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
this.yogaNode.setMeasureFunc(measureFunc)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
private rebuildCells(): void {
|
|
441
|
+
const newRowCount = this._content.length
|
|
442
|
+
const newColumnCount = this._content.reduce((max, row) => Math.max(max, row.length), 0)
|
|
443
|
+
|
|
444
|
+
if (this._cells.length === 0) {
|
|
445
|
+
this._rowCount = newRowCount
|
|
446
|
+
this._columnCount = newColumnCount
|
|
447
|
+
this._cells = []
|
|
448
|
+
this._prevCellContent = []
|
|
449
|
+
|
|
450
|
+
for (let rowIdx = 0; rowIdx < newRowCount; rowIdx++) {
|
|
451
|
+
const row = this._content[rowIdx] ?? []
|
|
452
|
+
const rowCells: TextTableCellState[] = []
|
|
453
|
+
const rowRefs: TextTableCellContent[] = []
|
|
454
|
+
|
|
455
|
+
for (let colIdx = 0; colIdx < newColumnCount; colIdx++) {
|
|
456
|
+
const cellContent = row[colIdx]
|
|
457
|
+
rowCells.push(this.createCell(cellContent))
|
|
458
|
+
rowRefs.push(cellContent)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
this._cells.push(rowCells)
|
|
462
|
+
this._prevCellContent.push(rowRefs)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
this.invalidateLayoutAndRaster()
|
|
466
|
+
return
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
this.updateCellsDiff(newRowCount, newColumnCount)
|
|
470
|
+
this.invalidateLayoutAndRaster()
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
private updateCellsDiff(newRowCount: number, newColumnCount: number): void {
|
|
474
|
+
const oldRowCount = this._rowCount
|
|
475
|
+
const oldColumnCount = this._columnCount
|
|
476
|
+
const keepRows = Math.min(oldRowCount, newRowCount)
|
|
477
|
+
const keepCols = Math.min(oldColumnCount, newColumnCount)
|
|
478
|
+
|
|
479
|
+
for (let rowIdx = 0; rowIdx < keepRows; rowIdx++) {
|
|
480
|
+
const newRow = this._content[rowIdx] ?? []
|
|
481
|
+
const cellRow = this._cells[rowIdx]
|
|
482
|
+
const refRow = this._prevCellContent[rowIdx]
|
|
483
|
+
|
|
484
|
+
for (let colIdx = 0; colIdx < keepCols; colIdx++) {
|
|
485
|
+
const cellContent = newRow[colIdx]
|
|
486
|
+
if (cellContent === refRow[colIdx]) continue
|
|
487
|
+
|
|
488
|
+
const oldCell = cellRow[colIdx]
|
|
489
|
+
oldCell.textBufferView.destroy()
|
|
490
|
+
oldCell.textBuffer.destroy()
|
|
491
|
+
oldCell.syntaxStyle.destroy()
|
|
492
|
+
|
|
493
|
+
cellRow[colIdx] = this.createCell(cellContent)
|
|
494
|
+
refRow[colIdx] = cellContent
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (newColumnCount > oldColumnCount) {
|
|
498
|
+
for (let colIdx = oldColumnCount; colIdx < newColumnCount; colIdx++) {
|
|
499
|
+
const cellContent = newRow[colIdx]
|
|
500
|
+
cellRow.push(this.createCell(cellContent))
|
|
501
|
+
refRow.push(cellContent)
|
|
502
|
+
}
|
|
503
|
+
} else if (newColumnCount < oldColumnCount) {
|
|
504
|
+
for (let colIdx = newColumnCount; colIdx < oldColumnCount; colIdx++) {
|
|
505
|
+
const cell = cellRow[colIdx]
|
|
506
|
+
cell.textBufferView.destroy()
|
|
507
|
+
cell.textBuffer.destroy()
|
|
508
|
+
cell.syntaxStyle.destroy()
|
|
509
|
+
}
|
|
510
|
+
cellRow.length = newColumnCount
|
|
511
|
+
refRow.length = newColumnCount
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (newRowCount > oldRowCount) {
|
|
516
|
+
for (let rowIdx = oldRowCount; rowIdx < newRowCount; rowIdx++) {
|
|
517
|
+
const newRow = this._content[rowIdx] ?? []
|
|
518
|
+
const rowCells: TextTableCellState[] = []
|
|
519
|
+
const rowRefs: TextTableCellContent[] = []
|
|
520
|
+
|
|
521
|
+
for (let colIdx = 0; colIdx < newColumnCount; colIdx++) {
|
|
522
|
+
const cellContent = newRow[colIdx]
|
|
523
|
+
rowCells.push(this.createCell(cellContent))
|
|
524
|
+
rowRefs.push(cellContent)
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
this._cells.push(rowCells)
|
|
528
|
+
this._prevCellContent.push(rowRefs)
|
|
529
|
+
}
|
|
530
|
+
} else if (newRowCount < oldRowCount) {
|
|
531
|
+
for (let rowIdx = newRowCount; rowIdx < oldRowCount; rowIdx++) {
|
|
532
|
+
const row = this._cells[rowIdx]
|
|
533
|
+
for (const cell of row) {
|
|
534
|
+
cell.textBufferView.destroy()
|
|
535
|
+
cell.textBuffer.destroy()
|
|
536
|
+
cell.syntaxStyle.destroy()
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
this._cells.length = newRowCount
|
|
540
|
+
this._prevCellContent.length = newRowCount
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
this._rowCount = newRowCount
|
|
544
|
+
this._columnCount = newColumnCount
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
private createCell(content: TextTableCellContent): TextTableCellState {
|
|
548
|
+
const styledText = this.toStyledText(content)
|
|
549
|
+
const textBuffer = TextBuffer.create(this._ctx.widthMethod)
|
|
550
|
+
const syntaxStyle = SyntaxStyle.create()
|
|
551
|
+
|
|
552
|
+
textBuffer.setDefaultFg(this._defaultFg)
|
|
553
|
+
textBuffer.setDefaultBg(this._defaultBg)
|
|
554
|
+
textBuffer.setDefaultAttributes(this._defaultAttributes)
|
|
555
|
+
textBuffer.setSyntaxStyle(syntaxStyle)
|
|
556
|
+
textBuffer.setStyledText(styledText)
|
|
557
|
+
|
|
558
|
+
const textBufferView = TextBufferView.create(textBuffer)
|
|
559
|
+
textBufferView.setWrapMode(this._wrapMode)
|
|
560
|
+
|
|
561
|
+
return { textBuffer, textBufferView, syntaxStyle }
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
private toStyledText(content: TextTableCellContent): StyledText {
|
|
565
|
+
if (Array.isArray(content)) {
|
|
566
|
+
return new StyledText(content)
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (content === null || content === undefined) {
|
|
570
|
+
return stringToStyledText("")
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return stringToStyledText(String(content))
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
private destroyCells(): void {
|
|
577
|
+
for (const row of this._cells) {
|
|
578
|
+
for (const cell of row) {
|
|
579
|
+
cell.textBufferView.destroy()
|
|
580
|
+
cell.textBuffer.destroy()
|
|
581
|
+
cell.syntaxStyle.destroy()
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
this._cells = []
|
|
586
|
+
this._prevCellContent = []
|
|
587
|
+
this._rowCount = 0
|
|
588
|
+
this._columnCount = 0
|
|
589
|
+
this._layout = this.createEmptyLayout()
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
private rebuildLayoutForCurrentWidth(): void {
|
|
593
|
+
const maxTableWidth = this.resolveLayoutWidthConstraint(this.width)
|
|
594
|
+
|
|
595
|
+
let layout: TextTableLayout
|
|
596
|
+
if (this._cachedMeasureLayout !== null && this._cachedMeasureWidth === maxTableWidth) {
|
|
597
|
+
layout = this._cachedMeasureLayout
|
|
598
|
+
} else {
|
|
599
|
+
layout = this.computeLayout(maxTableWidth)
|
|
600
|
+
}
|
|
601
|
+
this._cachedMeasureLayout = null
|
|
602
|
+
this._cachedMeasureWidth = undefined
|
|
603
|
+
|
|
604
|
+
this._layout = layout
|
|
605
|
+
this.applyLayoutToViews(layout)
|
|
606
|
+
this._layoutDirty = false
|
|
607
|
+
|
|
608
|
+
if (this._lastLocalSelection?.isActive) {
|
|
609
|
+
this.applySelectionToCells(this._lastLocalSelection, true)
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
private computeLayout(maxTableWidth?: number): TextTableLayout {
|
|
614
|
+
if (this._rowCount === 0 || this._columnCount === 0) {
|
|
615
|
+
return this.createEmptyLayout()
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const borderLayout = this.resolveBorderLayout()
|
|
619
|
+
const columnWidths = this.computeColumnWidths(maxTableWidth, borderLayout)
|
|
620
|
+
const rowHeights = this.computeRowHeights(columnWidths)
|
|
621
|
+
const columnOffsets = this.computeOffsets(
|
|
622
|
+
columnWidths,
|
|
623
|
+
borderLayout.left,
|
|
624
|
+
borderLayout.right,
|
|
625
|
+
borderLayout.innerVertical,
|
|
626
|
+
)
|
|
627
|
+
const rowOffsets = this.computeOffsets(
|
|
628
|
+
rowHeights,
|
|
629
|
+
borderLayout.top,
|
|
630
|
+
borderLayout.bottom,
|
|
631
|
+
borderLayout.innerHorizontal,
|
|
632
|
+
)
|
|
633
|
+
return {
|
|
634
|
+
columnWidths,
|
|
635
|
+
rowHeights,
|
|
636
|
+
columnOffsets,
|
|
637
|
+
rowOffsets,
|
|
638
|
+
columnOffsetsI32: new Int32Array(columnOffsets),
|
|
639
|
+
rowOffsetsI32: new Int32Array(rowOffsets),
|
|
640
|
+
tableWidth: (columnOffsets[columnOffsets.length - 1] ?? 0) + 1,
|
|
641
|
+
tableHeight: (rowOffsets[rowOffsets.length - 1] ?? 0) + 1,
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
private isFullWidthMode(): boolean {
|
|
646
|
+
return this._columnWidthMode === "full"
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
private computeColumnWidths(maxTableWidth: number | undefined, borderLayout: ResolvedTableBorderLayout): number[] {
|
|
650
|
+
const horizontalPadding = this.getHorizontalCellPadding()
|
|
651
|
+
const intrinsicWidths = new Array(this._columnCount).fill(1 + horizontalPadding)
|
|
652
|
+
|
|
653
|
+
for (let rowIdx = 0; rowIdx < this._rowCount; rowIdx++) {
|
|
654
|
+
for (let colIdx = 0; colIdx < this._columnCount; colIdx++) {
|
|
655
|
+
const cell = this._cells[rowIdx]?.[colIdx]
|
|
656
|
+
if (!cell) continue
|
|
657
|
+
|
|
658
|
+
const measure = cell.textBufferView.measureForDimensions(0, MEASURE_HEIGHT)
|
|
659
|
+
const measuredWidth = Math.max(1, measure?.widthColsMax ?? 0) + horizontalPadding
|
|
660
|
+
intrinsicWidths[colIdx] = Math.max(intrinsicWidths[colIdx], measuredWidth)
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (maxTableWidth === undefined || !Number.isFinite(maxTableWidth) || maxTableWidth <= 0) {
|
|
665
|
+
return intrinsicWidths
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const maxContentWidth = Math.max(1, Math.floor(maxTableWidth) - this.getVerticalBorderCount(borderLayout))
|
|
669
|
+
const currentWidth = intrinsicWidths.reduce((sum, width) => sum + width, 0)
|
|
670
|
+
|
|
671
|
+
if (currentWidth === maxContentWidth) {
|
|
672
|
+
return intrinsicWidths
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (currentWidth < maxContentWidth) {
|
|
676
|
+
if (this.isFullWidthMode()) {
|
|
677
|
+
return this.expandColumnWidths(intrinsicWidths, maxContentWidth)
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return intrinsicWidths
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (this._wrapMode === "none") {
|
|
684
|
+
return intrinsicWidths
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
return this.fitColumnWidths(intrinsicWidths, maxContentWidth)
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
private expandColumnWidths(widths: number[], targetContentWidth: number): number[] {
|
|
691
|
+
const baseWidths = widths.map((width) => Math.max(1, Math.floor(width)))
|
|
692
|
+
const totalBaseWidth = baseWidths.reduce((sum, width) => sum + width, 0)
|
|
693
|
+
|
|
694
|
+
if (totalBaseWidth >= targetContentWidth) {
|
|
695
|
+
return baseWidths
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const expanded = [...baseWidths]
|
|
699
|
+
const columns = expanded.length
|
|
700
|
+
const extraWidth = targetContentWidth - totalBaseWidth
|
|
701
|
+
const sharedWidth = Math.floor(extraWidth / columns)
|
|
702
|
+
const remainder = extraWidth % columns
|
|
703
|
+
|
|
704
|
+
for (let idx = 0; idx < columns; idx++) {
|
|
705
|
+
expanded[idx] += sharedWidth
|
|
706
|
+
if (idx < remainder) {
|
|
707
|
+
expanded[idx] += 1
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return expanded
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
private fitColumnWidths(widths: number[], targetContentWidth: number): number[] {
|
|
715
|
+
if (this._columnFitter === "balanced") {
|
|
716
|
+
return this.fitColumnWidthsBalanced(widths, targetContentWidth)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return this.fitColumnWidthsProportional(widths, targetContentWidth)
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
private fitColumnWidthsProportional(widths: number[], targetContentWidth: number): number[] {
|
|
723
|
+
const minWidth = 1 + this.getHorizontalCellPadding()
|
|
724
|
+
const hardMinWidths = new Array(widths.length).fill(minWidth)
|
|
725
|
+
const baseWidths = widths.map((width) => Math.max(1, Math.floor(width)))
|
|
726
|
+
|
|
727
|
+
const preferredMinWidths = baseWidths.map((width) => Math.min(width, minWidth + 1))
|
|
728
|
+
const preferredMinTotal = preferredMinWidths.reduce((sum, width) => sum + width, 0)
|
|
729
|
+
|
|
730
|
+
const floorWidths = preferredMinTotal <= targetContentWidth ? preferredMinWidths : hardMinWidths
|
|
731
|
+
const floorTotal = floorWidths.reduce((sum, width) => sum + width, 0)
|
|
732
|
+
const clampedTarget = Math.max(floorTotal, targetContentWidth)
|
|
733
|
+
|
|
734
|
+
const totalBaseWidth = baseWidths.reduce((sum, width) => sum + width, 0)
|
|
735
|
+
|
|
736
|
+
if (totalBaseWidth <= clampedTarget) {
|
|
737
|
+
return baseWidths
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const shrinkable = baseWidths.map((width, idx) => width - floorWidths[idx])
|
|
741
|
+
const totalShrinkable = shrinkable.reduce((sum, value) => sum + value, 0)
|
|
742
|
+
if (totalShrinkable <= 0) {
|
|
743
|
+
return [...floorWidths]
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const targetShrink = totalBaseWidth - clampedTarget
|
|
747
|
+
const integerShrink = new Array(baseWidths.length).fill(0)
|
|
748
|
+
const fractions = new Array(baseWidths.length).fill(0)
|
|
749
|
+
let usedShrink = 0
|
|
750
|
+
|
|
751
|
+
for (let idx = 0; idx < baseWidths.length; idx++) {
|
|
752
|
+
if (shrinkable[idx] <= 0) continue
|
|
753
|
+
|
|
754
|
+
const exact = (shrinkable[idx] / totalShrinkable) * targetShrink
|
|
755
|
+
const whole = Math.min(shrinkable[idx], Math.floor(exact))
|
|
756
|
+
integerShrink[idx] = whole
|
|
757
|
+
fractions[idx] = exact - whole
|
|
758
|
+
usedShrink += whole
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
let remainingShrink = targetShrink - usedShrink
|
|
762
|
+
|
|
763
|
+
while (remainingShrink > 0) {
|
|
764
|
+
let bestIdx = -1
|
|
765
|
+
let bestFraction = -1
|
|
766
|
+
|
|
767
|
+
for (let idx = 0; idx < baseWidths.length; idx++) {
|
|
768
|
+
if (shrinkable[idx] - integerShrink[idx] <= 0) continue
|
|
769
|
+
if (fractions[idx] > bestFraction) {
|
|
770
|
+
bestFraction = fractions[idx]
|
|
771
|
+
bestIdx = idx
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
if (bestIdx === -1) break
|
|
776
|
+
|
|
777
|
+
integerShrink[bestIdx] += 1
|
|
778
|
+
fractions[bestIdx] = 0
|
|
779
|
+
remainingShrink -= 1
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return baseWidths.map((width, idx) => Math.max(floorWidths[idx], width - integerShrink[idx]))
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
private fitColumnWidthsBalanced(widths: number[], targetContentWidth: number): number[] {
|
|
786
|
+
const minWidth = 1 + this.getHorizontalCellPadding()
|
|
787
|
+
const hardMinWidths = new Array(widths.length).fill(minWidth)
|
|
788
|
+
const baseWidths = widths.map((width) => Math.max(1, Math.floor(width)))
|
|
789
|
+
const totalBaseWidth = baseWidths.reduce((sum, width) => sum + width, 0)
|
|
790
|
+
const columns = baseWidths.length
|
|
791
|
+
|
|
792
|
+
if (columns === 0 || totalBaseWidth <= targetContentWidth) {
|
|
793
|
+
return baseWidths
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
const evenShare = Math.max(minWidth, Math.floor(targetContentWidth / columns))
|
|
797
|
+
const preferredMinWidths = baseWidths.map((width) => Math.min(width, evenShare))
|
|
798
|
+
const preferredMinTotal = preferredMinWidths.reduce((sum, width) => sum + width, 0)
|
|
799
|
+
const floorWidths = preferredMinTotal <= targetContentWidth ? preferredMinWidths : hardMinWidths
|
|
800
|
+
const floorTotal = floorWidths.reduce((sum, width) => sum + width, 0)
|
|
801
|
+
const clampedTarget = Math.max(floorTotal, targetContentWidth)
|
|
802
|
+
|
|
803
|
+
if (totalBaseWidth <= clampedTarget) {
|
|
804
|
+
return baseWidths
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const shrinkable = baseWidths.map((width, idx) => width - floorWidths[idx])
|
|
808
|
+
const totalShrinkable = shrinkable.reduce((sum, value) => sum + value, 0)
|
|
809
|
+
if (totalShrinkable <= 0) {
|
|
810
|
+
return [...floorWidths]
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
const targetShrink = totalBaseWidth - clampedTarget
|
|
814
|
+
const shrink = this.allocateShrinkByWeight(shrinkable, targetShrink, "sqrt")
|
|
815
|
+
|
|
816
|
+
return baseWidths.map((width, idx) => Math.max(floorWidths[idx], width - shrink[idx]))
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
private allocateShrinkByWeight(shrinkable: number[], targetShrink: number, mode: "linear" | "sqrt"): number[] {
|
|
820
|
+
const shrink = new Array(shrinkable.length).fill(0)
|
|
821
|
+
|
|
822
|
+
if (targetShrink <= 0) {
|
|
823
|
+
return shrink
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
const weights = shrinkable.map((value) => {
|
|
827
|
+
if (value <= 0) {
|
|
828
|
+
return 0
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
return mode === "sqrt" ? Math.sqrt(value) : value
|
|
832
|
+
})
|
|
833
|
+
const totalWeight = weights.reduce((sum, value) => sum + value, 0)
|
|
834
|
+
|
|
835
|
+
if (totalWeight <= 0) {
|
|
836
|
+
return shrink
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
const fractions = new Array(shrinkable.length).fill(0)
|
|
840
|
+
let usedShrink = 0
|
|
841
|
+
|
|
842
|
+
for (let idx = 0; idx < shrinkable.length; idx++) {
|
|
843
|
+
if (shrinkable[idx] <= 0 || weights[idx] <= 0) continue
|
|
844
|
+
|
|
845
|
+
const exact = (weights[idx] / totalWeight) * targetShrink
|
|
846
|
+
const whole = Math.min(shrinkable[idx], Math.floor(exact))
|
|
847
|
+
shrink[idx] = whole
|
|
848
|
+
fractions[idx] = exact - whole
|
|
849
|
+
usedShrink += whole
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
let remainingShrink = targetShrink - usedShrink
|
|
853
|
+
|
|
854
|
+
while (remainingShrink > 0) {
|
|
855
|
+
let bestIdx = -1
|
|
856
|
+
let bestFraction = -1
|
|
857
|
+
|
|
858
|
+
for (let idx = 0; idx < shrinkable.length; idx++) {
|
|
859
|
+
if (shrinkable[idx] - shrink[idx] <= 0) continue
|
|
860
|
+
|
|
861
|
+
if (
|
|
862
|
+
bestIdx === -1 ||
|
|
863
|
+
fractions[idx] > bestFraction ||
|
|
864
|
+
(fractions[idx] === bestFraction && shrinkable[idx] > shrinkable[bestIdx])
|
|
865
|
+
) {
|
|
866
|
+
bestIdx = idx
|
|
867
|
+
bestFraction = fractions[idx]
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (bestIdx === -1) {
|
|
872
|
+
break
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
shrink[bestIdx] += 1
|
|
876
|
+
fractions[bestIdx] = 0
|
|
877
|
+
remainingShrink -= 1
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
return shrink
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
private computeRowHeights(columnWidths: number[]): number[] {
|
|
884
|
+
const horizontalPadding = this.getHorizontalCellPadding()
|
|
885
|
+
const verticalPadding = this.getVerticalCellPadding()
|
|
886
|
+
const rowHeights = new Array(this._rowCount).fill(1 + verticalPadding)
|
|
887
|
+
|
|
888
|
+
for (let rowIdx = 0; rowIdx < this._rowCount; rowIdx++) {
|
|
889
|
+
for (let colIdx = 0; colIdx < this._columnCount; colIdx++) {
|
|
890
|
+
const cell = this._cells[rowIdx]?.[colIdx]
|
|
891
|
+
if (!cell) continue
|
|
892
|
+
|
|
893
|
+
const width = Math.max(1, (columnWidths[colIdx] ?? 1) - horizontalPadding)
|
|
894
|
+
const measure = cell.textBufferView.measureForDimensions(width, MEASURE_HEIGHT)
|
|
895
|
+
const lineCount = Math.max(1, measure?.lineCount ?? 1)
|
|
896
|
+
rowHeights[rowIdx] = Math.max(rowHeights[rowIdx], lineCount + verticalPadding)
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
return rowHeights
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
private computeOffsets(
|
|
904
|
+
parts: number[],
|
|
905
|
+
startBoundary: boolean,
|
|
906
|
+
endBoundary: boolean,
|
|
907
|
+
includeInnerBoundaries: boolean,
|
|
908
|
+
): number[] {
|
|
909
|
+
const offsets: number[] = [startBoundary ? 0 : -1]
|
|
910
|
+
let cursor = offsets[0] ?? 0
|
|
911
|
+
|
|
912
|
+
for (let idx = 0; idx < parts.length; idx++) {
|
|
913
|
+
const size = parts[idx] ?? 1
|
|
914
|
+
const hasBoundaryAfter = idx < parts.length - 1 ? includeInnerBoundaries : endBoundary
|
|
915
|
+
cursor += size + (hasBoundaryAfter ? 1 : 0)
|
|
916
|
+
offsets.push(cursor)
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
return offsets
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
private applyLayoutToViews(layout: TextTableLayout): void {
|
|
923
|
+
const horizontalPadding = this.getHorizontalCellPadding()
|
|
924
|
+
const verticalPadding = this.getVerticalCellPadding()
|
|
925
|
+
|
|
926
|
+
for (let rowIdx = 0; rowIdx < this._rowCount; rowIdx++) {
|
|
927
|
+
for (let colIdx = 0; colIdx < this._columnCount; colIdx++) {
|
|
928
|
+
const cell = this._cells[rowIdx]?.[colIdx]
|
|
929
|
+
if (!cell) continue
|
|
930
|
+
|
|
931
|
+
const colWidth = layout.columnWidths[colIdx] ?? 1
|
|
932
|
+
const rowHeight = layout.rowHeights[rowIdx] ?? 1
|
|
933
|
+
const contentWidth = Math.max(1, colWidth - horizontalPadding)
|
|
934
|
+
const contentHeight = Math.max(1, rowHeight - verticalPadding)
|
|
935
|
+
|
|
936
|
+
if (this._wrapMode === "none") {
|
|
937
|
+
cell.textBufferView.setWrapWidth(null)
|
|
938
|
+
} else {
|
|
939
|
+
cell.textBufferView.setWrapWidth(contentWidth)
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
cell.textBufferView.setViewport(0, 0, contentWidth, contentHeight)
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
private resolveBorderLayout(): ResolvedTableBorderLayout {
|
|
948
|
+
return {
|
|
949
|
+
left: this._outerBorder,
|
|
950
|
+
right: this._outerBorder,
|
|
951
|
+
top: this._outerBorder,
|
|
952
|
+
bottom: this._outerBorder,
|
|
953
|
+
innerVertical: this._border && this._columnCount > 1,
|
|
954
|
+
innerHorizontal: this._border && this._rowCount > 1,
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
private getVerticalBorderCount(borderLayout: ResolvedTableBorderLayout): number {
|
|
959
|
+
return (
|
|
960
|
+
(borderLayout.left ? 1 : 0) +
|
|
961
|
+
(borderLayout.right ? 1 : 0) +
|
|
962
|
+
(borderLayout.innerVertical ? Math.max(0, this._columnCount - 1) : 0)
|
|
963
|
+
)
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
private getHorizontalBorderCount(borderLayout: ResolvedTableBorderLayout): number {
|
|
967
|
+
return (
|
|
968
|
+
(borderLayout.top ? 1 : 0) +
|
|
969
|
+
(borderLayout.bottom ? 1 : 0) +
|
|
970
|
+
(borderLayout.innerHorizontal ? Math.max(0, this._rowCount - 1) : 0)
|
|
971
|
+
)
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
private drawBorders(buffer: OptimizedBuffer): void {
|
|
975
|
+
if (!this._showBorders) {
|
|
976
|
+
return
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
const borderLayout = this.resolveBorderLayout()
|
|
980
|
+
|
|
981
|
+
if (this.getVerticalBorderCount(borderLayout) === 0 && this.getHorizontalBorderCount(borderLayout) === 0) {
|
|
982
|
+
return
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
buffer.drawGrid({
|
|
986
|
+
borderChars: BorderCharArrays[this._borderStyle],
|
|
987
|
+
borderFg: this._borderColor,
|
|
988
|
+
borderBg: this._borderBackgroundColor,
|
|
989
|
+
columnOffsets: this._layout.columnOffsetsI32,
|
|
990
|
+
rowOffsets: this._layout.rowOffsetsI32,
|
|
991
|
+
drawInner: this._border,
|
|
992
|
+
drawOuter: this._outerBorder,
|
|
993
|
+
})
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
private drawCells(buffer: OptimizedBuffer): void {
|
|
997
|
+
this.drawCellRange(buffer, 0, this._rowCount - 1)
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
private drawCellRange(buffer: OptimizedBuffer, firstRow: number, lastRow: number): void {
|
|
1001
|
+
const colOffsets = this._layout.columnOffsets
|
|
1002
|
+
const rowOffsets = this._layout.rowOffsets
|
|
1003
|
+
const cellPadding = this._cellPadding
|
|
1004
|
+
|
|
1005
|
+
for (let rowIdx = firstRow; rowIdx <= lastRow; rowIdx++) {
|
|
1006
|
+
const cellY = (rowOffsets[rowIdx] ?? 0) + 1 + cellPadding
|
|
1007
|
+
|
|
1008
|
+
for (let colIdx = 0; colIdx < this._columnCount; colIdx++) {
|
|
1009
|
+
const cell = this._cells[rowIdx]?.[colIdx]
|
|
1010
|
+
if (!cell) continue
|
|
1011
|
+
buffer.drawTextBuffer(cell.textBufferView, (colOffsets[colIdx] ?? 0) + 1 + cellPadding, cellY)
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
private redrawSelectionRows(firstRow: number, lastRow: number): void {
|
|
1017
|
+
if (firstRow > lastRow) return
|
|
1018
|
+
|
|
1019
|
+
if (this._backgroundColor.a < 1) {
|
|
1020
|
+
this.invalidateRasterOnly()
|
|
1021
|
+
return
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
const buffer = this.frameBuffer
|
|
1025
|
+
if (!buffer) return
|
|
1026
|
+
|
|
1027
|
+
this.clearCellRange(buffer, firstRow, lastRow)
|
|
1028
|
+
this.drawCellRange(buffer, firstRow, lastRow)
|
|
1029
|
+
this.requestRender()
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
private clearCellRange(buffer: OptimizedBuffer, firstRow: number, lastRow: number): void {
|
|
1033
|
+
const colWidths = this._layout.columnWidths
|
|
1034
|
+
const rowHeights = this._layout.rowHeights
|
|
1035
|
+
const colOffsets = this._layout.columnOffsets
|
|
1036
|
+
const rowOffsets = this._layout.rowOffsets
|
|
1037
|
+
|
|
1038
|
+
for (let rowIdx = firstRow; rowIdx <= lastRow; rowIdx++) {
|
|
1039
|
+
const cellY = (rowOffsets[rowIdx] ?? 0) + 1
|
|
1040
|
+
const rowHeight = rowHeights[rowIdx] ?? 1
|
|
1041
|
+
|
|
1042
|
+
for (let colIdx = 0; colIdx < this._columnCount; colIdx++) {
|
|
1043
|
+
const cellX = (colOffsets[colIdx] ?? 0) + 1
|
|
1044
|
+
const colWidth = colWidths[colIdx] ?? 1
|
|
1045
|
+
buffer.fillRect(cellX, cellY, colWidth, rowHeight, this._backgroundColor)
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
private ensureLayoutReady(): void {
|
|
1051
|
+
if (!this._layoutDirty) return
|
|
1052
|
+
this.rebuildLayoutForCurrentWidth()
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
private getCellAtLocalPosition(localX: number, localY: number): CellPosition | null {
|
|
1056
|
+
if (this._rowCount === 0 || this._columnCount === 0) return null
|
|
1057
|
+
if (localX < 0 || localY < 0 || localX >= this._layout.tableWidth || localY >= this._layout.tableHeight) {
|
|
1058
|
+
return null
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
let rowIdx = -1
|
|
1062
|
+
for (let idx = 0; idx < this._rowCount; idx++) {
|
|
1063
|
+
const top = (this._layout.rowOffsets[idx] ?? 0) + 1
|
|
1064
|
+
const bottom = top + (this._layout.rowHeights[idx] ?? 1) - 1
|
|
1065
|
+
if (localY >= top && localY <= bottom) {
|
|
1066
|
+
rowIdx = idx
|
|
1067
|
+
break
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if (rowIdx < 0) return null
|
|
1072
|
+
|
|
1073
|
+
let colIdx = -1
|
|
1074
|
+
for (let idx = 0; idx < this._columnCount; idx++) {
|
|
1075
|
+
const left = (this._layout.columnOffsets[idx] ?? 0) + 1
|
|
1076
|
+
const right = left + (this._layout.columnWidths[idx] ?? 1) - 1
|
|
1077
|
+
if (localX >= left && localX <= right) {
|
|
1078
|
+
colIdx = idx
|
|
1079
|
+
break
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
if (colIdx < 0) return null
|
|
1084
|
+
|
|
1085
|
+
return { rowIdx, colIdx }
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
private applySelectionToCells(localSelection: LocalSelectionBounds, isStart: boolean): void {
|
|
1089
|
+
const minSelY = Math.min(localSelection.anchorY, localSelection.focusY)
|
|
1090
|
+
const maxSelY = Math.max(localSelection.anchorY, localSelection.focusY)
|
|
1091
|
+
|
|
1092
|
+
const firstRow = this.findRowForLocalY(minSelY)
|
|
1093
|
+
const lastRow = this.findRowForLocalY(maxSelY)
|
|
1094
|
+
const selection = this.resolveSelectionResolution(localSelection)
|
|
1095
|
+
const modeChanged = this._lastSelectionMode !== selection.mode
|
|
1096
|
+
this._lastSelectionMode = selection.mode
|
|
1097
|
+
const lockToAnchorColumn = selection.mode === "column-locked" && selection.anchorColumn !== null
|
|
1098
|
+
|
|
1099
|
+
for (let rowIdx = 0; rowIdx < this._rowCount; rowIdx++) {
|
|
1100
|
+
if (rowIdx < firstRow || rowIdx > lastRow) {
|
|
1101
|
+
this.resetRowSelection(rowIdx)
|
|
1102
|
+
continue
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
const cellTop = (this._layout.rowOffsets[rowIdx] ?? 0) + 1 + this._cellPadding
|
|
1106
|
+
|
|
1107
|
+
for (let colIdx = 0; colIdx < this._columnCount; colIdx++) {
|
|
1108
|
+
const cell = this._cells[rowIdx]?.[colIdx]
|
|
1109
|
+
if (!cell) continue
|
|
1110
|
+
|
|
1111
|
+
if (lockToAnchorColumn && colIdx !== selection.anchorColumn) {
|
|
1112
|
+
cell.textBufferView.resetLocalSelection()
|
|
1113
|
+
continue
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
const cellLeft = (this._layout.columnOffsets[colIdx] ?? 0) + 1 + this._cellPadding
|
|
1117
|
+
let coords: CellSelectionCoords = {
|
|
1118
|
+
anchorX: localSelection.anchorX - cellLeft,
|
|
1119
|
+
anchorY: localSelection.anchorY - cellTop,
|
|
1120
|
+
focusX: localSelection.focusX - cellLeft,
|
|
1121
|
+
focusY: localSelection.focusY - cellTop,
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
const isAnchorCell =
|
|
1125
|
+
selection.anchorCell !== null &&
|
|
1126
|
+
selection.anchorCell.rowIdx === rowIdx &&
|
|
1127
|
+
selection.anchorCell.colIdx === colIdx
|
|
1128
|
+
const forceSet = isAnchorCell && selection.mode !== "single-cell"
|
|
1129
|
+
|
|
1130
|
+
if (forceSet) {
|
|
1131
|
+
coords = this.getFullCellSelectionCoords(rowIdx, colIdx)
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
const shouldUseSet = isStart || modeChanged || forceSet
|
|
1135
|
+
|
|
1136
|
+
if (shouldUseSet) {
|
|
1137
|
+
cell.textBufferView.setLocalSelection(
|
|
1138
|
+
coords.anchorX,
|
|
1139
|
+
coords.anchorY,
|
|
1140
|
+
coords.focusX,
|
|
1141
|
+
coords.focusY,
|
|
1142
|
+
this._selectionBg,
|
|
1143
|
+
this._selectionFg,
|
|
1144
|
+
)
|
|
1145
|
+
} else {
|
|
1146
|
+
cell.textBufferView.updateLocalSelection(
|
|
1147
|
+
coords.anchorX,
|
|
1148
|
+
coords.anchorY,
|
|
1149
|
+
coords.focusX,
|
|
1150
|
+
coords.focusY,
|
|
1151
|
+
this._selectionBg,
|
|
1152
|
+
this._selectionFg,
|
|
1153
|
+
)
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
private resolveSelectionResolution(localSelection: LocalSelectionBounds): SelectionResolution {
|
|
1160
|
+
const anchorCell = this.getCellAtLocalPosition(localSelection.anchorX, localSelection.anchorY)
|
|
1161
|
+
const focusCell = this.getCellAtLocalPosition(localSelection.focusX, localSelection.focusY)
|
|
1162
|
+
const anchorColumn = anchorCell?.colIdx ?? this.getColumnAtLocalX(localSelection.anchorX)
|
|
1163
|
+
|
|
1164
|
+
if (
|
|
1165
|
+
anchorCell !== null &&
|
|
1166
|
+
focusCell !== null &&
|
|
1167
|
+
anchorCell.rowIdx === focusCell.rowIdx &&
|
|
1168
|
+
anchorCell.colIdx === focusCell.colIdx
|
|
1169
|
+
) {
|
|
1170
|
+
return {
|
|
1171
|
+
mode: "single-cell",
|
|
1172
|
+
anchorCell,
|
|
1173
|
+
anchorColumn,
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
const focusColumn = this.getColumnAtLocalX(localSelection.focusX)
|
|
1178
|
+
if (anchorColumn !== null && focusColumn === anchorColumn) {
|
|
1179
|
+
return {
|
|
1180
|
+
mode: "column-locked",
|
|
1181
|
+
anchorCell,
|
|
1182
|
+
anchorColumn,
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
return {
|
|
1187
|
+
mode: "grid",
|
|
1188
|
+
anchorCell,
|
|
1189
|
+
anchorColumn,
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
private getColumnAtLocalX(localX: number): number | null {
|
|
1194
|
+
if (this._columnCount === 0) return null
|
|
1195
|
+
if (localX < 0 || localX >= this._layout.tableWidth) return null
|
|
1196
|
+
|
|
1197
|
+
for (let colIdx = 0; colIdx < this._columnCount; colIdx++) {
|
|
1198
|
+
const colStart = (this._layout.columnOffsets[colIdx] ?? 0) + 1
|
|
1199
|
+
const colEnd = colStart + (this._layout.columnWidths[colIdx] ?? 1) - 1
|
|
1200
|
+
if (localX >= colStart && localX <= colEnd) {
|
|
1201
|
+
return colIdx
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
return null
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
private getFullCellSelectionCoords(rowIdx: number, colIdx: number): CellSelectionCoords {
|
|
1209
|
+
const colWidth = this._layout.columnWidths[colIdx] ?? 1
|
|
1210
|
+
const rowHeight = this._layout.rowHeights[rowIdx] ?? 1
|
|
1211
|
+
const contentWidth = Math.max(1, colWidth - this.getHorizontalCellPadding())
|
|
1212
|
+
const contentHeight = Math.max(1, rowHeight - this.getVerticalCellPadding())
|
|
1213
|
+
|
|
1214
|
+
return {
|
|
1215
|
+
anchorX: -1,
|
|
1216
|
+
anchorY: 0,
|
|
1217
|
+
focusX: contentWidth,
|
|
1218
|
+
focusY: contentHeight,
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
private findRowForLocalY(localY: number): number {
|
|
1223
|
+
if (this._rowCount === 0) return 0
|
|
1224
|
+
if (localY < 0) return 0
|
|
1225
|
+
|
|
1226
|
+
for (let rowIdx = 0; rowIdx < this._rowCount; rowIdx++) {
|
|
1227
|
+
const rowStart = (this._layout.rowOffsets[rowIdx] ?? 0) + 1
|
|
1228
|
+
const rowEnd = rowStart + (this._layout.rowHeights[rowIdx] ?? 1) - 1
|
|
1229
|
+
if (localY <= rowEnd) return rowIdx
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
return this._rowCount - 1
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
private getSelectionRowRange(selection: LocalSelectionBounds | null): RowRange | null {
|
|
1236
|
+
if (!selection?.isActive || this._rowCount === 0) return null
|
|
1237
|
+
|
|
1238
|
+
const minSelY = Math.min(selection.anchorY, selection.focusY)
|
|
1239
|
+
const maxSelY = Math.max(selection.anchorY, selection.focusY)
|
|
1240
|
+
|
|
1241
|
+
return {
|
|
1242
|
+
firstRow: this.findRowForLocalY(minSelY),
|
|
1243
|
+
lastRow: this.findRowForLocalY(maxSelY),
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
private getDirtySelectionRowRange(
|
|
1248
|
+
previousSelection: LocalSelectionBounds | null,
|
|
1249
|
+
currentSelection: LocalSelectionBounds | null,
|
|
1250
|
+
): RowRange | null {
|
|
1251
|
+
const previousRange = this.getSelectionRowRange(previousSelection)
|
|
1252
|
+
const currentRange = this.getSelectionRowRange(currentSelection)
|
|
1253
|
+
|
|
1254
|
+
if (previousRange === null) return currentRange
|
|
1255
|
+
if (currentRange === null) return previousRange
|
|
1256
|
+
|
|
1257
|
+
return {
|
|
1258
|
+
firstRow: Math.min(previousRange.firstRow, currentRange.firstRow),
|
|
1259
|
+
lastRow: Math.max(previousRange.lastRow, currentRange.lastRow),
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
private resetRowSelection(rowIdx: number): void {
|
|
1264
|
+
const row = this._cells[rowIdx]
|
|
1265
|
+
if (!row) return
|
|
1266
|
+
|
|
1267
|
+
for (const cell of row) {
|
|
1268
|
+
cell.textBufferView.resetLocalSelection()
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
private resetCellSelections(): void {
|
|
1273
|
+
for (let rowIdx = 0; rowIdx < this._rowCount; rowIdx++) {
|
|
1274
|
+
this.resetRowSelection(rowIdx)
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
private createEmptyLayout(): TextTableLayout {
|
|
1279
|
+
return {
|
|
1280
|
+
columnWidths: [],
|
|
1281
|
+
rowHeights: [],
|
|
1282
|
+
columnOffsets: [0],
|
|
1283
|
+
rowOffsets: [0],
|
|
1284
|
+
columnOffsetsI32: new Int32Array([0]),
|
|
1285
|
+
rowOffsetsI32: new Int32Array([0]),
|
|
1286
|
+
tableWidth: 0,
|
|
1287
|
+
tableHeight: 0,
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
private resolveLayoutWidthConstraint(width: number | undefined): number | undefined {
|
|
1292
|
+
if (width === undefined || !Number.isFinite(width) || width <= 0) {
|
|
1293
|
+
return undefined
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
if (this._wrapMode !== "none" || this.isFullWidthMode()) {
|
|
1297
|
+
return Math.max(1, Math.floor(width))
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
return undefined
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
private getHorizontalCellPadding(): number {
|
|
1304
|
+
return this._cellPadding * 2
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
private getVerticalCellPadding(): number {
|
|
1308
|
+
return this._cellPadding * 2
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
private resolveColumnFitter(value: TextTableColumnFitter | undefined): TextTableColumnFitter {
|
|
1312
|
+
if (value === undefined) {
|
|
1313
|
+
return this._defaultOptions.columnFitter
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
return value === "balanced" ? "balanced" : "proportional"
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
private resolveCellPadding(value: number | undefined): number {
|
|
1320
|
+
if (value === undefined || !Number.isFinite(value)) {
|
|
1321
|
+
return this._defaultOptions.cellPadding
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
return Math.max(0, Math.floor(value))
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
private invalidateLayoutAndRaster(markYogaDirty: boolean = true): void {
|
|
1328
|
+
this._layoutDirty = true
|
|
1329
|
+
this._rasterDirty = true
|
|
1330
|
+
this._cachedMeasureLayout = null
|
|
1331
|
+
this._cachedMeasureWidth = undefined
|
|
1332
|
+
|
|
1333
|
+
if (markYogaDirty) {
|
|
1334
|
+
this.yogaNode.markDirty()
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
this.requestRender()
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
private invalidateRasterOnly(): void {
|
|
1341
|
+
this._rasterDirty = true
|
|
1342
|
+
this.requestRender()
|
|
1343
|
+
}
|
|
1344
|
+
}
|