Skip to content

fix: handle partial reads in queryTerminal#619

Merged
aymanbagabas merged 1 commit into
charmbracelet:mainfrom
MartinodF:patch-4
Mar 3, 2026
Merged

fix: handle partial reads in queryTerminal#619
aymanbagabas merged 1 commit into
charmbracelet:mainfrom
MartinodF:patch-4

Conversation

@MartinodF

Copy link
Copy Markdown
Contributor

Note: re-opening #616 after v2-exp merge

What is broken?

This all started with lipgloss.HasDarkBackground timing out most (80%?) of the time when running in Windows Terminal.

After ignoring the problem for a few weeks, I finally decided to figure out what was happening: queryBackgroundColor sends an OSC 11 and a CSI DA1 request one after the other and is supposed to receive back (at least) one response of each type.

The code (with a couple of debug statements) running in the VSCode integrated terminal:

Read 25 bytes: "\x1b]11;rgb:1f1f/2424/3030\x1b\\"
Received sequence: "\x1b]11;rgb:1f1f/2424/3030\x1b\\", state: 0, command: 11, data: "11;rgb:1f1f/2424/3030"
Read 7 bytes: "\x1b[?1;2c"
Received sequence: "\x1b[?1;2c", state: 0, command: 16227, data: ""

The same code in Windows Terminal:

Read 16 bytes: "\x1b]11;rgb:1a1a/1b"
Received sequence: "\x1b]11;rgb:1a1a/1b", state: 5, command: 11, data: "11;rgb:1a1a/1b"
Read 7 bytes: "1b/2626"
Received sequence: "1b/2626", state: 5, command: 11, data: "11;rgb:1a1a/1b1b/2626"
Read 2 bytes: "\x1b\\"
Received sequence: "\x1b\\", state: 0, command: 11, data: "11;rgb:1a1a/1b1b/2626"
Read 16 bytes: "\x1b[?61;4;6;7;14;2"
Received sequence: "\x1b[?61;4;6;7;14;2", state: 2, command: 16128, data: ""
Read 16 bytes: "1;22;23;24;28;32"
Received sequence: "1;22;23;24;28;32", state: 2, command: 16128, data: ""
Read 7 bytes: ";42;52c"
Received sequence: ";42;52c", state: 0, command: 16227, data: ""

As you can see, the terminal response is being received in chunks. Lipgloss assumes that somehow we will always end the read on a sequence boundary, and:

  • Resets the parser state between reads
  • Sends every partial read to filter() regardless of the current parser state.

This causes filter() to:

  • Fail to parse the color (the least of our problems)
  • Fail to recognize the DA1 response, thus never exiting the loop until the cancel reader times out

What am I changing?

The patch makes sure that we only ever send complete sequences to filter(), while maintaining the parser state in between reads so that we can successfully complete parsing a sequence even if it's split inconveniently.

If you have any better ideas on how to implement this, please feel free to propose them :)


Side note, in all terminals I use the CSI DA1 request causes the terminal to return the background and foreground colors in addition to the DA1 response. I feel like maybe there's an opportunity to simplify queryBackgroundColor, but I don't know how this behaves in other terminal emulators.

  • I have read CONTRIBUTING.md.
  • I have created a discussion that was approved by a maintainer (for new features).

Nothing guarantees that we will read a complete sequence from in, and when we don't, we should not pass an incomplete one to filter or continue while discarding those bytes.
@MartinodF

Copy link
Copy Markdown
Contributor Author

@aymanbagabas anything I can do to help you review this?

@aymanbagabas aymanbagabas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@MartinodF This is great!

Thank you so much for your patience and contribution. I will merge this and cut a release soon 🙂

@aymanbagabas aymanbagabas merged commit 1dd352c into charmbracelet:main Mar 3, 2026
6 of 9 checks passed
@MartinodF

Copy link
Copy Markdown
Contributor Author

@MartinodF This is great!

Thank you so much for your patience and contribution. I will merge this and cut a release soon 🙂

Thanks! Then you'll be happy because I have another contribution for you 😂 #620

@G-Rath

G-Rath commented Mar 9, 2026

Copy link
Copy Markdown

fwiw it looks like this got missed off the changelog for v2.0.1 - I found this after noticing osv-scanner was taking at least 4 seconds to display --help, and tracked it down to be within the internals of lipgloss.HasDarkBackground(os.Stdin, os.Stdout) which gets called on import of compact.

Updating to v2.0.1 looks to have resolved this, assumingly because of this change 🎉

G-Rath added a commit to google/osv-scanner that referenced this pull request Mar 10, 2026
…erminal (#2630)

I noticed that it was taking ~4 seconds for `osv-scanner --help` to
resolve, which looks to be due to bug in `lipgloss` when running on
Windows Terminal that was fixed via
charmbracelet/lipgloss#619

This would have gotten fixed automatically by the next minor versions
update, but I figured I might as well do a dedicated PR for it since I
noticed it 🤷
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.

3 participants