mirror of
https://github.com/neovim/neovim.git
synced 2026-11-06 05:16:52 +00:00
fix(bufwrite): 'nomodified' if buffer changed during :write #42458
Problem: If a (naughty) autocmd (e.g. a `Progress` handler) changes the buffer during a slow `:write`, buf_write() still unsets 'modified', even though the buffer has unwritten changes. `:bdelete` would silently lose the changes. Solution: Reset 'modified' only if `b:changedtick` did not change while writing.
This commit is contained in:
1 parent
eb25a1192a
commit
89e915b67a
2 files changed
+13
-3
No files matched your search
+6
-3
@@ -1120,6 +1120,8 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
|
||||
int progress = -1; // Progress-message percent for a slow write.
|
||||
off_T progress_nchars = 0; // `nchars` at the last progress check.
|
||||
const uint64_t write_start = os_hrtime();
|
||||
// Safety: any changes hereafter (eg by Progress autocmds) are not written, so keep 'modified'.
|
||||
const varnumber_T write_tick = buf_get_changedtick(buf);
|
||||
msg_scroll = false; // always overwrite the file message now
|
||||
|
||||
char *buffer = verbose_try_malloc(WRITEBUFSIZE);
|
||||
@@ -1759,11 +1761,12 @@ restore_backup:
|
||||
: msg_with_id(IObuff, msg_id, "bufwrite", NULL, 0, true, true, false), 0);
|
||||
}
|
||||
|
||||
// When written everything correctly: reset 'modified'. Unless not
|
||||
// writing to the original file and '+' is not in 'cpoptions'.
|
||||
// When written everything correctly: reset 'modified'. Unless not writing to the original file
|
||||
// and not 'cpoptions=+', or the buffer changed (by autocmds!?) while writing.
|
||||
if (reset_changed && whole && !append
|
||||
&& !write_info.bw_conv_error
|
||||
&& (overwriting || vim_strchr(p_cpo, kCpoPlus) != NULL)) {
|
||||
&& (overwriting || vim_strchr(p_cpo, kCpoPlus) != NULL)
|
||||
&& buf_get_changedtick(buf) == write_tick) {
|
||||
unchanged(buf, true, false);
|
||||
const varnumber_T changedtick = buf_get_changedtick(buf);
|
||||
if (buf->b_last_changedtick + 1 == changedtick) {
|
||||
|
||||
@@ -3957,10 +3957,15 @@ describe('progress-message', function()
|
||||
return exec_lua(function(f, cmd)
|
||||
local out = {}
|
||||
vim.api.nvim_create_autocmd('Progress', {
|
||||
group = vim.api.nvim_create_augroup('test_slow_write', {}),
|
||||
callback = function(ev)
|
||||
table.insert(out, ev.data.status)
|
||||
-- :bwipeout disallowed (E937) while writing.
|
||||
out.wiped = out.wiped or pcall(vim.cmd.bwipe, { bang = true })
|
||||
if #out == 1 then
|
||||
-- Progress handler (naughty) edits the buffer DURING :write.
|
||||
vim.api.nvim_buf_set_lines(0, -1, -1, true, { 'not written' })
|
||||
end
|
||||
end,
|
||||
})
|
||||
vim.api.nvim_buf_set_lines(0, 0, -1, true, vim.fn['repeat']({ ('x'):rep(99) }, 2000))
|
||||
@@ -3978,6 +3983,8 @@ describe('progress-message', function()
|
||||
end
|
||||
|
||||
eq({ 'running', 'success', false, 'ok' }, slow_write('cat'))
|
||||
-- Buffer changed while writing: still 'modified'.
|
||||
eq(true, api.nvim_get_option_value('modified', {}))
|
||||
eq({ 'running', 'failed', false, 'E514' }, slow_write('head -c 100000')) -- Write error (EPIPE).
|
||||
end)
|
||||
|
||||
|
||||
Reference in new issue
Block a user