* todo-mode.el: Offer to convert legacy file. Update commentary.

(todo-show): If a legacy file is found and no new todo file
exists, offer to convert it and show it.  Restore autoload cookie.
(todo-convert-legacy-files): Delete unused local variable.
If todo-directory doesn't exist, create it before writing to file.
Check format of converted files.  Prompt to show converted todo file.
This commit is contained in:
Stephen Berman 2013-06-21 15:23:51 +02:00
parent e2ff2f69b9
commit 03e6d46963
2 changed files with 153 additions and 113 deletions

View file

@ -1,3 +1,12 @@
2013-06-21 Stephen Berman <stephen.berman@gmx.net>
* todo-mode.el: Offer to convert legacy file. Update commentary.
(todo-show): If a legacy file is found and no new todo file
exists, offer to convert it and show it. Restore autoload cookie.
(todo-convert-legacy-files): Delete unused local variable.
If todo-directory doesn't exist, create it before writing to file.
Check format of converted files. Prompt to show converted todo file.
2013-06-19 Stephen Berman <stephen.berman@gmx.net>
* todo-mode.el (todo-convert-legacy-files): Eliminate last change,

View file

@ -64,9 +64,13 @@
;; This package is a new version of Oliver Seidel's todo-mode.el.
;; While it retains the same basic organization and handling of todo
;; lists and the basic UI, it significantly extends these and adds
;; many features. This also required making changes to the internals,
;; including the file format. To convert files in the old format to
;; the new format, use the command `todo-convert-legacy-files'.
;; many features. This required also making changes to the internals,
;; including the file format. If you have a todo file in old format,
;; then the first time you invoke `todo-show' (i.e., before you have
;; created any todo file in the current format), it will ask you
;; whether to convert that file and show it. If you choose not to
;; convert the old-style file at this time, you can do so later by
;; calling the command `todo-convert-legacy-files'.
;;; Code:
@ -556,7 +560,7 @@ less than or equal the category's top priority setting."
:group 'todo-faces)
;; -----------------------------------------------------------------------------
;;; Entering and exiting Todo
;;; Entering and exiting
;; -----------------------------------------------------------------------------
(defcustom todo-visit-files-commands (list 'find-file 'dired-find-file)
@ -625,23 +629,27 @@ Otherwise, `todo-show' always visits `todo-default-todo-file'."
:type 'boolean
:group 'todo)
;;;###autoload
(defun todo-show (&optional solicit-file)
"Visit a Todo file and display one of its categories.
"Visit a todo file and display one of its categories.
When invoked in Todo mode, prompt for which todo file to visit.
When invoked outside of Todo mode with non-nil prefix argument
SOLICIT-FILE prompt for which todo file to visit; otherwise visit
`todo-default-todo-file'. Subsequent invocations from outside
of Todo mode revisit this file or, with option
`todo-show-current-file' non-nil (the default), whichever Todo
`todo-show-current-file' non-nil (the default), whichever todo
file was last visited.
Calling this command before any Todo file exists prompts for a
file name and an initial category (defaulting to
`todo-initial-file' and `todo-initial-category'), creates both
of these, visits the file and displays the category, and if
option `todo-add-item-if-new-category' is non-nil (the default),
prompts for the first item.
If you call this command before you have created any todo file in
the current format, and you have an todo file in old format, it
will ask you whether to convert that file and show it.
Otherwise, calling this command before any Todo file exists
prompts for a file name and an initial category (defaulting to
`todo-initial-file' and `todo-initial-category'), creates both of
these, visits the file and displays the category, and if option
`todo-add-item-if-new-category' is non-nil (the default), prompts
for the first item.
The first invocation of this command on an existing Todo file
interacts with the option `todo-show-first': if its value is
@ -661,104 +669,116 @@ and done items are always shown on visiting a category.
Invoking this command in Todo Archive mode visits the
corresponding Todo file, displaying the corresponding category."
(interactive "P")
(let* ((cat)
(show-first todo-show-first)
(file (cond ((or solicit-file
(and (called-interactively-p 'any)
(memq major-mode '(todo-mode
todo-archive-mode
todo-filtered-items-mode))))
(if (funcall todo-files-function)
(todo-read-file-name "Choose a Todo file to visit: "
nil t)
(user-error "There are no Todo files")))
((and (eq major-mode 'todo-archive-mode)
;; Called noninteractively via todo-quit
;; to jump to corresponding category in
;; todo file.
(not (called-interactively-p 'any)))
(setq cat (todo-current-category))
(concat (file-name-sans-extension
todo-current-todo-file) ".todo"))
(t
(or todo-current-todo-file
(and todo-show-current-file
todo-global-current-todo-file)
(todo-absolute-file-name todo-default-todo-file)
(todo-add-file)))))
add-item first-file)
(catch 'shown
;; If there is a legacy todo file but no todo file in the current
;; format, offer to convert the legacy file and show it.
(unless todo-default-todo-file
;; We just initialized the first todo file, so make it the default.
(setq todo-default-todo-file (todo-short-file-name file)
first-file t)
(todo-reevaluate-default-file-defcustom))
(unless (member file todo-visited)
;; Can't setq t-c-t-f here, otherwise wrong file shown when
;; todo-show is called from todo-show-categories-table.
(let ((todo-current-todo-file file))
(cond ((eq todo-show-first 'table)
(todo-show-categories-table))
((memq todo-show-first '(top diary regexp))
(let* ((shortf (todo-short-file-name file))
(fi-file (todo-absolute-file-name
shortf todo-show-first)))
(when (eq todo-show-first 'regexp)
(let ((rxfiles (directory-files todo-directory t
".*\\.todr$" t)))
(when (and rxfiles (> (length rxfiles) 1))
(let ((rxf (mapcar 'todo-short-file-name rxfiles)))
(setq fi-file (todo-absolute-file-name
(completing-read
"Choose a regexp items file: "
rxf) 'regexp))))))
(if (file-exists-p fi-file)
(set-window-buffer
(selected-window)
(set-buffer (find-file-noselect fi-file 'nowarn)))
(message "There is no %s file for %s"
(cond ((eq todo-show-first 'top)
"top priorities")
((eq todo-show-first 'diary)
"diary items")
((eq todo-show-first 'regexp)
"regexp items"))
shortf)
(setq todo-show-first 'first)))))))
(when (or (member file todo-visited)
(eq todo-show-first 'first))
(set-window-buffer (selected-window)
(set-buffer (find-file-noselect file 'nowarn)))
;; When quitting archive file, show corresponding category in
;; Todo file, if it exists.
(when (assoc cat todo-categories)
(setq todo-category-number (todo-category-number cat)))
;; If this is a new Todo file, add its first category.
(when (zerop (buffer-size))
(let (cat-added)
(unwind-protect
(setq todo-category-number
(todo-add-category todo-current-todo-file "")
add-item todo-add-item-if-new-category
cat-added t)
(if cat-added
;; If the category was added, save the file now, so we
;; don't risk having an empty todo file, which would
;; signal an error if we tried to visit it later,
;; since doing that looks for category boundaries.
(save-buffer 0)
;; If user cancels before adding the category, clean up
;; and exit, so we have a fresh slate the next time.
(delete-file file)
(setq todo-files (delete file todo-files))
(when first-file
(setq todo-default-todo-file nil
todo-current-todo-file nil))
(kill-buffer)
(keyboard-quit)))))
(save-excursion (todo-category-select))
(when add-item (todo-basic-insert-item)))
(setq todo-show-first show-first)
(add-to-list 'todo-visited file)))
(let ((legacy-todo-file (if (boundp 'todo-file-do)
todo-file-do
(locate-user-emacs-file "todo-do" ".todo-do"))))
(when (and (file-exists-p legacy-todo-file)
(y-or-n-p (concat "Do you want to convert a copy of your "
"old todo file to the new format? ")))
(when (todo-convert-legacy-files)
(throw 'shown nil)))))
(let* ((cat)
(show-first todo-show-first)
(file (cond ((or solicit-file
(and (called-interactively-p 'any)
(memq major-mode '(todo-mode
todo-archive-mode
todo-filtered-items-mode))))
(if (funcall todo-files-function)
(todo-read-file-name "Choose a Todo file to visit: "
nil t)
(user-error "There are no Todo files")))
((and (eq major-mode 'todo-archive-mode)
;; Called noninteractively via todo-quit
;; to jump to corresponding category in
;; todo file.
(not (called-interactively-p 'any)))
(setq cat (todo-current-category))
(concat (file-name-sans-extension
todo-current-todo-file) ".todo"))
(t
(or todo-current-todo-file
(and todo-show-current-file
todo-global-current-todo-file)
(todo-absolute-file-name todo-default-todo-file)
(todo-add-file)))))
add-item first-file)
(unless todo-default-todo-file
;; We just initialized the first todo file, so make it the default.
(setq todo-default-todo-file (todo-short-file-name file)
first-file t)
(todo-reevaluate-default-file-defcustom))
(unless (member file todo-visited)
;; Can't setq t-c-t-f here, otherwise wrong file shown when
;; todo-show is called from todo-show-categories-table.
(let ((todo-current-todo-file file))
(cond ((eq todo-show-first 'table)
(todo-show-categories-table))
((memq todo-show-first '(top diary regexp))
(let* ((shortf (todo-short-file-name file))
(fi-file (todo-absolute-file-name
shortf todo-show-first)))
(when (eq todo-show-first 'regexp)
(let ((rxfiles (directory-files todo-directory t
".*\\.todr$" t)))
(when (and rxfiles (> (length rxfiles) 1))
(let ((rxf (mapcar 'todo-short-file-name rxfiles)))
(setq fi-file (todo-absolute-file-name
(completing-read
"Choose a regexp items file: "
rxf) 'regexp))))))
(if (file-exists-p fi-file)
(set-window-buffer
(selected-window)
(set-buffer (find-file-noselect fi-file 'nowarn)))
(message "There is no %s file for %s"
(cond ((eq todo-show-first 'top)
"top priorities")
((eq todo-show-first 'diary)
"diary items")
((eq todo-show-first 'regexp)
"regexp items"))
shortf)
(setq todo-show-first 'first)))))))
(when (or (member file todo-visited)
(eq todo-show-first 'first))
(set-window-buffer (selected-window)
(set-buffer (find-file-noselect file 'nowarn)))
;; When quitting archive file, show corresponding category in
;; Todo file, if it exists.
(when (assoc cat todo-categories)
(setq todo-category-number (todo-category-number cat)))
;; If this is a new Todo file, add its first category.
(when (zerop (buffer-size))
(let (cat-added)
(unwind-protect
(setq todo-category-number
(todo-add-category todo-current-todo-file "")
add-item todo-add-item-if-new-category
cat-added t)
(if cat-added
;; If the category was added, save the file now, so we
;; don't risk having an empty todo file, which would
;; signal an error if we tried to visit it later,
;; since doing that looks for category boundaries.
(save-buffer 0)
;; If user cancels before adding the category, clean up
;; and exit, so we have a fresh slate the next time.
(delete-file file)
(setq todo-files (delete file todo-files))
(when first-file
(setq todo-default-todo-file nil
todo-current-todo-file nil))
(kill-buffer)
(keyboard-quit)))))
(save-excursion (todo-category-select))
(when add-item (todo-basic-insert-item)))
(setq todo-show-first show-first)
(add-to-list 'todo-visited file))))
(defun todo-save ()
"Save the current Todo file."
@ -4496,7 +4516,7 @@ name in `todo-directory'. See also the documentation string of
(todo-initials-tem (and (boundp 'todo-initials) todo-initials))
(todo-entry-prefix-function-tem (and (boundp 'todo-entry-prefix-function)
todo-entry-prefix-function))
todo-categories-tem todo-prefix-tem)
todo-prefix-tem)
;; Convert `todo-file-do'.
(if (not (file-exists-p todo-file-do-tem))
(message "No legacy Todo file exists")
@ -4539,11 +4559,14 @@ name in `todo-directory'. See also the documentation string of
(format "Save file as (default \"%s\"): " default)
nil nil default)
".todo"))
(unless (file-exists-p todo-directory)
(make-directory todo-directory))
(write-region (point-min) (point-max) file nil 'nomessage nil t))
(with-temp-buffer
(insert-file-contents file)
(let ((todo-categories (todo-make-categories-list t)))
(todo-update-categories-sexp))
(todo-update-categories-sexp)
(todo-check-format))
(write-region (point-min) (point-max) file nil 'nomessage))
(setq todo-files (funcall todo-files-function))
;; Convert `todo-file-done'.
@ -4626,7 +4649,8 @@ name in `todo-directory'. See also the documentation string of
(with-temp-buffer
(insert-file-contents file)
(let* ((todo-categories (todo-make-categories-list t)))
(todo-update-categories-sexp))
(todo-update-categories-sexp)
(todo-check-format))
(write-region (point-min) (point-max) file nil 'nomessage)
(setq archive-sexp (read (buffer-substring-no-properties
(line-beginning-position)
@ -4648,7 +4672,14 @@ name in `todo-directory'. See also the documentation string of
(write-region (point-min) (point-max) file nil 'nomessage))
(setq todo-archives (funcall todo-files-function t)))
(todo-reevaluate-filelist-defcustoms)
(message "Format conversion done.")))))
(when (y-or-n-p (concat "Format conversion done; do you want to "
"visit the converted file now? "))
(setq todo-current-todo-file file)
(unless todo-default-todo-file
;; We just initialized the first todo file, so make it the
;; default now to avoid an infinite recursion with todo-show.
(setq todo-default-todo-file (todo-short-file-name file)))
(todo-show))))))
;; -----------------------------------------------------------------------------
;;; Utility functions for Todo files, categories and items