Skip to content

Commit 578af35

Browse files
committed
Refactor SyntaxHighlight.scala
Added comments to updateLastToken function Use syntax highlight tests proposed in pr #4441 instead of the file based solution
1 parent edb401e commit 578af35

File tree

6 files changed

+169
-187
lines changed

6 files changed

+169
-187
lines changed

compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -287,18 +287,24 @@ object SyntaxHighlighting {
287287
case '@' => true
288288
case ',' => true
289289
case '.' => true
290-
case ',' => true
291-
case ')' => true
292290
case _ => false
293291
}
294292

295-
def shouldUpdateLastToken(currentPotentialToken: String): Boolean =
296-
(lastToken, currentPotentialToken) match {
297-
case (_, ("var" | "val" | "def" | "case")) => true
298-
case (("val" | "var"), "=") => true
299-
case ("case", ("=>" | "class" | "object")) => true
300-
case ("def", _) => true
301-
case _ => false
293+
val valDefStarterTokens = "var" :: "val" :: "def" :: "case" :: Nil
294+
295+
/** lastToken is used to check whether we want to show something
296+
* in valDef color or not. There are only a few cases when lastToken
297+
* should be updated, that way we can avoid stopping coloring too early.
298+
* eg.: case A(x, y, z) => ???
299+
* Without this function only x would be colored.
300+
*/
301+
def updateLastToken(currentToken: String): String =
302+
(lastToken, currentToken) match {
303+
case _ if valDefStarterTokens.contains(currentToken) => currentToken
304+
case (("val" | "var"), "=") => currentToken
305+
case ("case", ("=>" | "class" | "object")) => currentToken
306+
case ("def", _) => currentToken
307+
case _ => lastToken
302308
}
303309

304310
while (remaining.nonEmpty && !delim(curr)) {
@@ -310,13 +316,12 @@ object SyntaxHighlighting {
310316
val toAdd =
311317
if (shouldHL(str))
312318
highlight(str)
313-
else if (("var" :: "val" :: "def" :: "case" :: Nil).contains(lastToken) &&
314-
!List("=", "=>").contains(str))
319+
else if (valDefStarterTokens.contains(lastToken) && !List("=", "=>").contains(str))
315320
valDef(str)
316321
else str
317322
val suffix = if (delim(curr)) s"$curr" else ""
318323
newBuf append (toAdd + suffix)
319-
if (shouldUpdateLastToken(str)) lastToken = str
324+
lastToken = updateLastToken(str)
320325
prev = curr
321326
}
322327

compiler/test-resources/printing/defs

Lines changed: 0 additions & 31 deletions
This file was deleted.

compiler/test-resources/printing/patternMatching

Lines changed: 0 additions & 29 deletions
This file was deleted.

compiler/test-resources/printing/unionAndIntersectionTypes

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 152 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,167 @@
11
package dotty.tools.dotc.printing
22

3-
import java.io.PrintWriter
4-
5-
import dotty.tools.io.JFile
6-
import org.junit.Assert.fail
3+
import org.junit.Assert._
74
import org.junit.Test
85

9-
import scala.io.Source
10-
11-
/** Runs all tests contained in `compiler/test-resources/printing/`
12-
* To check test cases, you can use "cat" or "less -r" from bash
13-
* To generate test files you can call the generateTestFile method*/
6+
/** Adapted from Ammonite HighlightTests
7+
*/
148
class SyntaxHighlightingTests {
9+
import SyntaxHighlighting._
10+
11+
private def test(source: String, expected: String): Unit = {
12+
val highlighted = SyntaxHighlighting.apply(source)
13+
.mkString
14+
.replace(NoColor, ">")
15+
.replace(CommentColor, "<C|")
16+
.replace(KeywordColor, "<K|")
17+
.replace(ValDefColor, "<V|")
18+
.replace(LiteralColor, "<L|")
19+
.replace(StringColor, "<S|")
20+
.replace(TypeColor, "<T|")
21+
// .replace(AnnotationColor, "<A|") // is the same color as type color
1522

16-
private def scripts(path: String): Array[JFile] = {
17-
val dir = new JFile(getClass.getResource(path).getPath)
18-
assert(dir.exists && dir.isDirectory, "Couldn't load scripts dir")
19-
dir.listFiles
23+
if (expected != highlighted) {
24+
// assertEquals produces weird expected/found message
25+
fail(s"expected: $expected but was: $highlighted")
26+
}
2027
}
2128

22-
private def testFile(f: JFile): Unit = {
23-
val lines = Source.fromFile(f).getLines()
24-
val input = lines.takeWhile(_ != "result:").mkString("\n")
25-
val expectedOutput = lines.mkString("\n")
26-
val actualOutput = SyntaxHighlighting(input).mkString
29+
@Test
30+
def comments = {
31+
test("//a", "<C|//a>")
32+
test("/** a */", "<C|/** a */>")
33+
test("/* a */", "<C|/* a */>")
34+
}
2735

28-
if (expectedOutput != actualOutput) {
29-
println("Expected output:")
30-
println(expectedOutput)
31-
println("Actual output:")
32-
println(actualOutput)
36+
@Test
37+
def types = {
38+
test("type Foo = Int", "<K|type> <T|Foo> = <T|Int>")
39+
}
3340

34-
// Call generateTestFile when you want to update a test file
35-
// or generate a test from a scala source file
36-
// if (f.getName == "nameOfFileToConvertToATest") generateTestFile()
41+
@Test
42+
def literals = {
43+
test("1", "<L|1>")
44+
// test("1L", "<L|1L>")
45+
}
3746

38-
fail(s"Error in file $f, expected output did not match actual")
39-
}
47+
@Test
48+
def strings = {
49+
// For some reason we currently use literal color for string
50+
test("\"Hello\"", "<L|\"Hello\">")
51+
}
4052

41-
/** Writes `input` and `actualOutput` to the current test file*/
42-
def generateTestFile(): Unit = {
43-
val path = "compiler/test-resources/printing/" + f.getName
44-
new PrintWriter(path) {
45-
write(input + "\nresult:\n" + actualOutput)
46-
close()
47-
}
48-
println(s"Test file for ${f.getName} has been generated")
49-
}
53+
@Test
54+
def annotations = {
55+
test("@tailrec", "<T|@tailrec>")
56+
}
57+
58+
@Test
59+
def expressions = {
60+
test("val x = 1 + 2 + 3", "<K|val> <V|x> = <L|1> + <L|2> + <L|3>")
61+
}
62+
63+
@Test
64+
def valVarDef = {
65+
val source =
66+
"""
67+
|package test
68+
|
69+
|object A {
70+
| val a = 123
71+
| var b = 123 /*Int*/
72+
| var c = "123" // String
73+
| var d: Int = 123
74+
| var e:Int = 123;e
75+
| e
76+
| print(a)
77+
| 123;123
78+
| def f = 123
79+
| def f1(x: Int) = 123
80+
| def f2[T](x: T) = { 123 }
81+
|}
82+
""".stripMargin
83+
val expected =
84+
"""
85+
|<K|package> test
86+
|
87+
|<K|object> <T|A> {
88+
| <K|val> <V|a> = <L|123>
89+
| <K|var> <V|b> = <L|123> <C|/*Int*/>
90+
| <K|var> <V|c> = <L|"123"> <C|// String
91+
|> <K|var> <V|d>: <T|Int> = <L|123>
92+
| <K|var> <V|e>:<T|Int> = <L|123>;e
93+
| e
94+
| print(a)
95+
| <L|123>;<L|123>
96+
| <K|def> <V|f> = <L|123>
97+
| <K|def> <V|f1>(x: <T|Int>) = <L|123>
98+
| <K|def> <V|f2>[<T|T>](x: <T|T>) = { <L|123> }
99+
|}
100+
""".stripMargin
101+
test(source, expected)
102+
}
103+
104+
@Test
105+
def patternMatching = {
106+
val source =
107+
"""
108+
|val aFruit: Fruit = Apple("red", 123)
109+
|
110+
|val Apple(color, weight) = aFruit
111+
|
112+
|sealed trait Fruit
113+
|case class Apple(color: String, weight: Double) extends Fruit
114+
|case class Orange(weight: Double) extends Fruit
115+
|case class Melon(weight: Double) extends Fruit
116+
|
117+
|aFruit match {
118+
| case Apple(_, weight) => println(s"apple: $weight kgs")
119+
| case o: Orange => println(s"orange ${o.weight} kgs")
120+
| case m @ Melon(weight) => println(s"melon: ${m.weight} kgs")
121+
|}
122+
""".stripMargin
123+
val expected =
124+
"""
125+
|<K|val> <V|aFruit>: <T|Fruit> = <T|Apple>(<L|"red">, <L|123>)
126+
|
127+
|<K|val> <T|Apple>(<V|color>, <V|weight>) = aFruit
128+
|
129+
|<K|sealed> <K|trait> <T|Fruit>
130+
|<K|case> <K|class> <T|Apple>(color: <T|String>, weight: <T|Double>) <K|extends> <T|Fruit>
131+
|<K|case> <K|class> <T|Orange>(weight: <T|Double>) <K|extends> <T|Fruit>
132+
|<K|case> <K|class> <T|Melon>(weight: <T|Double>) <K|extends> <T|Fruit>
133+
|
134+
|aFruit <K|match> {
135+
| <K|case> <T|Apple>(<V|_>, <V|weight>) <T|=>> println(s<L|"apple: <V|$weight <L|kgs">)
136+
| <K|case> <V|o>: <T|Orange> <T|=>> println(s<L|"orange <V|${o.weight}<L| kgs">)
137+
| <K|case> <V|m> @ <T|Melon>(<V|weight>) <T|=>> println(s<L|"melon: <V|${m.weight}<L| kgs">)
138+
|}
139+
""".stripMargin
140+
test(source, expected)
50141
}
51142

52-
@Test def syntaxHighlight = scripts("/printing").foreach(testFile)
143+
@Test
144+
def unionTypes = {
145+
val source =
146+
"""
147+
|type A = String|Int| Long
148+
|type B = String |Int| Long
149+
|type C = String | Int | Long
150+
|type D = String&Int& Long
151+
|type E = String &Int& Long
152+
|type F = String & Int & Long
153+
|fn[String|Char](input)
154+
""".stripMargin
155+
val expected =
156+
"""
157+
|<K|type> <T|A> = <T|String>|<T|Int>| <T|Long>
158+
|<K|type> <T|B> = <T|String> |<T|Int>| <T|Long>
159+
|<K|type> <T|C> = <T|String> | <T|Int> | <T|Long>
160+
|<K|type> <T|D> = <T|String>&<T|Int>& <T|Long>
161+
|<K|type> <T|E> = <T|String> &<T|Int>& <T|Long>
162+
|<K|type> <T|F> = <T|String> & <T|Int> & <T|Long>
163+
|fn[<T|String>|<T|Char>](input)
164+
""".stripMargin
165+
test(source, expected)
166+
}
53167
}

compiler/test/dotty/tools/dotc/reporting/SyntaxHighlightingTests.scala

Lines changed: 0 additions & 62 deletions
This file was deleted.

0 commit comments

Comments
 (0)