Skip to content

Commit

Permalink
Custom requests (#188)
Browse files Browse the repository at this point in the history
* Add handling of custom requests/notifications
* Add Jakub's talk and Badlang
  • Loading branch information
keynmol authored Nov 30, 2023
1 parent 4079bad commit 5981e3b
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 20 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ lazy val app = projectMatrix

lazy val `e2e-tests` = projectMatrix
.in(file("modules/e2e-tests"))
.dependsOn(app)
.dependsOn(app, example)
.defaultAxes(V.default*)
.settings(enableSnapshots)
.jvmPlatform(
Expand Down
10 changes: 10 additions & 0 deletions docs/_docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,13 @@ Designed to serve as _the_ testbed for Langoustine changes, it's a small primiti
arithmetic language with associated set of tooling and built-in LSP.

This particular example runs on the JVM and uses Scala CLI exclusively for packaging.

## Another LSP for a toy language

[Badlang](https://github.com/kubukoz/badlang)

Made by [Jakub Kozłowski](https://twitter.com/kubukoz/) for various versions of his talk about
building a language and a LSP for it.

Check out the [talk at Lambda Days 2023](https://www.youtube.com/watch?v=HF0xVrBZqtI) which also
showcases the usage of Langoustine Tracer.
20 changes: 15 additions & 5 deletions modules/e2e-tests/src/test/scala/EndToEndTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ import jsonrpclib.Payload

import cats.syntax.all.*
import scala.concurrent.Promise
import cats.effect.std.Dispatcher.apply
import cats.effect.std.Dispatcher
import cats.effect.std.Semaphore.apply
import cats.effect.std.Semaphore
import cats.effect.std.*
import cats.effect.kernel.Ref
import cats.effect.kernel.Deferred
import cats.effect.kernel.Resource
import java.lang.management.ManagementFactory

import langoustine.example.{MyCustomRequest, MyCustomNotification}

case class Result(code: Int, stdout: List[String], stderr: List[String])

object EndToEndTests extends SimpleIOSuite:
Expand Down Expand Up @@ -78,7 +77,10 @@ object EndToEndTests extends SimpleIOSuite:
)
)

val send = str + str1 + str2
val str3 = sendNotification(MyCustomNotification, "hello!")
val str4 = sendRequest(MyCustomRequest, "hello request!")

val send = str + str1 + str2 + str3 + str4

val timeout = 5.seconds

Expand Down Expand Up @@ -117,6 +119,14 @@ object EndToEndTests extends SimpleIOSuite:
ShowMessageParams(
MessageType.Info,
"In total, 2 files registered!"
),
ShowMessageParams(
MessageType.Info,
"Received custom notification: hello!"
),
ShowMessageParams(
MessageType.Info,
"Received custom request: hello request!"
)
),
initialized == Vector(
Expand Down
18 changes: 17 additions & 1 deletion modules/example/src/main/scala/Example.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package langoustine.example

import langoustine.lsp.*
import langoustine.lsp.all.*
import langoustine.lsp.app.*
Expand Down Expand Up @@ -29,6 +31,20 @@ def myLSP(files: Ref[IO, Set[String]]) =
sendMessage(in.toClient, s"In total, $count files registered!")
}
}
.handleNotification(MyCustomNotification) { in =>
sendMessage(in.toClient, s"Received custom notification: ${in.params}")
}
.handleRequest(MyCustomRequest) { in =>
sendMessage(in.toClient, s"Received custom request: ${in.params}") *>
files.get.map(_.size)
}

object MyCustomNotification
extends CustomNotification[String]("custom/myNotification")

object MyCustomRequest extends CustomRequest[String, Int]("custom/myRequest")

def sendMessage(back: Communicate[IO], msg: String) =
back.notification(window.showMessage(ShowMessageParams(MessageType.Info, msg)))
back.notification(
window.showMessage(ShowMessageParams(MessageType.Info, msg))
)
56 changes: 43 additions & 13 deletions modules/generate/src/main/scala/render.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,48 @@ class Render(manager: Manager, packageName: String = "langoustine.lsp"):
}

val requestPrelude = """
|sealed abstract class LSPRequest(val requestMethod: String):
| type In
| type Out
|
| given inputReader: Reader[In]
| given inputWriter: Writer[In]
| given outputWriter: Writer[Out]
| given outputReader: Reader[Out]
|
| def apply(in: In): PreparedRequest[this.type] = PreparedRequest(this,in)
""".stripMargin.trim
|sealed abstract class LSPRequest(val requestMethod: String):
| type In
| type Out
|
| given inputReader: Reader[In]
| given inputWriter: Writer[In]
| given outputWriter: Writer[Out]
| given outputReader: Reader[Out]
|
| def apply(in: In): PreparedRequest[this.type] = PreparedRequest(this,in)
""".stripMargin.trim

requestPrelude.linesIterator.foreach(line)

line("")

val customRequestPrelude =
"""
|abstract class CustomRequest[I, O](method: String)(using ir: ReadWriter[I], or: ReadWriter[O]) extends LSPRequest(method):
| override type In = I
| override type Out = O
|
| override given inputReader: Reader[In] = ir
|
| override given inputWriter: Writer[In] = ir
|
| override given outputWriter: Writer[Out] = or
|
| override given outputReader: Reader[Out] = or
|
|abstract class CustomNotification[I](method: String)(using ir: ReadWriter[I]) extends LSPNotification(method):
| override type In = I
|
| override given inputReader: Reader[In] = ir
|
| override given inputWriter: Writer[In] = ir
""".stripMargin.trim

customRequestPrelude.linesIterator.foreach(line)

line("")

val notificationPrelude = """
|sealed abstract class LSPNotification(val notificationMethod: String):
| type In
Expand Down Expand Up @@ -170,7 +196,9 @@ class Render(manager: Manager, packageName: String = "langoustine.lsp"):

line("")

line(s"override def apply(in: $inTypeStr): PreparedRequest[this.type] = super.apply(in)")
line(
s"override def apply(in: $inTypeStr): PreparedRequest[this.type] = super.apply(in)"
)

summon[Context].inModified(
_.copy(definitionScope = "requests" :: req.segs)
Expand Down Expand Up @@ -285,7 +313,9 @@ class Render(manager: Manager, packageName: String = "langoustine.lsp"):

line("")

line(s"override def apply(in: $inTypeStr): PreparedNotification[this.type] = super.apply(in)")
line(
s"override def apply(in: $inTypeStr): PreparedNotification[this.type] = super.apply(in)"
)

codecsOut.topLevel {
codecsLine("")
Expand Down
19 changes: 19 additions & 0 deletions modules/lsp/src/main/scala/generated/requests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@ object requests:

def apply(in: In): PreparedRequest[this.type] = PreparedRequest(this,in)

abstract class CustomRequest[I, O](method: String)(using ir: ReadWriter[I], or: ReadWriter[O]) extends LSPRequest(method):
override type In = I
override type Out = O

override given inputReader: Reader[In] = ir

override given inputWriter: Writer[In] = ir

override given outputWriter: Writer[Out] = or

override given outputReader: Reader[Out] = or

abstract class CustomNotification[I](method: String)(using ir: ReadWriter[I]) extends LSPNotification(method):
override type In = I

override given inputReader: Reader[In] = ir

override given inputWriter: Writer[In] = ir

sealed abstract class LSPNotification(val notificationMethod: String):
type In

Expand Down

0 comments on commit 5981e3b

Please sign in to comment.