1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-06 06:20:55 -08:00

Fix `string-pixel-width' with alternate text properties

Fix possible wrong result of `string-pixel-width' with alternate
and default properties.  Create new regression tests.
* lisp/emacs-lisp/subr-x.el (string-pixel-width): Like for
`face-remapping-alist', use in work buffer the value of
`char-property-alias-alist' and `default-text-properties'
local to the passed buffer, to correctly compute pixel width.
(Bug#77042)

* test/lisp/misc-tests.el: Add tests for `string-pixel-width'.
This commit is contained in:
David Ponce 2025-03-16 11:31:21 +01:00 committed by Eli Zaretskii
parent cace07f27d
commit b1db48c0fc
2 changed files with 76 additions and 13 deletions

View file

@ -389,8 +389,8 @@ buffer when possible, instead of creating a new one on each call."
;;;###autoload ;;;###autoload
(defun string-pixel-width (string &optional buffer) (defun string-pixel-width (string &optional buffer)
"Return the width of STRING in pixels. "Return the width of STRING in pixels.
If BUFFER is non-nil, use the face remappings from that buffer when If BUFFER is non-nil, use the face remappings, alternative and default
determining the width. properties from that buffer when determining the width.
If you call this function to measure pixel width of a string If you call this function to measure pixel width of a string
with embedded newlines, it returns the width of the widest with embedded newlines, it returns the width of the widest
substring that does not include newlines." substring that does not include newlines."
@ -400,11 +400,14 @@ substring that does not include newlines."
;; Keeping a work buffer around is more efficient than creating a ;; Keeping a work buffer around is more efficient than creating a
;; new temporary buffer. ;; new temporary buffer.
(with-work-buffer (with-work-buffer
(if buffer ;; Setup current buffer to correctly compute pixel width.
(setq-local face-remapping-alist (when buffer
(with-current-buffer buffer (dolist (v '(face-remapping-alist
face-remapping-alist)) char-property-alias-alist
(kill-local-variable 'face-remapping-alist)) default-text-properties))
(if (local-variable-p v buffer)
(set (make-local-variable v)
(buffer-local-value v buffer)))))
;; Avoid deactivating the region as side effect. ;; Avoid deactivating the region as side effect.
(let (deactivate-mark) (let (deactivate-mark)
(insert string)) (insert string))
@ -413,12 +416,8 @@ substring that does not include newlines."
;; (bug#59311). Disable `line-prefix' and `wrap-prefix', ;; (bug#59311). Disable `line-prefix' and `wrap-prefix',
;; for the same reason. ;; for the same reason.
(add-text-properties (add-text-properties
(point-min) (point-max) '(display-line-numbers-disable t)) (point-min) (point-max)
;; Prefer `remove-text-properties' to `propertize' to avoid '(display-line-numbers-disable t line-prefix "" wrap-prefix ""))
;; creating a new string on each call.
(remove-text-properties
(point-min) (point-max) '(line-prefix nil wrap-prefix nil))
(setq line-prefix nil wrap-prefix nil)
(car (buffer-text-pixel-size nil nil t))))) (car (buffer-text-pixel-size nil nil t)))))
;;;###autoload ;;;###autoload

View file

@ -178,6 +178,70 @@
(should (equal (point) (+ 14 vdelta hdelta))) (should (equal (point) (+ 14 vdelta hdelta)))
(should (equal (mark) (+ 2 hdelta))))))))) (should (equal (mark) (+ 2 hdelta)))))))))
;; Check that `string-pixel-width' returns a consistent result in the
;; various situations that can lead to erroneous results.
(ert-deftest misc-test-string-pixel-width-char-property-alias-alist ()
"Test `string-pixel-width' with `char-property-alias-alist'."
(with-temp-buffer
(let ((text0 (propertize "This text"
'display "xxxx"
'face 'variable-pitch))
(text1 (propertize "This text"
'my-display "xxxx"
'my-face 'variable-pitch)))
(setq-local char-property-alias-alist '((display my-display)
(face my-face)))
(should (= (string-pixel-width text0 (current-buffer))
(string-pixel-width text1 (current-buffer)))))))
;; This test never fails in batch mode.
(ert-deftest misc-test-string-pixel-width-face-remapping-alist ()
"Test `string-pixel-width' with `face-remapping-alist'."
(with-temp-buffer
(setq-local face-remapping-alist '((variable-pitch . default)))
(let ((text0 (propertize "This text" 'face 'default))
(text1 (propertize "This text" 'face 'variable-pitch)))
(should (= (string-pixel-width text0 (current-buffer))
(string-pixel-width text1 (current-buffer)))))))
(ert-deftest misc-test-string-pixel-width-default-text-properties ()
"Test `string-pixel-width' with `default-text-properties'."
(with-temp-buffer
(setq-local default-text-properties '(display "XXXX"))
(let ((text0 (propertize "This text" 'display "XXXX"))
(text1 "This text"))
(should (= (string-pixel-width text0 (current-buffer))
(string-pixel-width text1 (current-buffer)))))))
(ert-deftest misc-test-string-pixel-width-line-and-wrap-prefix ()
"Test `string-pixel-width' with `line-prefix' and `wrap-prefix'."
(let ((lp (default-value 'line-prefix))
(wp (default-value 'line-prefix))
(text (make-string 2000 ?X))
w0 w1)
(unwind-protect
(progn
(setq-default line-prefix nil wrap-prefix nil)
(setq w0 (string-pixel-width text))
(setq-default line-prefix "PPPP" wrap-prefix "WWWW")
(setq w1 (string-pixel-width text)))
(setq-default line-prefix lp wrap-prefix wp))
(should (= w0 w1))))
;; This test never fails in batch mode.
(ert-deftest misc-test-string-pixel-width-display-line-numbers ()
"Test `string-pixel-width' with `display-line-numbers'."
(let ((dln (default-value 'display-line-numbers))
(text "This text")
w0 w1)
(unwind-protect
(progn
(setq-default display-line-numbers nil)
(setq w0 (string-pixel-width text))
(setq-default display-line-numbers t)
(setq w1 (string-pixel-width text)))
(setq-default display-line-numbers dln))
(should (= w0 w1))))
(provide 'misc-tests) (provide 'misc-tests)
;;; misc-tests.el ends here ;;; misc-tests.el ends here