summaryrefslogtreecommitdiffstats
path: root/scene/gui/code_edit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/code_edit.cpp')
-rw-r--r--scene/gui/code_edit.cpp349
1 files changed, 274 insertions, 75 deletions
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 443a639ff2..d35d35d36d 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -34,6 +34,7 @@
#include "core/os/keyboard.h"
#include "core/string/string_builder.h"
#include "core/string/ustring.h"
+#include "scene/theme/theme_db.h"
void CodeEdit::_notification(int p_what) {
switch (p_what) {
@@ -228,55 +229,6 @@ void CodeEdit::_notification(int p_what) {
}
}
-void CodeEdit::_update_theme_item_cache() {
- TextEdit::_update_theme_item_cache();
-
- /* Gutters */
- theme_cache.code_folding_color = get_theme_color(SNAME("code_folding_color"));
- theme_cache.can_fold_icon = get_theme_icon(SNAME("can_fold"));
- theme_cache.folded_icon = get_theme_icon(SNAME("folded"));
- theme_cache.folded_eol_icon = get_theme_icon(SNAME("folded_eol_icon"));
-
- theme_cache.breakpoint_color = get_theme_color(SNAME("breakpoint_color"));
- theme_cache.breakpoint_icon = get_theme_icon(SNAME("breakpoint"));
-
- theme_cache.bookmark_color = get_theme_color(SNAME("bookmark_color"));
- theme_cache.bookmark_icon = get_theme_icon(SNAME("bookmark"));
-
- theme_cache.executing_line_color = get_theme_color(SNAME("executing_line_color"));
- theme_cache.executing_line_icon = get_theme_icon(SNAME("executing_line"));
-
- theme_cache.line_number_color = get_theme_color(SNAME("line_number_color"));
-
- /* Code Completion */
- theme_cache.code_completion_style = get_theme_stylebox(SNAME("completion"));
- theme_cache.code_completion_icon_separation = get_theme_constant(SNAME("h_separation"), SNAME("ItemList"));
-
- theme_cache.code_completion_max_width = get_theme_constant(SNAME("completion_max_width"));
- theme_cache.code_completion_max_lines = get_theme_constant(SNAME("completion_lines"));
- theme_cache.code_completion_scroll_width = get_theme_constant(SNAME("completion_scroll_width"));
- theme_cache.code_completion_scroll_color = get_theme_color(SNAME("completion_scroll_color"));
- theme_cache.code_completion_scroll_hovered_color = get_theme_color(SNAME("completion_scroll_hovered_color"));
- theme_cache.code_completion_background_color = get_theme_color(SNAME("completion_background_color"));
- theme_cache.code_completion_selected_color = get_theme_color(SNAME("completion_selected_color"));
- theme_cache.code_completion_existing_color = get_theme_color(SNAME("completion_existing_color"));
-
- /* Code hint */
- theme_cache.code_hint_style = get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel"));
- theme_cache.code_hint_color = get_theme_color(SNAME("font_color"), SNAME("TooltipLabel"));
-
- /* Line length guideline */
- theme_cache.line_length_guideline_color = get_theme_color(SNAME("line_length_guideline_color"));
-
- /* Other visuals */
- theme_cache.style_normal = get_theme_stylebox(SNAME("normal"));
-
- theme_cache.font = get_theme_font(SNAME("font"));
- theme_cache.font_size = get_theme_font_size(SNAME("font_size"));
-
- theme_cache.line_spacing = get_theme_constant(SNAME("line_spacing"));
-}
-
void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
Ref<InputEventMouseButton> mb = p_gui_input;
if (mb.is_valid()) {
@@ -481,7 +433,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
// Allow unicode handling if:
// No modifiers are pressed (except Shift and CapsLock)
- bool allow_unicode_handling = !(k->is_command_or_control_pressed() || k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed());
+ bool allow_unicode_handling = !(k->is_ctrl_pressed() || k->is_alt_pressed() || k->is_meta_pressed());
/* AUTO-COMPLETE */
if (code_completion_enabled && k->is_action("ui_text_completion_query", true)) {
@@ -1571,7 +1523,19 @@ void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_regi
p_region.position += Point2(horizontal_padding, vertical_padding);
p_region.size -= Point2(horizontal_padding, vertical_padding) * 2;
- if (can_fold_line(p_line)) {
+ bool can_fold = can_fold_line(p_line);
+
+ if (is_line_code_region_start(p_line)) {
+ Color region_icon_color = theme_cache.folded_code_region_color;
+ region_icon_color.a = MAX(region_icon_color.a, 0.4f);
+ if (can_fold) {
+ theme_cache.can_fold_code_region_icon->draw_rect(get_canvas_item(), p_region, false, region_icon_color);
+ } else {
+ theme_cache.folded_code_region_icon->draw_rect(get_canvas_item(), p_region, false, region_icon_color);
+ }
+ return;
+ }
+ if (can_fold) {
theme_cache.can_fold_icon->draw_rect(get_canvas_item(), p_region, false, theme_cache.code_folding_color);
return;
}
@@ -1602,6 +1566,27 @@ bool CodeEdit::can_fold_line(int p_line) const {
return false;
}
+ // Check for code region.
+ if (is_line_code_region_end(p_line)) {
+ return false;
+ }
+ if (is_line_code_region_start(p_line)) {
+ int region_level = 0;
+ // Check if there is a valid end region tag.
+ for (int next_line = p_line + 1; next_line < get_line_count(); next_line++) {
+ if (is_line_code_region_end(next_line)) {
+ region_level -= 1;
+ if (region_level == -1) {
+ return true;
+ }
+ }
+ if (is_line_code_region_start(next_line)) {
+ region_level += 1;
+ }
+ }
+ return false;
+ }
+
/* Check for full multiline line or block strings / comments. */
int in_comment = is_in_comment(p_line);
int in_string = (in_comment == -1) ? is_in_string(p_line) : -1;
@@ -1610,13 +1595,13 @@ bool CodeEdit::can_fold_line(int p_line) const {
return false;
}
- int delimter_end_line = get_delimiter_end_position(p_line, get_line(p_line).size() - 1).y;
+ int delimiter_end_line = get_delimiter_end_position(p_line, get_line(p_line).size() - 1).y;
/* No end line, therefore we have a multiline region over the rest of the file. */
- if (delimter_end_line == -1) {
+ if (delimiter_end_line == -1) {
return true;
}
/* End line is the same therefore we have a block. */
- if (delimter_end_line == p_line) {
+ if (delimiter_end_line == p_line) {
/* Check we are the start of the block. */
if (p_line - 1 >= 0) {
if ((in_string != -1 && is_in_string(p_line - 1) != -1) || (in_comment != -1 && is_in_comment(p_line - 1) != -1)) {
@@ -1626,7 +1611,7 @@ bool CodeEdit::can_fold_line(int p_line) const {
/* Check it continues for at least one line. */
return ((in_string != -1 && is_in_string(p_line + 1) != -1) || (in_comment != -1 && is_in_comment(p_line + 1) != -1));
}
- return ((in_string != -1 && is_in_string(delimter_end_line) != -1) || (in_comment != -1 && is_in_comment(delimter_end_line) != -1));
+ return ((in_string != -1 && is_in_string(delimiter_end_line) != -1) || (in_comment != -1 && is_in_comment(delimiter_end_line) != -1));
}
/* Otherwise check indent levels. */
@@ -1650,31 +1635,51 @@ void CodeEdit::fold_line(int p_line) {
const int line_count = get_line_count() - 1;
int end_line = line_count;
- int in_comment = is_in_comment(p_line);
- int in_string = (in_comment == -1) ? is_in_string(p_line) : -1;
- if (in_string != -1 || in_comment != -1) {
- end_line = get_delimiter_end_position(p_line, get_line(p_line).size() - 1).y;
- /* End line is the same therefore we have a block of single line delimiters. */
- if (end_line == p_line) {
- for (int i = p_line + 1; i <= line_count; i++) {
- if ((in_string != -1 && is_in_string(i) == -1) || (in_comment != -1 && is_in_comment(i) == -1)) {
+ // Fold code region.
+ if (is_line_code_region_start(p_line)) {
+ int region_level = 0;
+ for (int endregion_line = p_line + 1; endregion_line < get_line_count(); endregion_line++) {
+ if (is_line_code_region_start(endregion_line)) {
+ region_level += 1;
+ }
+ if (is_line_code_region_end(endregion_line)) {
+ region_level -= 1;
+ if (region_level == -1) {
+ end_line = endregion_line;
break;
}
- end_line = i;
}
}
- } else {
- int start_indent = get_indent_level(p_line);
- for (int i = p_line + 1; i <= line_count; i++) {
- if (get_line(i).strip_edges().size() == 0) {
- continue;
- }
- if (get_indent_level(i) > start_indent) {
- end_line = i;
- continue;
+ set_line_background_color(p_line, theme_cache.folded_code_region_color);
+ }
+
+ int in_comment = is_in_comment(p_line);
+ int in_string = (in_comment == -1) ? is_in_string(p_line) : -1;
+ if (!is_line_code_region_start(p_line)) {
+ if (in_string != -1 || in_comment != -1) {
+ end_line = get_delimiter_end_position(p_line, get_line(p_line).size() - 1).y;
+ // End line is the same therefore we have a block of single line delimiters.
+ if (end_line == p_line) {
+ for (int i = p_line + 1; i <= line_count; i++) {
+ if ((in_string != -1 && is_in_string(i) == -1) || (in_comment != -1 && is_in_comment(i) == -1)) {
+ break;
+ }
+ end_line = i;
+ }
}
- if (is_in_string(i) == -1 && is_in_comment(i) == -1) {
- break;
+ } else {
+ int start_indent = get_indent_level(p_line);
+ for (int i = p_line + 1; i <= line_count; i++) {
+ if (get_line(i).strip_edges().size() == 0) {
+ continue;
+ }
+ if (get_indent_level(i) > start_indent) {
+ end_line = i;
+ continue;
+ }
+ if (is_in_string(i) == -1 && is_in_comment(i) == -1) {
+ break;
+ }
}
}
}
@@ -1725,6 +1730,9 @@ void CodeEdit::unfold_line(int p_line) {
break;
}
_set_line_as_hidden(i, false);
+ if (is_line_code_region_start(i - 1)) {
+ set_line_background_color(i - 1, Color(0.0, 0.0, 0.0, 0.0));
+ }
}
queue_redraw();
}
@@ -1764,6 +1772,95 @@ TypedArray<int> CodeEdit::get_folded_lines() const {
return folded_lines;
}
+/* Code region */
+void CodeEdit::create_code_region() {
+ // Abort if there is no selected text.
+ if (!has_selection()) {
+ return;
+ }
+ // Check that region tag find a comment delimiter and is valid.
+ if (code_region_start_string.is_empty()) {
+ WARN_PRINT_ONCE("Cannot create code region without any one line comment delimiters");
+ return;
+ }
+ begin_complex_operation();
+ // Merge selections if selection starts on the same line the previous one ends.
+ Vector<int> caret_edit_order = get_caret_index_edit_order();
+ Vector<int> carets_to_remove;
+ for (int i = 1; i < caret_edit_order.size(); i++) {
+ int current_caret = caret_edit_order[i - 1];
+ int next_caret = caret_edit_order[i];
+ if (get_selection_from_line(current_caret) == get_selection_to_line(next_caret)) {
+ select(get_selection_from_line(next_caret), get_selection_from_column(next_caret), get_selection_to_line(current_caret), get_selection_to_column(current_caret), next_caret);
+ carets_to_remove.append(current_caret);
+ }
+ }
+ // Sort and remove backwards to preserve indices.
+ carets_to_remove.sort();
+ for (int i = carets_to_remove.size() - 1; i >= 0; i--) {
+ remove_caret(carets_to_remove[i]);
+ }
+
+ // Adding start and end region tags.
+ int first_region_start = -1;
+ for (int caret_idx : get_caret_index_edit_order()) {
+ if (!has_selection(caret_idx)) {
+ continue;
+ }
+ int from_line = get_selection_from_line(caret_idx);
+ if (first_region_start == -1 || from_line < first_region_start) {
+ first_region_start = from_line;
+ }
+ int to_line = get_selection_to_line(caret_idx);
+ set_line(to_line, get_line(to_line) + "\n" + code_region_end_string);
+ insert_line_at(from_line, code_region_start_string + " " + RTR("New Code Region"));
+ fold_line(from_line);
+ }
+
+ // Select name of the first region to allow quick edit.
+ remove_secondary_carets();
+ set_caret_line(first_region_start);
+ int tag_length = code_region_start_string.length() + RTR("New Code Region").length() + 1;
+ set_caret_column(tag_length);
+ select(first_region_start, code_region_start_string.length() + 1, first_region_start, tag_length);
+
+ end_complex_operation();
+ queue_redraw();
+}
+
+String CodeEdit::get_code_region_start_tag() const {
+ return code_region_start_tag;
+}
+
+String CodeEdit::get_code_region_end_tag() const {
+ return code_region_end_tag;
+}
+
+void CodeEdit::set_code_region_tags(const String &p_start, const String &p_end) {
+ ERR_FAIL_COND_MSG(p_start == p_end, "Starting and ending region tags cannot be identical.");
+ ERR_FAIL_COND_MSG(p_start.is_empty(), "Starting region tag cannot be empty.");
+ ERR_FAIL_COND_MSG(p_end.is_empty(), "Ending region tag cannot be empty.");
+ code_region_start_tag = p_start;
+ code_region_end_tag = p_end;
+ _update_code_region_tags();
+}
+
+bool CodeEdit::is_line_code_region_start(int p_line) const {
+ ERR_FAIL_INDEX_V(p_line, get_line_count(), false);
+ if (code_region_start_string.is_empty()) {
+ return false;
+ }
+ return get_line(p_line).strip_edges().begins_with(code_region_start_string);
+}
+
+bool CodeEdit::is_line_code_region_end(int p_line) const {
+ ERR_FAIL_INDEX_V(p_line, get_line_count(), false);
+ if (code_region_start_string.is_empty()) {
+ return false;
+ }
+ return get_line(p_line).strip_edges().begins_with(code_region_end_string);
+}
+
/* Delimiters */
// Strings
void CodeEdit::add_string_delimiter(const String &p_start_key, const String &p_end_key, bool p_line_only) {
@@ -2302,6 +2399,19 @@ void CodeEdit::set_symbol_lookup_word_as_valid(bool p_valid) {
}
}
+/* Visual */
+Color CodeEdit::_get_brace_mismatch_color() const {
+ return theme_cache.brace_mismatch_color;
+}
+
+Color CodeEdit::_get_code_folding_color() const {
+ return theme_cache.code_folding_color;
+}
+
+Ref<Texture2D> CodeEdit::_get_folded_eol_icon() const {
+ return theme_cache.folded_eol_icon;
+}
+
void CodeEdit::_bind_methods() {
/* Indent management */
ClassDB::bind_method(D_METHOD("set_indent_size", "size"), &CodeEdit::set_indent_size);
@@ -2392,6 +2502,14 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_line_folded", "line"), &CodeEdit::is_line_folded);
ClassDB::bind_method(D_METHOD("get_folded_lines"), &CodeEdit::get_folded_lines);
+ /* Code region */
+ ClassDB::bind_method(D_METHOD("create_code_region"), &CodeEdit::create_code_region);
+ ClassDB::bind_method(D_METHOD("get_code_region_start_tag"), &CodeEdit::get_code_region_start_tag);
+ ClassDB::bind_method(D_METHOD("get_code_region_end_tag"), &CodeEdit::get_code_region_end_tag);
+ ClassDB::bind_method(D_METHOD("set_code_region_tags", "start", "end"), &CodeEdit::set_code_region_tags, DEFVAL("region"), DEFVAL("endregion"));
+ ClassDB::bind_method(D_METHOD("is_line_code_region_start", "line"), &CodeEdit::is_line_code_region_start);
+ ClassDB::bind_method(D_METHOD("is_line_code_region_end", "line"), &CodeEdit::is_line_code_region_end);
+
/* Delimiters */
// Strings
ClassDB::bind_method(D_METHOD("add_string_delimiter", "start_key", "end_key", "line_only"), &CodeEdit::add_string_delimiter, DEFVAL(false));
@@ -2527,6 +2645,57 @@ void CodeEdit::_bind_methods() {
/* Symbol lookup */
ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::INT, "column")));
ADD_SIGNAL(MethodInfo("symbol_validate", PropertyInfo(Variant::STRING, "symbol")));
+
+ /* Theme items */
+ /* Gutters */
+ BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, code_folding_color);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, folded_code_region_color);
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, can_fold_icon, "can_fold");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, folded_icon, "folded");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, can_fold_code_region_icon, "can_fold_code_region");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, folded_code_region_icon, "folded_code_region");
+ BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CodeEdit, folded_eol_icon);
+
+ BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, breakpoint_color);
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, breakpoint_icon, "breakpoint");
+
+ BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, bookmark_color);
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, bookmark_icon, "bookmark");
+
+ BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, executing_line_color);
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, executing_line_icon, "executing_line");
+
+ BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, line_number_color);
+
+ /* Code Completion */
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, CodeEdit, code_completion_style, "completion");
+ BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_CONSTANT, CodeEdit, code_completion_icon_separation, "h_separation", "ItemList");
+
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, CodeEdit, code_completion_max_width, "completion_max_width");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, CodeEdit, code_completion_max_lines, "completion_lines");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, CodeEdit, code_completion_scroll_width, "completion_scroll_width");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, CodeEdit, code_completion_scroll_color, "completion_scroll_color");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, CodeEdit, code_completion_scroll_hovered_color, "completion_scroll_hovered_color");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, CodeEdit, code_completion_background_color, "completion_background_color");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, CodeEdit, code_completion_selected_color, "completion_selected_color");
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, CodeEdit, code_completion_existing_color, "completion_existing_color");
+
+ /* Code hint */
+ BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, CodeEdit, code_hint_style, "panel", "TooltipPanel");
+ BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, CodeEdit, code_hint_color, "font_color", "TooltipLabel");
+
+ /* Line length guideline */
+ BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, line_length_guideline_color);
+
+ /* Other visuals */
+ BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, CodeEdit, style_normal, "normal");
+
+ BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, brace_mismatch_color);
+
+ BIND_THEME_ITEM(Theme::DATA_TYPE_FONT, CodeEdit, font);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, CodeEdit, font_size);
+
+ BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, CodeEdit, line_spacing);
}
/* Auto brace completion */
@@ -2630,6 +2799,27 @@ void CodeEdit::_update_gutter_indexes() {
}
}
+/* Code Region */
+void CodeEdit::_update_code_region_tags() {
+ code_region_start_string = "";
+ code_region_end_string = "";
+
+ if (code_region_start_tag.is_empty() || code_region_end_tag.is_empty()) {
+ return;
+ }
+
+ for (int i = 0; i < delimiters.size(); i++) {
+ if (delimiters[i].type != DelimiterType::TYPE_COMMENT) {
+ continue;
+ }
+ if (delimiters[i].end_key.is_empty() && delimiters[i].line_only == true) {
+ code_region_start_string = delimiters[i].start_key + code_region_start_tag;
+ code_region_end_string = delimiters[i].start_key + code_region_end_tag;
+ return;
+ }
+ }
+}
+
/* Delimiters */
void CodeEdit::_update_delimiter_cache(int p_from_line, int p_to_line) {
if (delimiters.size() == 0) {
@@ -2873,6 +3063,9 @@ void CodeEdit::_add_delimiter(const String &p_start_key, const String &p_end_key
delimiter_cache.clear();
_update_delimiter_cache();
}
+ if (p_type == DelimiterType::TYPE_COMMENT) {
+ _update_code_region_tags();
+ }
}
void CodeEdit::_remove_delimiter(const String &p_start_key, DelimiterType p_type) {
@@ -2890,6 +3083,9 @@ void CodeEdit::_remove_delimiter(const String &p_start_key, DelimiterType p_type
delimiter_cache.clear();
_update_delimiter_cache();
}
+ if (p_type == DelimiterType::TYPE_COMMENT) {
+ _update_code_region_tags();
+ }
break;
}
}
@@ -2933,6 +3129,9 @@ void CodeEdit::_clear_delimiters(DelimiterType p_type) {
if (!setting_delimiters) {
_update_delimiter_cache();
}
+ if (p_type == DelimiterType::TYPE_COMMENT) {
+ _update_code_region_tags();
+ }
}
TypedArray<String> CodeEdit::_get_delimiters(DelimiterType p_type) const {