Use hyperlinks directly in the terminal¶
This recipe is an example on how to leverage hyperlinks directly in the terminal. Hyperlinks are generated by terminal applications by using the OSC-8 escape sequences.
Requirements¶
By default, ls
doesn't generate hyperlinks, here is an example of aliases to enable them in some common tools:
alias ls='ls --hyperlink --color=auto'
alias delta="delta --hyperlinks --hyperlinks-file-link-format='file://{path}#{line}'"
alias rg='rg --hyperlink-format=kitty'
On macOS, you'll need to install
ls
from coreutils. For example using homebrew, or use a modern alternative like eza.
Configuration¶
With this snippet, you can:
- Click on an hyperlinked directory to navigate into that directory and list its contents
- Click on an hyperlinked file and if its MIME type is 'text', open it directly in Neovim
- Other hyperlinks like URLs remain unchanged and follow WezTerm's default behavior
local wezterm = require 'wezterm'
local act = wezterm.action
local config = wezterm.config_builder()
local function is_shell(foreground_process_name)
local shell_names = { 'bash', 'zsh', 'fish', 'sh', 'ksh', 'dash' }
local process = string.match(foreground_process_name, '[^/\\]+$')
or foreground_process_name
for _, shell in ipairs(shell_names) do
if process == shell then
return true
end
end
return false
end
wezterm.on('open-uri', function(window, pane, uri)
local editor = 'nvim'
if uri:find '^file:' == 1 and not pane:is_alt_screen_active() then
-- We're processing an hyperlink and the uri format should be: file://[HOSTNAME]/PATH[#linenr]
-- Also the pane is not in an alternate screen (an editor, less, etc)
local url = wezterm.url.parse(uri)
if is_shell(pane:get_foreground_process_name()) then
-- A shell has been detected. Wezterm can check the file type directly
-- figure out what kind of file we're dealing with
local success, stdout, _ = wezterm.run_child_process {
'file',
'--brief',
'--mime-type',
url.file_path,
}
if success then
if stdout:find 'directory' then
pane:send_text(
wezterm.shell_join_args { 'cd', url.file_path } .. '\r'
)
pane:send_text(wezterm.shell_join_args {
'ls',
'-a',
'-p',
'--group-directories-first',
} .. '\r')
return false
end
if stdout:find 'text' then
if url.fragment then
pane:send_text(wezterm.shell_join_args {
editor,
'+' .. url.fragment,
url.file_path,
} .. '\r')
else
pane:send_text(
wezterm.shell_join_args { editor, url.file_path } .. '\r'
)
end
return false
end
end
else
-- No shell detected, we're probably connected with SSH, use fallback command
local edit_cmd = url.fragment
and editor .. ' +' .. url.fragment .. ' "$_f"'
or editor .. ' "$_f"'
local cmd = '_f="'
.. url.file_path
.. '"; { test -d "$_f" && { cd "$_f" ; ls -a -p --hyperlink --group-directories-first; }; } '
.. '|| { test "$(file --brief --mime-type "$_f" | cut -d/ -f1 || true)" = "text" && '
.. edit_cmd
.. '; }; echo'
pane:send_text(cmd .. '\r')
return false
end
end
-- without a return value, we allow default actions
end)
return config
Optional configuration¶
By default, hyperlinks can be opened with a simple mouse click, without any modifier keys. If you find yourself accidentally clicking hyperlinks, you can customize this behavior.
Check the mouse bindings section for examples on modifying the OpenLinkAtMouseCursor
action.
For example, to require holding CTRL
before opening a hyperlink, use the following configuration:
config.mouse_bindings = {
{
event = { Up = { streak = 1, button = 'Left' } },
mods = 'CTRL',
action = act.OpenLinkAtMouseCursor,
},
-- Disable the 'Down' event of CTRL-Click to avoid weird program behaviors
{
event = { Down = { streak = 1, button = 'Left' } },
mods = 'CTRL',
action = act.Nop,
},
}
Drawbacks¶
This setup sends the text of the commands directly into the active pane which has some drawbacks:
- Does not work inside of any interactive terminal program that is not a shell; the pane must be in a shell prompt
- Requires an empty command prompt before clicking a hyperlink; otherwise, the command may not execute correctly
- When a shell is not detected (see the
is_shell
function), WezTerm falls back to a long shell command, which may clutter the prompt slightly. This happens because WezTerm cannot directly determine a file's MIME type when not connected to a local shell.
If you're using tmux, you'll need to enable the
hyperlinks terminal feature and, depending in your configuration
(see bypass mouse reporting modifiers), add Shift
when clicking an hyperlink.