10

Context

When emacs detects that a file was changed outside of an editing buffer.

Observed

emacs will ask:

somefilename changed in disk; really edit the buffer? (y, n, r or C-h)

(Incidentally, sometimes it happens even when there is no actual change, e.g. remote file on a server with drifted clock, but the question is interesting in all cases.)

Wished

emacs would ask:

somefilename changed in disk; really edit the buffer? (y, n, r, d or C-h)

Pressing d would show the difference between versions, e.g. ediff-current-file which allows to walk the differences interactively.

Additional information

That would be similar to what Debian package management does when it detects that a config file locally customized gets updated by a newer version of its owning package. For an example see A new version of configuration file /etc/default/grub is available, but the version installed currently has been locally modified - Unix & Linux Stack Exchange

Search before you ask

I usually find my way to solutions (e.g. emacsclient - From an external script, open file and run some simple expression whether emacs already running or not - Emacs Stack Exchange) but after searching on this I could not find any pre-existing solution.

I'd consider adjusting this myself but am not proficient enough in emacs-lisp and emacs internals.

Sketch of solution

  • Pressing C-g then 'M-x ediff-current-file` does the job, at the cost of some keystrokes.
  • The goal here would be to run ediff-current-file on one keypress at the above prompt.
2

grepping for "really edit the buffer" in emacs source I found function ask-user-about-supersession-threat in file userlock.el.

It seems straightforward to add a d choice for calling ediff-current-file. I haven't tested this extensively, though (edits marked with ;;- comments).

(defun ask-user-about-supersession-threat (fn)
  "Ask a user who is about to modify an obsolete buffer what to do.
This function has two choices: it can return, in which case the modification
of the buffer will proceed, or it can (signal 'file-supersession (file)),
in which case the proposed buffer modification will not be made.

You can rewrite this to use any criterion you like to choose which one to do.
The buffer in question is current when this function is called."
  (discard-input)
  (save-window-excursion
    (let ((prompt
       (format "%s changed on disk; \
really edit the buffer? (y, n, r, d or C-h) " ;;- changed
           (file-name-nondirectory fn)))
      (choices '(?y ?n ?r ?d ?? ?\C-h))       ;;- changed
      answer)
      (while (null answer)
    (setq answer (read-char-choice prompt choices))
    (cond ((memq answer '(?? ?\C-h))
           (ask-user-about-supersession-help)
           (setq answer nil))
          ((eq answer ?r)
           ;; Ask for confirmation if buffer modified
           (revert-buffer nil (not (buffer-modified-p)))
           (signal 'file-supersession
               (list "File reverted" fn)))
          ((eq answer ?d)                     ;;- added
           (ediff-current-file))              ;;- added
          ((eq answer ?n)
           (signal 'file-supersession
               (list "File changed on disk" fn)))))
      (message
       "File on disk now will become a backup file if you save these changes.")
      (setq buffer-backed-up nil))))
  • Seems to work well on emacs 46.1 on Ubuntu 16.04. Thanks! – Stéphane Gourichon Oct 23 '16 at 7:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.