/quarry.nvim

Neovim plugin to simplify mason, mason-lspconfig and lspconfig configuration.

Primary LanguageLuaApache License 2.0Apache-2.0

GitHub Release LuaRocks Package License

quarry.nvim is a wrapper for mason, mason-lspconfig, and lspconfig to orgnize LSP setup for Neovim.

✨ Rationale

Having multiple LSP can easily bloat your single-file configuration. You still want to manage LSP yourself so you still understand what is going on. So you need a simple way to split your setup into multiple files.

What quarry.nvim will do for you

  • Composable LSP configuration
  • Lazy LSP installation only when required to keep your Neovim blazingly fast
  • Additional tool installation without fuzz, ex. DAP, linter, formatter (⚠️ configuration of those is still with you)
  • Configures minimal LSP capabilities and server setup

What will quarry.nvim not do for you

  • it is not a swiss army knife solution with with a one-line setup to automagically manage all your Neovim LSP, DAP, formatter, and linter needs.

Important

If you are not interested in managing your own configuration, then you better head over to none-ls or lsp-zero.

⚡️Requirements

📦 Installation

return {
    {
        "rudionrails/quarry.nvim",
        dependencies = {
            "williamboman/mason.nvim",
            "williamboman/mason-lspconfig.nvim",

            -- the default server config assumes that you use lspconfig. If this is not the case,
            -- you can omit this and override with your own implementaiotn (see below examples).
            -- quarry.nvim will gracefully handle if lspconfig is not available.
            "neovim/nvim-lspconfig"
        },
    }
}

⚙️ Configuration

Note

The below shows the default configuration and you do not need to provide this explicitly.

require("quarry").setup({
    ---
    -- Define the features to be enabled (if supported) when the LSP attaches
    --
    -- Possible values are:
    --   "textDocument/documentHighlight",
    --   "textDocument/inlayHint",
    --   "textDocument/codeLens",
    features = {},

    ---
    -- Define the keymaps to be set for the buffer when the LSP attaches. The
    -- syntax is similar to Lazy nvim.
    -- 
    -- Examples:
    --   ["[d"] = { vim.diagnostic.goto_prev },
    --   ["]d"] = { vim.diagnostic.goto_next },
    --   ["K"] = { vim.lsp.buf.hover, desc = "Show lsp hover" },
    keys = {}

    ---
    -- Will be passed when the LSP attaches. Alternatively, use `LspAttach` event.
    -- You can manually manage LSP features or keymaps in here as well.
    on_attach = function(client, bufnr) end,

    ---
    -- will be passed to every LSP. Can also be defined as Lua table, ex. `capabilities = {}`
    capabilities = function()
        return vim.tbl_deep_extend("force", {}, vim.lsp.protocol.make_client_capabilities())
    end,

    ---
    -- Provide globally required mason tools; will be installed upon `require("quarry").setup()`
    ensure_installed = {},

    ---
    -- Provide specific LSP configuration here. Every config can have the following shape:
    -- 	
    -- servers = {
    --   lua_ls = {
	--     -- Specify the filetypes when to install the tools, ex. filetypes = { "lua" }
	--     ---@type string[]
	--     filetypes = {},
	--     -- List of tools to install for the server, ex. ensure_installed = { "luacheck", "stylua" }
	--     ---@type string[]
	--     ensure_installed = {},
	--     -- The LSP-specific options, ex. opts = { settings = { telemetry = { enable = false } } }
	--     ---@type table<any, any>
	--     opts = {},
    --   }
    -- }
    servers = {},

    ---
    -- Provide LSP-specific handler functions or override the default. A setup handler with `_`
    -- as the key will be used as default if no LSP-specific one is defined.
    setup = {
        _ = function(name, opts)
            local ok, lspconfig = pcall(require, "lspconfig")
            if ok then
                lspconfig[name].setup(opts)
            end
        end
    }
})

🚀 Composable configuration (best enjoyed with lazy.nvim)

When you use many LSP, your configuration table may become quite large. You can take advantage of a lazy.nvim behaviour and separate the LSP into different files. lazy.nvim merges the opts in case a plugin is defined multiple times.

Tip

on_attach and capabilities are optional. For details see :h lspconfig-configurations inside Neovim. Both settings are totally optional.

Note

You can tweak the below example however you like. I found it most simple for the majority of purposes.

inside your Neovim configuration directory, you will have:

  • lua/plugins/lsp.lua as your base setup
  • lua/plugins/extras/lua.lua for Lua LSP specific configuration
  • lua/plugins/extras/typescript.lua for Typescript LSP specific configuration
  • ...extend with other LSP as you like
