PROJECTS NOTES HOME

Capitalize each blog post title with elisp

1 The problem

When I found out about denote, I have moved bunch of my notes from elsewhere to .org files (to be used in denote).

The problem is that when I was creating those notes, I did not capitalize the titles. Now when I link to the note, it does not start with a capitalized word and looks

2 Possible solution

After playing with elisp a little bit already when trying to modify denote links, I realized the power of elisp and realized that everything you can do in emacs, you can actually use to your advantage, all the code is there, functions are there, use them! There is already a built in capitalize-word function, so I just need to make a for loop over my files and use that function. Let's do it!

A second attempt for me to try and use elisp to solve the problem!

So I want to:

  • take the denote notes directory,
  • create a loop that loops all the files,
    • then takes their title
    • capitalizes it (IF NOT capitalized already)
    • saves the file
    • goes onto the next one
  • When done, print how many files have been modified

3 The solution

This code does it:

(defun iterate-org-files-and-modify-titles (directory)
  "Iterate over each .org file in DIRECTORY, print its title in uppercase if it starts with a lowercase letter, and save the change."
  (dolist (file (directory-files directory t))
    (when (and (file-regular-p file)
               (string-suffix-p ".org" file))
      (with-temp-buffer
        (insert-file-contents file)
        (goto-char (point-min))
        (if (re-search-forward "^#\\+title: *\\(.*\\)$" nil t)
            (let* ((title (match-string 1))
                   (first-char (substring title 0 1))
                   (rest-title (substring title 1))
                   (new-title (if (and (string-match-p "[a-z]" first-char))
                                  (concat (upcase first-char) rest-title)
                                title)))
              (when (not (string= title new-title))
                (goto-char (match-beginning 1))
                (delete-region (match-beginning 1) (match-end 1))
                (insert new-title)
                ;; Write the entire buffer back to the file
                (write-region (point-min) (point-max) file))
              (message "Title: %s" new-title))
          (message "No title found in file: %s" file))))))

;; Usage example
(iterate-org-files-and-modify-titles "~/GIT/devnotes")

Of course, if it was my first time writing elisp myself, it would have taken me a while to create such piece. ChatGPT has helped a lot. I will now study this to understand the basic principles of how elisp works.

Btw, the code above did work, I modified about ~100 files this way, saved me some time.