branch: externals/taxy commit 247d473607fec8f42c9d2ad3326865f22c51324b Author: Adam Porter <a...@alphapapa.net> Commit: Adam Porter <a...@alphapapa.net>
Add/Change: taxy-take-keyed* -> taxy-take-keyed, key-fn chains taxy.el should now have met feature parity with bufler-group-tree.el, and also exceed it with the more flexible :take functions and other features. It should now be a good foundation for these packages to build functionality on. --- README.org | 92 +++++++++++++++++++++++++++++---- taxy.el | 135 ++++++++++++++++++++++++++++++------------------ taxy.info | 172 +++++++++++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 300 insertions(+), 99 deletions(-) diff --git a/README.org b/README.org index fa77774..2343d7a 100644 --- a/README.org +++ b/README.org @@ -288,7 +288,7 @@ And finally we'll define a taxy to organize them. In this, we use a helper macr :taxys (list (make-taxy :name "Sporty" :take (lambda (object taxy) - (taxy-take-keyed* + (taxy-take-keyed (list #'sport-venue (in 'ball 'sport-uses) (in 'disc 'sport-uses) @@ -296,7 +296,7 @@ And finally we'll define a taxy to organize them. In this, we use a helper macr (in 'racket 'sport-uses)) object taxy ;; We set the `:then' function of the taxys - ;; created by `taxy-take-keyed*' to `identity' + ;; created by `taxy-take-keyed' to `identity' ;; so they will not consume their objects. :then #'identity))))))) #+END_SRC @@ -345,7 +345,7 @@ That's pretty sporty. But classifying them by venue first makes the racket and :taxys (list (make-taxy :name "Sporty" :take (lambda (object taxy) - (taxy-take-keyed* + (taxy-take-keyed (list (in 'ball 'sport-uses) (in 'disc 'sport-uses) (in 'glove 'sport-uses) @@ -392,7 +392,7 @@ That's better. But I'd also like to see a very simple classification to help me (make-taxy :name "Funny" :take (lambda (object taxy) - (taxy-take-keyed* + (taxy-take-keyed (list (lambda (sport) (if (sport-fun sport) 'fun 'boring)) @@ -467,6 +467,7 @@ To return a taxy in a more human-readable format (with only relevant fields incl :END: :CONTENTS: - [[#multi-level-dynamic-taxys][Multi-level dynamic taxys]] +- [[#chains-of-independent-multi-level-dynamic-taxys]["Chains" of independent, multi-level dynamic taxys]] :END: You may not always know in advance what taxonomy a set of objects fits into, so =taxy= lets you add taxys dynamically by using the ~:take~ function to add a taxy when an object is "taken into" a parent taxy. For example, you could dynamically classify buffers by their major mode like so: @@ -481,7 +482,7 @@ You may not always know in advance what taxonomy a set of objects fits into, so :taxys (list (make-taxy :name "Modes" - :take (apply-partially #'taxy-take-keyed #'buffery-major-mode))))) + :take (apply-partially #'taxy-take-keyed (list #'buffery-major-mode)))))) ;; Note the use of `taxy-emptied' to avoid mutating the original taxy definition. (taxy-plain @@ -489,7 +490,7 @@ You may not always know in advance what taxonomy a set of objects fits into, so (taxy-emptied buffery))) #+END_SRC -The taxy's ~:take~ function is set to the ~taxy-take-keyed~ function, partially applied with the ~buffery-major-mode~ function as its ~key-fn~ (~taxy-fill~ supplies the buffer and the taxy as arguments), and it produces this taxonomy of buffers: +The taxy's ~:take~ function is set to the ~taxy-take-keyed~ function, partially applied with the ~buffery-major-mode~ function as its list of ~key-fns~ (~taxy-fill~ supplies the buffer and the taxy as arguments), and it produces this taxonomy of buffers: #+BEGIN_SRC elisp ("Buffers" @@ -522,9 +523,9 @@ The taxy's ~:take~ function is set to the ~taxy-take-keyed~ function, partially *** Multi-level dynamic taxys -Of course, the point of taxonomies is that they aren't restricted to a single level of depth, so you may also use the function ~taxy-take-keyed*~ (notice the ~*~) to dynamically make multi-level taxys. +Of course, the point of taxonomies is that they aren't restricted to a single level of depth, so you may also use the function ~taxy-take-keyed~ to dynamically make multi-level taxys. -Expanding on the previous example, we use ~cl-labels~ to define functions which are used in the taxy's definition, which are used in the ~:take~ function, which calls ~taxy-take-keyed*~ (rather than using ~apply-partially~ like in the previous example, we use a lambda function, which performs better than partially applied functions). Then when the taxy is filled, a multi-level hierarchy is created dynamically, organizing buffers first by their directory, and then by mode in each directory. +Expanding on the previous example, we use ~cl-labels~ to define functions which are used in the taxy's definition, which are used in the ~:take~ function, which calls ~taxy-take-keyed~ (rather than using ~apply-partially~ like in the previous example, we use a lambda function, which performs better than partially applied functions). Then when the taxy is filled, a multi-level hierarchy is created dynamically, organizing buffers first by their directory, and then by mode in each directory. # MAYBE: A macro to define :take functions more concisely. @@ -538,7 +539,7 @@ Expanding on the previous example, we use ~cl-labels~ to define functions which (make-taxy :name "Directories" :take (lambda (object taxy) - (taxy-take-keyed* (list #'buffer-directory #'buffer-mode) object taxy))))))) + (taxy-take-keyed (list #'buffer-directory #'buffer-mode) object taxy))))))) (taxy-plain (taxy-fill (buffer-list) @@ -568,6 +569,72 @@ That produces a list like: (#<buffer taxy-magit-section.el> #<buffer taxy.el<taxy.el> #<buffer scratch.el>)))))))) #+END_SRC +*** "Chains" of independent, multi-level dynamic taxys +:PROPERTIES: +:ID: 8aec3671-ee22-44a0-968c-81443f4dcd74 +:END: + +/Naming things is hard./ + +Going a step further, each element in the ~taxy-take-keyed~ function's ~KEY-FNS~ argument may be a list of functions (or a list of lists of functions, etc.), which creates a "chain" of "independent" dynamic taxys. Each such chain may be said to "short-circuit" the filling process in that, when an object is "taken" by the first key function in a chain, the object is not "offered" to other functions outside that chain. This allows each dynamic sub-taxy to have its own set of sub-taxys, r [...] + +Building on the ~sporty~ example, let's define a taxy in which outdoor sports are classified only by whether they involve a disc, but indoor sports are additionally classified by whatever equipment they may use: + +#+BEGIN_SRC elisp :exports code :results silent :lexical t + (defvar sporty-dynamic + (cl-macrolet ((in (needle haystack) + `(lambda (object) + (when (member ,needle (funcall ,haystack object)) + ,needle)))) + (cl-labels ((outdoor-p + (sport) (when (eq 'outdoor (sport-venue sport)) + "Outdoor")) + (indoor-p + (sport) (when (eq 'indoor (sport-venue sport)) + "Indoor")) + (disc-p + (sport) (if (funcall (in 'disc 'sport-uses) sport) + 'disc + 'non-disc))) + (make-taxy + :name "Sporty (dynamic)" + :take (lambda (object taxy) + (taxy-take-keyed + (list (list #'outdoor-p #'disc-p) + (list #'indoor-p + (in 'ball 'sport-uses) + (in 'disc 'sport-uses) + (in 'glove 'sport-uses) + (in 'racket 'sport-uses))) + object taxy)))))) +#+END_SRC + +Now let's fill the taxy with the sports and format it: + +#+BEGIN_SRC elisp :exports code :results code + (thread-last sporty-dynamic + taxy-emptied + (taxy-fill sports) + (taxy-mapcar #'sport-name) + taxy-plain) +#+END_SRC + +#+BEGIN_SRC elisp :exports code + ("Sporty (dynamic)" + (("Indoor" + ((ball + ("Volleyball" "Basketball") + ((glove + ("Handball")) + (racket + ("Racquetball")))))) + ("Outdoor" + ((disc + ("Ultimate" "Disc golf")) + (non-disc + ("Soccer" "Tennis" "Football" "Baseball")))))) +#+END_SRC + ** Reusable taxys Since taxys are structs, they may be stored in variables and used in other structs (being sure to copy the root taxy with ~taxy-emptied~ before filling). For example, this shows using =taxy= to classify Matrix rooms in [[https://github.com/alphapapa/ement.el][Ement.el]]: @@ -717,10 +784,15 @@ Note that =taxy-magit-section.el= is not installed with the =taxy= package by de ** 0.2-pre +*** Changes + ++ Function ~taxy-take-keyed*~ is renamed to ~taxy-take-keyed~, replacing the old function: it's more powerful, and there's little reason to maintain two versions. + *** Additions -+ Struct ~taxy~ now has a ~:make~ slot, a function called to make new sub-taxys by ~take-take-keyed*~ (defaulting to ~make-taxy~). This is useful when defining structs specialized on ~taxy~. ++ Struct ~taxy~ now has a ~:make~ slot, a function called to make new sub-taxys by ~take-take-keyed~ (defaulting to ~make-taxy~). This is useful when defining structs specialized on ~taxy~. + Struct ~taxy-magit-section~ now has an ~:indent~ slot, a number of characters by which to indent each level of sub-taxy, applied automatically by function ~taxy-magit-section-insert~. ++ Each element of the new ~taxy-take-keyed~'s ~KEY-FNS~ argument may now be a function or a list of functions (or a list of a list of functions, etc.). Lists of functions create "chains" of independent, dynamic taxys descending from a single root taxy. See [[id:8aec3671-ee22-44a0-968c-81443f4dcd74][example]]. *** Fixes diff --git a/taxy.el b/taxy.el index f39ec18..80e9013 100644 --- a/taxy.el +++ b/taxy.el @@ -134,61 +134,98 @@ Does not copy TAXY. Destructively modifies TAXY, if FN does." (defalias 'taxy-mapc* #'taxy-mapc-taxys) -(cl-defun taxy-take-keyed (key-fn object taxy &key (key-name-fn #'identity)) - "Take OBJECT into TAXY, adding new taxys dynamically. -Places OBJECT into a taxy in TAXY for the value returned by -KEY-FN called with OBJECT. The new taxy's name is that returned -by KEY-NAME-FN called with OBJECT." - (let* ((key (funcall key-fn object)) - (key-taxy - (or (cl-find-if (lambda (taxy-key) - (equal key taxy-key)) - (taxy-taxys taxy) - :key #'taxy-key) - (car - (push (make-taxy - :name (funcall key-name-fn key) :key key - :predicate (lambda (object) - (equal key (funcall key-fn object)))) - (taxy-taxys taxy)))))) - (push object (taxy-objects key-taxy)))) - -(cl-defun taxy-take-keyed* +(cl-defun taxy-take-keyed (key-fns object taxy &key (key-name-fn #'identity) (then #'ignore)) "Take OBJECT into TAXY, adding new taxys dynamically and recursively. Places OBJECT into a taxy in TAXY for the value returned by KEY-FNS called with OBJECT. The new taxys are added to TAXY recursively as necessary. Each new taxy's name is that returned -by KEY-NAME-FN called with OBJECT." - (let ((key-fn (car key-fns))) - (if-let ((key (funcall key-fn object))) - (let ((key-taxy - (or (cl-find-if (lambda (taxy-key) - (equal key taxy-key)) - (taxy-taxys taxy) - :key #'taxy-key) - (car - ;; Calling `make-taxy' directly might offer the compiler a chance to optimize - ;; compared to using `funcall', but allowing taxy structs to specify their - ;; own MAKE functions is very helpful when using specialized structs. - (push (funcall (taxy-make taxy) - :name (funcall key-name-fn key) - :key key - :predicate (lambda (object) - (equal key (funcall key-fn object))) - :take (when (cdr key-fns) - (lambda (object taxy) - (taxy-take-keyed* (cdr key-fns) object taxy))) - :then then) - (taxy-taxys taxy)))))) - (if (cdr key-fns) - (taxy-take-keyed* (cdr key-fns) object key-taxy) - (push object (taxy-objects key-taxy)))) - ;; No key value: push to this taxy. - (if (cdr key-fns) - (taxy-take-keyed* (cdr key-fns) object taxy) - (push object (taxy-objects taxy)))))) +by KEY-NAME-FN called with OBJECT. + +Each element of KEY-FNS may be a function or a list of functions. +A list of functions creates a \"chain\" of functions: when an +object is matched by the first function in a chain, it is placed +in that chain's taxonomy, and is not \"offered\" to functions +outside of that chain. + +For example, if KEY-FNS were: + + '(((lambda (n) (< n 10)) oddp) + ((lambda (n) (>= n 10)) evenp)) + +Then a list of numbers from 0-19 would be classified +like (listing numbers on a single line for the sake of example): + + - <10: + - 0, 2, 4, 6, 8 + - oddp: + - 1, 3, 5, 7, 9 + - >=10: + - 11, 13, 15, 17, 19 + - evenp: + - 10, 12, 14, 16, 18 + +So only numbers below 10 are tested against `oddp', and only +numbers greater-than-or-equal-to 10 are tested against +`evenp'. (A contrived example, of course, since testing against +`evenp' or `oddp' is just the inverse.)" + (declare (indent defun)) + (cl-macrolet ((offer-or-push + () `(if (cdr key-fns) + (taxy-take-keyed (cdr key-fns) object taxy + :key-name-fn key-name-fn :then then) + (push object (taxy-objects taxy))))) + (cl-typecase (car key-fns) + (function + ;; A single key function. + (let ((key-fn (car key-fns))) + (if-let ((key (funcall key-fn object))) + ;; This key function returned non-nil for the object: + ;; apply it to the appropriate sub-taxy. + (let ((key-taxy + (or (cl-find-if (lambda (taxy-key) + (equal key taxy-key)) + (taxy-taxys taxy) + :key #'taxy-key) + ;; No existing, matching sub-taxy found: make + ;; a new one and add it to TAXY's sub-taxys. + (car + (push (funcall + ;; NOTE: Calling `make-taxy' directly might offer the + ;; compiler a chance to optimize compared to using `funcall', + ;; but allowing taxy structs to specify their own MAKE + ;; functions is very helpful when using specialized structs. + (taxy-make taxy) + :name (funcall key-name-fn key) + :key key + :predicate (lambda (object) + (equal key (funcall key-fn object))) + :take (when (cdr key-fns) + (lambda (object taxy) + (taxy-take-keyed (cdr key-fns) object taxy + :key-name-fn key-name-fn :then then))) + :then then) + (taxy-taxys taxy)))))) + (if (cdr key-fns) + ;; Other key-fns remain: offer object to them, allowing + ;; them to create more sub-taxys beneath this key-taxy. + (taxy-take-keyed (cdr key-fns) object key-taxy + :key-name-fn key-name-fn :then then) + ;; No more key-fns remain: add object to this taxy. + (push object (taxy-objects key-taxy)))) + ;; No key value: offer to other KEY-FNS or push to this taxy. + (offer-or-push)))) + (list + ;; A "chain" of key functions. + (or (when (funcall (caar key-fns) object) + ;; The first function in this chain returns non-nil for + ;; the object: apply the object to the chain. + (taxy-take-keyed (car key-fns) object taxy + :key-name-fn key-name-fn :then then)) + ;; This "chain" of key-fns didn't take the object: offer it to + ;; other chains, or push to this taxy if they don't take it. + (offer-or-push)))))) (defun taxy-size (taxy) "Return the number of objects TAXY holds. diff --git a/taxy.info b/taxy.info index e255d51..17ffe88 100644 --- a/taxy.info +++ b/taxy.info @@ -44,6 +44,7 @@ Usage Dynamic taxys * Multi-level dynamic taxys:: +* "Chains" of independent, multi-level dynamic taxys: "Chains" of independent multi-level dynamic taxys. Changelog @@ -52,6 +53,7 @@ Changelog 0.2-pre +* Changes:: * Additions:: * Fixes:: @@ -323,7 +325,7 @@ key functions: :taxys (list (make-taxy :name "Sporty" :take (lambda (object taxy) - (taxy-take-keyed* + (taxy-take-keyed (list #'sport-venue (in 'ball 'sport-uses) (in 'disc 'sport-uses) @@ -331,7 +333,7 @@ key functions: (in 'racket 'sport-uses)) object taxy ;; We set the `:then' function of the taxys - ;; created by `taxy-take-keyed*' to `identity' + ;; created by `taxy-take-keyed' to `identity' ;; so they will not consume their objects. :then #'identity))))))) @@ -375,7 +377,7 @@ racket and glove sports not be listed together. Let’s swap that around: :taxys (list (make-taxy :name "Sporty" :take (lambda (object taxy) - (taxy-take-keyed* + (taxy-take-keyed (list (in 'ball 'sport-uses) (in 'disc 'sport-uses) (in 'glove 'sport-uses) @@ -419,7 +421,7 @@ to help me decide what to play: (make-taxy :name "Funny" :take (lambda (object taxy) - (taxy-take-keyed* + (taxy-take-keyed (list (lambda (sport) (if (sport-fun sport) 'fun 'boring)) @@ -521,7 +523,7 @@ File: README.info, Node: Dynamic taxys, Next: Reusable taxys, Up: Usage 3.1 Dynamic taxys ================= - • + • • You may not always know in advance what taxonomy a set of objects fits into, so taxy lets you add taxys dynamically by using the ‘:take’ function to add a taxy when an object is "taken into" a parent taxy. @@ -537,7 +539,7 @@ like so: :taxys (list (make-taxy :name "Modes" - :take (apply-partially #'taxy-take-keyed #'buffery-major-mode))))) + :take (apply-partially #'taxy-take-keyed (list #'buffery-major-mode)))))) ;; Note the use of `taxy-emptied' to avoid mutating the original taxy definition. (taxy-plain @@ -545,9 +547,9 @@ like so: (taxy-emptied buffery))) The taxy’s ‘:take’ function is set to the ‘taxy-take-keyed’ function, -partially applied with the ‘buffery-major-mode’ function as its ‘key-fn’ -(‘taxy-fill’ supplies the buffer and the taxy as arguments), and it -produces this taxonomy of buffers: +partially applied with the ‘buffery-major-mode’ function as its list of +‘key-fns’ (‘taxy-fill’ supplies the buffer and the taxy as arguments), +and it produces this taxonomy of buffers: ("Buffers" (("Modes" @@ -579,21 +581,21 @@ produces this taxonomy of buffers: * Menu: * Multi-level dynamic taxys:: +* "Chains" of independent, multi-level dynamic taxys: "Chains" of independent multi-level dynamic taxys. -File: README.info, Node: Multi-level dynamic taxys, Up: Dynamic taxys +File: README.info, Node: Multi-level dynamic taxys, Next: "Chains" of independent multi-level dynamic taxys, Up: Dynamic taxys 3.1.1 Multi-level dynamic taxys ------------------------------- Of course, the point of taxonomies is that they aren’t restricted to a single level of depth, so you may also use the function -‘taxy-take-keyed*’ (notice the ‘*’) to dynamically make multi-level -taxys. +‘taxy-take-keyed’ to dynamically make multi-level taxys. Expanding on the previous example, we use ‘cl-labels’ to define functions which are used in the taxy’s definition, which are used in the -‘:take’ function, which calls ‘taxy-take-keyed*’ (rather than using +‘:take’ function, which calls ‘taxy-take-keyed’ (rather than using ‘apply-partially’ like in the previous example, we use a lambda function, which performs better than partially applied functions). Then when the taxy is filled, a multi-level hierarchy is created dynamically, @@ -609,7 +611,7 @@ directory. (make-taxy :name "Directories" :take (lambda (object taxy) - (taxy-take-keyed* (list #'buffer-directory #'buffer-mode) object taxy))))))) + (taxy-take-keyed (list #'buffer-directory #'buffer-mode) object taxy))))))) (taxy-plain (taxy-fill (buffer-list) @@ -637,6 +639,78 @@ directory. (#<buffer taxy-magit-section.el> #<buffer taxy.el<taxy.el> #<buffer scratch.el>)))))))) +File: README.info, Node: "Chains" of independent multi-level dynamic taxys, Prev: Multi-level dynamic taxys, Up: Dynamic taxys + +3.1.2 "Chains" of independent, multi-level dynamic taxys +-------------------------------------------------------- + +_Naming things is hard._ + + Going a step further, each element in the ‘taxy-take-keyed’ +function’s ‘KEY-FNS’ argument may be a list of functions (or a list of +lists of functions, etc.), which creates a "chain" of "independent" +dynamic taxys. Each such chain may be said to "short-circuit" the +filling process in that, when an object is "taken" by the first key +function in a chain, the object is not "offered" to other functions +outside that chain. This allows each dynamic sub-taxy to have its own +set of sub-taxys, rather than sharing the same "global" set. In effect, +this creates multiple, unique taxonomies that share a single root taxy. + + Building on the ‘sporty’ example, let’s define a taxy in which +outdoor sports are classified only by whether they involve a disc, but +indoor sports are additionally classified by whatever equipment they may +use: + + (defvar sporty-dynamic + (cl-macrolet ((in (needle haystack) + `(lambda (object) + (when (member ,needle (funcall ,haystack object)) + ,needle)))) + (cl-labels ((outdoor-p + (sport) (when (eq 'outdoor (sport-venue sport)) + "Outdoor")) + (indoor-p + (sport) (when (eq 'indoor (sport-venue sport)) + "Indoor")) + (disc-p + (sport) (if (funcall (in 'disc 'sport-uses) sport) + 'disc + 'non-disc))) + (make-taxy + :name "Sporty (dynamic)" + :take (lambda (object taxy) + (taxy-take-keyed + (list (list #'outdoor-p #'disc-p) + (list #'indoor-p + (in 'ball 'sport-uses) + (in 'disc 'sport-uses) + (in 'glove 'sport-uses) + (in 'racket 'sport-uses))) + object taxy)))))) + + Now let’s fill the taxy with the sports and format it: + + (thread-last sporty-dynamic + taxy-emptied + (taxy-fill sports) + (taxy-mapcar #'sport-name) + taxy-plain) + + ("Sporty (dynamic)" + (("Indoor" + ((ball + ("Volleyball" "Basketball") + ((glove + ("Handball")) + (racket + ("Racquetball")))))) + ("Outdoor" + ((disc + ("Ultimate" "Disc golf")) + (non-disc + ("Soccer" "Tennis" "Football" "Baseball")))))) + + File: README.info, Node: Reusable taxys, Next: Threading macros, Prev: Dynamic taxys, Up: Usage 3.2 Reusable taxys @@ -815,26 +889,42 @@ File: README.info, Node: 02-pre, Next: 01, Up: Changelog * Menu: +* Changes:: * Additions:: * Fixes:: -File: README.info, Node: Additions, Next: Fixes, Up: 02-pre +File: README.info, Node: Changes, Next: Additions, Up: 02-pre + +4.1.1 Changes +------------- + + • Function ‘taxy-take-keyed*’ is renamed to ‘taxy-take-keyed’, + replacing the old function: it’s more powerful, and there’s little + reason to maintain two versions. + + +File: README.info, Node: Additions, Next: Fixes, Prev: Changes, Up: 02-pre -4.1.1 Additions +4.1.2 Additions --------------- • Struct ‘taxy’ now has a ‘:make’ slot, a function called to make new - sub-taxys by ‘take-take-keyed*’ (defaulting to ‘make-taxy’). This + sub-taxys by ‘take-take-keyed’ (defaulting to ‘make-taxy’). This is useful when defining structs specialized on ‘taxy’. • Struct ‘taxy-magit-section’ now has an ‘:indent’ slot, a number of characters by which to indent each level of sub-taxy, applied automatically by function ‘taxy-magit-section-insert’. + • Each element of the new ‘taxy-take-keyed’’s ‘KEY-FNS’ argument may + now be a function or a list of functions (or a list of a list of + functions, etc.). Lists of functions create "chains" of + independent, dynamic taxys descending from a single root taxy. See + *note example: "Chains" of independent multi-level dynamic taxys. File: README.info, Node: Fixes, Prev: Additions, Up: 02-pre -4.1.2 Fixes +4.1.3 Fixes ----------- • ‘taxy-magit-section’’s ‘insert-object’ function. @@ -896,28 +986,30 @@ GPLv3 Tag Table: Node: Top218 -Node: Examples1379 -Node: Numbery (starting basically)1698 -Node: Lettery (filling incrementally)7457 -Node: Sporty (understanding completely)9906 -Node: Applications16385 -Node: Installation16785 -Node: Usage17086 -Node: Dynamic taxys19207 -Node: Multi-level dynamic taxys21625 -Node: Reusable taxys23788 -Node: Threading macros27957 -Node: Modifying filled taxys28496 -Node: Magit section29589 -Node: Changelog30190 -Node: 02-pre30340 -Node: Additions30462 -Node: Fixes31002 -Node: 0131252 -Node: Development31355 -Node: Copyright assignment31561 -Node: Credits32160 -Node: License32350 +Node: Examples1497 +Node: Numbery (starting basically)1816 +Node: Lettery (filling incrementally)7575 +Node: Sporty (understanding completely)10024 +Node: Applications16499 +Node: Installation16899 +Node: Usage17200 +Node: Dynamic taxys19321 +Node: Multi-level dynamic taxys21865 +Node: "Chains" of independent multi-level dynamic taxys24062 +Node: Reusable taxys26942 +Node: Threading macros31111 +Node: Modifying filled taxys31650 +Node: Magit section32743 +Node: Changelog33344 +Node: 02-pre33494 +Node: Changes33628 +Node: Additions33916 +Node: Fixes34831 +Node: 0135081 +Node: Development35184 +Node: Copyright assignment35390 +Node: Credits35989 +Node: License36179 End Tag Table