Switch to recommended form of GPLv3 permissions notice.
[bpt/emacs.git] / lisp / emacs-lisp / ewoc.el
CommitLineData
e8af40ee 1;;; ewoc.el --- utility to maintain a view of a list of objects in a buffer
5b467bf4 2
3731a850 3;; Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
8b72699e 4;; 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5b467bf4
SM
5
6;; Author: Per Cederqvist <ceder@lysator.liu.se>
7;; Inge Wallin <inge@lysator.liu.se>
8;; Maintainer: monnier@gnu.org
9;; Created: 3 Aug 1992
10;; Keywords: extensions, lisp
11
12;; This file is part of GNU Emacs.
13
d6cba7ae 14;; GNU Emacs is free software: you can redistribute it and/or modify
5b467bf4 15;; it under the terms of the GNU General Public License as published by
d6cba7ae
GM
16;; the Free Software Foundation, either version 3 of the License, or
17;; (at your option) any later version.
5b467bf4
SM
18
19;; GNU Emacs 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
d6cba7ae 25;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
5b467bf4
SM
26
27;;; Commentary:
28
29;; Ewoc Was Once Cookie
30;; But now it's Emacs' Widget for Object Collections
31
32;; As the name implies this derives from the `cookie' package (part
10c471e6 33;; of Elib). The changes are pervasive though mostly superficial:
5b467bf4 34
10c471e6 35;; - uses CL (and its `defstruct')
5b467bf4
SM
36;; - separate from Elib.
37;; - uses its own version of a doubly-linked list which allows us
38;; to merge the elib-wrapper and the elib-node structures into ewoc-node
39;; - dropping functions not used by PCL-CVS (the only client of ewoc at the
40;; time of writing)
41;; - removing unused arguments
42;; - renaming:
43;; elib-node ==> ewoc--node
44;; collection ==> ewoc
45;; tin ==> ewoc--node
46;; cookie ==> data or element or elem
47
48;; Introduction
49;; ============
50;;
51;; Ewoc is a package that implements a connection between an
52;; dll (a doubly linked list) and the contents of a buffer.
53;; Possible uses are dired (have all files in a list, and show them),
54;; buffer-list, kom-prioritize (in the LysKOM elisp client) and
55;; others. pcl-cvs.el uses ewoc.el.
56;;
57;; Ewoc can be considered as the `view' part of a model-view-controller.
58;;
59;; A `element' can be any lisp object. When you use the ewoc
60;; package you specify a pretty-printer, a function that inserts
61;; a printable representation of the element in the buffer. (The
62;; pretty-printer should use "insert" and not
63;; "insert-before-markers").
64;;
65;; A `ewoc' consists of a doubly linked list of elements, a
66;; header, a footer and a pretty-printer. It is displayed at a
67;; certain point in a certain buffer. (The buffer and point are
68;; fixed when the ewoc is created). The header and the footer
69;; are constant strings. They appear before and after the elements.
5b467bf4
SM
70;;
71;; Ewoc does not affect the mode of the buffer in any way. It
72;; merely makes it easy to connect an underlying data representation
73;; to the buffer contents.
74;;
75;; A `ewoc--node' is an object that contains one element. There are
10c471e6
SM
76;; functions in this package that given an ewoc--node extract the data, or
77;; give the next or previous ewoc--node. (All ewoc--nodes are linked together
78;; in a doubly linked list. The `previous' ewoc--node is the one that appears
5b467bf4
SM
79;; before the other in the buffer.) You should not do anything with
80;; an ewoc--node except pass it to the functions in this package.
81;;
82;; An ewoc is a very dynamic thing. You can easily add or delete elements.
83;; You can apply a function to all elements in an ewoc, etc, etc.
84;;
85;; Remember that an element can be anything. Your imagination is the
86;; limit! It is even possible to have another ewoc as an
87;; element. In that way some kind of tree hierarchy can be created.
88;;
0741626e 89;; The Emacs Lisp Reference Manual documents ewoc.el's "public interface".
5b467bf4
SM
90
91;; Coding conventions
92;; ==================
93;;
94;; All functions of course start with `ewoc'. Functions and macros
95;; starting with the prefix `ewoc--' are meant for internal use,
96;; while those starting with `ewoc-' are exported for public use.
5b467bf4
SM
97
98;;; Code:
99
7ece7aba 100(eval-when-compile (require 'cl))
5b467bf4 101
bb7a346f
SM
102;; The doubly linked list is implemented as a circular list with a dummy
103;; node first and last. The dummy node is used as "the dll".
5b467bf4 104(defstruct (ewoc--node
63910b23 105 (:type vector) ;ewoc--node-nth needs this
7f934f3d 106 (:constructor nil)
5b467bf4
SM
107 (:constructor ewoc--node-create (start-marker data)))
108 left right data start-marker)
109
bb7a346f 110(defun ewoc--node-next (dll node)
5b467bf4 111 "Return the node after NODE, or nil if NODE is the last node."
7dd2e64c 112 (let ((R (ewoc--node-right node)))
bb7a346f 113 (unless (eq dll R) R)))
5b467bf4 114
bb7a346f 115(defun ewoc--node-prev (dll node)
5b467bf4 116 "Return the node before NODE, or nil if NODE is the first node."
7dd2e64c 117 (let ((L (ewoc--node-left node)))
bb7a346f 118 (unless (eq dll L) L)))
7dd2e64c 119
bb7a346f
SM
120(defun ewoc--node-nth (dll n)
121 "Return the Nth node from the doubly linked list `dll'.
7dd2e64c
TTN
122N counts from zero. If N is negative, return the -(N+1)th last element.
123If N is out of range, return nil.
bb7a346f
SM
124Thus, (ewoc--node-nth dll 0) returns the first node,
125and (ewoc--node-nth dll -1) returns the last node."
63910b23 126 ;; Presuming a node is ":type vector", starting with `left' and `right':
5b467bf4
SM
127 ;; Branch 0 ("follow left pointer") is used when n is negative.
128 ;; Branch 1 ("follow right pointer") is used otherwise.
129 (let* ((branch (if (< n 0) 0 1))
bb7a346f 130 (node (aref dll branch)))
5b467bf4 131 (if (< n 0) (setq n (- -1 n)))
bb7a346f 132 (while (and (not (eq dll node)) (> n 0))
63910b23 133 (setq node (aref node branch))
5b467bf4 134 (setq n (1- n)))
bb7a346f 135 (unless (eq dll node) node)))
5b467bf4 136
10c471e6
SM
137(defun ewoc-location (node)
138 "Return the start location of NODE."
139 (ewoc--node-start-marker node))
140
5b467bf4
SM
141\f
142;;; The ewoc data type
143
144(defstruct (ewoc
145 (:constructor nil)
f860b721 146 (:constructor ewoc--create (buffer pretty-printer dll))
5b467bf4 147 (:conc-name ewoc--))
2bbacc0e 148 buffer pretty-printer header footer dll last-node hf-pp)
5b467bf4
SM
149
150(defmacro ewoc--set-buffer-bind-dll-let* (ewoc varlist &rest forms)
151 "Execute FORMS with ewoc--buffer selected as current buffer,
bb7a346f
SM
152`dll' bound to the dll, and VARLIST bound as in a let*.
153`dll' will be bound when VARLIST is initialized, but
7dd2e64c 154the current buffer will *not* have been changed.
5b467bf4 155Return value of last form in FORMS."
b1c36c0f
TTN
156 (let ((hnd (make-symbol "ewoc")))
157 `(let* ((,hnd ,ewoc)
bb7a346f 158 (dll (ewoc--dll ,hnd))
8a946354 159 ,@varlist)
b1c36c0f
TTN
160 (with-current-buffer (ewoc--buffer ,hnd)
161 ,@forms))))
5b467bf4
SM
162
163(defmacro ewoc--set-buffer-bind-dll (ewoc &rest forms)
164 `(ewoc--set-buffer-bind-dll-let* ,ewoc nil ,@forms))
165
166(defsubst ewoc--filter-hf-nodes (ewoc node)
167 "Evaluate NODE once and return it.
168BUT if it is the header or the footer in EWOC return nil instead."
169 (unless (or (eq node (ewoc--header ewoc))
170 (eq node (ewoc--footer ewoc)))
171 node))
172
bb7a346f 173(defun ewoc--adjust (beg end node dll)
60eae434
TTN
174 ;; "Manually reseat" markers for NODE and its successors (including footer
175 ;; and dll), in the case where they originally shared start position with
176 ;; BEG, to END. BEG and END are buffer positions describing NODE's left
177 ;; neighbor. This operation is functionally equivalent to temporarily
178 ;; setting these nodes' markers' insertion type to t around the pretty-print
7dd2e64c 179 ;; call that precedes the call to `ewoc--adjust', and then changing them back
60eae434
TTN
180 ;; to nil.
181 (when (< beg end)
182 (let (m)
183 (while (and (= beg (setq m (ewoc--node-start-marker node)))
bb7a346f
SM
184 ;; The "dummy" node `dll' actually holds the marker that
185 ;; points to the end of the footer, so we check `dll'
186 ;; *after* reseating the marker.
60eae434
TTN
187 (progn
188 (set-marker m end)
bb7a346f 189 (not (eq dll node))))
60eae434
TTN
190 (setq node (ewoc--node-right node))))))
191
f718c2fc 192(defun ewoc--insert-new-node (node data pretty-printer dll)
7f0ea399 193 "Insert before NODE a new node for DATA, displayed by PRETTY-PRINTER.
f718c2fc 194Fourth arg DLL -- from `(ewoc--dll EWOC)' -- is for internal purposes.
7f0ea399
TTN
195Call PRETTY-PRINTER with point at NODE's start, thus pushing back
196NODE and leaving the new node's start there. Return the new node."
5b467bf4 197 (save-excursion
7ece7aba
SM
198 (let ((elemnode (ewoc--node-create
199 (copy-marker (ewoc--node-start-marker node)) data)))
200 (setf (ewoc--node-left elemnode) (ewoc--node-left node)
7f0ea399
TTN
201 (ewoc--node-right elemnode) node
202 (ewoc--node-right (ewoc--node-left node)) elemnode
203 (ewoc--node-left node) elemnode)
bb7a346f 204 (ewoc--refresh-node pretty-printer elemnode dll)
7f0ea399 205 elemnode)))
5b467bf4 206
bb7a346f 207(defun ewoc--refresh-node (pp node dll)
cb3430a1 208 "Redisplay the element represented by NODE using the pretty-printer PP."
60eae434
TTN
209 (let ((inhibit-read-only t)
210 (m (ewoc--node-start-marker node))
211 (R (ewoc--node-right node)))
bfbdb5ca 212 ;; First, remove the string from the buffer:
60eae434 213 (delete-region m (ewoc--node-start-marker R))
bfbdb5ca 214 ;; Calculate and insert the string.
60eae434
TTN
215 (goto-char m)
216 (funcall pp (ewoc--node-data node))
bb7a346f 217 (ewoc--adjust m (point) R dll)))
2bbacc0e
TTN
218
219(defun ewoc--wrap (func)
220 (lexical-let ((ewoc--user-pp func))
221 (lambda (data)
222 (funcall ewoc--user-pp data)
223 (insert "\n"))))
224
5b467bf4
SM
225\f
226;;; ===========================================================================
227;;; Public members of the Ewoc package
228
3fe35897 229;;;###autoload
2bbacc0e 230(defun ewoc-create (pretty-printer &optional header footer nosep)
5b467bf4
SM
231 "Create an empty ewoc.
232
cb3430a1 233The ewoc will be inserted in the current buffer at the current position.
5b467bf4
SM
234
235PRETTY-PRINTER should be a function that takes one argument, an
236element, and inserts a string representing it in the buffer (at
0111ab41 237point). The string PRETTY-PRINTER inserts may be empty or span
60eae434 238several lines. The PRETTY-PRINTER should use `insert', and not
0111ab41
JB
239`insert-before-markers'.
240
60eae434
TTN
241Optional second and third arguments HEADER and FOOTER are strings,
242possibly empty, that will always be present at the top and bottom,
2bbacc0e
TTN
243respectively, of the ewoc.
244
245Normally, a newline is automatically inserted after the header,
246the footer and every node's printed representation. Optional
247fourth arg NOSEP non-nil inhibits this."
2c246c9f
TTN
248 (let* ((dummy-node (ewoc--node-create 'DL-LIST 'DL-LIST))
249 (dll (progn (setf (ewoc--node-right dummy-node) dummy-node)
250 (setf (ewoc--node-left dummy-node) dummy-node)
251 dummy-node))
2bbacc0e 252 (wrap (if nosep 'identity 'ewoc--wrap))
f860b721 253 (new-ewoc (ewoc--create (current-buffer)
2bbacc0e 254 (funcall wrap pretty-printer)
f860b721 255 dll))
2bbacc0e 256 (hf-pp (funcall wrap 'insert))
7f0ea399
TTN
257 (pos (point))
258 head foot)
5b467bf4
SM
259 (ewoc--set-buffer-bind-dll new-ewoc
260 ;; Set default values
261 (unless header (setq header ""))
262 (unless footer (setq footer ""))
7f0ea399 263 (setf (ewoc--node-start-marker dll) (copy-marker pos)
f718c2fc
TTN
264 foot (ewoc--insert-new-node dll footer hf-pp dll)
265 head (ewoc--insert-new-node foot header hf-pp dll)
2bbacc0e 266 (ewoc--hf-pp new-ewoc) hf-pp
7f0ea399
TTN
267 (ewoc--footer new-ewoc) foot
268 (ewoc--header new-ewoc) head))
5b467bf4
SM
269 ;; Return the ewoc
270 new-ewoc))
271
8d1bec8d
TTN
272(defalias 'ewoc-data 'ewoc--node-data
273 "Extract the data encapsulated by NODE and return it.
274
275\(fn NODE)")
5b467bf4 276
bb8d35a2
TTN
277(defun ewoc-set-data (node data)
278 "Set NODE to encapsulate DATA."
279 (setf (ewoc--node-data node) data))
280
5b467bf4 281(defun ewoc-enter-first (ewoc data)
d4ddf783
TTN
282 "Enter DATA first in EWOC.
283Return the new node."
5b467bf4 284 (ewoc--set-buffer-bind-dll ewoc
bb7a346f 285 (ewoc-enter-after ewoc (ewoc--node-nth dll 0) data)))
5b467bf4
SM
286
287(defun ewoc-enter-last (ewoc data)
d4ddf783
TTN
288 "Enter DATA last in EWOC.
289Return the new node."
5b467bf4 290 (ewoc--set-buffer-bind-dll ewoc
bb7a346f 291 (ewoc-enter-before ewoc (ewoc--node-nth dll -1) data)))
5b467bf4 292
5b467bf4 293(defun ewoc-enter-after (ewoc node data)
10c471e6 294 "Enter a new element DATA after NODE in EWOC.
d4ddf783 295Return the new node."
5b467bf4 296 (ewoc--set-buffer-bind-dll ewoc
bb7a346f 297 (ewoc-enter-before ewoc (ewoc--node-next dll node) data)))
5b467bf4
SM
298
299(defun ewoc-enter-before (ewoc node data)
10c471e6 300 "Enter a new element DATA before NODE in EWOC.
d4ddf783 301Return the new node."
5b467bf4 302 (ewoc--set-buffer-bind-dll ewoc
f718c2fc 303 (ewoc--insert-new-node node data (ewoc--pretty-printer ewoc) dll)))
5b467bf4
SM
304
305(defun ewoc-next (ewoc node)
d4ddf783
TTN
306 "Return the node in EWOC that follows NODE.
307Return nil if NODE is nil or the last element."
5b467bf4
SM
308 (when node
309 (ewoc--filter-hf-nodes
bb7a346f 310 ewoc (ewoc--node-next (ewoc--dll ewoc) node))))
5b467bf4
SM
311
312(defun ewoc-prev (ewoc node)
d4ddf783
TTN
313 "Return the node in EWOC that precedes NODE.
314Return nil if NODE is nil or the first element."
5b467bf4
SM
315 (when node
316 (ewoc--filter-hf-nodes
bb7a346f 317 ewoc (ewoc--node-prev (ewoc--dll ewoc) node))))
5b467bf4 318
5b467bf4
SM
319(defun ewoc-nth (ewoc n)
320 "Return the Nth node.
f0529b5b 321N counts from zero. Return nil if there is less than N elements.
5b467bf4 322If N is negative, return the -(N+1)th last element.
7dd2e64c
TTN
323Thus, (ewoc-nth ewoc 0) returns the first node,
324and (ewoc-nth ewoc -1) returns the last node.
8d1bec8d 325Use `ewoc-data' to extract the data from the node."
5b467bf4
SM
326 ;; Skip the header (or footer, if n is negative).
327 (setq n (if (< n 0) (1- n) (1+ n)))
328 (ewoc--filter-hf-nodes ewoc
bb7a346f 329 (ewoc--node-nth (ewoc--dll ewoc) n)))
5b467bf4
SM
330
331(defun ewoc-map (map-function ewoc &rest args)
332 "Apply MAP-FUNCTION to all elements in EWOC.
333MAP-FUNCTION is applied to the first element first.
334If MAP-FUNCTION returns non-nil the element will be refreshed (its
335pretty-printer will be called once again).
336
0111ab41
JB
337Note that the buffer for EWOC will be the current buffer when
338MAP-FUNCTION is called. MAP-FUNCTION must restore the current
339buffer before it returns, if it changes it.
5b467bf4
SM
340
341If more than two arguments are given, the remaining
342arguments will be passed to MAP-FUNCTION."
343 (ewoc--set-buffer-bind-dll-let* ewoc
344 ((footer (ewoc--footer ewoc))
8433d470 345 (pp (ewoc--pretty-printer ewoc))
bb7a346f 346 (node (ewoc--node-nth dll 1)))
bfbdb5ca
TTN
347 (save-excursion
348 (while (not (eq node footer))
349 (if (apply map-function (ewoc--node-data node) args)
bb7a346f
SM
350 (ewoc--refresh-node pp node dll))
351 (setq node (ewoc--node-next dll node))))))
5b467bf4 352
f569c26e
TTN
353(defun ewoc-delete (ewoc &rest nodes)
354 "Delete NODES from EWOC."
355 (ewoc--set-buffer-bind-dll-let* ewoc
ec491f90 356 ((L nil) (R nil) (last (ewoc--last-node ewoc)))
f569c26e
TTN
357 (dolist (node nodes)
358 ;; If we are about to delete the node pointed at by last-node,
359 ;; set last-node to nil.
ec491f90
TTN
360 (when (eq last node)
361 (setf last nil (ewoc--last-node ewoc) nil))
f569c26e 362 (delete-region (ewoc--node-start-marker node)
bb7a346f 363 (ewoc--node-start-marker (ewoc--node-next dll node)))
f569c26e
TTN
364 (set-marker (ewoc--node-start-marker node) nil)
365 (setf L (ewoc--node-left node)
366 R (ewoc--node-right node)
367 ;; Link neighbors to each other.
368 (ewoc--node-right L) R
369 (ewoc--node-left R) L
370 ;; Forget neighbors.
371 (ewoc--node-left node) nil
372 (ewoc--node-right node) nil))))
373
5b467bf4
SM
374(defun ewoc-filter (ewoc predicate &rest args)
375 "Remove all elements in EWOC for which PREDICATE returns nil.
a1506d29 376Note that the buffer for EWOC will be current-buffer when PREDICATE
0111ab41 377is called. PREDICATE must restore the current buffer before it returns
5b467bf4 378if it changes it.
0111ab41 379The PREDICATE is called with the element as its first argument. If any
5b467bf4
SM
380ARGS are given they will be passed to the PREDICATE."
381 (ewoc--set-buffer-bind-dll-let* ewoc
bb7a346f 382 ((node (ewoc--node-nth dll 1))
5b467bf4 383 (footer (ewoc--footer ewoc))
f569c26e 384 (goodbye nil)
2c246c9f 385 (inhibit-read-only t))
5b467bf4 386 (while (not (eq node footer))
5b467bf4 387 (unless (apply predicate (ewoc--node-data node) args)
f569c26e 388 (push node goodbye))
bb7a346f 389 (setq node (ewoc--node-next dll node)))
f569c26e 390 (apply 'ewoc-delete ewoc goodbye)))
5b467bf4 391
44946a4c 392(defun ewoc-locate (ewoc &optional pos guess)
5b467bf4 393 "Return the node that POS (a buffer position) is within.
44946a4c 394POS may be a marker or an integer. It defaults to point.
0111ab41 395GUESS should be a node that it is likely to be near POS.
5b467bf4
SM
396
397If POS points before the first element, the first node is returned.
398If POS points after the last element, the last node is returned.
399If the EWOC is empty, nil is returned."
44946a4c 400 (unless pos (setq pos (point)))
bb7a346f 401 (ewoc--set-buffer-bind-dll ewoc
5b467bf4
SM
402
403 (cond
404 ;; Nothing present?
bb7a346f 405 ((eq (ewoc--node-nth dll 1) (ewoc--node-nth dll -1))
5b467bf4
SM
406 nil)
407
408 ;; Before second elem?
bb7a346f
SM
409 ((< pos (ewoc--node-start-marker (ewoc--node-nth dll 2)))
410 (ewoc--node-nth dll 1))
5b467bf4
SM
411
412 ;; After one-before-last elem?
bb7a346f
SM
413 ((>= pos (ewoc--node-start-marker (ewoc--node-nth dll -2)))
414 (ewoc--node-nth dll -2))
5b467bf4
SM
415
416 ;; We now know that pos is within a elem.
417 (t
418 ;; Make an educated guess about which of the three known
419 ;; node'es (the first, the last, or GUESS) is nearest.
bb7a346f 420 (let* ((best-guess (ewoc--node-nth dll 1))
5b467bf4
SM
421 (distance (abs (- pos (ewoc--node-start-marker best-guess)))))
422 (when guess
423 (let ((d (abs (- pos (ewoc--node-start-marker guess)))))
424 (when (< d distance)
425 (setq distance d)
426 (setq best-guess guess))))
427
bb7a346f 428 (let* ((g (ewoc--node-nth dll -1)) ;Check the last elem
5b467bf4
SM
429 (d (abs (- pos (ewoc--node-start-marker g)))))
430 (when (< d distance)
431 (setq distance d)
432 (setq best-guess g)))
433
7dd2e64c 434 (when (ewoc--last-node ewoc) ;Check "previous".
5b467bf4
SM
435 (let* ((g (ewoc--last-node ewoc))
436 (d (abs (- pos (ewoc--node-start-marker g)))))
437 (when (< d distance)
438 (setq distance d)
439 (setq best-guess g))))
440
441 ;; best-guess is now a "best guess".
442 ;; Find the correct node. First determine in which direction
443 ;; it lies, and then move in that direction until it is found.
a1506d29 444
5b467bf4
SM
445 (cond
446 ;; Is pos after the guess?
447 ((>= pos
448 (ewoc--node-start-marker best-guess))
449 ;; Loop until we are exactly one node too far down...
450 (while (>= pos (ewoc--node-start-marker best-guess))
bb7a346f 451 (setq best-guess (ewoc--node-next dll best-guess)))
5b467bf4 452 ;; ...and return the previous node.
bb7a346f 453 (ewoc--node-prev dll best-guess))
5b467bf4
SM
454
455 ;; Pos is before best-guess
456 (t
457 (while (< pos (ewoc--node-start-marker best-guess))
bb7a346f 458 (setq best-guess (ewoc--node-prev dll best-guess)))
5b467bf4
SM
459 best-guess)))))))
460
461(defun ewoc-invalidate (ewoc &rest nodes)
d4ddf783
TTN
462 "Call EWOC's pretty-printer for each element in NODES.
463Delete current text first, thus effecting a \"refresh\"."
8433d470
TTN
464 (ewoc--set-buffer-bind-dll-let* ewoc
465 ((pp (ewoc--pretty-printer ewoc)))
bfbdb5ca
TTN
466 (save-excursion
467 (dolist (node nodes)
bb7a346f 468 (ewoc--refresh-node pp node dll)))))
5b467bf4 469
44946a4c 470(defun ewoc-goto-prev (ewoc arg)
d4ddf783 471 "Move point to the ARGth previous element in EWOC.
5b467bf4 472Don't move if we are at the first element, or if EWOC is empty.
d4ddf783 473Return the node we moved to."
5b467bf4 474 (ewoc--set-buffer-bind-dll-let* ewoc
6d84ac85 475 ((node (ewoc-locate ewoc (point))))
5b467bf4 476 (when node
44946a4c
SM
477 ;; If we were past the last element, first jump to it.
478 (when (>= (point) (ewoc--node-start-marker (ewoc--node-right node)))
479 (setq arg (1- arg)))
5b467bf4
SM
480 (while (and node (> arg 0))
481 (setq arg (1- arg))
bb7a346f 482 (setq node (ewoc--node-prev dll node)))
5b467bf4
SM
483 ;; Never step above the first element.
484 (unless (ewoc--filter-hf-nodes ewoc node)
bb7a346f 485 (setq node (ewoc--node-nth dll 1)))
5b467bf4
SM
486 (ewoc-goto-node ewoc node))))
487
44946a4c 488(defun ewoc-goto-next (ewoc arg)
d4ddf783
TTN
489 "Move point to the ARGth next element in EWOC.
490Return the node (or nil if we just passed the last node)."
5b467bf4 491 (ewoc--set-buffer-bind-dll-let* ewoc
6d84ac85 492 ((node (ewoc-locate ewoc (point))))
5b467bf4
SM
493 (while (and node (> arg 0))
494 (setq arg (1- arg))
bb7a346f 495 (setq node (ewoc--node-next dll node)))
5b467bf4 496 ;; Never step below the first element.
44946a4c 497 ;; (unless (ewoc--filter-hf-nodes ewoc node)
bb7a346f 498 ;; (setq node (ewoc--node-nth dll -2)))
5b467bf4
SM
499 (ewoc-goto-node ewoc node)))
500
501(defun ewoc-goto-node (ewoc node)
d4ddf783 502 "Move point to NODE in EWOC."
5b467bf4
SM
503 (ewoc--set-buffer-bind-dll ewoc
504 (goto-char (ewoc--node-start-marker node))
505 (if goal-column (move-to-column goal-column))
506 (setf (ewoc--last-node ewoc) node)))
507
508(defun ewoc-refresh (ewoc)
509 "Refresh all data in EWOC.
510The pretty-printer that was specified when the EWOC was created
511will be called for all elements in EWOC.
512Note that `ewoc-invalidate' is more efficient if only a small
513number of elements needs to be refreshed."
514 (ewoc--set-buffer-bind-dll-let* ewoc
cb3430a1 515 ((footer (ewoc--footer ewoc)))
5b467bf4 516 (let ((inhibit-read-only t))
bb7a346f 517 (delete-region (ewoc--node-start-marker (ewoc--node-nth dll 1))
5b467bf4
SM
518 (ewoc--node-start-marker footer))
519 (goto-char (ewoc--node-start-marker footer))
340d9945 520 (let ((pp (ewoc--pretty-printer ewoc))
bb7a346f 521 (node (ewoc--node-nth dll 1)))
5b467bf4
SM
522 (while (not (eq node footer))
523 (set-marker (ewoc--node-start-marker node) (point))
340d9945 524 (funcall pp (ewoc--node-data node))
bb7a346f 525 (setq node (ewoc--node-next dll node)))))
5b467bf4
SM
526 (set-marker (ewoc--node-start-marker footer) (point))))
527
528(defun ewoc-collect (ewoc predicate &rest args)
529 "Select elements from EWOC using PREDICATE.
530Return a list of all selected data elements.
0111ab41
JB
531PREDICATE is a function that takes a data element as its first
532argument. The elements on the returned list will appear in the
533same order as in the buffer. You should not rely on the order of
534calls to PREDICATE.
535Note that the buffer the EWOC is displayed in is the current
536buffer when PREDICATE is called. PREDICATE must restore it if it
537changes it.
5b467bf4
SM
538If more than two arguments are given the
539remaining arguments will be passed to PREDICATE."
540 (ewoc--set-buffer-bind-dll-let* ewoc
541 ((header (ewoc--header ewoc))
bb7a346f 542 (node (ewoc--node-nth dll -2))
5b467bf4
SM
543 result)
544 (while (not (eq node header))
545 (if (apply predicate (ewoc--node-data node) args)
546 (push (ewoc--node-data node) result))
bb7a346f 547 (setq node (ewoc--node-prev dll node)))
d5337506 548 result))
5b467bf4
SM
549
550(defun ewoc-buffer (ewoc)
551 "Return the buffer that is associated with EWOC.
d4ddf783 552Return nil if the buffer has been deleted."
5b467bf4
SM
553 (let ((buf (ewoc--buffer ewoc)))
554 (when (buffer-name buf) buf)))
555
cb3430a1
SM
556(defun ewoc-get-hf (ewoc)
557 "Return a cons cell containing the (HEADER . FOOTER) of EWOC."
558 (cons (ewoc--node-data (ewoc--header ewoc))
559 (ewoc--node-data (ewoc--footer ewoc))))
560
561(defun ewoc-set-hf (ewoc header footer)
562 "Set the HEADER and FOOTER of EWOC."
60eae434
TTN
563 (ewoc--set-buffer-bind-dll-let* ewoc
564 ((head (ewoc--header ewoc))
2bbacc0e
TTN
565 (foot (ewoc--footer ewoc))
566 (hf-pp (ewoc--hf-pp ewoc)))
60eae434
TTN
567 (setf (ewoc--node-data head) header
568 (ewoc--node-data foot) footer)
569 (save-excursion
bb7a346f
SM
570 (ewoc--refresh-node hf-pp head dll)
571 (ewoc--refresh-node hf-pp foot dll))))
cb3430a1 572
5b467bf4
SM
573\f
574(provide 'ewoc)
575
7ece7aba
SM
576;; Local Variables:
577;; eval: (put 'ewoc--set-buffer-bind-dll 'lisp-indent-hook 1)
578;; eval: (put 'ewoc--set-buffer-bind-dll-let* 'lisp-indent-hook 2)
579;; End:
5b467bf4 580
7ece7aba 581;; arch-tag: d78915b9-9a07-44bf-aac6-04a1fc1bd6d4
5b467bf4 582;;; ewoc.el ends here