Parcourir la source

Extract Syntax highlighting from TextEdit and add EditorSyntaxHighlighter
- Extacted all syntax highlighting code from text edit
- Removed enable syntax highlighting from text edit
- Added line_edited_from signal to text_edit
- Renamed get/set_syntax_highlighting to get/set_syntax_highlighter
- Added EditorSyntaxHighligher

Paulb23 il y a 5 ans
Parent
commit
bc4cee4458

+ 1 - 0
core/script_language.h

@@ -256,6 +256,7 @@ struct ScriptCodeCompletionOption {
 	Kind kind = KIND_PLAIN_TEXT;
 	String display;
 	String insert_text;
+	Color font_color;
 	RES icon;
 
 	ScriptCodeCompletionOption() {}

+ 14 - 7
editor/code_editor.cpp

@@ -837,7 +837,14 @@ void CodeTextEditor::_complete_request() {
 	}
 
 	for (List<ScriptCodeCompletionOption>::Element *E = entries.front(); E; E = E->next()) {
-		E->get().icon = _get_completion_icon(E->get());
+		ScriptCodeCompletionOption *e = &E->get();
+		e->icon = _get_completion_icon(*e);
+		e->font_color = completion_font_color;
+		if (e->insert_text.begins_with("\"") || e->insert_text.begins_with("\'")) {
+			e->font_color = completion_string_color;
+		} else if (e->insert_text.begins_with("#") || e->insert_text.begins_with("//")) {
+			e->font_color = completion_comment_color;
+		}
 	}
 	text_editor->code_complete(entries, forced);
 }
@@ -910,7 +917,10 @@ bool CodeTextEditor::_add_font_size(int p_delta) {
 }
 
 void CodeTextEditor::update_editor_settings() {
-	text_editor->set_syntax_coloring(EditorSettings::get_singleton()->get("text_editor/highlighting/syntax_highlighting"));
+	completion_font_color = EDITOR_GET("text_editor/highlighting/completion_font_color");
+	completion_string_color = EDITOR_GET("text_editor/highlighting/string_color");
+	completion_comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
+
 	text_editor->set_highlight_all_occurrences(EditorSettings::get_singleton()->get("text_editor/highlighting/highlight_all_occurrences"));
 	text_editor->set_highlight_current_line(EditorSettings::get_singleton()->get("text_editor/highlighting/highlight_current_line"));
 	text_editor->set_indent_using_spaces(EditorSettings::get_singleton()->get("text_editor/indent/type"));
@@ -1407,11 +1417,8 @@ Variant CodeTextEditor::get_edit_state() {
 	state["breakpoints"] = text_editor->get_breakpoints_array();
 	state["bookmarks"] = text_editor->get_bookmarks_array();
 
-	state["syntax_highlighter"] = TTR("Standard");
-	Ref<SyntaxHighlighter> syntax_highlighter = text_editor->get_syntax_highlighting();
-	if (syntax_highlighter.is_valid()) {
-		state["syntax_highlighter"] = syntax_highlighter->_get_name();
-	}
+	Ref<EditorSyntaxHighlighter> syntax_highlighter = text_editor->get_syntax_highlighter();
+	state["syntax_highlighter"] = syntax_highlighter->_get_name();
 
 	return state;
 }

+ 3 - 0
editor/code_editor.h

@@ -174,6 +174,9 @@ class CodeTextEditor : public VBoxContainer {
 	void _zoom_changed();
 	void _reset_zoom();
 
+	Color completion_font_color;
+	Color completion_string_color;
+	Color completion_comment_color;
 	CodeTextEditorCodeCompleteFunc code_complete_func;
 	void *code_complete_ud;
 

+ 1 - 0
editor/editor_node.cpp

@@ -3603,6 +3603,7 @@ void EditorNode::register_editor_types() {
 	ClassDB::register_class<EditorVCSInterface>();
 	ClassDB::register_virtual_class<ScriptEditor>();
 	ClassDB::register_virtual_class<ScriptEditorBase>();
+	ClassDB::register_class<EditorSyntaxHighlighter>();
 	ClassDB::register_virtual_class<EditorInterface>();
 	ClassDB::register_class<EditorExportPlugin>();
 	ClassDB::register_class<EditorResourceConversionPlugin>();

+ 0 - 1
editor/editor_settings.cpp

@@ -420,7 +420,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 	_load_default_text_editor_theme();
 
 	// Highlighting
-	_initial_set("text_editor/highlighting/syntax_highlighting", true);
 	_initial_set("text_editor/highlighting/highlight_all_occurrences", true);
 	_initial_set("text_editor/highlighting/highlight_current_line", true);
 	_initial_set("text_editor/highlighting/highlight_type_safe_lines", true);

+ 166 - 3
editor/plugins/script_editor_plugin.cpp

@@ -51,6 +51,165 @@
 #include "servers/display_server.h"
 #include "text_editor.h"
 
+/*** SYNTAX HIGHLIGHTER ****/
+
+String EditorSyntaxHighlighter::_get_name() const {
+	ScriptInstance *si = get_script_instance();
+	if (si && si->has_method("_get_name")) {
+		return si->call("_get_name");
+	}
+	return "Unnamed";
+}
+
+Array EditorSyntaxHighlighter::_get_supported_languages() const {
+	ScriptInstance *si = get_script_instance();
+	if (si && si->has_method("_get_supported_languages")) {
+		return si->call("_get_supported_languages");
+	}
+	return Array();
+}
+
+Ref<EditorSyntaxHighlighter> EditorSyntaxHighlighter::_create() const {
+	Ref<EditorSyntaxHighlighter> syntax_highlighter;
+	syntax_highlighter.instance();
+	if (get_script_instance()) {
+		syntax_highlighter->set_script(get_script_instance()->get_script());
+	}
+	return syntax_highlighter;
+}
+
+void EditorSyntaxHighlighter::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("_get_edited_resource"), &EditorSyntaxHighlighter::_get_edited_resource);
+
+	BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_name"));
+	BIND_VMETHOD(MethodInfo(Variant::ARRAY, "_get_supported_languages"));
+	BIND_VMETHOD(MethodInfo(Variant::ARRAY, "_get_supported_extentions"));
+}
+
+////
+
+void EditorStandardSyntaxHighlighter::_update_cache() {
+	highlighter->set_text_edit(text_edit);
+	highlighter->clear_keyword_colors();
+	highlighter->clear_member_keyword_colors();
+	highlighter->clear_color_regions();
+
+	highlighter->set_symbol_color(EDITOR_GET("text_editor/highlighting/symbol_color"));
+	highlighter->set_function_color(EDITOR_GET("text_editor/highlighting/function_color"));
+	highlighter->set_number_color(EDITOR_GET("text_editor/highlighting/number_color"));
+	highlighter->set_member_variable_color(EDITOR_GET("text_editor/highlighting/member_variable_color"));
+
+	/* Engine types. */
+	const Color type_color = EDITOR_GET("text_editor/highlighting/engine_type_color");
+	List<StringName> types;
+	ClassDB::get_class_list(&types);
+	for (List<StringName>::Element *E = types.front(); E; E = E->next()) {
+		String n = E->get();
+		if (n.begins_with("_")) {
+			n = n.substr(1, n.length());
+		}
+		highlighter->add_keyword_color(n, type_color);
+	}
+
+	/* User types. */
+	const Color usertype_color = EDITOR_GET("text_editor/highlighting/user_type_color");
+	List<StringName> global_classes;
+	ScriptServer::get_global_class_list(&global_classes);
+	for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
+		highlighter->add_keyword_color(E->get(), usertype_color);
+	}
+
+	/* Autoloads. */
+	Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+	for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+		const ProjectSettings::AutoloadInfo &info = E->value();
+		if (info.is_singleton) {
+			highlighter->add_keyword_color(info.name, usertype_color);
+		}
+	}
+
+	const Ref<Script> script = _get_edited_resource();
+	if (script.is_valid()) {
+		/* Core types. */
+		const Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color");
+		List<String> core_types;
+		script->get_language()->get_core_type_words(&core_types);
+		for (List<String>::Element *E = core_types.front(); E; E = E->next()) {
+			highlighter->add_keyword_color(E->get(), basetype_color);
+		}
+
+		/* Reserved words. */
+		const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+		List<String> keywords;
+		script->get_language()->get_reserved_words(&keywords);
+		for (List<String>::Element *E = keywords.front(); E; E = E->next()) {
+			highlighter->add_keyword_color(E->get(), keyword_color);
+		}
+
+		/* Member types. */
+		const Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+		StringName instance_base = script->get_instance_base_type();
+		if (instance_base != StringName()) {
+			List<PropertyInfo> plist;
+			ClassDB::get_property_list(instance_base, &plist);
+			for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+				String name = E->get().name;
+				if (E->get().usage & PROPERTY_USAGE_CATEGORY || E->get().usage & PROPERTY_USAGE_GROUP || E->get().usage & PROPERTY_USAGE_SUBGROUP) {
+					continue;
+				}
+				if (name.find("/") != -1) {
+					continue;
+				}
+				highlighter->add_member_keyword_color(name, member_variable_color);
+			}
+
+			List<String> clist;
+			ClassDB::get_integer_constant_list(instance_base, &clist);
+			for (List<String>::Element *E = clist.front(); E; E = E->next()) {
+				highlighter->add_member_keyword_color(E->get(), member_variable_color);
+			}
+		}
+
+		/* Comments */
+		const Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
+		List<String> comments;
+		script->get_language()->get_comment_delimiters(&comments);
+		for (List<String>::Element *E = comments.front(); E; E = E->next()) {
+			String comment = E->get();
+			String beg = comment.get_slice(" ", 0);
+			String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String();
+			highlighter->add_color_region(beg, end, comment_color, end == "");
+		}
+
+		/* Strings */
+		const Color string_color = EDITOR_GET("text_editor/highlighting/string_color");
+		List<String> strings;
+		script->get_language()->get_string_delimiters(&strings);
+		for (List<String>::Element *E = strings.front(); E; E = E->next()) {
+			String string = E->get();
+			String beg = string.get_slice(" ", 0);
+			String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String();
+			highlighter->add_color_region(beg, end, string_color, end == "");
+		}
+	}
+}
+
+Ref<EditorSyntaxHighlighter> EditorStandardSyntaxHighlighter::_create() const {
+	Ref<EditorStandardSyntaxHighlighter> syntax_highlighter;
+	syntax_highlighter.instance();
+	return syntax_highlighter;
+}
+
+////
+
+Ref<EditorSyntaxHighlighter> EditorPlainTextSyntaxHighlighter::_create() const {
+	Ref<EditorPlainTextSyntaxHighlighter> syntax_highlighter;
+	syntax_highlighter.instance();
+	return syntax_highlighter;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 /*** SCRIPT EDITOR ****/
 
 void ScriptEditorBase::_bind_methods() {
@@ -2022,7 +2181,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
 	if (p_resource->get_class_name() != StringName("VisualScript")) {
 		bool highlighter_set = false;
 		for (int i = 0; i < syntax_highlighters.size(); i++) {
-			Ref<SyntaxHighlighter> highlighter = syntax_highlighters[i]->_create();
+			Ref<EditorSyntaxHighlighter> highlighter = syntax_highlighters[i]->_create();
 			if (highlighter.is_null()) {
 				continue;
 			}
@@ -2830,13 +2989,17 @@ void ScriptEditor::_open_script_request(const String &p_path) {
 	}
 }
 
-void ScriptEditor::register_syntax_highlighter(const Ref<SyntaxHighlighter> &p_syntax_highlighter) {
+void ScriptEditor::register_syntax_highlighter(const Ref<EditorSyntaxHighlighter> &p_syntax_highlighter) {
+	ERR_FAIL_COND(p_syntax_highlighter.is_null());
+
 	if (syntax_highlighters.find(p_syntax_highlighter) == -1) {
 		syntax_highlighters.push_back(p_syntax_highlighter);
 	}
 }
 
-void ScriptEditor::unregister_syntax_highlighter(const Ref<SyntaxHighlighter> &p_syntax_highlighter) {
+void ScriptEditor::unregister_syntax_highlighter(const Ref<EditorSyntaxHighlighter> &p_syntax_highlighter) {
+	ERR_FAIL_COND(p_syntax_highlighter.is_null());
+
 	syntax_highlighters.erase(p_syntax_highlighter);
 }
 

+ 52 - 6
editor/plugins/script_editor_plugin.h

@@ -47,6 +47,53 @@
 #include "scene/main/timer.h"
 #include "scene/resources/text_file.h"
 
+class EditorSyntaxHighlighter : public SyntaxHighlighter {
+	GDCLASS(EditorSyntaxHighlighter, SyntaxHighlighter)
+
+private:
+	REF edited_resourse;
+
+protected:
+	static void _bind_methods();
+
+public:
+	virtual String _get_name() const;
+	virtual Array _get_supported_languages() const;
+
+	void _set_edited_resource(const RES &p_res) { edited_resourse = p_res; }
+	REF _get_edited_resource() { return edited_resourse; }
+
+	virtual Ref<EditorSyntaxHighlighter> _create() const;
+};
+
+class EditorStandardSyntaxHighlighter : public EditorSyntaxHighlighter {
+	GDCLASS(EditorStandardSyntaxHighlighter, EditorSyntaxHighlighter)
+
+private:
+	Ref<CodeHighlighter> highlighter;
+
+public:
+	virtual void _update_cache() override;
+	virtual Dictionary _get_line_syntax_highlighting(int p_line) override { return highlighter->get_line_syntax_highlighting(p_line); }
+
+	virtual String _get_name() const override { return TTR("Standard"); }
+
+	virtual Ref<EditorSyntaxHighlighter> _create() const override;
+
+	EditorStandardSyntaxHighlighter() { highlighter.instance(); }
+};
+
+class EditorPlainTextSyntaxHighlighter : public EditorSyntaxHighlighter {
+	GDCLASS(EditorPlainTextSyntaxHighlighter, EditorSyntaxHighlighter)
+
+public:
+	virtual String _get_name() const override { return TTR("Plain Text"); }
+
+	virtual Ref<EditorSyntaxHighlighter> _create() const override;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
 class ScriptEditorQuickOpen : public ConfirmationDialog {
 	GDCLASS(ScriptEditorQuickOpen, ConfirmationDialog);
 
@@ -80,8 +127,8 @@ protected:
 	static void _bind_methods();
 
 public:
-	virtual void add_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) = 0;
-	virtual void set_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) = 0;
+	virtual void add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) = 0;
+	virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) = 0;
 
 	virtual void apply_code() = 0;
 	virtual RES get_edited_resource() const = 0;
@@ -120,7 +167,6 @@ public:
 	ScriptEditorBase() {}
 };
 
-typedef SyntaxHighlighter *(*CreateSyntaxHighlighterFunc)();
 typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const RES &p_resource);
 
 class EditorScriptCodeCompletionCache;
@@ -241,7 +287,7 @@ class ScriptEditor : public PanelContainer {
 	static int script_editor_func_count;
 	static CreateScriptEditorFunc script_editor_funcs[SCRIPT_EDITOR_FUNC_MAX];
 
-	Vector<Ref<SyntaxHighlighter> > syntax_highlighters;
+	Vector<Ref<EditorSyntaxHighlighter>> syntax_highlighters;
 
 	struct ScriptHistory {
 		Control *control;
@@ -440,8 +486,8 @@ public:
 
 	void set_live_auto_reload_running_scripts(bool p_enabled);
 
-	void register_syntax_highlighter(const Ref<SyntaxHighlighter> &p_syntax_highlighter);
-	void unregister_syntax_highlighter(const Ref<SyntaxHighlighter> &p_syntax_highlighter);
+	void register_syntax_highlighter(const Ref<EditorSyntaxHighlighter> &p_syntax_highlighter);
+	void unregister_syntax_highlighter(const Ref<EditorSyntaxHighlighter> &p_syntax_highlighter);
 
 	static void register_create_script_editor_function(CreateScriptEditorFunc p_func);
 

+ 41 - 144
editor/plugins/script_text_editor.cpp

@@ -132,7 +132,7 @@ void ScriptTextEditor::apply_code() {
 	}
 	script->set_source_code(code_editor->get_text_edit()->get_text());
 	script->update_exports();
-	_update_member_keywords();
+	code_editor->get_text_edit()->get_syntax_highlighter()->update_cache();
 }
 
 RES ScriptTextEditor::get_edited_resource() const {
@@ -155,43 +155,9 @@ void ScriptTextEditor::set_edited_resource(const RES &p_res) {
 	_validate_script();
 }
 
-void ScriptTextEditor::_update_member_keywords() {
-	member_keywords.clear();
-	code_editor->get_text_edit()->clear_member_keywords();
-	Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
-
-	StringName instance_base = script->get_instance_base_type();
-
-	if (instance_base == StringName()) {
-		return;
-	}
-	List<PropertyInfo> plist;
-	ClassDB::get_property_list(instance_base, &plist);
-
-	for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
-		String name = E->get().name;
-		if (E->get().usage & PROPERTY_USAGE_CATEGORY || E->get().usage & PROPERTY_USAGE_GROUP || E->get().usage & PROPERTY_USAGE_SUBGROUP) {
-			continue;
-		}
-		if (name.find("/") != -1) {
-			continue;
-		}
-
-		code_editor->get_text_edit()->add_member_keyword(name, member_variable_color);
-	}
-
-	List<String> clist;
-	ClassDB::get_integer_constant_list(instance_base, &clist);
-
-	for (List<String>::Element *E = clist.front(); E; E = E->next()) {
-		code_editor->get_text_edit()->add_member_keyword(E->get(), member_variable_color);
-	}
-}
-
 void ScriptTextEditor::_load_theme_settings() {
 	TextEdit *text_edit = code_editor->get_text_edit();
-
-	text_edit->clear_colors();
+	text_edit->clear_keywords();
 
 	Color background_color = EDITOR_GET("text_editor/highlighting/background_color");
 	Color completion_background_color = EDITOR_GET("text_editor/highlighting/completion_background_color");
@@ -210,9 +176,6 @@ void ScriptTextEditor::_load_theme_settings() {
 	Color current_line_color = EDITOR_GET("text_editor/highlighting/current_line_color");
 	Color line_length_guideline_color = EDITOR_GET("text_editor/highlighting/line_length_guideline_color");
 	Color word_highlighted_color = EDITOR_GET("text_editor/highlighting/word_highlighted_color");
-	Color number_color = EDITOR_GET("text_editor/highlighting/number_color");
-	Color function_color = EDITOR_GET("text_editor/highlighting/function_color");
-	Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
 	Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color");
 	Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color");
 	Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color");
@@ -220,13 +183,6 @@ void ScriptTextEditor::_load_theme_settings() {
 	Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color");
 	Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color");
 	Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color");
-	Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
-	Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
-	Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color");
-	Color type_color = EDITOR_GET("text_editor/highlighting/engine_type_color");
-	Color usertype_color = EDITOR_GET("text_editor/highlighting/user_type_color");
-	Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
-	Color string_color = EDITOR_GET("text_editor/highlighting/string_color");
 
 	text_edit->add_theme_color_override("background_color", background_color);
 	text_edit->add_theme_color_override("completion_background_color", completion_background_color);
@@ -245,9 +201,6 @@ void ScriptTextEditor::_load_theme_settings() {
 	text_edit->add_theme_color_override("current_line_color", current_line_color);
 	text_edit->add_theme_color_override("line_length_guideline_color", line_length_guideline_color);
 	text_edit->add_theme_color_override("word_highlighted_color", word_highlighted_color);
-	text_edit->add_theme_color_override("number_color", number_color);
-	text_edit->add_theme_color_override("function_color", function_color);
-	text_edit->add_theme_color_override("member_variable_color", member_variable_color);
 	text_edit->add_theme_color_override("bookmark_color", bookmark_color);
 	text_edit->add_theme_color_override("breakpoint_color", breakpoint_color);
 	text_edit->add_theme_color_override("executing_line_color", executing_line_color);
@@ -255,18 +208,9 @@ void ScriptTextEditor::_load_theme_settings() {
 	text_edit->add_theme_color_override("code_folding_color", code_folding_color);
 	text_edit->add_theme_color_override("search_result_color", search_result_color);
 	text_edit->add_theme_color_override("search_result_border_color", search_result_border_color);
-	text_edit->add_theme_color_override("symbol_color", symbol_color);
 
 	text_edit->add_theme_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6));
 
-	colors_cache.symbol_color = symbol_color;
-	colors_cache.keyword_color = keyword_color;
-	colors_cache.basetype_color = basetype_color;
-	colors_cache.type_color = type_color;
-	colors_cache.usertype_color = usertype_color;
-	colors_cache.comment_color = comment_color;
-	colors_cache.string_color = string_color;
-
 	theme_loaded = true;
 	if (!script.is_null()) {
 		_set_theme_for_script();
@@ -279,98 +223,47 @@ void ScriptTextEditor::_set_theme_for_script() {
 	}
 
 	TextEdit *text_edit = code_editor->get_text_edit();
+	text_edit->get_syntax_highlighter()->update_cache();
 
-	List<String> keywords;
-	script->get_language()->get_reserved_words(&keywords);
+	/* add keywords for auto completion */
+	// singleton autoloads (as types, just as engine singletons are)
+	Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+	for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+		const ProjectSettings::AutoloadInfo &info = E->value();
+		if (info.is_singleton) {
+			text_edit->add_keyword(info.name);
+		}
+	}
 
-	for (List<String>::Element *E = keywords.front(); E; E = E->next()) {
-		text_edit->add_keyword_color(E->get(), colors_cache.keyword_color);
-	}
-
-	//colorize core types
-	const Color basetype_color = colors_cache.basetype_color;
-	text_edit->add_keyword_color("String", basetype_color);
-	text_edit->add_keyword_color("Vector2", basetype_color);
-	text_edit->add_keyword_color("Vector2i", basetype_color);
-	text_edit->add_keyword_color("Rect2", basetype_color);
-	text_edit->add_keyword_color("Rect2i", basetype_color);
-	text_edit->add_keyword_color("Vector3", basetype_color);
-	text_edit->add_keyword_color("Vector3i", basetype_color);
-	text_edit->add_keyword_color("Transform2D", basetype_color);
-	text_edit->add_keyword_color("Plane", basetype_color);
-	text_edit->add_keyword_color("Quat", basetype_color);
-	text_edit->add_keyword_color("AABB", basetype_color);
-	text_edit->add_keyword_color("Basis", basetype_color);
-	text_edit->add_keyword_color("Transform", basetype_color);
-	text_edit->add_keyword_color("Color", basetype_color);
-	text_edit->add_keyword_color("StringName", basetype_color);
-	text_edit->add_keyword_color("NodePath", basetype_color);
-	text_edit->add_keyword_color("RID", basetype_color);
-	text_edit->add_keyword_color("Object", basetype_color);
-	text_edit->add_keyword_color("Callable", basetype_color);
-	text_edit->add_keyword_color("Dictionary", basetype_color);
-	text_edit->add_keyword_color("Array", basetype_color);
-	text_edit->add_keyword_color("PackedByteArray", basetype_color);
-	text_edit->add_keyword_color("PackedInt32Array", basetype_color);
-	text_edit->add_keyword_color("PackedInt64Array", basetype_color);
-	text_edit->add_keyword_color("PackedFloat32Array", basetype_color);
-	text_edit->add_keyword_color("PackedFloat64Array", basetype_color);
-	text_edit->add_keyword_color("PackedStringArray", basetype_color);
-	text_edit->add_keyword_color("PackedVector2Array", basetype_color);
-	text_edit->add_keyword_color("PackedVector3Array", basetype_color);
-	text_edit->add_keyword_color("PackedColorArray", basetype_color);
-
-	//colorize engine types
+	// engine types
 	List<StringName> types;
 	ClassDB::get_class_list(&types);
-
 	for (List<StringName>::Element *E = types.front(); E; E = E->next()) {
 		String n = E->get();
 		if (n.begins_with("_")) {
 			n = n.substr(1, n.length());
 		}
-
-		text_edit->add_keyword_color(n, colors_cache.type_color);
+		text_edit->add_keyword(n);
 	}
-	_update_member_keywords();
 
-	//colorize user types
+	// user types
 	List<StringName> global_classes;
 	ScriptServer::get_global_class_list(&global_classes);
-
 	for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
-		text_edit->add_keyword_color(E->get(), colors_cache.usertype_color);
+		text_edit->add_keyword(E->get());
 	}
 
-	//colorize singleton autoloads (as types, just as engine singletons are)
-	Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
-	for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
-		const ProjectSettings::AutoloadInfo &info = E->value();
-		if (info.is_singleton) {
-			text_edit->add_keyword_color(info.name, colors_cache.usertype_color);
-		}
-	}
-
-	//colorize comments
-	List<String> comments;
-	script->get_language()->get_comment_delimiters(&comments);
-
-	for (List<String>::Element *E = comments.front(); E; E = E->next()) {
-		String comment = E->get();
-		String beg = comment.get_slice(" ", 0);
-		String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String();
-
-		text_edit->add_color_region(beg, end, colors_cache.comment_color, end == "");
+	List<String> keywords;
+	script->get_language()->get_reserved_words(&keywords);
+	for (List<String>::Element *E = keywords.front(); E; E = E->next()) {
+		text_edit->add_keyword(E->get());
 	}
 
-	//colorize strings
-	List<String> strings;
-	script->get_language()->get_string_delimiters(&strings);
-	for (List<String>::Element *E = strings.front(); E; E = E->next()) {
-		String string = E->get();
-		String beg = string.get_slice(" ", 0);
-		String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String();
-		text_edit->add_color_region(beg, end, colors_cache.string_color, end == "");
+	// core types
+	List<String> core_types;
+	script->get_language()->get_core_type_words(&core_types);
+	for (List<String>::Element *E = core_types.front(); E; E = E->next()) {
+		text_edit->add_keyword(E->get());
 	}
 }
 
@@ -555,7 +448,7 @@ void ScriptTextEditor::_validate_script() {
 		if (!script->is_tool()) {
 			script->set_source_code(text);
 			script->update_exports();
-			_update_member_keywords();
+			te->get_syntax_highlighter()->update_cache();
 		}
 
 		functions.clear();
@@ -1380,23 +1273,20 @@ void ScriptTextEditor::_edit_option_toggle_inline_comment() {
 	code_editor->toggle_inline_comment(delimiter);
 }
 
-void ScriptTextEditor::add_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) {
+void ScriptTextEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
 	highlighters[p_highlighter->_get_name()] = p_highlighter;
 	highlighter_menu->add_radio_check_item(p_highlighter->_get_name());
 }
 
-void ScriptTextEditor::set_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) {
+void ScriptTextEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
 	TextEdit *te = code_editor->get_text_edit();
-	te->set_syntax_highlighting(p_highlighter);
-	if (p_highlighter.is_valid()) {
-		highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(p_highlighter->_get_name()), true);
-	} else {
-		highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(TTR("Standard")), true);
-	}
+	p_highlighter->_set_edited_resource(script);
+	te->set_syntax_highlighter(p_highlighter);
+	highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(p_highlighter->_get_name()), true);
 }
 
 void ScriptTextEditor::_change_syntax_highlighter(int p_idx) {
-	Map<String, Ref<SyntaxHighlighter> >::Element *el = highlighters.front();
+	Map<String, Ref<EditorSyntaxHighlighter>>::Element *el = highlighters.front();
 	while (el != nullptr) {
 		highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(el->key()), false);
 		el = el->next();
@@ -1826,14 +1716,21 @@ ScriptTextEditor::ScriptTextEditor() {
 	convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KEY_MASK_SHIFT | KEY_F6), EDIT_CAPITALIZE);
 	convert_case->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
 
