Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sort code autocompletion options by similarity based on input #65655

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions core/object/script_language.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,9 +322,8 @@ class ScriptLanguage : public Object {
};

enum CodeCompletionLocation {
LOCATION_LOCAL = 0,
LOCATION_PARENT_MASK = 1 << 8,
LOCATION_OTHER_USER_CODE = 1 << 9,
LOCATION_LOCAL = 1 << 0,
LOCATION_BASE = 1 << 1,
LOCATION_OTHER = 1 << 10,
};

Expand All @@ -346,6 +345,13 @@ class ScriptLanguage : public Object {
kind = p_kind;
location = p_location;
}

bool is_basic_identifier() const {
return (kind == ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE ||
kind == ScriptLanguage::CODE_COMPLETION_KIND_MEMBER ||
kind == ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION ||
kind == ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL);
}
};

virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<CodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; }
Expand Down
5 changes: 3 additions & 2 deletions core/object/script_language_extension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,9 @@ void ScriptLanguageExtension::_bind_methods() {
BIND_ENUM_CONSTANT(LOOKUP_RESULT_MAX);

BIND_ENUM_CONSTANT(LOCATION_LOCAL);
BIND_ENUM_CONSTANT(LOCATION_PARENT_MASK);
BIND_ENUM_CONSTANT(LOCATION_OTHER_USER_CODE);
BIND_ENUM_CONSTANT(LOCATION_BASE);
//BIND_ENUM_CONSTANT(LOCATION_PARENT_MASK);
//BIND_ENUM_CONSTANT(LOCATION_OTHER_USER_CODE);
BIND_ENUM_CONSTANT(LOCATION_OTHER);

BIND_ENUM_CONSTANT(CODE_COMPLETION_KIND_CLASS);
Expand Down
2 changes: 1 addition & 1 deletion editor/code_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ void CodeTextEditor::_complete_request() {
} else if (e.insert_text.begins_with("#") || e.insert_text.begins_with("//")) {
font_color = completion_comment_color;
}
text_editor->add_code_completion_option((CodeEdit::CodeCompletionKind)e.kind, e.display, e.insert_text, font_color, _get_completion_icon(e), e.default_value);
text_editor->add_code_completion_option((CodeEdit::CodeCompletionKind)e.kind, e.display, e.insert_text, font_color, _get_completion_icon(e), e.default_value, (CodeEdit::CodeCompletionLocation)e.location);
}
text_editor->update_code_completion_options(forced);
}
Expand Down
3 changes: 2 additions & 1 deletion editor/plugins/script_text_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,8 @@ void ScriptTextEditor::_code_complete_script(const String &p_code, List<ScriptLa
String hint;
Error err = script->get_language()->complete_code(p_code, script->get_path(), base, r_options, r_force, hint);

r_options->sort_custom_inplace<CodeCompletionOptionCompare>();
// Editor will sort later, no point doing it now?
//r_options->sort_custom_inplace<CodeCompletionOptionCompare>();

if (err == OK) {
code_editor->get_text_editor()->set_code_hint(hint);
Expand Down
47 changes: 0 additions & 47 deletions editor/plugins/script_text_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,51 +259,4 @@ class ScriptTextEditor : public ScriptEditorBase {
~ScriptTextEditor();
};

const int KIND_COUNT = 10;
// The order in which to sort code completion options.
const ScriptLanguage::CodeCompletionKind KIND_SORT_ORDER[KIND_COUNT] = {
ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE,
ScriptLanguage::CODE_COMPLETION_KIND_MEMBER,
ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION,
ScriptLanguage::CODE_COMPLETION_KIND_ENUM,
ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL,
ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT,
ScriptLanguage::CODE_COMPLETION_KIND_CLASS,
ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH,
ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH,
ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT,
};

// The custom comparer which will sort completion options.
struct CodeCompletionOptionCompare {
_FORCE_INLINE_ bool operator()(const ScriptLanguage::CodeCompletionOption &l, const ScriptLanguage::CodeCompletionOption &r) const {
if (l.location == r.location) {
// If locations are same, sort on kind
if (l.kind == r.kind) {
// If kinds are same, sort alphanumeric
return l.display < r.display;
}

// Sort kinds based on the const sorting array defined above. Lower index = higher priority.
int l_index = -1;
int r_index = -1;
for (int i = 0; i < KIND_COUNT; i++) {
const ScriptLanguage::CodeCompletionKind kind = KIND_SORT_ORDER[i];
l_index = kind == l.kind ? i : l_index;
r_index = kind == r.kind ? i : r_index;

if (l_index != -1 && r_index != -1) {
return l_index < r_index;
}
}

// This return should never be hit unless something goes wrong.
// l and r should always have a Kind which is in the sort order array.
return l.display < r.display;
}

return l.location < r.location;
}
};

#endif // SCRIPT_TEXT_EDITOR_H
127 changes: 18 additions & 109 deletions modules/gdscript/gdscript_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,89 +495,6 @@ struct GDScriptCompletionIdentifier {
const GDScriptParser::ExpressionNode *assigned_expression = nullptr;
};

// LOCATION METHODS
// These methods are used to populate the `CodeCompletionOption::location` integer.
// For these methods, the location is based on the depth in the inheritance chain that the property
// appears. For example, if you are completing code in a class that inherits Node2D, a property found on Node2D
// will have a "better" (lower) location "score" than a property that is found on CanvasItem.

static int _get_property_location(StringName p_class, StringName p_property) {
if (!ClassDB::has_property(p_class, p_property)) {
return ScriptLanguage::LOCATION_OTHER;
}

int depth = 0;
StringName class_test = p_class;
while (class_test && !ClassDB::has_property(class_test, p_property, true)) {
class_test = ClassDB::get_parent_class(class_test);
depth++;
}

return depth | ScriptLanguage::LOCATION_PARENT_MASK;
}

static int _get_constant_location(StringName p_class, StringName p_constant) {
if (!ClassDB::has_integer_constant(p_class, p_constant)) {
return ScriptLanguage::LOCATION_OTHER;
}

int depth = 0;
StringName class_test = p_class;
while (class_test && !ClassDB::has_integer_constant(class_test, p_constant, true)) {
class_test = ClassDB::get_parent_class(class_test);
depth++;
}

return depth | ScriptLanguage::LOCATION_PARENT_MASK;
}

static int _get_signal_location(StringName p_class, StringName p_signal) {
if (!ClassDB::has_signal(p_class, p_signal)) {
return ScriptLanguage::LOCATION_OTHER;
}

int depth = 0;
StringName class_test = p_class;
while (class_test && !ClassDB::has_signal(class_test, p_signal, true)) {
class_test = ClassDB::get_parent_class(class_test);
depth++;
}

return depth | ScriptLanguage::LOCATION_PARENT_MASK;
}

static int _get_method_location(StringName p_class, StringName p_method) {
if (!ClassDB::has_method(p_class, p_method)) {
return ScriptLanguage::LOCATION_OTHER;
}

int depth = 0;
StringName class_test = p_class;
while (class_test && !ClassDB::has_method(class_test, p_method, true)) {
class_test = ClassDB::get_parent_class(class_test);
depth++;
}

return depth | ScriptLanguage::LOCATION_PARENT_MASK;
}

static int _get_enum_constant_location(StringName p_class, StringName p_enum_constant) {
if (!ClassDB::get_integer_constant_enum(p_class, p_enum_constant)) {
return ScriptLanguage::LOCATION_OTHER;
}

int depth = 0;
StringName class_test = p_class;
while (class_test && !ClassDB::get_integer_constant_enum(class_test, p_enum_constant, true)) {
class_test = ClassDB::get_parent_class(class_test);
depth++;
}

return depth | ScriptLanguage::LOCATION_PARENT_MASK;
}

// END LOCATION METHODS

static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) {
if (p_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
String enum_name = p_info.class_name;
Expand Down Expand Up @@ -872,7 +789,7 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
for (const StringName &E : global_classes) {
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
r_result.insert(option.display, option);
}

Expand All @@ -884,24 +801,25 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
if (!info.is_singleton || info.path.get_extension().to_lower() != "gd") {
continue;
}
ScriptLanguage::CodeCompletionOption option(info.name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);
ScriptLanguage::CodeCompletionOption option(info.name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
r_result.insert(option.display, option);
}
}

static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth = 0) {
for (int i = 0; i < p_suite->locals.size(); i++) {
ScriptLanguage::CodeCompletionOption option;
ScriptLanguage::CodeCompletionLocation location = p_recursion_depth == 0 ? ScriptLanguage::LOCATION_LOCAL : ScriptLanguage::LOCATION_BASE;
if (p_suite->locals[i].type == GDScriptParser::SuiteNode::Local::CONSTANT) {
option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, ScriptLanguage::LOCATION_LOCAL);
option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);
option.default_value = p_suite->locals[i].constant->initializer->reduced_value;
} else {
option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE, ScriptLanguage::LOCATION_LOCAL);
option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE, location);
}
r_result.insert(option.display, option);
}
if (p_suite->parent_block) {
_find_identifiers_in_suite(p_suite->parent_block, r_result);
_find_identifiers_in_suite(p_suite->parent_block, r_result, p_recursion_depth + 1);
}
}

