<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>hi, it&#39;s mike</title>
    <link>https://mike.puddingtime.org/tags/lisp/</link>
    <description>Recent content on hi, it&#39;s mike</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>mike@puddingtime.org (mike)</managingEditor>
    <webMaster>mike@puddingtime.org (mike)</webMaster>
    <copyright>© 2026, mike</copyright>
    <lastBuildDate>Mon, 21 Apr 2025 08:37:39 -0700</lastBuildDate>
    <atom:link href="https://mike.puddingtime.org/tags/lisp/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Hugo posting in Emacs</title>
      <link>https://mike.puddingtime.org/posts/2025-04-21-hugo-posting-in-emacs/</link>
      <pubDate>Mon, 21 Apr 2025 08:37:39 -0700</pubDate><author>mike@puddingtime.org (mike)</author>
      <guid>https://mike.puddingtime.org/posts/2025-04-21-hugo-posting-in-emacs/</guid>
      <description>ox-hugo is nice and all</description>
      <content:encoded><![CDATA[<p>I dug a bunch of stuff out of archived configs to get the Hugo blog going again, including my old <a href="https://ox-hugo.scripter.co/">ox-hugo</a> setup.</p>
<p><code>ox-hugo</code> lets you keep a monolithic org file where each post is an org heading:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lisp" data-lang="lisp"><span class="line"><span class="cl"><span class="nv">**</span> <span class="nv">SyncTrain</span> <span class="nv">for</span> <span class="nv">Syncthing</span> <span class="nv">on</span> <span class="nv">iOS</span> <span class="ss">:syncthing:ios:iphone:</span>
</span></span><span class="line"><span class="cl"><span class="ss">:PROPERTIES:</span>
</span></span><span class="line"><span class="cl"><span class="ss">:EXPORT_FILE_NAME:</span> <span class="nv">2025-04-20-synctrain-for-syncthing-on-ios</span>
</span></span><span class="line"><span class="cl"><span class="ss">:EXPORT_HUGO_DATE:</span> <span class="nv">&lt;2025-04-20&gt;</span>
</span></span><span class="line"><span class="cl"><span class="ss">:EXPORT_DATE:</span> <span class="nv">2025-04-20</span>
</span></span><span class="line"><span class="cl"><span class="ss">:EXPORT_HUGO_SECTION:</span> <span class="nv">blog</span>
</span></span><span class="line"><span class="cl"><span class="ss">:EXPORT_HUGO_CUSTOM_FRONT_MATTER+:</span> <span class="ss">:cover</span> <span class="o">&#39;</span><span class="p">((</span><span class="nv">image</span> <span class="o">.</span><span class="s">&#34;&#34;</span> <span class="p">)</span> <span class="p">(</span><span class="nv">caption</span> <span class="o">.</span> <span class="s">&#34;&#34;</span> <span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="ss">:EXPORT_HUGO_CUSTOM_FRONT_MATTER+:</span> <span class="ss">:images</span> <span class="o">&#39;</span><span class="p">(</span><span class="nv">/mph-logo.png</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="ss">:EXPORT_DESCRIPTION:</span>
</span></span><span class="line"><span class="cl"><span class="ss">:END:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">A</span> <span class="nv">few</span> <span class="nv">years</span> <span class="nv">ago</span> <span class="nv">I</span> <span class="nv">gave</span> <span class="nv">Mobius</span> <span class="nv">Sync</span> <span class="nv">a</span> <span class="nv">try</span> <span class="nv">as</span> <span class="nv">a</span> <span class="nv">Syncthing</span> <span class="nv">client</span> <span class="nv">on</span> <span class="nv">my</span> <span class="nv">iPhone</span> <span class="nb">and</span> <span class="nv">iPad.</span> <span class="nv">That</span> <span class="nv">went</span> <span class="nv">about</span> <span class="nv">as</span> <span class="nv">well</span> <span class="nv">as</span> <span class="nv">you</span><span class="ss">&#39;d</span> <span class="nv">expect</span> <span class="nv">for</span> <span class="nv">an</span> <span class="nv">iOS</span> <span class="nv">adaptation</span> <span class="nv">of</span> <span class="nv">something</span> <span class="nv">that</span> <span class="nv">wants</span> <span class="nv">to</span> <span class="nv">be</span> <span class="nv">an</span> <span class="nv">always-on</span> <span class="nv">filesystem-watching</span> <span class="nv">daemon.</span> <span class="nv">It</span> <span class="nv">wasn</span><span class="ss">&#39;t</span> <span class="nv">really</span> <span class="nv">worth</span> <span class="k">the</span> <span class="nv">stress</span> <span class="nv">of</span> <span class="nv">wondering</span> <span class="nv">what</span> <span class="nv">quantum</span> <span class="nv">state</span> <span class="nv">of</span> <span class="nv">sync</span> <span class="nv">everything</span> <span class="nv">is</span> <span class="nv">in,</span> <span class="nb">and</span> <span class="nv">I</span> <span class="nv">hated</span> <span class="nv">having</span> <span class="nv">to</span> <span class="nv">explicitly</span> <span class="nf">open</span> <span class="nv">it</span> <span class="nv">up</span> <span class="nv">to</span> <span class="nv">nudge</span> <span class="nv">it</span> <span class="nv">to</span> <span class="nv">sync.</span></span></span></code></pre></div>
<p>If a heading is marked as <code>TODO</code>, that translates to &ldquo;draft&rdquo; for Hugo. If you use org tags in the heading <code>:tag1:tag2:</code> those become post tags.</p>
<p>If you set up <code>org-capture</code> and a few hooks correctly, it takes a lot of friction away by exporting the Markdown files when you save the file.</p>
<p>Something has changed since I was last using it regularly, and some bugs crept into my setup. I was willing to live with a few of them, but last night I came across some goofy thing where Hugo&rsquo;s Markdown renderer (goldmark) and ox-hugo were interacting strangely, and the org-to-markdown conversion was indenting unordered lists enough that goldmark picked them up as indented code blocks. I did some poking around and saw that a lot of people have been vexed by that: goldmark uses the <a href="https://commonmark.org/">CommonMark</a> specification, which includes indented code blocks, and goldmark offers no toggle for it as a workaround.</p>
<p>How many seconds do we have of this precious life?</p>
<p>I do like working out of Emacs and not switching around to do stuff, so I found an old Ruby script I wrote to make generating a Hugo post with all the stuff particular to my setup and worked with a co-pilot to convert it to a lisp package.</p>
<p>When you invoke <code>hpost-new</code> it prompts for whether to follow the &ldquo;daily&rdquo; or regular post style, then asks for title, tags, and description, then plops the new file into the right place and opens it for editing.</p>
<p>This goes in <code>~/.config/doom/lisp</code>:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lisp" data-lang="lisp"><span class="line"><span class="cl"><span class="c1">;;; hpost.el --- Create new Hugo posts from Emacs  -*- lexical-binding: t; -*-</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;; Adjust this to point at your Hugo or ox‑hugo content directory.</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nv">defcustom</span> <span class="nv">hpost-posts-dir</span> <span class="s">&#34;~/blog/content/posts/&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Directory where new Hugo posts are written.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:type</span> <span class="ss">&#39;directory</span> <span class="ss">:group</span> <span class="ss">&#39;hpost</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">defun</span> <span class="nv">hpost--slugify</span> <span class="p">(</span><span class="nv">title</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Convert TITLE to a URL‑friendly slug.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="k">let*</span> <span class="p">((</span><span class="nv">s</span> <span class="p">(</span><span class="nv">downcase</span> <span class="nv">title</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">         <span class="p">(</span><span class="nv">s</span> <span class="p">(</span><span class="nv">replace-regexp-in-string</span> <span class="s">&#34;[^a-z0-9]+&#34;</span> <span class="s">&#34;-&#34;</span> <span class="nv">s</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">         <span class="p">(</span><span class="nv">s</span> <span class="p">(</span><span class="nv">replace-regexp-in-string</span> <span class="s">&#34;^-\|-$&#34;</span> <span class="s">&#34;&#34;</span> <span class="nv">s</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nv">truncate-string-to-width</span> <span class="nv">s</span> <span class="mi">60</span> <span class="no">nil</span> <span class="no">nil</span> <span class="s">&#34;&#34;</span><span class="p">)))</span> <span class="c1">; hard cap at 60 chars</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">defun</span> <span class="nv">hpost--today</span> <span class="p">()</span> <span class="p">(</span><span class="nv">format-time-string</span> <span class="s">&#34;%Y-%m-%d&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">defun</span> <span class="nv">hpost--now</span>   <span class="p">()</span> <span class="p">(</span><span class="nv">format-time-string</span> <span class="s">&#34;%Y-%m-%dT%H:%M:%S%z&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">;;;###autoload</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">defun</span> <span class="nv">hpost-new</span> <span class="p">(</span><span class="nv">title</span> <span class="nv">tags</span> <span class="nv">summary</span> <span class="k">&amp;optional</span> <span class="nv">daily</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Create a new Hugo post.
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">Interactively prompts for TITLE, TAGS (comma‑delimited), and SUMMARY.
</span></span></span><span class="line"><span class="cl"><span class="s">With prefix arg, treat it as a daily note (pre‑sets title and tags).&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nv">interactive</span>
</span></span><span class="line"><span class="cl">   <span class="p">(</span><span class="k">let*</span> <span class="p">((</span><span class="nv">daily</span> <span class="p">(</span><span class="nf">y-or-n-p</span> <span class="s">&#34;Daily note? &#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">          <span class="p">(</span><span class="nv">title</span> <span class="p">(</span><span class="k">if</span> <span class="nv">daily</span>
</span></span><span class="line"><span class="cl">                     <span class="p">(</span><span class="nf">format</span> <span class="s">&#34;Daily notes for %s&#34;</span> <span class="p">(</span><span class="nv">hpost--today</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">                   <span class="p">(</span><span class="nv">read-string</span> <span class="s">&#34;Title: &#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">          <span class="p">(</span><span class="nv">tags</span>  <span class="p">(</span><span class="k">if</span> <span class="nv">daily</span> <span class="s">&#34;journal&#34;</span>
</span></span><span class="line"><span class="cl">                   <span class="p">(</span><span class="nv">read-string</span> <span class="s">&#34;Tags (comma): &#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">          <span class="p">(</span><span class="nv">summary</span> <span class="p">(</span><span class="nv">read-string</span> <span class="s">&#34;Summary: &#34;</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">     <span class="p">(</span><span class="nc">list</span> <span class="nv">title</span> <span class="nv">tags</span> <span class="nv">summary</span> <span class="nv">daily</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="k">let*</span> <span class="p">((</span><span class="nv">slug</span>    <span class="p">(</span><span class="k">if</span> <span class="nv">daily</span> <span class="s">&#34;daily-notes&#34;</span> <span class="p">(</span><span class="nv">hpost--slugify</span> <span class="nv">title</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">         <span class="p">(</span><span class="nv">fname</span>   <span class="p">(</span><span class="nv">concat</span> <span class="p">(</span><span class="nv">hpost--today</span><span class="p">)</span> <span class="s">&#34;-&#34;</span> <span class="nv">slug</span> <span class="s">&#34;.md&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">         <span class="p">(</span><span class="nv">path</span>    <span class="p">(</span><span class="nv">expand-file-name</span> <span class="nv">fname</span> <span class="nv">hpost-posts-dir</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nv">file-exists-p</span> <span class="nv">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">user-error</span> <span class="s">&#34;File %s already exists&#34;</span> <span class="nv">fname</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nv">with-temp-buffer</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">insert</span> <span class="p">(</span><span class="nf">format</span>
</span></span><span class="line"><span class="cl"><span class="s">&#34;---
</span></span></span><span class="line"><span class="cl"><span class="s">title: \&#34;%s\&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">date: %s
</span></span></span><span class="line"><span class="cl"><span class="s">draft: true
</span></span></span><span class="line"><span class="cl"><span class="s">tags:%s
</span></span></span><span class="line"><span class="cl"><span class="s">summary: \&#34;%s\&#34;
</span></span></span><span class="line"><span class="cl"><span class="s">---
</span></span></span><span class="line"><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="cl"><span class="s">&#34;</span> <span class="nv">title</span> <span class="p">(</span><span class="nv">hpost--now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="nv">mapconcat</span> <span class="p">(</span><span class="nb">lambda</span> <span class="p">(</span><span class="no">t</span><span class="p">)</span> <span class="p">(</span><span class="nf">format</span> <span class="s">&#34;\n- %s&#34;</span> <span class="p">(</span><span class="nf">string-trim</span> <span class="no">t</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">                   <span class="p">(</span><span class="nv">split-string</span> <span class="nv">tags</span> <span class="s">&#34;,&#34;</span><span class="p">)</span> <span class="s">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="nv">summary</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="nv">write-region</span> <span class="p">(</span><span class="nv">point-min</span><span class="p">)</span> <span class="p">(</span><span class="nv">point-max</span><span class="p">)</span> <span class="nv">path</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nv">find-file</span> <span class="nv">path</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nv">message</span> <span class="s">&#34;New post created: %s&#34;</span> <span class="nv">path</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nf">provide</span> <span class="ss">&#39;hpost</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">;;; hpost.el ends here</span></span></span></code></pre></div>
<p>&hellip; and the config:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lisp" data-lang="lisp"><span class="line"><span class="cl"><span class="c1">;; --- Hugo post helper ---------------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nv">use-package!</span> <span class="nv">hpost</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:load-path</span> <span class="s">&#34;lisp/&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:defer</span> <span class="no">t</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:custom</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nv">hpost-posts-dir</span> <span class="s">&#34;~/blog/content/posts/&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:config</span>
</span></span><span class="line"><span class="cl">  <span class="c1">;; Optional keybinding:  &lt;leader&gt; n h</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nv">map!</span> <span class="ss">:leader</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span><span class="ss">:prefix</span> <span class="p">(</span><span class="s">&#34;n&#34;</span> <span class="o">.</span> <span class="s">&#34;notes&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">         <span class="ss">:desc</span> <span class="s">&#34;New Hugo post&#34;</span> <span class="s">&#34;h&#34;</span> <span class="nf">#&#39;</span><span class="nv">hpost-new</span><span class="p">)))</span></span></span></code></pre></div>
<p>It goes well with a few helpers I made to fire up or shut down the Hugo preview server within Emacs:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lisp" data-lang="lisp"><span class="line"><span class="cl"><span class="p">(</span><span class="nb">defun</span> <span class="nv">my-start-hugo-server</span> <span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Run Hugo server with live reloading.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nv">interactive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="k">let*</span> <span class="p">((</span><span class="nv">root</span> <span class="p">(</span><span class="nv">projectile-project-root</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">         <span class="p">(</span><span class="nv">default-directory</span> <span class="nv">root</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nf">compile</span> <span class="s">&#34;hugo server -D --navigateToChanged&#34;</span> <span class="no">t</span><span class="p">)))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nb">defun</span> <span class="nv">my-stop-hugo-server</span> <span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="s">&#34;Stop Hugo server.&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nv">interactive</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">(</span><span class="nv">kill-compilation</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="nv">map!</span> <span class="ss">:leader</span>
</span></span><span class="line"><span class="cl">      <span class="p">(</span><span class="ss">:prefix</span> <span class="p">(</span><span class="s">&#34;H&#34;</span> <span class="o">.</span> <span class="s">&#34;Hugo&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">       <span class="ss">:desc</span> <span class="s">&#34;Start Hugo Server&#34;</span> <span class="s">&#34;S&#34;</span> <span class="nf">#&#39;</span><span class="nv">my-start-hugo-server</span>
</span></span><span class="line"><span class="cl">       <span class="ss">:desc</span> <span class="s">&#34;Stop Hugo Server&#34;</span> <span class="s">&#34;s&#34;</span> <span class="nf">#&#39;</span><span class="nv">my-stop-hugo-server</span><span class="p">))</span></span></span></code></pre></div>
<p>So, <code>SPC H S</code> to start the test server, and <code>SPC n h</code> to start a new post. When I save the buffer, the preview server jumps to the newly written page in the browser.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
