diff options
Diffstat (limited to 'modules/gdscript')
-rw-r--r-- | modules/gdscript/gd_compiler.cpp | 62 | ||||
-rw-r--r-- | modules/gdscript/gd_editor.cpp | 1568 | ||||
-rw-r--r-- | modules/gdscript/gd_functions.cpp | 4 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.cpp | 320 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.h | 56 | ||||
-rw-r--r-- | modules/gdscript/gd_script.cpp | 5 | ||||
-rw-r--r-- | modules/gdscript/gd_script.h | 20 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.cpp | 6 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.h | 1 |
9 files changed, 1611 insertions, 431 deletions
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index 5595db95de..278651d642 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -45,8 +45,13 @@ void GDCompiler::_set_error(const String& p_error,const GDParser::Node *p_node) return; error=p_error; - err_line=p_node->line; - err_column=p_node->column; + if (p_node) { + err_line=p_node->line; + err_column=p_node->column; + } else { + err_line=0; + err_column=0; + } } bool GDCompiler::_create_unary_operator(CodeGen& codegen,const GDParser::OperatorNode *on,Variant::Operator op, int p_stack_level) { @@ -523,7 +528,7 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre int ret = _parse_expression(codegen,on->arguments[i],slevel); if (ret<0) return ret; - if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { + if (ret&(GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS)) { slevel++; codegen.alloc_stack(slevel); } @@ -1168,6 +1173,7 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * codegen.current_line=0; codegen.call_max=0; codegen.debug_stack=ScriptDebugger::get_singleton()!=NULL; + Vector<StringName> argnames; int stack_level=0; @@ -1175,6 +1181,9 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * for(int i=0;i<p_func->arguments.size();i++) { int idx = i; codegen.add_stack_identifier(p_func->arguments[i],i); +#ifdef TOOLS_ENABLED + argnames.push_back(p_func->arguments[i]); +#endif } stack_level=p_func->arguments.size(); } @@ -1249,6 +1258,9 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * if (p_func) gdfunc->_static=p_func->_static; +#ifdef TOOLS_ENABLED + gdfunc->arg_names=argnames; +#endif //constants if (codegen.constant_map.size()) { gdfunc->_constant_count=codegen.constant_map.size(); @@ -1515,7 +1527,7 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars GDScript::MemberInfo minfo; minfo.index = p_script->member_indices.size(); minfo.setter = p_class->variables[i].setter; - minfo.getter = p_class->variables[i].getter; + minfo.getter = p_class->variables[i].getter; p_script->member_indices[name]=minfo; p_script->members.insert(name); @@ -1579,6 +1591,48 @@ Error GDCompiler::_parse_class(GDScript *p_script,GDScript *p_owner,const GDPars return err; } +#ifdef DEBUG_ENABLED + //validate setters/getters if debug is enabled + for(int i=0;i<p_class->variables.size();i++) { + + if (p_class->variables[i].setter) { + const Map<StringName,GDFunction>::Element *E=p_script->get_member_functions().find(p_class->variables[i].setter); + if (!E) { + _set_error("Setter function '"+String(p_class->variables[i].setter)+"' not found in class.",NULL); + err_line=p_class->variables[i].line; + err_column=0; + return ERR_PARSE_ERROR; + } + + if (E->get().is_static()) { + + _set_error("Setter function '"+String(p_class->variables[i].setter)+"' is static.",NULL); + err_line=p_class->variables[i].line; + err_column=0; + return ERR_PARSE_ERROR; + } + + } + if (p_class->variables[i].getter) { + const Map<StringName,GDFunction>::Element *E=p_script->get_member_functions().find(p_class->variables[i].getter); + if (!E) { + _set_error("Getter function '"+String(p_class->variables[i].getter)+"' not found in class.",NULL); + err_line=p_class->variables[i].line; + err_column=0; + return ERR_PARSE_ERROR; + } + + if (E->get().is_static()) { + + _set_error("Getter function '"+String(p_class->variables[i].getter)+"' is static.",NULL); + err_line=p_class->variables[i].line; + err_column=0; + return ERR_PARSE_ERROR; + } + + } + } +#endif return OK; } 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 { diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp index 0d11734bbd..fcfbbb04da 100644 --- a/modules/gdscript/gd_functions.cpp +++ b/modules/gdscript/gd_functions.cpp @@ -1166,6 +1166,8 @@ MethodInfo GDFunctions::get_info(Function p_func) { MethodInfo mi("weakref",PropertyInfo(Variant::OBJECT,"obj")); mi.return_val.type=Variant::OBJECT; + mi.return_val.name="WeakRef"; + return mi; } break; @@ -1173,6 +1175,7 @@ MethodInfo GDFunctions::get_info(Function p_func) { MethodInfo mi("funcref",PropertyInfo(Variant::OBJECT,"instance"),PropertyInfo(Variant::STRING,"funcname")); mi.return_val.type=Variant::OBJECT; + mi.return_val.name="FuncRef"; return mi; } break; @@ -1231,6 +1234,7 @@ MethodInfo GDFunctions::get_info(Function p_func) { MethodInfo mi("load",PropertyInfo(Variant::STRING,"path")); mi.return_val.type=Variant::OBJECT; + mi.return_val.name="Resource"; return mi; } break; case INST2DICT: { diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index 904b6ba52f..aa2878f9e1 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -30,19 +30,6 @@ #include "print_string.h" #include "io/resource_loader.h" #include "os/file_access.h" -/* TODO: - - *Property reduce constant expressions - *Implement missing operators in variant? - *constructor - */ - -/* - todo: - fix post ++,-- - make sure ++,-- don't work on constant expressions - seems passing parent node as param is not needed - */ template<class T> T* GDParser::alloc_node() { @@ -116,14 +103,20 @@ bool GDParser::_enter_indent_block(BlockNode* p_block) { } } -bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_static) { +bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_static,bool p_can_codecomplete) { if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) { tokenizer->advance(); } else { + int argidx=0; + while(true) { + if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { + _make_completable_call(argidx); + completion_node=p_parent; + } Node*arg = _parse_expression(p_parent,p_static); if (!arg) @@ -144,6 +137,7 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat } tokenizer->advance(); + argidx++; } else { // something is broken _set_error("Expected ',' or ')'"); @@ -158,6 +152,48 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat } +void GDParser::_make_completable_call(int p_arg) { + + completion_cursor=StringName(); + completion_type=COMPLETION_CALL_ARGUMENTS; + completion_class=current_class; + completion_function=current_function; + completion_line=tokenizer->get_token_line(); + completion_argument=p_arg; + completion_block=current_block; + tokenizer->advance(); + +} + + +bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& identifier) { + + identifier=StringName(); + if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) { + identifier=tokenizer->get_token_identifier(); + tokenizer->advance(); + } + if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { + + completion_cursor=identifier; + completion_type=p_type; + completion_class=current_class; + completion_function=current_function; + completion_line=tokenizer->get_token_line(); + completion_block=current_block; + tokenizer->advance(); + + if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) { + identifier=identifier.operator String() + tokenizer->get_token_identifier().operator String(); + tokenizer->advance(); + } + + return true; + } + + return false; +} + GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_allow_assign) { @@ -199,6 +235,9 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ tokenizer->advance(); expr=subexpr; + } else if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { + tokenizer->advance(); + continue; //no point in cursor in the middle of expression } else if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT) { @@ -327,12 +366,19 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ Variant::Type bi_type = tokenizer->get_token_type(); tokenizer->advance(2); - if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) { + + StringName identifier; + + if (_get_completable_identifier(COMPLETION_BUILT_IN_TYPE_CONSTANT,identifier)) { + + completion_built_in_constant=bi_type; + } + + if (identifier==StringName()) { _set_error("Built-in type constant expected after '.'"); return NULL; } - StringName identifier = tokenizer->get_token_identifier(); if (!Variant::has_numeric_constant(bi_type,identifier)) { _set_error("Static constant '"+identifier.operator String()+"' not present in built-in type "+Variant::get_type_name(bi_type)+"."); @@ -342,7 +388,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ ConstantNode *cn = alloc_node<ConstantNode>(); cn->value=Variant::get_numeric_constant_value(bi_type,identifier); expr=cn; - tokenizer->advance(); + } else if (tokenizer->get_token(1)==GDTokenizer::TK_PARENTHESIS_OPEN && (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_TYPE || tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER || tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_FUNC)) { //function or constructor @@ -355,23 +401,35 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ TypeNode *tn = alloc_node<TypeNode>(); tn->vtype=tokenizer->get_token_type(); op->arguments.push_back(tn); + tokenizer->advance(2); } else if (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_FUNC) { BuiltInFunctionNode *bn = alloc_node<BuiltInFunctionNode>(); bn->function=tokenizer->get_token_built_in_func(); op->arguments.push_back(bn); + tokenizer->advance(2); } else { SelfNode *self = alloc_node<SelfNode>(); op->arguments.push_back(self); + StringName identifier; + if (_get_completable_identifier(COMPLETION_FUNCTION,identifier)) { + + } + IdentifierNode* id = alloc_node<IdentifierNode>(); - id->name=tokenizer->get_token_identifier(); + id->name=identifier; op->arguments.push_back(id); + tokenizer->advance(1); } - tokenizer->advance(2); - if (!_parse_arguments(op,op->arguments,p_static)) + if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { + _make_completable_call(0); + completion_node=op; + + } + if (!_parse_arguments(op,op->arguments,p_static,true)) return NULL; expr=op; @@ -380,25 +438,27 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ //identifier (reference) const ClassNode* cln = static_cast<const ClassNode*>(get_parse_tree()); - bool bfn = false; - StringName idn( tokenizer->get_token_identifier() ); - - for( int i=0; i<cln->constant_expressions.size(); ++i ) { - - if( cln->constant_expressions[i].identifier == idn ) { - tokenizer->advance(); - expr = cln->constant_expressions[i].expression; - bfn = true; - break; - } - } - - if( !bfn ) { - IdentifierNode *id = alloc_node<IdentifierNode>(); - id->name = idn; - tokenizer->advance(); - expr = id; - } + bool bfn = false; + StringName identifier; + if (_get_completable_identifier(COMPLETION_IDENTIFIER,identifier)) { + + } + + for( int i=0; i<cln->constant_expressions.size(); ++i ) { + + if( cln->constant_expressions[i].identifier == identifier ) { + + expr = cln->constant_expressions[i].expression; + bfn = true; + break; + } + } + + if ( !bfn ) { + IdentifierNode *id = alloc_node<IdentifierNode>(); + id->name = identifier; + expr = id; + } } else if (/*tokenizer->get_token()==GDTokenizer::TK_OP_ADD ||*/ tokenizer->get_token()==GDTokenizer::TK_OP_SUB || tokenizer->get_token()==GDTokenizer::TK_OP_NOT || tokenizer->get_token()==GDTokenizer::TK_OP_BIT_INVERT) { @@ -600,7 +660,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ expr=dict; - } else if (tokenizer->get_token()==GDTokenizer::TK_PERIOD && tokenizer->get_token(1)==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token(2)==GDTokenizer::TK_PARENTHESIS_OPEN) { + } else if (tokenizer->get_token()==GDTokenizer::TK_PERIOD && (tokenizer->get_token(1)==GDTokenizer::TK_IDENTIFIER || tokenizer->get_token(1)==GDTokenizer::TK_CURSOR) && tokenizer->get_token(2)==GDTokenizer::TK_PARENTHESIS_OPEN) { // parent call tokenizer->advance(); //goto identifier @@ -611,12 +671,16 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ /*SelfNode *self = alloc_node<SelfNode>(); op->arguments.push_back(self); forbidden for now */ + StringName identifier; + if (_get_completable_identifier(COMPLETION_PARENT_FUNCTION,identifier)) { + //indexing stuff + } - IdentifierNode* id = alloc_node<IdentifierNode>(); - id->name=tokenizer->get_token_identifier(); + IdentifierNode *id = alloc_node<IdentifierNode>(); + id->name=identifier; op->arguments.push_back(id); - tokenizer->advance(2); + tokenizer->advance(1); if (!_parse_arguments(op,op->arguments,p_static)) return NULL; @@ -651,7 +715,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ //indexing using "." - if (tokenizer->get_token(1)!=GDTokenizer::TK_IDENTIFIER && tokenizer->get_token(1)!=GDTokenizer::TK_BUILT_IN_FUNC ) { + if (tokenizer->get_token(1)!=GDTokenizer::TK_CURSOR && tokenizer->get_token(1)!=GDTokenizer::TK_IDENTIFIER && tokenizer->get_token(1)!=GDTokenizer::TK_BUILT_IN_FUNC ) { _set_error("Expected identifier as member"); return NULL; } else if (tokenizer->get_token(2)==GDTokenizer::TK_PARENTHESIS_OPEN) { @@ -659,37 +723,67 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ OperatorNode * op = alloc_node<OperatorNode>(); op->op=OperatorNode::OP_CALL; + tokenizer->advance(); + IdentifierNode * id = alloc_node<IdentifierNode>(); - if (tokenizer->get_token(1)==GDTokenizer::TK_BUILT_IN_FUNC ) { + if (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_FUNC ) { //small hack so built in funcs don't obfuscate methods - id->name=GDFunctions::get_func_name(tokenizer->get_token_built_in_func(1)); + id->name=GDFunctions::get_func_name(tokenizer->get_token_built_in_func()); + tokenizer->advance(); + } else { - id->name=tokenizer->get_token_identifier(1); + StringName identifier; + if (_get_completable_identifier(COMPLETION_METHOD,identifier)) { + completion_node=op; + //indexing stuff + } + + id->name=identifier; } op->arguments.push_back(expr); // call what op->arguments.push_back(id); // call func //get arguments - tokenizer->advance(3); - if (!_parse_arguments(op,op->arguments,p_static)) + tokenizer->advance(1); + if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { + _make_completable_call(0); + completion_node=op; + + } + if (!_parse_arguments(op,op->arguments,p_static,true)) return NULL; expr=op; } else { //simple indexing! + + OperatorNode * op = alloc_node<OperatorNode>(); op->op=OperatorNode::OP_INDEX_NAMED; + tokenizer->advance(); + + + StringName identifier; + if (_get_completable_identifier(COMPLETION_INDEX,identifier)) { + + if (identifier==StringName()) { + identifier="@temp"; //so it parses allright + } + completion_node=op; + + //indexing stuff + } IdentifierNode * id = alloc_node<IdentifierNode>(); - id->name=tokenizer->get_token_identifier(1); + id->name=identifier; op->arguments.push_back(expr); op->arguments.push_back(id); expr=op; - tokenizer->advance(2); + } } else if (tokenizer->get_token()==GDTokenizer::TK_BRACKET_OPEN) { @@ -1442,6 +1536,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { cf_if->arguments.push_back(condition); cf_if->body = alloc_node<BlockNode>(); + cf_if->body->parent_block=p_block; p_block->sub_blocks.push_back(cf_if->body); if (!_enter_indent_block(cf_if->body)) { @@ -1449,7 +1544,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { return; } + current_block=cf_if->body; _parse_block(cf_if->body,p_static); + current_block=p_block; + if (error_set) return; p_block->statements.push_back(cf_if); @@ -1476,6 +1574,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { tokenizer->advance(); cf_if->body_else=alloc_node<BlockNode>(); + cf_if->body_else->parent_block=p_block; p_block->sub_blocks.push_back(cf_if->body_else); ControlFlowNode *cf_else = alloc_node<ControlFlowNode>(); @@ -1491,6 +1590,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { cf_if->body_else->statements.push_back(cf_else); cf_if=cf_else; cf_if->body=alloc_node<BlockNode>(); + cf_if->body->parent_block=p_block; p_block->sub_blocks.push_back(cf_if->body); @@ -1499,7 +1599,9 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { return; } + current_block=cf_else->body; _parse_block(cf_else->body,p_static); + current_block=p_block; if (error_set) return; @@ -1515,13 +1617,16 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { tokenizer->advance(); cf_if->body_else=alloc_node<BlockNode>(); + cf_if->body_else->parent_block=p_block; p_block->sub_blocks.push_back(cf_if->body_else); if (!_enter_indent_block(cf_if->body_else)) { p_block->end_line=tokenizer->get_token_line(); return; } + current_block=cf_if->body_else; _parse_block(cf_if->body_else,p_static); + current_block=p_block; if (error_set) return; @@ -1548,6 +1653,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { cf_while->arguments.push_back(condition); cf_while->body = alloc_node<BlockNode>(); + cf_while->body->parent_block=p_block; p_block->sub_blocks.push_back(cf_while->body); if (!_enter_indent_block(cf_while->body)) { @@ -1555,7 +1661,9 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { return; } + current_block=cf_while->body; _parse_block(cf_while->body,p_static); + current_block=p_block; if (error_set) return; p_block->statements.push_back(cf_while); @@ -1592,6 +1700,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { cf_for->arguments.push_back(container); cf_for->body = alloc_node<BlockNode>(); + cf_for->body->parent_block=p_block; p_block->sub_blocks.push_back(cf_for->body); if (!_enter_indent_block(cf_for->body)) { @@ -1599,7 +1708,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { return; } + current_block=cf_for->body; _parse_block(cf_for->body,p_static); + current_block=p_block; + if (error_set) return; p_block->statements.push_back(cf_for); @@ -1865,7 +1977,9 @@ void GDParser::_parse_class(ClassNode *p_class) { ClassNode *newclass = alloc_node<ClassNode>(); newclass->initializer = alloc_node<BlockNode>(); + newclass->initializer->parent_class=newclass; newclass->name=name; + newclass->owner=p_class; p_class->subclasses.push_back(newclass); @@ -1882,7 +1996,9 @@ void GDParser::_parse_class(ClassNode *p_class) { _set_error("Indented block expected."); return; } + current_class=newclass; _parse_class(newclass); + current_class=p_class; } break; /* this is for functions.... @@ -1911,14 +2027,20 @@ void GDParser::_parse_class(ClassNode *p_class) { } - if (tokenizer->get_token(1)!=GDTokenizer::TK_IDENTIFIER) { + tokenizer->advance(); + StringName name; + + if (_get_completable_identifier(COMPLETION_VIRTUAL_FUNC,name)) { + + } + + + if (name==StringName()) { _set_error("Expected identifier after 'func' (syntax: 'func <identifier>([arguments]):' )."); return; } - StringName name = tokenizer->get_token_identifier(1); - for(int i=0;i<p_class->functions.size();i++) { if (p_class->functions[i]->name==name) { _set_error("Function '"+String(name)+"' already exists in this class (at line: "+itos(p_class->functions[i]->line)+")."); @@ -1929,7 +2051,7 @@ void GDParser::_parse_class(ClassNode *p_class) { _set_error("Function '"+String(name)+"' already exists in this class (at line: "+itos(p_class->static_functions[i]->line)+")."); } } - tokenizer->advance(2); + if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_OPEN) { @@ -2020,6 +2142,7 @@ void GDParser::_parse_class(ClassNode *p_class) { tokenizer->advance(); BlockNode *block = alloc_node<BlockNode>(); + block->parent_class=p_class; if (name=="_init") { @@ -2095,8 +2218,12 @@ void GDParser::_parse_class(ClassNode *p_class) { p_class->functions.push_back(function); - _parse_block(block,_static); + current_function=function; function->body=block; + current_block=block; + _parse_block(block,_static); + current_block=NULL; + //arguments } break; case GDTokenizer::TK_PR_EXPORT: { @@ -2401,7 +2528,9 @@ void GDParser::_parse_class(ClassNode *p_class) { } member.identifier=tokenizer->get_token_identifier(); + member.expression=NULL; member._export.name=member.identifier; + member.line=tokenizer->get_token_line(); tokenizer->advance(); if (tokenizer->get_token()==GDTokenizer::TK_OP_ASSIGN) { @@ -2417,6 +2546,8 @@ void GDParser::_parse_class(ClassNode *p_class) { if (!subexpr) return; + member.expression=subexpr; + if (autoexport) { if (subexpr->type==Node::TYPE_ARRAY) { @@ -2608,6 +2739,8 @@ Error GDParser::_parse(const String& p_base_path) { //assume class ClassNode *main_class = alloc_node<ClassNode>(); main_class->initializer = alloc_node<BlockNode>(); + main_class->initializer->parent_class=main_class; + current_class=main_class; _parse_class(main_class); @@ -2625,6 +2758,15 @@ Error GDParser::_parse(const String& p_base_path) { Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p_base_path, const String &p_self_path) { + completion_type=COMPLETION_NONE; + completion_node=NULL; + completion_class=NULL; + completion_function=NULL; + completion_block=NULL; + current_block=NULL; + current_class=NULL; + current_function=NULL; + self_path=p_self_path; GDTokenizerBuffer *tb = memnew( GDTokenizerBuffer ); tb->set_code_buffer(p_bytecode); @@ -2638,6 +2780,16 @@ Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_just_validate, const String &p_self_path) { + completion_type=COMPLETION_NONE; + completion_node=NULL; + completion_class=NULL; + completion_function=NULL; + completion_block=NULL; + current_block=NULL; + current_class=NULL; + + current_function=NULL; + self_path=p_self_path; GDTokenizerText *tt = memnew( GDTokenizerText ); tt->set_code(p_code); @@ -2667,6 +2819,16 @@ void GDParser::clear() { head=NULL; list=NULL; + completion_type=COMPLETION_NONE; + completion_node=NULL; + completion_class=NULL; + completion_function=NULL; + completion_block=NULL; + current_block=NULL; + current_class=NULL; + + current_function=NULL; + validating=false; error_set=false; tab_level.clear(); @@ -2680,6 +2842,52 @@ void GDParser::clear() { } + +GDParser::CompletionType GDParser::get_completion_type() { + + return completion_type; +} + +StringName GDParser::get_completion_cursor() { + + return completion_cursor; +} + +int GDParser::get_completion_line() { + + return completion_line; +} + +Variant::Type GDParser::get_completion_built_in_constant(){ + + return completion_built_in_constant; +} + +GDParser::Node *GDParser::get_completion_node(){ + + return completion_node; +} + +GDParser::BlockNode *GDParser::get_completion_block() { + + return completion_block; +} + +GDParser::ClassNode *GDParser::get_completion_class(){ + + return completion_class; +} + +GDParser::FunctionNode *GDParser::get_completion_function(){ + + return completion_function; +} + +int GDParser::get_completion_argument_index() { + + return completion_argument; +} + GDParser::GDParser() { head=NULL; diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h index 3f82cafc61..44e7b55323 100644 --- a/modules/gdscript/gd_parser.h +++ b/modules/gdscript/gd_parser.h @@ -84,6 +84,8 @@ public: StringName identifier; StringName setter; StringName getter; + int line; + Node *expression; }; struct Constant { StringName identifier; @@ -96,10 +98,11 @@ public: Vector<FunctionNode*> functions; Vector<FunctionNode*> static_functions; BlockNode *initializer; + ClassNode *owner; //Vector<Node*> initializers; int end_line; - ClassNode() { tool=false; type=TYPE_CLASS; extends_used=false; end_line=-1;} + ClassNode() { tool=false; type=TYPE_CLASS; extends_used=false; end_line=-1; owner=NULL;} }; @@ -118,6 +121,8 @@ public: struct BlockNode : public Node { + ClassNode *parent_class; + BlockNode *parent_block; Map<StringName,int> locals; List<Node*> statements; Vector<StringName> variables; @@ -126,7 +131,7 @@ public: //the following is useful for code completion List<BlockNode*> sub_blocks; int end_line; - BlockNode() { type=TYPE_BLOCK; end_line=-1;} + BlockNode() { type=TYPE_BLOCK; end_line=-1; parent_block=NULL; parent_class=NULL; } }; struct TypeNode : public Node { @@ -349,6 +354,19 @@ public: }; */ + enum CompletionType { + COMPLETION_NONE, + COMPLETION_BUILT_IN_TYPE_CONSTANT, + COMPLETION_FUNCTION, + COMPLETION_IDENTIFIER, + COMPLETION_PARENT_FUNCTION, + COMPLETION_METHOD, + COMPLETION_CALL_ARGUMENTS, + COMPLETION_INDEX, + COMPLETION_VIRTUAL_FUNC + }; + + private: @@ -375,12 +393,31 @@ private: String base_path; String self_path; + + ClassNode *current_class; + FunctionNode *current_function; + BlockNode *current_block; + + bool _get_completable_identifier(CompletionType p_type,StringName& identifier); + void _make_completable_call(int p_arg); + + CompletionType completion_type; + StringName completion_cursor; + bool completion_static; + Variant::Type completion_built_in_constant; + Node *completion_node; + ClassNode *completion_class; + FunctionNode *completion_function; + BlockNode *completion_block; + int completion_line; + int completion_argument; + PropertyInfo current_export; void _set_error(const String& p_error, int p_line=-1, int p_column=-1); - bool _parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_static); + bool _parse_arguments(Node* p_parent, Vector<Node*>& p_args, bool p_static, bool p_can_codecomplete=false); bool _enter_indent_block(BlockNode *p_block=NULL); bool _parse_newline(); Node* _parse_expression(Node *p_parent,bool p_static,bool p_allow_assign=false); @@ -404,6 +441,19 @@ public: const Node *get_parse_tree() const; + //completion info + + CompletionType get_completion_type(); + StringName get_completion_cursor(); + int get_completion_line(); + Variant::Type get_completion_built_in_constant(); + Node *get_completion_node(); + ClassNode *get_completion_class(); + BlockNode *get_completion_block(); + FunctionNode *get_completion_function(); + int get_completion_argument_index(); + + void clear(); GDParser(); ~GDParser(); diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index 982584c75c..0aa115ffbc 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -820,7 +820,7 @@ Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_a gdfs->state.stack.resize(alloca_size); //copy variant stack for(int i=0;i<_stack_size;i++) { - memnew_placement(&stack[sizeof(Variant)*i],Variant(stack[i])); + memnew_placement(&gdfs->state.stack[sizeof(Variant)*i],Variant(stack[i])); } gdfs->state.stack_size=_stack_size; gdfs->state.self=self; @@ -1440,8 +1440,8 @@ GDInstance* GDScript::_create_instance(const Variant** p_args,int p_argcount,Obj if (err.error!=Variant::CallError::CALL_OK) { instance->script=Ref<GDScript>(); + instance->owner->set_script_instance(NULL); instances.erase(p_owner); - memdelete(instance); ERR_FAIL_COND_V(err.error!=Variant::CallError::CALL_OK, NULL); //error consrtucting } @@ -1756,6 +1756,7 @@ bool GDScript::_update_exports() { return changed; #endif + return false; } void GDScript::update_exports() { diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index f4e4dffaa5..5574b30d44 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -129,6 +129,10 @@ friend class GDCompiler; const char*_func_cname; #endif +#ifdef TOOLS_ENABLED + Vector<StringName> arg_names; +#endif + List<StackDebug> stack_debug; _FORCE_INLINE_ Variant *_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self,Variant *p_stack,String& r_error) const; @@ -169,6 +173,19 @@ public: _FORCE_INLINE_ bool is_empty() const { return _code_size==0; } int get_argument_count() const { return _argument_count; } + StringName get_argument_name(int p_idx) const { +#ifdef TOOLS_ENABLED + ERR_FAIL_INDEX_V(p_idx,arg_names.size(),StringName()); + return arg_names[p_idx]; +#endif + return StringName(); + + } + Variant get_default_argument(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx,default_arguments.size(),Variant()); + return default_arguments[p_idx]; + } + Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err,CallState *p_state=NULL); GDFunction(); @@ -293,6 +310,7 @@ protected: static void _bind_methods(); public: + bool is_valid() const { return valid; } const Map<StringName,Ref<GDScript> >& get_subclasses() const { return subclasses; } const Map<StringName,Variant >& get_constants() const { return constants; } @@ -488,7 +506,7 @@ public: virtual bool has_named_classes() const; virtual int find_function(const String& p_function,const String& p_code) const; virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const; - virtual Error complete_keyword(const String& p_code, int p_line, const String& p_base_path,const String& p_keyword, List<String>* r_options); + virtual Error complete_code(const String& p_code, const String& p_base_path, Object*p_owner,List<String>* r_options,String& r_call_hint); virtual void auto_indent_code(String& p_code,int p_from_line,int p_to_line) const; /* DEBUGGER FUNCTIONS */ diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp index 0f6ee41616..6f968f2080 100644 --- a/modules/gdscript/gd_tokenizer.cpp +++ b/modules/gdscript/gd_tokenizer.cpp @@ -110,7 +110,8 @@ const char* GDTokenizer::token_names[TK_MAX]={ "':'", "'\\n'", "Error", -"EOF"}; +"EOF", +"Cursor"}; const char *GDTokenizer::get_token_name(Token p_token) { @@ -648,6 +649,9 @@ void GDTokenizerText::_advance() { } } break; + case 0xFFFF: { + _make_token(TK_CURSOR); + } break; default: { if (_is_number(GETCHAR(0)) || (GETCHAR(0)=='.' && _is_number(GETCHAR(1)))) { diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h index fe7bfa73ca..ff59c249a7 100644 --- a/modules/gdscript/gd_tokenizer.h +++ b/modules/gdscript/gd_tokenizer.h @@ -118,6 +118,7 @@ public: TK_NEWLINE, TK_ERROR, TK_EOF, + TK_CURSOR, //used for code completion TK_MAX }; |