Skip to content

Commit c1832e6

Browse files
Use new capture syntax in inlay hints for definitions (#1098)
Now shows capture inlay hints using the `at` syntax on (function) definitions. This does not change the grammar, meaning it remains invalid syntax to explicitly annotate captures in these positions for now. Resolves #1077.
1 parent 0659bf4 commit c1832e6

File tree

8 files changed

+27
-34
lines changed

8 files changed

+27
-34
lines changed

effekt/js/src/main/scala/effekt/LanguageServer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class LanguageServer extends Intelligence {
117117
val source = VirtualFileSource(path)
118118
val range = kiama.util.Range(source.offsetToPosition(0), source.offsetToPosition(source.charCount))
119119
getInferredCaptures(range).map {
120-
case CaptureInfo(p, c, _) => new lsp.CaptureInfo(toLSPPosition(p), c.toString)
120+
case CaptureInfo(p, c) => new lsp.CaptureInfo(toLSPPosition(p), c.toString)
121121
}.toJSArray
122122
}
123123

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -374,19 +374,16 @@ class Server(config: EffektConfig, compileOnChange: Boolean=false) extends Langu
374374
hints = {
375375
val range = fromLSPRange(params.getRange, source)
376376
val captures = getInferredCaptures(range)(using context).map {
377-
case CaptureInfo(p, c, atSyntax) =>
377+
case CaptureInfo(p, c) =>
378378
val prettyCaptures = TypePrinter.show(c)
379-
val codeEdit = if atSyntax then s"at ${prettyCaptures}" else prettyCaptures
379+
val codeEdit = s"at ${prettyCaptures}"
380380
val inlayHint = new InlayHint(convertPosition(p), messages.Either.forLeft(codeEdit))
381381
inlayHint.setKind(InlayHintKind.Type)
382382
val markup = new MarkupContent()
383383
markup.setValue(s"captures: `${prettyCaptures}`")
384384
markup.setKind("markdown")
385385
inlayHint.setTooltip(markup)
386-
if (atSyntax) then
387-
inlayHint.setPaddingLeft(true)
388-
else
389-
inlayHint.setPaddingRight(true)
386+
inlayHint.setPaddingLeft(true)
390387
inlayHint.setData("capture")
391388
inlayHint
392389
}.toVector

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

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ class LSPTests extends FunSuite {
609609
val (textDoc, positions) = raw"""
610610
|↑
611611
|def main(): Unit = {
612-
|↑
612+
|
613613
| println("Hello, world!")
614614
|}
615615
|↑
@@ -618,12 +618,12 @@ class LSPTests extends FunSuite {
618618
val inlayHint = new InlayHint()
619619
inlayHint.setKind(InlayHintKind.Type)
620620
inlayHint.setPosition(positions(1))
621-
inlayHint.setLabel("{io}")
621+
inlayHint.setLabel("at {io}")
622622
val markup = new MarkupContent()
623623
markup.setKind("markdown")
624624
markup.setValue("captures: `{io}`")
625625
inlayHint.setTooltip(markup)
626-
inlayHint.setPaddingRight(true)
626+
inlayHint.setPaddingLeft(true)
627627
inlayHint.setData("capture")
628628

629629
val expectedInlayHints = List(inlayHint)
@@ -685,27 +685,27 @@ class LSPTests extends FunSuite {
685685
|
686686
|↑
687687
|def foo() = { do raise(); 5 }
688-
|
688+
|
689689
|
690690
|↑
691691
|""".textDocumentAndPositions
692692

693693
val captureHint = new InlayHint()
694694
captureHint.setKind(InlayHintKind.Type)
695695
captureHint.setPosition(positions(1))
696-
captureHint.setLabel("{}")
696+
captureHint.setLabel("at {}")
697697
captureHint.setData("capture")
698698
captureHint.setTooltip(new MarkupContent("markdown", "captures: `{}`"))
699-
captureHint.setPaddingRight(true)
699+
captureHint.setPaddingLeft(true)
700700

701701
val omittedHint = new InlayHint()
702702
omittedHint.setKind(InlayHintKind.Type)
703-
omittedHint.setPosition(positions(2))
703+
omittedHint.setPosition(positions(1))
704704
omittedHint.setLabel(": Int / { raise }")
705705
omittedHint.setData("return-type-annotation")
706706
omittedHint.setTextEdits(List(
707707
new TextEdit(
708-
new Range(positions(2), positions(2)),
708+
new Range(positions(1), positions(1)),
709709
": Int / { raise }"
710710
)
711711
).asJava)
@@ -720,7 +720,7 @@ class LSPTests extends FunSuite {
720720

721721
val params = new InlayHintParams()
722722
params.setTextDocument(textDoc.versionedTextDocumentIdentifier)
723-
params.setRange(new Range(positions(0), positions(3)))
723+
params.setRange(new Range(positions(0), positions(2)))
724724

725725
val inlayHints = server.getTextDocumentService().inlayHint(params).get()
726726
assertEquals(expectedInlayHints.asJava, inlayHints)
@@ -733,7 +733,7 @@ class LSPTests extends FunSuite {
733733
raw"""
734734
|↑
735735
|def main(): Unit = {
736-
|↑
736+
|
737737
| println("Hello, world!")
738738
|}
739739
|↑
@@ -742,12 +742,12 @@ class LSPTests extends FunSuite {
742742
val inlayHint = new InlayHint()
743743
inlayHint.setKind(InlayHintKind.Type)
744744
inlayHint.setPosition(positions(1))
745-
inlayHint.setLabel("{io}")
745+
inlayHint.setLabel("at {io}")
746746
val markup = new MarkupContent()
747747
markup.setKind("markdown")
748748
markup.setValue("captures: `{io}`")
749749
inlayHint.setTooltip(markup)
750-
inlayHint.setPaddingRight(true)
750+
inlayHint.setPaddingLeft(true)
751751
inlayHint.setData("capture")
752752

753753
val expectedInlayHints = List(inlayHint)
@@ -818,7 +818,7 @@ class LSPTests extends FunSuite {
818818
raw"""
819819
|↑
820820
|def main(): Unit = {
821-
|↑
821+
|
822822
| println("Hello, world!")
823823
|}
824824
|↑
@@ -827,12 +827,12 @@ class LSPTests extends FunSuite {
827827
val inlayHint = new InlayHint()
828828
inlayHint.setKind(InlayHintKind.Type)
829829
inlayHint.setPosition(positions(1))
830-
inlayHint.setLabel("{io}")
830+
inlayHint.setLabel("at {io}")
831831
val markup = new MarkupContent()
832832
markup.setKind("markdown")
833833
markup.setValue("captures: `{io}`")
834834
inlayHint.setTooltip(markup)
835-
inlayHint.setPaddingRight(true)
835+
inlayHint.setPaddingLeft(true)
836836
inlayHint.setData("capture")
837837

838838
val expectedInlayHints = List(inlayHint)

effekt/jvm/src/test/scala/effekt/ParserTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@ class ParserTests extends munit.FunSuite {
998998
parseDefinition(source.content),
999999
DefDef(
10001000
IdDef("foo", Span(source, pos(0), pos(1))),
1001-
None,
1001+
Maybe.None(Span(source, pos(1), pos(1))),
10021002
Var(IdRef(Nil, "f", Span(source, pos(2), pos(3))), Span(source, pos(2), pos(3))),
10031003
Info.empty(Span(source, 0, 0)),
10041004
Span(source, 0, pos.last)))

effekt/shared/src/main/scala/effekt/Intelligence.scala

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,13 @@ trait Intelligence {
259259
}.collect {
260260
case (t: source.FunDef, c) => for {
261261
pos <- C.positions.getStart(t)
262-
} yield CaptureInfo(pos, c)
262+
} yield CaptureInfo(t.ret.span.range.from, c)
263263
case (t: source.DefDef, c) => for {
264264
pos <- C.positions.getStart(t)
265-
} yield CaptureInfo(pos, c)
265+
} yield CaptureInfo(t.annot.span.range.from, c)
266266
case (source.Box(Maybe(None, span), block, _), _) if C.inferredCaptureOption(block).isDefined => for {
267267
capt <- C.inferredCaptureOption(block)
268-
} yield CaptureInfo(span.range.from, capt, true)
268+
} yield CaptureInfo(span.range.from, capt)
269269
}.flatten
270270

271271
def getInfoOf(sym: Symbol)(using C: Context): Option[SymbolInfo] = PartialFunction.condOpt(resolveCallTarget(sym)) {
@@ -491,9 +491,5 @@ object Intelligence {
491491
case class CaptureInfo(
492492
position: Position,
493493
captures: CaptureSet,
494-
/**
495-
* Whether this capture set could be written into the source code at `position` using the `at { captures }` syntax
496-
*/
497-
atSyntax: Boolean = false,
498494
)
499495
}

effekt/shared/src/main/scala/effekt/Namer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ object Namer extends Phase[Parsed, NameResolved] {
320320
case d @ source.DefDef(id, annot, binding, doc, span) =>
321321
val tpe = annot.map(resolveBlockType)
322322
resolve(binding)
323-
Context.define(id, DefBinder(Context.nameFor(id), tpe, d))
323+
Context.define(id, DefBinder(Context.nameFor(id), tpe.unspan, d))
324324

325325
// FunDef and InterfaceDef have already been resolved as part of the module declaration
326326
case f @ source.FunDef(id, tparams, vparams, bparams, ret, body, doc, span) =>

effekt/shared/src/main/scala/effekt/Parser.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -792,9 +792,9 @@ class Parser(positions: Positions, tokens: Seq[Token], source: Source) {
792792
nonterminal:
793793
if peek(`:`) then Some(valueTypeAnnotation()) else None
794794

795-
def maybeBlockTypeAnnotation(): Option[BlockType] =
795+
def maybeBlockTypeAnnotation(): Maybe[BlockType] =
796796
nonterminal:
797-
if peek(`:`) then Some(blockTypeAnnotation()) else None
797+
if peek(`:`) then Maybe.Some(blockTypeAnnotation(), span()) else Maybe.None(span())
798798

799799
def maybeReturnAnnotation(): Maybe[Effectful] =
800800
nonterminal:

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ enum Def extends Definition {
402402
case ValDef(id: IdDef, annot: Option[ValueType], binding: Stmt, info: Info, span: Span)
403403
case RegDef(id: IdDef, annot: Option[ValueType], region: IdRef, binding: Stmt, info: Info, span: Span)
404404
case VarDef(id: IdDef, annot: Option[ValueType], binding: Stmt, info: Info, span: Span)
405-
case DefDef(id: IdDef, annot: Option[BlockType], block: Term, info: Info, span: Span)
405+
case DefDef(id: IdDef, annot: Maybe[BlockType], block: Term, info: Info, span: Span)
406406

407407
case NamespaceDef(id: IdDef, definitions: List[Def], info: Info, span: Span)
408408

0 commit comments

Comments
 (0)