lsp.lua 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. local blink = require('blink.cmp')
  2. local lspconfig = require('lspconfig')
  3. -- 1. Setup blink.cmp
  4. blink.setup({
  5. keymap = {
  6. preset = 'default',
  7. ['<CR>'] = { 'accept', 'fallback' },
  8. ['<Tab>'] = { 'select_next', 'fallback' },
  9. ['<S-Tab>'] = { 'select_prev', 'fallback' },
  10. },
  11. snippets = { preset = 'luasnip' },
  12. appearance = {
  13. use_nvim_cmp_as_default = true,
  14. nerd_font_variant = 'mono'
  15. },
  16. sources = {
  17. default = { 'lsp', 'path', 'buffer', 'snippets' },
  18. },
  19. fuzzy = {
  20. implementation = "prefer_rust",
  21. },
  22. signature = { enabled = true }
  23. })
  24. -- Load custom snippets from ~/.config/nvim/snips
  25. require("luasnip.loaders.from_vscode").lazy_load({ paths = { "~/.config/nvim/snips" } })
  26. require("luasnip.loaders.from_snipmate").lazy_load({ paths = { "~/.config/nvim/snips" } })
  27. -- 2. Define on_attach
  28. local on_attach = function(client, bufnr)
  29. -- Keymaps are handled globally in keymaps.lua (formerly bindings.vim)
  30. -- Enable CodeLens if supported
  31. if client.supports_method("textDocument/codeLens") then
  32. vim.api.nvim_create_autocmd({ "BufEnter", "CursorHold", "InsertLeave" }, {
  33. buffer = bufnr,
  34. callback = function()
  35. vim.lsp.codelens.refresh({ bufnr = bufnr })
  36. end,
  37. })
  38. end
  39. -- Enable Document Highlight if supported
  40. if client.supports_method("textDocument/documentHighlight") then
  41. vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
  42. buffer = bufnr,
  43. callback = function()
  44. vim.lsp.buf.document_highlight()
  45. end,
  46. })
  47. vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
  48. buffer = bufnr,
  49. callback = function()
  50. vim.lsp.buf.clear_references()
  51. end,
  52. })
  53. end
  54. -- Format on Type (Commented out - prefer Format on Save in conform.nvim)
  55. -- if client.supports_method("textDocument/onTypeFormatting") then
  56. -- vim.api.nvim_create_autocmd("InsertLeave", {
  57. -- buffer = bufnr,
  58. -- callback = function()
  59. -- vim.lsp.buf.format({ bufnr = bufnr, async = true })
  60. -- end,
  61. -- })
  62. -- end
  63. end
  64. -- 3. Configure Servers using Neovim 0.11 API where possible
  65. local capabilities = blink.get_lsp_capabilities()
  66. -- Basic servers
  67. local servers = { 'pyright', 'ruff', 'bashls', 'html', 'cssls', 'jdtls', 'rust_analyzer', 'clangd' }
  68. for _, server in ipairs(servers) do
  69. vim.lsp.config(server, {
  70. capabilities = capabilities,
  71. on_attach = on_attach,
  72. })
  73. vim.lsp.enable(server)
  74. end
  75. -- cspell LSP integration (using the arch package cspell-lsp)
  76. vim.lsp.config('cspell', {
  77. cmd = { 'env', 'NODE_PATH=' .. vim.fn.expand('~/.local/share/npm/lib/node_modules'), 'cspell-lsp', '--stdio' },
  78. filetypes = {
  79. "python", "sh", "rust", "kotlin", "java", "c", "cpp", "cmake",
  80. "markdown", "text", "gitcommit", "lua", "json", "yaml"
  81. },
  82. -- Explicitly tell Neovim where to look for the project root
  83. root_markers = { 'cspell.config.yaml', 'cspell.json', '.cspell.json' },
  84. capabilities = capabilities,
  85. -- initializationOptions often helps where 'settings' fails
  86. initializationOptions = {
  87. enabled = true,
  88. },
  89. -- Force absolute path in on_init to stop it from creating cspell.json
  90. on_init = function(client)
  91. local root = client.root_dir
  92. if root then
  93. local config_file
  94. if vim.fn.filereadable(root .. "/cspell.json") == 1 then
  95. config_file = root .. "/cspell.json"
  96. elseif vim.fn.filereadable(root .. "/cspell.config.yaml") == 1 then
  97. config_file = root .. "/cspell.config.yaml"
  98. elseif vim.fn.filereadable(root .. "/.cspell.json") == 1 then
  99. config_file = root .. "/.cspell.json"
  100. end
  101. if config_file then
  102. client.config.settings.cSpell.configFile = config_file
  103. client.notify('workspace/didChangeConfiguration', { settings = client.config.settings })
  104. end
  105. end
  106. return true
  107. end,
  108. -- Boost severity so we actually "see" the spell errors clearly (they default to Information)
  109. handlers = {
  110. ["textDocument/publishDiagnostics"] = function(err, result, ctx, config)
  111. if result and result.diagnostics then
  112. for _, diagnostic in ipairs(result.diagnostics) do
  113. if diagnostic.source == "cSpell" then
  114. -- Map to HINT so we can color it separately from code WARNings
  115. diagnostic.severity = vim.diagnostic.severity.HINT
  116. end
  117. end
  118. end
  119. vim.lsp.handlers["textDocument/publishDiagnostics"](err, result, ctx, config)
  120. end,
  121. },
  122. settings = {
  123. cSpell = {
  124. enabled = true,
  125. }
  126. },
  127. on_attach = function(client, bufnr)
  128. on_attach(client, bufnr)
  129. -- Visual confirmation that cspell is handling this buffer
  130. vim.notify("CSpell LSP Active (nospell)", vim.log.levels.INFO)
  131. vim.opt_local.spell = false
  132. end,
  133. })
  134. vim.lsp.enable('cspell')
  135. -- Custom Veridian setup (Verilog)
  136. vim.lsp.config('veridian', {
  137. cmd = { 'veridian' },
  138. filetypes = { 'systemverilog', 'verilog' },
  139. capabilities = capabilities,
  140. on_attach = on_attach,
  141. })
  142. vim.lsp.enable('veridian')
  143. -- Official JetBrains Kotlin LSP
  144. vim.lsp.config('kotlin_lsp', {
  145. cmd = { 'kotlin-lsp' },
  146. filetypes = { 'kotlin' },
  147. -- Tell the LSP to attach at the root of your Android Gradle project
  148. root_markers = { 'settings.gradle.kts', 'settings.gradle', 'build.gradle.kts', 'build.gradle' },
  149. capabilities = capabilities,
  150. on_attach = on_attach,
  151. -- Note: We drop the old fwcd/kotlin-language-server 'settings' block.
  152. -- The JetBrains server is much smarter and infers JVM targets, hints,
  153. -- and completions directly from IntelliJ's internal engine and your Gradle model.
  154. })
  155. vim.lsp.set_log_level("trace")
  156. vim.lsp.enable('kotlin_lsp')
  157. -- 4. Formatting
  158. local conform = require("conform")
  159. conform.setup({
  160. formatters_by_ft = {
  161. kotlin = { "ktlint" },
  162. python = { "black" },
  163. java = { "lsp" },
  164. sh = { "shfmt" },
  165. rust = { "rustfmt" },
  166. c = { "clang-format" },
  167. cpp = { "clang-format" },
  168. cmake = { "cmake_format" },
  169. },
  170. format_on_save = {
  171. timeout_ms = 2000,
  172. lsp_fallback = true,
  173. },
  174. })
  175. -- 5. Linting
  176. local lint = require("lint")
  177. lint.linters_by_ft = {
  178. python = { "pylint" },
  179. sh = { "shellcheck" },
  180. rust = { "clippy" },
  181. }
  182. vim.api.nvim_create_autocmd({ "BufWritePost", "BufEnter" }, {
  183. callback = function()
  184. lint.try_lint()
  185. end,
  186. })
  187. -- 6. Diagnostics config
  188. vim.diagnostic.config({
  189. virtual_text = true,
  190. signs = true,
  191. update_in_insert = false,
  192. underline = true,
  193. severity_sort = true,
  194. float = { border = 'rounded' },
  195. })
  196. -- 7. Diagnostic & Spell Highlighting
  197. vim.api.nvim_set_hl(0, 'DiagnosticUnderlineError', { undercurl = true, sp = '#ff0000' })
  198. vim.api.nvim_set_hl(0, 'DiagnosticUnderlineWarn', { undercurl = true, sp = '#ff8800' })
  199. vim.api.nvim_set_hl(0, 'DiagnosticUnderlineHint', { undercurl = true, sp = '#ffff00' })
  200. vim.api.nvim_set_hl(0, 'SpellBad', { undercurl = true, sp = '#ffff00' })
  201. vim.api.nvim_set_hl(0, 'SpellCap', { undercurl = true, sp = '#ffff00' })
  202. vim.api.nvim_set_hl(0, 'SpellLocal', { undercurl = true, sp = '#ffff00' })
  203. vim.api.nvim_set_hl(0, 'SpellRare', { undercurl = true, sp = '#ffff00' })