commit e2cb675705221066522ae707ed5f572a0cf0ebcf
parent c1b1c8c2e04ec0ec9b35e5b99d3905d4618b10dd
Author: Jonny Kong <jonnykong1996@gmail.com>
Date: Tue, 28 Oct 2025 18:45:50 -0700
fix(filetype): move fallback logic to vim.filetype.match() #30141
Problem:
Previously, the fallback logic to ".conf" was located outside of
`vim.filetype.match()` and directly within the AutoCmd definition. As a
result, `vim.filetype.match()` would return nil instead of ".conf" for
fallback cases (#30100).
Solution:
Added a boolean return value to `vim.filetype.match()` that indicates
whether the match was the result of fallback. If true, the filetype will
be set using `setf FALLBACK <ft>` instead of `setf <ft>`.
Diffstat:
4 files changed, 43 insertions(+), 12 deletions(-)
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
@@ -2414,6 +2414,11 @@ vim.filetype.match({args}) *vim.filetype.match()*
(`function?`) A function that modifies buffer state when called (for
example, to set some filetype specific buffer variables). The function
accepts a buffer number as its only argument.
+ (`boolean?`) Return true if a match was found by falling back to a
+ generic filetype (e.g. ".conf"). If true, the filetype
+ should be set with `:setf FALLBACK conf`, which enables a later
+ |:setf| command to override the filetype. See `:help setf` for more
+ information.
==============================================================================
diff --git a/runtime/filetype.lua b/runtime/filetype.lua
@@ -11,21 +11,13 @@ vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'StdinReadPost' }, {
if not vim.api.nvim_buf_is_valid(args.buf) then
return
end
- local ft, on_detect = vim.filetype.match({
+ local ft, on_detect, is_fallback = vim.filetype.match({
-- The unexpanded file name is needed here. #27914
-- However, bufname() can't be used, as it doesn't work with :doautocmd. #31306
filename = args.file,
buf = args.buf,
})
- if not ft then
- -- Generic configuration file used as fallback
- ft = require('vim.filetype.detect').conf(args.file, args.buf)
- if ft then
- vim._with({ buf = args.buf }, function()
- vim.api.nvim_cmd({ cmd = 'setf', args = { 'FALLBACK', ft } }, {})
- end)
- end
- else
+ if ft then
-- on_detect is called before setting the filetype so that it can set any buffer local
-- variables that may be used the filetype's ftplugin
if on_detect then
@@ -33,7 +25,10 @@ vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'StdinReadPost' }, {
end
vim._with({ buf = args.buf }, function()
- vim.api.nvim_cmd({ cmd = 'setf', args = { ft } }, {})
+ vim.api.nvim_cmd({
+ cmd = 'setf',
+ args = (is_fallback and { 'FALLBACK', ft } or { ft }),
+ }, {})
end)
end
end,
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
@@ -3101,6 +3101,10 @@ end
---@return function|nil # A function that modifies buffer state when called (for example, to set some
--- filetype specific buffer variables). The function accepts a buffer number as
--- its only argument.
+---@return boolean|nil # Return true if a match was found by falling back to a generic configuration
+--- file (i.e., ".conf"). If true, the filetype should be set with
+--- `:setf FALLBACK conf`, which enables a later |:setf| command to override the
+--- filetype. See `:help setf` for more information.
function M.match(args)
vim.validate('arg', args, 'table')
@@ -3193,11 +3197,19 @@ function M.match(args)
return dispatch(extension[ext], name, bufnr)
end
)
- if ok then
+ if ok and ft then
return ft, on_detect
end
end
end
+
+ -- Generic configuration file used as fallback
+ if name and bufnr then
+ local ft = detect.conf(name, bufnr)
+ if ft then
+ return ft, nil, true
+ end
+ end
end
--- Get the default option value for a {filetype}.
diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua
@@ -210,6 +210,25 @@ describe('vim.filetype', function()
)
rmdir('Xfiletype')
end)
+
+ it('fallback to conf if any of the first five lines start with a #', function()
+ eq(
+ { 'conf', true },
+ exec_lua(function()
+ local bufnr = vim.api.nvim_create_buf(true, false)
+ local lines = {
+ '# foo',
+ }
+ vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
+
+ -- Needs to be set so detect.conf() doesn't fail
+ vim.g.ft_ignore_pat = '\\.\\(Z\\|gz\\|bz2\\|zip\\|tgz\\)$'
+
+ local ft, _, fallback = vim.filetype.match({ buf = bufnr })
+ return { ft, fallback }
+ end)
+ )
+ end)
end)
describe('filetype.lua', function()