New command: revert-buffer-with-fine-grain
* doc/emacs/files.texi (Reverting): Document the new command and the new variable. * etc/NEWS: Mention the new command and the new variable. * lisp/files.el (revert-buffer-with-fine-grain): New command. Revert a buffer trying to be non-destructive, by using replace-buffer-contents. (revert-buffer-insert-file-contents-delicately): New function, alternative to revert-buffer-insert-file-contents-function--default-function. (revert-buffer-with-fine-grain-max-seconds): New variable. Passed as argument MAX-SECS of replace-buffer-contents. * test/lisp/files-tests.el (files-tests-lao files-tests-tzu): Helper variables, taken from diffutils manual, to test reverting a buffer. (files-tests-revert-buffer) (files-tests-revert-buffer-with-fine-grain): New tests (bug#18).
This commit is contained in:
parent
4d184fb07f
commit
1278a9a907
4 changed files with 152 additions and 0 deletions
|
@ -921,6 +921,7 @@ Manual}). For customizations, see the Custom group @code{time-stamp}.
|
|||
@node Reverting
|
||||
@section Reverting a Buffer
|
||||
@findex revert-buffer
|
||||
@findex revert-buffer-with-fine-grain
|
||||
@cindex drastic changes
|
||||
@cindex reread a file
|
||||
|
||||
|
@ -941,6 +942,19 @@ reverted changes as a single modification to the buffer's undo history
|
|||
aliases to bring the reverted changes back, if you happen to change
|
||||
your mind.
|
||||
|
||||
@vindex revert-buffer-with-fine-grain-max-seconds
|
||||
To revert a buffer more conservatively, you can use the command
|
||||
@code{revert-buffer-with-fine-grain}. This command acts like
|
||||
@code{revert-buffer}, but it tries to be as non-destructive as
|
||||
possible, making an effort to preserve all markers, properties and
|
||||
overlays in the buffer. Since reverting this way can be very slow
|
||||
when you have made a large number of changes, you can modify the
|
||||
variable @code{revert-buffer-with-fine-grain-max-seconds} to
|
||||
specify a maximum amount of seconds that replacing the buffer
|
||||
contents this way should take. Note that it is not ensured that the
|
||||
whole execution of @code{revert-buffer-with-fine-grain} won't take
|
||||
longer than this.
|
||||
|
||||
Some kinds of buffers that are not associated with files, such as
|
||||
Dired buffers, can also be reverted. For them, reverting means
|
||||
recalculating their contents. Buffers created explicitly with
|
||||
|
|
8
etc/NEWS
8
etc/NEWS
|
@ -193,6 +193,14 @@ Completion of command names now considers obsolete aliases as
|
|||
candidates. Invoking a command via an obsolete alias now mentions the
|
||||
obsolescence fact and shows the new name of the command.
|
||||
|
||||
+++
|
||||
** New command 'revert-buffer-with-fine-grain'.
|
||||
Revert a buffer trying to be as non-destructive as possible,
|
||||
preserving markers, properties and overlays. The new variable
|
||||
'revert-buffer-with-fine-grain-max-seconds' specifies the maximum
|
||||
number of seconds that 'revert-buffer-with-fine-grain' should spend
|
||||
trying to be non-destructive.
|
||||
|
||||
|
||||
* Changes in Specialized Modes and Packages in Emacs 28.1
|
||||
|
||||
|
|
|
@ -6254,6 +6254,82 @@ an auto-save file."
|
|||
(insert-file-contents file-name (not auto-save-p)
|
||||
nil nil t))))))
|
||||
|
||||
(defvar revert-buffer-with-fine-grain-max-seconds 2.0
|
||||
"Maximum time that `revert-buffer-with-fine-grain' should use.
|
||||
The command tries to preserve markers, properties and overlays.
|
||||
If the operation takes more than this time, a single
|
||||
delete+insert is performed. Actually, this value is passed as
|
||||
the MAX-SECS argument to the function `replace-buffer-contents',
|
||||
so it is not ensured that the whole execution won't take longer.
|
||||
See `replace-buffer-contents' for more details.")
|
||||
|
||||
(defun revert-buffer-insert-file-contents-delicately (file-name _auto-save-p)
|
||||
"Optional function for `revert-buffer-insert-file-contents-function'.
|
||||
The function `revert-buffer-with-fine-grain' uses this function by binding
|
||||
`revert-buffer-insert-file-contents-function' to it.
|
||||
|
||||
As with `revert-buffer-insert-file-contents--default-function', FILE-NAME is
|
||||
the name of the file and AUTO-SAVE-P is non-nil if this is an auto-save file.
|
||||
Since calling `replace-buffer-contents' can take a long time, depending of
|
||||
the number of changes made to the buffer, it uses the value of the variable
|
||||
`revert-buffer-with-fine-grain-max-seconds' as a maximum time to try delicately
|
||||
reverting the buffer. If it fails, it does a delete+insert. For more details,
|
||||
see `replace-buffer-contents'."
|
||||
(cond
|
||||
((not (file-exists-p file-name))
|
||||
(error (if buffer-file-number
|
||||
"File %s no longer exists"
|
||||
"Cannot revert nonexistent file %s")
|
||||
file-name))
|
||||
((not (file-readable-p file-name))
|
||||
(error (if buffer-file-number
|
||||
"File %s no longer readable"
|
||||
"Cannot revert unreadable file %s")
|
||||
file-name))
|
||||
(t
|
||||
(let* ((buf (current-buffer)) ; current-buffer is the buffer to revert.
|
||||
(success
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file-name)
|
||||
(let ((temp-buf (current-buffer)))
|
||||
(set-buffer buf)
|
||||
(let ((buffer-file-name nil))
|
||||
(replace-buffer-contents
|
||||
temp-buf
|
||||
revert-buffer-with-fine-grain-max-seconds))))))))
|
||||
;; See comments in revert-buffer-with-fine-grain for an explanation.
|
||||
(defun revert-buffer-with-fine-grain-success-p ()
|
||||
success))
|
||||
(set-buffer-modified-p nil))))
|
||||
|
||||
(defun revert-buffer-with-fine-grain (&optional ignore-auto noconfirm)
|
||||
"Revert buffer preserving markers, overlays, etc.
|
||||
This command is an alternative to `revert-buffer' because it tries to be as
|
||||
non-destructive as possible, preserving markers, properties and overlays.
|
||||
Binds `revert-buffer-insert-file-contents-function' to the function
|
||||
`revert-buffer-insert-file-contents-delicately'.
|
||||
|
||||
With a prefix argument, offer to revert from latest auto-save file. For more
|
||||
details on the arguments, see `revert-buffer'."
|
||||
;; See revert-buffer for an explanation of this.
|
||||
(interactive (list (not current-prefix-arg)))
|
||||
;; Simply bind revert-buffer-insert-file-contents-function to the specialized
|
||||
;; function, and call revert-buffer.
|
||||
(let ((revert-buffer-insert-file-contents-function
|
||||
#'revert-buffer-insert-file-contents-delicately))
|
||||
(revert-buffer ignore-auto noconfirm t)
|
||||
;; This closure is defined in revert-buffer-insert-file-contents-function.
|
||||
;; It is needed because revert-buffer--default always returns t after
|
||||
;; reverting, and it might be needed to report the success/failure of
|
||||
;; reverting delicately.
|
||||
(when (fboundp 'revert-buffer-with-fine-grain-success-p)
|
||||
(prog1
|
||||
(revert-buffer-with-fine-grain-success-p)
|
||||
(fmakunbound 'revert-buffer-with-fine-grain-success-p)))))
|
||||
|
||||
(defun recover-this-file ()
|
||||
"Recover the visited file--get contents from its last auto-save file."
|
||||
(interactive)
|
||||
|
|
|
@ -1377,5 +1377,59 @@ See <https://debbugs.gnu.org/36401>."
|
|||
(normal-mode)
|
||||
(should (eq major-mode 'mhtml-mode))))
|
||||
|
||||
(defvar files-tests-lao "The Way that can be told of is not the eternal Way;
|
||||
The name that can be named is not the eternal name.
|
||||
The Nameless is the origin of Heaven and Earth;
|
||||
The Named is the mother of all things.
|
||||
Therefore let there always be non-being,
|
||||
so we may see their subtlety,
|
||||
And let there always be being,
|
||||
so we may see their outcome.
|
||||
The two are the same,
|
||||
But after they are produced,
|
||||
they have different names.
|
||||
")
|
||||
|
||||
(defvar files-tests-tzu "The Nameless is the origin of Heaven and Earth;
|
||||
The named is the mother of all things.
|
||||
|
||||
Therefore let there always be non-being,
|
||||
so we may see their subtlety,
|
||||
And let there always be being,
|
||||
so we may see their outcome.
|
||||
The two are the same,
|
||||
But after they are produced,
|
||||
they have different names.
|
||||
They both may be called deep and profound.
|
||||
Deeper and more profound,
|
||||
The door of all subtleties!
|
||||
")
|
||||
|
||||
(ert-deftest files-tests-revert-buffer ()
|
||||
"Test that revert-buffer is succesful."
|
||||
(files-tests--with-temp-file temp-file-name
|
||||
(with-temp-buffer
|
||||
(insert files-tests-lao)
|
||||
(write-file temp-file-name)
|
||||
(erase-buffer)
|
||||
(insert files-tests-tzu)
|
||||
(revert-buffer t t t)
|
||||
(should (compare-strings files-tests-lao nil nil
|
||||
(buffer-substring (point-min) (point-max))
|
||||
nil nil)))))
|
||||
|
||||
(ert-deftest files-tests-revert-buffer-with-fine-grain ()
|
||||
"Test that revert-buffer-with-fine-grain is successful."
|
||||
(files-tests--with-temp-file temp-file-name
|
||||
(with-temp-buffer
|
||||
(insert files-tests-lao)
|
||||
(write-file temp-file-name)
|
||||
(erase-buffer)
|
||||
(insert files-tests-tzu)
|
||||
(should (revert-buffer-with-fine-grain t t))
|
||||
(should (compare-strings files-tests-lao nil nil
|
||||
(buffer-substring (point-min) (point-max))
|
||||
nil nil)))))
|
||||
|
||||
(provide 'files-tests)
|
||||
;;; files-tests.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue