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

While saving a file, the inode number doesn't change #2036

Closed
lord-re opened this Issue May 8, 2018 · 10 comments

Comments

Projects
None yet
6 participants
@lord-re
Copy link

commented May 8, 2018

Steps

launch watch -d stat you-file

Open this file with kak and save it with :w

Outcome

The Modify and Change are updated but the inode number doesn't change

Expected

While doing the exact same thing with vim, the inode number is changed so external programs see the file has changed (for example hugo server --navigateToChanged)

Actually i don't think it's really a bug as discussed on irc, it's just a different way of doing things. But this way can be quite cumbersome while using other software which watch the edits only relying on the inode number.

@lenormf

This comment has been minimized.

Copy link
Contributor

commented May 9, 2018

The logs of the conversation about this issue on IRC:

2018-04-19 13:14:44 Screwtape Lord: If you can confirm that backupcopy=yes makes Vim exactly as broken as Kakoune with respect to Hugo, then we'll know exactly what Kakoune has to change to behave like Vim.
2018-04-19 13:14:56 martanne I actually recently looked into how a file should ideally be saved and unless I'm missing something all options have drawbacks (e.g. the "correct" approach on Linux requires a kernel patch) ...
2018-04-19 13:15:27 Screwtape The documentation for Vim's 'backupcopy' option makes it pretty clear that yeah, everything is terrible.
2018-04-19 13:16:42 Screwtape As just one data-point: some people want their editor to break hard-links when saving, as a cheap form of copy-on-write. Other people want their editor to keep hard-links, in order to keep all "copies" of a file in sync.
2018-04-19 13:18:33 ironzorg martanne: what patch ?
2018-04-19 13:21:57 martanne Screwtape: well that is just a question of semantics. But there are problems which apply even to regular files.
2018-04-19 13:22:16 martanne ironzorg: it adds new flags to linkat(2)

@mawww

This comment has been minimized.

Copy link
Owner

commented Jun 16, 2018

I dont plan to change the way we save files for now, it is not clearly better/worse than alternatives, so I'll just keep it as is until we have a strong reason to change.

@mawww mawww closed this Jun 16, 2018

@eraserhd

This comment has been minimized.

Copy link
Contributor

commented Jan 4, 2019

Leaving this here for informational purposes: Editing long-running shell scripts in Kakoune makes them fail with weird errors.

@dmerejkowsky

This comment has been minimized.

Copy link
Contributor

commented Feb 3, 2019

I've just tried this:

My vim does not change the inode number, but hugo sees the changes made by vim.

Using the watch trick from @lord-re I found that the access time is not changed by kakoune, but is changed by vim.

So maybe there's something something to fix in kakoune after all?

Update:

I use this as a workaround for now, but for some reason hugo only catches changes after one or two seconds:

hook global BufWritePost .*\.md %(nop %sh(touch -m $kak_hook_param))

Update Update:

Actually, hugo works fine with small files by default. I think the problem is the way we write buffers to files:

    for (LineCount i = 0; i < buffer.line_count(); ++i)
    {
        // end of lines are written according to eolformat but always
        // stored as \n
        StringView linedata = buffer[i];
        write(fd, linedata.substr(0, linedata.length()-1));
        write(fd, eoldata);
    }

Maybe hugo sees a quick succession of write() calls and gets throttled somehow.

I should try writing the whole contents at once.

Well, this commit dmerejkowsky@ea7f5d2 solves the problem. Pretty sure it's not the correct way to do it, though.

@Screwtapello

This comment has been minimized.

Copy link
Contributor

commented Feb 4, 2019

Using the watch trick from @lord-re I found that the access time is not changed by kakoune, but is changed by vim.

That might not be relevant. Traditionally the access time gets bumped every time the file is accessed, which happens a lot, so these days many systems are configured to only update the access time if it's older than the mtime or ctime. That is, if the atime changed for Vim but not for Kakoune, it might not be anything to do with Vim or Kakoune, but with the other timestamps on the file.

Maybe hugo sees a quick succession of write() calls and gets throttled somehow.

That makes a lot more sense. I don't use hugo, but with other "automatic rebuild on save" tools I occasionally break things:

  • make a change, save it
  • watcher detects a change, kicks off a rebuild
  • realise I forgot an apostrophe or something, fix it and save again
  • rebuild finishes, watcher resumes watching
  • because the second save occurred while the watcher wasn't watching, I have to save a third time to sort things out

In your case, I can imagine a sequence of events like:

  • make a change at the end of file, save it
  • Kakoune starts writing out the file from the beginning
  • hugo detects the change, reads the whole file
  • hugo decides the file content is identical, does nothing
  • Kakoune finishes writing out the file, including the change at the end

The tools I use work around this problem by introducing a delay: when they detect a change, they wait a quarter of a second or so before doing the live reload. That's slower than strictly necessary, but still faster than saving, waiting for the reload, swearing, saving again and waiting again. I had a brief look at the Hugo docs but I couldn't see a "delay" or "wait" option.

@dmerejkowsky

This comment has been minimized.

Copy link
Contributor

commented Feb 5, 2019

Thanks for the detailed answer :) I'll take a look at your suggestions

@mawww

This comment has been minimized.

Copy link
Owner

commented Feb 5, 2019

Seems there is no good solution to that except give control to the user through an option... My current understanding is that there are two reasonable way to save files:

  • open the file in write/truncate mode, write the data, close it
  • write to a temporary file next to the target file, then rename it on top of it.

We could introduce an option, say file_write_method to control that, and provide the two methods.

@Screwtapello

This comment has been minimized.

Copy link
Contributor

commented Feb 6, 2019

As you note, there are two reasonable ways to save files, which I'll call "rewrite" (open/truncate/write) and "replace" (create temp/write/rename). However, looking at Vim's backupcopy option, there's a little more nuance.

To support all the use cases Vim documents, Kakoune would need a new flags-type option named something like replace_on_write. The possible flags would be:

  • if_symlink: Kakoune replaces the existing file if it is a symlink.
  • if_hardlink: Kakoune replaces the existing file if its st_nlink field is greater than 1.
  • if_metadata_safe: Kakoune replaces the existing file if all its metadata can be safely copied to the new file. For example, Kakoune can't chown the temporary file to match the original unless it's running as root, and might not be able to set security-related POSIX extended attributes.
  • always: Kakoune always replaces the existing file. This flag makes all the other flags redundant.

When writing a file, if any of the tests indicated by flags in replace_on_write are true, Kakoune should replace the existing file, otherwise it rewrites.

Vim's default corresponds to having no flags set, i.e. never replace, always rewrite. Since that's also Kakoune's current behaviour, it'd be a good default for Kakoune too.

mawww added a commit that referenced this issue Feb 12, 2019

Introduce a writemethod option to either overwrite or replace files
This permit to choose if files should be written by overwriting their
content (the default), or by writing to a separate temporary file
and rename it to the current file.

As discussed in #2036
@mawww

This comment has been minimized.

Copy link
Owner

commented Feb 12, 2019

I pushed an initial implementation, I went the simple way with a writemethod that can either be overwrite or replace. If/When we get a real need for more fine grained control, I will be happy to revisit that.

@dmerejkowsky

This comment has been minimized.

Copy link
Contributor

commented Feb 12, 2019

Thanks for the patch! It does fix the issue with Hugo :)

update: oops, spoke too soon. Hugo only catches the changes after a few seconds :/

dmerejkowsky added a commit to dmerejkowsky/kakoune that referenced this issue Feb 14, 2019

HACK Write the whole content at once
mawww#2036

The 'rename()' workaround does not work either :/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.