Setup quarry.nvim in lua/plugins/lsp.lua
-- file: lua/plugins/quarry.lua
return {
    "rudionrails/quarry.nvim",
    event = "VeryLazy",
    dependencies = {
        "williamboman/mason.nvim",
        "williamboman/mason-lspconfig.nvim",
        "neovim/nvim-lspconfig",

        -- not required by quarry.nvim, just to show how to extend capabilities
        "hrsh7th/cmp-nvim-lsp",

        ---
        -- This takes advantage of lazy.nvim loading mechanism and makes Lazy aware to
        -- load modules from within /lua/plugins/extras/*
        --
        -- Alternatively, you can add this to lua/init.lua:
        --  
        --   -- ... require lazy.nvim as you usually would. Check out the documentation for detailed instructions ...
        --   require("lazy").setup({
        --       { import = "plugins" },
        --       { import = "extras" }, -- <- this is the relevant line, BTW
        --   }, {
        --       -- .. regular lazy.nvim configuration ...
        --   })
        { import = "plugins.extras" },
    },
    opts = {
        features = {
            "textDocument/documentHighlight",
            "textDocument/inlayHint",
            -- "textDocument/codeLens",
        },

        keys = {
            ["[d"] = { vim.diagnostic.goto_prev },
            ["]d"] = { vim.diagnostic.goto_next },
            ["K"]  = { vim.lsp.buf.hover, desc = "Show lsp hover" },
            ["gD"] = { vim.lsp.buf.declaration, desc = "[G]oto [D]eclaration" },
            ["gs"] = { vim.lsp.buf.signature_help, desc = "[G]oto [s]ignature" },
            ["gd"] = { vim.lsp.buf.definition, desc = "[G]oto [d]efinition" },
            ["gr"] = { vim.lsp.buf.references, desc = "[G]oto [r]eferences" },
            ["gi"] = { vim.lsp.buf.implementation, desc = "[G]oto [i]mplementation" },
            ["gt"] = { vim.lsp.buf.type_definition, desc = "Goto [t]ype definition" },

            ["<leader>a"] = { vim.lsp.buf.code_action, desc = "Code [a]ction" },
            ["<leader>r"] = { vim.lsp.buf.rename, desc = "[R]ename word under cursor within project" },
            ["<leader>h"] = {
                function()
                    vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled())
                end,
                desc = "Toggle inlay [h]int",
            },

            -- vim.api.nvim_command('inoremap <C-space> <C-x><C-o>')
            ["<C-space>"] = { "<C-x><C-o>", mode = "i", remap = false },
        }

        on_attach = function(client, bufnr)
            -- Enable completion triggered by <c-x><c-o>
            vim.bo[bufnr].omnifunc = "v:lua.vim.lsp.omnifunc"
        end,

        capabilities = function()
            local cmp_nvim_lsp = require("hrsh7th/cmp-nvim-lsp")

            return vim.tbl_deep_extend(
                "force",
                {},
                vim.lsp.protocol.make_client_capabilities(),
                cmp_nvim_lsp.default_capabilities()
            )
        end,
    },
}
Setup lua-language-server in lua/plugins/extras/lua.lua
-- file: lua/plugins/extras/lua.lua
return {
    "rudionrails/quarry.nvim",
    opts = {
        servers = {
            lua_ls = {
                filetypes = { "lua" },
                ensure_installed = {
                    -- "lua_ls" itself will be automatically installed, since it is the key of the LSP
                    "stylua",
                    "luacheck",
                },
                opts = {
                    settings = {
                        Lua = {
                            completion = { callSnippet = "Replace" },
                            doc = { privateName = { "^_" } },
                            codeLens = { enable = true },
                            hint = {
                                enable = true,
                                setType = false,
                                paramType = true,
                                paramName = "Disable",
                                semicolon = "Disable",
                                arrayIndex = "Disable",
                            },
                            workspace = {
                                checkThirdParty = false,
                            },
                        },

                        -- Do not send telemetry data containing a randomized but unique identifier
                        telemetry = { enable = false },
                    },
                },
            },
        },
    },
}
Setup typescript-language-server in lua/plugins/extras/typescript.lua
-- file: lua/plugins/extras/typescript.lua
return {
    "rudionrails/quarry.nvim",
    opts = {
        servers = {
            tsserver = {
                filetypes = {
                    "javascript",
                    "javascriptreact",
                    "javascript.jsx",
                    "typescript",
                    "typescriptreact",
                    "typescript.tsx",
                },

                ensure_installed = {
                    -- "tsserver" itself will be automatically installed, since it is the key of the LSP
                    "prettier", -- prettierd as alternative
                    "eslint", -- eslint_d as alternative
                },

                opts = {
                    completions = { completeFunctionCalls = true },
                    init_options = {
                        preferences = {
                            includeInlayParameterNameHints = "all", -- 'none' | 'literals' | 'all';
                            includeInlayParameterNameHintsWhenArgumentMatchesName = false,
                            includeInlayFunctionParameterTypeHints = true,
                            includeInlayVariableTypeHints = true,
                            includeInlayPropertyDeclarationTypeHints = true,
                            includeInlayFunctionLikeReturnTypeHints = true,
                            includeInlayEnumMemberValueHints = true,
                            importModuleSpecifierPreference = "non-relative",
                        },
                    },
                },
            },
        },
    },
}

Similar projects

Development