Skip to content

Under the hood

I realize some people would want to know what happens when they use the recommended configuration.

Okay, so this thing.

lua
local lsp_zero = require('lsp-zero')

lsp_zero.on_attach(function(client, bufnr)
  lsp_zero.default_keymaps({buffer = bufnr})
end)

require('mason').setup({})
require('mason-lspconfig').setup({
  handlers = {
    function(server_name)
      require('lspconfig')[server_name].setup({})
    end,
  },
})

Will turn into something very close to this.

lua
---
-- LSP Support
---

local lspconfig = require('lspconfig')

lspconfig.util.on_setup = lspconfig.util.add_hook_after(
  lspconfig.util.on_setup,
  function(config, user_config)
    config.capabilities = vim.tbl_deep_extend(
      'force',
      config.capabilities,
      require('cmp_nvim_lsp').default_capabilities(),
      vim.tbl_get(user_config, 'capabilities') or {}
    )
  end
)

local function lsp_settings()
  vim.diagnostic.config({
    float = {border = 'rounded'},
  })

  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 command = vim.api.nvim_create_user_command

  command('LspWorkspaceAdd', function()
    vim.lsp.buf.add_workspace_folder()
  end, {desc = 'Add folder to workspace'})

  command('LspWorkspaceList', function()
    vim.notify(vim.inspect(vim.lsp.buf.list_workspace_folders()))
  end, {desc = 'List workspace folders'})

  command('LspWorkspaceRemove', function()
    vim.lsp.buf.remove_workspace_folder()
  end, {desc = 'Remove folder from workspace'})
end

local function lsp_format(input)
  local name
  local async = input.bang

  if input.args ~= '' then
    name = input.args
  end

  vim.lsp.buf.format({async = async, name = name})
end

vim.api.nvim_create_autocmd('LspAttach', {
  desc = 'LSP actions',
  callback = function(event)
    local bufnr = event.buf
    local map = function(m, lhs, rhs)
      local opts = {buffer = bufnr}
      vim.keymap.set(m, lhs, rhs, opts)
    end

    vim.api.nvim_buf_create_user_command(
      bufnr,
      'LspFormat',
      lsp_format,
      {
        bang = true,
        nargs = '?',
        desc = 'Format buffer with language server'
      }
    )

    -- LSP actions
    map('n', 'K', '<cmd>lua vim.lsp.buf.hover()<cr>')
    map('n', 'gd', '<cmd>lua vim.lsp.buf.definition()<cr>')
    map('n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<cr>')
    map('n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<cr>')
    map('n', 'go', '<cmd>lua vim.lsp.buf.type_definition()<cr>')
    map('n', 'gr', '<cmd>lua vim.lsp.buf.references()<cr>')
    map('n', 'gs', '<cmd>lua vim.lsp.buf.signature_help()<cr>')
    map('n', '<F2>', '<cmd>lua vim.lsp.buf.rename()<cr>')
    map({'n', 'x'}, '<F3>', '<cmd>lua vim.lsp.buf.format({async = true})<cr>')
    map('n', '<F4>', '<cmd>lua vim.lsp.buf.code_action()<cr>')
    map('x', '<F4>', '<cmd>lua vim.lsp.buf.range_code_action()<cr>')

    -- Diagnostics
    map('n', 'gl', '<cmd>lua vim.diagnostic.open_float()<cr>')
    map('n', '[d', '<cmd>lua vim.diagnostic.goto_prev()<cr>')
    map('n', ']d', '<cmd>lua vim.diagnostic.goto_next()<cr>') 
  end
})


lsp_settings()

require('mason').setup({})
require('mason-lspconfig').setup({
  handlers = {
    function(server)
      require('lspconfig')[server].setup({})
    end,
  },
})


---
-- Autocompletion
---

local cmp = require('cmp')

local cmp_config = {
  sources = {
    {name = 'nvim_lsp'},
  },
  mapping = {
    -- confirm selection
    ['<C-y>'] = cmp.mapping.confirm({select = false}),

    -- cancel completion
    ['<C-e>'] = cmp.mapping.abort(),

    -- navigate items on the list
    ['<Up>'] = cmp.mapping.select_prev_item({behavior = 'select'}),
    ['<Down>'] = cmp.mapping.select_next_item({behavior = 'select'}),

    -- if completion menu is visible, go to the previous item
    -- else, trigger completion menu
    ['<C-p>'] = cmp.mapping(function()
      if cmp.visible() then
        cmp.select_prev_item({behavior = 'insert'})
      else
        cmp.complete()
      end
    end),

    -- if completion menu is visible, go to the next item
    -- else, trigger completion menu
    ['<C-n>'] = cmp.mapping(function()
      if cmp.visible() then
        cmp.select_next_item({behavior = 'insert'})
      else
        cmp.complete()
      end
    end),
  },
  snippet = {
    expand = function(args)
      luasnip.lsp_expand(args.body)
    end,
  },
}

cmp.setup(cmp_config)