優化org-mode管理觀影進度

「實戰Elisp」系列旨在講述我使用Elisp定製Emacs的經驗,拋磚引玉,還請廣大Emacs同好不吝賜教——若是真的有廣大Emacs用戶的話,哈哈哈。

序言

在以前的文章中,我提到本身用org-mode管理工做日午飯和晚餐要看的動畫及其進度。簡單來講,就是爲每部動畫建立同名的二級條目(它們有一個共同的一級條目叫「動畫」),每當準備看新一集時,就在對應二級條目下建立形如「觀看第X話」的三級條目並設爲TODO。html

按這種方式,在《鋼煉FA》下最終會建立出64個三級條目——而可憐的Emacs甚至一屏只能顯示50行!這些「閱後即焚」的三級條目浪費了其它(或許)更有價值的內容的展現空間,所以最好將每個切換至 DONE狀態的三級條目藏起來。git

org-mode能夠「internal archive」一個條目,但這樣仍沒法節省它們佔據的縱向空間。後來,我想到了org-mode的drawer特性。github

思路及函數定義

基於drawer的方案很直接:在org-after-todo-state-change-hook中新增一個鉤子。每當一個條目切換至DONE狀態、並知足一些條件(好比條目的heading符合「觀看第X話」這個模式)時,就將heading的文本與當前時間一塊兒追加到父級條目下、名爲「觀看進度」的drawer的末端。less

org-mode沒有內置的函數能夠往某個條目的某個drawer中追加內容,須要自行利用「移動光標」和「字符串搜索」來實現。最終的成品以下ide

(require 'cl)

(defun lt-org-move-episode-to-drawer ()
  "往上級heading的drawer中插入一個內容,並刪除當前heading。"
  ;; 用cl-block來實現nonlocal-exit
  (cl-block lt-org-move-episode-to-drawer
    (let ((state org-state))
      (unless (string= state "DONE")
        (cl-return-from lt-org-move-episode-to-drawer)))

    (let ((tags (org-get-tags-at))
          (text (nth 4 (org-heading-components))))
      ;; 只處理內容以「觀看」開頭、帶有「動畫」標籤的heading
      (unless (and (or (string-prefix-p "觀看" text)
                       (string-prefix-p "繼續閱讀" text))
                   (or (member "動畫" tags)
                       (member "閱讀" tags)))
        (cl-return-from lt-org-move-episode-to-drawer))

      (save-excursion
        (let (current-position)
          ;; 記下當前的位置,以後搜索的時候到這裏爲止
          (setq current-position (point))
          ;; 往上走一級,以便尋找一個名字叫作「觀看進度」的drawer
          (widen)
          (outline-up-heading 1)
          (unless (search-forward ":觀看進度:" current-position t)
            (message "請自行建立「觀看進度」的drawer")
            (cl-return-from lt-org-move-episode-to-drawer))

          ;; 繼續往前找到:END:的標記
          (unless (search-forward ":END:" current-position t)
            (message "請確保有完整的「觀看進度」的drawer")
            (cl-return-from lt-org-move-episode-to-drawer))
          ;; 往左走五步
          (backward-char 5)
          ;; 開闢一行新的,而後把剛剛完成的任務的內容和時間戳放進來
          (org-open-line 1)
          (insert (format "「%s」 DONE at %s" text (current-time-string)))))
      (org-cut-subtree))))

別忘了加入到鉤子中函數

(add-hook 'org-after-todo-state-change-hook 'lt-org-move-episode-to-drawer t)

後記

lt-org-move-episode-to-drawer的缺點在於它會刪掉切換至DONE狀態的條目,所以這個觀看記錄在*Org Agenda*中會徹底消失——這一點尚能接受。動畫

此外,上面的defun徹底能夠修改成cl-defun,並移除cl-blockui

閱讀原文spa

相關文章
相關標籤/搜索