tmux / tmux

tmux source code

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Feature request: Hyperlinks

egmontkob opened this issue · comments

Recently two popular terminal emulators, GNOME Terminal and iTerm2 have implemented a brand new feature: explicit hyperlinks.

Unlike the existing functionality of most terminal emulators of automatically detecting URLs that appear onscreen, this time it's like hyperlinks on web pages: the link target is explicitly specified by the OSC 8 escape sequence and the visible text can be an arbitrary piece of text.

We believe this feature can provide a noticeable productivity boost for plenty of users, and hence we're hoping to get some widespread recognition and support, in which tmux joining the game would be a key factor!

Ideally tmux would make its presence transparent to the user and make sure hyperlinks work exactly as if tmux weren't in the game. This includes autogenerating or mangling the id parameter in some trivial way, see the docs linked below for why and how, but that's a piece of cake. The toughchallenging bits is probably coming up with a memory-efficient way of storing a target URL for some of the character cells.

Please see https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda for details about this feature.

@egmontkob @nicm would you expect the escape passthrough sequence (\033Ptmux;\033<whatever>\033\\) to work with explicit links if the parent terminal supports links?

@ljfranklin Nope, absolutely not.

Are any applications using this yet?

Yes, for example systemd uses this starting with v239 (see the NEWS file and systemd/systemd#8767 for details) to format hyperlinks to manual pages and configuration files.

I guess it will still wait to see if some of the major terminal applications pick it up. irssi/weechat would seem to be the most obvious.

@nicm Please see the originally linked page for a list that I try to maintain up to date.

@lucaswerkmeister Thanks a lot for the systemd info, I didn't even know they had it on their roadmap. Added to my page.

@egmontkob

You know, instead of causing so much churn everywhere to support hyperlinks, maybe do it the other way around that does not require every project to add such code? Acme from Plan 9 did it in the 90s, that was true hyperlinking, because the environment decided how it will treat the resource (and then use Plumber to decide based on rules whether it needs to go to the edit port or the browser port, for instance). Much more powerful, much less code, and allows for greater extensibility. That was also true hyperlinking because to support a new resource you just needed a new rule -- and you could hyperlink anything with anything (consider selecting an ISBN number and in Plan 9 and that led you straight to your browser, or just selecting main.c:32 that opens your text editor with the cursor on that line, it's just like pipes, the limit to extend it is unimaginable).

The fact that every project needs to add hyperlink specific code screams that this model you're pushing for is broken. The editor I write tomorrow needs to accomodate for such a scheme to work, whilst it should the environment accomodating (and in my example, that boils down to adding one rule to match on what is being sent to be opened). Ofcourse, you can't decide if you need Plumber or D-Bus for this, which is why it should be mechanism free, nor do you need to conform to a URI scheme like what freedesktop has. Good design is when you don't need to do much, and it just happens by itself, and it's not like this wasn't done before.

@bl33pbl0p

First of all, I don't think tmux's issue tracker is the right place if you have problems with the overall design. This issue is meant to track the spec's implementation in tmux. The spec itself also has its homepage. But as you commented here, let me respond here.

Apologies for my ignorance, I've heard about Plan 9, but never seen it, don't know what Acme is (its wiki page says it's a "text editor and graphical shell", doesn't seem like a terminal emulator to me), I have no idea what Plumber, edit port, browser port etc. are. But: I have an overall feeling that it's a substantially different notion from what a terminal (terminal emulator) on a Unix system is.

Good design is when you don't need to do much, and it just happens by itself

Generally I agree. This happens when the underlying infrastructure is built up in a way that lets you easily do whatever you want to do. It doesn't necessarily mean fewer code, though, it just means that most of the code you need is luckily already available underneath you and you can build upon, rather than having to write it yourself.

Could you please be more constructive, and show us: How do you think on top of where we started from (Unix systems, terminal emulators, escape sequences, some utilities just spitting output to stdout, some utilities handling the entire terminal emulator canvas using whatever screen drawing methods, etc.) this issue could have been addressed in a more elegant way?

How can I detect if the terminal outside tmux supports this?

Oh I see you have a section about that, although it is not terribly useful. Do iterm2 and gnome-terminal have unique escape sequences which report their version?

We can have a global tree of hyperlinks keyed by a uint32_t ID and store that ID in the grid_cell. Each pane can also have its own tree mapping any application-assigned IDs to the global ID. We can throw them away when the cells are scrolled out of the history or overwritten, but probably not worth the bother. Maybe just on RIS/E3/clear-history.

Presumably the hyperlink target has to also match for the external terminal to highlight, not just the ID, but would need to check. What happens with the history? If two separate applications print the same hyperlink with the same ID at separate points in the history and the user then scrolls up so they are both visible presumably the external terminal will highlight both - probably doesn't really matter and probably no realistic way to fix it anyway.

Would need to track the current tty state to make sure we do not send the hyperlink sequence unnecessarily.

Will need terminfo entries or some other way to detect, best probably something like Shl=\E]8;id=%p1%s;%p2%s\E\\ and Rhl=\E]8;;\E\\. Or just a flag would do perhaps. Or detect from the terminal versions, but that is a pain with !xterm.

