Org-mode, 用文本文件管理日常(十三)

13 篇文章 0 订阅

本文翻译自 http://doc.norang.ca/org-mode.html ,原文作者为Bernt Hansen 。由于原文较长,因此会分多篇文章来发布。转载请标记出处。

提高生产力工具

这章主要是一系列我的org-mode中使用的emacs定制化的集合。

Abbrev-mode以及Skeletons

我在Abbrev-mode中使用skeletons,可以快速在我的emacs缓冲区添加预定义代码块。

我需要创建如下一些代码块:

  • org-mode中的通用代码块
  • org-mode中的plantuml块
    • plantuml活动图的代码块
    • plantuml序列图的代码块
  • org-mode中graphviz dot代码块
  • ditta代码块
  • org-mode中elisp代码块

我用依然用 < e TAB 或者 < s TAB 创建用例代码块以及shell脚本代码块。

这是我当前org-mode关联的设置。每个相关代码块都定义一个abbrev-mode快捷键,
因此我可以输入 splantuml RET 来创建一个plantuml代码块。同时弹出文件名
来命名生成的图像。

工作中生成代码块的同时也会为其添加 :tangle 头,这样代码块中就可以包含
@startuml 以及 @enduml 。因此我可以使用tangle功能,生成新文件,并分享
给同事。这样,新文件就可以在notepad以及 plantUML jar单独运行。

我使用 s 快捷键阻止abbrev-mode生效,防止在一个句子中输入plantuml时候被当
做要插入 PlantUML 代码块。

为在活动视图中方便使用方便,我添加了 sif 以及 sfor 用来生成if判断以及for
循环。

