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