summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp/subr-x.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/emacs-lisp/subr-x.el')
-rw-r--r--lisp/emacs-lisp/subr-x.el89
1 files changed, 75 insertions, 14 deletions
diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el
index 2fa0652bc5c..4ce7bd00f31 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -336,10 +336,61 @@ This construct can only be used with lexical binding."
(cl-labels ((,name ,fargs . ,body)) #',name)
. ,aargs)))
+(defvar work-buffer--list nil)
+(defvar work-buffer-limit 10
+ "Maximum number of reusable work buffers.
+When this limit is exceeded, newly allocated work buffers are
+automatically killed, which means that in a such case
+`with-work-buffer' becomes equivalent to `with-temp-buffer'.")
+
+(defsubst work-buffer--get ()
+ "Get a work buffer."
+ (let ((buffer (pop work-buffer--list)))
+ (if (buffer-live-p buffer)
+ buffer
+ (generate-new-buffer " *work*" t))))
+
+(defun work-buffer--release (buffer)
+ "Release work BUFFER."
+ (if (buffer-live-p buffer)
+ (with-current-buffer buffer
+ ;; Flush BUFFER before making it available again, i.e. clear
+ ;; its contents, remove all overlays and buffer-local
+ ;; variables. Is it enough to safely reuse the buffer?
+ (let ((inhibit-read-only t)
+ ;; Avoid deactivating the region as side effect.
+ deactivate-mark)
+ (erase-buffer))
+ (delete-all-overlays)
+ (let (change-major-mode-hook)
+ (kill-all-local-variables t))
+ ;; Make the buffer available again.
+ (push buffer work-buffer--list)))
+ ;; If the maximum number of reusable work buffers is exceeded, kill
+ ;; work buffer in excess, taking into account that the limit could
+ ;; have been let-bound to temporarily increase its value.
+ (when (> (length work-buffer--list) work-buffer-limit)
+ (mapc #'kill-buffer (nthcdr work-buffer-limit work-buffer--list))
+ (setq work-buffer--list (ntake work-buffer-limit work-buffer--list))))
+
;;;###autoload
-(defun string-pixel-width (string)
- "Return the width of STRING in pixels.
+(defmacro with-work-buffer (&rest body)
+ "Create a work buffer, and evaluate BODY there like `progn'.
+Like `with-temp-buffer', but reuse an already created temporary
+buffer when possible, instead of creating a new one on each call."
+ (declare (indent 0) (debug t))
+ (let ((work-buffer (make-symbol "work-buffer")))
+ `(let ((,work-buffer (work-buffer--get)))
+ (with-current-buffer ,work-buffer
+ (unwind-protect
+ (progn ,@body)
+ (work-buffer--release ,work-buffer))))))
+;;;###autoload
+(defun string-pixel-width (string &optional buffer)
+ "Return the width of STRING in pixels.
+If BUFFER is non-nil, use the face remappings from that buffer when
+determining the width.
If you call this function to measure pixel width of a string
with embedded newlines, it returns the width of the widest
substring that does not include newlines."
@@ -348,15 +399,26 @@ substring that does not include newlines."
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)
- (insert (propertize string 'line-prefix nil 'wrap-prefix nil))
+ (with-work-buffer
+ (if buffer
+ (setq-local face-remapping-alist
+ (with-current-buffer buffer
+ face-remapping-alist))
+ (kill-local-variable 'face-remapping-alist))
+ ;; Avoid deactivating the region as side effect.
+ (let (deactivate-mark)
+ (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.
+ (add-text-properties
+ (point-min) (point-max) '(display-line-numbers-disable t))
+ ;; 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))
+ (setq line-prefix nil wrap-prefix nil)
(car (buffer-text-pixel-size nil nil t)))))
;;;###autoload
@@ -418,7 +480,7 @@ this defaults to the current buffer."
(t
disp)))
;; Remove any old instances.
- (when-let ((old (assoc prop disp)))
+ (when-let* ((old (assoc prop disp)))
(setq disp (delete old disp)))
(setq disp (cons (list prop value) disp))
(when vector
@@ -489,8 +551,7 @@ as changes in text properties, `buffer-file-coding-system', buffer
multibyteness, etc. -- will not be noticed, and the buffer will still
be marked unmodified, effectively ignoring those changes."
(declare (debug t) (indent 0))
- (let ((hash (gensym))
- (buffer (gensym)))
+ (cl-with-gensyms (hash buffer)
`(let ((,hash (and (not (buffer-modified-p))
(buffer-hash)))
(,buffer (current-buffer)))