Posts tagged with "BlogMore"

blogmore.el v2.2

3 min read

It really feels like BlogMore has kicked off a whole new thing when it comes to personal hacking. During the past few years I've developed a lot of Python applications and libraries, and have had a ton of fun doing so, but during that time I've not really done anything with writing stuff for Emacs.

To a large degree I think this says something about how stable Emacs is for me (I've been using it for a touch over 30 years at this point, you'd think I'd be kind of settled with it), but it's still always fun to have a reason to hack on some Lisp code. There's little doubt my Lisp -- especially Emacs Lisp -- has got a wee bit rusty.

So I'm having a lot of fun at the moment falling into the rabbit hole of expanding on and tinkering with blogmore.el. The reason I've just made v2.2 is a good example of exactly this. There are no real user-facing changes in the code, it was all things I just wanted to tidy up.

The main thing that has been bugging me for the past day is the repeating boilerplate that resulted from adding all the different current-blog-aware setting getter functions. There were 7 different functions, all looking like this:

(defun blogmore--post-maker-function ()
  "Get the post maker function for the current blog."
  (or
   (blogmore--blog-post-maker-function (blogmore--chosen-blog))
   blogmore-default-post-maker-function))

Exact same pattern, the only thing different being the name of the getter function being called on, and the name of the variable that contained the global default value.

So just a little earlier I cleaned this up using one of my favourite things about Lisp: defmacro. There's something about macros that makes me really like coding in Lisp, and which I cite as a really good thing when asked why I like Lisp, but which I always seem to utterly fail to explain well. Macros feel like one of those things you just have to experience for yourself to really get1.

Now, thanks to this:

(defmacro blogmore--setting (setting)
  "Generate a function to get the value of SETTING for the current blog."
  `(defun ,(intern (format "blogmore--%s" setting)) ()
     ,(format "Get the %s for the current blog." setting)
     (or (,(intern (format "blogmore--blog-%s" setting)) (blogmore--chosen-blog))
         ,(intern (format "blogmore-default-%s" setting)))))

all those 7 functions can collapse to this2:

(blogmore--setting post-template)
(blogmore--setting post-maker-function)
(blogmore--setting category-maker-function)
(blogmore--setting tag-maker-function)
(blogmore--setting post-link-format)
(blogmore--setting category-link-format)
(blogmore--setting tag-link-format)

Now the code is shorter, cleaner, and if I need to change anything I only need to change it in one place. Sure, the latter part especially is one of those "you could do that with a function too" things (have the work in one place), but here I can get the language to write me a whole load of functions, all of which refer to different functions and variables, each one based off just the one symbol.

The point of all of this being: v2.2 of blogmore.el is now out, it adds nothing for the user (who I suspect is only me anyway), but I had an absolute blast dusting off more of my Emacs Lisp knowledge and getting back the urge to code even more Emacs Lisp.

All of this has even got me tidying up my ~/.emacs.d/ and has me thinking I should go back through some of my older code and clean up all that legacy nonsense.


  1. There was a time I would have said "grok" here but... well that's spoiled now. 

  2. I suppose I could reduce that to one "call" with a loop over a list of symbols, but that feels unnecessary here. 

blogmore.el v2.1

1 min read

I've given blogmore.el a wee bump to v2.1. This release fixes a small problem I noticed today when I tried to use it to edit the tags for a post on my photoblog: the code to find and gather properties from posts didn't handle deeply-nested directory hierarchies for the post markdown files. I should have noticed this when I first wrote the code, but of course I was so busy testing against my primary blog, which only goes one sub-level deep, that I never noticed it wasn't going to work deeper.

So rather than using grep to look for things like foo/**/*.md I swapped to a combination of find and grep. Which works, but is slightly (but noticeably) slower.

Then I got to thinking that if I was doing this by hand, on the command line, I'd be using ripgrep anyway. Given this I might as well use it here. Of course, not everyone who might use blogmore.el will have rg installed so it makes sense to look for that and use it if it's available, otherwise fall back on find/grep.

There's still some low-priority cleaning up I want to do around this; an obvious change I want to make being one where I want to collapse all cases of the same word (Tree vs tree, etc) into one "hit"1. For now though, as always, it's working well enough for my needs and this change fixed an obvious issue I ran into.


  1. BlogMore itself takes care of this, but it would be nice to have the prompt in blogmore.el also take this into account. 

blogmore.el v2.0

3 min read

After kicking off blogmore.el, and then tinkering with it more and more, I've found it really quite helpful while writing posts. One thing I have noticed though -- given I use BlogMore for this blog and my photoblog -- is that I wanted to be able to use the package for working with more than one blog.

So today I found myself with some time to kill and the result is that blogmore.el v2.0 has now been released. This version allows for setting up multiple blogs, each with their own settings for where posts live, how their paths are formatted, and so on.

To handle this I've also added the blogmore-work-on command so that the active blog can be quickly changed.

All of this can be configured using Emacs' customize feature.

Customize blogmore

This has all changed since v1.x, where most of the customize options have now been renamed to include -default- in their name. The idea here is that what was the value for a setting previously is now the default value if a given blog hasn't had that setting defined.

For any given blog you wish to work with, you configure a name (for your own reference) and the path to the posts. Optionally you can also set lots of other values too.

Customize the blog

If a value is left on Default, then the corresponding default setting will be used; if it's set, then that value is used for that specific blog.

The defaults out of the box match how I do things with my blogs, of course, so the configuration is pretty straightforward. As of the time of writing my use-package for blogmore.el looks like this:

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

In the above you can see that I've set only the blog title and posts path for each blog in blogmore-blogs; the remaining values are all implied nil and so will be defaulted. The full list of values for any given blog are:

(BLOG-NAME
 POSTS-DIRECTORY
 POST-TEMPLATE
 POST-MAKER-FUNCTION
 CATEGORY-MAKER-FUNCTION
 TAG-MAKER-FUNCTION
 POST-LINK-FORMAT
 CATEGORY-LINK-FORMAT
 TAG-LINK-FORMAT)

where:

  • BLOG-NAME is the descriptive name to use for the blog.
  • POSTS-DIRECTORY is the directory where the blog's posts are stored.
  • POST-TEMPLATE is a template for new posts. If nil, blogmore-default-template is used.
  • POST-MAKER-FUNCTION is a function that takes a filename and returns a string to be used in the post's URL. If nil, blogmore-default-post-maker-function is used.
  • CATEGORY-MAKER-FUNCTION is a function that takes a category name and returns a string to be used in the category's URL. If nil, blogmore-default-category-maker-function is used.
  • TAG-MAKER-FUNCTION is a function that takes a tag name and returns a string to be used in the tag's URL. If nil, blogmore-default-tag-maker-function is used.
  • POST-LINK-FORMAT is a format string for the post's URL, where %s is replaced with the value returned by the post maker function. If nil, blogmore-default-post-link-format is used.
  • CATEGORY-LINK-FORMAT is a format string for the category's URL, where %s is replaced with the value returned by the category maker function. If nil, blogmore-default-category-link-format is used.
  • TAG-LINK-FORMAT is a format string for the tag's URL, where %s is replaced with the value returned by the tag maker function. If nil, blogmore-default-tag-link-format is used.

While I very much doubt any of this is useful to anyone else, it's at least flexible for my purposes and can probably be configured to someone else's purpose should they happen to be using BlogMore and Emacs.

BlogMore v2.6.0

2 min read

Yesterday I read a rather positive post about BlogMore, which was lovely to see. But... when I saw the link for it over on Mastodon I noticed something wasn't quite right about the description in the preview:

The preview of the post

See, when BlogMore makes a post, if the author of the post hasn't provided a description in the frontmatter, the first paragraph of text will be used instead. When doing this the code should strip out any markup (and also skip any initial images, that sort of thing).

But, as you can see, there are things like [Dave][davep] in that description. So I checked in with Andy and that was something that came from the underlying Markdown. After a bit of checking, it became obvious that the code in BlogMore was only looking for and removing inline links, but wasn't doing anything about reference links.

So, as usual, one prompt later and the issue was handled.

As it stands, I don't think I'll keep up with the current approach. It doesn't feel quite right to me. The whole point is that the Markdown should be rendered down to pure text and then the first actual paragraph of text is used. The code I have there now is doing some regexp-based mucking about as an approximate approach. It works, more or less, but it feels like it's implementing a poor Markdown parser when there's a Markdown parser already built in.

Given this, at some point soon, I might have a play and look at the idea of "let's have a Markdown to pure text parser" and then use that. I could see it being useful for other purposes too.

Anyway, the upshot of all of this is that BlogMore v2.6.0 is now available and it handles the stripping of reference links from the description, plus the recently-added strikethrough markup too.

BlogMore v2.5.0

1 min read

I've released BlogMore v2.5.0 out into the world. This release is the result of an observation Andy made about the Markdown library used in BlogMore (it might apply to MarkdownIT too, which would of course affect Hike): it doesn't support strikethrough markup out of the box.

I'm not sure I've ever used that markup anywhere on my blog, but I've used it often enough on GitHub (for example) that I just assume it's going to be there (and now I think about it Hike might be okay 'cos it uses Textual's Markdown widget which I know for certain uses GitHub-flavoured Markdown). But, for sure, the Markdown library doesn't implement it because it's not part of the original approach to Markdown.

On the other hand: implementing a form of strikethrough markup is one of the samples in the documentation on writing extensions, so it seemed like a very reasonable thing to add. Given this, one quick prompt to the agents later and strikethrough was added.

Now I'm going to totally have to find a reason to use this markup from time to time to time.

BlogMore v2.4.0

1 min read

After adding the stats page to BlogMore yesterday I realised that the main stylesheet was starting to get fairly large. Not so big that it was a problem for downloading (and of course normally it would get cached anyway), just more that it was carrying around styles for things that only appear on one page (the styles for the stats, for example).

So I decided to break it up. Now, as well as the main style.css, there's also:

This should keep the load times for the main pages and individual posts just a wee bit faster when first encountered, leaving off all those styles that aren't necessary.

All of which means, along with a wee wording change on the stats page, BlogMore v2.4.0 is in the wild and ready to use.

BlogMore v2.3.0

1 min read

I've just pushed BlogMore v2.3.0 up to PyPI. This release has a couple of bug fixes and a couple of significant new features.

The first new feature, which came in as a request, is to add support for control over the themes used for code blocks, including independent control of the themes used for light and dark mode. With these you can specify any of the Pygments styles to use for code blocks. Personally, I prefer to have things blend in, but this now also gives you the chance to have them really contrast (use a light mode theme for dark-mode blog, or a dark mode theme for a light-mode blog).

The other big feature popped into my head earlier today and once I thought about it I had to have it. It's similar to something I had for the photography section of the older version of my website, consisting of a bunch of useless but fun stats and facts about the content.

Things like which hour of the day I tend to post during:

Hour of day

Or the day of the week:

Day of week

Or the month of the year1:

Month of year

This is designed to be turned off by default -- I can imagine most folk would not want this sort of thing on their blog -- but it can easily be turned on with with_stats. The location of the stats can also be controlled using stats_path.


  1. Unsurprisingly March is leaping ahead as of the time of writing. 

Copilot rate limits

2 min read

Last night, while tinkering with another BlogMore feature request, I ran into the sudden rate limit issue again. As before, there was no warning, there was no indication I was getting close to any sort of limit, and the <duration> I was supposed to wait to let things cool down was given as <duration> rather than an actual value.

So this time I decided to actually drop a ticket on GitHub support. Around 12 hours later they got back to me:

Thanks for writing in to GitHub Support!

I completely understand your frustration with hitting rate limits.

As usage continues to grow on Copilot — particularly with our latest models — we've made deliberate adjustments to our rate limiting to protect platform stability and ensure a reliable experience for all users. As part of this work, we corrected an issue where rate limits were not being consistently enforced across all models. You may notice increased rate limiting as these changes take effect.

Our goal is always that Copilot remains a great experience, and you are not disrupted in your work. If you encounter a rate limit, we recommend switching to a different model, using Auto mode. We're also working on improvements that will give you better visibility into your usage so you're never caught off guard.

We appreciate your patience as we roll out these changes.

So, in other words: expect to be rate limited more on this product we're trying to get everyone hooked on and trying to get everyone to subscribe to.

Neat.

I especially like this part:

We're also working on improvements that will give you better visibility into your usage so you're never caught off guard.

You know, if it were me, if I wanted to build up and keep goodwill for my product, I'd probably do that part first and communicate about it earlier rather than later.

I guess this is why I don't hold the sort of position that gets to make those decisions.

BlogMore v2.2.0

1 min read

I've just bumped BlogMore to v2.2.0. This release adds post counts to the archive page.

Post counts in the archive

The overall count appears at the top of the page, with further counts broken down for each year and each month. I've tried to ensure that the counts appear subtle enough, but still readable.

Also, serving the same purpose, but giving more information at a glance, I've added the same counts to the table of contents that appears to the right of the archive if you're on a suitably wide display.

Counts in the ToC

This means I can easily see that I've posted more times this month than I have in any other month since I started this blog. In fact, I've posted more times this month than I have in quite a few individual years in the past.

So... that's today's "I thought I'd added everything but oh look here's another thing to add" feature. Which goes some way to explain why there are so many posts this month, I guess.

BlogMore v2.1.0

2 min read

It's been a couple or so days since I last made any changes to BlogMore -- mainly because I've been messing with blogmore.el -- but yesterday morning I decided to make a change I've been wanting to make for a wee while.

Ensuring fenced codeblocks were handled was one of the things that was important when I started planning BlogMore and, while the result was looking good (thanks to Pygments), the way the block itself looked in the page wasn't quite to my taste. So yesterday I wrote out how I wanted things to be changed and tasked Copilot with getting the job done.

I'm pretty happy with the result.

(defun greet (name &optional (greeting "Hello"))
  "Prints a greeting to standard output."
  (format t "~A, ~A!~%" greeting name))

For the sake of any future reader, should I happen to tweak this even more at some point in the future, here's what the above looks like at the time of writing:

Example code block

As you can see: the language for the block is now shown to the left, and there's a handy "copy to clipboard" icon to the right. I'm still not sure I'm loving the subtle border around the sides and the bottom, I think I'm going to live with that for a few days and see how it sits with me.

I'm also wondering if I should tweak the name of the language a little too, so that it's capitalised correctly. Of course, I could just get into the habit of writing the language name in the start of the block with the correct casing...

def greet(name: str, greeting: str = "Hello") -> None:
    """Print a greeting to standard output.

    Args:
        name: The name of the person to greet.
        greeting: The greeting to use.
    """
    print(f"{greeting}, {name}!")

but given how many code blocks I've got in my blog by this point, where I've typed them in lower case... it's probably easier to just tweak it when presenting it. Moreover, I do want to try and keep my Markdown sources compatible with as many rendering engines as possible and I can't be sure that all of them would downcase before doing the language lookup (although you'd hope they all would).

Meanwhile... given how much more I like how code is looking now, I'm going to have to find more reasons to include code, and Pygments supports so many languages too! Even ones from my distant past...

Function Greet( cName, cGreeting )
   If cGreeting == NIL
      cGreeting := "Hello"
   EndIf
   ? cGreeting + ", " + cName + "!"
Return NIL

As an aside: I also just noticed that they list FoxPro, Clipper, xBase and VFP as aliases for xBase-type languages, but no Harbour! I might have to see about doing a PR for that...