Emacs is not just an editor. It's a fully programmable shell through which you can control almost all your computational needs, with a focus on dealing with textual data. With this mindset it's not hard to see how it can be used for a whole range of scripting needs.

Recently I migrated my blog from hugo to haunt, as documented in a previous post. Since both haunt and hugo use markdown to generate the posts, migrating was just to transform the TOML front-matter used in hugo posts in the YAML one needed by haunt.

Quite boring stuff to do for around twenty files. This was easily solved with some ugly Emacs Lisp in the scratch buffer.

(require 's)

(defun parse-hugo-post (filename)
  "Parse the data from a hugo post into an alist of title, data
and tags metadata with their values and content with the markdown
part. "
    (insert-file-contents filename)
    (search-forward "+++" nil nil 2)
    (let ((content (buffer-substring (point) (point-max)))
      (title (progn (search-backward "title = ")
            (s-chop-suffix "\""
                       (s-chop-prefix "title = \""
                              (s-chomp (thing-at-point 'line))))))
      (date (progn (search-forward "date = ")
               (let* ((date-string (s-chop-prefix "date = "
                              (s-chomp (thing-at-point 'line))))
                  (date-string-split (s-split "T" date-string))
                  (len (length (cadr date-string-split))))
             (concat (car date-string-split) " "
                 (substring (cadr date-string-split) 0 (- len 9))))))
      (tags (progn (search-forward "tags = ")
               (s-join ", " (mapcar (lambda (s)
                          (s-chop-suffix "\"]"
                                 (s-chop-prefix "[\"" s)))
                        (s-split ","
                             (s-chop-prefix "tags = "
                                    (s-chomp (thing-at-point 'line)))))))))
      `(("title" . ,title)
        ("date" . ,date)
        ("tags" . ,tags)
        ("content" . ,content)))))

(defun write-haunt-post (filename alist)
  "Create a file that is populated by the title, date, tags and
content data that have been obtained from a hugo post."
    (insert (concat "title: " (assoc-default "title" alist) "\n"))
    (insert (concat "date: " (assoc-default "date" alist) "\n"))
    (insert (concat "tags: " (assoc-default "tags" alist) "\n"))
    (insert "---")
    (insert (assoc-default "content" alist))
    (write-file filename)))

(defun migrate-posts (input-dir output-dir)
  (let ((files (directory-files input-dir)))
    (loop for file in files
      do (write-haunt-post (concat output-dir)
                   (parse-hugo-post (concat input-dir file))))))

We just parse the appropriate data into an alist and then construct the new file. So in about 10 minutes we have a not pretty but practical script to get the job done.

This kind of low-entry programming is what really kept me into Emacs all these years, together with all the wonderful infrastructure to juggle text around. Another more involved example of this usage of Emacs was recently presented in the Emacsconf by Howard Adams. It is a very interesting use-case that shows the power of Emacs Lisp and of the Emacs' ecosystem.