From a38b447413b1b00a01d68afbaebc3cd25760824a Mon Sep 17 00:00:00 2001
From: Hugo Locurcio <hugo.locurcio@hugo.pro>
Date: Tue, 12 Nov 2019 17:08:07 +0100
Subject: [PATCH] Highlight control flow keywords with a different color

This makes them easier to distinguish from other keywords.
---
 core/script_language.h                        |  1 +
 editor/editor_settings.cpp                    |  1 +
 editor/editor_themes.cpp                      |  2 ++
 editor/plugins/editor_preview_plugins.cpp     | 24 +++++++++++++++----
 editor/plugins/script_text_editor.cpp         |  9 ++++++-
 editor/plugins/script_text_editor.h           |  1 +
 editor/plugins/shader_editor_plugin.cpp       |  7 +++++-
 editor/plugins/text_editor.cpp                |  2 ++
 editor/plugins/text_editor.h                  |  1 +
 .../plugins/visual_shader_editor_plugin.cpp   | 14 +++++++++--
 .../gdnative/nativescript/nativescript.cpp    |  3 +++
 modules/gdnative/nativescript/nativescript.h  |  1 +
 .../pluginscript/pluginscript_language.cpp    |  4 ++++
 .../pluginscript/pluginscript_language.h      |  1 +
 modules/gdscript/gdscript.cpp                 | 13 ++++++++++
 modules/gdscript/gdscript.h                   |  1 +
 modules/mono/csharp_script.cpp                | 20 ++++++++++++++++
 modules/mono/csharp_script.h                  |  1 +
 modules/visual_script/visual_script.cpp       |  3 +++
 modules/visual_script/visual_script.h         |  1 +
 scene/gui/text_edit.cpp                       |  1 +
 scene/gui/text_edit.h                         |  1 +
 servers/visual/shader_language.cpp            | 14 +++++++++++
 servers/visual/shader_language.h              |  1 +
 24 files changed, 119 insertions(+), 8 deletions(-)

diff --git a/core/script_language.h b/core/script_language.h
index a53991f9006..9013dd406aa 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -265,6 +265,7 @@ public:
 	};
 
 	virtual void get_reserved_words(List<String> *p_words) const = 0;
