Skip to content

Update tree-sitter-highlight requirement from 0.24.7 to 0.25.1 - #2

Closed
dependabot[bot] wants to merge 1 commit into
masterfrom
dependabot/cargo/tree-sitter-highlight-0.25.1
Closed

Update tree-sitter-highlight requirement from 0.24.7 to 0.25.1#2
dependabot[bot] wants to merge 1 commit into
masterfrom
dependabot/cargo/tree-sitter-highlight-0.25.1

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Feb 3, 2025

Copy link
Copy Markdown
Contributor

Updates the requirements on tree-sitter-highlight to permit the latest version.

Release notes

Sourced from tree-sitter-highlight's releases.

v0.25.1

Changelog

[0.25.1] — 2025-02-02

Features

Bug Fixes

Documentation

  • Correct build steps for WASM files

Build System and CI

Other

  • rust: Correct doc comments
Commits
  • f5afe47 build: bump version to 0.25.1
  • f20d4b0 docs: correct build steps for WASM files
  • 05d443a style(rust): correct doc comments
  • eed662d fix(bindings): correct Zig bindings to expose a language function
  • 9ad096e fix(lib): prevent finished_tree assertion failure
  • ac8a4ba ci: use ubuntu-22.04 for x64 builds
  • c2221f2 feat(cli): specify abi version via env var
  • 16aaed7 build: update authors
  • a115e51 feat(web): include C source files for debugging
  • 060e69e build(web): relocate source files in WASM sourcemap
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Updates the requirements on [tree-sitter-highlight](https://github.com/tree-sitter/tree-sitter) to permit the latest version.
- [Release notes](https://github.com/tree-sitter/tree-sitter/releases)
- [Commits](tree-sitter/tree-sitter@v0.24.7...v0.25.1)

---
updated-dependencies:
- dependency-name: tree-sitter-highlight
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file rust Pull requests that update Rust code labels Feb 3, 2025
@dependabot @github

dependabot Bot commented on behalf of github Feb 24, 2025

Copy link
Copy Markdown
Contributor Author

Superseded by #3.

@dependabot dependabot Bot closed this Feb 24, 2025
@dependabot
dependabot Bot deleted the dependabot/cargo/tree-sitter-highlight-0.25.1 branch February 24, 2025 01:47
sinelaw pushed a commit that referenced this pull request Nov 5, 2025
Issue #1: Keys leaking through to editor in prompt context
Previously, when a prompt was active (e.g., git grep), unbound keys would
fall back to Normal context bindings, causing keys like PageUp to affect
the editor instead of being masked by the prompt.

Fix:
- Modified KeybindingResolver::resolve() to only allow application-wide
  actions (Quit, Save, Help) to fall back to Normal context
- Added is_application_wide_action() helper to define which actions are
  globally accessible
- This ensures prompts properly mask non-essential keybindings

Issue #2: Git grep jumping to wrong column
Git grep was parsing the column from results but ignoring it, always
positioning cursor at line start (column 1) instead of the match column.

Fix:
- Changed _column to column (removed underscore) in editor.rs:1647
- Calculate final_position = target_byte + column_offset
- Git grep --column returns 1-indexed byte offsets, converted to 0-indexed
- Updated status message to show "Jumped to file:line:column"
- Added bounds checking to ensure position doesn't exceed buffer length

Bonus Enhancement:
- Added PromptPageUp and PromptPageDown actions for scrolling through
  prompt suggestions (moves by 10 items)
- Added PageUp/PageDown keybindings to Prompt context
- Makes navigating large suggestion lists much easier

Testing:
- cargo build: successful
- cargo test --lib: 295 passed, 2 pre-existing LSP failures
- Individual git test (test_git_grep_cursor_position_accuracy): passes
  and shows cursor at correct position 26 (line start + column offset)
- Batch test flakiness pre-existing per original implementation notes

All changes maintain backward compatibility with existing functionality.
sinelaw pushed a commit that referenced this pull request Nov 5, 2025
Issue #1: Keys leaking through to editor in prompt context
Previously, when a prompt was active (e.g., git grep), unbound keys would
fall back to Normal context bindings, causing keys like PageUp to affect
the editor instead of being masked by the prompt.

Fix:
- Modified KeybindingResolver::resolve() to only allow application-wide
  actions (Quit, Save, Help) to fall back to Normal context
- Added is_application_wide_action() helper to define which actions are
  globally accessible
- This ensures prompts properly mask non-essential keybindings

Issue #2: Git grep jumping to wrong column
Git grep was parsing the column from results but ignoring it, always
positioning cursor at line start (column 1) instead of the match column.

Fix:
- Changed _column to column (removed underscore) in editor.rs:1647
- Calculate final_position = target_byte + column_offset
- Git grep --column returns 1-indexed byte offsets, converted to 0-indexed
- Updated status message to show "Jumped to file:line:column"
- Added bounds checking to ensure position doesn't exceed buffer length

Bonus Enhancement:
- Added PromptPageUp and PromptPageDown actions for scrolling through
  prompt suggestions (moves by 10 items)
- Added PageUp/PageDown keybindings to Prompt context
- Makes navigating large suggestion lists much easier

Testing:
- cargo build: successful
- cargo test --lib: 295 passed, 2 pre-existing LSP failures
- Individual git test (test_git_grep_cursor_position_accuracy): passes
  and shows cursor at correct position 26 (line start + column offset)
- Batch test flakiness pre-existing per original implementation notes

All changes maintain backward compatibility with existing functionality.
sinelaw pushed a commit that referenced this pull request Nov 5, 2025
Issue #1: Keys leaking through to editor in prompt context
Previously, when a prompt was active (e.g., git grep), unbound keys would
fall back to Normal context bindings, causing keys like PageUp to affect
the editor instead of being masked by the prompt.

Fix:
- Modified KeybindingResolver::resolve() to only allow application-wide
  actions (Quit, Save, Help) to fall back to Normal context
- Added is_application_wide_action() helper to define which actions are
  globally accessible
- This ensures prompts properly mask non-essential keybindings

Issue #2: Git grep jumping to wrong column
Git grep was parsing the column from results but ignoring it, always
positioning cursor at line start (column 1) instead of the match column.

Fix:
- Changed _column to column (removed underscore) in editor.rs:1647
- Calculate final_position = target_byte + column_offset
- Git grep --column returns 1-indexed byte offsets, converted to 0-indexed
- Updated status message to show "Jumped to file:line:column"
- Added bounds checking to ensure position doesn't exceed buffer length

Bonus Enhancement:
- Added PromptPageUp and PromptPageDown actions for scrolling through
  prompt suggestions (moves by 10 items)
- Added PageUp/PageDown keybindings to Prompt context
- Makes navigating large suggestion lists much easier

Testing:
- cargo build: successful
- cargo test --lib: 295 passed, 2 pre-existing LSP failures
- Individual git test (test_git_grep_cursor_position_accuracy): passes
  and shows cursor at correct position 26 (line start + column offset)
- Batch test flakiness pre-existing per original implementation notes

All changes maintain backward compatibility with existing functionality.
sinelaw pushed a commit that referenced this pull request Nov 10, 2025
…ce newlines

This commit implements two critical auto-indent features using tree-sitter:

1. **No indent after closing brace**: When pressing Enter after a closing delimiter (}, ], )),
   the next line correctly receives 0 indent instead of inheriting the block's indent.
   - Uses tree-sitter @indent captures with "last_nonws_is_closing" heuristic
   - Checks if last non-whitespace on current line is a closing delimiter
   - If so, skips @indent node matching to return baseline indent
   - Works with partial parsing (MAX_PARSE_BYTES window)

2. **Auto-dedent on typing closing delimiter**: When typing }, ], or ) on an indented line
   with only whitespace, automatically dedents to the correct nesting level.
   - Uses tree-sitter to count @indent nodes containing cursor position
   - Calculates correct indent as: baseline + (nesting_level - 1) * tab_size
   - Deletes incorrect spacing and inserts delimiter at proper column
   - Works across all languages with tree-sitter support

Both features:
- Use tree-sitter @indent/@dedent captures (not just pattern matching)
- Support partial/incremental parsing for huge files
- Include verification tests that confirm tree-sitter is being used
- Fall back gracefully when tree-sitter unavailable

Added tests:
- test_no_indent_after_close_brace: e2e test for issue #1
- test_auto_dedent_on_close_brace: e2e test for issue #2
- test_tree_sitter_enter_after_close_brace_returns_zero: unit test verifying tree-sitter
- test_tree_sitter_auto_dedent_on_close_brace: unit test for dedent calculation
- test_tree_sitter_handles_multiple_languages: multi-language verification

All 15 e2e tests pass, all 14 unit tests pass.
sinelaw pushed a commit that referenced this pull request Nov 10, 2025
…ce newlines

This commit implements two critical auto-indent features using tree-sitter:

1. **No indent after closing brace**: When pressing Enter after a closing delimiter (}, ], )),
   the next line correctly receives 0 indent instead of inheriting the block's indent.
   - Uses tree-sitter @indent captures with "last_nonws_is_closing" heuristic
   - Checks if last non-whitespace on current line is a closing delimiter
   - If so, skips @indent node matching to return baseline indent
   - Works with partial parsing (MAX_PARSE_BYTES window)

2. **Auto-dedent on typing closing delimiter**: When typing }, ], or ) on an indented line
   with only whitespace, automatically dedents to the correct nesting level.
   - Uses tree-sitter to count @indent nodes containing cursor position
   - Calculates correct indent as: baseline + (nesting_level - 1) * tab_size
   - Deletes incorrect spacing and inserts delimiter at proper column
   - Works across all languages with tree-sitter support

Both features:
- Use tree-sitter @indent/@dedent captures (not just pattern matching)
- Support partial/incremental parsing for huge files
- Include verification tests that confirm tree-sitter is being used
- Fall back gracefully when tree-sitter unavailable

Added tests:
- test_no_indent_after_close_brace: e2e test for issue #1
- test_auto_dedent_on_close_brace: e2e test for issue #2
- test_tree_sitter_enter_after_close_brace_returns_zero: unit test verifying tree-sitter
- test_tree_sitter_auto_dedent_on_close_brace: unit test for dedent calculation
- test_tree_sitter_handles_multiple_languages: multi-language verification

All 15 e2e tests pass, all 14 unit tests pass.
sinelaw added a commit that referenced this pull request Nov 19, 2025
Issue #1: Keys leaking through to editor in prompt context
Previously, when a prompt was active (e.g., git grep), unbound keys would
fall back to Normal context bindings, causing keys like PageUp to affect
the editor instead of being masked by the prompt.

Fix:
- Modified KeybindingResolver::resolve() to only allow application-wide
  actions (Quit, Save, Help) to fall back to Normal context
- Added is_application_wide_action() helper to define which actions are
  globally accessible
- This ensures prompts properly mask non-essential keybindings

Issue #2: Git grep jumping to wrong column
Git grep was parsing the column from results but ignoring it, always
positioning cursor at line start (column 1) instead of the match column.

Fix:
- Changed _column to column (removed underscore) in editor.rs:1647
- Calculate final_position = target_byte + column_offset
- Git grep --column returns 1-indexed byte offsets, converted to 0-indexed
- Updated status message to show "Jumped to file:line:column"
- Added bounds checking to ensure position doesn't exceed buffer length

Bonus Enhancement:
- Added PromptPageUp and PromptPageDown actions for scrolling through
  prompt suggestions (moves by 10 items)
- Added PageUp/PageDown keybindings to Prompt context
- Makes navigating large suggestion lists much easier

Testing:
- cargo build: successful
- cargo test --lib: 295 passed, 2 pre-existing LSP failures
- Individual git test (test_git_grep_cursor_position_accuracy): passes
  and shows cursor at correct position 26 (line start + column offset)
- Batch test flakiness pre-existing per original implementation notes

All changes maintain backward compatibility with existing functionality.
sinelaw added a commit that referenced this pull request Nov 19, 2025
…ce newlines

This commit implements two critical auto-indent features using tree-sitter:

1. **No indent after closing brace**: When pressing Enter after a closing delimiter (}, ], )),
   the next line correctly receives 0 indent instead of inheriting the block's indent.
   - Uses tree-sitter @indent captures with "last_nonws_is_closing" heuristic
   - Checks if last non-whitespace on current line is a closing delimiter
   - If so, skips @indent node matching to return baseline indent
   - Works with partial parsing (MAX_PARSE_BYTES window)

2. **Auto-dedent on typing closing delimiter**: When typing }, ], or ) on an indented line
   with only whitespace, automatically dedents to the correct nesting level.
   - Uses tree-sitter to count @indent nodes containing cursor position
   - Calculates correct indent as: baseline + (nesting_level - 1) * tab_size
   - Deletes incorrect spacing and inserts delimiter at proper column
   - Works across all languages with tree-sitter support

Both features:
- Use tree-sitter @indent/@dedent captures (not just pattern matching)
- Support partial/incremental parsing for huge files
- Include verification tests that confirm tree-sitter is being used
- Fall back gracefully when tree-sitter unavailable

Added tests:
- test_no_indent_after_close_brace: e2e test for issue #1
- test_auto_dedent_on_close_brace: e2e test for issue #2
- test_tree_sitter_enter_after_close_brace_returns_zero: unit test verifying tree-sitter
- test_tree_sitter_auto_dedent_on_close_brace: unit test for dedent calculation
- test_tree_sitter_handles_multiple_languages: multi-language verification

All 15 e2e tests pass, all 14 unit tests pass.
sinelaw pushed a commit that referenced this pull request Mar 6, 2026
Key changes since Feb 26:
- 10 previously triaged issues confirmed CLOSED (including our #1, #2, #4 bug
  priorities and #1 enhancement priority)
- 9 new issues added (#1128-#1202)
- Corrected #716 status (still open, not closed as previously stated)
- Added new duplicate groups: Deno/multi-LSP, C# naming, clipboard/OSC52
- New sections: Packaging/Distribution, Language/Syntax support
- Added velocity tracking table showing triage effectiveness
- #1054 still open but reporter confirmed fix - should be closed

https://claude.ai/code/session_01BsP93dzqKSGyLSbMRZ9LYS
sinelaw pushed a commit that referenced this pull request Apr 9, 2026
When a language had more than one LSP server configured (e.g. a
per-language server plus a universal/global one), `try_spawn()` would
pre-check that *any* config had `auto_start=true && enabled=true` and
then delegate to `force_spawn()`, which indiscriminately spawned *every*
enabled config — ignoring each config's own `auto_start` flag. Users
reasonably expected that marking a single server as auto-start would
only spawn *that* server on buffer load, but instead opening a file
dragged in every other enabled server (including ones they had
deliberately left as opt-in manual).

`force_spawn()` now distinguishes between its two callers:
- Manual paths (command palette Start/Restart, confirmation popup,
  manual restart) add the language to `allowed_languages` before
  calling in and still spawn every configured server regardless of
  per-config flags.
- The auto-start path (buffer open via `try_spawn`, crash recovery)
  sees `manually_allowed = false` and now honours each config's
  `enabled && auto_start` individually.

This preserves all prior behaviour for manually-started languages
(including the "start a disabled server" command) while fixing the
spurious spawn of opt-in servers on buffer load.

Add `lsp_autostart_selective` e2e tests covering the three
constellations called out by the user:
- multiple servers for a single language with mixed enabled/auto_start
- a universal LSP server with enabled=true, auto_start=false (which
  previously got dragged in by a per-language auto_start server)
- a server configured for a different language than the opened buffer

Each fake server publishes a diagnostic of distinct severity so the
tests verify behaviour via the rendered status bar (per CONTRIBUTING.md
rule #2 — examine rendered output, not internal state), with log-file
existence checks as a belt-and-braces negative assertion.

https://claude.ai/code/session_01NBtMusDeDrRQog7MGvNUtF
sinelaw pushed a commit that referenced this pull request Apr 9, 2026
When a language had more than one LSP server configured (e.g. a
per-language server plus a universal/global one), `try_spawn()` would
pre-check that *any* config had `auto_start=true && enabled=true` and
then delegate to `force_spawn()`, which indiscriminately spawned *every*
enabled config — ignoring each config's own `auto_start` flag. Users
reasonably expected that marking a single server as auto-start would
only spawn *that* server on buffer load, but instead opening a file
dragged in every other enabled server (including ones they had
deliberately left as opt-in manual).

`force_spawn()` now distinguishes between its two callers:
- Manual paths (command palette Start/Restart, confirmation popup,
  manual restart) add the language to `allowed_languages` before
  calling in and still spawn every configured server regardless of
  per-config flags.
- The auto-start path (buffer open via `try_spawn`, crash recovery)
  sees `manually_allowed = false` and now honours each config's
  `enabled && auto_start` individually.

This preserves all prior behaviour for manually-started languages
(including the "start a disabled server" command) while fixing the
spurious spawn of opt-in servers on buffer load.

Add `lsp_autostart_selective` e2e tests covering the three
constellations called out by the user:
- multiple servers for a single language with mixed enabled/auto_start
- a universal LSP server with enabled=true, auto_start=false (which
  previously got dragged in by a per-language auto_start server)
- a server configured for a different language than the opened buffer

Each fake server publishes a diagnostic of distinct severity so the
tests verify behaviour via the rendered status bar (per CONTRIBUTING.md
rule #2 — examine rendered output, not internal state), with log-file
existence checks as a belt-and-braces negative assertion.

https://claude.ai/code/session_01NBtMusDeDrRQog7MGvNUtF
sinelaw pushed a commit that referenced this pull request Apr 26, 2026
…ix to flash plan

Reviewed CONTRIBUTING.md and existing test infrastructure
(EditorTestHarness, copy_plugin, wait_until, proptest). Added a
testing section that maps the rules to concrete tests for flash:

- Test layers table (unit / property / integration / e2e / interaction
  / perf / snapshot) with the right tool for each.
- Property tests for the labeler invariants — chiefly the "assigned
  label never collides with any match's next-char" rule, which is
  *the* core flash correctness property.
- Cross-feature interaction matrix (rule #8) covering 17 concurrent-
  feature pairs: multi-cursor, vi_mode, LSP, splits, folds, soft-
  wrapped lines, CRLF, theme switch mid-flash, terminal resize, modal
  popups, concurrent plugin overlays, etc.
- A "reproduce before claiming" row per proposed API addition (#1#15)
  with the failing-without-fix test shape for each.
- Performance assertions guarding rule #2 (viewport-bounded scans).
- Lists test-infra additions needed: a flash-test plugin fixture, a
  `wait_until_screen_matches` convenience, and an
  `assert_no_orphan_overlays_in_namespace` cleanup-invariant check.
sinelaw pushed a commit that referenced this pull request Apr 26, 2026
Existing flash e2e tests peeked at editor model state — `editor_mode()`,
`cursor_position()`, `command_registry()` — to wait/assert.  CONTRIBUTING
is explicit: "asserts only on rendered output."  vi_mode tests follow
the same anti-pattern, but new code should be cleaner.

Why it matters here: the model-state accessors and the rendered
screen aren't always in lockstep under load.  `wait_until` ticks
every 50ms and may see `editor_mode == "flash"` set in the snapshot
before the corresponding `setStatus("Flash[]")` command has been
processed by the render side.  The CI hangs (6 flash tests TIMEOUT
at 180s under high parallelism) suggest the existing tests were
sensitive to that timing — passes locally where contention is lower.

Changes:
- arm_flash: drops the command_registry.get_all() peek and the
  `editor_mode()` peek.  Single readiness signal: the visible
  `Flash[]` status banner.  This is the same signal the plugin's
  setStatus call sets *inside* the main loop, so seeing it on
  screen proves the editor has processed setEditorMode AND
  beginKeyCapture AND the first iteration's setStatus — guarantees
  the next getNextKey is armed.
- All `wait_until(editor_mode != "flash")` waits → screen-only
  `wait_until(!screen.contains("Flash["))`.
- All `cursor_position()` assertions → marker-glyph assertions:
  send a unique character (`@`) after the jump and check the
  rendered buffer for `@hello there` etc.

Final flash tests now use exclusively screen-observable predicates.

All 6 flash + 46 vi_mode + 41 markdown_compose tests still pass
locally.  Hoping this also unblocks CI by removing the model/render
race window.
sinelaw pushed a commit that referenced this pull request Apr 26, 2026
…ix to flash plan

Reviewed CONTRIBUTING.md and existing test infrastructure
(EditorTestHarness, copy_plugin, wait_until, proptest). Added a
testing section that maps the rules to concrete tests for flash:

- Test layers table (unit / property / integration / e2e / interaction
  / perf / snapshot) with the right tool for each.
- Property tests for the labeler invariants — chiefly the "assigned
  label never collides with any match's next-char" rule, which is
  *the* core flash correctness property.
- Cross-feature interaction matrix (rule #8) covering 17 concurrent-
  feature pairs: multi-cursor, vi_mode, LSP, splits, folds, soft-
  wrapped lines, CRLF, theme switch mid-flash, terminal resize, modal
  popups, concurrent plugin overlays, etc.
- A "reproduce before claiming" row per proposed API addition (#1#15)
  with the failing-without-fix test shape for each.
- Performance assertions guarding rule #2 (viewport-bounded scans).
- Lists test-infra additions needed: a flash-test plugin fixture, a
  `wait_until_screen_matches` convenience, and an
  `assert_no_orphan_overlays_in_namespace` cleanup-invariant check.
sinelaw pushed a commit that referenced this pull request Apr 26, 2026
Existing flash e2e tests peeked at editor model state — `editor_mode()`,
`cursor_position()`, `command_registry()` — to wait/assert.  CONTRIBUTING
is explicit: "asserts only on rendered output."  vi_mode tests follow
the same anti-pattern, but new code should be cleaner.

Why it matters here: the model-state accessors and the rendered
screen aren't always in lockstep under load.  `wait_until` ticks
every 50ms and may see `editor_mode == "flash"` set in the snapshot
before the corresponding `setStatus("Flash[]")` command has been
processed by the render side.  The CI hangs (6 flash tests TIMEOUT
at 180s under high parallelism) suggest the existing tests were
sensitive to that timing — passes locally where contention is lower.

Changes:
- arm_flash: drops the command_registry.get_all() peek and the
  `editor_mode()` peek.  Single readiness signal: the visible
  `Flash[]` status banner.  This is the same signal the plugin's
  setStatus call sets *inside* the main loop, so seeing it on
  screen proves the editor has processed setEditorMode AND
  beginKeyCapture AND the first iteration's setStatus — guarantees
  the next getNextKey is armed.
- All `wait_until(editor_mode != "flash")` waits → screen-only
  `wait_until(!screen.contains("Flash["))`.
- All `cursor_position()` assertions → marker-glyph assertions:
  send a unique character (`@`) after the jump and check the
  rendered buffer for `@hello there` etc.

Final flash tests now use exclusively screen-observable predicates.

All 6 flash + 46 vi_mode + 41 markdown_compose tests still pass
locally.  Hoping this also unblocks CI by removing the model/render
race window.
sinelaw pushed a commit that referenced this pull request Apr 26, 2026
Bug #2 from DEVCONTAINER_USABILITY_TEST_2026-04-26.md (L172): a
syntactically broken `devcontainer.json` failed silently —
`findConfig` returned false, no `Dev Container:` commands
registered, no status appeared. The user lost the entire feature
with zero feedback about what was wrong.

Fix in `plugins/devcontainer.ts`:
* Extract a `tryParse(path, content)` helper that catches the
  `parseJsonc` failure and stashes `{ path, message }` into a
  module-level `lastParseError`.
* Add `showParseErrorIfAny()` which emits the captured error
  via `editor.setStatus(t("status.parse_failed", ...))`.
* Call `showParseErrorIfAny()` in the `findConfig() === false`
  branch of plugin init, so a parse failure now surfaces a
  visible status line ("devcontainer.json parse error in
  <path>: <message>") instead of silently disappearing.

Test: `broken_devcontainer_json_surfaces_parse_error_in_status_bar`.
Boots a workspace with an unclosed-brace config and asserts the
parse-error string lands in the rendered screen — fails on master
(no message), passes with the fix.
sinelaw pushed a commit that referenced this pull request Apr 26, 2026
Bug #3 from DEVCONTAINER_USABILITY_TEST_2026-04-26.md (L170,
Critical): when the editor restarts after a Rebuild against a
malformed `devcontainer.json` (or any path that re-runs plugin
init with broken JSON), the plugin's old `if (findConfig()) {
registerCommands(); ... }` would skip the entire `else` branch.
The user lost every `Dev Container:` command, including
`Open Config` — the only in-editor route back to fix the file.
Recovery required killing and restarting the editor.

Fix in `plugins/devcontainer.ts`:
* `tryParse` now sets `configPath` even on parse failure so the
  recovery `Open Config` knows where the broken file lives.
* New `registerRecoveryCommands()` registers a tiny set
  (`open_config`, `show_build_logs`) and unregisters the
  full-config commands. Used in the broken-config branch of
  plugin init.
* The `findConfig() === false` branch now calls both
  `showParseErrorIfAny()` (Bug #2) and `registerRecoveryCommands()`,
  so the user sees the parse error AND has a navigable command.

Test: `broken_devcontainer_json_keeps_recovery_commands_registered`.
Asserts `%cmd.open_config` + `%cmd.show_build_logs` survive a
plugin init against unparseable JSON, while `%cmd.rebuild` /
`%cmd.attach` (which would no-op without a parsed config) are
correctly absent.

Bug #2's regression test (`broken_devcontainer_json_surfaces_parse_
error_in_status_bar`) still passes — the status emit and the
recovery registration coexist.
sinelaw pushed a commit that referenced this pull request Apr 26, 2026
Bug #2 from DEVCONTAINER_USABILITY_TEST_2026-04-26.md (L172): a
syntactically broken `devcontainer.json` failed silently —
`findConfig` returned false, no `Dev Container:` commands
registered, no status appeared. The user lost the entire feature
with zero feedback about what was wrong.

Fix in `plugins/devcontainer.ts`:
* Extract a `tryParse(path, content)` helper that catches the
  `parseJsonc` failure and stashes `{ path, message }` into a
  module-level `lastParseError`.
* Add `showParseErrorIfAny()` which emits the captured error
  via `editor.setStatus(t("status.parse_failed", ...))`.
* Call `showParseErrorIfAny()` in the `findConfig() === false`
  branch of plugin init, so a parse failure now surfaces a
  visible status line ("devcontainer.json parse error in
  <path>: <message>") instead of silently disappearing.

Test: `broken_devcontainer_json_surfaces_parse_error_in_status_bar`.
Boots a workspace with an unclosed-brace config and asserts the
parse-error string lands in the rendered screen — fails on master
(no message), passes with the fix.
sinelaw pushed a commit that referenced this pull request Apr 26, 2026
Bug #3 from DEVCONTAINER_USABILITY_TEST_2026-04-26.md (L170,
Critical): when the editor restarts after a Rebuild against a
malformed `devcontainer.json` (or any path that re-runs plugin
init with broken JSON), the plugin's old `if (findConfig()) {
registerCommands(); ... }` would skip the entire `else` branch.
The user lost every `Dev Container:` command, including
`Open Config` — the only in-editor route back to fix the file.
Recovery required killing and restarting the editor.

Fix in `plugins/devcontainer.ts`:
* `tryParse` now sets `configPath` even on parse failure so the
  recovery `Open Config` knows where the broken file lives.
* New `registerRecoveryCommands()` registers a tiny set
  (`open_config`, `show_build_logs`) and unregisters the
  full-config commands. Used in the broken-config branch of
  plugin init.
* The `findConfig() === false` branch now calls both
  `showParseErrorIfAny()` (Bug #2) and `registerRecoveryCommands()`,
  so the user sees the parse error AND has a navigable command.

Test: `broken_devcontainer_json_keeps_recovery_commands_registered`.
Asserts `%cmd.open_config` + `%cmd.show_build_logs` survive a
plugin init against unparseable JSON, while `%cmd.rebuild` /
`%cmd.attach` (which would no-op without a parsed config) are
correctly absent.

Bug #2's regression test (`broken_devcontainer_json_surfaces_parse_
error_in_status_bar`) still passes — the status emit and the
recovery registration coexist.
sinelaw pushed a commit that referenced this pull request Apr 27, 2026
Bug #2 from DEVCONTAINER_USABILITY_TEST_2026-04-26.md (L172): a
syntactically broken `devcontainer.json` failed silently —
`findConfig` returned false, no `Dev Container:` commands
registered, no status appeared. The user lost the entire feature
with zero feedback about what was wrong.

Fix in `plugins/devcontainer.ts`:
* Extract a `tryParse(path, content)` helper that catches the
  `parseJsonc` failure and stashes `{ path, message }` into a
  module-level `lastParseError`.
* Add `showParseErrorIfAny()` which emits the captured error
  via `editor.setStatus(t("status.parse_failed", ...))`.
* Call `showParseErrorIfAny()` in the `findConfig() === false`
  branch of plugin init, so a parse failure now surfaces a
  visible status line ("devcontainer.json parse error in
  <path>: <message>") instead of silently disappearing.

Test: `broken_devcontainer_json_surfaces_parse_error_in_status_bar`.
Boots a workspace with an unclosed-brace config and asserts the
parse-error string lands in the rendered screen — fails on master
(no message), passes with the fix.
sinelaw pushed a commit that referenced this pull request Apr 27, 2026
Bug #3 from DEVCONTAINER_USABILITY_TEST_2026-04-26.md (L170,
Critical): when the editor restarts after a Rebuild against a
malformed `devcontainer.json` (or any path that re-runs plugin
init with broken JSON), the plugin's old `if (findConfig()) {
registerCommands(); ... }` would skip the entire `else` branch.
The user lost every `Dev Container:` command, including
`Open Config` — the only in-editor route back to fix the file.
Recovery required killing and restarting the editor.

Fix in `plugins/devcontainer.ts`:
* `tryParse` now sets `configPath` even on parse failure so the
  recovery `Open Config` knows where the broken file lives.
* New `registerRecoveryCommands()` registers a tiny set
  (`open_config`, `show_build_logs`) and unregisters the
  full-config commands. Used in the broken-config branch of
  plugin init.
* The `findConfig() === false` branch now calls both
  `showParseErrorIfAny()` (Bug #2) and `registerRecoveryCommands()`,
  so the user sees the parse error AND has a navigable command.

Test: `broken_devcontainer_json_keeps_recovery_commands_registered`.
Asserts `%cmd.open_config` + `%cmd.show_build_logs` survive a
plugin init against unparseable JSON, while `%cmd.rebuild` /
`%cmd.attach` (which would no-op without a parsed config) are
correctly absent.

Bug #2's regression test (`broken_devcontainer_json_surfaces_parse_
error_in_status_bar`) still passes — the status emit and the
recovery registration coexist.
sinelaw pushed a commit that referenced this pull request Apr 30, 2026
Adds docs/internal/e2e-test-migration-design.md covering Phase 1 (minimal
production seam) and Phase 2 (PoC on test_to_uppercase) for migrating
imperative crossterm-driven E2E tests to declarative theorem tests.

Key decisions:
- Reuse the existing Action enum as the semantic alphabet; no parallel
  HeadlessEditorCore.
- Tests bind to a named test_api module on Editor (EditorTestApi trait),
  not arbitrary internals (active_state/active_cursors/active_viewport).
- Keep CONTRIBUTING.md rule #2 intact: theorem tests live under
  tests/semantic/, not tests/e2e/. The two suites coexist.
- Three-layer test target plan: BufferTheorem (state), LayoutTheorem
  (RenderSnapshot, Phase 3), StyleTheorem (StyledFrame, Phase 3+) — so
  rendering issues stay testable without coupling to ratatui buffer
  scraping or themes.
sinelaw pushed a commit that referenced this pull request May 26, 2026
Re-explored Fresh editor v0.3.8 in a new tmux session. Extended coverage of
core editing, search/replace, command palette, terminal, and multi-cursor
features. Zero false positives this run.

Critical tmux finding: Fresh uses DECCKM (application cursor mode). Arrow keys
MUST be sent as \033O[A-D] sequences, not plain Up/Down key names.

New confirmed bugs:
- #2112: Search/Replace panel returns "No matches found" for files outside git
  workspace root (e.g. /tmp). UI misleadingly shows the file path but silently
  finds nothing. In-project files work correctly.
- #2113: Command palette keystrokes typed in fuzzy file mode can leak into the
  editor buffer — race condition during >command → file mode transition.

Updated: confirmed_bugs.md, github_issues.md, run_log.md, test_plan.md,
learning_db.md (DECCKM keys, Alt+W close-tab, tmux automation notes).
sinelaw added a commit that referenced this pull request May 27, 2026
…ize backlog

The per-run playbook was being silently lost: a later run overwrote
learning_db.md wholesale, dropping the ISSUE FILING STANDARDS, FALSE
POSITIVE PATTERNS, and Lessons 29-50 that Runs #2-11 referenced. Runs
also drifted into re-verifying already-passing Sprints 1-9 (Run #12)
instead of advancing the untested backlog.

Fixes:
- New AGENT_INSTRUCTIONS.md: the one durable, edit-rarely playbook.
  Folds in the hourly black-box mission + strict prohibitions (no
  source-code analysis, no fixes/PRs) from the external scheduler prompt.
  Adds STEP 0 preflight (sync, playbook-integrity check, lessons
  continuity, auth check, fixed-bug recheck) and ANTI-DRIFT rules R1-R4.
  Restores the lost ISSUE FILING STANDARDS, PRE-TESTING CHECKLIST, and
  FALSE POSITIVE PATTERNS (from 855bc57).
- test_plan.md: RUN #13+ priority order putting the deferred
  edge-case/stress/navigation backlog ahead of passing sprints; pointer
  to the playbook and R1/R2.
sinelaw pushed a commit that referenced this pull request Jun 8, 2026
…warning

#7: LineAlignment::from_hunks paired old/new lines positionally within a
hunk, so a pure insertion (e.g. comment lines above an unchanged block)
mis-aligned — the unchanged block read as a delete on the left and a
re-add on the right. Thread git's per-line ops (' '/'-'/'+') through
CompositeHunk -> DiffHunk and build the alignment from them: context lines
stay paired, deletions are old-only, insertions are new-only. Unchanged
code now lines up identically on both sides. Falls back to the positional
pairing when ops are absent.

#2: composite source buffers appended a trailing newline to the last line,
adding a phantom empty line with no ViewLine — 'ViewLine missing … line=N'
spam when scrolled to the bottom. Build entries without the trailing
newline so the buffer's line count matches the real lines.

Regenerated fresh.d.ts for the new TsCompositeHunk.ops field.

https://claude.ai/code/session_01D1vLAnwKHqmebN9qRQ9E47
sinelaw pushed a commit that referenced this pull request Jun 8, 2026
…shift-wheel h-scroll

- #2: opening a comment from the panel in side-by-side now rebuilds the
  composite focused on the comment's hunk (parameterized buildCenterComposite
  with a focus-hunk index) instead of staying at the first hunk — the unified
  cursor jump is inert when the composite is showing.
- #3: comments rail is narrow by default (~15% width; diff/comments split
  0.74 -> 0.82).
- #4: Shift+mouse-wheel over the side-by-side area now pans horizontally —
  handle_horizontal_scroll scrolls the composite's per-pane left_column
  (it only touched the split viewport, which the composite render ignores).
- #1: pin the FILES cursor to the selected row's start on every focus path
  (incl. mouse via buffer_activated), so it can't land horizontally scrolled.

https://claude.ai/code/session_01D1vLAnwKHqmebN9qRQ9E47
sinelaw pushed a commit that referenced this pull request Jun 8, 2026
…h-scroll (#4)

- test_review_comments_rail_is_narrow: the COMMENTS header starts past
  column 130 of a 160-col screen (rail ~15%).
- test_review_side_by_side_shift_wheel_scrolls_horizontally: Shift+wheel on
  the composite pans it (rendered content changes from the left-edge state).

(#2 comment-jump and #1 file-focus h-scroll are verified manually in tmux;
the harness can't drive the async comment-jump rebuild / the residual #1
edge reliably.)

https://claude.ai/code/session_01D1vLAnwKHqmebN9qRQ9E47
sinelaw pushed a commit that referenced this pull request Jun 8, 2026
…warning

#7: LineAlignment::from_hunks paired old/new lines positionally within a
hunk, so a pure insertion (e.g. comment lines above an unchanged block)
mis-aligned — the unchanged block read as a delete on the left and a
re-add on the right. Thread git's per-line ops (' '/'-'/'+') through
CompositeHunk -> DiffHunk and build the alignment from them: context lines
stay paired, deletions are old-only, insertions are new-only. Unchanged
code now lines up identically on both sides. Falls back to the positional
pairing when ops are absent.

#2: composite source buffers appended a trailing newline to the last line,
adding a phantom empty line with no ViewLine — 'ViewLine missing … line=N'
spam when scrolled to the bottom. Build entries without the trailing
newline so the buffer's line count matches the real lines.

Regenerated fresh.d.ts for the new TsCompositeHunk.ops field.

https://claude.ai/code/session_01D1vLAnwKHqmebN9qRQ9E47
sinelaw pushed a commit that referenced this pull request Jun 8, 2026
…shift-wheel h-scroll

- #2: opening a comment from the panel in side-by-side now rebuilds the
  composite focused on the comment's hunk (parameterized buildCenterComposite
  with a focus-hunk index) instead of staying at the first hunk — the unified
  cursor jump is inert when the composite is showing.
- #3: comments rail is narrow by default (~15% width; diff/comments split
  0.74 -> 0.82).
- #4: Shift+mouse-wheel over the side-by-side area now pans horizontally —
  handle_horizontal_scroll scrolls the composite's per-pane left_column
  (it only touched the split viewport, which the composite render ignores).
- #1: pin the FILES cursor to the selected row's start on every focus path
  (incl. mouse via buffer_activated), so it can't land horizontally scrolled.

https://claude.ai/code/session_01D1vLAnwKHqmebN9qRQ9E47
sinelaw pushed a commit that referenced this pull request Jun 8, 2026
…h-scroll (#4)

- test_review_comments_rail_is_narrow: the COMMENTS header starts past
  column 130 of a 160-col screen (rail ~15%).
- test_review_side_by_side_shift_wheel_scrolls_horizontally: Shift+wheel on
  the composite pans it (rendered content changes from the left-edge state).

(#2 comment-jump and #1 file-focus h-scroll are verified manually in tmux;
the harness can't drive the async comment-jump rebuild / the residual #1
edge reliably.)

https://claude.ai/code/session_01D1vLAnwKHqmebN9qRQ9E47
sinelaw pushed a commit that referenced this pull request Jun 24, 2026
Replaces the per-frame reconnect poller with an event-driven path and
unifies the two reconnect mechanisms, and makes a plain `fresh ssh://…`
launch a first-class remote session. Three connected changes:

#1 Uniform backend spec. `Authority::session_spec()` derives the
   persistable `SessionAuthoritySpec` from the live authority's
   `command_wrap` (SSH/Kube → `RemoteAgent`, local/container → `Local`),
   and `editor_init` uses it to fill a window's `authority_spec` when the
   resolved one is `Local`. A CLI `ssh://` launch previously left the
   spec `Local`, which made workspace persistence and the manual-reconnect
   rebuild (`start_remote_reconnect`, which matches on the spec) silently
   inert for it. Never downgrades an already-remote spec.

#2 Event-driven reconnect. `AgentChannel` gets a stable `id` and a
   `reconnect_notify` fired on each `replace_transport` hot-swap. New
   `FileSystem::remote_channel_id` / `remote_reconnect_notify` expose
   them; a lazily-spawned per-window forwarder turns a hot-swap into
   `AsyncMessage::RemoteReconnected { connection_id }`. This replaces the
   per-frame `is_remote_connected()` poll — it wakes the loop
   immediately, carries identity, and can't miss a fast flap.

#3 One reconnect handler. `handle_remote_reconnected` maps the connection
   id back to its window and calls the new `reattach_window`, which clears
   the disconnect indicator and respawns the embedded terminals that died
   with the old carrier. The app-level `RemoteAttachMode::Reconnect`
   rebuild path now routes through the same `reattach_window`, so both the
   silent hot-swap and the manual/dormant rebuild converge on one
   idempotent path (respawn skips live terminals). Drops the old
   `remote_was_connected` field and the double-respawn guard.

The e2e test now drives the reconnect dispatch (id→window→reattach),
covering revive, wrong-id no-op, and duplicate-event idempotency.

Note: true remote-terminal *session* survival across a reconnect (a
server-side multiplexer so the shell and its processes outlive the
carrier, rather than respawning a fresh shell) is left to a separate
effort.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHCFsZ3ReFxbqBgjwwBPdG
sinelaw pushed a commit that referenced this pull request Jun 24, 2026
Replaces the per-frame reconnect poller with an event-driven path and
unifies the two reconnect mechanisms, and makes a plain `fresh ssh://…`
launch a first-class remote session. Three connected changes:

#1 Uniform backend spec. `Authority::session_spec()` derives the
   persistable `SessionAuthoritySpec` from the live authority's
   `command_wrap` (SSH/Kube → `RemoteAgent`, local/container → `Local`),
   and `editor_init` uses it to fill a window's `authority_spec` when the
   resolved one is `Local`. A CLI `ssh://` launch previously left the
   spec `Local`, which made workspace persistence and the manual-reconnect
   rebuild (`start_remote_reconnect`, which matches on the spec) silently
   inert for it. Never downgrades an already-remote spec.

#2 Event-driven reconnect. `AgentChannel` gets a stable `id` and a
   `reconnect_notify` fired on each `replace_transport` hot-swap. New
   `FileSystem::remote_channel_id` / `remote_reconnect_notify` expose
   them; a lazily-spawned per-window forwarder turns a hot-swap into
   `AsyncMessage::RemoteReconnected { connection_id }`. This replaces the
   per-frame `is_remote_connected()` poll — it wakes the loop
   immediately, carries identity, and can't miss a fast flap.

#3 One reconnect handler. `handle_remote_reconnected` maps the connection
   id back to its window and calls the new `reattach_window`, which clears
   the disconnect indicator and respawns the embedded terminals that died
   with the old carrier. The app-level `RemoteAttachMode::Reconnect`
   rebuild path now routes through the same `reattach_window`, so both the
   silent hot-swap and the manual/dormant rebuild converge on one
   idempotent path (respawn skips live terminals). Drops the old
   `remote_was_connected` field and the double-respawn guard.

The e2e test now drives the reconnect dispatch (id→window→reattach),
covering revive, wrong-id no-op, and duplicate-event idempotency.

Note: true remote-terminal *session* survival across a reconnect (a
server-side multiplexer so the shell and its processes outlive the
carrier, rather than respawning a fresh shell) is left to a separate
effort.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHCFsZ3ReFxbqBgjwwBPdG
sinelaw pushed a commit that referenced this pull request Jul 20, 2026
…ead of removing them

CI's Playwright suite caught that removing the mutating POST routes broke the
web-UI E2E tests: the harness drives the editor through `POST /action` (15+
call sites), `/widget`, and `/reset` — the latter has no WebSocket equivalent.
My earlier "nothing uses them" investigation was wrong (it searched for `fetch`
/ uppercase `POST` and missed Playwright's `page.request.post`).

Restore the routes and instead close the CSRF/finding-#3 hole the intended way:
apply the SAME same-origin/Host check as the `/ws` upgrade to every
state-mutating POST (in `serve_request`). A cross-origin browser page sends its
own `Origin` and is rejected; non-browser callers (curl, the Playwright request
API, the parity harness) send no `Origin` and pass, so the routes stay
scriptable. This keeps the Content-Length crash fix (#1), the write-timeout
(#2), the rebinding guard (#4), and the rest intact.

Verified: the full Playwright suite passes locally (149/0), including the
trust-dialog and live-grep-toolbar sections that failed in CI; webui unit tests
and scene_parity pass. Docs updated to describe the gating (not removal).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsqkqerfYQ4A48asmUxDQG
sinelaw pushed a commit that referenced this pull request Jul 20, 2026
…ead of removing them

CI's Playwright suite caught that removing the mutating POST routes broke the
web-UI E2E tests: the harness drives the editor through `POST /action` (15+
call sites), `/widget`, and `/reset` — the latter has no WebSocket equivalent.
My earlier "nothing uses them" investigation was wrong (it searched for `fetch`
/ uppercase `POST` and missed Playwright's `page.request.post`).

Restore the routes and instead close the CSRF/finding-#3 hole the intended way:
apply the SAME same-origin/Host check as the `/ws` upgrade to every
state-mutating POST (in `serve_request`). A cross-origin browser page sends its
own `Origin` and is rejected; non-browser callers (curl, the Playwright request
API, the parity harness) send no `Origin` and pass, so the routes stay
scriptable. This keeps the Content-Length crash fix (#1), the write-timeout
(#2), the rebinding guard (#4), and the rest intact.

Verified: the full Playwright suite passes locally (149/0), including the
trust-dialog and live-grep-toolbar sections that failed in CI; webui unit tests
and scene_parity pass. Docs updated to describe the gating (not removal).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsqkqerfYQ4A48asmUxDQG
sinelaw pushed a commit that referenced this pull request Jul 23, 2026
…ional name cap

Follow-up fixes on the per-split tab-bar work (PR #2768):

1. Missing right `>` scroll indicator in a split pane. The split-control
   (maximize / close) buttons are painted over the right edge of the tab
   row, but the tab bar laid tabs across the *full* pane width, so the `>`
   right-overflow indicator was drawn at the same column as the maximize
   button and got overwritten — only `<` ever showed. Reserve the control
   buttons' columns (`split_control_reserve`) from both the render area and
   the scroll math (`split_tabs_width`) so `<`, `>` and the pinned `+` stay
   left of the buttons.

2. Split-header buttons mis-spaced. Maximize and close were drawn with a
   1-column gap (`□ ×`). Group them adjacent (`□×`) as one control cluster;
   the reserve keeps a gap between them and the tab content.

3. Tab-name cap is now conditional. The 25-column elision cap applies only
   when a split's tabs overflow the bar; when there is room to show every
   tab's full name without overflow, names render untruncated. Threaded a
   per-split `available_width` into `calculate_tab_widths` /
   `build_tab_spans` (kept in lockstep) and derived a shared cap.

Both #1 and #2 were pre-existing structural issues (the button overlay and
the show_right/build_visible_line logic predate the PR); the PR's correct
per-split scrolling surfaced the `>` collision reliably. Added unit tests
for the reserve, the conditional cap, and `>`/`<` indicator rendering, and
updated the migrated tab-scrolling and split-width tests for the new
semantics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P64ZBvEmKCHGdueUT2D5vt
sinelaw pushed a commit that referenced this pull request Jul 24, 2026
…ional name cap

Follow-up fixes on the per-split tab-bar work (PR #2768):

1. Missing right `>` scroll indicator in a split pane. The split-control
   (maximize / close) buttons are painted over the right edge of the tab
   row, but the tab bar laid tabs across the *full* pane width, so the `>`
   right-overflow indicator was drawn at the same column as the maximize
   button and got overwritten — only `<` ever showed. Reserve the control
   buttons' columns (`split_control_reserve`) from both the render area and
   the scroll math (`split_tabs_width`) so `<`, `>` and the pinned `+` stay
   left of the buttons.

2. Split-header buttons mis-spaced. Maximize and close were drawn with a
   1-column gap (`□ ×`). Group them adjacent (`□×`) as one control cluster;
   the reserve keeps a gap between them and the tab content.

3. Tab-name cap is now conditional. The 25-column elision cap applies only
   when a split's tabs overflow the bar; when there is room to show every
   tab's full name without overflow, names render untruncated. Threaded a
   per-split `available_width` into `calculate_tab_widths` /
   `build_tab_spans` (kept in lockstep) and derived a shared cap.

Both #1 and #2 were pre-existing structural issues (the button overlay and
the show_right/build_visible_line logic predate the PR); the PR's correct
per-split scrolling surfaced the `>` collision reliably. Added unit tests
for the reserve, the conditional cap, and `>`/`<` indicator rendering, and
updated the migrated tab-scrolling and split-width tests for the new
semantics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P64ZBvEmKCHGdueUT2D5vt
sinelaw pushed a commit that referenced this pull request Jul 24, 2026
…ional name cap

Follow-up fixes on the per-split tab-bar work (PR #2768):

1. Missing right `>` scroll indicator in a split pane. The split-control
   (maximize / close) buttons are painted over the right edge of the tab
   row, but the tab bar laid tabs across the *full* pane width, so the `>`
   right-overflow indicator was drawn at the same column as the maximize
   button and got overwritten — only `<` ever showed. Reserve the control
   buttons' columns (`split_control_reserve`) from both the render area and
   the scroll math (`split_tabs_width`) so `<`, `>` and the pinned `+` stay
   left of the buttons.

2. Split-header buttons mis-spaced. Maximize and close were drawn with a
   1-column gap (`□ ×`). Group them adjacent (`□×`) as one control cluster;
   the reserve keeps a gap between them and the tab content.

3. Tab-name cap is now conditional. The 25-column elision cap applies only
   when a split's tabs overflow the bar; when there is room to show every
   tab's full name without overflow, names render untruncated. Threaded a
   per-split `available_width` into `calculate_tab_widths` /
   `build_tab_spans` (kept in lockstep) and derived a shared cap.

Both #1 and #2 were pre-existing structural issues (the button overlay and
the show_right/build_visible_line logic predate the PR); the PR's correct
per-split scrolling surfaced the `>` collision reliably. Added unit tests
for the reserve, the conditional cap, and `>`/`<` indicator rendering, and
updated the migrated tab-scrolling and split-width tests for the new
semantics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P64ZBvEmKCHGdueUT2D5vt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants