Discussion:
Rename, delete and move current buffer and file
(too old to reply)
Jarosław Rzeszótko
2018-05-07 13:58:53 UTC
Permalink
It is surprisingly hard to do this in Emacs, and I think it is a common
problem, as evidenced by e.g.:

https://stackoverflow.com/questions/384284/how-do-i-rename-an-open-file-in-emacs
http://emacsredux.com/blog/2013/05/04/rename-file-and-buffer/
http://rejeep.github.io/emacs/elisp/2010/03/26/rename-file-and-buffer-in-emacs.html

You can use dired, but I personally find it to be a distraction for this
use case.

There is set-visited-file-name, but:
a) it creates a copy of the file by default
b) the name is not intuitive and I doubt many people looking to rename a
file will find it
c) the best UI/UX is IMO different for different use cases, rename is
different from move

A very similar related pain point is that it is hard to get the path and
directory of the current buffers visited file. There exist ways to do it,
but this is a pair of related use cases and the handling is completely
different: for the path you have default-directory, an elisp variable, and
pwd, a command capable of displaying the path in minibuffer or yanking it
at point. For file name I am only aware of the elisp function:
buffer-file-name. Again, two closely related use cases, handled very
differently, I by now looked those up like 10 times and every time I forget
how to get this info because the names are so far from what you would
expect.

How can we fix or improve those issues?

For rename/delete/move I would create three distinct commands:
rename-visited-file-with-buffer
move-visited-file-with-buffer
delete-visited-file-with-buffer

Those names make the functions easy to discover if you are using something
like ivy or ido for M-x, while they are still precise from the standpoint
of Emacs concepts. It seems good to me to separate rename, which should
prefill the minibuffer prompt with the current name, and ask only for a new
filename, WITHOUT directory selection, from move, which should prompt for a
full new path, WITH directory selection.

For the current buffers file and directory name, I think aliasing
default-directory as visited-file-name-directory, and introducing
visited-file-name is one option. Another option: introduce
buffer-directory-name as a complement to the existing buffer-file-name
function. That would cleanup the elisp part, and then at least the names
are something you can remember, and with M-: you can kill, insert or
display those.

Question remains how to fix those things also on an user interface level. I
think it's a pity clicking on the filename in the modeline changes the
current buffer, I am happy to hear if I am mistaken, but today this
behavior is rather unexpected and I would be very surprised to see somebody
switching buffers this way. On the other hand it looks like a nice place
for a menu where all the operations mentioned could be triggered (rename,
move and delete file+buffer, display/kill/insert directory name or file
name). Providing default key bindings under a common prefix for them could
be nice too.

Maybe someone has some more, or better, ideas for this.

Finally, while we are discussing functions everyone re-implements in their
.emacs, please lets make transpose-windows happen as an Emacs builtin :)

Cheers,
Jarosław Rzeszótko
Stefan Monnier
2018-05-07 14:53:31 UTC
Permalink
Post by Jarosław Rzeszótko
different: for the path you have default-directory, an elisp variable, and
[ Side note: After

C-x C-f /home/foo/bar

buffer-file-name should be "/home/foo/bar" and default-directory should
be "/home/foo/". But note that if you then do:

M-x cd RET / RET

you'll see that default-directory is now "/", i.e. default-directory is
not actually tied to the name of the buffer's file.

This said, the two are in-sync 99.9% of the time.
]
Post by Jarosław Rzeszótko
How can we fix or improve those issues?
rename-visited-file-with-buffer
move-visited-file-with-buffer
I don't really know what's the intended difference between "rename" and
"move", but I think rather than introduce new commands (whose name users
won't remember), it'd make more sense to "enhance" existing commands.
E.g. I personally rename/move files usually via

M-x delete-file RET M-n RET
C-x C-w <newname> RET

so maybe we could instead have `C-x C-w` prompt the user
"delete the old file (y or n)?"

Similarly

M-x rename-file RET

could try and detect if the source name matches some of the buffers's
filenames and ask whether we want to rename those buffers's filenames
accordingly.
Post by Jarosław Rzeszótko
delete-visited-file-with-buffer
Hmm... Not sure how best to introduce this behavior into existing commands.

I don't remember being conscious of having such a need, but I think I'd
do it with:

M-x delete-file RET M-n RET
C-x C-c

[ I have C-x C-c remapped the kill the current-buffer, since
I prefer to use `M-x kill-emacs RET` for those rare cases where
I really want to exit the current Emacs session. ]

So maybe `delete-file` could ask whether we wants to kill the
corresponding buffers (as for `rename-file` above it's "buffer*S*"
since it can affect several buffers in the case where the
deleted/renamed "file" is actually a directory).
Post by Jarosław Rzeszótko
Those names make the functions easy to discover if you are using something
like ivy or ido for M-x,
[ Same applies if you use `icomplete-mode` or if you use the
bog-standard default completion ;-) ]
Post by Jarosław Rzeszótko
while they are still precise from the standpoint
of Emacs concepts. It seems good to me to separate rename, which should
prefill the minibuffer prompt with the current name, and ask only for a new
filename, WITHOUT directory selection, from move, which should prompt for a
full new path, WITH directory selection.
C-x C-w gives you "/current/dir/" is initial input. If you then type
"/other/dir/" it will "move" the file without "renaming" it (I
personally don't like to make this distinction, probably because
I consider the file's name to include all the leading directories, which
is also the implicit point of view of the GNU Coding Standard which uses
"file name" rather than "path" and reserves the word "path" for things
like $PATH, $LS_LIBRARY_PATH, load-path, ...).
And M-n inserts the current name, so I think it handles both
use-cases well enough.

