232 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			GDScript
		
	
	
	
	
	
			
		
		
	
	
			232 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			GDScript
		
	
	
	
	
	
| @tool
 | |
| class_name DMSyntaxHighlighter extends SyntaxHighlighter
 | |
| 
 | |
| 
 | |
| var regex: DMCompilerRegEx = DMCompilerRegEx.new()
 | |
| var compilation: DMCompilation = DMCompilation.new()
 | |
| var expression_parser = DMExpressionParser.new()
 | |
| 
 | |
| var cache: Dictionary = {}
 | |
| 
 | |
| 
 | |
| func _clear_highlighting_cache() -> void:
 | |
| 	cache.clear()
 | |
| 
 | |
| 
 | |
| func _get_line_syntax_highlighting(line: int) -> Dictionary:
 | |
| 	expression_parser.include_comments = true
 | |
| 
 | |
| 	var colors: Dictionary = {}
 | |
| 	var text_edit: TextEdit = get_text_edit()
 | |
| 	var text: String = text_edit.get_line(line)
 | |
| 
 | |
| 	# Prevent an error from popping up while developing
 | |
| 	if not is_instance_valid(text_edit) or text_edit.theme_overrides.is_empty():
 | |
| 		return colors
 | |
| 
 | |
| 	# Disable this, as well as the line at the bottom of this function to remove the cache.
 | |
| 	if text in cache:
 | |
| 		return cache[text]
 | |
| 
 | |
| 	var theme: Dictionary = text_edit.theme_overrides
 | |
| 
 | |
| 	var index: int = 0
 | |
| 
 | |
| 	match DMCompiler.get_line_type(text):
 | |
| 		DMConstants.TYPE_USING:
 | |
| 			colors[index] = { color = theme.conditions_color }
 | |
| 			colors[index + "using ".length()] = { color = theme.text_color }
 | |
| 
 | |
| 		DMConstants.TYPE_IMPORT:
 | |
| 			colors[index] = { color = theme.conditions_color }
 | |
| 			var import: RegExMatch = regex.IMPORT_REGEX.search(text)
 | |
| 			if import:
 | |
| 				colors[index + import.get_start("path") - 1] = { color = theme.strings_color }
 | |
| 				colors[index + import.get_end("path") + 1] = { color = theme.conditions_color }
 | |
| 				colors[index + import.get_start("prefix")] = { color = theme.text_color }
 | |
| 
 | |
| 		DMConstants.TYPE_COMMENT:
 | |
| 			colors[index] = { color = theme.comments_color }
 | |
| 
 | |
| 		DMConstants.TYPE_TITLE:
 | |
| 			colors[index] = { color = theme.titles_color }
 | |
| 
 | |
| 		DMConstants.TYPE_CONDITION, DMConstants.TYPE_WHILE, DMConstants.TYPE_MATCH, DMConstants.TYPE_WHEN:
 | |
| 			colors[0] = { color = theme.conditions_color }
 | |
| 			index = text.find(" ")
 | |
| 			if index > -1:
 | |
| 				var expression: Array = expression_parser.tokenise(text.substr(index), DMConstants.TYPE_CONDITION, 0)
 | |
| 				if expression.size() == 0:
 | |
| 					colors[index] = { color = theme.critical_color }
 | |
| 				else:
 | |
| 					_highlight_expression(expression, colors, index)
 | |
| 
 | |
| 		DMConstants.TYPE_MUTATION:
 | |
| 			colors[0] = { color = theme.mutations_color }
 | |
| 			index = text.find(" ")
 | |
| 			var expression: Array = expression_parser.tokenise(text.substr(index), DMConstants.TYPE_MUTATION, 0)
 | |
| 			if expression.size() == 0:
 | |
| 				colors[index] = { color = theme.critical_color }
 | |
| 			else:
 | |
| 				_highlight_expression(expression, colors, index)
 | |
| 
 | |
| 		DMConstants.TYPE_GOTO:
 | |
| 			if text.strip_edges().begins_with("%"):
 | |
| 				colors[index] = { color = theme.symbols_color }
 | |
| 				index = text.find(" ")
 | |
| 			_highlight_goto(text, colors, index)
 | |
| 
 | |
| 		DMConstants.TYPE_RANDOM:
 | |
| 			colors[index] = { color = theme.symbols_color }
 | |
| 
 | |
| 		DMConstants.TYPE_DIALOGUE, DMConstants.TYPE_RESPONSE:
 | |
| 			if text.strip_edges().begins_with("%"):
 | |
| 				colors[index] = { color = theme.symbols_color }
 | |
| 				index = text.find(" ", text.find("%"))
 | |
| 			colors[index] = { color = theme.text_color.lerp(theme.symbols_color, 0.5) }
 | |
