-(defmacro enriched-push (item stack)
- "Push ITEM onto STACK.
-STACK should be a symbol whose value is a list."
- (` (setq (, stack) (cons (, item) (, stack)))))
-
-(defmacro enriched-pop (stack)
- "Remove and return first item on STACK."
- (` (let ((pop-item (car (, stack))))
- (setq (, stack) (cdr (, stack)))
- pop-item)))
-
-(defun enriched-delq1 (cons list)
- "Remove the given CONS from LIST by side effect.
-Since CONS could be the first element of LIST, write
-`(setq foo (enriched-delq1 element foo))' to be sure of changing the value
-of `foo'."
- (if (eq cons list)
- (cdr list)
- (let ((p list))
- (while (not (eq (cdr p) cons))
- (if (null p) (error "enriched-delq1: Attempt to delete a non-element"))
- (setq p (cdr p)))
- ;; Now (cdr p) is the cons to delete
- (setcdr p (cdr cons))
- list)))
-
-(defun enriched-make-list-uniq (list)
- "Destructively remove duplicates from LIST.
-Compares using `eq'."
- (let ((l list))
- (while l
- (setq l (setcdr l (delq (car l) (cdr l)))))
- list))
-
-(defun enriched-make-relatively-unique (a b)
- "Delete common elements of lists A and B, return as pair.
-Compares using `equal'."
- (let* ((acopy (copy-sequence a))
- (bcopy (copy-sequence b))
- (tail acopy))
- (while tail
- (let ((dup (member (car tail) bcopy))
- (next (cdr tail)))
- (if dup (setq acopy (enriched-delq1 tail acopy)
- bcopy (enriched-delq1 dup bcopy)))
- (setq tail next)))
- (cons acopy bcopy)))
-
-(defun enriched-common-tail (a b)
- "Given two lists that have a common tail, return it.
-Compares with `equal', and returns the part of A that is equal to the
-equivalent part of B. If even the last items of the two are not equal,
-returns nil."
- (let ((la (length a))
- (lb (length b)))
- ;; Make sure they are the same length
- (while (> la lb)
- (setq a (cdr a)
- la (1- la)))
- (while (> lb la)
- (setq b (cdr b)
- lb (1- lb))))
- (while (not (equal a b))
- (setq a (cdr a)
- b (cdr b)))
- a)
-
-(defun enriched-which-assoc (items list)
- "Return which one of ITEMS occurs first as a car of an element of LIST."
- (let (res)
- (while list
- (if (setq res (member (car (car list)) items))
- (setq res (car res)
- list nil)
- (setq list (cdr list))))
- res))
-
-(defun enriched-reorder (items order)
- "Arrange ITEMS to following partial ORDER.
-Elements of ITEMS equal to elements of ORDER will be rearranged to follow the
-ORDER. Unmatched items will go last."
- (if order
- (let ((item (member (car order) items)))
- (if item
- (cons (car item)
- (enriched-reorder (enriched-delq1 item items)
- (cdr order)))
- (enriched-reorder items (cdr order))))
- items))
-
-;;;
-;;; Utility functions
-;;;
-
-(defun enriched-get-face-attribute (attr face &optional frame)
- "Get an attribute of a face or list of faces.
-ATTRIBUTE should be one of the functions `face-font' `face-foreground',
-`face-background', or `face-underline-p'. FACE can be a face or a list of
-faces. If optional argument FRAME is given, report on the face in that frame.
-If FRAME is t, report on the defaults for the face in new frames. If FRAME is
-omitted or nil, use the selected frame."
- (cond ((null face) nil)
- ((or (symbolp face) (internal-facep face)) (funcall attr face frame))
- ((funcall attr (car face) frame))
- ((enriched-get-face-attribute attr (cdr face) frame))))
-
-(defun enriched-region-pars ()
- "Return region expanded to begin and end at paragraph breaks.
-If the region is not active, this is just the current paragraph.
-A paragraph does not count as overlapping the region if only whitespace is
-overlapping. Return value is a list of two numers, the beginning and end of
-the defined region."
- (save-excursion
- (let* ((b (progn (if mark-active (goto-char (region-beginning)))
- (enriched-beginning-of-paragraph)))
- (e (progn (if mark-active (progn (goto-char (region-end))
- (skip-chars-backward " \t\n" b)))
- (min (point-max)
- (1+ (enriched-end-of-paragraph))))))
- (list b e))))
-
-(defun enriched-end-of-paragraph ()
- "Move to the end of the current paragraph.
-Only hard newlines delimit paragraphs. Returns point."
- (interactive)
- (if (not (bolp)) (backward-char 1))
- (if (enriched-search-forward-with-props enriched-hard-newline nil 1)
- (backward-char 1))
- (point))
-
-(defun enriched-beginning-of-paragraph ()
- "Move to beginning of the current paragraph.
-Only hard newlines delimit paragraphs. Returns point."
- (interactive)
- (if (not (eolp)) (forward-char 1))
- (if (enriched-search-backward-with-props enriched-hard-newline nil 1)
- (forward-char 1))
- (point))
-
-(defun enriched-overlays-overlapping (begin end &optional test)
- "Return a list of the overlays which overlap the specified region.
-If optional arg TEST is given, it is called with each overlay as its
-argument, and only those for which it is true are returned."
- (overlay-recenter begin)
- (let ((res nil)
- (overlays (cdr (overlay-lists)))) ; includes all ending after BEGIN
- (while overlays
- (if (and (< (overlay-start (car overlays)) end)
- (or (not test)
- (funcall test (car overlays))))
- (enriched-push (car overlays) res))
- (setq overlays (cdr overlays)))
- res))
-
-(defun enriched-show-codes (&rest which)
- "Enable or disable highlighting of special regions.
-With argument null or `none', turns off highlighting.
-If argument is `newline', turns on display of hard newlines.
-If argument is `indent', highlights the automatic indentation at the beginning
-of each line.
-If argument is `margin', highlights all regions with non-standard margins."
- (interactive
- (list (intern (completing-read "Show which codes: "
- '(("none") ("newline") ("indent") ("margin"))
- nil t))))
- (if (null which)
- (setq enriched-show-codes nil)
- (setq enriched-show-codes which))
- ;; First delete current overlays
- (let* ((ol (overlay-lists))
- (overlays (append (car ol) (cdr ol))))
- (while overlays
- (if (eq (overlay-get (car overlays) 'face) 'enriched-code-face)
- (delete-overlay (car overlays)))
- (setq overlays (cdr overlays))))
- ;; Now add new ones for each thing displayed.
- (if (null which)
- (message "Code display off."))
- (while which
- (cond ((eq (car which) 'margin)
- (enriched-show-margin-codes))
- ((eq (car which) 'indent)
- (enriched-map-property-regions 'enriched-indentation
- (lambda (v b e)
- (if v (enriched-show-region-as-code b e 'indent)))))
- ((eq (car which) 'newline)
- (save-excursion
- (goto-char (point-min))
- (while (enriched-search-forward-with-props
- enriched-hard-newline nil t)
- (enriched-show-region-as-code (match-beginning 0) (match-end 0)
- 'newline)))))
- (setq which (cdr which))))
-
-(defun enriched-show-margin-codes (&optional from to)
- "Highlight regions with nonstandard left-margins.
-See `enriched-show-codes'."
- (enriched-map-property-regions 'left-margin
- (lambda (v b e)
- (if (and v (> v 0))
- (enriched-show-region-as-code b e 'margin)))
- from to)
- (enriched-map-property-regions 'right-margin
- (lambda (v b e)
- (if (and v (> v 0))
- (enriched-show-region-as-code b e 'margin)))
- from to))
-
-(defun enriched-show-region-as-code (from to type)
- "Display region between FROM and TO as a code if TYPE is displayed.
-Displays it only if TYPE is an element of `enriched-show-codes' or is t."
- (if (or (eq t type) (memq type enriched-show-codes))
- (let* ((old (enriched-overlays-overlapping
- from to (lambda (o)
- (eq 'enriched-code-face
- (overlay-get o 'face)))))
- (new (if old (move-overlay (car old) from to)
- (make-overlay from to))))
- (overlay-put new 'face 'enriched-code-face)
- (overlay-put new 'front-nogrow t)
- (if (eq type 'margin)
- (overlay-put new 'rear-grow t))
- (while (setq old (cdr old))
- (delete-overlay (car old))))))
-
-(defun enriched-nogrow-hook (beg end old-length)
- "Implement front-nogrow and rear-grow for overlays.
-Normally overlays have opposite inheritance properties than
-text-properties: they will expand to include text inserted at their
-beginning, but not text inserted at their end. However,
-if this function is an element of `after-change-functions', then
-overlays with a non-nil value of the `front-nogrow' property will not
-expand to include text that is inserted just in front of them, and
-overlays with a non-nil value of the `rear-grow' property will
-expand to include text that is inserted just after them."
- (if (not (zerop old-length))
- nil ;; not an insertion
- (let ((overlays (overlays-at end)) o)
- (while overlays
- (setq o (car overlays)
- overlays (cdr overlays))
- (if (and (overlay-get o 'front-nogrow)
- (= beg (overlay-start o)))
- (move-overlay o end (overlay-end o)))))
- (let ((overlays (overlays-at (1- beg))) o)
- (while overlays
- (setq o (car overlays)
- overlays (cdr overlays))
- (if (and (overlay-get o 'rear-grow)
- (= beg (overlay-end o)))
- (move-overlay o (overlay-start o) end))))))
-
-(defun enriched-warn (&rest args)
- "Display a warning message.
-Arguments are given to `format' and the result is displayed in a buffer."
- (save-excursion
- (let ((buf (current-buffer))
- (line (1+ (count-lines 1 (point))))
- (mark (point-marker)))
- (pop-to-buffer (get-buffer-create "*Enriched Warnings*"))
- (goto-char (point-max))
- (insert
-; (format "%s:%d: " (if (boundp 'enriched-file) enriched-file
-; (buffer-file-name buf))
-; line)
- (apply (function format) args)
- "\n")
- (pop-to-buffer buf))))
-
-(defun enriched-looking-at-with-props (string)
- "True if text at point is equal to STRING, including text props.
-This is a literal, not a regexp match.
-The buffer text must include all text properties that STRING has, in
-the same places, but it is allowed to have others that STRING lacks."
- (let ((buffer-string (buffer-substring (point) (+ (point) (length string)))))
- (and (string-equal string buffer-string)
- (enriched-text-properties-include string buffer-string))))
-
-(defun enriched-search-forward-with-props
- (string &optional bound noerror count)
- "Search forward for STRING, including its text properties.
-Set point to end of occurrence found, and return point.
-The match found must include all text properties that STRING has, in
-the same places, but it is allowed to have others that STRING lacks.
-An optional second argument bounds the search; it is a buffer position.
-The match found must not extend after that position. nil is equivalent
- to (point-max).
-Optional third argument, if t, means if fail just return nil (no error).
- If not nil and not t, move to limit of search and return nil.
-Optional fourth argument is repeat count--search for successive occurrences.
-See also the functions `match-beginning', `match-end' and `replace-match'."
- (interactive "sSearch for: ")
- (or bound (setq bound (point-max)))
- (or count (setq count 1))
- (let ((start (point))
- (res t))
- (while (and res (> count 0))
- (while (and (setq res (search-forward string bound t))
- (not (enriched-text-properties-include
- string (buffer-substring (match-beginning 0)
- (match-end 0))))))
- (setq count (1- count)))
- (cond (res)
- ((eq noerror t) (goto-char start) nil)
- (noerror (goto-char bound) nil)
- (t (goto-char start)
- (error "Search failed: %s" string)))))
-
-(defun enriched-search-backward-with-props
- (string &optional bound noerror count)
- "Search backward for STRING, including its text properties.
-Set point to the beginning of occurrence found, and return point.
-The match found must include all text properties that STRING has, in
-the same places, but it is allowed to have others that STRING lacks.
-An optional second argument bounds the search; it is a buffer position.
-The match found must not start before that position. nil is equivalent
- to (point-min).
-Optional third argument, if t, means if fail just return nil (no error).
- If not nil and not t, move to limit of search and return nil.
-Optional fourth argument is repeat count--search for successive occurrences.
-See also the functions `match-beginning', `match-end' and `replace-match'."
- (interactive "sSearch for: ")
- (or bound (setq bound (point-min)))
- (or count (setq count 1))
- (let ((start (point))
- (res t))
- (while (and res (> count 0))
- (while (and (setq res (search-backward string bound t))
- (not (enriched-text-properties-include
- string (buffer-substring (match-beginning 0)
- (match-end 0))))))
- (setq count (1- count)))
- (cond (res)
- ((eq noerror t) (goto-char start) nil)
- (noerror (goto-char bound) nil)
- (t (goto-char start)
- (error "Search failed: %s" string)))))
-
-(defun enriched-text-properties-include (a b)
- "True if all of A's text-properties are also properties of B.
-They must match in property name, value, and position. B must be at least as
-long as A, but comparison is done only up to the length of A."
- (let ((loc (length a)))
- (catch 'fail
- (while (>= loc 0)
- (let ((plist (text-properties-at loc a)))
- (while plist
- (if (not (equal (car (cdr plist))
- (get-text-property loc (car plist) b)))
- (throw 'fail nil))
- (setq plist (cdr (cdr plist)))))
- (setq loc (1- loc)))
- t)))
-