diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcSyntheticDecorationProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala similarity index 63% rename from presentation-compiler/src/main/dotty/tools/pc/PcSyntheticDecorationProvider.scala rename to presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala index d810ce5b07cc..3a17d30bc024 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcSyntheticDecorationProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala @@ -6,18 +6,18 @@ import java.nio.file.Paths import scala.meta.internal.metals.ReportContext import dotty.tools.pc.utils.MtagsEnrichments.* import dotty.tools.pc.printer.ShortenedTypePrinter +import scala.meta.internal.pc.InlayHints +import scala.meta.internal.pc.LabelPart +import scala.meta.internal.pc.LabelPart.* +import scala.meta.pc.InlayHintsParams import scala.meta.pc.SymbolSearch -import scala.meta.pc.SyntheticDecoration -import scala.meta.pc.SyntheticDecorationsParams -import scala.meta.internal.pc.DecorationKind -import scala.meta.internal.pc.Decoration - import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.StdNames.* +import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.interactive.InteractiveDriver @@ -26,9 +26,13 @@ import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.Spans.Span import dotty.tools.pc.IndexedContext -final class PcSyntheticDecorationsProvider( +import org.eclipse.lsp4j.InlayHint +import org.eclipse.lsp4j.InlayHintKind +import org.eclipse.{lsp4j as l} + +class PcInlayHintsProvider( driver: InteractiveDriver, - params: SyntheticDecorationsParams, + params: InlayHintsParams, symbolSearch: SymbolSearch, )(using ReportContext): @@ -43,75 +47,81 @@ final class PcSyntheticDecorationsProvider( given InferredType.Text = InferredType.Text(text) given ctx: Context = driver.currentCtx val unit = driver.currentCtx.run.nn.units.head + val pos = driver.sourcePosition(params) - def tpdTree = unit.tpdTree + def provide(): List[InlayHint] = + val deepFolder = DeepFolder[InlayHints](collectDecorations) + Interactive + .pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx) + .headOption + .getOrElse(unit.tpdTree) + .enclosedChildren(pos.span) + .flatMap(tpdTree => deepFolder(InlayHints.empty, tpdTree).result()) - def provide(): List[SyntheticDecoration] = - val deepFolder = DeepFolder[Synthetics](collectDecorations) - deepFolder(Synthetics.empty, tpdTree).result() + private def adjustPos(pos: SourcePosition): SourcePosition = + pos.adjust(text)._1 def collectDecorations( - decorations: Synthetics, + inlayHints: InlayHints, tree: Tree, - ): Synthetics = + ): InlayHints = tree match - case ImplicitConversion(name, range) if params.implicitConversions() => - val adjusted = range.adjust(text)._1 - decorations + case ImplicitConversion(symbol, range) if params.implicitConversions() => + val adjusted = adjustPos(range) + inlayHints .add( - Decoration( - adjusted.startPos.toLsp, - name + "(", - DecorationKind.ImplicitConversion, - ) + adjusted.startPos.toLsp, + labelPart(symbol, symbol.decodedName) :: LabelPart("(") :: Nil, + InlayHintKind.Parameter, ) .add( - Decoration( - adjusted.endPos.toLsp, - ")", - DecorationKind.ImplicitConversion, - ) + adjusted.endPos.toLsp, + LabelPart(")") :: Nil, + InlayHintKind.Parameter, ) - case ImplicitParameters(names, pos, allImplicit) + case ImplicitParameters(symbols, pos, allImplicit) if params.implicitParameters() => + val labelParts = symbols.map(s => List(labelPart(s, s.decodedName))) val label = - if allImplicit then names.mkString("(", ", ", ")") - else names.mkString(", ", ", ", "") - decorations.add( - Decoration( - pos.adjust(text)._1.toLsp, - label, - DecorationKind.ImplicitParameter, - ) + if allImplicit then labelParts.separated("(", ", ", ")") + else labelParts.separated(", ") + inlayHints.add( + adjustPos(pos).toLsp, + label, + InlayHintKind.Parameter, + ) + case ValueOf(label, pos) if params.implicitParameters() => + inlayHints.add( + adjustPos(pos).toLsp, + LabelPart("(") :: LabelPart(label) :: List(LabelPart(")")), + InlayHintKind.Parameter, ) case TypeParameters(tpes, pos, sel) if params.typeParameters() && !syntheticTupleApply(sel) => - val label = tpes.map(toLabel(_, pos)).mkString("[", ", ", "]") - decorations.add( - Decoration( - pos.adjust(text)._1.endPos.toLsp, - label, - DecorationKind.TypeParameter, - ) + val label = tpes.map(toLabelParts(_, pos)).separated("[", ", ", "]") + inlayHints.add( + adjustPos(pos).endPos.toLsp, + label, + InlayHintKind.Type, ) - case InferredType(tpe, pos, defTree) if params.inferredTypes() => - val adjustedPos = pos.adjust(text)._1.endPos - if decorations.containsDef(adjustedPos.start) then decorations + case InferredType(tpe, pos, defTree) + if params.inferredTypes() && !isErrorTpe(tpe) => + val adjustedPos = adjustPos(pos).endPos + if inlayHints.containsDef(adjustedPos.start) then inlayHints else - decorations.add( - Decoration( + inlayHints + .add( adjustedPos.toLsp, - ": " + toLabel(tpe, pos), - DecorationKind.InferredType, - ), - adjustedPos.start, - ) - case _ => decorations + LabelPart(": ") :: toLabelParts(tpe, pos), + InlayHintKind.Type, + ) + .addDefinition(adjustedPos.start) + case _ => inlayHints - private def toLabel( + private def toLabelParts( tpe: Type, pos: SourcePosition, - ): String = + ): List[LabelPart] = val tpdPath = Interactive.pathTo(unit.tpdTree, pos.span) @@ -129,13 +139,15 @@ final class PcSyntheticDecorationsProvider( case AppliedType(tycon, args) => isInScope(tycon) && args.forall(isInScope) case _ => true - if isInScope(tpe) - then tpe + if isInScope(tpe) then tpe else tpe.metalsDealias(using indexedCtx.ctx) val dealiased = optDealias(tpe) - printer.tpe(dealiased) - end toLabel + val tpeStr = printer.tpe(dealiased) + val usedRenames = printer.getUsedRenames + val parts = partsFromType(dealiased, usedRenames) + InlayHints.makeLabelParts(parts, tpeStr) + end toLabelParts private val definitions = IndexedContext(ctx).ctx.definitions private def syntheticTupleApply(tree: Tree): Boolean = @@ -152,7 +164,32 @@ final class PcSyntheticDecorationsProvider( case _ => true else false case _ => false -end PcSyntheticDecorationsProvider + + private def labelPart(symbol: Symbol, label: String) = + if symbol.source == pos.source then + LabelPart( + label, + pos = Some(symbol.sourcePos.toLsp.getStart().nn), + ) + else + LabelPart( + label, + symbol = SemanticdbSymbols.symbolName(symbol), + ) + + private def partsFromType( + tpe: Type, + usedRenames: Map[Symbol, String], + ): List[LabelPart] = + NamedPartsAccumulator(_ => true)(Nil, tpe) + .filter(_.symbol != NoSymbol) + .map { t => + val label = usedRenames.get(t.symbol).getOrElse(t.symbol.decodedName) + labelPart(t.symbol, label) + } + + private def isErrorTpe(tpe: Type): Boolean = tpe.isError +end PcInlayHintsProvider object ImplicitConversion: def unapply(tree: Tree)(using Context) = @@ -170,7 +207,7 @@ object ImplicitConversion: val lastArgPos = args.lastOption.map(_.sourcePos).getOrElse(fun.sourcePos) Some( - fun.symbol.decodedName, + fun.symbol, lastArgPos.withStart(fun.sourcePos.start), ) end ImplicitConversion @@ -183,23 +220,29 @@ object ImplicitParameters: val (implicitArgs, providedArgs) = args.partition(isSyntheticArg) val allImplicit = providedArgs.isEmpty val pos = implicitArgs.head.sourcePos - Some(implicitArgs.map(_.symbol.decodedName), pos, allImplicit) + Some(implicitArgs.map(_.symbol), pos, allImplicit) + case _ => None + + private def isSyntheticArg(tree: Tree)(using Context) = tree match + case tree: Ident => + tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit) + case _ => false +end ImplicitParameters + +object ValueOf: + def unapply(tree: Tree)(using Context) = + tree match case Apply(ta @ TypeApply(fun, _), _) if fun.span.isSynthetic && isValueOf(fun) => Some( - List("new " + tpnme.valueOf.decoded.capitalize + "(...)"), + "new " + tpnme.valueOf.decoded.capitalize + "(...)", fun.sourcePos, - true, ) case _ => None private def isValueOf(tree: Tree)(using Context) = val symbol = tree.symbol.maybeOwner symbol.name.decoded == tpnme.valueOf.decoded.capitalize - private def isSyntheticArg(tree: Tree)(using Context) = tree match - case tree: Ident => - tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit) - case _ => false -end ImplicitParameters +end ValueOf object TypeParameters: def unapply(tree: Tree)(using Context) = @@ -259,35 +302,7 @@ object InferredType: */ def isValDefBind(text: Text, vd: ValDef)(using Context) = val afterDef = text.drop(vd.nameSpan.end) - val index = afterDef.indexAfterSpacesAndComments + val index = indexAfterSpacesAndComments(afterDef) index >= 0 && index < afterDef.size && afterDef(index) == '@' end InferredType - -case class Synthetics( - decorations: List[Decoration], - definitions: Set[Int], -): - def containsDef(offset: Int) = definitions(offset) - def add(decoration: Decoration, offset: Int) = - copy( - decorations = addDecoration(decoration), - definitions = definitions + offset, - ) - def add(decoration: Decoration) = - copy ( - decorations = addDecoration(decoration) - ) - - // If method has both type parameter and implicit parameter, we want the type parameter decoration to be displayed first, - // but it's added second. This method adds the decoration to the right position in the list. - private def addDecoration(decoration: Decoration): List[Decoration] = - val atSamePos = - decorations.takeWhile(_.range.getStart() == decoration.range.getStart()) - (atSamePos :+ decoration) ++ decorations.drop(atSamePos.size) - - def result(): List[Decoration] = decorations.reverse -end Synthetics - -object Synthetics: - def empty: Synthetics = Synthetics(Nil, Set.empty) diff --git a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala index 87b67015f9e4..f3dc8d1658c4 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala @@ -106,15 +106,15 @@ case class ScalaPresentationCompiler( new PcSemanticTokensProvider(driver, params).provide().asJava } - override def syntheticDecorations( - params: SyntheticDecorationsParams - ): ju.concurrent.CompletableFuture[ju.List[SyntheticDecoration]] = + override def inlayHints( + params: InlayHintsParams + ): ju.concurrent.CompletableFuture[ju.List[l.InlayHint]] = compilerAccess.withInterruptableCompiler(Some(params))( - new ju.ArrayList[SyntheticDecoration](), + new ju.ArrayList[l.InlayHint](), params.token(), ) { access => val driver = access.compiler() - new PcSyntheticDecorationsProvider(driver, params, search) + new PcInlayHintsProvider(driver, params, search) .provide() .asJava } diff --git a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala index 3b763523f9e6..ed37e133372b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala @@ -66,6 +66,8 @@ class ShortenedTypePrinter( private val foundRenames = collection.mutable.LinkedHashMap.empty[Symbol, String] + def getUsedRenames: Map[Symbol, String] = foundRenames.toMap + def getUsedRenamesInfo(using Context): List[String] = foundRenames.map { (from, to) => s"type $to = ${from.showName}" diff --git a/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala b/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala index d6c8c10f8030..e4385392973f 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/utils/MtagsEnrichments.scala @@ -316,6 +316,24 @@ object MtagsEnrichments extends CommonMtagsEnrichments: .map(source.apply) .contains('.') case _ => false + + def children(using Context): List[Tree] = + val collector = new TreeAccumulator[List[Tree]]: + def apply(x: List[Tree], tree: Tree)(using Context): List[Tree] = + tree :: x + collector + .foldOver(Nil, tree) + .reverse + + /** + * Returns the children of the tree that overlap with the given span. + */ + def enclosedChildren(span: Span)(using Context): List[Tree] = + tree.children + .filter(tree => + tree.sourcePos.exists && tree.span.start <= span.end && tree.span.end >= span.start + ) + end enclosedChildren end extension extension (imp: Import) diff --git a/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala b/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala new file mode 100644 index 000000000000..94b00ca82aea --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala @@ -0,0 +1,56 @@ +package dotty.tools.pc.base + +import java.net.URI + +import scala.meta.internal.jdk.CollectionConverters._ +import scala.meta.internal.metals.CompilerInlayHintsParams +import scala.meta.internal.metals.CompilerRangeParams +import scala.language.unsafeNulls + +import dotty.tools.pc.utils.TestInlayHints +import dotty.tools.pc.utils.TextEdits + +import org.eclipse.lsp4j.TextEdit + +class BaseInlayHintsSuite extends BasePCSuite { + + def check( + base: String, + expected: String, + kind: Option[Int] = None, + ): Unit = + def pkgWrap(text: String) = + if (text.contains("package")) text + else s"package test\n$text" + + val withPkg = pkgWrap(base) + val rangeParams = CompilerRangeParams( + URI.create("file:/InlayHints.scala"), + withPkg, + 0, + withPkg.length() + ) + val pcParams = CompilerInlayHintsParams( + rangeParams, + true, + true, + true, + true + ) + + val inlayHints = presentationCompiler + .inlayHints( + pcParams + ) + .get() + .asScala + .toList + + val obtained = TestInlayHints.applyInlayHints(withPkg, inlayHints) + + assertNoDiff( + obtained, + pkgWrap(expected) + ) + +} \ No newline at end of file diff --git a/presentation-compiler/test/dotty/tools/pc/base/BaseSyntheticDecorationsSuite.scala b/presentation-compiler/test/dotty/tools/pc/base/BaseSyntheticDecorationsSuite.scala deleted file mode 100644 index 450a42f26153..000000000000 --- a/presentation-compiler/test/dotty/tools/pc/base/BaseSyntheticDecorationsSuite.scala +++ /dev/null @@ -1,60 +0,0 @@ -package dotty.tools.pc.base - -import java.net.URI - -import scala.meta.internal.jdk.CollectionConverters._ -import scala.meta.internal.metals.CompilerSyntheticDecorationsParams -import scala.meta.internal.metals.CompilerVirtualFileParams -import scala.language.unsafeNulls - -import dotty.tools.pc.utils.TextEdits - -import org.eclipse.lsp4j.TextEdit - -class BaseSyntheticDecorationsSuite extends BasePCSuite { - - def check( - base: String, - expected: String, - kind: Option[Int] = None, - ): Unit = - def pkgWrap(text: String) = - if (text.contains("package")) text - else s"package test\n$text" - - val withPkg = pkgWrap(base) - val vFile = CompilerVirtualFileParams( - URI.create("file:/Decorations.scala"), - withPkg, - ) - - val pcParams = CompilerSyntheticDecorationsParams( - vFile, - true, - true, - true, - true, - ) - - val allDecorations = presentationCompiler - .syntheticDecorations( - pcParams - ) - .get() - .asScala - .toList - - val decorations = kind match { - case Some(k) => allDecorations.filter(_.kind == k) - case _ => allDecorations - } - - val edits = decorations.map(d => new TextEdit(d.range(), d.label())) - val obtained = TextEdits.applyEdits(withPkg, edits) - - assertNoDiff( - obtained, - pkgWrap(expected), - ) - -} \ No newline at end of file diff --git a/presentation-compiler/test/dotty/tools/pc/tests/decorations/SyntheticDecorationsSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/decorations/SyntheticDecorationsSuite.scala deleted file mode 100644 index 49ffb911aaa3..000000000000 --- a/presentation-compiler/test/dotty/tools/pc/tests/decorations/SyntheticDecorationsSuite.scala +++ /dev/null @@ -1,528 +0,0 @@ -package dotty.tools.pc.tests.decorations - -import scala.meta.internal.pc.DecorationKind - -import dotty.tools.pc.base.BaseSyntheticDecorationsSuite - -import org.junit.Test - -class SyntheticDecorationsSuite extends BaseSyntheticDecorationsSuite: - - @Test def `type-params` = - check( - """|object Main { - | def hello[T](t: T) = t - | val x = hello(List(1)) - |} - |""".stripMargin, - """|object Main { - | def hello[T](t: T) = t - | val x = hello[List[Int]](List[Int](1)) - |} - |""".stripMargin, - kind = Some(DecorationKind.TypeParameter) - ) - - @Test def `type-params2` = - check( - """|object Main { - | def hello[T](t: T) = t - | val x = hello(Map((1,"abc"))) - |} - |""".stripMargin, - """|object Main { - | def hello[T](t: T) = t - | val x = hello[Map[Int, String]](Map[Int, String]((1,"abc"))) - |} - |""".stripMargin, - kind = Some(DecorationKind.TypeParameter) - ) - - @Test def `implicit-param` = - check( - """|case class User(name: String) - |object Main { - | implicit val imp: Int = 2 - | def addOne(x: Int)(implicit one: Int) = x + one - | val x = addOne(1) - |} - |""".stripMargin, - """|case class User(name: String) - |object Main { - | implicit val imp: Int = 2 - | def addOne(x: Int)(implicit one: Int) = x + one - | val x = addOne(1)(imp) - |} - |""".stripMargin, - kind = Some(DecorationKind.ImplicitParameter) - ) - - @Test def `implicit-conversion` = - check( - """|case class User(name: String) - |object Main { - | implicit def intToUser(x: Int): User = new User(x.toString) - | val y: User = 1 - |} - |""".stripMargin, - """|case class User(name: String) - |object Main { - | implicit def intToUser(x: Int): User = new User(x.toString) - | val y: User = intToUser(1) - |} - |""".stripMargin, - kind = Some(DecorationKind.ImplicitConversion) - ) - - @Test def `using-param` = - check( - """|case class User(name: String) - |object Main { - | implicit val imp: Int = 2 - | def addOne(x: Int)(using one: Int) = x + one - | val x = addOne(1) - |} - |""".stripMargin, - """|case class User(name: String) - |object Main { - | implicit val imp: Int = 2 - | def addOne(x: Int)(using one: Int) = x + one - | val x = addOne(1)(imp) - |} - |""".stripMargin, - kind = Some(DecorationKind.ImplicitParameter) - ) - - @Test def `given-conversion` = - check( - """|case class User(name: String) - |object Main { - | given intToUser: Conversion[Int, User] = User(_.toString) - | val y: User = 1 - |} - |""".stripMargin, - """|case class User(name: String) - |object Main { - | given intToUser: Conversion[Int, User] = User(_.toString) - | val y: User = intToUser(1) - |} - |""".stripMargin, - kind = Some(DecorationKind.ImplicitConversion) - ) - - @Test def `given-conversion2` = - check( - """|trait Xg: - | def doX: Int - |trait Yg: - | def doY: String - |given (using Xg): Yg with - | def doY = "7" - |""".stripMargin, - """|trait Xg: - | def doX: Int - |trait Yg: - | def doY: String - |given (using Xg): Yg with - | def doY: String = "7" - |""".stripMargin - ) - - @Test def `basic` = - check( - """|object Main { - | val foo = 123 - |} - |""".stripMargin, - """|object Main { - | val foo: Int = 123 - |} - |""".stripMargin - ) - - @Test def `list` = - check( - """|object Main { - | val foo = List[Int](123) - |} - |""".stripMargin, - """|object Main { - | val foo: List[Int] = List[Int](123) - |} - |""".stripMargin - ) - - @Test def `list2` = - check( - """|object O { - | def m = 1 :: List(1) - |} - |""".stripMargin, - """|object O { - | def m: List[Int] = 1 ::[Int] List[Int](1) - |} - |""".stripMargin - ) - - @Test def `two-param` = - check( - """|object Main { - | val foo = Map((1, "abc")) - |} - |""".stripMargin, - """|object Main { - | val foo: Map[Int, String] = Map[Int, String]((1, "abc")) - |} - |""".stripMargin - ) - - @Test def `tuple` = - check( - """|object Main { - | val foo = (123, 456) - |} - |""".stripMargin, - """|object Main { - | val foo: (Int, Int) = (123, 456) - |} - |""".stripMargin - ) - - @Test def `import-needed` = - check( - """|object Main { - | val foo = List[String]("").toBuffer[String] - |} - |""".stripMargin, - """|object Main { - | val foo: Buffer[String] = List[String]("").toBuffer[String] - |} - |""".stripMargin - ) - - @Test def `lambda-type` = - check( - """|object Main { - | val foo = () => 123 - |} - |""".stripMargin, - """|object Main { - | val foo: () => Int = () => 123 - |} - |""".stripMargin - ) - - @Test def `block` = - check( - """|object Main { - | val foo = { val z = 123; z + 2} - |} - |""".stripMargin, - """|object Main { - | val foo: Int = { val z: Int = 123; z + 2} - |} - |""".stripMargin - ) - - @Test def `refined-types` = - check( - """|object O{ - | trait Foo { - | type T - | type G - | } - | - | val c = new Foo { type T = Int; type G = Long} - |} - |""".stripMargin, - """|object O{ - | trait Foo { - | type T - | type G - | } - | - | val c: Foo{type T = Int; type G = Long} = new Foo { type T = Int; type G = Long} - |} - |""".stripMargin - ) - - @Test def `refined-types1` = - check( - """|object O{ - | trait Foo { - | type T - | } - | val c = new Foo { type T = Int } - | val d = c - |} - |""".stripMargin, - """|object O{ - | trait Foo { - | type T - | } - | val c: Foo{type T = Int} = new Foo { type T = Int } - | val d: Foo{type T = Int} = c - |} - |""".stripMargin - ) - - @Test def `refined-types4` = - check( - """|trait Foo extends Selectable { - | type T - |} - | - |val c = new Foo { - | type T = Int - | val x = 0 - | def y = 0 - | var z = 0 - |} - |""".stripMargin, - """|trait Foo extends Selectable { - | type T - |} - | - |val c: Foo{type T = Int; val x: Int; def y: Int; val z: Int; def z_=(x$1: Int): Unit} = new Foo { - | type T = Int - | val x: Int = 0 - | def y: Int = 0 - | var z: Int = 0 - |} - |""".stripMargin - ) - - @Test def `dealias` = - check( - """|class Foo() { - | type T = Int - | def getT: T = 1 - |} - | - |object O { - | val c = new Foo().getT - |} - |""".stripMargin, - """|class Foo() { - | type T = Int - | def getT: T = 1 - |} - | - |object O { - | val c: Int = new Foo().getT - |} - |""".stripMargin - ) - - @Test def `dealias2` = - check( - """|object Foo { - | type T = Int - | def getT: T = 1 - | val c = getT - |} - |""".stripMargin, - """|object Foo { - | type T = Int - | def getT: T = 1 - | val c: T = getT - |} - |""".stripMargin - ) - - @Test def `dealias3` = - check( - """|object Foo: - | opaque type T = Int - | def getT: T = 1 - |val c = Foo.getT - |""".stripMargin, - """|object Foo: - | opaque type T = Int - | def getT: T = 1 - |val c: T = Foo.getT - |""".stripMargin - ) - - @Test def `dealias4` = - check( - """|object O: - | type M = Int - | type W = M => Int - | def get: W = ??? - | - |val m = O.get - |""".stripMargin, - """|object O: - | type M = Int - | type W = M => Int - | def get: W = ??? - | - |val m: Int => Int = O.get - |""".stripMargin - ) - - @Test def `dealias5` = - check( - """|object O: - | opaque type M = Int - | type W = M => Int - | def get: W = ??? - | - |val m = O.get - |""".stripMargin, - """|object O: - | opaque type M = Int - | type W = M => Int - | def get: W = ??? - | - |val m: M => Int = O.get - |""".stripMargin - ) - - @Test def `explicit-tuple` = - check( - """|object Main { - | val x = Tuple2.apply(1, 2) - |} - |""".stripMargin, - """|object Main { - | val x: (Int, Int) = Tuple2.apply[Int, Int](1, 2) - |} - |""".stripMargin - ) - - @Test def `explicit-tuple1` = - check( - """|object Main { - | val x = Tuple2(1, 2) - |} - |""".stripMargin, - """|object Main { - | val x: (Int, Int) = Tuple2[Int, Int](1, 2) - |} - |""".stripMargin - ) - - @Test def `tuple-unapply` = - check( - """|object Main { - | val (fst, snd) = (1, 2) - |} - |""".stripMargin, - """|object Main { - | val (fst: Int, snd: Int) = (1, 2) - |} - |""".stripMargin - ) - - @Test def `list-unapply` = - check( - """|object Main { - | val hd :: tail = List(1, 2) - |} - |""".stripMargin, - """|object Main { - | val hd: Int ::[Int] tail: List[Int] = List[Int](1, 2) - |} - |""".stripMargin - ) - - @Test def `list-match` = - check( - """|object Main { - | val x = List(1, 2) match { - | case hd :: tail => hd - | } - |} - |""".stripMargin, - """|object Main { - | val x: Int = List[Int](1, 2) match { - | case hd: Int ::[Int] tail: List[Int] => hd - | } - |} - |""".stripMargin - ) - - @Test def `case-class-unapply` = - check( - """|object Main { - |case class Foo[A](x: A, y: A) - | val Foo(fst, snd) = Foo(1, 2) - |} - |""".stripMargin, - """|object Main { - |case class Foo[A](x: A, y: A) - | val Foo[Int](fst: Int, snd: Int) = Foo[Int](1, 2) - |} - |""".stripMargin - ) - - @Test def `valueOf` = - check( - """|object O { - | def foo[Total <: Int](implicit total: ValueOf[Total]): Int = total.value - | val m = foo[500] - |} - |""".stripMargin, - """|object O { - | def foo[Total <: Int](implicit total: ValueOf[Total]): Int = total.value - | val m: Int = foo[500](new ValueOf(...)) - |} - |""".stripMargin - ) - - @Test def `case-class1` = - check( - """|object O { - |case class A(x: Int, g: Int)(implicit y: String) - |} - |""".stripMargin, - """|object O { - |case class A(x: Int, g: Int)(implicit y: String) - |} - |""".stripMargin - ) - - @Test def `ord` = - check( - """ - |object Main { - | val ordered = "acb".sorted - |} - |""".stripMargin, - """ - |object Main { - | val ordered: String = augmentString("acb").sorted[Char](Char) - |} - |""".stripMargin - ) - - @Test def `val-def-with-bind` = - check( - """ - |object O { - | val tupleBound @ (one, two) = ("1", "2") - |} - |""".stripMargin, - """ - |object O { - | val tupleBound @ (one: String, two: String) = ("1", "2") - |} - |""".stripMargin - ) - - @Test def `val-def-with-bind-and-comment` = - check( - """ - |object O { - | val tupleBound /* comment */ @ (one, two) = ("1", "2") - |} - |""".stripMargin, - """ - |object O { - | val tupleBound /* comment */ @ (one: String, two: String) = ("1", "2") - |} - |""".stripMargin - ) - diff --git a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/SyntheticDecorationsSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/SyntheticDecorationsSuite.scala new file mode 100644 index 000000000000..0e32e5cf0682 --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/SyntheticDecorationsSuite.scala @@ -0,0 +1,667 @@ +package dotty.tools.pc.tests.inlayHints + +import dotty.tools.pc.base.BaseInlayHintsSuite + +import org.junit.Test +class InlayHintsSuite extends BaseInlayHintsSuite { + + @Test def `local` = + check( + """|object Main { + | def foo() = { + | implicit val imp: Int = 2 + | def addOne(x: Int)(implicit one: Int) = x + one + | val x = addOne(1) + | } + |} + |""".stripMargin, + """|object Main { + | def foo()/*: Unit<>*/ = { + | implicit val imp: Int = 2 + | def addOne(x: Int)(implicit one: Int)/*: Int<>*/ = x + one + | val x/*: Int<>*/ = addOne(1)/*(imp<<(3:17)>>)*/ + | } + |} + |""".stripMargin + ) + + @Test def `type-params` = + check( + """|object Main { + | def hello[T](t: T) = t + | val x = hello(List(1)) + |} + |""".stripMargin, + """|object Main { + | def hello[T](t: T)/*: T<<(2:12)>>*/ = t + | val x/*: List<>[Int<>]*/ = hello/*[List<>[Int<>]]*/(List/*[Int<>]*/(1)) + |} + |""".stripMargin + ) + + @Test def `type-params2` = + check( + """|object Main { + | def hello[T](t: T) = t + | val x = hello(Map((1,"abc"))) + |} + |""".stripMargin, + """|object Main { + | def hello[T](t: T)/*: T<<(2:12)>>*/ = t + | val x/*: Map<>[Int<>, String<>]*/ = hello/*[Map<>[Int<>, String<>]]*/(Map/*[Int<>, String<>]*/((1,"abc"))) + |} + |""".stripMargin, + ) + + @Test def `implicit-param` = + check( + """|case class User(name: String) + |object Main { + | implicit val imp: Int = 2 + | def addOne(x: Int)(implicit one: Int) = x + one + | val x = addOne(1) + |} + |""".stripMargin, + """|case class User(name: String) + |object Main { + | implicit val imp: Int = 2 + | def addOne(x: Int)(implicit one: Int)/*: Int<>*/ = x + one + | val x/*: Int<>*/ = addOne(1)/*(imp<<(3:15)>>)*/ + |} + |""".stripMargin + ) + + @Test def `implicit-conversion` = + check( + """|case class User(name: String) + |object Main { + | implicit def intToUser(x: Int): User = new User(x.toString) + | val y: User = 1 + |} + |""".stripMargin, + """|case class User(name: String) + |object Main { + | implicit def intToUser(x: Int): User = new User(x.toString) + | val y: User = /*intToUser<<(3:15)>>(*/1/*)*/ + |} + |""".stripMargin + ) + + @Test def `using-param` = + check( + """|case class User(name: String) + |object Main { + | implicit val imp: Int = 2 + | def addOne(x: Int)(using one: Int) = x + one + | val x = addOne(1) + |} + |""".stripMargin, + """|case class User(name: String) + |object Main { + | implicit val imp: Int = 2 + | def addOne(x: Int)(using one: Int)/*: Int<>*/ = x + one + | val x/*: Int<>*/ = addOne(1)/*(imp<<(3:15)>>)*/ + |} + |""".stripMargin + ) + + @Test def `given-conversion` = + check( + """|case class User(name: String) + |object Main { + | given intToUser: Conversion[Int, User] = User(_.toString) + | val y: User = 1 + |} + |""".stripMargin, + """|case class User(name: String) + |object Main { + | given intToUser: Conversion[Int, User] = User(_.toString) + | val y: User = /*intToUser<<(3:8)>>(*/1/*)*/ + |} + |""".stripMargin + ) + + @Test def `given-conversion2` = + check( + """|trait Xg: + | def doX: Int + |trait Yg: + | def doY: String + |given (using Xg): Yg with + | def doY = "7" + |""".stripMargin, + """|trait Xg: + | def doX: Int + |trait Yg: + | def doY: String + |given (using Xg): Yg with + | def doY/*: String<>*/ = "7" + |""".stripMargin + ) + + @Test def `basic` = + check( + """|object Main { + | val foo = 123 + |} + |""".stripMargin, + """|object Main { + | val foo/*: Int<>*/ = 123 + |} + |""".stripMargin + ) + + @Test def `list` = + check( + """|object Main { + | val foo = List[Int](123) + |} + |""".stripMargin, + """|object Main { + | val foo/*: List<>[Int<>]*/ = List[Int](123) + |} + |""".stripMargin + ) + + @Test def `list2` = + check( + """|object O { + | def m = 1 :: List(1) + |} + |""".stripMargin, + """|object O { + | def m/*: List<>[Int<>]*/ = 1 ::/*[Int<>]*/ List/*[Int<>]*/(1) + |} + |""".stripMargin + ) + + @Test def `two-param` = + check( + """|object Main { + | val foo = Map((1, "abc")) + |} + |""".stripMargin, + """|object Main { + | val foo/*: Map<>[Int<>, String<>]*/ = Map/*[Int<>, String<>]*/((1, "abc")) + |} + |""".stripMargin, + ) + + @Test def `tuple` = + check( + """|object Main { + | val foo = (123, 456) + |} + |""".stripMargin, + """|object Main { + | val foo/*: (Int<>, Int<>)*/ = (123, 456) + |} + |""".stripMargin + ) + + @Test def `import-needed` = + check( + """|object Main { + | val foo = List[String]("").toBuffer[String] + |} + |""".stripMargin, + """|object Main { + | val foo/*: Buffer<>[String<>]*/ = List[String]("").toBuffer[String] + |} + |""".stripMargin, + ) + + @Test def `lambda-type` = + check( + """|object Main { + | val foo = () => 123 + |} + |""".stripMargin, + """|object Main { + | val foo/*: () => Int<>*/ = () => 123 + |} + |""".stripMargin + ) + + @Test def `block` = + check( + """|object Main { + | val foo = { val z = 123; z + 2} + |} + |""".stripMargin, + """|object Main { + | val foo/*: Int<>*/ = { val z/*: Int<>*/ = 123; z + 2} + |} + |""".stripMargin + ) + + @Test def `refined-types` = + check( + """|object O{ + | trait Foo { + | type T + | type G + | } + | + | val c = new Foo { type T = Int; type G = Long} + |} + |""".stripMargin, + """|object O{ + | trait Foo { + | type T + | type G + | } + | + | val c/*: Foo<<(2:8)>>{type T = Int<>; type G = Long<>}*/ = new Foo { type T = Int; type G = Long} + |} + |""".stripMargin + ) + + @Test def `refined-types2` = + check( + """|object O{ + | trait Foo { + | type T + | } + | val c = new Foo { type T = Int } + | val d = c + |} + |""".stripMargin, + """|object O{ + | trait Foo { + | type T + | } + | val c/*: Foo<<(2:8)>>{type T = Int<>}*/ = new Foo { type T = Int } + | val d/*: Foo<<(2:8)>>{type T = Int<>}*/ = c + |} + |""".stripMargin + ) + + @Test def `refined-types3` = + check( + """|trait Foo extends Selectable { + | type T + |} + | + |val c = new Foo { + | type T = Int + | val x = 0 + | def y = 0 + | var z = 0 + |} + |""".stripMargin, + """|trait Foo extends Selectable { + | type T + |} + | + |val c/*: Foo<<(1:6)>>{type T = Int<>; val x: Int<>; def y: Int<>; val z: Int<>; def z_=(x$1: Int<>): Unit<>}*/ = new Foo { + | type T = Int + | val x/*: Int<>*/ = 0 + | def y/*: Int<>*/ = 0 + | var z/*: Int<>*/ = 0 + |} + |""".stripMargin + ) + + @Test def `dealias` = + check( + """|class Foo() { + | type T = Int + | def getT: T = 1 + |} + | + |object O { + | val c = new Foo().getT + |} + |""".stripMargin, + """|class Foo() { + | type T = Int + | def getT: T = 1 + |} + | + |object O { + | val c/*: Int<>*/ = new Foo().getT + |} + |""".stripMargin + ) + + @Test def `dealias2` = + check( + """|object Foo { + | type T = Int + | def getT: T = 1 + | val c = getT + |} + |""".stripMargin, + """|object Foo { + | type T = Int + | def getT: T = 1 + | val c/*: T<<(2:7)>>*/ = getT + |} + |""".stripMargin + ) + + @Test def `dealias3` = + check( + """|object Foo: + | opaque type T = Int + | def getT: T = 1 + |val c = Foo.getT + |""".stripMargin, + """|object Foo: + | opaque type T = Int + | def getT: T = 1 + |val c/*: T<<(2:14)>>*/ = Foo.getT + |""".stripMargin + ) + + @Test def `dealias4` = + check( + """|object O: + | type M = Int + | type W = M => Int + | def get: W = ??? + | + |val m = O.get + |""".stripMargin, + """|object O: + | type M = Int + | type W = M => Int + | def get: W = ??? + | + |val m/*: Int<> => Int<>*/ = O.get + |""".stripMargin + ) + + @Test def `dealias5` = + check( + """|object O: + | opaque type M = Int + | type W = M => Int + | def get: W = ??? + | + |val m = O.get + |""".stripMargin, + """|object O: + | opaque type M = Int + | type W = M => Int + | def get: W = ??? + | + |val m/*: M<<(2:13)>> => Int<>*/ = O.get + |""".stripMargin + ) + + @Test def `explicit-tuple` = + check( + """|object Main { + | val x = Tuple2.apply(1, 2) + |} + |""".stripMargin, + """|object Main { + | val x/*: (Int<>, Int<>)*/ = Tuple2.apply/*[Int<>, Int<>]*/(1, 2) + |} + |""".stripMargin + ) + + @Test def `explicit-tuple1` = + check( + """|object Main { + | val x = Tuple2(1, 2) + |} + |""".stripMargin, + """|object Main { + | val x/*: (Int<>, Int<>)*/ = Tuple2/*[Int<>, Int<>]*/(1, 2) + |} + |""".stripMargin + ) + + @Test def `tuple-unapply` = + check( + """|object Main { + | val (fst, snd) = (1, 2) + |} + |""".stripMargin, + """|object Main { + | val (fst/*: Int<>*/, snd/*: Int<>*/) = (1, 2) + |} + |""".stripMargin + ) + + @Test def `list-unapply` = + check( + """|object Main { + | val hd :: tail = List(1, 2) + |} + |""".stripMargin, + """|object Main { + | val hd/*: Int<>*/ ::/*[Int<>]*/ tail/*: List<>[Int<>]*/ = List/*[Int<>]*/(1, 2) + |} + |""".stripMargin, + ) + + @Test def `list-match` = + check( + """|object Main { + | val x = List(1, 2) match { + | case hd :: tail => hd + | } + |} + |""".stripMargin, + """|object Main { + | val x/*: Int<>*/ = List/*[Int<>]*/(1, 2) match { + | case hd/*: Int<>*/ ::/*[Int<>]*/ tail/*: List<>[Int<>]*/ => hd + | } + |} + |""".stripMargin, + ) + + @Test def `case-class-unapply` = + check( + """|object Main { + |case class Foo[A](x: A, y: A) + | val Foo(fst, snd) = Foo(1, 2) + |} + |""".stripMargin, + """|object Main { + |case class Foo[A](x: A, y: A) + | val Foo/*[Int<>]*/(fst/*: Int<>*/, snd/*: Int<>*/) = Foo/*[Int<>]*/(1, 2) + |} + |""".stripMargin, + ) + + @Test def `valueOf` = + check( + """|object O { + | def foo[Total <: Int](implicit total: ValueOf[Total]): Int = total.value + | val m = foo[500] + |} + |""".stripMargin, + """|object O { + | def foo[Total <: Int](implicit total: ValueOf[Total]): Int = total.value + | val m/*: Int<>*/ = foo[500]/*(new ValueOf(...))*/ + |} + |""".stripMargin + ) + + @Test def `case-class1` = + check( + """|object O { + |case class A(x: Int, g: Int)(implicit y: String) + |} + |""".stripMargin, + """|object O { + |case class A(x: Int, g: Int)(implicit y: String) + |} + |""".stripMargin + ) + + @Test def `ord` = + check( + """|object Main { + | val ordered = "acb".sorted + |} + |""".stripMargin, + """|object Main { + | val ordered/*: String<>*/ = /*augmentString<>(*/"acb"/*)*/.sorted/*[Char<>]*//*(Char<>)*/ + |} + |""".stripMargin + ) + + @Test def `partial-fun` = + check( + """|object Main { + | List(1).collect { case x => x } + | val x: PartialFunction[Int, Int] = { + | case 1 => 2 + | } + |} + |""".stripMargin, + """|object Main { + | List/*[Int<>]*/(1).collect/*[Int<>]*/ { case x/*: Int<>*/ => x } + | val x: PartialFunction[Int, Int] = { + | case 1 => 2 + | } + |} + |""".stripMargin + ) + + @Test def `val-def-with-bind` = + check( + """|object O { + | val tupleBound @ (one, two) = ("1", "2") + |} + |""".stripMargin, + """|object O { + | val tupleBound @ (one/*: String<>*/, two/*: String<>*/) = ("1", "2") + |} + |""".stripMargin + ) + + @Test def `val-def-with-bind-and-comment` = + check( + """|object O { + | val tupleBound /* comment */ @ (one, two) = ("1", "2") + |} + |""".stripMargin, + """|object O { + | val tupleBound /* comment */ @ (one/*: String<>*/, two/*: String<>*/) = ("1", "2") + |} + |""".stripMargin + ) + + @Test def `complex` = + check( + """|object ScalatestMock { + | class SRF + | implicit val subjectRegistrationFunction: SRF = new SRF() + | class Position + | implicit val here: Position = new Position() + | implicit class StringTestOps(name: String) { + | def should(right: => Unit)(implicit config: SRF): Unit = () + | def in(f: => Unit)(implicit pos: Position): Unit = () + | } + | implicit def instancesString: Eq[String] with Semigroup[String] = ??? + |} + | + |trait Eq[A] + |trait Semigroup[A] + | + |class DemoSpec { + | import ScalatestMock._ + | + | "foo" should { + | "checkThing1" in { + | checkThing1[String] + | } + | "checkThing2" in { + | checkThing2[String] + | } + | } + | + | "bar" should { + | "checkThing1" in { + | checkThing1[String] + | } + | } + | + | def checkThing1[A](implicit ev: Eq[A]) = ??? + | def checkThing2[A](implicit ev: Eq[A], sem: Semigroup[A]) = ??? + |} + |""".stripMargin, + """|object ScalatestMock { + | class SRF + | implicit val subjectRegistrationFunction: SRF = new SRF() + | class Position + | implicit val here: Position = new Position() + | implicit class StringTestOps(name: String) { + | def should(right: => Unit)(implicit config: SRF): Unit = () + | def in(f: => Unit)(implicit pos: Position): Unit = () + | } + | implicit def instancesString: Eq[String] with Semigroup[String] = ??? + |} + | + |trait Eq[A] + |trait Semigroup[A] + | + |class DemoSpec { + | import ScalatestMock._ + | + | /*StringTestOps<<(6:17)>>(*/"foo"/*)*/ should { + | /*StringTestOps<<(6:17)>>(*/"checkThing1"/*)*/ in { + | checkThing1[String]/*(instancesString<<(10:15)>>)*/ + | }/*(here<<(5:15)>>)*/ + | /*StringTestOps<<(6:17)>>(*/"checkThing2"/*)*/ in { + | checkThing2[String]/*(instancesString<<(10:15)>>, instancesString<<(10:15)>>)*/ + | }/*(here<<(5:15)>>)*/ + | }/*(subjectRegistrationFunction<<(3:15)>>)*/ + | + | /*StringTestOps<<(6:17)>>(*/"bar"/*)*/ should { + | /*StringTestOps<<(6:17)>>(*/"checkThing1"/*)*/ in { + | checkThing1[String]/*(instancesString<<(10:15)>>)*/ + | }/*(here<<(5:15)>>)*/ + | }/*(subjectRegistrationFunction<<(3:15)>>)*/ + | + | def checkThing1[A](implicit ev: Eq[A])/*: Nothing<>*/ = ??? + | def checkThing2[A](implicit ev: Eq[A], sem: Semigroup[A])/*: Nothing<>*/ = ??? + |} + |""".stripMargin + ) + + @Test def `import-rename` = + check( + """|import scala.collection.{AbstractMap => AB} + |import scala.collection.{Set => S} + | + |object Main { + | def test(d: S[Int], f: S[Char]): AB[Int, String] = { + | val x = d.map(_.toString) + | val y = f + | ??? + | } + | val x = test(Set(1), Set('a')) + |} + |""".stripMargin, + """|import scala.collection.{AbstractMap => AB} + |import scala.collection.{Set => S} + | + |object Main { + | def test(d: S[Int], f: S[Char]): AB[Int, String] = { + | val x/*: S<>[String<>]*/ = d.map/*[String<>]*/(_.toString) + | val y/*: S<>[Char<>]*/ = f + | ??? + | } + | val x/*: AB<>[Int<>, String<>]*/ = test(Set/*[Int<>]*/(1), Set/*[Char<>]*/('a')) + |} + |""".stripMargin, + ) + + @Test def `error-symbol` = + check( + """|package example + |case class ErrorMessage(error) + |""".stripMargin, + """|package example + |case class ErrorMessage(error) + |""".stripMargin + ) +} \ No newline at end of file diff --git a/presentation-compiler/test/dotty/tools/pc/utils/TestInlayHints.scala b/presentation-compiler/test/dotty/tools/pc/utils/TestInlayHints.scala new file mode 100644 index 000000000000..98ebb0852735 --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/utils/TestInlayHints.scala @@ -0,0 +1,70 @@ +package dotty.tools.pc.utils + +import scala.collection.mutable.ListBuffer + +import scala.meta.internal.jdk.CollectionConverters._ +import dotty.tools.pc.utils.MtagsEnrichments.* +import dotty.tools.pc.utils.TextEdits + +import org.eclipse.lsp4j.InlayHint +import org.eclipse.lsp4j.TextEdit +import org.eclipse.{lsp4j => l} + +object TestInlayHints { + + // For not local symbols - semanticdb symbol + // val x = 123 + // | + // v + // val x<<: Int/*scala/predef/Int#*/>> = 123 + // For local symbols - definition position in source + // type T = Int + // val x: T = ??? + // val y = x + // | + // v + // val y<<: T/*(0:5,0:5)*/>> = x + def decorationString(inlayHint: InlayHint): String = { + val buffer = ListBuffer.empty[String] + + val labels = inlayHint.getLabel().nn.asScala match { + case Left(label) => List(label) + case Right(labelParts) => labelParts.asScala.map(_.getValue()).toList + } + val data = + inlayHint.getData().asInstanceOf[Array[Any]] + buffer += "/*" + labels.zip(data).foreach { case (label, data) => + buffer += label.nn + buffer ++= readData(data) + } + buffer += "*/" + buffer.toList.mkString + } + + private def readData(data: Any): List[String] = { + data match { + case data: String if data.isEmpty => Nil + case data: String => List("<<", data, ">>") + case data: l.Position => + val str = s"(${data.getLine()}:${data.getCharacter()})" + List("<<", str, ">>") + } + } + + def applyInlayHints(text: String, inlayHints: List[InlayHint]): String = { + val textEdits = inlayHints.map { hint => + val newText = decorationString(hint) + val range = new l.Range(hint.getPosition(), hint.getPosition()) + new TextEdit( + range, + newText + ) + } + TextEdits.applyEdits(text, textEdits) + } + + def removeInlayHints(text: String): String = + text.replaceAll(raw"\/\*(.*?)\*\/", "").nn + +} \ No newline at end of file diff --git a/project/Build.scala b/project/Build.scala index 50f2cc8cb6f3..59321b5f677d 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1128,7 +1128,7 @@ object Build { BuildInfoPlugin.buildInfoDefaultSettings lazy val presentationCompilerSettings = { - val mtagsVersion = "1.2.0+67-30f8ab53-SNAPSHOT" + val mtagsVersion = "1.2.2+25-bb9dfbb9-SNAPSHOT" Seq( resolvers ++= Resolver.sonatypeOssRepos("snapshots"),