Neovim config from scratch (Part II)
Tags: neovim, config, lsp, lua
Published: 2023-01-11 17:57:04

This is a continuation from [Neovim config from scratch (Part I)](https://dev.to/oinak/neovim-config-from-scratch-part-i-3o2m).

There I covered:

- Nuke from orbit (starting from a clean slate)
- Create the config folder 
- Plugin manager 
- Fuzzy finder 
- Colorscheme 
- Treesitter
- Additional plugins 
- Set options 
- Mappings

I you came here loking for any of those, please visit the link.

# Language Support

I used to base my language support on CTags. But due to neovim's native support of LSP I am trying that too.

## CTags
**Requirements:** 
You need to have a CTags implementation like [universal-ctags](https://ctags.io/) installed on your system (on every system where you use vim).

  - For Linux distros it is commonly available as a package, see: [debian](https://packages.debian.org/stable/universal-ctags), [ubuntu](https://packages.ubuntu.com/focal/universal-ctags)
  - For OSX it is available via [homebrew](https://github.com/universal-ctags/homebrew-universal-ctags)

### Configs
Tell nvim where to find your tag file, the default is `tags`
```lua
-- (Exuberant/Universal) Ctags
vim.opt.tags = '.tags'
```
Key mapping to refresh your tags
```lua
function RTags()
  vim.cmd [[!ctags -f .tags --languages=ruby --exclude=.git -R .]]
end

vim.keymap.set("n", "rT", RTags)
```
You can automate this via githooks, if you work usually on the same repo(s).

On top of this you can use vim's default mappings for tags operations:

**`g]` go to definition **

Example, you type `g]` while having your cursor on the word `initialize` on line 41, and you see this list. `<number>+<enter>` takes you to each result.

![nvim window showing several results for solution tag](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xltbse0s0ifl5rcpumlc.png)

`C-]` (control+']') will jump to the first result without showing the list

You can run `:help tags` to learn about all of them.

However, if you are using telescope, you can add this to your `.config/nvim/lua/after/plugin/telescope.lua` (or equivalent) file:
```lua
local builtin = require('telescope.builtin')
-- ...
vim.keymap.set('n', 'gt', builtin.tags,            { desc = '[G]o to C[T]ags (telescope)', noremap = true })
```
And then if you press `gt` and then method or class name like `initialize` here, you would see something like:

![telescope window searching for initialize tag](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3f4itev84jhww0yl64rm.png).

## LSP
Go to [LSP-Zero](https://github.com/VonHeikemen/lsp-zero.nvim#installing) page, Istalling section and copy the **packer** block.

Paste it under the last `use` block, into your `.config/nvim/lua/core/plugins.lua` file that we created in [Part I](https://dev.to/oinak/neovim-config-from-scratch-part-i-3o2m#plugin-manager).

Save `:w`, source `:so %` and run `:PackerSync`.

Then you can create an `.config/nvim/after/plugin/lsp.lua` to add your custom config for LSP.

Mine is 
```lua
vim.opt.signcolumn = 'yes' -- Reserve space for diagnostic icons

local lsp = require('lsp-zero')

lsp.preset('recommended')

lsp.ensure_installed({
  'html',
  'cssls',
  'tsserver',
  'solargraph',
  'eslint',
  'sumneko_lua',
})

-- keymaps to add only on buffers with LSP support (overriding grep/tags based defaults)
lsp.on_attach(function(client, bufnr)
  vim.keymap.set("n", "gd", function() vim.lsp.buf.definition() end,     { desc = '[G]o to [d]efinition',buffer = bufnr, remap = false }))
  vim.keymap.set("n", "gD", function() vim.lsp.buf.declaration() end,    { desc = '[G]o to [D]eclaration',buffer = bufnr, remap = false }))
  vim.keymap.set("n", "gr", function() vim.lsp.buf.references() end,     { desc = '[G]o to [R]eferences (with LSP)', buffer = bufnr, remap = false })
  vim.keymap.set("n", "gi", function() vim.lsp.buf.implementation() end, { desc = '[G]o to [I]mplementation (with LSP)', buffer = bufnr, remap = false })
  vim.keymap.set("n", "K",  function() vim.lsp.buf.hover() end,          { desc = '[K] Hover info', buffer = bufnr, remap = false })
  vim.keymap.set("n", "R",  function() vim.lsp.buf.rename() end,         { desc = '[R]ename symbol (with LSP)', buffer = bufnr, remap = false })
end)

  -- Set up lspconfig.
  local capabilities = require('cmp_nvim_lsp').default_capabilities()
  -- Replace <YOUR_LSP_SERVER> with each lsp server you've enabled.
  require('lspconfig')['solargraph'].setup  { capabilities = capabilities }
  require('lspconfig')['html'].setup        { capabilities = capabilities }
  require('lspconfig')['cssls'].setup       { capabilities = capabilities }
  require('lspconfig')['tsserver'].setup    { capabilities = capabilities }
  require('lspconfig')['eslint'].setup      { capabilities = capabilities }
  require('lspconfig')['sumneko_lua'].setup { capabilities = capabilities }

lsp.setup()
```
Basically it selects which language servers are installed by default, sets custom keybinding only when LSP is acive (via the on_attach hook) and then sets up the servers.

LSP will auto-install your selected servers with mason, and propose you additional servers when you open a file of a new type.

You can look at what servers are installed with `:Mason`.

## LSP diagnostics

I have finally chose to use [trouble](https://github.com/TaffyQuinzel/trouble.vim) to see and jump to LSP diagnostic output in a non obstrusive way (I tried floating windows and inline messages but they were not for me)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/biacsr6z95nr3mhe93ij.png)

Just the default installation as suggested by the Readme page works for me.

## Ruby/Rails/Tests language support

I mainly develop in ruby so here are some plugins i have find very useful to add:

- [vim-ruby](https://github.com/vim-ruby/vim-ruby) language support
- [vim-rails](https://github.com/tpope/vim-rails) framework support with enhanced file navigation `gf` and custom `:Emodel`, `:Eview` etc commands, `:Rails` to run rails/rake commands without leavinf your editor.
- [vim-test](https://github.com/vim-test/vim-test) run your test with a simple mapping. Works with rSpec and Minitest (and dozens other languages)

```lua
-- make test commands execute using dispatch.vim
vim.g['test#strategy'] = "neovim"

vim.keymap.set("n", "<leader>tn", vim.cmd.TestNearest, { desc = '[T]est [N]earest' })
vim.keymap.set("n", "<leader>tf", vim.cmd.TestFile, { desc = '[T]est [F]ile' })
vim.keymap.set("n", "<leader>ts", vim.cmd.TestSuite, { desc = '[T]est [S]uite' })
vim.keymap.set("n", "<leader>tl", vim.cmd.TestLast, { desc = '[T]est [L]ast' })
vim.keymap.set("n", "<leader>tv", vim.cmd.TestVisit, { desc = '[T]est [V]isit' })
```

![vim-test plugin usage demo calling tests from the editor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cjfr8gsrs2zd0rh0za7w.gif)

- [splitjoin](https://github.com/AndrewRadev/splitjoin.vim) to alternate between abbreviated and expansive syntaxes for hashes, arrays, method calls...
- [vim-commentary](https://github.com/tpope/vim-commentary) comment code comfortably with motions and visual selections alike.
- [vim-surround](https://github.com/tpope/vim-surround) act on surrounding characters (think quotes, braces, parentheses...)

- [vim-textobj-ruby](https://github.com/rhysd/vim-textobj-ruby) use `r` as a ruby motion (like `w` for word) meaning "ruby block"

## Afterword

That's about it for my current configuration, I hope it was useful or at least interesting for you.

Please let me know if there is anything else you are struggling with. I might have com upon it after writing this piece.