1     object TextColor =
2         val regular = FontColor.black
3         val highlighted = FontColor.blue
4         val quoted = FontColor.green
5         val attribute = FontColor.gray
6         val commented = FontColor.light_gray
7         val failure = FontColor.red
8     
9     let word_end_chars : Set<Char> =
10        [' ', '\n', '(', ')', '[', ']', '<', '>', '.', ',', ':', ';', '/']
11    
12    let update_line_colors (line : TextLine) =
13        let word = StringBuilder.new
14        let mut is_double_quoted = false
15        let mut is_single_quoted = false
16        let mut is_commented_single_line = false
17        let mut commented_multiline = line.commented_in
18        let mut is_attribute_brackets = false
19        for index = 0 until line.length do
20            let c = line[index].char
21            let maybe_prev_c = if index == 0 then None else Some line[index - 1].char
22            let maybe_prev_prev_c = if index < 2 then None else Some line[index - 2].char
23            let maybe_next_c = if index == line.length - 1
24                               then None
25                               else Some line[index + 1].char
26    
27            if is_commented_single_line then
28                line.set_color index TextColor.commented
29    
30            else if commented_multiline > 0 then
31                line.set_color index TextColor.commented
32                if c == '/' && maybe_prev_c == Some '*' then
33                    commented_multiline -= 1
34    
35                else if c == '/' && maybe_next_c == Some '*' then
36                    commented_multiline += 1
37    
38            else if (c == '/' && maybe_next_c == Some '/' || c == '#')
39                    && not is_double_quoted && not is_single_quoted
40            then
41                is_commented_single_line = true
42                line.set_color index TextColor.commented
43    
44            else if c == '/' && maybe_next_c == Some '*' then
45                commented_multiline += 1
46                line.set_color index TextColor.commented
47    
48            else if c == '"' && not is_single_quoted then
49                is_double_quoted = not is_double_quoted
50                if is_double_quoted then
51                    for i = 0 until word.length do
52                        line.set_color (index - i - 1) TextColor.regular
53    
54                    word.clear
55    
56                word.append c
57                if not is_double_quoted then
58                    for i = 0 until word.length do
59                        line.set_color (index - i) TextColor.quoted
60    
61                    word.clear
62    
63            else if c == '\'' && not is_double_quoted
64                    && (maybe_prev_c <> Some '\\' || maybe_prev_prev_c == Some '\\')
65            then
66                is_single_quoted = not is_single_quoted
67                if is_single_quoted then
68                    for i = 0 until word.length do
69                        line.set_color (index - i - 1) TextColor.regular
70    
71                    word.clear
72    
73                word.append c
74                if not is_single_quoted then
75                    for i = 0 until word.length do
76                        line.set_color (index - i) TextColor.quoted
77    
78                    word.clear
79    
80            else if c == '@' && maybe_next_c == Some '[' then
81                word.append c
82                is_attribute_brackets = true
83    
84            else if is_attribute_brackets then
85                word.append c
86                if c == ']' then
87                    is_attribute_brackets = false
88    
89            else if word_end_chars.contains c && not is_double_quoted
90                    && not is_single_quoted
91            then
92                if word.is_not_empty then
93                    let t = word.as_string
94                    let mut color = case t of
95                        "let" | "local" | "val" | "var" | "def" | "fun" | "mut"
96                        | "if" | "then" | "else" | "do" | "case" | "of" | "is"
97                        | "while" | "repeat" | "until" | "for" | "downto"
98                        | "u8" | "u16" | "u32" | "i32" | "f32" | "u64" | "i64"
99                        | "f64" | "bool"
100                       | "where" | "assert" | "return" | "break" | "continue"
101                       | "f@" | "t@" ->
102                           TextColor.highlighted
103   
104                       "as" if c == ' ' -> TextColor.highlighted
105   
106                       "type" | "object" | "class" | "mixin" | "typealias"
107                       | "import" | "module"
108                       | "endmodule" if index - word.length == 0 ->
109                           TextColor.highlighted
110   
111                       "@mut" | "@c" | "@cpp" | "@abstract"
112                       | "@byval" | "@move" | "@data" | "@open" | "@literal"
113                       | "@decl" | "@impl" | "@secondary" | "@optional"
114                       | "@param" | "@owner" | "@auto" | "@publish" | "@dst"
115                       | "@late" | "@vararg" | "@restrict" | "@nonnull" ->
116                           TextColor.attribute
117   
118                       _ if t.starts_with "@[" && t.ends_with ']' -> TextColor.attribute
119   
120                       else -> TextColor.regular
121   
122                   let start = index - word.length
123                   for i = 0 until word.length do
124                       line.set_color (start + i) color
125   
126                       if word[i] == '@'
127                          && (i <> 0 || index > word.length
128                                        && line[index - word.length - 1].char == ']')
129                       then
130                           val u = t.drop (i + 1)
131                           let colorize = case u of
132                               "ptr" | "mut_ptr" | "ref" | "mut_ref" | "zero"
133                               | "atom" | "fn" | "value" | "to_ref" | "obs"
134                               | "null" | "all" | "type" | "is_set" | "is_unset"
135                               | "copy" -> true
136   
137                               else -> false
138   
139                           if colorize then
140                               line.set_color (start + i) TextColor.attribute
141   
142                   word.clear
143   
144               line.set_color index TextColor.regular
145   
146           else if c == '\n' && is_double_quoted then
147               for i = 0 until word.length do
148                   line.set_color (index - i - 1) TextColor.quoted
149           else
150               word.append c
151   
152       if is_attribute_brackets then
153           for i = line.length - word.length until line.length do
154               line.set_color i TextColor.regular
155   
156       line.commented_out = commented_multiline
157   
158   def update_colors_for_line (text : Text) (line_index : u32) : Unit =
159       if text.lines.size > line_index then
160           let line = text.lines[line_index]
161           let prev_is_commented_out = line.commented_out
162           update_line_colors line
163           if prev_is_commented_out <> line.commented_out
164              && text.lines.size > line_index + 1
165           then
166               text.lines[line_index + 1].commented_in = line.commented_out
167               update_colors_for_line text (line_index + 1)
168   
169   def update_colors (text : Text
170                      from_index : u32
171                      to_index : u32) =
172       if from_index == to_index then
173           update_colors_for_line text from_index
174       else
175           for line_index = from_index until to_index do
176               let line = text.lines[line_index]
177               update_line_colors line
178               if text.lines.size > line_index + 1 then
179                   let next_line = text.lines[line_index + 1]
180                   let prev_is_commented_in = next_line.commented_in
181                   next_line.commented_in = line.commented_out
182                   if line_index == to_index - 1
183                      && prev_is_commented_in <> next_line.commented_in
184                   then
185                       update_colors_for_line text (line_index + 1)
186