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 years ago
parent
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();

File diff suppressed because it is too large
+ 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);
 

Some files were not shown because too many files changed in this diff