todo管理もどき(修正)

todo管理もどき - Nosの日記で書いたつもりがなんかソースがちゃんと載せられてなかったし、あの後微妙につぎはぎしたので改めて。

とりあえずtodoのファイルに1行1todo形式で延々とtodoを記録しておくというもの。
C-x m vで↓こんな感じのtodoファイルが開く*1ので、Ctrl+上下で入れ替えたり、{キーでtodoを追加したり。{ }のところでenterするとhowmのaction-lockでマークが切り替わる。

[2010-08-27] Fri
{ } 買い物
{*} ゼミ発表
[2010-08-28] Sat
[2010-08-30] Mon
{ } 書類だしてくる
[2010-08-31] Tue
[2010-09-01] Wed
{ } 代数XCレポ提出

最終行で下にスクロールしようとすると↓みたく勝手に日付を継ぎ足していくようにしてみたところちょっと便利:

[2010-08-27] Fri
{ } 買い物
{*} ゼミ発表
[2010-08-28] Sat
[2010-08-30] Mon
{ } 書類だしてくる
[2010-08-31] Tue
[2010-09-01] Wed
{ } 代数XCレポ提出
[2010-09-02] Thu
[2010-09-03] Fri
[2010-09-04] Sat
[2010-09-05] Sun
[2010-09-06] Mon
[2010-09-07] Tue
[2010-09-08] Wed

依存:

以下2つを.xyzzy

(defvar *money-todo-logfile* "c:/docs/howm/etc/todo.txt") ;適当に
(defun money-todo-throw (todo)
  "todoファイルに登録"
  (interactive "swhat? : ")
  (save-excursion
    (let ((days-to-sink 7))
      (find-file *money-todo-logfile*)
      (unless (eql (get-buffer-file-name (selected-buffer)) *money-todo-logfile*)
	(end-of-buffer))
      (unless (bolp) (newline))
      (insert (concat (format-date-string "{ }")  " " todo))
      (newline))))

(defun money-todo-view ()
  (interactive)
  (select-pseudo-frame (or (find-pseudo-frame "*todo*")
			   (new-pseudo-frame "*todo*"))) ;別フレームで開く
  (pop-to-buffer (ed::find-file-internal *money-todo-logfile*))
  (widen)
  (text-mode)			; なんかelisp-lib::howm-after-saveが*after-save-buffer-hook*に(howm-mode t)のたびに登録されて重くなるので一旦まっさらに
  (howm-mode t)
  (line-shift-mode t)
  (money-todo-mode t)
  (goto-char (point-min))
  (scan-buffer "{ }")
;  (when (match-beginning 0) (goto-char (match-beginning 0)))
  (scan-buffer "^\\[[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\\]" :regexp t :reverse t)
  (when (match-beginning 0)
    (narrow-to-region (match-beginning 0) (point-max))
    (recenter 1)
    (next-line)))

(defun money-todo-latest-date ()
  "現在のバッファ中の[yyyy-mm-dd]形式の日付のうち末尾にあるものをuniversal-timeとして取得。なければnil。"
  (save-excursion
    (save-restriction
      (widen)
      (end-of-buffer)
      (scan-buffer "^\\[\\([0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)-\\([0-9][0-9]\\)\\]" :regexp t :reverse t)
      (if (match-beginning 0)
	  (let
	      ((y (parse-integer (match-string 1)))
	       (m (parse-integer (match-string 2)))
	       (d (parse-integer (match-string 3))))
	    (encode-universal-time 0 0 0 d m y))
	nil))))


(defun money-todo-insert-dates (n)
  (interactive "nhow many days: ")
  (save-excursion
    (save-restriction
      (widen)
      (let ((time (money-todo-latest-date)))
	(when (> (- (get-universal-time) time) (* 60 60 24)) (setq time (- (get-universal-time) (* 60 60 24))))
	(end-of-buffer)
	(dotimes (i n)
	  (insert (format-date-string "[%Y-%m-%d] %a\n" (+ time (* 60 60 24 (1+ i))))))
	))))

(defun money-todo-next-line-add-newdates (&optional (n 1))
  "下に移動、バッファの末尾では日付を継ぎ足す"
  (interactive "p")
  (when (eobp) (money-todo-insert-dates n))
  (next-line n))

(defun money-todo-previous-line-delete-newdates (&optional (n 1))
  "上に移動、そこから下に日付しかなければそれらを削除。money-todo-next-line-add-newdatesの逆"
  (interactive "p")
  (let ((bol (bolp)))
    (previous-line n)
    (save-excursion
      (save-restriction
	(goto-bol)
	(narrow-to-region (point) (point-max))
	(end-of-buffer)
	(scan-buffer "{" :reverse t)
	(if (match-beginning 0)
	    (progn
	      (goto-char (match-beginning 0))
	      (next-line))
	  (progn
	    (goto-char (point-min))
	    (unless bol (next-line))))
	  (delete-region (point) (point-max))))))

(defvar-local *money-todo-mode* nil)
(defvar *money-todo-mode-map* nil)
(unless *money-todo-mode-map*
  (setq *money-todo-mode-map* (make-sparse-keymap))
  (define-key *money-todo-mode-map* #\C-n 'money-todo-next-line-add-newdates)
  (define-key *money-todo-mode-map* #\Down 'money-todo-next-line-add-newdates)
  (define-key *money-todo-mode-map* #\C-p 'money-todo-previous-line-delete-newdates)
  (define-key *money-todo-mode-map* #\Up  'money-todo-previous-line-delete-newdates)
  (define-key *money-todo-mode-map* #\{   'money-todo-throw)
  (define-key *money-todo-mode-map* #\j 'money-todo-next-line-add-newdates)
  (define-key *money-todo-mode-map* #\k 'money-todo-previous-line-delete-newdates)
  )

(defun money-todo-mode (&optional (arg nil sv))
  (interactive "p")
  (ed::toggle-mode '*money-todo-mode* arg sv)
  (update-mode-line t)
  (if *money-todo-mode*
      (set-minor-mode-map *money-todo-mode-map*)
    (unset-minor-mode-map *money-todo-mode-map*))
  t)

(pushnew
 '(*money-todo-mode* . "MTodo") *minor-mode-alist* :key #'car)


(define-key money-map #\t 'money-todo-throw)
(define-key money-map #\v 'money-todo-view)

*1:最古のtodoが一番上にくるようにnarrowingする