From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp11.migadu.com ([2001:41d0:2:bcc0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms5.migadu.com with LMTPS id yL05BE0LJWMp8QAAbAwnHQ (envelope-from ) for ; Sat, 17 Sep 2022 01:48:29 +0200 Received: from aspmx1.migadu.com ([2001:41d0:2:bcc0::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp11.migadu.com with LMTPS id YIc3BE0LJWONogAA9RJhRA (envelope-from ) for ; Sat, 17 Sep 2022 01:48:29 +0200 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 6C4E547051 for ; Sat, 17 Sep 2022 01:48:28 +0200 (CEST) Received: from localhost ([::1]:37432 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1oZL4N-00070C-L7 for larch@yhetil.org; Fri, 16 Sep 2022 19:48:27 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:42432) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oZL3s-0006zy-5b for emacs-orgmode@gnu.org; Fri, 16 Sep 2022 19:47:56 -0400 Received: from mail-qk1-x72f.google.com ([2607:f8b0:4864:20::72f]:37413) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1oZL3q-0001Gc-9j for emacs-orgmode@gnu.org; Fri, 16 Sep 2022 19:47:55 -0400 Received: by mail-qk1-x72f.google.com with SMTP id s9so14657983qkg.4 for ; Fri, 16 Sep 2022 16:47:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=mime-version:user-agent:message-id:date:subject:to:from:from:to:cc :subject:date; bh=uow3c2QnOSVH3bm56B1JAjdtHqQ6hHmph5qwLrP90as=; b=fUH6caL9YXVzwR9M65MVrsFtv4wwidyLpFkVGhWH8VB3rMSiywJJQcjYUrNjY+sHeb i9UQvcwu/G+eKJXJGzJ/vDbKr/IYZ++YJ7w1xqfz1A0g7kOtJP29nQ2umudJBjcnkSvY 9SvBML7CPwQUPq7HZMSdB07oFjkOw3EAtK1U4Ur7b5S0MSE15nNdBnLEZqhU1LXh5BgE OpFHI8BEFOzDYRJYADelmY4RJpgCjz0vB4+TSRcPdAP/hJmFnxLGQym4uFTvD2z3osV+ P8l3KYm4dc6keak4AM4tZqurBGU6sa+OwJuGszZLTgYdlAbAVxkzaAqeFX9ivx9Njnjn fjRg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=mime-version:user-agent:message-id:date:subject:to:from :x-gm-message-state:from:to:cc:subject:date; bh=uow3c2QnOSVH3bm56B1JAjdtHqQ6hHmph5qwLrP90as=; b=oUGTw1zFbSmrVRUd8Ber5xxCKdtncIZBdd/aYZLrUba8aE0nUdkJQw3xPafL40cFqR PNV12adLVoqoWH4KyHYD6Er1Twk0JGDnJ+gR/9QFyFgdiF+D8b/YpPkcFQUlli+24YVl BfAkTSudK/LqgAsGKakFqkMLHsM+5yFgmWfwfgx53dnLhR1ItxqJrc6EaKjTx9HFEdX2 MYjMyztmfIHZ3LI8lLREgMN0KGE1lYTk4Ylidt8An/5fIh8PnBWSR7tC23dbitBLDTZ5 UM/xuKbn3O/mvuPcnhEuqvfNrpuzfNYXaUdpDopAmegpa+FgwE0+8b8H7aWOTm5//WPr 986Q== X-Gm-Message-State: ACrzQf3t6hhFlR4EjzGzB3NufJGTr4wpETyHpKdi4c80SR6xG20WOxeW ph9D9P4D9OOO3N4by3i0ZQsvd9F2MFtugw== X-Google-Smtp-Source: AMsMyM53ALtwpLEm7fkic3B/bm+uSFTznHGXVO4RCHZgCbVGtbPobLz8xm3rZxyhWJG0SD3bZJCbtg== X-Received: by 2002:a05:620a:4251:b0:6be:7b31:2275 with SMTP id w17-20020a05620a425100b006be7b312275mr5863228qko.604.1663372072334; Fri, 16 Sep 2022 16:47:52 -0700 (PDT) Received: from tyler-fedora ([2600:6c48:69f0:5f0::14f4]) by smtp.gmail.com with ESMTPSA id q4-20020a37f704000000b006cbc6e1478csm7148890qkj.57.2022.09.16.16.47.50 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Sep 2022 16:47:51 -0700 (PDT) From: Tyler Grinn To: emacs-orgmode@gnu.org Subject: contrib - ol-todo Date: Fri, 16 Sep 2022 19:47:48 -0400 Message-ID: <87edwax4nv.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/29.0.50 (gnu/linux) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=2607:f8b0:4864:20::72f; envelope-from=tylergrinn@gmail.com; helo=mail-qk1-x72f.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: "Emacs-orgmode" X-Migadu-Flow: FLOW_IN X-Migadu-To: larch@yhetil.org X-Migadu-Country: US ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1663372108; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=uow3c2QnOSVH3bm56B1JAjdtHqQ6hHmph5qwLrP90as=; b=qDe6gbQ2uFSoJWl1HMSA/RguWPrbC4FmfffROdHkcak2olTQr4RBb4qJZlfWmnvbUzKbTj auluh6pdIqrowzJj8FOy4z7dfIyQ1bV+hDLqSOD7xYgan5x3PBxzx0K66LWYW4W1xmfEz3 v1Lal1q65I2RQW1hFeY/ILg1QiRgAFksNNxHeQIS39UEHANwoB/BSsSHU6q/ux2JdFnkrF nzF2RLeLO0Kog0IOFy74rnXjs2y9yHnbdjg7iODTmG0Uwa5Fekt+fagfafauq2KhcFzXHl BnvJzHfOq/7ReOM1JPtVSuGY3jTF823FgHNxAA5Z47V28kuVEYel2vDIIHCtdA== ARC-Seal: i=1; s=key1; d=yhetil.org; t=1663372108; a=rsa-sha256; cv=none; b=S4jiuvb8xJZiGTFf3GRdzp3uF1jIr3vHq6aF3NYy66vA5PzJ485j7Hk7V7GmkftbcfFsPK Tf5UG1PGqRE97b/4/mGs/PZbr2FC/B0sUOygB3nagW73k4hARsY3mpB+sdENMT7jnbeZWL PuMhp4yaRbxkuWuLEzcbJZO4KeEU36gTFlRHNrLUurMnyuVl5Yd9nv/T15fDJk8fGGX6xx bDoPikQ7HPMXlEhHB3StaoIKmQoYAl+hZWDQHUFSTjiEAuhSZDvC8zAbtGnH0OBdpQlRsV 2D62T4hLJQebHfOggdGb/gBaIrN6gVh+4O13QfdJqNfhRXgxxEYxGhs9W+QnJg== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20210112 header.b=fUH6caL9; dmarc=pass (policy=none) header.from=gmail.com; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org" X-Migadu-Spam-Score: -3.83 Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20210112 header.b=fUH6caL9; dmarc=pass (policy=none) header.from=gmail.com; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org" X-Migadu-Queue-Id: 6C4E547051 X-Spam-Score: -3.83 X-Migadu-Scanner: scn1.migadu.com X-TUID: nx3WjRF0V333 --=-=-= Content-Type: text/plain I've built this small package which registers a todo type link: [[todo:~/projects.org::#my-todo][My todo]] And it is displayed like this: My todo Where DONE is the actual todo keyword on the target heading. The keyword on the link and target stay in sync, so setting a different todo keyword either on the link or the target will update both. The link does not show up in the agenda and can be placed anywhere an org link is valid. I'm using this to create a list of todos I want done today from a larger list of all my todos. Is this something that would be appropriate for org-contrib? --- When I tried to register a :store function which is valid for org buffers backed by a file, the desired behavior was that I could choose between storing a file link and a todo link, but instead, it simply stores a todo link without confirmation. Is this a known problem? --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=ol-todo.el Content-Description: ol-todo.el ;;; ol-todo.el --- Store symbolic link to a TODO entry -*- lexical-binding: t -*- ;; Copyright (C) 2014-2022 Free Software Foundation, Inc. ;; Author: Tyler Grinn ;; Package-Requires: ((emacs "27.2")) ;; Version: 0.0.1 ;;; Commentary: ;; When this type of link is inserted, the todo keyword of the target ;; heading is displayed before the link. With point inside a todo ;; link, use C-c C-c to update the link and C-c C-t to change the todo ;; status of the target heading. ;; ;; Use `org-todo-link-store' to store the heading at point in ;; `org-stored-links'. This is not registered as a provider for ;; `org-store-link' because it would override the default storing ;; behavior for org files. ;;; Code: ;;;; Requirements: (require 'org) (require 'ol) (require 'org-keys) (require 'org-refile) (require 'org-element) ;;;; Org todo link keymap (defun org-todo-link-recalculate () "Recalculate TODO status for todo link at point." (interactive) (if-let* ((ov (seq-find (lambda (o) (overlay-get o 'ol-todo)) (overlays-at (point)))) (start (car (org-in-regexp org-link-any-re))) (link (save-excursion (goto-char start) (org-element-link-parser))) (path (org-element-property :path link))) (overlay-put ov 'before-string (org-todo-link-get-todo path)))) (defun org-todo-link-todo () "Set TODO keyword on todo link at point." (interactive) (when-let* ((ov (seq-find (lambda (o) (overlay-get o 'ol-todo)) (overlays-at (point)))) (start (car (org-in-regexp org-link-any-re))) (link (save-excursion (goto-char start) (org-element-link-parser))) (path (org-element-property :path link))) (save-window-excursion (let* ((org-link-frame-setup '((file . find-file))) (pos (org-todo-link-find path))) (save-excursion (goto-char pos) (org-todo)))) (overlay-put ov 'before-string (org-todo-link-get-todo path)))) (defvar org-todo-link-keymap (let ((map (make-sparse-keymap))) (set-keymap-parent map org-mouse-map) (mapc (lambda (k) (define-key map (kbd (car k)) (cdr k))) '(("C-c C-c" . org-todo-link-recalculate) ("C-c C-t" . org-todo-link-todo))) map) "Keymap for todo links.") ;; Create `todo' style link ;;;###autoload (org-link-set-parameters "todo" :complete #'org-todo-link-complete :insert-description #'org-todo-link-description :activate-func #'org-todo-link-activate :face #'org-todo-link-face :follow #'org-todo-link-follow :keymap org-todo-link-keymap) (defun org-todo-link-complete (&optional _) "Prompt user to complete path to TODO item in refile targets." (let ((it (org-refile-get-location "TODO Item: ")) (org-link-frame-setup '((file . find-file))) org-stored-links) (save-window-excursion (org-open-file (nth 1 it)) (save-excursion (goto-char (nth 3 it)) (org-todo-link-store))) (caar org-stored-links))) (defun org-todo-link-description (loc _) "Generate probable description from todo link LOC." (save-window-excursion (let* ((org-link-frame-setup '((file . find-file))) (pos (org-todo-link-find (replace-regexp-in-string "^todo:" "" loc)))) (org-entry-get pos "ITEM")))) (defun org-todo-link-activate (start end path &rest _) "Create overlay from START to END and display todo of heading at PATH." (let ((overlays (seq-filter (lambda (o) (overlay-get o 'ol-todo)) (overlays-in start end)))) (if (not overlays) (let ((ov (make-overlay start end))) (overlay-put ov 'ol-todo t) (overlay-put ov 'evaporate t) (overlay-put ov 'before-string (org-todo-link-get-todo path))) (move-overlay (car overlays) start end) (overlay-put (car overlays) 'before-string (org-todo-link-get-todo path)) (mapc #'delete-overlay (cdr overlays))))) (defun org-todo-link-face (path) "Calculate TODO status for link at point with PATH." (if-let ((ov (seq-find (lambda (o) (overlay-get o 'ol-todo)) (overlays-at (1- (point)))))) (overlay-put ov 'before-string (org-todo-link-get-todo path))) 'org-link) (defun org-todo-link-follow (path _) "Open a todo link to PATH." (goto-char (org-todo-link-find path))) ;;;; Commands ;;;###autoload (defun org-todo-link-store () "Store a todo link to the current heading." (interactive) (when (and (buffer-file-name (buffer-base-buffer)) (derived-mode-p 'org-mode)) (let (link) (let (org-stored-links) (org-store-link '(16) t) (setq link (car org-stored-links))) (when link (setcar link (replace-regexp-in-string "^file:" "todo:" (car link))) (push link org-stored-links))))) ;;;; Utility Expressions (defun org-todo-link-find (path) "Jump to file part of PATH and return the heading position." (let ((link (with-temp-buffer (insert "[[file:" path "]]") (goto-char (point-min)) (org-element-link-parser))) (org-link-search-must-match-exact-headline t)) (org-open-file (org-element-property :path link)) (org-with-wide-buffer (org-link-search (org-element-property :search-option link)) (point)))) (defun org-todo-link-get-todo (path) "Get TODO keyword at PATH." (save-window-excursion (let* (broken (org-link-frame-setup '((file . find-file))) (todo (condition-case err (org-entry-get (org-todo-link-find path) "TODO") (error (message (error-message-string err)) (setq broken t))))) (concat "<" (cond (broken "BROKEN") (todo (propertize todo 'face (if (member todo org-done-keywords) 'org-done 'org-todo))) (t "NONE")) "> ")))) (provide 'ol-todo) ;;; ol-todo.el ends here --=-=-=--