2014-02-10 01:10:30 +00:00
/*************************************************************************/
/* gd_editor.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
2015-04-18 17:38:54 +00:00
/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */
2014-02-10 01:10:30 +00:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
# include "gd_script.h"
# include "gd_compiler.h"
2015-01-03 16:03:13 +00:00
# include "globals.h"
2014-02-10 01:10:30 +00:00
void GDScriptLanguage : : get_comment_delimiters ( List < String > * p_delimiters ) const {
p_delimiters - > push_back ( " # " ) ;
2015-04-09 21:24:38 +00:00
p_delimiters - > push_back ( " \" \" \" \" \" \" " ) ;
2014-02-10 01:10:30 +00:00
}
void GDScriptLanguage : : get_string_delimiters ( List < String > * p_delimiters ) const {
p_delimiters - > push_back ( " \" \" " ) ;
p_delimiters - > push_back ( " ' ' " ) ;
}
String GDScriptLanguage : : get_template ( const String & p_class_name , const String & p_base_class_name ) const {
String _template = String ( ) +
" \n extends %BASE% \n \n " +
" # member variables here, example: \n " +
" # var a=2 \n " +
" # var b= \" textvar \" \n \n " +
" func _ready(): \n " +
2015-02-10 18:39:52 +00:00
" \t # Initialization here \n " +
2014-02-10 01:10:30 +00:00
" \t pass \n " +
" \n " +
" \n " ;
return _template . replace ( " %BASE% " , p_base_class_name ) ;
}
bool GDScriptLanguage : : validate ( const String & p_script , int & r_line_error , int & r_col_error , String & r_test_error , const String & p_path , List < String > * r_functions ) const {
GDParser parser ;
2014-12-07 05:04:20 +00:00
Error err = parser . parse ( p_script , p_path . get_base_dir ( ) , true , p_path ) ;
2014-02-10 01:10:30 +00:00
if ( err ) {
r_line_error = parser . get_error_line ( ) ;
r_col_error = parser . get_error_column ( ) ;
r_test_error = parser . get_error ( ) ;
return false ;
} else {
const GDParser : : Node * root = parser . get_parse_tree ( ) ;
ERR_FAIL_COND_V ( root - > type ! = GDParser : : Node : : TYPE_CLASS , false ) ;
const GDParser : : ClassNode * cl = static_cast < const GDParser : : ClassNode * > ( root ) ;
Map < int , String > funcs ;
for ( int i = 0 ; i < cl - > functions . size ( ) ; i + + ) {
funcs [ cl - > functions [ i ] - > line ] = cl - > functions [ i ] - > name ;
}
for ( int i = 0 ; i < cl - > static_functions . size ( ) ; i + + ) {
funcs [ cl - > static_functions [ i ] - > line ] = cl - > static_functions [ i ] - > name ;
}
for ( Map < int , String > : : Element * E = funcs . front ( ) ; E ; E = E - > next ( ) ) {
r_functions - > push_back ( E - > get ( ) + " : " + itos ( E - > key ( ) ) ) ;
}
}
return true ;
}
bool GDScriptLanguage : : has_named_classes ( ) const {
return false ;
}
int GDScriptLanguage : : find_function ( const String & p_function , const String & p_code ) const {
2014-02-25 12:31:47 +00:00
GDTokenizerText tokenizer ;
2014-02-10 01:10:30 +00:00
tokenizer . set_code ( p_code ) ;
int indent = 0 ;
while ( tokenizer . get_token ( ) ! = GDTokenizer : : TK_EOF & & tokenizer . get_token ( ) ! = GDTokenizer : : TK_ERROR ) {
if ( tokenizer . get_token ( ) = = GDTokenizer : : TK_NEWLINE ) {
indent = tokenizer . get_token_line_indent ( ) ;
}
2014-10-12 05:13:22 +00:00
//print_line("TOKEN: "+String(GDTokenizer::get_token_name(tokenizer.get_token())));
2014-02-10 01:10:30 +00:00
if ( indent = = 0 & & tokenizer . get_token ( ) = = GDTokenizer : : TK_PR_FUNCTION & & tokenizer . get_token ( 1 ) = = GDTokenizer : : TK_IDENTIFIER ) {
String identifier = tokenizer . get_token_identifier ( 1 ) ;
if ( identifier = = p_function ) {
return tokenizer . get_token_line ( ) ;
}
}
tokenizer . advance ( ) ;
2014-10-12 05:13:22 +00:00
//print_line("NEXT: "+String(GDTokenizer::get_token_name(tokenizer.get_token())));
2014-02-10 01:10:30 +00:00
}
return - 1 ;
}
Script * GDScriptLanguage : : create_script ( ) const {
return memnew ( GDScript ) ;
}
/* DEBUGGER FUNCTIONS */
bool GDScriptLanguage : : debug_break_parse ( const String & p_file , int p_line , const String & p_error ) {
//break because of parse error
if ( ScriptDebugger : : get_singleton ( ) & & Thread : : get_caller_ID ( ) = = Thread : : get_main_ID ( ) ) {
_debug_parse_err_line = p_line ;
_debug_parse_err_file = p_file ;
_debug_error = p_error ;
ScriptDebugger : : get_singleton ( ) - > debug ( this , false ) ;
return true ;
} else {
return false ;
}
}
bool GDScriptLanguage : : debug_break ( const String & p_error , bool p_allow_continue ) {
if ( ScriptDebugger : : get_singleton ( ) & & Thread : : get_caller_ID ( ) = = Thread : : get_main_ID ( ) ) {
_debug_parse_err_line = - 1 ;
_debug_parse_err_file = " " ;
_debug_error = p_error ;
ScriptDebugger : : get_singleton ( ) - > debug ( this , p_allow_continue ) ;
return true ;
} else {
return false ;
}
}
String GDScriptLanguage : : debug_get_error ( ) const {
return _debug_error ;
}
int GDScriptLanguage : : debug_get_stack_level_count ( ) const {
if ( _debug_parse_err_line > = 0 )
return 1 ;
return _debug_call_stack_pos ;
}
int GDScriptLanguage : : debug_get_stack_level_line ( int p_level ) const {
if ( _debug_parse_err_line > = 0 )
return _debug_parse_err_line ;
ERR_FAIL_INDEX_V ( p_level , _debug_call_stack_pos , - 1 ) ;
int l = _debug_call_stack_pos - p_level - 1 ;
return * ( _call_stack [ l ] . line ) ;
}
String GDScriptLanguage : : debug_get_stack_level_function ( int p_level ) const {
if ( _debug_parse_err_line > = 0 )
return " " ;
ERR_FAIL_INDEX_V ( p_level , _debug_call_stack_pos , " " ) ;
int l = _debug_call_stack_pos - p_level - 1 ;
return _call_stack [ l ] . function - > get_name ( ) ;
}
String GDScriptLanguage : : debug_get_stack_level_source ( int p_level ) const {
if ( _debug_parse_err_line > = 0 )
return _debug_parse_err_file ;
ERR_FAIL_INDEX_V ( p_level , _debug_call_stack_pos , " " ) ;
int l = _debug_call_stack_pos - p_level - 1 ;
return _call_stack [ l ] . function - > get_script ( ) - > get_path ( ) ;
}
void GDScriptLanguage : : debug_get_stack_level_locals ( int p_level , List < String > * p_locals , List < Variant > * p_values , int p_max_subitems , int p_max_depth ) {
if ( _debug_parse_err_line > = 0 )
return ;
ERR_FAIL_INDEX ( p_level , _debug_call_stack_pos ) ;
int l = _debug_call_stack_pos - p_level - 1 ;
GDFunction * f = _call_stack [ l ] . function ;
List < Pair < StringName , int > > locals ;
f - > debug_get_stack_member_state ( * _call_stack [ l ] . line , & locals ) ;
for ( List < Pair < StringName , int > > : : Element * E = locals . front ( ) ; E ; E = E - > next ( ) ) {
p_locals - > push_back ( E - > get ( ) . first ) ;
p_values - > push_back ( _call_stack [ l ] . stack [ E - > get ( ) . second ] ) ;
}
}
void GDScriptLanguage : : debug_get_stack_level_members ( int p_level , List < String > * p_members , List < Variant > * p_values , int p_max_subitems , int p_max_depth ) {
if ( _debug_parse_err_line > = 0 )
return ;
ERR_FAIL_INDEX ( p_level , _debug_call_stack_pos ) ;
int l = _debug_call_stack_pos - p_level - 1 ;
GDInstance * instance = _call_stack [ l ] . instance ;
if ( ! instance )
return ;
Ref < GDScript > script = instance - > get_script ( ) ;
ERR_FAIL_COND ( script . is_null ( ) ) ;
2014-10-28 01:54:32 +00:00
const Map < StringName , GDScript : : MemberInfo > & mi = script - > debug_get_member_indices ( ) ;
2014-02-10 01:10:30 +00:00
2014-10-28 01:54:32 +00:00
for ( const Map < StringName , GDScript : : MemberInfo > : : Element * E = mi . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 01:10:30 +00:00
p_members - > push_back ( E - > key ( ) ) ;
2014-10-28 01:54:32 +00:00
p_values - > push_back ( instance - > debug_get_member_by_index ( E - > get ( ) . index ) ) ;
2014-02-10 01:10:30 +00:00
}
}
void GDScriptLanguage : : debug_get_globals ( List < String > * p_locals , List < Variant > * p_values , int p_max_subitems , int p_max_depth ) {
//no globals are really reachable in gdscript
}
String GDScriptLanguage : : debug_parse_stack_level_expression ( int p_level , const String & p_expression , int p_max_subitems , int p_max_depth ) {
if ( _debug_parse_err_line > = 0 )
return " " ;
return " " ;
}
void GDScriptLanguage : : get_recognized_extensions ( List < String > * p_extensions ) const {
p_extensions - > push_back ( " gd " ) ;
}
void GDScriptLanguage : : get_public_functions ( List < MethodInfo > * p_functions ) const {
for ( int i = 0 ; i < GDFunctions : : FUNC_MAX ; i + + ) {
p_functions - > push_back ( GDFunctions : : get_info ( GDFunctions : : Function ( i ) ) ) ;
}
}
2014-02-22 23:28:19 +00:00
void GDScriptLanguage : : get_public_constants ( List < Pair < String , Variant > > * p_constants ) const {
Pair < String , Variant > pi ;
pi . first = " PI " ;
pi . second = Math_PI ;
p_constants - > push_back ( pi ) ;
}
2014-02-10 01:10:30 +00:00
String GDScriptLanguage : : make_function ( const String & p_class , const String & p_name , const StringArray & p_args ) const {
String s = " func " + p_name + " ( " ;
if ( p_args . size ( ) ) {
s + = " " ;
for ( int i = 0 ; i < p_args . size ( ) ; i + + ) {
if ( i > 0 )
s + = " , " ;
s + = p_args [ i ] ;
}
s + = " " ;
}
s + = " ): \n \t pass # replace with function body \n " ;
return s ;
}
2014-12-17 02:17:35 +00:00
# if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
struct GDCompletionIdentifier {
StringName obj_type ;
Variant : : Type type ;
Variant value ; //im case there is a value, also return it
} ;
static GDCompletionIdentifier _get_type_from_variant ( const Variant & p_variant ) {
GDCompletionIdentifier t ;
t . type = p_variant . get_type ( ) ;
t . value = p_variant ;
if ( p_variant . get_type ( ) = = Variant : : OBJECT ) {
Object * obj = p_variant ;
if ( obj ) {
//if (obj->cast_to<GDNativeClass>()) {
// t.obj_type=obj->cast_to<GDNativeClass>()->get_name();
// t.value=Variant();
//} else {
t . obj_type = obj - > get_type ( ) ;
//}
2014-02-10 01:10:30 +00:00
}
}
2014-12-17 01:31:57 +00:00
return t ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
static GDCompletionIdentifier _get_type_from_pinfo ( const PropertyInfo & p_info ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
GDCompletionIdentifier t ;
t . type = p_info . type ;
if ( p_info . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
t . obj_type = p_info . hint_string ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
return t ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
struct GDCompletionContext {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
const GDParser : : ClassNode * _class ;
const GDParser : : FunctionNode * function ;
const GDParser : : BlockNode * block ;
Object * base ;
String base_path ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
} ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
static Ref < Reference > _get_parent_class ( GDCompletionContext & context ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( context . _class - > extends_used ) {
//do inheritance
String path = context . _class - > extends_file ;
Ref < GDScript > script ;
Ref < GDNativeClass > native ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( path ! = " " ) {
//path (and optionally subclasses)
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( path . is_rel_path ( ) ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
path = context . base_path . plus_file ( path ) ;
}
2015-06-26 04:14:31 +00:00
if ( ScriptCodeCompletionCache : : get_sigleton ( ) )
script = ScriptCodeCompletionCache : : get_sigleton ( ) - > get_cached_resource ( path ) ;
else
script = ResourceLoader : : load ( path ) ;
2014-12-17 01:31:57 +00:00
if ( script . is_null ( ) ) {
return REF ( ) ;
}
if ( script - > is_valid ( ) ) {
return REF ( ) ;
}
//print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid));
if ( context . _class - > extends_class . size ( ) ) {
for ( int i = 0 ; i < context . _class - > extends_class . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
String sub = context . _class - > extends_class [ i ] ;
if ( script - > get_subclasses ( ) . has ( sub ) ) {
script = script - > get_subclasses ( ) [ sub ] ;
} else {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
return REF ( ) ;
}
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( script . is_valid ( ) )
return script ;
} else {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( context . _class - > extends_class . size ( ) = = 0 ) {
ERR_PRINT ( " BUG " ) ;
return REF ( ) ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
String base = context . _class - > extends_class [ 0 ] ;
const GDParser : : ClassNode * p = context . _class - > owner ;
Ref < GDScript > base_class ;
#if 0
while ( p ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( p - > subclasses . has ( base ) ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
base_class = p - > subclasses [ base ] ;
break ;
}
p = p - > _owner ;
}
# endif
if ( base_class . is_valid ( ) ) {
#if 0
for ( int i = 1 ; i < context . _class - > extends_class . size ( ) ; i + + ) {
String subclass = context . _class - > extends_class [ i ] ;
if ( base_class - > subclasses . has ( subclass ) ) {
base_class = base_class - > subclasses [ subclass ] ;
2014-02-10 01:10:30 +00:00
} else {
2014-12-17 01:31:57 +00:00
print_line ( " Could not find subclass: " + subclass ) ;
return _get_type_from_class ( context ) ; //fail please
2014-02-10 01:10:30 +00:00
}
}
2014-12-17 01:31:57 +00:00
script = base_class ;
# endif
} else {
if ( context . _class - > extends_class . size ( ) > 1 ) {
return REF ( ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
//if not found, try engine classes
if ( ! GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . has ( base ) ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
return REF ( ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
int base_idx = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ base ] ;
native = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ base_idx ] ;
if ( ! native . is_valid ( ) ) {
print_line ( " Global not a class: ' " + base + " ' " ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
return native ;
}
2014-02-10 01:10:30 +00:00
}
}
2014-12-17 01:31:57 +00:00
return Ref < Reference > ( ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
static GDCompletionIdentifier _get_native_class ( GDCompletionContext & context ) {
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
//eeh...
GDCompletionIdentifier id ;
id . type = Variant : : NIL ;
REF pc = _get_parent_class ( context ) ;
if ( ! pc . is_valid ( ) ) {
return id ;
}
Ref < GDNativeClass > nc = pc ;
Ref < GDScript > s = pc ;
if ( s . is_null ( ) & & nc . is_null ( ) ) {
return id ;
}
while ( ! s . is_null ( ) ) {
nc = s - > get_native ( ) ;
s = s - > get_base ( ) ;
}
if ( nc . is_null ( ) ) {
return id ;
}
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
id . type = Variant : : OBJECT ;
if ( context . base )
id . value = context . base ;
id . obj_type = nc - > get_name ( ) ;
return id ;
}
static bool _guess_identifier_type ( GDCompletionContext & context , int p_line , const StringName & p_identifier , GDCompletionIdentifier & r_type ) ;
static bool _guess_expression_type ( GDCompletionContext & context , const GDParser : : Node * p_node , int p_line , GDCompletionIdentifier & r_type ) {
2014-12-08 18:47:45 +00:00
if ( p_node - > type = = GDParser : : Node : : TYPE_CONSTANT ) {
const GDParser : : ConstantNode * cn = static_cast < const GDParser : : ConstantNode * > ( p_node ) ;
2014-12-17 01:31:57 +00:00
r_type = _get_type_from_variant ( cn - > value ) ;
2014-12-08 18:47:45 +00:00
return true ;
} else if ( p_node - > type = = GDParser : : Node : : TYPE_DICTIONARY ) {
r_type . type = Variant : : DICTIONARY ;
2014-12-17 01:31:57 +00:00
//what the heck, fill it anyway
const GDParser : : DictionaryNode * an = static_cast < const GDParser : : DictionaryNode * > ( p_node ) ;
Dictionary d ;
for ( int i = 0 ; i < an - > elements . size ( ) ; i + + ) {
GDCompletionIdentifier k ;
if ( _guess_expression_type ( context , an - > elements [ i ] . key , p_line , k ) & & k . value . get_type ( ) ! = Variant : : NIL ) {
GDCompletionIdentifier v ;
if ( _guess_expression_type ( context , an - > elements [ i ] . value , p_line , v ) ) {
d [ k . value ] = v . value ;
}
}
}
r_type . value = d ;
2014-12-08 18:47:45 +00:00
return true ;
} else if ( p_node - > type = = GDParser : : Node : : TYPE_ARRAY ) {
r_type . type = Variant : : ARRAY ;
2014-12-17 01:31:57 +00:00
//what the heck, fill it anyway
const GDParser : : ArrayNode * an = static_cast < const GDParser : : ArrayNode * > ( p_node ) ;
Array arr ;
arr . resize ( an - > elements . size ( ) ) ;
for ( int i = 0 ; i < an - > elements . size ( ) ; i + + ) {
GDCompletionIdentifier ci ;
if ( _guess_expression_type ( context , an - > elements [ i ] , p_line , ci ) ) {
arr [ i ] = ci . value ;
}
}
r_type . value = arr ;
2014-12-08 18:47:45 +00:00
return true ;
} else if ( p_node - > type = = GDParser : : Node : : TYPE_BUILT_IN_FUNCTION ) {
MethodInfo mi = GDFunctions : : get_info ( static_cast < const GDParser : : BuiltInFunctionNode * > ( p_node ) - > function ) ;
2014-12-17 01:31:57 +00:00
r_type = _get_type_from_pinfo ( mi . return_val ) ;
2014-12-08 18:47:45 +00:00
return true ;
} else if ( p_node - > type = = GDParser : : Node : : TYPE_IDENTIFIER ) {
2014-12-17 01:31:57 +00:00
return _guess_identifier_type ( context , p_line - 1 , static_cast < const GDParser : : IdentifierNode * > ( p_node ) - > name , r_type ) ;
2014-12-08 18:47:45 +00:00
} else if ( p_node - > type = = GDParser : : Node : : TYPE_SELF ) {
//eeh...
2014-12-17 01:31:57 +00:00
r_type = _get_native_class ( context ) ;
return r_type . type ! = Variant : : NIL ;
2014-12-08 18:47:45 +00:00
} else if ( p_node - > type = = GDParser : : Node : : TYPE_OPERATOR ) {
2014-12-17 01:31:57 +00:00
2014-12-08 18:47:45 +00:00
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( p_node ) ;
if ( op - > op = = GDParser : : OperatorNode : : OP_CALL ) {
if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_TYPE ) {
const GDParser : : TypeNode * tn = static_cast < const GDParser : : TypeNode * > ( op - > arguments [ 0 ] ) ;
r_type . type = tn - > vtype ;
return true ;
} else if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_BUILT_IN_FUNCTION ) {
const GDParser : : BuiltInFunctionNode * bin = static_cast < const GDParser : : BuiltInFunctionNode * > ( op - > arguments [ 0 ] ) ;
2014-12-17 01:31:57 +00:00
return _guess_expression_type ( context , bin , p_line , r_type ) ;
2014-12-08 18:47:45 +00:00
} else if ( op - > arguments . size ( ) > 1 & & op - > arguments [ 1 ] - > type = = GDParser : : Node : : TYPE_IDENTIFIER ) {
2014-12-17 01:31:57 +00:00
2014-12-08 18:47:45 +00:00
GDCompletionIdentifier base ;
2014-12-17 01:31:57 +00:00
if ( ! _guess_expression_type ( context , op - > arguments [ 0 ] , p_line , base ) )
2014-12-08 18:47:45 +00:00
return false ;
2014-12-17 01:31:57 +00:00
StringName id = static_cast < const GDParser : : IdentifierNode * > ( op - > arguments [ 1 ] ) - > name ;
2014-12-08 18:47:45 +00:00
if ( base . type = = Variant : : OBJECT ) {
2014-12-20 18:30:06 +00:00
if ( id . operator String ( ) = = " new " & & base . value . get_type ( ) = = Variant : : OBJECT ) {
Object * obj = base . value ;
if ( obj & & obj - > cast_to < GDNativeClass > ( ) ) {
GDNativeClass * gdnc = obj - > cast_to < GDNativeClass > ( ) ;
r_type . type = Variant : : OBJECT ;
r_type . value = Variant ( ) ;
r_type . obj_type = gdnc - > get_name ( ) ;
return true ;
}
}
2014-12-17 01:31:57 +00:00
2014-12-08 18:47:45 +00:00
if ( ObjectTypeDB : : has_method ( base . obj_type , id ) ) {
2014-12-17 01:31:57 +00:00
2014-12-08 19:09:29 +00:00
# ifdef TOOLS_ENABLED
2014-12-08 18:47:45 +00:00
MethodBind * mb = ObjectTypeDB : : get_method ( base . obj_type , id ) ;
PropertyInfo pi = mb - > get_argument_info ( - 1 ) ;
2014-12-08 19:09:29 +00:00
2014-12-17 01:31:57 +00:00
//try calling the function if constant and all args are constant, should not crash..
Object * baseptr = base . value ;
if ( baseptr & & mb - > is_const ( ) & & pi . type = = Variant : : OBJECT ) {
bool all_valid = true ;
Vector < Variant > args ;
for ( int i = 2 ; i < op - > arguments . size ( ) ; i + + ) {
GDCompletionIdentifier arg ;
if ( _guess_expression_type ( context , op - > arguments [ i ] , p_line , arg ) ) {
if ( arg . value . get_type ( ) ! = Variant : : NIL & & arg . value . get_type ( ) ! = Variant : : OBJECT ) { // calling with object seems dangerous, i don' t know
args . push_back ( arg . value ) ;
} else {
all_valid = false ;
break ;
}
} else {
all_valid = false ;
}
}
if ( all_valid ) {
Vector < const Variant * > argptr ;
for ( int i = 0 ; i < args . size ( ) ; i + + ) {
argptr . push_back ( & args [ i ] ) ;
}
Variant : : CallError ce ;
Variant ret = mb - > call ( baseptr , argptr . ptr ( ) , argptr . size ( ) , ce ) ;
if ( ce . error = = Variant : : CallError : : CALL_OK & & ret . get_type ( ) ! = Variant : : NIL ) {
if ( ret . get_type ( ) ! = Variant : : OBJECT | | ret . operator Object * ( ) ! = NULL ) {
r_type = _get_type_from_variant ( ret ) ;
return true ;
}
}
}
}
2014-12-08 18:47:45 +00:00
r_type . type = pi . type ;
if ( pi . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
r_type . obj_type = pi . hint_string ;
}
2014-12-17 01:31:57 +00:00
return true ;
2014-12-08 19:09:29 +00:00
# else
return false ;
# endif
2014-12-08 18:47:45 +00:00
} else {
return false ;
}
} else {
//method for some variant..
Variant : : CallError ce ;
Variant v = Variant : : construct ( base . type , NULL , 0 , ce ) ;
List < MethodInfo > mi ;
v . get_method_list ( & mi ) ;
for ( List < MethodInfo > : : Element * E = mi . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-20 18:30:06 +00:00
if ( ! E - > get ( ) . name . begins_with ( " _ " ) & & E - > get ( ) . name = = id . operator String ( ) ) {
2014-12-08 18:47:45 +00:00
MethodInfo mi = E - > get ( ) ;
r_type . type = mi . return_val . type ;
if ( mi . return_val . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
r_type . obj_type = mi . return_val . hint_string ;
}
return true ;
}
}
}
}
2014-12-17 01:31:57 +00:00
} else if ( op - > op = = GDParser : : OperatorNode : : OP_INDEX | | op - > op = = GDParser : : OperatorNode : : OP_INDEX_NAMED ) {
GDCompletionIdentifier p1 ;
GDCompletionIdentifier p2 ;
if ( op - > op = = GDParser : : OperatorNode : : OP_INDEX_NAMED ) {
if ( op - > arguments [ 1 ] - > type = = GDParser : : Node : : TYPE_IDENTIFIER ) {
String id = static_cast < const GDParser : : IdentifierNode * > ( op - > arguments [ 1 ] ) - > name ;
p2 . type = Variant : : STRING ;
p2 . value = id ;
}
} else {
if ( op - > arguments [ 1 ] ) {
if ( ! _guess_expression_type ( context , op - > arguments [ 1 ] , p_line , p2 ) ) {
return false ;
}
}
}
if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_ARRAY ) {
const GDParser : : ArrayNode * an = static_cast < const GDParser : : ArrayNode * > ( op - > arguments [ 0 ] ) ;
if ( p2 . value . is_num ( ) ) {
int index = p2 . value ;
if ( index < 0 | | index > = an - > elements . size ( ) )
return false ;
return _guess_expression_type ( context , an - > elements [ index ] , p_line , r_type ) ;
}
} else if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_DICTIONARY ) {
const GDParser : : DictionaryNode * dn = static_cast < const GDParser : : DictionaryNode * > ( op - > arguments [ 0 ] ) ;
if ( p2 . value . get_type ( ) = = Variant : : NIL )
return false ;
for ( int i = 0 ; i < dn - > elements . size ( ) ; i + + ) {
GDCompletionIdentifier k ;
if ( ! _guess_expression_type ( context , dn - > elements [ i ] . key , p_line , k ) ) {
return false ;
}
if ( k . value . get_type ( ) = = Variant : : NIL )
return false ;
if ( k . value = = p2 . value ) {
return _guess_expression_type ( context , dn - > elements [ i ] . value , p_line , r_type ) ;
}
}
} else {
if ( op - > arguments [ 0 ] ) {
if ( ! _guess_expression_type ( context , op - > arguments [ 0 ] , p_line , p1 ) ) {
return false ;
}
}
if ( p1 . value . get_type ( ) = = Variant : : OBJECT ) {
//??
} else if ( p1 . value . get_type ( ) ! = Variant : : NIL ) {
bool valid ;
Variant ret = p1 . value . get ( p2 . value , & valid ) ;
if ( valid ) {
r_type = _get_type_from_variant ( ret ) ;
return true ;
}
} else {
if ( p1 . type ! = Variant : : NIL ) {
Variant : : CallError ce ;
Variant base = Variant : : construct ( p1 . type , NULL , 0 , ce ) ;
bool valid ;
Variant ret = base . get ( p2 . value , & valid ) ;
if ( valid ) {
r_type = _get_type_from_variant ( ret ) ;
return true ;
}
}
}
}
2014-12-08 18:47:45 +00:00
} else {
2014-12-17 01:31:57 +00:00
2014-12-08 18:47:45 +00:00
Variant : : Operator vop = Variant : : OP_MAX ;
switch ( op - > op ) {
2014-12-17 01:31:57 +00:00
case GDParser : : OperatorNode : : OP_ADD : vop = Variant : : OP_ADD ; break ;
case GDParser : : OperatorNode : : OP_SUB : vop = Variant : : OP_SUBSTRACT ; break ;
case GDParser : : OperatorNode : : OP_MUL : vop = Variant : : OP_MULTIPLY ; break ;
case GDParser : : OperatorNode : : OP_DIV : vop = Variant : : OP_DIVIDE ; break ;
case GDParser : : OperatorNode : : OP_MOD : vop = Variant : : OP_MODULE ; break ;
case GDParser : : OperatorNode : : OP_SHIFT_LEFT : vop = Variant : : OP_SHIFT_LEFT ; break ;
case GDParser : : OperatorNode : : OP_SHIFT_RIGHT : vop = Variant : : OP_SHIFT_RIGHT ; break ;
case GDParser : : OperatorNode : : OP_BIT_AND : vop = Variant : : OP_BIT_AND ; break ;
case GDParser : : OperatorNode : : OP_BIT_OR : vop = Variant : : OP_BIT_OR ; break ;
case GDParser : : OperatorNode : : OP_BIT_XOR : vop = Variant : : OP_BIT_XOR ; break ;
2014-12-08 18:47:45 +00:00
default : { }
}
2014-12-17 01:31:57 +00:00
2014-12-08 18:47:45 +00:00
if ( vop = = Variant : : OP_MAX )
return false ;
2014-12-17 01:31:57 +00:00
2014-12-08 18:47:45 +00:00
GDCompletionIdentifier p1 ;
GDCompletionIdentifier p2 ;
if ( op - > arguments [ 0 ] ) {
2014-12-17 01:31:57 +00:00
if ( ! _guess_expression_type ( context , op - > arguments [ 0 ] , p_line , p1 ) ) {
2014-12-08 18:47:45 +00:00
return false ;
2014-12-17 01:31:57 +00:00
}
2014-12-08 18:47:45 +00:00
}
if ( op - > arguments . size ( ) > 1 ) {
2014-12-17 01:31:57 +00:00
if ( ! _guess_expression_type ( context , op - > arguments [ 1 ] , p_line , p2 ) ) {
2014-12-08 18:47:45 +00:00
return false ;
2014-12-17 01:31:57 +00:00
}
2014-12-08 18:47:45 +00:00
}
Variant : : CallError ce ;
2014-12-17 01:31:57 +00:00
bool v1_use_value = p1 . value . get_type ( ) ! = Variant : : NIL & & p1 . value . get_type ( ) ! = Variant : : OBJECT ;
Variant v1 = ( v1_use_value ) ? p1 . value : Variant : : construct ( p1 . type , NULL , 0 , ce ) ;
bool v2_use_value = p2 . value . get_type ( ) ! = Variant : : NIL & & p2 . value . get_type ( ) ! = Variant : : OBJECT ;
Variant v2 = ( v2_use_value ) ? p2 . value : Variant : : construct ( p2 . type , NULL , 0 , ce ) ;
2014-12-08 18:47:45 +00:00
// avoid potential invalid ops
if ( ( vop = = Variant : : OP_DIVIDE | | vop = = Variant : : OP_MODULE ) & & v2 . get_type ( ) = = Variant : : INT ) {
v2 = 1 ;
2014-12-17 01:31:57 +00:00
v2_use_value = false ;
2014-12-08 18:47:45 +00:00
}
if ( vop = = Variant : : OP_DIVIDE & & v2 . get_type ( ) = = Variant : : REAL ) {
v2 = 1.0 ;
2014-12-17 01:31:57 +00:00
v2_use_value = false ;
2014-12-08 18:47:45 +00:00
}
Variant r ;
bool valid ;
Variant : : evaluate ( vop , v1 , v2 , r , valid ) ;
if ( ! valid )
return false ;
r_type . type = r . get_type ( ) ;
2014-12-17 01:31:57 +00:00
if ( v1_use_value & & v2_use_value )
r_type . value = r ;
2014-12-08 18:47:45 +00:00
return true ;
}
}
return false ;
}
2014-12-17 01:31:57 +00:00
static bool _guess_identifier_type_in_block ( GDCompletionContext & context , int p_line , const StringName & p_identifier , GDCompletionIdentifier & r_type ) {
2014-12-08 18:47:45 +00:00
const GDParser : : Node * last_assign = NULL ;
int last_assign_line = - 1 ;
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < context . block - > statements . size ( ) ; i + + ) {
if ( context . block - > statements [ i ] - > line > p_line )
continue ;
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
if ( context . block - > statements [ i ] - > type = = GDParser : : BlockNode : : TYPE_LOCAL_VAR ) {
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
const GDParser : : LocalVarNode * lv = static_cast < const GDParser : : LocalVarNode * > ( context . block - > statements [ i ] ) ;
2014-12-08 18:47:45 +00:00
if ( lv - > assign & & lv - > name = = p_identifier ) {
2014-12-17 01:31:57 +00:00
2014-12-08 18:47:45 +00:00
last_assign = lv - > assign ;
2014-12-17 01:31:57 +00:00
last_assign_line = context . block - > statements [ i ] - > line ;
2014-12-08 18:47:45 +00:00
}
}
2014-12-17 01:31:57 +00:00
if ( context . block - > statements [ i ] - > type = = GDParser : : BlockNode : : TYPE_OPERATOR ) {
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( context . block - > statements [ i ] ) ;
2014-12-08 18:47:45 +00:00
if ( op - > op = = GDParser : : OperatorNode : : OP_ASSIGN ) {
if ( op - > arguments . size ( ) & & op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_IDENTIFIER ) {
2014-12-17 01:31:57 +00:00
2014-12-08 18:47:45 +00:00
const GDParser : : IdentifierNode * id = static_cast < const GDParser : : IdentifierNode * > ( op - > arguments [ 0 ] ) ;
2014-12-17 01:31:57 +00:00
2014-12-08 18:47:45 +00:00
if ( id - > name = = p_identifier ) {
2014-12-17 01:31:57 +00:00
2014-12-08 18:47:45 +00:00
last_assign = op - > arguments [ 1 ] ;
2014-12-17 01:31:57 +00:00
last_assign_line = context . block - > statements [ i ] - > line ;
2014-12-08 18:47:45 +00:00
}
}
}
}
}
//use the last assignment, (then backwards?)
if ( last_assign ) {
2014-12-17 01:31:57 +00:00
return _guess_expression_type ( context , last_assign , last_assign_line , r_type ) ;
2014-12-08 18:47:45 +00:00
}
2014-12-17 01:31:57 +00:00
2014-12-08 18:47:45 +00:00
return false ;
}
2014-12-17 01:31:57 +00:00
static bool _guess_identifier_type ( GDCompletionContext & context , int p_line , const StringName & p_identifier , GDCompletionIdentifier & r_type ) {
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
//go to block first
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
const GDParser : : BlockNode * block = context . block ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
while ( block ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
GDCompletionContext c = context ;
c . block = block ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( _guess_identifier_type_in_block ( c , p_line , p_identifier , r_type ) ) {
return true ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
block = block - > parent_block ;
}
2014-02-10 01:10:30 +00:00
2014-12-20 18:30:06 +00:00
//guess from argument if virtual
2014-12-17 01:31:57 +00:00
if ( context . function & & context . function - > name ! = StringName ( ) ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
int argindex = - 1 ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < context . function - > arguments . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( context . function - > arguments [ i ] = = p_identifier ) {
argindex = i ;
break ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
2014-02-10 01:10:30 +00:00
}
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
if ( argindex ! = - 1 ) {
GDCompletionIdentifier id = _get_native_class ( context ) ;
if ( id . type = = Variant : : OBJECT & & id . obj_type ! = StringName ( ) ) {
//this kinda sucks but meh
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
List < MethodInfo > vmethods ;
ObjectTypeDB : : get_virtual_methods ( id . obj_type , & vmethods ) ;
for ( List < MethodInfo > : : Element * E = vmethods . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-08 18:47:45 +00:00
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( E - > get ( ) . name = = context . function - > name & & argindex < E - > get ( ) . arguments . size ( ) ) {
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
PropertyInfo arg = E - > get ( ) . arguments [ argindex ] ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
int scp = arg . name . find ( " : " ) ;
if ( scp ! = - 1 ) {
r_type . type = Variant : : OBJECT ;
r_type . obj_type = arg . name . substr ( scp + 1 , arg . name . length ( ) ) ;
return true ;
} else {
r_type . type = arg . type ;
if ( arg . hint = = PROPERTY_HINT_RESOURCE_TYPE )
r_type . obj_type = arg . hint_string ;
return true ;
}
}
}
}
2014-02-10 01:10:30 +00:00
}
}
2014-12-17 01:31:57 +00:00
//guess type in constant
for ( int i = 0 ; i < context . _class - > constant_expressions . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( context . _class - > constant_expressions [ i ] . identifier = = p_identifier ) {
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
ERR_FAIL_COND_V ( context . _class - > constant_expressions [ i ] . expression - > type ! = GDParser : : Node : : TYPE_CONSTANT , false ) ;
r_type = _get_type_from_variant ( static_cast < const GDParser : : ConstantNode * > ( context . _class - > constant_expressions [ i ] . expression ) - > value ) ;
return true ;
}
}
2014-12-08 18:47:45 +00:00
2014-12-20 18:30:06 +00:00
if ( ! ( context . function & & context . function - > _static ) ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < context . _class - > variables . size ( ) ; i + + ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( context . _class - > variables [ i ] . identifier = = p_identifier ) {
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
if ( context . _class - > variables [ i ] . _export . type ! = Variant : : NIL ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
r_type = _get_type_from_pinfo ( context . _class - > variables [ i ] . _export ) ;
return true ;
} else if ( context . _class - > variables [ i ] . expression ) {
return _guess_expression_type ( context , context . _class - > variables [ i ] . expression , context . _class - > variables [ i ] . line , r_type ) ;
2014-02-10 01:10:30 +00:00
}
}
}
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
2014-12-17 02:17:35 +00:00
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
for ( Map < StringName , int > : : Element * E = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > key ( ) = = p_identifier ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
r_type = _get_type_from_variant ( GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ E - > get ( ) ] ) ;
return true ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
return false ;
}
2014-12-17 01:31:57 +00:00
static void _find_identifiers_in_block ( GDCompletionContext & context , int p_line , bool p_only_functions , Set < String > & result ) {
if ( p_only_functions )
return ;
for ( int i = 0 ; i < context . block - > statements . size ( ) ; i + + ) {
if ( context . block - > statements [ i ] - > line > p_line )
continue ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( context . block - > statements [ i ] - > type = = GDParser : : BlockNode : : TYPE_LOCAL_VAR ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
const GDParser : : LocalVarNode * lv = static_cast < const GDParser : : LocalVarNode * > ( context . block - > statements [ i ] ) ;
result . insert ( lv - > name . operator String ( ) ) ;
2014-02-10 01:10:30 +00:00
}
}
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
static void _find_identifiers_in_class ( GDCompletionContext & context , bool p_static , bool p_only_functions , Set < String > & result ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( ! p_static & & ! p_only_functions ) {
for ( int i = 0 ; i < context . _class - > variables . size ( ) ; i + + ) {
result . insert ( context . _class - > variables [ i ] . identifier ) ;
2014-02-10 01:10:30 +00:00
}
}
2014-12-17 01:31:57 +00:00
if ( ! p_only_functions ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < context . _class - > constant_expressions . size ( ) ; i + + ) {
result . insert ( context . _class - > constant_expressions [ i ] . identifier ) ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < context . _class - > subclasses . size ( ) ; i + + ) {
result . insert ( context . _class - > subclasses [ i ] - > name ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < context . _class - > static_functions . size ( ) ; i + + ) {
if ( context . _class - > static_functions [ i ] - > arguments . size ( ) )
result . insert ( context . _class - > static_functions [ i ] - > name . operator String ( ) + " ( " ) ;
else
result . insert ( context . _class - > static_functions [ i ] - > name . operator String ( ) + " () " ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
if ( ! p_static ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < context . _class - > functions . size ( ) ; i + + ) {
if ( context . _class - > functions [ i ] - > arguments . size ( ) )
result . insert ( context . _class - > functions [ i ] - > name . operator String ( ) + " ( " ) ;
else
result . insert ( context . _class - > functions [ i ] - > name . operator String ( ) + " () " ) ;
}
}
2014-02-10 01:10:30 +00:00
2014-12-17 02:17:35 +00:00
//globals
2014-12-17 01:31:57 +00:00
Ref < Reference > base = _get_parent_class ( context ) ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
while ( true ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
Ref < GDScript > script = base ;
Ref < GDNativeClass > nc = base ;
if ( script . is_valid ( ) ) {
2014-12-08 18:47:45 +00:00
2014-12-17 01:31:57 +00:00
if ( ! p_static & & ! p_only_functions ) {
for ( const Set < StringName > : : Element * E = script - > get_members ( ) . front ( ) ; E ; E = E - > next ( ) ) {
result . insert ( E - > get ( ) . operator String ( ) ) ;
}
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( ! p_only_functions ) {
for ( const Map < StringName , Variant > : : Element * E = script - > get_constants ( ) . front ( ) ; E ; E = E - > next ( ) ) {
result . insert ( E - > key ( ) . operator String ( ) ) ;
}
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
for ( const Map < StringName , GDFunction > : : Element * E = script - > get_member_functions ( ) . front ( ) ; E ; E = E - > next ( ) ) {
if ( ! p_static | | E - > get ( ) . is_static ( ) ) {
if ( E - > get ( ) . get_argument_count ( ) )
result . insert ( E - > key ( ) . operator String ( ) + " ( " ) ;
else
result . insert ( E - > key ( ) . operator String ( ) + " () " ) ;
}
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( ! p_only_functions ) {
for ( const Map < StringName , Ref < GDScript > > : : Element * E = script - > get_subclasses ( ) . front ( ) ; E ; E = E - > next ( ) ) {
result . insert ( E - > key ( ) . operator String ( ) ) ;
2014-02-10 01:10:30 +00:00
}
}
2014-12-17 01:31:57 +00:00
base = script - > get_base ( ) ;
if ( base . is_null ( ) )
base = script - > get_native ( ) ;
} else if ( nc . is_valid ( ) ) {
if ( ! p_only_functions ) {
StringName type = nc - > get_name ( ) ;
List < String > constants ;
ObjectTypeDB : : get_integer_constant_list ( type , & constants ) ;
for ( List < String > : : Element * E = constants . front ( ) ; E ; E = E - > next ( ) ) {
result . insert ( E - > get ( ) ) ;
}
List < MethodInfo > methods ;
ObjectTypeDB : : get_method_list ( type , & methods ) ;
for ( List < MethodInfo > : : Element * E = methods . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-20 18:30:06 +00:00
if ( E - > get ( ) . name . begins_with ( " _ " ) )
continue ;
2014-12-17 01:31:57 +00:00
if ( E - > get ( ) . arguments . size ( ) )
result . insert ( E - > get ( ) . name + " ( " ) ;
else
result . insert ( E - > get ( ) . name + " () " ) ;
}
}
break ;
} else
break ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
static void _find_identifiers ( GDCompletionContext & context , int p_line , bool p_only_functions , Set < String > & result ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
const GDParser : : BlockNode * block = context . block ;
while ( block ) {
GDCompletionContext c = context ;
c . block = block ;
_find_identifiers_in_block ( c , p_line , p_only_functions , result ) ;
block = block - > parent_block ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
const GDParser : : ClassNode * clss = context . _class ;
2014-02-10 01:10:30 +00:00
2014-12-17 02:46:55 +00:00
bool _static = context . function & & context . function - > _static ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
while ( clss ) {
GDCompletionContext c = context ;
c . _class = clss ;
c . block = NULL ;
c . function = NULL ;
_find_identifiers_in_class ( c , _static , p_only_functions , result ) ;
clss = clss - > owner ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < GDFunctions : : FUNC_MAX ; i + + ) {
result . insert ( GDFunctions : : get_func_name ( GDFunctions : : Function ( i ) ) ) ;
}
2014-12-17 02:17:35 +00:00
static const char * _type_names [ Variant : : VARIANT_MAX ] = {
2015-06-22 03:03:19 +00:00
" null " , " bool " , " int " , " float " , " String " , " Vector2 " , " Rect2 " , " Vector3 " , " Matrix32 " , " Plane " , " Quat " , " AABB " , " Matrix3 " , " Transform " ,
2014-12-17 02:17:35 +00:00
" Color " , " Image " , " NodePath " , " RID " , " Object " , " InputEvent " , " Dictionary " , " Array " , " RawArray " , " IntArray " , " FloatArray " , " StringArray " ,
" Vector2Array " , " Vector3Array " , " ColorArray " } ;
for ( int i = 0 ; i < Variant : : VARIANT_MAX ; i + + ) {
result . insert ( _type_names [ i ] ) ;
}
2014-12-17 01:31:57 +00:00
for ( const Map < StringName , int > : : Element * E = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . front ( ) ; E ; E = E - > next ( ) ) {
result . insert ( E - > key ( ) . operator String ( ) ) ;
}
}
2014-12-20 18:30:06 +00:00
static String _get_visual_datatype ( const PropertyInfo & p_info , bool p_isarg = true ) {
2014-12-17 01:31:57 +00:00
String n = p_info . name ;
int idx = n . find ( " : " ) ;
if ( idx ! = - 1 ) {
return n . substr ( idx + 1 , n . length ( ) ) ;
}
if ( p_info . type = = Variant : : OBJECT & & p_info . hint = = PROPERTY_HINT_RESOURCE_TYPE )
return p_info . hint_string ;
2014-12-20 18:30:06 +00:00
if ( p_info . type = = Variant : : NIL ) {
if ( p_isarg )
return " var " ;
else
return " void " ;
}
2014-12-17 01:31:57 +00:00
return Variant : : get_type_name ( p_info . type ) ;
}
static void _make_function_hint ( const GDParser : : FunctionNode * p_func , int p_argidx , String & arghint ) {
arghint = " func " + p_func - > name + " ( " ;
for ( int i = 0 ; i < p_func - > arguments . size ( ) ; i + + ) {
if ( i > 0 )
arghint + = " , " ;
else
arghint + = " " ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
arghint + = p_func - > arguments [ i ] . operator String ( ) ;
int deffrom = p_func - > arguments . size ( ) - p_func - > default_values . size ( ) ;
if ( i > = deffrom ) {
int defidx = deffrom - i ;
if ( defidx > = 0 & & defidx < p_func - > default_values . size ( ) ) {
if ( p_func - > default_values [ defidx ] - > type = = GDParser : : Node : : TYPE_OPERATOR ) {
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( p_func - > default_values [ defidx ] ) ;
if ( op - > op = = GDParser : : OperatorNode : : OP_ASSIGN ) {
const GDParser : : ConstantNode * cn = static_cast < const GDParser : : ConstantNode * > ( op - > arguments [ 1 ] ) ;
arghint + = " = " + cn - > value . get_construct_string ( ) ;
}
} else {
2014-02-10 01:10:30 +00:00
}
}
}
2014-12-17 01:31:57 +00:00
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
if ( p_func - > arguments . size ( ) > 0 )
arghint + = " " ;
arghint + = " ) " ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
static void _find_type_arguments ( const GDParser : : Node * p_node , int p_line , const StringName & p_method , const GDCompletionIdentifier & id , int p_argidx , Set < String > & result , String & arghint ) {
2014-02-10 01:10:30 +00:00
2015-01-03 16:03:13 +00:00
if ( id . type = = Variant : : INPUT_EVENT & & String ( p_method ) = = " is_action " & & p_argidx = = 0 ) {
List < PropertyInfo > pinfo ;
Globals : : get_singleton ( ) - > get_property_list ( & pinfo ) ;
for ( List < PropertyInfo > : : Element * E = pinfo . front ( ) ; E ; E = E - > next ( ) ) {
const PropertyInfo & pi = E - > get ( ) ;
if ( ! pi . name . begins_with ( " input/ " ) )
continue ;
String name = pi . name . substr ( pi . name . find ( " / " ) + 1 , pi . name . length ( ) ) ;
result . insert ( " \" " + name + " \" " ) ;
}
} else if ( id . type = = Variant : : OBJECT & & id . obj_type ! = StringName ( ) ) {
2014-12-17 01:31:57 +00:00
MethodBind * m = ObjectTypeDB : : get_method ( id . obj_type , p_method ) ;
if ( ! m )
return ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( p_method . operator String ( ) = = " connect " ) {
if ( p_argidx = = 0 ) {
List < MethodInfo > sigs ;
ObjectTypeDB : : get_signal_list ( id . obj_type , & sigs ) ;
for ( List < MethodInfo > : : Element * E = sigs . front ( ) ; E ; E = E - > next ( ) ) {
result . insert ( " \" " + E - > get ( ) . name + " \" " ) ;
2014-02-10 01:10:30 +00:00
}
}
2014-12-17 01:31:57 +00:00
/*if (p_argidx==2) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
ERR_FAIL_COND ( p_node - > type ! = GDParser : : Node : : TYPE_OPERATOR ) ;
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( p_node ) ;
if ( op - > arguments . size ( ) > )
2014-02-10 01:10:30 +00:00
2015-01-03 16:03:13 +00:00
} */
2014-12-17 01:31:57 +00:00
} else {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
Object * obj = id . value ;
if ( obj ) {
List < String > options ;
obj - > get_argument_options ( p_method , p_argidx , & options ) ;
2015-06-26 04:14:31 +00:00
if ( obj - > is_type ( " Node " ) & & p_argidx = = 0 & & ( String ( p_method ) = = " get_node " | | String ( p_method ) = = " has_node " ) ) {
List < PropertyInfo > props ;
Globals : : get_singleton ( ) - > get_property_list ( & props ) ;
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
String s = E - > get ( ) . name ;
if ( ! s . begins_with ( " autoload/ " ) )
continue ;
// print_line("found "+s);
String name = s . get_slice ( " / " , 1 ) ;
options . push_back ( " \" /root/ " + name + " \" " ) ;
}
}
2014-12-17 01:31:57 +00:00
for ( List < String > : : Element * E = options . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
result . insert ( E - > get ( ) ) ;
}
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
2014-12-20 18:30:06 +00:00
arghint = _get_visual_datatype ( m - > get_argument_info ( - 1 ) , false ) + " " + p_method . operator String ( ) + String ( " ( " ) ;
2014-12-17 02:17:35 +00:00
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < m - > get_argument_count ( ) ; i + + ) {
if ( i > 0 )
arghint + = " , " ;
else
arghint + = " " ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
String n = m - > get_argument_info ( i ) . name ;
int dp = n . find ( " : " ) ;
if ( dp ! = - 1 )
n = n . substr ( 0 , dp ) ;
arghint + = _get_visual_datatype ( m - > get_argument_info ( i ) ) + " " + n ;
int deffrom = m - > get_argument_count ( ) - m - > get_default_argument_count ( ) ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( i > = deffrom ) {
int defidx = i - deffrom ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( defidx > = 0 & & defidx < m - > get_default_argument_count ( ) ) {
Variant v = m - > get_default_argument ( i ) ;
arghint + = " = " + v . get_construct_string ( ) ;
}
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
if ( m - > get_argument_count ( ) > 0 )
arghint + = " " ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
arghint + = " ) " ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
static void _find_call_arguments ( GDCompletionContext & context , const GDParser : : Node * p_node , int p_line , int p_argidx , Set < String > & result , String & arghint ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( ! p_node | | p_node - > type ! = GDParser : : Node : : TYPE_OPERATOR ) {
return ;
}
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( p_node ) ;
if ( op - > op ! = GDParser : : OperatorNode : : OP_CALL ) {
return ;
}
if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_BUILT_IN_FUNCTION ) {
//complete built-in function
const GDParser : : BuiltInFunctionNode * fn = static_cast < const GDParser : : BuiltInFunctionNode * > ( op - > arguments [ 0 ] ) ;
MethodInfo mi = GDFunctions : : get_info ( fn - > function ) ;
2014-12-20 18:30:06 +00:00
arghint = _get_visual_datatype ( mi . return_val , false ) + " " + GDFunctions : : get_func_name ( fn - > function ) + String ( " ( " ) ;
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < mi . arguments . size ( ) ; i + + ) {
if ( i > 0 )
arghint + = " , " ;
else
arghint + = " " ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
arghint + = _get_visual_datatype ( mi . arguments [ i ] ) + " " + mi . arguments [ i ] . name ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
}
if ( mi . arguments . size ( ) > 0 )
arghint + = " " ;
arghint + = " ) " ;
} else if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_TYPE ) {
//complete built-in function
const GDParser : : TypeNode * tn = static_cast < const GDParser : : TypeNode * > ( op - > arguments [ 0 ] ) ;
List < MethodInfo > mil ;
Variant : : get_constructor_list ( tn - > vtype , & mil ) ;
for ( List < MethodInfo > : : Element * E = mil . front ( ) ; E ; E = E - > next ( ) ) {
MethodInfo mi = E - > get ( ) ;
if ( mi . arguments . size ( ) = = 0 )
continue ;
if ( E - > prev ( ) )
arghint + = " \n " ;
arghint + = Variant : : get_type_name ( tn - > vtype ) + " " + Variant : : get_type_name ( tn - > vtype ) + String ( " ( " ) ;
for ( int i = 0 ; i < mi . arguments . size ( ) ; i + + ) {
if ( i > 0 )
arghint + = " , " ;
else
arghint + = " " ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
arghint + = _get_visual_datatype ( mi . arguments [ i ] ) + " " + mi . arguments [ i ] . name ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
}
if ( mi . arguments . size ( ) > 0 )
arghint + = " " ;
arghint + = " ) " ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
} else if ( op - > arguments . size ( ) > = 2 & & op - > arguments [ 1 ] - > type = = GDParser : : Node : : TYPE_IDENTIFIER ) {
//make sure identifier exists...
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
const GDParser : : IdentifierNode * id = static_cast < const GDParser : : IdentifierNode * > ( op - > arguments [ 1 ] ) ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_SELF ) {
//self, look up
for ( int i = 0 ; i < context . _class - > static_functions . size ( ) ; i + + ) {
if ( context . _class - > static_functions [ i ] - > name = = id - > name ) {
_make_function_hint ( context . _class - > static_functions [ i ] , p_argidx , arghint ) ;
return ;
}
}
if ( context . function & & ! context . function - > _static ) {
for ( int i = 0 ; i < context . _class - > functions . size ( ) ; i + + ) {
if ( context . _class - > functions [ i ] - > name = = id - > name ) {
_make_function_hint ( context . _class - > functions [ i ] , p_argidx , arghint ) ;
return ;
2014-02-10 01:10:30 +00:00
}
}
}
2014-12-17 01:31:57 +00:00
Ref < Reference > base = _get_parent_class ( context ) ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
while ( true ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
Ref < GDScript > script = base ;
Ref < GDNativeClass > nc = base ;
if ( script . is_valid ( ) ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
for ( const Map < StringName , GDFunction > : : Element * E = script - > get_member_functions ( ) . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > key ( ) = = id - > name ) {
if ( context . function & & context . function - > _static & & ! E - > get ( ) . is_static ( ) )
continue ;
arghint = " func " + id - > name . operator String ( ) + String ( " ( " ) ;
for ( int i = 0 ; i < E - > get ( ) . get_argument_count ( ) ; i + + ) {
if ( i > 0 )
arghint + = " , " ;
else
arghint + = " " ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
arghint + = E - > get ( ) . get_argument_name ( i ) ;
int deffrom = E - > get ( ) . get_argument_count ( ) - E - > get ( ) . get_default_argument_count ( ) ;
if ( i > = deffrom ) {
int defidx = deffrom - i ;
if ( defidx > = 0 & & defidx < E - > get ( ) . get_default_argument_count ( ) ) {
arghint + = " = " + E - > get ( ) . get_default_argument ( defidx ) . get_construct_string ( ) ;
}
}
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
}
if ( E - > get ( ) . get_argument_count ( ) > 0 )
arghint + = " " ;
arghint + = " ) " ;
return ;
}
}
base = script - > get_base ( ) ;
if ( base . is_null ( ) )
base = script - > get_native ( ) ;
} else if ( nc . is_valid ( ) ) {
if ( context . function & & ! context . function - > _static ) {
GDCompletionIdentifier ci ;
ci . type = Variant : : OBJECT ;
ci . obj_type = nc - > get_name ( ) ;
if ( ! context . _class - > owner )
ci . value = context . base ;
_find_type_arguments ( p_node , p_line , id - > name , ci , p_argidx , result , arghint ) ;
//guess type..
/*
List < MethodInfo > methods ;
ObjectTypeDB : : get_method_list ( type , & methods ) ;
for ( List < MethodInfo > : : Element * E = methods . front ( ) ; E ; E = E - > next ( ) ) {
//if (E->get().arguments.size())
// result.insert(E->get().name+"(");
//else
// result.insert(E->get().name+"()");
} */
}
2014-02-10 01:10:30 +00:00
break ;
2014-12-17 01:31:57 +00:00
} else
break ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
} else {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
GDCompletionIdentifier ci ;
if ( _guess_expression_type ( context , op - > arguments [ 0 ] , p_line , ci ) ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
_find_type_arguments ( p_node , p_line , id - > name , ci , p_argidx , result , arghint ) ;
return ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
}
#if 0
bool _static = context . function - > _static ;
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < context . _class - > static_functions . size ( ) ; i + + ) {
if ( context . _class - > static_functions [ i ] - > arguments . size ( ) )
result . insert ( context . _class - > static_functions [ i ] - > name . operator String ( ) + " ( " ) ;
else
result . insert ( context . _class - > static_functions [ i ] - > name . operator String ( ) + " () " ) ;
}
if ( ! p_static ) {
for ( int i = 0 ; i < context . _class - > functions . size ( ) ; i + + ) {
if ( context . _class - > functions [ i ] - > arguments . size ( ) )
result . insert ( context . _class - > functions [ i ] - > name . operator String ( ) + " ( " ) ;
else
result . insert ( context . _class - > functions [ i ] - > name . operator String ( ) + " () " ) ;
}
}
Ref < Reference > base = _get_parent_class ( context ) ;
while ( true ) {
Ref < GDScript > script = base ;
Ref < GDNativeClass > nc = base ;
if ( script . is_valid ( ) ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( ! p_static & & ! p_only_functions ) {
for ( const Set < StringName > : : Element * E = script - > get_members ( ) . front ( ) ; E ; E = E - > next ( ) ) {
result . insert ( E - > get ( ) . operator String ( ) ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
}
if ( ! p_only_functions ) {
for ( const Map < StringName , Variant > : : Element * E = script - > get_constants ( ) . front ( ) ; E ; E = E - > next ( ) ) {
result . insert ( E - > key ( ) . operator String ( ) ) ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
for ( const Map < StringName , GDFunction > : : Element * E = script - > get_member_functions ( ) . front ( ) ; E ; E = E - > next ( ) ) {
if ( ! p_static | | E - > get ( ) . is_static ( ) ) {
if ( E - > get ( ) . get_argument_count ( ) )
result . insert ( E - > key ( ) . operator String ( ) + " ( " ) ;
else
result . insert ( E - > key ( ) . operator String ( ) + " () " ) ;
2014-02-10 01:10:30 +00:00
}
}
2014-12-17 01:31:57 +00:00
if ( ! p_only_functions ) {
for ( const Map < StringName , Ref < GDScript > > : : Element * E = script - > get_subclasses ( ) . front ( ) ; E ; E = E - > next ( ) ) {
result . insert ( E - > key ( ) . operator String ( ) ) ;
}
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
base = script - > get_base ( ) ;
if ( base . is_null ( ) )
base = script - > get_native ( ) ;
} else if ( nc . is_valid ( ) ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
if ( ! p_only_functions ) {
StringName type = nc - > get_name ( ) ;
List < String > constants ;
ObjectTypeDB : : get_integer_constant_list ( type , & constants ) ;
for ( List < String > : : Element * E = constants . front ( ) ; E ; E = E - > next ( ) ) {
result . insert ( E - > get ( ) ) ;
}
List < MethodInfo > methods ;
ObjectTypeDB : : get_method_list ( type , & methods ) ;
for ( List < MethodInfo > : : Element * E = methods . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . arguments . size ( ) )
result . insert ( E - > get ( ) . name + " ( " ) ;
else
result . insert ( E - > get ( ) . name + " () " ) ;
}
}
break ;
} else
break ;
2014-02-10 01:10:30 +00:00
}
2014-12-17 01:31:57 +00:00
for ( int i = 0 ; i < GDFunctions : : FUNC_MAX ; i + + ) {
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
result . insert ( GDFunctions : : get_func_name ( GDFunctions : : Function ( i ) ) ) ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
# endif
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
}
2014-02-10 01:10:30 +00:00
2014-12-17 01:31:57 +00:00
Error GDScriptLanguage : : complete_code ( const String & p_code , const String & p_base_path , Object * p_owner , List < String > * r_options , String & r_call_hint ) {
//print_line( p_code.replace(String::chr(0xFFFF),"<cursor>"));
2014-12-08 18:47:45 +00:00
2014-02-10 01:10:30 +00:00
GDParser p ;
2015-06-26 04:14:31 +00:00
//Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false);
Error err = p . parse ( p_code , p_base_path , false , " " , true ) ;
2014-12-17 01:31:57 +00:00
bool isfunction = false ;
Set < String > options ;
GDCompletionContext context ;
context . _class = p . get_completion_class ( ) ;
context . block = p . get_completion_block ( ) ;
context . function = p . get_completion_function ( ) ;
context . base = p_owner ;
context . base_path = p_base_path ;
switch ( p . get_completion_type ( ) ) {
case GDParser : : COMPLETION_NONE : {
print_line ( " No completion " ) ;
} break ;
case GDParser : : COMPLETION_BUILT_IN_TYPE_CONSTANT : {
print_line ( " Built in type constant " ) ;
List < StringName > constants ;
Variant : : get_numeric_constants_for_type ( p . get_completion_built_in_constant ( ) , & constants ) ;
for ( List < StringName > : : Element * E = constants . front ( ) ; E ; E = E - > next ( ) ) {
options . insert ( E - > get ( ) . operator String ( ) ) ;
}
} break ;
case GDParser : : COMPLETION_FUNCTION :
isfunction = true ;
case GDParser : : COMPLETION_IDENTIFIER : {
_find_identifiers ( context , p . get_completion_line ( ) , isfunction , options ) ;
} break ;
case GDParser : : COMPLETION_PARENT_FUNCTION : {
print_line ( " parent function " ) ;
} break ;
case GDParser : : COMPLETION_METHOD :
isfunction = true ;
case GDParser : : COMPLETION_INDEX : {
const GDParser : : Node * node = p . get_completion_node ( ) ;
if ( node - > type ! = GDParser : : Node : : TYPE_OPERATOR )
break ;
GDCompletionIdentifier t ;
if ( _guess_expression_type ( context , static_cast < const GDParser : : OperatorNode * > ( node ) - > arguments [ 0 ] , p . get_completion_line ( ) , t ) ) {
if ( t . type = = Variant : : OBJECT & & t . obj_type ! = StringName ( ) ) {
2014-12-20 18:30:06 +00:00
if ( t . value . get_type ( ) ) {
Object * obj = t . value ;
if ( obj ) {
GDScript * scr = obj - > cast_to < GDScript > ( ) ;
while ( scr ) {
if ( ! isfunction ) {
for ( const Map < StringName , Variant > : : Element * E = scr - > get_constants ( ) . front ( ) ; E ; E = E - > next ( ) ) {
options . insert ( E - > key ( ) ) ;
}
}
for ( const Map < StringName , GDFunction > : : Element * E = scr - > get_member_functions ( ) . front ( ) ; E ; E = E - > next ( ) ) {
options . insert ( E - > key ( ) ) ;
}
if ( scr - > get_base ( ) . is_valid ( ) )
scr = scr - > get_base ( ) . ptr ( ) ;
else
scr = NULL ;
}
}
}
2014-12-17 01:31:57 +00:00
if ( ! isfunction ) {
ObjectTypeDB : : get_integer_constant_list ( t . obj_type , r_options ) ;
}
List < MethodInfo > mi ;
ObjectTypeDB : : get_method_list ( t . obj_type , & mi ) ;
for ( List < MethodInfo > : : Element * E = mi . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-20 18:30:06 +00:00
if ( E - > get ( ) . name . begins_with ( " _ " ) )
continue ;
2014-12-17 01:31:57 +00:00
if ( E - > get ( ) . arguments . size ( ) )
options . insert ( E - > get ( ) . name + " ( " ) ;
else
options . insert ( E - > get ( ) . name + " () " ) ;
}
} else {
2014-12-18 03:56:33 +00:00
if ( t . type = = Variant : : INPUT_EVENT ) {
//this is hardcoded otherwise it's not obvious
Set < String > exclude ;
for ( int i = 0 ; i < InputEvent : : TYPE_MAX ; i + + ) {
InputEvent ie ;
ie . type = InputEvent : : Type ( i ) ;
static const char * evnames [ ] = {
" # Common " ,
" # Key " ,
" # MouseMotion " ,
" # MouseButton " ,
" # JoyMotion " ,
" # JoyButton " ,
" # ScreenTouch " ,
" # ScreenDrag " ,
" # Action "
} ;
r_options - > push_back ( evnames [ i ] ) ;
Variant v = ie ;
if ( i = = 0 ) {
List < MethodInfo > mi ;
v . get_method_list ( & mi ) ;
for ( List < MethodInfo > : : Element * E = mi . front ( ) ; E ; E = E - > next ( ) ) {
r_options - > push_back ( E - > get ( ) . name + " ( " ) ;
}
}
List < PropertyInfo > pi ;
v . get_property_list ( & pi ) ;
for ( List < PropertyInfo > : : Element * E = pi . front ( ) ; E ; E = E - > next ( ) ) {
if ( i = = 0 )
exclude . insert ( E - > get ( ) . name ) ;
else if ( exclude . has ( E - > get ( ) . name ) )
continue ;
2014-12-17 01:31:57 +00:00
2014-12-18 03:56:33 +00:00
r_options - > push_back ( E - > get ( ) . name ) ;
}
}
return OK ;
} else {
if ( t . value . get_type ( ) = = Variant : : NIL ) {
Variant : : CallError ce ;
t . value = Variant : : construct ( t . type , NULL , 0 , ce ) ;
2014-12-17 01:31:57 +00:00
}
2014-12-18 03:56:33 +00:00
if ( ! isfunction ) {
List < PropertyInfo > pl ;
t . value . get_property_list ( & pl ) ;
for ( List < PropertyInfo > : : Element * E = pl . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . name . find ( " / " ) = = - 1 )
options . insert ( E - > get ( ) . name ) ;
}
}
List < MethodInfo > mi ;
t . value . get_method_list ( & mi ) ;
for ( List < MethodInfo > : : Element * E = mi . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . arguments . size ( ) )
options . insert ( E - > get ( ) . name + " ( " ) ;
else
options . insert ( E - > get ( ) . name + " () " ) ;
}
2014-12-17 01:31:57 +00:00
}
}
}
} break ;
case GDParser : : COMPLETION_CALL_ARGUMENTS : {
_find_call_arguments ( context , p . get_completion_node ( ) , p . get_completion_line ( ) , p . get_completion_argument_index ( ) , options , r_call_hint ) ;
} break ;
2015-01-03 16:03:13 +00:00
case GDParser : : COMPLETION_VIRTUAL_FUNC : {
GDCompletionIdentifier cid = _get_native_class ( context ) ;
if ( cid . obj_type ! = StringName ( ) ) {
List < MethodInfo > vm ;
ObjectTypeDB : : get_virtual_methods ( cid . obj_type , & vm ) ;
for ( List < MethodInfo > : : Element * E = vm . front ( ) ; E ; E = E - > next ( ) ) {
MethodInfo & mi = E - > get ( ) ;
String m = mi . name ;
if ( m . find ( " : " ) ! = - 1 )
m = m . substr ( 0 , m . find ( " : " ) ) ;
m + = " ( " ;
if ( mi . arguments . size ( ) ) {
for ( int i = 0 ; i < mi . arguments . size ( ) ; i + + ) {
if ( i > 0 )
m + = " , " ;
String n = mi . arguments [ i ] . name ;
if ( n . find ( " : " ) ! = - 1 )
n = n . substr ( 0 , n . find ( " : " ) ) ;
m + = n ;
}
}
m + = " ): " ;
options . insert ( m ) ;
}
}
} break ;
2014-12-17 01:31:57 +00:00
}
for ( Set < String > : : Element * E = options . front ( ) ; E ; E = E - > next ( ) ) {
r_options - > push_back ( E - > get ( ) ) ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 02:17:35 +00:00
return OK ;
}
2014-02-10 01:10:30 +00:00
2014-12-17 02:17:35 +00:00
# else
2014-02-10 01:10:30 +00:00
2014-12-17 02:17:35 +00:00
Error GDScriptLanguage : : complete_code ( const String & p_code , const String & p_base_path , Object * p_owner , List < String > * r_options , String & r_call_hint ) {
2014-02-10 01:10:30 +00:00
return OK ;
}
2014-12-17 02:17:35 +00:00
# endif
2014-12-17 01:31:57 +00:00
2014-05-24 04:35:47 +00:00
void GDScriptLanguage : : auto_indent_code ( String & p_code , int p_from_line , int p_to_line ) const {
Vector < String > lines = p_code . split ( " \n " ) ;
List < int > indent_stack ;
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
String l = lines [ i ] ;
int tc = 0 ;
for ( int j = 0 ; j < l . length ( ) ; j + + ) {
if ( l [ j ] = = ' ' | | l [ j ] = = ' \t ' ) {
tc + + ;
} else {
break ;
}
}
String st = l . substr ( tc , l . length ( ) ) . strip_edges ( ) ;
if ( st = = " " | | st . begins_with ( " # " ) )
continue ; //ignore!
int ilevel = 0 ;
if ( indent_stack . size ( ) ) {
ilevel = indent_stack . back ( ) - > get ( ) ;
}
if ( tc > ilevel ) {
indent_stack . push_back ( tc ) ;
} else if ( tc < ilevel ) {
while ( indent_stack . size ( ) & & indent_stack . back ( ) - > get ( ) > tc ) {
indent_stack . pop_back ( ) ;
}
if ( indent_stack . size ( ) & & indent_stack . back ( ) - > get ( ) ! = tc )
indent_stack . push_back ( tc ) ; //this is not right but gets the job done
}
if ( i > = p_from_line ) {
l = " " ;
for ( int j = 0 ; j < indent_stack . size ( ) ; j + + )
l + = " \t " ;
l + = st ;
} else if ( i > p_to_line ) {
break ;
}
//print_line(itos(indent_stack.size())+","+itos(tc)+": "+l);
lines [ i ] = l ;
}
p_code = " " ;
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
if ( i > 0 )
p_code + = " \n " ;
p_code + = lines [ i ] ;
}
}