scalameta / metals

Scala language server with rich IDE features 🚀

Home Page:https://scalameta.org/metals/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Metals Only Processes the Head Option of the contentChanges Array

tsxjsx opened this issue · comments

Describe the bug

When the client sends a textDocument/didChange request with parameters, Metals only considers the first index element (text='') option from the contentChanges array, disregarding the remaining index elements.

textDocument/didChange(LSP) specification
While the Language Server Protocol (LSP) specification dictates that the second index element from the contentChanges array in a textDocument/didChange request should take effect, Metals, when processing such requests, only considers the first index element (text=''), thus presenting a deviation from the standard

Client side:-

{
  "textDocument": {
    "version": 88,
    "uri": "file:///home/local/user/scala-cosmos/src/main/scala/org/cosmos/scala/Main.scala"
  },
  "contentChanges": [
    {
      "text": ""
    },
    {
      "range": {
        "start": {
          "line": 0,
          "character": 0
        },
        "end": {
          "line": 0,
          "character": 0
        }
      },
      "rangeLength": 0,
      "text": "package org.cosmos.scala\n\n// A new way to define the program entry point. Note the automatic argument extraction.\n@main def startApp() \u003d \n  Server.run(8888)                                                        \n}                         \n  \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n              \n\n\n\n\n\n            "
    }
  ]

Server Side:-

  override def didChange(
      params: DidChangeTextDocumentParams
  ): CompletableFuture[Unit] =
    params.getContentChanges.asScala.headOption match {
      case None => CompletableFuture.completedFuture(())
      case Some(change) =>
        val path = params.getTextDocument.getUri.toAbsolutePath
        buffers.put(path, change.getText)
        diagnostics.didChange(path)

        parseTrees(path)
          .flatMap { _ =>
            treeView.onWorkspaceFileDidChange(path)
            publishSynthetics(path)
          }
          .ignoreValue
          .asJava
    }
    
     /** Optionally selects the first element.
    *  $orderDependent
    *  @return  the first element of this $coll if it is nonempty,
    *           `None` if it is empty.
    */
  def headOption: Option[A] = {
    val it = iterator
    if (it.hasNext) Some(it.next()) else None
  }

Result:
Due to the exclusive consideration of the first index element, the server interprets the document as empty, resulting in the absence of functioning language features for the textDocument.

Expected behavior

According to the Language Server Protocol (LSP) specification, when a client sends a textDocument/didChange request with parameters containing multiple elements within the contentChanges array, the server should consider all elements for processing. Each element represents a change in the document, facilitating incremental updates.

In the ideal scenario, the server should iterate through each element within the contentChanges array, applying modifications to the document accordingly. This behavior ensures that language features are fully functional and responsive to incremental changes made by the user.

The server's adherence to the LSP specification enables seamless integration with various development environments and enhances the overall user experience by providing accurate and timely feedback on code modifications.

Operating system

Linux

Editor/Extension

None

Version of Metals

1.2.2+39-68cbb4ce-SNAPSHOT

Extra context or search terms

No response

Thanks for reporting! Metals only supports lsp4j.TextDocumentSyncKind.Full so I assumed there wouldn't be multiple versions changed. Did you find that while using VS Code?

I think in this case we would instead just take the last item and ignore everything else, since we are not interested on previous state of the document, but the latest one.

@kasiaMarek could this be related to the recent changes with saveAs?

@kasiaMarek could this be related to the recent changes with saveAs?

No, it doesn't seem related.