22return {
33 {
44 " neovim/nvim-lspconfig" ,
5- lazy = false ,
65 config = function ()
7- local lspconfig = require (" lspconfig" )
8- local util = require (" lspconfig.util" )
9- local configs = require (" lspconfig.configs" )
10-
11- -- Recognize .templ files
12- vim .filetype .add ({ extension = { templ = " templ" } })
13-
14- -- ==============================
15- -- gopls
16- -- ==============================
17- lspconfig .gopls .setup ({
18- root_dir = function (fname )
19- return util .root_pattern (" go.work" , " go.mod" , " .git" )(fname )
20- or util .path .dirname (fname )
21- end ,
22- handlers = {
23- [" textDocument/signatureHelp" ] = function (err , result , ctx , config )
24- if err and err .message and err .message :find (" cannot get type" ) then
25- return
26- end
27- return vim .lsp .handlers [" textDocument/signatureHelp" ](err , result , ctx , config )
28- end ,
29- },
30- settings = {
31- gopls = {
32- gofumpt = true ,
33- codelenses = {
34- gc_details = false ,
35- generate = true ,
36- regenerate_cgo = true ,
37- run_govulncheck = true ,
38- test = true ,
39- tidy = true ,
40- upgrade_dependency = true ,
41- vendor = true ,
42- },
43- hints = {
44- assignVariableTypes = true ,
45- compositeLiteralFields = true ,
46- compositeLiteralTypes = true ,
47- constantValues = true ,
48- functionTypeParameters = true ,
49- parameterNames = true ,
50- rangeVariableTypes = true ,
51- },
52- analyses = {
53- fieldalignment = true ,
54- nilness = true ,
55- unusedparams = true ,
56- unusedwrite = true ,
57- useany = true ,
58- },
59- usePlaceholders = true ,
60- completeUnimported = true ,
61- staticcheck = true ,
62- directoryFilters = {
63- " -.git" ," -.vscode" ," -.idea" ," -.vscode-test" ," -node_modules" ,
64- " -dist" ," -build" ," -out" ," -coverage" ," -tmp" ," -.cache" ,
65- },
66- semanticTokens = true ,
67- memoryMode = " DegradeClosed" ,
68- symbolMatcher = " FastFuzzy" ,
69- [" ui.completion.experimentalPostfixCompletions" ] = false ,
70- },
71- },
72- })
73-
74- -- ==============================
75- -- TypeScript / JavaScript (ts_ls OR tsserver fallback)
76- -- ==============================
77- local ts_server = lspconfig .ts_ls or lspconfig .tsserver
78- if ts_server then
79- ts_server .setup ({})
80- end
81-
82- -- ==============================
83- -- Astro (guard if missing)
84- -- ==============================
85- if lspconfig .astro then
86- local function get_typescript_lib ()
87- local mason_ts = vim .fs .normalize (
88- " ~/.local/share/nvim/mason/packages/typescript-language-server/node_modules/typescript/lib"
89- )
90- if vim .fn .isdirectory (mason_ts ) == 1 then return mason_ts end
91-
92- local global_ts = (vim .fn .system (" npm root -g" ):gsub (" \n " , " " )) .. " /typescript/lib"
93- if vim .fn .isdirectory (global_ts ) == 1 then return global_ts end
94-
95- return vim .fs .normalize (
96- " ~/.local/share/nvim/mason/packages/astro-language-server/node_modules/typescript/lib"
97- )
98- end
99-
100- lspconfig .astro .setup ({
101- init_options = { typescript = { tsdk = get_typescript_lib () } },
102- })
103- end
104-
105- -- ==============================
106- -- templ (register config if missing)
107- -- ==============================
108- if not configs .templ then
109- configs .templ = {
110- default_config = {
111- cmd = { " templ" , " lsp" },
112- filetypes = { " templ" },
113- root_dir = util .root_pattern (" go.mod" , " .git" ),
114- single_file_support = true ,
115- },
116- }
117- end
118- lspconfig .templ .setup ({})
119-
120- -- ==============================
121- -- Utilities
122- -- ==============================
6+ ---- -----------------------------------------------------------------------
7+ -- LSP client utilities
8+ ---- -----------------------------------------------------------------------
1239 vim .api .nvim_create_user_command (" LspClients" , function ()
10+ -- new API: vim.lsp.get_clients
12411 local clients = vim .lsp .get_clients ()
125- local counts = {}
126- for _ , c in ipairs (clients ) do
127- counts [c .name ] = (counts [c .name ] or 0 ) + 1
12+ local client_counts = {}
13+
14+ for _ , client in ipairs (clients ) do
15+ client_counts [client .name ] = (client_counts [client .name ] or 0 ) + 1
12816 end
17+
12918 print (" === Active LSP Clients ===" )
130- for name , n in pairs (counts ) do
131- local dup = n > 1 and " ⚠️ DUPLICATE" or " ✅"
132- print (string.format (" %s: %d client(s)%s" , name , n , dup ))
19+ for name , count in pairs (client_counts ) do
20+ local status = count > 1 and " ⚠️ DUPLICATE" or " ✅"
21+ print (string.format (" %s: %d client(s)%s" , name , count , status ))
22+ end
23+
24+ if next (client_counts ) == nil then
25+ print (" No active LSP clients" )
13326 end
134- if next (counts ) == nil then print (" No active LSP clients" ) end
135- end , {})
27+ end , { desc = " Show active LSP clients and detect duplicates" })
13628
13729 vim .api .nvim_create_user_command (" LspKillDuplicates" , function ()
30+ -- only worry about duplicate gopls, since that’s your main concern
13831 local gopls_clients = vim .lsp .get_clients ({ name = " gopls" })
13932 if # gopls_clients <= 1 then
14033 print (" No duplicate gopls clients found" )
14134 return
14235 end
143- local keep , kill = nil , {}
144- for _ , c in ipairs (gopls_clients ) do
145- local cnt = 0
146- if c .config .settings and c .config .settings .gopls then
147- for _ in pairs (c .config .settings .gopls ) do cnt = cnt + 1 end
36+
37+ local client_to_keep = nil
38+ local clients_to_kill = {}
39+
40+ for _ , client in ipairs (gopls_clients ) do
41+ local settings_count = 0
42+ if client .config .settings and client .config .settings .gopls then
43+ for _ in pairs (client .config .settings .gopls ) do
44+ settings_count = settings_count + 1
45+ end
14846 end
149- if cnt > 0 and not keep then keep = c else table.insert (kill , c ) end
47+
48+ if settings_count > 0 and not client_to_keep then
49+ client_to_keep = client
50+ else
51+ table.insert (clients_to_kill , client )
52+ end
53+ end
54+
55+ for _ , client in ipairs (clients_to_kill ) do
56+ print (string.format (" Killing duplicate gopls client (id: %d)" , client .id ))
57+ client .stop (true )
15058 end
151- for _ , c in ipairs ( kill ) do
152- print (( " Killing duplicate gopls client (id: %d) " ): format ( c . id ))
153- c . stop ( true )
59+
60+ if client_to_keep then
61+ print ( string.format ( " Kept gopls client (id: %d) with settings " , client_to_keep . id ) )
15462 end
155- if keep then print ((" Kept gopls client (id: %d) with settings" ):format (keep .id )) end
156- end , {})
63+ end , { desc = " Kill duplicate gopls clients" })
15764
65+ ---- -----------------------------------------------------------------------
66+ -- Hover safety + keymaps
67+ ---- -----------------------------------------------------------------------
15868 local function has_hover (bufnr )
159- for _ , c in pairs (vim .lsp .get_active_clients ({ bufnr = bufnr })) do
160- if c .server_capabilities and c .server_capabilities .hoverProvider then return true end
69+ local clients = vim .lsp .get_clients ({ bufnr = bufnr })
70+ for _ , c in pairs (clients ) do
71+ if c .server_capabilities and c .server_capabilities .hoverProvider then
72+ return true
73+ end
16174 end
16275 return false
16376 end
@@ -172,11 +85,16 @@ return {
17285 vim .keymap .set (mode , lhs , rhs , { buffer = bufnr , desc = desc })
17386 end
17487
88+ -- K = hover (safe)
17589 buf_map (" n" , " K" , function ()
176- if not has_hover (bufnr ) then return end
90+ if not has_hover (bufnr ) then
91+ return
92+ end
17793 local ok , saga_hover = pcall (require , " lspsaga.hover" )
17894 if ok and saga_hover and saga_hover .render_hover_doc then
179- pcall (function () saga_hover :render_hover_doc () end )
95+ pcall (function ()
96+ saga_hover :render_hover_doc ()
97+ end )
18098 else
18199 pcall (vim .lsp .buf .hover )
182100 end
0 commit comments