language.lua (5899B)
1 local api = vim.api 2 3 local M = {} 4 5 ---@type table<string,string> 6 local ft_to_lang = { 7 help = 'vimdoc', 8 checkhealth = 'vimdoc', 9 } 10 11 --- Returns the filetypes for which a parser named {lang} is used. 12 --- 13 --- The list includes {lang} itself plus all filetypes registered via 14 --- |vim.treesitter.language.register()|. 15 --- 16 --- @param lang string Name of parser 17 --- @return string[] filetypes 18 function M.get_filetypes(lang) 19 local r = { lang } ---@type string[] 20 for ft, p in pairs(ft_to_lang) do 21 if p == lang then 22 r[#r + 1] = ft 23 end 24 end 25 return r 26 end 27 28 --- Returns the language name to be used when loading a parser for {filetype}. 29 --- 30 --- If no language has been explicitly registered via |vim.treesitter.language.register()|, 31 --- default to {filetype}. For composite filetypes like `html.glimmer`, only the main filetype is 32 --- returned. 33 --- 34 --- @param filetype string 35 --- @return string|nil 36 function M.get_lang(filetype) 37 if filetype == '' then 38 return 39 end 40 if ft_to_lang[filetype] then 41 return ft_to_lang[filetype] 42 end 43 -- for subfiletypes like html.glimmer use only "main" filetype 44 filetype = assert(vim.split(filetype, '.', { plain = true })[1]) 45 return ft_to_lang[filetype] or filetype 46 end 47 48 ---@deprecated 49 function M.require_language(lang, path, silent, symbol_name) 50 vim.deprecate( 51 'vim.treesitter.language.require_language()', 52 'vim.treesitter.language.add()', 53 '0.12' 54 ) 55 local opts = { 56 silent = silent, 57 path = path, 58 symbol_name = symbol_name, 59 } 60 61 if silent then 62 local installed = pcall(M.add, lang, opts) 63 return installed 64 end 65 66 return M.add(lang, opts) 67 end 68 69 --- Load wasm or native parser (wrapper) 70 --- todo(clason): move to C 71 --- 72 ---@param path string Path of parser library 73 ---@param lang string Language name 74 ---@param symbol_name? string Internal symbol name for the language to load (default lang) 75 ---@return boolean? True if parser is loaded 76 local function loadparser(path, lang, symbol_name) 77 if vim.endswith(path, '.wasm') then 78 return vim._ts_add_language_from_wasm and vim._ts_add_language_from_wasm(path, lang) 79 else 80 return vim._ts_add_language_from_object(path, lang, symbol_name) 81 end 82 end 83 84 ---@class vim.treesitter.language.add.Opts 85 ---@inlinedoc 86 --- 87 ---Optional path the parser is located at 88 ---@field path? string 89 --- 90 ---Internal symbol name for the language to load 91 ---@field symbol_name? string 92 93 --- Load parser with name {lang} 94 --- 95 --- Parsers are searched in the `parser` runtime directory, or the provided {path}. 96 --- Can be used to check for available parsers before enabling treesitter features, e.g., 97 --- ```lua 98 --- if vim.treesitter.language.add('markdown') then 99 --- vim.treesitter.start(bufnr, 'markdown') 100 --- end 101 --- ``` 102 --- 103 ---@param lang string Name of the parser (alphanumerical and `_` only) 104 ---@param opts? vim.treesitter.language.add.Opts Options: 105 ---@return boolean? True if parser is loaded 106 ---@return string? Error if parser cannot be loaded 107 function M.add(lang, opts) 108 opts = opts or {} 109 local path = opts.path 110 local symbol_name = opts.symbol_name 111 112 vim.validate('lang', lang, 'string') 113 vim.validate('path', path, 'string', true) 114 vim.validate('symbol_name', symbol_name, 'string', true) 115 116 -- parser names are assumed to be lowercase (consistent behavior on case-insensitive file systems) 117 lang = lang:lower() 118 119 if vim._ts_has_language(lang) then 120 return true 121 end 122 123 if path == nil then 124 -- allow only safe language names when looking for libraries to load 125 if not (lang and lang:match('[%w_]+') == lang) then 126 return nil, string.format('Invalid language name "%s"', lang) 127 end 128 129 local fname = 'parser/' .. lang .. '.*' 130 local paths = api.nvim_get_runtime_file(fname, false) 131 if #paths == 0 then 132 return nil, string.format('No parser for language "%s"', lang) 133 end 134 path = paths[1] 135 end 136 137 local res = loadparser(path, lang, symbol_name) 138 return res, 139 res == nil and string.format('Cannot load parser %s for language "%s"', path, lang) or nil 140 end 141 142 --- @param x string|string[] 143 --- @return string[] 144 local function ensure_list(x) 145 if type(x) == 'table' then 146 return x 147 end 148 return { x } 149 end 150 151 --- Register a parser named {lang} to be used for {filetype}(s). 152 --- 153 --- Note: this adds or overrides the mapping for {filetype}, any existing mappings from other 154 --- filetypes to {lang} will be preserved. 155 --- 156 --- @param lang string Name of parser 157 --- @param filetype string|string[] Filetype(s) to associate with lang 158 function M.register(lang, filetype) 159 vim.validate('lang', lang, 'string') 160 vim.validate('filetype', filetype, { 'string', 'table' }) 161 162 for _, f in ipairs(ensure_list(filetype)) do 163 if f ~= '' then 164 ft_to_lang[f] = lang 165 end 166 end 167 end 168 169 --- Inspects the provided language. 170 --- 171 --- Inspecting provides some useful information on the language like ABI version, parser state count 172 --- (a measure of parser complexity), node and field names, and whether the language came from a 173 --- WASM module. 174 --- 175 --- Node names are returned in a table mapping each node name to a `boolean` indicating whether or 176 --- not the node is named (i.e., not anonymous). Anonymous nodes are surrounded with double quotes 177 --- (`"`). 178 --- 179 --- For ABI 15 parsers, also show parser metadata (major, minor, patch version) and a table of 180 --- supertypes with their respective subtypes. 181 --- 182 ---@param lang string Language 183 ---@return TSLangInfo 184 function M.inspect(lang) 185 M.add(lang) 186 return vim._ts_inspect_language(lang) 187 end 188 189 --- Returns available treesitter languages. 190 function M._complete() 191 local parsers = api.nvim_get_runtime_file('parser/*', true) 192 local parser_names_set = {} ---@type table<string, boolean> 193 for _, parser in ipairs(parsers) do 194 local parser_name = vim.fn.fnamemodify(parser, ':t:r') 195 parser_names_set[parser_name] = true 196 end 197 return vim.tbl_keys(parser_names_set) 198 end 199 200 return M