return { { "neovim/nvim-lspconfig", event = { "BufReadPre", "BufNewFile" }, dependencies = { "hrsh7th/cmp-nvim-lsp", "b0o/SchemaStore.nvim", { "j-hui/fidget.nvim", config = true, }, }, config = function() local lsp = require("lsp") local p = require("p") -- vim.lsp.set_log_level("debug") local function locations_equal(loc1, loc2) return (loc1.uri or loc1.targetUri) == (loc2.uri or loc2.targetUri) and (loc1.range or loc1.targetSelectionRange).start.line == (loc2.range or loc2.targetSelectionRange).start.line end local function location_handler(_, result, ctx, _) if result == nil or vim.tbl_isempty(result) then return nil end local client = vim.lsp.get_client_by_id(ctx.client_id) -- textDocument/definition can return Location or Location[] -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition local has_telescope = pcall(require, "telescope") if vim.tbl_islist(result) then if #result == 1 or (#result == 2 and locations_equal(result[1], result[2])) then pcall(vim.lsp.util.jump_to_location, result[1], client.offset_encoding, false) elseif has_telescope then local opts = {} local pickers = require("telescope.pickers") local finders = require("telescope.finders") local make_entry = require("telescope.make_entry") local conf = require("telescope.config").values local items = vim.lsp.util.locations_to_items(result, client.offset_encoding) pickers .new(opts, { prompt_title = "LSP Locations", finder = finders.new_table({ results = items, entry_maker = make_entry.gen_from_quickfix(opts), }), previewer = conf.qflist_previewer(opts), sorter = conf.generic_sorter(opts), }) :find() else vim.fn.setqflist({}, " ", { title = "LSP locations", items = vim.lsp.util.locations_to_items(result, client.offset_encoding), }) vim.cmd.copen({ mods = { split = "botright" } }) end else vim.lsp.util.jump_to_location(result, client.offset_encoding) end end vim.lsp.handlers["textDocument/declaration"] = location_handler vim.lsp.handlers["textDocument/definition"] = location_handler vim.lsp.handlers["textDocument/typeDefinition"] = location_handler vim.lsp.handlers["textDocument/implementation"] = location_handler 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" }) local diagnostics_handler = vim.lsp.handlers["textDocument/publishDiagnostics"] vim.lsp.handlers["textDocument/publishDiagnostics"] = function(err, result, context, config) local client = vim.lsp.get_client_by_id(context.client_id) if client.config.diagnostics ~= false then diagnostics_handler(err, result, context, config) end end vim.lsp.handlers["window/showMessage"] = function(_err, result, context, _config) local client_id = context.client_id local message_type = result.type local message = result.message local client = vim.lsp.get_client_by_id(client_id) local client_name = client and client.name or string.format("id=%d", client_id) if not client then vim.notify("LSP[" .. client_name .. "] client has shut down after sending the message", vim.log.levels.ERROR) end if message_type == vim.lsp.protocol.MessageType.Error then vim.notify("LSP[" .. client_name .. "] " .. message, vim.log.levels.ERROR) else local message_type_name = vim.lsp.protocol.MessageType[message_type] local map = { Error = vim.log.levels.ERROR, Warning = vim.log.levels.WARN, Info = vim.log.levels.INFO, Log = vim.log.levels.DEBUG, } -- The whole reason to override this handler is so that this uses vim.notify instead of -- vim.api.nvim_out_write vim.notify(string.format("LSP[%s] %s\n", client_name, message), map[message_type_name]) end return result end -- Configure the LSP servers local lspservers = { "bashls", "cssls", "eslint", "gdscript", "gopls", "html", "omnisharp", "pyright", "rust_analyzer", "vimls", "zls", } for _, server in ipairs(lspservers) do lsp.safe_setup(server) end lsp.safe_setup("yamlls", { settings = { yaml = { schemas = p.require("schemastore").json.schemas(), }, }, }) lsp.safe_setup("clangd", { filetypes = { "c", "cpp", "objc", "objcpp" }, }) lsp.safe_setup("jsonls", { filetypes = { "json", "jsonc", "json5" }, settings = { json = { schemas = p.require("schemastore").json.schemas(), }, }, }) lsp.safe_setup("flow", { root_dir = function(fname) local util = require("lspconfig.util") -- Disable flow when a typescript project is detected if util.root_pattern("tsconfig.json")(fname) then return nil end return util.root_pattern(".flowconfig")(fname) end, cmd = { "flow", "lsp", "--lazy" }, settings = { flow = { coverageSeverity = "warn", showUncovered = true, stopFlowOnExit = false, useBundledFlow = false, }, }, }) -- conflicts with work -- lspconfig.sorbet.setup({ -- capabilities = lsp.capabilities, -- cmd = { "bundle", "exec", "srb", "tc", "--lsp" }, -- }) local group = vim.api.nvim_create_augroup("LspSetup", {}) vim.api.nvim_create_autocmd("LspAttach", { desc = "My custom attach behavior", pattern = "*", group = group, callback = function(args) local client = vim.lsp.get_client_by_id(args.data.client_id) lsp.on_attach(client, args.buf) end, }) end, }, { "stevearc/flow-coverage.nvim", ft = { "javascript", "javascript.jsx", "javascriptreact" }, }, { "folke/neodev.nvim", opts = { experimental = { pathStrict = true } }, ft = "lua", dependencies = { "neovim/nvim-lspconfig" }, config = function(_, opts) require("neodev").setup(opts) local lsp = require("lsp") local sumneko_root_path = os.getenv("HOME") .. "/.local/share/nvim/language-servers/lua-language-server" local sumneko_binary = sumneko_root_path .. "/bin/lua-language-server" lsp.safe_setup("lua_ls", { cmd = { sumneko_binary, "-E", sumneko_root_path .. "/main.lua" }, settings = { Lua = { IntelliSense = { traceLocalSet = true, }, diagnostics = { globals = { "vim", "it", "describe", "before_each", "after_each", "a" }, }, telemetry = { enable = false, }, workspace = { checkThirdParty = false, }, }, }, }) end, }, { "pmizio/typescript-tools.nvim", ft = { "typescript", "typescriptreact", "javascript", "javascriptreact" }, dependencies = { "nvim-lua/plenary.nvim", "neovim/nvim-lspconfig" }, opts = { root_dir = function(fname) local util = require("lspconfig.util") -- Disable tsserver on js files when a flow project is detected if not string.match(fname, ".tsx?$") and util.root_pattern(".flowconfig")(fname) then return nil end local ts_root = util.root_pattern("tsconfig.json")(fname) or util.root_pattern("package.json", "jsconfig.json", ".git")(fname) if ts_root then return ts_root end if vim.g.started_by_firenvim then return util.path.dirname(fname) end return nil end, settings = { separate_diagnostic_server = false, tsserver_max_memory = 8 * 1024, }, }, }, -- { -- "mfussenegger/nvim-jdtls", -- ft = "java", -- dependencies = { "neovim/nvim-lspconfig" }, -- config = function() -- local aug = vim.api.nvim_create_augroup("Jdtls", {}) -- vim.api.nvim_create_autocmd("FileType", { -- desc = "Start JDTLS", -- pattern = "java", -- group = aug, -- callback = function(args) -- local local_share = uv.os_homedir() .. "/.local/share" -- local jdtls_root = local_share .. "/jdtls" -- local configuration -- if uv.os_uname().version:match("Windows") then -- configuration = jdtls_root .. "/config_win" -- elseif uv.os_uname().sysname == "Darwin" then -- configuration = jdtls_root .. "/config_mac" -- else -- configuration = jdtls_root .. "/config_linux" -- end -- if vim.fn.isdirectory(configuration) == 0 then -- return -- end -- -- local project_name = vim.fn.fnamemodify(vim.fn.getcwd(), ":p:h:t") -- local workspace_dir = vim.fn.stdpath("cache") .. "/jdtls/" .. project_name -- local launcher = vim.fn.glob(jdtls_root .. "/plugins/org.eclipse.equinox.launcher_*.jar") -- if launcher == "" then -- return -- end -- -- -- Look for a local install of java -- local java_cmd = vim.fn.glob(local_share .. "/java/Home/bin/java") -- if java_cmd == "" then -- java_cmd = "java" -- end -- if vim.fn.executable(java_cmd) == 0 then -- return -- end -- -- -- Possibly not supposed to include all of these jar files https://github.com/salesforce/bazel-vscode/blob/c7f5b7476a425b3f6481c9b23c1057d894c3ed33/package.json#L23 -- local extension_dirs = { local_share .. "/bazel-eclipse/plugins/*.jar" } -- local bundles = {} -- for _, glob in ipairs(extension_dirs) do -- vim.list_extend(bundles, vim.split(vim.fn.glob(glob), "\n")) -- end -- -- local config = { -- cmd = { -- java_cmd, -- "-Declipse.application=org.eclipse.jdt.ls.core.id1", -- "-Dosgi.bundles.defaultStartLevel=4", -- "-Declipse.product=org.eclipse.jdt.ls.core.product", -- "-Dlog.protocol=true", -- "-Dlog.level=ALL", -- "-Xmx4G", -- "-Xms4G", -- "--add-modules=ALL-SYSTEM", -- "--add-opens", -- "java.base/java.util=ALL-UNNAMED", -- "--add-opens", -- "java.base/java.lang=ALL-UNNAMED", -- "-jar", -- launcher, -- "-configuration", -- configuration, -- "-data", -- workspace_dir, -- }, -- -- root_dir = require("jdtls.setup").find_root({ ".git", "mvnw", "gradlew", ".bazelrc" }), -- -- -- See https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request -- -- for a list of options -- settings = require("projects")[args.buf].lsp_settings.jdtls or {}, -- -- init_options = { -- bundles = bundles, -- }, -- capabilities = require("lsp").capabilities, -- } -- -- vim.api.nvim_buf_call(args.buf, function() -- require("jdtls").start_or_attach(config) -- end) -- end, -- }) -- end, -- }, }