emacs-orgmode
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [O] Mutually-exclusive Org tags still inherit each other


From: Tina Russell
Subject: Re: [O] Mutually-exclusive Org tags still inherit each other
Date: Fri, 1 Mar 2019 13:47:44 -0800

Well, I think it’s unreasonable to ask users to reinforce manually and
continuously something they’ve already specified in their settings.
Besides, my example was intentionally trivial—imagine managing a large
tree and having to remember which tags are mutually exclusive to what,
all the time.

But, no matter! I have created a patch! It works great, even for edge
cases like when an entry has two tags that are set as mutually
exclusive to each other.

First, we have two new functions: one which returns the tags currently
defined as mutually exclusive, and one which checks for tags that
conflict with the given tag (with the help of the cl-loop “if” clause
and cl-set-difference).

(defun org-get-exclusive-tags (&optional alist)
  "Return a list of mutually exclusive tags occuring in ALIST.
ALIST defaults to `org-current-tag-alist'. The result is a list
of lists of strings, where each string represents a tag, and each
list of strings represents a group of mutually exclusive tags."
  ;; Most of this code was culled from `org-fast-tag-selection'
  (let ((alist (or alist org-current-tag-alist))
        groups ingroup intaggroup tag)
    (dolist (e alist groups)
      (cond
       ((eq (car e) :startgroup)
        (push '() groups) (setq ingroup t))
       ((eq (car e) :endgroup)
        (setq ingroup nil))
       ((eq (car e) :startgrouptag)
        (setq intaggroup t))
       ((eq (car e) :endgrouptag)
        (setq intaggroup nil))
       ((equal e '(:newline)))   ; do nothing
       ((equal e '(:grouptags))) ; do nothing
       (t
        (setq tag (copy-sequence (car e)))
        (when ingroup (push tag (car groups))))))))

(defun org-get-conflicting-tags (tags &optional alist)
  "For list TAGS, return tags which would conflict according to ALIST.
ALIST defaults to `org-current-tag-alist'. For more information
on mutually exclusive tags, see Info node `(org)Tag Hierarchy'."
  (let* ((alist (or alist org-current-tag-alist))
         (groups (org-get-exclusive-tags alist)))
    (cl-loop for group in groups
             if (cl-some (lambda (x) (member x group)) tags)
             append (cl-set-difference group tags :test 'equal))))

Then, we redefine org-get-tags slightly to remove conflicting tags
from the list of inherited tags.

(defun org-get-tags (&optional pos local)
;; "the usual docstring, skipped here for email brevity"
  (if (and org-trust-scanner-tags
           (or (not pos) (eq pos (point)))
           (not local))
      org-scanner-tags
    (org-with-point-at (or pos (point))
      (unless (org-before-first-heading-p)
        (org-back-to-heading t)
        (let ((ltags (org--get-local-tags)) itags)
          (if (or local (not org-use-tag-inheritance)) ltags
            (let* ((conflicting-tags (org-get-conflicting-tags ltags))
                   (ct-predicate (lambda (x) (member x conflicting-tags))))
              (while (org-up-heading-safe)
                (nconc conflicting-tags
                       (org-get-conflicting-tags itags
                                                 org-current-tag-alist))
                (setq itags (append (mapcar #'org-add-prop-inherited
                                            (cl-remove-if ct-predicate
                                              (org--get-local-tags)))
                                    itags)))
              (setq itags (append
                           (cl-remove-if ct-predicate org-file-tags)
                           itags)))
            (delete-dups
             (append (org-remove-uninherited-tags itags) ltags))))))))

If everything looks good, I’ll figure out how to submit it as a proper
Git patch (right now it’s just “code that’s been sitting in my config
for weeks”), and do the copyright-assignment thing so we can get it
merged. I hope you like it!

—Tina

On Sat, Feb 9, 2019 at 10:03 AM Nicolas Goaziou <address@hidden> wrote:
>
> Hello,
>
> Tina Russell <address@hidden> writes:
>
> > So, according to the Org documentation: “You can also group together tags
> > that are mutually exclusive by using braces … Selecting a tag in a group of
> > mutually exclusive tags will turn off any other tags from that group.
> >
> > But, if I do this…
> >
> > #+TAGS: { place(c) container(c) object(o) }
> >
> > * Room :place:
> > ** Box :container:
> > *** Toy :object:
> >
> > …and then use (org-get-tags) on “Toy,” it reports that it has the tags
> > “place”, “container”, and “object”, even though these tags are all defined
> > to be mutually exclusive! This is a problem,
>
> Not really. `org-get-tags' is a low-level function, i.e., it has no
> knowledge about tag groups or mutually exclusive tags.
>
> > since turning off tag
> > inheritance (for a document or for specific tags) seems to be an
> > all-or-nothing affair. That means if I wanted to do this:
> >
> > * Room :place:
> > ** Bookcase
> > ** Dresser
> > ** Desk
> > ** Nightstand
> > ** Closet
> > *** Box :container:
> > **** Toy :object:
> >
> > …and then search for all headings with the tag “place,” either (with tag
> > inheritance) everything, including “Box” and “Toy,” will be returned, or
> > (without tag inheritance) only “Room” would be returned. (I could put a tag
> > on every heading where I want it inherited, but that would both defeat the
> > purpose of inheritance and make it difficult to manage large trees.)
>
> You don't need to use mutually exclusive tags for this example. You
> could search for "place-container", assuming tag inheritance.
>
> Regards,
>
> --
> Nicolas Goaziou



reply via email to

[Prev in Thread] Current Thread [Next in Thread]