No Graph Needed

 — 

This month's Emacs Carnival theme is The Search For Knowledge, which is kinda something near and dear to my heart. I wrote an Obsidian to Emacs post from a little over a year ago and one of the things that I mentioned is that Obsidian has a Knowledge Graph that I didn't use. denote-explore does have one and I set graphing up once but I don't use it at all. I work just fine without a Knowledge Graph. Using Denote, ripgrep, and consult, things work great for me. It's smooth and not a lot of fuss.

I literally do all my knowledge management now within Emacs in a Denote system. I dump all of my notes into one folder in org format. Denote puts a time stamp on the file name as well as the title and the tags. Searching by tag is really just a filename search. See examples from my own denote folder:

20260714T130657--org-agenda-views__orgmode.org
20260714T100032--filtering-org-ql-results__orgmode_productivity.org
20250605T091129--escape-character-in-orgmode__orgmode.org

For me the file name is the index. It's simple. I don't need a SQLite database or anything else on the backend. As soon as I create a file I know that I can search by the file name or search its contents. This keeps things simple and, for me, simple is good. When I want to search for all of my notes about orgmode, I just run my mh/consult-denote-find-files , type orgmode and all of those files appear. If I want to add something else to that search, I just type that in. Here is my simple function that does that, using consult-find

(defun mh/consult-denote-find-files ()
  "Find files in the Denote notes directory by name via `consult-find'."
  (interactive)
  (consult-find (car (denote-directories))))

So that brings up the files using the wonderful consult-find format. I just keep typing to narrow down my search. Easy and effective.

And inside the files, the denote tags are the same as org mode tags when the document is open since it uses the TAGS property on the top of the file. I generally use just ripgrep for that – see the function I use below:

  (defun mh/rg-org-wiki ()
  "Search ~/org-wiki with ripgrep via `consult-ripgrep'.
Follows symlinks and ignores JSON files."
  (interactive)
  (let ((wiki-dir (expand-file-name "~/org-wiki")))
    (if (file-directory-p wiki-dir)
        (let ((consult-ripgrep-args
               (concat consult-ripgrep-args " --follow --glob=!*.json")))
          (consult-ripgrep wiki-dir))
      (message "Directory ~/org-wiki does not exist!"))))

