| 
					
				 | 
			
			
				@@ -84,42 +84,66 @@ directly, use instead: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ;;; Functions 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-(defun org-macro--set-template (name value templates) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+(defun org-macro--makeargs (template) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  "Compute the formal arglist to use for TEMPLATE." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (let ((max 0) (i 0)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    (while (string-match "\\$\\([0-9]+\\)" template i) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (setq i (match-end 0)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (setq max (max max (string-to-number (match-string 1 template))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    (let ((args '(&rest _))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (while (> max 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        (push (intern (format "$%d" max)) args) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        (setq max (1- max))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (cons '&optional args)))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+(defun org-macro--set-templates (templates) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   "Set template for the macro NAME. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 VALUE is the template of the macro.  The new value override the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-previous one, unless VALUE is nil.  TEMPLATES is the list of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-templates.  Return the updated list." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  (let ((old-definition (assoc name templates))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    (cond ((and value old-definition) (setcdr old-definition value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	  (old-definition) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	  (t (push (cons name (or value "")) templates)))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  templates) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+previous one, unless VALUE is nil.  Return the updated list." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (let ((new-templates nil)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    (pcase-dolist (`(,name . ,value) templates) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (let ((old-definition (assoc name new-templates))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        (when (and (stringp value) (string-match-p "\\`(eval\\>" value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          ;; Pre-process the evaluation form for faster macro expansion. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          (let* ((args (org-macro--makeargs value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                 (body 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  (condition-case nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      ;; `value' is of the form "(eval ...)" but we 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      ;; don't want this to mean to pass the result to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      ;; `eval' (which would cause double evaluation), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      ;; so we strip the `eval' away with `cadr'. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		      (cadr (read value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		    (error 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     (user-error "Invalid definition for macro %S" name))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	    (setq value (eval (macroexpand-all `(lambda ,args ,body)) t)))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        (cond ((and value old-definition) (setcdr old-definition value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	      (old-definition) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	      (t (push (cons name (or value "")) new-templates))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    new-templates)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 (defun org-macro--collect-macros () 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   "Collect macro definitions in current buffer and setup files. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 Return an alist containing all macro templates found." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  (let ((templates nil)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (let ((templates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         `(("author" . ,(org-macro--find-keyword-value "AUTHOR")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	   ("email" . ,(org-macro--find-keyword-value "EMAIL")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	   ("title" . ,(org-macro--find-keyword-value "TITLE" t)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	   ("date" . ,(org-macro--find-date))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     (pcase (org-collect-keywords '("MACRO")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       (`(("MACRO" . ,values)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				        (dolist (value values) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	 (when (string-match "^\\(\\S-+\\)[ \t]*" value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	   (let ((name (match-string 1 value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		 (definition (substring value (match-end 0)))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	     (setq templates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		   (org-macro--set-template name definition templates))))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    (let ((macros `(("author" . ,(org-macro--find-keyword-value "AUTHOR")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		    ("email" . ,(org-macro--find-keyword-value "EMAIL")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		    ("title" . ,(org-macro--find-keyword-value "TITLE" t)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		    ("date" . ,(org-macro--find-date))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      (pcase-dolist (`(,name . ,value) macros) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	(setq templates (org-macro--set-template name value templates)))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+             (push (cons name definition) templates)))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     templates)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-(defun org-macro-initialize-templates () 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+(defun org-macro-initialize-templates (&optional default) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   "Collect macro templates defined in current buffer. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-Templates are stored in buffer-local variable 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-`org-macro-templates'. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+DEFAULT is a list of globally available templates. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Templates are stored in buffer-local variable `org-macro-templates'. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 In addition to buffer-defined macros, the function installs the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 following ones: \"n\", \"author\", \"email\", \"keyword\", 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -129,8 +153,9 @@ a file, \"input-file\" and \"modification-time\"." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   (org-macro--counter-initialize)	;for "n" macro 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   (setq org-macro-templates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	(nconc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	 ;; Install user-defined macros. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	 (org-macro--collect-macros) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 ;; Install user-defined macros.  Local macros have higher 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         ;; precedence than global ones. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         (org-macro--set-templates (append default (org-macro--collect-macros))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	 ;; Install file-specific macros. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	 (let ((visited-file (buffer-file-name (buffer-base-buffer)))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	   (and visited-file 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -138,21 +163,23 @@ a file, \"input-file\" and \"modification-time\"." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		(list 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		 `("input-file" . ,(file-name-nondirectory visited-file)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		 `("modification-time" . 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		   ,(format "(eval 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-\(format-time-string $1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                     (or (and (org-string-nw-p $2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                              (org-macro--vc-modified-time %s)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                     '%s)))" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			    (prin1-to-string visited-file) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			    (prin1-to-string 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			     (file-attribute-modification-time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			      (file-attributes visited-file)))))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		   ,(let ((modtime (file-attribute-modification-time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			            (file-attributes visited-file)))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		      (lambda (arg1 arg2 &rest _) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		        (format-time-string 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         arg1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         (or (and (org-string-nw-p arg2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                  (org-macro--vc-modified-time visited-file)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             modtime)))))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	 ;; Install generic macros. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	 (list 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	  '("n" . "(eval (org-macro--counter-increment $1 $2))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	  '("keyword" . "(eval (org-macro--find-keyword-value $1))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	  '("time" . "(eval (format-time-string $1))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	  '("property" . "(eval (org-macro--get-property $1 $2))"))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 '(("keyword" . (lambda (arg1 &rest _) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          (org-macro--find-keyword-value arg1))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	   ("n" . (lambda (&optional arg1 arg2 &rest _) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    (org-macro--counter-increment arg1 arg2))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           ("property" . (lambda (arg1 &optional arg2 &rest _) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           (org-macro--get-property arg1 arg2))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	   ("time" . (lambda (arg1 &rest _) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       (format-time-string arg1))))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 (defun org-macro-expand (macro templates) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   "Return expanded MACRO, as a string. 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -164,21 +191,17 @@ default value.  Return nil if no template was found." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	 ;; Macro names are case-insensitive. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	 (cdr (assoc-string (org-element-property :key macro) templates t)))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     (when template 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      (let* ((eval? (string-match-p "\\`(eval\\>" template)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	     (value 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	      (replace-regexp-in-string 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	       "\\$[0-9]+" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	       (lambda (m) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		 (let ((arg (or (nth (1- (string-to-number (substring m 1))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				     (org-element-property :args macro)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				;; No argument: remove place-holder. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				""))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		   ;; `eval' implies arguments are strings. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		   (if eval? (format "%S" arg) arg))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	       template nil 'literal))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        (when eval? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-          (setq value (eval (condition-case nil (read value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			      (error (debug)))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (let* ((value 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	      (if (functionp template) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	          (apply template (org-element-property :args macro)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	        (replace-regexp-in-string 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	         "\\$[0-9]+" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	         (lambda (m) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		   (or (nth (1- (string-to-number (substring m 1))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			    (org-element-property :args macro)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		       ;; No argument: remove place-holder. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		       "")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		 template nil 'literal)))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         ;; Force return value to be a string. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         (format "%s" (or value "")))))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -380,7 +403,7 @@ value, i.e. do not increment. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 If the string represents an integer, set the counter to this number. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 Any other non-empty string resets the counter to 1." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  (let ((name-trimmed (org-trim name)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (let ((name-trimmed (if (stringp name) (org-trim name) "")) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         (action-trimmed (when (org-string-nw-p action) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                           (org-trim action)))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     (puthash name-trimmed 
			 |