Skip to content

Instantly share code, notes, and snippets.

@edr3x
Last active February 1, 2023 13:33
Show Gist options
  • Save edr3x/95f0e482ee6a704209a4b055e83835ec to your computer and use it in GitHub Desktop.
Save edr3x/95f0e482ee6a704209a4b055e83835ec to your computer and use it in GitHub Desktop.
Easy way to set up Neovim Native LSP

NVIM LSP setup

Easy Guide on Setting Native Neovim LSP with Lua

Prerequisites

  • Node.js & npm
  • package manager for nvim (recommended: Packer ) you can also use lazy or Vim-Plug

Install required packages

Using Packer

    use 'neovim/nvim-lspconfig'
    use 'williamboman/mason.nvim'
    use 'williamboman/mason-lspconfig.nvim'
    use 'hrsh7th/nvim-cmp'
    use 'hrsh7th/cmp-nvim-lsp'
    use 'hrsh7th/cmp-path'
    use 'L3MON4D3/LuaSnip'
    use 'saadparwaiz1/cmp_luasnip'
    use 'ray-x/lsp_signature.nvim'
    use 'RRethy/vim-illuminate'

Now create a file named lsp-handlers.lua inside lua/user/ direcotry and paste the following:

local M = {}

local status_cmp_ok, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp")
if not status_cmp_ok then
    vim.notify "cmp_nvim_lsp plugin not found"
    return
end

local signature_status, lsp_signature = pcall(require, "lsp_signature")
if not signature_status then
    vim.notify "lsp_signature plugin not found"
    return
end

local illuminate_status, illuminate = pcall(require, "illuminate")
if not illuminate_status then
    vim.notify "illuminate plugin not found"
    return
end

local opts = { noremap = true, silent = true }
local keymap = vim.api.nvim_buf_set_keymap

local lsp_keymaps = function(bufnr)
    keymap(bufnr, "n", "<leader>gd", "<cmd>lua vim.lsp.buf.declaration()<CR>", opts)
    keymap(bufnr, "n", "<leader>gD", "<cmd>lua vim.lsp.buf.definition()<CR>", opts)
    keymap(bufnr, "n", "<leader>gim", "<cmd>lua vim.lsp.buf.implementation()<CR>", opts)
    keymap(bufnr, "n", "<leader>gr", "<cmd>lua vim.lsp.buf.references()<CR>", opts)
    keymap(bufnr, "n", "<leader>ca", "<cmd>lua vim.lsp.buf.code_action()<CR>", opts)
    keymap(bufnr, "n", "<leader>r", "<cmd>lua vim.lsp.buf.rename()<CR>", opts)
    keymap(bufnr, "n", "<C-h>", "<cmd>lua vim.lsp.buf.signature_help()<CR>", opts)
    keymap(bufnr, "n", "K", "<cmd>lua vim.lsp.buf.hover()<CR>", opts)

    keymap(bufnr, "n", "<leader>e", "<cmd>lua vim.diagnostic.open_float()<CR>", opts)
    keymap(bufnr, "n", "<C-n>", "<cmd>lua vim.diagnostic.goto_next({buffer=0})<CR>", opts)
    keymap(bufnr, "n", "<C-p>", "<cmd>lua vim.diagnostic.goto_prev({buffer=0})<CR>", opts)
end

local signature_cfg = {
    hint_enable = false,
    floating_window = true,
    check_completion_visible = true,
    toggle_key = '<M-t>',
    select_signature_key = '<M-s>',
}

M.capabilities = cmp_nvim_lsp.default_capabilities()

M.on_attach = function(client, bufnr)
    if client.name == "tsserver" then
        client.server_capabilities.documentFormattingProvider = false -- comment this if you are not using null-ls for formatting;
    end

    client.server_capabilities.semanticTokensProvider = nil

    lsp_keymaps(bufnr)

    illuminate.on_attach(client)

    lsp_signature.on_attach(signature_cfg, bufnr)
end

return M
  • We could have put these inside our lsp file but this way we can reuse this file in language specific config also
  • Change the keymaps according to your liking
  • I have excluded formatting bind as i use format on save i.e.
vim.cmd [[autocmd BufWritePre * lua vim.lsp.buf.format()]] -- put this on your init.lua file

Create a folder lsp in lua/user/ directory

Create a file named nvim-lsp.lua in lua/user/lsp/ directory and paste the following