(yes, my denote files are in a folder called org-wiki. That actually dates back to a pre-Obsidian setup I had once. This function actually dates back to that but it's easy to reuse here. Again, another win for Emacs and simplicity)

I do have denote-explore installed and I do use denote-explore-random-note to, well, explore my notes. I find things that I forgot, and find things to update. I use that generally when I just want to work in my notes for a while.

But that is the exception and not the rule. I set up links to explore my notes. Using the example files above, I use denote-link to link to a top-level OrgMode note (since they're all about OrgMode). That note is kind of a Map Of Content about OrgMode. That is how I want to discover notes… the individual notes link to top-level notes and that note is linked to all else about OrgMode. Well that is the theory anyway… I set it up but I don't do it near as much as I thought I would. This goes back to how I used Obsidian. I tried really hard to use a very rigid and disciplined link structure and at the end I find I didn't use it. It took more time than writing a lot of my notes. So really my flow is – I find and read notes that my past self used and update them to what I currently think but I don't necessarily follow a web of links. While a graph is useful for a lot of people, it just doesn't seem as useful for me. The filename is a good enough index for me.

Category: tech Tags:

Why Org's agenda ignores your start day

 — 

I feel like a bad Emacs user because, while I've been using it for a long time, it's only been in the past few months that I have embraced Orgmode as my task manager. Sometime I will probably write about my journey to this point, but for now I want to talk about a small annoyance I had in org-agenda.

I use org-agenda many times a day. I used to use the Week view (C-c a a) because it gives me an overview of what I finished and what is coming up. The problem is that a week is a fixed box. On Monday I saw a full week ahead of me. By Friday that list was mostly useless because all I saw were the tasks I'd finished earlier in the week and the few I had left, with next week's work still invisible – it won't show up until next week.

But that's not how I work. By Thursday I don't generally care what I finished on Monday, but I'd love a peek at what's coming. What I wanted wasn't a calendar week at all – I wanted a window that follows me, always showing a little behind and a little ahead, with today sitting near the middle.

In typical Emacs fashion, this was easy to do – it was three lines:

(setq org-agenda-span 6
      org-agenda-start-day "-2d"
      org-agenda-start-on-weekday nil)

The first two options make a lot of sense:

  • org-agenda-span 6: show six days instead of a rigid calendar week.
  • org-agenda-start-day "-2d": start two days before today. Org accepts relative date strings like -2d or +1w here, which makes this easy to tweak.

The third line is seemingly innocent but actually it's the one that matters the most. If you set only the first two, nothing changes. You'll tweak start-day, reload the agenda, and watch it snap right back to Monday, and you'll wonder if you fat-fingered a variable name.

You see, by default org-agenda-start-on-weekday is set to a weekday, and when it's set, Org rounds your start day back to that week boundary – silently overriding start-day. Setting it to nil tells Org to stop snapping to the week and honor the start day you actually asked for. Of course, its docstring explains it if you already know to go looking, but nothing points you there when your config silently fails.

Once I had that, I went looking for how to zoom in and out from my rolling agenda and found the view shortcuts:

  • d - day view

  • w - week view (so 7 full days)

  • vt - fortnight (14 days)

  • vm - month

  • y - year

    To be more specific, v gives you a menu of options to choose from – everything that I listed here and more.

    None of this is strange – it's three variables that ship with Org. But org-agenda-start-on-weekday is a good reminder that in Emacs, the config item you want usually already exists, and so your reasoning should be less of "there's no setting for that" and more about "what setting am I missing?".

Category: tech Tags:

How Emacs Uses Nix

 — 

It's been a while since I wrote for the Emacs Blog Carnival but I have missed it. I decided to dip my toes back in when I saw July 2026's theme - Programming.

I started thinking about how my Emacs configuration for development isn't that special and then I realized that the centerpiece of my environment, direnv and Nix, is something that is worth talking about. I have a whole post on setting them up so this will continue with what that post glossed over: How does this work with Emacs?

The idea started off with the fact that I don't want separate configurations for my CLI and Emacs. I want to just configure it once and things will just work in both. Since getting my shell to see my Emacs configuration seems like a waste of time, I chose to let Emacs read what the shell was doing. What I ended up with is my system-wide Emacs reading the same configuration that my shell does, and then by default, Emacs uses the same paths and interpreters. I don't need a .dir-locals in any project folders to set special paths. It just works.

The center of this is the envrc package. It runs direnv when each buffer is opened so you have the right configuration when you need it. I don't think the envrc maintainers sought to support Nix in their package, but because it supports everything direnv does seamlessly, it supports Nix by default.

The envrc configuration in Emacs is easy:

(use-package envrc
  :hook (after-init . envrc-global-mode))

Then I make sure I set up my .envrc and shell.nix files like I did in my old post and then make sure I run direnv allow and… that is it. Whenever I open up a file in that project after that inside Emacs, envrc will setup the environment automatically. It finds the right interpreter, LSP engine, etc. I don't have to do anything.

This is especially powerful for LSP. This is an actual shell.nix file for an old Python project I maintain:

{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = [
    pkgs.python311
    pkgs.poetry
    pkgs.basedpyright
  ];


}

I install the LSP server – Basedpyright in this case – within the project instead of globally. That way I know that it has the right version of Python and can get its dependencies and then Eglot starts up just fine. When I need to work on this project, I just cd ~/Projects/project-a and then my shortcut for helm-projectile-switch-project and choose the project folder in Emacs.

For completion's sake, here is the .envrc for the same project:

use nix

The magic is that this is all local to the buffers. I can have two projects open at once, each pinned to a different Python, and Emacs just does the right thing in each.

So in project-a it looks like:

;; a buffer under ~/Projects/project-a
M-! which python
/nix/store/j0r98nqnq891c08q2dns661qnz42fz49-python3-3.11.15-env/bin/python

Then I switch to project-b

;; a buffer under ~/Projects/project-b
M-! which python
/nix/store/jn7s7larin2h9yz8fn9g0zkz5asg8i4d-python3-3.13.12-env/bin/python

Same Emacs, same command, two different interpreters because I'm in buffers in two different projects. I never touched exec-path or dropped a .dir-locals.el in either project. Each buffer is running under its own project's .envrc. Of course if I add environment variables to my .envrc file or change that in any way, I have to do another direnv allow in that folder, or in Emacs run envrc-allow in a buffer in that project.

On the other hand, I can change the shell.nix file as much as I want and it will be automatic when I reload the buffer.

Both my shell and Emacs use the same configuration, and I don't even think about it anymore. It just is so seamless and automatic.

Category: tech Tags:

Hyperlegible Fonts

 — 

I've always been a bit picky about fonts in books, the web, programming, etc. I want something that is easy to read and tell characters apart. While some developers choose fonts that are very light so they can get more characters in a line or screen, I go with readability and want my programming fonts bolder. It's a reason that I searched for a long time and chose IBM Plex Mono as my chosen programming font. Every once in a while I look at other fonts but always come back to Plex Mono. There is even a game about choosing a coding font and Plex Mono wins out for me. It's even open source – download it from Github.

This journey with programming fonts was great but I was struggling with my reading font and it was harder to make a good choice. You see, I read a ton on my Kobo Libra Color and I really enjoy it but have noted that long reading sessions fatigued my eyes. I messed with different built-in fonts but nothing ever really worked. Even increasing the font size a bit didn't really help, or it was so big it was annoying. Someone in a Reddit post posted a link to the Atkinson Hyperlegible font from the Braille Institute. I figured if people that help people who can't see or can't see well made a font to be hyperlegible, it must be good. So I downloaded it (I had to give them my email address but no money) and installed it on my Kobo (not hard but you do need to understand folders, etc). Honestly it took me a bit – I usually preferred more of a serif font to read but as I got used to it, I really found it to be, well, hyperlegible! I even toned down the size a bit and still like it. I would highly recommend giving it a shot!

Category: tech Tags:

More Map Utilities

 — 

A few weeks back I wrote about Maps and how MapUtils.isEmpty from Apache Commons Collections made the null-checks go away with isEmpty. Since then, I've been poking around the rest of MapUtils and it turns out the isEmpty method is not the only goodie hidden in there. MapUtils also has getter methods that are arguably even more useful, especially in a codebase where pretty much everything is a Map<String,Object>, for better or for worse (ok, probably for worse).

Here's the pattern I've been untangling lately:

// this is messy, since we always use Map<String,Object>
String myString = (String) myMap.get("someKey");

That cast is doing a lot of quiet work: it assumes the key exists, that the value is non-null, and that it's actually a String. Any one of those assumptions failing gives you a NullPointerException or a ClassCastException at runtime.

The first improvement I reached for is in the standard Map interface: getOrDefault:

// this is what I have been doing, to fix the NPEs
String myString = (String) myMap.getOrDefault("someKey", "");

This is somewhat better, I mean at least now missing keys don't blow up. But the cast is still there, and if some upstream code stuck an Integer in that slot, I still get a ClassCastException.

Enter MapUtils.getString:

// but this is better
String myString = MapUtils.getString(myMap, "someKey");

No casting at all! It handles the type coercion for me, and if the key is missing or the map is null, it returns null instead of throwing an exception. And of course, you can supply a default:

// but this is even better, because it saves the null
String myString = MapUtils.getString(myMap, "someKey", "");

Now I've got a one-liner that handles a null map, a missing key, a null value, and a value of the wrong type, without a cast and without a chain of if statements.

And it's not just getString. There's a whole family: getInteger, getLong, getBoolean, getDouble, getMap, and so on, and each has a default-value overload. If you live in Map<String,Object> land, these will save you a lot of defensive code.

What strikes me about all of this is that Commons Collections has been sitting in our classpath for years. It's not a new dependency, it's not exotic, it's just there. I've been writing casts and null checks by hand when the library I was already using had a cleaner answer the whole time. A reminder that it's worth occasionally re-reading the Javadocs or poking through the methods in autocomplete to see what you are missing (that is exactly what I did here).

Category: tech Tags:

Modern Java Maps

 — 

I'm back doing Java stuff for a while, and a lot of our codebase uses Maps a lot. Though it's a new project, the codebase leans heavily on the style of Java 7/8 and not with the new patterns that came with Java 9 and above. Here are some things that I have learned lately.

Creation

I've seen this pattern many times over my career and never loved it:

  Map<String,String> myMap = new HashMap<>();
  myMap.put("A","B");
  myMap.put("C","D");
  myMap.put("E","F");
//  ....

Java 9 introduced better ways to initialize a map. The simplest:

Map<String,String> myMap = Map.of("A","B","C","D","E","F");

Keys and values go in alternating order. One catch: Map.of only supports up to 10 entries. For larger maps, there's Map.ofEntries:

Map<String,String> myMap = Map.ofEntries(Map.Entry("A","B"),
                                         Map.Entry("C","D"),
                                         Map.Entry("E","F"));

More verbose, but still much cleaner than the old approach and there's no entry limit.

Testing for Empty

Empty-checking in Java is trickier than it should be. If the object might be null, the naive check becomes:

if (myMap==null || myMap.isEmpty()) { ///

That gets old fast. Apache Common Collections has a cleaner option and chances are it's already in your project even if you didn't add it explicitly:

if (MapUtils.isEmpty(myMap)) {////

MapUtils handles the null check for you. Worth noting: you can't use CollectionUtils here — it won't compile, because Map doesn't implement Collection in the Java type hierarchy. You can also get an immutable empty map from standard Java:

Map<String,String> myMap = Collections.emptyMap();

So Collections considers Map a collection but not CollectionUtils. Java consistency at its finest.

Coming back to a Java codebase after time away is a strange experience. The language has genuinely improved, but the old patterns are still used everywhere, written by people who had no reason to change them. I suspect I'll be finding these kinds of small upgrades for a while.

Category: tech Tags:

My Emacs Misconceptions

 — 

This is my Emacs Carnival post this month on Mistakes and Misconceptions.

Just in the past year, I realized something surprising: I no longer have vi muscle memory. I was a dedicated vi user for fifteen years before I switched to Emacs, and at some point it just… left. I can still do the basics with some effort, but the more advanced things I used to reach for in vi are simply gone. I now do everything much more easily in Emacs — without thinking about it.

So fixing what little vi usage I have left was easy: on the remote systems I log into, like my NAS, I alias vi and vim to mg, which is a lightweight Emacs clone. It gives me quick "edit and out" capability on the command line. It's a small thing, but it says a lot about where I've landed.

But to understand how I got here, I need to tell you how I first started using vi.

The Misconception I Inherited

My first job in college left me no choice but to use vi. Using anything else on our Solaris workstations was unheard of. We had an Emacs user who started that installed it in his home directory. The next day, when he logged in, it was gone. The sysadmin had a cron job that scanned for Emacs installs and deleted them overnight. The message was clear: Emacs was big, unwieldy, and not worth our time.

That misconception stuck with me longer than it should have.

Those early impressions didn't help. Vi has a steep learning curve, sure, but the basics were approachable — you could at least move around without thinking too hard. Emacs required two-key combinations just to navigate. That seemed like madness.

What Actually Changed My Mind

Years into my next job, I was supporting an application that required juggling a lot of files at once. Vim, at the time, made that genuinely painful. I knew some of the developers I admired online used Emacs heavily, so I decided to give it a real try.

I went cold turkey. No Viper, no Evil mode, no vi emulation layer — just stock Emacs keybindings from day one. Partly for the workflow improvement, partly out of curiosity. I haven't really looked back. That was 25 years ago.

The Misconception I Held About Myself

Here's the thing I didn't expect: I thought keeping some vi fluency was important. That it would always be there as a fallback. Fifteen years of muscle memory doesn't just disappear overnight. It decays slowly from lack of use – and I don't miss it. Not because vi isn't good, but because Emacs has replaced the underlying need. The things that made Emacs look unwieldy at first — the key chords, the weight, the learning curve — turned out to be features, not bugs. The same depth that frustrated me early on is exactly what made vi fade away naturally.

My config looks very different than it did a few years ago, and almost nothing like it did when I started. But that's the point. It grew with me, rather than the other way around.

Category: tech Tags:

Readeck: More Than a Read-It-Later App

 — 

In my last post I mentioned I switched to Readeck as my read-it-later app. It's starting to become essential to my workflow. The first thing that struck me was how fast it feels. My old Wallabag instance was sluggish when saving an article. Readeck saves asynchronously: you tell it to save a link, it goes into a queue, and Readeck processes it in the background. You can see the status in the web app interface. The same thing happens when you delete an article – it doesn't get deleted right away but the article is marked on the interface and it will go away soon. This sounds like a quirk (especially for the delete) but it keeps the interface responsive – the heavy lifting happens behind the scenes.

Readeck's Collections are a feature that I didn't expect to like and now they have become essential. A collection is basically a saved search – articles matching a tag, a keyword, a URL pattern, or a date range are collected automatically. I have one for articles saved in the last seven days and a few organized by topic. The Collections are like "smart folders" – they update without me doing anything.

Recently I started using the epub export. You can export a single article or an entire collection to an epub file and load it onto a Kobo or any other reader. Long articles and shortish stories read a lot better for me on eInk. I do wish there was a direct save to Dropbox or Google Drive so it lands on the Kobo without the manual transfer step.

You can also share your articles within Readeck. It generates a URL to the nicely formatted article that you can share with people. That URL expires after a certain amount of time. That expiration seems a little weird but it is the difference between sharing with a few people and re-publishing the original.

Readeck also has annotations for highlighting sections in articles and those highlights show up when you share an article publicly. I would like a mass export to a file that has a link to the article in my Readeck server, but that is minor. Annotations within the webapp work really well. Annotations are the main reason I use the mobile browser rather than the app – in the browser I can highlight things but I can't in the mobile app and the mobile site works really well.

The feature I've gotten the most personal use out of isn't in Readeck itself – it's a workflow I built around it. I used to save articles and then never look at them, so my reading list turned into a graveyard. I solved this with an iOS Shortcut that calls the Readeck API to save the page and simultaneously assigns a unique tag. The tag, Readeck URL and my API key are stored in DataJar (a free app for storing state, secrets and values in Shortcuts). When I save an article, the title and tag flash on screen, and I write them in my bullet journal as a reminder to at least open it later. The Shortcut is here if you want to adapt it.

If you want to try Readeck before committing, there's a Docker image you can run locally. That's exactly how I started – I saved a few articles, poked around, and only then moved it to Pikapod. It was certainly worth the experiment. If you're already happy with Instapaper, you probably won't find much to convince you to change. But if you've ever wanted a self-hosted option that doesn't feel like a compromise, Readeck is a great choice.

Category: tech Tags:

Pikapod Apps

 — 

I realized that my article from last fall on self-hosting on the NAS is now outdated. Not only am I not hosting apps on my NAS anymore, but I'm not even using those apps anymore. I started using Pikapod to host my RSS and read-it-later apps. Pikapod hosts a lot of open source apps of all types and needs.

I stopped hosting on my NAS simply because things got really slow, especially Wallabag. It even affected my normal NAS operations of copying and hosting files, which is really its main job for me. I stumbled upon Pikapod from the Installer newsletter and I decided to give it a shot. Ironically, they didn't have Selfoss as an option, so I did some research and picked FreshRSS. Also, I decided that there were things about Wallabag I didn't really like, and after some searching, I decided to choose Readeck, and I've been very happy with both.

Both of my apps are very fast, and I pay under $5/month to host both of them. Pikapod basically takes a Docker container and starts it up – though they don't give you any real details on how the apps run.

Not everything has been golden. I wanted to install some plugins in FreshRSS, so I had to temporarily turn on an SFTP connection to that pod and move the files up there. The only way to make your own personal backup of your apps (which you should do) is to use S3 on AWS. You have to dedicate a whole bucket to the backups, which isn't that bad though it gives you more to manage in your AWS environment. But the cost of the S3 usage is negligible.

As far as URL goes… you can use their randomly-generated URL or add your own cname to the pod. I like to start the pod up with the random name and then put in my cname when it's ready – yes you can change the URL after it's running.

There's no CLI or anything like that on your host apps, which is both good and bad considering I tend to make trouble if I'm given shell access. So, for me, the limitations are the feature: just enough control, not enough rope to hang myself.

The pods themselves work really well. Overall I'm really happy. In the future I'll cover FreshRSS and Readeck in more depth but I felt Pikapod needed a post in and of itself. If you need CLI access or absolute complete control, this isn't for you. If you want open-source tools that stay running, Pikapod is hard to beat.

Category: tech Tags:

Emacs Carnival: Completion

 — 

This is part of the February 2026 Emacs Blog Carnival on Completion

For me, completion is a odd thing to have as a carnival theme mostly because I just "do completion" and don't think about it. I really had to do a lot of thinking just to figure out what I would write. And then I realized that I use completion all the time - I just don't think about it.

In Buffers

To be frank, normal buffers I use the built-in hippie-expand which uses the contents of the buffer and maybe other buffers to offer smartest thing. So the ironic thing is I don't set up a lot of fancy completions - I just type M-/ and get what I want. For example, in a Python file I type def then M-/ and hippie-expand suggests defaultdict from my imports at the top of the file

I do use some lsp-modes and they give me completions, but it's a long list and that I don't generally use . But if it happens to be a long line that is exactly what I want, I have no problems using that. For me, it's generally not work setting up lsp mode for all the modes I'm into – just the big ones: Python or Java.

I also have yasnippet installed, but I don't use it all that often. I have even built my own templates, but to be frank, I kind of forget about them and just write my own code. Most of my problem is that I don't always know what the shortcuts are – so I just don't do it. Thinking about part of my completion journey caused me some time with robot friend Claude and we came up with a way to dump the yasnippet shortcuts and templates to a Denote file – could be easily changes to a normal Org file. See it here: https://gist.github.com/squarepegsys/2fe7caa75b862f6604a0afca660a8d58

This has made it more clear what each mode has. And my cheatsheet for orgmode has become handy while writing this post!

Utility Completion

I'm defining this as "not in the main buffer" – so the minibuffer, commands, switching buffers, looking for files, etc.

For this, I still use what I have using for many years: Helm. I've read about newer frameworks like Vertico, but I've never installed them to try. They don't seem to offer anything Helm doesn't already do, while Helm integrates with so many more packages. Helm integrates natively with projectile, ag/rg, org-mode, and dozens of other tools - workflows that require additional packages and configuration with newer stacks. If I was starting Emacs now, I would start with something else, but I have never had a reason to move from Helm.

For example, long ago I assigned the standard C-x C-f to helm-find-files. I type part of a filename, fuzzy match through directories, and soon I have exactly what I want – with the option to copy, rename, grep, or open as root without ever leaving the interface. It's essentially muscle memory now. Switching to Vertico would mean relearning what C-x C-f does, and losing actions I don't even consciously remember I use.

And, to expand on this some more… helm-find-files can do a lot more than just loading up the next file, Hitting Tab brings up a slew of options, like running grep, search and replace, etc.

More Helm magic I can't do without is helm-swoop. Think of it as a live, interactive grep of your current buffer—as you type, matching lines appear instantly. You can jump to any match, or – and this is the magic – press C-c C-e to enter edit mode, make changes across all matches, and C-c C-c to commit them back to the buffer.

It's hard to explain how that works, so below is a Gif of it in action, in a file of junk text finding and replacing Id with Ego:

In Conclusion

At the end of the day, I don't think about using commands like hippie-expand or helm-find-files – I just use them. They are muscle memory for me, a step in a longer workflow and I just expect that step to be there. That is probably the best part of completion in Emacs – it just works. Or, to put this back on theme: completion should be the sideshow to your Emacs Carnival.

Category: tech Tags:

© Mike Hostetler 2016

Powered by Pelican