diff lisp/emacs-lisp/ewoc.el @ 28088:b442dfc3cef0

*** empty log message ***
author Stefan Monnier <monnier@iro.umontreal.ca>
date Sat, 11 Mar 2000 03:51:31 +0000
parents
children 06cfa273543d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/emacs-lisp/ewoc.el	Sat Mar 11 03:51:31 2000 +0000
@@ -0,0 +1,620 @@
+;;; ewoc.el -- Utility to maintain a view of a list of objects in a buffer
+
+;; Copyright (C) 1991-2000   Free Software Foundation
+
+;; Author: Per Cederqvist <ceder@lysator.liu.se>
+;;	Inge Wallin <inge@lysator.liu.se>
+;; Maintainer: monnier@gnu.org
+;; Created: 3 Aug 1992
+;; Keywords: extensions, lisp
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; Ewoc Was Once Cookie
+;; But now it's Emacs' Widget for Object Collections
+
+;; As the name implies this derives from the `cookie' package (part
+;; of Elib).  The changes are mostly superficial:
+
+;; - uses CL (and its `defstruct'
+;; - separate from Elib.
+;; - uses its own version of a doubly-linked list which allows us
+;;   to merge the elib-wrapper and the elib-node structures into ewoc-node
+;; - dropping functions not used by PCL-CVS (the only client of ewoc at the
+;;   time of writing)
+;; - removing unused arguments
+;; - renaming:
+;;   elib-node	==>  ewoc--node
+;;   collection ==>  ewoc
+;;   tin 	==>  ewoc--node
+;;   cookie 	==>  data or element or elem
+
+;;     Introduction
+;;     ============
+;;
+;; Ewoc is a package that implements a connection between an
+;; dll (a doubly linked list) and the contents of a buffer.
+;; Possible uses are dired (have all files in a list, and show them),
+;; buffer-list, kom-prioritize (in the LysKOM elisp client) and
+;; others.  pcl-cvs.el uses ewoc.el.
+;;
+;; Ewoc can be considered as the `view' part of a model-view-controller.
+;;
+;; A `element' can be any lisp object.  When you use the ewoc
+;; package you specify a pretty-printer, a function that inserts
+;; a printable representation of the element in the buffer.  (The
+;; pretty-printer should use "insert" and not
+;; "insert-before-markers").
+;;
+;; A `ewoc' consists of a doubly linked list of elements, a
+;; header, a footer and a pretty-printer.  It is displayed at a
+;; certain point in a certain buffer.  (The buffer and point are
+;; fixed when the ewoc is created).  The header and the footer
+;; are constant strings.  They appear before and after the elements.
+;; (Currently, once set, they can not be changed).
+;;
+;; Ewoc does not affect the mode of the buffer in any way. It
+;; merely makes it easy to connect an underlying data representation
+;; to the buffer contents.
+;;
+;; A `ewoc--node' is an object that contains one element.  There are
+;; functions in this package that given an ewoc--node extracts the data, or
+;; gives the next or previous ewoc--node.  (All ewoc--nodes are linked together
+;; in a doubly linked list.  The 'previous' ewoc--node is the one that appears
+;; before the other in the buffer.)  You should not do anything with
+;; an ewoc--node except pass it to the functions in this package.
+;;
+;; An ewoc is a very dynamic thing.  You can easily add or delete elements.
+;; You can apply a function to all elements in an ewoc, etc, etc.
+;;
+;; Remember that an element can be anything.  Your imagination is the
+;; limit!  It is even possible to have another ewoc as an
+;; element.  In that way some kind of tree hierarchy can be created.
+;;
+;; Full documentation will, God willing, soon be available in a
+;; Texinfo manual.
+
+;; In the mean time `grep '^(.*ewoc-[^-]' emacs-lisp/ewoc.el' can help
+;; you find all the exported functions:
+;; 
+;; (defun ewoc-create (buffer pretty-printer &optional header footer pos)
+;; (defalias 'ewoc-data 'ewoc--node-data)
+;; (defun ewoc-enter-first (ewoc data)
+;; (defun ewoc-enter-last (ewoc data)
+;; (defun ewoc-enter-after (ewoc node data)
+;; (defun ewoc-enter-before (ewoc node data)
+;; (defun ewoc-next (ewoc node)
+;; (defun ewoc-prev (ewoc node)
+;; (defun ewoc-nth (ewoc n)
+;; (defun ewoc-map (map-function ewoc &rest args)
+;; (defun ewoc-filter (ewoc predicate &rest args)
+;; (defun ewoc-locate (ewoc pos &optional guess)
+;; (defun ewoc-invalidate (ewoc &rest nodes)
+;; (defun ewoc-goto-prev (ewoc pos arg)
+;; (defun ewoc-goto-next (ewoc pos arg)
+;; (defun ewoc-goto-node (ewoc node)
+;; (defun ewoc-refresh (ewoc)
+;; (defun ewoc-collect (ewoc predicate &rest args)
+;; (defun ewoc-buffer (ewoc)
+
+
+;;     Coding conventions
+;;     ==================
+;;
+;; All functions of course start with `ewoc'.  Functions and macros
+;; starting with the prefix `ewoc--' are meant for internal use,
+;; while those starting with `ewoc-' are exported for public use.
+;; There are currently no global or buffer-local variables used.
+
+
+;;; Code:
+
+(eval-when-compile (require 'cl))	;because of CL compiler macros
+
+;; The doubly linked list is implemented as a circular list
+;; with a dummy node first and last. The dummy node is used as
+;; "the dll" (or rather is the dll handle passed around).
+
+(defstruct (ewoc--node
+	    (:type vector)		;required for ewoc--node-branch hack
+	    (:constructor ewoc--node-create (start-marker data)))
+  left right data start-marker)
+
+(defalias 'ewoc--node-branch 'aref)
+
+(defun ewoc--dll-create ()
+  "Create an empty doubly linked list."
+  (let ((dummy-node (ewoc--node-create 'DL-LIST 'DL-LIST)))
+    (setf (ewoc--node-right dummy-node) dummy-node)
+    (setf (ewoc--node-left dummy-node) dummy-node)
+    dummy-node))
+
+(defun ewoc--node-enter-before (node elemnode)
+  "Insert ELEMNODE before NODE in a DLL."
+  (assert (and (null (ewoc--node-left elemnode)) (null (ewoc--node-right elemnode))))
+  (setf (ewoc--node-left elemnode) (ewoc--node-left node))
+  (setf (ewoc--node-right elemnode) node)
+  (setf (ewoc--node-right (ewoc--node-left node)) elemnode)
+  (setf (ewoc--node-left node) elemnode))
+
+(defun ewoc--node-enter-first (dll node)
+  "Add a free floating NODE first in DLL."
+  (ewoc--node-enter-before (ewoc--node-right dll) node))
+
+(defun ewoc--node-enter-last (dll node)
+  "Add a free floating NODE last in DLL."
+  (ewoc--node-enter-before dll node))
+
+(defun ewoc--node-next (dll node)
+  "Return the node after NODE, or nil if NODE is the last node."
+  (unless (eq (ewoc--node-right node) dll) (ewoc--node-right node)))
+
+(defun ewoc--node-prev (dll node)
+  "Return the node before NODE, or nil if NODE is the first node."
+  (unless (eq (ewoc--node-left node) dll) (ewoc--node-left node)))
+
+(defun ewoc--node-delete (node)
+  "Unbind NODE from its doubly linked list and return it."
+  ;; This is a no-op when applied to the dummy node. This will return
+  ;; nil if applied to the dummy node since it always contains nil.
+  (setf (ewoc--node-right (ewoc--node-left node)) (ewoc--node-right node))
+  (setf (ewoc--node-left (ewoc--node-right node)) (ewoc--node-left node))
+  (setf (ewoc--node-left node) nil)
+  (setf (ewoc--node-right node) nil)
+  node)
+
+(defun ewoc--node-nth (dll n)
+  "Return the Nth node from the doubly linked list DLL.
+N counts from zero. If DLL is not that long, nil is returned.
+If N is negative, return the -(N+1)th last element.
+Thus, (ewoc--node-nth dll 0) returns the first node,
+and (ewoc--node-nth dll -1) returns the last node."
+  ;; Branch 0 ("follow left pointer") is used when n is negative.
+  ;; Branch 1 ("follow right pointer") is used otherwise.
+  (let* ((branch (if (< n 0) 0 1))
+	 (node   (ewoc--node-branch dll branch)))
+    (if (< n 0) (setq n (- -1 n)))
+    (while (and (not (eq dll node)) (> n 0))
+      (setq node (ewoc--node-branch node branch))
+      (setq n (1- n)))
+    (unless (eq dll node) node)))
+
+
+;;; The ewoc data type
+
+(defstruct (ewoc
+	    (:constructor nil)
+	    (:constructor ewoc--create
+			  (buffer pretty-printer header footer dll))
+	    (:conc-name ewoc--))
+  buffer pretty-printer header footer dll last-node)
+
+(defmacro ewoc--set-buffer-bind-dll-let* (ewoc varlist &rest forms)
+  "Execute FORMS with ewoc--buffer selected as current buffer,
+dll bound to ewoc--dll, and VARLIST bound as in a let*.
+dll will be bound when VARLIST is initialized, but the current
+buffer will *not* have been changed.
+Return value of last form in FORMS."
+  (let ((old-buffer (make-symbol "old-buffer"))
+	(hnd (make-symbol "ewoc")))
+    (` (let* (((, old-buffer) (current-buffer))
+	      ((, hnd) (, ewoc))
+	      (dll (ewoc--dll (, hnd)))
+	      (,@ varlist))
+	 (set-buffer (ewoc--buffer (, hnd)))
+	 (unwind-protect
+	     (progn (,@ forms))
+	   (set-buffer (, old-buffer)))))))
+
+(defmacro ewoc--set-buffer-bind-dll (ewoc &rest forms)
+  `(ewoc--set-buffer-bind-dll-let* ,ewoc nil ,@forms))
+
+(defsubst ewoc--filter-hf-nodes (ewoc node)
+  "Evaluate NODE once and return it.
+BUT if it is the header or the footer in EWOC return nil instead."
+  (unless (or (eq node (ewoc--header ewoc))
+	      (eq node (ewoc--footer ewoc)))
+    node))
+
+
+(defun ewoc--create-special-node (data string pos)
+  "Insert STRING at POS in current buffer. Remember the start
+position. Create a wrapper containing that start position and the
+element DATA."
+  (save-excursion
+    ;; Remember the position as a number so that it doesn't move
+    ;; when we insert the string.
+    (when (markerp pos) (setq pos (marker-position pos)))
+    (goto-char pos)
+    (let ((inhibit-read-only t))
+      ;; Use insert-before-markers so that the marker for the
+      ;; next element is updated.
+      (insert-before-markers string)
+      ;; Always insert a newline. You want invisible elements? You
+      ;; lose. (At least in this version). FIXME-someday. (It is
+      ;; harder to fix than it might seem. All markers have to point
+      ;; to the right place all the time...)
+      (insert-before-markers ?\n)
+      (ewoc--node-create (copy-marker pos) data))))
+
+
+(defun ewoc--create-node (data pretty-printer pos)
+  "Call PRETTY-PRINTER with point set at POS in current buffer.
+Remember the start position. Create a wrapper containing that
+start position and the element DATA."
+  (save-excursion
+    ;; Remember the position as a number so that it doesn't move
+    ;; when we insert the string.
+    (when (markerp pos) (setq pos (marker-position pos)))
+    (goto-char pos)
+    (let ((inhibit-read-only t))
+      ;; Insert the trailing newline using insert-before-markers
+      ;; so that the start position for the next element is updated.
+      (insert-before-markers ?\n)
+      ;; Move back, and call the pretty-printer.
+      (backward-char 1)
+      (funcall pretty-printer data)
+      (ewoc--node-create (copy-marker pos) data))))
+
+
+(defun ewoc--delete-node-internal (ewoc node)
+  "Delete a data string from EWOC.
+Can not be used on the footer. Returns the wrapper that is deleted.
+The start-marker in the wrapper is set to nil, so that it doesn't
+consume any more resources."
+  (let ((dll (ewoc--dll ewoc))
+	(inhibit-read-only t))
+    ;; If we are about to delete the node pointed at by last-node,
+    ;; set last-node to nil.
+    (if (eq (ewoc--last-node ewoc) node)
+	(setf (ewoc--last-node ewoc) nil))
+
+    (delete-region (ewoc--node-start-marker node)
+		   (ewoc--node-start-marker (ewoc--node-next dll node)))
+    (set-marker (ewoc--node-start-marker node) nil)
+    ;; Delete the node, and return the wrapper.
+    (ewoc--node-delete node)))
+
+
+(defvar dll)				;passed by dynamic binding
+
+(defun ewoc--refresh-node (ewoc node)
+  "Redisplay the element represented by NODE.
+Can not be used on the footer. dll *must* be bound to
+\(ewoc--dll ewoc)."
+  (let ((inhibit-read-only t))
+    (save-excursion
+      ;; First, remove the string from the buffer:
+      (delete-region (ewoc--node-start-marker node)
+		     (1- (marker-position
+			  (ewoc--node-start-marker (ewoc--node-next dll node)))))
+      ;; Calculate and insert the string.
+      (goto-char (ewoc--node-start-marker node))
+      (funcall (ewoc--pretty-printer ewoc)
+	       (ewoc--node-data node)))))
+
+;;; ===========================================================================
+;;;                  Public members of the Ewoc package
+
+
+(defun ewoc-create (buffer pretty-printer &optional header footer pos)
+  "Create an empty ewoc.
+
+The ewoc will be inserted in BUFFER. BUFFER may be a
+buffer or a buffer name. It is created if it does not exist.
+
+PRETTY-PRINTER should be a function that takes one argument, an
+element, and inserts a string representing it in the buffer (at
+point). The string PRETTY-PRINTER inserts may be empty or span
+several linse. A trailing newline will always be inserted
+automatically. The PRETTY-PRINTER should use insert, and not
+insert-before-markers.
+
+Optional third argument HEADER is a string that will always be
+present at the top of the ewoc. HEADER should end with a
+newline.  Optionaly fourth argument FOOTER is similar, and will
+always be inserted at the bottom of the ewoc.
+
+Optional fifth argument POS is a buffer position, specifying
+where the ewoc will be inserted. It defaults to the
+beginning of the buffer."
+  (let ((new-ewoc
+	 (ewoc--create (get-buffer-create buffer)
+			    pretty-printer nil nil (ewoc--dll-create))))
+    (ewoc--set-buffer-bind-dll new-ewoc
+      ;; Set default values
+      (unless header (setq header ""))
+      (unless footer (setq footer ""))
+      (unless pos (setq pos (point-min)))
+      ;; Force header to be above footer.
+      (if (markerp pos) (setq pos (marker-position pos)))
+      (let ((foot (ewoc--create-special-node footer footer pos))
+	    (head (ewoc--create-special-node header header pos)))
+	(ewoc--node-enter-first dll head)
+	(ewoc--node-enter-last  dll foot)
+	(setf (ewoc--header new-ewoc) (ewoc--node-nth dll 0))
+	(setf (ewoc--footer new-ewoc) (ewoc--node-nth dll -1))))
+    ;; Return the ewoc
+    new-ewoc))
+
+(defalias 'ewoc-data 'ewoc--node-data)
+
+(defun ewoc-enter-first (ewoc data)
+  "Enter DATA first in EWOC."
+  (ewoc--set-buffer-bind-dll ewoc
+    (ewoc-enter-after ewoc (ewoc--node-nth dll 0) data)))
+
+(defun ewoc-enter-last (ewoc data)
+  "Enter DATA last in EWOC."
+  (ewoc--set-buffer-bind-dll ewoc
+    (ewoc-enter-before ewoc (ewoc--node-nth dll -1) data)))
+
+
+(defun ewoc-enter-after (ewoc node data)
+  "Enter a new element DATA after NODE in EWOC."
+  (ewoc--set-buffer-bind-dll ewoc
+    (ewoc-enter-before ewoc (ewoc--node-next dll node) data)))
+
+(defun ewoc-enter-before (ewoc node data)
+  "Enter a new element DATA before NODE in EWOC."
+  (ewoc--set-buffer-bind-dll ewoc
+    (ewoc--node-enter-before
+     node
+     (ewoc--create-node
+      data
+      (ewoc--pretty-printer ewoc)
+      (ewoc--node-start-marker node)))))
+
+(defun ewoc-next (ewoc node)
+  "Get the next node.
+Returns nil if NODE is nil or the last element."
+  (when node
+    (ewoc--filter-hf-nodes
+     ewoc (ewoc--node-next (ewoc--dll ewoc) node))))
+
+(defun ewoc-prev (ewoc node)
+  "Get the previous node.
+Returns nil if NODE is nil or the first element."
+  (when node
+    (ewoc--filter-hf-nodes
+     ewoc
+     (ewoc--node-prev (ewoc--dll ewoc) node))))
+
+
+(defun ewoc-nth (ewoc n)
+  "Return the Nth node.
+N counts from zero. Nil is returned if there is less than N elements.
+If N is negative, return the -(N+1)th last element.
+Thus, (ewoc-nth dll 0) returns the first node,
+and (ewoc-nth dll -1) returns the last node.
+Use `ewoc--node-data' to extract the data from the node."
+  ;; Skip the header (or footer, if n is negative).
+  (setq n (if (< n 0) (1- n) (1+ n)))
+  (ewoc--filter-hf-nodes ewoc
+		  (ewoc--node-nth (ewoc--dll ewoc) n)))
+
+(defun ewoc-map (map-function ewoc &rest args)
+  "Apply MAP-FUNCTION to all elements in EWOC.
+MAP-FUNCTION is applied to the first element first.
+If MAP-FUNCTION returns non-nil the element will be refreshed (its
+pretty-printer will be called once again).
+
+Note that the buffer for EWOC will be current buffer when MAP-FUNCTION 
+is called.  MAP-FUNCTION must restore the current buffer to BUFFER before 
+it returns, if it changes it.
+
+If more than two arguments are given, the remaining
+arguments will be passed to MAP-FUNCTION."
+  (ewoc--set-buffer-bind-dll-let* ewoc
+      ((footer (ewoc--footer ewoc))
+       (node (ewoc--node-nth dll 1)))
+    (while (not (eq node footer))
+      (if (apply map-function (ewoc--node-data node) args)
+	  (ewoc--refresh-node ewoc node))
+      (setq node (ewoc--node-next dll node)))))
+
+(defun ewoc-filter (ewoc predicate &rest args)
+  "Remove all elements in EWOC for which PREDICATE returns nil.
+Note that the buffer for EWOC will be current-buffer when PREDICATE 
+is called. PREDICATE must restore the current buffer before it returns
+if it changes it.
+The PREDICATE is called with the element as its first argument. If any
+ARGS are given they will be passed to the PREDICATE."
+  (ewoc--set-buffer-bind-dll-let* ewoc
+      ((node (ewoc--node-nth dll 1))
+       (footer (ewoc--footer ewoc))
+       (next nil))
+    (while (not (eq node footer))
+      (setq next (ewoc--node-next dll node))
+      (unless (apply predicate (ewoc--node-data node) args)
+	(ewoc--delete-node-internal ewoc node))
+      (setq node next))))
+
+(defun ewoc-locate (ewoc pos &optional guess)
+  "Return the node that POS (a buffer position) is within.
+POS may be a marker or an integer.
+GUESS should be a node that it is likely that POS is near.
+
+If POS points before the first element, the first node is returned.
+If POS points after the last element, the last node is returned.
+If the EWOC is empty, nil is returned."
+  (ewoc--set-buffer-bind-dll-let* ewoc
+      ((footer (ewoc--footer ewoc)))
+
+    (cond
+     ;; Nothing present?
+     ((eq (ewoc--node-nth dll 1) (ewoc--node-nth dll -1))
+      nil)
+
+     ;; Before second elem?
+     ((< pos (ewoc--node-start-marker (ewoc--node-nth dll 2)))
+      (ewoc--node-nth dll 1))
+
+     ;; After one-before-last elem?
+     ((>= pos (ewoc--node-start-marker (ewoc--node-nth dll -2)))
+      (ewoc--node-nth dll -2))
+
+     ;; We now know that pos is within a elem.
+     (t
+      ;; Make an educated guess about which of the three known
+      ;; node'es (the first, the last, or GUESS) is nearest.
+      (let* ((best-guess (ewoc--node-nth dll 1))
+	     (distance (abs (- pos (ewoc--node-start-marker best-guess)))))
+	(when guess
+	  (let ((d (abs (- pos (ewoc--node-start-marker guess)))))
+	    (when (< d distance)
+	      (setq distance d)
+	      (setq best-guess guess))))
+
+	(let* ((g (ewoc--node-nth dll -1))	;Check the last elem
+	       (d (abs (- pos (ewoc--node-start-marker g)))))
+	  (when (< d distance)
+	    (setq distance d)
+	    (setq best-guess g)))
+
+	(when (ewoc--last-node ewoc) ;Check "previous".
+	  (let* ((g (ewoc--last-node ewoc))
+		 (d (abs (- pos (ewoc--node-start-marker g)))))
+	    (when (< d distance)
+	      (setq distance d)
+	      (setq best-guess g))))
+
+	;; best-guess is now a "best guess".
+	;; Find the correct node. First determine in which direction
+	;; it lies, and then move in that direction until it is found.
+    
+	(cond
+	 ;; Is pos after the guess?
+	 ((>= pos
+	      (ewoc--node-start-marker best-guess))
+	  ;; Loop until we are exactly one node too far down...
+	  (while (>= pos (ewoc--node-start-marker best-guess))
+	    (setq best-guess (ewoc--node-next dll best-guess)))
+	  ;; ...and return the previous node.
+	  (ewoc--node-prev dll best-guess))
+
+	 ;; Pos is before best-guess
+	 (t
+	  (while (< pos (ewoc--node-start-marker best-guess))
+	    (setq best-guess (ewoc--node-prev dll best-guess)))
+	  best-guess)))))))
+
+(defun ewoc-invalidate (ewoc &rest nodes)
+  "Refresh some elements.
+The pretty-printer that for EWOC will be called for all NODES."
+  (ewoc--set-buffer-bind-dll ewoc
+    (dolist (node nodes)
+      (ewoc--refresh-node ewoc node))))
+
+(defun ewoc-goto-prev (ewoc pos arg)
+  "Move point to the ARGth previous element.
+Don't move if we are at the first element, or if EWOC is empty.
+Returns the node we moved to."
+  (ewoc--set-buffer-bind-dll-let* ewoc
+      ((node (ewoc-locate ewoc pos (ewoc--last-node ewoc))))
+    (when node
+      (while (and node (> arg 0))
+	(setq arg (1- arg))
+	(setq node (ewoc--node-prev dll node)))
+      ;; Never step above the first element.
+      (unless (ewoc--filter-hf-nodes ewoc node)
+	(setq node (ewoc--node-nth dll 1)))
+      (ewoc-goto-node ewoc node))))
+
+(defun ewoc-goto-next (ewoc pos arg)
+  "Move point to the ARGth next element.
+Don't move if we are at the last element.
+Returns the node."
+  (ewoc--set-buffer-bind-dll-let* ewoc
+      ((node (ewoc-locate ewoc pos (ewoc--last-node ewoc))))
+    (while (and node (> arg 0))
+      (setq arg (1- arg))
+      (setq node (ewoc--node-next dll node)))
+    ;; Never step below the first element.
+    (unless (ewoc--filter-hf-nodes ewoc node)
+      (setq node (ewoc--node-nth dll -2)))
+    (ewoc-goto-node ewoc node)))
+
+(defun ewoc-goto-node (ewoc node)
+  "Move point to NODE."
+  (ewoc--set-buffer-bind-dll ewoc
+    (goto-char (ewoc--node-start-marker node))
+    (if goal-column (move-to-column goal-column))
+    (setf (ewoc--last-node ewoc) node)))
+
+(defun ewoc-refresh (ewoc)
+  "Refresh all data in EWOC.
+The pretty-printer that was specified when the EWOC was created
+will be called for all elements in EWOC.
+Note that `ewoc-invalidate' is more efficient if only a small
+number of elements needs to be refreshed."
+  (ewoc--set-buffer-bind-dll-let* ewoc
+      ((header (ewoc--header ewoc))
+       (footer (ewoc--footer ewoc)))
+    (let ((inhibit-read-only t))
+      (delete-region (ewoc--node-start-marker (ewoc--node-nth dll 1))
+		     (ewoc--node-start-marker footer))
+      (goto-char (ewoc--node-start-marker footer))
+      (let ((node (ewoc--node-nth dll 1)))
+	(while (not (eq node footer))
+	  (set-marker (ewoc--node-start-marker node) (point))
+	  (funcall (ewoc--pretty-printer ewoc)
+		   (ewoc--node-data node))
+	  (insert "\n")
+	  (setq node (ewoc--node-next dll node)))))
+    (set-marker (ewoc--node-start-marker footer) (point))))
+
+(defun ewoc-collect (ewoc predicate &rest args)
+  "Select elements from EWOC using PREDICATE.
+Return a list of all selected data elements.
+PREDICATE is a function that takes a data element as its first argument.
+The elements on the returned list will appear in the same order as in
+the buffer.  You should not rely on in which order PREDICATE is
+called.
+Note that the buffer the EWOC is displayed in is current-buffer
+when PREDICATE is called.  If PREDICATE must restore current-buffer if
+it changes it.
+If more than two arguments are given the
+remaining arguments will be passed to PREDICATE."
+  (ewoc--set-buffer-bind-dll-let* ewoc
+      ((header (ewoc--header ewoc))
+       (node (ewoc--node-nth dll -2))
+       result)
+    (while (not (eq node header))
+      (if (apply predicate (ewoc--node-data node) args)
+	  (push (ewoc--node-data node) result))
+      (setq node (ewoc--node-prev dll node)))
+    result))
+
+(defun ewoc-buffer (ewoc)
+  "Return the buffer that is associated with EWOC.
+Returns nil if the buffer has been deleted."
+  (let ((buf (ewoc--buffer ewoc)))
+    (when (buffer-name buf) buf)))
+
+
+(provide 'ewoc)
+
+;;; Local Variables:
+;;; eval: (put 'ewoc--set-buffer-bind-dll 'lisp-indent-hook 1)
+;;; eval: (put 'ewoc--set-buffer-bind-dll-let* 'lisp-indent-hook 2)
+;;; End:
+
+;;; ewoc.el ends here