Update tp-put API and tests to new format

Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-12-13 09:59:27 +00:00
parent 3097efd788
commit 2fce83315b
2 changed files with 88 additions and 75 deletions

View File

@ -38,12 +38,12 @@
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
;; Set a single property ;; Set a single property
(tp-put 1 6 'face 'bold) (tp-put 1 6 '(face bold))
(should (eq (tp-get 1 'face) 'bold)) (should (eq (tp-get 1 'face) 'bold))
(should (eq (tp-get 3 'face) 'bold)) (should (eq (tp-get 3 'face) 'bold))
(should (null (tp-get 7 'face))) (should (null (tp-get 7 'face)))
;; Set multiple properties ;; Set multiple properties
(tp-put 7 12 'face 'italic 'help-echo "test") (tp-put 7 12 '(face italic help-echo "test"))
(should (eq (tp-get 7 'face) 'italic)) (should (eq (tp-get 7 'face) 'italic))
(should (equal (tp-get 7 'help-echo) "test")))) (should (equal (tp-get 7 'help-echo) "test"))))
@ -59,14 +59,14 @@
"Test tp-put returns the modified region." "Test tp-put returns the modified region."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello") (insert "Hello")
(let ((result (tp-put 1 6 'face 'bold))) (let ((result (tp-put 1 6 '(face bold))))
(should (equal result '(1 . 6)))))) (should (equal result '(1 . 6))))))
(ert-deftest tp-test-remove () (ert-deftest tp-test-remove ()
"Test tp-remove removes a specific property." "Test tp-remove removes a specific property."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello") (insert "Hello")
(tp-put 1 6 'face 'bold 'help-echo "test") (tp-put 1 6 '(face bold help-echo "test"))
(should (eq (tp-get 1 'face) 'bold)) (should (eq (tp-get 1 'face) 'bold))
(tp-remove 1 6 'face) (tp-remove 1 6 'face)
(should (null (tp-get 1 'face))) (should (null (tp-get 1 'face)))
@ -76,7 +76,7 @@
"Test tp-remove-list removes multiple properties." "Test tp-remove-list removes multiple properties."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello") (insert "Hello")
(tp-put 1 6 'face 'bold 'help-echo "test" 'mouse-face 'highlight) (tp-put 1 6 '(face bold help-echo "test" mouse-face highlight))
(tp-remove-list 1 6 '(face help-echo)) (tp-remove-list 1 6 '(face help-echo))
(should (null (tp-get 1 'face))) (should (null (tp-get 1 'face)))
(should (null (tp-get 1 'help-echo))) (should (null (tp-get 1 'help-echo)))
@ -86,8 +86,8 @@
"Test tp-clear removes all properties." "Test tp-clear removes all properties."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
(tp-put 1 6 'face 'bold) (tp-put 1 6 '(face bold))
(tp-put 7 12 'face 'italic) (tp-put 7 12 '(face italic))
(tp-clear 1 12) (tp-clear 1 12)
(should (null (tp-get 1 'face))) (should (null (tp-get 1 'face)))
(should (null (tp-get 7 'face))))) (should (null (tp-get 7 'face)))))
@ -96,7 +96,7 @@
"Test tp-clear defaults to entire buffer." "Test tp-clear defaults to entire buffer."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
(tp-put 1 12 'face 'bold) (tp-put 1 12 '(face bold))
(tp-clear) (tp-clear)
(should (null (tp-get 1 'face))) (should (null (tp-get 1 'face)))
(should (null (tp-get 7 'face))))) (should (null (tp-get 7 'face)))))
@ -105,7 +105,7 @@
"Test tp-at returns all properties at point." "Test tp-at returns all properties at point."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello") (insert "Hello")
(tp-put 1 6 'face 'bold 'help-echo "test") (tp-put 1 6 '(face bold help-echo "test"))
(let ((props (tp-at 1))) (let ((props (tp-at 1)))
(should (eq (plist-get props 'face) 'bold)) (should (eq (plist-get props 'face) 'bold))
(should (equal (plist-get props 'help-echo) "test"))))) (should (equal (plist-get props 'help-echo) "test")))))
@ -114,7 +114,7 @@
"Test tp-at defaults to current point." "Test tp-at defaults to current point."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello") (insert "Hello")
(tp-put 1 6 'face 'bold) (tp-put 1 6 '(face bold))
(goto-char 3) (goto-char 3)
(should (eq (plist-get (tp-at) 'face) 'bold)))) (should (eq (plist-get (tp-at) 'face) 'bold))))
@ -123,8 +123,8 @@
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
;; Put both properties on the same overlapping region for proper merging ;; Put both properties on the same overlapping region for proper merging
(tp-put 1 12 'face 'bold) (tp-put 1 12 '(face bold))
(tp-put 1 12 'help-echo "test") (tp-put 1 12 '(help-echo "test"))
(let ((props (tp-plist 1 12))) (let ((props (tp-plist 1 12)))
(should (eq (plist-get props 'face) 'bold)) (should (eq (plist-get props 'face) 'bold))
(should (equal (plist-get props 'help-echo) "test"))))) (should (equal (plist-get props 'help-echo) "test")))))
@ -142,8 +142,8 @@
"Test tp-intervals returns property intervals." "Test tp-intervals returns property intervals."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
(tp-put 1 6 'face 'bold) (tp-put 1 6 '(face bold))
(tp-put 7 12 'face 'italic) (tp-put 7 12 '(face italic))
(let ((intervals (tp-intervals 1 12))) (let ((intervals (tp-intervals 1 12)))
(should (>= (length intervals) 2))))) (should (>= (length intervals) 2)))))
@ -522,7 +522,7 @@
"Test tp-forward finds next property." "Test tp-forward finds next property."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
(tp-put 7 12 'face 'bold) (tp-put 7 12 '(face bold))
(goto-char 1) (goto-char 1)
;; text-property-search-forward may not exist in all Emacs versions ;; text-property-search-forward may not exist in all Emacs versions
(skip-unless (fboundp 'text-property-search-forward)) (skip-unless (fboundp 'text-property-search-forward))
@ -534,7 +534,7 @@
"Test tp-backward finds previous property." "Test tp-backward finds previous property."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
(tp-put 1 6 'face 'bold) (tp-put 1 6 '(face bold))
(goto-char 12) (goto-char 12)
;; text-property-search-backward may not exist in all Emacs versions ;; text-property-search-backward may not exist in all Emacs versions
;; Skip test if function is not available ;; Skip test if function is not available
@ -547,7 +547,7 @@
"Test tp-next returns next position with property." "Test tp-next returns next position with property."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
(tp-put 7 12 'face 'bold) (tp-put 7 12 '(face bold))
(let ((pos (tp-next 1 'face))) (let ((pos (tp-next 1 'face)))
(should (= pos 7))))) (should (= pos 7)))))
@ -555,7 +555,7 @@
"Test tp-prev returns previous position with property." "Test tp-prev returns previous position with property."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
(tp-put 1 6 'face 'bold) (tp-put 1 6 '(face bold))
(let ((pos (tp-prev 12 'face))) (let ((pos (tp-prev 12 'face)))
(should (= pos 1))))) (should (= pos 1)))))
@ -563,7 +563,7 @@
"Test tp-goto-next moves point." "Test tp-goto-next moves point."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
(tp-put 7 12 'face 'bold) (tp-put 7 12 '(face bold))
(goto-char 1) (goto-char 1)
(tp-goto-next 'face) (tp-goto-next 'face)
(should (= (point) 7)))) (should (= (point) 7))))
@ -572,7 +572,7 @@
"Test tp-goto-prev moves point." "Test tp-goto-prev moves point."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
(tp-put 1 6 'face 'bold) (tp-put 1 6 '(face bold))
(goto-char 12) (goto-char 12)
(tp-goto-prev 'face) (tp-goto-prev 'face)
(should (= (point) 1)))) (should (= (point) 1))))
@ -585,8 +585,8 @@
"Test tp-in finds regions with property." "Test tp-in finds regions with property."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World Test") (insert "Hello World Test")
(tp-put 1 6 'my-prop 'value1) (tp-put 1 6 '(my-prop value1))
(tp-put 7 12 'my-prop 'value2) (tp-put 7 12 '(my-prop value2))
(let ((regions (tp-in 'my-prop))) (let ((regions (tp-in 'my-prop)))
(should (= (length regions) 2))))) (should (= (length regions) 2)))))
@ -594,8 +594,8 @@
"Test tp-in filters by value." "Test tp-in filters by value."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World Test") (insert "Hello World Test")
(tp-put 1 6 'my-prop 'value1) (tp-put 1 6 '(my-prop value1))
(tp-put 7 12 'my-prop 'value2) (tp-put 7 12 '(my-prop value2))
(let ((regions (tp-in 'my-prop 'value1))) (let ((regions (tp-in 'my-prop 'value1)))
(should (= (length regions) 1)) (should (= (length regions) 1))
(should (equal (car (car regions)) 1))))) (should (equal (car (car regions)) 1)))))
@ -604,8 +604,8 @@
"Test tp-all returns all regions with properties." "Test tp-all returns all regions with properties."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
(tp-put 1 6 'face 'bold) (tp-put 1 6 '(face bold))
(tp-put 7 12 'face 'italic) (tp-put 7 12 '(face italic))
(let ((regions (tp-all))) (let ((regions (tp-all)))
(should (>= (length regions) 2))))) (should (>= (length regions) 2)))))
@ -613,8 +613,8 @@
"Test tp-regions-map applies function to regions." "Test tp-regions-map applies function to regions."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World Hello") (insert "Hello World Hello")
(tp-put 1 6 'marker t) (tp-put 1 6 '(marker t))
(tp-put 13 18 'marker t) (tp-put 13 18 '(marker t))
(let ((result nil)) (let ((result nil))
(tp-regions-map (tp-regions-map
(lambda (start end idx) (lambda (start end idx)
@ -626,8 +626,8 @@
"Test tp-strings-map applies function to strings." "Test tp-strings-map applies function to strings."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World Hello") (insert "Hello World Hello")
(tp-put 1 6 'marker t) (tp-put 1 6 '(marker t))
(tp-put 13 18 'marker t) (tp-put 13 18 '(marker t))
(let ((result nil)) (let ((result nil))
(tp-strings-map (tp-strings-map
(lambda (str idx) (lambda (str idx)
@ -654,7 +654,7 @@
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
;; Test tp-set alias ;; Test tp-set alias
(insert "Hello") (insert "Hello")
(tp-set 1 6 'face 'bold) (tp-set 1 6 '(face bold))
(should (eq (tp-get 1 'face) 'bold)))) (should (eq (tp-get 1 'face) 'bold))))
;;; ============================================================ ;;; ============================================================
@ -671,8 +671,8 @@
"Test overlapping property regions." "Test overlapping property regions."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "Hello World") (insert "Hello World")
(tp-put 1 8 'prop1 'val1) (tp-put 1 8 '(prop1 val1))
(tp-put 5 12 'prop2 'val2) (tp-put 5 12 '(prop2 val2))
(should (eq (tp-get 1 'prop1) 'val1)) (should (eq (tp-get 1 'prop1) 'val1))
(should (null (tp-get 1 'prop2))) (should (null (tp-get 1 'prop2)))
(should (eq (tp-get 6 'prop1) 'val1)) (should (eq (tp-get 6 'prop1) 'val1))
@ -684,7 +684,7 @@
"Test operations on single character." "Test operations on single character."
(tp-test-with-temp-buffer (tp-test-with-temp-buffer
(insert "H") (insert "H")
(tp-put 1 2 'face 'bold) (tp-put 1 2 '(face bold))
(should (eq (tp-get 1 'face) 'bold)))) (should (eq (tp-get 1 'face) 'bold))))
(ert-deftest tp-test-layer-on-string () (ert-deftest tp-test-layer-on-string ()
@ -700,17 +700,26 @@
(ert-deftest tp-test-put-on-string () (ert-deftest tp-test-put-on-string ()
"Test tp-put works on string objects." "Test tp-put works on string objects."
(let ((str (copy-sequence "Hello World"))) (let ((str (copy-sequence "Hello World")))
(tp-put str 0 5 'face 'bold) (tp-put 0 5 '(face bold) str)
(should (eq (get-text-property 0 'face str) 'bold)) (should (eq (get-text-property 0 'face str) 'bold))
(should (null (get-text-property 6 'face str))))) (should (null (get-text-property 6 'face str)))))
(ert-deftest tp-test-put-on-string-returns-string () (ert-deftest tp-test-put-on-string-returns-string ()
"Test tp-put returns the modified string." "Test tp-put returns the modified string."
(let* ((str (copy-sequence "Hello")) (let* ((str (copy-sequence "Hello"))
(result (tp-put str 0 5 'face 'bold))) (result (tp-put 0 5 '(face bold) str)))
(should (stringp result)) (should (stringp result))
(should (eq (get-text-property 0 'face result) 'bold)))) (should (eq (get-text-property 0 'face result) 'bold))))
(ert-deftest tp-test-put-entire-string ()
"Test tp-put applies to entire string with flat properties."
(let* ((str (copy-sequence "Hello"))
(result (tp-put str 'face 'bold 'help-echo "test")))
(should (stringp result))
(should (eq (get-text-property 0 'face result) 'bold))
(should (equal (get-text-property 0 'help-echo result) "test"))
(should (eq (get-text-property 4 'face result) 'bold))))
(ert-deftest tp-test-match-on-string () (ert-deftest tp-test-match-on-string ()
"Test tp-match works on string objects." "Test tp-match works on string objects."
(let* ((str (copy-sequence "Hello World Hello")) (let* ((str (copy-sequence "Hello World Hello"))

80
tp.el
View File

@ -100,45 +100,49 @@ Appends 'tp-name property to identify the layer."
;;; Basic text property functions (similar to ov.el) ;;; Basic text property functions (similar to ov.el)
(defun tp-put (object-or-start &optional start-or-end end-or-prop &rest properties) (defun tp-put (start-or-string &optional end props-or-prop &rest rest)
"Set text PROPERTIES on OBJECT (string or buffer region). "Set text properties on string or buffer region.
This function supports two calling conventions: This function supports four calling conventions:
1. With OBJECT (string or buffer): 1. Current buffer:
(tp-put OBJECT START END PROPERTY VALUE ...)
(tp-put OBJECT START END \\='(PROPERTY VALUE ...))
2. Without OBJECT (current buffer):
(tp-put START END PROPERTY VALUE ...)
(tp-put START END \\='(PROPERTY VALUE ...)) (tp-put START END \\='(PROPERTY VALUE ...))
2. Specific buffer:
(tp-put START END \\='(PROPERTY VALUE ...) BUFFER)
3. Specific string (0-indexed positions):
(tp-put START END \\='(PROPERTY VALUE ...) STRING)
4. Entire string:
(tp-put STRING PROPERTY VALUE ...)
PROPERTIES is a plist of property-value pairs. PROPERTIES is a plist of property-value pairs.
Return the modified object (string) or region (START . END) for buffer." Return the modified object (string) or region (START . END) for buffer."
(let (object start end props) (let (object start finish props)
;; Determine calling convention based on first argument type ;; Determine calling convention based on first argument type
(cond (cond
;; First arg is a string - use object convention ;; First arg is a string - apply to entire string
((stringp object-or-start) ((stringp start-or-string)
(setq object object-or-start (setq object start-or-string
start start-or-end start 0
end end-or-prop finish (length start-or-string)
props properties)) props (if end
;; First arg is a buffer - use object convention (if props-or-prop
((bufferp object-or-start) (cons end (cons props-or-prop rest))
(setq object object-or-start (list end))
start start-or-end nil)))
end end-or-prop ;; First arg is a number - region convention
props properties)) ((numberp start-or-string)
;; First arg is a number - use buffer region convention (setq start start-or-string
((numberp object-or-start) finish end)
;; Check if 4th arg (first of rest) is a buffer or string
(if (and rest (or (bufferp (car rest)) (stringp (car rest))))
(setq object (car rest)
props props-or-prop)
(setq object nil (setq object nil
start object-or-start props props-or-prop)))
end start-or-end (t (error "Invalid first argument: %S" start-or-string)))
props (if end-or-prop
(cons end-or-prop properties)
properties)))
(t (error "Invalid first argument: %S" object-or-start)))
;; Handle properties as a list ;; Handle properties as a list
(when (listp (car-safe props)) (when (listp (car-safe props))
(setq props (car props))) (setq props (car props)))
@ -146,7 +150,7 @@ Return the modified object (string) or region (START . END) for buffer."
(let ((len (length props)) (let ((len (length props))
(i 0)) (i 0))
(while (< i len) (while (< i len)
(put-text-property start end (put-text-property start finish
(nth i props) (nth i props)
(nth (1+ i) props) (nth (1+ i) props)
object) object)
@ -154,7 +158,7 @@ Return the modified object (string) or region (START . END) for buffer."
;; Return result ;; Return result
(if (stringp object) (if (stringp object)
object object
(cons start end)))) (cons start finish))))
(defalias 'tp-set 'tp-put (defalias 'tp-set 'tp-put
"Alias for `tp-put'.") "Alias for `tp-put'.")
@ -419,7 +423,7 @@ PROPERTIES should be a plist of property-value pairs."
(properties (cddr args))) (properties (cddr args)))
(when (listp (car-safe properties)) (when (listp (car-safe properties))
(setq properties (car properties))) (setq properties (car properties)))
(tp-put object start end properties) (tp-put start end properties object)
object)) ; Always return the object object)) ; Always return the object
(t (error "Invalid arguments to tp-propertize")))) (t (error "Invalid arguments to tp-propertize"))))
@ -452,7 +456,7 @@ Returns the modified object."
(fin (or end (if (stringp object) (fin (or end (if (stringp object)
(length object) (length object)
(with-current-buffer object (point-max)))))) (with-current-buffer object (point-max))))))
(tp-put object beg fin props) (tp-put beg fin props object)
object)) ; Always return the object object)) ; Always return the object
(t (error "Invalid object type: %S" (type-of object))))) (t (error "Invalid object type: %S" (type-of object)))))
(error "Layer %S doesn't exist!" layer))) (error "Layer %S doesn't exist!" layer)))
@ -613,7 +617,7 @@ Returns:
(let ((beg (match-beginning 0)) (let ((beg (match-beginning 0))
(end (match-end 0))) (end (match-end 0)))
(when properties (when properties
(tp-put object beg end properties)) (tp-put beg end properties object))
;; Advance position: for zero-width match, advance by 1 to avoid infinite loop ;; Advance position: for zero-width match, advance by 1 to avoid infinite loop
(setq pos (if (= beg end) (1+ beg) end)))) (setq pos (if (= beg end) (1+ beg) end))))
object)) object))
@ -628,7 +632,7 @@ Returns:
(let ((beg (match-beginning 0)) (let ((beg (match-beginning 0))
(end (match-end 0))) (end (match-end 0)))
(when properties (when properties
(tp-put beg end properties)) (tp-put beg end properties buf))
(push (cons beg end) regions))) (push (cons beg end) regions)))
(nreverse regions))))))))) (nreverse regions)))))))))
@ -677,7 +681,7 @@ Returns:
(let ((beg (match-beginning 0)) (let ((beg (match-beginning 0))
(end (match-end 0))) (end (match-end 0)))
(when properties (when properties
(tp-put object beg end properties)) (tp-put beg end properties object))
;; Advance position: for zero-width match, advance by 1 to avoid infinite loop ;; Advance position: for zero-width match, advance by 1 to avoid infinite loop
(setq pos (if (= beg end) (1+ beg) end)))) (setq pos (if (= beg end) (1+ beg) end))))
object)) object))
@ -692,7 +696,7 @@ Returns:
(let ((beg (match-beginning 0)) (let ((beg (match-beginning 0))
(end (match-end 0))) (end (match-end 0)))
(when properties (when properties
(tp-put beg end properties)) (tp-put beg end properties buf))
(push (cons beg end) regions))) (push (cons beg end) regions)))
(nreverse regions))))))))) (nreverse regions)))))))))