Skip to content

Add OSC8 hyperlink support#1360

Merged
achernya merged 7 commits intomobile-shell:masterfrom
zedinosaur:osc8
Mar 22, 2026
Merged

Add OSC8 hyperlink support#1360
achernya merged 7 commits intomobile-shell:masterfrom
zedinosaur:osc8

Conversation

@zedinosaur
Copy link
Contributor

Per the specification described in https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda, add hyperlink support to mosh.

Closes #1245

@achernya
Copy link
Collaborator

For us to consider merging this it needs to have test coverage.

@zedinosaur
Copy link
Contributor Author

That's a fair cop. I didn't immediately find the test suite but now I have I will take a crack at it.

@zedinosaur
Copy link
Contributor Author

Good catch. I'm rethinking the way the params are parsed and they should all be technically preserved, not just id.

@zedinosaur
Copy link
Contributor Author

PTAL, there's a test now and I reworked the param parsing.

@zedinosaur
Copy link
Contributor Author

Please let me know if there's any further issue. I'd like to take a stab at adding OSC7 support as well since it seems straightforward compared to this, but would like to see this merged first.

@eminence
Copy link
Member

eminence commented Dec 9, 2025

I did a quick test and review and it seems to work as advertised, but I'll do some more real-world testing over the next few weeks

@eminence
Copy link
Member

I'm doing some testing with ripgrep, which has support for OSC 8 hyperlinks. In particular there's a special "vscode" mode that causes some problems.

echo test > test
rg test -g test --hyperlink-format=vscode --color=always

In my terminal, it looks right:

image

But when I clear my terminal, I see some underlines left over:

image

This doesn't happen outside of mosh.

You can also test with this:

printf "\e]8;;vscode://file/home/achin/test:1:1\e\x5c\e[0m\e[35mtest\e[0m\e]8;;\e\x5c:\e[0m\e[1m\e[31mtest\e[0m\n"

@zedinosaur
Copy link
Contributor Author

Thanks for the review. I missed clearing the hyperlink correctly in Cell::reset(), fixed now. PTAL.

@zedinosaur
Copy link
Contributor Author

Please let me know if you found any other problems, would love to see this merged.

@zedinosaur zedinosaur force-pushed the osc8 branch 2 times, most recently from 63c1bb8 to ef3b296 Compare February 27, 2026 21:50
@zedinosaur zedinosaur force-pushed the osc8 branch 3 times, most recently from 2b16655 to 1e2ff5b Compare February 27, 2026 22:16
zedinosaur added a commit to zedinosaur/mosh that referenced this pull request Feb 28, 2026
@zedinosaur
Copy link
Contributor Author

Please let me know if there's anything else I can do to get this merged.


std::shared_ptr<const Hyperlink> Hyperlink::make_empty()
{
static auto* const empty_hyperlink = new std::shared_ptr<const Hyperlink>( new Hyperlink );
Copy link
Collaborator

Choose a reason for hiding this comment

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

There is no point in make_empty returning a std::shared_ptr.

static const auto* empty_hyperlink = new Hyperlink;
return empty_hyperlink;

suffices

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I do that every empty hyperlink will get a fresh shared_ptr, including allocating a new control block. I can do that but seems wasteful of memory?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Taking a step back, what problem is this shared_ptr trying to solve? Is the issue that it's going to be incremented in every cell by default?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My idea for the shared_ptr was just to make the representation of an empty hyperlink cheaper in a Cell, since most Cells won't have hyperlinks set. Creating a new shared_ptr for Cell that's empty seems similarly wasteful. I'll happily rip out all of the shared_ptr stuff out if you think this optimization isn't worth it.

fb->ds.set_hyperlink( Hyperlink::make_empty() );
} else {
fb->ds.set_hyperlink(
std::make_shared<const Hyperlink>( OSC_string.substr( 2, second_semicolon - 2 ), std::move( url ) ) );
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is the only place in the code where a non-empty hyperlink is created? I'm a bit weirded out by the indexing, but I think it's safe? OSC_substring is guaranteed to be at least size() > 2 on line 611 and second_semicolon is guaranteed to be 2..size() if it's not npos, which it's not on line 617, so it's probably fine?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's right. I can capture the params into a variable if that would make things clearer.

@achernya achernya merged commit decd9b7 into mobile-shell:master Mar 22, 2026
6 checks passed
achernya pushed a commit that referenced this pull request Mar 22, 2026
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.

Add support for OSC 8 sequences (hyperlinks)

3 participants