Kaynağa Gözat

Avoid resetting the code completion popup excessively

DESKTOP-UT43QTQ\Garrigan-Desktop 1 yıl önce
ebeveyn
işleme
303d0994e9
3 değiştirilmiş dosya ile 97 ekleme ve 10 silme
  1. 35 10
      scene/gui/code_edit.cpp
  2. 1 0
      scene/gui/code_edit.h
  3. 61 0
      tests/scene/test_code_edit.h

+ 35 - 10
scene/gui/code_edit.cpp

@@ -3143,7 +3143,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
 	int line_height = get_line_height();
 
 	if (GDVIRTUAL_IS_OVERRIDDEN(_filter_code_completion_candidates)) {
-		code_completion_options.clear();
+		Vector<ScriptLanguage::CodeCompletionOption> code_completion_options_new;
 		code_completion_base = "";
 
 		/* Build options argument. */
@@ -3193,11 +3193,15 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
 			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_options_new.push_back(option);
 		}
 
+		if (_should_reset_selected_option_for_new_options(code_completion_options_new)) {
+			code_completion_current_selected = 0;
+		}
+		code_completion_options = code_completion_options_new;
+
 		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;
 		queue_redraw();
@@ -3272,7 +3276,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
 	/* 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();
+	Vector<ScriptLanguage::CodeCompletionOption> code_completion_options_new;
 	code_completion_base = string_to_complete;
 
 	/* Don't autocomplete setting numerical values. */
@@ -3304,7 +3308,8 @@ 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);
+			code_completion_options_new.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);
 			}
@@ -3379,7 +3384,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
 				}
 			}
 
-			code_completion_options.push_back(option);
+			code_completion_options_new.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);
 			}
@@ -3387,26 +3392,46 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
 	}
 
 	/* No options to complete, cancel. */
-	if (code_completion_options.size() == 0) {
+	if (code_completion_options_new.size() == 0) {
 		cancel_code_completion();
 		return;
 	}
 
 	/* A perfect match, stop completion. */
-	if (code_completion_options.size() == 1 && string_to_complete == code_completion_options[0].display) {
+	if (code_completion_options_new.size() == 1 && string_to_complete == code_completion_options_new[0].display) {
 		cancel_code_completion();
 		return;
 	}
 
-	code_completion_options.sort_custom<CodeCompletionOptionCompare>();
+	code_completion_options_new.sort_custom<CodeCompletionOptionCompare>();
+	if (_should_reset_selected_option_for_new_options(code_completion_options_new)) {
+		code_completion_current_selected = 0;
+	}
+	code_completion_options = code_completion_options_new;
 
 	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;
 	queue_redraw();
 }
 
+// Assumes both the new_options and the code_completion_options are sorted.
+bool CodeEdit::_should_reset_selected_option_for_new_options(const Vector<ScriptLanguage::CodeCompletionOption> &p_new_options) {
+	if (code_completion_current_selected >= p_new_options.size()) {
+		return true;
+	}
+
+	for (int i = 0; i < code_completion_options.size() && i < p_new_options.size(); i++) {
+		if (i > code_completion_current_selected) {
+			return false;
+		}
+		if (code_completion_options[i].display != p_new_options[i].display) {
+			return true;
+		}
+	}
+	return false;
+}
+
 void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) {
 	_update_delimiter_cache(p_from_line, p_to_line);
 

+ 1 - 0
scene/gui/code_edit.h

@@ -222,6 +222,7 @@ private:
 
 	void _update_scroll_selected_line(float p_mouse_y);
 	void _filter_code_completion_candidates_impl();
+	bool _should_reset_selected_option_for_new_options(const Vector<ScriptLanguage::CodeCompletionOption> &p_new_options);
 
 	/* Line length guidelines */
 	TypedArray<int> line_length_guideline_columns;

+ 61 - 0
tests/scene/test_code_edit.h

@@ -3725,6 +3725,67 @@ TEST_CASE("[SceneTree][CodeEdit] completion") {
 		CHECK(code_edit->get_line(0) == "sstest");
 	}
 
+	SUBCASE("[CodeEdit] autocomplete currently selected option") {
+		code_edit->set_code_completion_enabled(true);
+		REQUIRE(code_edit->is_code_completion_enabled());
+
+		// Initially select item 0.
+		code_edit->insert_text_at_caret("te");
+		code_edit->set_caret_column(2);
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te1", "te1");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+		code_edit->update_code_completion_options();
+		CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 0, "Initially selected item should be 0.");
+
+		// After adding later options shouldn't update selection.
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te1", "te1");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4"); // Added te4.
+		code_edit->update_code_completion_options();
+		CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 0, "Adding later options shouldn't update selection.");
+
+		code_edit->set_code_completion_selected_index(2);
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te1", "te1");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te5", "te5"); // Added te5.
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te6", "te6"); // Added te6.
+		code_edit->update_code_completion_options();
+		CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 2, "Adding later options shouldn't update selection.");
+
+		// Removing elements after selected element shouldn't update selection.
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te1", "te1");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te5", "te5"); // Removed te6.
+		code_edit->update_code_completion_options();
+		CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 2, "Removing elements after selected element shouldn't update selection.");
+
+		// Changing elements after selected element shouldn't update selection.
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te1", "te1");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te6", "te6"); // Changed te5->te6.
+		code_edit->update_code_completion_options();
+		CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 2, "Changing elements after selected element shouldn't update selection.");
+
+		// Changing elements before selected element should reset selection.
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te2", "te2"); // Changed te1->te2.
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te6", "te6");
+		code_edit->update_code_completion_options();
+		CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 0, "Changing elements before selected element should reset selection.");
+
+		// Removing elements before selected element should reset selection.
+		code_edit->set_code_completion_selected_index(2);
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te3", "te3"); // Removed te2.
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te4", "te4");
+		code_edit->add_code_completion_option(CodeEdit::CodeCompletionKind::KIND_VARIABLE, "te6", "te6");
+		code_edit->update_code_completion_options();
+		CHECK_MESSAGE(code_edit->get_code_completion_selected_index() == 0, "Removing elements before selected element should reset selection.");
+	}
+
 	memdelete(code_edit);
 }