;; Enable abbrev-mode
(add-hook 'org-mode-hook (lambda () (abbrev-mode 1)))

;; Skeletons
;;
;; sblk - Generic block #+begin_FOO .. #+end_FOO
(define-skeleton skel-org-block
  "Insert an org block, querying for type."
  "Type: "
  "#+begin_" str "\n"
  _ - \n
  "#+end_" str "\n")

(define-abbrev org-mode-abbrev-table "sblk" "" 'skel-org-block)

;; splantuml - PlantUML Source block
(define-skeleton skel-org-block-plantuml
  "Insert a org plantuml block, querying for filename."
  "File (no extension): "
  "#+begin_src plantuml :file " str ".png :cache yes\n"
  _ - \n
  "#+end_src\n")

(define-abbrev org-mode-abbrev-table "splantuml" "" 'skel-org-block-plantuml)

(define-skeleton skel-org-block-plantuml-activity
  "Insert a org plantuml block, querying for filename."
  "File (no extension): "
  "#+begin_src plantuml :file " str "-act.png :cache yes :tangle " str "-act.txt\n"
  (bh/plantuml-reset-counters)
  "@startuml\n"
  "skinparam activity {\n"
  "BackgroundColor<<New>> Cyan\n"
  "}\n\n"
  "title " str " - \n"
  "note left: " str "\n"
  "(*) --> \"" str "\"\n"
  "--> (*)\n"
  _ - \n
  "@enduml\n"
  "#+end_src\n")

(defvar bh/plantuml-if-count 0)

(defun bh/plantuml-if () 
  (incf bh/plantuml-if-count)
  (number-to-string bh/plantuml-if-count))

(defvar bh/plantuml-loop-count 0)

(defun bh/plantuml-loop () 
  (incf bh/plantuml-loop-count)
  (number-to-string bh/plantuml-loop-count))

(defun bh/plantuml-reset-counters ()
  (setq bh/plantuml-if-count 0
        bh/plantuml-loop-count 0)
  "")

(define-abbrev org-mode-abbrev-table "sact" "" 'skel-org-block-plantuml-activity)

(define-skeleton skel-org-block-plantuml-activity-if
  "Insert a org plantuml block activity if statement"
  "" 
  "if \"\" then\n"
  "  -> [condition] ==IF" (setq ifn (bh/plantuml-if)) "==\n"
  "  --> ==IF" ifn "M1==\n"
  "  -left-> ==IF" ifn "M2==\n"
  "else\n"
  "end if\n"
  "--> ==IF" ifn "M2==")

(define-abbrev org-mode-abbrev-table "sif" "" 'skel-org-block-plantuml-activity-if)

(define-skeleton skel-org-block-plantuml-activity-for
  "Insert a org plantuml block activity for statement"
  "Loop for each: " 
  "--> ==LOOP" (setq loopn (bh/plantuml-loop)) "==\n"
  "note left: Loop" loopn ": For each " str "\n"
  "--> ==ENDLOOP" loopn "==\n"
  "note left: Loop" loopn ": End for each " str "\n" )

(define-abbrev org-mode-abbrev-table "sfor" "" 'skel-org-block-plantuml-activity-for)

(define-skeleton skel-org-block-plantuml-sequence
  "Insert a org plantuml activity diagram block, querying for filename."
  "File appends (no extension): "
  "#+begin_src plantuml :file " str "-seq.png :cache yes :tangle " str "-seq.txt\n"
  "@startuml\n"
  "title " str " - \n"
  "actor CSR as \"Customer Service Representative\"\n"
  "participant CSMO as \"CSM Online\"\n"
  "participant CSMU as \"CSM Unix\"\n"
  "participant NRIS\n"
  "actor Customer"
  _ - \n
  "@enduml\n"
  "#+end_src\n")

(define-abbrev org-mode-abbrev-table "sseq" "" 'skel-org-block-plantuml-sequence)

;; sdot - Graphviz DOT block
(define-skeleton skel-org-block-dot
  "Insert a org graphviz dot block, querying for filename."
  "File (no extension): "
  "#+begin_src dot :file " str ".png :cache yes :cmdline -Kdot -Tpng\n"
  "graph G {\n"
  _ - \n
  "}\n"
  "#+end_src\n")

(define-abbrev org-mode-abbrev-table "sdot" "" 'skel-org-block-dot)

;; sditaa - Ditaa source block
(define-skeleton skel-org-block-ditaa
  "Insert a org ditaa block, querying for filename."
  "File (no extension): "
  "#+begin_src ditaa :file " str ".png :cache yes\n"
  _ - \n
  "#+end_src\n")

(define-abbrev org-mode-abbrev-table "sditaa" "" 'skel-org-block-ditaa)

;; selisp - Emacs Lisp source block
(define-skeleton skel-org-block-elisp
  "Insert a org emacs-lisp block"
  ""
  "#+begin_src emacs-lisp\n"
  _ - \n
  "#+end_src\n")

(define-abbrev org-mode-abbrev-table "selisp" "" 'skel-org-block-elisp)

我在工作中做备忘,依然会使用abbrev-mode。我写了第一个人名后就可以展开成完整的名字。 因此
当我写了 mickey 就会自动展开成 Mickey Mouse . 为创建缩写,只要输入快捷键
C-x a i l 来创建当前模式的缩写。

你所要做的就是,不要使用通用的单词,来作为你的缩写,因为abbrev-mode会自动扩展所有你写入的。
我发现当我输入 plantuml 就会碰到这个问题,非常讨厌。

我同样在c源代码中使用abbrev-mode。这个非常适合我。

PlantUml 活动图生成示例

当需要创建活动图时,我会用 sif 以及 sfor 添加IF以及FOR代码块,并产生自动唯一的id。

例如: 创建一个包含两个IFs以及两个FOR代码块的活动图。

创建图标: “sact RET test RET”

示例

@startuml
skinparam activity {
BackgroundColor<<New>> Cyan
}

title test - 
note left: test
(*) --> "test"
--> (*)

@enduml

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HRrvNC0N-1645013712028)(test-act.png)]

将光标移动到–>(*)然后输入 “sif RET”

示例

@startuml
skinparam activity {
BackgroundColor<<New>> Cyan
}

title test - 
note left: test
(*) --> "test"
if "" then
  -> [condition] ==IF1==
  --> ==IF1M1==
  -left-> ==IF1M2==
else
end if
--> ==IF1M2==
--> (*)

@enduml

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DR4F3H4D-1645013712030)(test-act2.png)]

(*)行重复执行

示例

@startuml
skinparam activity {
BackgroundColor<<New>> Cyan
}

title test - 
note left: test
(*) --> "test"
if "" then
  -> [condition] ==IF1==
  --> ==IF1M1==
  -left-> ==IF1M2==
else
end if
--> ==IF1M2==
if "" then
  -> [condition] ==IF2==
  --> ==IF2M1==
  -left-> ==IF2M2==
else
end if
--> ==IF2M2==
--> (*)

@enduml

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m0axEdOn-1645013712030)(test-act3.png)]

通过“sfor RET line in file RET”以及"sfor RET address in addressbook RET",添加了两个for循环.

示例

@startuml
skinparam activity {
BackgroundColor<<New>> Cyan
}

title test - 
note left: test
(*) --> "test"
if "" then
  -> [condition] ==IF1==
  --> ==IF1M1==
  -left-> ==IF1M2==
else
end if
--> ==IF1M2==
if "" then
  -> [condition] ==IF2==
  --> ==IF2M1==
  -left-> ==IF2M2==
else
end if
--> ==IF2M2==
--> ==LOOP1==
note left: Loop1: For each line in file
--> ==ENDLOOP1==
note left: Loop1: End for each line in file
--> ==LOOP2==
note left: Loop2: For each address in addressbook
--> ==ENDLOOP2==
note left: Loop2: End for each address in addressbook

--> (*)

@enduml

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DNcIzvd2-1645013712031)(test-act4.png)]

当需要调整对齐时候,我会使用列编辑模式来处理。

聚焦当前工作

有多种方式可以达到这个要求,选择合适自己的方式就行。

执行 bh/org-todo 限制子任务树显示

将=f5= 以及 s-f5 分别绑定到限制显示范围以及恢复显示范围功能上。

我就可以这样使用:

  • T (任务)C-c / t 在当前缓冲区
  • N (限制)只显示该任务子树
  • U (向上)显示父任务子树
  • P (项目)显示父项目的子任务树
  • F (文件)只显示当前文件以及文件限制

但是agenda视图依然保持显示所有缓冲区任务,这方便聚焦我们在做的事情上。

(global-set-key (kbd "<f5>") 'bh/org-todo)

(defun bh/org-todo (arg)
  (interactive "p")
  (if (equal arg 4)
      (save-restriction
        (bh/narrow-to-org-subtree)
        (org-show-todo-tree nil))
    (bh/narrow-to-org-subtree)
    (org-show-todo-tree nil)))

(global-set-key (kbd "<S-f5>") 'bh/widen)

(defun bh/widen ()
  (interactive)
  (if (equal major-mode 'org-agenda-mode)
      (progn
        (org-agenda-remove-restriction-lock)
        (when org-agenda-sticky
          (org-agenda-redo)))
    (widen)))

(add-hook 'org-agenda-mode-hook
          '(lambda () (org-defkey org-agenda-mode-map "W" (lambda () (interactive) (setq bh/hide-scheduled-and-waiting-next-tasks t) (bh/widen))))
          'append)

(defun bh/restrict-to-file-or-follow (arg)
  "Set agenda restriction to 'file or with argument invoke follow mode.
I don't use follow mode very often but I restrict to file all the time
so change the default 'F' binding in the agenda to allow both"
  (interactive "p")
  (if (equal arg 4)
      (org-agenda-follow-mode)
    (widen)
    (bh/set-agenda-restriction-lock 4)
    (org-agenda-redo)
    (beginning-of-buffer)))

(add-hook 'org-agenda-mode-hook
          '(lambda () (org-defkey org-agenda-mode-map "F" 'bh/restrict-to-file-or-follow))
          'append)

(defun bh/narrow-to-org-subtree ()
  (widen)
  (org-narrow-to-subtree)
  (save-restriction
    (org-agenda-set-restriction-lock)))

(defun bh/narrow-to-subtree ()
  (interactive)
  (if (equal major-mode 'org-agenda-mode)
      (progn
        (org-with-point-at (org-get-at-bol 'org-hd-marker)
          (bh/narrow-to-org-subtree))
        (when org-agenda-sticky
          (org-agenda-redo)))
    (bh/narrow-to-org-subtree)))

(add-hook 'org-agenda-mode-hook
          '(lambda () (org-defkey org-agenda-mode-map "N" 'bh/narrow-to-subtree))
          'append)

(defun bh/narrow-up-one-org-level ()
  (widen)
  (save-excursion
    (outline-up-heading 1 'invisible-ok)
    (bh/narrow-to-org-subtree)))

(defun bh/get-pom-from-agenda-restriction-or-point ()
  (or (and (marker-position org-agenda-restrict-begin) org-agenda-restrict-begin)
      (org-get-at-bol 'org-hd-marker)
      (and (equal major-mode 'org-mode) (point))
      org-clock-marker))

(defun bh/narrow-up-one-level ()
  (interactive)
  (if (equal major-mode 'org-agenda-mode)
      (progn
        (org-with-point-at (bh/get-pom-from-agenda-restriction-or-point)
          (bh/narrow-up-one-org-level))
        (org-agenda-redo))
    (bh/narrow-up-one-org-level)))

(add-hook 'org-agenda-mode-hook
          '(lambda () (org-defkey org-agenda-mode-map "U" 'bh/narrow-up-one-level))
          'append)

(defun bh/narrow-to-org-project ()
  (widen)
  (save-excursion
    (bh/find-project-task)
    (bh/narrow-to-org-subtree)))

(defun bh/narrow-to-project ()
  (interactive)
  (if (equal major-mode 'org-agenda-mode)
      (progn
        (org-with-point-at (bh/get-pom-from-agenda-restriction-or-point)
          (bh/narrow-to-org-project)
          (save-excursion
            (bh/find-project-task)
            (org-agenda-set-restriction-lock)))
        (org-agenda-redo)
        (beginning-of-buffer))
    (bh/narrow-to-org-project)
    (save-restriction
      (org-agenda-set-restriction-lock))))

(add-hook 'org-agenda-mode-hook
          '(lambda () (org-defkey org-agenda-mode-map "P" 'bh/narrow-to-project))
          'append)

(defvar bh/project-list nil)

(defun bh/view-next-project ()
  (interactive)
  (let (num-project-left current-project)
    (unless (marker-position org-agenda-restrict-begin)
      (goto-char (point-min))
      ; Clear all of the existing markers on the list
      (while bh/project-list
        (set-marker (pop bh/project-list) nil))
      (re-search-forward "Tasks to Refile")
      (forward-visible-line 1))

    ; Build a new project marker list
    (unless bh/project-list
      (while (< (point) (point-max))
        (while (and (< (point) (point-max))
                    (or (not (org-get-at-bol 'org-hd-marker))
                        (org-with-point-at (org-get-at-bol 'org-hd-marker)
                          (or (not (bh/is-project-p))
                              (bh/is-project-subtree-p)))))
          (forward-visible-line 1))
        (when (< (point) (point-max))
          (add-to-list 'bh/project-list (copy-marker (org-get-at-bol 'org-hd-marker)) 'append))
        (forward-visible-line 1)))

    ; Pop off the first marker on the list and display
    (setq current-project (pop bh/project-list))
    (when current-project
      (org-with-point-at current-project
        (setq bh/hide-scheduled-and-waiting-next-tasks nil)
        (bh/narrow-to-project))
      ; Remove the marker
      (setq current-project nil)
      (org-agenda-redo)
      (beginning-of-buffer)
      (setq num-projects-left (length bh/project-list))
      (if (> num-projects-left 0)
          (message "%s projects left to view" num-projects-left)
        (beginning-of-buffer)
        (setq bh/hide-scheduled-and-waiting-next-tasks t)
        (error "All projects viewed.")))))

(add-hook 'org-agenda-mode-hook
          '(lambda () (org-defkey org-agenda-mode-map "V" 'bh/view-next-project))
          'append)

这些辅助函数,通过限制显示范围,方便临时隐藏org文件中细节。任务通过折叠高亮
等方式显示未完成的任务。

我每天执行很多 f5 (或者 T 快捷键)。它主要做的事情就是 org-narrow-to-subtree 以及
C-c / t 结合保证缓冲区不要显示太多不必要的任务。我使用 S-f5=(或者像 =U W F 之类
的快捷键)来切换到正常显示状态。

在agenda限制任务子树显示

C-c C-x < 开启子树agenda限制。这样agenda视图只显示当前子树。但是隐藏任务相关的
警告以及通知设置,在agenda之外依然正常工作。通过快捷键 C-c C-x > 关闭agenda限制
,让agenda正常显示所有任务。

(add-hook 'org-agenda-mode-hook
          '(lambda () (org-defkey org-agenda-mode-map "\C-c\C-x<" 'bh/set-agenda-restriction-lock))
          'append)

(defun bh/set-agenda-restriction-lock (arg)
  "Set restriction lock to current task subtree or file if prefix is specified"
  (interactive "p")
  (let* ((pom (bh/get-pom-from-agenda-restriction-or-point))
         (tags (org-with-point-at pom (org-get-tags-at))))
    (let ((restriction-type (if (equal arg 4) 'file 'subtree)))
      (save-restriction
        (cond
         ((and (equal major-mode 'org-agenda-mode) pom)
          (org-with-point-at pom
            (org-agenda-set-restriction-lock restriction-type))
          (org-agenda-redo))
         ((and (equal major-mode 'org-mode) (org-before-first-heading-p))
          (org-agenda-set-restriction-lock 'file))
         (pom
          (org-with-point-at pom
            (org-agenda-set-restriction-lock restriction-type))))))))

这允许我从agenda直接限制任务显示,我通常在agenda里边做很多工作,这样就非常方便。

与自动移到项目最上层任务比较 限制任务显示就显得不够惊喜了– 我曾经就是这么做的。
如果当前项目显示有限,需要向上查看,先移动到上面,再做限制。

往往一个项目有很多任务在里边,限制显示一个项目也会有很多要显示的,所以我想限制只
显示其中一个子树。这就是为什么我保留 N 以及 U 来调整显示区域。

我为agenda视图以及org-buffer添加了其他的新的快捷键来控制子树,父任务以及项目任务,当然也
包含相应的取消限制的快捷键。这些快捷键既适合在agenda中也适合在org 文件中使用。

  • N 限制显示当前任务子树
    功能和 C-c C-x < 一致

  • U 将限制扩展到该子树对应的父任务
    该功能对父子树做限制

  • P 限制整个任务所对应的项目
    将会向上找到最上层 TODO 关键字选择整个子树限制显示

  • W 取消限制,显示原样

    ;; Limit restriction lock highlighting to the headline only
    (setq org-agenda-restriction-lock-highlight-subtree nil)

限制agenda只显示一个文件

你也可以只让agenda显示单个文件的视图,当然有多种方法可以做到这点。

可以在第一个标题行前的任意行使用agenda的限制快捷键 C-c C-x < 来限制agenda视图
只显示当前文件视图。这相当于使用前置参数( C-u C-c C-x < )。这种限制一直有效,
如果需要取消,需要执行 = C-c C-x >=.

同样,在agenda中执行 C-u C-c c-x < 也可以达到这个效果。

另一种方法是通过 F12 < a 在org 文件中启动agenda视图。这就会让agenda只显示
该文件内容。我通常会对非 org-agenda-files 变量定义的文件会通过这种方法查看
agenda视图。

agenda视图开启

有很多定制化agenda视图方法来显示任务细节。这个章节会介绍我的工作流中使用到的
一些定制化。

高亮显示当前agenda行

下面的代码来自我的 .emacs 文件,高亮显示当前的agenda行。这会让该任务高亮显示,
这样就不会选错任务,从而导致对错的任务执行操作。

模式行中计时信息也会反向背景显示。

;; Always hilight the current agenda line
(add-hook 'org-agenda-mode-hook
          '(lambda () (hl-line-mode 1))
          'append)

;; The following custom-set-faces create the highlights
(custom-set-faces
  ;; custom-set-faces was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 '(org-mode-line-clock ((t (:background "grey75" :foreground "red" :box (:line-width -1 :style released-button)))) t))

在全局todo列表中显示时间戳

带(=SCHEDULED:= DEADLINE: 以及其他日期)在agenda中能够合理显示出来。agenda
视图(=F12 a=)只会让任务在一个地方显示(要么在日历中,要么在其他的agenda列表显示)。
我现在很少在org-mode使用全局列表查找(F12 t, F12 m)。只有需要找特定任务时候,我才会
这么做。现在该列表包含 所有 , 因此我能够找到我想要的任务。

agenda中不会显示那些截止日期以及计划在将来发生的任务,因此你能够先忽略他们,直到合适
时候他们才会出现在agenda列表中。

;; Keep tasks with dates on the global todo lists
(setq org-agenda-todo-ignore-with-date nil)

;; Keep tasks with deadlines on the global todo lists
(setq org-agenda-todo-ignore-deadlines nil)

;; Keep tasks with scheduled dates on the global todo lists
(setq org-agenda-todo-ignore-scheduled nil)

;; Keep tasks with timestamps on the global todo lists
(setq org-agenda-todo-ignore-timestamp nil)

;; Remove completed deadline tasks from the agenda view
(setq org-agenda-skip-deadline-if-done t)

;; Remove completed scheduled tasks from the agenda view
(setq org-agenda-skip-scheduled-if-done t)

;; Remove completed items from search results
(setq org-agenda-skip-timestamp-if-done t)

使用日记来记录假期以及纪念日

我并不用emacs日记来记录所有事情, 但是我还是希望能在我的agenda中查看到节日信息。
这能方便我在不需要工作时候(节假日),做合适的计划。

(setq org-agenda-include-diary nil)
(setq org-agenda-diary-file "~/git/org/diary.org")

日记文件包含着由capture mode创建的约会模板生成的 时间树 项。我也会用它记录一些突发的
需要计时的任务。

我不用 ~/diary 文件。只保留一个空文件,为了使得emacs能正常工作。我使用org-mode的
日记。在agenda视图中通过 i 来创建一个日期项,并同步到 ~/git/org/diary.org 文件中。

我将日历中的节假日导入我的 todo.org ,文件格式如下:

#+FILETAGS: PERSONAL
* Appointments
  :PROPERTIES:
  :CATEGORY: Appt
  :ARCHIVE:  %s_archive::* Appointments
  :END:      
** Holidays
   :PROPERTIES:
   :Category: Holiday
   :END:
   %%(org-calendar-holiday)
** Some other Appointment
   ...

我使用下面的设置使得agenda可以正常显示日志时间。

(setq org-agenda-insert-diary-extract-time t)

包含归档文件查找

我只用一个归档文件用来归档我所有的org-mode相关的项目文件。这样当我需要查一些信息时,
可以既查看当前文件也可以查看归档文件,方便我从归档文件中找到想要的信息。

这个功能我并不常用,但是有备无患,如果哪天需要,我还是可以立马能使用。

;; Include agenda archive files when searching for things
(setq org-agenda-text-search-extra-files (quote (agenda-archives)))

agenda视图调整

下面列举的是agenda视图需要定制化的地方:

  • 显示重复的任务
  • 在agenda中显示空日期
  • 调整任务排序
  • agenda周视图以周日开始
  • 显示网格
  • 在底部显示习惯

我定制化了排序函数,通过该函数,agenda中任务都是按照重要性来排序的。每日agenda视图
按照如下顺序排序:

  1. 有时间戳的在前面显示,因此就不会忽略这些任务了
  2. 今日的任务(带时间戳的活跃任务,但并不包含计划任务或者截止任务)
  3. 今日截止任务
  4. 后续截止任务
  5. 今日计划任务
  6. 待定的有截止日期的任务(很快可能要处理)
  7. 后续计划条目
  8. 习惯

这个排序算法可能并不特比完美,但是能够正常工作。

下面是在我 .emacs 中的相关配置:

;; Show all future entries for repeating tasks
(setq org-agenda-repeating-timestamp-show-all t)

;; Show all agenda dates - even if they are empty
(setq org-agenda-show-all-dates t)

;; Sorting order for tasks on the agenda
(setq org-agenda-sorting-strategy
      (quote ((agenda habit-down time-up user-defined-up effort-up category-keep)
              (todo category-up effort-up)
              (tags category-up effort-up)
              (search category-up))))

;; Start the weekly agenda on Monday
(setq org-agenda-start-on-weekday 1)

;; Enable display of the time grid so we can see the marker for the current time
(setq org-agenda-time-grid (quote ((daily today remove-match)
                                   #("----------------" 0 16 (org-heading t))
                                   (0900 1100 1300 1500 1700))))

;; Display tags farther right
(setq org-agenda-tags-column -102)

;;
;; Agenda sorting functions
;;
(setq org-agenda-cmp-user-defined 'bh/agenda-sort)

(defun bh/agenda-sort (a b)
  "Sorting strategy for agenda items.
Late deadlines first, then scheduled, then non-late deadlines"
  (let (result num-a num-b)
    (cond
     ; time specific items are already sorted first by org-agenda-sorting-strategy

     ; non-deadline and non-scheduled items next
     ((bh/agenda-sort-test 'bh/is-not-scheduled-or-deadline a b))

     ; deadlines for today next
     ((bh/agenda-sort-test 'bh/is-due-deadline a b))

     ; late deadlines next
     ((bh/agenda-sort-test-num 'bh/is-late-deadline '> a b))

     ; scheduled items for today next
     ((bh/agenda-sort-test 'bh/is-scheduled-today a b))

     ; late scheduled items next
     ((bh/agenda-sort-test-num 'bh/is-scheduled-late '> a b))

     ; pending deadlines last
     ((bh/agenda-sort-test-num 'bh/is-pending-deadline '< a b))

     ; finally default to unsorted
     (t (setq result nil)))
    result))

(defmacro bh/agenda-sort-test (fn a b)
  "Test for agenda sort"
  `(cond
    ; if both match leave them unsorted
    ((and (apply ,fn (list ,a))
          (apply ,fn (list ,b)))
     (setq result nil))
    ; if a matches put a first
    ((apply ,fn (list ,a))
     (setq result -1))
    ; otherwise if b matches put b first
    ((apply ,fn (list ,b))
     (setq result 1))
    ; if none match leave them unsorted
    (t nil)))

(defmacro bh/agenda-sort-test-num (fn compfn a b)
  `(cond
    ((apply ,fn (list ,a))
     (setq num-a (string-to-number (match-string 1 ,a)))
     (if (apply ,fn (list ,b))
         (progn
           (setq num-b (string-to-number (match-string 1 ,b)))
           (setq result (if (apply ,compfn (list num-a num-b))
                            -1
                          1)))
       (setq result -1)))
    ((apply ,fn (list ,b))
     (setq result 1))
    (t nil)))

(defun bh/is-not-scheduled-or-deadline (date-str)
  (and (not (bh/is-deadline date-str))
       (not (bh/is-scheduled date-str))))

(defun bh/is-due-deadline (date-str)
  (string-match "Deadline:" date-str))

(defun bh/is-late-deadline (date-str)
  (string-match "\\([0-9]*\\) d\. ago:" date-str))

(defun bh/is-pending-deadline (date-str)
  (string-match "In \\([^-]*\\)d\.:" date-str))

(defun bh/is-deadline (date-str)
  (or (bh/is-due-deadline date-str)
      (bh/is-late-deadline date-str)
      (bh/is-pending-deadline date-str)))

(defun bh/is-scheduled (date-str)
  (or (bh/is-scheduled-today date-str)
      (bh/is-scheduled-late date-str)))

(defun bh/is-scheduled-today (date-str)
  (string-match "Scheduled:" date-str))

(defun bh/is-scheduled-late (date-str)
  (string-match "Sched\.\\(.*\\)x:" date-str))

便利贴式的agenda

便利贴式的agenda允许你同时创建不止一个agenda视图。你可以快速切换到该视图而不需要额外的通过
agenda命令重建agenda视图。如果已经存在,那么就会显示存在视图。快捷键 g 可以强制重新生成
agenda视图。

我通常会使用两个agenda视图(=F12 a= 每日每周视图 F12 SPC 项目视图)。

开启该功能的配置如下:

;; Use sticky agenda's so they persist
(setq org-agenda-sticky t)

清单处理

清单对于那种重复性并包含非常多需要做的事情的任务适用。曾经,当清单任务完成后,我会手动
清除复选框的选择状态,以保证后续能继续执行。但是现在不会这样麻烦了,因为有 rog-checklist 功能,
当重复的任务标记完成时候,清单中复选框就会自动清除选择状态。

在你的.emacs中添加如下脚本来完成上述功能。

(add-to-list 'load-path (expand-file-name "~/git/org-mode/contrib/lisp"))

(require 'org-checklist)

当需要在任务中使用时候,只需要设置 RESET_CHECK_BOXESt .
像下面一样:

* TODO Invoicing and Archive Tasks [0/7]
  DEADLINE: <2009-07-01 Wed +1m -0d> 
  :PROPERTIES:
  :RESET_CHECK_BOXES: t
  :END:

  - [ ] Do task 1
  - [ ] Do task 2
  ...
  - [ ] Do task 7

备份

= 及时备份那些还没有完成的的任务 =

10年前,我曾经因为没有很好的备份手段而丢失掉大量数据。那时我就告诫自己 = 我以后
再也不想丢失任何数据 = 。到现在为止确实没有丢失过了:).

我在备份时候很谨慎。org模式怎么来帮助完成备份的?确实没有花很大力气,并不是我不想
花很多时间来备份–它时刻在发生–这节省我时间,并可以把这些时间花在处理其他更有趣
的事情上。

我备份的原则就是方便恢复数据–但是这条却并不容易实现。也并不需要非常容易/快速从备
份中恢复,因为从备份数据将其恢复也并不经常发生。恢复时候节省时间没什么意义。相反,
我希望备份时候能够快速无痛点,因为我经常做备份。

10年前我就自动化网络备份了,当然现在依然再用。我所用的所有电脑从网络驱动获取
每日备份。每个月收集一次并写入可移动硬盘。

我有个月备份任务,当满一个月,就会提醒我备份当前数据到外部存储中。我很开心
现在备份执行时间变得越来越少。

自从 git 进入我生活, 多主机备份 git 代码仓库就变得更加简单了。当前只要
有修改推送发送远程仓库,就会自动备份该仓库中所有东西。

处理受阻任务

受阻任务是那些包含子任务的任务,并且该任务至少有一个子任务不处于done状态。
受阻任务在agenda视图中以灰色来显示。

可以通过如下脚本,启用受阻任务功能:

(setq org-enforce-todo-dependencies t)

这个设置会阻碍试图将包含未在完成状态子任务的任务,设置为完成状态。除了重复
任务外。这样做非常好。对于重复任务,我通常会在任务下面添加很多=TODO=状态
子任务,并且这些子任务也不一定要这个周期完成,可以在下个周期完成。

当然,可以通过快捷键 C-u C-u C-u C-c C-t 临时修改任务设置,但我记不住
这组快捷键。我在重复任务设置了一个永久属性:

* TODO New Repeating Task
  SCHEDULED: <2009-06-16 Tue +1w>
  :PROPERTIES:
  :NOBLOCKING: t
  :END:
  ...
** TODO Subtask  

这可使得 新的重复任务 即使有子任务没有完成,也可以正常对父任务设置完成状态,不会受阻。

通常我会按照一个给定的顺序来完成任务。 org-mode有个 ORDERED 属性来对子
任务做顺序限制。

* TODO Some Task
  :PROPERTIES:
  :ORDERED: t
  :END:
** TODO Step 1
** TODO Step 2
** TODO Step 3  

这种情况下,你需要在完成 Step 1 后再去完成 Step 2 。org mode会阻止你去
对一个前面还有不在完成状态的任务,设置完成状态。

org任务结构以及展示

本节主要介绍我处理org文件时,是怎么设置任务显示的大量定制化脚本。

控制标题前置符号显示

对于任务的前置符号(*),org-mode可以定制化其是否显示。也有可能在其它层级中出现标题,
这样前置符号以及标题就可以在子树层对齐。

为使得org 显示前置符号,可以做如下设置

(setq org-hide-leading-stars nil)

我当前使用 org-indent mode来隐藏前置符号(*).

org-indent模式

我最近开始使用org-indent模式。我很喜欢这个模式。它会在org文件中移除这些对齐符号,
但是当编辑缓冲区时,这些对齐就会显示。

org-indent模式当org-odd-level-only 值为true时显示,但是显示效果比我之前的设置更加整洁。
因此我更喜欢它。

我的org-indent模式在emacs启动时自动生效:

(setq org-startup-indented t)

处理空行

空行很邪恶 😃. 他们自动在标题行之间插入,但是当标题行折叠后我并不想看到他们。
当我用 TAB (循环)折叠标题时,不希望在标题间看到空行。

下列设置保证隐藏空行,这样在标题折叠后,就会更加精炼整洁。

(setq org-cycle-separator-lines 0)

我发现在列表以及大纲显示空行也有点令人讨厌。为了获取list列表后内容,你需要包含
空行–合适缩放内容。我大部分list没有内容,所有我也不希望list后面有空行。

下面设置,防止在创建标题时插入空行,但是允许list兼容空行。

(setq org-blank-before-new-entry (quote ((heading)
                                         (plain-list-item . auto))))

快速添加不含当前任务内容的任务。

为在项目文件中创建一个新标题非常方便,可以通过快捷键 C-RETC-S-RET,
M-RET , 以及 M-S-RET . 这将插入一个新标题并包含 TODO 关键字。

通过如下设置:

(setq org-insert-heading-respect-content nil)

org在光标行通过 M- 插入大纲,通过 C- 插入内容。内容设置对于 C- 版本临时开启的,
会添加新标题. 所以可以通过快捷键 C-S-RET 在当前条目上执行后,就会在后面
添加一个新标题。也可以通过 M-S-RET 将一个标题分成两个。

备忘放在任务前部

我通过 C-c C-z 创建一个备忘任务(或者在agenda中执行 z )。有时候修改任务
状态也会弹出要求输入备忘(e.g. 移动到 WAITING 状态,就会弹出备忘,我会输入
进入等待状态的原因).这些备忘保存在任务最前端,因此当取消折叠任务时,备忘就会显示
最前端。

(setq org-reverse-note-order nil)

查找并显示结果

当需要在你的org文件中查询数据,org-mode的查找能力就起作用了。 C-c / /
做正则表达式查找当前文件,并在org文件折叠视图中显示匹配结果。

当使用如下设置,我的org模式就可以显示匹配项的任务,以及邻近的任务(并不是所有相邻任务):

(setq org-show-following-heading t)
(setq org-show-hierarchy-above t)
(setq org-show-siblings (quote ((default))))

这使得查找结果显示更加精简,也能够避免由于使用 C-k 从org文件中剪切太多的数据。剪切折叠
数据(包括…)非常危险,因为它也将那些你看不到的文本内容(包括相邻的子任务)也会
剪切掉。正因为这样,我总是在显示查找结果时,也会显示大纲。

编辑以及特殊按键处理

在处理标题时,org-mode快捷键C-a, C-e以及C-k快捷键都能正常工作。我也会用一些设置
来处理粘贴(yanks)子任务以及根据任务调整子树层级。通过查看帮助文档(C-h v org-yank-adjust- subtrees), 可以查看详细细节以及如何使用。

我使用 org-special-ctrl-a/e ,能够快速移动光标到标题开始结束位置。 我使用 M-m 或者 C-a C-a
来移动光标到行开始处。 因此,快捷键在org mode依然很好工作,并且通过快捷键 C-a
我能够将光标定位到行开始处。

附件

附件可以很方便在你的项目相关org-mode文件之外,添加关联的大量数据。在附件功能出来
之前我会在我的org文件中包含非常多的SQL代码段,来保存项目数据库的修改。这导致我的org
文件非常大。

(setq org-id-method (quote uuidgen))

假如说,你想把 x.sql 添加到当前任务中。在 /tmp/x.sql 中创建文件保存。

通过快捷键 C-c C-a a ,添加附件,并输入文件名: x.sql.这将为任务生成一个
唯一ID,然后把文件拷贝到附件目录。

* Attachments                                                        :ATTACH:
  :PROPERTIES:
  :Attachments: x.sql
  :ID:       f1d38e9a-ff70-4cc4-ab50-e8b58b2aaa7b
  :END:  

附件被保存在 data/f1/d38e9a-ff70-4cc4-ab50-e8b58b2aaa7b/.它的具体位置对
我来说并不重要–只要它保存下来了并且能够方便取得就可以了。org-mode拷贝原来文件
/tmp/x.sql 到备份目录。

对于有附件的任务,都会添加一个 ATTACH 标签,因此你可以很方便通过tag标签找到这些任务。

通过 C-c C-a o 打开一个带有附件的任务。这个命令会弹出输入要打开文件的提示,按 TAB
就可以自动补全。

ID 对于每个任务都会生成一个唯一的。

当然也可以自己定义附件存储位置,但是我并不需要这么做 – 存在默认位置就行,只要能找到就可以了。

我将我的org文件所有附件,都存储在子目录 data 中。这样就可以通过 git 来管理。
当有附件添加,我就可以直接push到远程仓库中。

截止日以及agenda可视化

截止日在生活必不可少。默认我会在agenda视图中显示30天内的任务。

下面设置可以完成这个目的:

(setq org-deadline-warning-days 30)

这样我就有足够时间在截止日前完成这个任务。

将表格导出成CSV格式

我有时会为任务添加org-mode表格,用来记录一些项目相关数据。我的客户也喜欢这种表格。
当然在org mode中,很方便将表格导出成HTML格式,但是对于希望编辑这种表格的人来说,
就不是特别方便。为解决这个问题,我会将表格导出成逗号分割(CSV)格式然后发给客户
(或者通过表格读取然后发送给可以)

org-mode可以将表格导出成TAB或者逗号分隔的格式。我设置导出默认的CSV格式脚本如下:

(setq org-table-export-default-format "orgtbl-to-csv")

我默认只导出成CSV格式,所有当导出时我只敲击RETURN来完成导出工作。

为导出下面表格,我将光标移动到表格里边然后执行 M-x org-table-export
,然后会提示输入名字以及格式,默认是CSV格式。

One Two Three 1 1 2 3 6 5 fred kpe mary 234.5 432.12 324.3

这将导出下列格式的数据文件

One,Two,Three
1,1,2
3,6,5
fred,kpe,mary
234.5,432.12,324.3

最小化Emacs Frames

我的org文件中包含各种各样的email链接,网页以及其他文件链接。下面设置控制
org-mode处理链接方法。

(setq org-link-frame-setup (quote ((vm . vm-visit-folder)
                                   (gnus . org-gnus-no-new-news)
                                   (file . find-file))))

; Use the current window for C-c ' source editing
(setq org-src-window-setup 'current-window)

我喜欢所有链接都在一个窗口打开,这样我就不需要在我的窗口中管理多个frame了。通常
我全屏工作,链接都是在这个窗口打开,这样工作方式很适合我。

我如果需要处理多个文件,我会手动通过快捷键 C-x 5 2 创建第二个frame或者用
快捷键 C-x 4 2 或者 C-x 4 3 分割窗口。当需要访问文件时,会将当前窗口内容替换
成新内容。

日志相关的

我大部分日志是通过全局 org-todo-keywords 来控制。

我的日志设置如下:

(setq org-log-done (quote time))
(setq org-log-into-drawer t)
(setq org-log-state-notes-insert-after-drawers nil)

我的 org-todo-keywords 设置如下:

(setq org-todo-keywords
      (quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
              (sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "PHONE" "MEETING"))))

当任务状态变化发生时, 日志就会被记录下来:

  • 达到 DONE 状态或者从 DONE 状态退出
  • WAITING 状态(包含备忘)或者退出 WAITING 状态

= 到 HOLD 状态

  • CANCELLED 状态(包含备忘)或者退出 CANCELLED 状态。

我将计时信息以及状态信息记录在任务 LOGBOOK 抽屉中,以保持我的任务整洁。如果一个任务在
等待状态,那么在LOGBOOK里,就会显示等待原因,LOGBOOK也会处于展开状态,用来提示信息。
在agenda视图中,只要对任务单击 SPC 就可以取得LOGBOOK信息。

限制任务计时

org-mode新引入一个非常棒的功能,就是一个任务评估时间到的时候会发送通知。我用这个功能来限制
每天某些任务执行时间。

例如,这个文档我已经花了将近2个月时间。我想尽快把它完成但是我没法只做这件事,这样
其他事情就没法做了。我想每天做点,比如限制每天只花1个小时在org-mode文档编写上。

因此我建了下面一个任务:

* NEXT Document my use of org-mode
  :LOGBOOK:...
  :PROPERTIES:
  :CLOCK_MODELINE_TOTAL: today
  :Effort:   1:00
  :END:  

这个任务有一个小时评估时间,当我开始做这个任务时候,mode-line显示如下

--:**  org-mode.org   91% (2348,73) Git:master  (Org Fly yas Font)-----[0:35/1:00 (Document my use of org-mode)]-------

我今天在这个项目中已经花了35分钟了。

我会设置一个报警声,当评估时间到了的时候就会想起星际迷航声音(是的,我是星际迷)。

(setq org-clock-sound "/usr/local/lib/tngchime.wav")

当一个小时评估时间到了,报警声就会想起,并且会弹出消息提示这个任务应该完成了。如果
我切换到其他任务,又切换回来,每次切换回来报警声都会想起来。这让我不得不做其他工作
😃

你也可以对重复任务做相应设置。默认情况下最后一次时间将会被记录下来。对于重复任务
mode-line时间显示的是最后一次计时。这方便几天后来评估自己花的时间。

习惯跟踪

John Wiegley最近在org-mode中添加了对习惯跟踪的功能支持。

我有很多习惯(有些是不好的习惯),但是我还是希望来改进并建立一些好的习惯。这就是
习惯跟踪做的事。它通过图形形式显示在agenda视图上,显示出你的习惯做的怎么样。

我有如下习惯:

  • 手洗碟子
  • 30分钟快步走
  • 打扫家庭卫生

等等。大部分习惯需要规律性完成,所以有时候需要给点压力才能这么做。为跟踪习惯是否正常
完成,需要记录状态切换日志。

习惯任务与一般的任务很像,除了它设置了特别的 属性 。该属性被设置为习惯,有个
计划 时间项,就像下面这样:

* TODO Update Org Mode Doc
  SCHEDULED: <2009-11-21 Sat .+7d/30d>
  [2009-11-14 Sat 11:45]
  :PROPERTIES:
  :STYLE: habit
  :END:  

这样习惯任务就可以和一般任务有所区别,agenda视图就会把它当做一个习惯任务来显示。
当习惯完成后,并被标记完成后,在agenda视图中就根据计划项,显示下次任务发生时间。
(.+7d).

SCHEDULED 项特殊地方是,我希望这个任务每天都被执行,或者每两天要执行。如果3天没有
标记习惯完成,那么在agenda视图中它就会显示红色,表示我很久没有执行这个习惯了。

如果习惯被忽略了,那么也不是什么世界末日的大事。你可以通过 K 键来在agenda显示或隐藏习惯
任务。

如下是我的习惯设置:

; Enable habit tracking (and a bunch of other modules)
(setq org-modules (quote (org-bbdb
                          org-bibtex
                          org-crypt
                          org-gnus
                          org-id
                          org-info
                          org-jsinfo
                          org-habit
                          org-inlinetask
                          org-irc
                          org-mew
                          org-mhe
                          org-protocol
                          org-rmail
                          org-vm
                          org-wl
                          org-w3m)))

; position the habit graph on the agenda to the right of the default
(setq org-habit-graph-column 50)

每日,我会通过 K 在agenda中关闭习惯任务显示。当关闭后,这个设置就会永久保持了,
当我的emacs后台运行几天后,习惯任务也不会自动显示在agenda视图中。为保证我每天早
上都能看到习惯执行情况,我做了如下设置,这样每天早上,习惯任务就能显示在agenda视图中。

(run-at-time "06:00" 86400 '(lambda () (setq org-habit-show-habits t)))

对于习惯只记录完成状态修改

我通常将习惯放在第一层任务 * Habits 中,并且仅当任务完成时候才会记录属性。这样
我就可以在取消一个习惯时候不会记录一个时间戳,从而让这些信息填满习惯任务中.当要
取消一个习惯,只要从agenda中移除。如果习惯没有执行,那么没法重做(例如早上6点起床)
,对于没有遵循的习惯任务,就不能标记成完成。我就会取消每日重复的习惯。

我的习惯任务就像下面一样-我习惯为每个org文件都添加习惯标题。

* Habits
  :PROPERTIES:
  :LOGGING:  DONE(!)
  :ARCHIVE:  %s_archive::* Habits
  :END:  

自动复原模式

我使用git在我的笔记本以及我的工作站之间同步org-mode文件。常规执行流程是保存当前修改,
推送到repo中,在其他系统中获取,然后我需要对我的所有org-mode文件执行revert buffer操作,
这样才能够获在buffer中显示更新的文件内容。

曾经我使用 org-revert-all-org-buffers 但是自从我发现 global-auto-revert-mode
之后,我就不再使用原来方法了。使用新的方法,只要缓冲区内容和硬盘中文件内容不一致,
缓冲区就会自动revert buffer。

这个模式对于我这种跨系统使用org-mode文件非常实用。

(global-auto-revert-mode t)

加密处理

我曾经将加密数据,比如说账户密码的保存到单独的GPG加密文件中。现在这些数据都放到
org-mode文件中,并添加特别的标签。这样加密数据就可以保留在org-mode文件中。

org-crypt 允许给任务添加一个特别的 crypt 标签,org-模式就会将这个标题中
的数据加密。当需要查看加密数据你也可以對它解密,但是只要你再次保存文件,org-mode
会重新对它加密。

如下是对加密的一些配置:

(require 'org-crypt)
; Encrypt all entries before saving
(org-crypt-use-before-save-magic)
(setq org-tags-exclude-from-inheritance (quote ("crypt")))
; GPG key to use for encryption
(setq org-crypt-key "F0B66B40")

M-x org-decrypt-entry 会弹出一个提示输入与你加密相关的密码,当正确后就用
明文来替代原来加密的密文。相反,加密并不需要输入密码-只要找到明文数据即可。

我习惯每个org文件都有个加密标题(像 * Passwords). 我阻止带 crypt 标签的任务
被继承,因为不希望加密数据中包含加密数据。 我发现当执行 M-x org-decrypt-entries
都输入解密密码(每次一个任务项执行一次)不方便。
我会对我的数据项加密直到我想查看他们为止-我根据实际需要选择解密,然后保存文件再次加密。
这样能保证数据明文出现时间尽可能短。

自动保存文件

emacs会临时保存缓冲区的内容到自动保存文件中,当你再编辑你的org缓冲区,并积累足够次数
后进行实际保存。 如果你的缓冲区中有解码的明文并还没有保存,那么这些明文将会写入这些临时自动保存
文件中,有可能会泄露些敏感信息。为防止这种情况,可以禁用自动保存功能。

个人来说,我非常喜欢自动保存功能。 99%情况我的加密项是安全的,因为他们一直都是处于加密
状态。 我通常会解密数据项后,立即通过快捷键 C-x C-s 保存文件,这样明文又会得到加密。
这就可以阻止自动保存文件中存在明文这种情况。

我的org crypt自动保存功能设置如下:

(setq org-crypt-disable-auto-save nil)

加速命令

org-mode有个非常让人兴奋的功能叫做 org-speed-commands.

加速命令允许在标题行开始处访问经常使用的命令-很像agenda中的1键命令。加速命令
可以自行配置,org-mode提供了些默认的命令。

我在默认基础上有添加了一些如下的加速键。我不怎么使用优先级,因此我重写了1,2,3
键的默认设置。我同样禁用了’c’同时加了’q’用来快速回到agenda视图并更新视图。

(setq org-use-speed-commands t)
(setq org-speed-commands-user (quote (("0" . ignore)
                                      ("1" . ignore)
                                      ("2" . ignore)
                                      ("3" . ignore)
                                      ("4" . ignore)
                                      ("5" . ignore)
                                      ("6" . ignore)
                                      ("7" . ignore)
                                      ("8" . ignore)
                                      ("9" . ignore)

                                      ("a" . ignore)
                                      ("d" . ignore)
                                      ("h" . bh/hide-other)
                                      ("i" progn
                                       (forward-char 1)
                                       (call-interactively 'org-insert-heading-respect-content))
                                      ("k" . org-kill-note-or-show-branches)
                                      ("l" . ignore)
                                      ("m" . ignore)
                                      ("q" . bh/show-org-agenda)
                                      ("r" . ignore)
                                      ("s" . org-save-all-org-buffers)
                                      ("w" . org-refile)
                                      ("x" . ignore)
                                      ("y" . ignore)
                                      ("z" . org-add-note)

                                      ("A" . ignore)
                                      ("B" . ignore)
                                      ("E" . ignore)
                                      ("F" . bh/restrict-to-file-or-follow)
                                      ("G" . ignore)
                                      ("H" . ignore)
                                      ("J" . org-clock-goto)
                                      ("K" . ignore)
                                      ("L" . ignore)
                                      ("M" . ignore)
                                      ("N" . bh/narrow-to-org-subtree)
                                      ("P" . bh/narrow-to-org-project)
                                      ("Q" . ignore)
                                      ("R" . ignore)
                                      ("S" . ignore)
                                      ("T" . bh/org-todo)
                                      ("U" . bh/narrow-up-one-org-level)
                                      ("V" . ignore)
                                      ("W" . bh/widen)
                                      ("X" . ignore)
                                      ("Y" . ignore)
                                      ("Z" . ignore))))

(defun bh/show-org-agenda ()
  (interactive)
  (if org-agenda-sticky
      (switch-to-buffer "*Org Agenda( )*")
    (switch-to-buffer "*Org Agenda*"))
  (delete-other-windows))

变量 org-speed-commands-default 设置了很多加速键。 我使用最多的键是
I O 用来计时以及 =t=修改任务状态。

J 跳到当前或者上个计时任务。

c 以及 C 被禁用了,因此当输入这些字符时,字符就会插入进去。我用 TAB 以及 S-TAB=来循环 折叠-所以我不需要 =c C. TAB 可以在任何地方工作但是 c C 只能在标题
行才能执行,有时候我还会误碰。

org 协议

Org protocol方便从其他应用中为org-mode创建捕获的备忘。
我使用这个方法来记录下我在firefox浏览过的网页。

我有个特殊捕获模板用作 org-protocol使用(设置到 w 键)。

我的org-protocol设置非常简单。它能够使用org-protocol并且像Capture Templates描述那样
创建一个捕获模板。

(require 'org-protocol)

另一部分设置是在firefox中,这样在firefox中执行 =C-c c就可以触发org-protocol,捕获我当前
在浏览网页。

保存文件时在文件末尾添加新行

下面的设置主要用在编辑yasnippets, 当我希望在同一行,扩展代码片段时。
我只用这个功能在会议备忘中,替换字符串以及初始化一些人名。我现在使用 abbrev=mode
目前不使用这个设置了。

(setq require-final-newline nil)

当我想在emacs中保存一个文件时,我希望新添加新行-这个对我所工作的项目源码非常有好处。
这些是我的现在设置:

(setq require-final-newline t)

插入不活动的时间戳并排除在导出之外

当我工作在org-mode文件时候,我会插入不活跃的时间戳。
对于记忆任务时间戳,是在记忆模板中的,但是对于正常编辑大纲时候,我希望时间戳能够自行添加。

我定义个可以运行在org-mode的hook,当任务标题创建时,自动插入不活动的时间。

可以用 f9 T 来控制标题时间戳创建开关。

(defvar bh/insert-inactive-timestamp t)

(defun bh/toggle-insert-inactive-timestamp ()
  (interactive)
  (setq bh/insert-inactive-timestamp (not bh/insert-inactive-timestamp))
  (message "Heading timestamps are %s" (if bh/insert-inactive-timestamp "ON" "OFF")))

(defun bh/insert-inactive-timestamp ()
  (interactive)
  (org-insert-time-stamp nil t t nil nil nil))

(defun bh/insert-heading-inactive-timestamp ()
  (save-excursion
    (when bh/insert-inactive-timestamp
      (org-return)
      (org-cycle)
      (bh/insert-inactive-timestamp))))

(add-hook 'org-insert-heading-hook 'bh/insert-heading-inactive-timestamp 'append)

每次我通过 M-RET 或者 =M-S-RET=创建标题时,hook调用此函数,然后就会插入不活动的时间戳

* <point here>
  [2009-11-22 Sun 18:45]  

这个会记录下任务什么时候会被创建,我觉得这个功能非常有用。

我也为这个函数定义了快捷键,因此我就可以按需要插入不活动的时间戳。

(global-set-key (kbd "<f9> t") 'bh/insert-inactive-timestamp)

为阻止时间戳被导出到文档中,我使用下面的设置。

(setq org-export-with-timestamps nil)

链接上回车

下面设置使得 RET 插入新行,而不是打开链接。这个功能我是既爱又恨。当这个功能被
发现时,我首先将其关闭,因为我想在我的链接上插入新行,但是 RET 将会打开链接令人
很烦恼。然后我重新训练自己手指,在上一行结束按回车来创建新行。

(setq org-return-follows-link t)

超时时高亮显示时钟信息

目前计时信息显示在modeline。如果有预估时间,并且我们超时执行了,我让modeline着重
显示红色,通过下面设置:

(custom-set-faces
  ;; custom-set-faces was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 '(org-mode-line-clock ((t (:foreground "red" :box (:line-width -1 :style released-button)))) t))

会议备忘

我用org-mode来做会议备忘。我使用org-mode点形式来记录会议会话。如果一个执行项
在会上决定去完成我会用句点好标记出来,并添加TODO:或者DONE:标记。

会议也是一个任务,当会议完成任务也完成。任务内容记录会议上所有细节。如果任务上提到
新任务,我会创建另一个新的TODO任务。

我使用 bh/prepare-meeting-notes 来准备会议备忘,用来发给会议参与者(用固定
宽度字体像“Courier New”). 当会议结束,备忘也就可以发送了–所以不需要花费额外的
时间来重写他们。我也不排斥HTML格式输出–内容比格式更加重要。

* TODO Sample Meeting
  - Attendees
    - [ ] Joe
    - [X] Larry
    - [X] Mary
    - [X] Fred
  - Joe is on vacation this week
  - Status Updates
    + Larry
      - did this
      - and that
      - TODO: Needs to follow up on this
    + Mary
      - got a promotion for her recent efforts
    + Fred
      - completed all his tasks 2 days early
      - needs more work
      - DONE: everything  

* TODO Sample Meeting
   - Attendees
     - [ ] Joe
     - [X] Larry
     - [X] Mary
     - [X] Fred
   - Joe is on vacation this week
   - Status Updates
     + Larry
       - did this
       - and that
>>>>>>>> TODO: Needs to follow up on this
     + Mary
       - got a promotion for her recent efforts
     + Fred
       - completed all his tasks 2 days early
       - needs more work
>>>>>>>> DONE: everything  

下面是格式函数。高亮备忘并将TABs转换成空格,高亮todo项。会议纪要同时会保存进删除缓冲区,这样就方便将内容贴到其他应用中。

(defun bh/prepare-meeting-notes ()
  "Prepare meeting notes for email
   Take selected region and convert tabs to spaces, mark TODOs with leading >>>, and copy to kill ring for pasting"
  (interactive)
  (let (prefix)
    (save-excursion
      (save-restriction
        (narrow-to-region (region-beginning) (region-end))
        (untabify (point-min) (point-max))
        (goto-char (point-min))
        (while (re-search-forward "^\\( *-\\\) \\(TODO\\|DONE\\): " (point-max) t)
          (replace-match (concat (make-string (length (match-string 1)) ?>) " " (match-string 2) ": ")))
        (goto-char (point-min))
        (kill-ring-save (point-min) (point-max))))))

修改后移除高亮

我发现当我需要在org文件中查找一些细节时候,我会通过快捷键=C-c / /= 使用使用org-occur
查找内容.下面设置保持查找结果高亮,即使内容被修改,依然高亮。这使得我可以直接修改文件而
不丢失高亮,当修改完成后,可以继续下个匹配项。 C-c C-c 移除所有的高亮。

(setq org-remove-highlights-with-change nil)

设置这个参数为t时,当修改缓冲区,高亮将不再。

我已经开始使用当修改缓冲区后自动移除高亮,因为现在使用通用的 M-x occur 在emacs
缓冲区中查找。

获取最新的org-mode帮助文档

我使用git仓库中的org-mode帮助文档,因此我设置emacs从git查找帮助文档,之后才常规查找
(过时)系统版本文档。

(add-to-list 'Info-default-directory-list "~/git/org-mode/doc")

选择将来时间吗?

默认情况下org会选择将来的某个时间。这意味着如果当前时间是5月2号,然后你输入一个
4月30号日期(2天前),org-mode将会跳转到明年的4月30号。我发现这个非常烦,因为
当我需要看看上周五发生什么我必须输入年。现在我训练我的手指,这样,如果需要查看以前
的信息我可以用快捷键 b ,所以这个问题对我来说也不是什么问题了。

自动修改点句

在开会时候我会使用点格式。跟其他的list点句一样,这样当层级超过3行,就会让读取
细节变得更难。

当修改层级时,org-mode可以自动修改list点句。

Current List Bullet Next indented list bullet + - * - 1. - 1) - A) - B) - a) - b) - A. - B. - a. - b. -
(setq org-list-demote-modify-bullet (quote (("+" . "-")
                                            ("*" . "-")
                                            ("1." . "-")
                                            ("1)" . "-")
                                            ("A)" . "-")
                                            ("B)" . "-")
                                            ("a)" . "-")
                                            ("b)" . "-")
                                            ("A." . "-")
                                            ("B." . "-")
                                            ("a." . "-")
                                            ("b." . "-"))))

删除agenda标签视图中的缩进

我不喜欢agenda视图中对匹配的标签子层级缩进显示,当我执行agenda标签查询(=F12 m=),
我只希望看到所有匹配的任务(包含子层级)。

为使得所有匹配的大纲都在agenda显示,可以设置如下变量:

(setq org-tags-match-list-sublevels t)

加固源码显示

我使用babel来在我的文档中包含源码

#+begin_src LANG
,,  ...
#+end_src

LANG代表使用的语言(ditaa, dot, sh, emacs-lisp等)这将使得在org-mode中以代码块显示,同样导出文档时也是以
代码块显示。

可以查看这个文档Git Repository synchronization 作为个例子。

持久化agenda过滤器

这是一个伟大功能!持久化agenda过滤器意味着当通过 / TAB SomeTag 查看后agenda
会记住这个过滤条件,直到你修改它。

通过如下参数可以启动持久化过滤器

(setq org-agenda-persistent-filter t)

为标记项添加标记

每个人都会碰到有些非常重要的信息,需要后续能够快速找到。

像这种备忘以及任务我添加了特殊的 :FLAGGED: 标记。这个标记有个快速按键 将会
在agenda中查找标记项。可以查看 Tags了解如何为 FLAGGED
设置 org-tag-alist

为查找标记项也非常简单,只要执行快捷键 F12 ? 就能获得。

使用compose-mail打开邮件链接

下列设置使得org-mode 可以使用compose-mail打开 mailto: 链接。

(setq org-link-mailto-program (quote (compose-mail "%a" "%s")))

org mode任务来作为邮件内容发送

可以创建基于org-mode子树内容的邮件。我通常使用 C-c M-o 来启动email消息,并从
子树获取内容,作为email邮件内容。我使用这种方式来处理重复的提醒任务,并需要发邮件
给别人。email内容已经包含在org-mode子树中,我只需要对子树执行 C-c M-o 然后
只要在发出前做稍许修改即可。

**使用smex作为 M-x ido-completion后端

:CUSTOMID: SmexAndIdo

我发现smex,并使用它作为IDO-completion 后端,当读取org-mode邮件列表后。我实际上会
执行M-x很多次,因为通过IDO补全方便。

下面是我的设置:

(add-to-list 'load-path (expand-file-name "~/.emacs.d"))
(require 'smex)
(smex-initialize)

(global-set-key (kbd "M-x") 'smex)
(global-set-key (kbd "C-x x") 'smex)
(global-set-key (kbd "M-X") 'smex-major-mode-commands)

使用emacs书签来快速导航

我开始使用emacs书签来保存位置,以方便通过标签自动返回。通常我想回到当前计时任务,这很简单-只要执行 F11
当我在查看列表,对任务设置一个书签,就可以快速跳转到该列表处理,当完成后,可以快速回到任务,然后将它的
复选框标记上。

我emacs书签相关设置如下:

;; Bookmark handling
;;
(global-set-key (kbd "<C-f6>") '(lambda () (interactive) (bookmark-set "SAVED")))
(global-set-key (kbd "<f6>") '(lambda () (interactive) (bookmark-jump "SAVED")))

当我想保存当前位置,我只需要执行快捷键 C-f6 ,当我想返回,我只需要执行 f6 .我会每次
重写这个标签来设置新位置。

使用org-mime发邮件

我现在正在使用通过mime来发送org相关邮件。我在 org-mode-hook 添加了 C-c M-o 按键绑定来从
org子树生成邮件。

从agenda中移除多个状态切换日志

我在agenda视图中通过如下视图,跳过相同任务项包含多个时间戳这种情况。

(setq org-agenda-skip-additional-timestamps-same-entry t)

这样可以移除多于的状态转换log当多个时间戳存在于一个任务项中。

在表格中舍弃老的引用格式

我通过如下设置,舍弃了表格中老的 A3/B4格式引用。

(setq org-table-use-standard-references (quote from))

使用系统设置来完成file-application选择

为使任务打开方式体验一致,我会像如下方式设置 org-file-apps .

(setq org-file-apps (quote ((auto-mode . emacs)
                            ("\\.mm\\'" . system)
                            ("\\.x?html?\\'" . system)
                            ("\\.pdf\\'" . system))))

当打开文件文件时,它使用定义在我系统 mailcap 中的项目设置,这使得我,在通过 C-c C-o
打开一些HTML链接时候有一致体验。

复制时删除ID

(setq org-clone-delete-id t)

折叠文本列表

org mode能够折叠(展开)文本列表。

(setq org-cycle-include-plain-lists t)

我发现当我的重复任务包含很多复选框子列表时,这个功能非常有用。我可以折叠起完成项,并只关注那些还没有完成的。

代码块语法高亮

在org-mode中可以显示native形式的源码。这可以高亮 C以及shell脚本源码。当我要
标记源码,我使用 =C-c '=(control-c单引号)会打开源码窗口,源码也会高亮。这个
设置也会将org-mode缓冲区的语法高亮。

插入代码块模板

在org-mode中有一些快捷键用来快速插入代码块模板。

我在org中使用example以及代码块非常多

Key Sequence Expands to < s TAB #+begin src … #+end src < e TAB #+begin example … #+end example

我添加了一个代码模板块用来从MS outlook中拷贝内容到我的org-mode任务。

下面的lisp在org-mode中将默认将字符转化成小写。

(setq org-structure-template-alist
      (quote (("s" "#+begin_src ?\n\n#+end_src" "<src lang=\"?\">\n\n</src>")
              ("e" "#+begin_example\n?\n#+end_example" "<example>\n?\n</example>")
              ("q" "#+begin_quote\n?\n#+end_quote" "<quote>\n?\n</quote>")
              ("v" "#+begin_verse\n?\n#+end_verse" "<verse>\n?\n</verse>")
              ("c" "#+begin_center\n?\n#+end_center" "<center>\n?\n</center>")
              ("l" "#+begin_latex\n?\n#+end_latex" "<literal style=\"latex\">\n?\n</literal>")
              ("L" "#+latex: " "<literal style=\"latex\">?</literal>")
              ("h" "#+begin_html\n?\n#+end_html" "<literal style=\"html\">\n?\n</literal>")
              ("H" "#+html: " "<literal style=\"html\">?</literal>")
              ("a" "#+begin_ascii\n?\n#+end_ascii")
              ("A" "#+ascii: ")
              ("i" "#+index: ?" "#+index: ?")
              ("I" "#+include %file ?" "<include file=%file markup=\"?\">"))))

NEXT状态只适用于任务

NEXT 任务只适用 tasks 并不适用 projects 。我写了个函数来处理状态改变,以及当子任务
状态变成 NEXT ,会将父任务从 NEXT 转换成 TODO ,因为父任务是项目,并不是任务。

默认折叠模式显示

启动时显示折叠模式

(setq org-startup-folded t)

我以前使用内容模式,因此可以在归档前查看子树内容,但是我的归档流程已经修改了,
因此我不需要这步了。

只允许字符类表项

下列设置添加如下的字符列表

a. item one
b. item two

(setq org-alphabetical-lists t)

为使得能正常工作,这个需要在exporter之前设置。

使用orgstruct 发邮件

orgstruct++-modeGnus 消息缓冲区开启,用以创建结构化邮件消息。

(add-hook 'message-mode-hook 'orgstruct++-mode 'append)
(add-hook 'message-mode-hook 'turn-on-auto-fill 'append)
(add-hook 'message-mode-hook 'bbdb-define-all-aliases 'append)
(add-hook 'message-mode-hook 'orgtbl-mode 'append)
(add-hook 'message-mode-hook 'turn-on-flyspell 'append)
(add-hook 'message-mode-hook
          '(lambda () (setq fill-column 72))
          'append)

使用flyspell mode减少拼写错误

flyspell-mode 默认在任何模式都开启,用来检查文档中包含拼写错误。

;; flyspell mode for spell checking everywhere
(add-hook 'org-mode-hook 'turn-on-flyspell 'append)

;; Disable keys in org-mode
;;    C-c [ 
;;    C-c ]
;;    C-c ;
;;    C-c C-x C-q  cancelling the clock (we never want this)
(add-hook 'org-mode-hook
          '(lambda ()
             ;; Undefine C-c [ and C-c ] since this breaks my
             ;; org-agenda files when directories are include It
             ;; expands the files in the directories individually
             (org-defkey org-mode-map "\C-c[" 'undefined)
             (org-defkey org-mode-map "\C-c]" 'undefined)
             (org-defkey org-mode-map "\C-c;" 'undefined)
             (org-defkey org-mode-map "\C-c\C-x\C-q" 'undefined))
          'append)

(add-hook 'org-mode-hook
          (lambda ()
            (local-set-key (kbd "C-c M-o") 'bh/mail-subtree))
          'append)

(defun bh/mail-subtree ()
  (interactive)
  (org-mark-subtree)
  (org-mime-subtree))

维持源码块对齐

我不会在源码块中维持对齐,因为这样看起来不是很好。唯一一个我觉得需要保留对齐是在文件中
的TABs需要保留(例如 Makefiles)。我不怎么在org-mode编辑这种文件,所以我默认将对齐关闭。

我修改了默认代码块对齐方式,这样就不会从文本开始对齐。这允许立即编辑源码而不需要执行
C-c ' ,因此代码看上去就正确了。

(setq org-src-preserve-indentation nil)
(setq org-edit-src-content-indentation 0)

阻止修改不可见文档

当光标在折叠去,下面设置阻止不小心修改隐藏文档。这种情况会发生在当光标在标题体内,然后通过
执行 S-TAB 折叠org文件。

我发现不可见编辑(撤销)很难处理,因此现在我禁止编辑不可见文本。 C-c C-r (org-reveal) 将会
显示光标,当光标折叠在不可见区域时,这样就可以编辑了。

(setq org-catch-invisible-edits 'error)

使用utf-8作为默认文字编码

我对我所有的org文件使用 utf-8 作为默认编码格式。

(setq org-export-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(set-charset-priority 'unicode)
(setq default-process-coding-system '(utf-8-unix . utf-8-unix))

使用小时来计时

默认计时时长变成以24小时每日计时。工作中我通常认为一天有6小时来工作(其它时间用在
开会以及其他事情上)所以以日计时对我来说没有什么意义。

下列设置修改计时为以小时以及分钟来计时(C-c C-x C-d )

(setq org-time-clocksum-format
      '(:hours "%d" :require-hours t :minutes ":%02d" :require-minutes t))

创建唯一ID为任务做链接

当设置下面设置后,使用 C-c l, 就会为标题在 PROPERTY 属性抽屉中,创建唯一ID.
当有了唯一ID,我即使将任务在文档中移动,链接到该任务的链接依然有效。

(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值