Skip to content

Commit

Permalink
Merge pull request #57 from alephium/watched-files
Browse files Browse the repository at this point in the history
Watched files
  • Loading branch information
tdroxler authored Nov 13, 2023
2 parents 75a570d + 6dcf760 commit 73710dd
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 6 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,21 @@ Add the following to your lua configuration

```
local function ralph_init()
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = true
local root_dir = vim.fs.dirname(vim.fs.find({'build.ralph', 'contracts', 'artifacts'}, { upward = true })[1])
if root_dir == nil then root_dir = vim.fn.getcwd() end
vim.lsp.start({
name = 'ralph-lsp',
cmd = {'java', '-jar', '<path-to-your-jar>/ralph-lsp.jar'},
root_dir = vim.fs.dirname(vim.fs.find({'build.ralph'}, { upward = true })[1])
root_dir = root_dir,
capabilities = capabilities
})
end
vim.api.nvim_create_autocmd('FileType', {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.alephium.ralph.lsp.server

import java.util.UUID

object MessageMethods {
val WORKSPACE_WATCHED_FILES = "workspace/didChangeWatchedFiles"

/* Unique ids used to register/unregister capabilities */
val WORKSPACE_WATCHED_FILES_ID: String = UUID.randomUUID().toString()
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package org.alephium.ralph.lsp.server

import org.alephium.ralph.lsp.server.MessageMethods._

import org.eclipse.lsp4j._
import org.eclipse.lsp4j.services.LanguageClient
import org.eclipse.lsp4j.jsonrpc.messages
import java.util.concurrent.CompletableFuture

object RalphLangClient {

Expand All @@ -13,6 +17,15 @@ object RalphLangClient {
error
}

/* Server needs to notify the client about which files to watch.
* The client will then send notifications when given files are deleted/created */
def registerWatchedFiles(): CompletableFuture[Void] = {
val watchers = java.util.Arrays.asList(new FileSystemWatcher(messages.Either.forLeft("**/*")))
val options = new DidChangeWatchedFilesRegistrationOptions(watchers)
val registration = new Registration(WORKSPACE_WATCHED_FILES_ID, WORKSPACE_WATCHED_FILES, options)

client.registerCapability(new RegistrationParams(java.util.Arrays.asList(registration)))
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ object RalphLangServer {
listener = Some(listener),
workspace = None,
buildErrors = None,
clientCapabilities = None,
shutdownReceived = false
)

Expand All @@ -53,6 +54,7 @@ object RalphLangServer {
listener = None,
workspace = None,
buildErrors = None,
clientCapabilities = None,
shutdownReceived = false
)
)
Expand Down Expand Up @@ -100,6 +102,7 @@ class RalphLangServer private(@volatile private var state: ServerState)(implicit
override def initialize(params: InitializeParams): CompletableFuture[InitializeResult] =
CompletableFutures.computeAsync {
cancelChecker =>
logger.debug("Initialize request")
// Previous commit uses the non-deprecated API but that does not work in vim.
val rootURI =
RalphLangServer.getRootUri(params)
Expand All @@ -112,11 +115,38 @@ class RalphLangServer private(@volatile private var state: ServerState)(implicit

setWorkspace(workspace)

setClientCapabilities(params.getCapabilities())

cancelChecker.checkCanceled()

new InitializeResult(serverCapabilities())
}

/** @inheritdoc */
override def initialized(params: InitializedParams): Unit = {
logger.debug("Client initialized")
thisServer.state.clientCapabilities match {
case Some(capabilities) =>
val maybeDynamicRegistration: Option[Boolean] = for {
workspace <- Option(capabilities.getWorkspace())
didChangeWatchedFiles <- Option(workspace.getDidChangeWatchedFiles())
dynamicRegistration <- Option(didChangeWatchedFiles.getDynamicRegistration())
} yield dynamicRegistration

if(maybeDynamicRegistration.getOrElse(false)) {
logger.debug("Register watched files")
getClient().registerWatchedFiles().whenComplete { case (_, error) =>
if(error != null) {
logger.error("Failed to register watched files", error)
}
}
} else {
logger.debug("Client doesn't support dynamic registration for watched files")
}
case None => ()
}
}

/** @inheritdoc */
override def didOpen(params: DidOpenTextDocumentParams): Unit = {
val fileURI = new URI(params.getTextDocument.getUri)
Expand Down Expand Up @@ -330,6 +360,11 @@ class RalphLangServer private(@volatile private var state: ServerState)(implicit
thisServer.state = thisServer.state.copy(workspace = Some(workspace))
}

private def setClientCapabilities(clientCapabilities: ClientCapabilities): Unit =
thisServer.synchronized {
thisServer.state = thisServer.state.copy(clientCapabilities = Some(clientCapabilities))
}

/**
* Set the workspace and returns diagnostics to publish for current state.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import org.alephium.ralph.lsp.pc.workspace.build.BuildState
import org.alephium.ralph.lsp.server.RalphLangClient

import java.util.concurrent.{Future => JFuture}
import org.eclipse.lsp4j.ClientCapabilities

/**
* Stores Ralph's LSP server state.
Expand All @@ -21,4 +22,5 @@ case class ServerState(client: Option[RalphLangClient],
listener: Option[JFuture[Void]],
workspace: Option[WorkspaceState],
buildErrors: Option[BuildState.BuildErrored],
clientCapabilities: Option[ClientCapabilities],
shutdownReceived: Boolean)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import org.alephium.ralph.lsp.access.compiler.CompilerAccess
import org.alephium.ralph.lsp.access.file.FileAccess
import org.alephium.ralph.lsp.pc.workspace.WorkspaceState
import org.alephium.ralph.lsp.server.state.ServerState
import org.eclipse.lsp4j.{InitializeParams, InitializeResult}
import org.eclipse.lsp4j._
import org.eclipse.lsp4j.jsonrpc.ResponseErrorException
import org.scalamock.scalatest.MockFactory
import org.scalatest.concurrent.ScalaFutures
Expand All @@ -14,6 +14,7 @@ import org.scalatest.wordspec.AnyWordSpec
import scala.jdk.FutureConverters._
import java.net.URI
import java.util.concurrent.{ CompletionStage,CompletableFuture, Future => JFuture}
import java.util.concurrent.atomic.AtomicBoolean
import scala.concurrent.Promise

class RalphLangServerSpec extends AnyWordSpec with Matchers with MockFactory with ScalaFutures{
Expand Down Expand Up @@ -48,6 +49,7 @@ class RalphLangServerSpec extends AnyWordSpec with Matchers with MockFactory wit
listener = Some(listener),
workspace = Some(WorkspaceState.Created(workspaceURI)),
buildErrors = None,
clientCapabilities = Some(initialise.getCapabilities),
shutdownReceived = false
)
}
Expand Down Expand Up @@ -111,6 +113,65 @@ class RalphLangServerSpec extends AnyWordSpec with Matchers with MockFactory wit
server.getState().listener.get.isCancelled shouldBe true
}
}

"initialized" should {

def buildServer(dynamicRegistration: Option[Boolean], registered: AtomicBoolean) = {
implicit val compiler: CompilerAccess = CompilerAccess.ralphc
implicit val file: FileAccess = FileAccess.disk
val client: RalphLangClient = new EmptyRalphLangClient {
override def registerCapability(params: RegistrationParams): CompletableFuture[Void] = {
registered.set(true)
new CompletableFuture[Void]()
}
}
val listener = CompletableFuture.runAsync(() => ())
val server = RalphLangServer(client, listener)

val capabilities = dynamicRegistration match {
case None => new ClientCapabilities()
case Some(dynamicRegistration) =>
val watchedFilesCapabilities = new DidChangeWatchedFilesCapabilities(dynamicRegistration)
val workspaceCapabilities = new WorkspaceClientCapabilities()
workspaceCapabilities.setDidChangeWatchedFiles(watchedFilesCapabilities)
val result = new ClientCapabilities()
result.setWorkspace(workspaceCapabilities)
result
}

val initialise = new InitializeParams()
val workspaceURI = new URI("file://test")
initialise.setRootUri(workspaceURI.toString)
initialise.setCapabilities(capabilities)

server.initialize(initialise).get()
server
}

"register watched files if dynamic registration is true" in {
val registered = new AtomicBoolean(false)
val server = buildServer(Some(true), registered)
server.initialized(new InitializedParams())

registered.get shouldBe true
}

"not register watched files if dynamic registration is false" in {
val registered = new AtomicBoolean(false)
val server = buildServer(Some(false), registered)
server.initialized(new InitializedParams())

registered.get shouldBe false
}

"not register watched files if dynamic registration is not set" in {
val registered = new AtomicBoolean(false)
val server = buildServer(None, registered)
server.initialized(new InitializedParams())

registered.get shouldBe false
}
}
}

trait EmptyRalphLangClient extends RalphLangClient {
Expand Down
5 changes: 1 addition & 4 deletions plugin-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ export function activate(context: ExtensionContext) {
};

const clientOptions: LanguageClientOptions = {
documentSelector: [{pattern: '**/*.{ral,ralph}'}],
synchronize: {
fileEvents: workspace.createFileSystemWatcher('**/')
}
documentSelector: [{pattern: '**/*.{ral,ralph}'}]
};

// Create the client and store it.
Expand Down

0 comments on commit 73710dd

Please sign in to comment.