Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow file/read to return the contents of a collaborative buffer #9994

Merged
merged 1 commit into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ class JsonConnectionController(
WriteFile -> file.WriteTextualFileHandler
.props(requestTimeout, fileManager),
ReadFile -> file.ReadTextualFileHandler
.props(requestTimeout, fileManager),
.props(requestTimeout, bufferRegistry, fileManager),
CreateFile -> file.CreateFileHandler.props(requestTimeout, fileManager),
DeleteFile -> file.DeleteFileHandler.props(requestTimeout, fileManager),
CopyFile -> file.CopyFileHandler.props(requestTimeout, fileManager),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ import org.enso.languageserver.filemanager.{
}
import org.enso.languageserver.filemanager.FileManagerApi.ReadFile
import org.enso.languageserver.requesthandler.RequestTimeout
import org.enso.languageserver.text.TextProtocol.{
ReadCollaborativeBuffer,
ReadCollaborativeBufferResult
}
import org.enso.languageserver.util.UnhandledLogging
import org.enso.logger.masking.MaskedString

import scala.concurrent.duration.FiniteDuration

class ReadTextualFileHandler(
requestTimeout: FiniteDuration,
bufferRegistry: ActorRef,
fileManager: ActorRef
) extends Actor
with LazyLogging
Expand All @@ -27,10 +32,53 @@ class ReadTextualFileHandler(

private def requestStage: Receive = {
case Request(ReadFile, id, params: ReadFile.Params) =>
bufferRegistry ! ReadCollaborativeBuffer(params.path)
val cancellable = context.system.scheduler
.scheduleOnce(requestTimeout, self, RequestTimeout)
context.become(requestBufferStage(id, sender(), cancellable, params))
}

private def requestBufferStage(
id: Id,
replyTo: ActorRef,
cancellable: Cancellable,
params: ReadFile.Params
): Receive = {
case Status.Failure(ex) =>
logger.error(
"Failure during [{}] operation: {}",
ReadFile,
MaskedString(ex.getMessage)
)
replyTo ! ResponseError(Some(id), Errors.ServiceError)
cancellable.cancel()
context.stop(self)

case RequestTimeout =>
logger.warn(
"Read collaborative buffer timed out. " +
"Falling back to reading file contents."
)
fileManager ! FileManagerProtocol.ReadFile(params.path)
val cancellable = context.system.scheduler
.scheduleOnce(requestTimeout, self, RequestTimeout)
context.become(responseStage(id, sender(), cancellable))
context.become(responseStage(id, replyTo, cancellable))

case ReadCollaborativeBufferResult(None) =>
cancellable.cancel()
fileManager ! FileManagerProtocol.ReadFile(params.path)
val newCancellable = context.system.scheduler
.scheduleOnce(requestTimeout, self, RequestTimeout)
context.become(responseStage(id, replyTo, newCancellable))

case ReadCollaborativeBufferResult(Some(buffer)) =>
replyTo ! ResponseResult(
ReadFile,
id,
ReadFile.Result(buffer.contents.toString)
)
cancellable.cancel()
context.stop(self)
}

private def responseStage(
Expand Down Expand Up @@ -70,7 +118,11 @@ class ReadTextualFileHandler(

object ReadTextualFileHandler {

def props(timeout: FiniteDuration, fileManager: ActorRef): Props =
Props(new ReadTextualFileHandler(timeout, fileManager))
def props(
timeout: FiniteDuration,
bufferRegistry: ActorRef,
fileManager: ActorRef
): Props =
Props(new ReadTextualFileHandler(timeout, bufferRegistry, fileManager))

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import org.enso.languageserver.text.TextProtocol.{
FileSaved,
OpenBuffer,
OpenFile,
ReadCollaborativeBuffer,
ReadCollaborativeBufferResult,
SaveFailed,
SaveFile
}
Expand Down Expand Up @@ -156,6 +158,13 @@ class BufferRegistry(
context.become(running(registry + (path -> bufferRef)))
}

case msg @ ReadCollaborativeBuffer(path) =>
if (registry.contains(path)) {
registry(path).forward(msg)
} else {
sender() ! ReadCollaborativeBufferResult(None)
}

case Terminated(bufferRef) =>
context.become(running(registry.filter(_._2 != bufferRef)))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,9 @@ class CollaborativeBuffer(
failure
)
}

case ReadCollaborativeBuffer(_) =>
sender() ! ReadCollaborativeBufferResult(Some(buffer))
}

private def waitingOnReloadedContent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,16 @@ object TextProtocol {
/** Signals that the file is modified on disk. */
case object SaveFailedFileModifiedOnDisk extends SaveFileResult

/** Read the contents of a collaborative buffer.
*
* @param path the file path
*/
case class ReadCollaborativeBuffer(path: Path)

/** The data carried by a successful read operation.
*
* @param buffer file contents
*/
case class ReadCollaborativeBufferResult(buffer: Option[Buffer])

}
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,126 @@ class TextOperationsTest
}""")
}

"allow file/read to read contents of a buffer" in {
val client = getInitialisedWsClient()
client.send(json"""
{ "jsonrpc": "2.0",
"method": "file/write",
"id": 0,
"params": {
"path": {
"rootId": $testContentRootId,
"segments": [ "file_read_test.txt" ]
},
"contents": "123456789"
}
}
""")
client.expectJson(json"""
{ "jsonrpc": "2.0",
"id": 0,
"result": null
}
""")
client.send(json"""
{ "jsonrpc": "2.0",
"method": "text/openFile",
"id": 1,
"params": {
"path": {
"rootId": $testContentRootId,
"segments": [ "file_read_test.txt" ]
}
}
}
""")

receiveAndReplyToOpenFile("file_read_test.txt")

client.expectJson(json"""
{
"jsonrpc" : "2.0",
"id" : 1,
"result" : {
"writeCapability" : {
"method" : "text/canEdit",
"registerOptions" : {
"path" : {
"rootId" : $testContentRootId,
"segments" : [
"file_read_test.txt"
]
}
}
},
"content" : "123456789",
"currentVersion" : "5795c3d628fd638c9835a4c79a55809f265068c88729a1a3fcdf8522"
}
}
""")

// apply edit
client.send(json"""
{ "jsonrpc": "2.0",
"method": "text/applyEdit",
"id": 2,
"params": {
"edit": {
"path": {
"rootId": $testContentRootId,
"segments": [ "file_read_test.txt" ]
},
"oldVersion": "5795c3d628fd638c9835a4c79a55809f265068c88729a1a3fcdf8522",
"newVersion": "ebe55342f9c8b86857402797dd723fb4a2174e0b56d6ace0a6929ec3",
"edits": [
{
"range": {
"start": { "line": 0, "character": 0 },
"end": { "line": 0, "character": 0 }
},
"text": "bar"
},
{
"range": {
"start": { "line": 0, "character": 12 },
"end": { "line": 0, "character": 12 }
},
"text": "foo"
}
]
}
}
}
""")
client.expectJson(json"""
{ "jsonrpc": "2.0",
"id": 2,
"result": null
}
""")

// file/read
client.send(json"""
{ "jsonrpc": "2.0",
"method": "file/read",
"id": 3,
"params": {
"path": {
"rootId": $testContentRootId,
"segments": [ "file_read_test.txt" ]
}
}
}
""")
client.expectJson(json"""
{ "jsonrpc": "2.0",
"id": 3,
"result": { "contents": "bar123456789foo" }
}
""")

}

}

"text/openFile" must {
Expand Down
Loading