-	highlighters[TTR("Standard")] = Ref<SyntaxHighlighter>();
 	highlighter_menu = memnew(PopupMenu);
 	highlighter_menu->set_name("highlighter_menu");
 	edit_menu->get_popup()->add_child(highlighter_menu);
 	edit_menu->get_popup()->add_submenu_item(TTR("Syntax Highlighter"), "highlighter_menu");
-	highlighter_menu->add_radio_check_item(TTR("Standard"));
 	highlighter_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_change_syntax_highlighter));
 
+	Ref<EditorPlainTextSyntaxHighlighter> plain_highlighter;
+	plain_highlighter.instance();
+	add_syntax_highlighter(plain_highlighter);
+
+	Ref<EditorStandardSyntaxHighlighter> highlighter;
+	highlighter.instance();
+	add_syntax_highlighter(highlighter);
+	set_syntax_highlighter(highlighter);
+
 	search_menu = memnew(MenuButton);
 	edit_hb->add_child(search_menu);
 	search_menu->set_text(TTR("Search"));

+ 3 - 15
editor/plugins/script_text_editor.h

@@ -83,18 +83,6 @@ class ScriptTextEditor : public ScriptEditorBase {
 	Vector2 color_position;
 	String color_args;
 
-	void _update_member_keywords();
-
-	struct ColorsCache {
-		Color symbol_color;
-		Color keyword_color;
-		Color basetype_color;
-		Color type_color;
-		Color usertype_color;
-		Color comment_color;
-		Color string_color;
-	} colors_cache;
-
 	bool theme_loaded;
 
 	enum {
@@ -164,7 +152,7 @@ protected:
 	void _notification(int p_what);
 	static void _bind_methods();
 
-	Map<String, Ref<SyntaxHighlighter> > highlighters;
+	Map<String, Ref<EditorSyntaxHighlighter>> highlighters;
 	void _change_syntax_highlighter(int p_idx);
 
 	void _edit_option(int p_op);
@@ -190,8 +178,8 @@ protected:
 public:
 	void _update_connected_methods();
 
-	virtual void add_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) override;
-	virtual void set_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) override;
+	virtual void add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
+	virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
 	void update_toggle_scripts_button();
 
 	virtual void apply_code() override;

+ 14 - 16
editor/plugins/shader_editor_plugin.cpp

