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