From 862518eb620ba95899b2e92dc4ad5fdeb5b5faa5 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] Nesting grouptags

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

  A new variable is defined =ORG-GROUP-TAGS-MAX-DEPTH= that is used to
  limit the depth of recursion when expanding tags. It defaults to 2.

Conflicts:
	lisp/org.el
---
 lisp/org.el | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/lisp/org.el b/lisp/org.el
index 05b7307..f4d93fb 100755
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -4929,6 +4929,12 @@ This can be turned on/off through `org-toggle-tags-groups'."
   :group 'org-startup
   :type 'boolean)
 
+(defcustom org-group-tags-max-depth 2
+  "Specifies the maximum recursive depth tags will be
+expanded. Only applies if org-group-tags is activated."
+  :group 'org-tags
+  :type 'integer)
+
 (defvar org-inhibit-startup nil)        ; Dynamically-scoped param.
 
 (defun org-toggle-tags-groups ()
@@ -14528,7 +14534,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 recursion-level)
   "Expand group tags in MATCH.
 
 This replaces every group tag in MATCH with a regexp tag search.
@@ -14579,6 +14585,20 @@ When DOWNCASE is non-nil, expand downcased TAGS."
 		 (tag (if downcased (downcase tag) tag)))
 	    (when (not (get-text-property 0 'grouptag (match-string 2 return-match)))
 	      (setq tags-in-group (assoc tag taggroups))
+	      ; Recursively expand each tag in the group, if there are
+	      ; nested groups and org-group-tags-max-depth allows it
+	      (if (or (not recursion-level)
+		      (> org-group-tags-max-depth recursion-level))
+		  (let ((lvl (if recursion-level (1+ recursion-level) 1))
+			tags-expanded-in-group)
+		    (dolist (x (cdr tags-in-group))
+		      (if (member x taggroups-keys)
+			  ;(match &optional single-as-list downcased recursion-level)
+			  (setq tags-expanded-in-group (append (org-tags-expand x t downcased lvl)
+							       tags-expanded-in-group))
+			(setq tags-expanded-in-group (append (list x) tags-expanded-in-group))))
+		    (setq tags-in-group (cons (car tags-in-group)
+					      tags-expanded-in-group))))
 	      ; Filter tag-regexps from tags
 	      (setq regexp-in-group-escaped (delq nil (mapcar (lambda (x)
 								(if (stringp x)
@@ -14600,6 +14620,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

