fzf icon indicating copy to clipboard operation
fzf copied to clipboard

fzf --height hangs on FreeBSD virtual terminals

Open tavianator opened this issue 2 years ago • 0 comments

  • [x] I have read through the manual page (man fzf)
  • [x] I have the latest version of fzf
  • [x] I have searched through the existing issues

Info

  • OS
    • [ ] Linux
    • [ ] Mac OS X
    • [ ] Windows
    • [x] Etc.
  • Shell
    • [ ] bash
    • [x] zsh
    • [ ] fish

Problem / Steps to reproduce

In FreeBSD's virtual terminals, things like Ctrl+T have always hung for me until I typed a few characters. I tracked it down today.

fzf by itself works fine, but fzf --height=40% hangs. The issue is the findOffset() call: https://github.com/junegunn/fzf/blob/885cd8ff04e73d61279f0ac58011f74613e809dd/src/tui/light.go#L162

findOffset() basically does write("\033[6n") and then read()s the response from the terminal: https://github.com/junegunn/fzf/blob/885cd8ff04e73d61279f0ac58011f74613e809dd/src/tui/light_unix.go#L85-L90

nonblock is set to tries > 0, so the first read attempt is blocking. Unfortunately, FreeBSD VTs never respond to any escape sequences, so the first read hangs until I press a key. The next offsetPollTries - 1 == 9 reads are non-blocking, so they time out eventually.

I tried a simple patch:

@@ -87,7 +87,7 @@ func (r *LightRenderer) findOffset() (row int, col int) {
        r.flush()
        bytes := []byte{}
        for tries := 0; tries < offsetPollTries; tries++ {
-               bytes = r.getBytesInternal(bytes, tries > 0)
+               bytes = r.getBytesInternal(bytes, true)
                offsets := offsetRegexp.FindSubmatch(bytes)
                if len(offsets) > 3 {
                        // Add anything we skipped over to the input buffer

and now instead of hanging I just get a ~1s delay at startup. The delay can be reduced by decreasing offsetPollTries, but I don't know if that's likely to break anything.

Rather than a retry loop, I think the best way to implement writing a terminal command and reading the response is something like

  • set raw mode
  • read tty until EOF, save to buffer
  • write \033[6n to tty
  • poll() for POLLIN with a timeout
    • if successful
      • read from tty
      • parse an escape sequence, if present
      • buffer anything else

But I don't know enough Go to try implementing that.

tavianator avatar Jun 29 '22 17:06 tavianator