@@ -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,342 +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 next = remaining.take(3 ).mkString
84
- if (next.startsWith(" \" " )) {
85
- newBuf += n
86
- prev = n
87
- if (remaining.nonEmpty) takeChar() // drop 1 for appendLiteral
88
- appendString('"' , next == " \"\"\" " )
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 == " \"\" " )
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 ) = {
195
- var curr : Char = 0
196
- var continue = true
197
- var closing = 0
198
- val inInterpolation = interpolationPrefixes.contains(prev)
199
- newBuf append (LiteralColor + delim)
200
-
201
- def shouldInterpolate =
202
- inInterpolation && curr == '$' && prev != '$' && remaining.nonEmpty
203
-
204
- def interpolate () = {
205
- val next = takeChar()
206
- if (next == '$' ) {
207
- newBuf += curr
208
- newBuf += next
209
- prev = '$'
210
- } else if (next == '{' ) {
211
- var open = 1 // keep track of open blocks
212
- newBuf append (ValDefColor + curr)
213
- newBuf += next
214
- while (remaining.nonEmpty && open > 0 ) {
215
- var c = takeChar()
216
- newBuf += c
217
- if (c == '}' ) open -= 1
218
- else if (c == '{' ) open += 1
219
- }
220
- newBuf append LiteralColor
221
- } else {
222
- newBuf append (ValDefColor + curr)
223
- newBuf += next
224
- var c : Char = 'a'
225
- while (c.isLetterOrDigit && remaining.nonEmpty) {
226
- c = takeChar()
227
- if (c != '"' ) newBuf += c
228
- }
229
- newBuf append LiteralColor
230
- if (c == '"' ) {
231
- newBuf += c
232
- continue = false
233
- }
234
- }
235
- closing = 0
236
- }
237
-
238
- while (continue && remaining.nonEmpty) {
239
- curr = takeChar()
240
- if (curr == '\\ ' && remaining.nonEmpty) {
241
- val next = takeChar()
242
- newBuf append (KeywordColor + curr)
243
- if (next == 'u' ) {
244
- val code = " u" + takeChars(4 ).mkString
245
- newBuf append code
246
- } else newBuf += next
247
- newBuf append LiteralColor
248
- closing = 0
249
- } else if (shouldInterpolate) {
250
- interpolate()
251
- } else if (curr == delim && multiline) {
252
- closing += 1
253
- if (closing == 3 ) continue = false
254
- newBuf += curr
255
- } else if (curr == delim) {
256
- continue = false
257
- newBuf += curr
258
- } else {
259
- newBuf += curr
260
- closing = 0
261
- }
262
-
263
- if (Chars .isLineBreakChar(curr)) {
264
- newBuf append LiteralColor
265
- }
266
- }
267
- newBuf append NoColor
268
- prev = curr
269
- }
270
-
271
- def appendSingleQuote (delim : Char ) = remaining.take(3 ) match {
272
- case chr #:: '\' ' #:: _ => // single character
273
- newBuf append LiteralColor
274
- newBuf appendAll s " ' $chr' "
275
- newBuf append NoColor
276
- takeChars(2 )
277
- prev = '\' '
278
- case '\\ ' #:: chr #:: '\' ' #:: _ => // escaped character
279
- newBuf append LiteralColor
280
- newBuf appendAll s " ' \\ $chr' "
281
- newBuf append NoColor
282
- takeChars(3 )
283
- prev = '\' '
284
- case _ => appendWhile(delim, ! typeEnders.contains(_), literal)
285
- }
286
-
287
- def append (c : Char , shouldHL : String => Boolean , highlight : String => String ) = {
288
- var curr : Char = 0
289
- val sb = new StringBuilder (s " $c" )
290
-
291
- def delim (c : Char ) = (c : @ switch) match {
292
- case ' ' => true
293
- case '\n ' => true
294
- case '(' => true
295
- case ')' => true
296
- case '[' => true
297
- case ']' => true
298
- case ':' => true
299
- case '@' => true
300
- case ',' => true
301
- case '.' => true
302
- case _ => false
303
- }
304
-
305
- val valDefStarterTokens = " var" :: " val" :: " def" :: " case" :: Nil
306
-
307
- /** lastValDefToken is used to check whether we want to show something
308
- * in valDef color or not. There are only a few cases when lastValDefToken
309
- * should be updated, that way we can avoid stopping coloring too early.
310
- * eg.: case A(x, y, z) => ???
311
- * Without this function only x would be colored.
312
- */
313
- def updateLastToken (currentToken : String ): String =
314
- (lastValDefToken, currentToken) match {
315
- case _ if valDefStarterTokens.contains(currentToken) => currentToken
316
- case ((" val" | " var" ), " =" ) => currentToken
317
- case (" case" , (" =>" | " class" | " object" )) => currentToken
318
- case (" def" , _) => currentToken
319
- case _ => lastValDefToken
320
- }
321
-
322
- while (remaining.nonEmpty && ! delim(curr)) {
323
- curr = takeChar()
324
- if (! delim(curr)) sb += curr
325
- }
326
-
327
- val str = sb.toString
328
- val toAdd =
329
- if (shouldHL(str))
330
- highlight(str)
331
- else if (valDefStarterTokens.contains(lastValDefToken) && ! List (" =" , " =>" ).contains(str))
332
- valDef(str)
333
- else str
334
- val suffix = if (delim(curr)) s " $curr" else " "
335
- newBuf append (toAdd + suffix)
336
- lastValDefToken = updateLastToken(str)
337
- prev = curr
338
- }
339
-
340
- def appendWhile (c : Char , pred : Char => Boolean , highlight : String => String ) = {
341
- var curr : Char = 0
342
- val sb = new StringBuilder (s " $c" )
343
- while (remaining.nonEmpty && pred(curr)) {
344
- curr = takeChar()
345
- if (pred(curr)) sb += curr
346
- }
347
-
348
- val str = sb.toString
349
- val suffix = if (! pred(curr)) s " $curr" else " "
350
- newBuf append (highlight(str) + suffix)
351
- prev = curr
352
- }
353
-
354
- def appendTo (c : Char , pred : Char => Boolean , highlight : String => String ) = {
355
- var curr : Char = 0
356
- val sb = new StringBuilder (s " $c" )
357
- while (remaining.nonEmpty && ! pred(curr)) {
358
- curr = takeChar()
359
- sb += curr
360
- }
361
-
362
- newBuf append highlight(sb.toString)
363
- prev = curr
364
- }
365
-
366
- newBuf.toIterable
367
- }
368
-
369
31
private class NoReporter extends Reporter {
370
32
override def doReport (m : MessageContainer )(implicit ctx : Context ): Unit = ()
371
33
}
0 commit comments