aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Ponce2025-03-16 11:31:21 +0100
committerEli Zaretskii2025-03-20 14:55:23 +0200
commitb1db48c0fcd438c903826fe0dba3bc28ffa73cc4 (patch)
treefb2acb33d51674853424c30790a5345c6f7bc302
parentcace07f27dc31091a606a70ae8b957cd5dd7da43 (diff)
downloademacs-b1db48c0fcd438c903826fe0dba3bc28ffa73cc4.tar.gz
emacs-b1db48c0fcd438c903826fe0dba3bc28ffa73cc4.zip
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'.
-rw-r--r--lisp/emacs-lisp/subr-x.el25
-rw-r--r--test/lisp/misc-tests.el64
2 files changed, 76 insertions, 13 deletions
diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el
index 4ce7bd00f31..6414ecab394 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -389,8 +389,8 @@ buffer when possible, instead of creating a new one on each call."
389;;;###autoload 389;;;###autoload
390(defun string-pixel-width (string &optional buffer) 390(defun string-pixel-width (string &optional buffer)
391 "Return the width of STRING in pixels. 391 "Return the width of STRING in pixels.
392If BUFFER is non-nil, use the face remappings from that buffer when 392If BUFFER is non-nil, use the face remappings, alternative and default
393determining the width. 393properties from that buffer when determining the width.
394If you call this function to measure pixel width of a string 394If you call this function to measure pixel width of a string
395with embedded newlines, it returns the width of the widest 395with embedded newlines, it returns the width of the widest
396substring that does not include newlines." 396substring that does not include newlines."
@@ -400,11 +400,14 @@ substring that does not include newlines."
400 ;; Keeping a work buffer around is more efficient than creating a 400 ;; Keeping a work buffer around is more efficient than creating a
401 ;; new temporary buffer. 401 ;; new temporary buffer.
402 (with-work-buffer 402 (with-work-buffer
403 (if buffer 403 ;; Setup current buffer to correctly compute pixel width.
404 (setq-local face-remapping-alist 404 (when buffer
405 (with-current-buffer buffer 405 (dolist (v '(face-remapping-alist
406 face-remapping-alist)) 406 char-property-alias-alist
407 (kill-local-variable 'face-remapping-alist)) 407 default-text-properties))
408 (if (local-variable-p v buffer)
409 (set (make-local-variable v)
410 (buffer-local-value v buffer)))))
408 ;; Avoid deactivating the region as side effect. 411 ;; Avoid deactivating the region as side effect.
409 (let (deactivate-mark) 412 (let (deactivate-mark)
410 (insert string)) 413 (insert string))
@@ -413,12 +416,8 @@ substring that does not include newlines."
413 ;; (bug#59311). Disable `line-prefix' and `wrap-prefix', 416 ;; (bug#59311). Disable `line-prefix' and `wrap-prefix',
414 ;; for the same reason. 417 ;; for the same reason.
415 (add-text-properties 418 (add-text-properties
416 (point-min) (point-max) '(display-line-numbers-disable t)) 419 (point-min) (point-max)
417 ;; Prefer `remove-text-properties' to `propertize' to avoid 420 '(display-line-numbers-disable t line-prefix "" wrap-prefix ""))
418 ;; creating a new string on each call.
419 (remove-text-properties
420 (point-min) (point-max) '(line-prefix nil wrap-prefix nil))
421 (setq line-prefix nil wrap-prefix nil)
422 (car (buffer-text-pixel-size nil nil t))))) 421 (car (buffer-text-pixel-size nil nil t)))))
423 422
424;;;###autoload 423;;;###autoload
diff --git a/test/lisp/misc-tests.el b/test/lisp/misc-tests.el
index 29bf2f02d0c..5b1343148af 100644
--- a/test/lisp/misc-tests.el
+++ b/test/lisp/misc-tests.el
@@ -178,6 +178,70 @@
178 (should (equal (point) (+ 14 vdelta hdelta))) 178 (should (equal (point) (+ 14 vdelta hdelta)))
179 (should (equal (mark) (+ 2 hdelta))))))))) 179 (should (equal (mark) (+ 2 hdelta)))))))))
180 180
181;; Check that `string-pixel-width' returns a consistent result in the
182;; various situations that can lead to erroneous results.
183(ert-deftest misc-test-string-pixel-width-char-property-alias-alist ()
184 "Test `string-pixel-width' with `char-property-alias-alist'."
185 (with-temp-buffer
186 (let ((text0 (propertize "This text"
187 'display "xxxx"
188 'face 'variable-pitch))
189 (text1 (propertize "This text"
190 'my-display "xxxx"
191 'my-face 'variable-pitch)))
192 (setq-local char-property-alias-alist '((display my-display)
193 (face my-face)))
194 (should (= (string-pixel-width text0 (current-buffer))
195 (string-pixel-width text1 (current-buffer)))))))
196
197;; This test never fails in batch mode.
198(ert-deftest misc-test-string-pixel-width-face-remapping-alist ()
199 "Test `string-pixel-width' with `face-remapping-alist'."
200 (with-temp-buffer
201 (setq-local face-remapping-alist '((variable-pitch . default)))
202 (let ((text0 (propertize "This text" 'face 'default))
203 (text1 (propertize "This text" 'face 'variable-pitch)))
204 (should (= (string-pixel-width text0 (current-buffer))
205 (string-pixel-width text1 (current-buffer)))))))
206
207(ert-deftest misc-test-string-pixel-width-default-text-properties ()
208 "Test `string-pixel-width' with `default-text-properties'."
209 (with-temp-buffer
210 (setq-local default-text-properties '(display "XXXX"))
211 (let ((text0 (propertize "This text" 'display "XXXX"))
212 (text1 "This text"))
213 (should (= (string-pixel-width text0 (current-buffer))
214 (string-pixel-width text1 (current-buffer)))))))
215
216(ert-deftest misc-test-string-pixel-width-line-and-wrap-prefix ()
217 "Test `string-pixel-width' with `line-prefix' and `wrap-prefix'."
218 (let ((lp (default-value 'line-prefix))
219 (wp (default-value 'line-prefix))
220 (text (make-string 2000 ?X))
221 w0 w1)
222 (unwind-protect
223 (progn
224 (setq-default line-prefix nil wrap-prefix nil)
225 (setq w0 (string-pixel-width text))
226 (setq-default line-prefix "PPPP" wrap-prefix "WWWW")
227 (setq w1 (string-pixel-width text)))
228 (setq-default line-prefix lp wrap-prefix wp))
229 (should (= w0 w1))))
230
231;; This test never fails in batch mode.
232(ert-deftest misc-test-string-pixel-width-display-line-numbers ()
233 "Test `string-pixel-width' with `display-line-numbers'."
234 (let ((dln (default-value 'display-line-numbers))
235 (text "This text")
236 w0 w1)
237 (unwind-protect
238 (progn
239 (setq-default display-line-numbers nil)
240 (setq w0 (string-pixel-width text))
241 (setq-default display-line-numbers t)
242 (setq w1 (string-pixel-width text)))
243 (setq-default display-line-numbers dln))
244 (should (= w0 w1))))
181 245
182(provide 'misc-tests) 246(provide 'misc-tests)
183;;; misc-tests.el ends here 247;;; misc-tests.el ends here