org-mode+MobileOrg(+Dropbox)でGTD始めました。オレオレ改造をごっそり公開

replace-matchのfixedcaseをtにしました
org-mode自体の説明は面倒なので割愛。
GTDへの応用についても面倒なので気が向いたら書きます。

今回は、処理が気に入らない部分に対応したものをちょこっと公開してみます。

※Emacs23.3.1、org-mode7.6、MobileOrg1.5.2です
※元ファイルをいじるのは嫌でアドバイスしまくりなので冗長になっている部分もあるかも。

MobileOrgのOutlinesでファイル名の代わりにカテゴリ名を表示する

各ファイルの#+CATEGORYを見てindex.orgに反映するようにしてみました。※1ファイル1カテゴリにしてます

(defadvice org-mobile-create-index-file (after org-mobile-create-index-file-apply-category activate)
  "#+CATEGORYをMobileOrgに反映する"
  (let ((index (expand-file-name org-mobile-index-file org-mobile-directory)))
    (while (get-file-buffer index) (kill-buffer (get-file-buffer index)))
    (with-current-buffer (find-file-noselect index)
      (goto-char (point-min))
      (while (re-search-forward "\\* \\[\\[file:\\(.*\\)\\]\\[\\1\\]\\]" nil t)
        (let ((category (with-current-buffer (find-file-noselect (expand-file-name (match-string-no-properties 1) org-directory))
                          (goto-char (point-min))
                          (save-match-data
                            (when (re-search-forward "^#\\+CATEGORY: \\(.*\\)" nil t)
                              (match-string-no-properties 1)))
                          )))
          (when category (replace-match (concat "* [[file:\\1][" category "]]") t))
          ))
      (save-buffer)
      (dolist (entry org-mobile-checksum-files)
        (when (string= org-mobile-index-file (car entry))
          (setcdr entry (md5 (current-buffer)))
          ))
      (kill-buffer)
      )
    ))

MobileOrgのAgenda Viewsがごちゃごちゃして見難いのでタイトルだけにする

(defadvice org-mobile-create-sumo-agenda (after org-mobile-create-sumo-agenda-fixup activate)
  "MobileOrgのAgenda Viewsを見やすくする"
  (let ((agendas (expand-file-name "agendas.org" org-mobile-directory)))
    (while (get-file-buffer agendas) (kill-buffer (get-file-buffer agendas)))
    (find-file agendas)
    (goto-char (point-min))
    (while (re-search-forward "\\* .*<after>KEYS=. TITLE: \\(.*\\)</after>\\(\n+\\)" nil t)
      (if (save-match-data (looking-at "\\* "))
          (replace-match "" t)
        (replace-match "* \\1\\2" t))
      )
    (save-buffer)
    (dolist (entry org-mobile-checksum-files)
      (when (string= "agendas.org" (car entry))
        (setcdr entry (md5 (current-buffer)))
        ))
    ))

手順を間違えると同期ミス(コンフリクト)を起こしやすいので、それなりに自動で同期する

同期を忘れたままRefileするとコンフリクトが起きやすい。多分。

  • 実際にpullするのはmobileorg.orgに何か入っている場合のみ
  • pullしたら勝手にpushする
  • push側でコンフリクトをチェックし、コンフリクトが残っている間はpushさせない
(defadvice org-mobile-push (around org-mobile-push-safe activate)
  "もしコンフリクトがあれば一覧を表示する。
そうで無ければ、全ファイルをTODO順にソートし、余分な入力日時を除去し、プッシュする。"
  (when (org-occur-in-agenda-files "^\\*\\* End of edit$")
    (dolist (file (org-agenda-files))
      (with-current-buffer (find-file-noselect file)
        (let ((before (buffer-substring-no-properties (point-min) (point-max))))
          (goto-char (point-min))
          (while (re-search-forward "^\\[[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} . [0-9]\\{2\\}:[0-9]\\{2\\}\\]\n" nil t)
            (replace-match "" t))
          (condition-case nil
              (org-sort-entries nil ?o)
            (error nil))
          (if (string= before (buffer-substring-no-properties (point-min) (point-max)))
              (set-buffer-modified-p nil)
            (save-buffer))
          )))
    ad-do-it
    (org-export-icalendar-combine-agenda-files)
    )
  )

(defadvice org-mobile-pull (around org-mobile-pull-and-push activate)
  "mobileorg.orgに何かデータが入っていれば、プル&プッシュする。"
  (when (<= 2 (nth 7 (file-attributes (expand-file-name org-mobile-capture-file org-mobile-directory))))
    ad-do-it
    (org-mobile-push)
    )
  )

これらをタイマーでブン回す。Emacs(とDropbox)を立ち上げておけば、MobileOrgを同期したら勝手に取り込まれて勝手にEmacs側で同期が走るので、しばらくしてからもう一度MobileOrgを同期するとすぐ反映されるのが便利。同期忘れない。

;; ボスケテ〜
(defun run-with-idle-timer-interval (secs repeat interval function &rest args)
  "run-with-idle-timerとほぼ同じだが、IDLE中は継続してINTERVAL秒毎に実行される点が異なる。"
  (lexical-let* ((interval-timer nil)
                 (wakeup-timer nil)
                 (last-idle-time nil)
                 (interval interval)
                 (function function)
                 (args args)
                 (interval-function #'(lambda ()
                                        (cond ((not (memq wakeup-timer timer-idle-list)) (cancel-timer interval-timer))
                                              ((and last-idle-time (time-less-p (current-idle-time) last-idle-time)) (cancel-timer interval-timer))
                                              (t (apply function args))
                                              )
                                        (setq last-idle-time (current-idle-time))
                                        ))
                 (wakeup-function #'(lambda ()
                                      (if (memq interval-timer timer-list) (cancel-timer interval-timer))
                                      (setq last-idle-time nil)
                                      (setq interval-timer (run-at-time 0 interval interval-function))
                                      ))
                 )
    (setq wakeup-timer (run-with-idle-timer secs repeat wakeup-function))
    ))
(setq org-mobile-pull-timer (run-with-idle-timer-interval 10 t 30 #'(lambda () (org-mobile-pull))))

終了時には全orgファイルの更新時刻をチェックして、更新があったら自動的にpushする。

TODO コンフリクトがあったときどうするか…

(add-hook 'kill-emacs-hook
          #'(lambda ()
              ;; 更新されたファイルがあったらプッシュする
              (let ((csfile (expand-file-name "checksums.dat" org-mobile-directory))
                    (files (org-agenda-files))
                    found)
                (while (and files (not found))
                  (setq found (file-newer-than-file-p (car files) csfile))
                  (setq files (cdr files)))
                (when found
                  (org-mobile-push))
                )
              ))

疲れたよママン…