Building a cozy Writing Environment in Emacs
Tools like Emacs are rare gems in my tool-kit. Actually, I cannot think of anything else like Emacs. Maybe my swiss army knife or multi-tool comes close, but even those tools only capture the multi-faceted capability of Emacs, and not it’s extensible nature.
Emacs is called a self documenting, extensible editor, which means that if I have an idea for a function that it currently doesn’t have, it becomes trivial to add it to Emacs. Since I first started using this editor in 2019, I have extended it with at least a dozen functions1, that make this tool uniquely mine, suited to my way of thinking.
A recent example is a function I designed to help me with blog posts.
I maintain a few different blogs. One is for the non-profit foundation that I manage, and another for the consulting firm that I run. There is also the one that you are currently reading.
I write the posts for each of these blogs in Emacs, in Markdown format and store them as individual files, in separate folders, (one for each blog) on my computer. When I want to create a new post, I open the appropriate folder and create a new markdown file, name it and get to work.
This process in itself is pretty streamlined, and it takes me only a few seconds to start writing. And yet, I couldn’t help but wonder if I could make it even more friction-less. For instance, navigating to the folder and creating a file, naming it… each of these steps requires me to pay attention. This means that when I have an idea and want to start writing immediately, there is a little bit of context switching. Sometimes this switch erases the seed of the idea from my goldfish of a brain.
Could I use not create a function that would help me create a file in the right folder in just a couple of steps. Since I use Emacs for writing, the answer to these kinds of questions is almost always yes.
I am not a programmer, so I detailed to ChatGPT, the function that I need. It got it right on the first go.
This new function asks me to pick the sub-directory that I need from a list and asks me for a title. That’s it. It creates a new markdown file in the right sub-directory with a header and the cursor poised right under it, ready to type all in under a couple of seconds.
It gets even better.
A few months ago I read a blog post about a software for writers called i.a writer, which had a pretty slick interface. The design of the interface promotes focused writing, which means there are no distracting interface elements around the writing area. It also has other niceties, such as focusing on the sentence being typed. while graying out everything else, a nice typeface designed by their own team, and so on. It seems to be a Mac only software.
Could I not replicate this in Emacs? Of course I could and I did.
I merged the earlier function for markdown file creation with the i.a writer like interface, so that as soon as a new markdown file is created, it enters this beautiful writing environment. As soon as I complete writing, a keyboard command restores Emacs to it’s normal state.
The light variant of my zen-like writing mode
… and it’s dark sibling
I love how easy it is to mould Emacs to my needs. There are a lot more examples I could offer, so here is just one more to illustrate how wonderful this software is.
Since most of my writing involves creating blog posts and social media posts for my work (and sometimes here, for myself) I tend to track each of these posts as tasks. When the writing is completed and published, I mark the task as done. This is useful because I have a log of all the posts I have written or need to write in one place. In my task manager. So, there are two steps here.
- Create a task for the post, in my task management file (an org-mode file)
- Write the post in its own markdown file
What generally happens in practice is that I either start writing and forget to log it in my task manager or create a task and never get around to writing the post. What if I could combine these steps. With a single capture command, I should be able to:
- Capture a task that goes into my task list.
- Ensure that the task is clocked in for time tracking
- have a markdown file created automatically in the right folder
- with the same filename as the task
- automatically open the file so I can start writing.
- As a bonus, the task should have a link to the markdown file as well.
It took me about 30 mins of working with ChatGPT to make this happen.
The Elisp Code
And below, the code for the markdown file creation and writing environment.
The function to create a new Markdown File
I have a bunch of sub-folders for my markdown files, one for each blog, with a root folder called Blogs. Customise the path to your folder setup.
;; ---------------------- New Markdown file after todo starts here --------------------
;; I had the problem where I would write something without creating a task first
;; or create a task, but then don't write because I have to create a markdown file first
;; and then the two would not be linked
;; I got chatGPT to write a custom command that does all this
(defun my-writing-idea-capture ()
"Custom command to capture a blog idea and create a corresponding markdown file."
(interactive)
(let* ((title (read-string "Title of blog idea: "))
(tag (completing-read "Pick a tag: " '("tag1" "tag2") nil t))
(due-date (org-read-date nil t nil "Due Date: "))
(org-file "~/path/to/task/list/file/writing.org")
(timestamp (format-time-string "[%Y-%m-%d %a %H:%M]"))
;; Markdown folder prompt
(root-dir "~/path/to/markdown/folder/")
(folders (directory-files root-dir nil "^[^.]")) ;; List folders
(chosen-folder (completing-read "Choose a folder: " folders nil t))
(date (format-time-string "%y%m%d"))
(sanitized-title (replace-regexp-in-string "[^a-zA-Z0-9_-]" "_" title))
(md-filename (concat chosen-folder "_" date "_" sanitized-title ".md"))
(md-filepath (expand-file-name md-filename (concat root-dir chosen-folder))) ;; absolute path
;; Prettified Org link
(org-heading (format "** TODO POST | %s :%s:\n:PROPERTIES:\n:OUTCOME:\n:DUEBY: %s\n:CREATED: %s\n:FILELINK: [[file:%s][%s]]\n:END:\n\n- *Notes and Links*\n - \n\n- *Next Actions* [/]\n - [ ] \n\n- *NOTED* %s\n"
title tag
(format-time-string (org-time-stamp-format nil t due-date))
timestamp
md-filepath title
timestamp)))
;; --- Insert into org file under "a level headline - Writing in Progress" ---
(find-file org-file)
(goto-char (point-min))
(unless (re-search-forward "^\\*+ Writing in Progress" nil t)
(goto-char (point-max))
(insert "* Writing in Progress\n"))
(org-end-of-subtree t t)
(open-line 2)
(forward-line)
(insert org-heading)
;; --- Clock in to the newly inserted TODO ---
(org-back-to-heading t)
(org-clock-in)
;; --- Create markdown file ---
(find-file md-filepath)
(insert (concat "# " title "\n\n"))
(save-buffer)
(goto-char (point-max))
(when (fboundp 'write-md-focus-in)
(write-md-focus-in))))
(global-set-key (kbd "C-c w") 'my-writing-idea-capture)
;; ---------------------- New Markdown file after todo ends here --------------------
The following section configures the writing environment. It depends on packages like Olivetti-mode, Focus-mode, Writegood-mode and Centered-cursor mode. I also created a Hydra (not included here) to make it easy to focus in and out because I have also adapted this code for other types of files – but you can just uncomment the key bindings at the end of this code block, and replace them with your own.
;; ----------- Markdown Mode Focus In ---------------------------
;; Adapted from Howard's code for Narrowing in and Out of a Heading, without distractions
;; Added on Friday 24 May 2024 05:08:54 PM IST
;; source - https://gitlab.com/howardabrams/spacemacs.d/-/blob/master/layers/ha-org/packages.el#L294
(defun write-md-focus-in ()
"Narrow down focus to a sub tree Call after creating an org-mode heading to focus on.Call 'focus-out' to reset."
(interactive)
(delete-other-windows) ;; Get rid of other windows
(load-theme 'doom-plain-dark t)
(olivetti-mode) ;; Centre the text
(olivetti-set-width 80)
(writegood-mode 1)
(centered-cursor-mode 1)
(focus-mode) ;; Activate focus mode
(variable-pitch-mode 1) ;; Activiate variable pitch font
;; (text-scale-set 2) ;; Text is now readable by others
;;(line-spacing 0.8)
(setq line-spacing 0.8)
(save-place-mode 1)
(goto-char (point-max))
(message "When finished taking your notes, run focus-out.")
)
(defun write-md-focus-out ()
"Widen out my focus and attempt to 'undo' the effects of 'focus-in'."
(interactive)
(focus-mode 0) ;; Remove focus mode
(disable-theme 'doom-plain-dark)
(olivetti-mode 0) ;; Remove centered text
(writegood-mode 0)
(centered-cursor-mode 0)
(variable-pitch-mode 0)
(text-scale-set 0) ;; Reset the font size increase
;;(line-spacing nil)
(setq line-spacing nil))
;; Removed keybindings because I've added these into a Hydra
;; (define-key global-map (kbd "") #'plan-focus-in)
;; (define-key global-map (kbd "") #'plan-focus-out)
Notes
All these things help me with my words, but then I wanted a prettier interface as well. I installed Olivetti-mode which centres the text nicely, and focus-mode which keeps the sentence i’m writing in focus and dims everything else.
Besides these niceties, Emacs already has some wonderful features that help writers, like transposing letters, words, sentences or paragraphs—meaning, I can swap these elements around. So if I mistakenly type goerge, I can make it george without having to retype the word—or the wonderful expand-region which expands a selection, going from a word to sentence to paragraph to entire page with each subsequent click. Very handy! There is also Avy-jump, which lets you jump to any section of the screen with a click, just like Jeff Raskin’s Leap Technology. See this demo video from 1987.
There are so many more sweet features that writers would love. A tiny set of examples are the mandatory word-counts, the ability to jump to the beginning or end of sentences, being able to collapse or expand parts of your text (org-mode) or hoist specific bits so you can focus your writing in sections. See this demo of hoisting by Dave Winer, the outliner OG.
A hat tip to Jay Dixit whose talk inspired me to try Emacs. See the talk here.
-
I added dictionary lookup, and not just any old dictionary mind you, but the 1913 Version of Webster’s dictionary. Read James Somers’ lovely post about it. I have added Merriam Webster Thesaurus, and Language Tool for good measure, and Writegood mode to keep me honest. Besides highlighting weasel-words, Writegood mode provides Flesch-Kincaid scoring to help me assess the readability of my prose. I also have the command-line tool Proselint installed, which is like a programmer’s linter but for prose. I wrapped all of these into a nice Hydra, so I can call any of these functions with a single click. ↩︎