Fix `org-set-tags-to'.
* lisp/org.el (org-set-tags-to): Do not throw an error on empty headlines. * testing/lisp/test-org.el (test-org/set-tags): Move test. (test-org/set-tags-to): New test. Reported-by: Adrian Bradd <a.bradd@columbia.edu> <http://lists.gnu.org/r/emacs-orgmode/2018-03/msg00210.html>
This commit is contained in:
parent
2f587d496f
commit
ea4cb14883
50
lisp/org.el
50
lisp/org.el
|
@ -14806,36 +14806,28 @@ Assume point is on a headline."
|
||||||
(org-set-tags arg just-align))))
|
(org-set-tags arg just-align))))
|
||||||
|
|
||||||
(defun org-set-tags-to (data)
|
(defun org-set-tags-to (data)
|
||||||
"Set the tags of the current entry to DATA, replacing the current tags.
|
"Set the tags of the current entry to DATA, replacing current tags.
|
||||||
DATA may be a tags string like :aa:bb:cc:, or a list of tags.
|
DATA may be a tags string like \":aa:bb:cc:\", or a list of tags.
|
||||||
If DATA is nil or the empty string, any tags will be removed."
|
If DATA is nil or the empty string, all tags are removed."
|
||||||
(interactive "sTags: ")
|
(interactive "sTags: ")
|
||||||
(setq data
|
(let ((data
|
||||||
(cond
|
(pcase (if (stringp data) (org-trim data) data)
|
||||||
((eq data nil) "")
|
((or `nil "") nil)
|
||||||
((equal data "") "")
|
((pred listp) (format ":%s:" (mapconcat #'identity data ":")))
|
||||||
((stringp data)
|
((pred stringp)
|
||||||
(concat ":" (mapconcat 'identity (org-split-string data ":+") ":")
|
(format ":%s:"
|
||||||
":"))
|
(mapconcat #'identity (org-split-string data ":+") ":")))
|
||||||
((listp data)
|
(_ (error "Invalid tag specification: %S" data)))))
|
||||||
(concat ":" (mapconcat 'identity data ":") ":"))))
|
(org-with-wide-buffer
|
||||||
(when data
|
(org-back-to-heading t)
|
||||||
(save-excursion
|
(let ((case-fold-search nil)) (looking-at org-complex-heading-regexp))
|
||||||
(org-back-to-heading t)
|
(when (or (match-end 5) data)
|
||||||
(when (let ((case-fold-search nil))
|
(goto-char (or (match-beginning 5) (line-end-position)))
|
||||||
(looking-at org-complex-heading-regexp))
|
(skip-chars-backward " \t")
|
||||||
(if (match-end 5)
|
(delete-region (point) (line-end-position))
|
||||||
(progn
|
(when data
|
||||||
(goto-char (match-beginning 5))
|
(insert " " data)
|
||||||
(insert data)
|
(org-set-tags nil 'align))))))
|
||||||
(delete-region (point) (point-at-eol))
|
|
||||||
(org-set-tags nil 'align))
|
|
||||||
(goto-char (point-at-eol))
|
|
||||||
(insert " " data)
|
|
||||||
(org-set-tags nil 'align)))
|
|
||||||
(beginning-of-line 1)
|
|
||||||
(when (looking-at ".*?\\([ \t]+\\)$")
|
|
||||||
(delete-region (match-beginning 1) (match-end 1))))))
|
|
||||||
|
|
||||||
(defun org-align-all-tags ()
|
(defun org-align-all-tags ()
|
||||||
"Align the tags in all headings."
|
"Align the tags in all headings."
|
||||||
|
|
|
@ -5953,6 +5953,88 @@ Paragraph<point>"
|
||||||
"* T<point>est :foo:bar:"
|
"* T<point>est :foo:bar:"
|
||||||
(org-get-tags-at)))))
|
(org-get-tags-at)))))
|
||||||
|
|
||||||
|
(ert-deftest test-org/set-tags ()
|
||||||
|
"Test `org-set-tags' specifications."
|
||||||
|
;; Tags set via fast-tag-selection should be visible afterwards
|
||||||
|
(should
|
||||||
|
(let ((org-tag-alist '(("NEXT" . ?n)))
|
||||||
|
(org-fast-tag-selection-single-key t))
|
||||||
|
(cl-letf (((symbol-function 'read-char-exclusive) (lambda () ?n))
|
||||||
|
((symbol-function 'window-width) (lambda (&rest args) 100)))
|
||||||
|
(org-test-with-temp-text "<point>* Headline\nAnd its content\n* And another headline\n\nWith some content"
|
||||||
|
;; Show only headlines
|
||||||
|
(org-content)
|
||||||
|
;; Set NEXT tag on current entry
|
||||||
|
(org-set-tags nil nil)
|
||||||
|
;; Move point to that NEXT tag
|
||||||
|
(search-forward "NEXT") (backward-word)
|
||||||
|
;; And it should be visible (i.e. no overlays)
|
||||||
|
(not (overlays-at (point))))))))
|
||||||
|
|
||||||
|
(ert-deftest test-org/set-tags-to ()
|
||||||
|
"Test `org-set-tags-to' specifications."
|
||||||
|
;; Throw an error on invalid data.
|
||||||
|
(should-error
|
||||||
|
(org-test-with-temp-text "* H"
|
||||||
|
(org-set-tags-to 'foo)))
|
||||||
|
;; `nil', an empty, and a blank string remove all tags.
|
||||||
|
(should
|
||||||
|
(equal "* H"
|
||||||
|
(org-test-with-temp-text "* H :tag1:tag2:"
|
||||||
|
(org-set-tags-to nil)
|
||||||
|
(buffer-string))))
|
||||||
|
(should
|
||||||
|
(equal "* H"
|
||||||
|
(org-test-with-temp-text "* H :tag1:tag2:"
|
||||||
|
(org-set-tags-to "")
|
||||||
|
(buffer-string))))
|
||||||
|
(should
|
||||||
|
(equal "* H"
|
||||||
|
(org-test-with-temp-text "* H :tag1:tag2:"
|
||||||
|
(org-set-tags-to " ")
|
||||||
|
(buffer-string))))
|
||||||
|
;; If there's nothing to remove, just bail out.
|
||||||
|
(should
|
||||||
|
(equal "* H"
|
||||||
|
(org-test-with-temp-text "* H"
|
||||||
|
(org-set-tags-to nil)
|
||||||
|
(buffer-string))))
|
||||||
|
(should
|
||||||
|
(equal "* "
|
||||||
|
(org-test-with-temp-text "* "
|
||||||
|
(org-set-tags-to nil)
|
||||||
|
(buffer-string))))
|
||||||
|
;; If DATA is a tag string, set current tags to it, even if it means
|
||||||
|
;; replacing old tags.
|
||||||
|
(should
|
||||||
|
(equal "* H :tag0:"
|
||||||
|
(org-test-with-temp-text "* H :tag1:tag2:"
|
||||||
|
(org-set-tags-to ":tag0:")
|
||||||
|
(buffer-string))))
|
||||||
|
(should
|
||||||
|
(equal "* H :tag0:"
|
||||||
|
(org-test-with-temp-text "* H"
|
||||||
|
(org-set-tags-to ":tag0:")
|
||||||
|
(buffer-string))))
|
||||||
|
;; If DATA is a list, set tags to this list, even if it means
|
||||||
|
;; replacing old tags.
|
||||||
|
(should
|
||||||
|
(equal "* H :tag0:"
|
||||||
|
(org-test-with-temp-text "* H :tag1:tag2:"
|
||||||
|
(org-set-tags-to '("tag0"))
|
||||||
|
(buffer-string))))
|
||||||
|
(should
|
||||||
|
(equal "* H :tag0:"
|
||||||
|
(org-test-with-temp-text "* H"
|
||||||
|
(org-set-tags-to '("tag0"))
|
||||||
|
(buffer-string))))
|
||||||
|
;; Special case: handle empty headlines.
|
||||||
|
(should
|
||||||
|
(equal "* :tag0:"
|
||||||
|
(org-test-with-temp-text "* "
|
||||||
|
(org-set-tags-to '("tag0"))
|
||||||
|
(buffer-string)))))
|
||||||
|
|
||||||
|
|
||||||
;;; TODO keywords
|
;;; TODO keywords
|
||||||
|
|
||||||
|
@ -6573,24 +6655,6 @@ CLOCK: [2012-03-29 Thu 10:00]--[2012-03-29 Thu 16:40] => 6:40"
|
||||||
(should-not
|
(should-not
|
||||||
(org-test-with-temp-text "Paragraph" (org-hide-block-toggle-maybe))))
|
(org-test-with-temp-text "Paragraph" (org-hide-block-toggle-maybe))))
|
||||||
|
|
||||||
(ert-deftest test-org/set-tags ()
|
|
||||||
"Test `org-set-tags' specifications."
|
|
||||||
;; Tags set via fast-tag-selection should be visible afterwards
|
|
||||||
(should
|
|
||||||
(let ((org-tag-alist '(("NEXT" . ?n)))
|
|
||||||
(org-fast-tag-selection-single-key t))
|
|
||||||
(cl-letf (((symbol-function 'read-char-exclusive) (lambda () ?n))
|
|
||||||
((symbol-function 'window-width) (lambda (&rest args) 100)))
|
|
||||||
(org-test-with-temp-text "<point>* Headline\nAnd its content\n* And another headline\n\nWith some content"
|
|
||||||
;; Show only headlines
|
|
||||||
(org-content)
|
|
||||||
;; Set NEXT tag on current entry
|
|
||||||
(org-set-tags nil nil)
|
|
||||||
;; Move point to that NEXT tag
|
|
||||||
(search-forward "NEXT") (backward-word)
|
|
||||||
;; And it should be visible (i.e. no overlays)
|
|
||||||
(not (overlays-at (point))))))))
|
|
||||||
|
|
||||||
(ert-deftest test-org/show-set-visibility ()
|
(ert-deftest test-org/show-set-visibility ()
|
||||||
"Test `org-show-set-visibility' specifications."
|
"Test `org-show-set-visibility' specifications."
|
||||||
;; Do not throw an error before first heading.
|
;; Do not throw an error before first heading.
|
||||||
|
|
Loading…
Reference in New Issue