<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.5">Jekyll</generator><link href="https://camsaul.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://camsaul.com/" rel="alternate" type="text/html" /><updated>2025-02-04T20:24:18+00:00</updated><id>https://camsaul.com/feed.xml</id><title type="html">camsaul.com</title><subtitle>What you want, when you want it</subtitle><entry><title type="html">My Experience Using Prolog in 2024 to Solve Advent of Code</title><link href="https://camsaul.com/prolog/advent-of-code/2025/02/04/advent-of-code-prolog.html" rel="alternate" type="text/html" title="My Experience Using Prolog in 2024 to Solve Advent of Code" /><published>2025-02-04T18:37:00+00:00</published><updated>2025-02-04T18:37:00+00:00</updated><id>https://camsaul.com/prolog/advent-of-code/2025/02/04/advent-of-code-prolog</id><content type="html" xml:base="https://camsaul.com/prolog/advent-of-code/2025/02/04/advent-of-code-prolog.html">&lt;p&gt;This was my first year doing &lt;a href=&quot;https://adventofcode.com/2024&quot;&gt;Advent of Code&lt;/a&gt;. Previous years I had other stuff I wanted
to do with my free time besides sit at a computer even more than I already do… after seeing some of my coworkers post
epic solutions I got FOMO and joined the challenge.&lt;/p&gt;

&lt;p&gt;I decided to try to solve things in Prolog to “kill two pigs with one bird” as the Gen Zs say. Green pig #1 was learning
Prolog at more than just a really superficial level… I’m a big fan of logic programming languages and Prolog has been
on my list for a while. Green pig #2 was I thought it would be a good fit for solving some of these challenges since I’d
(in theory) mostly be encoding the rules and letting the computer do the work for me. (Foreshadowing: it was not really
this simple in practice.) A bonus pig was flexing on everyone with 1337 skillz which I think I did actually achieve a
little bit, at least I like to think so.&lt;/p&gt;

&lt;p&gt;I made it all the way to Day 18 before I got sidetracked with family get-togethers and what not. I still think I’m going
to go back and finish the last week one of these days tho. At least before AoC 2025 hopefully, if the world still exists
by then. Checc out my code &lt;a href=&quot;https://github.com/camsaul/advent-of-code-2024/&quot;&gt;on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Someone convinced me to Twitch stream myself solving the challenges. I really didn’t believe anyone would want to sit
around and watch someone program but I guess on the off chance any of the youfs in college might find it useful to see
how the “pros” (legends?) work I went ahead and did it… I actually had a lot of people tune in, and no hecklers. I
might be the first person to ever live stream coding in Prolog on Twitch. Will I be doing more of this in the future?
Maybe. My dream is to be a content creator/influencer and get free merch and a blue checkmark next to my name. Every day
I wake up and hang my head in disgust because I am only a minor local celebrity and my invite to the Met Gala gets lost
in the mail most years. Gotta stay on that grind!!!!&lt;/p&gt;

&lt;p&gt;Anyways you can checc out my Twitch &lt;a href=&quot;https://www.twitch.tv/camsaul&quot;&gt;here&lt;/a&gt;. Will I be doing more streaming in the future?
Was the moon landing faked? Scientists don’t have answers to life’s two biggest questions yet, but keep your eyes
peeled.&lt;/p&gt;

&lt;p&gt;I also posted videos of some of the live streams on my &lt;a href=&quot;https://www.youtube.com/@camsaul&quot;&gt;YouTube&lt;/a&gt; for your viewing
pleasure.&lt;/p&gt;

&lt;h2 id=&quot;tldr-how-was-using-prolog-anyway&quot;&gt;TL;DR How Was Using Prolog Anyway?&lt;/h2&gt;

&lt;p&gt;Some of the challenges this year were super easy to solve in Prolog, but others really were not. There was an excessive
amount of 2-D grid stuff this year, and Prolog is just really not great at this stuff. (I used &lt;a href=&quot;https://www.swi-prolog.org/&quot;&gt;SWI
Prolog&lt;/a&gt;, and maybe one of the other implementations is better, but as a beginner I found
this easy to get started and it had a great Emacs integration.) There is no native array type in Prolog, so the
idiomatic way of representing a grid is probably a linked list of linked lists… this is fine for small grids but the
polynomial lookup/modification times really bite you in the butt when the grid is something larger like 2000 ✕ 2000 and
you have to do thousands or even millions of lookups.&lt;/p&gt;

&lt;p&gt;At the end of it I spent way more time trying to get my Prolog code to run quickly than I would have with a language
that had a better selection of data types and libraries. I wanted to change my grid code to use &lt;a href=&quot;https://en.wikipedia.org/wiki/Bit_array&quot;&gt;bit
sets&lt;/a&gt; instead of lists-of-lists so I could use bit arithmetic for O(1) lookup
and modification, but there was no bit set data type or library (that I know of) in SWI prolog… so I had to &lt;a href=&quot;https://github.com/camsaul/advent-of-code-2024/blob/master/bitset_grid_util.prolog&quot;&gt;implement
a hacky version myself&lt;/a&gt; using big
integers. A lot faster than lists-of-lists for sure, but if I was writing this in Clojure I could have just used
something like &lt;a href=&quot;https://docs.oracle.com/javase/8/docs/api/java/util/BitSet.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;java.util.BitSet&lt;/code&gt;&lt;/a&gt;. This story kept
repeating itself… I had to &lt;a href=&quot;https://github.com/camsaul/advent-of-code-2024/blob/master/a_star.prolog&quot;&gt;implement A*
myself&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Almost every challenge had me coming up with an elegant and straightforward working solution (for the examples)
relatively quickly that was just way too slow for the actual challenge input. I then had to spend time reworking my
pretty code and making it faster and uglier in order to complete the actual input in a reasonable amount of time.&lt;/p&gt;

&lt;p&gt;I think like the semantics and power of Prolog a lot better than
&lt;a href=&quot;https://minikanren.org/&quot;&gt;miniKanren&lt;/a&gt;/&lt;a href=&quot;https://github.com/clojure/core.logic&quot;&gt;core.logic&lt;/a&gt;. I really enjoyed wrapping my
head around Prolog, and really wish it was more practical to use for more things.&lt;/p&gt;

&lt;h3 id=&quot;pits&quot;&gt;Pits&lt;/h3&gt;

&lt;p&gt;I think the biggest missing things for me are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;A real lack of built-in data types… if Clojure can have immutable arrays then why can’t Prolog?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A lack of libraries – why am I wasting time implementing A* myself?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Fractured ecosystem – libraries and code written for SWI Prolog are incompatible with GNU Prolog or SICStus or
Scryer.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A lack of easy escape hatches to drop to lower-level stuff – I wish I could have dropped to something
&lt;code class=&quot;highlighter-rouge&quot;&gt;java.util.BitSet&lt;/code&gt; or a raw byte array to implement my bitset code. I don’t even know how I would go about
implementing a custom data type, I’m sure it would require C code but I don’t have a good sense of how well supported
extensions like that are.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Questionable performance. I think it’s way to easy to shoot yourself in the foot here and write code that is too slow
for real-world use… I spent way to much time performance tuning my code.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Some basic things like lambdas (anonymous functions/closures) are missing from the core language, and you can use
various competing libraries that utilize operator overloading to add support for them… the syntax is ugly and sad.
Prolog syntax is not terrible but I would have preferred writing it in S-expressions.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I wish &lt;a href=&quot;https://en.wikipedia.org/wiki/Constraint_logic_programming&quot;&gt;constraint logic programming&lt;/a&gt; was more generalized
and not limited to specific libraries that apply constraints to specific data types. What if this was the way things
worked normally instead of being something you have to opt in to?&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Thinking in terms of relations instead of functions really is harder, and made my head hurt sometimes. Generally not
too bad once I got used to it, but it was a little sad when I had to rework some of my relations so they would work
efficiently if say the first parameter was ungrounded versus the second – this sort of “extra-logical” code needed to
make things work efficiently takes away from the magic of pure logic programming&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;peaks&quot;&gt;Peaks&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The promise of writing declarative rules-based code (tell the computer what you want instead of how you want it to do
something) is so alluring it still makes up for the harsh reality of actually using things in a lot of cases.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Pattern matching is amazing – this makes me want to use something like &lt;a href=&quot;https://github.com/clojusc/defun&quot;&gt;Clojure
&lt;code class=&quot;highlighter-rouge&quot;&gt;defun&lt;/code&gt;&lt;/a&gt; all over the place&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;SWI Prolog’s Emacs integration, &lt;a href=&quot;https://github.com/SWI-Prolog/packages-sweep&quot;&gt;Sweep&lt;/a&gt;, is really good, and debugging
my code was really easy – especially compared to something like core.logic, which in my experience was a real
nightmare to debug.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I really like Prolog’s built-in &lt;a href=&quot;https://en.wikipedia.org/wiki/Definite_clause_grammar&quot;&gt;definite clause grammar&lt;/a&gt;
parsing capabilities. They worked really well for parsing the input for most of the challenges.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I used Microsoft Copilot to tell me how to do basic stuff in Prolog, and it generally gave me pretty good answers –
better than I expected given how niche Prolog is. I guess 50 years of Prolog code to train on worked well here. And
while more JS code probably gets written in a given day than all the Prolog code ever written, because Prolog is so
niche the AIs are probably training mostly on good code.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I am happy Prolog is not as dogmatic about pure logic programming as miniKanren. To me it seemed like the Common Lisp
vs. Scheme – the former is for actually solving real problems and the latter is more for writing research papers and
admiring the elegance of your solutions.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;There are lots of good books about learning Prolog from “back in the day”… they don’t all cover a lot of modern
stuff like CLP but they did a pretty good job of helping me wrap my head around thinking about things in a Prolog way
(as opposed to “writing C in any language”).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;other-thoughts&quot;&gt;Other thoughts&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;I “learned” Prolog in a programming languages course in college, and wow did they really sell the language short. I
really did not fully understand the power until I spent a month doing self-study&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;a href=&quot;https://mercurylang.org/&quot;&gt;Mercury language&lt;/a&gt; looks really interesting and makes bold promises about performance
but it doesn’t seem to have a lot of resources available for learning it.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I would really love if there was a nice implementation of Prolog in Clojure that let me use Clojure goodness like
S-expressions and macros and drop-down to low-level Java when needed. I think there are a few JVM implementations of
Prolog, but I’m not sure what state they’re in. I spent a little time playing around with things and got a basic
Prolog implementation working in Clojure… it’s honestly not rocket science. If I get more free time I might try to
play around with this more and see if I can turn it into something actually useful. Maybe I can use it for AoC 2025.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I know back in the day a lot of the Common Lisp people played around with implementing Prolog in CL – I even have a
few books on my bookshelf that walk thru how to write an implementation. Time to do some reading.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👇 Like and subscribe below! 👇&lt;/p&gt;</content><author><name></name></author><summary type="html">This was my first year doing Advent of Code. Previous years I had other stuff I wanted to do with my free time besides sit at a computer even more than I already do… after seeing some of my coworkers post epic solutions I got FOMO and joined the challenge.</summary></entry><entry><title type="html">I gave an epic talk at Clojure/conj 2024</title><link href="https://camsaul.com/clojure/2025/02/04/clojure-conj-talk.html" rel="alternate" type="text/html" title="I gave an epic talk at Clojure/conj 2024" /><published>2025-02-04T18:35:00+00:00</published><updated>2025-02-04T18:35:00+00:00</updated><id>https://camsaul.com/clojure/2025/02/04/clojure-conj-talk</id><content type="html" xml:base="https://camsaul.com/clojure/2025/02/04/clojure-conj-talk.html">&lt;p&gt;What’s up everyone. I gave an epic talk at Clojure/conj 2024 a few months back, you can watch it on YouTube here:
&lt;a href=&quot;https://youtu.be/vUe3slLHk20?si=SP6yaqaFv06nS2f2&quot;&gt;“Getting 50,000 Companies on Board with Clojure” by Cam Saul&lt;/a&gt;. Enjoi!&lt;/p&gt;

