Slugifying this blog's headings

A recurring concern I have with this website (or blog) is to make sure links don’t die. One of the measures I take is to never change a URL once it is published. I mean, changing the heading and the content is possible, but not the URL.

How is it done? As previously mentioned1, 2, this website is built with a mix of Org Mode and Hugo. In this case, the Hugo feature I’m using is the slug attribute on the page’s front matter. This basically overrides the file path that would be on the URL with a string of your choice.

Let’s imagine you were writing plain Markdown files and thought a title was not adequate anymore, but the content was still sound. This how this article would look like:

title = "Slugifying this blog's headings"
slug = "slugifying-this-blog-s-headings"
date = 2023-05-17

<!-- content -->

Here, the slug would keep the URL the same even if the heading changed. In my case, this is how this article’s portion looks like in Org:

** Slugifying this blog's headings                             :emacs:shell:
:EXPORT_DATE: 2023-05-17
:EXPORT_FILE_NAME: slugifying-this-blog-s-headings
:EXPORT_HUGO_CUSTOM_FRONT_MATTER: :slug slugifying-this-blog-s-headings

# content

Okay, but how to create the slugs? This can’t be a manual task3 as it is too error prone, I can make typos easily on bigger headings! Then I thought: “I’m already inside GNU Emacs, why not use it?”. The idea is really simple, I select the heading text and run a function me/slugify that will copy the slugified version on my clipboard.

🗒️ I might write a function that populates the heading plus properties automatically for me… in the future

The first step I took was to search on the internet some bash script that I could just glue (😜) together with Emacs Lisp. I ended up finding this gist comment that had a really straightforward command:

# note that the `echo` and last `tr` was added by me
echo "Slugifying this blog's headings" \
| iconv -t ascii//TRANSLIT  \
| sed -E -e 's/[^[:alnum:]]+/-/g' -e 's/^-+|-+$//g' \
| tr '[:upper:]' '[:lower:]' \
| tr -d '\n'

This is exactly what I was looking for, but how to make this an Emacs Lisp function I can call? Note that I know barely nothing about Emacs Lisp, the only thing I know here is that I want an interactive command. In this sense, we need to declare a function with these properties/steps:

After doing some C-h f and online research, I found out the functions I needed:

(defun gluer/slugify ()
  ;; only run this if there's an active region
  (when (use-region-p)
    ;; `let*` bounds the variables sequentially, useful to use a
    ;; previously declared variable
    (let* (
	   ;; stores the selected region on the `str` variable
	   (str (buffer-substring (region-beginning) (region-end)))
	   ;; interpolates the selected region on the `echo` command
	   ;; that will pipe it to the rest of the chain
	   (cmd (format "echo %s | iconv -t ascii//TRANSLIT | sed -E -e 's/[^[:alnum:]]+/-/g' -e 's/^-+|-+$//g' | tr '[:upper:]' '[:lower:]' | tr -d '\n'" str))
	   ;; stores the command output on the `slug` variable
	   (slug (shell-command-to-string cmd)))
      ;; copies the slug to the clipboard
      (kill-new slug)
      ;; diplays the slug on the bottom of the screen
      (message "copied '%s' to the clipboard" slug))))

Selecting the region and running M-x gluer/slugify gets us a nice slugified string on our clipboard! But it’s not over yet!

The funny part was that I didn’t do the most obvious thing: search for a “slug” function on GNU Emacs itself! Well, guess what? ox-hugo has a function called org-hugo-slug (!!!) that does exactly what I want…

(org-hugo-slug STR &optional ALLOW-DOUBLE-HYPHENS)

Convert string STR to a ‘slug’ and return that string.

Let’s give it a try and adapt our previous one:

(defun gluer/slugify ()
  (when (use-region-p)
    (let* ((str (buffer-substring (region-beginning) (region-end)))
	   (slug (org-hugo-slug str)))
      (kill-new slug)
      (message "copied '%s' to the clipboard" slug))))

This is way better than what I previously had. It’s cleaner, doesn’t add weird shell strings and uses the same mechanism behind the website generation.

Lesson learned: use the describe-* functions (in this case, describe-function) on GNU Emacs, they are there for a reason!

  1. How this website is built - Nix and ox-hugo ↩︎

  2. Why I use Nix and make(1) to develop ↩︎

  3. Although it has been a manual task for as long as this article. 😁 ↩︎

Articles from blogs I follow around the net

The four tenets of SOA revisited

Twenty years after. In the January 2004 issue of MSDN Magazine you can find an article by Don Box titled A Guide to Developing and Running Connected Systems with Indigo. Buried within the (now dated) discussion of the technology…

via ploeh blog March 4, 2024

Building a demo of the Bleichenbacher RSA attack in Rust

Recently while reading Real-World Cryptography, I got nerd sniped1 by the mention of Bleichenbacher's attack on RSA. This is cool, how does it work? I had to understand, and to understand something, I usually have to build it. Well, friends, that is what…

via blog March 4, 2024

How to unbreak Dolphin on SteamOS after the QT6 update

A recent update to Dolphin made it switch to QT6. This makes it crash with this error or something like it: dolphin-emu: symbol lookup error: dolphin-emu: undefined symbol: _Zls6QDebugRK11QDockWidget, version Qt_6 This is fix…

via Xe Iaso's blog March 3, 2024

Generated by openring