mirror of
https://github.com/enso-org/enso.git
synced 2024-12-24 03:02:30 +03:00
Add file/exists message to language server (#579)
* feat: impl file/exists * doc: update engine-services * doc: wrap 80
This commit is contained in:
parent
31d5e6eb5b
commit
22f505b6c4
@ -1334,7 +1334,8 @@ at the specified path.
|
|||||||
```
|
```
|
||||||
|
|
||||||
##### Errors
|
##### Errors
|
||||||
TBC
|
- [`ContentRootNotFoundError`](#contentrootnotfounderror) to signal that the
|
||||||
|
requested content root cannot be found.
|
||||||
|
|
||||||
#### `file/tree`
|
#### `file/tree`
|
||||||
This request asks the file manager component to generate and provide the
|
This request asks the file manager component to generate and provide the
|
||||||
@ -1599,7 +1600,7 @@ null
|
|||||||
```
|
```
|
||||||
|
|
||||||
##### Errors
|
##### Errors
|
||||||
- [`FileNotOpenedError`](#filenotopenederror) to signal that a file wasn't
|
- [`FileNotOpenedError`](#filenotopenederror) to signal that a file wasn't
|
||||||
opened.
|
opened.
|
||||||
|
|
||||||
#### `text/save`
|
#### `text/save`
|
||||||
@ -1766,7 +1767,7 @@ the language server. This is incredibly important for enabling the high levels
|
|||||||
of interactivity required by Enso Studio.
|
of interactivity required by Enso Studio.
|
||||||
|
|
||||||
#### Types
|
#### Types
|
||||||
The execution management API exposes a set of common types used by many of its
|
The execution management API exposes a set of common types used by many of its
|
||||||
messages.
|
messages.
|
||||||
|
|
||||||
##### `ExpressionId`
|
##### `ExpressionId`
|
||||||
@ -2051,4 +2052,4 @@ Signals that a file wasn't opened.
|
|||||||
"code" : 3001,
|
"code" : 3001,
|
||||||
"message" : "File not opened"
|
"message" : "File not opened"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -52,6 +52,7 @@ object ClientApi {
|
|||||||
.registerRequest(CloseFile)
|
.registerRequest(CloseFile)
|
||||||
.registerRequest(DeleteFile)
|
.registerRequest(DeleteFile)
|
||||||
.registerRequest(CopyFile)
|
.registerRequest(CopyFile)
|
||||||
|
.registerRequest(ExistsFile)
|
||||||
.registerNotification(ForceReleaseCapability)
|
.registerNotification(ForceReleaseCapability)
|
||||||
.registerNotification(GrantCapability)
|
.registerNotification(GrantCapability)
|
||||||
|
|
||||||
@ -130,6 +131,9 @@ class ClientController(
|
|||||||
|
|
||||||
case Request(CopyFile, id, params: CopyFile.Params) =>
|
case Request(CopyFile, id, params: CopyFile.Params) =>
|
||||||
copyFile(webActor, id, params)
|
copyFile(webActor, id, params)
|
||||||
|
|
||||||
|
case Request(ExistsFile, id, params: ExistsFile.Params) =>
|
||||||
|
existsFile(webActor, id, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def readFile(
|
private def readFile(
|
||||||
@ -243,4 +247,25 @@ class ClientController(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def existsFile(
|
||||||
|
webActor: ActorRef,
|
||||||
|
id: Id,
|
||||||
|
params: ExistsFile.Params
|
||||||
|
): Unit = {
|
||||||
|
(server ? FileManagerProtocol.ExistsFile(params.path))
|
||||||
|
.onComplete {
|
||||||
|
case Success(FileManagerProtocol.ExistsFileResult(Right(exists))) =>
|
||||||
|
webActor ! ResponseResult(ExistsFile, id, ExistsFile.Result(exists))
|
||||||
|
|
||||||
|
case Success(FileManagerProtocol.ExistsFileResult(Left(failure))) =>
|
||||||
|
webActor ! ResponseError(
|
||||||
|
Some(id),
|
||||||
|
FileSystemFailureMapper.mapFailure(failure)
|
||||||
|
)
|
||||||
|
|
||||||
|
case Failure(th) =>
|
||||||
|
log.error("An exception occurred during exists file command", th)
|
||||||
|
webActor ! ResponseError(Some(id), ServiceError)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,15 @@ class LanguageServer(config: Config, fs: FileSystemApi[IO])
|
|||||||
} yield ()
|
} yield ()
|
||||||
|
|
||||||
sender ! CopyFileResult(result)
|
sender ! CopyFileResult(result)
|
||||||
|
|
||||||
|
case ExistsFile(path) =>
|
||||||
|
val result =
|
||||||
|
for {
|
||||||
|
rootPath <- config.findContentRoot(path.rootId)
|
||||||
|
exists <- fs.exists(path.toFile(rootPath)).unsafeRunSync()
|
||||||
|
} yield exists
|
||||||
|
|
||||||
|
sender ! ExistsFileResult(result)
|
||||||
}
|
}
|
||||||
/* Note [Usage of unsafe methods]
|
/* Note [Usage of unsafe methods]
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -77,6 +77,20 @@ object FileManagerApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case object ExistsFile extends Method("file/exists") {
|
||||||
|
|
||||||
|
case class Params(path: Path)
|
||||||
|
|
||||||
|
case class Result(exists: Boolean)
|
||||||
|
|
||||||
|
implicit val hasParams = new HasParams[this.type] {
|
||||||
|
type Params = ExistsFile.Params
|
||||||
|
}
|
||||||
|
implicit val hasResult = new HasResult[this.type] {
|
||||||
|
type Result = ExistsFile.Result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
|
|
||||||
case class FileSystemError(override val message: String)
|
case class FileSystemError(override val message: String)
|
||||||
|
@ -74,4 +74,17 @@ object FileManagerProtocol {
|
|||||||
*/
|
*/
|
||||||
case class CopyFileResult(result: Either[FileSystemFailure, Unit])
|
case class CopyFileResult(result: Either[FileSystemFailure, Unit])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests the Language Server to check the existence of file system object.
|
||||||
|
*
|
||||||
|
* @param path a path to a file
|
||||||
|
*/
|
||||||
|
case class ExistsFile(path: Path)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a result of checking the existence of file system object.
|
||||||
|
*
|
||||||
|
* @param result either file system failure or file existence flag
|
||||||
|
*/
|
||||||
|
case class ExistsFileResult(result: Either[FileSystemFailure, Boolean])
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,21 @@ class FileSystem[F[_]: Sync] extends FileSystemApi[F] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the specified file exists.
|
||||||
|
*
|
||||||
|
* @param file path to the file or directory
|
||||||
|
* @return either [[FileSystemFailure]] or file existence flag
|
||||||
|
*/
|
||||||
|
override def exists(file: File): F[Either[FileSystemFailure, Boolean]] =
|
||||||
|
Sync[F].delay {
|
||||||
|
Either
|
||||||
|
.catchOnly[IOException] {
|
||||||
|
Files.exists(file.toPath)
|
||||||
|
}
|
||||||
|
.leftMap(errorHandling)
|
||||||
|
}
|
||||||
|
|
||||||
private val errorHandling: IOException => FileSystemFailure = {
|
private val errorHandling: IOException => FileSystemFailure = {
|
||||||
case _: FileNotFoundException => FileNotFound
|
case _: FileNotFoundException => FileNotFound
|
||||||
case _: NoSuchFileException => FileNotFound
|
case _: NoSuchFileException => FileNotFound
|
||||||
|
@ -66,4 +66,12 @@ trait FileSystemApi[F[_]] {
|
|||||||
to: File
|
to: File
|
||||||
): F[Either[FileSystemFailure, Unit]]
|
): F[Either[FileSystemFailure, Unit]]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the specified file exists.
|
||||||
|
*
|
||||||
|
* @param file path to the file or directory
|
||||||
|
* @return either [[FileSystemFailure]] or file existence flag
|
||||||
|
*/
|
||||||
|
def exists(file: File): F[Either[FileSystemFailure, Boolean]]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -233,6 +233,27 @@ class FileSystemSpec extends AnyFlatSpec with Matchers {
|
|||||||
to.toFile.exists shouldBe false
|
to.toFile.exists shouldBe false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it should "check file existence" in new TestCtx {
|
||||||
|
//given
|
||||||
|
val path = Paths.get(testDirPath.toString, "foo", "bar.txt")
|
||||||
|
createEmptyFile(path)
|
||||||
|
path.toFile.isFile shouldBe true
|
||||||
|
//when
|
||||||
|
val result = objectUnderTest.exists(path.toFile).unsafeRunSync()
|
||||||
|
//then
|
||||||
|
result shouldBe Right(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "check file non-existence" in new TestCtx {
|
||||||
|
//given
|
||||||
|
val path = Paths.get(testDirPath.toString, "nonexistent.txt")
|
||||||
|
path.toFile.exists shouldBe false
|
||||||
|
//when
|
||||||
|
val result = objectUnderTest.exists(path.toFile).unsafeRunSync()
|
||||||
|
//then
|
||||||
|
result shouldBe Right(false)
|
||||||
|
}
|
||||||
|
|
||||||
def readTxtFile(path: Path): String = {
|
def readTxtFile(path: Path): String = {
|
||||||
val buffer = Source.fromFile(path.toFile)
|
val buffer = Source.fromFile(path.toFile)
|
||||||
val content = buffer.getLines().mkString
|
val content = buffer.getLines().mkString
|
||||||
|
@ -507,6 +507,35 @@ class FileManagerTest extends WebSocketServerTest {
|
|||||||
val to = Paths.get(testContentRoot.toString, "some", "test.txt")
|
val to = Paths.get(testContentRoot.toString, "some", "test.txt")
|
||||||
to.toFile.isFile shouldBe false
|
to.toFile.isFile shouldBe false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"check file existence" in {
|
||||||
|
val client = new WsTestClient(address)
|
||||||
|
val path = Paths.get(testContentRoot.toString, "nonexistent.txt")
|
||||||
|
path.toFile.exists shouldBe false
|
||||||
|
|
||||||
|
// check file exists
|
||||||
|
client.send(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"method": "file/exists",
|
||||||
|
"id": 27,
|
||||||
|
"params": {
|
||||||
|
"path": {
|
||||||
|
"rootId": $testContentRootId,
|
||||||
|
"segments": [ "nonexistent.txt" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
client.expectJson(json"""
|
||||||
|
{ "jsonrpc": "2.0",
|
||||||
|
"id": 27,
|
||||||
|
"result": {
|
||||||
|
"exists": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user