Implement new operations ADD-TEXT-PROPERTY and REMOVE-TEXT-PROPERTY.
authorChris Hanson <org/chris-hanson/cph>
Thu, 9 Sep 1993 20:59:33 +0000 (20:59 +0000)
committerChris Hanson <org/chris-hanson/cph>
Thu, 9 Sep 1993 20:59:33 +0000 (20:59 +0000)
Make type and range checking on modification operations more strict.
Add interrupt locking to the modification operations.  Fix some typos.

v7/src/edwin/edwin.pkg
v7/src/edwin/txtprp.scm

index 5356e58d45e8fae70ccd4b279546489a46b6f4e9..8ded1e055aa7b9044eb4c9fbed83a3e551e01b49 100644 (file)
@@ -1,6 +1,6 @@
 #| -*-Scheme-*-
 
-$Id: edwin.pkg,v 1.129 1993/09/03 04:41:30 cph Exp $
+$Id: edwin.pkg,v 1.130 1993/09/09 20:59:33 cph Exp $
 
 Copyright (c) 1989-1993 Massachusetts Institute of Technology
 
@@ -838,6 +838,7 @@ MIT in each case. |#
   (parent (edwin))
   (export (edwin)
          add-text-properties
+         add-text-property
          get-property
          get-property-at
          group-extract-properties
@@ -848,6 +849,7 @@ MIT in each case. |#
          prev-specific-property-change
          previous-property-change
          remove-text-properties
+         remove-text-property
          set-text-properties
          text-not-deleteable?
          text-not-insertable?
index 79fb274b747b584857f40be4c65b51f932f0e5df..b0134da5efaafa09eeb4c0c0a9b85dd0e62d536e 100644 (file)
@@ -1,6 +1,6 @@
 ;;; -*-Scheme-*-
 ;;;
-;;;    $Id: txtprp.scm,v 1.8 1993/08/25 05:49:38 cph Exp $
+;;;    $Id: txtprp.scm,v 1.9 1993/09/09 20:59:26 cph Exp $
 ;;;
 ;;;    Copyright (c) 1993 Massachusetts Institute of Technology
 ;;;
 
 (declare (usual-integrations))
 \f
-(define-structure (interval
-                  (constructor make-interval
-                               (total-length start properties size)))
-  (left false)
-  (right false)
-  (parent false)
-  total-length
-  start
-  properties
-  size)
-
-(define (add-text-properties group start end plist)
+(define (add-text-property group start end key datum)
+  (validate-region-arguments group start end 'ADD-TEXT-PROPERTY)
+  (validate-symbol-argument key 'ADD-TEXT-PROPERTY)
+  (step group start end
+       (lambda (alist)
+         (let ((entry (assq key alist)))
+           (and entry
+                (eq? (cdr entry) datum))))
+       (lambda (alist)
+         (let loop ((alist alist))
+           (cond ((null? alist)
+                  (list (cons key datum)))
+                 ((eq? key (caar alist))
+                  (cons (cons key datum) (cdr alist)))
+                 (else
+                  (cons (car alist) (loop (cdr alist)))))))))
+
+(define (add-text-properties group start end alist)
   (validate-region-arguments group start end 'ADD-TEXT-PROPERTIES)
+  (validate-alist-argument alist 'ADD-TEXT-PROPERTIES)
   (step group start end
-       (lambda (i)
-         (add-properties plist i))
-       (lambda (i)
-         (not (add-properties? plist (interval-properties i))))))
-
-(define (set-text-properties group start end plist)
-  (validate-region-arguments group start end 'SET-TEXT-PROPERTIES)
+       (lambda (alist*)
+         (alist-subset? alist alist*))
+       (lambda (alist*)
+         (append (alist-copy alist)
+                 (list-transform-negative alist*
+                   (lambda (association)
+                     (assq (car association) alist)))))))
+
+(define (remove-text-property group start end key)
+  (validate-region-arguments group start end 'REMOVE-TEXT-PROPERTY)
+  (validate-symbol-argument key 'REMOVE-TEXT-PROPERTY)
   (step group start end
-       (lambda (i)
-         (set-properties plist i))
-       (lambda (i)
-         (not (set-properties? plist (interval-properties i))))))
-
-(define (remove-text-properties group start end plist)
+       (lambda (alist)
+         (not (assq key alist)))
+       (lambda (alist)
+         (let loop ((alist alist))
+           (cond ((null? alist) '())
+                 ((eq? key (caar alist)) (cdr alist))
+                 (else (cons (car alist) (loop (cdr alist)))))))))
+
+(define (remove-text-properties group start end keys)
   (validate-region-arguments group start end 'REMOVE-TEXT-PROPERTIES)
+  (if (not (and (list? keys)
+               (for-all? keys symbol?)))
+      (error:wrong-type-argument keys "list of symbols"
+                                'REMOVE-TEXT-PROPERTIES))
   (step group start end
-       (lambda (i)
-         (remove-properties plist i))
-       (lambda (i)
-         (not (remove-properties? plist (interval-properties i))))))
-
+       (lambda (alist*)
+         (let loop ((keys keys))
+           (or (null? keys)
+               (and (not (assq (car keys) alist*))
+                    (loop (cdr keys))))))
+       (lambda (alist*)
+         (list-transform-negative alist*
+           (lambda (association)
+             (memq (car association) keys))))))
+
+(define (set-text-properties group start end alist)
+  (validate-region-arguments group start end 'SET-TEXT-PROPERTIES)
+  (validate-alist-argument alist 'SET-TEXT-PROPERTIES)
+  (step group start end
+       (lambda (alist*)
+         (alist-same-set? alist alist*))
+       (lambda (alist*)
+         alist*
+         (alist-copy alist))))
+\f
 (define (validate-region-arguments group start end procedure)
   (validate-group group procedure)
   (validate-group-index group start procedure)
   (if (not (and (fix:<= (group-start-index group) index)
                (fix:<= index (group-end-index group))))
       (error:bad-range-argument index procedure)))
-\f
-;;; This also needs to test whether or not the left split is
-;;; necessary.  Maybe rather than a separate test we could grab the
-;;; plist before, let the proc do its magic, and then take action
-;;; afterword.
 
-(define (step group start end proc test)
+(define (validate-alist-argument alist procedure)
+  (if (not (alist? alist))
+      (error:wrong-type-argument alist "alist" procedure))
+  (if (not (let loop ((alist alist))
+            (or (null? alist)
+                (and (symbol? (caar alist))
+                     (not (assq (caar alist) (cdr alist)))
+                     (loop (cdr alist))))))
+      (error:bad-range-argument alist procedure)))
+
+(define (validate-symbol-argument key procedure)
+  (if (not (symbol? key))
+      (error:wrong-type-argument key "symbol" procedure)))
+
+(define (alist-subset? x y)
+  (let loop ((x x))
+    (or (null? x)
+       (let ((entry (assq (caar x) y)))
+         (and entry
+              (eq? (cdar x) (cdr entry))
+              (loop (cdr x)))))))
+
+(define (alist-same-set? x y)
+  ;; Slow but effective.
+  (and (alist-subset? x y)
+       (alist-subset? y x)))
+\f
+(define (step group start end dont-modify? modify-alist)
   (define (loop i lst)
     ;; we now know that we are starting on the begining of an interval
     (let ((next (next-interval i))
          (p (interval-properties i))
          (start (interval-start i)))
-      (let ((end* (if next
-                     (interval-start next)
-                     (fix:+ (interval-start i)
-                            (interval-length i)))))
-       (cond ((fix:= end end*)
-              (if (proc i)
-                  (cons (list start end p) lst)
-                  lst))
-             ((fix:< end end*)
-              (if (proc (split-interval-left i end group))
-                  (cons (list start end lst) lst)
-                  lst))
-             (else
-              (loop next
-                    (if (proc i)
-                        (cons (list start end* p) lst)
-                        lst)))))))
-  (record-property-changes!
-   (let ((i
-         (if (group-text-properties group)
-             (find-interval group start)
-             (create-initial-interval group))))
-     (let ((start* (interval-start i)))
-       (cond ((group-start-changes-index group)
-             (lambda (gsc)
-               (set-group-start-changes-index!
-                group
-                (if (fix:< start gsc) start gsc))
-               (set-group-end-changes-index!
-                group
-                (let ((gec (group-end-changes-index group)))
-                  (if (fix:> end gec) end gec)))))
-            (else
-             (set-group-start-changes-index! group start)
-             (set-group-end-changes-index! group end)))
-       (if (fix:= start start*)
-          (loop i '())
-          (let ((no-split? (test i))
-                (next (next-interval i)))
-            (cond ((and no-split?
-                        (or (not next) (<= end (interval-start next))))
-                   '())
-                  (no-split?
-                   (loop next
-                         '()))
-                  (else
-                   (loop (split-interval-right i start group)
-                         '())))))))
-   group))
-
-(define (record-property-changes! p group)
-  ;; Return false if no changes were actually made.
-  (if p
-      (begin
-       (undo-record-property-changes! group p)
-       (set-group-modified?! group true)
-       (vector-set! group group-index:modified-tick
-                    (fix:+ (group-modified-tick group) 1))
-       true)
-      false))
+      (let ((end* (if next (interval-start next) (interval-end i))))
+       (if (fix:> end end*)
+           (loop next
+                 (if (dont-modify? p)
+                     lst
+                     (begin
+                       (set-interval-properties! i (modify-alist p))
+                       (cons (list start end* p) lst))))
+           (let ((i
+                  (if (fix:< end end*) (split-interval-left i end group) i)))
+             (if (dont-modify? p)
+                 lst
+                 (begin
+                   (set-interval-properties! i (modify-alist p))
+                   (cons (list start end p) lst))))))))
+  (let ((interrupt-mask (set-interrupt-enables! interrupt-mask/gc-ok)))
+    (let ((p
+          (let ((i
+                 (if (group-text-properties group)
+                     (find-interval group start)
+                     (create-initial-interval group))))
+            (let ((start* (interval-start i)))
+              (if (fix:= start start*)
+                  (loop i '())
+                  (let ((dont-split? (dont-modify? (interval-properties i)))
+                        (next (next-interval i)))
+                    (if (and dont-split?
+                             (or (not next)
+                                 (fix:<= end (interval-start next))))
+                        '()
+                        (loop (if dont-split?
+                                  next
+                                  (split-interval-right i start group))
+                              '()))))))))
+      (cond ((group-start-changes-index group)
+            =>
+            (lambda (gsc)
+              (set-group-start-changes-index!
+               group
+               (if (fix:< start gsc) start gsc))
+              (set-group-end-changes-index!
+               group
+               (let ((gec (group-end-changes-index group)))
+                 (if (fix:> end gec) end gec)))))
+           (else
+            (set-group-start-changes-index! group start)
+            (set-group-end-changes-index! group end)))
+      (if (not (null? p))
+         (begin
+           (undo-record-property-changes! group p)
+           (set-group-modified?! group true)
+           (vector-set! group group-index:modified-tick
+                        (fix:+ (group-modified-tick group) 1)))))
+    (set-interrupt-enables! interrupt-mask)))
 \f
 (define (text-properties-at index group)
   (validate-point-arguments group index 'TEXT-PROPERTIES-AT)
   (if (group-text-properties group)
-      (interval-properties (find-interval group index))
-      default-properties))
+      (alist-copy (interval-properties (find-interval group index)))
+      '()))
 
 (define (get-property-at prop index group)
   (validate-point-arguments group index 'GET-PROPERTY-AT)
-  (if (group-text-properties group)
-      (get-property prop (interval-properties (find-interval group index)))
-      #f))
+  (and (group-text-properties group)
+       (assq prop (interval-properties (find-interval group index)))))
 
 (define (local-comtabs mark)
   (let ((property
         (get-property-at 'COMMAND-TABLE (mark-index mark) (mark-group mark))))
     (and property
-        (cadr property))))
+        (cdr property))))
 
 ;;; The next four procedures are all about the same
 ;;; and none have been tested.
         (let ((p1 (interval-properties z)))
           (let loop ((next (next-interval z)))
             (and next
-                 (if (interval-properties-equal? p1
-                                                 (interval-properties next))
+                 (if (alist-same-set? p1 (interval-properties next))
                      (loop (next-interval next))
                      (interval-start next))))))))
 
         (let ((p1 (interval-properties z)))
           (let loop ((prev (previous-interval z)))
             (and prev
-                 (if (interval-properties-equal? p1
-                                                 (interval-properties prev))
+                 (if (alist-same-set? p1 (interval-properties prev))
                      (loop (previous-interval prev))
                      (interval-start prev))))))))
 
                  (if (eq? p (assq prop (interval-properties prev)))
                      (loop (previous-interval prev))
                      (interval-start prev))))))))
-
-(define (interval-properties-equal? p1 p2)
-  ;; Slow but effective.
-  (let ((subset?
-        (lambda (p1 p2)
-          (let loop ((p1 p1))
-            (or (null? p1)
-                (let ((entry (assq (caar p1) p2)))
-                  (and entry
-                       (eq? (cdar p1) (cdr entry))
-                       (loop (cdr p1)))))))))
-    (and (subset? p1 p2)
-        (subset? p2 p1))))
 \f
 ;;; The READ-ONLY property is applied to a contiguous region of
 ;;; characters.  No insertions are allowed within that region, and no
                                       (vector-ref x 2)))
                properties)))
 \f
-;;;; Property Lists
-;;;; these are complicated becase of the desire to recognize
-;;;; unnecessary changes
-(define-integrable default-properties '())
-
-(define (get-property prop plist)
-  (assq prop plist))
-
-(define (remove-property prop plist)
-  (del-assq prop plist))
-
-(define (add-properties? plist plist2)
-  (there-exists? plist
-    (lambda (p1)
-      (let ((p2 (get-property (car p1) plist2)))
-       (not (and p2 (eq? (cdr p1) (cdr p2))))))))
-
-(define (add-properties plist interval)
-  (let ((plist2 (interval-properties interval)))
-    (cond ((add-properties? plist plist2)
-           (set-interval-properties!
-            interval
-            (append plist
-                    (append-map
-                     (lambda (p2)
-                       (if (get-property (car p2) plist)
-                           '()
-                           (list p2)))
-                     plist2)))
-           true)
-         (else false))))
-
-(define (remove-properties? plist plist2)
-  (there-exists? plist
-    (lambda (p1)
-      (get-property (car p1) plist2))))
-
-(define (remove-properties plist interval)
-  (let ((plist2 (interval-properties interval)))
-    (cond ((remove-properties? plist plist2)
-          (set-interval-properties!
-           interval
-           (append-map
-            (lambda (p2)
-              (if (get-property (car p2) plist)
-                  '()
-                  (list p2)))
-            plist2))
-          true)
-         (else false))))
-
-(define (set-properties? plist plist2)
-  (cond ((not (= (length plist)
-                (length plist2)))
-        true)
-       (else
-        (there-exists? plist
-          (lambda (p)
-            (let ((p2 (get-property (car p) plist2)))
-              (if (and p2 (or
-                           (eq? (cdr p2) (cdr p))
-                           (eq? (cadr p2) (cadr p))))
-                  false
-                  true)))))))
-
-(define (set-properties plist interval)
-  (let ((plist2 (interval-properties interval)))
-    (cond ((set-properties? plist plist2)
-          (set-interval-properties!
-           interval
-           plist)
-          true)
-         (else false))))
-\f
+(define-structure (interval
+                  (constructor make-interval
+                               (total-length start properties size)))
+  (left false)
+  (right false)
+  (parent false)
+  total-length
+  start
+  properties
+  size)
+
 (define-integrable (interval-property interval key default)
   (let ((entry (assq key (interval-properties interval))))
     (if entry
 (define-integrable (null-left-child? t)
   (not (interval-left t)))
 
-(define (null-parent? t)
-  (not (interval-parent t)))
-
-(define-integrable (total-length i)
-  (if (not i)
-      0
-      (interval-total-length i)))
-
 (define-integrable (left-total-length t)
   (if (interval-left t)
       (interval-total-length (interval-left t))
   (if child
       (set-interval-parent! child parent)))
 
-(define-integrable (interval-add-amount! i amt)
-  (set-interval-total-length!
-   i
-   (fix:+ (interval-total-length i) amt))
-  amt)
-
 (define (create-initial-interval group)
-  (let ((i (make-interval (group-length group) 0 default-properties 1)))
+  (let ((i (make-interval (group-length group) 0 '() 1)))
     (set-group-text-properties! group i)
     i))
 
        (rightmost-interval r)
        t)))
 \f
-#|
-(define (left-insert interval amt group)
-  (let ((i (make-interval amt false default-properties)))
-    (if (null-left-child? interval)
-       (connect-left! interval i)
-       (begin
-         (connect-left! i (interval-left interval))
-         (connect-left! interval i)
-         (set-interval-total-length!
-          i (fix:+ (left-total-length i) amt))))
-    (add-amount-up-tree interval amt)
-    (balance i group)))
-
-(define (right-insert interval amt group)
-  (let ((i (make-interval amt false default-properties)))
-    (if (null-right-child? interval)
-       (connect-right! interval i)
-       (begin
-         (connect-right! i (interval-right interval))
-         (connect-right! interval i)
-         (set-interval-total-length!
-          i (fix:+ (right-total-length i) amt))))
-    (add-amount-up-tree interval amt)
-    (balance i group)))
-|#
-\f
 ;;;
 ;;;     interval                  interval
 ;;;      /    \       --->         /    \