diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index c086491d9bb0..c33d276cbec0 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -181,7 +181,7 @@ object Contexts { val local = incCallback local != null && local.enabled || forceRun - /** The Zinc compile progress callback implementation if we are run from Zinc, null otherwise */ + /** The Zinc compile progress callback implementation if we are run from Zinc or used by presentation compiler, null otherwise */ def progressCallback: ProgressCallback | Null = store(progressCallbackLoc) /** Run `op` if there exists a Zinc progress callback */ diff --git a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala index 40c6667fdd24..57c0c2fd3693 100644 --- a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala +++ b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala @@ -14,6 +14,7 @@ import java.util.zip.* import scala.collection.* import scala.io.Codec +import dotty.tools.dotc.sbt.interfaces.ProgressCallback import dotty.tools.io.AbstractFile import ast.{Trees, tpd} @@ -30,6 +31,9 @@ class InteractiveDriver(val settings: List[String]) extends Driver { override def sourcesRequired: Boolean = false + private var myProgressCallback: ProgressCallback = new ProgressCallback: + override def isCancelled(): Boolean = Thread.interrupted() + private val myInitCtx: Context = { val rootCtx = initCtx.fresh.addMode(Mode.ReadPositions).addMode(Mode.Interactive) rootCtx.setSetting(rootCtx.settings.YretainTrees, true) @@ -151,7 +155,7 @@ class InteractiveDriver(val settings: List[String]) extends Driver { val reporter = new StoreReporter(null) with UniqueMessagePositions with HideNonSensicalMessages - val run = compiler.newRun(using myInitCtx.fresh.setReporter(reporter)) + val run = compiler.newRun(using myInitCtx.fresh.setReporter(reporter).setProgressCallback(myProgressCallback)) myCtx = run.runContext.withRootImports given Context = myCtx @@ -169,8 +173,7 @@ class InteractiveDriver(val settings: List[String]) extends Driver { myCtx = myCtx.fresh.setPhase(myInitCtx.base.typerPhase) reporter.removeBufferedMessages - } - catch { + } catch { case ex: FatalError => myCtx = previousCtx close(uri) diff --git a/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala b/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala index f5715c2780a9..37c2bd891086 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala @@ -29,7 +29,8 @@ import scala.compiletime.uninitialized */ class CachingDriver(override val settings: List[String]) extends InteractiveDriver(settings): - @volatile private var lastCompiledURI: URI = uninitialized + private var lastCompiledURI: URI = uninitialized + private var previousDiags = List.empty[Diagnostic] private def alreadyCompiled(uri: URI, content: Array[Char]): Boolean = compilationUnits.get(uri) match @@ -40,17 +41,8 @@ class CachingDriver(override val settings: List[String]) extends InteractiveDriv case _ => false override def run(uri: URI, source: SourceFile): List[Diagnostic] = - val diags = - if alreadyCompiled(uri, source.content) then Nil - else super.run(uri, source) + if !alreadyCompiled(uri, source.content) then previousDiags = super.run(uri, source) lastCompiledURI = uri - diags - - override def run(uri: URI, sourceCode: String): List[Diagnostic] = - val diags = - if alreadyCompiled(uri, sourceCode.toCharArray().nn) then Nil - else super.run(uri, sourceCode) - lastCompiledURI = uri - diags + previousDiags end CachingDriver diff --git a/presentation-compiler/src/main/dotty/tools/pc/DiagnosticProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/DiagnosticProvider.scala new file mode 100644 index 000000000000..bd00114f65ce --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/DiagnosticProvider.scala @@ -0,0 +1,65 @@ +package dotty.tools.pc + +import org.eclipse.lsp4j +import org.eclipse.lsp4j.DiagnosticSeverity +import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.dotc.interfaces.Diagnostic as DiagnosticInterfaces +import dotty.tools.dotc.reporting.Diagnostic +import dotty.tools.pc.utils.InteractiveEnrichments.toLsp + +import scala.meta.pc.VirtualFileParams +import ch.epfl.scala.bsp4j +import dotty.tools.dotc.reporting.CodeAction +import dotty.tools.dotc.rewrites.Rewrites.ActionPatch +import scala.jdk.CollectionConverters.* +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.reporting.ErrorMessageID +import org.eclipse.lsp4j.DiagnosticTag + +class DiagnosticProvider(driver: InteractiveDriver, params: VirtualFileParams): + + def diagnostics(): List[lsp4j.Diagnostic] = + val diags = driver.run(params.uri().nn, params.text().nn) + given Context = driver.currentCtx + diags.flatMap(toLsp) + + private def toLsp(diag: Diagnostic)(using Context): Option[lsp4j.Diagnostic] = + Option.when(diag.pos.exists): + val lspDiag = lsp4j.Diagnostic( + diag.pos.toLsp, + diag.msg.message, + toDiagnosticSeverity(diag.level), + "presentation compiler", + ) + lspDiag.setCode(diag.msg.errorId.errorNumber) + + val scalaDiagnostic = new bsp4j.ScalaDiagnostic() + val actions = diag.msg.actions.map(toBspScalaAction).asJava + scalaDiagnostic.setActions(actions) + // lspDiag.setRelatedInformation(???) Currently not emitted by the compiler + lspDiag.setData(scalaDiagnostic) + if diag.msg.errorId == ErrorMessageID.UnusedSymbolID then + lspDiag.setTags(List(DiagnosticTag.Unnecessary).asJava) + + lspDiag + + private def toBspScalaAction(action: CodeAction): bsp4j.ScalaAction = + val bspAction = bsp4j.ScalaAction(action.title) + action.description.foreach(bspAction.setDescription) + val workspaceEdit = bsp4j.ScalaWorkspaceEdit(action.patches.map(toBspTextEdit).asJava) + bspAction.setEdit(workspaceEdit) + bspAction + + private def toBspTextEdit(actionPatch: ActionPatch): bsp4j.ScalaTextEdit = + val startPos = bsp4j.Position(actionPatch.srcPos.startLine, actionPatch.srcPos.startColumn) + val endPos = bsp4j.Position(actionPatch.srcPos.endLine, actionPatch.srcPos.endColumn) + val range = bsp4j.Range(startPos, endPos) + bsp4j.ScalaTextEdit(range, actionPatch.replacement) + + + private def toDiagnosticSeverity(severity: Int): DiagnosticSeverity = + severity match + case DiagnosticInterfaces.ERROR => DiagnosticSeverity.Error + case DiagnosticInterfaces.WARNING => DiagnosticSeverity.Warning + case DiagnosticInterfaces.INFO => DiagnosticSeverity.Information + case _ => DiagnosticSeverity.Information diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala index 71aa1626bb18..831e5ba181f7 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala @@ -32,6 +32,7 @@ import dotty.tools.dotc.util.Spans.Span import org.eclipse.lsp4j.InlayHint import org.eclipse.lsp4j.InlayHintKind import org.eclipse.{lsp4j as l} +import scala.meta.internal.pc.InlayHintOrigin class PcInlayHintsProvider( driver: InteractiveDriver, @@ -80,7 +81,7 @@ class PcInlayHintsProvider( ) case _ => inlayHints } - + tree match case ImplicitConversion(symbol, range) => val adjusted = adjustPos(range) @@ -89,11 +90,13 @@ class PcInlayHintsProvider( adjusted.startPos.toLsp, labelPart(symbol, symbol.decodedName) :: LabelPart("(") :: Nil, InlayHintKind.Parameter, + InlayHintOrigin.ImplicitConversion ) .add( adjusted.endPos.toLsp, LabelPart(")") :: Nil, InlayHintKind.Parameter, + InlayHintOrigin.ImplicitConversion ) case ImplicitParameters(trees, pos) => firstPassHints.add( @@ -104,12 +107,14 @@ class PcInlayHintsProvider( case None => LabelPart(label) ), InlayHintKind.Parameter, + InlayHintOrigin.ImplicitParameters ) case ValueOf(label, pos) => firstPassHints.add( adjustPos(pos).toLsp, LabelPart("(") :: LabelPart(label) :: List(LabelPart(")")), InlayHintKind.Parameter, + InlayHintOrigin.ImplicitParameters ) case TypeParameters(tpes, pos, sel) if !syntheticTupleApply(sel) => @@ -118,19 +123,18 @@ class PcInlayHintsProvider( adjustPos(pos).endPos.toLsp, label, InlayHintKind.Type, + InlayHintOrigin.TypeParameters ) case InferredType(tpe, pos, defTree) if !isErrorTpe(tpe) => val adjustedPos = adjustPos(pos).endPos - if firstPassHints.containsDef(adjustedPos.start) then firstPassHints - else - firstPassHints - .add( - adjustedPos.toLsp, - LabelPart(": ") :: toLabelParts(tpe, pos), - InlayHintKind.Type, - ) - .addDefinition(adjustedPos.start) + firstPassHints + .add( + adjustedPos.toLsp, + LabelPart(": ") :: toLabelParts(tpe, pos), + InlayHintKind.Type, + InlayHintOrigin.InferredType + ) case Parameters(isInfixFun, args) => def isNamedParam(pos: SourcePosition): Boolean = val start = text.indexWhere(!_.isWhitespace, pos.start) @@ -167,6 +171,7 @@ class PcInlayHintsProvider( hintPos.startPos.toLsp, List(LabelPart(labelStr)), InlayHintKind.Parameter, + if params.byNameParameters then InlayHintOrigin.ByNameParameters else InlayHintOrigin.NamedParameters ) else ih } @@ -515,7 +520,7 @@ object XRayModeHint: case Some(sel: Select) if sel.isForComprehensionMethod => false case Some(par) if par.sourcePos.exists && par.sourcePos.line == tree.sourcePos.line => true case _ => false - + tree match /* anotherTree @@ -530,7 +535,7 @@ object XRayModeHint: .select */ case select @ Select(innerTree, _) - if innerTree.sourcePos.exists && !isParentOnSameLine && !isParentApply && + if innerTree.sourcePos.exists && !isParentOnSameLine && !isParentApply && isEndOfLine(tree.sourcePos) => Some((select.tpe.widen.deepDealiasAndSimplify, tree.sourcePos)) case _ => None diff --git a/presentation-compiler/src/main/dotty/tools/pc/RawScalaPresentationCompiler.scala b/presentation-compiler/src/main/dotty/tools/pc/RawScalaPresentationCompiler.scala new file mode 100644 index 000000000000..d422db9a9da7 --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/RawScalaPresentationCompiler.scala @@ -0,0 +1,340 @@ +package dotty.tools.pc + +import java.io.File +import java.net.URI +import java.nio.file.Path +import java.util.Optional +import java.util as ju + +import scala.jdk.CollectionConverters._ +import scala.language.unsafeNulls +import scala.meta.internal.metals.CompilerVirtualFileParams +import scala.meta.pc.reports.EmptyReportContext +import scala.meta.internal.metals.PcQueryContext +import scala.meta.pc.reports.ReportContext +import scala.meta.internal.metals.ReportLevel +import scala.meta.internal.mtags.CommonMtagsEnrichments.* +import scala.meta.internal.pc.EmptySymbolSearch +import scala.meta.internal.pc.PresentationCompilerConfigImpl +import scala.meta.pc.* +import scala.meta.pc.{PcSymbolInformation as IPcSymbolInformation} + +import dotty.tools.pc.completions.CompletionProvider +import dotty.tools.pc.InferExpectedType +import dotty.tools.pc.completions.OverrideCompletions +import dotty.tools.pc.buildinfo.BuildInfo +import dotty.tools.pc.SymbolInformationProvider +import dotty.tools.dotc.interactive.InteractiveDriver + +import org.eclipse.lsp4j.DocumentHighlight +import org.eclipse.lsp4j.TextEdit +import org.eclipse.lsp4j as l + + +case class RawScalaPresentationCompiler( + buildTargetIdentifier: String = "", + buildTargetName: Option[String] = None, + classpath: Seq[Path] = Nil, + options: List[String] = Nil, + search: SymbolSearch = EmptySymbolSearch, + config: PresentationCompilerConfig = PresentationCompilerConfigImpl(), + folderPath: Option[Path] = None, + reportsLevel: ReportLevel = ReportLevel.Info, + completionItemPriority: CompletionItemPriority = (_: String) => 0, + reportContext: ReportContext = EmptyReportContext() +) extends RawPresentationCompiler: + + def this() = this("uninitialized-presentation-compiler") + + given ReportContext = reportContext + + override def supportedCodeActions(): ju.List[String] = List( + CodeActionId.ConvertToNamedArguments, + CodeActionId.ImplementAbstractMembers, + CodeActionId.ExtractMethod, + CodeActionId.InlineValue, + CodeActionId.InsertInferredType, + CodeActionId.InsertInferredMethod, + PcConvertToNamedLambdaParameters.codeActionId + ).asJava + + override val scalaVersion = BuildInfo.scalaVersion + + private val forbiddenOptions = Set("-print-lines", "-print-tasty") + private val forbiddenDoubleOptions = Set.empty[String] + + val driverSettings = + val implicitSuggestionTimeout = List("-Ximport-suggestion-timeout", "0") + val defaultFlags = List("-color:never") + val filteredOptions = removeDoubleOptions(options.filterNot(forbiddenOptions)) + + filteredOptions ::: defaultFlags ::: implicitSuggestionTimeout ::: "-classpath" :: classpath + .mkString(File.pathSeparator) :: Nil + + lazy val driver: InteractiveDriver = CachingDriver(driverSettings) + + override def codeAction[T]( + params: OffsetParams, + codeActionId: String, + codeActionPayload: Optional[T] + ): ju.List[TextEdit] = + (codeActionId, codeActionPayload.asScala) match + case ( + CodeActionId.ConvertToNamedArguments, + Some(argIndices: ju.List[_]) + ) => + val payload = argIndices.asScala.collect { case i: Integer => i.toInt }.toSet + convertToNamedArguments(params, payload) + case (CodeActionId.ImplementAbstractMembers, _) => + implementAbstractMembers(params) + case (CodeActionId.InsertInferredType, _) => + insertInferredType(params) + case (CodeActionId.InsertInferredMethod, _) => + insertInferredMethod(params) + case (CodeActionId.InlineValue, _) => + inlineValue(params) + case (CodeActionId.ExtractMethod, Some(extractionPos: OffsetParams)) => + params match { + case range: RangeParams => + extractMethod(range, extractionPos) + case _ => throw new IllegalArgumentException(s"Expected range parameters") + } + case (PcConvertToNamedLambdaParameters.codeActionId, _) => + PcConvertToNamedLambdaParameters(driver, params).convertToNamedLambdaParameters + case (id, _) => throw new IllegalArgumentException(s"Unsupported action id $id") + + override def withCompletionItemPriority( + priority: CompletionItemPriority + ): RawPresentationCompiler = + copy(completionItemPriority = priority) + + override def withBuildTargetName(buildTargetName: String): RawPresentationCompiler = + copy(buildTargetName = Some(buildTargetName)) + + override def withReportsLoggerLevel(level: String): RawPresentationCompiler = + copy(reportsLevel = ReportLevel.fromString(level)) + + private def removeDoubleOptions(options: List[String]): List[String] = + options match + case head :: _ :: tail if forbiddenDoubleOptions(head) => + removeDoubleOptions(tail) + case head :: tail => head :: removeDoubleOptions(tail) + case Nil => options + + override def semanticTokens( + params: VirtualFileParams + ): ju.List[Node] = + PcSemanticTokensProvider(driver, params).provide().asJava + + override def inlayHints( + params: InlayHintsParams + ): ju.List[l.InlayHint] = + PcInlayHintsProvider(driver, params, search) + .provide() + .asJava + + override def getTasty( + targetUri: URI, + isHttpEnabled: Boolean + ): String = + TastyUtils.getTasty(targetUri, isHttpEnabled) + + override def complete(params: OffsetParams, completionTriggerKind: l.CompletionTriggerKind): l.CompletionList = + CompletionProvider( + search, + driver, + () => InteractiveDriver(driverSettings), + params, + config, + buildTargetIdentifier, + folderPath, + completionItemPriority + ).completions() + + def definition(params: OffsetParams): DefinitionResult = + PcDefinitionProvider(driver, params, search).definitions() + + override def typeDefinition( + params: OffsetParams + ): DefinitionResult = + PcDefinitionProvider(driver, params, search).typeDefinitions() + + override def documentHighlight( + params: OffsetParams + ): ju.List[DocumentHighlight] = + PcDocumentHighlightProvider(driver, params).highlights.asJava + + override def references( + params: ReferencesRequest + ): ju.List[ReferencesResult] = + PcReferencesProvider(driver, params) + .references() + .asJava + + def inferExpectedType(params: OffsetParams): ju.Optional[String] = + InferExpectedType(search, driver, params).infer().asJava + + override def info( + symbol: String + ): Optional[IPcSymbolInformation] = + SymbolInformationProvider(using driver.currentCtx) + .info(symbol) + .map(_.asJava) + .asJava + + override def semanticdbTextDocument( + filename: URI, + code: String + ): Array[Byte] = + val provider = SemanticdbTextDocumentProvider(driver, folderPath) + provider.textDocument(filename, code) + + override def completionItemResolve( + item: l.CompletionItem, + symbol: String + ): l.CompletionItem = + CompletionItemResolver.resolve(item, symbol, search, config)(using driver.currentCtx) // FIX ME + + override def autoImports( + name: String, + params: scala.meta.pc.OffsetParams, + isExtension: java.lang.Boolean + ): ju.List[scala.meta.pc.AutoImportsResult] = + AutoImportsProvider( + search, + driver, + name, + params, + config, + buildTargetIdentifier + ) + .autoImports(isExtension) + .asJava + + override def implementAbstractMembers( + params: OffsetParams + ): ju.List[l.TextEdit] = + OverrideCompletions.implementAllAt( + params, + driver, + search, + config + ) + + override def insertInferredType( + params: OffsetParams + ): ju.List[l.TextEdit] = + InferredTypeProvider(params, driver, config, search) + .inferredTypeEdits() + .asJava + + private def insertInferredMethod( + params: OffsetParams + ): ju.List[l.TextEdit] = + InferredMethodProvider(params, driver, config, search) + .inferredMethodEdits() + .asJava + + override def inlineValue( + params: OffsetParams + ): ju.List[l.TextEdit] = + PcInlineValueProvider(driver, params).getInlineTextEdits() match + case Right(edits: List[TextEdit]) => edits.asJava + case Left(error: String) => throw new DisplayableException(error) + + override def extractMethod( + range: RangeParams, + extractionPos: OffsetParams + ): ju.List[l.TextEdit] = + ExtractMethodProvider( + range, + extractionPos, + driver, + search, + options.contains("-no-indent"), + ) + .extractMethod() + .asJava + + override def convertToNamedArguments( + params: OffsetParams, + argIndices: ju.List[Integer] + ): ju.List[l.TextEdit] = + convertToNamedArguments(params, argIndices.asScala.toSet.map(_.toInt)) + + def convertToNamedArguments( + params: OffsetParams, + argIndices: Set[Int] + ): ju.List[l.TextEdit] = + ConvertToNamedArgumentsProvider( + driver, + params, + argIndices + ).convertToNamedArguments match + case Left(error: String) => throw new DisplayableException(error) + case Right(edits: List[l.TextEdit]) => edits.asJava + + override def selectionRange( + params: ju.List[OffsetParams] + ): ju.List[l.SelectionRange] = + SelectionRangeProvider( + driver, + params, + ).selectionRange().asJava + + override def hover( + params: OffsetParams + ): ju.Optional[HoverSignature] = + HoverProvider.hover(params, driver, search, config.hoverContentType()) + + override def prepareRename( + params: OffsetParams + ): ju.Optional[l.Range] = + PcRenameProvider(driver, params, None).prepareRename().asJava + + override def rename( + params: OffsetParams, + name: String + ): ju.List[l.TextEdit] = + PcRenameProvider(driver, params, Some(name)).rename().asJava + + override def newInstance( + buildTargetIdentifier: String, + classpath: ju.List[Path], + options: ju.List[String] + ): RawPresentationCompiler = + copy( + buildTargetIdentifier = buildTargetIdentifier, + classpath = classpath.asScala.toSeq, + options = options.asScala.toList + ) + + override def signatureHelp(params: OffsetParams): l.SignatureHelp = + SignatureHelpProvider.signatureHelp(driver, params, search) + + override def didChange(params: VirtualFileParams): ju.List[l.Diagnostic] = + DiagnosticProvider(driver, params).diagnostics().asJava + + override def didClose(uri: URI): Unit = + driver.close(uri) + + override def semanticdbTextDocument(params: VirtualFileParams): Array[Byte] = + semanticdbTextDocument(params.uri, params.text) + + override def buildTargetId(): String = buildTargetIdentifier + + override def withConfiguration( + config: PresentationCompilerConfig + ): RawPresentationCompiler = + copy(config = config) + + override def withSearch(search: SymbolSearch): RawPresentationCompiler = + copy(search = search) + + override def withReportContext(reportContext: ReportContext): RawPresentationCompiler = + copy(reportContext = reportContext) + + override def withWorkspace(workspace: Path): RawPresentationCompiler = + copy(folderPath = Some(workspace)) + +end RawScalaPresentationCompiler diff --git a/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala b/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala index 431e4908013a..1ec734cec047 100644 --- a/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala @@ -38,7 +38,8 @@ class BaseInlayHintsSuite extends BasePCSuite { byNameParameters = true, implicitConversions = true, namedParameters = true, - hintsInPatternMatch = hintsInPatternMatch + hintsInPatternMatch = hintsInPatternMatch, + closingLabels = false ) val inlayHints = presentationCompiler diff --git a/presentation-compiler/test/dotty/tools/pc/tests/DiagnosticProviderSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/DiagnosticProviderSuite.scala new file mode 100644 index 000000000000..74a1d824bfaf --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/tests/DiagnosticProviderSuite.scala @@ -0,0 +1,60 @@ +package dotty.tools.pc.tests + +import java.net.URI +import scala.meta.internal.jdk.CollectionConverters.* +import scala.meta.internal.metals.CompilerVirtualFileParams +import scala.meta.internal.metals.EmptyCancelToken + +import org.junit.Test +import org.eclipse.lsp4j.DiagnosticSeverity +import dotty.tools.pc.utils.TestExtensions.getOffset +import dotty.tools.pc.base.TestResources +import java.nio.file.Path +import dotty.tools.pc.RawScalaPresentationCompiler +import dotty.tools.pc.utils.PcAssertions + +class DiagnosticProviderSuite extends PcAssertions { + case class TestDiagnostic(startIndex: Int, endIndex: Int, msg: String, severity: DiagnosticSeverity) + + val pc = RawScalaPresentationCompiler().newInstance("", TestResources.classpath.asJava, Nil.asJava) + + def check( + text: String, + expected: List[TestDiagnostic] + ): Unit = + val diagnostics = pc + .didChange(CompilerVirtualFileParams(URI.create("file:/Diagnostic.scala"), text, EmptyCancelToken)) + .asScala + + val actual = diagnostics.map(d => TestDiagnostic(d.getRange().getStart().getOffset(text), d.getRange().getEnd().getOffset(text), d.getMessage(), d.getSeverity())) + assertEquals(expected, actual, s"Expected [${expected.mkString(", ")}] but got [${actual.mkString(", ")}]") + + @Test def error = + check( + """|object M: + | Int.maaxValue + |""".stripMargin, + List(TestDiagnostic(12,25, "value maaxValue is not a member of object Int - did you mean Int.MaxValue?", DiagnosticSeverity.Error)) + ) + + @Test def warning = + check( + """|object M: + | 1 + 1 + |""".stripMargin, + List(TestDiagnostic(12, 17, "A pure expression does nothing in statement position", DiagnosticSeverity.Warning)) + ) + + @Test def mixed = + check( + """object M: + | Int.maaxValue + | 1 + 1 + |""".stripMargin, + List( + TestDiagnostic(12,25, "value maaxValue is not a member of object Int - did you mean Int.MaxValue?", DiagnosticSeverity.Error), + TestDiagnostic(28, 33, "A pure expression does nothing in statement position", DiagnosticSeverity.Warning) + ) + ) + +} diff --git a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala index daad8b974620..15cd370ed9e7 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala @@ -1069,7 +1069,7 @@ class InlayHintsSuite extends BaseInlayHintsSuite { """|def hello/*: (path : String<>, num : Int<>)*/ = (path = ".", num = 5)/*[(String<>, Int<>)]*/ | |def test/*: (path : String<>, num : Int<>, line : Int<>)*/ = - | hello ++/*[Tuple1<>["line"], Tuple1<>[Int<>]]*/ (line = 1)/*(using refl<>)*//*[Tuple1<>[Int<>]]*/ + | hello ++/*[Tuple1<>["line"], Tuple1<>[Int<>]]*/ (line = 1)/*[Tuple1<>[Int<>]]*//*(using refl<>)*/ | |@main def bla/*: Unit<>*/ = | val x: (path: String, num: Int, line: Int) = test diff --git a/project/Build.scala b/project/Build.scala index 9c4fbafbb330..a5744f3a6007 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -2826,15 +2826,17 @@ object Build { BuildInfoPlugin.buildInfoDefaultSettings lazy val presentationCompilerSettings = { - val mtagsVersion = "1.6.2" + val mtagsVersion = "1.6.2+105-e665821a-SNAPSHOT" Seq( libraryDependencies ++= Seq( "org.lz4" % "lz4-java" % "1.8.0", "io.get-coursier" % "interface" % "1.0.18", "org.scalameta" % "mtags-interfaces" % mtagsVersion, "com.google.guava" % "guava" % "33.2.1-jre", + "ch.epfl.scala" % "bsp4j" % "2.1.1", ), libraryDependencies += ("org.scalameta" % "mtags-shared_2.13.16" % mtagsVersion % SourceDeps), + resolvers += Resolver.sonatypeCentralSnapshots, ivyConfigurations += SourceDeps.hide, transitiveClassifiers := Seq("sources"), scalacOptions ++= Seq("-source", "3.3"), // To avoid fatal migration warnings