diff --git a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala index a4f3d0a522ae..08a34a2e9fbc 100644 --- a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala +++ b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala @@ -42,18 +42,19 @@ object SyntaxHighlighting { 'q' :: 'r' :: 's' :: 't' :: 'u' :: 'v' :: 'w' :: 'x' :: 'y' :: 'z' :: Nil private val typeEnders = - '{' :: '}' :: ')' :: '(' :: '[' :: ']' :: '=' :: ' ' :: ',' :: '.' :: - '\n' :: Nil + '{' :: '}' :: ')' :: '(' :: '[' :: ']' :: '=' :: ' ' :: ',' :: '.' :: '|' :: + '&' :: '\n' :: Nil def apply(chars: Iterable[Char]): Iterable[Char] = { var prev: Char = 0 var remaining = chars.toStream val newBuf = new StringBuilder - var lastToken = "" + var lastValDefToken = "" @inline def keywordStart = prev == 0 || prev == ' ' || prev == '{' || prev == '(' || - prev == '\n' || prev == '[' || prev == ',' + prev == '\n' || prev == '[' || prev == ',' || prev == ':' || + prev == '|' || prev == '&' @inline def numberStart(c: Char) = c.isDigit && (!prev.isLetter || prev == '.' || prev == ' ' || prev == '(' || prev == '\u0000') @@ -289,6 +290,23 @@ object SyntaxHighlighting { case _ => false } + val valDefStarterTokens = "var" :: "val" :: "def" :: "case" :: Nil + + /** lastValDefToken is used to check whether we want to show something + * in valDef color or not. There are only a few cases when lastValDefToken + * should be updated, that way we can avoid stopping coloring too early. + * eg.: case A(x, y, z) => ??? + * Without this function only x would be colored. + */ + def updateLastToken(currentToken: String): String = + (lastValDefToken, currentToken) match { + case _ if valDefStarterTokens.contains(currentToken) => currentToken + case (("val" | "var"), "=") => currentToken + case ("case", ("=>" | "class" | "object")) => currentToken + case ("def", _) => currentToken + case _ => lastValDefToken + } + while (remaining.nonEmpty && !delim(curr)) { curr = takeChar() if (!delim(curr)) sb += curr @@ -298,12 +316,12 @@ object SyntaxHighlighting { val toAdd = if (shouldHL(str)) highlight(str) - else if (("var" :: "val" :: "def" :: "case" :: Nil).contains(lastToken)) + else if (valDefStarterTokens.contains(lastValDefToken) && !List("=", "=>").contains(str)) valDef(str) else str val suffix = if (delim(curr)) s"$curr" else "" newBuf append (toAdd + suffix) - lastToken = str + lastValDefToken = updateLastToken(str) prev = curr } diff --git a/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala b/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala new file mode 100644 index 000000000000..7166d3cb0db1 --- /dev/null +++ b/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala @@ -0,0 +1,98 @@ +package dotty.tools.dotc.printing + +import org.junit.Assert._ +import org.junit.Test + +/** Adapted from Ammonite HighlightTests + */ +class SyntaxHighlightingTests { + import SyntaxHighlighting._ + + private def test(source: String, expected: String): Unit = { + val highlighted = SyntaxHighlighting.apply(source) + .mkString + .replace(NoColor, ">") + .replace(CommentColor, "") + test("/** a */", "") + test("/* a */", "") + } + + @Test + def types = { + test("type Foo = Int", " = ") + } + + @Test + def literals = { + test("1", "") + // test("1L", "") + } + + @Test + def strings = { + // For some reason we currently use literal color for string + test("\"Hello\"", "") + } + + @Test + def annotations = { + test("@tailrec", "") + } + + @Test + def expressions = { + test("val x = 1 + 2 + 3", " = + + ") + } + + @Test + def valDef = { + test("val a = 123", " = ") + test("var b = 123 /*Int*/", " = ") + test("""var c = "123" // String""", """ = """) + test("var e:Int = 123;e", " : = ;e") + test("def f = 123", " = ") + test("def f1(x: Int) = 123", " (x: ) = ") + test("def f2[T](x: T) = { 123 }", " [](x: ) = { }") + } + + @Test + def patternMatching = { + test("""val aFruit: Fruit = Apple("red", 123)""", + """ : = (, )""") + test("""val Apple(color, weight) = aFruit""", + """ (, ) = aFruit""") + test("""case Apple(_, weight) => println(s"apple: $weight kgs")""", + """ (, ) > println(s)""") + test("""case o: Orange => println(s"orange ${o.weight} kgs")""", + """ : > println(s)""") + test("""case m @ Melon(weight) => println(s"melon: ${m.weight} kgs")""", + """ @ () > println(s)""") + } + + @Test + def unionTypes = { + test("type A = String|Int| Long", " = || ") + test("type B = String |Int| Long", " = || ") + test("type C = String | Int | Long", " = | | ") + test("type D = String&Int& Long", " = && ") + test("type E = String &Int& Long", " = && ") + test("type F = String & Int & Long", " = & & ") + test("fn[String|Char](input)", "fn[|](input)") + } +} diff --git a/compiler/test/dotty/tools/dotc/reporting/SyntaxHighlightingTests.scala b/compiler/test/dotty/tools/dotc/reporting/SyntaxHighlightingTests.scala deleted file mode 100644 index 35f81e43263e..000000000000 --- a/compiler/test/dotty/tools/dotc/reporting/SyntaxHighlightingTests.scala +++ /dev/null @@ -1,62 +0,0 @@ -package dotty.tools.dotc.printing - -import org.junit.Assert._ -import org.junit.Test - -/** Adapted from Ammonite HighlightTests - */ -class SyntaxHighlightingTests { - import SyntaxHighlighting._ - - private def test(source: String, expected: String): Unit = { - val highlighted = SyntaxHighlighting.apply(source) - .mkString - .replace(NoColor, ">") - .replace(CommentColor, "") - test("/** a */", "") - test("/* a */", "") - } - - @Test - def types = { - test("type Foo = Int", " = ") - } - - @Test - def literals = { - test("1", "") - // test("1L", "") - } - - @Test - def strings = { - // For some reason we currently use literal color for string - test("\"Hello\"", "") - } - - @Test - def annotations = { - test("@tailrec", "") - } - - @Test - def expressions = { - test("val x = 1 + 2 + 3", " = + + ") - } -}