-
Notifications
You must be signed in to change notification settings - Fork 640
Conversation
c.unformat has logic for resetting specific attributes for the instance of Color 'c'. This is useful when applying several Colors with different attributes to a string or io stream, as Colors will aim to only reset escape codes *they* applied, leaving others' intact. c.format is invoked in 3 places, but c.unformat is invoked only in one spot, highlighting an asymmetry. If we look at these missing spots, they are: 1. c.Set vs. c.unset 2. c.SetWriter vs. c.UnsetWriter It makes sense to apply the 'c.unformat' logic to these two cases. We don't touch the global 'Unset' function as it does not have the context of a particular Color, and so continues to apply a global reset code. While here, we also make the private 'c.unset' into an exported 'c.Unset' so that 'c.Set' has a counterpart. The added tests all fail prior to this commit, but pass after.
[1] lays out a standard for terminals to support hyperlinked text. Support is quite widespread but not universal. Examples of support include iTerm2, Ghostty, Alacritty, Hyper, Kitty, and more [2]. This OSC 8 standard is achieved via escape codes, very similar to what this project does with colors and other attributes - it's a natural fit. Because it is a little unique in how it functions, this 'hyperlink' feature doesn't follow the existing 'Attribute' pattern. Instead, it's a standalone field which gets special handling. That said, updating just c.format and c.unformat is enough, as with the last commit, these are now used in all relevant spots that affect formatting. [1] https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda [2] https://github.com/Alhadis/OSC8-Adoption/?tab=readme-ov-file
This uses my fork of fatih/color (amterp/color) to use the OSC 8 standard for hyperlinking text in terminals. Hopefully the amterp/color fork dependency is temporary and we can return to fatih/color after the upstream issue [1] and PR [2] are merged in and released. [1] fatih/color#253 [2] fatih/color#255
amterp
commented
Apr 22, 2025
Hey @fatih, I appreciate you may be busy, just wanted to give a friendly ping/bump to ask if you can have a look at this sometime, thanks! :)
amterp
commented
Jul 13, 2025
FYI for any readers interested in using this feature, I have a fork here https://github.com/amterp/color which includes it.
Hopefully this PR can progress and I can close that fork, however.
@rhansen
rhansen
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if the reset logic is correct. With the following:
package main import "github.com/amterp/color" func main() { color.NoColor = false color.Cyan("before %s after", color.New().Hyperlink("https://example.com/").Sprint("link")) }
I get the same as:
printf '\e[36mbefore \e]8;;https://example.com/\e\\\e[mlink\e[m\e]8;;\e\\ after\n\e[0m'
Notice that the cyan ends at the start of the linked text; I expect all of the text to be cyan. Is this expected behavior? If so, is there an ergonomic way to accomplish what I want?
Prior to this commit, when a hyperlink with no color attributes (example:) color.New().Hyperlink(url) is nested inside colored text, the outer color is being reset instead of preserved. The root cause is that format() and unformat() are unconditionally emitting SGR codes even when c.params are empty. An empty params array results in '\x1b[m' (equivalent to reset-all), which kills surrounding colors. The fix in this commit adds a conditional check: only emit SGR codes when 'len(c.params) > 0'. This allows plain hyperlinks to emit only OSC 8 hyperlink codes without any color resets, preserving any outer color context. Hyperlinks with color attributes continue to work as before since they have non-empty params. We add a test case to cover this case.
Hey @rhansen, yes good catch, that's a bug. We should be doing a length check in format() and unformat() for c.params, otherwise the sequence() call results in a 'reset all' code.
I've committed a fix to the https://github.com/amterp/color fork and released in v1.20.1, let me know if that works for you.
I've also pushed a copy of the commit to this PR, in case it gets picked up.
@rhansen
rhansen
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks! Hopefully this will get picked up.
Use a terminal color package to improve readability and automatically disable escape codes when stdout is not a terminal. Add a new command-line option to allow the user to force color on or off. github.com/amterp/color is a temporary fork of github.com/fatih/color; it is used because fatih/color#255 has not been merged yet.
Fixes #253 .
Copy paste of 'hyperlink support' commit:
I also include a preceding commit to make our use of
c.formatandc.unformatmore consistent, as I noticed myHyperlink_Fprinttest was failing due to what appeared like a bug. Namely, in my test, I usec.Fprintwhich usesc.SetWriterandc.UnsetWriter.c.UnsetWriterwas not invokingc.unformatbut was instead applying its own global reset, which seems like an oversight. By re-usingc.unformat, things work more consistently.Please take a look, keen to hear feedback :) Thanks for your work on the library!