[ For those how like the "default" to be pre-inserted in the buffer
(and highlighted as the current region so that delete-selection-mode
will automatically delete it) rather than go through M-n, this should
be a global user-config option. ]
Post by Jarosław Rzeszótko
For the current buffers file and directory name, I think aliasing
default-directory as visited-file-name-directory, and introducing
visited-file-name is one option. Another option: introduce
buffer-directory-name as a complement to the existing buffer-file-name
function. That would cleanup the elisp part, and then at least the names
are something you can remember, and with M-: you can kill, insert or
display those.
I thought `M-x pwd RET` was a good enough way to get that info.
Post by Jarosław Rzeszótko
Question remains how to fix those things also on an user interface level. I
think it's a pity clicking on the filename in the modeline changes the
current buffer, I am happy to hear if I am mistaken, but today this
behavior is rather unexpected and I would be very surprised to see somebody
switching buffers this way.
On the contrary I'd be surprised if noone uses this buffer-switching
system, since it's been around for so many years. I don't claim it's
the best use of that "button-like thingy", but changing it would
inevitably irk some users.

[ FWIW, I don't use that functionality, but then again, I don't use
dired either. ]
Post by Jarosław Rzeszótko
On the other hand it looks like a nice place for a menu where all the
operations mentioned could be triggered
I guess it could make sense, indeed.
Post by Jarosław Rzeszótko
(rename, move and delete file+buffer, display/kill/insert directory
name or file name). Providing default key bindings under a common
prefix for them could be nice too.
A common key-prefix for such file operations would be nice, indeed
(this key-prefix could be considered as a kind of "buffer-less
lightweight dired").


Stefan
Jarosław Rzeszótko
2018-05-07 16:20:26 UTC
Permalink
Post by Stefan Monnier
Post by Jarosław Rzeszótko
different: for the path you have default-directory, an elisp variable,
and
[ Side note: After
C-x C-f /home/foo/bar
buffer-file-name should be "/home/foo/bar" and default-directory should
M-x cd RET / RET
you'll see that default-directory is now "/", i.e. default-directory is
not actually tied to the name of the buffer's file.
This said, the two are in-sync 99.9% of the time.
]
That's maybe one more reason a function like buffer-directory-name would be
nice? Semantics for all the operations being discussed surely take some
thought and maybe trial & error to get right, as is also clear when looking
at set-visited-file-name and seeing how it is quite complicated.
Post by Stefan Monnier
Post by Jarosław Rzeszótko
How can we fix or improve those issues?
rename-visited-file-with-buffer
move-visited-file-with-buffer
I don't really know what's the intended difference between "rename" and
"move", but I think rather than introduce new commands (whose name users
won't remember), it'd make more sense to "enhance" existing commands.
E.g. I personally rename/move files usually via
M-x delete-file RET M-n RET
C-x C-w <newname> RET
so maybe we could instead have `C-x C-w` prompt the user
"delete the old file (y or n)?"
Similarly
M-x rename-file RET
could try and detect if the source name matches some of the buffers's
filenames and ask whether we want to rename those buffers's filenames
accordingly.
People might not remember the whole command name, but when they use M-x, or
C-h f with some filtering it will pop up when typing "rename" or "move",
which is what those operations are now called pretty universally in other
programs. It's easily discoverable this way, and you might key bind if you
want, while renaming via delete-file is something that you might figure out
but not necessarily anything close to the first thing you would try. So I
stand by my commands proposal. As mentioned I also hope there are ways to
integrate it into keybindings and UI, maybe also a manual section for such
not-dired file operations would be nice. I think those are basic things
people want to do, and a bit of a gap in the basic set of Emacs
functionalities.

The difference between "rename" and "move" I intended was for rename to
mean changing the filename (without moving to another directory), and for
move to mean moving to another directory, keeping or also changing the
filename. You can present slightly different prompt/completion in the
minibuffer for the two cases, which I thought might make for a nice UX. I
would also be fine with just a "move" command, which lets you do both
things, see below.
Post by Stefan Monnier
Post by Jarosław Rzeszótko
delete-visited-file-with-buffer
Hmm... Not sure how best to introduce this behavior into existing commands.
I don't remember being conscious of having such a need, but I think I'd
M-x delete-file RET M-n RET
C-x C-c
[ I have C-x C-c remapped the kill the current-buffer, since
I prefer to use `M-x kill-emacs RET` for those rare cases where
I really want to exit the current Emacs session. ]
So maybe `delete-file` could ask whether we wants to kill the
corresponding buffers (as for `rename-file` above it's "buffer*S*"
since it can affect several buffers in the case where the
deleted/renamed "file" is actually a directory).
Post by Jarosław Rzeszótko
Those names make the functions easy to discover if you are using
something
Post by Jarosław Rzeszótko
like ivy or ido for M-x,
[ Same applies if you use `icomplete-mode` or if you use the
bog-standard default completion ;-) ]
Post by Jarosław Rzeszótko
while they are still precise from the standpoint
of Emacs concepts. It seems good to me to separate rename, which should
prefill the minibuffer prompt with the current name, and ask only for a
new
Post by Jarosław Rzeszótko
filename, WITHOUT directory selection, from move, which should prompt
for a
Post by Jarosław Rzeszótko
full new path, WITH directory selection.
C-x C-w gives you "/current/dir/" is initial input. If you then type
"/other/dir/" it will "move" the file without "renaming" it (I
personally don't like to make this distinction, probably because
I consider the file's name to include all the leading directories, which
is also the implicit point of view of the GNU Coding Standard which uses
"file name" rather than "path" and reserves the word "path" for things
like $PATH, $LS_LIBRARY_PATH, load-path, ...).
And M-n inserts the current name, so I think it handles both
use-cases well enough.
This creates a copy of the file, while I want to rename/move it. This might
include things like deal with version control. A single "move" function is
fine, maybe even the set-visited-file-name semantics are OK, it just has a
bad name, has no key bindings, no menu item, and I would like a
delete-visited-file to complement it. When I look for a command to move a
file in the M-x completion prompt, I will try "move" and "rename" and see
what matches, but I would surely not naturally come up with any substring
of set-visited-file-name when thinking how Emacs might have named a command
to move files, except for "file-name", but this matches a ton of things. I
am sure many people using Emacs don't even know the concept of a visited
file.

Also, note there is rename-file, rename-buffer, but then
set-visited-file-name for what is effectively rename-file-with-visited-buffer.
There is also an interactive delete-file, obviously there is kill-buffer,
but no way to delete file and kill its visiting buffer. I just hope maybe
we can find some way to make this more uniform and complete.
Stefan Monnier
2018-05-07 17:01:28 UTC
Permalink
Post by Jarosław Rzeszótko
Post by Stefan Monnier
Similarly
M-x rename-file RET
could try and detect if the source name matches some of the buffers's
filenames and ask whether we want to rename those buffers's filenames
accordingly.
[...]
Post by Jarosław Rzeszótko
People might not remember the whole command name, but when they use M-x, or
C-h f with some filtering it will pop up when typing "rename" or "move",
[...]
Post by Jarosław Rzeszótko
So I stand by my commands proposal.
I don't understand: it seems like "rename-file" is a name which should
just work with your M-x and C-h f examples, so I don't see how those examples
argues in favor of "my commands proposal" instead of using "rename-file".
Post by Jarosław Rzeszótko
Post by Stefan Monnier
C-x C-w gives you "/current/dir/" is initial input. If you then type
"/other/dir/" it will "move" the file without "renaming" it (I
personally don't like to make this distinction, probably because
I consider the file's name to include all the leading directories, which
is also the implicit point of view of the GNU Coding Standard which uses
"file name" rather than "path" and reserves the word "path" for things
like $PATH, $LS_LIBRARY_PATH, load-path, ...).
And M-n inserts the current name, so I think it handles both
use-cases well enough.
This creates a copy of the file, while I want to rename/move it.
I only presented this example to illustrate how Emacs merges both "new
name" and "new directory" into a single UI (tho indeed currently
C-x C-w doesn't actually "move/rename" but it copies instead).
Post by Jarosław Rzeszótko
include things like deal with version control. A single "move" function is
fine, maybe even the set-visited-file-name semantics are OK, it just has a
bad name, has no key bindings, no menu item,
C-x C-w does have all those feature (other than the name).
Post by Jarosław Rzeszótko
and I would like a delete-visited-file to complement it.
Could you give details as to why you'd want to separate it from
`delete-file`?
Post by Jarosław Rzeszótko
When I look for a command to move a file in the M-x completion prompt,
I will try "move" and "rename" and see what matches,
Right, and you'll find `rename-file`, which is what I think should do
what you want.
Post by Jarosław Rzeszótko
but I would surely not naturally come up with any substring
of set-visited-file-name when thinking how Emacs might have named a command
to move files, except for "file-name", but this matches a ton of things. I
am sure many people using Emacs don't even know the concept of a visited
file.
Agreed. So I find it odd that you insist on having "visited-file" in
the name of the commands ;-)
Post by Jarosław Rzeszótko
Also, note there is rename-file, rename-buffer, but then
set-visited-file-name for what is effectively
rename-file-with-visited-buffer.
set-visited-file-name does not rename any file, AFAIK (and I don't see
any suggestion to change this).
Post by Jarosław Rzeszótko
There is also an interactive delete-file, obviously there is kill-buffer,
but no way to delete file and kill its visiting buffer.
FWIW, I very rarely need to do delete-file and kill-buffer at the same
time, so I'm not convinced there's a need for a separate command for
that. But as noted, I'd be OK for delete-file to kill the matching
buffer(s) [ either subject to a prompt or a user-config, for those users
who like to `delete-file` while keeping the buffer, as is occasionally
my case. ]


