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