diff options
Diffstat (limited to 'modules/gdscript/gd_editor.cpp')
-rw-r--r-- | modules/gdscript/gd_editor.cpp | 1568 |
1 files changed, 1204 insertions, 364 deletions
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index 546fed4e8a..12dc1bb139 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -28,7 +28,7 @@ /*************************************************************************/ #include "gd_script.h" #include "gd_compiler.h" - +#include "globals.h" void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const { @@ -312,171 +312,278 @@ String GDScriptLanguage::make_function(const String& p_class,const String& p_nam } -static void _parse_native_symbols(const StringName& p_native,bool p_static,List<String>* r_options) { +#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED) - if (!p_static) { - List<MethodInfo> methods; - ObjectTypeDB::get_method_list(p_native,&methods); - for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) { - if (!E->get().name.begins_with("_")) { - r_options->push_back(E->get().name); - } +struct GDCompletionIdentifier { + + StringName obj_type; + Variant::Type type; + Variant value; //im case there is a value, also return it +}; + + + +static GDCompletionIdentifier _get_type_from_variant(const Variant& p_variant) { + + GDCompletionIdentifier t; + t.type=p_variant.get_type(); + t.value=p_variant; + if (p_variant.get_type()==Variant::OBJECT) { + Object *obj = p_variant; + if (obj) { + //if (obj->cast_to<GDNativeClass>()) { + // t.obj_type=obj->cast_to<GDNativeClass>()->get_name(); + // t.value=Variant(); + //} else { + t.obj_type=obj->get_type(); + //} } } + return t; +} - List<String> constants; - ObjectTypeDB::get_integer_constant_list(p_native,&constants); +static GDCompletionIdentifier _get_type_from_pinfo(const PropertyInfo& p_info) { - for(List<String>::Element *E=constants.front();E;E=E->next()) { - r_options->push_back(E->get()); + GDCompletionIdentifier t; + t.type=p_info.type; + if (p_info.hint==PROPERTY_HINT_RESOURCE_TYPE) { + t.obj_type=p_info.hint_string; } - + return t; } +struct GDCompletionContext { -static bool _parse_script_symbols(const Ref<GDScript>& p_script,bool p_static,List<String>* r_options,List<String>::Element *p_indices); + const GDParser::ClassNode *_class; + const GDParser::FunctionNode *function; + const GDParser::BlockNode *block; + Object* base; + String base_path; +}; -static bool _parse_completion_variant(const Variant& p_var,List<String>* r_options,List<String>::Element *p_indices) { - if (p_indices) { +static Ref<Reference> _get_parent_class(GDCompletionContext& context) { - bool ok; - Variant si = p_var.get(p_indices->get(),&ok); - if (!ok) - return false; - return _parse_completion_variant(si,r_options,p_indices->next()); - } else { - switch(p_var.get_type()) { + if (context._class->extends_used) { + //do inheritance + String path = context._class->extends_file; - case Variant::DICTIONARY: { + Ref<GDScript> script; + Ref<GDNativeClass> native; - Dictionary d=p_var; - List<Variant> vl; - d.get_key_list(&vl); - for (List<Variant>::Element *E=vl.front();E;E=E->next()) { + if (path!="") { + //path (and optionally subclasses) - if (E->get().get_type()==Variant::STRING) - r_options->push_back(E->get()); - } + if (path.is_rel_path()) { + path=context.base_path.plus_file(path); + } + script = ResourceLoader::load(path); + if (script.is_null()) { + return REF(); + } + if (script->is_valid()) { - List<MethodInfo> ml; - p_var.get_method_list(&ml); - for(List<MethodInfo>::Element *E=ml.front();E;E=E->next()) { - r_options->push_back(E->get().name); + return REF(); + } + //print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid)); + + if (context._class->extends_class.size()) { + + for(int i=0;i<context._class->extends_class.size();i++) { + + String sub = context._class->extends_class[i]; + if (script->get_subclasses().has(sub)) { + + script=script->get_subclasses()[sub]; + } else { + + return REF(); + } } + } - } break; - case Variant::OBJECT: { + if (script.is_valid()) + return script; + + } else { + if (context._class->extends_class.size()==0) { + ERR_PRINT("BUG"); + return REF(); + } + + String base=context._class->extends_class[0]; + const GDParser::ClassNode *p = context._class->owner; + Ref<GDScript> base_class; +#if 0 + while(p) { + + if (p->subclasses.has(base)) { + + base_class=p->subclasses[base]; + break; + } + p=p->_owner; + } +#endif + if (base_class.is_valid()) { +#if 0 + for(int i=1;i<context._class->extends_class.size();i++) { - Object *o=p_var; - if (o) { - print_line("OBJECT: "+o->get_type()); - if (p_var.is_ref() && o->cast_to<GDScript>()) { + String subclass=context._class->extends_class[i]; - Ref<GDScript> gds = p_var; - _parse_script_symbols(gds,true,r_options,NULL); - } else if (o->is_type("GDNativeClass")){ + if (base_class->subclasses.has(subclass)) { - GDNativeClass *gnc = o->cast_to<GDNativeClass>(); - _parse_native_symbols(gnc->get_name(),false,r_options); + base_class=base_class->subclasses[subclass]; } else { - print_line("REGULAR BLEND"); - _parse_native_symbols(o->get_type(),false,r_options); + print_line("Could not find subclass: "+subclass); + return _get_type_from_class(context); //fail please } } - } break; - default: { + script=base_class; +#endif + + } else { + + if (context._class->extends_class.size()>1) { + + return REF(); + - List<PropertyInfo> pi; - p_var.get_property_list(&pi); - for(List<PropertyInfo>::Element *E=pi.front();E;E=E->next()) { - r_options->push_back(E->get().name); } - List<StringName> cl; + //if not found, try engine classes + if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) { - p_var.get_numeric_constants_for_type(p_var.get_type(),&cl); - for(List<StringName>::Element *E=cl.front();E;E=E->next()) { - r_options->push_back(E->get()); + return REF(); } - List<MethodInfo> ml; - p_var.get_method_list(&ml); - for(List<MethodInfo>::Element *E=ml.front();E;E=E->next()) { - r_options->push_back(E->get().name); + int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base]; + native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx]; + if (!native.is_valid()) { + + print_line("Global not a class: '"+base+"'"); + } + return native; + } + - } break; } - return true; } - + return Ref<Reference>(); } -struct GDCompletionIdentifier { - StringName obj_type; - Variant::Type type; -}; +static GDCompletionIdentifier _get_native_class(GDCompletionContext& context) { + //eeh... + GDCompletionIdentifier id; + id.type=Variant::NIL; -static GDCompletionIdentifier _guess_identifier_type(const GDParser::ClassNode *p_class,int p_line,const StringName& p_identifier); + REF pc = _get_parent_class(context); + if (!pc.is_valid()) { + return id; + } + Ref<GDNativeClass> nc = pc; + Ref<GDScript> s = pc; + if (s.is_null() && nc.is_null()) { + return id; + } + while(!s.is_null()) { + nc=s->get_native(); + s=s->get_base(); + } + if (nc.is_null()) { + return id; + } + + + + id.type=Variant::OBJECT; + if (context.base) + id.value=context.base; + id.obj_type=nc->get_name(); + return id; +} -static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_class,const GDParser::Node *p_node,int p_line,GDCompletionIdentifier &r_type) { +static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type); + + +static bool _guess_expression_type(GDCompletionContext& context,const GDParser::Node* p_node,int p_line,GDCompletionIdentifier &r_type) { if (p_node->type==GDParser::Node::TYPE_CONSTANT) { const GDParser::ConstantNode *cn=static_cast<const GDParser::ConstantNode *>(p_node); - r_type.type=cn->value.get_type(); - if (r_type.type==Variant::OBJECT) { - Object *obj = cn->value; - if (obj) { - r_type.obj_type=obj->get_type(); - } - } + r_type=_get_type_from_variant(cn->value); return true; } else if (p_node->type==GDParser::Node::TYPE_DICTIONARY) { r_type.type=Variant::DICTIONARY; + + + //what the heck, fill it anyway + const GDParser::DictionaryNode *an = static_cast<const GDParser::DictionaryNode *>(p_node); + Dictionary d; + for(int i=0;i<an->elements.size();i++) { + GDCompletionIdentifier k; + if (_guess_expression_type(context,an->elements[i].key,p_line,k) && k.value.get_type()!=Variant::NIL) { + GDCompletionIdentifier v; + if (_guess_expression_type(context,an->elements[i].value,p_line,v)) { + d[k.value]=v.value; + } + + } + } + r_type.value=d; return true; } else if (p_node->type==GDParser::Node::TYPE_ARRAY) { r_type.type=Variant::ARRAY; + //what the heck, fill it anyway + const GDParser::ArrayNode *an = static_cast<const GDParser::ArrayNode *>(p_node); + Array arr; + arr.resize(an->elements.size()); + for(int i=0;i<an->elements.size();i++) { + GDCompletionIdentifier ci; + if (_guess_expression_type(context,an->elements[i],p_line,ci)) { + arr[i]=ci.value; + } + } + r_type.value=arr; return true; } else if (p_node->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) { MethodInfo mi = GDFunctions::get_info(static_cast<const GDParser::BuiltInFunctionNode*>(p_node)->function); - r_type.type=mi.return_val.type; - if (mi.return_val.hint==PROPERTY_HINT_RESOURCE_TYPE) { - r_type.obj_type=mi.return_val.hint_string; - } + r_type=_get_type_from_pinfo(mi.return_val); + return true; } else if (p_node->type==GDParser::Node::TYPE_IDENTIFIER) { - - r_type=_guess_identifier_type(p_class,p_line,static_cast<const GDParser::IdentifierNode *>(p_node)->name); - return true; + return _guess_identifier_type(context,p_line-1,static_cast<const GDParser::IdentifierNode *>(p_node)->name,r_type); } else if (p_node->type==GDParser::Node::TYPE_SELF) { //eeh... - return false; + + r_type=_get_native_class(context); + return r_type.type!=Variant::NIL; } else if (p_node->type==GDParser::Node::TYPE_OPERATOR) { + + const GDParser::OperatorNode *op = static_cast<const GDParser::OperatorNode *>(p_node); if (op->op==GDParser::OperatorNode::OP_CALL) { - if (op->arguments[0]->type==GDParser::Node::TYPE_TYPE) { const GDParser::TypeNode *tn = static_cast<const GDParser::TypeNode *>(op->arguments[0]); @@ -486,25 +593,86 @@ static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_cl const GDParser::BuiltInFunctionNode *bin = static_cast<const GDParser::BuiltInFunctionNode *>(op->arguments[0]); - return _guess_identifier_type_in_expression(p_class,bin,p_line,r_type); + return _guess_expression_type(context,bin,p_line,r_type); } else if (op->arguments.size()>1 && op->arguments[1]->type==GDParser::Node::TYPE_IDENTIFIER) { + GDCompletionIdentifier base; - if (!_guess_identifier_type_in_expression(p_class,op->arguments[0],p_line,base)) + if (!_guess_expression_type(context,op->arguments[0],p_line,base)) return false; - StringName id = static_cast<const GDParser::IdentifierNode *>(p_node)->name; + + StringName id = static_cast<const GDParser::IdentifierNode *>(op->arguments[1])->name; + if (base.type==Variant::OBJECT) { + if (id.operator String()=="new" && base.value.get_type()==Variant::OBJECT) { + Object *obj = base.value; + if (obj && obj->cast_to<GDNativeClass>()) { + GDNativeClass *gdnc = obj->cast_to<GDNativeClass>(); + r_type.type=Variant::OBJECT; + r_type.value=Variant(); + r_type.obj_type=gdnc->get_name(); + return true; + } + } + if (ObjectTypeDB::has_method(base.obj_type,id)) { + #ifdef TOOLS_ENABLED MethodBind *mb = ObjectTypeDB::get_method(base.obj_type,id); PropertyInfo pi = mb->get_argument_info(-1); + //try calling the function if constant and all args are constant, should not crash.. + Object *baseptr = base.value; + + if (baseptr && mb->is_const() && pi.type==Variant::OBJECT) { + bool all_valid=true; + Vector<Variant> args; + for(int i=2;i<op->arguments.size();i++) { + GDCompletionIdentifier arg; + + if (_guess_expression_type(context,op->arguments[i],p_line,arg)) { + if (arg.value.get_type()!=Variant::NIL && arg.value.get_type()!=Variant::OBJECT) { // calling with object seems dangerous, i don' t know + args.push_back(arg.value); + } else { + all_valid=false; + break; + } + } else { + all_valid=false; + } + } + if (all_valid) { + Vector<const Variant*> argptr; + for(int i=0;i<args.size();i++) { + argptr.push_back(&args[i]); + } + + Variant::CallError ce; + Variant ret=mb->call(baseptr,argptr.ptr(),argptr.size(),ce); + + + if (ce.error==Variant::CallError::CALL_OK && ret.get_type()!=Variant::NIL) { + + if (ret.get_type()!=Variant::OBJECT || ret.operator Object*()!=NULL) { + + r_type=_get_type_from_variant(ret); + return true; + } + } + + } + } + r_type.type=pi.type; if (pi.hint==PROPERTY_HINT_RESOURCE_TYPE) { r_type.obj_type=pi.hint_string; } + + + + return true; #else return false; #endif @@ -519,7 +687,8 @@ static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_cl v.get_method_list(&mi); for (List<MethodInfo>::Element *E=mi.front();E;E=E->next()) { - if (E->get().name==id.operator String()) { + if (!E->get().name.begins_with("_") && E->get().name==id.operator String()) { + MethodInfo mi = E->get(); r_type.type=mi.return_val.type; @@ -534,49 +703,157 @@ static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_cl } + } else if (op->op==GDParser::OperatorNode::OP_INDEX || op->op==GDParser::OperatorNode::OP_INDEX_NAMED) { + + GDCompletionIdentifier p1; + GDCompletionIdentifier p2; + + + + if (op->op==GDParser::OperatorNode::OP_INDEX_NAMED) { + + if (op->arguments[1]->type==GDParser::Node::TYPE_IDENTIFIER) { + String id = static_cast<const GDParser::IdentifierNode*>(op->arguments[1])->name; + p2.type=Variant::STRING; + p2.value=id; + } + + } else { + if (op->arguments[1]) { + if (!_guess_expression_type(context,op->arguments[1],p_line,p2)) { + + return false; + } + } + } + + if (op->arguments[0]->type==GDParser::Node::TYPE_ARRAY) { + + const GDParser::ArrayNode *an = static_cast<const GDParser::ArrayNode *>(op->arguments[0]); + if (p2.value.is_num()) { + int index = p2.value; + if (index<0 || index>=an->elements.size()) + return false; + return _guess_expression_type(context,an->elements[index],p_line,r_type); + } + + } else if (op->arguments[0]->type==GDParser::Node::TYPE_DICTIONARY) { + + const GDParser::DictionaryNode *dn = static_cast<const GDParser::DictionaryNode *>(op->arguments[0]); + + if (p2.value.get_type()==Variant::NIL) + return false; + + for(int i=0;i<dn->elements.size();i++) { + + GDCompletionIdentifier k; + + if (!_guess_expression_type(context,dn->elements[i].key,p_line,k)) { + + return false; + } + + if (k.value.get_type()==Variant::NIL) + return false; + + if (k.value==p2.value) { + + return _guess_expression_type(context,dn->elements[i].value,p_line,r_type); + } + } + + } else { + + if (op->arguments[0]) { + if (!_guess_expression_type(context,op->arguments[0],p_line,p1)) { + + return false; + } + + } + + if (p1.value.get_type()==Variant::OBJECT) { + //?? + } else if (p1.value.get_type()!=Variant::NIL) { + + bool valid; + Variant ret = p1.value.get(p2.value,&valid); + if (valid) { + r_type=_get_type_from_variant(ret); + return true; + } + + } else { + if (p1.type!=Variant::NIL) { + Variant::CallError ce; + Variant base = Variant::construct(p1.type,NULL,0,ce); + bool valid; + Variant ret = base.get(p2.value,&valid); + if (valid) { + r_type=_get_type_from_variant(ret); + return true; + } + } + } + } + } else { + Variant::Operator vop = Variant::OP_MAX; switch(op->op) { - case GDParser::OperatorNode::OP_ASSIGN_ADD: vop=Variant::OP_ADD; break; - case GDParser::OperatorNode::OP_ASSIGN_SUB: vop=Variant::OP_SUBSTRACT; break; - case GDParser::OperatorNode::OP_ASSIGN_MUL: vop=Variant::OP_MULTIPLY; break; - case GDParser::OperatorNode::OP_ASSIGN_DIV: vop=Variant::OP_DIVIDE; break; - case GDParser::OperatorNode::OP_ASSIGN_MOD: vop=Variant::OP_MODULE; break; - case GDParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT: vop=Variant::OP_SHIFT_LEFT; break; - case GDParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT: vop=Variant::OP_SHIFT_RIGHT; break; - case GDParser::OperatorNode::OP_ASSIGN_BIT_AND: vop=Variant::OP_BIT_AND; break; - case GDParser::OperatorNode::OP_ASSIGN_BIT_OR: vop=Variant::OP_BIT_OR; break; - case GDParser::OperatorNode::OP_ASSIGN_BIT_XOR: vop=Variant::OP_BIT_XOR; break; + case GDParser::OperatorNode::OP_ADD: vop=Variant::OP_ADD; break; + case GDParser::OperatorNode::OP_SUB: vop=Variant::OP_SUBSTRACT; break; + case GDParser::OperatorNode::OP_MUL: vop=Variant::OP_MULTIPLY; break; + case GDParser::OperatorNode::OP_DIV: vop=Variant::OP_DIVIDE; break; + case GDParser::OperatorNode::OP_MOD: vop=Variant::OP_MODULE; break; + case GDParser::OperatorNode::OP_SHIFT_LEFT: vop=Variant::OP_SHIFT_LEFT; break; + case GDParser::OperatorNode::OP_SHIFT_RIGHT: vop=Variant::OP_SHIFT_RIGHT; break; + case GDParser::OperatorNode::OP_BIT_AND: vop=Variant::OP_BIT_AND; break; + case GDParser::OperatorNode::OP_BIT_OR: vop=Variant::OP_BIT_OR; break; + case GDParser::OperatorNode::OP_BIT_XOR: vop=Variant::OP_BIT_XOR; break; default:{} } + + if (vop==Variant::OP_MAX) return false; + + GDCompletionIdentifier p1; GDCompletionIdentifier p2; if (op->arguments[0]) { - if (!_guess_identifier_type_in_expression(p_class,op->arguments[0],p_line,p1)) + if (!_guess_expression_type(context,op->arguments[0],p_line,p1)) { + return false; + } + } if (op->arguments.size()>1) { - if (!_guess_identifier_type_in_expression(p_class,op->arguments[1],p_line,p2)) + if (!_guess_expression_type(context,op->arguments[1],p_line,p2)) { + return false; + } } Variant::CallError ce; - Variant v1 = Variant::construct(p1.type,NULL,0,ce); - Variant v2 = Variant::construct(p2.type,NULL,0,ce); + bool v1_use_value = p1.value.get_type()!=Variant::NIL && p1.value.get_type()!=Variant::OBJECT; + Variant v1 = (v1_use_value)?p1.value:Variant::construct(p1.type,NULL,0,ce); + bool v2_use_value = p2.value.get_type()!=Variant::NIL && p2.value.get_type()!=Variant::OBJECT; + Variant v2 = (v2_use_value)?p2.value:Variant::construct(p2.type,NULL,0,ce); // avoid potential invalid ops if ((vop==Variant::OP_DIVIDE || vop==Variant::OP_MODULE) && v2.get_type()==Variant::INT) { v2=1; + v2_use_value=false; } if (vop==Variant::OP_DIVIDE && v2.get_type()==Variant::REAL) { v2=1.0; + v2_use_value=false; } Variant r; @@ -585,6 +862,9 @@ static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_cl if (!valid) return false; r_type.type=r.get_type(); + if (v1_use_value && v2_use_value) + r_type.value=r; + return true; } @@ -594,46 +874,42 @@ static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_cl return false; } -static bool _guess_identifier_type_in_block(const GDParser::ClassNode *p_class,const GDParser::BlockNode *p_block,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) { - - - for(int i=0;i<p_block->sub_blocks.size();i++) { - //parse inner first - if (p_line>=p_block->sub_blocks[i]->line && (p_line<=p_block->sub_blocks[i]->end_line || p_block->sub_blocks[i]->end_line==-1)) { - if (_guess_identifier_type_in_block(p_class,p_block->sub_blocks[i],p_line,p_identifier,r_type)) - return true; - } - } +static bool _guess_identifier_type_in_block(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) { const GDParser::Node *last_assign=NULL; int last_assign_line=-1; - for (int i=0;i<p_block->statements.size();i++) { + for (int i=0;i<context.block->statements.size();i++) { + + if (context.block->statements[i]->line>p_line) + continue; - if (p_block->statements[i]->line>p_line) - break; + if (context.block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) { - if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) { + const GDParser::LocalVarNode *lv=static_cast<const GDParser::LocalVarNode *>(context.block->statements[i]); - const GDParser::LocalVarNode *lv=static_cast<const GDParser::LocalVarNode *>(p_block->statements[i]); if (lv->assign && lv->name==p_identifier) { + last_assign=lv->assign; - last_assign_line=p_block->statements[i]->line; + last_assign_line=context.block->statements[i]->line; } } - if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_OPERATOR) { - const GDParser::OperatorNode *op = static_cast<const GDParser::OperatorNode *>(p_block->statements[i]); + if (context.block->statements[i]->type==GDParser::BlockNode::TYPE_OPERATOR) { + const GDParser::OperatorNode *op = static_cast<const GDParser::OperatorNode *>(context.block->statements[i]); if (op->op==GDParser::OperatorNode::OP_ASSIGN) { if (op->arguments.size() && op->arguments[0]->type==GDParser::Node::TYPE_IDENTIFIER) { + const GDParser::IdentifierNode *id = static_cast<const GDParser::IdentifierNode *>(op->arguments[0]); + if (id->name==p_identifier) { + last_assign=op->arguments[1]; - last_assign_line=p_block->statements[i]->line; + last_assign_line=context.block->statements[i]->line; } } } @@ -642,418 +918,982 @@ static bool _guess_identifier_type_in_block(const GDParser::ClassNode *p_class,c //use the last assignment, (then backwards?) if (last_assign) { - return _guess_identifier_type_in_expression(p_class,last_assign,last_assign_line-1,r_type); + + return _guess_expression_type(context,last_assign,last_assign_line,r_type); } + return false; } -static GDCompletionIdentifier _guess_identifier_type(const GDParser::ClassNode *p_class,int p_line,const StringName& p_identifier) { +static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) { + //go to block first - return GDCompletionIdentifier(); -} -static void _parse_expression_node(const GDParser::ClassNode *p_class,const GDParser::Node *p_node,int p_line,List<String>* r_options,List<String>::Element *p_indices) { + const GDParser::BlockNode *block=context.block; + while(block) { + GDCompletionContext c = context; + c.block=block; - if (p_node->type==GDParser::Node::TYPE_CONSTANT) { - - const GDParser::ConstantNode *cn=static_cast<const GDParser::ConstantNode *>(p_node); - _parse_completion_variant(cn->value,r_options,p_indices?p_indices->next():NULL); - } else if (p_node->type==GDParser::Node::TYPE_DICTIONARY) { - - const GDParser::DictionaryNode *dn=static_cast<const GDParser::DictionaryNode*>(p_node); - for (int i=0;i<dn->elements.size();i++) { + if (_guess_identifier_type_in_block(c,p_line,p_identifier,r_type)) { + return true; + } - if (dn->elements[i].key->type==GDParser::Node::TYPE_CONSTANT) { + block=block->parent_block; + } - const GDParser::ConstantNode *cn=static_cast<const GDParser::ConstantNode *>(dn->elements[i].key); - if (cn->value.get_type()==Variant::STRING) { + //guess from argument if virtual + if (context.function && context.function->name!=StringName()) { - String str=cn->value; - if (p_indices) { + int argindex = -1; - if (str==p_indices->get()) { - _parse_expression_node(p_class,dn->elements[i].value,p_line,r_options,p_indices->next()); - return; - } + for(int i=0;i<context.function->arguments.size();i++) { - } else { - r_options->push_back(str); - } - } + if (context.function->arguments[i]==p_identifier) { + argindex=i; + break; } + } - } else if (p_node->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) { - MethodInfo mi = GDFunctions::get_info(static_cast<const GDParser::BuiltInFunctionNode*>(p_node)->function); + if (argindex!=-1) { + GDCompletionIdentifier id =_get_native_class(context); + if (id.type==Variant::OBJECT && id.obj_type!=StringName()) { + //this kinda sucks but meh - Variant::CallError ce; - _parse_completion_variant(Variant::construct(mi.return_val.type,NULL,0,ce),r_options,p_indices?p_indices->next():NULL); - } else if (p_node->type==GDParser::Node::TYPE_IDENTIFIER) { + List<MethodInfo> vmethods; + ObjectTypeDB::get_virtual_methods(id.obj_type,&vmethods); + for (List<MethodInfo>::Element *E=vmethods.front();E;E=E->next()) { - //GDCompletionIdentifier idt = _guess_identifier_type(p_class,p_line-1,static_cast<const GDParser::IdentifierNode *>(p_node)->name); - //Variant::CallError ce; - //_parse_completion_variant(Variant::construct(mi.return_val.type,NULL,0,ce),r_options,p_indices?p_indices->next():NULL); - } -} -static bool _parse_completion_block(const GDParser::ClassNode *p_class,const GDParser::BlockNode *p_block,int p_line,List<String>* r_options,List<String>::Element *p_indices) { + if (E->get().name==context.function->name && argindex<E->get().arguments.size()) { - print_line("COMPLETION BLOCK "+itos(p_block->line)+" -> "+itos(p_block->end_line)); + PropertyInfo arg=E->get().arguments[argindex]; - for(int i=0;i<p_block->sub_blocks.size();i++) { - //parse inner first - if (p_line>=p_block->sub_blocks[i]->line && (p_line<=p_block->sub_blocks[i]->end_line || p_block->sub_blocks[i]->end_line==-1)) { - if (_parse_completion_block(p_class,p_block->sub_blocks[i],p_line,r_options,p_indices)) - return true; + int scp = arg.name.find(":"); + if (scp!=-1) { + + + r_type.type=Variant::OBJECT; + r_type.obj_type=arg.name.substr(scp+1,arg.name.length()); + return true; + + } else { + + r_type.type=arg.type; + if (arg.hint==PROPERTY_HINT_RESOURCE_TYPE) + r_type.obj_type=arg.hint_string; + return true; + } + } + } + } } } - if (p_indices) { + //guess type in constant - //parse indices in expressions :| + for(int i=0;i<context._class->constant_expressions.size();i++) { - const GDParser::Node *last_assign=NULL; - int last_assign_line=-1; + if (context._class->constant_expressions[i].identifier==p_identifier) { - for (int i=0;i<p_block->statements.size();i++) { + ERR_FAIL_COND_V( context._class->constant_expressions[i].expression->type!=GDParser::Node::TYPE_CONSTANT, false ); + r_type=_get_type_from_variant(static_cast<const GDParser::ConstantNode*>(context._class->constant_expressions[i].expression)->value ); + return true; + } + } - if (p_block->statements[i]->line>p_line) - break; + if (!(context.function && context.function->_static)) { + + for(int i=0;i<context._class->variables.size();i++) { + if (context._class->variables[i].identifier==p_identifier) { - if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) { + if (context._class->variables[i]._export.type!=Variant::NIL) { - const GDParser::LocalVarNode *lv=static_cast<const GDParser::LocalVarNode *>(p_block->statements[i]); - if (lv->assign && String(lv->name)==p_indices->get()) { - last_assign=lv->assign; - last_assign_line=p_block->statements[i]->line; + r_type=_get_type_from_pinfo(context._class->variables[i]._export); + return true; + } else if (context._class->variables[i].expression) { + return _guess_expression_type(context,context._class->variables[i].expression,context._class->variables[i].line,r_type); } } } + } - //use the last assignment, (then backwards?) - if (last_assign) { - _parse_expression_node(p_class,last_assign,last_assign_line,r_options,p_indices->next()); - return true; - } - } else { - //no indices, just add all variables and continue - for(int i=0;i<p_block->variables.size();i++) { - //parse variables second - if (p_line>=p_block->variable_lines[i]) { - r_options->push_back(p_block->variables[i]); - } else break; + for(Map<StringName,int>::Element *E=GDScriptLanguage::get_singleton()->get_global_map().front();E;E=E->next()) { + if (E->key()==p_identifier) { + + r_type=_get_type_from_variant(GDScriptLanguage::get_singleton()->get_global_array()[E->get()]); + return true; } - } + } return false; } -static bool _parse_script_symbols(const Ref<GDScript>& p_script,bool p_static,List<String>* r_options,List<String>::Element *p_indices) { +static void _find_identifiers_in_block(GDCompletionContext& context,int p_line,bool p_only_functions,Set<String>& result) { + + if (p_only_functions) + return; + + for (int i=0;i<context.block->statements.size();i++) { + + if (context.block->statements[i]->line>p_line) + continue; - //for (Map<StringName,Ref<GDScript> >::Element ? - if (!p_static && !p_indices) { - for(const Set<StringName>::Element *E=p_script->get_members().front();E;E=E->next()) { + if (context.block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) { - r_options->push_back(E->get()); + const GDParser::LocalVarNode *lv=static_cast<const GDParser::LocalVarNode *>(context.block->statements[i]); + result.insert(lv->name.operator String()); } } +} - for (const Map<StringName,Variant >::Element *E=p_script->get_constants().front();E;E=E->next()) { +static void _find_identifiers_in_class(GDCompletionContext& context,bool p_static,bool p_only_functions,Set<String>& result) { - if( p_indices) { - if (p_indices->get()==String(E->get())) { - _parse_completion_variant(E->get(),r_options,p_indices->next()); - return true; - } - } else { - r_options->push_back(E->key()); + if (!p_static && !p_only_functions) { + + for(int i=0;i<context._class->variables.size();i++) { + result.insert(context._class->variables[i].identifier); } } + if (!p_only_functions) { - if (!p_indices){ - for (const Map<StringName,GDFunction>::Element *E=p_script->get_member_functions().front();E;E=E->next()) { + for(int i=0;i<context._class->constant_expressions.size();i++) { + result.insert(context._class->constant_expressions[i].identifier); + } - if (E->get().is_static() || !p_static) - r_options->push_back(E->key()); + for(int i=0;i<context._class->subclasses.size();i++) { + result.insert(context._class->subclasses[i]->name); } + } - if (p_script->get_base().is_valid()){ - if (_parse_script_symbols(p_script->get_base(),p_static,r_options,p_indices)) - return true; - } else if (p_script->get_native().is_valid() && !p_indices) { - _parse_native_symbols(p_script->get_native()->get_name(),p_static,r_options); + for(int i=0;i<context._class->static_functions.size();i++) { + if (context._class->static_functions[i]->arguments.size()) + result.insert(context._class->static_functions[i]->name.operator String()+"("); + else + result.insert(context._class->static_functions[i]->name.operator String()+"()"); } - return false; -} + if (!p_static) { + + for(int i=0;i<context._class->functions.size();i++) { + if (context._class->functions[i]->arguments.size()) + result.insert(context._class->functions[i]->name.operator String()+"("); + else + result.insert(context._class->functions[i]->name.operator String()+"()"); + } + } + + //globals + + Ref<Reference> base = _get_parent_class(context); + + while(true) { + Ref<GDScript> script = base; + Ref<GDNativeClass> nc = base; + if (script.is_valid()) { -static bool _parse_completion_class(const String& p_base_path,const GDParser::ClassNode *p_class,int p_line,List<String>* r_options,List<String>::Element *p_indices) { + if (!p_static && !p_only_functions) { + for (const Set<StringName>::Element *E=script->get_members().front();E;E=E->next()) { + result.insert(E->get().operator String()); + } + } - //checks known classes or built-in types for completion + if (!p_only_functions) { + for (const Map<StringName,Variant>::Element *E=script->get_constants().front();E;E=E->next()) { + result.insert(E->key().operator String()); + } + } - if (p_indices && !p_indices->next()) { - //built-in types do not have sub-classes, try these first if no sub-indices exist. - static const char*_type_names[Variant::VARIANT_MAX]={ - "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Trasnform", - "Color","Image","NodePath","RID","Object","InputEvent","Dictionary","Array","RawArray","IntArray","FloatArray","StringArray", - "Vector2Array","Vector3Array","ColorArray"}; + for (const Map<StringName,GDFunction>::Element *E=script->get_member_functions().front();E;E=E->next()) { + if (!p_static || E->get().is_static()) { + if (E->get().get_argument_count()) + result.insert(E->key().operator String()+"("); + else + result.insert(E->key().operator String()+"()"); + } + } + + if (!p_only_functions) { + for (const Map<StringName,Ref<GDScript> >::Element *E=script->get_subclasses().front();E;E=E->next()) { + result.insert(E->key().operator String()); + } + } - for(int i=0;i<Variant::VARIANT_MAX;i++) { + base=script->get_base(); + if (base.is_null()) + base=script->get_native(); + } else if (nc.is_valid()) { - if (p_indices->get()==_type_names[i]) { + if (!p_only_functions) { - List<StringName> ic; + StringName type = nc->get_name(); + List<String> constants; + ObjectTypeDB::get_integer_constant_list(type,&constants); + for(List<String>::Element *E=constants.front();E;E=E->next()) { + result.insert(E->get()); + } - Variant::get_numeric_constants_for_type(Variant::Type(i),&ic); - for(List<StringName>::Element *E=ic.front();E;E=E->next()) { - r_options->push_back(E->get()); + List<MethodInfo> methods; + ObjectTypeDB::get_method_list(type,&methods); + for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) { + if (E->get().name.begins_with("_")) + continue; + if (E->get().arguments.size()) + result.insert(E->get().name+"("); + else + result.insert(E->get().name+"()"); } - return true; } - } + break; + } else + break; + } - // check the sub-classes of current class +} - for(int i=0;i<p_class->subclasses.size();i++) { +static void _find_identifiers(GDCompletionContext& context,int p_line,bool p_only_functions,Set<String>& result) { - if (p_line>=p_class->subclasses[i]->line && (p_line<=p_class->subclasses[i]->end_line || p_class->subclasses[i]->end_line==-1)) { - // if OK in sub-classes, try completing the sub-class - if (_parse_completion_class(p_base_path,p_class->subclasses[i],p_line,r_options,p_indices)) - return true; - } + const GDParser::BlockNode *block=context.block; + + while(block) { + + GDCompletionContext c = context; + c.block=block; + + _find_identifiers_in_block(c,p_line,p_only_functions,result); + block=block->parent_block; } - bool in_static_func=false; + const GDParser::ClassNode *clss=context._class; - for(int i=0;i<p_class->functions.size();i++) { + bool _static=context.function && context.function->_static; - const GDParser::FunctionNode *fu = p_class->functions[i]; + while(clss) { + GDCompletionContext c = context; + c._class=clss; + c.block=NULL; + c.function=NULL; + _find_identifiers_in_class(c,_static,p_only_functions,result); + clss=clss->owner; + } - if (p_line>=fu->body->line && (p_line<=fu->body->end_line || fu->body->end_line==-1)) { - //if in function, first block stuff from outer to inner - if (_parse_completion_block(p_class,fu->body,p_line,r_options,p_indices)) - return true; - //then function arguments - if (!p_indices) { - for(int j=0;j<fu->arguments.size();j++) { + for(int i=0;i<GDFunctions::FUNC_MAX;i++) { + + result.insert(GDFunctions::get_func_name(GDFunctions::Function(i))); + } + + static const char*_type_names[Variant::VARIANT_MAX]={ + "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Trasnform", + "Color","Image","NodePath","RID","Object","InputEvent","Dictionary","Array","RawArray","IntArray","FloatArray","StringArray", + "Vector2Array","Vector3Array","ColorArray"}; + + for(int i=0;i<Variant::VARIANT_MAX;i++) { + result.insert(_type_names[i]); + } + + for(const Map<StringName,int>::Element *E=GDScriptLanguage::get_singleton()->get_global_map().front();E;E=E->next()) { + result.insert(E->key().operator String()); + } +} + + +static String _get_visual_datatype(const PropertyInfo& p_info,bool p_isarg=true) { + + String n = p_info.name; + int idx = n.find(":"); + if (idx!=-1) { + return n.substr(idx+1,n.length()); + } + + if (p_info.type==Variant::OBJECT && p_info.hint==PROPERTY_HINT_RESOURCE_TYPE) + return p_info.hint_string; + if (p_info.type==Variant::NIL) { + if (p_isarg) + return "var"; + else + return "void"; + } + + return Variant::get_type_name(p_info.type); +} + +static void _make_function_hint(const GDParser::FunctionNode* p_func,int p_argidx,String& arghint) { + + arghint="func "+p_func->name+"("; + for (int i=0;i<p_func->arguments.size();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+=p_func->arguments[i].operator String(); + int deffrom = p_func->arguments.size()-p_func->default_values.size(); + + if (i>=deffrom) { + int defidx = deffrom-i; + + if (defidx>=0 && defidx<p_func->default_values.size()) { + + if (p_func->default_values[defidx]->type==GDParser::Node::TYPE_OPERATOR) { + + const GDParser::OperatorNode *op=static_cast<const GDParser::OperatorNode *>(p_func->default_values[defidx]); + if (op->op==GDParser::OperatorNode::OP_ASSIGN) { + const GDParser::ConstantNode *cn=static_cast<const GDParser::ConstantNode *>(op->arguments[1]); + arghint+="="+cn->value.get_construct_string(); + + } + } else { - r_options->push_back(fu->arguments[j]); } } } + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } } + if (p_func->arguments.size()>0) + arghint+=" "; + arghint+=")"; +} - for(int i=0;i<p_class->static_functions.size();i++) { - const GDParser::FunctionNode *fu = p_class->static_functions[i]; +static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const StringName& p_method,const GDCompletionIdentifier& id, int p_argidx, Set<String>& result, String& arghint) { - if (p_line>=fu->body->line && (p_line<=fu->body->end_line || fu->body->end_line==-1)) { - //if in function, first block stuff from outer to inne - if (_parse_completion_block(p_class,fu->body,p_line,r_options,p_indices)) - return true; - //then function arguments - if (!p_indices) { - for(int j=0;j<fu->arguments.size();j++) { + if (id.type==Variant::INPUT_EVENT && String(p_method)=="is_action" && p_argidx==0) { - r_options->push_back(fu->arguments[j]); - } - } + List<PropertyInfo> pinfo; + Globals::get_singleton()->get_property_list(&pinfo); + + for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) { + const PropertyInfo &pi=E->get(); - in_static_func=true; + if (!pi.name.begins_with("input/")) + continue; + + String name = pi.name.substr(pi.name.find("/")+1,pi.name.length()); + result.insert("\""+name+"\""); } - } + } else if (id.type==Variant::OBJECT && id.obj_type!=StringName()) { - //add all local names - if (!p_indices) { - if (!in_static_func) { + MethodBind *m = ObjectTypeDB::get_method(id.obj_type,p_method); + if (!m) + return; - for(int i=0;i<p_class->variables.size();i++) { + if (p_method.operator String()=="connect") { - r_options->push_back(p_class->variables[i].identifier); + + if (p_argidx==0) { + List<MethodInfo> sigs; + ObjectTypeDB::get_signal_list(id.obj_type,&sigs); + for (List<MethodInfo>::Element *E=sigs.front();E;E=E->next()) { + result.insert("\""+E->get().name+"\""); + } } - } + /*if (p_argidx==2) { - for(int i=0;i<p_class->constant_expressions.size();i++) { + ERR_FAIL_COND(p_node->type!=GDParser::Node::TYPE_OPERATOR); + const GDParser::OperatorNode *op=static_cast<const GDParser::OperatorNode *>(p_node); + if (op->arguments.size()>) - r_options->push_back(p_class->constant_expressions[i].identifier); - } + }*/ + } else { - if (!in_static_func) { - for(int i=0;i<p_class->functions.size();i++) { + Object *obj=id.value; + if (obj) { + List<String> options; + obj->get_argument_options(p_method,p_argidx,&options); + for(List<String>::Element *E=options.front();E;E=E->next()) { - r_options->push_back(p_class->functions[i]->name); + result.insert(E->get()); + } } + } - for(int i=0;i<p_class->static_functions.size();i++) { + arghint = _get_visual_datatype(m->get_argument_info(-1),false)+" "+p_method.operator String()+String("("); + + for(int i=0;i<m->get_argument_count();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + String n = m->get_argument_info(i).name; + int dp = n.find(":"); + if (dp!=-1) + n=n.substr(0,dp); + arghint+=_get_visual_datatype(m->get_argument_info(i))+" "+n; + int deffrom = m->get_argument_count()-m->get_default_argument_count(); + + + if (i>=deffrom) { + int defidx = i-deffrom; + + if (defidx>=0 && defidx<m->get_default_argument_count()) { + Variant v= m->get_default_argument(i); + arghint+="="+v.get_construct_string(); + } + } + + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } - r_options->push_back(p_class->static_functions[i]->name); } + if (m->get_argument_count()>0) + arghint+=" "; + + + arghint+=")"; + } +} - if (p_class->extends_used) { - //do inheritance - String path = p_class->extends_file; +static void _find_call_arguments(GDCompletionContext& context,const GDParser::Node* p_node, int p_line,int p_argidx, Set<String>& result, String& arghint) { - Ref<GDScript> script; - Ref<GDNativeClass> native; - if (path!="") { - //path (and optionally subclasses) - script = ResourceLoader::load(path); - if (script.is_null()) { - return false; + if (!p_node || p_node->type!=GDParser::Node::TYPE_OPERATOR) { + + return; + } + + const GDParser::OperatorNode *op = static_cast<const GDParser::OperatorNode *>(p_node); + + if (op->op!=GDParser::OperatorNode::OP_CALL) { + + return; + } + + if (op->arguments[0]->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) { + //complete built-in function + const GDParser::BuiltInFunctionNode *fn = static_cast<const GDParser::BuiltInFunctionNode*>(op->arguments[0]); + MethodInfo mi = GDFunctions::get_info(fn->function); + + arghint = _get_visual_datatype(mi.return_val,false)+" "+GDFunctions::get_func_name(fn->function)+String("("); + for(int i=0;i<mi.arguments.size();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+=_get_visual_datatype(mi.arguments[i])+" "+mi.arguments[i].name; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); } - if (p_class->extends_class.size()) { + } + if (mi.arguments.size()>0) + arghint+=" "; + arghint+=")"; + + } else if (op->arguments[0]->type==GDParser::Node::TYPE_TYPE) { + //complete built-in function + const GDParser::TypeNode *tn = static_cast<const GDParser::TypeNode*>(op->arguments[0]); + + List<MethodInfo> mil; + Variant::get_constructor_list(tn->vtype,&mil); + + for(List<MethodInfo>::Element *E=mil.front();E;E=E->next()) { + + MethodInfo mi = E->get(); + if (mi.arguments.size()==0) + continue; + if (E->prev()) + arghint+="\n"; + arghint += Variant::get_type_name(tn->vtype)+" "+Variant::get_type_name(tn->vtype)+String("("); + for(int i=0;i<mi.arguments.size();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+=_get_visual_datatype(mi.arguments[i])+" "+mi.arguments[i].name; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } - for(int i=0;i<p_class->extends_class.size();i++) { + } + if (mi.arguments.size()>0) + arghint+=" "; + arghint+=")"; + } - String sub = p_class->extends_class[i]; - if (script->get_subclasses().has(sub)) { + } else if (op->arguments.size()>=2 && op->arguments[1]->type==GDParser::Node::TYPE_IDENTIFIER) { + //make sure identifier exists... - script=script->get_subclasses()[sub]; - } else { + const GDParser::IdentifierNode *id=static_cast<const GDParser::IdentifierNode *>(op->arguments[1]); - return false; + if (op->arguments[0]->type==GDParser::Node::TYPE_SELF) { + //self, look up + + for(int i=0;i<context._class->static_functions.size();i++) { + if (context._class->static_functions[i]->name==id->name) { + _make_function_hint(context._class->static_functions[i],p_argidx,arghint); + return; + } + } + + if (context.function && !context.function->_static) { + + for(int i=0;i<context._class->functions.size();i++) { + if (context._class->functions[i]->name==id->name) { + _make_function_hint(context._class->functions[i],p_argidx,arghint); + return; } } } - } else { + Ref<Reference> base = _get_parent_class(context); - ERR_FAIL_COND_V(p_class->extends_class.size()==0,false); - //look around for the subclasses + while(true) { - String base=p_class->extends_class[0]; - Ref<GDScript> base_class; -#if 0 - while(p) { + Ref<GDScript> script = base; + Ref<GDNativeClass> nc = base; + if (script.is_valid()) { - if (p->subclasses.has(base)) { - base_class=p->subclasses[base]; + for (const Map<StringName,GDFunction>::Element *E=script->get_member_functions().front();E;E=E->next()) { + + if (E->key()==id->name) { + + if (context.function && context.function->_static && !E->get().is_static()) + continue; + + + arghint = "func "+id->name.operator String()+String("("); + for(int i=0;i<E->get().get_argument_count();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+=E->get().get_argument_name(i); + int deffrom = E->get().get_argument_count()-E->get().get_default_argument_count(); + if (i>=deffrom) { + int defidx = deffrom-i; + if (defidx>=0 && defidx<E->get().get_default_argument_count()) { + arghint+="="+E->get().get_default_argument(defidx).get_construct_string(); + } + } + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + + } + if (E->get().get_argument_count()>0) + arghint+=" "; + arghint+=")"; + return; + } + } + + base=script->get_base(); + if (base.is_null()) + base=script->get_native(); + } else if (nc.is_valid()) { + + if (context.function && !context.function->_static) { + + GDCompletionIdentifier ci; + ci.type=Variant::OBJECT; + ci.obj_type=nc->get_name(); + if (!context._class->owner) + ci.value=context.base; + + _find_type_arguments(p_node,p_line,id->name,ci,p_argidx,result,arghint); + //guess type.. + /* + List<MethodInfo> methods; + ObjectTypeDB::get_method_list(type,&methods); + for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) { + //if (E->get().arguments.size()) + // result.insert(E->get().name+"("); + //else + // result.insert(E->get().name+"()"); + }*/ + } break; - } - p=p->_owner; + } else + break; + } + } else { - if (base_class.is_valid()) { - for(int i=1;i<p_class->extends_class.size();i++) { + GDCompletionIdentifier ci; + if (_guess_expression_type(context,op->arguments[0],p_line,ci)) { - String subclass=p_class->extends_class[i]; + _find_type_arguments(p_node,p_line,id->name,ci,p_argidx,result,arghint); + return; + } - if (base_class->subclasses.has(subclass)) { + } - base_class=base_class->subclasses[subclass]; - } else { + } +#if 0 + bool _static=context.function->_static; - _set_error("Could not find subclass: "+subclass,p_class); - return ERR_FILE_NOT_FOUND; - } - } - } else { -#endif - if (p_class->extends_class.size()>1) { - return false; + for(int i=0;i<context._class->static_functions.size();i++) { + if (context._class->static_functions[i]->arguments.size()) + result.insert(context._class->static_functions[i]->name.operator String()+"("); + else + result.insert(context._class->static_functions[i]->name.operator String()+"()"); + } + + if (!p_static) { + for(int i=0;i<context._class->functions.size();i++) { + if (context._class->functions[i]->arguments.size()) + result.insert(context._class->functions[i]->name.operator String()+"("); + else + result.insert(context._class->functions[i]->name.operator String()+"()"); + } + } + + Ref<Reference> base = _get_parent_class(context); + + while(true) { + + Ref<GDScript> script = base; + Ref<GDNativeClass> nc = base; + if (script.is_valid()) { + + if (!p_static && !p_only_functions) { + for (const Set<StringName>::Element *E=script->get_members().front();E;E=E->next()) { + result.insert(E->get().operator String()); } - //if not found, try engine classes - if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) { - return false; + } + + if (!p_only_functions) { + for (const Map<StringName,Variant>::Element *E=script->get_constants().front();E;E=E->next()) { + result.insert(E->key().operator String()); } + } - int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base]; - native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx]; - if (!native.is_valid()) { - return false; + for (const Map<StringName,GDFunction>::Element *E=script->get_member_functions().front();E;E=E->next()) { + if (!p_static || E->get().is_static()) { + if (E->get().get_argument_count()) + result.insert(E->key().operator String()+"("); + else + result.insert(E->key().operator String()+"()"); } -#if 0 } -#endif - } + if (!p_only_functions) { + for (const Map<StringName,Ref<GDScript> >::Element *E=script->get_subclasses().front();E;E=E->next()) { + result.insert(E->key().operator String()); + } + } - if (script.is_valid()) { - if (_parse_script_symbols(script,in_static_func,r_options,p_indices)) - return true; + base=script->get_base(); + if (base.is_null()) + base=script->get_native(); + } else if (nc.is_valid()) { - } else if (native.is_valid() && !p_indices) { + if (!p_only_functions) { - _parse_native_symbols(native->get_name(),in_static_func,r_options); - } - } + StringName type = nc->get_name(); + List<String> constants; + ObjectTypeDB::get_integer_constant_list(type,&constants); + for(List<String>::Element *E=constants.front();E;E=E->next()) { + result.insert(E->get()); + } - return false; + List<MethodInfo> methods; + ObjectTypeDB::get_method_list(type,&methods); + for(List<MethodInfo>::Element *E=methods.front();E;E=E->next()) { + if (E->get().arguments.size()) + result.insert(E->get().name+"("); + else + result.insert(E->get().name+"()"); + } + } + break; + } else + break; -} + } + for(int i=0;i<GDFunctions::FUNC_MAX;i++) { -Error GDScriptLanguage::complete_keyword(const String& p_code, int p_line, const String& p_base_path, const String& p_base, List<String>* r_options) { + result.insert(GDFunctions::get_func_name(GDFunctions::Function(i))); + } +#endif + +} +Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base_path, Object*p_owner, List<String>* r_options, String &r_call_hint) { + //print_line( p_code.replace(String::chr(0xFFFF),"<cursor>")); GDParser p; Error err = p.parse(p_code,p_base_path); - // don't care much about error I guess - const GDParser::Node* root = p.get_parse_tree(); - ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,ERR_INVALID_DATA); + bool isfunction=false; + Set<String> options; + + GDCompletionContext context; + context._class=p.get_completion_class(); + context.block=p.get_completion_block(); + context.function=p.get_completion_function(); + context.base=p_owner; + context.base_path=p_base_path; + + switch(p.get_completion_type()) { + + case GDParser::COMPLETION_NONE: { + print_line("No completion"); + } break; + case GDParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: { + print_line("Built in type constant"); + List<StringName> constants; + Variant::get_numeric_constants_for_type(p.get_completion_built_in_constant(),&constants); + for(List<StringName>::Element *E=constants.front();E;E=E->next()) { + options.insert(E->get().operator String()); + } - print_line("BASE: "+p_base); - const GDParser::ClassNode *cl = static_cast<const GDParser::ClassNode*>(root); - List<String> indices; - Vector<String> spl = p_base.split("."); + } break; + case GDParser::COMPLETION_FUNCTION: + isfunction=true; + case GDParser::COMPLETION_IDENTIFIER: { - for(int i=0;i<spl.size()-1;i++) { - print_line("INDEX "+itos(i)+": "+spl[i]); - indices.push_back(spl[i]); - } + _find_identifiers(context,p.get_completion_line(),isfunction,options); + } break; + case GDParser::COMPLETION_PARENT_FUNCTION: { + print_line("parent function"); - //parse completion inside of the class first - if (_parse_completion_class(p_base,cl,p_line,r_options,indices.front())) - return OK; + } break; + case GDParser::COMPLETION_METHOD: + isfunction=true; + case GDParser::COMPLETION_INDEX: { + + const GDParser::Node *node = p.get_completion_node(); + if (node->type!=GDParser::Node::TYPE_OPERATOR) + break; - //if parsing completion inside of the class fails (none found), try using globals for completion - for(Map<StringName,int>::Element *E=globals.front();E;E=E->next()) { - if (!indices.empty()) { - if (String(E->key())==indices.front()->get()) { - _parse_completion_variant(global_array[E->get()],r_options,indices.front()->next()); - return OK; + + GDCompletionIdentifier t; + if (_guess_expression_type(context,static_cast<const GDParser::OperatorNode *>(node)->arguments[0],p.get_completion_line(),t)) { + + if (t.type==Variant::OBJECT && t.obj_type!=StringName()) { + + + if (t.value.get_type()) { + Object *obj=t.value; + if (obj) { + GDScript *scr = obj->cast_to<GDScript>(); + while (scr) { + + if (!isfunction) { + for (const Map<StringName,Variant>::Element *E=scr->get_constants().front();E;E=E->next()) { + options.insert(E->key()); + } + } + for (const Map<StringName,GDFunction>::Element *E=scr->get_member_functions().front();E;E=E->next()) { + options.insert(E->key()); + } + + if (scr->get_base().is_valid()) + scr=scr->get_base().ptr(); + else + scr=NULL; + } + } + } + + + if (!isfunction) { + ObjectTypeDB::get_integer_constant_list(t.obj_type,r_options); + } + List<MethodInfo> mi; + ObjectTypeDB::get_method_list(t.obj_type,&mi); + for (List<MethodInfo>::Element *E=mi.front();E;E=E->next()) { + + if (E->get().name.begins_with("_")) + continue; + + if (E->get().arguments.size()) + options.insert(E->get().name+"("); + else + options.insert(E->get().name+"()"); + + } + } else { + + + if (t.type==Variant::INPUT_EVENT) { + + //this is hardcoded otherwise it's not obvious + Set<String> exclude; + + for(int i=0;i<InputEvent::TYPE_MAX;i++) { + + InputEvent ie; + ie.type=InputEvent::Type(i); + static const char*evnames[]={ + "# Common", + "# Key", + "# MouseMotion", + "# MouseButton", + "# JoyMotion", + "# JoyButton", + "# ScreenTouch", + "# ScreenDrag", + "# Action" + }; + + r_options->push_back(evnames[i]); + + Variant v = ie; + + if (i==0) { + List<MethodInfo> mi; + v.get_method_list(&mi); + for (List<MethodInfo>::Element *E=mi.front();E;E=E->next()) { + r_options->push_back(E->get().name+"("); + + } + + } + + List<PropertyInfo> pi; + v.get_property_list(&pi); + + for (List<PropertyInfo>::Element *E=pi.front();E;E=E->next()) { + + if (i==0) + exclude.insert(E->get().name); + else if (exclude.has(E->get().name)) + continue; + + r_options->push_back(E->get().name); + } + } + return OK; + } else { + if (t.value.get_type()==Variant::NIL) { + Variant::CallError ce; + t.value=Variant::construct(t.type,NULL,0,ce); + } + + + if (!isfunction) { + List<PropertyInfo> pl; + t.value.get_property_list(&pl); + for (List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) { + + if (E->get().name.find("/")==-1) + options.insert(E->get().name); + } + } + + List<MethodInfo> mi; + t.value.get_method_list(&mi); + for (List<MethodInfo>::Element *E=mi.front();E;E=E->next()) { + if (E->get().arguments.size()) + options.insert(E->get().name+"("); + else + options.insert(E->get().name+"()"); + + } + } + } } - } else { - r_options->push_back(E->key()); - } + + + } break; + case GDParser::COMPLETION_CALL_ARGUMENTS: { + + _find_call_arguments(context,p.get_completion_node(),p.get_completion_line(),p.get_completion_argument_index(),options,r_call_hint); + } break; + case GDParser::COMPLETION_VIRTUAL_FUNC: { + + GDCompletionIdentifier cid = _get_native_class(context); + + if (cid.obj_type!=StringName()) { + List<MethodInfo> vm; + ObjectTypeDB::get_virtual_methods(cid.obj_type,&vm); + for(List<MethodInfo>::Element *E=vm.front();E;E=E->next()) { + + MethodInfo &mi=E->get(); + String m = mi.name; + if (m.find(":")!=-1) + m=m.substr(0,m.find(":")); + m+="("; + + if (mi.arguments.size()) { + for(int i=0;i<mi.arguments.size();i++) { + if (i>0) + m+=", "; + String n =mi.arguments[i].name; + if (n.find(":")!=-1) + n=n.substr(0,n.find(":")); + m+=n; + } + } + m+="):"; + + options.insert(m); + } + } + } break; + + + } + + + for(Set<String>::Element *E=options.front();E;E=E->next()) { + r_options->push_back(E->get()); } return OK; } +#else + +Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base_path, Object*p_owner, List<String>* r_options, String &r_call_hint) { + return OK; +} + +#endif + + void GDScriptLanguage::auto_indent_code(String& p_code,int p_from_line,int p_to_line) const { |