>From d2b2648efa51343a3b7dd84ada89110e84aa8658 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" Date: Tue, 1 Dec 2020 01:12:32 +0000 Subject: [PATCH] Inhibit buffer hooks in temporary buffers Give get-buffer-create an optional argument to inhibit buffer hooks in internal or temporary buffers for efficiency (bug#34765). * .dir-locals.el (c-mode): Enforce existing indent-tabs-mode policy. * etc/NEWS: Announce new parameter of get-buffer-create and generate-new-buffer, and that with-temp-buffer and with-temp-file now inhibit buffer hooks. * doc/lispref/buffers.texi (Buffer Names): Fix typo. (Creating Buffers): Document new parameter of get-buffer-create and generate-new-buffer. (Buffer List, Killing Buffers): Document when buffer hooks are inhibited. (Current Buffer): * doc/lispref/files.texi (Writing to Files): Document that with-temp-buffer and with-temp-file inhibit buffer hooks. * doc/lispref/internals.texi (Buffer Internals): Document inhibit_buffer_hooks flag. Remove stale comment. * doc/misc/gnus-faq.texi (FAQ 5-8): * lisp/simple.el (shell-command-on-region): Fix indentation. * lisp/files.el (kill-buffer-hook): Document when hook is inhibited. (create-file-buffer): * lisp/gnus/gnus-uu.el (gnus-uu-unshar-article): * lisp/international/mule.el (load-with-code-conversion): * lisp/mh-e/mh-xface.el (mh-x-image-url-fetch-image): * lisp/net/imap.el (imap-open): * lisp/net/mailcap.el (mailcap-maybe-eval): * lisp/progmodes/flymake-proc.el (flymake-proc--read-file-to-temp-buffer) (flymake-proc--copy-buffer-to-temp-buffer): Simplify. * lisp/subr.el (generate-new-buffer): Forward new optional argument to inhibit buffer hooks to get-buffer-create. (with-temp-file, with-temp-buffer, with-output-to-string): * lisp/json.el (json-encode-string): Inhibit buffer hooks in buffer used. * src/buffer.c (run_buffer_list_update_hook): New helper function. (Fget_buffer_create): Use it. Add optional argument to set inhibit_buffer_hooks flag instead of comparing the buffer name to Vcode_conversion_workbuf_name. All callers changed. (Fmake_indirect_buffer, Frename_buffer, Fbury_buffer_internal) (record_buffer): Use run_buffer_list_update_hook. (Fkill_buffer): Document when buffer hooks are inhibited. Use run_buffer_list_update_hook. (init_buffer_once): Inhibit buffer hooks in Vprin1_to_string_buffer. (Vkill_buffer_query_functions, Vbuffer_list_update_hook): Document when hooks are inhibited. * src/buffer.h (struct buffer): Update inhibit_buffer_hooks commentary. * src/coding.h (Vcode_conversion_workbuf_name): * src/coding.c (Vcode_conversion_workbuf_name): Make static again since it is no longer needed in src/buffer.c. (code_conversion_restore, code_conversion_save, syms_of_coding): Prefer boolean over integer constants. * src/fileio.c (Finsert_file_contents): Inhibit buffer hooks in " *code-converting-work*" buffer. * src/window.c (Fselect_window): Fix grammar. Mention window-selection-change-functions alongside buffer-list-update-hook. * test/src/buffer-tests.el: Fix requires. (buffer-tests-inhibit-buffer-hooks): New test. --- .dir-locals.el | 1 + doc/lispref/buffers.texi | 55 +++++++++++++++------ doc/lispref/files.texi | 7 ++- doc/lispref/internals.texi | 11 ++++- doc/misc/gnus-faq.texi | 8 ++-- etc/NEWS | 16 +++++++ lisp/files.el | 6 ++- lisp/gnus/gnus-uu.el | 3 +- lisp/international/mule.el | 9 ++-- lisp/json.el | 2 +- lisp/mh-e/mh-xface.el | 3 +- lisp/net/imap.el | 3 +- lisp/net/mailcap.el | 3 +- lisp/progmodes/flymake-proc.el | 13 +++-- lisp/simple.el | 3 +- lisp/subr.el | 17 ++++--- src/buffer.c | 87 +++++++++++++++++++--------------- src/buffer.h | 10 ++-- src/callproc.c | 5 +- src/coding.c | 12 ++--- src/coding.h | 3 -- src/fileio.c | 2 +- src/minibuf.c | 2 +- src/print.c | 2 +- src/process.c | 12 ++--- src/w32fns.c | 2 +- src/window.c | 7 +-- src/xdisp.c | 4 +- src/xfns.c | 2 +- src/xwidget.c | 3 +- test/src/buffer-tests.el | 33 +++++++++++-- 31 files changed, 217 insertions(+), 129 deletions(-) diff --git a/.dir-locals.el b/.dir-locals.el index 27d50c6069..b313945936 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -9,6 +9,7 @@ (c-noise-macro-names . ("INLINE" "ATTRIBUTE_NO_SANITIZE_UNDEFINED" "UNINIT" "CALLBACK" "ALIGN_STACK")) (electric-quote-comment . nil) (electric-quote-string . nil) + (indent-tabs-mode . t) (mode . bug-reference-prog))) (objc-mode . ((c-file-style . "GNU") (electric-quote-comment . nil) diff --git a/doc/lispref/buffers.texi b/doc/lispref/buffers.texi index 2860343628..c8c4852e30 100644 --- a/doc/lispref/buffers.texi +++ b/doc/lispref/buffers.texi @@ -225,13 +225,18 @@ Current Buffer @defmac with-temp-buffer body@dots{} @anchor{Definition of with-temp-buffer} -The @code{with-temp-buffer} macro evaluates the @var{body} forms -with a temporary buffer as the current buffer. It saves the identity of +The @code{with-temp-buffer} macro evaluates the @var{body} forms with +a temporary buffer as the current buffer. It saves the identity of the current buffer, creates a temporary buffer and makes it current, evaluates the @var{body} forms, and finally restores the previous -current buffer while killing the temporary buffer. By default, undo -information (@pxref{Undo}) is not recorded in the buffer created by -this macro (but @var{body} can enable that, if needed). +current buffer while killing the temporary buffer. + +By default, undo information (@pxref{Undo}) is not recorded in the +buffer created by this macro (but @var{body} can enable that, if +needed). The temporary buffer also does not run the hooks +@code{kill-buffer-hook}, @code{kill-buffer-query-functions} +(@pxref{Killing Buffers}), and @code{buffer-list-update-hook} +(@pxref{Buffer List}). The return value is the value of the last form in @var{body}. You can return the contents of the temporary buffer by using @@ -345,9 +350,9 @@ Buffer Names If the optional second argument @var{ignore} is non-@code{nil}, it should be a string, a potential buffer name. It means to consider -that potential buffer acceptable, if it is tried, even it is the name -of an existing buffer (which would normally be rejected). Thus, if -buffers named @samp{foo}, @samp{foo<2>}, @samp{foo<3>} and +that potential buffer acceptable, if it is tried, even if it is the +name of an existing buffer (which would normally be rejected). Thus, +if buffers named @samp{foo}, @samp{foo<2>}, @samp{foo<3>} and @samp{foo<4>} exist, @example @@ -932,13 +937,17 @@ Buffer List @defvar buffer-list-update-hook This is a normal hook run whenever the buffer list changes. Functions (implicitly) running this hook are @code{get-buffer-create} -(@pxref{Creating Buffers}), @code{rename-buffer} (@pxref{Buffer Names}), -@code{kill-buffer} (@pxref{Killing Buffers}), @code{bury-buffer} (see -above) and @code{select-window} (@pxref{Selecting Windows}). +(@pxref{Creating Buffers}), @code{rename-buffer} (@pxref{Buffer +Names}), @code{kill-buffer} (@pxref{Killing Buffers}), +@code{bury-buffer} (see above), and @code{select-window} +(@pxref{Selecting Windows}). This hook is not run for internal or +temporary buffers created by @code{get-buffer-create} or +@code{generate-new-buffer} with a non-@code{nil} argument +@var{inhibit-buffer-hooks}. Functions run by this hook should avoid calling @code{select-window} -with a nil @var{norecord} argument or @code{with-temp-buffer} since -either may lead to infinite recursion. +with a @code{nil} @var{norecord} argument since this may lead to +infinite recursion. @end defvar @node Creating Buffers @@ -951,12 +960,20 @@ Creating Buffers with the specified name; @code{generate-new-buffer} always creates a new buffer and gives it a unique name. + Both functions accept an optional argument @var{inhibit-buffer-hooks}. +If it is non-@code{nil}, the buffer they create does not run the hooks +@code{kill-buffer-hook}, @code{kill-buffer-query-functions} +(@pxref{Killing Buffers}), and @code{buffer-list-update-hook} +(@pxref{Buffer List}). This avoids slowing down internal or temporary +buffers that are never presented to users or passed on to other +applications. + Other functions you can use to create buffers include @code{with-output-to-temp-buffer} (@pxref{Temporary Displays}) and @code{create-file-buffer} (@pxref{Visiting Files}). Starting a subprocess can also create a buffer (@pxref{Processes}). -@defun get-buffer-create buffer-or-name +@defun get-buffer-create buffer-or-name &optional inhibit-buffer-hooks This function returns a buffer named @var{buffer-or-name}. The buffer returned does not become the current buffer---this function does not change which buffer is current. @@ -980,7 +997,7 @@ Creating Buffers buffer initially disables undo information recording (@pxref{Undo}). @end defun -@defun generate-new-buffer name +@defun generate-new-buffer name &optional inhibit-buffer-hooks This function returns a newly created, empty buffer, but does not make it current. The name of the buffer is generated by passing @var{name} to the function @code{generate-new-buffer-name} (@pxref{Buffer @@ -1092,6 +1109,10 @@ Killing Buffers they are called. The idea of this feature is that these functions will ask for confirmation from the user. If any of them returns @code{nil}, @code{kill-buffer} spares the buffer's life. + +This hook is not run for internal or temporary buffers created by +@code{get-buffer-create} or @code{generate-new-buffer} with a +non-@code{nil} argument @var{inhibit-buffer-hooks}. @end defvar @defvar kill-buffer-hook @@ -1100,6 +1121,10 @@ Killing Buffers The buffer to be killed is current when the hook functions run. @xref{Hooks}. This variable is a permanent local, so its local binding is not cleared by changing major modes. + +This hook is not run for internal or temporary buffers created by +@code{get-buffer-create} or @code{generate-new-buffer} with a +non-@code{nil} argument @var{inhibit-buffer-hooks}. @end defvar @defopt buffer-offer-save diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index d49ac42bb4..6949ca29c6 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -701,8 +701,11 @@ Writing to Files The current buffer is restored even in case of an abnormal exit via @code{throw} or error (@pxref{Nonlocal Exits}). -See also @code{with-temp-buffer} in @ref{Definition of -with-temp-buffer,, The Current Buffer}. +Like @code{with-temp-buffer} (@pxref{Definition of with-temp-buffer,, +Current Buffer}), the temporary buffer used by this macro does not run +the hooks @code{kill-buffer-hook}, @code{kill-buffer-query-functions} +(@pxref{Killing Buffers}), and @code{buffer-list-update-hook} +(@pxref{Buffer List}). @end defmac @node File Locks diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index 28a5fdb349..757368bc98 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -2391,6 +2391,15 @@ Buffer Internals This flag indicates that redisplay optimizations should not be used to display this buffer. +@item inhibit_buffer_hooks +This flag indicates that the buffer should not run the hooks +@code{kill-buffer-hook}, @code{kill-buffer-query-functions} +(@pxref{Killing Buffers}), and @code{buffer-list-update-hook} +(@pxref{Buffer List}). It is set at buffer creation (@pxref{Creating +Buffers}), and avoids slowing down internal or temporary buffers, such +as those created by @code{with-temp-buffer} (@pxref{Definition of +with-temp-buffer,, Current Buffer}). + @item overlay_center This field holds the current overlay center position. @xref{Managing Overlays}. @@ -2404,8 +2413,6 @@ Buffer Internals and @code{overlays_after} is sorted in order of increasing beginning position. -@c FIXME? the following are now all Lisp_Object BUFFER_INTERNAL_FIELD (foo). - @item name A Lisp string that names the buffer. It is guaranteed to be unique. @xref{Buffer Names}. This and the following fields have their names diff --git a/doc/misc/gnus-faq.texi b/doc/misc/gnus-faq.texi index adb812f572..c30e80ff56 100644 --- a/doc/misc/gnus-faq.texi +++ b/doc/misc/gnus-faq.texi @@ -1523,10 +1523,10 @@ FAQ 5-8 @example (setq message-default-headers - (with-temp-buffer - (insert "X-Face: ") - (insert-file-contents "~/.xface") - (buffer-string))) + (with-temp-buffer + (insert "X-Face: ") + (insert-file-contents "~/.xface") + (buffer-string))) @end example @noindent diff --git a/etc/NEWS b/etc/NEWS index 87463372d5..217b0b76a1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1817,6 +1817,13 @@ modifies the string's text properties; instead, it uses and returns a copy of the string. This helps avoid trouble when strings are shared or constants. ++++ +** Temporary buffers no longer run certain buffer hooks. +The macros 'with-temp-buffer' and 'with-temp-file' no longer run the +hooks 'kill-buffer-hook', 'kill-buffer-query-functions', and +'buffer-list-update-hook' for the temporary buffers they create. This +avoids slowing them down when a lot of these hooks are defined. + --- ** The obsolete function 'thread-alive-p' has been removed. @@ -2173,6 +2180,15 @@ Until it is solved you could ignore such errors by performing ** The error 'ftp-error' belongs also to category 'remote-file-error'. ++++ +** Buffers can now be created with certain hooks disabled. +The functions 'get-buffer-create' and 'generate-new-buffer' accept a +new optional argument 'inhibit-buffer-hooks'. If non-nil, the new +buffer does not run the hooks 'kill-buffer-hook', +'kill-buffer-query-functions', and 'buffer-list-update-hook'. This +avoids slowing down internal or temporary buffers that are never +presented to users or passed on to other applications. + * Changes in Emacs 28.1 on Non-Free Operating Systems diff --git a/lisp/files.el b/lisp/files.el index 093b5f92e5..70d451cccf 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -1850,6 +1850,10 @@ kill-buffer-hook The buffer being killed is current while the hook is running. See `kill-buffer'. +This hook is not run for internal or temporary buffers created by +`get-buffer-create' or `generate-new-buffer' with argument +INHIBIT-BUFFER-HOOKS non-nil. + Note: Be careful with let-binding this hook considering it is frequently used for cleanup.") @@ -1951,7 +1955,7 @@ create-file-buffer (let ((lastname (file-name-nondirectory filename))) (if (string= lastname "") (setq lastname filename)) - (generate-new-buffer (if (string-match-p "\\` " lastname) + (generate-new-buffer (if (string-prefix-p " " lastname) (concat "|" lastname) lastname)))) diff --git a/lisp/gnus/gnus-uu.el b/lisp/gnus/gnus-uu.el index 70aeac00d7..00ca430cc5 100644 --- a/lisp/gnus/gnus-uu.el +++ b/lisp/gnus/gnus-uu.el @@ -1587,8 +1587,7 @@ gnus-uu-unshar-article (save-excursion (switch-to-buffer (current-buffer)) (delete-other-windows) - (let ((buffer (get-buffer-create (generate-new-buffer-name - "*Warning*")))) + (let ((buffer (generate-new-buffer "*Warning*"))) (unless (unwind-protect (with-current-buffer buffer diff --git a/lisp/international/mule.el b/lisp/international/mule.el index 212e7232b4..6571454dff 100644 --- a/lisp/international/mule.el +++ b/lisp/international/mule.el @@ -307,12 +307,9 @@ load-with-code-conversion (and (null noerror) (signal 'file-error (list "Cannot open load file" file))) ;; Read file with code conversion, and then eval. - (let* ((buffer - ;; We can't use `generate-new-buffer' because files.el - ;; is not yet loaded. - (get-buffer-create (generate-new-buffer-name " *load*"))) - (load-in-progress t) - (source (save-match-data (string-match "\\.el\\'" fullname)))) + (let ((buffer (generate-new-buffer " *load*")) + (load-in-progress t) + (source (string-suffix-p ".el" fullname))) (unless nomessage (if source (message "Loading %s (source)..." file) diff --git a/lisp/json.el b/lisp/json.el index c2fc1574fa..5f512b94cd 100644 --- a/lisp/json.el +++ b/lisp/json.el @@ -435,7 +435,7 @@ json-encode-string (concat "\"" (substring-no-properties string) "\"") (with-current-buffer (or json--string-buffer - (with-current-buffer (generate-new-buffer " *json-string*") + (with-current-buffer (generate-new-buffer " *json-string*" t) ;; This seems to afford decent performance gains. (setq-local inhibit-modification-hooks t) (setq json--string-buffer (current-buffer)))) diff --git a/lisp/mh-e/mh-xface.el b/lisp/mh-e/mh-xface.el index 909f1fe95d..65039310e7 100644 --- a/lisp/mh-e/mh-xface.el +++ b/lisp/mh-e/mh-xface.el @@ -425,8 +425,7 @@ mh-x-image-url-fetch-image be displayed in a buffer and position specified by MARKER. The actual display is carried out by the SENTINEL function." (if mh-wget-executable - (let ((buffer (get-buffer-create (generate-new-buffer-name - mh-temp-fetch-buffer))) + (let ((buffer (generate-new-buffer mh-temp-fetch-buffer)) (filename (or (mh-funcall-if-exists make-temp-file "mhe-fetch") (expand-file-name (make-temp-name "~/mhe-fetch"))))) (with-current-buffer buffer diff --git a/lisp/net/imap.el b/lisp/net/imap.el index 0394f0efea..50b08d9612 100644 --- a/lisp/net/imap.el +++ b/lisp/net/imap.el @@ -1033,8 +1033,7 @@ imap-open (when (funcall (nth 1 (assq stream imap-stream-alist)) buffer) ;; Stream changed? (if (not (eq imap-default-stream stream)) - (with-current-buffer (get-buffer-create - (generate-new-buffer-name " *temp*")) + (with-current-buffer (generate-new-buffer " *temp*") (mapc 'make-local-variable imap-local-variables) (set-buffer-multibyte nil) (buffer-disable-undo) diff --git a/lisp/net/mailcap.el b/lisp/net/mailcap.el index d0f8c1272d..bc99f02fe3 100644 --- a/lisp/net/mailcap.el +++ b/lisp/net/mailcap.el @@ -386,8 +386,7 @@ mailcap-maybe-eval (when (save-window-excursion (delete-other-windows) - (let ((buffer (get-buffer-create (generate-new-buffer-name - "*Warning*")))) + (let ((buffer (generate-new-buffer "*Warning*"))) (unwind-protect (with-current-buffer buffer (insert (substitute-command-keys diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 152dc725c7..e9d5018f30 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el @@ -429,16 +429,15 @@ flymake-proc--replace-region (defun flymake-proc--read-file-to-temp-buffer (file-name) "Insert contents of FILE-NAME into newly created temp buffer." - (let* ((temp-buffer (get-buffer-create (generate-new-buffer-name (concat "flymake:" (file-name-nondirectory file-name)))))) - (with-current-buffer temp-buffer - (insert-file-contents file-name)) - temp-buffer)) + (with-current-buffer (generate-new-buffer + (concat "flymake:" (file-name-nondirectory file-name))) + (insert-file-contents file-name) + (current-buffer))) (defun flymake-proc--copy-buffer-to-temp-buffer (buffer) "Copy contents of BUFFER into newly created temp buffer." - (with-current-buffer - (get-buffer-create (generate-new-buffer-name - (concat "flymake:" (buffer-name buffer)))) + (with-current-buffer (generate-new-buffer + (concat "flymake:" (buffer-name buffer))) (insert-buffer-substring buffer) (current-buffer))) diff --git a/lisp/simple.el b/lisp/simple.el index 090162b973..aab650e98a 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -4306,8 +4306,7 @@ shell-command-on-region (defun shell-command-to-string (command) "Execute shell command COMMAND and return its output as a string." (with-output-to-string - (with-current-buffer - standard-output + (with-current-buffer standard-output (shell-command command t)))) (defun process-file (program &optional infile buffer display &rest args) diff --git a/lisp/subr.el b/lisp/subr.el index 77c19c5bbf..6d5d7b4bac 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3701,10 +3701,11 @@ internal--after-with-selected-window (when (window-live-p (nth 1 state)) (select-window (nth 1 state) 'norecord))) -(defun generate-new-buffer (name) +(defun generate-new-buffer (name &optional inhibit-buffer-hooks) "Create and return a buffer with a name based on NAME. -Choose the buffer's name using `generate-new-buffer-name'." - (get-buffer-create (generate-new-buffer-name name))) +Choose the buffer's name using `generate-new-buffer-name'. +See `get-buffer-create' for the meaning of INHIBIT-BUFFER-HOOKS." + (get-buffer-create (generate-new-buffer-name name) inhibit-buffer-hooks)) (defmacro with-selected-window (window &rest body) "Execute the forms in BODY with WINDOW as the selected window. @@ -3866,12 +3867,14 @@ with-output-to-temp-buffer (defmacro with-temp-file (file &rest body) "Create a new buffer, evaluate BODY there, and write the buffer to FILE. The value returned is the value of the last form in BODY. +The buffer does not run the hooks `kill-buffer-hook', +`kill-buffer-query-functions', and `buffer-list-update-hook'. See also `with-temp-buffer'." (declare (indent 1) (debug t)) (let ((temp-file (make-symbol "temp-file")) (temp-buffer (make-symbol "temp-buffer"))) `(let ((,temp-file ,file) - (,temp-buffer (generate-new-buffer " *temp file*"))) + (,temp-buffer (generate-new-buffer " *temp file*" t))) (unwind-protect (prog1 (with-current-buffer ,temp-buffer @@ -3906,10 +3909,12 @@ with-temp-message (defmacro with-temp-buffer (&rest body) "Create a temporary buffer, and evaluate BODY there like `progn'. +The buffer does not run the hooks `kill-buffer-hook', +`kill-buffer-query-functions', and `buffer-list-update-hook'. See also `with-temp-file' and `with-output-to-string'." (declare (indent 0) (debug t)) (let ((temp-buffer (make-symbol "temp-buffer"))) - `(let ((,temp-buffer (generate-new-buffer " *temp*"))) + `(let ((,temp-buffer (generate-new-buffer " *temp*" t))) ;; `kill-buffer' can change current-buffer in some odd cases. (with-current-buffer ,temp-buffer (unwind-protect @@ -3944,7 +3949,7 @@ with-silent-modifications (defmacro with-output-to-string (&rest body) "Execute BODY, return the text it sent to `standard-output', as a string." (declare (indent 0) (debug t)) - `(let ((standard-output (generate-new-buffer " *string-output*"))) + `(let ((standard-output (generate-new-buffer " *string-output*" t))) (unwind-protect (progn (let ((standard-output standard-output)) diff --git a/src/buffer.c b/src/buffer.c index dfc34faf6e..9564f66096 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -37,7 +37,6 @@ Copyright (C) 1985-1989, 1993-1995, 1997-2020 Free Software Foundation, #include "window.h" #include "commands.h" #include "character.h" -#include "coding.h" #include "buffer.h" #include "region-cache.h" #include "indent.h" @@ -514,16 +513,32 @@ get_truename_buffer (register Lisp_Object filename) return Qnil; } -DEFUN ("get-buffer-create", Fget_buffer_create, Sget_buffer_create, 1, 1, 0, +/* Run 'buffer-list-update-hook' if Vrun_hooks is non-nil, and BUF is + either NULL or does not have buffer hooks inhibited. */ + +static void +run_buffer_list_update_hook (struct buffer *buf) +{ + if (! (NILP (Vrun_hooks) || (buf && buf->inhibit_buffer_hooks))) + call1 (Vrun_hooks, Qbuffer_list_update_hook); +} + +DEFUN ("get-buffer-create", Fget_buffer_create, Sget_buffer_create, 1, 2, 0, doc: /* Return the buffer specified by BUFFER-OR-NAME, creating a new one if needed. If BUFFER-OR-NAME is a string and a live buffer with that name exists, return that buffer. If no such buffer exists, create a new buffer with -that name and return it. If BUFFER-OR-NAME starts with a space, the new -buffer does not keep undo information. +that name and return it. + +If BUFFER-OR-NAME starts with a space, the new buffer does not keep undo +information. If optional argument INHIBIT-BUFFER-HOOKS is non-nil, the +new buffer does not run the hooks `kill-buffer-hook', +`kill-buffer-query-functions', and `buffer-list-update-hook'. This +avoids slowing down internal or temporary buffers that are never +presented to users or passed on to other applications. If BUFFER-OR-NAME is a buffer instead of a string, return it as given, even if it is dead. The return value is never nil. */) - (register Lisp_Object buffer_or_name) + (register Lisp_Object buffer_or_name, Lisp_Object inhibit_buffer_hooks) { register Lisp_Object buffer, name; register struct buffer *b; @@ -598,11 +613,7 @@ DEFUN ("get-buffer-create", Fget_buffer_create, Sget_buffer_create, 1, 1, 0, set_string_intervals (name, NULL); bset_name (b, name); - b->inhibit_buffer_hooks - = (STRINGP (Vcode_conversion_workbuf_name) - && strncmp (SSDATA (name), SSDATA (Vcode_conversion_workbuf_name), - SBYTES (Vcode_conversion_workbuf_name)) == 0); - + b->inhibit_buffer_hooks = !NILP (inhibit_buffer_hooks); bset_undo_list (b, SREF (name, 0) != ' ' ? Qnil : Qt); reset_buffer (b); @@ -614,9 +625,8 @@ DEFUN ("get-buffer-create", Fget_buffer_create, Sget_buffer_create, 1, 1, 0, /* Put this in the alist of all live buffers. */ XSETBUFFER (buffer, b); Vbuffer_alist = nconc2 (Vbuffer_alist, list1 (Fcons (name, buffer))); - /* And run buffer-list-update-hook. */ - if (!NILP (Vrun_hooks) && !b->inhibit_buffer_hooks) - call1 (Vrun_hooks, Qbuffer_list_update_hook); + + run_buffer_list_update_hook (b); return buffer; } @@ -890,9 +900,7 @@ DEFUN ("make-indirect-buffer", Fmake_indirect_buffer, Smake_indirect_buffer, set_buffer_internal_1 (old_b); } - /* Run buffer-list-update-hook. */ - if (!NILP (Vrun_hooks)) - call1 (Vrun_hooks, Qbuffer_list_update_hook); + run_buffer_list_update_hook (NULL); return buf; } @@ -1536,9 +1544,7 @@ DEFUN ("rename-buffer", Frename_buffer, Srename_buffer, 1, 2, && !NILP (BVAR (current_buffer, auto_save_file_name))) call0 (intern ("rename-auto-save-file")); - /* Run buffer-list-update-hook. */ - if (!NILP (Vrun_hooks) && !current_buffer->inhibit_buffer_hooks) - call1 (Vrun_hooks, Qbuffer_list_update_hook); + run_buffer_list_update_hook (current_buffer); /* Refetch since that last call may have done GC. */ return BVAR (current_buffer, name); @@ -1612,7 +1618,7 @@ DEFUN ("other-buffer", Fother_buffer, Sother_buffer, 0, 3, 0, buf = Fget_buffer (scratch); if (NILP (buf)) { - buf = Fget_buffer_create (scratch); + buf = Fget_buffer_create (scratch, Qnil); Fset_buffer_major_mode (buf); } return buf; @@ -1636,7 +1642,7 @@ other_buffer_safely (Lisp_Object buffer) buf = Fget_buffer (scratch); if (NILP (buf)) { - buf = Fget_buffer_create (scratch); + buf = Fget_buffer_create (scratch, Qnil); Fset_buffer_major_mode (buf); } @@ -1713,7 +1719,9 @@ DEFUN ("kill-buffer", Fkill_buffer, Skill_buffer, 0, 1, "bKill buffer: ", the buffer is not killed. The hook `kill-buffer-hook' is run before the buffer is actually killed. The buffer being killed will be current while the hook is running. Functions called by any of these hooks are -supposed to not change the current buffer. +supposed to not change the current buffer. Neither hook is run for +internal or temporary buffers created by `get-buffer-create' or +`generate-new-buffer' with argument INHIBIT-BUFFER-HOOKS non-nil. Any processes that have this buffer as the `process-buffer' are killed with SIGHUP. This function calls `replace-buffer-in-windows' for @@ -1973,9 +1981,7 @@ DEFUN ("kill-buffer", Fkill_buffer, Skill_buffer, 0, 1, "bKill buffer: ", bset_width_table (b, Qnil); unblock_input (); - /* Run buffer-list-update-hook. */ - if (!NILP (Vrun_hooks) && !b->inhibit_buffer_hooks) - call1 (Vrun_hooks, Qbuffer_list_update_hook); + run_buffer_list_update_hook (b); return Qt; } @@ -2015,9 +2021,7 @@ record_buffer (Lisp_Object buffer) fset_buffer_list (f, Fcons (buffer, Fdelq (buffer, f->buffer_list))); fset_buried_buffer_list (f, Fdelq (buffer, f->buried_buffer_list)); - /* Run buffer-list-update-hook. */ - if (!NILP (Vrun_hooks) && !XBUFFER (buffer)->inhibit_buffer_hooks) - call1 (Vrun_hooks, Qbuffer_list_update_hook); + run_buffer_list_update_hook (XBUFFER (buffer)); } @@ -2054,9 +2058,7 @@ DEFUN ("bury-buffer-internal", Fbury_buffer_internal, Sbury_buffer_internal, fset_buried_buffer_list (f, Fcons (buffer, Fdelq (buffer, f->buried_buffer_list))); - /* Run buffer-list-update-hook. */ - if (!NILP (Vrun_hooks) && !XBUFFER (buffer)->inhibit_buffer_hooks) - call1 (Vrun_hooks, Qbuffer_list_update_hook); + run_buffer_list_update_hook (XBUFFER (buffer)); return Qnil; } @@ -5349,10 +5351,11 @@ init_buffer_once (void) Fput (Qkill_buffer_hook, Qpermanent_local, Qt); /* Super-magic invisible buffer. */ - Vprin1_to_string_buffer = Fget_buffer_create (build_pure_c_string (" prin1")); + Vprin1_to_string_buffer = + Fget_buffer_create (build_pure_c_string (" prin1"), Qt); Vbuffer_alist = Qnil; - Fset_buffer (Fget_buffer_create (build_pure_c_string ("*scratch*"))); + Fset_buffer (Fget_buffer_create (build_pure_c_string ("*scratch*"), Qnil)); inhibit_modification_hooks = 0; } @@ -5397,7 +5400,7 @@ init_buffer (void) #endif /* USE_MMAP_FOR_BUFFERS */ AUTO_STRING (scratch, "*scratch*"); - Fset_buffer (Fget_buffer_create (scratch)); + Fset_buffer (Fget_buffer_create (scratch, Qnil)); if (NILP (BVAR (&buffer_defaults, enable_multibyte_characters))) Fset_buffer_multibyte (Qnil); @@ -6300,9 +6303,14 @@ from (abs POSITION). If POSITION is positive, point was at the front DEFVAR_LISP ("kill-buffer-query-functions", Vkill_buffer_query_functions, doc: /* List of functions called with no args to query before killing a buffer. The buffer being killed will be current while the functions are running. +See `kill-buffer'. If any of them returns nil, the buffer is not killed. Functions run by -this hook are supposed to not change the current buffer. */); +this hook are supposed to not change the current buffer. + +This hook is not run for internal or temporary buffers created by +`get-buffer-create' or `generate-new-buffer' with argument +INHIBIT-BUFFER-HOOKS non-nil. */); Vkill_buffer_query_functions = Qnil; DEFVAR_LISP ("change-major-mode-hook", Vchange_major_mode_hook, @@ -6315,9 +6323,12 @@ from (abs POSITION). If POSITION is positive, point was at the front doc: /* Hook run when the buffer list changes. Functions (implicitly) running this hook are `get-buffer-create', `make-indirect-buffer', `rename-buffer', `kill-buffer', `bury-buffer' -and `select-window'. Functions run by this hook should avoid calling -`select-window' with a nil NORECORD argument or `with-temp-buffer' -since either may lead to infinite recursion. */); +and `select-window'. This hook is not run for internal or temporary +buffers created by `get-buffer-create' or `generate-new-buffer' with +argument INHIBIT-BUFFER-HOOKS non-nil. + +Functions run by this hook should avoid calling `select-window' with a +nil NORECORD argument since it may lead to infinite recursion. */); Vbuffer_list_update_hook = Qnil; DEFSYM (Qbuffer_list_update_hook, "buffer-list-update-hook"); diff --git a/src/buffer.h b/src/buffer.h index fe549c5dac..b8c5162be4 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -663,11 +663,11 @@ #define BVAR(buf, field) ((buf)->field ## _) /* Non-zero whenever the narrowing is changed in this buffer. */ bool_bf clip_changed : 1; - /* Non-zero for internally used temporary buffers that don't need to - run hooks kill-buffer-hook, buffer-list-update-hook, and - kill-buffer-query-functions. This is used in coding.c to avoid - slowing down en/decoding when there are a lot of these hooks - defined. */ + /* Non-zero for internal or temporary buffers that don't need to + run hooks kill-buffer-hook, kill-buffer-query-functions, and + buffer-list-update-hook. This is used in coding.c to avoid + slowing down en/decoding when a lot of these hooks are + defined, as well as by with-temp-buffer, for example. */ bool_bf inhibit_buffer_hooks : 1; /* List of overlays that end at or before the current center, diff --git a/src/callproc.c b/src/callproc.c index e3346e2eab..4bca1e5ebd 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -405,9 +405,8 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, if (! (NILP (buffer) || EQ (buffer, Qt) || FIXNUMP (buffer))) { - Lisp_Object spec_buffer; - spec_buffer = buffer; - buffer = Fget_buffer_create (buffer); + Lisp_Object spec_buffer = buffer; + buffer = Fget_buffer_create (buffer, Qnil); /* Mention the buffer name for a better error message. */ if (NILP (buffer)) CHECK_BUFFER (spec_buffer); diff --git a/src/coding.c b/src/coding.c index 2142e7fa51..1afa4aa474 100644 --- a/src/coding.c +++ b/src/coding.c @@ -7821,7 +7821,7 @@ encode_coding (struct coding_system *coding) /* A string that serves as name of the reusable work buffer, and as base name of temporary work buffers used for code-conversion operations. */ -Lisp_Object Vcode_conversion_workbuf_name; +static Lisp_Object Vcode_conversion_workbuf_name; /* The reusable working buffer, created once and never killed. */ static Lisp_Object Vcode_conversion_reused_workbuf; @@ -7839,7 +7839,7 @@ code_conversion_restore (Lisp_Object arg) if (! NILP (workbuf)) { if (EQ (workbuf, Vcode_conversion_reused_workbuf)) - reused_workbuf_in_use = 0; + reused_workbuf_in_use = false; else Fkill_buffer (workbuf); } @@ -7857,13 +7857,13 @@ code_conversion_save (bool with_work_buf, bool multibyte) { Lisp_Object name = Fgenerate_new_buffer_name (Vcode_conversion_workbuf_name, Qnil); - workbuf = Fget_buffer_create (name); + workbuf = Fget_buffer_create (name, Qt); } else { if (NILP (Fbuffer_live_p (Vcode_conversion_reused_workbuf))) Vcode_conversion_reused_workbuf - = Fget_buffer_create (Vcode_conversion_workbuf_name); + = Fget_buffer_create (Vcode_conversion_workbuf_name, Qt); workbuf = Vcode_conversion_reused_workbuf; } } @@ -7881,7 +7881,7 @@ code_conversion_save (bool with_work_buf, bool multibyte) bset_undo_list (current_buffer, Qt); bset_enable_multibyte_characters (current_buffer, multibyte ? Qt : Qnil); if (EQ (workbuf, Vcode_conversion_reused_workbuf)) - reused_workbuf_in_use = 1; + reused_workbuf_in_use = true; set_buffer_internal (current); } @@ -11639,7 +11639,7 @@ syms_of_coding (void) staticpro (&Vcode_conversion_workbuf_name); Vcode_conversion_workbuf_name = build_pure_c_string (" *code-conversion-work*"); - reused_workbuf_in_use = 0; + reused_workbuf_in_use = false; PDUMPER_REMEMBER_SCALAR (reused_workbuf_in_use); DEFSYM (Qcharset, "charset"); diff --git a/src/coding.h b/src/coding.h index 4973cf89eb..9ad1e954f8 100644 --- a/src/coding.h +++ b/src/coding.h @@ -97,9 +97,6 @@ #define EMACS_CODING_H extern Lisp_Object Vcoding_system_hash_table; -/* Name (or base name) of work buffer for code conversion. */ -extern Lisp_Object Vcode_conversion_workbuf_name; - /* Enumeration of index to an attribute vector of a coding system. */ enum coding_attr_index diff --git a/src/fileio.c b/src/fileio.c index c97f4daf20..51f12e104e 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4004,7 +4004,7 @@ because (1) it preserves some marker positions (in unchanged portions record_unwind_current_buffer (); - workbuf = Fget_buffer_create (name); + workbuf = Fget_buffer_create (name, Qt); buf = XBUFFER (workbuf); delete_all_overlays (buf); diff --git a/src/minibuf.c b/src/minibuf.c index fc3fd92a88..1940564a80 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -809,7 +809,7 @@ get_minibuffer (EMACS_INT depth) static char const name_fmt[] = " *Minibuf-%"pI"d*"; char name[sizeof name_fmt + INT_STRLEN_BOUND (EMACS_INT)]; AUTO_STRING_WITH_LEN (lname, name, sprintf (name, name_fmt, depth)); - buf = Fget_buffer_create (lname); + buf = Fget_buffer_create (lname, Qnil); /* Although the buffer's name starts with a space, undo should be enabled in it. */ diff --git a/src/print.c b/src/print.c index 008bf5e639..ec271d914c 100644 --- a/src/print.c +++ b/src/print.c @@ -562,7 +562,7 @@ temp_output_buffer_setup (const char *bufname) record_unwind_current_buffer (); - Fset_buffer (Fget_buffer_create (build_string (bufname))); + Fset_buffer (Fget_buffer_create (build_string (bufname), Qnil)); Fkill_all_local_variables (); delete_all_overlays (current_buffer); diff --git a/src/process.c b/src/process.c index 4fe8ac7fc0..9efefb1de7 100644 --- a/src/process.c +++ b/src/process.c @@ -1731,7 +1731,7 @@ DEFUN ("make-process", Fmake_process, Smake_process, 0, MANY, 0, buffer = Fplist_get (contact, QCbuffer); if (!NILP (buffer)) - buffer = Fget_buffer_create (buffer); + buffer = Fget_buffer_create (buffer, Qnil); /* Make sure that the child will be able to chdir to the current buffer's current directory, or its unhandled equivalent. We @@ -1768,7 +1768,7 @@ DEFUN ("make-process", Fmake_process, Smake_process, 0, MANY, 0, QCname, concat2 (name, build_string (" stderr")), QCbuffer, - Fget_buffer_create (xstderr), + Fget_buffer_create (xstderr, Qnil), QCnoquery, query_on_exit ? Qnil : Qt); } @@ -2443,7 +2443,7 @@ DEFUN ("make-pipe-process", Fmake_pipe_process, Smake_pipe_process, buffer = Fplist_get (contact, QCbuffer); if (NILP (buffer)) buffer = name; - buffer = Fget_buffer_create (buffer); + buffer = Fget_buffer_create (buffer, Qnil); pset_buffer (p, buffer); pset_childp (p, contact); @@ -3173,7 +3173,7 @@ DEFUN ("make-serial-process", Fmake_serial_process, Smake_serial_process, buffer = Fplist_get (contact, QCbuffer); if (NILP (buffer)) buffer = name; - buffer = Fget_buffer_create (buffer); + buffer = Fget_buffer_create (buffer, Qnil); pset_buffer (p, buffer); pset_childp (p, contact); @@ -4188,7 +4188,7 @@ DEFUN ("make-network-process", Fmake_network_process, Smake_network_process, open_socket: if (!NILP (buffer)) - buffer = Fget_buffer_create (buffer); + buffer = Fget_buffer_create (buffer, Qnil); /* Unwind bind_polling_period. */ unbind_to (count, Qnil); @@ -4961,7 +4961,7 @@ server_accept_connection (Lisp_Object server, int channel) if (!NILP (buffer)) { args[1] = buffer; - buffer = Fget_buffer_create (Fformat (nargs, args)); + buffer = Fget_buffer_create (Fformat (nargs, args), Qnil); } } diff --git a/src/w32fns.c b/src/w32fns.c index a840f0e122..36bee0676b 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -7372,7 +7372,7 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, tip_f = XFRAME (tip_frame); window = FRAME_ROOT_WINDOW (tip_f); - tip_buf = Fget_buffer_create (tip); + tip_buf = Fget_buffer_create (tip, Qnil); /* We will mark the tip window a "pseudo-window" below, and such windows cannot have display margins. */ bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0)); diff --git a/src/window.c b/src/window.c index bcc989b5a7..5db166e345 100644 --- a/src/window.c +++ b/src/window.c @@ -617,11 +617,12 @@ DEFUN ("select-window", Fselect_window, Sselect_window, 1, 2, 0, Run `buffer-list-update-hook' unless NORECORD is non-nil. Note that applications and internal routines often select a window temporarily for various purposes; mostly, to simplify coding. As a rule, such -selections should be not recorded and therefore will not pollute +selections should not be recorded and therefore will not pollute `buffer-list-update-hook'. Selections that "really count" are those causing a visible change in the next redisplay of WINDOW's frame and -should be always recorded. So if you think of running a function each -time a window gets selected put it on `buffer-list-update-hook'. +should always be recorded. So if you think of running a function each +time a window gets selected, put it on `buffer-list-update-hook' or +`window-selection-change-functions'. Also note that the main editor command loop sets the current buffer to the buffer of the selected window before each command. */) diff --git a/src/xdisp.c b/src/xdisp.c index 0fd5ec5ec5..b5adee5105 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -10880,7 +10880,7 @@ message_dolog (const char *m, ptrdiff_t nbytes, bool nlflag, bool multibyte) /* Ensure the Messages buffer exists, and switch to it. If we created it, set the major-mode. */ bool newbuffer = NILP (Fget_buffer (Vmessages_buffer_name)); - Fset_buffer (Fget_buffer_create (Vmessages_buffer_name)); + Fset_buffer (Fget_buffer_create (Vmessages_buffer_name, Qnil)); if (newbuffer && !NILP (Ffboundp (intern ("messages-buffer-mode")))) call0 (intern ("messages-buffer-mode")); @@ -11366,7 +11366,7 @@ ensure_echo_area_buffers (void) static char const name_fmt[] = " *Echo Area %d*"; char name[sizeof name_fmt + INT_STRLEN_BOUND (int)]; AUTO_STRING_WITH_LEN (lname, name, sprintf (name, name_fmt, i)); - echo_buffer[i] = Fget_buffer_create (lname); + echo_buffer[i] = Fget_buffer_create (lname, Qnil); bset_truncate_lines (XBUFFER (echo_buffer[i]), Qnil); /* to force word wrap in echo area - it was decided to postpone this*/ diff --git a/src/xfns.c b/src/xfns.c index 46e4bd73a6..abe293e903 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -7041,7 +7041,7 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, tip_f = XFRAME (tip_frame); window = FRAME_ROOT_WINDOW (tip_f); - tip_buf = Fget_buffer_create (tip); + tip_buf = Fget_buffer_create (tip, Qnil); /* We will mark the tip window a "pseudo-window" below, and such windows cannot have display margins. */ bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0)); diff --git a/src/xwidget.c b/src/xwidget.c index e078a28a35..accde65b52 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -100,7 +100,8 @@ DEFUN ("make-xwidget", Lisp_Object val; xw->type = type; xw->title = title; - xw->buffer = NILP (buffer) ? Fcurrent_buffer () : Fget_buffer_create (buffer); + xw->buffer = (NILP (buffer) ? Fcurrent_buffer () + : Fget_buffer_create (buffer, Qnil)); xw->height = XFIXNAT (height); xw->width = XFIXNAT (width); xw->kill_without_query = false; diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 0db66f9751..dd8927457a 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -19,9 +19,7 @@ ;;; Code: -(require 'ert) -(require 'seq) -(eval-when-compile (require 'cl-lib)) +(require 'cl-lib) (ert-deftest overlay-modification-hooks-message-other-buf () "Test for bug#21824. @@ -1334,4 +1332,33 @@ buffer-tests-buffer-local-variables-undo (with-temp-buffer (should (assq 'buffer-undo-list (buffer-local-variables))))) +(ert-deftest buffer-tests-inhibit-buffer-hooks () + "Test `get-buffer-create' argument INHIBIT-BUFFER-HOOKS." + (let* (run-bluh (bluh (lambda () (setq run-bluh t)))) + (unwind-protect + (let* ( run-kbh (kbh (lambda () (setq run-kbh t))) + run-kbqf (kbqf (lambda () (setq run-kbqf t))) ) + + ;; Inhibited. + (add-hook 'buffer-list-update-hook bluh) + (with-current-buffer (generate-new-buffer " foo" t) + (add-hook 'kill-buffer-hook kbh nil t) + (add-hook 'kill-buffer-query-functions kbqf nil t) + (kill-buffer)) + (with-temp-buffer) + (with-output-to-string) + (should-not run-bluh) + (should-not run-kbh) + (should-not run-kbqf) + + ;; Not inhibited. + (with-current-buffer (generate-new-buffer " foo") + (should run-bluh) + (add-hook 'kill-buffer-hook kbh nil t) + (add-hook 'kill-buffer-query-functions kbqf nil t) + (kill-buffer)) + (should run-kbh) + (should run-kbqf)) + (remove-hook 'buffer-list-update-hook bluh)))) + ;;; buffer-tests.el ends here -- 2.29.2