Skip to content

AstroLsp

AstroLsp用于在 AstroNvim 中配置 LSP 即语言服务器。neovim 中集成了一个 lsp 客户端,AstroLsp 主要就是配置来实现自动安装对应语言的 LSP 服务器,它主要提供了对以下插件的集成:

  • nvim-lspconfig: neovim 官方提供的 lsp 服务器配置插件
  • mason.nvim: 帮助我们自动安装 lsp 服务器
  • none-ls.nvim: 它将一些非 lsp 服务器的工具(主要是 format 和 lint 工具)包装为 lsp 接口

mason

该插件能过帮助我们轻松安装外部工具,例如 lsp 服务器、dap 服务器、linter 和 formater 工具。

默认情况下他们会安装到 ~/.local/share/nvim/mason 目录下,其中还存在一个 bin/ 目录来放置可执行程序(软连接)。bin/ 目录会被 mason 设置到 Neovim 进程的 PATH 中,从而运行从 Neovim 内置函数(shell、终端等)以及其他第三方插件无缝访问

Note

mason 插件有很多的依赖,例如 git、pip、npm 等,他们是安装这些工具的核心命令

mason 工具提供了几个管理命令:

  • :Mason: 交互窗口,在其中 i-安装 u-更新 X-卸载
  • :MasonUpdate: 更新所有工具
  • :MasonInstall <package>: 安装指定工具
  • :MasonUninstall <package>: 卸载指定工具
  • :MasonUninstallAll: 卸载所有工具
  • :MasonLog: 打开日志文件

nvim-lspconfig and mason-lspconfig

nvim-lspconfig 插件是官方提供的用于配置 lsp 服务器,之后通过:

Lua
-- 配置特定 lsp server 例如 pyright
-- opts 是 lspconfig 的配置项目
require('lspconfig')[server_name].setup(opts)

但是它需要你手动安装 lsp 服务器,因此就需要 mason 插件来安装。而mason-lspconfig来沟通两者,来自动通过 mason 安装 lsp 服务器并自动应用 nvim-lspconfig 中的默认配置(甚至不需要你在运行上面的初始化语句):

Lua
local DEFAULT_SETTINGS = {
    ---@type string[]
    ensure_installed = {}, -- 自动安装

    -- Can either be:
    --   - false: Servers are not automatically installed.
    --   - true: All servers set up via lspconfig are automatically installed.
    --   - { exclude: string[] }: All servers set up via lspconfig, except the ones provided in the list, are automatically installed.
    ---@type boolean | {exclude: string[]}
    -- 即 lsp 服务器由 lspconfig 配置完成,由 mason-lspconfig 自动安装
    automatic_installation = false,

    -- nil 不要自动配置 lsp 服务器
    --
    ---@type table<string, fun(server_name: string)>?
    handlers = nil,

    handlers = {
      function (server_name) -- default handler (自动配置所有 lsp 服务器)
        require("lspconfig")[server_name].setup {}
      end,
      -- 对特定语言进行配置
      ["lua_ls"] = function()
        local lspconfig = require("lspconfig")
        lspconfig.lua_ls.setup {
          settings = {
            Lua  = {
              diagnostics = { globals = { "vim" }}
            }
          }
        }
      end,
    }
}

Tips

说白了就是将 lspconfig 的配置迁移到 mason-lspconfig 的 handlers 中,并且添加了自动安装的功能。

null-ls and mason-null-ls

null-ls 将一些格式化、linter 工具进行包装来为兼容 lsp 协议的接口来当作 lsp 服务器使用。对他的设置只需要添加需要的源即可:

Lua
local null_ls = require("null-ls")

-- Check supported formatters and linters
-- https://github.com/nvimtools/none-ls.nvim/tree/main/lua/null-ls/builtins/formatting
-- https://github.com/nvimtools/none-ls.nvim/tree/main/lua/null-ls/builtins/diagnostics
null_ls.setup({
    sources = {
        null_ls.builtins.formatting.stylua,
        null_ls.builtins.completion.spell,
    },
})

同样要想让源真正的生效 PATH 中必须存在对应的工具,他可以使用 mason 来安装,而mason-null-ls.nvim就是让我们自动安装并启用对应源的插件,它沟通了 mason 和 null-ls 这两个插件:

