mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 08:21:49 +03:00
Add attributes to filesystem events (#8767)
close #8200 Changelog: - add: `attributes` field to the file event notification
This commit is contained in:
parent
04b0d266e8
commit
51540e2eb2
@ -2350,6 +2350,7 @@ of the (possibly multiple) content roots.
|
|||||||
interface FileEventNotification {
|
interface FileEventNotification {
|
||||||
path: Path;
|
path: Path;
|
||||||
kind: FileEventKind;
|
kind: FileEventKind;
|
||||||
|
attributes?: FileAttributes;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -8,8 +8,13 @@ import org.enso.filewatcher.Watcher
|
|||||||
*
|
*
|
||||||
* @param path path to the file system object
|
* @param path path to the file system object
|
||||||
* @param kind type of file system event
|
* @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 {
|
object FileEvent {
|
||||||
|
|
||||||
@ -18,21 +23,27 @@ object FileEvent {
|
|||||||
* @param root a project root
|
* @param root a project root
|
||||||
* @param base a watched path
|
* @param base a watched path
|
||||||
* @param event a file system event
|
* @param event a file system event
|
||||||
|
* @param attributes a file attributes
|
||||||
* @return file event
|
* @return file event
|
||||||
*/
|
*/
|
||||||
def fromWatcherEvent(
|
def fromWatcherEvent(
|
||||||
root: File,
|
root: File,
|
||||||
base: Path,
|
base: Path,
|
||||||
event: Watcher.WatcherEvent
|
event: Watcher.WatcherEvent,
|
||||||
): FileEvent =
|
attributes: Either[FileSystemFailure, FileSystemApi.Attributes]
|
||||||
|
): FileEvent = {
|
||||||
|
val eventPath = Path.getRelativePath(root, base, event.path)
|
||||||
FileEvent(
|
FileEvent(
|
||||||
Path.getRelativePath(root, base, event.path),
|
eventPath,
|
||||||
FileEventKind(event.eventType)
|
FileEventKind(event.eventType),
|
||||||
|
attributes.map(
|
||||||
|
FileAttributes.fromFileSystemAttributes(root, eventPath, _)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Type of a file event.
|
/** Type of a file event. */
|
||||||
*/
|
|
||||||
sealed trait FileEventKind extends EnumEntry
|
sealed trait FileEventKind extends EnumEntry
|
||||||
|
|
||||||
object FileEventKind extends Enum[FileEventKind] with CirceEnum[FileEventKind] {
|
object FileEventKind extends Enum[FileEventKind] with CirceEnum[FileEventKind] {
|
||||||
|
@ -183,7 +183,11 @@ object FileManagerApi {
|
|||||||
|
|
||||||
case object EventFile extends Method("file/event") {
|
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] =
|
implicit val hasParams: HasParams.Aux[this.type, EventFile.Params] =
|
||||||
new HasParams[this.type] {
|
new HasParams[this.type] {
|
||||||
|
@ -140,7 +140,17 @@ final class PathWatcher(
|
|||||||
|
|
||||||
case e: Watcher.WatcherEvent =>
|
case e: Watcher.WatcherEvent =>
|
||||||
restartCounter.reset()
|
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))
|
clients.foreach(_ ! FileEventResult(event))
|
||||||
context.system.eventStream.publish(event)
|
context.system.eventStream.publish(event)
|
||||||
|
|
||||||
|
@ -354,13 +354,16 @@ class JsonConnectionController(
|
|||||||
FileModifiedOnDisk.Params(path)
|
FileModifiedOnDisk.Params(path)
|
||||||
)
|
)
|
||||||
|
|
||||||
case TextProtocol.FileEvent(path, event) =>
|
case TextProtocol.FileEvent(path, event, attributes) =>
|
||||||
webActor ! Notification(EventFile, EventFile.Params(path, event))
|
webActor ! Notification(
|
||||||
|
EventFile,
|
||||||
|
EventFile.Params(path, event, attributes)
|
||||||
|
)
|
||||||
|
|
||||||
case PathWatcherProtocol.FileEventResult(event) =>
|
case PathWatcherProtocol.FileEventResult(event) =>
|
||||||
webActor ! Notification(
|
webActor ! Notification(
|
||||||
EventFile,
|
EventFile,
|
||||||
EventFile.Params(event.path, event.kind)
|
EventFile.Params(event.path, event.kind, event.attributes.toOption)
|
||||||
)
|
)
|
||||||
|
|
||||||
case ContextRegistryProtocol
|
case ContextRegistryProtocol
|
||||||
|
@ -215,7 +215,7 @@ class BufferRegistry(
|
|||||||
registry
|
registry
|
||||||
)
|
)
|
||||||
|
|
||||||
case msg @ FileEvent(path, kind) =>
|
case msg @ FileEvent(path, kind, _) =>
|
||||||
if (kind == FileEventKind.Added || kind == FileEventKind.Modified) {
|
if (kind == FileEventKind.Added || kind == FileEventKind.Modified) {
|
||||||
registry.get(path).foreach { buffer =>
|
registry.get(path).foreach { buffer =>
|
||||||
buffer ! msg
|
buffer ! msg
|
||||||
|
@ -324,81 +324,37 @@ class CollaborativeBuffer(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
case FileEvent(path, _) =>
|
case FileEvent(path, _, attributes) =>
|
||||||
fileManager ! FileManagerProtocol.InfoFile(path)
|
attributes match {
|
||||||
val timeoutCancellable = context.system.scheduler.scheduleOnce(
|
case Right(attrs) =>
|
||||||
timingsConfig.requestTimeout,
|
val newBuffer = buffer.fileWithMetadata.lastModifiedTime.map {
|
||||||
self,
|
bufferLastModifiedTime =>
|
||||||
IOTimeout
|
if (attrs.lastModifiedTime.isAfter(bufferLastModifiedTime)) {
|
||||||
)
|
clients.values.foreach {
|
||||||
context.become(
|
_.rpcController ! FileModifiedOnDisk(path)
|
||||||
waitingOnFileEventContent(
|
}
|
||||||
path,
|
buffer
|
||||||
buffer,
|
.withLastModifiedTime(attrs.lastModifiedTime)
|
||||||
timeoutCancellable,
|
.withModifiedOnDisk()
|
||||||
clients,
|
} else {
|
||||||
lockHolder,
|
buffer
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
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(
|
private def waitingOnReloadedContent(
|
||||||
@ -444,7 +400,11 @@ class CollaborativeBuffer(
|
|||||||
|
|
||||||
case FileManagerProtocol.ReadFileWithAttributesResult(Left(FileNotFound)) =>
|
case FileManagerProtocol.ReadFileWithAttributesResult(Left(FileNotFound)) =>
|
||||||
clients.values.foreach {
|
clients.values.foreach {
|
||||||
_.rpcController ! TextProtocol.FileEvent(path, FileEventKind.Removed)
|
_.rpcController ! TextProtocol.FileEvent(
|
||||||
|
path,
|
||||||
|
FileEventKind.Removed,
|
||||||
|
None
|
||||||
|
)
|
||||||
}
|
}
|
||||||
replyTo ! ReloadedBuffer(path)
|
replyTo ! ReloadedBuffer(path)
|
||||||
timeoutCancellable.cancel()
|
timeoutCancellable.cancel()
|
||||||
|
@ -2,6 +2,7 @@ package org.enso.languageserver.text
|
|||||||
|
|
||||||
import org.enso.languageserver.data.{CapabilityRegistration, ClientId}
|
import org.enso.languageserver.data.{CapabilityRegistration, ClientId}
|
||||||
import org.enso.languageserver.filemanager.{
|
import org.enso.languageserver.filemanager.{
|
||||||
|
FileAttributes,
|
||||||
FileEventKind,
|
FileEventKind,
|
||||||
FileSystemFailure,
|
FileSystemFailure,
|
||||||
Path
|
Path
|
||||||
@ -151,8 +152,14 @@ object TextProtocol {
|
|||||||
* a file event after reloading the buffer to sync with file system
|
* a file event after reloading the buffer to sync with file system
|
||||||
*
|
*
|
||||||
* @param path path to the file
|
* @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.
|
/** Requests the language server to save a file on behalf of a given user.
|
||||||
*
|
*
|
||||||
|
@ -84,7 +84,7 @@ class ReceivesTreeUpdatesHandlerTest
|
|||||||
// create file
|
// create file
|
||||||
val path = Paths.get(testContentRoot.file.toString, "oneone.txt")
|
val path = Paths.get(testContentRoot.file.toString, "oneone.txt")
|
||||||
Files.createFile(path)
|
Files.createFile(path)
|
||||||
client1.expectJson(json"""
|
client1.fuzzyExpectJson(json"""
|
||||||
{ "jsonrpc": "2.0",
|
{ "jsonrpc": "2.0",
|
||||||
"method": "file/event",
|
"method": "file/event",
|
||||||
"params": {
|
"params": {
|
||||||
@ -92,11 +92,12 @@ class ReceivesTreeUpdatesHandlerTest
|
|||||||
"rootId": $testContentRootId,
|
"rootId": $testContentRootId,
|
||||||
"segments": [ "oneone.txt" ]
|
"segments": [ "oneone.txt" ]
|
||||||
},
|
},
|
||||||
"kind": "Added"
|
"kind": "Added",
|
||||||
|
"attributes": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
client2.expectJson(json"""
|
client2.fuzzyExpectJson(json"""
|
||||||
{ "jsonrpc": "2.0",
|
{ "jsonrpc": "2.0",
|
||||||
"method": "file/event",
|
"method": "file/event",
|
||||||
"params": {
|
"params": {
|
||||||
@ -104,14 +105,15 @@ class ReceivesTreeUpdatesHandlerTest
|
|||||||
"rootId": $testContentRootId,
|
"rootId": $testContentRootId,
|
||||||
"segments": [ "oneone.txt" ]
|
"segments": [ "oneone.txt" ]
|
||||||
},
|
},
|
||||||
"kind": "Added"
|
"kind": "Added",
|
||||||
|
"attributes": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
// update file
|
// update file
|
||||||
Files.write(path, "Hello".getBytes())
|
Files.write(path, "Hello".getBytes())
|
||||||
client1.expectJson(json"""
|
client1.fuzzyExpectJson(json"""
|
||||||
{ "jsonrpc": "2.0",
|
{ "jsonrpc": "2.0",
|
||||||
"method": "file/event",
|
"method": "file/event",
|
||||||
"params": {
|
"params": {
|
||||||
@ -119,11 +121,12 @@ class ReceivesTreeUpdatesHandlerTest
|
|||||||
"rootId": $testContentRootId,
|
"rootId": $testContentRootId,
|
||||||
"segments": [ "oneone.txt" ]
|
"segments": [ "oneone.txt" ]
|
||||||
},
|
},
|
||||||
"kind": "Modified"
|
"kind": "Modified",
|
||||||
|
"attributes": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
client2.expectJson(json"""
|
client2.fuzzyExpectJson(json"""
|
||||||
{ "jsonrpc": "2.0",
|
{ "jsonrpc": "2.0",
|
||||||
"method": "file/event",
|
"method": "file/event",
|
||||||
"params": {
|
"params": {
|
||||||
@ -131,7 +134,8 @@ class ReceivesTreeUpdatesHandlerTest
|
|||||||
"rootId": $testContentRootId,
|
"rootId": $testContentRootId,
|
||||||
"segments": [ "oneone.txt" ]
|
"segments": [ "oneone.txt" ]
|
||||||
},
|
},
|
||||||
"kind": "Modified"
|
"kind": "Modified",
|
||||||
|
"attributes": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
@ -150,7 +154,8 @@ class ReceivesTreeUpdatesHandlerTest
|
|||||||
"rootId": $testContentRootId,
|
"rootId": $testContentRootId,
|
||||||
"segments": [ "oneone.txt" ]
|
"segments": [ "oneone.txt" ]
|
||||||
},
|
},
|
||||||
"kind": "Removed"
|
"kind": "Removed",
|
||||||
|
"attributes": null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
@ -1324,7 +1324,8 @@ class VcsManagerTest
|
|||||||
$testBarFileName
|
$testBarFileName
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"kind" : "Removed"
|
"kind" : "Removed",
|
||||||
|
"attributes" : null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
@ -1354,7 +1355,8 @@ class VcsManagerTest
|
|||||||
$testBarFileName
|
$testBarFileName
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"kind" : "Removed"
|
"kind" : "Removed",
|
||||||
|
"attributes" : null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
Loading…
Reference in New Issue
Block a user