diff options
author | Gregory Anders <8965202+gpanders@users.noreply.github.com> | 2022-06-09 13:12:36 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-09 13:12:36 -0600 |
commit | 58323b1fe2e494cf6a75f108780e21c08996c08e (patch) | |
tree | a301a6d047003efa3835460a793a87d4f205f785 /runtime/lua/vim | |
parent | 28e43881b74b800fa37957e77b5e56994e1120cd (diff) | |
download | rneovim-58323b1fe2e494cf6a75f108780e21c08996c08e.tar.gz rneovim-58323b1fe2e494cf6a75f108780e21c08996c08e.tar.bz2 rneovim-58323b1fe2e494cf6a75f108780e21c08996c08e.zip |
feat(filetype): remove side effects from vim.filetype.match (#18894)
Many filetypes from filetype.vim set buffer-local variables, meaning
vim.filetype.match cannot be used without side effects. Instead of
setting these buffer-local variables in the filetype detection functions
themselves, have vim.filetype.match return an optional function value
that, when called, sets these variables. This allows vim.filetype.match
to work without side effects.
Diffstat (limited to 'runtime/lua/vim')
-rw-r--r-- | runtime/lua/vim/filetype.lua | 127 | ||||
-rw-r--r-- | runtime/lua/vim/filetype/detect.lua | 83 |
2 files changed, 127 insertions, 83 deletions
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index c091e2f35c..536580c604 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -26,7 +26,7 @@ end ---@private local function getline(bufnr, start_lnum, end_lnum) end_lnum = end_lnum or start_lnum - local lines = vim.api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum, false) + local lines = api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum, false) return table.concat(lines) or '' end @@ -40,9 +40,9 @@ end function M.getlines(bufnr, start_lnum, end_lnum) if not end_lnum then -- Return a single line as a string - return vim.api.nvim_buf_get_lines(bufnr, start_lnum - 1, start_lnum, false)[1] + return api.nvim_buf_get_lines(bufnr, start_lnum - 1, start_lnum, false)[1] end - return vim.api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum, false) + return api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum, false) end ---@private @@ -1513,12 +1513,14 @@ local filename = { ['/.pinforc'] = 'pinfo', ['.povrayrc'] = 'povini', ['printcap'] = function(path, bufnr) - vim.b[bufnr].ptcap_type = 'print' - return 'ptcap' + return 'ptcap', function(b) + vim.b[b].ptcap_type = 'print' + end end, ['termcap'] = function(path, bufnr) - vim.b[bufnr].ptcap_type = 'term' - return 'ptcap' + return 'ptcap', function(b) + vim.b[b].ptcap_type = 'term' + end end, ['.procmailrc'] = 'procmail', ['.procmail'] = 'procmail', @@ -1604,12 +1606,14 @@ local filename = { ['xdm-config'] = 'xdefaults', ['.Xdefaults'] = 'xdefaults', ['xorg.conf'] = function(path, bufnr) - vim.b[bufnr].xf86conf_xfree86_version = 4 - return 'xf86conf' + return 'xf86conf', function(b) + vim.b[b].xf86conf_xfree86_version = 4 + end end, ['xorg.conf-4'] = function(path, bufnr) - vim.b[bufnr].xf86conf_xfree86_version = 4 - return 'xf86conf' + return 'xf86conf', function(b) + vim.b[b].xf86conf_xfree86_version = 4 + end end, ['/etc/xinetd.conf'] = 'xinetd', fglrxrc = 'xml', @@ -1662,7 +1666,7 @@ local filename = { bashrc = function(path, bufnr) return require('vim.filetype.detect').sh(path, bufnr, 'bash') end, - crontab = starsetf('crontab'), + crontab = 'crontab', ['csh.cshrc'] = function(path, bufnr) return require('vim.filetype.detect').csh(path, bufnr) end, @@ -1682,7 +1686,7 @@ local filename = { return require('vim.filetype.detect').shell(path, bufnr, 'tcsh') end, ['XF86Config'] = function(path, bufnr) - return require('vim.filetype.detect').xf86conf(bufnr) + return require('vim.filetype.detect').xfree86(bufnr) end, -- END FILENAME } @@ -1830,8 +1834,9 @@ local pattern = { ['.*/etc/protocols'] = 'protocols', ['.*printcap.*'] = starsetf(function(path, bufnr) if vim.fn.did_filetype() == 0 then - vim.b[bufnr].ptcap_type = 'print' - return 'ptcap' + return 'ptcap', function(b) + vim.b[b].ptcap_type = 'print' + end end end), ['.*baseq[2-3]/.*%.cfg'] = 'quake', @@ -1881,8 +1886,9 @@ local pattern = { ['.*/%.config/systemd/user/%.#.*'] = 'systemd', ['.*termcap.*'] = starsetf(function(path, bufnr) if vim.fn.did_filetype() == 0 then - vim.b[bufnr].ptcap_type = 'term' - return 'ptcap' + return 'ptcap', function(b) + vim.b[b].ptcap_type = 'term' + end end end), ['.*%.t%.html'] = 'tilde', @@ -1966,12 +1972,14 @@ local pattern = { ['.*%.vhdl_[0-9].*'] = starsetf('vhdl'), ['.*/%.fvwm/.*'] = starsetf('fvwm'), ['.*fvwmrc.*'] = starsetf(function(path, bufnr) - vim.b[bufnr].fvwm_version = 1 - return 'fvwm' + return 'fvwm', function(b) + vim.b[b].fvwm_version = 1 + end end), ['.*fvwm95.*%.hook'] = starsetf(function(path, bufnr) - vim.b[bufnr].fvwm_version = 1 - return 'fvwm' + return 'fvwm', function(b) + vim.b[b].fvwm_version = 1 + end end), ['.*/%.gitconfig%.d/.*'] = starsetf('gitconfig'), ['.*/Xresources/.*'] = starsetf('xdefaults'), @@ -2117,17 +2125,18 @@ local pattern = { ['.*/queries/.*%.scm'] = 'query', -- tree-sitter queries ['.*,v'] = 'rcs', ['.*/xorg%.conf%.d/.*%.conf'] = function(path, bufnr) - vim.b[bufnr].xf86conf_xfree86_version = 4 - return 'xf86config' + return 'xf86config', function(b) + vim.b[b].xf86conf_xfree86_version = 4 + end end, -- Increase priority to run before the pattern below - ['XF86Config%-4'] = starsetf(function(path, bufnr) - vim.b[bufnr].xf86conf_xfree86_version = 4 - return 'xf86config' + ['XF86Config%-4.*'] = starsetf(function(path, bufnr) + return 'xf86conf', function(b) + vim.b[b].xf86conf_xfree86_version = 4 + end end, { priority = -math.huge + 1 }), ['XF86Config.*'] = starsetf(function(path, bufnr) - vim.b[bufnr].xf86conf_xfree86_version = 4 - return require('vim.filetype.detect').xf86conf(bufnr) + return require('vim.filetype.detect').xfree86(bufnr) end), ['[cC]hange[lL]og.*'] = starsetf(function(path, bufnr) local line = getline(bufnr, 1):lower() @@ -2141,8 +2150,9 @@ local pattern = { if vim.fn.fnamemodify(path, ':e') == 'm4' then return 'fvwm2m4' else - vim.b[bufnr].fvwm_version = 2 - return 'fvwm' + return 'fvwm', function(b) + vim.b[b].fvwm_version = 2 + end end end), ['.*%.[Ll][Oo][Gg]'] = function(path, bufnr) @@ -2220,7 +2230,9 @@ end --- filetype directly) or a function. If a function, it takes the full path and --- buffer number of the file as arguments (along with captures from the matched --- pattern, if any) and should return a string that will be used as the ---- buffer's filetype. +--- buffer's filetype. Optionally, the function can return a second function +--- value which, when called, modifies the state of the buffer. This can be used +--- to, for example, set filetype-specific buffer variables. --- --- Filename patterns can specify an optional priority to resolve cases when a --- file path matches multiple patterns. Higher priorities are matched first. @@ -2238,7 +2250,10 @@ end --- foo = "fooscript", --- bar = function(path, bufnr) --- if some_condition() then ---- return "barscript" +--- return "barscript", function(bufnr) +--- -- Set a buffer variable +--- vim.b[bufnr].barscript_version = 2 +--- end --- end --- return "bar" --- end, @@ -2283,13 +2298,13 @@ end ---@private local function dispatch(ft, path, bufnr, ...) + local on_detect if type(ft) == 'function' then - ft = ft(path, bufnr, ...) + ft, on_detect = ft(path, bufnr, ...) end if type(ft) == 'string' then - api.nvim_buf_set_option(bufnr, 'filetype', ft) - return true + return ft, on_detect end -- Any non-falsey value (that is, anything other than 'nil' or 'false') will @@ -2314,11 +2329,20 @@ local function match_pattern(name, path, tail, pat) return matches end ---- Set the filetype for the given buffer from a file name. +--- Find the filetype for the given filename and buffer. --- ---@param name string File name (can be an absolute or relative path) ---@param bufnr number|nil The buffer to set the filetype for. Defaults to the current buffer. +---@return string|nil If a match was found, the matched filetype. +---@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. function M.match(name, bufnr) + vim.validate({ + name = { name, 's' }, + bufnr = { bufnr, 'n', true }, + }) + -- When fired from the main filetypedetect autocommand the {bufnr} argument is omitted, so we use -- the current buffer. The {bufnr} argument is provided to allow extensibility in case callers -- wish to perform filetype detection on buffers other than the current one. @@ -2326,16 +2350,20 @@ function M.match(name, bufnr) name = normalize_path(name) + local ft, on_detect + -- First check for the simple case where the full path exists as a key local path = vim.fn.resolve(vim.fn.fnamemodify(name, ':p')) - if dispatch(filename[path], path, bufnr) then - return + ft, on_detect = dispatch(filename[path], path, bufnr) + if ft then + return ft, on_detect end -- Next check against just the file name local tail = vim.fn.fnamemodify(name, ':t') - if dispatch(filename[tail], path, bufnr) then - return + ft, on_detect = dispatch(filename[tail], path, bufnr) + if ft then + return ft, on_detect end -- Next, check the file path against available patterns with non-negative priority @@ -2348,19 +2376,21 @@ function M.match(name, bufnr) break end - local ft = v[k][1] + local filetype = v[k][1] local matches = match_pattern(name, path, tail, k) if matches then - if dispatch(ft, path, bufnr, matches) then - return + ft, on_detect = dispatch(filetype, path, bufnr, matches) + if ft then + return ft, on_detect end end end -- Next, check file extension local ext = vim.fn.fnamemodify(name, ':e') - if dispatch(extension[ext], path, bufnr) then - return + ft, on_detect = dispatch(extension[ext], path, bufnr) + if ft then + return ft, on_detect end -- Finally, check patterns with negative priority @@ -2368,11 +2398,12 @@ function M.match(name, bufnr) local v = pattern_sorted[i] local k = next(v) - local ft = v[k][1] + local filetype = v[k][1] local matches = match_pattern(name, path, tail, k) if matches then - if dispatch(ft, path, bufnr, matches) then - return + ft, on_detect = dispatch(filetype, path, bufnr, matches) + if ft then + return ft, on_detect end end end diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 2cc75b026e..7e5ed0f4d1 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -27,24 +27,22 @@ local matchregex = vim.filetype.matchregex -- This function checks for the kind of assembly that is wanted by the user, or -- can be detected from the first five lines of the file. function M.asm(bufnr) - -- Make sure b:asmsyntax exists - if not vim.b[bufnr].asmsyntax then - vim.b[bufnr].asmsyntax = '' - end - - if vim.b[bufnr].asmsyntax == '' then - M.asm_syntax(bufnr) + local syntax = vim.b[bufnr].asmsyntax + if not syntax or syntax == '' then + syntax = M.asm_syntax(bufnr) end -- If b:asmsyntax still isn't set, default to asmsyntax or GNU - if vim.b[bufnr].asmsyntax == '' then + if not syntax or syntax == '' then if vim.g.asmsyntax and vim.g.asmsyntax ~= 0 then - vim.b[bufnr].asmsyntax = vim.g.asmsyntax + syntax = vim.g.asmsyntax else - vim.b[bufnr].asmsyntax = 'asm' + syntax = 'asm' end end - return vim.fn.fnameescape(vim.b[bufnr].asmsyntax) + return syntax, function(b) + vim.b[b].asmsyntax = syntax + end end -- Checks the first 5 lines for a asmsyntax=foo override. @@ -53,9 +51,9 @@ function M.asm_syntax(bufnr) local lines = table.concat(getlines(bufnr, 1, 5), ' '):lower() local match = lines:match('%sasmsyntax=([a-zA-Z0-9]+)%s') if match then - vim.b['asmsyntax'] = match + return match elseif findany(lines, { '%.title', '%.ident', '%.macro', '%.subtitle', '%.library' }) then - vim.b['asmsyntax'] = 'vmasm' + return 'vmasm' end end @@ -376,12 +374,13 @@ function M.inc(bufnr) elseif findany(lines, { '^%s{', '^%s%(%*' }) or matchregex(lines, pascal_keywords) then return 'pascal' else - M.asm_syntax(bufnr) - if vim.b[bufnr].asm_syntax then - return vim.fn.fnameescape(vim.b[bufnr].asm_syntax) - else + local syntax = M.asm_syntax(bufnr) + if not syntax or syntax == '' then return 'pov' end + return syntax, function(b) + vim.b[b].asmsyntax = syntax + end end end @@ -778,6 +777,8 @@ function M.sh(path, bufnr, name) return end + local on_detect + if matchregex(name, [[\<csh\>]]) then -- Some .sh scripts contain #!/bin/csh. return M.shell(path, bufnr, 'csh') @@ -788,19 +789,25 @@ function M.sh(path, bufnr, name) elseif matchregex(name, [[\<zsh\>]]) then return M.shell(path, bufnr, 'zsh') elseif matchregex(name, [[\<ksh\>]]) then - vim.b[bufnr].is_kornshell = 1 - vim.b[bufnr].is_bash = nil - vim.b[bufnr].is_sh = nil + on_detect = function(b) + vim.b[b].is_kornshell = 1 + vim.b[b].is_bash = nil + vim.b[b].is_sh = nil + end elseif vim.g.bash_is_sh or matchregex(name, [[\<bash\>]]) or matchregex(name, [[\<bash2\>]]) then - vim.b[bufnr].is_bash = 1 - vim.b[bufnr].is_kornshell = nil - vim.b[bufnr].is_sh = nil + on_detect = function(b) + vim.b[b].is_bash = 1 + vim.b[b].is_kornshell = nil + vim.b[b].is_sh = nil + end elseif matchregex(name, [[\<sh\>]]) then - vim.b[bufnr].is_sh = 1 - vim.b[bufnr].is_kornshell = nil - vim.b[bufnr].is_bash = nil + on_detect = function(b) + vim.b[b].is_sh = 1 + vim.b[b].is_kornshell = nil + vim.b[b].is_bash = nil + end end - return M.shell(path, bufnr, 'sh') + return M.shell(path, bufnr, 'sh'), on_detect end -- For shell-like file types, check for an "exec" command hidden in a comment, as used for Tcl. @@ -918,9 +925,11 @@ function M.xml(bufnr) line = line:lower() local is_docbook5 = line:find([[ xmlns="http://docbook.org/ns/docbook"]]) if is_docbook4 or is_docbook5 then - vim.b[bufnr].docbk_type = 'xml' - vim.b[bufnr].docbk_ver = is_docbook4 and 4 or 5 - return 'docbk' + return 'docbk', + function(b) + vim.b[b].docbk_type = 'xml' + vim.b[b].docbk_ver = is_docbook4 and 4 or 5 + end end if line:find([[xmlns:xbl="http://www.mozilla.org/xbl"]]) then return 'xbl' @@ -954,9 +963,10 @@ function M.sgml(bufnr) if lines:find('linuxdoc') then return 'smgllnx' elseif lines:find('<!DOCTYPE.*DocBook') then - vim.b[bufnr].docbk_type = 'sgml' - vim.b[bufnr].docbk_ver = 4 - return 'docbk' + return 'docbk', function(b) + vim.b[b].docbk_type = 'sgml' + vim.b[b].docbk_ver = 4 + end else return 'sgml' end @@ -1049,10 +1059,13 @@ end -- XFree86 config function M.xfree86(bufnr) local line = getlines(bufnr, 1) + local on_detect if matchregex(line, [[\<XConfigurator\>]]) then - vim.b[bufnr].xf86conf_xfree86_version = 3 - return 'xf86conf' + on_detect = function(b) + vim.b[b].xf86conf_xfree86_version = 3 + end end + return 'xf86conf', on_detect end -- luacheck: pop |