Reimplement recent with-silent-modifications auto-save changes

* doc/lispref/buffers.texi (Buffer Modification): Document
buffer-modified-p returning `autosaved'.
* lisp/subr.el (with-silent-modifications): Use
restore-buffer-modified-p instead of altering the buffer modiff
(since this has other side effects like not updating after async
`display' changes.

* src/buffer.c (Fbuffer_modified_p): Allow returning whether the
buffer has been autosaved after changes.
(Frestore_buffer_modified_p): Allow adjusting whether the buffer
has been autosaved after changes.

* src/fileio.c (Fdo_auto_save): Refill the doc string.
This commit is contained in:
Lars Ingebrigtsen 2022-05-10 03:38:01 +02:00
parent 0b2f550e32
commit 0bee4cda88
5 changed files with 86 additions and 29 deletions

View file

@ -541,10 +541,12 @@ file formerly visited.
@ref{Text}.
@defun buffer-modified-p &optional buffer
This function returns @code{t} if the buffer @var{buffer} has been modified
since it was last read in from a file or saved, or @code{nil}
otherwise. If @var{buffer} is not supplied, the current buffer
is tested.
This function returns non-@code{nil} if the buffer @var{buffer} has
been modified since it was last read in from a file or saved, or
@code{nil} otherwise. If @var{buffer} has been autosaved after
@var{buffer} was last modified, the symbol @code{autosaved} is
returned. If @var{buffer} is not supplied, the current buffer is
tested.
@end defun
@defun set-buffer-modified-p flag

View file

