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