| 
 | |
| 			var dialogue_text: String = text.substr(index, text.find("=>"))
 | |
| 
 | |
| 			# Highlight character name (but ignore ":" within line ID reference)
 | |
| 			var split_index: int = dialogue_text.replace("\\:", "??").find(":")
 | |
| 			if text.substr(split_index - 3, 3) != "[ID":
 | |
| 				colors[index + split_index + 1] = { color = theme.text_color }
 | |
| 			else:
 | |
| 				# If there's no character name then just highlight the text as dialogue.
 | |
| 				colors[index] = { color = theme.text_color }
 | |
| 
 | |
| 			# Interpolation
 | |
| 			var replacements: Array[RegExMatch] = regex.REPLACEMENTS_REGEX.search_all(dialogue_text)
 | |
| 			for replacement: RegExMatch in replacements:
 | |
| 				var expression_text: String = replacement.get_string().substr(0, replacement.get_string().length() - 2).substr(2)
 | |
| 				var expression: Array = expression_parser.tokenise(expression_text, DMConstants.TYPE_MUTATION, replacement.get_start())
 | |
| 				var expression_index: int = index + replacement.get_start()
 | |
| 				colors[expression_index] = { color = theme.symbols_color }
 | |
| 				if expression.size() == 0 or expression[0].type == DMConstants.TYPE_ERROR:
 | |
| 					colors[expression_index] = { color = theme.critical_color }
 | |
| 				else:
 | |
| 					_highlight_expression(expression, colors, index + 2)
 | |
| 				colors[expression_index + expression_text.length() + 2] = { color = theme.symbols_color }
 | |
| 				colors[expression_index + expression_text.length() + 4] = { color = theme.text_color }
 | |
| 			# Tags (and inline mutations)
 | |
| 			var resolved_line_data: DMResolvedLineData = DMResolvedLineData.new("")
 | |
| 			var bbcodes: Array[Dictionary] = resolved_line_data.find_bbcode_positions_in_string(dialogue_text, true, true)
 | |
| 			for bbcode: Dictionary in bbcodes:
 | |
| 				var tag: String = bbcode.code
 | |
| 				var code: String = bbcode.raw_args
 | |
| 				if code.begins_with("["):
 | |
| 					colors[index + bbcode.start] = { color = theme.symbols_color }
 | |
| 					colors[index + bbcode.start + 2] = { color = theme.text_color }
 | |
| 					var pipe_cursor: int = code.find("|")
 | |
| 					while pipe_cursor > -1:
 | |
| 						colors[index + bbcode.start + pipe_cursor + 1] = { color = theme.symbols_color }
 | |
| 						colors[index + bbcode.start + pipe_cursor + 2] = { color = theme.text_color }
 | |
| 						pipe_cursor = code.find("|", pipe_cursor + 1)
 | |
| 					colors[index + bbcode.end - 1] = { color = theme.symbols_color }
 | |
| 					colors[index + bbcode.end + 1] = { color = theme.text_color }
 | |
| 				else:
 | |
| 					colors[index + bbcode.start] = { color = theme.symbols_color }
 | |
| 					if tag.begins_with("do") or tag.begins_with("set") or tag.begins_with("if"):
 | |
| 						if tag.begins_with("if"):
 | |
| 							colors[index + bbcode.start + 1] = { color = theme.conditions_color }
 | |
| 						else:
 | |
| 							colors[index + bbcode.start + 1] = { color = theme.mutations_color }
 | |
| 						var expression: Array = expression_parser.tokenise(code, DMConstants.TYPE_MUTATION, bbcode.start + bbcode.code.length())
 | |
| 						if expression.size() == 0 or expression[0].type == DMConstants.TYPE_ERROR:
 | |
| 							colors[index + bbcode.start + tag.length() + 1] = { color = theme.critical_color }
 | |
| 						else:
 | |
| 							_highlight_expression(expression, colors, index + 2)
 | |
| 					# else and closing if have no expression
 | |
| 					elif tag.begins_with("else") or tag.begins_with("/if"):
 | |
| 						colors[index + bbcode.start + 1] = { color = theme.conditions_color }
 | |
| 					colors[index + bbcode.end] = { color = theme.symbols_color }
 | |
| 					colors[index + bbcode.end + 1] = { color = theme.text_color }
 | |
| 			# Jumps
 | |
| 			if "=> " in text or "=>< " in text:
 | |
| 				_highlight_goto(text, colors, index)
 | |
| 
 | |
| 	# Order the dictionary keys to prevent CodeEdit from having issues
 | |
| 	var ordered_colors: Dictionary = {}
 | |
| 	var ordered_keys: Array = colors.keys()
 | |
| 	ordered_keys.sort()
 | |
