Browse Source

Update the handling of agenda/capture keys contextualization.

* org.el (org-contextualize-keys): Rename from
`org-contextualize-agenda-or-capture'.  Fix normalization to
handle empty key replacement string.
(org-contextualize-validate-key): Rename from
`org-contexts-validate'.  Allow checking against a custom
function.

* org-agenda.el (org-agenda-custom-commands-contexts): Update.
(org-agenda): Use `org-contextualize-keys'.

* org-capture.el (org-capture-templates-contexts): Ditto.

* org.texi (Templates in contexts, Setting Options): Update to
reflect changes in how contexts options are processed.
Bastien Guerry 7 years ago
parent
commit
10dbdf5fc2
4 changed files with 77 additions and 63 deletions
  1. 18 10
      doc/org.texi
  2. 20 20
      lisp/org-agenda.el
  3. 22 22
      lisp/org-capture.el
  4. 17 11
      lisp/org.el

+ 18 - 10
doc/org.texi

@@ -6864,13 +6864,18 @@ emails containing patches.  Then you would configure this option like this:
 
 @example
 (setq org-capture-templates-contexts
-      '(("p" "p" (in-mode . "message-mode"))))
+      '(("p" (in-mode . "message-mode"))))
 @end example
 
-The second @code{"p"} tells what template to really call in the specified
-context.  You can set this to another capture template, so that @code{p} will
-transparently fall back on it.  See the docstring of the variable for more
-information.
+You can also tell that the command key @code{"p"} should refer to another
+template.  In that case, add this command key like this:
+
+@example
+(setq org-capture-templates-contexts
+      '(("p" "q" (in-mode . "message-mode"))))
+@end example
+
+See the docstring of the variable for more information.
 
 @node Attachments, RSS Feeds, Capture, Capture - Refile - Archive
 @section Attachments
@@ -8783,13 +8788,16 @@ like this:
 
 @example
 (setq org-agenda-custom-commands-contexts
-      '(("o" "o" (in-mode . "message-mode"))))
+      '(("o" (in-mode . "message-mode"))))
 @end example
 
-The second @code{"o"} tells what command to really call in the specified
-context.  You can set this to another agenda custom command, so that @code{o}
-will transparently fall back on it.  See the docstring of the variable for
-more information.
+You can also tell that the command key @code{"o"} should refer to another
+command key @code{"r"}.  In that case, add this command key like this:
+
+@example
+(setq org-agenda-custom-commands-contexts
+      '(("o" "r" (in-mode . "message-mode"))))
+@end example
 
 See the docstring of the variable for more information.
 

+ 20 - 20
lisp/org-agenda.el

@@ -2335,49 +2335,49 @@ that have been changed along."
 (defvar org-agenda-overriding-restriction nil)
 
 (defcustom org-agenda-custom-commands-contexts nil
-  "Alist of custom agenda commands and valid contexts.
+  "Alist of custom agenda keys and contextual rules.
 
 For example, if you have a custom agenda command \"p\" and you
 want this command to be accessible only from plain text files,
 use this:
 
-   '((\"p\" \"p\" (in-file . \"\\.txt\")))
+   '((\"p\" (in-file . \"\\.txt\")))
 
-If you replace the second \"p\" by another key (say \"q\"), then
-the \"p\" key will be associated with the \"q\" command in the
-valid contexts.  This is useful if you want to use the same key
-to reach multiple commands depending on the context:
-
-   '((\"p\" \"q\" (in-file . \"\\.txt\"))
-     (\"p\" \"r\" (in-file . \"\\.el\"))
-     (\"p\" \"s\" (in-file . \"\\.c\")))
-
-Here, the \"p\" key will be accessible from buffers visiting
-.txt, .el and .c files, and it will be a synonym for \"q\", \"r\"
-and \"s\" respectively.
-
-Here are the available contexts definition:
+Here are the available contexts definitions:
 
       in-file: command displayed only in matching files
       in-mode: command displayed only in matching modes
   not-in-file: command not displayed in matching files
   not-in-mode: command not displayed in matching modes
+   [function]: a custom function taking no argument
 
 If you define several checks, the agenda command will be
-accessible if there is at least one valid check."
+accessible if there is at least one valid check.
+
+You can also bind a key to another agenda custom command
+depending on contextual rules.
+
+    '((\"p\" \"q\" (in-file . \"\\.txt\")))
+
+Here it means: in .txt files, use \"p\" as the key for the
+agenda command otherwise associated with \"q\".  (The command
+originally associated with \"q\" is not displayed to avoid
+duplicates.)"
   ;; :version "24.3"
   :group 'org-agenda-custom-commands
   :type '(repeat (list :tag "Rule"
-		       (string :tag "Agenda key")
+		       (string :tag "        Agenda key")
 		       (string :tag "Replace by command")
 		       (repeat :tag "Available when"
+			      (choice
 			       (cons :tag "Condition"
 				     (choice
 				      (const :tag "In file" in-file)
 				      (const :tag "Not in file" not-in-file)
 				      (const :tag "In mode" in-mode)
 				      (const :tag "Not in mode" not-in-mode))
-				     (regexp))))))
+				     (regexp))
+			       (function :tag "Custom function"))))))
 
 ;;;###autoload
 (defun org-agenda (&optional arg keys restriction)
@@ -2435,7 +2435,7 @@ Pressing `<' twice means to restrict to the current subtree or region
 			   (t (cons (car x) (cons "" (cdr x))))))
 		   org-agenda-custom-commands)))
 	   (org-agenda-custom-commands
-	    (org-contextualize-agenda-or-capture
+	    (org-contextualize-keys
 	     org-agenda-custom-commands org-agenda-custom-commands-contexts))
 	   (buf (current-buffer))
 	   (bfn (buffer-file-name (buffer-base-buffer)))

+ 22 - 22
lisp/org-capture.el

@@ -439,49 +439,49 @@ for a capture buffer.")
     (org-capture)))
 
 (defcustom org-capture-templates-contexts nil
-  "Bind capture keys with rules on where to display them.
+  "Alist of capture templates and valid contexts.
 
 For example, if you have a capture template \"c\" and you want
 this template to be accessible only from `message-mode' buffers,
 use this:
 
-   '((\"c\" \"c\" (in-mode . \"message-mode\")))
+   '((\"c\" (in-mode . \"message-mode\")))
 
-If you replace the second \"c\" by another key (say \"d\"), then
-the \"c\" key will be associated with the \"d\" template in the
-valid contexts.  This is useful if you want to use the same key
-for different templates depending on the context:
+Here are the available contexts definitions:
 
-   '((\"c\" \"d\" (in-file . \"\\.txt\"))
-     (\"c\" \"e\" (in-file . \"\\.el\"))
-     (\"c\" \"f\" (in-file . \"\\.c\")))
+      in-file: command displayed only in matching files
+      in-mode: command displayed only in matching modes
+  not-in-file: command not displayed in matching files
+  not-in-mode: command not displayed in matching modes
+   [function]: a custom function taking no argument
 
-Here, the \"c\" key will be accessible from buffers visiting
-.txt, .el and .c files, and it will be a synonym for \"d\", \"e\"
-and \"f\" respectively.
+If you define several checks, the agenda command will be
+accessible if there is at least one valid check.
 
-Here are the available contexts definition:
+You can also bind a key to another agenda custom command
+depending on contextual rules.
 
-      in-file: template displayed only in matching files
-      in-mode: template displayed only in matching modes
-  not-in-file: template not displayed in matching files
-  not-in-mode: template not displayed in matching modes
+    '((\"c\" \"d\" (in-mode . \"message-mode\")))
 
-If you define several checks, the capture template will be
-accessible if there is at least one valid check."
+Here it means: in `message-mode buffers', use \"d\" as the
+key for the capture template otherwise associated with \"d\".
+\(The template originally associated with \"q\" is not displayed
+to avoid duplicates.)"
   ;; :version "24.3"
   :group 'org-capture
   :type '(repeat (list :tag "Rule"
-		       (string :tag "Capture key")
+		       (string :tag "        Capture key")
 		       (string :tag "Replace by template")
 		       (repeat :tag "Available when"
+			      (choice
 			       (cons :tag "Condition"
 				     (choice
 				      (const :tag "In file" in-file)
 				      (const :tag "Not in file" not-in-file)
 				      (const :tag "In mode" in-mode)
 				      (const :tag "Not in mode" not-in-mode))
-				     (regexp))))))
+				     (regexp))
+			       (function :tag "Custom function"))))))
 
 ;;;###autoload
 (defun org-capture (&optional goto keys)
@@ -1327,7 +1327,7 @@ Use PREFIX as a prefix for the name of the indirect buffer."
   "Select a capture template.
 Lisp programs can force the template by setting KEYS to a string."
   (let ((org-capture-templates
-	 (or (org-contextualize-agenda-or-capture
+	 (or (org-contextualize-keys
 	      org-capture-templates org-capture-templates-contexts)
 	     '(("t" "Task" entry (file+headline "" "Tasks")
 		"* TODO %?\n  %u\n  %a")))))

+ 17 - 11
lisp/org.el

@@ -8619,16 +8619,21 @@ to execute outside of tables."
 					   keys)
 				   '('orgstruct-error))))))))
 
