.
675 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ELPA_DEPENDENCIES = package-lint let-alist
2 | ELPA_ARCHIVES = melpa-stable gnu
3 | TEST_ERT_FILES = $(wildcard test/*.el)
4 | LINT_PACKAGE_LINT_FILES = $(wildcard *.el)
5 | LINT_CHECKDOC_FILES = ${LINT_PACKAGE_LINT_FILES} ${TEST_ERT_FILES}
6 | LINT_COMPILE_FILES = ${LINT_CHECKDOC_FILES}
7 |
8 | makel.mk:
9 | # Download makel
10 | @if [ -f ../makel/makel.mk ]; then \
11 | ln -s ../makel/makel.mk .; \
12 | else \
13 | curl \
14 | --fail --silent --show-error --insecure --location \
15 | --retry 9 --retry-delay 9 \
16 | -O https://gitea.petton.fr/DamienCassou/makel/raw/v0.5.3/makel.mk; \
17 | fi
18 |
19 | # Include makel.mk if present
20 | -include makel.mk
21 |
--------------------------------------------------------------------------------
/README.org:
--------------------------------------------------------------------------------
1 | * ⚠ Integration into Emas core
2 |
3 | This project got integrated into Emacs core in release [[https://www.gnu.org/savannah-checkouts/gnu/emacs/news/NEWS.28.1][28.1]].
4 | Please contribute patches to Emacs core directly instead of here.
5 |
6 | * Hierarchy
7 | #+BEGIN_HTML
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | #+END_HTML
17 |
18 | ** Summary
19 |
20 | Library to create, query, navigate and display hierarchy structures. You might want to read the [[https://emacs.cafe/emacs/guest-post/2017/06/26/hierarchy.html][introductory blog post]].
21 |
22 | ** Installing
23 |
24 | Use [[http://melpa.org/][melpa]].
25 |
26 | ** Using
27 |
28 | After having created a hierarchy with ~hierarchy-new~, populate it by
29 | calling ~hierarchy-add-tree~ or ~hierarchy-add-trees~. You can
30 | then optionally sort its element with ~hierarchy-sort~. For example,
31 | you can create an animal hierarchy by passing a child-to-parent
32 | function to ~hierarchy-add-tree~:
33 |
34 | #+BEGIN_SRC emacs-lisp :session animals
35 | (require 'hierarchy)
36 |
37 | (setq animals (hierarchy-new))
38 |
39 | (let ((parentfn
40 | ;; Given an item, return its parent
41 | (lambda (item)
42 | (cl-case item
43 | (dove 'bird)
44 | (pigeon 'bird)
45 | (bird 'animal)
46 | (dolphin 'animal)
47 | (cow 'animal)))))
48 | (hierarchy-add-tree animals 'dove parentfn)
49 | (hierarchy-add-tree animals 'pigeon parentfn)
50 | (hierarchy-add-tree animals 'dolphin parentfn)
51 | (hierarchy-add-tree animals 'cow parentfn))
52 |
53 | (hierarchy-sort animals)
54 | #+END_SRC
55 |
56 | #+RESULTS:
57 | | bird | animal |
58 |
59 | You can learn more about your hierarchy by using functions such as
60 | ~hierarchy-roots~, ~hierarchy-length~, ~hierarchy-children~,
61 | ~hierarchy-descendant-p~. For example, ~hierarchy-roots~ returns any
62 | item without a parent in a hierarchy:
63 |
64 | #+BEGIN_SRC emacs-lisp :session animals :exports both
65 | (hierarchy-roots animals)
66 | #+END_SRC
67 |
68 | #+RESULTS:
69 | | animal |
70 |
71 | ~animal~ is the only item of the ~animals~ hierarchy with no
72 | parent. To get all items with no child, use ~hierarchy-leafs~:
73 |
74 | #+BEGIN_SRC emacs-lisp :session animals :exports both
75 | (hierarchy-leafs animals)
76 | #+END_SRC
77 |
78 | #+RESULTS:
79 | | dove | pigeon | dolphin | cow |
80 |
81 | It is possible to get the children of an item by using
82 | ~hierarchy-children~:
83 |
84 | #+BEGIN_SRC emacs-lisp :session animals :exports both
85 | (hierarchy-children animals 'animal)
86 | #+END_SRC
87 |
88 | #+RESULTS:
89 | | bird | cow | dolphin |
90 |
91 | We see here that ~animal~ has three children.
92 |
93 | You can navigate a hierarchy using ~hierarchy-map-item~,
94 | ~hierarchy-map~ and ~hierarchy-map-tree~. For example, this code
95 | inserts a text view of a hierarchy in a buffer:
96 |
97 | #+BEGIN_SRC emacs-lisp :session animals :exports both
98 | (with-temp-buffer
99 | (hierarchy-map
100 | (hierarchy-labelfn-indent
101 | (lambda (animal _) (insert (symbol-name animal) "\n")))
102 | animals)
103 | (buffer-substring (point-min) (point-max)))
104 | #+END_SRC
105 |
106 | #+RESULTS:
107 | : animal
108 | : bird
109 | : dove
110 | : pigeon
111 | : cow
112 | : dolphin
113 |
114 | The indentation between a parent and its child can be configured by
115 | passing one more parameter to ~hierarchy-labelfn-indent~. You can also
116 | display clickable buttons instead of just plain text using either
117 | ~hierarchy-labelfn-button~ or ~hierarchy-labelfn-button-if~.
118 |
119 | If you want a buffer containing only a hierarchy while being able to
120 | navigate it with standard key-bindings use either
121 | ~hierarchy-tabulated-display~ or ~hierarchy-tree-display~ as
122 | shown in below animated pictures.
123 |
124 | #+BEGIN_SRC emacs-lisp :session animals :exports code
125 | (switch-to-buffer
126 | (hierarchy-tabulated-display
127 | animals
128 | (hierarchy-labelfn-indent
129 | (hierarchy-labelfn-button
130 | (lambda (item _) (insert (symbol-name item)))
131 | (lambda (item _) (message "You clicked on: %s" item))))))
132 | #+END_SRC
133 |
134 | #+RESULTS:
135 | : #>
136 |
137 | [[file:media/animals-tabulated-anime.gif]]
138 |
139 | #+BEGIN_SRC emacs-lisp :session animals :exports code
140 | (switch-to-buffer
141 | (hierarchy-tree-display
142 | animals
143 | (lambda (item _) (insert (symbol-name item)))))
144 | #+END_SRC
145 |
146 | #+RESULTS:
147 | : t
148 |
149 | [[file:media/animals-tree-anime.gif]]
150 |
151 | ** Examples
152 | *** File-system example
153 | The hierarchy library can be used to display any kind of hierarchy you
154 | need. For example, a [[file:examples/hierarchy-examples-fs.el][file-system navigator]] is provided as an example.
155 |
156 | #+BEGIN_SRC emacs-lisp :session animals :exports code
157 | (load "./examples/hierarchy-examples-fs.el")
158 |
159 | ;; Execute one of the following lines to show the `.emacd.d' hierarchy
160 | ;; in either a tabulated list or a tree widget. This takes around 3
161 | ;; seconds on my computer.
162 |
163 | (hierarchy-examples-fs-display-filesystem "~/.emacs.d")
164 |
165 | (hierarchy-examples-fs-display-filesystem-tree "~/.emacs.d")
166 | #+END_SRC
167 |
168 | #+RESULTS:
169 | : #
170 |
171 | [[file:media/files-tabulated-anime.gif]]
172 |
173 | [[file:media/files-tree-anime.gif]]
174 | *** Faces hierarchy example
175 | Emacs and packages define quite a lot of faces. Because a face may
176 | inherit from another one, we can get [[file:examples/hierarchy-examples-faces.el][a hierarchy of them]]:
177 |
178 | [[file:media/faces-tree.png]]
179 |
180 | This is based on an [[https://github.com/DamienCassou/hierarchy/issues/74][idea and code from Yuan Fu]].
181 | *** Major modes hierarchy example
182 | Emacs and packages define quite a lot of major modes. A major mode
183 | usually derives from another one which means we can get a [[file:examples/hierarchy-examples-major-modes.el][hierarchy of
184 | major modes]]:
185 |
186 | [[file:media/major-modes-tabulated.png]]
187 | *** Class hierarchy example
188 | With a bit more work, the hierarchy library can also be used to
189 | display class hierarchies (as I am currently experimenting in [[https://github.com/DamienCassou/klassified.el][this
190 | project]]).
191 |
192 | [[file:media/klassified-tabulated-anime.gif]]
193 | *** JSON navigator example
194 | A [[https://github.com/DamienCassou/json-navigator][JSON navigator]] is also implemented as yet another example.
195 |
196 | [[file:media/json-tree-anime.gif]]
197 | ** Contributing
198 |
199 | Yes, please do! See [[file:CONTRIBUTING.md][CONTRIBUTING]] for guidelines.
200 |
201 | ** License
202 |
203 | See [[file:COPYING][COPYING]]. Copyright (c) 2017 Damien Cassou.
204 |
--------------------------------------------------------------------------------
/examples/hierarchy-examples-faces.el:
--------------------------------------------------------------------------------
1 | ;;; hierarchy-examples-faces.el --- Represent how faces inherit from each other -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2017 Damien Cassou
4 |
5 | ;; Author: Damien Cassou
6 |
7 | ;; This program is free software; you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 |
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 |
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 |
20 | ;;; Commentary:
21 |
22 | ;;
23 |
24 | ;;; Code:
25 |
26 | (require 'hierarchy)
27 |
28 | (defun hierarchy-examples-faces--parent (face)
29 | "Return parent face of FACE.
30 | If FACE doesn't inherit from any other face or inherit from an
31 | invalid face, return 'root-face."
32 | (let ((parent-face (if (eq face 'root-face)
33 | nil ;; the root has no parent
34 | (or (face-attribute face :inherit nil 'default)
35 | 'root-face))))
36 | (cond ((facep parent-face) parent-face)
37 | ((null parent-face) nil) ;; the root has no parent
38 | (t 'root-face))))
39 |
40 | (defun hierarchy-examples-faces--build-hierarchy ()
41 | "Return a hierarchy of all faces Emacs knows about."
42 | (let ((hierarchy (hierarchy-new)))
43 | (hierarchy-add-trees hierarchy (face-list) #'hierarchy-examples-faces--parent)
44 | hierarchy))
45 |
46 | (defun hierarchy-examples-faces-display-faces ()
47 | "Display hierarchy of all faces Emacs knows about in a tree widget."
48 | (interactive)
49 | (require 'tree-widget)
50 | (let* ((hierarchy (hierarchy-examples-faces--build-hierarchy)))
51 | (switch-to-buffer
52 | (hierarchy-tree-display
53 | hierarchy
54 | (lambda (face _) (insert (format "%s" face)))))))
55 |
56 | ;; (hierarchy-examples-faces-display-faces)
57 |
58 | (provide 'hierarchy-examples-faces)
59 | ;;; hierarchy-examples-faces.el ends here
60 |
--------------------------------------------------------------------------------
/examples/hierarchy-examples-fs.el:
--------------------------------------------------------------------------------
1 | ;;; hierarchy-examples-fs.el --- Represent the filesystem as a hierarchy -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2017 Damien Cassou
4 |
5 | ;; Author: Damien Cassou
6 |
7 | ;; This program is free software; you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 |
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 |
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 |
20 | ;;; Commentary:
21 |
22 | ;;
23 |
24 | ;;; Code:
25 |
26 | (require 'hierarchy)
27 |
28 | (defun hierarchy-examples-fs-directory-p (file)
29 | "Return non-nil if FILE is a directory and not . or ..."
30 | (and (not (string-suffix-p "/." file))
31 | (not (string-suffix-p "/.." file))
32 | (file-directory-p file)))
33 |
34 | (defun hierarchy-examples-fs-children (folder)
35 | "Return sub-directories of FOLDER as absolute paths."
36 | (when (file-directory-p folder)
37 | (seq-filter #'hierarchy-examples-fs-directory-p (directory-files folder t))))
38 |
39 | (defun hierarchy-examples-fs-parent (folder)
40 | "Return parent of FOLDER."
41 | (when (not (string= folder "/"))
42 | (directory-file-name (file-name-directory folder))))
43 |
44 | (defun hierarchy-examples-fs-build-fs-hierarchy (folder)
45 | "Return hierarchy of FOLDER."
46 | (let* ((folder (expand-file-name folder))
47 | (parentfn #'hierarchy-examples-fs-parent)
48 | (childrenfn (lambda (file) (when (string-prefix-p folder file)
49 | (hierarchy-examples-fs-children file))))
50 | (hierarchy (hierarchy-new)))
51 | (hierarchy-add-tree hierarchy folder parentfn childrenfn)
52 | (hierarchy-sort hierarchy)
53 | hierarchy))
54 |
55 | (defun hierarchy-examples-fs-labelfn (folder _)
56 | "Insert name of FOLDER at current position.
57 |
58 | _ is ignored."
59 | (insert (if (string= folder "/")
60 | "/"
61 | (file-name-nondirectory folder))))
62 |
63 | (defun hierarchy-examples-fs-display-filesystem (&optional folder)
64 | "Display hierarchy of FOLDER in a tabulated list."
65 | (let* ((hierarchy (hierarchy-examples-fs-build-fs-hierarchy folder))
66 | (buffer (hierarchy-tabulated-display
67 | hierarchy
68 | (hierarchy-labelfn-indent
69 | (hierarchy-labelfn-button
70 | #'hierarchy-examples-fs-labelfn (lambda (item _) (dired item)))))))
71 | (switch-to-buffer buffer)))
72 |
73 | (defun hierarchy-examples-fs-display-filesystem-tree (&optional folder)
74 | "Display hierarchy of FOLDER in a tree widget."
75 | (require 'tree-widget)
76 | (let* ((hierarchy (hierarchy-examples-fs-build-fs-hierarchy folder)))
77 | (switch-to-buffer (hierarchy-tree-display hierarchy #'hierarchy-examples-fs-labelfn))))
78 |
79 | ;; Execute one of the following lines to show the .emacd.d hierarchy
80 | ;; in either a tabulated list or a tree widget. This takes around 4
81 | ;; seconds on my computer.
82 | ;;
83 | ;; (hierarchy-examples-fs-display-filesystem "~/.emacs.d")
84 | ;;
85 | ;; (hierarchy-examples-fs-display-filesystem-tree "~/.emacs.d")
86 |
87 | (provide 'hierarchy-examples-fs)
88 | ;;; hierarchy-examples-fs.el ends here
89 |
--------------------------------------------------------------------------------
/examples/hierarchy-examples-major-modes.el:
--------------------------------------------------------------------------------
1 | ;;; hierarchy-examples-major-modes.el --- Represent how major-modes inherit from each other -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2019 Damien Cassou
4 |
5 | ;; Author: Damien Cassou
6 |
7 | ;; This program is free software; you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 |
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 |
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 |
20 | ;;; Commentary:
21 |
22 | ;;
23 |
24 | ;;; Code:
25 |
26 | (require 'hierarchy)
27 |
28 | (defun hierarchy-examples-major-modes--major-mode-p (f)
29 | "Return non-nil if F is a major-mode function."
30 | ;; copy-edited from counsel.el (in the swiper/ivy repository)
31 | (and (commandp f) (string-match "-mode$" (symbol-name f))
32 | (null (help-function-arglist f))))
33 |
34 | (defun hierarchy-examples-major-modes--all-major-modes ()
35 | "Return a list of all major modes."
36 | (let ((major-modes (list)))
37 | (mapatoms
38 | (lambda (symbol)
39 | (when (hierarchy-examples-major-modes--major-mode-p symbol)
40 | (setq major-modes (cons symbol major-modes)))))
41 | major-modes))
42 |
43 | (defun hierarchy-examples-major-modes--major-mode-parent (f)
44 | "Return the major mode F derive from.
45 | If F doesn't derive from any major-mode, return `root-mode'."
46 | (let ((parent-mode (or (get f 'derived-mode-parent))))
47 | (cond
48 | ((eq f 'root-mode) nil)
49 | ((null parent-mode) 'root-mode)
50 | (t parent-mode))))
51 |
52 | (defun hierarchy-examples-major-modes--major-mode-build-hierarchy ()
53 | "Return a hierarchy of all major modes."
54 | (let ((hierarchy (hierarchy-new)))
55 | (hierarchy-add-trees hierarchy (hierarchy-examples-major-modes--all-major-modes) #'hierarchy-examples-major-modes--major-mode-parent)
56 | (hierarchy-sort hierarchy)
57 | hierarchy))
58 |
59 | (defun hierarchy-examples-major-modes-display-major-modes ()
60 | "Display all major modes and their inheritance relationship."
61 | (interactive)
62 | (let* ((hierarchy (hierarchy-examples-major-modes--major-mode-build-hierarchy))
63 | (buffer (hierarchy-tabulated-display
64 | hierarchy
65 | (hierarchy-labelfn-indent
66 | (hierarchy-labelfn-button
67 | (lambda (item _) (insert (format "%s" item)))
68 | (lambda (item _) (find-function item)))))))
69 | (switch-to-buffer buffer)))
70 |
71 | ;; (hierarchy-examples-major-modes-display-major-modes)
72 |
73 | (provide 'hierarchy-examples-major-modes)
74 | ;;; hierarchy-examples-major-modes.el ends here
75 |
--------------------------------------------------------------------------------
/hierarchy.el:
--------------------------------------------------------------------------------
1 | ;;; hierarchy.el --- Library to create and display hierarchy structures -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2017 Damien Cassou
4 |
5 | ;; Author: Damien Cassou
6 | ;; Maintainer: Damien Cassou
7 | ;; Version: 0.7.0
8 | ;; Package-Requires: ((emacs "25.1"))
9 | ;; GIT: https://github.com/DamienCassou/hierarchy
10 | ;; URL: https://github.com/DamienCassou/hierarchy
11 | ;;
12 | ;; This file is not part of GNU Emacs.
13 |
14 | ;; This program is free software: you can redistribute it and/or modify
15 | ;; it under the terms of the GNU General Public License as published by
16 | ;; the Free Software Foundation, either version 3 of the License, or
17 | ;; (at your option) any later version.
18 |
19 | ;; This program is distributed in the hope that it will be useful,
20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 | ;; GNU General Public License for more details.
23 |
24 | ;; You should have received a copy of the GNU General Public License
25 | ;; along with this program. If not, see .
26 |
27 | ;;; Commentary:
28 |
29 | ;; Library to create, query, navigate and display hierarchy structures.
30 |
31 | ;; Creation: After having created a hierarchy with `hierarchy-new', populate it by
32 | ;; calling `hierarchy-add-tree' or `hierarchy-add-trees'. You can then optionally sort its
33 | ;; element with `hierarchy-sort'.
34 |
35 | ;; Querying: You can learn more about your hierarchy by using functions such as
36 | ;; `hierarchy-roots', `hierarchy-has-item', `hierarchy-length', `hierarchy-parent', `hierarchy-descendant-p'.
37 |
38 | ;; Navigation: When your hierarchy is ready, you can use `hierarchy-map-item', `hierarchy-map',
39 | ;; and `map-tree' to apply functions to elements of the hierarchy.
40 |
41 | ;; Display: You can display a hierarchy as a tabulated list using
42 | ;; `hierarchy-tabulated-display' and as an expandable/foldable tree
43 | ;; using `hierarchy-convert-to-tree-widget'. The
44 | ;; `hierarchy-labelfn-*' functions will help you display each item of
45 | ;; the hierarchy the way you want it.
46 |
47 | ;;; Limitation:
48 |
49 | ;; Current implementation uses #'equal to find and distinguish elements. Support
50 | ;; for user-provided equality definition is desired but not yet implemented.
51 |
52 | ;;; Code:
53 |
54 | (require 'seq)
55 | (require 'map)
56 | (require 'subr-x)
57 |
58 |
59 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
60 | ;; Helpers
61 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
62 |
63 | (cl-defstruct (hierarchy
64 | (:constructor hierarchy--make)
65 | (:conc-name hierarchy--))
66 | (roots (list)) ; list of the hierarchy roots (no parent)
67 | (parents (make-hash-table :test 'equal)) ; map an item to its parent
68 | (children (make-hash-table :test 'equal)) ; map an item to its childre
69 | ;; cache containing the set of all items in the hierarchy
70 | (seen-items (make-hash-table :test 'equal))) ; map an item to t
71 |
72 | (defun hierarchy--seen-items-add (hierarchy item)
73 | "In HIERARCHY, add ITEM to seen items."
74 | (map-put (hierarchy--seen-items hierarchy) item t))
75 |
76 | (defun hierarchy--compute-roots (hierarchy)
77 | "Search roots of HIERARCHY and return them."
78 | (cl-set-difference
79 | (map-keys (hierarchy--seen-items hierarchy))
80 | (map-keys (hierarchy--parents hierarchy))
81 | :test #'equal))
82 |
83 | (defun hierarchy--sort-roots (hierarchy sortfn)
84 | "Compute, sort and store the roots of HIERARCHY.
85 |
86 | SORTFN is a function taking two items of the hierarchy as parameter and
87 | returning non-nil if the first parameter is lower than the second."
88 | (setf (hierarchy--roots hierarchy)
89 | (sort (hierarchy--compute-roots hierarchy)
90 | sortfn)))
91 |
92 | (defun hierarchy--add-relation (hierarchy item parent acceptfn)
93 | "In HIERARCHY, add ITEM as child of PARENT.
94 |
95 | ACCEPTFN is a function returning non-nil if its parameter (any object)
96 | should be an item of the hierarchy."
97 | (let* ((existing-parent (hierarchy-parent hierarchy item))
98 | (has-parent-p (funcall acceptfn existing-parent)))
99 | (cond
100 | ((and has-parent-p (not (equal existing-parent parent)))
101 | (error "An item (%s) can only have one parent: '%s' vs '%s'"
102 | item existing-parent parent))
103 | ((not has-parent-p)
104 | (let ((existing-children (map-elt (hierarchy--children hierarchy) parent (list))))
105 | (map-put (hierarchy--children hierarchy) parent (append existing-children (list item))))
106 | (map-put (hierarchy--parents hierarchy) item parent)))))
107 |
108 | (defun hierarchy--set-equal (list1 list2 &rest cl-keys)
109 | "Return non-nil if LIST1 and LIST2 have same elements.
110 |
111 | I.e., if every element of LIST1 also appears in LIST2 and if
112 | every element of LIST2 also appears in LIST1.
113 |
114 | CL-KEYS are key-value pairs just like in `cl-subsetp'. Supported
115 | keys are :key and :test."
116 | (and (apply 'cl-subsetp list1 list2 cl-keys)
117 | (apply 'cl-subsetp list2 list1 cl-keys)))
118 |
119 |
120 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
121 | ;; Creation
122 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
123 |
124 | (defun hierarchy-new ()
125 | "Create a hierarchy and return it."
126 | (hierarchy--make))
127 |
128 | (defun hierarchy-add-tree (hierarchy item parentfn &optional childrenfn acceptfn)
129 | "In HIERARCHY, add ITEM.
130 |
131 | PARENTFN is either nil or a function defining the child-to-parent
132 | relationship: this function takes an item as parameter and should return
133 | the parent of this item in the hierarchy. If the item has no parent in the
134 | hierarchy (i.e., it should be a root), the function should return an object
135 | not accepted by acceptfn (i.e., nil for the default value of acceptfn).
136 |
137 | CHILDRENFN is either nil or a function defining the parent-to-children
138 | relationship: this function takes an item as parameter and should return a
139 | list of children of this item in the hierarchy.
140 |
141 | If both PARENTFN and CHILDRENFN are non-nil, the results of PARENTFN and
142 | CHILDRENFN are expected to be coherent with each other.
143 |
144 | ACCEPTFN is a function returning non-nil if its parameter (any object)
145 | should be an item of the hierarchy. By default, ACCEPTFN returns non-nil
146 | if its parameter is non-nil."
147 | (unless (hierarchy-has-item hierarchy item)
148 | (let ((acceptfn (or acceptfn #'identity)))
149 | (hierarchy--seen-items-add hierarchy item)
150 | (let ((parent (and parentfn (funcall parentfn item))))
151 | (when (funcall acceptfn parent)
152 | (hierarchy--add-relation hierarchy item parent acceptfn)
153 | (hierarchy-add-tree hierarchy parent parentfn childrenfn)))
154 | (let ((children (and childrenfn (funcall childrenfn item))))
155 | (mapc (lambda (child)
156 | (when (funcall acceptfn child)
157 | (hierarchy--add-relation hierarchy child item acceptfn)
158 | (hierarchy-add-tree hierarchy child parentfn childrenfn)))
159 | children)))))
160 |
161 | (defun hierarchy-add-trees (hierarchy items parentfn &optional childrenfn acceptfn)
162 | "Call `hierarchy-add-tree' on HIERARCHY and each element of ITEMS.
163 |
164 | PARENTFN, CHILDRENFN and ACCEPTFN have the same meaning as in `hierarchy-add'."
165 | (seq-map (lambda (item)
166 | (hierarchy-add-tree hierarchy item parentfn childrenfn acceptfn))
167 | items))
168 |
169 | (defun hierarchy-add-list (hierarchy list &optional wrap childrenfn)
170 | "Add to HIERARCHY the sub-lists in LIST.
171 |
172 | If WRAP is non-nil, allow duplicate items in LIST by wraping each
173 | item in a cons (id . item). The root's id is 1.
174 |
175 | CHILDRENFN is a function (defaults to `cdr') taking LIST as a
176 | parameter which should return LIST's children (a list). Each
177 | child is (recursively) passed as a parameter to CHILDRENFN to get
178 | its own children. Because of this parameter, LIST can be
179 | anything, not necessarily a list."
180 | (let* ((childrenfn (or childrenfn #'cdr))
181 | (id 0)
182 | (wrapfn (lambda (item)
183 | (if wrap
184 | (cons (setq id (1+ id)) item)
185 | item)))
186 | (unwrapfn (if wrap #'cdr #'identity)))
187 | (hierarchy-add-tree
188 | hierarchy (funcall wrapfn list) nil
189 | (lambda (item)
190 | (mapcar wrapfn (funcall childrenfn
191 | (funcall unwrapfn item)))))
192 | hierarchy))
193 |
194 | (defun hierarchy-from-list (list &optional wrap childrenfn)
195 | "Create and return a hierarchy built from LIST.
196 |
197 | This function passes LIST, WRAP and CHILDRENFN unchanged to
198 | `hierarchy-add-list'."
199 | (hierarchy-add-list (hierarchy-new) list wrap childrenfn))
200 |
201 | (defun hierarchy-sort (hierarchy &optional sortfn)
202 | "Modify HIERARCHY so that its roots and item's children are sorted.
203 |
204 | SORTFN is a function taking two items of the hierarchy as parameter and
205 | returning non-nil if the first parameter is lower than the second. By
206 | default, SORTFN is `string-lessp'."
207 | (let ((sortfn (or sortfn #'string-lessp)))
208 | (hierarchy--sort-roots hierarchy sortfn)
209 | (mapc (lambda (parent)
210 | (setf
211 | (map-elt (hierarchy--children hierarchy) parent)
212 | (sort (map-elt (hierarchy--children hierarchy) parent) sortfn)))
213 | (map-keys (hierarchy--children hierarchy)))))
214 |
215 | (defun hierarchy-extract-tree (hierarchy item)
216 | "Return a copy of HIERARCHY with ITEM's descendants and parents."
217 | (if (not (hierarchy-has-item hierarchy item))
218 | nil
219 | (let ((tree (hierarchy-new)))
220 | (hierarchy-add-tree tree item
221 | (lambda (each) (hierarchy-parent hierarchy each))
222 | (lambda (each)
223 | (when (or (equal each item)
224 | (hierarchy-descendant-p hierarchy each item))
225 | (hierarchy-children hierarchy each))))
226 | tree)))
227 |
228 | (defun hierarchy-copy (hierarchy)
229 | "Return a copy of HIERARCHY.
230 |
231 | Items in HIERARCHY are shared, but structure is not."
232 | (hierarchy-map-hierarchy (lambda (item _) (identity item)) hierarchy))
233 |
234 |
235 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
236 | ;; Querying
237 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
238 |
239 | (defun hierarchy-items (hierarchy)
240 | "Return a list of all items of HIERARCHY."
241 | (map-keys (hierarchy--seen-items hierarchy)))
242 |
243 | (defun hierarchy-has-item (hierarchy item)
244 | "Return t if HIERARCHY includes ITEM."
245 | (map-contains-key (hierarchy--seen-items hierarchy) item))
246 |
247 | (defun hierarchy-empty-p (hierarchy)
248 | "Return t if HIERARCHY is empty."
249 | (= 0 (hierarchy-length hierarchy)))
250 |
251 | (defun hierarchy-length (hierarchy)
252 | "Return the number of items in HIERARCHY."
253 | (hash-table-count (hierarchy--seen-items hierarchy)))
254 |
255 | (defun hierarchy-has-root (hierarchy item)
256 | "Return t if one of HIERARCHY's roots is ITEM.
257 |
258 | A root is an item with no parent."
259 | (seq-contains (hierarchy-roots hierarchy) item))
260 |
261 | (defun hierarchy-roots (hierarchy)
262 | "Return all roots of HIERARCHY.
263 |
264 | A root is an item with no parent."
265 | (let ((roots (hierarchy--roots hierarchy)))
266 | (or roots
267 | (hierarchy--compute-roots hierarchy))))
268 |
269 | (defun hierarchy-leafs (hierarchy &optional node)
270 | "Return all leafs of HIERARCHY.
271 |
272 | A leaf is an item with no child.
273 |
274 | If NODE is an item of HIERARCHY, only return leafs under NODE."
275 | (let ((leafs (cl-set-difference
276 | (map-keys (hierarchy--seen-items hierarchy))
277 | (map-keys (hierarchy--children hierarchy)))))
278 | (if (hierarchy-has-item hierarchy node)
279 | (seq-filter (lambda (item) (hierarchy-descendant-p hierarchy item node)) leafs)
280 | leafs)))
281 |
282 | (defun hierarchy-parent (hierarchy item)
283 | "In HIERARCHY, return parent of ITEM."
284 | (map-elt (hierarchy--parents hierarchy) item))
285 |
286 | (defun hierarchy-children (hierarchy parent)
287 | "In HIERARCHY, return children of PARENT."
288 | (map-elt (hierarchy--children hierarchy) parent (list)))
289 |
290 | (defun hierarchy-child-p (hierarchy item1 item2)
291 | "In HIERARCHY, return non-nil if and only if ITEM1 is a child of ITEM2."
292 | (equal (hierarchy-parent hierarchy item1) item2))
293 |
294 | (defun hierarchy-descendant-p (hierarchy item1 item2)
295 | "In HIERARCHY, return non-nil if and only if ITEM1 is a descendant of ITEM2.
296 |
297 | ITEM1 is a descendant of ITEM2 if and only if both are items of HIERARCHY
298 | and either:
299 |
300 | - ITEM1 is child of ITEM2, or
301 | - ITEM1's parent is a descendant of ITEM2."
302 | (and
303 | (hierarchy-has-item hierarchy item1)
304 | (hierarchy-has-item hierarchy item2)
305 | (or
306 | (hierarchy-child-p hierarchy item1 item2)
307 | (hierarchy-descendant-p hierarchy (hierarchy-parent hierarchy item1) item2))))
308 |
309 | (defun hierarchy-equal (hierarchy1 hierarchy2)
310 | "Return t if HIERARCHY1 and HIERARCHY2 are equal.
311 |
312 | Two equal hierarchies share the same items and the same
313 | relationships among them."
314 | (and (hierarchy-p hierarchy1)
315 | (hierarchy-p hierarchy2)
316 | (= (hierarchy-length hierarchy1) (hierarchy-length hierarchy2))
317 | ;; parents are the same
318 | (seq-every-p (lambda (child)
319 | (equal (hierarchy-parent hierarchy1 child)
320 | (hierarchy-parent hierarchy2 child)))
321 | (map-keys (hierarchy--parents hierarchy1)))
322 | ;; children are the same
323 | (seq-every-p (lambda (parent)
324 | (hierarchy--set-equal
325 | (hierarchy-children hierarchy1 parent)
326 | (hierarchy-children hierarchy2 parent)
327 | :test #'equal))
328 | (map-keys (hierarchy--children hierarchy1)))))
329 |
330 |
331 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
332 | ;; Navigation
333 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
334 |
335 | (defun hierarchy-map-item (func item hierarchy &optional indent)
336 | "Return the result of applying FUNC to ITEM and its descendants in HIERARCHY.
337 |
338 | This function navigates the tree top-down: FUNCTION is first called on item
339 | and then on each of its children. Results are concatenated in a list.
340 |
341 | INDENT is a number (default 0) representing the indentation of ITEM in
342 | HIERARCHY. FUNC should take 2 argument: the item and its indentation
343 | level."
344 | (let ((indent (or indent 0)))
345 | (cons
346 | (funcall func item indent)
347 | (seq-mapcat (lambda (child) (hierarchy-map-item func child hierarchy (1+ indent)))
348 | (hierarchy-children hierarchy item)))))
349 |
350 | (defun hierarchy-map (func hierarchy &optional indent)
351 | "Return the result of applying FUNC to each element of HIERARCHY.
352 |
353 | This function navigates the tree top-down: FUNCTION is first called on each
354 | root. To do so, it calls `hierarchy-map-item' on each root
355 | sequentially. Results are concatenated in a list.
356 |
357 | FUNC should take 2 arguments: the item and its indentation level.
358 |
359 | INDENT is a number (default 0) representing the indentation of HIERARCHY's
360 | roots."
361 | (let ((indent (or indent 0)))
362 | (seq-mapcat (lambda (root) (hierarchy-map-item func root hierarchy indent))
363 | (hierarchy-roots hierarchy))))
364 |
365 | (defun hierarchy-map-tree (function hierarchy &optional item indent)
366 | "Apply FUNCTION on each item of HIERARCHY under ITEM.
367 |
368 | This function navigates the tree bottom-up: FUNCTION is first called on
369 | leafs and the result is passed as parameter when calling FUNCTION on
370 | parents.
371 |
372 | FUNCTION should take 3 parameters: the current item, its indentation
373 | level (a number), and a list representing the result of applying
374 | `hierarchy-map-tree' to each child of the item.
375 |
376 | INDENT is 0 by default and is passed as second parameter to FUNCTION.
377 | INDENT is incremented by 1 at each level of the tree.
378 |
379 | This function returns the result of applying FUNCTION to ITEM (the first
380 | root if nil)."
381 | (let ((item (or item (car (hierarchy-roots hierarchy))))
382 | (indent (or indent 0)))
383 | (funcall function item indent
384 | (mapcar (lambda (child)
385 | (hierarchy-map-tree function hierarchy child (1+ indent)))
386 | (hierarchy-children hierarchy item)))))
387 |
388 | (defun hierarchy-map-hierarchy (function hierarchy)
389 | "Apply FUNCTION to each item of HIERARCHY in a new hierarchy.
390 |
391 | FUNCTION should take 2 parameters, the current item and its
392 | indentation level (a number), and should return an item to be
393 | added to the new hierarchy."
394 | (let* ((items (make-hash-table :test #'equal))
395 | (transform (lambda (item) (map-elt items item))))
396 | ;; Make 'items', a table mapping original items to their
397 | ;; transformation
398 | (hierarchy-map (lambda (item indent)
399 | (map-put items item (funcall function item indent)))
400 | hierarchy)
401 | (hierarchy--make
402 | :roots (mapcar transform (hierarchy-roots hierarchy))
403 | :parents (let ((result (make-hash-table :test #'equal)))
404 | (map-apply (lambda (child parent)
405 | (map-put result
406 | (funcall transform child)
407 | (funcall transform parent)))
408 | (hierarchy--parents hierarchy))
409 | result)
410 | :children (let ((result (make-hash-table :test #'equal)))
411 | (map-apply (lambda (parent children)
412 | (map-put result
413 | (funcall transform parent)
414 | (seq-map transform children)))
415 | (hierarchy--children hierarchy))
416 | result)
417 | :seen-items (let ((result (make-hash-table :test #'equal)))
418 | (map-apply (lambda (item v)
419 | (map-put result
420 | (funcall transform item)
421 | v))
422 | (hierarchy--seen-items hierarchy))
423 | result))))
424 |
425 |
426 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
427 | ;; Display
428 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
429 |
430 | (defun hierarchy-labelfn-indent (labelfn &optional indent-string)
431 | "Return a function rendering LABELFN indented with INDENT-STRING.
432 |
433 | INDENT-STRING defaults to a 2-space string. Indentation is
434 | multiplied by the depth of the displayed item."
435 | (let ((indent-string (or indent-string " ")))
436 | (lambda (item indent)
437 | (dotimes (_ indent) (insert indent-string))
438 | (funcall labelfn item indent))))
439 |
440 | (defun hierarchy-labelfn-button (labelfn actionfn)
441 | "Return a function rendering LABELFN in a button.
442 |
443 | Clicking the button triggers ACTIONFN. ACTIONFN is a function
444 | taking an item of HIERARCHY and an indentation value (a number)
445 | as input. This function is called when an item is clicked. The
446 | return value of ACTIONFN is ignored."
447 | (lambda (item indent)
448 | (let ((start (point)))
449 | (funcall labelfn item indent)
450 | (make-text-button start (point)
451 | 'action (lambda (_) (funcall actionfn item indent))))))
452 |
453 | (defun hierarchy-labelfn-button-if (labelfn buttonp actionfn)
454 | "Return a function rendering LABELFN as a button if BUTTONP.
455 |
456 | Pass LABELFN and ACTIONFN to `hierarchy-labelfn-button' if
457 | BUTTONP is non-nil. Otherwise, render LABELFN without making it
458 | a button.
459 |
460 | BUTTONP is a function taking an item of HIERARCHY and an
461 | indentation value (a number) as input."
462 | (lambda (item indent)
463 | (if (funcall buttonp item indent)
464 | (funcall (hierarchy-labelfn-button labelfn actionfn) item indent)
465 | (funcall labelfn item indent))))
466 |
467 | (defun hierarchy-labelfn-to-string (labelfn item indent)
468 | "Execute LABELFN on ITEM and INDENT. Return result as a string."
469 | (with-temp-buffer
470 | (funcall labelfn item indent)
471 | (buffer-substring (point-min) (point-max))))
472 |
473 | (defun hierarchy-print (hierarchy &optional to-string)
474 | "Insert HIERARCHY in current buffer as plain text.
475 |
476 | Use TO-STRING to convert each element to a string. TO-STRING is
477 | a function taking an item of HIERARCHY as input and returning a
478 | string. If nil, TO-STRING defaults to a call to `format' with \"%s\"."
479 | (let ((to-string (or to-string (lambda (item) (format "%s" item)))))
480 | (hierarchy-map
481 | (hierarchy-labelfn-indent (lambda (item _) (insert (funcall to-string item) "\n")))
482 | hierarchy)))
483 |
484 | (defun hierarchy-to-string (hierarchy &optional to-string)
485 | "Return a string representing HIERARCHY.
486 |
487 | TO-STRING is passed unchanged to `hierarchy-print'."
488 | (with-temp-buffer
489 | (hierarchy-print hierarchy to-string)
490 | (buffer-substring (point-min) (point-max))))
491 |
492 | (defun hierarchy-tabulated-imenu-action (_item-name position)
493 | "Move to ITEM-NAME at POSITION in current buffer."
494 | (goto-char position)
495 | (back-to-indentation))
496 |
497 | (define-derived-mode hierarchy-tabulated-mode tabulated-list-mode "Hierarchy tabulated"
498 | "Major mode to display a hierarchy as a tabulated list."
499 | (setq-local imenu-generic-expression
500 | ;; debbugs: 26457 - Cannot pass a function to
501 | ;; imenu-generic-expression. Add
502 | ;; `hierarchy-tabulated-imenu-action' to the end of the
503 | ;; list when bug is fixed
504 | '(("Item" "^[[:space:]]+\\(?1:.+\\)$" 1))))
505 |
506 | (defun hierarchy-tabulated-display (hierarchy labelfn &optional buffer)
507 | "Display HIERARCHY as a tabulated list in `hierarchy-tabulated-mode'.
508 |
509 | LABELFN is a function taking an item of HIERARCHY and an indentation
510 | level (a number) as input and inserting a string to be displayed in the
511 | table.
512 |
513 | The tabulated list is displayed in BUFFER, or a newly created buffer if
514 | nil. The buffer is returned."
515 | (let ((buffer (or buffer (generate-new-buffer "hierarchy-tabulated"))))
516 | (with-current-buffer buffer
517 | (hierarchy-tabulated-mode)
518 | (setq tabulated-list-format
519 | (vector '("Item name" 0 nil)))
520 | (setq tabulated-list-entries
521 | (hierarchy-map (lambda (item indent)
522 | (list item (vector (hierarchy-labelfn-to-string labelfn item indent))))
523 | hierarchy))
524 | (tabulated-list-init-header)
525 | (tabulated-list-print))
526 | buffer))
527 |
528 | (declare-function widget-convert "wid-edit")
529 | (defun hierarchy-convert-to-tree-widget (hierarchy labelfn)
530 | "Return a tree-widget for HIERARCHY.
531 |
532 | LABELFN is a function taking an item of HIERARCHY and an indentation
533 | value (a number) as parameter and inserting a string to be displayed as a
534 | node label."
535 | (require 'wid-edit)
536 | (require 'tree-widget)
537 | (hierarchy-map-tree (lambda (item indent children)
538 | (widget-convert
539 | 'tree-widget
540 | :tag (hierarchy-labelfn-to-string labelfn item indent)
541 | :args children))
542 | hierarchy))
543 |
544 | (defun hierarchy-tree-display (hierarchy labelfn &optional buffer)
545 | "Display HIERARCHY as a tree widget in a new buffer.
546 |
547 | HIERARCHY and LABELFN are passed unchanged to
548 | `hierarchy-convert-to-tree-widget'.
549 |
550 | The tree widget is displayed in BUFFER, or a newly created buffer if
551 | nil. The buffer is returned."
552 | (let ((buffer (or buffer (generate-new-buffer "*hierarchy-tree*")))
553 | (tree-widget (hierarchy-convert-to-tree-widget hierarchy labelfn)))
554 | (with-current-buffer buffer
555 | (setq-local buffer-read-only t)
556 | (let ((inhibit-read-only t))
557 | (erase-buffer)
558 | (widget-create tree-widget)
559 | (goto-char (point-min))
560 | (special-mode)))
561 | buffer))
562 |
563 | (provide 'hierarchy)
564 |
565 | ;;; hierarchy.el ends here
566 |
--------------------------------------------------------------------------------
/media/animals-tabulated-anime.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/animals-tabulated-anime.gif
--------------------------------------------------------------------------------
/media/animals-tree-anime.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/animals-tree-anime.gif
--------------------------------------------------------------------------------
/media/faces-tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/faces-tree.png
--------------------------------------------------------------------------------
/media/files-tabulated-anime.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/files-tabulated-anime.gif
--------------------------------------------------------------------------------
/media/files-tree-anime.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/files-tree-anime.gif
--------------------------------------------------------------------------------
/media/json-tree-anime.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/json-tree-anime.gif
--------------------------------------------------------------------------------
/media/klassified-tabulated-anime.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/klassified-tabulated-anime.gif
--------------------------------------------------------------------------------
/media/major-modes-tabulated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/major-modes-tabulated.png
--------------------------------------------------------------------------------
/media/media-src/animals-tabulated-01.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/animals-tabulated-01.gif
--------------------------------------------------------------------------------
/media/media-src/animals-tabulated-02.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/animals-tabulated-02.gif
--------------------------------------------------------------------------------
/media/media-src/animals-tabulated-03.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/animals-tabulated-03.gif
--------------------------------------------------------------------------------
/media/media-src/animals-tabulated-04.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/animals-tabulated-04.gif
--------------------------------------------------------------------------------
/media/media-src/animals-tabulated-05.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/animals-tabulated-05.gif
--------------------------------------------------------------------------------
/media/media-src/animals-tree-01.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/animals-tree-01.gif
--------------------------------------------------------------------------------
/media/media-src/animals-tree-02.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/animals-tree-02.gif
--------------------------------------------------------------------------------
/media/media-src/animals-tree-03.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/animals-tree-03.gif
--------------------------------------------------------------------------------
/media/media-src/animals-tree-04.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/animals-tree-04.gif
--------------------------------------------------------------------------------
/media/media-src/animals-tree-05.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/animals-tree-05.gif
--------------------------------------------------------------------------------
/media/media-src/animals-tree-06.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/animals-tree-06.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-01.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-01.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-02.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-02.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-03.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-03.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-04.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-04.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-05.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-05.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-06.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-06.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-07.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-07.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-08.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-08.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-09.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-09.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-10-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-10-1.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-10-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-10-2.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-11-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-11-1.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-11-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-11-2.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-11-3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-11-3.gif
--------------------------------------------------------------------------------
/media/media-src/files-tabulated-11-4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tabulated-11-4.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-01.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-01.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-02.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-02.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-03.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-03.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-04.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-04.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-05.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-05.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-06.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-06.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-07.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-07.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-08.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-08.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-09.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-09.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-10.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-10.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-11.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-12.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-13.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-13.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-14.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-14.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-15.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-15.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-16.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-16.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-17-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-17-1.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-17-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-17-2.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-17-3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-17-3.gif
--------------------------------------------------------------------------------
/media/media-src/files-tree-17-4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/files-tree-17-4.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-00-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-00-1.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-00-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-00-2.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-01-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-01-1.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-01-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-01-2.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-02.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-02.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-03.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-03.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-04.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-04.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-05.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-05.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-06.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-06.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-07.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-07.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-08.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-08.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-09.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-09.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-10.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-10.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-11.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-12.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-13.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-13.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-14.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-14.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-15.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-15.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-16.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-16.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-17.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-17.gif
--------------------------------------------------------------------------------
/media/media-src/json-tree-18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/json-tree-18.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-01.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-01.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-02.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-02.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-03.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-03.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-04.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-04.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-05.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-05.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-06.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-06.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-07.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-07.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-08.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-08.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-09.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-09.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-10.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-10.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-11.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-12.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-13.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-13.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-15.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-15.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-16.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-16.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-17.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-17.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-18.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-19.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-19.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-20.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-20.gif
--------------------------------------------------------------------------------
/media/media-src/klassified-tabulated-21.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DamienCassou/hierarchy/d8373b376642e9297595468dbe72ec63a6e4b571/media/media-src/klassified-tabulated-21.gif
--------------------------------------------------------------------------------
/test/hierarchy-test.el:
--------------------------------------------------------------------------------
1 | ;;; hierarchy-test.el --- Tests for hierarchy.el
2 |
3 | ;; Copyright (C) 2013 Damien Cassou
4 |
5 | ;; Author: Damien Cassou
6 | ;; Maintainer: Damien Cassou
7 |
8 | ;; This file is not part of GNU Emacs.
9 |
10 | ;; This program is free software: you can redistribute it and/or modify
11 | ;; it under the terms of the GNU General Public License as published by
12 | ;; the Free Software Foundation, either version 3 of the License, or
13 | ;; (at your option) any later version.
14 |
15 | ;; This program is distributed in the hope that it will be useful,
16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | ;; GNU General Public License for more details.
19 |
20 | ;; You should have received a copy of the GNU General Public License
21 | ;; along with this program. If not, see .
22 |
23 | ;;; Commentary:
24 |
25 | ;; Tests for hierarchy.el
26 |
27 | ;;; Code:
28 |
29 | (require 'ert)
30 | (require 'hierarchy)
31 |
32 | (declare-function test-helper-animals "test-helper")
33 |
34 | (ert-deftest hierarchy-add-one-root ()
35 | (let ((parentfn (lambda (_) nil))
36 | (hierarchy (hierarchy-new)))
37 | (hierarchy-add-tree hierarchy 'animal parentfn)
38 | (should (equal (hierarchy-roots hierarchy) '(animal)))))
39 |
40 | (ert-deftest hierarchy-add-one-item-with-parent ()
41 | (let ((parentfn (lambda (item)
42 | (cl-case item
43 | (bird 'animal))))
44 | (hierarchy (hierarchy-new)))
45 | (hierarchy-add-tree hierarchy 'bird parentfn)
46 | (should (equal (hierarchy-roots hierarchy) '(animal)))
47 | (should (equal (hierarchy-children hierarchy 'animal) '(bird)))))
48 |
49 | (ert-deftest hierarchy-add-one-item-with-parent-and-grand-parent ()
50 | (let ((parentfn (lambda (item)
51 | (cl-case item
52 | (dove 'bird)
53 | (bird 'animal))))
54 | (hierarchy (hierarchy-new)))
55 | (hierarchy-add-tree hierarchy 'dove parentfn)
56 | (should (equal (hierarchy-roots hierarchy) '(animal)))
57 | (should (equal (hierarchy-children hierarchy 'animal) '(bird)))
58 | (should (equal (hierarchy-children hierarchy 'bird) '(dove)))))
59 |
60 | (ert-deftest hierarchy-add-same-root-twice ()
61 | (let ((parentfn (lambda (_) nil))
62 | (hierarchy (hierarchy-new)))
63 | (hierarchy-add-tree hierarchy 'animal parentfn)
64 | (hierarchy-add-tree hierarchy 'animal parentfn)
65 | (should (equal (hierarchy-roots hierarchy) '(animal)))))
66 |
67 | (ert-deftest hierarchy-add-same-child-twice ()
68 | (let ((parentfn (lambda (item)
69 | (cl-case item
70 | (bird 'animal))))
71 | (hierarchy (hierarchy-new)))
72 | (hierarchy-add-tree hierarchy 'bird parentfn)
73 | (hierarchy-add-tree hierarchy 'bird parentfn)
74 | (should (equal (hierarchy-roots hierarchy) '(animal)))
75 | (should (equal (hierarchy-children hierarchy 'animal) '(bird)))))
76 |
77 | (ert-deftest hierarchy-add-item-and-its-parent ()
78 | (let ((parentfn (lambda (item)
79 | (cl-case item
80 | (bird 'animal))))
81 | (hierarchy (hierarchy-new)))
82 | (hierarchy-add-tree hierarchy 'bird parentfn)
83 | (hierarchy-add-tree hierarchy 'animal parentfn)
84 | (should (equal (hierarchy-roots hierarchy) '(animal)))
85 | (should (equal (hierarchy-children hierarchy 'animal) '(bird)))))
86 |
87 | (ert-deftest hierarchy-add-item-and-its-child ()
88 | (let ((parentfn (lambda (item)
89 | (cl-case item
90 | (bird 'animal))))
91 | (hierarchy (hierarchy-new)))
92 | (hierarchy-add-tree hierarchy 'animal parentfn)
93 | (hierarchy-add-tree hierarchy 'bird parentfn)
94 | (should (equal (hierarchy-roots hierarchy) '(animal)))
95 | (should (equal (hierarchy-children hierarchy 'animal) '(bird)))))
96 |
97 | (ert-deftest hierarchy-add-two-items-sharing-parent ()
98 | (let ((parentfn (lambda (item)
99 | (cl-case item
100 | (dove 'bird)
101 | (pigeon 'bird))))
102 | (hierarchy (hierarchy-new)))
103 | (hierarchy-add-tree hierarchy 'dove parentfn)
104 | (hierarchy-add-tree hierarchy 'pigeon parentfn)
105 | (should (equal (hierarchy-roots hierarchy) '(bird)))
106 | (should (equal (hierarchy-children hierarchy 'bird) '(dove pigeon)))))
107 |
108 | (ert-deftest hierarchy-add-two-hierarchies ()
109 | (let ((parentfn (lambda (item)
110 | (cl-case item
111 | (dove 'bird)
112 | (circle 'shape))))
113 | (hierarchy (hierarchy-new)))
114 | (hierarchy-add-tree hierarchy 'dove parentfn)
115 | (hierarchy-add-tree hierarchy 'circle parentfn)
116 | (should (equal (hierarchy-roots hierarchy) '(bird shape)))
117 | (should (equal (hierarchy-children hierarchy 'bird) '(dove)))
118 | (should (equal (hierarchy-children hierarchy 'shape) '(circle)))))
119 |
120 | (ert-deftest hierarchy-add-with-childrenfn ()
121 | (let ((childrenfn (lambda (item)
122 | (cl-case item
123 | (animal '(bird))
124 | (bird '(dove pigeon)))))
125 | (hierarchy (hierarchy-new)))
126 | (hierarchy-add-tree hierarchy 'animal nil childrenfn)
127 | (should (equal (hierarchy-roots hierarchy) '(animal)))
128 | (should (equal (hierarchy-children hierarchy 'animal) '(bird)))
129 | (should (equal (hierarchy-children hierarchy 'bird) '(dove pigeon)))))
130 |
131 | (ert-deftest hierarchy-add-with-parentfn-and-childrenfn ()
132 | (let ((parentfn (lambda (item)
133 | (cl-case item
134 | (bird 'animal)
135 | (animal 'life-form))))
136 | (childrenfn (lambda (item)
137 | (cl-case item
138 | (bird '(dove pigeon))
139 | (pigeon '(ashy-wood-pigeon)))))
140 | (hierarchy (hierarchy-new)))
141 | (hierarchy-add-tree hierarchy 'bird parentfn childrenfn)
142 | (should (equal (hierarchy-roots hierarchy) '(life-form)))
143 | (should (equal (hierarchy-children hierarchy 'life-form) '(animal)))
144 | (should (equal (hierarchy-children hierarchy 'animal) '(bird)))
145 | (should (equal (hierarchy-children hierarchy 'bird) '(dove pigeon)))
146 | (should (equal (hierarchy-children hierarchy 'pigeon) '(ashy-wood-pigeon)))))
147 |
148 | (ert-deftest hierarchy-add-twice-with-parentfn-and-childrenfn ()
149 | (let* ((parentfn (lambda (item)
150 | (cl-case item
151 | (dove 'bird)
152 | (bird 'animal))))
153 | (childrenfn (lambda (item)
154 | (cl-case item
155 | (animal '(bird))
156 | (bird '(dove)))))
157 | (hierarchy (hierarchy-new)))
158 | (hierarchy-add-tree hierarchy 'bird parentfn childrenfn)
159 | (should (equal (hierarchy-children hierarchy 'animal) '(bird)))
160 | (should (equal (hierarchy-children hierarchy 'bird) '(dove)))))
161 |
162 | (ert-deftest hierarchy-add-trees ()
163 | (let ((parentfn (lambda (item)
164 | (cl-case item
165 | (dove 'bird)
166 | (pigeon 'bird)
167 | (bird 'animal))))
168 | (hierarchy (hierarchy-new)))
169 | (hierarchy-add-trees hierarchy '(dove pigeon) parentfn)
170 | (should (equal (hierarchy-roots hierarchy) '(animal)))
171 | (should (equal (hierarchy-children hierarchy 'animal) '(bird)))
172 | (should (equal (hierarchy-children hierarchy 'bird) '(dove pigeon)))))
173 |
174 | (ert-deftest hierarchy-from-list ()
175 | (let ((hierarchy (hierarchy-from-list
176 | '(animal (bird (dove)
177 | (pigeon))
178 | (cow)
179 | (dolphin)))))
180 | (hierarchy-sort hierarchy (lambda (item1 item2)
181 | (string< (car item1)
182 | (car item2))))
183 | (should (equal (hierarchy-to-string hierarchy (lambda (item) (symbol-name (car item))))
184 | "animal\n bird\n dove\n pigeon\n cow\n dolphin\n"))))
185 |
186 | (ert-deftest hierarchy-from-list-with-duplicates ()
187 | (let ((hierarchy (hierarchy-from-list
188 | '(a (b) (b))
189 | t)))
190 | (hierarchy-sort hierarchy (lambda (item1 item2)
191 | ;; sort by ID
192 | (< (car item1) (car item2))))
193 | (should (equal (hierarchy-length hierarchy) 3))
194 | (should (equal (hierarchy-to-string
195 | hierarchy
196 | (lambda (item)
197 | (format "%s(%s)"
198 | (cadr item)
199 | (car item))))
200 | "a(1)\n b(2)\n b(3)\n"))))
201 |
202 | (ert-deftest hierarchy-from-list-with-childrenfn ()
203 | (let ((hierarchy (hierarchy-from-list
204 | "abc"
205 | nil
206 | (lambda (item)
207 | (when (string= item "abc")
208 | (split-string item "" t))))))
209 | (hierarchy-sort hierarchy (lambda (item1 item2) (string< item1 item2)))
210 | (should (equal (hierarchy-length hierarchy) 4))
211 | (should (equal (hierarchy-to-string hierarchy)
212 | "abc\n a\n b\n c\n"))))
213 |
214 | (ert-deftest hierarchy-add-relation-check-error-when-different-parent ()
215 | (let ((parentfn (lambda (item)
216 | (cl-case item
217 | (bird 'animal))))
218 | (hierarchy (hierarchy-new)))
219 | (hierarchy-add-tree hierarchy 'bird parentfn)
220 | (should-error
221 | (hierarchy--add-relation hierarchy 'bird 'cow #'identity))))
222 |
223 | (ert-deftest hierarchy-empty-p-return-non-nil-for-empty ()
224 | (should (hierarchy-empty-p (hierarchy-new))))
225 |
226 | (ert-deftest hierarchy-empty-p-return-nil-for-non-empty ()
227 | (should-not (hierarchy-empty-p (test-helper-animals))))
228 |
229 | (ert-deftest hierarchy-length-of-empty-is-0 ()
230 | (should (equal (hierarchy-length (hierarchy-new)) 0)))
231 |
232 | (ert-deftest hierarchy-length-of-non-empty-counts-items ()
233 | (let ((parentfn (lambda (item)
234 | (cl-case item
235 | (bird 'animal)
236 | (dove 'bird)
237 | (pigeon 'bird))))
238 | (hierarchy (hierarchy-new)))
239 | (hierarchy-add-tree hierarchy 'dove parentfn)
240 | (hierarchy-add-tree hierarchy 'pigeon parentfn)
241 | (should (equal (hierarchy-length hierarchy) 4))))
242 |
243 | (ert-deftest hierarchy-has-root ()
244 | (let ((parentfn (lambda (item)
245 | (cl-case item
246 | (bird 'animal)
247 | (dove 'bird)
248 | (pigeon 'bird))))
249 | (hierarchy (hierarchy-new)))
250 | (should-not (hierarchy-has-root hierarchy 'animal))
251 | (should-not (hierarchy-has-root hierarchy 'bird))
252 | (hierarchy-add-tree hierarchy 'dove parentfn)
253 | (hierarchy-add-tree hierarchy 'pigeon parentfn)
254 | (should (hierarchy-has-root hierarchy 'animal))
255 | (should-not (hierarchy-has-root hierarchy 'bird))))
256 |
257 | (ert-deftest hierarchy-leafs ()
258 | (let ((animals (test-helper-animals)))
259 | (should (equal (hierarchy-leafs animals)
260 | '(dove pigeon dolphin cow)))))
261 |
262 | (ert-deftest hierarchy-leafs-includes-lonely-roots ()
263 | (let ((parentfn (lambda (item) nil))
264 | (hierarchy (hierarchy-new)))
265 | (hierarchy-add-tree hierarchy 'foo parentfn)
266 | (should (equal (hierarchy-leafs hierarchy)
267 | '(foo)))))
268 |
269 | (ert-deftest hierarchy-leafs-of-node ()
270 | (let ((animals (test-helper-animals)))
271 | (should (equal (hierarchy-leafs animals 'cow) '()))
272 | (should (equal (hierarchy-leafs animals 'animal) '(dove pigeon dolphin cow)))
273 | (should (equal (hierarchy-leafs animals 'bird) '(dove pigeon)))
274 | (should (equal (hierarchy-leafs animals 'dove) '()))))
275 |
276 | (ert-deftest hierarchy-child-p ()
277 | (let ((animals (test-helper-animals)))
278 | (should (hierarchy-child-p animals 'dove 'bird))
279 | (should (hierarchy-child-p animals 'bird 'animal))
280 | (should (hierarchy-child-p animals 'cow 'animal))
281 | (should-not (hierarchy-child-p animals 'cow 'bird))
282 | (should-not (hierarchy-child-p animals 'bird 'cow))
283 | (should-not (hierarchy-child-p animals 'animal 'dove))
284 | (should-not (hierarchy-child-p animals 'animal 'bird))))
285 |
286 | (ert-deftest hierarchy-descendant ()
287 | (let ((animals (test-helper-animals)))
288 | (should (hierarchy-descendant-p animals 'dove 'animal))
289 | (should (hierarchy-descendant-p animals 'dove 'bird))
290 | (should (hierarchy-descendant-p animals 'bird 'animal))
291 | (should (hierarchy-descendant-p animals 'cow 'animal))
292 | (should-not (hierarchy-descendant-p animals 'cow 'bird))
293 | (should-not (hierarchy-descendant-p animals 'bird 'cow))
294 | (should-not (hierarchy-descendant-p animals 'animal 'dove))
295 | (should-not (hierarchy-descendant-p animals 'animal 'bird))))
296 |
297 | (ert-deftest hierarchy-descendant-if-not-same ()
298 | (let ((animals (test-helper-animals)))
299 | (should-not (hierarchy-descendant-p animals 'cow 'cow))
300 | (should-not (hierarchy-descendant-p animals 'dove 'dove))
301 | (should-not (hierarchy-descendant-p animals 'bird 'bird))
302 | (should-not (hierarchy-descendant-p animals 'animal 'animal))))
303 |
304 | ;; keywords supported: :test :key
305 | (ert-deftest hierarchy--set-equal ()
306 | (should (hierarchy--set-equal '(1 2 3) '(1 2 3)))
307 | (should (hierarchy--set-equal '(1 2 3) '(3 2 1)))
308 | (should (hierarchy--set-equal '(3 2 1) '(1 2 3)))
309 | (should-not (hierarchy--set-equal '(2 3) '(3 2 1)))
310 | (should-not (hierarchy--set-equal '(1 2 3) '(2 3)))
311 | (should-not (hierarchy--set-equal '("1" "2") '("2" "1") :test #'eq))
312 | (should (hierarchy--set-equal '("1" "2") '("2" "1") :test #'equal))
313 | (should-not (hierarchy--set-equal '(1 2) '(-1 -2)))
314 | (should (hierarchy--set-equal '(1 2) '(-1 -2) :key #'abs))
315 | (should-not (hierarchy--set-equal '(("1" 1) ("2" 1)) '(("1" 2) ("2" 2))))
316 | (should-not (hierarchy--set-equal '(("1" 1) ("2" 1)) '(("1" 2) ("2" 2)) :key #'car))
317 | (should-not (hierarchy--set-equal '(("1" 1) ("2" 1)) '(("1" 2) ("2" 2)) :test #'equal))
318 | (should (hierarchy--set-equal '(("1" 1) ("2" 1)) '(("1" 2) ("2" 2)) :key #'car :test #'equal)))
319 |
320 | (ert-deftest hierarchy-equal-returns-true-for-same-hierarchy ()
321 | (let ((animals (test-helper-animals)))
322 | (should (hierarchy-equal animals animals))
323 | (should (hierarchy-equal (test-helper-animals) animals))))
324 |
325 | (ert-deftest hierarchy-equal-returns-true-for-hierarchy-copies ()
326 | (let ((animals (test-helper-animals)))
327 | (should (hierarchy-equal animals (hierarchy-copy animals)))))
328 |
329 | (ert-deftest hierarchy-map-item-on-leaf ()
330 | (let* ((animals (test-helper-animals))
331 | (result (hierarchy-map-item (lambda (item indent) (cons item indent))
332 | 'cow
333 | animals)))
334 | (should (equal result '((cow . 0))))))
335 |
336 | (ert-deftest hierarchy-map-item-on-leaf-with-indent ()
337 | (let* ((animals (test-helper-animals))
338 | (result (hierarchy-map-item (lambda (item indent) (cons item indent))
339 | 'cow
340 | animals
341 | 2)))
342 | (should (equal result '((cow . 2))))))
343 |
344 | (ert-deftest hierarchy-map-item-on-parent ()
345 | (let* ((animals (test-helper-animals))
346 | (result (hierarchy-map-item (lambda (item indent) (cons item indent))
347 | 'bird
348 | animals)))
349 | (should (equal result '((bird . 0) (dove . 1) (pigeon . 1))))))
350 |
351 | (ert-deftest hierarchy-map-item-on-grand-parent ()
352 | (let* ((animals (test-helper-animals))
353 | (result (hierarchy-map-item (lambda (item indent) (cons item indent))
354 | 'animal
355 | animals)))
356 | (should (equal result '((animal . 0) (bird . 1) (dove . 2) (pigeon . 2)
357 | (cow . 1) (dolphin . 1))))))
358 |
359 | (ert-deftest hierarchy-map-conses ()
360 | (let* ((animals (test-helper-animals))
361 | (result (hierarchy-map (lambda (item indent)
362 | (cons item indent))
363 | animals)))
364 | (should (equal result '((animal . 0)
365 | (bird . 1)
366 | (dove . 2)
367 | (pigeon . 2)
368 | (cow . 1)
369 | (dolphin . 1))))))
370 |
371 | (ert-deftest hierarchy-map-tree ()
372 | (let ((animals (test-helper-animals)))
373 | (should (equal (hierarchy-map-tree (lambda (item indent children)
374 | (list item indent children))
375 | animals)
376 | '(animal
377 | 0
378 | ((bird 1 ((dove 2 nil) (pigeon 2 nil)))
379 | (cow 1 nil)
380 | (dolphin 1 nil)))))))
381 |
382 | (ert-deftest hierarchy-map-hierarchy-keeps-hierarchy ()
383 | (let* ((animals (test-helper-animals))
384 | (result (hierarchy-map-hierarchy (lambda (item _) (identity item))
385 | animals)))
386 | (should (hierarchy-equal animals result))))
387 |
388 | (ert-deftest hierarchy-map-applies-function ()
389 | (let* ((animals (test-helper-animals))
390 | (parentfn (lambda (item)
391 | (cond
392 | ((equal item "bird") "animal")
393 | ((equal item "dove") "bird")
394 | ((equal item "pigeon") "bird")
395 | ((equal item "cow") "animal")
396 | ((equal item "dolphin") "animal"))))
397 | (expected (hierarchy-new)))
398 | (hierarchy-add-tree expected "dove" parentfn)
399 | (hierarchy-add-tree expected "pigeon" parentfn)
400 | (hierarchy-add-tree expected "cow" parentfn)
401 | (hierarchy-add-tree expected "dolphin" parentfn)
402 | (should (hierarchy-equal
403 | (hierarchy-map-hierarchy (lambda (item _) (symbol-name item)) animals)
404 | expected))))
405 |
406 | (ert-deftest hierarchy-extract-tree ()
407 | (let* ((animals (test-helper-animals))
408 | (birds (hierarchy-extract-tree animals 'bird)))
409 | (hierarchy-sort birds)
410 | (should (equal (hierarchy-roots birds) '(animal)))
411 | (should (equal (hierarchy-children birds 'animal) '(bird)))
412 | (should (equal (hierarchy-children birds 'bird) '(dove pigeon)))))
413 |
414 | (ert-deftest hierarchy-extract-tree-nil-if-not-in-hierarchy ()
415 | (let* ((animals (test-helper-animals)))
416 | (should-not (hierarchy-extract-tree animals 'foobar))))
417 |
418 | (ert-deftest hierarchy-items-of-empty-hierarchy-is-empty ()
419 | (should (seq-empty-p (hierarchy-items (hierarchy-new)))))
420 |
421 | (ert-deftest hierarchy-items-returns-sequence-of-same-length ()
422 | (let* ((animals (test-helper-animals))
423 | (result (hierarchy-items animals)))
424 | (should (= (seq-length result) (hierarchy-length animals)))))
425 |
426 | (ert-deftest hierarchy-items-return-all-elements-of-hierarchy ()
427 | (let* ((animals (test-helper-animals))
428 | (result (hierarchy-items animals)))
429 | (should (equal (seq-sort #'string< result) '(animal bird cow dolphin dove pigeon)))))
430 |
431 | (ert-deftest hierarchy-labelfn-indent-no-indent-if-0 ()
432 | (let* ((labelfn-base (lambda (_item _indent) (insert "foo")))
433 | (labelfn (hierarchy-labelfn-indent labelfn-base)))
434 | (should (equal
435 | (with-temp-buffer
436 | (funcall labelfn "bar" 0)
437 | (buffer-substring (point-min) (point-max)))
438 | "foo"))))
439 |
440 | (ert-deftest hierarchy-labelfn-indent-three-times-if-3 ()
441 | (let* ((labelfn-base (lambda (_item _indent) (insert "foo")))
442 | (labelfn (hierarchy-labelfn-indent labelfn-base)))
443 | (should (equal
444 | (with-temp-buffer
445 | (funcall labelfn "bar" 3)
446 | (buffer-substring (point-min) (point-max)))
447 | " foo"))))
448 |
449 | (ert-deftest hierarchy-labelfn-indent-default-indent-string ()
450 | (let* ((labelfn-base (lambda (_item _indent) (insert "foo")))
451 | (labelfn (hierarchy-labelfn-indent labelfn-base)))
452 | (should (equal
453 | (with-temp-buffer
454 | (funcall labelfn "bar" 1)
455 | (buffer-substring (point-min) (point-max)))
456 | " foo"))))
457 |
458 | (ert-deftest hierarchy-labelfn-indent-custom-indent-string ()
459 | (let* ((labelfn-base (lambda (_item _indent) (insert "foo")))
460 | (labelfn (hierarchy-labelfn-indent labelfn-base "###"))
461 | (content (with-temp-buffer
462 | (funcall labelfn "bar" 1)
463 | (buffer-substring (point-min) (point-max)))))
464 | (should (equal content "###foo"))))
465 |
466 | (ert-deftest hierarchy-labelfn-button-propertize ()
467 | (let* ((labelfn-base (lambda (_item _indent) (insert "foo")))
468 | (actionfn #'identity)
469 | (labelfn (hierarchy-labelfn-button labelfn-base actionfn))
470 | (properties (with-temp-buffer
471 | (funcall labelfn "bar" 1)
472 | (text-properties-at 1))))
473 | (should (equal (car properties) 'action))))
474 |
475 | (ert-deftest hierarchy-labelfn-button-execute-labelfn ()
476 | (let* ((labelfn-base (lambda (_item _indent) (insert "foo")))
477 | (actionfn #'identity)
478 | (labelfn (hierarchy-labelfn-button labelfn-base actionfn))
479 | (content (with-temp-buffer
480 | (funcall labelfn "bar" 1)
481 | (buffer-substring-no-properties (point-min) (point-max)))))
482 | (should (equal content "foo"))))
483 |
484 | (ert-deftest hierarchy-labelfn-button-if-does-not-button-unless-condition ()
485 | (let ((labelfn-base (lambda (_item _indent) (insert "foo")))
486 | (spy-count 0)
487 | (condition (lambda (_item _indent) nil)))
488 | (cl-letf (((symbol-function 'hierarchy-labelfn-button) (lambda (_labelfn _actionfn) (lambda (_item _indent) (cl-incf spy-count)))))
489 | (funcall (hierarchy-labelfn-button-if labelfn-base condition #'identity) nil nil)
490 | (should (equal spy-count 0)))))
491 |
492 | (ert-deftest hierarchy-labelfn-button-if-does-button-when-condition ()
493 | (let ((labelfn-base (lambda (_item _indent) (insert "foo")))
494 | (spy-count 0)
495 | (condition (lambda (_item _indent) t)))
496 | (cl-letf (((symbol-function 'hierarchy-labelfn-button) (lambda (_labelfn _actionfn) (lambda (_item _indent) (cl-incf spy-count)))))
497 | (funcall (hierarchy-labelfn-button-if labelfn-base condition #'identity) nil nil)
498 | (should (equal spy-count 1)))))
499 |
500 | (ert-deftest hierarchy-labelfn-to-string ()
501 | (let ((labelfn (lambda (item _indent) (insert item))))
502 | (should (equal (hierarchy-labelfn-to-string labelfn "foo" 1) "foo"))))
503 |
504 | (ert-deftest hierarchy-print ()
505 | (let* ((animals (test-helper-animals))
506 | (result (with-temp-buffer
507 | (hierarchy-print animals)
508 | (buffer-substring-no-properties (point-min) (point-max)))))
509 | (should (equal result "animal\n bird\n dove\n pigeon\n cow\n dolphin\n"))))
510 |
511 | (ert-deftest hierarchy-to-string ()
512 | (let* ((animals (test-helper-animals))
513 | (result (hierarchy-to-string animals)))
514 | (should (equal result "animal\n bird\n dove\n pigeon\n cow\n dolphin\n"))))
515 |
516 | (ert-deftest hierarchy-tabulated-display ()
517 | (let* ((animals (test-helper-animals))
518 | (labelfn (lambda (item _indent) (insert (symbol-name item))))
519 | (contents (with-temp-buffer
520 | (hierarchy-tabulated-display animals labelfn (current-buffer))
521 | (buffer-substring-no-properties (point-min) (point-max)))))
522 | (should (equal contents "animal\nbird\ndove\npigeon\ncow\ndolphin\n"))))
523 |
524 | (ert-deftest hierarchy-sort-non-root-nodes ()
525 | (let* ((animals (test-helper-animals)))
526 | (should (equal (hierarchy-roots animals) '(animal)))
527 | (should (equal (hierarchy-children animals 'animal) '(bird cow dolphin)))
528 | (should (equal (hierarchy-children animals 'bird) '(dove pigeon)))))
529 |
530 | (ert-deftest hierarchy-sort-roots ()
531 | (let* ((organisms (hierarchy-new))
532 | (parentfn (lambda (item)
533 | (cl-case item
534 | (oak 'plant)
535 | (bird 'animal)))))
536 | (hierarchy-add-tree organisms 'oak parentfn)
537 | (hierarchy-add-tree organisms 'bird parentfn)
538 | (hierarchy-sort organisms)
539 | (should (equal (hierarchy-roots organisms) '(animal plant)))))
540 |
541 | (provide 'hierarchy-test)
542 | ;;; hierarchy-test.el ends here
543 |
--------------------------------------------------------------------------------
/test/test-helper.el:
--------------------------------------------------------------------------------
1 | ;;; test-helper.el --- Helper functions to test hierarchies -*- lexical-binding: t; -*-
2 |
3 | ;; Copyright (C) 2017 Damien Cassou
4 |
5 | ;; Author: Damien Cassou
6 |
7 | ;; This program is free software; you can redistribute it and/or modify
8 | ;; it under the terms of the GNU General Public License as published by
9 | ;; the Free Software Foundation, either version 3 of the License, or
10 | ;; (at your option) any later version.
11 |
12 | ;; This program is distributed in the hope that it will be useful,
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | ;; GNU General Public License for more details.
16 |
17 | ;; You should have received a copy of the GNU General Public License
18 | ;; along with this program. If not, see .
19 |
20 | ;;; Commentary:
21 |
22 | ;;
23 |
24 | ;;; Code:
25 |
26 | (declare-function undercover "undercover")
27 |
28 | (when (require 'undercover nil t)
29 | (undercover "hierarchy.el"))
30 |
31 | (require 'hierarchy)
32 |
33 | (defun test-helper-animals ()
34 | "Create a sorted animal hierarchy."
35 | (let ((parentfn (lambda (item) (cl-case item
36 | (dove 'bird)
37 | (pigeon 'bird)
38 | (bird 'animal)
39 | (dolphin 'animal)
40 | (cow 'animal))))
41 | (hierarchy (hierarchy-new)))
42 | (hierarchy-add-tree hierarchy 'dove parentfn)
43 | (hierarchy-add-tree hierarchy 'pigeon parentfn)
44 | (hierarchy-add-tree hierarchy 'dolphin parentfn)
45 | (hierarchy-add-tree hierarchy 'cow parentfn)
46 | (hierarchy-sort hierarchy)
47 | hierarchy))
48 |
49 | ;;; test-helper.el ends here
50 |
--------------------------------------------------------------------------------