Merge branch 'maint'
This commit is contained in:
commit
9bbae3ce8f
|
@ -83,51 +83,61 @@ directly, use instead:
|
||||||
|
|
||||||
;;; Functions
|
;;; 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.
|
"Collect macro definitions in current buffer and setup files.
|
||||||
Return an alist containing all macro templates found."
|
Return an alist containing all macro templates found.
|
||||||
(letrec ((collect-macros
|
|
||||||
(lambda (files templates)
|
FILES is a list of setup files names read so far, used to avoid
|
||||||
;; Return an alist of macro templates. FILES is a list
|
circular dependencies. TEMPLATES is the alist collected so far.
|
||||||
;; of setup files names read so far, used to avoid
|
The two arguments are used in recursive calls."
|
||||||
;; circular dependencies. TEMPLATES is the alist
|
(let ((case-fold-search t))
|
||||||
;; collected so far.
|
(org-with-point-at 1
|
||||||
(let ((case-fold-search t))
|
(while (re-search-forward "^[ \t]*#\\+\\(MACRO\\|SETUPFILE\\):" nil t)
|
||||||
(org-with-wide-buffer
|
(let ((element (org-element-at-point)))
|
||||||
(goto-char (point-min))
|
(when (eq (org-element-type element) 'keyword)
|
||||||
(while (re-search-forward
|
(let ((val (org-element-property :value element)))
|
||||||
"^[ \t]*#\\+\\(MACRO\\|SETUPFILE\\):" nil t)
|
(if (equal "MACRO" (org-element-property :key element))
|
||||||
(let ((element (org-element-at-point)))
|
;; Install macro in TEMPLATES.
|
||||||
(when (eq (org-element-type element) 'keyword)
|
(when (string-match "^\\(\\S-+\\)[ \t]*" val)
|
||||||
(let ((val (org-element-property :value element)))
|
(let ((name (match-string 1 val))
|
||||||
(if (equal (org-element-property :key element) "MACRO")
|
(value (substring val (match-end 0))))
|
||||||
;; Install macro in TEMPLATES.
|
(setq templates
|
||||||
(when (string-match
|
(org-macro--set-template name value templates))))
|
||||||
"^\\(.*?\\)\\(?:\\s-+\\(.*\\)\\)?\\s-*$" val)
|
;; Enter setup file.
|
||||||
(let* ((name (match-string 1 val))
|
(let* ((uri (org-strip-quotes val))
|
||||||
(template (or (match-string 2 val) ""))
|
(uri-is-url (org-file-url-p uri))
|
||||||
(old-cell (assoc name templates)))
|
(uri (if uri-is-url
|
||||||
(if old-cell (setcdr old-cell template)
|
uri
|
||||||
(push (cons name template) templates))))
|
(expand-file-name uri))))
|
||||||
;; Enter setup file.
|
;; Avoid circular dependencies.
|
||||||
(let* ((uri (org-strip-quotes (org-trim val)))
|
(unless (member uri files)
|
||||||
(uri-is-url (org-file-url-p uri))
|
(with-temp-buffer
|
||||||
(uri (if uri-is-url
|
(unless uri-is-url
|
||||||
uri
|
(setq default-directory (file-name-directory uri)))
|
||||||
(expand-file-name uri))))
|
(org-mode)
|
||||||
;; Avoid circular dependencies.
|
(insert (org-file-contents uri 'noerror))
|
||||||
(unless (member uri files)
|
(setq templates
|
||||||
(with-temp-buffer
|
(org-macro--collect-macros
|
||||||
(unless uri-is-url
|
(cons uri files) templates)))))))))))
|
||||||
(setq default-directory
|
(let ((macros `(("author" . ,(org-macro--find-keyword-value "AUTHOR"))
|
||||||
(file-name-directory uri)))
|
("email" . ,(org-macro--find-keyword-value "EMAIL"))
|
||||||
(org-mode)
|
("title" . ,(org-macro--find-keyword-value "TITLE" t))
|
||||||
(insert (org-file-contents uri 'noerror))
|
("date" . ,(org-macro--find-date)))))
|
||||||
(setq templates
|
(pcase-dolist (`(,name . ,value) macros)
|
||||||
(funcall collect-macros (cons uri files)
|
(setq templates (org-macro--set-template name value templates))))
|
||||||
templates)))))))))))
|
templates))
|
||||||
templates))))
|
|
||||||
(funcall collect-macros nil nil)))
|
|
||||||
|
|
||||||
(defun org-macro-initialize-templates ()
|
(defun org-macro-initialize-templates ()
|
||||||
"Collect macro templates defined in current buffer.
|
"Collect macro templates defined in current buffer.
|
||||||
|
@ -161,27 +171,12 @@ a file, \"input-file\" and \"modification-time\"."
|
||||||
(prin1-to-string
|
(prin1-to-string
|
||||||
(file-attribute-modification-time
|
(file-attribute-modification-time
|
||||||
(file-attributes visited-file))))))))
|
(file-attributes visited-file))))))))
|
||||||
;; Install built-in macros.
|
;; Install generic macros.
|
||||||
(list
|
(list
|
||||||
'("n" . "(eval (org-macro--counter-increment $1 $2))")
|
'("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))")
|
'("keyword" . "(eval (org-macro--find-keyword-value $1))")
|
||||||
'("time" . "(eval (format-time-string $1))")
|
'("time" . "(eval (format-time-string $1))")
|
||||||
`("title" . ,(org-macro--find-keyword-value "TITLE"))
|
'("property" . "(eval (org-macro--get-property $1 $2))")))))
|
||||||
'("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)))))))
|
|
||||||
|
|
||||||
(defun org-macro-expand (macro templates)
|
(defun org-macro-expand (macro templates)
|
||||||
"Return expanded MACRO, as a string.
|
"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))))
|
(error "Macro property failed: cannot find location %s" location))))
|
||||||
(org-entry-get nil property 'selective)))
|
(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.
|
"Find value for keyword NAME in current buffer.
|
||||||
KEYWORD is a string. Return value associated to the keywords
|
Return value associated to the keywords named after NAME, as
|
||||||
named after NAME, as a string, or nil."
|
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
|
(org-with-point-at 1
|
||||||
(let ((regexp (format "^[ \t]*#\\+%s:" (regexp-quote name)))
|
(let ((regexp (format "^[ \t]*#\\+%s:" (regexp-quote name)))
|
||||||
(case-fold-search t)
|
(case-fold-search t)
|
||||||
(result nil))
|
(result nil))
|
||||||
(while (re-search-forward regexp nil t)
|
(catch :exit
|
||||||
(let ((element (org-element-at-point)))
|
(while (re-search-forward regexp nil t)
|
||||||
(when (eq 'keyword (org-element-type element))
|
(let ((element (org-element-at-point)))
|
||||||
(setq result (concat result
|
(when (eq 'keyword (org-element-type element))
|
||||||
" "
|
(let ((value (org-element-property :value element)))
|
||||||
(org-element-property :value element))))))
|
(if (not collect) (throw :exit value)
|
||||||
(and result (org-trim result)))))
|
(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)
|
(defun org-macro--vc-modified-time (file)
|
||||||
(save-window-excursion
|
(save-window-excursion
|
||||||
|
|
|
@ -103,18 +103,7 @@
|
||||||
"#+MACRO: macro expansion\n* COMMENT H1\n** H2\n<point>{{{macro}}}"
|
"#+MACRO: macro expansion\n* COMMENT H1\n** H2\n<point>{{{macro}}}"
|
||||||
(org-macro-initialize-templates)
|
(org-macro-initialize-templates)
|
||||||
(org-macro-replace-all org-macro-templates)
|
(org-macro-replace-all org-macro-templates)
|
||||||
(org-with-wide-buffer (buffer-string)))))
|
(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))))))
|
|
||||||
|
|
||||||
(ert-deftest test-org-macro/property ()
|
(ert-deftest test-org-macro/property ()
|
||||||
"Test {{{property}}} macro."
|
"Test {{{property}}} macro."
|
||||||
|
@ -312,16 +301,6 @@
|
||||||
"#+keyword: value\n<point>{{{keyword(KEYWORD)}}}"
|
"#+keyword: value\n<point>{{{keyword(KEYWORD)}}}"
|
||||||
(org-macro-initialize-templates)
|
(org-macro-initialize-templates)
|
||||||
(org-macro-replace-all org-macro-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
|
(buffer-substring-no-properties
|
||||||
(line-beginning-position) (point-max))))))
|
(line-beginning-position) (point-max))))))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue