是什么

官方定义:https://microsoft.github.io/language-server-protocol/overviews/lsp/overview/

为编程语言实现自动补全、定义跳转或在鼠标悬停时显示文档等功能的支持是一项重大工作。传统上,这项工作必须为每个开发工具重复进行,因为每个工具提供的实现相同功能的API都不同。

LSP的理念就是通过一种协议,与开发工具交流,实现功能。通过中间层,来做到一个语言服务器服务多个开发工具。

怎么用

意识就是开发工具会捕捉一个个事件点,与语言服务器进行交互,比如这里的:打开文档,编辑文档,用户执行定义跳转,关闭文档等。

可以具体看一下定义跳转的交互

请求:

{
    "jsonrpc": "2.0",
    "id" : 1,
    "method": "textDocument/definition",
    "params": {
        "textDocument": {
            "uri": "file:///p%3A/mseng/VSCode/Playgrounds/cpp/use.cpp"
        },
        "position": {
            "line": 3,
            "character": 12
        }
    }
}

响应:

{
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
        "uri": "file:///p%3A/mseng/VSCode/Playgrounds/cpp/provide.cpp",
        "range": {
            "start": {
                "line": 0,
                "character": 4
            },
            "end": {
                "line": 0,
                "character": 11
            }
        }
    }
}

具体参数信息可看:官方文档

并非所有语言服务器都支持协议中定义的所有功能,所以需要在初始化时通过Capabilities,来告诉开发工具,我能做啥。

自己实现

编译审查

完整代码:gist

def check_syntax file
  # 检查语法
  RubyVM::InstructionSequence.compile_file(file)
  nil
rescue SyntaxError => e
  e # only return syntax errors
rescue Exception
  nil # ignore anything else
end

# 获取异常信息
line_number = error.message[/(?<=:)\d+/].to_i
# 找到具体行
line = File.readlines(file)[line_number - 1]

# 返回信息
result = {
  :uri => doc[:uri],
  :diagnostics => [
    {
      "range" => {
        "start" => { "character" => 0, "line" => line_number - 1 },
        "end"   => { "character" => line.bytesize, "line" => line_number - 1 },
      },
      # 具体返回的信息
      "message" => error.message.lines.first,
      # 标识符,表示错误、警告、信息或提示。
      "severity" => 1
    },
  ],
}

# 写回流中
writer.write(method: "textDocument/publishDiagnostics", params: result)