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