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.cpp236
1 files changed, 211 insertions, 25 deletions
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 6a5fdc3360..f74d7fb906 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -1523,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;
}
@@ -1554,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;
@@ -1562,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)) {
@@ -1578,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. */
@@ -1602,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;
+ }
}
}
}
@@ -1677,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();
}
@@ -1716,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) {
@@ -2344,6 +2489,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));
@@ -2483,8 +2636,11 @@ void CodeEdit::_bind_methods() {
/* 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);
@@ -2628,6 +2784,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) {
@@ -2871,6 +3048,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) {
@@ -2888,6 +3068,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;
}
}
@@ -2931,6 +3114,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 {