aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Berman2013-12-20 18:21:12 +0100
committerStephen Berman2013-12-20 18:21:12 +0100
commitf3a66082f986c33df1c72b0ab2b77195cdd8b435 (patch)
tree3ec14c9da30b1d0aacd5a1954116e4ea23804630
parent2f99433b944d602c382bfa87c8e3e27b1eaaed3b (diff)
downloademacs-f3a66082f986c33df1c72b0ab2b77195cdd8b435.tar.gz
emacs-f3a66082f986c33df1c72b0ab2b77195cdd8b435.zip
New implementation of Todo item insertion commands and key bindings.
* calendar/todo-mode.el: New implementation of item insertion commands and key bindings. (todo-key-prompt): New face. (todo-insert-item): New command. (todo-insert-item--parameters): New defconst, replacing defvar todo-insertion-commands-args-genlist. (todo-insert-item--param-key-alist): New defconst, replacing defvar todo-insertion-commands-arg-key-list. (todo-insert-item--keyof, todo-insert-item--this-key): New defsubsts. (todo-insert-item--argsleft, todo-insert-item--apply-args) (todo-insert-item--next-param): New functions. (todo-insert-item--args, todo-insert-item--argleft) (todo-insert-item--argsleft, todo-insert-item--newargsleft): New variables. (todo-key-bindings-t): Change binding of "i" from todo-insertion-map to todo-insert-item. (todo-powerset, todo-gen-arglists, todo-insertion-commands-args) (todo-insertion-command-name, todo-insertion-commands-names) (todo-define-insertion-command, todo-insertion-commands) (todo-insertion-key-bindings, todo-insertion-map): Remove.
-rw-r--r--lisp/ChangeLog23
-rw-r--r--lisp/calendar/todo-mode.el330
2 files changed, 220 insertions, 133 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index e367749fb39..3286c90caed 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,5 +1,28 @@
12013-12-20 Stephen Berman <stephen.berman@gmx.net> 12013-12-20 Stephen Berman <stephen.berman@gmx.net>
2 2
3 * calendar/todo-mode.el: New implementation of item insertion
4 commands and key bindings.
5 (todo-key-prompt): New face.
6 (todo-insert-item): New command.
7 (todo-insert-item--parameters): New defconst, replacing defvar
8 todo-insertion-commands-args-genlist.
9 (todo-insert-item--param-key-alist): New defconst, replacing
10 defvar todo-insertion-commands-arg-key-list.
11 (todo-insert-item--keyof, todo-insert-item--this-key): New defsubsts.
12 (todo-insert-item--argsleft, todo-insert-item--apply-args)
13 (todo-insert-item--next-param): New functions.
14 (todo-insert-item--args, todo-insert-item--argleft)
15 (todo-insert-item--argsleft, todo-insert-item--newargsleft):
16 New variables.
17 (todo-key-bindings-t): Change binding of "i" from
18 todo-insertion-map to todo-insert-item.
19 (todo-powerset, todo-gen-arglists, todo-insertion-commands-args)
20 (todo-insertion-command-name, todo-insertion-commands-names)
21 (todo-define-insertion-command, todo-insertion-commands)
22 (todo-insertion-key-bindings, todo-insertion-map): Remove.
23
242013-12-20 Stephen Berman <stephen.berman@gmx.net>
25
3 * calendar/todo-mode.el: Bug fixes and new features (bug#15225). 26 * calendar/todo-mode.el: Bug fixes and new features (bug#15225).
4 (todo-toggle-item-highlighting): Use eval-and-compile instead of 27 (todo-toggle-item-highlighting): Use eval-and-compile instead of
5 eval-when-compile. 28 eval-when-compile.
diff --git a/lisp/calendar/todo-mode.el b/lisp/calendar/todo-mode.el
index 3dcb305f05a..055c97972a8 100644
--- a/lisp/calendar/todo-mode.el
+++ b/lisp/calendar/todo-mode.el
@@ -330,6 +330,11 @@ shown in the Fancy Diary display."
330;;; Faces 330;;; Faces
331;; ----------------------------------------------------------------------------- 331;; -----------------------------------------------------------------------------
332 332
333(defface todo-key-prompt
334 '((t (:weight bold)))
335 "Face for making keys in item insertion prompt stand out."
336 :group 'todo-faces)
337
333(defface todo-mark 338(defface todo-mark
334 ;; '((t :inherit font-lock-warning-face)) 339 ;; '((t :inherit font-lock-warning-face))
335 '((((class color) 340 '((((class color)
@@ -1743,6 +1748,30 @@ marking of the next N items."
1743(defvar todo-date-from-calendar nil 1748(defvar todo-date-from-calendar nil
1744 "Helper variable for setting item date from the Emacs Calendar.") 1749 "Helper variable for setting item date from the Emacs Calendar.")
1745 1750
1751(defvar todo-insert-item--keys-so-far)
1752(defvar todo-insert-item--parameters)
1753
1754(defun todo-insert-item (&optional arg)
1755 "Insert a new todo item into a category.
1756
1757With no prefix argument ARG, add the item to the current
1758category; with one prefix argument (`C-u'), prompt for a category
1759from the current todo file; with two prefix arguments (`C-u
1760C-u'), first prompt for a todo file, then a category in that
1761file. If a non-existing category is entered, ask whether to add
1762it to the todo file; if answered affirmatively, add the category
1763and insert the item there.
1764
1765There are a number of item insertion parameters which can be
1766combined by entering specific keys to produce different insertion
1767commands. After entering each key, a message shows which have
1768already been entered and which remain available. See
1769`todo-basic-insert-item' for details of the parameters and their
1770effects."
1771 (interactive "P")
1772 (setq todo-insert-item--keys-so-far "i")
1773 (todo-insert-item--next-param nil (list arg) todo-insert-item--parameters))
1774
1746(defun todo-basic-insert-item (&optional arg diary nonmarking date-type time 1775(defun todo-basic-insert-item (&optional arg diary nonmarking date-type time
1747 region-or-here) 1776 region-or-here)
1748 "Insert a new todo item into a category. 1777 "Insert a new todo item into a category.
@@ -5425,131 +5454,173 @@ of each other."
5425;;; Utilities for generating item insertion commands and key bindings 5454;;; Utilities for generating item insertion commands and key bindings
5426;; ----------------------------------------------------------------------------- 5455;; -----------------------------------------------------------------------------
5427 5456
5428;; Wolfgang Jenkner posted this powerset definition to emacs-devel 5457;; Thanks to Stefan Monnier for suggesting dynamically generating item
5429;; (http://lists.gnu.org/archive/html/emacs-devel/2013-06/msg00423.html) 5458;; insertion commands and their key bindings, and offering an elegant
5430;; and kindly gave me permission to use it. 5459;; implementation, which, however, relies on lexical scoping and so
5431 5460;; cannot be used here until the Calendar code used by todo-mode.el is
5432(defun todo-powerset (list) 5461;; converted to lexical binding. Hence, the following implementation
5433 "Return the powerset of LIST." 5462;; uses dynamic binding.
5434 (let ((powerset (list nil))) 5463
5435 (dolist (elt list (mapcar 'reverse powerset)) 5464(defconst todo-insert-item--parameters
5436 (nconc powerset (mapcar (apply-partially 'cons elt) powerset))))) 5465 '((default copy) diary nonmarking (calendar date dayname) time (here region))
5437 5466 "List of all item insertion parameters.
5438(defun todo-gen-arglists (arglist) 5467Passed by `todo-insert-item' to `todo-insert-item--next-param' to
5439 "Return list of lists of non-nil atoms produced from ARGLIST. 5468dynamically create item insertion commands.")
5440The elements of ARGLIST may be atoms or lists." 5469
5441 (let (arglists) 5470(defconst todo-insert-item--param-key-alist
5442 (while arglist 5471 '((default . "i")
5443 (let ((arg (pop arglist))) 5472 (copy . "p")
5444 (cond ((symbolp arg) 5473 (diary . "y")
5445 (setq arglists (if arglists 5474 (nonmarking . "k")
5446 (mapcar (lambda (l) (push arg l)) arglists) 5475 (calendar . "c")
5447 (list (push arg arglists))))) 5476 (date . "d")
5448 ((listp arg) 5477 (dayname . "n")
5449 (setq arglists 5478 (time . "t")
5450 (mapcar (lambda (a) 5479 (here . "h")
5451 (if (= 1 (length arglists)) 5480 (region . "r"))
5452 (apply (lambda (l) (push a l)) arglists) 5481 "List pairing item insertion parameters with their completion keys.")
5453 (mapcar (lambda (l) (push a l)) arglists))) 5482
5454 arg)))))) 5483(defsubst todo-insert-item--keyof (param)
5455 (setq arglists (mapcar 'reverse (apply 'append (mapc 'car arglists)))))) 5484 "Return key paired with item insertion PARAM."
5456 5485 (cdr (assoc param todo-insert-item--param-key-alist)))
5457(defvar todo-insertion-commands-args-genlist 5486
5458 '(diary nonmarking (calendar date dayname) time (here region)) 5487(defun todo-insert-item--argsleft (key list)
5459 "Generator list for argument lists of item insertion commands.") 5488 "Return sublist of LIST whose first member corresponds to KEY."
5460 5489 (let (l sym)
5461(defvar todo-insertion-commands-args 5490 (mapc (lambda (m)
5462 (let ((arglist (todo-gen-arglists todo-insertion-commands-args-genlist)) 5491 (when (consp m)
5463 res new) 5492 (catch 'found1
5464 (setq res (cl-remove-duplicates 5493 (dolist (s m)
5465 (apply 'append (mapcar 'todo-powerset arglist)) :test 'equal)) 5494 (when (equal key (todo-insert-item--keyof s))
5466 (dolist (l res) 5495 (throw 'found1 (setq sym s))))))
5467 (unless (= 5 (length l)) 5496 (if sym
5468 (let ((v (make-vector 5 nil)) elt) 5497 (progn
5469 (while l 5498 (push sym l)
5470 (setq elt (pop l)) 5499 (setq sym nil))
5471 (cond ((eq elt 'diary) 5500 (push m l)))
5472 (aset v 0 elt)) 5501 list)
5473 ((eq elt 'nonmarking) 5502 (setq list (reverse l)))
5474 (aset v 1 elt)) 5503 (memq (catch 'found2
5475 ((or (eq elt 'calendar) 5504 (dolist (e todo-insert-item--param-key-alist)
5476 (eq elt 'date) 5505 (when (equal key (cdr e))
5477 (eq elt 'dayname)) 5506 (throw 'found2 (car e)))))
5478 (aset v 2 elt)) 5507 list))
5479 ((eq elt 'time) 5508
5480 (aset v 3 elt)) 5509(defsubst todo-insert-item--this-key () (char-to-string last-command-event))
5481 ((or (eq elt 'here) 5510
5482 (eq elt 'region)) 5511(defvar todo-insert-item--keys-so-far ""
5483 (aset v 4 elt)))) 5512 "String of item insertion keys so far entered for this command.")
5484 (setq l (append v nil)))) 5513
5485 (setq new (append new (list l)))) 5514(defvar todo-insert-item--args nil)
5486 new) 5515(defvar todo-insert-item--argleft nil)
5487 "List of all argument lists for Todo mode item insertion commands.") 5516(defvar todo-insert-item--argsleft nil)
5488 5517(defvar todo-insert-item--newargsleft nil)
5489(defun todo-insertion-command-name (arglist) 5518
5490 "Generate Todo mode item insertion command name from ARGLIST." 5519(defun todo-insert-item--apply-args ()
5491 (replace-regexp-in-string 5520 "Build list of arguments for item insertion and apply them.
5492 "-\\_>" "" 5521The list consists of item insertion parameters that can be passed
5493 (replace-regexp-in-string 5522as insertion command arguments in fixed positions. If a position
5494 "-+" "-" 5523in the list is not occupied by the corresponding parameter, it is
5495 (concat "todo-insert-item-" 5524occupied by `nil'."
5496 (mapconcat (lambda (e) (if e (symbol-name e))) arglist "-"))))) 5525 (let* ((arg (list (car todo-insert-item--args)))
5497 5526 (args (nconc (cdr todo-insert-item--args)
5498(defvar todo-insertion-commands-names 5527 (list (car (todo-insert-item--argsleft
5499 (mapcar (lambda (l) 5528 (todo-insert-item--this-key)
5500 (todo-insertion-command-name l)) 5529 todo-insert-item--argsleft)))))
5501 todo-insertion-commands-args) 5530 (arglist (unless (= 5 (length args))
5502 "List of names of Todo mode item insertion commands.") 5531 (let ((v (make-vector 5 nil)) elt)
5503 5532 (while args
5504(defmacro todo-define-insertion-command (&rest args) 5533 (setq elt (pop args))
5505 "Generate Todo mode item insertion command definitions from ARGS." 5534 (cond ((eq elt 'diary)
5506 (let ((name (intern (todo-insertion-command-name args))) 5535 (aset v 0 elt))
5507 (arg0 (nth 0 args)) 5536 ((eq elt 'nonmarking)
5508 (arg1 (nth 1 args)) 5537 (aset v 1 elt))
5509 (arg2 (nth 2 args)) 5538 ((or (eq elt 'calendar)
5510 (arg3 (nth 3 args)) 5539 (eq elt 'date)
5511 (arg4 (nth 4 args))) 5540 (eq elt 'dayname))
5512 `(defun ,name (&optional arg &rest args) 5541 (aset v 2 elt))
5513 "Todo mode item insertion command generated from ARGS. 5542 ((eq elt 'time)
5514For descriptions of the individual arguments, their values, and 5543 (aset v 3 elt))
5515their relation to key bindings, see `todo-basic-insert-item'." 5544 ((or (eq elt 'here)
5516 (interactive (list current-prefix-arg)) 5545 (eq elt 'region))
5517 (todo-basic-insert-item arg ',arg0 ',arg1 ',arg2 ',arg3 ',arg4)))) 5546 (aset v 4 elt))))
5518 5547 (append v nil)))))
5519(defvar todo-insertion-commands 5548 (apply #'todo-basic-insert-item (nconc arg arglist))))
5520 (mapcar (lambda (c) 5549
5521 (eval `(todo-define-insertion-command ,@c))) 5550(defun todo-insert-item--next-param (last args argsleft)
5522 todo-insertion-commands-args) 5551 "Build item insertion command from LAST, ARGS and ARGSLEFT and call it.
5523 "List of Todo mode item insertion commands.") 5552Dynamically generate key bindings, prompting with the keys
5524 5553already entered and those still available."
5525(defvar todo-insertion-commands-arg-key-list 5554 (cl-assert argsleft)
5526 '(("diary" "y" "yy") 5555 (let* ((map (make-sparse-keymap))
5527 ("nonmarking" "k" "kk") 5556 (prompt nil)
5528 ("calendar" "c" "cc") 5557 (addprompt (lambda (k name)
5529 ("date" "d" "dd") 5558 (setq prompt (concat prompt
5530 ("dayname" "n" "nn") 5559 (format (concat
5531 ("time" "t" "tt") 5560 (if (or (eq name 'default)
5532 ("here" "h" "h") 5561 (eq name 'calendar)
5533 ("region" "r" "r")) 5562 (eq name 'here))
5534 "List of mappings of item insertion command arguments to key sequences.") 5563 " { " " ")
5535 5564 "%s=>%s"
5536(defun todo-insertion-key-bindings (map) 5565 (when (or (eq name 'copy)
5537 "Generate key binding definitions for item insertion keymap MAP." 5566 (eq name 'dayname)
5538 (dolist (c todo-insertion-commands) 5567 (eq name 'region))
5539 (let* ((key "") 5568 " }"))
5540 (cname (symbol-name c))) 5569 (propertize k 'face
5541 (mapc (lambda (l) 5570 'todo-key-prompt)
5542 (let ((arg (nth 0 l)) 5571 name))))))
5543 (key1 (nth 1 l)) 5572 (setq todo-insert-item--args args)
5544 (key2 (nth 2 l))) 5573 (setq todo-insert-item--argsleft argsleft)
5545 (if (string-match (concat (regexp-quote arg) "\\_>") cname) 5574 (when last
5546 (setq key (concat key key2))) 5575 (cond ((eq last 'default)
5547 (if (string-match (concat (regexp-quote arg) ".+") cname) 5576 (apply #'todo-basic-insert-item (car todo-insert-item--args))
5548 (setq key (concat key key1))))) 5577 (setq todo-insert-item--argsleft nil))
5549 todo-insertion-commands-arg-key-list) 5578 ((eq last 'copy)
5550 (if (string-match (concat (regexp-quote "todo-insert-item") "\\_>") cname) 5579 (todo-copy-item)
5551 (setq key (concat key "i"))) 5580 (setq todo-insert-item--argsleft nil))
5552 (define-key map key c)))) 5581 (t (let ((k (todo-insert-item--keyof last)))
5582 (funcall addprompt k 'GO!)
5583 (define-key map (todo-insert-item--keyof last)
5584 (lambda () (interactive)
5585 (todo-insert-item--apply-args)))))))
5586 (while todo-insert-item--argsleft
5587 (let ((x (car todo-insert-item--argsleft)))
5588 (setq todo-insert-item--newargsleft (cdr todo-insert-item--argsleft))
5589 (dolist (argleft (if (consp x) x (list x)))
5590 (let ((k (todo-insert-item--keyof argleft)))
5591 (funcall addprompt k argleft)
5592 (define-key map k
5593 (if (null todo-insert-item--newargsleft)
5594 (lambda () (interactive)
5595 (todo-insert-item--apply-args))
5596 (lambda () (interactive)
5597 (when (equal "k" (todo-insert-item--this-key))
5598 (unless (string-match "y" todo-insert-item--keys-so-far)
5599 (when (y-or-n-p (concat "`k' only takes effect with `y';"
5600 " add `y'? "))
5601 (setq todo-insert-item--keys-so-far
5602 (concat todo-insert-item--keys-so-far " y"))
5603 (setq todo-insert-item--args
5604 (nconc todo-insert-item--args (list 'diary))))))
5605 (setq todo-insert-item--keys-so-far
5606 (concat todo-insert-item--keys-so-far " "
5607 (todo-insert-item--this-key)))
5608 (todo-insert-item--next-param
5609 (car (todo-insert-item--argsleft
5610 (todo-insert-item--this-key)
5611 todo-insert-item--argsleft))
5612 (nconc todo-insert-item--args
5613 (list (car (todo-insert-item--argsleft
5614 (todo-insert-item--this-key)
5615 todo-insert-item--argsleft))))
5616 (cdr (todo-insert-item--argsleft
5617 (todo-insert-item--this-key)
5618 todo-insert-item--argsleft)))))))))
5619 (setq todo-insert-item--argsleft todo-insert-item--newargsleft))
5620 (when prompt (message "Enter a key (so far `%s'): %s"
5621 todo-insert-item--keys-so-far prompt))
5622 (set-temporary-overlay-map map)
5623 (setq todo-insert-item--argsleft argsleft)))
5553 5624
5554;; ----------------------------------------------------------------------------- 5625;; -----------------------------------------------------------------------------
5555;;; Todo minibuffer utilities 5626;;; Todo minibuffer utilities
@@ -6224,13 +6295,6 @@ Filtered Items mode following todo (not done) items."
6224;;; Key binding 6295;;; Key binding
6225;; ----------------------------------------------------------------------------- 6296;; -----------------------------------------------------------------------------
6226 6297
6227(defvar todo-insertion-map
6228 (let ((map (make-keymap)))
6229 (todo-insertion-key-bindings map)
6230 (define-key map "p" 'todo-copy-item)
6231 map)
6232 "Keymap for Todo mode item insertion commands.")
6233
6234(defvar todo-key-bindings-t 6298(defvar todo-key-bindings-t
6235 `( 6299 `(
6236 ("Af" todo-find-archive) 6300 ("Af" todo-find-archive)
@@ -6272,7 +6336,7 @@ Filtered Items mode following todo (not done) items."
6272 ("eyk" todo-edit-item-diary-nonmarking) 6336 ("eyk" todo-edit-item-diary-nonmarking)
6273 ("ec" todo-edit-done-item-comment) 6337 ("ec" todo-edit-done-item-comment)
6274 ("d" todo-item-done) 6338 ("d" todo-item-done)
6275 ("i" ,todo-insertion-map) 6339 ("i" todo-insert-item)
6276 ("k" todo-delete-item) 6340 ("k" todo-delete-item)
6277 ("m" todo-move-item) 6341 ("m" todo-move-item)
6278 ("u" todo-item-undone) 6342 ("u" todo-item-undone)