Skip to content

Commit e7164ec

Browse files
committed
Rewrite ghostel-compile: spawn directly, add opt-in global mode
The old ghostel-compile typed its command into an already-running interactive shell via `ghostel--flush-output'. That made every embedded newline in a multi-line shell paragraph look like a RET press, so scripts broke. It also tied completion detection to OSC 133 D markers, making shell-integration a hard requirement. Spawn the command directly. Each invocation runs `shell-file-name -c COMMAND' via `make-process' through a PTY owned by the ghostel renderer — no shell between the command and the user. Multi-line scripts pass through verbatim. The process sentinel reports the real exit status, so shell-integration is no longer required. The header ("Compilation started at ...") is written into the VT terminal before spawn so the user sees the banner live, not only after finalize. During the run the buffer stays plain `ghostel-mode' — keystrokes reach the process, which is what makes interactive programs like `htop', `less', or read prompts work. When the process exits the sentinel flushes output, trims trailing grid padding, appends the footer, parses errors over the run's own output region, and switches the buffer to `ghostel-compile-view-mode' (read-only, `g'/`n'/`p' bindings, standard error navigation). Add `ghostel-compile-global-mode', an opt-in global minor mode that advises `compilation-start' so every caller — `compile', `recompile', `project-compile', and any other command that goes through `compilation-start' — automatically runs in a ghostel buffer. Falls through to the stock implementation for `grep-mode' (output-parser conflict), `mode=t' (comint), and non-nil `continue' (append semantics this design can't honour). The excluded set is customisable via `ghostel-compile-global-mode-excluded-modes'. `ghostel-recompile' now re-runs into the CURRENT buffer whenever it holds a local `ghostel-compile--command' — so pressing `g' in a `*compilation*' buffer (produced by the advice) reuses that buffer and its window, matching `M-x recompile's behaviour. Previously it always targeted `*ghostel-compile*', which produced a second unfocused window side-by-side. `ghostel-compile--prepare-buffer' also resets the buffer in place (interrupts the old process, erases content, rebuilds the terminal) rather than killing and recreating it, so the existing window stays attached across recompiles. Other details worth noting: - No more hot shell in `--prepare-buffer'. The previous version routed through `ghostel--init-buffer' which spawns a shell and in TRAMP buffers does a full remote-integration round-trip (uploads temp files and plumbs env); we then killed it immediately, leaking the remote temp files. Creates the terminal directly now. - `ghostel--managed-buffer-name' is pinned to a sentinel so an OSC 2 title sequence from the compile command can't rename the buffer mid-run. - The scan marker anchors to the end of the rendered header — not `point-max' of the post-render grid — so `--trim-trailing-blanks' actually trims the VT grid's blank-row padding between output and the footer (this padding scales with window height). - Dropped customs/state that no longer apply: `ghostel-compile-mode' (and its keymap + `--owns-compilation-minor-mode' flag), `ghostel-compile-hide-prompts', `ghostel-compile-clear-buffer', `ghostel-compile--on-start' and `--on-finish' (OSC 133 hooks), the `--running' state machine, `--finalize-timer' gating, the `--header-marker' / `--footer-marker' buffer-locals and the `--clear-markers' helper, and a bash shell-integration exit-status test. Tests rewritten to match the new flow. End-to-end native test drives `ghostel-compile--start' with a multi-line paragraph and asserts output + exit status. Interactive-input test drives `cat' and asserts keystrokes round-trip. Global-mode tests cover advice add/remove, routing, and all three fall-through cases (grep-mode, mode=t, continue, plus custom-excluded mode).
1 parent 9846c64 commit e7164ec

3 files changed

Lines changed: 886 additions & 1003 deletions

File tree

README.md

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,13 @@ footer, error highlighting, and `next-error` navigation — but backed by
473473
a real TTY so programs that probe `isatty(3)` (coloured output, progress
474474
bars, curses tools) behave as they do in a normal shell.
475475

476+
Each invocation spawns a fresh process via
477+
`shell-file-name -c COMMAND` through a PTY owned by the ghostel
478+
renderer — no interactive shell sits between the command and the
479+
user, so multi-line shell scripts are passed through verbatim and
480+
no shell-integration setup is required. The process sentinel
481+
delivers the real exit status.
482+
476483
```elisp
477484
(require 'ghostel-compile)
478485
@@ -481,10 +488,11 @@ bars, curses tools) behave as they do in a normal shell.
481488

482489
Commands:
483490

484-
| Command | Description |
485-
|-------------------------|-----------------------------------------------------|
486-
| `M-x ghostel-compile` | Prompt for a command and run it (uses `compile-command`) |
487-
| `M-x ghostel-recompile` | Re-run the last command in its original directory |
491+
| Command | Description |
492+
|------------------------------|----------------------------------------------------------|
493+
| `M-x ghostel-compile` | Prompt for a command and run it (uses `compile-command`) |
494+
| `M-x ghostel-recompile` | Re-run the last command in its original directory |
495+
| `M-x ghostel-compile-global-mode` | Route *all* `compile`-style calls through ghostel (opt-in) |
488496

489497
What a run looks like — the buffer text matches `M-x compile`:
490498

@@ -499,16 +507,15 @@ make -j4 test
499507
Compilation finished at Wed Apr 15 08:30:19, duration 8.20 s
500508
```
501509

502-
When the command finishes, the live shell and ghostel renderer are torn
503-
down and the buffer's major mode is switched to `ghostel-compile-view-mode`
504-
(derived from `compilation-mode`). The buffer becomes a regular,
505-
read-only Emacs buffer with compile-mode's coloured error / line-number
506-
faces; the buffer never returns to an interactive ghostel terminal —
507-
a recompile discards it and starts fresh in the original directory.
508-
Point stays at the end of the output (where the renderer left it) so
509-
you see the latest output and the footer rather than jumping to the
510-
top. `mode-line-process` shows `:run` while the command is running
511-
and `:exit [N]` afterwards, using the same faces `M-x compile` uses.
510+
When the command finishes, the live process and ghostel renderer are
511+
torn down and the buffer's major mode is switched to
512+
`ghostel-compile-view-mode` (derived from `compilation-mode`). The
513+
buffer becomes a regular, read-only Emacs buffer with compile-mode's
514+
coloured error / line-number faces; the buffer never returns to an
515+
interactive ghostel terminal — a recompile discards it and starts
516+
fresh in the original directory. `mode-line-process` shows
517+
`:run` while the command is running and `:exit [N]` afterwards, using
518+
the same faces `M-x compile` uses.
512519

513520
Keybindings (in `ghostel-compile-view-mode`):
514521

@@ -538,23 +545,35 @@ These standard `compile` options are honoured:
538545
`compilation-scroll-output` non-nil).
539546

540547
`ghostel-recompile` runs in the directory the original `ghostel-compile`
541-
was invoked from, regardless of which buffer you're in when you press
542-
`g`.
548+
was invoked from, regardless of which buffer you're in when you press `g`.
549+
550+
#### Make `compile` / `recompile` / `project-compile` use ghostel
551+
552+
Enable `ghostel-compile-global-mode` to advise `compilation-start`
553+
so every caller that goes through it — `M-x compile`,
554+
`M-x recompile`, `M-x project-compile`, and any third-party command
555+
that uses `compilation-start` under the hood — runs in a ghostel
556+
buffer automatically.
557+
558+
```elisp
559+
(require 'ghostel-compile)
560+
(ghostel-compile-global-mode 1)
561+
```
562+
563+
`grep-mode` falls through to the stock `compilation-start`
564+
implementation by default, because its output parsing and
565+
window-management conventions don't fit a live TTY. Extend
566+
`ghostel-compile-global-mode-excluded-modes` to opt other modes out.
543567

544568
Ghostel-specific customisation:
545569

546-
| Option | Effect |
547-
|---------------------------------------|--------------------------------------------------------------------------------------------------------------------|
548-
| `ghostel-compile-buffer-name` | Buffer name (default `*ghostel-compile*`) |
549-
| `ghostel-compile-finished-major-mode` | Major mode to switch to after each run (default `ghostel-compile-view-mode`; set to nil to stay in `ghostel-mode`) |
550-
| `ghostel-compile-hide-prompts` | Hide surrounding shell prompts (default `t`) |
551-
| `ghostel-compile-clear-buffer` | Clear the buffer before each run (default `t`) |
552-
| `ghostel-compile-finish-functions` | Ghostel-specific finish hook (runs alongside `compilation-finish-functions`) |
553-
| `ghostel-compile-debug` | Log every OSC 133 C/D event to `*Messages*` (default `nil`) |
554-
555-
Completion is detected via the OSC 133 `D;<exit>` semantic prompt
556-
marker, so shell integration (`ghostel-shell-integration`, enabled by
557-
default) must be active.
570+
| Option | Effect |
571+
|----------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
572+
| `ghostel-compile-buffer-name` | Buffer name (default `*ghostel-compile*`) |
573+
| `ghostel-compile-finished-major-mode` | Major mode to switch to after each run (default `ghostel-compile-view-mode`; set to nil to stay in `ghostel-mode`) |
574+
| `ghostel-compile-finish-functions` | Ghostel-specific finish hook (runs alongside `compilation-finish-functions`) |
575+
| `ghostel-compile-global-mode-excluded-modes` | Modes for which the global advice falls through to stock `compile` (default `(grep-mode)`) |
576+
| `ghostel-compile-debug` | Log lifecycle events to `*Messages*` (default `nil`) |
558577

559578
#### Hooks for your own integrations
560579

0 commit comments

Comments
 (0)