Repository: scalameta/scalameta
Pull request #3491 (2024-01-14 02:21:04)

InternalTrees: define `text` and `dialectText`


Patch:
diff --git a/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/InternalTrees.scala b/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/InternalTrees.scala
index a65d76ec2..39ab1ef7c 100644
--- a/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/InternalTrees.scala
+++ b/scalameta/trees/shared/src/main/scala/scala/meta/internal/trees/InternalTrees.scala
@@ -5,10 +5,12 @@ package trees
 import scala.collection.mutable
 import scala.meta.inputs._
 import scala.meta.internal.tokenizers.Compat
+import scala.meta.internal.prettyprinters.TreeSyntax
 import scala.meta.prettyprinters._
 import scala.meta.tokenizers._
 import scala.meta.tokens._
 import scala.meta.tokens.Token._
+import scala.meta.trees.Error
 import scala.meta.trees.Origin
 
 // NOTE: Methods that start with "private" are NOT intended to be called outside scala.meta.
@@ -67,16 +69,38 @@ trait InternalTree extends Product {
   private lazy val dialectTokensOpt: Option[Tokens] =
     origin match {
       case x: Origin.Parsed => Some(x.tokens)
-      case _ => origin.dialectOpt.map(tokensForDialect(_))
+      case _ => origin.dialectOpt.map(tokensForDialect)
     }
 
-  private def tokensForDialect(implicit dialect: Dialect): Tokens =
+  private def tokensForDialect(dialect: Dialect): Tokens =
     this match {
       case Lit.String(value) =>
         val input = Input.VirtualFile("<InternalTrees.tokens>", value)
         Tokens(Array(Constant.String(input, dialect, 0, value.length, value)))
       case _ =>
-        dialect(Input.VirtualFile("<InternalTrees.tokens>", this.syntax)).tokenize.get
+        dialect(Input.VirtualFile("<InternalTrees.tokens>", dialectText(dialect))).tokenize.get
+    }
+
+  // ==============================================================
+  // Text or syntax
+  // ==============================================================
+
+  def text: String = textOpt.getOrElse {
+    throw new Error.MissingDialectException(
+      "Tree missing a dialect; update root tree `.withDialectIfRootAndNotSet` first, or call `.dialectText`."
+    )
+  }
+
+  def dialectText(implicit dialect: Dialect): String =
+    if (origin.dialectOpt.contains(dialect)) textOpt.get else reprintSyntax(dialect)
+
+  private def reprintSyntax(dialect: Dialect): String =
+    TreeSyntax.reprint(this)(dialect).toString
+
+  private lazy val textOpt: Option[String] =
+    origin match {
+      case x: Origin.Parsed => Some(x.position.text)
+      case _ => origin.dialectOpt.map(reprintSyntax)
     }
 
   // ==============================================================
diff --git a/scalameta/trees/shared/src/main/scala/scala/meta/trees/Error.scala b/scalameta/trees/shared/src/main/scala/scala/meta/trees/Error.scala
new file mode 100644
index 000000000..4855b3d31
--- /dev/null
+++ b/scalameta/trees/shared/src/main/scala/scala/meta/trees/Error.scala
@@ -0,0 +1,5 @@
+package scala.meta.trees
+
+object Error {
+  class MissingDialectException(message: String) extends Exception(message)
+}


Test patch:
diff --git a/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala b/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala
index a904932ac..333f6252b 100644
--- a/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala
+++ b/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala
@@ -172,6 +172,8 @@ class SurfaceSuite extends FunSuite {
       |scala.meta.transversers.Transformer
       |scala.meta.transversers.Traverser
       |scala.meta.trees
+      |scala.meta.trees.Error *
+      |scala.meta.trees.Error.MissingDialectException *
       |scala.meta.trees.Origin *
       |scala.meta.trees.Origin.DialectOnly *
       |scala.meta.trees.Origin.None *
diff --git a/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala
index f65461a90..3a7131d32 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala
@@ -450,4 +450,7 @@ class PublicSuite extends TreeSuiteBase {
   test("scala.meta.trees.Origin.Parsed") {}
   test("scala.meta.trees.Origin.ParsedSource") {}
 
+  test("scala.meta.trees.Error") {}
+  test("scala.meta.trees.Error.MissingDialectException") {}
+
 }
diff --git a/tests/shared/src/test/scala/scala/meta/tests/trees/TokensSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/trees/TokensSuite.scala
index 1d0eb02f8..08d464331 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/trees/TokensSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/trees/TokensSuite.scala
@@ -20,6 +20,10 @@ class TokensSuite extends TreeSuiteBase {
 
   test("Tree.tokens: manual") {
     val tree: Term = Term.ApplyInfix(Term.Name("foo"), Term.Name("+"), Nil, List(Term.Name("bar")))
+    interceptMessage[trees.Error.MissingDialectException](
+      "Tree missing a dialect; update root tree `.withDialectIfRootAndNotSet` first, or call `.dialectText`."
+    )(tree.text)
+    assertEquals(tree.dialectText, "foo + bar")
     assertEquals(tree.syntax, "foo + bar")
     assert(tree.origin eq trees.Origin.None)
     val tokens = tree.tokens
@@ -27,6 +31,8 @@ class TokensSuite extends TreeSuiteBase {
     assert(tokens.forall(_.input.isInstanceOf[Input.VirtualFile]))
     val dialectTree = tree.withDialectIfRootAndNotSet
     assertNotEquals(dialectTree, tree)
+    assertEquals(dialectTree.text, "foo + bar")
+    assertEquals(dialectTree.dialectText, "foo + bar")
     assert(dialectTree.origin ne trees.Origin.None)
     val dialectTokens = dialectTree.tokens
     assertEquals(dialectTokens.syntax, "foo + bar")