]> code.delx.au - gnu-emacs-elpa/blobdiff - packages/auto-overlays/auto-overlay-self.el
Add auto-overlays package.
[gnu-emacs-elpa] / packages / auto-overlays / auto-overlay-self.el
diff --git a/packages/auto-overlays/auto-overlay-self.el b/packages/auto-overlays/auto-overlay-self.el
new file mode 100644 (file)
index 0000000..b5c61cd
--- /dev/null
@@ -0,0 +1,321 @@
+;;; auto-overlay-self.el --- self-delimited automatic overlays
+
+
+;; Copyright (C) 2005-2015  Free Software Foundation, Inc
+
+;; Author: Toby Cubitt <toby-predictive@dr-qubit.org>
+;; Maintainer: Toby Cubitt <toby-predictive@dr-qubit.org>
+;; URL: http://www.dr-qubit.org/emacs.php
+;; Repository: http://www.dr-qubit.org/git/predictive.git
+
+;; This file is part of the Emacs.
+;;
+;; This file 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 3 of the License, or (at your option)
+;; any later version.
+;;
+;; This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+;;; Code:
+
+(require 'auto-overlays)
+(provide 'auto-overlay-self)
+
+(defvar auto-o-pending-self-cascade nil)
+
+;; set self overlay parsing and suicide functions
+(put 'self 'auto-overlay-parse-function 'auto-o-parse-self-match)
+(put 'self 'auto-overlay-suicide-function 'auto-o-self-suicide)
+
+;; add initialisation and clear functions to hooks
+(add-hook 'auto-overlay-load-hook 'auto-o-self-load)
+(add-hook 'auto-overlay-unload-hook 'auto-o-self-unload)
+
+
+
+(defun auto-o-self-load ()
+  ;; Make sure `auto-o-perform-self-cascades' is in `before-change-functions',
+  ;; so that any cascading that is required is performed before anything else
+  ;; happens.
+  (add-hook 'before-change-functions 'auto-o-perform-self-cascades
+           nil t)
+  ;; initialise variables
+  (setq auto-o-pending-self-cascade nil)
+)
+
+
+(defun auto-o-self-unload ()
+  ;; Remove `auto-o-perform-self-cascades' from `before-change-functions'.
+  (remove-hook 'before-change-functions 'auto-o-perform-self-cascades t)
+)
+
+
+
+
+(defun auto-o-parse-self-match (o-match)
+  ;; perform any necessary updates of auto overlays due to a match for a self
+  ;; regexp
+
+  (let* ((overlay-list (auto-o-self-list o-match))
+        (o (car overlay-list)))
+
+    (cond
+     ;; if stack is empty, create a new end-unmatched overlay, adding it to
+     ;; the list of unascaded overlays (avoids treating it as a special
+     ;; case), and return it
+     ((null overlay-list)
+      (auto-o-make-self o-match nil))
+
+     ;; if new delimiter is inside the first existing overlay and existing one
+     ;; is end-unmatched, just match it
+     ((and (not (overlay-get o 'end))
+          (>= (overlay-get o-match 'delim-start) (overlay-start o)))
+      (auto-o-match-overlay o nil o-match 'no-props)
+      ;; remove it from the list of uncascaded overlays
+      (setq auto-o-pending-self-cascade (delq o auto-o-pending-self-cascade))
+      ;; return nil since haven't created any new overlays
+      nil)
+
+
+     ;; otherwise...
+     (t
+      (let (o-new)
+       ;; if the new match is outside existing overlays...
+       (if (< (overlay-get o-match 'delim-end) (overlay-start o))
+           ;; create overlay from new match till start of next match, and add
+           ;; it to the list of uncascaded overlays
+           (setq o-new (auto-o-make-self
+                        o-match
+                        (overlay-get (overlay-get o 'start) 'delim-start)))
+
+         ;; if the new match is inside an existing overlay...
+         (setq o (pop overlay-list))
+         ;; create overlay from end of existing one till start of the one
+         ;; after (or end of buffer if there isn't one), and add it to the
+         ;; list of uncascaded overlays
+         (setq o-new (auto-o-make-self
+                      (overlay-get o 'end)
+                      (when overlay-list
+                        (overlay-get (overlay-get (car overlay-list) 'start)
+                                     'delim-start))))
+         ;; match end of existing one with the new match, protecting its old
+         ;; end match which is now matched with start of new one
+         (auto-o-match-overlay o nil o-match 'no-props nil 'protect-match))
+
+      ;; return newly created overlay
+      o-new))
+     ))
+)
+
+
+
+
+(defun auto-o-self-suicide (o-self)
+  ;; Called when match no longer matches. Unmatch the match overlay O-SELF, if
+  ;; necessary deleting its parent overlay or cascading.
+
+  (let ((o-parent (overlay-get o-self 'parent)))
+    (cond
+     ;; if parent is end-unmatched, delete it from buffer and from list of
+     ;; uncascaded overlays
+     ((not (auto-o-end-matched-p o-parent))
+      (auto-o-delete-overlay o-parent)
+      (setq auto-o-pending-self-cascade
+           (delq o-parent auto-o-pending-self-cascade)))
+
+     ;; if we match the end of parent...
+     ((eq (overlay-get o-parent 'end) o-self)
+      ;; unmatch ourselves from parent and extend parent till next overlay, or
+      ;; end of buffer if there is none
+      (let ((o (nth 1 (auto-o-self-list o-self))))
+       (auto-o-match-overlay
+        o-parent nil (if o (overlay-get (overlay-get o 'start) 'delim-start)
+                       'unmatched)))
+      ;; add parent to uncascaded overlay list
+      (push o-parent auto-o-pending-self-cascade))
+
+     ;; if we match the start of parent...
+     (t
+      (let* ((o-end (overlay-get o-parent 'end))
+            (o (nth 1 (auto-o-self-list o-end))))
+       ;; unmatch ourselves from parent and "flip"
+       (auto-o-match-overlay
+        o-parent o-end
+        (if o (overlay-get (overlay-get o 'start) 'delim-start)
+          'unmatched)))
+      ;; add parent to uncascaded overlay list
+      (push o-parent auto-o-pending-self-cascade))
+     ))
+)
+
+
+
+
+(defun auto-o-make-self (o-start &optional end)
+  ;; Create a self overlay starting at match overlay O-START.
+  ;; If END is a number or marker, the new overlay is end-unmatched and ends
+  ;; at the buffer location specified by the number or marker.
+  ;; If END is nil, the new overlay is end-unmatched and ends at the end of
+  ;; the buffer.
+  (let (o-new)
+
+    ;; create new overlay (location ensures right things happen when matched)
+    (let (pos)
+      (cond
+       ((overlayp end) (setq pos (overlay-get end 'delim-start)))
+       ((number-or-marker-p end) (setq pos end))
+       (t (setq pos (point-max))))
+      (setq o-new (make-overlay pos pos nil nil 'rear-advance)))
+
+    ;; give overlay some basic properties
+    (overlay-put o-new 'auto-overlay t)
+    (overlay-put o-new 'set-id (overlay-get o-start 'set-id))
+    (overlay-put o-new 'definition-id (overlay-get o-start 'definition-id))
+
+    ;; if overlay is end-unmatched, add it to the list of uncascaded overlays
+    (unless (overlayp end) (push o-new auto-o-pending-self-cascade))
+
+    ;; match the new overlay and return it
+    (auto-o-match-overlay o-new o-start (if (overlayp end) end nil))
+    o-new)
+)
+
+
+
+
+(defun auto-o-perform-self-cascades (beg end)
+  ;; Perform any necessary self-overlay cascading before the text in the
+  ;; buffer is modified. Called from `before-change-functions'.
+
+  ;; check all overlays waiting to be cascaded, from first in buffer to last
+  (dolist (o (sort auto-o-pending-self-cascade
+                  (lambda (a b) (< (overlay-start a) (overlay-start b)))))
+    ;; if buffer modification occurs after the end of an overlay waiting to be
+    ;; cascaded, cascade all overlays between it and the modified text
+    (when (and (overlay-end o) (< (overlay-end o) end))
+      (auto-o-self-cascade (auto-o-self-list (overlay-get o 'start) end))))
+)
+
+
+
+
+(defun auto-o-self-cascade (overlay-list)
+  ;; "Flip" overlays down through buffer (assumes first overlay in list is
+  ;; end-unmatched).
+  (when (> (length overlay-list) 1)
+    (let ((o (car overlay-list))
+         (o1 (nth 1 overlay-list)))
+
+      ;; match first (presumably end-matched) overlay and remove it from list
+      (pop overlay-list)
+      (auto-o-match-overlay o nil (overlay-get o1 'start) 'no-props)
+      ;; remove it from list of uncascaded overlays
+      (setq auto-o-pending-self-cascade (delq o auto-o-pending-self-cascade))
+      ;; if we've hit an end-unmatched overlay, we can stop cascading
+      (if (not (auto-o-end-matched-p o1))
+         (progn
+           (auto-o-delete-overlay o1 nil 'protect-match)
+           (setq auto-o-pending-self-cascade
+                 (delq o1 auto-o-pending-self-cascade)))
+
+       ;; otherwise, cascade overlay list till one is left or we hit an
+       ;; end-unmached overlay
+       (unless
+           (catch 'stop
+             (dotimes (i (1- (length overlay-list)))
+               (setq o (nth i overlay-list))
+               (setq o1 (nth (1+ i) overlay-list))
+               (auto-o-match-overlay o (overlay-get o 'end)
+                                     (overlay-get o1 'start)
+                                     'no-props nil 'protect-match)
+               ;; if we hit an end-unmatched overlay, we can stop cascading
+               (when (not (auto-o-end-matched-p o1))
+                 (throw 'stop (progn
+                                ;; delete the end-unmatched overlay
+                                (auto-o-delete-overlay o1 nil 'protect-match)
+                                ;; remove it from uncascaded overlays list
+                                (setq auto-o-pending-self-cascade
+                                      (delq o1 auto-o-pending-self-cascade))
+                                ;; return t to indicate cascading ended early
+                                t)))))
+
+         ;; if there's an overlay left, "flip" it so it's end-unmatched and
+         ;; extends to next overlay in buffer, and add it to the list of
+         ;; unmatched overlays
+         (let (pos)
+           (setq o (car (last overlay-list)))
+           (if (setq o1 (nth 1 (auto-o-self-list (overlay-get o 'end))))
+               (setq pos (overlay-get (overlay-get o1 'start) 'delim-start))
+             (setq pos (point-max)))
+           (auto-o-match-overlay o (overlay-get o 'end) pos
+                                 'no-props nil 'protect-match))
+         (push o auto-o-pending-self-cascade)))
+      ))
+)
+
+
+
+
+;; (defun auto-o-self-list (o-start &optional end)
+;;   ;; Return list of self overlays ending at or after match overlay O-START and
+;;   ;; starting before or at END, corresponding to same entry as O-START. If END
+;;   ;; is null, all overlays after O-START are included.
+
+;;   (when (null end) (setq end (point-max)))
+
+;;   (let (overlay-list)
+;;     ;; create list of all overlays corresponding to same entry between O-START
+;;     ;; and END
+;;     (mapc (lambda (o) (when (and (>= (overlay-end o)
+;;                                  (overlay-get o-start 'delim-start))
+;;                              (<= (overlay-start o) end))
+;;                     (push o overlay-list)))
+;;       (auto-overlays-in
+;;        (point-min) (point-max)
+;;        (list
+;;         '(identity auto-overlay)
+;;         (list 'eq 'set-id (overlay-get o-start 'set-id))
+;;         (list 'eq 'definition-id (overlay-get o-start 'definition-id)))))
+;;     ;; sort the list by start position, from first to last
+;;     (sort overlay-list
+;;       (lambda (a b) (< (overlay-start a) (overlay-start b)))))
+;; )
+
+
+
+(defun auto-o-self-list (o-start &optional end)
+  ;; Return list of self overlays ending at or after match overlay O-START and
+  ;; starting before or at END, corresponding to same entry as O-START. If END
+  ;; is null, all overlays after O-START are included.
+
+  (when (null end) (setq end (point-max)))
+
+  (let (overlay-list)
+    ;; create list of all overlays corresponding to same entry between O-START
+    ;; and END
+    (setq overlay-list
+         ;; Note: We subtract 1 from start and add 1 to end to catch overlays
+         ;;       that end at start or start at end. This seems to give the
+         ;;       same results as the old version of `auto-o-self-list'
+         ;;       (above) in all circumstances.
+         (auto-overlays-in
+          (1- (overlay-get o-start 'delim-start)) (1+ end)
+          (list
+           '(identity auto-overlay)
+           (list 'eq 'set-id (overlay-get o-start 'set-id))
+           (list 'eq 'definition-id (overlay-get o-start 'definition-id)))))
+    ;; sort the list by start position, from first to last
+    (sort overlay-list
+         (lambda (a b) (< (overlay-start a) (overlay-start b)))))
+)
+
+
+;;; auto-overlay-self.el ends here