Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add Hyperlink support + minor consistency improvements#255

Open
amterp wants to merge 3 commits into
fatih:main from
amterp:main
Open

Add Hyperlink support + minor consistency improvements #255
amterp wants to merge 3 commits into
fatih:main from
amterp:main

Conversation

@amterp

@amterp amterp commented Mar 31, 2025

Copy link
Copy Markdown

Fixes #253 .

Copy paste of 'hyperlink support' commit:

[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

I also include a preceding commit to make our use of c.format and c.unformat more consistent, as I noticed my Hyperlink_Fprint test was failing due to what appeared like a bug. Namely, in my test, I use c.Fprint which uses c.SetWriter and c.UnsetWriter. c.UnsetWriter was not invoking c.unformat but was instead applying its own global reset, which seems like an oversight. By re-using c.unformat, things work more consistently.


Please take a look, keen to hear feedback :) Thanks for your work on the library!

janvhs and rhansen reacted with thumbs up emoji
amterp added 2 commits March 31, 2025 21:45
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 
amterp added a commit to amterp/rad that referenced this pull request Apr 1, 2025
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

Copy link
Copy Markdown
Author

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

Copy link
Copy Markdown
Author

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

Copy link
Copy Markdown

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.

amterp commented Oct 26, 2025
edited
Loading

Copy link
Copy Markdown
Author

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.

amterp/color@add458f

I've also pushed a copy of the commit to this PR, in case it gets picked up.

@rhansen rhansen left a comment

Copy link
Copy Markdown

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.

rhansen added a commit to rhansen/dh-make-golang that referenced this pull request Feb 23, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

1 more reviewer

@rhansen rhansen rhansen approved these changes

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Support hyperlinking escape chars

2 participants

AltStyle によって変換されたページ (->オリジナル) /