Skip to content

Commit

Permalink
Backport "Completion assert diffs will now show completion source" to…
Browse files Browse the repository at this point in the history
… LTS (#20792)

Backports #18890 to the LTS branch.

PR submitted by the release tooling.
[skip ci]
  • Loading branch information
WojciechMazur authored Jun 26, 2024
2 parents e69a18d + e9b3bf2 commit fcd7562
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,8 @@ class CompletionProvider(
item.setAdditionalTextEdits((completion.additionalEdits ++ additionalEdits).asJava)
completion.insertMode.foreach(item.setInsertTextMode)

completion
.completionData(buildTargetIdentifier)
.foreach(data => item.setData(data.toJson))
val data = completion.completionData(buildTargetIdentifier)
item.setData(data.toJson)

item.setTags(completion.lspTags.asJava)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,24 @@ import org.eclipse.lsp4j.InsertTextMode
import org.eclipse.lsp4j.Range
import org.eclipse.lsp4j.TextEdit

enum CompletionSource:
case Empty
case OverrideKind
case ImplementAllKind
case CompilerKind
case KeywordKind
case ScopeKind
case WorkspaceKind
case ExtensionKind
case NamedArgKind
case AutoFillKind
case FileSystemMemberKind
case IvyImportKind
case InterpolatorKind
case MatchCompletionKind
case CaseKeywordKind
case DocumentKind

sealed trait CompletionValue:
def label: String
def insertText: Option[String] = None
Expand All @@ -24,11 +42,12 @@ sealed trait CompletionValue:
def range: Option[Range] = None
def filterText: Option[String] = None
def completionItemKind(using Context): CompletionItemKind
def completionItemDataKind: Integer = CompletionItemData.None
def description(printer: ShortenedTypePrinter)(using Context): String = ""
def insertMode: Option[InsertTextMode] = None
def completionData(buildTargetIdentifier: String)(
using Context
): Option[CompletionItemData] = None
): CompletionItemData = CompletionItemData("<no-symbol>", buildTargetIdentifier, kind = completionItemDataKind)
def command: Option[String] = None

/**
Expand All @@ -44,17 +63,15 @@ object CompletionValue:
sealed trait Symbolic extends CompletionValue:
def symbol: Symbol
def isFromWorkspace: Boolean = false
def completionItemDataKind = CompletionItemData.None
override def completionItemDataKind = CompletionItemData.None

override def completionData(
buildTargetIdentifier: String
)(using Context): Option[CompletionItemData] =
Some(
CompletionItemData(
SemanticdbSymbols.symbolName(symbol),
buildTargetIdentifier,
kind = completionItemDataKind
)
)(using Context): CompletionItemData =
CompletionItemData(
SemanticdbSymbols.symbolName(symbol),
buildTargetIdentifier,
kind = completionItemDataKind
)
def importSymbol: Symbol = symbol

Expand Down Expand Up @@ -106,19 +123,24 @@ object CompletionValue:
label: String,
symbol: Symbol,
override val snippetSuffix: CompletionSuffix
) extends Symbolic
) extends Symbolic {
override def completionItemDataKind: Integer = CompletionSource.CompilerKind.ordinal
}
case class Scope(
label: String,
symbol: Symbol,
override val snippetSuffix: CompletionSuffix,
) extends Symbolic
) extends Symbolic {
override def completionItemDataKind: Integer = CompletionSource.ScopeKind.ordinal
}
case class Workspace(
label: String,
symbol: Symbol,
override val snippetSuffix: CompletionSuffix,
override val importSymbol: Symbol
) extends Symbolic:
override def isFromWorkspace: Boolean = true
override def completionItemDataKind: Integer = CompletionSource.WorkspaceKind.ordinal

/**
* CompletionValue for extension methods via SymbolSearch
Expand All @@ -130,6 +152,7 @@ object CompletionValue:
) extends Symbolic:
override def completionItemKind(using Context): CompletionItemKind =
CompletionItemKind.Method
override def completionItemDataKind: Integer = CompletionSource.ExtensionKind.ordinal
override def description(printer: ShortenedTypePrinter)(using Context): String =
s"${printer.completionSymbol(symbol)} (extension)"

Expand All @@ -150,8 +173,7 @@ object CompletionValue:
override val range: Option[Range]
) extends Symbolic:
override def insertText: Option[String] = Some(value)
override def completionItemDataKind: Integer =
CompletionItemData.OverrideKind
override def completionItemDataKind: Integer = CompletionSource.OverrideKind.ordinal
override def completionItemKind(using Context): CompletionItemKind =
CompletionItemKind.Method
override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String =
Expand All @@ -164,6 +186,7 @@ object CompletionValue:
symbol: Symbol
) extends Symbolic:
override def insertText: Option[String] = Some(label.replace("$", "$$").nn)
override def completionItemDataKind: Integer = CompletionSource.OverrideKind.ordinal
override def completionItemKind(using Context): CompletionItemKind =
CompletionItemKind.Field
override def description(printer: ShortenedTypePrinter)(using Context): String =
Expand All @@ -178,11 +201,13 @@ object CompletionValue:
) extends CompletionValue:
override def completionItemKind(using Context): CompletionItemKind =
CompletionItemKind.Enum
override def completionItemDataKind: Integer = CompletionSource.OverrideKind.ordinal
override def insertText: Option[String] = Some(value)
override def label: String = "Autofill with default values"

case class Keyword(label: String, override val insertText: Option[String])
extends CompletionValue:
override def completionItemDataKind: Integer = CompletionSource.KeywordKind.ordinal
override def completionItemKind(using Context): CompletionItemKind =
CompletionItemKind.Keyword

Expand All @@ -193,6 +218,7 @@ object CompletionValue:
) extends CompletionValue:
override def label: String = filename
override def insertText: Option[String] = Some(filename.stripSuffix(".sc"))
override def completionItemDataKind: Integer = CompletionSource.FileSystemMemberKind.ordinal
override def completionItemKind(using Context): CompletionItemKind =
CompletionItemKind.File

Expand All @@ -202,6 +228,7 @@ object CompletionValue:
override val range: Option[Range]
) extends CompletionValue:
override val filterText: Option[String] = insertText
override def completionItemDataKind: Integer = CompletionSource.IvyImportKind.ordinal
override def completionItemKind(using Context): CompletionItemKind =
CompletionItemKind.Folder

Expand All @@ -216,6 +243,7 @@ object CompletionValue:
isWorkspace: Boolean = false,
isExtension: Boolean = false
) extends Symbolic:
override def completionItemDataKind: Integer = CompletionSource.InterpolatorKind.ordinal
override def description(
printer: ShortenedTypePrinter
)(using Context): String =
Expand All @@ -229,6 +257,7 @@ object CompletionValue:
override val additionalEdits: List[TextEdit],
desc: String
) extends CompletionValue:
override def completionItemDataKind: Integer = CompletionSource.MatchCompletionKind.ordinal
override def completionItemKind(using Context): CompletionItemKind =
CompletionItemKind.Enum
override def description(printer: ShortenedTypePrinter)(using Context): String =
Expand All @@ -242,6 +271,7 @@ object CompletionValue:
override val range: Option[Range] = None,
override val command: Option[String] = None
) extends Symbolic:
override def completionItemDataKind: Integer = CompletionSource.CaseKeywordKind.ordinal
override def completionItemKind(using Context): CompletionItemKind =
CompletionItemKind.Method

Expand All @@ -254,6 +284,7 @@ object CompletionValue:
override def filterText: Option[String] = Some(description)

override def insertText: Option[String] = Some(doc)
override def completionItemDataKind: Integer = CompletionSource.DocumentKind.ordinal
override def completionItemKind(using Context): CompletionItemKind =
CompletionItemKind.Snippet

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import scala.meta.internal.metals.{CompilerOffsetParams, EmptyCancelToken}
import scala.meta.pc.CancelToken
import scala.language.unsafeNulls

import dotty.tools.pc.completions.CompletionSource
import dotty.tools.pc.utils.MtagsEnrichments.*
import dotty.tools.pc.utils.{TestCompletions, TextEdits}

Expand Down Expand Up @@ -172,7 +173,7 @@ abstract class BaseCompletionSuite extends BasePCSuite:
}
.mkString("\n")

assertCompletions(expected, obtained, Some(original))
assertWithDiff(expected, obtained, includeSources = false, Some(original))

/**
* Check completions that will be shown in original param after `@@` marker
Expand Down Expand Up @@ -245,13 +246,19 @@ abstract class BaseCompletionSuite extends BasePCSuite:
.append(commitCharacter)
.append("\n")
}
val expectedResult = sortLines(stableOrder, expected)
val actualResult = sortLines(
val completionSources = filteredItems
.map(_.data.map(data => CompletionSource.fromOrdinal(data.kind))
.getOrElse(CompletionSource.Empty))
.toList

val (expectedResult, _) = sortLines(stableOrder, expected)
val (actualResult, sources) = sortLines(
stableOrder,
postProcessObtained(trimTrailingSpace(out.toString()))
postProcessObtained(trimTrailingSpace(out.toString())),
completionSources,
)

assertCompletions(expectedResult, actualResult, Some(original))
assertWithDiff(expectedResult, actualResult, includeSources = true, Some(original), sources)

if (filterText.nonEmpty) {
filteredItems.foreach { item =>
Expand Down
10 changes: 7 additions & 3 deletions presentation-compiler/test/dotty/tools/pc/base/BasePCSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import scala.meta.pc.{PresentationCompiler, PresentationCompilerConfig}
import scala.language.unsafeNulls

import dotty.tools.pc.*
import dotty.tools.pc.completions.CompletionSource
import dotty.tools.pc.ScalaPresentationCompiler
import dotty.tools.pc.tests.buildinfo.BuildInfo
import dotty.tools.pc.utils._
Expand Down Expand Up @@ -113,10 +114,13 @@ abstract class BasePCSuite extends PcAssertions:
" " + e.getRight.getValue
}.trim

def sortLines(stableOrder: Boolean, string: String): String =
def sortLines(stableOrder: Boolean, string: String, completionSources: List[CompletionSource] = Nil): (String, List[CompletionSource]) =
val strippedString = string.linesIterator.toList.filter(_.nonEmpty)
if (stableOrder) strippedString.mkString("\n")
else strippedString.sorted.mkString("\n")
if (stableOrder) strippedString.mkString("\n") -> completionSources
else
val paddedSources = completionSources.padTo(strippedString.size, CompletionSource.Empty)
val (sortedCompletions, sortedSources) = (strippedString zip paddedSources).sortBy(_._1).unzip
sortedCompletions.mkString("\n") -> sortedSources

extension (s: String)
def triplequoted: String = s.replace("'''", "\"\"\"")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,6 @@ abstract class BaseSignatureHelpSuite extends BasePCSuite:
}
}

val obtainedSorted = sortLines(stableOrder, out.toString())
val expectedSorted = sortLines(stableOrder, expected)
assertCompletions(expectedSorted, obtainedSorted, Some(original))
val (obtainedSorted, _) = sortLines(stableOrder, out.toString())
val (expectedSorted, _) = sortLines(stableOrder, expected)
assertWithDiff(expectedSorted, obtainedSorted, includeSources = false, Some(original))
61 changes: 44 additions & 17 deletions presentation-compiler/test/dotty/tools/pc/utils/PcAssertions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dotty.tools.pc.utils

import scala.language.unsafeNulls

import dotty.tools.pc.completions.CompletionSource
import dotty.tools.dotc.util.DiffUtil
import dotty.tools.pc.utils.MtagsEnrichments.*

Expand All @@ -10,20 +11,22 @@ import org.hamcrest.*

trait PcAssertions:

def assertCompletions(
def assertWithDiff(
expected: String,
actual: String,
snippet: Option[String] = None
includeSources: Boolean,
snippet: Option[String] = None,
completionSources: List[CompletionSource] = Nil,
): Unit =
val longestExpeceted =
expected.linesIterator.maxByOption(_.length).map(_.length).getOrElse(0)
expected.linesIterator.map(_.length).maxOption.getOrElse(0)
val longestActual =
actual.linesIterator.maxByOption(_.length).map(_.length).getOrElse(0)
actual.linesIterator.map(_.length).maxOption.getOrElse(0)

val actualMatcher =
if longestActual >= 40 || longestExpeceted >= 40 then
lineByLineDiffMatcher(expected)
else sideBySideDiffMatcher(expected)
lineByLineDiffMatcher(expected, completionSources, includeSources)
else sideBySideDiffMatcher(expected, completionSources, includeSources)

assertThat(actual, actualMatcher, snippet)

Expand Down Expand Up @@ -115,27 +118,51 @@ trait PcAssertions:
error.setStackTrace(Array.empty)
throw error

private def lineByLineDiffMatcher(expected: String): TypeSafeMatcher[String] =

private def lineByLineDiffMatcher(
expected: String,
completionSources: List[CompletionSource] = Nil,
isCompletion: Boolean = false
): TypeSafeMatcher[String] =
def getDetailedMessage(diff: String): String =
val lines = diff.linesIterator.toList
val sources = completionSources.padTo(lines.size, CompletionSource.Empty)
val maxLength = lines.map(_.length).maxOption.getOrElse(0)
var redLineIndex = 0
lines.map: line =>
if line.startsWith(Console.BOLD + Console.RED) then
redLineIndex = redLineIndex + 1
s"$line | [${sources(redLineIndex - 1)}]"
else
line
.mkString("\n")

new TypeSafeMatcher[String]:

override def describeMismatchSafely(
item: String,
mismatchDescription: org.hamcrest.Description
): Unit =
val diff = DiffUtil.mkColoredHorizontalLineDiff(unifyNewlines(expected), unifyNewlines(item))
val maybeEnhancedDiff = if isCompletion then getDetailedMessage(diff) else diff
mismatchDescription.appendText(System.lineSeparator)
mismatchDescription.appendText(
DiffUtil.mkColoredHorizontalLineDiff(
unifyNewlines(expected),
unifyNewlines(item)
)
)
mismatchDescription.appendText(maybeEnhancedDiff)
mismatchDescription.appendText(System.lineSeparator)

override def describeTo(description: org.hamcrest.Description): Unit = ()
override def matchesSafely(item: String): Boolean =
unifyNewlines(expected) == unifyNewlines(item)

private def sideBySideDiffMatcher(expected: String): TypeSafeMatcher[String] =
private def sideBySideDiffMatcher(
expected: String,
completionSources: List[CompletionSource] = Nil,
isCompletion: Boolean = false
): TypeSafeMatcher[String] =
def getDetailedMessage(diff: String): String =
val lines = diff.linesIterator.toList
val sources = completionSources.padTo(lines.size, CompletionSource.Empty)
(lines zip sources).map((line, source) => s"$line | [$source]").mkString("\n")

new TypeSafeMatcher[String]:

override def describeMismatchSafely(
Expand All @@ -147,10 +174,10 @@ trait PcAssertions:

val expectedLines = cleanedExpected.linesIterator.toSeq
val actualLines = cleanedActual.linesIterator.toSeq
val diff = DiffUtil.mkColoredLineDiff(expectedLines, actualLines)
val maybeEnhancedDiff = if isCompletion then getDetailedMessage(diff) else diff

mismatchDescription.appendText(
DiffUtil.mkColoredLineDiff(expectedLines, actualLines)
)
mismatchDescription.appendText(maybeEnhancedDiff)
mismatchDescription.appendText(System.lineSeparator)

override def describeTo(description: org.hamcrest.Description): Unit = ()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@ class TestingWorkspaceSearch(classpath: Seq[String]):

val nioPath = Paths.get(path)
val uri = nioPath.toUri()
val symbols =
DefSymbolCollector(
driver,
CompilerVirtualFileParams(uri, text)
).namedDefSymbols
val symbols = DefSymbolCollector(driver, CompilerVirtualFileParams(uri, text)).namedDefSymbols

// We have to map symbol from this Context, to one in PresentationCompiler
// To do it we are searching it with semanticdb symbol
Expand Down

0 comments on commit fcd7562

Please sign in to comment.