aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Berman2018-08-12 23:25:53 +0200
committerStephen Berman2018-08-12 23:25:53 +0200
commit2b1cac26855b99644b00a839f7ea25446d997572 (patch)
tree7ba33556ac5b9cd6e124522e7b9751702c22c322
parentf99ee7378f8529e748f894859f305d4cca2483e4 (diff)
downloademacs-2b1cac26855b99644b00a839f7ea25446d997572.tar.gz
emacs-2b1cac26855b99644b00a839f7ea25446d997572.zip
Update and improve todo-mode item insertion and editing code
* lisp/calendar/todo-mode.el (todo-insert-item--param-key-alist) (todo-insert-item--keyof, todo-insert-item--this-key) (todo-insert-item--keys-so-far, todo-insert-item--args) (todo-insert-item--argleft. todo-insert-item--argsleft) (todo-insert-item--newargsleft, todo-insert-item--apply-args) (todo-edit-item--param-key-alist, todo-edit-item--prompt) (todo-edit-item--date-param-key-alist) (todo-edit-done-item--param-key-alist): Remove. (todo-insert-item--next-param): Reimplement to take advantage of lexical binding. (todo-insert-item): Adjust to new implementation of the above. (todo-edit-item--next-key): Incorporate now removed global variables, adjust signature accordingly, update use of pcase. (todo-edit-item): Adjust to changed signature of the above.
-rw-r--r--lisp/calendar/todo-mode.el349
1 files changed, 164 insertions, 185 deletions
diff --git a/lisp/calendar/todo-mode.el b/lisp/calendar/todo-mode.el
index c1c292129e2..9c770f17fb1 100644
--- a/lisp/calendar/todo-mode.el
+++ b/lisp/calendar/todo-mode.el
@@ -1830,7 +1830,6 @@ consist of the last todo items and the first done items."
1830(defvar todo-date-from-calendar nil 1830(defvar todo-date-from-calendar nil
1831 "Helper variable for setting item date from the Emacs Calendar.") 1831 "Helper variable for setting item date from the Emacs Calendar.")
1832 1832
1833(defvar todo-insert-item--keys-so-far)
1834(defvar todo-insert-item--parameters) 1833(defvar todo-insert-item--parameters)
1835 1834
1836(defun todo-insert-item (&optional arg) 1835(defun todo-insert-item (&optional arg)
@@ -1852,8 +1851,7 @@ already been entered and which remain available. See
1852`(todo-mode) Inserting New Items' for details of the parameters, 1851`(todo-mode) Inserting New Items' for details of the parameters,
1853their associated keys and their effects." 1852their associated keys and their effects."
1854 (interactive "P") 1853 (interactive "P")
1855 (setq todo-insert-item--keys-so-far "i") 1854 (todo-insert-item--next-param (list arg) todo-insert-item--parameters nil "i"))
1856 (todo-insert-item--next-param nil (list arg) todo-insert-item--parameters))
1857 1855
1858(defun todo-insert-item--basic (&optional arg diary-type date-type time where) 1856(defun todo-insert-item--basic (&optional arg diary-type date-type time where)
1859 "Function implementing the core of `todo-insert-item'." 1857 "Function implementing the core of `todo-insert-item'."
@@ -2101,17 +2099,14 @@ the item at point."
2101 (let (todo-show-with-done) (todo-category-select))))) 2099 (let (todo-show-with-done) (todo-category-select)))))
2102 (if ov (delete-overlay ov))))) 2100 (if ov (delete-overlay ov)))))
2103 2101
2104(defvar todo-edit-item--param-key-alist)
2105(defvar todo-edit-done-item--param-key-alist)
2106
2107(defun todo-edit-item (&optional arg) 2102(defun todo-edit-item (&optional arg)
2108 "Choose an editing operation for the current item and carry it out." 2103 "Choose an editing operation for the current item and carry it out."
2109 (interactive "P") 2104 (interactive "P")
2110 (let ((marked (assoc (todo-current-category) todo-categories-with-marks))) 2105 (let ((marked (assoc (todo-current-category) todo-categories-with-marks)))
2111 (cond ((and (todo-done-item-p) (not marked)) 2106 (cond ((and (todo-done-item-p) (not marked))
2112 (todo-edit-item--next-key todo-edit-done-item--param-key-alist)) 2107 (todo-edit-item--next-key 'done arg))
2113 ((or marked (todo-item-string)) 2108 ((or marked (todo-item-string))
2114 (todo-edit-item--next-key todo-edit-item--param-key-alist arg))))) 2109 (todo-edit-item--next-key 'todo arg)))))
2115 2110
2116(defun todo-edit-item--text (&optional arg) 2111(defun todo-edit-item--text (&optional arg)
2117 "Function providing the text editing facilities of `todo-edit-item'." 2112 "Function providing the text editing facilities of `todo-edit-item'."
@@ -5523,12 +5518,14 @@ of each other."
5523;;; Generating and applying item insertion and editing key sequences 5518;;; Generating and applying item insertion and editing key sequences
5524;; ----------------------------------------------------------------------------- 5519;; -----------------------------------------------------------------------------
5525 5520
5526;; Thanks to Stefan Monnier for suggesting dynamically generating item 5521;; Thanks to Stefan Monnier for (i) not only suggesting dynamically
5527;; insertion commands and their key bindings, and offering an elegant 5522;; generating item insertion commands and their key bindings but also
5528;; implementation, which, however, relies on lexical scoping and so 5523;; offering an elegant implementation which, however, since it used
5529;; cannot be used here until the Calendar code used by todo-mode.el is 5524;; lexical binding, was at the time incompatible with the Calendar and
5530;; converted to lexical binding. Hence, the following implementation 5525;; Diary code in todo-mode.el; and (ii) later making that code
5531;; uses dynamic binding. 5526;; compatible with lexical binding, so that his implementation, of
5527;; which the following is a somewhat expanded version, could be
5528;; realized in todo-mode.el.
5532 5529
5533(defconst todo-insert-item--parameters 5530(defconst todo-insert-item--parameters
5534 '((default copy) (diary nonmarking) (calendar date dayname) time (here region)) 5531 '((default copy) (diary nonmarking) (calendar date dayname) time (here region))
@@ -5536,91 +5533,33 @@ of each other."
5536Passed by `todo-insert-item' to `todo-insert-item--next-param' to 5533Passed by `todo-insert-item' to `todo-insert-item--next-param' to
5537dynamically create item insertion commands.") 5534dynamically create item insertion commands.")
5538 5535
5539(defconst todo-insert-item--param-key-alist 5536(defun todo-insert-item--next-param (args params last keys-so-far)
5540 '((default . "i") 5537 "Generate and invoke an item insertion command.
5541 (copy . "p") 5538Dynamically generate the command, its arguments ARGS and its key
5542 (diary . "y") 5539binding by recursing through the list of parameters PARAMS,
5543 (nonmarking . "k") 5540taking the LAST from a sublist and prompting with KEYS-SO-FAR
5544 (calendar . "c") 5541keys already entered and those still available."
5545 (date . "d") 5542 (cl-assert params)
5546 (dayname . "n")
5547 (time . "t")
5548 (here . "h")
5549 (region . "r"))
5550 "List pairing item insertion parameters with their completion keys.")
5551
5552(defsubst todo-insert-item--keyof (param)
5553 "Return key paired with item insertion PARAM."
5554 (cdr (assoc param todo-insert-item--param-key-alist)))
5555
5556(defun todo-insert-item--argsleft (key list)
5557 "Return sublist of LIST whose first member corresponds to KEY."
5558 (let (l sym)
5559 (mapc (lambda (m)
5560 (when (consp m)
5561 (catch 'found1
5562 (dolist (s m)
5563 (when (equal key (todo-insert-item--keyof s))
5564 (throw 'found1 (setq sym s))))))
5565 (if sym
5566 (progn
5567 (push sym l)
5568 (setq sym nil))
5569 (push m l)))
5570 list)
5571 (setq list (reverse l)))
5572 (memq (catch 'found2
5573 (dolist (e todo-insert-item--param-key-alist)
5574 (when (equal key (cdr e))
5575 (throw 'found2 (car e)))))
5576 list))
5577
5578(defsubst todo-insert-item--this-key () (char-to-string last-command-event))
5579
5580(defvar todo-insert-item--keys-so-far ""
5581 "String of item insertion keys so far entered for this command.")
5582
5583(defvar todo-insert-item--args nil)
5584(defvar todo-insert-item--argleft nil)
5585(defvar todo-insert-item--argsleft nil)
5586(defvar todo-insert-item--newargsleft nil)
5587
5588(defun todo-insert-item--apply-args ()
5589 "Build list of arguments for item insertion and apply them.
5590The list consists of item insertion parameters that can be passed
5591as insertion command arguments in fixed positions. If a position
5592in the list is not occupied by the corresponding parameter, it is
5593occupied by nil."
5594 (let* ((arg (list (car todo-insert-item--args)))
5595 (args (nconc (cdr todo-insert-item--args)
5596 (list (car (todo-insert-item--argsleft
5597 (todo-insert-item--this-key)
5598 todo-insert-item--argsleft)))))
5599 (arglist (if (= 4 (length args))
5600 args
5601 (let ((v (make-vector 4 nil)) elt)
5602 (while args
5603 (setq elt (pop args))
5604 (cond ((memq elt '(diary nonmarking))
5605 (aset v 0 elt))
5606 ((memq elt '(calendar date dayname))
5607 (aset v 1 elt))
5608 ((eq elt 'time)
5609 (aset v 2 elt))
5610 ((memq elt '(copy here region))
5611 (aset v 3 elt))))
5612 (append v nil)))))
5613 (apply #'todo-insert-item--basic (nconc arg arglist))))
5614
5615(defun todo-insert-item--next-param (last args argsleft)
5616 "Build item insertion command from LAST, ARGS and ARGSLEFT and call it.
5617Dynamically generate key bindings, prompting with the keys
5618already entered and those still available."
5619 (cl-assert argsleft)
5620 (let* ((map (make-sparse-keymap)) 5543 (let* ((map (make-sparse-keymap))
5544 (param-key-alist '((default . "i")
5545 (copy . "p")
5546 (diary . "y")
5547 (nonmarking . "k")
5548 (calendar . "c")
5549 (date . "d")
5550 (dayname . "n")
5551 (time . "t")
5552 (here . "h")
5553 (region . "r")))
5554 ;; Return key paired with given item insertion parameter.
5555 (key-of (lambda (param) (cdr (assoc param param-key-alist))))
5556 ;; The key just typed.
5557 (this-key (lambda () (char-to-string last-command-event)))
5621 (prompt nil) 5558 (prompt nil)
5622 (addprompt 5559 ;; Add successively entered keys to the prompt and show what
5623 (lambda (k name) 5560 ;; possibilities remain.
5561 (add-to-prompt
5562 (lambda (key name)
5624 (setq prompt 5563 (setq prompt
5625 (concat prompt 5564 (concat prompt
5626 (format 5565 (format
@@ -5630,80 +5569,119 @@ already entered and those still available."
5630 "%s=>%s" 5569 "%s=>%s"
5631 (when (memq name '(copy nonmarking dayname region)) 5570 (when (memq name '(copy nonmarking dayname region))
5632 " }")) 5571 " }"))
5633 (propertize k 'face 'todo-key-prompt) 5572 (propertize key 'face 'todo-key-prompt)
5634 name)))))) 5573 name)))))
5635 (setq todo-insert-item--args args) 5574 ;; Return the sublist of the given list of parameters whose
5636 (setq todo-insert-item--argsleft argsleft) 5575 ;; first member is paired with the given key.
5576 (get-params
5577 (lambda (key lst)
5578 (setq lst (if (consp lst) lst (list lst)))
5579 (let (l sym)
5580 (mapc (lambda (m)
5581 (when (consp m)
5582 (catch 'found1
5583 (dolist (s m)
5584 (when (equal key (funcall key-of s))
5585 (throw 'found1 (setq sym s))))))
5586 (if sym
5587 (progn
5588 (push sym l)
5589 (setq sym nil))
5590 (push m l)))
5591 lst)
5592 (setq lst (reverse l)))
5593 (memq (catch 'found2
5594 (dolist (e param-key-alist)
5595 (when (equal key (cdr e))
5596 (throw 'found2 (car e)))))
5597 lst)))
5598 ;; Build list of arguments for item insertion and then
5599 ;; execute the basic insertion function. The list consists of
5600 ;; item insertion parameters that can be passed as insertion
5601 ;; command arguments in fixed positions. If a position in
5602 ;; the list is not occupied by the corresponding parameter,
5603 ;; it is occupied by nil.
5604 (gen-and-exec
5605 (lambda ()
5606 (let* ((arg (list (car args))) ; Possible prefix argument.
5607 (rest (nconc (cdr args)
5608 (list (car (funcall get-params
5609 (funcall this-key)
5610 params)))))
5611 (parlist (if (= 4 (length rest))
5612 rest
5613 (let ((v (make-vector 4 nil)) elt)
5614 (while rest
5615 (setq elt (pop rest))
5616 (cond ((memq elt '(diary nonmarking))
5617 (aset v 0 elt))
5618 ((memq elt '(calendar date dayname))
5619 (aset v 1 elt))
5620 ((eq elt 'time)
5621 (aset v 2 elt))
5622 ((memq elt '(copy here region))
5623 (aset v 3 elt))))
5624 (append v nil)))))
5625 (apply #'todo-insert-item--basic (nconc arg parlist)))))
5626 ;; Operate on a copy of the parameter list so the original is
5627 ;; not consumed, thus available for the next key typed.
5628 (params0 params))
5637 (when last 5629 (when last
5638 (if (memq last '(default copy)) 5630 (if (memq last '(default copy))
5639 (progn 5631 (progn
5640 (setq todo-insert-item--argsleft nil) 5632 (setq params0 nil)
5641 (todo-insert-item--apply-args)) 5633 (funcall gen-and-exec))
5642 (let ((k (todo-insert-item--keyof last))) 5634 (let ((key (funcall key-of last)))
5643 (funcall addprompt k (make-symbol (concat (symbol-name last) ":GO!"))) 5635 (funcall add-to-prompt key (make-symbol
5644 (define-key map (todo-insert-item--keyof last) 5636 (concat (symbol-name last) ":GO!")))
5637 (define-key map (funcall key-of last)
5645 (lambda () (interactive) 5638 (lambda () (interactive)
5646 (todo-insert-item--apply-args)))))) 5639 (funcall gen-and-exec))))))
5647 (while todo-insert-item--argsleft 5640 (while params0
5648 (let ((x (car todo-insert-item--argsleft))) 5641 (let* ((x (car params0))
5649 (setq todo-insert-item--newargsleft (cdr todo-insert-item--argsleft)) 5642 (restparams (cdr params0)))
5650 (dolist (argleft (if (consp x) x (list x))) 5643 (dolist (param (if (consp x) x (list x)))
5651 (let ((k (todo-insert-item--keyof argleft))) 5644 (let ((key (funcall key-of param)))
5652 (funcall addprompt k argleft) 5645 (funcall add-to-prompt key param)
5653 (define-key map k 5646 (define-key map key
5654 (if (null todo-insert-item--newargsleft) 5647 (if (null restparams)
5655 (lambda () (interactive) 5648 (lambda () (interactive)
5656 (todo-insert-item--apply-args)) 5649 (funcall gen-and-exec))
5657 (lambda () (interactive) 5650 (lambda () (interactive)
5658 (setq todo-insert-item--keys-so-far 5651 (setq keys-so-far (concat keys-so-far " " (funcall this-key)))
5659 (concat todo-insert-item--keys-so-far " " 5652 (todo-insert-item--next-param
5660 (todo-insert-item--this-key))) 5653 (nconc args (list (car (funcall get-params
5661 (todo-insert-item--next-param 5654 (funcall this-key) param))))
5662 (car (todo-insert-item--argsleft 5655 (cdr (funcall get-params (funcall this-key) params))
5663 (todo-insert-item--this-key) 5656 (car (funcall get-params (funcall this-key) param))
5664 todo-insert-item--argsleft)) 5657 keys-so-far))))))
5665 (nconc todo-insert-item--args 5658 (setq params0 restparams)))
5666 (list (car (todo-insert-item--argsleft
5667 (todo-insert-item--this-key)
5668 todo-insert-item--argsleft))))
5669 (cdr (todo-insert-item--argsleft
5670 (todo-insert-item--this-key)
5671 todo-insert-item--argsleft)))))))))
5672 (setq todo-insert-item--argsleft todo-insert-item--newargsleft))
5673 (when prompt (message "Press a key (so far `%s'): %s"
5674 todo-insert-item--keys-so-far prompt))
5675 (set-transient-map map) 5659 (set-transient-map map)
5676 (setq todo-insert-item--argsleft argsleft))) 5660 (when prompt (message "Press a key (so far `%s'): %s" keys-so-far prompt))
5677 5661 (setq params0 params)))
5678(defconst todo-edit-item--param-key-alist 5662
5679 '((edit . "e") 5663(defun todo-edit-item--next-key (type &optional arg)
5680 (header . "h") 5664 (let* ((todo-param-key-alist '((edit . "e")
5681 (multiline . "m") 5665 (header . "h")
5682 (diary . "y") 5666 (multiline . "m")
5683 (nonmarking . "k") 5667 (diary . "y")
5684 (date . "d") 5668 (nonmarking . "k")
5685 (time . "t")) 5669 (date . "d")
5686 "Alist of item editing parameters and their keys.") 5670 (time . "t")))
5687 5671 (done-param-key-alist '((add/edit . "c")
5688(defconst todo-edit-item--date-param-key-alist 5672 (delete . "d")))
5689 '((full . "f") 5673 (date-param-key-alist '((full . "f")
5690 (calendar . "c") 5674 (calendar . "c")
5691 (today . "a") 5675 (today . "a")
5692 (dayname . "n") 5676 (dayname . "n")
5693 (year . "y") 5677 (year . "y")
5694 (month . "m") 5678 (month . "m")
5695 (daynum . "d")) 5679 (daynum . "d")))
5696 "Alist of item date editing parameters and their keys.") 5680 (params (pcase type
5697 5681 ('todo todo-param-key-alist)
5698(defconst todo-edit-done-item--param-key-alist 5682 ('done done-param-key-alist)
5699 '((add/edit . "c") 5683 ('date date-param-key-alist)))
5700 (delete . "d")) 5684 (p->k (mapconcat (lambda (elt)
5701 "Alist of done item comment editing parameters and their keys.")
5702
5703(defvar todo-edit-item--prompt "Press a key (so far `e'): ")
5704
5705(defun todo-edit-item--next-key (params &optional arg)
5706 (let* ((p->k (mapconcat (lambda (elt)
5707 (format "%s=>%s" 5685 (format "%s=>%s"
5708 (propertize (cdr elt) 'face 5686 (propertize (cdr elt) 'face
5709 'todo-key-prompt) 5687 'todo-key-prompt)
@@ -5712,31 +5690,32 @@ already entered and those still available."
5712 '(add/edit delete)) 5690 '(add/edit delete))
5713 " comment")))) 5691 " comment"))))
5714 params " ")) 5692 params " "))
5715 (key-prompt (substitute-command-keys todo-edit-item--prompt)) 5693 (key-prompt (substitute-command-keys
5694 (concat "Press a key (so far `e"
5695 (if (eq type 'date) " d" "")
5696 "'): ")))
5716 (this-key (let ((key (read-key (concat key-prompt p->k)))) 5697 (this-key (let ((key (read-key (concat key-prompt p->k))))
5717 (and (characterp key) (char-to-string key)))) 5698 (and (characterp key) (char-to-string key))))
5718 (this-param (car (rassoc this-key params)))) 5699 (this-param (car (rassoc this-key params))))
5719 (pcase this-param 5700 (pcase this-param
5720 (`edit (todo-edit-item--text)) 5701 ('edit (todo-edit-item--text))
5721 (`header (todo-edit-item--text 'include-header)) 5702 ('header (todo-edit-item--text 'include-header))
5722 (`multiline (todo-edit-item--text 'multiline)) 5703 ('multiline (todo-edit-item--text 'multiline))
5723 (`add/edit (todo-edit-item--text 'comment-edit)) 5704 ('add/edit (todo-edit-item--text 'comment-edit))
5724 (`delete (todo-edit-item--text 'comment-delete)) 5705 ('delete (todo-edit-item--text 'comment-delete))
5725 (`diary (todo-edit-item--diary-inclusion)) 5706 ('diary (todo-edit-item--diary-inclusion))
5726 (`nonmarking (todo-edit-item--diary-inclusion 'nonmarking)) 5707 ('nonmarking (todo-edit-item--diary-inclusion 'nonmarking))
5727 (`date (let ((todo-edit-item--prompt "Press a key (so far `e d'): ")) 5708 ('date (todo-edit-item--next-key 'date arg))
5728 (todo-edit-item--next-key 5709 ('full (progn (todo-edit-item--header 'date)
5729 todo-edit-item--date-param-key-alist arg)))
5730 (`full (progn (todo-edit-item--header 'date)
5731 (when todo-always-add-time-string 5710 (when todo-always-add-time-string
5732 (todo-edit-item--header 'time)))) 5711 (todo-edit-item--header 'time))))
5733 (`calendar (todo-edit-item--header 'calendar)) 5712 ('calendar (todo-edit-item--header 'calendar))
5734 (`today (todo-edit-item--header 'today)) 5713 ('today (todo-edit-item--header 'today))
5735 (`dayname (todo-edit-item--header 'dayname)) 5714 ('dayname (todo-edit-item--header 'dayname))
5736 (`year (todo-edit-item--header 'year arg)) 5715 ('year (todo-edit-item--header 'year arg))
5737 (`month (todo-edit-item--header 'month arg)) 5716 ('month (todo-edit-item--header 'month arg))
5738 (`daynum (todo-edit-item--header 'day arg)) 5717 ('daynum (todo-edit-item--header 'day arg))
5739 (`time (todo-edit-item--header 'time))))) 5718 ('time (todo-edit-item--header 'time)))))
5740 5719
5741;; ----------------------------------------------------------------------------- 5720;; -----------------------------------------------------------------------------
5742;;; Todo minibuffer utilities 5721;;; Todo minibuffer utilities