Commit 1b13757ba8dc

Vincent Demeester <vincent@sbr.pm>
2025-03-28 13:29:19
tools/emacs: replace org-mru-clock with my own functions
This is a very basic, non-optimized function, but it works. `vde/org-clock-in-any-heading` list all the heading so that I can then clock-in any. The list has the heading and the file name so that it's easier to filter things (e.g. type "tekton release" to find all release related tasks). It probably needs some enhancement like filtering "DONE" or "CANCELLED" keywords. Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 07d92c5
Changed files (2)
tools
tools/emacs/config/config-org.el
@@ -68,6 +68,7 @@
 	 ("C-c o r R" . vde/reload-org-refile-targets)
          ("C-c o a a" . org-agenda)
 	 ("C-c o a r" . vde/reload-org-agenda-files)
+	 ("C-c C-x i" . vde/org-clock-in-any-heading)
          ("C-c o s" . org-sort)
          ("<f12>" . org-agenda))
   :hook (org-mode . vde/org-mode-hook)
@@ -640,13 +641,5 @@ Within those groups, sort by date and priority."
 (use-package consult-org
   :after (consult))
 
-;; FIXME still doesn't do what I want. I want a simple thing: list me the org heading (with =TODO=, … keywords) and let me clock on it.
-(use-package org-mru-clock
-  :bind* (("C-c C-x i" . org-mru-clock-in)
-          ("C-c C-x C-j" . org-mru-clock-select-recent-task))
-  :config
-  (setq org-mru-clock-how-many 300)
-  (add-hook 'minibuffer-setup-hook #'org-mru-clock-embark-minibuffer-hook))
-
 (provide 'config-org)
 ;;; config-org.el ends here
tools/emacs/lisp/org-func.el
@@ -47,5 +47,49 @@ BEGIN and END are regexps which define the line range to use."
           (setq r (1+ (line-number-at-pos (match-end 0)))))
         (format "%s-%s" (+ l 1) (- r 1)))))) ;; Exclude wrapper
 
+(defun vde--get-outline-path (element)
+  "Return the outline path (as a list of titles) for ELEMENT, which is a headline."
+  (let (path)
+    (while (and element (eq (org-element-type element) 'headline))
+      (let ((title (org-element-property :title element)))
+        (when title
+          (push title path)))
+      (setq element (org-element-property :parent element)))
+    (reverse path)))
+
+;;;###autoload
+(defun vde/org-clock-in-any-heading ()
+  "Clock into any Org heading from `org-agenda-files'."
+  (interactive)
+  (let (headings)
+    (dolist (file org-agenda-files)
+      (when (file-exists-p file)
+        (with-current-buffer (find-file-noselect file)
+          (org-map-entries (lambda ()
+                              (let* ((element (org-element-context))
+                                     (path (vde--get-outline-path element)))
+                                (push (list :path path
+                                            :file (buffer-file-name)
+                                            :position (point))
+                                      headings)))
+                           t 'file))))
+    (let* (candidates)
+      (dolist (h headings)
+        (let* ((path (plist-get h :path))
+               (path-str (mapconcat 'identity path " > "))
+               (file (plist-get h :file))
+               (candidate (format "%s : %s" path-str (file-name-nondirectory file)))
+               (data (list file (plist-get h :position))))
+          (push (cons candidate data) candidates)))
+      (let* ((selected-candidate (completing-read "Select heading: " candidates))
+             (matching (cl-find-if (lambda (c) (string= (car c) selected-candidate)) candidates)))
+        (when matching
+          (let* ((data (cdr matching))
+                 (file (car data))
+                 (pos (cadr data)))
+            (find-file file)
+            (goto-char pos)
+            (org-clock-in)))))))
+
 (provide 'org-func)
 ;;; org-func.el ends here