Browse Source

Merge pull request #40973 from Paulb23/code_edit

Add CodeEdit and TextEdit gutter system
Rémi Verschelde 5 years ago
parent
commit
beb9ebcda0

+ 95 - 104
editor/code_editor.cpp

@@ -40,7 +40,7 @@
 #include "scene/gui/separator.h"
 #include "scene/resources/dynamic_font.h"
 
-void GotoLineDialog::popup_find_line(TextEdit *p_edit) {
+void GotoLineDialog::popup_find_line(CodeEdit *p_edit) {
 	text_editor = p_edit;
 
 	line->set_text(itos(text_editor->cursor_get_line()));
@@ -113,7 +113,7 @@ void FindReplaceBar::_unhandled_input(const Ref<InputEvent> &p_event) {
 	}
 
 	Control *focus_owner = get_focus_owner();
-	if (text_edit->has_focus() || (focus_owner && vbc_lineedit->is_a_parent_of(focus_owner))) {
+	if (text_editor->has_focus() || (focus_owner && vbc_lineedit->is_a_parent_of(focus_owner))) {
 		bool accepted = true;
 
 		switch (k->get_keycode()) {
@@ -135,20 +135,20 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
 	int line, col;
 	String text = get_search_text();
 
-	bool found = text_edit->search(text, p_flags, p_from_line, p_from_col, line, col);
+	bool found = text_editor->search(text, p_flags, p_from_line, p_from_col, line, col);
 
 	if (found) {
 		if (!preserve_cursor) {
-			text_edit->unfold_line(line);
-			text_edit->cursor_set_line(line, false);
-			text_edit->cursor_set_column(col + text.length(), false);
-			text_edit->center_viewport_to_cursor();
-			text_edit->select(line, col, line, col + text.length());
+			text_editor->unfold_line(line);
+			text_editor->cursor_set_line(line, false);
+			text_editor->cursor_set_column(col + text.length(), false);
+			text_editor->center_viewport_to_cursor();
+			text_editor->select(line, col, line, col + text.length());
 		}
 
-		text_edit->set_search_text(text);
-		text_edit->set_search_flags(p_flags);
-		text_edit->set_current_search_result(line, col);
+		text_editor->set_search_text(text);
+		text_editor->set_search_flags(p_flags);
+		text_editor->set_current_search_result(line, col);
 
 		result_line = line;
 		result_col = col;
@@ -158,9 +158,9 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
 		results_count = 0;
 		result_line = -1;
 		result_col = -1;
-		text_edit->set_search_text("");
-		text_edit->set_search_flags(p_flags);
-		text_edit->set_current_search_result(line, col);
+		text_editor->set_search_text("");
+		text_editor->set_search_flags(p_flags);
+		text_editor->set_current_search_result(line, col);
 	}
 
 	_update_matches_label();
@@ -169,67 +169,67 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
 }
 
 void FindReplaceBar::_replace() {
-	bool selection_enabled = text_edit->is_selection_active();
+	bool selection_enabled = text_editor->is_selection_active();
 	Point2i selection_begin, selection_end;
 	if (selection_enabled) {
-		selection_begin = Point2i(text_edit->get_selection_from_line(), text_edit->get_selection_from_column());
-		selection_end = Point2i(text_edit->get_selection_to_line(), text_edit->get_selection_to_column());
+		selection_begin = Point2i(text_editor->get_selection_from_line(), text_editor->get_selection_from_column());
+		selection_end = Point2i(text_editor->get_selection_to_line(), text_editor->get_selection_to_column());
 	}
 
 	String replace_text = get_replace_text();
 	int search_text_len = get_search_text().length();
 
-	text_edit->begin_complex_operation();
+	text_editor->begin_complex_operation();
 	if (selection_enabled && is_selection_only()) { // To restrict search_current() to selected region
-		text_edit->cursor_set_line(selection_begin.width);
-		text_edit->cursor_set_column(selection_begin.height);
+		text_editor->cursor_set_line(selection_begin.width);
+		text_editor->cursor_set_column(selection_begin.height);
 	}
 
 	if (search_current()) {
-		text_edit->unfold_line(result_line);
-		text_edit->select(result_line, result_col, result_line, result_col + search_text_len);
+		text_editor->unfold_line(result_line);
+		text_editor->select(result_line, result_col, result_line, result_col + search_text_len);
 
 		if (selection_enabled && is_selection_only()) {
 			Point2i match_from(result_line, result_col);
 			Point2i match_to(result_line, result_col + search_text_len);
 			if (!(match_from < selection_begin || match_to > selection_end)) {
-				text_edit->insert_text_at_cursor(replace_text);
+				text_editor->insert_text_at_cursor(replace_text);
 				if (match_to.x == selection_end.x) { // Adjust selection bounds if necessary
 					selection_end.y += replace_text.length() - search_text_len;
 				}
 			}
 		} else {
-			text_edit->insert_text_at_cursor(replace_text);
+			text_editor->insert_text_at_cursor(replace_text);
 		}
 	}
-	text_edit->end_complex_operation();
+	text_editor->end_complex_operation();
 	results_count = -1;
 
 	if (selection_enabled && is_selection_only()) {
 		// Reselect in order to keep 'Replace' restricted to selection
-		text_edit->select(selection_begin.x, selection_begin.y, selection_end.x, selection_end.y);
+		text_editor->select(selection_begin.x, selection_begin.y, selection_end.x, selection_end.y);
 	} else {
-		text_edit->deselect();
+		text_editor->deselect();
 	}
 }
 
 void FindReplaceBar::_replace_all() {
-	text_edit->disconnect("text_changed", callable_mp(this, &FindReplaceBar::_editor_text_changed));
+	text_editor->disconnect("text_changed", callable_mp(this, &FindReplaceBar::_editor_text_changed));
 	// Line as x so it gets priority in comparison, column as y.
-	Point2i orig_cursor(text_edit->cursor_get_line(), text_edit->cursor_get_column());
+	Point2i orig_cursor(text_editor->cursor_get_line(), text_editor->cursor_get_column());
 	Point2i prev_match = Point2(-1, -1);
 
-	bool selection_enabled = text_edit->is_selection_active();
+	bool selection_enabled = text_editor->is_selection_active();
 	Point2i selection_begin, selection_end;
 	if (selection_enabled) {
-		selection_begin = Point2i(text_edit->get_selection_from_line(), text_edit->get_selection_from_column());
-		selection_end = Point2i(text_edit->get_selection_to_line(), text_edit->get_selection_to_column());
+		selection_begin = Point2i(text_editor->get_selection_from_line(), text_editor->get_selection_from_column());
+		selection_end = Point2i(text_editor->get_selection_to_line(), text_editor->get_selection_to_column());
 	}
 
-	int vsval = text_edit->get_v_scroll();
+	int vsval = text_editor->get_v_scroll();
 
-	text_edit->cursor_set_line(0);
-	text_edit->cursor_set_column(0);
+	text_editor->cursor_set_line(0);
+	text_editor->cursor_set_column(0);
 
 	String replace_text = get_replace_text();
 	int search_text_len = get_search_text().length();
@@ -238,11 +238,11 @@ void FindReplaceBar::_replace_all() {
 
 	replace_all_mode = true;
 
-	text_edit->begin_complex_operation();
+	text_editor->begin_complex_operation();
 
 	if (selection_enabled && is_selection_only()) {
-		text_edit->cursor_set_line(selection_begin.width);
-		text_edit->cursor_set_column(selection_begin.height);
+		text_editor->cursor_set_line(selection_begin.width);
+		text_editor->cursor_set_column(selection_begin.height);
 	}
 	if (search_current()) {
 		do {
@@ -256,8 +256,8 @@ void FindReplaceBar::_replace_all() {
 
 			prev_match = Point2i(result_line, result_col + replace_text.length());
 
-			text_edit->unfold_line(result_line);
-			text_edit->select(result_line, result_col, result_line, match_to.y);
+			text_editor->unfold_line(result_line);
+			text_editor->select(result_line, result_col, result_line, match_to.y);
 
 			if (selection_enabled && is_selection_only()) {
 				if (match_from < selection_begin || match_to > selection_end) {
@@ -265,48 +265,48 @@ void FindReplaceBar::_replace_all() {
 				}
 
 				// Replace but adjust selection bounds.
-				text_edit->insert_text_at_cursor(replace_text);
+				text_editor->insert_text_at_cursor(replace_text);
 				if (match_to.x == selection_end.x) {
 					selection_end.y += replace_text.length() - search_text_len;
 				}
 
 			} else {
 				// Just replace.
-				text_edit->insert_text_at_cursor(replace_text);
+				text_editor->insert_text_at_cursor(replace_text);
 			}
 
 			rc++;
 		} while (search_next());
 	}
 
-	text_edit->end_complex_operation();
+	text_editor->end_complex_operation();
 
 	replace_all_mode = false;
 
 	// Restore editor state (selection, cursor, scroll).
-	text_edit->cursor_set_line(orig_cursor.x);
-	text_edit->cursor_set_column(orig_cursor.y);
+	text_editor->cursor_set_line(orig_cursor.x);
+	text_editor->cursor_set_column(orig_cursor.y);
 
 	if (selection_enabled && is_selection_only()) {
 		// Reselect.
-		text_edit->select(selection_begin.x, selection_begin.y, selection_end.x, selection_end.y);
+		text_editor->select(selection_begin.x, selection_begin.y, selection_end.x, selection_end.y);
 	} else {
-		text_edit->deselect();
+		text_editor->deselect();
 	}
 
-	text_edit->set_v_scroll(vsval);
+	text_editor->set_v_scroll(vsval);
 	matches_label->add_theme_color_override("font_color", rc > 0 ? get_theme_color("font_color", "Label") : get_theme_color("error_color", "Editor"));
 	matches_label->set_text(vformat(TTR("%d replaced."), rc));
 
-	text_edit->call_deferred("connect", "text_changed", Callable(this, "_editor_text_changed"));
+	text_editor->call_deferred("connect", "text_changed", Callable(this, "_editor_text_changed"));
 	results_count = -1;
 }
 
 void FindReplaceBar::_get_search_from(int &r_line, int &r_col) {
-	r_line = text_edit->cursor_get_line();
-	r_col = text_edit->cursor_get_column();
+	r_line = text_editor->cursor_get_line();
+	r_col = text_editor->cursor_get_column();
 
-	if (text_edit->is_selection_active() && is_selection_only()) {
+	if (text_editor->is_selection_active() && is_selection_only()) {
 		return;
 	}
 
@@ -327,7 +327,7 @@ void FindReplaceBar::_update_results_count() {
 		return;
 	}
 
-	String full_text = text_edit->get_text();
+	String full_text = text_editor->get_text();
 
 	int from_pos = 0;
 
@@ -399,7 +399,7 @@ bool FindReplaceBar::search_prev() {
 
 	int line, col;
 	_get_search_from(line, col);
-	if (text_edit->is_selection_active()) {
+	if (text_editor->is_selection_active()) {
 		col--; // Skip currently selected word.
 	}
 
@@ -407,9 +407,9 @@ bool FindReplaceBar::search_prev() {
 	if (col < 0) {
 		line -= 1;
 		if (line < 0) {
-			line = text_edit->get_line_count() - 1;
+			line = text_editor->get_line_count() - 1;
 		}
-		col = text_edit->get_line(line).length();
+		col = text_editor->get_line(line).length();
 	}
 
 	return _search(flags, line, col);
@@ -440,9 +440,9 @@ bool FindReplaceBar::search_next() {
 
 	if (line == result_line && col == result_col) {
 		col += text.length();
-		if (col > text_edit->get_line(line).length()) {
+		if (col > text_editor->get_line(line).length()) {
 			line += 1;
-			if (line >= text_edit->get_line_count()) {
+			if (line >= text_editor->get_line_count()) {
 				line = 0;
 			}
 			col = 0;
@@ -454,10 +454,10 @@ bool FindReplaceBar::search_next() {
 
 void FindReplaceBar::_hide_bar() {
 	if (replace_text->has_focus() || search_text->has_focus()) {
-		text_edit->grab_focus();
+		text_editor->grab_focus();
 	}
 
-	text_edit->set_search_text("");
+	text_editor->set_search_text("");
 	result_line = -1;
 	result_col = -1;
 	hide();
@@ -477,8 +477,8 @@ void FindReplaceBar::_show_search(bool p_focus_replace, bool p_show_only) {
 		search_text->call_deferred("grab_focus");
 	}
 
-	if (text_edit->is_selection_active() && !selection_only->is_pressed()) {
-		search_text->set_text(text_edit->get_selection_text());
+	if (text_editor->is_selection_active() && !selection_only->is_pressed()) {
+		search_text->set_text(text_editor->get_selection_text());
 	}
 
 	if (!get_search_text().empty()) {
@@ -511,9 +511,9 @@ void FindReplaceBar::popup_replace() {
 		hbc_option_replace->show();
 	}
 
-	selection_only->set_pressed((text_edit->is_selection_active() && text_edit->get_selection_from_line() < text_edit->get_selection_to_line()));
+	selection_only->set_pressed((text_editor->is_selection_active() && text_editor->get_selection_from_line() < text_editor->get_selection_to_line()));
 
-	_show_search(is_visible() || text_edit->is_selection_active());
+	_show_search(is_visible() || text_editor->is_selection_active());
 }
 
 void FindReplaceBar::_search_options_changed(bool p_pressed) {
@@ -544,7 +544,7 @@ void FindReplaceBar::_search_text_entered(const String &p_text) {
 }
 
 void FindReplaceBar::_replace_text_entered(const String &p_text) {
-	if (selection_only->is_pressed() && text_edit->is_selection_active()) {
+	if (selection_only->is_pressed() && text_editor->is_selection_active()) {
 		_replace_all();
 		_hide_bar();
 	} else if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
@@ -579,10 +579,10 @@ void FindReplaceBar::set_error(const String &p_label) {
 	emit_signal("error", p_label);
 }
 
-void FindReplaceBar::set_text_edit(TextEdit *p_text_edit) {
+void FindReplaceBar::set_text_edit(CodeEdit *p_text_edit) {
 	results_count = -1;
-	text_edit = p_text_edit;
-	text_edit->connect("text_changed", callable_mp(this, &FindReplaceBar::_editor_text_changed));
+	text_editor = p_text_edit;
+	text_editor->connect("text_changed", callable_mp(this, &FindReplaceBar::_editor_text_changed));
 }
 
 void FindReplaceBar::_bind_methods() {
@@ -932,11 +932,9 @@ void CodeTextEditor::update_editor_settings() {
 	text_editor->set_v_scroll_speed(EditorSettings::get_singleton()->get("text_editor/navigation/v_scroll_speed"));
 	text_editor->set_draw_minimap(EditorSettings::get_singleton()->get("text_editor/navigation/show_minimap"));
 	text_editor->set_minimap_width((int)EditorSettings::get_singleton()->get("text_editor/navigation/minimap_width") * EDSCALE);
-	text_editor->set_show_line_numbers(EditorSettings::get_singleton()->get("text_editor/appearance/show_line_numbers"));
+	text_editor->set_draw_line_numbers(EditorSettings::get_singleton()->get("text_editor/appearance/show_line_numbers"));
 	text_editor->set_line_numbers_zero_padded(EditorSettings::get_singleton()->get("text_editor/appearance/line_numbers_zero_padded"));
-	text_editor->set_bookmark_gutter_enabled(EditorSettings::get_singleton()->get("text_editor/appearance/show_bookmark_gutter"));
-	text_editor->set_breakpoint_gutter_enabled(EditorSettings::get_singleton()->get("text_editor/appearance/show_breakpoint_gutter"));
-	text_editor->set_draw_info_gutter(EditorSettings::get_singleton()->get("text_editor/appearance/show_info_gutter"));
+	text_editor->set_draw_bookmarks_gutter(EditorSettings::get_singleton()->get("text_editor/appearance/show_bookmark_gutter"));
 	text_editor->set_hiding_enabled(EditorSettings::get_singleton()->get("text_editor/appearance/code_folding"));
 	text_editor->set_draw_fold_gutter(EditorSettings::get_singleton()->get("text_editor/appearance/code_folding"));
 	text_editor->set_wrap_enabled(EditorSettings::get_singleton()->get("text_editor/appearance/word_wrap"));
@@ -1390,11 +1388,11 @@ void CodeTextEditor::goto_line_centered(int p_line) {
 }
 
 void CodeTextEditor::set_executing_line(int p_line) {
-	text_editor->set_executing_line(p_line);
+	text_editor->set_line_as_executing(p_line, true);
 }
 
 void CodeTextEditor::clear_executing_line() {
-	text_editor->clear_executing_line();
+	text_editor->clear_executing_lines();
 }
 
 Variant CodeTextEditor::get_edit_state() {
@@ -1405,8 +1403,8 @@ Variant CodeTextEditor::get_edit_state() {
 	state["column"] = text_editor->cursor_get_column();
 	state["row"] = text_editor->cursor_get_line();
 
-	state["selection"] = get_text_edit()->is_selection_active();
-	if (get_text_edit()->is_selection_active()) {
+	state["selection"] = get_text_editor()->is_selection_active();
+	if (get_text_editor()->is_selection_active()) {
 		state["selection_from_line"] = text_editor->get_selection_from_line();
 		state["selection_from_column"] = text_editor->get_selection_from_column();
 		state["selection_to_line"] = text_editor->get_selection_to_line();
@@ -1414,8 +1412,8 @@ Variant CodeTextEditor::get_edit_state() {
 	}
 
 	state["folded_lines"] = text_editor->get_folded_lines();
-	state["breakpoints"] = text_editor->get_breakpoints_array();
-	state["bookmarks"] = text_editor->get_bookmarks_array();
+	state["breakpoints"] = text_editor->get_breakpointed_lines();
+	state["bookmarks"] = text_editor->get_bookmarked_lines();
 
 	Ref<EditorSyntaxHighlighter> syntax_highlighter = text_editor->get_syntax_highlighter();
 	state["syntax_highlighter"] = syntax_highlighter->_get_name();
@@ -1453,7 +1451,7 @@ void CodeTextEditor::set_edit_state(const Variant &p_state) {
 	if (state.has("bookmarks")) {
 		Array bookmarks = state["bookmarks"];
 		for (int i = 0; i < bookmarks.size(); i++) {
-			text_editor->set_line_as_bookmark(bookmarks[i], true);
+			text_editor->set_line_as_bookmarked(bookmarks[i], true);
 		}
 	}
 }
@@ -1591,27 +1589,26 @@ void CodeTextEditor::set_warning_nb(int p_warning_nb) {
 
 void CodeTextEditor::toggle_bookmark() {
 	int line = text_editor->cursor_get_line();
-	text_editor->set_line_as_bookmark(line, !text_editor->is_line_set_as_bookmark(line));
+	text_editor->set_line_as_bookmarked(line, !text_editor->is_line_bookmarked(line));
 }
 
 void CodeTextEditor::goto_next_bookmark() {
-	List<int> bmarks;
-	text_editor->get_bookmarks(&bmarks);
+	Array bmarks = text_editor->get_bookmarked_lines();
 	if (bmarks.size() <= 0) {
 		return;
 	}
 
 	int line = text_editor->cursor_get_line();
-	if (line >= bmarks[bmarks.size() - 1]) {
+	if (line >= (int)bmarks[bmarks.size() - 1]) {
 		text_editor->unfold_line(bmarks[0]);
 		text_editor->cursor_set_line(bmarks[0]);
 		text_editor->center_viewport_to_cursor();
 	} else {
-		for (List<int>::Element *E = bmarks.front(); E; E = E->next()) {
-			int bline = E->get();
-			if (bline > line) {
-				text_editor->unfold_line(bline);
-				text_editor->cursor_set_line(bline);
+		for (int i = 0; i < bmarks.size(); i++) {
+			int bmark_line = bmarks[i];
+			if (bmark_line > line) {
+				text_editor->unfold_line(bmark_line);
+				text_editor->cursor_set_line(bmark_line);
 				text_editor->center_viewport_to_cursor();
 				return;
 			}
@@ -1620,23 +1617,22 @@ void CodeTextEditor::goto_next_bookmark() {
 }
 
 void CodeTextEditor::goto_prev_bookmark() {
-	List<int> bmarks;
-	text_editor->get_bookmarks(&bmarks);
+	Array bmarks = text_editor->get_bookmarked_lines();
 	if (bmarks.size() <= 0) {
 		return;
 	}
 
 	int line = text_editor->cursor_get_line();
-	if (line <= bmarks[0]) {
+	if (line <= (int)bmarks[0]) {
 		text_editor->unfold_line(bmarks[bmarks.size() - 1]);
 		text_editor->cursor_set_line(bmarks[bmarks.size() - 1]);
 		text_editor->center_viewport_to_cursor();
 	} else {
-		for (List<int>::Element *E = bmarks.back(); E; E = E->prev()) {
-			int bline = E->get();
-			if (bline < line) {
-				text_editor->unfold_line(bline);
-				text_editor->cursor_set_line(bline);
+		for (int i = bmarks.size(); i >= 0; i--) {
+			int bmark_line = bmarks[i];
+			if (bmark_line < line) {
+				text_editor->unfold_line(bmark_line);
+				text_editor->cursor_set_line(bmark_line);
 				text_editor->center_viewport_to_cursor();
 				return;
 			}
@@ -1645,12 +1641,7 @@ void CodeTextEditor::goto_prev_bookmark() {
 }
 
 void CodeTextEditor::remove_all_bookmarks() {
-	List<int> bmarks;
-	text_editor->get_bookmarks(&bmarks);
-
-	for (List<int>::Element *E = bmarks.front(); E; E = E->next()) {
-		text_editor->set_line_as_bookmark(E->get(), false);
-	}
+	text_editor->clear_bookmarked_lines();
 }
 
 void CodeTextEditor::_bind_methods() {
@@ -1681,7 +1672,7 @@ CodeTextEditor::CodeTextEditor() {
 	ED_SHORTCUT("script_editor/zoom_out", TTR("Zoom Out"), KEY_MASK_CMD | KEY_MINUS);
 	ED_SHORTCUT("script_editor/reset_zoom", TTR("Reset Zoom"), KEY_MASK_CMD | KEY_0);
 
-	text_editor = memnew(TextEdit);
+	text_editor = memnew(CodeEdit);
 	add_child(text_editor);
 	text_editor->set_v_size_flags(SIZE_EXPAND_FILL);
 
@@ -1693,7 +1684,7 @@ CodeTextEditor::CodeTextEditor() {
 
 	find_replace_bar->set_text_edit(text_editor);
 
-	text_editor->set_show_line_numbers(true);
+	text_editor->set_draw_line_numbers(true);
 	text_editor->set_brace_matching(true);
 	text_editor->set_auto_indent(true);
 

+ 8 - 8
editor/code_editor.h

@@ -34,9 +34,9 @@
 #include "editor/editor_plugin.h"
 #include "scene/gui/check_box.h"
 #include "scene/gui/check_button.h"
+#include "scene/gui/code_edit.h"
 #include "scene/gui/dialogs.h"
 #include "scene/gui/line_edit.h"
-#include "scene/gui/text_edit.h"
 #include "scene/main/timer.h"
 
 class GotoLineDialog : public ConfirmationDialog {
@@ -45,15 +45,15 @@ class GotoLineDialog : public ConfirmationDialog {
 	Label *line_label;
 	LineEdit *line;
 
-	TextEdit *text_editor;
+	CodeEdit *text_editor;
 
 	virtual void ok_pressed() override;
 
 public:
-	void popup_find_line(TextEdit *p_edit);
+	void popup_find_line(CodeEdit *p_edit);
 	int get_line() const;
 
-	void set_text_editor(TextEdit *p_text_editor);
+	void set_text_editor(CodeEdit *p_text_editor);
 	GotoLineDialog();
 };
 
@@ -77,7 +77,7 @@ class FindReplaceBar : public HBoxContainer {
 	HBoxContainer *hbc_button_replace;
 	HBoxContainer *hbc_option_replace;
 
-	TextEdit *text_edit;
+	CodeEdit *text_editor;
 
 	int result_line;
 	int result_col;
@@ -120,7 +120,7 @@ public:
 	bool is_selection_only() const;
 	void set_error(const String &p_label);
 
-	void set_text_edit(TextEdit *p_text_edit);
+	void set_text_edit(CodeEdit *p_text_edit);
 
 	void popup_search(bool p_show_only = false);
 	void popup_replace();
@@ -137,7 +137,7 @@ typedef void (*CodeTextEditorCodeCompleteFunc)(void *p_ud, const String &p_code,
 class CodeTextEditor : public VBoxContainer {
 	GDCLASS(CodeTextEditor, VBoxContainer);
 
-	TextEdit *text_editor;
+	CodeEdit *text_editor;
 	FindReplaceBar *find_replace_bar;
 	HBoxContainer *status_bar;
 
@@ -240,7 +240,7 @@ public:
 	void set_error(const String &p_error);
 	void set_error_pos(int p_line, int p_column);
 	void update_line_and_column() { _line_col_changed(); }
-	TextEdit *get_text_edit() { return text_editor; }
+	CodeEdit *get_text_editor() { return text_editor; }
 	FindReplaceBar *get_find_replace_bar() { return find_replace_bar; }
 	virtual void apply_code() {}
 	void goto_error();

+ 2 - 3
editor/editor_settings.cpp

@@ -445,7 +445,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 	_initial_set("text_editor/appearance/show_line_numbers", true);
 	_initial_set("text_editor/appearance/line_numbers_zero_padded", false);
 	_initial_set("text_editor/appearance/show_bookmark_gutter", true);
-	_initial_set("text_editor/appearance/show_breakpoint_gutter", true);
 	_initial_set("text_editor/appearance/show_info_gutter", true);
 	_initial_set("text_editor/appearance/code_folding", true);
 	_initial_set("text_editor/appearance/word_wrap", false);
@@ -707,8 +706,8 @@ void EditorSettings::_load_default_text_editor_theme() {
 	_initial_set("text_editor/highlighting/member_variable_color", Color(0.9, 0.31, 0.35));
 	_initial_set("text_editor/highlighting/mark_color", Color(1.0, 0.4, 0.4, 0.4));
 	_initial_set("text_editor/highlighting/bookmark_color", Color(0.08, 0.49, 0.98));
-	_initial_set("text_editor/highlighting/breakpoint_color", Color(0.8, 0.8, 0.4, 0.2));
-	_initial_set("text_editor/highlighting/executing_line_color", Color(0.2, 0.8, 0.2, 0.4));
+	_initial_set("text_editor/highlighting/breakpoint_color", Color(0.9, 0.29, 0.3));
+	_initial_set("text_editor/highlighting/executing_line_color", Color(0.98, 0.89, 0.27));
 	_initial_set("text_editor/highlighting/code_folding_color", Color(0.8, 0.8, 0.8, 0.8));
 	_initial_set("text_editor/highlighting/search_result_color", Color(0.05, 0.25, 0.05, 1));
 	_initial_set("text_editor/highlighting/search_result_border_color", Color(0.41, 0.61, 0.91, 0.38));

+ 15 - 3
editor/editor_themes.cpp

@@ -868,12 +868,24 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
 	theme->set_constant("side_margin", "TabContainer", 0);
 	theme->set_icon("tab", "TextEdit", theme->get_icon("GuiTab", "EditorIcons"));
 	theme->set_icon("space", "TextEdit", theme->get_icon("GuiSpace", "EditorIcons"));
-	theme->set_icon("folded", "TextEdit", theme->get_icon("GuiTreeArrowRight", "EditorIcons"));
-	theme->set_icon("fold", "TextEdit", theme->get_icon("GuiTreeArrowDown", "EditorIcons"));
 	theme->set_color("font_color", "TextEdit", font_color);
 	theme->set_color("caret_color", "TextEdit", font_color);
 	theme->set_color("selection_color", "TextEdit", font_color_selection);
 
+	// CodeEdit
+	theme->set_stylebox("normal", "CodeEdit", style_widget);
+	theme->set_stylebox("focus", "CodeEdit", style_widget_hover);
+	theme->set_stylebox("read_only", "CodeEdit", style_widget_disabled);
+	theme->set_constant("side_margin", "TabContainer", 0);
+	theme->set_icon("tab", "CodeEdit", theme->get_icon("GuiTab", "EditorIcons"));
+	theme->set_icon("space", "CodeEdit", theme->get_icon("GuiSpace", "EditorIcons"));
+	theme->set_icon("folded", "CodeEdit", theme->get_icon("GuiTreeArrowRight", "EditorIcons"));
+	theme->set_icon("can_fold", "CodeEdit", theme->get_icon("GuiTreeArrowDown", "EditorIcons"));
+	theme->set_icon("executing_line", "CodeEdit", theme->get_icon("MainPlay", "EditorIcons"));
+	theme->set_color("font_color", "CodeEdit", font_color);
+	theme->set_color("caret_color", "CodeEdit", font_color);
+	theme->set_color("selection_color", "CodeEdit", font_color_selection);
+
 	// H/VSplitContainer
 	theme->set_stylebox("bg", "VSplitContainer", make_stylebox(theme->get_icon("GuiVsplitBg", "EditorIcons"), 1, 1, 1, 1));
 	theme->set_stylebox("bg", "HSplitContainer", make_stylebox(theme->get_icon("GuiHsplitBg", "EditorIcons"), 1, 1, 1, 1));
@@ -1179,7 +1191,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
 	const Color mark_color = Color(error_color.r, error_color.g, error_color.b, 0.3);
 	const Color bookmark_color = Color(0.08, 0.49, 0.98);
 	const Color breakpoint_color = error_color;
-	const Color executing_line_color = Color(0.2, 0.8, 0.2, 0.4);
+	const Color executing_line_color = Color(0.98, 0.89, 0.27);
 	const Color code_folding_color = alpha3;
 	const Color search_result_color = alpha1;
 	const Color search_result_border_color = Color(0.41, 0.61, 0.91, 0.38);

+ 3 - 4
editor/plugins/script_editor_plugin.cpp

@@ -1585,15 +1585,14 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
 			continue;
 		}
 
-		List<int> bpoints;
-		se->get_breakpoints(&bpoints);
 		String base = script->get_path();
 		if (base.begins_with("local://") || base == "") {
 			continue;
 		}
 
-		for (List<int>::Element *E = bpoints.front(); E; E = E->next()) {
-			p_breakpoints->push_back(base + ":" + itos(E->get() + 1));
+		Array bpoints = se->get_breakpoints();
+		for (int j = 0; j < bpoints.size(); j++) {
+			p_breakpoints->push_back(base + ":" + itos((int)bpoints[j] + 1));
 		}
 	}
 }

+ 1 - 1
editor/plugins/script_editor_plugin.h

@@ -151,7 +151,7 @@ public:
 	virtual void ensure_focus() = 0;
 	virtual void tag_saved_version() = 0;
 	virtual void reload(bool p_soft) {}
-	virtual void get_breakpoints(List<int> *p_breakpoints) = 0;
+	virtual Array get_breakpoints() = 0;
 	virtual void add_callback(const String &p_function, PackedStringArray p_args) = 0;
 	virtual void update_settings() = 0;
 	virtual void set_debugger_active(bool p_active) = 0;

+ 152 - 91
editor/plugins/script_text_editor.cpp

@@ -111,7 +111,7 @@ ConnectionInfoDialog::ConnectionInfoDialog() {
 Vector<String> ScriptTextEditor::get_functions() {
 	String errortxt;
 	int line = -1, col;
-	TextEdit *te = code_editor->get_text_edit();
+	CodeEdit *te = code_editor->get_text_editor();
 	String text = te->get_text();
 	List<String> fnc;
 
@@ -130,9 +130,9 @@ void ScriptTextEditor::apply_code() {
 	if (script.is_null()) {
 		return;
 	}
-	script->set_source_code(code_editor->get_text_edit()->get_text());
+	script->set_source_code(code_editor->get_text_editor()->get_text());
 	script->update_exports();
-	code_editor->get_text_edit()->get_syntax_highlighter()->update_cache();
+	code_editor->get_text_editor()->get_syntax_highlighter()->update_cache();
 }
 
 RES ScriptTextEditor::get_edited_resource() const {
@@ -145,9 +145,9 @@ void ScriptTextEditor::set_edited_resource(const RES &p_res) {
 
 	script = p_res;
 
-	code_editor->get_text_edit()->set_text(script->get_source_code());
-	code_editor->get_text_edit()->clear_undo_history();
-	code_editor->get_text_edit()->tag_saved_version();
+	code_editor->get_text_editor()->set_text(script->get_source_code());
+	code_editor->get_text_editor()->clear_undo_history();
+	code_editor->get_text_editor()->tag_saved_version();
 
 	emit_signal("name_changed");
 	code_editor->update_line_and_column();
@@ -167,9 +167,19 @@ void ScriptTextEditor::enable_editor() {
 }
 
 void ScriptTextEditor::_load_theme_settings() {
-	TextEdit *text_edit = code_editor->get_text_edit();
+	CodeEdit *text_edit = code_editor->get_text_editor();
 	text_edit->clear_keywords();
 
+	Color updated_safe_line_number_color = EDITOR_GET("text_editor/highlighting/safe_line_number_color");
+	if (updated_safe_line_number_color != safe_line_number_color) {
+		safe_line_number_color = updated_safe_line_number_color;
+		for (int i = 0; i < text_edit->get_line_count(); i++) {
+			if (text_edit->get_line_gutter_item_color(i, line_number_gutter) != default_line_number_color) {
+				text_edit->set_line_gutter_item_color(i, line_number_gutter, safe_line_number_color);
+			}
+		}
+	}
+
 	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");
@@ -178,7 +188,6 @@ void ScriptTextEditor::_load_theme_settings() {
 	Color completion_font_color = EDITOR_GET("text_editor/highlighting/completion_font_color");
 	Color text_color = EDITOR_GET("text_editor/highlighting/text_color");
 	Color line_number_color = EDITOR_GET("text_editor/highlighting/line_number_color");
-	Color safe_line_number_color = EDITOR_GET("text_editor/highlighting/safe_line_number_color");
 	Color caret_color = EDITOR_GET("text_editor/highlighting/caret_color");
 	Color caret_background_color = EDITOR_GET("text_editor/highlighting/caret_background_color");
 	Color text_selected_color = EDITOR_GET("text_editor/highlighting/text_selected_color");
@@ -203,7 +212,6 @@ void ScriptTextEditor::_load_theme_settings() {
 	text_edit->add_theme_color_override("completion_font_color", completion_font_color);
 	text_edit->add_theme_color_override("font_color", text_color);
 	text_edit->add_theme_color_override("line_number_color", line_number_color);
-	text_edit->add_theme_color_override("safe_line_number_color", safe_line_number_color);
 	text_edit->add_theme_color_override("caret_color", caret_color);
 	text_edit->add_theme_color_override("caret_background_color", caret_background_color);
 	text_edit->add_theme_color_override("font_color_selected", text_selected_color);
@@ -233,7 +241,7 @@ void ScriptTextEditor::_set_theme_for_script() {
 		return;
 	}
 
-	TextEdit *text_edit = code_editor->get_text_edit();
+	CodeEdit *text_edit = code_editor->get_text_editor();
 	text_edit->get_syntax_highlighter()->update_cache();
 
 	/* add keywords for auto completion */
@@ -284,10 +292,10 @@ void ScriptTextEditor::_show_warnings_panel(bool p_show) {
 
 void ScriptTextEditor::_warning_clicked(Variant p_line) {
 	if (p_line.get_type() == Variant::INT) {
-		code_editor->get_text_edit()->cursor_set_line(p_line.operator int64_t());
+		code_editor->get_text_editor()->cursor_set_line(p_line.operator int64_t());
 	} else if (p_line.get_type() == Variant::DICTIONARY) {
 		Dictionary meta = p_line.operator Dictionary();
-		code_editor->get_text_edit()->insert_at("# warning-ignore:" + meta["code"].operator String(), meta["line"].operator int64_t() - 1);
+		code_editor->get_text_editor()->insert_at("# warning-ignore:" + meta["code"].operator String(), meta["line"].operator int64_t() - 1);
 		_validate_script();
 	}
 }
@@ -295,7 +303,7 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) {
 void ScriptTextEditor::reload_text() {
 	ERR_FAIL_COND(script.is_null());
 
-	TextEdit *te = code_editor->get_text_edit();
+	CodeEdit *te = code_editor->get_text_editor();
 	int column = te->cursor_get_column();
 	int row = te->cursor_get_line();
 	int h = te->get_h_scroll();
@@ -313,20 +321,20 @@ void ScriptTextEditor::reload_text() {
 }
 
 void ScriptTextEditor::add_callback(const String &p_function, PackedStringArray p_args) {
-	String code = code_editor->get_text_edit()->get_text();
+	String code = code_editor->get_text_editor()->get_text();
 	int pos = script->get_language()->find_function(p_function, code);
 	if (pos == -1) {
 		//does not exist
-		code_editor->get_text_edit()->deselect();
-		pos = code_editor->get_text_edit()->get_line_count() + 2;
+		code_editor->get_text_editor()->deselect();
+		pos = code_editor->get_text_editor()->get_line_count() + 2;
 		String func = script->get_language()->make_function("", p_function, p_args);
 		//code=code+func;
-		code_editor->get_text_edit()->cursor_set_line(pos + 1);
-		code_editor->get_text_edit()->cursor_set_column(1000000); //none shall be that big
-		code_editor->get_text_edit()->insert_text_at_cursor("\n\n" + func);
+		code_editor->get_text_editor()->cursor_set_line(pos + 1);
+		code_editor->get_text_editor()->cursor_set_column(1000000); //none shall be that big
+		code_editor->get_text_editor()->insert_text_at_cursor("\n\n" + func);
 	}
-	code_editor->get_text_edit()->cursor_set_line(pos);
-	code_editor->get_text_edit()->cursor_set_column(1);
+	code_editor->get_text_editor()->cursor_set_line(pos);
+	code_editor->get_text_editor()->cursor_set_column(1);
 }
 
 bool ScriptTextEditor::show_members_overview() {
@@ -334,12 +342,13 @@ bool ScriptTextEditor::show_members_overview() {
 }
 
 void ScriptTextEditor::update_settings() {
+	code_editor->get_text_editor()->set_gutter_draw(connection_gutter, EditorSettings::get_singleton()->get("text_editor/appearance/show_info_gutter"));
 	code_editor->update_editor_settings();
 }
 
 bool ScriptTextEditor::is_unsaved() {
 	const bool unsaved =
-			code_editor->get_text_edit()->get_version() != code_editor->get_text_edit()->get_saved_version() ||
+			code_editor->get_text_editor()->get_version() != code_editor->get_text_editor()->get_saved_version() ||
 			script->get_path().empty(); // In memory.
 	return unsaved;
 }
@@ -385,7 +394,7 @@ void ScriptTextEditor::convert_indent_to_tabs() {
 }
 
 void ScriptTextEditor::tag_saved_version() {
-	code_editor->get_text_edit()->tag_saved_version();
+	code_editor->get_text_editor()->tag_saved_version();
 }
 
 void ScriptTextEditor::goto_line(int p_line, bool p_with_error) {
@@ -409,7 +418,7 @@ void ScriptTextEditor::clear_executing_line() {
 }
 
 void ScriptTextEditor::ensure_focus() {
-	code_editor->get_text_edit()->grab_focus();
+	code_editor->get_text_editor()->grab_focus();
 }
 
 String ScriptTextEditor::get_name() {
@@ -443,7 +452,7 @@ Ref<Texture2D> ScriptTextEditor::get_theme_icon() {
 void ScriptTextEditor::_validate_script() {
 	String errortxt;
 	int line = -1, col;
-	TextEdit *te = code_editor->get_text_edit();
+	CodeEdit *te = code_editor->get_text_editor();
 
 	String text = te->get_text();
 	List<String> fnc;
@@ -540,16 +549,16 @@ void ScriptTextEditor::_validate_script() {
 		te->set_line_as_marked(i, line == i);
 		if (highlight_safe) {
 			if (safe_lines.has(i + 1)) {
-				te->set_line_as_safe(i, true);
+				te->set_line_gutter_item_color(i, line_number_gutter, safe_line_number_color);
 				last_is_safe = true;
 			} else if (last_is_safe && (te->is_line_comment(i) || te->get_line(i).strip_edges().empty())) {
-				te->set_line_as_safe(i, true);
+				te->set_line_gutter_item_color(i, line_number_gutter, safe_line_number_color);
 			} else {
-				te->set_line_as_safe(i, false);
+				te->set_line_gutter_item_color(i, line_number_gutter, default_line_number_color);
 				last_is_safe = false;
 			}
 		} else {
-			te->set_line_as_safe(i, false);
+			te->set_line_gutter_item_color(line, 1, default_line_number_color);
 		}
 	}
 
@@ -566,7 +575,7 @@ void ScriptTextEditor::_update_bookmark_list() {
 	bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT);
 	bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV);
 
-	Array bookmark_list = code_editor->get_text_edit()->get_bookmarks_array();
+	Array bookmark_list = code_editor->get_text_editor()->get_bookmarked_lines();
 	if (bookmark_list.size() == 0) {
 		return;
 	}
@@ -576,7 +585,7 @@ void ScriptTextEditor::_update_bookmark_list() {
 	for (int i = 0; i < bookmark_list.size(); i++) {
 		// Strip edges to remove spaces or tabs.
 		// Also replace any tabs by spaces, since we can't print tabs in the menu.
-		String line = code_editor->get_text_edit()->get_line(bookmark_list[i]).replace("\t", "  ").strip_edges();
+		String line = code_editor->get_text_editor()->get_line(bookmark_list[i]).replace("\t", "  ").strip_edges();
 
 		// Limit the size of the line if too big.
 		if (line.length() > 50) {
@@ -593,7 +602,7 @@ void ScriptTextEditor::_bookmark_item_pressed(int p_idx) {
 		_edit_option(bookmarks_menu->get_item_id(p_idx));
 	} else {
 		code_editor->goto_line(bookmarks_menu->get_item_metadata(p_idx));
-		code_editor->get_text_edit()->call_deferred("center_viewport_to_cursor"); //Need to be deferred, because goto uses call_deferred().
+		code_editor->get_text_editor()->call_deferred("center_viewport_to_cursor"); //Need to be deferred, because goto uses call_deferred().
 	}
 }
 
@@ -704,7 +713,7 @@ void ScriptTextEditor::_code_complete_script(const String &p_code, List<ScriptCo
 	String hint;
 	Error err = script->get_language()->complete_code(p_code, script->get_path(), base, r_options, r_force, hint);
 	if (err == OK) {
-		code_editor->get_text_edit()->set_code_hint(hint);
+		code_editor->get_text_editor()->set_code_hint(hint);
 	}
 }
 
@@ -717,7 +726,7 @@ void ScriptTextEditor::_update_breakpoint_list() {
 	breakpoints_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_breakpoint"), DEBUG_GOTO_NEXT_BREAKPOINT);
 	breakpoints_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_breakpoint"), DEBUG_GOTO_PREV_BREAKPOINT);
 
-	Array breakpoint_list = code_editor->get_text_edit()->get_breakpoints_array();
+	Array breakpoint_list = code_editor->get_text_editor()->get_breakpointed_lines();
 	if (breakpoint_list.size() == 0) {
 		return;
 	}
@@ -727,7 +736,7 @@ void ScriptTextEditor::_update_breakpoint_list() {
 	for (int i = 0; i < breakpoint_list.size(); i++) {
 		// Strip edges to remove spaces or tabs.
 		// Also replace any tabs by spaces, since we can't print tabs in the menu.
-		String line = code_editor->get_text_edit()->get_line(breakpoint_list[i]).replace("\t", "  ").strip_edges();
+		String line = code_editor->get_text_editor()->get_line(breakpoint_list[i]).replace("\t", "  ").strip_edges();
 
 		// Limit the size of the line if too big.
 		if (line.length() > 50) {
@@ -744,12 +753,12 @@ void ScriptTextEditor::_breakpoint_item_pressed(int p_idx) {
 		_edit_option(breakpoints_menu->get_item_id(p_idx));
 	} else {
 		code_editor->goto_line(breakpoints_menu->get_item_metadata(p_idx));
-		code_editor->get_text_edit()->call_deferred("center_viewport_to_cursor"); //Need to be deferred, because goto uses call_deferred().
+		code_editor->get_text_editor()->call_deferred("center_viewport_to_cursor"); //Need to be deferred, because goto uses call_deferred().
 	}
 }
 
 void ScriptTextEditor::_breakpoint_toggled(int p_row) {
-	EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), p_row + 1, code_editor->get_text_edit()->is_line_set_as_breakpoint(p_row));
+	EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), p_row + 1, code_editor->get_text_editor()->is_line_breakpointed(p_row));
 }
 
 void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_column) {
@@ -771,7 +780,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
 			EditorNode::get_singleton()->load_resource(p_symbol);
 		}
 
-	} else if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), p_symbol, script->get_path(), base, result) == OK) {
+	} else if (script->get_language()->lookup_code(code_editor->get_text_editor()->get_text_for_lookup_completion(), p_symbol, script->get_path(), base, result) == OK) {
 		_goto_line(p_row);
 
 		result.class_name = result.class_name.trim_prefix("_");
@@ -866,7 +875,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
 }
 
 void ScriptTextEditor::_validate_symbol(const String &p_symbol) {
-	TextEdit *text_edit = code_editor->get_text_edit();
+	CodeEdit *text_edit = code_editor->get_text_editor();
 
 	Node *base = get_tree()->get_edited_scene_root();
 	if (base) {
@@ -874,7 +883,7 @@ void ScriptTextEditor::_validate_symbol(const String &p_symbol) {
 	}
 
 	ScriptLanguage::LookupResult result;
-	if (ScriptServer::is_global_class(p_symbol) || p_symbol.is_resource_file() || script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), p_symbol, script->get_path(), base, result) == OK || (ProjectSettings::get_singleton()->has_autoload(p_symbol) && ProjectSettings::get_singleton()->get_autoload(p_symbol).is_singleton)) {
+	if (ScriptServer::is_global_class(p_symbol) || p_symbol.is_resource_file() || script->get_language()->lookup_code(code_editor->get_text_editor()->get_text_for_lookup_completion(), p_symbol, script->get_path(), base, result) == OK || (ProjectSettings::get_singleton()->has_autoload(p_symbol) && ProjectSettings::get_singleton()->get_autoload(p_symbol).is_singleton)) {
 		text_edit->set_highlighted_word(p_symbol);
 	} else if (p_symbol.is_rel_path()) {
 		String path = _get_absolute_path(p_symbol);
@@ -902,8 +911,15 @@ void ScriptTextEditor::update_toggle_scripts_button() {
 }
 
 void ScriptTextEditor::_update_connected_methods() {
-	TextEdit *text_edit = code_editor->get_text_edit();
-	text_edit->clear_info_icons();
+	CodeEdit *text_edit = code_editor->get_text_editor();
+	for (int i = 0; i < text_edit->get_line_count(); i++) {
+		if (text_edit->get_line_gutter_metadata(i, connection_gutter) == "") {
+			continue;
+		}
+		text_edit->set_line_gutter_metadata(i, connection_gutter, "");
+		text_edit->set_line_gutter_icon(i, connection_gutter, nullptr);
+		text_edit->set_line_gutter_clickable(i, connection_gutter, false);
+	}
 	missing_connections.clear();
 
 	if (!script_is_valid) {
@@ -943,8 +959,10 @@ void ScriptTextEditor::_update_connected_methods() {
 				for (int j = 0; j < functions.size(); j++) {
 					String name = functions[j].get_slice(":", 0);
 					if (name == connection.callable.get_method()) {
-						line = functions[j].get_slice(":", 1).to_int();
-						text_edit->set_line_info_icon(line - 1, get_parent_control()->get_theme_icon("Slot", "EditorIcons"), connection.callable.get_method());
+						line = functions[j].get_slice(":", 1).to_int() - 1;
+						text_edit->set_line_gutter_metadata(line, connection_gutter, connection.callable.get_method());
+						text_edit->set_line_gutter_icon(line, connection_gutter, get_parent_control()->get_theme_icon("Slot", "EditorIcons"));
+						text_edit->set_line_gutter_clickable(line, connection_gutter, true);
 						methods_found.insert(connection.callable.get_method());
 						break;
 					}
@@ -974,18 +992,41 @@ void ScriptTextEditor::_update_connected_methods() {
 	}
 }
 
-void ScriptTextEditor::_lookup_connections(int p_row, String p_method) {
+void ScriptTextEditor::_update_gutter_indexes() {
+	for (int i = 0; i < code_editor->get_text_editor()->get_gutter_count(); i++) {
+		if (code_editor->get_text_editor()->get_gutter_name(i) == "connection_gutter") {
+			connection_gutter = i;
+			continue;
+		}
+
+		if (code_editor->get_text_editor()->get_gutter_name(i) == "line_numbers") {
+			line_number_gutter = i;
+			continue;
+		}
+	}
+}
+
+void ScriptTextEditor::_gutter_clicked(int p_line, int p_gutter) {
+	if (p_gutter != connection_gutter) {
+		return;
+	}
+
+	String method = code_editor->get_text_editor()->get_line_gutter_metadata(p_line, p_gutter);
+	if (method == "") {
+		return;
+	}
+
 	Node *base = get_tree()->get_edited_scene_root();
 	if (!base) {
 		return;
 	}
 
 	Vector<Node *> nodes = _find_all_node_for_script(base, base, script);
-	connection_info_dialog->popup_connections(p_method, nodes);
+	connection_info_dialog->popup_connections(method, nodes);
 }
 
 void ScriptTextEditor::_edit_option(int p_op) {
-	TextEdit *tx = code_editor->get_text_edit();
+	CodeEdit *tx = code_editor->get_text_editor();
 
 	switch (p_op) {
 		case EDIT_UNDO: {
@@ -1109,7 +1150,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
 		} break;
 		case EDIT_EVALUATE: {
 			Expression expression;
-			Vector<String> lines = code_editor->get_text_edit()->get_selection_text().split("\n");
+			Vector<String> lines = code_editor->get_text_editor()->get_selection_text().split("\n");
 			PackedStringArray results;
 
 			for (int i = 0; i < lines.size(); i++) {
@@ -1128,9 +1169,9 @@ void ScriptTextEditor::_edit_option(int p_op) {
 				}
 			}
 
-			code_editor->get_text_edit()->begin_complex_operation(); //prevents creating a two-step undo
-			code_editor->get_text_edit()->insert_text_at_cursor(String("\n").join(results));
-			code_editor->get_text_edit()->end_complex_operation();
+			code_editor->get_text_editor()->begin_complex_operation(); //prevents creating a two-step undo
+			code_editor->get_text_editor()->insert_text_at_cursor(String("\n").join(results));
+			code_editor->get_text_editor()->end_complex_operation();
 		} break;
 		case SEARCH_FIND: {
 			code_editor->get_find_replace_bar()->popup_search();
@@ -1145,14 +1186,14 @@ void ScriptTextEditor::_edit_option(int p_op) {
 			code_editor->get_find_replace_bar()->popup_replace();
 		} break;
 		case SEARCH_IN_FILES: {
-			String selected_text = code_editor->get_text_edit()->get_selection_text();
+			String selected_text = code_editor->get_text_editor()->get_selection_text();
 
 			// Yep, because it doesn't make sense to instance this dialog for every single script open...
 			// So this will be delegated to the ScriptEditor.
 			emit_signal("search_in_files_requested", selected_text);
 		} break;
 		case REPLACE_IN_FILES: {
-			String selected_text = code_editor->get_text_edit()->get_selection_text();
+			String selected_text = code_editor->get_text_editor()->get_selection_text();
 
 			emit_signal("replace_in_files_requested", selected_text);
 		} break;
@@ -1177,24 +1218,22 @@ void ScriptTextEditor::_edit_option(int p_op) {
 		} break;
 		case DEBUG_TOGGLE_BREAKPOINT: {
 			int line = tx->cursor_get_line();
-			bool dobreak = !tx->is_line_set_as_breakpoint(line);
+			bool dobreak = !tx->is_line_breakpointed(line);
 			tx->set_line_as_breakpoint(line, dobreak);
 			EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), line + 1, dobreak);
 		} break;
 		case DEBUG_REMOVE_ALL_BREAKPOINTS: {
-			List<int> bpoints;
-			tx->get_breakpoints(&bpoints);
+			Array bpoints = tx->get_breakpointed_lines();
 
-			for (List<int>::Element *E = bpoints.front(); E; E = E->next()) {
-				int line = E->get();
-				bool dobreak = !tx->is_line_set_as_breakpoint(line);
+			for (int i = 0; i < bpoints.size(); i++) {
+				int line = bpoints[i];
+				bool dobreak = !tx->is_line_breakpointed(line);
 				tx->set_line_as_breakpoint(line, dobreak);
 				EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), line + 1, dobreak);
 			}
 		} break;
 		case DEBUG_GOTO_NEXT_BREAKPOINT: {
-			List<int> bpoints;
-			tx->get_breakpoints(&bpoints);
+			Array bpoints = tx->get_breakpointed_lines();
 			if (bpoints.size() <= 0) {
 				return;
 			}
@@ -1202,13 +1241,13 @@ void ScriptTextEditor::_edit_option(int p_op) {
 			int line = tx->cursor_get_line();
 
 			// wrap around
-			if (line >= bpoints[bpoints.size() - 1]) {
+			if (line >= (int)bpoints[bpoints.size() - 1]) {
 				tx->unfold_line(bpoints[0]);
 				tx->cursor_set_line(bpoints[0]);
 				tx->center_viewport_to_cursor();
 			} else {
-				for (List<int>::Element *E = bpoints.front(); E; E = E->next()) {
-					int bline = E->get();
+				for (int i = 0; i < bpoints.size(); i++) {
+					int bline = bpoints[i];
 					if (bline > line) {
 						tx->unfold_line(bline);
 						tx->cursor_set_line(bline);
@@ -1220,21 +1259,20 @@ void ScriptTextEditor::_edit_option(int p_op) {
 
 		} break;
 		case DEBUG_GOTO_PREV_BREAKPOINT: {
-			List<int> bpoints;
-			tx->get_breakpoints(&bpoints);
+			Array bpoints = tx->get_breakpointed_lines();
 			if (bpoints.size() <= 0) {
 				return;
 			}
 
 			int line = tx->cursor_get_line();
 			// wrap around
-			if (line <= bpoints[0]) {
+			if (line <= (int)bpoints[0]) {
 				tx->unfold_line(bpoints[bpoints.size() - 1]);
 				tx->cursor_set_line(bpoints[bpoints.size() - 1]);
 				tx->center_viewport_to_cursor();
 			} else {
-				for (List<int>::Element *E = bpoints.back(); E; E = E->prev()) {
-					int bline = E->get();
+				for (int i = bpoints.size(); i >= 0; i--) {
+					int bline = bpoints[i];
 					if (bline < line) {
 						tx->unfold_line(bline);
 						tx->cursor_set_line(bline);
@@ -1303,7 +1341,7 @@ void ScriptTextEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_hig
 		el = el->next();
 	}
 
-	TextEdit *te = code_editor->get_text_edit();
+	CodeEdit *te = code_editor->get_text_editor();
 	p_highlighter->_set_edited_resource(script);
 	te->set_syntax_highlighter(p_highlighter);
 }
@@ -1312,6 +1350,16 @@ void ScriptTextEditor::_change_syntax_highlighter(int p_idx) {
 	set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]);
 }
 
+void ScriptTextEditor::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_THEME_CHANGED: {
+			code_editor->get_text_editor()->set_gutter_width(connection_gutter, code_editor->get_text_editor()->get_row_height());
+		} break;
+		default:
+			break;
+	}
+}
+
 void ScriptTextEditor::_bind_methods() {
 	ClassDB::bind_method("_update_connected_methods", &ScriptTextEditor::_update_connected_methods);
 
@@ -1331,7 +1379,7 @@ void ScriptTextEditor::clear_edit_menu() {
 }
 
 void ScriptTextEditor::reload(bool p_soft) {
-	TextEdit *te = code_editor->get_text_edit();
+	CodeEdit *te = code_editor->get_text_editor();
 	Ref<Script> scr = script;
 	if (scr.is_null()) {
 		return;
@@ -1342,12 +1390,12 @@ void ScriptTextEditor::reload(bool p_soft) {
 	scr->get_language()->reload_tool_script(scr, soft);
 }
 
-void ScriptTextEditor::get_breakpoints(List<int> *p_breakpoints) {
-	code_editor->get_text_edit()->get_breakpoints(p_breakpoints);
+Array ScriptTextEditor::get_breakpoints() {
+	return code_editor->get_text_editor()->get_breakpointed_lines();
 }
 
 void ScriptTextEditor::set_tooltip_request_func(String p_method, Object *p_obj) {
-	code_editor->get_text_edit()->set_tooltip_request_func(p_obj, p_method, this);
+	code_editor->get_text_editor()->set_tooltip_request_func(p_obj, p_method, this);
 }
 
 void ScriptTextEditor::set_debugger_active(bool p_active) {
@@ -1393,7 +1441,7 @@ static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const
 void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
 	Dictionary d = p_data;
 
-	TextEdit *te = code_editor->get_text_edit();
+	CodeEdit *te = code_editor->get_text_editor();
 	int row, col;
 	te->_get_mouse_pos(p_point, row, col);
 
@@ -1466,7 +1514,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
 	Point2 local_pos;
 	bool create_menu = false;
 
-	TextEdit *tx = code_editor->get_text_edit();
+	CodeEdit *tx = code_editor->get_text_editor();
 	if (mb.is_valid() && mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) {
 		local_pos = mb->get_global_position() - tx->get_global_position();
 		create_menu = true;
@@ -1519,7 +1567,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
 				base = _find_node_for_script(base, base, script);
 			}
 			ScriptLanguage::LookupResult result;
-			if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), word_at_pos, script->get_path(), base, result) == OK) {
+			if (script->get_language()->lookup_code(code_editor->get_text_editor()->get_text_for_lookup_completion(), word_at_pos, script->get_path(), base, result) == OK) {
 				open_docs = true;
 			}
 		}
@@ -1567,17 +1615,17 @@ void ScriptTextEditor::_color_changed(const Color &p_color) {
 		new_args = String("(" + rtos(p_color.r) + ", " + rtos(p_color.g) + ", " + rtos(p_color.b) + ", " + rtos(p_color.a) + ")");
 	}
 
-	String line = code_editor->get_text_edit()->get_line(color_position.x);
+	String line = code_editor->get_text_editor()->get_line(color_position.x);
 	int color_args_pos = line.find(color_args, color_position.y);
 	String line_with_replaced_args = line;
 	line_with_replaced_args.erase(color_args_pos, color_args.length());
 	line_with_replaced_args = line_with_replaced_args.insert(color_args_pos, new_args);
 
 	color_args = new_args;
-	code_editor->get_text_edit()->begin_complex_operation();
-	code_editor->get_text_edit()->set_line(color_position.x, line_with_replaced_args);
-	code_editor->get_text_edit()->end_complex_operation();
-	code_editor->get_text_edit()->update();
+	code_editor->get_text_editor()->begin_complex_operation();
+	code_editor->get_text_editor()->set_line(color_position.x, line_with_replaced_args);
+	code_editor->get_text_editor()->end_complex_operation();
+	code_editor->get_text_editor()->update();
 }
 
 void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition, Vector2 p_pos) {
@@ -1636,12 +1684,15 @@ void ScriptTextEditor::_enable_code_editor() {
 	code_editor->connect("show_warnings_panel", callable_mp(this, &ScriptTextEditor::_show_warnings_panel));
 	code_editor->connect("validate_script", callable_mp(this, &ScriptTextEditor::_validate_script));
 	code_editor->connect("load_theme_settings", callable_mp(this, &ScriptTextEditor::_load_theme_settings));
-	code_editor->get_text_edit()->connect("breakpoint_toggled", callable_mp(this, &ScriptTextEditor::_breakpoint_toggled));
-	code_editor->get_text_edit()->connect("symbol_lookup", callable_mp(this, &ScriptTextEditor::_lookup_symbol));
-	code_editor->get_text_edit()->connect("symbol_validate", callable_mp(this, &ScriptTextEditor::_validate_symbol));
-	code_editor->get_text_edit()->connect("info_clicked", callable_mp(this, &ScriptTextEditor::_lookup_connections));
-	code_editor->get_text_edit()->connect("gui_input", callable_mp(this, &ScriptTextEditor::_text_edit_gui_input));
+	code_editor->get_text_editor()->connect("breakpoint_toggled", callable_mp(this, &ScriptTextEditor::_breakpoint_toggled));
+	code_editor->get_text_editor()->connect("symbol_lookup", callable_mp(this, &ScriptTextEditor::_lookup_symbol));
+	code_editor->get_text_editor()->connect("symbol_validate", callable_mp(this, &ScriptTextEditor::_validate_symbol));
+	code_editor->get_text_editor()->connect("gutter_added", callable_mp(this, &ScriptTextEditor::_update_gutter_indexes));
+	code_editor->get_text_editor()->connect("gutter_removed", callable_mp(this, &ScriptTextEditor::_update_gutter_indexes));
+	code_editor->get_text_editor()->connect("gutter_clicked", callable_mp(this, &ScriptTextEditor::_gutter_clicked));
+	code_editor->get_text_editor()->connect("gui_input", callable_mp(this, &ScriptTextEditor::_text_edit_gui_input));
 	code_editor->show_toggle_scripts_button();
+	_update_gutter_indexes();
 
 	editor_box->add_child(warnings_panel);
 	warnings_panel->add_theme_font_override(
@@ -1758,6 +1809,16 @@ ScriptTextEditor::ScriptTextEditor() {
 	code_editor->set_code_complete_func(_code_complete_scripts, this);
 	code_editor->set_v_size_flags(SIZE_EXPAND_FILL);
 
+	code_editor->get_text_editor()->set_draw_breakpoints_gutter(true);
+	code_editor->get_text_editor()->set_draw_executing_lines_gutter(true);
+
+	connection_gutter = 1;
+	code_editor->get_text_editor()->add_gutter(connection_gutter);
+	code_editor->get_text_editor()->set_gutter_name(connection_gutter, "connection_gutter");
+	code_editor->get_text_editor()->set_gutter_draw(connection_gutter, false);
+	code_editor->get_text_editor()->set_gutter_overwritable(connection_gutter, true);
+	code_editor->get_text_editor()->set_gutter_type(connection_gutter, TextEdit::GUTTER_TPYE_ICON);
+
 	warnings_panel = memnew(RichTextLabel);
 	warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE));
 	warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -1768,12 +1829,12 @@ ScriptTextEditor::ScriptTextEditor() {
 
 	update_settings();
 
-	code_editor->get_text_edit()->set_callhint_settings(
+	code_editor->get_text_editor()->set_callhint_settings(
 			EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line"),
 			EditorSettings::get_singleton()->get("text_editor/completion/callhint_tooltip_offset"));
 
-	code_editor->get_text_edit()->set_select_identifiers_on_hover(true);
-	code_editor->get_text_edit()->set_context_menu_enabled(false);
+	code_editor->get_text_editor()->set_select_identifiers_on_hover(true);
+	code_editor->get_text_editor()->set_context_menu_enabled(false);
 
 	context_menu = memnew(PopupMenu);
 
@@ -1816,7 +1877,7 @@ ScriptTextEditor::ScriptTextEditor() {
 
 	connection_info_dialog = memnew(ConnectionInfoDialog);
 
-	code_editor->get_text_edit()->set_drag_forwarding(this);
+	code_editor->get_text_editor()->set_drag_forwarding(this);
 }
 
 ScriptTextEditor::~ScriptTextEditor() {

+ 10 - 3
editor/plugins/script_text_editor.h

@@ -81,6 +81,14 @@ class ScriptTextEditor : public ScriptEditorBase {
 	ScriptEditorQuickOpen *quick_open = nullptr;
 	ConnectionInfoDialog *connection_info_dialog = nullptr;
 
+	int connection_gutter = -1;
+	void _gutter_clicked(int p_line, int p_gutter);
+	void _update_gutter_indexes();
+
+	int line_number_gutter = -1;
+	Color default_line_number_color = Color(1, 1, 1);
+	Color safe_line_number_color = Color(1, 1, 1);
+
 	PopupPanel *color_panel = nullptr;
 	ColorPicker *color_picker = nullptr;
 	Vector2 color_position;
@@ -154,6 +162,7 @@ protected:
 	void _show_warnings_panel(bool p_show);
 	void _warning_clicked(Variant p_line);
 
+	void _notification(int p_what);
 	static void _bind_methods();
 
 	Map<String, Ref<EditorSyntaxHighlighter>> highlighters;
@@ -169,8 +178,6 @@ protected:
 	void _lookup_symbol(const String &p_symbol, int p_row, int p_column);
 	void _validate_symbol(const String &p_symbol);
 
-	void _lookup_connections(int p_row, String p_method);
-
 	void _convert_case(CodeTextEditor::CaseStyle p_case);
 
 	Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
@@ -211,7 +218,7 @@ public:
 	virtual void clear_executing_line() override;
 
 	virtual void reload(bool p_soft) override;
-	virtual void get_breakpoints(List<int> *p_breakpoints) override;
+	virtual Array get_breakpoints() override;
 
 	virtual void add_callback(const String &p_function, PackedStringArray p_args) override;
 	virtual void update_settings() override;

+ 61 - 78
editor/plugins/shader_editor_plugin.cpp

@@ -55,8 +55,8 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) {
 
 	_load_theme_settings();
 
-	get_text_edit()->set_text(p_shader->get_code());
-	get_text_edit()->clear_undo_history();
+	get_text_editor()->set_text(p_shader->get_code());
+	get_text_editor()->clear_undo_history();
 
 	_validate_script();
 	_line_col_changed();
@@ -65,7 +65,7 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) {
 void ShaderTextEditor::reload_text() {
 	ERR_FAIL_COND(shader.is_null());
 
-	TextEdit *te = get_text_edit();
+	CodeEdit *te = get_text_editor();
 	int column = te->cursor_get_column();
 	int row = te->cursor_get_line();
 	int h = te->get_h_scroll();
@@ -107,29 +107,29 @@ void ShaderTextEditor::_load_theme_settings() {
 	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");
 
-	get_text_edit()->add_theme_color_override("background_color", background_color);
-	get_text_edit()->add_theme_color_override("completion_background_color", completion_background_color);
-	get_text_edit()->add_theme_color_override("completion_selected_color", completion_selected_color);
-	get_text_edit()->add_theme_color_override("completion_existing_color", completion_existing_color);
-	get_text_edit()->add_theme_color_override("completion_scroll_color", completion_scroll_color);
-	get_text_edit()->add_theme_color_override("completion_font_color", completion_font_color);
-	get_text_edit()->add_theme_color_override("font_color", text_color);
-	get_text_edit()->add_theme_color_override("line_number_color", line_number_color);
-	get_text_edit()->add_theme_color_override("caret_color", caret_color);
-	get_text_edit()->add_theme_color_override("caret_background_color", caret_background_color);
-	get_text_edit()->add_theme_color_override("font_color_selected", text_selected_color);
-	get_text_edit()->add_theme_color_override("selection_color", selection_color);
-	get_text_edit()->add_theme_color_override("brace_mismatch_color", brace_mismatch_color);
-	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("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);
-	get_text_edit()->add_theme_color_override("executing_line_color", executing_line_color);
-	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_editor()->add_theme_color_override("background_color", background_color);
+	get_text_editor()->add_theme_color_override("completion_background_color", completion_background_color);
+	get_text_editor()->add_theme_color_override("completion_selected_color", completion_selected_color);
+	get_text_editor()->add_theme_color_override("completion_existing_color", completion_existing_color);
+	get_text_editor()->add_theme_color_override("completion_scroll_color", completion_scroll_color);
+	get_text_editor()->add_theme_color_override("completion_font_color", completion_font_color);
+	get_text_editor()->add_theme_color_override("font_color", text_color);
+	get_text_editor()->add_theme_color_override("line_number_color", line_number_color);
+	get_text_editor()->add_theme_color_override("caret_color", caret_color);
+	get_text_editor()->add_theme_color_override("caret_background_color", caret_background_color);
+	get_text_editor()->add_theme_color_override("font_color_selected", text_selected_color);
+	get_text_editor()->add_theme_color_override("selection_color", selection_color);
+	get_text_editor()->add_theme_color_override("brace_mismatch_color", brace_mismatch_color);
+	get_text_editor()->add_theme_color_override("current_line_color", current_line_color);
+	get_text_editor()->add_theme_color_override("line_length_guideline_color", line_length_guideline_color);
+	get_text_editor()->add_theme_color_override("word_highlighted_color", word_highlighted_color);
+	get_text_editor()->add_theme_color_override("mark_color", mark_color);
+	get_text_editor()->add_theme_color_override("bookmark_color", bookmark_color);
+	get_text_editor()->add_theme_color_override("breakpoint_color", breakpoint_color);
+	get_text_editor()->add_theme_color_override("executing_line_color", executing_line_color);
+	get_text_editor()->add_theme_color_override("code_folding_color", code_folding_color);
+	get_text_editor()->add_theme_color_override("search_result_color", search_result_color);
+	get_text_editor()->add_theme_color_override("search_result_border_color", search_result_border_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"));
@@ -176,7 +176,7 @@ void ShaderTextEditor::_load_theme_settings() {
 }
 
 void ShaderTextEditor::_check_shader_mode() {
-	String type = ShaderLanguage::get_shader_type(get_text_edit()->get_text());
+	String type = ShaderLanguage::get_shader_type(get_text_editor()->get_text());
 
 	Shader::Mode mode;
 
@@ -189,7 +189,7 @@ void ShaderTextEditor::_check_shader_mode() {
 	}
 
 	if (shader->get_mode() != mode) {
-		shader->set_code(get_text_edit()->get_text());
+		shader->set_code(get_text_editor()->get_text());
 		_load_theme_settings();
 	}
 }
@@ -207,13 +207,13 @@ void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptCo
 
 	sl.complete(p_code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type, r_options, calltip);
 
-	get_text_edit()->set_code_hint(calltip);
+	get_text_editor()->set_code_hint(calltip);
 }
 
 void ShaderTextEditor::_validate_script() {
 	_check_shader_mode();
 
-	String code = get_text_edit()->get_text();
+	String code = get_text_editor()->get_text();
 	//List<StringName> params;
 	//shader->get_param_list(&params);
 
@@ -225,14 +225,14 @@ void ShaderTextEditor::_validate_script() {
 		String error_text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text();
 		set_error(error_text);
 		set_error_pos(sl.get_error_line() - 1, 0);
-		for (int i = 0; i < get_text_edit()->get_line_count(); i++) {
-			get_text_edit()->set_line_as_marked(i, false);
+		for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
+			get_text_editor()->set_line_as_marked(i, false);
 		}
-		get_text_edit()->set_line_as_marked(sl.get_error_line() - 1, true);
+		get_text_editor()->set_line_as_marked(sl.get_error_line() - 1, true);
 
 	} else {
-		for (int i = 0; i < get_text_edit()->get_line_count(); i++) {
-			get_text_edit()->set_line_as_marked(i, false);
+		for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
+			get_text_editor()->set_line_as_marked(i, false);
 		}
 		set_error("");
 	}
@@ -245,7 +245,7 @@ void ShaderTextEditor::_bind_methods() {
 
 ShaderTextEditor::ShaderTextEditor() {
 	syntax_highlighter.instance();
-	get_text_edit()->set_syntax_highlighter(syntax_highlighter);
+	get_text_editor()->set_syntax_highlighter(syntax_highlighter);
 }
 
 /*** SCRIPT EDITOR ******/
@@ -253,22 +253,22 @@ ShaderTextEditor::ShaderTextEditor() {
 void ShaderEditor::_menu_option(int p_option) {
 	switch (p_option) {
 		case EDIT_UNDO: {
-			shader_editor->get_text_edit()->undo();
+			shader_editor->get_text_editor()->undo();
 		} break;
 		case EDIT_REDO: {
-			shader_editor->get_text_edit()->redo();
+			shader_editor->get_text_editor()->redo();
 		} break;
 		case EDIT_CUT: {
-			shader_editor->get_text_edit()->cut();
+			shader_editor->get_text_editor()->cut();
 		} break;
 		case EDIT_COPY: {
-			shader_editor->get_text_edit()->copy();
+			shader_editor->get_text_editor()->copy();
 		} break;
 		case EDIT_PASTE: {
-			shader_editor->get_text_edit()->paste();
+			shader_editor->get_text_editor()->paste();
 		} break;
 		case EDIT_SELECT_ALL: {
-			shader_editor->get_text_edit()->select_all();
+			shader_editor->get_text_editor()->select_all();
 		} break;
 		case EDIT_MOVE_LINE_UP: {
 			shader_editor->move_lines_up();
@@ -281,7 +281,7 @@ void ShaderEditor::_menu_option(int p_option) {
 				return;
 			}
 
-			TextEdit *tx = shader_editor->get_text_edit();
+			CodeEdit *tx = shader_editor->get_text_editor();
 			tx->indent_left();
 
 		} break;
@@ -290,7 +290,7 @@ void ShaderEditor::_menu_option(int p_option) {
 				return;
 			}
 
-			TextEdit *tx = shader_editor->get_text_edit();
+			CodeEdit *tx = shader_editor->get_text_editor();
 			tx->indent_right();
 
 		} break;
@@ -309,7 +309,7 @@ void ShaderEditor::_menu_option(int p_option) {
 
 		} break;
 		case EDIT_COMPLETE: {
-			shader_editor->get_text_edit()->query_code_comple();
+			shader_editor->get_text_editor()->query_code_comple();
 		} break;
 		case SEARCH_FIND: {
 			shader_editor->get_find_replace_bar()->popup_search();
@@ -324,7 +324,7 @@ void ShaderEditor::_menu_option(int p_option) {
 			shader_editor->get_find_replace_bar()->popup_replace();
 		} break;
 		case SEARCH_GOTO_LINE: {
-			goto_line_dialog->popup_find_line(shader_editor->get_text_edit());
+			goto_line_dialog->popup_find_line(shader_editor->get_text_editor());
 		} break;
 		case BOOKMARK_TOGGLE: {
 			shader_editor->toggle_bookmark();
@@ -343,7 +343,7 @@ void ShaderEditor::_menu_option(int p_option) {
 		} break;
 	}
 	if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) {
-		shader_editor->get_text_edit()->call_deferred("grab_focus");
+		shader_editor->get_text_editor()->call_deferred("grab_focus");
 	}
 }
 
@@ -358,28 +358,11 @@ void ShaderEditor::_params_changed() {
 }
 
 void ShaderEditor::_editor_settings_changed() {
-	shader_editor->get_text_edit()->set_auto_brace_completion(EditorSettings::get_singleton()->get("text_editor/completion/auto_brace_complete"));
-	shader_editor->get_text_edit()->set_scroll_pass_end_of_file(EditorSettings::get_singleton()->get("text_editor/cursor/scroll_past_end_of_file"));
-	shader_editor->get_text_edit()->set_indent_size(EditorSettings::get_singleton()->get("text_editor/indent/size"));
-	shader_editor->get_text_edit()->set_indent_using_spaces(EditorSettings::get_singleton()->get("text_editor/indent/type"));
-	shader_editor->get_text_edit()->set_auto_indent(EditorSettings::get_singleton()->get("text_editor/indent/auto_indent"));
-	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_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"));
-	shader_editor->get_text_edit()->cursor_set_blink_speed(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink_speed"));
-	shader_editor->get_text_edit()->add_theme_constant_override("line_spacing", EditorSettings::get_singleton()->get("text_editor/theme/line_spacing"));
-	shader_editor->get_text_edit()->cursor_set_block_mode(EditorSettings::get_singleton()->get("text_editor/cursor/block_caret"));
-	shader_editor->get_text_edit()->set_smooth_scroll_enabled(EditorSettings::get_singleton()->get("text_editor/navigation/smooth_scrolling"));
-	shader_editor->get_text_edit()->set_v_scroll_speed(EditorSettings::get_singleton()->get("text_editor/navigation/v_scroll_speed"));
-	shader_editor->get_text_edit()->set_draw_minimap(EditorSettings::get_singleton()->get("text_editor/navigation/show_minimap"));
-	shader_editor->get_text_edit()->set_minimap_width((int)EditorSettings::get_singleton()->get("text_editor/navigation/minimap_width") * EDSCALE);
-	shader_editor->get_text_edit()->set_show_line_length_guidelines(EditorSettings::get_singleton()->get("text_editor/appearance/show_line_length_guidelines"));
-	shader_editor->get_text_edit()->set_line_length_guideline_soft_column(EditorSettings::get_singleton()->get("text_editor/appearance/line_length_guideline_soft_column"));
-	shader_editor->get_text_edit()->set_line_length_guideline_hard_column(EditorSettings::get_singleton()->get("text_editor/appearance/line_length_guideline_hard_column"));
-	shader_editor->get_text_edit()->set_breakpoint_gutter_enabled(false);
+	shader_editor->update_editor_settings();
+
+	shader_editor->get_text_editor()->add_theme_constant_override("line_spacing", EditorSettings::get_singleton()->get("text_editor/theme/line_spacing"));
+	shader_editor->get_text_editor()->set_draw_breakpoints_gutter(false);
+	shader_editor->get_text_editor()->set_draw_executing_lines_gutter(false);
 }
 
 void ShaderEditor::_bind_methods() {
@@ -466,7 +449,7 @@ void ShaderEditor::save_external_data(const String &p_str) {
 void ShaderEditor::apply_shaders() {
 	if (shader.is_valid()) {
 		String shader_code = shader->get_code();
-		String editor_code = shader_editor->get_text_edit()->get_text();
+		String editor_code = shader_editor->get_text_editor()->get_text();
 		if (shader_code != editor_code) {
 			shader->set_code(editor_code);
 			shader->set_edited(true);
@@ -480,7 +463,7 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
 	if (mb.is_valid()) {
 		if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) {
 			int col, row;
-			TextEdit *tx = shader_editor->get_text_edit();
+			CodeEdit *tx = shader_editor->get_text_editor();
 			tx->_get_mouse_pos(mb->get_global_position() - tx->get_global_position(), row, col);
 			tx->set_right_click_moves_caret(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret"));
 
@@ -507,7 +490,7 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
 
 	Ref<InputEventKey> k = ev;
 	if (k.is_valid() && k->is_pressed() && k->get_keycode() == KEY_MENU) {
-		TextEdit *tx = shader_editor->get_text_edit();
+		CodeEdit *tx = shader_editor->get_text_editor();
 		_make_context_menu(tx->is_selection_active(), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->_get_cursor_pixel_pos()));
 		context_menu->grab_focus();
 	}
@@ -521,7 +504,7 @@ void ShaderEditor::_update_bookmark_list() {
 	bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT);
 	bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV);
 
-	Array bookmark_list = shader_editor->get_text_edit()->get_bookmarks_array();
+	Array bookmark_list = shader_editor->get_text_editor()->get_bookmarked_lines();
 	if (bookmark_list.size() == 0) {
 		return;
 	}
@@ -529,7 +512,7 @@ void ShaderEditor::_update_bookmark_list() {
 	bookmarks_menu->add_separator();
 
 	for (int i = 0; i < bookmark_list.size(); i++) {
-		String line = shader_editor->get_text_edit()->get_line(bookmark_list[i]).strip_edges();
+		String line = shader_editor->get_text_editor()->get_line(bookmark_list[i]).strip_edges();
 		// Limit the size of the line if too big.
 		if (line.length() > 50) {
 			line = line.substr(0, 50);
@@ -581,13 +564,13 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) {
 	shader_editor->connect("script_changed", callable_mp(this, &ShaderEditor::apply_shaders));
 	EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ShaderEditor::_editor_settings_changed));
 
-	shader_editor->get_text_edit()->set_callhint_settings(
+	shader_editor->get_text_editor()->set_callhint_settings(
 			EditorSettings::get_singleton()->get("text_editor/completion/put_callhint_tooltip_below_current_line"),
 			EditorSettings::get_singleton()->get("text_editor/completion/callhint_tooltip_offset"));
 
-	shader_editor->get_text_edit()->set_select_identifiers_on_hover(true);
-	shader_editor->get_text_edit()->set_context_menu_enabled(false);
-	shader_editor->get_text_edit()->connect("gui_input", callable_mp(this, &ShaderEditor::_text_edit_gui_input));
+	shader_editor->get_text_editor()->set_select_identifiers_on_hover(true);
+	shader_editor->get_text_editor()->set_context_menu_enabled(false);
+	shader_editor->get_text_editor()->connect("gui_input", callable_mp(this, &ShaderEditor::_text_edit_gui_input));
 
 	shader_editor->update_editor_settings();
 

+ 23 - 22
editor/plugins/text_editor.cpp

@@ -50,7 +50,7 @@ void TextEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlight
 		el = el->next();
 	}
 
-	TextEdit *te = code_editor->get_text_edit();
+	CodeEdit *te = code_editor->get_text_editor();
 	te->set_syntax_highlighter(p_highlighter);
 }
 
@@ -59,7 +59,7 @@ void TextEditor::_change_syntax_highlighter(int p_idx) {
 }
 
 void TextEditor::_load_theme_settings() {
-	TextEdit *text_edit = code_editor->get_text_edit();
+	CodeEdit *text_edit = code_editor->get_text_editor();
 	text_edit->get_syntax_highlighter()->update_cache();
 
 	Color background_color = EDITOR_GET("text_editor/highlighting/background_color");
@@ -147,9 +147,9 @@ void TextEditor::set_edited_resource(const RES &p_res) {
 
 	text_file = p_res;
 
-	code_editor->get_text_edit()->set_text(text_file->get_text());
-	code_editor->get_text_edit()->clear_undo_history();
-	code_editor->get_text_edit()->tag_saved_version();
+	code_editor->get_text_editor()->set_text(text_file->get_text());
+	code_editor->get_text_editor()->clear_undo_history();
+	code_editor->get_text_editor()->tag_saved_version();
 
 	emit_signal("name_changed");
 	code_editor->update_line_and_column();
@@ -171,13 +171,14 @@ void TextEditor::add_callback(const String &p_function, PackedStringArray p_args
 void TextEditor::set_debugger_active(bool p_active) {
 }
 
-void TextEditor::get_breakpoints(List<int> *p_breakpoints) {
+Array TextEditor::get_breakpoints() {
+	return Array();
 }
 
 void TextEditor::reload_text() {
 	ERR_FAIL_COND(text_file.is_null());
 
-	TextEdit *te = code_editor->get_text_edit();
+	CodeEdit *te = code_editor->get_text_editor();
 	int column = te->cursor_get_column();
 	int row = te->cursor_get_line();
 	int h = te->get_h_scroll();
@@ -207,7 +208,7 @@ void TextEditor::_update_bookmark_list() {
 	bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT);
 	bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV);
 
-	Array bookmark_list = code_editor->get_text_edit()->get_bookmarks_array();
+	Array bookmark_list = code_editor->get_text_editor()->get_bookmarked_lines();
 	if (bookmark_list.size() == 0) {
 		return;
 	}
@@ -215,7 +216,7 @@ void TextEditor::_update_bookmark_list() {
 	bookmarks_menu->add_separator();
 
 	for (int i = 0; i < bookmark_list.size(); i++) {
-		String line = code_editor->get_text_edit()->get_line(bookmark_list[i]).strip_edges();
+		String line = code_editor->get_text_editor()->get_line(bookmark_list[i]).strip_edges();
 		// Limit the size of the line if too big.
 		if (line.length() > 50) {
 			line = line.substr(0, 50);
@@ -235,12 +236,12 @@ void TextEditor::_bookmark_item_pressed(int p_idx) {
 }
 
 void TextEditor::apply_code() {
-	text_file->set_text(code_editor->get_text_edit()->get_text());
+	text_file->set_text(code_editor->get_text_editor()->get_text());
 }
 
 bool TextEditor::is_unsaved() {
 	const bool unsaved =
-			code_editor->get_text_edit()->get_version() != code_editor->get_text_edit()->get_saved_version() ||
+			code_editor->get_text_editor()->get_version() != code_editor->get_text_editor()->get_saved_version() ||
 			text_file->get_path().empty(); // In memory.
 	return unsaved;
 }
@@ -280,7 +281,7 @@ void TextEditor::convert_indent_to_tabs() {
 }
 
 void TextEditor::tag_saved_version() {
-	code_editor->get_text_edit()->tag_saved_version();
+	code_editor->get_text_editor()->tag_saved_version();
 }
 
 void TextEditor::goto_line(int p_line, bool p_with_error) {
@@ -300,7 +301,7 @@ void TextEditor::clear_executing_line() {
 }
 
 void TextEditor::ensure_focus() {
-	code_editor->get_text_edit()->grab_focus();
+	code_editor->get_text_editor()->grab_focus();
 }
 
 Vector<String> TextEditor::get_functions() {
@@ -316,7 +317,7 @@ void TextEditor::update_settings() {
 }
 
 void TextEditor::set_tooltip_request_func(String p_method, Object *p_obj) {
-	code_editor->get_text_edit()->set_tooltip_request_func(p_obj, p_method, this);
+	code_editor->get_text_editor()->set_tooltip_request_func(p_obj, p_method, this);
 }
 
 Control *TextEditor::get_edit_menu() {
@@ -328,7 +329,7 @@ void TextEditor::clear_edit_menu() {
 }
 
 void TextEditor::_edit_option(int p_op) {
-	TextEdit *tx = code_editor->get_text_edit();
+	CodeEdit *tx = code_editor->get_text_editor();
 
 	switch (p_op) {
 		case EDIT_UNDO: {
@@ -416,14 +417,14 @@ void TextEditor::_edit_option(int p_op) {
 			code_editor->get_find_replace_bar()->popup_replace();
 		} break;
 		case SEARCH_IN_FILES: {
-			String selected_text = code_editor->get_text_edit()->get_selection_text();
+			String selected_text = code_editor->get_text_editor()->get_selection_text();
 
 			// Yep, because it doesn't make sense to instance this dialog for every single script open...
 			// So this will be delegated to the ScriptEditor.
 			emit_signal("search_in_files_requested", selected_text);
 		} break;
 		case REPLACE_IN_FILES: {
-			String selected_text = code_editor->get_text_edit()->get_selection_text();
+			String selected_text = code_editor->get_text_editor()->get_selection_text();
 
 			emit_signal("replace_in_files_requested", selected_text);
 		} break;
@@ -470,7 +471,7 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
 	if (mb.is_valid()) {
 		if (mb->get_button_index() == BUTTON_RIGHT) {
 			int col, row;
-			TextEdit *tx = code_editor->get_text_edit();
+			CodeEdit *tx = code_editor->get_text_editor();
 			tx->_get_mouse_pos(mb->get_global_position() - tx->get_global_position(), row, col);
 
 			tx->set_right_click_moves_caret(EditorSettings::get_singleton()->get("text_editor/cursor/right_click_moves_caret"));
@@ -503,7 +504,7 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
 
 	Ref<InputEventKey> k = ev;
 	if (k.is_valid() && k->is_pressed() && k->get_keycode() == KEY_MENU) {
-		TextEdit *tx = code_editor->get_text_edit();
+		CodeEdit *tx = code_editor->get_text_editor();
 		int line = tx->cursor_get_line();
 		_make_context_menu(tx->is_selection_active(), tx->can_fold(line), tx->is_folded(line), (get_global_transform().inverse() * tx->get_global_transform()).xform(tx->_get_cursor_pixel_pos()));
 		context_menu->grab_focus();
@@ -552,8 +553,8 @@ TextEditor::TextEditor() {
 
 	update_settings();
 
-	code_editor->get_text_edit()->set_context_menu_enabled(false);
-	code_editor->get_text_edit()->connect("gui_input", callable_mp(this, &TextEditor::_text_edit_gui_input));
+	code_editor->get_text_editor()->set_context_menu_enabled(false);
+	code_editor->get_text_editor()->connect("gui_input", callable_mp(this, &TextEditor::_text_edit_gui_input));
 
 	context_menu = memnew(PopupMenu);
 	add_child(context_menu);
@@ -649,7 +650,7 @@ TextEditor::TextEditor() {
 	goto_line_dialog = memnew(GotoLineDialog);
 	add_child(goto_line_dialog);
 
-	code_editor->get_text_edit()->set_drag_forwarding(this);
+	code_editor->get_text_editor()->set_drag_forwarding(this);
 }
 
 TextEditor::~TextEditor() {

+ 1 - 1
editor/plugins/text_editor.h

@@ -119,7 +119,7 @@ public:
 	virtual Variant get_edit_state() override;
 	virtual void set_edit_state(const Variant &p_state) override;
 	virtual Vector<String> get_functions() override;
-	virtual void get_breakpoints(List<int> *p_breakpoints) override;
+	virtual Array get_breakpoints() override;
 	virtual void goto_line(int p_line, bool p_with_error = false) override;
 	void goto_line_selection(int p_line, int p_begin, int p_end);
 	virtual void set_executing_line(int p_line) override;

+ 6 - 6
editor/plugins/visual_shader_editor_plugin.cpp

@@ -935,7 +935,7 @@ void VisualShaderEditor::_update_graph() {
 		}
 
 		if (is_expression) {
-			TextEdit *expression_box = memnew(TextEdit);
+			CodeEdit *expression_box = memnew(CodeEdit);
 			Ref<CodeHighlighter> expression_syntax_highlighter;
 			expression_syntax_highlighter.instance();
 			expression_node->set_control(expression_box, 0);
@@ -968,7 +968,7 @@ void VisualShaderEditor::_update_graph() {
 
 			expression_box->set_text(expression);
 			expression_box->set_context_menu_enabled(false);
-			expression_box->set_show_line_numbers(true);
+			expression_box->set_draw_line_numbers(true);
 
 			expression_box->connect("focus_exited", callable_mp(this, &VisualShaderEditor::_expression_focus_out), varray(expression_box, nodes[n_i]));
 		}
@@ -1186,14 +1186,14 @@ void VisualShaderEditor::_remove_output_port(int p_node, int p_port) {
 	undo_redo->commit_action();
 }
 
-void VisualShaderEditor::_expression_focus_out(Object *text_edit, int p_node) {
+void VisualShaderEditor::_expression_focus_out(Object *code_edit, int p_node) {
 	VisualShader::Type type = get_current_shader_type();
 	Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node);
 	if (node.is_null()) {
 		return;
 	}
 
-	TextEdit *expression_box = Object::cast_to<TextEdit>(text_edit);
+	CodeEdit *expression_box = Object::cast_to<CodeEdit>(code_edit);
 
 	if (node->get_expression() == expression_box->get_text()) {
 		return;
@@ -2550,14 +2550,14 @@ VisualShaderEditor::VisualShaderEditor() {
 	preview_vbox = memnew(VBoxContainer);
 	preview_vbox->set_visible(preview_showed);
 	main_box->add_child(preview_vbox);
-	preview_text = memnew(TextEdit);
+	preview_text = memnew(CodeEdit);
 	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_highlighter(syntax_highlighter);
-	preview_text->set_show_line_numbers(true);
+	preview_text->set_draw_line_numbers(true);
 	preview_text->set_readonly(true);
 
 	error_text = memnew(Label);

+ 2 - 2
editor/plugins/visual_shader_editor_plugin.h

@@ -115,7 +115,7 @@ class VisualShaderEditor : public VBoxContainer {
 	bool pending_update_preview;
 	bool shader_error;
 	VBoxContainer *preview_vbox;
-	TextEdit *preview_text;
+	CodeEdit *preview_text;
 	Ref<CodeHighlighter> syntax_highlighter;
 	Label *error_text;
 
@@ -304,7 +304,7 @@ class VisualShaderEditor : public VBoxContainer {
 	void _change_output_port_type(int p_type, int p_node, int p_port);
 	void _change_output_port_name(const String &p_text, Object *line_edit, int p_node, int p_port);
 
-	void _expression_focus_out(Object *text_edit, int p_node);
+	void _expression_focus_out(Object *code_edit, int p_node);
 
 	void _set_node_size(int p_type, int p_node, const Size2 &p_size);
 	void _node_resized(const Vector2 &p_new_size, int p_type, int p_node);

+ 4 - 2
modules/visual_script/visual_script_editor.cpp

@@ -2682,7 +2682,8 @@ void VisualScriptEditor::reload(bool p_soft) {
 	_update_graph();
 }
 
-void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) {
+Array VisualScriptEditor::get_breakpoints() {
+	Array breakpoints;
 	List<StringName> functions;
 	script->get_function_list(&functions);
 	for (List<StringName>::Element *E = functions.front(); E; E = E->next()) {
@@ -2691,10 +2692,11 @@ void VisualScriptEditor::get_breakpoints(List<int> *p_breakpoints) {
 		for (List<int>::Element *F = nodes.front(); F; F = F->next()) {
 			Ref<VisualScriptNode> vsn = script->get_node(E->get(), F->get());
 			if (vsn->is_breakpoint()) {
-				p_breakpoints->push_back(F->get() - 1); //subtract 1 because breakpoints in text start from zero
+				breakpoints.push_back(F->get() - 1); //subtract 1 because breakpoints in text start from zero
 			}
 		}
 	}
+	return breakpoints;
 }
 
 void VisualScriptEditor::add_callback(const String &p_function, PackedStringArray p_args) {

+ 1 - 1
modules/visual_script/visual_script_editor.h

@@ -312,7 +312,7 @@ public:
 	virtual void ensure_focus() override;
 	virtual void tag_saved_version() override;
 	virtual void reload(bool p_soft) override;
-	virtual void get_breakpoints(List<int> *p_breakpoints) override;
+	virtual Array get_breakpoints() override;
 	virtual void add_callback(const String &p_function, PackedStringArray p_args) override;
 	virtual void update_settings() override;
 	virtual bool show_members_overview() override;

+ 444 - 0
scene/gui/code_edit.cpp

@@ -0,0 +1,444 @@
+/*************************************************************************/
+/*  code_edit.cpp                                                        */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "code_edit.h"
+
+void CodeEdit::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_THEME_CHANGED:
+		case NOTIFICATION_ENTER_TREE: {
+			set_gutter_width(main_gutter, cache.row_height);
+			set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0').width);
+			set_gutter_width(fold_gutter, cache.row_height / 1.2);
+
+			breakpoint_color = get_theme_color("breakpoint_color");
+			breakpoint_icon = get_theme_icon("breakpoint");
+
+			bookmark_color = get_theme_color("bookmark_color");
+			bookmark_icon = get_theme_icon("bookmark");
+
+			executing_line_color = get_theme_color("executing_line_color");
+			executing_line_icon = get_theme_icon("executing_line");
+
+			line_number_color = get_theme_color("line_number_color");
+
+			folding_color = get_theme_color("code_folding_color");
+			can_fold_icon = get_theme_icon("can_fold");
+			folded_icon = get_theme_icon("folded");
+		} break;
+		case NOTIFICATION_DRAW: {
+		} break;
+	}
+}
+
+/* Main Gutter */
+void CodeEdit::_update_draw_main_gutter() {
+	set_gutter_draw(main_gutter, draw_breakpoints || draw_bookmarks || draw_executing_lines);
+}
+
+void CodeEdit::set_draw_breakpoints_gutter(bool p_draw) {
+	draw_breakpoints = p_draw;
+	set_gutter_clickable(main_gutter, p_draw);
+	_update_draw_main_gutter();
+}
+
+bool CodeEdit::is_drawing_breakpoints_gutter() const {
+	return draw_breakpoints;
+}
+
+void CodeEdit::set_draw_bookmarks_gutter(bool p_draw) {
+	draw_bookmarks = p_draw;
+	_update_draw_main_gutter();
+}
+
+bool CodeEdit::is_drawing_bookmarks_gutter() const {
+	return draw_bookmarks;
+}
+
+void CodeEdit::set_draw_executing_lines_gutter(bool p_draw) {
+	draw_executing_lines = p_draw;
+	_update_draw_main_gutter();
+}
+
+bool CodeEdit::is_drawing_executing_lines_gutter() const {
+	return draw_executing_lines;
+}
+
+void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
+	if (draw_breakpoints && is_line_breakpointed(p_line)) {
+		int padding = p_region.size.x / 6;
+
+		Rect2 breakpoint_region = p_region;
+		breakpoint_region.position += Point2(padding, padding);
+		breakpoint_region.size -= Point2(padding, padding) * 2;
+		breakpoint_icon->draw_rect(get_canvas_item(), breakpoint_region, false, breakpoint_color);
+	}
+
+	if (draw_bookmarks && is_line_bookmarked(p_line)) {
+		int horizontal_padding = p_region.size.x / 2;
+		int vertical_padding = p_region.size.y / 4;
+
+		Rect2 bookmark_region = p_region;
+		bookmark_region.position += Point2(horizontal_padding, 0);
+		bookmark_region.size -= Point2(horizontal_padding * 1.1, vertical_padding);
+		bookmark_icon->draw_rect(get_canvas_item(), bookmark_region, false, bookmark_color);
+	}
+
+	if (draw_executing_lines && is_line_executing(p_line)) {
+		int horizontal_padding = p_region.size.x / 10;
+		int vertical_padding = p_region.size.y / 4;
+
+		Rect2 executing_line_region = p_region;
+		executing_line_region.position += Point2(horizontal_padding, vertical_padding);
+		executing_line_region.size -= Point2(horizontal_padding, vertical_padding) * 2;
+		executing_line_icon->draw_rect(get_canvas_item(), executing_line_region, false, executing_line_color);
+	}
+}
+
+// Breakpoints
+void CodeEdit::set_line_as_breakpoint(int p_line, bool p_breakpointed) {
+	int mask = get_line_gutter_metadata(p_line, main_gutter);
+	set_line_gutter_metadata(p_line, main_gutter, p_breakpointed ? mask | MAIN_GUTTER_BREAKPOINT : mask & ~MAIN_GUTTER_BREAKPOINT);
+	if (p_breakpointed) {
+		breakpointed_lines[p_line] = true;
+	} else if (breakpointed_lines.has(p_line)) {
+		breakpointed_lines.erase(p_line);
+	}
+	emit_signal("breakpoint_toggled", p_line);
+}
+
+bool CodeEdit::is_line_breakpointed(int p_line) const {
+	return (int)get_line_gutter_metadata(p_line, main_gutter) & MAIN_GUTTER_BREAKPOINT;
+}
+
+void CodeEdit::clear_breakpointed_lines() {
+	for (int i = 0; i < get_line_count(); i++) {
+		if (is_line_breakpointed(i)) {
+			set_line_as_breakpoint(i, false);
+		}
+	}
+}
+
+Array CodeEdit::get_breakpointed_lines() const {
+	Array ret;
+	for (int i = 0; i < get_line_count(); i++) {
+		if (is_line_breakpointed(i)) {
+			ret.append(i);
+		}
+	}
+	return ret;
+}
+
+// Bookmarks
+void CodeEdit::set_line_as_bookmarked(int p_line, bool p_bookmarked) {
+	int mask = get_line_gutter_metadata(p_line, main_gutter);
+	set_line_gutter_metadata(p_line, main_gutter, p_bookmarked ? mask | MAIN_GUTTER_BOOKMARK : mask & ~MAIN_GUTTER_BOOKMARK);
+}
+
+bool CodeEdit::is_line_bookmarked(int p_line) const {
+	return (int)get_line_gutter_metadata(p_line, main_gutter) & MAIN_GUTTER_BOOKMARK;
+}
+
+void CodeEdit::clear_bookmarked_lines() {
+	for (int i = 0; i < get_line_count(); i++) {
+		if (is_line_bookmarked(i)) {
+			set_line_as_bookmarked(i, false);
+		}
+	}
+}
+
+Array CodeEdit::get_bookmarked_lines() const {
+	Array ret;
+	for (int i = 0; i < get_line_count(); i++) {
+		if (is_line_bookmarked(i)) {
+			ret.append(i);
+		}
+	}
+	return ret;
+}
+
+// executing lines
+void CodeEdit::set_line_as_executing(int p_line, bool p_executing) {
+	int mask = get_line_gutter_metadata(p_line, main_gutter);
+	set_line_gutter_metadata(p_line, main_gutter, p_executing ? mask | MAIN_GUTTER_EXECUTING : mask & ~MAIN_GUTTER_EXECUTING);
+}
+
+bool CodeEdit::is_line_executing(int p_line) const {
+	return (int)get_line_gutter_metadata(p_line, main_gutter) & MAIN_GUTTER_EXECUTING;
+}
+
+void CodeEdit::clear_executing_lines() {
+	for (int i = 0; i < get_line_count(); i++) {
+		if (is_line_executing(i)) {
+			set_line_as_executing(i, false);
+		}
+	}
+}
+
+Array CodeEdit::get_executing_lines() const {
+	Array ret;
+	for (int i = 0; i < get_line_count(); i++) {
+		if (is_line_executing(i)) {
+			ret.append(i);
+		}
+	}
+	return ret;
+}
+
+/* Line numbers */
+void CodeEdit::set_draw_line_numbers(bool p_draw) {
+	set_gutter_draw(line_number_gutter, p_draw);
+}
+
+bool CodeEdit::is_draw_line_numbers_enabled() const {
+	return is_gutter_drawn(line_number_gutter);
+}
+
+void CodeEdit::set_line_numbers_zero_padded(bool p_zero_padded) {
+	p_zero_padded ? line_number_padding = "0" : line_number_padding = " ";
+	update();
+}
+
+bool CodeEdit::is_line_numbers_zero_padded() const {
+	return line_number_padding == "0";
+}
+
+void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
+	String fc = String::num(p_line + 1).lpad(line_number_digits, line_number_padding);
+
+	int yofs = p_region.position.y + (cache.row_height - cache.font->get_height()) / 2;
+	Color number_color = get_line_gutter_item_color(p_line, line_number_gutter);
+	if (number_color == Color(1, 1, 1)) {
+		number_color = line_number_color;
+	}
+	cache.font->draw(get_canvas_item(), Point2(p_region.position.x, yofs + cache.font->get_ascent()), fc, number_color);
+}
+
+/* Fold Gutter */
+void CodeEdit::set_draw_fold_gutter(bool p_draw) {
+	set_gutter_draw(fold_gutter, p_draw);
+}
+
+bool CodeEdit::is_drawing_fold_gutter() const {
+	return is_gutter_drawn(fold_gutter);
+}
+
+void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_region) {
+	if (!can_fold(p_line) && !is_folded(p_line)) {
+		set_line_gutter_clickable(p_line, fold_gutter, false);
+		return;
+	}
+	set_line_gutter_clickable(p_line, fold_gutter, true);
+
+	int horizontal_padding = p_region.size.x / 10;
+	int vertical_padding = p_region.size.y / 6;
+
+	p_region.position += Point2(horizontal_padding, vertical_padding);
+	p_region.size -= Point2(horizontal_padding, vertical_padding) * 2;
+
+	if (can_fold(p_line)) {
+		can_fold_icon->draw_rect(get_canvas_item(), p_region, false, folding_color);
+		return;
+	}
+	folded_icon->draw_rect(get_canvas_item(), p_region, false, folding_color);
+}
+
+void CodeEdit::_bind_methods() {
+	/* Main Gutter */
+	ClassDB::bind_method(D_METHOD("_main_gutter_draw_callback"), &CodeEdit::_main_gutter_draw_callback);
+
+	ClassDB::bind_method(D_METHOD("set_draw_breakpoints_gutter", "enable"), &CodeEdit::set_draw_breakpoints_gutter);
+	ClassDB::bind_method(D_METHOD("is_drawing_breakpoints_gutter"), &CodeEdit::is_drawing_breakpoints_gutter);
+
+	ClassDB::bind_method(D_METHOD("set_draw_bookmarks_gutter", "enable"), &CodeEdit::set_draw_bookmarks_gutter);
+	ClassDB::bind_method(D_METHOD("is_drawing_bookmarks_gutter"), &CodeEdit::is_drawing_bookmarks_gutter);
+
+	ClassDB::bind_method(D_METHOD("set_draw_executing_lines_gutter", "enable"), &CodeEdit::set_draw_executing_lines_gutter);
+	ClassDB::bind_method(D_METHOD("is_drawing_executing_lines_gutter"), &CodeEdit::is_drawing_executing_lines_gutter);
+
+	// Breakpoints
+	ClassDB::bind_method(D_METHOD("set_line_as_breakpoint", "line", "breakpointed"), &CodeEdit::set_line_as_breakpoint);
+	ClassDB::bind_method(D_METHOD("is_line_breakpointed", "line"), &CodeEdit::is_line_breakpointed);
+	ClassDB::bind_method(D_METHOD("clear_breakpointed_lines"), &CodeEdit::clear_breakpointed_lines);
+	ClassDB::bind_method(D_METHOD("get_breakpointed_lines"), &CodeEdit::get_breakpointed_lines);
+
+	// Bookmarks
+	ClassDB::bind_method(D_METHOD("set_line_as_bookmarked", "line", "bookmarked"), &CodeEdit::set_line_as_bookmarked);
+	ClassDB::bind_method(D_METHOD("is_line_bookmarked", "line"), &CodeEdit::is_line_bookmarked);
+	ClassDB::bind_method(D_METHOD("clear_bookmarked_lines"), &CodeEdit::clear_bookmarked_lines);
+	ClassDB::bind_method(D_METHOD("get_bookmarked_lines"), &CodeEdit::get_bookmarked_lines);
+
+	// executing lines
+	ClassDB::bind_method(D_METHOD("set_line_as_executing", "line", "executing"), &CodeEdit::set_line_as_executing);
+	ClassDB::bind_method(D_METHOD("is_line_executing", "line"), &CodeEdit::is_line_executing);
+	ClassDB::bind_method(D_METHOD("clear_executing_lines"), &CodeEdit::clear_executing_lines);
+	ClassDB::bind_method(D_METHOD("get_executing_lines"), &CodeEdit::get_executing_lines);
+
+	/* Line numbers */
+	ClassDB::bind_method(D_METHOD("_line_number_draw_callback"), &CodeEdit::_line_number_draw_callback);
+
+	ClassDB::bind_method(D_METHOD("set_draw_line_numbers", "enable"), &CodeEdit::set_draw_line_numbers);
+	ClassDB::bind_method(D_METHOD("is_draw_line_numbers_enabled"), &CodeEdit::is_draw_line_numbers_enabled);
+	ClassDB::bind_method(D_METHOD("set_line_numbers_zero_padded", "enable"), &CodeEdit::set_line_numbers_zero_padded);
+	ClassDB::bind_method(D_METHOD("is_line_numbers_zero_padded"), &CodeEdit::is_line_numbers_zero_padded);
+
+	/* Fold Gutter */
+	ClassDB::bind_method(D_METHOD("_fold_gutter_draw_callback"), &CodeEdit::_fold_gutter_draw_callback);
+
+	ClassDB::bind_method(D_METHOD("set_draw_fold_gutter", "enable"), &CodeEdit::set_draw_fold_gutter);
+	ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &CodeEdit::is_drawing_fold_gutter);
+
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_breakpoints_gutter"), "set_draw_breakpoints_gutter", "is_drawing_breakpoints_gutter");
+
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_bookmarks"), "set_draw_bookmarks_gutter", "is_drawing_bookmarks_gutter");
+
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_executing_lines"), "set_draw_executing_lines_gutter", "is_drawing_executing_lines_gutter");
+
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_line_numbers"), "set_draw_line_numbers", "is_draw_line_numbers_enabled");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "zero_pad_line_numbers"), "set_line_numbers_zero_padded", "is_line_numbers_zero_padded");
+
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter");
+
+	ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "line")));
+}
+
+void CodeEdit::_gutter_clicked(int p_line, int p_gutter) {
+	if (p_gutter == main_gutter) {
+		if (draw_breakpoints) {
+			set_line_as_breakpoint(p_line, !is_line_breakpointed(p_line));
+		}
+		return;
+	}
+
+	if (p_gutter == line_number_gutter) {
+		cursor_set_line(p_line);
+		return;
+	}
+
+	if (p_gutter == fold_gutter) {
+		if (is_folded(p_line)) {
+			unfold_line(p_line);
+		} else if (can_fold(p_line)) {
+			fold_line(p_line);
+		}
+		return;
+	}
+}
+
+void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) {
+	if (p_from_line == p_to_line) {
+		return;
+	}
+
+	int lc = get_line_count();
+	line_number_digits = 1;
+	while (lc /= 10) {
+		line_number_digits++;
+	}
+	set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0').width);
+
+	int from_line = MIN(p_from_line, p_to_line);
+	int line_count = (p_to_line - p_from_line);
+	List<int> breakpoints;
+	breakpointed_lines.get_key_list(&breakpoints);
+	for (const List<int>::Element *E = breakpoints.front(); E; E = E->next()) {
+		int line = E->get();
+		if (line <= from_line) {
+			continue;
+		}
+		breakpointed_lines.erase(line);
+
+		emit_signal("breakpoint_toggled", line);
+		if (line_count > 0 || line >= p_from_line) {
+			emit_signal("breakpoint_toggled", line + line_count);
+			breakpointed_lines[line + line_count] = true;
+			continue;
+		}
+	}
+}
+
+void CodeEdit::_update_gutter_indexes() {
+	for (int i = 0; i < get_gutter_count(); i++) {
+		if (get_gutter_name(i) == "main_gutter") {
+			main_gutter = i;
+			continue;
+		}
+
+		if (get_gutter_name(i) == "line_numbers") {
+			line_number_gutter = i;
+			continue;
+		}
+
+		if (get_gutter_name(i) == "fold_gutter") {
+			fold_gutter = i;
+			continue;
+		}
+	}
+}
+
+CodeEdit::CodeEdit() {
+	/* Gutters */
+	int gutter_idx = 0;
+
+	/* Main Gutter */
+	add_gutter();
+	set_gutter_name(gutter_idx, "main_gutter");
+	set_gutter_draw(gutter_idx, false);
+	set_gutter_overwritable(gutter_idx, true);
+	set_gutter_type(gutter_idx, GUTTER_TPYE_CUSTOM);
+	set_gutter_custom_draw(gutter_idx, this, "_main_gutter_draw_callback");
+	gutter_idx++;
+
+	/* Line numbers */
+	add_gutter();
+	set_gutter_name(gutter_idx, "line_numbers");
+	set_gutter_draw(gutter_idx, false);
+	set_gutter_type(gutter_idx, GUTTER_TPYE_CUSTOM);
+	set_gutter_custom_draw(gutter_idx, this, "_line_number_draw_callback");
+	gutter_idx++;
+
+	/* Fold Gutter */
+	add_gutter();
+	set_gutter_name(gutter_idx, "fold_gutter");
+	set_gutter_draw(gutter_idx, false);
+	set_gutter_type(gutter_idx, GUTTER_TPYE_CUSTOM);
+	set_gutter_custom_draw(gutter_idx, this, "_fold_gutter_draw_callback");
+	gutter_idx++;
+
+	connect("lines_edited_from", callable_mp(this, &CodeEdit::_lines_edited_from));
+	connect("gutter_clicked", callable_mp(this, &CodeEdit::_gutter_clicked));
+
+	connect("gutter_added", callable_mp(this, &CodeEdit::_update_gutter_indexes));
+	connect("gutter_removed", callable_mp(this, &CodeEdit::_update_gutter_indexes));
+	_update_gutter_indexes();
+}
+
+CodeEdit::~CodeEdit() {
+}

+ 135 - 0
scene/gui/code_edit.h

@@ -0,0 +1,135 @@
+/*************************************************************************/
+/*  code_edit.h                                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef CODEEDIT_H
+#define CODEEDIT_H
+
+#include "scene/gui/text_edit.h"
+
+class CodeEdit : public TextEdit {
+	GDCLASS(CodeEdit, TextEdit)
+
+private:
+	/* Main Gutter */
+	enum MainGutterType {
+		MAIN_GUTTER_BREAKPOINT = 0x01,
+		MAIN_GUTTER_BOOKMARK = 0x02,
+		MAIN_GUTTER_EXECUTING = 0x04
+	};
+
+	int main_gutter = -1;
+	void _update_draw_main_gutter();
+	void _main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region);
+
+	// breakpoints
+	HashMap<int, bool> breakpointed_lines;
+	bool draw_breakpoints = false;
+	Color breakpoint_color = Color(1, 1, 1);
+	Ref<Texture2D> breakpoint_icon = Ref<Texture2D>();
+
+	// bookmarks
+	bool draw_bookmarks = false;
+	Color bookmark_color = Color(1, 1, 1);
+	Ref<Texture2D> bookmark_icon = Ref<Texture2D>();
+
+	// executing lines
+	bool draw_executing_lines = false;
+	Color executing_line_color = Color(1, 1, 1);
+	Ref<Texture2D> executing_line_icon = Ref<Texture2D>();
+
+	/* Line numbers */
+	int line_number_gutter = -1;
+	int line_number_digits = 0;
+	String line_number_padding = " ";
+	Color line_number_color = Color(1, 1, 1);
+	void _line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region);
+
+	/* Fold Gutter */
+	int fold_gutter = -1;
+	bool draw_fold_gutter = false;
+	Color folding_color = Color(1, 1, 1);
+	Ref<Texture2D> can_fold_icon = Ref<Texture2D>();
+	Ref<Texture2D> folded_icon = Ref<Texture2D>();
+	void _fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_region);
+
+	void _gutter_clicked(int p_line, int p_gutter);
+	void _lines_edited_from(int p_from_line, int p_to_line);
+
+	void _update_gutter_indexes();
+
+protected:
+	void _notification(int p_what);
+
+	static void _bind_methods();
+
+public:
+	/* Main Gutter */
+	void set_draw_breakpoints_gutter(bool p_draw);
+	bool is_drawing_breakpoints_gutter() const;
+
+	void set_draw_bookmarks_gutter(bool p_draw);
+	bool is_drawing_bookmarks_gutter() const;
+
+	void set_draw_executing_lines_gutter(bool p_draw);
+	bool is_drawing_executing_lines_gutter() const;
+
+	// breakpoints
+	void set_line_as_breakpoint(int p_line, bool p_breakpointed);
+	bool is_line_breakpointed(int p_line) const;
+	void clear_breakpointed_lines();
+	Array get_breakpointed_lines() const;
+
+	// bookmarks
+	void set_line_as_bookmarked(int p_line, bool p_bookmarked);
+	bool is_line_bookmarked(int p_line) const;
+	void clear_bookmarked_lines();
+	Array get_bookmarked_lines() const;
+
+	// executing lines
+	void set_line_as_executing(int p_line, bool p_executing);
+	bool is_line_executing(int p_line) const;
+	void clear_executing_lines();
+	Array get_executing_lines() const;
+
+	/* Line numbers */
+	void set_draw_line_numbers(bool p_draw);
+	bool is_draw_line_numbers_enabled() const;
+	void set_line_numbers_zero_padded(bool p_zero_padded);
+	bool is_line_numbers_zero_padded() const;
+
+	/* Fold gutter */
+	void set_draw_fold_gutter(bool p_draw);
+	bool is_drawing_fold_gutter() const;
+
+	CodeEdit();
+	~CodeEdit();
+};
+
+#endif // CODEEDIT_H

File diff suppressed because it is too large
+ 317 - 459
scene/gui/text_edit.cpp


+ 139 - 142
scene/gui/text_edit.h

@@ -41,28 +41,53 @@ class TextEdit : public Control {
 	GDCLASS(TextEdit, Control);
 
 public:
+	enum GutterType {
+		GUTTER_TYPE_STRING,
+		GUTTER_TPYE_ICON,
+		GUTTER_TPYE_CUSTOM
+	};
+
+private:
+	struct GutterInfo {
+		GutterType type = GutterType::GUTTER_TYPE_STRING;
+		String name = "";
+		int width = 24;
+		bool draw = true;
+		bool clickable = false;
+		bool overwritable = false;
+
+		ObjectID custom_draw_obj = ObjectID();
+		StringName custom_draw_callback;
+	};
+	Vector<GutterInfo> gutters;
+	int gutters_width = 0;
+	int gutter_padding = 0;
+
+	void _update_gutter_width();
+
 	class Text {
 	public:
+		struct Gutter {
+			Variant metadata;
+			bool clickable = false;
+
+			Ref<Texture2D> icon = Ref<Texture2D>();
+			String text = "";
+			Color color = Color(1, 1, 1);
+		};
+
 		struct Line {
+			Vector<Gutter> gutters;
+
 			int width_cache : 24;
 			bool marked : 1;
-			bool breakpoint : 1;
-			bool bookmark : 1;
 			bool hidden : 1;
-			bool safe : 1;
-			bool has_info : 1;
 			int wrap_amount_cache : 24;
-			Ref<Texture2D> info_icon;
-			String info;
 			String data;
 			Line() {
 				width_cache = 0;
 				marked = false;
-				breakpoint = false;
-				bookmark = false;
 				hidden = false;
-				safe = false;
-				has_info = false;
 				wrap_amount_cache = 0;
 			}
 		};
@@ -70,7 +95,8 @@ public:
 	private:
 		mutable Vector<Line> text;
 		Ref<Font> font;
-		int indent_size;
+		int indent_size = 4;
+		int gutter_count = 0;
 
 		void _update_line_cache(int p_line) const;
 
@@ -85,38 +111,37 @@ public:
 		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; }
-		void set_bookmark(int p_line, bool p_bookmark) { text.write[p_line].bookmark = p_bookmark; }
-		bool is_bookmark(int p_line) const { return text[p_line].bookmark; }
-		void set_breakpoint(int p_line, bool p_breakpoint) { text.write[p_line].breakpoint = p_breakpoint; }
-		bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
 		void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; }
 		bool is_hidden(int p_line) const { return text[p_line].hidden; }
-		void set_safe(int p_line, bool p_safe) { text.write[p_line].safe = p_safe; }
-		bool is_safe(int p_line) const { return text[p_line].safe; }
-		void set_info_icon(int p_line, Ref<Texture2D> p_icon, String p_info) {
-			if (p_icon.is_null()) {
-				text.write[p_line].has_info = false;
-				return;
-			}
-			text.write[p_line].info_icon = p_icon;
-			text.write[p_line].info = p_info;
-			text.write[p_line].has_info = true;
-		}
-		bool has_info_icon(int p_line) const { return text[p_line].has_info; }
-		const Ref<Texture2D> &get_info_icon(int p_line) const { return text[p_line].info_icon; }
-		const String &get_info(int p_line) const { return text[p_line].info; }
 		void insert(int p_at, const String &p_text);
 		void remove(int p_at);
 		int size() const { return text.size(); }
 		void clear();
 		void clear_width_cache();
 		void clear_wrap_cache();
-		void clear_info_icons();
 		_FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
-		Text() { indent_size = 4; }
+
+		/* Gutters. */
+		void add_gutter(int p_at);
+		void remove_gutter(int p_gutter);
+		void move_gutters(int p_from_line, int p_to_line);
+
+		void set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata) { text.write[p_line].gutters.write[p_gutter].metadata = p_metadata; }
+		const Variant &get_line_gutter_metadata(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].metadata; }
+
+		void set_line_gutter_text(int p_line, int p_gutter, const String &p_text) { text.write[p_line].gutters.write[p_gutter].text = p_text; }
+		const String &get_line_gutter_text(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].text; }
+
+		void set_line_gutter_icon(int p_line, int p_gutter, Ref<Texture2D> p_icon) { text.write[p_line].gutters.write[p_gutter].icon = p_icon; }
+		const Ref<Texture2D> &get_line_gutter_icon(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].icon; }
+
+		void set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color) { text.write[p_line].gutters.write[p_gutter].color = p_color; }
+		const Color &get_line_gutter_item_color(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].color; }
+
+		void set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable) { text.write[p_line].gutters.write[p_gutter].clickable = p_clickable; }
+		bool is_line_gutter_clickable(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].clickable; }
 	};
 
-private:
 	struct Cursor {
 		int last_fit_x;
 		int line, column; ///< cursor
@@ -169,60 +194,6 @@ private:
 		}
 	} selection;
 
-	struct Cache {
-		Ref<Texture2D> tab_icon;
-		Ref<Texture2D> space_icon;
-		Ref<Texture2D> can_fold_icon;
-		Ref<Texture2D> folded_icon;
-		Ref<Texture2D> folded_eol_icon;
-		Ref<Texture2D> executing_icon;
-		Ref<StyleBox> style_normal;
-		Ref<StyleBox> style_focus;
-		Ref<StyleBox> style_readonly;
-		Ref<Font> font;
-		Color completion_background_color;
-		Color completion_selected_color;
-		Color completion_existing_color;
-		Color completion_font_color;
-		Color caret_color;
-		Color caret_background_color;
-		Color line_number_color;
-		Color safe_line_number_color;
-		Color font_color;
-		Color font_color_selected;
-		Color font_color_readonly;
-		Color selection_color;
-		Color mark_color;
-		Color bookmark_color;
-		Color breakpoint_color;
-		Color executing_line_color;
-		Color code_folding_color;
-		Color current_line_color;
-		Color line_length_guideline_color;
-		Color brace_mismatch_color;
-		Color word_highlighted_color;
-		Color search_result_color;
-		Color search_result_border_color;
-		Color background_color;
-
-		int row_height;
-		int line_spacing;
-		int line_number_w;
-		int breakpoint_gutter_width;
-		int fold_gutter_width;
-		int info_gutter_width;
-		int minimap_width;
-		Cache() {
-			row_height = 0;
-			line_spacing = 0;
-			line_number_w = 0;
-			breakpoint_gutter_width = 0;
-			fold_gutter_width = 0;
-			info_gutter_width = 0;
-			minimap_width = 0;
-		}
-	} cache;
-
 	Map<int, Dictionary> syntax_highlighting_cache;
 
 	struct TextOperation {
@@ -318,19 +289,10 @@ private:
 	bool cursor_changed_dirty;
 	bool text_changed_dirty;
 	bool undo_enabled;
-	bool line_numbers;
-	bool line_numbers_zero_padded;
 	bool line_length_guidelines;
 	int line_length_guideline_soft_col;
 	int line_length_guideline_hard_col;
-	bool draw_bookmark_gutter;
-	bool draw_breakpoint_gutter;
-	int breakpoint_gutter_width;
-	bool draw_fold_gutter;
-	int fold_gutter_width;
 	bool hiding_enabled;
-	bool draw_info_gutter;
-	int info_gutter_width;
 	bool draw_minimap;
 	int minimap_width;
 	Point2 minimap_char_size;
@@ -385,11 +347,8 @@ private:
 
 	bool context_menu_enabled;
 	bool shortcut_keys_enabled;
-
 	bool virtual_keyboard_enabled = true;
 
-	int executing_line;
-
 	void _generate_context_menu();
 
 	int get_visible_rows() const;
@@ -447,8 +406,6 @@ private:
 	Size2 get_minimum_size() const override;
 	int _get_control_height() const;
 
-	int get_row_height() const;
-
 	void _reset_caret_blink_timer();
 	void _toggle_draw_caret();
 
@@ -480,6 +437,44 @@ private:
 	int _calculate_spaces_till_next_right_indent(int column);
 
 protected:
+	struct Cache {
+		Ref<Texture2D> tab_icon;
+		Ref<Texture2D> space_icon;
+		Ref<Texture2D> folded_eol_icon;
+		Ref<StyleBox> style_normal;
+		Ref<StyleBox> style_focus;
+		Ref<StyleBox> style_readonly;
+		Ref<Font> font;
+		Color completion_background_color;
+		Color completion_selected_color;
+		Color completion_existing_color;
+		Color completion_font_color;
+		Color caret_color;
+		Color caret_background_color;
+		Color font_color;
+		Color font_color_selected;
+		Color font_color_readonly;
+		Color selection_color;
+		Color mark_color;
+		Color code_folding_color;
+		Color current_line_color;
+		Color line_length_guideline_color;
+		Color brace_mismatch_color;
+		Color word_highlighted_color;
+		Color search_result_color;
+		Color search_result_border_color;
+		Color background_color;
+
+		int row_height;
+		int line_spacing;
+		int minimap_width;
+		Cache() {
+			row_height = 0;
+			line_spacing = 0;
+			minimap_width = 0;
+		}
+	} cache;
+
 	virtual String get_tooltip(const Point2 &p_pos) const override;
 
 	void _insert_text(int p_line, int p_char, const String &p_text, int *r_end_line = nullptr, int *r_end_char = nullptr);
@@ -494,9 +489,51 @@ protected:
 	static void _bind_methods();
 
 public:
+	/* Syntax Highlighting. */
 	Ref<SyntaxHighlighter> get_syntax_highlighter();
 	void set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter);
 
+	/* Gutters. */
+	void add_gutter(int p_at = -1);
+	void remove_gutter(int p_gutter);
+	int get_gutter_count() const;
+
+	void set_gutter_name(int p_gutter, const String &p_name);
+	String get_gutter_name(int p_gutter) const;
+
+	void set_gutter_type(int p_gutter, GutterType p_type);
+	GutterType get_gutter_type(int p_gutter) const;
+
+	void set_gutter_width(int p_gutter, int p_width);
+	int get_gutter_width(int p_gutter) const;
+
+	void set_gutter_draw(int p_gutter, bool p_draw);
+	bool is_gutter_drawn(int p_gutter) const;
+
+	void set_gutter_clickable(int p_gutter, bool p_clickable);
+	bool is_gutter_clickable(int p_gutter) const;
+
+	void set_gutter_overwritable(int p_gutter, bool p_overwritable);
+	bool is_gutter_overwritable(int p_gutter) const;
+
+	void set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback);
+
+	// Line gutters.
+	void set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata);
+	Variant get_line_gutter_metadata(int p_line, int p_gutter) const;
+
+	void set_line_gutter_text(int p_line, int p_gutter, const String &p_text);
+	String get_line_gutter_text(int p_line, int p_gutter) const;
+
+	void set_line_gutter_icon(int p_line, int p_gutter, Ref<Texture2D> p_icon);
+	Ref<Texture2D> get_line_gutter_icon(int p_line, int p_gutter) const;
+
+	void set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color);
+	Color get_line_gutter_item_color(int p_line, int p_gutter);
+
+	void set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable);
+	bool is_line_gutter_clickable(int p_line, int p_gutter) const;
+
 	enum MenuItems {
 		MENU_CUT,
 		MENU_COPY,
@@ -534,22 +571,6 @@ public:
 	void insert_at(const String &p_text, int at);
 	int get_line_count() const;
 	void set_line_as_marked(int p_line, bool p_marked);
-	void set_line_as_bookmark(int p_line, bool p_bookmark);
-	bool is_line_set_as_bookmark(int p_line) const;
-	void get_bookmarks(List<int> *p_bookmarks) const;
-	Array get_bookmarks_array() const;
-	void set_line_as_breakpoint(int p_line, bool p_breakpoint);
-	bool is_line_set_as_breakpoint(int p_line) const;
-	void set_executing_line(int p_line);
-	void clear_executing_line();
-	void set_line_as_safe(int p_line, bool p_safe);
-	bool is_line_set_as_safe(int p_line) const;
-	void get_breakpoints(List<int> *p_breakpoints) const;
-	Array get_breakpoints_array() const;
-	void remove_breakpoints();
-
-	void set_line_info_icon(int p_line, Ref<Texture2D> p_icon, String p_info = "");
-	void clear_info_icons();
 
 	void set_line_as_hidden(int p_line, bool p_hidden);
 	bool is_line_hidden(int p_line) const;
@@ -569,6 +590,7 @@ public:
 	String get_text();
 	String get_line(int line) const;
 	void set_line(int line, String new_text);
+	int get_row_height() const;
 	void backspace_at_cursor();
 
 	void indent_left();
@@ -690,39 +712,13 @@ public:
 
 	void menu_option(int p_option);
 
-	void set_show_line_numbers(bool p_show);
-	bool is_show_line_numbers_enabled() const;
-
 	void set_highlight_current_line(bool p_enabled);
 	bool is_highlight_current_line_enabled() const;
 
-	void set_line_numbers_zero_padded(bool p_zero_padded);
-
 	void set_show_line_length_guidelines(bool p_show);
 	void set_line_length_guideline_soft_column(int p_column);
 	void set_line_length_guideline_hard_column(int p_column);
 
-	void set_bookmark_gutter_enabled(bool p_draw);
-	bool is_bookmark_gutter_enabled() const;
-
-	void set_breakpoint_gutter_enabled(bool p_draw);
-	bool is_breakpoint_gutter_enabled() const;
-
-	void set_breakpoint_gutter_width(int p_gutter_width);
-	int get_breakpoint_gutter_width() const;
-
-	void set_draw_fold_gutter(bool p_draw);
-	bool is_drawing_fold_gutter() const;
-
-	void set_fold_gutter_width(int p_gutter_width);
-	int get_fold_gutter_width() const;
-
-	void set_draw_info_gutter(bool p_draw);
-	bool is_drawing_info_gutter() const;
-
-	void set_info_gutter_width(int p_gutter_width);
-	int get_info_gutter_width() const;
-
 	void set_draw_minimap(bool p_draw);
 	bool is_drawing_minimap() const;
 
@@ -764,6 +760,7 @@ public:
 	~TextEdit();
 };
 
+VARIANT_ENUM_CAST(TextEdit::GutterType);
 VARIANT_ENUM_CAST(TextEdit::MenuItems);
 VARIANT_ENUM_CAST(TextEdit::SearchFlags);
 

+ 2 - 0
scene/register_scene_types.cpp

@@ -81,6 +81,7 @@
 #include "scene/gui/center_container.h"
 #include "scene/gui/check_box.h"
 #include "scene/gui/check_button.h"
+#include "scene/gui/code_edit.h"
 #include "scene/gui/color_picker.h"
 #include "scene/gui/color_rect.h"
 #include "scene/gui/control.h"
@@ -351,6 +352,7 @@ void register_scene_types() {
 	ClassDB::register_class<Tree>();
 
 	ClassDB::register_class<TextEdit>();
+	ClassDB::register_class<CodeEdit>();
 	ClassDB::register_class<SyntaxHighlighter>();
 	ClassDB::register_class<CodeHighlighter>();
 

BIN
scene/resources/default_theme/bookmark.png


+ 44 - 7
scene/resources/default_theme/default_theme.cpp

@@ -382,8 +382,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
 
 	theme->set_icon("tab", "TextEdit", make_icon(tab_png));
 	theme->set_icon("space", "TextEdit", make_icon(space_png));
-	theme->set_icon("folded", "TextEdit", make_icon(arrow_right_png));
-	theme->set_icon("fold", "TextEdit", make_icon(arrow_down_png));
 
 	theme->set_font("font", "TextEdit", Ref<Font>());
 
@@ -398,16 +396,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
 	theme->set_color("font_color_readonly", "TextEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
 	theme->set_color("selection_color", "TextEdit", font_color_selection);
 	theme->set_color("mark_color", "TextEdit", Color(1.0, 0.4, 0.4, 0.4));
-	theme->set_color("bookmark_color", "TextEdit", Color(0.08, 0.49, 0.98));
-	theme->set_color("breakpoint_color", "TextEdit", Color(0.8, 0.8, 0.4, 0.2));
-	theme->set_color("executing_line_color", "TextEdit", Color(0.2, 0.8, 0.2, 0.4));
 	theme->set_color("code_folding_color", "TextEdit", Color(0.8, 0.8, 0.8, 0.8));
 	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("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("word_highlighted_color", "TextEdit", Color(0.8, 0.9, 0.9, 0.15));
 
 	theme->set_constant("completion_lines", "TextEdit", 7);
@@ -415,6 +408,50 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
 	theme->set_constant("completion_scroll_width", "TextEdit", 3);
 	theme->set_constant("line_spacing", "TextEdit", 4 * scale);
 
+	// CodeEdit
+	theme->set_stylebox("normal", "CodeEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
+	theme->set_stylebox("focus", "CodeEdit", focus);
+	theme->set_stylebox("read_only", "CodeEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4, 0, 0, 0, 0));
+	theme->set_stylebox("completion", "CodeEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0));
+
+	theme->set_icon("tab", "CodeEdit", make_icon(tab_png));
+	theme->set_icon("space", "CodeEdit", make_icon(space_png));
+	theme->set_icon("breakpoint", "CodeEdit", make_icon(graph_port_png));
+	theme->set_icon("bookmark", "CodeEdit", make_icon(bookmark_png));
+	theme->set_icon("executing_line", "CodeEdit", make_icon(arrow_right_png));
+	theme->set_icon("can_fold", "CodeEdit", make_icon(arrow_down_png));
+	theme->set_icon("folded", "CodeEdit", make_icon(arrow_right_png));
+
+	theme->set_font("font", "CodeEdit", Ref<Font>());
+
+	theme->set_color("background_color", "CodeEdit", Color(0, 0, 0, 0));
+	theme->set_color("completion_background_color", "CodeEdit", Color(0.17, 0.16, 0.2));
+	theme->set_color("completion_selected_color", "CodeEdit", Color(0.26, 0.26, 0.27));
+	theme->set_color("completion_existing_color", "CodeEdit", Color(0.87, 0.87, 0.87, 0.13));
+	theme->set_color("completion_scroll_color", "CodeEdit", control_font_color_pressed);
+	theme->set_color("completion_font_color", "CodeEdit", Color(0.67, 0.67, 0.67));
+	theme->set_color("font_color", "CodeEdit", control_font_color);
+	theme->set_color("font_color_selected", "CodeEdit", Color(0, 0, 0));
+	theme->set_color("font_color_readonly", "CodeEdit", Color(control_font_color.r, control_font_color.g, control_font_color.b, 0.5f));
+	theme->set_color("selection_color", "CodeEdit", font_color_selection);
+	theme->set_color("mark_color", "CodeEdit", Color(1.0, 0.4, 0.4, 0.4));
+	theme->set_color("bookmark_color", "CodeEdit", Color(0.5, 0.64, 1, 0.8));
+	theme->set_color("breakpoint_color", "CodeEdit", Color(0.9, 0.29, 0.3));
+	theme->set_color("executing_line_color", "CodeEdit", Color(0.98, 0.89, 0.27));
+	theme->set_color("code_folding_color", "CodeEdit", Color(0.8, 0.8, 0.8, 0.8));
+	theme->set_color("current_line_color", "CodeEdit", Color(0.25, 0.25, 0.26, 0.8));
+	theme->set_color("caret_color", "CodeEdit", control_font_color);
+	theme->set_color("caret_background_color", "CodeEdit", Color(0, 0, 0));
+	theme->set_color("brace_mismatch_color", "CodeEdit", Color(1, 0.2, 0.2));
+	theme->set_color("line_number_color", "CodeEdit", Color(0.67, 0.67, 0.67, 0.4));
+	theme->set_color("safe_line_number_color", "CodeEdit", Color(0.67, 0.78, 0.67, 0.6));
+	theme->set_color("word_highlighted_color", "CodeEdit", Color(0.8, 0.9, 0.9, 0.15));
+
+	theme->set_constant("completion_lines", "CodeEdit", 7);
+	theme->set_constant("completion_max_width", "CodeEdit", 50);
+	theme->set_constant("completion_scroll_width", "CodeEdit", 3);
+	theme->set_constant("line_spacing", "CodeEdit", 4 * scale);
+
 	Ref<Texture2D> empty_icon = memnew(ImageTexture);
 
 	// HScrollBar

+ 4 - 0
scene/resources/default_theme/theme_data.h

@@ -10,6 +10,10 @@ static const unsigned char arrow_right_png[] = {
 	0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc, 0x8, 0x4, 0x0, 0x0, 0x0, 0xfc, 0x7c, 0x94, 0x6c, 0x0, 0x0, 0x0, 0x2e, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x17, 0x3c, 0xf8, 0xf, 0x82, 0xf7, 0x13, 0x70, 0x48, 0x3c, 0xf8, 0xf2, 0x50, 0x1b, 0x43, 0x2, 0xa, 0xaf, 0xbe, 0xe0, 0xc6, 0x2e, 0xf1, 0xff, 0xe1, 0x7c, 0x12, 0x24, 0x10, 0x46, 0x11, 0xb6, 0x1c, 0xe1, 0x5c, 0xa, 0x0, 0x0, 0xe0, 0x14, 0x48, 0xb1, 0x3d, 0x1b, 0x7a, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
 };
 
+static const unsigned char bookmark_png[] = {
+	0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, 0x61, 0x0, 0x0, 0x0, 0x4, 0x73, 0x42, 0x49, 0x54, 0x8, 0x8, 0x8, 0x8, 0x7c, 0x8, 0x64, 0x88, 0x0, 0x0, 0x0, 0x57, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8d, 0xed, 0x93, 0x31, 0xa, 0xc0, 0x30, 0xc, 0x3, 0xa5, 0xd0, 0xff, 0x7f, 0x59, 0x1d, 0x8a, 0x42, 0x8, 0x9, 0x95, 0xc9, 0xd2, 0xa1, 0x9a, 0x8c, 0xf1, 0xdd, 0x62, 0x1b, 0x38, 0xc, 0x87, 0x5a, 0x5, 0xae, 0x79, 0xde, 0x2, 0x1, 0x80, 0x94, 0x39, 0x48, 0x76, 0x49, 0x17, 0xa4, 0xf0, 0x24, 0x61, 0x2b, 0x51, 0x8b, 0xfc, 0x82, 0xcf, 0xb, 0x48, 0x7a, 0xdf, 0x75, 0x81, 0xf, 0xe5, 0x29, 0xf7, 0x92, 0x6b, 0x3, 0x1a, 0x1e, 0xda, 0x7c, 0x3d, 0x77, 0x21, 0x7b, 0xa8, 0x74, 0x2e, 0xcb, 0xd, 0xc8, 0x75, 0x13, 0x28, 0x9, 0xed, 0xc2, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
 static const unsigned char button_disabled_png[] = {
 	0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0xc7, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xd0, 0x81, 0x66, 0x43, 0x31, 0x14, 0x87, 0xf1, 0xf, 0x5, 0x17, 0xb8, 0x28, 0x2e, 0x8, 0x71, 0xf3, 0x6, 0x19, 0xb6, 0xb9, 0xcb, 0xac, 0x95, 0xa4, 0xb7, 0xad, 0x6a, 0xd5, 0x68, 0x5f, 0xe4, 0x3e, 0x76, 0x1e, 0xe1, 0xbf, 0x21, 0xa6, 0xab, 0xf8, 0x1, 0x7c, 0x9c, 0x73, 0xe, 0xac, 0xe8, 0xe8, 0x19, 0x30, 0x58, 0xc6, 0xca, 0x62, 0x18, 0xe8, 0xe9, 0x58, 0x41, 0xc7, 0x1a, 0x87, 0x27, 0x10, 0x49, 0xe4, 0x5f, 0x89, 0x48, 0xc0, 0xe3, 0x58, 0xd3, 0x41, 0x8f, 0xb, 0xcb, 0xbd, 0x7c, 0xeb, 0xbf, 0x7b, 0x9, 0xb, 0x8e, 0x1e, 0x6, 0xfc, 0xad, 0x64, 0x6d, 0xb5, 0x79, 0xb0, 0x55, 0xd6, 0xad, 0xe0, 0x19, 0xc0, 0x10, 0xae, 0xda, 0x34, 0x5c, 0x45, 0xc0, 0x80, 0x25, 0x5e, 0xf4, 0xd5, 0x70, 0x11, 0x11, 0xb, 0x23, 0xe9, 0xac, 0xcf, 0x86, 0xb3, 0x48, 0x8c, 0x30, 0x92, 0x4f, 0xa, 0xd, 0x27, 0x91, 0x6b, 0x70, 0xd4, 0x47, 0xc3, 0xf1, 0x2f, 0x48, 0x7, 0x4d, 0xd, 0x87, 0x3a, 0xc2, 0x12, 0x67, 0xbd, 0x37, 0xcc, 0x75, 0x49, 0x43, 0xd8, 0xe9, 0xad, 0x61, 0x57, 0xcf, 0x1c, 0xf0, 0xfb, 0x32, 0xe9, 0xf5, 0xc9, 0xa4, 0x7d, 0x7d, 0x54, 0x8f, 0x7b, 0x59, 0xe6, 0x92, 0x14, 0x1f, 0x24, 0xcd, 0x3f, 0x7b, 0x6b, 0xa, 0xe, 0x6a, 0x82, 0x91, 0x45, 0x30, 0xba, 0x1, 0x4a, 0x51, 0xc4, 0x35, 0x1f, 0xe5, 0xa1, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
 };

+ 4 - 4
scene/resources/syntax_highlighter.cpp

@@ -53,13 +53,13 @@ Dictionary SyntaxHighlighter::get_line_syntax_highlighting(int p_line) {
 	return color_map;
 }
 
-void SyntaxHighlighter::_line_edited_from(int p_line) {
+void SyntaxHighlighter::_lines_edited_from(int p_from_line, int p_to_line) {
 	if (highlighting_cache.size() < 1) {
 		return;
 	}
 
 	int cache_size = highlighting_cache.back()->key();
-	for (int i = p_line - 1; i <= cache_size; i++) {
+	for (int i = MIN(p_from_line, p_to_line) - 1; i <= cache_size; i++) {
 		if (highlighting_cache.has(i)) {
 			highlighting_cache.erase(i);
 		}
@@ -93,7 +93,7 @@ void SyntaxHighlighter::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->disconnect("lines_edited_from", callable_mp(this, &SyntaxHighlighter::_lines_edited_from));
 	}
 
 	text_edit = p_text_edit;
@@ -101,7 +101,7 @@ void SyntaxHighlighter::set_text_edit(TextEdit *p_text_edit) {
 		return;
 	}
 	text_edit_instance_id = text_edit->get_instance_id();
-	text_edit->connect("line_edited_from", callable_mp(this, &SyntaxHighlighter::_line_edited_from));
+	text_edit->connect("lines_edited_from", callable_mp(this, &SyntaxHighlighter::_lines_edited_from));
 	update_cache();
 }
 

+ 1 - 1
scene/resources/syntax_highlighter.h

@@ -40,7 +40,7 @@ class SyntaxHighlighter : public Resource {
 
 private:
 	Map<int, Dictionary> highlighting_cache;
-	void _line_edited_from(int p_line);
+	void _lines_edited_from(int p_from_line, int p_to_line);
 
 protected:
 	ObjectID text_edit_instance_id; // For validity check

Some files were not shown because too many files changed in this diff