Stefan
Jarosław Rzeszótko
2018-05-07 17:47:15 UTC
Permalink
Post by Stefan Monnier
Post by Jarosław Rzeszótko
Post by Stefan Monnier
Similarly
M-x rename-file RET
could try and detect if the source name matches some of the buffers's
filenames and ask whether we want to rename those buffers's filenames
accordingly.
[...]
Post by Jarosław Rzeszótko
People might not remember the whole command name, but when they use M-x,
or
Post by Jarosław Rzeszótko
C-h f with some filtering it will pop up when typing "rename" or "move",
[...]
Post by Jarosław Rzeszótko
So I stand by my commands proposal.
I don't understand: it seems like "rename-file" is a name which should
just work with your M-x and C-h f examples, so I don't see how those examples
argues in favor of "my commands proposal" instead of using "rename-file".
I have a file open in a buffer in front of me. I want to rename this file
and have the buffer be changed accordingly: it should now be visiting the
file under the new name.

rename-file will instead:
- rename the file but do nothing with the buffer, if I now save the buffer
it will get saved under the old file name. The buffer still visits the file
under the old name.
- it will also first prompt for a file to rename, while I want to rename
the file I am currently editing in a buffer, along with the buffer.

Emacs distinguishing so strongly between buffer and visited file is why
people commonly implement something they typically name
rename-file-and-buffer, for example:

