@@ -11,9 +11,7 @@ import dotty.tools.dotc.reporting.Reporter
1111import dotty .tools .dotc .reporting .diagnostic .MessageContainer
1212import dotty .tools .dotc .util .Positions .Position
1313
14- import scala .annotation .switch
15- import scala .collection .mutable .StringBuilder
16- import util .{Chars , SourceFile }
14+ import util .SourceFile
1715
1816import scala .collection .mutable
1917
@@ -30,346 +28,6 @@ object SyntaxHighlighting {
3028 val TypeColor = Console .MAGENTA
3129 val AnnotationColor = Console .MAGENTA
3230
33- private def none (str : String ) = str
34- private def keyword (str : String ) = KeywordColor + str + NoColor
35- private def typeDef (str : String ) = TypeColor + str + NoColor
36- private def literal (str : String ) = LiteralColor + str + NoColor
37- private def valDef (str : String ) = ValDefColor + str + NoColor
38- private def operator (str : String ) = TypeColor + str + NoColor
39- private def annotation (str : String ) =
40- if (str.trim == " @" ) str else { AnnotationColor + str + NoColor }
41- private val tripleQs = Console .RED_B + " ???" + NoColor
42-
43- private val keywords : Seq [String ] = for {
44- index <- IF to ERASED // All alpha keywords
45- } yield tokenString(index)
46-
47- private val interpolationPrefixes =
48- 'A' :: 'B' :: 'C' :: 'D' :: 'E' :: 'F' :: 'G' :: 'H' :: 'I' :: 'J' :: 'K' ::
49- 'L' :: 'M' :: 'N' :: 'O' :: 'P' :: 'Q' :: 'R' :: 'S' :: 'T' :: 'U' :: 'V' ::
50- 'W' :: 'X' :: 'Y' :: 'Z' :: '$' :: '_' :: 'a' :: 'b' :: 'c' :: 'd' :: 'e' ::
51- 'f' :: 'g' :: 'h' :: 'i' :: 'j' :: 'k' :: 'l' :: 'm' :: 'n' :: 'o' :: 'p' ::
52- 'q' :: 'r' :: 's' :: 't' :: 'u' :: 'v' :: 'w' :: 'x' :: 'y' :: 'z' :: Nil
53-
54- private val typeEnders =
55- '{' :: '}' :: ')' :: '(' :: '[' :: ']' :: '=' :: ' ' :: ',' :: '.' :: '|' ::
56- '&' :: '\n ' :: Nil
57-
58- def apply (chars : Iterable [Char ])(implicit ctx : Context ): Iterable [Char ] = {
59- if (ctx.settings.color.value != " never" ) highlight(chars)
60- else chars
61- }
62-
63- def highlight (chars : Iterable [Char ]): Iterable [Char ] = {
64- var prev : Char = 0
65- var remaining = chars.toStream
66- val newBuf = new StringBuilder
67- var lastValDefToken = " "
68-
69- @ forceInline def keywordStart =
70- prev == 0 || prev == ' ' || prev == '{' || prev == '(' ||
71- prev == '\n ' || prev == '[' || prev == ',' || prev == ':' ||
72- prev == '|' || prev == '&' || prev.isDigit
73-
74- @ forceInline def numberStart (c : Char ) =
75- c.isDigit && (! prev.isLetter || prev == '.' || prev == ' ' || prev == '(' || prev == '\u0000 ' )
76-
77- def takeChar (): Char = takeChars(1 ).head
78- def takeChars (x : Int ): Seq [Char ] = {
79- val taken = remaining.take(x)
80- remaining = remaining.drop(x)
81- taken
82- }
83-
84- while (remaining.nonEmpty) {
85- val n = takeChar()
86- if (interpolationPrefixes.contains(n)) {
87- // Interpolation prefixes are a superset of the keyword start chars
88- val (prefix, after) = remaining.span(interpolationPrefixes.contains)
89- if (after.startsWith(" \" " )) {
90- newBuf += n ++= prefix
91- prev = prefix.lastOption.getOrElse(n)
92- if (remaining.nonEmpty) takeChars(prefix.length + 1 ) // drop 1 for appendLiteral
93- appendString('"' , after.startsWith(" \"\"\" " ), true )
94- } else {
95- if (n.isUpper && (keywordStart || prev == '.' )) {
96- appendWhile(n, ! typeEnders.contains(_), typeDef)
97- } else if (keywordStart) {
98- append(n, keywords.contains(_), { kw =>
99- if (kw == " new" ) typeDef(kw) else keyword(kw)
100- })
101- } else {
102- newBuf += n
103- prev = n
104- }
105- }
106- } else {
107- (n : @ switch) match {
108- case '/' =>
109- if (remaining.nonEmpty) {
110- remaining.head match {
111- case '/' =>
112- takeChar()
113- eolComment()
114- case '*' =>
115- takeChar()
116- blockComment()
117- case x =>
118- newBuf += '/'
119- }
120- } else newBuf += '/'
121- case '=' =>
122- append('=' , _ == " =>" , operator)
123- case '<' =>
124- append('<' , { x => x == " <-" || x == " <:" || x == " <%" }, operator)
125- case '>' =>
126- append('>' , { x => x == " >:" }, operator)
127- case '#' =>
128- if (prev != ' ' && prev != '.' ) newBuf append operator(" #" )
129- else newBuf += n
130- prev = '#'
131- case '@' =>
132- appendWhile('@' , ! typeEnders.contains(_), annotation)
133- case '\" ' =>
134- appendString('\" ' , multiline = remaining.take(2 ).mkString == " \"\" " , false )
135- case '\' ' =>
136- appendSingleQuote('\' ' )
137- case '`' =>
138- appendTo('`' , _ == '`' , none)
139- case _ => {
140- if (n == '?' && remaining.take(2 ).mkString == " ??" ) {
141- takeChars(2 )
142- newBuf append tripleQs
143- prev = '?'
144- }
145- else if (n.isUpper && keywordStart)
146- appendWhile(n, ! typeEnders.contains(_), typeDef)
147- else if (numberStart(n)) {
148- def isNumber (c : Char ): Boolean =
149- c.isDigit || c == '\u0000 ' || (c == '.' && remaining.nonEmpty && remaining.head.isDigit)
150- appendWhile(n, isNumber, literal)
151- } else
152- newBuf += n; prev = n
153- }
154- }
155- }
156- }
157-
158- def eolComment () = {
159- newBuf append (CommentColor + " //" )
160- var curr = '/'
161- while (curr != '\n ' && remaining.nonEmpty) {
162- curr = takeChar()
163- newBuf += curr
164- }
165- prev = curr
166- newBuf append NoColor
167- }
168-
169- def blockComment () = {
170- newBuf append (CommentColor + " /*" )
171- var curr = '*'
172- var open = 1
173- while (open > 0 && remaining.nonEmpty) {
174- curr = takeChar()
175- if (curr == '@' ) {
176- appendWhile('@' , ! typeEnders.contains(_), annotation)
177- newBuf append CommentColor
178- }
179- else newBuf += curr
180-
181- if (curr == '*' && remaining.nonEmpty) {
182- curr = takeChar()
183- newBuf += curr
184- if (curr == '/' ) open -= 1
185- } else if (curr == '/' && remaining.nonEmpty) {
186- curr = takeChar()
187- newBuf += curr
188- if (curr == '*' ) open += 1
189- }
190-
191- if (Chars .isLineBreakChar(curr)) {
192- newBuf append CommentColor
193- }
194- }
195- prev = curr
196- newBuf append NoColor
197- }
198-
199- def appendString (delim : Char , multiline : Boolean = false , inInterpolation : Boolean ) = {
200- var curr : Char = 0
201- var continue = true
202- var closing = 0
203- newBuf append (LiteralColor + delim)
204-
205- def shouldInterpolate =
206- inInterpolation && curr == '$' && prev != '$' && remaining.nonEmpty
207-
208- def interpolate () = {
209- val next = takeChar()
210- if (next == '$' ) {
211- newBuf += curr
212- newBuf += next
213- prev = '$'
214- } else if (next == '{' ) {
215- var open = 1 // keep track of open blocks
216- newBuf append (ValDefColor + curr)
217- newBuf += next
218- while (remaining.nonEmpty && open > 0 ) {
219- var c = takeChar()
220- newBuf += c
221- if (c == '}' ) open -= 1
222- else if (c == '{' ) open += 1
223- }
224- newBuf append LiteralColor
225- } else {
226- newBuf append (ValDefColor + curr)
227- newBuf += next
228- var c : Char = 'a'
229- while (c.isLetterOrDigit && remaining.nonEmpty) {
230- c = takeChar()
231- if (c != '"' ) newBuf += c
232- }
233- newBuf append LiteralColor
234- if (c == '"' ) {
235- newBuf += c
236- continue = false
237- }
238- }
239- closing = 0
240- }
241-
242- while (continue && remaining.nonEmpty) {
243- curr = takeChar()
244- if (curr == '\\ ' && remaining.nonEmpty) {
245- val next = takeChar()
246- newBuf append (KeywordColor + curr)
247- if (next == 'u' ) {
248- val code = " u" + takeChars(4 ).mkString
249- newBuf append code
250- } else newBuf += next
251- newBuf append LiteralColor
252- closing = 0
253- } else if (shouldInterpolate) {
254- interpolate()
255- } else if (curr == delim && multiline) {
256- closing += 1
257- if (closing == 3 ) continue = false
258- newBuf += curr
259- } else if (curr == delim) {
260- continue = false
261- newBuf += curr
262- } else {
263- newBuf += curr
264- closing = 0
265- }
266-
267- if (Chars .isLineBreakChar(curr)) {
268- newBuf append LiteralColor
269- }
270- }
271- newBuf append NoColor
272- prev = curr
273- }
274-
275- def appendSingleQuote (delim : Char ) = remaining.take(3 ) match {
276- case chr #:: '\' ' #:: _ => // single character
277- newBuf append LiteralColor
278- newBuf appendAll s " ' $chr' "
279- newBuf append NoColor
280- takeChars(2 )
281- prev = '\' '
282- case '\\ ' #:: chr #:: '\' ' #:: _ => // escaped character
283- newBuf append LiteralColor
284- newBuf appendAll s " ' \\ $chr' "
285- newBuf append NoColor
286- takeChars(3 )
287- prev = '\' '
288- case _ => appendWhile(delim, ! typeEnders.contains(_), literal)
289- }
290-
291- def append (c : Char , shouldHL : String => Boolean , highlight : String => String ) = {
292- var curr : Char = 0
293- val sb = new StringBuilder (s " $c" )
294-
295- def delim (c : Char ) = (c : @ switch) match {
296- case ' ' => true
297- case '\n ' => true
298- case '(' => true
299- case ')' => true
300- case '[' => true
301- case ']' => true
302- case ':' => true
303- case '@' => true
304- case ',' => true
305- case '.' => true
306- case _ => false
307- }
308-
309- val valDefStarterTokens = " var" :: " val" :: " def" :: " case" :: Nil
310-
311- /** lastValDefToken is used to check whether we want to show something
312- * in valDef color or not. There are only a few cases when lastValDefToken
313- * should be updated, that way we can avoid stopping coloring too early.
314- * eg.: case A(x, y, z) => ???
315- * Without this function only x would be colored.
316- */
317- def updateLastToken (currentToken : String ): String =
318- (lastValDefToken, currentToken) match {
319- case _ if valDefStarterTokens.contains(currentToken) => currentToken
320- case ((" val" | " var" ), " =" ) => currentToken
321- case (" case" , (" =>" | " class" | " object" )) => currentToken
322- case (" def" , _) => currentToken
323- case _ => lastValDefToken
324- }
325-
326- while (remaining.nonEmpty && ! delim(curr)) {
327- curr = takeChar()
328- if (! delim(curr)) sb += curr
329- }
330-
331- val str = sb.toString
332- val toAdd =
333- if (shouldHL(str))
334- highlight(str)
335- else if (valDefStarterTokens.contains(lastValDefToken) && ! List (" =" , " =>" ).contains(str))
336- valDef(str)
337- else str
338- val suffix = if (delim(curr)) s " $curr" else " "
339- newBuf append (toAdd + suffix)
340- lastValDefToken = updateLastToken(str)
341- prev = curr
342- }
343-
344- def appendWhile (c : Char , pred : Char => Boolean , highlight : String => String ) = {
345- var curr : Char = 0
346- val sb = new StringBuilder (s " $c" )
347- while (remaining.nonEmpty && pred(curr)) {
348- curr = takeChar()
349- if (pred(curr)) sb += curr
350- }
351-
352- val str = sb.toString
353- val suffix = if (! pred(curr)) s " $curr" else " "
354- newBuf append (highlight(str) + suffix)
355- prev = curr
356- }
357-
358- def appendTo (c : Char , pred : Char => Boolean , highlight : String => String ) = {
359- var curr : Char = 0
360- val sb = new StringBuilder (s " $c" )
361- while (remaining.nonEmpty && ! pred(curr)) {
362- curr = takeChar()
363- sb += curr
364- }
365-
366- newBuf append highlight(sb.toString)
367- prev = curr
368- }
369-
370- newBuf.toIterable
371- }
372-
37331 private class NoReporter extends Reporter {
37432 override def doReport (m : MessageContainer )(implicit ctx : Context ): Unit = ()
37533 }
0 commit comments