(redirected from Homepage)

Strona domowa

Witam na mojej prywatnej stronie internetowej!

[If this is all Polish to you, click here: English]

Uwaga: z oczywistych powodów nie mogę zagwarantować swojej nieomylności, choć staram się o zgodność tego, co piszę, z Prawdą. Jest również oczywiste, że nie gwarantuję takiej zgodności w przypadku komentarzy. Umieszczenie linku do strony spoza niniejszego serwisu nie musi oznaczać, że podzielam poglądy autora tej strony, a jedynie, że uważam ją za wartościową z takich czy innych powodów.

Marcin ‘mbork’ Borkowski

2023-11-27 Clocking in to parent entries

Like many, many Emacs users these days, I’m a heavy user of Org mode. In fact, both of my blogs are written in Org. And while this very blog usually has fairly short entries, my other blog has much longer posts, divided into subsections. This creates a minor problem for me. I track time I spend on writing, but I don’t want to track time spent on individual sections of a blog post. (The reason is a bit complicated. These sections are often pretty short, and I tend to spend my writing time in chunks of 25 minutes, and I have a Beeminder goal to have at least 10 of such chunks every day, so if possible, I prefer not to divide my work time into too short stretches.)

In other words, even if I am in some lower-level heading, when I clock in, I want Org to clock in in the parent heading. Emacs being Emacs, I figured there must be a way to make that happen automatically.

It seems there are at least two ways of achieving this. One is advising the clock-in function, and the other is using one of the several clock-related hooks. It is known that hooks are the preferable way – the Emacs Lisp reference says,
If it is possible to do the same thing [modify a function’s behavior] via a hook, that is preferable.

M-x apropos-variable RET org.*clock.*hook gives several answers, and the most natural candidate seems to be org-clock-in-prepare-hook. A cursory examination of Org sources suggests that using this hook may solve my issue. So, let’s get to work!

Let’s begin with deciding how to mark headings which should be “skipped” when clocking in. Assume that I have the following file structure

* Main entry
** First subentry
** Second subentry

and clocking in when in First subentry should actually clock in the Main entry. How to tell Org this is what I want? An obvious way is to use some tag on First subentry, but I don’t like this idea. I want the “subentries” to be lightweight and easy to add, and having to remember to tag them goes against this goal. I would very much prefer to be able to mark the Main entry as the one to clock in to whenever the user starts work in any of its subentries.

So, let’s get started. (I have to admit that I needed several failed attempts to get this right.) What we need is a function which does this:

  • if the current entry has the CLOCK_HERE property, stay at it;
  • otherwise, go up one level;
  • rinse and repeat unless we get to the top level, in which case get back to where we started.

Here is my code.

(defun go-up-until-clock-here ()
  "Go up in Org hierarchy until an entry with property CLOCK_HERE.
If no such entry is found, leave the point where it was."
  (let ((dest (point)))
    (while (if (org-entry-get (point) "clock_here")
               (not (setq dest (point)))
             (org-up-heading-safe)))
    (goto-char dest)))

(add-hook 'org-clock-in-prepare-hook #'go-up-until-clock-here)

This code is a great illustration of what you (usually) shouldn’t do. It’s extremely concise, has no comments, and – dare I say – is a bit clever. Here’s how it works.

The body of the while loop is empty, so the loop will just repeatedly run the if form until it evaluates to nil. That can happen in two situations.

One of them is when the entry the point is in has the CLOCK_HERE property (by the way, Org mode heading properties are case insensitive), the dest variable will be set to where we are, the not form will evaluate to nil and the while loop will end. This may happen during the first iteration if we happen to start in a “clock here” entry or later, when we get to one while moving up. In either case, the subsequent (goto-char dest) will do nothing, since dest will coincide with (point) – but it won’t hurt, either.

The other way the loop may end is when there is no parent heading to go and org-up-heading-safe returns nil. This means that we traveled to the top of the hierarchy of headlines without finding one with the CLOCK_HERE property. In that case, dest will be what it was first defined to be, that is, the position of point before our function was called, and (goto-char dest) will return to that place as planned.

Now the only remaining thing is to make sure that every headline which starts a blog post has the CLOCK_HERE property set. My little blog engine has the org-clive-create-page command, but I didn’t want to add code very specific to my usage there. (I suspect I am the only user of Org Clive, but maybe I am not, or I will be not.) Of course, that means that what I want is to add a hook to that command. And that is precisely what I have just pushed to master – the org-clive-after-create-page-hook variable. I added this to my init file:

(defun org-add-clock-here-property ()
  "Add the `CLOCK_HERE' property to the current node."
  (org-set-property "CLOCK_HERE" "t"))

(add-hook 'org-clive-after-create-page-hook #'org-add-clock-here-property)

and now I don’t have to worry about adding the CLOCK_HERE property myself anymore.

As usual, Emacs lets you have it your way.

CategoryEnglish, CategoryBlog, CategoryEmacs, CategoryOrgMode

Comments on this page

More...