utils.lua 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. local M = {}
  2. local tbl = require "lvim.utils.table"
  3. local Log = require "lvim.core.log"
  4. function M.is_client_active(name)
  5. local clients = vim.lsp.get_active_clients()
  6. return tbl.find_first(clients, function(client)
  7. return client.name == name
  8. end)
  9. end
  10. function M.get_active_clients_by_ft(filetype)
  11. local matches = {}
  12. local clients = vim.lsp.get_active_clients()
  13. for _, client in pairs(clients) do
  14. local supported_filetypes = client.config.filetypes or {}
  15. if client.name ~= "null-ls" and vim.tbl_contains(supported_filetypes, filetype) then
  16. table.insert(matches, client)
  17. end
  18. end
  19. return matches
  20. end
  21. function M.get_client_capabilities(client_id)
  22. local client
  23. if not client_id then
  24. local buf_clients = vim.lsp.buf_get_clients()
  25. for _, buf_client in pairs(buf_clients) do
  26. if buf_client.name ~= "null-ls" then
  27. client = buf_client
  28. break
  29. end
  30. end
  31. else
  32. client = vim.lsp.get_client_by_id(tonumber(client_id))
  33. end
  34. if not client then
  35. error "Unable to determine client_id"
  36. return
  37. end
  38. local enabled_caps = {}
  39. for capability, status in pairs(client.server_capabilities or client.resolved_capabilities) do
  40. if status == true then
  41. table.insert(enabled_caps, capability)
  42. end
  43. end
  44. return enabled_caps
  45. end
  46. ---Get supported filetypes per server
  47. ---@param server_name string can be any server supported by nvim-lsp-installer
  48. ---@return string[] supported filestypes as a list of strings
  49. function M.get_supported_filetypes(server_name)
  50. local status_ok, config = pcall(require, ("lspconfig.server_configurations.%s"):format(server_name))
  51. if not status_ok then
  52. return {}
  53. end
  54. return config.default_config.filetypes or {}
  55. end
  56. ---Get supported servers per filetype
  57. ---@param filter { filetype: string | string[] }?: (optional) Used to filter the list of server names.
  58. ---@return string[] list of names of supported servers
  59. function M.get_supported_servers(filter)
  60. local _, supported_servers = pcall(function()
  61. return require("mason-lspconfig").get_available_servers(filter)
  62. end)
  63. return supported_servers or {}
  64. end
  65. ---Get all supported filetypes by nvim-lsp-installer
  66. ---@return string[] supported filestypes as a list of strings
  67. function M.get_all_supported_filetypes()
  68. local status_ok, filetype_server_map = pcall(require, "mason-lspconfig.mappings.filetype")
  69. if not status_ok then
  70. return {}
  71. end
  72. return vim.tbl_keys(filetype_server_map or {})
  73. end
  74. function M.setup_document_highlight(client, bufnr)
  75. if lvim.builtin.illuminate.active then
  76. Log:debug "skipping setup for document_highlight, illuminate already active"
  77. return
  78. end
  79. local status_ok, highlight_supported = pcall(function()
  80. return client.supports_method "textDocument/documentHighlight"
  81. end)
  82. if not status_ok or not highlight_supported then
  83. return
  84. end
  85. local group = "lsp_document_highlight"
  86. local hl_events = { "CursorHold", "CursorHoldI" }
  87. local ok, hl_autocmds = pcall(vim.api.nvim_get_autocmds, {
  88. group = group,
  89. buffer = bufnr,
  90. event = hl_events,
  91. })
  92. if ok and #hl_autocmds > 0 then
  93. return
  94. end
  95. vim.api.nvim_create_augroup(group, { clear = false })
  96. vim.api.nvim_create_autocmd(hl_events, {
  97. group = group,
  98. buffer = bufnr,
  99. callback = vim.lsp.buf.document_highlight,
  100. })
  101. vim.api.nvim_create_autocmd("CursorMoved", {
  102. group = group,
  103. buffer = bufnr,
  104. callback = vim.lsp.buf.clear_references,
  105. })
  106. end
  107. function M.setup_document_symbols(client, bufnr)
  108. vim.g.navic_silence = false -- can be set to true to suppress error
  109. local symbols_supported = client.supports_method "textDocument/documentSymbol"
  110. if not symbols_supported then
  111. Log:debug("skipping setup for document_symbols, method not supported by " .. client.name)
  112. return
  113. end
  114. local status_ok, navic = pcall(require, "nvim-navic")
  115. if status_ok then
  116. navic.attach(client, bufnr)
  117. end
  118. end
  119. function M.setup_codelens_refresh(client, bufnr)
  120. local status_ok, codelens_supported = pcall(function()
  121. return client.supports_method "textDocument/codeLens"
  122. end)
  123. if not status_ok or not codelens_supported then
  124. return
  125. end
  126. local group = "lsp_code_lens_refresh"
  127. local cl_events = { "BufEnter", "InsertLeave" }
  128. local ok, cl_autocmds = pcall(vim.api.nvim_get_autocmds, {
  129. group = group,
  130. buffer = bufnr,
  131. event = cl_events,
  132. })
  133. if ok and #cl_autocmds > 0 then
  134. return
  135. end
  136. vim.api.nvim_create_augroup(group, { clear = false })
  137. vim.api.nvim_create_autocmd(cl_events, {
  138. group = group,
  139. buffer = bufnr,
  140. callback = vim.lsp.codelens.refresh,
  141. })
  142. end
  143. ---filter passed to vim.lsp.buf.format
  144. ---always selects null-ls if it's available and caches the value per buffer
  145. ---@param client table client attached to a buffer
  146. ---@return boolean if client matches
  147. function M.format_filter(client)
  148. local filetype = vim.bo.filetype
  149. local n = require "null-ls"
  150. local s = require "null-ls.sources"
  151. local method = n.methods.FORMATTING
  152. local available_formatters = s.get_available(filetype, method)
  153. if #available_formatters > 0 then
  154. return client.name == "null-ls"
  155. elseif client.supports_method "textDocument/formatting" then
  156. return true
  157. else
  158. return false
  159. end
  160. end
  161. ---Provide vim.lsp.buf.format for nvim <0.8
  162. ---@param opts table
  163. function M.format(opts)
  164. opts = opts or {}
  165. opts.filter = opts.filter or M.format_filter
  166. if vim.lsp.buf.format then
  167. return vim.lsp.buf.format(opts)
  168. end
  169. local bufnr = opts.bufnr or vim.api.nvim_get_current_buf()
  170. ---@type table|nil
  171. local clients = vim.lsp.get_active_clients {
  172. id = opts.id,
  173. bufnr = bufnr,
  174. name = opts.name,
  175. }
  176. if opts.filter then
  177. clients = vim.tbl_filter(opts.filter, clients)
  178. end
  179. clients = vim.tbl_filter(function(client)
  180. return client.supports_method "textDocument/formatting"
  181. end, clients)
  182. if #clients == 0 then
  183. vim.notify_once "[LSP] Format request failed, no matching language servers."
  184. end
  185. local timeout_ms = opts.timeout_ms or 1000
  186. for _, client in pairs(clients) do
  187. local params = vim.lsp.util.make_formatting_params(opts.formatting_options)
  188. local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr)
  189. if result and result.result then
  190. vim.lsp.util.apply_text_edits(result.result, bufnr, client.offset_encoding)
  191. elseif err then
  192. vim.notify(string.format("[LSP][%s] %s", client.name, err), vim.log.levels.WARN)
  193. end
  194. end
  195. end
  196. return M