Skip to content

Commit

Permalink
Refactor the LS to support multiple content roots (#1800)
Browse files Browse the repository at this point in the history
  • Loading branch information
iamrecursion committed Jun 24, 2021
1 parent 3905f22 commit ed84cde
Show file tree
Hide file tree
Showing 26 changed files with 474 additions and 377 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ jobs:
run: |
sleep 1
sbt --no-colors "launcher/assembly"
sbt --no-colors --mem 1536 "launcher-manager/buildNativeImage"
sbt --no-colors --mem 1536 "launcher/buildNativeImage"
- name: Build the PM Native Image
working-directory: repo
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/scala.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,15 @@ jobs:
run: |
echo "CI_TEST_TIMEFACTOR=2" >> $GITHUB_ENV
echo "CI_TEST_FLAKY_ENABLE=true" >> $GITHUB_ENV
- name: Build the Launcher
- name: Build the Launcher Native Image
run: |
sleep 1
sbt --no-colors "launcher/assembly"
sbt --no-colors --mem 1536 "launcher/buildNativeImage"
- name: Build the PM Native Image
run: |
sbt --no-colors "project-manager/assembly"
sbt --no-colors --mem 1536 "project-manager/buildNativeImage"
- name: Build the Runner & Runtime Uberjars
run: |
Expand Down
3 changes: 3 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
- Added support for reading and writing byte ranges in files remotely
([#1795](https://github.com/enso-org/enso/pull/1795)). This allows the IDE to
transfer files to a remote back-end in a streaming fashion.
- Added support for multiple content roots in the language server
([#1800](https://github.com/enso-org/enso/pull/1800/)). It is not yet exposed
to the IDE, as this will be done as part of future work.

## Libraries

Expand Down
44 changes: 40 additions & 4 deletions docs/language-server/protocol-language-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -1035,11 +1035,11 @@ A representation of the contents of a file.
#### Format

```typescript
interface FileContents[T] {
interface FileContents<T> {
contents: T;
}

class TextFileContents extends FileContents[String];
class TextFileContents extends FileContents<String> {}
```

### `FileSystemObject`
Expand Down Expand Up @@ -1147,6 +1147,42 @@ table FileSegment {
The `byteOffset` property is zero-indexed, so the last byte in the file is at
index `file.length - 1`.

### `ContentRoot`

A representation of a content root for use in the IDE. A content root represents
a location on a real file-system that has been virtualised for use in the Enso
VFS.

```typescript
interface ContentRoot {
// A unique identifier for the content root.
id: UUID;
// The type of content root.
type: ContentRootType;

// The name of the content root.
name: String;
}
```

### `ContentRootType`

The type of the annotated content root.

```typescript
type ContentRootType = Project | Root | Home | Library | Custom;
```

These represent:

- `Project`: This content root points to the project home.
- `Root`: This content root points to the system root (`/`) on unix systems, or
to a drive root on Windows. In Windows' case, there may be multiple `Root`
entries corresponding to the various drives.
- `Home`: The user's home directory.
- `Library`: An Enso library location.
- `Custom`: A content root that has been added by the IDE (unused for now).

## Connection Management

In order to properly set-up and tear-down the language server connection, we
Expand Down Expand Up @@ -1469,7 +1505,7 @@ must fail.
```typescript
{
path: Path;
contents: FileContents[T];
contents: FileContents<T>;
}
```

Expand Down Expand Up @@ -1513,7 +1549,7 @@ return the contents from the in-memory buffer rather than the file on disk.

```typescript
{
contents: FileContents[T];
contents: FileContents<T>;
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ package org.enso.languageserver.boot

import java.io.File
import java.net.URI

import akka.actor.ActorSystem
import org.enso.jsonrpc.JsonRpcServer
import org.enso.languageserver.boot.DeploymentType.{Azure, Desktop}
import org.enso.languageserver.capability.CapabilityRouter
import org.enso.languageserver.data._
import org.enso.languageserver.effect.ZioExec
import org.enso.languageserver.filemanager.{
ContentRootType,
ContentRootWithFile,
FileManager,
FileSystem,
ReceivesTreeUpdatesHandler
Expand Down Expand Up @@ -56,9 +57,15 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
logLevel
)

val directoriesConfig = DirectoriesConfig(serverConfig.contentRootPath)
val directoriesConfig = ProjectDirectoriesConfig(serverConfig.contentRootPath)
private val contentRoot = ContentRootWithFile(
serverConfig.contentRootUuid,
ContentRootType.Project,
"Project",
new File(serverConfig.contentRootPath)
)
val languageServerConfig = Config(
Map(serverConfig.contentRootUuid -> new File(serverConfig.contentRootPath)),
Map(serverConfig.contentRootUuid -> contentRoot),
FileManagerConfig(timeout = 3.seconds),
PathWatcherConfig(),
ExecutionContextConfig(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import org.enso.languageserver.boot.resource.{
SequentialResourcesInitialization,
TruffleContextInitialization
}
import org.enso.languageserver.data.DirectoriesConfig
import org.enso.languageserver.data.ProjectDirectoriesConfig
import org.enso.searcher.sql.{SqlSuggestionsRepo, SqlVersionsRepo}
import org.graalvm.polyglot.Context

Expand All @@ -29,11 +29,11 @@ object ResourcesInitialization {
* @return the initialization component
*/
def apply(
eventStream: EventStream,
directoriesConfig: DirectoriesConfig,
suggestionsRepo: SqlSuggestionsRepo,
versionsRepo: SqlVersionsRepo,
truffleContext: Context
eventStream: EventStream,
directoriesConfig: ProjectDirectoriesConfig,
suggestionsRepo: SqlSuggestionsRepo,
versionsRepo: SqlVersionsRepo,
truffleContext: Context
)(implicit ec: ExecutionContext): InitializationComponent = {
val resources = Seq(
new DirectoriesInitialization(directoriesConfig),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package org.enso.languageserver.boot.resource

import com.typesafe.scalalogging.LazyLogging
import org.enso.languageserver.data.DirectoriesConfig
import org.enso.languageserver.data.ProjectDirectoriesConfig

import scala.concurrent.{ExecutionContext, Future}

/** Directories initialization.
*
* @param directoriesConfig the directories config
*/
class DirectoriesInitialization(directoriesConfig: DirectoriesConfig)(implicit
ec: ExecutionContext
class DirectoriesInitialization(directoriesConfig: ProjectDirectoriesConfig)(implicit
ec: ExecutionContext
) extends InitializationComponent
with LazyLogging {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import java.nio.file.{FileSystemException, Files, NoSuchFileException}
import akka.event.EventStream
import com.typesafe.scalalogging.LazyLogging
import org.apache.commons.io.FileUtils
import org.enso.languageserver.data.DirectoriesConfig
import org.enso.languageserver.data.ProjectDirectoriesConfig
import org.enso.languageserver.event.InitializedEvent
import org.enso.logger.masking.MaskedPath
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo, SqlVersionsRepo}
Expand All @@ -23,10 +23,10 @@ import scala.util.{Failure, Success}
* @param versionsRepo the versions repo
*/
class RepoInitialization(
directoriesConfig: DirectoriesConfig,
eventStream: EventStream,
suggestionsRepo: SqlSuggestionsRepo,
versionsRepo: SqlVersionsRepo
directoriesConfig: ProjectDirectoriesConfig,
eventStream: EventStream,
suggestionsRepo: SqlSuggestionsRepo,
versionsRepo: SqlVersionsRepo
)(implicit ec: ExecutionContext)
extends InitializationComponent
with LazyLogging {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package org.enso.languageserver.data
import java.io.File
import java.nio.file.Files
import java.util.UUID

import org.enso.languageserver.filemanager.{
ContentRootNotFound,
ContentRootWithFile,
FileSystemFailure,
Path
}
Expand Down Expand Up @@ -78,15 +78,15 @@ object ExecutionContextConfig {
*
* @param root the root directory path
*/
case class DirectoriesConfig(root: File) extends ToLogString {
case class ProjectDirectoriesConfig(root: File) extends ToLogString {

/** The data directory path. */
val dataDirectory: File =
new File(root, DirectoriesConfig.DataDirectory)
new File(root, ProjectDirectoriesConfig.DataDirectory)

/** The suggestions database file path. */
val suggestionsDatabaseFile: File =
new File(dataDirectory, DirectoriesConfig.SuggestionsDatabaseFile)
new File(dataDirectory, ProjectDirectoriesConfig.SuggestionsDatabaseFile)

/** @inheritdoc */
override def toLogString(shouldMask: Boolean): String = {
Expand All @@ -101,21 +101,21 @@ case class DirectoriesConfig(root: File) extends ToLogString {
Files.createDirectories(dataDirectory.toPath)
}

object DirectoriesConfig {
object ProjectDirectoriesConfig {

val DataDirectory: String = ".enso"
val SuggestionsDatabaseFile: String = "suggestions.db"

def apply(root: String): DirectoriesConfig =
new DirectoriesConfig(new File(root))
def apply(root: String): ProjectDirectoriesConfig =
new ProjectDirectoriesConfig(new File(root))

/** Create default data directory config, creating directories if not exist.
*
* @param root the root directory path
* @return data directory config
*/
def initialize(root: File): DirectoriesConfig = {
val config = new DirectoriesConfig(root)
def initialize(root: File): ProjectDirectoriesConfig = {
val config = new ProjectDirectoriesConfig(root)
config.createDirectories()
config
}
Expand All @@ -131,11 +131,11 @@ object DirectoriesConfig {
* @param directories the configuration of internal directories
*/
case class Config(
contentRoots: Map[UUID, File],
contentRoots: Map[UUID, ContentRootWithFile],
fileManager: FileManagerConfig,
pathWatcher: PathWatcherConfig,
executionContext: ExecutionContextConfig,
directories: DirectoriesConfig
directories: ProjectDirectoriesConfig
) extends ToLogString {

/** @inheritdoc */
Expand All @@ -144,7 +144,7 @@ case class Config(
if (shouldMask) {
contentRoots
.map { case (k, v) =>
k -> MaskingUtils.toMaskedPath(v.toPath)
k -> MaskingUtils.toMaskedPath(v.file.toPath)
}
} else {
contentRoots
Expand All @@ -158,15 +158,17 @@ case class Config(
s")"
}

def findContentRoot(rootId: UUID): Either[FileSystemFailure, File] =
def findContentRoot(
rootId: UUID
): Either[FileSystemFailure, ContentRootWithFile] =
contentRoots
.get(rootId)
.toRight(ContentRootNotFound)

def findRelativePath(path: File): Option[Path] =
contentRoots.view.flatMap { case (id, root) =>
if (path.toPath.startsWith(root.toPath)) {
Some(Path(id, root.toPath.relativize(path.toPath)))
if (path.toPath.startsWith(root.file.toPath)) {
Some(Path(id, root.file.toPath.relativize(path.toPath)))
} else {
None
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.enso.languageserver.filemanager

import enumeratum._

import java.io.File
import java.util.UUID

/** A representation of a content root.
*
* @param id the unique identifier of the content root
* @param type the type of the content root
* @param name The name of the content root
*/
case class ContentRoot(id: UUID, `type`: ContentRootType, name: String)

/** The type of entity that the content root represents.
*/
sealed trait ContentRootType extends EnumEntry
object ContentRootType extends Enum[ContentRootType] with CirceEnum[ContentRootType] {

/** The content root represents the root of the current Enso project.
*/
case object Project extends ContentRootType

/** The content root represents a system root (`/` on unix, drives on
* windows).
*
* There may be multiple of this type of root sent by default.
*/
case object Root extends ContentRootType

/** The content root represents the user's home directory.
*/
case object Home extends ContentRootType

/** The content root represents an Enso library.
*/
case object Library extends ContentRootType

/** The content root was a custom location added by the IDE.
*/
case object Custom extends ContentRootType

/** Necessary for Enumeratum and Circe. */
override val values = findValues
}

/** A representation of a content root.
*
* @param id the unique identifier of the content root
* @param `type` the type of the content root
* @param name The name of the content root
* @param file the file on the filesystem that is the content root
*/
case class ContentRootWithFile(
id: UUID,
`type`: ContentRootType,
name: String,
file: File
) {

/** Convert this to a content root for use in the protocol.
*
* @return a protocol content root
*/
def toContentRoot: ContentRoot = {
ContentRoot(id, `type`, name)
}
}

0 comments on commit ed84cde

Please sign in to comment.