diff options
| author | David Ponce | 2025-03-16 11:31:21 +0100 |
|---|---|---|
| committer | Eli Zaretskii | 2025-03-20 14:55:23 +0200 |
| commit | b1db48c0fcd438c903826fe0dba3bc28ffa73cc4 (patch) | |
| tree | fb2acb33d51674853424c30790a5345c6f7bc302 | |
| parent | cace07f27dc31091a606a70ae8b957cd5dd7da43 (diff) | |
| download | emacs-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.el | 25 | ||||
| -rw-r--r-- | test/lisp/misc-tests.el | 64 |
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. |
| 392 | If BUFFER is non-nil, use the face remappings from that buffer when | 392 | If BUFFER is non-nil, use the face remappings, alternative and default |
| 393 | determining the width. | 393 | properties from that buffer when determining the width. |
| 394 | If you call this function to measure pixel width of a string | 394 | If you call this function to measure pixel width of a string |
| 395 | with embedded newlines, it returns the width of the widest | 395 | with embedded newlines, it returns the width of the widest |
| 396 | substring that does not include newlines." | 396 | substring 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 |