@dst0/p-tui 0.4.40 → 5.0.2
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/dist/autocomplete.d.ts.map +1 -1
- package/dist/autocomplete.js.map +1 -1
- package/dist/components/box.d.ts +3 -1
- package/dist/components/box.d.ts.map +1 -1
- package/dist/components/box.js +19 -10
- package/dist/components/box.js.map +1 -1
- package/dist/components/cancellable-loader.d.ts.map +1 -1
- package/dist/components/cancellable-loader.js.map +1 -1
- package/dist/components/editor/constants.d.ts +9 -0
- package/dist/components/editor/constants.d.ts.map +1 -0
- package/dist/components/editor/constants.js +12 -0
- package/dist/components/editor/constants.js.map +1 -0
- package/dist/components/editor/editor-methods/autocomplete-matching.d.ts +29 -0
- package/dist/components/editor/editor-methods/autocomplete-matching.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/autocomplete-matching.js +104 -0
- package/dist/components/editor/editor-methods/autocomplete-matching.js.map +1 -0
- package/dist/components/editor/editor-methods/autocomplete-request.d.ts +14 -0
- package/dist/components/editor/editor-methods/autocomplete-request.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/autocomplete-request.js +72 -0
- package/dist/components/editor/editor-methods/autocomplete-request.js.map +1 -0
- package/dist/components/editor/editor-methods/backspace-cursor.d.ts +9 -0
- package/dist/components/editor/editor-methods/backspace-cursor.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/backspace-cursor.js +122 -0
- package/dist/components/editor/editor-methods/backspace-cursor.js.map +1 -0
- package/dist/components/editor/editor-methods/configuration.d.ts +17 -0
- package/dist/components/editor/editor-methods/configuration.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/configuration.js +107 -0
- package/dist/components/editor/editor-methods/configuration.js.map +1 -0
- package/dist/components/editor/editor-methods/cursor-movement.d.ts +7 -0
- package/dist/components/editor/editor-methods/cursor-movement.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/cursor-movement.js +112 -0
- package/dist/components/editor/editor-methods/cursor-movement.js.map +1 -0
- package/dist/components/editor/editor-methods/input-handling.d.ts +3 -0
- package/dist/components/editor/editor-methods/input-handling.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/input-handling.js +257 -0
- package/dist/components/editor/editor-methods/input-handling.js.map +1 -0
- package/dist/components/editor/editor-methods/paste-submit.d.ts +7 -0
- package/dist/components/editor/editor-methods/paste-submit.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/paste-submit.js +105 -0
- package/dist/components/editor/editor-methods/paste-submit.js.map +1 -0
- package/dist/components/editor/editor-methods/render.d.ts +3 -0
- package/dist/components/editor/editor-methods/render.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/render.js +114 -0
- package/dist/components/editor/editor-methods/render.js.map +1 -0
- package/dist/components/editor/editor-methods/text-insertion.d.ts +4 -0
- package/dist/components/editor/editor-methods/text-insertion.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/text-insertion.js +91 -0
- package/dist/components/editor/editor-methods/text-insertion.js.map +1 -0
- package/dist/components/editor/editor-methods/text-layout.d.ts +15 -0
- package/dist/components/editor/editor-methods/text-layout.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/text-layout.js +129 -0
- package/dist/components/editor/editor-methods/text-layout.js.map +1 -0
- package/dist/components/editor/editor-methods/vertical-movement.d.ts +8 -0
- package/dist/components/editor/editor-methods/vertical-movement.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/vertical-movement.js +123 -0
- package/dist/components/editor/editor-methods/vertical-movement.js.map +1 -0
- package/dist/components/editor/editor-methods/word-operations.d.ts +19 -0
- package/dist/components/editor/editor-methods/word-operations.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/word-operations.js +122 -0
- package/dist/components/editor/editor-methods/word-operations.js.map +1 -0
- package/dist/components/editor/editor-methods/yank-undo.d.ts +11 -0
- package/dist/components/editor/editor-methods/yank-undo.d.ts.map +1 -0
- package/dist/components/editor/editor-methods/yank-undo.js +132 -0
- package/dist/components/editor/editor-methods/yank-undo.js.map +1 -0
- package/dist/components/editor/editor.d.ts +67 -0
- package/dist/components/editor/editor.d.ts.map +1 -0
- package/dist/components/editor/editor.js +93 -0
- package/dist/components/editor/editor.js.map +1 -0
- package/dist/components/editor/helpers.d.ts +3 -0
- package/dist/components/editor/helpers.d.ts.map +1 -0
- package/dist/components/editor/helpers.js +3 -0
- package/dist/components/editor/helpers.js.map +1 -0
- package/dist/components/editor/index.d.ts +4 -0
- package/dist/components/editor/index.d.ts.map +1 -0
- package/dist/components/editor/index.js +3 -0
- package/dist/components/editor/index.js.map +1 -0
- package/dist/components/editor/paste-marker.d.ts +5 -0
- package/dist/components/editor/paste-marker.d.ts.map +1 -0
- package/dist/components/editor/paste-marker.js +134 -0
- package/dist/components/editor/paste-marker.js.map +1 -0
- package/dist/components/editor/trigger-patterns.d.ts +4 -0
- package/dist/components/editor/trigger-patterns.d.ts.map +1 -0
- package/dist/components/editor/trigger-patterns.js +11 -0
- package/dist/components/editor/trigger-patterns.js.map +1 -0
- package/dist/components/editor/types.d.ts +25 -0
- package/dist/components/editor/types.d.ts.map +1 -0
- package/dist/components/editor/types.js +2 -0
- package/dist/components/editor/types.js.map +1 -0
- package/dist/components/editor.d.ts +1 -253
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +1 -1915
- package/dist/components/editor.js.map +1 -1
- package/dist/components/image.d.ts.map +1 -1
- package/dist/components/image.js.map +1 -1
- package/dist/components/input/constants.d.ts +2 -0
- package/dist/components/input/constants.d.ts.map +1 -0
- package/dist/components/input/constants.js +3 -0
- package/dist/components/input/constants.js.map +1 -0
- package/dist/components/input/index.d.ts +2 -0
- package/dist/components/input/index.d.ts.map +1 -0
- package/dist/components/input/index.js +2 -0
- package/dist/components/input/index.js.map +1 -0
- package/dist/components/input/input-methods/input-handling.d.ts +3 -0
- package/dist/components/input/input-methods/input-handling.d.ts.map +1 -0
- package/dist/components/input/input-methods/input-handling.js +145 -0
- package/dist/components/input/input-methods/input-handling.js.map +1 -0
- package/dist/components/input/input-methods/render.d.ts +3 -0
- package/dist/components/input/input-methods/render.d.ts.map +1 -0
- package/dist/components/input/input-methods/render.js +62 -0
- package/dist/components/input/input-methods/render.js.map +1 -0
- package/dist/components/input/input-methods/text-editing.d.ts +17 -0
- package/dist/components/input/input-methods/text-editing.d.ts.map +1 -0
- package/dist/components/input/input-methods/text-editing.js +145 -0
- package/dist/components/input/input-methods/text-editing.js.map +1 -0
- package/dist/components/input/input-methods/value-access.d.ts +4 -0
- package/dist/components/input/input-methods/value-access.d.ts.map +1 -0
- package/dist/components/input/input-methods/value-access.js +20 -0
- package/dist/components/input/input-methods/value-access.js.map +1 -0
- package/dist/components/input/input.d.ts +37 -0
- package/dist/components/input/input.d.ts.map +1 -0
- package/dist/components/input/input.js +77 -0
- package/dist/components/input/input.js.map +1 -0
- package/dist/components/input/types.d.ts +5 -0
- package/dist/components/input/types.d.ts.map +1 -0
- package/dist/components/input/types.js +2 -0
- package/dist/components/input/types.js.map +1 -0
- package/dist/components/input.d.ts +1 -36
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +1 -377
- package/dist/components/input.js.map +1 -1
- package/dist/components/loader.d.ts.map +1 -1
- package/dist/components/loader.js.map +1 -1
- package/dist/components/markdown/constants.d.ts +4 -0
- package/dist/components/markdown/constants.d.ts.map +1 -0
- package/dist/components/markdown/constants.js +8 -0
- package/dist/components/markdown/constants.js.map +1 -0
- package/dist/components/markdown/index.d.ts +3 -0
- package/dist/components/markdown/index.d.ts.map +1 -0
- package/dist/components/markdown/index.js +2 -0
- package/dist/components/markdown/index.js.map +1 -0
- package/dist/components/markdown/markdown-methods/inline-rendering.d.ts +7 -0
- package/dist/components/markdown/markdown-methods/inline-rendering.d.ts.map +1 -0
- package/dist/components/markdown/markdown-methods/inline-rendering.js +95 -0
- package/dist/components/markdown/markdown-methods/inline-rendering.js.map +1 -0
- package/dist/components/markdown/markdown-methods/list-table-helpers.d.ts +7 -0
- package/dist/components/markdown/markdown-methods/list-table-helpers.d.ts.map +1 -0
- package/dist/components/markdown/markdown-methods/list-table-helpers.js +61 -0
- package/dist/components/markdown/markdown-methods/list-table-helpers.js.map +1 -0
- package/dist/components/markdown/markdown-methods/table-rendering.d.ts +5 -0
- package/dist/components/markdown/markdown-methods/table-rendering.d.ts.map +1 -0
- package/dist/components/markdown/markdown-methods/table-rendering.js +146 -0
- package/dist/components/markdown/markdown-methods/table-rendering.js.map +1 -0
- package/dist/components/markdown/markdown-methods/text-rendering.d.ts +6 -0
- package/dist/components/markdown/markdown-methods/text-rendering.d.ts.map +1 -0
- package/dist/components/markdown/markdown-methods/text-rendering.js +115 -0
- package/dist/components/markdown/markdown-methods/text-rendering.js.map +1 -0
- package/dist/components/markdown/markdown-methods/theming.d.ts +6 -0
- package/dist/components/markdown/markdown-methods/theming.d.ts.map +1 -0
- package/dist/components/markdown/markdown-methods/theming.js +41 -0
- package/dist/components/markdown/markdown-methods/theming.js.map +1 -0
- package/dist/components/markdown/markdown-methods/token-rendering.d.ts +5 -0
- package/dist/components/markdown/markdown-methods/token-rendering.d.ts.map +1 -0
- package/dist/components/markdown/markdown-methods/token-rendering.js +142 -0
- package/dist/components/markdown/markdown-methods/token-rendering.js.map +1 -0
- package/dist/components/markdown/markdown.d.ts +32 -0
- package/dist/components/markdown/markdown.d.ts.map +1 -0
- package/dist/components/markdown/markdown.js +72 -0
- package/dist/components/markdown/markdown.js.map +1 -0
- package/dist/components/markdown/strictstrikethroughtokenizer.d.ts +6 -0
- package/dist/components/markdown/strictstrikethroughtokenizer.d.ts.map +1 -0
- package/dist/components/markdown/strictstrikethroughtokenizer.js +18 -0
- package/dist/components/markdown/strictstrikethroughtokenizer.js.map +1 -0
- package/dist/components/markdown/types.d.ts +42 -0
- package/dist/components/markdown/types.d.ts.map +1 -0
- package/dist/components/markdown/types.js +2 -0
- package/dist/components/markdown/types.js.map +1 -0
- package/dist/components/markdown.d.ts +1 -96
- package/dist/components/markdown.d.ts.map +1 -1
- package/dist/components/markdown.js +1 -653
- package/dist/components/markdown.js.map +1 -1
- package/dist/components/select-list.d.ts.map +1 -1
- package/dist/components/select-list.js +8 -3
- package/dist/components/select-list.js.map +1 -1
- package/dist/components/settings-list.d.ts.map +1 -1
- package/dist/components/settings-list.js +2 -6
- package/dist/components/settings-list.js.map +1 -1
- package/dist/components/spacer.d.ts.map +1 -1
- package/dist/components/spacer.js.map +1 -1
- package/dist/components/text.d.ts.map +1 -1
- package/dist/components/text.js +2 -2
- package/dist/components/text.js.map +1 -1
- package/dist/components/truncated-text.d.ts.map +1 -1
- package/dist/components/truncated-text.js.map +1 -1
- package/dist/editor-component.d.ts.map +1 -1
- package/dist/editor-component.js.map +1 -1
- package/dist/fuzzy.d.ts.map +1 -1
- package/dist/fuzzy.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keybindings.d.ts.map +1 -1
- package/dist/keybindings.js.map +1 -1
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +145 -14
- package/dist/keys.js.map +1 -1
- package/dist/kill-ring.d.ts.map +1 -1
- package/dist/kill-ring.js.map +1 -1
- package/dist/native-modifiers.d.ts +9 -0
- package/dist/native-modifiers.d.ts.map +1 -1
- package/dist/native-modifiers.js +5 -0
- package/dist/native-modifiers.js.map +1 -1
- package/dist/stdin-buffer.d.ts +3 -0
- package/dist/stdin-buffer.d.ts.map +1 -1
- package/dist/stdin-buffer.js +24 -21
- package/dist/stdin-buffer.js.map +1 -1
- package/dist/terminal/constants.d.ts +9 -0
- package/dist/terminal/constants.d.ts.map +1 -0
- package/dist/terminal/constants.js +10 -0
- package/dist/terminal/constants.js.map +1 -0
- package/dist/terminal/helpers.d.ts +6 -0
- package/dist/terminal/helpers.d.ts.map +1 -0
- package/dist/terminal/helpers.js +23 -0
- package/dist/terminal/helpers.js.map +1 -0
- package/dist/terminal/index.d.ts +4 -0
- package/dist/terminal/index.d.ts.map +1 -0
- package/dist/terminal/index.js +3 -0
- package/dist/terminal/index.js.map +1 -0
- package/dist/terminal/processterminal-methods/cursor-screen.d.ts +11 -0
- package/dist/terminal/processterminal-methods/cursor-screen.d.ts.map +1 -0
- package/dist/terminal/processterminal-methods/cursor-screen.js +50 -0
- package/dist/terminal/processterminal-methods/cursor-screen.js.map +1 -0
- package/dist/terminal/processterminal-methods/keyboard-protocol.d.ts +14 -0
- package/dist/terminal/processterminal-methods/keyboard-protocol.d.ts.map +1 -0
- package/dist/terminal/processterminal-methods/keyboard-protocol.js +147 -0
- package/dist/terminal/processterminal-methods/keyboard-protocol.js.map +1 -0
- package/dist/terminal/processterminal-methods/terminal-io.d.ts +9 -0
- package/dist/terminal/processterminal-methods/terminal-io.d.ts.map +1 -0
- package/dist/terminal/processterminal-methods/terminal-io.js +153 -0
- package/dist/terminal/processterminal-methods/terminal-io.js.map +1 -0
- package/dist/terminal/processterminal.d.ts +49 -0
- package/dist/terminal/processterminal.d.ts.map +1 -0
- package/dist/terminal/processterminal.js +129 -0
- package/dist/terminal/processterminal.js.map +1 -0
- package/dist/terminal/types.d.ts +31 -0
- package/dist/terminal/types.d.ts.map +1 -0
- package/dist/terminal/types.js +2 -0
- package/dist/terminal/types.js.map +1 -0
- package/dist/terminal-colors.d.ts +2 -0
- package/dist/terminal-colors.d.ts.map +1 -1
- package/dist/terminal-colors.js +17 -5
- package/dist/terminal-colors.js.map +1 -1
- package/dist/terminal-image.d.ts.map +1 -1
- package/dist/terminal-image.js.map +1 -1
- package/dist/terminal.d.ts +1 -109
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +1 -432
- package/dist/terminal.js.map +1 -1
- package/dist/tui/constants.d.ts +3 -0
- package/dist/tui/constants.d.ts.map +1 -0
- package/dist/tui/constants.js +3 -0
- package/dist/tui/constants.js.map +1 -0
- package/dist/tui/container.d.ts +10 -0
- package/dist/tui/container.d.ts.map +1 -0
- package/dist/tui/container.js +31 -0
- package/dist/tui/container.js.map +1 -0
- package/dist/tui/helpers.d.ts +8 -0
- package/dist/tui/helpers.d.ts.map +1 -0
- package/dist/tui/helpers.js +53 -0
- package/dist/tui/helpers.js.map +1 -0
- package/dist/tui/index.d.ts +6 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +5 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/tui-methods/configuration.d.ts +20 -0
- package/dist/tui/tui-methods/configuration.d.ts.map +1 -0
- package/dist/tui/tui-methods/configuration.js +131 -0
- package/dist/tui/tui-methods/configuration.js.map +1 -0
- package/dist/tui/tui-methods/cursor-background.d.ts +10 -0
- package/dist/tui/tui-methods/cursor-background.d.ts.map +1 -0
- package/dist/tui/tui-methods/cursor-background.js +52 -0
- package/dist/tui/tui-methods/cursor-background.js.map +1 -0
- package/dist/tui/tui-methods/incremental-render.d.ts +17 -0
- package/dist/tui/tui-methods/incremental-render.d.ts.map +1 -0
- package/dist/tui/tui-methods/incremental-render.js +259 -0
- package/dist/tui/tui-methods/incremental-render.js.map +1 -0
- package/dist/tui/tui-methods/input-handling.d.ts +5 -0
- package/dist/tui/tui-methods/input-handling.d.ts.map +1 -0
- package/dist/tui/tui-methods/input-handling.js +111 -0
- package/dist/tui/tui-methods/input-handling.js.map +1 -0
- package/dist/tui/tui-methods/line-compositing.d.ts +7 -0
- package/dist/tui/tui-methods/line-compositing.d.ts.map +1 -0
- package/dist/tui/tui-methods/line-compositing.js +61 -0
- package/dist/tui/tui-methods/line-compositing.js.map +1 -0
- package/dist/tui/tui-methods/overlay-compositing.d.ts +12 -0
- package/dist/tui/tui-methods/overlay-compositing.d.ts.map +1 -0
- package/dist/tui/tui-methods/overlay-compositing.js +123 -0
- package/dist/tui/tui-methods/overlay-compositing.js.map +1 -0
- package/dist/tui/tui-methods/overlay-layout.d.ts +11 -0
- package/dist/tui/tui-methods/overlay-layout.d.ts.map +1 -0
- package/dist/tui/tui-methods/overlay-layout.js +124 -0
- package/dist/tui/tui-methods/overlay-layout.js.map +1 -0
- package/dist/tui/tui-methods/overlay-management.d.ts +11 -0
- package/dist/tui/tui-methods/overlay-management.d.ts.map +1 -0
- package/dist/tui/tui-methods/overlay-management.js +152 -0
- package/dist/tui/tui-methods/overlay-management.js.map +1 -0
- package/dist/tui/tui-methods/render-pipeline.d.ts +3 -0
- package/dist/tui/tui-methods/render-pipeline.d.ts.map +1 -0
- package/dist/tui/tui-methods/render-pipeline.js +176 -0
- package/dist/tui/tui-methods/render-pipeline.js.map +1 -0
- package/dist/tui/tui-methods/render-width-error.d.ts +3 -0
- package/dist/tui/tui-methods/render-width-error.d.ts.map +1 -0
- package/dist/tui/tui-methods/render-width-error.js +28 -0
- package/dist/tui/tui-methods/render-width-error.js.map +1 -0
- package/dist/tui/tui-methods/terminal-control.d.ts +8 -0
- package/dist/tui/tui-methods/terminal-control.d.ts.map +1 -0
- package/dist/tui/tui-methods/terminal-control.js +101 -0
- package/dist/tui/tui-methods/terminal-control.js.map +1 -0
- package/dist/tui/tui.d.ts +59 -0
- package/dist/tui/tui.d.ts.map +1 -0
- package/dist/tui/tui.js +78 -0
- package/dist/tui/tui.js.map +1 -0
- package/dist/tui/types.d.ts +124 -0
- package/dist/tui/types.d.ts.map +1 -0
- package/dist/tui/types.js +2 -0
- package/dist/tui/types.js.map +1 -0
- package/dist/tui.d.ts +1 -254
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +1 -1344
- package/dist/tui.js.map +1 -1
- package/dist/undo-stack.d.ts.map +1 -1
- package/dist/undo-stack.js.map +1 -1
- package/dist/utils/ansi-wrapping.d.ts +5 -0
- package/dist/utils/ansi-wrapping.d.ts.map +1 -0
- package/dist/utils/ansi-wrapping.js +52 -0
- package/dist/utils/ansi-wrapping.js.map +1 -0
- package/dist/utils/ansicodetracker.d.ts +27 -0
- package/dist/utils/ansicodetracker.d.ts.map +1 -0
- package/dist/utils/ansicodetracker.js +222 -0
- package/dist/utils/ansicodetracker.js.map +1 -0
- package/dist/utils/column-slicing.d.ts +12 -0
- package/dist/utils/column-slicing.d.ts.map +1 -0
- package/dist/utils/column-slicing.js +111 -0
- package/dist/utils/column-slicing.js.map +1 -0
- package/dist/utils/column-truncation.d.ts +2 -0
- package/dist/utils/column-truncation.d.ts.map +1 -0
- package/dist/utils/column-truncation.js +129 -0
- package/dist/utils/column-truncation.js.map +1 -0
- package/dist/utils/constants.d.ts +12 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +12 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/install-delegated-methods.d.ts +5 -0
- package/dist/utils/install-delegated-methods.d.ts.map +1 -0
- package/dist/utils/install-delegated-methods.js +27 -0
- package/dist/utils/install-delegated-methods.js.map +1 -0
- package/dist/utils/segmentation.d.ts +10 -0
- package/dist/utils/segmentation.d.ts.map +1 -0
- package/dist/utils/segmentation.js +111 -0
- package/dist/utils/segmentation.js.map +1 -0
- package/dist/utils/terminal-output.d.ts +10 -0
- package/dist/utils/terminal-output.d.ts.map +1 -0
- package/dist/utils/terminal-output.js +122 -0
- package/dist/utils/terminal-output.js.map +1 -0
- package/dist/utils/types.d.ts +7 -0
- package/dist/utils/types.d.ts.map +1 -0
- package/dist/utils/types.js +2 -0
- package/dist/utils/types.js.map +1 -0
- package/dist/utils/width-truncation.d.ts +6 -0
- package/dist/utils/width-truncation.d.ts.map +1 -0
- package/dist/utils/width-truncation.js +121 -0
- package/dist/utils/width-truncation.js.map +1 -0
- package/dist/utils/word-wrapping.d.ts +4 -0
- package/dist/utils/word-wrapping.d.ts.map +1 -0
- package/dist/utils/word-wrapping.js +139 -0
- package/dist/utils/word-wrapping.js.map +1 -0
- package/dist/utils.d.ts +1 -84
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1 -1059
- package/dist/utils.js.map +1 -1
- package/dist/word-navigation.d.ts.map +1 -1
- package/dist/word-navigation.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { matchesKey } from "../../../keys.js";
|
|
2
|
+
export function do_handlePaste(self, pastedText) {
|
|
3
|
+
self.cancelAutocomplete();
|
|
4
|
+
self.exitHistoryBrowsing();
|
|
5
|
+
self.lastAction = null;
|
|
6
|
+
self.pushUndoSnapshot();
|
|
7
|
+
// Some terminals (e.g. tmux popups with extended-keys-format=csi-u) re-encode
|
|
8
|
+
// control bytes inside bracketed paste as CSI-u Ctrl+<letter> sequences
|
|
9
|
+
// (ESC [ <codepoint> ; 5 u). Decode those back to their literal byte so the
|
|
10
|
+
// per-char filter below preserves newlines instead of stripping ESC and
|
|
11
|
+
// leaking the printable tail (e.g. "[106;5u") into the editor.
|
|
12
|
+
const decodedText = pastedText.replace(/\x1b\[(\d+);5u/g, (match, code) => {
|
|
13
|
+
const cp = Number(code);
|
|
14
|
+
if (cp >= 97 && cp <= 122)
|
|
15
|
+
return String.fromCharCode(cp - 96);
|
|
16
|
+
if (cp >= 65 && cp <= 90)
|
|
17
|
+
return String.fromCharCode(cp - 64);
|
|
18
|
+
return match;
|
|
19
|
+
});
|
|
20
|
+
// Clean the pasted text: normalize line endings, expand tabs
|
|
21
|
+
const cleanText = self.normalizeText(decodedText);
|
|
22
|
+
// Filter out non-printable characters except newlines
|
|
23
|
+
let filteredText = cleanText
|
|
24
|
+
.split("")
|
|
25
|
+
.filter((char) => char === "\n" || char.charCodeAt(0) >= 32)
|
|
26
|
+
.join("");
|
|
27
|
+
// If pasting a file path (starts with /, ~, or .) and the character before
|
|
28
|
+
// the cursor is a word character, prepend a space for better readability
|
|
29
|
+
if (/^[/~.]/.test(filteredText)) {
|
|
30
|
+
const currentLine = self.state.lines[self.state.cursorLine] || "";
|
|
31
|
+
const charBeforeCursor = self.state.cursorCol > 0 ? currentLine[self.state.cursorCol - 1] : "";
|
|
32
|
+
if (charBeforeCursor && /\w/.test(charBeforeCursor)) {
|
|
33
|
+
filteredText = ` ${filteredText}`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Split into lines to check for large paste
|
|
37
|
+
const pastedLines = filteredText.split("\n");
|
|
38
|
+
// Check if self is a large paste (> 10 lines or > 1000 characters)
|
|
39
|
+
const totalChars = filteredText.length;
|
|
40
|
+
if (pastedLines.length > 10 || totalChars > 1000) {
|
|
41
|
+
// Store the paste and insert a marker
|
|
42
|
+
self.pasteCounter++;
|
|
43
|
+
const pasteId = self.pasteCounter;
|
|
44
|
+
self.pastes.set(pasteId, filteredText);
|
|
45
|
+
// Insert marker like "[paste #1 +123 lines]" or "[paste #1 1234 chars]"
|
|
46
|
+
const marker = pastedLines.length > 10
|
|
47
|
+
? `[paste #${pasteId} +${pastedLines.length} lines]`
|
|
48
|
+
: `[paste #${pasteId} ${totalChars} chars]`;
|
|
49
|
+
self.insertTextAtCursorInternal(marker);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (pastedLines.length === 1) {
|
|
53
|
+
// Single line - insert atomically (do not trigger autocomplete during paste)
|
|
54
|
+
self.insertTextAtCursorInternal(filteredText);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// Multi-line paste - use direct state manipulation
|
|
58
|
+
self.insertTextAtCursorInternal(filteredText);
|
|
59
|
+
}
|
|
60
|
+
export function do_addNewLine(self) {
|
|
61
|
+
self.cancelAutocomplete();
|
|
62
|
+
self.exitHistoryBrowsing();
|
|
63
|
+
self.lastAction = null;
|
|
64
|
+
self.pushUndoSnapshot();
|
|
65
|
+
const currentLine = self.state.lines[self.state.cursorLine] || "";
|
|
66
|
+
const before = currentLine.slice(0, self.state.cursorCol);
|
|
67
|
+
const after = currentLine.slice(self.state.cursorCol);
|
|
68
|
+
// Split current line
|
|
69
|
+
self.state.lines[self.state.cursorLine] = before;
|
|
70
|
+
self.state.lines.splice(self.state.cursorLine + 1, 0, after);
|
|
71
|
+
// Move cursor to start of new line
|
|
72
|
+
self.state.cursorLine++;
|
|
73
|
+
self.setCursorCol(0);
|
|
74
|
+
if (self.onChange) {
|
|
75
|
+
self.onChange(self.getText());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export function do_shouldSubmitOnBackslashEnter(self, data, kb) {
|
|
79
|
+
if (self.disableSubmit)
|
|
80
|
+
return false;
|
|
81
|
+
if (!matchesKey(data, "enter"))
|
|
82
|
+
return false;
|
|
83
|
+
const submitKeys = kb.getKeys("tui.input.submit");
|
|
84
|
+
const hasShiftEnter = submitKeys.includes("shift+enter") || submitKeys.includes("shift+return");
|
|
85
|
+
if (!hasShiftEnter)
|
|
86
|
+
return false;
|
|
87
|
+
const currentLine = self.state.lines[self.state.cursorLine] || "";
|
|
88
|
+
return self.state.cursorCol > 0 && currentLine[self.state.cursorCol - 1] === "\\";
|
|
89
|
+
}
|
|
90
|
+
export function do_submitValue(self) {
|
|
91
|
+
self.cancelAutocomplete();
|
|
92
|
+
const result = self.expandPasteMarkers(self.state.lines.join("\n")).trim();
|
|
93
|
+
self.state = { lines: [""], cursorLine: 0, cursorCol: 0 };
|
|
94
|
+
self.pastes.clear();
|
|
95
|
+
self.pasteCounter = 0;
|
|
96
|
+
self.exitHistoryBrowsing();
|
|
97
|
+
self.scrollOffset = 0;
|
|
98
|
+
self.undoStack.clear();
|
|
99
|
+
self.lastAction = null;
|
|
100
|
+
if (self.onChange)
|
|
101
|
+
self.onChange("");
|
|
102
|
+
if (self.onSubmit)
|
|
103
|
+
self.onSubmit(result);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=paste-submit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paste-submit.js","sourceRoot":"","sources":["../../../../src/components/editor/editor-methods/paste-submit.ts"],"names":[],"mappings":"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","sourcesContent":["import type { getKeybindings } from \"../../../keybindings.ts\";\nimport { matchesKey } from \"../../../keys.ts\";\nimport type { Editor } from \"../editor.ts\";\n\nexport function do_handlePaste(self: Editor, pastedText: string): void {\n self.cancelAutocomplete();\n self.exitHistoryBrowsing();\n self.lastAction = null;\n\n self.pushUndoSnapshot();\n\n // Some terminals (e.g. tmux popups with extended-keys-format=csi-u) re-encode\n // control bytes inside bracketed paste as CSI-u Ctrl+<letter> sequences\n // (ESC [ <codepoint> ; 5 u). Decode those back to their literal byte so the\n // per-char filter below preserves newlines instead of stripping ESC and\n // leaking the printable tail (e.g. \"[106;5u\") into the editor.\n const decodedText = pastedText.replace(/\\x1b\\[(\\d+);5u/g, (match, code) => {\n const cp = Number(code);\n if (cp >= 97 && cp <= 122) return String.fromCharCode(cp - 96);\n if (cp >= 65 && cp <= 90) return String.fromCharCode(cp - 64);\n return match;\n });\n\n // Clean the pasted text: normalize line endings, expand tabs\n const cleanText = self.normalizeText(decodedText);\n\n // Filter out non-printable characters except newlines\n let filteredText = cleanText\n .split(\"\")\n .filter((char) => char === \"\\n\" || char.charCodeAt(0) >= 32)\n .join(\"\");\n\n // If pasting a file path (starts with /, ~, or .) and the character before\n // the cursor is a word character, prepend a space for better readability\n if (/^[/~.]/.test(filteredText)) {\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n const charBeforeCursor = self.state.cursorCol > 0 ? currentLine[self.state.cursorCol - 1] : \"\";\n if (charBeforeCursor && /\\w/.test(charBeforeCursor)) {\n filteredText = ` ${filteredText}`;\n }\n }\n\n // Split into lines to check for large paste\n const pastedLines = filteredText.split(\"\\n\");\n\n // Check if self is a large paste (> 10 lines or > 1000 characters)\n const totalChars = filteredText.length;\n if (pastedLines.length > 10 || totalChars > 1000) {\n // Store the paste and insert a marker\n self.pasteCounter++;\n const pasteId = self.pasteCounter;\n self.pastes.set(pasteId, filteredText);\n\n // Insert marker like \"[paste #1 +123 lines]\" or \"[paste #1 1234 chars]\"\n const marker =\n pastedLines.length > 10\n ? `[paste #${pasteId} +${pastedLines.length} lines]`\n : `[paste #${pasteId} ${totalChars} chars]`;\n self.insertTextAtCursorInternal(marker);\n return;\n }\n\n if (pastedLines.length === 1) {\n // Single line - insert atomically (do not trigger autocomplete during paste)\n self.insertTextAtCursorInternal(filteredText);\n return;\n }\n\n // Multi-line paste - use direct state manipulation\n self.insertTextAtCursorInternal(filteredText);\n}\n\nexport function do_addNewLine(self: Editor): void {\n self.cancelAutocomplete();\n self.exitHistoryBrowsing();\n self.lastAction = null;\n\n self.pushUndoSnapshot();\n\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n\n const before = currentLine.slice(0, self.state.cursorCol);\n const after = currentLine.slice(self.state.cursorCol);\n\n // Split current line\n self.state.lines[self.state.cursorLine] = before;\n self.state.lines.splice(self.state.cursorLine + 1, 0, after);\n\n // Move cursor to start of new line\n self.state.cursorLine++;\n self.setCursorCol(0);\n\n if (self.onChange) {\n self.onChange(self.getText());\n }\n}\n\nexport function do_shouldSubmitOnBackslashEnter(\n self: Editor,\n data: string,\n kb: ReturnType<typeof getKeybindings>,\n): boolean {\n if (self.disableSubmit) return false;\n if (!matchesKey(data, \"enter\")) return false;\n const submitKeys = kb.getKeys(\"tui.input.submit\");\n const hasShiftEnter = submitKeys.includes(\"shift+enter\") || submitKeys.includes(\"shift+return\");\n if (!hasShiftEnter) return false;\n\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n return self.state.cursorCol > 0 && currentLine[self.state.cursorCol - 1] === \"\\\\\";\n}\n\nexport function do_submitValue(self: Editor): void {\n self.cancelAutocomplete();\n const result = self.expandPasteMarkers(self.state.lines.join(\"\\n\")).trim();\n\n self.state = { lines: [\"\"], cursorLine: 0, cursorCol: 0 };\n self.pastes.clear();\n self.pasteCounter = 0;\n self.exitHistoryBrowsing();\n self.scrollOffset = 0;\n self.undoStack.clear();\n self.lastAction = null;\n\n if (self.onChange) self.onChange(\"\");\n if (self.onSubmit) self.onSubmit(result);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../../src/components/editor/editor-methods/render.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA6H/D","sourcesContent":["import { CURSOR_MARKER } from \"../../../tui.ts\";\nimport { truncateToWidth, visibleWidth } from \"../../../utils.ts\";\nimport type { Editor } from \"../editor.ts\";\n\nexport function do_render(self: Editor, width: number): string[] {\n const maxPadding = Math.max(0, Math.floor((width - 1) / 2));\n const paddingX = Math.min(self.paddingX, maxPadding);\n const contentWidth = Math.max(1, width - paddingX * 2);\n\n // Layout width: with padding the cursor can overflow into it,\n // without padding we reserve 1 column for the cursor.\n const layoutWidth = Math.max(1, contentWidth - (paddingX ? 0 : 1));\n\n // Store for cursor navigation (must match wrapping width)\n self.lastWidth = layoutWidth;\n\n const horizontal = self.borderColor(\"─\");\n\n // Layout the text\n const layoutLines = self.layoutText(layoutWidth);\n\n // Calculate max visible lines: 30% of terminal height, minimum 5 lines\n const terminalRows = self.tui.terminal.rows;\n const maxVisibleLines = Math.max(5, Math.floor(terminalRows * 0.3));\n\n // Find the cursor line index in layoutLines\n let cursorLineIndex = layoutLines.findIndex((line) => line.hasCursor);\n if (cursorLineIndex === -1) cursorLineIndex = 0;\n\n // Adjust scroll offset to keep cursor visible\n if (cursorLineIndex < self.scrollOffset) {\n self.scrollOffset = cursorLineIndex;\n } else if (cursorLineIndex >= self.scrollOffset + maxVisibleLines) {\n self.scrollOffset = cursorLineIndex - maxVisibleLines + 1;\n }\n\n // Clamp scroll offset to valid range\n const maxScrollOffset = Math.max(0, layoutLines.length - maxVisibleLines);\n self.scrollOffset = Math.max(0, Math.min(self.scrollOffset, maxScrollOffset));\n\n // Get visible lines slice\n const visibleLines = layoutLines.slice(self.scrollOffset, self.scrollOffset + maxVisibleLines);\n\n const result: string[] = [];\n const leftPadding = \" \".repeat(paddingX);\n const rightPadding = leftPadding;\n\n // Render top border (with scroll indicator if scrolled down)\n if (self.scrollOffset > 0) {\n const indicator = `─── ↑ ${self.scrollOffset} more `;\n const remaining = width - visibleWidth(indicator);\n if (remaining >= 0) {\n result.push(self.borderColor(indicator + \"─\".repeat(remaining)));\n } else {\n result.push(self.borderColor(truncateToWidth(indicator, width)));\n }\n } else {\n result.push(horizontal.repeat(width));\n }\n\n // Render each visible layout line\n // Emit hardware cursor marker when focused so TUI can position the\n // hardware cursor for IME candidate-window placement even while\n // autocomplete (e.g. slash-command menu) is visible.\n const emitCursorMarker = self.focused;\n\n for (const layoutLine of visibleLines) {\n let displayText = layoutLine.text;\n let lineVisibleWidth = visibleWidth(layoutLine.text);\n let cursorInPadding = false;\n\n // Add cursor if self line has it\n if (layoutLine.hasCursor && layoutLine.cursorPos !== undefined) {\n const before = displayText.slice(0, layoutLine.cursorPos);\n const after = displayText.slice(layoutLine.cursorPos);\n\n // Hardware cursor marker (zero-width, emitted before fake cursor for IME positioning)\n const marker = emitCursorMarker ? CURSOR_MARKER : \"\";\n\n if (after.length > 0) {\n // Cursor is on a character (grapheme) - replace it with highlighted version\n // Get the first grapheme from 'after'\n const afterGraphemes = [...self.segment(after, \"grapheme\")];\n const firstGrapheme = afterGraphemes[0]?.segment || \"\";\n const restAfter = after.slice(firstGrapheme.length);\n const cursor = `\\x1b[7m${firstGrapheme}\\x1b[0m`;\n displayText = before + marker + cursor + restAfter;\n // lineVisibleWidth stays the same - we're replacing, not adding\n } else {\n // Cursor is at the end - add highlighted space\n const cursor = \"\\x1b[7m \\x1b[0m\";\n displayText = before + marker + cursor;\n lineVisibleWidth = lineVisibleWidth + 1;\n // If cursor overflows content width into the padding, flag it\n if (lineVisibleWidth > contentWidth && paddingX > 0) {\n cursorInPadding = true;\n }\n }\n }\n\n // Calculate padding based on actual visible width\n const padding = \" \".repeat(Math.max(0, contentWidth - lineVisibleWidth));\n const lineRightPadding = cursorInPadding ? rightPadding.slice(1) : rightPadding;\n\n // Render the line (no side borders, just horizontal lines above and below)\n result.push(`${leftPadding}${displayText}${padding}${lineRightPadding}`);\n }\n\n // Render bottom border (with scroll indicator if more content below)\n const linesBelow = layoutLines.length - (self.scrollOffset + visibleLines.length);\n if (linesBelow > 0) {\n const indicator = `─── ↓ ${linesBelow} more `;\n const remaining = width - visibleWidth(indicator);\n result.push(self.borderColor(indicator + \"─\".repeat(Math.max(0, remaining))));\n } else {\n result.push(horizontal.repeat(width));\n }\n\n // Add autocomplete list if active\n if (self.autocompleteState && self.autocompleteList) {\n const autocompleteResult = self.autocompleteList.render(contentWidth);\n for (const line of autocompleteResult) {\n const lineWidth = visibleWidth(line);\n const linePadding = \" \".repeat(Math.max(0, contentWidth - lineWidth));\n result.push(`${leftPadding}${line}${linePadding}${rightPadding}`);\n }\n }\n\n return result;\n}\n"]}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { CURSOR_MARKER } from "../../../tui.js";
|
|
2
|
+
import { truncateToWidth, visibleWidth } from "../../../utils.js";
|
|
3
|
+
export function do_render(self, width) {
|
|
4
|
+
const maxPadding = Math.max(0, Math.floor((width - 1) / 2));
|
|
5
|
+
const paddingX = Math.min(self.paddingX, maxPadding);
|
|
6
|
+
const contentWidth = Math.max(1, width - paddingX * 2);
|
|
7
|
+
// Layout width: with padding the cursor can overflow into it,
|
|
8
|
+
// without padding we reserve 1 column for the cursor.
|
|
9
|
+
const layoutWidth = Math.max(1, contentWidth - (paddingX ? 0 : 1));
|
|
10
|
+
// Store for cursor navigation (must match wrapping width)
|
|
11
|
+
self.lastWidth = layoutWidth;
|
|
12
|
+
const horizontal = self.borderColor("─");
|
|
13
|
+
// Layout the text
|
|
14
|
+
const layoutLines = self.layoutText(layoutWidth);
|
|
15
|
+
// Calculate max visible lines: 30% of terminal height, minimum 5 lines
|
|
16
|
+
const terminalRows = self.tui.terminal.rows;
|
|
17
|
+
const maxVisibleLines = Math.max(5, Math.floor(terminalRows * 0.3));
|
|
18
|
+
// Find the cursor line index in layoutLines
|
|
19
|
+
let cursorLineIndex = layoutLines.findIndex((line) => line.hasCursor);
|
|
20
|
+
if (cursorLineIndex === -1)
|
|
21
|
+
cursorLineIndex = 0;
|
|
22
|
+
// Adjust scroll offset to keep cursor visible
|
|
23
|
+
if (cursorLineIndex < self.scrollOffset) {
|
|
24
|
+
self.scrollOffset = cursorLineIndex;
|
|
25
|
+
}
|
|
26
|
+
else if (cursorLineIndex >= self.scrollOffset + maxVisibleLines) {
|
|
27
|
+
self.scrollOffset = cursorLineIndex - maxVisibleLines + 1;
|
|
28
|
+
}
|
|
29
|
+
// Clamp scroll offset to valid range
|
|
30
|
+
const maxScrollOffset = Math.max(0, layoutLines.length - maxVisibleLines);
|
|
31
|
+
self.scrollOffset = Math.max(0, Math.min(self.scrollOffset, maxScrollOffset));
|
|
32
|
+
// Get visible lines slice
|
|
33
|
+
const visibleLines = layoutLines.slice(self.scrollOffset, self.scrollOffset + maxVisibleLines);
|
|
34
|
+
const result = [];
|
|
35
|
+
const leftPadding = " ".repeat(paddingX);
|
|
36
|
+
const rightPadding = leftPadding;
|
|
37
|
+
// Render top border (with scroll indicator if scrolled down)
|
|
38
|
+
if (self.scrollOffset > 0) {
|
|
39
|
+
const indicator = `─── ↑ ${self.scrollOffset} more `;
|
|
40
|
+
const remaining = width - visibleWidth(indicator);
|
|
41
|
+
if (remaining >= 0) {
|
|
42
|
+
result.push(self.borderColor(indicator + "─".repeat(remaining)));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
result.push(self.borderColor(truncateToWidth(indicator, width)));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
result.push(horizontal.repeat(width));
|
|
50
|
+
}
|
|
51
|
+
// Render each visible layout line
|
|
52
|
+
// Emit hardware cursor marker when focused so TUI can position the
|
|
53
|
+
// hardware cursor for IME candidate-window placement even while
|
|
54
|
+
// autocomplete (e.g. slash-command menu) is visible.
|
|
55
|
+
const emitCursorMarker = self.focused;
|
|
56
|
+
for (const layoutLine of visibleLines) {
|
|
57
|
+
let displayText = layoutLine.text;
|
|
58
|
+
let lineVisibleWidth = visibleWidth(layoutLine.text);
|
|
59
|
+
let cursorInPadding = false;
|
|
60
|
+
// Add cursor if self line has it
|
|
61
|
+
if (layoutLine.hasCursor && layoutLine.cursorPos !== undefined) {
|
|
62
|
+
const before = displayText.slice(0, layoutLine.cursorPos);
|
|
63
|
+
const after = displayText.slice(layoutLine.cursorPos);
|
|
64
|
+
// Hardware cursor marker (zero-width, emitted before fake cursor for IME positioning)
|
|
65
|
+
const marker = emitCursorMarker ? CURSOR_MARKER : "";
|
|
66
|
+
if (after.length > 0) {
|
|
67
|
+
// Cursor is on a character (grapheme) - replace it with highlighted version
|
|
68
|
+
// Get the first grapheme from 'after'
|
|
69
|
+
const afterGraphemes = [...self.segment(after, "grapheme")];
|
|
70
|
+
const firstGrapheme = afterGraphemes[0]?.segment || "";
|
|
71
|
+
const restAfter = after.slice(firstGrapheme.length);
|
|
72
|
+
const cursor = `\x1b[7m${firstGrapheme}\x1b[0m`;
|
|
73
|
+
displayText = before + marker + cursor + restAfter;
|
|
74
|
+
// lineVisibleWidth stays the same - we're replacing, not adding
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Cursor is at the end - add highlighted space
|
|
78
|
+
const cursor = "\x1b[7m \x1b[0m";
|
|
79
|
+
displayText = before + marker + cursor;
|
|
80
|
+
lineVisibleWidth = lineVisibleWidth + 1;
|
|
81
|
+
// If cursor overflows content width into the padding, flag it
|
|
82
|
+
if (lineVisibleWidth > contentWidth && paddingX > 0) {
|
|
83
|
+
cursorInPadding = true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Calculate padding based on actual visible width
|
|
88
|
+
const padding = " ".repeat(Math.max(0, contentWidth - lineVisibleWidth));
|
|
89
|
+
const lineRightPadding = cursorInPadding ? rightPadding.slice(1) : rightPadding;
|
|
90
|
+
// Render the line (no side borders, just horizontal lines above and below)
|
|
91
|
+
result.push(`${leftPadding}${displayText}${padding}${lineRightPadding}`);
|
|
92
|
+
}
|
|
93
|
+
// Render bottom border (with scroll indicator if more content below)
|
|
94
|
+
const linesBelow = layoutLines.length - (self.scrollOffset + visibleLines.length);
|
|
95
|
+
if (linesBelow > 0) {
|
|
96
|
+
const indicator = `─── ↓ ${linesBelow} more `;
|
|
97
|
+
const remaining = width - visibleWidth(indicator);
|
|
98
|
+
result.push(self.borderColor(indicator + "─".repeat(Math.max(0, remaining))));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
result.push(horizontal.repeat(width));
|
|
102
|
+
}
|
|
103
|
+
// Add autocomplete list if active
|
|
104
|
+
if (self.autocompleteState && self.autocompleteList) {
|
|
105
|
+
const autocompleteResult = self.autocompleteList.render(contentWidth);
|
|
106
|
+
for (const line of autocompleteResult) {
|
|
107
|
+
const lineWidth = visibleWidth(line);
|
|
108
|
+
const linePadding = " ".repeat(Math.max(0, contentWidth - lineWidth));
|
|
109
|
+
result.push(`${leftPadding}${line}${linePadding}${rightPadding}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../../src/components/editor/editor-methods/render.ts"],"names":[],"mappings":"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","sourcesContent":["import { CURSOR_MARKER } from \"../../../tui.ts\";\nimport { truncateToWidth, visibleWidth } from \"../../../utils.ts\";\nimport type { Editor } from \"../editor.ts\";\n\nexport function do_render(self: Editor, width: number): string[] {\n const maxPadding = Math.max(0, Math.floor((width - 1) / 2));\n const paddingX = Math.min(self.paddingX, maxPadding);\n const contentWidth = Math.max(1, width - paddingX * 2);\n\n // Layout width: with padding the cursor can overflow into it,\n // without padding we reserve 1 column for the cursor.\n const layoutWidth = Math.max(1, contentWidth - (paddingX ? 0 : 1));\n\n // Store for cursor navigation (must match wrapping width)\n self.lastWidth = layoutWidth;\n\n const horizontal = self.borderColor(\"─\");\n\n // Layout the text\n const layoutLines = self.layoutText(layoutWidth);\n\n // Calculate max visible lines: 30% of terminal height, minimum 5 lines\n const terminalRows = self.tui.terminal.rows;\n const maxVisibleLines = Math.max(5, Math.floor(terminalRows * 0.3));\n\n // Find the cursor line index in layoutLines\n let cursorLineIndex = layoutLines.findIndex((line) => line.hasCursor);\n if (cursorLineIndex === -1) cursorLineIndex = 0;\n\n // Adjust scroll offset to keep cursor visible\n if (cursorLineIndex < self.scrollOffset) {\n self.scrollOffset = cursorLineIndex;\n } else if (cursorLineIndex >= self.scrollOffset + maxVisibleLines) {\n self.scrollOffset = cursorLineIndex - maxVisibleLines + 1;\n }\n\n // Clamp scroll offset to valid range\n const maxScrollOffset = Math.max(0, layoutLines.length - maxVisibleLines);\n self.scrollOffset = Math.max(0, Math.min(self.scrollOffset, maxScrollOffset));\n\n // Get visible lines slice\n const visibleLines = layoutLines.slice(self.scrollOffset, self.scrollOffset + maxVisibleLines);\n\n const result: string[] = [];\n const leftPadding = \" \".repeat(paddingX);\n const rightPadding = leftPadding;\n\n // Render top border (with scroll indicator if scrolled down)\n if (self.scrollOffset > 0) {\n const indicator = `─── ↑ ${self.scrollOffset} more `;\n const remaining = width - visibleWidth(indicator);\n if (remaining >= 0) {\n result.push(self.borderColor(indicator + \"─\".repeat(remaining)));\n } else {\n result.push(self.borderColor(truncateToWidth(indicator, width)));\n }\n } else {\n result.push(horizontal.repeat(width));\n }\n\n // Render each visible layout line\n // Emit hardware cursor marker when focused so TUI can position the\n // hardware cursor for IME candidate-window placement even while\n // autocomplete (e.g. slash-command menu) is visible.\n const emitCursorMarker = self.focused;\n\n for (const layoutLine of visibleLines) {\n let displayText = layoutLine.text;\n let lineVisibleWidth = visibleWidth(layoutLine.text);\n let cursorInPadding = false;\n\n // Add cursor if self line has it\n if (layoutLine.hasCursor && layoutLine.cursorPos !== undefined) {\n const before = displayText.slice(0, layoutLine.cursorPos);\n const after = displayText.slice(layoutLine.cursorPos);\n\n // Hardware cursor marker (zero-width, emitted before fake cursor for IME positioning)\n const marker = emitCursorMarker ? CURSOR_MARKER : \"\";\n\n if (after.length > 0) {\n // Cursor is on a character (grapheme) - replace it with highlighted version\n // Get the first grapheme from 'after'\n const afterGraphemes = [...self.segment(after, \"grapheme\")];\n const firstGrapheme = afterGraphemes[0]?.segment || \"\";\n const restAfter = after.slice(firstGrapheme.length);\n const cursor = `\\x1b[7m${firstGrapheme}\\x1b[0m`;\n displayText = before + marker + cursor + restAfter;\n // lineVisibleWidth stays the same - we're replacing, not adding\n } else {\n // Cursor is at the end - add highlighted space\n const cursor = \"\\x1b[7m \\x1b[0m\";\n displayText = before + marker + cursor;\n lineVisibleWidth = lineVisibleWidth + 1;\n // If cursor overflows content width into the padding, flag it\n if (lineVisibleWidth > contentWidth && paddingX > 0) {\n cursorInPadding = true;\n }\n }\n }\n\n // Calculate padding based on actual visible width\n const padding = \" \".repeat(Math.max(0, contentWidth - lineVisibleWidth));\n const lineRightPadding = cursorInPadding ? rightPadding.slice(1) : rightPadding;\n\n // Render the line (no side borders, just horizontal lines above and below)\n result.push(`${leftPadding}${displayText}${padding}${lineRightPadding}`);\n }\n\n // Render bottom border (with scroll indicator if more content below)\n const linesBelow = layoutLines.length - (self.scrollOffset + visibleLines.length);\n if (linesBelow > 0) {\n const indicator = `─── ↓ ${linesBelow} more `;\n const remaining = width - visibleWidth(indicator);\n result.push(self.borderColor(indicator + \"─\".repeat(Math.max(0, remaining))));\n } else {\n result.push(horizontal.repeat(width));\n }\n\n // Add autocomplete list if active\n if (self.autocompleteState && self.autocompleteList) {\n const autocompleteResult = self.autocompleteList.render(contentWidth);\n for (const line of autocompleteResult) {\n const lineWidth = visibleWidth(line);\n const linePadding = \" \".repeat(Math.max(0, contentWidth - lineWidth));\n result.push(`${leftPadding}${line}${linePadding}${rightPadding}`);\n }\n }\n\n return result;\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Editor } from "../editor.ts";
|
|
2
|
+
export declare function do_insertTextAtCursorInternal(self: Editor, text: string): void;
|
|
3
|
+
export declare function do_insertCharacter(self: Editor, char: string, skipUndoCoalescing?: boolean): void;
|
|
4
|
+
//# sourceMappingURL=text-insertion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-insertion.d.ts","sourceRoot":"","sources":["../../../../src/components/editor/editor-methods/text-insertion.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAyC9E;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,OAAO,GAAG,IAAI,CA0DjG","sourcesContent":["import { isWhitespaceChar } from \"../../../utils.ts\";\nimport type { Editor } from \"../editor.ts\";\n\nexport function do_insertTextAtCursorInternal(self: Editor, text: string): void {\n if (!text) return;\n\n // Normalize line endings and tabs\n const normalized = self.normalizeText(text);\n const insertedLines = normalized.split(\"\\n\");\n\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n const beforeCursor = currentLine.slice(0, self.state.cursorCol);\n const afterCursor = currentLine.slice(self.state.cursorCol);\n\n if (insertedLines.length === 1) {\n // Single line - insert at cursor position\n self.state.lines[self.state.cursorLine] = beforeCursor + normalized + afterCursor;\n self.setCursorCol(self.state.cursorCol + normalized.length);\n } else {\n // Multi-line insertion\n self.state.lines = [\n // All lines before current line\n ...self.state.lines.slice(0, self.state.cursorLine),\n\n // The first inserted line merged with text before cursor\n beforeCursor + insertedLines[0],\n\n // All middle inserted lines\n ...insertedLines.slice(1, -1),\n\n // The last inserted line with text after cursor\n insertedLines[insertedLines.length - 1] + afterCursor,\n\n // All lines after current line\n ...self.state.lines.slice(self.state.cursorLine + 1),\n ];\n\n self.state.cursorLine += insertedLines.length - 1;\n self.setCursorCol((insertedLines[insertedLines.length - 1] || \"\").length);\n }\n\n if (self.onChange) {\n self.onChange(self.getText());\n }\n}\n\nexport function do_insertCharacter(self: Editor, char: string, skipUndoCoalescing?: boolean): void {\n self.exitHistoryBrowsing();\n\n // Undo coalescing (fish-style):\n // - Consecutive word chars coalesce into one undo unit\n // - Space captures state before itself (so undo removes space+following word together)\n // - Each space is separately undoable\n // Skip coalescing when called from atomic operations (e.g., handlePaste)\n if (!skipUndoCoalescing) {\n if (isWhitespaceChar(char) || self.lastAction !== \"type-word\") {\n self.pushUndoSnapshot();\n }\n self.lastAction = \"type-word\";\n }\n\n const line = self.state.lines[self.state.cursorLine] || \"\";\n\n const before = line.slice(0, self.state.cursorCol);\n const after = line.slice(self.state.cursorCol);\n\n self.state.lines[self.state.cursorLine] = before + char + after;\n self.setCursorCol(self.state.cursorCol + char.length);\n\n if (self.onChange) {\n self.onChange(self.getText());\n }\n\n // Check if we should trigger or update autocomplete\n if (!self.autocompleteState) {\n // Auto-trigger for \"/\" at the start of a line (slash commands)\n if (char === \"/\" && self.isAtStartOfMessage()) {\n self.tryTriggerAutocomplete();\n }\n // Auto-trigger for symbol-based completion like @, #, or provider triggers at token boundaries\n else if (self.autocompleteTriggerCharacters.includes(char)) {\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n const textBeforeCursor = currentLine.slice(0, self.state.cursorCol);\n const charBeforeSymbol = textBeforeCursor[textBeforeCursor.length - 2];\n if (textBeforeCursor.length === 1 || charBeforeSymbol === \" \" || charBeforeSymbol === \"\\t\") {\n self.tryTriggerAutocomplete();\n }\n }\n // Also auto-trigger when typing letters in a slash command or symbol completion context\n else if (/[a-zA-Z0-9.\\-_]/.test(char)) {\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n const textBeforeCursor = currentLine.slice(0, self.state.cursorCol);\n // Check if we're in a slash command (with or without space for arguments)\n if (self.isInSlashCommandContext(textBeforeCursor)) {\n self.tryTriggerAutocomplete();\n }\n // Check if we're in a symbol-based completion context like @, #, or provider triggers\n else if (self.autocompleteTriggerPattern.test(textBeforeCursor)) {\n self.tryTriggerAutocomplete();\n }\n }\n } else {\n self.updateAutocomplete();\n }\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { isWhitespaceChar } from "../../../utils.js";
|
|
2
|
+
export function do_insertTextAtCursorInternal(self, text) {
|
|
3
|
+
if (!text)
|
|
4
|
+
return;
|
|
5
|
+
// Normalize line endings and tabs
|
|
6
|
+
const normalized = self.normalizeText(text);
|
|
7
|
+
const insertedLines = normalized.split("\n");
|
|
8
|
+
const currentLine = self.state.lines[self.state.cursorLine] || "";
|
|
9
|
+
const beforeCursor = currentLine.slice(0, self.state.cursorCol);
|
|
10
|
+
const afterCursor = currentLine.slice(self.state.cursorCol);
|
|
11
|
+
if (insertedLines.length === 1) {
|
|
12
|
+
// Single line - insert at cursor position
|
|
13
|
+
self.state.lines[self.state.cursorLine] = beforeCursor + normalized + afterCursor;
|
|
14
|
+
self.setCursorCol(self.state.cursorCol + normalized.length);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
// Multi-line insertion
|
|
18
|
+
self.state.lines = [
|
|
19
|
+
// All lines before current line
|
|
20
|
+
...self.state.lines.slice(0, self.state.cursorLine),
|
|
21
|
+
// The first inserted line merged with text before cursor
|
|
22
|
+
beforeCursor + insertedLines[0],
|
|
23
|
+
// All middle inserted lines
|
|
24
|
+
...insertedLines.slice(1, -1),
|
|
25
|
+
// The last inserted line with text after cursor
|
|
26
|
+
insertedLines[insertedLines.length - 1] + afterCursor,
|
|
27
|
+
// All lines after current line
|
|
28
|
+
...self.state.lines.slice(self.state.cursorLine + 1),
|
|
29
|
+
];
|
|
30
|
+
self.state.cursorLine += insertedLines.length - 1;
|
|
31
|
+
self.setCursorCol((insertedLines[insertedLines.length - 1] || "").length);
|
|
32
|
+
}
|
|
33
|
+
if (self.onChange) {
|
|
34
|
+
self.onChange(self.getText());
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function do_insertCharacter(self, char, skipUndoCoalescing) {
|
|
38
|
+
self.exitHistoryBrowsing();
|
|
39
|
+
// Undo coalescing (fish-style):
|
|
40
|
+
// - Consecutive word chars coalesce into one undo unit
|
|
41
|
+
// - Space captures state before itself (so undo removes space+following word together)
|
|
42
|
+
// - Each space is separately undoable
|
|
43
|
+
// Skip coalescing when called from atomic operations (e.g., handlePaste)
|
|
44
|
+
if (!skipUndoCoalescing) {
|
|
45
|
+
if (isWhitespaceChar(char) || self.lastAction !== "type-word") {
|
|
46
|
+
self.pushUndoSnapshot();
|
|
47
|
+
}
|
|
48
|
+
self.lastAction = "type-word";
|
|
49
|
+
}
|
|
50
|
+
const line = self.state.lines[self.state.cursorLine] || "";
|
|
51
|
+
const before = line.slice(0, self.state.cursorCol);
|
|
52
|
+
const after = line.slice(self.state.cursorCol);
|
|
53
|
+
self.state.lines[self.state.cursorLine] = before + char + after;
|
|
54
|
+
self.setCursorCol(self.state.cursorCol + char.length);
|
|
55
|
+
if (self.onChange) {
|
|
56
|
+
self.onChange(self.getText());
|
|
57
|
+
}
|
|
58
|
+
// Check if we should trigger or update autocomplete
|
|
59
|
+
if (!self.autocompleteState) {
|
|
60
|
+
// Auto-trigger for "/" at the start of a line (slash commands)
|
|
61
|
+
if (char === "/" && self.isAtStartOfMessage()) {
|
|
62
|
+
self.tryTriggerAutocomplete();
|
|
63
|
+
}
|
|
64
|
+
// Auto-trigger for symbol-based completion like @, #, or provider triggers at token boundaries
|
|
65
|
+
else if (self.autocompleteTriggerCharacters.includes(char)) {
|
|
66
|
+
const currentLine = self.state.lines[self.state.cursorLine] || "";
|
|
67
|
+
const textBeforeCursor = currentLine.slice(0, self.state.cursorCol);
|
|
68
|
+
const charBeforeSymbol = textBeforeCursor[textBeforeCursor.length - 2];
|
|
69
|
+
if (textBeforeCursor.length === 1 || charBeforeSymbol === " " || charBeforeSymbol === "\t") {
|
|
70
|
+
self.tryTriggerAutocomplete();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Also auto-trigger when typing letters in a slash command or symbol completion context
|
|
74
|
+
else if (/[a-zA-Z0-9.\-_]/.test(char)) {
|
|
75
|
+
const currentLine = self.state.lines[self.state.cursorLine] || "";
|
|
76
|
+
const textBeforeCursor = currentLine.slice(0, self.state.cursorCol);
|
|
77
|
+
// Check if we're in a slash command (with or without space for arguments)
|
|
78
|
+
if (self.isInSlashCommandContext(textBeforeCursor)) {
|
|
79
|
+
self.tryTriggerAutocomplete();
|
|
80
|
+
}
|
|
81
|
+
// Check if we're in a symbol-based completion context like @, #, or provider triggers
|
|
82
|
+
else if (self.autocompleteTriggerPattern.test(textBeforeCursor)) {
|
|
83
|
+
self.tryTriggerAutocomplete();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
self.updateAutocomplete();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=text-insertion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-insertion.js","sourceRoot":"","sources":["../../../../src/components/editor/editor-methods/text-insertion.ts"],"names":[],"mappings":"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","sourcesContent":["import { isWhitespaceChar } from \"../../../utils.ts\";\nimport type { Editor } from \"../editor.ts\";\n\nexport function do_insertTextAtCursorInternal(self: Editor, text: string): void {\n if (!text) return;\n\n // Normalize line endings and tabs\n const normalized = self.normalizeText(text);\n const insertedLines = normalized.split(\"\\n\");\n\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n const beforeCursor = currentLine.slice(0, self.state.cursorCol);\n const afterCursor = currentLine.slice(self.state.cursorCol);\n\n if (insertedLines.length === 1) {\n // Single line - insert at cursor position\n self.state.lines[self.state.cursorLine] = beforeCursor + normalized + afterCursor;\n self.setCursorCol(self.state.cursorCol + normalized.length);\n } else {\n // Multi-line insertion\n self.state.lines = [\n // All lines before current line\n ...self.state.lines.slice(0, self.state.cursorLine),\n\n // The first inserted line merged with text before cursor\n beforeCursor + insertedLines[0],\n\n // All middle inserted lines\n ...insertedLines.slice(1, -1),\n\n // The last inserted line with text after cursor\n insertedLines[insertedLines.length - 1] + afterCursor,\n\n // All lines after current line\n ...self.state.lines.slice(self.state.cursorLine + 1),\n ];\n\n self.state.cursorLine += insertedLines.length - 1;\n self.setCursorCol((insertedLines[insertedLines.length - 1] || \"\").length);\n }\n\n if (self.onChange) {\n self.onChange(self.getText());\n }\n}\n\nexport function do_insertCharacter(self: Editor, char: string, skipUndoCoalescing?: boolean): void {\n self.exitHistoryBrowsing();\n\n // Undo coalescing (fish-style):\n // - Consecutive word chars coalesce into one undo unit\n // - Space captures state before itself (so undo removes space+following word together)\n // - Each space is separately undoable\n // Skip coalescing when called from atomic operations (e.g., handlePaste)\n if (!skipUndoCoalescing) {\n if (isWhitespaceChar(char) || self.lastAction !== \"type-word\") {\n self.pushUndoSnapshot();\n }\n self.lastAction = \"type-word\";\n }\n\n const line = self.state.lines[self.state.cursorLine] || \"\";\n\n const before = line.slice(0, self.state.cursorCol);\n const after = line.slice(self.state.cursorCol);\n\n self.state.lines[self.state.cursorLine] = before + char + after;\n self.setCursorCol(self.state.cursorCol + char.length);\n\n if (self.onChange) {\n self.onChange(self.getText());\n }\n\n // Check if we should trigger or update autocomplete\n if (!self.autocompleteState) {\n // Auto-trigger for \"/\" at the start of a line (slash commands)\n if (char === \"/\" && self.isAtStartOfMessage()) {\n self.tryTriggerAutocomplete();\n }\n // Auto-trigger for symbol-based completion like @, #, or provider triggers at token boundaries\n else if (self.autocompleteTriggerCharacters.includes(char)) {\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n const textBeforeCursor = currentLine.slice(0, self.state.cursorCol);\n const charBeforeSymbol = textBeforeCursor[textBeforeCursor.length - 2];\n if (textBeforeCursor.length === 1 || charBeforeSymbol === \" \" || charBeforeSymbol === \"\\t\") {\n self.tryTriggerAutocomplete();\n }\n }\n // Also auto-trigger when typing letters in a slash command or symbol completion context\n else if (/[a-zA-Z0-9.\\-_]/.test(char)) {\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n const textBeforeCursor = currentLine.slice(0, self.state.cursorCol);\n // Check if we're in a slash command (with or without space for arguments)\n if (self.isInSlashCommandContext(textBeforeCursor)) {\n self.tryTriggerAutocomplete();\n }\n // Check if we're in a symbol-based completion context like @, #, or provider triggers\n else if (self.autocompleteTriggerPattern.test(textBeforeCursor)) {\n self.tryTriggerAutocomplete();\n }\n }\n } else {\n self.updateAutocomplete();\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Editor } from "../editor.ts";
|
|
2
|
+
import type { LayoutLine } from "../types.ts";
|
|
3
|
+
export declare function do_layoutText(self: Editor, contentWidth: number): LayoutLine[];
|
|
4
|
+
export declare function do_getText(self: Editor): string;
|
|
5
|
+
export declare function do_expandPasteMarkers(self: Editor, text: string): string;
|
|
6
|
+
export declare function do_getExpandedText(self: Editor): string;
|
|
7
|
+
export declare function do_getLines(self: Editor): string[];
|
|
8
|
+
export declare function do_getCursor(self: Editor): {
|
|
9
|
+
line: number;
|
|
10
|
+
col: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function do_setText(self: Editor, text: string): void;
|
|
13
|
+
export declare function do_insertTextAtCursor(self: Editor, text: string): void;
|
|
14
|
+
export declare function do_normalizeText(_self: Editor, text: string): string;
|
|
15
|
+
//# sourceMappingURL=text-layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-layout.d.ts","sourceRoot":"","sources":["../../../../src/components/editor/editor-methods/text-layout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,UAAU,EAAE,CAsF9E;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAOxE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAElD;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAExE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAU3D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAOtE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpE","sourcesContent":["import { visibleWidth } from \"../../../utils.ts\";\nimport type { Editor } from \"../editor.ts\";\nimport { wordWrapLine } from \"../paste-marker.ts\";\nimport type { LayoutLine } from \"../types.ts\";\n\nexport function do_layoutText(self: Editor, contentWidth: number): LayoutLine[] {\n const layoutLines: LayoutLine[] = [];\n\n if (self.state.lines.length === 0 || (self.state.lines.length === 1 && self.state.lines[0] === \"\")) {\n // Empty editor\n layoutLines.push({\n text: \"\",\n hasCursor: true,\n cursorPos: 0,\n });\n return layoutLines;\n }\n\n // Process each logical line\n for (let i = 0; i < self.state.lines.length; i++) {\n const line = self.state.lines[i] || \"\";\n const isCurrentLine = i === self.state.cursorLine;\n const lineVisibleWidth = visibleWidth(line);\n\n if (lineVisibleWidth <= contentWidth) {\n // Line fits in one layout line\n if (isCurrentLine) {\n layoutLines.push({\n text: line,\n hasCursor: true,\n cursorPos: self.state.cursorCol,\n });\n } else {\n layoutLines.push({\n text: line,\n hasCursor: false,\n });\n }\n } else {\n // Line needs wrapping - use word-aware wrapping\n const chunks = wordWrapLine(line, contentWidth, [...self.segment(line, \"grapheme\")]);\n\n for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {\n const chunk = chunks[chunkIndex];\n if (!chunk) continue;\n\n const cursorPos = self.state.cursorCol;\n const isLastChunk = chunkIndex === chunks.length - 1;\n\n // Determine if cursor is in self chunk\n // For word-wrapped chunks, we need to handle the case where\n // cursor might be in trimmed whitespace at end of chunk\n let hasCursorInChunk = false;\n let adjustedCursorPos = 0;\n\n if (isCurrentLine) {\n if (isLastChunk) {\n // Last chunk: cursor belongs here if >= startIndex\n hasCursorInChunk = cursorPos >= chunk.startIndex;\n adjustedCursorPos = cursorPos - chunk.startIndex;\n } else {\n // Non-last chunk: cursor belongs here if in range [startIndex, endIndex)\n // But we need to handle the visual position in the trimmed text\n hasCursorInChunk = cursorPos >= chunk.startIndex && cursorPos < chunk.endIndex;\n if (hasCursorInChunk) {\n adjustedCursorPos = cursorPos - chunk.startIndex;\n // Clamp to text length (in case cursor was in trimmed whitespace)\n if (adjustedCursorPos > chunk.text.length) {\n adjustedCursorPos = chunk.text.length;\n }\n }\n }\n }\n\n if (hasCursorInChunk) {\n layoutLines.push({\n text: chunk.text,\n hasCursor: true,\n cursorPos: adjustedCursorPos,\n });\n } else {\n layoutLines.push({\n text: chunk.text,\n hasCursor: false,\n });\n }\n }\n }\n }\n\n return layoutLines;\n}\n\nexport function do_getText(self: Editor): string {\n return self.state.lines.join(\"\\n\");\n}\n\nexport function do_expandPasteMarkers(self: Editor, text: string): string {\n let result = text;\n for (const [pasteId, pasteContent] of self.pastes) {\n const markerRegex = new RegExp(`\\\\[paste #${pasteId}( (\\\\+\\\\d+ lines|\\\\d+ chars))?\\\\]`, \"g\");\n result = result.replace(markerRegex, () => pasteContent);\n }\n return result;\n}\n\nexport function do_getExpandedText(self: Editor): string {\n return self.expandPasteMarkers(self.state.lines.join(\"\\n\"));\n}\n\nexport function do_getLines(self: Editor): string[] {\n return [...self.state.lines];\n}\n\nexport function do_getCursor(self: Editor): { line: number; col: number } {\n return { line: self.state.cursorLine, col: self.state.cursorCol };\n}\n\nexport function do_setText(self: Editor, text: string): void {\n self.cancelAutocomplete();\n self.lastAction = null;\n self.exitHistoryBrowsing();\n const normalized = self.normalizeText(text);\n // Push undo snapshot if content differs (makes programmatic changes undoable)\n if (self.getText() !== normalized) {\n self.pushUndoSnapshot();\n }\n self.setTextInternal(normalized);\n}\n\nexport function do_insertTextAtCursor(self: Editor, text: string): void {\n if (!text) return;\n self.cancelAutocomplete();\n self.pushUndoSnapshot();\n self.lastAction = null;\n self.exitHistoryBrowsing();\n self.insertTextAtCursorInternal(text);\n}\n\nexport function do_normalizeText(_self: Editor, text: string): string {\n return text.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").replace(/\\t/g, \" \");\n}\n"]}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { visibleWidth } from "../../../utils.js";
|
|
2
|
+
import { wordWrapLine } from "../paste-marker.js";
|
|
3
|
+
export function do_layoutText(self, contentWidth) {
|
|
4
|
+
const layoutLines = [];
|
|
5
|
+
if (self.state.lines.length === 0 || (self.state.lines.length === 1 && self.state.lines[0] === "")) {
|
|
6
|
+
// Empty editor
|
|
7
|
+
layoutLines.push({
|
|
8
|
+
text: "",
|
|
9
|
+
hasCursor: true,
|
|
10
|
+
cursorPos: 0,
|
|
11
|
+
});
|
|
12
|
+
return layoutLines;
|
|
13
|
+
}
|
|
14
|
+
// Process each logical line
|
|
15
|
+
for (let i = 0; i < self.state.lines.length; i++) {
|
|
16
|
+
const line = self.state.lines[i] || "";
|
|
17
|
+
const isCurrentLine = i === self.state.cursorLine;
|
|
18
|
+
const lineVisibleWidth = visibleWidth(line);
|
|
19
|
+
if (lineVisibleWidth <= contentWidth) {
|
|
20
|
+
// Line fits in one layout line
|
|
21
|
+
if (isCurrentLine) {
|
|
22
|
+
layoutLines.push({
|
|
23
|
+
text: line,
|
|
24
|
+
hasCursor: true,
|
|
25
|
+
cursorPos: self.state.cursorCol,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
layoutLines.push({
|
|
30
|
+
text: line,
|
|
31
|
+
hasCursor: false,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Line needs wrapping - use word-aware wrapping
|
|
37
|
+
const chunks = wordWrapLine(line, contentWidth, [...self.segment(line, "grapheme")]);
|
|
38
|
+
for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
|
|
39
|
+
const chunk = chunks[chunkIndex];
|
|
40
|
+
if (!chunk)
|
|
41
|
+
continue;
|
|
42
|
+
const cursorPos = self.state.cursorCol;
|
|
43
|
+
const isLastChunk = chunkIndex === chunks.length - 1;
|
|
44
|
+
// Determine if cursor is in self chunk
|
|
45
|
+
// For word-wrapped chunks, we need to handle the case where
|
|
46
|
+
// cursor might be in trimmed whitespace at end of chunk
|
|
47
|
+
let hasCursorInChunk = false;
|
|
48
|
+
let adjustedCursorPos = 0;
|
|
49
|
+
if (isCurrentLine) {
|
|
50
|
+
if (isLastChunk) {
|
|
51
|
+
// Last chunk: cursor belongs here if >= startIndex
|
|
52
|
+
hasCursorInChunk = cursorPos >= chunk.startIndex;
|
|
53
|
+
adjustedCursorPos = cursorPos - chunk.startIndex;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Non-last chunk: cursor belongs here if in range [startIndex, endIndex)
|
|
57
|
+
// But we need to handle the visual position in the trimmed text
|
|
58
|
+
hasCursorInChunk = cursorPos >= chunk.startIndex && cursorPos < chunk.endIndex;
|
|
59
|
+
if (hasCursorInChunk) {
|
|
60
|
+
adjustedCursorPos = cursorPos - chunk.startIndex;
|
|
61
|
+
// Clamp to text length (in case cursor was in trimmed whitespace)
|
|
62
|
+
if (adjustedCursorPos > chunk.text.length) {
|
|
63
|
+
adjustedCursorPos = chunk.text.length;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (hasCursorInChunk) {
|
|
69
|
+
layoutLines.push({
|
|
70
|
+
text: chunk.text,
|
|
71
|
+
hasCursor: true,
|
|
72
|
+
cursorPos: adjustedCursorPos,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
layoutLines.push({
|
|
77
|
+
text: chunk.text,
|
|
78
|
+
hasCursor: false,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return layoutLines;
|
|
85
|
+
}
|
|
86
|
+
export function do_getText(self) {
|
|
87
|
+
return self.state.lines.join("\n");
|
|
88
|
+
}
|
|
89
|
+
export function do_expandPasteMarkers(self, text) {
|
|
90
|
+
let result = text;
|
|
91
|
+
for (const [pasteId, pasteContent] of self.pastes) {
|
|
92
|
+
const markerRegex = new RegExp(`\\[paste #${pasteId}( (\\+\\d+ lines|\\d+ chars))?\\]`, "g");
|
|
93
|
+
result = result.replace(markerRegex, () => pasteContent);
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
export function do_getExpandedText(self) {
|
|
98
|
+
return self.expandPasteMarkers(self.state.lines.join("\n"));
|
|
99
|
+
}
|
|
100
|
+
export function do_getLines(self) {
|
|
101
|
+
return [...self.state.lines];
|
|
102
|
+
}
|
|
103
|
+
export function do_getCursor(self) {
|
|
104
|
+
return { line: self.state.cursorLine, col: self.state.cursorCol };
|
|
105
|
+
}
|
|
106
|
+
export function do_setText(self, text) {
|
|
107
|
+
self.cancelAutocomplete();
|
|
108
|
+
self.lastAction = null;
|
|
109
|
+
self.exitHistoryBrowsing();
|
|
110
|
+
const normalized = self.normalizeText(text);
|
|
111
|
+
// Push undo snapshot if content differs (makes programmatic changes undoable)
|
|
112
|
+
if (self.getText() !== normalized) {
|
|
113
|
+
self.pushUndoSnapshot();
|
|
114
|
+
}
|
|
115
|
+
self.setTextInternal(normalized);
|
|
116
|
+
}
|
|
117
|
+
export function do_insertTextAtCursor(self, text) {
|
|
118
|
+
if (!text)
|
|
119
|
+
return;
|
|
120
|
+
self.cancelAutocomplete();
|
|
121
|
+
self.pushUndoSnapshot();
|
|
122
|
+
self.lastAction = null;
|
|
123
|
+
self.exitHistoryBrowsing();
|
|
124
|
+
self.insertTextAtCursorInternal(text);
|
|
125
|
+
}
|
|
126
|
+
export function do_normalizeText(_self, text) {
|
|
127
|
+
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\t/g, " ");
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=text-layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-layout.js","sourceRoot":"","sources":["../../../../src/components/editor/editor-methods/text-layout.ts"],"names":[],"mappings":"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","sourcesContent":["import { visibleWidth } from \"../../../utils.ts\";\nimport type { Editor } from \"../editor.ts\";\nimport { wordWrapLine } from \"../paste-marker.ts\";\nimport type { LayoutLine } from \"../types.ts\";\n\nexport function do_layoutText(self: Editor, contentWidth: number): LayoutLine[] {\n const layoutLines: LayoutLine[] = [];\n\n if (self.state.lines.length === 0 || (self.state.lines.length === 1 && self.state.lines[0] === \"\")) {\n // Empty editor\n layoutLines.push({\n text: \"\",\n hasCursor: true,\n cursorPos: 0,\n });\n return layoutLines;\n }\n\n // Process each logical line\n for (let i = 0; i < self.state.lines.length; i++) {\n const line = self.state.lines[i] || \"\";\n const isCurrentLine = i === self.state.cursorLine;\n const lineVisibleWidth = visibleWidth(line);\n\n if (lineVisibleWidth <= contentWidth) {\n // Line fits in one layout line\n if (isCurrentLine) {\n layoutLines.push({\n text: line,\n hasCursor: true,\n cursorPos: self.state.cursorCol,\n });\n } else {\n layoutLines.push({\n text: line,\n hasCursor: false,\n });\n }\n } else {\n // Line needs wrapping - use word-aware wrapping\n const chunks = wordWrapLine(line, contentWidth, [...self.segment(line, \"grapheme\")]);\n\n for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {\n const chunk = chunks[chunkIndex];\n if (!chunk) continue;\n\n const cursorPos = self.state.cursorCol;\n const isLastChunk = chunkIndex === chunks.length - 1;\n\n // Determine if cursor is in self chunk\n // For word-wrapped chunks, we need to handle the case where\n // cursor might be in trimmed whitespace at end of chunk\n let hasCursorInChunk = false;\n let adjustedCursorPos = 0;\n\n if (isCurrentLine) {\n if (isLastChunk) {\n // Last chunk: cursor belongs here if >= startIndex\n hasCursorInChunk = cursorPos >= chunk.startIndex;\n adjustedCursorPos = cursorPos - chunk.startIndex;\n } else {\n // Non-last chunk: cursor belongs here if in range [startIndex, endIndex)\n // But we need to handle the visual position in the trimmed text\n hasCursorInChunk = cursorPos >= chunk.startIndex && cursorPos < chunk.endIndex;\n if (hasCursorInChunk) {\n adjustedCursorPos = cursorPos - chunk.startIndex;\n // Clamp to text length (in case cursor was in trimmed whitespace)\n if (adjustedCursorPos > chunk.text.length) {\n adjustedCursorPos = chunk.text.length;\n }\n }\n }\n }\n\n if (hasCursorInChunk) {\n layoutLines.push({\n text: chunk.text,\n hasCursor: true,\n cursorPos: adjustedCursorPos,\n });\n } else {\n layoutLines.push({\n text: chunk.text,\n hasCursor: false,\n });\n }\n }\n }\n }\n\n return layoutLines;\n}\n\nexport function do_getText(self: Editor): string {\n return self.state.lines.join(\"\\n\");\n}\n\nexport function do_expandPasteMarkers(self: Editor, text: string): string {\n let result = text;\n for (const [pasteId, pasteContent] of self.pastes) {\n const markerRegex = new RegExp(`\\\\[paste #${pasteId}( (\\\\+\\\\d+ lines|\\\\d+ chars))?\\\\]`, \"g\");\n result = result.replace(markerRegex, () => pasteContent);\n }\n return result;\n}\n\nexport function do_getExpandedText(self: Editor): string {\n return self.expandPasteMarkers(self.state.lines.join(\"\\n\"));\n}\n\nexport function do_getLines(self: Editor): string[] {\n return [...self.state.lines];\n}\n\nexport function do_getCursor(self: Editor): { line: number; col: number } {\n return { line: self.state.cursorLine, col: self.state.cursorCol };\n}\n\nexport function do_setText(self: Editor, text: string): void {\n self.cancelAutocomplete();\n self.lastAction = null;\n self.exitHistoryBrowsing();\n const normalized = self.normalizeText(text);\n // Push undo snapshot if content differs (makes programmatic changes undoable)\n if (self.getText() !== normalized) {\n self.pushUndoSnapshot();\n }\n self.setTextInternal(normalized);\n}\n\nexport function do_insertTextAtCursor(self: Editor, text: string): void {\n if (!text) return;\n self.cancelAutocomplete();\n self.pushUndoSnapshot();\n self.lastAction = null;\n self.exitHistoryBrowsing();\n self.insertTextAtCursorInternal(text);\n}\n\nexport function do_normalizeText(_self: Editor, text: string): string {\n return text.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").replace(/\\t/g, \" \");\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Editor } from "../editor.ts";
|
|
2
|
+
export declare function do_computeVerticalMoveColumn(self: Editor, currentVisualCol: number, sourceMaxVisualCol: number, targetMaxVisualCol: number): number;
|
|
3
|
+
export declare function do_moveToLineStart(self: Editor): void;
|
|
4
|
+
export declare function do_moveToLineEnd(self: Editor): void;
|
|
5
|
+
export declare function do_deleteToStartOfLine(self: Editor): void;
|
|
6
|
+
export declare function do_deleteToEndOfLine(self: Editor): void;
|
|
7
|
+
export declare function do_deleteWordBackwards(self: Editor): void;
|
|
8
|
+
//# sourceMappingURL=vertical-movement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vertical-movement.d.ts","sourceRoot":"","sources":["../../../../src/components/editor/editor-methods/vertical-movement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,MAAM,EACxB,kBAAkB,EAAE,MAAM,EAC1B,kBAAkB,EAAE,MAAM,GACzB,MAAM,CA2BR;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGrD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAInD;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAiCzD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA8BvD;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA2CzD","sourcesContent":["import type { Editor } from \"../editor.ts\";\n\nexport function do_computeVerticalMoveColumn(\n self: Editor,\n currentVisualCol: number,\n sourceMaxVisualCol: number,\n targetMaxVisualCol: number,\n): number {\n const hasPreferred = self.preferredVisualCol !== null; // P\n const cursorInMiddle = currentVisualCol < sourceMaxVisualCol; // S\n const targetTooShort = targetMaxVisualCol < currentVisualCol; // T\n\n if (!hasPreferred || cursorInMiddle) {\n if (targetTooShort) {\n // Cases 2 and 7\n self.preferredVisualCol = currentVisualCol;\n return targetMaxVisualCol;\n }\n\n // Cases 1 and 6\n self.preferredVisualCol = null;\n return currentVisualCol;\n }\n\n const targetCantFitPreferred = targetMaxVisualCol < self.preferredVisualCol!; // U\n if (targetTooShort || targetCantFitPreferred) {\n // Cases 4 and 5\n return targetMaxVisualCol;\n }\n\n // Case 3\n const result = self.preferredVisualCol!;\n self.preferredVisualCol = null;\n return result;\n}\n\nexport function do_moveToLineStart(self: Editor): void {\n self.lastAction = null;\n self.setCursorCol(0);\n}\n\nexport function do_moveToLineEnd(self: Editor): void {\n self.lastAction = null;\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n self.setCursorCol(currentLine.length);\n}\n\nexport function do_deleteToStartOfLine(self: Editor): void {\n self.exitHistoryBrowsing();\n\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n\n if (self.state.cursorCol > 0) {\n self.pushUndoSnapshot();\n\n // Calculate text to be deleted and save to kill ring (backward deletion = prepend)\n const deletedText = currentLine.slice(0, self.state.cursorCol);\n self.killRing.push(deletedText, { prepend: true, accumulate: self.lastAction === \"kill\" });\n self.lastAction = \"kill\";\n\n // Delete from start of line up to cursor\n self.state.lines[self.state.cursorLine] = currentLine.slice(self.state.cursorCol);\n self.setCursorCol(0);\n } else if (self.state.cursorLine > 0) {\n self.pushUndoSnapshot();\n\n // At start of line - merge with previous line, treating newline as deleted text\n self.killRing.push(\"\\n\", { prepend: true, accumulate: self.lastAction === \"kill\" });\n self.lastAction = \"kill\";\n\n const previousLine = self.state.lines[self.state.cursorLine - 1] || \"\";\n self.state.lines[self.state.cursorLine - 1] = previousLine + currentLine;\n self.state.lines.splice(self.state.cursorLine, 1);\n self.state.cursorLine--;\n self.setCursorCol(previousLine.length);\n }\n\n if (self.onChange) {\n self.onChange(self.getText());\n }\n}\n\nexport function do_deleteToEndOfLine(self: Editor): void {\n self.exitHistoryBrowsing();\n\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n\n if (self.state.cursorCol < currentLine.length) {\n self.pushUndoSnapshot();\n\n // Calculate text to be deleted and save to kill ring (forward deletion = append)\n const deletedText = currentLine.slice(self.state.cursorCol);\n self.killRing.push(deletedText, { prepend: false, accumulate: self.lastAction === \"kill\" });\n self.lastAction = \"kill\";\n\n // Delete from cursor to end of line\n self.state.lines[self.state.cursorLine] = currentLine.slice(0, self.state.cursorCol);\n } else if (self.state.cursorLine < self.state.lines.length - 1) {\n self.pushUndoSnapshot();\n\n // At end of line - merge with next line, treating newline as deleted text\n self.killRing.push(\"\\n\", { prepend: false, accumulate: self.lastAction === \"kill\" });\n self.lastAction = \"kill\";\n\n const nextLine = self.state.lines[self.state.cursorLine + 1] || \"\";\n self.state.lines[self.state.cursorLine] = currentLine + nextLine;\n self.state.lines.splice(self.state.cursorLine + 1, 1);\n }\n\n if (self.onChange) {\n self.onChange(self.getText());\n }\n}\n\nexport function do_deleteWordBackwards(self: Editor): void {\n self.exitHistoryBrowsing();\n\n const currentLine = self.state.lines[self.state.cursorLine] || \"\";\n\n // If at start of line, behave like backspace at column 0 (merge with previous line)\n if (self.state.cursorCol === 0) {\n if (self.state.cursorLine > 0) {\n self.pushUndoSnapshot();\n\n // Treat newline as deleted text (backward deletion = prepend)\n self.killRing.push(\"\\n\", { prepend: true, accumulate: self.lastAction === \"kill\" });\n self.lastAction = \"kill\";\n\n const previousLine = self.state.lines[self.state.cursorLine - 1] || \"\";\n self.state.lines[self.state.cursorLine - 1] = previousLine + currentLine;\n self.state.lines.splice(self.state.cursorLine, 1);\n self.state.cursorLine--;\n self.setCursorCol(previousLine.length);\n }\n } else {\n self.pushUndoSnapshot();\n\n // Save lastAction before cursor movement (moveWordBackwards resets it)\n const wasKill = self.lastAction === \"kill\";\n\n const oldCursorCol = self.state.cursorCol;\n self.moveWordBackwards();\n const deleteFrom = self.state.cursorCol;\n self.setCursorCol(oldCursorCol);\n\n const deletedText = currentLine.slice(deleteFrom, self.state.cursorCol);\n self.killRing.push(deletedText, { prepend: true, accumulate: wasKill });\n self.lastAction = \"kill\";\n\n self.state.lines[self.state.cursorLine] =\n currentLine.slice(0, deleteFrom) + currentLine.slice(self.state.cursorCol);\n self.setCursorCol(deleteFrom);\n }\n\n if (self.onChange) {\n self.onChange(self.getText());\n }\n}\n"]}
|