summaryrefslogtreecommitdiff
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
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
-rw-r--r--core/method_bind.cpp4
-rw-r--r--core/object.cpp5
-rw-r--r--core/object.h1
-rw-r--r--core/script_language.h2
-rw-r--r--core/variant.cpp126
-rw-r--r--core/variant.h2
-rw-r--r--drivers/unix/os_unix.cpp6
-rw-r--r--makefile30
-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
-rw-r--r--platform/windows/os_windows.cpp3
-rw-r--r--scene/animation/animation_player.cpp13
-rw-r--r--scene/animation/animation_player.h3
-rw-r--r--scene/gui/control.cpp96
-rw-r--r--scene/gui/text_edit.cpp679
-rw-r--r--scene/gui/text_edit.h10
-rw-r--r--scene/main/node.cpp20
-rw-r--r--scene/main/node.h1
-rw-r--r--scene/resources/material.cpp16
-rw-r--r--scene/resources/material.h1
-rw-r--r--tools/editor/code_editor.cpp13
-rw-r--r--tools/editor/code_editor.h4
-rw-r--r--tools/editor/plugins/collision_polygon_editor_plugin.cpp2
-rw-r--r--tools/editor/plugins/script_editor_plugin.cpp48
-rw-r--r--tools/editor/plugins/script_editor_plugin.h2
30 files changed, 2089 insertions, 773 deletions
diff --git a/core/method_bind.cpp b/core/method_bind.cpp
index 739745f70f..6ee4882571 100644
--- a/core/method_bind.cpp
+++ b/core/method_bind.cpp
@@ -87,7 +87,9 @@ Vector<StringName> MethodBind::get_argument_names() const {
void MethodBind::set_default_arguments(const Vector<Variant>& p_defargs) {
- default_arguments=p_defargs; default_argument_count=default_arguments.size();
+ default_arguments=p_defargs;
+ default_argument_count=default_arguments.size();
+
}
#ifdef DEBUG_METHODS_ENABLED
diff --git a/core/object.cpp b/core/object.cpp
index 42d570042f..82144ab4be 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -1694,6 +1694,11 @@ void ObjectDB::debug_objects(DebugFunc p_func) {
}
+void Object::get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const {
+
+
+}
+
int ObjectDB::get_object_count() {
GLOBAL_LOCK_FUNCTION;
diff --git a/core/object.h b/core/object.h
index 7ce07869a6..97ca50cb1a 100644
--- a/core/object.h
+++ b/core/object.h
@@ -583,6 +583,7 @@ public:
virtual void get_translatable_strings(List<String> *p_strings) const;
+ virtual void get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const;
StringName XL_MESSAGE(const StringName& p_message) const; //translate message (internationalization)
StringName tr(const StringName& p_message) const; //translate message (alternative)
diff --git a/core/script_language.h b/core/script_language.h
index d62e9849a1..802eff190a 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -144,7 +144,7 @@ public:
virtual bool has_named_classes() const=0;
virtual int find_function(const String& p_function,const String& p_code) const=0;
virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const=0;
- virtual Error complete_keyword(const String& p_code, int p_line, const String& p_base_path, const String& p_keyword, List<String>* r_options) { return ERR_UNAVAILABLE; }
+ virtual Error complete_code(const String& p_code, const String& p_base_path, Object*p_owner,List<String>* r_options,String& r_call_hint) { return ERR_UNAVAILABLE; }
virtual void auto_indent_code(String& p_code,int p_from_line,int p_to_line) const=0;
/* DEBUGGER FUNCTIONS */
diff --git a/core/variant.cpp b/core/variant.cpp
index fdb14c0c0f..2f0eca9e91 100644
--- a/core/variant.cpp
+++ b/core/variant.cpp
@@ -2631,3 +2631,129 @@ Variant Variant::call(const StringName& p_method,VARIANT_ARG_DECLARE) {
return ret;
}
+
+String Variant::get_construct_string() const {
+
+ switch( type ) {
+
+ case NIL: return "null";
+ case BOOL: return _data._bool ? "true" : "false";
+ case INT: return String::num(_data._int);
+ case REAL: return String::num(_data._real);
+ case STRING: return "\""+*reinterpret_cast<const String*>(_data._mem)+"\"";
+ case VECTOR2: return "Vector2("+operator Vector2()+")";
+ case RECT2: return "Rect2("+operator Rect2()+")";
+ case MATRIX32: return "Matrix32("+operator Matrix32()+")";
+ case VECTOR3: return "Vector3("+operator Vector3()+")";
+ case PLANE: return "Plane("+operator Plane()+")";
+ //case QUAT:
+ case _AABB: return "AABB("+operator AABB()+")";
+ case QUAT: return "Quat("+operator Quat()+")";
+ case MATRIX3: return "Matrix3("+operator Matrix3()+")";
+ case TRANSFORM: return "Transform("+operator Transform()+")";
+ case NODE_PATH: return "@\""+operator NodePath()+"\"";
+ case INPUT_EVENT: return "InputEvent()";
+ case COLOR: return "Color("+String::num( operator Color().r)+","+String::num( operator Color().g)+","+String::num( operator Color().b)+","+String::num( operator Color().a)+")" ;
+ case DICTIONARY: {
+
+ const Dictionary &d =*reinterpret_cast<const Dictionary*>(_data._mem);
+ //const String *K=NULL;
+ String str="{";
+ List<Variant> keys;
+ d.get_key_list(&keys);
+
+ Vector<_VariantStrPair> pairs;
+
+ for(List<Variant>::Element *E=keys.front();E;E=E->next()) {
+
+ _VariantStrPair sp;
+ sp.key=E->get().get_construct_string();
+ sp.value=d[E->get()].get_construct_string();
+ pairs.push_back(sp);
+ }
+
+ pairs.sort();
+
+ for(int i=0;i<pairs.size();i++) {
+ if (i>0)
+ str+=", ";
+ str+="("+pairs[i].key+":"+pairs[i].value+")";
+ }
+ str+="}";
+
+ return str;
+ } break;
+ case VECTOR3_ARRAY: {
+
+ DVector<Vector3> vec = operator DVector<Vector3>();
+ String str="[";
+ for(int i=0;i<vec.size();i++) {
+
+ if (i>0)
+ str+=", ";
+ str+=Variant( vec[i] ).get_construct_string();
+ }
+ return str+"]";
+ } break;
+ case STRING_ARRAY: {
+
+ DVector<String> vec = operator DVector<String>();
+ String str="[";
+ for(int i=0;i<vec.size();i++) {
+
+ if (i>0)
+ str+=", ";
+ str=str+=Variant( vec[i] ).get_construct_string();
+ }
+ return str+"]";
+ } break;
+ case INT_ARRAY: {
+
+ DVector<int> vec = operator DVector<int>();
+ String str="[";
+ for(int i=0;i<vec.size();i++) {
+
+ if (i>0)
+ str+=", ";
+ str=str+itos(vec[i]);
+ }
+ return str+"]";
+ } break;
+ case REAL_ARRAY: {
+
+ DVector<real_t> vec = operator DVector<real_t>();
+ String str="[";
+ for(int i=0;i<vec.size();i++) {
+
+ if (i>0)
+ str+=", ";
+ str=str+rtos(vec[i]);
+ }
+ return str+"]";
+ } break;
+ case ARRAY: {
+
+ Array arr = operator Array();
+ String str="[";
+ for (int i=0; i<arr.size(); i++) {
+ if (i)
+ str+=", ";
+ str += arr[i].get_construct_string();
+ };
+ return str+"]";
+
+ } break;
+ case OBJECT: {
+
+ if (_get_obj().obj)
+ return _get_obj().obj->get_type()+".new()";
+ else
+ return "null";
+
+ } break;
+ default: {
+ return "["+get_type_name(type)+"]";
+ }
+ }
+
+}
diff --git a/core/variant.h b/core/variant.h
index 9109f4ad08..b85c1ea640 100644
--- a/core/variant.h
+++ b/core/variant.h
@@ -415,6 +415,8 @@ public:
static bool has_numeric_constant(Variant::Type p_type, const StringName& p_value);
static int get_numeric_constant_value(Variant::Type p_type, const StringName& p_value);
+ String get_construct_string() const;
+
void operator=(const Variant& p_variant); // only this is enough for all the other types
Variant(const Variant& p_variant);
_FORCE_INLINE_ Variant() { type=NIL; }
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index e6458068ea..2de975e5d1 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -332,6 +332,12 @@ Error OS_Unix::execute(const String& p_path, const List<String>& p_arguments,boo
Error OS_Unix::kill(const ProcessID& p_pid) {
int ret = ::kill(p_pid,SIGKILL);
+ if (!ret) {
+ //avoid zombie process
+ int st;
+ ::waitpid(p_pid,&st,0);
+
+ }
return ret?ERR_INVALID_PARAMETER:OK;
}
diff --git a/makefile b/makefile
deleted file mode 100644
index d24bd0cd32..0000000000
--- a/makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-#*************************************************************************/
-#* This file is part of: */
-#* GODOT ENGINE */
-#* http://www.godotengine.org */
-#*************************************************************************/
-# Simple makefile to give support for external C/C++ IDEs */
-#*************************************************************************/
-
-# Default build
-all: debug
-
-# Release Build
-release:
- scons target="release" bin/godot
-
-# Profile Build
-profile:
- scons target="profile" bin/godot
-
-# Debug Build
-debug:
- # Debug information (code size gets severely affected):
- # g: Default (same as g2)
- # g0: no debug info
- # g1: minimal info
- # g3: maximal info
- scons target="debug" CCFLAGS="-g" bin/godot
-
-clean:
- scons -c bin/godot
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
};
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 1ab15dcda3..a10152a025 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -1396,8 +1396,9 @@ void OS_Windows::set_window_title(const String& p_title) {
void OS_Windows::set_video_mode(const VideoMode& p_video_mode,int p_screen) {
-
+
}
+
OS::VideoMode OS_Windows::get_video_mode(int p_screen) const {
return video_mode;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 011850138b..f9d36138a2 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -1178,6 +1178,19 @@ NodePath AnimationPlayer::get_root() const {
return root;
}
+void AnimationPlayer::get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const {
+
+ String pf = p_function;
+ if (p_function=="play" || p_function=="remove_animation" || p_function=="has_animation" || p_function=="queue") {
+ List<StringName> al;
+ get_animation_list(&al);
+ for (List<StringName>::Element *E=al.front();E;E=E->next()) {
+
+ r_options->push_back("\""+String(E->get())+"\"");
+ }
+ }
+ Node::get_argument_options(p_function,p_idx,r_options);
+}
void AnimationPlayer::_bind_methods() {
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index 038c43d569..8ac5d96bf3 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -289,6 +289,9 @@ public:
NodePath get_root() const;
void clear_caches(); ///< must be called by hand if an animation was modified after added
+
+ void get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const;
+
AnimationPlayer();
~AnimationPlayer();
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index aea0aacf42..5a0706f01e 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -1325,9 +1325,12 @@ Size2 Control::get_minimum_size() const {
Ref<Texture> Control::get_icon(const StringName& p_name,const StringName& p_type) const {
- const Ref<Texture>* tex = data.icon_override.getptr(p_name);
- if (tex)
- return *tex;
+ if (p_type==StringName()) {
+
+ const Ref<Texture>* tex = data.icon_override.getptr(p_name);
+ if (tex)
+ return *tex;
+ }
StringName type = p_type?p_type:get_type_name();
@@ -1353,12 +1356,11 @@ Ref<Texture> Control::get_icon(const StringName& p_name,const StringName& p_type
Ref<StyleBox> Control::get_stylebox(const StringName& p_name,const StringName& p_type) const {
-
- const Ref<StyleBox>* style = data.style_override.getptr(p_name);
-
-
- if (style)
- return *style;
+ if (p_type==StringName()) {
+ const Ref<StyleBox>* style = data.style_override.getptr(p_name);
+ if (style)
+ return *style;
+ }
StringName type = p_type?p_type:get_type_name();
@@ -1381,10 +1383,12 @@ Ref<StyleBox> Control::get_stylebox(const StringName& p_name,const StringName& p
}
Ref<Font> Control::get_font(const StringName& p_name,const StringName& p_type) const {
-
- const Ref<Font>* font = data.font_override.getptr(p_name);
- if (font)
- return *font;
+
+ if (p_type==StringName()) {
+ const Ref<Font>* font = data.font_override.getptr(p_name);
+ if (font)
+ return *font;
+ }
StringName type = p_type?p_type:get_type_name();
@@ -1410,10 +1414,12 @@ Ref<Font> Control::get_font(const StringName& p_name,const StringName& p_type) c
}
Color Control::get_color(const StringName& p_name,const StringName& p_type) const {
-
- const Color* color = data.color_override.getptr(p_name);
- if (color)
- return *color;
+
+ if (p_type==StringName()) {
+ const Color* color = data.color_override.getptr(p_name);
+ if (color)
+ return *color;
+ }
StringName type = p_type?p_type:get_type_name();
// try with custom themes
@@ -1437,10 +1443,12 @@ Color Control::get_color(const StringName& p_name,const StringName& p_type) cons
}
int Control::get_constant(const StringName& p_name,const StringName& p_type) const {
-
- const int* constant = data.constant_override.getptr(p_name);
- if (constant)
- return *constant;
+
+ if (p_type==StringName()) {
+ const int* constant = data.constant_override.getptr(p_name);
+ if (constant)
+ return *constant;
+ }
StringName type = p_type?p_type:get_type_name();
// try with custom themes
@@ -1467,9 +1475,11 @@ int Control::get_constant(const StringName& p_name,const StringName& p_type) con
bool Control::has_icon(const StringName& p_name,const StringName& p_type) const {
- const Ref<Texture>* tex = data.icon_override.getptr(p_name);
- if (tex)
- return true;
+ if (p_type==StringName()) {
+ const Ref<Texture>* tex = data.icon_override.getptr(p_name);
+ if (tex)
+ return true;
+ }
StringName type = p_type?p_type:get_type_name();
@@ -1494,11 +1504,12 @@ bool Control::has_icon(const StringName& p_name,const StringName& p_type) const
}
bool Control::has_stylebox(const StringName& p_name,const StringName& p_type) const {
-
- const Ref<StyleBox>* style = data.style_override.getptr(p_name);
-
- if (style)
- return true;
+ if (p_type==StringName()) {
+ const Ref<StyleBox>* style = data.style_override.getptr(p_name);
+
+ if (style)
+ return true;
+ }
StringName type = p_type?p_type:get_type_name();
@@ -1523,9 +1534,11 @@ bool Control::has_stylebox(const StringName& p_name,const StringName& p_type) co
}
bool Control::has_font(const StringName& p_name,const StringName& p_type) const {
- const Ref<Font>* font = data.font_override.getptr(p_name);
- if (font)
- return true;
+ if (p_type==StringName()) {
+ const Ref<Font>* font = data.font_override.getptr(p_name);
+ if (font)
+ return true;
+ }
StringName type = p_type?p_type:get_type_name();
@@ -1551,9 +1564,11 @@ bool Control::has_font(const StringName& p_name,const StringName& p_type) const
}
bool Control::has_color(const StringName& p_name,const StringName& p_type) const {
- const Color* color = data.color_override.getptr(p_name);
- if (color)
- return true;
+ if (p_type==StringName()) {
+ const Color* color = data.color_override.getptr(p_name);
+ if (color)
+ return true;
+ }
StringName type = p_type?p_type:get_type_name();
@@ -1578,10 +1593,13 @@ bool Control::has_color(const StringName& p_name,const StringName& p_type) const
}
bool Control::has_constant(const StringName& p_name,const StringName& p_type) const {
-
- const int* constant = data.constant_override.getptr(p_name);
- if (constant)
- return true;
+
+ if (p_type==StringName()) {
+
+ const int* constant = data.constant_override.getptr(p_name);
+ if (constant)
+ return true;
+ }
StringName type = p_type?p_type:get_type_name();
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index ba68948e6b..715c3cbf7e 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -359,385 +359,445 @@ void TextEdit::_update_scrollbars() {
void TextEdit::_notification(int p_what) {
- switch(p_what) {
- case NOTIFICATION_ENTER_TREE: {
+ switch(p_what) {
+ case NOTIFICATION_ENTER_TREE: {
- _update_caches();
- if (cursor_changed_dirty)
- MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit");
- if (text_changed_dirty)
- MessageQueue::get_singleton()->push_call(this,"_text_changed_emit");
+ _update_caches();
+ if (cursor_changed_dirty)
+ MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit");
+ if (text_changed_dirty)
+ MessageQueue::get_singleton()->push_call(this,"_text_changed_emit");
- } break;
- case NOTIFICATION_RESIZED: {
+ } break;
+ case NOTIFICATION_RESIZED: {
- cache.size=get_size();
- adjust_viewport_to_cursor();
+ cache.size=get_size();
+ adjust_viewport_to_cursor();
- } break;
- case NOTIFICATION_THEME_CHANGED: {
+ } break;
+ case NOTIFICATION_THEME_CHANGED: {
- _update_caches();
- };
- case NOTIFICATION_DRAW: {
+ _update_caches();
+ };
+ case NOTIFICATION_DRAW: {
- int line_number_char_count=0;
+ int line_number_char_count=0;
- {
- int lc=text.size()+1;
- cache.line_number_w=0;
- while(lc) {
- cache.line_number_w+=1;
- lc/=10;
- };
+ {
+ int lc=text.size()+1;
+ cache.line_number_w=0;
+ while(lc) {
+ cache.line_number_w+=1;
+ lc/=10;
+ };
- if (line_numbers) {
+ if (line_numbers) {
- line_number_char_count=cache.line_number_w;
- cache.line_number_w=(cache.line_number_w+1)*cache.font->get_char_size('0').width;
- } else {
- cache.line_number_w=0;
- }
+ line_number_char_count=cache.line_number_w;
+ cache.line_number_w=(cache.line_number_w+1)*cache.font->get_char_size('0').width;
+ } else {
+ cache.line_number_w=0;
+ }
- }
- _update_scrollbars();
+ }
+ _update_scrollbars();
- RID ci = get_canvas_item();
- int xmargin_beg=cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w;
- int xmargin_end=cache.size.width-cache.style_normal->get_margin(MARGIN_RIGHT);
- //let's do it easy for now:
- cache.style_normal->draw(ci,Rect2(Point2(),cache.size));
- if (has_focus())
- cache.style_focus->draw(ci,Rect2(Point2(),cache.size));
+ RID ci = get_canvas_item();
+ int xmargin_beg=cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w;
+ int xmargin_end=cache.size.width-cache.style_normal->get_margin(MARGIN_RIGHT);
+ //let's do it easy for now:
+ cache.style_normal->draw(ci,Rect2(Point2(),cache.size));
+ if (has_focus())
+ cache.style_focus->draw(ci,Rect2(Point2(),cache.size));
- int ascent=cache.font->get_ascent();
+ int ascent=cache.font->get_ascent();
- int visible_rows = get_visible_rows();
+ int visible_rows = get_visible_rows();
- int tab_w = cache.font->get_char_size(' ').width*tab_size;
+ int tab_w = cache.font->get_char_size(' ').width*tab_size;
- Color color = cache.font_color;
- int in_region=-1;
+ Color color = cache.font_color;
+ int in_region=-1;
- if (syntax_coloring) {
+ if (syntax_coloring) {
- if (custom_bg_color.a>0.01) {
+ if (custom_bg_color.a>0.01) {
- Point2i ofs = Point2i(cache.style_normal->get_offset())/2.0;
- VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(ofs, get_size()-cache.style_normal->get_minimum_size()+ofs),custom_bg_color);
- }
- //compute actual region to start (may be inside say, a comment).
- //slow in very large documments :( but ok for source!
+ Point2i ofs = Point2i(cache.style_normal->get_offset())/2.0;
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(ofs, get_size()-cache.style_normal->get_minimum_size()+ofs),custom_bg_color);
+ }
+ //compute actual region to start (may be inside say, a comment).
+ //slow in very large documments :( but ok for source!
- for(int i=0;i<cursor.line_ofs;i++) {
+ for(int i=0;i<cursor.line_ofs;i++) {
- const Map<int,Text::ColorRegionInfo>& cri_map=text.get_color_region_info(i);
+ const Map<int,Text::ColorRegionInfo>& cri_map=text.get_color_region_info(i);
- if (in_region>=0 && color_regions[in_region].line_only) {
- in_region=-1; //reset regions that end at end of line
- }
+ if (in_region>=0 && color_regions[in_region].line_only) {
+ in_region=-1; //reset regions that end at end of line
+ }
- for( const Map<int,Text::ColorRegionInfo>::Element* E= cri_map.front();E;E=E->next() ) {
+ for( const Map<int,Text::ColorRegionInfo>::Element* E= cri_map.front();E;E=E->next() ) {
- const Text::ColorRegionInfo &cri=E->get();
+ const Text::ColorRegionInfo &cri=E->get();
- if (in_region==-1) {
+ if (in_region==-1) {
- if (!cri.end) {
+ if (!cri.end) {
- in_region=cri.region;
- }
- } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
+ in_region=cri.region;
+ }
+ } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
- if (cri.end || color_regions[cri.region].eq) {
+ if (cri.end || color_regions[cri.region].eq) {
- in_region=-1;
- }
- }
- }
- }
- }
+ in_region=-1;
+ }
+ }
+ }
+ }
+ }
- int deregion=0; //force it to clear inrgion
- Point2 cursor_pos;
+ int deregion=0; //force it to clear inrgion
+ Point2 cursor_pos;
- for (int i=0;i<visible_rows;i++) {
+ for (int i=0;i<visible_rows;i++) {
- int line=i+cursor.line_ofs;
+ int line=i+cursor.line_ofs;
- if (line<0 || line>=(int)text.size())
- continue;
+ if (line<0 || line>=(int)text.size())
+ continue;
- const String &str=text[line];
+ const String &str=text[line];
- int char_margin=xmargin_beg-cursor.x_ofs;
- int char_ofs=0;
- int ofs_y=i*get_row_height()+cache.line_spacing/2;
- bool prev_is_char=false;
- bool in_keyword=false;
- Color keyword_color;
+ int char_margin=xmargin_beg-cursor.x_ofs;
+ int char_ofs=0;
+ int ofs_y=i*get_row_height()+cache.line_spacing/2;
+ bool prev_is_char=false;
+ bool in_keyword=false;
+ Color keyword_color;
- if (cache.line_number_w) {
- Color fcol = cache.font_color;
- fcol.a*=0.4;
- String fc = String::num(line+1);
- while (fc.length() < line_number_char_count) {
- fc="0"+fc;
- }
+ if (cache.line_number_w) {
+ Color fcol = cache.font_color;
+ fcol.a*=0.4;
+ String fc = String::num(line+1);
+ while (fc.length() < line_number_char_count) {
+ fc="0"+fc;
+ }
- cache.font->draw(ci,Point2(cache.style_normal->get_margin(MARGIN_LEFT),ofs_y+cache.font->get_ascent()),fc,fcol);
- }
+ cache.font->draw(ci,Point2(cache.style_normal->get_margin(MARGIN_LEFT),ofs_y+cache.font->get_ascent()),fc,fcol);
+ }
- const Map<int,Text::ColorRegionInfo>& cri_map=text.get_color_region_info(line);
+ const Map<int,Text::ColorRegionInfo>& cri_map=text.get_color_region_info(line);
- if (text.is_marked(line)) {
+ if (text.is_marked(line)) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.mark_color);
- }
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.mark_color);
+ }
- if (text.is_breakpoint(line)) {
+ if (text.is_breakpoint(line)) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.breakpoint_color);
- }
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.breakpoint_color);
+ }
- if (line==cursor.line) {
+ if (line==cursor.line) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.current_line_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.current_line_color);
- }
- for (int j=0;j<str.length();j++) {
+ }
+ for (int j=0;j<str.length();j++) {
- //look for keyword
+ //look for keyword
- if (deregion>0) {
- deregion--;
- if (deregion==0)
- in_region=-1;
- }
- if (syntax_coloring && deregion==0) {
+ if (deregion>0) {
+ deregion--;
+ if (deregion==0)
+ in_region=-1;
+ }
+ if (syntax_coloring && deregion==0) {
- color = cache.font_color; //reset
- //find keyword
- bool is_char = _is_text_char(str[j]);
- bool is_symbol=_is_symbol(str[j]);
+ color = cache.font_color; //reset
+ //find keyword
+ bool is_char = _is_text_char(str[j]);
+ bool is_symbol=_is_symbol(str[j]);
- if (j==0 && in_region>=0 && color_regions[in_region].line_only) {
- in_region=-1; //reset regions that end at end of line
- }
+ if (j==0 && in_region>=0 && color_regions[in_region].line_only) {
+ in_region=-1; //reset regions that end at end of line
+ }
- if (is_symbol && cri_map.has(j)) {
+ if (is_symbol && cri_map.has(j)) {
- const Text::ColorRegionInfo &cri=cri_map[j];
+ const Text::ColorRegionInfo &cri=cri_map[j];
- if (in_region==-1) {
+ if (in_region==-1) {
- if (!cri.end) {
+ if (!cri.end) {
- in_region=cri.region;
- }
- } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
+ in_region=cri.region;
+ }
+ } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
- if (cri.end || color_regions[cri.region].eq) {
+ if (cri.end || color_regions[cri.region].eq) {
- deregion=color_regions[cri.region].eq?color_regions[cri.region].begin_key.length():color_regions[cri.region].end_key.length();
- }
- }
- }
+ deregion=color_regions[cri.region].eq?color_regions[cri.region].begin_key.length():color_regions[cri.region].end_key.length();
+ }
+ }
+ }
- if (!is_char)
- in_keyword=false;
+ if (!is_char)
+ in_keyword=false;
- if (in_region==-1 && !in_keyword && is_char && !prev_is_char) {
+ if (in_region==-1 && !in_keyword && is_char && !prev_is_char) {
- int to=j;
- while(_is_text_char(str[to]) && to<str.length())
- to++;
+ int to=j;
+ while(_is_text_char(str[to]) && to<str.length())
+ to++;
- uint32_t hash = String::hash(&str[j],to-j);
- StrRange range(&str[j],to-j);
+ uint32_t hash = String::hash(&str[j],to-j);
+ StrRange range(&str[j],to-j);
- const Color *col=keywords.custom_getptr(range,hash);
+ const Color *col=keywords.custom_getptr(range,hash);
- if (col) {
+ if (col) {
- in_keyword=true;
- keyword_color=*col;
- }
- }
+ in_keyword=true;
+ keyword_color=*col;
+ }
+ }
- if (in_region>=0)
- color=color_regions[in_region].color;
- else if (in_keyword)
- color=keyword_color;
- else if (is_symbol)
- color=symbol_color;
+ if (in_region>=0)
+ color=color_regions[in_region].color;
+ else if (in_keyword)
+ color=keyword_color;
+ else if (is_symbol)
+ color=symbol_color;
- prev_is_char=is_char;
+ prev_is_char=is_char;
- }
- int char_w;
+ }
+ int char_w;
- //handle tabulator
+ //handle tabulator
- if (str[j]=='\t') {
- int left = char_ofs%tab_w;
- if (left==0)
- char_w=tab_w;
- else
- char_w=tab_w-char_ofs%tab_w; // is right...
+ if (str[j]=='\t') {
+ int left = char_ofs%tab_w;
+ if (left==0)
+ char_w=tab_w;
+ else
+ char_w=tab_w-char_ofs%tab_w; // is right...
- } else {
- char_w=cache.font->get_char_size(str[j],str[j+1]).width;
- }
+ } else {
+ char_w=cache.font->get_char_size(str[j],str[j+1]).width;
+ }
- if ( (char_ofs+char_margin)<xmargin_beg) {
- char_ofs+=char_w;
- continue;
- }
+ if ( (char_ofs+char_margin)<xmargin_beg) {
+ char_ofs+=char_w;
+ continue;
+ }
- if ( (char_ofs+char_margin+char_w)>=xmargin_end) {
- if (syntax_coloring)
- continue;
- else
- break;
- }
+ if ( (char_ofs+char_margin+char_w)>=xmargin_end) {
+ if (syntax_coloring)
+ continue;
+ else
+ break;
+ }
- bool in_selection = (selection.active && line>=selection.from_line && line<=selection.to_line && (line>selection.from_line || j>=selection.from_column) && (line<selection.to_line || j<selection.to_column));
+ bool in_selection = (selection.active && line>=selection.from_line && line<=selection.to_line && (line>selection.from_line || j>=selection.from_column) && (line<selection.to_line || j<selection.to_column));
- if (in_selection) {
- //inside selection!
- VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y ), Size2i(char_w,get_row_height())),cache.selection_color);
- }
+ if (in_selection) {
+ //inside selection!
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y ), Size2i(char_w,get_row_height())),cache.selection_color);
+ }
- if (str[j]>=32)
- cache.font->draw_char(ci,Point2i( char_ofs+char_margin, ofs_y+ascent),str[j],str[j+1],in_selection?cache.font_selected_color:color);
- else if (draw_tabs && str[j]=='\t') {
- int yofs= (get_row_height() - cache.tab_icon->get_height())/2;
- cache.tab_icon->draw(ci, Point2(char_ofs+char_margin,ofs_y+yofs),in_selection?cache.font_selected_color:color);
- }
+ if (str[j]>=32)
+ cache.font->draw_char(ci,Point2i( char_ofs+char_margin, ofs_y+ascent),str[j],str[j+1],in_selection?cache.font_selected_color:color);
+ else if (draw_tabs && str[j]=='\t') {
+ int yofs= (get_row_height() - cache.tab_icon->get_height())/2;
+ cache.tab_icon->draw(ci, Point2(char_ofs+char_margin,ofs_y+yofs),in_selection?cache.font_selected_color:color);
+ }
- if (cursor.column==j && cursor.line==line) {
+ if (cursor.column==j && cursor.line==line) {
- cursor_pos = Point2i( char_ofs+char_margin, ofs_y );
- VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color);
+ cursor_pos = Point2i( char_ofs+char_margin, ofs_y );
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color);
- }
- char_ofs+=char_w;
+ }
+ char_ofs+=char_w;
- }
+ }
- if (cursor.column==str.length() && cursor.line==line) {
+ if (cursor.column==str.length() && cursor.line==line) {
- cursor_pos=Point2i( char_ofs+char_margin, ofs_y );
- VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color);
+ cursor_pos=Point2i( char_ofs+char_margin, ofs_y );
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color);
- }
- }
+ }
+ }
- if (completion_active) {
- // code completion box
- Ref<StyleBox> csb = get_stylebox("completion");
- Ref<StyleBox> csel = get_stylebox("completion_selected");
- int maxlines = get_constant("completion_lines");
- int cmax_width = get_constant("completion_max_width")*cache.font->get_char_size('x').x;
- Color existing = get_color("completion_existing");
- int scrollw = get_constant("completion_scroll_width");
- Color scrollc = get_color("completion_scroll_color");
+ if (completion_active) {
+ // code completion box
+ Ref<StyleBox> csb = get_stylebox("completion");
+ Ref<StyleBox> csel = get_stylebox("completion_selected");
+ int maxlines = get_constant("completion_lines");
+ int cmax_width = get_constant("completion_max_width")*cache.font->get_char_size('x').x;
+ Color existing = get_color("completion_existing");
+ int scrollw = get_constant("completion_scroll_width");
+ Color scrollc = get_color("completion_scroll_color");
- int lines = MIN(completion_options.size(),maxlines);
- int w=0;
- int h=lines*get_row_height();
- int nofs = cache.font->get_string_size(completion_base).width;
+ int lines = MIN(completion_options.size(),maxlines);
+ int w=0;
+ int h=lines*get_row_height();
+ int nofs = cache.font->get_string_size(completion_base).width;
- if (completion_options.size() < 50) {
- for(int i=0;i<completion_options.size();i++) {
- int w2=MIN(cache.font->get_string_size(completion_options[i]).x,cmax_width);
- if (w2>w)
- w=w2;
- }
- } else {
- w=cmax_width;
- }
- int th = h + csb->get_minimum_size().y;
- if (cursor_pos.y+get_row_height()+th > get_size().height) {
- completion_rect.pos.y=cursor_pos.y-th;
- } else {
- completion_rect.pos.y=cursor_pos.y+get_row_height()+csb->get_offset().y;
- }
+ if (completion_options.size() < 50) {
+ for(int i=0;i<completion_options.size();i++) {
+ int w2=MIN(cache.font->get_string_size(completion_options[i]).x,cmax_width);
+ if (w2>w)
+ w=w2;
+ }
+ } else {
+ w=cmax_width;
+ }
- if (cursor_pos.x-nofs+w+scrollw > get_size().width) {
- completion_rect.pos.x=get_size().width-w-scrollw;
- } else {
- completion_rect.pos.x=cursor_pos.x-nofs;
- }
+ int th = h + csb->get_minimum_size().y;
+ if (cursor_pos.y+get_row_height()+th > get_size().height) {
+ completion_rect.pos.y=cursor_pos.y-th;
+ } else {
+ completion_rect.pos.y=cursor_pos.y+get_row_height()+csb->get_offset().y;
- completion_rect.size.width=w;
- completion_rect.size.height=h;
- if (completion_options.size()<=maxlines)
- scrollw=0;
+ }
- draw_style_box(csb,Rect2(completion_rect.pos-csb->get_offset(),completion_rect.size+csb->get_minimum_size()+Size2(scrollw,0)));
+ if (cursor_pos.x-nofs+w+scrollw > get_size().width) {
+ completion_rect.pos.x=get_size().width-w-scrollw;
+ } else {
+ completion_rect.pos.x=cursor_pos.x-nofs;
+ }
+ completion_rect.size.width=w;
+ completion_rect.size.height=h;
+ if (completion_options.size()<=maxlines)
+ scrollw=0;
- int line_from = CLAMP(completion_index - lines/2, 0, completion_options.size() - lines);
- draw_style_box(csel,Rect2(Point2(completion_rect.pos.x,completion_rect.pos.y+(completion_index-line_from)*get_row_height()),Size2(completion_rect.size.width,get_row_height())));
+ draw_style_box(csb,Rect2(completion_rect.pos-csb->get_offset(),completion_rect.size+csb->get_minimum_size()+Size2(scrollw,0)));
- draw_rect(Rect2(completion_rect.pos,Size2(nofs,completion_rect.size.height)),existing);
- for(int i=0;i<lines;i++) {
+ int line_from = CLAMP(completion_index - lines/2, 0, completion_options.size() - lines);
+ draw_style_box(csel,Rect2(Point2(completion_rect.pos.x,completion_rect.pos.y+(completion_index-line_from)*get_row_height()),Size2(completion_rect.size.width,get_row_height())));
- int l = line_from + i;
- ERR_CONTINUE( l < 0 || l>= completion_options.size());
- draw_string(cache.font,Point2(completion_rect.pos.x,completion_rect.pos.y+i*get_row_height()+cache.font->get_ascent()),completion_options[l],cache.font_color,completion_rect.size.width);
- }
+ draw_rect(Rect2(completion_rect.pos,Size2(nofs,completion_rect.size.height)),existing);
- if (scrollw) {
- //draw a small scroll rectangle to show a position in the options
- float r = maxlines / (float)completion_options.size();
- float o = line_from / (float)completion_options.size();
- draw_rect(Rect2(completion_rect.pos.x+completion_rect.size.width,completion_rect.pos.y+o*completion_rect.size.y,scrollw,completion_rect.size.y*r),scrollc);
- }
+ for(int i=0;i<lines;i++) {
- completion_line_ofs=line_from;
+ int l = line_from + i;
+ ERR_CONTINUE( l < 0 || l>= completion_options.size());
+ draw_string(cache.font,Point2(completion_rect.pos.x,completion_rect.pos.y+i*get_row_height()+cache.font->get_ascent()),completion_options[l],cache.font_color,completion_rect.size.width);
+ }
- }
+ if (scrollw) {
+ //draw a small scroll rectangle to show a position in the options
+ float r = maxlines / (float)completion_options.size();
+ float o = line_from / (float)completion_options.size();
+ draw_rect(Rect2(completion_rect.pos.x+completion_rect.size.width,completion_rect.pos.y+o*completion_rect.size.y,scrollw,completion_rect.size.y*r),scrollc);
+ }
+ completion_line_ofs=line_from;
+ }
- } break;
- case NOTIFICATION_FOCUS_ENTER: {
+ if (completion_hint!="") {
- if (OS::get_singleton()->has_virtual_keyboard())
- OS::get_singleton()->show_virtual_keyboard(get_text(),get_global_rect());
+ Ref<StyleBox> sb = get_stylebox("panel","TooltipPanel");
+ Ref<Font> font = cache.font;
+ Color font_color = get_color("font_color","TooltipLabel");
- } break;
- case NOTIFICATION_FOCUS_EXIT: {
- if (OS::get_singleton()->has_virtual_keyboard())
- OS::get_singleton()->hide_virtual_keyboard();
+ int max_w=0;
+ int sc = completion_hint.get_slice_count("\n");
+ int offset=0;
+ int spacing=0;
+ for(int i=0;i<sc;i++) {
- } break;
+ String l = completion_hint.get_slice("\n",i);
+ int len = font->get_string_size(l).x;
+ max_w = MAX(len,max_w);
+ if (i==0) {
+ offset = font->get_string_size(l.substr(0,l.find(String::chr(0xFFFF)))).x;
+ } else {
+ spacing+=cache.line_spacing;
+ }
- }
+
+ }
+
+
+
+ Size2 size = Size2(max_w,sc*font->get_height()+spacing);
+ Size2 minsize = size+sb->get_minimum_size();
+
+
+ if (completion_hint_offset==-0xFFFF) {
+ completion_hint_offset=cursor_pos.x-offset;
+ }
+
+
+ Point2 hint_ofs = Vector2(completion_hint_offset,cursor_pos.y-minsize.y);
+ draw_style_box(sb,Rect2(hint_ofs,minsize));
+
+ spacing=0;
+ for(int i=0;i<sc;i++) {
+ int begin=0;
+ int end=0;
+ String l = completion_hint.get_slice("\n",i);
+
+ if (l.find(String::chr(0xFFFF))!=-1) {
+ begin = font->get_string_size(l.substr(0,l.find(String::chr(0xFFFF)))).x;
+ end = font->get_string_size(l.substr(0,l.rfind(String::chr(0xFFFF)))).x;
+ }
+
+ draw_string(font,hint_ofs+sb->get_offset()+Vector2(0,font->get_ascent()+font->get_height()*i+spacing),l.replace(String::chr(0xFFFF),""),font_color);
+ if (end>0) {
+ Vector2 b = hint_ofs+sb->get_offset()+Vector2(begin,font->get_height()+font->get_height()*i+spacing-1);
+ draw_line(b,b+Vector2(end-begin,0),font_color);
+ }
+ spacing+=cache.line_spacing;
+ }
+ }
+
+
+ } break;
+ case NOTIFICATION_FOCUS_ENTER: {
+
+ if (OS::get_singleton()->has_virtual_keyboard())
+ OS::get_singleton()->show_virtual_keyboard(get_text(),get_global_rect());
+
+ } break;
+ case NOTIFICATION_FOCUS_EXIT: {
+
+ if (OS::get_singleton()->has_virtual_keyboard())
+ OS::get_singleton()->hide_virtual_keyboard();
+
+ } break;
+
+ }
}
void TextEdit::_consume_pair_symbol(CharType ch) {
@@ -918,6 +978,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
return;
} else {
_cancel_completion();
+ _cancel_code_hint();
}
if (mb.pressed) {
@@ -1172,6 +1233,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
}
_cancel_completion();
+
}
/* TEST CONTROL FIRST!! */
@@ -1268,6 +1330,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
break;
unselect=true;
break;
+
default:
if (k.unicode>=32 && !k.mod.command && !k.mod.alt && !k.mod.meta)
clear=true;
@@ -1318,6 +1381,13 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
_push_current_op();
} break;
+ case KEY_ESCAPE: {
+ if (completion_hint!="") {
+ completion_hint="";
+ update();
+
+ }
+ } break;
case KEY_TAB: {
if (readonly)
@@ -1454,6 +1524,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
if (k.mod.shift)
_post_shift_selection();
+ _cancel_code_hint();
} break;
case KEY_DOWN: {
@@ -1473,6 +1544,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
if (k.mod.shift)
_post_shift_selection();
+ _cancel_code_hint();
} break;
@@ -2333,6 +2405,30 @@ String TextEdit::get_text() {
};
+String TextEdit::get_text_for_completion() {
+
+ String longthing;
+ int len = text.size();
+ for (int i=0;i<len;i++) {
+
+ if (i==cursor.line) {
+ longthing+=text[i].substr(0,cursor.column);
+ longthing+=String::chr(0xFFFF); //not unicode, represents the cursor
+ longthing+=text[i].substr(cursor.column,text[i].size());
+ } else {
+
+ longthing+=text[i];
+ }
+
+
+ if (i!=len-1)
+ longthing+="\n";
+ }
+
+ return longthing;
+
+};
+
String TextEdit::get_line(int line) const {
@@ -2966,33 +3062,56 @@ void TextEdit::_confirm_completion() {
if (same)
cursor_set_column(cursor.column+remaining.length());
- else
+ else {
insert_text_at_cursor(remaining);
+ if (remaining.ends_with("(") && auto_brace_completion_enabled) {
+ insert_text_at_cursor(")");
+ cursor.column--;
+ }
+ }
_cancel_completion();
}
+
+void TextEdit::_cancel_code_hint() {
+ completion_hint="";
+ update();
+}
+
void TextEdit::_cancel_completion() {
if (!completion_active)
return;
- completion_active=false;
+ completion_active=false;
update();
}
+static bool _is_completable(CharType c) {
+
+ return !_is_symbol(c) || c=='"' || c=='\'';
+}
+
+
void TextEdit::_update_completion_candidates() {
String l = text[cursor.line];
int cofs = CLAMP(cursor.column,0,l.length());
+
String s;
- while(cofs>0 && l[cofs-1]>32 && !_is_symbol(l[cofs-1])) {
- s=String::chr(l[cofs-1])+s;
+
+ while(cofs>0 && l[cofs-1]>32 && _is_completable(l[cofs-1])) {
+ s=String::chr(l[cofs-1])+s;
+ if (l[cofs-1]=='\'' || l[cofs-1]=='"')
+ break;
+
cofs--;
}
+
update();
if (s=="" && (cofs==0 || !completion_prefixes.has(String::chr(l[cofs-1])))) {
@@ -3055,36 +3174,24 @@ void TextEdit::_update_completion_candidates() {
completion_enabled=true;
}
+
+
void TextEdit::query_code_comple() {
- String l = text[cursor.line];
- int ofs = CLAMP(cursor.column,0,l.length());
- String cs;
- while(ofs>0 && l[ofs-1]>32) {
-
- if (_is_symbol(l[ofs-1])) {
- String s;
- while(ofs>0 && l[ofs-1]>32 && _is_symbol(l[ofs-1])) {
- s=String::chr(l[ofs-1])+s;
- ofs--;
- }
- if (completion_prefixes.has(s))
- cs=s+cs;
- else
- break;
- } else {
+ String l = text[cursor.line];
+ int ofs = CLAMP(cursor.column,0,l.length());
- cs=String::chr(l[ofs-1])+cs;
- ofs--;
- }
+ if (ofs>0 && (_is_completable(l[ofs-1]) || completion_prefixes.has(String::chr(l[ofs-1]))))
+ emit_signal("request_completion");
- }
+}
- if (cs!="") {
- emit_signal("request_completion",cs,cursor.line);
- }
+void TextEdit::set_code_hint(const String& p_hint) {
+ completion_hint=p_hint;
+ completion_hint_offset=-0xFFFF;
+ update();
}
void TextEdit::code_complete(const Vector<String> &p_strings) {
@@ -3236,7 +3343,7 @@ void TextEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("cursor_changed"));
ADD_SIGNAL(MethodInfo("text_changed"));
- ADD_SIGNAL(MethodInfo("request_completion",PropertyInfo(Variant::STRING,"keyword"),PropertyInfo(Variant::INT,"line")));
+ ADD_SIGNAL(MethodInfo("request_completion"));
}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index d70403a944..120d5db54e 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -185,6 +185,8 @@ class TextEdit : public Control {
int completion_index;
Rect2i completion_rect;
int completion_line_ofs;
+ String completion_hint;
+ int completion_hint_offset;
bool setting_text;
@@ -261,6 +263,7 @@ class TextEdit : public Control {
void _clear();
void _cancel_completion();
+ void _cancel_code_hint();
void _confirm_completion();
void _update_completion_candidates();
@@ -350,7 +353,7 @@ public:
void undo();
void redo();
- void clear_undo_history();
+ void clear_undo_history();
void set_draw_tabs(bool p_draw);
@@ -376,10 +379,13 @@ public:
void set_tooltip_request_func(Object *p_obj, const StringName& p_function, const Variant& p_udata);
- void set_completion(bool p_enabled,const Vector<String>& p_prefixes);
+ void set_completion(bool p_enabled,const Vector<String>& p_prefixes);
void code_complete(const Vector<String> &p_strings);
+ void set_code_hint(const String& p_hint);
void query_code_comple();
+ String get_text_for_completion();
+
TextEdit();
~TextEdit();
};
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 45a30d7bca..d9b208d6d3 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -1731,6 +1731,26 @@ NodePath Node::get_import_path() const {
#endif
+static void _add_nodes_to_options(const Node *p_base,const Node *p_node,List<String>*r_options) {
+
+ if (p_node!=p_base && !p_node->get_owner())
+ return;
+ String n = p_base->get_path_to(p_node);
+ r_options->push_back("\""+n+"\"");
+ for(int i=0;i<p_node->get_child_count();i++) {
+ _add_nodes_to_options(p_base,p_node->get_child(i),r_options);
+ }
+}
+
+void Node::get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const {
+
+ String pf=p_function;
+ if ((pf=="has_node" || pf=="get_node") && p_idx==0) {
+
+ _add_nodes_to_options(this,this,r_options);
+ }
+ Object::get_argument_options(p_function,p_idx,r_options);
+}
void Node::_bind_methods() {
diff --git a/scene/main/node.h b/scene/main/node.h
index 371a5325ca..47f49eb625 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -284,6 +284,7 @@ public:
NodePath get_import_path() const;
#endif
+ void get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const;
_FORCE_INLINE_ Viewport *get_viewport() const { return data.viewport; }
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 2c278f4fed..e3427cbe2c 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -458,6 +458,8 @@ FixedMaterial::~FixedMaterial() {
}
+
+
bool ShaderMaterial::_set(const StringName& p_name, const Variant& p_value) {
if (p_name==SceneStringNames::get_singleton()->shader_shader) {
@@ -558,7 +560,21 @@ void ShaderMaterial::_bind_methods() {
}
+void ShaderMaterial::get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const {
+
+ String f = p_function.operator String();
+ if ((f=="get_shader_param" || f=="set_shader_param") && p_idx==0) {
+ if (shader.is_valid()) {
+ List<PropertyInfo> pl;
+ shader->get_param_list(&pl);
+ for (List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {
+ r_options->push_back(E->get().name);
+ }
+ }
+ }
+ Material::get_argument_options(p_function,p_idx,r_options);
+}
ShaderMaterial::ShaderMaterial() :Material(VisualServer::get_singleton()->material_create()){
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 9c3feede08..2b10078e16 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -243,6 +243,7 @@ public:
void set_shader_param(const StringName& p_param,const Variant& p_value);
Variant get_shader_param(const StringName& p_param) const;
+ void get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const;
ShaderMaterial();
};
diff --git a/tools/editor/code_editor.cpp b/tools/editor/code_editor.cpp
index 6de59f184a..84bf7d33f4 100644
--- a/tools/editor/code_editor.cpp
+++ b/tools/editor/code_editor.cpp
@@ -487,6 +487,7 @@ FindReplaceDialog::FindReplaceDialog() {
vb->add_child(error_label);
+
set_hide_on_ok(false);
}
@@ -507,15 +508,19 @@ void CodeTextEditor::_text_changed() {
}
void CodeTextEditor::_code_complete_timer_timeout() {
+ if (!is_visible())
+ return;
if (enable_complete_timer)
text_editor->query_code_comple();
}
-void CodeTextEditor::_complete_request(const String& p_request, int p_line) {
+void CodeTextEditor::_complete_request() {
List<String> entries;
- _code_complete_script(text_editor->get_text(),p_request,p_line,&entries);
+ _code_complete_script(text_editor->get_text_for_completion(),&entries);
// print_line("COMPLETE: "+p_request);
+ if (entries.size()==0)
+ return;
Vector<String> strs;
strs.resize(entries.size());
int i=0;
@@ -555,7 +560,7 @@ void CodeTextEditor::_on_settings_change() {
// AUTO BRACE COMPLETION
text_editor->set_auto_brace_completion(
- EDITOR_DEF("text_editor/auto_brace_complete", false)
+ EDITOR_DEF("text_editor/auto_brace_complete", true)
);
code_complete_timer->set_wait_time(
@@ -632,6 +637,8 @@ CodeTextEditor::CodeTextEditor() {
text_editor->connect("request_completion", this,"_complete_request");
Vector<String> cs;
cs.push_back(".");
+ cs.push_back(",");
+ cs.push_back("(");
text_editor->set_completion(true,cs);
idle->connect("timeout", this,"_text_changed_idle_timeout");
diff --git a/tools/editor/code_editor.h b/tools/editor/code_editor.h
index 1804237f18..f82eaf5ec5 100644
--- a/tools/editor/code_editor.h
+++ b/tools/editor/code_editor.h
@@ -135,7 +135,7 @@ class CodeTextEditor : public Control {
void _on_settings_change();
- void _complete_request(const String& p_request,int p_line);
+ void _complete_request();
protected:
void set_error(const String& p_error);
@@ -143,7 +143,7 @@ protected:
virtual void _load_theme_settings() {}
virtual void _validate_script()=0;
- virtual void _code_complete_script(const String& p_code, const String& p_keyword,int p_line, List<String>* r_options) {};
+ virtual void _code_complete_script(const String& p_code, List<String>* r_options) {};
void _text_changed_idle_timeout();
diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_editor_plugin.cpp
index 35f22aa6f8..b92acb60f9 100644
--- a/tools/editor/plugins/collision_polygon_editor_plugin.cpp
+++ b/tools/editor/plugins/collision_polygon_editor_plugin.cpp
@@ -120,6 +120,8 @@ void CollisionPolygonEditor::_wip_close() {
bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) {
+ if (!node)
+ return false;
Transform gt = node->get_global_transform();
float depth = node->get_depth()*0.5;
diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp
index c5fb574c71..159e0de7fd 100644
--- a/tools/editor/plugins/script_editor_plugin.cpp
+++ b/tools/editor/plugins/script_editor_plugin.cpp
@@ -384,9 +384,35 @@ void ScriptTextEditor::_validate_script() {
_update_name();
}
-void ScriptTextEditor::_code_complete_script(const String& p_code, const String& p_keyword,int p_line, List<String>* r_options) {
- Error err = script->get_language()->complete_keyword(p_code,p_line,script->get_path().get_base_dir(),p_keyword,r_options);
+static Node* _find_node_for_script(Node* p_base, Node*p_current, const Ref<Script>& p_script) {
+
+ if (p_current->get_owner()!=p_base && p_base!=p_current)
+ return NULL;
+ Ref<Script> c = p_current->get_script();
+ if (c==p_script)
+ return p_current;
+ for(int i=0;i<p_current->get_child_count();i++) {
+ Node *found = _find_node_for_script(p_base,p_current->get_child(i),p_script);
+ if (found)
+ return found;
+ }
+
+ return NULL;
+}
+
+void ScriptTextEditor::_code_complete_script(const String& p_code, List<String>* r_options) {
+
+ Node *base = get_tree()->get_edited_scene_root();
+ if (base) {
+ base = _find_node_for_script(base,base,script);
+ }
+ String hint;
+ Error err = script->get_language()->complete_code(p_code,script->get_path().get_base_dir(),base,r_options,hint);
+ if (hint!="") {
+ get_text_edit()->set_code_hint(hint);
+ print_line("hint: "+hint.replace(String::chr(0xFFFF),"|"));
+ }
}
@@ -1559,21 +1585,21 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
edit_menu = memnew( MenuButton );
menu_hb->add_child(edit_menu);
edit_menu->set_text("Edit");
- edit_menu->get_popup()->add_item("Undo",EDIT_UNDO,KEY_MASK_CMD|KEY_Z);
- edit_menu->get_popup()->add_item("Redo",EDIT_REDO,KEY_MASK_CMD|KEY_Y);
+ edit_menu->get_popup()->add_item("Undo",EDIT_UNDO,KEY_MASK_CMD|KEY_Z);
+ edit_menu->get_popup()->add_item("Redo",EDIT_REDO,KEY_MASK_CMD|KEY_Y);
edit_menu->get_popup()->add_separator();
edit_menu->get_popup()->add_item("Cut",EDIT_CUT,KEY_MASK_CMD|KEY_X);
edit_menu->get_popup()->add_item("Copy",EDIT_COPY,KEY_MASK_CMD|KEY_C);
edit_menu->get_popup()->add_item("Paste",EDIT_PASTE,KEY_MASK_CMD|KEY_V);
edit_menu->get_popup()->add_separator();
edit_menu->get_popup()->add_item("Select All",EDIT_SELECT_ALL,KEY_MASK_CMD|KEY_A);
- edit_menu->get_popup()->add_separator();
- edit_menu->get_popup()->add_item("Move Up",EDIT_MOVE_LINE_UP,KEY_MASK_ALT|KEY_UP);
- edit_menu->get_popup()->add_item("Move Down",EDIT_MOVE_LINE_DOWN,KEY_MASK_ALT|KEY_DOWN);
- edit_menu->get_popup()->add_item("Indent Left",EDIT_INDENT_LEFT,KEY_MASK_ALT|KEY_LEFT);
- edit_menu->get_popup()->add_item("Indent Right",EDIT_INDENT_RIGHT,KEY_MASK_ALT|KEY_RIGHT);
- edit_menu->get_popup()->add_item("Toggle Comment",EDIT_TOGGLE_COMMENT,KEY_MASK_CMD|KEY_SLASH);
- edit_menu->get_popup()->add_item("Clone Down",EDIT_CLONE_DOWN,KEY_MASK_CMD|KEY_B);
+ edit_menu->get_popup()->add_separator();
+ edit_menu->get_popup()->add_item("Move Up",EDIT_MOVE_LINE_UP,KEY_MASK_ALT|KEY_UP);
+ edit_menu->get_popup()->add_item("Move Down",EDIT_MOVE_LINE_DOWN,KEY_MASK_ALT|KEY_DOWN);
+ edit_menu->get_popup()->add_item("Indent Left",EDIT_INDENT_LEFT,KEY_MASK_ALT|KEY_LEFT);
+ edit_menu->get_popup()->add_item("Indent Right",EDIT_INDENT_RIGHT,KEY_MASK_ALT|KEY_RIGHT);
+ edit_menu->get_popup()->add_item("Toggle Comment",EDIT_TOGGLE_COMMENT,KEY_MASK_CMD|KEY_SLASH);
+ edit_menu->get_popup()->add_item("Clone Down",EDIT_CLONE_DOWN,KEY_MASK_CMD|KEY_B);
edit_menu->get_popup()->add_separator();
edit_menu->get_popup()->add_item("Complete Symbol",EDIT_COMPLETE,KEY_MASK_CMD|KEY_SPACE);
edit_menu->get_popup()->add_item("Auto Indent",EDIT_AUTO_INDENT,KEY_MASK_CMD|KEY_I);
diff --git a/tools/editor/plugins/script_editor_plugin.h b/tools/editor/plugins/script_editor_plugin.h
index 01942fab2a..136d966587 100644
--- a/tools/editor/plugins/script_editor_plugin.h
+++ b/tools/editor/plugins/script_editor_plugin.h
@@ -85,7 +85,7 @@ protected:
virtual void _validate_script();
- virtual void _code_complete_script(const String& p_code,const String& p_keyword, int p_line, List<String>* r_options);
+ virtual void _code_complete_script(const String& p_code, List<String>* r_options);
virtual void _load_theme_settings();
void _notification(int p_what);