Preventing checkbox inheritance in org-mode
One feature in org-mode that I've wished I could customise for a long time is the checkbox inheritance feature. Org's behaviour is that if I have a list like this…
- [ ] This is a parent item - [ ] This is a child nested item
…then, as soon as I check all the child items as
[X], the parent item will
automatically be updated to
This doesn't fit with how I like to use nested checkboxes: generally I do use them to represent a kind of "subtask" concept, but it's not necessarily the full set of items that make up the "parent" task, and completing them doesn't mean I'm done with the parent item. I'd prefer to be able to manually trigger the two checkboxes independently: changing state on the child item shouldn't affect the parent.
2. Fixing it by disabling org-list-struct-fix-box
A lot of org-mode's behaviour can be customised via variables, hooks etc. However, this particular behaviour doesn't seem to be customisable, and when searching I've not found any immediate solutions or ideas that people use to achieve this.
After some poking around in the source this week, I came up with something that
seems to work for me: this checkbox behaviour is implemented in a function named
org-list-struct-fix-box. That's the only thing this function does, and so if
we turn this function into a no-op, then I get the behaviour that I want:
updating child checkbox items has no affect on the parent.
To achieve this, I define advice around the original function that prevents the original implementation from being called:
(defadvice org-list-struct-fix-box (around md/noop last activate) "Turn org-list-struct-fix-box into a no-op. By default, if an org list item is checked using the square-bracket syntax [X], then org will look for a parent checkbox, and if all child items are checked, it will set [X] on the parent too. This isn't how I personally use child items -- I'll often use child checkboxes as subtasks, but it's almost never an exhaustive list of everything that has to be done to close out the parent -- and so I'd prefer to just control the parent checkbox state manually. AFAICT org-mode doesn't provide a way to customise this behaviour, /but/ the behaviour all seems to be implemented in 'org-list-struct-fix-box'. And so I'm trying something out by turning it into a no-op. It seems to work nicely initially, but I won't be surprised if it causes an issue at some point because it's very hacky." nil)
That's it: all this time and I only had to write one line of code to achieve what I want.
3. Why use advice over
Instead of advice, another approach would be to just redefine the original
defun. I prefer to use advice though, so I can still see the
original function, still jump to its definition, explicitly know that it exists
and that I'm modifying it, etc. If I use
org-list-struct-fix-box, it will tell me that the function is advised, and as
I'm using the excellent helpful package, I can see additional information
including the definition of both the original function and the advice.
4. Isn't this pretty hacky?
Yes. I've only been using it for a few days.
seem to be called in many places though, so hopefully nothing major will break.
The most likely issue is that I break third-party packages that depend on the
original behaviour, but I don't think any of the packages I'm using are likely
to depend on this particular function.
I think this kind of approach can be risky in a prod-environment program that other people depend on, but as it's just my personal config, it's fine – I'm not sure I'd go as far to say it's encouraged, but this "advice" patching concept is one of the tools provided by Emacs to customise a module's behaviour for this exact kind of situation, where you otherwise don't have the ability to achieve the behaviour you want without redefining whole functions.
5. Contributing upstream?
It doesn't seem far-fetched to me that this could be contributed upstream: not
as advice, but as a new custom variable that can be used to disable plain
checklist inheritance – you might even be able to get away with an
implementation that just looks at the value of this new variable in
Another more complex approach would be to support new syntax for opting into the
"inheritance" behaviour on a per-list basis, similar to how you can set your
checkbox value to
[/] to have it automatically show the count of completed
6. Am I missing something?
If you're aware of a way to achieve this without patching, please let me know!
You can find my config in my dotfiles repo.