ghostty
ghostty copied to clipboard
Click to move cursor doesn't work properly when command is too long
When I try to move the cursor with alt+click on a long command (which wraps around and takes more than one line), I'm faced with this unexpected behavior. Sometimes the cursor jumps to the wrong line and sometimes it jumps to the last column on the screen and refuses to move to another line.
I demonstrated it in this video:
https://github.com/user-attachments/assets/cde340ec-4351-4699-9e5e-157b669efde1
Does this work in other terminals? What shell? (looks like fish?)
I'm not sure of an easy way to fix this. The way click to move works is by synthesizing arrow key presses (that's how it works in other terminals too). Depending on the shell it can get pretty messy to figure out the right arrows to get if the shell is jumping the cursor around...
The place to look is this function:
https://github.com/ghostty-org/ghostty/blob/d47df211b03894427592851b0be3b528ff8221ff/src/terminal/Screen.zig#L2651-L2664
This is well unit tested, and given some screen state we can write a unit test and assert we generate the correct left/up arrow keys to get there.
Yes the shell is fish. I use this a lot in kitty and can confirm it works there:
https://github.com/user-attachments/assets/817d256a-d6f8-46a5-b57f-bea8b5091cd2
Thanks. I'll mark this as a bug even though I haven't confirmed it. I've given guidance above on exactly what needs to be modified and tested to fix this.
I'm seeing similar behavior with long command lines in default zsh on macOS 15.2.
I'm seeing this in Vim when opened in Ghostty (v1.0.1, commit, macOS 15.2) - nothing happens. Other terminals like iTerm2 or macOS Terminal are fine jumping to wherever I alt+click
Ran into the same issue today, and I realized that clicking on a different line seemed to always replace the current line with some previous one in the shell’s history pager.
I took a look at the implementation of clickMoveCursor (https://github.com/ghostty-org/ghostty/blob/main/src/Surface.zig#L3138) and it does emit up arrow keys in order to go up.
This doesn’t work for fish, because the up arrow key is (by default) bound to up-or-search, which is defined as “depending on cursor position and current mode, either search backward or move up one line” (see https://github.com/fish-shell/fish-shell/blob/master/share/functions/up-or-search.fish).
I took a look at what iTerm2 does (code here) and they only move either left/right, extract the position of the cursor after each left/right movement and repeat till they reach the target position.
I also suspect that the native Terminal.app uses this same logic, because I can momentarily see the cursor flash along the way as it “reaches” its destination.
Would this behavior be possible to implement in Ghostty? Would be willing to work on a PR if the iTerm2 method seems reasonable and feasible to implement.
I took a look at what iTerm2 does (code here)
Would be willing to work on a PR if the iTerm2 method seems reasonable and feasible to implement.
Do note that iTerm2 is GPLv2, and I think you hence can't legally contribute code taken from iTerm2 to Ghostty (since Ghostty is MIT). IANAL and this might be entirely wrong, I'm just going based off what I've seen people say before in the Ghostty Discord.
There’s an implementation of something similar in xterm.js, which seems to have been written without reference to the iTerm2 code (issue: https://github.com/xtermjs/xterm.js/issues/890, pr: https://github.com/xtermjs/xterm.js/pull/896).
It was mentioned above that the feature also works in Kitty, which is ~MIT~ GPL licensed; the code in question is [removed links to GPL'd code]
in outline terms, it does a couple of things - it checks if the cursor is in a shell prompt (which looks to be using metadata in the underlying Screen object, I didn't dig too far) and checks if the Screen supports click natively - if it does, it sends the click using a CSI code, otherwise it fakes mouse motion. The mouse motion code is mostly straightforward - if the target is above or on the same row and left, the left key will be used, otherwise right. it examines the text it will pass over - there's special casing in there for empty cells because they have special meaning in zsh and fish, and for dualwidth characters because zsh only uses a single arrow press to go past them. Once the characters are counted, the keypresses are output as a single buffer.
The dualwidth case is interesting, as Mitch suggested more analysis was needed on #7645 to see if that handled motion the way other terminals do, and it looks to me like that - and the empty cell case - might need more detailed tests to cover this.
kitty is licensed under the GPLv3, not MIT. https://github.com/kovidgoyal/kitty/blob/master/LICENSE
Oof, apologies @kovidgoyal , I googled to check instead of just looking at the license file, and the answer was wrong. Correcting that above, and removing the direct links to the code to discourage copying.