Skip to content

Skip link detection on the active prompt and prompt prefix (#199)#212

Merged
dakra merged 1 commit into
mainfrom
skip-link-detection-on-input
May 1, 2026
Merged

Skip link detection on the active prompt and prompt prefix (#199)#212
dakra merged 1 commit into
mainfrom
skip-link-detection-on-input

Conversation

@dakra
Copy link
Copy Markdown
Owner

@dakra dakra commented Apr 30, 2026

Summary

  • In tty Emacs, the link-keymap text property's RET binding hijacks the actual return key when the cursor sits on a linkified cell (e.g. a path inside a typed cd src/main.rs). The shell never sees RET — issue After adding a command on the shell, I need a trailing space before return executes the command #199.
  • Renderer marks OSC 133 B..C cells with a new ghostel-input text property. ghostel--detect-urls now skips ghostel-prompt always (shell-generated decoration) and skips ghostel-input only on the cursor's line — historical typed commands stay clickable.
  • Hot-loop perf: cursor's line is captured once as a (bol . eol) cons; the per-match skip check is O(1) instead of line-number-at-pos (O(pos)).

Test plan

  • make -j4 all: 39 zig + 103 native + 219 elisp, all green.
  • New tests added — verified each fails on main first, passes after the fix:
    • ghostel-test-detect-urls-skips-active-input (elisp): historical input/URL → linkified; active input/URL → skipped; intra-line partial skip; prompt prefix never linkified.
    • ghostel-test-osc133-input-text-property (native): cells between OSC 133 B and C carry ghostel-input; prompt prefix does not.
    • ghostel-test-osc133-input-wide-char-boundary (native): wide CJK input takes 1 Emacs char of ghostel-input; the spacer-tail produces no Emacs char and doesn't extend the region.
  • Bench (make bench-quick, 10 iters × 512KB URL/path-heavy data) — within run-to-run noise vs main. The skip-check optimization protects against the worst case (large scrollback × many matches per redraw).
  • Live-verified in *ghostel* (zsh shell integration): typing echo ghostel.el is not clickable while typing; after RET, output ghostel.el is clickable; scrolling back to the historical command line, the path inside echo ghostel.el is also clickable; the cwd shown by the prompt prefix is never clickable.

Caveats

  • The fix relies on cell-level OSC 133 B..C, which ghostel's bash/zsh/fish integration emits. Shells that emit only OSC 133 A fall back to the row-level ghostel-prompt and lose the historical-input-clickable benefit (the active prompt is still RET-safe).
  • TUIs that don't emit OSC 133 (Claude Code, REPLs, etc.) are unaffected — there's no semantic signal for ghostel to use. Fix would need to come from upstream.

@dakra dakra force-pushed the skip-link-detection-on-input branch from 91a9d97 to 31ee7fa Compare April 30, 2026 23:27
@dakra dakra requested a review from Copilot April 30, 2026 23:30
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses issue #199 where, in tty Emacs, linkified text can steal the RET keybinding while the cursor is on the actively edited prompt line. It adds a semantic “user input” marker from the renderer and updates the Elisp URL/file detection to avoid linkifying the active input while keeping historical input linkified.

Changes:

  • Add a ghostel-input text property (set by the Zig renderer for OSC 133 B..C input cells).
  • Update ghostel--detect-urls to always skip ghostel-prompt and to skip ghostel-input only on the active prompt line (with an O(1) per-match check).
  • Add Elisp + native-focused tests covering active-input skipping and wide-char input boundaries.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
lisp/ghostel.el Adds skip predicate and active-line bounds to avoid linkifying active prompt input.
src/render.zig Tracks per-row input character range and applies ghostel-input property during insert/styling.
src/emacs.zig Adds pre-interned symbol for ghostel-input so the native module can set the property.
test/ghostel-test.el Adds regression tests for active-input URL detection skipping and OSC 133 input property behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lisp/ghostel.el
@dakra dakra force-pushed the skip-link-detection-on-input branch 2 times, most recently from aff8f14 to a53a7ba Compare May 1, 2026 10:47
Plain-text URL/file detection ran across the whole viewport including
the cell the user was typing into. In tty Emacs, RET on a linkified
cell resolves to `ghostel-open-link-at-point' (text-property keymap
overrides ghostel-mode-map), so pressing return at a path the shell
echoes — e.g. `cd src/main.rs' — opens the file instead of running
the command.

The renderer now marks OSC 133 B..C cells with a `ghostel-input' text
property alongside the existing `ghostel-prompt', and paints
`ghostel-prompt' only over the prompt prefix when an input range is
known (instead of the whole row).  `ghostel--detect-urls' skips
`ghostel-prompt' regions unconditionally and skips `ghostel-input'
on the cursor's line only — historical typed commands keep their
links so the user can still follow a path inside an old `echo foo/bar'
or `cd ~/proj'.

The bundled bash/zsh/fish integrations are updated to emit 133;A and
133;B from PS1/PROMPT/fish_prompt itself rather than back-to-back from
PROMPT_COMMAND/precmd before the prompt expands.  With both markers
fired pre-prompt, libghostty saw zero cells between A and B and tagged
the entire prompt row (prefix + input) as INPUT, defeating the prefix
protection.

For bash there are two extra wrinkles:

  * bash 5.x readline redraws the prompt after bracketed-paste-mode
    setup without re-firing PROMPT_COMMAND, and for multi-line prompts
    redraws only the last visual line.  We inject 133;A after every
    newline in PS1 (literal newlines and the `\n' PS1 escape) so the
    partial last-line redraw still has a 133;A to anchor PROMPT scope.

  * The OSC terminator is BEL (`\a') rather than ST (`\e\\'): bash's
    `${var//pat/repl}' substitution eats backslashes in the
    replacement, mangling ST-terminated markers.

Closes #199
@dakra dakra force-pushed the skip-link-detection-on-input branch 2 times, most recently from 5301fee to c145c5e Compare May 1, 2026 13:00
@dakra dakra merged commit c145c5e into main May 1, 2026
17 of 18 checks passed
@dakra dakra deleted the skip-link-detection-on-input branch May 3, 2026 07:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants