Add attributes to filesystem events (#8767)

close #8200

Changelog:
- add: `attributes` field to the file event notification
This commit is contained in:
Dmitry Bushev 2024-01-16 13:48:53 +00:00 committed by GitHub
parent 04b0d266e8
commit 51540e2eb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 102 additions and 99 deletions

View File

@ -2350,6 +2350,7 @@ of the (possibly multiple) content roots.
interface FileEventNotification {
path: Path;
kind: FileEventKind;
attributes?: FileAttributes;
}
```

View File

@ -8,8 +8,13 @@ import org.enso.filewatcher.Watcher
*
* @param path path to the file system object
* @param kind type of file system event
* @param attributes the file attributes
*/
case class FileEvent(path: Path, kind: FileEventKind)
case class FileEvent(
path: Path,
kind: FileEventKind,
attributes: Either[FileSystemFailure, FileAttributes]
)
object FileEvent {
@ -18,21 +23,27 @@ object FileEvent {
* @param root a project root
* @param base a watched path
* @param event a file system event
* @param attributes a file attributes
* @return file event
*/
def fromWatcherEvent(
root: File,
base: Path,
event: Watcher.WatcherEvent
): FileEvent =
event: Watcher.WatcherEvent,
attributes: Either[FileSystemFailure, FileSystemApi.Attributes]
): FileEvent = {
val eventPath = Path.getRelativePath(root, base, event.path)
FileEvent(
Path.getRelativePath(root, base, event.path),
FileEventKind(event.eventType)
eventPath,
FileEventKind(event.eventType),
attributes.map(
FileAttributes.fromFileSystemAttributes(root, eventPath, _)
)
)
}
}
/** Type of a file event.
*/
/** Type of a file event. */
sealed trait FileEventKind extends EnumEntry
object FileEventKind extends Enum[FileEventKind] with CirceEnum[FileEventKind] {

View File

@ -183,7 +183,11 @@ object FileManagerApi {
case object EventFile extends Method("file/event") {
case class Params(path: Path, kind: FileEventKind)
case class Params(
path: Path,
kind: FileEventKind,
attributes: Option[FileAttributes]
)
implicit val hasParams: HasParams.Aux[this.type, EventFile.Params] =
new HasParams[this.type] {

View File

@ -140,7 +140,17 @@ final class PathWatcher(
case e: Watcher.WatcherEvent =>
restartCounter.reset()
val event = FileEvent.fromWatcherEvent(root, base, e)
val fileInfo =
if (e.eventType == Watcher.EventTypeDelete) ZIO.fail(FileNotFound)
else fs.info(e.path.toFile)
exec
.exec(fileInfo)
.map(FileEvent.fromWatcherEvent(root, base, e, _))
.pipeTo(self)
case event: FileEvent =>
clients.foreach(_ ! FileEventResult(event))
context.system.eventStream.publish(event)

View File

@ -354,13 +354,16 @@ class JsonConnectionController(
FileModifiedOnDisk.Params(path)
)
case TextProtocol.FileEvent(path, event) =>
webActor ! Notification(EventFile, EventFile.Params(path, event))
case TextProtocol.FileEvent(path, event, attributes) =>
webActor ! Notification(
EventFile,
EventFile.Params(path, event, attributes)
)
case PathWatcherProtocol.FileEventResult(event) =>
webActor ! Notification(
EventFile,
EventFile.Params(event.path, event.kind)
EventFile.Params(event.path, event.kind, event.attributes.toOption)
)
case ContextRegistryProtocol

View File

@ -215,7 +215,7 @@ class BufferRegistry(
registry
)
case msg @ FileEvent(path, kind) =>
case msg @ FileEvent(path, kind, _) =>
if (kind == FileEventKind.Added || kind == FileEventKind.Modified) {
registry.get(path).foreach { buffer =>
buffer ! msg

View File

@ -324,81 +324,37 @@ class CollaborativeBuffer(
)
)
case FileEvent(path, _) =>
fileManager ! FileManagerProtocol.InfoFile(path)
val timeoutCancellable = context.system.scheduler.scheduleOnce(
timingsConfig.requestTimeout,
self,
IOTimeout
)
context.become(
waitingOnFileEventContent(
path,
buffer,
timeoutCancellable,
clients,
lockHolder,
autoSave
)
)
}
private def waitingOnFileEventContent(
path: Path,
buffer: Buffer,
timeoutCancellable: Cancellable,
clients: Map[ClientId, JsonSession],
lockHolder: Option[JsonSession],
autoSave: Map[ClientId, (ContentVersion, Cancellable)]
): Receive = {
case FileManagerProtocol.InfoFileResult(Right(attrs)) =>
timeoutCancellable.cancel()
val newBuffer = buffer.fileWithMetadata.lastModifiedTime.map {
bufferLastModifiedTime =>
if (attrs.lastModifiedTime.isAfter(bufferLastModifiedTime)) {
clients.values.foreach {
_.rpcController ! FileModifiedOnDisk(path)
}
buffer
.withLastModifiedTime(attrs.lastModifiedTime)
.withModifiedOnDisk()
} else {
buffer
case FileEvent(path, _, attributes) =>
attributes match {
case Right(attrs) =>
val newBuffer = buffer.fileWithMetadata.lastModifiedTime.map {
bufferLastModifiedTime =>
if (attrs.lastModifiedTime.isAfter(bufferLastModifiedTime)) {
clients.values.foreach {
_.rpcController ! FileModifiedOnDisk(path)
}
buffer
.withLastModifiedTime(attrs.lastModifiedTime)
.withModifiedOnDisk()
} else {
buffer
}
}
context.become(
collaborativeEditing(
newBuffer.getOrElse(buffer),
clients,
lockHolder,
autoSave
)
)
case Left(failure) =>
logger.error(
"Failed to read file attributes for [{}]. {}",
path,
failure
)
}
unstashAll()
context.become(
collaborativeEditing(
newBuffer.getOrElse(buffer),
clients,
lockHolder,
autoSave
)
)
case FileManagerProtocol.InfoFileResult(Left(err)) =>
timeoutCancellable.cancel()
logger.error("Failed to read file attributes for [{}]. {}", path, err)
unstashAll()
context.become(
collaborativeEditing(buffer, clients, lockHolder, autoSave)
)
case Status.Failure(ex) =>
logger.error("Failed to read file attributes for [{}].", path, ex)
unstashAll()
context.become(
collaborativeEditing(buffer, clients, lockHolder, autoSave)
)
case IOTimeout =>
unstashAll()
context.become(
collaborativeEditing(buffer, clients, lockHolder, autoSave)
)
case _ => stash()
}
private def waitingOnReloadedContent(
@ -444,7 +400,11 @@ class CollaborativeBuffer(
case FileManagerProtocol.ReadFileWithAttributesResult(Left(FileNotFound)) =>
clients.values.foreach {
_.rpcController ! TextProtocol.FileEvent(path, FileEventKind.Removed)
_.rpcController ! TextProtocol.FileEvent(
path,
FileEventKind.Removed,
None
)
}
replyTo ! ReloadedBuffer(path)
timeoutCancellable.cancel()

View File

@ -2,6 +2,7 @@ package org.enso.languageserver.text
import org.enso.languageserver.data.{CapabilityRegistration, ClientId}
import org.enso.languageserver.filemanager.{
FileAttributes,
FileEventKind,
FileSystemFailure,
Path
@ -151,8 +152,14 @@ object TextProtocol {
* a file event after reloading the buffer to sync with file system
*
* @param path path to the file
* @param kind file event kind
* @param attributes file attributes
*/
case class FileEvent(path: Path, event: FileEventKind)
case class FileEvent(
path: Path,
kind: FileEventKind,
attributes: Option[FileAttributes]
)
/** Requests the language server to save a file on behalf of a given user.
*

View File

@ -84,7 +84,7 @@ class ReceivesTreeUpdatesHandlerTest
// create file
val path = Paths.get(testContentRoot.file.toString, "oneone.txt")
Files.createFile(path)
client1.expectJson(json"""
client1.fuzzyExpectJson(json"""
{ "jsonrpc": "2.0",
"method": "file/event",
"params": {
@ -92,11 +92,12 @@ class ReceivesTreeUpdatesHandlerTest
"rootId": $testContentRootId,
"segments": [ "oneone.txt" ]
},
"kind": "Added"
"kind": "Added",
"attributes": "*"
}
}
""")
client2.expectJson(json"""
client2.fuzzyExpectJson(json"""
{ "jsonrpc": "2.0",
"method": "file/event",
"params": {
@ -104,14 +105,15 @@ class ReceivesTreeUpdatesHandlerTest
"rootId": $testContentRootId,
"segments": [ "oneone.txt" ]
},
"kind": "Added"
"kind": "Added",
"attributes": "*"
}
}
""")
// update file
Files.write(path, "Hello".getBytes())
client1.expectJson(json"""
client1.fuzzyExpectJson(json"""
{ "jsonrpc": "2.0",
"method": "file/event",
"params": {
@ -119,11 +121,12 @@ class ReceivesTreeUpdatesHandlerTest
"rootId": $testContentRootId,
"segments": [ "oneone.txt" ]
},
"kind": "Modified"
"kind": "Modified",
"attributes": "*"
}
}
""")
client2.expectJson(json"""
client2.fuzzyExpectJson(json"""
{ "jsonrpc": "2.0",
"method": "file/event",
"params": {
@ -131,7 +134,8 @@ class ReceivesTreeUpdatesHandlerTest
"rootId": $testContentRootId,
"segments": [ "oneone.txt" ]
},
"kind": "Modified"
"kind": "Modified",
"attributes": "*"
}
}
""")
@ -150,7 +154,8 @@ class ReceivesTreeUpdatesHandlerTest
"rootId": $testContentRootId,
"segments": [ "oneone.txt" ]
},
"kind": "Removed"
"kind": "Removed",
"attributes": null
}
}
""")

View File

@ -1324,7 +1324,8 @@ class VcsManagerTest
$testBarFileName
]
},
"kind" : "Removed"
"kind" : "Removed",
"attributes" : null
}
}
"""
@ -1354,7 +1355,8 @@ class VcsManagerTest
$testBarFileName
]
},
"kind" : "Removed"
"kind" : "Removed",
"attributes" : null
}
}
""")