aboutsummaryrefslogtreecommitdiff
path: root/scripts/gen_filetype.lua
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/gen_filetype.lua')
-rw-r--r--scripts/gen_filetype.lua201
1 files changed, 201 insertions, 0 deletions
diff --git a/scripts/gen_filetype.lua b/scripts/gen_filetype.lua
new file mode 100644
index 0000000000..42478a1082
--- /dev/null
+++ b/scripts/gen_filetype.lua
@@ -0,0 +1,201 @@
+local do_not_run = true
+if do_not_run then
+ print([[
+ This script was used to bootstrap the filetype patterns in runtime/lua/vim/filetype.lua. It
+ should no longer be used except for testing purposes. New filetypes, or changes to existing
+ filetypes, should be ported manually as part of the vim-patch process.
+ ]])
+ return
+end
+
+local filetype_vim = "runtime/filetype.vim"
+local filetype_lua = "runtime/lua/vim/filetype.lua"
+
+local keywords = {
+ ["for"] = true,
+ ["or"] = true,
+ ["and"] = true,
+ ["end"] = true,
+ ["do"] = true,
+ ["if"] = true,
+ ["while"] = true,
+ ["repeat"] = true,
+}
+
+local sections = {
+ extension = { str = {}, func = {} },
+ filename = { str = {}, func = {} },
+ pattern = { str = {}, func = {} },
+}
+
+local specialchars = "%*%?\\%$%[%]%{%}"
+
+local function add_pattern(pat, ft)
+ local ok = true
+
+ -- Patterns that start or end with { or } confuse splitting on commas and make parsing harder, so just skip those
+ if not string.find(pat, "^%{") and not string.find(pat, "%}$") then
+ for part in string.gmatch(pat, "[^,]+") do
+ if not string.find(part, "[" .. specialchars .. "]") then
+ if type(ft) == "string" then
+ sections.filename.str[part] = ft
+ else
+ sections.filename.func[part] = ft
+ end
+ elseif string.match(part, "^%*%.[^%./" .. specialchars .. "]+$") then
+ if type(ft) == "string" then
+ sections.extension.str[part:sub(3)] = ft
+ else
+ sections.extension.func[part:sub(3)] = ft
+ end
+ else
+ if string.match(part, "^%*/[^" .. specialchars .. "]+$") then
+ -- For patterns matching */some/pattern we want to easily match files
+ -- with path /some/pattern, so include those in filename detection
+ if type(ft) == "string" then
+ sections.filename.str[part:sub(2)] = ft
+ else
+ sections.filename.func[part:sub(2)] = ft
+ end
+ end
+
+ if string.find(part, "^[%w-_.*?%[%]/]+$") then
+ local p = part:gsub("%.", "%%."):gsub("%*", ".*"):gsub("%?", ".")
+ -- Insert into array to maintain order rather than setting
+ -- key-value directly
+ if type(ft) == "string" then
+ sections.pattern.str[p] = ft
+ else
+ sections.pattern.func[p] = ft
+ end
+ else
+ ok = false
+ end
+ end
+ end
+ end
+
+ return ok
+end
+
+local function parse_line(line)
+ local pat, ft
+ pat, ft = line:match("^%s*au%a* Buf[%a,]+%s+(%S+)%s+setf%s+(%S+)")
+ if pat then
+ return add_pattern(pat, ft)
+ else
+ local func
+ pat, func = line:match("^%s*au%a* Buf[%a,]+%s+(%S+)%s+call%s+(%S+)")
+ if pat then
+ return add_pattern(pat, function() return func end)
+ end
+ end
+end
+
+local unparsed = {}
+local full_line
+for line in io.lines(filetype_vim) do
+ local cont = string.match(line, "^%s*\\%s*(.*)$")
+ if cont then
+ full_line = full_line .. " " .. cont
+ else
+ if full_line then
+ if not parse_line(full_line) and string.find(full_line, "^%s*au%a* Buf") then
+ table.insert(unparsed, full_line)
+ end
+ end
+ full_line = line
+ end
+end
+
+if #unparsed > 0 then
+ print("Failed to parse the following patterns:")
+ for _, v in ipairs(unparsed) do
+ print(v)
+ end
+end
+
+local function add_item(indent, key, ft)
+ if type(ft) == "string" then
+ if string.find(key, "%A") or keywords[key] then
+ key = string.format("[\"%s\"]", key)
+ end
+ return string.format([[%s%s = "%s",]], indent, key, ft)
+ elseif type(ft) == "function" then
+ local func = ft()
+ if string.find(key, "%A") or keywords[key] then
+ key = string.format("[\"%s\"]", key)
+ end
+ -- Right now only a single argument is supported, which covers
+ -- everything in filetype.vim as of this writing
+ local arg = string.match(func, "%((.*)%)$")
+ func = string.gsub(func, "%(.*$", "")
+ if arg == "" then
+ -- Function with no arguments, call the function directly
+ return string.format([[%s%s = function() vim.fn["%s"]() end,]], indent, key, func)
+ elseif string.match(arg, [[^(["']).*%1$]]) then
+ -- String argument
+ if func == "s:StarSetf" then
+ return string.format([[%s%s = starsetf(%s),]], indent, key, arg)
+ else
+ return string.format([[%s%s = function() vim.fn["%s"](%s) end,]], indent, key, func, arg)
+ end
+ elseif string.find(arg, "%(") then
+ -- Function argument
+ return string.format([[%s%s = function() vim.fn["%s"](vim.fn.%s) end,]], indent, key, func, arg)
+ else
+ assert(false, arg)
+ end
+ end
+end
+
+do
+ local lines = {}
+ local start = false
+ for line in io.lines(filetype_lua) do
+ if line:match("^%s+-- END [A-Z]+$") then
+ start = false
+ end
+
+ if not start then
+ table.insert(lines, line)
+ end
+
+ local indent, section = line:match("^(%s+)-- BEGIN ([A-Z]+)$")
+ if section then
+ start = true
+ local t = sections[string.lower(section)]
+
+ local sorted = {}
+ for k, v in pairs(t.str) do
+ table.insert(sorted, {[k] = v})
+ end
+
+ table.sort(sorted, function(a, b)
+ return a[next(a)] < b[next(b)]
+ end)
+
+ for _, v in ipairs(sorted) do
+ local k = next(v)
+ table.insert(lines, add_item(indent, k, v[k]))
+ end
+
+ sorted = {}
+ for k, v in pairs(t.func) do
+ table.insert(sorted, {[k] = v})
+ end
+
+ table.sort(sorted, function(a, b)
+ return next(a) < next(b)
+ end)
+
+ for _, v in ipairs(sorted) do
+ local k = next(v)
+ table.insert(lines, add_item(indent, k, v[k]))
+ end
+ end
+ end
+ local f = io.open(filetype_lua, "w")
+ f:write(table.concat(lines, "\n") .. "\n")
+ f:close()
+end