diff options
author | Juan Linietsky <reduzio@gmail.com> | 2014-12-16 22:31:57 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2014-12-16 22:31:57 -0300 |
commit | bcf27feb980aec593c7cb771984e46113cfad757 (patch) | |
tree | dabc98af627732ccf5d1bbfa8aa58348030f6324 /modules | |
parent | be4e40e90a5a322f6a7cec4893854ef5b15db600 (diff) |
New Code Completion
-=-=-=-=-=-=-=-=-=-
-Massive improvement to code completion
-Argument hinting for functions
If you manage to out-smart the code-completion in a situation where completion
should be possible to guess, let me know.
Please enter the commit message for your changes. Lines starting
Diffstat (limited to 'modules')
-rw-r--r-- | modules/gdscript/gd_compiler.cpp | 7 | ||||
-rw-r--r-- | modules/gdscript/gd_editor.cpp | 1391 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.cpp | 295 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.h | 55 | ||||
-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 |
7 files changed, 1379 insertions, 396 deletions
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index 5595db95de..d4fe8b626b 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -1168,6 +1168,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 +1176,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 +1253,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(); diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index 546fed4e8a..7bec0ebd91 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -312,171 +312,277 @@ 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 (!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; + + Ref<GDScript> script; + Ref<GDNativeClass> native; - case Variant::DICTIONARY: { + if (path!="") { + //path (and optionally subclasses) - 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.is_rel_path()) { - if (E->get().get_type()==Variant::STRING) - r_options->push_back(E->get()); - } + path=context.base_path.plus_file(path); + } + script = ResourceLoader::load(path); + if (script.is_null()) { + return REF(); + } + if (script->is_valid()) { + + 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)) { - 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); + script=script->get_subclasses()[sub]; + } else { + + return REF(); + } } + } + + if (script.is_valid()) + return script; - } break; - case Variant::OBJECT: { + } else { + if (context._class->extends_class.size()==0) { + ERR_PRINT("BUG"); + return REF(); + } - Object *o=p_var; - if (o) { - print_line("OBJECT: "+o->get_type()); - if (p_var.is_ref() && o->cast_to<GDScript>()) { + String base=context._class->extends_class[0]; + const GDParser::ClassNode *p = context._class->owner; + Ref<GDScript> base_class; +#if 0 + while(p) { - Ref<GDScript> gds = p_var; - _parse_script_symbols(gds,true,r_options,NULL); - } else if (o->is_type("GDNativeClass")){ + if (p->subclasses.has(base)) { - GDNativeClass *gnc = o->cast_to<GDNativeClass>(); - _parse_native_symbols(gnc->get_name(),false,r_options); + 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++) { + + String subclass=context._class->extends_class[i]; + + if (base_class->subclasses.has(subclass)) { + + 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(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type); -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_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 +592,76 @@ 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 (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 @@ -534,49 +691,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 +850,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 +862,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 (p_block->statements[i]->line>p_line) - break; + if (context.block->statements[i]->line>p_line) + continue; - if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) { + if (context.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,382 +906,830 @@ 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) { - - - return GDCompletionIdentifier(); -} +static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) { -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) { + //go to block first + const GDParser::BlockNode *block=context.block; - if (p_node->type==GDParser::Node::TYPE_CONSTANT) { + while(block) { - 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) { + GDCompletionContext c = context; + c.block=block; - 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) { + //TODO guess identifier type of arguments (ONLY if this is a virtual function) + 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) { + + 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; + } + } - for (int i=0;i<p_block->statements.size();i++) { + if (context.function && !context.function->_static) { - if (p_block->statements[i]->line>p_line) - break; + 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; - } + //globals - } 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) { - //for (Map<StringName,Ref<GDScript> >::Element ? + if (p_only_functions) + return; - if (!p_static && !p_indices) { - for(const Set<StringName>::Element *E=p_script->get_members().front();E;E=E->next()) { + for (int i=0;i<context.block->statements.size();i++) { - r_options->push_back(E->get()); + if (context.block->statements[i]->line>p_line) + continue; + + + if (context.block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) { + + 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()+"()"); + } + } -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) { + Ref<Reference> base = _get_parent_class(context); - //checks known classes or built-in types for completion + while(true) { - 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"}; + Ref<GDScript> script = base; + Ref<GDNativeClass> nc = base; + if (script.is_valid()) { - for(int i=0;i<Variant::VARIANT_MAX;i++) { + 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 (p_indices->get()==_type_names[i]) { + 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()); + } + } - List<StringName> ic; + 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()+"()"); + } + } - 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()); + 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()); } - return true; } - } + + base=script->get_base(); + if (base.is_null()) + base=script->get_native(); + } else if (nc.is_valid()) { + + if (!p_only_functions) { + + 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()); + } + + 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; + } - // 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; + + bool _static=context.function->_static; - for(int i=0;i<p_class->functions.size();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; + } - const GDParser::FunctionNode *fu = p_class->functions[i]; + for(int i=0;i<GDFunctions::FUNC_MAX;i++) { - 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++) { + result.insert(GDFunctions::get_func_name(GDFunctions::Function(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) { + + 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) + 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::OBJECT && id.obj_type!=StringName()) { + + + MethodBind *m = ObjectTypeDB::get_method(id.obj_type,p_method); + if (!m) + return; - r_options->push_back(fu->arguments[j]); + if (p_method.operator String()=="connect") { + + + 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) { - in_static_func=true; - } + 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()>) - } + }*/ + } else { + 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()) { - //add all local names - if (!p_indices) { + result.insert(E->get()); + } + } - if (!in_static_func) { + } - for(int i=0;i<p_class->variables.size();i++) { + arghint = _get_visual_datatype(m->get_argument_info(-1))+" "+p_method.operator String()+String("("); + for(int i=0;i<m->get_argument_count();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; - r_options->push_back(p_class->variables[i].identifier); + 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(); - for(int i=0;i<p_class->constant_expressions.size();i++) { - r_options->push_back(p_class->constant_expressions[i].identifier); - } + if (i>=deffrom) { + int defidx = i-deffrom; - if (!in_static_func) { - for(int i=0;i<p_class->functions.size();i++) { + if (defidx>=0 && defidx<m->get_default_argument_count()) { + Variant v= m->get_default_argument(i); + arghint+="="+v.get_construct_string(); + } + } - r_options->push_back(p_class->functions[i]->name); + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); } + } + if (m->get_argument_count()>0) + arghint+=" "; - for(int i=0;i<p_class->static_functions.size();i++) { - r_options->push_back(p_class->static_functions[i]->name); - } + 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)+" "+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) { + + 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()); + } + + 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; - _parse_native_symbols(native->get_name(),in_static_func,r_options); - } } - return false; + for(int i=0;i<GDFunctions::FUNC_MAX;i++) { -} + result.insert(GDFunctions::get_func_name(GDFunctions::Function(i))); + } +#endif -Error GDScriptLanguage::complete_keyword(const String& p_code, int p_line, const String& p_base_path, const String& p_base, List<String>* r_options) { +} +Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base_path, Object*p_owner, List<String>* r_options, String &r_call_hint) { +/* bugs: + a[0].<complete> does not work + functions should end in ( + when completing virtuals, ask for full back + */ + //print_line( p_code.replace(String::chr(0xFFFF),"<cursor>")); GDParser p; Error err = p.parse(p_code,p_base_path); + 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()); + } + + + } break; + case GDParser::COMPLETION_FUNCTION: + isfunction=true; + case GDParser::COMPLETION_IDENTIFIER: { + + _find_identifiers(context,p.get_completion_line(),isfunction,options); + } break; + case GDParser::COMPLETION_PARENT_FUNCTION: { + print_line("parent function"); + + } break; + case GDParser::COMPLETION_METHOD: + isfunction=true; + case GDParser::COMPLETION_INDEX: { + + print_line("index"); + const GDParser::Node *node = p.get_completion_node(); + if (node->type!=GDParser::Node::TYPE_OPERATOR) + break; + + + + + 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 (!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().arguments.size()) + options.insert(E->get().name+"("); + else + options.insert(E->get().name+"()"); + + } + } 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+"()"); + + } + } + } + + + + } 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; + + + } + + + for(Set<String>::Element *E=options.front();E;E=E->next()) { + r_options->push_back(E->get()); + } +#if 0 // 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); @@ -1050,10 +1762,11 @@ Error GDScriptLanguage::complete_keyword(const String& p_code, int p_line, const r_options->push_back(E->key()); } } - +#endif return OK; } + void GDScriptLanguage::auto_indent_code(String& p_code,int p_from_line,int p_to_line) const { diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index 904b6ba52f..1c46a2f4fa 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.... @@ -2020,6 +2136,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 +2212,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 +2522,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 +2540,8 @@ void GDParser::_parse_class(ClassNode *p_class) { if (!subexpr) return; + member.expression=subexpr; + if (autoexport) { if (subexpr->type==Node::TYPE_ARRAY) { @@ -2608,6 +2733,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 +2752,12 @@ 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; + self_path=p_self_path; GDTokenizerBuffer *tb = memnew( GDTokenizerBuffer ); tb->set_code_buffer(p_bytecode); @@ -2638,6 +2771,12 @@ 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; + self_path=p_self_path; GDTokenizerText *tt = memnew( GDTokenizerText ); tt->set_code(p_code); @@ -2667,6 +2806,12 @@ void GDParser::clear() { head=NULL; list=NULL; + completion_type=COMPLETION_NONE; + completion_node=NULL; + completion_class=NULL; + completion_function=NULL; + completion_block=NULL; + validating=false; error_set=false; tab_level.clear(); @@ -2680,6 +2825,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..107f1439c0 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=NULL; + BlockNode *parent_block=NULL; 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,18 @@ public: }; */ + enum CompletionType { + COMPLETION_NONE, + COMPLETION_BUILT_IN_TYPE_CONSTANT, + COMPLETION_FUNCTION, + COMPLETION_IDENTIFIER, + COMPLETION_PARENT_FUNCTION, + COMPLETION_METHOD, + COMPLETION_CALL_ARGUMENTS, + COMPLETION_INDEX, + }; + + private: @@ -375,12 +392,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 +440,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.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 }; |