https://stackoverflow.com/questions/384284/how-do-i-rename-an-open-file-in-emacs
http://emacsredux.com/blog/2013/05/04/rename-file-and-buffer/
http://rejeep.github.io/emacs/elisp/2010/03/26/rename-file-and-buffer-in-emacs.html

But rename-file-and-buffer acting on the current buffers file is
inconsistent with rename-file first prompting for a file to rename, hence
the logical way would be to have a new set of functions with some shared
suffix.
Post by Stefan Monnier
Post by Jarosław Rzeszótko
Post by Stefan Monnier
C-x C-w gives you "/current/dir/" is initial input. If you then type
"/other/dir/" it will "move" the file without "renaming" it (I
personally don't like to make this distinction, probably because
I consider the file's name to include all the leading directories, which
is also the implicit point of view of the GNU Coding Standard which uses
"file name" rather than "path" and reserves the word "path" for things
like $PATH, $LS_LIBRARY_PATH, load-path, ...).
And M-n inserts the current name, so I think it handles both
use-cases well enough.
This creates a copy of the file, while I want to rename/move it.
I only presented this example to illustrate how Emacs merges both "new
name" and "new directory" into a single UI (tho indeed currently
C-x C-w doesn't actually "move/rename" but it copies instead).
OK, I am completely fine with a single combined move/rename, no
disagreement here.
Post by Stefan Monnier
Post by Jarosław Rzeszótko
include things like deal with version control. A single "move" function
is
Post by Jarosław Rzeszótko
fine, maybe even the set-visited-file-name semantics are OK, it just has
a
Post by Jarosław Rzeszótko
bad name, has no key bindings, no menu item,
C-x C-w does have all those feature (other than the name).
Post by Jarosław Rzeszótko
and I would like a delete-visited-file to complement it.
Could you give details as to why you'd want to separate it from
`delete-file`?
Like rename-file, delete-file will prompt for a file to delete, and it will
do nothing to a buffer visiting the file, rather than getting rid of the
*current* buffer and the file it visits. I often decide to delete the file
after I have opened it and viewed it, it seems too much to retype the
filename to delete it.
Post by Stefan Monnier
When I look for a command to move a file in the M-x completion prompt,
Post by Jarosław Rzeszótko
I will try "move" and "rename" and see what matches,
Right, and you'll find `rename-file`, which is what I think should do
what you want.
Post by Jarosław Rzeszótko
but I would surely not naturally come up with any substring
of set-visited-file-name when thinking how Emacs might have named a
command
Post by Jarosław Rzeszótko
to move files, except for "file-name", but this matches a ton of things.
I
Post by Jarosław Rzeszótko
am sure many people using Emacs don't even know the concept of a visited
file.
Agreed. So I find it odd that you insist on having "visited-file" in
the name of the commands ;-)
That's because there are existing functions that end with -file that act on
files but disregard any existing buffers associated with the file. In
addition to those existing file functions, and to functions that act on
buffers, I was thinking of having a third parallel set of functions that
acts on the current buffer and its associated file, and I would like the
naming to somehow be consistent between the three groups, that's why I came
up with the -buffer-and-visited-file suffix. If that doesn't ring right,
maybe it should be something like -current-buffer-and-file instead. Maybe
it should actually be four groups:

