From 9fa68b24a784b14321e05bf556fcc11cc9ae129b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 19 Oct 2026 15:48:53 -0400 Subject: [PATCH] T59: a prompt with no cell before the cursor, as fish's empty left prompt beside a marked right one, starts the input at its first row, where the press assumed a cell and panicked --- src/term/click.rs | 8 ++++---- src/term/click/tests.rs | 11 +++++++++++ tasks/acceptance.md | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/term/click.rs b/src/term/click.rs index 49dd325..ea2c739 100644 --- a/src/term/click.rs +++ b/src/term/click.rs @@ -101,7 +101,7 @@ pub fn press(term: &Term, click: Point, relative: bool) -> Option> let cursor = grid.cursor.point; let first = start(term, cursor.line.0)?; let cell = |line: i32, column: usize| &grid[Line(line)][Column(column)]; - // The prompt ends at its last cell before the cursor. + // The prompt ends at its last cell before the cursor; an empty one, at its first row's start. let end = (first..=cursor.line.0) .flat_map(|line| { let width = if line == cursor.line.0 { @@ -112,10 +112,10 @@ pub fn press(term: &Term, click: Point, relative: bool) -> Option> (0..width).map(move |column| (line, column)) }) .filter(|&(line, column)| prompted(cell(line, column))) - .last() - .expect("a prompt's first row holds its cells"); + .last(); let at = (click.line.0, click.column.0); - if at <= end || prompted(cell(at.0, at.1)) { + let before = end.map_or(at.0 < first, |end| at <= end); + if before || prompted(cell(at.0, at.1)) { return None; } let row = if relative { diff --git a/src/term/click/tests.rs b/src/term/click/tests.rs index 6184f32..cd8dab7 100644 --- a/src/term/click/tests.rs +++ b/src/term/click/tests.rs @@ -139,6 +139,17 @@ fn a_shell_that_takes_clicks_hears_the_press_where_it_landed() { sent(&right, click(0, 3), false).as_deref(), Some("\x1b[<0;4;1M") ); + let empty = term( + 20, + "\x1b7\x1b[1;17H\x1b]133;A;click_events=1\x07~/a\x1b]133;B\x07\x1b8ls", + ); + let left = sent(&empty, click(0, 1), false); + assert_eq!(left.as_deref(), Some("\x1b[<0;2;1M"), "only a right prompt"); + assert_eq!( + sent(&empty, click(0, 17), false), + None, + "on that right prompt" + ); let two = term( 20, "\x1b]133;A;click_events=2\x07~/src\r\n> \x1b]133;B\x07ls", diff --git a/tasks/acceptance.md b/tasks/acceptance.md index c8909e3..1d1a00d 100644 --- a/tasks/acceptance.md +++ b/tasks/acceptance.md @@ -71,7 +71,7 @@ What no check reached: no real KWin drew the blur; of Wayland desktops, only Hyp | OSC 8 and plain links on Cmd-press, Ctrl-press off macOS; inside herdr, herdr opens them whole | T09, T45 | `term::links` tests; on macOS with real herdr 0.9.5 and a stand-in `open` on its PATH, Cmd-press on a URL herdr wraps, on its wrapped tail, on an OSC 8 link and on a URL over three rows opened the whole URL and the OSC 8 target, where before the app opened `.../ELYTermin` and nothing; Cmd-hover underlined the long URL across its rows; outside herdr the app opened the URL itself; an OSC 8 link whose label holds `file:///etc/hosts` and whose target is a web page opened the page through herdr, which keeps the target from the terminal while it takes the mouse; Shift-Cmd-press on a plain `file:///etc/hosts` inside herdr the app opened itself, caught by an `NSWorkspace` stand-in | Pass | | Focus reporting, title, scrollback limit, find | T10 | `term::find` tests; round 3 failed at the cap, its fix stands on a red-green test | Pass | | A scrollbar over the history: a drag on its thumb scrolls, whatever the program does with the wheel | T58 | `term::scrollbar` test: the thumb's share and place follow the history and the view's offset, and a place past either end of the track holds at it; under Xorg's dummy screen, after `seq 1 500`, the thumb stood at the bottom right, a drag to the top showed the first prompt and lines 1 to 39, and one back down lines 220 to 259; with mouse tracking on, the wheel went to the program as reports and did not scroll, while a drag on the thumb showed lines 245 to 284 | Pass | -| Click to move: a plain click on the line the shell reads, past its prompt, moves its cursor there, the press itself to a shell that takes clicks, else arrows | T59 | `term::marks` test: A's `click_events=1` and `2` read as a press at the screen's row or counted from the prompt; `term::click` tests: a shell that takes clicks hears an SGR left press at the clicked cell, a row below the prompt too, none before the prompt, in the history or on the alternate screen; B reads the line its prompt started; a click on the typed line counts the characters to it, left or right, past the end means the end, a wide character takes one arrow, soft-wrapped rows are one line, trailing spaces count up to the cursor, and a click on the prompt, another line, a line no prompt shows, output without marks or the alternate screen moves nothing; on macOS `shot` ran zsh with a marked prompt: after `echo hello` a click on the `h` logged `moves the cursor -5`, and `X` typed then ran `echo Xhello`; a drag sent nothing, and clicks on the prompt and on output logged `off the input`; round 1 of T59's review found fish 4.9 a code point per Left, so a count put `X` inside `é`, and its redraw dropped the wrap flag a count followed: with fish's own `click_events=1` the press goes to fish, which then typed `echo Xéabc`, and a second click on the redrawn wrapped line put `Y` at row 2, column 3; zsh still counted 4 arrows; a press let go in another cell and one whose program turned on tracking before the release sent nothing; round 2 found a click on fish's `$` sent to fish, which moved its cursor to the start: a press now goes only past the prompt's last cell before the cursor and never on a prompt cell, so clicks on `$` and its space logged `off the input` and `X` still ended `echo helloX` | Pass | +| Click to move: a plain click on the line the shell reads, past its prompt, moves its cursor there, the press itself to a shell that takes clicks, else arrows | T59 | `term::marks` test: A's `click_events=1` and `2` read as a press at the screen's row or counted from the prompt; `term::click` tests: a shell that takes clicks hears an SGR left press at the clicked cell, a row below the prompt too, none before the prompt, in the history or on the alternate screen; B reads the line its prompt started; a click on the typed line counts the characters to it, left or right, past the end means the end, a wide character takes one arrow, soft-wrapped rows are one line, trailing spaces count up to the cursor, and a click on the prompt, another line, a line no prompt shows, output without marks or the alternate screen moves nothing; on macOS `shot` ran zsh with a marked prompt: after `echo hello` a click on the `h` logged `moves the cursor -5`, and `X` typed then ran `echo Xhello`; a drag sent nothing, and clicks on the prompt and on output logged `off the input`; round 1 of T59's review found fish 4.9 a code point per Left, so a count put `X` inside `é`, and its redraw dropped the wrap flag a count followed: with fish's own `click_events=1` the press goes to fish, which then typed `echo Xéabc`, and a second click on the redrawn wrapped line put `Y` at row 2, column 3; zsh still counted 4 arrows; a press let go in another cell and one whose program turned on tracking before the release sent nothing; round 2 found a click on fish's `$` sent to fish, which moved its cursor to the start: a press now goes only past the prompt's last cell before the cursor and never on a prompt cell, so clicks on `$` and its space logged `off the input` and `X` still ended `echo helloX`; round 3 found that a prompt whose only cells lie after the cursor, fish's empty left prompt beside a marked right one, crashed the press: with no prompt cell before the cursor the input starts at the prompt's first row, and a click on `o` there put `X` at `echXo` | Pass | | A bell makes no sound; the row marks it | T10, T12 | T12 review | Pass | ## 6. Main window and layout