From c21bfc5fe6e660c203aff108b839c66602805889 Mon Sep 17 00:00:00 2001 From: Daniel Fichtinger Date: Mon, 7 Jul 2025 19:23:32 -0400 Subject: [PATCH] wrote opening url in kak post --- content/blog/kak/url-open.md | 151 +++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 content/blog/kak/url-open.md diff --git a/content/blog/kak/url-open.md b/content/blog/kak/url-open.md new file mode 100644 index 0000000..d5435c1 --- /dev/null +++ b/content/blog/kak/url-open.md @@ -0,0 +1,151 @@ +--- +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 'sb"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.ca +``` + +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.ca +``` + +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 ': url-open' +``` + +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 'sb"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}" + } + } +} +``` +