emacs-org-mode中同步滴答清单内容
1. 基础配置
请参考github的README
2. 进阶配置
2.1. 直接capture,不放入指定文件
:直接调用 org-capture 并将任务输入到指定位置的函数。
2.1.1. 自定义dida相关函数
(use-package dida :demand t
:vc (:url "git@github.com:TomoeMami/dida"
:rev :last-release)
:config
(setq dida-client-id "YOUR-CLIENT-ID")
(setq dida-client-secret "YOUR-CLIENT-SECRET")
(defun dida-simple-task (task)
(let* ((id (alist-get 'id task))
(pid (alist-get 'projectId task))
(isallday (alist-get 'isAllDay task))
(title (alist-get 'title task ))
(content (alist-get 'content task ))
(status (alist-get 'status task))
(due-date (alist-get 'dueDate task))
(priority (alist-get 'priority task))
(repeatflag (alist-get 'repeatFlag task))
(deadline-or-scheduled "SCHEDULED: ")
(h-m (if (eq isallday ':json-false) " %H:%M" "")))
(concat (if (= status 2) "DONE " "TODO ") title
(when due-date
(concat "\n" deadline-or-scheduled
(format-time-string
(concat "<%Y-%m-%d %a" h-m ">")
(date-to-time due-date))))
;; 优化:如果 content 存在且不为空,前面加一个换行,保证 org 格式美观
(if (and content (not (string-empty-p content)))
(concat "\n" content)
""))))
(defun dida-cut ()
"从dida获取所有任务并删除云端任务."
(interactive)
(unless (dida--check-token)
(dida-authorize))
(let ((projects (dida-get-user-project))
(result))
(dolist (project projects)
(let* ((project-id (alist-get 'id project))
(project-name (alist-get 'name project))
(project-data (dida-get-project-by-id-with-data project-id))
(tasks (append (alist-get 'tasks project-data) nil)))
;; Insert all tasks under this project
(dolist (task tasks)
(setf result (concat result "* " (dida-simple-task task)))
(dida-complete-task project-id (alist-get 'id task)))))
result))
2.1.2. 配置doct模板
注意,这里我们使用了 (keyboard-quit) 函数,这个函数会打断当前进行的系列函数,所以不要把这个 (org-capture nil "d") 放进任何hook里。幸运的是我们一般也不需要把他们放进hook中,只需要定时执行拉取就好了。
(setq org-capture-templates
(doct '(("Dida Sync" :keys "d"
;这里改成你平常用的agenda files之一路径
:file my-org-timeline
;; 使用你常用的定位函数,可忽略
:function org-reverse-datetree-goto-date-in-file
;; 动态调用选择函数获取内容
:template (lambda () (let ((result (dida-cut)))
(if (and result (stringp result))
result
;;没有内容就默默退出吧孩子
(keyboard-quit))))
;; 捕获后立即完成,不需要手动按 C-c C-c (可选)
:immediate-finish t)
)))
2.1.3. 定期执行
;;每60秒执行一次
(run-with-idle-timer 60 t #'(lambda () (org-capture nil "d")))
;;启动后稍等10秒执行一次
(run-at-time 10 nil #'(lambda () (org-capture nil "d")))
2.2. 直接capture且不调用keyboard-quit函数
:鉴于我的使用场景中,只会将滴答清单的任务放到指定文件的指定层级,因此修改上述函数,硬编码了层级,这样可以在为空的时候不插入任何内容。
修改部分如下:
(defun dida-cut ()
"从dida获取所有任务并删除云端任务."
(interactive)
(unless (dida--check-token)
(dida-authorize))
(let ((projects (dida-get-user-project))
(result-list nil)) ;; 改用列表来收集所有的 task 文本
(dolist (project projects)
(let* ((project-id (alist-get 'id project))
(project-name (alist-get 'name project))
(project-data (dida-get-project-by-id-with-data project-id))
(tasks (append (alist-get 'tasks project-data) nil)))
;; 遍历每个任务
(dolist (task tasks)
;; 给生成的文本开头加上 "**** " 并收集到 result-list 中
(push (concat "**** " (dida-simple-task task)) result-list)
;; 完成云端任务
(dida-complete-task project-id (alist-get 'id task)))))
;; 用 "\n" 连接每个任务块
(mapconcat #'identity result-list "\n")))
(setq org-capture-templates
(doct '(("Dida Fetch" :keys "d"
:file my-org-timeline
;; 使用你指定的定位函数
:function org-reverse-datetree-goto-date-in-file
;; 动态调用选择函数获取内容
:template (lambda () (or (dida-cut) ""))
:type plain
;; 捕获后立即完成,不需要手动按 C-c C-c (可选)
:immediate-finish t)
)))
2.3. 快捷capture
将 dida.org 中的事项refile到其他文件并把滴答清单该事项删除的函数
(defun akirak/org-refile-to-cpb-today (arg)
"根据不同情况调整refile的目标"
(interactive "P")
(when (org-entry-is-todo-p)
;;dida的要先发送delete
(when (string-match "dida.org" (buffer-file-name))
(dida-delete-task (org-entry-get nil "DIDA_PID" t) (org-entry-get nil "DIDA_TID")))))
3. 使用流程
- 不在电脑旁边时,用滴答清单快速录入待办事项到指定清单中
- 启动Emacs,同步dida清单到本地
- 切换到
dida.org,调用上述函数将事项refile到其他org文件中,同时删除滴答清单中的内容;或者将里面的TODO标为DONE,则会自动完成滴答清单中对应事项
4. 一些建议
因为这个库不会保存org标题的meta内容,所以不建议主力使用,仅作为事项收集的inbox用即可。