summaryrefslogtreecommitdiff
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.cpp521
1 files changed, 442 insertions, 79 deletions
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 2e6befb706..32922f609d 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -72,13 +72,34 @@ void CodeEdit::_notification(int p_what) {
code_completion_background_color = get_theme_color(SNAME("completion_background_color"));
code_completion_selected_color = get_theme_color(SNAME("completion_selected_color"));
code_completion_existing_color = get_theme_color(SNAME("completion_existing_color"));
+
+ line_length_guideline_color = get_theme_color(SNAME("line_length_guideline_color"));
} break;
case NOTIFICATION_DRAW: {
RID ci = get_canvas_item();
+ const Size2 size = get_size();
const bool caret_visible = is_caret_visible();
const bool rtl = is_layout_rtl();
const int row_height = get_row_height();
+ if (line_length_guideline_columns.size() > 0) {
+ const int xmargin_beg = cache.style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width();
+ const int xmargin_end = size.width - cache.style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0);
+ const int char_size = (int)cache.font->get_char_size('0', 0, cache.font_size).width;
+
+ for (int i = 0; i < line_length_guideline_columns.size(); i++) {
+ const int xoffset = xmargin_beg + char_size * (int)line_length_guideline_columns[i] - get_h_scroll();
+ if (xoffset > xmargin_beg && xoffset < xmargin_end) {
+ Color guideline_color = (i == 0) ? line_length_guideline_color : line_length_guideline_color * Color(1, 1, 1, 0.5);
+ if (rtl) {
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(size.width - xoffset, 0), Point2(size.width - xoffset, size.height), guideline_color);
+ continue;
+ }
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2(xoffset, 0), Point2(xoffset, size.height), guideline_color);
+ }
+ }
+ }
+
bool code_completion_below = false;
if (caret_visible && code_completion_active && code_completion_options.size() > 0) {
Ref<StyleBox> csb = get_theme_stylebox(SNAME("completion"));
@@ -204,8 +225,15 @@ void CodeEdit::_notification(int p_what) {
round_ofs = round_ofs.round();
draw_string(font, round_ofs, line.replace(String::chr(0xFFFF), ""), HALIGN_LEFT, -1, cache.font_size, font_color);
if (end > 0) {
- Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + line_spacing - 1);
- draw_line(b, b + Vector2(end - begin, 0), font_color);
+ // Draw an underline for the currently edited function parameter.
+ const Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + line_spacing);
+ draw_line(b, b + Vector2(end - begin, 0), font_color, 2);
+
+ // Draw a translucent text highlight as well.
+ const Rect2 highlight_rect = Rect2(
+ hint_ofs + sb->get_offset() + Vector2(begin, 0),
+ Vector2(end - begin, font_height));
+ draw_rect(highlight_rect, font_color * Color(1, 1, 1, 0.2));
}
line_spacing += cache.line_spacing;
}
@@ -278,6 +306,39 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
}
+ } else {
+ if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->is_command_pressed() && symbol_lookup_word != String()) {
+ Vector2i mpos = mb->get_position();
+ if (is_layout_rtl()) {
+ mpos.x = get_size().x - mpos.x;
+ }
+ int line, col;
+ _get_mouse_pos(Point2i(mpos.x, mpos.y), line, col);
+
+ emit_signal(SNAME("symbol_lookup"), symbol_lookup_word, line, col);
+ return;
+ }
+ }
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_gui_input;
+ if (mm.is_valid()) {
+ Vector2i mpos = mm->get_position();
+ if (is_layout_rtl()) {
+ mpos.x = get_size().x - mpos.x;
+ }
+
+ if (symbol_lookup_on_click_enabled) {
+ if (mm->is_command_pressed() && mm->get_button_mask() == 0 && !is_dragging_cursor()) {
+ symbol_lookup_new_word = get_word_at_pos(mpos);
+ if (symbol_lookup_new_word != symbol_lookup_word) {
+ emit_signal(SNAME("symbol_validate"), symbol_lookup_new_word);
+ }
+ } else {
+ set_symbol_lookup_word_as_valid(false);
+ }
}
}
@@ -288,6 +349,25 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
+ /* Ctrl + Hover symbols */
+#ifdef OSX_ENABLED
+ if (k->get_keycode() == KEY_META) {
+#else
+ if (k->get_keycode() == KEY_CTRL) {
+#endif
+ if (symbol_lookup_on_click_enabled) {
+ if (k->is_pressed() && !is_dragging_cursor()) {
+ symbol_lookup_new_word = get_word_at_pos(_get_local_mouse_pos());
+ if (symbol_lookup_new_word != symbol_lookup_word) {
+ emit_signal(SNAME("symbol_validate"), symbol_lookup_new_word);
+ }
+ } else {
+ set_symbol_lookup_word_as_valid(false);
+ }
+ }
+ return;
+ }
+
/* If a modifier has been pressed, and nothing else, return. */
if (!k->is_pressed() || k->get_keycode() == KEY_CTRL || k->get_keycode() == KEY_ALT || k->get_keycode() == KEY_SHIFT || k->get_keycode() == KEY_META) {
return;
@@ -437,7 +517,12 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
+/* General overrides */
Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
+ if (symbol_lookup_word != String()) {
+ return CURSOR_POINTING_HAND;
+ }
+
if ((code_completion_active && code_completion_rect.has_point(p_pos)) || (is_readonly() && (!is_selecting_enabled() || get_line_count() == 0))) {
return CURSOR_ARROW;
}
@@ -459,6 +544,58 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
return TextEdit::get_cursor_shape(p_pos);
}
+void CodeEdit::handle_unicode_input(uint32_t p_unicode) {
+ bool had_selection = is_selection_active();
+ if (had_selection) {
+ begin_complex_operation();
+ delete_selection();
+ }
+
+ // Remove the old character if in insert mode and no selection.
+ if (is_insert_mode() && !had_selection) {
+ begin_complex_operation();
+
+ // Make sure we don't try and remove empty space.
+ if (cursor_get_column() < get_line(cursor_get_line()).length()) {
+ _remove_text(cursor_get_line(), cursor_get_column(), cursor_get_line(), cursor_get_column() + 1);
+ }
+ }
+
+ const char32_t chr[2] = { (char32_t)p_unicode, 0 };
+
+ if (auto_brace_completion_enabled) {
+ int cl = cursor_get_line();
+ int cc = cursor_get_column();
+ int caret_move_offset = 1;
+
+ int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
+
+ if (has_string_delimiter(chr) && cc > 0 && _is_char(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
+ insert_text_at_cursor(chr);
+ } else if (cc < get_line(cl).length() && _is_char(get_line(cl)[cc])) {
+ insert_text_at_cursor(chr);
+ } else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
+ caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
+ } else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
+ insert_text_at_cursor(chr);
+ } else {
+ insert_text_at_cursor(chr);
+
+ int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
+ if (pre_brace_pair != -1) {
+ insert_text_at_cursor(auto_brace_completion_pairs[pre_brace_pair].close_key);
+ }
+ }
+ cursor_set_column(cc + caret_move_offset);
+ } else {
+ insert_text_at_cursor(chr);
+ }
+
+ if ((is_insert_mode() && !had_selection) || (had_selection)) {
+ end_complex_operation();
+ }
+}
+
/* Indent management */
void CodeEdit::set_indent_size(const int p_size) {
ERR_FAIL_COND_MSG(p_size <= 0, "Indend size must be greater than 0.");
@@ -527,13 +664,13 @@ void CodeEdit::do_indent() {
}
if (!indent_using_spaces) {
- _insert_text_at_cursor("\t");
+ insert_text_at_cursor("\t");
return;
}
int spaces_to_add = _calculate_spaces_till_next_right_indent(cursor_get_column());
if (spaces_to_add > 0) {
- _insert_text_at_cursor(String(" ").repeat(spaces_to_add));
+ insert_text_at_cursor(String(" ").repeat(spaces_to_add));
}
}
@@ -713,34 +850,6 @@ int CodeEdit::_calculate_spaces_till_next_right_indent(int p_column) const {
return indent_size - p_column % indent_size;
}
-/* TODO: remove once brace completion is refactored. */
-static char32_t _get_right_pair_symbol(char32_t c) {
- if (c == '"') {
- return '"';
- }
- if (c == '\'') {
- return '\'';
- }
- if (c == '(') {
- return ')';
- }
- if (c == '[') {
- return ']';
- }
- if (c == '{') {
- return '}';
- }
- return 0;
-}
-
-static bool _is_pair_left_symbol(char32_t c) {
- return c == '"' ||
- c == '\'' ||
- c == '(' ||
- c == '[' ||
- c == '{';
-}
-
void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
if (is_readonly()) {
return;
@@ -803,9 +912,8 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
if (should_indent) {
ins += indent_text;
- /* TODO: Change when brace completion is refactored. */
- char32_t closing_char = _get_right_pair_symbol(indent_char);
- if (closing_char != 0 && closing_char == line[cc]) {
+ String closing_pair = get_auto_brace_completion_close_key(String::chr(indent_char));
+ if (!closing_pair.is_empty() && line.find(closing_pair, cc) == cc) {
/* No need to move the brace below if we are not taking the text with us. */
if (p_split_current_line) {
brace_indent = true;
@@ -873,12 +981,20 @@ void CodeEdit::backspace() {
merge_gutters(cl, prev_line);
- /* TODO: Change when brace completion is refactored. */
- if (auto_brace_completion_enabled && cc > 0 && _is_pair_left_symbol(get_line(cl)[cc - 1])) {
- _consume_backspace_for_pair_symbol(prev_line, prev_column);
- cursor_set_line(prev_line, false, true);
- cursor_set_column(prev_column);
- return;
+ if (auto_brace_completion_enabled && cc > 0) {
+ int idx = _get_auto_brace_pair_open_at_pos(cl, cc);
+ if (idx != -1) {
+ prev_column = cc - auto_brace_completion_pairs[idx].open_key.length();
+
+ if (_get_auto_brace_pair_close_at_pos(cl, cc) == idx) {
+ _remove_text(prev_line, prev_column, cl, cc + auto_brace_completion_pairs[idx].close_key.length());
+ } else {
+ _remove_text(prev_line, prev_column, cl, cc);
+ }
+ cursor_set_line(prev_line, false, true);
+ cursor_set_column(prev_column);
+ return;
+ }
}
/* For space indentation we need to do a simple unindent if there are no chars to the left, acting in the */
@@ -896,6 +1012,93 @@ void CodeEdit::backspace() {
cursor_set_column(prev_column);
}
+/* Auto brace completion */
+void CodeEdit::set_auto_brace_completion_enabled(bool p_enabled) {
+ auto_brace_completion_enabled = p_enabled;
+}
+
+bool CodeEdit::is_auto_brace_completion_enabled() const {
+ return auto_brace_completion_enabled;
+}
+
+void CodeEdit::set_highlight_matching_braces_enabled(bool p_enabled) {
+ highlight_matching_braces_enabled = p_enabled;
+ update();
+}
+
+bool CodeEdit::is_highlight_matching_braces_enabled() const {
+ return highlight_matching_braces_enabled;
+}
+
+void CodeEdit::add_auto_brace_completion_pair(const String &p_open_key, const String &p_close_key) {
+ ERR_FAIL_COND_MSG(p_open_key.is_empty(), "auto brace completion open key cannot be empty");
+ ERR_FAIL_COND_MSG(p_close_key.is_empty(), "auto brace completion close key cannot be empty");
+
+ for (int i = 0; i < p_open_key.length(); i++) {
+ ERR_FAIL_COND_MSG(!is_symbol(p_open_key[i]), "auto brace completion open key must be a symbol");
+ }
+ for (int i = 0; i < p_close_key.length(); i++) {
+ ERR_FAIL_COND_MSG(!is_symbol(p_close_key[i]), "auto brace completion close key must be a symbol");
+ }
+
+ int at = 0;
+ for (int i = 0; i < auto_brace_completion_pairs.size(); i++) {
+ ERR_FAIL_COND_MSG(auto_brace_completion_pairs[i].open_key == p_open_key, "auto brace completion open key '" + p_open_key + "' already exists.");
+ if (p_open_key.length() < auto_brace_completion_pairs[i].open_key.length()) {
+ at++;
+ }
+ }
+
+ BracePair brace_pair;
+ brace_pair.open_key = p_open_key;
+ brace_pair.close_key = p_close_key;
+ auto_brace_completion_pairs.insert(at, brace_pair);
+}
+
+void CodeEdit::set_auto_brace_completion_pairs(const Dictionary &p_auto_brace_completion_pairs) {
+ auto_brace_completion_pairs.clear();
+
+ Array keys = p_auto_brace_completion_pairs.keys();
+ for (int i = 0; i < keys.size(); i++) {
+ add_auto_brace_completion_pair(keys[i], p_auto_brace_completion_pairs[keys[i]]);
+ }
+}
+
+Dictionary CodeEdit::get_auto_brace_completion_pairs() const {
+ Dictionary brace_pairs;
+ for (int i = 0; i < auto_brace_completion_pairs.size(); i++) {
+ brace_pairs[auto_brace_completion_pairs[i].open_key] = auto_brace_completion_pairs[i].close_key;
+ }
+ return brace_pairs;
+}
+
+bool CodeEdit::has_auto_brace_completion_open_key(const String &p_open_key) const {
+ for (int i = 0; i < auto_brace_completion_pairs.size(); i++) {
+ if (auto_brace_completion_pairs[i].open_key == p_open_key) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CodeEdit::has_auto_brace_completion_close_key(const String &p_close_key) const {
+ for (int i = 0; i < auto_brace_completion_pairs.size(); i++) {
+ if (auto_brace_completion_pairs[i].close_key == p_close_key) {
+ return true;
+ }
+ }
+ return false;
+}
+
+String CodeEdit::get_auto_brace_completion_close_key(const String &p_open_key) const {
+ for (int i = 0; i < auto_brace_completion_pairs.size(); i++) {
+ if (auto_brace_completion_pairs[i].open_key == p_open_key) {
+ return auto_brace_completion_pairs[i].close_key;
+ }
+ }
+ return String();
+}
+
/* Main Gutter */
void CodeEdit::_update_draw_main_gutter() {
set_gutter_draw(main_gutter, draw_breakpoints || draw_bookmarks || draw_executing_lines);
@@ -1700,35 +1903,40 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
insert_text_at_cursor(insert_text.substr(matching_chars));
}
- /* TODO: merge with autobrace completion, when in CodeEdit. */
/* Handle merging of symbols eg strings, brackets. */
const String line = get_line(caret_line);
char32_t next_char = line[cursor_get_column()];
char32_t last_completion_char = insert_text[insert_text.length() - 1];
char32_t last_completion_char_display = display_text[display_text.length() - 1];
- if ((last_completion_char == '"' || last_completion_char == '\'') && (last_completion_char == next_char || last_completion_char_display == next_char)) {
+ int pre_brace_pair = cursor_get_column() > 0 ? _get_auto_brace_pair_open_at_pos(caret_line, cursor_get_column()) : -1;
+ int post_brace_pair = cursor_get_column() < get_line(caret_line).length() ? _get_auto_brace_pair_close_at_pos(caret_line, cursor_get_column()) : -1;
+
+ if (post_brace_pair != -1 && (last_completion_char == next_char || last_completion_char_display == next_char)) {
_remove_text(caret_line, cursor_get_column(), caret_line, cursor_get_column() + 1);
}
- if (last_completion_char == '(') {
- if (next_char == last_completion_char) {
- _remove_text(caret_line, cursor_get_column() - 1, caret_line, cursor_get_column());
- } else if (auto_brace_completion_enabled) {
- insert_text_at_cursor(")");
- cursor_set_column(cursor_get_column() - 1);
- }
- } else if (last_completion_char == ')' && next_char == '(') {
- _remove_text(caret_line, cursor_get_column() - 2, caret_line, cursor_get_column());
- if (line[cursor_get_column() + 1] != ')') {
- cursor_set_column(cursor_get_column() - 1);
+ if (pre_brace_pair != -1 && pre_brace_pair != post_brace_pair && (last_completion_char == next_char || last_completion_char_display == next_char)) {
+ _remove_text(caret_line, cursor_get_column(), caret_line, cursor_get_column() + 1);
+ } else if (auto_brace_completion_enabled && pre_brace_pair != -1 && post_brace_pair == -1) {
+ insert_text_at_cursor(auto_brace_completion_pairs[pre_brace_pair].close_key);
+ cursor_set_column(cursor_get_column() - auto_brace_completion_pairs[pre_brace_pair].close_key.length());
+ }
+
+ if (pre_brace_pair == -1 && post_brace_pair == -1 && cursor_get_column() > 0 && cursor_get_column() < get_line(caret_line).length()) {
+ pre_brace_pair = _get_auto_brace_pair_open_at_pos(caret_line, cursor_get_column() + 1);
+ if (pre_brace_pair == _get_auto_brace_pair_close_at_pos(caret_line, cursor_get_column() - 1)) {
+ _remove_text(caret_line, cursor_get_column() - 2, caret_line, cursor_get_column());
+ if (_get_auto_brace_pair_close_at_pos(caret_line, cursor_get_column() - 1) != pre_brace_pair) {
+ cursor_set_column(cursor_get_column() - 1);
+ }
}
}
end_complex_operation();
cancel_code_completion();
- if (last_completion_char == '(') {
+ if (code_completion_prefixes.has(String::chr(last_completion_char))) {
request_code_completion();
}
}
@@ -1742,6 +1950,58 @@ void CodeEdit::cancel_code_completion() {
update();
}
+/* Line length guidelines */
+void CodeEdit::set_line_length_guidelines(TypedArray<int> p_guideline_columns) {
+ line_length_guideline_columns = p_guideline_columns;
+ update();
+}
+
+TypedArray<int> CodeEdit::get_line_length_guidelines() const {
+ return line_length_guideline_columns;
+}
+
+/* Symbol lookup */
+void CodeEdit::set_symbol_lookup_on_click_enabled(bool p_enabled) {
+ symbol_lookup_on_click_enabled = p_enabled;
+ set_symbol_lookup_word_as_valid(false);
+}
+
+bool CodeEdit::is_symbol_lookup_on_click_enabled() const {
+ return symbol_lookup_on_click_enabled;
+}
+
+String CodeEdit::get_text_for_symbol_lookup() {
+ int line, col;
+ Point2i mp = _get_local_mouse_pos();
+ _get_mouse_pos(mp, line, col);
+
+ StringBuilder lookup_text;
+ const int text_size = get_line_count();
+ for (int i = 0; i < text_size; i++) {
+ String text = get_line(i);
+
+ if (i == line) {
+ lookup_text += text.substr(0, col);
+ /* Not unicode, represents the cursor. */
+ lookup_text += String::chr(0xFFFF);
+ lookup_text += text.substr(col, text.size());
+ } else {
+ lookup_text += text;
+ }
+
+ if (i != text_size - 1) {
+ lookup_text += "\n";
+ }
+ }
+ return lookup_text.as_string();
+}
+
+void CodeEdit::set_symbol_lookup_word_as_valid(bool p_valid) {
+ symbol_lookup_word = p_valid ? symbol_lookup_new_word : "";
+ symbol_lookup_new_word = "";
+ _set_symbol_lookup_word(symbol_lookup_word);
+}
+
void CodeEdit::_bind_methods() {
/* Indent management */
ClassDB::bind_method(D_METHOD("set_indent_size", "size"), &CodeEdit::set_indent_size);
@@ -1762,6 +2022,22 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("indent_lines"), &CodeEdit::indent_lines);
ClassDB::bind_method(D_METHOD("unindent_lines"), &CodeEdit::unindent_lines);
+ /* Auto brace completion */
+ ClassDB::bind_method(D_METHOD("set_auto_brace_completion_enabled", "enable"), &CodeEdit::set_auto_brace_completion_enabled);
+ ClassDB::bind_method(D_METHOD("is_auto_brace_completion_enabled"), &CodeEdit::is_auto_brace_completion_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_highlight_matching_braces_enabled", "enable"), &CodeEdit::set_highlight_matching_braces_enabled);
+ ClassDB::bind_method(D_METHOD("is_highlight_matching_braces_enabled"), &CodeEdit::is_highlight_matching_braces_enabled);
+
+ ClassDB::bind_method(D_METHOD("add_auto_brace_completion_pair", "start_key", "end_key"), &CodeEdit::add_auto_brace_completion_pair);
+ ClassDB::bind_method(D_METHOD("set_auto_brace_completion_pairs", "pairs"), &CodeEdit::set_auto_brace_completion_pairs);
+ ClassDB::bind_method(D_METHOD("get_auto_brace_completion_pairs"), &CodeEdit::get_auto_brace_completion_pairs);
+
+ ClassDB::bind_method(D_METHOD("has_auto_brace_completion_open_key", "open_key"), &CodeEdit::has_auto_brace_completion_open_key);
+ ClassDB::bind_method(D_METHOD("has_auto_brace_completion_close_key", "close_key"), &CodeEdit::has_auto_brace_completion_close_key);
+
+ ClassDB::bind_method(D_METHOD("get_auto_brace_completion_close_key", "open_key"), &CodeEdit::get_auto_brace_completion_close_key);
+
/* Main Gutter */
ClassDB::bind_method(D_METHOD("_main_gutter_draw_callback"), &CodeEdit::_main_gutter_draw_callback);
@@ -1890,19 +2166,35 @@ void CodeEdit::_bind_methods() {
BIND_VMETHOD(MethodInfo("_request_code_completion", PropertyInfo(Variant::BOOL, "force")));
BIND_VMETHOD(MethodInfo(Variant::ARRAY, "_filter_code_completion_candidates", PropertyInfo(Variant::ARRAY, "candidates")));
+ /* Line length guidelines */
+ ClassDB::bind_method(D_METHOD("set_line_length_guidelines", "guideline_columns"), &CodeEdit::set_line_length_guidelines);
+ ClassDB::bind_method(D_METHOD("get_line_length_guidelines"), &CodeEdit::get_line_length_guidelines);
+
+ /* Symbol lookup */
+ ClassDB::bind_method(D_METHOD("set_symbol_lookup_on_click_enabled", "enable"), &CodeEdit::set_symbol_lookup_on_click_enabled);
+ ClassDB::bind_method(D_METHOD("is_symbol_lookup_on_click_enabled"), &CodeEdit::is_symbol_lookup_on_click_enabled);
+
+ ClassDB::bind_method(D_METHOD("get_text_for_symbol_lookup"), &CodeEdit::get_text_for_symbol_lookup);
+
+ ClassDB::bind_method(D_METHOD("set_symbol_lookup_word_as_valid", "valid"), &CodeEdit::set_symbol_lookup_word_as_valid);
+
/* Inspector */
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_breakpoints_gutter"), "set_draw_breakpoints_gutter", "is_drawing_breakpoints_gutter");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "symbol_lookup_on_click"), "set_symbol_lookup_on_click_enabled", "is_symbol_lookup_on_click_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "line_folding"), "set_line_folding_enabled", "is_line_folding_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_bookmarks"), "set_draw_bookmarks_gutter", "is_drawing_bookmarks_gutter");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "line_length_guidelines"), "set_line_length_guidelines", "get_line_length_guidelines");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_executing_lines"), "set_draw_executing_lines_gutter", "is_drawing_executing_lines_gutter");
+ ADD_GROUP("Gutters", "gutters_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gutters_draw_breakpoints_gutter"), "set_draw_breakpoints_gutter", "is_drawing_breakpoints_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, "gutters_draw_bookmarks"), "set_draw_bookmarks_gutter", "is_drawing_bookmarks_gutter");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gutters_draw_executing_lines"), "set_draw_executing_lines_gutter", "is_drawing_executing_lines_gutter");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "line_folding"), "set_line_folding_enabled", "is_line_folding_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gutters_draw_line_numbers"), "set_draw_line_numbers", "is_draw_line_numbers_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gutters_zero_pad_line_numbers"), "set_line_numbers_zero_padded", "is_line_numbers_zero_padded");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gutters_draw_fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter");
ADD_GROUP("Delimiters", "delimiter_");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "delimiter_strings"), "set_string_delimiters", "get_string_delimiters");
@@ -1918,11 +2210,74 @@ void CodeEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_automatic"), "set_auto_indent_enabled", "is_auto_indent_enabled");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "indent_automatic_prefixes"), "set_auto_indent_prefixes", "get_auto_indent_prefixes");
+ ADD_GROUP("Auto brace completion", "auto_brace_completion_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_brace_completion_enabled"), "set_auto_brace_completion_enabled", "is_auto_brace_completion_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_brace_completion_highlight_matching"), "set_highlight_matching_braces_enabled", "is_highlight_matching_braces_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "auto_brace_completion_pairs"), "set_auto_brace_completion_pairs", "get_auto_brace_completion_pairs");
+
/* Signals */
+ /* Gutters */
ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "line")));
+
+ /* Code Completion */
ADD_SIGNAL(MethodInfo("request_code_completion"));
+
+ /* 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")));
}
+/* Auto brace completion */
+int CodeEdit::_get_auto_brace_pair_open_at_pos(int p_line, int p_col) {
+ const String &line = get_line(p_line);
+
+ /* Should be fast enough, expecting low amount of pairs... */
+ for (int i = 0; i < auto_brace_completion_pairs.size(); i++) {
+ const String &open_key = auto_brace_completion_pairs[i].open_key;
+ if (p_col - open_key.length() < 0) {
+ continue;
+ }
+
+ bool is_match = true;
+ for (int j = 0; j < open_key.length(); j++) {
+ if (line[(p_col - 1) - j] != open_key[(open_key.length() - 1) - j]) {
+ is_match = false;
+ break;
+ }
+ }
+
+ if (is_match) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int CodeEdit::_get_auto_brace_pair_close_at_pos(int p_line, int p_col) {
+ const String &line = get_line(p_line);
+
+ /* Should be fast enough, expecting low amount of pairs... */
+ for (int i = 0; i < auto_brace_completion_pairs.size(); i++) {
+ if (p_col + auto_brace_completion_pairs[i].close_key.length() > line.length()) {
+ continue;
+ }
+
+ bool is_match = true;
+ for (int j = 0; j < auto_brace_completion_pairs[i].close_key.length(); j++) {
+ if (line[p_col + j] != auto_brace_completion_pairs[i].close_key[j]) {
+ is_match = false;
+ break;
+ }
+ }
+
+ if (is_match) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/* Gutters */
void CodeEdit::_gutter_clicked(int p_line, int p_gutter) {
if (p_gutter == main_gutter) {
if (draw_breakpoints) {
@@ -2128,7 +2483,7 @@ int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) c
int region = (p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value();
bool in_region = region != -1 && delimiters[region].type == p_type;
for (Map<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) {
- /* If column is specified, loop untill the key is larger then the column. */
+ /* If column is specified, loop until the key is larger then the column. */
if (p_column != -1) {
if (E->key() > p_column) {
break;
@@ -2138,7 +2493,7 @@ int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) c
continue;
}
- /* If no column, calulate if the entire line is a region */
+ /* If no column, calculate if the entire line is a region */
/* excluding whitespace. */
const String line = get_line(p_line);
if (!in_region) {
@@ -2288,14 +2643,14 @@ void CodeEdit::_filter_code_completion_candidates() {
TypedArray<Dictionary> completion_options_sources;
completion_options_sources.resize(code_completion_option_sources.size());
int i = 0;
- for (List<ScriptCodeCompletionOption>::Element *E = code_completion_option_sources.front(); E; E = E->next()) {
+ for (const ScriptCodeCompletionOption &E : code_completion_option_sources) {
Dictionary option;
- option["kind"] = E->get().kind;
- option["display_text"] = E->get().display;
- option["insert_text"] = E->get().insert_text;
- option["font_color"] = E->get().font_color;
- option["icon"] = E->get().icon;
- option["default_value"] = E->get().default_value;
+ option["kind"] = E.kind;
+ option["display_text"] = E.display;
+ option["insert_text"] = E.insert_text;
+ option["font_color"] = E.font_color;
+ option["icon"] = E.icon;
+ option["default_value"] = E.default_value;
completion_options_sources[i] = option;
i++;
}
@@ -2406,9 +2761,7 @@ void CodeEdit::_filter_code_completion_candidates() {
int max_width = 0;
String string_to_complete_lower = string_to_complete.to_lower();
- for (List<ScriptCodeCompletionOption>::Element *E = code_completion_option_sources.front(); E; E = E->next()) {
- ScriptCodeCompletionOption &option = E->get();
-
+ for (ScriptCodeCompletionOption &option : code_completion_option_sources) {
if (single_quote && option.display.is_quoted()) {
option.display = option.display.unquote().quote("'");
}
@@ -2527,8 +2880,7 @@ void CodeEdit::_lines_edited_from(int p_from_line, int 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();
+ for (const int line : breakpoints) {
if (line <= from_line) {
continue;
}
@@ -2550,6 +2902,17 @@ CodeEdit::CodeEdit() {
auto_indent_prefixes.insert('[');
auto_indent_prefixes.insert('(');
+ /* Auto brace completion */
+ add_auto_brace_completion_pair("(", ")");
+ add_auto_brace_completion_pair("{", "}");
+ add_auto_brace_completion_pair("[", "]");
+ add_auto_brace_completion_pair("\"", "\"");
+ add_auto_brace_completion_pair("\'", "\'");
+
+ /* Delimiter traking */
+ add_string_delimiter("\"", "\"", false);
+ add_string_delimiter("\'", "\'", false);
+
/* Text Direction */
set_layout_direction(LAYOUT_DIRECTION_LTR);
set_text_direction(TEXT_DIRECTION_LTR);
@@ -2562,7 +2925,7 @@ CodeEdit::CodeEdit() {
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_type(gutter_idx, GUTTER_TYPE_CUSTOM);
set_gutter_custom_draw(gutter_idx, this, "_main_gutter_draw_callback");
gutter_idx++;
@@ -2570,7 +2933,7 @@ CodeEdit::CodeEdit() {
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_type(gutter_idx, GUTTER_TYPE_CUSTOM);
set_gutter_custom_draw(gutter_idx, this, "_line_number_draw_callback");
gutter_idx++;
@@ -2578,7 +2941,7 @@ CodeEdit::CodeEdit() {
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_type(gutter_idx, GUTTER_TYPE_CUSTOM);
set_gutter_custom_draw(gutter_idx, this, "_fold_gutter_draw_callback");
gutter_idx++;