neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

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