diff options
author | Lewis Russell <lewis6991@gmail.com> | 2023-08-08 16:36:06 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-08 16:36:06 +0100 |
commit | c6c21db82b31ea43ce878ab3725dcd901db1e7a1 (patch) | |
tree | 6002ddf7e9ef9d2ca40ef0029f501864ff43cbd9 | |
parent | 61ed45486da01dca788f8a444fc2021002f0c947 (diff) | |
download | rneovim-c6c21db82b31ea43ce878ab3725dcd901db1e7a1.tar.gz rneovim-c6c21db82b31ea43ce878ab3725dcd901db1e7a1.tar.bz2 rneovim-c6c21db82b31ea43ce878ab3725dcd901db1e7a1.zip |
fix(filetype): add typing and dry (#24573)
-rw-r--r-- | runtime/doc/lua.txt | 6 | ||||
-rw-r--r-- | runtime/lua/vim/_editor.lua | 7 | ||||
-rw-r--r-- | runtime/lua/vim/filetype.lua | 1205 | ||||
-rw-r--r-- | runtime/lua/vim/filetype/detect.lua | 546 | ||||
-rw-r--r-- | scripts/lua2dox.lua | 6 |
5 files changed, 823 insertions, 947 deletions
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index efde69c8f4..c3e540a4ae 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -2654,10 +2654,10 @@ vim.filetype.add({filetypes}) *vim.filetype.add()* ['.*'] = { priority = -math.huge, function(path, bufnr) - local content = vim.filetype.getlines(bufnr, 1) - if vim.filetype.matchregex(content, [[^#!.*\<mine\>]]) then + local content = vim.api.nvim_buf_get_lines(bufnr, 0, 1, false)[1] or '' + if vim.regex([[^#!.*\<mine\>]]):match_str(content) ~= nil then return 'mine' - elseif vim.filetype.matchregex(content, [[\<drawing\>]]) then + elseif vim.regex([[\<drawing\>]]):match_str(content) ~= nil then return 'drawing' end end, diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 6182270708..d81464a3ca 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -428,10 +428,17 @@ vim.cmd = setmetatable({}, { end, }) +--- @class vim.var_accessor +--- @field [string] any +--- @field [integer] vim.var_accessor + -- These are the vim.env/v/g/o/bo/wo variable magic accessors. do local validate = vim.validate + --- @param scope string + --- @param handle? false|integer + --- @return vim.var_accessor local function make_dict_accessor(scope, handle) validate({ scope = { scope, 's' }, diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 66e224ce69..a53216f8ea 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -1,7 +1,16 @@ local api = vim.api +local fn = vim.fn local M = {} +--- @alias vim.filetype.mapfn fun(path:string,bufnr:integer, ...):string?, fun(b:integer)? +--- @alias vim.filetype.maptbl {[1]:string|vim.filetype.mapfn, [2]:{priority:integer}} +--- @alias vim.filetype.mapping.value string|vim.filetype.mapfn|vim.filetype.maptbl +--- @alias vim.filetype.mapping table<string,vim.filetype.mapping.value> + +--- @param ft string|vim.filetype.mapfn +--- @param opts? {priority:integer} +--- @return vim.filetype.maptbl local function starsetf(ft, opts) return { function(path, bufnr) @@ -23,36 +32,38 @@ local function starsetf(ft, opts) end ---@private ---- Get a single line or line range from the buffer. ---- If only start_lnum is specified, return a single line as a string. ---- If both start_lnum and end_lnum are omitted, return all lines from the buffer. ---- +--- Get a line range from the buffer. ---@param bufnr integer The buffer to get the lines from ---@param start_lnum integer|nil The line number of the first line (inclusive, 1-based) ---@param end_lnum integer|nil The line number of the last line (inclusive, 1-based) ----@return table<string>|string Array of lines, or string when end_lnum is omitted -function M.getlines(bufnr, start_lnum, end_lnum) - if end_lnum then - -- Return a line range - return api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum, false) - end +---@return string[] # Array of lines +function M._getlines(bufnr, start_lnum, end_lnum) if start_lnum then - -- Return a single line - return api.nvim_buf_get_lines(bufnr, start_lnum - 1, start_lnum, false)[1] or '' - else - -- Return all lines - return api.nvim_buf_get_lines(bufnr, 0, -1, false) + return api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum or start_lnum, false) end + + -- Return all lines + return api.nvim_buf_get_lines(bufnr, 0, -1, false) +end + +---@private +--- Get a single line from the buffer. +---@param bufnr integer The buffer to get the lines from +---@param start_lnum integer The line number of the first line (inclusive, 1-based) +---@return string +function M._getline(bufnr, start_lnum) + -- Return a single line + return api.nvim_buf_get_lines(bufnr, start_lnum - 1, start_lnum, false)[1] or '' end ---@private --- Check whether a string matches any of the given Lua patterns. --- ----@param s string The string to check ----@param patterns table<string> A list of Lua patterns +---@param s string? The string to check +---@param patterns string[] A list of Lua patterns ---@return boolean `true` if s matched a pattern, else `false` -function M.findany(s, patterns) - if s == nil then +function M._findany(s, patterns) + if not s then return false end for _, v in ipairs(patterns) do @@ -69,8 +80,8 @@ end ---@param bufnr integer The buffer to get the line from ---@param start_lnum integer The line number of the first line to start from (inclusive, 1-based) ---@return string|nil The first non-blank line if found or `nil` otherwise -function M.nextnonblank(bufnr, start_lnum) - for _, line in ipairs(M.getlines(bufnr, start_lnum, -1)) do +function M._nextnonblank(bufnr, start_lnum) + for _, line in ipairs(M._getlines(bufnr, start_lnum, -1)) do if not line:find('^%s*$') then return line end @@ -78,30 +89,93 @@ function M.nextnonblank(bufnr, start_lnum) return nil end ----@private ---- Check whether the given string matches the Vim regex pattern. -M.matchregex = (function() - local cache = {} - return function(s, pattern) - if s == nil then - return nil +do + --- @type table<string,vim.regex> + local regex_cache = {} + + ---@private + --- Check whether the given string matches the Vim regex pattern. + --- @param s string? + --- @param pattern string + --- @return boolean + function M._matchregex(s, pattern) + if not s then + return false + end + if not regex_cache[pattern] then + regex_cache[pattern] = vim.regex(pattern) + end + return regex_cache[pattern]:match_str(s) ~= nil + end +end + +--- @module 'vim.filetype.detect' +local detect = setmetatable({}, { + --- @param k string + --- @param t table<string,function> + --- @return function + __index = function(t, k) + t[k] = function(...) + return require('vim.filetype.detect')[k](...) + end + return t[k] + end, +}) + +--- @param ... string|vim.filetype.mapfn +--- @return vim.filetype.mapfn +local function detect_seq(...) + local candidates = { ... } + return function(...) + for _, c in ipairs(candidates) do + if type(c) == 'string' then + return c + end + if type(c) == 'function' then + local r = c(...) + if r then + return r + end + end end - if not cache[pattern] then - cache[pattern] = vim.regex(pattern) + end +end + +local function detect_noext(path, bufnr) + local root = fn.fnamemodify(path, ':r') + return M.match({ buf = bufnr, filename = root }) +end + +--- @param pat string +--- @param a string? +--- @param b string? +--- @return vim.filetype.mapfn +local function detect_line1(pat, a, b) + return function(_path, bufnr) + if M._getline(bufnr, 1):find(pat) then + return a end - return cache[pattern]:match_str(s) + return b end -end)() +end + +--- @type vim.filetype.mapfn +local detect_rc = function(path, _bufnr) + if not path:find('/etc/Muttrc%.d/') then + return 'rc' + end +end -- luacheck: push no unused args -- luacheck: push ignore 122 -- Filetypes based on file extension ---@diagnostic disable: unused-local +--- @type vim.filetype.mapping local extension = { -- BEGIN EXTENSION ['8th'] = '8th', - ['a65'] = 'a65', + a65 = 'a65', aap = 'aap', abap = 'abap', abc = 'abc', @@ -127,51 +201,33 @@ local extension = { end return 'aspvbs' end, - asm = function(path, bufnr) - return require('vim.filetype.detect').asm(bufnr) - end, - lst = function(path, bufnr) - return require('vim.filetype.detect').asm(bufnr) - end, - mac = function(path, bufnr) - return require('vim.filetype.detect').asm(bufnr) - end, - ['asn1'] = 'asn', + asm = detect.asm, + lst = detect.asm, + mac = detect.asm, + asn1 = 'asn', asn = 'asn', - asp = function(path, bufnr) - return require('vim.filetype.detect').asp(bufnr) - end, + asp = detect.asp, astro = 'astro', atl = 'atlas', as = 'atlas', ahk = 'autohotkey', - ['au3'] = 'autoit', + au3 = 'autoit', ave = 'ave', gawk = 'awk', awk = 'awk', ref = 'b', imp = 'b', mch = 'b', - bas = function(path, bufnr) - return require('vim.filetype.detect').bas(bufnr) - end, + bas = detect.bas, bass = 'bass', - bi = function(path, bufnr) - return require('vim.filetype.detect').bas(bufnr) - end, - bm = function(path, bufnr) - return require('vim.filetype.detect').bas(bufnr) - end, + bi = detect.bas, + bm = detect.bas, bc = 'bc', bdf = 'bdf', beancount = 'beancount', bib = 'bib', - com = function(path, bufnr) - return require('vim.filetype.detect').bindzone(bufnr, 'dcl') - end, - db = function(path, bufnr) - return require('vim.filetype.detect').bindzone(bufnr) - end, + com = detect_seq(detect.bindzone, 'dcl'), + db = detect.bindzone, bicep = 'bicep', bb = 'bitbake', bbappend = 'bitbake', @@ -201,9 +257,7 @@ local extension = { hgrc = 'cfg', chf = 'ch', chai = 'chaiscript', - ch = function(path, bufnr) - return require('vim.filetype.detect').change(bufnr) - end, + ch = detect.change, chs = 'chaskell', chatito = 'chatito', chopro = 'chordpro', @@ -226,7 +280,7 @@ local extension = { atg = 'coco', recipe = 'conaryrecipe', hook = function(path, bufnr) - return M.getlines(bufnr, 1) == '[Trigger]' and 'conf' + return M._getline(bufnr, 1) == '[Trigger]' and 'conf' or nil end, nmconnection = 'confini', mklx = 'context', @@ -234,16 +288,10 @@ local extension = { mkii = 'context', mkxl = 'context', mkvi = 'context', - control = function(path, bufnr) - return require('vim.filetype.detect').control(bufnr) - end, - copyright = function(path, bufnr) - return require('vim.filetype.detect').copyright(bufnr) - end, + control = detect.control, + copyright = detect.copyright, corn = 'corn', - csh = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, + csh = detect.csh, cpon = 'cpon', moc = 'cpp', hh = 'cpp', @@ -261,12 +309,8 @@ local extension = { cppm = 'cpp', cxxm = 'cpp', ['c++m'] = 'cpp', - cpp = function(path, bufnr) - return vim.g.cynlib_syntax_for_cpp and 'cynlib' or 'cpp' - end, - cc = function(path, bufnr) - return vim.g.cynlib_syntax_for_cc and 'cynlib' or 'cpp' - end, + cpp = detect.cpp, + cc = detect.cpp, cql = 'cqlang', crm = 'crm', cr = 'crystal', @@ -291,15 +335,9 @@ local extension = { drt = 'dart', ds = 'datascript', dcd = 'dcd', - decl = function(path, bufnr) - return require('vim.filetype.detect').decl(bufnr) - end, - dec = function(path, bufnr) - return require('vim.filetype.detect').decl(bufnr) - end, - dcl = function(path, bufnr) - return require('vim.filetype.detect').decl(bufnr) or 'clean' - end, + decl = detect.decl, + dec = detect.decl, + dcl = detect_seq(detect.decl, 'clean'), def = 'def', desc = 'desc', directory = 'desktop', @@ -317,27 +355,19 @@ local extension = { drac = 'dracula', drc = 'dracula', dtd = 'dtd', - d = function(path, bufnr) - return require('vim.filetype.detect').dtrace(bufnr) - end, + d = detect.dtrace, dts = 'dts', dtsi = 'dts', dylan = 'dylan', intr = 'dylanintr', lid = 'dylanlid', - e = function(path, bufnr) - return require('vim.filetype.detect').e(bufnr) - end, - E = function(path, bufnr) - return require('vim.filetype.detect').e(bufnr) - end, + e = detect.e, + E = detect.e, ecd = 'ecd', edf = 'edif', edif = 'edif', edo = 'edif', - edn = function(path, bufnr) - return require('vim.filetype.detect').edn(bufnr) - end, + edn = detect.edn, eex = 'eelixir', leex = 'eelixir', am = 'elf', @@ -345,9 +375,7 @@ local extension = { elm = 'elm', lc = 'elsa', elv = 'elvish', - ent = function(path, bufnr) - return require('vim.filetype.detect').ent(bufnr) - end, + ent = detect.ent, epp = 'epuppet', erl = 'erlang', hrl = 'erlang', @@ -358,43 +386,23 @@ local extension = { ec = 'esqlc', EC = 'esqlc', strl = 'esterel', - eu = function(path, bufnr) - return vim.g.filetype_euphoria or 'euphoria3' - end, - EU = function(path, bufnr) - return vim.g.filetype_euphoria or 'euphoria3' - end, - ew = function(path, bufnr) - return vim.g.filetype_euphoria or 'euphoria3' - end, - EW = function(path, bufnr) - return vim.g.filetype_euphoria or 'euphoria3' - end, - EX = function(path, bufnr) - return vim.g.filetype_euphoria or 'euphoria3' - end, - exu = function(path, bufnr) - return vim.g.filetype_euphoria or 'euphoria3' - end, - EXU = function(path, bufnr) - return vim.g.filetype_euphoria or 'euphoria3' - end, - exw = function(path, bufnr) - return vim.g.filetype_euphoria or 'euphoria3' - end, - EXW = function(path, bufnr) - return vim.g.filetype_euphoria or 'euphoria3' - end, - ex = function(path, bufnr) - return require('vim.filetype.detect').ex(bufnr) - end, + eu = detect.euphoria, + EU = detect.euphoria, + ew = detect.euphoria, + EW = detect.euphoria, + EX = detect.euphoria, + exu = detect.euphoria, + EXU = detect.euphoria, + exw = detect.euphoria, + EXW = detect.euphoria, + ex = detect.ex, exp = 'expect', factor = 'factor', fal = 'falcon', fan = 'fan', fwt = 'fan', fnl = 'fennel', - ['m4gl'] = 'fgl', + m4gl = 'fgl', ['4gl'] = 'fgl', ['4gh'] = 'fgl', fir = 'firrtl', @@ -404,33 +412,29 @@ local extension = { fth = 'forth', ft = 'forth', FOR = 'fortran', - ['f77'] = 'fortran', - ['f03'] = 'fortran', + f77 = 'fortran', + f03 = 'fortran', fortran = 'fortran', - ['F95'] = 'fortran', - ['f90'] = 'fortran', - ['F03'] = 'fortran', + F95 = 'fortran', + f90 = 'fortran', + F03 = 'fortran', fpp = 'fortran', FTN = 'fortran', ftn = 'fortran', ['for'] = 'fortran', - ['F90'] = 'fortran', - ['F77'] = 'fortran', - ['f95'] = 'fortran', + F90 = 'fortran', + F77 = 'fortran', + f95 = 'fortran', FPP = 'fortran', f = 'fortran', F = 'fortran', - ['F08'] = 'fortran', - ['f08'] = 'fortran', + F08 = 'fortran', + f08 = 'fortran', fpc = 'fpcmake', fsl = 'framescript', - frm = function(path, bufnr) - return require('vim.filetype.detect').frm(bufnr) - end, + frm = detect.frm, fb = 'freebasic', - fs = function(path, bufnr) - return require('vim.filetype.detect').fs(bufnr) - end, + fs = detect.fs, fsh = 'fsh', fsi = 'fsharp', fsx = 'fsharp', @@ -478,9 +482,7 @@ local extension = { ht = 'haste', htpp = 'hastepreproc', hb = 'hb', - h = function(path, bufnr) - return require('vim.filetype.detect').header(bufnr) - end, + h = detect.header, sum = 'hercules', errsum = 'hercules', ev = 'hercules', @@ -495,56 +497,28 @@ local extension = { hog = 'hog', hws = 'hollywood', hoon = 'hoon', - cpt = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) - end, - dtml = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) - end, - htm = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) - end, - html = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) - end, - pt = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) - end, - shtml = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) - end, - stm = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) - end, + cpt = detect.html, + dtml = detect.html, + htm = detect.html, + html = detect.html, + pt = detect.html, + shtml = detect.html, + stm = detect.html, htt = 'httest', htb = 'httest', - hw = function(path, bufnr) - return require('vim.filetype.detect').hw(bufnr) - end, - module = function(path, bufnr) - return require('vim.filetype.detect').hw(bufnr) - end, - pkg = function(path, bufnr) - return require('vim.filetype.detect').hw(bufnr) - end, + hw = detect.hw, + module = detect.hw, + pkg = detect.hw, iba = 'ibasic', ibi = 'ibasic', icn = 'icon', - idl = function(path, bufnr) - return require('vim.filetype.detect').idl(bufnr) - end, - inc = function(path, bufnr) - return require('vim.filetype.detect').inc(bufnr) - end, + idl = detect.idl, + inc = detect.inc, inf = 'inform', INF = 'inform', ii = 'initng', - inp = function(path, bufnr) - return require('vim.filetype.detect').inp(bufnr) - end, - ms = function(path, bufnr) - return require('vim.filetype.detect').nroff(bufnr) or 'xmath' - end, + inp = detect.inp, + ms = detect_seq(detect.nroff, 'xmath'), iss = 'iss', mst = 'ist', ist = 'ist', @@ -566,7 +540,7 @@ local extension = { jsx = 'javascriptreact', clp = 'jess', jgr = 'jgraph', - ['j73'] = 'jovial', + j73 = 'jovial', jov = 'jovial', jovial = 'jovial', properties = 'jproperties', @@ -628,29 +602,21 @@ local extension = { lou = 'lout', ulpc = 'lpc', lpc = 'lpc', - c = function(path, bufnr) - return require('vim.filetype.detect').lpc(bufnr) - end, - lsl = function(path, bufnr) - return require('vim.filetype.detect').lsl(bufnr) - end, + c = detect.lpc, + lsl = detect.lsl, lss = 'lss', nse = 'lua', rockspec = 'lua', lua = 'lua', luau = 'luau', lrc = 'lyrics', - m = function(path, bufnr) - return require('vim.filetype.detect').m(bufnr) - end, + m = detect.m, at = 'm4', - mc = function(path, bufnr) - return require('vim.filetype.detect').mc(bufnr) - end, + mc = detect.mc, quake = 'm3quake', - ['m4'] = function(path, bufnr) + m4 = function(path, bufnr) path = path:lower() - return not (path:find('html%.m4$') or path:find('fvwm2rc')) and 'm4' + return not (path:find('html%.m4$') or path:find('fvwm2rc')) and 'm4' or nil end, eml = 'mail', mk = 'make', @@ -689,16 +655,12 @@ local extension = { mib = 'mib', mix = 'mix', mixal = 'mix', - mm = function(path, bufnr) - return require('vim.filetype.detect').mm(bufnr) - end, + mm = detect.mm, nb = 'mma', mmp = 'mmp', - mms = function(path, bufnr) - return require('vim.filetype.detect').mms(bufnr) - end, + mms = detect.mms, DEF = 'modula2', - ['m2'] = 'modula2', + m2 = 'modula2', mi = 'modula2', lm3 = 'modula3', ssc = 'monk', @@ -709,28 +671,16 @@ local extension = { moon = 'moonscript', move = 'move', mp = 'mp', - mpiv = function(path, bufnr) - return 'mp', function(b) - vim.b[b].mp_metafun = 1 - end - end, - mpvi = function(path, bufnr) - return 'mp', function(b) - vim.b[b].mp_metafun = 1 - end - end, - mpxl = function(path, bufnr) - return 'mp', function(b) - vim.b[b].mp_metafun = 1 - end - end, + mpiv = detect.mp, + mpvi = detect.mp, + mpxl = detect.mp, mof = 'msidl', odl = 'msidl', msql = 'msql', mu = 'mupad', mush = 'mush', mysql = 'mysql', - ['n1ql'] = 'n1ql', + n1ql = 'n1ql', nql = 'n1ql', nanorc = 'nanorc', ncf = 'ncf', @@ -798,12 +748,10 @@ local extension = { pike = 'pike', pmod = 'pike', rcp = 'pilrc', - PL = function(path, bufnr) - return require('vim.filetype.detect').pl(bufnr) - end, + PL = detect.pl, pli = 'pli', - ['pl1'] = 'pli', - ['p36'] = 'plm', + pl1 = 'pli', + p36 = 'plm', plm = 'plm', pac = 'plm', plp = 'plp', @@ -832,11 +780,11 @@ local extension = { pml = 'promela', proto = 'proto', prql = 'prql', - ['psd1'] = 'ps1', - ['psm1'] = 'ps1', - ['ps1'] = 'ps1', + psd1 = 'ps1', + psm1 = 'ps1', + ps1 = 'ps1', pssc = 'ps1', - ['ps1xml'] = 'ps1xml', + ps1xml = 'ps1xml', psf = 'psf', psl = 'psl', pug = 'pug', @@ -850,22 +798,20 @@ local extension = { ql = 'ql', qll = 'ql', qmd = 'quarto', - R = function(path, bufnr) - return require('vim.filetype.detect').r(bufnr) - end, + R = detect.r, rkt = 'racket', rktd = 'racket', rktl = 'racket', rad = 'radiance', mat = 'radiance', - ['pod6'] = 'raku', + pod6 = 'raku', rakudoc = 'raku', rakutest = 'raku', rakumod = 'raku', - ['pm6'] = 'raku', + pm6 = 'raku', raku = 'raku', - ['t6'] = 'raku', - ['p6'] = 'raku', + t6 = 'raku', + p6 = 'raku', raml = 'raml', rbs = 'rbs', rego = 'rego', @@ -938,34 +884,18 @@ local extension = { sdl = 'sdl', sed = 'sed', sexp = 'sexplib', - bash = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - ebuild = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - eclass = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - env = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr)) - end, - ksh = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'ksh') - end, - sh = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr)) - end, + bash = detect.bash, + ebuild = detect.bash, + eclass = detect.bash, + env = detect.sh, + ksh = detect.ksh, + sh = detect.sh, sieve = 'sieve', siv = 'sieve', - sig = function(path, bufnr) - return require('vim.filetype.detect').sig(bufnr) - end, - sil = function(path, bufnr) - return require('vim.filetype.detect').sil(bufnr) - end, + sig = detect.sig, + sil = detect.sil, sim = 'simula', - ['s85'] = 'sinda', + s85 = 'sinda', sin = 'sinda', ssm = 'sisu', sst = 'sisu', @@ -1002,9 +932,7 @@ local extension = { spi = 'spyce', spy = 'spyce', tyc = 'sql', - typ = function(path, bufnr) - return require('vim.filetype.detect').typ(bufnr) - end, + typ = detect.typ, pkb = 'sql', tyb = 'sql', pks = 'sql', @@ -1012,11 +940,11 @@ local extension = { sqi = 'sqr', sqr = 'sqr', nut = 'squirrel', - ['s28'] = 'srec', - ['s37'] = 'srec', + s28 = 'srec', + s37 = 'srec', srec = 'srec', mot = 'srec', - ['s19'] = 'srec', + s19 = 'srec', srt = 'srt', ssa = 'ssa', ass = 'ssa', @@ -1056,9 +984,7 @@ local extension = { bbl = 'tex', latex = 'tex', sty = 'tex', - cls = function(path, bufnr) - return require('vim.filetype.detect').cls(bufnr) - end, + cls = detect.cls, texi = 'texinfo', txi = 'texinfo', texinfo = 'texinfo', @@ -1077,9 +1003,7 @@ local extension = { tsv = 'tsv', tutor = 'tutor', twig = 'twig', - ts = function(path, bufnr) - return M.getlines(bufnr, 1):find('<%?xml') and 'xml' or 'typescript' - end, + ts = detect_line1('<%?xml', 'xml', 'typescript'), mts = 'typescript', cts = 'typescript', tsx = 'typescriptreact', @@ -1104,9 +1028,7 @@ local extension = { vr = 'vera', vri = 'vera', vrh = 'vera', - v = function(path, bufnr) - return require('vim.filetype.detect').v(bufnr) - end, + v = detect.v, va = 'verilogams', vams = 'verilogams', vhdl = 'vhdl', @@ -1136,7 +1058,7 @@ local extension = { xht = 'xhtml', msc = 'xmath', msf = 'xmath', - ['psc1'] = 'xml', + psc1 = 'xml', tpm = 'xml', xliff = 'xml', atom = 'xml', @@ -1152,10 +1074,8 @@ local extension = { csproj = 'xml', wpl = 'xml', xmi = 'xml', - xpm = function(path, bufnr) - return M.getlines(bufnr, 1):find('XPM2') and 'xpm2' or 'xpm' - end, - ['xpm2'] = 'xpm2', + xpm = detect_line1('XPM2', 'xpm2', 'xpm'), + xpm2 = 'xpm2', xqy = 'xquery', xqm = 'xquery', xquery = 'xquery', @@ -1172,7 +1092,7 @@ local extension = { yaml = 'yaml', yang = 'yang', yuck = 'yuck', - ['z8a'] = 'z8a', + z8a = 'z8a', zig = 'zig', zir = 'zir', zu = 'zimbu', @@ -1180,183 +1100,69 @@ local extension = { zs = 'zserio', zsh = 'zsh', vala = 'vala', - web = function(path, bufnr) - return require('vim.filetype.detect').web(bufnr) - end, - pl = function(path, bufnr) - return require('vim.filetype.detect').pl(bufnr) - end, - pp = function(path, bufnr) - return require('vim.filetype.detect').pp(bufnr) - end, - i = function(path, bufnr) - return require('vim.filetype.detect').progress_asm(bufnr) - end, - w = function(path, bufnr) - return require('vim.filetype.detect').progress_cweb(bufnr) - end, - p = function(path, bufnr) - return require('vim.filetype.detect').progress_pascal(bufnr) - end, - pro = function(path, bufnr) - return require('vim.filetype.detect').proto(bufnr, 'idlang') - end, - patch = function(path, bufnr) - return require('vim.filetype.detect').patch(bufnr) - end, - r = function(path, bufnr) - return require('vim.filetype.detect').r(bufnr) - end, - rdf = function(path, bufnr) - return require('vim.filetype.detect').redif(bufnr) - end, - rules = function(path, bufnr) - return require('vim.filetype.detect').rules(path) - end, - sc = function(path, bufnr) - return require('vim.filetype.detect').sc(bufnr) - end, - scd = function(path, bufnr) - return require('vim.filetype.detect').scd(bufnr) - end, + web = detect.web, + pl = detect.pl, + pp = detect.pp, + i = detect.progress_asm, + w = detect.progress_cweb, + p = detect.progress_pascal, + pro = detect_seq(detect.proto, 'idlang'), + patch = detect.patch, + r = detect.r, + rdf = detect.redif, + rules = detect.rules, + sc = detect.sc, + scd = detect.scd, tcsh = function(path, bufnr) - return require('vim.filetype.detect').shell(path, M.getlines(bufnr), 'tcsh') - end, - sql = function(path, bufnr) - return vim.g.filetype_sql and vim.g.filetype_sql or 'sql' - end, - zsql = function(path, bufnr) - return vim.g.filetype_sql and vim.g.filetype_sql or 'sql' - end, - tex = function(path, bufnr) - return require('vim.filetype.detect').tex(path, bufnr) - end, - tf = function(path, bufnr) - return require('vim.filetype.detect').tf(bufnr) - end, - txt = function(path, bufnr) - return require('vim.filetype.detect').txt(bufnr) - end, - xml = function(path, bufnr) - return require('vim.filetype.detect').xml(bufnr) - end, - y = function(path, bufnr) - return require('vim.filetype.detect').y(bufnr) - end, - cmd = function(path, bufnr) - return M.getlines(bufnr, 1):find('^/%*') and 'rexx' or 'dosbatch' - end, - rul = function(path, bufnr) - return require('vim.filetype.detect').rul(bufnr) - end, - cpy = function(path, bufnr) - return M.getlines(bufnr, 1):find('^##') and 'python' or 'cobol' - end, - dsl = function(path, bufnr) - return M.getlines(bufnr, 1):find('^%s*<!') and 'dsl' or 'structurizr' - end, - smil = function(path, bufnr) - return M.getlines(bufnr, 1):find('<%?%s*xml.*%?>') and 'xml' or 'smil' - end, - smi = function(path, bufnr) - return require('vim.filetype.detect').smi(bufnr) - end, - install = function(path, bufnr) - return require('vim.filetype.detect').install(path, bufnr) - end, - pm = function(path, bufnr) - return require('vim.filetype.detect').pm(bufnr) - end, - me = function(path, bufnr) - return require('vim.filetype.detect').me(path) - end, - reg = function(path, bufnr) - return require('vim.filetype.detect').reg(bufnr) - end, - ttl = function(path, bufnr) - return require('vim.filetype.detect').ttl(bufnr) - end, - rc = function(path, bufnr) - if not path:find('/etc/Muttrc%.d/') then - return 'rc' - end - end, - rch = function(path, bufnr) - if not path:find('/etc/Muttrc%.d/') then - return 'rc' - end - end, - class = function(path, bufnr) - require('vim.filetype.detect').class(bufnr) - end, - sgml = function(path, bufnr) - return require('vim.filetype.detect').sgml(bufnr) - end, - sgm = function(path, bufnr) - return require('vim.filetype.detect').sgml(bufnr) - end, - t = function(path, bufnr) - local nroff = require('vim.filetype.detect').nroff(bufnr) - return nroff or require('vim.filetype.detect').perl(path, bufnr) or 'tads' - end, + return require('vim.filetype.detect').shell(path, M._getlines(bufnr), 'tcsh') + end, + sql = detect.sql, + zsql = detect.sql, + tex = detect.tex, + tf = detect.tf, + txt = detect.txt, + xml = detect.xml, + y = detect.y, + cmd = detect_line1('^/%*', 'rexx', 'dosbatch'), + rul = detect.rul, + cpy = detect_line1('^##', 'python', 'cobol'), + dsl = detect_line1('^%s*<!', 'dsl', 'structurizr'), + smil = detect_line1('<%?%s*xml.*%?>', 'xml', 'smil'), + smi = detect.smi, + install = detect.install, + pm = detect.pm, + me = detect.me, + reg = detect.reg, + ttl = detect.ttl, + rc = detect_rc, + rch = detect_rc, + class = detect.class, + sgml = detect.sgml, + sgm = detect.sgml, + t = detect_seq(detect.nroff, detect.perl, 'tads'), -- Ignored extensions - bak = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, - ['dpkg-bak'] = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, - ['dpkg-dist'] = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, - ['dpkg-old'] = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, - ['dpkg-new'] = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, + bak = detect_noext, + ['dpkg-bak'] = detect_noext, + ['dpkg-dist'] = detect_noext, + ['dpkg-old'] = detect_noext, + ['dpkg-new'] = detect_noext, ['in'] = function(path, bufnr) if vim.fs.basename(path) ~= 'configure.in' then - local root = vim.fn.fnamemodify(path, ':r') + local root = fn.fnamemodify(path, ':r') return M.match({ buf = bufnr, filename = root }) end end, - new = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, - old = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, - orig = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, - pacsave = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, - pacnew = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, - rpmsave = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, - rmpnew = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match({ buf = bufnr, filename = root }) - end, + new = detect_noext, + old = detect_noext, + orig = detect_noext, + pacsave = detect_noext, + pacnew = detect_noext, + rpmsave = detect_noext, + rmpnew = detect_noext, -- END EXTENSION } +--- @type vim.filetype.mapping local filename = { -- BEGIN FILENAME ['a2psrc'] = 'a2ps', @@ -1387,24 +1193,12 @@ local filename = { ['/etc/defaults/cdrdao'] = 'cdrdaoconf', ['cfengine.conf'] = 'cfengine', ['CMakeLists.txt'] = 'cmake', - ['.alias'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['.cshrc'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['.login'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['csh.cshrc'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['csh.login'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['csh.logout'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, + ['.alias'] = detect.csh, + ['.cshrc'] = detect.csh, + ['.login'] = detect.csh, + ['csh.cshrc'] = detect.csh, + ['csh.login'] = detect.csh, + ['csh.logout'] = detect.csh, ['auto.master'] = 'conf', ['configure.in'] = 'config', ['configure.ac'] = 'config', @@ -1444,18 +1238,10 @@ local filename = { ['exim.conf'] = 'exim', exports = 'exports', ['.fetchmailrc'] = 'fetchmail', - fvSchemes = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - fvSolution = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - fvConstraints = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - fvModels = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, + fvSchemes = detect.foam, + fvSolution = detect.foam, + fvConstraints = detect.foam, + fvModels = detect.foam, fstab = 'fstab', mtab = 'fstab', ['.gdbinit'] = 'gdb', @@ -1463,11 +1249,11 @@ local filename = { ['.gdbearlyinit'] = 'gdb', gdbearlyinit = 'gdb', ['lltxxxxx.txt'] = 'gedcom', - ['TAG_EDITMSG'] = 'gitcommit', - ['MERGE_MSG'] = 'gitcommit', - ['COMMIT_EDITMSG'] = 'gitcommit', - ['NOTES_EDITMSG'] = 'gitcommit', - ['EDIT_DESCRIPTION'] = 'gitcommit', + TAG_EDITMSG = 'gitcommit', + MERGE_MSG = 'gitcommit', + COMMIT_EDITMSG = 'gitcommit', + NOTES_EDITMSG = 'gitcommit', + EDIT_DESCRIPTION = 'gitcommit', ['.gitconfig'] = 'gitconfig', ['.gitmodules'] = 'gitconfig', ['.gitattributes'] = 'gitattributes', @@ -1486,7 +1272,7 @@ local filename = { ['.gprc'] = 'gp', ['/.gnupg/gpg.conf'] = 'gpg', ['/.gnupg/options'] = 'gpg', - ['Jenkinsfile'] = 'groovy', + Jenkinsfile = 'groovy', ['/var/backups/gshadow.bak'] = 'group', ['/etc/gshadow'] = 'group', ['/etc/group-'] = 'group', @@ -1536,9 +1322,7 @@ local filename = { ['.sawfishrc'] = 'lisp', ['/etc/login.access'] = 'loginaccess', ['/etc/login.defs'] = 'logindefs', - ['.lsl'] = function(path, bufnr) - return require('vim.filetype.detect').lsl(bufnr) - end, + ['.lsl'] = detect.lsl, ['.busted'] = 'lua', ['.luacheckrc'] = 'lua', ['lynx.cfg'] = 'lynx', @@ -1568,9 +1352,7 @@ local filename = { ['/etc/nanorc'] = 'nanorc', Neomuttrc = 'neomuttrc', ['.netrc'] = 'netrc', - NEWS = function(path, bufnr) - return require('vim.filetype.detect').news(bufnr) - end, + NEWS = detect.news, ['env.nu'] = 'nu', ['config.nu'] = 'nu', ['.ocamlinit'] = 'ocaml', @@ -1601,34 +1383,28 @@ local filename = { ['/etc/pinforc'] = 'pinfo', ['/.pinforc'] = 'pinfo', ['.povrayrc'] = 'povini', - ['printcap'] = function(path, bufnr) + printcap = function(path, bufnr) return 'ptcap', function(b) vim.b[b].ptcap_type = 'print' end end, - ['termcap'] = function(path, bufnr) + termcap = function(path, bufnr) return 'ptcap', function(b) vim.b[b].ptcap_type = 'term' end end, ['.procmailrc'] = 'procmail', ['.procmail'] = 'procmail', - ['indent.pro'] = function(path, bufnr) - return require('vim.filetype.detect').proto(bufnr, 'indent') - end, + ['indent.pro'] = detect_seq(detect.proto, 'indent'), ['/etc/protocols'] = 'protocols', - ['INDEX'] = function(path, bufnr) - return require('vim.filetype.detect').psf(bufnr) - end, - ['INFO'] = function(path, bufnr) - return require('vim.filetype.detect').psf(bufnr) - end, + INDEX = detect.psf, + INFO = detect.psf, ['.pythonstartup'] = 'python', ['.pythonrc'] = 'python', SConstruct = 'python', qmldir = 'qmldir', ['.Rprofile'] = 'r', - ['Rprofile'] = 'r', + Rprofile = 'r', ['Rprofile.site'] = 'r', ratpoisonrc = 'ratpoison', ['.ratpoisonrc'] = 'ratpoison', @@ -1650,42 +1426,18 @@ local filename = { ['/etc/services'] = 'services', ['/etc/serial.conf'] = 'setserial', ['/etc/udev/cdsymlinks.conf'] = 'sh', - ['bash.bashrc'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - bashrc = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - ['.bashrc'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - ['.env'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr)) - end, - ['.kshrc'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'ksh') - end, - ['.profile'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr)) - end, - ['/etc/profile'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr)) - end, - APKBUILD = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - PKGBUILD = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - ['.tcshrc'] = function(path, bufnr) - return require('vim.filetype.detect').shell(path, M.getlines(bufnr), 'tcsh') - end, - ['tcsh.login'] = function(path, bufnr) - return require('vim.filetype.detect').shell(path, M.getlines(bufnr), 'tcsh') - end, - ['tcsh.tcshrc'] = function(path, bufnr) - return require('vim.filetype.detect').shell(path, M.getlines(bufnr), 'tcsh') - end, + ['bash.bashrc'] = detect.bash, + bashrc = detect.bash, + ['.bashrc'] = detect.bash, + ['.env'] = detect.sh, + ['.kshrc'] = detect.ksh, + ['.profile'] = detect.sh, + ['/etc/profile'] = detect.sh, + APKBUILD = detect.bash, + PKGBUILD = detect.bash, + ['.tcshrc'] = detect.tcsh, + ['tcsh.login'] = detect.tcsh, + ['tcsh.tcshrc'] = detect.tcsh, ['/etc/slp.conf'] = 'slpconf', ['/etc/slp.reg'] = 'slpreg', ['/etc/slp.spi'] = 'slpspi', @@ -1739,19 +1491,9 @@ local filename = { ['.Xpdefaults'] = 'xdefaults', ['xdm-config'] = 'xdefaults', ['.Xdefaults'] = 'xdefaults', - ['xorg.conf'] = function(path, bufnr) - return 'xf86conf', function(b) - vim.b[b].xf86conf_xfree86_version = 4 - end - end, - ['xorg.conf-4'] = function(path, bufnr) - return 'xf86conf', function(b) - vim.b[b].xf86conf_xfree86_version = 4 - end - end, - ['XF86Config'] = function(path, bufnr) - return require('vim.filetype.detect').xfree86() - end, + ['xorg.conf'] = detect.xfree86_v4, + ['xorg.conf-4'] = detect.xfree86_v4, + ['XF86Config'] = detect.xfree86_v3, ['/etc/xinetd.conf'] = 'xinetd', fglrxrc = 'xml', ['/etc/blkid.tab'] = 'xml', @@ -1770,6 +1512,12 @@ local filename = { -- END FILENAME } +-- Re-use closures as much as possible +local detect_apache = starsetf('apache') +local detect_muttrc = starsetf('muttrc') +local detect_neomuttrc = starsetf('neomuttrc') + +--- @type vim.filetype.mapping local pattern = { -- BEGIN PATTERN ['.*/etc/a2ps/.*%.cfg'] = 'a2ps', @@ -1778,31 +1526,27 @@ local pattern = { ['.*/etc/asound%.conf'] = 'alsaconf', ['.*/etc/apache2/sites%-.*/.*%.com'] = 'apache', ['.*/etc/httpd/.*%.conf'] = 'apache', - ['.*/etc/apache2/.*%.conf.*'] = starsetf('apache'), - ['.*/etc/apache2/conf%..*/.*'] = starsetf('apache'), - ['.*/etc/apache2/mods%-.*/.*'] = starsetf('apache'), - ['.*/etc/apache2/sites%-.*/.*'] = starsetf('apache'), - ['access%.conf.*'] = starsetf('apache'), - ['apache%.conf.*'] = starsetf('apache'), - ['apache2%.conf.*'] = starsetf('apache'), - ['httpd%.conf.*'] = starsetf('apache'), - ['srm%.conf.*'] = starsetf('apache'), - ['.*/etc/httpd/conf%..*/.*'] = starsetf('apache'), - ['.*/etc/httpd/conf%.d/.*%.conf.*'] = starsetf('apache'), - ['.*/etc/httpd/mods%-.*/.*'] = starsetf('apache'), - ['.*/etc/httpd/sites%-.*/.*'] = starsetf('apache'), + ['.*/etc/apache2/.*%.conf.*'] = detect_apache, + ['.*/etc/apache2/conf%..*/.*'] = detect_apache, + ['.*/etc/apache2/mods%-.*/.*'] = detect_apache, + ['.*/etc/apache2/sites%-.*/.*'] = detect_apache, + ['access%.conf.*'] = detect_apache, + ['apache%.conf.*'] = detect_apache, + ['apache2%.conf.*'] = detect_apache, + ['httpd%.conf.*'] = detect_apache, + ['srm%.conf.*'] = detect_apache, + ['.*/etc/httpd/conf%..*/.*'] = detect_apache, + ['.*/etc/httpd/conf%.d/.*%.conf.*'] = detect_apache, + ['.*/etc/httpd/mods%-.*/.*'] = detect_apache, + ['.*/etc/httpd/sites%-.*/.*'] = detect_apache, ['.*/etc/proftpd/.*%.conf.*'] = starsetf('apachestyle'), ['.*/etc/proftpd/conf%..*/.*'] = starsetf('apachestyle'), ['proftpd%.conf.*'] = starsetf('apachestyle'), ['.*asterisk/.*%.conf.*'] = starsetf('asterisk'), ['.*asterisk.*/.*voicemail%.conf.*'] = starsetf('asteriskvm'), ['.*/%.aptitude/config'] = 'aptconf', - ['.*%.[aA]'] = function(path, bufnr) - return require('vim.filetype.detect').asm(bufnr) - end, - ['.*%.[sS]'] = function(path, bufnr) - return require('vim.filetype.detect').asm(bufnr) - end, + ['.*%.[aA]'] = detect.asm, + ['.*%.[sS]'] = detect.asm, ['[mM]akefile%.am'] = 'automake', ['.*/bind/db%..*'] = starsetf('bindzone'), ['.*/named/db%..*'] = starsetf('bindzone'), @@ -1825,16 +1569,12 @@ local pattern = { ['.*/etc/default/cdrdao'] = 'cdrdaoconf', ['.*hgrc'] = 'cfg', ['.*%.[Cc][Ff][Gg]'] = { - function(path, bufnr) - return require('vim.filetype.detect').cfg(bufnr) - end, + detect.cfg, -- Decrease priority to avoid conflicts with more specific patterns -- such as '.*/etc/a2ps/.*%.cfg', '.*enlightenment/.*%.cfg', etc. { priority = -1 }, }, - ['[cC]hange[lL]og.*'] = starsetf(function(path, bufnr) - return require('vim.filetype.detect').changelog(bufnr) - end), + ['[cC]hange[lL]og.*'] = starsetf(detect.changelog), ['.*%.%.ch'] = 'chill', ['.*%.cmake%.in'] = 'cmake', -- */cmus/rc and */.cmus/rc @@ -1846,19 +1586,11 @@ local pattern = { ['.*/etc/hostname%..*'] = starsetf('config'), ['crontab%..*'] = starsetf('crontab'), ['.*/etc/cron%.d/.*'] = starsetf('crontab'), - ['%.cshrc.*'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['%.login.*'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, + ['%.cshrc.*'] = detect.csh, + ['%.login.*'] = detect.csh, ['cvs%d+'] = 'cvs', - ['.*%.[Dd][Aa][Tt]'] = function(path, bufnr) - return require('vim.filetype.detect').dat(path, bufnr) - end, - ['.*/debian/patches/.*'] = function(path, bufnr) - return require('vim.filetype.detect').dep3patch(path, bufnr) - end, + ['.*%.[Dd][Aa][Tt]'] = detect.dat, + ['.*/debian/patches/.*'] = detect.dep3patch, ['.*/etc/dnsmasq%.d/.*'] = starsetf('dnsmasq'), ['Containerfile%..*'] = starsetf('dockerfile'), ['Dockerfile%..*'] = starsetf('dockerfile'), @@ -1884,53 +1616,25 @@ local pattern = { ['.*/dtrace/.*%.d'] = 'dtrace', ['.*esmtprc'] = 'esmtprc', ['.*Eterm/.*%.cfg'] = 'eterm', - ['[a-zA-Z0-9].*Dict'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['[a-zA-Z0-9].*Dict%..*'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['[a-zA-Z].*Properties'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['[a-zA-Z].*Properties%..*'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['.*Transport%..*'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['.*/constant/g'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['.*/0/.*'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['.*/0%.orig/.*'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, + ['[a-zA-Z0-9].*Dict'] = detect.foam, + ['[a-zA-Z0-9].*Dict%..*'] = detect.foam, + ['[a-zA-Z].*Properties'] = detect.foam, + ['[a-zA-Z].*Properties%..*'] = detect.foam, + ['.*Transport%..*'] = detect.foam, + ['.*/constant/g'] = detect.foam, + ['.*/0/.*'] = detect.foam, + ['.*/0%.orig/.*'] = detect.foam, ['.*/%.fvwm/.*'] = starsetf('fvwm'), - ['.*fvwmrc.*'] = starsetf(function(path, bufnr) - return 'fvwm', function(b) - vim.b[b].fvwm_version = 1 - end - end), - ['.*fvwm95.*%.hook'] = starsetf(function(path, bufnr) - return 'fvwm', function(b) - vim.b[b].fvwm_version = 1 - end - end), - ['.*fvwm2rc.*'] = starsetf(function(path, bufnr) - return require('vim.filetype.detect').fvwm(path) - end), + ['.*fvwmrc.*'] = starsetf(detect.fvwm_v1), + ['.*fvwm95.*%.hook'] = starsetf(detect.fvwm_v1), + ['.*fvwm2rc.*'] = starsetf(detect.fvwm_v2), ['.*/tmp/lltmp.*'] = starsetf('gedcom'), ['.*/etc/gitconfig%.d/.*'] = starsetf('gitconfig'), ['.*/gitolite%-admin/conf/.*'] = starsetf('gitolite'), ['tmac%..*'] = starsetf('nroff'), ['.*/%.gitconfig%.d/.*'] = starsetf('gitconfig'), ['.*%.git/.*'] = { - function(path, bufnr) - return require('vim.filetype.detect').git(bufnr) - end, + detect.git, -- Decrease priority to run after simple pattern checks { priority = -1 }, }, @@ -2093,15 +1797,13 @@ local pattern = { ['.*/log/news/news%.notice'] = 'messages', ['.*/log/syslog%.notice'] = 'messages', ['.*/log/user%.notice'] = 'messages', - ['.*%.[Mm][Oo][Dd]'] = function(path, bufnr) - return require('vim.filetype.detect').mod(path, bufnr) - end, + ['.*%.[Mm][Oo][Dd]'] = detect.mod, ['.*/etc/modules%.conf'] = 'modconf', ['.*/etc/conf%.modules'] = 'modconf', ['.*/etc/modules'] = 'modconf', ['.*/etc/modprobe%..*'] = starsetf('modconf'), ['.*/etc/modutils/.*'] = starsetf(function(path, bufnr) - if vim.fn.executable(vim.fn.expand(path)) ~= 1 then + if fn.executable(fn.expand(path)) ~= 1 then return 'modconf' end end), @@ -2110,32 +1812,30 @@ local pattern = { ['Muttngrc'] = 'muttrc', ['.*/etc/Muttrc%.d/.*'] = starsetf('muttrc'), ['.*/%.mplayer/config'] = 'mplayerconf', - ['Muttrc.*'] = starsetf('muttrc'), - ['Muttngrc.*'] = starsetf('muttrc'), + ['Muttrc.*'] = detect_muttrc, + ['Muttngrc.*'] = detect_muttrc, -- muttrc* and .muttrc* - ['%.?muttrc.*'] = starsetf('muttrc'), + ['%.?muttrc.*'] = detect_muttrc, -- muttngrc* and .muttngrc* - ['%.?muttngrc.*'] = starsetf('muttrc'), - ['.*/%.mutt/muttrc.*'] = starsetf('muttrc'), - ['.*/%.muttng/muttrc.*'] = starsetf('muttrc'), - ['.*/%.muttng/muttngrc.*'] = starsetf('muttrc'), + ['%.?muttngrc.*'] = detect_muttrc, + ['.*/%.mutt/muttrc.*'] = detect_muttrc, + ['.*/%.muttng/muttrc.*'] = detect_muttrc, + ['.*/%.muttng/muttngrc.*'] = detect_muttrc, ['rndc.*%.conf'] = 'named', ['rndc.*%.key'] = 'named', ['named.*%.conf'] = 'named', ['.*/etc/nanorc'] = 'nanorc', ['.*%.NS[ACGLMNPS]'] = 'natural', - ['Neomuttrc.*'] = starsetf('neomuttrc'), + ['Neomuttrc.*'] = detect_neomuttrc, -- neomuttrc* and .neomuttrc* - ['%.?neomuttrc.*'] = starsetf('neomuttrc'), - ['.*/%.neomutt/neomuttrc.*'] = starsetf('neomuttrc'), + ['%.?neomuttrc.*'] = detect_neomuttrc, + ['.*/%.neomutt/neomuttrc.*'] = detect_neomuttrc, ['nginx.*%.conf'] = 'nginx', ['.*/etc/nginx/.*'] = 'nginx', ['.*nginx%.conf'] = 'nginx', ['.*/nginx/.*%.conf'] = 'nginx', ['.*/usr/local/nginx/conf/.*'] = 'nginx', - ['.*%.[1-9]'] = function(path, bufnr) - return require('vim.filetype.detect').nroff(bufnr) - end, + ['.*%.[1-9]'] = detect.nroff, ['.*%.ml%.cppo'] = 'ocaml', ['.*%.mli%.cppo'] = 'ocaml', ['.*%.opam%.template'] = 'opam', @@ -2156,9 +1856,7 @@ local pattern = { ['.*%.php%d'] = 'php', ['.*/%.pinforc'] = 'pinfo', ['.*/etc/pinforc'] = 'pinfo', - ['.*%.[Pp][Rr][Gg]'] = function(path, bufnr) - return require('vim.filetype.detect').prg(bufnr) - end, + ['.*%.[Pp][Rr][Gg]'] = detect.prg, ['.*/etc/protocols'] = 'protocols', ['.*printcap.*'] = starsetf(function(path, bufnr) return require('vim.filetype.detect').printcap('print') @@ -2178,30 +1876,14 @@ local pattern = { ['.*/etc/services'] = 'services', ['.*/etc/serial%.conf'] = 'setserial', ['.*/etc/udev/cdsymlinks%.conf'] = 'sh', - ['%.bash[_%-]aliases'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - ['%.bash[_%-]logout'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - ['%.bash[_%-]profile'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - ['%.kshrc.*'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'ksh') - end, - ['%.profile.*'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr)) - end, - ['.*/etc/profile'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr)) - end, - ['bash%-fc[%-%.].*'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') - end, - ['%.tcshrc.*'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'tcsh') - end, + ['%.bash[_%-]aliases'] = detect.bash, + ['%.bash[_%-]logout'] = detect.bash, + ['%.bash[_%-]profile'] = detect.bash, + ['%.kshrc.*'] = detect.ksh, + ['%.profile.*'] = detect.sh, + ['.*/etc/profile'] = detect.sh, + ['bash%-fc[%-%.].*'] = detect.bash, + ['%.tcshrc.*'] = detect.tcsh, ['.*/etc/sudoers%.d/.*'] = starsetf('sudoers'), ['.*%._sst%.meta'] = 'sisu', ['.*%.%-sst%.meta'] = 'sisu', @@ -2213,17 +1895,13 @@ local pattern = { ['.*/%.ssh/config'] = 'sshconfig', ['.*/%.ssh/.*%.conf'] = 'sshconfig', ['.*/etc/ssh/sshd_config%.d/.*%.conf'] = 'sshdconfig', - ['.*%.[Ss][Rr][Cc]'] = function(path, bufnr) - return require('vim.filetype.detect').src(bufnr) - end, + ['.*%.[Ss][Rr][Cc]'] = detect.src, ['.*/etc/sudoers'] = 'sudoers', ['svn%-commit.*%.tmp'] = 'svn', ['.*/sway/config'] = 'swayconfig', ['.*/%.sway/config'] = 'swayconfig', ['.*%.swift%.gyb'] = 'swiftgyb', - ['.*%.[Ss][Yy][Ss]'] = function(path, bufnr) - return require('vim.filetype.detect').sys(bufnr) - end, + ['.*%.[Ss][Yy][Ss]'] = detect.sys, ['.*/etc/sysctl%.conf'] = 'sysctl', ['.*/etc/sysctl%.d/.*%.conf'] = 'sysctl', ['.*/systemd/.*%.automount'] = 'systemd', @@ -2266,9 +1944,7 @@ local pattern = { ['.*/%.config/upstart/.*%.conf'] = 'upstart', ['.*/%.init/.*%.conf'] = 'upstart', ['.*/usr/share/upstart/.*%.override'] = 'upstart', - ['.*%.[Ll][Oo][Gg]'] = function(path, bufnr) - return require('vim.filetype.detect').log(path) - end, + ['.*%.[Ll][Oo][Gg]'] = detect.log, ['.*%.vhdl_[0-9].*'] = starsetf('vhdl'), ['.*%.ws[fc]'] = 'wsh', ['.*/Xresources/.*'] = starsetf('xdefaults'), @@ -2286,20 +1962,10 @@ local pattern = { ['Xresources.*'] = starsetf('xdefaults'), ['.*/etc/xinetd%.d/.*'] = starsetf('xinetd'), ['.*xmodmap.*'] = starsetf('xmodmap'), - ['.*/xorg%.conf%.d/.*%.conf'] = function(path, bufnr) - return 'xf86config', function(b) - vim.b[b].xf86conf_xfree86_version = 4 - end - end, + ['.*/xorg%.conf%.d/.*%.conf'] = detect.xfree86_v4, -- Increase priority to run before the pattern below - ['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) - return require('vim.filetype.detect').xfree86() - end), + ['XF86Config%-4.*'] = starsetf(detect.xfree86_v4, { priority = -math.huge + 1 }), + ['XF86Config.*'] = starsetf(detect.xfree86_v3), ['%.zcompdump.*'] = starsetf('zsh'), -- .zlog* and zlog* ['%.?zlog.*'] = starsetf('zsh'), @@ -2309,7 +1975,7 @@ local pattern = { ['.*~'] = function(path, bufnr) local short = path:gsub('~$', '', 1) if path ~= short and short ~= '' then - return M.match({ buf = bufnr, filename = vim.fn.fnameescape(short) }) + return M.match({ buf = bufnr, filename = fn.fnameescape(short) }) end end, -- END PATTERN @@ -2317,8 +1983,10 @@ local pattern = { -- luacheck: pop -- luacheck: pop +--- @param t vim.filetype.mapping +--- @return vim.filetype.mapping[] local function sort_by_priority(t) - local sorted = {} + local sorted = {} --- @type vim.filetype.mapping[] for k, v in pairs(t) do local ft = type(v) == 'table' and v[1] or v assert( @@ -2340,6 +2008,9 @@ end local pattern_sorted = sort_by_priority(pattern) +--- @param path string +--- @param as_pattern? true +--- @return string local function normalize_path(path, as_pattern) local normal = path:gsub('\\', '/') if normal:find('^~') then @@ -2348,12 +2019,17 @@ local function normalize_path(path, as_pattern) -- The rest of path should already be properly escaped. normal = vim.pesc(vim.env.HOME) .. normal:sub(2) else - normal = vim.env.HOME .. normal:sub(2) + normal = vim.env.HOME .. normal:sub(2) --- @type string end end return normal end +--- @class vim.filetype.add.filetypes +--- @field pattern vim.filetype.mapping +--- @field extension vim.filetype.mapping +--- @field filename vim.filetype.mapping + --- Add new filetype mappings. --- --- Filetype mappings can be added either by extension or by filename (either @@ -2423,10 +2099,10 @@ end --- ['.*'] = { --- priority = -math.huge, --- function(path, bufnr) ---- local content = vim.filetype.getlines(bufnr, 1) ---- if vim.filetype.matchregex(content, [[^#!.*\\<mine\\>]]) then +--- local content = vim.api.nvim_buf_get_lines(bufnr, 0, 1, false)[1] or '' +--- if vim.regex([[^#!.*\\<mine\\>]]):match_str(content) ~= nil then --- return 'mine' ---- elseif vim.filetype.matchregex(content, [[\\<drawing\\>]]) then +--- elseif vim.regex([[\\<drawing\\>]]):match_str(content) ~= nil then --- return 'drawing' --- end --- end, @@ -2435,7 +2111,7 @@ end --- } --- </pre> --- ----@param filetypes table A table containing new filetype maps (see example). +---@param filetypes vim.filetype.add.filetypes A table containing new filetype maps (see example). function M.add(filetypes) for k, v in pairs(filetypes.extension or {}) do extension[k] = v @@ -2454,38 +2130,62 @@ function M.add(filetypes) end end +--- @param ft vim.filetype.mapping.value +--- @param path? string +--- @param bufnr? integer +--- @param ... any +--- @return string? +--- @return fun(b: integer)? local function dispatch(ft, path, bufnr, ...) - local on_detect - if type(ft) == 'function' then - if bufnr then - ft, on_detect = ft(path, bufnr, ...) - else - -- If bufnr is nil (meaning we are matching only against the filename), set it to an invalid - -- value (-1) and catch any errors from the filetype detection function. If the function tries - -- to use the buffer then it will fail, but this enables functions which do not need a buffer - -- to still work. - local ok - ok, ft, on_detect = pcall(ft, path, -1, ...) - if not ok then - return - end + if type(ft) == 'string' then + return ft + end + + if type(ft) ~= 'function' then + return + end + + assert(path) + + ---@type string|false?, fun(b: integer)? + local ft0, on_detect + if bufnr then + ft0, on_detect = ft(path, bufnr, ...) + else + -- If bufnr is nil (meaning we are matching only against the filename), set it to an invalid + -- value (-1) and catch any errors from the filetype detection function. If the function tries + -- to use the buffer then it will fail, but this enables functions which do not need a buffer + -- to still work. + local ok + ok, ft0, on_detect = pcall(ft, path, -1, ...) + if not ok then + return end end - if type(ft) == 'string' then - return ft, on_detect + if not ft0 then + return end + + return ft0, on_detect end --- Lookup table/cache for patterns that contain an environment variable pattern, e.g. ${SOME_VAR}. +--- Lookup table/cache for patterns that contain an environment variable pattern, e.g. ${SOME_VAR}. +--- @type table<string,boolean> local expand_env_lookup = {} +--- @param name string +--- @param path string +--- @param tail string +--- @param pat string +--- @return string|false? local function match_pattern(name, path, tail, pat) if expand_env_lookup[pat] == nil then expand_env_lookup[pat] = pat:find('%${') ~= nil end if expand_env_lookup[pat] then - local return_early + local return_early --- @type true? + --- @type string pat = pat:gsub('%${(%S-)}', function(env) -- If an environment variable is present in the pattern but not set, there is no match if not vim.env[env] then @@ -2501,18 +2201,22 @@ local function match_pattern(name, path, tail, pat) -- If the pattern contains a / match against the full path, otherwise just the tail local fullpat = '^' .. pat .. '$' - local matches + if pat:find('/') then -- Similar to |autocmd-pattern|, if the pattern contains a '/' then check for a match against -- both the short file name (as typed) and the full file name (after expanding to full path -- and resolving symlinks) - matches = name:match(fullpat) or path:match(fullpat) - else - matches = tail:match(fullpat) + return (name:match(fullpat) or path:match(fullpat)) end - return matches + + return (tail:match(fullpat)) end +--- @class vim.filetype.match.args +--- @field buf? integer +--- @field filename? string +--- @field contents? string[] + --- Perform filetype detection. --- --- The filetype can be detected using one of three methods: @@ -2542,7 +2246,8 @@ end --- vim.filetype.match({ contents = {'#!/usr/bin/env bash'} }) --- </pre> --- ----@param args table Table specifying which matching strategy to use. Accepted keys are: +---@param args vim.filetype.match.args Table specifying which matching strategy to use. +--- Accepted keys are: --- * buf (number): Buffer number to use for matching. Mutually exclusive with --- {contents} --- * filename (string): Filename to use for matching. When {buf} is given, @@ -2555,8 +2260,8 @@ end --- * contents (table): An array of lines representing file contents to use for --- matching. Can be used with {filename}. Mutually exclusive --- with {buf}. ----@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 +---@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(args) @@ -2576,20 +2281,21 @@ function M.match(args) name = api.nvim_buf_get_name(bufnr) end + --- @type string?, fun(b: integer)? local ft, on_detect if name then name = normalize_path(name) -- First check for the simple case where the full path exists as a key - local path = vim.fn.fnamemodify(name, ':p') + local path = fn.fnamemodify(name, ':p') 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') + local tail = fn.fnamemodify(name, ':t') ft, on_detect = dispatch(filename[tail], path, bufnr) if ft then return ft, on_detect @@ -2616,7 +2322,7 @@ function M.match(args) end -- Next, check file extension - local ext = vim.fn.fnamemodify(name, ':e') + local ext = fn.fnamemodify(name, ':e') ft, on_detect = dispatch(extension[ext], path, bufnr) if ft then return ft, on_detect @@ -2641,12 +2347,13 @@ function M.match(args) -- Finally, check file contents if contents or bufnr then if contents == nil then + assert(bufnr) if api.nvim_buf_line_count(bufnr) > 101 then -- only need first 100 and last line for current checks - contents = M.getlines(bufnr, 1, 100) - contents[#contents + 1] = M.getlines(bufnr, -1) + contents = M._getlines(bufnr, 1, 100) + contents[#contents + 1] = M._getline(bufnr, -1) else - contents = M.getlines(bufnr) + contents = M._getlines(bufnr) end end -- If name is nil, catch any errors from the contents filetype detection function. diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 4e18ed190a..59b50f2288 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -17,22 +17,26 @@ -- Example: -- `if line =~ '^\s*unwind_protect\>'` => `if matchregex(line, [[\c^\s*unwind_protect\>]])` +local fn = vim.fn + local M = {} -local getlines = vim.filetype.getlines -local findany = vim.filetype.findany -local nextnonblank = vim.filetype.nextnonblank -local matchregex = vim.filetype.matchregex +local getlines = vim.filetype._getlines +local getline = vim.filetype._getline +local findany = vim.filetype._findany +local nextnonblank = vim.filetype._nextnonblank +local matchregex = vim.filetype._matchregex -- luacheck: push no unused args -- luacheck: push ignore 122 -- 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) +--- @type vim.filetype.mapfn +function M.asm(path, bufnr) local syntax = vim.b[bufnr].asmsyntax if not syntax or syntax == '' then - syntax = M.asm_syntax(bufnr) + syntax = M.asm_syntax(path, bufnr) end -- If b:asmsyntax still isn't set, default to asmsyntax or GNU @@ -48,20 +52,21 @@ function M.asm(bufnr) end end --- Active Server Pages (with Perl or Visual Basic Script) -function M.asp(bufnr) +--- Active Server Pages (with Perl or Visual Basic Script) +--- @type vim.filetype.mapfn +function M.asp(_, bufnr) if vim.g.filetype_asp then return vim.g.filetype_asp elseif table.concat(getlines(bufnr, 1, 3)):lower():find('perlscript') then return 'aspperl' - else - return 'aspvbs' end + return 'aspvbs' end -- Checks the first 5 lines for a asmsyntax=foo override. -- Only whitespace characters can be present immediately before or after this statement. -function M.asm_syntax(bufnr) +--- @type vim.filetype.mapfn +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 @@ -75,7 +80,8 @@ local visual_basic_content = { 'vb_name', 'begin vb%.form', 'begin vb%.mdiform', 'begin vb%.usercontrol' } -- See frm() for Visual Basic form file detection -function M.bas(bufnr) +--- @type vim.filetype.mapfn +function M.bas(_, bufnr) if vim.g.filetype_bas then return vim.g.filetype_bas end @@ -106,18 +112,21 @@ function M.bas(bufnr) return 'basic' end -function M.bindzone(bufnr, default) +--- @type vim.filetype.mapfn +function M.bindzone(_, bufnr) local lines = table.concat(getlines(bufnr, 1, 4)) if findany(lines, { '^; <<>> DiG [0-9%.]+.* <<>>', '%$ORIGIN', '%$TTL', 'IN%s+SOA' }) then return 'bindzone' end - return default end -- Returns true if file content looks like RAPID +--- @param bufnr integer +--- @param extension? string +--- @return string|boolean? local function is_rapid(bufnr, extension) if extension == 'cfg' then - local line = getlines(bufnr, 1):lower() + local line = getline(bufnr, 1):lower() return findany(line, { 'eio:cfg', 'mmc:cfg', 'moc:cfg', 'proc:cfg', 'sio:cfg', 'sys:cfg' }) end local line = nextnonblank(bufnr, 1) @@ -128,23 +137,24 @@ local function is_rapid(bufnr, extension) return false end -function M.cfg(bufnr) +--- @type vim.filetype.mapfn +function M.cfg(_, bufnr) if vim.g.filetype_cfg then - return vim.g.filetype_cfg + return vim.g.filetype_cfg --[[@as string]] elseif is_rapid(bufnr, 'cfg') then return 'rapid' - else - return 'cfg' end + return 'cfg' end --- This function checks if one of the first ten lines start with a '@'. In --- that case it is probably a change file. --- If the first line starts with # or ! it's probably a ch file. --- If a line has "main", "include", "//" or "/*" it's probably ch. --- Otherwise CHILL is assumed. -function M.change(bufnr) - local first_line = getlines(bufnr, 1) +--- This function checks if one of the first ten lines start with a '@'. In +--- that case it is probably a change file. +--- If the first line starts with # or ! it's probably a ch file. +--- If a line has "main", "include", "//" or "/*" it's probably ch. +--- Otherwise CHILL is assumed. +--- @type vim.filetype.mapfn +function M.change(_, bufnr) + local first_line = getline(bufnr, 1) if findany(first_line, { '^#', '^!' }) then return 'ch' end @@ -161,39 +171,42 @@ function M.change(bufnr) return 'chill' end -function M.changelog(bufnr) - local line = getlines(bufnr, 1):lower() +--- @type vim.filetype.mapfn +function M.changelog(_, bufnr) + local line = getline(bufnr, 1):lower() if line:find('; urgency=') then return 'debchangelog' end return 'changelog' end -function M.class(bufnr) +--- @type vim.filetype.mapfn +function M.class(_, bufnr) -- Check if not a Java class (starts with '\xca\xfe\xba\xbe') - if not getlines(bufnr, 1):find('^\202\254\186\190') then + if not getline(bufnr, 1):find('^\202\254\186\190') then return 'stata' end end -function M.cls(bufnr) +--- @type vim.filetype.mapfn +function M.cls(_, bufnr) if vim.g.filetype_cls then return vim.g.filetype_cls end - local line = getlines(bufnr, 1) + local line = getline(bufnr, 1) if line:find('^[%%\\]') then return 'tex' elseif line:find('^#') and line:lower():find('rexx') then return 'rexx' elseif line == 'VERSION 1.0 CLASS' then return 'vb' - else - return 'st' end + return 'st' end +--- @type vim.filetype.mapfn function M.conf(path, bufnr) - if vim.fn.did_filetype() ~= 0 or path:find(vim.g.ft_ignore_pat) then + if fn.did_filetype() ~= 0 or path:find(vim.g.ft_ignore_pat) then return end if path:find('%.conf$') then @@ -206,22 +219,30 @@ function M.conf(path, bufnr) end end --- Debian Control -function M.control(bufnr) - if getlines(bufnr, 1):find('^Source:') then +--- Debian Control +--- @type vim.filetype.mapfn +function M.control(_, bufnr) + if getline(bufnr, 1):find('^Source:') then return 'debcontrol' end end --- Debian Copyright -function M.copyright(bufnr) - if getlines(bufnr, 1):find('^Format:') then +--- Debian Copyright +--- @type vim.filetype.mapfn +function M.copyright(_, bufnr) + if getline(bufnr, 1):find('^Format:') then return 'debcopyright' end end +--- @type vim.filetype.mapfn +function M.cpp(_, _) + return vim.g.cynlib_syntax_for_cpp and 'cynlib' or 'cpp' +end + +--- @type vim.filetype.mapfn function M.csh(path, bufnr) - if vim.fn.did_filetype() ~= 0 then + if fn.did_filetype() ~= 0 then -- Filetype was already detected return end @@ -235,6 +256,9 @@ function M.csh(path, bufnr) end end +--- @param path string +--- @param contents string[] +--- @return string? local function cvs_diff(path, contents) for _, line in ipairs(contents) do if not line:find('^%? ') then @@ -280,8 +304,9 @@ local function cvs_diff(path, contents) end end +--- @type vim.filetype.mapfn function M.dat(path, bufnr) - local file_name = vim.fn.fnamemodify(path, ':t'):lower() + local file_name = fn.fnamemodify(path, ':t'):lower() -- Innovation data processing if findany(file_name, { '^upstream%.dat$', '^upstream%..*%.dat$', '^.*%.upstream%.dat$' }) then return 'upstreamdat' @@ -296,7 +321,8 @@ function M.dat(path, bufnr) end end -function M.decl(bufnr) +--- @type vim.filetype.mapfn +function M.decl(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 3)) do if line:lower():find('^<!sgml') then return 'sgmldecl' @@ -306,8 +332,9 @@ end -- This function is called for all files under */debian/patches/*, make sure not -- to non-dep3patch files, such as README and other text files. +--- @type vim.filetype.mapfn function M.dep3patch(path, bufnr) - local file_name = vim.fn.fnamemodify(path, ':t') + local file_name = fn.fnamemodify(path, ':t') if file_name == 'series' then return end @@ -352,7 +379,7 @@ local function diff(contents) end end -function M.dns_zone(contents) +local function dns_zone(contents) if findany( contents[1] .. contents[2] .. contents[3] .. contents[4], @@ -370,8 +397,9 @@ function M.dns_zone(contents) end end -function M.dtrace(bufnr) - if vim.fn.did_filetype() ~= 0 then +--- @type vim.filetype.mapfn +function M.dtrace(_, bufnr) + if fn.did_filetype() ~= 0 then -- Filetype was already detected return end @@ -386,7 +414,8 @@ function M.dtrace(bufnr) return 'd' end -function M.e(bufnr) +--- @type vim.filetype.mapfn +function M.e(_, bufnr) if vim.g.filetype_euphoria then return vim.g.filetype_euphoria end @@ -398,8 +427,9 @@ function M.e(bufnr) return 'eiffel' end -function M.edn(bufnr) - local line = getlines(bufnr, 1) +--- @type vim.filetype.mapfn +function M.edn(_, bufnr) + local line = getline(bufnr, 1) if matchregex(line, [[\c^\s*(\s*edif\>]]) then return 'edif' else @@ -410,7 +440,8 @@ end -- This function checks for valid cl syntax in the first five lines. -- Look for either an opening comment, '#', or a block start, '{'. -- If not found, assume SGML. -function M.ent(bufnr) +--- @type vim.filetype.mapfn +function M.ent(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 5)) do if line:find('^%s*[#{]') then return 'cl' @@ -423,7 +454,13 @@ function M.ent(bufnr) return 'dtd' end -function M.ex(bufnr) +--- @type vim.filetype.mapfn +function M.euphoria(_, _) + return vim.g.filetype_euphoria or 'euphoria3' +end + +--- @type vim.filetype.mapfn +function M.ex(_, bufnr) if vim.g.filetype_euphoria then return vim.g.filetype_euphoria else @@ -439,7 +476,8 @@ end -- This function checks the first 15 lines for appearance of 'FoamFile' -- and then 'object' in a following line. -- In that case, it's probably an OpenFOAM file -function M.foam(bufnr) +--- @type vim.filetype.mapfn +function M.foam(_, bufnr) local foam_file = false for _, line in ipairs(getlines(bufnr, 1, 15)) do if line:find('^FoamFile') then @@ -450,7 +488,8 @@ function M.foam(bufnr) end end -function M.frm(bufnr) +--- @type vim.filetype.mapfn +function M.frm(_, bufnr) if vim.g.filetype_frm then return vim.g.filetype_frm end @@ -462,8 +501,16 @@ function M.frm(bufnr) end end -function M.fvwm(path) - if vim.fn.fnamemodify(path, ':e') == 'm4' then +--- @type vim.filetype.mapfn +function M.fvwm_1(_, _) + return 'fvwm', function(bufnr) + vim.b[bufnr].fvwm_version = 1 + end +end + +--- @type vim.filetype.mapfn +function M.fvwm_v2(path, _) + if fn.fnamemodify(path, ':e') == 'm4' then return 'fvwm2m4' end return 'fvwm', function(bufnr) @@ -472,7 +519,8 @@ function M.fvwm(path) end -- Distinguish between Forth and F#. -function M.fs(bufnr) +--- @type vim.filetype.mapfn +function M.fs(_, bufnr) if vim.g.filetype_fs then return vim.g.filetype_fs end @@ -484,14 +532,16 @@ function M.fs(bufnr) return 'fsharp' end -function M.git(bufnr) - local line = getlines(bufnr, 1) +--- @type vim.filetype.mapfn +function M.git(_, bufnr) + local line = getline(bufnr, 1) if matchregex(line, [[^\x\{40,\}\>\|^ref: ]]) then return 'git' end end -function M.header(bufnr) +--- @type vim.filetype.mapfn +function M.header(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 200)) do if findany(line:lower(), { '^@interface', '^@end', '^@class' }) then if vim.g.c_syntax_for_h then @@ -510,7 +560,8 @@ function M.header(bufnr) end end -function M.html(bufnr) +--- @type vim.filetype.mapfn +function M.html(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 10)) do if matchregex(line, [[\<DTD\s\+XHTML\s]]) then return 'xhtml' @@ -522,14 +573,16 @@ function M.html(bufnr) end -- Virata Config Script File or Drupal module -function M.hw(bufnr) - if getlines(bufnr, 1):lower():find('<%?php') then +--- @type vim.filetype.mapfn +function M.hw(_, bufnr) + if getline(bufnr, 1):lower():find('<%?php') then return 'php' end return 'virata' end -function M.idl(bufnr) +--- @type vim.filetype.mapfn +function M.idl(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 50)) do if findany(line:lower(), { '^%s*import%s+"unknwn"%.idl', '^%s*import%s+"objidl"%.idl' }) then return 'msidl' @@ -542,7 +595,8 @@ local pascal_comments = { '^%s*{', '^%s*%(%*', '^%s*//' } local pascal_keywords = [[\c^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>]] -function M.inc(bufnr) +--- @type vim.filetype.mapfn +function M.inc(path, bufnr) if vim.g.filetype_inc then return vim.g.filetype_inc end @@ -560,7 +614,7 @@ function M.inc(bufnr) elseif findany(lines, { '^%s*inherit ', '^%s*require ', '^%s*%u[%w_:${}]*%s+%??[?:+]?= ' }) then return 'bitbake' else - local syntax = M.asm_syntax(bufnr) + local syntax = M.asm_syntax(path, bufnr) if not syntax or syntax == '' then return 'pov' end @@ -570,8 +624,9 @@ function M.inc(bufnr) end end -function M.inp(bufnr) - if getlines(bufnr, 1):find('^%*') then +--- @type vim.filetype.mapfn +function M.inp(_, bufnr) + if getline(bufnr, 1):find('^%*') then return 'abaqus' else for _, line in ipairs(getlines(bufnr, 1, 500)) do @@ -582,16 +637,18 @@ function M.inp(bufnr) end end +--- @type vim.filetype.mapfn function M.install(path, bufnr) - if getlines(bufnr, 1):lower():find('<%?php') then + if getline(bufnr, 1):lower():find('<%?php') then return 'php' end - return M.sh(path, getlines(bufnr), 'bash') + return M.bash(path, bufnr) end --- Innovation Data Processing --- (refactor of filetype.vim since the patterns are case-insensitive) -function M.log(path) +--- Innovation Data Processing +--- (refactor of filetype.vim since the patterns are case-insensitive) +--- @type vim.filetype.mapfn +function M.log(path, _) path = path:lower() if findany( @@ -614,7 +671,8 @@ function M.log(path) end end -function M.lpc(bufnr) +--- @type vim.filetype.mapfn +function M.lpc(_, bufnr) if vim.g.lpc_syntax_for_c then for _, line in ipairs(getlines(bufnr, 1, 12)) do if @@ -637,7 +695,8 @@ function M.lpc(bufnr) return 'c' end -function M.lsl(bufnr) +--- @type vim.filetype.mapfn +function M.lsl(_, bufnr) if vim.g.filetype_lsl then return vim.g.filetype_lsl end @@ -650,7 +709,8 @@ function M.lsl(bufnr) end end -function M.m(bufnr) +--- @type vim.filetype.mapfn +function M.m(_, bufnr) if vim.g.filetype_m then return vim.g.filetype_m end @@ -703,6 +763,8 @@ function M.m(bufnr) end end +--- @param contents string[] +--- @return string? local function m4(contents) for _, line in ipairs(contents) do if matchregex(line, [[^\s*dnl\>]]) then @@ -715,9 +777,10 @@ local function m4(contents) end end --- Rely on the file to start with a comment. --- MS message text files use ';', Sendmail files use '#' or 'dnl' -function M.mc(bufnr) +--- Rely on the file to start with a comment. +--- MS message text files use ';', Sendmail files use '#' or 'dnl' +--- @type vim.filetype.mapfn +function M.mc(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 20)) do if findany(line:lower(), { '^%s*#', '^%s*dnl' }) then -- Sendmail .mc file @@ -730,14 +793,17 @@ function M.mc(bufnr) return 'm4' end +--- @param path string +--- @return string? function M.me(path) - local filename = vim.fn.fnamemodify(path, ':t'):lower() + local filename = fn.fnamemodify(path, ':t'):lower() if filename ~= 'read.me' and filename ~= 'click.me' then return 'nroff' end end -function M.mm(bufnr) +--- @type vim.filetype.mapfn +function M.mm(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 20)) do if matchregex(line, [[\c^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)]]) then return 'objcpp' @@ -746,7 +812,8 @@ function M.mm(bufnr) return 'nroff' end -function M.mms(bufnr) +--- @type vim.filetype.mapfn +function M.mms(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 20)) do if findany(line, { '^%s*%%', '^%s*//', '^%*' }) then return 'mmix' @@ -757,7 +824,9 @@ function M.mms(bufnr) return 'mmix' end --- Returns true if file content looks like LambdaProlog +--- Returns true if file content looks like LambdaProlog +--- @param bufnr integer +--- @return boolean local function is_lprolog(bufnr) -- Skip apparent comments and blank lines, what looks like -- LambdaProlog comment may be RAPID header @@ -765,12 +834,14 @@ local function is_lprolog(bufnr) -- The second pattern matches a LambdaProlog comment if not findany(line, { '^%s*$', '^%s*%%' }) then -- The pattern must not catch a go.mod file - return matchregex(line, [[\c\<module\s\+\w\+\s*\.\s*\(%\|$\)]]) ~= nil + return matchregex(line, [[\c\<module\s\+\w\+\s*\.\s*\(%\|$\)]]) end end + return false end --- Determine if *.mod is ABB RAPID, LambdaProlog, Modula-2, Modsim III or go.mod +--- Determine if *.mod is ABB RAPID, LambdaProlog, Modula-2, Modsim III or go.mod +--- @type vim.filetype.mapfn function M.mod(path, bufnr) if vim.g.filetype_mod then return vim.g.filetype_mod @@ -782,21 +853,30 @@ function M.mod(path, bufnr) return 'modula2' elseif is_rapid(bufnr) then return 'rapid' - else - -- Nothing recognized, assume modsim3 - return 'modsim3' + end + -- Nothing recognized, assume modsim3 + return 'modsim3' +end + +--- Determine if *.mod is ABB RAPID, LambdaProlog, Modula-2, Modsim III or go.mod +--- @type vim.filetype.mapfn +function M.mp(_, _) + return 'mp', function(b) + vim.b[b].mp_metafun = 1 end end -function M.news(bufnr) - if getlines(bufnr, 1):lower():find('; urgency=') then +--- @type vim.filetype.mapfn +function M.news(_, bufnr) + if getline(bufnr, 1):lower():find('; urgency=') then return 'debchangelog' end end --- This function checks if one of the first five lines start with a dot. In --- that case it is probably an nroff file. -function M.nroff(bufnr) +--- This function checks if one of the first five lines start with a dot. In +--- that case it is probably an nroff file. +--- @type vim.filetype.mapfn +function M.nroff(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 5)) do if line:find('^%.') then return 'nroff' @@ -804,25 +884,26 @@ function M.nroff(bufnr) end end -function M.patch(bufnr) - local firstline = getlines(bufnr, 1) +--- @type vim.filetype.mapfn +function M.patch(_, bufnr) + local firstline = getline(bufnr, 1) if string.find(firstline, '^From ' .. string.rep('%x', 40) .. '+ Mon Sep 17 00:00:00 2001$') then return 'gitsendemail' - else - return 'diff' end + return 'diff' end --- If the file has an extension of 't' and is in a directory 't' or 'xt' then --- it is almost certainly a Perl test file. --- If the first line starts with '#' and contains 'perl' it's probably a Perl file. --- (Slow test) If a file contains a 'use' statement then it is almost certainly a Perl file. +--- If the file has an extension of 't' and is in a directory 't' or 'xt' then +--- it is almost certainly a Perl test file. +--- If the first line starts with '#' and contains 'perl' it's probably a Perl file. +--- (Slow test) If a file contains a 'use' statement then it is almost certainly a Perl file. +--- @type vim.filetype.mapfn function M.perl(path, bufnr) local dir_name = vim.fs.dirname(path) - if vim.fn.expand(path, '%:e') == 't' and (dir_name == 't' or dir_name == 'xt') then + if fn.expand(path, '%:e') == 't' and (dir_name == 't' or dir_name == 'xt') then return 'perl' end - local first_line = getlines(bufnr, 1) + local first_line = getline(bufnr, 1) if first_line:find('^#') and first_line:lower():find('perl') then return 'perl' end @@ -833,7 +914,8 @@ function M.perl(path, bufnr) end end -function M.pl(bufnr) +--- @type vim.filetype.mapfn +function M.pl(_, bufnr) if vim.g.filetype_pl then return vim.g.filetype_pl end @@ -851,8 +933,9 @@ function M.pl(bufnr) end end -function M.pm(bufnr) - local line = getlines(bufnr, 1) +--- @type vim.filetype.mapfn +function M.pm(_, bufnr) + local line = getline(bufnr, 1) if line:find('XPM2') then return 'xpm2' elseif line:find('XPM') then @@ -862,7 +945,8 @@ function M.pm(bufnr) end end -function M.pp(bufnr) +--- @type vim.filetype.mapfn +function M.pp(_, bufnr) if vim.g.filetype_pp then return vim.g.filetype_pp end @@ -874,7 +958,8 @@ function M.pp(bufnr) end end -function M.prg(bufnr) +--- @type vim.filetype.mapfn +function M.prg(_, bufnr) if vim.g.filetype_prg then return vim.g.filetype_prg elseif is_rapid(bufnr) then @@ -886,7 +971,7 @@ function M.prg(bufnr) end function M.printcap(ptcap_type) - if vim.fn.did_filetype() == 0 then + if fn.did_filetype() == 0 then return 'ptcap', function(bufnr) vim.b[bufnr].ptcap_type = ptcap_type end @@ -895,14 +980,15 @@ end -- This function checks for an assembly comment in the first ten lines. -- If not found, assume Progress. -function M.progress_asm(bufnr) +--- @type vim.filetype.mapfn +function M.progress_asm(path, bufnr) if vim.g.filetype_i then return vim.g.filetype_i end for _, line in ipairs(getlines(bufnr, 1, 10)) do if line:find('^%s*;') or line:find('^/%*') then - return M.asm(bufnr) + return M.asm(path, bufnr) elseif not line:find('^%s*$') or line:find('^/%*') then -- Not an empty line: doesn't look like valid assembly code -- or it looks like a Progress /* comment. @@ -912,13 +998,14 @@ function M.progress_asm(bufnr) return 'progress' end -function M.progress_cweb(bufnr) +--- @type vim.filetype.mapfn +function M.progress_cweb(_, bufnr) if vim.g.filetype_w then return vim.g.filetype_w else if - getlines(bufnr, 1):lower():find('^&analyze') - or getlines(bufnr, 3):lower():find('^&global%-define') + getline(bufnr, 1):lower():find('^&analyze') + or getline(bufnr, 3):lower():find('^&global%-define') then return 'progress' else @@ -930,7 +1017,8 @@ end -- This function checks for valid Pascal syntax in the first 10 lines. -- Look for either an opening comment or a program start. -- If not found, assume Progress. -function M.progress_pascal(bufnr) +--- @type vim.filetype.mapfn +function M.progress_pascal(_, bufnr) if vim.g.filetype_p then return vim.g.filetype_p end @@ -946,34 +1034,33 @@ function M.progress_pascal(bufnr) return 'progress' end --- Distinguish between "default", Prolog and Cproto prototype file. -function M.proto(bufnr, default) +--- Distinguish between "default", Prolog and Cproto prototype file. +--- @type vim.filetype.mapfn +function M.proto(_, bufnr) -- Cproto files have a comment in the first line and a function prototype in -- the second line, it always ends in ";". Indent files may also have -- comments, thus we can't match comments to see the difference. -- IDL files can have a single ';' in the second line, require at least one -- character before the ';'. - if getlines(bufnr, 2):find('.;$') then + if getline(bufnr, 2):find('.;$') then return 'cpp' - else - -- Recognize Prolog by specific text in the first non-empty line; - -- require a blank after the '%' because Perl uses "%list" and "%translate" - local line = nextnonblank(bufnr, 1) - if - line and line:find(':%-') - or matchregex(line, [[\c\<prolog\>]]) - or findany(line, { '^%s*%%+%s', '^%s*%%+$', '^%s*/%*' }) - then - return 'prolog' - else - return default - end + end + -- Recognize Prolog by specific text in the first non-empty line; + -- require a blank after the '%' because Perl uses "%list" and "%translate" + local line = nextnonblank(bufnr, 1) + if + line and line:find(':%-') + or matchregex(line, [[\c\<prolog\>]]) + or findany(line, { '^%s*%%+%s', '^%s*%%+$', '^%s*/%*' }) + then + return 'prolog' end end -- Software Distributor Product Specification File (POSIX 1387.2-1995) -function M.psf(bufnr) - local line = getlines(bufnr, 1):lower() +--- @type vim.filetype.mapfn +function M.psf(_, bufnr) + local line = getline(bufnr, 1):lower() if findany(line, { '^%s*distribution%s*$', @@ -987,7 +1074,8 @@ function M.psf(bufnr) end end -function M.r(bufnr) +--- @type vim.filetype.mapfn +function M.r(_, bufnr) local lines = getlines(bufnr, 1, 50) -- Rebol is easy to recognize, check for that first if matchregex(table.concat(lines), [[\c\<rebol\>]]) then @@ -1008,13 +1096,13 @@ function M.r(bufnr) -- Nothing recognized, use user default or assume R if vim.g.filetype_r then return vim.g.filetype_r - else - -- Rexx used to be the default, but R appears to be much more popular. - return 'r' end + -- Rexx used to be the default, but R appears to be much more popular. + return 'r' end -function M.redif(bufnr) +--- @type vim.filetype.mapfn +function M.redif(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 5)) do if line:lower():find('^template%-type:') then return 'redif' @@ -1022,8 +1110,9 @@ function M.redif(bufnr) end end -function M.reg(bufnr) - local line = getlines(bufnr, 1):lower() +--- @type vim.filetype.mapfn +function M.reg(_, bufnr) + local line = getline(bufnr, 1):lower() if line:find('^regedit[0-9]*%s*$') or line:find('^windows registry editor version %d*%.%d*%s*$') then @@ -1032,7 +1121,8 @@ function M.reg(bufnr) end -- Diva (with Skill) or InstallShield -function M.rul(bufnr) +--- @type vim.filetype.mapfn +function M.rul(_, bufnr) if table.concat(getlines(bufnr, 1, 6)):lower():find('installshield') then return 'ishd' end @@ -1040,6 +1130,7 @@ function M.rul(bufnr) end local udev_rules_pattern = '^%s*udev_rules%s*=%s*"([%^"]+)/*".*' +--- @type vim.filetype.mapfn function M.rules(path) path = path:lower() if @@ -1059,11 +1150,12 @@ function M.rules(path) elseif findany(path, { '^/etc/polkit%-1/rules%.d', '/usr/share/polkit%-1/rules%.d' }) then return 'javascript' else - local ok, config_lines = pcall(vim.fn.readfile, '/etc/udev/udev.conf') + local ok, config_lines = pcall(fn.readfile, '/etc/udev/udev.conf') + --- @cast config_lines +string[] if not ok then return 'hog' end - local dir = vim.fn.expand(path, ':h') + local dir = fn.expand(path, ':h') for _, line in ipairs(config_lines) do local match = line:match(udev_rules_pattern) if match then @@ -1078,7 +1170,8 @@ function M.rules(path) end -- LambdaProlog and Standard ML signature files -function M.sig(bufnr) +--- @type vim.filetype.mapfn +function M.sig(_, bufnr) if vim.g.filetype_sig then return vim.g.filetype_sig end @@ -1096,7 +1189,8 @@ end -- This function checks the first 25 lines of file extension "sc" to resolve -- detection between scala and SuperCollider -function M.sc(bufnr) +--- @type vim.filetype.mapfn +function M.sc(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 25)) do if findany(line, { @@ -1116,18 +1210,19 @@ end -- This function checks the first line of file extension "scd" to resolve -- detection between scdoc and SuperCollider -function M.scd(bufnr) +--- @type vim.filetype.mapfn +function M.scd(_, bufnr) local first = '^%S+%(%d[0-9A-Za-z]*%)' local opt = [[%s+"[^"]*"]] - local line = getlines(bufnr, 1) + local line = getline(bufnr, 1) if findany(line, { first .. '$', first .. opt .. '$', first .. opt .. opt .. '$' }) then return 'scdoc' - else - return 'supercollider' end + return 'supercollider' end -function M.sgml(bufnr) +--- @type vim.filetype.mapfn +function M.sgml(_, bufnr) local lines = table.concat(getlines(bufnr, 1, 5)) if lines:find('linuxdoc') then return 'smgllnx' @@ -1142,15 +1237,17 @@ function M.sgml(bufnr) end end -function M.sh(path, contents, name) +--- @param path string +--- @param contents string[] +--- @param name? string +--- @return string?, fun(b: integer)? +local function sh(path, contents, name) -- Path may be nil, do not fail in that case - if vim.fn.did_filetype() ~= 0 or (path or ''):find(vim.g.ft_ignore_pat) then + if fn.did_filetype() ~= 0 or (path or ''):find(vim.g.ft_ignore_pat) then -- Filetype was already detected or detection should be skipped return end - local on_detect - -- Get the name from the first line if not specified name = name or contents[1] if matchregex(name, [[\<csh\>]]) then @@ -1162,7 +1259,11 @@ function M.sh(path, contents, name) -- Some .sh scripts contain #!/bin/zsh. elseif matchregex(name, [[\<zsh\>]]) then return M.shell(path, contents, 'zsh') - elseif matchregex(name, [[\<ksh\>]]) then + end + + local on_detect --- @type fun(b: integer)? + + if matchregex(name, [[\<ksh\>]]) then on_detect = function(b) vim.b[b].is_kornshell = 1 vim.b[b].is_bash = nil @@ -1185,9 +1286,26 @@ function M.sh(path, contents, name) return M.shell(path, contents, 'sh'), on_detect end --- For shell-like file types, check for an "exec" command hidden in a comment, as used for Tcl. +--- @param name? string +--- @return vim.filetype.mapfn +local function sh_with(name) + return function(path, bufnr) + return sh(path, getlines(bufnr), name) + end +end + +M.sh = sh_with() +M.bash = sh_with('bash') +M.ksh = sh_with('ksh') +M.tcsh = sh_with('tcsh') + +--- For shell-like file types, check for an "exec" command hidden in a comment, as used for Tcl. +--- @param path string +--- @param contents string[] +--- @param name? string +--- @return string? function M.shell(path, contents, name) - if vim.fn.did_filetype() ~= 0 or matchregex(path, vim.g.ft_ignore_pat) then + if fn.did_filetype() ~= 0 or matchregex(path, vim.g.ft_ignore_pat) then -- Filetype was already detected or detection should be skipped return end @@ -1196,6 +1314,7 @@ function M.shell(path, contents, name) for line_nr, line in ipairs(contents) do -- Skip the first line if line_nr ~= 1 then + --- @type string line = line:lower() if line:find('%s*exec%s') and not prev_line:find('^%s*#.*\\$') then -- Found an "exec" line after a comment with continuation @@ -1211,7 +1330,8 @@ function M.shell(path, contents, name) end -- Swift Intermediate Language or SILE -function M.sil(bufnr) +--- @type vim.filetype.mapfn +function M.sil(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 100)) do if line:find('^%s*[\\%%]') then return 'sile' @@ -1224,8 +1344,9 @@ function M.sil(bufnr) end -- SMIL or SNMP MIB file -function M.smi(bufnr) - local line = getlines(bufnr, 1) +--- @type vim.filetype.mapfn +function M.smi(_, bufnr) + local line = getline(bufnr, 1) if matchregex(line, [[\c\<smil\>]]) then return 'smil' else @@ -1233,8 +1354,14 @@ function M.smi(bufnr) end end +--- @type vim.filetype.mapfn +function M.sql(_, _) + return vim.g.filetype_sql and vim.g.filetype_sql or 'sql' +end + -- Determine if a *.src file is Kuka Robot Language -function M.src(bufnr) +--- @type vim.filetype.mapfn +function M.src(_, bufnr) if vim.g.filetype_src then return vim.g.filetype_src end @@ -1244,23 +1371,25 @@ function M.src(bufnr) end end -function M.sys(bufnr) +--- @type vim.filetype.mapfn +function M.sys(_, bufnr) if vim.g.filetype_sys then return vim.g.filetype_sys elseif is_rapid(bufnr) then return 'rapid' - else - return 'bat' end + return 'bat' end -- Choose context, plaintex, or tex (LaTeX) based on these rules: -- 1. Check the first line of the file for "%&<format>". -- 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords. -- 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc. +--- @type vim.filetype.mapfn function M.tex(path, bufnr) - local matched, _, format = getlines(bufnr, 1):find('^%%&%s*(%a+)') + local matched, _, format = getline(bufnr, 1):find('^%%&%s*(%a+)') if matched then + --- @type string format = format:lower():gsub('pdf', '', 1) elseif path:lower():find('tex/context/.*/.*%.tex') then return 'context' @@ -1299,7 +1428,8 @@ function M.tex(path, bufnr) end -- Determine if a *.tf file is TF mud client or terraform -function M.tf(bufnr) +--- @type vim.filetype.mapfn +function M.tf(_, bufnr) for _, line in ipairs(getlines(bufnr)) do -- Assume terraform file on a non-empty line (not whitespace-only) -- and when the first non-whitespace character is not a ; or / @@ -1310,22 +1440,25 @@ function M.tf(bufnr) return 'tf' end -function M.ttl(bufnr) - local line = getlines(bufnr, 1):lower() +--- @type vim.filetype.mapfn +function M.ttl(_, bufnr) + local line = getline(bufnr, 1):lower() if line:find('^@?prefix') or line:find('^@?base') then return 'turtle' end return 'teraterm' end -function M.txt(bufnr) +--- @type vim.filetype.mapfn +function M.txt(_, bufnr) -- helpfiles match *.txt, but should have a modeline as last line - if not getlines(bufnr, -1):find('vim:.*ft=help') then + if not getline(bufnr, -1):find('vim:.*ft=help') then return 'text' end end -function M.typ(bufnr) +--- @type vim.filetype.mapfn +function M.typ(_, bufnr) if vim.g.filetype_typ then return vim.g.filetype_typ end @@ -1348,8 +1481,9 @@ function M.typ(bufnr) end -- Determine if a .v file is Verilog, V, or Coq -function M.v(bufnr) - if vim.fn.did_filetype() ~= 0 then +--- @type vim.filetype.mapfn +function M.v(_, bufnr) + if fn.did_filetype() ~= 0 then -- Filetype was already detected return end @@ -1367,7 +1501,8 @@ end -- WEB (*.web is also used for Winbatch: Guess, based on expecting "%" comment -- lines in a WEB file). -function M.web(bufnr) +--- @type vim.filetype.mapfn +function M.web(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 5)) do if line:find('^%%') then return 'web' @@ -1377,17 +1512,27 @@ function M.web(bufnr) end -- XFree86 config -function M.xfree86() +--- @type vim.filetype.mapfn +function M.xfree86_v3(_, _) return 'xf86conf', function(bufnr) - local line = getlines(bufnr, 1) + local line = getline(bufnr, 1) if matchregex(line, [[\<XConfigurator\>]]) then vim.b[bufnr].xf86conf_xfree86_version = 3 end end end -function M.xml(bufnr) +-- XFree86 config +--- @type vim.filetype.mapfn +function M.xfree86_v4(_, _) + return 'xf86conf', function(b) + vim.b[b].xf86conf_xfree86_version = 4 + end +end + +--- @type vim.filetype.mapfn +function M.xml(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 100)) do local is_docbook4 = line:find('<!DOCTYPE.*DocBook') line = line:lower() @@ -1406,7 +1551,8 @@ function M.xml(bufnr) return 'xml' end -function M.y(bufnr) +--- @type vim.filetype.mapfn +function M.y(_, bufnr) for _, line in ipairs(getlines(bufnr, 1, 100)) do if line:find('^%s*%%') then return 'yacc' @@ -1463,7 +1609,12 @@ local patterns_hashbang = { } ---@private --- File starts with "#!". +--- File starts with "#!". +--- @param contents string[] +--- @param path string +--- @param dispatch_extension fun(name: string): string?, fun(b: integer)? +--- @return string? +--- @return fun(b: integer)? local function match_from_hashbang(contents, path, dispatch_extension) local first_line = contents[1] -- Check for a line like "#!/usr/bin/env {options} bash". Turn it into @@ -1475,7 +1626,7 @@ local function match_from_hashbang(contents, path, dispatch_extension) :gsub('%-%-ignore%-environment', '', 1) :gsub('%-%-split%-string', '', 1) :gsub('%-[iS]', '', 1) - first_line = vim.fn.substitute(first_line, [[\<env\s\+]], '', '') + first_line = fn.substitute(first_line, [[\<env\s\+]], '', '') end -- Get the program name. @@ -1484,15 +1635,15 @@ local function match_from_hashbang(contents, path, dispatch_extension) -- "#!/usr/bin/env perl [path/args]" -- If there is no path use the first word: "#!perl [path/args]". -- Otherwise get the last word after a slash: "#!/usr/bin/perl [path/args]". - local name + local name --- @type string if first_line:find('^#!%s*%a:[/\\]') then - name = vim.fn.substitute(first_line, [[^#!.*[/\\]\(\i\+\).*]], '\\1', '') + name = fn.substitute(first_line, [[^#!.*[/\\]\(\i\+\).*]], '\\1', '') elseif matchregex(first_line, [[^#!.*\<env\>]]) then - name = vim.fn.substitute(first_line, [[^#!.*\<env\>\s\+\(\i\+\).*]], '\\1', '') + name = fn.substitute(first_line, [[^#!.*\<env\>\s\+\(\i\+\).*]], '\\1', '') elseif matchregex(first_line, [[^#!\s*[^/\\ ]*\>\([^/\\]\|$\)]]) then - name = vim.fn.substitute(first_line, [[^#!\s*\([^/\\ ]*\>\).*]], '\\1', '') + name = fn.substitute(first_line, [[^#!\s*\([^/\\ ]*\>\).*]], '\\1', '') else - name = vim.fn.substitute(first_line, [[^#!\s*\S*[/\\]\(\f\+\).*]], '\\1', '') + name = fn.substitute(first_line, [[^#!\s*\S*[/\\]\(\f\+\).*]], '\\1', '') end -- tcl scripts may have #!/bin/sh in the first line and "exec wish" in the @@ -1503,11 +1654,11 @@ local function match_from_hashbang(contents, path, dispatch_extension) if matchregex(name, [[^\(bash\d*\|dash\|ksh\d*\|sh\)\>]]) then -- Bourne-like shell scripts: bash bash2 dash ksh ksh93 sh - return require('vim.filetype.detect').sh(path, contents, first_line) + return sh(path, contents, first_line) elseif matchregex(name, [[^csh\>]]) then - return require('vim.filetype.detect').shell(path, contents, vim.g.filetype_csh or 'csh') + return M.shell(path, contents, vim.g.filetype_csh or 'csh') elseif matchregex(name, [[^tcsh\>]]) then - return require('vim.filetype.detect').shell(path, contents, 'tcsh') + return M.shell(path, contents, 'tcsh') end for k, v in pairs(patterns_hashbang) do @@ -1612,9 +1763,7 @@ local patterns_text = { ['S Y S T E M S I M P R O V E D '] = { 'syndaout', { start_lnum = 3 } }, ['Run Date: '] = { 'takcmp', { start_lnum = 6 } }, ['Node File 1'] = { 'sindacmp', { start_lnum = 9 } }, - function(contents) - require('vim.filetype.detect').dns_zone(contents) - end, + dns_zone, -- Valgrind ['^==%d+== valgrind'] = 'valgrind', ['^==%d+== Using valgrind'] = { 'valgrind', { start_lnum = 3 } }, @@ -1653,11 +1802,15 @@ local patterns_text = { } ---@private --- File does not start with "#!". +--- File does not start with "#!". +--- @param contents string[] +--- @param path string +--- @return string? +--- @return fun(b: integer)? local function match_from_text(contents, path) if contents[1]:find('^:$') then -- Bourne-like shell scripts: sh ksh bash bash2 - return M.sh(path, contents) + return sh(path, contents) elseif matchregex( '\n' .. table.concat(contents, '\n'), @@ -1708,6 +1861,11 @@ local function match_from_text(contents, path) return cvs_diff(path, contents) end +--- @param contents string[] +--- @param path string +--- @param dispatch_extension fun(name: string): string?, fun(b: integer)? +--- @return string? +--- @return fun(b: integer)? function M.match_contents(contents, path, dispatch_extension) local first_line = contents[1] if first_line:find('^#!') then diff --git a/scripts/lua2dox.lua b/scripts/lua2dox.lua index 01bb8ab57a..7594a0e7df 100644 --- a/scripts/lua2dox.lua +++ b/scripts/lua2dox.lua @@ -58,7 +58,11 @@ local TYPES = { 'integer', 'number', 'string', 'table', 'list', 'boolean', 'func local TAGGED_TYPES = { 'TSNode', 'LanguageTree' } -- Document these as 'table' -local ALIAS_TYPES = { 'Range', 'Range4', 'Range6', 'TSMetadata' } +local ALIAS_TYPES = { + 'Range', 'Range4', 'Range6', 'TSMetadata', + 'vim.filetype.add.filetypes', + 'vim.filetype.match.args' +} local debug_outfile = nil --- @type string? local debug_output = {} |