Mercurial > emacs
comparison 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 |
comparison
equal
deleted
inserted
replaced
28087:9ca294cf76c7 | 28088:b442dfc3cef0 |
---|---|
1 ;;; ewoc.el -- Utility to maintain a view of a list of objects in a buffer | |
2 | |
3 ;; Copyright (C) 1991-2000 Free Software Foundation | |
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 2, or (at your option) | |
16 ;; 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; see the file COPYING. If not, write to the | |
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
26 ;; Boston, MA 02111-1307, USA. | |
27 | |
28 ;;; Commentary: | |
29 | |
30 ;; Ewoc Was Once Cookie | |
31 ;; But now it's Emacs' Widget for Object Collections | |
32 | |
33 ;; As the name implies this derives from the `cookie' package (part | |
34 ;; of Elib). The changes are mostly superficial: | |
35 | |
36 ;; - uses CL (and its `defstruct' | |
37 ;; - separate from Elib. | |
38 ;; - uses its own version of a doubly-linked list which allows us | |
39 ;; to merge the elib-wrapper and the elib-node structures into ewoc-node | |
40 ;; - dropping functions not used by PCL-CVS (the only client of ewoc at the | |
41 ;; time of writing) | |
42 ;; - removing unused arguments | |
43 ;; - renaming: | |
44 ;; elib-node ==> ewoc--node | |
45 ;; collection ==> ewoc | |
46 ;; tin ==> ewoc--node | |
47 ;; cookie ==> data or element or elem | |
48 | |
49 ;; Introduction | |
50 ;; ============ | |
51 ;; | |
52 ;; Ewoc is a package that implements a connection between an | |
53 ;; dll (a doubly linked list) and the contents of a buffer. | |
54 ;; Possible uses are dired (have all files in a list, and show them), | |
55 ;; buffer-list, kom-prioritize (in the LysKOM elisp client) and | |
56 ;; others. pcl-cvs.el uses ewoc.el. | |
57 ;; | |
58 ;; Ewoc can be considered as the `view' part of a model-view-controller. | |
59 ;; | |
60 ;; A `element' can be any lisp object. When you use the ewoc | |
61 ;; package you specify a pretty-printer, a function that inserts | |
62 ;; a printable representation of the element in the buffer. (The | |
63 ;; pretty-printer should use "insert" and not | |
64 ;; "insert-before-markers"). | |
65 ;; | |
66 ;; A `ewoc' consists of a doubly linked list of elements, a | |
67 ;; header, a footer and a pretty-printer. It is displayed at a | |
68 ;; certain point in a certain buffer. (The buffer and point are | |
69 ;; fixed when the ewoc is created). The header and the footer | |
70 ;; are constant strings. They appear before and after the elements. | |
71 ;; (Currently, once set, they can not be changed). | |
72 ;; | |
73 ;; Ewoc does not affect the mode of the buffer in any way. It | |
74 ;; merely makes it easy to connect an underlying data representation | |
75 ;; to the buffer contents. | |
76 ;; | |
77 ;; A `ewoc--node' is an object that contains one element. There are | |
78 ;; functions in this package that given an ewoc--node extracts the data, or | |
79 ;; gives the next or previous ewoc--node. (All ewoc--nodes are linked together | |
80 ;; in a doubly linked list. The 'previous' ewoc--node is the one that appears | |
81 ;; before the other in the buffer.) You should not do anything with | |
82 ;; an ewoc--node except pass it to the functions in this package. | |
83 ;; | |
84 ;; An ewoc is a very dynamic thing. You can easily add or delete elements. | |
85 ;; You can apply a function to all elements in an ewoc, etc, etc. | |
86 ;; | |
87 ;; Remember that an element can be anything. Your imagination is the | |
88 ;; limit! It is even possible to have another ewoc as an | |
89 ;; element. In that way some kind of tree hierarchy can be created. | |
90 ;; | |
91 ;; Full documentation will, God willing, soon be available in a | |
92 ;; Texinfo manual. | |
93 | |
94 ;; In the mean time `grep '^(.*ewoc-[^-]' emacs-lisp/ewoc.el' can help | |
95 ;; you find all the exported functions: | |
96 ;; | |
97 ;; (defun ewoc-create (buffer pretty-printer &optional header footer pos) | |
98 ;; (defalias 'ewoc-data 'ewoc--node-data) | |
99 ;; (defun ewoc-enter-first (ewoc data) | |
100 ;; (defun ewoc-enter-last (ewoc data) | |
101 ;; (defun ewoc-enter-after (ewoc node data) | |
102 ;; (defun ewoc-enter-before (ewoc node data) | |
103 ;; (defun ewoc-next (ewoc node) | |
104 ;; (defun ewoc-prev (ewoc node) | |
105 ;; (defun ewoc-nth (ewoc n) | |
106 ;; (defun ewoc-map (map-function ewoc &rest args) | |
107 ;; (defun ewoc-filter (ewoc predicate &rest args) | |
108 ;; (defun ewoc-locate (ewoc pos &optional guess) | |
109 ;; (defun ewoc-invalidate (ewoc &rest nodes) | |
110 ;; (defun ewoc-goto-prev (ewoc pos arg) | |
111 ;; (defun ewoc-goto-next (ewoc pos arg) | |
112 ;; (defun ewoc-goto-node (ewoc node) | |
113 ;; (defun ewoc-refresh (ewoc) | |
114 ;; (defun ewoc-collect (ewoc predicate &rest args) | |
115 ;; (defun ewoc-buffer (ewoc) | |
116 | |
117 | |
118 ;; Coding conventions | |
119 ;; ================== | |
120 ;; | |
121 ;; All functions of course start with `ewoc'. Functions and macros | |
122 ;; starting with the prefix `ewoc--' are meant for internal use, | |
123 ;; while those starting with `ewoc-' are exported for public use. | |
124 ;; There are currently no global or buffer-local variables used. | |
125 | |
126 | |
127 ;;; Code: | |
128 | |
129 (eval-when-compile (require 'cl)) ;because of CL compiler macros | |
130 | |
131 ;; The doubly linked list is implemented as a circular list | |
132 ;; with a dummy node first and last. The dummy node is used as | |
133 ;; "the dll" (or rather is the dll handle passed around). | |
134 | |
135 (defstruct (ewoc--node | |
136 (:type vector) ;required for ewoc--node-branch hack | |
137 (:constructor ewoc--node-create (start-marker data))) | |
138 left right data start-marker) | |
139 | |
140 (defalias 'ewoc--node-branch 'aref) | |
141 | |
142 (defun ewoc--dll-create () | |
143 "Create an empty doubly linked list." | |
144 (let ((dummy-node (ewoc--node-create 'DL-LIST 'DL-LIST))) | |
145 (setf (ewoc--node-right dummy-node) dummy-node) | |
146 (setf (ewoc--node-left dummy-node) dummy-node) | |
147 dummy-node)) | |
148 | |
149 (defun ewoc--node-enter-before (node elemnode) | |
150 "Insert ELEMNODE before NODE in a DLL." | |
151 (assert (and (null (ewoc--node-left elemnode)) (null (ewoc--node-right elemnode)))) | |
152 (setf (ewoc--node-left elemnode) (ewoc--node-left node)) | |
153 (setf (ewoc--node-right elemnode) node) | |
154 (setf (ewoc--node-right (ewoc--node-left node)) elemnode) | |
155 (setf (ewoc--node-left node) elemnode)) | |
156 | |
157 (defun ewoc--node-enter-first (dll node) | |
158 "Add a free floating NODE first in DLL." | |
159 (ewoc--node-enter-before (ewoc--node-right dll) node)) | |
160 | |
161 (defun ewoc--node-enter-last (dll node) | |
162 "Add a free floating NODE last in DLL." | |
163 (ewoc--node-enter-before dll node)) | |
164 | |
165 (defun ewoc--node-next (dll node) | |
166 "Return the node after NODE, or nil if NODE is the last node." | |
167 (unless (eq (ewoc--node-right node) dll) (ewoc--node-right node))) | |
168 | |
169 (defun ewoc--node-prev (dll node) | |
170 "Return the node before NODE, or nil if NODE is the first node." | |
171 (unless (eq (ewoc--node-left node) dll) (ewoc--node-left node))) | |
172 | |
173 (defun ewoc--node-delete (node) | |
174 "Unbind NODE from its doubly linked list and return it." | |
175 ;; This is a no-op when applied to the dummy node. This will return | |
176 ;; nil if applied to the dummy node since it always contains nil. | |
177 (setf (ewoc--node-right (ewoc--node-left node)) (ewoc--node-right node)) | |
178 (setf (ewoc--node-left (ewoc--node-right node)) (ewoc--node-left node)) | |
179 (setf (ewoc--node-left node) nil) | |
180 (setf (ewoc--node-right node) nil) | |
181 node) | |
182 | |
183 (defun ewoc--node-nth (dll n) | |
184 "Return the Nth node from the doubly linked list DLL. | |
185 N counts from zero. If DLL is not that long, nil is returned. | |
186 If N is negative, return the -(N+1)th last element. | |
187 Thus, (ewoc--node-nth dll 0) returns the first node, | |
188 and (ewoc--node-nth dll -1) returns the last node." | |
189 ;; Branch 0 ("follow left pointer") is used when n is negative. | |
190 ;; Branch 1 ("follow right pointer") is used otherwise. | |
191 (let* ((branch (if (< n 0) 0 1)) | |
192 (node (ewoc--node-branch dll branch))) | |
193 (if (< n 0) (setq n (- -1 n))) | |
194 (while (and (not (eq dll node)) (> n 0)) | |
195 (setq node (ewoc--node-branch node branch)) | |
196 (setq n (1- n))) | |
197 (unless (eq dll node) node))) | |
198 | |
199 | |
200 ;;; The ewoc data type | |
201 | |
202 (defstruct (ewoc | |
203 (:constructor nil) | |
204 (:constructor ewoc--create | |
205 (buffer pretty-printer header footer dll)) | |
206 (:conc-name ewoc--)) | |
207 buffer pretty-printer header footer dll last-node) | |
208 | |
209 (defmacro ewoc--set-buffer-bind-dll-let* (ewoc varlist &rest forms) | |
210 "Execute FORMS with ewoc--buffer selected as current buffer, | |
211 dll bound to ewoc--dll, and VARLIST bound as in a let*. | |
212 dll will be bound when VARLIST is initialized, but the current | |
213 buffer will *not* have been changed. | |
214 Return value of last form in FORMS." | |
215 (let ((old-buffer (make-symbol "old-buffer")) | |
216 (hnd (make-symbol "ewoc"))) | |
217 (` (let* (((, old-buffer) (current-buffer)) | |
218 ((, hnd) (, ewoc)) | |
219 (dll (ewoc--dll (, hnd))) | |
220 (,@ varlist)) | |
221 (set-buffer (ewoc--buffer (, hnd))) | |
222 (unwind-protect | |
223 (progn (,@ forms)) | |
224 (set-buffer (, old-buffer))))))) | |
225 | |
226 (defmacro ewoc--set-buffer-bind-dll (ewoc &rest forms) | |
227 `(ewoc--set-buffer-bind-dll-let* ,ewoc nil ,@forms)) | |
228 | |
229 (defsubst ewoc--filter-hf-nodes (ewoc node) | |
230 "Evaluate NODE once and return it. | |
231 BUT if it is the header or the footer in EWOC return nil instead." | |
232 (unless (or (eq node (ewoc--header ewoc)) | |
233 (eq node (ewoc--footer ewoc))) | |
234 node)) | |
235 | |
236 | |
237 (defun ewoc--create-special-node (data string pos) | |
238 "Insert STRING at POS in current buffer. Remember the start | |
239 position. Create a wrapper containing that start position and the | |
240 element DATA." | |
241 (save-excursion | |
242 ;; Remember the position as a number so that it doesn't move | |
243 ;; when we insert the string. | |
244 (when (markerp pos) (setq pos (marker-position pos))) | |
245 (goto-char pos) | |
246 (let ((inhibit-read-only t)) | |
247 ;; Use insert-before-markers so that the marker for the | |
248 ;; next element is updated. | |
249 (insert-before-markers string) | |
250 ;; Always insert a newline. You want invisible elements? You | |
251 ;; lose. (At least in this version). FIXME-someday. (It is | |
252 ;; harder to fix than it might seem. All markers have to point | |
253 ;; to the right place all the time...) | |
254 (insert-before-markers ?\n) | |
255 (ewoc--node-create (copy-marker pos) data)))) | |
256 | |
257 | |
258 (defun ewoc--create-node (data pretty-printer pos) | |
259 "Call PRETTY-PRINTER with point set at POS in current buffer. | |
260 Remember the start position. Create a wrapper containing that | |
261 start position and the element DATA." | |
262 (save-excursion | |
263 ;; Remember the position as a number so that it doesn't move | |
264 ;; when we insert the string. | |
265 (when (markerp pos) (setq pos (marker-position pos))) | |
266 (goto-char pos) | |
267 (let ((inhibit-read-only t)) | |
268 ;; Insert the trailing newline using insert-before-markers | |
269 ;; so that the start position for the next element is updated. | |
270 (insert-before-markers ?\n) | |
271 ;; Move back, and call the pretty-printer. | |
272 (backward-char 1) | |
273 (funcall pretty-printer data) | |
274 (ewoc--node-create (copy-marker pos) data)))) | |
275 | |
276 | |
277 (defun ewoc--delete-node-internal (ewoc node) | |
278 "Delete a data string from EWOC. | |
279 Can not be used on the footer. Returns the wrapper that is deleted. | |
280 The start-marker in the wrapper is set to nil, so that it doesn't | |
281 consume any more resources." | |
282 (let ((dll (ewoc--dll ewoc)) | |
283 (inhibit-read-only t)) | |
284 ;; If we are about to delete the node pointed at by last-node, | |
285 ;; set last-node to nil. | |
286 (if (eq (ewoc--last-node ewoc) node) | |
287 (setf (ewoc--last-node ewoc) nil)) | |
288 | |
289 (delete-region (ewoc--node-start-marker node) | |
290 (ewoc--node-start-marker (ewoc--node-next dll node))) | |
291 (set-marker (ewoc--node-start-marker node) nil) | |
292 ;; Delete the node, and return the wrapper. | |
293 (ewoc--node-delete node))) | |
294 | |
295 | |
296 (defvar dll) ;passed by dynamic binding | |
297 | |
298 (defun ewoc--refresh-node (ewoc node) | |
299 "Redisplay the element represented by NODE. | |
300 Can not be used on the footer. dll *must* be bound to | |
301 \(ewoc--dll ewoc)." | |
302 (let ((inhibit-read-only t)) | |
303 (save-excursion | |
304 ;; First, remove the string from the buffer: | |
305 (delete-region (ewoc--node-start-marker node) | |
306 (1- (marker-position | |
307 (ewoc--node-start-marker (ewoc--node-next dll node))))) | |
308 ;; Calculate and insert the string. | |
309 (goto-char (ewoc--node-start-marker node)) | |
310 (funcall (ewoc--pretty-printer ewoc) | |
311 (ewoc--node-data node))))) | |
312 | |
313 ;;; =========================================================================== | |
314 ;;; Public members of the Ewoc package | |
315 | |
316 | |
317 (defun ewoc-create (buffer pretty-printer &optional header footer pos) | |
318 "Create an empty ewoc. | |
319 | |
320 The ewoc will be inserted in BUFFER. BUFFER may be a | |
321 buffer or a buffer name. It is created if it does not exist. | |
322 | |
323 PRETTY-PRINTER should be a function that takes one argument, an | |
324 element, and inserts a string representing it in the buffer (at | |
325 point). The string PRETTY-PRINTER inserts may be empty or span | |
326 several linse. A trailing newline will always be inserted | |
327 automatically. The PRETTY-PRINTER should use insert, and not | |
328 insert-before-markers. | |
329 | |
330 Optional third argument HEADER is a string that will always be | |
331 present at the top of the ewoc. HEADER should end with a | |
332 newline. Optionaly fourth argument FOOTER is similar, and will | |
333 always be inserted at the bottom of the ewoc. | |
334 | |
335 Optional fifth argument POS is a buffer position, specifying | |
336 where the ewoc will be inserted. It defaults to the | |
337 beginning of the buffer." | |
338 (let ((new-ewoc | |
339 (ewoc--create (get-buffer-create buffer) | |
340 pretty-printer nil nil (ewoc--dll-create)))) | |
341 (ewoc--set-buffer-bind-dll new-ewoc | |
342 ;; Set default values | |
343 (unless header (setq header "")) | |
344 (unless footer (setq footer "")) | |
345 (unless pos (setq pos (point-min))) | |
346 ;; Force header to be above footer. | |
347 (if (markerp pos) (setq pos (marker-position pos))) | |
348 (let ((foot (ewoc--create-special-node footer footer pos)) | |
349 (head (ewoc--create-special-node header header pos))) | |
350 (ewoc--node-enter-first dll head) | |
351 (ewoc--node-enter-last dll foot) | |
352 (setf (ewoc--header new-ewoc) (ewoc--node-nth dll 0)) | |
353 (setf (ewoc--footer new-ewoc) (ewoc--node-nth dll -1)))) | |
354 ;; Return the ewoc | |
355 new-ewoc)) | |
356 | |
357 (defalias 'ewoc-data 'ewoc--node-data) | |
358 | |
359 (defun ewoc-enter-first (ewoc data) | |
360 "Enter DATA first in EWOC." | |
361 (ewoc--set-buffer-bind-dll ewoc | |
362 (ewoc-enter-after ewoc (ewoc--node-nth dll 0) data))) | |
363 | |
364 (defun ewoc-enter-last (ewoc data) | |
365 "Enter DATA last in EWOC." | |
366 (ewoc--set-buffer-bind-dll ewoc | |
367 (ewoc-enter-before ewoc (ewoc--node-nth dll -1) data))) | |
368 | |
369 | |
370 (defun ewoc-enter-after (ewoc node data) | |
371 "Enter a new element DATA after NODE in EWOC." | |
372 (ewoc--set-buffer-bind-dll ewoc | |
373 (ewoc-enter-before ewoc (ewoc--node-next dll node) data))) | |
374 | |
375 (defun ewoc-enter-before (ewoc node data) | |
376 "Enter a new element DATA before NODE in EWOC." | |
377 (ewoc--set-buffer-bind-dll ewoc | |
378 (ewoc--node-enter-before | |
379 node | |
380 (ewoc--create-node | |
381 data | |
382 (ewoc--pretty-printer ewoc) | |
383 (ewoc--node-start-marker node))))) | |
384 | |
385 (defun ewoc-next (ewoc node) | |
386 "Get the next node. | |
387 Returns nil if NODE is nil or the last element." | |
388 (when node | |
389 (ewoc--filter-hf-nodes | |
390 ewoc (ewoc--node-next (ewoc--dll ewoc) node)))) | |
391 | |
392 (defun ewoc-prev (ewoc node) | |
393 "Get the previous node. | |
394 Returns nil if NODE is nil or the first element." | |
395 (when node | |
396 (ewoc--filter-hf-nodes | |
397 ewoc | |
398 (ewoc--node-prev (ewoc--dll ewoc) node)))) | |
399 | |
400 | |
401 (defun ewoc-nth (ewoc n) | |
402 "Return the Nth node. | |
403 N counts from zero. Nil is returned if there is less than N elements. | |
404 If N is negative, return the -(N+1)th last element. | |
405 Thus, (ewoc-nth dll 0) returns the first node, | |
406 and (ewoc-nth dll -1) returns the last node. | |
407 Use `ewoc--node-data' to extract the data from the node." | |
408 ;; Skip the header (or footer, if n is negative). | |
409 (setq n (if (< n 0) (1- n) (1+ n))) | |
410 (ewoc--filter-hf-nodes ewoc | |
411 (ewoc--node-nth (ewoc--dll ewoc) n))) | |
412 | |
413 (defun ewoc-map (map-function ewoc &rest args) | |
414 "Apply MAP-FUNCTION to all elements in EWOC. | |
415 MAP-FUNCTION is applied to the first element first. | |
416 If MAP-FUNCTION returns non-nil the element will be refreshed (its | |
417 pretty-printer will be called once again). | |
418 | |
419 Note that the buffer for EWOC will be current buffer when MAP-FUNCTION | |
420 is called. MAP-FUNCTION must restore the current buffer to BUFFER before | |
421 it returns, if it changes it. | |
422 | |
423 If more than two arguments are given, the remaining | |
424 arguments will be passed to MAP-FUNCTION." | |
425 (ewoc--set-buffer-bind-dll-let* ewoc | |
426 ((footer (ewoc--footer ewoc)) | |
427 (node (ewoc--node-nth dll 1))) | |
428 (while (not (eq node footer)) | |
429 (if (apply map-function (ewoc--node-data node) args) | |
430 (ewoc--refresh-node ewoc node)) | |
431 (setq node (ewoc--node-next dll node))))) | |
432 | |
433 (defun ewoc-filter (ewoc predicate &rest args) | |
434 "Remove all elements in EWOC for which PREDICATE returns nil. | |
435 Note that the buffer for EWOC will be current-buffer when PREDICATE | |
436 is called. PREDICATE must restore the current buffer before it returns | |
437 if it changes it. | |
438 The PREDICATE is called with the element as its first argument. If any | |
439 ARGS are given they will be passed to the PREDICATE." | |
440 (ewoc--set-buffer-bind-dll-let* ewoc | |
441 ((node (ewoc--node-nth dll 1)) | |
442 (footer (ewoc--footer ewoc)) | |
443 (next nil)) | |
444 (while (not (eq node footer)) | |
445 (setq next (ewoc--node-next dll node)) | |
446 (unless (apply predicate (ewoc--node-data node) args) | |
447 (ewoc--delete-node-internal ewoc node)) | |
448 (setq node next)))) | |
449 | |
450 (defun ewoc-locate (ewoc pos &optional guess) | |
451 "Return the node that POS (a buffer position) is within. | |
452 POS may be a marker or an integer. | |
453 GUESS should be a node that it is likely that POS is near. | |
454 | |
455 If POS points before the first element, the first node is returned. | |
456 If POS points after the last element, the last node is returned. | |
457 If the EWOC is empty, nil is returned." | |
458 (ewoc--set-buffer-bind-dll-let* ewoc | |
459 ((footer (ewoc--footer ewoc))) | |
460 | |
461 (cond | |
462 ;; Nothing present? | |
463 ((eq (ewoc--node-nth dll 1) (ewoc--node-nth dll -1)) | |
464 nil) | |
465 | |
466 ;; Before second elem? | |
467 ((< pos (ewoc--node-start-marker (ewoc--node-nth dll 2))) | |
468 (ewoc--node-nth dll 1)) | |
469 | |
470 ;; After one-before-last elem? | |
471 ((>= pos (ewoc--node-start-marker (ewoc--node-nth dll -2))) | |
472 (ewoc--node-nth dll -2)) | |
473 | |
474 ;; We now know that pos is within a elem. | |
475 (t | |
476 ;; Make an educated guess about which of the three known | |
477 ;; node'es (the first, the last, or GUESS) is nearest. | |
478 (let* ((best-guess (ewoc--node-nth dll 1)) | |
479 (distance (abs (- pos (ewoc--node-start-marker best-guess))))) | |
480 (when guess | |
481 (let ((d (abs (- pos (ewoc--node-start-marker guess))))) | |
482 (when (< d distance) | |
483 (setq distance d) | |
484 (setq best-guess guess)))) | |
485 | |
486 (let* ((g (ewoc--node-nth dll -1)) ;Check the last elem | |
487 (d (abs (- pos (ewoc--node-start-marker g))))) | |
488 (when (< d distance) | |
489 (setq distance d) | |
490 (setq best-guess g))) | |
491 | |
492 (when (ewoc--last-node ewoc) ;Check "previous". | |
493 (let* ((g (ewoc--last-node ewoc)) | |
494 (d (abs (- pos (ewoc--node-start-marker g))))) | |
495 (when (< d distance) | |
496 (setq distance d) | |
497 (setq best-guess g)))) | |
498 | |
499 ;; best-guess is now a "best guess". | |
500 ;; Find the correct node. First determine in which direction | |
501 ;; it lies, and then move in that direction until it is found. | |
502 | |
503 (cond | |
504 ;; Is pos after the guess? | |
505 ((>= pos | |
506 (ewoc--node-start-marker best-guess)) | |
507 ;; Loop until we are exactly one node too far down... | |
508 (while (>= pos (ewoc--node-start-marker best-guess)) | |
509 (setq best-guess (ewoc--node-next dll best-guess))) | |
510 ;; ...and return the previous node. | |
511 (ewoc--node-prev dll best-guess)) | |
512 | |
513 ;; Pos is before best-guess | |
514 (t | |
515 (while (< pos (ewoc--node-start-marker best-guess)) | |
516 (setq best-guess (ewoc--node-prev dll best-guess))) | |
517 best-guess))))))) | |
518 | |
519 (defun ewoc-invalidate (ewoc &rest nodes) | |
520 "Refresh some elements. | |
521 The pretty-printer that for EWOC will be called for all NODES." | |
522 (ewoc--set-buffer-bind-dll ewoc | |
523 (dolist (node nodes) | |
524 (ewoc--refresh-node ewoc node)))) | |
525 | |
526 (defun ewoc-goto-prev (ewoc pos arg) | |
527 "Move point to the ARGth previous element. | |
528 Don't move if we are at the first element, or if EWOC is empty. | |
529 Returns the node we moved to." | |
530 (ewoc--set-buffer-bind-dll-let* ewoc | |
531 ((node (ewoc-locate ewoc pos (ewoc--last-node ewoc)))) | |
532 (when node | |
533 (while (and node (> arg 0)) | |
534 (setq arg (1- arg)) | |
535 (setq node (ewoc--node-prev dll node))) | |
536 ;; Never step above the first element. | |
537 (unless (ewoc--filter-hf-nodes ewoc node) | |
538 (setq node (ewoc--node-nth dll 1))) | |
539 (ewoc-goto-node ewoc node)))) | |
540 | |
541 (defun ewoc-goto-next (ewoc pos arg) | |
542 "Move point to the ARGth next element. | |
543 Don't move if we are at the last element. | |
544 Returns the node." | |
545 (ewoc--set-buffer-bind-dll-let* ewoc | |
546 ((node (ewoc-locate ewoc pos (ewoc--last-node ewoc)))) | |
547 (while (and node (> arg 0)) | |
548 (setq arg (1- arg)) | |
549 (setq node (ewoc--node-next dll node))) | |
550 ;; Never step below the first element. | |
551 (unless (ewoc--filter-hf-nodes ewoc node) | |
552 (setq node (ewoc--node-nth dll -2))) | |
553 (ewoc-goto-node ewoc node))) | |
554 | |
555 (defun ewoc-goto-node (ewoc node) | |
556 "Move point to NODE." | |
557 (ewoc--set-buffer-bind-dll ewoc | |
558 (goto-char (ewoc--node-start-marker node)) | |
559 (if goal-column (move-to-column goal-column)) | |
560 (setf (ewoc--last-node ewoc) node))) | |
561 | |
562 (defun ewoc-refresh (ewoc) | |
563 "Refresh all data in EWOC. | |
564 The pretty-printer that was specified when the EWOC was created | |
565 will be called for all elements in EWOC. | |
566 Note that `ewoc-invalidate' is more efficient if only a small | |
567 number of elements needs to be refreshed." | |
568 (ewoc--set-buffer-bind-dll-let* ewoc | |
569 ((header (ewoc--header ewoc)) | |
570 (footer (ewoc--footer ewoc))) | |
571 (let ((inhibit-read-only t)) | |
572 (delete-region (ewoc--node-start-marker (ewoc--node-nth dll 1)) | |
573 (ewoc--node-start-marker footer)) | |
574 (goto-char (ewoc--node-start-marker footer)) | |
575 (let ((node (ewoc--node-nth dll 1))) | |
576 (while (not (eq node footer)) | |
577 (set-marker (ewoc--node-start-marker node) (point)) | |
578 (funcall (ewoc--pretty-printer ewoc) | |
579 (ewoc--node-data node)) | |
580 (insert "\n") | |
581 (setq node (ewoc--node-next dll node))))) | |
582 (set-marker (ewoc--node-start-marker footer) (point)))) | |
583 | |
584 (defun ewoc-collect (ewoc predicate &rest args) | |
585 "Select elements from EWOC using PREDICATE. | |
586 Return a list of all selected data elements. | |
587 PREDICATE is a function that takes a data element as its first argument. | |
588 The elements on the returned list will appear in the same order as in | |
589 the buffer. You should not rely on in which order PREDICATE is | |
590 called. | |
591 Note that the buffer the EWOC is displayed in is current-buffer | |
592 when PREDICATE is called. If PREDICATE must restore current-buffer if | |
593 it changes it. | |
594 If more than two arguments are given the | |
595 remaining arguments will be passed to PREDICATE." | |
596 (ewoc--set-buffer-bind-dll-let* ewoc | |
597 ((header (ewoc--header ewoc)) | |
598 (node (ewoc--node-nth dll -2)) | |
599 result) | |
600 (while (not (eq node header)) | |
601 (if (apply predicate (ewoc--node-data node) args) | |
602 (push (ewoc--node-data node) result)) | |
603 (setq node (ewoc--node-prev dll node))) | |
604 result)) | |
605 | |
606 (defun ewoc-buffer (ewoc) | |
607 "Return the buffer that is associated with EWOC. | |
608 Returns nil if the buffer has been deleted." | |
609 (let ((buf (ewoc--buffer ewoc))) | |
610 (when (buffer-name buf) buf))) | |
611 | |
612 | |
613 (provide 'ewoc) | |
614 | |
615 ;;; Local Variables: | |
616 ;;; eval: (put 'ewoc--set-buffer-bind-dll 'lisp-indent-hook 1) | |
617 ;;; eval: (put 'ewoc--set-buffer-bind-dll-let* 'lisp-indent-hook 2) | |
618 ;;; End: | |
619 | |
620 ;;; ewoc.el ends here |