@@ -20,7 +20,12 @@ class LSPTests extends FunSuite {
20
20
//
21
21
//
22
22
23
- def withClientAndServer (testBlock : (MockLanguageClient , Server ) => Unit ): Unit = {
23
+
24
+ /**
25
+ * @param compileOnChange The server currently uses `compileOnChange = false` by default, but we set it to `true` for testing
26
+ * because we would like to switch to `didChange` events once we have working caching for references.
27
+ */
28
+ def withClientAndServer (compileOnChange : Boolean )(testBlock : (MockLanguageClient , Server ) => Unit ): Unit = {
24
29
val driver = new Driver {}
25
30
val config = EffektConfig (Seq (" --server" ))
26
31
config.verify()
@@ -30,9 +35,7 @@ class LSPTests extends FunSuite {
30
35
val serverIn = new PipedInputStream (clientOut)
31
36
val serverOut = new PipedOutputStream (clientIn)
32
37
33
- // The server currently uses `compileOnChange = false` by default, but we set it to `true` for testing
34
- // because we would like to switch to `didChange` events once we have working caching for references.
35
- val server = new Server (config, compileOnChange = true )
38
+ val server = new Server (config, compileOnChange)
36
39
37
40
val mockClient = new MockLanguageClient ()
38
41
server.connect(mockClient)
@@ -42,7 +45,11 @@ class LSPTests extends FunSuite {
42
45
testBlock(mockClient, server)
43
46
}
44
47
45
- // Fixtures
48
+ def withClientAndServer (testBlock : (MockLanguageClient , Server ) => Unit ): Unit = {
49
+ withClientAndServer(true )(testBlock)
50
+ }
51
+
52
+ // Fixtures
46
53
//
47
54
//
48
55
@@ -343,6 +350,78 @@ class LSPTests extends FunSuite {
343
350
}
344
351
}
345
352
353
+ test(" Hovering works after editing" ) {
354
+ withClientAndServer { (client, server) =>
355
+ // Initial code
356
+ //
357
+ //
358
+
359
+ val (textDoc, firstPos) = raw """
360
+ |val x: Int = 42
361
+ | ↑
362
+ | """ .textDocumentAndPosition
363
+ val hoverContents =
364
+ raw """ |#### Value binder
365
+ |```effekt
366
+ |test::x: Int
367
+ |```
368
+ | """ .stripMargin
369
+
370
+ val didOpenParams = new DidOpenTextDocumentParams ()
371
+ didOpenParams.setTextDocument(textDoc)
372
+ server.getTextDocumentService().didOpen(didOpenParams)
373
+
374
+ val hoverParams = new HoverParams (textDoc.versionedTextDocumentIdentifier, firstPos)
375
+ val hover = server.getTextDocumentService().hover(hoverParams).get()
376
+
377
+ val expectedHover = (pos : Position ) => {
378
+ val expectedHover = new Hover ()
379
+ expectedHover.setRange(new Range (pos, pos))
380
+ expectedHover.setContents(new MarkupContent (" markdown" , hoverContents))
381
+ expectedHover
382
+ }
383
+ assertEquals(hover, expectedHover(firstPos))
384
+
385
+ // First edit: now we add a blank line in front
386
+ //
387
+ //
388
+
389
+ val (newTextDoc, changeEvent) = textDoc.changeTo(
390
+ raw """
391
+ |
392
+ |val x: Int = 42
393
+ | """ .stripMargin
394
+ )
395
+ val secondPos = new Position (firstPos.getLine + 1 , firstPos.getCharacter)
396
+
397
+ val didChangeParams = new DidChangeTextDocumentParams ()
398
+ didChangeParams.setTextDocument(newTextDoc.versionedTextDocumentIdentifier)
399
+ didChangeParams.setContentChanges(util.Arrays .asList(changeEvent))
400
+ server.getTextDocumentService().didChange(didChangeParams)
401
+
402
+ val hoverParamsAfterChange = new HoverParams (newTextDoc.versionedTextDocumentIdentifier, secondPos)
403
+ val hoverAfterChange = server.getTextDocumentService().hover(hoverParamsAfterChange).get()
404
+
405
+ assertEquals(hoverAfterChange, expectedHover(secondPos))
406
+
407
+ // Second edit: we revert the change
408
+ //
409
+ //
410
+
411
+ val (revertedTextDoc, revertedChangeEvent) = newTextDoc.changeTo(textDoc.getText)
412
+
413
+ val didChangeParamsReverted = new DidChangeTextDocumentParams ()
414
+ didChangeParamsReverted.setTextDocument(revertedTextDoc.versionedTextDocumentIdentifier)
415
+ didChangeParamsReverted.setContentChanges(util.Arrays .asList(revertedChangeEvent))
416
+ server.getTextDocumentService().didChange(didChangeParamsReverted)
417
+
418
+ val hoverParamsAfterRevert = new HoverParams (revertedTextDoc.versionedTextDocumentIdentifier, firstPos)
419
+ val hoverAfterRevert = server.getTextDocumentService().hover(hoverParamsAfterRevert).get()
420
+
421
+ assertEquals(hoverAfterRevert, expectedHover(firstPos))
422
+ }
423
+ }
424
+
346
425
// LSP: Document symbols
347
426
//
348
427
//
@@ -487,6 +566,156 @@ class LSPTests extends FunSuite {
487
566
}
488
567
}
489
568
569
+ test(" inlayHints work after editing" ) {
570
+ withClientAndServer { (client, server) =>
571
+ val (textDoc, positions) =
572
+ raw """
573
+ |↑
574
+ |def main() = {
575
+ |↑
576
+ | println("Hello, world!")
577
+ |}
578
+ |↑
579
+ | """ .textDocumentAndPositions
580
+
581
+ val inlayHint = new InlayHint ()
582
+ inlayHint.setKind(InlayHintKind .Type )
583
+ inlayHint.setPosition(positions(1 ))
584
+ inlayHint.setLabel(" {io}" )
585
+ val markup = new MarkupContent ()
586
+ markup.setKind(" markdown" )
587
+ markup.setValue(" captures: `{io}`" )
588
+ inlayHint.setTooltip(markup)
589
+ inlayHint.setPaddingRight(true )
590
+ inlayHint.setData(" capture" )
591
+
592
+ val expectedInlayHints = List (inlayHint)
593
+
594
+ val didOpenParams = new DidOpenTextDocumentParams ()
595
+ didOpenParams.setTextDocument(textDoc)
596
+ server.getTextDocumentService().didOpen(didOpenParams)
597
+
598
+ val params = new InlayHintParams ()
599
+ params.setTextDocument(textDoc.versionedTextDocumentIdentifier)
600
+ params.setRange(new Range (positions(0 ), positions(2 )))
601
+
602
+ val inlayHints = server.getTextDocumentService().inlayHint(params).get()
603
+ assertEquals(inlayHints, expectedInlayHints.asJava)
604
+
605
+ // First edit: now we add a blank line in front
606
+ //
607
+ //
608
+
609
+ val (newTextDoc, changeEvent) = textDoc.changeTo(
610
+ raw """
611
+ |
612
+ |def main() = {
613
+ | println("Hello, world!")
614
+ |}
615
+ | """ .stripMargin
616
+ )
617
+ val newPos = new Position (positions(1 ).getLine + 1 , positions(1 ).getCharacter)
618
+
619
+ val didChangeParams = new DidChangeTextDocumentParams ()
620
+ didChangeParams.setTextDocument(newTextDoc.versionedTextDocumentIdentifier)
621
+ didChangeParams.setContentChanges(util.Arrays .asList(changeEvent))
622
+ server.getTextDocumentService().didChange(didChangeParams)
623
+
624
+ val paramsAfterChange = new InlayHintParams ()
625
+ paramsAfterChange.setTextDocument(newTextDoc.versionedTextDocumentIdentifier)
626
+ paramsAfterChange.setRange(new Range (positions(0 ), new Position (positions(2 ).getLine + 1 , positions(2 ).getCharacter)))
627
+
628
+ inlayHint.setPosition(newPos)
629
+ val inlayHintsAfterChange = server.getTextDocumentService().inlayHint(paramsAfterChange).get()
630
+ assertEquals(inlayHintsAfterChange, expectedInlayHints.asJava)
631
+
632
+ // Second edit: we revert the change
633
+ //
634
+ //
635
+
636
+ val (revertedTextDoc, revertedChangeEvent) = newTextDoc.changeTo(textDoc.getText)
637
+ inlayHint.setPosition(positions(1 ))
638
+
639
+ val didChangeParamsReverted = new DidChangeTextDocumentParams ()
640
+ didChangeParamsReverted.setTextDocument(revertedTextDoc.versionedTextDocumentIdentifier)
641
+ didChangeParamsReverted.setContentChanges(util.Arrays .asList(revertedChangeEvent))
642
+ server.getTextDocumentService().didChange(didChangeParamsReverted)
643
+
644
+ val paramsAfterRevert = new InlayHintParams ()
645
+ paramsAfterRevert.setTextDocument(revertedTextDoc.versionedTextDocumentIdentifier)
646
+ paramsAfterRevert.setRange(new Range (positions(0 ), positions(2 )))
647
+
648
+ val inlayHintsAfterRevert = server.getTextDocumentService().inlayHint(paramsAfterRevert).get()
649
+ assertEquals(inlayHintsAfterRevert, expectedInlayHints.asJava)
650
+ }
651
+
652
+ }
653
+
654
+ test(" inlayHints work after invalid edits" ) {
655
+ withClientAndServer(false ) { (client, server) =>
656
+ val (textDoc, positions) =
657
+ raw """
658
+ |↑
659
+ |def main() = {
660
+ |↑
661
+ | println("Hello, world!")
662
+ |}
663
+ |↑
664
+ | """ .textDocumentAndPositions
665
+
666
+ val inlayHint = new InlayHint ()
667
+ inlayHint.setKind(InlayHintKind .Type )
668
+ inlayHint.setPosition(positions(1 ))
669
+ inlayHint.setLabel(" {io}" )
670
+ val markup = new MarkupContent ()
671
+ markup.setKind(" markdown" )
672
+ markup.setValue(" captures: `{io}`" )
673
+ inlayHint.setTooltip(markup)
674
+ inlayHint.setPaddingRight(true )
675
+ inlayHint.setData(" capture" )
676
+
677
+ val expectedInlayHints = List (inlayHint)
678
+
679
+ val didOpenParams = new DidOpenTextDocumentParams ()
680
+ didOpenParams.setTextDocument(textDoc)
681
+ server.getTextDocumentService().didOpen(didOpenParams)
682
+
683
+ val params = new InlayHintParams ()
684
+ params.setTextDocument(textDoc.versionedTextDocumentIdentifier)
685
+ params.setRange(new Range (positions(0 ), positions(2 )))
686
+
687
+ val inlayHints = server.getTextDocumentService().inlayHint(params).get()
688
+ assertEquals(inlayHints, expectedInlayHints.asJava)
689
+
690
+ // Edit: now we add some invalid syntax to the end
691
+ //
692
+ //
693
+
694
+ val (newTextDoc, changeEvent) = textDoc.changeTo(
695
+ raw """
696
+ |def main() = {
697
+ | println("Hello, world!")
698
+ |}
699
+ |invalid syntax
700
+ | """ .stripMargin
701
+ )
702
+
703
+ val didChangeParams = new DidChangeTextDocumentParams ()
704
+ didChangeParams.setTextDocument(newTextDoc.versionedTextDocumentIdentifier)
705
+ didChangeParams.setContentChanges(util.Arrays .asList(changeEvent))
706
+ server.getTextDocumentService().didChange(didChangeParams)
707
+
708
+ val paramsAfterChange = new InlayHintParams ()
709
+ paramsAfterChange.setTextDocument(newTextDoc.versionedTextDocumentIdentifier)
710
+ // The client may send a range that is outside of the text the server currently has
711
+ // We use somewhat arbitrary values here.
712
+ paramsAfterChange.setRange(new Range (positions(0 ), new Position (positions(2 ).getLine + 1 , positions(2 ).getCharacter + 5 )))
713
+
714
+ val inlayHintsAfterChange = server.getTextDocumentService().inlayHint(paramsAfterChange).get()
715
+ assertEquals(inlayHintsAfterChange, expectedInlayHints.asJava)
716
+ }
717
+ }
718
+
490
719
// Effekt: Publish IR
491
720
//
492
721
//
0 commit comments