集成org-roam和任务管理

用org-roam管理笔记一段时间了,记录、捕获、文献管理都用得舒服,就是任务管理不方便。我原来的agenda是固定在几个文件中(手动设置org-agenda-files),使用org-roam后会生成大量org,如果全部设置为agenda目录,则生成agenda太慢也太乱。

于是提出如下想法:

  1. 固定的agenda文件仍然在org-agenda-files中
  2. 由一个变量维护动态agenda文件,在任何org文件中,当org entry状态变为todo类时,自动把文件加到该变量,当任务完成后,如果该文件中所有任务都完成,则从变量去除org文件
  3. 变量保存到custom中(不知道更好办法)

经过折腾,如下代码暂时可用:

(defvar dynamic-agenda-files nil
  "dynamic generate agenda files list when changing org state")

(defun update-dynamic-agenda-hook ()
  (let ((done (or (not org-state) ;; nil when no TODO list
                  (member org-state org-done-keywords)))
        (file (buffer-file-name))
        (agenda (funcall (ad-get-orig-definition 'org-agenda-files)) ))
    (unless (member file agenda)
      (if done
          (save-excursion
            (goto-char (point-min))
            ;; Delete file from dynamic files when all TODO entry changed to DONE
            (unless (search-forward-regexp org-not-done-heading-regexp nil t)
              (customize-save-variable
               'dynamic-agenda-files
               (cl-delete-if (lambda (k) (string= k file))
                             dynamic-agenda-files))))
        ;; Add this file to dynamic agenda files
        (unless (member file dynamic-agenda-files)
          (customize-save-variable 'dynamic-agenda-files
                                   (add-to-list 'dynamic-agenda-files file)))))))

(defun dynamic-agenda-files-advice (orig-val)
  (union orig-val dynamic-agenda-files :test #'equal))

(advice-add 'org-agenda-files :filter-return #'dynamic-agenda-files-advice)
(add-to-list 'org-after-todo-state-change-hook 'update-dynamic-agenda-hook t)
21 个赞

很好用。Agenda 集中管理任务的优势可以和 Org-roam 的发散思维优势合并在一起。

把楼主的代码,也搬运到了 Org-roam 的英文社区,让更多的人用到你的函数。

2 个赞

我现在是只用一个task文件, 添加新任务通过org-roam-capture往task.org中来添加.

提示symbol’s function definition is void: union 问下有啥解决方法吗

1 个赞

我也遇到了这个问题,暂时没精力去 debug。 d12frosted 给出了类似的解决办法,可供参考。另外 Org-roam 最近在准备一次大的的升级,可以等新版本出来以后再折腾。

很奇怪的问题, 我使用org-roam-capture追加的内容, 会添加到文档的首行, 而不是末行. 不知道中哪儿有问题, 也不知道怎么解决.

因为union是common lisp 的一个函数,在emacs中通过cl-union来模拟这个函数。

所以把union换成cl-union就可以了

多谢楼主,已经在使用这个方案了。非常棒。 稍微有些小瑕疵,不过不影响使用

多谢回复,可以用了,同时感谢楼主

谢谢补充,union在我这是cl-seq.el文件中,换成cl-union吧

最新遇到了个问题,有时候会出现agenda视图不能用,我的是doom emacs,执行org-agenda,会出现org-check-agenda-file: Wrong type argument: stringp, nil错误。然后我把楼主的配置去掉,重启就又好了

我也是在doom emacs下用,没出现这个问题。看下你的org-agenda-files,还可检查下dynamic-agenda-files的值。我是手动设置org-agenda-files固定几个文件作日常任务。然后动态配合其他笔记作任务管理。

可能是doom的问题吧,我找到了类似的国外友人的一个实现方法,Boris Buliga - Task management with org-roam Vol. 5: Dynamic and fast agenda

我昨天也刚好读到,不过由于我的是静态与动态agenda files相结合,暂时没用上。

我目前的方式是,把 org-agenda-files 变量设置成一个文档,比如 “gtd.org”。然后把 daily 目录添加到这个文档里面。如果在其他笔记里面有任务,就通过 “C-c [” 手工添加到 “gtd.org” 这个文档里。

目前用的是下面这个,以前的性能不是太好。参考过的都在代码的链接里了。

主要想法:

  1. org-agenda-files 列表,里面放固定作为agenda扫描的目录或文件。
  2. 所有在 org-roam 中的文件,当一个headline变为 TODO了,就把这个文件作为agenda file。当完成时,自动排除。省去考虑agenda的心智。
;;* dynamic agenda https://github.com/brianmcgillion/doomd/blob/master/config.org
  ;; https://d12frosted.io/posts/2021-01-16-task-management-with-roam-vol5.html
  ;; The 'roam-agenda' tag is used to tell vulpea that there is a todo item in this file
  (add-to-list 'org-tags-exclude-from-inheritance "roam-agenda")

  (require 'vulpea)

  (defun vulpea-buffer-p ()
    "Return non-nil if the currently visited buffer is a note."
    (and buffer-file-name
         (string-prefix-p
          (expand-file-name (file-name-as-directory org-roam-directory))
          (file-name-directory buffer-file-name))))

  (defun vulpea-project-p ()
    "Return non-nil if current buffer has any todo entry.

TODO entries marked as done are ignored, meaning the this
function returns nil if current buffer contains only completed
tasks."
    (seq-find                                 ; (3)
     (lambda (type)
       (eq type 'todo))
     (org-element-map                         ; (2)
         (org-element-parse-buffer 'headline) ; (1)
         'headline
       (lambda (h)
         (org-element-property :todo-type h)))))

  (defun vulpea-project-update-tag (&optional arg)
    "Update PROJECT tag in the current buffer."
    (interactive "P")
    (when (and (not (active-minibuffer-window))
               (vulpea-buffer-p))
      (save-excursion
        (goto-char (point-min))
        (let* ((tags (vulpea-buffer-tags-get))
               (original-tags tags))
          (if (vulpea-project-p)
              (setq tags (cons "roam-agenda" tags))
            (setq tags (remove "roam-agenda" tags)))

          ;; cleanup duplicates
          (setq tags (seq-uniq tags))

          ;; update tags if changed
          (when (or (seq-difference tags original-tags)
                    (seq-difference original-tags tags))
            (apply #'vulpea-buffer-tags-set tags))))))

  ;; https://systemcrafters.net/build-a-second-brain-in-emacs/5-org-roam-hacks/
  (defun my/org-roam-filter-by-tag (tag-name)
    (lambda (node)
      (member tag-name (org-roam-node-tags node))))

  (defun my/org-roam-list-notes-by-tag (tag-name)
    (mapcar #'org-roam-node-file
            (seq-filter
             (my/org-roam-filter-by-tag tag-name)
             (org-roam-node-list))))

  (defun dynamic-agenda-files-advice (orig-val)
    (let ((roam-agenda-files (delete-dups (my/org-roam-list-notes-by-tag "roam-agenda"))))
      (cl-union orig-val roam-agenda-files :test #'equal)))

  (add-hook 'before-save-hook #'vulpea-project-update-tag)
  (advice-add 'org-agenda-files :filter-return #'dynamic-agenda-files-advice)

3 个赞

有类似需求,试试看 :grinning:

挺好的, 正好需要这个.

我现在还只能到处抄代码的小白阶段, 用的是 doom emacs,

把上面这段复制到 config.el 里, 第一行就报错. 是不是我还应该安装其它的什么东西?

我也是好久之前用过下doom,快忘光了。你试下把它放在after!宏里面:

(after! org-roam
;;放在这里
)

还有要安装个 vulpea

安装了 vulpea 包, 也把代码用 after! 括起来了, 测试了一下, emacs 没有报错, 也可以找得到 roam 里面刚刚添加的 todo 了. 多谢多谢…

不过, 发现一个小问题: 之前我有个目录~/roam/, 保存的是 roam v1 的 org 文档. 后来升级了 roam v2, 现在指定的 org 目录是 ~/roam2/, 在运行 org-agenda 命令的时候, 它把 v1 里面的 todo 也显示出来了. 而且, 第一次运行的时候, 非常慢.