From mboxrd@z Thu Jan 1 00:00:00 1970 From: Tina Russell Subject: Re: Mutually-exclusive Org tags still inherit each other Date: Fri, 1 Mar 2019 13:47:44 -0800 Message-ID: References: <874l9cdeki.fsf@nicolasgoaziou.fr> Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Return-path: Received: from eggs.gnu.org ([209.51.188.92]:47871) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gzq0j-0001yM-19 for emacs-orgmode@gnu.org; Fri, 01 Mar 2019 16:48:06 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gzq0h-0007S8-FO for emacs-orgmode@gnu.org; Fri, 01 Mar 2019 16:48:04 -0500 Received: from mail-lj1-x22d.google.com ([2a00:1450:4864:20::22d]:37813) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gzq0d-0007DJ-OJ for emacs-orgmode@gnu.org; Fri, 01 Mar 2019 16:48:03 -0500 Received: by mail-lj1-x22d.google.com with SMTP id a17so21691066ljd.4 for ; Fri, 01 Mar 2019 13:47:59 -0800 (PST) In-Reply-To: <874l9cdeki.fsf@nicolasgoaziou.fr> List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: "Emacs-orgmode" To: emacs-orgmode@gnu.org Well, I think it=E2=80=99s unreasonable to ask users to reinforce manually = and continuously something they=E2=80=99ve already specified in their settings. Besides, my example was intentionally trivial=E2=80=94imagine managing a la= rge 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 =E2=80=9Cif=E2=80= =9D 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=E2=80=99ll figure out how to submit it as a pro= per Git patch (right now it=E2=80=99s just =E2=80=9Ccode that=E2=80=99s been si= tting in my config for weeks=E2=80=9D), and do the copyright-assignment thing so we can get it merged. I hope you like it! =E2=80=94Tina On Sat, Feb 9, 2019 at 10:03 AM Nicolas Goaziou wr= ote: > > Hello, > > Tina Russell writes: > > > So, according to the Org documentation: =E2=80=9CYou can also group tog= ether tags > > that are mutually exclusive by using braces =E2=80=A6 Selecting a tag i= n a group of > > mutually exclusive tags will turn off any other tags from that group. > > > > But, if I do this=E2=80=A6 > > > > #+TAGS: { place(c) container(c) object(o) } > > > > * Room :place: > > ** Box :container: > > *** Toy :object: > > > > =E2=80=A6and then use (org-get-tags) on =E2=80=9CToy,=E2=80=9D it repor= ts that it has the tags > > =E2=80=9Cplace=E2=80=9D, =E2=80=9Ccontainer=E2=80=9D, and =E2=80=9Cobje= ct=E2=80=9D, 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: > > > > =E2=80=A6and then search for all headings with the tag =E2=80=9Cplace,= =E2=80=9D either (with tag > > inheritance) everything, including =E2=80=9CBox=E2=80=9D and =E2=80=9CT= oy,=E2=80=9D will be returned, or > > (without tag inheritance) only =E2=80=9CRoom=E2=80=9D 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