gen_filetype.lua (5793B)
1 local do_not_run = true 2 if do_not_run then 3 print([[ 4 This script was used to bootstrap the filetype patterns in runtime/lua/vim/filetype.lua. It 5 should no longer be used except for testing purposes. New filetypes, or changes to existing 6 filetypes, should be ported manually as part of the vim-patch process. 7 ]]) 8 return 9 end 10 11 local filetype_vim = 'runtime/filetype.vim' 12 local filetype_lua = 'runtime/lua/vim/filetype.lua' 13 14 local keywords = { 15 ['for'] = true, 16 ['or'] = true, 17 ['and'] = true, 18 ['end'] = true, 19 ['do'] = true, 20 ['if'] = true, 21 ['while'] = true, 22 ['repeat'] = true, 23 } 24 25 local sections = { 26 extension = { str = {}, func = {} }, 27 filename = { str = {}, func = {} }, 28 pattern = { str = {}, func = {} }, 29 } 30 31 local specialchars = '%*%?\\%$%[%]%{%}' 32 33 local function add_pattern(pat, ft) 34 local ok = true 35 36 -- Patterns that start or end with { or } confuse splitting on commas and make parsing harder, so just skip those 37 if not string.find(pat, '^%{') and not string.find(pat, '%}$') then 38 for part in string.gmatch(pat, '[^,]+') do 39 if not string.find(part, '[' .. specialchars .. ']') then 40 if type(ft) == 'string' then 41 sections.filename.str[part] = ft 42 else 43 sections.filename.func[part] = ft 44 end 45 elseif string.match(part, '^%*%.[^%./' .. specialchars .. ']+$') then 46 if type(ft) == 'string' then 47 sections.extension.str[part:sub(3)] = ft 48 else 49 sections.extension.func[part:sub(3)] = ft 50 end 51 else 52 if string.match(part, '^%*/[^' .. specialchars .. ']+$') then 53 -- For patterns matching */some/pattern we want to easily match files 54 -- with path /some/pattern, so include those in filename detection 55 if type(ft) == 'string' then 56 sections.filename.str[part:sub(2)] = ft 57 else 58 sections.filename.func[part:sub(2)] = ft 59 end 60 end 61 62 if string.find(part, '^[%w-_.*?%[%]/]+$') then 63 local p = part:gsub('%.', '%%.'):gsub('%*', '.*'):gsub('%?', '.') 64 -- Insert into array to maintain order rather than setting 65 -- key-value directly 66 if type(ft) == 'string' then 67 sections.pattern.str[p] = ft 68 else 69 sections.pattern.func[p] = ft 70 end 71 else 72 ok = false 73 end 74 end 75 end 76 end 77 78 return ok 79 end 80 81 local function parse_line(line) 82 local pat, ft 83 pat, ft = line:match('^%s*au%a* Buf[%a,]+%s+(%S+)%s+setf%s+(%S+)') 84 if pat then 85 return add_pattern(pat, ft) 86 else 87 local func 88 pat, func = line:match('^%s*au%a* Buf[%a,]+%s+(%S+)%s+call%s+(%S+)') 89 if pat then 90 return add_pattern(pat, function() 91 return func 92 end) 93 end 94 end 95 end 96 97 local unparsed = {} 98 local full_line 99 for line in io.lines(filetype_vim) do 100 local cont = string.match(line, '^%s*\\%s*(.*)$') 101 if cont then 102 full_line = full_line .. ' ' .. cont 103 else 104 if full_line then 105 if not parse_line(full_line) and string.find(full_line, '^%s*au%a* Buf') then 106 table.insert(unparsed, full_line) 107 end 108 end 109 full_line = line 110 end 111 end 112 113 if #unparsed > 0 then 114 print('Failed to parse the following patterns:') 115 for _, v in ipairs(unparsed) do 116 print(v) 117 end 118 end 119 120 local function add_item(indent, key, ft) 121 if type(ft) == 'string' then 122 if string.find(key, '%A') or keywords[key] then 123 key = string.format('["%s"]', key) 124 end 125 return string.format([[%s%s = "%s",]], indent, key, ft) 126 elseif type(ft) == 'function' then 127 local func = ft() 128 if string.find(key, '%A') or keywords[key] then 129 key = string.format('["%s"]', key) 130 end 131 -- Right now only a single argument is supported, which covers 132 -- everything in filetype.vim as of this writing 133 local arg = string.match(func, '%((.*)%)$') 134 func = string.gsub(func, '%(.*$', '') 135 if arg == '' then 136 -- Function with no arguments, call the function directly 137 return string.format([[%s%s = function() vim.fn["%s"]() end,]], indent, key, func) 138 elseif string.match(arg, [[^(["']).*%1$]]) then 139 -- String argument 140 if func == 's:StarSetf' then 141 return string.format([[%s%s = starsetf(%s),]], indent, key, arg) 142 else 143 return string.format([[%s%s = function() vim.fn["%s"](%s) end,]], indent, key, func, arg) 144 end 145 elseif string.find(arg, '%(') then 146 -- Function argument 147 return string.format( 148 [[%s%s = function() vim.fn["%s"](vim.fn.%s) end,]], 149 indent, 150 key, 151 func, 152 arg 153 ) 154 else 155 assert(false, arg) 156 end 157 end 158 end 159 160 do 161 local lines = {} 162 local start = false 163 for line in io.lines(filetype_lua) do 164 if line:match('^%s+-- END [A-Z]+$') then 165 start = false 166 end 167 168 if not start then 169 table.insert(lines, line) 170 end 171 172 local indent, section = line:match('^(%s+)-- BEGIN ([A-Z]+)$') 173 if section then 174 start = true 175 local t = sections[string.lower(section)] 176 177 local sorted = {} 178 for k, v in pairs(t.str) do 179 table.insert(sorted, { [k] = v }) 180 end 181 182 table.sort(sorted, function(a, b) 183 return a[next(a)] < b[next(b)] 184 end) 185 186 for _, v in ipairs(sorted) do 187 local k = next(v) 188 table.insert(lines, add_item(indent, k, v[k])) 189 end 190 191 sorted = {} 192 for k, v in pairs(t.func) do 193 table.insert(sorted, { [k] = v }) 194 end 195 196 table.sort(sorted, function(a, b) 197 return next(a) < next(b) 198 end) 199 200 for _, v in ipairs(sorted) do 201 local k = next(v) 202 table.insert(lines, add_item(indent, k, v[k])) 203 end 204 end 205 end 206 local f = io.open(filetype_lua, 'w') 207 f:write(table.concat(lines, '\n') .. '\n') 208 f:close() 209 end