Vim for the VS Code User: Part 2 - Language Support
Tags: vim, neovim, vscode, programming
Published: 2023-07-03 06:02:29

[Last time](https://dev.to/aziznal/vim-for-the-vs-code-user-part-1-initial-setup-29la) I showed you how to setup vim with a basic config which you can extend to make vim exactly how you like it.

In this article, we'll setup **LSP** support, **Autocomplete**, and **Syntax Highlighting**.

## Before You Start

It's important to know what plugin is taking care of what responsibility so you can customize things to your own taste later. I'll explain each functionality and how it's achieved in an abstract way in this section.

To summarize, there are 3 independent concepts you should know about:

![3 concepts to know](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ok430g8a4tfb1kho60mu.png)

LSP is different from Syntax Highlighting is different from Autocomplete!

And to achieve the functionality we need, we'll use the following plugins:
- [lsp-zero](https://github.com/VonHeikemen/lsp-zero.nvim) (and its dependencies)
- [mason](https://github.com/williamboman/mason.nvim)
- [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig)
- [nvim-cmp](https://github.com/hrsh7th/nvim-cmp)
- [treesitter](https://github.com/nvim-treesitter/nvim-treesitter)

![Plugins we will use](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uhsp14xtnfddzjwr4rz0.png)

### Language Server Protocol

LSP stands for language server protocol. Here's what the LSP does:

- Autocomplete options (but not the menu itself)
- Diagnostics
- Code actions (refactor, extract function, etc.)
- Import management
- Other (depends on language & specific LSP)

### Syntax Highlighting

Syntax highlighting is how you get all the pretty colors in your code.

### Autocomplete

Gives you completion options based on where your cursor currently is and what you're typing with the help of your LSP.

With that out of the way, let's get to setting everything up.

## Setting Up Syntax Highlighting
Install `nvim-treesitter`. I'll do it with Packer ([refer to 1st article](https://dev.to/aziznal/vim-for-the-vs-code-user-part-1-initial-setup-29la)):

```lua
-- packer.lua
return require("packer").startup(
    function(use)

      -- ... other plugins ...

      use("nvim-treesitter/nvim-treesitter", {run = ":TSUpdate"})
    end
)
```
Then run `:so` followed by `:PackerSync` to install it.

Next, create a configuration for it using whatever method you like. I did it by creating a file under `~/.config/nvim/after/plugin/treesitter.lua`

```lua
require "nvim-treesitter.configs".setup {
    -- A list of parser names, or "all" (customize according to taste)
    ensure_installed = {"javascript", "typescript", "rust", "lua", "vim", "vimdoc", "query"},

    -- Install parsers synchronously (only applied to `ensure_installed`)
    sync_install = false,

    -- Automatically install missing parsers when entering buffer
    auto_install = true,

    highlight = {
        enable = true,

        -- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
        -- Using this option may slow down your editor, and you may see some duplicate highlights.
        -- Instead of true it can also be a list of languages
        additional_vim_regex_highlighting = false
    }
}
```

Above is my own personal configuration. You can mess around with yours to make it do whatever you like. [Check the docs for more info](https://github.com/nvim-treesitter/nvim-treesitter).

With that, you're done! Now this plugin will automatically install whatever's necessary to highlight your code without you needing to do a thing!


![nvim-treesitter automatically installs necessary parsers to highlight your code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szf3uishbf5bbtyb1kg1.gif)

## LSP Zero (Autocomplete & Language Support)
Add the following to your packer file:

```lua
use {
    "VonHeikemen/lsp-zero.nvim",
    branch = "v2.x",
    requires = {

        -- LSP Support
        {"neovim/nvim-lspconfig"}, -- Required

        {
            -- Optional
            "williamboman/mason.nvim",
            run = function()
                pcall(vim.cmd, "MasonUpdate")
            end
        },

        {"williamboman/mason-lspconfig.nvim"}, -- Optional

        -- Autocompletion
        {"hrsh7th/nvim-cmp"}, -- Required
        {"hrsh7th/cmp-nvim-lsp"}, -- Required
        {"L3MON4D3/LuaSnip"} -- Required
    }
}
```

Then run `:so` followed by `:PackerSync` to install all the added plugins.

If all goes well, you should be ready to setup your config.

I set mine up under `~/.config/nvim/after/lsp.lua` as the following:

```lua
local lsp = require("lsp-zero")

lsp.preset("recommended")

-- navigating completions in the autocomplete popup
local cmp = require("cmp")
local cmp_select = {behavior = cmp.SelectBehavior.Select}
local cmp_mappings =
    lsp.defaults.cmp_mappings(
    {
        -- prev item in menu
        ["<C-p>"] = cmp.mapping.select_prev_item(cmp_select),

        -- next item in menu
        ["<C-n>"] = cmp.mapping.select_next_item(cmp_select),

        -- confirm current selection
        ["<C-y>"] = cmp.mapping.confirm({select = true}),

        -- misc
        ["<C-Space>"] = cmp.mapping.complete()
    }
)

-- assigning previously defined mappings
lsp.setup_nvim_cmp(
    {
        mappings = cmp_mappings
    }
)

-- shortcuts for making use of LSPs
lsp.on_attach(
    function(client, bufnr)
        local opts = {buffer = bufnr, remap = false}

        -- go to definition of symbol under cursor
        vim.keymap.set(
            "n",
            "gd",
            function()
                vim.lsp.buf.definition()
            end,
            opts
        )

        -- shows info about current symbol as if you hovered on it
        -- with the cursor (really handy!)
        vim.keymap.set(
            "n",
            "K",
            function()
                vim.lsp.buf.hover()
            end,
            opts
        )

        -- lookup a workspace symbol
        vim.keymap.set(
            "n",
            "<leader>vws",
            function()
                vim.lsp.buf.workspace_symbol()
            end,
            opts
        )

        -- show diagnostic in a floating dialog (like when you hover on a 
        -- a line with a red squiggly under it)
        vim.keymap.set(
            "n",
            "<leader>vd",
            function()
                vim.diagnostic.open_float()
            end,
            opts
        )

        -- go to next diagnostic
        vim.keymap.set(
            "n",
            "[d",
            function()
                vim.diagnostic.goto_next()
            end,
            opts
        )

        -- go to prev diagnostic
        vim.keymap.set(
            "n",
            "]d",
            function()
                vim.diagnostic.goto_prev()
            end,
            opts
        )

        -- show code actions (refactor, inline function, etc..)
        vim.keymap.set(
            "n",
            "<leader>vca",
            function()
                vim.lsp.buf.code_action()
            end,
            opts
        )

        -- show references to symbol under cursor
        vim.keymap.set(
            "n",
            "<leader>vrr",
            function()
                vim.lsp.buf.references()
            end,
            opts
        )

        -- rename current symbol (works across files too!)
        vim.keymap.set(
            "n",
            "<leader>vrn",
            function()
                vim.lsp.buf.rename()
            end,
            opts
        )

        -- show help for current function when in insert mode (helps to see
        -- params of current function for example)
        vim.keymap.set(
            "i",
            "<C-h>",
            function()
                vim.lsp.buf.signature_help()
            end,
            opts
        )
    end
)

-- setup lua language server (with support for nvim)
require("lspconfig").lua_ls.setup(lsp.nvim_lua_ls())

lsp.setup()
```

The above configuration sets up everything you need for a base you can expand on. I've added a lot of annotations to make it easier to know what does what.

Whenever you add an LSP, autocomplete and everything else will "just work" requiring only minimal configuration most of the time.

For most of your needs, you should consult the [lspconfig server configurations page](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md).

Let's add the html and emmet LSPs as a demo.

**Step 1:** Install it with `LspInstall`. To do this, simply run the command `:LspInstall` from a file with the `.html` extension.

![html lsp install](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6n9l1wytza2rxky4l11l.gif)

We'll do the same to install support for emmet

![emmet lsp install](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5357yx94qmidmvish2hf.gif)

Let's do a quick check to see if that worked. Exit your current vim session and open it again so the LSPs are loaded.

Here's a demo of emmet with autocomplete:

![emmet autocomplete demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d4p9jrqqwhnpt50gr6qj.gif)

And here is what the html lsp gives us:

![html lsp demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72pwpctex6vebwwlacdf.gif)


**Step 2:** configure LSPs with lspconfig (if necessary)

For the emmet and html LSPs, this isn't necessary (thanks to lsp-zero). You can still do it if you want to customize things to your needs. Refer to the [server configurations page](https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md) in that case.

You can see an example at the bottom of [this file](https://github.com/aziznal/my-neovim-config/blob/main/after/plugin/lsp.lua) in my own neovim config.

What actually happened here though? Let's break it down:
- You ran the `:LspInstall` command which looked up what LSPs are associated with the file you have open
- You installed the html and emmet lsps suggested by LspInstall, which in turn installed them using Mason (it's the popup that shows after you select which lsp to install)

If you want to see what LSPs you have installed, you can run the `:Mason` command.

![Mason demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iotcy6ea496alktj3yi6.gif)

You can use some basic shortcuts to manage your LSPs from this menu, like `i` to install an available LSP, `X` to uninstall one, and `/` to search for one. You can exit this menu using `q`.

## Conclusion

We setup syntax highlighting with Nvim-Treesitter, and language support, including autocomplete and diagnostics, using lsp-zero, mason, and nvim-cmp.

Here's my own nvim config if you're interested (yes, I will shamelessly link to this every time 🌚):

- https://github.com/aziznal/my-neovim-config/