*-file functions
*-buffer
*-buffer-with-file
*-current-buffer-with-file

Since it's just move/rename and delete/kill, it's just 8 functions, 4 of
which already exist. You might not want to do this everyday, but it's also
not an exotic use case, again as evidenced by how many times I have seen it
discussed on the web and in people's .emacs files. rename-buffer-with-file
and delete-buffer-with-file might be useful for programmatic usage via
emacs-lisp as well.
Post by Stefan Monnier
Post by Jarosław Rzeszótko
Also, note there is rename-file, rename-buffer, but then
set-visited-file-name for what is effectively
rename-file-with-visited-buffer.
set-visited-file-name does not rename any file, AFAIK (and I don't see
any suggestion to change this).
If you set the along-with-file argument to t, or use prefix argument, I
think it executes a rename like the one I would like to be able to do, so
file+buffer.
Post by Stefan Monnier
Post by Jarosław Rzeszótko
There is also an interactive delete-file, obviously there is kill-buffer,
but no way to delete file and kill its visiting buffer.
FWIW, I very rarely need to do delete-file and kill-buffer at the same
time, so I'm not convinced there's a need for a separate command for
that. But as noted, I'd be OK for delete-file to kill the matching
buffer(s) [ either subject to a prompt or a user-config, for those users
who like to `delete-file` while keeping the buffer, as is occasionally
my case. ]
delete-file and rename-file prompting whether to do the corresponding
action also on the buffer makes sense in general, better yet if it would be
possible to configure Emacs to do this always, by default, when using the
functions interactively. Is something like that safe with regards to
backward compatibility?

Those act on a file you have to select though, I would like something that
specifically acts on the current buffer and its visited file.

Cheers,
Jarosław Rzeszótko
Stefan Monnier
2018-05-11 16:14:05 UTC
Permalink
That method has been obsolete for decades. POSIX requires rename to be
atomic.
Even when moving from one directory to another?
Yes, that requirement has been in POSIX ever since POSIX was introduced;
I just now checked my printed copy of IEEE Std 1003.1-1988.
Cool! I had no idea.
Thank,


