unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Missing `with' macro?
@ 2006-07-24 10:17 Mathias Dahl
  2006-07-24 13:46 ` Stefan Monnier
  2006-07-24 18:22 ` Richard Stallman
  0 siblings, 2 replies; 18+ messages in thread
From: Mathias Dahl @ 2006-07-24 10:17 UTC (permalink / raw)


I am currently optimizing some defuns in tumme.el and found that I do
the following repeatdely:

 (setq buf (find-file tumme-db-file))
 ;; do a lot of stuff in it
 (save-buffer) ;; optionally
 (kill-buffer buf)

I really like the different `with' macros that Emacs has and was
surprised there wasn't any `with-file' macro, that could be callable
like this:

(with-file tumme-db-file
  ;; do stuff)

It could have two modes, reading and writing as sometimes you just
want to use the information in the opened file, not write back to it.

I guess what I want is this, without the call to `insert-file-contents':

(with-temp-file tumme-db-file
  (insert-file-contents tumme-db-file)
  ;; do stuff)

What do others think? Is the with-temp-file + insert-file-contents combo enough?

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-07-24 10:17 Missing `with' macro? Mathias Dahl
@ 2006-07-24 13:46 ` Stefan Monnier
  2006-07-24 14:33   ` Mathias Dahl
  2006-07-24 18:22 ` Richard Stallman
  1 sibling, 1 reply; 18+ messages in thread
From: Stefan Monnier @ 2006-07-24 13:46 UTC (permalink / raw)
  Cc: emacs-devel

> (setq buf (find-file tumme-db-file))
> ;; do a lot of stuff in it
> (save-buffer) ;; optionally
> (kill-buffer buf)

Just for the record, the above code will bomb out if the current window is
a minibuffer or is dedicated:  Always prefer find-file-noselect to find-file.


        Stefan "whose windows are all dedicated or minibuffers"


PS: Regarding with-file, it would be nice, but please make sure it can be
    used also when you just want to read the file, and also when you
    specifically don't want the major mode to be set, and also when you do
    want the major mode to be set, and also be careful not to kill
    a pre-existing buffer inadvertently, ...

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-07-24 13:46 ` Stefan Monnier
@ 2006-07-24 14:33   ` Mathias Dahl
  0 siblings, 0 replies; 18+ messages in thread
From: Mathias Dahl @ 2006-07-24 14:33 UTC (permalink / raw)
  Cc: emacs-devel

> > (setq buf (find-file tumme-db-file))
> > ;; do a lot of stuff in it
> > (save-buffer) ;; optionally
> > (kill-buffer buf)
>
> Just for the record, the above code will bomb out if the current window is
> a minibuffer or is dedicated:  Always prefer find-file-noselect to find-file.

Thanks for the advice! I will probably replace it with a combination
of `with-temp-file' and `insert-file-contents'.

>        Stefan "whose windows are all dedicated or minibuffers"

/Mathias - who has never cared about this dedicated business... :)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-07-24 10:17 Missing `with' macro? Mathias Dahl
  2006-07-24 13:46 ` Stefan Monnier
