Posts in category "Emacs"

expando.el v1.5

1 min read

While I have been doing a lot of hacking on blogmore.el, I haven't forgotten my plan to revisit and refresh some of my older personal packages. This evening I've paid some attention to expando.el.

This started life a long time ago, as part of my grab-bag of handy functions that got carried around and copied from machine to machine, until I did a big tidy-up of everything back in 2017 and turned various things into packages that I managed via a self-hosted (well, GitHub pages hosted) package index.

It's a pretty simple but very useful bit of code that lets me quickly macroexpand a sexp at point and pretty print it into a display window. I've often found it indispensable when it came to writing my own macros.

expando in action

This release simply adds a lexical-binding header to the file, and also adds a q key binding to the resulting view window so that it can be quickly and easily closed.

Also, as with all my other personal packages, I've swapped away from using delpa to simply using :vc to pull it in.

(use-package expando
  :vc (:url "https://github.com/davep/expando.el" :rev :newest)
  :bind
  ("C-c e" . expando-macro))

Or perhaps I should say...

(progn
  (use-package-vc-install
   '(expando (:url "https://github.com/davep/expando.el") nil) nil)
  (defvar use-package--warning69
    #'(lambda (keyword err)
        (let
            ((msg
              (format "%s/%s: %s" 'expando keyword
                      (error-message-string err))))
          (display-warning 'use-package msg :error))))
  (condition-case err
      (progn
        (if (fboundp 'expando-macro) nil
          (autoload #'expando-macro "expando" nil t))
        (let*
            ((name "C-c e") (key [3 101])
             (kmap
              (or (if (and nil (symbolp nil)) (symbol-value nil) nil)
                  global-map))
             (kdesc
              (cons (if (stringp name) name (key-description name))
                    (if (symbolp nil) nil 'nil)))
             (binding (lookup-key kmap key)))
          (require 'bind-key)
          (let
              ((entry (assoc kdesc personal-keybindings))
               (details
                (list #'expando-macro (if (numberp binding) nil binding))))
            (if entry (setcdr entry details)
              (add-to-list 'personal-keybindings (cons kdesc details))))
          (define-key kmap key #'expando-macro)))
    ((debug error) (funcall use-package--warning69 :catch err))))

blogmore.el v4.0

2 min read

Despite having bumped it from 2.x to 3.x yesterday, I'm calling v4.0 on blogmore.el today. There's a good reason for this though. While tinkering with some of the configuration yesterday, and also answering a configuration question last night, I realised that it made sense to make some of the internals into public utility functions.

Now, sure, Emacs Lisp doesn't really have internals in the private function sense, but I've always liked the approach that a package-- prefix communicates "internal, might go away" vs package- which tells me "this is a stable part of the API of this package". With this in mind I've always tried to write my code using this convention. I did this with blogmore.el too and a lot of the code had the blogmore-- prefix.

There's plenty of code in there that someone might want to make use of, if they wanted to add their own commands, or do fun things with the configuration. So with this in mind I've "promoted" a bunch of code to being "public" and, in that case, I feel this deserves another major version bump1.

Things that are now part of the "public" interface include:

  • blogmore-clean-time-string
  • blogmore-get-frontmatter
  • blogmore-remove-frontmatter
  • blogmore-set-frontmatter
  • blogmore-slug
  • blogmore-toggle-frontmatter
  • blogmore-with-post

Each one is documented via its docstring (just a quick Ctrl+h f function-name RET away) and hopefully is pretty self-explanatory.

blogmore-with-post is especially handy as it provides a quick and easy way of pulling information from a post file. So something like this:

(blogmore-with-post "~/write/davep.github.com/content/posts/2026/04/2026-04-05-blogmore-el-v3-1.md"
  (list
   (blogmore-get-frontmatter "title")
   (blogmore-get-frontmatter "date")))

resulting in:

("blogmore.el v3.1" "2026-04-05 20:04:44+0100")

Meaning that this snippet from yesterday's post:

(with-temp-buffer
  (insert-file-contents-literally file)
  (parse-iso8601-time-string
   (blogmore--clean-time-string (blogmore--get-frontmatter-property "date"))))

becomes:

(blogmore-with-post file
  (parse-iso8601-time-string
   (blogmore-clean-time-string (blogmore-get-frontmatter "date"))))

Not massively different, but it reads better and now all the calls are to the "public API" of the package.

Not all the changes are "promoted internals". I've also added a blogmore-remove-tag command (and also added it to the transient menu).

Removing a tag

I've also changed the way that blogmore-add-tag works so that, now, if it's called from the transient, it immediately goes back to the tag input prompt, allowing for another tag to be immediately selected (you can quit out of this with Ctrl+g). Removal of a tag works in a similar way, making things a lot quicker.

I've also added some extra tests too, which makes it even easier for me to make future changes with confidence. The more I work with it the more I appreciate that ERT is available.


  1. Ordinarily it shouldn't matter as the public interface isn't changing, but some of the "internal" functions had been mentioned as options for configuration. 

blogmore.el v3.1

4 min read

When I first started writing blogmore.el it was just going to be a handful of commands that let me spin up a new blog post, and insert the odd link here and there when needed. Initially it only handled a single blog, and everything it did was based around how I lay my personal blog out, and was also very much geared to how I'd made BlogMore work.

But then I wanted to use it to edit both my personal blog and my photoblog. So then I had to add support for configuring different ways of laying out posts for different blogs, etc.

Still, it was mostly written as a personal tool that worked for my stuff. I tried to make it so that it was easy enough to configure (and let's be fair: it's for Emacs and written in Emacs Lisp, it's kind of hard to not be very configurable if you're happy to get your hands dirty with some coding), but there were still some parts of it that weren't as easy to change as I'd have liked.

Also, when I'd originally added the multi-blog configuration, I'd chosen a format for the list of blogs that was an assoc-list of pure lists wrapped by a cl-defstruct to make for easier access. It worked well but was very positional in its nature.

So when the request came in to be able to have better control over the name of the file when starting a new post, which meant I was going to need to rearrange the structure again, it was time to try and do something about it.

Which is how I'm now on v3.1 (yes, there was a v3.0 but I quickly found something in that that needed fixing1). It's a major version bump because I've totally changed how the blogmore-blogs variable holds the data.

From now on I'm using EIEIO to create a class that holds all of the data for a given blog. This, I believe, makes the code easier to read and should also make it more resilient to the addition of any new properties. Also thanks to how such classes can work with the customize system the customize experience remains pretty much the same too.

Personally I don't use the customize UI, instead I declare everything via use-package. As of the time of writing my declaration for blogmore looks like this:

(use-package blogmore
  :ensure t
  :defer t
  :vc (:url "https://github.com/davep/blogmore.el" :rev :newest)
  :init
  (add-hook 'blogmore-new-post-hook #'end-it)
  (blogmore-work-on "blog.davep.org")
  :custom
  (blogmore-blogs
   (list
    (blogmore-blog
     :title "blog.davep.org"
     :posts-directory "~/write/davep.github.com/content/posts/"
     :post-subdirectory-function (lambda () (format-time-string "%Y/%m/")))
    (blogmore-blog
     :title "seen-by.davep.dev"
     :posts-directory "~/write/seen-by/content/posts/")))
  :bind
  ("<f12> b" . blogmore))

There's a bunch of other changes and tweaks under the hood in this release too. All of these should come together to make blogmore.el a little more configurable than it was before. I think, to get the best out of it, anyone wanting to configure it "just so" for their purposes will still have to do a little bit of work, which makes me want to spend some time soon writing some proper documentation, complete with examples of how you might achieve different things.

One big change I've made under the hood is to the code that is used when you insert a link to a post (blogmore-link-post). When this runs it lets me pick a file in your filesystem that is a post from my currently-active blog. Once it has the filename it needs to turn it into a root-relative link. So this:

~/write/davep.github.com/content/posts/2026/04/01/2026-04-01-foo.md

needs to become:

/2026/04/01/foo.html

Until now I just did some regexp faffing that took the 2026-04-01- at the start of the filename and swapped each - for /. Nice and easy. Simple enough to code up and get things working a few weeks back. Not at all flexible.

So as a proof-of-concept of how sophisticated someone could get with configuring this I've changed blogmore-default-post-maker-function from this:

(defcustom blogmore-default-post-maker-function
  (lambda (file)
    (replace-regexp-in-string
     (rx bos (group (+ digit)) "-" (group (+ digit)) "-" (group (+ digit)) "-")
     "\\1/\\2/\\3/"
     (file-name-base (file-name-sans-extension file))))
  "Default function to generate a link for a blog post from its filename."
  :type 'function
  :group 'blogmore)

and turned it into this:

(defcustom blogmore-default-post-maker-function
  (lambda (file)
    (format
     "%s/%s"
     (format-time-string
      "%Y/%m/%d"
      (with-temp-buffer
        (insert-file-contents-literally file)
        (parse-iso8601-time-string
         (blogmore--clean-time-string (blogmore--get-frontmatter-property "date")))))
     (replace-regexp-in-string
      (rx bol (= 4 digit) "-" (= 2 digit) "-" (= 2 digit) "-")
      ""
      (file-name-base file))))
  "Default function to generate a link for a blog post from its filename."
  :type 'function
  :group 'blogmore)

So whereas before I was simply messing with the file's name, now I'm loading the date frontmatter from the chosen file and using that to create the date portion of the path in the URL that I use on my blog. The benefit here is that someone might want to keep the date portion of the path in the URL, but never want it as part of the Markdown source file's name, and so this change means they can call the file anything they want; it doesn't look at that but instead uses the actual date from the post's frontmatter.

I think this nicely demonstrates that, especially thanks to how powerful Emacs and Emacs Lisp are, it's fairly easy to make blogmore.el work just the way you want. I think I've provided almost all the hooks necessary to configure it all sorts of ways, but if you do happen to use it and run into something I might have missed, let me know.


  1. I have at least two slightly different date formats going on in my Markdown and Emacs' parse-iso8601-time-string is kind of picky. So I added a function to try and clean that up

blogmore.el v2.7

3 min read

There's no question that the experiment that is BlogMore has resulted in me blogging more. Although my previous setup wasn't exactly all friction, there's something about "owning" most of the tools and really knowing how they work, and being able to quickly modify them so they work "just so", that makes me more inclined to quickly write something up.

I can see this if I look at the numbers in the archive for this blog. In March alone I wrote 43 posts; that's more than I wrote in any whole year, other than 2023. While I suspect this will start to calm down as work on BlogMore and blogmore.el settles down, I sense I'll be writing a bit more often for some time to come.

Because of this I decided to do a little bit of housekeeping on the posts directory in the blog's source repository. Originally I had the Markdown source for every post all in one directory. Then last month I broke those down by year. Then earlier today, seeing how this year is going, I decided to break 2026 down by month.

Then I realised I had a problem in blogmore.el. It assumed that the Markdown file for a new post (blogmore-new) would always be created in a subdirectory named after the year, underneath the defined posts directory. Until today that was the case1, but now I wanted it to work differently.

So this is why I'm making a second release in one day: I added the ability to configure the subdirectory where a new post is created. I've changed the default now so that it assumes the user wants the subdirectory to be YYYY/MM/DD (because more granular feels like the right default). In my case I don't want that, I just want YYYY/MM, but now I can configure that. The value that is set is a function that returns the name of the subdirectory, so in the case of my blog I have it as:

(lambda () (format-time-string "%Y/%m/"))

On the other hand, for my photoblog I want the full date as a subdirectory so I can leave it as the default. The whole use-package for blogmore now looks like:

(use-package blogmore
  :ensure t
  :defer t
  :vc (:url "https://github.com/davep/blogmore.el" :rev :newest)
  :init
  (add-hook 'blogmore-new-post-hook #'end-it)
  (blogmore-work-on "blog.davep.org")
  :custom
  (blogmore-blogs
   '(("blog.davep.org"
      ;; Root directory for posts.
      "~/write/davep.github.com/content/posts/"
      ;; Subdirectory for new posts, relative to the root.
      (lambda () (format-time-string "%Y/%m/")))
     ("seen-by.davep.dev"
      ;; Root directory for posts.
      "~/write/seen-by/content/posts/")))
  :bind
  ("<f12> b" . blogmore))

Technically this is a breaking change because it bumps the meaning of each "position" in the values within blogmore-blogs. However, in my case, because I was only ever defining the blog name and the top-level directory for the posts (both mandatory), this didn't break anything; I also strongly suspect nobody else is using this so I very much doubt I'm messing with someone else's setup2. If I have I apologise; do let me know.

Anyway, all of this goes to explain why the heck I made two releases of the same package back to back in the same day. This is what happens when my namesake is having fun outside and so I just want to sit on the sofa, hack on some code, and watch the chaos in the garden.


  1. For my blog, which again shows that blogmore.el started as a quick hack for getting work done on my blog, but I also want to make it as configurable as possible. 

  2. Even if someone else is using this I would suspect they hadn't configured anything more than I have. 

blogmore.el v2.6

2 min read

Like most people, I imagine, I first ran into transient when first using magit. I took to it pretty quickly and it's always made sense to me as a user interface. But... I've never used it for any code I've ever written.

I think, incorrectly, I'd half assumed it was going to be some faff to set up, and of course for a good while it wasn't part of Emacs anyway. Given this, I'd always had it filed under the heading "that's so neat I'll give it a go one day but not at the moment".

Meanwhile... ever since I did the last big revamp of my Emacs configuration, I found myself leaning into a command binding approach that does the whole prefix-letter-letter thing. For reasons I can't actually remember I fell into the habit of using F121 as my chosen prefix key. As such, over the past 10 or so years (since I greatly overhauled my Emacs setup), I've got into setting up bindings for commands that follow this prefix convention.

So when I created blogmore.el I set up the commands following this pattern.

(use-package blogmore
  :ensure t
  :defer t
  :vc (:url "https://github.com/davep/blogmore.el" :rev :newest)
  :init
  (add-hook 'blogmore-new-post-hook #'end-it)
  (blogmore-work-on "blog.davep.org")
  :custom
  (blogmore-blogs
   '(("blog.davep.org" "~/write/davep.github.com/content/posts/")
     ("seen-by.davep.dev" "~/write/seen-by/content/posts/")))
  :bind
  ("<f12> b b" . blogmore-work-on)
  ("<f12> b n" . blogmore-new)
  ("<f12> b e" . blogmore-edit)
  ("<f12> b d" . blogmore-toggle-draft)
  ("<f12> b s c" . blogmore-set-category)
  ("<f12> b a t" . blogmore-add-tag)
  ("<f12> b u d" . blogmore-update-date)
  ("<f12> b u m" . blogmore-update-modified)
  ("<f12> b l p" . blogmore-link-post)
  ("<f12> b l c" . blogmore-link-category)
  ("<f12> b l t" . blogmore-link-tag))

It works well, it makes it nice and easy to remember the bindings, etc. Nobody needs me to sell them on the merits of this approach.

Then I got to thinking last night: why am I setting up all those bindings when I could probably do it all via a transient? So that was the moment to actually RTFM and get it going. The first version was incredibly quick to get up and running and I was kicking myself that I'd taken so long to actually look at the package properly.

This morning I've worked on it a little more and the final form is still pretty straightforward.

(transient-define-prefix blogmore ()
  "Show a transient for BlogMore commands."
  [:description
   (lambda ()
     (format "BlogMore: %s\n"
             (if (blogmore--chosen-blog-sans-error)
                 (blogmore--blog-title)
               "No blog selected")))
   ["Blog"
    ("b"  "Select blog" blogmore-work-on)]
   ["Post"
    ("n" "New post" blogmore-new :inapt-if-not blogmore--chosen-blog-sans-error)
    ("e" "Edit post" blogmore-edit :inapt-if-not blogmore--chosen-blog-sans-error)
    ("d" "Toggle draft status" blogmore-toggle-draft :inapt-if-not blogmore--blog-post-p)
    ("c" "Set post category" blogmore-set-category :inapt-if-not blogmore--blog-post-p)
    ("t" "Add tag" blogmore-add-tag :inapt-if-not blogmore--blog-post-p)
    ("u d" "Update date" blogmore-update-date :inapt-if-not blogmore--blog-post-p)
    ("u m" "Update modified date" blogmore-update-modified :inapt-if-not blogmore--blog-post-p)]
   ["Links"
    ("l c" "Link to a category" blogmore-link-category :inapt-if-not blogmore--blog-post-p)
    ("l p" "Link to a post" blogmore-link-post :inapt-if-not blogmore--blog-post-p)
    ("l t" "Link to a tag" blogmore-link-tag :inapt-if-not blogmore--blog-post-p)]])

With this in place I can simplify my use-package quite a bit, just binding a single key to run blogmore.

(use-package blogmore
  :ensure t
  :defer t
  :vc (:url "https://github.com/davep/blogmore.el" :rev :newest)
  :init
  (add-hook 'blogmore-new-post-hook #'end-it)
  (blogmore-work-on "blog.davep.org")
  :custom
  (blogmore-blogs
   '(("blog.davep.org" "~/write/davep.github.com/content/posts/")
     ("seen-by.davep.dev" "~/write/seen-by/content/posts/")))
  :bind
  ("<f12> b" . blogmore))

Now, when I'm working on a blog post, I can just hit F12 b and I get a neat menu:

BlogMore with all commands available

Better still, because of how transient works, I can ensure that only applicable commands are available, while still showing them all. So if I've not even got a blog selected yet:

With no commands available

Or with a blog selected but not actually working on a post yet:

With some commands available

So far I'm really liking this approach, and I'm tempted to lean into transient more with some of my packages now. While on the surface it does seem that it has the downside of the binding choices being dictated by me, the fact is that the commands are all still there and anyone can use their own bindings, or I guess override the transient itself and do their own thing.


  1. Yes, it is a bit out of the way on the keyboard, but so is Esc. I find my muscle memory has no problem with it. 

nukneval.el v1.3

2 min read

Back when I first really got into writing Emacs Lisp code, one of the first things I got very used to and really fell in love with was being able to eval-last-sexp (C-x C-e) the code I was writing, either to test it right there in the buffer, or to cause it to be bound so I could use it elsewhere. It was so different from any other mode of working I'd used before and it was really addictive as a way of hacking on code.

Also quite quickly I got used to the fact that eval-last-sexp wasn't so helpful with things like a defvar or a defconst, if I was changing them up to try out new ways of doing things with the code I was working on; there I had to remember to get used to using eval-defun (C-M-x). Hardly a great problem, but something to keep in mind1.

Pretty quickly, as I worked on longer packages, I found myself wanting to, in effect, unload a whole buffer of code and evaluate it again. From this desire came nukneval.el.

The original version of this has been sat around since 2002 or so, perhaps a little earlier, and has served me well every time I've been messing with a new package. While I suspect there is (now, perhaps was then too?) a better way of doing things, the approach used in nukneval helped me learn some things and served me well (and still does). Now it's muscle-memory to run it.

The way it works is quite simple: go to the start of a buffer, read each form, check if the car is of a given list of symbols, decide if it's something I want to unbind, and then pick either makunbound or fmakunbound and use that on the symbol. Finally, once the end of the buffer has been hit: eval-buffer.

I've just released v1.3 as part of my slow wander through my old Emacs Lisp packages, with this release cleaning up a deprecated use of setf to move point, and also rewriting the code so it's a bit cleaner and also gives better feedback.

make-phony.el v1.3

1 min read

A wee bit over 5 years back I wrote a tiny package to quickly insert PHONY target markers into a Makefile. While it's far from my most-used package, it's one that gets a call on occasion, so it's one I still carry around in my Emacs configuration.

Given I'm currently engaging in a slow background process of cleaning up some of my Emacs Lisp packages, removing some obsoleted practices, I've given make-phony.el a little bit of attention.

As well as dropping the use of setf to set point to the start of a line, I also tweaked the code a little so that it only inserts a PHONY if there isn't already one there. While that's hardly been a problem for me, it just felt like a neat bit of cleaning up to how it works.

blogmore.el v2.5

2 min read

Following on from yesterday's release, I've bumped blogmore.el to v2.5. The main change to the package is the thing I mentioned yesterday about the toggle of the draft status. The draft toggle yesterday was pretty simple, with it working like:

  • If there is no draft frontmatter, draft: true is added
  • If there is any draft frontmatter, it is removed

This meant that if you had draft: false set and you went to toggle, it would be removed, which is the same as setting it to draft: false.

Unlikely to generally be an issue, but I also couldn't let that stay like that. It bothered me.

So now it works as you'd expect:

  • If there is no draft frontmatter, draft: true is added
  • If draft: true is there, it is removed
  • If draft: false is there, it is set to draft: true

Much better.

Another change is that I fixed a problem with older supported versions of Emacs. I didn't know this was a problem because I'm running 30.2 everywhere. Meanwhile, thanks to package-lint-current-buffer from package-lint.el, I have:

Package-Requires: ((emacs "29.1"))

in the metadata for the package. Turns out though that sort used to require two parameters (the sequence and the predicate), whereas now it's fine with just one (it will accept just the sequence and will default the predicate). So of course blogmore.el was working fine for me, but would have crashed for someone with an earlier Emacs.

As for how I found this out... well I finally, for the first time ever, dived into using ERT to write some tests. While I've used testing frameworks in other languages, I'd never looked at this with Emacs Lisp. It works a treat and is great to work with; I think I'll be using this a lot more from now on.

Having got tests going I realised I should run them with GitHub actions, which then meant I managed to discover setup-emacs. Having found this the next logical step was to set up a matrix test for all the versions of Emacs I expect blogmore.el to work on. This worked fine, except... it didn't. While the tests worked locally, they were failing for some Emacsen over on GitHub.

And that's how I discovered the issue with sort on versions earlier than the one I'm using locally.

All in all, that was a good little period of hacking. New things discovered, the package improved, and a wider range of support for different versions of Emacs.

blogmore.el v2.4

1 min read

I've just released a little update to blogmore.el, adding blogmore-toggle-draft as a command. This came about in connection with the feature request that resulted in BlogMore v2.7.0 being released.

While I don't personally use draft for my posts, I can see the utility of it and if someone were to happen to use blogmore.el, it could be useful to have this bound to a handy key combination.

As for how it works: that's simple. When run, if there is no draft: frontmatter property, then draft: true is added. If draft: is there it is removed. Yes, it does mean that it will also remove draft: false too but... eh. Okay, fine, I might handle that case as a followup but I couldn't really imagine someone wanting to keep draft: false in the frontmatter.

If a post is ready to go, why bother with a header that means the same thing when it's not there?

binclock.el v1.12

2 min read

Some time in the late 1990s, after I'd been using GNU Emacs for a few years1, I grabbed a copy of Writing GNU Emacs Extensions. While I'd obviously created and added to and tinkered with my ~/.emacs some, I'd never written any non-trivial code. I feel it was around 1998 or 1999 that I really started to get into trying to write actual extensions, thanks to that book.

I can't remember what the first complete package was. I think it was actually 5x5.el2 but it might also have been binclock.el. Honestly, it's so long ago now that I don't have any good recollection and I don't have any record3. All of which is to say, binclock.el is one of my oldest bits of Emacs Lisp code, so it seemed fair that while I'm cleaning things up, I should give it a tidy too.

For anyone curious: it's a very simple binary clock type of thing. It opens a very small window and, depending on your settings, shows the time in various ways, all of which are in some way a binary display.

I can't say I've honestly ever had it running for more than a few moments, as an amusement, but I do remember it being a really helpful body of code to work on to help get familiar with Elisp. So, 27 years on from when I first opened the buffer to create it, it's tweaked and tidied and hopefully ready for another 27.

Would be cool if I'm around long enough to give it yet another tweak then.


  1. I first met it on OS/2 of all places, in 1994, or thereabouts. 

  2. Yes, the one that became Tools > Games > 5x5

  3. If I was using version control at all then, it was probably just rcs