home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / lucid / lemacs-19.6 / lisp / pcl-cvs / cookie.el < prev    next >
Encoding:
Text File  |  1992-10-28  |  25.8 KB  |  906 lines

  1. ;;; Id: cookie.el,v 1.10 1992/07/20 16:01:20 ceder Exp 
  2. ;;; cookie.el -- Utility to display cookies in buffers
  3. ;;; Copyright (C) 1991, 1992  Per Cederqvist
  4. ;;;
  5. ;;; This program is free software; you can redistribute it and/or modify
  6. ;;; it under the terms of the GNU General Public License as published by
  7. ;;; the Free Software Foundation; either version 2 of the License, or
  8. ;;; (at your option) any later version.
  9. ;;;
  10. ;;; This program is distributed in the hope that it will be useful,
  11. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. ;;; GNU General Public License for more details.
  14. ;;;
  15. ;;; You should have received a copy of the GNU General Public License
  16. ;;; along with this program; if not, write to the Free Software
  17. ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19. ;;; Note that this file is still under development.  Comments,
  20. ;;; enhancements and bug fixes are welcome.
  21. ;;; Send them to ceder@lysator.liu.se.
  22.  
  23. (defun impl nil (error "Not yet implemented!"))
  24.  
  25. ;;; Cookie is a package that imlements a connection between an
  26. ;;; elib-dll (a doubly linked list) and the contents of a buffer.
  27. ;;; Possible uses are dired (have all files in a list, and show them),
  28. ;;; buffer-list, kom-prioritize (in the LysKOM elisp client) and
  29. ;;; others. pcl-cvs.el uses cookie.el.
  30. ;;;
  31. ;;; A cookie buffer contains a header, any number of cookies, and a
  32. ;;; footer. The header and footer are constant strings that are given
  33. ;;; to cookie-create when the buffer is placed under cookie. Each
  34. ;;; cookie is formatted in the buffer by calling a user-supplied
  35. ;;; function that takes a cookie and returns a string. The string may
  36. ;;; be empty, or contain any number of lines. An extra newline is
  37. ;;; always appended unless the string is empty.
  38. ;;;
  39. ;;; Cookie does not affect the mode of the buffer in any way. It
  40. ;;; merely makes it easy to connect an underlying data representation
  41. ;;; to the buffer contents.
  42. ;;;
  43. ;;; A tin is an object that contains one cookie.  There are functions
  44. ;;; in this package that for instance given a tin extracts the cookie,
  45. ;;; or gives the next or previous tin.  (All tins are linked together
  46. ;;; in a doubly linked list.  The 'previous' tin is the one that
  47. ;;; appears before the other in the buffer.)  You should not do
  48. ;;; anything with a tin except handle it to the functions in this
  49. ;;; package.
  50.  
  51. ;;; INTERNAL DOCUMENTATION (Your understanding of this package might
  52. ;;; increase if you read it, but you should not exploit the knowledge
  53. ;;; you gain. The internal details might change without notice).
  54. ;;;
  55. ;;; At the implementation level a tin is really an elib-node that
  56. ;;; consists of
  57. ;;;      left        Pointer to previous tin
  58. ;;;      right       Pointer to next tin
  59. ;;;      data        Holder of a 'wrapper'.
  60. ;;; The internals of an elib-node are in fact unknown to cookie.el.
  61. ;;; It uses elib-dll.el to handle everything that deals with the
  62. ;;; doubly linked list.
  63. ;;;
  64. ;;; The wrapper data type contains
  65. ;;;      start-marker    Position of the printed representation of the
  66. ;;;                      cookie in the buffer. 
  67. ;;;      cookie          The user-supplied element.
  68. ;;;
  69. ;;; The wrapper is not accessible to the user of this package.
  70.  
  71. (require 'elib-dll)
  72. (provide 'cookie)
  73.  
  74. (defvar cookies nil
  75.   "A doubly linked list that contains the underlying data representation
  76. for the contents of a cookie buffer. The package elib-dll is used to
  77. manipulate this list.")
  78.  
  79. (defvar cookie-pretty-printer nil
  80.   "The function that is used to pretty-print a cookie in this buffer.")
  81.  
  82. (defvar cookie-header nil
  83.   "The tin that holds the header cookie.")
  84.  
  85. (defvar cookie-footer nil
  86.   "The tin that holds the footer cookie.")
  87.  
  88. (defvar cookie-last-tin nil
  89.   "The tin the cursor was positioned at, the last time the cookie
  90. package checked the cursor position. Buffer local in all buffers
  91. the cookie package works on. You may set this if your package
  92. thinks it knows where the cursor will be the next time this
  93. package is called. It can speed things up.
  94.  
  95. It must never be set to a tin that has been deleted.")
  96.  
  97. ;;; ================================================================
  98. ;;;      Internal functions for use in the cookie package
  99.  
  100. (put 'cookie-set-buffer 'lisp-indent-hook 1)
  101.  
  102. (defmacro cookie-set-buffer (buffer &rest forms)
  103.  
  104.   ;; Execute FORMS with BUFFER selected as current buffer.
  105.   ;; Return value of last form in FORMS.  INTERNAL USE ONLY.
  106.  
  107.   (let ((old-buffer (make-symbol "old-buffer")))
  108.     (` (let (((, old-buffer) (current-buffer)))
  109.      (set-buffer (get-buffer-create (, buffer)))
  110.      (unwind-protect
  111.          (progn (,@ forms))
  112.        (set-buffer (, old-buffer)))))))
  113.  
  114.  
  115. (defmacro cookie-filter-hf (tin)
  116.  
  117.   ;; Evaluate TIN once and return it. BUT if it is
  118.   ;; equal to cookie-header or cookie-footer return nil instead.
  119.   ;; INTERNAL USE ONLY.
  120.  
  121.   (let ((tempvar (make-symbol "tin")))
  122.     (` (let (((, tempvar) (, tin)))
  123.      (if (or (eq (, tempvar) cookie-header)
  124.          (eq (, tempvar) cookie-footer))
  125.          nil
  126.        (, tempvar))))))
  127.  
  128.  
  129. ;;; The wrapper data type.
  130. ;;; Constructor:
  131.  
  132. (defun cookie-create-wrapper (start-marker
  133.                   cookie)
  134.   ;; Create a wrapper.   INTERNAL USE ONLY.
  135.   (cons 'WRAPPER (vector start-marker cookie)))
  136.  
  137.  
  138. ;;; Selectors:
  139.  
  140. (defun cookie-wrapper-start-marker (wrapper)
  141.   ;; Get start-marker from wrapper.    INTERNAL USE ONLY.
  142.   (elt (cdr wrapper) 0))
  143.  
  144.  
  145. (defun cookie-wrapper-cookie-safe (wrapper)
  146.   ;; Get cookie from wrapper.   INTERNAL USE ONLY.
  147.   ;; Returns nil if given nil as input.
  148.   ;; This is the same as cookie-wrapper-cookie in version 18.57
  149.   ;; of emacs, but elt should signal an error when given nil
  150.   ;; as input (according to the info files).
  151.   (elt (cdr wrapper) 1))
  152.  
  153. (defun cookie-wrapper-cookie (wrapper)
  154.   ;; Get cookie from wrapper.   INTERNAL USE ONLY.
  155.   (elt (cdr wrapper) 1))
  156.  
  157.  
  158. ;;; Modifiers:
  159.  
  160. ;; Currently not used.
  161. ;;(defun cookie-wrapper-set-start-marker (wrapper newval)
  162. ;;  ;; Set start-marker in WRAPPER to NEWVAL.   INTERNAL USE ONLY.
  163. ;;  (aset (cdr wrapper) 0 newval))
  164.  
  165. ;; Currently not used.
  166. ;;(defun cookie-wrapper-set-cookie (wrapper newval)
  167. ;;  ;; Set cookie in WRAPPER to NEWVAL.   INTERNAL USE ONLY.
  168. ;;  (aset (cdr wrapper) 1 newval))
  169.  
  170.  
  171.  
  172. ;;; Predicate:
  173.  
  174. ;; Currently not used.
  175. ;;(defun cookie-wrapper-p (object)
  176. ;;  ;; Return t if OBJECT is a wrapper.   INTERNAL USE ONLY.
  177. ;;  (eq (car-safe object) 'WRAPPER))
  178.  
  179. ;;; end of wrapper data type.
  180.                  
  181.  
  182. (defun cookie-create-wrapper-and-insert (cookie string pos)
  183.   ;; Insert STRING at POS in current buffer. Remember start
  184.   ;; position. Create a wrapper containing the start position and the
  185.   ;; COOKIE.
  186.   ;;    INTERNAL USE ONLY.
  187.  
  188.   (save-excursion
  189.     (goto-char pos)
  190.     ;; Remember the position as a number so that it doesn't move
  191.     ;; when we insert the string.
  192.     (let ((start (if (markerp pos)
  193.              (marker-position pos)
  194.            pos))
  195.       buffer-read-only)
  196.       ;; Use insert-before-markers so that the marker for the
  197.       ;; next cookie is updated.
  198.       (insert-before-markers string)
  199.       (insert-before-markers ?\n)
  200.       (cookie-create-wrapper (copy-marker start) cookie))))
  201.  
  202.  
  203. (defun cookie-delete-tin-internal (tin)
  204.   ;; Delete a cookie from the buffer.  INTERNAL USE ONLY.
  205.   ;; Can not be used on the footer.
  206.   (let (buffer-read-only)
  207.     (delete-region (cookie-wrapper-start-marker (dll-element cookies tin))
  208.            (cookie-wrapper-start-marker
  209.             (dll-element cookies
  210.                  (dll-next cookies  tin))))))
  211.  
  212.  
  213.  
  214. (defun cookie-refresh-tin (tin)
  215.   ;; Redisplay the cookie represented by TIN. INTERNAL USE ONLY.
  216.   ;; Can not be used on the footer.
  217.  
  218.   (save-excursion
  219.     (let (buffer-read-only)
  220.       ;; First, remove the string:
  221.       (delete-region (cookie-wrapper-start-marker (dll-element cookies tin))
  222.              (1- (marker-position
  223.               (cookie-wrapper-start-marker
  224.                (dll-element cookies
  225.                     (dll-next cookies  tin))))))
  226.  
  227.       ;; Calculate and insert the string.
  228.  
  229.       (goto-char (cookie-wrapper-start-marker (dll-element cookies tin)))
  230.       (insert
  231.        (funcall cookie-pretty-printer
  232.         (cookie-wrapper-cookie (dll-element cookies tin)))))))
  233.  
  234.  
  235. ;;; ================================================================
  236. ;;;      The public members of the cookie package
  237.  
  238.  
  239. (defun cookie-cookie (buffer tin)
  240.   "Get the cookie from a TIN. Args: BUFFER TIN."
  241.   (cookie-set-buffer buffer
  242.     (cookie-wrapper-cookie (dll-element cookies tin))))
  243.  
  244.  
  245.  
  246.  
  247. (defun cookie-create (buffer pretty-printer &optional header footer)
  248.  
  249.   "Start to use the cookie package in BUFFER.
  250. BUFFER may be a buffer or a buffer name. It is created if it does not exist.
  251. Beware that the entire contents of the buffer will be erased, and all local
  252. varables deleted. (The mode of the BUFFER should therefore be set after the
  253. call to cookie-create).
  254.  
  255. PRETTY-PRINTER is a function that takes one cookie and returns a string
  256. to be displayed in the buffer. The string may be empty. If it is not
  257. empty a newline will be added automatically. It may span several lines.
  258. Optional third argument HEADER is a string that will always be present
  259. at the top of the buffer. HEADER should end with a newline. Optionaly
  260. fourth argument FOOTER is similar, and will always be inserted at the
  261. bottom of the buffer."
  262.  
  263.   (cookie-set-buffer buffer
  264.  
  265.     (kill-all-local-variables)
  266.     (setq buffer-read-only nil)
  267.     (erase-buffer)
  268.  
  269.     (make-local-variable 'cookie-last-tin)
  270.     (make-local-variable 'cookie-pretty-printer)
  271.     (make-local-variable 'cookie-header)
  272.     (make-local-variable 'cookie-footer)
  273.     (make-local-variable 'cookies)
  274.  
  275.     (setq cookie-last-tin nil)
  276.     (setq cookie-pretty-printer pretty-printer)
  277.     (setq cookies (dll-create))
  278.  
  279.     (dll-enter-first cookies
  280.              (cookie-create-wrapper-and-insert
  281.               header header 0))
  282.     (setq cookie-header (dll-nth cookies 0))
  283.  
  284.     (dll-enter-last cookies
  285.             (cookie-create-wrapper-and-insert
  286.              footer footer (point-max)))
  287.     (setq cookie-footer (dll-nth cookies -1))
  288.  
  289.     (goto-char (point-min))
  290.     (forward-line 1)
  291.     (setq buffer-read-only t)))
  292.  
  293.  
  294. (defun cookie-set-header (buffer header)
  295.   "Change the header. Args: BUFFER HEADER."
  296.   (impl))
  297.  
  298.  
  299. (defun cookie-set-footer (buffer header)
  300.   "Change the footer. Args: BUFFER FOOTER."
  301.   (impl))
  302.  
  303.  
  304.  
  305. (defun cookie-enter-first (buffer cookie)
  306.   "Enter a COOKIE first in BUFFER.
  307. Args: BUFFER COOKIE."
  308.  
  309.   (cookie-set-buffer buffer
  310.  
  311.     ;; It is always safe to insert an element after the first element,
  312.     ;; because the header is always present. (dll-nth cookies 0) should
  313.     ;; never return nil.
  314.  
  315.     (dll-enter-after
  316.      cookies
  317.      (dll-nth cookies 0)
  318.      (cookie-create-wrapper-and-insert
  319.       cookie
  320.       (funcall cookie-pretty-printer cookie)
  321.       (cookie-wrapper-start-marker
  322.        (dll-element cookies (dll-nth cookies 1)))))))
  323.  
  324.  
  325.  
  326. (defun cookie-enter-last (buffer cookie)
  327.   "Enter a COOKIE last in BUFFER.
  328. Args: BUFFER COOKIE."
  329.  
  330.   (cookie-set-buffer buffer
  331.  
  332.     ;; Remember that the header and footer are always present. There
  333.     ;; is no need to check if (dll-nth cookies -2) returns nil.
  334.  
  335.     (dll-enter-before
  336.      cookies
  337.      (dll-nth cookies -1)
  338.      (cookie-create-wrapper-and-insert
  339.       cookie
  340.       (funcall cookie-pretty-printer cookie)
  341.       (cookie-wrapper-start-marker (dll-last cookies))))))
  342.  
  343.  
  344. (defun cookie-enter-after (buffer node cookie)
  345.   (impl))
  346.  
  347.  
  348. (defun cookie-enter-before (buffer node cookie)
  349.   (impl))
  350.  
  351.  
  352.  
  353. (defun tin-next (buffer tin)
  354.   "Get the next tin. Args: BUFFER TIN.
  355. Returns nil if TIN is nil or the last cookie."
  356.   (if tin
  357.       (cookie-set-buffer buffer
  358.     (cookie-filter-hf (dll-next cookies tin)))))
  359.  
  360.  
  361.  
  362. (defun tin-previous (buffer tin)
  363.   "Get the previous tin. Args: BUFFER TIN.
  364. Returns nil if TIN is nil or the first cookie."
  365.   (if tin
  366.       (cookie-set-buffer buffer
  367.     (cookie-filter-hf (dll-previous cookies tin)))))
  368.  
  369.  
  370. (defun tin-nth (buffer n)
  371.  
  372.   "Return the Nth tin. Args: BUFFER N.
  373. N counts from zero. Nil is returned if there is less than N cookies.
  374. If N is negative, return the -(N+1)th last element.
  375. Thus, (tin-nth dll 0) returns the first node,
  376. and (tin-nth dll -1) returns the last node.
  377.  
  378. Use cookie-cookie to extract the cookie from the tin."
  379.  
  380.   (cookie-set-buffer buffer
  381.  
  382.     ;; Skip the header (or footer, if n is negative).
  383.     (if (< n 0)
  384.     (setq n (1- n))
  385.       (setq n (1+ n)))
  386.  
  387.     (cookie-filter-hf (dll-nth cookies n))))
  388.  
  389.  
  390.  
  391. (defun tin-delete (buffer tin)
  392.   "Delete a cookie. Args: BUFFER TIN."
  393.  
  394.   (cookie-set-buffer buffer
  395.     (if (eq cookie-last-tin tin)
  396.     (setq cookie-last-tin nil))
  397.  
  398.     (cookie-delete-tin-internal tin)
  399.     (dll-delete cookies tin)))
  400.  
  401.  
  402.  
  403. (defun cookie-delete-first (buffer)
  404.   "Delete first cookie and return it. Args: BUFFER.
  405. Returns nil if there is no cookie left."
  406.  
  407.   (cookie-set-buffer buffer
  408.  
  409.     ;; We have to check that we do not try to delete the footer.
  410.  
  411.     (let ((tin (dll-nth cookies 1)))    ;Skip the header.
  412.       (if (eq tin cookie-footer)
  413.       nil
  414.     (cookie-delete-tin-internal tin)
  415.     (cookie-wrapper-cookie (dll-delete cookies tin))))))
  416.  
  417.  
  418.  
  419. (defun cookie-delete-last (buffer)
  420.   "Delete last cookie and return it. Args: BUFFER.
  421. Returns nil if there is no cookie left."
  422.  
  423.   (cookie-set-buffer buffer
  424.  
  425.     ;; We have to check that we do not try to delete the header.
  426.  
  427.     (let ((tin (dll-nth cookies -2)))    ;Skip the footer.
  428.       (if (eq tin cookie-header)
  429.       nil
  430.     (cookie-delete-tin-internal tin)
  431.     (cookie-wrapper-cookie (dll-delete cookies tin))))))
  432.  
  433.  
  434.  
  435. (defun cookie-first (buffer)
  436.  
  437.   "Return the first cookie in BUFFER. The cookie is not removed."
  438.  
  439.   (cookie-set-buffer buffer
  440.     (let ((tin (cookie-filter-hf (dll-nth cookies -1))))
  441.       (if tin
  442.       (cookie-wrapper-cookie-safe
  443.        (dll-element cookies tin))))))
  444.  
  445.  
  446. (defun cookie-last (buffer)
  447.  
  448.   "Return the last cookie in BUFFER. The cookie is not removed."
  449.  
  450.   (cookie-set-buffer buffer
  451.     (let ((tin (cookie-filter-hf (dll-nth cookies -2))))
  452.       (if tin
  453.       (cookie-wrapper-cookie-safe
  454.        (dll-element cookies tin))))))
  455.  
  456.  
  457. (defun cookie-empty (buffer)
  458.  
  459.   "Return true if there are no cookies in BUFFER."
  460.  
  461.   (cookie-set-buffer buffer
  462.     (eq (dll-nth cookies 1) cookie-footer)))
  463.  
  464.  
  465. (defun cookie-length (buffer)
  466.  
  467.   "Return number of cookies in BUFFER."
  468.  
  469.   ;; Don't count the footer and header.
  470.  
  471.   (cookie-set-buffer buffer
  472.     (- (dll-length cookies) 2)))
  473.  
  474.  
  475. (defun cookie-all (buffer)
  476.  
  477.   "Return a list of all cookies in BUFFER."
  478.  
  479.   (cookie-set-buffer buffer
  480.     (let (result 
  481.       (tin (dll-nth cookies -2)))
  482.       (while (not (eq tin cookie-header))
  483.     (setq result (cons (cookie-wrapper-cookie (dll-element cookies tin))
  484.                result))
  485.     (setq tin (dll-previous cookies tin)))
  486.       result)))
  487.  
  488. (defun cookie-clear (buffer)
  489.  
  490.   "Remove all cookies in buffer."
  491.  
  492.   (cookie-set-buffer buffer
  493.     (cookie-create buffer cookie-pretty-printer
  494.            (cookie-wrapper-cookie (dll-element cookies cookie-header))
  495.            (cookie-wrapper-cookie (dll-element cookies cookie-footer)))))
  496.  
  497.  
  498.  
  499. (defun cookie-map (map-function buffer &rest map-args)
  500.  
  501.   "Apply MAP-FUNCTION to all cookies in BUFFER.
  502. MAP-FUNCTION is applied to the first element first.
  503. If MAP-FUNCTION returns non-nil the cookie will be refreshed.
  504.  
  505. Note that BUFFER will be current buffer when MAP-FUNCTION is called.
  506.  
  507. If more than two arguments are given to cookie-map, remaining
  508. arguments will be passed to MAP-FUNCTION."
  509.  
  510.   (cookie-set-buffer buffer
  511.     (let ((tin (dll-nth cookies 1))
  512.       result)
  513.  
  514.       (while (not (eq tin cookie-footer))
  515.  
  516.     (if (apply map-function
  517.            (cookie-wrapper-cookie (dll-element cookies tin))
  518.            map-args)
  519.         (cookie-refresh-tin tin))
  520.  
  521.     (setq tin (dll-next cookies tin))))))
  522.  
  523.  
  524.  
  525. (defun cookie-map-reverse (map-function buffer &rest map-args)
  526.  
  527.   "Apply MAP-FUNCTION to all cookies in BUFFER.
  528. MAP-FUNCTION is applied to the last cookie first.
  529. If MAP-FUNCTION returns non-nil the cookie will be refreshed.
  530.  
  531. Note that BUFFER will be current buffer when MAP-FUNCTION is called.
  532.  
  533. If more than two arguments are given to cookie-map, remaining
  534. arguments will be passed to MAP-FUNCTION."
  535.  
  536.   (cookie-set-buffer buffer
  537.     (let ((tin (dll-nth cookies -2))
  538.       result)
  539.  
  540.       (while (not (eq tin cookie-header))
  541.  
  542.     (if (apply map-function
  543.            (cookie-wrapper-cookie (dll-element cookies tin))
  544.            map-args)
  545.         (cookie-refresh-tin tin))
  546.  
  547.     (setq tin (dll-previous cookies tin))))))
  548.  
  549.  
  550.  
  551. (defun cookie-enter-cookies (buffer cookie-list)
  552.  
  553.   "Insert all cookies in the list COOKIE-LIST last in BUFFER.
  554. Args: BUFFER COOKIE-LIST."
  555.  
  556.   (while cookie-list
  557.     (cookie-enter-last buffer (car cookie-list))
  558.     (setq cookie-list (cdr cookie-list))))
  559.  
  560.  
  561. (defun cookie-filter (buffer predicate)
  562.  
  563.   "Remove all cookies in BUFFER for which PREDICATE returns nil.
  564. Note that BUFFER will be current-buffer when PREDICATE is called.
  565.  
  566. The PREDICATE is called with one argument, the cookie."
  567.  
  568.   (cookie-set-buffer buffer
  569.     (let ((tin (dll-nth cookies 1))
  570.       next)
  571.       (while (not (eq tin cookie-footer))
  572.     (setq next (dll-next cookies tin))
  573.     (if (funcall predicate (cookie-wrapper-cookie (dll-element cookies tin)))
  574.         nil
  575.       (cookie-delete-tin-internal tin)
  576.       (dll-delete cookies tin))
  577.     (setq tin next)))))
  578.  
  579.  
  580. (defun tin-filter (buffer predicate)
  581.  
  582.   "Remove all cookies in BUFFER for which PREDICATE returns nil.
  583. Note that BUFFER will be current-buffer when PREDICATE is called.
  584.  
  585. The PREDICATE is called with one argument, the tin."
  586.  
  587.   (cookie-set-buffer buffer
  588.     (let ((tin (dll-nth cookies 1))
  589.       next)
  590.       (while (not (eq tin cookie-footer))
  591.     (setq next (dll-next cookies tin))
  592.     (if (funcall predicate tin)
  593.         nil
  594.       (cookie-delete-tin-internal tin)
  595.       (dll-delete cookies tin))
  596.     (setq tin next)))))
  597.  
  598. (defun cookie-pos-before-middle-p (pos tin1 tin2)
  599.  
  600.   "Return true if POS is in the first half of the region defined by TIN1 and
  601. TIN2."
  602.  
  603.   (< pos (/ (+ (cookie-wrapper-start-marker (dll-element cookies tin1))
  604.            (cookie-wrapper-start-marker (dll-element cookies tin2)))
  605.         2)))
  606.   
  607.  
  608. (defun tin-get-selection (buffer pos &optional guess force-guess)
  609.  
  610.   "Return the tin the POS is within.
  611. Args: BUFFER POS &optional GUESS FORCE-GUESS.
  612. GUESS should be a tin that it is likely that POS is near. If FORCE-GUESS
  613. is non-nil GUESS is always used as a first guess, otherwise the first
  614. guess is the first tin, last tin, or GUESS, whichever is nearest to
  615. pos in the BUFFER.
  616.  
  617. If pos points within the header, the first cookie is returned.
  618. If pos points within the footer, the last cookie is returned.
  619. Nil is returned if there is no cookie.
  620.  
  621. It is often good to specify cookie-last-tin as GUESS, but remember
  622. that cookie-last-tin is buffer local in all buffers that cookie
  623. operates on."
  624.  
  625.   (cookie-set-buffer buffer
  626.  
  627.     (cond
  628.      ;; No cookies present?
  629.      ((eq (dll-nth cookies 1) (dll-nth cookies -1))
  630.       nil)
  631.  
  632.      ;; Before first cookie?
  633.      ((< pos (cookie-wrapper-start-marker
  634.           (dll-element cookies (dll-nth cookies 1))))
  635.       (dll-nth cookies 1))
  636.  
  637.      ;; After last cookie?
  638.      ((>= pos (cookie-wrapper-start-marker (dll-last cookies)))
  639.       (dll-nth cookies -2))
  640.  
  641.      ;; We now now that pos is within a cookie.
  642.      (t
  643.       ;; Make an educated guess about which of the three known
  644.       ;; cookies (the first, the last, or GUESS) is nearest.
  645.       (setq
  646.        guess
  647.        (cond
  648.     (force-guess guess)
  649.     (guess
  650.      (cond
  651.       ;; Closest to first cookie?
  652.       ((cookie-pos-before-middle-p
  653.         pos guess
  654.         (dll-nth cookies 1))
  655.        (dll-nth cookies 1))
  656.       ;; Closest to GUESS?
  657.       ((cookie-pos-before-middle-p
  658.         pos guess
  659.         cookie-footer)
  660.        guess)
  661.       ;; Closest to last cookie.
  662.       (t (dll-previous cookies cookie-footer))))
  663.     (t
  664.      ;; No guess given.
  665.      (cond
  666.       ;; First half?
  667.       ((cookie-pos-before-middle-p
  668.         pos (dll-nth cookies 1)
  669.         cookie-footer)    
  670.        (dll-nth cookies 1))
  671.       (t (dll-previous cookies cookie-footer))))))
  672.  
  673.       ;; GUESS is now a "best guess".
  674.      
  675.       ;; Find the correct cookie. First determine in which direction
  676.       ;; it lies, and then move in that direction until it is found.
  677.     
  678.       (cond
  679.        ;; Is pos after the guess?
  680.        ((>= pos (cookie-wrapper-start-marker (dll-element cookiess guess)))
  681.  
  682.     ;; Loop until we are exactly one cookie too far down...
  683.     (while (>= pos (cookie-wrapper-start-marker (dll-element cookiess guess)))
  684.       (setq guess (dll-next cookies guess)))
  685.  
  686.     ;; ...and return the previous cookie.
  687.     (dll-previous cookies guess))
  688.  
  689.        ;; Pos is before guess
  690.        (t
  691.  
  692.     (while (< pos (cookie-wrapper-start-marker (dll-element cookiess guess)))
  693.       (setq guess (dll-previous cookies guess)))
  694.  
  695.     guess))))))
  696.  
  697.  
  698. (defun tin-start-marker (buffer tin)
  699.  
  700.   "Return start-position of a cookie in BUFFER.
  701. Args: BUFFER TIN.
  702. The marker that is returned should not be modified in any way,
  703. and is only valid until the contents of the cookie buffer changes."
  704.  
  705.   (cookie-set-buffer buffer
  706.     (cookie-wrapper-start-marker (dll-element cookies tin))))
  707.  
  708.  
  709. (defun tin-end-marker (buffer tin)
  710.  
  711.   "Return end-position of a cookie in BUFFER.
  712. Args: BUFFER TIN.
  713. The marker that is returned should not be modified in any way,
  714. and is only valid until the contents of the cookie buffer changes."
  715.  
  716.   (cookie-set-buffer buffer
  717.     (cookie-wrapper-start-marker
  718.      (dll-element cookies (dll-next cookies tin)))))
  719.  
  720.  
  721.  
  722. (defun cookie-refresh (buffer)
  723.  
  724.   "Refresh all cookies in BUFFER.
  725. Cookie-pretty-printer will be called for all cookies and the new result
  726. displayed.
  727.  
  728. See also tin-invalidate-tins."
  729.  
  730.   (cookie-set-buffer buffer
  731.  
  732.     (let (buffer-read-only)
  733.       (erase-buffer)
  734.  
  735.       (set-marker (cookie-wrapper-start-marker (dll-element cookies cookie-header))
  736.           (point) buffer)
  737.       (insert (cookie-wrapper-cookie (dll-element cookies cookie-header)))
  738.       (insert "\n")
  739.     
  740.       (let ((tin (dll-nth cookies 1)))
  741.     (while (not (eq tin cookie-footer))
  742.  
  743.       (set-marker (cookie-wrapper-start-marker (dll-element cookies tin))
  744.               (point) buffer)
  745.       (insert
  746.        (funcall cookie-pretty-printer
  747.             (cookie-wrapper-cookie (dll-element cookies tin))))
  748.       (insert "\n")
  749.       (setq tin (dll-next cookies tin))))
  750.     
  751.       (set-marker (cookie-wrapper-start-marker (dll-element cookies cookie-footer))
  752.           (point) buffer)
  753.       (insert (cookie-wrapper-cookie (dll-element cookies cookie-footer)))
  754.       (insert "\n"))))
  755.  
  756.  
  757. (defun tin-invalidate-tins (buffer &rest tins)
  758.  
  759.   "Refresh some cookies.
  760. Args: BUFFER &rest TINS."
  761.  
  762.   (cookie-set-buffer buffer
  763.     
  764.     (while tins
  765.       (cookie-refresh-tin (car tins))
  766.       (setq tins (cdr tins)))))
  767.  
  768.  
  769. ;;; Cookie movement commands.
  770.  
  771. (defun cookie-set-goal-column (buffer goal)
  772.   "Set goal-column for BUFFER.
  773. Args: BUFFER GOAL.
  774. goal-column is made buffer-local."
  775.   (cookie-set-buffer buffer
  776.     (make-local-variable 'goal-column)
  777.     (setq goal-column goal)))
  778.  
  779.  
  780. (defun cookie-previous-cookie (buffer pos arg)
  781.   "Move point to the ARGth previous cookie.
  782. Don't move if we are at the first cookie.
  783. ARG is the prefix argument when called interactively.
  784. Args: BUFFER POS ARG.
  785. Sets cookie-last-tin to the cookie we move to."
  786.  
  787.   (interactive (list (current-buffer) (point)
  788.              (prefix-numeric-value current-prefix-arg)))
  789.  
  790.   (cookie-set-buffer buffer
  791.     (setq cookie-last-tin
  792.       (tin-get-selection buffer pos cookie-last-tin))
  793.  
  794.     (while (and cookie-last-tin (> arg 0))
  795.       (setq arg (1- arg))
  796.       (setq cookie-last-tin 
  797.         (dll-previous cookies cookie-last-tin)))
  798.  
  799.     ;; Never step above the first cookie.
  800.  
  801.     (if (null (cookie-filter-hf cookie-last-tin))
  802.     (setq cookie-last-tin (dll-nth cookies 1)))
  803.  
  804.     (goto-char
  805.      (cookie-wrapper-start-marker
  806.       (dll-element cookies cookie-last-tin)))
  807.  
  808.     (if goal-column
  809.     (move-to-column goal-column))))
  810.  
  811.  
  812.  
  813. (defun cookie-next-cookie (buffer pos arg)
  814.   "Move point to the ARGth next cookie.
  815. Don't move if we are at the last cookie.
  816. ARG is the prefix argument when called interactively.
  817. Args: BUFFER POS ARG.
  818. Sets cookie-last-tin to the cookie we move to."
  819.  
  820.   (interactive (list (current-buffer) (point)
  821.              (prefix-numeric-value current-prefix-arg)))
  822.  
  823.   (cookie-set-buffer buffer
  824.     (setq cookie-last-tin
  825.       (tin-get-selection buffer pos cookie-last-tin))
  826.  
  827.     (while (and cookie-last-tin (> arg 0))
  828.       (setq arg (1- arg))
  829.       (setq cookie-last-tin 
  830.         (dll-next cookies cookie-last-tin)))
  831.  
  832.     (if (null (cookie-filter-hf cookie-last-tin))
  833.     (setq cookie-last-tin (dll-nth cookies -2)))
  834.  
  835.     (goto-char
  836.      (cookie-wrapper-start-marker
  837.       (dll-element cookies cookie-last-tin)))
  838.  
  839.     (if goal-column
  840.     (move-to-column goal-column))))
  841.  
  842.  
  843. (defun tin-collect (buffer predicate &rest predicate-args)
  844.  
  845.   "Return a list of all tins in BUFFER whose cookie PREDICATE
  846. returns true for.
  847. PREDICATE is a function that takes a cookie as its argument.
  848. The tins on the returned list will appear in the same order
  849. as in the buffer. You should not rely on in which order PREDICATE
  850. is called. Note that BUFFER is current-buffer when PREDICATE
  851. is called. (If you call cookie-collect with another buffer set
  852. as current-buffer and need to access buffer-local variables
  853. from that buffer within PREDICATE you must send them via
  854. PREDICATE-ARGS).
  855.  
  856. If more than two arguments are given to cookie-collect the remaining
  857. arguments will be passed to PREDICATE.
  858.  
  859. Use cookie-cookie to get the cookie from the tin."
  860.  
  861.   (cookie-set-buffer buffer
  862.     (let ((tin (dll-nth cookies -2))
  863.       result)
  864.  
  865.       (while (not (eq tin cookie-header))
  866.  
  867.     (if (apply predicate
  868.            (cookie-wrapper-cookie (dll-element cookies tin))
  869.            predicate-args)
  870.         (setq result (cons tin result)))
  871.  
  872.     (setq tin (dll-previous cookies tin)))
  873.       result)))
  874.  
  875.  
  876. (defun cookie-collect (buffer predicate &rest predicate-args)
  877.  
  878.   "Return a list of all cookies in BUFFER that PREDICATE
  879. returns true for.
  880. PREDICATE is a function that takes a cookie as its argument.
  881. The cookie on the returned list will appear in the same order
  882. as in the buffer. You should not rely on in which order PREDICATE
  883. is called. Note that BUFFER is current-buffer when PREDICATE
  884. is called. (If you call cookie-collect with another buffer set
  885. as current-buffer and need to access buffer-local variables
  886. from that buffer within PREDICATE you must send them via
  887. PREDICATE-ARGS).
  888.  
  889. If more than two arguments are given to cookie-collect the remaining
  890. arguments will be passed to PREDICATE."
  891.  
  892.   (cookie-set-buffer buffer
  893.     (let ((tin (dll-nth cookies -2))
  894.       result)
  895.  
  896.       (while (not (eq tin cookie-header))
  897.  
  898.     (if (apply predicate
  899.            (cookie-wrapper-cookie (dll-element cookies tin))
  900.            predicate-args)
  901.         (setq result (cons (cookie-wrapper-cookie (dll-element cookies tin))
  902.                    result)))
  903.  
  904.     (setq tin (dll-previous cookies tin)))
  905.       result)))
  906.