@@ -83,8 +83,6 @@ void ShaderTextEditor::reload_text() {
 }
 
 void ShaderTextEditor::_load_theme_settings() {
-	get_text_edit()->clear_colors();
-
 	Color background_color = EDITOR_GET("text_editor/highlighting/background_color");
 	Color completion_background_color = EDITOR_GET("text_editor/highlighting/completion_background_color");
 	Color completion_selected_color = EDITOR_GET("text_editor/highlighting/completion_selected_color");
@@ -101,9 +99,6 @@ void ShaderTextEditor::_load_theme_settings() {
 	Color current_line_color = EDITOR_GET("text_editor/highlighting/current_line_color");
 	Color line_length_guideline_color = EDITOR_GET("text_editor/highlighting/line_length_guideline_color");
 	Color word_highlighted_color = EDITOR_GET("text_editor/highlighting/word_highlighted_color");
-	Color number_color = EDITOR_GET("text_editor/highlighting/number_color");
-	Color function_color = EDITOR_GET("text_editor/highlighting/function_color");
-	Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
 	Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color");
 	Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color");
 	Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color");
@@ -111,9 +106,6 @@ void ShaderTextEditor::_load_theme_settings() {
 	Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color");
 	Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color");
 	Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color");
-	Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
-	Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
-	Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
 
 	get_text_edit()->add_theme_color_override("background_color", background_color);
 	get_text_edit()->add_theme_color_override("completion_background_color", completion_background_color);
@@ -131,9 +123,6 @@ void ShaderTextEditor::_load_theme_settings() {
 	get_text_edit()->add_theme_color_override("current_line_color", current_line_color);
 	get_text_edit()->add_theme_color_override("line_length_guideline_color", line_length_guideline_color);
 	get_text_edit()->add_theme_color_override("word_highlighted_color", word_highlighted_color);
-	get_text_edit()->add_theme_color_override("number_color", number_color);
-	get_text_edit()->add_theme_color_override("function_color", function_color);
-	get_text_edit()->add_theme_color_override("member_variable_color", member_variable_color);
 	get_text_edit()->add_theme_color_override("mark_color", mark_color);
 	get_text_edit()->add_theme_color_override("bookmark_color", bookmark_color);
 	get_text_edit()->add_theme_color_override("breakpoint_color", breakpoint_color);
@@ -141,7 +130,11 @@ void ShaderTextEditor::_load_theme_settings() {
 	get_text_edit()->add_theme_color_override("code_folding_color", code_folding_color);
 	get_text_edit()->add_theme_color_override("search_result_color", search_result_color);
 	get_text_edit()->add_theme_color_override("search_result_border_color", search_result_border_color);
-	get_text_edit()->add_theme_color_override("symbol_color", symbol_color);
+
+	syntax_highlighter->set_number_color(EDITOR_GET("text_editor/highlighting/number_color"));
+	syntax_highlighter->set_symbol_color(EDITOR_GET("text_editor/highlighting/symbol_color"));
+	syntax_highlighter->set_function_color(EDITOR_GET("text_editor/highlighting/function_color"));
+	syntax_highlighter->set_member_variable_color(EDITOR_GET("text_editor/highlighting/member_variable_color"));
 
 	List<String> keywords;
 	ShaderLanguage::get_keyword_list(&keywords);
@@ -158,13 +151,17 @@ void ShaderTextEditor::_load_theme_settings() {
 		}
 	}
 
+	const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+	syntax_highlighter->clear_keyword_colors();
 	for (List<String>::Element *E = keywords.front(); E; E = E->next()) {
-		get_text_edit()->add_keyword_color(E->get(), keyword_color);
+		syntax_highlighter->add_keyword_color(E->get(), keyword_color);
 	}
 
 	//colorize comments
-	get_text_edit()->add_color_region("/*", "*/", comment_color, false);
-	get_text_edit()->add_color_region("//", "", comment_color, false);
+	const Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
+	syntax_highlighter->clear_color_regions();
+	syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
+	syntax_highlighter->add_color_region("//", "", comment_color, false);
 }
 
 void ShaderTextEditor::_check_shader_mode() {
@@ -236,6 +233,8 @@ void ShaderTextEditor::_bind_methods() {
 }
 
 ShaderTextEditor::ShaderTextEditor() {
+	syntax_highlighter.instance();
+	get_text_edit()->set_syntax_highlighter(syntax_highlighter);
 }
 
 /*** SCRIPT EDITOR ******/
@@ -356,7 +355,6 @@ void ShaderEditor::_editor_settings_changed() {
 	shader_editor->get_text_edit()->set_draw_tabs(EditorSettings::get_singleton()->get("text_editor/indent/draw_tabs"));
 	shader_editor->get_text_edit()->set_draw_spaces(EditorSettings::get_singleton()->get("text_editor/indent/draw_spaces"));
 	shader_editor->get_text_edit()->set_show_line_numbers(EditorSettings::get_singleton()->get("text_editor/appearance/show_line_numbers"));
-	shader_editor->get_text_edit()->set_syntax_coloring(EditorSettings::get_singleton()->get("text_editor/highlighting/syntax_highlighting"));
 	shader_editor->get_text_edit()->set_highlight_all_occurrences(EditorSettings::get_singleton()->get("text_editor/highlighting/highlight_all_occurrences"));
 	shader_editor->get_text_edit()->set_highlight_current_line(EditorSettings::get_singleton()->get("text_editor/highlighting/highlight_current_line"));
 	shader_editor->get_text_edit()->cursor_set_blink_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink"));

+ 1 - 0
editor/plugins/shader_editor_plugin.h

@@ -44,6 +44,7 @@
 class ShaderTextEditor : public CodeTextEditor {
 	GDCLASS(ShaderTextEditor, CodeTextEditor);
 
+	Ref<CodeHighlighter> syntax_highlighter;
 	Ref<Shader> shader;
 
 	void _check_shader_mode();

+ 15 - 45
editor/plugins/text_editor.cpp

@@ -33,35 +33,19 @@
 #include "core/os/keyboard.h"
 #include "editor/editor_node.h"
 
-void TextEditor::add_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) {
+void TextEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
 	highlighters[p_highlighter->_get_name()] = p_highlighter;
 	highlighter_menu->add_radio_check_item(p_highlighter->_get_name());
 }
 
-void TextEditor::set_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) {
+void TextEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
 	TextEdit *te = code_editor->get_text_edit();
-	te->set_syntax_highlighting(p_highlighter);
-	if (p_highlighter.is_valid()) {
-		highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(p_highlighter->_get_name()), true);
-	} else {
-		highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text("Standard"), true);
-	}
-
-	// little work around. GDScript highlighter goes through text_edit for colours,
-	// so to remove all colours we need to set and unset them here.
-	if (p_highlighter == nullptr) { // standard
-		TextEdit *text_edit = code_editor->get_text_edit();
-		text_edit->add_theme_color_override("number_color", colors_cache.font_color);
-		text_edit->add_theme_color_override("function_color", colors_cache.font_color);
-		text_edit->add_theme_color_override("number_color", colors_cache.font_color);
-		text_edit->add_theme_color_override("member_variable_color", colors_cache.font_color);
-	} else {
-		_load_theme_settings();
-	}
+	te->set_syntax_highlighter(p_highlighter);
+	highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(p_highlighter->_get_name()), true);
 }
 
 void TextEditor::_change_syntax_highlighter(int p_idx) {
-	Map<String, Ref<SyntaxHighlighter> >::Element *el = highlighters.front();
+	Map<String, Ref<EditorSyntaxHighlighter>>::Element *el = highlighters.front();
 	while (el != nullptr) {
 		highlighter_menu->set_item_checked(highlighter_menu->get_item_idx_from_text(el->key()), false);
 		el = el->next();
@@ -71,7 +55,7 @@ void TextEditor::_change_syntax_highlighter(int p_idx) {
 
 void TextEditor::_load_theme_settings() {
 	TextEdit *text_edit = code_editor->get_text_edit();
-	text_edit->clear_colors();
+	text_edit->get_syntax_highlighter()->update_cache();
 
 	Color background_color = EDITOR_GET("text_editor/highlighting/background_color");
 	Color completion_background_color = EDITOR_GET("text_editor/highlighting/completion_background_color");
@@ -89,9 +73,6 @@ void TextEditor::_load_theme_settings() {
 	Color current_line_color = EDITOR_GET("text_editor/highlighting/current_line_color");
 	Color line_length_guideline_color = EDITOR_GET("text_editor/highlighting/line_length_guideline_color");
 	Color word_highlighted_color = EDITOR_GET("text_editor/highlighting/word_highlighted_color");
-	Color number_color = EDITOR_GET("text_editor/highlighting/number_color");
-	Color function_color = EDITOR_GET("text_editor/highlighting/function_color");
-	Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
 	Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color");
 	Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color");
 	Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color");
@@ -99,12 +80,6 @@ void TextEditor::_load_theme_settings() {
 	Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color");
 	Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color");
 	Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color");
-	Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
-	Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
-	Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color");
-	Color type_color = EDITOR_GET("text_editor/highlighting/engine_type_color");
-	Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
-	Color string_color = EDITOR_GET("text_editor/highlighting/string_color");
 
 	text_edit->add_theme_color_override("background_color", background_color);
 	text_edit->add_theme_color_override("completion_background_color", completion_background_color);
@@ -122,9 +97,6 @@ void TextEditor::_load_theme_settings() {
 	text_edit->add_theme_color_override("current_line_color", current_line_color);
 	text_edit->add_theme_color_override("line_length_guideline_color", line_length_guideline_color);
 	text_edit->add_theme_color_override("word_highlighted_color", word_highlighted_color);
-	text_edit->add_theme_color_override("number_color", number_color);
-	text_edit->add_theme_color_override("function_color", function_color);
-	text_edit->add_theme_color_override("member_variable_color", member_variable_color);
 	text_edit->add_theme_color_override("breakpoint_color", breakpoint_color);
 	text_edit->add_theme_color_override("executing_line_color", executing_line_color);
 	text_edit->add_theme_color_override("mark_color", mark_color);
@@ -132,17 +104,8 @@ void TextEditor::_load_theme_settings() {
 	text_edit->add_theme_color_override("code_folding_color", code_folding_color);
 	text_edit->add_theme_color_override("search_result_color", search_result_color);
 	text_edit->add_theme_color_override("search_result_border_color", search_result_border_color);
-	text_edit->add_theme_color_override("symbol_color", symbol_color);
 
 	text_edit->add_theme_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6));
-
-	colors_cache.font_color = text_color;
-	colors_cache.symbol_color = symbol_color;
-	colors_cache.keyword_color = keyword_color;
-	colors_cache.basetype_color = basetype_color;
-	colors_cache.type_color = type_color;
-	colors_cache.comment_color = comment_color;
-	colors_cache.string_color = string_color;
 }
 
 String TextEditor::get_name() {
@@ -635,14 +598,21 @@ TextEditor::TextEditor() {
 	convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize")), EDIT_CAPITALIZE);
 	convert_case->connect("id_pressed", callable_mp(this, &TextEditor::_edit_option));
 
-	highlighters["Standard"] = Ref<SyntaxHighlighter>();
 	highlighter_menu = memnew(PopupMenu);
 	highlighter_menu->set_name("highlighter_menu");
 	edit_menu->get_popup()->add_child(highlighter_menu);
 	edit_menu->get_popup()->add_submenu_item(TTR("Syntax Highlighter"), "highlighter_menu");
-	highlighter_menu->add_radio_check_item(TTR("Standard"));
 	highlighter_menu->connect("id_pressed", callable_mp(this, &TextEditor::_change_syntax_highlighter));
 
+	Ref<EditorPlainTextSyntaxHighlighter> plain_highlighter;
+	plain_highlighter.instance();
+	add_syntax_highlighter(plain_highlighter);
+
+	Ref<EditorStandardSyntaxHighlighter> highlighter;
+	highlighter.instance();
+	add_syntax_highlighter(highlighter);
+	set_syntax_highlighter(plain_highlighter);
+
 	MenuButton *goto_menu = memnew(MenuButton);
 	edit_hb->add_child(goto_menu);
 	goto_menu->set_text(TTR("Go To"));

+ 3 - 13
editor/plugins/text_editor.h

@@ -50,16 +50,6 @@ private:
 
 	GotoLineDialog *goto_line_dialog;
 
-	struct ColorsCache {
-		Color font_color;
-		Color symbol_color;
-		Color keyword_color;
-		Color basetype_color;
-		Color type_color;
-		Color comment_color;
-		Color string_color;
-	} colors_cache;
-
 	enum {
 		EDIT_UNDO,
 		EDIT_REDO,
@@ -104,7 +94,7 @@ protected:
 	void _make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded, Vector2 p_position);
 	void _text_edit_gui_input(const Ref<InputEvent> &ev);
 
-	Map<String, Ref<SyntaxHighlighter> > highlighters;
+	Map<String, Ref<EditorSyntaxHighlighter>> highlighters;
 	void _change_syntax_highlighter(int p_idx);
 	void _load_theme_settings();
 
@@ -116,8 +106,8 @@ protected:
 	void _bookmark_item_pressed(int p_idx);
 
 public:
-	virtual void add_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) override;
-	virtual void set_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) override;
+	virtual void add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
+	virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
 
 	virtual String get_name() override;
 	virtual Ref<Texture2D> get_theme_icon() override;

+ 13 - 10
editor/plugins/visual_shader_editor_plugin.cpp

@@ -824,6 +824,8 @@ void VisualShaderEditor::_update_graph() {
 
 		if (is_expression) {
 			TextEdit *expression_box = memnew(TextEdit);
+			Ref<CodeHighlighter> expression_syntax_highlighter;
+			expression_syntax_highlighter.instance();
 			expression_node->set_control(expression_box, 0);
 			node->add_child(expression_box);
 
@@ -833,18 +835,18 @@ void VisualShaderEditor::_update_graph() {
 			Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
 			Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
 
-			expression_box->set_syntax_coloring(true);
+			expression_box->set_syntax_highlighter(expression_syntax_highlighter);
 			expression_box->add_theme_color_override("background_color", background_color);
 
 			for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) {
-				expression_box->add_keyword_color(E->get(), keyword_color);
+				expression_syntax_highlighter->add_keyword_color(E->get(), keyword_color);
 			}
 
 			expression_box->add_theme_font_override("font", get_theme_font("expression", "EditorFonts"));
 			expression_box->add_theme_color_override("font_color", text_color);
-			expression_box->add_theme_color_override("symbol_color", symbol_color);
-			expression_box->add_color_region("/*", "*/", comment_color, false);
-			expression_box->add_color_region("//", "", comment_color, false);
+			expression_syntax_highlighter->set_symbol_color(symbol_color);
+			expression_syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
+			expression_syntax_highlighter->add_color_region("//", "", comment_color, false);
 
 			expression_box->set_text(expression);
 			expression_box->set_context_menu_enabled(false);
@@ -1703,14 +1705,14 @@ void VisualShaderEditor::_notification(int p_what) {
 			preview_text->add_theme_color_override("background_color", background_color);
 
 			for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) {
-				preview_text->add_keyword_color(E->get(), keyword_color);
+				syntax_highlighter->add_keyword_color(E->get(), keyword_color);
 			}
 
 			preview_text->add_theme_font_override("font", get_theme_font("expression", "EditorFonts"));
 			preview_text->add_theme_color_override("font_color", text_color);
-			preview_text->add_theme_color_override("symbol_color", symbol_color);
-			preview_text->add_color_region("/*", "*/", comment_color, false);
-			preview_text->add_color_region("//", "", comment_color, false);
+			syntax_highlighter->set_symbol_color(symbol_color);
+			syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
+			syntax_highlighter->add_color_region("//", "", comment_color, false);
 
 			error_text->add_theme_font_override("font", get_theme_font("status_source", "EditorFonts"));
 			error_text->add_theme_color_override("font_color", get_theme_color("error_color", "Editor"));
@@ -2369,11 +2371,12 @@ VisualShaderEditor::VisualShaderEditor() {
 	preview_vbox->set_visible(preview_showed);
 	main_box->add_child(preview_vbox);
 	preview_text = memnew(TextEdit);
+	syntax_highlighter.instance();
 	preview_vbox->add_child(preview_text);
 	preview_text->set_h_size_flags(SIZE_EXPAND_FILL);
 	preview_text->set_v_size_flags(SIZE_EXPAND_FILL);
 	preview_text->set_custom_minimum_size(Size2(400 * EDSCALE, 0));
-	preview_text->set_syntax_coloring(true);
+	preview_text->set_syntax_highlighter(syntax_highlighter);
 	preview_text->set_show_line_numbers(true);
 	preview_text->set_readonly(true);
 

+ 1 - 0
editor/plugins/visual_shader_editor_plugin.h

@@ -72,6 +72,7 @@ class VisualShaderEditor : public VBoxContainer {
 	bool shader_error;
 	VBoxContainer *preview_vbox;
 	TextEdit *preview_text;
+	Ref<CodeHighlighter> syntax_highlighter;
 	Label *error_text;
 
 	UndoRedo *undo_redo;

+ 272 - 79
modules/gdscript/editor/gdscript_highlighter.cpp

@@ -29,25 +29,14 @@
 /*************************************************************************/
 
 #include "gdscript_highlighter.h"
+#include "../gdscript.h"
 #include "../gdscript_tokenizer.h"
 #include "editor/editor_settings.h"
 
-inline bool _is_symbol(CharType c) {
-	return is_symbol(c);
-}
-
-static bool _is_text_char(CharType c) {
-	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
-}
-
 static bool _is_char(CharType c) {
 	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
 }
 
-static bool _is_number(CharType c) {
-	return (c >= '0' && c <= '9');
-}
-
 static bool _is_hex_symbol(CharType c) {
 	return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
 }
@@ -81,36 +70,136 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
 	Color keyword_color;
 	Color color;
 
-	int in_region = text_edit->_is_line_in_region(p_line);
-	int deregion = 0;
+	color_region_cache[p_line] = -1;
+	int in_region = -1;
+	if (p_line != 0) {
+		if (!color_region_cache.has(p_line - 1)) {
+			get_line_syntax_highlighting(p_line - 1);
+		}
+		in_region = color_region_cache[p_line - 1];
+	}
 
-	const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text_edit->_get_line_color_region_info(p_line);
 	const String &str = text_edit->get_line(p_line);
+	const int line_length = str.length();
 	Color prev_color;
 	for (int j = 0; j < str.length(); j++) {
 		Dictionary highlighter_info;
 
-		if (deregion > 0) {
-			deregion--;
-			if (deregion == 0) {
-				in_region = -1;
+		color = font_color;
+		bool is_char = !is_symbol(str[j]);
+		bool is_a_symbol = is_symbol(str[j]);
+		bool is_number = (str[j] >= '0' && str[j] <= '9');
+
+		/* color regions */
+		if (is_a_symbol || in_region != -1) {
+			int from = j;
+			for (; from < line_length; from++) {
+				if (str[from] == '\\') {
+					from++;
+					continue;
+				}
+				break;
 			}
-		}
 
-		if (deregion != 0) {
-			if (color != prev_color) {
-				prev_color = color;
-				highlighter_info["color"] = color;
-				color_map[j] = highlighter_info;
-			}
-			continue;
-		}
+			if (from != line_length) {
+				/* check if we are in entering a region */
+				if (in_region == -1) {
+					for (int c = 0; c < color_regions.size(); c++) {
+						/* check there is enough room */
+						int chars_left = line_length - from;
+						int start_key_length = color_regions[c].start_key.length();
+						int end_key_length = color_regions[c].end_key.length();
+						if (chars_left < start_key_length) {
+							continue;
+						}
+
+						/* search the line */
+						bool match = true;
+						const CharType *start_key = color_regions[c].start_key.c_str();
+						for (int k = 0; k < start_key_length; k++) {
+							if (start_key[k] != str[from + k]) {
+								match = false;
+								break;
+							}
+						}
+						if (!match) {
+							continue;
+						}
+						in_region = c;
+						from += start_key_length;
+
+						/* check if it's the whole line */
+						if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) {
+							prev_color = color_regions[in_region].color;
+							highlighter_info["color"] = color_regions[c].color;
+							color_map[j] = highlighter_info;
+
+							j = line_length;
+							if (!color_regions[c].line_only) {
+								color_region_cache[p_line] = c;
+							}
+						}
+						break;
+					}
 
-		color = font_color;
+					if (j == line_length) {
+						continue;
+					}
+				}
 
-		bool is_char = _is_text_char(str[j]);
-		bool is_symbol = _is_symbol(str[j]);
-		bool is_number = _is_number(str[j]);
+				/* if we are in one find the end key */
+				if (in_region != -1) {
+					/* check there is enough room */
+					int chars_left = line_length - from;
+					int end_key_length = color_regions[in_region].end_key.length();
+					if (chars_left < end_key_length) {
+						continue;
+					}
+
+					/* search the line */
+					int region_end_index = -1;
+					const CharType *end_key = color_regions[in_region].start_key.c_str();
+					for (; from < line_length; from++) {
+						if (!is_a_symbol) {
+							continue;
+						}
+
+						if (str[from] == '\\') {
+							from++;
+							continue;
+						}
+
+						for (int k = 0; k < end_key_length; k++) {
+							if (end_key[k] == str[from + k]) {
+								region_end_index = from;
+								break;
+							}
+						}
+
+						if (region_end_index != -1) {
+							break;
+						}
+					}
+
+					prev_color = color_regions[in_region].color;
+					highlighter_info["color"] = color_regions[in_region].color;
+					color_map[j] = highlighter_info;
+
+					previous_type = REGION;
+					previous_text = "";
+					previous_column = j;
+					j = from;
+					if (region_end_index == -1) {
+						color_region_cache[p_line] = in_region;
+					}
+
+					in_region = -1;
+					prev_is_char = false;
+					prev_is_number = false;
+					continue;
+				}
+			}
+		}
 
 		// allow ABCDEF in hex notation
 		if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
@@ -132,7 +221,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
 		// check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation
 		if ((str[j] == '.' || str[j] == 'x' || str[j] == 'b' || str[j] == '_' || str[j] == 'e') && !in_word && prev_is_number && !is_number) {
 			is_number = true;
-			is_symbol = false;
+			is_a_symbol = false;
 			is_char = false;
 
 			if (str[j] == 'x' && str[j - 1] == '0') {
@@ -150,43 +239,26 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
 			is_number = false;
 		}
 
-		if (is_symbol && str[j] != '.' && in_word) {
+		if (is_a_symbol && str[j] != '.' && in_word) {
 			in_word = false;
 		}
 
-		if (is_symbol && cri_map.has(j)) {
-			const TextEdit::Text::ColorRegionInfo &cri = cri_map[j];
-
-			if (in_region == -1) {
-				if (!cri.end) {
-					in_region = cri.region;
-				}
-			} else {
-				TextEdit::ColorRegion cr = text_edit->_get_color_region(cri.region);
-				if (in_region == cri.region && !cr.line_only) { //ignore otherwise
-					if (cri.end || cr.eq) {
-						deregion = cr.eq ? cr.begin_key.length() : cr.end_key.length();
-					}
-				}
-			}
-		}
-
 		if (!is_char) {
 			in_keyword = false;
 		}
 
-		if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
+		if (!in_keyword && is_char && !prev_is_char) {
 			int to = j;
-			while (to < str.length() && _is_text_char(str[to])) {
+			while (to < str.length() && !is_symbol(str[to])) {
 				to++;
 			}
 
 			String word = str.substr(j, to - j);
 			Color col = Color();
-			if (text_edit->has_keyword_color(word)) {
-				col = text_edit->get_keyword_color(word);
-			} else if (text_edit->has_member_color(word)) {
-				col = text_edit->get_member_color(word);
+			if (keywords.has(word)) {
+				col = keywords[word];
+			} else if (member_keywords.has(word)) {
+				col = member_keywords[word];
 				for (int k = j - 1; k >= 0; k--) {
 					if (str[k] == '.') {
 						col = Color(); //member indexing not allowed
@@ -205,7 +277,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
 
 		if (!in_function_name && in_word && !in_keyword) {
 			int k = j;
-			while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+			while (k < str.length() && !is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
 				k++;
 			}
 
@@ -223,7 +295,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
 
 		if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
 			int k = j;
-			while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+			while (k > 0 && !is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
 				k--;
 			}
 
@@ -232,7 +304,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
 			}
 		}
 
-		if (is_symbol) {
+		if (is_a_symbol) {
 			if (in_function_name) {
 				in_function_args = true;
 			}
@@ -269,14 +341,11 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
 
 		if (!in_node_path && in_region == -1 && str[j] == '$') {
 			in_node_path = true;
-		} else if (in_region != -1 || (is_symbol && str[j] != '/')) {
+		} else if (in_region != -1 || (is_a_symbol && str[j] != '/')) {
 			in_node_path = false;
 		}
 
-		if (in_region >= 0) {
-			next_type = REGION;
-			color = text_edit->_get_color_region(in_region).color;
-		} else if (in_node_path) {
+		if (in_node_path) {
 			next_type = NODE_PATH;
 			color = node_path_color;
 		} else if (in_keyword) {
@@ -293,7 +362,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
 			} else {
 				color = function_color;
 			}
-		} else if (is_symbol) {
+		} else if (is_a_symbol) {
 			next_type = SYMBOL;
 			color = symbol_color;
 		} else if (is_number) {
@@ -352,13 +421,114 @@ Array GDScriptSyntaxHighlighter::_get_supported_languages() const {
 }
 
 void GDScriptSyntaxHighlighter::_update_cache() {
+	keywords.clear();
+	member_keywords.clear();
+	color_regions.clear();
+	color_region_cache.clear();
+
 	font_color = text_edit->get_theme_color("font_color");
-	symbol_color = text_edit->get_theme_color("symbol_color");
-	function_color = text_edit->get_theme_color("function_color");
-	number_color = text_edit->get_theme_color("number_color");
-	member_color = text_edit->get_theme_color("member_variable_color");
+	symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
+	function_color = EDITOR_GET("text_editor/highlighting/function_color");
+	number_color = EDITOR_GET("text_editor/highlighting/number_color");
+	member_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+
+	/* Engine types. */
+	const Color types_color = EDITOR_GET("text_editor/highlighting/engine_type_color");
+	List<StringName> types;
+	ClassDB::get_class_list(&types);
+	for (List<StringName>::Element *E = types.front(); E; E = E->next()) {
+		String n = E->get();
+		if (n.begins_with("_")) {
+			n = n.substr(1, n.length());
+		}
+		keywords[n] = types_color;
+	}
+
+	/* User types. */
+	const Color usertype_color = EDITOR_GET("text_editor/highlighting/user_type_color");
+	List<StringName> global_classes;
+	ScriptServer::get_global_class_list(&global_classes);
+	for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
+		keywords[String(E->get())] = usertype_color;
+	}
+
+	/* Autoloads. */
+	Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+	for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
+		const ProjectSettings::AutoloadInfo &info = E->value();
+		if (info.is_singleton) {
+			keywords[info.name] = usertype_color;
+		}
+	}
+
+	const GDScriptLanguage *gdscript = GDScriptLanguage::get_singleton();
+
+	/* Core types. */
+	const Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color");
+	List<String> core_types;
+	gdscript->get_core_type_words(&core_types);
+	for (List<String>::Element *E = core_types.front(); E; E = E->next()) {
+		keywords[E->get()] = basetype_color;
+	}
+
+	/* Reserved words. */
+	const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+	List<String> keyword_list;
+	gdscript->get_reserved_words(&keyword_list);
+	for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) {
+		keywords[E->get()] = keyword_color;
+	}
+
+	/* Comments */
+	const Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
+	List<String> comments;
+	gdscript->get_comment_delimiters(&comments);
+	for (List<String>::Element *E = comments.front(); E; E = E->next()) {
+		String comment = E->get();
+		String beg = comment.get_slice(" ", 0);
+		String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String();
+		add_color_region(beg, end, comment_color, end == "");
+	}
+
+	/* Strings */
+	const Color string_color = EDITOR_GET("text_editor/highlighting/string_color");
+	List<String> strings;
+	gdscript->get_string_delimiters(&strings);
+	for (List<String>::Element *E = strings.front(); E; E = E->next()) {
+		String string = E->get();
+		String beg = string.get_slice(" ", 0);
+		String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String();
+		add_color_region(beg, end, string_color, end == "");
+	}
+
+	const Ref<Script> script = _get_edited_resource();
+	if (script.is_valid()) {
+		/* Member types. */
+		const Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+		StringName instance_base = script->get_instance_base_type();
+		if (instance_base != StringName()) {
+			List<PropertyInfo> plist;
+			ClassDB::get_property_list(instance_base, &plist);
+			for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
+				String name = E->get().name;
+				if (E->get().usage & PROPERTY_USAGE_CATEGORY || E->get().usage & PROPERTY_USAGE_GROUP || E->get().usage & PROPERTY_USAGE_SUBGROUP) {
+					continue;
+				}
+				if (name.find("/") != -1) {
+					continue;
+				}
+				member_keywords[name] = member_variable_color;
+			}
+
+			List<String> clist;
+			ClassDB::get_integer_constant_list(instance_base, &clist);
+			for (List<String>::Element *E = clist.front(); E; E = E->next()) {
+				member_keywords[E->get()] = member_variable_color;
+			}
+		}
+	}
 
-	const String text_edit_color_theme = EditorSettings::get_singleton()->get("text_edit/theme/color_theme");
+	const String text_edit_color_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
 	const bool default_theme = text_edit_color_theme == "Default";
 
 	if (default_theme || EditorSettings::get_singleton()->is_dark_theme()) {
@@ -369,25 +539,48 @@ void GDScriptSyntaxHighlighter::_update_cache() {
 		node_path_color = Color(0.32, 0.55, 0.29);
 	}
 
-	EDITOR_DEF("text_edit/highlighting/gdscript/function_definition_color", function_definition_color);
-	EDITOR_DEF("text_edit/highlighting/gdscript/node_path_color", node_path_color);
+	EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color);
+	EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color);
 	if (text_edit_color_theme == "Adaptive" || default_theme) {
 		EditorSettings::get_singleton()->set_initial_value(
-				"text_edit/highlighting/gdscript/function_definition_color",
+				"text_editor/highlighting/gdscript/function_definition_color",
 				function_definition_color,
 				true);
 		EditorSettings::get_singleton()->set_initial_value(
-				"text_edit/highlighting/gdscript/node_path_color",
+				"text_editor/highlighting/gdscript/node_path_color",
 				node_path_color,
 				true);
 	}
 
-	function_definition_color = EDITOR_GET("text_edit/highlighting/gdscript/function_definition_color");
-	node_path_color = EDITOR_GET("text_edit/highlighting/gdscript/node_path_color");
-	type_color = EDITOR_GET("text_edit/highlighting/base_type_color");
+	function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color");
+	node_path_color = EDITOR_GET("text_editor/highlighting/gdscript/node_path_color");
+	type_color = EDITOR_GET("text_editor/highlighting/base_type_color");
+}
+
+void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
+	for (int i = 0; i < p_start_key.length(); i++) {
+		ERR_FAIL_COND_MSG(!is_symbol(p_start_key[i]), "color regions must start with a symbol");
+	}
+
+	if (p_end_key.length() > 0) {
+		for (int i = 0; i < p_end_key.length(); i++) {
+			ERR_FAIL_COND_MSG(!is_symbol(p_end_key[i]), "color regions must end with a symbol");
+		}
+	}
+
+	for (int i = 0; i < color_regions.size(); i++) {
+		ERR_FAIL_COND_MSG(color_regions[i].start_key == p_start_key, "color region with start key '" + p_start_key + "' already exists.");
+	}
+
+	ColorRegion color_region;
+	color_region.color = p_color;
+	color_region.start_key = p_start_key;
+	color_region.end_key = p_end_key;
+	color_region.line_only = p_line_only;
+	color_regions.push_back(color_region);
 }
 
-Ref<SyntaxHighlighter> GDScriptSyntaxHighlighter::_create() const {
+Ref<EditorSyntaxHighlighter> GDScriptSyntaxHighlighter::_create() const {
 	Ref<GDScriptSyntaxHighlighter> syntax_highlighter;
 	syntax_highlighter.instance();
 	return syntax_highlighter;

+ 18 - 3
modules/gdscript/editor/gdscript_highlighter.h

@@ -31,12 +31,25 @@
 #ifndef GDSCRIPT_HIGHLIGHTER_H
 #define GDSCRIPT_HIGHLIGHTER_H
 
+#include "editor/plugins/script_editor_plugin.h"
 #include "scene/gui/text_edit.h"
 
-class GDScriptSyntaxHighlighter : public SyntaxHighlighter {
-	GDCLASS(GDScriptSyntaxHighlighter, SyntaxHighlighter)
+class GDScriptSyntaxHighlighter : public EditorSyntaxHighlighter {
+	GDCLASS(GDScriptSyntaxHighlighter, EditorSyntaxHighlighter)
 
 private:
+	struct ColorRegion {
+		Color color;
+		String start_key;
+		String end_key;
+		bool line_only;
+	};
+	Vector<ColorRegion> color_regions;
+	Map<int, int> color_region_cache;
+
+	Dictionary keywords;
+	Dictionary member_keywords;
+
 	enum Type {
 		NONE,
 		REGION,
@@ -61,6 +74,8 @@ private:
 	Color node_path_color;
 	Color type_color;
 
+	void add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only = false);
+
 public:
 	virtual void _update_cache() override;
 	virtual Dictionary _get_line_syntax_highlighting(int p_line) override;
@@ -68,7 +83,7 @@ public:
 	virtual String _get_name() const override;
 	virtual Array _get_supported_languages() const override;
 
-	virtual Ref<SyntaxHighlighter> _create() const override;
+	virtual Ref<EditorSyntaxHighlighter> _create() const override;
 };
 
 #endif // GDSCRIPT_HIGHLIGHTER_H

+ 2 - 2
modules/visual_script/visual_script_editor.cpp

@@ -4660,10 +4660,10 @@ void VisualScriptEditor::_member_option(int p_option) {
 	}
 }
 
-void VisualScriptEditor::add_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) {
+void VisualScriptEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
 }
 
-void VisualScriptEditor::set_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) {
+void VisualScriptEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
 }
 
 void VisualScriptEditor::_bind_methods() {

+ 2 - 2
modules/visual_script/visual_script_editor.h

@@ -288,8 +288,8 @@ protected:
 	static void _bind_methods();
 
 public:
-	virtual void add_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) override;
-	virtual void set_syntax_highlighter(Ref<SyntaxHighlighter> p_highlighter) override;
+	virtual void add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
+	virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
 
 	virtual void apply_code() override;
 	virtual RES get_edited_resource() const override;

+ 49 - 487
scene/gui/text_edit.cpp

@@ -60,14 +60,6 @@ static bool _is_char(CharType c) {
 	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
 }
 
-static bool _is_number(CharType c) {
-	return (c >= '0' && c <= '9');
-}
-
-static bool _is_hex_symbol(CharType c) {
-	return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
 static bool _is_pair_right_symbol(CharType c) {
 	return c == '"' ||
 		   c == '\'' ||
@@ -136,94 +128,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
 	}
 
 	text.write[p_line].width_cache = w;
-
 	text.write[p_line].wrap_amount_cache = -1;
-
-	// Update regions.
-
-	text.write[p_line].region_info.clear();
-
-	for (int i = 0; i < len; i++) {
-		if (!_is_symbol(str[i])) {
-			continue;
-		}
-		if (str[i] == '\\') {
-			i++; // Skip quoted anything.
-			continue;
-		}
-
-		int left = len - i;
-
-		for (int j = 0; j < color_regions->size(); j++) {
-			const ColorRegion &cr = color_regions->operator[](j);
-
-			/* BEGIN */
-
-			int lr = cr.begin_key.length();
-			const CharType *kc;
-			bool match;
-
-			if (lr != 0 && lr <= left) {
-				kc = cr.begin_key.c_str();
-
-				match = true;
-
-				for (int k = 0; k < lr; k++) {
-					if (kc[k] != str[i + k]) {
-						match = false;
-						break;
-					}
-				}
-
-				if (match) {
-					ColorRegionInfo cri;
-					cri.end = false;
-					cri.region = j;
-					text.write[p_line].region_info[i] = cri;
-					i += lr - 1;
-
-					break;
-				}
-			}
-
-			/* END */
-
-			lr = cr.end_key.length();
-			if (lr != 0 && lr <= left) {
-				kc = cr.end_key.c_str();
-
-				match = true;
-
-				for (int k = 0; k < lr; k++) {
-					if (kc[k] != str[i + k]) {
-						match = false;
-						break;
-					}
-				}
-
-				if (match) {
-					ColorRegionInfo cri;
-					cri.end = true;
-					cri.region = j;
-					text.write[p_line].region_info[i] = cri;
-					i += lr - 1;
-
-					break;
-				}
-			}
-		}
-	}
-}
-
-const Map<int, TextEdit::Text::ColorRegionInfo> &TextEdit::Text::get_color_region_info(int p_line) const {
-	static Map<int, ColorRegionInfo> cri;
-	ERR_FAIL_INDEX_V(p_line, text.size(), cri);
-
-	if (text[p_line].width_cache == -1) {
-		_update_line_cache(p_line);
-	}
-
-	return text[p_line].region_info;
 }
 
 int TextEdit::Text::get_line_width(int p_line) const {
@@ -601,7 +506,6 @@ void TextEdit::_notification(int p_what) {
 		case NOTIFICATION_THEME_CHANGED: {
 			_update_caches();
 			_update_wrap_at();
-			syntax_highlighting_cache.clear();
 		} break;
 		case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
 			window_has_focus = true;
@@ -640,6 +544,14 @@ void TextEdit::_notification(int p_what) {
 				adjust_viewport_to_cursor();
 				first_draw = false;
 			}
+
+			/* Prevent the resource getting lost between the editor and game. */
+			if (Engine::get_singleton()->is_editor_hint()) {
+				if (syntax_highlighter.is_valid() && syntax_highlighter->get_text_edit() != this) {
+					syntax_highlighter->set_text_edit(this);
+				}
+			}
+
 			Size2 size = get_size();
 			if ((!has_focus() && !menu->has_focus()) || !window_has_focus) {
 				draw_caret = false;
@@ -711,10 +623,8 @@ void TextEdit::_notification(int p_what) {
 
 			Color color = readonly ? cache.font_color_readonly : cache.font_color;
 
-			if (syntax_coloring) {
-				if (cache.background_color.a > 0.01) {
-					RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color);
-				}
+			if (cache.background_color.a > 0.01) {
+				RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color);
 			}
 
 			if (line_length_guidelines) {
@@ -937,10 +847,7 @@ void TextEdit::_notification(int p_what) {
 						break;
 					}
 
-					Dictionary color_map;
-					if (syntax_coloring) {
-						color_map = _get_line_syntax_highlighting(minimap_line);
-					}
+					Dictionary color_map = _get_line_syntax_highlighting(minimap_line);
 
 					Color current_color = cache.font_color;
 					if (readonly) {
@@ -978,15 +885,13 @@ void TextEdit::_notification(int p_what) {
 						int characters = 0;
 						int tabs = 0;
 						for (int j = 0; j < str.length(); j++) {
-							if (syntax_coloring) {
-								if (color_map.has(last_wrap_column + j)) {
-									current_color = color_map[last_wrap_column + j].get("color");
-									if (readonly) {
-										current_color.a = cache.font_color_readonly.a;
-									}
+							if (color_map.has(last_wrap_column + j)) {
+								current_color = color_map[last_wrap_column + j].get("color");
+								if (readonly) {
+									current_color.a = cache.font_color_readonly.a;
 								}
-								color = current_color;
 							}
+							color = current_color;
 
 							if (j == 0) {
 								previous_color = color;
@@ -1060,10 +965,8 @@ void TextEdit::_notification(int p_what) {
 
 				const String &fullstr = text[line];
 
-				Dictionary color_map;
-				if (syntax_coloring) {
-					color_map = _get_line_syntax_highlighting(line);
-				}
+				Dictionary color_map = _get_line_syntax_highlighting(line);
+
 				// Ensure we at least use the font color.
 				Color current_color = readonly ? cache.font_color_readonly : cache.font_color;
 
@@ -1253,15 +1156,13 @@ void TextEdit::_notification(int p_what) {
 					// Loop through characters in one line.
 					int j = 0;
 					for (; j < str.length(); j++) {
-						if (syntax_coloring) {
-							if (color_map.has(last_wrap_column + j)) {
-								current_color = color_map[last_wrap_column + j].get("color");
-								if (readonly && current_color.a > cache.font_color_readonly.a) {
-									current_color.a = cache.font_color_readonly.a;
-								}
+						if (color_map.has(last_wrap_column + j)) {
+							current_color = color_map[last_wrap_column + j].get("color");
+							if (readonly && current_color.a > cache.font_color_readonly.a) {
+								current_color.a = cache.font_color_readonly.a;
 							}
-							color = current_color;
 						}
+						color = current_color;
 
 						int char_w;
 
@@ -1447,7 +1348,7 @@ void TextEdit::_notification(int p_what) {
 
 						if (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && block_caret && draw_caret && !insert_mode) {
 							color = cache.caret_background_color;
-						} else if (!syntax_coloring && block_caret) {
+						} else if (block_caret) {
 							color = readonly ? cache.font_color_readonly : cache.font_color;
 						}
 
@@ -1609,12 +1510,6 @@ void TextEdit::_notification(int p_what) {
 				for (int i = 0; i < lines; i++) {
 					int l = line_from + i;
 					ERR_CONTINUE(l < 0 || l >= completion_options_size);
-					Color text_color = cache.completion_font_color;
-					for (int j = 0; j < color_regions.size(); j++) {
-						if (completion_options[l].insert_text.begins_with(color_regions[j].begin_key)) {
-							text_color = color_regions[j].color;
-						}
-					}
 					int yofs = (get_row_height() - cache.font->get_height()) / 2;
 					Point2 title_pos(completion_rect.position.x, completion_rect.position.y + i * get_row_height() + cache.font->get_ascent() + yofs);
 
@@ -1630,7 +1525,7 @@ void TextEdit::_notification(int p_what) {
 					}
 
 					title_pos.x = icon_area.position.x + icon_area.size.width + icon_hsep;
-					draw_string(cache.font, title_pos, completion_options[l].display, text_color, completion_rect.size.width - (icon_area_size.x + icon_hsep));
+					draw_string(cache.font, title_pos, completion_options[l].display, completion_options[l].font_color, completion_rect.size.width - (icon_area_size.x + icon_hsep));
 				}
 
 				if (scrollw) {
@@ -2851,7 +2746,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
 					// Indent once again if previous line will end with ':','{','[','(' and the line is not a comment
 					// (i.e. colon/brace precedes current cursor position).
 					if (cursor.column > 0) {
-						const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(cursor.line);
 						bool indent_char_found = false;
 						bool should_indent = false;
 						char indent_char = ':';
@@ -2870,7 +2764,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
 									continue;
 							}
 
-							if (indent_char_found && cri_map.has(i) && (color_regions[cri_map[i].region].begin_key == "#" || color_regions[cri_map[i].region].begin_key == "//")) {
+							if (indent_char_found && is_line_comment(i)) {
 								should_indent = true;
 								break;
 							} else if (indent_char_found && !_is_whitespace(c)) {
@@ -3943,7 +3837,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
 		}
 		text_changed_dirty = true;
 	}
-	_line_edited_from(p_line);
+	emit_signal("line_edited_from", p_line);
 }
 
 String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const {
@@ -4006,7 +3900,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
 		}
 		text_changed_dirty = true;
 	}
-	_line_edited_from(p_from_line);
+	emit_signal("line_edited_from", p_from_line);
 }
 
 void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r_end_line, int *r_end_char) {
@@ -4126,22 +4020,6 @@ void TextEdit::_insert_text_at_cursor(const String &p_text) {
 	update();
 }
 
-void TextEdit::_line_edited_from(int p_line) {
-	int cache_size = color_region_cache.size();
-	for (int i = p_line; i < cache_size; i++) {
-		color_region_cache.erase(i);
-	}
-
-	if (syntax_highlighting_cache.size() > 0) {
-		cache_size = syntax_highlighting_cache.back()->key();
-		for (int i = p_line - 1; i <= cache_size; i++) {
-			if (syntax_highlighting_cache.has(i)) {
-				syntax_highlighting_cache.erase(i);
-			}
-		}
-	}
-}
-
 int TextEdit::get_char_count() {
 	int totalsize = 0;
 
@@ -4985,10 +4863,6 @@ void TextEdit::_update_caches() {
 	cache.font_color = get_theme_color("font_color");
 	cache.font_color_selected = get_theme_color("font_color_selected");
 	cache.font_color_readonly = get_theme_color("font_color_readonly");
-	cache.keyword_color = get_theme_color("keyword_color");
-	cache.function_color = get_theme_color("function_color");
-	cache.member_variable_color = get_theme_color("member_variable_color");
-	cache.number_color = get_theme_color("number_color");
 	cache.selection_color = get_theme_color("selection_color");
 	cache.mark_color = get_theme_color("mark_color");
 	cache.current_line_color = get_theme_color("current_line_color");
@@ -5001,7 +4875,6 @@ void TextEdit::_update_caches() {
 	cache.word_highlighted_color = get_theme_color("word_highlighted_color");
 	cache.search_result_color = get_theme_color("search_result_color");
 	cache.search_result_border_color = get_theme_color("search_result_border_color");
-	cache.symbol_color = get_theme_color("symbol_color");
 	cache.background_color = get_theme_color("background_color");
 #ifdef TOOLS_ENABLED
 	cache.line_spacing = get_theme_constant("line_spacing") * EDSCALE;
@@ -5018,140 +4891,28 @@ void TextEdit::_update_caches() {
 	text.set_font(cache.font);
 
 	if (syntax_highlighter.is_valid()) {
-		syntax_highlighter->update_cache();
+		syntax_highlighter->set_text_edit(this);
 	}
 }
 
-Ref<SyntaxHighlighter> TextEdit::get_syntax_highlighting() {
+Ref<SyntaxHighlighter> TextEdit::get_syntax_highlighter() {
 	return syntax_highlighter;
 }
 
-void TextEdit::set_syntax_highlighting(Ref<SyntaxHighlighter> p_syntax_highlighter) {
+void TextEdit::set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter) {
 	syntax_highlighter = p_syntax_highlighter;
 	if (syntax_highlighter.is_valid()) {
 		syntax_highlighter->set_text_edit(this);
-		syntax_highlighter->update_cache();
 	}
-	syntax_highlighting_cache.clear();
 	update();
 }
 
-int TextEdit::_is_line_in_region(int p_line) {
-	// Do we have in cache?
-	if (color_region_cache.has(p_line)) {
-		return color_region_cache[p_line];
-	}
-
-	// If not find the closest line we have.
-	int previous_line = p_line - 1;
-	for (; previous_line > -1; previous_line--) {
-		if (color_region_cache.has(p_line)) {
-			break;
-		}
-	}
-
-	// Calculate up to line we need and update the cache along the way.
-	int in_region = color_region_cache[previous_line];
-	if (previous_line == -1) {
-		in_region = -1;
-	}
-	for (int i = previous_line; i < p_line; i++) {
-		const Map<int, Text::ColorRegionInfo> &cri_map = _get_line_color_region_info(i);
-		for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) {
-			const Text::ColorRegionInfo &cri = E->get();
-			if (in_region == -1) {
-				if (!cri.end) {
-					in_region = cri.region;
-				}
-			} else if (in_region == cri.region && !_get_color_region(cri.region).line_only) {
-				if (cri.end || _get_color_region(cri.region).eq) {
-					in_region = -1;
-				}
-			}
-		}
-
-		if (in_region >= 0 && _get_color_region(in_region).line_only) {
-			in_region = -1;
-		}
-
-		color_region_cache[i + 1] = in_region;
-	}
-	return in_region;
-}
-
-TextEdit::ColorRegion TextEdit::_get_color_region(int p_region) const {
-	if (p_region < 0 || p_region >= color_regions.size()) {
-		return ColorRegion();
-	}
-	return color_regions[p_region];
+void TextEdit::add_keyword(const String &p_keyword) {
+	keywords.insert(p_keyword);
 }
 
-Map<int, TextEdit::Text::ColorRegionInfo> TextEdit::_get_line_color_region_info(int p_line) const {
-	if (p_line < 0 || p_line > text.size() - 1) {
-		return Map<int, Text::ColorRegionInfo>();
-	}
-	return text.get_color_region_info(p_line);
-}
-
-void TextEdit::clear_colors() {
+void TextEdit::clear_keywords() {
 	keywords.clear();
-	member_keywords.clear();
-	color_regions.clear();
-	color_region_cache.clear();
-	syntax_highlighting_cache.clear();
-	text.clear_width_cache();
-	update();
-}
-
-void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color) {
-	keywords[p_keyword] = p_color;
-	syntax_highlighting_cache.clear();
-	update();
-}
-
-bool TextEdit::has_keyword_color(String p_keyword) const {
-	return keywords.has(p_keyword);
-}
-
-Color TextEdit::get_keyword_color(String p_keyword) const {
-	ERR_FAIL_COND_V(!keywords.has(p_keyword), Color());
-	return keywords[p_keyword];
-}
-
-void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
-	color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only));
-	syntax_highlighting_cache.clear();
-	text.clear_width_cache();
-	update();
-}
-
-void TextEdit::add_member_keyword(const String &p_keyword, const Color &p_color) {
-	member_keywords[p_keyword] = p_color;
-	syntax_highlighting_cache.clear();
-	update();
-}
-
-bool TextEdit::has_member_color(String p_member) const {
-	return member_keywords.has(p_member);
-}
-
-Color TextEdit::get_member_color(String p_member) const {
-	return member_keywords[p_member];
-}
-
-void TextEdit::clear_member_keywords() {
-	member_keywords.clear();
-	syntax_highlighting_cache.clear();
-	update();
-}
-
-void TextEdit::set_syntax_coloring(bool p_enabled) {
-	syntax_coloring = p_enabled;
-	update();
-}
-
-bool TextEdit::is_syntax_coloring_enabled() const {
-	return syntax_coloring;
 }
 
 void TextEdit::set_auto_indent(bool p_auto_indent) {
@@ -5827,18 +5588,19 @@ bool TextEdit::is_line_comment(int p_line) const {
 	// Checks to see if this line is the start of a comment.
 	ERR_FAIL_INDEX_V(p_line, text.size(), false);
 
-	const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(p_line);
-
 	int line_length = text[p_line].size();
 	for (int i = 0; i < line_length - 1; i++) {
-		if (_is_symbol(text[p_line][i]) && cri_map.has(i)) {
-			const Text::ColorRegionInfo &cri = cri_map[i];
-			return color_regions[cri.region].begin_key == "#" || color_regions[cri.region].begin_key == "//";
-		} else if (_is_whitespace(text[p_line][i])) {
+		if (_is_whitespace(text[p_line][i])) {
 			continue;
-		} else {
-			break;
 		}
+		if (_is_symbol(text[p_line][i])) {
+			if (text[p_line][i] == '\\') {
+				i++; // Skip quoted anything.
+				continue;
+			}
+			return text[p_line][i] == '#' || (i + 1 < line_length && text[p_line][i] == '/' && text[p_line][i + 1] == '/');
+		}
+		break;
 	}
 	return false;
 }
@@ -7053,11 +6815,8 @@ void TextEdit::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &TextEdit::set_override_selected_font_color);
 	ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &TextEdit::is_overriding_selected_font_color);
 
-	ClassDB::bind_method(D_METHOD("set_syntax_coloring", "enable"), &TextEdit::set_syntax_coloring);
-	ClassDB::bind_method(D_METHOD("is_syntax_coloring_enabled"), &TextEdit::is_syntax_coloring_enabled);
-
-	ClassDB::bind_method(D_METHOD("set_syntax_highlighting", "syntax_highlighter"), &TextEdit::set_syntax_highlighting);
-	ClassDB::bind_method(D_METHOD("get_syntax_highlighting"), &TextEdit::get_syntax_highlighting);
+	ClassDB::bind_method(D_METHOD("set_syntax_highlighter", "syntax_highlighter"), &TextEdit::set_syntax_highlighter);
+	ClassDB::bind_method(D_METHOD("get_syntax_highlighter"), &TextEdit::get_syntax_highlighter);
 
 	ClassDB::bind_method(D_METHOD("set_highlight_current_line", "enabled"), &TextEdit::set_highlight_current_line);
 	ClassDB::bind_method(D_METHOD("is_highlight_current_line_enabled"), &TextEdit::is_highlight_current_line_enabled);
@@ -7071,11 +6830,6 @@ void TextEdit::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_h_scroll", "value"), &TextEdit::set_h_scroll);
 	ClassDB::bind_method(D_METHOD("get_h_scroll"), &TextEdit::get_h_scroll);
 
-	ClassDB::bind_method(D_METHOD("add_keyword_color", "keyword", "color"), &TextEdit::add_keyword_color);
-	ClassDB::bind_method(D_METHOD("has_keyword_color", "keyword"), &TextEdit::has_keyword_color);
-	ClassDB::bind_method(D_METHOD("get_keyword_color", "keyword"), &TextEdit::get_keyword_color);
-	ClassDB::bind_method(D_METHOD("add_color_region", "begin_key", "end_key", "color", "line_only"), &TextEdit::add_color_region, DEFVAL(false));
-	ClassDB::bind_method(D_METHOD("clear_colors"), &TextEdit::clear_colors);
 	ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option);
 	ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu);
 
@@ -7090,8 +6844,6 @@ void TextEdit::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "readonly"), "set_readonly", "is_readonly");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled");
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled");
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter"), "set_syntax_highlighting", "get_syntax_highlighting");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces");
@@ -7109,6 +6861,8 @@ void TextEdit::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical"), "set_v_scroll", "get_v_scroll");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
 
+	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter");
+
 	ADD_GROUP("Minimap", "minimap_");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "draw_minimap", "is_drawing_minimap");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "minimap_width"), "set_minimap_width", "get_minimap_width");
@@ -7121,6 +6875,7 @@ void TextEdit::_bind_methods() {
 
 	ADD_SIGNAL(MethodInfo("cursor_changed"));
 	ADD_SIGNAL(MethodInfo("text_changed"));
+	ADD_SIGNAL(MethodInfo("line_edited_from", PropertyInfo(Variant::INT, "line")));
 	ADD_SIGNAL(MethodInfo("request_completion"));
 	ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "row")));
 	ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::INT, "column")));
@@ -7154,7 +6909,6 @@ TextEdit::TextEdit() {
 	wrap_at = 0;
 	wrap_right_offset = 10;
 	set_focus_mode(FOCUS_ALL);
-	syntax_highlighter = Ref<SyntaxHighlighter>(NULL);
 	_update_caches();
 	cache.row_height = 1;
 	cache.line_spacing = 1;
@@ -7170,7 +6924,6 @@ TextEdit::TextEdit() {
 	indent_size = 4;
 	text.set_indent_size(indent_size);
 	text.clear();
-	text.set_color_regions(&color_regions);
 
 	h_scroll = memnew(HScrollBar);
 	v_scroll = memnew(VScrollBar);
@@ -7194,7 +6947,6 @@ TextEdit::TextEdit() {
 	selection.selecting_column = 0;
 	selection.selecting_text = false;
 	selection.active = false;
-	syntax_coloring = false;
 
 	block_caret = false;
 	caret_blink_enabled = false;
@@ -7286,195 +7038,5 @@ TextEdit::~TextEdit() {
 ///////////////////////////////////////////////////////////////////////////////
 
 Dictionary TextEdit::_get_line_syntax_highlighting(int p_line) {
-	if (syntax_highlighting_cache.has(p_line)) {
-		return syntax_highlighting_cache[p_line];
-	}
-
-	if (syntax_highlighter.is_valid()) {
-		Dictionary color_map = syntax_highlighter->get_line_syntax_highlighting(p_line);
-		syntax_highlighting_cache[p_line] = color_map;
-		return color_map;
-	}
-
-	Dictionary color_map;
-
-	bool prev_is_char = false;
-	bool prev_is_number = false;
-	bool in_keyword = false;
-	bool in_word = false;
-	bool in_function_name = false;
-	bool in_member_variable = false;
-	bool is_hex_notation = false;
-	Color keyword_color;
-	Color color;
-
-	int in_region = _is_line_in_region(p_line);
-	int deregion = 0;
-
-	const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text.get_color_region_info(p_line);
-	const String &str = text[p_line];
-	Color prev_color;
-	for (int j = 0; j < str.length(); j++) {
-		Dictionary highlighter_info;
-
-		if (deregion > 0) {
-			deregion--;
-			if (deregion == 0) {
-				in_region = -1;
-			}
-		}
-
-		if (deregion != 0) {
-			if (color != prev_color) {
-				prev_color = color;
-				highlighter_info["color"] = color;
-				color_map[j] = highlighter_info;
-			}
-			continue;
-		}
-
-		color = cache.font_color;
-
-		bool is_char = _is_text_char(str[j]);
-		bool is_symbol = _is_symbol(str[j]);
-		bool is_number = _is_number(str[j]);
-
-		// Allow ABCDEF in hex notation.
-		if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
-			is_number = true;
-		} else {
-			is_hex_notation = false;
-		}
-
-		// Check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation.
-		if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'f' || str[j] == 'e') && !in_word && prev_is_number && !is_number) {
-			is_number = true;
-			is_symbol = false;
-			is_char = false;
-
-			if (str[j] == 'x' && str[j - 1] == '0') {
-				is_hex_notation = true;
-			}
-		}
-
-		if (!in_word && _is_char(str[j]) && !is_number) {
-			in_word = true;
-		}
-
-		if ((in_keyword || in_word) && !is_hex_notation) {
-			is_number = false;
-		}
-
-		if (is_symbol && str[j] != '.' && in_word) {
-			in_word = false;
-		}
-
-		if (is_symbol && cri_map.has(j)) {
-			const TextEdit::Text::ColorRegionInfo &cri = cri_map[j];
-
-			if (in_region == -1) {
-				if (!cri.end) {
-					in_region = cri.region;
-				}
-			} else if (in_region == cri.region && !color_regions[cri.region].line_only) { // Ignore otherwise.
-				if (cri.end || color_regions[cri.region].eq) {
-					deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length();
-				}
-			}
-		}
-
-		if (!is_char) {
-			in_keyword = false;
-		}
-
-		if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
-			int to = j;
-			while (to < str.length() && _is_text_char(str[to])) {
-				to++;
-			}
-
-			uint32_t hash = String::hash(&str[j], to - j);
-			StrRange range(&str[j], to - j);
-
-			const Color *col = keywords.custom_getptr(range, hash);
-
-			if (!col) {
-				col = member_keywords.custom_getptr(range, hash);
-
-				if (col) {
-					for (int k = j - 1; k >= 0; k--) {
-						if (str[k] == '.') {
-							col = nullptr; // Member indexing not allowed.
-							break;
-						} else if (str[k] > 32) {
-							break;
-						}
-					}
-				}
-			}
-
-			if (col) {
-				in_keyword = true;
-				keyword_color = *col;
-			}
-		}
-
-		if (!in_function_name && in_word && !in_keyword) {
-			int k = j;
-			while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
-				k++;
-			}
-
-			// Check for space between name and bracket.
-			while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
-				k++;
-			}
-
-			if (str[k] == '(') {
-				in_function_name = true;
-			}
-		}
-
-		if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
-			int k = j;
-			while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
-				k--;
-			}
-
-			if (str[k] == '.') {
-				in_member_variable = true;
-			}
-		}
-
-		if (is_symbol) {
-			in_function_name = false;
-			in_member_variable = false;
-		}
-
-		if (in_region >= 0) {
-			color = color_regions[in_region].color;
-		} else if (in_keyword) {
-			color = keyword_color;
-		} else if (in_member_variable) {
-			color = cache.member_variable_color;
-		} else if (in_function_name) {
-			color = cache.function_color;
-		} else if (is_symbol) {
-			color = cache.symbol_color;
-		} else if (is_number) {
-			color = cache.number_color;
-		}
-
-		prev_is_char = is_char;
-		prev_is_number = is_number;
-
-		if (color != prev_color) {
-			prev_color = color;
-			highlighter_info["color"] = color;
-			color_map[j] = highlighter_info;
-		}
-	}
-
-	syntax_highlighting_cache[p_line] = color_map;
-	return color_map;
+	return syntax_highlighter.is_null() && !setting_text ? Dictionary() : syntax_highlighter->get_line_syntax_highlighting(p_line);
 }

+ 5 - 60
scene/gui/text_edit.h

@@ -41,32 +41,8 @@ class TextEdit : public Control {
 	GDCLASS(TextEdit, Control);
 
 public:
-	struct ColorRegion {
-		Color color;
-		String begin_key;
-		String end_key;
-		bool line_only;
-		bool eq;
-		ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) {
-			begin_key = p_begin_key;
-			end_key = p_end_key;
-			color = p_color;
-			line_only = p_line_only || p_end_key == "";
-			eq = begin_key == end_key;
-		}
-	};
-
 	class Text {
 	public:
-		struct ColorRegionInfo {
-			int region;
-			bool end;
-			ColorRegionInfo() {
-				region = 0;
-				end = false;
-			}
-		};
-
 		struct Line {
 			int width_cache : 24;
 			bool marked : 1;
@@ -76,7 +52,6 @@ public:
 			bool safe : 1;
 			bool has_info : 1;
 			int wrap_amount_cache : 24;
-			Map<int, ColorRegionInfo> region_info;
 			Ref<Texture2D> info_icon;
 			String info;
 			String data;
@@ -93,7 +68,6 @@ public:
 		};
 
 	private:
-		const Vector<ColorRegion> *color_regions;
 		mutable Vector<Line> text;
 		Ref<Font> font;
 		int indent_size;
@@ -103,13 +77,11 @@ public:
 	public:
 		void set_indent_size(int p_indent_size);
 		void set_font(const Ref<Font> &p_font);
-		void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; }
 		int get_line_width(int p_line) const;
 		int get_max_width(bool p_exclude_hidden = false) const;
 		int get_char_width(CharType c, CharType next_c, int px) const;
 		void set_line_wrap_amount(int p_line, int p_wrap_amount) const;
 		int get_line_wrap_amount(int p_line) const;
-		const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const;
 		void set(int p_line, const String &p_text);
 		void set_marked(int p_line, bool p_marked) { text.write[p_line].marked = p_marked; }
 		bool is_marked(int p_line) const { return text[p_line].marked; }
@@ -219,10 +191,6 @@ private:
 		Color font_color;
 		Color font_color_selected;
 		Color font_color_readonly;
-		Color keyword_color;
-		Color number_color;
-		Color function_color;
-		Color member_variable_color;
 		Color selection_color;
 		Color mark_color;
 		Color bookmark_color;
@@ -235,7 +203,6 @@ private:
 		Color word_highlighted_color;
 		Color search_result_color;
 		Color search_result_border_color;
-		Color symbol_color;
 		Color background_color;
 
 		int row_height;
@@ -256,7 +223,6 @@ private:
 		}
 	} cache;
 
-	Map<int, int> color_region_cache;
 	Map<int, Dictionary> syntax_highlighting_cache;
 
 	struct TextOperation {
@@ -301,13 +267,10 @@ private:
 
 	//syntax coloring
 	Ref<SyntaxHighlighter> syntax_highlighter;
-	HashMap<String, Color> keywords;
-	HashMap<String, Color> member_keywords;
+	Set<String> keywords;
 
 	Dictionary _get_line_syntax_highlighting(int p_line);
 
-	Vector<ColorRegion> color_regions;
-
 	Set<String> completion_prefixes;
 	bool completion_enabled;
 	List<ScriptCodeCompletionOption> completion_sources;
@@ -332,7 +295,6 @@ private:
 
 	int max_chars;
 	bool readonly;
-	bool syntax_coloring;
 	bool indent_using_spaces;
 	int indent_size;
 	String space_indent;
@@ -491,7 +453,6 @@ private:
 	void _update_caches();
 	void _cursor_changed_emit();
 	void _text_changed_emit();
-	void _line_edited_from(int p_line);
 
 	void _push_current_op();
 
@@ -531,12 +492,8 @@ protected:
 	static void _bind_methods();
 
 public:
-	Ref<SyntaxHighlighter> get_syntax_highlighting();
-	void set_syntax_highlighting(Ref<SyntaxHighlighter> p_syntax_highlighter);
-
-	int _is_line_in_region(int p_line);
-	ColorRegion _get_color_region(int p_region) const;
-	Map<int, Text::ColorRegionInfo> _get_line_color_region_info(int p_line) const;
+	Ref<SyntaxHighlighter> get_syntax_highlighter();
+	void set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter);
 
 	enum MenuItems {
 		MENU_CUT,
@@ -666,9 +623,6 @@ public:
 
 	void clear();
 
-	void set_syntax_coloring(bool p_enabled);
-	bool is_syntax_coloring_enabled() const;
-
 	void cut();
 	void copy();
 	void paste();
@@ -713,17 +667,8 @@ public:
 	void set_insert_mode(bool p_enabled);
 	bool is_insert_mode() const;
 
-	void add_keyword_color(const String &p_keyword, const Color &p_color);
-	bool has_keyword_color(String p_keyword) const;
-	Color get_keyword_color(String p_keyword) const;
-
-	void add_color_region(const String &p_begin_key = String(), const String &p_end_key = String(), const Color &p_color = Color(), bool p_line_only = false);
-	void clear_colors();
-
-	void add_member_keyword(const String &p_keyword, const Color &p_color);
-	bool has_member_color(String p_member) const;
-	Color get_member_color(String p_member) const;
-	void clear_member_keywords();
+	void add_keyword(const String &p_keyword);
+	void clear_keywords();
 
 	double get_v_scroll() const;
 	void set_v_scroll(double p_scroll);

+ 1 - 0
scene/register_scene_types.cpp

@@ -348,6 +348,7 @@ void register_scene_types() {
 
 	ClassDB::register_class<TextEdit>();
 	ClassDB::register_class<SyntaxHighlighter>();
+	ClassDB::register_class<CodeHighlighter>();
 
 	ClassDB::register_virtual_class<TreeItem>();
 	ClassDB::register_class<OptionButton>();

+ 0 - 4
scene/resources/default_theme/default_theme.cpp

@@ -405,13 +405,9 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
 	theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8));
 	theme->set_color("caret_color", "TextEdit", control_font_color);
 	theme->set_color("caret_background_color", "TextEdit", Color(0, 0, 0));
-	theme->set_color("symbol_color", "TextEdit", control_font_color_hover);
 	theme->set_color("brace_mismatch_color", "TextEdit", Color(1, 0.2, 0.2));
 	theme->set_color("line_number_color", "TextEdit", Color(0.67, 0.67, 0.67, 0.4));
 	theme->set_color("safe_line_number_color", "TextEdit", Color(0.67, 0.78, 0.67, 0.6));
-	theme->set_color("function_color", "TextEdit", Color(0.4, 0.64, 0.81));
-	theme->set_color("member_variable_color", "TextEdit", Color(0.9, 0.31, 0.35));
-	theme->set_color("number_color", "TextEdit", Color(0.92, 0.58, 0.2));
 	theme->set_color("word_highlighted_color", "TextEdit", Color(0.8, 0.9, 0.9, 0.15));
 
 	theme->set_constant("completion_lines", "TextEdit", 7);

+ 566 - 23
scene/resources/syntax_highlighter.cpp

@@ -34,53 +34,596 @@
 #include "scene/gui/text_edit.h"
 
 Dictionary SyntaxHighlighter::get_line_syntax_highlighting(int p_line) {
-	return call("_get_line_syntax_highlighting", p_line);
+	if (highlighting_cache.has(p_line)) {
+		return highlighting_cache[p_line];
+	}
+
+	Dictionary color_map;
+	if (text_edit == nullptr) {
+		return color_map;
+	}
+
+	ScriptInstance *si = get_script_instance();
+	if (si && si->has_method("_get_line_syntax_highlighting")) {
+		color_map = si->call("_get_line_syntax_highlighting", p_line);
+	} else {
+		color_map = _get_line_syntax_highlighting(p_line);
+	}
+	highlighting_cache[p_line] = color_map;
+	return color_map;
 }
 
-void SyntaxHighlighter::update_cache() {
-	call("_update_cache");
+void SyntaxHighlighter::_line_edited_from(int p_line) {
+	if (highlighting_cache.size() < 1) {
+		return;
+	}
+
+	int cache_size = highlighting_cache.back()->key();
+	for (int i = p_line - 1; i <= cache_size; i++) {
+		if (highlighting_cache.has(i)) {
+			highlighting_cache.erase(i);
+		}
+	}
 }
 
-String SyntaxHighlighter::_get_name() const {
+void SyntaxHighlighter::clear_highlighting_cache() {
+	highlighting_cache.clear();
+
 	ScriptInstance *si = get_script_instance();
-	if (si && si->has_method("_get_name")) {
-		return si->call("_get_name");
+	if (si && si->has_method("_clear_highlighting_cache")) {
+		si->call("_clear_highlighting_cache");
+		return;
 	}
-	return "Unamed";
+	_clear_highlighting_cache();
 }
 
-Array SyntaxHighlighter::_get_supported_languages() const {
+void SyntaxHighlighter::update_cache() {
+	clear_highlighting_cache();
+
+	if (text_edit == nullptr) {
+		return;
+	}
 	ScriptInstance *si = get_script_instance();
-	if (si && si->has_method("_get_supported_languages")) {
-		return si->call("_get_supported_languages");
+	if (si && si->has_method("_update_cache")) {
+		si->call("_update_cache");
+		return;
 	}
-	return Array();
+	_update_cache();
 }
 
 void SyntaxHighlighter::set_text_edit(TextEdit *p_text_edit) {
+	if (text_edit && ObjectDB::get_instance(text_edit_instance_id)) {
+		text_edit->disconnect("line_edited_from", callable_mp(this, &SyntaxHighlighter::_line_edited_from));
+	}
+
 	text_edit = p_text_edit;
+	if (p_text_edit == nullptr) {
+		return;
+	}
+	text_edit_instance_id = text_edit->get_instance_id();
+	text_edit->connect("line_edited_from", callable_mp(this, &SyntaxHighlighter::_line_edited_from));
+	update_cache();
 }
 
 TextEdit *SyntaxHighlighter::get_text_edit() {
 	return text_edit;
 }
 
-Ref<SyntaxHighlighter> SyntaxHighlighter::_create() const {
-	Ref<SyntaxHighlighter> syntax_highlighter;
-	syntax_highlighter.instance();
-	if (get_script_instance()) {
-		syntax_highlighter->set_script(get_script_instance()->get_script());
-	}
-	return syntax_highlighter;
-}
-
 void SyntaxHighlighter::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("get_line_syntax_highlighting", "p_line"), &SyntaxHighlighter::get_line_syntax_highlighting);
+	ClassDB::bind_method(D_METHOD("update_cache"), &SyntaxHighlighter::update_cache);
+	ClassDB::bind_method(D_METHOD("clear_highlighting_cache"), &SyntaxHighlighter::clear_highlighting_cache);
+	ClassDB::bind_method(D_METHOD("get_text_edit"), &SyntaxHighlighter::get_text_edit);
+
 	ClassDB::bind_method(D_METHOD("_get_line_syntax_highlighting", "p_line"), &SyntaxHighlighter::_get_line_syntax_highlighting);
 	ClassDB::bind_method(D_METHOD("_update_cache"), &SyntaxHighlighter::_update_cache);
-	ClassDB::bind_method(D_METHOD("get_text_edit"), &SyntaxHighlighter::get_text_edit);
+	ClassDB::bind_method(D_METHOD("_clear_highlighting_cache"), &SyntaxHighlighter::_clear_highlighting_cache);
 
-	BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_name"));
-	BIND_VMETHOD(MethodInfo(Variant::ARRAY, "_get_supported_languages"));
 	BIND_VMETHOD(MethodInfo(Variant::DICTIONARY, "_get_line_syntax_highlighting", PropertyInfo(Variant::INT, "p_line")));
 	BIND_VMETHOD(MethodInfo("_update_cache"));
 }
+
+////////////////////////////////////////////////////////////////////////////////
+
+static bool _is_char(CharType c) {
+	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
+}
+
+static bool _is_hex_symbol(CharType c) {
+	return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+Dictionary CodeHighlighter::_get_line_syntax_highlighting(int p_line) {
+	Dictionary color_map;
+
+	bool prev_is_char = false;
+	bool prev_is_number = false;
+	bool in_keyword = false;
+	bool in_word = false;
+	bool in_function_name = false;
+	bool in_member_variable = false;
+	bool is_hex_notation = false;
+	Color keyword_color;
+	Color color;
+
+	color_region_cache[p_line] = -1;
+	int in_region = -1;
+	if (p_line != 0) {
+		if (!color_region_cache.has(p_line - 1)) {
+			get_line_syntax_highlighting(p_line - 1);
+		}
+		in_region = color_region_cache[p_line - 1];
+	}
+
+	const String &str = text_edit->get_line(p_line);
+	const int line_length = str.length();
+	Color prev_color;
+	for (int j = 0; j < line_length; j++) {
+		Dictionary highlighter_info;
+
+		color = font_color;
+		bool is_char = !is_symbol(str[j]);
+		bool is_a_symbol = is_symbol(str[j]);
+		bool is_number = (str[j] >= '0' && str[j] <= '9');
+
+		/* color regions */
+		if (is_a_symbol || in_region != -1) {
+			int from = j;
+			for (; from < line_length; from++) {
+				if (str[from] == '\\') {
+					from++;
+					continue;
+				}
+				break;
+			}
+
+			if (from != line_length) {
+				/* check if we are in entering a region */
+				if (in_region == -1) {
+					for (int c = 0; c < color_regions.size(); c++) {
+						/* check there is enough room */
+						int chars_left = line_length - from;
+						int start_key_length = color_regions[c].start_key.length();
+						int end_key_length = color_regions[c].end_key.length();
+						if (chars_left < start_key_length) {
+							continue;
+						}
+
+						/* search the line */
+						bool match = true;
+						const CharType *start_key = color_regions[c].start_key.c_str();
+						for (int k = 0; k < start_key_length; k++) {
+							if (start_key[k] != str[from + k]) {
+								match = false;
+								break;
+							}
+						}
+						if (!match) {
+							continue;
+						}
+						in_region = c;
+						from += start_key_length;
+
+						/* check if it's the whole line */
+						if (end_key_length == 0 || color_regions[c].line_only || from + end_key_length > line_length) {
+							prev_color = color_regions[in_region].color;
+							highlighter_info["color"] = color_regions[c].color;
+							color_map[j] = highlighter_info;
+
+							j = line_length;
+							if (!color_regions[c].line_only) {
+								color_region_cache[p_line] = c;
+							}
+						}
+						break;
+					}
+
+					if (j == line_length) {
+						continue;
+					}
+				}
+
+				/* if we are in one find the end key */
+				if (in_region != -1) {
+					/* check there is enough room */
+					int chars_left = line_length - from;
+					int end_key_length = color_regions[in_region].end_key.length();
+					if (chars_left < end_key_length) {
+						continue;
+					}
+
+					/* search the line */
+					int region_end_index = -1;
+					const CharType *end_key = color_regions[in_region].start_key.c_str();
+					for (; from < line_length; from++) {
+						if (!is_a_symbol) {
+							continue;
+						}
+
+						if (str[from] == '\\') {
+							from++;
+							continue;
+						}
+
+						for (int k = 0; k < end_key_length; k++) {
+							if (end_key[k] == str[from + k]) {
+								region_end_index = from;
+								break;
+							}
+						}
+
+						if (region_end_index != -1) {
+							break;
+						}
+					}
+
+					prev_color = color_regions[in_region].color;
+					highlighter_info["color"] = color_regions[in_region].color;
+					color_map[j] = highlighter_info;
+
+					j = from;
+					if (region_end_index == -1) {
+						color_region_cache[p_line] = in_region;
+					}
+
+					in_region = -1;
+					prev_is_char = false;
+					prev_is_number = false;
+					continue;
+				}
+			}
+		}
+
+		// Allow ABCDEF in hex notation.
+		if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+			is_number = true;
+		} else {
+			is_hex_notation = false;
+		}
+
+		// Check for dot or underscore or 'x' for hex notation in floating point number or 'e' for scientific notation.
+		if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'f' || str[j] == 'e') && !in_word && prev_is_number && !is_number) {
+			is_number = true;
+			is_a_symbol = false;
+			is_char = false;
+
+			if (str[j] == 'x' && str[j - 1] == '0') {
+				is_hex_notation = true;
+			}
+		}
+
+		if (!in_word && _is_char(str[j]) && !is_number) {
+			in_word = true;
+		}
+
+		if ((in_keyword || in_word) && !is_hex_notation) {
+			is_number = false;
+		}
+
+		if (is_a_symbol && str[j] != '.' && in_word) {
+			in_word = false;
+		}
+
+		if (!is_char) {
+			in_keyword = false;
+		}
+
+		if (!in_keyword && is_char && !prev_is_char) {
+			int to = j;
+			while (to < line_length && !is_symbol(str[to])) {
+				to++;
+			}
+
+			String word = str.substr(j, to - j);
+			Color col = Color();
+			if (keywords.has(word)) {
+				col = keywords[word];
+			} else if (member_keywords.has(word)) {
+				col = member_keywords[word];
+				for (int k = j - 1; k >= 0; k--) {
+					if (str[k] == '.') {
+						col = Color(); //member indexing not allowed
+						break;
+					} else if (str[k] > 32) {
+						break;
+					}
+				}
+			}
+
+			if (col != Color()) {
+				in_keyword = true;
+				keyword_color = col;
+			}
+		}
+
+		if (!in_function_name && in_word && !in_keyword) {
+			int k = j;
+			while (k < line_length && !is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+				k++;
+			}
+
+			// Check for space between name and bracket.
+			while (k < line_length && (str[k] == '\t' || str[k] == ' ')) {
+				k++;
+			}
+
+			if (str[k] == '(') {
+				in_function_name = true;
+			}
+		}
+
+		if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
+			int k = j;
+			while (k > 0 && !is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+				k--;
+			}
+
+			if (str[k] == '.') {
+				in_member_variable = true;
+			}
+		}
+
+		if (is_a_symbol) {
+			in_function_name = false;
+			in_member_variable = false;
+		}
+
+		if (in_keyword) {
+			color = keyword_color;
+		} else if (in_member_variable) {
+			color = member_color;
+		} else if (in_function_name) {
+			color = function_color;
+		} else if (is_a_symbol) {
+			color = symbol_color;
+		} else if (is_number) {
+			color = number_color;
+		}
+
+		prev_is_char = is_char;
+		prev_is_number = is_number;
+
+		if (color != prev_color) {
+			prev_color = color;
+			highlighter_info["color"] = color;
+			color_map[j] = highlighter_info;
+		}
+	}
+
+	return color_map;
+}
+
+void CodeHighlighter::_clear_highlighting_cache() {
+	color_region_cache.clear();
+}
+
+void CodeHighlighter::_update_cache() {
+	font_color = text_edit->get_theme_color("font_color");
+}
+
+void CodeHighlighter::add_keyword_color(const String &p_keyword, const Color &p_color) {
+	keywords[p_keyword] = p_color;
+	clear_highlighting_cache();
+}
+
+void CodeHighlighter::remove_keyword_color(const String &p_keyword) {
+	keywords.erase(p_keyword);
+	clear_highlighting_cache();
+}
+
+bool CodeHighlighter::has_keyword_color(const String &p_keyword) const {
+	return keywords.has(p_keyword);
+}
+
+Color CodeHighlighter::get_keyword_color(const String &p_keyword) const {
+	ERR_FAIL_COND_V(!keywords.has(p_keyword), Color());
+	return keywords[p_keyword];
+}
+
+void CodeHighlighter::set_keyword_colors(const Dictionary p_keywords) {
+	keywords.clear();
+	keywords = p_keywords;
+	clear_highlighting_cache();
+}
+
+void CodeHighlighter::clear_keyword_colors() {
+	keywords.clear();
+	clear_highlighting_cache();
+}
+
+Dictionary CodeHighlighter::get_keyword_colors() const {
+	return keywords;
+}
+
+void CodeHighlighter::add_member_keyword_color(const String &p_member_keyword, const Color &p_color) {
+	member_keywords[p_member_keyword] = p_color;
+	clear_highlighting_cache();
+}
+
+void CodeHighlighter::remove_member_keyword_color(const String &p_member_keyword) {
+	member_keywords.erase(p_member_keyword);
+	clear_highlighting_cache();
+}
+
+bool CodeHighlighter::has_member_keyword_color(const String &p_member_keyword) const {
+	return member_keywords.has(p_member_keyword);
+}
+
+Color CodeHighlighter::get_member_keyword_color(const String &p_member_keyword) const {
+	ERR_FAIL_COND_V(!member_keywords.has(p_member_keyword), Color());
+	return member_keywords[p_member_keyword];
+}
+
+void CodeHighlighter::set_member_keyword_colors(const Dictionary &p_member_keywords) {
+	member_keywords.clear();
+	member_keywords = p_member_keywords;
+	clear_highlighting_cache();
+}
+
+void CodeHighlighter::clear_member_keyword_colors() {
+	member_keywords.clear();
+	clear_highlighting_cache();
+}
+
+Dictionary CodeHighlighter::get_member_keyword_colors() const {
+	return member_keywords;
+}
+
+void CodeHighlighter::add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
+	for (int i = 0; i < p_start_key.length(); i++) {
+		ERR_FAIL_COND_MSG(!is_symbol(p_start_key[i]), "color regions must start with a symbol");
+	}
+
+	if (p_end_key.length() > 0) {
+		for (int i = 0; i < p_end_key.length(); i++) {
+			ERR_FAIL_COND_MSG(!is_symbol(p_end_key[i]), "color regions must end with a symbol");
+		}
+	}
+
+	for (int i = 0; i < color_regions.size(); i++) {
+		ERR_FAIL_COND_MSG(color_regions[i].start_key == p_start_key, "color region with start key '" + p_start_key + "' already exists.");
+	}
+
+	ColorRegion color_region;
+	color_region.color = p_color;
+	color_region.start_key = p_start_key;
+	color_region.end_key = p_end_key;
+	color_region.line_only = p_line_only;
+	color_regions.push_back(color_region);
+	clear_highlighting_cache();
+}
+
+void CodeHighlighter::remove_color_region(const String &p_start_key) {
+	for (int i = 0; i < color_regions.size(); i++) {
+		if (color_regions[i].start_key == p_start_key) {
+			color_regions.remove(i);
+			break;
+		}
+	}
+	clear_highlighting_cache();
+}
+
+bool CodeHighlighter::has_color_region(const String &p_start_key) const {
+	for (int i = 0; i < color_regions.size(); i++) {
+		if (color_regions[i].start_key == p_start_key) {
+			return true;
+		}
+	}
+	return false;
+}
+
+void CodeHighlighter::set_color_regions(const Dictionary &p_color_regions) {
+	color_regions.clear();
+
+	List<Variant> keys;
+	p_color_regions.get_key_list(&keys);
+
+	for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
+		String key = E->get();
+
+		String start_key = key.get_slice(" ", 0);
+		String end_key = key.get_slice_count(" ") > 1 ? key.get_slice(" ", 1) : String();
+
+		add_color_region(start_key, end_key, p_color_regions[key], end_key == "");
+	}
+	clear_highlighting_cache();
+}
+
+void CodeHighlighter::clear_color_regions() {
+	color_regions.clear();
+	clear_highlighting_cache();
+}
+
+Dictionary CodeHighlighter::get_color_regions() const {
+	Dictionary r_color_regions;
+	for (int i = 0; i < color_regions.size(); i++) {
+		ColorRegion region = color_regions[i];
+		r_color_regions[region.start_key + (region.end_key.empty() ? "" : " " + region.end_key)] = region.color;
+	}
+	return r_color_regions;
+}
+
+void CodeHighlighter::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("add_keyword_color", "keyword", "color"), &CodeHighlighter::add_keyword_color);
+	ClassDB::bind_method(D_METHOD("remove_keyword_color", "keyword"), &CodeHighlighter::remove_keyword_color);
+	ClassDB::bind_method(D_METHOD("has_keyword_color", "keyword"), &CodeHighlighter::has_keyword_color);
+	ClassDB::bind_method(D_METHOD("get_keyword_color", "keyword"), &CodeHighlighter::get_keyword_color);
+
+	ClassDB::bind_method(D_METHOD("set_keyword_colors", "keywords"), &CodeHighlighter::set_keyword_colors);
+	ClassDB::bind_method(D_METHOD("clear_keyword_colors"), &CodeHighlighter::clear_keyword_colors);
+	ClassDB::bind_method(D_METHOD("get_keyword_colors"), &CodeHighlighter::get_keyword_colors);
+
+	ClassDB::bind_method(D_METHOD("add_member_keyword_color", "member_keyword", "color"), &CodeHighlighter::add_member_keyword_color);
+	ClassDB::bind_method(D_METHOD("remove_member_keyword_color", "member_keyword"), &CodeHighlighter::remove_member_keyword_color);
+	ClassDB::bind_method(D_METHOD("has_member_keyword_color", "member_keyword"), &CodeHighlighter::has_member_keyword_color);
+	ClassDB::bind_method(D_METHOD("get_member_keyword_color", "member_keyword"), &CodeHighlighter::get_member_keyword_color);
+
+	ClassDB::bind_method(D_METHOD("set_member_keyword_colors", "member_keyword"), &CodeHighlighter::set_member_keyword_colors);
+	ClassDB::bind_method(D_METHOD("clear_member_keyword_colors"), &CodeHighlighter::clear_member_keyword_colors);
+	ClassDB::bind_method(D_METHOD("get_member_keyword_colors"), &CodeHighlighter::get_member_keyword_colors);
+
+	ClassDB::bind_method(D_METHOD("add_color_region", "p_start_key", "p_end_key", "p_color", "p_line_only"), &CodeHighlighter::add_color_region, DEFVAL(false));
+	ClassDB::bind_method(D_METHOD("remove_color_region", "p_start_key"), &CodeHighlighter::remove_color_region);
+	ClassDB::bind_method(D_METHOD("has_color_region", "p_start_key"), &CodeHighlighter::has_color_region);
+
+	ClassDB::bind_method(D_METHOD("set_color_regions", "p_color_regions"), &CodeHighlighter::set_color_regions);
+	ClassDB::bind_method(D_METHOD("clear_color_regions"), &CodeHighlighter::clear_color_regions);
+	ClassDB::bind_method(D_METHOD("get_color_regions"), &CodeHighlighter::get_color_regions);
+
+	ClassDB::bind_method(D_METHOD("set_function_color", "color"), &CodeHighlighter::set_function_color);
+	ClassDB::bind_method(D_METHOD("get_function_color"), &CodeHighlighter::get_function_color);
+
+	ClassDB::bind_method(D_METHOD("set_number_color", "color"), &CodeHighlighter::set_number_color);
+	ClassDB::bind_method(D_METHOD("get_number_color"), &CodeHighlighter::get_number_color);
+
+	ClassDB::bind_method(D_METHOD("set_symbol_color", "color"), &CodeHighlighter::set_symbol_color);
+	ClassDB::bind_method(D_METHOD("get_symbol_color"), &CodeHighlighter::get_symbol_color);
+
+	ClassDB::bind_method(D_METHOD("set_member_variable_color", "color"), &CodeHighlighter::set_member_variable_color);
+	ClassDB::bind_method(D_METHOD("get_member_variable_color"), &CodeHighlighter::get_member_variable_color);
+
+	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "number_color"), "set_number_color", "get_number_color");
+	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "symbol_color"), "set_symbol_color", "get_symbol_color");
+	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "function_color"), "set_function_color", "get_function_color");
+	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "member_variable_color"), "set_member_variable_color", "get_member_variable_color");
+
+	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "keyword_colors"), "set_keyword_colors", "get_keyword_colors");
+	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "member_keyword_colors"), "set_member_keyword_colors", "get_member_keyword_colors");
+	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "color_regions"), "set_color_regions", "get_color_regions");
+}
+
+void CodeHighlighter::set_number_color(Color p_color) {
+	number_color = p_color;
+	clear_highlighting_cache();
+}
+
+Color CodeHighlighter::get_number_color() const {
+	return number_color;
+}
+
+void CodeHighlighter::set_symbol_color(Color p_color) {
+	symbol_color = p_color;
+	clear_highlighting_cache();
+}
+
+Color CodeHighlighter::get_symbol_color() const {
+	return symbol_color;
+}
+
+void CodeHighlighter::set_function_color(Color p_color) {
+	function_color = p_color;
+	clear_highlighting_cache();
+}
+
+Color CodeHighlighter::get_function_color() const {
+	return function_color;
+}
+
+void CodeHighlighter::set_member_variable_color(Color p_color) {
+	member_color = p_color;
+	clear_highlighting_cache();
+}
+
+Color CodeHighlighter::get_member_variable_color() const {
+	return member_color;
+}