Stefan
Clément Pit-Claudel
2018-05-11 16:06:17 UTC
Permalink
That method has been obsolete for decades. POSIX requires rename
to be atomic.
Even when moving from one directory to another?
Yes, that requirement has been in POSIX ever since POSIX was
introduced; I just now checked my printed copy of IEEE Std
1003.1-1988.
I don't know much about this, so sorry if the following is silly. Hopefully someone can clarify. The glibc implementation of rename in glibc/sysdeps/posix/rename.c is this:

/* Rename the file OLD to NEW. */
int
rename (const char *old, const char *new)
{
int save = errno;
if (__link (old, new) < 0)
{
if (errno == EEXIST)
{
__set_errno (save);
/* Race condition, required for 1003.1 conformance. */
if (__unlink (new) < 0 ||
__link (old, new) < 0)
return -1;
}
else
return -1;
}
if (__unlink (old) < 0)
{
save = errno;
if (__unlink (new) == 0)
__set_errno (save);
return -1;
}
return 0;
}

Is this just a fallback implementation (the "obsolete for decades" method that Andreas referred to?), unused on most platforms?

Thanks!
Clément.
Eli Zaretskii
2018-05-10 16:04:42 UTC
Permalink
Date: Thu, 10 May 2018 11:20:05 +0300
Post by Stefan Monnier
I do not understand what you have in mind with write-file. This creates a
copy, which is natural given the name. Do you envision some changes to it?
so maybe we could instead have `C-x C-w` prompt the user
"delete the old file (y or n)?"
For me the use case for using C-x C-w is not to move a file, but to
create a copy of a file leaving the original file untouched -- for
example if I want to create a new file but I want to start from an
existing one as a template.
Let me remind people that "C-x C-w" is the Emacs implementation of the
"Save As" paradigm, so it must stay as it is today. It would be
possible to make it do something slightly different given an argument,
but it already accepts an argument and interprets it in a different
way. Maybe we could do the renaming with some special value of the
argument, though.

