diff options
25 files changed, 1324 insertions, 926 deletions
diff --git a/core/script_language.h b/core/script_language.h index ec06e1355d..314b047027 100644 --- a/core/script_language.h +++ b/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() {} diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 9e1a5f2d43..d8648310b6 100644 --- a/editor/code_editor.cpp +++ b/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; } diff --git a/editor/code_editor.h b/editor/code_editor.h index d3090b28c1..450c85c64b 100644 --- a/editor/code_editor.h +++ b/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; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 2f669d9006..0da7e5210a 100644 --- a/editor/editor_node.cpp +++ b/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>(); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 5f293f1fb3..3a1a0d5b01 100644 --- a/editor/editor_settings.cpp +++ b/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); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 237aefb1b9..67268e0027 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/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); } diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 4ce3b0e4ad..3891af4091 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/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); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 875e6ef350..1e03d9dfab 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/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")); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index d9ba579102..6d7f84d746 100644 --- a/editor/plugins/script_text_editor.h +++ b/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; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 7dd0b8a238..3867c8538a 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/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")); diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 6ad3413dd7..904aed186a 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/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(); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 25c0da90ad..d602d152fe 100644 --- a/editor/plugins/text_editor.cpp +++ b/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")); diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h index 0db8f70a85..5299776b56 100644 --- a/editor/plugins/text_editor.h +++ b/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; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index c19140ae7c..ea6c5edcef 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/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); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index dd9144c567..0601b35131 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/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; diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 8ab3dc1d56..43d0116125 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/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; diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index c1de22167a..49357f3d2e 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/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 diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index c8ecf0c283..2fcd9332a1 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/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() { diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index f39def0b32..e59618e120 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/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; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index a56262e552..07ebdb6523 100644 --- a/scene/gui/text_edit.cpp +++ b/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); } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index b916c80377..5a6ed99845 100644 --- a/scene/gui/text_edit.h +++ b/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); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 98678cd2e3..0ad1d39755 100644 --- a/scene/register_scene_types.cpp +++ b/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>(); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index b0c01da2b0..9008f6d5b9 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/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); diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp index f4fc12fa19..abf7235fd6 100644 --- a/scene/resources/syntax_highlighter.cpp +++ b/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; +} diff --git a/scene/resources/syntax_highlighter.h b/scene/resources/syntax_highlighter.h index 7059ef4ce1..40a8870b45 100644 --- a/scene/resources/syntax_highlighter.h +++ b/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 |