2017-06-21 13:20:20 +02:00
|
|
|
|
;;; org-macro.el --- Macro Replacement Code for Org -*- lexical-binding: t; -*-
|
2013-11-12 14:06:26 +01:00
|
|
|
|
|
2019-01-01 00:59:58 +00:00
|
|
|
|
;; Copyright (C) 2013-2019 Free Software Foundation, Inc.
|
2013-11-12 14:06:26 +01:00
|
|
|
|
|
|
|
|
|
;; Author: Nicolas Goaziou <n.goaziou@gmail.com>
|
|
|
|
|
;; Keywords: outlines, hypermedia, calendar, wp
|
|
|
|
|
|
Fix and standardize some copyright and license notices
* ob-abc.el: Add year, part of Emacs, standardize license text.
* ob-ebnf.el: Part of Emacs, standardize license text.
* ob-makefile.el: Fix years.
* org-macro.el, ox-beamer.el, ox-latex.el, ox-org.el: Part of Emacs.
* ox-ascii.el, ox-md.el, ox.el: Use range for years, part of Emacs.
* ox-html.el: Part of Emacs.
(org-html-scripts): Add 2013 to copyright years.
(org-html-infojs-template): Set copyright holder to FSF.
* ox-icalendar.el: Part of Emacs, fix years.
* ox-texinfo.el: Copyright to FSF, use range for years.
2013-11-12 09:03:46 -08:00
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
|
|
2013-11-12 14:06:26 +01:00
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
;; (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
2017-09-13 15:52:52 -07:00
|
|
|
|
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
2013-11-12 14:06:26 +01:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;; Macros are expanded with `org-macro-replace-all', which relies
|
|
|
|
|
;; internally on `org-macro-expand'.
|
|
|
|
|
|
|
|
|
|
;; Default templates for expansion are stored in the buffer-local
|
|
|
|
|
;; variable `org-macro-templates'. This variable is updated by
|
|
|
|
|
;; `org-macro-initialize-templates', which recursively calls
|
|
|
|
|
;; `org-macro--collect-macros' in order to read setup files.
|
|
|
|
|
|
2017-06-21 13:20:20 +02:00
|
|
|
|
;; Argument in macros are separated with commas. Proper escaping rules
|
|
|
|
|
;; are implemented in `org-macro-escape-arguments' and arguments can
|
|
|
|
|
;; be extracted from a string with `org-macro-extract-arguments'.
|
|
|
|
|
|
2013-11-12 14:06:26 +01:00
|
|
|
|
;; Along with macros defined through #+MACRO: keyword, default
|
|
|
|
|
;; templates include the following hard-coded macros:
|
2017-09-18 12:01:12 +02:00
|
|
|
|
;; {{{time(format-string)}}},
|
|
|
|
|
;; {{{property(node-property)}}},
|
|
|
|
|
;; {{{input-file}}},
|
|
|
|
|
;; {{{modification-time(format-string)}}},
|
|
|
|
|
;; {{{n(counter,action}}}.
|
2013-11-12 14:06:26 +01:00
|
|
|
|
|
|
|
|
|
;; Upon exporting, "ox.el" will also provide {{{author}}}, {{{date}}},
|
|
|
|
|
;; {{{email}}} and {{{title}}} macros.
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(require 'cl-lib)
|
2013-11-12 14:06:26 +01:00
|
|
|
|
(require 'org-macs)
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(require 'org-compat)
|
2013-11-12 14:06:26 +01:00
|
|
|
|
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(declare-function org-element-at-point "org-element" ())
|
2013-11-12 14:06:26 +01:00
|
|
|
|
(declare-function org-element-context "org-element" (&optional element))
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(declare-function org-element-macro-parser "org-element" ())
|
2013-11-12 14:06:26 +01:00
|
|
|
|
(declare-function org-element-property "org-element" (property element))
|
|
|
|
|
(declare-function org-element-type "org-element" (element))
|
2017-09-18 12:01:12 +02:00
|
|
|
|
(declare-function org-file-contents "org" (file &optional noerror nocache))
|
|
|
|
|
(declare-function org-file-url-p "org" (file))
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(declare-function org-in-commented-heading-p "org" (&optional no-inheritance))
|
|
|
|
|
(declare-function org-mode "org" ())
|
2017-09-18 12:01:12 +02:00
|
|
|
|
(declare-function org-trim "org" (s &optional keep-lead))
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(declare-function vc-backend "vc-hooks" (f))
|
|
|
|
|
(declare-function vc-call "vc-hooks" (fun file &rest args) t)
|
|
|
|
|
(declare-function vc-exec-after "vc-dispatcher" (code))
|
2013-11-12 14:06:26 +01:00
|
|
|
|
|
|
|
|
|
;;; Variables
|
|
|
|
|
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(defvar-local org-macro-templates nil
|
2013-11-12 14:06:26 +01:00
|
|
|
|
"Alist containing all macro templates in current buffer.
|
|
|
|
|
Associations are in the shape of (NAME . TEMPLATE) where NAME
|
|
|
|
|
stands for macro's name and template for its replacement value,
|
|
|
|
|
both as strings. This is an internal variable. Do not set it
|
|
|
|
|
directly, use instead:
|
|
|
|
|
|
|
|
|
|
#+MACRO: name template")
|
|
|
|
|
|
|
|
|
|
;;; Functions
|
|
|
|
|
|
|
|
|
|
(defun org-macro--collect-macros ()
|
|
|
|
|
"Collect macro definitions in current buffer and setup files.
|
|
|
|
|
Return an alist containing all macro templates found."
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(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.
|
2017-09-18 12:01:12 +02:00
|
|
|
|
(let* ((uri (org-unbracket-string "\"" "\"" (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)
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(with-temp-buffer
|
2017-09-18 12:01:12 +02:00
|
|
|
|
(unless uri-is-url
|
|
|
|
|
(setq default-directory
|
|
|
|
|
(file-name-directory uri)))
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(org-mode)
|
2017-09-18 12:01:12 +02:00
|
|
|
|
(insert (org-file-contents uri 'noerror))
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(setq templates
|
2017-09-18 12:01:12 +02:00
|
|
|
|
(funcall collect-macros (cons uri files)
|
2017-06-21 13:20:20 +02:00
|
|
|
|
templates)))))))))))
|
|
|
|
|
templates))))
|
2013-11-12 14:06:26 +01:00
|
|
|
|
(funcall collect-macros nil nil)))
|
|
|
|
|
|
|
|
|
|
(defun org-macro-initialize-templates ()
|
|
|
|
|
"Collect macro templates defined in current buffer.
|
|
|
|
|
Templates are stored in buffer-local variable
|
|
|
|
|
`org-macro-templates'. In addition to buffer-defined macros, the
|
|
|
|
|
function installs the following ones: \"property\",
|
|
|
|
|
\"time\". and, if the buffer is associated to a file,
|
|
|
|
|
\"input-file\" and \"modification-time\"."
|
2018-03-27 23:34:41 +02:00
|
|
|
|
(let* ((templates nil)
|
2013-11-12 14:06:26 +01:00
|
|
|
|
(update-templates
|
|
|
|
|
(lambda (cell)
|
|
|
|
|
(let ((old-template (assoc (car cell) templates)))
|
|
|
|
|
(if old-template (setcdr old-template (cdr cell))
|
|
|
|
|
(push cell templates))))))
|
2017-09-18 12:01:12 +02:00
|
|
|
|
;; Install "property", "time" macros.
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(mapc update-templates
|
|
|
|
|
(list (cons "property"
|
|
|
|
|
"(eval (save-excursion
|
|
|
|
|
(let ((l \"$2\"))
|
|
|
|
|
(when (org-string-nw-p l)
|
|
|
|
|
(condition-case _
|
|
|
|
|
(let ((org-link-search-must-match-exact-headline t))
|
|
|
|
|
(org-link-search l nil t))
|
|
|
|
|
(error
|
|
|
|
|
(error \"Macro property failed: cannot find location %s\"
|
|
|
|
|
l)))))
|
|
|
|
|
(org-entry-get nil \"$1\" 'selective)))")
|
2013-11-12 14:06:26 +01:00
|
|
|
|
(cons "time" "(eval (format-time-string \"$1\"))")))
|
2017-09-18 12:01:12 +02:00
|
|
|
|
;; Install "input-file", "modification-time" macros.
|
2013-11-12 14:06:26 +01:00
|
|
|
|
(let ((visited-file (buffer-file-name (buffer-base-buffer))))
|
|
|
|
|
(when (and visited-file (file-exists-p visited-file))
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(mapc update-templates
|
2013-11-12 14:06:26 +01:00
|
|
|
|
(list (cons "input-file" (file-name-nondirectory visited-file))
|
|
|
|
|
(cons "modification-time"
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(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)
|
2013-11-12 14:06:26 +01:00
|
|
|
|
(prin1-to-string
|
file-attributes cleanup
Mostly, this replaces magic-number calls like (nth 4 A) with
more-informative calls like (file-attribute-access-time A).
It also fixes some documentation and minor timestamp coding
issues that I noticed while looking into this.
* doc/lispref/files.texi (File Attributes):
* lisp/files.el (file-attribute-size)
(file-attribute-inode-number, file-attribute-device-number):
* src/dired.c (Fdirectory_files_and_attributes)
(Ffile_attributes):
Mention which attributes must be integers, or nonnegative integers,
as opposed to merely being numbers. Remove no-longer-correct
talk about representing large integers as conses of integers.
* doc/lispref/files.texi (Magic File Names):
* doc/misc/gnus.texi (Low-level interface to the spam-stat dictionary):
* lisp/autorevert.el (auto-revert-find-file-function)
(auto-revert-tail-mode, auto-revert-handler):
* lisp/auth-source.el (auth-source-netrc-parse):
* lisp/cedet/ede/files.el (ede--inode-for-dir):
* lisp/cedet/semantic/db-file.el (object-write):
* lisp/cedet/semantic/db-mode.el (semanticdb-kill-hook):
* lisp/cedet/semantic/db.el (semanticdb-needs-refresh-p)
(semanticdb-synchronize):
* lisp/cedet/srecode/table.el (srecode-mode-table-new):
* lisp/desktop.el (desktop-save, desktop-read):
* lisp/dired-aux.el (dired-file-set-difference)
(dired-do-chxxx, dired-do-chmod, dired-copy-file-recursive)
(dired-create-files):
* lisp/dired.el (dired-directory-changed-p, dired-readin):
* lisp/dos-w32.el (w32-direct-print-region-helper):
* lisp/emacs-lisp/autoload.el (autoload-generate-file-autoloads)
(autoload-find-destination, update-directory-autoloads):
* lisp/emacs-lisp/shadow.el (load-path-shadows-same-file-or-nonexistent):
* lisp/epg.el (epg--start, epg-wait-for-completion):
* lisp/eshell/em-ls.el (eshell-ls-filetype-p)
(eshell-ls-applicable, eshell-ls-size-string)
(eshell-ls-file, eshell-ls-dir, eshell-ls-files)
(eshell-ls-entries):
* lisp/eshell/em-pred.el (eshell-predicate-alist)
(eshell-pred-file-type, eshell-pred-file-links)
(eshell-pred-file-size):
* lisp/eshell/em-unix.el (eshell-shuffle-files, eshell/cat)
(eshell-du-sum-directory, eshell/du):
* lisp/eshell/esh-util.el (eshell-read-passwd)
(eshell-read-hosts):
* lisp/files.el (remote-file-name-inhibit-cache)
(find-file-noselect, insert-file-1, dir-locals-find-file)
(dir-locals-read-from-dir, backup-buffer)
(file-ownership-preserved-p, copy-directory)
(read-file-modes):
* lisp/find-lisp.el (find-lisp-format):
* lisp/gnus/gnus-agent.el (gnus-agent-unfetch-articles)
(gnus-agent-read-agentview, gnus-agent-expire-group-1)
(gnus-agent-request-article, gnus-agent-regenerate-group)
(gnus-agent-update-files-total-fetched-for)
(gnus-agent-update-view-total-fetched-for):
* lisp/gnus/gnus-cache.el (gnus-cache-read-active)
(gnus-cache-update-file-total-fetched-for)
(gnus-cache-update-overview-total-fetched-for):
* lisp/gnus/gnus-cloud.el (gnus-cloud-file-new-p):
* lisp/gnus/gnus-score.el (gnus-score-score-files):
* lisp/gnus/gnus-start.el (gnus-save-newsrc-file)
(gnus-master-read-slave-newsrc):
* lisp/gnus/gnus-sum.el (gnus-summary-import-article):
* lisp/gnus/gnus-util.el (gnus-file-newer-than)
(gnus-cache-file-contents):
* lisp/gnus/mail-source.el (mail-source-delete-old-incoming)
(mail-source-callback, mail-source-movemail):
* lisp/gnus/nneething.el (nneething-create-mapping)
(nneething-make-head):
* lisp/gnus/nnfolder.el (nnfolder-read-folder):
* lisp/gnus/nnheader.el (nnheader-file-size)
(nnheader-insert-nov-file):
* lisp/gnus/nnmail.el (nnmail-activate):
* lisp/gnus/nnmaildir.el (nnmaildir--group-maxnum)
(nnmaildir--new-number, nnmaildir--update-nov)
(nnmaildir--scan, nnmaildir-request-scan)
(nnmaildir-request-update-info)
(nnmaildir-request-expire-articles):
* lisp/gnus/nnmh.el (nnmh-request-list-1)
(nnmh-request-expire-articles, nnmh-update-gnus-unreads):
* lisp/gnus/nnml.el (nnml-request-expire-articles):
* lisp/gnus/spam-stat.el (spam-stat-save, spam-stat-load)
(spam-stat-process-directory, spam-stat-test-directory):
* lisp/ido.el (ido-directory-too-big-p)
(ido-file-name-all-completions):
* lisp/image-dired.el (image-dired-get-thumbnail-image)
(image-dired-create-thumb-1):
* lisp/info.el (info-insert-file-contents):
* lisp/ls-lisp.el (ls-lisp-insert-directory)
(ls-lisp-handle-switches, ls-lisp-classify-file)
(ls-lisp-format):
* lisp/mail/blessmail.el:
* lisp/mail/feedmail.el (feedmail-default-date-generator)
(feedmail-default-message-id-generator):
* lisp/mail/mailabbrev.el (mail-abbrevs-sync-aliases)
(mail-abbrevs-setup):
* lisp/mail/mspools.el (mspools-size-folder):
* lisp/mail/rmail.el (rmail-insert-inbox-text):
* lisp/mail/sendmail.el (sendmail-sync-aliases):
* lisp/mh-e/mh-alias.el (mh-alias-tstamp):
* lisp/net/ange-ftp.el (ange-ftp-parse-netrc)
(ange-ftp-write-region, ange-ftp-file-newer-than-file-p)
(ange-ftp-cf1):
* lisp/net/eudcb-mab.el (eudc-mab-query-internal):
* lisp/net/eww.el (eww-read-bookmarks):
* lisp/net/netrc.el (netrc-parse):
* lisp/net/newst-backend.el (newsticker--image-get):
* lisp/nxml/rng-loc.el (rng-get-parsed-schema-locating-file):
* lisp/obsolete/fast-lock.el (fast-lock-save-cache):
* lisp/obsolete/vc-arch.el (vc-arch-state)
(vc-arch-diff3-rej-p):
* lisp/org/ob-eval.el (org-babel--shell-command-on-region):
* lisp/org/org-attach.el (org-attach-commit):
* lisp/org/org-macro.el (org-macro-initialize-templates):
* lisp/org/org.el (org-babel-load-file)
(org-file-newer-than-p):
* lisp/org/ox-html.el (org-html-format-spec):
* lisp/org/ox-publish.el (org-publish-find-date)
(org-publish-cache-ctime-of-src):
* lisp/pcmpl-gnu.el (pcomplete/tar):
* lisp/pcmpl-rpm.el (pcmpl-rpm-packages):
* lisp/play/cookie1.el (cookie-snarf):
* lisp/progmodes/cmacexp.el (c-macro-expansion):
* lisp/ps-bdf.el (bdf-file-mod-time):
* lisp/server.el (server-ensure-safe-dir):
* lisp/simple.el (shell-command-on-region):
* lisp/speedbar.el (speedbar-item-info-file-helper)
(speedbar-check-obj-this-line):
* lisp/thumbs.el (thumbs-cleanup-thumbsdir):
* lisp/time.el (display-time-mail-check-directory)
(display-time-file-nonempty-p):
* lisp/url/url-cache.el (url-is-cached):
* lisp/url/url-file.el (url-file-asynch-callback):
* lisp/vc/diff-mode.el (diff-delete-if-empty):
* lisp/vc/pcvs-info.el (cvs-fileinfo-from-entries):
* lisp/vc/vc-bzr.el (vc-bzr-state-heuristic):
* lisp/vc/vc-cvs.el (vc-cvs-checkout-model)
(vc-cvs-state-heuristic, vc-cvs-merge-news)
(vc-cvs-retrieve-tag, vc-cvs-parse-status, vc-cvs-parse-entry):
* lisp/vc/vc-hg.el (vc-hg--slurp-hgignore-1)
(vc-hg--ignore-patterns-valid-p)
(vc-hg--cached-dirstate-search, vc-hg-state-fast):
* lisp/vc/vc-hooks.el (vc-after-save):
* lisp/vc/vc-rcs.el (vc-rcs-workfile-is-newer):
* lisp/vc/vc-svn.el (vc-svn-merge-news, vc-svn-parse-status):
* lisp/vc/vc.el (vc-checkout, vc-checkin, vc-revert-file):
* lisp/xdg.el (xdg-mime-apps):
Prefer (file-attribute-size A) to (nth 7 A), and similarly
for other file attributes accessors.
* doc/lispref/files.texi (File Attributes):
* doc/lispref/intro.texi (Version Info):
* doc/lispref/os.texi (Idle Timers):
* lisp/erc/erc.el (erc-string-to-emacs-time):
* lisp/files.el (file-attribute-access-time)
(file-attribute-modification-time)
(file-attribute-status-change-time):
* lisp/net/tramp-compat.el:
(tramp-compat-file-attribute-modification-time)
(tramp-compat-file-attribute-size):
* src/buffer.c (syms_of_buffer):
* src/editfns.c (Fget_internal_run_time):
* src/fileio.c (Fvisited_file_modtime)
(Fset_visited_file_modtime):
* src/keyboard.c (Fcurrent_idle_time):
* src/process.c (Fprocess_attributes):
Defer implementation details about timestamp format to the
section that talks about timestamp format, to make it easier
to change the documentation later if timestamp formats are
extended.
* lisp/gnus/gnus-util.el (gnus-file-newer-than):
* lisp/speedbar.el (speedbar-check-obj-this-line):
* lisp/vc/vc-rcs.el (vc-rcs-workfile-is-newer):
Prefer time-less-p to doing it by hand.
* lisp/ls-lisp.el (ls-lisp-format): Inode numbers are no longer conses.
* lisp/vc/vc-bzr.el (vc-bzr-state-heuristic):
Use eql, not eq, to compare integers that might be bignums.
* lisp/org/ox-publish.el (org-publish-cache-ctime-of-src):
Prefer float-time to doing time arithmetic by hand.
2018-09-23 18:30:46 -07:00
|
|
|
|
(file-attribute-modification-time
|
|
|
|
|
(file-attributes visited-file)))))))))
|
2017-09-18 12:01:12 +02:00
|
|
|
|
;; Initialize and install "n" macro.
|
|
|
|
|
(org-macro--counter-initialize)
|
|
|
|
|
(funcall update-templates
|
|
|
|
|
(cons "n" "(eval (org-macro--counter-increment \"$1\" \"$2\"))"))
|
2018-03-27 23:34:41 +02:00
|
|
|
|
(setq org-macro-templates (nconc (org-macro--collect-macros) templates))))
|
2013-11-12 14:06:26 +01:00
|
|
|
|
|
|
|
|
|
(defun org-macro-expand (macro templates)
|
|
|
|
|
"Return expanded MACRO, as a string.
|
|
|
|
|
MACRO is an object, obtained, for example, with
|
|
|
|
|
`org-element-context'. TEMPLATES is an alist of templates used
|
|
|
|
|
for expansion. See `org-macro-templates' for a buffer-local
|
|
|
|
|
default value. Return nil if no template was found."
|
|
|
|
|
(let ((template
|
|
|
|
|
;; Macro names are case-insensitive.
|
|
|
|
|
(cdr (assoc-string (org-element-property :key macro) templates t))))
|
|
|
|
|
(when template
|
|
|
|
|
(let ((value (replace-regexp-in-string
|
|
|
|
|
"\\$[0-9]+"
|
|
|
|
|
(lambda (arg)
|
|
|
|
|
(or (nth (1- (string-to-number (substring arg 1)))
|
|
|
|
|
(org-element-property :args macro))
|
|
|
|
|
;; No argument: remove place-holder.
|
|
|
|
|
""))
|
|
|
|
|
template nil 'literal)))
|
|
|
|
|
;; VALUE starts with "(eval": it is a s-exp, `eval' it.
|
|
|
|
|
(when (string-match "\\`(eval\\>" value)
|
|
|
|
|
(setq value (eval (read value))))
|
|
|
|
|
;; Return string.
|
|
|
|
|
(format "%s" (or value ""))))))
|
|
|
|
|
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(defun org-macro-replace-all (templates &optional finalize keywords)
|
2013-11-12 14:06:26 +01:00
|
|
|
|
"Replace all macros in current buffer by their expansion.
|
2017-06-21 13:20:20 +02:00
|
|
|
|
|
2013-11-12 14:06:26 +01:00
|
|
|
|
TEMPLATES is an alist of templates used for expansion. See
|
2017-06-21 13:20:20 +02:00
|
|
|
|
`org-macro-templates' for a buffer-local default value.
|
|
|
|
|
|
|
|
|
|
If optional arg FINALIZE is non-nil, raise an error if a macro is
|
|
|
|
|
found in the buffer with no definition in TEMPLATES.
|
|
|
|
|
|
|
|
|
|
Optional argument KEYWORDS, when non-nil is a list of keywords,
|
|
|
|
|
as strings, where macro expansion is allowed."
|
2017-08-29 10:07:08 +02:00
|
|
|
|
(org-with-wide-buffer
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(let ((properties-regexp (format "\\`EXPORT_%s\\+?\\'"
|
|
|
|
|
(regexp-opt keywords)))
|
|
|
|
|
record)
|
|
|
|
|
(while (re-search-forward "{{{[-A-Za-z0-9_]" nil t)
|
|
|
|
|
(unless (save-match-data (org-in-commented-heading-p))
|
|
|
|
|
(let* ((datum (save-match-data (org-element-context)))
|
|
|
|
|
(type (org-element-type datum))
|
|
|
|
|
(macro
|
|
|
|
|
(cond
|
|
|
|
|
((eq type 'macro) datum)
|
|
|
|
|
;; In parsed keywords and associated node
|
|
|
|
|
;; properties, force macro recognition.
|
|
|
|
|
((or (and (eq type 'keyword)
|
|
|
|
|
(member (org-element-property :key datum) keywords))
|
|
|
|
|
(and (eq type 'node-property)
|
|
|
|
|
(string-match-p properties-regexp
|
|
|
|
|
(org-element-property :key datum))))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char (match-beginning 0))
|
|
|
|
|
(org-element-macro-parser))))))
|
|
|
|
|
(when macro
|
|
|
|
|
(let* ((value (org-macro-expand macro templates))
|
|
|
|
|
(begin (org-element-property :begin macro))
|
|
|
|
|
(signature (list begin
|
|
|
|
|
macro
|
|
|
|
|
(org-element-property :args macro))))
|
|
|
|
|
;; Avoid circular dependencies by checking if the same
|
|
|
|
|
;; macro with the same arguments is expanded at the
|
|
|
|
|
;; same position twice.
|
|
|
|
|
(cond ((member signature record)
|
|
|
|
|
(error "Circular macro expansion: %s"
|
|
|
|
|
(org-element-property :key macro)))
|
|
|
|
|
(value
|
|
|
|
|
(push signature record)
|
|
|
|
|
(delete-region
|
|
|
|
|
begin
|
|
|
|
|
;; Preserve white spaces after the macro.
|
|
|
|
|
(progn (goto-char (org-element-property :end macro))
|
|
|
|
|
(skip-chars-backward " \t")
|
|
|
|
|
(point)))
|
|
|
|
|
;; Leave point before replacement in case of
|
|
|
|
|
;; recursive expansions.
|
|
|
|
|
(save-excursion (insert value)))
|
|
|
|
|
(finalize
|
|
|
|
|
(error "Undefined Org macro: %s; aborting"
|
|
|
|
|
(org-element-property :key macro))))))))))))
|
2017-06-21 13:20:20 +02:00
|
|
|
|
|
|
|
|
|
(defun org-macro-escape-arguments (&rest args)
|
|
|
|
|
"Build macro's arguments string from ARGS.
|
|
|
|
|
ARGS are strings. Return value is a string with arguments
|
|
|
|
|
properly escaped and separated with commas. This is the opposite
|
|
|
|
|
of `org-macro-extract-arguments'."
|
|
|
|
|
(let ((s ""))
|
|
|
|
|
(dolist (arg (reverse args) (substring s 1))
|
|
|
|
|
(setq s
|
|
|
|
|
(concat
|
|
|
|
|
","
|
|
|
|
|
(replace-regexp-in-string
|
|
|
|
|
"\\(\\\\*\\),"
|
|
|
|
|
(lambda (m)
|
|
|
|
|
(concat (make-string (1+ (* 2 (length (match-string 1 m)))) ?\\)
|
|
|
|
|
","))
|
|
|
|
|
;; If a non-terminal argument ends on backslashes, make
|
|
|
|
|
;; sure to also escape them as they will be followed by
|
|
|
|
|
;; a comma.
|
|
|
|
|
(concat arg (and (not (equal s ""))
|
|
|
|
|
(string-match "\\\\+\\'" arg)
|
|
|
|
|
(match-string 0 arg)))
|
|
|
|
|
nil t)
|
|
|
|
|
s)))))
|
|
|
|
|
|
|
|
|
|
(defun org-macro-extract-arguments (s)
|
|
|
|
|
"Extract macro arguments from string S.
|
|
|
|
|
S is a string containing comma separated values properly escaped.
|
|
|
|
|
Return a list of arguments, as strings. This is the opposite of
|
|
|
|
|
`org-macro-escape-arguments'."
|
|
|
|
|
;; Do not use `org-split-string' since empty strings are
|
|
|
|
|
;; meaningful here.
|
|
|
|
|
(split-string
|
|
|
|
|
(replace-regexp-in-string
|
|
|
|
|
"\\(\\\\*\\),"
|
|
|
|
|
(lambda (str)
|
|
|
|
|
(let ((len (length (match-string 1 str))))
|
|
|
|
|
(concat (make-string (/ len 2) ?\\)
|
|
|
|
|
(if (zerop (mod len 2)) "\000" ","))))
|
|
|
|
|
s nil t)
|
|
|
|
|
"\000"))
|
|
|
|
|
|
2017-09-18 12:01:12 +02:00
|
|
|
|
|
|
|
|
|
;;; Helper functions and variables for internal macros
|
|
|
|
|
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(defun org-macro--vc-modified-time (file)
|
|
|
|
|
(save-window-excursion
|
|
|
|
|
(when (vc-backend file)
|
|
|
|
|
(let ((buf (get-buffer-create " *org-vc*"))
|
|
|
|
|
(case-fold-search t)
|
|
|
|
|
date)
|
|
|
|
|
(unwind-protect
|
|
|
|
|
(progn
|
|
|
|
|
(vc-call print-log file buf nil nil 1)
|
|
|
|
|
(with-current-buffer buf
|
|
|
|
|
(vc-exec-after
|
|
|
|
|
(lambda ()
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(when (re-search-forward "Date:?[ \t]*" nil t)
|
|
|
|
|
(let ((time (parse-time-string
|
|
|
|
|
(buffer-substring
|
|
|
|
|
(point) (line-end-position)))))
|
|
|
|
|
(when (cl-some #'identity time)
|
Simplify use of encode-time
Most uses of (apply #'encode-time foo) can now be replaced
with (encode-time foo). Make similar simplifications.
* lisp/calendar/time-date.el (date-to-time):
* lisp/calendar/timeclock.el (timeclock-when-to-leave)
(timeclock-day-base, timeclock-generate-report):
* lisp/emacs-lisp/timer.el (timer-set-idle-time):
* lisp/eshell/esh-util.el (eshell-parse-ange-ls):
* lisp/gnus/gnus-art.el (article-make-date-line):
* lisp/gnus/gnus-delay.el (gnus-delay-article)
(gnus-delay-send-queue):
* lisp/gnus/gnus-icalendar.el (gnus-icalendar-event--decode-datefield):
* lisp/gnus/gnus-logic.el (gnus-advanced-date):
* lisp/gnus/message.el (message-make-expires-date):
* lisp/gnus/nndiary.el (nndiary-compute-reminders):
* lisp/mail/ietf-drums.el (ietf-drums-parse-date):
* lisp/net/tramp-adb.el (tramp-adb-ls-output-time-less-p):
* lisp/org/org-agenda.el (org-agenda-get-timestamps)
(org-agenda-get-progress, org-agenda-show-clocking-issues):
* lisp/org/org-capture.el (org-capture-set-target-location):
* lisp/org/org-clock.el (org-clock-get-sum-start, org-clock-sum)
(org-clocktable-steps):
* lisp/org/org-colview.el (org-colview-construct-allowed-dates)
* lisp/org/org-macro.el (org-macro--vc-modified-time):
* lisp/org/org-table.el (org-table-eval-formula):
* lisp/org/org.el (org-current-time, org-store-link)
(org-time-today, org-read-date, org-read-date-display)
(org-display-custom-time, org-time-string-to-time)
(org-timestamp-change, org-timestamp--to-internal-time):
* lisp/url/url-dav.el (url-dav-process-date-property):
* lisp/vc/vc-cvs.el (vc-cvs-annotate-current-time)
(vc-cvs-parse-entry):
Simplify use of encode-time.
* lisp/org/org-clock.el (org-clock-get-clocked-time):
(org-clock-resolve, org-resolve-clocks, org_clock_out)
(org-clock-update-time-maybe):
Avoid some rounding problems with encode-time and float-time.
* lisp/org/org-clock.el (org-clock-in, org-clock-update-time-maybe):
* lisp/org/org-colview.el (org-columns--age-to-minutes):
* lisp/org/org.el (org-get-scheduled-time, org-get-deadline-time)
(org-add-planning-info, org-2ft, org-time-string-to-absolute)
(org-closest-date):
Use org-time-string-to-time instead of doing it by hand with
encode-time.
* lisp/org/org.el (org-current-time): Simplify rounding.
(org-read-date): Avoid extra trip through encode-time.
2019-02-10 20:25:22 -08:00
|
|
|
|
(setq date (encode-time time))))))))
|
2017-06-21 13:20:20 +02:00
|
|
|
|
(let ((proc (get-buffer-process buf)))
|
|
|
|
|
(while (and proc (accept-process-output proc .5 nil t)))))
|
|
|
|
|
(kill-buffer buf))
|
|
|
|
|
date))))
|
2013-11-12 14:06:26 +01:00
|
|
|
|
|
2017-09-18 12:01:12 +02:00
|
|
|
|
(defvar org-macro--counter-table nil
|
|
|
|
|
"Hash table containing counter value per name.")
|
|
|
|
|
|
|
|
|
|
(defun org-macro--counter-initialize ()
|
|
|
|
|
"Initialize `org-macro--counter-table'."
|
|
|
|
|
(setq org-macro--counter-table (make-hash-table :test #'equal)))
|
|
|
|
|
|
|
|
|
|
(defun org-macro--counter-increment (name &optional action)
|
|
|
|
|
"Increment counter NAME.
|
|
|
|
|
NAME is a string identifying the counter.
|
|
|
|
|
|
|
|
|
|
When non-nil, optional argument ACTION is a string.
|
|
|
|
|
|
|
|
|
|
If the string is \"-\", keep the NAME counter at its current
|
|
|
|
|
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))
|
|
|
|
|
(action-trimmed (when (org-string-nw-p action)
|
|
|
|
|
(org-trim action))))
|
|
|
|
|
(puthash name-trimmed
|
|
|
|
|
(cond ((not (org-string-nw-p action-trimmed))
|
|
|
|
|
(1+ (gethash name-trimmed org-macro--counter-table 0)))
|
|
|
|
|
((string= "-" action-trimmed)
|
|
|
|
|
(gethash name-trimmed org-macro--counter-table 1))
|
|
|
|
|
((string-match-p "\\`[0-9]+\\'" action-trimmed)
|
|
|
|
|
(string-to-number action-trimmed))
|
|
|
|
|
(t 1))
|
|
|
|
|
org-macro--counter-table)))
|
|
|
|
|
|
2013-11-12 14:06:26 +01:00
|
|
|
|
|
|
|
|
|
(provide 'org-macro)
|
|
|
|
|
;;; org-macro.el ends here
|