详解nvim内建LSP体系与基于nvim-cmp的代码补全体系

本文详细介绍了nvim中的LSP(语言服务协议)体系,包括nvim-lspconfig、nvim-lspsaga和null-ls.nvim的使用,以及nvim-cmp代码补全系统的配置和运作原理。通过这些插件,nvim可以实现语法检查、代码补全、格式化等功能,提供强大的IDE体验。文章还阐述了LSP的工作流程和nvim中各个插件之间的关系,帮助读者理解LSP和代码补全的实现机制。
摘要由CSDN通过智能技术生成

2023年,nvim以及其生态已经发展的愈来愈完善了。nvim内置的LSP(以及具体的语言服务)加上众多插件,可以搭建出支持各种类型语法检查、代码补全、代码格式化等功能的IDE。网络上关于如何配置的文章很多,但本人发现绝大多数的文章仅仅停留在配置本身,没有深入的解释这些插件的作用和它们之间的关系,这就导致了很多入门的小伙伴在配置、使用的过程中遇到各种问题也不知如何下手。本文将手把手,一步一步的演进并解释,帮助小伙伴了解这块的内容。

注意1:本文主要探讨nvim关于LSP、null-ls以及代码补全内容,不会详细介绍如何使用插件系统。

注意2:本文阅读前需要读者已经掌握了如何使用插件管理器来安装插件并setup插件配置。

认识LSP

在本文的开始,让我们先介绍一下LSP(Language Server Protocol,语言服务协议)。当然,网络上有很多详细的介绍LSP的内容,本文不会深入介绍它的实现机制,仅作为本文的入门的解释。

简单来讲,该协议定义了两端:Language Client(语言服务客户端)和Language Server(语言服务端),其核心是将代码编辑器文本界面的展示代码语言分析(语言支持,自动补全,定义与引用解析等)解耦。通常,我们的文本编辑器就是一个客户端,而各种语言的解析则会有对应LSP协议实现的服务端。

为了让读者更加清楚的理解LSP的运作,我们编写有如下TypeScript代码:

// 1. 定义接口
interface User {
  name: string;
}
// 2. 实现接口的对象
const user: User = {
  name: 'hello'
}
// 3. 打印对象的age属性
console.log(user.age); // error

上述这段代码首先定义了一个名为User的接口(interface User),该接口拥有一个字段name;然后,我们创建了一个基于User接口的user实例;最后,我们打印了user的age属性。user并不具备age字段,所以按照严格的TypeScript语言规范来讲,代码编译肯定会有错误:

基于LSP的模型,我们可以将这个过程描述出来:

  1. 在编辑器上写入上述的TS代码;
  2. 编辑器将上述代码通过某种通讯协议发送给TypeScript语言服务器;
  3. TS语言服务读取TS代码,进行语法检查,得到了编译错误信息(包含行列数,基本的建议提示信息)返回给编辑器;
  4. 编辑器接收到错误信息,通过自己的方式展示在编辑器UI上。

现在,我们已经了解了基于LSP的代码分析处理流程,那么这个语言服务器在什么地方呢?首先,不要看到服务器三个字,就认为它一定是一个在远端的Web应用服务,语言服务器一般就是一个软件程序,只不过它能够处理专门解析你编写的程序代码,并做出响应。

使用LSP这套体系,有两个必备步骤:

  1. 获取并安装语言服务器程序;
  2. 启动语言服务器,让它处于运行状态。

有些语言服务器基于js编写实现,它一般是一个NPM包,我们以npm -g全局安装的形式安装它(例如TypeScript的语言服务器的实现typescript-language-server);有的语言服务器直接就是可执行程序(例如lua语言服务器lua-language-server),我们从网络上下载它存放到电脑上。通常,我们会把它们的可执行文件路径加入到环境变量中,以便随时在命令行中启动它们。启动以后,它就在一个进程中默默的的等待着客户端(也就是编辑器)链接,并在建立连接以后,进行代码的分析处理工作。

nvim中的LSP

了解了LSP的基本概念以后,接下来我们介绍在nvim中的LSP模块。在nvim 0.5+版本以后,已经内置了语言服务客户端的接口(Lsp - Neovim docs注意只是语言服务客户端部分),比较常用的API:

  • vim.lsp.buf.hover():代码的TIPS悬浮展示。
  • vim.lsp.buf.format():代码格式化。
  • vim.lsp.buf.references():当前代码符号的引用查询。
  • vim.lsp.buf.implementation():当前代码(主要是函数方法)的实现定位。
  • vim.lsp.buf.code_action():当前代码的一些优化操作。

但需要注意,上述这些都是接口方法,它只是一个封装后的壳子方法,不具备具体的实现。具体的实现,需要为每一个编程语言单独配置。也就是说,nvim内置的lsp模块的运行架构如下:

面对不同的语言,我们按照对应的语言服务的要求来配置nvim的内置LSP模块。在官方的文档中给了如下的示例来启动一个LSP:

vim.lsp.start({
  name = 'my-server-name',
  cmd = {'name-of-language-server-executable'},
  root_dir = vim.fs.dirname(vim.fs.find({'setup.py', 'pyproject.toml'}, { upward = true })[1]),
})

这段代码不过多赘述,因为它比起即将介绍的lspconfig插件来说,使用起来更加复杂。

nvim-lspconfig

每当有一个编程语言需要使用LSP的时候,我们都需要形如上述的nvim原生LSP配置来启动对应的语言服务器,同时还需要关注很多细节,譬如࿰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值