April 7, 2024 • 4 minute read

Your Terminal Might Support Links?

Markdown-style links MIGHT be supported! Find out here!

Table Of Contents
  1. Parsing ANSI
  2. Embedding Links
  3. Comparing Terminals
    1. Disappointments
    2. Passable
    3. Great
    4. Perfect
  4. logpipe

I’ve been working on logpipe over the weekend. It’s a tool to display logs directly from CLI programs by just piping the output directly into it.

I apply some heuristics to provide automatic syntax-highlighting and grouping. It’s neat. You should check it out.

One of the issues I ran up against was tools that don’t plan on having their output piped. They preserve their syntax highlighting through ANSI color codes. It’s how you do color on the terminal in general.

Most tools will remove it if they detect their output is going to something that isn’t a terminal, but some tools don’t suspect that will ever happen, like the CLI output from Shuttle.

So you get something like this.

logpipe without ANSI

Which is not ideal. I could simply remove all these ANSI color codes, but it’s probably better if I just parse them. The colors already included are likely better than ones I would be guessing at.

Parsing ANSI

There’s a lot going on, honestly. People have made libraries for this stuff. I’m going to keep it simple and only parse a common subset. I’ll expand it as bug reports are filed.

The syntax you’re looking for is \x1B[ + number sequence + "m". For example:

\x1B[31m Hello There! \x1B[0m

Which outputs:

Hello There!

The \x1B\[0m at the end is a “reset” which just removes all previous modifications. Try not to forget this. You can accidentally mess up the rest of your output if you don’t reset the styles.

You can test this out directly in Node.js console logs or using echo -e.

Terminal window
echo -e "\x1B[31m Hello there! \x1B[0m"

You can stack modifications, like making text green, bold, and underlined.

\x1B[32m \x1B[1m \x1B[4m Hello \x1B[0m
Hello

There’s a lot of repetition there. We can combine modifiers by semicolon separating them.

\x1B[31;1;4m Hello \x1B[0m
Hello

I’d recommend checking out this StackOverflow answer for a larger explanation. It’s surprisingly thorough.

If you ever wondered how console coloring libraries like chalk work, this is how. Just ANSI escapes.

Embedding Links

Okay, so this is the part that was really surprising to me. I built my parser and found some text not captured by my code.

\x1B]8;;https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles\x1B\\`dev` profile [unoptimized + debuginfo]\x1B]8;;\x1B\\

It’s hard to tell with all that other text there. Here are the escapes without the text (replaced with <name>).

\x1B]8;; <link> \x1B\\ <text> \x1B]8;;\x1B\\

The syntax here is different. We’re used to seeing the left [ bracket, but what is this \x1B]8;;? And then sometime later there’s a lone \ with \x1B\\? It looks bizarre. Well, I guess the previous syntax was also strange.

From the context I recognized this as a markdown-like link, but with the link before the text. A lot of these orderings make more sense when you think about performance characteristics. It’s not there for readability.

The link above is directly from cargo build. It looks something like this:

warning: `inspect_wav` (bin "inspect_wav") generated 3 warnings Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s

Yeah, I had no idea that middle bit was clickable either!

Comparing Terminals

Many terminals either don’t highlight this behavior or don’t make it clickable at all. I went through a couple.

These are the terminals with default settings. It’s possible they contain some secret toggle which I didn’t have time to find.

Disappointments

Terminal.app, Hyper, and Warp all do not display special formatting and do not let you click these links. Nothing breaks - it just appears as regular text.

Passable

Then some terminals like Wezterm, Alacritty, and Kitty let you click the link! They display an underline when you hover over the clickable text, but not before. This makes discovery a bit difficult, but at least it works!

kitty link while hovering

Notably, cargo will not output the special link text for Alacritty for some reason. I don’t think it’s special-cased, so there’s some context being used. Wezterm and Kitty get the upper hand here.

Great

iterm2 comes in with an almost perfect design. It gives special syntax, but the link is not clickable unless you are holding cmd. When you do, it shows a link preview, which is better than the previous 3 terminals.

Annoyingly, it suffers from the same issue as Alacritty. Cargo does not seem to think it supports links, so it won’t supply the ANSI escape codes.

iterm2 while command-hovering

Perfect

The winner absolutely no one predicted - VS Code’s integrated terminal!

VS Code before hovering

By far the best experience. It displays this dotted underline for clickable links. It works with Cargo. When you hover over, the underline becomes solid and it shows the destination.

VS Code right after hovering

EDIT: Zed’s terminal also does a great job of this, although the default styling is simply an underline as opposed to a custom one just for links.

logpipe

My tool displays terminal content. I have a chance to do the correct thing. So I do!

If I detect a terminal markdown-like link, I make it clickable.

logpipe after parsing ANSI links

# end note