summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien <bzg@gnu.org>2018-11-12 07:03:45 +0100
committerBastien <bzg@gnu.org>2018-11-12 07:03:45 +0100
commiteb57221e5db728f42deedbfadc4911e8b76a55c7 (patch)
tree1949db7ac144c9555135cea576e6ff5c96e1af9d
parente6739e08b4bcc90997d6985d83b20318973df7ac (diff)
parentafcb1d3899f5c34b444c5ddd6a8930894fa55065 (diff)
downloadorg-mode-eb57221e5db728f42deedbfadc4911e8b76a55c7.tar.gz
Merge branch 'master' of code.orgmode.org:bzg/org-mode
-rw-r--r--lisp/org-compat.el5
-rw-r--r--lisp/org-lint.el2
-rw-r--r--lisp/org-macro.el2
-rw-r--r--lisp/org-macs.el15
-rw-r--r--lisp/org.el435
-rw-r--r--lisp/ox-odt.el28
-rw-r--r--lisp/ox-texinfo.el2
-rw-r--r--lisp/ox.el13
-rw-r--r--testing/lisp/test-org.el57
9 files changed, 281 insertions, 278 deletions
diff --git a/lisp/org-compat.el b/lisp/org-compat.el
index f180f11..77bf036 100644
--- a/lisp/org-compat.el
+++ b/lisp/org-compat.el
@@ -222,6 +222,7 @@ Counting starts at 1."
'org-activate-links "Org 9.0")
(define-obsolete-function-alias 'org-activate-plain-links 'ignore "Org 9.0")
(define-obsolete-function-alias 'org-activate-angle-links 'ignore "Org 9.0")
+(define-obsolete-function-alias 'org-remove-double-quotes 'org-strip-quotes "Org 9.0")
(define-obsolete-function-alias 'org-get-indentation
'current-indentation "Org 9.2")
(define-obsolete-function-alias 'org-capture-member 'org-capture-get "Org 9.2")
@@ -327,10 +328,6 @@ See `org-link-parameters' for documentation on the other parameters."
(org-unbracket-string "<" ">" s))
(make-obsolete 'org-remove-angle-brackets 'org-unbracket-string "Org 9.0")
-(defun org-remove-double-quotes (s)
- (org-unbracket-string "\"" "\"" s))
-(make-obsolete 'org-remove-double-quotes 'org-unbracket-string "Org 9.0")
-
(defcustom org-publish-sitemap-file-entry-format "%t"
"Format string for site-map file entry.
You could use brackets to delimit on what part the link will be.
diff --git a/lisp/org-lint.el b/lisp/org-lint.el
index a311b1d..f22a6b3 100644
--- a/lisp/org-lint.el
+++ b/lisp/org-lint.el
@@ -589,7 +589,7 @@ Use :header-args: instead"
(path
(and (string-match "^\\(\".+\"\\|\\S-+\\)[ \t]*" value)
(save-match-data
- (org-unbracket-string "\"" "\"" (match-string 1 value))))))
+ (org-strip-quotes (match-string 1 value))))))
(if (not path)
(list (org-element-property :post-affiliated k)
"Missing location argument in INCLUDE keyword")
diff --git a/lisp/org-macro.el b/lisp/org-macro.el
index ec686d9..10e35cc 100644
--- a/lisp/org-macro.el
+++ b/lisp/org-macro.el
@@ -110,7 +110,7 @@ Return an alist containing all macro templates found."
(if old-cell (setcdr old-cell template)
(push (cons name template) templates))))
;; Enter setup file.
- (let* ((uri (org-unbracket-string "\"" "\"" (org-trim val)))
+ (let* ((uri (org-strip-quotes (org-trim val)))
(uri-is-url (org-file-url-p uri))
(uri (if uri-is-url
uri
diff --git a/lisp/org-macs.el b/lisp/org-macs.el
index c731cef..6da69c7 100644
--- a/lisp/org-macs.el
+++ b/lisp/org-macs.el
@@ -899,14 +899,17 @@ Otherwise return nil."
(defun org-unbracket-string (pre post string)
"Remove PRE/POST from the beginning/end of STRING.
Both PRE and POST must be pre-/suffixes of STRING, or neither is
-removed."
- (if (and (string-prefix-p pre string)
- (string-suffix-p post string))
- (substring string (length pre) (- (length post)))
- string))
+removed. Return the new string. If STRING is nil, return nil."
+ (declare (indent 2))
+ (and string
+ (if (and (string-prefix-p pre string)
+ (string-suffix-p post string))
+ (substring string (length pre) (- (length post)))
+ string)))
(defun org-strip-quotes (string)
- "Strip double quotes from around a string, if applicable."
+ "Strip double quotes from around STRING, if applicable.
+If STRING is nil, return nil."
(org-unbracket-string "\"" "\"" string))
(defsubst org-current-line-string (&optional to-here)
diff --git a/lisp/org.el b/lisp/org.el
index 33c8467..eb1affb 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -674,7 +674,8 @@ on a string that terminates immediately after the date.")
The time stamps may be either active or inactive.")
(defconst org-repeat-re
- "<[0-9]\\{4\\}-[0-9][0-9]-[0-9][0-9] [^>\n]*?\\([.+]?\\+[0-9]+[hdwmy]\\(/[0-9]+[hdwmy]\\)?\\)"
+ "[[<][0-9]\\{4\\}-[0-9][0-9]-[0-9][0-9] [^]>\n]*?\
+\\([.+]?\\+[0-9]+[hdwmy]\\(/[0-9]+[hdwmy]\\)?\\)"
"Regular expression for specifying repeated events.
After a match, group 1 contains the repeat expression.")
@@ -5173,8 +5174,7 @@ Return value contains the following keys: `archive', `category',
((equal key "SETUPFILE")
(unless buffer-read-only ; Do not check in Gnus messages.
(let ((f (and (org-string-nw-p value)
- (expand-file-name
- (org-unbracket-string "\"" "\"" value)))))
+ (expand-file-name (org-strip-quotes value)))))
(when (and f (file-readable-p f) (not (member f files)))
(with-temp-buffer
(setq default-directory (file-name-directory f))
@@ -12731,117 +12731,110 @@ This function is run automatically after each state change to a DONE state."
(org-log-done nil)
(org-todo-log-states nil)
(end (copy-marker (org-entry-end-position))))
- (unwind-protect
- (when (and repeat (not (zerop (string-to-number (substring repeat 1)))))
- (when (eq org-log-repeat t) (setq org-log-repeat 'state))
- (let ((to-state (or (org-entry-get nil "REPEAT_TO_STATE" 'selective)
- (and (stringp org-todo-repeat-to-state)
- org-todo-repeat-to-state)
- (and org-todo-repeat-to-state org-last-state))))
- (org-todo (cond
- ((and to-state (member to-state org-todo-keywords-1))
- to-state)
- ((eq interpret 'type) org-last-state)
- (head)
- (t 'none))))
- (org-back-to-heading t)
- (org-add-planning-info nil nil 'closed)
- ;; When `org-log-repeat' is non-nil or entry contains
- ;; a clock, set LAST_REPEAT property.
- (when (or org-log-repeat
- (catch :clock
- (save-excursion
- (while (re-search-forward org-clock-line-re end t)
- (when (org-at-clock-log-p) (throw :clock t))))))
- (org-entry-put nil "LAST_REPEAT" (format-time-string
- (org-time-stamp-format t t)
- (current-time))))
- (when org-log-repeat
- (if (or (memq 'org-add-log-note (default-value 'post-command-hook))
- (memq 'org-add-log-note post-command-hook))
- ;; We are already setup for some record.
- (when (eq org-log-repeat 'note)
- ;; Make sure we take a note, not only a time stamp.
- (setq org-log-note-how 'note))
- ;; Set up for taking a record.
- (org-add-log-setup 'state
- (or done-word (car org-done-keywords))
- org-last-state
- org-log-repeat)))
- (let ((planning-re (regexp-opt
- (list org-scheduled-string org-deadline-string))))
- (while (re-search-forward org-ts-regexp end t)
- (let* ((ts (match-string 0))
- (planning? (org-at-planning-p))
- (type (if (not planning?) "Plain:"
- (save-excursion
- (re-search-backward
- planning-re (line-beginning-position) t)
- (match-string 0)))))
- (cond
- ;; Ignore fake time-stamps (e.g., within comments).
- ((not (org-at-timestamp-p 'agenda)))
- ;; Time-stamps without a repeater are usually
- ;; skipped. However, a SCHEDULED time-stamp without
- ;; one is removed, as they are no longer relevant.
- ((not (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([hdwmy]\\)"
- ts))
- (when (equal type org-scheduled-string)
- (org-remove-timestamp-with-keyword type)))
- (t
- (let ((n (string-to-number (match-string 2 ts)))
- (what (match-string 3 ts)))
- (when (equal what "w") (setq n (* n 7) what "d"))
- (when (and (equal what "h")
- (not (string-match-p "[0-9]\\{1,2\\}:[0-9]\\{2\\}"
- ts)))
- (user-error
- "Cannot repeat in Repeat in %d hour(s) because no hour \
-has been set"
- n))
- ;; Preparation, see if we need to modify the start
- ;; date for the change.
- (when (match-end 1)
- (let ((time (save-match-data
- (org-time-string-to-time ts))))
- (cond
- ((equal (match-string 1 ts) ".")
- ;; Shift starting date to today
- (org-timestamp-change
- (- (org-today) (time-to-days time))
- 'day))
- ((equal (match-string 1 ts) "+")
- (let ((nshiftmax 10)
- (nshift 0))
- (while (or (= nshift 0)
- (not (time-less-p (current-time) time)))
- (when (= (cl-incf nshift) nshiftmax)
- (or (y-or-n-p
- (format "%d repeater intervals were not \
+ (when (and repeat (not (= 0 (string-to-number (substring repeat 1)))))
+ (when (eq org-log-repeat t) (setq org-log-repeat 'state))
+ (let ((to-state (or (org-entry-get nil "REPEAT_TO_STATE" 'selective)
+ (and (stringp org-todo-repeat-to-state)
+ org-todo-repeat-to-state)
+ (and org-todo-repeat-to-state org-last-state))))
+ (org-todo (cond ((and to-state (member to-state org-todo-keywords-1))
+ to-state)
+ ((eq interpret 'type) org-last-state)
+ (head)
+ (t 'none))))
+ (org-back-to-heading t)
+ (org-add-planning-info nil nil 'closed)
+ ;; When `org-log-repeat' is non-nil or entry contains
+ ;; a clock, set LAST_REPEAT property.
+ (when (or org-log-repeat
+ (catch :clock
+ (save-excursion
+ (while (re-search-forward org-clock-line-re end t)
+ (when (org-at-clock-log-p) (throw :clock t))))))
+ (org-entry-put nil "LAST_REPEAT" (format-time-string
+ (org-time-stamp-format t t)
+ (current-time))))
+ (when org-log-repeat
+ (if (or (memq 'org-add-log-note (default-value 'post-command-hook))
+ (memq 'org-add-log-note post-command-hook))
+ ;; We are already setup for some record.
+ (when (eq org-log-repeat 'note)
+ ;; Make sure we take a note, not only a time stamp.
+ (setq org-log-note-how 'note))
+ ;; Set up for taking a record.
+ (org-add-log-setup 'state
+ (or done-word (car org-done-keywords))
+ org-last-state
+ org-log-repeat)))
+ (let ((planning-re (regexp-opt
+ (list org-scheduled-string org-deadline-string))))
+ (while (re-search-forward org-ts-regexp-both end t)
+ (let* ((ts (match-string 0))
+ (planning? (org-at-planning-p))
+ (type (if (not planning?) "Plain:"
+ (save-excursion
+ (re-search-backward
+ planning-re (line-beginning-position) t)
+ (match-string 0)))))
+ (cond
+ ;; Ignore fake time-stamps (e.g., within comments).
+ ((not (org-at-timestamp-p 'agenda)))
+ ((string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([hdwmy]\\)" ts)
+ (let ((n (string-to-number (match-string 2 ts)))
+ (what (match-string 3 ts)))
+ (when (equal what "w") (setq n (* n 7) what "d"))
+ (when (and (equal what "h")
+ (not (string-match-p "[0-9]\\{1,2\\}:[0-9]\\{2\\}"
+ ts)))
+ (user-error
+ "Cannot repeat in %d hour(s) because no hour has been set"
+ n))
+ ;; Preparation, see if we need to modify the start
+ ;; date for the change.
+ (when (match-end 1)
+ (let ((time (save-match-data (org-time-string-to-time ts)))
+ (repeater-type (match-string 1 ts)))
+ (cond
+ ((equal "." repeater-type)
+ ;; Shift starting date to today.
+ (org-timestamp-change (- (org-today) (time-to-days time))
+ 'day))
+ ((equal "+" repeater-type)
+ (let ((nshiftmax 10)
+ (nshift 0))
+ (while (or (= nshift 0)
+ (not (time-less-p (current-time) time)))
+ (when (= nshiftmax (cl-incf nshift))
+ (or (y-or-n-p
+ (format "%d repeater intervals were not \
enough to shift date past today. Continue? "
- nshift))
- (user-error "Abort")))
- (org-timestamp-change n (cdr (assoc what whata)))
- (org-in-regexp org-ts-regexp3)
- (setq ts (match-string 1))
- (setq time
- (save-match-data
- (org-time-string-to-time ts)))))
- (org-timestamp-change (- n) (cdr (assoc what whata)))
- ;; Rematch, so that we have everything in place
- ;; for the real shift.
+ nshift))
+ (user-error "Abort")))
+ (org-timestamp-change n (cdr (assoc what whata)))
(org-in-regexp org-ts-regexp3)
(setq ts (match-string 1))
- (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([hdwmy]\\)"
- ts)))))
- (save-excursion
- (org-timestamp-change n (cdr (assoc what whata)) nil t))
- (setq msg
- (concat
- msg type " " org-last-changed-timestamp " "))))))))
- (setq org-log-post-message msg)
- (message "%s" msg))
- (set-marker end nil))))
+ (setq time
+ (save-match-data
+ (org-time-string-to-time ts)))))
+ (org-timestamp-change (- n) (cdr (assoc what whata)))
+ ;; Rematch, so that we have everything in place
+ ;; for the real shift.
+ (org-in-regexp org-ts-regexp3)
+ (setq ts (match-string 1))
+ (string-match "\\([.+]\\)?\\(\\+[0-9]+\\)\\([hdwmy]\\)"
+ ts)))))
+ (save-excursion
+ (org-timestamp-change n (cdr (assoc what whata)) nil t))
+ (setq msg
+ (concat msg type " " org-last-changed-timestamp " "))))
+ (t
+ ;; Time-stamps without a repeater are usually skipped.
+ ;; However, a SCHEDULED time-stamp without one is
+ ;; removed, as they are no longer relevant.
+ (when (equal type org-scheduled-string)
+ (org-remove-timestamp-with-keyword type)))))))
+ (setq org-log-post-message msg)
+ (message "%s" msg))))
(defun org-show-todo-tree (arg)
"Make a compact tree which shows all headlines marked with TODO.
@@ -14083,7 +14076,20 @@ See also `org-scan-tags'."
(setq matcher `(and (member todo org-not-done-keywords) ,matcher)))
(cons match0 `(lambda (todo tags-list level) ,matcher)))))
-(defun org-tags-expand (match &optional single-as-list downcased tags-already-expanded)
+(defun org--tags-expand-group (group tag-groups expanded)
+ "Recursively Expand all tags in GROUP, according to TAG-GROUPS.
+TAG-GROUPS is the list of groups used for expansion. EXPANDED is
+an accumulator used in recursive calls."
+ (dolist (tag group)
+ (unless (member tag expanded)
+ (let ((group (assoc tag tag-groups)))
+ (push tag expanded)
+ (when group
+ (setq expanded
+ (org--tags-expand-group (cdr group) tag-groups expanded))))))
+ expanded)
+
+(defun org-tags-expand (match &optional single-as-list downcased)
"Expand group tags in MATCH.
This replaces every group tag in MATCH with a regexp tag search.
@@ -14100,7 +14106,7 @@ E.g., this expansion
Work|Home => {\\(?:Work\\|Lab\\|Conf\\}|Home
will match anything tagged with \"Lab\" and \"Home\", or tagged
-with \"Conf\" and \"Home\" or tagged with \"Work\" and \"home\".
+with \"Conf\" and \"Home\" or tagged with \"Work\" and \"Home\".
A group tag in MATCH can contain regular expressions of its own.
For example, a group tag \"Proj\" defined as { Proj : {P@.+} }
@@ -14112,118 +14118,61 @@ When the optional argument SINGLE-AS-LIST is non-nil, MATCH is
assumed to be a single group tag, and the function will return
the list of tags in this group.
-When DOWNCASE is non-nil, expand downcased TAGS."
- (if org-group-tags
+When DOWNCASED is non-nil, expand downcased TAGS."
+ (unless (org-string-nw-p match) (error "Invalid match tag: %S" match))
+ (let ((tag-groups
+ (let ((g (or org-tag-groups-alist-for-agenda org-tag-groups-alist)))
+ (if (not downcased) g
+ (mapcar (lambda (s) (mapcar #'downcase s)) g)))))
+ (cond
+ (single-as-list (org--tags-expand-group (list match) tag-groups nil))
+ (org-group-tags
(let* ((case-fold-search t)
- (stable org-mode-syntax-table)
- (taggroups (or org-tag-groups-alist-for-agenda org-tag-groups-alist))
- (taggroups (if downcased
- (mapcar (lambda (tg) (mapcar #'downcase tg))
- taggroups)
- taggroups))
- (taggroups-keys (mapcar #'car taggroups))
- (return-match (if downcased (downcase match) match))
- (count 0)
- (work-already-expanded tags-already-expanded)
- regexps-in-match tags-in-group regexp-in-group regexp-in-group-escaped)
+ (tag-syntax org-mode-syntax-table)
+ (group-keys (mapcar #'car tag-groups))
+ (key-regexp (concat "\\([+-]?\\)" (regexp-opt group-keys 'words)))
+ (return-match (if downcased (downcase match) match)))
+ ;; Mark regexp-expressions in the match-expression so that we
+ ;; do not replace them later on.
+ (let ((s 0))
+ (while (string-match "{.+?}" return-match s)
+ (setq s (match-end 0))
+ (add-text-properties
+ (match-beginning 0) (match-end 0) '(regexp t) return-match)))
;; @ and _ are allowed as word-components in tags.
- (modify-syntax-entry ?@ "w" stable)
- (modify-syntax-entry ?_ "w" stable)
- ;; Temporarily replace regexp-expressions in the match-expression.
- (while (string-match "{.+?}" return-match)
- (cl-incf count)
- (push (match-string 0 return-match) regexps-in-match)
- (setq return-match (replace-match (format "<%d>" count) t nil return-match)))
- (while (and taggroups-keys
- (with-syntax-table stable
- (string-match
- (concat "\\(?1:[+-]?\\)\\(?2:\\<"
- (regexp-opt taggroups-keys) "\\>\\)")
- return-match)))
- (let* ((dir (match-string 1 return-match))
- (tag (match-string 2 return-match))
- (tag (if downcased (downcase tag) tag)))
- (unless (or (get-text-property 0 'grouptag (match-string 2 return-match))
- (member tag tags-already-expanded))
- (setq tags-in-group (assoc tag taggroups))
- (push tag work-already-expanded)
- ;; Recursively expand each tag in the group, if the tag hasn't
- ;; already been expanded. Restore the match-data after all recursive calls.
- (save-match-data
- (let (tags-expanded)
- (dolist (x (cdr tags-in-group))
- (if (and (member x taggroups-keys)
- (not (member x work-already-expanded)))
- (setq tags-expanded
- (delete-dups
- (append
- (org-tags-expand x t downcased
- work-already-expanded)
- tags-expanded)))
- (setq tags-expanded
- (append (list x) tags-expanded)))
- (setq work-already-expanded
- (delete-dups
- (append tags-expanded
- work-already-expanded))))
- (setq tags-in-group
- (delete-dups (cons (car tags-in-group)
- tags-expanded)))))
- ;; Filter tag-regexps from tags.
- (setq regexp-in-group-escaped
- (delq nil (mapcar (lambda (x)
- (if (stringp x)
- (and (equal "{" (substring x 0 1))
- (equal "}" (substring x -1))
- x)
- x))
- tags-in-group))
- regexp-in-group
- (mapcar (lambda (x)
- (substring x 1 -1))
- regexp-in-group-escaped)
- tags-in-group
- (delq nil (mapcar (lambda (x)
- (if (stringp x)
- (and (not (equal "{" (substring x 0 1)))
- (not (equal "}" (substring x -1)))
- x)
- x))
- tags-in-group)))
- ;; If single-as-list, do no more in the while-loop.
- (if (not single-as-list)
- (progn
- (when regexp-in-group
- (setq regexp-in-group
- (concat "\\|"
- (mapconcat 'identity regexp-in-group
- "\\|"))))
- (setq tags-in-group
- (concat dir
- "{\\<"
- (regexp-opt tags-in-group)
- "\\>"
- regexp-in-group
- "}"))
- (when (stringp tags-in-group)
- (org-add-props tags-in-group '(grouptag t)))
- (setq return-match
- (replace-match tags-in-group t t return-match)))
- (setq tags-in-group
- (append regexp-in-group-escaped tags-in-group))))
- (setq taggroups-keys (delete tag taggroups-keys))))
- ;; Add the regular expressions back into the match-expression again.
- (while regexps-in-match
- (setq return-match (replace-regexp-in-string (format "<%d>" count)
- (pop regexps-in-match)
- return-match t t))
- (cl-decf count))
- (if single-as-list
- (if tags-in-group tags-in-group (list return-match))
- return-match))
- (if single-as-list
- (list (if downcased (downcase match) match))
- match)))
+ (modify-syntax-entry ?@ "w" tag-syntax)
+ (modify-syntax-entry ?_ "w" tag-syntax)
+ ;; For each tag token found in MATCH, compute a regexp and it
+ (with-syntax-table tag-syntax
+ (replace-regexp-in-string
+ key-regexp
+ (lambda (m)
+ (if (get-text-property (match-beginning 2) 'regexp m)
+ m ;regexp tag: ignore
+ (let* ((operator (match-string 1 m))
+ (tag-token (let ((tag (match-string 2 m)))
+ (list (if downcased (downcase tag) tag))))
+ regexp-tags regular-tags)
+ ;; Partition tags between regexp and regular tags.
+ ;; Remove curly bracket syntax from regexp tags.
+ (dolist (tag (org--tags-expand-group tag-token tag-groups nil))
+ (save-match-data
+ (if (string-match "{\\(.+?\\)}" tag)
+ (push (match-string 1 tag) regexp-tags)
+ (push tag regular-tags))))
+ ;; Replace tag token by the appropriate regexp.
+ ;; Regular tags need to be regexp-quoted, whereas
+ ;; regexp-tags are inserted as-is.
+ (let ((regular (regexp-opt regular-tags))
+ (regexp (mapconcat #'identity regexp-tags "\\|")))
+ (concat operator
+ (cond
+ ((null regular-tags) (format "{%s}" regexp))
+ ((null regexp-tags) (format "{\\<%s\\>}" regular))
+ (t (format "{\\<%s\\>\\|%s}" regular regexp))))))))
+ return-match
+ t t))))
+ (t match))))
(defun org-op-to-function (op &optional stringp)
"Turn an operator into the appropriate function."
@@ -14726,11 +14675,10 @@ Assume point is at the beginning of the headline."
When argument POS is non-nil, retrieve tags for headline at POS.
-Accoring to `org-use-tags-inheritance', tags may be inherited
+According to `org-use-tags-inheritance', tags may be inherited
from parent headlines, and from the whole document, through
`org-file-tags'. However, when optional argument LOCAL is
-non-nil, only return tags really specified in the considered
-headline.
+non-nil, only return tags specified at the headline.
Inherited tags have the `inherited' text property."
(if (and org-trust-scanner-tags
@@ -20128,28 +20076,27 @@ Otherwise, return a user error."
(params (nth 2 info))
(session (cdr (assq :session params))))
(if (not session) (org-edit-src-code)
- ;; At a src-block with a session and function called with
- ;; an ARG: switch to the buffer related to the inferior
- ;; process.
+ ;; At a source block with a session and function called
+ ;; with an ARG: switch to the buffer related to the
+ ;; inferior process.
(switch-to-buffer
(funcall (intern (concat "org-babel-prep-session:" lang))
session params))))))
(`keyword
- (if (member (org-element-property :key element) '("INCLUDE" "SETUPFILE"))
- (org-open-link-from-string
- (format "[[%s]]"
- (expand-file-name
- (let ((value (org-element-property :value element)))
- (cond ((org-file-url-p value)
- (user-error "The file is specified as a URL, cannot be edited"))
- ((not (org-string-nw-p value))
- (user-error "No file to edit"))
- ((string-match "\\`\"\\(.*?\\)\"" value)
- (match-string 1 value))
- ((string-match "\\`[^ \t\"]\\S-*" value)
- (match-string 0 value))
- (t (user-error "No valid file specified")))))))
- (user-error "No special environment to edit here")))
+ (unless (member (org-element-property :key element)
+ '("INCLUDE" "SETUPFILE"))
+ (user-error "No special environment to edit here"))
+ (org-open-link-from-string
+ (format "[[%s]]"
+ (expand-file-name
+ (let ((value (org-strip-quotes
+ (org-element-property :value element))))
+ (cond
+ ((not (org-string-nw-p value))
+ (user-error "No file to edit"))
+ ((org-file-url-p value)
+ (user-error "Files located with a URL cannot be edited"))
+ (t value)))))))
(`table
(if (eq (org-element-property :type element) 'table.el)
(org-edit-table.el)
diff --git a/lisp/ox-odt.el b/lisp/ox-odt.el
index fd1cb1d..2626efb 100644
--- a/lisp/ox-odt.el
+++ b/lisp/ox-odt.el
@@ -27,8 +27,9 @@
(require 'cl-lib)
(require 'format-spec)
-(require 'ox)
(require 'org-compat)
+(require 'org-macs)
+(require 'ox)
(require 'table nil 'noerror)
;;; Define Back-End
@@ -1357,19 +1358,18 @@ original parsed data. INFO is a plist holding export options."
;; Update styles file.
;; Copy styles.xml. Also dump htmlfontify styles, if there is any.
;; Write styles file.
- (let* ((styles-file (plist-get info :odt-styles-file))
- (styles-file (and (org-string-nw-p styles-file)
- (org-trim styles-file)))
- (styles-file (if (string-match-p "\\`(.*)\\'" styles-file)
- (ignore-errors (read styles-file))
- styles-file))
- ;; Non-availability of styles.xml is not a critical
- ;; error. For now, throw an error.
- (styles-file (or styles-file
- (expand-file-name "OrgOdtStyles.xml"
- org-odt-styles-dir)
- (error "org-odt: Missing styles file?"))))
+ (let* ((styles-file
+ (pcase (plist-get info :odt-styles-file)
+ (`nil (expand-file-name "OrgOdtStyles.xml" org-odt-styles-dir))
+ ((and s (pred (string-match-p "\\`(.*)\\'")))
+ (condition-case nil
+ (read s)
+ (error (user-error "Invalid styles file specification: %S" s))))
+ (filename (org-strip-quotes filename)))))
(cond
+ ;; Non-availability of styles.xml is not a critical error. For
+ ;; now, throw an error.
+ ((null styles-file) (error "Missing styles file"))
((listp styles-file)
(let ((archive (nth 0 styles-file))
(members (nth 1 styles-file)))
@@ -1379,7 +1379,7 @@ original parsed data. INFO is a plist holding export options."
(let* ((image-type (file-name-extension member))
(media-type (format "image/%s" image-type)))
(org-odt-create-manifest-file-entry media-type member))))))
- ((and (stringp styles-file) (file-exists-p styles-file))
+ ((file-exists-p styles-file)
(let ((styles-file-type (file-name-extension styles-file)))
(cond
((string= styles-file-type "xml")
diff --git a/lisp/ox-texinfo.el b/lisp/ox-texinfo.el
index 825f95f..f5c4f9f 100644
--- a/lisp/ox-texinfo.el
+++ b/lisp/ox-texinfo.el
@@ -576,7 +576,7 @@ holding export options."
(concat
"\\input texinfo @c -*- texinfo -*-\n"
"@c %**start of header\n"
- (let ((file (or (plist-get info :texinfo-filename)
+ (let ((file (or (org-strip-quotes (plist-get info :texinfo-filename))
(let ((f (plist-get info :output-file)))
(and f (concat (file-name-sans-extension f) ".info"))))))
(and file (format "@setfilename %s\n" file)))
diff --git a/lisp/ox.el b/lisp/ox.el
index 45c9415..743fcd7 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -1499,7 +1499,7 @@ Assume buffer is in Org mode. Narrowing, if any, is ignored."
(cond
;; Options in `org-export-special-keywords'.
((equal key "SETUPFILE")
- (let* ((uri (org-unbracket-string "\"" "\"" (org-trim val)))
+ (let* ((uri (org-strip-quotes (org-trim val)))
(uri-is-url (org-file-url-p uri))
(uri (if uri-is-url
uri
@@ -1650,7 +1650,7 @@ an alist where associations are (VARIABLE-NAME VALUE)."
"BIND")
(push (read (format "(%s)" val)) alist)
;; Enter setup file.
- (let* ((uri (org-unbracket-string "\"" "\"" val))
+ (let* ((uri (org-strip-quotes val))
(uri-is-url (org-file-url-p uri))
(uri (if uri-is-url
uri
@@ -3283,8 +3283,8 @@ storing and resolving footnotes. It is created automatically."
(ind (current-indentation))
location
(file
- (and (string-match
- "^\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)" value)
+ (and (string-match "^\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)"
+ value)
(prog1
(save-match-data
(let ((matched (match-string 1 value)))
@@ -3293,9 +3293,8 @@ storing and resolving footnotes. It is created automatically."
(setq location (match-string 2 matched))
(setq matched
(replace-match "" nil nil matched 1)))
- (expand-file-name
- (org-unbracket-string "\"" "\"" matched)
- dir)))
+ (expand-file-name (org-strip-quotes matched)
+ dir)))
(setq value (replace-match "" nil nil value)))))
(only-contents
(and (string-match ":only-contents *\\([^: \r\t\n]\\S-*\\)?"
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 3f5aa09..669ad43 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -6468,6 +6468,54 @@ Paragraph<point>"
(org-toggle-tag "foo"))
(buffer-string)))))
+(ert-deftest test-org/tags-expand ()
+ "Test `org-tags-expand' specifications."
+ ;; Expand tag groups as a regexp enclosed withing curly brackets.
+ (should
+ (equal "{\\<[ABC]\\>}"
+ (org-test-with-temp-text "#+TAGS: [ A : B C ]"
+ (org-mode-restart)
+ (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "A")))))
+ (should
+ (equal "{\\<\\(?:Aa\\|Bb\\|Cc\\)\\>}"
+ (org-test-with-temp-text "#+TAGS: [ Aa : Bb Cc ]"
+ (org-mode-restart)
+ (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "Aa")))))
+ ;; Preserve operator before the regexp.
+ (should
+ (equal "+{\\<[ABC]\\>}"
+ (org-test-with-temp-text "#+TAGS: [ A : B C ]"
+ (org-mode-restart)
+ (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "+A")))))
+ (should
+ (equal "-{\\<[ABC]\\>}"
+ (org-test-with-temp-text "#+TAGS: [ A : B C ]"
+ (org-mode-restart)
+ (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "-A")))))
+ ;; Handle "|" syntax.
+ (should
+ (equal "{\\<[ABC]\\>}|D"
+ (org-test-with-temp-text "#+TAGS: [ A : B C ]"
+ (org-mode-restart)
+ (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "A|D")))))
+ ;; Handle nested groups.
+ (should
+ (equal "{\\<[A-D]\\>}"
+ (org-test-with-temp-text "#+TAGS: [ A : B C ]\n#+TAGS: [ B : D ]"
+ (org-mode-restart)
+ (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "A")))))
+ ;; Expand multiple occurrences of the same group.
+ (should
+ (equal "{\\<[ABC]\\>}|{\\<[ABC]\\>}"
+ (org-test-with-temp-text "#+TAGS: [ A : B C ]"
+ (org-mode-restart)
+ (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "A|A")))))
+ ;; Preserve regexp matches.
+ (should
+ (equal "{A+}"
+ (org-test-with-temp-text "#+TAGS: [ A : B C ]"
+ (org-mode-restart)
+ (let ((org-tag-alist-for-agenda nil)) (org-tags-expand "{A+}"))))))
;;; TODO keywords
@@ -6559,6 +6607,15 @@ Paragraph<point>"
(org-test-with-temp-text "* TODO H\n<2012-03-29 Thu +2h>"
(org-todo "DONE")
(buffer-string))))
+ ;; Also repeat inactive time stamps with a repeater.
+ (should
+ (string-match-p
+ "\\[2014-03-29 .* \\+2y\\]"
+ (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
+ (org-test-with-temp-text
+ "* TODO H\n[2012-03-29 Thu. +2y]"
+ (org-todo "DONE")
+ (buffer-string)))))
;; Do not repeat commented time stamps.
(should-not
(string-prefix-p