* Proposal: move write-contents-functions higher up in basic-save-buffer @ 2017-05-23 7:19 Eric Abrahamsen 2017-05-23 7:25 ` Eric Abrahamsen 2017-05-23 18:39 ` Eli Zaretskii 0 siblings, 2 replies; 12+ messages in thread From: Eric Abrahamsen @ 2017-05-23 7:19 UTC (permalink / raw) To: emacs-devel Most special-mode buffers aren't visiting a file, and thus they miss out on all the `do-auto-save' and `save-some-buffers' mechanisms. I'd guess a fair number of packages that use special-mode *do* have some concept of saving, or persisting data in some other way. I think the `write-contents-functions' hook would be an ideal way of solving this problem, except that the way `basic-save-buffer' is written, it won't let you get that far without having a file name. My proposal is to declare `write-contents-functions' as *explicitly* a hook for buffers that don't have any file associated with them at all (this would be in contrast to `write-file-functions'). Then we'd move it up higher in the process: either earlier in `basic-save-buffer', or all the way up to `save-buffer' -- that way `basic-save-buffer' could only be for buffers that have a file. Then `save-some-buffers' could check for the buffer-local presence of this variable, and do the save. `do-auto-save' would behave the same. "s" could be bound to `save-buffer' by default in special-mode. WDYT? I think the docstring of `write-contents-functions' already supports this interpretation, it just needs a bit of tweaking to divorce it from buffer-file-name altogether. Eric PS: My original idea was to introduce a buffer-local `save-buffer-function' variable, but I think this makes more sense. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Proposal: move write-contents-functions higher up in basic-save-buffer 2017-05-23 7:19 Proposal: move write-contents-functions higher up in basic-save-buffer Eric Abrahamsen @ 2017-05-23 7:25 ` Eric Abrahamsen 2017-05-23 18:41 ` Eli Zaretskii 2017-05-23 18:39 ` Eli Zaretskii 1 sibling, 1 reply; 12+ messages in thread From: Eric Abrahamsen @ 2017-05-23 7:25 UTC (permalink / raw) To: emacs-devel Eric Abrahamsen <eric@ericabrahamsen.net> writes: > Most special-mode buffers aren't visiting a file, and thus they miss out > on all the `do-auto-save' and `save-some-buffers' mechanisms. I'd guess > a fair number of packages that use special-mode *do* have some concept > of saving, or persisting data in some other way. > > I think the `write-contents-functions' hook would be an ideal way of > solving this problem, except that the way `basic-save-buffer' is > written, it won't let you get that far without having a file name. > > My proposal is to declare `write-contents-functions' as *explicitly* a > hook for buffers that don't have any file associated with them at all > (this would be in contrast to `write-file-functions'). Then we'd move it > up higher in the process: either earlier in `basic-save-buffer', or all > the way up to `save-buffer' -- that way `basic-save-buffer' could only > be for buffers that have a file. > > Then `save-some-buffers' could check for the buffer-local presence of > this variable, and do the save. `do-auto-save' would behave the same. > "s" could be bound to `save-buffer' by default in special-mode. I forgot to say, auto-save would obviously be more difficult, since you'd have to handle the file name and location for the auto save file. I think it would be worth coming up with a solution, though. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Proposal: move write-contents-functions higher up in basic-save-buffer 2017-05-23 7:25 ` Eric Abrahamsen @ 2017-05-23 18:41 ` Eli Zaretskii 0 siblings, 0 replies; 12+ messages in thread From: Eli Zaretskii @ 2017-05-23 18:41 UTC (permalink / raw) To: Eric Abrahamsen; +Cc: emacs-devel > From: Eric Abrahamsen <eric@ericabrahamsen.net> > Date: Tue, 23 May 2017 15:25:01 +0800 > > I forgot to say, auto-save would obviously be more difficult, since > you'd have to handle the file name and location for the auto save file. Perhaps for such buffers it would make sense to make auto-save-visited-mode the default. Then these problems will be automatically taken care of, if you teach basic-save-buffer to save such buffers, per my proposal. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Proposal: move write-contents-functions higher up in basic-save-buffer 2017-05-23 7:19 Proposal: move write-contents-functions higher up in basic-save-buffer Eric Abrahamsen 2017-05-23 7:25 ` Eric Abrahamsen @ 2017-05-23 18:39 ` Eli Zaretskii 2017-05-23 23:09 ` Eric Abrahamsen 1 sibling, 1 reply; 12+ messages in thread From: Eli Zaretskii @ 2017-05-23 18:39 UTC (permalink / raw) To: Eric Abrahamsen; +Cc: emacs-devel > From: Eric Abrahamsen <eric@ericabrahamsen.net> > Date: Tue, 23 May 2017 15:19:11 +0800 > > Most special-mode buffers aren't visiting a file, and thus they miss out > on all the `do-auto-save' and `save-some-buffers' mechanisms. I'd guess > a fair number of packages that use special-mode *do* have some concept > of saving, or persisting data in some other way. > > I think the `write-contents-functions' hook would be an ideal way of > solving this problem, except that the way `basic-save-buffer' is > written, it won't let you get that far without having a file name. > > My proposal is to declare `write-contents-functions' as *explicitly* a > hook for buffers that don't have any file associated with them at all > (this would be in contrast to `write-file-functions'). Then we'd move it > up higher in the process: either earlier in `basic-save-buffer', or all > the way up to `save-buffer' -- that way `basic-save-buffer' could only > be for buffers that have a file. Did you investigate the alternative -- teach basic-save-buffer to save buffers that don't visit files? If that's possible, it should be easier. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Proposal: move write-contents-functions higher up in basic-save-buffer 2017-05-23 18:39 ` Eli Zaretskii @ 2017-05-23 23:09 ` Eric Abrahamsen 2017-05-24 2:38 ` Eli Zaretskii ` (2 more replies) 0 siblings, 3 replies; 12+ messages in thread From: Eric Abrahamsen @ 2017-05-23 23:09 UTC (permalink / raw) To: emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Eric Abrahamsen <eric@ericabrahamsen.net> >> Date: Tue, 23 May 2017 15:19:11 +0800 >> >> Most special-mode buffers aren't visiting a file, and thus they miss out >> on all the `do-auto-save' and `save-some-buffers' mechanisms. I'd guess >> a fair number of packages that use special-mode *do* have some concept >> of saving, or persisting data in some other way. >> >> I think the `write-contents-functions' hook would be an ideal way of >> solving this problem, except that the way `basic-save-buffer' is >> written, it won't let you get that far without having a file name. >> >> My proposal is to declare `write-contents-functions' as *explicitly* a >> hook for buffers that don't have any file associated with them at all >> (this would be in contrast to `write-file-functions'). Then we'd move it >> up higher in the process: either earlier in `basic-save-buffer', or all >> the way up to `save-buffer' -- that way `basic-save-buffer' could only >> be for buffers that have a file. > > Did you investigate the alternative -- teach basic-save-buffer to save > buffers that don't visit files? If that's possible, it should be > easier. I thought that's what I was doing! If a buffer isn't visiting a file, there's essentially no way to guess what "saving it" would mean. The mode that created the buffer would need to provide a function that does the saving. Then basic-save-buffer would need to be taught to call that function, instead of insisting that the buffer have a file. My original thought was to have a new buffer-local variable, save-buffer-function, that points to this function. Then it occurred to me that write-contents-functions seems like a good place to do this. Now I'm not sure. Eric ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Proposal: move write-contents-functions higher up in basic-save-buffer 2017-05-23 23:09 ` Eric Abrahamsen @ 2017-05-24 2:38 ` Eli Zaretskii 2017-05-24 4:55 ` Eric Abrahamsen 2017-05-24 14:12 ` Richard Stallman 2017-05-28 10:12 ` Eric Abrahamsen 2 siblings, 1 reply; 12+ messages in thread From: Eli Zaretskii @ 2017-05-24 2:38 UTC (permalink / raw) To: Eric Abrahamsen; +Cc: emacs-devel > From: Eric Abrahamsen <eric@ericabrahamsen.net> > Date: Wed, 24 May 2017 07:09:07 +0800 > > Eli Zaretskii <eliz@gnu.org> writes: > > >> From: Eric Abrahamsen <eric@ericabrahamsen.net> > >> Date: Tue, 23 May 2017 15:19:11 +0800 > >> > >> Most special-mode buffers aren't visiting a file, and thus they miss out > >> on all the `do-auto-save' and `save-some-buffers' mechanisms. I'd guess > >> a fair number of packages that use special-mode *do* have some concept > >> of saving, or persisting data in some other way. > >> > >> I think the `write-contents-functions' hook would be an ideal way of > >> solving this problem, except that the way `basic-save-buffer' is > >> written, it won't let you get that far without having a file name. > >> > >> My proposal is to declare `write-contents-functions' as *explicitly* a > >> hook for buffers that don't have any file associated with them at all > >> (this would be in contrast to `write-file-functions'). Then we'd move it > >> up higher in the process: either earlier in `basic-save-buffer', or all > >> the way up to `save-buffer' -- that way `basic-save-buffer' could only > >> be for buffers that have a file. > > > > Did you investigate the alternative -- teach basic-save-buffer to save > > buffers that don't visit files? If that's possible, it should be > > easier. > > I thought that's what I was doing! I was referring specifically to this party of your description: > I think the `write-contents-functions' hook would be an ideal way of > solving this problem, except that the way `basic-save-buffer' is > written, it won't let you get that far without having a file name. My thinking was that by somehow overcoming this obstacle, you can allow users to easily use write-contents-functions as they need. Does this make sense? If not, can you tell what is the difficulty in this regard? ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Proposal: move write-contents-functions higher up in basic-save-buffer 2017-05-24 2:38 ` Eli Zaretskii @ 2017-05-24 4:55 ` Eric Abrahamsen 2017-05-24 12:29 ` Stefan Monnier 2017-05-24 17:46 ` Eli Zaretskii 0 siblings, 2 replies; 12+ messages in thread From: Eric Abrahamsen @ 2017-05-24 4:55 UTC (permalink / raw) To: emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Eric Abrahamsen <eric@ericabrahamsen.net> >> Date: Wed, 24 May 2017 07:09:07 +0800 >> >> Eli Zaretskii <eliz@gnu.org> writes: >> >> >> From: Eric Abrahamsen <eric@ericabrahamsen.net> >> >> Date: Tue, 23 May 2017 15:19:11 +0800 >> >> >> >> Most special-mode buffers aren't visiting a file, and thus they miss out >> >> on all the `do-auto-save' and `save-some-buffers' mechanisms. I'd guess >> >> a fair number of packages that use special-mode *do* have some concept >> >> of saving, or persisting data in some other way. >> >> >> >> I think the `write-contents-functions' hook would be an ideal way of >> >> solving this problem, except that the way `basic-save-buffer' is >> >> written, it won't let you get that far without having a file name. >> >> >> >> My proposal is to declare `write-contents-functions' as *explicitly* a >> >> hook for buffers that don't have any file associated with them at all >> >> (this would be in contrast to `write-file-functions'). Then we'd move it >> >> up higher in the process: either earlier in `basic-save-buffer', or all >> >> the way up to `save-buffer' -- that way `basic-save-buffer' could only >> >> be for buffers that have a file. >> > >> > Did you investigate the alternative -- teach basic-save-buffer to save >> > buffers that don't visit files? If that's possible, it should be >> > easier. >> >> I thought that's what I was doing! > > I was referring specifically to this party of your description: > >> I think the `write-contents-functions' hook would be an ideal way of >> solving this problem, except that the way `basic-save-buffer' is >> written, it won't let you get that far without having a file name. > > My thinking was that by somehow overcoming this obstacle, you can > allow users to easily use write-contents-functions as they need. > > Does this make sense? If not, can you tell what is the difficulty in > this regard? I probably just did a poor job writing the initial message. That's what I was proposing to begin with: to jiggle `basic-save-buffer' (and I think also `save-some-buffers') so that the running of `write-contents-functions' comes earlier in the function, or is otherwise in its own branch that doesn't require a `buffer-file-name'. I think it would be a fairly unintrusive change, it would just require a bit of thought. I can try to produce a patch, if this is acceptable in principle. Eric ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Proposal: move write-contents-functions higher up in basic-save-buffer 2017-05-24 4:55 ` Eric Abrahamsen @ 2017-05-24 12:29 ` Stefan Monnier 2017-05-25 7:42 ` Eric Abrahamsen 2017-05-24 17:46 ` Eli Zaretskii 1 sibling, 1 reply; 12+ messages in thread From: Stefan Monnier @ 2017-05-24 12:29 UTC (permalink / raw) To: emacs-devel > I probably just did a poor job writing the initial message. That's what > I was proposing to begin with: to jiggle `basic-save-buffer' (and I > think also `save-some-buffers') so that the running of > `write-contents-functions' comes earlier in the function, or is > otherwise in its own branch that doesn't require a `buffer-file-name'. I > think it would be a fairly unintrusive change, it would just require a > bit of thought. I can try to produce a patch, if this is acceptable in > principle. I haven't looked at basic-save-buffer recently, but in the worst case we could keep the current code and add a (if (null buffer-file-name) (run-hook-... 'write-contents-functions) ...) but admittedly, it's better if we can move the single call to write-contents-functions so it's shared by the file and the non-file cases. Stefan ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Proposal: move write-contents-functions higher up in basic-save-buffer 2017-05-24 12:29 ` Stefan Monnier @ 2017-05-25 7:42 ` Eric Abrahamsen 0 siblings, 0 replies; 12+ messages in thread From: Eric Abrahamsen @ 2017-05-25 7:42 UTC (permalink / raw) To: emacs-devel [-- Attachment #1: Type: text/plain, Size: 1261 bytes --] Stefan Monnier <monnier@iro.umontreal.ca> writes: > I haven't looked at basic-save-buffer recently, but in the worst case we > could keep the current code and add a > > (if (null buffer-file-name) > (run-hook-... 'write-contents-functions) > ...) > > but admittedly, it's better if we can move the single call to > write-contents-functions so it's shared by the file and the > non-file cases. Eli Zaretskii <eliz@gnu.org> writes: >> From: Eric Abrahamsen <eric@ericabrahamsen.net> >> Date: Wed, 24 May 2017 12:55:53 +0800 >> I can try to produce a patch, if this is acceptable in principle. > > Yes, please. Richard Stallman <rms@gnu.org> writes: > Please try using write-contents-functions for this, and you'll see if it > does the job. If not, you'll see what more change is needed. Okay, here's a first stab at it. I think it should work correctly: all the short-circuit hooks get a chance to run in all cases, but the function only insists on the presence of a file if `write-contents-functions' are not present, or if they fail with a nil value. I'd like to specify in the docs that those functions should fail with an error. If this looks okay I'll spend a bit more time testing it, then make docstring and manual edits. Eric [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-First-whack-at-write-contents-functions-for-non-file.patch --] [-- Type: text/x-diff, Size: 7052 bytes --] From af4f811439785113fe2be71f499006776958755b Mon Sep 17 00:00:00 2001 From: Eric Abrahamsen <eric@ericabrahamsen.net> Date: Thu, 25 May 2017 15:28:19 +0800 Subject: [PATCH] First whack at write-contents-functions for non-file buffers --- lisp/files.el | 106 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/lisp/files.el b/lisp/files.el index 8ac1993754..c074fa7995 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -4943,29 +4943,14 @@ basic-save-buffer (if (buffer-base-buffer) (set-buffer (buffer-base-buffer))) (if (or (buffer-modified-p) - ;; handle the case when no modification has been made but - ;; the file disappeared since visited + ;; Handle the case when no modification has been made but + ;; the file disappeared since visited. (and buffer-file-name (not (file-exists-p buffer-file-name)))) (let ((recent-save (recent-auto-save-p)) setmodes) - ;; If buffer has no file name, ask user for one. - (or buffer-file-name - (let ((filename - (expand-file-name - (read-file-name "File to save in: " - nil (expand-file-name (buffer-name)))))) - (if (file-exists-p filename) - (if (file-directory-p filename) - ;; Signal an error if the user specified the name of an - ;; existing directory. - (error "%s is a directory" filename) - (unless (y-or-n-p (format-message - "File `%s' exists; overwrite? " - filename)) - (error "Canceled")))) - (set-visited-file-name filename))) - (or (verify-visited-file-modtime (current-buffer)) + (or (null buffer-file-name) + (verify-visited-file-modtime (current-buffer)) (not (file-exists-p buffer-file-name)) (yes-or-no-p (format @@ -4977,6 +4962,7 @@ basic-save-buffer (save-excursion (and (> (point-max) (point-min)) (not find-file-literally) + (null buffer-read-only) (/= (char-after (1- (point-max))) ?\n) (not (and (eq selective-display t) (= (char-after (1- (point-max))) ?\r))) @@ -4989,41 +4975,60 @@ basic-save-buffer (save-excursion (goto-char (point-max)) (insert ?\n)))) - ;; Support VC version backups. - (vc-before-save) ;; Don't let errors prevent saving the buffer. (with-demoted-errors (run-hooks 'before-save-hook)) - (or (run-hook-with-args-until-success 'write-contents-functions) - (run-hook-with-args-until-success 'local-write-file-hooks) - (run-hook-with-args-until-success 'write-file-functions) - ;; If a hook returned t, file is already "written". - ;; Otherwise, write it the usual way now. - (let ((dir (file-name-directory - (expand-file-name buffer-file-name)))) - (unless (file-exists-p dir) - (if (y-or-n-p - (format-message - "Directory `%s' does not exist; create? " dir)) - (make-directory dir t) - (error "Canceled"))) - (setq setmodes (basic-save-buffer-1)))) + ;; Give `write-contents-functions' a chance to + ;; short-circuit the whole process. + (unless (run-hook-with-args-until-success 'write-contents-functions) + ;; If buffer has no file name, ask user for one. + (or buffer-file-name + (let ((filename + (expand-file-name + (read-file-name "File to save in: " + nil (expand-file-name (buffer-name)))))) + (if (file-exists-p filename) + (if (file-directory-p filename) + ;; Signal an error if the user specified the name of an + ;; existing directory. + (error "%s is a directory" filename) + (unless (y-or-n-p (format-message + "File `%s' exists; overwrite? " + filename)) + (error "Canceled")))) + (set-visited-file-name filename))) + ;; Support VC version backups. + (vc-before-save) + (or (run-hook-with-args-until-success 'local-write-file-hooks) + (run-hook-with-args-until-success 'write-file-functions) + ;; If a hook returned t, file is already "written". + ;; Otherwise, write it the usual way now. + (let ((dir (file-name-directory + (expand-file-name buffer-file-name)))) + (unless (file-exists-p dir) + (if (y-or-n-p + (format-message + "Directory `%s' does not exist; create? " dir)) + (make-directory dir t) + (error "Canceled"))) + (setq setmodes (basic-save-buffer-1))))) ;; Now we have saved the current buffer. Let's make sure ;; that buffer-file-coding-system is fixed to what ;; actually used for saving by binding it locally. - (if save-buffer-coding-system - (setq save-buffer-coding-system last-coding-system-used) - (setq buffer-file-coding-system last-coding-system-used)) - (setq buffer-file-number - (nthcdr 10 (file-attributes buffer-file-name))) - (if setmodes - (condition-case () - (progn - (unless - (with-demoted-errors - (set-file-modes buffer-file-name (car setmodes))) - (set-file-extended-attributes buffer-file-name - (nth 1 setmodes)))) - (error nil)))) + (when buffer-file-name + (if save-buffer-coding-system + (setq save-buffer-coding-system last-coding-system-used) + (setq buffer-file-coding-system last-coding-system-used)) + (setq buffer-file-number + (nthcdr 10 (file-attributes buffer-file-name))) + (if setmodes + (condition-case () + (progn + (unless + (with-demoted-errors + (set-file-modes buffer-file-name (car setmodes))) + (set-file-extended-attributes buffer-file-name + (nth 1 setmodes)))) + (error nil))))) ;; If the auto-save file was recent before this command, ;; delete it now. (delete-auto-save-file-if-necessary recent-save) @@ -5255,7 +5260,8 @@ save-some-buffers (and pred (progn (set-buffer buffer) - (and buffer-offer-save (> (buffer-size) 0))))) + (and buffer-offer-save (> (buffer-size) 0)))) + write-contents-functions) (or (not (functionp pred)) (with-current-buffer buffer (funcall pred))) (if arg -- 2.13.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: Proposal: move write-contents-functions higher up in basic-save-buffer 2017-05-24 4:55 ` Eric Abrahamsen 2017-05-24 12:29 ` Stefan Monnier @ 2017-05-24 17:46 ` Eli Zaretskii 1 sibling, 0 replies; 12+ messages in thread From: Eli Zaretskii @ 2017-05-24 17:46 UTC (permalink / raw) To: Eric Abrahamsen; +Cc: emacs-devel > From: Eric Abrahamsen <eric@ericabrahamsen.net> > Date: Wed, 24 May 2017 12:55:53 +0800 > > > My thinking was that by somehow overcoming this obstacle, you can > > allow users to easily use write-contents-functions as they need. > > > > Does this make sense? If not, can you tell what is the difficulty in > > this regard? > > I probably just did a poor job writing the initial message. That's what > I was proposing to begin with: to jiggle `basic-save-buffer' (and I > think also `save-some-buffers') so that the running of > `write-contents-functions' comes earlier in the function, or is > otherwise in its own branch that doesn't require a `buffer-file-name'. Then I guess we are in violent agreement. > I can try to produce a patch, if this is acceptable in principle. Yes, please. Thanks. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Proposal: move write-contents-functions higher up in basic-save-buffer 2017-05-23 23:09 ` Eric Abrahamsen 2017-05-24 2:38 ` Eli Zaretskii @ 2017-05-24 14:12 ` Richard Stallman 2017-05-28 10:12 ` Eric Abrahamsen 2 siblings, 0 replies; 12+ messages in thread From: Richard Stallman @ 2017-05-24 14:12 UTC (permalink / raw) To: Eric Abrahamsen; +Cc: emacs-devel [[[ To any NSA and FBI agents reading my email: please consider ]]] [[[ whether defending the US Constitution against all enemies, ]]] [[[ foreign or domestic, requires you to follow Snowden's example. ]]] > I thought that's what I was doing! If a buffer isn't visiting a file, > there's essentially no way to guess what "saving it" would mean. That's true, as a generality. The only way that "saving" a non-file buffer could be meaningful is if Emacs is told precisely what saving should mean for that buffer. The > mode that created the buffer would need to provide a function that does > the saving. Then basic-save-buffer would need to be taught to call that > function, instead of insisting that the buffer have a file. > Then it occurred to > me that write-contents-functions seems like a good place to do this. Now > I'm not sure. Please try using write-contents-functions for this, and you'll see if it does the job. If not, you'll see what more change is needed. -- Dr Richard Stallman President, Free Software Foundation (gnu.org, fsf.org) Internet Hall-of-Famer (internethalloffame.org) Skype: No way! See stallman.org/skype.html. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: Proposal: move write-contents-functions higher up in basic-save-buffer 2017-05-23 23:09 ` Eric Abrahamsen 2017-05-24 2:38 ` Eli Zaretskii 2017-05-24 14:12 ` Richard Stallman @ 2017-05-28 10:12 ` Eric Abrahamsen 2 siblings, 0 replies; 12+ messages in thread From: Eric Abrahamsen @ 2017-05-28 10:12 UTC (permalink / raw) To: emacs-devel [-- Attachment #1: Type: text/plain, Size: 2231 bytes --] Eric Abrahamsen <eric@ericabrahamsen.net> writes: > Eli Zaretskii <eliz@gnu.org> writes: > >>> From: Eric Abrahamsen <eric@ericabrahamsen.net> >>> Date: Tue, 23 May 2017 15:19:11 +0800 >>> >>> Most special-mode buffers aren't visiting a file, and thus they miss out >>> on all the `do-auto-save' and `save-some-buffers' mechanisms. I'd guess >>> a fair number of packages that use special-mode *do* have some concept >>> of saving, or persisting data in some other way. >>> >>> I think the `write-contents-functions' hook would be an ideal way of >>> solving this problem, except that the way `basic-save-buffer' is >>> written, it won't let you get that far without having a file name. >>> >>> My proposal is to declare `write-contents-functions' as *explicitly* a >>> hook for buffers that don't have any file associated with them at all >>> (this would be in contrast to `write-file-functions'). Then we'd move it >>> up higher in the process: either earlier in `basic-save-buffer', or all >>> the way up to `save-buffer' -- that way `basic-save-buffer' could only >>> be for buffers that have a file. Okay, I've poked at this in all the ways I can think of, and it seems to work okay. Basic recap: The goal is to re-interpret `write-contents-functions' as a mechanism for allowing buffers that are not visiting a file to specify a custom save mechanism. The original idea was to let special-mode buffers install their own save routines, which would run on `save-buffer', and also as a part of the `save-some-buffers' routine. If the buffer-local value of `write-contents-functions' is non-nil for buffer BUF, then `save-some-buffers' will accept BUF as a potentially saveable buffer. `basic-save-buffer' has been rearranged so that the `write-contents-functions' hook is run a little earlier on. Only if the functions in that hook fail will `basic-save-buffer' go on to prompt the user for a file to save the buffer in. I'm leaving `do-auto-save' as a problem for another day. I've done a proper commit, with manual edits and everything. I'm a bit leery of just committing this, as it theoretically touches every buffer in an Emacs session. If anyone wants to take a hard stare at it, that would be very welcome. Eric [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Allow-write-contents-functions-to-short-circuit-buff.patch --] [-- Type: text/x-diff, Size: 11183 bytes --] From 7b5f18648e3d4b2aa9a5af536a624d6518d8fdd7 Mon Sep 17 00:00:00 2001 From: Eric Abrahamsen <eric@ericabrahamsen.net> Date: Thu, 25 May 2017 15:28:19 +0800 Subject: [PATCH] Allow write-contents-functions to short-circuit buffer saving * lisp/files.el (basic-save-buffer): If write-contents-functions is non-nil, give the functions in that hook a chance to save buffer contents before checking if buffer is visiting a file. (save-some-buffers): If write-contents-functions is non nil, consider the buffer eligible for a save prompt. * doc/lispref/files.texi (Saving Buffers): Mention new behavior, note that special-mode buffers can use this to "save" themselves. --- doc/lispref/files.texi | 18 +++++-- lisp/files.el | 136 +++++++++++++++++++++++++++---------------------- 2 files changed, 87 insertions(+), 67 deletions(-) diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 2b692dbf68..a6ee0cc69c 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -457,15 +457,23 @@ Saving Buffers @defvar write-contents-functions This works just like @code{write-file-functions}, but it is intended for hooks that pertain to the buffer's contents, not to the particular -visited file or its location. Such hooks are usually set up by major -modes, as buffer-local bindings for this variable. This variable -automatically becomes buffer-local whenever it is set; switching to a -new major mode always resets this variable, but calling -@code{set-visited-file-name} does not. +visited file or its location, and can be used to create arbitrary save +processes for buffers that aren't visiting files at all. Such hooks +are usually set up by major modes, as buffer-local bindings for this +variable. This variable automatically becomes buffer-local whenever +it is set; switching to a new major mode always resets this variable, +but calling @code{set-visited-file-name} does not. If any of the functions in this hook returns non-@code{nil}, the file is considered already written and the rest are not called and neither are the functions in @code{write-file-functions}. + +When using this hook to save buffers that are not visiting files (for +instance, special-mode buffers), keep in mind that, if the function +fails to save correctly and returns a @code{nil} value, +@code{save-buffer} will go on to prompt the user for a file to save +the buffer in. If this is undesirable, consider having the function +fail by raising an error. @end defvar @defopt before-save-hook diff --git a/lisp/files.el b/lisp/files.el index 8ac1993754..1f88f86b76 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -514,10 +514,12 @@ 'write-contents-hooks 'write-contents-functions "22.1") (defvar write-contents-functions nil "List of functions to be called before writing out a buffer to a file. -Only used by `save-buffer'. -If one of them returns non-nil, the file is considered already written -and the rest are not called and neither are the functions in -`write-file-functions'. + +Only used by `save-buffer'. If one of them returns non-nil, the +file is considered already written and the rest are not called +and neither are the functions in `write-file-functions'. This +hook can thus be used to create save behavior for buffers that +are not visiting a file at all. This variable is meant to be used for hooks that pertain to the buffer's contents, not to the particular visited file; thus, @@ -4932,9 +4934,12 @@ save-buffer-coding-system (defun basic-save-buffer (&optional called-interactively) "Save the current buffer in its visited file, if it has been modified. -The hooks `write-contents-functions' and `write-file-functions' get a chance -to do the job of saving; if they do not, then the buffer is saved in -the visited file in the usual way. + +The hooks `write-contents-functions', `local-write-file-hooks' +and `write-file-functions' get a chance to do the job of saving; +if they do not, then the buffer is saved in the visited file in +the usual way. + Before and after saving the buffer, this function runs `before-save-hook' and `after-save-hook', respectively." (interactive '(called-interactively)) @@ -4943,29 +4948,14 @@ basic-save-buffer (if (buffer-base-buffer) (set-buffer (buffer-base-buffer))) (if (or (buffer-modified-p) - ;; handle the case when no modification has been made but - ;; the file disappeared since visited + ;; Handle the case when no modification has been made but + ;; the file disappeared since visited. (and buffer-file-name (not (file-exists-p buffer-file-name)))) (let ((recent-save (recent-auto-save-p)) setmodes) - ;; If buffer has no file name, ask user for one. - (or buffer-file-name - (let ((filename - (expand-file-name - (read-file-name "File to save in: " - nil (expand-file-name (buffer-name)))))) - (if (file-exists-p filename) - (if (file-directory-p filename) - ;; Signal an error if the user specified the name of an - ;; existing directory. - (error "%s is a directory" filename) - (unless (y-or-n-p (format-message - "File `%s' exists; overwrite? " - filename)) - (error "Canceled")))) - (set-visited-file-name filename))) - (or (verify-visited-file-modtime (current-buffer)) + (or (null buffer-file-name) + (verify-visited-file-modtime (current-buffer)) (not (file-exists-p buffer-file-name)) (yes-or-no-p (format @@ -4977,6 +4967,7 @@ basic-save-buffer (save-excursion (and (> (point-max) (point-min)) (not find-file-literally) + (null buffer-read-only) (/= (char-after (1- (point-max))) ?\n) (not (and (eq selective-display t) (= (char-after (1- (point-max))) ?\r))) @@ -4989,46 +4980,65 @@ basic-save-buffer (save-excursion (goto-char (point-max)) (insert ?\n)))) - ;; Support VC version backups. - (vc-before-save) ;; Don't let errors prevent saving the buffer. (with-demoted-errors (run-hooks 'before-save-hook)) - (or (run-hook-with-args-until-success 'write-contents-functions) - (run-hook-with-args-until-success 'local-write-file-hooks) - (run-hook-with-args-until-success 'write-file-functions) - ;; If a hook returned t, file is already "written". - ;; Otherwise, write it the usual way now. - (let ((dir (file-name-directory - (expand-file-name buffer-file-name)))) - (unless (file-exists-p dir) - (if (y-or-n-p - (format-message - "Directory `%s' does not exist; create? " dir)) - (make-directory dir t) - (error "Canceled"))) - (setq setmodes (basic-save-buffer-1)))) + ;; Give `write-contents-functions' a chance to + ;; short-circuit the whole process. + (unless (run-hook-with-args-until-success 'write-contents-functions) + ;; If buffer has no file name, ask user for one. + (or buffer-file-name + (let ((filename + (expand-file-name + (read-file-name "File to save in: " + nil (expand-file-name (buffer-name)))))) + (if (file-exists-p filename) + (if (file-directory-p filename) + ;; Signal an error if the user specified the name of an + ;; existing directory. + (error "%s is a directory" filename) + (unless (y-or-n-p (format-message + "File `%s' exists; overwrite? " + filename)) + (error "Canceled")))) + (set-visited-file-name filename))) + ;; Support VC version backups. + (vc-before-save) + (or (run-hook-with-args-until-success 'local-write-file-hooks) + (run-hook-with-args-until-success 'write-file-functions) + ;; If a hook returned t, file is already "written". + ;; Otherwise, write it the usual way now. + (let ((dir (file-name-directory + (expand-file-name buffer-file-name)))) + (unless (file-exists-p dir) + (if (y-or-n-p + (format-message + "Directory `%s' does not exist; create? " dir)) + (make-directory dir t) + (error "Canceled"))) + (setq setmodes (basic-save-buffer-1))))) ;; Now we have saved the current buffer. Let's make sure ;; that buffer-file-coding-system is fixed to what ;; actually used for saving by binding it locally. - (if save-buffer-coding-system - (setq save-buffer-coding-system last-coding-system-used) - (setq buffer-file-coding-system last-coding-system-used)) - (setq buffer-file-number - (nthcdr 10 (file-attributes buffer-file-name))) - (if setmodes - (condition-case () - (progn - (unless - (with-demoted-errors - (set-file-modes buffer-file-name (car setmodes))) - (set-file-extended-attributes buffer-file-name - (nth 1 setmodes)))) - (error nil)))) - ;; If the auto-save file was recent before this command, - ;; delete it now. - (delete-auto-save-file-if-necessary recent-save) - ;; Support VC `implicit' locking. - (vc-after-save) + (when buffer-file-name + (if save-buffer-coding-system + (setq save-buffer-coding-system last-coding-system-used) + (setq buffer-file-coding-system last-coding-system-used)) + (setq buffer-file-number + (nthcdr 10 (file-attributes buffer-file-name))) + (if setmodes + (condition-case () + (progn + (unless + (with-demoted-errors + (set-file-modes buffer-file-name (car setmodes))) + (set-file-extended-attributes buffer-file-name + (nth 1 setmodes)))) + (error nil))) + ;; Support VC `implicit' locking. + (vc-after-save)) + ;; If the auto-save file was recent before this command, + ;; delete it now. + (delete-auto-save-file-if-necessary recent-save)) (run-hooks 'after-save-hook)) (or noninteractive (not called-interactively) @@ -5255,7 +5265,9 @@ save-some-buffers (and pred (progn (set-buffer buffer) - (and buffer-offer-save (> (buffer-size) 0))))) + (and buffer-offer-save (> (buffer-size) 0)))) + (buffer-local-value + 'write-contents-functions buffer)) (or (not (functionp pred)) (with-current-buffer buffer (funcall pred))) (if arg -- 2.13.0 ^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2017-05-28 10:12 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2017-05-23 7:19 Proposal: move write-contents-functions higher up in basic-save-buffer Eric Abrahamsen 2017-05-23 7:25 ` Eric Abrahamsen 2017-05-23 18:41 ` Eli Zaretskii 2017-05-23 18:39 ` Eli Zaretskii 2017-05-23 23:09 ` Eric Abrahamsen 2017-05-24 2:38 ` Eli Zaretskii 2017-05-24 4:55 ` Eric Abrahamsen 2017-05-24 12:29 ` Stefan Monnier 2017-05-25 7:42 ` Eric Abrahamsen 2017-05-24 17:46 ` Eli Zaretskii 2017-05-24 14:12 ` Richard Stallman 2017-05-28 10:12 ` Eric Abrahamsen
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).