diff options
author | Nicolas Goaziou <mail@nicolasgoaziou.fr> | 2019-04-29 20:49:09 +0200 |
---|---|---|
committer | Nicolas Goaziou <mail@nicolasgoaziou.fr> | 2019-04-29 20:49:09 +0200 |
commit | 9bbae3ce8f5628f9236bf86d24886fc444fb0dd1 (patch) | |
tree | 2e94cb0bfaa11fa78627dbc2f1d1cb5f2c0c648e | |
parent | 7cfc9ce95fd42b68b829decdb3eefd7d0ba74a93 (diff) | |
parent | 727c3f442b7af5fb45cfcb1d589aa64524ea5a1e (diff) | |
download | org-mode-9bbae3ce8f5628f9236bf86d24886fc444fb0dd1.tar.gz |
Merge branch 'maint'
-rw-r--r-- | lisp/org-macro.el | 155 | ||||
-rw-r--r-- | testing/lisp/test-org-macro.el | 23 |
2 files changed, 85 insertions, 93 deletions
diff --git a/lisp/org-macro.el b/lisp/org-macro.el index a3a0d1f..c928ea7 100644 --- a/lisp/org-macro.el +++ b/lisp/org-macro.el @@ -83,51 +83,61 @@ directly, use instead: ;;; Functions -(defun org-macro--collect-macros () +(defun org-macro--set-template (name value 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." + (when value + (let ((old-definition (assoc name templates))) + (if old-definition + (setcdr old-definition value) + (push (cons name value) templates)))) + templates) + +(defun org-macro--collect-macros (&optional files templates) "Collect macro definitions in current buffer and setup files. -Return an alist containing all macro templates found." - (letrec ((collect-macros - (lambda (files templates) - ;; Return an alist of macro templates. FILES is a list - ;; of setup files names read so far, used to avoid - ;; circular dependencies. TEMPLATES is the alist - ;; collected so far. - (let ((case-fold-search t)) - (org-with-wide-buffer - (goto-char (point-min)) - (while (re-search-forward - "^[ \t]*#\\+\\(MACRO\\|SETUPFILE\\):" nil t) - (let ((element (org-element-at-point))) - (when (eq (org-element-type element) 'keyword) - (let ((val (org-element-property :value element))) - (if (equal (org-element-property :key element) "MACRO") - ;; Install macro in TEMPLATES. - (when (string-match - "^\\(.*?\\)\\(?:\\s-+\\(.*\\)\\)?\\s-*$" val) - (let* ((name (match-string 1 val)) - (template (or (match-string 2 val) "")) - (old-cell (assoc name templates))) - (if old-cell (setcdr old-cell template) - (push (cons name template) templates)))) - ;; Enter setup file. - (let* ((uri (org-strip-quotes (org-trim val))) - (uri-is-url (org-file-url-p uri)) - (uri (if uri-is-url - uri - (expand-file-name uri)))) - ;; Avoid circular dependencies. - (unless (member uri files) - (with-temp-buffer - (unless uri-is-url - (setq default-directory - (file-name-directory uri))) - (org-mode) - (insert (org-file-contents uri 'noerror)) - (setq templates - (funcall collect-macros (cons uri files) - templates))))))))))) - templates)))) - (funcall collect-macros nil nil))) +Return an alist containing all macro templates found. + +FILES is a list of setup files names read so far, used to avoid +circular dependencies. TEMPLATES is the alist collected so far. +The two arguments are used in recursive calls." + (let ((case-fold-search t)) + (org-with-point-at 1 + (while (re-search-forward "^[ \t]*#\\+\\(MACRO\\|SETUPFILE\\):" nil t) + (let ((element (org-element-at-point))) + (when (eq (org-element-type element) 'keyword) + (let ((val (org-element-property :value element))) + (if (equal "MACRO" (org-element-property :key element)) + ;; Install macro in TEMPLATES. + (when (string-match "^\\(\\S-+\\)[ \t]*" val) + (let ((name (match-string 1 val)) + (value (substring val (match-end 0)))) + (setq templates + (org-macro--set-template name value templates)))) + ;; Enter setup file. + (let* ((uri (org-strip-quotes val)) + (uri-is-url (org-file-url-p uri)) + (uri (if uri-is-url + uri + (expand-file-name uri)))) + ;; Avoid circular dependencies. + (unless (member uri files) + (with-temp-buffer + (unless uri-is-url + (setq default-directory (file-name-directory uri))) + (org-mode) + (insert (org-file-contents uri 'noerror)) + (setq templates + (org-macro--collect-macros + (cons uri files) 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)))) + templates)) (defun org-macro-initialize-templates () "Collect macro templates defined in current buffer. @@ -161,27 +171,12 @@ a file, \"input-file\" and \"modification-time\"." (prin1-to-string (file-attribute-modification-time (file-attributes visited-file)))))))) - ;; Install built-in macros. + ;; Install generic macros. (list '("n" . "(eval (org-macro--counter-increment $1 $2))") - `("author" . ,(org-macro--find-keyword-value "AUTHOR")) - `("email" . ,(org-macro--find-keyword-value "EMAIL")) '("keyword" . "(eval (org-macro--find-keyword-value $1))") '("time" . "(eval (format-time-string $1))") - `("title" . ,(org-macro--find-keyword-value "TITLE")) - '("property" . "(eval (org-macro--get-property $1 $2))") - `("date" . - ,(let* ((value (org-macro--find-keyword-value "DATE")) - (date (org-element-parse-secondary-string - value (org-element-restriction 'keyword)))) - (if (and (consp date) - (not (cdr date)) - (eq 'timestamp (org-element-type (car date)))) - (format "(eval (if (org-string-nw-p $1) %s %S))" - (format "(org-timestamp-format '%S $1)" - (org-element-copy (car date))) - value) - value))))))) + '("property" . "(eval (org-macro--get-property $1 $2))"))))) (defun org-macro-expand (macro templates) "Return expanded MACRO, as a string. @@ -332,21 +327,39 @@ by `org-link-search', or the empty string." (error "Macro property failed: cannot find location %s" location)))) (org-entry-get nil property 'selective))) -(defun org-macro--find-keyword-value (name) +(defun org-macro--find-keyword-value (name &optional collect) "Find value for keyword NAME in current buffer. -KEYWORD is a string. Return value associated to the keywords -named after NAME, as a string, or nil." +Return value associated to the keywords named after NAME, as +a string, or nil. When optional argument COLLECT is non-nil, +concatenate values, separated with a space, from various keywords +in the buffer." (org-with-point-at 1 (let ((regexp (format "^[ \t]*#\\+%s:" (regexp-quote name))) (case-fold-search t) (result nil)) - (while (re-search-forward regexp nil t) - (let ((element (org-element-at-point))) - (when (eq 'keyword (org-element-type element)) - (setq result (concat result - " " - (org-element-property :value element)))))) - (and result (org-trim result))))) + (catch :exit + (while (re-search-forward regexp nil t) + (let ((element (org-element-at-point))) + (when (eq 'keyword (org-element-type element)) + (let ((value (org-element-property :value element))) + (if (not collect) (throw :exit value) + (setq result (concat result " " value))))))) + (and result (org-trim result)))))) + +(defun org-macro--find-date () + "Find value for DATE in current buffer. +Return value as a string." + (let* ((value (org-macro--find-keyword-value "DATE")) + (date (org-element-parse-secondary-string + value (org-element-restriction 'keyword)))) + (if (and (consp date) + (not (cdr date)) + (eq 'timestamp (org-element-type (car date)))) + (format "(eval (if (org-string-nw-p $1) %s %S))" + (format "(org-timestamp-format '%S $1)" + (org-element-copy (car date))) + value) + value))) (defun org-macro--vc-modified-time (file) (save-window-excursion diff --git a/testing/lisp/test-org-macro.el b/testing/lisp/test-org-macro.el index 20ffb0f..28bc712 100644 --- a/testing/lisp/test-org-macro.el +++ b/testing/lisp/test-org-macro.el @@ -103,18 +103,7 @@ "#+MACRO: macro expansion\n* COMMENT H1\n** H2\n<point>{{{macro}}}" (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) - (org-with-wide-buffer (buffer-string))))) - ;; User-defined macros take precedence over built-in macros. - (should - (equal - "foo" - (org-test-with-temp-text - "#+MACRO: title foo\n#+TITLE: bar\n<point>{{{title}}}" - (org-macro-initialize-templates) - (org-macro-replace-all org-macro-templates) - (goto-char (point-max)) - (buffer-substring-no-properties (line-beginning-position) - (line-end-position)))))) + (org-with-wide-buffer (buffer-string)))))) (ert-deftest test-org-macro/property () "Test {{{property}}} macro." @@ -313,16 +302,6 @@ (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-substring-no-properties - (line-beginning-position) (point-max))))) - ;; Replace macro with keyword's value. - (should - (equal - "value value2" - (org-test-with-temp-text - "#+keyword: value\n#+keyword: value2\n<point>{{{keyword(KEYWORD)}}}" - (org-macro-initialize-templates) - (org-macro-replace-all org-macro-templates) - (buffer-substring-no-properties (line-beginning-position) (point-max)))))) (ert-deftest test-org-macro/escape-arguments () |