Note: From Python to Emacs Lisp

June 28, 2024
emacs lisp

Ever feel like your brilliant blog ideas evaporate faster than spilled coffee on a hot keyboard?

If you’re like me, the lower the barrier to creating a note for your blog, the better. I started with a Python script to streamline my blog writing process, but it wasn’t quite hitting the mark. It required me to open the terminal, run the command, and then open the file in Emacs. The experience felt clunky.

Then it hit me: I’m using Emacs, why the heck am I doing this in Python.

I decided to port my Python script to Emacs Lisp, and not only was it easier to write, but the overall experience also improved significantly. It prompts you with a few questions and opens a buffer for you to start writing.

For reference, here’s the code that I ended up writing.

(defun pet/write-blog ()
  "Create a new blog note or post."
  (let* ((is-note (y-or-n-p "Do you want to write a note? "))
         (title (read-string "What is the title? "))
         (tags (read-string "Do you want to tag it? "))
         (filename (concat (pet--slugify title) ".md"))
         (dir-type (if is-note "note" "post"))
         (path (expand-file-name
                 (concat "content/" dir-type "s") pet/blog-directory)))
         (date (format-time-string "%Y-%m-%d"))
          `(("${title}" . ,title)
            ("${date}" . ,date)
            ("${type}" . ,dir-type)
            ("${taxonomies}" . ,(format (if is-note "tags: [%s]" "categories: [%s]") tags))
            ("${tags}" . ,tags))))
    (find-file path)
    (insert pet/blog-frontmatter) ;; insert template for frontmatter
    (goto-char (point-min)) ;; beginning of buffer, so we can replace the title
    (pet--replace-strings replacements)
    (goto-char (point-max))))

(defun pet--slugify (s)
  "Create a slug from string S."
  (replace-regexp-in-string "^-\\|-$" ""
     "[^a-z0-9]+" "-" (downcase s))))

(defun pet--replace-strings (replacement-list)
  "Replace multiple strings in the current buffer.

REPLACEMENT-LIST is an alist where each element is a cons cell (SEARCH
. REPLACE).  For each pair, all occurrences of SEARCH are replaced with
    (dolist (rep replacement-list)
      (goto-char (point-min))
      (while (search-forward (car rep) nil t)
        (replace-match (cdr rep) t t)))))

Now it’s just one command pet/write-blog to start writing a new note, just like the one you are reading right now.

Sync Fastmail with isync Embrace the Challenge of Learning: Insights from Andrej Karpathy