Expand All @@ -916,7 +834,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
int classes_processed = 0;
while (clss) {
for (int i = 0; i < clss->members.size(); i++) {
const int location = (classes_processed + p_recursion_depth) | ScriptLanguage::LOCATION_PARENT_MASK;
const int location = p_recursion_depth + classes_processed <= 1 ? ScriptLanguage::LOCATION_LOCAL : ScriptLanguage::LOCATION_BASE;
const GDScriptParser::ClassNode::Member &member = clss->members[i];
ScriptLanguage::CodeCompletionOption option;
switch (member.type) {
Expand Down Expand Up @@ -1020,24 +938,21 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<PropertyInfo> members;
scr->get_script_property_list(&members);
for (const PropertyInfo &E : members) {
int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.class_name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::LOCATION_BASE);
r_result.insert(option.display, option);
}
}
HashMap<StringName, Variant> constants;
scr->get_constants(&constants);
for (const KeyValue<StringName, Variant> &E : constants) {
int location = p_recursion_depth + _get_constant_location(scr->get_class_name(), E.key);
ScriptLanguage::CodeCompletionOption option(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);
ScriptLanguage::CodeCompletionOption option(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, ScriptLanguage::LOCATION_BASE);
r_result.insert(option.display, option);
}

List<MethodInfo> signals;
scr->get_script_signal_list(&signals);
for (const MethodInfo &E : signals) {
int location = p_recursion_depth + _get_signal_location(scr->get_class_name(), E.name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, ScriptLanguage::LOCATION_BASE);
r_result.insert(option.display, option);
}
}
Expand All @@ -1048,8 +963,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
if (E.name.begins_with("@")) {
continue;
}
int location = p_recursion_depth + _get_method_location(scr->get_class_name(), E.name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_BASE);
if (E.arguments.size()) {
option.insert_text += "(";
} else {
Expand Down Expand Up @@ -1079,16 +993,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<String> constants;
ClassDB::get_integer_constant_list(type, &constants);
for (const String &E : constants) {
int location = p_recursion_depth + _get_constant_location(type, StringName(E));
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, ScriptLanguage::LOCATION_BASE);
r_result.insert(option.display, option);
}

