neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 1e44a001ecdbfcd7155b3b18adaece3c3c816e36
parent ab5a92bff67d654c543d89b4803a64b2e648253a
Author: zeertzjq <zeertzjq@outlook.com>
Date:   Sun, 28 Dec 2025 08:14:45 +0800

vim-patch:9.1.2024: 'fsync' option cannot be set per buffer (#37129)

Problem:  'fsync' option cannot be set per buffer
Solution: Make 'fsync' option global-local
          (glepnir)

closes: vim/vim#19019

https://github.com/vim/vim/commit/4d5b30372663e8ea356b25fe94334558c6ae283f

Co-authored-by: glepnir <glephunter@gmail.com>
Diffstat:
Mruntime/doc/news.txt | 2+-
Mruntime/doc/options.txt | 4+++-
Mruntime/lua/vim/_meta/options.lua | 4++++
Msrc/nvim/buffer.c | 1+
Msrc/nvim/buffer_defs.h | 1+
Msrc/nvim/bufwrite.c | 6+++---
Msrc/nvim/option.c | 7+++++++
Msrc/nvim/options.lua | 4+++-
Msrc/nvim/undo.c | 3++-
Mtest/old/testdir/test_options.vim | 27++++++++++++++++++---------
10 files changed, 43 insertions(+), 16 deletions(-)

diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt @@ -326,7 +326,7 @@ OPTIONS • 'diffanchors' specifies addresses to anchor a diff. • 'diffopt' `inline:` configures diff highlighting for changes within a line. • 'fillchars' has new flag "foldinner". -• 'grepformat' is now a |global-local| option. +• 'fsync' and 'grepformat' are now |global-local| options. • 'jumpoptions' flag "view" now applies when popping the |tagstack|. • 'maxsearchcount' sets maximum value for |searchcount()| and defaults to 999. • 'pummaxwidth' sets maximum width for the completion popup menu. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt @@ -3197,7 +3197,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'fsync'* *'fs'* *'nofsync'* *'nofs'* 'fsync' 'fs' boolean (default on) - global + global or local to buffer |global-local| When on, the OS function fsync() will be called after saving a file (|:write|, |writefile()|, …), |swap-file|, |undo-persistence| and |shada-file|. This flushes the file to disk, ensuring that it is safely written. @@ -3210,6 +3210,8 @@ A jump table for the options with a short description can be found at |Q_op|. - system signals low battery life - Nvim exits abnormally + This is a |global-local| option, so it can be set per buffer, for + example when writing to a slow filesystem. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua @@ -3002,12 +3002,16 @@ vim.go.fp = vim.go.formatprg --- - system signals low battery life --- - Nvim exits abnormally --- +--- This is a `global-local` option, so it can be set per buffer, for +--- example when writing to a slow filesystem. --- This option cannot be set from a `modeline` or in the `sandbox`, for --- security reasons. --- --- @type boolean vim.o.fsync = true vim.o.fs = vim.o.fsync +vim.bo.fsync = vim.o.fsync +vim.bo.fs = vim.bo.fsync vim.go.fsync = vim.o.fsync vim.go.fs = vim.go.fsync diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c @@ -2145,6 +2145,7 @@ void free_buf_options(buf_T *buf, bool free_p_ff) clear_string_option(&buf->b_p_qe); buf->b_p_ac = -1; buf->b_p_ar = -1; + buf->b_p_fs = -1; buf->b_p_ul = NO_LOCAL_UNDOLEVEL; clear_string_option(&buf->b_p_lw); clear_string_option(&buf->b_p_bkc); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h @@ -586,6 +586,7 @@ struct file_buffer { char *b_p_fp; ///< 'formatprg' char *b_p_fex; ///< 'formatexpr' uint32_t b_p_fex_flags; ///< flags for 'formatexpr' + int b_p_fs; ///< 'fsync' char *b_p_kp; ///< 'keywordprg' int b_p_lisp; ///< 'lisp' char *b_p_lop; ///< 'lispoptions' diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c @@ -1226,7 +1226,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en // the original file. // Don't do this if there is a backup file and we are exiting. if (reset_changed && !newfile && overwriting && !(exiting && backup != NULL)) { - ml_preserve(buf, false, !!p_fs); + ml_preserve(buf, false, !!(buf->b_p_fs >= 0 ? buf->b_p_fs : p_fs)); if (got_int) { err = set_err(_(e_interr)); goto restore_backup; @@ -1575,9 +1575,9 @@ restore_backup: // (could be a pipe). // If the 'fsync' option is false, don't fsync(). Useful for laptops. int error; - if (p_fs && (error = os_fsync(fd)) != 0 && !device + if ((buf->b_p_fs >= 0 ? buf->b_p_fs : p_fs) && (error = os_fsync(fd)) != 0 // fsync not supported on this storage. - && error != UV_ENOTSUP) { + && error != UV_ENOTSUP && !device) { err = set_err_arg(e_fsync, error); end = 0; } diff --git a/src/nvim/option.c b/src/nvim/option.c @@ -387,6 +387,7 @@ void set_init_1(bool clean_arg) curbuf->b_p_initialized = true; curbuf->b_p_ac = -1; curbuf->b_p_ar = -1; // no local 'autoread' value + curbuf->b_p_fs = -1; // no local 'fsync' value curbuf->b_p_ul = NO_LOCAL_UNDOLEVEL; check_buf_options(curbuf); check_win_options(curwin); @@ -3391,6 +3392,7 @@ static OptVal get_option_unset_value(OptIndex opt_idx) switch (opt_idx) { case kOptAutocomplete: case kOptAutoread: + case kOptFsync: return BOOLEAN_OPTVAL(kNone); case kOptScrolloff: case kOptSidescrolloff: @@ -4434,6 +4436,8 @@ void *get_varp_scope_from(vimoption_T *p, int opt_flags, buf_T *buf, win_T *win) switch (opt_idx) { case kOptFormatprg: return &(buf->b_p_fp); + case kOptFsync: + return &(buf->b_p_fs); case kOptFindfunc: return &(buf->b_p_ffu); case kOptErrorformat: @@ -4567,6 +4571,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return *buf->b_p_tsrfu != NUL ? &(buf->b_p_tsrfu) : p->var; case kOptFormatprg: return *buf->b_p_fp != NUL ? &(buf->b_p_fp) : p->var; + case kOptFsync: + return buf->b_p_fs >= 0 ? &(buf->b_p_fs) : p->var; case kOptFindfunc: return *buf->b_p_ffu != NUL ? &(buf->b_p_ffu) : p->var; case kOptErrorformat: @@ -5241,6 +5247,7 @@ void buf_copy_options(buf_T *buf, int flags) // are not copied, start using the global value buf->b_p_ac = -1; buf->b_p_ar = -1; + buf->b_p_fs = -1; buf->b_p_ul = NO_LOCAL_UNDOLEVEL; buf->b_p_bkc = empty_string_option; buf->b_bkc_flags = 0; diff --git a/src/nvim/options.lua b/src/nvim/options.lua @@ -3813,11 +3813,13 @@ local options = { - system signals low battery life - Nvim exits abnormally + This is a |global-local| option, so it can be set per buffer, for + example when writing to a slow filesystem. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. ]=], full_name = 'fsync', - scope = { 'global' }, + scope = { 'global', 'buf' }, secure = true, short_desc = N_('whether to invoke fsync() after file write'), type = 'boolean', diff --git a/src/nvim/undo.c b/src/nvim/undo.c @@ -1340,7 +1340,8 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, } #endif - if (p_fs && fflush(fp) == 0 && os_fsync(fd) != 0) { + if ((buf->b_p_fs >= 0 ? buf->b_p_fs : p_fs) && fflush(fp) == 0 + && os_fsync(fd) != 0) { write_ok = false; } diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim @@ -1662,27 +1662,36 @@ endfunc " Test for setting boolean global-local option value func Test_set_boolean_global_local_option() - setglobal autoread - setlocal noautoread + CheckUnix + + setglobal autoread fsync + setlocal noautoread nofsync call assert_equal(1, &g:autoread) call assert_equal(0, &l:autoread) call assert_equal(0, &autoread) + call assert_equal(1, &g:fsync) + call assert_equal(0, &l:fsync) + call assert_equal(0, &fsync) " :setlocal {option}< set the effective value of {option} to its global value. - "set autoread< - setlocal autoread< + "set autoread< fsync< + setlocal autoread< fsync< call assert_equal(1, &l:autoread) call assert_equal(1, &autoread) + call assert_equal(1, &l:fsync) + call assert_equal(1, &fsync) " :set {option}< removes the local value, so that the global value will be used. - setglobal noautoread - setlocal autoread - "setlocal autoread< - set autoread< + setglobal noautoread nofsync + setlocal autoread fsync + "setlocal autoread< fsync< + set autoread< fsync< call assert_equal(-1, &l:autoread) call assert_equal(0, &autoread) + call assert_equal(-1, &l:fsync) + call assert_equal(0, &fsync) - set autoread& + set autoread& fsync& endfunc func Test_set_in_sandbox()