From 469cfa1ccc742182a0267e99d96e97f6304a57ed Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 13:21:46 +0300 Subject: [PATCH 01/21] fix(functions.lua): implement the missing `dist#ft#SetFileTypeSh` I created a `filetype.detect` module that stores the functions used to detect the filetypes of shell files. The function I implemented checks is the shell name passed to it is in the shebang table and sets filetype to that or it sets the filetype to the shell name directly. I also moved the `analyze_shebang` function to `filetype_detect` since it fits there more than having it in the main module. I moved `getlines` to `filetype.util` and renamed it to `getlines_as_string` The shell functions I added were taken from `vim.filetype.detect` but I modified the behaviour so that it bash, ksh, and dash do not get converted to sh filetype and instead it is up to the user to do so if they want. closes: #79,#34 Can correctly identify shebangs with arguments closes: #63 Can correctly identify python3 files as python closes: #43 Provide an option to set a default filetype --- README.md | 42 +++++++-- lua/filetype/detect.lua | 133 +++++++++++++++++++++++++++ lua/filetype/init.lua | 57 ++++-------- lua/filetype/mappings/function.lua | 140 ++++++++++++++--------------- lua/filetype/util.lua | 45 ++++++++++ 5 files changed, 297 insertions(+), 120 deletions(-) create mode 100644 lua/filetype/detect.lua create mode 100644 lua/filetype/util.lua diff --git a/README.md b/README.md index 70be126..27ccac5 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ creating [800+ of them](https://github.com/vim/vim/blob/master/runtime/filetype. As you can see, `filetype.vim` is by far the heaviest nvim runtime file ```diff -13.782 [runtime] +13.782 [runtime] - 9.144 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/filetype.vim 1.662 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/plugin/matchit.vim 0.459 /usr/local/Cellar/neovim/0.5.0/share/nvim/runtime/syntax/synload.vim @@ -103,10 +103,42 @@ require("filetype").setup({ end, }, + -- Force check the first line of the file for a shebang if default_filetype is set + force_shebang_check = true, -- default is false + + -- Check if the entirety of the shell file for a hint of the executable being used; + -- currently only checks for `tclsh` + check_sh_contents = true, -- default is false + + -- The default behaviour when a shebang is detected is to set the filetype to binary + -- used unless the there is mapping from the binary name to filetype defined. + -- You can define your own mapping here shebang = { -- Set the filetype of files with a dash shebang to sh dash = "sh", + + -- You don't need to define mappings where the binary name matches the filetype + gnuplot = "gnuplot" -- this is unnecessary + + -- Execute code when a python shebang is detected + -- Version numbers at the end of binary names and the env binary are ignored: + -- => #!/bin/python2 = #!/bin/python3 = #!/bin/python = #!/bin/env python = python + python = { + filetype = "python", -- Required if you override the default mapping + on_detect = function() + vim.bo.expandtab = false + end, + }, + + -- Binary names must end in an alpha character and not contain a space + -- to be correctly identified + ["my-sh_interpeter"] = "sh", + ["my-sh_interpeter-Ver2"] = "sh", -- This won't work even if it is the actual binary name + ["bash --posix"] = "sh", -- Neither would this }, + + -- Set a default filetype in the case no matching filetype is detected + default_filetype = 'foo', }, }) ``` @@ -126,12 +158,12 @@ Average startup time (100 rounds): **36.410 ms**
Sample log - + ```diff times in msec clock self+sourced self: sourced script clock elapsed: other lines - + 000.008 000.008: --- NVIM STARTING --- 000.827 000.819: locale set 001.304 000.477: inits 1 @@ -229,12 +261,12 @@ Average startup time (100 rounds): **26.492 ms**
Sample log - + ```diff times in msec clock self+sourced self: sourced script clock elapsed: other lines - + 000.008 000.008: --- NVIM STARTING --- 000.813 000.805: locale set 001.282 000.470: inits 1 diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua new file mode 100644 index 0000000..baaee51 --- /dev/null +++ b/lua/filetype/detect.lua @@ -0,0 +1,133 @@ +local util = require("filetype.util") + +local M = {} + +-- A map from executable name to filetype. +M.shebang = { + ["node"] = "javascript", + ["tclsh"] = "tcl", + ["ksh"] = { + filetype = "ksh", + on_detect = function() + vim.b.is_kornshell = 1 + vim.b.is_bash = nil + vim.b.is_dash = nil + vim.b.is_sh = nil + end, + }, + ["bash"] = { + filetype = "bash", + on_detect = function() + vim.b.is_bash = 1 + vim.b.is_kornshell = nil + vim.b.is_dash = nil + vim.b.is_sh = nil + end, + }, + ["dash"] = { + filetype = "dash", + on_detect = function() + vim.b.is_dash = 1 + vim.b.is_kornshell = nil + vim.b.is_bash = nil + vim.b.is_sh = nil + end, + }, + ["sh"] = { + filetype = "sh", + on_detect = function() + vim.b.is_sh = 1 + vim.b.is_kornshell = vim.g.is_kornshell + vim.b.is_bash = vim.g.is_bash or vim.g.bash_is_sh + vim.b.is_dash = vim.g.is_dash + end, + }, +} + +--- Checks the first line in the buffer for a shebang If there is one, set the +--- filetype appropriately. +--- Taken from vim.filetype.detect +--- +--- @param args table|nil +--- * fallback string|nil The shell binary that is returned as the +--- filetype if no filetype is associated with it +--- * force_shebang_check boolean Forces checking the shebang line even +--- if a fallback filetype is defined +--- * check_contents boolean Decides whether the buffer content is +--- checked for shell-like filetypes. +--- @return string|nil The detected filetype +function M.sh(args) + args = args or {} + + if vim.fn.did_filetype() ~= 0 then + -- Filetype was already detected or detection should be skipped + return + end + + local name = args.fallback + + -- Analyze the first line if there is no file type + if not name or args.force_shebang_check then + name = M.analyze_shebang(util.getline()) or name + end + + -- Check the contents of the file if it overrides the shebang or the + -- passed name + name = (args.check_contents and M.shell(name, util.getlines())) or name + + -- prioritize the passed shebang over the builtin map. use the passed name + -- if it isn't defined in either + name = (M.shebang and M.shebang[name]) or name + if type(name) == "table" then + name.on_detect() + name = name.filetype + end + + return name +end + +--- Function to extract the binary name from from the shebang +--- +--- @param shebang string The shebang to analyze +--- @return string|nil The extracted binary name +function M.analyze_shebang(shebang) + if not shebang or type(shebang) ~= "string" then + return -- Not a string, so don't bother + end + + -- The regex requires that all binaries end in an alpha character, so that + -- the same shell with different version numbers as suffix are treated the same + -- (python3 => python | zsh-5.9 => zsh | test-b#in_sh2 => test-b#in_sh ) + return shebang:match("#!.*/env%s+([^/%s]*%a)") or shebang:match("#!.*/([^/%s]*%a)") +end + +--- For shell-like file types, check for an "exec" command hidden in a comment, +--- as used for Tcl. +--- Taken from vim.filetype.detect +--- +--- @param name string|nil The filetype returned if the contents don't hint to a +--- different filetype +--- @param contents table An array of the lines in the buffer +--- @return string|nil The detected filetype +function M.shell(name, contents) + if vim.fn.did_filetype() ~= 0 then + -- Filetype was already detected or detection should be skipped + return + end + + local prev_line = "" + for _, line in ipairs(contents) do + 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 + if util.match_vim_regex(line, [[\c\") then + if util.getline():find("") then return "xml" else return "smil" end end, ["cls"] = function() - local first_line = getlines(1) + local first_line = util.getline() if first_line:find("^%%") then return "tex" elseif first_line:sub(1, 1) == "#" and first_line:find("rexx") then @@ -89,24 +85,24 @@ M.extensions = { end end, ["install"] = function() - if getlines(1):find("%<%?php") then + if util.getline():find("%<%?php") then return "php" else - vim.cmd([[call dist#ft#SetFileTypeSH("bash")]]) + return detect.sh({ fallback = "bash" }) end end, ["decl"] = function() - if getlines(1, 3):find("^%<%!SGML") then + if util.getlines_as_string(0, 3, " "):find("^%<%!SGML") then return "sgmldecl" end end, ["sgm"] = function() - local top_file = getlines(1, 5) + local top_file = util.getlines(0, 5) if top_file:find("linuxdoc") then return "sgmlnx" elseif - getlines(1):find("%<%!DOCTYPE.*DocBook") - or getlines(2):find(" Array of lines, can be empty +function M.getlines(i, j) + i = i or 0 + j = j or -1 + return vim.api.nvim_buf_get_lines(0, i, j, false) +end + +--- Function to get a range of lines from the current buffer current buffer. The +--- function is zero-indexed. +--- +--- @param i number|nil The start index, 0 if nil +--- @param j number|nil The end index (exclusive), buffer length if nil +--- @param sep string|nil The line separator, empty string if nil +--- @return string String representing lines concatenated by sep +function M.getlines_as_string(i, j, sep) + sep = sep or "" + return table.concat(M.getlines(i, j), sep) +end + +--- Check whether the given string matches the Vim regex pattern. It +--- stores the patterns in a cache +--- +--- @param s string String to check against regex against +--- @param pattern string Vim regex pattern +--- @return integer(s) The byte indices for the beginning and end of the match +M.match_vim_regex = vim.filetype.matchregex + +return M From d067f009c1948bcbce658799e12313079f672bf3 Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 13:23:32 +0300 Subject: [PATCH 02/21] fix(function.lua): replace `dist#ft#CSH` --- lua/filetype/detect.lua | 23 +++++++++++++++++++++++ lua/filetype/mappings/function.lua | 18 +++++++++--------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index baaee51..42888aa 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -130,4 +130,27 @@ function M.shell(name, contents) return name end +--- The function tries to determine which csh varient is this filetype. The +--- function still checks if shebang matches or not +--- Taken from vim.filetype.detect +--- +--- @return string|nil The detected filetype +function M.csh() + if vim.fn.did_filetype() ~= 0 then + -- Filetype was already detected + return + end + + local fallback + if vim.g.filetype_csh then + fallback = vim.g.filetype_csh + elseif string.find(vim.o.shell, "tcsh") then + fallback = "tcsh" + else + fallback = "csh" + end + + return M.sh({ fallback = fallback, force_shebang_check = true }) +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index c7e22c1..ea028dc 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -393,7 +393,7 @@ M.extensions = { return detect.sh({ fallback = "tcsh" }) end, ["csh"] = function() - vim.cmd([[call dist#ft#CSH()]]) + return detect.csh() end, ["rules"] = function() vim.cmd([[call dist#ft#FTRules()]]) @@ -503,22 +503,22 @@ M.literal = { return detect.sh({ fallback = "tcsh" }) end, [".login"] = function() - vim.cmd([[call dist#ft#CSH()]]) + return detect.csh() end, [".cshrc"] = function() - vim.cmd([[call dist#ft#CSH()]]) + return detect.csh() end, ["csh.cshrc"] = function() - vim.cmd([[call dist#ft#CSH()]]) + return detect.csh() end, ["csh.login"] = function() - vim.cmd([[call dist#ft#CSH()]]) + return detect.csh() end, ["csh.logout"] = function() - vim.cmd([[call dist#ft#CSH()]]) + return detect.csh() end, [".alias"] = function() - vim.cmd([[call dist#ft#CSH()]]) + return detect.csh() end, [".d"] = function() return detect.sh({ fallback = "bash" }) @@ -576,10 +576,10 @@ M.complex = { return detect.sh({ fallback = "tcsh" }) end, ["%.login.*"] = function() - vim.cmd([[call dist#ft#CSH()]]) + return detect.csh() end, ["%.cshrc.*"] = function() - vim.cmd([[call dist#ft#CSH()]]) + return detect.csh() end, } From 480ec009cf9770615962754221285d4485f573f7 Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 13:23:56 +0300 Subject: [PATCH 03/21] fix(functions.lua): implement the missing `dist#ft#FTasm` --- lua/filetype/detect.lua | 40 ++++++++++++++++++++++++++++++ lua/filetype/mappings/function.lua | 14 +++++------ lua/filetype/util.lua | 7 ++++++ 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index 42888aa..65a01af 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -153,4 +153,44 @@ function M.csh() return M.sh({ fallback = fallback, force_shebang_check = true }) end +--- 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. +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.asm() + local syntax = vim.b.asmsyntax + if not syntax or syntax == "" then + syntax = M.asm_syntax() + end + + vim.b.asmsyntax = syntax + return syntax +end + +--- Checks the first 5 lines for a asmsyntax=foo override. +--- Only whitespace characters can be present immediately before or after this statement. +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype or g:asmsyntax or "asm" +function M.asm_syntax() + local lines = " " .. util.getlines_as_string(0, 5, " "):lower() .. " " + local match = lines:match("%sasmsyntax=([a-zA-Z0-9]+)%s") + if match then + return match + end + + if + util.findany( + lines, + { "%.title", "%.ident", "%.macro", "%.subtitle", "%.library" } + ) + then + return "vmasm" + end + + -- Defaults to g:asmsyntax or GNU + return (vim.g.asmsyntax ~= 0 and vim.g.asmsyntax) or "asm" +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index ea028dc..87caaae 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -228,25 +228,25 @@ M.extensions = { vim.cmd([[call dist#ft#Check_inp()]]) end, ["asm"] = function() - vim.cmd([[call dist#ft#FTasm()]]) + return detect.asm() end, ["s"] = function() - vim.cmd([[call dist#ft#FTasm()]]) + return detect.asm() end, ["S"] = function() - vim.cmd([[call dist#ft#FTasm()]]) + return detect.asm() end, ["a"] = function() - vim.cmd([[call dist#ft#FTasm()]]) + return detect.asm() end, ["A"] = function() - vim.cmd([[call dist#ft#FTasm()]]) + return detect.asm() end, ["mac"] = function() - vim.cmd([[call dist#ft#FTasm()]]) + return detect.asm() end, ["lst"] = function() - vim.cmd([[call dist#ft#FTasm()]]) + return detect.asm() end, ["bas"] = function() vim.cmd([[call dist#ft#FTVB("basic")]]) diff --git a/lua/filetype/util.lua b/lua/filetype/util.lua index 9c6644e..4448d89 100644 --- a/lua/filetype/util.lua +++ b/lua/filetype/util.lua @@ -42,4 +42,11 @@ end --- @return integer(s) The byte indices for the beginning and end of the match M.match_vim_regex = vim.filetype.matchregex +--- Check whether a string matches any of the given Lua patterns. +--- +---@param s string The string to check +---@param patterns table A list of Lua patterns +---@return boolean `true` if s matched a pattern, else `false` +M.findany = vim.filetype.findany + return M From af67c788c1a1d46ccd0e6d5be4340f6150277c66 Mon Sep 17 00:00:00 2001 From: Xevnar Date: Wed, 30 Nov 2022 17:43:18 +0300 Subject: [PATCH 04/21] feat(functions.lua): implement `dist#ft` function related to euphoria --- lua/filetype/detect.lua | 51 ++++++++++++++++++++++++++++++ lua/filetype/mappings/function.lua | 38 +++++++++++----------- 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index 65a01af..ea744c8 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -193,4 +193,55 @@ function M.asm_syntax() return (vim.g.asmsyntax ~= 0 and vim.g.asmsyntax) or "asm" end +--- This function checks for user define g:filetype_euphoria and returns +--- "euphoira3" if it isn't set +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.euphoria_check() + if vim.g.filetype_euphoria then + return vim.g.filetype_euphoria + end + + return "euphoria3" +end + +--- This function checks for user define g:filetype_euphoria and checks +--- the contents of the first 100 lines for hints if it isn't set +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.eiffel_check() + if vim.g.filetype_euphoria then + return vim.g.filetype_euphoria + end + + for _, line in ipairs(util.getlines(0, 100)) do + if util.findany(line, { "^%s*<'%s*$", "^%s*'>%s*$" }) then + return "specman" + end + end + + return "eiffel" +end + +--- This function checks for user define g:filetype_euphoria and checks +--- the contents of the first 100 lines for hints if it isn't set +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.elixir_check() + if vim.g.filetype_euphoria then + return vim.g.filetype_euphoria + end + + for _, line in ipairs(util.getlines(0, 100)) do + if util.match_vim_regex(line, [[\c^--\|^ifdef\>\|^include\>]]) then + return "euphoria3" + end + end + + return "elixir" +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index 87caaae..d400e7c 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -266,38 +266,44 @@ M.extensions = { ["ch"] = function() vim.cmd([[call dist#ft#FTchange()]]) end, - ["ent"] = function() - vim.cmd([[call dist#ft#FTent()]]) - end, ["ex"] = function() - vim.cmd([[call dist#ft#ExCheck()]]) + return detect.elixir_check() end, ["eu"] = function() - vim.cmd([[call dist#ft#EuphoriaCheck()]]) + return detect.euphoria_check() end, ["ew"] = function() - vim.cmd([[call dist#ft#EuphoriaCheck()]]) + return detect.euphoria_check() end, ["exu"] = function() - vim.cmd([[call dist#ft#EuphoriaCheck()]]) + return detect.euphoria_check() end, ["exw"] = function() - vim.cmd([[call dist#ft#EuphoriaCheck()]]) + return detect.euphoria_check() end, ["EU"] = function() - vim.cmd([[call dist#ft#EuphoriaCheck()]]) + return detect.euphoria_check() end, ["EW"] = function() - vim.cmd([[call dist#ft#EuphoriaCheck()]]) + return detect.euphoria_check() end, ["EX"] = function() - vim.cmd([[call dist#ft#EuphoriaCheck()]]) + return detect.euphoria_check() end, ["EXU"] = function() - vim.cmd([[call dist#ft#EuphoriaCheck()]]) + return detect.euphoria_check() end, ["EXW"] = function() - vim.cmd([[call dist#ft#EuphoriaCheck()]]) + return detect.euphoria_check() + end, + ["e"] = function() + return detect.eiffel_check() + end, + ["E"] = function() + return detect.eiffel_check() + end, + ["ent"] = function() + return detect.eiffel_check() end, ["d"] = function() vim.cmd([[call dist#ft#DtraceCheck()]]) @@ -305,12 +311,6 @@ M.extensions = { ["com"] = function() vim.cmd([[call dist#ft#BindzoneCheck('dcl')]]) end, - ["e"] = function() - vim.cmd([[call dist#ft#FTe()]]) - end, - ["E"] = function() - vim.cmd([[call dist#ft#FTe()]]) - end, ["html"] = function() vim.cmd([[call dist#ft#FThtml()]]) end, From ae6e8ef80113145559ff9d924784c1879bcf7cdf Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 14:37:19 +0300 Subject: [PATCH 05/21] feat(functions.lua): pass table containing file metadata to each function callback This allows for more intricate functions to be written that provide better filetype detection --- lua/filetype/init.lua | 75 +++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/lua/filetype/init.lua b/lua/filetype/init.lua index 61b52aa..4251a80 100644 --- a/lua/filetype/init.lua +++ b/lua/filetype/init.lua @@ -11,17 +11,28 @@ local function setf(filetype) end end +-- Arguments to pass to function callbacks. +-- The argements should be set when the resolve function is called +local callback_args = { + file_path = "", + file_name = "", + file_ext = "", +} + local function set_filetype(name) if type(name) == "string" then setf(name) return true - elseif type(name) == "function" then - local result = name() + end + + if type(name) == "function" then + local result = name(callback_args) if type(result) == "string" then setf(result) return true end end + return false end @@ -47,13 +58,10 @@ local function try_regex(absolute_path, maps, star_set) for regexp, ft in pairs(maps) do if absolute_path:find(regexp) then if star_set then - if star_set_filetype(ft) then - return true - end - else - set_filetype(ft) - return true + return star_set_filetype(ft) end + + return set_filetype(ft) end end return false @@ -64,8 +72,7 @@ local function try_lookup(query, map) return false end if map[query] ~= nil then - set_filetype(map[query]) - return true + return set_filetype(map[query]) end return false end @@ -82,18 +89,18 @@ function M.resolve() -- Just in case vim.g.did_load_filetypes = 1 - local absolute_path = vim.api.nvim_buf_get_name(0) + callback_args.file_path = vim.api.nvim_buf_get_name(0) if vim.bo.filetype == "bqfpreview" then - absolute_path = vim.fn.expand("") + callback_args.file_path = vim.fn.expand("") end - if #absolute_path == 0 then + if #callback_args.file_path == 0 then return end - local filename = absolute_path:match(".*[\\/](.*)") - local ext = filename:match(".+%.(%w+)") + callback_args.file_name = callback_args.file_path:match(".*[\\/](.*)") + callback_args.file_ext = callback_args.file_name:match(".+%.(%w+)") -- Used at the end if no filetype is detected or an extension isn't available local detect_sh_args @@ -101,35 +108,35 @@ function M.resolve() -- Try to match the custom defined filetypes if custom_map ~= nil then -- Avoid indexing nil - if try_lookup(ext, custom_map.extensions) then + if try_lookup(callback_args.file_ext, custom_map.extensions) then return end - if try_lookup(filename, custom_map.literal) then + if try_lookup(callback_args.file_name, custom_map.literal) then return end - if try_lookup(ext, custom_map.function_extensions) then + if try_lookup(callback_args.file_ext, custom_map.function_extensions) then return end - if try_lookup(filename, custom_map.function_literal) then + if try_lookup(callback_args.file_name, custom_map.function_literal) then return end - if try_regex(absolute_path, custom_map.endswith) then + if try_regex(callback_args.file_path, custom_map.endswith) then return end - if try_regex(absolute_path, custom_map.complex) then + if try_regex(callback_args.file_path, custom_map.complex) then return end - if try_regex(absolute_path, custom_map.function_complex) then + if try_regex(callback_args.file_path, custom_map.function_complex) then return end - if try_regex(absolute_path, custom_map.star_sets, true) then + if try_regex(callback_args.file_path, custom_map.star_sets, true) then return end @@ -145,42 +152,41 @@ function M.resolve() end local extension_map = require("filetype.mappings.extensions") - if try_lookup(ext, extension_map) then + if try_lookup(callback_args.file_ext, extension_map) then return end local literal_map = require("filetype.mappings.literal") - if try_lookup(filename, literal_map) then + if try_lookup(callback_args.file_name, literal_map) then return end local function_maps = require("filetype.mappings.function") - if try_lookup(ext, function_maps.extensions) then + if try_lookup(callback_args.file_ext, function_maps.extensions) then return end - if try_lookup(filename, function_maps.literal) then + if try_lookup(callback_args.file_name, function_maps.literal) then return end - if try_regex(absolute_path, function_maps.complex) then + if try_regex(callback_args.file_path, function_maps.complex) then return end local complex_maps = require("filetype.mappings.complex") - if try_regex(absolute_path, complex_maps.endswith) then + if try_regex(callback_args.file_path, complex_maps.endswith) then return end - if try_regex(absolute_path, complex_maps.complex) then + if try_regex(callback_args.file_path, complex_maps.complex) then return end - if try_regex(absolute_path, complex_maps.star_sets, true) then + if try_regex(callback_args.file_path, complex_maps.star_sets, true) then return end -- At this point, no filetype has been detected -- so let's just default to the extension, if it has one - if ext then - set_filetype(ext) + if callback_args.file_ext and set_filetype(callback_args.file_ext) then return end @@ -188,8 +194,7 @@ function M.resolve() -- that. Look for a shebang override in custom_map first. If there is none, -- check the default shebangs defined in function_maps. Otherwise, default -- to setting the filetype to the value of shebang itself. - local ft = detect.sh(detect_sh_args) - set_filetype(ft) + set_filetype(detect.sh(detect_sh_args)) end return M From 6da3c279f26031a13cb464fba8374d8ea8ab927e Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 15:00:17 +0300 Subject: [PATCH 06/21] fix(functions.lua): implement messing `dist#ft#nroff` and `dist#ft#FTperl` --- lua/filetype/detect.lua | 40 ++++++++++++++++++++++++++++++ lua/filetype/mappings/function.lua | 9 +++---- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index ea744c8..a403913 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -244,4 +244,44 @@ function M.elixir_check() return "elixir" end +--- This function checks if one of the first five lines start with a dot. In +--- that case it is probably an nroff file. +--- Taken from vim.filetype.detect +--- +--- @return string|nil The detected filetype +function M.nroff() + for _, line in ipairs(util.getlines(0, 5)) do + if line:find("^%.") then + return "nroff" + end + end +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. +--- Taken from vim.filetype.detect +--- +--- @param file_path string|nil The absolute path to the file +--- @param file_ext string|nil The file extension +--- @return string|nil The detected filetype +function M.perl(file_path, file_ext) + local dir_name = vim.fs.dirname(file_path) + if file_ext == "t" and (dir_name == "t" or dir_name == "xt") then + return "perl" + end + + local first_line = util.getline() + if first_line:find("^#") and first_line:lower():find("perl") then + return M.sh({ fallback = "perl" }) + end + + for _, line in ipairs(util.getlines(0, 30)) do + if util.match_vim_regex(line, [[\c^use\s\s*\k]]) then + return "perl" + end + end +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index d400e7c..e06dfaa 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -5,7 +5,7 @@ local M = {} M.extensions = { ["ms"] = function() - vim.cmd([[if !dist#ft#FTnroff() | setf xmath | endif]]) + return detect.nroff() or "xmath" end, ["xpm"] = function() if util.getline():find("XPM2") then @@ -47,11 +47,8 @@ M.extensions = { return "stata" end end, - ["t"] = function() - -- Don't know how to translate this :( - vim.cmd( - [[if !dist#ft#FTnroff() && !dist#ft#FTperl() | setf tads | endif]] - ) + ["t"] = function(args) + return detect.nroff() or detect.perl(args.file_path, args.file_ext) or "tads" end, ["class"] = function() -- Decimal escape sequence From 576a65fc24d598695df2b834bdf0e4f6a75a008e Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 17:16:08 +0300 Subject: [PATCH 07/21] fix(functions.lua): implement missing `dist#ft#FTVB` --- lua/filetype/detect.lua | 64 ++++++++++++++++++++++++++++++ lua/filetype/mappings/function.lua | 4 +- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index a403913..1759d69 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -284,4 +284,68 @@ function M.perl(file_path, file_ext) end end +local visual_basic_markers = { + "vb_name", + "begin vb%.form", + "begin vb%.mdiform", + "begin vb%.usercontrol", +} + +--- Read the first 100 lines to check for any hints to Basic filetype +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.vbasic() + if vim.g.filetype_bas then + return vim.g.filetype_bas + end + + -- Most frequent FreeBASIC-specific keywords in distro files + local fb_keywords = + [[\c^\s*\%(extern\|var\|enum\|private\|scope\|union\|byref\|operator\|constructor\|delete\|namespace\|public\|property\|with\|destructor\|using\)\>\%(\s*[:=(]\)\@!]] + local fb_preproc = + [[\c^\s*\%(#\s*\a\+\|option\s\+\%(byval\|dynamic\|escape\|\%(no\)\=gosub\|nokeyword\|private\|static\)\>\|\%(''\|rem\)\s*\$lang\>\|def\%(byte\|longint\|short\|ubyte\|uint\|ulongint\|ushort\)\>\)]] + + local fb_comment = "^%s*/'" + -- OPTION EXPLICIT, without the leading underscore, is common to many dialects + local qb64_preproc = + [[\c^\s*\%($\a\+\|option\s\+\%(_explicit\|_\=explicitarray\)\>\)]] + + for _, line in ipairs(util.getlines(0, 100)) do + if util.findany(line:lower(), visual_basic_markers) then + return "vb" + end + + if + line:find(fb_comment) + or util.match_vim_regex(line, fb_preproc) + or util.match_vim_regex(line, fb_keywords) + then + return "freebasic" + end + + if util.match_vim_regex(line, qb64_preproc) then + return "qb64" + end + end + return "basic" +end + +--- Read the first 100 lines to check for any hints to Basic form filetype +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.vbasic_form() + if vim.g.filetype_frm then + return vim.g.filetype_frm + end + + local lines = table.concat(util.getlines(0, 5)):lower() + if util.findany(lines, visual_basic_markers) then + return "vb" + end + + return "form" +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index e06dfaa..dc0eab0 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -246,7 +246,7 @@ M.extensions = { return detect.asm() end, ["bas"] = function() - vim.cmd([[call dist#ft#FTVB("basic")]]) + return detect.vbasic() end, ["btm"] = function() vim.cmd([[call dist#ft#FTbtm()]]) @@ -402,7 +402,7 @@ M.extensions = { vim.cmd([[call dist#ft#FTtex()]]) end, ["frm"] = function() - vim.cmd([[call dist#ft#FTVB("form")]]) + return detect.vbasic_form() end, ["xml"] = function() vim.cmd([[call dist#ft#FTxml()]]) From fcbc0a0a9d6713563073ff0adeb8b8d37147744d Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 17:21:00 +0300 Subject: [PATCH 08/21] fix(functions.lua): implement missing `dist#ft#FThtml` --- lua/filetype/detect.lua | 18 ++++++++++++++++++ lua/filetype/mappings/function.lua | 14 +++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index 1759d69..c9289ce 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -348,4 +348,22 @@ function M.vbasic_form() return "form" end +--- Read the first 10 lines to check for any hints +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.html() + for _, line in ipairs(util.getlines(0, 10)) do + if util.match_vim_regex(line, [[\\|{#\s\+]]) then + return "htmldjango" + end + end + + return "html" +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index dc0eab0..c516264 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -309,16 +309,16 @@ M.extensions = { vim.cmd([[call dist#ft#BindzoneCheck('dcl')]]) end, ["html"] = function() - vim.cmd([[call dist#ft#FThtml()]]) + return detect.html() end, ["htm"] = function() - vim.cmd([[call dist#ft#FThtml()]]) + return detect.html() end, ["shtml"] = function() - vim.cmd([[call dist#ft#FThtml()]]) + return detect.html() end, ["stm"] = function() - vim.cmd([[call dist#ft#FThtml()]]) + return detect.html() end, ["idl"] = function() vim.cmd([[call dist#ft#FTidl()]]) @@ -411,13 +411,13 @@ M.extensions = { vim.cmd([[call dist#ft#FTy()]]) end, ["dtml"] = function() - vim.cmd([[call dist#ft#FThtml()]]) + return detect.html() end, ["pt"] = function() - vim.cmd([[call dist#ft#FThtml()]]) + return detect.html() end, ["cpt"] = function() - vim.cmd([[call dist#ft#FThtml()]]) + return detect.html() end, ["zsql"] = function() vim.cmd([[call dist#ft#SQL()]]) From 5feb7d99a5e3a9ceb114fc2558057cc1ec533d39 Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 17:51:56 +0300 Subject: [PATCH 09/21] fix(functions.lua): implement missing `dist#ft#FTxml` and refactor sgml file detection --- lua/filetype/detect.lua | 52 ++++++++++++++++++++++++++++++ lua/filetype/mappings/function.lua | 30 ++--------------- 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index c9289ce..230749e 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -366,4 +366,56 @@ function M.html() return "html" end +--- Checks if the line is a doc book or not +--- Taken from vim.filetype.detect +--- +--- @return string|nil The docbook filetype +local function is_docbook(line, type) + local is_docbook4 = line:find("%<%!DOCTYPE.*DocBook") + local is_docbook5 = line:lower():find([[xmlns="http://docbook.org/ns/docbook"]]) + if is_docbook4 or is_docbook5 then + vim.b.docbk_type = type + vim.b.docbk_ver = is_docbook4 and 4 or 5 + return "docbk" + end +end + +--- Read the first 100 lines to check for any hints on whether it's a dockbook file or not +--- +--- @return string The detected filetype +function M.sgml() + for _, line in ipairs(util.getlines(0, 100)) do + if line:find("linuxdoc") then + return "sgmlnx" + end + + local ft = is_docbook(line, "sgml") + if ft then + return ft + end + end + + return "sgml" +end + +--- Read the first 100 lines to check for any hints on whether it's a dockbook or not file +--- or a docbook +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.xml() + for _, line in ipairs(util.getlines(0, 100)) do + local ft = is_docbook(line, "sgml") + if ft then + return ft + end + + if line:find([[xmlns:xbl="http://www.mozilla.org/xbl"]]) then + return "xbl" + end + end + + return "xml" +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index c516264..5e74c19 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -94,34 +94,10 @@ M.extensions = { end end, ["sgm"] = function() - local top_file = util.getlines(0, 5) - if top_file:find("linuxdoc") then - return "sgmlnx" - elseif - util.getline():find("%<%!DOCTYPE.*DocBook") - or util.getline(1):find(" Date: Thu, 1 Dec 2022 17:58:34 +0300 Subject: [PATCH 10/21] fix(functions.lua): implement missing `dist#ft#SQL` --- lua/filetype/detect.lua | 12 ++++++++++++ lua/filetype/mappings/function.lua | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index 230749e..c157f0c 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -418,4 +418,16 @@ function M.xml() return "xml" end +--- Return either the user defined sql filetype or not +--- Taken from dist#ft +--- +--- @return string The detected filetype +function M.sql() + if vim.g.filetype_sql then + return vim.g.filetype_sql + end + + return "sql" +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index 5e74c19..6504baa 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -372,7 +372,7 @@ M.extensions = { vim.cmd([[call dist#ft#FTRules()]]) end, ["sql"] = function() - vim.cmd([[call dist#ft#SQL()]]) + return detect.sql() end, ["tex"] = function() vim.cmd([[call dist#ft#FTtex()]]) @@ -396,7 +396,7 @@ M.extensions = { return detect.html() end, ["zsql"] = function() - vim.cmd([[call dist#ft#SQL()]]) + return detect.sql() end, } M.literal = { From 7679f9c7cd2c3e7f266bddfbd8d3c46a274950fc Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 18:14:37 +0300 Subject: [PATCH 11/21] fix(functions.lua): implement missing `dist#ft#FTtex` --- lua/filetype/detect.lua | 67 ++++++++++++++++++++++++++++++ lua/filetype/mappings/complex.lua | 1 + lua/filetype/mappings/function.lua | 4 +- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index c157f0c..8526f57 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -430,4 +430,71 @@ function M.sql() return "sql" 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. +--- Taken from vim.filetype.detect +--- +--- @param file_path string The absolute path of the file +--- @return string The detected filetype +function M.tex(file_path) + local format = M.getline():find("^%%&%s*(%a+)") + if format then + format = format:lower():gsub("pdf", "", 1) + if format == "tex" then + return "tex" + end + + if format == "plaintex" then + return "plaintex" + end + end + + -- Early guarantee that the fileytpe is context + if file_path:lower():find("tex/context/.*/.*%.tex") then + return "context" + end + + local latex_pat = + [[documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>]] + local context_pat = + [[start\a\+\|setup\a\+\|usemodule\|enablemode\|enableregime\|setvariables\|useencoding\|usesymbols\|stelle\a\+\|verwende\a\+\|stel\a\+\|gebruik\a\+\|usa\a\+\|imposta\a\+\|regle\a\+\|utilisemodule\>]] + for i, l in ipairs(util.getlines(0, 1000)) do + -- Skip comments + if l:find("^%s*%%%S") then + goto continue + end + + -- Check the next thousand lines for a LaTeX or ConTeXt keyword. + for _, line in ipairs(util.getlines(i, i + 1000)) do + local lpat_match, cpat_match = util.match_vim_regex( + line, + [[\c^\s*\\\%(]] .. latex_pat .. [[\)\|^\s*\\\(]] .. context_pat .. [[\)]] + ) + + if lpat_match then + return "tex" + end + if cpat_match then + return "context" + end + end + + ::continue:: + end + + -- TODO: add AMSTeX, RevTex, others? + if not vim.g.tex_flavor or vim.g.tex_flavor == "plain" then + return "plaintex" + end + + if vim.g.tex_flavor == "context" then + return "context" + end + + -- Probably LaTeX + return "tex" +end + return M diff --git a/lua/filetype/mappings/complex.lua b/lua/filetype/mappings/complex.lua index 4e67903..e828e5a 100644 --- a/lua/filetype/mappings/complex.lua +++ b/lua/filetype/mappings/complex.lua @@ -103,6 +103,7 @@ M.complex = { ["named.*%.conf"] = "named", ["rndc.*%.conf"] = "named", ["rndc.*%.key"] = "named", + [".*/tex/context/.*/.*%.tex"] = "context", } -- These require a special set_ft function diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index 6504baa..5399199 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -374,8 +374,8 @@ M.extensions = { ["sql"] = function() return detect.sql() end, - ["tex"] = function() - vim.cmd([[call dist#ft#FTtex()]]) + ["tex"] = function(args) + return detect.tex(args.file_path) end, ["frm"] = function() return detect.vbasic_form() From 719308531092b4b26322be1336fbd9519c946c06 Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 18:20:26 +0300 Subject: [PATCH 12/21] fix(functions.lua): implement missing `dist#ft#FTr` --- lua/filetype/detect.lua | 33 ++++++++++++++++++++++++++++++ lua/filetype/mappings/function.lua | 4 ++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index 8526f57..47bcdde 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -497,4 +497,37 @@ function M.tex(file_path) return "tex" end +--- Detect the flavor of R that is used. +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.r() + local lines = util.getlines(0, 50) + -- Rebol is easy to recognize, check for that first + if util.match_vim_regex(table.concat(lines), [[\c\]]) then + return "rebol" + end + + -- Check for comment style + for _, line in ipairs(lines) do + -- R has # comments + if line:find("^%s*#") then + return "r" + end + + -- Rexx has /* comments */ + if line:find("^%s*/%*") then + return "rexx" + end + end + + -- Nothing recognized, use user default or assume R + if vim.g.filetype_r then + return vim.g.filetype_r + end + + -- Rexx used to be the default, but R appears to be much more popular. + return "r" +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index 5399199..6431aaa 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -333,10 +333,10 @@ M.extensions = { vim.cmd([[call dist#ft#FTprogress_pascal()]]) end, ["r"] = function() - vim.cmd([[call dist#ft#FTr()]]) + return detect.r() end, ["R"] = function() - vim.cmd([[call dist#ft#FTr()]]) + return detect.r() end, ["mc"] = function() vim.cmd([[call dist#ft#McSetf()]]) From 2f6a091e23b6a8d75161fb3ab353266c57ddcf4e Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 19:44:27 +0300 Subject: [PATCH 13/21] fix(functions.lua): implement missing `dist#ft#ProtoCheck` --- lua/filetype/detect.lua | 29 +++++++++++++++++++++++++++++ lua/filetype/mappings/function.lua | 4 ++-- lua/filetype/util.lua | 13 +++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index 47bcdde..527b0da 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -530,4 +530,33 @@ function M.r() return "r" end +--- Distinguish between "default", Prolog and Cproto prototype file. +--- Taken from vim.filetype.detect +--- +--- @param default string|nil The default filetype for prototype files +--- @return string|nil The filetype detected +function M.proto(default) + -- 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 util.getlines_as_string(0, 2, " "):find(".;$") then + return "cpp" + 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 = util.get_next_nonblank_line() + if + line and line:find(":%-") + or util.match_vim_regex(line, [[\c\]]) + or util.findany(line, { "^%s*%%+%s", "^%s*%%+$", "^%s*/%*" }) + then + return "prolog" + end + + return default +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index 6431aaa..4a72c8d 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -300,7 +300,7 @@ M.extensions = { vim.cmd([[call dist#ft#FTidl()]]) end, ["pro"] = function() - vim.cmd([[call dist#ft#ProtoCheck('idlang')]]) + return detect.proto("idlang") end, ["m"] = function() vim.cmd([[call dist#ft#FTm()]]) @@ -443,7 +443,7 @@ M.literal = { end end, ["indent.pro"] = function() - vim.cmd([[call dist#ft#ProtoCheck('indent')]]) + return detect.proto("indent") end, [".bashrc"] = function() return detect.sh({ fallback = "bash" }) diff --git a/lua/filetype/util.lua b/lua/filetype/util.lua index 4448d89..7115dca 100644 --- a/lua/filetype/util.lua +++ b/lua/filetype/util.lua @@ -34,6 +34,19 @@ function M.getlines_as_string(i, j, sep) return table.concat(M.getlines(i, j), sep) end +--- Get the next non-whitespace line in the buffer. +--- +---@param i number|nil The line number of the first line to start from (inclusive, 0-based) +---@return string|nil The first non-blank line if found or `nil` otherwise +function M.get_next_nonblank_line(i) + for _, line in ipairs(M.getlines(i)) do + if not line:find("^%s*$") then + return line + end + end + return nil +end + --- Check whether the given string matches the Vim regex pattern. It --- stores the patterns in a cache --- From f9e19e0ab32bc5519358334cb98b643677b6749b Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 20:12:46 +0300 Subject: [PATCH 14/21] fix(functions.lua): implement missing `dist#ft#DTraceCheck` --- lua/filetype/detect.lua | 24 ++++++++++++++++++++++++ lua/filetype/mappings/function.lua | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index 527b0da..fde55e5 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -559,4 +559,28 @@ function M.proto(default) return default end +--- Distinguish between dtrace and d files +--- Taken from vim.filetype.detect +--- +--- @return string|nil The filetype detected +function M.dtrace() + if vim.fn.did_filetype() ~= 0 then + -- Filetype was already detected + return + end + + for _, line in ipairs(util.getlines(0, 100)) do + if util.match_vim_regex(line, [[\c^module\>\|^import\>]]) then + -- D files often start with a module and/or import statement. + return "d" + end + + if util.findany(line, { "^#!%S+dtrace", "#pragma%s+D%s+option", ":%S-:%S-:" }) then + return "dtrace" + end + end + + return "d" +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index 4a72c8d..1a6b871 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -279,7 +279,7 @@ M.extensions = { return detect.eiffel_check() end, ["d"] = function() - vim.cmd([[call dist#ft#DtraceCheck()]]) + return detect.dtrace() end, ["com"] = function() vim.cmd([[call dist#ft#BindzoneCheck('dcl')]]) From 0ef9cd524aa9a10e44beb39fefe56fa6881f29a0 Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 20:21:24 +0300 Subject: [PATCH 15/21] fix(functions.lua): implement missing `dist#ft#FTheader` and `dist#ft#FTlpc` --- lua/filetype/detect.lua | 57 ++++++++++++++++++++++++++++++ lua/filetype/mappings/function.lua | 4 +-- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index fde55e5..bb15b9c 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -583,4 +583,61 @@ function M.dtrace() return "d" end +--- Check for lpc syntax if the user specifies g:lpc_syntax_for_c +--- Taken from vim.filetype.detect +--- +--- @return string The filetype detected +function M.lpc() + if not vim.g.lpc_syntax_for_c then + return "c" + end + + for _, line in ipairs(util.getlines(0, 12)) do + if + util.findany(line, { + "^//", + "^inherit", + "^private", + "^protected", + "^nosave", + "^string", + "^object", + "^mapping", + "^mixed", + }) + then + return "lpc" + end + end + + return "c" +end + +--- Distinguish between different header files +--- Taken from vim.filetype.detect +--- +--- @return string The filetype detected +function M.header() + -- Check the file contents for objective c hints + for _, line in ipairs(util.getlines(0, 200)) do + if util.findany(line:lower(), { "^@interface", "^@end", "^@class" }) then + if vim.g.c_syntax_for_h then + return "objc" + end + + return "objcpp" + end + end + + if vim.g.c_syntax_for_h then + return "c" + end + + if vim.g.ch_syntax_for_h then + return "ch" + end + + return "cpp" +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index 1a6b871..f6cb5d3 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -231,10 +231,10 @@ M.extensions = { vim.cmd([[call dist#ft#BindzoneCheck('')]]) end, ["c"] = function() - vim.cmd([[call dist#ft#FTlpc()]]) + return detect.lpc() end, ["h"] = function() - vim.cmd([[call dist#ft#FTheader()]]) + return detect.header() end, ["ch"] = function() vim.cmd([[call dist#ft#FTchange()]]) From 5827b6c1c912cc2af29d701dfc87f991fa533d4e Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 20:25:43 +0300 Subject: [PATCH 16/21] fix(functions.lua): implement missing `dist#ft#FTchange` --- lua/filetype/detect.lua | 30 ++++++++++++++++++++++++++++++ lua/filetype/mappings/function.lua | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index bb15b9c..13a14a5 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -640,4 +640,34 @@ function M.header() return "cpp" end +--- This function checks: +--- 1. If one of the first ten lines start with a '@'. In that case it is +--- probably a change file. +--- 2. If the first line starts with # or ! it's probably a ch file. +--- 3. If a line has "main", "include", "//" or "/*" it's probably ch. +--- 4. Otherwise CHILL is assumed. +--- @return string The detected filetype +function M.change() + local first_line = util.getline() + if util.findany(first_line, { "^#", "^!" }) then + return "ch" + end + + for _, line in ipairs(util.getlines(0, 10)) do + if line:find("^@") then + return "change" + end + + if line:find("MODULE") then + return "chill" + end + + if util.findany(line:lower(), { "main%s*%(", "#%s*include", "//" }) then + return "ch" + end + end + + return "chill" +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index f6cb5d3..02aa25a 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -237,7 +237,7 @@ M.extensions = { return detect.header() end, ["ch"] = function() - vim.cmd([[call dist#ft#FTchange()]]) + return detect.change() end, ["ex"] = function() return detect.elixir_check() From 6cb826a9a09ad82b8ffc8cfa8d8e5830f90c4f1a Mon Sep 17 00:00:00 2001 From: Xevnar Date: Thu, 1 Dec 2022 20:45:18 +0300 Subject: [PATCH 17/21] fix(functions.lua): implement missing `dist#ft#FTidl` --- lua/filetype/detect.lua | 13 +++++++++++++ lua/filetype/mappings/function.lua | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index 13a14a5..c0b3530 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -670,4 +670,17 @@ function M.change() return "chill" end +--- This function checks the first 50 lines for msidl hints +--- +--- @return string The detected filetype +function M.idl() + for _, line in ipairs(util.getlines(0, 50)) do + if util.findany(line:lower(), { '^%s*import%s+"unknwn"%.idl', '^%s*import%s+"objidl"%.idl' }) then + return "msidl" + end + end + + return "idl" +end + return M diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index 02aa25a..00df89a 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -297,7 +297,7 @@ M.extensions = { return detect.html() end, ["idl"] = function() - vim.cmd([[call dist#ft#FTidl()]]) + return detect.idl() end, ["pro"] = function() return detect.proto("idlang") From 816a4a87a6f459ce0e70a2125dc82610c776f83e Mon Sep 17 00:00:00 2001 From: Xevnar Date: Fri, 2 Dec 2022 23:12:51 +0300 Subject: [PATCH 18/21] fix(functions.lua): implement the remaining missing `dist#ft` functions closes: #91 all `dist#ft` functions that are being used have been ported over --- lua/filetype/detect.lua | 460 ++++++++++++++++++++++++++++- lua/filetype/init.lua | 8 +- lua/filetype/mappings/function.lua | 55 ++-- 3 files changed, 484 insertions(+), 39 deletions(-) diff --git a/lua/filetype/detect.lua b/lua/filetype/detect.lua index c0b3530..9e460ab 100644 --- a/lua/filetype/detect.lua +++ b/lua/filetype/detect.lua @@ -98,7 +98,8 @@ function M.analyze_shebang(shebang) -- The regex requires that all binaries end in an alpha character, so that -- the same shell with different version numbers as suffix are treated the same -- (python3 => python | zsh-5.9 => zsh | test-b#in_sh2 => test-b#in_sh ) - return shebang:match("#!.*/env%s+([^/%s]*%a)") or shebang:match("#!.*/([^/%s]*%a)") + return shebang:match("#!.*/env%s+([^/%s]*%a)") + or shebang:match("#!.*/([^/%s]*%a)") end --- For shell-like file types, check for an "exec" command hidden in a comment, @@ -144,7 +145,9 @@ function M.csh() local fallback if vim.g.filetype_csh then fallback = vim.g.filetype_csh - elseif string.find(vim.o.shell, "tcsh") then + end + + if string.find(vim.o.shell, "tcsh") then fallback = "tcsh" else fallback = "csh" @@ -358,7 +361,12 @@ function M.html() return "xhtml" end - if util.match_vim_regex( line, [[\c{%\s*\(extends\|block\|load\)\>\|{#\s\+]]) then + if + util.match_vim_regex( + line, + [[\c{%\s*\(extends\|block\|load\)\>\|{#\s\+]] + ) + then return "htmldjango" end end @@ -372,7 +380,8 @@ end --- @return string|nil The docbook filetype local function is_docbook(line, type) local is_docbook4 = line:find("%<%!DOCTYPE.*DocBook") - local is_docbook5 = line:lower():find([[xmlns="http://docbook.org/ns/docbook"]]) + local is_docbook5 = line:lower() + :find([[xmlns="http://docbook.org/ns/docbook"]]) if is_docbook4 or is_docbook5 then vim.b.docbk_type = type vim.b.docbk_ver = is_docbook4 and 4 or 5 @@ -470,7 +479,11 @@ function M.tex(file_path) for _, line in ipairs(util.getlines(i, i + 1000)) do local lpat_match, cpat_match = util.match_vim_regex( line, - [[\c^\s*\\\%(]] .. latex_pat .. [[\)\|^\s*\\\(]] .. context_pat .. [[\)]] + [[\c^\s*\\\%(]] + .. latex_pat + .. [[\)\|^\s*\\\(]] + .. context_pat + .. [[\)]] ) if lpat_match then @@ -530,12 +543,11 @@ function M.r() return "r" end ---- Distinguish between "default", Prolog and Cproto prototype file. +--- Distinguish between Prolog and Cproto prototype file. --- Taken from vim.filetype.detect --- ---- @param default string|nil The default filetype for prototype files --- @return string|nil The filetype detected -function M.proto(default) +function M.proto() -- 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. @@ -555,8 +567,6 @@ function M.proto(default) then return "prolog" end - - return default end --- Distinguish between dtrace and d files @@ -575,7 +585,12 @@ function M.dtrace() return "d" end - if util.findany(line, { "^#!%S+dtrace", "#pragma%s+D%s+option", ":%S-:%S-:" }) then + if + util.findany( + line, + { "^#!%S+dtrace", "#pragma%s+D%s+option", ":%S-:%S-:" } + ) + then return "dtrace" end end @@ -620,7 +635,9 @@ end function M.header() -- Check the file contents for objective c hints for _, line in ipairs(util.getlines(0, 200)) do - if util.findany(line:lower(), { "^@interface", "^@end", "^@class" }) then + if + util.findany(line:lower(), { "^@interface", "^@end", "^@class" }) + then if vim.g.c_syntax_for_h then return "objc" end @@ -662,7 +679,9 @@ function M.change() return "chill" end - if util.findany(line:lower(), { "main%s*%(", "#%s*include", "//" }) then + if + util.findany(line:lower(), { "main%s*%(", "#%s*include", "//" }) + then return "ch" end end @@ -675,7 +694,12 @@ end --- @return string The detected filetype function M.idl() for _, line in ipairs(util.getlines(0, 50)) do - if util.findany(line:lower(), { '^%s*import%s+"unknwn"%.idl', '^%s*import%s+"objidl"%.idl' }) then + if + util.findany( + line:lower(), + { '^%s*import%s+"unknwn"%.idl', '^%s*import%s+"objidl"%.idl' } + ) + then return "msidl" end end @@ -683,4 +707,412 @@ function M.idl() return "idl" end +--- Diffrentiate between matlab, octave, objective c, and other filetypes +--- Taken from vim.filetype.detect +--- +--- @return string the Detected filetype +function M.m() + if vim.g.filetype_m then + return vim.g.filetype_m + end + + -- Excluding end(for|function|if|switch|while) common to Murphi + local octave_block_terminators = + [[\]] + local objc_preprocessor = + [[\c^\s*#\s*\%(import\|include\|define\|if\|ifn\=def\|undef\|line\|error\|pragma\)\>]] + + -- Whether we've seen a multiline comment leader + local saw_comment = false + for _, line in ipairs(util.getlines(0, 100)) do + if line:find("^%s*/%*") then + -- /* ... */ is a comment in Objective C and Murphi, so we can't conclude + -- it's either of them yet, but track this as a hint in case we don't see + -- anything more definitive. + saw_comment = true + end + + if + line:find("^%s*//") + or util.match_vim_regex(line, [[\c^\s*@import\>]]) + or util.match_vim_regex(line, objc_preprocessor) + then + return "objc" + end + + if + util.findany(line, { "^%s*#", "^%s*%%!" }) + or util.match_vim_regex(line, [[\c^\s*unwind_protect\>]]) + or util.match_vim_regex( + line, + [[\c\%(^\|;\)\s*]] .. octave_block_terminators + ) + then + return "octave" + end + + if line:find("^%s*%%") then + return "matlab" + end + + if line:find("^%s*%(%*") then + return "mma" + end + + if util.match_vim_regex(line, [[\c^\s*\(\(type\|var\)\>\|--\)]]) then + return "murphi" + end + end + + if saw_comment then + -- We didn't see anything definitive, but this looks like either Objective C + -- or Murphi based on the comment leader. Assume the former as it is more + -- common. + return "objc" + end + + -- Default is Matlab + return "matlab" +end + +--- Diffrentiate between nroff and objective cpp +--- Taken from vim.filetype.detect +--- +--- @return string the Detected filetype +function M.mm() + for _, line in ipairs(util.getlines(0, 20)) do + if + util.match_vim_regex( + line, + [[\c^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)]] + ) + then + return "objcpp" + end + end + + return "nroff" +end + +--- Diffrentiate between make and mmix files +--- Taken from vim.filetype.detect +--- +--- @return string the Detected filetype +function M.mms() + for _, line in ipairs(util.getlines(0, 20)) do + if util.findany(line, { "^%s*%%", "^%s*//", "^%*" }) then + return "mmix" + end + + if line:find("^%s*#") then + return "make" + end + end + + return "mmix" +end + +local pascal_comments = { "^%s*{", "^%s*%(%*", "^%s*//" } +local pascal_keywords = + [[\c^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>]] + +--- Diffrentiate between pascal and puppet filetypes +--- Taken from vim.filetype.detect +--- +--- @return string the Detected filetype +function M.pp() + if vim.g.filetype_pp then + return vim.g.filetype_pp + end + + local line = util.get_next_nonblank_line() + if + util.findany(line, pascal_comments) + or util.match_vim_regex(line, pascal_keywords) + then + return "pascal" + end + + return "puppet" +end + +--- Diffrentiate between prolog and perl filetypes +--- Taken from vim.filetype.detect +--- +--- @return string the Detected filetype +function M.pl() + if vim.g.filetype_pl then + return vim.g.filetype_pl + 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 = util.get_next_nonblank_line() + if + line and line:find(":%-") + or util.match_vim_regex(line, [[\c\]]) + or util.findany(line, { "^%s*%%+%s", "^%s*%%+$", "^%s*/%*" }) + then + return "prolog" + end + + return "perl" +end + +--- Diffrentiate between different inc filetypes +--- Taken from vim.filetype.detect +--- +--- @return string the Detected filetype +function M.inc() + if vim.g.filetype_inc then + return vim.g.filetype_inc + end + + local lines = util.getlines_as_string(0, 3, " ") + if lines:lower():find("perlscript") then + return "aspperl" + end + + if lines:find("<%%") then + return "aspvbs" + end + + if lines:find("<%?") then + return "php" + end + + -- Pascal supports // comments but they're vary rarely used for file + -- headers so assume POV-Ray + if + util.findany(lines, { "^%s{", "^%s%(%*" }) + or util.match_vim_regex(lines, pascal_keywords) + then + return "pascal" + end + + if + util.findany(lines, { + "^%s*inherit ", + "^%s*require ", + "^%s*%u[%w_:${}]*%s+%??[?:+]?= ", + }) + then + return "bitbake" + end + + local syntax = M.asm_syntax() + if syntax == vim.g.asmsyntax or syntax == "asm" then + return "pov" -- If the default asm syntax is found + end + + vim.b.asmsyntax = syntax + return syntax +end + +--- This function checks for an assembly comment in the first ten lines. +--- If not found, assume Progress. +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.progress_asm() + if vim.g.filetype_i then + return vim.g.filetype_i + end + + for _, line in ipairs(util.getlines(0, 10)) do + if line:find("^%s*;") or line:find("^/%*") then + return M.asm() + end + + if 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. + break + end + end + + return "progress" +end + +--- This function checks cweb files for hints on whether they are progress files or not +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.progress_cweb() + if vim.g.filetype_w then + return vim.g.filetype_w + else + if + util.getlines():lower():find("^&analyze") + or util.getlines(2):lower():find("^&global%-define") + then + return "progress" + end + + return "cweb" + end +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. +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.progress_pascal() + if vim.g.filetype_p then + return vim.g.filetype_p + end + + for _, line in ipairs(util.getlines(0, 10)) do + if + util.findany(line, pascal_comments) + or util.match_vim_regex(line, pascal_keywords) + then + return "pascal" + end + + if not line:find("^%s*$") or line:find("^/%*") then + -- Not an empty line: Doesn't look like valid Pascal code. + -- Or it looks like a Progress /* comment + break + end + end + + return "progress" +end + +--- Checks if this is a bindzone file or not +--- Taken from vim.filetype.detect +--- +--- @return string|nil The detected filetype +function M.bindzone() + local lines = util.getlines_as_string(0, 4) + if + util.findany( + lines, + { "^; <<>> DiG [0-9%.]+.* <<>>", "%$ORIGIN", "%$TTL", "IN%s+SOA" } + ) + then + return "bindzone" + end +end + +local udev_rules_pattern = '^%s*udev_rules%s*=%s*"([%^"]+)/*".*' + +--- This function looks at the file path rather the contents of the rule file. +--- if the path is in any of the predifined udev rules path or is in one off +--- the paths defined in '/etc/udev/udev.conf', then it is not a udevrules file +--- Taken from vim.filetype.detect +--- +--- @param path string The absolute path the file is at +--- @return string The detected filetype +function M.rules(path) + path = path:lower() + if + util.findany(path, { + "/etc/udev/.*%.rules$", + "/etc/udev/rules%.d/.*$.rules$", + "/usr/lib/udev/.*%.rules$", + "/usr/lib/udev/rules%.d/.*%.rules$", + "/lib/udev/.*%.rules$", + "/lib/udev/rules%.d/.*%.rules$", + }) + then + return "udevrules" + end + + if path:find("^/etc/ufw/") then + -- Better than hog + return "conf" + end + + if + util.findany( + path, + { "^/etc/polkit%-1/rules%.d", "/usr/share/polkit%-1/rules%.d" } + ) + then + return "javascript" + end + + local ok, config_lines = pcall(vim.fn.readfile, "/etc/udev/udev.conf") + if not ok then + return "hog" + end + + local dir = vim.fs.dirname(path) + for _, line in ipairs(config_lines) do + local match = line:match(udev_rules_pattern) + if not match then + goto continue + end + + local udev_rules = line:gsub(udev_rules_pattern, match, 1) + if dir == udev_rules then + return "udevrules" + end + + ::continue:: + end + + return "hog" +end + +--- Diffrentiate between racc and yacc +--- Taken from vim.filetype.detect +--- +--- @return string|nil The detected filetype +function M.inp() + if util.getline():find("^%*") then + return "abaqus" + end + + for _, line in ipairs(util.getlines(0, 500)) do + if line:lower():find("^header surface data") then + return "trasys" + end + end +end + +--- Diffrentiate between racc and yacc +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.y() + for _, line in ipairs(util.getlines(0, 100)) do + if line:find("^%s*%%") then + return "yacc" + end + + if + util.match_vim_regex(line, [[\c^\s*\(#\|class\>\)]]) + and not line:lower():find("^%s*#%s*include") + then + return "racc" + end + end + + return "yacc" +end + +--- Rely on the file to start with a comment. +--- MS message text files use ';', Sendmail files use '#' or 'dnl' +--- Taken from vim.filetype.detect +--- +--- @return string The detected filetype +function M.mc() + for _, line in ipairs(util.getlines(0, 20)) do + if util.findany(line, { "^%s*#", "^s*dnl" }) then + return "m4" + end + + if line:find("^#s*;") then + return "msmessages" + end + end + + return "m4" +end + return M diff --git a/lua/filetype/init.lua b/lua/filetype/init.lua index 4251a80..86735ce 100644 --- a/lua/filetype/init.lua +++ b/lua/filetype/init.lua @@ -116,11 +116,15 @@ function M.resolve() return end - if try_lookup(callback_args.file_ext, custom_map.function_extensions) then + if + try_lookup(callback_args.file_ext, custom_map.function_extensions) + then return end - if try_lookup(callback_args.file_name, custom_map.function_literal) then + if + try_lookup(callback_args.file_name, custom_map.function_literal) + then return end diff --git a/lua/filetype/mappings/function.lua b/lua/filetype/mappings/function.lua index 00df89a..38c2e1b 100644 --- a/lua/filetype/mappings/function.lua +++ b/lua/filetype/mappings/function.lua @@ -48,7 +48,9 @@ M.extensions = { end end, ["t"] = function(args) - return detect.nroff() or detect.perl(args.file_path, args.file_ext) or "tads" + return detect.nroff() + or detect.perl(args.file_path, args.file_ext) + or "tads" end, ["class"] = function() -- Decimal escape sequence @@ -198,7 +200,7 @@ M.extensions = { end end, ["inp"] = function() - vim.cmd([[call dist#ft#Check_inp()]]) + return detect.inp() end, ["asm"] = function() return detect.asm() @@ -225,10 +227,17 @@ M.extensions = { return detect.vbasic() end, ["btm"] = function() - vim.cmd([[call dist#ft#FTbtm()]]) + if + vim.fn.exists("g:dosbatch_syntax_for_btm") == 1 + and vim.g.dosbatch_syntax_for_btm ~= 0 + then + return "dosbatch" + end + + return "btm" end, ["db"] = function() - vim.cmd([[call dist#ft#BindzoneCheck('')]]) + return detect.bindzone() end, ["c"] = function() return detect.lpc() @@ -282,7 +291,7 @@ M.extensions = { return detect.dtrace() end, ["com"] = function() - vim.cmd([[call dist#ft#BindzoneCheck('dcl')]]) + return detect.bindzone() or "dcl" end, ["html"] = function() return detect.html() @@ -300,37 +309,37 @@ M.extensions = { return detect.idl() end, ["pro"] = function() - return detect.proto("idlang") + return detect.proto() or "idlang" end, ["m"] = function() - vim.cmd([[call dist#ft#FTm()]]) + return detect.m() end, - ["mms"] = function() - vim.cmd([[call dist#ft#FTmms()]]) + ["mm"] = function() + return detect.mm() end, - ["*.mm"] = function() - vim.cmd([[call dist#ft#FTmm()]]) + ["mms"] = function() + return detect.mms() end, ["pp"] = function() - vim.cmd([[call dist#ft#FTpp()]]) + return detect.pp() end, ["pl"] = function() - vim.cmd([[call dist#ft#FTpl()]]) + return detect.pl() end, ["PL"] = function() - vim.cmd([[call dist#ft#FTpl()]]) + return detect.pl() end, ["inc"] = function() - vim.cmd([[call dist#ft#FTinc()]]) + return detect.inc() end, ["w"] = function() - vim.cmd([[call dist#ft#FTprogress_cweb()]]) + return detect.progress_cweb() end, ["i"] = function() - vim.cmd([[call dist#ft#FTprogress_asm()]]) + return detect.progress_asm() end, ["p"] = function() - vim.cmd([[call dist#ft#FTprogress_pascal()]]) + return detect.progress_pascal() end, ["r"] = function() return detect.r() @@ -339,7 +348,7 @@ M.extensions = { return detect.r() end, ["mc"] = function() - vim.cmd([[call dist#ft#McSetf()]]) + return detect.mc() end, ["ebuild"] = function() return detect.sh({ fallback = "bash" }) @@ -368,8 +377,8 @@ M.extensions = { ["csh"] = function() return detect.csh() end, - ["rules"] = function() - vim.cmd([[call dist#ft#FTRules()]]) + ["rules"] = function(args) + return detect.rules(args.file_path) end, ["sql"] = function() return detect.sql() @@ -384,7 +393,7 @@ M.extensions = { return detect.xml() end, ["y"] = function() - vim.cmd([[call dist#ft#FTy()]]) + return detect.y() end, ["dtml"] = function() return detect.html() @@ -443,7 +452,7 @@ M.literal = { end end, ["indent.pro"] = function() - return detect.proto("indent") + return detect.proto() or "indent" end, [".bashrc"] = function() return detect.sh({ fallback = "bash" }) From e251f8bd94a0de83c384f95e1bd8f0185c46cb0c Mon Sep 17 00:00:00 2001 From: Xevnar Date: Sat, 3 Dec 2022 12:46:22 +0300 Subject: [PATCH 19/21] feat(extensions): add systemd `.service` extension closes: #73 --- lua/filetype/mappings/extensions.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/filetype/mappings/extensions.lua b/lua/filetype/mappings/extensions.lua index 877a184..8684bac 100644 --- a/lua/filetype/mappings/extensions.lua +++ b/lua/filetype/mappings/extensions.lua @@ -505,6 +505,7 @@ return { ["sdc"] = "sdc", ["sdl"] = "sdl", ["sed"] = "sed", + ["service"] = "systemd", ["sexp"] = "sexplib", ["si"] = "cuplsim", ["sieve"] = "sieve", From a32b6f6941207a559769b74526a987638c68fa02 Mon Sep 17 00:00:00 2001 From: Xevnar Date: Sat, 3 Dec 2022 12:53:00 +0300 Subject: [PATCH 20/21] fix(star_sets): fix broken muttrc lua pattern --- lua/filetype/mappings/complex.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lua/filetype/mappings/complex.lua b/lua/filetype/mappings/complex.lua index e828e5a..cc9a354 100644 --- a/lua/filetype/mappings/complex.lua +++ b/lua/filetype/mappings/complex.lua @@ -152,9 +152,12 @@ M.star_sets = { ["[rR]akefile.*"] = [[ruby]], ["reportbug-.*"] = [[mail]], [".*/etc/modprobe%..*"] = [[modconf]], - ["%.mutt{ng,}rc.*"] = [[muttrc]], - [".*/%.mutt{ng,}/mutt{ng,}rc.*"] = [[muttrc]], - ["mutt{ng,}rc.*,Mutt{ng,}rc.*"] = [[muttrc]], + ["%.muttrc.*"] = [[muttrc]], + ["%.muttngrc.*"] = [[muttrc]], + [".*/%.mutt/mutt.*rc.*"] = [[muttrc]], + [".*/%.muttng/mutt.*rc.*"] = [[muttrc]], + ["[mM]uttrc.*"] = [[muttrc]], + ["[mM]uttngrc.*"] = [[muttrc]], ["%.neomuttrc.*"] = [[neomuttrc]], [".*/%.neomutt/neomuttrc.*"] = [[neomuttrc]], ["neomuttrc.*"] = [[neomuttrc]], From 31d41a468bf30951fad5ced63ef86e3c51f6708e Mon Sep 17 00:00:00 2001 From: Xevnar Date: Sat, 3 Dec 2022 14:02:14 +0300 Subject: [PATCH 21/21] docs(README): document the table passed to function callbacks --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 27ccac5..28202f6 100644 --- a/README.md +++ b/README.md @@ -84,12 +84,15 @@ require("filetype").setup({ -- Remove annoying indent jumping vim.bo.cinoptions = vim.bo.cinoptions .. "L0" end, - ["pdf"] = function() + + -- The functions recieves an table table with following fields: + -- args table: * file_path: The absolute path of the file + -- * file_name: The name of the file (including extension) + -- * file_ext: The extention at the end of the file + ["pdf"] = function(args) vim.bo.filetype = "pdf" -- Open in PDF viewer (Skim.app) automatically - vim.fn.jobstart( - "open -a skim " .. '"' .. vim.fn.expand("%") .. '"' - ) + vim.fn.jobstart([[open -a skim "]] .. args.file_path .. '"') end, }, function_literal = {