Lua
local DEFAULT_SETTINGS = {
    -- A list of sources to install if they're not already installed.
    -- This setting has no relation with the `automatic_installation` setting.
    ensure_installed = {},
    -- Enable or disable null-ls methods to get set up
    -- This setting is useful if some functionality is handled by other plugins such as `conform` and `nvim-lint`
    methods = {
        diagnostics = true,
        formatting = true,
        code_actions = true,
        completion = true,
        hover = true,
    },
    -- Run `require("null-ls").setup`.
    -- Will automatically install masons tools based on selected sources in `null-ls`.
    -- Can also be an exclusion list.
    -- Example: `automatic_installation = { exclude = { "rust_analyzer", "solargraph" } }`
    automatic_installation = false,
    -- See [#handlers-usage](#handlers-usage) section
    -- handlers = nil,
    handlers = {
        function() end, -- disables automatic setup of all null-ls sources
        stylua = function(source_name, methods)
          local null_ls = require 'null-ls'
          null_ls.register(null_ls.builtins.formatting.stylua)
        end,
        shfmt = function(source_name, methods)
          -- custom logic
          require('mason-null-ls').default_setup(source_name, methods) -- to maintain default behavior
        end,
    },
}

Tips

他的 handlers 和 mason-lspconfig 一样,如果为 nil 表示配置源由 null-ls 自己设置,可以使用 handlers = {} 来自动安装并配置(这是 AstroNvim 的默认配置)

它还有一个就是所有的源由 null-ls 配置来让 mason-null-ls 自动安装:

Lua
require("mason").setup()
require("null-ls").setup({
    sources = {
        -- all sources go here.
    }
})
require("mason-null-ls").setup({
    ensure_installed = nil,
    automatic_installation = true,
})

astrolsp

在安装模版中它在lua/plugin/astrolsp.lua中被引入,因此配置也在这里进行。它的配置完全遵循lazy规范:

Lua
---@type AstroLSPConfig
local opts = {
  -- Configuration table of features provided by AstroLSP
  features = {
    codelens = true, -- enable/disable codelens refresh on start
    inlay_hints = false, -- enable/disable inlay hints on start
    semantic_tokens = true, -- enable/disable semantic token highlighting
  },
  -- Configure buffer local auto commands to add when attaching a language server
  autocmds = {
    -- first key is the `augroup` (:h augroup)
    lsp_document_highlight = {
      -- condition to create/delete auto command group
      -- can either be a string of a client capability or a function of `fun(client, bufnr): boolean`
      -- condition will be resolved for each client on each execution and if it ever fails for all clients,
      -- the auto commands will be deleted for that buffer
      cond = "textDocument/documentHighlight",
      -- list of auto commands to set
      {
        -- events to trigger
        event = { "CursorHold", "CursorHoldI" },
        -- the rest of the autocmd options (:h nvim_create_autocmd)
        desc = "Document Highlighting",
        callback = function() vim.lsp.buf.document_highlight() end,
      },
      {
        event = { "CursorMoved", "CursorMovedI", "BufLeave" },
        desc = "Document Highlighting Clear",
        callback = function() vim.lsp.buf.clear_references() end,
      },
    },
  },
  -- Configure buffer local user commands to add when attaching a language server
  commands = {
    Format = {
      function() vim.lsp.buf.format() end,
      -- condition to create the user command
      -- can either be a string of a client capability or a function of `fun(client, bufnr): boolean`
      cond = "textDocument/formatting",
      -- the rest of the user command options (:h nvim_create_user_command)
      desc = "Format file with LSP",
    },
  },
  -- Configure default capabilities for language servers (`:h vim.lsp.protocol.make_client.capabilities()`)
  capabilities = {
    textDocument = {
      foldingRange = { dynamicRegistration = false },
    },
  },
  -- Configure language servers for `lspconfig` (`:h lspconfig-setup`)
  config = {
    lua_ls = {
      settings = {
        Lua = {
          hint = { enable = true, arrayIndex = "Disable" },
        },
      },
    },
    clangd = {
      capabilities = {
        offsetEncoding = "utf-8",
      },
    },
  },
  -- A custom flags table to be passed to all language servers  (`:h lspconfig-setup`)
  flags = {
    exit_timeout = 5000,
  },
  -- Configuration options for controlling formatting with language servers
  formatting = {
    -- control auto formatting on save
    format_on_save = {
      -- enable or disable format on save globally
      enabled = true,
      -- enable format on save for specified filetypes only
      allow_filetypes = {
        "go",
      },
      -- disable format on save for specified filetypes
      ignore_filetypes = {
        "python",
      },
    },
    -- disable formatting capabilities for specific language servers
    disabled = {
      "lua_ls",
    },
    -- default format timeout
    timeout_ms = 1000,
    -- fully override the default formatting function
    filter = function(client) return true end,
  },
  -- Configure how language servers get set up
  handlers = {
    -- default handler, first entry with no key
    function(server, opts) require("lspconfig")[server].setup(opts) end,
    -- custom function handler for pyright
    pyright = function(_, opts) require("lspconfig").pyright.setup(opts) end,
    -- set to false to disable the setup of a language server
    rust_analyzer = false,
  },
  -- Configure `vim.lsp.handlers`
  lsp_handlers = {
    ["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, { border = "rounded", silent = true }),
    ["textDocument/signatureHelp"] = false, -- set to false to disable any custom handlers
  },
  -- Configuration of mappings added when attaching a language server during the core `on_attach` function
  -- The first key into the table is the vim map mode (`:h map-modes`), and the value is a table of entries to be passed to `vim.keymap.set` (`:h vim.keymap.set`):
  --   - The key is the first parameter or the vim mode (only a single mode supported) and the value is a table of keymaps within that mode:
  --     - The first element with no key in the table is the action (the 2nd parameter) and the rest of the keys/value pairs are options for the third parameter.
  --       There is also a special `cond` key which can either be a string of a language server capability or a function with `client` and `bufnr` parameters that returns a boolean of whether or not the mapping is added.
  mappings = {
    -- map mode (:h map-modes)
    n = {
      -- a binding with no condition and therefore is always added
      gl = {
        function() vim.diagnostic.open_float() end,
        desc = "Hover diagnostics",
      },
      -- condition for only server with declaration capabilities
      gD = {
        function() vim.lsp.buf.declaration() end,
        desc = "Declaration of current symbol",
        cond = "textDocument/declaration",
      },
      -- condition with a full function with `client` and `bufnr`
      ["<leader>uY"] = {
        function() require("astrolsp.toggles").buffer_semantic_tokens() end,
        desc = "Toggle LSP semantic highlight (buffer)",
        cond = function(client, bufnr)
          return client.server_capabilities.semanticTokensProvider and vim.lsp.semantic_tokens
        end,
      },
    },
  },
  -- A list like table of servers that should be setup, useful for enabling language servers not installed with Mason.
  servers = { "dartls" },
  -- A custom `on_attach` function to be run after the default `on_attach` function, takes two parameters `client` and `bufnr`  (`:h lspconfig-setup`)
  on_attach = function(client, bufnr) client.server_capabilities.semanticTokensProvider = nil end,
}