;; This file implements spans in terms of extents, for emacs19.
;;
;; Copyright (C) 1998 LFCS Edinburgh
;; Author:	Healfdene Goguen
;; Maintainer:  David Aspinall <David.Aspinall@ed.ac.uk>
;; License:     GPL (GNU GENERAL PUBLIC LICENSE)
;;
;; span-overlay.el,v 1.3 2005/05/17 19:10:39 da Exp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;               Bridging the emacs19/xemacs gulf                   ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; FIXME: NAMESPACE!!!!!!!

(defalias 'span-start 'overlay-start)
(defalias 'span-end 'overlay-end)
(defalias 'set-span-property 'overlay-put)
(defalias 'span-property 'overlay-get)
(defalias 'make-span 'make-overlay)
(defalias 'detach-span 'delete-overlay)
(defalias 'set-span-endpoints 'move-overlay)
(defalias 'span-buffer 'overlay-buffer)

(defun span-read-only-hook (overlay after start end &optional len)
  (unless inhibit-read-only
    (error "Region is read-only")))

(defun span-read-only (span)
  "Set SPAN to be read only."
  ;; This function may be called on spans which are detached from a
  ;; buffer, which gives an error here, since text-properties are
  ;; associated with text in a particular buffer position.  So we use
  ;; our own read only hook.
  ;(add-text-properties (span-start span) (span-end span) '(read-only t)))
  ;; 30.8.02: tested using overlay-put as below with Emacs 21.2.1,
  ;; bit this seems to have no effect when the overlay is added to
  ;; the buffer.  (Maybe read-only is only a text property, not an
  ;; overlay property?).
  ;; (overlay-put span 'read-only t))
  (set-span-property span 'modification-hooks '(span-read-only-hook))
  (set-span-property span 'insert-in-front-hooks '(span-read-only-hook)))

(defun span-read-write (span)
  "Set SPAN to be writeable."
  ;; See comment above for text properties problem.
  (set-span-property span 'modification-hooks nil)
  (set-span-property span 'insert-in-front-hooks nil))

(defun span-give-warning (&rest args)
  "Give a warning message."
  (message "You should not edit here!"))

(defun span-write-warning (span)
  "Give a warning message when SPAN is changed."
  (set-span-property span 'modification-hooks '(span-give-warning))
  (set-span-property span 'insert-in-front-hooks '(span-give-warning)))

;; We use end first because proof-locked-queue is often changed, and
;; its starting point is always 1
(defun span-lt (s u)
  (or (< (span-end s) (span-end u))
      (and (eq (span-end s) (span-end u))
	   (< (span-start s) (span-start u)))))

(defun spans-at-point-prop (pt prop)
  (let ((ols ()))
    (dolist (ol (overlays-at pt))
      (if (or (null prop) (overlay-get ol prop)) (push ol ols)))
    ols))

(defun spans-at-region-prop (start end prop &optional val)
  (let ((ols ()))
    (dolist (ol (overlays-in start end))
      (if (or (null prop)
	      (if val (eq val (overlay-get ol prop)) (overlay-get ol prop)))
	  (push ol ols)))
    ols))

(defun span-at (pt prop)
  "Return the SPAN at point PT with property PROP.
For XEmacs, span-at gives smallest extent at pos.
For Emacs, we assume that spans don't overlap."
  (car (spans-at-point-prop pt prop)))

(defsubst delete-span (span)
  "Delete SPAN."
  (let ((predelfn (span-property span 'span-delete-action)))
    (and predelfn (funcall predelfn)))
  (delete-overlay span))

;; The next two change ordering of list of spans:
(defsubst mapcar-spans (fn start end prop &optional val)
  "Apply function FN to all spans between START and END with property PROP set"
  (mapcar fn (spans-at-region-prop start end prop (or val nil))))

(defun span-at-before (pt prop)
  "Return the smallest SPAN at before PT with property PROP.
A span is before PT if it begins before the character before PT."
  (let ((ols (if (eq (point-min) pt)
		 nil ;; (overlays-at pt)
	       (overlays-in (1- pt) pt))))
    ;; Check the PROP is set.
    (when prop
      (dolist (ol (prog1 ols (setq ols nil)))
	(if (overlay-get ol prop) (push ol ols))))
    ;; Eliminate the case of an empty overlay at (1- pt).
    (dolist (ol (prog1 ols (setq ols nil)))
      (if (>= (overlay-end ol) pt) (push ol ols)))
    ;; "Get the smallest".  I have no idea what that means, so I just do
    ;; something somewhat random but vaguely meaningful.  -Stef
    (car (last (sort ols 'span-lt)))))
  
(defun prev-span (span prop)
  "Return span before SPAN with property PROP."
  (span-at-before (span-start span) prop))

; overlays are [start, end)
 
(defun next-span (span prop)
  "Return span after SPAN with property PROP."
  ;; Presuming the span-extents.el is the reference, its code does the same
  ;; as the code below.
  (span-at (span-end span) prop)
  ;; ;; 3.4 fix here: Now we do a proper search, so this should work with
  ;; ;; nested overlays, after a fashion.  Use overlays-in to get a list
  ;; ;; for the entire buffer, this avoids repeatedly checking the same
  ;; ;; overlays in an ever expanding list (see v6.1).  (However, this
  ;; ;; list may be huge: is it a bottleneck?)
  ;; ;; [Why has this function never used the before-list ?]
  ;; (let* ((start     (overlay-start span))
  ;; 	 ;; (pos       start)
  ;; 	 (nextos    (overlays-in (overlay-end span) 
  ;; 		     (1+ start)
  ;; 		     (point-max)))
  ;; 	 (resstart  (1+ (point-max)))
  ;; 	 spanres)
  ;;   ;; overlays are returned in an unspecified order; we
  ;;   ;; must search whole list for a closest-next one.
  ;;   (dolist (newres nextos spanres)
  ;;     (if (and (span-property newres prop)
  ;; 	       (< start (span-start newres))
  ;; 	       (< (span-start newres) resstart))
  ;; 	  (progn
  ;; 	    (setq spanres newres)
  ;; 	    (setq resstart (span-start spanres))))))
)

(defsubst span-live-p (span)
  "Return non-nil if SPAN is in a live buffer."
  (and span
       (overlay-buffer span)
       (buffer-live-p (overlay-buffer span))))

(defun span-raise (span)
  "Set priority of span to make it appear above other spans.
FIXME: new hack added nov 99 because of disappearing overlays.
Behaviour is still worse than before."	;??? --Stef
  (set-span-property span 'priority 100))

(defalias 'span-object 'overlay-buffer)

(defun span-string (span)
  (with-current-buffer (overlay-buffer span)
    (buffer-substring (overlay-start span) (overlay-end span))))


;Pierre: new utility functions for "holes" 
(defun set-span-properties (span plist)
  "Set SPAN's properties, plist is a plist."
  (let ((pl plist))
    (while pl
      (let* ((name (car pl))
	     (value (car (cdr pl))))
	(overlay-put span name value)
	(setq pl (cdr (cdr pl))))
      )
    )
  )

(defun span-find-span (overlay-list &optional prop)
  "Returns the first overlay of overlay-list having property prop (default 'span), nil if no such overlay belong to the list."
  (let ((l overlay-list))
    (while (and l (not (overlay-get (car l) (or prop 'span))))
      (setq l (cdr l)))
    (car l)))

(defsubst span-at-event (event &optional prop)
  (span-find-span (overlays-at (posn-point (event-start event))) prop)
  )


(defun make-detached-span ()
  (let ((ol (make-overlay 0 0)))
    (delete-overlay ol)
    ol))

(defun fold-spans (f &optional buffer from to maparg ignored-flags prop val)
  (with-current-buffer (or buffer (current-buffer))
    (let ((ols (overlays-in (or from (point-min)) (or to (point-max))))
	  res)
      ;; Check the PROP.
      (when prop
	(dolist (ol (prog1 ols (setq ols nil)))
	  (if (if val (eq val (overlay-get ol prop)) (overlay-get ol prop))
	      (push ol ols))))
      ;; Iterate in order.
      (setq ols (sort ols 'span-lt))
      (while (and ols (not (setq res (funcall f (pop ols) maparg)))))
      res)))


(defsubst span-detached-p (span)
  "is this span detached? nil for no, t for yes"
  (null (overlay-buffer span)))

(defsubst set-span-face (span face)
  "set the face of a span"
  (overlay-put span 'face face))

(defun set-span-keymap (span kmap)
  "set the face of a span"
  ;; In Emacs-20, the `keymap' does not exist, instead we could use the
  ;; `local-map' property, tho it is not ideal.
  (overlay-put span 'keymap kmap))

(provide 'span-overlay)
