Using LSP without nvim-lspconfig

I'm not a fan of lspconfig because I think it abstracts too much of how neovim functions behind obscure lua code. Imo, you really don't need to be that proficient in lua before you can figure out what's happening behind the scenes with your editor.

If your goal is to be proficient in neovim and lua such that you can write your own scripts when the need rises, you should imo try to do without lspconfig.

All that happens really is that neovim launches a particular binary, with particular settings when a particular filetype is opened.

This is all you need to have in your init.lua to set up lua language server:

vim.api.nvim_create_autocmd('FileType', {
  pattern = 'lua',
  callback = function(ev)
    vim.lsp.start({
      name = "lua_ls",
      cmd = { 'lua-language-server' },
      root_dir = vim.fs.root(ev.buf, { '.luarc.json', '.luarc.jsonc' }),
      settings = {
        Lua = {
          runtime = {
            version = "LuaJIT",
          },
          workspace = {
            library = {
              "/usr/share/nvim/runtime",
            }
          },
        },
      },
    })
  end,
})

Its so obvious what's going on right? We just create an autocommand. Whenever a filetype lua is opened, the function in callback gets executed. All that function does is launches the binary of language server. Now, if you've ever looked into the differences between treesitter and lsp, you might know that unlike treesitter the lsp is aware of your project as a whole. It knows what you should import from another file so that the variables in the current file you're editing is in scope. For that to happen, you need to let the lsp know which directory is the root of the project. This is why there's a root_dir argument.

You can find out what goes into settings argument by reading the documentation of the specific language server and by checking out server-configurations page in nvim-lspconfig: https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md

In lua for example, this section reveals what the settings should be:

client.config.settings.Lua = vim.tbl_deep_extend('force', client.config.settings.Lua, {
  runtime = {
    -- Tell the language server which version of Lua you're using
    -- (most likely LuaJIT in the case of Neovim)
    version = 'LuaJIT'
  },
  -- Make the server aware of Neovim runtime files
  workspace = {
    checkThirdParty = false,
    library = {
      vim.env.VIMRUNTIME
      -- Depending on the usage, you might want to add additional paths here.
      -- "${3rd}/luv/library"
      -- "${3rd}/busted/library",
    }
    -- or pull in all of 'runtimepath'. NOTE: this is a lot slower
    -- library = vim.api.nvim_get_runtime_file("", true)
  }
})

Additional example (dart language server):

vim.api.nvim_create_autocmd('FileType', {
  pattern = 'dart',
  callback = function(ev)
    vim.lsp.start({
      name = "dartls",
      cmd = { 'dart', 'language-server', '--protocol=lsp' },
      root_dir = vim.fs.root(ev.buf, { 'pubspec.yaml' }),
      init_options = {
        closingLabels = true,
        flutterOutline = true,
        onlyAnalyzeProjectsWithOpenFiles = true,
        outline = true,
        suggestFromUnimportedLibraries = true
      },
      settings = {
        dart = {
          completeFunctionCalls = true,
          showTodos = true,
        },
      },
    })
  end,
})

Comments