2 extends SyntaxHighlighter
4 ## Syntax highlighter for the dialogic text timeline editor and text events in the visual editor.
6 enum Modes {TEXT_EVENT_ONLY, FULL_HIGHLIGHTING}
7 var mode := Modes.FULL_HIGHLIGHTING
11 var word_regex := RegEx.new()
12 var region_regex := RegEx.new()
13 var number_regex := RegEx.create_from_string(r"(\d|\.)+")
14 var shortcode_regex := RegEx.create_from_string(r"\W*\[(?<id>\w*)(?<args>[^\]]*)?")
15 var shortcode_param_regex := RegEx.create_from_string(r'((?<parameter>[^\s=]*)\s*=\s*"(?<value>([^=]|\\=)*)(?<!\\)")')
18 var normal_color: Color
19 var translation_id_color: Color
21 var code_flow_color: Color
22 var boolean_operator_color: Color
23 var variable_color: Color
24 var string_color: Color
25 var character_name_color: Color
26 var character_portrait_color: Color
28 var shortcode_events := {}
29 var custom_syntax_events := []
30 var text_event: DialogicTextEvent = null
35 DialogicUtil.get_dialogic_plugin().get_editor_interface().get_base_control().theme_changed.connect(update_colors)
38 func update_colors() -> void:
39 if not DialogicUtil.get_dialogic_plugin():
41 var editor_settings: EditorSettings = DialogicUtil.get_dialogic_plugin().get_editor_interface().get_editor_settings()
42 normal_color = editor_settings.get('text_editor/theme/highlighting/text_color')
43 translation_id_color = editor_settings.get('text_editor/theme/highlighting/comment_color')
45 code_flow_color = editor_settings.get("text_editor/theme/highlighting/control_flow_keyword_color")
46 boolean_operator_color = code_flow_color.lightened(0.5)
47 variable_color = editor_settings.get('text_editor/theme/highlighting/engine_type_color')
48 string_color = editor_settings.get('text_editor/theme/highlighting/string_color')
49 character_name_color = editor_settings.get('text_editor/theme/highlighting/symbol_color').lerp(normal_color, 0.3)
50 character_portrait_color = character_name_color.lerp(normal_color, 0.5)
53 func _get_line_syntax_highlighting(line:int) -> Dictionary:
54 var str_line := get_text_edit().get_line(line)
56 if shortcode_events.is_empty():
57 for event in DialogicResourceUtil.get_event_cache():
58 if event.get_shortcode() != 'default_shortcode':
59 shortcode_events[event.get_shortcode()] = event
61 custom_syntax_events.append(event)
62 if event is DialogicTextEvent:
64 text_event.load_text_effects()
67 dict[0] = {'color':normal_color}
69 dict = color_translation_id(dict, str_line)
71 if mode == Modes.FULL_HIGHLIGHTING:
72 if line_is_shortcode_event(line):
73 var full_event := get_full_event(line)
74 var result := shortcode_regex.search(full_event)
76 if result.get_string('id') in shortcode_events:
77 if full_event.begins_with(str_line):
78 dict[result.get_start('id')] = {"color":shortcode_events[result.get_string('id')].event_color.lerp(normal_color, 0.4)}
79 dict[result.get_end('id')] = {"color":normal_color}
81 if result.get_string('args'):
82 color_shortcode_content(dict, str_line, result.get_start('args'), result.get_end('args'), shortcode_events[result.get_string('id')].event_color)
84 color_shortcode_content(dict, str_line, 0, 0, shortcode_events[result.get_string('id')].event_color)
88 for event in custom_syntax_events:
89 if event.is_valid_event(str_line.strip_edges()):
90 dict = event._get_syntax_highlighting(self, dict, str_line)
94 dict = text_event._get_syntax_highlighting(self, dict, str_line)
98 func line_is_shortcode_event(line_idx:int) -> bool:
99 var str_line := get_text_edit().get_line(line_idx)
100 if text_event.text_effects_regex.search(str_line.get_slice(' ', 0)):
103 if str_line.strip_edges().begins_with("["):
106 if line_idx > 0 and get_text_edit().get_line(line_idx-1).ends_with('\\'):
107 return line_is_shortcode_event(line_idx-1)
112 func get_full_event(line_idx:int) -> String:
113 var str_line := get_text_edit().get_line(line_idx)
116 while get_text_edit().get_line(line_idx-offset).ends_with('\\'):
117 str_line = get_text_edit().get_line(line_idx-offset).trim_suffix('\\')+"\n"+str_line
120 # This is commented out, as it is not needed right now.
121 # However without it, this isn't actually the full event.
122 # Might need to be included some day.
124 ## Add following lines
125 #while get_text_edit().get_line(line_idx+offset).ends_with('\\'):
126 #str_line = str_line.trim_suffix('\\')+"\n"+get_text_edit().get_line(line_idx+offset)
131 func fix_dict(dict:Dictionary) -> Dictionary:
140 func color_condition(dict:Dictionary, line:String, from:int = 0, to:int = 0) -> Dictionary:
141 dict = color_word(dict, code_flow_color, line, 'or', from, to)
142 dict = color_word(dict, code_flow_color, line, 'and', from, to)
143 dict = color_word(dict, code_flow_color, line, '==', from, to)
144 dict = color_word(dict, code_flow_color, line, '!=', from, to)
146 dict = color_word(dict, code_flow_color, line, '>', from, to)
148 dict = color_word(dict, code_flow_color, line, '>=', from, to)
150 dict = color_word(dict, code_flow_color, line, '<', from, to)
152 dict = color_word(dict, code_flow_color, line, '<=', from, to)
153 dict = color_region(dict, variable_color, line, '{', '}', from, to)
154 dict = color_region(dict, string_color, line, '"', '"', from, to)
160 func color_translation_id(dict:Dictionary, line:String) -> Dictionary:
161 dict = color_region(dict, translation_id_color, line, '#id:', '')
165 func color_word(dict:Dictionary, color:Color, line:String, word:String, from:int= 0, to:int = 0) -> Dictionary:
166 word_regex.compile("\\W(?<word>"+word+")\\W")
169 for i in word_regex.search_all(line.substr(from, to-from+2)):
170 dict[i.get_start('word')+from] = {'color':color}
171 dict[i.get_end('word')+from] = {'color':normal_color}
175 func color_region(dict:Dictionary, color:Color, line:String, start:String, end:String, from:int = 0, to:int = 0, base_color:Color=normal_color) -> Dictionary:
182 region_regex.compile("(?<!\\\\)"+start+".*")
184 region_regex.compile("(?<!\\\\)"+start+"((?!"+end+").)*"+end)
187 for region in region_regex.search_all(line.substr(from, to-from+2)):
188 dict[region.get_start()+from] = {'color':color}
189 dict[region.get_end()+from] = {'color':base_color}
193 func color_shortcode_content(dict:Dictionary, line:String, from:int = 0, to:int = 0, base_color:=normal_color) -> Dictionary:
196 var args_result := shortcode_param_regex.search_all(line.substr(from, to-from+2))
197 for x in args_result:
198 dict[x.get_start()+from] = {"color":base_color.lerp(normal_color, 0.5)}
199 dict[x.get_start('value')+from-1] = {"color":base_color.lerp(normal_color, 0.7)}
200 dict[x.get_end()+from] = {"color":normal_color}