emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* Mutually-exclusive Org tags still inherit each other
@ 2019-02-08  0:42 Tina Russell
  2019-02-09 18:03 ` Nicolas Goaziou
  0 siblings, 1 reply; 4+ messages in thread
From: Tina Russell @ 2019-02-08  0:42 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 1535 bytes --]

First of all, I want to say that I really appreciate Org Mode, I wouldn’t
be using Emacs without it, and it’s where I spend the vast majority of my
time in Emacs. Thank you all!

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, 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.)

I can’t find any solutions to this online, so I’m posting this here. I hope
it can get fixed. Thank you!

—Tina

[-- Attachment #2: Type: text/html, Size: 1841 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Mutually-exclusive Org tags still inherit each other
  2019-02-08  0:42 Mutually-exclusive Org tags still inherit each other Tina Russell
@ 2019-02-09 18:03 ` Nicolas Goaziou
  2019-03-01 21:47   ` Tina Russell
  0 siblings, 1 reply; 4+ messages in thread
From: Nicolas Goaziou @ 2019-02-09 18:03 UTC (permalink / raw)
  To: Tina Russell; +Cc: emacs-orgmode

Hello,

Tina Russell <tinakellyrussell@gmail.com> 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

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Mutually-exclusive Org tags still inherit each other
  2019-02-09 18:03 ` Nicolas Goaziou
@ 2019-03-01 21:47   ` Tina Russell
  2019-03-04  9:48     ` Nicolas Goaziou
  0 siblings, 1 reply; 4+ messages in thread
From: Tina Russell @ 2019-03-01 21:47 UTC (permalink / raw)
  To: emacs-orgmode

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 <mail@nicolasgoaziou.fr> wrote:
>
> Hello,
>
> Tina Russell <tinakellyrussell@gmail.com> 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

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Mutually-exclusive Org tags still inherit each other
  2019-03-01 21:47   ` Tina Russell
@ 2019-03-04  9:48     ` Nicolas Goaziou
  0 siblings, 0 replies; 4+ messages in thread
From: Nicolas Goaziou @ 2019-03-04  9:48 UTC (permalink / raw)
  To: Tina Russell; +Cc: emacs-orgmode

Hello,

Tina Russell <tinakellyrussell@gmail.com> writes:

> 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.

Thank you for the patch.

Unfortunately, I think we are miscommunicating, because we are thinking
at different levels of abstraction. Le me clarify this.

Org syntax supports colons-wrapped cookies at the end of a headline,
called tags. That's about it. Of course, you can extend those cookies to
support, e.g., inheritance, groups, mutual exclusion, and whatnot. But
at the lowest level, there are only cookies at the end of a headline.

The function `org-get-tags' was implemented to get those, possibly with
inheritance. Most, if not all, of its callers in the code base do not
care about groups, or mutual exclusion. Also, most, if not all, callers
care about internal tags. Not that some internal tags are automatically
inherited, hence support for this mechanism in `org-get-tags'.

You apparently have a need for user-defined tags, with all bells and
whistles. However, IIUC, you don't really need to list them, but
ultimately do a tag search on them. 

There are advanced functions for tag searches: `org-make-tags-matcher'
and `org-scan-tags'. My point is that you should first check if they
already do what you want, and patch them otherwise, instead of changing
`org-get-tags', which has a clear scope.

WDYT?

Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2019-03-04  9:48 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-08  0:42 Mutually-exclusive Org tags still inherit each other Tina Russell
2019-02-09 18:03 ` Nicolas Goaziou
2019-03-01 21:47   ` Tina Russell
2019-03-04  9:48     ` Nicolas Goaziou

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).