gwsw / less

Less - text pager
http://greenwoodsoftware.com/less
Other
560 stars 88 forks source link

less sometimes runs commands only after pressing Enter #579

Open injust opened 1 week ago

injust commented 1 week ago

I came across a really strange bug today. I reproduced it in a new Git repo with a minimal config, but it still seems very specific to my environment.

I am running git diff on a Bun lockfile, with git's pager set to delta. delta pipes its output to less. I followed the instructions at https://bun.sh/guides/install/git-diff-bun-lockfile to configure Git to work with Bun lockfiles.

This asciinema recording can best explain what is happening: https://asciinema.org/a/Iy7ds9zCaqPTn5YAAabYe3F4b or asciicast.txt

Usually I press f and b to navigate, and q to quit. However, ~1/3 of the time, less doesn't seem to do anything until Enter is pressed. When this issue occurs, it persists until less exits.

The issue seems to stop if I do any of the following:


Git: 2.47.0 delta: 0.18.2 less: 661 macOS: Sequoia 15.1 Terminals: Ghostty and Terminal.app


.gitattributes:

*.lockb binary diff=lockb

.gitconfig:

[core]
    fsmonitor = true
    pager = LESS= delta

[diff "lockb"]
    binary = true
    textconv = bun

bun.lockb files: old.bun.lockb.zip and new.bun.lockb.zip

Minimal repo that reproduces for me: repo.zip (test with git diff HEAD^)

injust commented 1 week ago

While the issue was happening (pressing f et al didn't execute immediately), I ran printenv LESS and pressed Enter when it was done. After that, less started running commands like f instantly.

This convinces me that this is a bug in less.

gwsw commented 1 week ago

I have not investigated this yet, but the symptom suggests that the terminal is not in raw mode. The fact that it happens inconsistently suggests that there is some kind of race when less starts up and sets the terminal mode, where another program, perhaps bun or delta, is also trying to set the terminal mode. If less sets the mode first and then the other program overwrites it, you will see this symptom. If the other program sets the mode first, then less will overwrite it and behave normally. If you run the ! command (or use ctrl-Z) to run printenv, less will set the terminal mode again after the command completes, which explains why that seems to fix the problem.

injust commented 1 week ago

I immediately thought of delta's dark/light detection (grep for --detect-dark-light in https://dandavison.github.io/delta/full---help-output.html), but the documentation says it shouldn't cause a race condition unless manually piping to less (which is not the case here).

I tested by adding --detect-dark-light=never or --dark to delta in core.pager, but the issue still happens.

It would really surprise me if bun is setting the terminal mode. I'd expect Git's textconv to happen early, before less is started.

avih commented 1 week ago

It would really surprise me if bun is setting the terminal mode

I would bet on delta.

On windows, it also sets the console output CP to UTF8 even when stdout is not a tty, which also causses an issue with "less" - because when it's piped into "less", the terminal codepage which "less" sees is UTF-8 (because delta modified it), but after delta exits it restores it to whatever was there initially, but "less" doesn't know that, so UTF-8 output gets broken.

I.e. IMO delta's terminal handling is problematic also on windows, so I would bet that it also changes the terminal mode uncoditionally in this issue - also when its stdout is not a tty.