branch: externals/taxy commit fcc780f44a0a354519f190d96d71b57874ad2e00 Author: Adam Porter <a...@alphapapa.net> Commit: Adam Porter <a...@alphapapa.net>
Add: (taxy-take-keyed*) --- README.org | 54 ++++++++++++++++++++++++++++++++++++--- taxy.el | 29 +++++++++++++++++++++ taxy.info | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 154 insertions(+), 15 deletions(-) diff --git a/README.org b/README.org index e062744..722e0a6 100644 --- a/README.org +++ b/README.org @@ -250,6 +250,12 @@ After defining a taxy, call ~taxy-fill~ with it and a list of objects to fill th To return a taxy in a more human-readable format (with only relevant fields included), use ~taxy-plain~. You may also use ~taxy-map~ to replace objects in a taxy with, e.g. a more useful representation. ** Dynamic taxys +:PROPERTIES: +:TOC: :include descendants +:END: +:CONTENTS: +- [[#multi-level-dynamic-taxys][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: @@ -302,9 +308,51 @@ The taxy's ~:take~ function is set to the ~taxy-take-keyed~ function, partially #<buffer dash.el> #<buffer *Pp Eval Output*> #<buffer taxy.el> #<buffer scratch.el>)))))) #+END_SRC -# ** Tips -# -# + You can customize settings in the =taxy= group. +*** 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. 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 perf [...] + +# MAYBE: A macro to define :take functions more concisely. + +#+BEGIN_SRC elisp :exports code + (defvar buffery + (cl-labels ((buffer-mode (buffer) (buffer-local-value 'major-mode buffer)) + (buffer-directory (buffer) (buffer-local-value 'default-directory buffer))) + (make-taxy + :name "Buffers" + :taxys (list + (make-taxy + :name "Directories" + :take (lambda (object taxy) + (taxy-take-keyed* (list #'buffer-directory #'buffer-mode) object taxy))))))) + + (taxy-plain + (taxy-fill (buffer-list) + (taxy-emptied buffery))) +#+END_SRC + +That produces a list like: + +#+BEGIN_SRC elisp + ("Buffers" + (("Directories" + (("~/src/emacs/ement.el/" + ((dired-mode + (#<buffer ement.el<emacs>)) + (emacs-lisp-mode + (#<buffer ement.el<ement.el> #<buffer ement-room-list.el> #<buffer ement-room.el>)) + (magit-diff-mode + (#<buffer magit-diff: ement.el>)))) + ("~/src/emacs/taxy.el/" + ((dired-mode + (#<buffer taxy.el<emacs>)) + (Info-mode + (#<buffer *info*>)) + (magit-status-mode + (#<buffer magit: taxy.el>)) + (emacs-lisp-mode + (#<buffer taxy-magit-section.el> #<buffer taxy.el<taxy.el> #<buffer scratch.el>)))))))) +#+END_SRC ** Reusable taxys diff --git a/taxy.el b/taxy.el index 03e09b9..47bfb8c 100644 --- a/taxy.el +++ b/taxy.el @@ -133,6 +133,35 @@ by KEY-NAME-FN called with OBJECT." (taxy-taxys taxy)))))) (push object (taxy-objects key-taxy)))) +(cl-defun taxy-take-keyed* (key-fns object taxy &key (key-name-fn #'identity)) + "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 + (push (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)))) + (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)))))) + ;;;; Footer (provide 'taxy) diff --git a/taxy.info b/taxy.info index f0d0a8b..ac6b346 100644 --- a/taxy.info +++ b/taxy.info @@ -29,7 +29,9 @@ Usage * Threading macros:: * Magit section:: +Dynamic taxys +* Multi-level dynamic taxys:: Changelog @@ -263,8 +265,9 @@ File: README.info, Node: Dynamic taxys, Next: Reusable taxys, Up: Usage 2.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’ + • + 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: @@ -317,6 +320,64 @@ produces this taxonomy of buffers: #<buffer ement-room.el> #<buffer init.el> #<buffer bufler.el> #<buffer dash.el> #<buffer *Pp Eval Output*> #<buffer taxy.el> #<buffer scratch.el>)))))) +* Menu: + +* Multi-level dynamic taxys:: + + +File: README.info, Node: Multi-level dynamic taxys, Up: Dynamic taxys + +2.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. 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. + + (defvar buffery + (cl-labels ((buffer-mode (buffer) (buffer-local-value 'major-mode buffer)) + (buffer-directory (buffer) (buffer-local-value 'default-directory buffer))) + (make-taxy + :name "Buffers" + :taxys (list + (make-taxy + :name "Directories" + :take (lambda (object taxy) + (taxy-take-keyed* (list #'buffer-directory #'buffer-mode) object taxy))))))) + + (taxy-plain + (taxy-fill (buffer-list) + (taxy-emptied buffery))) + + That produces a list like: + + ("Buffers" + (("Directories" + (("~/src/emacs/ement.el/" + ((dired-mode + (#<buffer ement.el<emacs>)) + (emacs-lisp-mode + (#<buffer ement.el<ement.el> #<buffer ement-room-list.el> #<buffer ement-room.el>)) + (magit-diff-mode + (#<buffer magit-diff: ement.el>)))) + ("~/src/emacs/taxy.el/" + ((dired-mode + (#<buffer taxy.el<emacs>)) + (Info-mode + (#<buffer *info*>)) + (magit-status-mode + (#<buffer magit: taxy.el>)) + (emacs-lisp-mode + (#<buffer taxy-magit-section.el> #<buffer taxy.el<taxy.el> #<buffer scratch.el>)))))))) + File: README.info, Node: Reusable taxys, Next: Threading macros, Prev: Dynamic taxys, Up: Usage @@ -489,16 +550,17 @@ GPLv3 Tag Table: Node: Top218 -Node: Examples1057 -Node: Usage8957 -Node: Dynamic taxys11040 -Node: Reusable taxys13407 -Node: Threading macros17564 -Node: Magit section18085 -Node: Changelog18680 -Node: 01-pre18818 -Node: Development18912 -Node: License19083 +Node: Examples1101 +Node: Usage9001 +Node: Dynamic taxys11084 +Node: Multi-level dynamic taxys13502 +Node: Reusable taxys15662 +Node: Threading macros19819 +Node: Magit section20340 +Node: Changelog20935 +Node: 01-pre21073 +Node: Development21167 +Node: License21338 End Tag Table