&lt;p&gt;👇 Like and subscribe below! 👇&lt;/p&gt;</content><author><name></name></author><summary type="html">What’s up everyone. I gave an epic talk at Clojure/conj 2024 a few months back, you can watch it on YouTube here: “Getting 50,000 Companies on Board with Clojure” by Cam Saul. Enjoi!</summary></entry><entry><title type="html">Debugging Custom Kondo Hooks and Clojure LSP Jump-to-Definition Functionality</title><link href="https://camsaul.com/clojure/2024/09/03/debugging-clojure-lsp-jump-to-definition.html" rel="alternate" type="text/html" title="Debugging Custom Kondo Hooks and Clojure LSP Jump-to-Definition Functionality" /><published>2024-09-03T15:13:00+00:00</published><updated>2024-09-03T15:13:00+00:00</updated><id>https://camsaul.com/clojure/2024/09/03/debugging-clojure-lsp-jump-to-definition</id><content type="html" xml:base="https://camsaul.com/clojure/2024/09/03/debugging-clojure-lsp-jump-to-definition.html">&lt;p&gt;At &lt;a href=&quot;https://github.com/metabase/metabase&quot;&gt;Metabase&lt;/a&gt; I wrote &lt;a href=&quot;https://github.com/metabase/metabase/blob/339ff2433d4a14f2d153d211746b75bf334f798b/src/metabase/api/common.clj#L384-L417&quot;&gt;a macro called
&lt;code class=&quot;highlighter-rouge&quot;&gt;defendpoint&lt;/code&gt;&lt;/a&gt;
for defining (you guessed it!) REST API endpoints with a typical usage that looks something like&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/defendpoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/:id/copy&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Copy a `Card`, with the new name 'Copy of _name_'&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; Malli schemas defined after the argslist are used for validation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:maybe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ms/PositiveInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orig-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/read-check&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Copy of &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orig-card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orig-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;card/create-card!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api/*current-user*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:last-edit-info&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last-edit/edit-information-for-user&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api/*current-user*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I was spending a lot of time scratching my head wondering why &lt;a href=&quot;https://clojure-lsp.io/&quot;&gt;Clojure LSP&lt;/a&gt; jump-to-definition
functionality wasn’t working as expected inside &lt;code class=&quot;highlighter-rouge&quot;&gt;defendpoint&lt;/code&gt;. I wrote a &lt;a href=&quot;https://github.com/clj-kondo/clj-kondo/blob/master/doc/hooks.md&quot;&gt;custom clj-kondo
hook&lt;/a&gt; for it, but moving my cursor to something like
&lt;code class=&quot;highlighter-rouge&quot;&gt;api/read-check&lt;/code&gt; and doing something &lt;code class=&quot;highlighter-rouge&quot;&gt;M-x lsp-find-definition&lt;/code&gt; would just take me to the start of the &lt;code class=&quot;highlighter-rouge&quot;&gt;defendpoint&lt;/code&gt; form
rather than what I was expecting.&lt;/p&gt;

&lt;p&gt;Debugging this issue was pretty hard – while there was clearly something wrong with my custom Kondo hook, but I
couldn’t figure out what it was.&lt;/p&gt;

&lt;p&gt;In the end I finally managed to figure out how to debug these sorts of things so I’m writing down my steps in case
anyone else runs into the same problem.&lt;/p&gt;

&lt;h2 id=&quot;add-clojure-lsp-as-a-dependency&quot;&gt;Add Clojure LSP as a dependency&lt;/h2&gt;

&lt;p&gt;I added this to my &lt;code class=&quot;highlighter-rouge&quot;&gt;deps.edn&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:aliases&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:lsp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:extra-deps&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;com.github.clojure-lsp/clojure-lsp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:mvn/version&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2024.08.05-18.16.00&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dev.weavejester/cljfmt&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;             &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:git/url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://github.com/weavejester/cljfmt&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                                    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:git/sha&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;434408f6909924f524c8027b37422d32bb49622d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}}}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that at the time of this writing because of
&lt;a href=&quot;https://github.com/clojure-lsp/clojure-lsp/issues/1867&quot;&gt;clojure-lsp/clojure-lsp#1867&lt;/a&gt; I had to add a fake dependency
for &lt;a href=&quot;https://github.com/weavejester/cljfmt&quot;&gt;cljfmt&lt;/a&gt; to make this work as well – I just used the latest Git SHA at the
time of this writing.&lt;/p&gt;

&lt;h2 id=&quot;launch-repl-with-the-lsp-alias&quot;&gt;Launch REPL with the &lt;code class=&quot;highlighter-rouge&quot;&gt;:lsp&lt;/code&gt; alias&lt;/h2&gt;

&lt;p&gt;In Emacs I used&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;C-u M-x cider-jack-in
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;to let me edit the command it used to launch the nREPL to add the &lt;code class=&quot;highlighter-rouge&quot;&gt;:lsp&lt;/code&gt; alias. Note that I launched the REPL from the
same project I was debugging.&lt;/p&gt;

&lt;h2 id=&quot;debug-from-the-repl&quot;&gt;Debug from the REPL&lt;/h2&gt;

&lt;p&gt;This is the code I came up with while poking thru Kondo and LSP internals to debug things:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'clj-kondo.hooks-api&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'clojure-lsp.kondo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'clojure-lsp.queries&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
         &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'clojure.java.io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find-element&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clj-kondo.hooks-api/*reload*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;letfn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;analysis&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;atom&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;           &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file-analyzed-fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clojure-lsp.kondo/run-kondo-on-paths!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file-analyzed-fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;db-with-analysis&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clojure-lsp.kondo/db-with-analysis&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;analysis&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;db-with-analysis&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file://&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.getAbsolutePath&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clojure.java.io/file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clojure-lsp.queries/find-element-under-cursor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Binding &lt;code class=&quot;highlighter-rouge&quot;&gt;clj-kondo.hooks-api/*reload*&lt;/code&gt; is important so Clojure LSP will reload your Kondo config (e.g., custom hooks)
after you make changes to it.&lt;/p&gt;

&lt;p&gt;The basic idea here is to run Kondo on a specific file &lt;code class=&quot;highlighter-rouge&quot;&gt;path&lt;/code&gt; and then pass that analysis along to LSP which can find
the element at a given position in a file based on that analysis.&lt;/p&gt;

&lt;p&gt;All you need is a file name to debug and a line number and column number that positions the cursor over the symbol you
want to debug.&lt;/p&gt;

&lt;p&gt;Example usage:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;src/metabase/api/card_2.clj&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;some-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find-element&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;select-keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:alias&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:from-var&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;;=&amp;gt; {:name read-check, :alias api, :from metabase.api.card-2, :from-var POST_:id_copy, :to metabase.api.common}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If your Kondo hook is broken then this will either return nothing or return the wrong information. If the information
looks something like the example result above then things should work as expected.&lt;/p&gt;

&lt;h2 id=&quot;what-was-broken&quot;&gt;What was broken?&lt;/h2&gt;

&lt;p&gt;My original version of the hook returned when I used the &lt;code class=&quot;highlighter-rouge&quot;&gt;find-element&lt;/code&gt; code above.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POST_&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:id_copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My hook basically looked like this (simplified a bit):&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hooks.metabase.api.common&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clj-kondo.hooks-api&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clojure.string&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route-fn-name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;route fn hook&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;vector?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;str/replace&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;;; Original custom Kondo hook -- BROKEN!!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defendpoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;letfn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update-defendpoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_defendpoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:children&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/list-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/token-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/token-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;compojure.core&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/sexpr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/list-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/token-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'clojure.core/defn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/token-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;route-fn-name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/sexpr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/sexpr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;with-meta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))))]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update-defendpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;which means the example at the beginning would be transformed in a node representing a form like this:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;;; Output of custom Kondo hook&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;compojure.core/POST&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;clojure.core/defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POST_&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:id_copy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Copy a `Card`, with the new name 'Copy of _name_'&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:maybe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ms/PositiveInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orig-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/read-check&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Copy of &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orig-card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orig-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;card/create-card!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api/*current-user*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:last-edit-info&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last-edit/edit-information-for-user&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api/*current-user*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re generating a &lt;code class=&quot;highlighter-rouge&quot;&gt;do&lt;/code&gt; form here so we can capture the usage of &lt;code class=&quot;highlighter-rouge&quot;&gt;compojure.core/POST&lt;/code&gt; so Kondo doesn’t think it’s
unused if it’s in our &lt;code class=&quot;highlighter-rouge&quot;&gt;:require&lt;/code&gt; form in the &lt;code class=&quot;highlighter-rouge&quot;&gt;ns&lt;/code&gt; declaration.&lt;/p&gt;

&lt;p&gt;Side note: this is mentioned in the Kondo docs but you can verify your Kondo hook output looks correct on the surface
level by Kondo itself as a dep and using &lt;code class=&quot;highlighter-rouge&quot;&gt;clj-kondo.hooks-api/parse-string&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;clj-kondo.hooks-api/sexpr&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;;; debugging custom Kondo hooks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/defendpoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/:id/copy&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Copy a `Card`, with the new name 'Copy of _name_'&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:maybe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ms/PositiveInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orig-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/read-check&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Copy of &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orig-card&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orig-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
       &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;card/create-card!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new-card&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api/*current-user*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
           &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:last-edit-info&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last-edit/edit-information-for-user&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api/*current-user*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pr-str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api/parse-string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defendpoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api/sexpr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve actually started putting stuff like these in tests to make sure I don’t break my dozens of custom Kondo hooks.&lt;/p&gt;

&lt;p&gt;Anyways “looks correct at a surface level” is key here. Because while this output looks correct, what you do with the
metadata (specifically stuff like start and end line and column numbers) from the original nodes is actually super
important to Clojure LSP. (They’re important to Kondo too to make sure warnings pop up in the right place.)&lt;/p&gt;

&lt;p&gt;If you want to see what this metadata looks like in your REPL you can&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set!&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;*print-meta*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;or just bind &lt;code class=&quot;highlighter-rouge&quot;&gt;*print-meta*&lt;/code&gt; and use something like &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.pprint/pprint&lt;/code&gt; e.g.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;binding&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;*print-meta*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                 &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pr-str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api/parse-string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hooks.metabase.api.common/defendpoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api/sexpr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clojure.pprint/pprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output is actually super noisy so I’ll leave it out here.&lt;/p&gt;

&lt;p&gt;The fixed version of the hook actually has the exact same output aside from some changes to
metadata.&lt;/p&gt;

&lt;p&gt;Here’s the fixed version of the hook above:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;;; custom Kondo hook -- FIXED&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defendpoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;letfn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update-defendpoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_defendpoint&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:children&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/list-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/token-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/token-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;compojure.core&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/sexpr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;with-meta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/list-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                     &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/token-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'clojure.core/defn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/token-node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;route-fn-name&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/sexpr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;api/sexpr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;with-meta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;with-meta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:node&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update-defendpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The important change here was to give the generated function name symbol &lt;code class=&quot;highlighter-rouge&quot;&gt;POST_:id_copy&lt;/code&gt; the metadata (line and column
start and end numbers) from the original &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;/:id/copy&quot;&lt;/code&gt; string. It seems like without knowing where a function name is
defined in the text file (or rather, where it is restricted to) LSP for whatever reason was assuming the entire
&lt;code class=&quot;highlighter-rouge&quot;&gt;defendpoint&lt;/code&gt; form served as a the function name which meant any attempt to find the definition under the cursor
anywhere in that form would assume you were trying jump to the function definition itself.&lt;/p&gt;

&lt;p&gt;Other changes I made which didn’t seem to fix anything but seem more correct are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;compojure.core/POST&lt;/code&gt; now gets the metadata (line and column start and end numbers) from the original &lt;code class=&quot;highlighter-rouge&quot;&gt;POST&lt;/code&gt; symbol
(the &lt;code class=&quot;highlighter-rouge&quot;&gt;method&lt;/code&gt; parameter) from the original node&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The overall new top-level list node gets the metadata from the original top-level node rather than putting it on the
generated &lt;code class=&quot;highlighter-rouge&quot;&gt;defn&lt;/code&gt; node. Not sure this makes a huge difference TBH.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Custom Kondo hooks are powerful and worth the time it takes to wrap your head around
&lt;a href=&quot;https://github.com/clj-commons/rewrite-clj&quot;&gt;rewrite-clj&lt;/a&gt;. Debugging them is tricky, and debugging them when used by LSP
is even trickier. I haven’t seen it documented anywhere before, but it is possible. I hope you find this useful!&lt;/p&gt;

&lt;p&gt;👇 Like and subscribe below! 👇&lt;/p&gt;</content><author><name></name></author><summary type="html">At Metabase I wrote a macro called defendpoint for defining (you guessed it!) REST API endpoints with a typical usage that looks something like</summary></entry><entry><title type="html">Humane Are Just Dropped!</title><link href="https://camsaul.com/announcement/2022/09/01/humane-are-just-dropped.html" rel="alternate" type="text/html" title="Humane Are Just Dropped!" /><published>2022-09-01T20:40:00+00:00</published><updated>2022-09-01T20:40:00+00:00</updated><id>https://camsaul.com/announcement/2022/09/01/humane-are-just-dropped</id><content type="html" xml:base="https://camsaul.com/announcement/2022/09/01/humane-are-just-dropped.html">&lt;h1 id=&quot;hello-world-its-me-your-friendly-neighborhood-cam&quot;&gt;Hello, world. It’s me, your friendly neighborhood #cam.&lt;/h1&gt;

&lt;p&gt;You may have been concerned about my whereabouts for the last 2 years, but I was doing the HUMANE thing and working on
a new library for you all… In my free time while I’m not being a full time #birddad I have been working on a fun new
project that will help you write tests. Let me break it down.&lt;/p&gt;

&lt;p&gt;I’ve been using some version of this or another for a while now; I thought it was finally time to spin it out into a
separate library, in case anyone else found it useful. &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.test/are&lt;/code&gt; is great for writing lots of assertions
quickly, but it has two big problems that prevent me from using it everywhere:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Failing assertions give no indication as to which set of arguments failed if you’re using anything that pretty
prints test output, such as &lt;a href=&quot;https://github.com/pjstadig/humane-test-output&quot;&gt;Humane Test Output&lt;/a&gt;,
&lt;a href=&quot;https://github.com/clojure-emacs/cider&quot;&gt;CIDER&lt;/a&gt;, or &lt;a href=&quot;https://github.com/weavejester/eftest&quot;&gt;eftest&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;are&lt;/code&gt; lets you shoot yourself in the foot by writing expressions that include &lt;code class=&quot;highlighter-rouge&quot;&gt;is&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;testing&lt;/code&gt;, and wraps them in
another &lt;code class=&quot;highlighter-rouge&quot;&gt;is&lt;/code&gt; without complaining&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can learn more about it on &lt;a href=&quot;https://github.com/camsaul/humane-are&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope everyone finds this as useful as I hope I will.&lt;/p&gt;

&lt;p&gt;Peace, love, and bird seed.&lt;/p&gt;

&lt;p&gt;xoxo CamSaul&lt;/p&gt;</content><author><name></name></author><summary type="html">Hello, world. It’s me, your friendly neighborhood #cam.</summary></entry><entry><title type="html">My Clojure/north 2020 talk about Methodical is now available on YouTube</title><link href="https://camsaul.com/methodical/2020/08/19/my-clojure-north-2020-talk-about-methodical-is-up.html" rel="alternate" type="text/html" title="My Clojure/north 2020 talk about Methodical is now available on YouTube" /><published>2020-08-19T20:07:00+00:00</published><updated>2020-08-19T20:07:00+00:00</updated><id>https://camsaul.com/methodical/2020/08/19/my-clojure-north-2020-talk-about-methodical-is-up</id><content type="html" xml:base="https://camsaul.com/methodical/2020/08/19/my-clojure-north-2020-talk-about-methodical-is-up.html">&lt;p&gt;&lt;a href=&quot;https://clojurenorth.com/cam-saul.html&quot;&gt;I gave a talk about a library I wrote called Methodical at Clojure/north
2020.&lt;/a&gt; &lt;a href=&quot;https://github.com/camsaul/methodical&quot;&gt;Methodical&lt;/a&gt; ports the “generic
method” parts of the Common Lisp Object System (CLOS) to Clojure in a Clojurey way. As far as I can tell, it’s the
best/most complete port of the CLOS to Clojure out there, even though it only ports a subset of CLOS.&lt;/p&gt;

&lt;p&gt;This talk is now available to watch on YouTube:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://youtu.be/If3GT8zSHfE&quot;&gt;&lt;img src=&quot;https://img.youtube.com/vi/If3GT8zSHfE/maxresdefault.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you enjoyed the talk and have money burning a hole in your pocket, consider buying me a cup of coffee at &lt;a href=&quot;https://github.com/sponsors/camsaul&quot;&gt;GitHub
Sponsors&lt;/a&gt;.&lt;/p&gt;</content><author><name></name></author><summary type="html">I gave a talk about a library I wrote called Methodical at Clojure/north 2020. Methodical ports the “generic method” parts of the Common Lisp Object System (CLOS) to Clojure in a Clojurey way. As far as I can tell, it’s the best/most complete port of the CLOS to Clojure out there, even though it only ports a subset of CLOS.</summary></entry><entry><title type="html">Intro to Emacs Lisp: Adding Live Previews when Editing Markdown Files (Part 3 of 3)</title><link href="https://camsaul.com/emacs-lisp/2020/06/10/emacs-lisp-intro-markdown-live-previews-part-3.html" rel="alternate" type="text/html" title="Intro to Emacs Lisp: Adding Live Previews when Editing Markdown Files (Part 3 of 3)" /><published>2020-06-10T02:12:00+00:00</published><updated>2020-06-10T02:12:00+00:00</updated><id>https://camsaul.com/emacs-lisp/2020/06/10/emacs-lisp-intro-markdown-live-previews-part-3</id><content type="html" xml:base="https://camsaul.com/emacs-lisp/2020/06/10/emacs-lisp-intro-markdown-live-previews-part-3.html">&lt;p&gt;This post is part three of a series of 3 posts. View the other parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-1.html&quot;&gt;Part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-2.html&quot;&gt;Part 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/10/emacs-lisp-intro-markdown-live-previews-part-3.html&quot;&gt;Part 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, we’ll make the command more useful by:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Having it prompt us for a file to preview when running interactively.&lt;/li&gt;
  &lt;li&gt;Adding documentation&lt;/li&gt;
  &lt;li&gt;Having Emacs run it automatically whenever we save a Markdown file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll discuss the following Emacs Lisp concepts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Optional arguments&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;interactive&lt;/code&gt; code characters&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;progn&lt;/code&gt; forms&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; forms&lt;/li&gt;
  &lt;li&gt;Docstrings&lt;/li&gt;
  &lt;li&gt;Hooks&lt;/li&gt;
  &lt;li&gt;Buffer-local variables&lt;/li&gt;
  &lt;li&gt;What it means that Emacs Lisp is a Lisp-2&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;prompting-for-a-file-to-preview&quot;&gt;Prompting for a file to preview&lt;/h1&gt;

&lt;p&gt;Here we’ve broken out &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/preview-markdown&lt;/code&gt; a bit further.&lt;/p&gt;

&lt;p&gt;This version of the command works the same as before when run programmatically,
but when executed interactively (e.g. via &lt;code class=&quot;highlighter-rouge&quot;&gt;M-x&lt;/code&gt;) it will prompt you for the file
to preview, complete with autocomplete; it defaults to the current file.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/-scroll-percentage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line-number-at-pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;window-start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line-number-at-pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/-set-window-start-to-percentage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;scroll-percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;goto-char&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;target-line-number&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;truncate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line-number-at-pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scroll-percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;forward-line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;1-&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;target-line-number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;set-window-start&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/-render-markdown-preview-current-buffer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Rendering Markdown preview of %s&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shell-command-on-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pandoc -f gfm&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;switch-to-buffer-other-window&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;libxml-parse-html-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;erase-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shr-insert-document&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-read-only&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/-preview-markdown-file&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-selected-window&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;find-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;file://&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;scroll-percentage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/-scroll-percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/-render-markdown-preview-current-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/-set-window-start-to-percentage&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scroll-percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;amp;optional&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Render a markdown preview of FILENAME (by default, the current file) to HTML and display it with `shr-insert-document'.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;fFile: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;progn&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/-preview-markdown-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;switch-to-buffer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;current-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/-preview-markdown-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are a couple of new concepts to introduce here:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;optional&lt;/code&gt; arguments&lt;/li&gt;
  &lt;li&gt;arguments to the &lt;code class=&quot;highlighter-rouge&quot;&gt;(interactive)&lt;/code&gt; declaration&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;progn&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Emacs Lisp docstrings&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;optional-arguments&quot;&gt;Optional arguments&lt;/h3&gt;

&lt;p&gt;In function definitions, &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;optional&lt;/code&gt; is used to denote optional positional
arguments. If an optional argument is not passed, its value will be &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt;
in the body of the function:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-filename&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;amp;optional&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-filename&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;x.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;; -&amp;gt; &quot;x.txt&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;         &lt;span class=&quot;c1&quot;&gt;; -&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;amp;optional&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;we are making the &lt;code class=&quot;highlighter-rouge&quot;&gt;filename&lt;/code&gt; argument optional, because we’d like to be able to
call this function with a specific file to preview, but default to the current
file when it is called with no argument (such as when it is called as part of a
&lt;em&gt;hook&lt;/em&gt;, as discussed below).&lt;/p&gt;

&lt;h3 id=&quot;specifying-a-prompt-in-the-interactive-declaration&quot;&gt;Specifying a prompt in the &lt;code class=&quot;highlighter-rouge&quot;&gt;interactive&lt;/code&gt; declaration&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;interactive&lt;/code&gt; declaration can optionally take an argument that tells Emacs
how to prompt for values to use as the command’s arguments:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;(interactive &quot;fFile: &quot;)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;f&lt;/code&gt; &lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/elisp/Interactive-Codes.html#Interactive-Codes&quot;&gt;code
character&lt;/a&gt;
tells Emacs to prompt for an &lt;strong&gt;existing filename&lt;/strong&gt;, defaulting to the name of the
file in the current buffer. The rest of the string is the text of the prompt to
show the user in the minibuffer (&lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;File: &quot;&lt;/code&gt;). Note that we have to include the
space after the prompt ourselves.&lt;/p&gt;

&lt;p&gt;The string name of the file the user chooses will be passed in as the &lt;code class=&quot;highlighter-rouge&quot;&gt;filename&lt;/code&gt;
argument. When the function is invoked programmatically, Emacs will not prompt
the user for a value of &lt;code class=&quot;highlighter-rouge&quot;&gt;filename&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;if-forms&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; forms&lt;/h3&gt;

&lt;p&gt;In Emacs Lisp, &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; forms have the syntax&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;condition&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;then-form&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;else-forms...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; t means true and nil means null/false&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; form in the example is roughly equivalent to this Algol-style if form:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;progn-forms&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;progn&lt;/code&gt; forms&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;progn&lt;/code&gt; can be used to execute multiple statements as a single form. (If you’re
familiar with Clojure, this is the classic Lisp equivalent of &lt;code class=&quot;highlighter-rouge&quot;&gt;do&lt;/code&gt;). Each form is
evaluated in order, and the result of a &lt;code class=&quot;highlighter-rouge&quot;&gt;progn&lt;/code&gt; form is the result of the last
form contained by it:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;progn&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;We are adding some numbers.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Because only the value of the last form is returned, forms other than the last
are usually executed for side effects.&lt;/p&gt;

&lt;h3 id=&quot;understanding-the-updated-campreview-markdown&quot;&gt;Understanding the updated &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/preview-markdown&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Now that we’ve discussed the new concepts, Let’s take a deeper look at our simplified &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/preview-markdown&lt;/code&gt; function:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;&amp;amp;optional&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;Render a markdown preview of FILENAME (by default, the current file) to HTML and display it with `shr-insert-document'.&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;fFile: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;progn&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/-preview-markdown-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;switch-to-buffer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;current-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/-preview-markdown-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;If a &lt;code class=&quot;highlighter-rouge&quot;&gt;filename&lt;/code&gt; argument was passed:
    &lt;ol&gt;
      &lt;li&gt;Call &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/-preview-markdown-file&lt;/code&gt; with that filename&lt;/li&gt;
      &lt;li&gt;Switch back to the current buffer (&lt;code class=&quot;highlighter-rouge&quot;&gt;save-selected-window&lt;/code&gt;, called in
&lt;code class=&quot;highlighter-rouge&quot;&gt;cam/-preview-markdown-file&lt;/code&gt;, also restores the current buffer, but
doesn’t necessarily bring it to the top).&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;If no &lt;code class=&quot;highlighter-rouge&quot;&gt;filename&lt;/code&gt; argument was passed:
    &lt;ol&gt;
      &lt;li&gt;Call &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/-preview-markdown-file&lt;/code&gt; with the filename of the current
buffer.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;opening-a-fileswitching-to-existing-buffer-for-a-file&quot;&gt;Opening a file/switching to existing buffer for a file&lt;/h3&gt;

&lt;p&gt;In &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/-preview-markdown-file&lt;/code&gt; we’ve added a call to &lt;code class=&quot;highlighter-rouge&quot;&gt;find-file&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;find-file&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;switches to a buffer containing &lt;code class=&quot;highlighter-rouge&quot;&gt;filename&lt;/code&gt;. In cases where we’re already looking
at that file, &lt;code class=&quot;highlighter-rouge&quot;&gt;find-file&lt;/code&gt; doesn’t change anything, so this code continues to
work normally when rendering the file in the current buffer. For other files, it
will switch to a buffer for that file, creating a buffer (opening the file) if
needed.&lt;/p&gt;

&lt;h3 id=&quot;adding-a-docsting&quot;&gt;Adding a docsting&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-emacs-liso&quot;&gt;(defun cam/preview-markdown (&amp;amp;optional filename)
  &quot;Render a markdown preview of FILENAME (by default, the current file) to HTML and display it with `shr-insert-document'.&quot;
  (interactive &quot;fFile: &quot;)
  ...)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Emacs Lisp docstrings come after the argument list but &lt;em&gt;before&lt;/em&gt; the
&lt;code class=&quot;highlighter-rouge&quot;&gt;interactive&lt;/code&gt; declaration, if there is one. Emacs Lisp docstrings conventionally
mention arguments in all capital letters (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;FILENAME&lt;/code&gt;). When viewing the
documentation for a function, arguments mentioned this way are highlighted
automatically.&lt;/p&gt;

&lt;p&gt;You can add hyperlinks to other Emacs Lisp functions or variables using the&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;`symbol-name'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;syntax. These days you can also use &lt;em&gt;curved&lt;/em&gt; single quotes instead, but figuring
out how to type them involves too much effort, so I stick with the
backtick-single-quote convention, which is the one you’ll encounter most
commonly in the wild.&lt;/p&gt;

&lt;p&gt;Try it yourself! &lt;code class=&quot;highlighter-rouge&quot;&gt;C-h f cam/preview-markdown&lt;/code&gt; to view the documentation for
the function &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/preview-markdown&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/elisp/Documentation-Tips.html&quot;&gt;This
page&lt;/a&gt;
of the Emacs Lisp documentation has a very good explanation of docstrings for further reading.&lt;/p&gt;

&lt;h1 id=&quot;adding-an-after-save-hook&quot;&gt;Adding an &lt;code class=&quot;highlighter-rouge&quot;&gt;after-save-hook&lt;/code&gt;&lt;/h1&gt;

&lt;p&gt;Now all that’s left to do is telling Emacs to automatically run our command
whenever we save a Markdown file.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'markdown-mode-hook&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'after-save-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Whenever we open a Markdown file, Emacs will set the major mode to
&lt;code class=&quot;highlighter-rouge&quot;&gt;markdown-mode&lt;/code&gt;. Major modes and most minor modes have &lt;em&gt;hooks&lt;/em&gt; that run whenever
the mode is entered. When we enter &lt;code class=&quot;highlighter-rouge&quot;&gt;markdown-mode&lt;/code&gt;, Emacs will run any functions
in &lt;code class=&quot;highlighter-rouge&quot;&gt;markdown-mode-hook&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To &lt;code class=&quot;highlighter-rouge&quot;&gt;markdown-mode-hook&lt;/code&gt; we add a &lt;em&gt;lambda&lt;/em&gt; (anonymous function) that itself adds
the command &lt;code class=&quot;highlighter-rouge&quot;&gt;#'cam/preview-markdown&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;after-save-hook&lt;/code&gt;. Any functions in
&lt;code class=&quot;highlighter-rouge&quot;&gt;after-save-hook&lt;/code&gt; will run after a file is saved.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;hook&lt;/em&gt; is just a variable that contains a list of functions that get ran at a
specific point in time. &lt;code class=&quot;highlighter-rouge&quot;&gt;markdown-mode-hook&lt;/code&gt; is a list of functions to run when
entering &lt;code class=&quot;highlighter-rouge&quot;&gt;markdown-mode&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;after-save-hook&lt;/code&gt; is a list of functions to run
after saving a file.&lt;/p&gt;

&lt;h3 id=&quot;add-hook-and-buffer-local-variables&quot;&gt;Add-hook and buffer-local variables&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;add-hook&lt;/code&gt; adds a function to a hook (which, again, is just a list), creating
the variable if it doesn’t already exist. &lt;code class=&quot;highlighter-rouge&quot;&gt;add-hook&lt;/code&gt; has two &lt;code class=&quot;highlighter-rouge&quot;&gt;optional&lt;/code&gt; args,
&lt;code class=&quot;highlighter-rouge&quot;&gt;append&lt;/code&gt; (default &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt;) and &lt;code class=&quot;highlighter-rouge&quot;&gt;local&lt;/code&gt; (default &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt;). &lt;code class=&quot;highlighter-rouge&quot;&gt;append&lt;/code&gt; tells it to add
the function at the end of the list, meaning it gets ran last. In some cases, it
is preferable to run a certain function after others in the hook have ran. In
our case, it doesn’t really matter if our function runs before or after others,
so we’ll pass the default value of &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;local&lt;/code&gt; tells it to add it to the
&lt;em&gt;buffer-local&lt;/em&gt; version of the hook rather than the global version. We’ll explore
the difference more in the future, but for now suffice to know that variables
can have global values as well as values specific to a buffer. Buffer-local
values overshadow global values.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'after-save-hook&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;without the optional args would add the function &lt;code class=&quot;highlighter-rouge&quot;&gt;#'cam/preview-markdown&lt;/code&gt; to the
&lt;em&gt;global&lt;/em&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;after-save-hook&lt;/code&gt;, which means it would run after saving &lt;em&gt;any&lt;/em&gt; file,
which is not what we want. By adding the &lt;code class=&quot;highlighter-rouge&quot;&gt;local&lt;/code&gt; option, the function is only
added to the hook for the current buffer, meaning it only runs for the current buffer.&lt;/p&gt;

&lt;p&gt;To sum it up: &lt;code class=&quot;highlighter-rouge&quot;&gt;markdown-mode-hook&lt;/code&gt; gets ran once for every new Markdown file we
open, and we use that to add &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/preview-markdown&lt;/code&gt; to the &lt;code class=&quot;highlighter-rouge&quot;&gt;after-save-hook&lt;/code&gt; for
the newly created buffer. Files that aren’t opened in &lt;code class=&quot;highlighter-rouge&quot;&gt;markdown-mode&lt;/code&gt; aren’t
affected at all.&lt;/p&gt;

&lt;h4 id=&quot;lisp-1s-and-lisp-2s&quot;&gt;Lisp-1s and Lisp-2s&lt;/h4&gt;

&lt;p&gt;Emacs Lisp is a Lisp-2, which means that variables and functions live in separate
“namespaces”. For example, &lt;code class=&quot;highlighter-rouge&quot;&gt;length&lt;/code&gt; could refer to both a variable named &lt;code class=&quot;highlighter-rouge&quot;&gt;length&lt;/code&gt;
&lt;em&gt;and&lt;/em&gt; a function named &lt;code class=&quot;highlighter-rouge&quot;&gt;length&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; Emacs Lisp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 3&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; the length variable doesn't overshadow the length function&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Contrast this to Clojure, a Lisp-1, where variables and functions share a
“namespace”:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; Clojure&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; count is the Clojure equivalent of length&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 3&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; let-bound count overshadows *any* usage of the symbol&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; [100 line stacktrace: Integer cannot be invoked as a function]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using functions passed as arguments is much simpler in Lisp-1s, however:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; Clojure&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call-f&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call-f&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 101&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With Lisp-2s, you have to use &lt;code class=&quot;highlighter-rouge&quot;&gt;funcall&lt;/code&gt; to call a function bound to a variable:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; Emacs Lisp&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; the symbol f refers only to variable, so to use it as a function contained in&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; f, you have to use funcall&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;call-f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;funcall&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;call-f&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;1+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 101&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are pros and cons to both Lisp-1s and Lisp-2s. Lisp-1s lend themselves
more elegantly to passing around functions since you don’t need to use special
forms like &lt;code class=&quot;highlighter-rouge&quot;&gt;funcall&lt;/code&gt;. However you have to be much more careful not to
unintentionally overshadow functions in Lisp-1s, which usually means
intentionally misspelling function parameter names. You’ll often see nonsense
like this in Lisp-1s:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; clojure&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; so as to not overshadow the &quot;type&quot; function, we have to give our type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;;; parameter a different name, such as &quot;typ&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True if the type of `x` is equal to `typ`.&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are other differences to explore in the a future post, but let’s get back to tweaking our command!&lt;/p&gt;

&lt;h3 id=&quot;quoting-function-names&quot;&gt;Quoting function names&lt;/h3&gt;

&lt;p&gt;Attempting to evaluate &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/preview-markdown&lt;/code&gt; will result in an error:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt;
&lt;span class=&quot;vg&quot;&gt;***&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Eval&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;***&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;Symbol&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;variable&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;void:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead, we can quote the symbol name so the reader doesn’t attempt to evaluate
it. Emacs offers an alternative syntax for quoting symbols that refer to
functions:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;#'&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just as &lt;code class=&quot;highlighter-rouge&quot;&gt;'x&lt;/code&gt; is shorthand for &lt;code class=&quot;highlighter-rouge&quot;&gt;(quote x)&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;#'x&lt;/code&gt; is shorthand for &lt;code class=&quot;highlighter-rouge&quot;&gt;(function x)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In many cases, using &lt;code class=&quot;highlighter-rouge&quot;&gt;quote&lt;/code&gt; for function names will still work correctly:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;add-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'after-save-hook&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'cam/preview-markdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But &lt;code class=&quot;highlighter-rouge&quot;&gt;function&lt;/code&gt; is preferred over &lt;code class=&quot;highlighter-rouge&quot;&gt;quote&lt;/code&gt; for symbols that name functions for a
couple of reasons: it’s clearer and more explicit, and the compiler is better
able to optimize &lt;code class=&quot;highlighter-rouge&quot;&gt;function&lt;/code&gt; forms.&lt;/p&gt;

&lt;h1 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h1&gt;

&lt;p&gt;Complete source for the final version can be found at this &lt;a href=&quot;https://gist.github.com/camsaul/71d8d8c3e9c1cc4e0a3ee2d4b04d0fef&quot;&gt;GitHub
Gist&lt;/a&gt;. Please
feel free to leave comments or suggestions there, or on &lt;a href=&quot;https://www.reddit.com/r/emacs/comments/h08cq8/intro_to_emacs_lisp_adding_live_previews_when/&quot;&gt;this Reddit
thread&lt;/a&gt;.
If there’s enough positive feedback from these posts, I’ll be sure to add more!&lt;/p&gt;

&lt;p&gt;If you enjoyed these posts and have money burning a hole in your pocket,
consider buying me a cup of coffee at &lt;a href=&quot;https://github.com/sponsors/camsaul&quot;&gt;GitHub
Sponsors&lt;/a&gt;.&lt;/p&gt;</content><author><name></name></author><summary type="html">This post is part three of a series of 3 posts. View the other parts:</summary></entry><entry><title type="html">Intro to Emacs Lisp: Adding Live Previews when Editing Markdown Files (Part 2 of 3)</title><link href="https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-2.html" rel="alternate" type="text/html" title="Intro to Emacs Lisp: Adding Live Previews when Editing Markdown Files (Part 2 of 3)" /><published>2020-06-09T23:12:00+00:00</published><updated>2020-06-09T23:12:00+00:00</updated><id>https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-2</id><content type="html" xml:base="https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-2.html">&lt;p&gt;This post is part two of a series of 3 posts. View the other parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-1.html&quot;&gt;Part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-2.html&quot;&gt;Part 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/10/emacs-lisp-intro-markdown-live-previews-part-3.html&quot;&gt;Part 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post we’ll make a series of improvements to the &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/preview-markdown&lt;/code&gt; command, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Rendering in a way that images and other files relative to the directory
containing the Markdown source work correctly&lt;/li&gt;
  &lt;li&gt;Preserving the current window&lt;/li&gt;
  &lt;li&gt;Making &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt; read-only&lt;/li&gt;
  &lt;li&gt;Scrolling the preview so it’s at approximately the same location as the Markdown buffer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll also look at the following Emacs Lisp concepts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Lists, cons cells, and association lists (briefly)&lt;/li&gt;
  &lt;li&gt;Backquote-splicing&lt;/li&gt;
  &lt;li&gt;Macros&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;viewing-source&quot;&gt;Viewing Source&lt;/h3&gt;

&lt;p&gt;A side note about how I figured out which functions to use to parse and render
HTML: SHR isn’t terribly well documented. I wasn’t sure how to get it to do what
I wanted until I poked around the source for EWW (the Emacs Web “Wowser”).&lt;/p&gt;

&lt;p&gt;I strongly recommend you install a package like
&lt;a href=&quot;https://github.com/purcell/elisp-slime-nav&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;elisp-slime-nav&lt;/code&gt;&lt;/a&gt; that makes it
easy to jump to the source code for functions. When editing Emacs Lisp,
&lt;code class=&quot;highlighter-rouge&quot;&gt;elisp-slime-nav&lt;/code&gt; binds &lt;code class=&quot;highlighter-rouge&quot;&gt;M-.&lt;/code&gt; to jump to the source of a function or variable;
if your cursor is over a symbol, it will jump straight to its definition.&lt;/p&gt;

&lt;h1 id=&quot;improving-the-preview-markdown-command&quot;&gt;Improving the &lt;code class=&quot;highlighter-rouge&quot;&gt;preview-markdown&lt;/code&gt; command&lt;/h1&gt;

&lt;h3 id=&quot;rendering-relative-to-the-current-directory&quot;&gt;Rendering relative to the current directory&lt;/h3&gt;

&lt;p&gt;Now that the meat and potatoes of our command are finished it’s time to polish
it up and fix some of the annoyances. One issue with the preview is that links
and images with paths relative to the markdown document don’t work correctly in
our preview. We can fix this by prepending the parsed &lt;code class=&quot;highlighter-rouge&quot;&gt;document&lt;/code&gt; with a another
list that contains information as the base URL.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Rendering Markdown preview of %s&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shell-command-on-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pandoc -f gfm&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-selected-window&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;switch-to-buffer-other-window&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;libxml-parse-html-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;;; [1] Create the base URL&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;file://&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;erase-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;;; [2] wrap `document` in a &amp;lt;base&amp;gt; element&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shr-insert-document&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;goto-char&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First, we need to create a &lt;code class=&quot;highlighter-rouge&quot;&gt;file://&lt;/code&gt; URL that SHR can use as the base URL for
images or other elements with a relative path. If our original file is
&lt;code class=&quot;highlighter-rouge&quot;&gt;/home/cam/README.markdown&lt;/code&gt;, our URL should be
&lt;code class=&quot;highlighter-rouge&quot;&gt;file:///home/cam/README.markdown&lt;/code&gt;. We can use &lt;code class=&quot;highlighter-rouge&quot;&gt;concat&lt;/code&gt; to concatenate multiple
arguments together into a single string:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;file://&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/home/cam/README.markdown&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; &quot;file:///home/cam/README.markdown&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, we need to wrap &lt;code class=&quot;highlighter-rouge&quot;&gt;document&lt;/code&gt; in a list that includes the base URL.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;document&lt;/code&gt; itself is simply an Emacs Lisp representation of an HTML/XML data structure.
It has the basic format&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;element-name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;child-elements...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So for example HTML like&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is &lt;span class=&quot;nt&quot;&gt;&amp;lt;span&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;important&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;IMPORTANT&lt;span class=&quot;nt&quot;&gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;would be represented as&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;This is &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;span&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;important&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;IMPORTANT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Try it yourself. &lt;code class=&quot;highlighter-rouge&quot;&gt;M-:&lt;/code&gt; and then type or paste the following form&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shr-insert-document&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;This is &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;span&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;important&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;IMPORTANT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;to render it and insert it into the current buffer.&lt;/p&gt;

&lt;h4 id=&quot;quoting&quot;&gt;Quoting&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Quoting&lt;/em&gt; a form tells Emacs Lisp not evaluate it, and to instead return it
as-is:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 3&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; (+ 1 2)&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; &quot;/home/cam/README.md&quot;&lt;/span&gt;

&lt;span class=&quot;ss&quot;&gt;'buffer-file-name&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; buffer-file-name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is especially useful for passing in lists, which would otherwise get
interpreted as function calls, or symbols, which would otherwise get interpreted
as function names or variables. The &lt;code class=&quot;highlighter-rouge&quot;&gt;'&lt;/code&gt; syntax is shorthand for the &lt;code class=&quot;highlighter-rouge&quot;&gt;(quote)&lt;/code&gt; form:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;quote&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; (+ 1 2)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;understanding-lists-cons-cells-and-association-lists&quot;&gt;Understanding Lists, Cons Cells, and Association Lists&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;attributes&lt;/code&gt; are an &lt;em&gt;association list&lt;/em&gt;, which is a classic Lisp way to store a
map/dictionary as a list of key/value pairs. A deep dive into association lists
is outside the scope of this article, but we can go over them briefly. But
first, we should take a step back and look at how lists work in Lisp:&lt;/p&gt;

&lt;p&gt;A Lisp list is composed of &lt;em&gt;cons cells&lt;/em&gt;, which is Lisp-speak for linked list
nodes. A cons cell has two pointers: one to an item, and one to the next cons
cell. If a cons cell holds the last item in a list, its pointer to the next cell is &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt;. A cons
cell is written with the &lt;em&gt;dotted pair&lt;/em&gt; syntax &lt;code class=&quot;highlighter-rouge&quot;&gt;(x . y)&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; (1)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cons&lt;/code&gt; can also be used to create cons cells and is equivalent to the dotted
pair syntax.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; (1)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Very important! Note that &lt;code class=&quot;highlighter-rouge&quot;&gt;(1 . nil)&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;(cons 1 nil)&lt;/code&gt; are the same thing as the list &lt;code class=&quot;highlighter-rouge&quot;&gt;(1)&lt;/code&gt;. A
list with a single item is just a cons cell whose first pointer is to the item,
and whose second pointer is &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can chain together dotted pair forms or calls to &lt;code class=&quot;highlighter-rouge&quot;&gt;cons&lt;/code&gt; to create lists if you like doing
things the hard way.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; (1 2 3)&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; (1 2 3)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One more thing to note: to add an item to the front of an existing list, you can use &lt;code class=&quot;highlighter-rouge&quot;&gt;cons&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; (1 2 3 4)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The newly created cons cell’s item and next-cons-cell pointers are to &lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt; and
the list &lt;code class=&quot;highlighter-rouge&quot;&gt;(2 3 4)&lt;/code&gt; respectively. This is not a destructive operation – the
original list of &lt;code class=&quot;highlighter-rouge&quot;&gt;(2 3 4)&lt;/code&gt; does not need to be modified in any way. Prepending
items to a list via &lt;code class=&quot;highlighter-rouge&quot;&gt;cons&lt;/code&gt; is a common operation in classic Lisp languages
(not so much in Clojure).&lt;/p&gt;

&lt;h4 id=&quot;other-uses-of-cons-cells&quot;&gt;Other uses of cons cells&lt;/h4&gt;

&lt;p&gt;In some cases, both pointers in a cons cell are used to point to items.
Association lists are one such example: an association list is a list of cons
cells, where each cell represents a &lt;code class=&quot;highlighter-rouge&quot;&gt;(key . value)&lt;/code&gt; pair.&lt;/p&gt;

&lt;p&gt;So a JSON dictionary like&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;could be translated to an association list like&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;wrapping-our-html-document&quot;&gt;Wrapping our HTML &lt;code class=&quot;highlighter-rouge&quot;&gt;document&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;With that big explanation out of the way, we need to take our &lt;code class=&quot;highlighter-rouge&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;&lt;/code&gt; element
stored in &lt;code class=&quot;highlighter-rouge&quot;&gt;document&lt;/code&gt; and wrap it in a &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;base&amp;gt;&lt;/code&gt; form, so it looks like this:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;base&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;file:///home/cam/README.markdown&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;...&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/base&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the Lisp representation, this means we need the list&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;file:///home/cam/README.markdown&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are a few ways to create such a list. None are particularly convenient,
because you need to quote symbols &lt;code class=&quot;highlighter-rouge&quot;&gt;base&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;href&lt;/code&gt; so the reader doesn’t try to
evaluate them, but you can’t quote the entire form, because you need to splice in
the URL we created, as well as the value of &lt;code class=&quot;highlighter-rouge&quot;&gt;document&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'base&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'href&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;is one option:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;my doc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;my url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'base&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'href&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; (base ((href . &quot;my url&quot;)) &quot;my doc&quot;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;backquote-splicing&quot;&gt;Backquote-splicing&lt;/h4&gt;

&lt;p&gt;Luckily, we can use &lt;em&gt;backtick&lt;/em&gt; or &lt;em&gt;backquote-splicing&lt;/em&gt;, which is more fun. As
mentioned before, we want something like &lt;code class=&quot;highlighter-rouge&quot;&gt;(base ((href . url)) document)&lt;/code&gt;, but
quoting the entire form e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;'(base ((href . url)) document)&lt;/code&gt; won’t work
because then &lt;code class=&quot;highlighter-rouge&quot;&gt;url&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;document&lt;/code&gt; would be quoted as well and thus wouldn’t get
evaluated. Backquote splicing lets you quote things but selectively disable
quoting:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;my doc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;my url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;;-&amp;gt; (base ((href . &quot;my url&quot;)) &quot;my doc&quot;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The backtick starts backquote-splicing, which you can think of as like switching
on a “quote mode” that lasts for the entire form; commas nested temporarily
inside disable quote mode for the next form. (For those familiar with Clojure:
&lt;code class=&quot;highlighter-rouge&quot;&gt;,&lt;/code&gt; is the traditional Lisp equivalent of Clojure’s &lt;code class=&quot;highlighter-rouge&quot;&gt;~&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;You can nest backticks forms as well – in other words, you can turn “quote
mode” back on and off inside nested forms as you see fit:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cons&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; (1 (2 1 0))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;splicing-items-into-the-parent-form-with-&quot;&gt;Splicing items into the parent form with &lt;code class=&quot;highlighter-rouge&quot;&gt;,@&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;There’s another splicing operator: &lt;code class=&quot;highlighter-rouge&quot;&gt;,@&lt;/code&gt;, which lets you splice the items in a
list directly into their parent form:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; splice the list directly&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-list&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; ((1 2 3) 4 5)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; splice the items in the list into the parent list&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-list&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; (1 2 3 4 5)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Backquote splicing is essential when writing macros, so we’ll revisit it in a
future post.&lt;/p&gt;

&lt;h3 id=&quot;preserving-the-current-window&quot;&gt;Preserving the current window&lt;/h3&gt;

&lt;p&gt;Right now, our command switches to the window containing the rendered HTML every
time it runs, so you have to switch back to the window with the Markdown file to
continue editing it. We eventually want this command to run automatically whenever we save
a file, and this behavior will get pretty annoying.&lt;/p&gt;

&lt;p&gt;We can use &lt;code class=&quot;highlighter-rouge&quot;&gt;save-selected-window&lt;/code&gt; to restore the current window after evaluating a series of forms:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Rendering Markdown preview of %s&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shell-command-on-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pandoc -f gfm&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-selected-window&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;switch-to-buffer-other-window&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;libxml-parse-html-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;file://&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;erase-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shr-insert-document&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;goto-char&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To recap: &lt;code class=&quot;highlighter-rouge&quot;&gt;switch-to-buffer-other-window&lt;/code&gt; switches to a different window and
brings up the &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt; buffer, and &lt;code class=&quot;highlighter-rouge&quot;&gt;shr-insert-document&lt;/code&gt;
renders the GUI widgets into the current buffer. After that, we’re free to
switch back to the original window. &lt;code class=&quot;highlighter-rouge&quot;&gt;save-selected-window&lt;/code&gt; will preserve the
selected window and current buffer before the forms it wraps are executed; the
forms are then executed normally, and &lt;code class=&quot;highlighter-rouge&quot;&gt;save-selected-widnow&lt;/code&gt; restores the
selected window and current buffer.&lt;/p&gt;

&lt;h3 id=&quot;making-preview-markdown-output-read-only&quot;&gt;Making &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt; read-only&lt;/h3&gt;

&lt;p&gt;Since &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt; contains rendered HTML widgets derived from a
separate source file, it’s probably less confusing if we make &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt;
 a read-only buffer. Whether a buffer is read-only is determined by the
variable &lt;code class=&quot;highlighter-rouge&quot;&gt;buffer-read-only&lt;/code&gt;, which automatically becomes buffer-local if set.
This means when you set this variable, you are setting it only for the current
buffer; the current buffer’s value with overshadow the global value.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell-command-on-region&lt;/code&gt; will “helpfully” clear the read-only flag when it
writes its output to &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt;, so we don’t have to worry
about clearing it out if we end up reusing the buffer after running the command
a second time.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Rendering Markdown preview of %s&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shell-command-on-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pandoc -f gfm&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-selected-window&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;switch-to-buffer-other-window&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;libxml-parse-html-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;file://&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;erase-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shr-insert-document&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;goto-char&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;;; [1] Make buffer read-only&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-read-only&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To change the value of a variable, you can use &lt;code class=&quot;highlighter-rouge&quot;&gt;setq&lt;/code&gt;. It’s the equivalent of &lt;code class=&quot;highlighter-rouge&quot;&gt;=&lt;/code&gt; assignment in Algol-style languages:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// javascript&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; emacs-lisp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;q&lt;/code&gt; in &lt;code class=&quot;highlighter-rouge&quot;&gt;setq&lt;/code&gt; stands for &lt;em&gt;quote&lt;/em&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;setq&lt;/code&gt; in the example above is actually
equivalent to:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;'x&lt;/code&gt; is quoted because we’re setting the value named by the symbol &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt;. We want&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;x = 10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;rather than&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[current value of x] = 10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;which probably won’t make sense.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;'x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 100&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;vg&quot;&gt;***&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Eval&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;vg&quot;&gt;***&lt;/span&gt;  &lt;span class=&quot;nv&quot;&gt;Wrong&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;argument:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;symbolp,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This error means that it expected a symbol (i.e., something that satisfied the
&lt;code class=&quot;highlighter-rouge&quot;&gt;symbolp&lt;/code&gt; predicate function) but got &lt;code class=&quot;highlighter-rouge&quot;&gt;100&lt;/code&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;100 = 200&lt;/code&gt; doesn’t really make
sense.&lt;/p&gt;

&lt;h4 id=&quot;macros-101&quot;&gt;Macros 101&lt;/h4&gt;

&lt;p&gt;So how does &lt;code class=&quot;highlighter-rouge&quot;&gt;setq&lt;/code&gt; work if the variable name needs to be quoted? With macros,
you can take the arguments you’re passed, &lt;em&gt;before they are evaluated&lt;/em&gt;, and
generate whatever code you want. This new code is used in place of the macro form.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;setq&lt;/code&gt; could be implemented as a macro that quotes the variable name symbol.
(&lt;code class=&quot;highlighter-rouge&quot;&gt;setq&lt;/code&gt; is &lt;em&gt;actually&lt;/em&gt; a built-in special form, but you can implement similar
behavior with a macro). Here’s a simple &lt;code class=&quot;highlighter-rouge&quot;&gt;setq&lt;/code&gt;-style macro:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defmacro&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-setq&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;symbol&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;',symbol&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The important thing to know about Lisp macros is that they’re just functions
that take arguments and return a new Lisp form to be used in their place. Macros
and regular functions essentially differ only in when they’re evaluated, and
what’s done with the result, but everything else works the same. Macros can call
other functions, and more complex macros often have parts of their
implementation split out into separate functions, defined with &lt;code class=&quot;highlighter-rouge&quot;&gt;defun&lt;/code&gt; like any
other function.&lt;/p&gt;

&lt;p&gt;Evaluating a macro form and replacing it with the result is called macro
“expansion”. When a form is evaluated, all macros are expanded first (top-level
macro forms are expanded before ones nested inside them), and only &lt;em&gt;then&lt;/em&gt; are
variables and function calls evaluated.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;(set 'x 100)&lt;/code&gt; is just a list with the elements &lt;code class=&quot;highlighter-rouge&quot;&gt;set&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;'x&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;100&lt;/code&gt;, so if we
have a macro generate that sort of list for us our dreams will come true. In the
example above, we use backquote splicing to construct our list.&lt;/p&gt;

&lt;h5 id=&quot;macroexpansion&quot;&gt;Macroexpansion&lt;/h5&gt;

&lt;p&gt;You can use &lt;code class=&quot;highlighter-rouge&quot;&gt;macroexpand&lt;/code&gt; to expand a macro form to see the Lisp form it expands to:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;macroexpand&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-variable&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; (set 'my-variable 200)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that you need to quote the form passed so it doesn’t get evaluated before
&lt;code class=&quot;highlighter-rouge&quot;&gt;macroexpand&lt;/code&gt; sees it.&lt;/p&gt;

&lt;p&gt;Macros can of course expand to other macros, and Emacs Lisp will continue
expanding things until there are no more macros to expand. To expand a macro
form once instead of completely, you can use &lt;code class=&quot;highlighter-rouge&quot;&gt;macroexpand-1&lt;/code&gt; instead of
&lt;code class=&quot;highlighter-rouge&quot;&gt;macroexpand&lt;/code&gt;. In the example above, the result of &lt;code class=&quot;highlighter-rouge&quot;&gt;my-setq&lt;/code&gt; is already fully
expanded, so both functions give you the same result.&lt;/p&gt;

&lt;p&gt;When writing macros, some tools you might you might find the built-in
&lt;code class=&quot;highlighter-rouge&quot;&gt;pp-macroexpand-last-sexp&lt;/code&gt; command useful; it pretty-prints the results of a
macro expansion. I’m also a big fan of the
&lt;a href=&quot;https://github.com/joddie/macrostep&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;macrostep&lt;/code&gt;&lt;/a&gt; package, which lets you view
the expansions of macros directly in your source code.&lt;/p&gt;

&lt;h3 id=&quot;scrolling-to-approximate-location-in-preview&quot;&gt;Scrolling to approximate location in preview&lt;/h3&gt;

&lt;p&gt;It’s a little annoying to work on a giant Markdown file like this blog post and
have the preview always scroll to the very top. Why not just have it scroll
approximately the same position we’re currently looking at?&lt;/p&gt;

&lt;p&gt;Here’s the quick and dirty solution we’ll implement:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;When first running our command, while the Markdown buffer is still current,
record how far we’ve scrolled thru the document (e.g. 40%).&lt;/li&gt;
  &lt;li&gt;After we’ve rendered the HTML as GUI widgets, scroll to the line that is
approximately the same distance thru the document.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This will work even if the rendered output has a lot more or less lines than the
Markdown source. For example, if our Markdown source file is 1000 lines, and the
rendered output 800 lines, and the top of the window shows line 400, we’d record
a &lt;code class=&quot;highlighter-rouge&quot;&gt;scroll-percentage&lt;/code&gt; of 0.4 (40% scrolled); in the rendered output, we’ll
scroll to line 320 (800 * 0.4).&lt;/p&gt;

&lt;p&gt;This isn’t perfect, since there isn’t a 1:1 translation between Markdown text
and rendered HTML lines, but in my experience it works well enough that I
haven’t bothered creating a more sophisticated version.&lt;/p&gt;

&lt;p&gt;For readability, I broke the command we’ve been working on out into a few
separate functions. Since Emacs Lisp doesn’t have encapsulation features like
private functions, functions intended to be private are often given a name with
a dash after the “namespace” part of the name, such as &lt;code class=&quot;highlighter-rouge&quot;&gt;package--function&lt;/code&gt; or
&lt;code class=&quot;highlighter-rouge&quot;&gt;package/-function&lt;/code&gt;. Thus I named our “private” functions with the pattern
&lt;code class=&quot;highlighter-rouge&quot;&gt;cam/-function-name&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/-scroll-percentage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line-number-at-pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;window-start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line-number-at-pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/-set-window-start-to-percentage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;scroll-percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; Move to the beginning of the rendered output buffer&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;goto-char&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; target line number is floor(total-number-of lines * scroll-percentage)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;target-line-number&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;truncate&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line-number-at-pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scroll-percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;;; move to target line number&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;forward-line&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;1-&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;target-line-number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; now scroll the window so the line in question is at the top&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;set-window-start&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;file://&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;;; record how far thru the Markdown source file we've scrolled&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;scroll-percentage&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/-scroll-percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Rendering Markdown preview of %s&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shell-command-on-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pandoc -f gfm&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;save-selected-window&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;switch-to-buffer-other-window&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;libxml-parse-html-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;erase-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shr-insert-document&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;href&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cam/-set-window-start-to-percentage&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scroll-percentage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-read-only&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is pretty straightforward. To calculate the scroll percentage, we:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Use &lt;code class=&quot;highlighter-rouge&quot;&gt;(window-start)&lt;/code&gt; to get the position of the first character visible in
the current window&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Use &lt;code class=&quot;highlighter-rouge&quot;&gt;line-number-at-pos&lt;/code&gt; to convert the position to a line number.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Calculate the total number of lines by getting the line number of the last
character in the buffer by calling &lt;code class=&quot;highlighter-rouge&quot;&gt;(line-number-at-pos (point-max))&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Divide the window start line by the last line to get a percentage. As in C,
integer division is truncated. By first casting the integers to
floating-point numbers with &lt;code class=&quot;highlighter-rouge&quot;&gt;(float)&lt;/code&gt; we can use floating-point division
instead; the final result is a number like &lt;code class=&quot;highlighter-rouge&quot;&gt;0.4&lt;/code&gt;.&lt;/p&gt;

    &lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 0&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 0.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the output is rendered, to scroll to the line the desired line, we:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Move the cursor to the beginning of the buffer by calling &lt;code class=&quot;highlighter-rouge&quot;&gt;(goto-char (point-min))&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Calculate the total number of lines using &lt;code class=&quot;highlighter-rouge&quot;&gt;(line-number-at-pos (point-max))&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Multiply the total number of lines by &lt;code class=&quot;highlighter-rouge&quot;&gt;scroll-percentage&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Call &lt;code class=&quot;highlighter-rouge&quot;&gt;truncate&lt;/code&gt; to convert the resulting floating point number to an
integer. The result of this is our &lt;code class=&quot;highlighter-rouge&quot;&gt;target-line-number&lt;/code&gt;.&lt;/p&gt;

    &lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;truncate&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;20.5432&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Next, we move the cursor to our target line. Since we already moved to line
1 in step 1, we need to move forward by &lt;code class=&quot;highlighter-rouge&quot;&gt;target-line-number - 1&lt;/code&gt; lines.
&lt;code class=&quot;highlighter-rouge&quot;&gt;forward-line&lt;/code&gt; is used to move forward a number of lines. For example, if we
want to move from line 1 to line 20, we can call &lt;code class=&quot;highlighter-rouge&quot;&gt;(forward-line 19)&lt;/code&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;1-&lt;/code&gt;
function returns its argument minus one.&lt;/p&gt;

    &lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;1-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 19&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Now that the cursor is at the correct position, we can scroll the window so
the first line is the one with the cursor. &lt;code class=&quot;highlighter-rouge&quot;&gt;(point)&lt;/code&gt; gets the current
position of the cursor (the &lt;em&gt;point&lt;/em&gt;) and &lt;code class=&quot;highlighter-rouge&quot;&gt;set-window-start&lt;/code&gt; scrolls the
window so it shows that position in the first line (“start”) of the window.
Nice!&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h1&gt;

&lt;p&gt;In &lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/10/emacs-lisp-intro-markdown-live-previews-part-3.html&quot;&gt;Part 3&lt;/a&gt;, we’ll have
the command prompt us for a file to preview when running interactively, and have
Emacs run it automatically whenever we save a Markdown file. We’ll discuss
optional arguments, &lt;code class=&quot;highlighter-rouge&quot;&gt;interactive&lt;/code&gt; code characters, &lt;code class=&quot;highlighter-rouge&quot;&gt;progn&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; forms, hooks,
buffer-local variables, and Lisp-2s.&lt;/p&gt;</content><author><name></name></author><summary type="html">This post is part two of a series of 3 posts. View the other parts:</summary></entry><entry><title type="html">Intro to Emacs Lisp: Adding Live Previews when Editing Markdown Files (Part 1 of 3)</title><link href="https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-1.html" rel="alternate" type="text/html" title="Intro to Emacs Lisp: Adding Live Previews when Editing Markdown Files (Part 1 of 3)" /><published>2020-06-09T20:12:00+00:00</published><updated>2020-06-09T20:12:00+00:00</updated><id>https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-1</id><content type="html" xml:base="https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-1.html">&lt;p&gt;This post is part one of a series of 3 posts. View the other parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-1.html&quot;&gt;Part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-2.html&quot;&gt;Part 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/10/emacs-lisp-intro-markdown-live-previews-part-3.html&quot;&gt;Part 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the first post in what I hope is an ongoing series of posts that show
you how to get started with Emacs Lisp by writing practical Emacs commands and
customizing Emacs in other ways. For me, one of the best things about Emacs is
that you can customize it to work however you want, but learning how to do so is
easier said than done, and while the official Emacs Lisp documentation is
extremely detailed, it can be hard to approach.&lt;/p&gt;

&lt;p&gt;I’m hoping I can demonstrate that Emacs Lisp is fun to write and not as hard as
you might think. I’m going to assume experience using Emacs. For example, you
should have a basic understanding of things like &lt;em&gt;windows&lt;/em&gt; and &lt;em&gt;buffers&lt;/em&gt;, and know
how to read and type keyboard shortcuts written in the Emacs style (&lt;code class=&quot;highlighter-rouge&quot;&gt;C-x C-s&lt;/code&gt; the world).&lt;/p&gt;

&lt;p&gt;I will also assume experience programming in other languages, but not
necessarily Lisp ones; however, you should still have a very basic
understanding of how Lisp languages work, for example what the result of &lt;code class=&quot;highlighter-rouge&quot;&gt;(+ 1 2)&lt;/code&gt;
is. If you don’t, the &lt;a href=&quot;https://www.gnu.org/software/emacs/manual/eintr.html&quot;&gt;GNU Intro to Emacs Lisp&lt;/a&gt;
is a good starting point.&lt;/p&gt;

&lt;p&gt;In this series of posts I’m going to show you step-by-step how to set write a
command to render a preview of a Markdown file and open it directly in Emacs.
Then I’ll show you how to have it run automatically whenever you save a Markdown
file.&lt;/p&gt;

&lt;p&gt;For this tutorial you’ll want to install &lt;a href=&quot;https://pandoc.org/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;pandoc&lt;/code&gt;&lt;/a&gt;
which we will use for rendering Markdown files, Emacs 25 or newer, and the
&lt;a href=&quot;https://github.com/jrblevin/markdown-mode&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;markdown-mode&lt;/code&gt;&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/assets/emacs-markdown-live-preview.gif&quot;&gt;&lt;img src=&quot;/assets/emacs-markdown-live-preview.gif&quot; alt=&quot;Preview&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;evaluating-forms-and-ielm&quot;&gt;Evaluating forms and &lt;code class=&quot;highlighter-rouge&quot;&gt;ielm&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Before we get started, I want to briefly mention how to follow along with the
code in this post. For the functions we’re working on, you’ll probably want to
add them to your &lt;code class=&quot;highlighter-rouge&quot;&gt;~/.emacs.d/init.el&lt;/code&gt; file. You can evaluate function
definitions by moving the cursor to some point in the function definition and
then doing &lt;code class=&quot;highlighter-rouge&quot;&gt;C-M-x&lt;/code&gt;. You can also evaluate the form immediately preceding your
cursor with the key binding &lt;code class=&quot;highlighter-rouge&quot;&gt;C-x C-e&lt;/code&gt;. Give them a try!&lt;/p&gt;

&lt;p&gt;A big part of Lisp development is using a REPL. &lt;code class=&quot;highlighter-rouge&quot;&gt;ielm&lt;/code&gt; is the Emacs Lisp REPL.
You can start it with &lt;code class=&quot;highlighter-rouge&quot;&gt;M-x ielm&lt;/code&gt; and use it to evaluate Emacs Lisp forms. This
is especially useful for evaluating the smaller bits of code like &lt;code class=&quot;highlighter-rouge&quot;&gt;(+ 1 2)&lt;/code&gt; that
eventually make it in to larger functions.&lt;/p&gt;

&lt;h1 id=&quot;writing-a-preview-markdown-command&quot;&gt;Writing a &lt;code class=&quot;highlighter-rouge&quot;&gt;preview-markdown&lt;/code&gt; command&lt;/h1&gt;

&lt;p&gt;Without further ado, let’s start writing our command.&lt;/p&gt;

&lt;h3 id=&quot;defining-a-function&quot;&gt;Defining a function&lt;/h3&gt;

&lt;p&gt;Let’s start off with an empty function declaration:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’re completely new to Emacs Lisp, especially if you come from a Clojure
background, you might think the &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/&lt;/code&gt; portion of the name of the function has
some sort of special significance. It doesn’t! Emacs Lisp doesn’t have the
notion of separate namespaces that other languages have, so every function and
variable name is global; like Objective-C, it’s a best-practice to prefix all
function and variable names with something that makes it clear they’re part of a
particular package. Usually you’ll see names prefixed with &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;package-name&amp;gt;-&lt;/code&gt;,
for example &lt;code class=&quot;highlighter-rouge&quot;&gt;helm-mark-ring&lt;/code&gt;, but &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;package-name&amp;gt;/&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;package-name&amp;gt;:&lt;/code&gt; are
reasonably common conventions as well. The &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; character itself has no special
significance in symbol names. I prefer prefixing all of my own functions and
macros with &lt;code class=&quot;highlighter-rouge&quot;&gt;cam/&lt;/code&gt;, so I’ll use that in the code in this post.&lt;/p&gt;

&lt;h3 id=&quot;making-it-a-command&quot;&gt;Making it a command&lt;/h3&gt;

&lt;p&gt;Now this function itself doesn’t do anything, and you can’t invoke it with
&lt;code class=&quot;highlighter-rouge&quot;&gt;M-x&lt;/code&gt;. Emacs Lisp functions can either be invoked programmatically (e.g. by
other Emacs Lisp functions, or the &lt;code class=&quot;highlighter-rouge&quot;&gt;ielm&lt;/code&gt; REPL, or via &lt;code class=&quot;highlighter-rouge&quot;&gt;M-:&lt;/code&gt;) or &lt;em&gt;interactively&lt;/em&gt;
(via &lt;code class=&quot;highlighter-rouge&quot;&gt;M-x&lt;/code&gt; or a keybinding). A function that be invoked interactively is known
as a &lt;em&gt;command&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To make a function a command, you add an &lt;code class=&quot;highlighter-rouge&quot;&gt;interactive&lt;/code&gt; declaration, which always
goes right after the argument list (or docstring, if you have one):&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now the function can be invoked interactively. Try it out!
&lt;code class=&quot;highlighter-rouge&quot;&gt;M-x cam/preview-markdown&lt;/code&gt;. Cool! But it still doesn’t do
anything.&lt;/p&gt;

&lt;h3 id=&quot;logging-messages&quot;&gt;Logging Messages&lt;/h3&gt;

&lt;p&gt;Let’s start by having our command log a message. &lt;code class=&quot;highlighter-rouge&quot;&gt;message&lt;/code&gt; is the Emacs Lisp
equivalent of &lt;code class=&quot;highlighter-rouge&quot;&gt;printf&lt;/code&gt;, so this is sort of a “Hello World” example. Exciting!
We’ll add a call &lt;code class=&quot;highlighter-rouge&quot;&gt;message&lt;/code&gt; in the function body:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Rendering Markdown preview of %s&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The variable &lt;code class=&quot;highlighter-rouge&quot;&gt;buffer-file-name&lt;/code&gt; is bound to the string name of the file associated with
the current buffer (i.e., the one from which you invoked the command). Create a
markdown file and switch to it in Emacs, then run &lt;code class=&quot;highlighter-rouge&quot;&gt;M-x cam/preview-markdown&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;You’ll see something like&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Rendering Markdown preview of /home/cam/README.markdown
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;appear in the minibuffer, and it will be appended to the &lt;code class=&quot;highlighter-rouge&quot;&gt;*Messages*&lt;/code&gt; buffer as
well. When writing any code, it’s important to be able to debug it, and until we
talk about using the Emacs Lisp debugger being able to print stuff out is a good first step.&lt;/p&gt;

&lt;h3 id=&quot;calling-external-commands&quot;&gt;Calling external commands&lt;/h3&gt;

&lt;p&gt;Our plan is to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Take the markdown content in the current buffer and render it as HTML to a buffer named &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Parse the HTML in &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Clear &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt; and then render the pretty GUI widgets
like you saw in the GIF at the start of this post.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So let’s add a command to render the Markdown to HTML using the external &lt;code class=&quot;highlighter-rouge&quot;&gt;pandoc&lt;/code&gt; command:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Rendering Markdown preview of %s&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shell-command-on-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pandoc -f gfm&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;shell-command-on-region&lt;/code&gt; runs a command (&lt;code class=&quot;highlighter-rouge&quot;&gt;pandoc -f gfm&lt;/code&gt;) on a &lt;em&gt;region&lt;/em&gt;. The
simplest explanation of a region is that it’s all the text between two positions
in a buffer – if you think of the current buffer as one giant string, then a
region would be a substring.&lt;/p&gt;

&lt;p&gt;Since we want to render the entire buffer as HTML rather than just part of it, we want
our region to be the entire buffer. The functions &lt;code class=&quot;highlighter-rouge&quot;&gt;point-min&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;point-max&lt;/code&gt;
get the minimum possible position and maximum possible position in the current
buffer respectively; together those positions represent the region we’re operating on.&lt;/p&gt;

&lt;p&gt;The final argument to &lt;code class=&quot;highlighter-rouge&quot;&gt;shell-command-on-region&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/code&gt;,
tells it the name of the buffer to write the output of the shell command to
(creating it if it doesn’t already exist). If the buffer already exists,
&lt;code class=&quot;highlighter-rouge&quot;&gt;shell-command-on-region&lt;/code&gt; will clear any existing contents of the buffer before
writing the command output.&lt;/p&gt;

&lt;p&gt;Emacs Lisp buffers used for internally or for other special purposes are conventionally given names with
asterisks (affectionately known as &lt;em&gt;earmuffs&lt;/em&gt;), but you could name the buffer
anything you want.&lt;/p&gt;

&lt;p&gt;Go ahead and give it a try in your Markdown file buffer, and you’ll see a buffer
called &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt; pop up with the HTML output.&lt;/p&gt;

&lt;p&gt;Before moving on, let’s take a quick break to discuss some essentials that
you’ll really want to know when writing Emacs Lisp.&lt;/p&gt;

&lt;h3 id=&quot;looking-up-documentation&quot;&gt;Looking up documentation&lt;/h3&gt;

&lt;p&gt;To look up documentation for a function, you can use &lt;code class=&quot;highlighter-rouge&quot;&gt;C-h f &amp;lt;name-of-function&amp;gt;&lt;/code&gt;.
If you want to write Emacs Lisp, commit that keybinding to memory right away –
you’ll be using it all the time. Try using it to learn more about one of the
functions we’re using.&lt;/p&gt;

&lt;p&gt;Similarly, &lt;code class=&quot;highlighter-rouge&quot;&gt;C-h v&lt;/code&gt; opens documentation for a variable, and &lt;code class=&quot;highlighter-rouge&quot;&gt;C-h k&lt;/code&gt; will tell you
the function that gets run for a given keybinding. Write these down on a Post-It
and stick it to your computer until they are burned into your brain.&lt;/p&gt;

&lt;h3 id=&quot;evaluating-emacs-lisp-from-the-minibuffer-with-m-&quot;&gt;Evaluating Emacs Lisp from the minibuffer with &lt;code class=&quot;highlighter-rouge&quot;&gt;M-:&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Another keybinding you’ll want to commit to memory is &lt;code class=&quot;highlighter-rouge&quot;&gt;M-:&lt;/code&gt;, which allows you to
evaluate an Emacs Lisp form from the minibuffer. The code is evaluated in the
context of the current buffer. If you want to look at certain
values of variables or test how certain code works when evaluated in a certain
buffer, &lt;code class=&quot;highlighter-rouge&quot;&gt;M-:&lt;/code&gt; is one of the easiest ways to do so.&lt;/p&gt;

&lt;p&gt;Try it from you markdown buffer – &lt;code class=&quot;highlighter-rouge&quot;&gt;M-:&lt;/code&gt; and then type or paste
&lt;code class=&quot;highlighter-rouge&quot;&gt;buffer-file-name&lt;/code&gt; and hit return. The output is visible in the minibuffer and
&lt;code class=&quot;highlighter-rouge&quot;&gt;*Messages*&lt;/code&gt;. Try it with a different buffer – you’ll see a different result.&lt;/p&gt;

&lt;h3 id=&quot;viewing-the-html&quot;&gt;Viewing the HTML&lt;/h3&gt;

&lt;p&gt;We can use the built-in Simple HTML Renderer (SHR) to render HTML directly
in Emacs. Change our function to the following and give this a try:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;defun&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;cam/preview-markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;interactive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; [1] bind the value of buffer-file-name to local variable filename&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Rendering Markdown preview of %s&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shell-command-on-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;pandoc -f gfm&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;;; [2] switch to a different window and bring up the *Preview Markdown Output* buffer&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;switch-to-buffer-other-window&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;;; [3] Parse the HTML source in *Preview Markdown Output*, and store it in document&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;libxml-parse-html-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;;; [4] Clear the contents of *Preview Markdown Output*&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;erase-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;;; [5] Render the parsed HTML into *Preview Markdown Output*&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shr-insert-document&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;;; [6] Move back to the beginning of the buffer&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;goto-char&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re doing a few new things here, and I’ll explain each in turn:&lt;/p&gt;

&lt;h4 id=&quot;binding-local-variables-with-let&quot;&gt;Binding local variables with &lt;code class=&quot;highlighter-rouge&quot;&gt;let&lt;/code&gt;&lt;/h4&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Not all buffers are associated with files. &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt; isn’t
actually associated with a file, so &lt;code class=&quot;highlighter-rouge&quot;&gt;buffer-file-name&lt;/code&gt; will be &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt; if we
evaluate it with &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt; as the current buffer. To work
around this, we can evaluate &lt;code class=&quot;highlighter-rouge&quot;&gt;buffer-file-name&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; we switch buffers and
save it for later as the local variable &lt;code class=&quot;highlighter-rouge&quot;&gt;filename&lt;/code&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;(let ...)&lt;/code&gt; is used to
introduce local bindings.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; x = 100&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You might be wondering why you need two sets of parentheses to set &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; in the
example above. The syntax for &lt;code class=&quot;highlighter-rouge&quot;&gt;let&lt;/code&gt; is&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;bindings&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;where &lt;code class=&quot;highlighter-rouge&quot;&gt;bindings&lt;/code&gt; is a list of bindings of the form &lt;code class=&quot;highlighter-rouge&quot;&gt;(symbol value)&lt;/code&gt;, e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;(x 100)&lt;/code&gt;.
This means you can pass in multiple binding forms to bind several local
variables at once:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; x, y = 100, 200&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 300&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Bindings can also be of the form &lt;code class=&quot;highlighter-rouge&quot;&gt;symbol&lt;/code&gt;, which is just shorthand for &lt;code class=&quot;highlighter-rouge&quot;&gt;(symbol nil)&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; x = nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is sort of like declaring a variable in an Algol-family language, e.g.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// JavaScript&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’d probably do this with the intention of (maybe) giving that variable a
value at some later point, e.g.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;;; x = nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;;; x = 100&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;setq&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can use &lt;code class=&quot;highlighter-rouge&quot;&gt;setq&lt;/code&gt; to set the value of a variable. &lt;code class=&quot;highlighter-rouge&quot;&gt;setq&lt;/code&gt; is discussed more at
length in Part 2. Note that Emacs Lisp is in many ways a traditional imperative
language. Emacs Lisp &lt;em&gt;is&lt;/em&gt; a functional language in the sense that functions are
first-class objects that can be passed around as parameters and returned. But
it’s not a functional language in the same pure function/immutable value sense
that Clojure or Haskell are.&lt;/p&gt;

&lt;p&gt;There’s one more thing you should know about &lt;code class=&quot;highlighter-rouge&quot;&gt;let&lt;/code&gt;: bindings are done
independently of one another, as if they were done in parallel.&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;is equivalent to the JavaScript&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// JavaScript&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So something like this doesn’t work:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; Error: x is not bound yet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Many times you do want to refer to the results of one binding form in a subsequent one. In this case, you can use &lt;code class=&quot;highlighter-rouge&quot;&gt;let*&lt;/code&gt;,
which binds things sequentially rather than in parallel:&lt;/p&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; -&amp;gt; 200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the equivalent of the JavaScript&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// JavaScript&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;switching-to-a-different-window&quot;&gt;Switching to a different window&lt;/h4&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;switch-to-buffer-other-window&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;*Preview Markdown Output*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It would be annoying to replace the window with your Markdown file with
&lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt;, so it’s a nicer experience to show it in a window
you’re not currently using. If &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt; is already visible in
a different window, it will switch to that window; otherwise it will pick
another window (creating one if needed) and then switch to the &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview
Markdown Output*&lt;/code&gt; buffer in that window.&lt;/p&gt;

&lt;p&gt;There are a lot of ways to create and split windows in Emacs. You might decide
that you’d rather have &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt; open a new window rather than
replace the contents of an existing one; you can use functions like
&lt;code class=&quot;highlighter-rouge&quot;&gt;split-window-sensibly&lt;/code&gt; to split the current window instead. A full discussion
of other options is outside the scope of this article, but hopefully I can
include more examples in the future.&lt;/p&gt;

&lt;h4 id=&quot;parsing-the-html-source&quot;&gt;Parsing the HTML source&lt;/h4&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;libxml-parse-html-region&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Before we can render the HTML, we have to parse it. &lt;code class=&quot;highlighter-rouge&quot;&gt;libxml-parse-html-region&lt;/code&gt;
parses HTML in the region. Again, we’re using &lt;code class=&quot;highlighter-rouge&quot;&gt;point-min&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;point-max&lt;/code&gt; to
tell it to parse the entire buffer; we’re saving the output of that function as
&lt;code class=&quot;highlighter-rouge&quot;&gt;document&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;clearing-the-contents-of-preview-markdown-output&quot;&gt;Clearing the contents of &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt;&lt;/h4&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;erase-buffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’re going to put the rendered HTML back into &lt;code class=&quot;highlighter-rouge&quot;&gt;*Preview Markdown Output*&lt;/code&gt;, so
we need to clear out the HTML source first. &lt;code class=&quot;highlighter-rouge&quot;&gt;(erase-buffer)&lt;/code&gt; clears the contents
of the current buffer.&lt;/p&gt;

&lt;h4 id=&quot;render-the-parsed-html-document-with-shr&quot;&gt;Render the parsed HTML document with SHR&lt;/h4&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shr-insert-document&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Renders the parsed HTML document in the current buffer.&lt;/p&gt;

&lt;h4 id=&quot;moving-back-to-the-beginning-of-the-buffer&quot;&gt;Moving back to the beginning of the buffer&lt;/h4&gt;

&lt;div class=&quot;language-emacs-lisp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;goto-char&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;point-min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point, the cursor will be all the way at the end of the buffer. It’s
nicer behavior if our preview shows us the beginning of the file rather than the
end. &lt;code class=&quot;highlighter-rouge&quot;&gt;goto-char&lt;/code&gt; moves the cursor, and we’re moving it to the beginning of the
buffer, again using &lt;code class=&quot;highlighter-rouge&quot;&gt;point-min&lt;/code&gt; to determine the beginning.&lt;/p&gt;

&lt;h1 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h1&gt;

&lt;p&gt;In &lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/09/emacs-lisp-intro-markdown-live-previews-part-2.html&quot;&gt;Part 2&lt;/a&gt;, we’ll make
improvements to the command, and discuss Emacs Lisp lists, cons cells,
backquote-splicing, and macros.&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;https://camsaul.com/emacs-lisp/2020/06/10/emacs-lisp-intro-markdown-live-previews-part-3.html&quot;&gt;Part 3&lt;/a&gt;, we’ll have
the command prompt us for a file to preview when running interactively, and have
Emacs run it automatically whenever we save a Markdown file. We’ll discuss
optional arguments, &lt;code class=&quot;highlighter-rouge&quot;&gt;interactive&lt;/code&gt; code characters, &lt;code class=&quot;highlighter-rouge&quot;&gt;progn&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; forms, hooks,
buffer-local variables, and Lisp-2s.&lt;/p&gt;</content><author><name></name></author><summary type="html">This post is part one of a series of 3 posts. View the other parts:</summary></entry><entry><title type="html">Cam’s Secret Hot Sauce Recipe</title><link href="https://camsaul.com/hot-sauce/2020/04/25/cams-house-sauce-recipe.html" rel="alternate" type="text/html" title="Cam's Secret Hot Sauce Recipe" /><published>2020-04-25T03:11:00+00:00</published><updated>2020-04-25T03:11:00+00:00</updated><id>https://camsaul.com/hot-sauce/2020/04/25/cams-house-sauce-recipe</id><content type="html" xml:base="https://camsaul.com/hot-sauce/2020/04/25/cams-house-sauce-recipe.html">&lt;p&gt;Not a day goes by without someone asking me the recipe for Cam’s House Sauce, widely considered one of the greatest
hot sauces in the world. Surely you are familiar with its delicate and subtle blend of habanero with notes of cinnamon
and cherry.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/house-sauce-bottle.jpg&quot; alt=&quot;Cam's House Sauce&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now in this Coronavirus pandemic you can make it yourself from the comfort of your very own armchair. For1
a limited time only, here is the recipie:&lt;/p&gt;

&lt;h4 id=&quot;ingredients&quot;&gt;Ingredients&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;11 oz of habaneros&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;11 oz of serrano peppers or equivalent (remove stems, keep everything else)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;20 cloves of garlic&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;1 red onion (remove skin)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;2 cups of fresh-squeezed ruby red grapefruit juice. Depending on the size of the grapefruits, this is probably about
3 grapefruits&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;4 fl oz of white wine vinegar&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;1 tablespoon of ghost pepper salt&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;1 bunch of cilantro (leaves only)&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Needless to say, either grow all of these ingredients yourself or be sure to use the highest-quality organic
free-range peppers and herbs.&lt;/p&gt;

&lt;h4 id=&quot;instructions&quot;&gt;Instructions&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Throw everything in a blender and blend until it is a nice puree&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Take resulting pulp and run thru a juicer.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Bring to a boil in a pot. Important! Make sure you use a non-reactive pot such as stainless steel. Don’t use
aluminum unless you want it to taste weird.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Reduce heat and simmer 20 mins.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After that you can take it off the heat and bottle it with the hot fill method. I think this makes somewhere between 5
and 10 bottles the size of the ones in the picture above.&lt;/p&gt;

&lt;h4 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h4&gt;

&lt;p&gt;The lawyers advised me against giving away trade secrets like these for nothing, but I couldn’t resist the urge to
lend a helping hand in this time of need. Also I wanted to type this recipe up in case I lose the envelope I wrote it
down on that I’ve been using for the last 2 years:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/house-sauce-recipe.jpg&quot; alt=&quot;Cam's House Sauce&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Have fun and be sure to let me know if you make any tweaks or improvements to the recipe!&lt;/p&gt;

&lt;p&gt;👇 Like and subscribe below! 👇&lt;/p&gt;</content><author><name></name></author><summary type="html">Not a day goes by without someone asking me the recipe for Cam’s House Sauce, widely considered one of the greatest hot sauces in the world. Surely you are familiar with its delicate and subtle blend of habanero with notes of cinnamon and cherry.</summary></entry><entry><title type="html">Methodical now supports partial default methods</title><link href="https://camsaul.com/methodical/2020/04/22/methodical-now-supports-partial-default-methods.html" rel="alternate" type="text/html" title="Methodical now supports partial default methods" /><published>2020-04-22T20:36:00+00:00</published><updated>2020-04-22T20:36:00+00:00</updated><id>https://camsaul.com/methodical/2020/04/22/methodical-now-supports-partial-default-methods</id><content type="html" xml:base="https://camsaul.com/methodical/2020/04/22/methodical-now-supports-partial-default-methods.html">&lt;p&gt;&lt;a href=&quot;https://github.com/camsaul/methodical&quot;&gt;Methodical&lt;/a&gt;, my Clojure library that ports the “generic function” part of the
Common Lisp Object System (CLOS) to Clojure (and makes multimethods &lt;em&gt;way&lt;/em&gt; more powerful) just got even more powerful.
Methodical now supports dispatch on partial defaults!&lt;/p&gt;

&lt;h3 id=&quot;what-does-that-even-mean&quot;&gt;What does that even mean?&lt;/h3&gt;

&lt;p&gt;Take a look at this Clojure code:&lt;/p&gt;

&lt;div class=&quot;language-clj highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defmulti&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vanilla-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defmethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vanilla-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:x-y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defmethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vanilla-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:y-default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defmethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vanilla-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vanilla-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; -&amp;gt; :default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Vanilla multimethods support exactly one default method, and that’s it. If you want to have a method for any dispatch
value where the second value is &lt;code class=&quot;highlighter-rouge&quot;&gt;:y&lt;/code&gt;, you are completely out of luck. The best you can do is some hackery like this:&lt;/p&gt;

&lt;div class=&quot;language-clj highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defmethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vanilla-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default-method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get-method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vanilla-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x-default-method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get-method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vanilla-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y-default-method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get-method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vanilla-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;condp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;default-method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x-default-method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;x-default-method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y-default-method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;y-default-method&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;vanilla-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; -&amp;gt; :y-default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s something I’ve actually done in real life, in production!&lt;/p&gt;

&lt;h3 id=&quot;why&quot;&gt;Why?&lt;/h3&gt;

&lt;p&gt;There’s some real-world use cases where you’d want partial defaults. When writing
&lt;a href=&quot;https://github.com/metabase/metabase/&quot;&gt;Metabase&lt;/a&gt;’s query processsing code for JDBC databases, I wanted to write a
multimethod for reading columns in a &lt;code class=&quot;highlighter-rouge&quot;&gt;ResultSet&lt;/code&gt; that dispatched on both the database and the JDBC type. For example,
we have a default method for reading &lt;code class=&quot;highlighter-rouge&quot;&gt;java.sql.Types/TIME_WITH_TIMEZONE&lt;/code&gt; columns like:&lt;/p&gt;

&lt;div class=&quot;language-clj highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defmethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read-column-thunk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;java.sql.Types/TIME_WITH_TIMEZONE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For some databases, we override this method like:&lt;/p&gt;

&lt;div class=&quot;language-clj highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defmethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read-column-thunk&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:postgres&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;java.sql.Types/TIME_WITH_TIMEZONE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So what happens when we call &lt;code class=&quot;highlighter-rouge&quot;&gt;read-column-thunk&lt;/code&gt; with the dispatch value &lt;code class=&quot;highlighter-rouge&quot;&gt;[:mysql java.sql.Types/TIME_WITH_TIMEZONE]&lt;/code&gt;?
By default, it doesn’t work – it will use the &lt;code class=&quot;highlighter-rouge&quot;&gt;:default&lt;/code&gt; method rather than the &lt;code class=&quot;highlighter-rouge&quot;&gt;[:default java.sql.Types/TIME_WITH_TIMEZONE]&lt;/code&gt; method
as we would like.&lt;/p&gt;

&lt;h3 id=&quot;cant-i-use-hierarchies&quot;&gt;Can’t I use hierarchies?&lt;/h3&gt;

&lt;p&gt;The astute reader will note that you can create a keyword hierarchy and have both &lt;code class=&quot;highlighter-rouge&quot;&gt;:postgres&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;:mysql&lt;/code&gt; derive from
a common parent keyword (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;:database&lt;/code&gt;). That’s certainly true! But creating a hierarchy (or using the global
hierarchy with namespaced keywords) just for the sake of a multimethod is annoying, and it &lt;em&gt;still&lt;/em&gt; wouldn’t handle cases
like &lt;code class=&quot;highlighter-rouge&quot;&gt;[nil java.sql.Types/TIME_WITH_TIMEZONE]&lt;/code&gt;, where the first part of the dispatch value doesn’t derive from the
common parent. It doesn’t work as a true fall-thru for any &lt;code class=&quot;highlighter-rouge&quot;&gt;java.sql.Types/TIME_WITH_TIMEZONE&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;enter-methodical&quot;&gt;Enter Methodical&lt;/h3&gt;

&lt;p&gt;Methodical now supports partial defaults! Here’s how to do it in Methodical:&lt;/p&gt;

&lt;div class=&quot;language-clj highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;methodical.core&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;m/defmulti&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;m/defmethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:x-y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;m/defmethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:y-default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;m/defmethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;my-multimethod&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; -&amp;gt; :y-default&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The new &lt;code class=&quot;highlighter-rouge&quot;&gt;multi-default-dispatcher&lt;/code&gt; is the default dispatcher, but with Methodical &lt;a href=&quot;https://github.com/camsaul/methodical#advanced-customization&quot;&gt;you can customize method
dispatch&lt;/a&gt; and have it work any way you’d like!&lt;/p&gt;

&lt;h3 id=&quot;how-can-i-learn-more&quot;&gt;How Can I Learn More?&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://clojurenorth.com/cam-saul.html&quot;&gt;I’m giving a talk about Methodical at Clojure/north this year&lt;/a&gt;, so stay
tuned! As far as I know, Clojure/north is going to be a remote conference due to Coronavirus concerns, so you can
attend from anywhere in the world!&lt;/p&gt;

&lt;p&gt;👇 Like and subscribe below! 👇&lt;/p&gt;</content><author><name></name></author><summary type="html">Methodical, my Clojure library that ports the “generic function” part of the Common Lisp Object System (CLOS) to Clojure (and makes multimethods way more powerful) just got even more powerful. Methodical now supports dispatch on partial defaults!</summary></entry></feed>