From b518aceaa8f738e581e58aacae93514699b5ff8e Mon Sep 17 00:00:00 2001 From: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com> Date: Tue, 14 Feb 2023 00:04:16 +0100 Subject: feat(filetype): fall back to file extension when matching from hashbang (#22140) If nothing matched in match_from_hashbang, also check the file extension table. For a hashbang like '#!/bin/env foo', this will set the filetype to 'fooscript' assuming the filetype for the 'foo' extension is 'fooscript' in the extension table. --- runtime/lua/vim/filetype/detect.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index edffdde9c7..b3d9fedeae 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1420,7 +1420,7 @@ local patterns_hashbang = { ---@private -- File starts with "#!". -local function match_from_hashbang(contents, path) +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 -- "#!/usr/bin/bash" to make matching easier. @@ -1473,6 +1473,11 @@ local function match_from_hashbang(contents, path) return ft end end + + -- If nothing matched, check the extension table. For a hashbang like + -- '#!/bin/env foo', this will set the filetype to 'fooscript' assuming + -- the filetype for the 'foo' extension is 'fooscript' in the extension table. + return dispatch_extension(name) end local patterns_text = { @@ -1652,10 +1657,10 @@ local function match_from_text(contents, path) return cvs_diff(path, contents) end -M.match_contents = function(contents, path) +function M.match_contents(contents, path, dispatch_extension) local first_line = contents[1] if first_line:find('^#!') then - return match_from_hashbang(contents, path) + return match_from_hashbang(contents, path, dispatch_extension) else return match_from_text(contents, path) end -- cgit From e5641df6d3fc3bb6c3c55593b6152082bfc561b6 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 11 Mar 2023 17:11:02 +0000 Subject: feat: add `vim.filetype.get_option()` --- runtime/lua/vim/filetype/options.lua | 82 ++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 runtime/lua/vim/filetype/options.lua (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/options.lua b/runtime/lua/vim/filetype/options.lua new file mode 100644 index 0000000000..77a66991d5 --- /dev/null +++ b/runtime/lua/vim/filetype/options.lua @@ -0,0 +1,82 @@ +local api = vim.api + +local M = {} + +local function get_ftplugin_runtime(filetype) + return api.nvim__get_runtime({ + string.format('ftplugin/%s.vim', filetype), + string.format('ftplugin/%s_*.vim', filetype), + string.format('ftplugin/%s/*.vim', filetype), + string.format('ftplugin/%s.lua', filetype), + string.format('ftplugin/%s_*.lua', filetype), + string.format('ftplugin/%s/*.lua', filetype), + }, true, {}) --[[@as string[] ]] +end + +-- Keep track of ftplugin files +local ftplugin_cache = {} ---@type table> + +-- Keep track of total number of FileType autocmds +local ft_autocmd_num ---@type integer? + +-- Keep track of filetype options +local ft_option_cache = {} ---@type table> + +--- @param path string +--- @return integer +local function hash(path) + local mtime0 = vim.loop.fs_stat(path).mtime + return mtime0.sec * 1000000000 + mtime0.nsec +end + +--- Only update the cache on changes to the number of FileType autocmds +--- and changes to any ftplugin/ file. This isn't guaranteed to catch everything +--- but should be good enough. +--- @param filetype string +local function update_ft_option_cache(filetype) + local new_ftautos = #api.nvim_get_autocmds({ event = 'FileType' }) + if new_ftautos ~= ft_autocmd_num then + -- invalidate + ft_option_cache[filetype] = nil + ft_autocmd_num = new_ftautos + end + + local files = get_ftplugin_runtime(filetype) + + ftplugin_cache[filetype] = ftplugin_cache[filetype] or {} + + if #files ~= #vim.tbl_keys(ftplugin_cache[filetype]) then + -- invalidate + ft_option_cache[filetype] = nil + ftplugin_cache[filetype] = {} + end + + for _, f in ipairs(files) do + -- VIMRUNTIME should be static so shouldn't need to worry about it changing + if not vim.startswith(f, vim.env.VIMRUNTIME) then + local mtime = hash(f) + if ftplugin_cache[filetype][f] ~= mtime then + -- invalidate + ft_option_cache[filetype] = nil + ftplugin_cache[filetype][f] = mtime + end + end + end +end + +--- @private +--- @param filetype string Filetype +--- @param option string Option name +--- @return string|integer|boolean +function M.get_option(filetype, option) + update_ft_option_cache(filetype) + + if not ft_option_cache[filetype] or not ft_option_cache[filetype][option] then + ft_option_cache[filetype] = ft_option_cache[filetype] or {} + ft_option_cache[filetype][option] = api.nvim_get_option_value(option, { filetype = filetype }) + end + + return ft_option_cache[filetype][option] +end + +return M -- cgit From 28cfcf51269b7390b447c8a5b61d5083c830e834 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 22 Mar 2023 16:49:04 +0000 Subject: fix(api): vim.filetype.get_option() (#22753) - Fix a bug in the cache - Set some buffer options on the dummy buffer --- runtime/lua/vim/filetype/options.lua | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/options.lua b/runtime/lua/vim/filetype/options.lua index 77a66991d5..a093c249f7 100644 --- a/runtime/lua/vim/filetype/options.lua +++ b/runtime/lua/vim/filetype/options.lua @@ -3,7 +3,7 @@ local api = vim.api local M = {} local function get_ftplugin_runtime(filetype) - return api.nvim__get_runtime({ + local files = api.nvim__get_runtime({ string.format('ftplugin/%s.vim', filetype), string.format('ftplugin/%s_*.vim', filetype), string.format('ftplugin/%s/*.vim', filetype), @@ -11,6 +11,15 @@ local function get_ftplugin_runtime(filetype) string.format('ftplugin/%s_*.lua', filetype), string.format('ftplugin/%s/*.lua', filetype), }, true, {}) --[[@as string[] ]] + + local r = {} ---@type string[] + for _, f in ipairs(files) do + -- VIMRUNTIME should be static so shouldn't need to worry about it changing + if not vim.startswith(f, vim.env.VIMRUNTIME) then + r[#r + 1] = f + end + end + return r end -- Keep track of ftplugin files @@ -52,14 +61,11 @@ local function update_ft_option_cache(filetype) end for _, f in ipairs(files) do - -- VIMRUNTIME should be static so shouldn't need to worry about it changing - if not vim.startswith(f, vim.env.VIMRUNTIME) then - local mtime = hash(f) - if ftplugin_cache[filetype][f] ~= mtime then - -- invalidate - ft_option_cache[filetype] = nil - ftplugin_cache[filetype][f] = mtime - end + local mtime = hash(f) + if ftplugin_cache[filetype][f] ~= mtime then + -- invalidate + ft_option_cache[filetype] = nil + ftplugin_cache[filetype][f] = mtime end end end -- cgit From e30cc8be1950a6d1dec7395807966e1b5d0d9194 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 3 Apr 2023 16:27:49 +0200 Subject: vim-patch:9.0.1438: .fs files are falsely recognized as forth files (#22871) Problem: .fs files are falsely recognized as forth files. Solution: Check 100 lines for something that looks like forth. (Johan Kotlinski, closes vim/vim#12219, closes vim/vim#11988) https://github.com/vim/vim/commit/065088d5549e7711668321cc5a77c9a9b684b142 Co-authored-by: Johan Kotlinski --- runtime/lua/vim/filetype/detect.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index b3d9fedeae..d1eabadc4a 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -473,12 +473,12 @@ function M.fs(bufnr) if vim.g.filetype_fs then return vim.g.filetype_fs end - local line = nextnonblank(bufnr, 1) - if findany(line, { '^%s*%.?%( ', '^%s*\\G? ', '^\\$', '^%s*: %S' }) then - return 'forth' - else - return 'fsharp' + for _, line in ipairs(getlines(bufnr, 1, 100)) do + if line:find('^[:(\\] ') then + return 'forth' + end end + return 'fsharp' end function M.git(bufnr) -- cgit From 6d9f5b6bf0fc324b33ce01f74a6030c9271b1a01 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 19 Apr 2023 09:41:26 +0200 Subject: vim-patch:9.0.1464: strace filetype detection is expensive (#23175) Problem: Strace filetype detection is expensive. Solution: Match with a cheap pattern first. (Federico Mengozzi, closes vim/vim#12220) https://github.com/vim/vim/commit/6e5a9f948221b52caaaf106079cb3430c4dd7c77 Co-authored-by: Federico Mengozzi <19249682+fedemengo@users.noreply.github.com> --- runtime/lua/vim/filetype/detect.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index d1eabadc4a..94114ae7c3 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1550,8 +1550,15 @@ local patterns_text = { ['^SNNS pattern definition file'] = 'snnspat', ['^SNNS result file'] = 'snnsres', ['^%%.-[Vv]irata'] = { 'virata', { start_lnum = 1, end_lnum = 5 } }, - ['[0-9:%.]* *execve%('] = 'strace', - ['^__libc_start_main'] = 'strace', + function(lines) + if + -- inaccurate fast match first, then use accurate slow match + (lines[1]:find('execve%(') and lines[1]:find('^[0-9:%.]* *execve%(')) + or lines[1]:find('^__libc_start_main') + then + return 'strace' + end + end, -- VSE JCL ['^\\* $$ JOB\\>'] = { 'vsejcl', { vim_regex = true } }, ['^// *JOB\\>'] = { 'vsejcl', { vim_regex = true } }, -- cgit From f17bb4f41102ecec7989bc4c14c626dc595e2f0b Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 23 Apr 2023 14:15:52 +0200 Subject: vim-patch:9.0.1478: filetypes for *.v files not detected properly (#23282) * vim-patch:9.0.1478: filetypes for *.v files not detected properly Problem: Filetypes for *.v files not detected properly. Solution: Use the file contents to detect the filetype. (Turiiya, closes vim/vim#12281) https://github.com/vim/vim/commit/80406c26188219f3773b2e9c49160caeeb386ee2 Co-authored-by: Turiiya <34311583+tobealive@users.noreply.github.com> Co-authored-by: Jonas Strittmatter <40792180+smjonas@users.noreply.github.com> --- runtime/lua/vim/filetype/detect.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 94114ae7c3..74b01d569c 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1322,6 +1322,24 @@ function M.txt(bufnr) end end +-- Determine if a .v file is Verilog, V, or Coq +function M.v(bufnr) + if vim.fn.did_filetype() ~= 0 then + -- Filetype was already detected + return + end + for _, line in ipairs(getlines(bufnr, 1, 200)) do + if not line:find('^%s*/') then + if findany(line, { ';%s*$', ';%s*/' }) then + return 'verilog' + elseif findany(line, { '%.%s*$', '%.%s*%(%*' }) then + return 'coq' + end + end + end + return 'v' +end + -- WEB (*.web is also used for Winbatch: Guess, based on expecting "%" comment -- lines in a WEB file). function M.web(bufnr) -- cgit From e90b506903babc84f8c5b796c3b25c7f20e183d2 Mon Sep 17 00:00:00 2001 From: GaƩtan Lepage <33058747+GaetanLepage@users.noreply.github.com> Date: Thu, 11 May 2023 09:43:02 +0200 Subject: vim-patch:9.0.1539: typst filetype is not recognized (#23578) Problem: Typst filetype is not recognized. Solution: Distinguish between sql and typst. (Gaetan Lepage, closes vim/vim#12363) https://github.com/vim/vim/commit/4ce1bda869e4ec0152d7dcbe1e491ceac5341d5e --- runtime/lua/vim/filetype/detect.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 74b01d569c..94106a3547 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1322,6 +1322,28 @@ function M.txt(bufnr) end end +function M.typ(bufnr) + if vim.g.filetype_typ then + return vim.g.filetype_typ + end + + for _, line in ipairs(getlines(bufnr, 1, 200)) do + if + findany(line, { + '^CASE[%s]?=[%s]?SAME$', + '^CASE[%s]?=[%s]?LOWER$', + '^CASE[%s]?=[%s]?UPPER$', + '^CASE[%s]?=[%s]?OPPOSITE$', + '^TYPE%s', + }) + then + return 'sql' + end + end + + return 'typst' +end + -- Determine if a .v file is Verilog, V, or Coq function M.v(bufnr) if vim.fn.did_filetype() ~= 0 then -- cgit From e8776074f50593df90402a3642236990edfba6f0 Mon Sep 17 00:00:00 2001 From: Omar El Halabi Date: Tue, 30 May 2023 00:44:23 +0200 Subject: vim-patch:9.0.1591: some "gomod" files are not recognized (#23820) Problem: Some "gomod" files are not recognized. Solution: Check for "go.mod" file name before checking out the contents. (Omar El Halabi, closes vim/vim#12462) https://github.com/vim/vim/commit/c9fbd2560f24180d2efa40028ed68427341d2d99 --- runtime/lua/vim/filetype/detect.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 94106a3547..70e590ed10 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -771,14 +771,14 @@ end function M.mod(path, bufnr) if vim.g.filetype_mod then return vim.g.filetype_mod + elseif matchregex(path, [[\c\ Date: Sat, 3 Jun 2023 11:06:00 +0100 Subject: feat(lua): rename vim.loop -> vim.uv (#22846) --- runtime/lua/vim/filetype/options.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/options.lua b/runtime/lua/vim/filetype/options.lua index a093c249f7..2a28b5a8e3 100644 --- a/runtime/lua/vim/filetype/options.lua +++ b/runtime/lua/vim/filetype/options.lua @@ -34,7 +34,7 @@ local ft_option_cache = {} ---@type table> --- @param path string --- @return integer local function hash(path) - local mtime0 = vim.loop.fs_stat(path).mtime + local mtime0 = vim.uv.fs_stat(path).mtime return mtime0.sec * 1000000000 + mtime0.nsec end -- cgit From 0a439e38634542fd961bb4f04b1cdda304cb1bb5 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 4 Jun 2023 00:39:46 +0200 Subject: vim-patch:9.0.1601: filetype detection fails for *.conf file without comments (#23896) Problem: Filetype detection fails for *.conf file without comments. (Dmitrii Tcyganok) Solution: Use "conf" filetype as a fallback for an empty .conf file. (closes vim/vim#12487, closes vim/vim#12483) https://github.com/vim/vim/commit/664fd12aa27a3c6bd19cfa474c4630d6c03fcc61 Co-authored-by: zeertzjq --- runtime/lua/vim/filetype/detect.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 70e590ed10..b347215895 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -196,6 +196,9 @@ function M.conf(path, bufnr) if vim.fn.did_filetype() ~= 0 or path:find(vim.g.ft_ignore_pat) then return end + if path:find('%.conf$') then + return 'conf' + end for _, line in ipairs(getlines(bufnr, 1, 5)) do if line:find('^#') then return 'conf' -- cgit From f31dba93f921891159eb707b185517648df00d6b Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 9 Jun 2023 08:59:28 +0200 Subject: vim-patch:9.0.1620: Nix files are not recognized from the hashbang line (#23961) Problem: Nix files are not recognized from the hashbang line. Solution: Add a hashbang check. (issue vim/vim#12507) https://github.com/vim/vim/commit/19548c6a742d954ecd0b50b0680c37cc6ced7473 Co-authored-by: Bram Moolenaar --- runtime/lua/vim/filetype/detect.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index b347215895..4e18ed190a 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1459,6 +1459,7 @@ local patterns_hashbang = { ['gforth\\>'] = { 'forth', { vim_regex = true } }, ['icon\\>'] = { 'icon', { vim_regex = true } }, guile = 'scheme', + ['nix%-shell'] = 'nix', } ---@private @@ -1491,7 +1492,7 @@ local function match_from_hashbang(contents, path, dispatch_extension) elseif matchregex(first_line, [[^#!\s*[^/\\ ]*\>\([^/\\]\|$\)]]) then name = vim.fn.substitute(first_line, [[^#!\s*\([^/\\ ]*\>\).*]], '\\1', '') else - name = vim.fn.substitute(first_line, [[^#!\s*\S*[/\\]\(\i\+\).*]], '\\1', '') + name = vim.fn.substitute(first_line, [[^#!\s*\S*[/\\]\(\f\+\).*]], '\\1', '') end -- tcl scripts may have #!/bin/sh in the first line and "exec wish" in the -- cgit From c6c21db82b31ea43ce878ab3725dcd901db1e7a1 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 8 Aug 2023 16:36:06 +0100 Subject: fix(filetype): add typing and dry (#24573) --- runtime/lua/vim/filetype/detect.lua | 546 +++++++++++++++++++++++------------- 1 file changed, 352 insertions(+), 194 deletions(-) (limited to 'runtime/lua/vim/filetype') 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('^]]) 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, [[\]] -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\]]) - 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\]]) + 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\]]) 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, [[\]]) then @@ -1162,7 +1259,11 @@ function M.sh(path, contents, name) -- Some .sh scripts contain #!/bin/zsh. elseif matchregex(name, [[\]]) then return M.shell(path, contents, 'zsh') - elseif matchregex(name, [[\]]) then + end + + local on_detect --- @type fun(b: integer)? + + if matchregex(name, [[\]]) 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\]]) 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 "%&". -- 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, [[\]]) 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(']]) then - name = vim.fn.substitute(first_line, [[^#!.*\\s\+\(\i\+\).*]], '\\1', '') + name = fn.substitute(first_line, [[^#!.*\\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 -- cgit From 08fa71fd274530be23b6ae6b10222afee0b57522 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Aug 2023 19:32:11 +0800 Subject: vim-patch:9.0.1773: cannot distinguish Forth and Fortran *.f files (#24841) Problem: cannot distinguish Forth and Fortran *.f files Solution: Add Filetype detection Code Also add *.4th as a Forth filetype closes: vim/vim#12251 https://github.com/vim/vim/commit/19a3bc3addf9b4aa8150a01b11b4249c67d15d3b Don't remove filetype files from Vim patches: - filetype.vim, script.vim, ft.vim usually contain useful changes - script.vim and ft.vim don't even have their paths spelled correctly Co-authored-by: Doug Kearns --- runtime/lua/vim/filetype/detect.lua | 40 ++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 59b50f2288..4a7a1cbefe 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -473,6 +473,38 @@ function M.ex(_, bufnr) end end +--- @param bufnr integer +--- @return boolean +local function is_forth(bufnr) + local first_line = nextnonblank(bufnr, 1) + + -- SwiftForth block comment (line is usually filled with '-' or '=') or + -- OPTIONAL (sometimes precedes the header comment) + if first_line and findany(first_line:lower(), { '^%{%s', '^%{$', '^optional%s' }) then + return true + end + + for _, line in ipairs(getlines(bufnr, 1, 100)) do + -- Forth comments and colon definitions + if line:find('^[:(\\] ') then + return true + end + end + return false +end + +-- Distinguish between Forth and Fortran +--- @type vim.filetype.mapfn +function M.f(_, bufnr) + if vim.g.filetype_f then + return vim.g.filetype_f + end + if is_forth(bufnr) then + return 'forth' + end + return 'fortran' +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 @@ -518,16 +550,14 @@ function M.fvwm_v2(path, _) end end --- Distinguish between Forth and F#. +-- Distinguish between Forth and F# --- @type vim.filetype.mapfn function M.fs(_, bufnr) if vim.g.filetype_fs then return vim.g.filetype_fs end - for _, line in ipairs(getlines(bufnr, 1, 100)) do - if line:find('^[:(\\] ') then - return 'forth' - end + if is_forth(bufnr) then + return 'forth' end return 'fsharp' end -- cgit From 64631168188b76bfb5b57fb36e57cfd1114cf7f7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 31 Aug 2023 04:38:28 +0800 Subject: vim-patch:9.0.1807: runtime: crystal scripts not recognised (#24949) Problem: runtime: crystal scripts not recognised Solution: Filetype detect Crystal scripts by shebang line closes: vim/vim#12935 https://github.com/vim/vim/commit/9b73902dbe6f7940326bcd8dbc89d010d85d69c5 Co-authored-by: Doug Kearns --- runtime/lua/vim/filetype/detect.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 4a7a1cbefe..af6d77ecbf 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1636,6 +1636,7 @@ local patterns_hashbang = { ['icon\\>'] = { 'icon', { vim_regex = true } }, guile = 'scheme', ['nix%-shell'] = 'nix', + ['crystal\\>'] = { 'crystal', { vim_regex = true } }, } ---@private -- cgit From 79148813567909aae8bc733e585d7713f6069401 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 31 Aug 2023 07:44:33 +0800 Subject: vim-patch:9.0.1797: Vimball/Visual Basic filetype detection conflict (#24947) Problem: Vimball/Visual Basic filetype detection conflict Solution: runtime(vb): Improve Vimball and Visual Basic detection logic Only run Vimball Archiver's BufEnter autocommand on Vimball archives. Fixes vim/vim#2694. closes: vim/vim#12899 https://github.com/vim/vim/commit/f97f6bbf56408c0c97b4ddbe81fba858d7455b0d Co-authored-by: Doug Kearns --- runtime/lua/vim/filetype/detect.lua | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'runtime/lua/vim/filetype') diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index af6d77ecbf..0609ac5513 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -77,7 +77,7 @@ function M.asm_syntax(_, bufnr) end local visual_basic_content = - { 'vb_name', 'begin vb%.form', 'begin vb%.mdiform', 'begin vb%.usercontrol' } + [[\c^\s*\%(Attribute\s\+VB_Name\|Begin\s\+\%(VB\.\|{\%(\x\+-\)\+\x\+}\)\)]] -- See frm() for Visual Basic form file detection --- @type vim.filetype.mapfn @@ -97,7 +97,7 @@ function M.bas(_, bufnr) local qb64_preproc = [[\c^\s*\%($\a\+\|option\s\+\%(_explicit\|_\=explicitarray\)\>\)]] for _, line in ipairs(getlines(bufnr, 1, 100)) do - if findany(line:lower(), visual_basic_content) then + if matchregex(line, visual_basic_content) then return 'vb' elseif line:find(fb_comment) @@ -193,12 +193,12 @@ function M.cls(_, bufnr) if vim.g.filetype_cls then return vim.g.filetype_cls end - local line = getline(bufnr, 1) - if line:find('^[%%\\]') then + local line1 = getline(bufnr, 1) + if line1:find('^[%%\\]') then return 'tex' - elseif line:find('^#') and line:lower():find('rexx') then + elseif line1:find('^#') and line1:lower():find('rexx') then return 'rexx' - elseif line == 'VERSION 1.0 CLASS' then + elseif line1 == 'VERSION 1.0 CLASS' then return 'vb' end return 'st' @@ -525,12 +525,15 @@ function M.frm(_, bufnr) if vim.g.filetype_frm then return vim.g.filetype_frm end - local lines = table.concat(getlines(bufnr, 1, 5)):lower() - if findany(lines, visual_basic_content) then + if getline(bufnr, 1) == 'VERSION 5.00' then return 'vb' - else - return 'form' end + for _, line in ipairs(getlines(bufnr, 1, 5)) do + if matchregex(line, visual_basic_content) then + return 'vb' + end + end + return 'form' end --- @type vim.filetype.mapfn @@ -1529,6 +1532,14 @@ function M.v(_, bufnr) return 'v' end +--- @type vim.filetype.mapfn +function M.vba(_, bufnr) + if getline(bufnr, 1):find('^["#] Vimball Archiver') then + return 'vim'