首页 > 解决方案 > LSP4J:如何让语言服务器从客户端请求一些东西

问题描述

我正在开发使用 LSP4J 用 Scala 编写的语言服务器。我一直在关注 VS Code 和 LSP4J 的文档,并达到了可以在客户端和服务器之间来回发送通知的地步。我还设法让客户端从服务器请求东西,然后与它们一起工作。但是,我还没有完全设法反过来。即,我可以让服务器向客户端发出请求,但这些请求永远不会完成。

为了给你一些背景,这就是我所做的:

  1. 为 VS Code 扩展实现了一个状态栏项。
  2. 注册了一个命令,该命令将在单击项目时向服务器发送通知。
  3. 在服务器端为该通知实现了一个处理程序。
  4. 让该处理程序向客户端发送请求。
  5. 在客户端为该请求实现了一个处理程序。

到目前为止,一切都很好。我可以追踪每一步,并知道一切都按我的预期执行。现在,在服务器端,我应该返回一个CompletableFuture. 根据我对事物的理解,这CompletableFuture最终应该以请求处理程序(在客户端)返回的值完成。

以下是上述代码的相关部分。

trait IdeLanguageClient extends LanguageClient {

  //...

  //This is the request sent to the client
  @JsonRequest("SomeRequest")
  def s2c_request(): CompletableFuture[String]
}
class ExampleLanguageServer() extends LanguageClientAware {
  private var client: Option[IdeLanguageClient] = None

  //...  


  //This is the handler for the notification the client sends after clicking on the status bar.
  @JsonNotification("sbi")
  def c2s_notification(): Unit = {
    client match {
      case Some(c) =>
        val completable_future = c.s2c_request()
        while(!fut.isDone){
          println("sleepy")
          Thread.sleep(1000)   // This goes on for ever!!
        }
        val s = fut.get()
        println(s)
    }
  }

  override def connect(client: LanguageClient): Unit = {
    println("client is connected")
    this.client = Some(client.asInstanceOf[IdeLanguageClient])
  }
}
export function activate(context: ExtensionContext) {

  //...

  // Create the language client.
  client = new LanguageClient(
    'languageServerExample',
    'Language Server Example',
    startServer,
    clientOptions
  );
        
  createStatusBarItem();

  // Handler for the server's request
  client.onReady().then(ready => {
      client.onRequest("SomeRequest", () => {
          "Some Responce to the request"
      });
  });
    
  // Start the client. This will also launch the server
  client.start();

  function createStatusBarItem() {...}
  // Invokes the jars of the language server
  function startServer(): Promise<StreamInfo> {...}
}

关键部分是 while 循环c2s_notification。在我的理解中,未来应该最终(考虑到微小的任务,几乎立即)完成并产生onRequest句柄返回的值。相反,循环只是每秒打印一次“睡眠”。我是不是误会了什么?还是我只是做错了什么?

我希望问题表述得足够清楚,并且包含所有必要的信息。

谢谢你的帮助!

标签: typescriptscalavscode-extensionslanguage-server-protocol

解决方案


我发现了问题所在。我认为CompletableFutures 可以像Futures 一样对待。显然情况并非如此。相反,join-ing(或get-ting)来自 a 的结果的异步计算CompletableFuture必须由 a 包装Future

即,用以下内容替换 while 循环周围的关键部分可以解决c2sNotification问题:

client match {
  case Some(c) =>
    val f: Future[String] = Future {
      val cf: CompletableFuture[String] = c.s2c_request()
      val s = cf.join()
      s
    }
    f.onComplete({
      case Success(s) => println(s)
      case Failure(e) => println(e)
    })
}

推荐阅读