We would need the pane tree to map (application ID, hyperlink) -> global ID not just the ID, in case two applications use the same ID for different hyperlinks.

No need to worry about alternate screen because all the global IDs would be stored in the cells so would be switched with everything else. If applications in the same pane use the same (ID, hyperlink) pair it should map back to the same global ID.

We should assign a new global ID for each new (ID, hyperlink) pair that is not in the pane tree where it is received. We can't reuse them across panes because multiple panes may have them visible on screen at the same time. So lookup in the global tree is only for writing to tty (ID from cell to hyperlink); pane tree is when OSC 8 is received to see if an ID already exists.

Presumably the hyperlink target has to also match for the external terminal to highlight, not just the ID, but would need to check.

Yes; at least that was my intent, and is the behavior in VTE. I cannot test iTerm2. From the gist: "Character cells that have the same target URI and the same nonempty id are always underlined together on mouseover."

If two separate applications print the same hyperlink with the same ID at separate points in the history and the user then scrolls up so they are both visible presumably the external terminal will highlight both - probably doesn't really matter and probably no realistic way to fix it anyway.

I agree it's not realistic. This is only relevant if these two are closer to each other than the terminal's size. IDs are mainly useful for fullscreen apps. Simple utilities probably just don't emit an ID; hyperlink cells that don't have an ID are only joined with the ones emitted in the same OSC 8 run.

(The way it is implemented in VTE: each time an OSC 8 without ID (but with non-empty hyperlink) is encountered, a new unique ID is automatically generated for this. This new ID also contains a semicolon if I remember correct, a character that cannot be part of the explicit ID due to OSC 8's syntax. This guarantees that the new autogenerated ID cannot accidentally clash with an explicit one. Of course tmux couldn't use this exact approach since it has to forward the generated ID to the host emulator, but it presumably mangles the IDs anyway (prefix with the pane ID or such), and then it could similarly pick some other prefix that's guaranteed not to conflict with these; or just go with some random that is highly unlikely to clash.)


Just FYI: There are two different ways tmux could leverage the OSC 8 feature. One is when it encounters an OSC 8 from the application running inside, and forwards it towards the host emulator. Another one is when it doesn't receive any, but receives a simple text that looks like a URL, however, with vertical panes, it won't be recognized by the host graphical terminal emulator as such. In this case tmux could help by recognizing it and converting to OSC 8. This latter one sounds more complicated, at least requires that tmux matches the visible contents of its panes against some (complex) regex and takes action on the match – not sure if tmux has anything like this at the moment. This is just a heads-up to keep it in mind for a possible future improvement, not a requirement by any means.


Adding support for feature detection is out of my radar at this moment. Both the terminfo approach and the asynchronous escape sequence response approach would suffer from several problems.

You can just emit OSC 8 and be pretty sure that the terminal emulator will either handle it according to the hyperlink specs, or ignore it. Or, if that's not the case for a certain emulator then apps emitting them break anyway even outside of tmux, tmux forwarding these escape sequences wouldn't make it much worse. I'm not sure about apps emitting OSC 8 hyperlinks, but I'd guess that it's an opt-in feature in them, not an out of box one (at least it's definitely opt-in in ls). That being said, you might introduce a config option to disable this feature, a simple kill switch for those encountering troubles.

We're not going to send random stuff to the terminal because there is no guarantee at all that it will ignore it. terminfo exists to avoid this problem.

Other than that I don't think this is that complicated. There is no need for internal IDs to be used outside tmux, it can just replace them with its own IDs. Hyperlinks without an ID can be just be allocated an external ID and then forgotten about, hyperlinks with an ID can be stored in a tree linked to the pane so that any additions can be mapped to the same external ID.

Your call, of course.