List<MethodInfo> signals;
ClassDB::get_signal_list(type, &signals);
for (const MethodInfo &E : signals) {
int location = p_recursion_depth + _get_signal_location(type, StringName(E.name));
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, ScriptLanguage::LOCATION_BASE);
r_result.insert(option.display, option);
}

Expand All @@ -1102,8 +1014,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
if (E.name.contains("/")) {
continue;
}
int location = p_recursion_depth + _get_property_location(type, E.class_name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::LOCATION_BASE);
r_result.insert(option.display, option);
}
}
Expand All @@ -1120,8 +1031,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
if (E.name.begins_with("_")) {
continue;
}
int location = p_recursion_depth + _get_method_location(type, E.name);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);
ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_BASE);
if (E.arguments.size()) {
option.insert_text += "(";
} else {
Expand Down Expand Up @@ -1283,7 +1193,7 @@ static void _find_identifiers(const GDScriptParser::CompletionContext &p_context
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
for (const StringName &E : global_classes) {
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);
ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER);
r_result.insert(option.display, option);
}
}
Expand Down Expand Up @@ -2404,8 +2314,7 @@ static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_co
ClassDB::get_enum_constants(class_name, enum_name, &enum_constants);
for (const StringName &E : enum_constants) {
String candidate = class_name + "." + E;
int location = _get_enum_constant_location(class_name, E);
ScriptLanguage::CodeCompletionOption option(candidate, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);
ScriptLanguage::CodeCompletionOption option(candidate, ScriptLanguage::CODE_COMPLETION_KIND_ENUM);
r_result.insert(option.display, option);
}
}
Expand Down
Loading