+ 81 - 6
scene/resources/syntax_highlighter.h

@@ -38,8 +38,13 @@ class TextEdit;
 class SyntaxHighlighter : public Resource {
 	GDCLASS(SyntaxHighlighter, Resource)
 
+private:
+	Map<int, Dictionary> highlighting_cache;
+	void _line_edited_from(int p_line);
+
 protected:
-	TextEdit *text_edit;
+	ObjectID text_edit_instance_id; // For validity check
+	TextEdit *text_edit = nullptr;
 
 	static void _bind_methods();
 
@@ -47,19 +52,89 @@ public:
 	Dictionary get_line_syntax_highlighting(int p_line);
 	virtual Dictionary _get_line_syntax_highlighting(int p_line) { return Dictionary(); }
 
+	void clear_highlighting_cache();
+	virtual void _clear_highlighting_cache() {}
+
 	void update_cache();
 	virtual void _update_cache() {}
 
-	virtual String _get_name() const;
-	virtual Array _get_supported_languages() const;
-
 	void set_text_edit(TextEdit *p_text_edit);
 	TextEdit *get_text_edit();
 
-	virtual Ref<SyntaxHighlighter> _create() const;
-
 	SyntaxHighlighter() {}
 	virtual ~SyntaxHighlighter() {}
 };
 
