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