From mboxrd@z Thu Jan 1 00:00:00 1970 From: Paul Sexton Subject: Fixes for org-capture-templates-contexts Date: Thu, 10 Jan 2013 21:04:54 +0000 (UTC) Message-ID: Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Return-path: Received: from eggs.gnu.org ([208.118.235.92]:37140) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TtPss-0001Mv-PP for emacs-orgmode@gnu.org; Thu, 10 Jan 2013 16:42:02 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TtPsp-0000Rp-MK for emacs-orgmode@gnu.org; Thu, 10 Jan 2013 16:41:58 -0500 Received: from plane.gmane.org ([80.91.229.3]:40592) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TtPJN-00006P-5e for emacs-orgmode@gnu.org; Thu, 10 Jan 2013 16:05:17 -0500 Received: from list by plane.gmane.org with local (Exim 4.69) (envelope-from ) id 1TtPJW-0000JS-AB for emacs-orgmode@gnu.org; Thu, 10 Jan 2013 22:05:31 +0100 Received: from wireless-nat-1.auckland.ac.nz ([130.216.30.112]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Thu, 10 Jan 2013 22:05:26 +0100 Received: from psexton.2a by wireless-nat-1.auckland.ac.nz with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Thu, 10 Jan 2013 22:05:26 +0100 List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: emacs-orgmode@gnu.org org-capture-templates-contexts currently appears not to work. The structure that the function 'org-contextualize-validate-key' expects to find in the variable seems quite different from the structure described in the docstring. Here are fixed versions of the functions 'org-contextualize-validate-key' and 'org-contextualize-keys', both from org.el. I have also added some functionality: - new context specifiers in-buffer and not-in-buffer - in-mode and not-in-mode expect a symbol, not a regexp. - if a rule specifies a template that has 'sub-templates', those sub-templates will also be affected by the rule. For example if you have templates 't', 'ta', 'tb' and 'tc', you can specify a rule for 't' which will affect all of them. I have also rewritten the docstring for org-capture-templates-contexts, from org-capture.el. -------------------- (defcustom org-capture-templates-contexts nil "Alist of capture templates and valid contexts. Each entry in the alist takes the form: (KEY [USUAL-KEY] CONTEXT [CONTEXT...]) Where: KEY :: a string of one or more letters, identifying a capture template. USUAL-KEY :: if supplied, this is the string that identifies the capture template in `org-capture-templates', while KEY becomes the string which will be used to select the template only in the present context (see below). CONTEXT :: a context definition. Each context definition (CONTEXT) takes the form: FUNCTION or (SPECIFIER . ARGUMENT) Where: FUNCTION :: either a lambda form or a symbol naming a function. The function must take no arguments. SPECIFIER :: a symbol matching one of the context specifiers listed below. ARGUMENT :: either a string regular expression (for in-file and in-buffer), or a symbol (for in-mode). Here are the available context specifiers: in-file: command displayed in files matching regex in-buffer: command displayed in buffers matching regex in-mode: command displayed if major mode matches symbol not-in-file: command not displayed in files matching regex not-in-buffer: command not displayed in buffers matching regex not-in-mode: command not displayed when major mode matches symbol 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\" (in-mode . message-mode))) If you include several context definitions, the agenda command will be accessible if at least one of them is valid. If the template specified by KEY has sub-templates, they will also be affected by the rule (unless they have their own rules). For example, if you have a template `t' and sub-templates `ta', `tb' and `tc', then a rule for `t' will affect whether all of those contexts are accessible. You can also bind a key to another agenda custom command depending on contextual rules. '((\"c\" \"d\" (in-file . \"\\.el$\") (in-buffer \"scratch\"))) Here it means: in files ending in `.el' and in buffers whose name contains `scratch', use \"c\" 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 "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)) (function :tag "Custom function")))))) (defun org-contextualize-validate-key (key contexts) "Check CONTEXTS for agenda or capture KEY." (let (clause context res) (while (setq clause (pop contexts)) (destructuring-bind (context-key old-key . context-list) clause (mapc (lambda (context) (when (cond ((and (>= (length context-key) (length key)) (not (equal key context-key))) nil) ((and (< (length context-key) (length key)) (not (string-prefix-p context-key key))) nil) ((functionp context) (funcall context)) (t (destructuring-bind (context-spec . context-arg) context (message "Considering context %s" context) (or (and (eq context-spec 'in-file) (buffer-file-name) (string-match context-arg (buffer-file-name))) (and (eq context-spec 'in-buffer) (string-match context-arg (buffer-name))) (and (eq context-spec 'in-mode) (eq context-arg major-mode)) (when (and (eq context-spec 'not-in-file) (buffer-file-name)) (not (string-match context-arg (buffer-file-name)))) (and (eq context-spec 'not-in-buffer) (not (string-match context-arg (buffer-name)))) (when (eq context-spec 'not-in-mode) (not (eq context-arg major-mode))))))) (push clause res))) context-list))) (delete-dups (delq nil res)))) (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) (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)) (let (vrules repl) (cond ((and (not (assoc (car c) contexts)) (not (assoc (string (elt (car c) 0)) contexts))) (push c r)) ((and (or (assoc (car c) contexts) (assoc (string (elt (car c) 0)) contexts)) (setq vrules (org-contextualize-validate-key (car c) contexts))) (mapc (lambda (vr) (when (not (equal (car vr) (cadr vr))) (setq repl vr))) vrules) (if (not repl) (push c r) (push (cadr repl) s) (push (cons (car c) (cdr (or (assoc (cadr repl) alist) (error "Undefined key `%s' as contextual replacement for `%s'" (cadr repl) (car c))))) r)))))) ;; Return limited ALIST, possibly with keys modified, and deduplicated (delq nil (delete-dups (mapcar (lambda (x) (let ((tpl (car x))) (when (not (delq nil (mapcar (lambda(y) (equal y tpl)) s))) x))) (reverse r))))))