diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index b30c1d5065..3cda63dd58 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -123,6 +123,7 @@ DEFAULTS • 'statusline' default is exposed as a statusline expression (previously it was implemented as an internal C routine). • Project-local configuration ('exrc') is also loaded from parent directories. + Unset 'exrc' to stop further search. DIAGNOSTICS diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 574e0b9efb..6bf607302c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2416,6 +2416,9 @@ A jump table for the options with a short description can be found at |Q_op|. directories (ordered upwards), if the files are in the |trust| list. Use |:trust| to manage trusted files. See also |vim.secure.read()|. + Unset 'exrc' to stop further searching of 'exrc' files in parent + directories, similar to |editorconfig.root|. + Compare 'exrc' to |editorconfig|: - 'exrc' can execute any code; editorconfig only specifies settings. - 'exrc' is Nvim-specific; editorconfig works in other editors. diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 9ae2412350..52fdfd0aef 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -930,25 +930,29 @@ do group = vim.api.nvim_create_augroup('nvim.find_exrc', {}), desc = 'Find exrc files in parent directories', callback = function() - if vim.o.exrc then - -- Start from parent directory, as exrc file in the current - -- directory is already loaded in do_exrc_initalization(). - local files = vim.fs.find({ '.nvim.lua', '.nvimrc', '.exrc' }, { - type = 'file', - upward = true, - limit = math.huge, - path = vim.fs.dirname(vim.uv.cwd()), - }) - for _, file in ipairs(files) do - local trusted = vim.secure.read(file) --[[@as string|nil]] - if trusted then - if vim.endswith(file, '.lua') then - loadstring(trusted)() - else - vim.api.nvim_exec2(trusted, {}) - end + if not vim.o.exrc then + return + end + local files = vim.fs.find({ '.nvim.lua', '.nvimrc', '.exrc' }, { + type = 'file', + upward = true, + limit = math.huge, + -- exrc in cwd already handled from C, thus start in parent directory. + path = vim.fs.dirname(vim.uv.cwd()), + }) + for _, file in ipairs(files) do + local trusted = vim.secure.read(file) --[[@as string|nil]] + if trusted then + if vim.endswith(file, '.lua') then + loadstring(trusted)() + else + vim.api.nvim_exec2(trusted, {}) end end + -- If the user unset 'exrc' in the current exrc then stop searching + if not vim.o.exrc then + return + end end end, }) diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index b77b135f92..90118cb4a1 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -2080,6 +2080,9 @@ vim.bo.et = vim.bo.expandtab --- directories (ordered upwards), if the files are in the `trust` list. --- Use `:trust` to manage trusted files. See also `vim.secure.read()`. --- +--- Unset 'exrc' to stop further searching of 'exrc' files in parent +--- directories, similar to `editorconfig.root`. +--- --- Compare 'exrc' to `editorconfig`: --- - 'exrc' can execute any code; editorconfig only specifies settings. --- - 'exrc' is Nvim-specific; editorconfig works in other editors. diff --git a/src/nvim/options.lua b/src/nvim/options.lua index f6e24d0e04..75c6f51fae 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2749,6 +2749,9 @@ local options = { directories (ordered upwards), if the files are in the |trust| list. Use |:trust| to manage trusted files. See also |vim.secure.read()|. + Unset 'exrc' to stop further searching of 'exrc' files in parent + directories, similar to |editorconfig.root|. + Compare 'exrc' to |editorconfig|: - 'exrc' can execute any code; editorconfig only specifies settings. - 'exrc' is Nvim-specific; editorconfig works in other editors. diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 463553d4a0..21bd004386 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -1128,6 +1128,10 @@ describe('user config init', function() end before_each(function() + for _, file in ipairs({ '.exrc', '.nvimrc', '.nvim.lua' }) do + os.remove('../' .. file) + os.remove(file) + end write_file( init_lua_path, [[ @@ -1139,7 +1143,10 @@ describe('user config init', function() end) after_each(function() - os.remove(exrc_path) + for _, file in ipairs({ '.exrc', '.nvimrc', '.nvim.lua' }) do + os.remove('../' .. file) + os.remove(file) + end rmdir(xstate) end) @@ -1188,13 +1195,9 @@ describe('user config init', function() end) end - it('exrc from all parent directories', function() - -- make sure that there are not any exrc files left from previous tests - for _, file in ipairs({ '.exrc', '.nvimrc', '.nvim.lua', '../.nvim.lua', '../.nvimrc' }) do - os.remove(file) - end - setup_exrc_file('../.exrc') + it('exrc from parent directories', function() setup_exrc_file('.nvim.lua') + setup_exrc_file('../.exrc') clear { args_rm = { '-u' }, env = xstateenv } local screen = Screen.new(50, 8) screen._default_attr_ids = nil @@ -1206,16 +1209,16 @@ describe('user config init', function() }) -- current directory exrc is found first screen:expect({ any = '.nvim.lua' }) - screen:expect({ any = pesc('[i]gnore, (v)iew, (d)eny, (a)llow:') }) + screen:expect({ any = pesc('[i]gnore, (v)iew, (d)eny, (a)llow:'), unchanged = true }) feed('ia') -- after that the exrc in the parent directory screen:expect({ any = '.exrc' }) - screen:expect({ any = pesc('[i]gnore, (v)iew, (d)eny, (a)llow:') }) - feed('ia') - clear { args_rm = { '-u' }, env = xstateenv } + screen:expect({ any = pesc('[i]gnore, (v)iew, (d)eny, (a)llow:'), unchanged = true }) + feed('a') -- a total of 2 exrc files are executed - eq(2, eval('g:exrc_count')) + feed(':echo g:exrc_count') + screen:expect({ any = '2' }) end) end)