neovim

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

_tagfunc.lua (3216B)


      1 local lsp = vim.lsp
      2 local api = vim.api
      3 local util = lsp.util
      4 
      5 ---@param name string
      6 ---@param range lsp.Range
      7 ---@param uri string
      8 ---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
      9 ---@return {name: string, filename: string, cmd: string, kind?: string}
     10 local function mk_tag_item(name, range, uri, position_encoding)
     11  local bufnr = vim.uri_to_bufnr(uri)
     12  -- This is get_line_byte_from_position is 0-indexed, call cursor expects a 1-indexed position
     13  local byte = util._get_line_byte_from_position(bufnr, range.start, position_encoding) + 1
     14  return {
     15    name = name,
     16    filename = vim.uri_to_fname(uri),
     17    cmd = string.format([[/\%%%dl\%%%dc/]], range.start.line + 1, byte),
     18  }
     19 end
     20 
     21 ---@param pattern string
     22 ---@return table[]
     23 local function query_definition(pattern)
     24  local bufnr = api.nvim_get_current_buf()
     25  local win = api.nvim_get_current_win()
     26  local results = {}
     27 
     28  --- @param range lsp.Range
     29  --- @param uri string
     30  ---@param position_encoding 'utf-8'|'utf-16'|'utf-32'
     31  local add = function(range, uri, position_encoding)
     32    table.insert(results, mk_tag_item(pattern, range, uri, position_encoding))
     33  end
     34 
     35  local request_results, _ = lsp.buf_request_sync(bufnr, 'textDocument/definition', function(client)
     36    return util.make_position_params(win, client.offset_encoding)
     37  end)
     38 
     39  for client_id, res in pairs(request_results or {}) do
     40    local client = assert(lsp.get_client_by_id(client_id))
     41    local result = res.result ---@type lsp.Location|lsp.Location[]|lsp.LocationLink[]|nil
     42 
     43    if result then
     44      local encoding = client.offset_encoding
     45      -- single Location
     46      if result.range then
     47        add(result.range, result.uri, encoding)
     48      else
     49        for _, location in ipairs(result) do
     50          if location.range then -- Location
     51            add(location.range, location.uri, encoding)
     52          else -- LocationLink
     53            add(location.targetSelectionRange, location.targetUri, encoding)
     54          end
     55        end
     56      end
     57    end
     58  end
     59 
     60  return results
     61 end
     62 
     63 ---@param pattern string
     64 ---@return table[]
     65 local function query_workspace_symbols(pattern)
     66  local results_by_client, err =
     67    lsp.buf_request_sync(0, 'workspace/symbol', { query = pattern }, 1000)
     68  if err then
     69    return {}
     70  end
     71  local results = {}
     72  for client_id, responses in pairs(assert(results_by_client)) do
     73    local client = lsp.get_client_by_id(client_id)
     74    local position_encoding = client and client.offset_encoding or 'utf-16'
     75    local symbols = responses.result --[[@as lsp.SymbolInformation[]|nil]]
     76    for _, symbol in pairs(symbols or {}) do
     77      local loc = symbol.location
     78      local item = mk_tag_item(symbol.name, loc.range, loc.uri, position_encoding)
     79      item.kind = lsp.protocol.SymbolKind[symbol.kind] or 'Unknown'
     80      table.insert(results, item)
     81    end
     82  end
     83  return results
     84 end
     85 
     86 local function tagfunc(pattern, flags)
     87  -- avoid definition/symbol queries for insert completion
     88  if string.match(flags, 'i') then
     89    return vim.NIL
     90  end
     91  local matches = string.match(flags, 'c') and query_definition(pattern)
     92    or query_workspace_symbols(pattern)
     93  -- fall back to tags if no matches
     94  return #matches > 0 and matches or vim.NIL
     95 end
     96 
     97 return tagfunc