mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 21:12:44 +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
|
||||
TBC
|
||||
- [`ContentRootNotFoundError`](#contentrootnotfounderror) to signal that the
|
||||
requested content root cannot be found.
|
||||
|
||||
#### `file/tree`
|
||||
This request asks the file manager component to generate and provide the
|
||||
@ -1599,7 +1600,7 @@ null
|
||||
```
|
||||
|
||||
##### Errors
|
||||
- [`FileNotOpenedError`](#filenotopenederror) to signal that a file wasn't
|
||||
- [`FileNotOpenedError`](#filenotopenederror) to signal that a file wasn't
|
||||
opened.
|
||||
|
||||
#### `text/save`
|
||||
@ -1766,7 +1767,7 @@ the language server. This is incredibly important for enabling the high levels
|
||||
of interactivity required by Enso Studio.
|
||||
|
||||
#### 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.
|
||||
|
||||
##### `ExpressionId`
|
||||
@ -2051,4 +2052,4 @@ Signals that a file wasn't opened.
|
||||
"code" : 3001,
|
||||
"message" : "File not opened"
|
||||
}
|
||||
```
|
||||
```
|
||||
|
@ -52,6 +52,7 @@ object ClientApi {
|
||||
.registerRequest(CloseFile)
|
||||
.registerRequest(DeleteFile)
|
||||
.registerRequest(CopyFile)
|
||||
.registerRequest(ExistsFile)
|
||||
.registerNotification(ForceReleaseCapability)
|
||||
.registerNotification(GrantCapability)
|
||||
|
||||
@ -130,6 +131,9 @@ class ClientController(
|
||||
|
||||
case Request(CopyFile, id, params: CopyFile.Params) =>
|
||||
copyFile(webActor, id, params)
|
||||
|
||||
case Request(ExistsFile, id, params: ExistsFile.Params) =>
|
||||
existsFile(webActor, id, params)
|
||||
}
|
||||
|
||||
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 ()
|
||||
|
||||
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]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -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
|
||||
|
||||
case class FileSystemError(override val message: String)
|
||||
|
@ -74,4 +74,17 @@ object FileManagerProtocol {
|
||||
*/
|
||||
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 = {
|
||||
case _: FileNotFoundException => FileNotFound
|
||||
case _: NoSuchFileException => FileNotFound
|
||||
|
@ -66,4 +66,12 @@ trait FileSystemApi[F[_]] {
|
||||
to: File
|
||||
): 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
|
||||
}
|
||||
|
||||
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 = {
|
||||
val buffer = Source.fromFile(path.toFile)
|
||||
val content = buffer.getLines().mkString
|
||||
|
@ -507,6 +507,35 @@ class FileManagerTest extends WebSocketServerTest {
|
||||
val to = Paths.get(testContentRoot.toString, "some", "test.txt")
|
||||
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