diff options
author | Bastien <bzg@gnu.org> | 2018-11-12 07:03:45 +0100 |
---|---|---|
committer | Bastien <bzg@gnu.org> | 2018-11-12 07:03:45 +0100 |
commit | eb57221e5db728f42deedbfadc4911e8b76a55c7 (patch) | |
tree | 1949db7ac144c9555135cea576e6ff5c96e1af9d | |
parent | e6739e08b4bcc90997d6985d83b20318973df7ac (diff) | |
parent | afcb1d3899f5c34b444c5ddd6a8930894fa55065 (diff) | |
download | org-mode-eb57221e5db728f42deedbfadc4911e8b76a55c7.tar.gz |
Merge branch 'master' of code.orgmode.org:bzg/org-mode
-rw-r--r-- | lisp/org-compat.el | 5 | ||||
-rw-r--r-- | lisp/org-lint.el | 2 | ||||
-rw-r--r-- | lisp/org-macro.el | 2 | ||||
-rw-r--r-- | lisp/org-macs.el | 15 | ||||
-rw-r--r-- | lisp/org.el | 435 | ||||
-rw-r--r-- | lisp/ox-odt.el | 28 | ||||
-rw-r--r-- | lisp/ox-texinfo.el | 2 | ||||
-rw-r--r-- | lisp/ox.el | 13 | ||||
-rw-r--r-- | testing/lisp/test-org.el | 57 |
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))) @@ -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 |