diff options
Diffstat (limited to 'common/nvim/lua/plugins/lsp.lua')
| -rwxr-xr-x | common/nvim/lua/plugins/lsp.lua | 674 |
1 files changed, 0 insertions, 674 deletions
diff --git a/common/nvim/lua/plugins/lsp.lua b/common/nvim/lua/plugins/lsp.lua deleted file mode 100755 index 5ed1152..0000000 --- a/common/nvim/lua/plugins/lsp.lua +++ /dev/null @@ -1,674 +0,0 @@ -local M = {} - --- Safe require helper -local function safe_require(name) - local ok, mod = pcall(require, name) - return ok and mod or nil -end - --- Autocmd groups for managing event listeners -local augroup_format = vim.api.nvim_create_augroup("LspFormattingOnSave", { clear = true }) -local augroup_diag_float = vim.api.nvim_create_augroup("ShowLineDiagnostics", { clear = true }) -local augroup_diag_load = vim.api.nvim_create_augroup("OpenDiagnosticsOnLoad", { clear = true }) -local augroup_highlight = vim.api.nvim_create_augroup("LspDocumentHighlight", { clear = true }) - --- Border for floating windows -local border = { - { "┌", "FloatBorder" }, { "─", "FloatBorder" }, { "┐", "FloatBorder" }, - { "│", "FloatBorder" }, { "┘", "FloatBorder" }, { "─", "FloatBorder" }, - { "└", "FloatBorder" }, { "│", "FloatBorder" } -} - --- Initialize LSP modules -local function init_modules() - -- Silently try to load each module - M.lspconfig = safe_require("lspconfig") - M.mason = safe_require("mason") - M.mason_lspconfig = safe_require("mason-lspconfig") - M.mason_tool_installer = safe_require("mason-tool-installer") - M.null_ls = safe_require("null-ls") - - if M.null_ls then - M.builtins = M.null_ls.builtins - end - - return true -end - --- Check Neovim version compatibility and feature availability -local function has_feature(feature) - if feature == "diagnostic_api" then - return vim.fn.has("nvim-0.6") == 1 - elseif feature == "native_lsp_config" then - -- Check for both vim.lsp.enable AND vim.lsp.config - return vim.fn.has("nvim-0.11") == 1 and vim.lsp.enable ~= nil - elseif feature == "lsp_get_client_by_id" then - return vim.fn.has("nvim-0.10") == 1 - elseif feature == "cmp_nvim_lsp" then - return pcall(require, "cmp_nvim_lsp") - elseif feature == "virtual_text_disabled_by_default" then - return vim.fn.has("nvim-0.11") == 1 - elseif feature == "deprecated_lsp_handlers" then - -- vim.lsp.handlers.hover and signature_help deprecated in 0.12, removed in 0.13 - return vim.fn.has("nvim-0.12") == 0 - elseif feature == "new_lsp_config_api" then - -- New LSP config API available from 0.12+ - return vim.fn.has("nvim-0.12") == 1 and vim.lsp.config ~= nil - end - return false -end - --- Backwards compatible capabilities setup -local function setup_capabilities() - local capabilities - - if has_feature("cmp_nvim_lsp") then - capabilities = require('cmp_nvim_lsp').default_capabilities() - elseif vim.lsp.protocol and vim.lsp.protocol.make_client_capabilities then - capabilities = vim.lsp.protocol.make_client_capabilities() - else - capabilities = {} - end - - -- Add snippet support if available - if capabilities.textDocument then - capabilities.textDocument.completion = capabilities.textDocument.completion or {} - capabilities.textDocument.completion.completionItem = - capabilities.textDocument.completion.completionItem or {} - capabilities.textDocument.completion.completionItem.snippetSupport = true - end - - -- Set offset encoding for newer versions (0.11+ supports utf-8 and utf-32) - if vim.fn.has("nvim-0.11") == 1 then - capabilities.offsetEncoding = { "utf-8", "utf-32", "utf-16" } - elseif vim.fn.has("nvim-0.9") == 1 then - capabilities.offsetEncoding = { "utf-8", "utf-16" } - end - - return capabilities -end - --- Default LSP keymaps (fallback if external not available) -local function setup_fallback_keymaps(bufnr) - -- Only set up minimal fallbacks, prefer external setup - local opts = { buffer = bufnr, silent = true, noremap = true } - vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts) - vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts) - vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts) - vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts) -end - --- Create LSP directory and config files for native LSP -local function setup_native_lsp_configs() - local config_path = vim.fn.stdpath("config") - local lsp_dir = config_path .. "/lsp" - - -- Create lsp directory if it doesn't exist - vim.fn.mkdir(lsp_dir, "p") - - -- LSP server configurations for native config - local server_configs = { - lua_ls = { - cmd = { "lua-language-server" }, - filetypes = { "lua" }, - root_markers = { ".luarc.json", ".luarc.jsonc", ".luacheckrc", ".stylua.toml", "stylua.toml", "selene.toml", "selene.yml" }, - settings = { - Lua = { - diagnostics = { - globals = { "vim", "use", "_G", "packer_plugins", "P" }, - disable = { - "undefined-global", - "lowercase-global", - "unused-local", - "unused-vararg", - "trailing-space" - }, - }, - workspace = { - library = { - vim.env.VIMRUNTIME, - "${3rd}/luv/library", - "${3rd}/busted/library", - }, - checkThirdParty = false, - }, - telemetry = { - enable = false, - }, - }, - }, - }, - - pyright = { - cmd = { "pyright-langserver", "--stdio" }, - filetypes = { "python" }, - root_markers = { "pyproject.toml", "setup.py", "setup.cfg", "requirements.txt", "Pipfile", "pyrightconfig.json" }, - settings = { - python = { - formatting = { - provider = "none" - } - } - } - }, - - ts_ls = { - cmd = { "typescript-language-server", "--stdio" }, - filetypes = { "javascript", "javascriptreact", "javascript.jsx", "typescript", "typescriptreact", "typescript.tsx" }, - root_markers = { "tsconfig.json", "jsconfig.json", "package.json" }, - init_options = { - disableAutomaticTypeAcquisition = true - }, - }, - - rust_analyzer = { - cmd = { "rust-analyzer" }, - filetypes = { "rust" }, - root_markers = { "Cargo.toml", "rust-project.json" }, - }, - - clangd = { - cmd = { "clangd", "--background-index", "--clang-tidy", "--header-insertion=iwyu" }, - filetypes = { "c", "cpp", "objc", "objcpp", "cuda", "proto" }, - root_markers = { ".clangd", ".clang-tidy", ".clang-format", "compile_commands.json", "compile_flags.txt", "configure.ac" }, - }, - - gopls = { - cmd = { "gopls" }, - filetypes = { "go", "gomod", "gowork", "gotmpl" }, - root_markers = { "go.work", "go.mod" }, - settings = { - gopls = { - gofumpt = true, - codelenses = { - gc_details = false, - generate = true, - regenerate_cgo = true, - run_govulncheck = true, - test = true, - tidy = true, - upgrade_dependency = true, - vendor = true, - }, - hints = { - assignVariableTypes = true, - compositeLiteralFields = true, - compositeLiteralTypes = true, - constantValues = true, - functionTypeParameters = true, - parameterNames = true, - rangeVariableTypes = true, - }, - analyses = { - fieldalignment = true, - nilness = true, - unusedparams = true, - unusedwrite = true, - useany = true, - }, - usePlaceholders = true, - completeUnimported = true, - staticcheck = true, - directoryFilters = { "-.git", "-.vscode", "-.idea", "-.vscode-test", "-node_modules" }, - semanticTokens = true, - }, - }, - }, - - -- Add more basic configs - bashls = { - cmd = { "bash-language-server", "start" }, - filetypes = { "sh", "bash" }, - }, - - --html = { - -- cmd = { "vscode-html-language-server", "--stdio" }, - -- filetypes = { "html" }, - --}, - - --cssls = { - -- cmd = { "vscode-css-language-server", "--stdio" }, - -- filetypes = { "css", "scss", "less" }, - --}, - - --jsonls = { - -- cmd = { "vscode-json-language-server", "--stdio" }, - -- filetypes = { "json", "jsonc" }, - --}, - - yamlls = { - cmd = { "yaml-language-server", "--stdio" }, - filetypes = { "yaml", "yml" }, - }, - } - - -- Write config files to lsp directory - for server_name, config in pairs(server_configs) do - local file_path = lsp_dir .. "/" .. server_name .. ".lua" - local file_content = "return " .. vim.inspect(config) - - -- Only write if file doesn't exist to avoid overwriting user customizations - if vim.fn.filereadable(file_path) == 0 then - local file = io.open(file_path, "w") - if file then - file:write(file_content) - file:close() - vim.notify("Created LSP config: " .. file_path, vim.log.levels.DEBUG) - end - end - end - - return vim.tbl_keys(server_configs) -end - --- Set up LSP on_attach function -local function create_on_attach() - return function(client, bufnr) - -- Your existing keymap setup function from keys.lua - if _G.setup_lsp_keymaps then - _G.setup_lsp_keymaps(bufnr) - else - setup_fallback_keymaps(bufnr) - end - - -- Disable LSP formatting in favor of null-ls (if null-ls is available) - if M.null_ls then - client.server_capabilities.documentFormattingProvider = false - client.server_capabilities.documentRangeFormattingProvider = false - end - - -- Disable specific LSP capabilities to avoid conflicts - if client.name == "ruff" then - -- Disable ruff hover in favor of Pyright - client.server_capabilities.hoverProvider = false - elseif client.name == "ts_ls" then - -- Disable ts_ls formatting in favor of prettier via null-ls - client.server_capabilities.documentFormattingProvider = false - client.server_capabilities.documentRangeFormattingProvider = false - elseif client.name == "pyright" and M.null_ls then - -- Disable pyright formatting in favor of black/isort via null-ls - client.server_capabilities.documentFormattingProvider = false - client.server_capabilities.documentRangeFormattingProvider = false - end - - -- Set log level (backwards compatible) - if vim.lsp.set_log_level then - vim.lsp.set_log_level("warn") - end - - -- Document highlight on cursor hold - if client.server_capabilities and client.server_capabilities.documentHighlightProvider then - vim.api.nvim_create_autocmd("CursorHold", { - group = augroup_highlight, - buffer = bufnr, - callback = function() - if vim.lsp.buf.document_highlight then - vim.lsp.buf.document_highlight() - end - end, - }) - vim.api.nvim_create_autocmd("CursorMoved", { - group = augroup_highlight, - buffer = bufnr, - callback = function() - if vim.lsp.buf.clear_references then - vim.lsp.buf.clear_references() - end - end, - }) - end - end -end - --- Set up basic LSP configuration -function M.setup() - -- Initialize all required modules - init_modules() - - -- Enable virtual_text diagnostics by default for 0.11+ (since it's disabled by default) - if has_feature("virtual_text_disabled_by_default") then - vim.diagnostic.config({ virtual_text = true }) - end - - -- Set up Mason if available (useful for tool management) - if M.mason then - M.mason.setup({ - ui = { - border = 'rounded', - icons = { - package_installed = '✓', - package_pending = '➜', - package_uninstalled = '✗' - } - } - }) - end - - -- Set up mason-tool-installer if available - if M.mason_tool_installer then - M.mason_tool_installer.setup({ - ensure_installed = { - -- Language servers - "lua-language-server", "pyright", "typescript-language-server", "rust-analyzer", - "clangd", "bash-language-server", "yaml-language-server", - -- Formatters - "stylua", "clang-format", "prettier", "shfmt", "black", "isort", "goimports", - "sql-formatter", "shellharden", - -- Linters/Diagnostics - "eslint_d", "selene", "flake8", "dotenv-linter", "phpcs", - -- Utilities - "jq" - }, - auto_update = false, - run_on_start = true, - start_delay = 3000, - }) - end - - -- Set up null-ls if available - if M.null_ls and M.builtins then - local sources = { - M.builtins.diagnostics.selene.with({ - condition = function(utils) - return utils.root_has_file({"selene.toml"}) - end, - }), - M.builtins.diagnostics.dotenv_linter, - M.builtins.diagnostics.tidy, - M.builtins.diagnostics.phpcs.with({ - condition = function(utils) - return utils.root_has_file({"phpcs.xml", "phpcs.xml.dist", ".phpcs.xml", ".phpcs.xml.dist"}) - end, - }), - - -- Formatters (prioritized over LSP formatting) - M.builtins.formatting.stylua.with({ - extra_args = { "--quote-style", "AutoPreferSingle", "--indent-width", "2", "--column-width", "160" }, - condition = function(utils) - return utils.root_has_file({"stylua.toml", ".stylua.toml"}) - end, - }), - M.builtins.formatting.prettier.with({ - extra_args = { "--single-quote", "--tab-width", "4", "--print-width", "100" }, - filetypes = { "javascript", "javascriptreact", "typescript", "typescriptreact", "vue", "css", "scss", "less", "html", "json", "jsonc", "yaml", "markdown", "graphql", "handlebars" }, - prefer_local = "node_modules/.bin", - }), - M.builtins.formatting.black.with({ - extra_args = { "--fast" }, - prefer_local = ".venv/bin", - }), - M.builtins.formatting.isort.with({ - extra_args = { "--profile", "black" }, - prefer_local = ".venv/bin", - }), - M.builtins.formatting.goimports, - M.builtins.formatting.clang_format.with({ - extra_args = { "--style", "{BasedOnStyle: Google, IndentWidth: 4}" } - }), - M.builtins.formatting.shfmt.with({ - extra_args = { "-i", "2", "-ci" } - }), - M.builtins.formatting.shellharden, - M.builtins.formatting.sql_formatter, - M.builtins.formatting.dart_format, - - -- Code actions - M.builtins.code_actions.gitsigns, - M.builtins.code_actions.gitrebase, - } - - M.null_ls.setup({ - sources = sources, - update_in_insert = false, - on_attach = function(client, bufnr) - -- Disable LSP formatting in favor of null-ls - client.server_capabilities.documentFormattingProvider = false - client.server_capabilities.documentRangeFormattingProvider = false - - local function lsp_supports_method(client, method) - if client.supports_method then - return client:supports_method(method) - elseif client.server_capabilities then - local capability_map = { - ["textDocument/formatting"] = "documentFormattingProvider", - ["textDocument/rangeFormatting"] = "documentRangeFormattingProvider", - ["textDocument/hover"] = "hoverProvider", - ["textDocument/signatureHelp"] = "signatureHelpProvider", - ["textDocument/documentHighlight"] = "documentHighlightProvider", - } - local cap = capability_map[method] - return cap and client.server_capabilities[cap] - end - return false - end - - if lsp_supports_method(client, "textDocument/formatting") then - vim.api.nvim_create_autocmd("BufWritePre", { - group = augroup_format, - buffer = bufnr, - callback = function() - if vim.fn.has("nvim-0.8") == 1 then - vim.lsp.buf.format({ - async = false, - bufnr = bufnr, - filter = function(formatting_client) - return formatting_client.name == "null-ls" - end, - }) - else - vim.lsp.buf.formatting_sync() - end - end, - }) - end - end, - }) - end - - -- Set up LSP capabilities - local capabilities = setup_capabilities() - local on_attach = create_on_attach() - - -- Set up LSP handlers with version compatibility (avoid deprecated APIs) - if has_feature("deprecated_lsp_handlers") then - -- Use old handler setup for versions before 0.12 - if vim.lsp.handlers then - vim.lsp.handlers['textDocument/hover'] = vim.lsp.with( - vim.lsp.handlers.hover, { border = 'rounded' } - ) - - vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with( - vim.lsp.handlers.signature_help, { border = 'rounded' } - ) - end - else - -- Use new handler setup for 0.12+ (when old handlers are deprecated/removed) - if vim.lsp.handlers then - vim.lsp.handlers['textDocument/hover'] = vim.lsp.with( - vim.lsp.handlers['textDocument/hover'], { border = 'rounded' } - ) - - vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with( - vim.lsp.handlers['textDocument/signatureHelp'], { border = 'rounded' } - ) - end - end - - -- Choose configuration method based on Neovim version and available features - if has_feature("native_lsp_config") then - -- Set up native LSP configuration - local servers = setup_native_lsp_configs() - - -- Set default on_attach and capabilities for all LSP servers - vim.lsp.config('*', { - on_attach = on_attach, - capabilities = capabilities, - }) - - -- Enable the LSP servers - vim.lsp.enable(servers) - - elseif M.mason_lspconfig and M.lspconfig then - -- Set up mason-lspconfig if available - if M.mason_lspconfig then - M.mason_lspconfig.setup({ - ensure_installed = { - "lua_ls", "pyright", "ts_ls", "rust_analyzer", "clangd", "gopls", - "bashls", "html", "cssls", "jsonls", "yamlls" - }, - automatic_installation = true, - }) - end - - -- Use traditional lspconfig with mason - local enabled_servers = {} - - local server_configs = { - lua_ls = { - settings = { - Lua = { - diagnostics = { - globals = { "vim", "use", "_G", "packer_plugins", "P" }, - }, - workspace = { - library = { - vim.env.VIMRUNTIME, - "${3rd}/luv/library", - "${3rd}/busted/library", - }, - checkThirdParty = false, - }, - telemetry = { enable = false }, - }, - }, - }, - pyright = { - settings = { - python = { - formatting = { provider = "none" } - } - } - }, - ts_ls = { - init_options = { - disableAutomaticTypeAcquisition = true - }, - }, - clangd = { - cmd = { "clangd", "--background-index", "--clang-tidy", "--header-insertion=iwyu" }, - }, - gopls = { - settings = { - gopls = { - gofumpt = true, - usePlaceholders = true, - completeUnimported = true, - staticcheck = true, - }, - }, - }, - } - - M.mason_lspconfig.setup_handlers({ - function(server_name) - if not enabled_servers[server_name] then - local config = server_configs[server_name] or {} - config.on_attach = on_attach - config.capabilities = capabilities - M.lspconfig[server_name].setup(config) - enabled_servers[server_name] = true - end - end, - }) - - elseif M.lspconfig then - -- Fallback: Set up servers manually if mason-lspconfig is not available - local servers = { 'lua_ls', 'pyright', 'ts_ls', 'rust_analyzer', 'clangd', 'gopls', 'bashls', 'html', 'cssls', 'jsonls', 'yamlls' } - local enabled_servers = {} - - for _, server in ipairs(servers) do - if not enabled_servers[server] and M.lspconfig[server] then - local config = { - on_attach = on_attach, - capabilities = capabilities, - } - M.lspconfig[server].setup(config) - enabled_servers[server] = true - end - end - end - - return true -end - --- Global toggle for diagnostics (backwards compatible) -vim.g.diagnostics_visible = true -function _G.toggle_diagnostics() - if has_feature("diagnostic_api") then - if vim.g.diagnostics_visible then - vim.g.diagnostics_visible = false - vim.diagnostic.disable() - else - vim.g.diagnostics_visible = true - vim.diagnostic.enable() - end - else - -- Fallback for older versions - if vim.g.diagnostics_visible then - vim.g.diagnostics_visible = false - vim.lsp.handlers["textDocument/publishDiagnostics"] = function() end - else - vim.g.diagnostics_visible = true - vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with( - vim.lsp.diagnostic.on_publish_diagnostics, {} - ) - end - end -end - --- Create Mason command if Mason is available -if M.mason then - vim.api.nvim_create_user_command("Mason", function() - require("mason.ui").open() - end, {}) -end - --- Automatically show diagnostics in a float window for the current line -if has_feature("diagnostic_api") then - vim.api.nvim_create_autocmd("CursorHold", { - group = augroup_diag_float, - pattern = "*", - callback = function() - local opts = { - focusable = false, - close_events = { "BufLeave", "CursorMoved", "InsertEnter", "FocusLost" }, - border = border, - source = "always", - prefix = " ", - scope = "cursor", - } - vim.diagnostic.open_float(nil, opts) - end, - }) - - -- Autocmd to open the diagnostic window when a file with errors is opened - vim.api.nvim_create_autocmd({ "LspAttach", "BufReadPost" }, { - group = augroup_diag_load, - callback = function() - local has_errors = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.ERROR }) > 0 - if has_errors then - vim.diagnostic.setqflist({ - open = true, - title = "Diagnostics", - }) - end - end, - }) -end - --- Create Toggle Diagnostic command -vim.api.nvim_create_user_command("ToggleDiagnostics", _G.toggle_diagnostics, { - desc = "Toggle global diagnostics visibility" -}) - -return M |
