From a537e4fe76c967db35923c02cae7902b04be788c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gustav=20Wikstr=C3=B6m?= <gustav@UVServer>
Date: Sat, 24 Jan 2015 02:47:47 +0100
Subject: [PATCH 3/3] org: Nesting grouptags

* lisp/org.el (org-tags-expand): Nesting grouptags. Allowing subtags
  to be defined as groups themselves.

  : #+TAGS: [ Group : SubOne(1) SubTwo ]
  : #+TAGS: [ SubOne : SubOne1 SubOne2 ]
  : #+TAGS: [ SubTwo : SubTwo1 SubTwo2 ]

  Should be seen as a tree of tags:
  - Group
    - SubOne
      - SubOne1
      - SubOne2
    - SubTwo
      - SubTwo1
      - SubTwo2

  Searching for "Group" should return all tags defined above.
---
 lisp/org.el | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index 6bb8edf..5c80238 100755
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -14543,7 +14543,7 @@ See also `org-scan-tags'.
 			  matcher)))
     (cons match0 matcher)))
 
-(defun org-tags-expand (match &optional single-as-list downcased)
+(defun org-tags-expand (match &optional single-as-list downcased tags-already-expanded)
   "Expand group tags in MATCH.
 
 This replaces every group tag in MATCH with a regexp tag search.
@@ -14575,6 +14575,7 @@ When DOWNCASE is non-nil, expand downcased TAGS."
 	     (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)
 	;; @ and _ are allowed as word-components in tags
 	(modify-syntax-entry ?@ "w" stable)
@@ -14592,8 +14593,23 @@ When DOWNCASE is non-nil, expand downcased TAGS."
 	  (let* ((dir (match-string 1 return-match))
 		 (tag (match-string 2 return-match))
 		 (tag (if downcased (downcase tag) tag)))
-	    (when (not (get-text-property 0 'grouptag (match-string 2 return-match)))
+	    (when (and (not (get-text-property 0 'grouptag (match-string 2 return-match)))
+		       (not (member tag work-already-expanded)))
 	      (setq tags-in-group (assoc tag taggroups))
+	      (setq work-already-expanded (append (list tag) work-already-expanded))
+	      ; Recursively expand each tag in the group, if the tag hasn't
+	      ; already been expanded
+	      (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)
@@ -14615,6 +14631,11 @@ When DOWNCASE is non-nil, expand downcased TAGS."
 			(setq regexp-in-group (concat "\\|" (mapconcat 'identity regexp-in-group "\\|"))))
 		    (setq tags-in-group (concat dir "{\\<" (regexp-opt tags-in-group) regexp-in-group  "\\>}"))
 		    (if (stringp tags-in-group) (org-add-props tags-in-group '(grouptag t)))
+		    ;; Redo the regexp-match because the recursive calls seems to mess it up...
+		    (with-syntax-table stable
+		      (string-match
+		       (concat "\\(?1:[+-]?\\)\\(?2:\\<"
+			       (regexp-opt taggroups-keys) "\\>\\)") return-match))
 		    (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))))
-- 
1.9.1

