You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-10 13:00:37 +00:00
GDScript: Fix autocompletion issues with nested types
This commit is contained in:
@@ -1057,6 +1057,7 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Unify with _find_identifiers_in_class.
|
||||||
if (p_context.current_class) {
|
if (p_context.current_class) {
|
||||||
if (!p_inherit_only && p_context.current_class->base_type.is_set()) {
|
if (!p_inherit_only && p_context.current_class->base_type.is_set()) {
|
||||||
// Native enums from base class
|
// Native enums from base class
|
||||||
@@ -1067,25 +1068,27 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
|
|||||||
r_result.insert(option.display, option);
|
r_result.insert(option.display, option);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check current class for potential types
|
// Check current class for potential types.
|
||||||
|
// TODO: Also check classes the current class inherits from.
|
||||||
const GDScriptParser::ClassNode *current = p_context.current_class;
|
const GDScriptParser::ClassNode *current = p_context.current_class;
|
||||||
|
int location_offset = 0;
|
||||||
while (current) {
|
while (current) {
|
||||||
for (int i = 0; i < current->members.size(); i++) {
|
for (int i = 0; i < current->members.size(); i++) {
|
||||||
const GDScriptParser::ClassNode::Member &member = current->members[i];
|
const GDScriptParser::ClassNode::Member &member = current->members[i];
|
||||||
switch (member.type) {
|
switch (member.type) {
|
||||||
case GDScriptParser::ClassNode::Member::CLASS: {
|
case GDScriptParser::ClassNode::Member::CLASS: {
|
||||||
ScriptLanguage::CodeCompletionOption option(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL);
|
ScriptLanguage::CodeCompletionOption option(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL + location_offset);
|
||||||
r_result.insert(option.display, option);
|
r_result.insert(option.display, option);
|
||||||
} break;
|
} break;
|
||||||
case GDScriptParser::ClassNode::Member::ENUM: {
|
case GDScriptParser::ClassNode::Member::ENUM: {
|
||||||
if (!p_inherit_only) {
|
if (!p_inherit_only) {
|
||||||
ScriptLanguage::CodeCompletionOption option(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, ScriptLanguage::LOCATION_LOCAL);
|
ScriptLanguage::CodeCompletionOption option(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, ScriptLanguage::LOCATION_LOCAL + location_offset);
|
||||||
r_result.insert(option.display, option);
|
r_result.insert(option.display, option);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case GDScriptParser::ClassNode::Member::CONSTANT: {
|
case GDScriptParser::ClassNode::Member::CONSTANT: {
|
||||||
if (member.constant->get_datatype().is_meta_type && p_context.current_class->outer != nullptr) {
|
if (member.constant->get_datatype().is_meta_type) {
|
||||||
ScriptLanguage::CodeCompletionOption option(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL);
|
ScriptLanguage::CodeCompletionOption option(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL + location_offset);
|
||||||
r_result.insert(option.display, option);
|
r_result.insert(option.display, option);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -1093,6 +1096,7 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
location_offset += 1;
|
||||||
current = current->outer;
|
current = current->outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1162,7 +1166,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
|
|||||||
option = ScriptLanguage::CodeCompletionOption(member.variable->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
|
option = ScriptLanguage::CodeCompletionOption(member.variable->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);
|
||||||
break;
|
break;
|
||||||
case GDScriptParser::ClassNode::Member::CONSTANT:
|
case GDScriptParser::ClassNode::Member::CONSTANT:
|
||||||
if (p_types_only || p_only_functions) {
|
if ((p_types_only && !member.constant->datatype.is_meta_type) || p_only_functions) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (r_result.has(member.constant->identifier->name)) {
|
if (r_result.has(member.constant->identifier->name)) {
|
||||||
@@ -1409,6 +1413,10 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
|
|||||||
return;
|
return;
|
||||||
} break;
|
} break;
|
||||||
case GDScriptParser::DataType::ENUM: {
|
case GDScriptParser::DataType::ENUM: {
|
||||||
|
if (p_types_only) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String type_str = base_type.native_type;
|
String type_str = base_type.native_type;
|
||||||
|
|
||||||
if (type_str.contains_char('.')) {
|
if (type_str.contains_char('.')) {
|
||||||
@@ -2484,6 +2492,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
|
|||||||
r_type.type.is_meta_type = true;
|
r_type.type.is_meta_type = true;
|
||||||
r_type.value = Variant();
|
r_type.value = Variant();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -2645,6 +2654,16 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ClassDB::has_enum(class_name, p_identifier)) {
|
||||||
|
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
||||||
|
r_type.type.kind = GDScriptParser::DataType::ENUM;
|
||||||
|
r_type.type.enum_type = p_identifier;
|
||||||
|
r_type.type.is_constant = true;
|
||||||
|
r_type.type.is_meta_type = true;
|
||||||
|
r_type.type.native_type = String(class_name) + "." + p_identifier;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} break;
|
} break;
|
||||||
case GDScriptParser::DataType::BUILTIN: {
|
case GDScriptParser::DataType::BUILTIN: {
|
||||||
@@ -3543,44 +3562,27 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
|
|||||||
if (!completion_context.current_class) {
|
if (!completion_context.current_class) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(completion_context.node);
|
const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(completion_context.node);
|
||||||
bool found = true;
|
ERR_FAIL_INDEX_V_MSG(completion_context.type_chain_index - 1, type->type_chain.size(), Error::ERR_BUG, "Could not complete type argument with out of bounds type chain index.");
|
||||||
|
|
||||||
GDScriptCompletionIdentifier base;
|
GDScriptCompletionIdentifier base;
|
||||||
base.type.kind = GDScriptParser::DataType::CLASS;
|
|
||||||
base.type.type_source = GDScriptParser::DataType::INFERRED;
|
|
||||||
base.type.is_constant = true;
|
|
||||||
|
|
||||||
if (completion_context.current_argument == 1) {
|
if (_guess_identifier_type(completion_context, type->type_chain[0], base)) {
|
||||||
StringName type_name = type->type_chain[0]->name;
|
bool found = true;
|
||||||
|
for (int i = 1; i < completion_context.type_chain_index; i++) {
|
||||||
if (ClassDB::class_exists(type_name)) {
|
|
||||||
base.type.kind = GDScriptParser::DataType::NATIVE;
|
|
||||||
base.type.native_type = type_name;
|
|
||||||
} else if (ScriptServer::is_global_class(type_name)) {
|
|
||||||
base.type.kind = GDScriptParser::DataType::SCRIPT;
|
|
||||||
String scr_path = ScriptServer::get_global_class_path(type_name);
|
|
||||||
base.type.script_type = ResourceLoader::load(scr_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (base.type.kind == GDScriptParser::DataType::CLASS) {
|
|
||||||
base.type.class_type = completion_context.current_class;
|
|
||||||
base.value = completion_context.base;
|
|
||||||
|
|
||||||
for (int i = 0; i < completion_context.current_argument; i++) {
|
|
||||||
GDScriptCompletionIdentifier ci;
|
GDScriptCompletionIdentifier ci;
|
||||||
if (!_guess_identifier_type_from_base(completion_context, base, type->type_chain[i]->name, ci)) {
|
found = _guess_identifier_type_from_base(completion_context, base, type->type_chain[i]->name, ci);
|
||||||
found = false;
|
base = ci;
|
||||||
|
if (!found) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
base = ci;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
_find_identifiers_in_base(base, false, true, true, options, 0);
|
_find_identifiers_in_base(base, false, true, true, options, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
r_forced = true;
|
r_forced = true;
|
||||||
} break;
|
} break;
|
||||||
case GDScriptParser::COMPLETION_RESOURCE_PATH: {
|
case GDScriptParser::COMPLETION_RESOURCE_PATH: {
|
||||||
|
|||||||
@@ -1317,7 +1317,10 @@ public:
|
|||||||
FunctionNode *current_function = nullptr;
|
FunctionNode *current_function = nullptr;
|
||||||
SuiteNode *current_suite = nullptr;
|
SuiteNode *current_suite = nullptr;
|
||||||
int current_line = -1;
|
int current_line = -1;
|
||||||
|
union {
|
||||||
int current_argument = -1;
|
int current_argument = -1;
|
||||||
|
int type_chain_index;
|
||||||
|
};
|
||||||
Variant::Type builtin_type = Variant::VARIANT_MAX;
|
Variant::Type builtin_type = Variant::VARIANT_MAX;
|
||||||
Node *node = nullptr;
|
Node *node = nullptr;
|
||||||
Object *base = nullptr;
|
Object *base = nullptr;
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
|
class InnerA:
|
||||||
|
class InnerInnerA:
|
||||||
|
enum EnumOfInnerInnerA {
|
||||||
|
ENUM_VALUE_1,
|
||||||
|
ENUM_VALUE_2,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EnumOfInnerA {
|
||||||
|
ENUM_VALUE_1,
|
||||||
|
ENUM_VALUE_2,
|
||||||
|
}
|
||||||
|
|
||||||
|
signal signal_of_inner_a
|
||||||
|
var property_of_inner_a
|
||||||
|
func func_of_inner_a():
|
||||||
|
pass
|
||||||
|
|
||||||
|
enum EnumOfA {
|
||||||
|
ENUM_VALUE_1,
|
||||||
|
ENUM_VALUE_2,
|
||||||
|
}
|
||||||
|
|
||||||
signal signal_of_a
|
signal signal_of_a
|
||||||
|
|
||||||
var property_of_a
|
var property_of_a
|
||||||
|
|||||||
31
modules/gdscript/tests/scripts/completion/class_b.notest.gd
Normal file
31
modules/gdscript/tests/scripts/completion/class_b.notest.gd
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
extends Node
|
||||||
|
class_name B
|
||||||
|
|
||||||
|
class InnerB:
|
||||||
|
class InnerInnerB:
|
||||||
|
enum EnumOfInnerInnerB {
|
||||||
|
ENUM_VALUE_1,
|
||||||
|
ENUM_VALUE_2,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EnumOfInnerB {
|
||||||
|
ENUM_VALUE_1,
|
||||||
|
ENUM_VALUE_2,
|
||||||
|
}
|
||||||
|
|
||||||
|
signal signal_of_inner_b
|
||||||
|
var property_of_inner_b
|
||||||
|
func func_of_inner_b():
|
||||||
|
pass
|
||||||
|
|
||||||
|
enum EnumOfB {
|
||||||
|
ENUM_VALUE_1,
|
||||||
|
ENUM_VALUE_2,
|
||||||
|
}
|
||||||
|
|
||||||
|
signal signal_of_b
|
||||||
|
|
||||||
|
var property_of_b
|
||||||
|
|
||||||
|
func func_of_b():
|
||||||
|
pass
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
class B:
|
class TestClass:
|
||||||
func to_str(b: int):
|
func to_str(b: int):
|
||||||
return str(b)
|
return str(b)
|
||||||
|
|
||||||
var a: B
|
var a: TestClass
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
a.to_str(10).➡
|
a.to_str(10).➡
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
extends Node
|
|
||||||
|
|
||||||
var test = {
|
|
||||||
t = 1,
|
|
||||||
AutoTranslateMode.➡
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Disabled due to GH-105421
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
var test = {
|
||||||
|
t = 1,
|
||||||
|
AutoTranslateMode.➡
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
[output]
|
||||||
|
include=[
|
||||||
|
{"display": "A"},
|
||||||
|
{"display": "B"},
|
||||||
|
{"display": "LocalInnerClass"},
|
||||||
|
{"display": "LocalInnerEnum"},
|
||||||
|
{"display": "ConnectFlags"},
|
||||||
|
{"display": "int"},
|
||||||
|
{"display": "String"},
|
||||||
|
{"display": "float"},
|
||||||
|
{"display": "Vector2"},
|
||||||
|
{"display": "Vector3"},
|
||||||
|
{"display": "Vector4"},
|
||||||
|
{"display": "Node"},
|
||||||
|
{"display": "Node2D"},
|
||||||
|
]
|
||||||
|
exclude=[
|
||||||
|
{"display": "AInner"},
|
||||||
|
{"display": "LocalInnerInnerEnum"},
|
||||||
|
{"display": "LocalInnerInnerClass"},
|
||||||
|
]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
const A = preload("res://completion/class_a.notest.gd")
|
||||||
|
|
||||||
|
class LocalInnerClass:
|
||||||
|
const AInner = preload("res://completion/class_a.notest.gd")
|
||||||
|
enum LocalInnerInnerEnum {}
|
||||||
|
class LocalInnerInnerClass:
|
||||||
|
pass
|
||||||
|
|
||||||
|
enum LocalInnerEnum {}
|
||||||
|
|
||||||
|
var test_var: A➡
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[output]
|
||||||
|
include=[
|
||||||
|
{"display": "A"},
|
||||||
|
{"display": "AInner"},
|
||||||
|
{"display": "B"},
|
||||||
|
{"display": "LocalInnerClass"},
|
||||||
|
{"display": "LocalInnerInnerClass"},
|
||||||
|
{"display": "LocalInnerEnum"},
|
||||||
|
{"display": "LocalInnerInnerEnum"},
|
||||||
|
{"display": "ConnectFlags"},
|
||||||
|
{"display": "int"},
|
||||||
|
{"display": "String"},
|
||||||
|
{"display": "float"},
|
||||||
|
{"display": "Vector2"},
|
||||||
|
{"display": "Vector3"},
|
||||||
|
{"display": "Vector4"},
|
||||||
|
{"display": "Node"},
|
||||||
|
{"display": "Node2D"},
|
||||||
|
]
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
const A = preload("res://completion/class_a.notest.gd")
|
||||||
|
|
||||||
|
class LocalInnerClass:
|
||||||
|
const AInner = preload("res://completion/class_a.notest.gd")
|
||||||
|
enum LocalInnerInnerEnum {}
|
||||||
|
class LocalInnerInnerClass:
|
||||||
|
pass
|
||||||
|
var test_var: A➡
|
||||||
|
|
||||||
|
enum LocalInnerEnum {}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[output]
|
||||||
|
include=[
|
||||||
|
{"display": "InnerB"},
|
||||||
|
{"display": "EnumOfB"},
|
||||||
|
{"display": "ConnectFlags"},
|
||||||
|
]
|
||||||
|
exclude=[
|
||||||
|
{"display": "int"},
|
||||||
|
{"display": "String"},
|
||||||
|
{"display": "Node2D"},
|
||||||
|
{"display": "B"},
|
||||||
|
]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
var test_var: B.➡
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[output]
|
||||||
|
include=[
|
||||||
|
{"display": "InnerInnerClass"},
|
||||||
|
{"display": "InnerInnerEnum"},
|
||||||
|
{"display": "ConnectFlags"},
|
||||||
|
]
|
||||||
|
exclude=[
|
||||||
|
{"display": "int"},
|
||||||
|
{"display": "String"},
|
||||||
|
{"display": "Node2D"},
|
||||||
|
{"display": "B"},
|
||||||
|
]
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
const A = preload("res://completion/class_a.notest.gd")
|
||||||
|
|
||||||
|
class LocalInnerClass:
|
||||||
|
class InnerInnerClass:
|
||||||
|
pass
|
||||||
|
enum InnerInnerEnum {}
|
||||||
|
|
||||||
|
var test_var: LocalInnerClass.➡
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[output]
|
||||||
|
exclude=[
|
||||||
|
{"display": "TEST_LOCAL_VAL"},
|
||||||
|
{"display": "ConnectFlags"},
|
||||||
|
]
|
||||||
|
exclude=[
|
||||||
|
{"display": "int"},
|
||||||
|
{"display": "String"},
|
||||||
|
{"display": "Node2D"},
|
||||||
|
{"display": "B"},
|
||||||
|
]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
const A = preload("res://completion/class_a.notest.gd")
|
||||||
|
|
||||||
|
enum LocalInnerEnum {
|
||||||
|
TEST_LOCAL_VAL,
|
||||||
|
}
|
||||||
|
|
||||||
|
var test_var: LocalInnerEnum.➡
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[output]
|
||||||
|
include=[
|
||||||
|
{"display": "InnerA"},
|
||||||
|
{"display": "EnumOfA"},
|
||||||
|
{"display": "ConnectFlags"},
|
||||||
|
]
|
||||||
|
exclude=[
|
||||||
|
{"display": "int"},
|
||||||
|
{"display": "String"},
|
||||||
|
{"display": "Node2D"},
|
||||||
|
{"display": "B"},
|
||||||
|
]
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
const A = preload("res://completion/class_a.notest.gd")
|
||||||
|
|
||||||
|
var test_var: A.➡
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[output]
|
||||||
|
include=[
|
||||||
|
{"display": "AInnerInner"},
|
||||||
|
{"display": "InnerInnerInnerEnum"},
|
||||||
|
{"display": "InnerInnerInnerClass"},
|
||||||
|
{"display": "ConnectFlags"},
|
||||||
|
]
|
||||||
|
exclude=[
|
||||||
|
{"display": "A"},
|
||||||
|
{"display": "AInner"},
|
||||||
|
{"display": "TestEnum"},
|
||||||
|
{"display": "InnerInnerEnum"},
|
||||||
|
{"display": "InnerInnerClass"},
|
||||||
|
{"display": "LocalInnerClass"},
|
||||||
|
{"display": "int"},
|
||||||
|
{"display": "String"},
|
||||||
|
{"display": "Node2D"},
|
||||||
|
{"display": "B"},
|
||||||
|
]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
const A = preload("res://completion/class_a.notest.gd")
|
||||||
|
|
||||||
|
class LocalInnerClass:
|
||||||
|
const AInner = preload("res://completion/class_a.notest.gd")
|
||||||
|
class InnerInnerClass:
|
||||||
|
const AInnerInner = preload("res://completion/class_a.notest.gd")
|
||||||
|
enum InnerInnerInnerEnum {}
|
||||||
|
class InnerInnerInnerClass:
|
||||||
|
pass
|
||||||
|
enum InnerInnerEnum {}
|
||||||
|
|
||||||
|
enum TestEnum {}
|
||||||
|
|
||||||
|
var test_var: LocalInnerClass.InnerInnerClass.➡
|
||||||
@@ -223,13 +223,52 @@ static void test_directory(const String &p_dir) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setup_global_classes(const String &p_dir) {
|
||||||
|
Error err = OK;
|
||||||
|
Ref<DirAccess> dir = DirAccess::open(p_dir, &err);
|
||||||
|
|
||||||
|
if (err != OK) {
|
||||||
|
FAIL("Invalid test directory.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = dir->get_current_dir();
|
||||||
|
|
||||||
|
dir->list_dir_begin();
|
||||||
|
String next = dir->get_next();
|
||||||
|
|
||||||
|
while (!next.is_empty()) {
|
||||||
|
if (dir->current_is_dir() && next != "." && next != "..") {
|
||||||
|
setup_global_classes(path.path_join(next));
|
||||||
|
} else if (next.ends_with(".gd")) {
|
||||||
|
String base_type;
|
||||||
|
bool is_abstract;
|
||||||
|
bool is_tool;
|
||||||
|
String source_file = path.path_join(next);
|
||||||
|
String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(source_file, &base_type, nullptr, &is_abstract, &is_tool);
|
||||||
|
if (class_name.is_empty()) {
|
||||||
|
next = dir->get_next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ERR_FAIL_COND_MSG(ScriptServer::is_global_class(class_name),
|
||||||
|
"Class name \"" + class_name + "\" from \"" + source_file + "\" is already used in \"" + ScriptServer::get_global_class_path(class_name) + "\".");
|
||||||
|
|
||||||
|
ScriptServer::add_global_class(class_name, base_type, GDScriptLanguage::get_singleton()->get_name(), source_file, is_abstract, is_tool);
|
||||||
|
}
|
||||||
|
next = dir->get_next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE("[Modules][GDScript][Completion]") {
|
TEST_SUITE("[Modules][GDScript][Completion]") {
|
||||||
TEST_CASE("[Editor] Check suggestion list") {
|
TEST_CASE("[Editor] Check suggestion list") {
|
||||||
// Set all editor settings that code completion relies on.
|
// Set all editor settings that code completion relies on.
|
||||||
EditorSettings::get_singleton()->set_setting("text_editor/completion/use_single_quotes", false);
|
EditorSettings::get_singleton()->set_setting("text_editor/completion/use_single_quotes", false);
|
||||||
init_language("modules/gdscript/tests/scripts");
|
init_language("modules/gdscript/tests/scripts");
|
||||||
|
|
||||||
|
setup_global_classes("modules/gdscript/tests/scripts/completion");
|
||||||
test_directory("modules/gdscript/tests/scripts/completion");
|
test_directory("modules/gdscript/tests/scripts/completion");
|
||||||
|
|
||||||
|
finish_language();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace GDScriptTests
|
} // namespace GDScriptTests
|
||||||
|
|||||||
Reference in New Issue
Block a user