@ 2006-07-24 18:22 ` Richard Stallman
  2006-07-24 20:55   ` Jorgen Schaefer
  2006-07-28  2:14   ` Stuart D. Herring
  1 sibling, 2 replies; 18+ messages in thread
From: Richard Stallman @ 2006-07-24 18:22 UTC (permalink / raw)
  Cc: emacs-devel

    I am currently optimizing some defuns in tumme.el and found that I do
    the following repeatdely:

     (setq buf (find-file tumme-db-file))
     ;; do a lot of stuff in it
     (save-buffer) ;; optionally
     (kill-buffer buf)

We don't usually call find-file for such temporary purposes.
We usually use code like this:

     (with-temp-buffer
       (insert-file-contents file)
       operate on the file)

except that if the file's already visited in a buffer,
it is more efficient to use that buffer (and not kill it).

To develop a general macro for this would be useful.  It is not a
trivial thing.  Please start working on it if you want.  The case of
just examining a file without altering it, since that is more common,
and easier.  I suspect that we may need two totally different macros
for the read-only case and the case of altering the file.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-07-24 18:22 ` Richard Stallman
@ 2006-07-24 20:55   ` Jorgen Schaefer
  2006-07-25  3:09     ` Richard Stallman
  2006-07-28  2:14   ` Stuart D. Herring
  1 sibling, 1 reply; 18+ messages in thread
From: Jorgen Schaefer @ 2006-07-24 20:55 UTC (permalink / raw)


Richard Stallman <rms@gnu.org> writes:

> We don't usually call find-file for such temporary purposes.
> We usually use code like this:
>
>      (with-temp-buffer
>        (insert-file-contents file)
>        operate on the file)
>
> except that if the file's already visited in a buffer,
> it is more efficient to use that buffer (and not kill it).
>
> To develop a general macro for this would be useful.  It is not a
> trivial thing.  Please start working on it if you want.

I have written such a macro for my program to generate my weblog
and rss feed:

(put 'with-file 'lisp-indent-function 1)
(defmacro with-file (file &rest body)
  "Run BODY in a buffer visiting FILE.
If the buffer was not open before calling this macro, it's killed
afterwards."
  (let ((file-sym (make-symbol "file"))
        (existsp-sym (make-symbol "existsp"))
        (buf-sym (make-symbol "buf")))
    `(let* ((,file-sym ,file)
            (,existsp-sym (get-file-buffer ,file-sym))
            (,buf-sym (find-file-noselect ,file-sym)))
       (unwind-protect
           (with-current-buffer ,buf-sym
             (save-excursion
               (save-restriction
                 (widen)
                 ,@body)))
         (when (not ,existsp-sym)
           (kill-buffer ,buf-sym))))))

Regards,
        -- Jorgen

-- 
((email . "forcer@forcix.cx") (www . "http://www.forcix.cx/")
 (gpg   . "1024D/028AF63C")   (irc . "nick forcer on IRCnet"))

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-07-24 20:55   ` Jorgen Schaefer
@ 2006-07-25  3:09     ` Richard Stallman
  0 siblings, 0 replies; 18+ messages in thread
