From 836bf499107e7b090e7c102baabc1b8ec84d8c21 Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Mon, 9 Dec 2019 13:49:16 +0900 Subject: [PATCH 01/12] Sort auto-completed exhaustive match'es by declaration order from stale https://github.com/scalameta/metals-feature-requests/issues/49 This commit enable metals to sort auto-completed exhaustive match'es by declaration order. However, it caluculates their declaration positions from the infromation retrieved from interactive compiler, and we cannot sort them if the interactive compiler hasn't yet loaded the symbols. --- .../scala/meta/internal/pc/Completions.scala | 5 ++++ .../scala/tests/pc/CompletionMatchSuite.scala | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala b/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala index ebfb1be43f9..cf74117a487 100644 --- a/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala +++ b/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala @@ -1297,7 +1297,12 @@ trait Completions { this: MetalsGlobal => val members = ListBuffer.empty[TextEditMember] val importPos = autoImportPosition(pos, text) val context = doLocateImportContext(pos, importPos) + val subclasses = ListBuffer.empty[Symbol] tpe.typeSymbol.foreachKnownDirectSubClass { sym => + subclasses += sym + } + val sortedSubclasses = subclasses.result().sortBy(sym => (sym.pos.line, sym.pos.column)) + sortedSubclasses.foreach { sym => val (shortName, edits) = importPos match { case Some(value) => diff --git a/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala index 6d19e27ca00..fead71c9cdd 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala @@ -103,6 +103,33 @@ object CompletionMatchSuite extends BaseCompletionSuite { filter = _.contains("exhaustive") ) + checkEdit( + "Sort auto-completed exhaustive match keywords by declaration order", + """package sort + |sealed abstract class TestTree + |case class Branch1(t1: TestTree) extends TestTree + |case class Leaf(v: Int) extends TestTree + |case class Branch2(t1: TestTree, t2: TestTree) extends TestTree + |object App { + | null.asInstanceOf[TestTree] matc@@ + |} + |""".stripMargin, + """|package sort + |sealed abstract class TestTree + |case class Branch1(t1: TestTree) extends TestTree + |case class Leaf(v: Int) extends TestTree + |case class Branch2(t1: TestTree, t2: TestTree) extends TestTree + |object App { + | null.asInstanceOf[TestTree] match { + |\tcase Branch1(t1) => $0 + |\tcase Leaf(v) => + |\tcase Branch2(t1, t2) => + |} + |} + |""".stripMargin, + filter = _.contains("exhaustive") + ) + check( "inner-class", """ From 7b22279ad05682c32fc7aa251b9f0ded6f3624c6 Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Mon, 9 Dec 2019 20:03:36 +0900 Subject: [PATCH 02/12] Sort auto-completed exhaustive matches without stale This commit enable metals to sort completed exhaustive match'es by declaration order without stale: read the whole symbols of TextDocument that contains the definition of the given symbol. --- .../bench/ClasspathOnlySymbolSearch.scala | 4 + .../internal/metals/DefinitionProvider.scala | 16 ++++ .../internal/metals/MetalsSymbolSearch.scala | 9 ++ .../main/java/scala/meta/pc/SymbolSearch.java | 5 + .../main/java/scala/meta/pc/VirtualFile.java | 18 ++++ .../internal/metals/MetalsVirtualFile.scala | 16 ++++ .../scala/meta/internal/pc/Completions.scala | 39 +++++++- .../meta/internal/pc/EmptySymbolSearch.scala | 5 + .../scala/tests/TestingSymbolSearch.scala | 19 ++++ .../feature/CompletionCrossLspSuite.scala | 6 ++ .../scala/tests/BaseCompletionLspSuite.scala | 91 +++++++++++++++++++ 11 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 mtags-interfaces/src/main/java/scala/meta/pc/VirtualFile.java create mode 100644 mtags/src/main/scala/scala/meta/internal/metals/MetalsVirtualFile.scala diff --git a/metals-bench/src/main/scala/bench/ClasspathOnlySymbolSearch.scala b/metals-bench/src/main/scala/bench/ClasspathOnlySymbolSearch.scala index ceed77f0dae..3340a5556f4 100644 --- a/metals-bench/src/main/scala/bench/ClasspathOnlySymbolSearch.scala +++ b/metals-bench/src/main/scala/bench/ClasspathOnlySymbolSearch.scala @@ -8,6 +8,7 @@ import scala.meta.internal.metals.WorkspaceSymbolQuery import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch import scala.meta.pc.SymbolSearchVisitor +import scala.meta.pc.VirtualFile /** * Implementation of `SymbolSearch` for only classpath symbols. @@ -21,6 +22,9 @@ class ClasspathOnlySymbolSearch(classpath: ClasspathSearch) def definition(symbol: String): ju.List[Location] = ju.Collections.emptyList() + override def definitionSource(symbol: String): ju.Optional[VirtualFile] = + ju.Optional.empty() + override def search( query: String, buildTargetIdentifier: String, diff --git a/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala b/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala index 03bd45ecaff..d375d17164c 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala @@ -73,6 +73,22 @@ final class DefinitionProvider( case Some(destination) => destination.locations } + /** + * Returns VirtualFile that contains the definition of + * the given symbol (of semanticdb). + */ + private[internal] def definitionPathInputFromSymbol( + sym: String + ): Option[Input.VirtualFile] = { + for { + symbolDefinition <- index.definition(Symbol(sym)) + defnPathInput = symbolDefinition.path.toInputFromBuffers(buffers) + } yield Input.VirtualFile( + defnPathInput.path, + defnPathInput.text + ) + } + def symbolOccurence( source: AbsolutePath, dirtyPosition: TextDocumentPositionParams diff --git a/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala b/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala index e010046d114..894e63fb92b 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala @@ -7,6 +7,7 @@ import ch.epfl.scala.bsp4j.BuildTargetIdentifier import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch import scala.meta.pc.SymbolSearchVisitor +import scala.meta.pc.VirtualFile /** * Implementation of SymbolSearch that delegates to WorkspaceSymbolProvider and SymbolDocumentationIndexer. @@ -23,6 +24,14 @@ class MetalsSymbolSearch( defn.fromSymbol(symbol) } + override def definitionSource(symbol: String): Optional[VirtualFile] = + Optional.ofNullable( + defn + .definitionPathInputFromSymbol(symbol) + .map(input => MetalsVirtualFile.fromInput(input)) + .orNull + ) + override def search( query: String, buildTargetIdentifier: String, diff --git a/mtags-interfaces/src/main/java/scala/meta/pc/SymbolSearch.java b/mtags-interfaces/src/main/java/scala/meta/pc/SymbolSearch.java index a1078bb27e9..809b497a57e 100644 --- a/mtags-interfaces/src/main/java/scala/meta/pc/SymbolSearch.java +++ b/mtags-interfaces/src/main/java/scala/meta/pc/SymbolSearch.java @@ -19,6 +19,11 @@ public interface SymbolSearch { */ List definition(String symbol); + /** + * Returns the file where the symbol is defined, if any. + */ + Optional definitionSource(String symbol); + /** * Runs fuzzy symbol search for the given query. * diff --git a/mtags-interfaces/src/main/java/scala/meta/pc/VirtualFile.java b/mtags-interfaces/src/main/java/scala/meta/pc/VirtualFile.java new file mode 100644 index 00000000000..d1272a57557 --- /dev/null +++ b/mtags-interfaces/src/main/java/scala/meta/pc/VirtualFile.java @@ -0,0 +1,18 @@ +package scala.meta.pc; + +/** + * An interface of the virtual file that contains + * path information and its content. + */ +public interface VirtualFile { + + /** + * The path name of the virtual file. + */ + String path(); + + /** + * The content of the virtual file. + */ + String value(); +} \ No newline at end of file diff --git a/mtags/src/main/scala/scala/meta/internal/metals/MetalsVirtualFile.scala b/mtags/src/main/scala/scala/meta/internal/metals/MetalsVirtualFile.scala new file mode 100644 index 00000000000..6bd6ddc64fd --- /dev/null +++ b/mtags/src/main/scala/scala/meta/internal/metals/MetalsVirtualFile.scala @@ -0,0 +1,16 @@ +package scala.meta.internal.metals + +import scala.meta.pc.VirtualFile +import scala.meta.inputs.Input + +case class MetalsVirtualFile( + path: String, + value: String +) extends VirtualFile +object MetalsVirtualFile { + def fromInput(input: Input.VirtualFile): MetalsVirtualFile = + MetalsVirtualFile( + input.path, + input.value + ) +} diff --git a/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala b/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala index cf74117a487..b95ea772bed 100644 --- a/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala +++ b/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala @@ -16,6 +16,8 @@ import scala.meta.internal.tokenizers.Chars import scala.meta.pc.PresentationCompilerConfig.OverrideDefFormat import scala.util.control.NonFatal import scala.collection.immutable.Nil +import scala.meta.inputs.Input +import scala.meta.internal.mtags.Mtags /** * Utility methods for completions. @@ -1298,10 +1300,45 @@ trait Completions { this: MetalsGlobal => val importPos = autoImportPosition(pos, text) val context = doLocateImportContext(pos, importPos) val subclasses = ListBuffer.empty[Symbol] + tpe.typeSymbol.foreachKnownDirectSubClass { sym => subclasses += sym } - val sortedSubclasses = subclasses.result().sortBy(sym => (sym.pos.line, sym.pos.column)) + val subclassesResult = subclasses.result() + + // sort subclasses by declaration order + // see: https://github.com/scalameta/metals-feature-requests/issues/49 + val sortedSubclasses = + if (subclassesResult.forall(_.pos.isDefined)) { + // if all the symbols of subclasses' position is defined + // we can sort those symbols by declaration order + // based on their position information quite cheaply + subclassesResult.sortBy(subclass => + (subclass.pos.line, subclass.pos.column) + ) + } else { + // Read all the symbols in the file that contains + // the definition of the symbol in declaration order + search + .definitionSource(semanticdbSymbol(tpe.typeSymbol)) + .asScala + .map { file => + Mtags.toplevels( + Input.VirtualFile( + file.path(), + file.value() + ) + ) + } + .map(defnSymbols => + subclassesResult + .sortBy(sym => { + defnSymbols.indexOf(semanticdbSymbol(sym)) + }) + ) + .getOrElse(subclassesResult) + } + sortedSubclasses.foreach { sym => val (shortName, edits) = importPos match { diff --git a/mtags/src/main/scala/scala/meta/internal/pc/EmptySymbolSearch.scala b/mtags/src/main/scala/scala/meta/internal/pc/EmptySymbolSearch.scala index e65a1a42a42..7631e98072d 100644 --- a/mtags/src/main/scala/scala/meta/internal/pc/EmptySymbolSearch.scala +++ b/mtags/src/main/scala/scala/meta/internal/pc/EmptySymbolSearch.scala @@ -6,6 +6,7 @@ import org.eclipse.lsp4j.Location import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch import scala.meta.pc.SymbolSearchVisitor +import scala.meta.pc.VirtualFile object EmptySymbolSearch extends SymbolSearch { override def search( @@ -20,6 +21,10 @@ object EmptySymbolSearch extends SymbolSearch { ju.Collections.emptyList() } + override def definitionSource(symbol: String): ju.Optional[VirtualFile] = { + ju.Optional.empty() + } + override def documentation(symbol: String): Optional[SymbolDocumentation] = Optional.empty() } diff --git a/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala b/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala index d90e2aade06..cbfd40ab917 100644 --- a/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala +++ b/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala @@ -5,6 +5,7 @@ import org.eclipse.lsp4j.Location import java.util.Optional import scala.meta.internal.metals.ClasspathSearch import scala.meta.internal.metals.Docstrings +import scala.meta.internal.metals.MetalsVirtualFile import scala.meta.internal.metals.WorkspaceSymbolQuery import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch @@ -12,6 +13,7 @@ import scala.meta.pc.SymbolSearchVisitor import scala.meta.internal.mtags.OnDemandSymbolIndex import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.internal.mtags.Symbol +import scala.meta.pc.VirtualFile /** * Implementation of `SymbolSearch` for testing purposes. @@ -46,6 +48,23 @@ class TestingSymbolSearch( } } + override def definitionSource(symbol: String): ju.Optional[VirtualFile] = { + index.definition(Symbol(symbol)) match { + case None => + ju.Optional.empty() + case Some(value) => + import java.nio.file.Files + val filename = value.path.toNIO.getFileName().toString() + val content = new String(Files.readAllBytes(value.path.toNIO)) + ju.Optional.of( + MetalsVirtualFile( + filename, + content + ) + ) + } + } + override def search( textQuery: String, buildTargetIdentifier: String, diff --git a/tests/slow/src/test/scala/tests/feature/CompletionCrossLspSuite.scala b/tests/slow/src/test/scala/tests/feature/CompletionCrossLspSuite.scala index 55bafeba92a..20a32f5a2af 100644 --- a/tests/slow/src/test/scala/tests/feature/CompletionCrossLspSuite.scala +++ b/tests/slow/src/test/scala/tests/feature/CompletionCrossLspSuite.scala @@ -11,4 +11,10 @@ object CompletionCrossLspSuite testAsync("basic-213") { basicTest(V.scala213) } + testAsync("match-211") { + matchKeywordTest(V.scala213) + } + testAsync("match-213") { + matchKeywordTest(V.scala213) + } } diff --git a/tests/unit/src/main/scala/tests/BaseCompletionLspSuite.scala b/tests/unit/src/main/scala/tests/BaseCompletionLspSuite.scala index b083232e895..cc80181cd95 100644 --- a/tests/unit/src/main/scala/tests/BaseCompletionLspSuite.scala +++ b/tests/unit/src/main/scala/tests/BaseCompletionLspSuite.scala @@ -2,6 +2,7 @@ package tests import org.eclipse.lsp4j.CompletionList import scala.concurrent.Future +import scala.meta.internal.metals.TextEdits abstract class BaseCompletionLspSuite(name: String) extends BaseLspSuite(name) { @@ -33,6 +34,40 @@ abstract class BaseCompletionLspSuite(name: String) extends BaseLspSuite(name) { } } + def withCompletionEdit( + query: String, + project: Char = 'a', + filter: String => Boolean = _ => true + )( + fn: String => Unit + ): Future[Unit] = { + import scala.collection.JavaConverters._ + val filename = s"$project/src/main/scala/$project/${project.toUpper}.scala" + val text = server + .textContentsOnDisk(filename) + .replaceAllLiterally("// @@", query.replaceAllLiterally("@@", "")) + for { + _ <- server.didChange(filename)(_ => text) + completion <- server.completionList(filename, query) + } yield { + val items = + completion.getItems().asScala.filter(item => filter(item.getLabel)) + val obtained = TextEdits.applyEdits(text, items.head) + fn(obtained) + } + } + + def assertCompletionEdit( + query: String, + expected: String, + project: Char = 'a', + filter: String => Boolean = _ => true + ): Future[Unit] = { + withCompletionEdit(query, project, filter) { obtained => + assertNoDiff(obtained, expected) + } + } + def basicTest(scalaVersion: String): Future[Unit] = { cleanWorkspace() for { @@ -125,4 +160,60 @@ abstract class BaseCompletionLspSuite(name: String) extends BaseLspSuite(name) { ) } yield () } + + def matchKeywordTest(scalaVersion: String): Future[Unit] = { + cleanWorkspace() + for { + _ <- server.initialize( + s"""/metals.json + |{ + | "a": { "scalaVersion": "${scalaVersion}" } + |} + |/a/src/main/scala/a/A.scala + |package a + |object A { + | val x: Option[Int] = Some(1) + | // @@ + |} + |/a/src/main/scala/a/Color.scala + |package a + |abstract sealed class Color + |case object Red extends Color + |case object Blue extends Color + |case object Green extends Color + |""".stripMargin + ) + _ <- server.didOpen("a/src/main/scala/a/A.scala") + _ = assertNoDiagnostics() + // completed exhausted matches should be sorted by declaration order + // https://github.com/scala/scala/blob/cca78e1e18c55e5b0223b9dfa4ac230f7bc6a858/src/library/scala/Option.scala#L513-L527 + _ <- assertCompletionEdit( + "x matc@@", + """|package a + |object A { + | val x: Option[Int] = Some(1) + | x match { + |\tcase Some(value) => + |\tcase None => + |} + |} + |""".stripMargin, + filter = _.contains("exhaustive") + ) + _ <- assertCompletionEdit( + "null.asInstanceOf[Color] matc@@", + """|package a + |object A { + | val x: Option[Int] = Some(1) + | null.asInstanceOf[Color] match { + |\tcase Red => + |\tcase Blue => + |\tcase Green => + |} + |} + |""".stripMargin, + filter = _.contains("exhaustive") + ) + } yield () + } } From cde0baa1bdc0093f2de445774494a79f4d8e5678 Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Tue, 10 Dec 2019 22:08:53 +0900 Subject: [PATCH 03/12] Rename test name to short one --- tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala index fead71c9cdd..d642dcdd8ce 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala @@ -104,7 +104,7 @@ object CompletionMatchSuite extends BaseCompletionSuite { ) checkEdit( - "Sort auto-completed exhaustive match keywords by declaration order", + "exhaustive-sorting", """package sort |sealed abstract class TestTree |case class Branch1(t1: TestTree) extends TestTree From bbfaeaad2cf291ccff176c69a3ab026a205ff638 Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Tue, 10 Dec 2019 22:18:32 +0900 Subject: [PATCH 04/12] Rename MetalsVirtualFile into VirtualFileImpl and some refactor stuff --- .../scala/meta/internal/metals/MetalsSymbolSearch.scala | 2 +- .../{MetalsVirtualFile.scala => VirtualFileImpl.scala} | 9 +++++---- .../mtest/src/main/scala/tests/TestingSymbolSearch.scala | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) rename mtags/src/main/scala/scala/meta/internal/metals/{MetalsVirtualFile.scala => VirtualFileImpl.scala} (58%) diff --git a/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala b/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala index 894e63fb92b..e101bd0674e 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala @@ -28,7 +28,7 @@ class MetalsSymbolSearch( Optional.ofNullable( defn .definitionPathInputFromSymbol(symbol) - .map(input => MetalsVirtualFile.fromInput(input)) + .map(input => VirtualFileImpl.fromInput(input)) .orNull ) diff --git a/mtags/src/main/scala/scala/meta/internal/metals/MetalsVirtualFile.scala b/mtags/src/main/scala/scala/meta/internal/metals/VirtualFileImpl.scala similarity index 58% rename from mtags/src/main/scala/scala/meta/internal/metals/MetalsVirtualFile.scala rename to mtags/src/main/scala/scala/meta/internal/metals/VirtualFileImpl.scala index 6bd6ddc64fd..ce208a3e5cd 100644 --- a/mtags/src/main/scala/scala/meta/internal/metals/MetalsVirtualFile.scala +++ b/mtags/src/main/scala/scala/meta/internal/metals/VirtualFileImpl.scala @@ -3,13 +3,14 @@ package scala.meta.internal.metals import scala.meta.pc.VirtualFile import scala.meta.inputs.Input -case class MetalsVirtualFile( +case class VirtualFileImpl( path: String, value: String ) extends VirtualFile -object MetalsVirtualFile { - def fromInput(input: Input.VirtualFile): MetalsVirtualFile = - MetalsVirtualFile( + +object VirtualFileImpl { + def fromInput(input: Input.VirtualFile): VirtualFileImpl = + VirtualFileImpl( input.path, input.value ) diff --git a/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala b/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala index cbfd40ab917..502aa71220a 100644 --- a/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala +++ b/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala @@ -3,9 +3,10 @@ package tests import java.{util => ju} import org.eclipse.lsp4j.Location import java.util.Optional +import java.nio.file.Files import scala.meta.internal.metals.ClasspathSearch import scala.meta.internal.metals.Docstrings -import scala.meta.internal.metals.MetalsVirtualFile +import scala.meta.internal.metals.VirtualFileImpl import scala.meta.internal.metals.WorkspaceSymbolQuery import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch @@ -53,11 +54,10 @@ class TestingSymbolSearch( case None => ju.Optional.empty() case Some(value) => - import java.nio.file.Files val filename = value.path.toNIO.getFileName().toString() val content = new String(Files.readAllBytes(value.path.toNIO)) ju.Optional.of( - MetalsVirtualFile( + VirtualFileImpl( filename, content ) From 240dfe234bc19ca5ed5253e79e3417f66ff39f8b Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Sat, 14 Dec 2019 21:09:56 +0900 Subject: [PATCH 05/12] Change definitionSource API to return the symbols directly --- .../bench/ClasspathOnlySymbolSearch.scala | 5 ++- .../internal/metals/MetalsSymbolSearch.scala | 16 +++++----- .../main/java/scala/meta/pc/SymbolSearch.java | 5 +-- .../main/java/scala/meta/pc/VirtualFile.java | 18 ----------- .../internal/metals/VirtualFileImpl.scala | 17 ---------- .../scala/meta/internal/pc/Completions.scala | 31 ++++++------------- .../meta/internal/pc/EmptySymbolSearch.scala | 5 ++- .../scala/tests/TestingSymbolSearch.scala | 18 +++++------ 8 files changed, 34 insertions(+), 81 deletions(-) delete mode 100644 mtags-interfaces/src/main/java/scala/meta/pc/VirtualFile.java delete mode 100644 mtags/src/main/scala/scala/meta/internal/metals/VirtualFileImpl.scala diff --git a/metals-bench/src/main/scala/bench/ClasspathOnlySymbolSearch.scala b/metals-bench/src/main/scala/bench/ClasspathOnlySymbolSearch.scala index 3340a5556f4..b9e088381d1 100644 --- a/metals-bench/src/main/scala/bench/ClasspathOnlySymbolSearch.scala +++ b/metals-bench/src/main/scala/bench/ClasspathOnlySymbolSearch.scala @@ -8,7 +8,6 @@ import scala.meta.internal.metals.WorkspaceSymbolQuery import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch import scala.meta.pc.SymbolSearchVisitor -import scala.meta.pc.VirtualFile /** * Implementation of `SymbolSearch` for only classpath symbols. @@ -22,8 +21,8 @@ class ClasspathOnlySymbolSearch(classpath: ClasspathSearch) def definition(symbol: String): ju.List[Location] = ju.Collections.emptyList() - override def definitionSource(symbol: String): ju.Optional[VirtualFile] = - ju.Optional.empty() + override def definitionSourceToplevels(symbol: String): ju.List[String] = + ju.Collections.emptyList() override def search( query: String, diff --git a/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala b/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala index e101bd0674e..57d95358e09 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala @@ -7,7 +7,7 @@ import ch.epfl.scala.bsp4j.BuildTargetIdentifier import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch import scala.meta.pc.SymbolSearchVisitor -import scala.meta.pc.VirtualFile +import scala.meta.internal.mtags.Mtags /** * Implementation of SymbolSearch that delegates to WorkspaceSymbolProvider and SymbolDocumentationIndexer. @@ -24,13 +24,13 @@ class MetalsSymbolSearch( defn.fromSymbol(symbol) } - override def definitionSource(symbol: String): Optional[VirtualFile] = - Optional.ofNullable( - defn - .definitionPathInputFromSymbol(symbol) - .map(input => VirtualFileImpl.fromInput(input)) - .orNull - ) + override def definitionSourceToplevels(symbol: String): ju.List[String] = { + import scala.collection.JavaConverters._ + defn + .definitionPathInputFromSymbol(symbol) + .map(input => Mtags.toplevels(input).asJava) + .getOrElse(ju.Collections.emptyList()) + } override def search( query: String, diff --git a/mtags-interfaces/src/main/java/scala/meta/pc/SymbolSearch.java b/mtags-interfaces/src/main/java/scala/meta/pc/SymbolSearch.java index 809b497a57e..a38539f9143 100644 --- a/mtags-interfaces/src/main/java/scala/meta/pc/SymbolSearch.java +++ b/mtags-interfaces/src/main/java/scala/meta/pc/SymbolSearch.java @@ -20,9 +20,10 @@ public interface SymbolSearch { List definition(String symbol); /** - * Returns the file where the symbol is defined, if any. + * Returns the all symbols in the file where the given symbol is defined + * in declaration order, if any. */ - Optional definitionSource(String symbol); + List definitionSourceToplevels(String symbol); /** * Runs fuzzy symbol search for the given query. diff --git a/mtags-interfaces/src/main/java/scala/meta/pc/VirtualFile.java b/mtags-interfaces/src/main/java/scala/meta/pc/VirtualFile.java deleted file mode 100644 index d1272a57557..00000000000 --- a/mtags-interfaces/src/main/java/scala/meta/pc/VirtualFile.java +++ /dev/null @@ -1,18 +0,0 @@ -package scala.meta.pc; - -/** - * An interface of the virtual file that contains - * path information and its content. - */ -public interface VirtualFile { - - /** - * The path name of the virtual file. - */ - String path(); - - /** - * The content of the virtual file. - */ - String value(); -} \ No newline at end of file diff --git a/mtags/src/main/scala/scala/meta/internal/metals/VirtualFileImpl.scala b/mtags/src/main/scala/scala/meta/internal/metals/VirtualFileImpl.scala deleted file mode 100644 index ce208a3e5cd..00000000000 --- a/mtags/src/main/scala/scala/meta/internal/metals/VirtualFileImpl.scala +++ /dev/null @@ -1,17 +0,0 @@ -package scala.meta.internal.metals - -import scala.meta.pc.VirtualFile -import scala.meta.inputs.Input - -case class VirtualFileImpl( - path: String, - value: String -) extends VirtualFile - -object VirtualFileImpl { - def fromInput(input: Input.VirtualFile): VirtualFileImpl = - VirtualFileImpl( - input.path, - input.value - ) -} diff --git a/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala b/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala index b95ea772bed..bafa0f8a148 100644 --- a/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala +++ b/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala @@ -16,8 +16,6 @@ import scala.meta.internal.tokenizers.Chars import scala.meta.pc.PresentationCompilerConfig.OverrideDefFormat import scala.util.control.NonFatal import scala.collection.immutable.Nil -import scala.meta.inputs.Input -import scala.meta.internal.mtags.Mtags /** * Utility methods for completions. @@ -1317,26 +1315,17 @@ trait Completions { this: MetalsGlobal => (subclass.pos.line, subclass.pos.column) ) } else { - // Read all the symbols in the file that contains + // Read all the symbols in the source that contains // the definition of the symbol in declaration order - search - .definitionSource(semanticdbSymbol(tpe.typeSymbol)) - .asScala - .map { file => - Mtags.toplevels( - Input.VirtualFile( - file.path(), - file.value() - ) - ) - } - .map(defnSymbols => - subclassesResult - .sortBy(sym => { - defnSymbols.indexOf(semanticdbSymbol(sym)) - }) - ) - .getOrElse(subclassesResult) + val defnSymbols = search + .definitionSourceToplevels(semanticdbSymbol(tpe.typeSymbol)).asScala + if (defnSymbols.length > 0) + subclassesResult + .sortBy(sym => { + defnSymbols.indexOf(semanticdbSymbol(sym)) + }) + else + subclassesResult } sortedSubclasses.foreach { sym => diff --git a/mtags/src/main/scala/scala/meta/internal/pc/EmptySymbolSearch.scala b/mtags/src/main/scala/scala/meta/internal/pc/EmptySymbolSearch.scala index 7631e98072d..bac2c4bf602 100644 --- a/mtags/src/main/scala/scala/meta/internal/pc/EmptySymbolSearch.scala +++ b/mtags/src/main/scala/scala/meta/internal/pc/EmptySymbolSearch.scala @@ -6,7 +6,6 @@ import org.eclipse.lsp4j.Location import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch import scala.meta.pc.SymbolSearchVisitor -import scala.meta.pc.VirtualFile object EmptySymbolSearch extends SymbolSearch { override def search( @@ -21,8 +20,8 @@ object EmptySymbolSearch extends SymbolSearch { ju.Collections.emptyList() } - override def definitionSource(symbol: String): ju.Optional[VirtualFile] = { - ju.Optional.empty() + override def definitionSourceToplevels(symbol: String): ju.List[String] = { + ju.Collections.emptyList() } override def documentation(symbol: String): Optional[SymbolDocumentation] = diff --git a/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala b/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala index 502aa71220a..bcfae96b685 100644 --- a/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala +++ b/tests/mtest/src/main/scala/tests/TestingSymbolSearch.scala @@ -4,9 +4,9 @@ import java.{util => ju} import org.eclipse.lsp4j.Location import java.util.Optional import java.nio.file.Files +import scala.meta.inputs.Input import scala.meta.internal.metals.ClasspathSearch import scala.meta.internal.metals.Docstrings -import scala.meta.internal.metals.VirtualFileImpl import scala.meta.internal.metals.WorkspaceSymbolQuery import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch @@ -14,7 +14,7 @@ import scala.meta.pc.SymbolSearchVisitor import scala.meta.internal.mtags.OnDemandSymbolIndex import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.internal.mtags.Symbol -import scala.meta.pc.VirtualFile +import scala.meta.internal.mtags.Mtags /** * Implementation of `SymbolSearch` for testing purposes. @@ -49,19 +49,19 @@ class TestingSymbolSearch( } } - override def definitionSource(symbol: String): ju.Optional[VirtualFile] = { + override def definitionSourceToplevels(symbol: String): ju.List[String] = { index.definition(Symbol(symbol)) match { case None => - ju.Optional.empty() + ju.Collections.emptyList() case Some(value) => + import scala.collection.JavaConverters._ val filename = value.path.toNIO.getFileName().toString() val content = new String(Files.readAllBytes(value.path.toNIO)) - ju.Optional.of( - VirtualFileImpl( - filename, - content - ) + val input = Input.VirtualFile( + filename, + content ) + Mtags.toplevels(input).asJava } } From 4c252eb28d3f638b5f068ebb4e83394eaefddd57 Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Sat, 14 Dec 2019 21:32:51 +0900 Subject: [PATCH 06/12] Add a test that check metals is capable to sort exhaustive matches for 3rd party defined symbols --- .../scala/tests/pc/CompletionMatchSuite.scala | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala index d642dcdd8ce..279dabf392d 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala @@ -3,6 +3,10 @@ package tests.pc import tests.BaseCompletionSuite object CompletionMatchSuite extends BaseCompletionSuite { + override def beforeAll(): Unit = { + indexScalaLibrary() + } + check( "match", """ @@ -130,6 +134,24 @@ object CompletionMatchSuite extends BaseCompletionSuite { filter = _.contains("exhaustive") ) + checkEdit( + "exhaustive-sorting-scalalib", + """package sort + |object App { + | Option(1) matc@@ + |} + |""".stripMargin, + """package sort + |object App { + | Option(1) match { + |\tcase Some(value) => $0 + |\tcase None => + |} + |} + |""".stripMargin, + filter = _.contains("exhaustive") + ) + check( "inner-class", """ From a70886e97250f94890b922cb89662fb911f3eaf5 Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Sat, 14 Dec 2019 23:35:22 +0900 Subject: [PATCH 07/12] Cache the result of definitionSourceToplevels --- .../meta/internal/metals/MetalsSymbolSearch.scala | 15 +++++++++++---- .../scala/meta/internal/pc/Completions.scala | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala b/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala index 57d95358e09..ea46123249f 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala @@ -3,6 +3,7 @@ package scala.meta.internal.metals import java.{util => ju} import org.eclipse.lsp4j.Location import java.util.Optional +import scala.collection.concurrent.TrieMap import ch.epfl.scala.bsp4j.BuildTargetIdentifier import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch @@ -17,6 +18,9 @@ class MetalsSymbolSearch( wsp: WorkspaceSymbolProvider, defn: DefinitionProvider ) extends SymbolSearch { + // cache for definitionSourceTopLevels + private val cache = new TrieMap[String, ju.List[String]]() + override def documentation(symbol: String): Optional[SymbolDocumentation] = docs.documentation(symbol) @@ -26,10 +30,13 @@ class MetalsSymbolSearch( override def definitionSourceToplevels(symbol: String): ju.List[String] = { import scala.collection.JavaConverters._ - defn - .definitionPathInputFromSymbol(symbol) - .map(input => Mtags.toplevels(input).asJava) - .getOrElse(ju.Collections.emptyList()) + cache.getOrElseUpdate( + symbol, + defn + .definitionPathInputFromSymbol(symbol) + .map(input => Mtags.toplevels(input).asJava) + .getOrElse(ju.Collections.emptyList()) + ) } override def search( diff --git a/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala b/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala index bafa0f8a148..f56c3de7ea5 100644 --- a/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala +++ b/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala @@ -1318,7 +1318,8 @@ trait Completions { this: MetalsGlobal => // Read all the symbols in the source that contains // the definition of the symbol in declaration order val defnSymbols = search - .definitionSourceToplevels(semanticdbSymbol(tpe.typeSymbol)).asScala + .definitionSourceToplevels(semanticdbSymbol(tpe.typeSymbol)) + .asScala if (defnSymbols.length > 0) subclassesResult .sortBy(sym => { From ad787b074a77049967a5e2b54b213a4774e7d9a3 Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Sun, 15 Dec 2019 00:04:28 +0900 Subject: [PATCH 08/12] Fix MatchKeywordSuite in scala 2.11 In scala 2.11, the parameter of `Some` is x instead of value https://github.com/scala/scala/blob/2e2f65a201b0b06e01a39fec4ae232c5b94efcca/src/library/scala/Option.scala#L332-L348 --- .../scala/tests/pc/CompletionMatchSuite.scala | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala index 279dabf392d..0232bf64d48 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala @@ -134,23 +134,35 @@ object CompletionMatchSuite extends BaseCompletionSuite { filter = _.contains("exhaustive") ) - checkEdit( - "exhaustive-sorting-scalalib", - """package sort - |object App { - | Option(1) matc@@ - |} - |""".stripMargin, - """package sort - |object App { - | Option(1) match { - |\tcase Some(value) => $0 - |\tcase None => - |} - |} - |""".stripMargin, - filter = _.contains("exhaustive") - ) + if (!isScala211) { + checkEdit( + "exhaustive-sorting-scalalib", + """package sort + |object App { + | Option(1) matc@@ + |} + |""".stripMargin, + if (!isScala211) + """package sort + |object App { + | Option(1) match { + |\tcase Some(value) => $0 + |\tcase None => + |} + |} + |""".stripMargin + else + """package sort + |object App { + | Option(1) match { + |\tcase Some(x) => $0 + |\tcase None => + |} + |} + |""".stripMargin, + filter = _.contains("exhaustive") + ) + } check( "inner-class", From a68027a5e8832ac885c8515cb22cc9b7cb33593c Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Mon, 16 Dec 2019 01:19:52 +0900 Subject: [PATCH 09/12] Reuse the cache in WorkspaceSymbolProvider in definitionSourceToplevels Since the cache for AbsolutePath -> SymbolInformation in WorkspaceSymbolProvider is updated on file save, definitionSourceToplevels can reflect the file changes while caching the symbols. --- .../internal/metals/DefinitionProvider.scala | 13 ++--- .../metals/MetalsLanguageServer.scala | 13 +++-- .../internal/metals/MetalsSymbolSearch.scala | 56 +++++++++++++++---- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala b/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala index d375d17164c..753606b81b3 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala @@ -79,15 +79,10 @@ final class DefinitionProvider( */ private[internal] def definitionPathInputFromSymbol( sym: String - ): Option[Input.VirtualFile] = { - for { - symbolDefinition <- index.definition(Symbol(sym)) - defnPathInput = symbolDefinition.path.toInputFromBuffers(buffers) - } yield Input.VirtualFile( - defnPathInput.path, - defnPathInput.text - ) - } + ): Option[Input.VirtualFile] = + index + .definition(Symbol(sym)) + .map(symDef => symDef.path.toInputFromBuffers(buffers)) def symbolOccurence( source: AbsolutePath, diff --git a/metals/src/main/scala/scala/meta/internal/metals/MetalsLanguageServer.scala b/metals/src/main/scala/scala/meta/internal/metals/MetalsLanguageServer.scala index d453f3e066a..2576b62b4f6 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MetalsLanguageServer.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MetalsLanguageServer.scala @@ -173,6 +173,7 @@ class MetalsLanguageServer( private var foldingRangeProvider: FoldingRangeProvider = _ private val packageProvider: PackageProvider = new PackageProvider(buildTargets) + private var symbolSearch: MetalsSymbolSearch = _ private var compilers: Compilers = _ var tables: Tables = _ var statusBar: StatusBar = _ @@ -386,6 +387,11 @@ class MetalsLanguageServer( interactiveSemanticdbs.toFileOnDisk ) foldingRangeProvider = FoldingRangeProvider(trees, buffers, params) + symbolSearch = new MetalsSymbolSearch( + symbolDocs, + workspaceSymbols, + definitionProvider + ) compilers = register( new Compilers( workspace, @@ -393,11 +399,7 @@ class MetalsLanguageServer( () => userConfig, buildTargets, buffers, - new MetalsSymbolSearch( - symbolDocs, - workspaceSymbols, - definitionProvider - ), + symbolSearch, embedded, statusBar, sh, @@ -1597,6 +1599,7 @@ class MetalsLanguageServer( semanticDBIndexer.reset() treeView.reset() worksheetProvider.reset() + symbolSearch.reset() buildTargets.addWorkspaceBuildTargets(i.workspaceBuildTargets) buildTargets.addScalacOptions(i.scalacOptions) for { diff --git a/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala b/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala index ea46123249f..991ca8e7c08 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MetalsSymbolSearch.scala @@ -9,6 +9,8 @@ import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch import scala.meta.pc.SymbolSearchVisitor import scala.meta.internal.mtags.Mtags +import scala.meta.io.AbsolutePath +import scala.meta.internal.metals.MetalsEnrichments._ /** * Implementation of SymbolSearch that delegates to WorkspaceSymbolProvider and SymbolDocumentationIndexer. @@ -18,8 +20,15 @@ class MetalsSymbolSearch( wsp: WorkspaceSymbolProvider, defn: DefinitionProvider ) extends SymbolSearch { - // cache for definitionSourceTopLevels - private val cache = new TrieMap[String, ju.List[String]]() + // A cache for definitionSourceToplevels. + // The key is an absolutepath to the dependency source file, and + // the value is the list of symbols that the file contains. + private val dependencySourceCache = + new TrieMap[AbsolutePath, ju.List[String]]() + + def reset(): Unit = { + dependencySourceCache.clear() + } override def documentation(symbol: String): Optional[SymbolDocumentation] = docs.documentation(symbol) @@ -28,15 +37,42 @@ class MetalsSymbolSearch( defn.fromSymbol(symbol) } + /** + * Returns a list of semanticdb symbols in a source file that contains the + * definition of the given symbol. + */ override def definitionSourceToplevels(symbol: String): ju.List[String] = { - import scala.collection.JavaConverters._ - cache.getOrElseUpdate( - symbol, - defn - .definitionPathInputFromSymbol(symbol) - .map(input => Mtags.toplevels(input).asJava) - .getOrElse(ju.Collections.emptyList()) - ) + defn + .definitionPathInputFromSymbol(symbol) + .map(input => { + val path = AbsolutePath(input.path) + if (path.isWorkspaceSource(wsp.workspace)) { + // If the source file is a workspace source, retrieve its symbols from + // WorkspaceSymbolProvider so that metals server can reuse its cache. + wsp.inWorkspace + .get(path.toNIO) + .map(symInfo => { + symInfo.symbols + .sortBy(sym => + ( + sym.range.getStart().getLine(), + sym.range.getStart().getCharacter() + ) + ) + .map(_.symbol) + .asJava + }) + .getOrElse( + ju.Collections.emptyList[String]() + ) + } else { + dependencySourceCache.getOrElseUpdate( + path, + Mtags.toplevels(input).asJava + ) + } + }) + .getOrElse(ju.Collections.emptyList()) } override def search( From 45c6c892d1caebdd6801500dcb61df8733bdf4aa Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Mon, 16 Dec 2019 02:03:20 +0900 Subject: [PATCH 10/12] Remove package private modifier from definitionPathInputFromSymbol --- .../scala/scala/meta/internal/metals/DefinitionProvider.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala b/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala index 753606b81b3..f363244d090 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/DefinitionProvider.scala @@ -77,7 +77,7 @@ final class DefinitionProvider( * Returns VirtualFile that contains the definition of * the given symbol (of semanticdb). */ - private[internal] def definitionPathInputFromSymbol( + def definitionPathInputFromSymbol( sym: String ): Option[Input.VirtualFile] = index From 707a24f40d27f1bb528fa719742b417e89a06b06 Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Mon, 16 Dec 2019 02:46:15 +0900 Subject: [PATCH 11/12] Cache the symbol's order while sorting exhastive match cases --- .../main/scala/scala/meta/internal/pc/Completions.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala b/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala index f56c3de7ea5..8ef7717bc5b 100644 --- a/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala +++ b/mtags/src/main/scala/scala/meta/internal/pc/Completions.scala @@ -1320,13 +1320,15 @@ trait Completions { this: MetalsGlobal => val defnSymbols = search .definitionSourceToplevels(semanticdbSymbol(tpe.typeSymbol)) .asScala - if (defnSymbols.length > 0) + if (defnSymbols.length > 0) { + val symbolIdx = defnSymbols.zipWithIndex.toMap subclassesResult .sortBy(sym => { - defnSymbols.indexOf(semanticdbSymbol(sym)) + symbolIdx.getOrElse(semanticdbSymbol(sym), -1) }) - else + } else { subclassesResult + } } sortedSubclasses.foreach { sym => From 0d5f96fb977b6c73c44b8b5c061480393ecb1c82 Mon Sep 17 00:00:00 2001 From: tanishiking24 Date: Mon, 16 Dec 2019 21:56:44 +0900 Subject: [PATCH 12/12] Remove unnecessary scala version checking --- .../scala/tests/pc/CompletionMatchSuite.scala | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala b/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala index 0232bf64d48..f5e274efe08 100644 --- a/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala +++ b/tests/cross/src/test/scala/tests/pc/CompletionMatchSuite.scala @@ -134,35 +134,33 @@ object CompletionMatchSuite extends BaseCompletionSuite { filter = _.contains("exhaustive") ) - if (!isScala211) { - checkEdit( - "exhaustive-sorting-scalalib", + checkEdit( + "exhaustive-sorting-scalalib", + """package sort + |object App { + | Option(1) matc@@ + |} + |""".stripMargin, + if (!isScala211) + """package sort + |object App { + | Option(1) match { + |\tcase Some(value) => $0 + |\tcase None => + |} + |} + |""".stripMargin + else """package sort |object App { - | Option(1) matc@@ + | Option(1) match { + |\tcase Some(x) => $0 + |\tcase None => + |} |} |""".stripMargin, - if (!isScala211) - """package sort - |object App { - | Option(1) match { - |\tcase Some(value) => $0 - |\tcase None => - |} - |} - |""".stripMargin - else - """package sort - |object App { - | Option(1) match { - |\tcase Some(x) => $0 - |\tcase None => - |} - |} - |""".stripMargin, - filter = _.contains("exhaustive") - ) - } + filter = _.contains("exhaustive") + ) check( "inner-class",