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