Browse Source

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

Juan Linietsky 10 năm trước cách đây
mục cha
commit
bcf27feb98

+ 3 - 1
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

+ 5 - 0
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;

+ 1 - 0
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)

+ 1 - 1
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 */

+ 126 - 0
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)+"]";
+		}
+	}
+
+}

+ 2 - 0
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; }

+ 6 - 0
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;
 }
 

+ 0 - 30
makefile

@@ -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

+ 7 - 0
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();

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1050 - 338
modules/gdscript/gd_editor.cpp


+ 243 - 52
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;

+ 52 - 3
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();

+ 19 - 1
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 */

+ 5 - 1
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)))) {

+ 1 - 0
modules/gdscript/gd_tokenizer.h

@@ -118,6 +118,7 @@ public:
 		TK_NEWLINE,
 		TK_ERROR,
 		TK_EOF,
+		TK_CURSOR, //used for code completion
 		TK_MAX
 	};
 

+ 2 - 1
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;

+ 13 - 0
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() {
 

+ 3 - 0
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();

+ 57 - 39
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();

+ 393 - 286
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"));
 
 }
 

+ 8 - 2
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();
 };

+ 20 - 0
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() {
 

+ 1 - 0
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; }
 

+ 16 - 0
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()){
 

+ 1 - 0
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();
 };

+ 10 - 3
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");
 

+ 2 - 2
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();

+ 2 - 0
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;

+ 37 - 11
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);

+ 1 - 1
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);
 

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác