Commit Graph

7 Commits

Author SHA1 Message Date
Levi Neuwirth 7479213c3f fix(highlight): env -S payload is a full arg list, not a bare interpreter
The attached split-string payload (`-Spython3`, `--split-string=...`) can
itself begin with env options or VAR=value assignments before the
interpreter: `-S-i python3`, `-SFOO=bar python3`,
`--split-string=-u FOO python3`. Rather than taking the payload's first
word as the interpreter, re-inject the attached payload into the token
stream so it flows through the same option / operand / assignment state
machine as a separated payload. Adds the three cases as resolver tests.
2026-07-14 16:00:47 +01:00
Levi Neuwirth 558d00020f fix(highlight): PR #116 round 2 — pin grammar across switch, attached env -S
Two follow-ups from review, both in builtin/runtime/syntax.lua.

1. [P2] Buffer switching bypassed the pinned grammar. after-edit already
   reparsed the pinned language, but the after-switch reattach path
   (attach_for_active_buffer) re-resolved from scratch — so open an
   extensionless `#!/bin/sh` (bash), edit its shebang to lua, switch away
   and back, and the grammar flipped to lua while the LSP side kept its
   bash attachment (lsp.lua's after-switch reuses the existing record).
   attach_for_active_buffer now reuses the language pinned at first attach
   whenever a parse view already exists; only a first-seen buffer
   resolves. A language change still needs a close/reopen, matching both
   the after-edit behavior and how extensions work.

2. [P2] Attached `env -S`/`--split-string` forms failed. The walk skipped
   the whole option token, but for split-string the interpreter rides
   inside it: `-Spython3`, `-vSpython3` (after no-operand short flags
   i/v/0), and `--split-string=python3` all resolved to nil (the last was
   also eaten by the earlier `=` branch). The env walk now extracts the
   interpreter from the attached value (`^-[iv0]*S(.+)$` /
   `^--split-string=(.+)$`); the separated forms (`-S python3`) still work
   by walking on to the next token.

Tests (bite-verified against the round-1 syntax.lua — both fail there;
scripts/bite HEAD builtin/runtime/syntax.lua):
- m4_shebang_edit_keeps_pinned_grammar now adds a switch-away/back cycle
  (via pmacs.window.switch_buffer, which fires after-switch
  synchronously) and asserts the tree stays bash.
- m4_shebang_resolver_maps_interpreters adds the attached split-string
  cases (`-Spython3`, `--split-string=python3`, `-vSpython3`).

Gates: fmt; clippy -D warnings; m4_acceptance --skip basedpyright; GPU;
git diff --check green. Only-known-flake caveat as round 1
(editor::composition_overhead_under_ten_percent perf microbenchmark,
unrelated to this Lua change). Change is Lua-only plus the acceptance
tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJ9FQ832QwftJXCD9LeFan
2026-07-14 15:20:22 +01:00
Levi Neuwirth f300b77533 fix(highlight): PR #116 round 1 — shebang precedence, pinned grammar, env operands
Three review findings, all in builtin/runtime/syntax.lua.

1. [P1] Syntax bypassed extension precedence and grammar availability.
   attach_for_active_buffer resolved `language_for_path or shebang`, but
   language_for_path knows only grammar-backed extensions — so a `.py`
   file opening with `#!/bin/sh` fell through to the shebang and got a
   bash parse tree, and an extensionless `#!/usr/bin/env python3` script
   dispatched "python" (no grammar) and raised "unknown language". A new
   resolve_active_language walks the full precedence chain — grammar
   extension -> LSP filetype map -> shebang — consulting the shebang only
   when the extension is unrecognized (a recognized non-grammar extension
   like .py is authoritative). Dispatch is then gated on
   pmacs.parse._has_language(lang), so grammarless languages are skipped
   silently. The extension parts stay keyed on buf:name() (unchanged from
   before), so path-less buffers that resolve a grammar by name — e.g.
   generated markdown buffers — are unaffected.

2. [P2] Editing an open script's shebang left parsing/highlighting stale.
   The after-edit path re-sniffed the mutable shebang: sh -> python
   raised "unknown language" while leaving the old bash tree, and
   sh -> lua swapped the parse tree under a highlight overlay still
   holding the original grammar's query. Reparse now uses the language
   pinned at first attach (parse_lang_by_buffer), never re-resolving —
   a language change needs a close/reopen, as it does for extensions.

3. [P2] `env` options with operands were mistaken for interpreters.
   `#!/usr/bin/env -u FOO python3` skipped `-u` but took `FOO`. The env
   walk now skips the operand of the operand-consuming GNU-env options
   (-u/--unset, -C/--chdir, -a/--argv0) before selecting the interpreter.
   -S/--split-string stays excluded (its string carries the interpreter).

Tests (bite-verified against pre-fix syntax.lua — each fails without its
fix; scripts/bite HEAD builtin/runtime/syntax.lua):
- m4_shebang_does_not_override_extension now also asserts _has_view is
  false (no bash grammar tree for a `.py` + `#!/bin/sh`), not only the
  LSP language.
- m4_shebang_extensionless_grammarless_language_is_silent — extensionless
  python resolves for LSP, gets no grammar view, and records no error.
- m4_shebang_edit_keeps_pinned_grammar — rewriting a `#!/bin/sh` script's
  shebang to lua keeps the bash tree and reports no error.
- m4_shebang_resolver_maps_interpreters — added the env-operand cases
  (`-u FOO`, `-C /tmp`, combined).

Gates: fmt; clippy -D warnings; m4_acceptance --skip basedpyright; GPU;
git diff --check all green. The only sweep failure is the pre-existing
editor::composition_overhead_under_ten_percent render microbenchmark
(ratio hovers at the 1.10 cutoff; flakes ~1/3 even isolated single-
threaded, already asserted-off on macOS) — a pure-Rust render loop this
Lua-only change cannot touch. Change is Lua-only plus the acceptance
tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJ9FQ832QwftJXCD9LeFan
2026-07-14 15:02:59 +01:00
Levi Neuwirth 4a60e4c858 feat(highlight): shebang-based language detection for extensionless scripts
Extension detection missed extensionless scripts — `scripts/deploy`, git
hooks, `configure`, and `scripts/bite` itself — so they got neither
highlighting nor an LSP server. Add a first-line shebang fallback.

- New `pmacs.parse.language_from_shebang(buf)` (builtin/runtime/syntax.lua):
  sniffs the first line (capped at 256 bytes), maps the interpreter's
  basename to a language, and resolves the `#!/usr/bin/env python3`
  indirection (skipping env's own `-S`/flags and `VAR=val` assignments).
  Backed by `pmacs.parse.shebangs`, a user-extensible map seeded with the
  interpreters pmacs can act on: sh-family -> bash, python* -> python,
  node -> javascript, lua* -> lua.
- Wired as a strict *fallback* on both resolution paths: syntax.lua's
  grammar attach (`language_for_path or language_from_shebang`) and
  lsp.lua's `buffer_language` (grammar -> filetypes -> shebang). A
  recognized extension always wins, so a `.py`/`.sh` file is never
  re-classified by a stray shebang.
- Cross-language, not shell-only: `#!/usr/bin/env python` /`node` /`lua`
  resolve too. Special filenames (`.bashrc`, `Dockerfile`, `Makefile`)
  are intentionally deferred until there are grammars behind them.

Bite-verified acceptance (tests/m4_acceptance.rs):
- m4_shebang_resolver_maps_interpreters — the mapping incl. env
  indirection and `env -S`; non-shebangs and unmapped interpreters
  (ruby) resolve to nil.
- m4_shebang_extensionless_script_resolves_bash — opening an
  extensionless `#!/bin/sh` script resolves to bash on BOTH paths:
  lsp.lua's `active_buffer_language()` and a settled bash parse tree
  (grammar attach). Reachable only via the shebang, since the file has
  no extension.
- m4_shebang_does_not_override_extension — a `.py` file opening with
  `#!/bin/sh` still resolves to python (extension precedence).

Gates green: fmt; clippy -D warnings; test --lib; --features crdt;
m4_acceptance --skip basedpyright; GPU; full workspace sweep;
git diff --check. Change is Lua-only plus the acceptance tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJ9FQ832QwftJXCD9LeFan
2026-07-14 14:33:06 +01:00
Levi Neuwirth 5c5caa9482 fix(panels): follow active buffer on semantic frontends; re-attach overlays on switch
Two PR #94 validation findings.

1. (High, blocking) GPU stuck after leaving a panel: the GPU only
   swaps its displayed buffer on BufferSnapshot, and the daemon only
   sent one on the first CRDT upgrade (F29's ensure returns None for
   an already-backed buffer). A panel's q / RET switched the daemon's
   active buffer back to the already-known source and sent nothing --
   the GPU kept rendering the panel while input targeted the source: a
   typing-into-a-buffer-you-can't-see hazard. Fix: the per-tick loop
   now FOLLOWS each replica frontend's own active buffer -- when it
   differs from the last snapshot sent to that frontend, ship that
   buffer's snapshot to that frontend only (the F29 broadcast records
   itself so the upgrade tick doesn't double-send). First-tick send
   also repairs the attach-time last-snapshot-wins ambiguity. Snapshot
   export extracted and shared with the F29 broadcast; per-fid state
   cleaned on both detach paths.

2. (High, wider than reported) 'LSP doesn't activate on navigate':
   switch_active_buffer clears the window's overlays, and the runtime
   dedup tables (highlighted_buffers, styled_buffers,
   diag_viewed_buffers) blocked re-attachment -- so EVERY buffer
   switch (plain C-x b included, long-latent) permanently stripped
   syntax color, LSP semantic style, and diagnostic underlines;
   verified: overlay kinds [syntax-highlight, lsp-style, diagnostic]
   -> [] after one away-and-back. Fix: a new additive
   buffer.after-switch hook, fired by the window.switch_buffer binding
   and find_or_open's existing-buffer branch; syntax.lua and lsp.lua
   subscribe and re-push their views (the just-cleared window makes
   that exactly-once per switch; fresh loads keep firing after-load).

Regression: tests/overlay_reattach_acceptance.rs (double round-trip
counts exactly one highlight overlay; panel q restores styling).
The daemon follow path is validated live (daemon + GPU) -- its unit
seam is the shared export helper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:46:50 -04:00
Levi Neuwirth 9718958c4c Fix stale TUI styling after edits 2026-05-26 11:15:23 -04:00
Levi Neuwirth 4da4b09d5d Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00