Skip to content

Commit 37c705f

Browse files
Add inlay hints for showing omitted function return types (#1011)
Fixes #869. The attached text edits also suffer from #1001.
1 parent 9c4520e commit 37c705f

File tree

3 files changed

+86
-11
lines changed

3 files changed

+86
-11
lines changed

effekt/jvm/src/main/scala/effekt/Server.scala

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import effekt.source.{Span, Tree}
88
import effekt.symbols.Binder.{ValBinder, VarBinder}
99
import effekt.symbols.BlockTypeConstructor.{ExternInterface, Interface}
1010
import effekt.symbols.TypeConstructor.{DataType, ExternType}
11-
import effekt.symbols.{Anon, Binder, Callable, Effects, Module, Param, Symbol, TypeAlias, TypePrinter, UserFunction, ValueType, isSynthetic}
11+
import effekt.symbols.{Anon, Binder, Callable, Effects, ErrorMessageInterpolator, Module, Param, Symbol, TypeAlias, TypePrinter, UserFunction, ValueType, isSynthetic}
1212
import effekt.util.messages.EffektError
1313
import effekt.util.{PlainMessaging, PrettyPrinter}
1414
import kiama.util.Collections.{mapToJavaMap, seqToJavaList}
@@ -373,7 +373,7 @@ class Server(config: EffektConfig, compileOnChange: Boolean=false) extends Langu
373373
source <- sources.get(params.getTextDocument.getUri)
374374
hints = {
375375
val range = fromLSPRange(params.getRange, source)
376-
getInferredCaptures(range)(using context).map {
376+
val captures = getInferredCaptures(range)(using context).map {
377377
case (p, c) =>
378378
val prettyCaptures = TypePrinter.show(c)
379379
val inlayHint = new InlayHint(convertPosition(p), messages.Either.forLeft(prettyCaptures))
@@ -386,6 +386,32 @@ class Server(config: EffektConfig, compileOnChange: Boolean=false) extends Langu
386386
inlayHint.setData("capture")
387387
inlayHint
388388
}.toVector
389+
390+
val unannotated = getTreesInRange(range)(using context).map { trees =>
391+
trees.collect {
392+
// Functions without an annotated type:
393+
case fun: FunDef if fun.ret.isEmpty => for {
394+
sym <- context.symbolOption(fun.id)
395+
tpe <- context.functionTypeOption(sym)
396+
pos = fun.ret.span.range.from
397+
} yield {
398+
val prettyType = pp": ${tpe.result} / ${tpe.effects}"
399+
val inlayHint = new InlayHint(convertPosition(pos), messages.Either.forLeft(prettyType))
400+
inlayHint.setKind(InlayHintKind.Type)
401+
val markup = new MarkupContent()
402+
markup.setValue(s"return type${prettyType}")
403+
markup.setKind("markdown")
404+
inlayHint.setTooltip(markup)
405+
inlayHint.setPaddingLeft(true)
406+
inlayHint.setData("return-type-annotation")
407+
val textEdit = new TextEdit(convertRange(fun.ret.span.range), prettyType)
408+
inlayHint.setTextEdits(Collections.seqToJavaList(List(textEdit)))
409+
inlayHint
410+
}
411+
}.flatten
412+
}.getOrElse(Vector())
413+
414+
captures ++ unannotated
389415
}
390416
} yield hints
391417

effekt/jvm/src/test/scala/effekt/LSPTests.scala

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -572,11 +572,11 @@ class LSPTests extends FunSuite {
572572
//
573573
//
574574

575-
test("inlayHints should show the io effect") {
575+
test("inlayHint should show the io effect") {
576576
withClientAndServer { (client, server) =>
577577
val (textDoc, positions) = raw"""
578578
|↑
579-
|def main() = {
579+
|def main(): Unit = {
580580
|↑
581581
| println("Hello, world!")
582582
|}
@@ -609,12 +609,61 @@ class LSPTests extends FunSuite {
609609
}
610610
}
611611

612-
test("inlayHints work after editing") {
612+
test("inlayHint shows omitted return type and effect") {
613+
withClientAndServer { (client, server) =>
614+
val (textDoc, positions) =
615+
raw"""effect raise(): Unit
616+
|
617+
|↑
618+
|def foo() = { do raise(); 5 }
619+
|↑ ↑
620+
|
621+
|↑
622+
|""".textDocumentAndPositions
623+
624+
val captureHint = new InlayHint()
625+
captureHint.setKind(InlayHintKind.Type)
626+
captureHint.setPosition(positions(1))
627+
captureHint.setLabel("{}")
628+
captureHint.setData("capture")
629+
captureHint.setTooltip(new MarkupContent("markdown", "captures: `{}`"))
630+
captureHint.setPaddingRight(true)
631+
632+
val omittedHint = new InlayHint()
633+
omittedHint.setKind(InlayHintKind.Type)
634+
omittedHint.setPosition(positions(2))
635+
omittedHint.setLabel(": Int / { raise }")
636+
omittedHint.setData("return-type-annotation")
637+
omittedHint.setTextEdits(List(
638+
new TextEdit(
639+
new Range(positions(2), positions(2)),
640+
": Int / { raise }"
641+
)
642+
).asJava)
643+
omittedHint.setTooltip(new MarkupContent("markdown", "return type: Int / { raise }"))
644+
omittedHint.setPaddingLeft(true)
645+
646+
val expectedInlayHints = List(captureHint, omittedHint)
647+
648+
val didOpenParams = new DidOpenTextDocumentParams()
649+
didOpenParams.setTextDocument(textDoc)
650+
server.getTextDocumentService().didOpen(didOpenParams)
651+
652+
val params = new InlayHintParams()
653+
params.setTextDocument(textDoc.versionedTextDocumentIdentifier)
654+
params.setRange(new Range(positions(0), positions(3)))
655+
656+
val inlayHints = server.getTextDocumentService().inlayHint(params).get()
657+
assertEquals(expectedInlayHints.asJava, inlayHints)
658+
}
659+
}
660+
661+
test("inlayHint works after editing") {
613662
withClientAndServer { (client, server) =>
614663
val (textDoc, positions) =
615664
raw"""
616665
|↑
617-
|def main() = {
666+
|def main(): Unit = {
618667
|↑
619668
| println("Hello, world!")
620669
|}
@@ -652,7 +701,7 @@ class LSPTests extends FunSuite {
652701
val (newTextDoc, changeEvent) = textDoc.changeTo(
653702
raw"""
654703
|
655-
|def main() = {
704+
|def main(): Unit = {
656705
| println("Hello, world!")
657706
|}
658707
|""".stripMargin
@@ -694,12 +743,12 @@ class LSPTests extends FunSuite {
694743

695744
}
696745

697-
test("inlayHints work after invalid edits") {
746+
test("inlayHint works after invalid edits") {
698747
withClientAndServer(false) { (client, server) =>
699748
val (textDoc, positions) =
700749
raw"""
701750
|↑
702-
|def main() = {
751+
|def main(): Unit = {
703752
|↑
704753
| println("Hello, world!")
705754
|}
@@ -736,7 +785,7 @@ class LSPTests extends FunSuite {
736785

737786
val (newTextDoc, changeEvent) = textDoc.changeTo(
738787
raw"""
739-
|def main() = {
788+
|def main(): Unit = {
740789
| println("Hello, world!")
741790
|}
742791
|invalid syntax

effekt/shared/src/main/scala/effekt/source/Tree.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ case class Maybe[T](unspan: Option[T], span: Span) {
298298
case None => alternative
299299
}
300300

301-
export unspan.{foreach, get, getOrElse}
301+
export unspan.{foreach, get, getOrElse, isEmpty}
302302
}
303303

304304
object Maybe {

0 commit comments

Comments
 (0)