For the English part of the blog, see Content AND Presentation.
Like many Emacsers, I am a heavy Magit user. If you use Magit, I don’t need to tell you how great it is; if you don’t, I suggest you do yourself a favor and try it out.
That doesn’t mean that Magit is ideal, though. It has some issues, though usually very minor ones. Today I’d like to write about something which is definitely not a “Magit issue”, but rather something I personally miss in it.
When I start working on a feature, I create a branch for it. Usually this means pressing b c (magit-branch-and-checkout). Magit then asks me for the branch’s name. I like my branches to have short names (not more than 32 characters). When I see that the name I’ve typed seems long, I can press C-x
h M-= (that is, mark-whole-buffer and count-words-region) and see how many characters I’ve typed. I’d prefer, however, to be shown that length while I type.
This turned out to be a bit more complex than I thought it would be. First of all, when you make a mistake while working on functions you have put into post-command-hook, you might make your Emacs unresponsive. (This is exactly what happened to me while working on this very feature. From then on, I experimented with this code in a separate Emacs instance.) Second, it is easy to write a function which shows the length of the minibuffer, suitable to include in post-command-hook, but it’s less obvious how to include it there. My first idea was to add an :around advice to magit-read-string to add that function before calling magit-read-string and remove it afterwards – but this didn’t help when I pressed C-g while typing the new branch name and the code after the invocation of magit-read-string was skipped. After some experiments (and a short chat with an LLM, I have to admit) I think I found a working (though not exactly elegant) solution. Emacs has two hooks which can help with minibuffer shenanigans: minibuffer-setup-hook and minibuffer-exit-hook. They are run when entering and exiting the minibuffer, and the crucial part is that minibuffer-exit-hook is run even when the user exits the minibuffer via C-g.
So, what I decided to do was to set up hooks within hooks. First of all, I defined a simple function which echoes the number of characters in the minibuffer, using the minibuffer-contents function. Then, I created two functions, magit-branch-length--minibuffer-setup-length and magit-branch-length--minibuffer-exit-length, which (respectively) adds and removes the previously defined echoing function from post-command-hook. These functions are supposed to be added to the minibuffer hooks I mentioned above. Then I advised the magit-read-string function so that before it’s run, my hook-adding-hooks are added where they should be. In my previous attempt, I used an :around advice combinator so that these hooks would be removed after exiting magit-read-string, but that didn’t work – the removing code was skipped when I pressed C-g while entering the branch name. So, I defined one more function, whose sole purpose is to remove all minibuffer hooks, and put it in the minibuffer-exit-hook, too. (Of course, it also removes itself from that hooks.) That way, all my code gets properly removed from all hooks when I exit the minibuffer (in any way).
And that was quite a mouthful, right? I suspect there is some easier method to achieve my goal, so if you know one, please do let me know. In the meantime, I’m using this code (and I’m quite happy about having it!), and remembering the hook trickery I devised for this feature in case I need it again some day.
(defun minibuffer-show-length ()
"Show the length of minibuffer contents using `minibuffer-message'."
(minibuffer-message "%d characters" (length (minibuffer-contents))))
(defun magit-branch-length--magit-read-string-before (&rest args)
"Setup the minibuffer so that its length will be shown.
Make it so only the next time the minibuffer is used."
(add-hook 'minibuffer-setup-hook
#'magit-branch-length--minibuffer-setup-length)
(add-hook 'minibuffer-exit-hook
#'magit-branch-length--minibuffer-exit-length)
(add-hook 'minibuffer-exit-hook
#'magit-branch-length--remove-minibuffer-hooks))
(defun magit-branch-length--minibuffer-setup-length ()
"Add `minibuffer-show-length' to `post-command-hook'."
(add-hook 'post-command-hook #'minibuffer-show-length nil t))
(defun magit-branch-length--minibuffer-exit-length ()
"Remove `minibuffer-show-length' from `post-command-hook'."
(remove-hook 'post-command-hook #'minibuffer-show-length))
(defun magit-branch-length--remove-minibuffer-hooks ()
"Remove functions added to the minibuffer setup and exit hooks."
(remove-hook 'minibuffer-setup-hook
#'magit-branch-length--minibuffer-setup-length)
(remove-hook 'minibuffer-exit-hook
#'magit-branch-length--minibuffer-exit-length)
(remove-hook 'minibuffer-exit-hook
#'magit-branch-length--remove-minibuffer-hooks))
(advice-add 'magit-read-string
:before
#'magit-branch-length--magit-read-string-before)
This time my Christmas wishes to the readers are short but sincere – after some family visits I’m pretty tired. Still, as usual, I will pray a decade of Rosary for all of you. Merry Christmas!
For some time now I’ve been planning my tasks for the upcoming week every Sunday morning. Not for my day job – this is not something I want to bother my mind with on Sundays, and besides we have Jira for that – but for my personal life. Stuff like doing things around the house, planning doctor’s appointments, etc. For each task I estimate the time it’s going to take, and I don’t want to plan more than some fixed amount of time per week. Naturally, I’d like to have the total time counted for me by my computer.
Now, Org mode already has me covered – I can associate effort estimates with tasks and use column view to sum them up. Trouble is, I usually plan my week in a plain list under a single Org mode headline, and effort estimates only work for actual subheadlines. Right now, I’m doing things this way: my planned tasks are kept in a plain list (with checkboxes), the time estimates are at the end of each task, in parentheses, and the corresponding tasks are kept in a separate subtree where I actually clock them, sometimes working on one of them over a few weeks. Of course, I could change my customs (well, actually my template for the weekly review) to utilize headlines. That would have the added benefit of having a good place to clock my tasks for the week. A drawback is that some things are really “projects”, taking more than one week, so then I’d have them split to many headlines, each for a particular week’s portion of the project. This might be a good thing, though!
Funny thing is, I started writing this post with a clear goal in mind – to write a short Elisp routine to sum up my estimates in the format I use. Now that I have written about it, changing my template to use “proper” Org mode tasks seems a better idea – for example, I could have more than two states (like TODO, DONE, CANCELED and perhaps more), while checkboxes only have two. On the other hand, I’d like to show (again!) how easy it is to mold Emacs to my specific needs or habits, so let’s write that code anyway, just for fun.
Here is the format of my list.
1. [ ] Find that box in the cellar (20') 2. [ ] Write an email to an old friend (10') 3. [ ] Research vacation destinations (45') 4. [ ] Set up a reminder app for my phone (5')
So, here is what I need: start with the line the point is on, for each line find a number with an apostrophe (which should actually be a prime symbol, but I don’t want to bother with Unicode here), and sum up these numbers, stopping on the last line where such a number can be found.
So, let’s get to it!
(require 'cl-lib)
(defun sum-plain-list-estimates (&optional interactive)
"Sum the estimates (in parens, in minutes) starting on this line."
(interactive "p")
(save-excursion
(let ((total 0))
(while (let* ((line (buffer-substring (line-beginning-position)
(line-end-position)))
(estimate (when (string-match "(\\([0-9]+\\)')"
line)
(string-to-number
(match-string 1 line)))))
(when estimate
(cl-incf total estimate)))
(forward-line))
(when interactive
(message "Total minutes: %d" total))
total)))
The coolest thing about this code is that it took me less than 15 minutes to write! (And this time I made a point of not asking LLMs for help.) Of course, speed like this comes with a bit of experience, but it still shows insanely flexible and extensible Emacs is.
One thing I’d like to mention about my code is the use of the interactive parameter. It is a well-known trick to check if the function was called interactively or from Elisp, which allows me not to pollute the echo area if I ever want to call my little function programmatically, but on the other hand make it possible to pretend that it was called interactively by explicitly passing a non-nil argument to it.
That’s it for today! If you feel jealous and want to learn how to extend Emacs like this to adapt it to your specific needs, let me remind you (as usual) that a great starting point is the late Robert J. Chassell’s fantastic Introduction to programming in Emacs Lisp, and that I also wrote a little book about Emacs Lisp, as sort of a spiritual sequel to it. Happy Elisp coding!
CategoryEnglish, CategoryBlog, CategoryEmacs, CategoryOrgMode