Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using rename to move an existing file to a subdirectory #442

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

timtatt
Copy link

@timtatt timtatt commented Jul 6, 2024

Previously an oil window with

foo.txt

changing to:

bar/foo.txt

would result in the following error "Filename cannot contain path separator"

With this code change, running the above Oil change will create a subdirectory bar if it doesn't exist and will move foo.txt inside

@github-actions github-actions bot requested a review from stevearc July 6, 2024 13:37
@timtatt timtatt changed the title Adding support for moving an existing file to a subdirectory Using rename to move an existing file to a subdirectory Jul 6, 2024
@stevearc
Copy link
Owner

stevearc commented Jul 6, 2024

This currently has the same problems as #400 and #305

mkdir foo
touch foo/a.txt
touch a.txt

Then nvim . and edit the buffer to look like this:

foo/
foo/a.txt    // renamed from a.txt

When you save, it moves a.txt to foo/a.txt, obliterating the pre-existing file.

The reason this feature does not exist yet is because of the difficulty in ensuring that these operations are safe and cannot produce unexpected data loss.

@timtatt
Copy link
Author

timtatt commented Jul 7, 2024

This currently has the same problems as #400 and #305

mkdir foo
touch foo/a.txt
touch a.txt

Then nvim . and edit the buffer to look like this:

foo/
foo/a.txt    // renamed from a.txt

When you save, it moves a.txt to foo/a.txt, obliterating the pre-existing file.

The reason this feature does not exist yet is because of the difficulty in ensuring that these operations are safe and cannot produce unexpected data loss.

Great points @stevearc, I've pushed a couple ideas to resolve this. Added a file_exists method to the adapter which can be used in the parse phase. Currently only implemented for 'files' adapter due to complexity with async for SSH (open to ideas here).

Below are the use cases I've identified, let me know if I'm missing any. If you're happy with the approach I'll add some tests to tie it up.

  1. When foo/bar.txt exists AND bar.txt is renamed to foo/bar.txt
mkdir foo
touch foo/bar.txt
foo/
foo/bar.txt // renamed from bar.txt

Current Behaviour:
image

New Behaviour:
image

  1. When foo/bar.txt exists AND bar.txt is renamed to bar/bar.txt AND foo/ is renamed to bar/
  • Expect to throw an error that you cannot create a file in a subdirectory while renaming the same directory
mkdir foo
touch foo/bar.txt
touch bar.txt
bar/ // renamed from foo/
bar/bar.txt // renamed from bar.txt
  1. When foo/bar.txt exists AND bar/bar.txt is created AND foo/ is renamed to bar/

3a. bar/bar.txt is below bar/

mkdir foo
touch foo/bar.txt
bar/ // renamed from foo/
bar/bar.txt // added

Previous Behaviour:
No actions, the bar/bar.txt file is ignored

New Behaviour:
image

3b. bar/bar.txt is above bar/

mkdir foo
touch foo/bar.txt
bar/bar.txt // added
bar/ // renamed from foo/

Previous Behaviour:
If bar/bar.txt is before bar/ in the buffer, it will result in:
image

New Behaviour:
Not 100% happy with the error message here, it is using the existing dedup functionality
image

@timtatt timtatt marked this pull request as draft July 7, 2024 02:57
@timtatt
Copy link
Author

timtatt commented Jul 14, 2024

@stevearc what do you think this solution?

Copy link
Owner

@stevearc stevearc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the right general idea of checking for conflicts before performing the actions. Two issues with the current approach:

  1. It's not supported in the ssh adapter. It would be ideal if all the adapter special-case logic could be removed, but that may not be possible because the trash adapters may still need to be handled differently. Haven't thought about that too deeply yet.
  2. It checks for collisions with the current state of the filesystem, but not the target state. For example: if you put a file into foo/bar/baz.txt from multiple locations in the same operation, we won't catch that and they'll clobber each other with only one winning. Likewise, if you put a file into foo/bar.txt (with a conflicting existing file) and rename the current file to foo/baz.txt in the same operation, it will incorrectly block you.

To be a truly safe operation, each of these nested files will need to be checked to see if there are any conflicts in any open oil buffers, as well as the target directory. Note that the current implementation seems to support ../foo.txt, which can get complicated. For example, in /home/user you can move a file to ../../foo.txt and ../../../foo.txt, which look different but both resolve to /foo.txt

I suspect that pursuing the above goal will also solve the issue with the unsupported ssh adapter, since the approach won't be using a direct "does this file exist" check.

@ccidral
Copy link

ccidral commented Oct 3, 2024

@timtatt's solution looks fine to me, but if I may I'd like to add my two cents to this statement:

ensuring that these operations are safe and cannot produce unexpected data loss.

Since Neovim users are mostly software developers, I personally think we can trust they know what they're doing (well, most of the time 😊) and let them deal with the consequences. To me the "move by renaming" operation wouldn't be much different than opening a terminal and executing:

mv foo.txt bar/foo.txt

I'd say this mv operation is exactly what was in my mind when I tried to replicate it in Oil. By default mv just silently overwrites the file at the destination, no questions asked.

But still I understand why accidental data loss is a valid concern, so suppose Oil were to allow silent overwriting files by move-renaming them, this behavior could be disabled by default and Oil could provide some configuration option to explicitly enable it. Assuming @timtatt's solution eventually gets implemented, there could be an additional option to enable explicitly overwriting a file via some kind of syntactic element, e.g. when changing

foo.txt

to

bar/foo.txt

you could add something like ! or whatever to indicate that you want to replace the existing file, if it exists:

bar/foo.txt !

Just thinking out loud.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants