meain/blog

Jul 26, 2021 . 3 min

Edit everything in Emacs

Hello everyone, another installment of me going over my setup. This is one of those things which I don't use all that much but wheneve I do, I really am glad that this is a thing.

Let me first explain the feature that I am talking about. I have my editor(Emacs) setup exactly how I want it to be(for the most part). Here is a sample workflow which I am trying to fix. Let's say I am responding to someone on a Github issue. I start off with typing, half way through I want to write some code and now I could technically write it in the Github UI itself, but that is gonna be a PITA. Why would I do that when I have such a really good setup for me to write stuff in?

Here is what I doing before. I would copy past whatever I wrote to a scratch buffer in Emacs, set the major-mode to markdown-mode and start editing in Emacs. Once I have everything, I copy the entire buffer to clipboard, go hunt for my browser from all the windows, find the input box, focus on it and paste it there.

With that out of the way, let me show you what I did.

I pretty much converted what I would have done direcly into code. The first part is for me to, when I press some key combo, copy text from the current input box and create a new buffer in Emacs with my specific settings and paste the text we have so far into there. This was done in two pieces. The OS level interaction was done using Hammerspoon(I use macOS). The code is something like below.

local emacs = hs.application.find(editor)
quick_edit_app = hs.window.focusedWindow()
hs.eventtap.keyStroke({"cmd"}, "a")
hs.eventtap.keyStroke({"cmd"}, "c")
emacs:activate()
hs.eventtap.keyStroke({"alt", "shift"}, ";")
hs.eventtap.keyStrokes("(meain/quick-edit)")
hs.eventtap.keyStroke({}, "return")

In the first part all I am doing is backing up the name of the "app" that I am currently editing, then do a cmda and cmdc to select eveything and copy it to clipboard. After that I focus the Emacs window and using metashift; I call the Emacs function (meain/quick-edit). Here is what that does.

(defun meain/quick-edit ()
"Util function for use with hammerspoon quick edit functionality."
(interactive)
(let ((qed-buffer-name (concatenate 'string
"qed-"
(substring (uuid-string)
0
4))))
(generate-new-buffer qed-buffer-name)
(switch-to-buffer qed-buffer-name)
(evil-paste-after 1)
(gfm-mode)))

In here, all I do is create a buffer with the name qed-<uuid>, switch to it, paste the contents, and enable gfm-mode (gfm = Github Flavoured Markdown). Well, I told you that I literlly just converted what I was doing manually to this.

OK, now to go editing in the buffer...

Edit over, let's get this thing back into the Github issue.

hs.eventtap.keyStroke({"alt", "shift"}, ";")
hs.eventtap.keyStrokes("(meain/quick-edit-end)")
hs.eventtap.keyStroke({}, "return")
quick_edit_app:focus()
hs.eventtap.keyStroke({"cmd"}, "a")
hs.eventtap.keyStroke({"cmd"}, "v")

OK, again, I call an elisp function meain/quick-edit-end which looks something like this:

(defun meain/quick-edit-end ()
"Util function to be executed on qed completion."
(interactive)
(mark-whole-buffer)
(call-interactively 'kill-ring-save)
(kill-buffer-and-window))

All this does is to copy stuff into clipboard and kill the buffer. Once I have this, I just change focus to quick_edit_app which I had previously saved as the app from which I copied. Once I focus on it, we automatically have our focus on the input box in that page. So all I have to do is cmda and cmdv to change what we have in that input field to what we wrote in Emacs.

And there you go, just another simple hack to make life better. Btw, you can probably even combine this with popup frames and writeroom-mode if that is your jam.

I have added some more things to it. Here is the full code. Elisp part and the hammerspoon part.

← Home