@ -4594,21 +4594,17 @@ like `buffer-modified-p', checking whether the file is locked by
someone else, running buffer modification hooks, and other things
of that nature."
(declare (debug t) (indent 0))
(let ((modified (make-symbol "modified"))
(tick (make-symbol "tick")))
(let ((modified (make-symbol "modified")))
`(let* ((,modified (buffer-modified-p))
(,tick (buffer-modified-tick))
(buffer-undo-list t)
(inhibit-read-only t)
(inhibit-modification-hooks t))
(unwind-protect
(progn
,@body)
;; We restore the buffer tick count, too, because otherwise
;; we'll trigger a new auto-save.
(internal--set-buffer-modified-tick ,tick)
(unless ,modified
(restore-buffer-modified-p nil))))))
(when (or (not ,modified)
(eq ,modified 'autosaved))
(restore-buffer-modified-p ,modified))))))
(defmacro with-output-to-string (&rest body)
"Execute BODY, return the text it sent to `standard-output', as a string."

View file

@ -1376,12 +1376,23 @@ No argument or nil as argument means use current buffer as BUFFER. */)
DEFUN ("buffer-modified-p", Fbuffer_modified_p, Sbuffer_modified_p,
0, 1, 0,
doc: /* Return t if BUFFER was modified since its file was last read or saved.
No argument or nil as argument means use current buffer as BUFFER. */)
doc: /* Return non-nil if BUFFER was modified since its file was last read or saved.
No argument or nil as argument means use current buffer as BUFFER.
If BUFFER has been autosaved after BUFFER was last modified, the
symbol `autosaved' is returned. */)
(Lisp_Object buffer)
{
struct buffer *buf = decode_buffer (buffer);
return BUF_SAVE_MODIFF (buf) < BUF_MODIFF (buf) ? Qt : Qnil;
if (BUF_SAVE_MODIFF (buf) < BUF_MODIFF (buf))
{
if (BUF_AUTOSAVE_MODIFF (buf) == BUF_MODIFF (buf))
return Qautosaved;
else
return Qt;
}
else
return Qnil;
}
DEFUN ("force-mode-line-update", Fforce_mode_line_update,
@ -1436,6 +1447,11 @@ and `buffer-file-truename' are non-nil. */)
DEFUN ("restore-buffer-modified-p", Frestore_buffer_modified_p,
Srestore_buffer_modified_p, 1, 1, 0,
doc: /* Like `set-buffer-modified-p', but doesn't redisplay buffer's mode line.
A nil FLAG means to mark the buffer as unmodified. A non-nil FLAG
means mark the buffer as modified, except the special value
`autosaved', which will instead mark the buffer as having been
autosaved.
This function also locks or unlocks the file visited by the buffer,
if both `buffer-file-truename' and `buffer-file-name' are non-nil.
@ -1475,16 +1491,19 @@ state of the current buffer. Use with care. */)
recent-auto-save-p from t to nil.
Vice versa, if FLAG is non-nil and SAVE_MODIFF>=auto_save_modified
we risk changing recent-auto-save-p from nil to t. */
SAVE_MODIFF = (NILP (flag)
/* FIXME: This unavoidably sets recent-auto-save-p to nil. */
? MODIFF
/* Let's try to preserve recent-auto-save-p. */
: SAVE_MODIFF < MODIFF ? SAVE_MODIFF
/* If SAVE_MODIFF == auto_save_modified == MODIFF,
we can either decrease SAVE_MODIFF and auto_save_modified
or increase MODIFF. */
: modiff_incr (&MODIFF));
if (NILP (flag))
/* This unavoidably sets recent-auto-save-p to nil. */
SAVE_MODIFF = MODIFF;
else
{
if (EQ (flag, Qautosaved))
BUF_AUTOSAVE_MODIFF (b) = MODIFF;
/* If SAVE_MODIFF == auto_save_modified == MODIFF, we can either
decrease SAVE_MODIFF and auto_save_modified or increase
MODIFF. */
else if (SAVE_MODIFF >= MODIFF)
SAVE_MODIFF = modiff_incr (&MODIFF);
}
return flag;
}
@ -6465,5 +6484,7 @@ will run for `clone-indirect-buffer' calls as well. */);
defsubr (&Soverlay_put);
defsubr (&Srestore_buffer_modified_p);
DEFSYM (Qautosaved, "autosaved");
Fput (intern_c_string ("erase-buffer"), Qdisabled, Qt);
}

View file

@ -5972,14 +5972,17 @@ do_auto_save_eh (Lisp_Object ignore)
DEFUN ("do-auto-save", Fdo_auto_save, Sdo_auto_save, 0, 2, "",
doc: /* Auto-save all buffers that need it.
This is all buffers that have auto-saving enabled
and are changed since last auto-saved.
Auto-saving writes the buffer into a file
so that your editing is not lost if the system crashes.
This is all buffers that have auto-saving enabled and are changed
since last auto-saved.
Auto-saving writes the buffer into a file so that your editing is not
lost if the system crashes.
This file is not the file you visited; that changes only when you save.
Normally, run the normal hook `auto-save-hook' before saving.
A non-nil NO-MESSAGE argument means do not print any message if successful.
A non-nil CURRENT-ONLY argument means save only current buffer. */)
(Lisp_Object no_message, Lisp_Object current_only)
{

View file

@ -1482,4 +1482,39 @@ with parameters from the *Messages* buffer modification."
(when auto-save
(ignore-errors (delete-file auto-save))))))))
(ert-deftest test-buffer-modifications ()
(ert-with-temp-file file
(with-current-buffer (find-file file)
(auto-save-mode 1)
(should-not (buffer-modified-p))
(insert "foo")
(should (buffer-modified-p))
(should-not (eq (buffer-modified-p) 'autosaved))
(do-auto-save nil t)
(should (eq (buffer-modified-p) 'autosaved))
(with-silent-modifications
(put-text-property 1 3 'face 'bold))
(should (eq (buffer-modified-p) 'autosaved))
(save-buffer)
(should-not (buffer-modified-p))
(with-silent-modifications
(put-text-property 1 3 'face 'italic))
(should-not (buffer-modified-p)))))
(ert-deftest test-restore-buffer-modified-p ()
(ert-with-temp-file file
(with-current-buffer (find-file file)
(auto-save-mode 1)
(should-not (buffer-modified-p))
(insert "foo")
(should (buffer-modified-p))
(restore-buffer-modified-p nil)
(should-not (buffer-modified-p))
(insert "bar")
(do-auto-save nil t)
(should (eq (buffer-modified-p) 'autosaved))
(insert "zot")
(restore-buffer-modified-p 'autosaved)
(should (eq (buffer-modified-p) 'autosaved)))))
;;; buffer-tests.el ends here