From: Richard Stallman @ 2006-07-25  3:09 UTC (permalink / raw)
  Cc: emacs-devel

    (defmacro with-file (file &rest body)
      "Run BODY in a buffer visiting FILE.
    If the buffer was not open before calling this macro, it's killed
    afterwards."
      (let ((file-sym (make-symbol "file"))
	    (existsp-sym (make-symbol "existsp"))
	    (buf-sym (make-symbol "buf")))
	`(let* ((,file-sym ,file)
		(,existsp-sym (get-file-buffer ,file-sym))
		(,buf-sym (find-file-noselect ,file-sym)))

Using find-file-noselect could be the right thing in some cases,
but there are many cases (especially where you just read the file
or scan it quickly) where that is much too heavy.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-07-24 18:22 ` Richard Stallman
  2006-07-24 20:55   ` Jorgen Schaefer
@ 2006-07-28  2:14   ` Stuart D. Herring
  2006-07-28  2:23     ` Stuart D. Herring
  2006-07-29 15:18     ` Richard Stallman
  1 sibling, 2 replies; 18+ messages in thread
From: Stuart D. Herring @ 2006-07-28  2:14 UTC (permalink / raw)
  Cc: emacs-devel, Mathias Dahl

[-- Attachment #1: Type: text/plain, Size: 896 bytes --]

> To develop a general macro for this would be useful.  It is not a
> trivial thing.  Please start working on it if you want.  The case of
> just examining a file without altering it, since that is more common,
> and easier.  I suspect that we may need two totally different macros
> for the read-only case and the case of altering the file.

I've implemented a first draft of this, complete with file-writing
capacity in the same macro: see the attached file and please comment.  (In
particular, is there a clean way to silence `save-buffer' or the
equivalent?)  I still don't quite have papers, but I am led to believe
that it will happen Real Soon Now; since this isn't going into this
release AFAIK, I hope that's not a problem.

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or
too sparse, it is because mass-energy conversion has occurred during
shipping.

[-- Attachment #2: with-file.el --]
[-- Type: application/octet-stream, Size: 5120 bytes --]

(put 'hide-message 'lisp-indent-function 0)
(defmacro hide-message (&rest body)
	"Execute BODY, then restore the previous contents of the echo area.
Messages produced by BODY are not logged."
	(let ((pm-sym (make-symbol "prev-msg")))
		`(let ((,pm-sym (current-message)) message-log-max)
			 ,@body
			 (message ,pm-sym))))

(put 'with-file 'lisp-indent-function 4)
(defmacro with-file (file visit reuse write &rest body)
	"Execute the forms in BODY in a buffer with FILE's text loaded.
Point begins at the beginning of the buffer, which is not narrowed.
The value returned is the value of the last form in BODY.

VISIT t means actually visit the file, loading its appropriate major mode and
local variables and such.  VISIT nil means do none of these things; BODY will
be executed in a buffer with the contents of FILE but that is visiting no
file.  Any other value for VISIT works like nil except that the file is read
literally (see `insert-file-contents-literally').

REUSE non-nil means to use an existing buffer if it is visiting FILE.  (The
value of `find-file-existing-other-name' affects this determination.)  The
buffer will only be re-used if its literal status (see `find-file-literally')
matches the literal status requested by VISIT.  Note that reusing a buffer may
mean that the text on which BODY operates differs from the actual contents of
FILE.

WRITE non-nil means to modify the file; if the BODY finishes normally, then
the buffer is saved if the file was visited or written otherwise.  If WRITE is
neither t nor nil, saving messages are suppressed.  The buffer will be
read-only when BODY is executed iff WRITE is nil.

If a buffer is created in the execution of this macro, it is killed when it
finishes (even in the case of abnormal exit), regardless of any unsaved
changes made."
	(let ((file-sym (make-symbol "file"))
				(visit-sym (make-symbol "visit"))
				(raw-sym (make-symbol "raw"))
				(reuse-sym (make-symbol "reuse"))
				(write-sym (make-symbol "write"))
				(quiet-sym (make-symbol "quiet"))
        (extant-sym (make-symbol "extant"))
        (extant-file-sym (make-symbol "extant-file"))
        (buf-sym (make-symbol "buf"))
				(reused-sym (make-symbol "reused"))
				(bro-sym (make-symbol "buf-readonly")))
		`(let* ((,file-sym ,file)
						(,visit-sym ,visit)
						(,raw-sym (and ,visit-sym (not (eq ,visit-sym t))))
						(,reuse-sym ,reuse)
						(,write-sym ,write)
						(,quiet-sym (and ,write-sym (not (eq ,write-sym t))))
						(,extant-sym (if find-file-existing-other-name
														 (find-buffer-visiting ,file-sym)
													 (get-file-buffer ,file-sym)))
						(,extant-file-sym (and ,extant-sym (buffer-file-name ,extant-sym)))
						(,buf-sym
						 (or (and ,reuse-sym ,extant-sym
											(eq (not (with-current-buffer ,extant-sym
																 find-file-literally))
													(not ,raw-sym))
											,extant-sym)
								 (if (eq ,visit-sym t)
										 (progn
											 ;; We need to pretend that any other visiting buffer
											 ;; doesn't actually exist.
											 (if ,extant-sym
													 (with-current-buffer ,extant-sym
														 (setq buffer-file-name nil)))
											 (find-file-noselect ,file-sym t))
									 (with-current-buffer (generate-new-buffer "*with-file*")
										 (if (eq ,visit-sym nil)
												 (insert-file-contents ,file-sym)
											 (insert-file-contents-literally ,file-sym))
										 (current-buffer)))))
						(,reused-sym (eq ,buf-sym ,extant-sym))
						(,bro-sym (with-current-buffer ,buf-sym buffer-read-only)))
			 (with-current-buffer ,buf-sym
				 (unwind-protect
						 (save-excursion
							 (save-restriction
								 (widen)
								 (goto-char (point-min))
								 ;; We don't just let-bind buffer-read-only because that
								 ;; interacts poorly with switching buffers.
								 (setq buffer-read-only (not ,write-sym))
								 (prog1 (save-current-buffer ,@body)
									 (and (buffer-live-p ,buf-sym) ,write-sym
												(if (or ,reused-sym (eq ,visit-sym t))
														(if ,quiet-sym (hide-message (save-buffer))
															(save-buffer))
													(let ((coding-system-for-write
																 (if ,raw-sym 'no-conversion
																	 coding-system-for-write)))
														(widen)
														(write-region (point-min) (point-max) ,file-sym nil
																					(if ,quiet-sym 'lambda))))))))
					 (when (buffer-live-p ,buf-sym)
						 (setq buffer-read-only ,bro-sym)
						 (unless ,reused-sym
							 (set-buffer-modified-p nil)
							 (kill-buffer nil)))
					 (when (buffer-live-p ,extant-sym)
						 (with-current-buffer ,extant-sym
							 (setq buffer-file-name ,extant-file-sym))))))))

(defun with-file-test (file arg)
	"Test `with-file' by inserting at the beginning of FILE.
Use 'silent with prefix ARG.  Use 'raw with two C-u's."
	(interactive "f\nP")
	(with-file file (if (equal arg '(16)) 'raw t) nil (if arg 'silent t)
		(insert "foo\n")
		;; Print some multibyte stuff to demonstrate the action of literal mode.
		(message "%S" (and (re-search-forward "[^\0-\d]" nil t)
											 (match-string 0)))))

[-- Attachment #3: Type: text/plain, Size: 142 bytes --]

_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/emacs-devel

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-07-28  2:14   ` Stuart D. Herring
@ 2006-07-28  2:23     ` Stuart D. Herring
  2006-07-29 15:18     ` Richard Stallman
  1 sibling, 0 replies; 18+ messages in thread
From: Stuart D. Herring @ 2006-07-28  2:23 UTC (permalink / raw)
  Cc: emacs-devel, Mathias Dahl

In my previous message I neglected to mention that I based my `with-file'
macro on Jorgen Schaefer's; while I added a lot to it, I would be remiss
to not acknowledge my starting point.  Sorry-and-thanks, Jorgen.

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or
too sparse, it is because mass-energy conversion has occurred during
shipping.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-07-28  2:14   ` Stuart D. Herring
  2006-07-28  2:23     ` Stuart D. Herring
@ 2006-07-29 15:18     ` Richard Stallman
  2006-08-01  1:06       ` Stuart D. Herring
  1 sibling, 1 reply; 18+ messages in thread
From: Richard Stallman @ 2006-07-29 15:18 UTC (permalink / raw)
  Cc: emacs-devel, mathias.dahl

* This macro has two separate args REUSE and WRITE.
But I think only two combinations make sense: t nil, and nil t.
It is always wrong to reuse an existing buffer if you're going
to modify the file.  And if you're not going to modify it,
there is never any point in not reusing one.

So I think you should get rid of the REUSE argument.

* This is a horrible kludge

		       ;; We need to pretend that any other visiting buffer
		       ;; doesn't actually exist.
		       (if ,extant-sym
			   (with-current-buffer ,extant-sym
			     (setq buffer-file-name nil)))
		       (find-file-noselect ,file-sym t))

and without an unwind-protect it will permanently ruin the other buffer
if an error occurs.  How about this instead?

	  ;; Create a new buffer.
	  (setq buf (create-file-buffer filename))
	  ;; find-file-noselect-1 may use a different buffer.
	  (find-file-noselect-1 buf filename nowarn
				rawfile truename number))))))

* This seems like a mistake:

		 ;; We don't just let-bind buffer-read-only because that
		 ;; interacts poorly with switching buffers.
		 (setq buffer-read-only (not ,write-sym))

What is the problem you're concerned about?

*  Use 'silent with prefix ARG.  Use 'raw with two C-u's."

Please write `silent' and `raw'; that is our convention.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-07-29 15:18     ` Richard Stallman
@ 2006-08-01  1:06       ` Stuart D. Herring
  2006-08-07  5:01         ` Richard Stallman
  0 siblings, 1 reply; 18+ messages in thread
From: Stuart D. Herring @ 2006-08-01  1:06 UTC (permalink / raw)
  Cc: emacs-devel

> * This macro has two separate args REUSE and WRITE.
> But I think only two combinations make sense: t nil, and nil t.
> It is always wrong to reuse an existing buffer if you're going
> to modify the file.  And if you're not going to modify it,
> there is never any point in not reusing one.

If we use REUSE nil and WRITE t, then if the user has visited the file
(which could easily be a file for which WRITE t makes sense, e.g.
~/.emacs.d/.whatever-package-rc) and has unsaved changes in it, those are
effectively lost because they may clash with the updated file.  Even if
the user has not modified the buffer, the odd result is that the buffer is
out of date as a result of actions within Emacs.

Moreover, using REUSE nil can be useful if we want to read the actual file
on disk and not whatever is in a buffer that is visiting the file (which
could be a temporary modification, or an unstable version, or...).  It
also avoids any complications of file-local variables and so forth (so
long as VISIT is nil or 'raw), or subsequent buffer-but-not-file effects
(selective-display with ^M, user-added buffer-local variables, etc.).

> * This is a horrible kludge
>
> 		       ;; We need to pretend that any other visiting buffer
> 		       ;; doesn't actually exist.
> 		       (if ,extant-sym
> 			   (with-current-buffer ,extant-sym
> 			     (setq buffer-file-name nil)))
> 		       (find-file-noselect ,file-sym t))
>
> and without an unwind-protect it will permanently ruin the other buffer
> if an error occurs.  How about this instead?

I'll grant that it's a kludge, but I don't know of a better way (I respond
to your suggestion below).  There is in fact an unwind-protect on the
majority of the code that will undo that effect; however, I realize now
that it will not run if `find-file-noselect' exits abnormally.  Failing a
complete rewrite of this mechanism, I'll make sure that the kludge is
protected more thoroughly.

> 	  ;; Create a new buffer.
> 	  (setq buf (create-file-buffer filename))
> 	  ;; find-file-noselect-1 may use a different buffer.
> 	  (find-file-noselect-1 buf filename nowarn
> 				rawfile truename number))))))

Three things:
1. Thanks for pointing me at `find-file-noselect-1'; I didn't realize it
before, but it's exactly what I want here.  I'll certainly use it.
2. Should we call `set-buffer-major-mode' here, like `find-file-noselect'
does?
3. This does allow visiting a buffer repeatedly, but it doesn't entirely
obviate the need for the kludge because the code that checks for a file
being modified on disk since you read it when you try to edit it (my
apologies that I don't know a name for this code) will notice another
buffer visiting the file and will react if -its- modtime is out of date,
even if the modtime of the edited buffer is recent.  I discovered this
while testing; I should have mentioned it in comments.
Of course, I guess my kludge just reduces it to a race condition, where if
the file is modified externally during the execution of `with-file' and
another modification is made, the warning will appear anyway.  Then again,
is there anywhere in Emacs that we attempt to avoid that?
Anyway, is there a better way to avoid the modtime problems? 
`find-file-noselect-1' solves everything else.

> * This seems like a mistake:
>
> 		 ;; We don't just let-bind buffer-read-only because that
> 		 ;; interacts poorly with switching buffers.
> 		 (setq buffer-read-only (not ,write-sym))
>
> What is the problem you're concerned about?

See (elisp)Intro to Buffer-Local; I was avoiding the problems discussed
with the text "you can get Emacs very confused by binding the variable"
near the bottom.  I realize now, re-reading that, that a `save-excursion'
(or even just `save-current-buffer', I guess) can prevent problems here...
but what happens if the buffer is killed before exiting the `let'?  A
quick test on the 21.3 I have in front of me now seems to indicate that
nothing bad happens, but I'd like some more assurance if I can get it.  If
it's true that nothing will get hurt then, I'll just let-bind
`buffer-read-only' inside the `prog1' there and around the
`save-current-buffer'.

> *  Use 'silent with prefix ARG.  Use 'raw with two C-u's."
>
> Please write `silent' and `raw'; that is our convention.

Sorry.  It was a quick docstring for a testing function, written without
thought, told by an idiot, full of sound and fury, signifying nothing.

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or
too sparse, it is because mass-energy conversion has occurred during
shipping.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-08-01  1:06       ` Stuart D. Herring
@ 2006-08-07  5:01         ` Richard Stallman
  2006-08-07 21:38           ` Stuart D. Herring
  0 siblings, 1 reply; 18+ messages in thread
From: Richard Stallman @ 2006-08-07  5:01 UTC (permalink / raw)
  Cc: emacs-devel

    If we use REUSE nil and WRITE t, then if the user has visited the file
    (which could easily be a file for which WRITE t makes sense, e.g.
    ~/.emacs.d/.whatever-package-rc) and has unsaved changes in it, those are
    effectively lost because they may clash with the updated file.

Maybe that is correct.  It would be as if some other unrelated program
had altered the file.  If the person tries to save his buffer, Emacs
will report that the file has changed.

To use the user's existing buffer would save the user's own unsaved
changes without asking.  That is completely unacceptable.

I can see one other conceivable way to handle this case: to merge the
changes that the program makes into the user's own buffer, imitating
the example of CVS merging remote and local changes.  But that is not
easy to do.

Unless we implement such merging, I stand by what I said:
WRITE = t requires REUSE = nil.

I don't follow this part:

    3. This does allow visiting a buffer repeatedly, but it doesn't entirely
    obviate the need for the kludge because the code that checks for a file
    being modified on disk since you read it when you try to edit it (my
    apologies that I don't know a name for this code)

I can't make sense of all those "it"s.

						      will notice another
    buffer visiting the file and will react if -its- modtime is out of date,
    even if the modtime of the edited buffer is recent.  I discovered this
    while testing; I should have mentioned it in comments.

Please show me the code you're talking about; it is the only way I can
understand this.

    See (elisp)Intro to Buffer-Local; I was avoiding the problems discussed
    with the text "you can get Emacs very confused by binding the variable"
    near the bottom.  I realize now, re-reading that, that a `save-excursion'
    (or even just `save-current-buffer', I guess) can prevent problems here...
    but what happens if the buffer is killed before exiting the `let'?

Nothing bad happens.  Don't worry about this.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-08-07  5:01         ` Richard Stallman
@ 2006-08-07 21:38           ` Stuart D. Herring
  2006-08-08 18:01             ` Richard Stallman
  2006-08-08 18:01             ` Richard Stallman
  0 siblings, 2 replies; 18+ messages in thread
From: Stuart D. Herring @ 2006-08-07 21:38 UTC (permalink / raw)
  Cc: emacs-devel

>     If we use REUSE nil and WRITE t, then if the user has visited the file
>     (which could easily be a file for which WRITE t makes sense, e.g.
>     ~/.emacs.d/.whatever-package-rc) and has unsaved changes in it, those
> are
>     effectively lost because they may clash with the updated file.
>
> Maybe that is correct.  It would be as if some other unrelated program
> had altered the file.  If the person tries to save his buffer, Emacs
> will report that the file has changed.
>
> To use the user's existing buffer would save the user's own unsaved
> changes without asking.  That is completely unacceptable.

Is it completely unacceptable even if the file in question is principally
"owned" by code anyway?  I can't imagine that `with-file' would ever be
invoked on someone's source code unless it was a specialized tool that
would (via confirmation or so) verify the user's intent.

> I can see one other conceivable way to handle this case: to merge the
> changes that the program makes into the user's own buffer, imitating
> the example of CVS merging remote and local changes.  But that is not
> easy to do.

I see other possibilities:
1. (and REUSE WRITE (buffer-modified-p #<buffer extant-buffer>) (error
"Attempt to co-opt user's unsaved changes")) - that is, only allow REUSE
and WRITE on an unmodified buffer.
2. Go on modifying (because of WRITE) the buffer (because of REUSE), but
don't save the changes at the end, or else ask the user whether to save
them.

Either of these could be combined with a query beforehand:
"Lisp wants to modify foobar.c.  (S)ave your changes first or (r)evert the
buffer first?  (s or r)"
(Of course, C-g would also be a useful response.)

> Unless we implement such merging, I stand by what I said:
> WRITE = t requires REUSE = nil.

I'd really like to keep the option, but if we can't find a reasonable
solution I'll do that.

> Please show me the code you're talking about; it is the only way I can
> understand this.

OK, I've done my homework now.  The querying function is
`ask-user-about-supersession-threat'; it's called from lock_file() in
filelock.c, which uses get_truename_buffer() to find the buffer whose
modification time to examine.  This means that if an existing buffer
visiting the file is out of date, and get_truename_buffer() happens to
return it instead of the up-to-date buffer created by `with-file', the
supersession query will be raised.

I don't know what the correct approach is here (without rewriting
lock_file()): should I `flet' `ask-user-about-supersession-threat', or
what?

My current version of `with-file' does avoid losing the proper file name
by destroying its filename only after entering the `unwind-protect' that
restores it, and restoring it as the first unwind action.  (This was made
much simpler by the use of `find-file-noselect-1', as it reduced the
period of time during which the file name needed to be hidden.)

> Nothing bad happens.  Don't worry about this.

OK.  Worrying halted (and `buffer-read-only' let-bound).

Davis

PS - While investigating this, I discovered the variable `buffer_count' in
buffer.c: it seems to never be used by any code anywhere.  Is it obsolete?

-- 
This product is sold by volume, not by mass.  If it appears too dense or
too sparse, it is because mass-energy conversion has occurred during
shipping.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-08-07 21:38           ` Stuart D. Herring
@ 2006-08-08 18:01             ` Richard Stallman
  2006-08-08 18:32               ` Stuart D. Herring
  2006-08-08 18:01             ` Richard Stallman
  1 sibling, 1 reply; 18+ messages in thread
From: Richard Stallman @ 2006-08-08 18:01 UTC (permalink / raw)
  Cc: emacs-devel

    > To use the user's existing buffer would save the user's own unsaved
    > changes without asking.  That is completely unacceptable.

    Is it completely unacceptable even if the file in question is principally
    "owned" by code anyway?

Yes, I think so.  If it is unlikely that users will edit the file by
hand, that means there is unlikely to be a buffer to reuse.  But IF
there is a buffer to reuse, it means the user edited the file by hand.
When he does so, you should not save his changes without his ok!

    I see other possibilities:
    1. (and REUSE WRITE (buffer-modified-p #<buffer extant-buffer>) (error
    "Attempt to co-opt user's unsaved changes")) - that is, only allow REUSE
    and WRITE on an unmodified buffer.

It is better to go ahead not reusing the buffer than to signal an
error.

    2. Go on modifying (because of WRITE) the buffer (because of REUSE), but
    don't save the changes at the end, or else ask the user whether to save
    them.

That would be very inconvenient.  There is no need to implement that
option, and it would be better to avoid it for simplicity's sake.

    Either of these could be combined with a query beforehand:

Any query will be an inconvenience.  That's another reason now to
implement these options.

Complexity is a drawback, not a feature.  I am looking for ways
to simplify this.  We should not reject them because of maybes.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-08-07 21:38           ` Stuart D. Herring
  2006-08-08 18:01             ` Richard Stallman
@ 2006-08-08 18:01             ` Richard Stallman
  2006-08-16 19:59               ` Stuart D. Herring
  1 sibling, 1 reply; 18+ messages in thread
From: Richard Stallman @ 2006-08-08 18:01 UTC (permalink / raw)
  Cc: emacs-devel

    OK, I've done my homework now.  The querying function is
    `ask-user-about-supersession-threat'; it's called from lock_file() in
    filelock.c, which uses get_truename_buffer() to find the buffer whose
    modification time to examine.  This means that if an existing buffer
    visiting the file is out of date, and get_truename_buffer() happens to
    return it instead of the up-to-date buffer created by `with-file', the
    supersession query will be raised.

I see.  When there is more than one candidate, get_truename_buffer
picks the one that comes first in the buffer list.  So the cleanest
solution would be to make sure it always finds with-file's buffer.

The simplest and most natural way is to put that buffer at the front
of the buffer list.  There are kludgy ways to do this from Lisp, but
the cleanest way is just to make a primitive to do it.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-08-08 18:01             ` Richard Stallman
@ 2006-08-08 18:32               ` Stuart D. Herring
  2006-08-09  4:58                 ` Richard Stallman
  0 siblings, 1 reply; 18+ messages in thread
From: Stuart D. Herring @ 2006-08-08 18:32 UTC (permalink / raw)
  Cc: emacs-devel

>     Is it completely unacceptable even if the file in question is
> principally
>     "owned" by code anyway?
>
> Yes, I think so.  If it is unlikely that users will edit the file by
> hand, that means there is unlikely to be a buffer to reuse.  But IF
> there is a buffer to reuse, it means the user edited the file by hand.
> When he does so, you should not save his changes without his ok!

It means the user -visited- the file explicitly.  He may or may not have
been interested in changing it by hand.  I don't know, however, if this
distinction is important.

>     I see other possibilities:
>     1. (and REUSE WRITE (buffer-modified-p #<buffer extant-buffer>) (error
>     "Attempt to co-opt user's unsaved changes")) - that is, only allow
> REUSE
>     and WRITE on an unmodified buffer.
>
> It is better to go ahead not reusing the buffer than to signal an
> error.

Aha -- perhaps there's a good "compromise" here.  What if REUSE is treated
as nil if the extant buffer is modified and WRITE is non-nil?  Since the
file may be visited using the wrong literality, there is already no
guarantee that REUSE "does anything", so it's not drastically different. 
There is still the problem of the user's unsaved changes clashing, but
client code can always detect this eventuality if it's important, and it
can happen anyway (the user could intend to C-x C-w another buffer into
the file, or the literality condition could apply to a modified buffer,
or...).

It would be more consistent to categorically only reuse unmodified buffers
-- but for just reading the file, it can make sense to see the user's
changes (if any), and any program which wanted to avoid "unstable" file
contents can just pass REUSE as nil anyway.

The relevant section of the doc string would then read

REUSE non-nil means to use an existing buffer if it is visiting FILE. 
(The value of `find-file-existing-other-name' affects this determination.)
 The buffer will only be re-used if its literal status (see
`find-file-literally') corresponds to RAW.  To avoid interacting with a
user's unsaved changes, the buffer will be ignored if it is modified and
WRITE is non-nil.  Note that reusing a buffer may mean that the text on
which BODY operates differs from the actual contents of FILE.

Should I add a note that the user's buffer can become outdated as a result
of failed or unattempted reuse of it?

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or
too sparse, it is because mass-energy conversion has occurred during
shipping.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-08-08 18:32               ` Stuart D. Herring
@ 2006-08-09  4:58                 ` Richard Stallman
  0 siblings, 0 replies; 18+ messages in thread
From: Richard Stallman @ 2006-08-09  4:58 UTC (permalink / raw)
  Cc: emacs-devel

    > Yes, I think so.  If it is unlikely that users will edit the file by
    > hand, that means there is unlikely to be a buffer to reuse.  But IF
    > there is a buffer to reuse, it means the user edited the file by hand.
    > When he does so, you should not save his changes without his ok!

    It means the user -visited- the file explicitly.  He may or may not have
    been interested in changing it by hand.  I don't know, however, if this
    distinction is important.

If he didn't decide to change it by hand, then either (1) the buffer
is unmodified, or (2) he changed it unwittingly.

    Aha -- perhaps there's a good "compromise" here.  What if REUSE is treated
    as nil if the extant buffer is modified and WRITE is non-nil?

It would work, but getting rid of the REUSE argument is much better
because it makes this macro simpler to use.

    Should I add a note that the user's buffer can become outdated as a result
    of failed or unattempted reuse of it?

No need.  There are lots of ways a file could be changed on disk while
it is visited in an Emacs buffer; there is no particular reason to
distinguish this way from all the rest.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-08-08 18:01             ` Richard Stallman
@ 2006-08-16 19:59               ` Stuart D. Herring
  2006-08-17 15:18                 ` Richard Stallman
  0 siblings, 1 reply; 18+ messages in thread
From: Stuart D. Herring @ 2006-08-16 19:59 UTC (permalink / raw)
  Cc: emacs-devel

> I see.  When there is more than one candidate, get_truename_buffer
> picks the one that comes first in the buffer list.  So the cleanest
> solution would be to make sure it always finds with-file's buffer.
>
> The simplest and most natural way is to put that buffer at the front
> of the buffer list.  There are kludgy ways to do this from Lisp, but
> the cleanest way is just to make a primitive to do it.

`record_buffer' does precisely this (along with promoting the buffer in
its frame's list).  Should this be made callable from Lisp so that such
things as `with-file' can use it?

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or
too sparse, it is because mass-energy conversion has occurred during
shipping.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: Missing `with' macro?
  2006-08-16 19:59               ` Stuart D. Herring
@ 2006-08-17 15:18                 ` Richard Stallman
  0 siblings, 0 replies; 18+ messages in thread
From: Richard Stallman @ 2006-08-17 15:18 UTC (permalink / raw)
  Cc: emacs-devel

    `record_buffer' does precisely this (along with promoting the buffer in
    its frame's list).  Should this be made callable from Lisp so that such
    things as `with-file' can use it?

Yes, I think so.

But let's do it after the release.

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2006-08-17 15:18 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-07-24 10:17 Missing `with' macro? Mathias Dahl
2006-07-24 13:46 ` Stefan Monnier
2006-07-24 14:33   ` Mathias Dahl
2006-07-24 18:22 ` Richard Stallman
2006-07-24 20:55   ` Jorgen Schaefer
2006-07-25  3:09     ` Richard Stallman
2006-07-28  2:14   ` Stuart D. Herring
2006-07-28  2:23     ` Stuart D. Herring
2006-07-29 15:18     ` Richard Stallman
2006-08-01  1:06       ` Stuart D. Herring
2006-08-07  5:01         ` Richard Stallman
2006-08-07 21:38           ` Stuart D. Herring
2006-08-08 18:01             ` Richard Stallman
2006-08-08 18:32               ` Stuart D. Herring
2006-08-09  4:58                 ` Richard Stallman
2006-08-08 18:01             ` Richard Stallman
2006-08-16 19:59               ` Stuart D. Herring
2006-08-17 15:18                 ` Richard Stallman

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).