+///////////////////////////////////////////////////////////////////////////////
+
+class CodeHighlighter : public SyntaxHighlighter {
+	GDCLASS(CodeHighlighter, SyntaxHighlighter)
+
+private:
+	struct ColorRegion {
+		Color color;
+		String start_key;
+		String end_key;
+		bool line_only;
+	};
+	Vector<ColorRegion> color_regions;
+	Map<int, int> color_region_cache;
+
+	Dictionary keywords;
+	Dictionary member_keywords;
+
+	Color font_color;
+	Color member_color;
+	Color function_color;
+	Color symbol_color;
+	Color number_color;
+
+protected:
+	static void _bind_methods();
+
+public:
+	virtual Dictionary _get_line_syntax_highlighting(int p_line) override;
+
+	virtual void _clear_highlighting_cache() override;
+	virtual void _update_cache() override;
+
+	void add_keyword_color(const String &p_keyword, const Color &p_color);
+	void remove_keyword_color(const String &p_keyword);
+	bool has_keyword_color(const String &p_keyword) const;
+	Color get_keyword_color(const String &p_keyword) const;
+
+	void set_keyword_colors(const Dictionary p_keywords);
+	void clear_keyword_colors();
+	Dictionary get_keyword_colors() const;
+
+	void add_member_keyword_color(const String &p_member_keyword, const Color &p_color);
+	void remove_member_keyword_color(const String &p_member_keyword);
+	bool has_member_keyword_color(const String &p_member_keyword) const;
+	Color get_member_keyword_color(const String &p_member_keyword) const;
+
+	void set_member_keyword_colors(const Dictionary &p_color_regions);
+	void clear_member_keyword_colors();
+	Dictionary get_member_keyword_colors() const;
+
+	void add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only = false);
+	void remove_color_region(const String &p_start_key);
+	bool has_color_region(const String &p_start_key) const;
+
+	void set_color_regions(const Dictionary &p_member_keyword);
+	void clear_color_regions();
+	Dictionary get_color_regions() const;
+
+	void set_number_color(Color p_color);
+	Color get_number_color() const;
+
+	void set_symbol_color(Color p_color);
+	Color get_symbol_color() const;
+
+	void set_function_color(Color p_color);
+	Color get_function_color() const;
+
+	void set_member_variable_color(Color p_color);
+	Color get_member_variable_color() const;
+};
+
 #endif