diff options
Diffstat (limited to 'scene/gui/code_edit.cpp')
-rw-r--r-- | scene/gui/code_edit.cpp | 627 |
1 files changed, 372 insertions, 255 deletions
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index ee39327d4d..1944b8db98 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -36,44 +36,10 @@ void CodeEdit::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_THEME_CHANGED: - case NOTIFICATION_ENTER_TREE: { - style_normal = get_theme_stylebox(SNAME("normal")); - - font = get_theme_font(SNAME("font")); - font_size = get_theme_font_size(SNAME("font_size")); - - line_spacing = get_theme_constant(SNAME("line_spacing")); - + case NOTIFICATION_THEME_CHANGED: { set_gutter_width(main_gutter, get_line_height()); - set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', font_size).width); + set_gutter_width(line_number_gutter, (line_number_digits + 1) * theme_cache.font->get_char_size('0', theme_cache.font_size).width); set_gutter_width(fold_gutter, get_line_height() / 1.2); - - breakpoint_color = get_theme_color(SNAME("breakpoint_color")); - breakpoint_icon = get_theme_icon(SNAME("breakpoint")); - - bookmark_color = get_theme_color(SNAME("bookmark_color")); - bookmark_icon = get_theme_icon(SNAME("bookmark")); - - executing_line_color = get_theme_color(SNAME("executing_line_color")); - executing_line_icon = get_theme_icon(SNAME("executing_line")); - - line_number_color = get_theme_color(SNAME("line_number_color")); - - folding_color = get_theme_color(SNAME("code_folding_color")); - can_fold_icon = get_theme_icon(SNAME("can_fold")); - folded_icon = get_theme_icon(SNAME("folded")); - - code_completion_max_width = get_theme_constant(SNAME("completion_max_width")); - code_completion_max_lines = get_theme_constant(SNAME("completion_lines")); - code_completion_scroll_width = get_theme_constant(SNAME("completion_scroll_width")); - code_completion_scroll_color = get_theme_color(SNAME("completion_scroll_color")); - code_completion_scroll_hovered_color = get_theme_color(SNAME("completion_scroll_hovered_color")); - 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: { @@ -84,14 +50,14 @@ void CodeEdit::_notification(int p_what) { const int row_height = get_line_height(); if (line_length_guideline_columns.size() > 0) { - const int xmargin_beg = style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width(); - const int xmargin_end = size.width - style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0); - const float char_size = font->get_char_size('0', font_size).width; + const int xmargin_beg = theme_cache.style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width(); + const int xmargin_end = size.width - theme_cache.style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0); + const float char_size = theme_cache.font->get_char_size('0', theme_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); + Color guideline_color = (i == 0) ? theme_cache.line_length_guideline_color : theme_cache.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; @@ -103,45 +69,42 @@ void CodeEdit::_notification(int p_what) { bool code_completion_below = false; if (caret_visible && code_completion_active && code_completion_options.size() > 0) { - Ref<StyleBox> csb = get_theme_stylebox(SNAME("completion")); - const int code_completion_options_count = code_completion_options.size(); - const int lines = MIN(code_completion_options_count, code_completion_max_lines); - const int icon_hsep = get_theme_constant(SNAME("h_separation"), SNAME("ItemList")); + const int lines = MIN(code_completion_options_count, theme_cache.code_completion_max_lines); const Size2 icon_area_size(row_height, row_height); - code_completion_rect.size.width = code_completion_longest_line + icon_hsep + icon_area_size.width + 2; + code_completion_rect.size.width = code_completion_longest_line + theme_cache.code_completion_icon_separation + icon_area_size.width + 2; code_completion_rect.size.height = lines * row_height; const Point2 caret_pos = get_caret_draw_pos(); - const int total_height = csb->get_minimum_size().y + code_completion_rect.size.height; + const int total_height = theme_cache.code_completion_style->get_minimum_size().y + code_completion_rect.size.height; const bool can_fit_completion_above = (caret_pos.y - row_height > total_height); const bool can_fit_completion_below = (caret_pos.y + row_height + total_height <= get_size().height); if (!can_fit_completion_below && can_fit_completion_above) { - code_completion_rect.position.y = (caret_pos.y - total_height - row_height) + line_spacing; + code_completion_rect.position.y = (caret_pos.y - total_height - row_height) + theme_cache.line_spacing; } else { - code_completion_rect.position.y = caret_pos.y + (line_spacing / 2.0f); + code_completion_rect.position.y = caret_pos.y + (theme_cache.line_spacing / 2.0f); code_completion_below = true; } - const int scroll_width = code_completion_options_count > code_completion_max_lines ? code_completion_scroll_width : 0; - const int code_completion_base_width = font->get_string_size(code_completion_base, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width; + const int scroll_width = code_completion_options_count > theme_cache.code_completion_max_lines ? theme_cache.code_completion_scroll_width : 0; + const int code_completion_base_width = theme_cache.font->get_string_size(code_completion_base, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width; if (caret_pos.x - code_completion_base_width + code_completion_rect.size.width + scroll_width > get_size().width) { code_completion_rect.position.x = get_size().width - code_completion_rect.size.width - scroll_width; } else { code_completion_rect.position.x = caret_pos.x - code_completion_base_width; } - draw_style_box(csb, Rect2(code_completion_rect.position - csb->get_offset(), code_completion_rect.size + csb->get_minimum_size() + Size2(scroll_width, 0))); - if (code_completion_background_color.a > 0.01) { - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(code_completion_rect.position, code_completion_rect.size + Size2(scroll_width, 0)), code_completion_background_color); + draw_style_box(theme_cache.code_completion_style, Rect2(code_completion_rect.position - theme_cache.code_completion_style->get_offset(), code_completion_rect.size + theme_cache.code_completion_style->get_minimum_size() + Size2(scroll_width, 0))); + if (theme_cache.code_completion_background_color.a > 0.01) { + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(code_completion_rect.position, code_completion_rect.size + Size2(scroll_width, 0)), theme_cache.code_completion_background_color); } code_completion_scroll_rect.position = code_completion_rect.position + Vector2(code_completion_rect.size.width, 0); code_completion_scroll_rect.size = Vector2(scroll_width, code_completion_rect.size.height); code_completion_line_ofs = CLAMP((code_completion_force_item_center < 0 ? code_completion_current_selected : code_completion_force_item_center) - lines / 2, 0, code_completion_options_count - lines); - RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(code_completion_rect.position.x, code_completion_rect.position.y + (code_completion_current_selected - code_completion_line_ofs) * row_height), Size2(code_completion_rect.size.width, row_height)), code_completion_selected_color); + RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(code_completion_rect.position.x, code_completion_rect.position.y + (code_completion_current_selected - code_completion_line_ofs) * row_height), Size2(code_completion_rect.size.width, row_height)), theme_cache.code_completion_selected_color); for (int i = 0; i < lines; i++) { int l = code_completion_line_ofs + i; @@ -149,7 +112,7 @@ void CodeEdit::_notification(int p_what) { Ref<TextLine> tl; tl.instantiate(); - tl->add_string(code_completion_options[l].display, font, font_size); + tl->add_string(code_completion_options[l].display, theme_cache.font, theme_cache.font_size); int yofs = (row_height - tl->get_size().y) / 2; Point2 title_pos(code_completion_rect.position.x, code_completion_rect.position.y + i * row_height + yofs); @@ -161,9 +124,9 @@ void CodeEdit::_notification(int p_what) { Size2 icon_size = icon_area.size * 0.7; icon->draw_rect(ci, Rect2(icon_area.position + (icon_area.size - icon_size) / 2, icon_size)); } - title_pos.x = icon_area.position.x + icon_area.size.width + icon_hsep; + title_pos.x = icon_area.position.x + icon_area.size.width + theme_cache.code_completion_icon_separation; - tl->set_width(code_completion_rect.size.width - (icon_area_size.x + icon_hsep)); + tl->set_width(code_completion_rect.size.width - (icon_area_size.x + theme_cache.code_completion_icon_separation)); if (rtl) { if (code_completion_options[l].default_value.get_type() == Variant::COLOR) { draw_rect(Rect2(Point2(code_completion_rect.position.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value); @@ -176,24 +139,23 @@ void CodeEdit::_notification(int p_what) { tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT); } - Point2 match_pos = Point2(code_completion_rect.position.x + icon_area_size.x + icon_hsep, code_completion_rect.position.y + i * row_height); + Point2 match_pos = Point2(code_completion_rect.position.x + icon_area_size.x + theme_cache.code_completion_icon_separation, code_completion_rect.position.y + i * row_height); for (int j = 0; j < code_completion_options[l].matches.size(); j++) { - Pair<int, int> match = code_completion_options[l].matches[j]; - int match_offset = font->get_string_size(code_completion_options[l].display.substr(0, match.first), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width; - int match_len = font->get_string_size(code_completion_options[l].display.substr(match.first, match.second), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width; + Pair<int, int> match_segment = code_completion_options[l].matches[j]; + int match_offset = theme_cache.font->get_string_size(code_completion_options[l].display.substr(0, match_segment.first), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width; + int match_len = theme_cache.font->get_string_size(code_completion_options[l].display.substr(match_segment.first, match_segment.second), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width; - draw_rect(Rect2(match_pos + Point2(match_offset, 0), Size2(match_len, row_height)), code_completion_existing_color); + draw_rect(Rect2(match_pos + Point2(match_offset, 0), Size2(match_len, row_height)), theme_cache.code_completion_existing_color); } - tl->draw(ci, title_pos, code_completion_options[l].font_color); } /* Draw a small scroll rectangle to show a position in the options. */ if (scroll_width) { - Color scroll_color = is_code_completion_scroll_hovered || is_code_completion_scroll_pressed ? code_completion_scroll_hovered_color : code_completion_scroll_color; + Color scroll_color = is_code_completion_scroll_hovered || is_code_completion_scroll_pressed ? theme_cache.code_completion_scroll_hovered_color : theme_cache.code_completion_scroll_color; - float r = (float)code_completion_max_lines / code_completion_options_count; + float r = (float)theme_cache.code_completion_max_lines / code_completion_options_count; float o = (float)code_completion_line_ofs / code_completion_options_count; draw_rect(Rect2(code_completion_rect.position.x + code_completion_rect.size.width, code_completion_rect.position.y + o * code_completion_rect.size.y, scroll_width, code_completion_rect.size.y * r), scroll_color); } @@ -201,31 +163,29 @@ void CodeEdit::_notification(int p_what) { /* Code hint */ if (caret_visible && !code_hint.is_empty() && (!code_completion_active || (code_completion_below != code_hint_draw_below))) { - const int font_height = font->get_height(font_size); - Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel")); - Color color = get_theme_color(SNAME("font_color"), SNAME("TooltipLabel")); + const int font_height = theme_cache.font->get_height(theme_cache.font_size); Vector<String> code_hint_lines = code_hint.split("\n"); int line_count = code_hint_lines.size(); int max_width = 0; for (int i = 0; i < line_count; i++) { - max_width = MAX(max_width, font->get_string_size(code_hint_lines[i], HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x); + max_width = MAX(max_width, theme_cache.font->get_string_size(code_hint_lines[i], HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x); } - Size2 minsize = sb->get_minimum_size() + Size2(max_width, line_count * font_height + (line_spacing * line_count - 1)); + Size2 minsize = theme_cache.code_hint_style->get_minimum_size() + Size2(max_width, line_count * font_height + (theme_cache.line_spacing * line_count - 1)); - int offset = font->get_string_size(code_hint_lines[0].substr(0, code_hint_lines[0].find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x; + int offset = theme_cache.font->get_string_size(code_hint_lines[0].substr(0, code_hint_lines[0].find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x; if (code_hint_xpos == -0xFFFF) { code_hint_xpos = get_caret_draw_pos().x - offset; } Point2 hint_ofs = Vector2(code_hint_xpos, get_caret_draw_pos().y); if (code_hint_draw_below) { - hint_ofs.y += line_spacing / 2.0f; + hint_ofs.y += theme_cache.line_spacing / 2.0f; } else { - hint_ofs.y -= (minsize.y + row_height) - line_spacing; + hint_ofs.y -= (minsize.y + row_height) - theme_cache.line_spacing; } - draw_style_box(sb, Rect2(hint_ofs, minsize)); + draw_style_box(theme_cache.code_hint_style, Rect2(hint_ofs, minsize)); int yofs = 0; for (int i = 0; i < line_count; i++) { @@ -234,34 +194,90 @@ void CodeEdit::_notification(int p_what) { int begin = 0; int end = 0; if (line.contains(String::chr(0xFFFF))) { - begin = font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x; - end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x; + begin = theme_cache.font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x; + end = theme_cache.font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x; } - Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent(font_size) + font_height * i + yofs); + Point2 round_ofs = hint_ofs + theme_cache.code_hint_style->get_offset() + Vector2(0, theme_cache.font->get_ascent(theme_cache.font_size) + font_height * i + yofs); round_ofs = round_ofs.round(); - draw_string(font, round_ofs, line.replace(String::chr(0xFFFF), ""), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color); + draw_string(theme_cache.font, round_ofs, line.replace(String::chr(0xFFFF), ""), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size, theme_cache.code_hint_color); if (end > 0) { // Draw an underline for the currently edited function parameter. - const Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + yofs); - draw_line(b, b + Vector2(end - begin, 0), color, 2); + const Vector2 b = hint_ofs + theme_cache.code_hint_style->get_offset() + Vector2(begin, font_height + font_height * i + yofs); + draw_line(b, b + Vector2(end - begin, 0), theme_cache.code_hint_color, 2); // Draw a translucent text highlight as well. const Rect2 highlight_rect = Rect2( b - Vector2(0, font_height), Vector2(end - begin, font_height)); - draw_rect(highlight_rect, color * Color(1, 1, 1, 0.2)); + draw_rect(highlight_rect, theme_cache.code_hint_color * Color(1, 1, 1, 0.2)); } - yofs += line_spacing; + yofs += theme_cache.line_spacing; } } } break; + + case NOTIFICATION_DRAG_BEGIN: { + cancel_code_completion(); + } break; + + case NOTIFICATION_MOUSE_EXIT: { + queue_redraw(); + } break; } } +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()) { /* Ignore mouse clicks in IME input mode. */ if (has_ime_text()) { @@ -270,14 +286,24 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { if (is_code_completion_scroll_pressed && mb->get_button_index() == MouseButton::LEFT) { is_code_completion_scroll_pressed = false; + accept_event(); + queue_redraw(); + return; + } + + if (is_code_completion_drag_started && !mb->is_pressed()) { + is_code_completion_drag_started = false; + accept_event(); queue_redraw(); return; } if (code_completion_active && code_completion_rect.has_point(mb->get_position())) { if (!mb->is_pressed()) { + accept_event(); return; } + is_code_completion_drag_started = true; switch (mb->get_button_index()) { case MouseButton::WHEEL_UP: { @@ -309,19 +335,23 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { break; } + accept_event(); return; } else if (code_completion_active && code_completion_scroll_rect.has_point(mb->get_position())) { if (mb->get_button_index() != MouseButton::LEFT) { + accept_event(); return; } if (mb->is_pressed()) { + is_code_completion_drag_started = true; is_code_completion_scroll_pressed = true; _update_scroll_selected_line(mb->get_position().y); queue_redraw(); } + accept_event(); return; } @@ -342,7 +372,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { if (is_line_folded(line)) { int wrap_index = get_line_wrap_index_at_column(line, col); if (wrap_index == get_line_wrap_count(line)) { - int eol_icon_width = folded_eol_icon->get_width(); + int eol_icon_width = theme_cache.folded_eol_icon->get_width(); int left_margin = get_total_gutter_width() + eol_icon_width + get_line_width(line, wrap_index) - get_h_scroll(); if (mpos.x > left_margin && mpos.x <= left_margin + eol_icon_width + 3) { unfold_line(line); @@ -394,14 +424,21 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { bool scroll_hovered = code_completion_scroll_rect.has_point(mpos); if (is_code_completion_scroll_hovered != scroll_hovered) { is_code_completion_scroll_hovered = scroll_hovered; + accept_event(); queue_redraw(); } if (is_code_completion_scroll_pressed) { _update_scroll_selected_line(mpos.y); + accept_event(); queue_redraw(); return; } + + if (code_completion_active && code_completion_rect.has_point(mm->get_position())) { + accept_event(); + return; + } } Ref<InputEventKey> k = p_gui_input; @@ -412,16 +449,17 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { bool update_code_completion = false; if (!k.is_valid()) { - TextEdit::gui_input(p_gui_input); + // MouseMotion events should not be handled by TextEdit logic if we're + // currently clicking and dragging from the code completion panel. + if (!mm.is_valid() || !is_code_completion_drag_started) { + TextEdit::gui_input(p_gui_input); + } return; } /* Ctrl + Hover symbols */ -#ifdef MACOS_ENABLED - if (k->get_keycode() == Key::META) { -#else - if (k->get_keycode() == Key::CTRL) { -#endif + bool mac_keys = OS::get_singleton()->has_feature("macos") || OS::get_singleton()->has_feature("web_macos") || OS::get_singleton()->has_feature("web_ios"); + if ((mac_keys && k->get_keycode() == Key::META) || (!mac_keys && k->get_keycode() == Key::CTRL)) { 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()); @@ -436,12 +474,12 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } /* 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) { + if (!k->is_pressed() || k->get_keycode() == Key::CTRL || k->get_keycode() == Key::ALT || k->get_keycode() == Key::SHIFT || k->get_keycode() == Key::META || k->get_keycode() == Key::CAPSLOCK) { return; } - /* Allow unicode handling if: */ - /* No Modifiers are pressed (except shift) */ + // 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()); /* AUTO-COMPLETE */ @@ -475,14 +513,14 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { return; } if (k->is_action("ui_page_up", true)) { - code_completion_current_selected = MAX(0, code_completion_current_selected - code_completion_max_lines); + code_completion_current_selected = MAX(0, code_completion_current_selected - theme_cache.code_completion_max_lines); code_completion_force_item_center = -1; queue_redraw(); accept_event(); return; } if (k->is_action("ui_page_down", true)) { - code_completion_current_selected = MIN(code_completion_options.size() - 1, code_completion_current_selected + code_completion_max_lines); + code_completion_current_selected = MIN(code_completion_options.size() - 1, code_completion_current_selected + theme_cache.code_completion_max_lines); code_completion_force_item_center = -1; queue_redraw(); accept_event(); @@ -553,7 +591,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { return; } - // Override new line actions, for auto indent + // Override new line actions, for auto indent. if (k->is_action("ui_text_newline_above", true)) { _new_line(false, true); accept_event(); @@ -570,7 +608,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { return; } - /* Remove shift otherwise actions will not match. */ + // Remove shift, otherwise actions will not match. k = k->duplicate(); k->set_shift_pressed(false); @@ -611,7 +649,7 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const { if (line != -1 && is_line_folded(line)) { int wrap_index = get_line_wrap_index_at_column(line, col); if (wrap_index == get_line_wrap_count(line)) { - int eol_icon_width = folded_eol_icon->get_width(); + int eol_icon_width = theme_cache.folded_eol_icon->get_width(); int left_margin = get_total_gutter_width() + eol_icon_width + get_line_width(line, wrap_index) - get_h_scroll(); if (p_pos.x > left_margin && p_pos.x <= left_margin + eol_icon_width + 3) { return CURSOR_POINTING_HAND; @@ -743,8 +781,7 @@ void CodeEdit::_backspace_internal(int p_caret) { } } - // For space indentation we need to do a simple unindent if there are no chars to the left, acting in the - // same way as tabs. + // For space indentation we need to do a basic unindent if there are no chars to the left, acting the same way as tabs. if (indent_using_spaces && cc != 0) { if (get_first_non_whitespace_column(cl) >= cc) { prev_column = cc - _calculate_spaces_till_next_left_indent(cc); @@ -977,6 +1014,116 @@ void CodeEdit::unindent_lines() { queue_redraw(); } +void CodeEdit::convert_indent(int p_from_line, int p_to_line) { + if (!is_editable()) { + return; + } + + // Check line range. + p_from_line = (p_from_line < 0) ? 0 : p_from_line; + p_to_line = (p_to_line < 0) ? get_line_count() - 1 : p_to_line; + + ERR_FAIL_COND(p_from_line >= get_line_count()); + ERR_FAIL_COND(p_to_line >= get_line_count()); + ERR_FAIL_COND(p_to_line < p_from_line); + + // Store caret states. + Vector<int> caret_columns; + Vector<Pair<int, int>> from_selections; + Vector<Pair<int, int>> to_selections; + caret_columns.resize(get_caret_count()); + from_selections.resize(get_caret_count()); + to_selections.resize(get_caret_count()); + for (int c = 0; c < get_caret_count(); c++) { + caret_columns.write[c] = get_caret_column(c); + + // Set "selection_from_line" to -1 to allow checking if there was a selection later. + if (!has_selection(c)) { + from_selections.write[c].first = -1; + continue; + } + from_selections.write[c].first = get_selection_from_line(c); + from_selections.write[c].second = get_selection_from_column(c); + to_selections.write[c].first = get_selection_to_line(c); + to_selections.write[c].second = get_selection_to_column(c); + } + + // Check lines within range. + const char32_t from_indent_char = indent_using_spaces ? '\t' : ' '; + int size_diff = indent_using_spaces ? indent_size - 1 : -(indent_size - 1); + bool changed_indentation = false; + for (int i = p_from_line; i <= p_to_line; i++) { + String line = get_line(i); + + if (line.length() <= 0) { + continue; + } + + // Check chars in the line. + int j = 0; + int space_count = 0; + bool line_changed = false; + while (j < line.length() && (line[j] == ' ' || line[j] == '\t')) { + if (line[j] != from_indent_char) { + space_count = 0; + j++; + continue; + } + space_count++; + + if (!indent_using_spaces && space_count != indent_size) { + j++; + continue; + } + + line_changed = true; + if (!changed_indentation) { + begin_complex_operation(); + changed_indentation = true; + } + + // Calculate new caret state. + for (int c = 0; c < get_caret_count(); c++) { + if (get_caret_line(c) != i || caret_columns[c] <= j) { + continue; + } + caret_columns.write[c] += size_diff; + + if (from_selections.write[c].first == -1) { + continue; + } + from_selections.write[c].second = from_selections[c].first == i ? from_selections[c].second + size_diff : from_selections[c].second; + to_selections.write[c].second = to_selections[c].first == i ? to_selections[c].second + size_diff : to_selections[c].second; + } + + // Calculate new line. + line = line.left(j + ((size_diff < 0) ? size_diff : 0)) + indent_text + line.substr(j + 1); + + space_count = 0; + j += size_diff; + } + + if (line_changed) { + set_line(i, line); + } + } + + if (!changed_indentation) { + return; + } + + // Restore caret states. + for (int c = 0; c < get_caret_count(); c++) { + set_caret_column(caret_columns[c], c == 0, c); + if (from_selections.write[c].first != -1) { + select(from_selections.write[c].first, from_selections.write[c].second, to_selections.write[c].first, to_selections.write[c].second, c); + } + } + merge_overlapping_carets(); + end_complex_operation(); + queue_redraw(); +} + int CodeEdit::_calculate_spaces_till_next_left_indent(int p_column) const { int spaces_till_indent = p_column % indent_size; if (spaces_till_indent == 0) { @@ -1041,7 +1188,7 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) { for (; line_col < cc; line_col++) { char32_t c = line[line_col]; - if (auto_indent_prefixes.has(c)) { + if (auto_indent_prefixes.has(c) && is_in_comment(cl, line_col) == -1) { should_indent = true; indent_char = c; continue; @@ -1222,7 +1369,7 @@ bool CodeEdit::is_drawing_executing_lines_gutter() const { } void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) { - if (draw_breakpoints && breakpoint_icon.is_valid()) { + if (draw_breakpoints && theme_cache.breakpoint_icon.is_valid()) { bool breakpointed = is_line_breakpointed(p_line); bool hovering = p_region.has_point(get_local_mouse_pos()); bool shift_pressed = Input::get_singleton()->is_key_pressed(Key::SHIFT); @@ -1230,18 +1377,18 @@ void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 if (breakpointed || (hovering && !is_dragging_cursor() && !shift_pressed)) { int padding = p_region.size.x / 6; - Color use_color = breakpoint_color; + Color use_color = theme_cache.breakpoint_color; if (hovering && !shift_pressed) { use_color = breakpointed ? use_color.lightened(0.3) : use_color.darkened(0.5); } Rect2 icon_region = p_region; icon_region.position += Point2(padding, padding); icon_region.size -= Point2(padding, padding) * 2; - breakpoint_icon->draw_rect(get_canvas_item(), icon_region, false, use_color); + theme_cache.breakpoint_icon->draw_rect(get_canvas_item(), icon_region, false, use_color); } } - if (draw_bookmarks && bookmark_icon.is_valid()) { + if (draw_bookmarks && theme_cache.bookmark_icon.is_valid()) { bool bookmarked = is_line_bookmarked(p_line); bool hovering = p_region.has_point(get_local_mouse_pos()); bool shift_pressed = Input::get_singleton()->is_key_pressed(Key::SHIFT); @@ -1250,25 +1397,25 @@ void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 int horizontal_padding = p_region.size.x / 2; int vertical_padding = p_region.size.y / 4; - Color use_color = bookmark_color; + Color use_color = theme_cache.bookmark_color; if (hovering && shift_pressed) { use_color = bookmarked ? use_color.lightened(0.3) : use_color.darkened(0.5); } Rect2 icon_region = p_region; icon_region.position += Point2(horizontal_padding, 0); icon_region.size -= Point2(horizontal_padding * 1.1, vertical_padding); - bookmark_icon->draw_rect(get_canvas_item(), icon_region, false, use_color); + theme_cache.bookmark_icon->draw_rect(get_canvas_item(), icon_region, false, use_color); } } - if (draw_executing_lines && is_line_executing(p_line) && executing_line_icon.is_valid()) { + if (draw_executing_lines && is_line_executing(p_line) && theme_cache.executing_line_icon.is_valid()) { int horizontal_padding = p_region.size.x / 10; int vertical_padding = p_region.size.y / 4; Rect2 icon_region = p_region; icon_region.position += Point2(horizontal_padding, vertical_padding); icon_region.size -= Point2(horizontal_padding, vertical_padding) * 2; - executing_line_icon->draw_rect(get_canvas_item(), icon_region, false, executing_line_color); + theme_cache.executing_line_icon->draw_rect(get_canvas_item(), icon_region, false, theme_cache.executing_line_color); } } @@ -1338,7 +1485,7 @@ PackedInt32Array CodeEdit::get_bookmarked_lines() const { return ret; } -// executing lines +// 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); @@ -1392,11 +1539,11 @@ void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 } Ref<TextLine> tl; tl.instantiate(); - tl->add_string(fc, font, font_size); + tl->add_string(fc, theme_cache.font, theme_cache.font_size); int yofs = p_region.position.y + (get_line_height() - tl->get_size().y) / 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; + number_color = theme_cache.line_number_color; } tl->draw(get_canvas_item(), Point2(p_region.position.x, yofs), number_color); } @@ -1424,10 +1571,10 @@ void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_regi p_region.size -= Point2(horizontal_padding, vertical_padding) * 2; if (can_fold_line(p_line)) { - can_fold_icon->draw_rect(get_canvas_item(), p_region, false, folding_color); + theme_cache.can_fold_icon->draw_rect(get_canvas_item(), p_region, false, theme_cache.code_folding_color); return; } - folded_icon->draw_rect(get_canvas_item(), p_region, false, folding_color); + theme_cache.folded_icon->draw_rect(get_canvas_item(), p_region, false, theme_cache.code_folding_color); } /* Line Folding */ @@ -1891,7 +2038,7 @@ void CodeEdit::request_code_completion(bool p_force) { } } -void CodeEdit::add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color, const Ref<Resource> &p_icon, const Variant &p_value) { +void CodeEdit::add_code_completion_option(CodeCompletionKind p_type, const String &p_display_text, const String &p_insert_text, const Color &p_text_color, const Ref<Resource> &p_icon, const Variant &p_value, int p_location) { ScriptLanguage::CodeCompletionOption completion_option; completion_option.kind = (ScriptLanguage::CodeCompletionKind)p_type; completion_option.display = p_display_text; @@ -1899,6 +2046,7 @@ void CodeEdit::add_code_completion_option(CodeCompletionKind p_type, const Strin completion_option.font_color = p_text_color; completion_option.icon = p_icon; completion_option.default_value = p_value; + completion_option.location = p_location; code_completion_option_submitted.push_back(completion_option); } @@ -1923,6 +2071,7 @@ TypedArray<Dictionary> CodeEdit::get_code_completion_options() const { option["insert_text"] = code_completion_options[i].insert_text; option["font_color"] = code_completion_options[i].font_color; option["icon"] = code_completion_options[i].icon; + option["location"] = code_completion_options[i].location; option["default_value"] = code_completion_options[i].default_value; completion_options[i] = option; } @@ -1941,6 +2090,7 @@ Dictionary CodeEdit::get_code_completion_option(int p_index) const { option["insert_text"] = code_completion_options[p_index].insert_text; option["font_color"] = code_completion_options[p_index].font_color; option["icon"] = code_completion_options[p_index].icon; + option["location"] = code_completion_options[p_index].location; option["default_value"] = code_completion_options[p_index].default_value; return option; } @@ -1968,7 +2118,7 @@ void CodeEdit::confirm_code_completion(bool p_replace) { return; } - char32_t caret_last_completion_char; + char32_t caret_last_completion_char = 0; begin_complex_operation(); Vector<int> caret_edit_order = get_caret_index_edit_order(); for (const int &i : caret_edit_order) { @@ -2030,7 +2180,7 @@ void CodeEdit::confirm_code_completion(bool p_replace) { insert_text_at_caret(insert_text.substr(matching_chars), i); } - //* Handle merging of symbols eg strings, brackets. + // Handle merging of symbols eg strings, brackets. const String line = get_line(caret_line); char32_t next_char = line[get_caret_column(i)]; char32_t last_completion_char = insert_text[insert_text.length() - 1]; @@ -2084,6 +2234,7 @@ void CodeEdit::cancel_code_completion() { } code_completion_forced = false; code_completion_active = false; + is_code_completion_drag_started = false; queue_redraw(); } @@ -2166,6 +2317,8 @@ void CodeEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("indent_lines"), &CodeEdit::indent_lines); ClassDB::bind_method(D_METHOD("unindent_lines"), &CodeEdit::unindent_lines); + ClassDB::bind_method(D_METHOD("convert_indent", "from_line", "to_line"), &CodeEdit::convert_indent, DEFVAL(-1), DEFVAL(-1)); + /* 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); @@ -2204,7 +2357,7 @@ void CodeEdit::_bind_methods() { 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 + // 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); @@ -2281,9 +2434,14 @@ void CodeEdit::_bind_methods() { BIND_ENUM_CONSTANT(KIND_FILE_PATH); BIND_ENUM_CONSTANT(KIND_PLAIN_TEXT); + BIND_ENUM_CONSTANT(LOCATION_LOCAL); + BIND_ENUM_CONSTANT(LOCATION_PARENT_MASK); + BIND_ENUM_CONSTANT(LOCATION_OTHER_USER_CODE) + BIND_ENUM_CONSTANT(LOCATION_OTHER); + ClassDB::bind_method(D_METHOD("get_text_for_code_completion"), &CodeEdit::get_text_for_code_completion); ClassDB::bind_method(D_METHOD("request_code_completion", "force"), &CodeEdit::request_code_completion, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("add_code_completion_option", "type", "display_text", "insert_text", "text_color", "icon", "value"), &CodeEdit::add_code_completion_option, DEFVAL(Color(1, 1, 1)), DEFVAL(Ref<Resource>()), DEFVAL(Variant::NIL)); + ClassDB::bind_method(D_METHOD("add_code_completion_option", "type", "display_text", "insert_text", "text_color", "icon", "value", "location"), &CodeEdit::add_code_completion_option, DEFVAL(Color(1, 1, 1)), DEFVAL(Ref<Resource>()), DEFVAL(Variant::NIL), DEFVAL(LOCATION_OTHER)); ClassDB::bind_method(D_METHOD("update_code_completion_options", "force"), &CodeEdit::update_code_completion_options); ClassDB::bind_method(D_METHOD("get_code_completion_options"), &CodeEdit::get_code_completion_options); ClassDB::bind_method(D_METHOD("get_code_completion_option", "index"), &CodeEdit::get_code_completion_option); @@ -2811,6 +2969,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { option["font_color"] = E.font_color; option["icon"] = E.icon; option["default_value"] = E.default_value; + option["location"] = E.location; completion_options_sources[i] = option; i++; } @@ -2834,6 +2993,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { option.insert_text = completion_options[i].get("insert_text"); option.font_color = completion_options[i].get("font_color"); option.icon = completion_options[i].get("icon"); + option.location = completion_options[i].get("location"); option.default_value = completion_options[i].get("default_value"); int offset = 0; @@ -2841,13 +3001,13 @@ void CodeEdit::_filter_code_completion_candidates_impl() { offset = line_height; } - if (font.is_valid()) { - max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); + if (theme_cache.font.is_valid()) { + max_width = MAX(max_width, theme_cache.font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + offset); } code_completion_options.push_back(option); } - code_completion_longest_line = MIN(max_width, code_completion_max_width * font_size); + code_completion_longest_line = MIN(max_width, theme_cache.code_completion_max_width * theme_cache.font_size); code_completion_current_selected = 0; code_completion_force_item_center = -1; code_completion_active = true; @@ -2920,29 +3080,28 @@ void CodeEdit::_filter_code_completion_candidates_impl() { } /* Filter Options. */ - /* For now handle only tradional quoted strings. */ + /* For now handle only traditional quoted strings. */ bool single_quote = in_string != -1 && first_quote_col > 0 && delimiters[in_string].start_key == "'"; code_completion_options.clear(); code_completion_base = string_to_complete; - Vector<ScriptLanguage::CodeCompletionOption> completion_options_casei; - Vector<ScriptLanguage::CodeCompletionOption> completion_options_substr; - Vector<ScriptLanguage::CodeCompletionOption> completion_options_substr_casei; - Vector<ScriptLanguage::CodeCompletionOption> completion_options_subseq; - Vector<ScriptLanguage::CodeCompletionOption> completion_options_subseq_casei; + /* Don't autocomplete setting numerical values. */ + if (code_completion_base.is_numeric()) { + cancel_code_completion(); + return; + } int max_width = 0; String string_to_complete_lower = string_to_complete.to_lower(); + for (ScriptLanguage::CodeCompletionOption &option : code_completion_option_sources) { + option.matches.clear(); if (single_quote && option.display.is_quoted()) { option.display = option.display.unquote().quote("'"); } - int offset = 0; - if (option.default_value.get_type() == Variant::COLOR) { - offset = line_height; - } + int offset = option.default_value.get_type() == Variant::COLOR ? line_height : 0; if (in_string != -1) { String quote = single_quote ? "'" : "\""; @@ -2955,146 +3114,81 @@ void CodeEdit::_filter_code_completion_candidates_impl() { } if (string_to_complete.length() == 0) { + option.get_option_characteristics(string_to_complete); code_completion_options.push_back(option); - if (font.is_valid()) { - max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); + if (theme_cache.font.is_valid()) { + max_width = MAX(max_width, theme_cache.font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + offset); } continue; } - /* This code works the same as: + String target_lower = option.display.to_lower(); + const char32_t *string_to_complete_char_lower = &string_to_complete_lower[0]; + const char32_t *target_char_lower = &target_lower[0]; - if (option.display.begins_with(s)) { - completion_options.push_back(option); - } else if (option.display.to_lower().begins_with(s.to_lower())) { - completion_options_casei.push_back(option); - } else if (s.is_subsequence_of(option.display)) { - completion_options_subseq.push_back(option); - } else if (s.is_subsequence_ofn(option.display)) { - completion_options_subseq_casei.push_back(option); - } - - But is more performant due to being inlined and looping over the characters only once - */ - - String display_lower = option.display.to_lower(); - - const char32_t *ssq = &string_to_complete[0]; - const char32_t *ssq_lower = &string_to_complete_lower[0]; - - const char32_t *tgt = &option.display[0]; - const char32_t *tgt_lower = &display_lower[0]; - - const char32_t *sst = &string_to_complete[0]; - const char32_t *sst_lower = &display_lower[0]; - - Vector<Pair<int, int>> ssq_matches; - int ssq_match_start = 0; - int ssq_match_len = 0; - - Vector<Pair<int, int>> ssq_lower_matches; - int ssq_lower_match_start = 0; - int ssq_lower_match_len = 0; - - int sst_start = -1; - int sst_lower_start = -1; - - for (int i = 0; *tgt; tgt++, tgt_lower++, i++) { - // Check substring. - if (*sst == *tgt) { - sst++; - if (sst_start == -1) { - sst_start = i; - } - } else if (sst_start != -1 && *sst) { - sst = &string_to_complete[0]; - sst_start = -1; + Vector<Vector<Pair<int, int>>> all_possible_subsequence_matches; + for (int i = 0; *target_char_lower; i++, target_char_lower++) { + if (*target_char_lower == *string_to_complete_char_lower) { + all_possible_subsequence_matches.push_back({ { i, 1 } }); } + } + string_to_complete_char_lower++; - // Check subsequence. - if (*ssq == *tgt) { - ssq++; - if (ssq_match_len == 0) { - ssq_match_start = i; + for (int i = 1; *string_to_complete_char_lower && (all_possible_subsequence_matches.size() > 0); i++, string_to_complete_char_lower++) { + // find all occurrences of ssq_lower to avoid looking everywhere each time + Vector<int> all_ocurence; + for (int j = i; j < target_lower.length(); j++) { + if (target_lower[j] == *string_to_complete_char_lower) { + all_ocurence.push_back(j); } - ssq_match_len++; - } else if (ssq_match_len > 0) { - ssq_matches.push_back(Pair<int, int>(ssq_match_start, ssq_match_len)); - ssq_match_len = 0; } - - // Check lower substring. - if (*sst_lower == *tgt) { - sst_lower++; - if (sst_lower_start == -1) { - sst_lower_start = i; + Vector<Vector<Pair<int, int>>> next_subsequence_matches; + for (Vector<Pair<int, int>> &subsequence_matches : all_possible_subsequence_matches) { + Pair<int, int> match_last_segment = subsequence_matches[subsequence_matches.size() - 1]; + int next_index = match_last_segment.first + match_last_segment.second; + // get the last index from current sequence + // and look for next char starting from that index + if (target_lower[next_index] == *string_to_complete_char_lower) { + Vector<Pair<int, int>> new_matches = subsequence_matches; + new_matches.write[new_matches.size() - 1].second++; + next_subsequence_matches.push_back(new_matches); } - } else if (sst_lower_start != -1 && *sst_lower) { - sst_lower = &string_to_complete[0]; - sst_lower_start = -1; - } - - // Check lower subsequence. - if (*ssq_lower == *tgt_lower) { - ssq_lower++; - if (ssq_lower_match_len == 0) { - ssq_lower_match_start = i; + for (int index : all_ocurence) { + if (index > next_index) { + Vector<Pair<int, int>> new_matches = subsequence_matches; + new_matches.push_back({ index, 1 }); + next_subsequence_matches.push_back(new_matches); + } } - ssq_lower_match_len++; - } else if (ssq_lower_match_len > 0) { - ssq_lower_matches.push_back(Pair<int, int>(ssq_lower_match_start, ssq_lower_match_len)); - ssq_lower_match_len = 0; } - } - - /* Matched the whole subsequence in s. */ - if (!*ssq) { // Matched the whole subsequence in s. - option.matches.clear(); - - if (sst_start == 0) { // Matched substring in beginning of s. - option.matches.push_back(Pair<int, int>(sst_start, string_to_complete.length())); - code_completion_options.push_back(option); - } else if (sst_start > 0) { // Matched substring in s. - option.matches.push_back(Pair<int, int>(sst_start, string_to_complete.length())); - completion_options_substr.push_back(option); - } else { - if (ssq_match_len > 0) { - ssq_matches.push_back(Pair<int, int>(ssq_match_start, ssq_match_len)); + all_possible_subsequence_matches = next_subsequence_matches; + } + // go through all possible matches to get the best one as defined by CodeCompletionOptionCompare + if (all_possible_subsequence_matches.size() > 0) { + option.matches = all_possible_subsequence_matches[0]; + option.get_option_characteristics(string_to_complete); + all_possible_subsequence_matches = all_possible_subsequence_matches.slice(1); + if (all_possible_subsequence_matches.size() > 0) { + CodeCompletionOptionCompare compare; + ScriptLanguage::CodeCompletionOption compared_option = option; + compared_option.clear_characteristics(); + for (Vector<Pair<int, int>> &matches : all_possible_subsequence_matches) { + compared_option.matches = matches; + compared_option.get_option_characteristics(string_to_complete); + if (compare(compared_option, option)) { + option = compared_option; + compared_option.clear_characteristics(); + } } - option.matches.append_array(ssq_matches); - completion_options_subseq.push_back(option); - } - if (font.is_valid()) { - max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); } - } else if (!*ssq_lower) { // Matched the whole subsequence in s_lower. - option.matches.clear(); - if (sst_lower_start == 0) { // Matched substring in beginning of s_lower. - option.matches.push_back(Pair<int, int>(sst_lower_start, string_to_complete.length())); - completion_options_casei.push_back(option); - } else if (sst_lower_start > 0) { // Matched substring in s_lower. - option.matches.push_back(Pair<int, int>(sst_lower_start, string_to_complete.length())); - completion_options_substr_casei.push_back(option); - } else { - if (ssq_lower_match_len > 0) { - ssq_lower_matches.push_back(Pair<int, int>(ssq_lower_match_start, ssq_lower_match_len)); - } - option.matches.append_array(ssq_lower_matches); - completion_options_subseq_casei.push_back(option); - } - if (font.is_valid()) { - max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); + code_completion_options.push_back(option); + if (theme_cache.font.is_valid()) { + max_width = MAX(max_width, theme_cache.font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + offset); } } } - code_completion_options.append_array(completion_options_casei); - code_completion_options.append_array(completion_options_substr); - code_completion_options.append_array(completion_options_substr_casei); - code_completion_options.append_array(completion_options_subseq); - code_completion_options.append_array(completion_options_subseq_casei); - /* No options to complete, cancel. */ if (code_completion_options.size() == 0) { cancel_code_completion(); @@ -3107,7 +3201,9 @@ void CodeEdit::_filter_code_completion_candidates_impl() { return; } - code_completion_longest_line = MIN(max_width, code_completion_max_width * font_size); + code_completion_options.sort_custom<CodeCompletionOptionCompare>(); + + code_completion_longest_line = MIN(max_width, theme_cache.code_completion_max_width * theme_cache.font_size); code_completion_current_selected = 0; code_completion_force_item_center = -1; code_completion_active = true; @@ -3143,8 +3239,8 @@ void CodeEdit::_text_changed() { line_number_digits++; } - if (font.is_valid()) { - set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', font_size).width); + if (theme_cache.font.is_valid()) { + set_gutter_width(line_number_gutter, (line_number_digits + 1) * theme_cache.font->get_char_size('0', theme_cache.font_size).width); } lc = get_line_count(); @@ -3235,3 +3331,24 @@ CodeEdit::CodeEdit() { CodeEdit::~CodeEdit() { } + +// Return true if l should come before r +bool CodeCompletionOptionCompare::operator()(const ScriptLanguage::CodeCompletionOption &l, const ScriptLanguage::CodeCompletionOption &r) const { + TypedArray<int> lcharac = l.get_option_cached_characteristics(); + TypedArray<int> rcharac = r.get_option_cached_characteristics(); + + if (lcharac != rcharac) { + return lcharac < rcharac; + } + + // to get here they need to have the same size so we can take the size of whichever we want + for (int i = 0; i < l.matches.size(); ++i) { + if (l.matches[i].first != r.matches[i].first) { + return l.matches[i].first < r.matches[i].first; + } + if (l.matches[i].second != r.matches[i].second) { + return l.matches[i].second > r.matches[i].second; + } + } + return l.display.naturalnocasecmp_to(r.display) < 0; +} |