summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2014-12-16 22:31:57 -0300
committerJuan Linietsky <reduzio@gmail.com>2014-12-16 22:31:57 -0300
commitbcf27feb980aec593c7cb771984e46113cfad757 (patch)
treedabc98af627732ccf5d1bbfa8aa58348030f6324 /modules
parentbe4e40e90a5a322f6a7cec4893854ef5b15db600 (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.cpp7
-rw-r--r--modules/gdscript/gd_editor.cpp1391
-rw-r--r--modules/gdscript/gd_parser.cpp295
-rw-r--r--modules/gdscript/gd_parser.h55
-rw-r--r--modules/gdscript/gd_script.h20
-rw-r--r--modules/gdscript/gd_tokenizer.cpp6
-rw-r--r--modules/gdscript/gd_tokenizer.h1
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
};