]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix `string-pixel-width' with alternate text properties
authorDavid Ponce <da_vid@orange.fr>
Sun, 16 Mar 2025 10:31:21 +0000 (11:31 +0100)
committerEshel Yaron <me@eshelyaron.com>
Sun, 23 Mar 2025 18:13:53 +0000 (19:13 +0100)
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'.

(cherry picked from commit b1db48c0fcd438c903826fe0dba3bc28ffa73cc4)

lisp/emacs-lisp/subr-x.el
test/lisp/misc-tests.el

index 13f39f396182fc9bddbb74e2c8e5ae895158e995..be6cbf33a97f633af41adc30e0738a3ebf5d3fe0 100644 (file)
@@ -395,22 +395,22 @@ determining the width."
     ;; Keeping a work buffer around is more efficient than creating a
     ;; new temporary buffer.
     (with-work-buffer
+      ;; Setup current buffer to correctly compute pixel width.
+      (when buffer
+        (dolist (v '(face-remapping-alist
+                     char-property-alias-alist
+                     default-text-properties))
+          (if (local-variable-p v buffer)
+              (set (make-local-variable v)
+                   (buffer-local-value v buffer)))))
+      (insert string)
       ;; If `display-line-numbers' is enabled in internal
       ;; buffers (e.g. globally), it breaks width calculation
       ;; (bug#59311).  Disable `line-prefix' and `wrap-prefix',
       ;; for the same reason.
-      (setq display-line-numbers nil
-            line-prefix nil wrap-prefix nil)
-      (if buffer
-          (setq-local face-remapping-alist
-                      (with-current-buffer buffer
-                        face-remapping-alist))
-        (kill-local-variable 'face-remapping-alist))
-      (insert string)
-      ;; Prefer `remove-text-properties' to `propertize' to avoid
-      ;; creating a new string on each call.
-      (remove-text-properties
-       (point-min) (point-max) '(line-prefix nil wrap-prefix nil))
+      (add-text-properties
+       (point-min) (point-max)
+       '(display-line-numbers-disable t line-prefix "" wrap-prefix ""))
       (car (buffer-text-pixel-size nil nil t)))))
 
 ;;;###autoload
index 29bf2f02d0c93a419e81fd6af6aa940afe71be86..5b1343148afc7b3c1712b76b14b54ad6ea02c24d 100644 (file)
             (should (equal (point) (+ 14 vdelta 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)
 ;;; misc-tests.el ends here