summaryrefslogtreecommitdiff
path: root/kotl/kproperty.el
diff options
context:
space:
mode:
Diffstat (limited to 'kotl/kproperty.el')
-rw-r--r--kotl/kproperty.el143
1 files changed, 138 insertions, 5 deletions
diff --git a/kotl/kproperty.el b/kotl/kproperty.el
index e1bec2d..3734548 100644
--- a/kotl/kproperty.el
+++ b/kotl/kproperty.el
@@ -1,4 +1,4 @@
-;;; kproperty.el --- Wrapper for koutline text property implementations
+;;; kproperty.el --- Wrapper for koutline text property implementations -*- lexical-binding:t -*-
;;
;; Author: Bob Weiner
;;
@@ -12,14 +12,147 @@
;;; Commentary:
;;; Code:
+
;;; ************************************************************************
-;;; Other required Elisp libraries
+;;; Public functions
;;; ************************************************************************
-;; Ensures kotl/ is in load-path.
-(require 'hyperbole)
+;; (get-text-property (pos prop &optional object))
+;; Return the value of position POS's property PROP, in OBJECT.
+;; OBJECT is optional and defaults to the current buffer.
+;; If POSITION is at the end of OBJECT, the value is nil.
+(defalias 'kproperty:get #'get-text-property)
+
+(defun kproperty:map (function property value)
+ "Apply FUNCTION to each character with PROPERTY `eq' to VALUE in the current buffer.
+FUNCTION is called with the start and end points of the text span with the matching PROPERTY
+and with point at the start."
+ (let ((result)
+ (start) end)
+ (save-excursion
+ (if (featurep 'xemacs)
+ (map-extents (lambda (extent _)
+ (when (setq start (extent-start-position extent))
+ (goto-char start)
+ (setq end (extent-end-position extent))
+ (push (funcall function start end) result))
+ nil)
+ nil nil nil nil nil property value)
+ ;; Emacs version.
+ (setq start (point-min))
+ (while (and (< start (point-max))
+ (setq start (text-property-any start (point-max)
+ property value)))
+ (goto-char start)
+ (setq end (or (text-property-not-all start (point-max) property value) (point-max)))
+ (push (funcall function start end) result)
+ (setq start end))))
+ (nreverse result)))
+
+;; (next-single-property-change (pos prop &optional object))
+;; Return the position of next property change for a specific property.
+;; Scans characters forward from POS till it finds
+;; a change in the PROP property, then returns the position of the change.
+;; The optional third argument OBJECT is the string or buffer to scan.
+;; Return nil if the property is constant all the way to the end of OBJECT.
+;; If the value is non-nil, it is a position greater than POS, never equal.
+(defalias 'kproperty:next-single-change #'next-single-property-change)
+
+;; (previous-single-property-change (pos prop &optional object))
+;; Return the position of previous property change for a specific property.
+;; Scans characters backward from POS till it finds
+;; a change in the PROP property, then returns the position of the change.
+;; The optional third argument OBJECT is the string or buffer to scan.
+;; Return nil if the property is constant all the way to the start of OBJECT.
+;; If the value is non-nil, it is a position less than POS, never equal.
+(defalias 'kproperty:previous-single-change #'previous-single-property-change)
+
+(defalias 'kproperty:properties
+ (if (featurep 'xemacs) #'extent-properties-at #'text-properties-at))
+
+(defun kproperty:put (start end property-list &optional object)
+ "From START to END, add PROPERTY-LIST properties to the text.
+The optional fourth argument, OBJECT, is the string or buffer containing the
+text. Text inserted before or after this region does not inherit the added
+properties."
+ (if (not (featurep 'xemacs))
+ ;; Emacs version.
+ (add-text-properties
+ ;; FIXME: Here we force `rear-nonsticky' on all properties, including
+ ;; those not applied via `kproperty:put'!
+ start end (append property-list '(rear-nonsticky t)) object)
+ ;; XEmacs version.
+ ;; Don't use text properties internally because they don't work as desired
+ ;; when copied to a string and then reinserted, at least in some versions
+ ;; of XEmacs.
+ (let ((extent (make-extent start end object)))
+ (if (null extent)
+ (error "(kproperty:put): No extent at %d-%d to add properties %s"
+ start end property-list))
+ (if (/= (mod (length property-list) 2) 0)
+ (error "(kproperty:put): Property-list has odd number of elements, %s"
+ property-list))
+ (set-extent-property extent 'text-prop (car property-list))
+ (set-extent-property extent 'duplicable t)
+ (set-extent-property extent 'start-open t)
+ (set-extent-property extent 'end-open t)
+ (while property-list
+ (set-extent-property
+ extent (car property-list) (car (cdr property-list)))
+ (setq property-list (nthcdr 2 property-list)))
+ extent)))
+
+(defun kproperty:remove (start end property-list &optional object)
+ "From START to END, remove the text properties in PROPERTY-LIST.
+The optional fourth argument, OBJECT, is the string or buffer containing the
+text. PROPERTY-LIST should be a plist; if the value of a property is
+non-nil, then only a property with a matching value will be removed.
+Returns t if any property was changed, nil otherwise."
+ ;; Don't use text property functions internally because they only look for
+ ;; closed extents, which kproperty does not use.
+ (let ((changed) property value)
+ (while property-list
+ (setq property (car property-list)
+ value (car (cdr property-list))
+ property-list (nthcdr 2 property-list))
+ (if (featurep 'xemacs)
+ (map-extents
+ (lambda (extent _)
+ (if (extent-live-p extent)
+ (progn (setq changed t)
+ (delete-extent extent)))
+ nil)
+ object start end nil nil property value)
+ ;; Emacs version.
+ (let ((next start))
+ (while (setq next (text-property-any next end property value object))
+ ;; FIXME: Rather than remove it one-char at a time, we can use
+ ;; next-single-property-change to do it more efficiently!
+ (remove-text-properties next (1+ next) (list property value) object)
+ (setq changed t next (1+ next))))))
+ changed))
+
+(defun kproperty:replace-separator (pos label-separator old-sep-len)
+ "Replace at POS the cell label separator with LABEL-SEPARATOR.
+OLD-SEP-LEN is the length of the separator being replaced."
+ (while (setq pos (kproperty:next-single-change (point) 'kcell))
+ (goto-char pos)
+ (if (featurep 'xemacs)
+ (let ((extent (extent-at pos)))
+ ;; Replace label-separator while maintaining cell properties.
+ (insert label-separator)
+ (set-extent-endpoints extent pos (+ pos 2)))
+ ;; Emacs version
+ (let ((properties (text-properties-at pos)))
+ ;; Replace label-separator while maintaining cell properties.
+ (insert label-separator)
+ (add-text-properties pos (+ pos 2) properties)))
+ (delete-region (point) (+ (point) old-sep-len))))
-(load "kprop-em")
+(defun kproperty:set (property value)
+ "Set PROPERTY of character at point to VALUE."
+ (kproperty:put (point) (min (+ 2 (point)) (point-max))
+ (list property value)))
(provide 'kproperty)