aboutsummaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/gd_editor.cpp307
-rw-r--r--modules/gdscript/gd_parser.cpp16
-rw-r--r--modules/gdscript/gd_parser.h5
-rw-r--r--modules/gdscript/gd_script.cpp166
-rw-r--r--modules/gdscript/gd_script.h13
5 files changed, 442 insertions, 65 deletions
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp
index b1db087fb..546fed4e8 100644
--- a/modules/gdscript/gd_editor.cpp
+++ b/modules/gdscript/gd_editor.cpp
@@ -66,7 +66,7 @@ bool GDScriptLanguage::validate(const String& p_script, int &r_line_error,int &r
GDParser parser;
- Error err = parser.parse(p_script,p_path.get_base_dir(),true);
+ Error err = parser.parse(p_script,p_path.get_base_dir(),true,p_path);
if (err) {
r_line_error=parser.get_error_line();
r_col_error=parser.get_error_column();
@@ -421,8 +421,240 @@ static bool _parse_completion_variant(const Variant& p_var,List<String>* r_optio
}
+struct GDCompletionIdentifier {
-static void _parse_expression_node(const GDParser::Node *p_node,List<String>* r_options,List<String>::Element *p_indices) {
+ StringName obj_type;
+ Variant::Type type;
+};
+
+
+static GDCompletionIdentifier _guess_identifier_type(const GDParser::ClassNode *p_class,int p_line,const StringName& p_identifier);
+
+
+static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_class,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();
+ }
+ }
+
+ return true;
+ } else if (p_node->type==GDParser::Node::TYPE_DICTIONARY) {
+
+ r_type.type=Variant::DICTIONARY;
+ return true;
+ } else if (p_node->type==GDParser::Node::TYPE_ARRAY) {
+
+ r_type.type=Variant::ARRAY;
+ 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;
+ }
+ 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;
+ } else if (p_node->type==GDParser::Node::TYPE_SELF) {
+ //eeh...
+ return false;
+
+ } 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]);
+ r_type.type=tn->vtype;
+ return true;
+ } else if (op->arguments[0]->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) {
+
+
+ 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);
+
+ } 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))
+ return false;
+ StringName id = static_cast<const GDParser::IdentifierNode *>(p_node)->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);
+
+ r_type.type=pi.type;
+ if (pi.hint==PROPERTY_HINT_RESOURCE_TYPE) {
+ r_type.obj_type=pi.hint_string;
+ }
+#else
+ return false;
+#endif
+ } else {
+ return false;
+ }
+ } else {
+ //method for some variant..
+ Variant::CallError ce;
+ Variant v = Variant::construct(base.type,NULL,0,ce);
+ List<MethodInfo> mi;
+ v.get_method_list(&mi);
+ for (List<MethodInfo>::Element *E=mi.front();E;E=E->next()) {
+
+ if (E->get().name==id.operator String()) {
+
+ MethodInfo mi = E->get();
+ 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;
+ }
+ 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;
+ 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))
+ return false;
+ }
+
+ if (op->arguments.size()>1) {
+ if (!_guess_identifier_type_in_expression(p_class,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);
+ // avoid potential invalid ops
+ if ((vop==Variant::OP_DIVIDE || vop==Variant::OP_MODULE) && v2.get_type()==Variant::INT) {
+ v2=1;
+ }
+ if (vop==Variant::OP_DIVIDE && v2.get_type()==Variant::REAL) {
+ v2=1.0;
+ }
+
+ Variant r;
+ bool valid;
+ Variant::evaluate(vop,v1,v2,r,valid);
+ if (!valid)
+ return false;
+ r_type.type=r.get_type();
+ return true;
+
+ }
+
+ }
+
+ 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;
+ }
+ }
+
+
+
+ const GDParser::Node *last_assign=NULL;
+ int last_assign_line=-1;
+
+ for (int i=0;i<p_block->statements.size();i++) {
+
+ if (p_block->statements[i]->line>p_line)
+ break;
+
+
+ if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) {
+
+ 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;
+ }
+ }
+
+ if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_OPERATOR) {
+ const GDParser::OperatorNode *op = static_cast<const GDParser::OperatorNode *>(p_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;
+ }
+ }
+ }
+ }
+ }
+
+ //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 false;
+}
+
+static GDCompletionIdentifier _guess_identifier_type(const GDParser::ClassNode *p_class,int p_line,const StringName& p_identifier) {
+
+
+ return GDCompletionIdentifier();
+}
+
+static void _parse_expression_node(const GDParser::ClassNode *p_class,const GDParser::Node *p_node,int p_line,List<String>* r_options,List<String>::Element *p_indices) {
@@ -433,7 +665,7 @@ static void _parse_expression_node(const GDParser::Node *p_node,List<String>* r_
} else if (p_node->type==GDParser::Node::TYPE_DICTIONARY) {
const GDParser::DictionaryNode *dn=static_cast<const GDParser::DictionaryNode*>(p_node);
- for(int i=0;i<dn->elements.size();i++) {
+ for (int i=0;i<dn->elements.size();i++) {
if (dn->elements[i].key->type==GDParser::Node::TYPE_CONSTANT) {
@@ -444,7 +676,7 @@ static void _parse_expression_node(const GDParser::Node *p_node,List<String>* r_
if (p_indices) {
if (str==p_indices->get()) {
- _parse_expression_node(dn->elements[i].value,r_options,p_indices->next());
+ _parse_expression_node(p_class,dn->elements[i].value,p_line,r_options,p_indices->next());
return;
}
@@ -454,15 +686,28 @@ static void _parse_expression_node(const GDParser::Node *p_node,List<String>* r_
}
}
}
+ } else if (p_node->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) {
+
+ MethodInfo mi = GDFunctions::get_info(static_cast<const GDParser::BuiltInFunctionNode*>(p_node)->function);
+
+ 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) {
+
+ //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::BlockNode *p_block,int p_line,List<String>* r_options,List<String>::Element *p_indices) {
+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) {
+
+ print_line("COMPLETION BLOCK "+itos(p_block->line)+" -> "+itos(p_block->end_line));
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_block->sub_blocks[i],p_line,r_options,p_indices))
+ if (_parse_completion_block(p_class,p_block->sub_blocks[i],p_line,r_options,p_indices))
return true;
}
}
@@ -470,29 +715,39 @@ static bool _parse_completion_block(const GDParser::BlockNode *p_block,int p_lin
if (p_indices) {
//parse indices in expressions :|
+
+ const GDParser::Node *last_assign=NULL;
+ int last_assign_line=-1;
+
for (int i=0;i<p_block->statements.size();i++) {
if (p_block->statements[i]->line>p_line)
break;
+
if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) {
- const GDParser::LocalVarNode *lv=static_cast<const GDParser::LocalVarNode *>(p_block->statements[i]);
+ const GDParser::LocalVarNode *lv=static_cast<const GDParser::LocalVarNode *>(p_block->statements[i]);
if (lv->assign && String(lv->name)==p_indices->get()) {
-
- _parse_expression_node(lv->assign,r_options,p_indices->next());
- return true;
+ last_assign=lv->assign;
+ last_assign_line=p_block->statements[i]->line;
}
}
}
+ //use the last assignment, (then backwards?)
+ if (last_assign) {
+ _parse_expression_node(p_class,last_assign,last_assign_line,r_options,p_indices->next());
+ return true;
+ }
+
} else {
+ //no indices, just add all variables and continue
for(int i=0;i<p_block->variables.size();i++) {
//parse variables second
if (p_line>=p_block->variable_lines[i]) {
r_options->push_back(p_block->variables[i]);
- }
- else break;
+ } else break;
}
}
@@ -545,13 +800,15 @@ static bool _parse_script_symbols(const Ref<GDScript>& p_script,bool p_static,Li
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) {
-
- 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"};
+ //checks known classes or built-in types for completion
if (p_indices && !p_indices->next()) {
+ //built-in types do not have sub-classes, try these first if no sub-indices exist.
+ static const char*_type_names[Variant::VARIANT_MAX]={
+ "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Trasnform",
+ "Color","Image","NodePath","RID","Object","InputEvent","Dictionary","Array","RawArray","IntArray","FloatArray","StringArray",
+ "Vector2Array","Vector3Array","ColorArray"};
+
for(int i=0;i<Variant::VARIANT_MAX;i++) {
if (p_indices->get()==_type_names[i]) {
@@ -567,12 +824,12 @@ static bool _parse_completion_class(const String& p_base_path,const GDParser::Cl
}
}
-
+ // check the sub-classes of current class
for(int i=0;i<p_class->subclasses.size();i++) {
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;
}
@@ -586,7 +843,7 @@ static bool _parse_completion_class(const String& p_base_path,const GDParser::Cl
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(fu->body,p_line,r_options,p_indices))
+ if (_parse_completion_block(p_class,fu->body,p_line,r_options,p_indices))
return true;
//then function arguments
if (!p_indices) {
@@ -606,7 +863,7 @@ static bool _parse_completion_class(const String& p_base_path,const GDParser::Cl
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(fu->body,p_line,r_options,p_indices))
+ if (_parse_completion_block(p_class,fu->body,p_line,r_options,p_indices))
return true;
//then function arguments
if (!p_indices) {
@@ -757,24 +1014,30 @@ static bool _parse_completion_class(const String& p_base_path,const GDParser::Cl
Error GDScriptLanguage::complete_keyword(const String& p_code, int p_line, const String& p_base_path, const String& p_base, List<String>* r_options) {
+
+
GDParser p;
Error err = p.parse(p_code,p_base_path);
// don't care much about error I guess
const GDParser::Node* root = p.get_parse_tree();
ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,ERR_INVALID_DATA);
+ print_line("BASE: "+p_base);
const GDParser::ClassNode *cl = static_cast<const GDParser::ClassNode*>(root);
List<String> indices;
Vector<String> spl = p_base.split(".");
for(int i=0;i<spl.size()-1;i++) {
+ print_line("INDEX "+itos(i)+": "+spl[i]);
indices.push_back(spl[i]);
}
+ //parse completion inside of the class first
if (_parse_completion_class(p_base,cl,p_line,r_options,indices.front()))
return OK;
- //and the globals x_x?
+
+ //if parsing completion inside of the class fails (none found), try using globals for completion
for(Map<StringName,int>::Element *E=globals.front();E;E=E->next()) {
if (!indices.empty()) {
if (String(E->key())==indices.front()->get()) {
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index de2b5219a..904b6ba52 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -225,7 +225,14 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
String path = tokenizer->get_token_constant();
if (!path.is_abs_path() && base_path!="")
path=base_path+"/"+path;
- path = path.replace("///","//");
+ path = path.replace("///","//").simplify_path();
+ if (path==self_path) {
+
+ _set_error("Can't preload itself (use 'get_script()').");
+ return NULL;
+
+ }
+
Ref<Resource> res;
if (!validating) {
@@ -2616,8 +2623,9 @@ Error GDParser::_parse(const String& p_base_path) {
return OK;
}
-Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,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) {
+ self_path=p_self_path;
GDTokenizerBuffer *tb = memnew( GDTokenizerBuffer );
tb->set_code_buffer(p_bytecode);
tokenizer=tb;
@@ -2628,9 +2636,9 @@ 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) {
-
+Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_just_validate, const String &p_self_path) {
+ self_path=p_self_path;
GDTokenizerText *tt = memnew( GDTokenizerText );
tt->set_code(p_code);
diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h
index 5fac34396..3f82cafc6 100644
--- a/modules/gdscript/gd_parser.h
+++ b/modules/gdscript/gd_parser.h
@@ -373,6 +373,7 @@ private:
List<int> tab_level;
String base_path;
+ String self_path;
PropertyInfo current_export;
@@ -398,8 +399,8 @@ public:
String get_error() const;
int get_error_line() const;
int get_error_column() const;
- Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false);
- Error parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p_base_path="");
+ Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="");
+ Error parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p_base_path="",const String& p_self_path="");
const Node *get_parse_tree() const;
diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp
index 7085ae6a5..982584c75 100644
--- a/modules/gdscript/gd_script.cpp
+++ b/modules/gdscript/gd_script.cpp
@@ -1523,6 +1523,7 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
placeholders.erase(p_placeholder);
}
+/*
void GDScript::_update_placeholder(PlaceHolderScriptInstance *p_placeholder) {
@@ -1563,7 +1564,7 @@ void GDScript::_update_placeholder(PlaceHolderScriptInstance *p_placeholder) {
p_placeholder->update(plist,default_values);
-}
+}*/
#endif
ScriptInstance* GDScript::instance_create(Object *p_this) {
@@ -1582,7 +1583,8 @@ ScriptInstance* GDScript::instance_create(Object *p_this) {
}*/
PlaceHolderScriptInstance *si = memnew( PlaceHolderScriptInstance(GDScriptLanguage::get_singleton(),Ref<Script>(this),p_this) );
placeholders.insert(si);
- _update_placeholder(si);
+ //_update_placeholder(si);
+ _update_exports();
return si;
#else
return NULL;
@@ -1623,61 +1625,136 @@ String GDScript::get_source_code() const {
}
void GDScript::set_source_code(const String& p_code) {
+ if (source==p_code)
+ return;
source=p_code;
+#ifdef TOOLS_ENABLED
+ source_changed_cache=true;
+ //print_line("SC CHANGED "+get_path());
+#endif
+
}
+#ifdef TOOLS_ENABLED
+void GDScript::_update_exports_values(Map<StringName,Variant>& values, List<PropertyInfo> &propnames) {
+
+ if (base_cache.is_valid()) {
+ base_cache->_update_exports_values(values,propnames);
+ }
+
+ for(Map<StringName,Variant>::Element *E=member_default_values_cache.front();E;E=E->next()) {
+ values[E->key()]=E->get();
+ }
+
+ for (List<PropertyInfo>::Element *E=members_cache.front();E;E=E->next()) {
+ propnames.push_back(E->get());
+ }
+
+}
+#endif
-void GDScript::_update_exports(Set<PlaceHolderScriptInstance*> *p_instances) {
+bool GDScript::_update_exports() {
#ifdef TOOLS_ENABLED
- String basedir=path;
+ bool changed=false;
- if (basedir=="")
- basedir=get_path();
+ if (source_changed_cache) {
+ //print_line("updating source for "+get_path());
+ source_changed_cache=false;
+ changed=true;
- if (basedir!="")
- basedir=basedir.get_base_dir();
+ String basedir=path;
- GDParser parser;
- Error err = parser.parse(source,basedir,true);
- if (err)
- return; //do none
+ if (basedir=="")
+ basedir=get_path();
- const GDParser::Node* root = parser.get_parse_tree();
- ERR_FAIL_COND(root->type!=GDParser::Node::TYPE_CLASS);
+ if (basedir!="")
+ basedir=basedir.get_base_dir();
+ GDParser parser;
+ Error err = parser.parse(source,basedir,true,path);
+ if (err==OK) {
- const GDParser::ClassNode *c = static_cast<const GDParser::ClassNode*>(root);
+ const GDParser::Node* root = parser.get_parse_tree();
+ ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,false);
- if (c->extends_used && String(c->extends_file)!="") {
+ const GDParser::ClassNode *c = static_cast<const GDParser::ClassNode*>(root);
- Ref<GDScript> bf = ResourceLoader::load(c->extends_file);
- if (bf.is_valid()) {
+ if (base_cache.is_valid()) {
+ base_cache->inheriters_cache.erase(get_instance_ID());
+ base_cache=Ref<GDScript>();
+ }
- bf->_update_exports(p_instances);
- }
- }
+ if (c->extends_used && String(c->extends_file)!="") {
- List<PropertyInfo> plist;
+ String path = c->extends_file;
+ if (path.is_rel_path()) {
- Map<StringName,Variant> default_values;
+ String base = get_path();
+ if (base=="" || base.is_rel_path()) {
- for(int i=0;i<c->variables.size();i++) {
- if (c->variables[i]._export.type==Variant::NIL)
- continue;
+ ERR_PRINT(("Could not resolve relative path for parent class: "+path).utf8().get_data());
+ } else {
+ path=base.get_base_dir().plus_file(path);
+ }
+ }
+
+ Ref<GDScript> bf = ResourceLoader::load(path);
+
+ if (bf.is_valid()) {
+
+ //print_line("parent is: "+bf->get_path());
+ base_cache=bf;
+ bf->inheriters_cache.insert(get_instance_ID());
+
+ //bf->_update_exports(p_instances,true,false);
+
+ }
+ }
+
+ members_cache.clear();;
+ member_default_values_cache.clear();
+
+ for(int i=0;i<c->variables.size();i++) {
+ if (c->variables[i]._export.type==Variant::NIL)
+ continue;
+
+ members_cache.push_back(c->variables[i]._export);
+ //print_line("found "+c->variables[i]._export.name);
+ member_default_values_cache[c->variables[i].identifier]=c->variables[i].default_value;
+ }
+ }
+ } else {
+ //print_line("unchaged is "+get_path());
- plist.push_back(c->variables[i]._export);
- default_values[c->variables[i].identifier]=c->variables[i].default_value;
}
+ if (base_cache.is_valid()) {
+ if (base_cache->_update_exports()) {
+ changed = true;
+ }
+ }
- for (Set<PlaceHolderScriptInstance*>::Element *E=p_instances->front();E;E=E->next()) {
+ if (/*changed &&*/ placeholders.size()) { //hm :(
- E->get()->update(plist,default_values);
+ //print_line("updating placeholders for "+get_path());
+
+ //update placeholders if any
+ Map<StringName,Variant> values;
+ List<PropertyInfo> propnames;
+ _update_exports_values(values,propnames);
+
+ for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) {
+
+ E->get()->update(propnames,values);
+ }
}
+
+ return changed;
+
#endif
}
@@ -1685,8 +1762,20 @@ void GDScript::update_exports() {
#ifdef TOOLS_ENABLED
- _update_exports(&placeholders);
+ _update_exports();
+ Set<ObjectID> copy=inheriters_cache; //might get modified
+
+ //print_line("update exports for "+get_path()+" ic: "+itos(copy.size()));
+ for(Set<ObjectID>::Element *E=copy.front();E;E=E->next()) {
+ Object *id=ObjectDB::get_instance(E->get());
+ if (!id)
+ continue;
+ GDScript *s=id->cast_to<GDScript>();
+ if (!s)
+ continue;
+ s->update_exports();
+ }
#endif
}
@@ -1718,7 +1807,7 @@ Error GDScript::reload() {
valid=false;
GDParser parser;
- Error err = parser.parse(source,basedir);
+ Error err = parser.parse(source,basedir,false,path);
if (err) {
if (ScriptDebugger::get_singleton()) {
GDScriptLanguage::get_singleton()->debug_break_parse(get_path(),parser.get_error_line(),"Parser Error: "+parser.get_error());
@@ -1746,10 +1835,10 @@ Error GDScript::reload() {
}
#ifdef TOOLS_ENABLED
- for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) {
+ /*for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) {
_update_placeholder(E->get());
- }
+ }*/
#endif
return OK;
}
@@ -1892,7 +1981,7 @@ Error GDScript::load_byte_code(const String& p_path) {
valid=false;
GDParser parser;
- Error err = parser.parse_bytecode(bytecode,basedir);
+ Error err = parser.parse_bytecode(bytecode,basedir,get_path());
if (err) {
_err_print_error("GDScript::load_byte_code",path.empty()?"built-in":(const char*)path.utf8().get_data(),parser.get_error_line(),("Parse Error: "+parser.get_error()).utf8().get_data());
ERR_FAIL_V(ERR_PARSE_ERROR);
@@ -1945,6 +2034,10 @@ Error GDScript::load_source_code(const String& p_path) {
}
source=s;
+#ifdef TOOLS_ENABLED
+ source_changed_cache=true;
+#endif
+ //print_line("LSC :"+get_path());
path=p_path;
return OK;
@@ -1986,6 +2079,9 @@ GDScript::GDScript() {
_base=NULL;
_owner=NULL;
tool=false;
+#ifdef TOOLS_ENABLED
+ source_changed_cache=false;
+#endif
}
diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h
index 3b183a41b..f4e4dffaa 100644
--- a/modules/gdscript/gd_script.h
+++ b/modules/gdscript/gd_script.h
@@ -245,7 +245,16 @@ friend class GDScriptLanguage;
Map<StringName,Ref<GDScript> > subclasses;
#ifdef TOOLS_ENABLED
+
Map<StringName,Variant> member_default_values;
+
+ List<PropertyInfo> members_cache;
+ Map<StringName,Variant> member_default_values_cache;
+ Ref<GDScript> base_cache;
+ Set<ObjectID> inheriters_cache;
+ bool source_changed_cache;
+ void _update_exports_values(Map<StringName,Variant>& values, List<PropertyInfo> &propnames);
+
#endif
Map<StringName,PropertyInfo> member_info;
@@ -265,13 +274,13 @@ friend class GDScriptLanguage;
#ifdef TOOLS_ENABLED
Set<PlaceHolderScriptInstance*> placeholders;
- void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
+ //void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder);
#endif
- void _update_exports(Set<PlaceHolderScriptInstance *> *p_instances);
+ bool _update_exports();
protected:
bool _get(const StringName& p_name,Variant &r_ret) const;