- 1 课程回顾
- 2 概述
- 3 calendar相关配置
- 3.1 calendar基本设置
- 3.2 中文日历增强
- 4 org-agenda的配置和使用
- 4.1 org-agenda 基本配置
- 4.2 org-agenda 的使用
- 5 使用Org mode进行周期性的习惯管理
- 6 使用Org mode写日记
- 7 任务管理通知
- 8 与移动端同步
- 9 结语
1 课程回顾 🔗
通过上节课,我们学习了在Org mode里写文档的一些小技巧,比如图片粘贴、给org文件本身插入目录、插入各种类型的链接等,小技巧千千万,远不止我写的这些,更多的技巧需要持续的学习、多读多看多写多用,在使用中发现缺失,在搜索中发现解决方案,在阅读中思考和总结,然后你会猛然发现,原来,不知不觉已经会的这么多了。
今天,我们来学习如何通过Org mode来进行任务管理。
2 概述 🔗
Org mode天然适合进行任务管理,因为所有的标题行都可以通过 C-c C-t
(org-todo) 来设置 TODO 状态,状态的种类由 org-todo-keywords
这个变量定义,默认值为 TODO
和 DONE
。我们在 第8课:Org mode的基本配置和美化 中,设置了这个变量:
(setq org-todo-keywords '((sequence "TODO(t)" "HOLD(h!)" "WIP(i!)" "WAIT(w!)" "|" "DONE(d!)" "CANCELLED(c@/!)")
(sequence "REPORT(r)" "BUG(b)" "KNOWNCAUSE(k)" "|" "FIXED(f!)")))
你可以根据自己的需要来重新设置这个变量,从我个人的使用情况来说,定义下面几个状态是比较合适的:
- TODO:即将要做的事
- WIP:正在做的事
- WAIT:需要等待的事
- DONE:做完的事
- CANCELLED:不做了的事
我们只需要在标题行的标题文字前面写上状态关键字+空格即可:
** TODO 需要做的事
** WIP 正在做的事
** WAIT 需要等待的事
** DONE 做完的事
** CANCELLED 不做了的事
然后针对某个标题行,随着我们工作的进展,我们可以通过 C-c C-t
来改变它的状态,比如从 TODO
到 WIP
,再从 WIP
到 DONE
。
另外,Org mode还支持在一个标题行下,通过任务列表来跟踪子任务的完成情况,任务列表很简单,只需要以 - [ ]
开头即可,当完成某个任务列表的任务,只需要移动光标到这一行,然后按下 C-c C-c
,这个任务就会自动的变成 - [X]
代表已经完成。如果你安装了 org-modern
插件,那就会是从复选框未选中状态变成选中状态。在标题行的标题文字后添加一个空格 + [/]
后,这个标题行会自动跟踪子任务的完成情况,并以 [已完成/任务总数]
的格式显示:
** TODO 某个任务 [1/3]
- [ ] 子任务1
- [ ] 子任务2
- [X] 子任务3
标题行和任务列表,是我们通过Org mode进行任务管理的基础。
3 calendar相关配置 🔗
在具体讲述如何使用Org mode来进行日程管理之前,我们先来聊聊 Emacs 自带的 calendar,设置好它,是我们配置 org-agenda 的前提和基础。
3.1 calendar基本设置 🔗
首先,我们对Emacs自带的calendar进行一些配置。Emacs自带 calendar
功能,我们只需要按下 M-x calendar
即可启动:
我们可以稍微对calendar做一些改造,让它更符合中国的习惯:
(use-package calendar
:ensure nil
:hook (calendar-today-visible . calendar-mark-today)
:custom
;; 是否显示中国节日,我们使用 `cal-chinese-x' 插件
(calendar-chinese-all-holidays-flag nil)
;; 是否显示节日
(calendar-mark-holidays-flag t)
;; 是否显示Emacs的日记,我们使用org的日记
(calendar-mark-diary-entries-flag nil)
;; 数字方式显示时区,如 +0800,默认是字符方式如 CST
(calendar-time-zone-style 'numeric)
;; 日期显示方式:year/month/day
(calendar-date-style 'iso)
;; 中文天干地支设置
(calendar-chinese-celestial-stem ["甲" "乙" "丙" "丁" "戊" "己" "庚" "辛" "壬" "癸"])
(calendar-chinese-terrestrial-branch ["子" "丑" "寅" "卯" "辰" "巳" "午" "未" "申" "酉" "戌" "亥"])
;; 设置中文月份
(calendar-month-name-array ["一月" "二月" "三月" "四月" "五月" "六月" "七月" "八月" "九月" "十月" "十一月" "十二月"])
;; 设置星期标题显示
(calendar-day-name-array ["日" "一" "二" "三" "四" "五" "六"])
;; 周一作为一周第一天
(calendar-week-start-day 1)
)
当我们配置完后再次打开 calendar 界面,我们可以看到主要信息显示的都是中文了,而且所有的节日都高亮:
3.2 中文日历增强 🔗
下面,我们通过 cal-china-x 插件进一步地增强中文日历,显示农历等信息:
;; 时间解析增加中文拼音
(use-package parse-time
:ensure nil
:defer t
:config
(setq parse-time-months
(append '(("yiy" . 1) ("ery" . 2) ("sany" . 3)
("siy" . 4) ("wuy" . 5) ("liuy" . 6)
("qiy" . 7) ("bay" . 8) ("jiuy" . 9)
("shiy" . 10) ("shiyiy" . 11) ("shiery" . 12)
("yiyue" . 1) ("eryue" . 2) ("sanyue" . 3)
("siyue" . 4) ("wuyue" . 5) ("liuyue" . 6)
("qiyue" . 7) ("bayue" . 8) ("jiuyue" . 9)
("shiyue" . 10) ("shiyiyue" . 11) ("shieryue" . 12))
parse-time-months))
(setq parse-time-weekdays
(append '(("zri" . 0) ("zqi" . 0)
("zyi" . 1) ("zer" . 2) ("zsan" . 3)
("zsi" . 4) ("zwu" . 5) ("zliu" . 6)
("zr" . 0) ("zq" . 0)
("zy" . 1) ("ze" . 2) ("zs" . 3)
("zsi" . 4) ("zw" . 5) ("zl" . 6))
parse-time-weekdays)))
;; 中国节日设置
(use-package cal-china-x
:ensure t
:commands cal-china-x-setup
:hook (after-init . cal-china-x-setup)
:config
;; 重要节日设置
(setq cal-china-x-important-holidays cal-china-x-chinese-holidays)
;; 所有节日设置
(setq cal-china-x-general-holidays
'(;;公历节日
(holiday-fixed 1 1 "元旦")
(holiday-fixed 2 14 "情人节")
(holiday-fixed 3 8 "妇女节")
(holiday-fixed 3 14 "白色情人节")
(holiday-fixed 4 1 "愚人节")
(holiday-fixed 5 1 "劳动节")
(holiday-fixed 5 4 "青年节")
(holiday-float 5 0 2 "母亲节")
(holiday-fixed 6 1 "儿童节")
(holiday-float 6 0 3 "父亲节")
(holiday-fixed 9 10 "教师节")
(holiday-fixed 10 1 "国庆节")
(holiday-fixed 10 2 "国庆节")
(holiday-fixed 10 3 "国庆节")
(holiday-fixed 10 24 "程序员节")
(holiday-fixed 11 11 "双11购物节")
(holiday-fixed 12 25 "圣诞节")
;; 农历节日
(holiday-lunar 12 30 "春节" 0)
(holiday-lunar 1 1 "春节" 0)
(holiday-lunar 1 2 "春节" 0)
(holiday-lunar 1 15 "元宵节" 0)
(holiday-solar-term "清明" "清明节")
(holiday-solar-term "小寒" "小寒")
(holiday-solar-term "大寒" "大寒")
(holiday-solar-term "立春" "立春")
(holiday-solar-term "雨水" "雨水")
(holiday-solar-term "惊蛰" "惊蛰")
(holiday-solar-term "春分" "春分")
(holiday-solar-term "谷雨" "谷雨")
(holiday-solar-term "立夏" "立夏")
(holiday-solar-term "小满" "小满")
(holiday-solar-term "芒种" "芒种")
(holiday-solar-term "夏至" "夏至")
(holiday-solar-term "小暑" "小暑")
(holiday-solar-term "大暑" "大暑")
(holiday-solar-term "立秋" "立秋")
(holiday-solar-term "处暑" "处暑")
(holiday-solar-term "白露" "白露")
(holiday-solar-term "秋分" "秋分")
(holiday-solar-term "寒露" "寒露")
(holiday-solar-term "霜降" "霜降")
(holiday-solar-term "立冬" "立冬")
(holiday-solar-term "小雪" "小雪")
(holiday-solar-term "大雪" "大雪")
(holiday-solar-term "冬至" "冬至")
(holiday-lunar 5 5 "端午节" 0)
(holiday-lunar 8 15 "中秋节" 0)
(holiday-lunar 7 7 "七夕情人节" 0)
(holiday-lunar 12 8 "腊八节" 0)
(holiday-lunar 9 9 "重阳节" 0)))
;; 设置日历的节日,通用节日已经包含了所有节日
(setq calendar-holidays (append cal-china-x-general-holidays)))
设置完后,我们再次打开 calendar,可以看到显示了农历、生肖、星座等信息:
4 org-agenda的配置和使用 🔗
4.1 org-agenda 基本配置 🔗
设置好 calendar 后,我们对 org-agenda
进行配置:
(use-package org-agenda
:ensure nil
:hook (org-agenda-finalize . org-agenda-to-appt)
:bind (("\e\e a" . org-agenda)
:map org-agenda-mode-map
("i" . (lambda () (interactive) (org-capture nil "d")))
("J" . consult-org-agenda))
:config
;; 日程模式的日期格式设置
(setq org-agenda-format-date 'org-agenda-format-date-aligned)
(defun org-agenda-format-date-aligned (date)
"Format a DATE string for display in the daily/weekly agenda, or timeline.
This function makes sure that dates are aligned for easy reading."
(require 'cal-iso)
(let* ((dayname (aref cal-china-x-days
(calendar-day-of-week date)))
(day (cadr date))
(month (car date))
(year (nth 2 date))
(day-of-week (calendar-day-of-week date))
(iso-week (org-days-to-iso-week
(calendar-absolute-from-gregorian date)))
(cn-date (calendar-chinese-from-absolute (calendar-absolute-from-gregorian date)))
(cn-month (cl-caddr cn-date))
(cn-day (cl-cadddr cn-date))
(cn-month-string (concat (aref cal-china-x-month-name
(1- (floor cn-month)))
(if (integerp cn-month)
""
"(闰月)")))
(cn-day-string (aref cal-china-x-day-name
(1- cn-day)))
(extra (format " 农历%s%s%s%s"
(if (or (eq org-agenda-current-span 'day)
(= day-of-week 1)
(= cn-day 1))
cn-month-string
"")
(if (or (= day-of-week 1)
(= cn-day 1))
(if (integerp cn-month) "" "[闰]")
"")
cn-day-string
(if (or (= day-of-week 1)
(eq org-agenda-current-span 'day))
(format " 今年第%02d周" iso-week)
"")
))
)
(format "%04d-%02d-%02d 星期%s%s%s\n" year month
day dayname extra (concat " 第" (format-time-string "%j") "天"))))
;; 显示时间线
(setq org-agenda-use-time-grid t)
;; 设置面包屑分隔符
;; (setq org-agenda-breadcrumbs-separator " ❱ ")
;; 设置时间线的当前时间指示串
(setq org-agenda-current-time-string "⏰------------now")
;; 时间线范围和颗粒度设置
(setq org-agenda-time-grid (quote ((daily today)
(0600 0800 1000 1200
1400 1600 1800
2000 2200 2400)
"......" "----------------")))
;; 日程视图的前缀设置
(setq org-agenda-prefix-format '((agenda . " %i %-25:c %5t %s")
(todo . " %i %-25:c ")
(tags . " %i %-25:c ")
(search . " %i %-25:c ")))
;; 对于计划中的任务在视图里的显示
(setq org-agenda-scheduled-leaders
'("计划 " "应在%02d天前开始 "))
;; 对于截止日期的任务在视图里的显示
(setq org-agenda-deadline-leaders
'("截止 " "还有%02d天到期 " "已经过期%02d天 "))
;; =====================
;; 自定义日程视图,分别显示TODO,WIP,WIAT中的任务
;; n键显示自定义视图,p键纯文本视图,a键默认视图
;; =====================
(defvar my-org-custom-daily-agenda
`((todo "TODO"
((org-agenda-block-separator nil)
(org-agenda-overriding-header "所有待办任务\n")))
(todo "WIP"
((org-agenda-block-separator nil)
(org-agenda-overriding-header "\n进行中的任务\n")))
(todo "WAIT"
((org-agenda-block-separator nil)
(org-agenda-overriding-header "\n等待中的任务\n")))
(agenda "" ((org-agenda-block-separator nil)
(org-agenda-overriding-header "\n今日日程\n"))))
"Custom agenda for use in `org-agenda-custom-commands'.")
(setq org-agenda-custom-commands
`(("n" "Daily agenda and top priority tasks"
,my-org-custom-daily-agenda)
("p" "Plain text daily agenda and top priorities"
,my-org-custom-daily-agenda
((org-agenda-with-colors nil)
(org-agenda-prefix-format "%t %s")
(org-agenda-current-time-string ,(car (last org-agenda-time-grid)))
(org-agenda-fontify-priorities nil)
(org-agenda-remove-tags t))
("agenda.txt"))))
;; 时间戳格式设置,会影响到 `svg-tag' 等基于正则的设置
;; 这里设置完后是 <2022-12-24 星期六> 或 <2022-12-24 星期六 06:53>
(setq system-time-locale "zh_CN.UTF-8")
(setq org-time-stamp-formats '("<%Y-%m-%d %A>" . "<%Y-%m-%d %A %H:%M>"))
;; 不同日程类别间的间隔
(setq org-cycle-separator-lines 2)
:custom
;; 设置需要被日程监控的org文件
(org-agenda-files
(list (expand-file-name "tasks.org" org-directory)
(expand-file-name "diary.org" org-directory)
(expand-file-name "habits.org" org-directory)
(expand-file-name "mail.org" org-directory)
(expand-file-name "emacs-config.org" user-emacs-directory)
))
;; 设置org的日记文件
(org-agenda-diary-file (expand-file-name "diary.org" org-directory))
;; 日记插入精确时间戳
(org-agenda-insert-diary-extract-time t)
;; 设置日程视图更加紧凑
;; (org-agenda-compact-blocks t)
;; 日程视图的块分隔符
(org-agenda-block-separator ?─)
;; 日视图还是周视图,通过 v-d, v-w, v-m, v-y 切换视图,默认周视图
(org-agenda-span 'day)
;; q退出时删除agenda缓冲区
(org-agenda-sticky t)
;; 是否包含直接日期
(org-agenda-include-deadlines t)
;; 禁止日程启动画面
(org-agenda-inhibit-startup t)
;; 显示每一天,不管有没有条目
(org-agenda-show-all-dates t)
;; 时间不足位时前面加0
(org-agenda-time-leading-zero t)
;; 日程同时启动log mode
(org-agenda-start-with-log-mode t)
;; 日程同时启动任务时间记录报告模式
(org-agenda-start-with-clockreport-mode t)
;; 截止的任务完成后不显示
;; (org-agenda-skip-deadline-if-done t)
;; 当计划的任务完成后不显示
(org-agenda-skip-scheduled-if-done t)
;; 计划过期上限
(org-scheduled-past-days 365)
;; 计划截止上限
(org-deadline-past-days 365)
;; 计划中的任务不提醒截止时间
(org-agenda-skip-deadline-prewarning-if-scheduled 1)
(org-agenda-skip-scheduled-if-deadline-is-shown t)
(org-agenda-skip-timestamp-if-deadline-is-shown t)
;; 设置工时记录报告格式
(org-agenda-clockreport-parameter-plist
'(:link t :maxlevel 5 :fileskip0 t :compact nil :narrow 80))
(org-agenda-columns-add-appointments-to-effort-sum t)
(org-agenda-restore-windows-after-quit t)
(org-agenda-window-setup 'current-window)
;; 标签显示的位置,第100列往前右对齐
(org-agenda-tags-column -100)
;; 从星期一开始作为一周第一天
(org-agenda-start-on-weekday 1)
;; 是否使用am/pm
;; (org-agenda-timegrid-use-ampm nil)
;; 搜索是不看时间
(org-agenda-search-headline-for-time nil)
;; 提前3天截止日期到期告警
(org-deadline-warning-days 3)
)
配置完后,我们按下 ESC-ESC a
键(即按两下 ESC 键,再按 a 键,你也可以设置成自己喜欢的快捷键)后,就可以显示 Org-agenda
视图,展示出当天的日程,你还可以按下 j
键跳转到任意的一天查看当天的日程。在这个视图的最顶层,我们也能清晰的看到当前日期的相关信息。
值得一提的是,我们通过 org-agenda-files
这个变量来追踪需要进行任务管理的org文件,所有在这个变量的列表里的文件,只要有标题行写了 TODO
等状态,就会被 org-agenda
视图自动追踪并显示。
4.2 org-agenda 的使用 🔗
org-agenda 的使用非常简单,只需要在 org-agenda-files
这个变量所追踪的org文件里,添加某个 TODO
(或其他状态)的标题行即可。我们可以通过 第12课:通过Emacs来记笔记 - org-capture快速记录 这个章节所描述的 org-capture
特性来综合使用。
这里我们以 ~/org/tasks.org
文件为例,我们按 ESC-ESC c
来触发 org-capture
然后按 t
键来快速记录一个任务:
记录完后,我们按下 C-c C-c
结束记录,然后我们打开 ~/org/tasks.org
文件可以看到,刚才添加的「写完第19课教程」的任务已经被加入到这个文件的第一个标题行了,我们还设置了一个截止时间为 2023-01-26 20:30
(通过 C-c C-d
来设置截止时间)。
然后我们再按下 Esc-Esc a a
就可以看到这条任务了:
在 org-agenda
视图,我们可以移动光标到这条任务上,然后按回车后,会自动跳转到 ~/org/tasks.org
文件的这一条任务的标题行,非常便利。
当我们完成了某个任务,我们可以在 org-agenda
视图先移动光标到这一行,然后按下 C-c C-t
然后把标题行的状态改成 DONE
,我们也可以打开相应的 org 文件,直接在标题行操作,效果是一样的。当我们改变了标题行的状态, org-agenda
视图的状态也会随之发生改变。
5 使用Org mode进行周期性的习惯管理 🔗
Org mode还能够自动的管理你的日常习惯,比如每周三健身、每周五写周报、每周日陪孩子上早教等,我们提前配置一下 org-habit
:
(use-package org-habit
:ensure nil
:defer t
:custom
(org-habit-show-habits t)
(org-habit-graph-column 70)
(org-habit-show-all-today t)
(org-habit-show-done-always-green t)
(org-habit-scheduled-past-days t)
;; org habit show 7 days before today and 7 days after today. ! means not done. * means done.
(org-habit-preceding-days 7)
)
配置完后,我们可以在 ~/org/habits.org
文件里写上如下:
* Working
** 写周报
SCHEDULED: <2023-02-03 星期五 17:30-18:00 .+7d>
:PROPERTIES:
:style: habit
:END:
* Life
** 练钢笔字
SCHEDULED: <2023-01-26 星期四 21:00-21:30 .+1d>
:PROPERTIES:
:style: habit
:END:
** 早教
SCHEDULED: <2023-02-05 星期日 10:00-11:00 .+7d>
:PROPERTIES:
:style: habit
:END:
然后就可以看到在 org-agenda
视图里,所有的日常习惯也都追踪显示了:
6 使用Org mode写日记 🔗
我们还可以使用Org mode来写日记。当然,在Emacs里写日记的方法不止一种,Emacs自带的Diary、Org roam都能够写日记,但我最喜欢的还是使用Org mode来写日记,他可以在早上帮你疏理一天的日程安排和计划,可以帮你记录下你一天的工作内容、感悟,而且都保存在org文件里,便于查找和复盘。
我们使用 Org mode 来记日记,有两个设置需要注意:
org-agenda-diary-file
变量设置日记文件,这里我设置的是~/org/diary.org
。org-agenda-include-diary
设置为nil
,不要引入 Emacs 自带的 diary 文件。
我们根据 第12课:通过Emacs来记笔记 - org-capture快速记录 这个章节配置的 org-capture
,我们按下 ESC-ESC c
再按下 d t
来触发一次日记 TODO 的快速记录:
然后我们就可以看到在 ~/org/diary.org
文件的今天这个标题行下面,刚刚添加的两个任务「写完博客」和「看完第三章书」都已经在下面了,而且都设置好了截止时间。
我们再次看下 org-agenda
视图,可以看到刚刚添加的2个任务,都在里面进行了记录和追踪。
7 任务管理通知 🔗
我们设置完了任务,那到了时间可以提醒吗?当然可以,不仅仅可以在Emacs里提醒,还可以触发一个系统级别的提醒。要做到这一点,我们需要一些额外的工具和配置。
7.1 系统级别的提醒设置 🔗
要实现系统级别的提醒,在 MacOS 下需要提前安装 terminal-notifier:
brew install terminal-notifier
然后我们来设置一下Emacs自带的 notifications
包:
(use-package notifications
:ensure nil
:commands notify-send
:config
(cond ((eq system-type 'darwin)
(defun notify-send (&rest params)
"Send notifications via `terminal-notifier'."
(let ((title (plist-get params :title))
(body (plist-get params :body)))
(start-process "terminal-notifier"
nil
"terminal-notifier"
"-group" "Emacs"
"-title" title
"-message" body
"-activate" "org.gnu.Emacs"))))
(t
(defalias 'notify-send 'notifications-notify)))
)
这里最重要的是 notify-send
这个函数,我们很多需要有系统级别的通知提醒(如后续会讲到的 notmuch 邮件提醒等)都会用到这个函数。
7.2 appt邀约提醒 🔗
我们在设置一下Emacs自带的 appt
包,将 org-agenda
里的提醒同步到 appt
,这样就能够在Emacs内以及操作系统级别都产生提醒了。具体可以参考下面这篇文章:
http://blog.lujun9972.win/blog/2020/02/18/%E6%95%B4%E5%90%88appt%E4%B8%8Eorg-agenda/index.html
;; appointment settings
(use-package appt
:ensure nil
:hook ((after-init . (lambda () (appt-activate 1)))
(org-finalize-agenda . org-agenda-to-appt))
:config
;; 通知提醒
(defun appt-display-with-notification (min-to-app new-time appt-msg)
(notify-send :title (format "Appointment in %s minutes" min-to-app)
:body appt-msg
:urgency 'critical)
(appt-disp-window min-to-app new-time appt-msg))
;; 每15分钟更新一次appt
(run-at-time t 900 #'org-agenda-to-appt)
(shut-up! #'org-agenda-to-appt)
:custom
;; 是否显示日记
(appt-display-diary nil)
;; 提醒间隔时间,每15分钟提醒一次
(appt-display-interval 15)
;; 模式栏显示提醒
(appt-display-mode-line t)
;; 设置提醒响铃
(appt-audible t)
;; 提前30分钟提醒
(appt-message-warning-time 30)
;; 通知提醒函数
(appt-disp-window-function #'appt-display-with-notification)
)
设置完后,在日程的30分钟前,15分钟前在Emacs和操作系统层级都会有提醒,你也可以按照自己的需求修改提前提醒的时间。
8 与移动端同步 🔗
由于本人使用的是iPhone,在iOS上,有个App叫做 beorg ,我们可以利用这个app将电脑上的org文件同步到beorg上,并进行手机端的提醒。要实现这个,一般需要下面两种同步方案:
- 开启iCloud同步,通过
beorg
的iCloud同步功能来同步org文件; - 通过自己搭建的webdav来同步;
这里我使用的是第2种方案(正准备换安卓,因此正在做去iOS化),我把 ~/org
文件夹与我的NAS做了同步,然后通过NAS的webdav功能,在 beorg
上配置了NAS的webdav服务地址和用户名密码即可。可以看到 beorg
很好的同步了org文件,并自动追踪日程,通过App的通知功能在手机上进行提醒。
9 结语 🔗
通过今天的学习,我们了解了如何通过Emacs和Org mode来进行日程管理,我们可以将我们的计划、日程都通过Org mode来管理,Org mode和Emacs拥有良好的机制能让你的任务管理变得井井有条,并且有迹可循,所有的一切都保存在文本里,永流传!
这节课的配置文件的快照见:emacs-config-l19.org
你也可以在 这里 查看最新的配置文件。