+	virtual bool is_control_flow_keyword(String p_string) const = 0;
 	virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0;
 	virtual void get_string_delimiters(List<String> *p_delimiters) const = 0;
 	virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0;
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index a0d77329fe7..e7df987e4e6 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -703,6 +703,7 @@ void EditorSettings::_load_default_text_editor_theme() {
 
 	_initial_set("text_editor/highlighting/symbol_color", Color(0.73, 0.87, 1.0));
 	_initial_set("text_editor/highlighting/keyword_color", Color(1.0, 1.0, 0.7));
+	_initial_set("text_editor/highlighting/control_flow_keyword_color", Color(1.0, 0.85, 0.7));
 	_initial_set("text_editor/highlighting/base_type_color", Color(0.64, 1.0, 0.83));
 	_initial_set("text_editor/highlighting/engine_type_color", Color(0.51, 0.83, 1.0));
 	_initial_set("text_editor/highlighting/user_type_color", Color(0.42, 0.67, 0.93));
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index c1e364ae628..15f38757a1d 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -1260,6 +1260,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
 
 	const Color symbol_color = Color(0.34, 0.57, 1.0).linear_interpolate(mono_color, dark_theme ? 0.5 : 0.3);
 	const Color keyword_color = Color(1.0, 0.44, 0.52);
+	const Color control_flow_keyword_color = dark_theme ? Color(1.0, 0.55, 0.8) : Color(0.8, 0.4, 0.6);
 	const Color basetype_color = dark_theme ? Color(0.26, 1.0, 0.76) : Color(0.0, 0.76, 0.38);
 	const Color type_color = basetype_color.linear_interpolate(mono_color, dark_theme ? 0.4 : 0.3);
 	const Color usertype_color = basetype_color.linear_interpolate(mono_color, dark_theme ? 0.7 : 0.5);
@@ -1299,6 +1300,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
 	if (text_editor_color_theme == "Adaptive") {
 		setting->set_initial_value("text_editor/highlighting/symbol_color", symbol_color, true);
 		setting->set_initial_value("text_editor/highlighting/keyword_color", keyword_color, true);
+		setting->set_initial_value("text_editor/highlighting/control_flow_keyword_color", control_flow_keyword_color, true);
 		setting->set_initial_value("text_editor/highlighting/base_type_color", basetype_color, true);
 		setting->set_initial_value("text_editor/highlighting/engine_type_color", type_color, true);
 		setting->set_initial_value("text_editor/highlighting/user_type_color", usertype_color, true);
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 5c66cc89355..1218d4047d6 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -491,10 +491,15 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size2
 	List<String> kwors;
 	scr->get_language()->get_reserved_words(&kwors);
 
+	Set<String> control_flow_keywords;
 	Set<String> keywords;
 
 	for (List<String>::Element *E = kwors.front(); E; E = E->next()) {
-		keywords.insert(E->get());
+		if (scr->get_language()->is_control_flow_keyword(E->get())) {
+			control_flow_keywords.insert(E->get());
+		} else {
+			keywords.insert(E->get());
+		}
 	}
 
 	int line = 0;
@@ -506,6 +511,7 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size2
 
 	Color bg_color = EditorSettings::get_singleton()->get("text_editor/highlighting/background_color");
 	Color keyword_color = EditorSettings::get_singleton()->get("text_editor/highlighting/keyword_color");
+	Color control_flow_keyword_color = EditorSettings::get_singleton()->get("text_editor/highlighting/control_flow_keyword_color");
 	Color text_color = EditorSettings::get_singleton()->get("text_editor/highlighting/text_color");
 	Color symbol_color = EditorSettings::get_singleton()->get("text_editor/highlighting/symbol_color");
 	Color comment_color = EditorSettings::get_singleton()->get("text_editor/highlighting/comment_color");
@@ -529,6 +535,7 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size2
 	col = x0;
 
 	bool prev_is_text = false;
+	bool in_control_flow_keyword = false;
 	bool in_keyword = false;
 	bool in_comment = false;
 	for (int i = 0; i < code.length(); i++) {
@@ -547,6 +554,7 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size2
 					if (c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t')) {
 						//make symbol a little visible
 						color = symbol_color;
+						in_control_flow_keyword = false;
 						in_keyword = false;
 					} else if (!prev_is_text && _is_text_char(c)) {
 						int pos = i;
@@ -554,16 +562,23 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size2
 						while (_is_text_char(code[pos])) {
 							pos++;
 						}
-						String word = code.substr(i, pos - i);
-						if (keywords.has(word)) {
+
+						const String word = code.substr(i, pos - i);
+
+						if (control_flow_keywords.has(word)) {
+							in_control_flow_keyword = true;
+						} else if (keywords.has(word)) {
 							in_keyword = true;
 						}
 
 					} else if (!_is_text_char(c)) {
+						in_control_flow_keyword = false;
 						in_keyword = false;
 					}
 
-					if (in_keyword) {
+					if (in_control_flow_keyword) {
+						color = control_flow_keyword_color;
+					} else if (in_keyword) {
 						color = keyword_color;
 					}
 				}
@@ -578,6 +593,7 @@ Ref<Texture> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size2
 			col++;
 		} else {
 			prev_is_text = false;
+			in_control_flow_keyword = false;
 			in_keyword = false;
 
 			if (c == '\n') {
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index c9ec0089ef5..e603ab76818 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -232,6 +232,7 @@ void ScriptTextEditor::_load_theme_settings() {
 	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 control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_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");
@@ -271,6 +272,7 @@ void ScriptTextEditor::_load_theme_settings() {
 
 	colors_cache.symbol_color = symbol_color;
 	colors_cache.keyword_color = keyword_color;
+	colors_cache.control_flow_keyword_color = control_flow_keyword_color;
 	colors_cache.basetype_color = basetype_color;
 	colors_cache.type_color = type_color;
 	colors_cache.usertype_color = usertype_color;
@@ -294,7 +296,12 @@ void ScriptTextEditor::_set_theme_for_script() {
 	script->get_language()->get_reserved_words(&keywords);
 
 	for (List<String>::Element *E = keywords.front(); E; E = E->next()) {
-		text_edit->add_keyword_color(E->get(), colors_cache.keyword_color);
+		if (script->get_language()->is_control_flow_keyword(E->get())) {
+			// Use a different color for control flow keywords to make them easier to distinguish.
+			text_edit->add_keyword_color(E->get(), colors_cache.control_flow_keyword_color);
+		} else {
+			text_edit->add_keyword_color(E->get(), colors_cache.keyword_color);
+		}
 	}
 
 	//colorize core types
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 70d96e48f74..794f5495b33 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -91,6 +91,7 @@ class ScriptTextEditor : public ScriptEditorBase {
 	struct ColorsCache {
 		Color symbol_color;
 		Color keyword_color;
+		Color control_flow_keyword_color;
 		Color basetype_color;
 		Color type_color;
 		Color usertype_color;
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index bf7aa67a4a9..ab19a3d299e 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -113,6 +113,7 @@ void ShaderTextEditor::_load_theme_settings() {
 	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 control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
 	Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
 
 	get_text_edit()->add_color_override("background_color", background_color);
@@ -147,7 +148,11 @@ void ShaderTextEditor::_load_theme_settings() {
 	ShaderLanguage::get_keyword_list(&keywords);
 
 	for (List<String>::Element *E = keywords.front(); E; E = E->next()) {
-		get_text_edit()->add_keyword_color(E->get(), keyword_color);
+		if (ShaderLanguage::is_control_flow_keyword(E->get())) {
+			get_text_edit()->add_keyword_color(E->get(), control_flow_keyword_color);
+		} else {
+			get_text_edit()->add_keyword_color(E->get(), keyword_color);
+		}
 	}
 
 	// Colorize built-ins like `COLOR` differently to make them easier
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 9937549076b..e8f98c97b26 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -101,6 +101,7 @@ void TextEditor::_load_theme_settings() {
 	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 control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_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");
@@ -139,6 +140,7 @@ void TextEditor::_load_theme_settings() {
 	colors_cache.font_color = text_color;
 	colors_cache.symbol_color = symbol_color;
 	colors_cache.keyword_color = keyword_color;
+	colors_cache.control_flow_keyword_color = control_flow_keyword_color;
 	colors_cache.basetype_color = basetype_color;
 	colors_cache.type_color = type_color;
 	colors_cache.comment_color = comment_color;
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index f8a51dace9e..3288485fbb6 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -55,6 +55,7 @@ private:
 		Color font_color;
 		Color symbol_color;
 		Color keyword_color;
+		Color control_flow_keyword_color;
 		Color basetype_color;
 		Color type_color;
 		Color comment_color;
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index b5773e34963..9d708e28fd5 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -856,6 +856,7 @@ void VisualShaderEditor::_update_graph() {
 			Color background_color = EDITOR_GET("text_editor/highlighting/background_color");
 			Color text_color = EDITOR_GET("text_editor/highlighting/text_color");
 			Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+			Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
 			Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
 			Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
 
@@ -863,7 +864,11 @@ void VisualShaderEditor::_update_graph() {
 			expression_box->add_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);
+				if (ShaderLanguage::is_control_flow_keyword(E->get())) {
+					expression_box->add_keyword_color(E->get(), control_flow_keyword_color);
+				} else {
+					expression_box->add_keyword_color(E->get(), keyword_color);
+				}
 			}
 
 			expression_box->add_font_override("font", get_font("expression", "EditorFonts"));
@@ -1705,13 +1710,18 @@ void VisualShaderEditor::_notification(int p_what) {
 			Color background_color = EDITOR_GET("text_editor/highlighting/background_color");
 			Color text_color = EDITOR_GET("text_editor/highlighting/text_color");
 			Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+			Color control_flow_keyword_color = EDITOR_GET("text_editor/highlighting/control_flow_keyword_color");
 			Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
 			Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
 
 			preview_text->add_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);
+				if (ShaderLanguage::is_control_flow_keyword(E->get())) {
+					preview_text->add_keyword_color(E->get(), control_flow_keyword_color);
+				} else {
+					preview_text->add_keyword_color(E->get(), keyword_color);
+				}
 			}
 
 			preview_text->add_font_override("font", get_font("expression", "EditorFonts"));
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 136d8f756c5..100a9727e39 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -1072,6 +1072,9 @@ void NativeScriptLanguage::finish() {
 }
 void NativeScriptLanguage::get_reserved_words(List<String> *p_words) const {
 }
+bool NativeScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+	return false;
+}
 void NativeScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
 }
 void NativeScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 701d46106a9..305deedd304 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -311,6 +311,7 @@ public:
 	virtual Error execute_file(const String &p_path);
 	virtual void finish();
 	virtual void get_reserved_words(List<String> *p_words) const;
+	virtual bool is_control_flow_keyword(String p_keyword) const;
 	virtual void get_comment_delimiters(List<String> *p_delimiters) const;
 	virtual void get_string_delimiters(List<String> *p_delimiters) const;
 	virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp
index d7517018f9d..83ca40d36ff 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_language.cpp
@@ -77,6 +77,10 @@ void PluginScriptLanguage::get_reserved_words(List<String> *p_words) const {
 	}
 }
 
+bool PluginScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+	return false;
+}
+
 void PluginScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
 	if (_desc.comment_delimiters) {
 		const char **w = _desc.comment_delimiters;
diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h
index 59c44852144..ac421a48296 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.h
+++ b/modules/gdnative/pluginscript/pluginscript_language.h
@@ -71,6 +71,7 @@ public:
 
 	/* EDITOR FUNCTIONS */
 	virtual void get_reserved_words(List<String> *p_words) const;
+	virtual bool is_control_flow_keyword(String p_keyword) const;
 	virtual void get_comment_delimiters(List<String> *p_delimiters) const;
 	virtual void get_string_delimiters(List<String> *p_delimiters) const;
 	virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 862114e08ec..48bbb6a857c 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1758,6 +1758,19 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
 	}
 }
 
+bool GDScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+	return p_keyword == "break" ||
+		   p_keyword == "continue" ||
+		   p_keyword == "elif" ||
+		   p_keyword == "else" ||
+		   p_keyword == "if" ||
+		   p_keyword == "for" ||
+		   p_keyword == "match" ||
+		   p_keyword == "pass" ||
+		   p_keyword == "return" ||
+		   p_keyword == "while";
+}
+
 bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
 	return p_type == "GDScript";
 }
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 29bb43182e6..d90aea28891 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -453,6 +453,7 @@ public:
 
 	/* EDITOR FUNCTIONS */
 	virtual void get_reserved_words(List<String> *p_words) const;
+	virtual bool is_control_flow_keyword(String p_keywords) const;
 	virtual void get_comment_delimiters(List<String> *p_delimiters) const;
 	virtual void get_string_delimiters(List<String> *p_delimiters) const;
 	virtual String _get_processed_template(const String &p_template, const String &p_base_class_name) const;
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index a80a9a122c1..9ccff8eae79 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -289,6 +289,26 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const {
 	}
 }
 
+bool CSharpLanguage::is_control_flow_keyword(String p_keyword) const {
+	return p_keyword == "break" ||
+		   p_keyword == "case" ||
+		   p_keyword == "catch" ||
+		   p_keyword == "continue" ||
+		   p_keyword == "default" ||
+		   p_keyword == "do" ||
+		   p_keyword == "else" ||
+		   p_keyword == "finally" ||
+		   p_keyword == "for" ||
+		   p_keyword == "foreach" ||
+		   p_keyword == "goto" ||
+		   p_keyword == "if" ||
+		   p_keyword == "return" ||
+		   p_keyword == "switch" ||
+		   p_keyword == "throw" ||
+		   p_keyword == "try" ||
+		   p_keyword == "while";
+}
+
 void CSharpLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
 	p_delimiters->push_back("//"); // single-line comment
 	p_delimiters->push_back("/* */"); // delimited comment
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index d04ffd57c01..6dce21d7c49 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -399,6 +399,7 @@ public:
 
 	/* EDITOR FUNCTIONS */
 	virtual void get_reserved_words(List<String> *p_words) const;
+	virtual bool is_control_flow_keyword(String p_keyword) const;
 	virtual void get_comment_delimiters(List<String> *p_delimiters) const;
 	virtual void get_string_delimiters(List<String> *p_delimiters) const;
 	virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index f697ef507ae..8ba90f073f0 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -2303,6 +2303,9 @@ void VisualScriptLanguage::finish() {
 /* EDITOR FUNCTIONS */
 void VisualScriptLanguage::get_reserved_words(List<String> *p_words) const {
 }
+bool VisualScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+	return false;
+}
 void VisualScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
 }
 void VisualScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index b6170a95bda..8f26ebf79c9 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -556,6 +556,7 @@ public:
 
 	/* EDITOR FUNCTIONS */
 	virtual void get_reserved_words(List<String> *p_words) const;
+	virtual bool is_control_flow_keyword(String p_keyword) const;
 	virtual void get_comment_delimiters(List<String> *p_delimiters) const;
 	virtual void get_string_delimiters(List<String> *p_delimiters) const;
 	virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 2b6bbff1341..dbcf171e477 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -5073,6 +5073,7 @@ void TextEdit::_update_caches() {
 	cache.font_color_selected = get_color("font_color_selected");
 	cache.font_color_readonly = get_color("font_color_readonly");
 	cache.keyword_color = get_color("keyword_color");
+	cache.control_flow_keyword_color = get_color("control_flow_keyword_color");
 	cache.function_color = get_color("function_color");
 	cache.member_variable_color = get_color("member_variable_color");
 	cache.number_color = get_color("number_color");
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 7c648807898..3a35705843b 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -225,6 +225,7 @@ private:
 		Color font_color_selected;
 		Color font_color_readonly;
 		Color keyword_color;
+		Color control_flow_keyword_color;
 		Color number_color;
 		Color function_color;
 		Color member_variable_color;
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
index 9f52222ac55..b1595eeccdb 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -2628,6 +2628,20 @@ void ShaderLanguage::get_keyword_list(List<String> *r_keywords) {
 	}
 }
 
+bool ShaderLanguage::is_control_flow_keyword(String p_keyword) {
+	return p_keyword == "break" ||
+		   p_keyword == "case" ||
+		   p_keyword == "continue" ||
+		   p_keyword == "default" ||
+		   p_keyword == "do" ||
+		   p_keyword == "else" ||
+		   p_keyword == "for" ||
+		   p_keyword == "if" ||
+		   p_keyword == "return" ||
+		   p_keyword == "switch" ||
+		   p_keyword == "while";
+}
+
 void ShaderLanguage::get_builtin_funcs(List<String> *r_keywords) {
 	Set<String> kws;
 
diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h
index 5167a26b0d7..992d0636b86 100644
--- a/servers/visual/shader_language.h
+++ b/servers/visual/shader_language.h
@@ -711,6 +711,7 @@ public:
 	static Variant constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE);
 
 	static void get_keyword_list(List<String> *r_keywords);
+	static bool is_control_flow_keyword(String p_keyword);
 	static void get_builtin_funcs(List<String> *r_keywords);
 
 	struct BuiltInInfo {