Emacs pages and micro-packages

Published 2025-09-09

tag(s): #programming #emacs

A couple days ago I mentioned that I learned about page-ext, a built-in Emacs package that adds page navigation features.[1]

Turns out page-ext has some things going against it:

  1. It clobbers C-x C-p with a its own prefix map that has lots of commands. A very minor crime, except...
  2. Many of said commands are geared towers using a text file as an address book. With entries separated by page delimiters.
  3. It really likes to narrow-to-page, which can be disabled in most, but not all the commands.
Now, none of these are unsolvable problems (of course), and I can ignore the address book feature. But if I disable all options for narrowing plus fixing the cases that don't have an option...that's quite a bit of code to add to my configuration.

And at the end of the day there is only one feature I really liked and want to explore: the "page directory".

Reducing scope to reduce complexity

I figured I could do the same thing I did with a few packages by now, which is to create a less featureful, and more tailored, version of the code. Most times it ends up being completely different, because for the one or two things I want to do, I don't need to deal with edge cases or complex scenarios.

The first time I did this was with fill-function-arguments, which "pretty prints"...well, function arguments. And literals of lists, dictionaries, etc.
Except that sometimes it would not indent things correctly, or put the second argument in the wrong place, and a bunch of other corner cases around comments and strings.
And after adding more and more little tweaks in my own code to account for these problems, I figured I might as well replace the whole package with my own version.[2]

Writing that was an interesting exercise because as I dropped more and more features, I came to realize that what I really wanted was: to split a line by separators (but keeping strings alone), and indent the altered text. That was equivalent to 90% of my uses of fill-function-arguments.
So I wrote a single command that does exactly that.
It is way more limited in scope, but it is also just a few lines of elisp. And since it does less, it also has less surprising or unexpected behaviours.

page-ext to hoagie-pages

I wrote today hoagie-pages. It is v1, so might take some time until it is polished. The code is in my config repo.

It does away with almost everything in pages-ext but the "list all pages" portion. When I was formatting the output, I wanted occur-like bindings to navigate to the next and previous page[3]. And that made me realize that maybe I could get the result I wanted using said command instead of the pages functionality.

The most promising version of this approach was capturing occur matches to get the page "title", but there was always something a little bit off with the output. Unless I used a numeric prefix to show multiple lines around the match, and by then I had to determine how to augment the page-delimiter regexp to capture what I wanted, or find other mechanism to keep the "delimiter regex" easy to use and...

Yeah, it was getting complex rather quickly. And the occur internals are gnarly. So I decided to stick to the "mimic the pages directory" ethos.
After all, I don't even know how long the pages stuff will stick!!!

The one thing the I kept from those experiments, was making the output occur-like: on top of the bindings mentioned before, I also went for the same look. Compare these three set of results, first using the original pages-directory, then occur, and finally the new hoagie-list-pages. First in the page-ext.el buffer:

Screenshot of Emacs with three windows.
(direct link to image)

Note that for occur, I used a prefix argument of 1 to include in the output the line with the page title.
Now same comparison, but running the three commands in this very buffer:

Another screenshot of Emacs with three windows.
(direct link to image)

Bonus: Some page-delimiter ideas

As spoiled in the screenshot, I have a couple suggestions of page-delimiter values that I already set in my config:

And the last point is the one that makes me think maybe I forget about this feature and delete all the code in three months: there are already a lot of mechanisms to navigate buffers by some sort of unit.
A couple days ago I removed all imenu related code from my init file, because I never used it.

As a counterexample, when I learned about M-^ (aka delete-indentation aka join-line) I thought it was cute, but I already had cycle-spacing (with negative argument), so I didn't need it.
Yet nowadays I always join lines, and mostly cycle spaces without prefix argument. 🤷

Time will tell.

Footnotes
  1. Right here, if you are feeling lazy.
  2. Other examples of this are my mode line and my custom commands to insert/delete pairs of characters.
  3. For example, C-n moves to the next line, but a single n moves to the next line AND moves point to the match in original buffer

Share your thoughts (via email)

Back to top

Back to homepage