| 	for key_index: int in ordered_keys:
 | |
| 		ordered_colors[key_index] = colors[key_index]
 | |
| 
 | |
| 	cache[text] = ordered_colors
 | |
| 	return ordered_colors
 | |
| 
 | |
| 
 | |
| func _highlight_expression(tokens: Array, colors: Dictionary, index: int) -> int:
 | |
| 	var theme: Dictionary = get_text_edit().theme_overrides
 | |
| 	var last_index: int = index
 | |
| 	for token: Dictionary in tokens:
 | |
| 		last_index = token.i
 | |
| 		match token.type:
 | |
| 			DMConstants.TOKEN_COMMENT:
 | |
| 				colors[index + token.i] = { color = theme.comments_color }
 | |
| 
 | |
| 			DMConstants.TOKEN_CONDITION, DMConstants.TOKEN_AND_OR:
 | |
| 				colors[index + token.i] = { color = theme.conditions_color }
 | |
| 
 | |
| 			DMConstants.TOKEN_VARIABLE:
 | |
| 				if token.value in ["true", "false"]:
 | |
| 					colors[index + token.i] = { color = theme.conditions_color }
 | |
| 				else:
 | |
| 					colors[index + token.i] = { color = theme.members_color }
 | |
| 
 | |
| 			DMConstants.TOKEN_OPERATOR, DMConstants.TOKEN_COLON, \
 | |
| 			DMConstants.TOKEN_COMMA, DMConstants.TOKEN_DOT, DMConstants.TOKEN_NULL_COALESCE, \
 | |
| 			DMConstants.TOKEN_NUMBER, DMConstants.TOKEN_ASSIGNMENT:
 | |
| 				colors[index + token.i] = { color = theme.symbols_color }
 | |
| 
 | |
| 			DMConstants.TOKEN_STRING:
 | |
| 				colors[index + token.i] = { color = theme.strings_color }
 | |
| 
 | |
| 			DMConstants.TOKEN_FUNCTION:
 | |
| 				colors[index + token.i] = { color = theme.mutations_color }
 | |
| 				colors[index + token.i + token.function.length()] = { color = theme.symbols_color }
 | |
| 				for parameter: Array in token.value:
 | |
| 					last_index = _highlight_expression(parameter, colors, index)
 | |
| 			DMConstants.TOKEN_PARENS_CLOSE:
 | |
| 				colors[index + token.i] = { color = theme.symbols_color }
 | |
| 
 | |
| 			DMConstants.TOKEN_DICTIONARY_REFERENCE:
 | |
| 				colors[index + token.i] = { color = theme.members_color }
 | |
| 				colors[index + token.i + token.variable.length()] = { color = theme.symbols_color }
 | |
| 				last_index = _highlight_expression(token.value, colors, index)
 | |
| 			DMConstants.TOKEN_ARRAY:
 | |
| 				colors[index + token.i] = { color = theme.symbols_color }
 | |
| 				for item: Array in token.value:
 | |
| 					last_index = _highlight_expression(item, colors, index)
 | |
| 			DMConstants.TOKEN_BRACKET_CLOSE:
 | |
| 				colors[index + token.i] = { color = theme.symbols_color }
 | |
| 
 | |
| 			DMConstants.TOKEN_DICTIONARY:
 | |
| 				colors[index + token.i] = { color = theme.symbols_color }
 | |
| 				last_index = _highlight_expression(token.value.keys() + token.value.values(), colors, index)
 | |
| 			DMConstants.TOKEN_BRACE_CLOSE:
 | |
| 				colors[index + token.i] = { color = theme.symbols_color }
 | |
| 				last_index += 1
 | |
| 
 | |
| 			DMConstants.TOKEN_GROUP:
 | |
| 				last_index = _highlight_expression(token.value, colors, index)
 | |
| 
 | |
| 	return last_index
 | |
| 
 | |
| 
 | |
| func _highlight_goto(text: String, colors: Dictionary, index: int) -> int:
 | |
| 	var theme: Dictionary = get_text_edit().theme_overrides
 | |
| 	var goto_data: DMResolvedGotoData = DMResolvedGotoData.new(text, {})
 | |
| 	colors[goto_data.index] = { color = theme.jumps_color }
 | |
| 	if "{{" in text:
 | |
| 		index = text.find("{{", goto_data.index)
 | |
| 		var last_index: int = 0
 | |
| 		if goto_data.error:
 | |
| 			colors[index + 2] = { color = theme.critical_color }
 | |
| 		else:
 | |
| 			last_index = _highlight_expression(goto_data.expression, colors, index)
 | |
| 		index = text.find("}}", index + last_index)
 | |
| 		colors[index] = { color = theme.jumps_color }
 | |
| 
 | |
| 	return index
 |