From 64e8920525f98713c786f2a78c4f763e1cd62a29 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 Jan 2018 18:45:51 +0100 Subject: [PATCH 01/19] Add IDE tests --- .drone.yml | 9 ++- .../src/dotty/tools/dotc/util/DiffUtil.scala | 7 ++ .../dotty/tools/vulpix/ParallelTesting.scala | 5 +- .../tools/languageserver/CompletionTest.scala | 13 ++++ .../tools/languageserver/DefinitionTest.scala | 39 ++++++++++ .../languageserver/DocumentSymbolTest.scala | 23 ++++++ .../tools/languageserver/HighlightTest.scala | 22 ++++++ .../tools/languageserver/HoverTest.scala | 74 ++++++++++++++++++ .../dotty/tools/languageserver/Main.scala | 63 ++++++++++++++++ .../tools/languageserver/ReferencesTest.scala | 22 ++++++ .../tools/languageserver/RenameTest.scala | 30 ++++++++ .../tools/languageserver/SymbolTest.scala | 27 +++++++ .../tools/languageserver/util/Code.scala | 63 ++++++++++++++++ .../tools/languageserver/util/CodeRange.scala | 48 ++++++++++++ .../languageserver/util/CodeTester.scala | 75 +++++++++++++++++++ .../languageserver/util/PositionContext.scala | 28 +++++++ .../tools/languageserver/util/SymInfo.scala | 14 ++++ .../languageserver/util/actions/Action.scala | 19 +++++ .../util/actions/ActionOnMarker.scala | 9 +++ .../util/actions/ActionOnRange.scala | 29 +++++++ .../util/actions/CodeCompletion.scala | 28 +++++++ .../util/actions/CodeDefinition.scala | 16 ++++ .../util/actions/CodeDocumentHighlight.scala | 26 +++++++ .../util/actions/CodeDocumentSymbol.scala | 20 +++++ .../util/actions/CodeHover.scala | 26 +++++++ .../util/actions/CodeReferences.scala | 18 +++++ .../util/actions/CodeRename.scala | 24 ++++++ .../util/actions/CodeSymbol.scala | 20 +++++ .../util/embedded/CodeInRange.scala | 5 ++ .../util/embedded/CodeMarker.scala | 44 +++++++++++ .../util/embedded/Embedded.scala | 3 + .../util/server/TestClient.scala | 35 +++++++++ .../languageserver/util/server/TestFile.scala | 14 ++++ .../util/server/TestServer.scala | 60 +++++++++++++++ project/Build.scala | 2 + 35 files changed, 955 insertions(+), 5 deletions(-) create mode 100644 language-server/test/dotty/tools/languageserver/CompletionTest.scala create mode 100644 language-server/test/dotty/tools/languageserver/DefinitionTest.scala create mode 100644 language-server/test/dotty/tools/languageserver/DocumentSymbolTest.scala create mode 100644 language-server/test/dotty/tools/languageserver/HighlightTest.scala create mode 100644 language-server/test/dotty/tools/languageserver/HoverTest.scala create mode 100644 language-server/test/dotty/tools/languageserver/Main.scala create mode 100644 language-server/test/dotty/tools/languageserver/ReferencesTest.scala create mode 100644 language-server/test/dotty/tools/languageserver/RenameTest.scala create mode 100644 language-server/test/dotty/tools/languageserver/SymbolTest.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/Code.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/CodeRange.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/CodeTester.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/PositionContext.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/SymInfo.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/actions/Action.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/actions/ActionOnMarker.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/actions/ActionOnRange.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/embedded/CodeInRange.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/embedded/Embedded.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/server/TestClient.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/server/TestFile.scala create mode 100644 language-server/test/dotty/tools/languageserver/util/server/TestServer.scala diff --git a/.drone.yml b/.drone.yml index 07ee3a45f974..374c10c4b6e8 100644 --- a/.drone.yml +++ b/.drone.yml @@ -43,11 +43,18 @@ pipeline: - cp -R . /tmp/3/ && cd /tmp/3/ - ./project/scripts/sbt dotty-optimised/test - test_sbt: + test_ide: group: test image: lampepfl/dotty:2018-04-10 commands: - cp -R . /tmp/4/ && cd /tmp/4/ + - ./project/scripts/sbt dotty-language-server/test:run + + test_sbt: + group: test + image: lampepfl/dotty:2017-11-17 + commands: + - cp -R . /tmp/5/ && cd /tmp/5/ - ./project/scripts/sbt sbt-dotty/scripted when: # sbt scripted tests are slow and only run on nightly or deployment diff --git a/compiler/src/dotty/tools/dotc/util/DiffUtil.scala b/compiler/src/dotty/tools/dotc/util/DiffUtil.scala index 12a56a771b47..37159c90e50e 100644 --- a/compiler/src/dotty/tools/dotc/util/DiffUtil.scala +++ b/compiler/src/dotty/tools/dotc/util/DiffUtil.scala @@ -59,6 +59,13 @@ object DiffUtil { (fnd, exp, totalChange.toDouble / (expected.length + found.length)) } + def mkColoredLineDiff(expected: Seq[String], actual: Seq[String]): String = { + val expectedSize = DiffUtil.EOF.length max expected.map(_.length).max + actual.padTo(expected.length, "").zip(expected.padTo(actual.length, "")).map { case (act, exp) => + mkColoredLineDiff(exp, act, expectedSize) + }.mkString("\n") + } + def mkColoredLineDiff(expected: String, actual: String, expectedSize: Int): String = { lazy val diff = { val tokens = splitTokens(expected, Nil).toArray diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 9a3a2b093a57..945d5fa3efb4 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -612,10 +612,7 @@ trait ParallelTesting extends RunnerOrchestration { self => if (outputLines.length != checkLines.length || !linesMatch) { // Print diff to files and summary: - val expectedSize = DiffUtil.EOF.length max checkLines.map(_.length).max - val diff = outputLines.padTo(checkLines.length, "").zip(checkLines.padTo(outputLines.length, "")).map { case (act, exp) => - DiffUtil.mkColoredLineDiff(exp, act, expectedSize) - }.mkString("\n") + val diff = DiffUtil.mkColoredLineDiff(checkLines, outputLines) val msg = s"""|Output from '$sourceTitle' did not match check file. diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala new file mode 100644 index 000000000000..079afc17073f --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -0,0 +1,13 @@ +package dotty.tools.languageserver + +import org.junit.Test + +import dotty.tools.languageserver.util.Code._ + +class CompletionTest { + + @Test def competion0: Unit = { + code"class Foo { val xyz: Int = 0; def y: Int = xy$m1 }".withSource + .completion(m1, List(("xyz", "Field", "Int"))) + } +} diff --git a/language-server/test/dotty/tools/languageserver/DefinitionTest.scala b/language-server/test/dotty/tools/languageserver/DefinitionTest.scala new file mode 100644 index 000000000000..3e3a1de3ec59 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/DefinitionTest.scala @@ -0,0 +1,39 @@ +package dotty.tools.languageserver + +import org.junit.Test + +import dotty.tools.languageserver.util.Code._ + +class DefinitionTest { + + @Test def classDefinitionNotFound0: Unit = + code"class Foo { new ${m1}Bar$m2 }".withSource.definition(m1 to m2, None) + + @Test def classDefinition0: Unit = { + withSources( + code"class ${m1}Foo$m2 { new Foo }", + code"class Bar { val foo: ${m3}Foo$m4 = new ${m5}Foo$m6 }" + ) .definition(m1 to m2, Some(m1 to m2)) + .definition(m3 to m4, Some(m1 to m2)) + .definition(m5 to m6, Some(m1 to m2)) + } + + @Test def valDefinition0: Unit = { + withSources( + code"class Foo { val ${m1}x$m2 = 0; ${m3}x$m4 }", + code"class Bar { val foo = new Foo; foo.${m5}x$m6 }" + ) .definition(m1 to m2, Some(m1 to m2)) + .definition(m3 to m4, Some(m1 to m2)) + .definition(m5 to m6, Some(m1 to m2)) + } + + @Test def defDefinition0: Unit = { + withSources( + code"class Foo { def ${m1}x$m2 = 0; ${m3}x$m4 }", + code"class Bar { val foo = new Foo; foo.${m5}x$m6 }" + ) .definition(m1 to m2, Some(m1 to m2)) + .definition(m3 to m4, Some(m1 to m2)) + .definition(m5 to m6, Some(m1 to m2)) + } + +} diff --git a/language-server/test/dotty/tools/languageserver/DocumentSymbolTest.scala b/language-server/test/dotty/tools/languageserver/DocumentSymbolTest.scala new file mode 100644 index 000000000000..c647d2d5f9f4 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/DocumentSymbolTest.scala @@ -0,0 +1,23 @@ +package dotty.tools.languageserver + +import org.junit.Test + +import dotty.tools.languageserver.util.Code._ + +class DocumentSymbolTest { + + @Test def documentSymbol0: Unit = + code"class ${m1}Foo$m2".withSource.documentSymbol(m1, (m1 to m2).symInfo("Foo", "Class")) + + @Test def documentSymbol1: Unit = + code"class ${m1}Foo$m2; class ${m3}Bar$m4".withSource + .documentSymbol(m1, (m1 to m2).symInfo("Foo", "Class"), (m3 to m4).symInfo("Bar", "Class")) + + @Test def documentSymbol3: Unit = { + withSources( + code"class ${m1}Foo$m2", + code"class ${m3}Bar$m4" + ) .documentSymbol(m1, (m1 to m2).symInfo("Foo", "Class")) + .documentSymbol(m3, (m3 to m4).symInfo("Bar", "Class")) + } +} diff --git a/language-server/test/dotty/tools/languageserver/HighlightTest.scala b/language-server/test/dotty/tools/languageserver/HighlightTest.scala new file mode 100644 index 000000000000..49ac1ecbf691 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/HighlightTest.scala @@ -0,0 +1,22 @@ +package dotty.tools.languageserver + +import org.junit.Test + +import dotty.tools.languageserver.util.Code._ + +class HighlightTest { + + @Test def valHighlight0: Unit = { + val xDef = (m1 to m2).withCode("x") + code"class X { val $xDef = 9 }".withSource + .highlight(xDef.range, (xDef.range, "Read")) + } + + @Test def valHighlight1: Unit = { + val xDef = (m1 to m2).withCode("x") + val xRef = (m3 to m4).withCode("x") + code"class X { val $xDef = 9; $xRef}".withSource + .highlight(xRef.range, (xDef.range, "Read"), (xRef.range, "Read")) + } + +} diff --git a/language-server/test/dotty/tools/languageserver/HoverTest.scala b/language-server/test/dotty/tools/languageserver/HoverTest.scala new file mode 100644 index 000000000000..34dac2ce0b91 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/HoverTest.scala @@ -0,0 +1,74 @@ +package dotty.tools.languageserver + +import org.junit.Test + +import dotty.tools.languageserver.util.Code._ + +class HoverTest { + + @Test def hoverOnWhiteSpace0: Unit = + code"$m1 $m2".withSource.hover(m1 to m2, "") + + @Test def hoverOnClass0: Unit = { + code"""$m1 ${m2}class Foo $m3 $m4""".withSource + .hover(m1 to m2, "") + .hover(m2 to m3, "Foo") + .hover(m3 to m4, "") + } + + @Test def hoverOnClass1: Unit = { + code"""$m1 ${m2}class Foo { } $m3 $m4""".withSource + .hover(m1 to m2, "") + .hover(m2 to m3, "Foo") + .hover(m3 to m4, "") + } + + @Test def hoverOnValDef0: Unit = { + code"""class Foo { + | ${m1}val x = ${m2}8$m3; ${m4}x$m5 + |}""".withSource + .hover(m1 to m2, "Int") + .hover(m2 to m3, "Int(8)") + .hover(m4 to m5, "Int") + } + + @Test def hoverOnValDef1: Unit = { + code"""class Foo { + | ${m1}final val x = 8$m2; ${m3}x$m4 + |}""".withSource + .hover(m1 to m2, "Int(8)") + .hover(m3 to m4, "Int(8)") + } + + @Test def hoverOnDefDef0: Unit = { + code"""class Foo { + | ${m1}def x = ${m2}8$m3; ${m4}x$m5 + |}""".withSource + .hover(m1 to m2, "Int") + .hover(m2 to m3, "Int(8)") + .hover(m4 to m5, "Int") + } + + @Test def hoverMissingRef0: Unit = { + code"""class Foo { + | ${m1}x$m2 + |}""".withSource + .hover(m1 to m2, "") + } + + @Test def hoverFun0: Unit = { + code"""class Foo { + | def x: String = $m1"abc"$m2 + | ${m3}x$m4 + | + | def y(): Int = 9 + | ${m5}y($m6)$m7 + |} + """.withSource + .hover(m1 to m2, "String(\"abc\")") + .hover(m3 to m4, "String") + .hover(m5 to m6, "(): Int") + .hover(m6 to m7, "Int") + } + +} diff --git a/language-server/test/dotty/tools/languageserver/Main.scala b/language-server/test/dotty/tools/languageserver/Main.scala new file mode 100644 index 000000000000..6ba33fe24f06 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/Main.scala @@ -0,0 +1,63 @@ +package dotty.tools.languageserver + +import org.junit.Test + +import java.lang.reflect.InvocationTargetException + +// TODO remove this and use JUnit to run the tests +object Main { + def main(args: Array[String]): Unit = { + var testsFailed = 0 + for (clazz <- testsClasses) { + val t0 = System.currentTimeMillis() + var passed = 0 + var failed = 0 + println(s"Starting tests in ${clazz.getSimpleName}") + for (method <- clazz.getMethods.sortBy(_.getName)) { + if (method.getAnnotation(classOf[Test]) ne null) { + print(s"Testing $clazz.${method.getName} ") + try { + method.invoke(clazz.getConstructor().newInstance()) + println(Console.GREEN + "passed" + Console.RESET) + passed += 1 + } catch { + case ex: InvocationTargetException => + ex.getCause match { + case ex1: AssertionError => + println(Console.RED + "failed" + Console.RESET) + System.err.println(s"${method.getName} failed with") + ex1.printStackTrace() + failed += 1 + case _ => throw ex.getCause + } + } + } + } + + val time = (System.currentTimeMillis() - t0).toDouble / 1000 + + if (failed == 0) { + println(s"${Console.GREEN}Passed all $passed tests${Console.RESET} in ${time}s") + } else { + testsFailed += 1 + System.err.println(s"Passed $passed, ${Console.RED}failed $failed${Console.RESET}, total ${passed + failed} in ${time}s") + } + println() + } + if (testsFailed != 0) { + System.err.println(s"Failed $testsFailed tests") + System.exit(1) + } + } + + private def testsClasses = List( + classOf[HighlightTest], + classOf[CompletionTest], + classOf[DefinitionTest], + classOf[HoverTest], + classOf[ReferencesTest], + classOf[RenameTest], + classOf[DocumentSymbolTest], + classOf[SymbolTest], + ) +} diff --git a/language-server/test/dotty/tools/languageserver/ReferencesTest.scala b/language-server/test/dotty/tools/languageserver/ReferencesTest.scala new file mode 100644 index 000000000000..427b2d244d5f --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/ReferencesTest.scala @@ -0,0 +1,22 @@ +package dotty.tools.languageserver + +import org.junit.Test + +import dotty.tools.languageserver.util.Code._ + +class ReferencesTest { + + @Test def valNoReferences0: Unit = + code"class X { val ${m1}x$m2 = 9 }".withSource.references(m1 to m2, Nil) + + @Test def valReferences0: Unit = { + code"class X { val ${m1}x$m2 = 9; ${m3}x$m4; ${m5}x$m6 }".withSource + .references(m1 to m2, List(m3 to m4, m5 to m6)) + } + + @Test def valReferences1: Unit = { + code"class X { val ${m1}x$m2 = 9; ${m3}x$m4; ${m5}x$m6 }".withSource + .references(m1 to m2, List(m1 to m2, m3 to m4, m5 to m6), withDecl = true) + } + +} diff --git a/language-server/test/dotty/tools/languageserver/RenameTest.scala b/language-server/test/dotty/tools/languageserver/RenameTest.scala new file mode 100644 index 000000000000..b2392560658e --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/RenameTest.scala @@ -0,0 +1,30 @@ +package dotty.tools.languageserver + +import org.junit.Test + +import dotty.tools.languageserver.util.Code._ +import dotty.tools.languageserver.util.embedded.CodeMarker + +class RenameTest { + + @Test def rename0: Unit = { + def testRenameFrom(m: CodeMarker) = + code"class ${m1}Foo$m2 { new ${m3}Foo$m4 }".withSource.rename(m, "Bar", List(m1 to m2, m3 to m4)) + testRenameFrom(m1) + testRenameFrom(m3) + } + + + @Test def rename1: Unit = { + def testRenameFrom(m: CodeMarker) = + withSources( + code"class ${m1}Foo$m2 { new ${m3}Foo$m4 }", + code"class Bar { new ${m5}Foo$m6 }" + ).rename(m, "Bar", List(m1 to m2, m3 to m4, m5 to m6)) + + testRenameFrom(m1) + testRenameFrom(m3) + testRenameFrom(m5) + } + +} diff --git a/language-server/test/dotty/tools/languageserver/SymbolTest.scala b/language-server/test/dotty/tools/languageserver/SymbolTest.scala new file mode 100644 index 000000000000..cd66da333cca --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/SymbolTest.scala @@ -0,0 +1,27 @@ +package dotty.tools.languageserver + +import org.junit.Test + +import dotty.tools.languageserver.util.Code._ + +class SymbolTest { + +// @Test def symbol0: Unit = +// code"class ${m1}Foo$m2".withSource.symbol("Foo", ("Foo", "Class", m1 to m2)) + + @Test def symbol1: Unit = { + val Foo = (m1 to m2).withCode("Foo") + val Bar = (m3 to m4).withCode("Bar") + val fooFoo = (m5 to m6).withCode("Foo") + withSources( + code"class $Foo", + code"class $Bar", + code"""package foo + |class $fooFoo { + | class Bar + |} + """ + ) .symbol("Foo", Foo.range.symInfo("Foo", "Class"), fooFoo.range.symInfo("Foo", "Class", "foo")) + .symbol("Bar", Bar.range.symInfo("Bar", "Class")) + } +} diff --git a/language-server/test/dotty/tools/languageserver/util/Code.scala b/language-server/test/dotty/tools/languageserver/util/Code.scala new file mode 100644 index 000000000000..db7220813eaf --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/Code.scala @@ -0,0 +1,63 @@ +package dotty.tools.languageserver.util + +import dotty.tools.languageserver.util.embedded._ + +object Code { + + // Default positions + val m1 = new CodeMarker("m1") + val m2 = new CodeMarker("m2") + val m3 = new CodeMarker("m3") + val m4 = new CodeMarker("m4") + val m5 = new CodeMarker("m5") + val m6 = new CodeMarker("m6") + val m7 = new CodeMarker("m7") + val m8 = new CodeMarker("m8") + + implicit class CodeHelper(val sc: StringContext) extends AnyVal { + def code(args: Embedded*): SourceWithPositions = { + val pi = sc.parts.iterator + val ai = args.iterator + + var line = 0 + var char = 0 + def scan(str: String): Unit = { + for (c <- str) + if (c == '\n') { line += 1; char = 0 } else { char += 1 } + } + + val stringBuilder = new StringBuilder + val positions = List.newBuilder[(CodeMarker, Int, Int)] + + while (ai.hasNext) { + val next = pi.next().stripMargin + stringBuilder.append(next) + scan(next) + + ai.next() match { + case emb: CodeMarker => + positions += Tuple3(emb, line, char) + + case emb: CodeInRange => + positions += Tuple3(emb.range.start, line, char) + scan(emb.text) + stringBuilder.append(emb.text) + positions += Tuple3(emb.range.end, line, char) + } + + } + + if (pi.hasNext) + stringBuilder.append(pi.next()) + + SourceWithPositions(stringBuilder.result(), positions.result()) + } + } + + def withSources(sources: SourceWithPositions*): CodeTester = new CodeTester(sources.toList, Nil) + + case class SourceWithPositions(text: String, positions: List[(CodeMarker, Int, Int)]) { + def withSource: CodeTester = new CodeTester(this :: Nil, Nil) + } + +} diff --git a/language-server/test/dotty/tools/languageserver/util/CodeRange.scala b/language-server/test/dotty/tools/languageserver/util/CodeRange.scala new file mode 100644 index 000000000000..ee8ba76ce48a --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/CodeRange.scala @@ -0,0 +1,48 @@ +package dotty.tools.languageserver.util + +import dotty.tools.languageserver.util.embedded.{CodeInRange, CodeMarker} +import dotty.tools.languageserver.util.server.TestFile + +import org.eclipse.lsp4j._ + +import PositionContext._ + +case class CodeRange(start: CodeMarker, end: CodeMarker) { + private var checked = false + def check(): PosCtx[Unit] = { + if (!checked) { + assert(start.file == end.file, s"$start and $end where not in the same file") + assert(start.line <= end.line, s"Expected $end to be after $start") + assert(start.line != end.line || start.character < end.character, s"Expected $end to be after $start") + checked = true + } + } + + def file: PosCtx[TestFile] = { + check() + start.file + } + + def toTuple: PosCtx[(Int, Int, Int, Int)] = { + check() + (start.line, start.character, end.line, end.character) + } + + def withCode(text: String): CodeInRange = CodeInRange(text, this) + + def symInfo(name: String, kind: String, container: String = null): SymInfo = + new SymInfo(name, kind, this, container) + + def toRange: PosCtx[Range] = { + check() + new Range(start.toPosition, end.toPosition) + } + + def toLocation: PosCtx[Location] = { + check() + new Location(file.uri, toRange) + } + + def show: PosCtx[String] = + s"[start=${start.show}, end=${end.show}]" +} \ No newline at end of file diff --git a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala new file mode 100644 index 000000000000..28a597def3e4 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala @@ -0,0 +1,75 @@ +package dotty.tools.languageserver.util + +import dotty.tools.languageserver.util.Code.SourceWithPositions +import dotty.tools.languageserver.util.actions._ +import dotty.tools.languageserver.util.embedded.CodeMarker +import dotty.tools.languageserver.util.server.{TestFile, TestServer} +import org.eclipse.lsp4j.SymbolInformation + +class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { + + private val testServer = new TestServer(TestFile.testDir) + + private val files = sources.zipWithIndex.map { case (code, i) => + testServer.openCode(code.text, s"Source$i.scala") + } + private val positions: PositionContext = getPositions(files) + + def hover(range: CodeRange, expected: String): CodeTester = + doAction(new CodeHover(range, expected)) + + def definition(range: CodeRange, refOpt: Option[CodeRange]): CodeTester = + doAction(new CodeDefinition(range, refOpt)) + + def highlight(range: CodeRange, highs: (CodeRange, String)*): CodeTester = + doAction(new CodeDocumentHighlight(range, highs)) + + def references(range: CodeRange, refs: List[CodeRange], withDecl: Boolean = false): CodeTester = + doAction(new CodeReferences(range, refs, withDecl)) + + def completion(marker: CodeMarker, completions: List[(String, String, String)]): CodeTester = + doAction(new CodeCompletion(marker, completions)) + + def rename(marker: CodeMarker, newName: String, expected: List[CodeRange]): CodeTester = + doAction(new CodeRename(marker, newName, expected)) // TODO apply changes to the sources and positions + + def documentSymbol(marker: CodeMarker, symbols: SymInfo*): CodeTester = + doAction(new CodeDocumentSymbol(marker, symbols)) + + def symbol(query: String, symbols: SymInfo*): CodeTester = + doAction(new CodeSymbol(query, symbols)) + + private def doAction(action: Action): this.type = { + try { + action.execute()(testServer, positions) + } catch { + case ex: AssertionError => + val sourcesStr = sources.zip(files).map{ case (source, file) => "// " + file.file + "\n" + source.text}.mkString("\n") + val msg = + s""" + | + |$sourcesStr + | + |while executing action: ${action.show(positions)} + | + """.stripMargin + val assertionError = new AssertionError(msg + ex.getMessage) + assertionError.setStackTrace(ex.getStackTrace) + throw assertionError + } + this + } + + private def getPositions(files: List[TestFile]): PositionContext = { + val posSeq = { + for { + (code, file) <- sources.zip(files) + (position, line, char) <- code.positions + } yield position -> (file, line, char) + } + val posMap = posSeq.toMap + assert(posSeq.size == posMap.size, + "Each CodeMarker instance can only appear once in the code: " + posSeq.map(x => (x._1, x._2._2, x._2._3))) + new PositionContext(posMap) + } +} diff --git a/language-server/test/dotty/tools/languageserver/util/PositionContext.scala b/language-server/test/dotty/tools/languageserver/util/PositionContext.scala new file mode 100644 index 000000000000..59f8adc5d207 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/PositionContext.scala @@ -0,0 +1,28 @@ +package dotty.tools.languageserver.util + +import dotty.tools.languageserver.util.embedded.CodeMarker +import dotty.tools.languageserver.util.server.TestFile + +class PositionContext(positionMap: Map[CodeMarker, (TestFile, Int, Int)]) { + private var lastKey: CodeMarker = _ + private var lastValue: (TestFile, Int, Int) = _ + def positionOf(pos: CodeMarker): (TestFile, Int, Int) = { + if (lastKey eq pos) lastValue + else { + lastValue = positionMap.getOrElse(pos, + { assert(false, "CodePosition was not found in the code: " + pos); null } + ) + lastKey = pos + lastValue + } + } + + def contains(pos: CodeMarker): Boolean = positionMap.contains(pos) + + def withPos(marker: CodeMarker, pos: (TestFile, Int, Int)) = + new PositionContext(positionMap.updated(marker, pos)) +} + +object PositionContext { + type PosCtx[T] = implicit PositionContext => T +} diff --git a/language-server/test/dotty/tools/languageserver/util/SymInfo.scala b/language-server/test/dotty/tools/languageserver/util/SymInfo.scala new file mode 100644 index 000000000000..ded418518075 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/SymInfo.scala @@ -0,0 +1,14 @@ +package dotty.tools.languageserver.util + +import dotty.tools.languageserver.util.PositionContext._ +import org.eclipse.lsp4j._ + +class SymInfo(name: String, kind: String, range: CodeRange, container: String) { + def toSymInformation: PosCtx[SymbolInformation] = + new SymbolInformation(name, SymbolKind.valueOf(kind), range.toLocation, container) + + def show: PosCtx[String] = + s"SymInfo($name, $kind, ${range.show}, $container)" + override def toString: String = + s"SymInfo($name, $kind, $range, $container)" +} \ No newline at end of file diff --git a/language-server/test/dotty/tools/languageserver/util/actions/Action.scala b/language-server/test/dotty/tools/languageserver/util/actions/Action.scala new file mode 100644 index 000000000000..a5faa560d0cd --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/actions/Action.scala @@ -0,0 +1,19 @@ +package dotty.tools.languageserver.util.actions + +import dotty.tools.languageserver.DottyLanguageServer +import dotty.tools.languageserver.util.PositionContext +import dotty.tools.languageserver.util.server.TestServer + +import PositionContext._ + +trait Action { + type Exec[T] = implicit (TestServer, PositionContext) => T + def execute(): Exec[Unit] + def show: PosCtx[String] + def server: Exec[DottyLanguageServer] = implicitly[TestServer].server + + // FIXME + // Workaroud an issue with implicit functions and phantomArgLift. + // That phase will dispear which should fix this issue. Just remove calls to param when that hapens + protected def fix[T](f: PositionContext => T): PosCtx[T] = f(implicitly[PositionContext]) +} diff --git a/language-server/test/dotty/tools/languageserver/util/actions/ActionOnMarker.scala b/language-server/test/dotty/tools/languageserver/util/actions/ActionOnMarker.scala new file mode 100644 index 000000000000..de15f9bf5439 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/actions/ActionOnMarker.scala @@ -0,0 +1,9 @@ +package dotty.tools.languageserver.util.actions + +import dotty.tools.languageserver.util.embedded.CodeMarker + +trait ActionOnMarker extends Action { + + def marker: CodeMarker + +} diff --git a/language-server/test/dotty/tools/languageserver/util/actions/ActionOnRange.scala b/language-server/test/dotty/tools/languageserver/util/actions/ActionOnRange.scala new file mode 100644 index 000000000000..b714fb0c2740 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/actions/ActionOnRange.scala @@ -0,0 +1,29 @@ +package dotty.tools.languageserver.util.actions + +import dotty.tools.languageserver.util.{CodeRange, PositionContext} +import dotty.tools.languageserver.util.embedded.CodeMarker + +trait ActionOnRange extends Action { + + def range: CodeRange + def onMarker(marker: CodeMarker): Exec[Unit] + + def execute(): Exec[Unit] = { + val posCtx = implicitly[PositionContext] + range.check() + val file = range.file + val start = range.start + val startLine = start.line + val startCharacter = start.character + val endLine = range.end.line + val endCharacter = range.end.character + assert(startLine <= endLine, "Start of range should be before end " + range.show) + assert(startLine != endLine || startCharacter < endCharacter, "Start of range should be before end " + range.show) + assert(startLine == endLine, "multiline ranges not supported") // TODO implement multiline + (startCharacter until endCharacter).foreach { char => + val marker = new CodeMarker(start.name + "-with-offset=" + (char - startCharacter)) + implicit def posCtx2: PositionContext = posCtx.withPos(marker, (file, startLine, char)) + onMarker(marker) + } + } +} diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala new file mode 100644 index 000000000000..f235c2b5594a --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala @@ -0,0 +1,28 @@ +package dotty.tools.languageserver.util.actions + +import dotty.tools.languageserver.util.PositionContext +import dotty.tools.languageserver.util.embedded.CodeMarker +import dotty.tools.languageserver.util.server.TestFile + +import scala.collection.JavaConverters._ + +class CodeCompletion(val marker: CodeMarker, completions: List[(String, String, String)]) extends ActionOnMarker { + + override def execute(): Exec[Unit] = { + val res = server.completion(marker.toTextDocumentPositionParams).get() + assert(res.isRight, res) + val cList = res.getRight + assert(!cList.isIncomplete, res) + completions.foreach { completion => + assert( + cList.getItems.asScala.exists(item => + completion == (item.getLabel, item.getKind.toString, item.getDetail) + ), + "Did not return completion for " + completion + "\n" + cList.getItems.asScala.toList + ) + } + } + + override def show: PositionContext.PosCtx[String] = + s"CodeCompletion(${marker.show}, $completions)" +} diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala new file mode 100644 index 000000000000..2a202091d6bb --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala @@ -0,0 +1,16 @@ +package dotty.tools.languageserver.util.actions + +import dotty.tools.languageserver.util._ +import dotty.tools.languageserver.util.embedded.CodeMarker + +class CodeDefinition(val range: CodeRange, refOpt: Option[CodeRange]) extends ActionOnRange { + + override def onMarker(marker: CodeMarker): Exec[Unit] = { + val res = server.definition(fix(marker.toTextDocumentPositionParams)).get() + assert(res.size() == refOpt.size, res) + refOpt.foreach(ref => assert(res.get(0) == ref.toLocation, res)) + } + + override def show: PositionContext.PosCtx[String] = + s"CodeDefinition(${range.show}, ${refOpt.map(_.show)})" +} diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala new file mode 100644 index 000000000000..fbbf210920b9 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala @@ -0,0 +1,26 @@ +package dotty.tools.languageserver.util.actions + +import dotty.tools.languageserver.util.embedded.CodeMarker +import dotty.tools.languageserver.util.{CodeRange, PositionContext} +import org.eclipse.lsp4j._ + +import scala.collection.JavaConverters._ + +class CodeDocumentHighlight(val range: CodeRange, highs: Seq[(CodeRange, String)]) extends ActionOnRange { + + override def onMarker(marker: CodeMarker): Exec[Unit] = { + val (refs, kinds) = highs.unzip + val res = server.documentHighlight(fix(marker.toTextDocumentPositionParams)).get() + assert(res.size() == refs.size, res) + assert(refs.size == kinds.length, res) + res.asScala.zip(refs).zip(kinds).foreach { case ((dhl, ref), kind) => + assert(dhl.getKind == DocumentHighlightKind.valueOf(kind), res) + assert(dhl.getRange == ref.toRange, res) + } + } + + override def show: PositionContext.PosCtx[String] = { + val (refs, kinds) = highs.unzip + s"CodeDocumentHighlight(${range.show}, ${refs.map(_.show)}, $kinds)" + } +} diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala new file mode 100644 index 000000000000..d00b280d68a8 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala @@ -0,0 +1,20 @@ +package dotty.tools.languageserver.util.actions + +import dotty.tools.languageserver.util.embedded.CodeMarker +import dotty.tools.languageserver.util.{PositionContext, SymInfo} + +import scala.collection.JavaConverters._ + +class CodeDocumentSymbol(val marker: CodeMarker, symbols: Seq[SymInfo]) extends ActionOnMarker { + + override def execute(): Exec[Unit] = { + val res = server.documentSymbol(marker.toDocumentSymbolParams).get() + assert(res.size() == symbols.size, res) + for ((symInfo, expected) <- res.asScala.zip(symbols)) { + assert(symInfo == expected.toSymInformation, res) + } + } + + override def show: PositionContext.PosCtx[String] = + s"CodeDocumentSymbol(${marker.show}, ${symbols.map(_.show)})" +} diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala new file mode 100644 index 000000000000..910bc1dc7ae5 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala @@ -0,0 +1,26 @@ +package dotty.tools.languageserver.util.actions + +import dotty.tools.languageserver.util.embedded.CodeMarker +import dotty.tools.languageserver.util.{CodeRange, PositionContext} + +import org.eclipse.lsp4j._ + +import PositionContext._ + +class CodeHover(val range: CodeRange, expected: String) extends ActionOnRange { + + override def onMarker(marker: CodeMarker): Exec[Unit] = { + val res = server.hover(fix(marker.toTextDocumentPositionParams)).get() + assert(res.getRange == null) + if (expected == "") assert(res.getContents == null, "Expected null contents in " + res) + else { + assert(res.getContents.size() == 1, res) + val content = res.getContents.get(0) + assert(content.isLeft, "Expected left but was " + content) + assert(content.getLeft == expected, s"Expected $expected but was ${content.getLeft}") + } + } + + override def show: PositionContext.PosCtx[String] = + s"CodeHover(${range.show}, $expected" +} diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala new file mode 100644 index 000000000000..b28082756c10 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala @@ -0,0 +1,18 @@ +package dotty.tools.languageserver.util.actions + +import dotty.tools.languageserver.util.embedded.CodeMarker +import dotty.tools.languageserver.util.{CodeRange, PositionContext} + +import scala.collection.JavaConverters._ + +class CodeReferences(val range: CodeRange, refs: List[CodeRange], withDecl: Boolean) extends ActionOnRange { + + override def onMarker(marker: CodeMarker): Exec[Unit] = { + val res = server.references(fix(marker.toReferenceParams(withDecl))).get() + assert(res.size() == refs.size) + res.asScala.zip(refs).foreach { case (loc, ref) => assert(loc == ref.toLocation, res) } + } + + override def show: PositionContext.PosCtx[String] = + s"CodeReferences(${range.show}, ${refs.map(_.show)}, $withDecl)" +} diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala new file mode 100644 index 000000000000..606c731951e5 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala @@ -0,0 +1,24 @@ +package dotty.tools.languageserver.util.actions + +import dotty.tools.languageserver.util.embedded.CodeMarker +import dotty.tools.languageserver.util.{CodeRange, PositionContext} + +import scala.collection.JavaConverters._ + +class CodeRename(val marker: CodeMarker, newName: String, expected: List[CodeRange]) extends ActionOnMarker { + + override def execute(): Exec[Unit] = { + val res = server.rename(marker.toRenameParams(newName)).get() + assert(res.getDocumentChanges == null, res) + val editItems = res.getChanges.values().asScala.flatMap(_.asScala) // TDO use a Map + assert(expected.forall { exp => + editItems.exists { editItem => + editItem.getNewText == newName && + editItem.getRange == exp.toRange + } + }, res) + } + + override def show: PositionContext.PosCtx[String] = + s"CodeRename(${marker.show}, $newName, ${expected.map(_.show)})" +} diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala new file mode 100644 index 000000000000..427860188d79 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala @@ -0,0 +1,20 @@ +package dotty.tools.languageserver.util.actions + +import dotty.tools.languageserver.util.{CodeRange, PositionContext, SymInfo} +import org.eclipse.lsp4j._ + +import scala.collection.JavaConverters._ + +class CodeSymbol(query: String, symbols: Seq[SymInfo]) extends Action { + + override def execute(): Exec[Unit] = { + val res = server.symbol(new WorkspaceSymbolParams(query)).get() + assert(res.size() == symbols.size, res) + for ((symInfo, expected) <- res.asScala.zip(symbols)) { + assert(symInfo == expected.toSymInformation, res) + } + } + + override def show: PositionContext.PosCtx[String] = + s"CodeDocumentSymbol($query, ${symbols.map(_.show)})" +} diff --git a/language-server/test/dotty/tools/languageserver/util/embedded/CodeInRange.scala b/language-server/test/dotty/tools/languageserver/util/embedded/CodeInRange.scala new file mode 100644 index 000000000000..89f87b81fba6 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/embedded/CodeInRange.scala @@ -0,0 +1,5 @@ +package dotty.tools.languageserver.util.embedded + +import dotty.tools.languageserver.util.CodeRange + +case class CodeInRange(text: String, range: CodeRange) extends Embedded diff --git a/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala b/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala new file mode 100644 index 000000000000..ac02e9305e9c --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala @@ -0,0 +1,44 @@ +package dotty.tools.languageserver.util.embedded + +import dotty.tools.languageserver.util.server.TestFile +import dotty.tools.languageserver.util.{CodeRange, PositionContext} + +import org.eclipse.lsp4j._ + +import PositionContext._ + +/** Used to mark positions in the code */ +class CodeMarker(val name: String) extends Embedded { + + def to(other: CodeMarker): CodeRange = CodeRange(this, other) + + def file: PosCtx[TestFile] = implicitly[PositionContext].positionOf(this)._1 + + def line: PosCtx[Int] = implicitly[PositionContext].positionOf(this)._2 + + def character: PosCtx[Int] = implicitly[PositionContext].positionOf(this)._3 + + def toPosition: PosCtx[Position] = new Position(line, character) + + def toTextDocumentPositionParams: PosCtx[TextDocumentPositionParams] = + new TextDocumentPositionParams(toTextDocumentIdentifier, toPosition) + + def toDocumentSymbolParams: PosCtx[DocumentSymbolParams] = + new DocumentSymbolParams(toTextDocumentIdentifier) + + def toRenameParams(newName: String): PosCtx[RenameParams] = + new RenameParams(toTextDocumentIdentifier, toPosition, newName) + + def toTextDocumentIdentifier: PosCtx[TextDocumentIdentifier] = + new TextDocumentIdentifier(file.uri) + + def toReferenceParams(withDecl: Boolean): PosCtx[ReferenceParams] = { + val rp = new ReferenceParams(new ReferenceContext(withDecl)) + rp.setTextDocument(toTextDocumentIdentifier) + rp.setPosition(toPosition) + rp + } + + def show: PosCtx[String] = s"($name,line=$line,char=$character)" + override def toString: String = s"CodePosition($name)" +} diff --git a/language-server/test/dotty/tools/languageserver/util/embedded/Embedded.scala b/language-server/test/dotty/tools/languageserver/util/embedded/Embedded.scala new file mode 100644 index 000000000000..4058d0923713 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/embedded/Embedded.scala @@ -0,0 +1,3 @@ +package dotty.tools.languageserver.util.embedded + +trait Embedded diff --git a/language-server/test/dotty/tools/languageserver/util/server/TestClient.scala b/language-server/test/dotty/tools/languageserver/util/server/TestClient.scala new file mode 100644 index 000000000000..c900aadf05f0 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/server/TestClient.scala @@ -0,0 +1,35 @@ +package dotty.tools.languageserver.util.server + +import java.util.concurrent.CompletableFuture + +import org.eclipse.lsp4j._ +import org.eclipse.lsp4j.services._ + +class TestClient extends LanguageClient { + + private val log = new StringBuilder + + def getLog: String = log.result() + + override def logMessage(message: MessageParams) = { + log.append(message.toString) + } + + override def showMessage(messageParams: MessageParams) = { + log.append(messageParams.toString) + } + + override def telemetryEvent(obj: scala.Any) = { + log.append(obj.toString) + } + + override def showMessageRequest(requestParams: ShowMessageRequestParams) = { + log.append(requestParams.toString) + new CompletableFuture[MessageActionItem] + } + + override def publishDiagnostics(diagnostics: PublishDiagnosticsParams) = { + log.append(diagnostics.toString) + } + +} diff --git a/language-server/test/dotty/tools/languageserver/util/server/TestFile.scala b/language-server/test/dotty/tools/languageserver/util/server/TestFile.scala new file mode 100644 index 000000000000..a77e82dbdc40 --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/server/TestFile.scala @@ -0,0 +1,14 @@ +package dotty.tools.languageserver.util.server + +import java.nio.file.{Path, Paths} + +import org.eclipse.lsp4j.TextDocumentIdentifier + +class TestFile(val file: String) extends AnyVal { + def uri: String = s"file://${TestFile.sourceDir}/$file" +} + +object TestFile { + lazy val testDir: Path = Paths.get("../out/ide-tests").toAbsolutePath + lazy val sourceDir: Path = testDir.resolve("src") +} diff --git a/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala b/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala new file mode 100644 index 000000000000..8133a05c789f --- /dev/null +++ b/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala @@ -0,0 +1,60 @@ +package dotty.tools.languageserver.util.server + +import java.io.PrintWriter +import java.net.URI +import java.nio.file.Path +import java.util + +import dotty.tools.languageserver.DottyLanguageServer +import org.eclipse.lsp4j._ + +import scala.collection.JavaConverters._ + +class TestServer(testFolder: Path) { + + // TODO initialise config file from DottyIDEPlugin + private val baseDir = java.nio.file.Paths.get("..").toAbsolutePath + private val ivyDir = java.nio.file.Paths.get("~/.ivy2").toAbsolutePath + private val javaHome = System.getProperty("java.home") + private val dottyIdeJson: String = + s"""[ { + | "id" : "dotty-ide-test", + | "compilerVersion" : "0.6.0-bin-SNAPSHOT-nonbootstrapped", + | "compilerArguments" : [ "-feature", "-deprecation", "-unchecked", "-Xfatal-warnings", "-encoding", "UTF8", "-language:existentials,higherKinds,implicitConversions" ], + | "sourceDirectories" : [ "$baseDir/out/ide-tests/src" ], + | "dependencyClasspath" : [ "$baseDir/library/../out/bootstrap/dotty-library-bootstrapped/scala-0.7/classes", "$ivyDir/cache/org.scala-lang/scala-library/jars/scala-library-2.12.4.jar" ], + | "classDirectory" : "$baseDir/out/ide-tests/out" + |} + |] + """.stripMargin + private val configFile = testFolder.resolve(DottyLanguageServer.IDE_CONFIG_FILE) + testFolder.toFile.mkdirs() + testFolder.resolve("src").toFile.mkdirs() + testFolder.resolve("out").toFile.mkdirs() + new PrintWriter(configFile.toString) { write(dottyIdeJson); close() } + + val server = new DottyLanguageServer + private val client = new TestClient + server.connect(client) + + private val initParams = new InitializeParams() + initParams.setRootUri(testFolder.toAbsolutePath.toUri.toString) + server.initialize(initParams).get() + + /** Open the code in the given file and returns the file. + * @param code code in file + * @param fileName file path in the source directory + * @return the file opened + */ + def openCode(code: String, fileName: String): TestFile = { + val testFile = new TestFile(fileName) + val dotdp = new DidOpenTextDocumentParams() + val tdi = new TextDocumentItem() + tdi.setUri(testFile.uri) + tdi.setText(code) + dotdp.setTextDocument(tdi) + server.didOpen(dotdp) + testFile + } + +} diff --git a/project/Build.scala b/project/Build.scala index e93cbb946b88..69c2b94b466b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -772,6 +772,8 @@ object Build { ), javaOptions := (javaOptions in `dotty-compiler-bootstrapped`).value, + test := {}, // Tests should be run with dotty-language-server/test:run + run := Def.inputTaskDyn { val inputArgs = spaceDelimited("").parsed From 0d01da8e69741753674d4079b5e0fc8ae931f3cd Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 28 Mar 2018 10:50:52 +0200 Subject: [PATCH 02/19] Add documentation for `DiffUtil.mkColoredLineDiff` --- compiler/src/dotty/tools/dotc/util/DiffUtil.scala | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/util/DiffUtil.scala b/compiler/src/dotty/tools/dotc/util/DiffUtil.scala index 37159c90e50e..3028562ec643 100644 --- a/compiler/src/dotty/tools/dotc/util/DiffUtil.scala +++ b/compiler/src/dotty/tools/dotc/util/DiffUtil.scala @@ -59,11 +59,20 @@ object DiffUtil { (fnd, exp, totalChange.toDouble / (expected.length + found.length)) } + /** + * Return a colored diff between the tokens of every line in `expected` and `actual`. Each line of + * output contains the expected value on the left and the actual value on the right. + * + * @param expected The expected lines + * @param actual The actual lines + * @return A string with one element of `expected` and `actual` on each lines, where + * differences are highlighted. + */ def mkColoredLineDiff(expected: Seq[String], actual: Seq[String]): String = { - val expectedSize = DiffUtil.EOF.length max expected.map(_.length).max + val expectedSize = EOF.length max expected.map(_.length).max actual.padTo(expected.length, "").zip(expected.padTo(actual.length, "")).map { case (act, exp) => mkColoredLineDiff(exp, act, expectedSize) - }.mkString("\n") + }.mkString(System.lineSeparator) } def mkColoredLineDiff(expected: String, actual: String, expectedSize: Int): String = { From 480f6a2d52b73d7246468620c69e3043f9893d3f Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 28 Mar 2018 10:52:16 +0200 Subject: [PATCH 03/19] Typo: competion -> completion --- .../test/dotty/tools/languageserver/CompletionTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala index 079afc17073f..96aeaea9d33e 100644 --- a/language-server/test/dotty/tools/languageserver/CompletionTest.scala +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -6,7 +6,7 @@ import dotty.tools.languageserver.util.Code._ class CompletionTest { - @Test def competion0: Unit = { + @Test def completion0: Unit = { code"class Foo { val xyz: Int = 0; def y: Int = xy$m1 }".withSource .completion(m1, List(("xyz", "Field", "Int"))) } From 1fa3a931a863b30b8dbc1e4b447d16c91af91aac Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 28 Mar 2018 11:26:07 +0200 Subject: [PATCH 04/19] Use JUnit to run IDE tests --- .drone.yml | 2 +- .../dotty/tools/languageserver/Main.scala | 63 ------------------- project/Build.scala | 3 +- 3 files changed, 2 insertions(+), 66 deletions(-) delete mode 100644 language-server/test/dotty/tools/languageserver/Main.scala diff --git a/.drone.yml b/.drone.yml index 374c10c4b6e8..4d063a8a49ef 100644 --- a/.drone.yml +++ b/.drone.yml @@ -48,7 +48,7 @@ pipeline: image: lampepfl/dotty:2018-04-10 commands: - cp -R . /tmp/4/ && cd /tmp/4/ - - ./project/scripts/sbt dotty-language-server/test:run + - ./project/scripts/sbt dotty-language-server/test test_sbt: group: test diff --git a/language-server/test/dotty/tools/languageserver/Main.scala b/language-server/test/dotty/tools/languageserver/Main.scala deleted file mode 100644 index 6ba33fe24f06..000000000000 --- a/language-server/test/dotty/tools/languageserver/Main.scala +++ /dev/null @@ -1,63 +0,0 @@ -package dotty.tools.languageserver - -import org.junit.Test - -import java.lang.reflect.InvocationTargetException - -// TODO remove this and use JUnit to run the tests -object Main { - def main(args: Array[String]): Unit = { - var testsFailed = 0 - for (clazz <- testsClasses) { - val t0 = System.currentTimeMillis() - var passed = 0 - var failed = 0 - println(s"Starting tests in ${clazz.getSimpleName}") - for (method <- clazz.getMethods.sortBy(_.getName)) { - if (method.getAnnotation(classOf[Test]) ne null) { - print(s"Testing $clazz.${method.getName} ") - try { - method.invoke(clazz.getConstructor().newInstance()) - println(Console.GREEN + "passed" + Console.RESET) - passed += 1 - } catch { - case ex: InvocationTargetException => - ex.getCause match { - case ex1: AssertionError => - println(Console.RED + "failed" + Console.RESET) - System.err.println(s"${method.getName} failed with") - ex1.printStackTrace() - failed += 1 - case _ => throw ex.getCause - } - } - } - } - - val time = (System.currentTimeMillis() - t0).toDouble / 1000 - - if (failed == 0) { - println(s"${Console.GREEN}Passed all $passed tests${Console.RESET} in ${time}s") - } else { - testsFailed += 1 - System.err.println(s"Passed $passed, ${Console.RED}failed $failed${Console.RESET}, total ${passed + failed} in ${time}s") - } - println() - } - if (testsFailed != 0) { - System.err.println(s"Failed $testsFailed tests") - System.exit(1) - } - } - - private def testsClasses = List( - classOf[HighlightTest], - classOf[CompletionTest], - classOf[DefinitionTest], - classOf[HoverTest], - classOf[ReferencesTest], - classOf[RenameTest], - classOf[DocumentSymbolTest], - classOf[SymbolTest], - ) -} diff --git a/project/Build.scala b/project/Build.scala index 69c2b94b466b..da4d8e3ce397 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -766,14 +766,13 @@ object Build { // fork so that the shutdown hook in Main is run when we ctrl+c a run // (you need to have `cancelable in Global := true` in your global sbt config to ctrl+c a run) fork in run := true, + fork in Test := true, libraryDependencies ++= Seq( "org.eclipse.lsp4j" % "org.eclipse.lsp4j" % "0.3.0", Dependencies.`jackson-databind` ), javaOptions := (javaOptions in `dotty-compiler-bootstrapped`).value, - test := {}, // Tests should be run with dotty-language-server/test:run - run := Def.inputTaskDyn { val inputArgs = spaceDelimited("").parsed From f8498b7c60b00b1127fa53923507309fc6f37ece Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 28 Mar 2018 17:41:19 +0200 Subject: [PATCH 05/19] Generate config for IDE tests with sbt-buildinfo --- .../util/server/TestServer.scala | 16 ++++----- project/Build.scala | 36 +++++++++++++++++++ project/plugins.sbt | 2 ++ 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala b/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala index 8133a05c789f..ef63e3be607d 100644 --- a/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala +++ b/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala @@ -12,18 +12,16 @@ import scala.collection.JavaConverters._ class TestServer(testFolder: Path) { - // TODO initialise config file from DottyIDEPlugin - private val baseDir = java.nio.file.Paths.get("..").toAbsolutePath - private val ivyDir = java.nio.file.Paths.get("~/.ivy2").toAbsolutePath - private val javaHome = System.getProperty("java.home") + // Fill the configuration with values populated by sbt + private def showSeq[T](lst: Seq[T]): String = lst.map(elem => '"' + elem.toString + '"').mkString("[ ", ", ", " ]") private val dottyIdeJson: String = s"""[ { | "id" : "dotty-ide-test", - | "compilerVersion" : "0.6.0-bin-SNAPSHOT-nonbootstrapped", - | "compilerArguments" : [ "-feature", "-deprecation", "-unchecked", "-Xfatal-warnings", "-encoding", "UTF8", "-language:existentials,higherKinds,implicitConversions" ], - | "sourceDirectories" : [ "$baseDir/out/ide-tests/src" ], - | "dependencyClasspath" : [ "$baseDir/library/../out/bootstrap/dotty-library-bootstrapped/scala-0.7/classes", "$ivyDir/cache/org.scala-lang/scala-library/jars/scala-library-2.12.4.jar" ], - | "classDirectory" : "$baseDir/out/ide-tests/out" + | "compilerVersion" : "${BuildInfo.ideTestsCompilerVersion}", + | "compilerArguments" : ${showSeq(BuildInfo.ideTestsCompilerArguments)}, + | "sourceDirectories" : ${showSeq(BuildInfo.ideTestsSourceDirectories)}, + | "dependencyClasspath" : ${showSeq(BuildInfo.ideTestsDependencyClasspath)}, + | "classDirectory" : "${BuildInfo.ideTestsClassDirectory}" |} |] """.stripMargin diff --git a/project/Build.scala b/project/Build.scala index da4d8e3ce397..eaca19bdc07a 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -18,6 +18,9 @@ import dotty.tools.sbtplugin.DottyPlugin.autoImport._ import dotty.tools.sbtplugin.DottyIDEPlugin.{ prepareCommand, runProcess } import dotty.tools.sbtplugin.DottyIDEPlugin.autoImport._ +import sbtbuildinfo.BuildInfoPlugin +import sbtbuildinfo.BuildInfoPlugin.autoImport._ + /* In sbt 0.13 the Build trait would expose all vals to the shell, where you * can use them in "set a := b" like expressions. This re-exposes them. */ @@ -85,6 +88,13 @@ object Build { // Only available in vscode-dotty lazy val unpublish = taskKey[Unit]("Unpublish a package") + // Settings used to configure the test language server + lazy val ideTestsCompilerVersion = taskKey[String]("Compiler version to use in IDE tests") + lazy val ideTestsCompilerArguments = taskKey[Seq[String]]("Compiler arguments to use in IDE tests") + lazy val ideTestsSourceDirectories = taskKey[Seq[File]]("Source directories to use in IDE tests") + lazy val ideTestsDependencyClasspath = taskKey[Seq[File]]("Dependency classpath to use in IDE tests") + lazy val ideTestsClassDirectory = taskKey[File]("Class directory to use in IDE tests") + // Settings shared by the build (scoped in ThisBuild). Used in build.sbt lazy val thisBuildSettings = Def.settings( organization := dottyOrganization, @@ -789,6 +799,32 @@ object Build { runTask(Runtime, mainClass, allArgs: _*) }.dependsOn(compile in (`vscode-dotty`, Compile)).evaluated + ). + settings( + ideTestsCompilerVersion := (version in `dotty-compiler`).value, + ideTestsCompilerArguments := (scalacOptions in `dotty-compiler`).value, + ideTestsSourceDirectories := Seq((baseDirectory in ThisBuild).value / "out" / "ide-tests" / "src"), + ideTestsDependencyClasspath := { + val dottyLib = (classDirectory in `dotty-library-bootstrapped` in Compile).value + val scalaLib = + (dependencyClasspath in `dotty-library-bootstrapped` in Compile) + .value + .map(_.data) + .filter(_.getName.matches("scala-library.*\\.jar")) + .toList + dottyLib :: scalaLib + }, + ideTestsClassDirectory := (baseDirectory in ThisBuild).value / "out" / "ide-tests" / "out", + buildInfoKeys in Test := Seq[BuildInfoKey]( + ideTestsCompilerVersion, + ideTestsCompilerArguments, + ideTestsSourceDirectories, + ideTestsDependencyClasspath, + ideTestsClassDirectory + ), + buildInfoPackage in Test := "dotty.tools.languageserver.util.server", + BuildInfoPlugin.buildInfoScopedSettings(Test), + BuildInfoPlugin.buildInfoDefaultSettings ).disablePlugins(ScriptedPlugin) lazy val `dotty-bench` = project.in(file("bench")).asDottyBench(NonBootstrapped) diff --git a/project/plugins.sbt b/project/plugins.sbt index 8e9587ae889d..83690d191839 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,3 +14,5 @@ addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0") addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.10.1") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.2") + +addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.7.0") From 2e5211eb27047f5d3e04b7b14206627bf989ff6c Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 28 Mar 2018 17:53:07 +0200 Subject: [PATCH 06/19] Use `SymbolKind` in `SymInfo` --- .../tools/languageserver/DocumentSymbolTest.scala | 10 ++++++---- .../test/dotty/tools/languageserver/SymbolTest.scala | 5 +++-- .../dotty/tools/languageserver/util/CodeRange.scala | 4 ++-- .../test/dotty/tools/languageserver/util/SymInfo.scala | 6 +++--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/DocumentSymbolTest.scala b/language-server/test/dotty/tools/languageserver/DocumentSymbolTest.scala index c647d2d5f9f4..6add328b57dd 100644 --- a/language-server/test/dotty/tools/languageserver/DocumentSymbolTest.scala +++ b/language-server/test/dotty/tools/languageserver/DocumentSymbolTest.scala @@ -1,23 +1,25 @@ package dotty.tools.languageserver import org.junit.Test +import org.eclipse.lsp4j.SymbolKind import dotty.tools.languageserver.util.Code._ + class DocumentSymbolTest { @Test def documentSymbol0: Unit = - code"class ${m1}Foo$m2".withSource.documentSymbol(m1, (m1 to m2).symInfo("Foo", "Class")) + code"class ${m1}Foo$m2".withSource.documentSymbol(m1, (m1 to m2).symInfo("Foo", SymbolKind.Class)) @Test def documentSymbol1: Unit = code"class ${m1}Foo$m2; class ${m3}Bar$m4".withSource - .documentSymbol(m1, (m1 to m2).symInfo("Foo", "Class"), (m3 to m4).symInfo("Bar", "Class")) + .documentSymbol(m1, (m1 to m2).symInfo("Foo", SymbolKind.Class), (m3 to m4).symInfo("Bar", SymbolKind.Class)) @Test def documentSymbol3: Unit = { withSources( code"class ${m1}Foo$m2", code"class ${m3}Bar$m4" - ) .documentSymbol(m1, (m1 to m2).symInfo("Foo", "Class")) - .documentSymbol(m3, (m3 to m4).symInfo("Bar", "Class")) + ) .documentSymbol(m1, (m1 to m2).symInfo("Foo", SymbolKind.Class)) + .documentSymbol(m3, (m3 to m4).symInfo("Bar", SymbolKind.Class)) } } diff --git a/language-server/test/dotty/tools/languageserver/SymbolTest.scala b/language-server/test/dotty/tools/languageserver/SymbolTest.scala index cd66da333cca..218fb19f7a4a 100644 --- a/language-server/test/dotty/tools/languageserver/SymbolTest.scala +++ b/language-server/test/dotty/tools/languageserver/SymbolTest.scala @@ -1,6 +1,7 @@ package dotty.tools.languageserver import org.junit.Test +import org.eclipse.lsp4j.SymbolKind import dotty.tools.languageserver.util.Code._ @@ -21,7 +22,7 @@ class SymbolTest { | class Bar |} """ - ) .symbol("Foo", Foo.range.symInfo("Foo", "Class"), fooFoo.range.symInfo("Foo", "Class", "foo")) - .symbol("Bar", Bar.range.symInfo("Bar", "Class")) + ) .symbol("Foo", Foo.range.symInfo("Foo", SymbolKind.Class), fooFoo.range.symInfo("Foo", SymbolKind.Class, "foo")) + .symbol("Bar", Bar.range.symInfo("Bar", SymbolKind.Class)) } } diff --git a/language-server/test/dotty/tools/languageserver/util/CodeRange.scala b/language-server/test/dotty/tools/languageserver/util/CodeRange.scala index ee8ba76ce48a..ca6f7e25df2e 100644 --- a/language-server/test/dotty/tools/languageserver/util/CodeRange.scala +++ b/language-server/test/dotty/tools/languageserver/util/CodeRange.scala @@ -30,7 +30,7 @@ case class CodeRange(start: CodeMarker, end: CodeMarker) { def withCode(text: String): CodeInRange = CodeInRange(text, this) - def symInfo(name: String, kind: String, container: String = null): SymInfo = + def symInfo(name: String, kind: SymbolKind, container: String = null): SymInfo = new SymInfo(name, kind, this, container) def toRange: PosCtx[Range] = { @@ -45,4 +45,4 @@ case class CodeRange(start: CodeMarker, end: CodeMarker) { def show: PosCtx[String] = s"[start=${start.show}, end=${end.show}]" -} \ No newline at end of file +} diff --git a/language-server/test/dotty/tools/languageserver/util/SymInfo.scala b/language-server/test/dotty/tools/languageserver/util/SymInfo.scala index ded418518075..64e5cc7ade31 100644 --- a/language-server/test/dotty/tools/languageserver/util/SymInfo.scala +++ b/language-server/test/dotty/tools/languageserver/util/SymInfo.scala @@ -3,12 +3,12 @@ package dotty.tools.languageserver.util import dotty.tools.languageserver.util.PositionContext._ import org.eclipse.lsp4j._ -class SymInfo(name: String, kind: String, range: CodeRange, container: String) { +class SymInfo(name: String, kind: SymbolKind, range: CodeRange, container: String) { def toSymInformation: PosCtx[SymbolInformation] = - new SymbolInformation(name, SymbolKind.valueOf(kind), range.toLocation, container) + new SymbolInformation(name, kind, range.toLocation, container) def show: PosCtx[String] = s"SymInfo($name, $kind, ${range.show}, $container)" override def toString: String = s"SymInfo($name, $kind, $range, $container)" -} \ No newline at end of file +} From cc0cb290e073ac19cc68d8d55919c7f5726c9779 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 28 Mar 2018 18:33:17 +0200 Subject: [PATCH 07/19] Support multiple results in `CodeDefinition` --- .../tools/languageserver/DefinitionTest.scala | 20 +++++++++---------- .../languageserver/util/CodeTester.scala | 2 +- .../util/actions/CodeDefinition.scala | 12 ++++++----- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/DefinitionTest.scala b/language-server/test/dotty/tools/languageserver/DefinitionTest.scala index 3e3a1de3ec59..cf18ea0914b8 100644 --- a/language-server/test/dotty/tools/languageserver/DefinitionTest.scala +++ b/language-server/test/dotty/tools/languageserver/DefinitionTest.scala @@ -7,33 +7,33 @@ import dotty.tools.languageserver.util.Code._ class DefinitionTest { @Test def classDefinitionNotFound0: Unit = - code"class Foo { new ${m1}Bar$m2 }".withSource.definition(m1 to m2, None) + code"class Foo { new ${m1}Bar$m2 }".withSource.definition(m1 to m2, Nil) @Test def classDefinition0: Unit = { withSources( code"class ${m1}Foo$m2 { new Foo }", code"class Bar { val foo: ${m3}Foo$m4 = new ${m5}Foo$m6 }" - ) .definition(m1 to m2, Some(m1 to m2)) - .definition(m3 to m4, Some(m1 to m2)) - .definition(m5 to m6, Some(m1 to m2)) + ) .definition(m1 to m2, List(m1 to m2)) + .definition(m3 to m4, List(m1 to m2)) + .definition(m5 to m6, List(m1 to m2)) } @Test def valDefinition0: Unit = { withSources( code"class Foo { val ${m1}x$m2 = 0; ${m3}x$m4 }", code"class Bar { val foo = new Foo; foo.${m5}x$m6 }" - ) .definition(m1 to m2, Some(m1 to m2)) - .definition(m3 to m4, Some(m1 to m2)) - .definition(m5 to m6, Some(m1 to m2)) + ) .definition(m1 to m2, List(m1 to m2)) + .definition(m3 to m4, List(m1 to m2)) + .definition(m5 to m6, List(m1 to m2)) } @Test def defDefinition0: Unit = { withSources( code"class Foo { def ${m1}x$m2 = 0; ${m3}x$m4 }", code"class Bar { val foo = new Foo; foo.${m5}x$m6 }" - ) .definition(m1 to m2, Some(m1 to m2)) - .definition(m3 to m4, Some(m1 to m2)) - .definition(m5 to m6, Some(m1 to m2)) + ) .definition(m1 to m2, List(m1 to m2)) + .definition(m3 to m4, List(m1 to m2)) + .definition(m5 to m6, List(m1 to m2)) } } diff --git a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala index 28a597def3e4..41dbe2faab76 100644 --- a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala +++ b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala @@ -18,7 +18,7 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { def hover(range: CodeRange, expected: String): CodeTester = doAction(new CodeHover(range, expected)) - def definition(range: CodeRange, refOpt: Option[CodeRange]): CodeTester = + def definition(range: CodeRange, refOpt: Seq[CodeRange]): CodeTester = doAction(new CodeDefinition(range, refOpt)) def highlight(range: CodeRange, highs: (CodeRange, String)*): CodeTester = diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala index 2a202091d6bb..3dc6e171f84e 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala @@ -3,14 +3,16 @@ package dotty.tools.languageserver.util.actions import dotty.tools.languageserver.util._ import dotty.tools.languageserver.util.embedded.CodeMarker -class CodeDefinition(val range: CodeRange, refOpt: Option[CodeRange]) extends ActionOnRange { +class CodeDefinition(val range: CodeRange, expected: Seq[CodeRange]) extends ActionOnRange { override def onMarker(marker: CodeMarker): Exec[Unit] = { - val res = server.definition(fix(marker.toTextDocumentPositionParams)).get() - assert(res.size() == refOpt.size, res) - refOpt.foreach(ref => assert(res.get(0) == ref.toLocation, res)) + val results = server.definition(fix(marker.toTextDocumentPositionParams)).get() + assert(results.size == expected.size, s"Expected ${expected.size} matches, found ${results.size}") + (0 until results.size).foreach { i => + assert(results.get(i) == expected(i).toLocation, s"Expected ${expected(i).toLocation}, found ${results.get(i)}.") + } } override def show: PositionContext.PosCtx[String] = - s"CodeDefinition(${range.show}, ${refOpt.map(_.show)})" + s"CodeDefinition(${range.show}, ${expected.map(_.show)})" } From 4aa5827ad789f436258473f48ba09127ab49d79f Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 28 Mar 2018 20:48:14 +0200 Subject: [PATCH 08/19] Add documentation for the IDE tests --- .../tools/languageserver/util/Code.scala | 30 ++++- .../tools/languageserver/util/CodeRange.scala | 6 + .../languageserver/util/CodeTester.scala | 105 ++++++++++++++++-- .../languageserver/util/actions/Action.scala | 10 ++ .../util/actions/ActionOnMarker.scala | 2 + .../util/actions/ActionOnRange.scala | 6 +- .../util/actions/CodeCompletion.scala | 18 ++- .../util/actions/CodeDefinition.scala | 9 +- .../util/actions/CodeDocumentHighlight.scala | 30 +++-- .../util/actions/CodeDocumentSymbol.scala | 20 +++- .../util/actions/CodeHover.scala | 21 ++-- .../util/actions/CodeReferences.scala | 20 +++- .../util/actions/CodeRename.scala | 20 +++- .../util/actions/CodeSymbol.scala | 15 ++- .../util/embedded/CodeInRange.scala | 6 + .../util/embedded/CodeMarker.scala | 5 + 16 files changed, 268 insertions(+), 55 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/util/Code.scala b/language-server/test/dotty/tools/languageserver/util/Code.scala index db7220813eaf..a0ec56d290ee 100644 --- a/language-server/test/dotty/tools/languageserver/util/Code.scala +++ b/language-server/test/dotty/tools/languageserver/util/Code.scala @@ -2,6 +2,13 @@ package dotty.tools.languageserver.util import dotty.tools.languageserver.util.embedded._ +/** + * Helper object to create virtual source files and extract markers from the source files, using the + * `code` interpolator. + * + * The markers can then be used to perform tests at different positions within the source + * file. + */ object Code { // Default positions @@ -15,6 +22,19 @@ object Code { val m8 = new CodeMarker("m8") implicit class CodeHelper(val sc: StringContext) extends AnyVal { + + /** + * An interpolator that lets set marker inside a virtual source file. + * + * For instance: + * ``` + * code"""object ${m1}Foo${m2} { def bar = ${m3}Hello{$m4}.quux }""" + * ``` + * + * This will define a source file where the markers `m1` and `m2` enclose the identifier `Foo`, + * and `m3` and `m4` enclose the identifier `Hello`. These positions can then be used to ask to + * perform actions such as finding all references, etc. + */ def code(args: Embedded*): SourceWithPositions = { val pi = sc.parts.iterator val ai = args.iterator @@ -54,10 +74,18 @@ object Code { } } + /** A new `CodeTester` working with `sources` in the workspace. */ def withSources(sources: SourceWithPositions*): CodeTester = new CodeTester(sources.toList, Nil) + /** + * A virtual source file where several markers have been set. + * + * @param text The code contained within the virtual source file. + * @param positions The positions of the markers that have been set. + */ case class SourceWithPositions(text: String, positions: List[(CodeMarker, Int, Int)]) { - def withSource: CodeTester = new CodeTester(this :: Nil, Nil) + /** A new `CodeTester` with only this source in the workspace. */ + def withSource: CodeTester = new CodeTester(this :: Nil, Nil) } } diff --git a/language-server/test/dotty/tools/languageserver/util/CodeRange.scala b/language-server/test/dotty/tools/languageserver/util/CodeRange.scala index ca6f7e25df2e..bc4a585e2782 100644 --- a/language-server/test/dotty/tools/languageserver/util/CodeRange.scala +++ b/language-server/test/dotty/tools/languageserver/util/CodeRange.scala @@ -7,6 +7,12 @@ import org.eclipse.lsp4j._ import PositionContext._ +/** + * A range of positions between two markers. + * + * @param start The start marker. + * @param end The end marker. + */ case class CodeRange(start: CodeMarker, end: CodeMarker) { private var checked = false def check(): PosCtx[Unit] = { diff --git a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala index 41dbe2faab76..ad063295304d 100644 --- a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala +++ b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala @@ -6,6 +6,12 @@ import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.server.{TestFile, TestServer} import org.eclipse.lsp4j.SymbolInformation +/** + * Simulates an LSP client for test in a workspace defined by `sources`. + * + * @param sources The list of sources in the workspace + * @param actions Unused + */ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { private val testServer = new TestServer(TestFile.testDir) @@ -15,27 +21,106 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { } private val positions: PositionContext = getPositions(files) + /** + * Perform a hover over `range`, verifies that result matches `expected`. + * + * @param range The range over which to hover. + * @param expected The expected result. + * @return This `CodeTester` after performing the action. + * + * @see dotty.tools.languageserver.util.actions.CodeHover + */ def hover(range: CodeRange, expected: String): CodeTester = doAction(new CodeHover(range, expected)) - def definition(range: CodeRange, refOpt: Seq[CodeRange]): CodeTester = - doAction(new CodeDefinition(range, refOpt)) + /** + * Perform a jump to definition over `range`, verifies that the results are `expected`. + * + * @param range The range of positions from which run `jump to definition`. + * @param expected The expected positions to jump to. + * @return This `CodeTester` after performing the action. + * + * @see dotty.tools.languageserver.util.actions.CodeDefinition + */ + def definition(range: CodeRange, expected: Seq[CodeRange]): CodeTester = + doAction(new CodeDefinition(range, expected)) - def highlight(range: CodeRange, highs: (CodeRange, String)*): CodeTester = - doAction(new CodeDocumentHighlight(range, highs)) + /** + * Perform a highlight over `range`, verifies that the ranges and kinds of symbols match + * `expected`. + * + * @param range The range of positions to highlight. + * @param expected The expected ranges and the kind of symbols that should be highlighted. + * @return This `CodeTester` after performing the action. + * + * @see dotty.tools.languageserver.util.actions.CodeDefinition + */ + def highlight(range: CodeRange, expected: (CodeRange, String)*): CodeTester = + doAction(new CodeDocumentHighlight(range, expected)) - def references(range: CodeRange, refs: List[CodeRange], withDecl: Boolean = false): CodeTester = - doAction(new CodeReferences(range, refs, withDecl)) + /** + * Finds all the references to the symbol in `range`, verifies that the results match `expected`. + * + * @param range The range of positions from which search for references. + * @param expected The expected positions of the references + * @param withDecl When set, include the declaration of the symbol under `range` in the results. + * @return This `CodeTester` after performing the action. + * + * @see dotty.tools.languageserver.util.actions.CodeReferences + */ + def references(range: CodeRange, expected: List[CodeRange], withDecl: Boolean = false): CodeTester = + doAction(new CodeReferences(range, expected, withDecl)) - def completion(marker: CodeMarker, completions: List[(String, String, String)]): CodeTester = - doAction(new CodeCompletion(marker, completions)) + /** + * Requests completion at the position defined by `marker`, verifies that the results match + * `expected`. + * + * @param marker The position from which to ask for completions. + * @param expected The expected completion results. + * @return This `CodeTester` after performing the action. + * + * @see dotty.tools.languageserver.util.actions.CodeCompletion + */ + def completion(marker: CodeMarker, expected: List[(String, String, String)]): CodeTester = + doAction(new CodeCompletion(marker, expected)) + /** + * Performs a workspace-wide renaming of the symbol under `marker`, verifies that the positions to + * update match `expected`. + * + * @param marker The position from which to ask for renaming. + * @param newName The new name to give to the symbol. + * @param expected The expected list of positions to change. + * @return This `CodeTester` after performing the action. + * + * @see dotty.tools.languageserver.util.actions.CodeRename + */ def rename(marker: CodeMarker, newName: String, expected: List[CodeRange]): CodeTester = doAction(new CodeRename(marker, newName, expected)) // TODO apply changes to the sources and positions - def documentSymbol(marker: CodeMarker, symbols: SymInfo*): CodeTester = - doAction(new CodeDocumentSymbol(marker, symbols)) + /** + * Queries for all the symbols referenced in the source file in `marker`, verifies that they match + * `expected`. + * + * @param marker The marker defining the source file from which to query. + * @param expected The expected symbols to be found. + * @return This `CodeTester` after performing the action. + * + * @see dotty.tools.languageserver.util.actions.CodeDocumentSymbol + */ + def documentSymbol(marker: CodeMarker, expected: SymInfo*): CodeTester = + doAction(new CodeDocumentSymbol(marker, expected)) + /** + * Queries the whole workspace for symbols matching `query`, verifies that the results match + * `expected`. + * + * @param query The query used to find symbols. + * @param expected The expected symbols to be found. + * @return This `CodeTester` after performing the action. + * + * @see dotty.tools.languageserver.util.actions.CodeSymbol + */ def symbol(query: String, symbols: SymInfo*): CodeTester = doAction(new CodeSymbol(query, symbols)) diff --git a/language-server/test/dotty/tools/languageserver/util/actions/Action.scala b/language-server/test/dotty/tools/languageserver/util/actions/Action.scala index a5faa560d0cd..272a4603530d 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/Action.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/Action.scala @@ -6,10 +6,20 @@ import dotty.tools.languageserver.util.server.TestServer import PositionContext._ +/** + * Base trait for representing an action performed against a language server (such as hover, go to + * definition, etc.) + */ trait Action { type Exec[T] = implicit (TestServer, PositionContext) => T + + /** Execute the action. */ def execute(): Exec[Unit] + + /** Return a textual representation of this action. */ def show: PosCtx[String] + + /** The server that this action targets. */ def server: Exec[DottyLanguageServer] = implicitly[TestServer].server // FIXME diff --git a/language-server/test/dotty/tools/languageserver/util/actions/ActionOnMarker.scala b/language-server/test/dotty/tools/languageserver/util/actions/ActionOnMarker.scala index de15f9bf5439..fca14d77b106 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/ActionOnMarker.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/ActionOnMarker.scala @@ -2,8 +2,10 @@ package dotty.tools.languageserver.util.actions import dotty.tools.languageserver.util.embedded.CodeMarker +/** An action that defines a `marker` where the action will be performed. */ trait ActionOnMarker extends Action { + /** The marker that defines where the action will be performed. */ def marker: CodeMarker } diff --git a/language-server/test/dotty/tools/languageserver/util/actions/ActionOnRange.scala b/language-server/test/dotty/tools/languageserver/util/actions/ActionOnRange.scala index b714fb0c2740..6c345037142a 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/ActionOnRange.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/ActionOnRange.scala @@ -3,12 +3,16 @@ package dotty.tools.languageserver.util.actions import dotty.tools.languageserver.util.{CodeRange, PositionContext} import dotty.tools.languageserver.util.embedded.CodeMarker +/** An action that executes on a range. */ trait ActionOnRange extends Action { + /** The range on which the action is performed. */ def range: CodeRange + + /** The action to perform for every point of the range. */ def onMarker(marker: CodeMarker): Exec[Unit] - def execute(): Exec[Unit] = { + override def execute(): Exec[Unit] = { val posCtx = implicitly[PositionContext] range.check() val file = range.file diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala index f235c2b5594a..f35907f512da 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala @@ -6,13 +6,21 @@ import dotty.tools.languageserver.util.server.TestFile import scala.collection.JavaConverters._ -class CodeCompletion(val marker: CodeMarker, completions: List[(String, String, String)]) extends ActionOnMarker { +/** + * An action requesting for code completion at `marker`, expecting `completions`. + * This action corresponds to the `textDocument/completion` method of the Language Server Protocol. + * + * @param marker The marker indicating the position where completion should be requested. + * @param completions The expected results from the language server. + */ +class CodeCompletion(override val marker: CodeMarker, + completions: List[(String, String, String)]) extends ActionOnMarker { override def execute(): Exec[Unit] = { - val res = server.completion(marker.toTextDocumentPositionParams).get() - assert(res.isRight, res) - val cList = res.getRight - assert(!cList.isIncomplete, res) + val result = server.completion(marker.toTextDocumentPositionParams).get() + assert(result.isRight, result) + val cList = result.getRight + assert(!cList.isIncomplete, result) completions.foreach { completion => assert( cList.getItems.asScala.exists(item => diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala index 3dc6e171f84e..fa2ec65d93dc 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala @@ -3,7 +3,14 @@ package dotty.tools.languageserver.util.actions import dotty.tools.languageserver.util._ import dotty.tools.languageserver.util.embedded.CodeMarker -class CodeDefinition(val range: CodeRange, expected: Seq[CodeRange]) extends ActionOnRange { +/** + * An action requesting for the definition of the symbol inside `range`. + * This action corresponds to the `textDocument/definition` method of the Language Server Protocol. + * + * @param range The range of positions for which to request the definition. + * @param expected The expected results. + */ +class CodeDefinition(override val range: CodeRange, expected: Seq[CodeRange]) extends ActionOnRange { override def onMarker(marker: CodeMarker): Exec[Unit] = { val results = server.definition(fix(marker.toTextDocumentPositionParams)).get() diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala index fbbf210920b9..436922e3b4a9 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala @@ -6,21 +6,31 @@ import org.eclipse.lsp4j._ import scala.collection.JavaConverters._ -class CodeDocumentHighlight(val range: CodeRange, highs: Seq[(CodeRange, String)]) extends ActionOnRange { +/** + * An action requesting for the ranges that should be highlighted, when a position within `range` + * is selected. + * This action corresponds to the `textDocument/documentHighlight` method of the Language Server + * Protocol. + * + * @param range The range to of positions to test. + * @param expected The expected results. + */ +class CodeDocumentHighlight(override val range: CodeRange, + expected: Seq[(CodeRange, String)]) extends ActionOnRange { override def onMarker(marker: CodeMarker): Exec[Unit] = { - val (refs, kinds) = highs.unzip - val res = server.documentHighlight(fix(marker.toTextDocumentPositionParams)).get() - assert(res.size() == refs.size, res) - assert(refs.size == kinds.length, res) - res.asScala.zip(refs).zip(kinds).foreach { case ((dhl, ref), kind) => - assert(dhl.getKind == DocumentHighlightKind.valueOf(kind), res) - assert(dhl.getRange == ref.toRange, res) + val (references, kinds) = expected.unzip + val results = server.documentHighlight(fix(marker.toTextDocumentPositionParams)).get() + assert(results.size() == references.size, results) + assert(references.size == kinds.length, results) + results.asScala.zip(references).zip(kinds).foreach { case ((dhl, ref), kind) => + assert(dhl.getKind == DocumentHighlightKind.valueOf(kind), results) + assert(dhl.getRange == ref.toRange, results) } } override def show: PositionContext.PosCtx[String] = { - val (refs, kinds) = highs.unzip - s"CodeDocumentHighlight(${range.show}, ${refs.map(_.show)}, $kinds)" + val (references, kinds) = expected.unzip + s"CodeDocumentHighlight(${range.show}, ${references.map(_.show)}, $kinds)" } } diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala index d00b280d68a8..8d0499a8dd69 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala @@ -5,16 +5,24 @@ import dotty.tools.languageserver.util.{PositionContext, SymInfo} import scala.collection.JavaConverters._ -class CodeDocumentSymbol(val marker: CodeMarker, symbols: Seq[SymInfo]) extends ActionOnMarker { +/** + * An action requesting for the symbols found in the document matching `marker`. + * This action corresponds to the `textDocument/documentSymbol` method of the Language Server + * Protocol. + * + * @param marker The marker that identifies the document for which to request the symbols. + * @param expected The expected symbols to receive. + */ +class CodeDocumentSymbol(override val marker: CodeMarker, expected: Seq[SymInfo]) extends ActionOnMarker { override def execute(): Exec[Unit] = { - val res = server.documentSymbol(marker.toDocumentSymbolParams).get() - assert(res.size() == symbols.size, res) - for ((symInfo, expected) <- res.asScala.zip(symbols)) { - assert(symInfo == expected.toSymInformation, res) + val results = server.documentSymbol(marker.toDocumentSymbolParams).get() + assert(results.size == expected.size, results) + for ((symInfo, expected) <- results.asScala.zip(expected)) { + assert(symInfo == expected.toSymInformation, results) } } override def show: PositionContext.PosCtx[String] = - s"CodeDocumentSymbol(${marker.show}, ${symbols.map(_.show)})" + s"CodeDocumentSymbol(${marker.show}, ${expected.map(_.show)})" } diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala index 910bc1dc7ae5..61c03ebee241 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala @@ -7,20 +7,27 @@ import org.eclipse.lsp4j._ import PositionContext._ -class CodeHover(val range: CodeRange, expected: String) extends ActionOnRange { +/** + * An action requesting for the info shown when `range` is hovered. + * This action corresponds to the `textDocument/hover` method of the Language Server Protocol. + * + * @param range The range of positions that should be hovered. + * @param expected The expected result. + */ +class CodeHover(override val range: CodeRange, expected: String) extends ActionOnRange { override def onMarker(marker: CodeMarker): Exec[Unit] = { - val res = server.hover(fix(marker.toTextDocumentPositionParams)).get() - assert(res.getRange == null) - if (expected == "") assert(res.getContents == null, "Expected null contents in " + res) + val result = server.hover(fix(marker.toTextDocumentPositionParams)).get() + assert(result.getRange == null) + if (expected == "") assert(result.getContents == null, "Expected null contents in " + result) else { - assert(res.getContents.size() == 1, res) - val content = res.getContents.get(0) + assert(result.getContents.size() == 1, result) + val content = result.getContents.get(0) assert(content.isLeft, "Expected left but was " + content) assert(content.getLeft == expected, s"Expected $expected but was ${content.getLeft}") } } override def show: PositionContext.PosCtx[String] = - s"CodeHover(${range.show}, $expected" + s"CodeHover(${range.show}, $expected)" } diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala index b28082756c10..d3b0bc9b2adb 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala @@ -5,14 +5,24 @@ import dotty.tools.languageserver.util.{CodeRange, PositionContext} import scala.collection.JavaConverters._ -class CodeReferences(val range: CodeRange, refs: List[CodeRange], withDecl: Boolean) extends ActionOnRange { +/** + * An action requesting for all the references to the symbol in `range` within the workspace. + * This action corresponds to the `textDocument/references` method of the Language Server Protocol. + * + * @param range The range of positions to test. + * @param expected The expected results. + * @param withDecl Whether the declaration of the current symbol should be included. + */ +class CodeReferences(override val range: CodeRange, + expected: List[CodeRange], + withDecl: Boolean) extends ActionOnRange { override def onMarker(marker: CodeMarker): Exec[Unit] = { - val res = server.references(fix(marker.toReferenceParams(withDecl))).get() - assert(res.size() == refs.size) - res.asScala.zip(refs).foreach { case (loc, ref) => assert(loc == ref.toLocation, res) } + val results = server.references(fix(marker.toReferenceParams(withDecl))).get() + assert(results.size() == expected.size) + results.asScala.zip(expected).foreach { case (loc, ref) => assert(loc == ref.toLocation, results) } } override def show: PositionContext.PosCtx[String] = - s"CodeReferences(${range.show}, ${refs.map(_.show)}, $withDecl)" + s"CodeReferences(${range.show}, ${expected.map(_.show)}, $withDecl)" } diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala index 606c731951e5..6cae3e18d8ef 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala @@ -5,18 +5,28 @@ import dotty.tools.languageserver.util.{CodeRange, PositionContext} import scala.collection.JavaConverters._ -class CodeRename(val marker: CodeMarker, newName: String, expected: List[CodeRange]) extends ActionOnMarker { +/** + * An action requesting for a rename of the symbol at `marker`. + * This action corresponds to the `textDocument/rename` method of the Language Server Protocol. + * + * @param marker The positions where to test to rename. + * @param newName The new name to give to the selected symbol. + * @param expected The expected ranges that should be modified. + */ +class CodeRename(override val marker: CodeMarker, + newName: String, + expected: List[CodeRange]) extends ActionOnMarker { override def execute(): Exec[Unit] = { - val res = server.rename(marker.toRenameParams(newName)).get() - assert(res.getDocumentChanges == null, res) - val editItems = res.getChanges.values().asScala.flatMap(_.asScala) // TDO use a Map + val results = server.rename(marker.toRenameParams(newName)).get() + assert(results.getDocumentChanges == null, results) + val editItems = results.getChanges.values().asScala.flatMap(_.asScala) // TODO use a Map assert(expected.forall { exp => editItems.exists { editItem => editItem.getNewText == newName && editItem.getRange == exp.toRange } - }, res) + }, results) } override def show: PositionContext.PosCtx[String] = diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala index 427860188d79..0b77f54f880c 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala @@ -5,13 +5,20 @@ import org.eclipse.lsp4j._ import scala.collection.JavaConverters._ +/** + * An action requesting for all the symbols in the workspace matching `query`. + * This action corresponds to the `workspace/symbol` method of the Language Server Protocol. + * + * @param query The string to query for. + * @param expected The expected results. + */ class CodeSymbol(query: String, symbols: Seq[SymInfo]) extends Action { override def execute(): Exec[Unit] = { - val res = server.symbol(new WorkspaceSymbolParams(query)).get() - assert(res.size() == symbols.size, res) - for ((symInfo, expected) <- res.asScala.zip(symbols)) { - assert(symInfo == expected.toSymInformation, res) + val results = server.symbol(new WorkspaceSymbolParams(query)).get() + assert(results.size() == symbols.size, results) + for ((symInfo, expected) <- results.asScala.zip(symbols)) { + assert(symInfo == expected.toSymInformation, results) } } diff --git a/language-server/test/dotty/tools/languageserver/util/embedded/CodeInRange.scala b/language-server/test/dotty/tools/languageserver/util/embedded/CodeInRange.scala index 89f87b81fba6..ac8724346316 100644 --- a/language-server/test/dotty/tools/languageserver/util/embedded/CodeInRange.scala +++ b/language-server/test/dotty/tools/languageserver/util/embedded/CodeInRange.scala @@ -2,4 +2,10 @@ package dotty.tools.languageserver.util.embedded import dotty.tools.languageserver.util.CodeRange +/** + * Wrapper for some text that appears within a range. + * + * @param text The text that is comprised inside `range`. + * @param range The range of positions that encloses the `text`. + */ case class CodeInRange(text: String, range: CodeRange) extends Embedded diff --git a/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala b/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala index ac02e9305e9c..bb98ced727d3 100644 --- a/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala +++ b/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala @@ -10,14 +10,19 @@ import PositionContext._ /** Used to mark positions in the code */ class CodeMarker(val name: String) extends Embedded { + /** A range of positions between this marker and `other`. */ def to(other: CodeMarker): CodeRange = CodeRange(this, other) + /** The file containing this marker. */ def file: PosCtx[TestFile] = implicitly[PositionContext].positionOf(this)._1 + /** The line containing this marker. */ def line: PosCtx[Int] = implicitly[PositionContext].positionOf(this)._2 + /** The columng number of this marker. */ def character: PosCtx[Int] = implicitly[PositionContext].positionOf(this)._3 + /** Converts this marker to a position. */ def toPosition: PosCtx[Position] = new Position(line, character) def toTextDocumentPositionParams: PosCtx[TextDocumentPositionParams] = From a31baf05ccd3a5b9bbe78287b088a1ff21055a42 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 28 Mar 2018 21:02:33 +0200 Subject: [PATCH 09/19] Use `CompletionItemKind` in `CodeCompletion` --- .../test/dotty/tools/languageserver/CompletionTest.scala | 3 ++- .../test/dotty/tools/languageserver/util/CodeTester.scala | 4 ++-- .../tools/languageserver/util/actions/CodeCompletion.scala | 6 ++++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala index 96aeaea9d33e..9c3e61f105f0 100644 --- a/language-server/test/dotty/tools/languageserver/CompletionTest.scala +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -1,6 +1,7 @@ package dotty.tools.languageserver import org.junit.Test +import org.eclipse.lsp4j.CompletionItemKind import dotty.tools.languageserver.util.Code._ @@ -8,6 +9,6 @@ class CompletionTest { @Test def completion0: Unit = { code"class Foo { val xyz: Int = 0; def y: Int = xy$m1 }".withSource - .completion(m1, List(("xyz", "Field", "Int"))) + .completion(m1, List(("xyz", CompletionItemKind.Field, "Int"))) } } diff --git a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala index ad063295304d..8e5606503ae3 100644 --- a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala +++ b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala @@ -4,7 +4,7 @@ import dotty.tools.languageserver.util.Code.SourceWithPositions import dotty.tools.languageserver.util.actions._ import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.server.{TestFile, TestServer} -import org.eclipse.lsp4j.SymbolInformation +import org.eclipse.lsp4j.CompletionItemKind /** * Simulates an LSP client for test in a workspace defined by `sources`. @@ -81,7 +81,7 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * * @see dotty.tools.languageserver.util.actions.CodeCompletion */ - def completion(marker: CodeMarker, expected: List[(String, String, String)]): CodeTester = + def completion(marker: CodeMarker, expected: List[(String, CompletionItemKind, String)]): CodeTester = doAction(new CodeCompletion(marker, expected)) /** diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala index f35907f512da..73dcd64ccdaa 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala @@ -4,6 +4,8 @@ import dotty.tools.languageserver.util.PositionContext import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.server.TestFile +import org.eclipse.lsp4j.CompletionItemKind + import scala.collection.JavaConverters._ /** @@ -14,7 +16,7 @@ import scala.collection.JavaConverters._ * @param completions The expected results from the language server. */ class CodeCompletion(override val marker: CodeMarker, - completions: List[(String, String, String)]) extends ActionOnMarker { + completions: List[(String, CompletionItemKind, String)]) extends ActionOnMarker { override def execute(): Exec[Unit] = { val result = server.completion(marker.toTextDocumentPositionParams).get() @@ -24,7 +26,7 @@ class CodeCompletion(override val marker: CodeMarker, completions.foreach { completion => assert( cList.getItems.asScala.exists(item => - completion == (item.getLabel, item.getKind.toString, item.getDetail) + completion == (item.getLabel, item.getKind, item.getDetail) ), "Did not return completion for " + completion + "\n" + cList.getItems.asScala.toList ) From c1d6a8acacb52657d8e61ccaac51f6ff20c7fbd7 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 28 Mar 2018 21:07:07 +0200 Subject: [PATCH 10/19] Use `JavaConverters` in `CodeDefinition` --- .../tools/languageserver/util/actions/CodeDefinition.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala index fa2ec65d93dc..41bc61fdc3b5 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala @@ -3,6 +3,8 @@ package dotty.tools.languageserver.util.actions import dotty.tools.languageserver.util._ import dotty.tools.languageserver.util.embedded.CodeMarker +import scala.collection.JavaConverters._ + /** * An action requesting for the definition of the symbol inside `range`. * This action corresponds to the `textDocument/definition` method of the Language Server Protocol. @@ -15,8 +17,9 @@ class CodeDefinition(override val range: CodeRange, expected: Seq[CodeRange]) ex override def onMarker(marker: CodeMarker): Exec[Unit] = { val results = server.definition(fix(marker.toTextDocumentPositionParams)).get() assert(results.size == expected.size, s"Expected ${expected.size} matches, found ${results.size}") - (0 until results.size).foreach { i => - assert(results.get(i) == expected(i).toLocation, s"Expected ${expected(i).toLocation}, found ${results.get(i)}.") + results.asScala.zip(expected).foreach { + case (result, expected) => + assert(result == expected.toLocation, s"Expected ${expected.toLocation}, found $result.") } } From 8cb7a9211b0a5a48182543627ffaf3ac49c45dba Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Thu, 29 Mar 2018 09:06:52 +0200 Subject: [PATCH 11/19] Address review comments --- .../tools/languageserver/CompletionTest.scala | 2 +- .../tools/languageserver/util/Code.scala | 6 ++--- .../tools/languageserver/util/CodeRange.scala | 7 +----- .../languageserver/util/CodeTester.scala | 2 +- .../languageserver/util/PositionContext.scala | 4 ++-- .../util/actions/CodeCompletion.scala | 22 +++++++------------ .../util/embedded/CodeMarker.scala | 8 ++++--- 7 files changed, 21 insertions(+), 30 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala index 9c3e61f105f0..2e33f77d040c 100644 --- a/language-server/test/dotty/tools/languageserver/CompletionTest.scala +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -9,6 +9,6 @@ class CompletionTest { @Test def completion0: Unit = { code"class Foo { val xyz: Int = 0; def y: Int = xy$m1 }".withSource - .completion(m1, List(("xyz", CompletionItemKind.Field, "Int"))) + .completion(m1, Set(("xyz", CompletionItemKind.Field, "Int"))) } } diff --git a/language-server/test/dotty/tools/languageserver/util/Code.scala b/language-server/test/dotty/tools/languageserver/util/Code.scala index a0ec56d290ee..dc43e43c57d8 100644 --- a/language-server/test/dotty/tools/languageserver/util/Code.scala +++ b/language-server/test/dotty/tools/languageserver/util/Code.scala @@ -56,13 +56,13 @@ object Code { ai.next() match { case emb: CodeMarker => - positions += Tuple3(emb, line, char) + positions += ((emb, line, char)) case emb: CodeInRange => - positions += Tuple3(emb.range.start, line, char) + positions += ((emb.range.start, line, char)) scan(emb.text) stringBuilder.append(emb.text) - positions += Tuple3(emb.range.end, line, char) + positions += ((emb.range.end, line, char)) } } diff --git a/language-server/test/dotty/tools/languageserver/util/CodeRange.scala b/language-server/test/dotty/tools/languageserver/util/CodeRange.scala index bc4a585e2782..60a965955f21 100644 --- a/language-server/test/dotty/tools/languageserver/util/CodeRange.scala +++ b/language-server/test/dotty/tools/languageserver/util/CodeRange.scala @@ -14,7 +14,7 @@ import PositionContext._ * @param end The end marker. */ case class CodeRange(start: CodeMarker, end: CodeMarker) { - private var checked = false + private[this] var checked = false def check(): PosCtx[Unit] = { if (!checked) { assert(start.file == end.file, s"$start and $end where not in the same file") @@ -29,11 +29,6 @@ case class CodeRange(start: CodeMarker, end: CodeMarker) { start.file } - def toTuple: PosCtx[(Int, Int, Int, Int)] = { - check() - (start.line, start.character, end.line, end.character) - } - def withCode(text: String): CodeInRange = CodeInRange(text, this) def symInfo(name: String, kind: SymbolKind, container: String = null): SymInfo = diff --git a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala index 8e5606503ae3..29354c08c177 100644 --- a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala +++ b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala @@ -81,7 +81,7 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * * @see dotty.tools.languageserver.util.actions.CodeCompletion */ - def completion(marker: CodeMarker, expected: List[(String, CompletionItemKind, String)]): CodeTester = + def completion(marker: CodeMarker, expected: Set[(String, CompletionItemKind, String)]): CodeTester = doAction(new CodeCompletion(marker, expected)) /** diff --git a/language-server/test/dotty/tools/languageserver/util/PositionContext.scala b/language-server/test/dotty/tools/languageserver/util/PositionContext.scala index 59f8adc5d207..45b97c886078 100644 --- a/language-server/test/dotty/tools/languageserver/util/PositionContext.scala +++ b/language-server/test/dotty/tools/languageserver/util/PositionContext.scala @@ -4,8 +4,8 @@ import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.server.TestFile class PositionContext(positionMap: Map[CodeMarker, (TestFile, Int, Int)]) { - private var lastKey: CodeMarker = _ - private var lastValue: (TestFile, Int, Int) = _ + private[this] var lastKey: CodeMarker = _ + private[this] var lastValue: (TestFile, Int, Int) = _ def positionOf(pos: CodeMarker): (TestFile, Int, Int) = { if (lastKey eq pos) lastValue else { diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala index 73dcd64ccdaa..f619afb24a06 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala @@ -9,30 +9,24 @@ import org.eclipse.lsp4j.CompletionItemKind import scala.collection.JavaConverters._ /** - * An action requesting for code completion at `marker`, expecting `completions`. + * An action requesting for code completion at `marker`, expecting `expected`. * This action corresponds to the `textDocument/completion` method of the Language Server Protocol. * - * @param marker The marker indicating the position where completion should be requested. - * @param completions The expected results from the language server. + * @param marker The marker indicating the position where completion should be requested. + * @param expected The expected results from the language server. */ class CodeCompletion(override val marker: CodeMarker, - completions: List[(String, CompletionItemKind, String)]) extends ActionOnMarker { + expected: Set[(String, CompletionItemKind, String)]) extends ActionOnMarker { override def execute(): Exec[Unit] = { val result = server.completion(marker.toTextDocumentPositionParams).get() assert(result.isRight, result) - val cList = result.getRight - assert(!cList.isIncomplete, result) - completions.foreach { completion => - assert( - cList.getItems.asScala.exists(item => - completion == (item.getLabel, item.getKind, item.getDetail) - ), - "Did not return completion for " + completion + "\n" + cList.getItems.asScala.toList - ) + assert(!result.getRight.isIncomplete, s"Completion results were 'incomplete': $result") + val completionResults = result.getRight.getItems.asScala.toSet.map { item => + (item.getLabel, item.getKind, item.getDetail) } } override def show: PositionContext.PosCtx[String] = - s"CodeCompletion(${marker.show}, $completions)" + s"CodeCompletion(${marker.show}, $expected)" } diff --git a/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala b/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala index bb98ced727d3..a75d835941cd 100644 --- a/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala +++ b/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala @@ -14,13 +14,13 @@ class CodeMarker(val name: String) extends Embedded { def to(other: CodeMarker): CodeRange = CodeRange(this, other) /** The file containing this marker. */ - def file: PosCtx[TestFile] = implicitly[PositionContext].positionOf(this)._1 + def file: PosCtx[TestFile] = posCtx.positionOf(this)._1 /** The line containing this marker. */ - def line: PosCtx[Int] = implicitly[PositionContext].positionOf(this)._2 + def line: PosCtx[Int] = posCtx.positionOf(this)._2 /** The columng number of this marker. */ - def character: PosCtx[Int] = implicitly[PositionContext].positionOf(this)._3 + def character: PosCtx[Int] = posCtx.positionOf(this)._3 /** Converts this marker to a position. */ def toPosition: PosCtx[Position] = new Position(line, character) @@ -46,4 +46,6 @@ class CodeMarker(val name: String) extends Embedded { def show: PosCtx[String] = s"($name,line=$line,char=$character)" override def toString: String = s"CodePosition($name)" + + private implicit def posCtx(implicit ctx: PositionContext): PositionContext = ctx } From b0f9c9a72cca823ef3d211bb919d6283fed26423 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Tue, 3 Apr 2018 15:12:54 +0200 Subject: [PATCH 12/19] Use `DocumentHighlightKind` instead of strings --- .../test/dotty/tools/languageserver/HighlightTest.scala | 6 +++--- .../test/dotty/tools/languageserver/util/CodeTester.scala | 4 ++-- .../languageserver/util/actions/CodeDocumentHighlight.scala | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/HighlightTest.scala b/language-server/test/dotty/tools/languageserver/HighlightTest.scala index 49ac1ecbf691..a863197fdf2e 100644 --- a/language-server/test/dotty/tools/languageserver/HighlightTest.scala +++ b/language-server/test/dotty/tools/languageserver/HighlightTest.scala @@ -1,22 +1,22 @@ package dotty.tools.languageserver import org.junit.Test - import dotty.tools.languageserver.util.Code._ +import org.eclipse.lsp4j.DocumentHighlightKind class HighlightTest { @Test def valHighlight0: Unit = { val xDef = (m1 to m2).withCode("x") code"class X { val $xDef = 9 }".withSource - .highlight(xDef.range, (xDef.range, "Read")) + .highlight(xDef.range, (xDef.range, DocumentHighlightKind.Read)) } @Test def valHighlight1: Unit = { val xDef = (m1 to m2).withCode("x") val xRef = (m3 to m4).withCode("x") code"class X { val $xDef = 9; $xRef}".withSource - .highlight(xRef.range, (xDef.range, "Read"), (xRef.range, "Read")) + .highlight(xRef.range, (xDef.range, DocumentHighlightKind.Read), (xRef.range, DocumentHighlightKind.Read)) } } diff --git a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala index 29354c08c177..7f31c2c07b2c 100644 --- a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala +++ b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala @@ -4,7 +4,7 @@ import dotty.tools.languageserver.util.Code.SourceWithPositions import dotty.tools.languageserver.util.actions._ import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.server.{TestFile, TestServer} -import org.eclipse.lsp4j.CompletionItemKind +import org.eclipse.lsp4j.{CompletionItemKind, DocumentHighlightKind} /** * Simulates an LSP client for test in a workspace defined by `sources`. @@ -55,7 +55,7 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * * @see dotty.tools.languageserver.util.actions.CodeDefinition */ - def highlight(range: CodeRange, expected: (CodeRange, String)*): CodeTester = + def highlight(range: CodeRange, expected: (CodeRange, DocumentHighlightKind)*): CodeTester = doAction(new CodeDocumentHighlight(range, expected)) /** diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala index 436922e3b4a9..b95629d9e156 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala @@ -16,7 +16,7 @@ import scala.collection.JavaConverters._ * @param expected The expected results. */ class CodeDocumentHighlight(override val range: CodeRange, - expected: Seq[(CodeRange, String)]) extends ActionOnRange { + expected: Seq[(CodeRange, DocumentHighlightKind)]) extends ActionOnRange { override def onMarker(marker: CodeMarker): Exec[Unit] = { val (references, kinds) = expected.unzip @@ -24,7 +24,7 @@ class CodeDocumentHighlight(override val range: CodeRange, assert(results.size() == references.size, results) assert(references.size == kinds.length, results) results.asScala.zip(references).zip(kinds).foreach { case ((dhl, ref), kind) => - assert(dhl.getKind == DocumentHighlightKind.valueOf(kind), results) + assert(dhl.getKind == kind, results) assert(dhl.getRange == ref.toRange, results) } } From 2832527d82765cb6c2a6bc1ff3849d9a92f1a8da Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 4 Apr 2018 16:22:04 +0200 Subject: [PATCH 13/19] Refactor `TestServer` --- compiler/src/dotty/tools/dotc/util/DiffUtil.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/util/DiffUtil.scala b/compiler/src/dotty/tools/dotc/util/DiffUtil.scala index 3028562ec643..ea49ef2391f1 100644 --- a/compiler/src/dotty/tools/dotc/util/DiffUtil.scala +++ b/compiler/src/dotty/tools/dotc/util/DiffUtil.scala @@ -69,7 +69,7 @@ object DiffUtil { * differences are highlighted. */ def mkColoredLineDiff(expected: Seq[String], actual: Seq[String]): String = { - val expectedSize = EOF.length max expected.map(_.length).max + val expectedSize = EOF.length max expected.maxBy(_.length).length actual.padTo(expected.length, "").zip(expected.padTo(actual.length, "")).map { case (act, exp) => mkColoredLineDiff(exp, act, expectedSize) }.mkString(System.lineSeparator) From bfe5387236bd3dcd03b9e746a0ab0d580d38de2e Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 4 Apr 2018 16:33:15 +0200 Subject: [PATCH 14/19] Add documentation for `Embedded` --- .../test/dotty/tools/languageserver/util/embedded/Embedded.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/language-server/test/dotty/tools/languageserver/util/embedded/Embedded.scala b/language-server/test/dotty/tools/languageserver/util/embedded/Embedded.scala index 4058d0923713..ce96f6bf5d1e 100644 --- a/language-server/test/dotty/tools/languageserver/util/embedded/Embedded.scala +++ b/language-server/test/dotty/tools/languageserver/util/embedded/Embedded.scala @@ -1,3 +1,4 @@ package dotty.tools.languageserver.util.embedded +/** Base trait for objects that can be used inside the `code""` interpolator. */ trait Embedded From a9b762bdb278ad2081abe29a5d7955c166e38333 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 4 Apr 2018 17:48:00 +0200 Subject: [PATCH 15/19] Fix renaming of synthetic positions in IDE While working on the IDE tests, I noticed that the Dotty IDE didn't behave as I expected in the following scenario: // Rename `Foo` to `Bar` class Foo { new Foo } The Dotty IDE would consider that 3 places need changing, instead of only 2: - The `Ident(Foo)` in the `TypeDef` - The `Ident(Foo)` in the constructor - The `Ident(Foo)` in `Select(new Foo, )` The third tree, however is synthetic: it doesn't need to be changed in the editor. Trees whose positions are not derived from the source are now excluded when doing a rename. --- .../src/dotty/tools/languageserver/DottyLanguageServer.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala index fe8954760ddc..3341566f600f 100644 --- a/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala +++ b/language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala @@ -303,8 +303,9 @@ class DottyLanguageServer extends LanguageServer val newName = params.getNewName val refs = Interactive.namedTrees(trees, includeReferences = true, tree => - (Interactive.matchSymbol(tree, sym, Include.overriding) - || (linkedSym != NoSymbol && Interactive.matchSymbol(tree, linkedSym, Include.overriding)))) + tree.pos.isSourceDerived + && (Interactive.matchSymbol(tree, sym, Include.overriding) + || (linkedSym != NoSymbol && Interactive.matchSymbol(tree, linkedSym, Include.overriding)))) val changes = refs.groupBy(ref => toUri(ref.source).toString).mapValues(_.map(ref => new TextEdit(range(ref.namePos), newName)).asJava) From 0aef2d2f57c608b10098a8a3733a57b4767b036c Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 4 Apr 2018 17:59:43 +0200 Subject: [PATCH 16/19] Address review comments --- .../tools/languageserver/RenameTest.scala | 4 +- .../languageserver/util/CodeTester.scala | 4 +- .../languageserver/util/actions/Action.scala | 4 -- .../util/actions/CodeCompletion.scala | 2 + .../util/actions/CodeDefinition.scala | 12 ++-- .../util/actions/CodeDocumentHighlight.scala | 14 ++--- .../util/actions/CodeDocumentSymbol.scala | 10 ++-- .../util/actions/CodeHover.scala | 13 +++-- .../util/actions/CodeReferences.scala | 9 ++- .../util/actions/CodeRename.scala | 17 +++--- .../util/actions/CodeSymbol.scala | 18 +++--- .../util/server/TestServer.scala | 55 +++++++++++-------- 12 files changed, 84 insertions(+), 78 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/RenameTest.scala b/language-server/test/dotty/tools/languageserver/RenameTest.scala index b2392560658e..a349e417bdca 100644 --- a/language-server/test/dotty/tools/languageserver/RenameTest.scala +++ b/language-server/test/dotty/tools/languageserver/RenameTest.scala @@ -9,7 +9,7 @@ class RenameTest { @Test def rename0: Unit = { def testRenameFrom(m: CodeMarker) = - code"class ${m1}Foo$m2 { new ${m3}Foo$m4 }".withSource.rename(m, "Bar", List(m1 to m2, m3 to m4)) + code"class ${m1}Foo$m2 { new ${m3}Foo$m4 }".withSource.rename(m, "Bar", Set(m1 to m2, m3 to m4)) testRenameFrom(m1) testRenameFrom(m3) } @@ -20,7 +20,7 @@ class RenameTest { withSources( code"class ${m1}Foo$m2 { new ${m3}Foo$m4 }", code"class Bar { new ${m5}Foo$m6 }" - ).rename(m, "Bar", List(m1 to m2, m3 to m4, m5 to m6)) + ).rename(m, "Bar", Set(m1 to m2, m3 to m4, m5 to m6)) testRenameFrom(m1) testRenameFrom(m3) diff --git a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala index 7f31c2c07b2c..c63c6b4d7aad 100644 --- a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala +++ b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala @@ -90,12 +90,12 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * * @param marker The position from which to ask for renaming. * @param newName The new name to give to the symbol. - * @param expected The expected list of positions to change. + * @param expected The expected positions to change. * @return This `CodeTester` after performing the action. * * @see dotty.tools.languageserver.util.actions.CodeRename */ - def rename(marker: CodeMarker, newName: String, expected: List[CodeRange]): CodeTester = + def rename(marker: CodeMarker, newName: String, expected: Set[CodeRange]): CodeTester = doAction(new CodeRename(marker, newName, expected)) // TODO apply changes to the sources and positions /** diff --git a/language-server/test/dotty/tools/languageserver/util/actions/Action.scala b/language-server/test/dotty/tools/languageserver/util/actions/Action.scala index 272a4603530d..06d0bed35a53 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/Action.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/Action.scala @@ -22,8 +22,4 @@ trait Action { /** The server that this action targets. */ def server: Exec[DottyLanguageServer] = implicitly[TestServer].server - // FIXME - // Workaroud an issue with implicit functions and phantomArgLift. - // That phase will dispear which should fix this issue. Just remove calls to param when that hapens - protected def fix[T](f: PositionContext => T): PosCtx[T] = f(implicitly[PositionContext]) } diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala index f619afb24a06..9d4c51957f84 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala @@ -5,6 +5,7 @@ import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.server.TestFile import org.eclipse.lsp4j.CompletionItemKind +import org.junit.Assert.assertEquals import scala.collection.JavaConverters._ @@ -25,6 +26,7 @@ class CodeCompletion(override val marker: CodeMarker, val completionResults = result.getRight.getItems.asScala.toSet.map { item => (item.getLabel, item.getKind, item.getDetail) } + assertEquals(expected, completionResults) } override def show: PositionContext.PosCtx[String] = diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala index 41bc61fdc3b5..a22da6ffc5ed 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala @@ -5,6 +5,8 @@ import dotty.tools.languageserver.util.embedded.CodeMarker import scala.collection.JavaConverters._ +import org.junit.Assert.assertEquals + /** * An action requesting for the definition of the symbol inside `range`. * This action corresponds to the `textDocument/definition` method of the Language Server Protocol. @@ -15,12 +17,10 @@ import scala.collection.JavaConverters._ class CodeDefinition(override val range: CodeRange, expected: Seq[CodeRange]) extends ActionOnRange { override def onMarker(marker: CodeMarker): Exec[Unit] = { - val results = server.definition(fix(marker.toTextDocumentPositionParams)).get() - assert(results.size == expected.size, s"Expected ${expected.size} matches, found ${results.size}") - results.asScala.zip(expected).foreach { - case (result, expected) => - assert(result == expected.toLocation, s"Expected ${expected.toLocation}, found $result.") - } + val results = server.definition(marker.toTextDocumentPositionParams).get().asScala.toSeq + val expectedLocations = expected.map(_.toLocation) + + assertEquals(expectedLocations, results) } override def show: PositionContext.PosCtx[String] = diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala index b95629d9e156..4b5df6c8621b 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala @@ -3,6 +3,7 @@ package dotty.tools.languageserver.util.actions import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.{CodeRange, PositionContext} import org.eclipse.lsp4j._ +import org.junit.Assert.assertEquals import scala.collection.JavaConverters._ @@ -19,14 +20,11 @@ class CodeDocumentHighlight(override val range: CodeRange, expected: Seq[(CodeRange, DocumentHighlightKind)]) extends ActionOnRange { override def onMarker(marker: CodeMarker): Exec[Unit] = { - val (references, kinds) = expected.unzip - val results = server.documentHighlight(fix(marker.toTextDocumentPositionParams)).get() - assert(results.size() == references.size, results) - assert(references.size == kinds.length, results) - results.asScala.zip(references).zip(kinds).foreach { case ((dhl, ref), kind) => - assert(dhl.getKind == kind, results) - assert(dhl.getRange == ref.toRange, results) - } + val expectedPairs = expected.map { case (codeRange, kind) => (codeRange.toRange, kind) } + val results = server.documentHighlight(marker.toTextDocumentPositionParams).get() + val resultPairs = results.asScala.map { result => (result.getRange, result.getKind) } + + assertEquals(expectedPairs, resultPairs) } override def show: PositionContext.PosCtx[String] = { diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala index 8d0499a8dd69..32d6bf5540f7 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentSymbol.scala @@ -2,6 +2,7 @@ package dotty.tools.languageserver.util.actions import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.{PositionContext, SymInfo} +import org.junit.Assert.assertEquals import scala.collection.JavaConverters._ @@ -16,11 +17,10 @@ import scala.collection.JavaConverters._ class CodeDocumentSymbol(override val marker: CodeMarker, expected: Seq[SymInfo]) extends ActionOnMarker { override def execute(): Exec[Unit] = { - val results = server.documentSymbol(marker.toDocumentSymbolParams).get() - assert(results.size == expected.size, results) - for ((symInfo, expected) <- results.asScala.zip(expected)) { - assert(symInfo == expected.toSymInformation, results) - } + val results = server.documentSymbol(marker.toDocumentSymbolParams).get().asScala + val expectedSymInfos = expected.map(_.toSymInformation) + + assertEquals(expectedSymInfos, results) } override def show: PositionContext.PosCtx[String] = diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala index 61c03ebee241..92028fb01d44 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala @@ -4,6 +4,7 @@ import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.{CodeRange, PositionContext} import org.eclipse.lsp4j._ +import org.junit.Assert.{assertEquals, assertNull, assertTrue} import PositionContext._ @@ -17,14 +18,14 @@ import PositionContext._ class CodeHover(override val range: CodeRange, expected: String) extends ActionOnRange { override def onMarker(marker: CodeMarker): Exec[Unit] = { - val result = server.hover(fix(marker.toTextDocumentPositionParams)).get() - assert(result.getRange == null) - if (expected == "") assert(result.getContents == null, "Expected null contents in " + result) + val result = server.hover(marker.toTextDocumentPositionParams).get() + assertNull(result.getRange) + if (expected.isEmpty) assertNull(result.getContents) else { - assert(result.getContents.size() == 1, result) + assertEquals(1, result.getContents.size) val content = result.getContents.get(0) - assert(content.isLeft, "Expected left but was " + content) - assert(content.getLeft == expected, s"Expected $expected but was ${content.getLeft}") + assertTrue(content.isLeft) + assertEquals(expected, content.getLeft) } } diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala index d3b0bc9b2adb..758a2b0e43fc 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeReferences.scala @@ -3,6 +3,8 @@ package dotty.tools.languageserver.util.actions import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.{CodeRange, PositionContext} +import org.junit.Assert.assertEquals + import scala.collection.JavaConverters._ /** @@ -18,9 +20,10 @@ class CodeReferences(override val range: CodeRange, withDecl: Boolean) extends ActionOnRange { override def onMarker(marker: CodeMarker): Exec[Unit] = { - val results = server.references(fix(marker.toReferenceParams(withDecl))).get() - assert(results.size() == expected.size) - results.asScala.zip(expected).foreach { case (loc, ref) => assert(loc == ref.toLocation, results) } + val expectedLocations = expected.map(_.toLocation) + val results = server.references(marker.toReferenceParams(withDecl)).get().asScala + + assertEquals(expectedLocations, results) } override def show: PositionContext.PosCtx[String] = diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala index 6cae3e18d8ef..5e69d00b4bbb 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeRename.scala @@ -3,6 +3,8 @@ package dotty.tools.languageserver.util.actions import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.{CodeRange, PositionContext} +import org.junit.Assert.{assertEquals, assertNull} + import scala.collection.JavaConverters._ /** @@ -15,18 +17,15 @@ import scala.collection.JavaConverters._ */ class CodeRename(override val marker: CodeMarker, newName: String, - expected: List[CodeRange]) extends ActionOnMarker { + expected: Set[CodeRange]) extends ActionOnMarker { override def execute(): Exec[Unit] = { val results = server.rename(marker.toRenameParams(newName)).get() - assert(results.getDocumentChanges == null, results) - val editItems = results.getChanges.values().asScala.flatMap(_.asScala) // TODO use a Map - assert(expected.forall { exp => - editItems.exists { editItem => - editItem.getNewText == newName && - editItem.getRange == exp.toRange - } - }, results) + val changes = results.getChanges.asScala.mapValues(_.asScala.toSet.map(ch => (ch.getNewText, ch.getRange))) + val expectedChanges = expected.groupBy(_.file.uri).mapValues(_.map(range => (newName, range.toRange))) + + assertNull(results.getDocumentChanges) + assertEquals(expectedChanges, changes) } override def show: PositionContext.PosCtx[String] = diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala index 0b77f54f880c..3fd5d59b15b4 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeSymbol.scala @@ -1,7 +1,8 @@ package dotty.tools.languageserver.util.actions -import dotty.tools.languageserver.util.{CodeRange, PositionContext, SymInfo} -import org.eclipse.lsp4j._ +import dotty.tools.languageserver.util.{PositionContext, SymInfo} +import org.eclipse.lsp4j.WorkspaceSymbolParams +import org.junit.Assert.assertEquals import scala.collection.JavaConverters._ @@ -12,16 +13,15 @@ import scala.collection.JavaConverters._ * @param query The string to query for. * @param expected The expected results. */ -class CodeSymbol(query: String, symbols: Seq[SymInfo]) extends Action { +class CodeSymbol(query: String, expected: Seq[SymInfo]) extends Action { override def execute(): Exec[Unit] = { - val results = server.symbol(new WorkspaceSymbolParams(query)).get() - assert(results.size() == symbols.size, results) - for ((symInfo, expected) <- results.asScala.zip(symbols)) { - assert(symInfo == expected.toSymInformation, results) - } + val results = server.symbol(new WorkspaceSymbolParams(query)).get().asScala + val expectedSymInfo = expected.map(_.toSymInformation) + + assertEquals(expectedSymInfo, results) } override def show: PositionContext.PosCtx[String] = - s"CodeDocumentSymbol($query, ${symbols.map(_.show)})" + s"CodeDocumentSymbol($query, ${expected.map(_.show)})" } diff --git a/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala b/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala index ef63e3be607d..e0fbdb1bb0f7 100644 --- a/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala +++ b/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala @@ -12,32 +12,39 @@ import scala.collection.JavaConverters._ class TestServer(testFolder: Path) { - // Fill the configuration with values populated by sbt - private def showSeq[T](lst: Seq[T]): String = lst.map(elem => '"' + elem.toString + '"').mkString("[ ", ", ", " ]") - private val dottyIdeJson: String = - s"""[ { - | "id" : "dotty-ide-test", - | "compilerVersion" : "${BuildInfo.ideTestsCompilerVersion}", - | "compilerArguments" : ${showSeq(BuildInfo.ideTestsCompilerArguments)}, - | "sourceDirectories" : ${showSeq(BuildInfo.ideTestsSourceDirectories)}, - | "dependencyClasspath" : ${showSeq(BuildInfo.ideTestsDependencyClasspath)}, - | "classDirectory" : "${BuildInfo.ideTestsClassDirectory}" - |} - |] - """.stripMargin - private val configFile = testFolder.resolve(DottyLanguageServer.IDE_CONFIG_FILE) - testFolder.toFile.mkdirs() - testFolder.resolve("src").toFile.mkdirs() - testFolder.resolve("out").toFile.mkdirs() - new PrintWriter(configFile.toString) { write(dottyIdeJson); close() } - val server = new DottyLanguageServer - private val client = new TestClient - server.connect(client) + init() + + private[this] def init(): InitializeResult = { + // Fill the configuration with values populated by sbt + def showSeq[T](lst: Seq[T]): String = lst.map(elem => '"' + elem.toString + '"').mkString("[ ", ", ", " ]") + val dottyIdeJson: String = + s"""[ { + | "id" : "dotty-ide-test", + | "compilerVersion" : "${BuildInfo.ideTestsCompilerVersion}", + | "compilerArguments" : ${showSeq(BuildInfo.ideTestsCompilerArguments)}, + | "sourceDirectories" : ${showSeq(BuildInfo.ideTestsSourceDirectories)}, + | "dependencyClasspath" : ${showSeq(BuildInfo.ideTestsDependencyClasspath)}, + | "classDirectory" : "${BuildInfo.ideTestsClassDirectory}" + |} + |]""".stripMargin + val configFile = testFolder.resolve(DottyLanguageServer.IDE_CONFIG_FILE) + testFolder.toFile.mkdirs() + testFolder.resolve("src").toFile.mkdirs() + testFolder.resolve("out").toFile.mkdirs() + + new PrintWriter(configFile.toString) { + write(dottyIdeJson) + close() + } - private val initParams = new InitializeParams() - initParams.setRootUri(testFolder.toAbsolutePath.toUri.toString) - server.initialize(initParams).get() + val client = new TestClient + server.connect(client) + + val initParams = new InitializeParams() + initParams.setRootUri(testFolder.toAbsolutePath.toUri.toString) + server.initialize(initParams).get() + } /** Open the code in the given file and returns the file. * @param code code in file From 2a511eadf889551834d33764ce8be00487a48dbf Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Tue, 10 Apr 2018 09:24:11 +0200 Subject: [PATCH 17/19] Revive commented-out test --- .../test/dotty/tools/languageserver/SymbolTest.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/SymbolTest.scala b/language-server/test/dotty/tools/languageserver/SymbolTest.scala index 218fb19f7a4a..5a7cb73af6df 100644 --- a/language-server/test/dotty/tools/languageserver/SymbolTest.scala +++ b/language-server/test/dotty/tools/languageserver/SymbolTest.scala @@ -7,8 +7,10 @@ import dotty.tools.languageserver.util.Code._ class SymbolTest { -// @Test def symbol0: Unit = -// code"class ${m1}Foo$m2".withSource.symbol("Foo", ("Foo", "Class", m1 to m2)) + @Test def symbol0: Unit = { + val Foo = (m1 to m2).withCode("Foo") + withSources(code"class $Foo").symbol("Foo", Foo.range.symInfo("Foo", SymbolKind.Class)) + } @Test def symbol1: Unit = { val Foo = (m1 to m2).withCode("Foo") From 73f1112516874bca5ca997392df750f6aa33bb5f Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Tue, 10 Apr 2018 09:54:46 +0200 Subject: [PATCH 18/19] Cleanup --- .../tools/languageserver/util/actions/CodeCompletion.scala | 6 +++--- .../tools/languageserver/util/actions/CodeDefinition.scala | 2 +- .../languageserver/util/actions/CodeDocumentHighlight.scala | 2 +- .../dotty/tools/languageserver/util/actions/CodeHover.scala | 3 --- .../tools/languageserver/util/embedded/CodeMarker.scala | 2 +- .../dotty/tools/languageserver/util/server/TestServer.scala | 4 +--- 6 files changed, 7 insertions(+), 12 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala index 9d4c51957f84..1dca68c19df9 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala @@ -5,7 +5,7 @@ import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.server.TestFile import org.eclipse.lsp4j.CompletionItemKind -import org.junit.Assert.assertEquals +import org.junit.Assert.{assertEquals, assertFalse, assertTrue} import scala.collection.JavaConverters._ @@ -21,8 +21,8 @@ class CodeCompletion(override val marker: CodeMarker, override def execute(): Exec[Unit] = { val result = server.completion(marker.toTextDocumentPositionParams).get() - assert(result.isRight, result) - assert(!result.getRight.isIncomplete, s"Completion results were 'incomplete': $result") + assertTrue(s"Completion results where not 'right': $result", result.isRight) + assertFalse(s"Completion results were 'incomplete': $result", result.getRight.isIncomplete) val completionResults = result.getRight.getItems.asScala.toSet.map { item => (item.getLabel, item.getKind, item.getDetail) } diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala index a22da6ffc5ed..0e1e41774ac2 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDefinition.scala @@ -1,6 +1,6 @@ package dotty.tools.languageserver.util.actions -import dotty.tools.languageserver.util._ +import dotty.tools.languageserver.util.{CodeRange, PositionContext} import dotty.tools.languageserver.util.embedded.CodeMarker import scala.collection.JavaConverters._ diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala index 4b5df6c8621b..eefbac6dd40f 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeDocumentHighlight.scala @@ -2,7 +2,7 @@ package dotty.tools.languageserver.util.actions import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.{CodeRange, PositionContext} -import org.eclipse.lsp4j._ +import org.eclipse.lsp4j.DocumentHighlightKind import org.junit.Assert.assertEquals import scala.collection.JavaConverters._ diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala index 92028fb01d44..62b1c8564b0f 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeHover.scala @@ -3,11 +3,8 @@ package dotty.tools.languageserver.util.actions import dotty.tools.languageserver.util.embedded.CodeMarker import dotty.tools.languageserver.util.{CodeRange, PositionContext} -import org.eclipse.lsp4j._ import org.junit.Assert.{assertEquals, assertNull, assertTrue} -import PositionContext._ - /** * An action requesting for the info shown when `range` is hovered. * This action corresponds to the `textDocument/hover` method of the Language Server Protocol. diff --git a/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala b/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala index a75d835941cd..5f7416927474 100644 --- a/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala +++ b/language-server/test/dotty/tools/languageserver/util/embedded/CodeMarker.scala @@ -5,7 +5,7 @@ import dotty.tools.languageserver.util.{CodeRange, PositionContext} import org.eclipse.lsp4j._ -import PositionContext._ +import PositionContext.PosCtx /** Used to mark positions in the code */ class CodeMarker(val name: String) extends Embedded { diff --git a/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala b/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala index e0fbdb1bb0f7..39c53ccfb0c9 100644 --- a/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala +++ b/language-server/test/dotty/tools/languageserver/util/server/TestServer.scala @@ -6,9 +6,7 @@ import java.nio.file.Path import java.util import dotty.tools.languageserver.DottyLanguageServer -import org.eclipse.lsp4j._ - -import scala.collection.JavaConverters._ +import org.eclipse.lsp4j.{ DidOpenTextDocumentParams, InitializeParams, InitializeResult, TextDocumentItem} class TestServer(testFolder: Path) { From ef978a7b041018810707b124c28fbc86481603cc Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 11 Apr 2018 15:53:23 +0200 Subject: [PATCH 19/19] Address review comments --- .../languageserver/util/CodeTester.scala | 24 +++++++------------ .../util/actions/CodeCompletion.scala | 2 +- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala index c63c6b4d7aad..5d79ff733a86 100644 --- a/language-server/test/dotty/tools/languageserver/util/CodeTester.scala +++ b/language-server/test/dotty/tools/languageserver/util/CodeTester.scala @@ -26,11 +26,10 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * * @param range The range over which to hover. * @param expected The expected result. - * @return This `CodeTester` after performing the action. * * @see dotty.tools.languageserver.util.actions.CodeHover */ - def hover(range: CodeRange, expected: String): CodeTester = + def hover(range: CodeRange, expected: String): this.type = doAction(new CodeHover(range, expected)) /** @@ -38,11 +37,10 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * * @param range The range of positions from which run `jump to definition`. * @param expected The expected positions to jump to. - * @return This `CodeTester` after performing the action. * * @see dotty.tools.languageserver.util.actions.CodeDefinition */ - def definition(range: CodeRange, expected: Seq[CodeRange]): CodeTester = + def definition(range: CodeRange, expected: Seq[CodeRange]): this.type = doAction(new CodeDefinition(range, expected)) /** @@ -51,11 +49,10 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * * @param range The range of positions to highlight. * @param expected The expected ranges and the kind of symbols that should be highlighted. - * @return This `CodeTester` after performing the action. * * @see dotty.tools.languageserver.util.actions.CodeDefinition */ - def highlight(range: CodeRange, expected: (CodeRange, DocumentHighlightKind)*): CodeTester = + def highlight(range: CodeRange, expected: (CodeRange, DocumentHighlightKind)*): this.type = doAction(new CodeDocumentHighlight(range, expected)) /** @@ -64,11 +61,10 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * @param range The range of positions from which search for references. * @param expected The expected positions of the references * @param withDecl When set, include the declaration of the symbol under `range` in the results. - * @return This `CodeTester` after performing the action. * * @see dotty.tools.languageserver.util.actions.CodeReferences */ - def references(range: CodeRange, expected: List[CodeRange], withDecl: Boolean = false): CodeTester = + def references(range: CodeRange, expected: List[CodeRange], withDecl: Boolean = false): this.type = doAction(new CodeReferences(range, expected, withDecl)) /** @@ -77,11 +73,10 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * * @param marker The position from which to ask for completions. * @param expected The expected completion results. - * @return This `CodeTester` after performing the action. * * @see dotty.tools.languageserver.util.actions.CodeCompletion */ - def completion(marker: CodeMarker, expected: Set[(String, CompletionItemKind, String)]): CodeTester = + def completion(marker: CodeMarker, expected: Set[(String, CompletionItemKind, String)]): this.type = doAction(new CodeCompletion(marker, expected)) /** @@ -91,11 +86,10 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * @param marker The position from which to ask for renaming. * @param newName The new name to give to the symbol. * @param expected The expected positions to change. - * @return This `CodeTester` after performing the action. * * @see dotty.tools.languageserver.util.actions.CodeRename */ - def rename(marker: CodeMarker, newName: String, expected: Set[CodeRange]): CodeTester = + def rename(marker: CodeMarker, newName: String, expected: Set[CodeRange]): this.type = doAction(new CodeRename(marker, newName, expected)) // TODO apply changes to the sources and positions /** @@ -104,11 +98,10 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * * @param marker The marker defining the source file from which to query. * @param expected The expected symbols to be found. - * @return This `CodeTester` after performing the action. * * @see dotty.tools.languageserver.util.actions.CodeDocumentSymbol */ - def documentSymbol(marker: CodeMarker, expected: SymInfo*): CodeTester = + def documentSymbol(marker: CodeMarker, expected: SymInfo*): this.type = doAction(new CodeDocumentSymbol(marker, expected)) /** @@ -117,11 +110,10 @@ class CodeTester(sources: List[SourceWithPositions], actions: List[Action]) { * * @param query The query used to find symbols. * @param expected The expected symbols to be found. - * @return This `CodeTester` after performing the action. * * @see dotty.tools.languageserver.util.actions.CodeSymbol */ - def symbol(query: String, symbols: SymInfo*): CodeTester = + def symbol(query: String, symbols: SymInfo*): this.type = doAction(new CodeSymbol(query, symbols)) private def doAction(action: Action): this.type = { diff --git a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala index 1dca68c19df9..1c2c96699a26 100644 --- a/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala +++ b/language-server/test/dotty/tools/languageserver/util/actions/CodeCompletion.scala @@ -21,7 +21,7 @@ class CodeCompletion(override val marker: CodeMarker, override def execute(): Exec[Unit] = { val result = server.completion(marker.toTextDocumentPositionParams).get() - assertTrue(s"Completion results where not 'right': $result", result.isRight) + assertTrue(s"Completion results were not 'right': $result", result.isRight) assertFalse(s"Completion results were 'incomplete': $result", result.getRight.isIncomplete) val completionResults = result.getRight.getItems.asScala.toSet.map { item => (item.getLabel, item.getKind, item.getDetail)