https://en.wikipedia.org/wiki/Emacs
Emacs is an extensible text editor with a long history.
I like using Emacs. Why? Because Emacs is future oriented. It doesn’t lock you into pre-determined habits. You may shape it to your will and intent.
This page is a record of some of the Emacs things I’ve learned.
My learning process works something like this:
This document is the written record of the process of me exploring Emacs, and what learning Emacs can do for me.
I index on date, then topic. That means adding information is easy. “But is that nice to read?” Perhaps. This journal is optimized for writing, not reading. Other documents can be read-optimized.
So … why even keep this public?
For my ease of reading, and for easily sharing with others. The web is accessible :)
’Morning.
Problem statement. I’ve been trying
out Jack Rusher’s recommendation to bind a key to save document and
refresh with Clerk. That lead to really fast refreshes, which I enjoyed.
However, I coudn’t get past my muscle memory of pressing the wrong key.
On my Norwegian keyboard, pressing C-ø
is
really convenient. On a US ANSI layout, that would be C-;
. I’ve bound this to save-buffer
, to avoid the sequence of having to
press ESC SPC f s i
to exit to normal
mode, save and re-enter insert mode (with Evil and Doom Emacs).
Interlude – Writing this down feels really good. I think it will help me avoid getting distracted. Back to Emacs.
Let’s give this a shot!
I had a look at the Emacs Lisp, and it simply saves and messages Clerk to reload!
defun clerk-show ()
(
(interactive)
(save-buffer)let ((filename (buffer-file-name)))
(when filename
(
(cider-interactive-eval"(nextjournal.clerk/show! \"" filename "\")")))))
(concat
"<M-return>") 'clerk-show) (define-key clojure-mode-map (kbd
Source: Clerk README.
See? It saves the buffer, then runs nextjournal.clerk/show!
in the current Cider
REPL.
Let’s extract a function for the save hook.
defun teod/clerk-show ()
(let ((filename (buffer-file-name)))
(when filename
(
(cider-interactive-eval"(nextjournal.clerk/show! \"" filename "\")"))))) (concat
But … how do we run this?
My original ideas:
save-buffer
to also sync to clerk on saveclerk
.But I didn’t know where I should start. I asked on the Clojurians Slack - in the #doom-emacs channel, and got some much appreciated pointers.
In essence:
Signature for add-hook
:
(add-hook HOOK FUNCTION &optional DEPTH LOCAL)
And there’s an after-save-hook
that I
can use.
In my case, I think I can use the following:
0 't) (add-hook after-save-hook teod/clerk-show
Though I admit I don’t really understand when I should use different function reference styles.
; like this?
teod/clerk-show 'teod/clerk-show ; or this?
#'teod/clerk-show ; or this?
Let’s try the simplest.
defun teod/clerk-autoshow ()
(
(interactive)0 't)) (add-hook after-save-hook teod/clerk-show
Nope! That didn’t work. I got the following in the *Messages*
buffer:
clerk-autoshow: Symbol’s value as variable is void: teod/clerk-show apply
Let’s try #'
.
Didn’t work. Bah!
OK, found the manual: https://www.gnu.org/software/emacs/manual/html_node/elisp/Setting-Hooks.html
Example from manual:
'lisp-interaction-mode-hook 'auto-fill-mode) (add-hook
So .. both should be qoted. OK! (interlude: I’m not super comfortable with when to use symbols. Clojure has some different idioms here.)
Let’s try the following:
defun teod/clerk-autoshow ()
(
(interactive)'after-save-hook 'teod/clerk-show 0 't)) (add-hook
Yaaay!
Reflection. Really enjoyable. I was able to achieve what I wanted. I got stuck first, asking got me unstuck. And asking also allowed me to nail a way simpler solution than just pushing ahead.
Future work.
;; ... run the clerk autosave thing ...
on top
of Clerk Clojure filesActionables now.
Also need to eat breakfast and not miss my plans.
Idea:
Writing this at 21:22. Wanting to summarize a bit.
Deciding to dig into Clerk workflow rather than simply working was a decision that didn’t come natrually to me. Is this right? Should I be working on this?
I keep saying to myself that half of what I do should make long term sense, and half should make short term sense. This makes long term sense. And it’s a blocker for getting some reasonable value out short term. But … why does it feel like cheating? Imposter syndrome perhaps, I’m not worthy to make tooling? Not sure.
Anyway - I never came to the “more clerk - startup and files” part.
I’ll probably scrap that work. I tried getting a minor mode up, but when
I set mode:
clerk-auto
in my file, I got just clerk-auto-mode and
not clojure-mode. So back to the drawing board.
In summary:
Now, I’m going to try use clerk a bit. See you later!
Teodor
’Afternoon
Use case: need to use a big screen, want to change font size without an Emacs restart.
First, re-evaluate the form setting the font.
setq doom-font (font-spec :family "monospace" :size 16)) (
Then, reload the font.
M-x doom/reload-font
Done!
Guten Morgen. Dobre utro.
I just tried saving my journal, which is quite big. Emacs froze. Not
sure why. Happens both when I use my own C-ø
binding, and with the built-in SPC f s
.
Vacation is whatever I decide it is.
Value prop: for small, specific use cases, Emacs can be a better choice than a CLI.
Sneak peak — this is what we’re going to create:
TODO gif
When I create something for myself, I usually either make a small CLI or something in Emacs.
CLIs are:
So, what can specific Emacs snippets provide?
Let’s get to it!
I typically write a small interactive Emacs Lisp function that I can
redefine and test out. I don’t attempt to solve my problem at once –
first I want to see what I’m doing. For that, I usually print stuff:
(message "my output")
completing-read
and
read-string
works.read-string
small example:
let ((name (read-string "Please enter your name: ")))
("Hello, " name "!"))) (message (s-concat
read-string
big example:
let ((page-id (read-string "Page id: "))
("Page title: "))
(title (read-string "~/dev/teodorlu/play.teod.eu"))
(default-directory "./play.clj create-page " page-id " :title \"" title "\""))
(shell-command-to-string (s-concat (switch-to-buffer (find-file-noselect page-id)))
What about completing read
? Doing it
the emacsy way, I tried the built-in docs first. That worked out really
well! Google not required.
defun teod/lol ()
(
(interactive);; read-string's argument is the text prompt
let ((name (read-string "What is your name? ")))
("Your name is: " name))
(message (s-concat ;; completing-read's first two arguments are the text prompt and a list of options
let ((dish (completing-read "What is your favourite dish? " '(:pizza :pasta :red-hot-chili-peppers))))
(" likes: " dish))
(message (s-concat name cond ((equal dish ":pizza") (message "I also like pizza!"))
(equal dish ":pasta") (message "Pasta is good."))
(("? Doesn't sound familiar")))))))
(:else (message (s-concat dish ;; run with M-x teod/lol
;;
;; depending on your input, it may print:
;;
;; Your name is: Teodor
;; Teodor likes: :pizza
;; I also like pizza!
I want to compare a string input with my of options. What kind of
comparison should I use? I started to write eq
, then saw both eq
and equal
show
up. I read docs for both and tried them out:
eq :pizza :pizza)
(;; => t
eq "pizza" "pizza")
(;; => nil
equal :pizza :pizza)
(;; => t
equal "pizza" "pizza")
(;; => t
I wanted string equality, so equal
was
the right choice.
… aaaand … this is where I discover that I’m stuck. My ./play.clj create-page
CLI entrypoint currently
only suports setting title. BUT! Perhaps it should not set
title. Perhaps that’s best handled externally.
Other option:
Here’s what a CLI invocation could look like:
$ cat ./clojure-lightbulb-moments/play.edn | bb '(assoc *input* :thing :thong)'
{:title "Clojure Lightbulb Moments", :readiness :forever-incomplete, :author-url "https://teod.eu", :form :rambling, :lang :en, :thing :thong}
I’m stopping at a point of uncertainty. I really want one way to edit tags effectively. Not slap stuff on the CLI on the way in. Yet, I just can’t make up my mind.
Well, I did learn to use completing-read
. That was easy!
let
and let*
This works:
let* ((x "Hello!")
(
(_ (message x))"123")))) (_ (message
This crashes:
let ((x "Hello!")
(
(_ (message x))"123")))) (_ (message
Why?
let | scope | example |
---|---|---|
let |
in | (let ((x 1) ) (message x)) |
let* |
in or down | (let* ((x 1) (y (* x 10)) (message y)) |
I really don’t like UPS.
I’m using Doom Emacs. I enjoy using Doom Emacs. So far, I’ve leaned heavily into workflows — how can I get this done? I haven’t put too much effort into structure. That’s about to change.
Why? This page. play.teod.eu. I’ve created quite a few Emacs lisp commands that play nice with the play.teod.eu structure. Up until now, all of these have gone into my Doom Emacs config.el.
Three things
clojure-mode
, perhaps.Org-mode is amazing.
From anywhere, M-x org-store-link
. That
link goes into the org-stored-links
variable. Next time a link is added with org-insert-link
, you can see all the links
you’ve stored.
M-x battery |
Returns / shows battery info |
M-x battery-display-mode |
Puts a nice battery indicator in the modeline |
I think <f8> is the way to bind stuff.
LOL I already have <f8>
bound to
open an org-roam node, haha NOPE, that inserts a link to the node.
Remove.
Wtf, now it works. I have a wrong model for the map!
macro somehow. Moving stuff up fixed my
problems.
“Again?” Yes. Again. I’ve learned this before.
In vanilla Emacs:
M-x magit-show-refs
k
to deleteIn Doom Emacs:
M-x magit-show-refs
x
to delete.Beware Dired differences!
In Dired, a single d
press marks a file
for deletion. In Magit, d
sends you off to
magit-diff
. So it’s different! But “select
lines then press x” does the same, both for Doom Emacs Dired and Doom
Emacs Magit.
Repo: https://github.com/clojure-emacs/parseedn
"{:x 123}") (parseedn-read-str