Are really all of tmux's features present in terminfo? There are plenty of features supported by emulators that don't have a way to describe them in terminfo.

We're not going to send random stuff

Well, it's not just "random stuff", it uses the OSC framework defined in ECMA and such. If an emulator cannot silently ignore it then I believe that emulator is broken.


I might one day write down my thoughts about how broken terminfo is, it'd be about as long as this thread so far.

The VTE terminal emulation widget supports hyperlinks. Unlike most of its features, however, it requires the embedding emulator application (e.g. GNOME Terminal, Tilix etc.) to add support too. So we'd need to give up with the model of VTE setting a particular value for $TERM, it would have to be set by the embedding emulator depending on whether it supports hyperlinks or not.

Or, I'm wondering, would you be fine with a terminfo entry that denotes "this emulator is known to either correctly handle or silently ignore OSC 8"...?

VTE used to set TERM=gnome or something similar, it switched to TERM=xterm before I actively joined the project. I don't know the reasons behind this decision. As far as I can tell, Konsole did similarly too. While it would certainly have tons of upsides for us to go with our own descriptor, it would have downsides too. Most importantly, it's unclear who would be responsible for maintaining it, where would be its primary source, how would it reach various Unix systems, even the ones that don't have VTE (the library itself) installed, how could we be sure that if a user ssh's to a remote machine then not only xterm-256color but also vte-256color (or whatever we go with) are available?

I truly doubt VTE will switch back to going with its own terminfo in the foreseeable future. I assume whoever decided to switch to piggybacking TERM=xterm had good reasons. I also cannot see the arguments for going with our own one clearly outweighing the arguments for staying with xterm.

Long story short: assuming that xterm's description won't contain this flag in the foreeseable future, neither will VTE's.


What we could more easily do, and I'm open to adding it, is an escape sequence that asynchronously reports back whether OSC 8 is supported or not. I have my problems with asynchronous stuff too, but that's mostly concerning the application side, so if you're fine with them then I'm okay.

I'm not famililar with these escape sequences to design a good one for reporting this feature, but if anyone comes up with a recommendation that's accepted by VTE's main developer and iTerm2's developer too them I'm fine. We can easily add it to forthcoming vte-0.56, and it could even respond according to whether the embedding emulator app hooked up or not. Unfortunately I don't have the knowledge to design it, but if someone designs it and key developers agree then it gets my support and I'm happy to implement it.

Yes, although there are a few things that are odd: a few controlled by flags (AX for default colours, XT for xterm-style titles); or by the VT version (margins - if XT is present so we know the terminal is broadly xterm-compatible then we send DA); or by an option (the mouse, although we also check for the presence of kmous; also focus events).

There doesn't seem to me much sense in using a flag when a string capability would work just as well.

The advantage of using terminfo is that not only can we change this if a future terminal implements an equivalent feature in a different way, but tmux already has a mechanism (terminal-overrides) to modify or disable terminfo capabilities manually, which means users with a terminal which does not handle this correctly (an older VTE version, for example), can disable it without us needing to add an extra option. tmux has too many options already without adding dozens more for terminal features.

For this particular feature it is unlikely that someone else will implement it differently, or indeed that a terminal that isn't VT compatible will appear and not have much bigger things to worry about before it gets to this point, but I don't see any big reason to make this a special case.

I believe some other applications work similarly - default from terminfo with a way to override or remove capabilities.

Upstream terminfo already contains an entry for vte which is maintained. If the entries are added there then users have the option of either setting TERM=vte, or configuring tmux to add the entries itself. If they aren't, they can only do the latter, which for this is probably not a big ask.

You probably don't need to worry about it, if it comes to it I will invent a couple of terminal capability extension names and see if it is worth trying to get them into upstream terminfo.

I'm not a particular fan of terminfo but it is what we have. Every application implementing their own database of escape sequences or hardcoding them all over the place is clearly a worse option.

I think terminals should be self-describing and able to report their own escape sequences, but that is a much bigger change altogether ;-).

I think terminals should be self-describing and able to report their own escape sequences, but that is a much bigger change altogether ;-).

Definitely! (To both parts of your sentence!)

There doesn't seem to me much sense in using a flag when a string capability would work just as well.

Apologies for sloppy wording, I washed together boolean flags with string capabilities.

if it comes to it I will invent a couple of terminal capability extension names and see if it is worth trying to get them into upstream terminfo

Sounds great, thanks a lot! :)

