150 lines
4.7 KiB
Markdown
150 lines
4.7 KiB
Markdown
---
|
|
title: Opening URLs In Kakoune
|
|
date: 07 Jul 2025
|
|
description: |
|
|
A brief post about how I implemented URL opening in Kakoune.
|
|
---
|
|
|
|
[Kakoune]: https://kakoune.org
|
|
|
|
One feature built into [Helix](https://docs.helix-editor.com/) is the ability to
|
|
super-easily open URLs in your browser. All you need to do is move your cursor
|
|
over a URL, and press `gf`. This is _very_ helpful when reading documentation,
|
|
emails, et cetera...
|
|
|
|
So, how can we do this in [Kakoune]?
|
|
|
|
[TOC]
|
|
|
|
## Goal
|
|
|
|
Here's our target user experience:
|
|
|
|
1. The user moves their cursor over a URL.
|
|
2. The user presses a key.
|
|
3. The URL is selected, validated, and opened.
|
|
4. The user is notified of any errors or success.
|
|
|
|
Like any bit of custom functionality, the best way to go is to define a
|
|
**command**. This gives us re-usability, and greater flexibility (since `map`
|
|
only allows mapping to _key sequences_, not any arbitrary string of commands).
|
|
|
|
## Selecting
|
|
|
|
First, we need to find the URL under the cursor. Of course, if there _isn't_
|
|
one, we should fail at this step. The standard way to do this in Kakoune is by
|
|
using a regular expression to filter the selection. If there's no matches at
|
|
all, that's something we can `try/catch`.
|
|
|
|
We won't get far without a URL regex. When I'm including long regexes in
|
|
`#!kak execute-keys` commands, I like saving them to a register, like so:
|
|
|
|
```kak
|
|
set-register b 'https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)'
|
|
```
|
|
|
|
Then, we just need to check if this regex exists around the user's cursor. Since
|
|
URLs don't have spaces, we can select the surrounding `WORD`, and filter from
|
|
there:
|
|
|
|
```kak
|
|
execute-keys -draft '<a-a><a-w>s<c-r>b<ret>"ay'
|
|
```
|
|
|
|
The above command selects the outer `WORD`, then runs the select command with
|
|
our regex (stored in register `b`). Once the URL has been selected, it's copied
|
|
to the `a` register. If there is no URL, the command fails.
|
|
|
|
## Cleaning
|
|
|
|
It's usually better to allow regex to be a bit greedier, and then filter
|
|
unwanted characters out of the result. In our case, some trailing punctuation is
|
|
included in the capture -- we can use `sed` in a shell block to strip them.
|
|
|
|
```sh
|
|
clean_url="$(echo "$kak_reg_a" | sed 's/[][(){}.,;!?]*$//')"
|
|
```
|
|
|
|
## Opening
|
|
|
|
Our next step is to figure out how we'd open a URL from the shell -- after all,
|
|
anything we implement in Kakoune ends up running shell commands! If you have
|
|
`xdg-open` available (part of the `xdg-utils` package on Arch Linux), this is
|
|
simple:
|
|
|
|
```sh
|
|
xdg-open https://ficd.sh
|
|
```
|
|
|
|
XDG handles figuring out the correct application to forward the URL to. If your
|
|
environment is set up properly, this is probably your default browser. If you
|
|
don't have (or don't want to use) `xdg-open`, most browsers let you open URLs
|
|
from the command line directly:
|
|
|
|
```sh
|
|
firefox https://ficd.sh
|
|
```
|
|
|
|
Depending on the browser -- if you already have an open session, the link will
|
|
be opened as a new tab in an existing window. Nice!
|
|
|
|
Let's define an **option** which contains the shell command we'll use to open
|
|
the link:
|
|
|
|
```kak
|
|
# %s gets replaced with the URL
|
|
declare-option str url_open_cmd 'xdg-open %s'
|
|
```
|
|
|
|
Then, in our shell block, we can substitute the cleaned URL into the `%s` format
|
|
specifier, and evaluate the resulting string as a command:
|
|
|
|
```sh
|
|
if eval "$(printf "$kak_opt_url_open_cmd" "$clean_url")" >/dev/null 2>&1; then
|
|
echo "info -title 'URL Opened' '$clean_url'"
|
|
else
|
|
echo "fail 'url_open_cmd failed!'"
|
|
fi
|
|
```
|
|
|
|
## Completed Plugin
|
|
|
|
That's it, that's all! We now have a command called `url-open`, which we can
|
|
easily bind to something like `gu`:
|
|
|
|
```kak
|
|
map global goto u '<esc>: url-open<ret>'
|
|
```
|
|
|
|
Here's the complete plugin code:
|
|
|
|
```kak
|
|
declare-option -docstring %{
|
|
Command for opening URLs.
|
|
} str url_open_cmd 'xdg-open %s'
|
|
define-command -docstring %{
|
|
Open the URL the cursor is on with url_open_cmd.
|
|
} url-open %{
|
|
evaluate-commands -save-regs 'ab' %{
|
|
set-register b 'https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)'
|
|
try %{
|
|
try %{
|
|
execute-keys -draft '<a-a><a-w>s<c-r>b<ret>"ay'
|
|
} catch %{
|
|
fail 'No URL found!'
|
|
}
|
|
evaluate-commands %sh{
|
|
# strip trailing punctuation
|
|
clean_url="$(echo "$kak_reg_a" | sed 's/[][(){}.,;!?]*$//')"
|
|
if eval "$(printf "$kak_opt_url_open_cmd" "$clean_url")" >/dev/null 2>&1; then
|
|
echo "info -title 'URL Opened' '$clean_url'"
|
|
else
|
|
echo "fail 'url_open_cmd failed!'"
|
|
fi
|
|
}
|
|
} catch %{
|
|
info -title 'URL Open' "Couldn't open URL: %val{error}"
|
|
}
|
|
}
|
|
}
|
|
```
|