151 lines
4.6 KiB
Markdown
151 lines
4.6 KiB
Markdown
---
|
|
title: Opening URLs In Kakoune
|
|
date: 07 Jul 2025
|
|
---
|
|
|
|
[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}"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|