Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add better handling of broken/missing build files #65

Merged
merged 4 commits into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lsp/src/main/scala/playground/lsp/BuildLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ object BuildLoader {

case class Loaded(config: BuildConfig, configFilePath: Path)

object Loaded {
// Path is irrelevant when no imports are provided.
val default: Loaded = Loaded(BuildConfig(), Path("/"))
}

def instance[F[_]: TextDocumentProvider: Sync]: BuildLoader[F] =
new BuildLoader[F] {

Expand Down
75 changes: 47 additions & 28 deletions lsp/src/main/scala/playground/lsp/LanguageServer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import smithy4s.dynamic.DynamicSchemaIndex

import scala.jdk.CollectionConverters._
import scala.util.chaining._
import playground.lsp.buildinfo.BuildInfo

trait LanguageServer[F[_]] {
def initialize(params: InitializeParams): F[InitializeResult]
Expand Down Expand Up @@ -90,7 +91,21 @@ object LanguageServer {
.tap(_.setDiagnosticProvider(new DiagnosticRegistrationOptions()))
.tap(_.setCodeLensProvider(new CodeLensOptions()))

new InitializeResult(capabilities).pure[F]
LanguageClient[F]
.showInfoMessage(s"Hello from Smithy Playground v${BuildInfo.version}") *>
ServerLoader[F]
.prepare
.flatMap { prepped =>
ServerLoader[F].perform(prepped.params).flatTap { stats =>
LanguageClient[F]
.showInfoMessage(
s"Loaded Smithy Playground server with ${stats.render}"
)
}
}
.onError { case e => LanguageClient[F].showErrorMessage(e.getMessage()) }
.attempt
.as(new InitializeResult(capabilities))
}

def initialized(params: InitializedParams): F[Unit] = Applicative[F].unit
Expand Down Expand Up @@ -200,34 +215,38 @@ object LanguageServer {

def didChangeWatchedFiles(
params: DidChangeWatchedFilesParams
): F[Unit] = ServerLoader[F].prepare.flatMap {
case prepared if !prepared.isChanged =>
LanguageClient[F].showInfoMessage(
"No change detected, not rebuilding server"
): F[Unit] = ServerLoader[F]
.prepare
.flatMap {
case prepared if !prepared.isChanged =>
LanguageClient[F].showInfoMessage(
"No change detected, not rebuilding server"
)
case prepared =>
LanguageClient[F].showInfoMessage("Detected changes, will try to rebuild server...") *>
ServerLoader[F]
.perform(prepared.params)
.onError { case e =>
LanguageClient[F].showErrorMessage(
"Couldn't reload server: " + e.getMessage
)
}
.flatMap { stats =>
// Can't make (and wait for) client requests while handling a client request (file change)
{
LanguageClient[F].refreshDiagnostics *>
LanguageClient[F].refreshCodeLenses *> LanguageClient[F]
.showInfoMessage(
s"Reloaded Smithy Playground server with ${stats.render}"
)
}.supervise(sup).void
}
}
.onError { case e =>
LanguageClient[F].showErrorMessage(
s"Couldn't rebuild server. Check your config file and the output panel.\nError: ${e.getMessage()}"
)
case prepared =>
LanguageClient[F].showInfoMessage("Detected changes, will try to rebuild server...") *>
ServerLoader[F]
.perform(prepared.params)
.onError { case e =>
LanguageClient[F].showErrorMessage(
"Couldn't reload server: " + e.getMessage
)
}
.flatMap { stats =>
// Can't make (and wait for) client requests while handling a client request (file change)
{
LanguageClient[F].refreshDiagnostics *>
LanguageClient[F].refreshCodeLenses *> LanguageClient[F]
.showInfoMessage(
s"Reloaded Smithy Playground server with " +
s"${stats.importCount} imports, " +
s"${stats.dependencyCount} dependencies and " +
s"${stats.pluginCount} plugins"
)
}.supervise(sup).void
}
}
}

def executeCommand(
params: ExecuteCommandParams
Expand Down
3 changes: 0 additions & 3 deletions lsp/src/main/scala/playground/lsp/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import cats.effect.std.Dispatcher
import cats.implicits._
import org.eclipse.lsp4j.launch.LSPLauncher
import playground.TextDocumentManager
import playground.lsp.buildinfo.BuildInfo

import java.io.File
import java.io.FileOutputStream
Expand Down Expand Up @@ -80,8 +79,6 @@ object Main extends IOApp.Simple {

log[IO]("connecting") *>
clientRef.complete(LanguageClient.adapt[IO](launcher.getRemoteProxy())) *>
LanguageClient[IO]
.showInfoMessage(s"Hello from Smithy Playground v${BuildInfo.version}") *>
log[IO]("Server connected")
.as(launcher)
}
Expand Down
28 changes: 20 additions & 8 deletions lsp/src/main/scala/playground/lsp/ServerLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,16 @@ trait ServerLoader[F[_]] {
object ServerLoader {
def apply[F[_]](implicit F: ServerLoader[F]): F.type = F

type Aux[F[_], Params_] = ServerLoader[F] { type Params = Params_ }

case class PrepareResult[A](params: A, isChanged: Boolean)
case class WorkspaceStats(importCount: Int, dependencyCount: Int, pluginCount: Int)

case class WorkspaceStats(importCount: Int, dependencyCount: Int, pluginCount: Int) {

def render: String =
s"$importCount imports, $dependencyCount dependencies and $pluginCount plugins"

}

object WorkspaceStats {

Expand All @@ -28,14 +36,17 @@ object ServerLoader {

}

def instance[F[_]: ServerBuilder: BuildLoader: Ref.Make: MonadThrow]: F[ServerLoader[F]] = {
def instance[
F[_]: ServerBuilder: BuildLoader: Ref.Make: MonadThrow
]: F[ServerLoader.Aux[F, BuildLoader.Loaded]] = {
case class State(currentServer: LanguageServer[F], lastUsedConfig: Option[BuildConfig])
object State {
val initial: State = apply(LanguageServer.notAvailable[F], none)
}

Ref[F].of(State.initial).flatMap { serverRef =>
val instance =
Ref[F]
.of(State.initial)
.map[ServerLoader.Aux[F, BuildLoader.Loaded]] { serverRef =>
new ServerLoader[F] {
type Params = BuildLoader.Loaded

Expand All @@ -54,10 +65,11 @@ object ServerLoader {

val server: LanguageServer[F] = LanguageServer.defer(serverRef.get.map(_.currentServer))
}

// Initial load
BuildLoader[F].load.flatMap(instance.perform).as(instance)
}
}
.flatTap { serverLoader =>
// loading with dummy config to initialize server without dependencies
serverLoader.perform(BuildLoader.Loaded.default)
}
}

}