2024-08-17 David Ponce Tweak the implementation of string-pixel-width to run faster and use less memory. * subr-x.el (string--pixel-width-buffer): New variable and function. (string--pixel-width): Use it. Prefer `remove-text-properties' to `propertize' to avoid creating a new string on each call. diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index 058c06bc5f6..1f564fa5628 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -336,6 +336,23 @@ named-let (cl-labels ((,name ,fargs . ,body)) #',name) . ,aargs))) +(defvar string--pixel-width-buffer nil) + +(defsubst string--pixel-width-buffer () + "Get internal buffer used to calculate the pixel width of a string." + ;; Keeping a work buffer around is more efficient than creating a + ;; new temporary buffer on each call to `string-pixel-width'. + (if (buffer-live-p string--pixel-width-buffer) + string--pixel-width-buffer + (with-current-buffer (get-buffer-create " *string-pixel-width*" t) + ;; 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. + ;; Set all these variables only one time here: they + ;; automatically become buffer-local when set. + (setq display-line-numbers nil line-prefix nil wrap-prefix nil) + (setq string--pixel-width-buffer (current-buffer))))) + ;;;###autoload (defun string-pixel-width (string &optional buffer) "Return the width of STRING in pixels. @@ -344,22 +361,18 @@ string-pixel-width (declare (important-return-value t)) (if (zerop (length string)) 0 - ;; Keeping a work buffer around is more efficient than creating a - ;; new temporary buffer. - (with-current-buffer (get-buffer-create " *string-pixel-width*") - ;; If `display-line-numbers' is enabled in internal buffers - ;; (e.g. globally), it breaks width calculation (bug#59311) - (setq-local display-line-numbers nil) - (delete-region (point-min) (point-max)) - ;; Disable line-prefix and wrap-prefix, for the same reason. - (setq line-prefix nil - wrap-prefix nil) + (with-current-buffer (string--pixel-width-buffer) + (erase-buffer) (if buffer (setq-local face-remapping-alist (with-current-buffer buffer face-remapping-alist)) (kill-local-variable 'face-remapping-alist)) - (insert (propertize string 'line-prefix nil 'wrap-prefix nil)) + (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)) (car (buffer-text-pixel-size nil nil t))))) ;;;###autoload