I'm not sure about apps emitting OSC 8 hyperlinks, but I'd guess that it's an opt-in feature in them, not an out of box one (at least it's definitely opt-in in ls).

FWIW, systemd sends the hyperlink escape sequences by default when talking to a terminal (unless paging to less). Try running TERM= SYSTEMD_PAGER=cat systemctl status systemd-journald and you should see hyperlinks for the unit file and the documentation pages.

Well this will need to wait until I can either get gnome-terminal to build or Debian updates their package in any case.

Oh I am on crack, the version I have does support it...

Here is enough to support this without IDs: tmux-hl.diff.txt. You need:

set -as terminal-overrides ',*:Hls=\E]8;id=%p1%s;%p2%s\E\\:Hlr=\E]8;;\E\\'

Adding IDs should be pretty simple.

It is quite nice but I'm not sure it has enough support to really be worth it (although I guess you could say the same about a lot of terminal features), so I might leave it for another while.

It is quite nice but I'm not sure it has enough support to really be worth it (although I guess you could say the same about a lot of terminal features), so I might leave it for another while.

I just want to provide a use case for this feature: in Yarn, a package manager for Javascript, we print information related to a lot of various packages (for example to explain why it isn't compatible with the current environment).

At the moment when there's a problem related to a package we print its name and version, and leave it to our users to go on the registry website to get more precise information about the faulty package (multiply this by the number of warnings). Having a way to redirect them to the individual pages just by clicking on the package names would go a long way towards improving the DevX (we can't afford to print the package links as they are unnecessary most of the time and would clutter the output).

Another use case I'm aware of would be to abbreviate parts of some filesystem paths, in particular in developer tools. In a lot of case you don't care about the absolute path - you just care about the path starting from the project root (usually the repository root).

More generally, I believe this feature would help a lot your fellow developers 🙂

A pretty nice use case is to view markdown documents with links but without them occupying space. Markdown documents are prevalent in projects nowadays but I need to navigate to its homepage to view a rendered page so those meaningful text is not hiding in a host of links (but I can still visit them when I find interested).

(Well my GUI terminal still doesn't support links....)

Well, I like this feature but it is a fairly big change to do fully and so far this is all either theoretical or tiny use cases. What about the popular applications where this would be a great addition? Mutt/neomutt or alpine? Irssi and weechat? Org mode and mail mode in emacs? Git?

Well, this is a chicken-and-egg problem. It'll be not hard to write a pager for git to link commit hashes and issue references to URLs. I can do that, but I can't use that unless tmux supports hyperlinks (I can switch to another terminal, I can ignore mosh since I spend more time locally, but I can't live without tmux).

PS: I don't feel mutt needs this as most terminals recognize plain URLs. It would be great if weechat supported this but I'd need mosh and tmux's support too.

Sure, but not everyone uses tmux and lack of tmux support hasn't stopped terminal features being added before, and it didn't stop this feature being designed and added to VTE.

The patch I posted above would be enough for simple cases to work, does it work with your application?

I don't want tmux to be the driver for this and end up full of code for new escape sequences that hardly anyone uses.

Someone has to go first and I'd just like to this taken up more widely.

A lot of people use mutt with urlview, this could be a replacement for that.

Sorry I missed the patch. It works for me! I can start using it now 👍

Here is an updated patch against master as of today: tmux-hyperlinks.diff.txt

I noticed that there is a patch here, but the Feature request is closed and the patch is not in the repository. Is support for passing OSC 8 going to be supported in tmux?

For reference, some GCC developers want to start adding support for hyperlinked diagnostics in GCC, but this bug is creating complications for it: https://gcc.gnu.org/ml/gcc-patches/2020-02/msg00022.html

This makes no sense, tmux will ignore unknown OSC sequences, so there is no need for any special handling. It is no different than any terminal that does not support OSC 8, it will just not work.

I don't understand this either, especially given the recent conversation with the same guy in the hyperlink spec's comment section where he didn't mention tmux at all.

If this claim from the patch

Do not enable URLs when TMUX is detected, since that is known to not work reliably.

is valid, it should be justified with a reproducing test case, and ideally even reported here by him.

I have other problems as well with the approach he takes, which I expressed in the comments, e.g. that he aims to detect his favorite terminal emulator, but totally ignores other similarly popular ones.

(Also note he pretty much always misspells "tmux" as "tumx".)

Note: he's using a system that is somewhere between 3–6 years old. Did tmux from that era also silently ignore unsupported OSCs? Or could it be that he's using an old one that was buggy?

commented

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.