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
This commit is contained in:
Juan Linietsky 2014-12-16 22:31:57 -03:00
parent be4e40e90a
commit bcf27feb98
30 changed files with 2245 additions and 929 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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();

View file

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

View file

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

View file

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

View file

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

View file

@ -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() {

View file

@ -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();

View file

@ -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();

View file

@ -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) {
String l = text[cursor.line];
int ofs = CLAMP(cursor.column,0,l.length());
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 {
if (ofs>0 && (_is_completable(l[ofs-1]) || completion_prefixes.has(String::chr(l[ofs-1]))))
emit_signal("request_completion");
cs=String::chr(l[ofs-1])+cs;
ofs--;
}
}
}
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"));
}

View file

@ -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();
};

View file

@ -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() {

View file

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

View file

@ -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()){

View file

@ -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();
};

View file

@ -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");

View file

@ -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();

View file

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

View file

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

View file

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