But the default behavior should definitely stay, IMO.
Van L
2018-05-10 13:18:04 UTC
Permalink
This might be a personal thing, but Dired for a quick rename is a bit like killing a fly with a cannon. I find anything involving additional buffers distracting when I am focused on changing a set of files - I am typically refactoring a program, which already involves keeping some items in working memory.
The way a mechanism like `uservoice’ for demand driven development might work in this case is for the programmer to make the suggestion and post their idea as solved already in elisp snippet for voting on the next step.

I bet the cannon’s shot won’t ever come in contact with a fly in a mythbuster test. :-)
Phil Sainty
2018-05-10 08:59:46 UTC
Permalink
Count me as one of those who find a "rename buffer and file" command
useful. I've had one of those bound to "C-c r" for a very long time,
and I consider it convenient to be able to do this directly from the
buffer in question, rather than going to dired.
Post by Stefan Monnier
so maybe we could instead have `C-x C-w` prompt the user
"delete the old file (y or n)?"
adding the option to delete the original file is unnecessary, and
prompting the user would just interrupt.
I'd certainly find it annoying if C-x C-w added an extra prompt.


-Phil

Andreas Schwab
2018-05-07 18:20:47 UTC
Permalink
Post by Jarosław Rzeszótko
That's maybe one more reason a function like buffer-directory-name would be
nice?
How is that different from (file-name-directory (buffer-file-name))?

Andreas.
--
Andreas Schwab, ***@linux-m68k.org
GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510 2552 DF73 E780 A9DA AEC1
"And now for something completely different."
Andreas Röhler
2018-05-07 15:38:48 UTC
Permalink
Post by Jarosław Rzeszótko
It is surprisingly hard to do this in Emacs, and I think it is a
https://stackoverflow.com/questions/384284/how-do-i-rename-an-open-file-in-emacs
http://emacsredux.com/blog/2013/05/04/rename-file-and-buffer/
http://rejeep.github.io/emacs/elisp/2010/03/26/rename-file-and-buffer-in-emacs.html
You can use dired, but I personally find it to be a distraction for
this use case.
a) it creates a copy of the file by default
b) the name is not intuitive and I doubt many people looking to rename
a file will find it
c) the best UI/UX is IMO different for different use cases, rename is
different from move
A very similar related pain point is that it is hard to get the path
and directory of the current buffers visited file. There exist ways to
do it, but this is a pair of related use cases and the handling is
completely different: for the path you have default-directory, an
elisp variable, and pwd, a command capable of displaying the path in
minibuffer or yanking it at point. For file name I am only aware of
the elisp function: buffer-file-name. Again, two closely related use
cases, handled very differently, I by now looked those up like 10
times and every time I forget how to get this info because the names
are so far from what you would expect.
How can we fix or improve those issues?
rename-visited-file-with-buffer
move-visited-file-with-buffer
delete-visited-file-with-buffer
Those names make the functions easy to discover if you are using
something like ivy or ido for M-x, while they are still precise from
the standpoint of Emacs concepts. It seems good to me to separate
rename, which should prefill the minibuffer prompt with the current
name, and ask only for a new filename, WITHOUT directory selection,
from move, which should prompt for a full new path, WITH directory
selection.
For the current buffers file and directory name, I think aliasing
default-directory as visited-file-name-directory, and introducing
visited-file-name is one option. Another option: introduce
buffer-directory-name as a complement to the existing buffer-file-name
function. That would cleanup the elisp part, and then at least the
names are something you can remember, and with M-: you can kill,
insert or display those.
Question remains how to fix those things also on an user interface
level. I think it's a pity clicking on the filename in the modeline
changes the current buffer, I am happy to hear if I am mistaken, but
today this behavior is rather unexpected and I would be very surprised
to see somebody switching buffers this way. On the other hand it looks
like a nice place for a menu where all the operations mentioned could
be triggered (rename, move and delete file+buffer, display/kill/insert
directory name or file name). Providing default key bindings under a
common prefix for them could be nice too.
Maybe someone has some more, or better, ideas for this.
Finally, while we are discussing functions everyone re-implements in
their .emacs, please lets make transpose-windows happen as an Emacs
builtin :)
Cheers,
Jarosław Rzeszótko
Good question.
[(control a)]         ;; Store the buffer-name in kill-ring, code see below
[(control x) (d)]     ;; dired, commonly the wanted working dir
[(control s)(meta y)] ;; yank the buffer-name as argument to
              ;; isearch-forward
After all seeing dired-jump is better here.

So lets forget about that ;)
Yuri Khan
2018-05-07 15:28:20 UTC
Permalink
It is surprisingly hard to do this in Emacs […]
You can use dired, but I personally find it to be a distraction for this
use case.

Why?

I think of deleting, renaming and moving as operations on the file as a
whole and not on its content, so saving the file and going “outside” it is
the intuitive first step for me. ‘dired-jump’ takes me to the Dired buffer
of the enclosing directory and puts point on the file. It’s on C-x C-j by
default, but I bind it on <M-S-up> so my fingers think going “outside” is a
single spatial movement.

If I want to delete the file, I press D and confirm. To rename, I press R
and enter the new name; the buffer is renamed automatically.

When copying or moving files, I prefer to see the target directory before I
do it. So, I split the window, switch there, navigate to the target
directory, switch back, R (or C to copy), RET (because with
‘dired-dwim-target’ set to non-nil the target directory is automatically
suggested as the default), then deal with any changes to the window
configuration.

Note here the DWIM behavior: R suggests the directory in the other window,
but if there is no other window, then the current directory.
A very similar related pain point is that it is hard to get the path and
directory of the current buffers visited file.

Your favorite binding of ‘find-file’, followed by your preferred method to
get the current line to clipboard. (This breaks if you ‘cd’ to a different
directory while editing a file.)
Finally, while we are discussing functions everyone re-implements in
their .emacs, please lets make transpose-windows happen as an Emacs builtin
:)

You mean the windcycle library?
Jarosław Rzeszótko
2018-05-07 16:20:53 UTC
Permalink
Post by Jarosław Rzeszótko
It is surprisingly hard to do this in Emacs [
]
You can use dired, but I personally find it to be a distraction for this
use case.
Why?
I personally most often want this when working on a programming project, I
have a bunch of files open and I am in the middle of a planned sequence of
changes, popping up a new buffer and dealing with dired which I do not
otherwise use much breaks my concentration. It is hard to explain this
fully rationally, but judging by how many .emacs, libraries, wiki pages
etc. I have seen that have rename-file-and-buffer in them I am not the only
one.

Note that an interactive delete-file function already exists, but it
doesn't kill the associated buffer. That's why I consider it a gap in the
Emacs set of functions. There are three sets of operations: file operations
(rename-file, delete-file), buffer operations (rename-buffer, kill-buffer)
and some file+buffer operations (set-visited-file-name). It would be nice
if there was some unity among the three sets, so that it would be possible
to do the common operations in all three ways (file/buffer/file+buffer),
and that the naming is reasonably consistent. Of course backwards
compatibility is an issue as always.
Post by Jarosław Rzeszótko
[...]
A very similar related pain point is that it is hard to get the path and
directory of the current buffers visited file.
Your favorite binding of ‘find-file’, followed by your preferred method to
get the current line to clipboard. (This breaks if you ‘cd’ to a different
directory while editing a file.)
This is not that easy if you use a completion system like ivy or ido. It's
also not nice from an elisp standpoint, that for the two strongly related
things, one is accessible only as a variable and the other either as a
command or function.
Post by Jarosław Rzeszótko
Finally, while we are discussing functions everyone re-implements in
their .emacs, please lets make transpose-windows happen as an Emacs builtin
:)
You mean the windcycle library?
I mean:

https://github.com/bbatsov/crux/blob/master/crux.el#L471
https://www.emacswiki.org/emacs/TransposeWindows

Sure there are packages to do this, it just seems strange among the many
built-in window functions there is no transpose. Again, you will easily
find very many .emacs on the web implementing a function like this, which
for me looks like a bit of gap in what is provided out-of-the-box.

Cheers,
Jarosław Rzeszótko
Post by Jarosław Rzeszótko
It is surprisingly hard to do this in Emacs [
]
You can use dired, but I personally find it to be a distraction for this
use case.
Why?
I think of deleting, renaming and moving as operations on the file as a
whole and not on its content, so saving the file and going “outside” it is
the intuitive first step for me. ‘dired-jump’ takes me to the Dired buffer
of the enclosing directory and puts point on the file. It’s on C-x C-j by
default, but I bind it on <M-S-up> so my fingers think going “outside” is a
single spatial movement.
If I want to delete the file, I press D and confirm. To rename, I press R
and enter the new name; the buffer is renamed automatically.
When copying or moving files, I prefer to see the target directory before I
do it. So, I split the window, switch there, navigate to the target
directory, switch back, R (or C to copy), RET (because with
‘dired-dwim-target’ set to non-nil the target directory is automatically
suggested as the default), then deal with any changes to the window
configuration.
Note here the DWIM behavior: R suggests the directory in the other window,
but if there is no other window, then the current directory.
A very similar related pain point is that it is hard to get the path and
directory of the current buffers visited file.
Your favorite binding of ‘find-file’, followed by your preferred method to
get the current line to clipboard. (This breaks if you ‘cd’ to a different
directory while editing a file.)
Finally, while we are discussing functions everyone re-implements in
their .emacs, please lets make transpose-windows happen as an Emacs builtin
:)
You mean the windcycle library?
net june
2018-05-11 16:10:23 UTC
Permalink
Sound cool and should save a keystroke WRT dired-jump and friends.
May you post the code?
Yes. The following is it.

(defun my-rename-file (arg)
"Rename or delete the file visited by current buffer."
(interactive "P")
(unless buffer-file-name
(user-error "Not a file buffer"))
(let ((old-name (file-name-nondirectory buffer-file-name))
new-file-name)
(if arg
(let ((tmp-file-name (read-file-name "Rename to: ")))
(setq new-file-name
(if (file-directory-p tmp-file-name)
(concat (file-name-as-directory tmp-file-name) old-name)
tmp-file-name)))
(let* ((new-name (read-string "New file name: " old-name)))
(when (string= new-name old-name)
(user-error "Same as the old name. Not renamed."))
(if (string= new-name "")
(when (yes-or-no-p
(format "Delete file \"%s\"?" buffer-file-name))
;; On fail, it will signal an error and abort.
(delete-file buffer-file-name)
(set-buffer-modified-p nil)
(kill-buffer)
(message "File deleted"))
(setq new-file-name (expand-file-name new-name)))))
(when new-file-name
(let ((modify-flag (buffer-modified-p))
(old-file-name buffer-file-name))
;; On fail, it will signal an error and abort.
(rename-file buffer-file-name new-file-name 1)
(set-visited-file-name new-file-name)
(rename-buffer (file-name-nondirectory new-file-name) t)
(set-buffer-modified-p modify-flag)
(message "Renamed: %s ==> %s" old-
Loading...