-(defun org-contextualize-agenda-or-capture (alist contexts)
-  "Return a subset of elements in ALIST depending on CONTEXTS.
-ALIST can be either `org-agenda-custom-commands' or
-`org-capture-templates'."
+(defun org-contextualize-keys (alist contexts)
+  "Return valid elements in ALIST depending on CONTEXTS.
+
+`org-agenda-custom-commands' or `org-capture-templates' are the
+values used for ALIST, and `org-agenda-custom-commands-contexts'
+or `org-capture-templates-contexts' are the associated contexts
+definitions."
   (let ((contexts
 	 ;; normalize contexts
 	 (mapcar
-	  (lambda(c) (if (listp (cadr c))
-			 (list (car c) (car c) (cadr c))
-		       c)) contexts))
+	  (lambda(c) (cond ((listp (cadr c))
+			    (list (car c) (car c) (cadr c)))
+			   ((string= "" (cadr c))
+			    (list (car c) (car c) (caddr c)))
+			   (t c))) contexts))
 	(a alist) c r s)
     ;; loop over all commands or templates
     (while (setq c (pop a))
@@ -8637,7 +8642,7 @@ ALIST can be either `org-agenda-custom-commands' or
 	 ((not (assoc (car c) contexts))
 	  (push c r))
 	 ((and (assoc (car c) contexts)
-	       (setq vrules (org-contexts-validate
+	       (setq vrules (org-contextualize-validate-key
 			     (car c) contexts)))
 	  (mapc (lambda (vr)
 		  (when (not (equal (car vr) (cadr vr)))
@@ -8662,8 +8667,8 @@ ALIST can be either `org-agenda-custom-commands' or
 					(equal y tpl)) s))) x)))
 	      (reverse r))))))
 
-(defun org-contexts-validate (key contexts)
-  "Return valid CONTEXTS."
+(defun org-contextualize-validate-key (key contexts)
+  "Check CONTEXTS for agenda or capture KEY."
   (let (r rr res)
     (while (setq r (pop contexts))
       (mapc
@@ -8679,7 +8684,8 @@ ALIST can be either `org-agenda-custom-commands' or
 			      (buffer-file-name))
 		     (not (string-match (cdr rr) (buffer-file-name))))
 		   (when (eq (car rr) 'not-in-mode)
-		     (not (string-match (cdr rr) (symbol-name major-mode))))))
+		     (not (string-match (cdr rr) (symbol-name major-mode))))
+		   (when (functionp rr) (funcall rr))))
 	  (push r res)))
        (car (last r))))
     (delete-dups (delq nil res))))