Yi Tang Data Science and Emacs

Jekyll in Emacs - Update Blog Post Title and Date

Table of Contents

  1. Emacs Lisp Time
  2. Code

I’m the type of writer who writes first and comes up with the title later. The title in the end is usually rather different to what I started with. To change the title is straightforward - update the title and date fields in the front matter.

However, doing so leads to discrepancies between the title and date fields in front matter and the filename. In Jekyll, the filename consists of the original date and title when the post is first created.

This can be confusing sometimes in finding the file when I want to update a post. I have to rely on grep/ack to find the right files. A little bit of inefficiency is fine.

Recently, I realised that readers sometimes can be confused as well because the URL apparently also depends on the filename.

For example, I have my previous post in a file named 2022-12-08-trx-3970x.md. It indicates that I started writing it on 08 Dec with the initial title “trx 3970x”. A couple of days later on 13 Dec, I published the post with the title “How Much Does Threadripper 3970x Help in Training LightGBM Models?”.

The URL is however yitang.uk/2022/12/13/trx-3970x. It has the correct updated publish date, but the title is still the old one. This is just how Jekyll works.

Anyways, the correct URL should be

http://yitang.uk/2023/12/13/how-much-does-threadripper-3970x-help-in-training-lightgbm-models/

From that point, I decided to write a bit of Emacs Lisp code to help the readers.

Emacs Lisp Time

The core functionality is updating the filename and front matter to have the same publish date and title. It can breakdown into three parts:

  1. when called, it promotes a new title. The publish date is fixed to whenever the function is called.

  2. It renames the current blog post file with the new date and title. It also updates the title and date fields in the front matter accordingly.

  3. It deletes the old file, closes the related buffer, and opens the new file so I can continue to work on it.

My Emacs Lisp coding skill is rusty but I managed to get it working in less than 2 hours. I won’t say it looks beautiful, but it does the job!

I spent a bit of time debugging, it turns out the (org-show-all) needs to be called first to flatten the org file, otherwise, editing with some parts of the content hidden can lead to unexpected results.

I always found working with the filename/directory in vanilla Emacs Lisp cumbersome, I wonder if is there any modern lisp library with a better API, something like Python’s pathlib module?

Code

Here are the main functions in case someone needs something similar. They are extracted from my Emacs configuration.

 
 (defun yt/jekyll-update-post-name ()
  "it update the post filename with a new title and today's date.

it also update the font matter."
  (interactive)
  (let* ((title (read-string "new title: "))
         (ext (file-name-extension (buffer-file-name)))  ;; as of now, the ext is always .org.

         ;; the new filename is in the format of {date}-{new-title}.org
         (filename (concat
                    (format-time-string "%Y-%m-%d-")
                    (file-name-with-extension (jekyll-make-slug title) ext)))

         ;; normalise the filename. 
         (filename (expand-file-name filename))

         ;; keep the current point which we will go back to after editing.
         (old-point (point))
         )
    (rename-file (buffer-file-name) filename) ;; update the filename
    (kill-buffer nil)  ;; kill the current buffer, i.e. the old file.
    (find-file filename)  ;; open the new file.
    (set-window-point (selected-window) old-point)  ;; set the cursor to where i was in the old file.

    ;; udpate title field. 
    ;; note jekyll-yaml-escape is called to ensure the title field is yaml friendly.
    (yt/jekyll-update-frontmatter--title (jekyll-yaml-escape title))    
    )

  )

(defun yt/jekyll-update-frontmatter--title (title)
  "Update the title field in the front matter.

title case is used. 
"
  (let* ((old-point (point)))

    ;; ensure expand all the code/headers/drawers before editing.
    (org-show-all)

    ;; go to the first occurence of 'title:'.
    (goto-char (point-min))
    (search-forward "title: ")

    ;; update the title field with the new title.
    (beginning-of-line)
    (kill-line)
    (insert (format "title: %s" title))

    ;; ensure the title is in title case
    (xah-title-case-region-or-line (+ (line-beginning-position) 7) (line-end-position))

    ;; save and reset cursor back to where it started.
    (save-buffer)    
    (goto-char old-point)
    ))
 
If you have any questions or comments, please post them below. If you liked this post, you can share it with your followers or follow me on Twitter!
comments powered by Disqus