local mason_status, mason = pcall(require, "mason")
if not mason_status then
    return
end

local mason_lsp_status, masonlsp = pcall(require, "mason-lspconfig")
if not mason_lsp_status then
    return
end

local lsp_status, lspconf = pcall(require, "lspconfig")
if not lsp_status then
    return
end

local servers = {
    'sumneko_lua',
    'tsserver',
                 -- add other lsp server name here
}

mason.setup {
    ui = {
        border = "none",
        icons = {
            package_installed = "",
            package_pending = "",
            package_uninstalled = ""
        },
    },
    log_level = vim.log.levels.INFO,
    max_concurrent_installers = 4,
}

masonlsp.setup {
    ensure_installed = servers,
    automatic_installation = true,
}

for _, lsp in pairs(servers) do
    lspconf[lsp].setup {
        on_attach = require("user.lsp-handlers").on_attach,
        capabilities = require("user.lsp-handlers").capabilities,
        flags = {
            debounce_text_changes = 150,
        },
        settings = {
            Lua = {
                diagnostics = {
                    globals = { 'vim' }
                }
            }
        }
    }
end
  • You can see available language servers from here and add that inside servers table above

Create a file nvim-cmp.lua in lua/user/lsp/ and paste the following

local luaship_status, luasnip = pcall(require, "luasnip")
if not luaship_status then
    return
end

local cmp_status, cmp = pcall(require, "cmp")
if not cmp_status then
    return
end

local snippet_status, csnip = pcall(require, "luasnip/loaders/from_vscode")
if not snippet_status then
    return
end

local kind_icons = {
    Text = "",
    Method = "m",
    Function = "",
    Constructor = "",
    Field = "",
    Variable = "",
    Class = "",
    Interface = "",
    Module = "",
    Property = "",
    Unit = "",
    Value = "",
    Enum = "",
    Keyword = "",
    Snippet = "",
    Color = "",
    File = "",
    Reference = "",
    Folder = "",
    EnumMember = "",
    Constant = "",
    Struct = "",
    Event = "",
    Operator = "",
    TypeParameter = "",
}

local borderstyle = {
    border = { "", "", "", "", "", "", "", "" },
    winhighlight = "Normal:CmpPmenu,CursorLine:PmenuSel,Search:None",
}

cmp.setup {
    snippet = {
        expand = function(args)
            luasnip.lsp_expand(args.body)
        end,
    },
    mapping = cmp.mapping.preset.insert({
        ["<M-k>"] = cmp.mapping.select_prev_item(), -- select suggestion up
        ["<M-j>"] = cmp.mapping.select_next_item(), -- select suggestion down
        ["<M-i>"] = cmp.mapping(cmp.mapping.scroll_docs(-1), { "i", "c" }), -- scroll docs up
        ["<M-o>"] = cmp.mapping(cmp.mapping.scroll_docs(1), { "i", "c" }), -- scroll docs down
        ["<C-Space>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }),
        ["<C-y>"] = cmp.config.disable,
        ["<C-e>"] = cmp.mapping {
            i = cmp.mapping.abort(),
            c = cmp.mapping.close(),
        },
        ["<CR>"] = cmp.mapping.confirm { select = true },
    }),
    formatting = {
        fields = { "kind", "abbr", "menu" },
        format = function(entry, vim_item)
            vim_item.kind = string.format("%s", kind_icons[vim_item.kind])
            vim_item.menu = ({
                nvim_lsp = "[LSP]",
                luasnip = "[Snippet]",
                buffer = "[Buffer]",
                path = "[Path]",
            })[entry.source.name]
            return vim_item
        end,
    },
    sources = {
        { name = "nvim_lsp" },
        { name = "luasnip" },
        { name = "buffer" },
        { name = "path" },
        { name = "nvim_lua" },
    },
    confirm_opts = {
        behavior = cmp.ConfirmBehavior.Replace,
        select = false,
    },
    window = {
        completion = borderstyle,
        documentation = borderstyle,
    },
    experimental = {
        ghost_text = false,
        native_menu = false,
    },
}

csnip.lazy_load { paths = vim.fn.stdpath "config" .. "/snippets" }

Add the following to your init.lua file

require('user.lsp.nvim-lsp')
require('user.lsp.nvim-cmp')

Result Screenshots

CMP

cmp

Buff Hover

Hover

Snippet

snip

Signature Help

help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment