aboutsummaryrefslogtreecommitdiff
path: root/runtime/lua/vim/lsp/_dynamic.lua
blob: 04040e8e285fd240a2a5faed1aa56168a9e5c689 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
local wf = require('vim.lsp._watchfiles')

--- @class lsp.DynamicCapabilities
--- @field capabilities table<string, lsp.Registration[]>
--- @field client_id number
local M = {}

--- @param client_id number
function M.new(client_id)
  return setmetatable({
    capabilities = {},
    client_id = client_id,
  }, { __index = M })
end

function M:supports_registration(method)
  local client = vim.lsp.get_client_by_id(self.client_id)
  if not client then
    return false
  end
  local capability = vim.tbl_get(client.config.capabilities, unpack(vim.split(method, '/')))
  return type(capability) == 'table' and capability.dynamicRegistration
end

--- @param registrations lsp.Registration[]
--- @private
function M:register(registrations)
  -- remove duplicates
  self:unregister(registrations)
  for _, reg in ipairs(registrations) do
    local method = reg.method
    if not self.capabilities[method] then
      self.capabilities[method] = {}
    end
    table.insert(self.capabilities[method], reg)
  end
end

--- @param unregisterations lsp.Unregistration[]
--- @private
function M:unregister(unregisterations)
  for _, unreg in ipairs(unregisterations) do
    local method = unreg.method
    if not self.capabilities[method] then
      return
    end
    local id = unreg.id
    for i, reg in ipairs(self.capabilities[method]) do
      if reg.id == id then
        table.remove(self.capabilities[method], i)
        break
      end
    end
  end
end

--- @param method string
--- @param opts? {bufnr?: number}
--- @return lsp.Registration? (table|nil) the registration if found
--- @private
function M:get(method, opts)
  opts = opts or {}
  opts.bufnr = opts.bufnr or vim.api.nvim_get_current_buf()
  for _, reg in ipairs(self.capabilities[method] or {}) do
    if not reg.registerOptions then
      return reg
    end
    local documentSelector = reg.registerOptions.documentSelector
    if not documentSelector then
      return reg
    end
    if M.match(opts.bufnr, documentSelector) then
      return reg
    end
  end
end

--- @param method string
--- @param opts? {bufnr?: number}
--- @private
function M:supports(method, opts)
  return self:get(method, opts) ~= nil
end

--- @param bufnr number
--- @param documentSelector lsp.DocumentSelector
--- @private
function M.match(bufnr, documentSelector)
  local ft = vim.bo[bufnr].filetype
  local uri = vim.uri_from_bufnr(bufnr)
  local fname = vim.uri_to_fname(uri)
  for _, filter in ipairs(documentSelector) do
    local matches = true
    if filter.language and ft ~= filter.language then
      matches = false
    end
    if matches and filter.scheme and not vim.startswith(uri, filter.scheme .. ':') then
      matches = false
    end
    if matches and filter.pattern and not wf._match(filter.pattern, fname) then
      matches = false
    end
    if matches then
      return true
    end
  end
end

return M