37

If I use C-xC-w (write-file) to write the current buffer's content to a new location, then afterwards my buffer will be visiting the new file instead of the original one. Sometimes I would like to save off a copy of the buffer to a new location, but then keep editing at the original location.

I know I can kill the existing filename from the (write-file) minibuffer history, and then yank that back at the C-xC-f (find-file) prompt immediately afterwards to re-visit the original file, and this is the work-around I use at the moment. However, this feels inelegant.

I had wondered if (write-file) might take a prefix argument of some kind to not visit the buffer, but this only appears to affect overwrite confirmation.

So: Is there any simpler way to save my buffer content to a file without altering which file I'm visiting?

6 Answers 6

53

Select the entire buffer with C-xh, then use M-xwrite-region.

Sign up to request clarification or add additional context in comments.

1 Comment

This feels suitably elegant. Thanks!
7

If you do this regularly enough that it's getting annoying for you, you could define this function

(defun write-file-copy (filename) (interactive "F") (write-region (point-min) (point-max) filename)) 

and bind it to something that makes sense to you.

1 Comment

I would wrap (save-restriction (widen) ...) around that (write-region).
7

This is based on Inaimathi's answer and my comment to that, plus some additional tweaks:

  • Offer the current filename for editing by default, as personally I often want a variation on that, and it's slightly annoying not having it there to begin with.
  • Don't overwrite an existing file without asking the user, and never overwrite the current buffer's file.
  • If the region is active, write the region; otherwise write the entire (widened) buffer.
(defun my-write-copy-to-file () "Write a copy of the current buffer or region to a file." (interactive) (let* ((curr (buffer-file-name)) (new (read-file-name "Copy to file: " nil nil nil (and curr (file-name-nondirectory curr)))) (mustbenew (if (and curr (file-equal-p new curr)) 'excl t))) (if (use-region-p) (write-region (region-beginning) (region-end) new nil nil nil mustbenew) (save-restriction (widen) (write-region (point-min) (point-max) new nil nil nil mustbenew))))) (global-set-key (kbd "C-c w") 'my-write-copy-to-file) 

7 Comments

Myself, I'd be tempted to force whole-file writing and simplify the "filename must differ" logic if I went down this route, in the same spirit as (write-file).
Thanks for pointing out (use-region-p) -- that's a great looking function -- and also for catching the need for (widen) around (write-region).
Regarding the first comment, I did wonder if perhaps the function should throw an error directly when file-equal-p, rather than deferring to write-region for that (or even better, prevent you from selecting it in the first place), but I added that protection due to my setting the default value to the current buffer filename (which is useful, but also makes it easy to select that default). As such, I wouldn't remove it -- after all, if you're using this function to save the current buffer to its existing file, you've definitely made a mistake.
Ah, I see your reasoning for keeping the protection in now (and hadn't appreciated that the default value would make it easier to enter the same filename for the destination). As for "you've definitely made a mistake", though, I often find tools being used for purposes I'd not originally expected, so I prefer not to constrain their use unless there's a technical reason to do so. For example, your function would also make quite a good method for forcing a save when C-x C-s says there aren't any changes to save.
@LoremIpsum FYI that value is specifically named for the O_EXCL flag used by the file open system call. pubs.opengroup.org/onlinepubs/007908799/xsh/fcntl.h.html calls it the "Exclusive use flag." See also pubs.opengroup.org/onlinepubs/007908799/xsh/open.html or gnu.org/software/libc/manual/html_node/Open_002dtime-Flags.html
|
5

If you require dired-x by default, or otherwise make the dired-jump function available1, then the following is pretty simple:

C-xC-jC (type new name) RETq

Which is:

  • Jump to dired, with point at the file you came from
  • Copy that file
  • Exit dired, returning to the file buffer

1 C-hig (dired-x) Optional Installation Dired Jump RET

1 Comment

If I were more of a regular Dired user then I'd probably go with this answer -- this is a good incentive to (require 'dired-x) and get dired-jump.
3

After reading phils' (dired-jump) answer, the following approach occurred to me for people who are less familiar with Dired:

M - !cp FILE NEWNAMERET

As a bonus, you have tab completion available and you're already in FILE's directory, so typing the first filename is nice and fast.

Comments

2

This is based on phils' response, adapted to be called programmatically from Lisp. See docstring for details.

(defun my-backup-buffer-or-region (&optional buffer-or-name file beg end) "Write copy of BUFFER-OR-NAME between BEG and END to FILE. BUFFER-OR-NAME is either a buffer object or name. Uses current buffer when none is passed. Uses entire buffer for region when BEG and END are nil. Prompts for filename when called interactively. Will always ask before overwriting. Returns the name of the file written to. See URL `https://stackoverflow.com/a/18780453/5065796'." (interactive) (let* ((buffer-or-name (or buffer-or-name (current-buffer))) (buffo (or (get-buffer buffer-or-name) (error "Buffer does not exist"))) ; buffer object (buffn (or (buffer-file-name buffo) (buffer-name buffo))) ; buffer name (beg (or beg (if (use-region-p) (region-beginning) beg))) (end (or end (if (use-region-p) (region-end) end))) (prompt (if (and beg end) "region" "buffer")) (new (if (called-interactively-p 'interactive) (read-file-name (concat "Write " prompt " to file: ") nil nil nil (and buffn (file-name-nondirectory buffn))) (or file (error "Filename cannot be nil")))) ;; See `write-region' for meaning of 'excl (mustbenew (if (and buffn (file-equal-p new buffn)) 'excl t))) (with-current-buffer buffo (if (and beg end) (write-region beg end new nil nil nil mustbenew) (save-restriction (widen) (write-region (point-min) (point-max) new nil nil nil mustbenew)))) new)) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.