2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* gd_editor.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2017-01-01 22:01:57 +01:00
/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
2017-04-08 00:11:42 +02:00
/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
2014-02-10 02:10:30 +01: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. */
/*************************************************************************/
2017-08-27 21:07:15 +02:00
# include "gd_script.h"
2017-04-17 15:24:30 +02:00
# include "editor/editor_settings.h"
2014-02-10 02:10:30 +01:00
# include "gd_compiler.h"
2017-08-24 05:06:56 +02:00
# include "global_constants.h"
2015-06-27 20:52:39 +02:00
# include "os/file_access.h"
2017-07-22 12:47:04 +02:00
# include "project_settings.h"
2017-08-27 21:07:15 +02:00
2017-04-16 21:09:17 +02:00
# ifdef TOOLS_ENABLED
# include "editor/editor_file_system.h"
# include "editor/editor_settings.h"
2017-08-19 01:02:56 +02:00
# include "engine.h"
2017-04-16 21:09:17 +02:00
# endif
2014-02-10 02:10:30 +01:00
void GDScriptLanguage : : get_comment_delimiters ( List < String > * p_delimiters ) const {
p_delimiters - > push_back ( " # " ) ;
2015-04-09 23:24:38 +02:00
p_delimiters - > push_back ( " \" \" \" \" \" \" " ) ;
2014-02-10 02:10:30 +01:00
}
void GDScriptLanguage : : get_string_delimiters ( List < String > * p_delimiters ) const {
p_delimiters - > push_back ( " \" \" " ) ;
p_delimiters - > push_back ( " ' ' " ) ;
}
2017-03-05 16:44:50 +01:00
Ref < Script > GDScriptLanguage : : get_template ( const String & p_class_name , const String & p_base_class_name ) const {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
String _template = String ( ) +
" extends %BASE% \n \n " +
" # class member variables go here, for example: \n " +
" # var a = 2 \n " +
" # var b = \" textvar \" \n \n " +
" func _ready(): \n " +
2017-04-25 03:27:49 +02:00
" %TS%# Called every time the node is added to the scene. \n " +
" %TS%# Initialization here \n " +
" %TS%pass \n " ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
_template = _template . replace ( " %BASE% " , p_base_class_name ) ;
2017-04-25 03:27:49 +02:00
_template = _template . replace ( " %TS% " , _get_indentation ( ) ) ;
2016-08-06 03:46:45 +02:00
Ref < GDScript > script ;
script . instance ( ) ;
script - > set_source_code ( _template ) ;
return script ;
2014-02-10 02:10:30 +01:00
}
2017-06-13 22:03:08 +02:00
bool GDScriptLanguage : : is_using_templates ( ) {
return true ;
}
void GDScriptLanguage : : make_template ( const String & p_class_name , const String & p_base_class_name , Ref < Script > & p_script ) {
String src = p_script - > get_source_code ( ) ;
src = src . replace ( " %BASE% " , p_base_class_name ) ;
src = src . replace ( " %TS% " , _get_indentation ( ) ) ;
p_script - > set_source_code ( src ) ;
}
2017-03-05 16:44:50 +01:00
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 {
2014-02-10 02:10:30 +01:00
GDParser parser ;
2017-03-05 16:44:50 +01:00
Error err = parser . parse ( p_script , p_path . get_base_dir ( ) , true , p_path ) ;
2014-02-10 02:10:30 +01:00
if ( err ) {
2017-03-05 16:44:50 +01:00
r_line_error = parser . get_error_line ( ) ;
r_col_error = parser . get_error_column ( ) ;
r_test_error = parser . get_error ( ) ;
2014-02-10 02:10:30 +01:00
return false ;
} else {
const GDParser : : Node * root = parser . get_parse_tree ( ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( root - > type ! = GDParser : : Node : : TYPE_CLASS , false ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const GDParser : : ClassNode * cl = static_cast < const GDParser : : ClassNode * > ( root ) ;
Map < int , String > funcs ;
for ( int i = 0 ; i < cl - > functions . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
funcs [ cl - > functions [ i ] - > line ] = cl - > functions [ i ] - > name ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < cl - > static_functions . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
funcs [ cl - > static_functions [ i ] - > line ] = cl - > static_functions [ i ] - > name ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
for ( Map < int , String > : : Element * E = funcs . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
r_functions - > push_back ( E - > get ( ) + " : " + itos ( E - > key ( ) ) ) ;
2014-02-10 02:10:30 +01:00
}
}
return true ;
}
bool GDScriptLanguage : : has_named_classes ( ) const {
return false ;
}
2017-03-05 16:44:50 +01:00
int GDScriptLanguage : : find_function ( const String & p_function , const String & p_code ) const {
2014-02-10 02:10:30 +01:00
2014-02-25 13:31:47 +01:00
GDTokenizerText tokenizer ;
2014-02-10 02:10:30 +01:00
tokenizer . set_code ( p_code ) ;
2017-03-05 16:44:50 +01:00
int indent = 0 ;
while ( tokenizer . get_token ( ) ! = GDTokenizer : : TK_EOF & & tokenizer . get_token ( ) ! = GDTokenizer : : TK_ERROR ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( tokenizer . get_token ( ) = = GDTokenizer : : TK_NEWLINE ) {
indent = tokenizer . get_token_line_indent ( ) ;
2014-02-10 02:10:30 +01:00
}
2014-10-12 07:13:22 +02:00
//print_line("TOKEN: "+String(GDTokenizer::get_token_name(tokenizer.get_token())));
2017-03-05 16:44:50 +01:00
if ( indent = = 0 & & tokenizer . get_token ( ) = = GDTokenizer : : TK_PR_FUNCTION & & tokenizer . get_token ( 1 ) = = GDTokenizer : : TK_IDENTIFIER ) {
2014-02-10 02:10:30 +01:00
String identifier = tokenizer . get_token_identifier ( 1 ) ;
2017-03-05 16:44:50 +01:00
if ( identifier = = p_function ) {
2014-02-10 02:10:30 +01:00
return tokenizer . get_token_line ( ) ;
}
}
tokenizer . advance ( ) ;
2014-10-12 07:13:22 +02:00
//print_line("NEXT: "+String(GDTokenizer::get_token_name(tokenizer.get_token())));
2014-02-10 02:10:30 +01:00
}
return - 1 ;
}
Script * GDScriptLanguage : : create_script ( ) const {
2017-03-05 16:44:50 +01:00
return memnew ( GDScript ) ;
2014-02-10 02:10:30 +01:00
}
/* DEBUGGER FUNCTIONS */
2017-03-05 16:44:50 +01:00
bool GDScriptLanguage : : debug_break_parse ( const String & p_file , int p_line , const String & p_error ) {
2014-02-10 02:10:30 +01:00
//break because of parse error
2017-08-07 12:17:31 +02:00
if ( ScriptDebugger : : get_singleton ( ) & & Thread : : get_caller_id ( ) = = Thread : : get_main_id ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
_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 ;
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
bool GDScriptLanguage : : debug_break ( const String & p_error , bool p_allow_continue ) {
2014-02-10 02:10:30 +01:00
2017-08-07 12:17:31 +02:00
if ( ScriptDebugger : : get_singleton ( ) & & Thread : : get_caller_id ( ) = = Thread : : get_main_id ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
_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 ;
}
2014-02-10 02:10:30 +01:00
}
String GDScriptLanguage : : debug_get_error ( ) const {
2017-03-05 16:44:50 +01:00
return _debug_error ;
2014-02-10 02:10:30 +01:00
}
int GDScriptLanguage : : debug_get_stack_level_count ( ) const {
2017-03-05 16:44:50 +01:00
if ( _debug_parse_err_line > = 0 )
2014-02-10 02:10:30 +01:00
return 1 ;
return _debug_call_stack_pos ;
}
int GDScriptLanguage : : debug_get_stack_level_line ( int p_level ) const {
2017-03-05 16:44:50 +01:00
if ( _debug_parse_err_line > = 0 )
2014-02-10 02:10:30 +01:00
return _debug_parse_err_line ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX_V ( p_level , _debug_call_stack_pos , - 1 ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int l = _debug_call_stack_pos - p_level - 1 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
return * ( _call_stack [ l ] . line ) ;
2014-02-10 02:10:30 +01:00
}
String GDScriptLanguage : : debug_get_stack_level_function ( int p_level ) const {
2017-03-05 16:44:50 +01:00
if ( _debug_parse_err_line > = 0 )
2014-02-10 02:10:30 +01:00
return " " ;
2017-03-05 16:44:50 +01:00
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 ( ) ;
2014-02-10 02:10:30 +01:00
}
String GDScriptLanguage : : debug_get_stack_level_source ( int p_level ) const {
2017-03-05 16:44:50 +01:00
if ( _debug_parse_err_line > = 0 )
2014-02-10 02:10:30 +01:00
return _debug_parse_err_file ;
2017-03-05 16:44:50 +01:00
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_source ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
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 ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( _debug_parse_err_line > = 0 )
2014-02-10 02:10:30 +01:00
return ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX ( p_level , _debug_call_stack_pos ) ;
int l = _debug_call_stack_pos - p_level - 1 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
GDFunction * f = _call_stack [ l ] . function ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
List < Pair < StringName , int > > locals ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
f - > debug_get_stack_member_state ( * _call_stack [ l ] . line , & locals ) ;
for ( List < Pair < StringName , int > > : : Element * E = locals . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
p_locals - > push_back ( E - > get ( ) . first ) ;
p_values - > push_back ( _call_stack [ l ] . stack [ E - > get ( ) . second ] ) ;
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
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 ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( _debug_parse_err_line > = 0 )
2014-02-10 02:10:30 +01:00
return ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX ( p_level , _debug_call_stack_pos ) ;
int l = _debug_call_stack_pos - p_level - 1 ;
2014-02-10 02:10:30 +01:00
2015-06-27 20:52:39 +02:00
GDInstance * instance = _call_stack [ l ] . instance ;
2014-02-10 02:10:30 +01:00
2015-06-27 20:52:39 +02:00
if ( ! instance )
return ;
2014-02-10 02:10:30 +01:00
2015-06-27 20:52:39 +02:00
Ref < GDScript > script = instance - > get_script ( ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( script . is_null ( ) ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const Map < StringName , GDScript : : MemberInfo > & mi = script - > debug_get_member_indices ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( const Map < StringName , GDScript : : MemberInfo > : : Element * E = mi . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2015-06-27 20:52:39 +02:00
p_members - > push_back ( E - > key ( ) ) ;
2017-03-05 16:44:50 +01:00
p_values - > push_back ( instance - > debug_get_member_by_index ( E - > get ( ) . index ) ) ;
2015-06-27 20:52:39 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : debug_get_globals ( List < String > * p_locals , List < Variant > * p_values , int p_max_subitems , int p_max_depth ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
//no globals are really reachable in gdscript
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
String GDScriptLanguage : : debug_parse_stack_level_expression ( int p_level , const String & p_expression , int p_max_subitems , int p_max_depth ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( _debug_parse_err_line > = 0 )
2014-02-10 02:10:30 +01:00
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 {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < GDFunctions : : FUNC_MAX ; i + + ) {
2014-02-10 02:10:30 +01:00
p_functions - > push_back ( GDFunctions : : get_info ( GDFunctions : : Function ( i ) ) ) ;
}
2016-02-09 20:58:03 +01:00
//not really "functions", but..
{
MethodInfo mi ;
2017-03-05 16:44:50 +01:00
mi . name = " preload:Resource " ;
mi . arguments . push_back ( PropertyInfo ( Variant : : STRING , " path " ) ) ;
mi . return_val = PropertyInfo ( Variant : : OBJECT , " " , PROPERTY_HINT_RESOURCE_TYPE , " Resource " ) ;
2016-02-09 20:58:03 +01:00
p_functions - > push_back ( mi ) ;
}
{
MethodInfo mi ;
2017-03-05 16:44:50 +01:00
mi . name = " yield:GDFunctionState " ;
mi . arguments . push_back ( PropertyInfo ( Variant : : OBJECT , " object " ) ) ;
mi . arguments . push_back ( PropertyInfo ( Variant : : STRING , " signal " ) ) ;
2016-02-09 20:58:03 +01:00
mi . default_arguments . push_back ( Variant : : NIL ) ;
mi . default_arguments . push_back ( Variant : : STRING ) ;
p_functions - > push_back ( mi ) ;
}
{
MethodInfo mi ;
2017-03-05 16:44:50 +01:00
mi . name = " assert " ;
mi . arguments . push_back ( PropertyInfo ( Variant : : BOOL , " condition " ) ) ;
2016-02-09 20:58:03 +01:00
p_functions - > push_back ( mi ) ;
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : get_public_constants ( List < Pair < String , Variant > > * p_constants ) const {
2014-02-23 00:28:19 +01:00
2017-03-05 16:44:50 +01:00
Pair < String , Variant > pi ;
pi . first = " PI " ;
pi . second = Math_PI ;
2014-02-23 00:28:19 +01:00
p_constants - > push_back ( pi ) ;
2017-02-06 23:44:22 +01:00
Pair < String , Variant > infinity ;
infinity . first = " INF " ;
infinity . second = Math_INF ;
p_constants - > push_back ( infinity ) ;
Pair < String , Variant > nan ;
nan . first = " NAN " ;
nan . second = Math_NAN ;
p_constants - > push_back ( nan ) ;
2014-02-23 00:28:19 +01:00
}
2017-03-05 16:44:50 +01:00
String GDScriptLanguage : : make_function ( const String & p_class , const String & p_name , const PoolStringArray & p_args ) const {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
String s = " func " + p_name + " ( " ;
2014-02-10 02:10:30 +01:00
if ( p_args . size ( ) ) {
2017-03-05 16:44:50 +01:00
s + = " " ;
for ( int i = 0 ; i < p_args . size ( ) ; i + + ) {
if ( i > 0 )
s + = " , " ;
s + = p_args [ i ] . get_slice ( " : " , 0 ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
s + = " " ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
s + = " ): \n \t pass # replace with function body \n " ;
2014-02-10 02:10:30 +01:00
return s ;
}
2014-12-17 03:17:35 +01:00
# if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
struct GDCompletionIdentifier {
2017-08-24 05:06:56 +02:00
String enumeration ;
2017-01-04 21:37:45 +01:00
StringName obj_type ;
2015-06-27 20:52:39 +02:00
Ref < GDScript > script ;
2014-12-17 02:31:57 +01:00
Variant : : Type type ;
Variant value ; //im case there is a value, also return it
} ;
2017-08-28 00:03:19 +02:00
static GDCompletionIdentifier _get_type_from_variant ( const Variant & p_variant , bool p_allow_gdnative_class = false ) {
2014-12-17 02:31:57 +01:00
GDCompletionIdentifier t ;
2017-03-05 16:44:50 +01:00
t . type = p_variant . get_type ( ) ;
t . value = p_variant ;
if ( p_variant . get_type ( ) = = Variant : : OBJECT ) {
2014-12-17 02:31:57 +01:00
Object * obj = p_variant ;
if ( obj ) {
2017-08-28 00:03:19 +02:00
if ( p_allow_gdnative_class & & Object : : cast_to < GDNativeClass > ( obj ) ) {
t . obj_type = Object : : cast_to < GDNativeClass > ( obj ) - > get_name ( ) ;
t . value = Variant ( ) ;
2017-01-14 12:26:56 +01:00
} else {
2017-08-28 00:03:19 +02:00
t . obj_type = obj - > get_class ( ) ;
}
2014-02-10 02:10:30 +01:00
}
}
2014-12-17 02:31:57 +01:00
return t ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
static GDCompletionIdentifier _get_type_from_pinfo ( const PropertyInfo & p_info ) {
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
GDCompletionIdentifier t ;
2017-03-05 16:44:50 +01:00
t . type = p_info . type ;
if ( p_info . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
t . obj_type = p_info . hint_string ;
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
return t ;
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
struct GDCompletionContext {
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
const GDParser : : ClassNode * _class ;
const GDParser : : FunctionNode * function ;
const GDParser : : BlockNode * block ;
2017-03-05 16:44:50 +01:00
Object * base ;
2014-12-17 02:31:57 +01:00
String base_path ;
} ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
static Ref < Reference > _get_parent_class ( GDCompletionContext & context ) {
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
if ( context . _class - > extends_used ) {
//do inheritance
String path = context . _class - > extends_file ;
Ref < GDScript > script ;
Ref < GDNativeClass > native ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( path ! = " " ) {
2014-12-17 02:31:57 +01:00
//path (and optionally subclasses)
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
if ( path . is_rel_path ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
path = context . base_path . plus_file ( path ) ;
2014-12-17 02:31:57 +01:00
}
2015-06-26 06:14:31 +02:00
if ( ScriptCodeCompletionCache : : get_sigleton ( ) )
script = ScriptCodeCompletionCache : : get_sigleton ( ) - > get_cached_resource ( path ) ;
else
script = ResourceLoader : : load ( path ) ;
2014-12-17 02:31:57 +01:00
if ( script . is_null ( ) ) {
return REF ( ) ;
}
2017-03-17 23:27:44 +01:00
if ( ! script - > is_valid ( ) ) {
2014-12-17 02:31:57 +01:00
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 ( ) ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > extends_class . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
String sub = context . _class - > extends_class [ i ] ;
if ( script - > get_subclasses ( ) . has ( sub ) ) {
2017-03-05 16:44:50 +01:00
script = script - > get_subclasses ( ) [ sub ] ;
2014-12-17 02:31:57 +01:00
} else {
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
return REF ( ) ;
}
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
if ( script . is_valid ( ) )
return script ;
} else {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( context . _class - > extends_class . size ( ) = = 0 ) {
2014-12-17 02:31:57 +01:00
ERR_PRINT ( " BUG " ) ;
return REF ( ) ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
String base = context . _class - > extends_class [ 0 ] ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( context . _class - > extends_class . size ( ) > 1 ) {
2014-12-17 02:31:57 +01:00
2016-07-03 23:19:22 +02:00
return REF ( ) ;
}
//if not found, try engine classes
if ( ! GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . has ( base ) ) {
2014-02-10 02:10:30 +01:00
2016-07-03 23:19:22 +02:00
return REF ( ) ;
2014-12-17 02:31:57 +01:00
}
2016-07-03 23:19:22 +02:00
int base_idx = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) [ base ] ;
native = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ base_idx ] ;
return native ;
2014-02-10 02:10:30 +01:00
}
}
2014-12-17 02:31:57 +01:00
return Ref < Reference > ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
static GDCompletionIdentifier _get_native_class ( GDCompletionContext & context ) {
2014-12-08 19:47:45 +01:00
2014-12-17 02:31:57 +01:00
//eeh...
GDCompletionIdentifier id ;
2017-03-05 16:44:50 +01:00
id . type = Variant : : NIL ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
REF pc = _get_parent_class ( context ) ;
2014-12-17 02:31:57 +01:00
if ( ! pc . is_valid ( ) ) {
return id ;
}
Ref < GDNativeClass > nc = pc ;
Ref < GDScript > s = pc ;
if ( s . is_null ( ) & & nc . is_null ( ) ) {
return id ;
}
2017-03-05 16:44:50 +01:00
while ( ! s . is_null ( ) ) {
nc = s - > get_native ( ) ;
s = s - > get_base ( ) ;
2014-12-17 02:31:57 +01:00
}
if ( nc . is_null ( ) ) {
return id ;
}
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
id . type = Variant : : OBJECT ;
2014-12-17 02:31:57 +01:00
if ( context . base )
2017-03-05 16:44:50 +01:00
id . value = context . base ;
id . obj_type = nc - > get_name ( ) ;
2014-12-17 02:31:57 +01:00
return id ;
}
2017-08-28 00:03:19 +02:00
static bool _guess_identifier_type ( GDCompletionContext & context , int p_line , const StringName & p_identifier , GDCompletionIdentifier & r_type , bool p_for_indexing ) ;
2014-12-17 02:31:57 +01:00
2017-08-28 00:03:19 +02:00
static bool _guess_expression_type ( GDCompletionContext & context , const GDParser : : Node * p_node , int p_line , GDCompletionIdentifier & r_type , bool p_for_indexing = false ) {
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
if ( p_node - > type = = GDParser : : Node : : TYPE_CONSTANT ) {
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
const GDParser : : ConstantNode * cn = static_cast < const GDParser : : ConstantNode * > ( p_node ) ;
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
r_type = _get_type_from_variant ( cn - > value ) ;
2014-12-08 19:47:45 +01:00
return true ;
2017-03-05 16:44:50 +01:00
} else if ( p_node - > type = = GDParser : : Node : : TYPE_DICTIONARY ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
r_type . type = Variant : : DICTIONARY ;
2014-12-17 02:31:57 +01:00
//what the heck, fill it anyway
const GDParser : : DictionaryNode * an = static_cast < const GDParser : : DictionaryNode * > ( p_node ) ;
Dictionary d ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < an - > elements . size ( ) ; i + + ) {
2014-12-17 02:31:57 +01:00
GDCompletionIdentifier k ;
2017-03-05 16:44:50 +01:00
if ( _guess_expression_type ( context , an - > elements [ i ] . key , p_line , k ) & & k . value . get_type ( ) ! = Variant : : NIL ) {
2014-12-17 02:31:57 +01:00
GDCompletionIdentifier v ;
2017-03-05 16:44:50 +01:00
if ( _guess_expression_type ( context , an - > elements [ i ] . value , p_line , v ) ) {
d [ k . value ] = v . value ;
2014-12-17 02:31:57 +01:00
}
}
}
2017-03-05 16:44:50 +01:00
r_type . value = d ;
2014-12-08 19:47:45 +01:00
return true ;
2017-03-05 16:44:50 +01:00
} else if ( p_node - > type = = GDParser : : Node : : TYPE_ARRAY ) {
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
r_type . type = Variant : : ARRAY ;
2014-12-17 02:31:57 +01: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 ( ) ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < an - > elements . size ( ) ; i + + ) {
2014-12-17 02:31:57 +01:00
GDCompletionIdentifier ci ;
2017-03-05 16:44:50 +01:00
if ( _guess_expression_type ( context , an - > elements [ i ] , p_line , ci ) ) {
arr [ i ] = ci . value ;
2014-12-17 02:31:57 +01:00
}
}
2017-03-05 16:44:50 +01:00
r_type . value = arr ;
2014-12-08 19:47:45 +01:00
return true ;
2017-03-05 16:44:50 +01:00
} else if ( p_node - > type = = GDParser : : Node : : TYPE_BUILT_IN_FUNCTION ) {
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
MethodInfo mi = GDFunctions : : get_info ( static_cast < const GDParser : : BuiltInFunctionNode * > ( p_node ) - > function ) ;
r_type = _get_type_from_pinfo ( mi . return_val ) ;
2014-12-17 02:31:57 +01:00
2014-12-08 19:47:45 +01:00
return true ;
2017-03-05 16:44:50 +01:00
} else if ( p_node - > type = = GDParser : : Node : : TYPE_IDENTIFIER ) {
2014-12-08 19:47:45 +01:00
2017-08-28 00:03:19 +02:00
return _guess_identifier_type ( context , p_line - 1 , static_cast < const GDParser : : IdentifierNode * > ( p_node ) - > name , r_type , p_for_indexing ) ;
2017-03-05 16:44:50 +01:00
} else if ( p_node - > type = = GDParser : : Node : : TYPE_SELF ) {
2014-12-08 19:47:45 +01:00
//eeh...
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
r_type = _get_native_class ( context ) ;
return r_type . type ! = Variant : : NIL ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
} else if ( p_node - > type = = GDParser : : Node : : TYPE_OPERATOR ) {
2014-12-17 02:31:57 +01:00
2014-12-08 19:47:45 +01:00
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( p_node ) ;
2017-03-05 16:44:50 +01:00
if ( op - > op = = GDParser : : OperatorNode : : OP_CALL ) {
2017-08-28 05:03:34 +02:00
2017-03-05 16:44:50 +01:00
if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_TYPE ) {
2014-12-08 19:47:45 +01:00
const GDParser : : TypeNode * tn = static_cast < const GDParser : : TypeNode * > ( op - > arguments [ 0 ] ) ;
2017-03-05 16:44:50 +01:00
r_type . type = tn - > vtype ;
2014-12-08 19:47:45 +01:00
return true ;
2017-03-05 16:44:50 +01:00
} else if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_BUILT_IN_FUNCTION ) {
2014-12-08 19:47:45 +01:00
const GDParser : : BuiltInFunctionNode * bin = static_cast < const GDParser : : BuiltInFunctionNode * > ( op - > arguments [ 0 ] ) ;
2017-03-05 16:44:50 +01:00
return _guess_expression_type ( context , bin , p_line , r_type ) ;
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
} else if ( op - > arguments . size ( ) > 1 & & op - > arguments [ 1 ] - > type = = GDParser : : Node : : TYPE_IDENTIFIER ) {
2014-12-17 02:31:57 +01:00
2017-08-28 05:03:34 +02:00
StringName id = static_cast < const GDParser : : IdentifierNode * > ( op - > arguments [ 1 ] ) - > name ;
if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_IDENTIFIER & & String ( id ) = = " new " ) {
//shortcut
StringName identifier = static_cast < const GDParser : : IdentifierNode * > ( op - > arguments [ 0 ] ) - > name ;
if ( ClassDB : : class_exists ( identifier ) ) {
r_type . type = Variant : : OBJECT ;
r_type . value = Variant ( ) ;
r_type . obj_type = identifier ;
return true ;
}
}
2014-12-08 19:47:45 +01:00
GDCompletionIdentifier base ;
2017-03-05 16:44:50 +01:00
if ( ! _guess_expression_type ( context , op - > arguments [ 0 ] , p_line , base ) )
2014-12-08 19:47:45 +01:00
return false ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( base . type = = Variant : : OBJECT ) {
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
if ( id . operator String ( ) = = " new " & & base . value . get_type ( ) = = Variant : : OBJECT ) {
2017-08-28 05:03:34 +02:00
2014-12-20 19:30:06 +01:00
Object * obj = base . value ;
2017-08-28 05:03:34 +02:00
if ( obj & & Object : : cast_to < GDNativeClass > ( obj ) ) {
GDNativeClass * gdnc = Object : : cast_to < GDNativeClass > ( obj ) ;
2017-03-05 16:44:50 +01:00
r_type . type = Variant : : OBJECT ;
r_type . value = Variant ( ) ;
r_type . obj_type = gdnc - > get_name ( ) ;
2014-12-20 19:30:06 +01:00
return true ;
2017-08-28 05:03:34 +02:00
} else {
if ( base . obj_type ! = StringName ( ) ) {
r_type . type = Variant : : OBJECT ;
r_type . value = Variant ( ) ;
r_type . obj_type = base . obj_type ;
return true ;
}
2014-12-20 19:30:06 +01:00
}
}
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( ClassDB : : has_method ( base . obj_type , id ) ) {
2014-12-17 02:31:57 +01:00
2014-12-08 20:09:29 +01:00
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
MethodBind * mb = ClassDB : : get_method ( base . obj_type , id ) ;
2017-08-24 05:06:56 +02:00
PropertyInfo pi = mb - > get_return_info ( ) ;
2014-12-08 20:09:29 +01:00
2014-12-17 02:31:57 +01:00
//try calling the function if constant and all args are constant, should not crash..
Object * baseptr = base . value ;
2017-03-05 16:44:50 +01:00
if ( mb - > is_const ( ) & & pi . type = = Variant : : OBJECT ) {
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
bool all_valid = true ;
2014-12-17 02:31:57 +01:00
Vector < Variant > args ;
2017-03-05 16:44:50 +01:00
for ( int i = 2 ; i < op - > arguments . size ( ) ; i + + ) {
2014-12-17 02:31:57 +01:00
GDCompletionIdentifier arg ;
2017-03-05 16:44:50 +01:00
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
2014-12-17 02:31:57 +01:00
args . push_back ( arg . value ) ;
} else {
2017-03-05 16:44:50 +01:00
all_valid = false ;
2014-12-17 02:31:57 +01:00
break ;
}
} else {
2017-03-05 16:44:50 +01:00
all_valid = false ;
2014-12-17 02:31:57 +01:00
}
}
2017-03-05 16:44:50 +01:00
if ( all_valid & & String ( id ) = = " get_node " & & ClassDB : : is_parent_class ( base . obj_type , " Node " ) & & args . size ( ) ) {
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
String arg1 = args [ 0 ] ;
2015-06-27 20:52:39 +02:00
if ( arg1 . begins_with ( " /root/ " ) ) {
2017-03-05 16:44:50 +01:00
String which = arg1 . get_slice ( " / " , 2 ) ;
if ( which ! = " " ) {
2015-06-27 20:52:39 +02:00
List < PropertyInfo > props ;
2017-07-19 22:00:46 +02:00
ProjectSettings : : get_singleton ( ) - > get_property_list ( & props ) ;
2015-06-27 20:52:39 +02:00
//print_line("find singleton");
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2015-06-27 20:52:39 +02:00
String s = E - > get ( ) . name ;
if ( ! s . begins_with ( " autoload/ " ) )
continue ;
//print_line("found "+s);
2017-03-05 16:44:50 +01:00
String name = s . get_slice ( " / " , 1 ) ;
2015-06-27 20:52:39 +02:00
//print_line("name: "+name+", which: "+which);
2017-03-05 16:44:50 +01:00
if ( name = = which ) {
2017-07-19 22:00:46 +02:00
String script = ProjectSettings : : get_singleton ( ) - > get ( s ) ;
2014-12-17 02:31:57 +01:00
2015-06-27 20:52:39 +02:00
if ( ! script . begins_with ( " res:// " ) ) {
2017-03-05 16:44:50 +01:00
script = " res:// " + script ;
2015-06-27 20:52:39 +02:00
}
2014-12-17 02:31:57 +01:00
2015-06-27 20:52:39 +02:00
if ( ! script . ends_with ( " .gd " ) ) {
//not a script, try find the script anyway,
//may have some success
2017-03-05 16:44:50 +01:00
script = script . get_basename ( ) + " .gd " ;
2015-06-27 20:52:39 +02:00
}
2014-12-17 02:31:57 +01:00
2015-06-27 20:52:39 +02:00
if ( FileAccess : : exists ( script ) ) {
2014-12-17 02:31:57 +01:00
2015-06-27 20:52:39 +02:00
//print_line("is a script");
Ref < Script > scr ;
if ( ScriptCodeCompletionCache : : get_sigleton ( ) )
scr = ScriptCodeCompletionCache : : get_sigleton ( ) - > get_cached_resource ( script ) ;
else
scr = ResourceLoader : : load ( script ) ;
2017-03-05 16:44:50 +01:00
r_type . obj_type = " Node " ;
r_type . type = Variant : : OBJECT ;
r_type . script = scr ;
r_type . value = Variant ( ) ;
2015-06-27 20:52:39 +02:00
return true ;
}
}
}
2014-12-17 02:31:57 +01:00
}
}
2015-06-27 20:52:39 +02:00
}
if ( baseptr ) {
2014-12-17 02:31:57 +01:00
2015-06-27 20:52:39 +02:00
if ( all_valid ) {
2017-03-05 16:44:50 +01:00
Vector < const Variant * > argptr ;
for ( int i = 0 ; i < args . size ( ) ; i + + ) {
2015-06-27 20:52:39 +02:00
argptr . push_back ( & args [ i ] ) ;
}
Variant : : CallError ce ;
2017-03-05 16:44:50 +01:00
Variant ret = mb - > call ( baseptr , argptr . ptr ( ) , argptr . size ( ) , ce ) ;
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
if ( ce . error = = Variant : : CallError : : CALL_OK & & ret . get_type ( ) ! = Variant : : NIL ) {
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
if ( ret . get_type ( ) ! = Variant : : OBJECT | | ret . operator Object * ( ) ! = NULL ) {
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
r_type = _get_type_from_variant ( ret ) ;
2015-06-27 20:52:39 +02:00
return true ;
}
}
}
2014-12-17 02:31:57 +01:00
}
}
2017-03-05 16:44:50 +01:00
r_type . type = pi . type ;
if ( pi . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
r_type . obj_type = pi . hint_string ;
2014-12-08 19:47:45 +01:00
}
2014-12-17 02:31:57 +01:00
return true ;
2014-12-08 20:09:29 +01:00
# else
return false ;
# endif
2014-12-08 19:47:45 +01:00
} else {
return false ;
}
} else {
//method for some variant..
Variant : : CallError ce ;
2017-03-05 16:44:50 +01:00
Variant v = Variant : : construct ( base . type , NULL , 0 , ce ) ;
2014-12-08 19:47:45 +01:00
List < MethodInfo > mi ;
v . get_method_list ( & mi ) ;
2017-03-05 16:44:50 +01:00
for ( List < MethodInfo > : : Element * E = mi . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-20 19:30:06 +01:00
2017-03-05 16:44:50 +01:00
if ( ! E - > get ( ) . name . begins_with ( " _ " ) & & E - > get ( ) . name = = id . operator String ( ) ) {
2014-12-08 19:47:45 +01:00
MethodInfo mi = E - > get ( ) ;
2017-03-05 16:44:50 +01:00
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 ;
2014-12-08 19:47:45 +01:00
}
return true ;
}
}
}
}
2017-03-05 16:44:50 +01:00
} else if ( op - > op = = GDParser : : OperatorNode : : OP_INDEX | | op - > op = = GDParser : : OperatorNode : : OP_INDEX_NAMED ) {
2014-12-17 02:31:57 +01:00
GDCompletionIdentifier p1 ;
GDCompletionIdentifier p2 ;
2017-03-05 16:44:50 +01:00
if ( op - > op = = GDParser : : OperatorNode : : OP_INDEX_NAMED ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
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 ;
2014-12-17 02:31:57 +01:00
}
} else {
if ( op - > arguments [ 1 ] ) {
2017-03-05 16:44:50 +01:00
if ( ! _guess_expression_type ( context , op - > arguments [ 1 ] , p_line , p2 ) ) {
2014-12-17 02:31:57 +01:00
return false ;
}
}
}
2017-03-05 16:44:50 +01:00
if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_ARRAY ) {
2014-12-17 02:31:57 +01:00
const GDParser : : ArrayNode * an = static_cast < const GDParser : : ArrayNode * > ( op - > arguments [ 0 ] ) ;
if ( p2 . value . is_num ( ) ) {
int index = p2 . value ;
2017-03-05 16:44:50 +01:00
if ( index < 0 | | index > = an - > elements . size ( ) )
2014-12-17 02:31:57 +01:00
return false ;
2017-03-05 16:44:50 +01:00
return _guess_expression_type ( context , an - > elements [ index ] , p_line , r_type ) ;
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
} else if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_DICTIONARY ) {
2014-12-17 02:31:57 +01:00
const GDParser : : DictionaryNode * dn = static_cast < const GDParser : : DictionaryNode * > ( op - > arguments [ 0 ] ) ;
2017-03-05 16:44:50 +01:00
if ( p2 . value . get_type ( ) = = Variant : : NIL )
2014-12-17 02:31:57 +01:00
return false ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < dn - > elements . size ( ) ; i + + ) {
2014-12-17 02:31:57 +01:00
GDCompletionIdentifier k ;
2017-03-05 16:44:50 +01:00
if ( ! _guess_expression_type ( context , dn - > elements [ i ] . key , p_line , k ) ) {
2014-12-17 02:31:57 +01:00
return false ;
}
2017-03-05 16:44:50 +01:00
if ( k . value . get_type ( ) = = Variant : : NIL )
2014-12-17 02:31:57 +01:00
return false ;
2017-03-05 16:44:50 +01:00
if ( k . value = = p2 . value ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
return _guess_expression_type ( context , dn - > elements [ i ] . value , p_line , r_type ) ;
2014-12-17 02:31:57 +01:00
}
}
} else {
if ( op - > arguments [ 0 ] ) {
2017-03-05 16:44:50 +01:00
if ( ! _guess_expression_type ( context , op - > arguments [ 0 ] , p_line , p1 ) ) {
2014-12-17 02:31:57 +01:00
return false ;
}
}
2017-03-05 16:44:50 +01:00
if ( p1 . value . get_type ( ) = = Variant : : OBJECT ) {
2014-12-17 02:31:57 +01:00
//??
2017-08-28 00:03:19 +02:00
2017-08-24 05:06:56 +02:00
if ( p1 . obj_type ! = StringName ( ) & & p2 . type = = Variant : : STRING ) {
2017-08-28 00:03:19 +02:00
StringName base_type = p1 . obj_type ;
if ( p1 . obj_type = = " GDNativeClass " ) {
//native enum
Ref < GDNativeClass > gdn = p1 . value ;
if ( gdn . is_valid ( ) ) {
base_type = gdn - > get_name ( ) ;
}
}
2017-08-24 05:06:56 +02:00
StringName index = p2 . value ;
bool valid ;
2017-08-28 00:03:19 +02:00
Variant : : Type t = ClassDB : : get_property_type ( base_type , index , & valid ) ;
2017-08-24 05:06:56 +02:00
if ( t ! = Variant : : NIL & & valid ) {
r_type . type = t ;
2017-08-28 00:03:19 +02:00
if ( t = = Variant : : INT | | t = = Variant : : OBJECT ) {
2017-08-24 05:06:56 +02:00
//check for enum!
# if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
2017-08-28 00:03:19 +02:00
StringName getter = ClassDB : : get_property_getter ( base_type , index ) ;
2017-08-24 05:06:56 +02:00
if ( getter ! = StringName ( ) ) {
2017-08-28 00:03:19 +02:00
MethodBind * mb = ClassDB : : get_method ( base_type , getter ) ;
2017-08-24 05:06:56 +02:00
if ( mb ) {
PropertyInfo rt = mb - > get_return_info ( ) ;
2017-08-28 00:03:19 +02:00
if ( rt . usage & PROPERTY_USAGE_CLASS_IS_ENUM & & t = = Variant : : INT ) {
2017-08-24 05:06:56 +02:00
r_type . enumeration = rt . class_name ;
2017-08-28 00:03:19 +02:00
} else if ( t = = Variant : : OBJECT ) {
r_type . obj_type = rt . class_name ;
2017-08-24 05:06:56 +02:00
}
}
}
# endif
}
return true ;
}
}
2017-03-05 16:44:50 +01:00
} else if ( p1 . value . get_type ( ) ! = Variant : : NIL ) {
2014-12-17 02:31:57 +01:00
bool valid ;
2017-03-05 16:44:50 +01:00
Variant ret = p1 . value . get ( p2 . value , & valid ) ;
2014-12-17 02:31:57 +01:00
if ( valid ) {
2017-03-05 16:44:50 +01:00
r_type = _get_type_from_variant ( ret ) ;
2014-12-17 02:31:57 +01:00
return true ;
}
} else {
2017-03-05 16:44:50 +01:00
if ( p1 . type ! = Variant : : NIL ) {
2014-12-17 02:31:57 +01:00
Variant : : CallError ce ;
2017-03-05 16:44:50 +01:00
Variant base = Variant : : construct ( p1 . type , NULL , 0 , ce ) ;
2014-12-17 02:31:57 +01:00
bool valid ;
2017-03-05 16:44:50 +01:00
Variant ret = base . get ( p2 . value , & valid ) ;
2014-12-17 02:31:57 +01:00
if ( valid ) {
2017-03-05 16:44:50 +01:00
r_type = _get_type_from_variant ( ret ) ;
2014-12-17 02:31:57 +01:00
return true ;
}
}
}
}
2014-12-08 19:47:45 +01:00
} else {
Variant : : Operator vop = Variant : : OP_MAX ;
2017-03-05 16:44:50 +01:00
switch ( op - > op ) {
case GDParser : : OperatorNode : : OP_ADD : vop = Variant : : OP_ADD ; break ;
case GDParser : : OperatorNode : : OP_SUB : vop = Variant : : OP_SUBSTRACT ; break ;
case GDParser : : OperatorNode : : OP_MUL : vop = Variant : : OP_MULTIPLY ; break ;
case GDParser : : OperatorNode : : OP_DIV : vop = Variant : : OP_DIVIDE ; break ;
case GDParser : : OperatorNode : : OP_MOD : vop = Variant : : OP_MODULE ; break ;
case GDParser : : OperatorNode : : OP_SHIFT_LEFT : vop = Variant : : OP_SHIFT_LEFT ; break ;
case GDParser : : OperatorNode : : OP_SHIFT_RIGHT : vop = Variant : : OP_SHIFT_RIGHT ; break ;
case GDParser : : OperatorNode : : OP_BIT_AND : vop = Variant : : OP_BIT_AND ; break ;
case GDParser : : OperatorNode : : OP_BIT_OR : vop = Variant : : OP_BIT_OR ; break ;
case GDParser : : OperatorNode : : OP_BIT_XOR : vop = Variant : : OP_BIT_XOR ; break ;
default : { }
2014-12-08 19:47:45 +01:00
}
2017-03-05 16:44:50 +01:00
if ( vop = = Variant : : OP_MAX )
2014-12-08 19:47:45 +01:00
return false ;
GDCompletionIdentifier p1 ;
GDCompletionIdentifier p2 ;
if ( op - > arguments [ 0 ] ) {
2017-03-05 16:44:50 +01:00
if ( ! _guess_expression_type ( context , op - > arguments [ 0 ] , p_line , p1 ) ) {
2014-12-17 02:31:57 +01:00
2014-12-08 19:47:45 +01:00
return false ;
2014-12-17 02:31:57 +01:00
}
2014-12-08 19:47:45 +01:00
}
2017-03-05 16:44:50 +01:00
if ( op - > arguments . size ( ) > 1 ) {
if ( ! _guess_expression_type ( context , op - > arguments [ 1 ] , p_line , p2 ) ) {
2014-12-17 02:31:57 +01:00
2014-12-08 19:47:45 +01:00
return false ;
2014-12-17 02:31:57 +01:00
}
2014-12-08 19:47:45 +01:00
}
Variant : : CallError ce ;
2017-03-05 16:44:50 +01: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 19:47:45 +01:00
// avoid potential invalid ops
2017-03-05 16:44:50 +01:00
if ( ( vop = = Variant : : OP_DIVIDE | | vop = = Variant : : OP_MODULE ) & & v2 . get_type ( ) = = Variant : : INT ) {
v2 = 1 ;
v2_use_value = false ;
2014-12-08 19:47:45 +01:00
}
2017-03-05 16:44:50 +01:00
if ( vop = = Variant : : OP_DIVIDE & & v2 . get_type ( ) = = Variant : : REAL ) {
v2 = 1.0 ;
v2_use_value = false ;
2014-12-08 19:47:45 +01:00
}
Variant r ;
bool valid ;
2017-03-05 16:44:50 +01:00
Variant : : evaluate ( vop , v1 , v2 , r , valid ) ;
2014-12-08 19:47:45 +01:00
if ( ! valid )
return false ;
2017-03-05 16:44:50 +01:00
r_type . type = r . get_type ( ) ;
2014-12-17 02:31:57 +01:00
if ( v1_use_value & & v2_use_value )
2017-03-05 16:44:50 +01:00
r_type . value = r ;
2014-12-17 02:31:57 +01:00
2014-12-08 19:47:45 +01:00
return true ;
}
}
return false ;
}
2017-03-05 16:44:50 +01:00
static bool _guess_identifier_type_in_block ( GDCompletionContext & context , int p_line , const StringName & p_identifier , GDCompletionIdentifier & r_type ) {
2014-12-08 19:47:45 +01:00
2017-08-25 05:34:32 +02:00
if ( context . block - > if_condition & & context . block - > if_condition - > type = = GDParser : : Node : : TYPE_OPERATOR & & static_cast < const GDParser : : OperatorNode * > ( context . block - > if_condition ) - > op = = GDParser : : OperatorNode : : OP_IS ) {
//is used, check if identifier is in there! this helps resolve in blocks that are (if (identifier is value)): which are very common..
//super dirty hack, but very useful
//credit: Zylann
//TODO: this could be hacked to detect ANDed conditions too..
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( context . block - > if_condition ) ;
if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_IDENTIFIER & & static_cast < const GDParser : : IdentifierNode * > ( op - > arguments [ 0 ] ) - > name = = p_identifier ) {
//bingo
if ( _guess_expression_type ( context , op - > arguments [ 1 ] , op - > line , r_type ) ) {
return true ;
}
}
}
2017-01-04 21:37:45 +01:00
GDCompletionIdentifier gdi = _get_native_class ( context ) ;
2017-03-05 16:44:50 +01:00
if ( gdi . obj_type ! = StringName ( ) ) {
2017-01-04 21:37:45 +01:00
bool valid ;
2017-03-05 16:44:50 +01:00
Variant : : Type t = ClassDB : : get_property_type ( gdi . obj_type , p_identifier , & valid ) ;
if ( t ! = Variant : : NIL & & valid ) {
r_type . type = t ;
2017-08-24 05:06:56 +02:00
if ( t = = Variant : : INT ) {
//check for enum!
# if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
StringName getter = ClassDB : : get_property_getter ( gdi . obj_type , p_identifier ) ;
if ( getter ! = StringName ( ) ) {
MethodBind * mb = ClassDB : : get_method ( gdi . obj_type , getter ) ;
if ( mb ) {
PropertyInfo rt = mb - > get_return_info ( ) ;
if ( rt . usage & PROPERTY_USAGE_CLASS_IS_ENUM ) {
r_type . enumeration = rt . class_name ;
}
}
}
# endif
}
2017-01-04 21:37:45 +01:00
return true ;
}
}
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
const GDParser : : Node * last_assign = NULL ;
int last_assign_line = - 1 ;
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . block - > statements . size ( ) ; i + + ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( context . block - > statements [ i ] - > line > p_line )
2014-12-17 02:31:57 +01:00
continue ;
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
if ( context . block - > statements [ i ] - > type = = GDParser : : BlockNode : : TYPE_LOCAL_VAR ) {
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
const GDParser : : LocalVarNode * lv = static_cast < const GDParser : : LocalVarNode * > ( context . block - > statements [ i ] ) ;
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
if ( lv - > assign & & lv - > name = = p_identifier ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
last_assign = lv - > assign ;
last_assign_line = context . block - > statements [ i ] - > line ;
2014-12-08 19:47:45 +01:00
}
}
2017-03-05 16:44:50 +01:00
if ( context . block - > statements [ i ] - > type = = GDParser : : BlockNode : : TYPE_OPERATOR ) {
2014-12-17 02:31:57 +01:00
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( context . block - > statements [ i ] ) ;
2017-03-05 16:44:50 +01:00
if ( op - > op = = GDParser : : OperatorNode : : OP_ASSIGN ) {
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
if ( op - > arguments . size ( ) & & op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_IDENTIFIER ) {
2014-12-17 02:31:57 +01:00
2014-12-08 19:47:45 +01:00
const GDParser : : IdentifierNode * id = static_cast < const GDParser : : IdentifierNode * > ( op - > arguments [ 0 ] ) ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( id - > name = = p_identifier ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
last_assign = op - > arguments [ 1 ] ;
last_assign_line = context . block - > statements [ i ] - > line ;
2014-12-08 19:47:45 +01:00
}
}
}
}
}
//use the last assignment, (then backwards?)
if ( last_assign ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
return _guess_expression_type ( context , last_assign , last_assign_line , r_type ) ;
2014-12-08 19:47:45 +01:00
}
return false ;
}
2017-03-05 16:44:50 +01:00
static bool _guess_identifier_from_assignment_in_function ( GDCompletionContext & context , int p_src_line , const StringName & p_identifier , const StringName & p_function , GDCompletionIdentifier & r_type ) {
2015-08-30 16:50:10 +02:00
2017-03-05 16:44:50 +01:00
const GDParser : : FunctionNode * func = NULL ;
for ( int i = 0 ; i < context . _class - > functions . size ( ) ; i + + ) {
if ( context . _class - > functions [ i ] - > name = = p_function ) {
func = context . _class - > functions [ i ] ;
2015-08-30 16:50:10 +02:00
break ;
}
}
if ( ! func )
return false ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < func - > body - > statements . size ( ) ; i + + ) {
2015-08-30 16:50:10 +02:00
2016-06-18 02:57:46 +02:00
if ( func - > body - > statements [ i ] - > line = = p_src_line ) {
2016-06-14 03:19:39 +02:00
break ;
}
2015-08-30 16:50:10 +02:00
2017-03-05 16:44:50 +01:00
if ( func - > body - > statements [ i ] - > type = = GDParser : : BlockNode : : TYPE_OPERATOR ) {
2015-08-30 16:50:10 +02:00
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( func - > body - > statements [ i ] ) ;
2017-03-05 16:44:50 +01:00
if ( op - > op = = GDParser : : OperatorNode : : OP_ASSIGN ) {
2015-08-30 16:50:10 +02:00
2017-03-05 16:44:50 +01:00
if ( op - > arguments . size ( ) & & op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_IDENTIFIER ) {
2015-08-30 16:50:10 +02:00
const GDParser : : IdentifierNode * id = static_cast < const GDParser : : IdentifierNode * > ( op - > arguments [ 0 ] ) ;
2017-03-05 16:44:50 +01:00
if ( id - > name = = p_identifier ) {
2015-08-30 16:50:10 +02:00
2017-03-05 16:44:50 +01:00
return _guess_expression_type ( context , op - > arguments [ 1 ] , func - > body - > statements [ i ] - > line , r_type ) ;
2015-08-30 16:50:10 +02:00
}
}
}
}
}
return false ;
}
2017-08-28 00:03:19 +02:00
static bool _guess_identifier_type ( GDCompletionContext & context , int p_line , const StringName & p_identifier , GDCompletionIdentifier & r_type , bool p_for_indexing ) {
2014-12-08 19:47:45 +01:00
2014-12-17 02:31:57 +01:00
//go to block first
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const GDParser : : BlockNode * block = context . block ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( block ) {
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
GDCompletionContext c = context ;
2017-03-05 16:44:50 +01:00
c . block = block ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( _guess_identifier_type_in_block ( c , p_line , p_identifier , r_type ) ) {
2014-12-17 02:31:57 +01:00
return true ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
block = block - > parent_block ;
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
2014-12-20 19:30:06 +01:00
//guess from argument if virtual
2017-03-05 16:44:50 +01:00
if ( context . function & & context . function - > name ! = StringName ( ) ) {
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
int argindex = - 1 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . function - > arguments . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( context . function - > arguments [ i ] = = p_identifier ) {
argindex = i ;
2014-12-17 02:31:57 +01:00
break ;
2014-02-10 02:10:30 +01:00
}
}
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
if ( argindex ! = - 1 ) {
GDCompletionIdentifier id = _get_native_class ( context ) ;
if ( id . type = = Variant : : OBJECT & & id . obj_type ! = StringName ( ) ) {
2014-12-17 02:31:57 +01:00
//this kinda sucks but meh
2014-12-08 19:47:45 +01:00
2014-12-17 02:31:57 +01:00
List < MethodInfo > vmethods ;
2017-03-05 16:44:50 +01:00
ClassDB : : get_virtual_methods ( id . obj_type , & vmethods ) ;
for ( List < MethodInfo > : : Element * E = vmethods . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( E - > get ( ) . name = = context . function - > name & & argindex < E - > get ( ) . arguments . size ( ) ) {
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
PropertyInfo arg = E - > get ( ) . arguments [ argindex ] ;
2014-02-10 02:10:30 +01:00
2017-08-24 00:10:32 +02:00
int scp = String ( arg . name ) . find ( " : " ) ;
2017-03-05 16:44:50 +01:00
if ( scp ! = - 1 ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
r_type . type = Variant : : OBJECT ;
2017-08-24 00:10:32 +02:00
r_type . obj_type = String ( arg . name ) . substr ( scp + 1 , String ( arg . name ) . length ( ) ) ;
2014-12-17 02:31:57 +01:00
return true ;
} else {
2017-03-05 16:44:50 +01:00
r_type . type = arg . type ;
if ( arg . hint = = PROPERTY_HINT_RESOURCE_TYPE )
r_type . obj_type = arg . hint_string ;
2014-12-17 02:31:57 +01:00
return true ;
}
}
}
}
2014-02-10 02:10:30 +01:00
}
}
2014-12-17 02:31:57 +01:00
//guess type in constant
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > constant_expressions . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( context . _class - > constant_expressions [ i ] . identifier = = p_identifier ) {
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01: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 ) ;
2014-12-17 02:31:57 +01:00
return true ;
}
}
2014-12-08 19:47:45 +01:00
2014-12-20 19:30:06 +01:00
if ( ! ( context . function & & context . function - > _static ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > variables . size ( ) ; i + + ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( context . _class - > variables [ i ] . identifier = = p_identifier ) {
2014-12-08 19:47:45 +01:00
2017-03-05 16:44:50 +01:00
if ( context . _class - > variables [ i ] . _export . type ! = Variant : : NIL ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
r_type = _get_type_from_pinfo ( context . _class - > variables [ i ] . _export ) ;
2014-12-17 02:31:57 +01:00
return true ;
} else if ( context . _class - > variables [ i ] . expression ) {
2015-08-30 16:50:10 +02:00
2017-03-05 16:44:50 +01:00
bool rtype = _guess_expression_type ( context , context . _class - > variables [ i ] . expression , context . _class - > variables [ i ] . line , r_type ) ;
if ( rtype & & r_type . type ! = Variant : : NIL )
2015-08-30 16:50:10 +02:00
return true ;
//return _guess_expression_type(context,context._class->variables[i].expression,context._class->variables[i].line,r_type);
2014-02-10 02:10:30 +01:00
}
2015-08-30 16:50:10 +02:00
2017-03-24 21:45:31 +01:00
//try to guess from assignment in constructor or _ready
2017-03-05 16:44:50 +01:00
if ( _guess_identifier_from_assignment_in_function ( context , p_line + 1 , p_identifier , " _ready " , r_type ) )
2015-08-30 16:50:10 +02:00
return true ;
2017-03-05 16:44:50 +01:00
if ( _guess_identifier_from_assignment_in_function ( context , p_line + 1 , p_identifier , " _enter_tree " , r_type ) )
2015-08-30 16:50:10 +02:00
return true ;
2017-03-05 16:44:50 +01:00
if ( _guess_identifier_from_assignment_in_function ( context , p_line + 1 , p_identifier , " _init " , r_type ) )
2015-08-30 16:50:10 +02:00
return true ;
return false ;
2014-02-10 02:10:30 +01:00
}
}
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
2015-12-28 20:36:29 +01:00
//autoloads as singletons
List < PropertyInfo > props ;
2017-07-19 22:00:46 +02:00
ProjectSettings : : get_singleton ( ) - > get_property_list ( & props ) ;
2014-12-17 03:17:35 +01:00
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-08 19:47:45 +01:00
2015-12-28 20:36:29 +01:00
String s = E - > get ( ) . name ;
if ( ! s . begins_with ( " autoload/ " ) )
continue ;
2017-03-05 16:44:50 +01:00
String name = s . get_slice ( " / " , 1 ) ;
if ( name = = String ( p_identifier ) ) {
2015-12-28 20:36:29 +01:00
2017-07-19 22:00:46 +02:00
String path = ProjectSettings : : get_singleton ( ) - > get ( s ) ;
2015-12-28 20:36:29 +01:00
if ( path . begins_with ( " * " ) ) {
2017-03-05 16:44:50 +01:00
String script = path . substr ( 1 , path . length ( ) ) ;
2015-12-28 20:36:29 +01:00
if ( ! script . ends_with ( " .gd " ) ) {
//not a script, try find the script anyway,
//may have some success
2017-03-05 16:44:50 +01:00
script = script . get_basename ( ) + " .gd " ;
2015-12-28 20:36:29 +01:00
}
if ( FileAccess : : exists ( script ) ) {
//print_line("is a script");
Ref < Script > scr ;
if ( ScriptCodeCompletionCache : : get_sigleton ( ) )
scr = ScriptCodeCompletionCache : : get_sigleton ( ) - > get_cached_resource ( script ) ;
else
scr = ResourceLoader : : load ( script ) ;
2017-03-05 16:44:50 +01:00
r_type . obj_type = " Node " ;
r_type . type = Variant : : OBJECT ;
r_type . script = scr ;
r_type . value = Variant ( ) ;
2015-12-28 20:36:29 +01:00
return true ;
}
}
}
}
//global
2017-03-05 16:44:50 +01: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 02:10:30 +01:00
2017-08-28 00:03:19 +02:00
r_type = _get_type_from_variant ( GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ E - > get ( ) ] , ! p_for_indexing ) ;
2014-12-17 02:31:57 +01:00
return true ;
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
return false ;
}
2017-03-05 16:44:50 +01:00
static void _find_identifiers_in_block ( GDCompletionContext & context , int p_line , bool p_only_functions , Set < String > & result ) {
2014-12-17 02:31:57 +01:00
if ( p_only_functions )
return ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . block - > statements . size ( ) ; i + + ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( context . block - > statements [ i ] - > line > p_line )
2014-12-17 02:31:57 +01:00
continue ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( context . block - > statements [ i ] - > type = = GDParser : : BlockNode : : TYPE_LOCAL_VAR ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const GDParser : : LocalVarNode * lv = static_cast < const GDParser : : LocalVarNode * > ( context . block - > statements [ i ] ) ;
2014-12-17 02:31:57 +01:00
result . insert ( lv - > name . operator String ( ) ) ;
2014-02-10 02:10:30 +01:00
}
}
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
static void _find_identifiers_in_class ( GDCompletionContext & context , bool p_static , bool p_only_functions , Set < String > & result ) {
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
if ( ! p_static & & ! p_only_functions ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > variables . size ( ) ; i + + ) {
2014-12-17 02:31:57 +01:00
result . insert ( context . _class - > variables [ i ] . identifier ) ;
2014-02-10 02:10:30 +01:00
}
}
2014-12-17 02:31:57 +01:00
if ( ! p_only_functions ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > constant_expressions . size ( ) ; i + + ) {
2014-12-17 02:31:57 +01:00
result . insert ( context . _class - > constant_expressions [ i ] . identifier ) ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > subclasses . size ( ) ; i + + ) {
2014-12-17 02:31:57 +01:00
result . insert ( context . _class - > subclasses [ i ] - > name ) ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > static_functions . size ( ) ; i + + ) {
2014-12-17 02:31:57 +01:00
if ( context . _class - > static_functions [ i ] - > arguments . size ( ) )
2017-03-05 16:44:50 +01:00
result . insert ( context . _class - > static_functions [ i ] - > name . operator String ( ) + " ( " ) ;
2014-12-17 02:31:57 +01:00
else
2017-03-05 16:44:50 +01:00
result . insert ( context . _class - > static_functions [ i ] - > name . operator String ( ) + " () " ) ;
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
if ( ! p_static ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > functions . size ( ) ; i + + ) {
2014-12-17 02:31:57 +01:00
if ( context . _class - > functions [ i ] - > arguments . size ( ) )
2017-03-05 16:44:50 +01:00
result . insert ( context . _class - > functions [ i ] - > name . operator String ( ) + " ( " ) ;
2014-12-17 02:31:57 +01:00
else
2017-03-05 16:44:50 +01:00
result . insert ( context . _class - > functions [ i ] - > name . operator String ( ) + " () " ) ;
2014-12-17 02:31:57 +01:00
}
}
2014-02-10 02:10:30 +01:00
2014-12-17 03:17:35 +01:00
//globals
2014-12-17 02:31:57 +01:00
Ref < Reference > base = _get_parent_class ( context ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
Ref < GDScript > script = base ;
Ref < GDNativeClass > nc = base ;
if ( script . is_valid ( ) ) {
2014-12-08 19:47:45 +01:00
2014-12-17 02:31:57 +01:00
if ( ! p_static & & ! p_only_functions ) {
2017-03-05 16:44:50 +01:00
for ( const Set < StringName > : : Element * E = script - > get_members ( ) . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-17 02:31:57 +01:00
result . insert ( E - > get ( ) . operator String ( ) ) ;
}
}
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
if ( ! p_only_functions ) {
2017-03-05 16:44:50 +01:00
for ( const Map < StringName , Variant > : : Element * E = script - > get_constants ( ) . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-17 02:31:57 +01:00
result . insert ( E - > key ( ) . operator String ( ) ) ;
}
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( const Map < StringName , GDFunction * > : : Element * E = script - > get_member_functions ( ) . front ( ) ; E ; E = E - > next ( ) ) {
2016-05-22 02:18:16 +02:00
if ( ! p_static | | E - > get ( ) - > is_static ( ) ) {
if ( E - > get ( ) - > get_argument_count ( ) )
2017-03-05 16:44:50 +01:00
result . insert ( E - > key ( ) . operator String ( ) + " ( " ) ;
2014-12-17 02:31:57 +01:00
else
2017-03-05 16:44:50 +01:00
result . insert ( E - > key ( ) . operator String ( ) + " () " ) ;
2014-12-17 02:31:57 +01:00
}
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( ! p_only_functions ) {
for ( const Map < StringName , Ref < GDScript > > : : Element * E = script - > get_subclasses ( ) . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-17 02:31:57 +01:00
result . insert ( E - > key ( ) . operator String ( ) ) ;
2014-02-10 02:10:30 +01:00
}
}
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
base = script - > get_base ( ) ;
2014-12-17 02:31:57 +01:00
if ( base . is_null ( ) )
2017-03-05 16:44:50 +01:00
base = script - > get_native ( ) ;
2014-12-17 02:31:57 +01:00
} else if ( nc . is_valid ( ) ) {
2017-01-04 21:37:45 +01:00
StringName type = nc - > get_name ( ) ;
2014-12-17 02:31:57 +01:00
if ( ! p_only_functions ) {
List < String > constants ;
2017-03-05 16:44:50 +01:00
ClassDB : : get_integer_constant_list ( type , & constants ) ;
for ( List < String > : : Element * E = constants . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-17 02:31:57 +01:00
result . insert ( E - > get ( ) ) ;
}
2017-01-04 21:37:45 +01:00
List < PropertyInfo > pinfo ;
2017-03-05 16:44:50 +01:00
ClassDB : : get_property_list ( type , & pinfo ) ;
2017-01-04 21:37:45 +01:00
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = pinfo . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . usage & ( PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY ) )
2014-12-20 19:30:06 +01:00
continue ;
2017-08-24 00:10:32 +02:00
if ( String ( E - > get ( ) . name ) . find ( " / " ) ! = - 1 )
2017-01-04 21:37:45 +01:00
continue ;
result . insert ( E - > get ( ) . name ) ;
2014-12-17 02:31:57 +01:00
}
}
2017-01-04 21:37:45 +01:00
List < MethodInfo > methods ;
2017-06-23 20:10:46 +02:00
ClassDB : : get_method_list ( type , & methods , false , true ) ;
2017-03-05 16:44:50 +01:00
for ( List < MethodInfo > : : Element * E = methods . front ( ) ; E ; E = E - > next ( ) ) {
2017-01-04 21:37:45 +01:00
if ( E - > get ( ) . name . begins_with ( " _ " ) )
continue ;
if ( E - > get ( ) . arguments . size ( ) )
2017-03-05 16:44:50 +01:00
result . insert ( E - > get ( ) . name + " ( " ) ;
2017-01-04 21:37:45 +01:00
else
2017-03-05 16:44:50 +01:00
result . insert ( E - > get ( ) . name + " () " ) ;
2017-01-04 21:37:45 +01:00
}
2014-12-17 02:31:57 +01:00
break ;
} else
break ;
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
static void _find_identifiers ( GDCompletionContext & context , int p_line , bool p_only_functions , Set < String > & result ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const GDParser : : BlockNode * block = context . block ;
2014-12-17 02:31:57 +01:00
2015-11-26 13:40:13 +01:00
if ( context . function ) {
2017-03-05 16:44:50 +01:00
const GDParser : : FunctionNode * f = context . function ;
2015-11-26 13:40:13 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < f - > arguments . size ( ) ; i + + ) {
2015-11-26 13:40:13 +01:00
result . insert ( f - > arguments [ i ] . operator String ( ) ) ;
}
}
2017-03-05 16:44:50 +01:00
while ( block ) {
2014-12-17 02:31:57 +01:00
GDCompletionContext c = context ;
2017-03-05 16:44:50 +01:00
c . block = block ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
_find_identifiers_in_block ( c , p_line , p_only_functions , result ) ;
block = block - > parent_block ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
const GDParser : : ClassNode * clss = context . _class ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
bool _static = context . function & & context . function - > _static ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( clss ) {
2014-12-17 02:31:57 +01:00
GDCompletionContext c = context ;
2017-03-05 16:44:50 +01:00
c . _class = clss ;
c . block = NULL ;
c . function = NULL ;
_find_identifiers_in_class ( c , _static , p_only_functions , result ) ;
clss = clss - > owner ;
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < GDFunctions : : FUNC_MAX ; i + + ) {
2014-12-17 02:31:57 +01:00
result . insert ( GDFunctions : : get_func_name ( GDFunctions : : Function ( i ) ) ) ;
}
2017-03-05 16:44:50 +01:00
static const char * _type_names [ Variant : : VARIANT_MAX ] = {
" null " , " bool " , " int " , " float " , " String " , " Vector2 " , " Rect2 " , " Vector3 " , " Transform2D " , " Plane " , " Quat " , " AABB " , " Basis " , " Transform " ,
2017-07-20 11:51:15 +02:00
" Color " , " NodePath " , " RID " , " Object " , " Dictionary " , " Array " , " PoolByteArray " , " PoolIntArray " , " PoolRealArray " , " PoolStringArray " ,
" PoolVector2Array " , " PoolVector3Array " , " PoolColorArray "
2017-03-05 16:44:50 +01:00
} ;
2014-12-17 03:17:35 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < Variant : : VARIANT_MAX ; i + + ) {
2014-12-17 03:17:35 +01:00
result . insert ( _type_names [ i ] ) ;
}
2015-12-28 20:36:29 +01:00
//autoload singletons
List < PropertyInfo > props ;
2017-07-19 22:00:46 +02:00
ProjectSettings : : get_singleton ( ) - > get_property_list ( & props ) ;
2015-12-28 20:36:29 +01:00
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2015-12-28 20:36:29 +01:00
String s = E - > get ( ) . name ;
if ( ! s . begins_with ( " autoload/ " ) )
continue ;
2017-03-05 16:44:50 +01:00
String name = s . get_slice ( " / " , 1 ) ;
2017-07-19 22:00:46 +02:00
String path = ProjectSettings : : get_singleton ( ) - > get ( s ) ;
2015-12-28 20:36:29 +01:00
if ( path . begins_with ( " * " ) ) {
result . insert ( name ) ;
}
}
2017-03-05 16:44:50 +01:00
for ( const Map < StringName , int > : : Element * E = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-17 02:31:57 +01:00
result . insert ( E - > key ( ) . operator String ( ) ) ;
}
}
2017-03-05 16:44:50 +01:00
static String _get_visual_datatype ( const PropertyInfo & p_info , bool p_isarg = true ) {
2014-12-17 02:31:57 +01:00
String n = p_info . name ;
int idx = n . find ( " : " ) ;
2017-03-05 16:44:50 +01:00
if ( idx ! = - 1 ) {
return n . substr ( idx + 1 , n . length ( ) ) ;
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
if ( p_info . type = = Variant : : OBJECT & & p_info . hint = = PROPERTY_HINT_RESOURCE_TYPE )
2014-12-17 02:31:57 +01:00
return p_info . hint_string ;
2017-03-05 16:44:50 +01:00
if ( p_info . type = = Variant : : NIL ) {
2014-12-20 19:30:06 +01:00
if ( p_isarg )
return " var " ;
else
return " void " ;
}
2014-12-17 02:31:57 +01:00
return Variant : : get_type_name ( p_info . type ) ;
}
2017-03-05 16:44:50 +01:00
static void _make_function_hint ( const GDParser : : FunctionNode * p_func , int p_argidx , String & arghint ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
arghint = " func " + p_func - > name + " ( " ;
for ( int i = 0 ; i < p_func - > arguments . size ( ) ; i + + ) {
if ( i > 0 )
arghint + = " , " ;
2014-12-17 02:31:57 +01:00
else
2017-03-05 16:44:50 +01:00
arghint + = " " ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
arghint + = p_func - > arguments [ i ] . operator String ( ) ;
int deffrom = p_func - > arguments . size ( ) - p_func - > default_values . size ( ) ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( i > = deffrom ) {
int defidx = deffrom - i ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( defidx > = 0 & & defidx < p_func - > default_values . size ( ) ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( p_func - > default_values [ defidx ] - > type = = GDParser : : Node : : TYPE_OPERATOR ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
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 ( ) ;
2014-12-17 02:31:57 +01:00
}
} else {
2014-02-10 02:10:30 +01:00
}
}
}
2017-03-05 16:44:50 +01:00
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
if ( p_func - > arguments . size ( ) > 0 )
arghint + = " " ;
arghint + = " ) " ;
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
2017-04-16 21:09:17 +02:00
void get_directory_contents ( EditorFileSystemDirectory * p_dir , Set < String > & r_list ) {
for ( int i = 0 ; i < p_dir - > get_subdir_count ( ) ; i + + ) {
get_directory_contents ( p_dir - > get_subdir ( i ) , r_list ) ;
}
for ( int i = 0 ; i < p_dir - > get_file_count ( ) ; i + + ) {
r_list . insert ( " \" " + p_dir - > get_file_path ( i ) + " \" " ) ;
}
}
2017-08-24 05:06:56 +02:00
static void _find_type_arguments ( GDCompletionContext & context , const GDParser : : Node * p_node , int p_line , const StringName & p_method , const GDCompletionIdentifier & id , int p_argidx , Set < String > & result , bool & r_forced , String & arghint ) {
2014-02-10 02:10:30 +01:00
2015-06-27 20:52:39 +02:00
//print_line("find type arguments?");
2017-05-20 17:38:03 +02:00
if ( id . type = = Variant : : OBJECT & & id . obj_type ! = StringName ( ) ) {
2015-01-03 17:03:13 +01:00
2017-03-05 16:44:50 +01:00
MethodBind * m = ClassDB : : get_method ( id . obj_type , p_method ) ;
2015-06-27 20:52:39 +02:00
if ( ! m ) {
//not in static method, see script
//print_line("not in static: "+String(p_method));
Ref < GDScript > on_script ;
if ( id . value . get_type ( ) ) {
2017-03-05 16:44:50 +01:00
Object * obj = id . value ;
2015-06-27 20:52:39 +02:00
2017-08-24 22:58:51 +02:00
GDScript * scr = Object : : cast_to < GDScript > ( obj ) ;
if ( scr ) {
while ( scr ) {
2015-06-27 20:52:39 +02:00
2017-08-24 22:58:51 +02:00
for ( const Map < StringName , GDFunction * > : : Element * E = scr - > get_member_functions ( ) . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) - > is_static ( ) & & p_method = = E - > get ( ) - > get_name ( ) ) {
arghint = " static func " + String ( p_method ) + " ( " ;
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 + = " var " + 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 ( ) ;
2015-06-27 20:52:39 +02:00
}
}
2017-08-24 22:58:51 +02:00
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
2015-06-27 20:52:39 +02:00
}
2017-08-24 22:58:51 +02:00
arghint + = " ) " ;
return ; //found
2015-06-27 20:52:39 +02:00
}
}
2017-08-24 22:58:51 +02:00
if ( scr - > get_base ( ) . is_valid ( ) )
scr = scr - > get_base ( ) . ptr ( ) ;
else
scr = NULL ;
2015-06-27 20:52:39 +02:00
}
2017-08-24 22:58:51 +02:00
} else {
2017-08-26 22:53:28 +02:00
if ( obj ) {
on_script = obj - > get_script ( ) ;
}
2015-06-27 20:52:39 +02:00
}
}
//print_line("but it has a script?");
if ( ! on_script . is_valid ( ) & & id . script . is_valid ( ) ) {
//print_line("yes");
2017-03-05 16:44:50 +01:00
on_script = id . script ;
2015-06-27 20:52:39 +02:00
}
if ( on_script . is_valid ( ) ) {
2014-02-10 02:10:30 +01:00
2015-06-27 20:52:39 +02:00
GDScript * scr = on_script . ptr ( ) ;
if ( scr ) {
while ( scr ) {
2014-12-17 02:31:57 +01:00
2015-06-27 20:52:39 +02:00
String code = scr - > get_source_code ( ) ;
//print_line("has source code!");
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( code ! = " " ) {
2015-06-27 20:52:39 +02:00
//if there is code, parse it. This way is slower but updates in real-time
GDParser p ;
//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);
2017-03-05 16:44:50 +01:00
Error err = p . parse ( scr - > get_source_code ( ) , scr - > get_path ( ) . get_base_dir ( ) , true , " " , false ) ;
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
if ( err = = OK ) {
2015-06-27 20:52:39 +02:00
//print_line("checking the functions...");
//only if ok, otherwise use what is cached on the script
//GDParser::ClassNode *base = p.
const GDParser : : Node * root = p . get_parse_tree ( ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( root - > type ! = GDParser : : Node : : TYPE_CLASS ) ;
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
const GDParser : : ClassNode * cl = static_cast < const GDParser : : ClassNode * > ( root ) ;
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
const GDParser : : FunctionNode * func = NULL ;
bool st = false ;
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < cl - > functions . size ( ) ; i + + ) {
2015-06-27 20:52:39 +02:00
//print_line(String(cl->functions[i]->name)+" vs "+String(p_method));
2017-03-05 16:44:50 +01:00
if ( cl - > functions [ i ] - > name = = p_method ) {
func = cl - > functions [ i ] ;
2015-06-27 20:52:39 +02:00
}
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < cl - > static_functions . size ( ) ; i + + ) {
2015-06-27 20:52:39 +02:00
//print_line(String(cl->static_functions[i]->name)+" vs "+String(p_method));
2017-03-05 16:44:50 +01:00
if ( cl - > static_functions [ i ] - > name = = p_method ) {
func = cl - > static_functions [ i ] ;
st = true ;
2015-06-27 20:52:39 +02:00
}
}
if ( func ) {
2017-03-05 16:44:50 +01:00
arghint = " func " + String ( p_method ) + " ( " ;
2015-06-27 20:52:39 +02:00
if ( st )
2017-03-05 16:44:50 +01:00
arghint = " static " + arghint ;
for ( int i = 0 ; i < func - > arguments . size ( ) ; i + + ) {
if ( i > 0 )
arghint + = " , " ;
2015-06-27 20:52:39 +02:00
else
2017-03-05 16:44:50 +01:00
arghint + = " " ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2015-06-27 20:52:39 +02:00
}
2017-03-05 16:44:50 +01:00
arghint + = " var " + String ( func - > arguments [ i ] ) ;
int deffrom = func - > arguments . size ( ) - func - > default_values . size ( ) ;
if ( i > = deffrom ) {
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
int defidx = deffrom - i ;
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
if ( defidx > = 0 & & defidx < func - > default_values . size ( ) & & func - > default_values [ defidx ] - > type = = GDParser : : Node : : TYPE_OPERATOR ) {
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( 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 ( ) ;
2015-06-27 20:52:39 +02:00
}
}
}
2017-03-05 16:44:50 +01:00
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2015-06-27 20:52:39 +02:00
}
}
2017-03-05 16:44:50 +01:00
arghint + = " ) " ;
2015-06-27 20:52:39 +02:00
return ;
}
} else {
//print_line("failed parsing?");
2017-03-05 16:44:50 +01:00
code = " " ;
2015-06-27 20:52:39 +02:00
}
}
2017-03-05 16:44:50 +01:00
if ( code = = " " ) {
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
for ( const Map < StringName , GDFunction * > : : Element * E = scr - > get_member_functions ( ) . front ( ) ; E ; E = E - > next ( ) ) {
if ( p_method = = E - > get ( ) - > get_name ( ) ) {
arghint = " func " + String ( p_method ) + " ( " ;
for ( int i = 0 ; i < E - > get ( ) - > get_argument_count ( ) ; i + + ) {
if ( i > 0 )
arghint + = " , " ;
2015-06-27 20:52:39 +02:00
else
2017-03-05 16:44:50 +01:00
arghint + = " " ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2015-06-27 20:52:39 +02:00
}
2017-03-05 16:44:50 +01:00
arghint + = " var " + 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 ( ) ;
2015-06-27 20:52:39 +02:00
}
}
2017-03-05 16:44:50 +01:00
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2015-06-27 20:52:39 +02:00
}
}
2017-03-05 16:44:50 +01:00
arghint + = " ) " ;
2015-06-27 20:52:39 +02:00
return ; //found
}
}
}
if ( scr - > get_base ( ) . is_valid ( ) )
2017-03-05 16:44:50 +01:00
scr = scr - > get_base ( ) . ptr ( ) ;
2015-06-27 20:52:39 +02:00
else
2017-03-05 16:44:50 +01:00
scr = NULL ;
2015-06-27 20:52:39 +02:00
}
2014-02-10 02:10:30 +01:00
}
}
2014-12-17 02:31:57 +01:00
} else {
2017-08-24 05:06:56 +02:00
//regular method
# if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
if ( p_argidx < m - > get_argument_count ( ) ) {
PropertyInfo pi = m - > get_argument_info ( p_argidx ) ;
if ( pi . usage & PROPERTY_USAGE_CLASS_IS_ENUM ) {
String enumeration = pi . class_name ;
if ( enumeration . find ( " . " ) ! = - 1 ) {
//class constant
List < StringName > constants ;
String cls = enumeration . get_slice ( " . " , 0 ) ;
String enm = enumeration . get_slice ( " . " , 1 ) ;
ClassDB : : get_enum_constants ( cls , enm , & constants ) ;
//constants.sort_custom<StringName::AlphCompare>();
for ( List < StringName > : : Element * E = constants . front ( ) ; E ; E = E - > next ( ) ) {
String add = cls + " . " + E - > get ( ) ;
result . insert ( add ) ;
r_forced = true ;
}
} else {
2015-06-27 20:52:39 +02:00
2017-08-24 05:06:56 +02:00
//global constant
StringName current_enum = enumeration ;
for ( int i = 0 ; i < GlobalConstants : : get_global_constant_count ( ) ; i + + ) {
if ( GlobalConstants : : get_global_constant_enum ( i ) = = current_enum ) {
result . insert ( GlobalConstants : : get_global_constant_name ( i ) ) ;
r_forced = true ;
}
}
//global
}
}
}
# endif
2017-06-20 22:13:17 +02:00
if ( p_method . operator String ( ) = = " connect " | | ( p_method . operator String ( ) = = " emit_signal " & & p_argidx = = 0 ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( p_argidx = = 0 ) {
2015-06-27 20:52:39 +02:00
List < MethodInfo > sigs ;
2017-03-05 16:44:50 +01:00
ClassDB : : get_signal_list ( id . obj_type , & sigs ) ;
2016-08-19 21:48:08 +02:00
if ( id . script . is_valid ( ) ) {
id . script - > get_script_signal_list ( & sigs ) ;
2017-03-05 16:44:50 +01:00
} else if ( id . value . get_type ( ) = = Variant : : OBJECT ) {
2016-08-19 21:48:08 +02:00
Object * obj = id . value ;
if ( obj & & ! obj - > get_script ( ) . is_null ( ) ) {
2017-03-05 16:44:50 +01:00
Ref < Script > scr = obj - > get_script ( ) ;
2016-08-19 21:48:08 +02:00
if ( scr . is_valid ( ) ) {
scr - > get_script_signal_list ( & sigs ) ;
}
}
}
2017-03-05 16:44:50 +01:00
for ( List < MethodInfo > : : Element * E = sigs . front ( ) ; E ; E = E - > next ( ) ) {
result . insert ( " \" " + E - > get ( ) . name + " \" " ) ;
2017-08-24 05:06:56 +02:00
r_forced = true ;
2015-06-27 20:52:39 +02:00
}
2016-08-19 21:48:08 +02:00
2017-03-05 16:44:50 +01:00
} else if ( p_argidx = = 2 ) {
2016-08-19 21:48:08 +02:00
if ( context . _class ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > functions . size ( ) ; i + + ) {
result . insert ( " \" " + context . _class - > functions [ i ] - > name + " \" " ) ;
2017-08-24 05:06:56 +02:00
r_forced = true ;
2016-08-19 21:48:08 +02:00
}
}
2015-06-27 20:52:39 +02:00
}
/*if (p_argidx==2) {
ERR_FAIL_COND ( p_node - > type ! = GDParser : : Node : : TYPE_OPERATOR ) ;
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( p_node ) ;
if ( op - > arguments . size ( ) > )
} */
} else {
2017-03-05 16:44:50 +01:00
if ( p_argidx = = 0 & & ( String ( p_method ) = = " get_node " | | String ( p_method ) = = " has_node " ) & & ClassDB : : is_parent_class ( id . obj_type , " Node " ) ) {
2015-06-26 06:14:31 +02:00
List < PropertyInfo > props ;
2017-07-19 22:00:46 +02:00
ProjectSettings : : get_singleton ( ) - > get_property_list ( & props ) ;
2015-06-26 06:14:31 +02:00
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2015-06-26 06:14:31 +02:00
String s = E - > get ( ) . name ;
if ( ! s . begins_with ( " autoload/ " ) )
continue ;
2017-01-14 12:26:56 +01:00
//print_line("found "+s);
2017-03-05 16:44:50 +01:00
String name = s . get_slice ( " / " , 1 ) ;
result . insert ( " \" /root/ " + name + " \" " ) ;
2017-08-24 05:06:56 +02:00
r_forced = true ;
2015-06-26 06:14:31 +02:00
}
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Object * obj = id . value ;
2015-06-27 20:52:39 +02:00
if ( obj ) {
List < String > options ;
2017-03-05 16:44:50 +01:00
obj - > get_argument_options ( p_method , p_argidx , & options ) ;
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
for ( List < String > : : Element * E = options . front ( ) ; E ; E = E - > next ( ) ) {
2015-06-27 20:52:39 +02:00
result . insert ( E - > get ( ) ) ;
2017-08-24 05:06:56 +02:00
r_forced = true ;
2015-06-27 20:52:39 +02:00
}
2014-12-17 02:31:57 +01:00
}
}
2014-02-10 02:10:30 +01:00
2017-08-24 05:06:56 +02:00
arghint = _get_visual_datatype ( m - > get_return_info ( ) , false ) + " " + p_method . operator String ( ) + String ( " ( " ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < m - > get_argument_count ( ) ; i + + ) {
if ( i > 0 )
arghint + = " , " ;
2015-06-27 20:52:39 +02:00
else
2017-03-05 16:44:50 +01:00
arghint + = " " ;
2014-12-17 03:17:35 +01:00
2017-03-05 16:44:50 +01:00
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2015-06-27 20:52:39 +02:00
}
String n = m - > get_argument_info ( i ) . name ;
int dp = n . find ( " : " ) ;
2017-03-05 16:44:50 +01:00
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 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( i > = deffrom ) {
int defidx = i - deffrom ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( defidx > = 0 & & defidx < m - > get_default_argument_count ( ) ) {
Variant v = m - > get_default_argument ( i ) ;
arghint + = " = " + v . get_construct_string ( ) ;
2015-06-27 20:52:39 +02:00
}
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
if ( m - > get_argument_count ( ) > 0 )
arghint + = " " ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
arghint + = " ) " ;
2015-06-27 20:52:39 +02:00
}
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
2017-08-24 05:06:56 +02:00
static void _find_call_arguments ( GDCompletionContext & context , const GDParser : : Node * p_node , int p_line , int p_argidx , Set < String > & result , bool & r_forced , String & arghint ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( ! p_node | | p_node - > type ! = GDParser : : Node : : TYPE_OPERATOR ) {
2014-12-17 02:31:57 +01:00
return ;
}
const GDParser : : OperatorNode * op = static_cast < const GDParser : : OperatorNode * > ( p_node ) ;
2017-03-05 16:44:50 +01:00
if ( op - > op ! = GDParser : : OperatorNode : : OP_CALL ) {
2014-12-17 02:31:57 +01:00
return ;
}
2017-03-05 16:44:50 +01:00
if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_BUILT_IN_FUNCTION ) {
2014-12-17 02:31:57 +01:00
//complete built-in function
2017-03-05 16:44:50 +01:00
const GDParser : : BuiltInFunctionNode * fn = static_cast < const GDParser : : BuiltInFunctionNode * > ( op - > arguments [ 0 ] ) ;
2014-12-17 02:31:57 +01:00
MethodInfo mi = GDFunctions : : get_info ( fn - > function ) ;
2017-04-16 21:09:17 +02:00
if ( mi . name = = " load " & & bool ( EditorSettings : : get_singleton ( ) - > get ( " text_editor/completion/complete_file_paths " ) ) ) {
get_directory_contents ( EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) , result ) ;
}
2017-03-05 16:44:50 +01:00
arghint = _get_visual_datatype ( mi . return_val , false ) + " " + GDFunctions : : get_func_name ( fn - > function ) + String ( " ( " ) ;
for ( int i = 0 ; i < mi . arguments . size ( ) ; i + + ) {
if ( i > 0 )
arghint + = " , " ;
2014-12-17 02:31:57 +01:00
else
2017-03-05 16:44:50 +01:00
arghint + = " " ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
arghint + = _get_visual_datatype ( mi . arguments [ i ] ) + " " + mi . arguments [ i ] . name ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
if ( mi . arguments . size ( ) > 0 )
arghint + = " " ;
arghint + = " ) " ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
} else if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_TYPE ) {
2015-06-27 20:52:39 +02:00
//complete constructor
2017-03-05 16:44:50 +01:00
const GDParser : : TypeNode * tn = static_cast < const GDParser : : TypeNode * > ( op - > arguments [ 0 ] ) ;
2014-12-17 02:31:57 +01:00
List < MethodInfo > mil ;
2017-03-05 16:44:50 +01:00
Variant : : get_constructor_list ( tn - > vtype , & mil ) ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
for ( List < MethodInfo > : : Element * E = mil . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-17 02:31:57 +01:00
MethodInfo mi = E - > get ( ) ;
2017-03-05 16:44:50 +01:00
if ( mi . arguments . size ( ) = = 0 )
2014-12-17 02:31:57 +01:00
continue ;
if ( E - > prev ( ) )
2017-03-05 16:44:50 +01:00
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 + = " , " ;
2014-12-17 02:31:57 +01:00
else
2017-03-05 16:44:50 +01:00
arghint + = " " ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
arghint + = _get_visual_datatype ( mi . arguments [ i ] ) + " " + mi . arguments [ i ] . name ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-12-17 02:31:57 +01:00
}
}
2017-03-05 16:44:50 +01:00
if ( mi . arguments . size ( ) > 0 )
arghint + = " " ;
arghint + = " ) " ;
2014-12-17 02:31:57 +01:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
} else if ( op - > arguments . size ( ) > = 2 & & op - > arguments [ 1 ] - > type = = GDParser : : Node : : TYPE_IDENTIFIER ) {
2014-12-17 02:31:57 +01:00
//make sure identifier exists...
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
const GDParser : : IdentifierNode * id = static_cast < const GDParser : : IdentifierNode * > ( op - > arguments [ 1 ] ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( op - > arguments [ 0 ] - > type = = GDParser : : Node : : TYPE_SELF ) {
2014-12-17 02:31:57 +01:00
//self, look up
2017-03-05 16:44:50 +01:00
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 ) ;
2014-12-17 02:31:57 +01:00
return ;
}
}
if ( context . function & & ! context . function - > _static ) {
2017-03-05 16:44:50 +01:00
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 ) ;
2014-12-17 02:31:57 +01:00
return ;
2014-02-10 02:10:30 +01:00
}
}
}
2014-12-17 02:31:57 +01:00
Ref < Reference > base = _get_parent_class ( context ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( true ) {
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
Ref < GDScript > script = base ;
Ref < GDNativeClass > nc = base ;
if ( script . is_valid ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( const Map < StringName , GDFunction * > : : Element * E = script - > get_member_functions ( ) . front ( ) ; E ; E = E - > next ( ) ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( E - > key ( ) = = id - > name ) {
2014-12-17 02:31:57 +01:00
2016-05-22 02:18:16 +02:00
if ( context . function & & context . function - > _static & & ! E - > get ( ) - > is_static ( ) )
2014-12-17 02:31:57 +01:00
continue ;
2017-03-05 16:44:50 +01:00
arghint = " func " + id - > name . operator String ( ) + String ( " ( " ) ;
for ( int i = 0 ; i < E - > get ( ) - > get_argument_count ( ) ; i + + ) {
if ( i > 0 )
arghint + = " , " ;
2014-12-17 02:31:57 +01:00
else
2017-03-05 16:44:50 +01:00
arghint + = " " ;
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
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 ( ) ;
2014-12-17 02:31:57 +01:00
}
}
2017-03-05 16:44:50 +01:00
if ( i = = p_argidx ) {
arghint + = String : : chr ( 0xFFFF ) ;
2014-12-17 02:31:57 +01:00
}
}
2017-03-05 16:44:50 +01:00
if ( E - > get ( ) - > get_argument_count ( ) > 0 )
arghint + = " " ;
arghint + = " ) " ;
2014-12-17 02:31:57 +01:00
return ;
}
}
2017-03-05 16:44:50 +01:00
base = script - > get_base ( ) ;
2014-12-17 02:31:57 +01:00
if ( base . is_null ( ) )
2017-03-05 16:44:50 +01:00
base = script - > get_native ( ) ;
2014-12-17 02:31:57 +01:00
} else if ( nc . is_valid ( ) ) {
if ( context . function & & ! context . function - > _static ) {
GDCompletionIdentifier ci ;
2017-03-05 16:44:50 +01:00
ci . type = Variant : : OBJECT ;
ci . obj_type = nc - > get_name ( ) ;
2014-12-17 02:31:57 +01:00
if ( ! context . _class - > owner )
2017-03-05 16:44:50 +01:00
ci . value = context . base ;
2014-12-17 02:31:57 +01:00
2017-08-24 05:06:56 +02:00
_find_type_arguments ( context , p_node , p_line , id - > name , ci , p_argidx , result , r_forced , arghint ) ;
2014-12-17 02:31:57 +01:00
//guess type..
/*
List < MethodInfo > methods ;
2017-01-03 03:03:46 +01:00
ClassDB : : get_method_list ( type , & methods ) ;
2014-12-17 02:31:57 +01:00
for ( List < MethodInfo > : : Element * E = methods . front ( ) ; E ; E = E - > next ( ) ) {
2017-01-14 12:26:56 +01:00
if ( E - > get ( ) . arguments . size ( ) )
result . insert ( E - > get ( ) . name + " ( " ) ;
else
result . insert ( E - > get ( ) . name + " () " ) ;
2014-12-17 02:31:57 +01:00
} */
}
2014-02-10 02:10:30 +01:00
break ;
2014-12-17 02:31:57 +01:00
} else
break ;
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
} else {
2015-06-27 20:52:39 +02:00
//indexed lookup
2014-02-10 02:10:30 +01:00
2014-12-17 02:31:57 +01:00
GDCompletionIdentifier ci ;
2017-03-05 16:44:50 +01:00
if ( _guess_expression_type ( context , op - > arguments [ 0 ] , p_line , ci ) ) {
2014-02-10 02:10:30 +01:00
2017-08-24 05:06:56 +02:00
_find_type_arguments ( context , p_node , p_line , id - > name , ci , p_argidx , result , r_forced , arghint ) ;
2014-12-17 02:31:57 +01:00
return ;
}
}
}
}
2014-02-10 02:10:30 +01:00
2017-08-24 05:06:56 +02:00
Error GDScriptLanguage : : complete_code ( const String & p_code , const String & p_base_path , Object * p_owner , List < String > * r_options , bool & r_forced , String & r_call_hint ) {
2014-12-08 19:47:45 +01:00
2014-02-10 02:10:30 +01:00
GDParser p ;
2015-06-26 06:14:31 +02:00
2017-03-05 16:44:50 +01:00
p . parse ( p_code , p_base_path , false , " " , true ) ;
bool isfunction = false ;
2014-12-17 02:31:57 +01:00
Set < String > options ;
2017-08-24 05:06:56 +02:00
r_forced = false ;
2014-12-17 02:31:57 +01:00
GDCompletionContext context ;
2017-03-05 16:44:50 +01:00
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 ;
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
switch ( p . get_completion_type ( ) ) {
2014-12-17 02:31:57 +01:00
case GDParser : : COMPLETION_NONE : {
} break ;
case GDParser : : COMPLETION_BUILT_IN_TYPE_CONSTANT : {
List < StringName > constants ;
2017-03-05 16:44:50 +01:00
Variant : : get_numeric_constants_for_type ( p . get_completion_built_in_constant ( ) , & constants ) ;
for ( List < StringName > : : Element * E = constants . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-17 02:31:57 +01:00
options . insert ( E - > get ( ) . operator String ( ) ) ;
}
} break ;
case GDParser : : COMPLETION_FUNCTION :
2017-03-05 16:44:50 +01:00
isfunction = true ;
2014-12-17 02:31:57 +01:00
case GDParser : : COMPLETION_IDENTIFIER : {
2017-03-05 16:44:50 +01:00
_find_identifiers ( context , p . get_completion_line ( ) , isfunction , options ) ;
2014-12-17 02:31:57 +01:00
} break ;
case GDParser : : COMPLETION_PARENT_FUNCTION : {
2017-01-08 07:01:52 +01:00
} break ;
case GDParser : : COMPLETION_GET_NODE : {
if ( p_owner ) {
List < String > opts ;
2017-03-05 16:44:50 +01:00
p_owner - > get_argument_options ( " get_node " , 0 , & opts ) ;
2017-01-08 07:01:52 +01:00
2017-03-05 16:44:50 +01:00
for ( List < String > : : Element * E = opts . front ( ) ; E ; E = E - > next ( ) ) {
2017-01-08 07:01:52 +01:00
String opt = E - > get ( ) . strip_edges ( ) ;
if ( opt . begins_with ( " \" " ) & & opt . ends_with ( " \" " ) ) {
2017-08-24 05:06:56 +02:00
r_forced = true ;
2017-03-05 16:44:50 +01:00
String idopt = opt . substr ( 1 , opt . length ( ) - 2 ) ;
if ( idopt . replace ( " / " , " _ " ) . is_valid_identifier ( ) ) {
2017-01-08 07:01:52 +01:00
options . insert ( idopt ) ;
} else {
options . insert ( opt ) ;
}
}
}
}
2014-12-17 02:31:57 +01:00
} break ;
case GDParser : : COMPLETION_METHOD :
2017-03-05 16:44:50 +01:00
isfunction = true ;
2014-12-17 02:31:57 +01:00
case GDParser : : COMPLETION_INDEX : {
const GDParser : : Node * node = p . get_completion_node ( ) ;
2017-03-05 16:44:50 +01:00
if ( node - > type ! = GDParser : : Node : : TYPE_OPERATOR )
2014-12-17 02:31:57 +01:00
break ;
GDCompletionIdentifier t ;
2017-08-28 00:03:19 +02:00
if ( _guess_expression_type ( context , static_cast < const GDParser : : OperatorNode * > ( node ) - > arguments [ 0 ] , p . get_completion_line ( ) , t , true ) ) {
2014-12-17 02:31:57 +01:00
2017-03-05 16:44:50 +01:00
if ( t . type = = Variant : : OBJECT & & t . obj_type = = " GDNativeClass " ) {
2016-09-11 17:20:28 +02:00
//native enum
Ref < GDNativeClass > gdn = t . value ;
if ( gdn . is_valid ( ) ) {
StringName cn = gdn - > get_name ( ) ;
List < String > cnames ;
2017-03-05 16:44:50 +01:00
ClassDB : : get_integer_constant_list ( cn , & cnames ) ;
for ( List < String > : : Element * E = cnames . front ( ) ; E ; E = E - > next ( ) ) {
2016-09-11 17:20:28 +02:00
options . insert ( E - > get ( ) ) ;
}
2017-01-04 21:37:45 +01:00
List < PropertyInfo > pinfo ;
2017-03-05 16:44:50 +01:00
ClassDB : : get_property_list ( cn , & pinfo ) ;
2017-01-04 21:37:45 +01:00
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = pinfo . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . usage & ( PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY ) )
2017-01-04 21:37:45 +01:00
continue ;
2017-08-24 00:10:32 +02:00
if ( String ( E - > get ( ) . name ) . find ( " / " ) ! = - 1 )
2017-01-04 21:37:45 +01:00
continue ;
options . insert ( E - > get ( ) . name ) ;
}
2016-09-11 17:20:28 +02:00
}
2017-03-05 16:44:50 +01:00
} else if ( t . type = = Variant : : OBJECT & & t . obj_type ! = StringName ( ) ) {
2014-12-20 19:30:06 +01:00
2015-06-27 20:52:39 +02:00
Ref < GDScript > on_script ;
2014-12-20 19:30:06 +01:00
if ( t . value . get_type ( ) ) {
2017-03-05 16:44:50 +01:00
Object * obj = t . value ;
2015-06-27 20:52:39 +02:00
2017-08-24 22:58:51 +02:00
GDScript * scr = Object : : cast_to < GDScript > ( obj ) ;
if ( scr ) {
while ( scr ) {
2015-06-27 20:52:39 +02:00
2017-08-24 22:58:51 +02:00
if ( ! isfunction ) {
for ( const Map < StringName , Variant > : : Element * E = scr - > get_constants ( ) . front ( ) ; E ; E = E - > next ( ) ) {
options . insert ( E - > key ( ) ) ;
2015-06-27 20:52:39 +02:00
}
}
2017-08-24 22:58:51 +02:00
for ( const Map < StringName , GDFunction * > : : Element * E = scr - > get_member_functions ( ) . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) - > is_static ( ) )
options . insert ( E - > key ( ) ) ;
}
if ( scr - > get_base ( ) . is_valid ( ) )
scr = scr - > get_base ( ) . ptr ( ) ;
else
scr = NULL ;
2015-06-27 20:52:39 +02:00
}
2017-08-24 22:58:51 +02:00
} else {
2017-08-26 22:53:28 +02:00
if ( obj ) {
on_script = obj - > get_script ( ) ;
}
2015-06-27 20:52:39 +02:00
}
}
if ( ! on_script . is_valid ( ) & & t . script . is_valid ( ) ) {
2017-03-05 16:44:50 +01:00
on_script = t . script ;
2015-06-27 20:52:39 +02:00
}
if ( on_script . is_valid ( ) ) {
GDScript * scr = on_script . ptr ( ) ;
if ( scr ) {
2014-12-20 19:30:06 +01:00
while ( scr ) {
2015-06-27 20:52:39 +02:00
String code = scr - > get_source_code ( ) ;
2017-03-05 16:44:50 +01:00
if ( code ! = " " ) {
2015-06-27 20:52:39 +02:00
//if there is code, parse it. This way is slower but updates in real-time
GDParser p ;
2017-03-05 16:44:50 +01:00
Error err = p . parse ( scr - > get_source_code ( ) , scr - > get_path ( ) . get_base_dir ( ) , true , " " , false ) ;
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
if ( err = = OK ) {
2015-06-27 20:52:39 +02:00
//only if ok, otherwise use what is cached on the script
//GDParser::ClassNode *base = p.
const GDParser : : Node * root = p . get_parse_tree ( ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( root - > type ! = GDParser : : Node : : TYPE_CLASS , ERR_PARSE_ERROR ) ;
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
const GDParser : : ClassNode * cl = static_cast < const GDParser : : ClassNode * > ( root ) ;
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < cl - > functions . size ( ) ; i + + ) {
2015-06-27 20:52:39 +02:00
if ( cl - > functions [ i ] - > arguments . size ( ) )
2017-03-05 16:44:50 +01:00
options . insert ( String ( cl - > functions [ i ] - > name ) + " ( " ) ;
2015-06-27 20:52:39 +02:00
else
2017-03-05 16:44:50 +01:00
options . insert ( String ( cl - > functions [ i ] - > name ) + " () " ) ;
2015-06-27 20:52:39 +02:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < cl - > static_functions . size ( ) ; i + + ) {
2015-06-27 20:52:39 +02:00
if ( cl - > static_functions [ i ] - > arguments . size ( ) )
2017-03-05 16:44:50 +01:00
options . insert ( String ( cl - > static_functions [ i ] - > name ) + " ( " ) ;
2015-06-27 20:52:39 +02:00
else
2017-03-05 16:44:50 +01:00
options . insert ( String ( cl - > static_functions [ i ] - > name ) + " () " ) ;
2015-06-27 20:52:39 +02:00
}
if ( ! isfunction ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < cl - > variables . size ( ) ; i + + ) {
2015-06-27 20:52:39 +02:00
options . insert ( String ( cl - > variables [ i ] . identifier ) ) ;
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < cl - > constant_expressions . size ( ) ; i + + ) {
2015-06-27 20:52:39 +02:00
options . insert ( String ( cl - > constant_expressions [ i ] . identifier ) ) ;
}
}
} else {
2017-03-05 16:44:50 +01:00
code = " " ; //well, then no code
2014-12-20 19:30:06 +01:00
}
}
2015-06-27 20:52:39 +02:00
2017-03-05 16:44:50 +01:00
if ( code = = " " ) {
2015-06-27 20:52:39 +02:00
//use class directly, no code was found
if ( ! isfunction ) {
2017-03-05 16:44:50 +01:00
for ( const Map < StringName , Variant > : : Element * E = scr - > get_constants ( ) . front ( ) ; E ; E = E - > next ( ) ) {
2015-06-27 20:52:39 +02:00
options . insert ( E - > key ( ) ) ;
}
}
2017-03-05 16:44:50 +01:00
for ( const Map < StringName , GDFunction * > : : Element * E = scr - > get_member_functions ( ) . front ( ) ; E ; E = E - > next ( ) ) {
2016-05-22 02:18:16 +02:00
if ( E - > get ( ) - > get_argument_count ( ) )
2017-03-05 16:44:50 +01:00
options . insert ( String ( E - > key ( ) ) + " () " ) ;
2015-06-27 20:52:39 +02:00
else
2017-03-05 16:44:50 +01:00
options . insert ( String ( E - > key ( ) ) + " ( " ) ;
2015-06-27 20:52:39 +02:00
}
2017-03-05 16:44:50 +01:00
for ( const Set < StringName > : : Element * E = scr - > get_members ( ) . front ( ) ; E ; E = E - > next ( ) ) {
2015-06-27 20:52:39 +02:00
options . insert ( E - > get ( ) ) ;
}
2014-12-20 19:30:06 +01:00
}
if ( scr - > get_base ( ) . is_valid ( ) )
2017-03-05 16:44:50 +01:00
scr = scr - > get_base ( ) . ptr ( ) ;
2014-12-20 19:30:06 +01:00
else
2017-03-05 16:44:50 +01:00
scr = NULL ;
2014-12-20 19:30:06 +01:00
}
}
}
2014-12-17 02:31:57 +01:00
if ( ! isfunction ) {
2017-03-05 16:44:50 +01:00
ClassDB : : get_integer_constant_list ( t . obj_type , r_options ) ;
2017-01-04 21:37:45 +01:00
List < PropertyInfo > pinfo ;
2017-03-05 16:44:50 +01:00
ClassDB : : get_property_list ( t . obj_type , & pinfo ) ;
2017-01-04 21:37:45 +01:00
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = pinfo . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) . usage & ( PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY ) )
2017-01-04 21:37:45 +01:00
continue ;
2017-08-24 00:10:32 +02:00
if ( String ( E - > get ( ) . name ) . find ( " / " ) ! = - 1 )
2017-01-04 21:37:45 +01:00
continue ;
r_options - > push_back ( E - > get ( ) . name ) ;
}
2014-12-17 02:31:57 +01:00
}
2017-01-04 21:37:45 +01:00
2014-12-17 02:31:57 +01:00
List < MethodInfo > mi ;
2017-06-23 20:10:46 +02:00
ClassDB : : get_method_list ( t . obj_type , & mi , false , true ) ;
2017-03-05 16:44:50 +01:00
for ( List < MethodInfo > : : Element * E = mi . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-17 02:31:57 +01:00
2014-12-20 19:30:06 +01:00
if ( E - > get ( ) . name . begins_with ( " _ " ) )
continue ;
2014-12-17 02:31:57 +01:00
if ( E - > get ( ) . arguments . size ( ) )
2017-03-05 16:44:50 +01:00
options . insert ( E - > get ( ) . name + " ( " ) ;
2014-12-17 02:31:57 +01:00
else
2017-03-05 16:44:50 +01:00
options . insert ( E - > get ( ) . name + " () " ) ;
2014-12-17 02:31:57 +01:00
}
} else {
2017-05-20 17:38:03 +02:00
//check InputEvent hint
{
2017-03-05 16:44:50 +01:00
if ( t . value . get_type ( ) = = Variant : : NIL ) {
2014-12-18 04:56:33 +01:00
Variant : : CallError ce ;
2017-03-05 16:44:50 +01:00
t . value = Variant : : construct ( t . type , NULL , 0 , ce ) ;
2014-12-17 02:31:57 +01:00
}
2014-12-18 04:56:33 +01:00
if ( ! isfunction ) {
List < PropertyInfo > pl ;
t . value . get_property_list ( & pl ) ;
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = pl . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-18 04:56:33 +01:00
2017-08-24 00:10:32 +02:00
if ( String ( E - > get ( ) . name ) . find ( " / " ) = = - 1 )
2014-12-18 04:56:33 +01:00
options . insert ( E - > get ( ) . name ) ;
}
}
List < MethodInfo > mi ;
t . value . get_method_list ( & mi ) ;
2017-03-05 16:44:50 +01:00
for ( List < MethodInfo > : : Element * E = mi . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-18 04:56:33 +01:00
if ( E - > get ( ) . arguments . size ( ) )
2017-03-05 16:44:50 +01:00
options . insert ( E - > get ( ) . name + " ( " ) ;
2014-12-18 04:56:33 +01:00
else
2017-03-05 16:44:50 +01:00
options . insert ( E - > get ( ) . name + " () " ) ;
2014-12-18 04:56:33 +01:00
}
2014-12-17 02:31:57 +01:00
}
}
}
} break ;
case GDParser : : COMPLETION_CALL_ARGUMENTS : {
2017-08-24 05:06:56 +02:00
_find_call_arguments ( context , p . get_completion_node ( ) , p . get_completion_line ( ) , p . get_completion_argument_index ( ) , options , r_forced , r_call_hint ) ;
2014-12-17 02:31:57 +01:00
} break ;
2015-01-03 17:03:13 +01:00
case GDParser : : COMPLETION_VIRTUAL_FUNC : {
GDCompletionIdentifier cid = _get_native_class ( context ) ;
2017-03-05 16:44:50 +01:00
if ( cid . obj_type ! = StringName ( ) ) {
2015-01-03 17:03:13 +01:00
List < MethodInfo > vm ;
2017-03-05 16:44:50 +01:00
ClassDB : : get_virtual_methods ( cid . obj_type , & vm ) ;
for ( List < MethodInfo > : : Element * E = vm . front ( ) ; E ; E = E - > next ( ) ) {
2015-01-03 17:03:13 +01:00
2017-03-05 16:44:50 +01:00
MethodInfo & mi = E - > get ( ) ;
2015-01-03 17:03:13 +01:00
String m = mi . name ;
2017-03-05 16:44:50 +01:00
if ( m . find ( " : " ) ! = - 1 )
m = m . substr ( 0 , m . find ( " : " ) ) ;
m + = " ( " ;
2015-01-03 17:03:13 +01:00
if ( mi . arguments . size ( ) ) {
2017-03-05 16:44:50 +01:00
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 ;
2015-01-03 17:03:13 +01:00
}
}
2017-03-05 16:44:50 +01:00
m + = " ): " ;
2015-01-03 17:03:13 +01:00
options . insert ( m ) ;
}
}
} break ;
2016-08-07 03:11:03 +02:00
case GDParser : : COMPLETION_YIELD : {
2014-12-17 02:31:57 +01:00
2016-08-07 03:11:03 +02:00
const GDParser : : Node * node = p . get_completion_node ( ) ;
GDCompletionIdentifier t ;
2017-03-05 16:44:50 +01:00
if ( ! _guess_expression_type ( context , node , p . get_completion_line ( ) , t ) )
2016-08-07 03:11:03 +02:00
break ;
2017-03-05 16:44:50 +01:00
if ( t . type = = Variant : : OBJECT & & t . obj_type ! = StringName ( ) ) {
2016-08-07 03:11:03 +02:00
List < MethodInfo > sigs ;
2017-03-05 16:44:50 +01:00
ClassDB : : get_signal_list ( t . obj_type , & sigs ) ;
for ( List < MethodInfo > : : Element * E = sigs . front ( ) ; E ; E = E - > next ( ) ) {
options . insert ( " \" " + E - > get ( ) . name + " \" " ) ;
2017-08-24 05:06:56 +02:00
r_forced = true ;
2016-08-07 03:11:03 +02:00
}
}
} break ;
2017-05-08 21:39:27 +02:00
case GDParser : : COMPLETION_RESOURCE_PATH : {
2017-04-16 21:09:17 +02:00
if ( EditorSettings : : get_singleton ( ) - > get ( " text_editor/completion/complete_file_paths " ) )
get_directory_contents ( EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) , options ) ;
} break ;
2017-08-24 05:06:56 +02:00
case GDParser : : COMPLETION_ASSIGN : {
# if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
GDCompletionIdentifier ci ;
if ( _guess_expression_type ( context , p . get_completion_node ( ) , p . get_completion_line ( ) , ci ) ) {
String enumeration = ci . enumeration ;
if ( enumeration . find ( " . " ) ! = - 1 ) {
//class constant
List < StringName > constants ;
String cls = enumeration . get_slice ( " . " , 0 ) ;
String enm = enumeration . get_slice ( " . " , 1 ) ;
ClassDB : : get_enum_constants ( cls , enm , & constants ) ;
//constants.sort_custom<StringName::AlphCompare>();
for ( List < StringName > : : Element * E = constants . front ( ) ; E ; E = E - > next ( ) ) {
String add = cls + " . " + E - > get ( ) ;
r_options - > push_back ( add ) ;
r_forced = true ;
}
} else {
//global constant
StringName current_enum = enumeration ;
for ( int i = 0 ; i < GlobalConstants : : get_global_constant_count ( ) ; i + + ) {
if ( GlobalConstants : : get_global_constant_enum ( i ) = = current_enum ) {
r_options - > push_back ( GlobalConstants : : get_global_constant_name ( i ) ) ;
r_forced = true ;
}
}
//global
}
}
# endif
} break ;
2014-12-17 02:31:57 +01:00
}
2017-03-05 16:44:50 +01:00
for ( Set < String > : : Element * E = options . front ( ) ; E ; E = E - > next ( ) ) {
2014-12-17 02:31:57 +01:00
r_options - > push_back ( E - > get ( ) ) ;
}
2014-02-10 02:10:30 +01:00
2014-12-17 03:17:35 +01:00
return OK ;
}
2014-02-10 02:10:30 +01:00
2014-12-17 03:17:35 +01:00
# else
2014-02-10 02:10:30 +01:00
2017-08-24 12:44:51 +02:00
Error GDScriptLanguage : : complete_code ( const String & p_code , const String & p_base_path , Object * p_owner , List < String > * r_options , bool & r_forced , String & r_call_hint ) {
2014-02-10 02:10:30 +01:00
return OK ;
}
2014-12-17 03:17:35 +01:00
# endif
2017-04-17 15:24:30 +02:00
String GDScriptLanguage : : _get_indentation ( ) const {
# ifdef TOOLS_ENABLED
2017-08-19 01:02:56 +02:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2017-04-26 13:14:03 +02:00
bool use_space_indentation = EDITOR_DEF ( " text_editor/indent/type " , 0 ) ;
2017-04-17 15:24:30 +02:00
2017-04-25 03:27:49 +02:00
if ( use_space_indentation ) {
int indent_size = EDITOR_DEF ( " text_editor/indent/size " , 4 ) ;
2017-04-17 15:24:30 +02:00
2017-04-25 03:27:49 +02:00
String space_indent = " " ;
for ( int i = 0 ; i < indent_size ; i + + ) {
space_indent + = " " ;
}
return space_indent ;
2017-04-17 15:24:30 +02:00
}
}
# endif
return " \t " ;
}
2017-03-05 16:44:50 +01:00
void GDScriptLanguage : : auto_indent_code ( String & p_code , int p_from_line , int p_to_line ) const {
2014-05-24 06:35:47 +02:00
2017-04-17 15:24:30 +02:00
String indent = _get_indentation ( ) ;
2014-05-24 06:35:47 +02:00
Vector < String > lines = p_code . split ( " \n " ) ;
List < int > indent_stack ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
2014-05-24 06:35:47 +02:00
String l = lines [ i ] ;
2017-03-05 16:44:50 +01:00
int tc = 0 ;
for ( int j = 0 ; j < l . length ( ) ; j + + ) {
if ( l [ j ] = = ' ' | | l [ j ] = = ' \t ' ) {
2014-05-24 06:35:47 +02:00
tc + + ;
} else {
break ;
}
}
2017-03-05 16:44:50 +01:00
String st = l . substr ( tc , l . length ( ) ) . strip_edges ( ) ;
if ( st = = " " | | st . begins_with ( " # " ) )
2014-05-24 06:35:47 +02:00
continue ; //ignore!
2017-03-05 16:44:50 +01:00
int ilevel = 0 ;
2014-05-24 06:35:47 +02:00
if ( indent_stack . size ( ) ) {
2017-03-05 16:44:50 +01:00
ilevel = indent_stack . back ( ) - > get ( ) ;
2014-05-24 06:35:47 +02:00
}
2017-03-05 16:44:50 +01:00
if ( tc > ilevel ) {
2014-05-24 06:35:47 +02:00
indent_stack . push_back ( tc ) ;
2017-03-05 16:44:50 +01:00
} else if ( tc < ilevel ) {
while ( indent_stack . size ( ) & & indent_stack . back ( ) - > get ( ) > tc ) {
2014-05-24 06:35:47 +02:00
indent_stack . pop_back ( ) ;
}
2017-03-05 16:44:50 +01:00
if ( indent_stack . size ( ) & & indent_stack . back ( ) - > get ( ) ! = tc )
2014-05-24 06:35:47 +02:00
indent_stack . push_back ( tc ) ; //this is not right but gets the job done
}
2017-03-05 16:44:50 +01:00
if ( i > = p_from_line ) {
2014-05-24 06:35:47 +02:00
2017-03-05 16:44:50 +01:00
l = " " ;
2017-04-17 15:24:30 +02:00
for ( int j = 0 ; j < indent_stack . size ( ) ; j + + ) {
l + = indent ;
}
2017-03-05 16:44:50 +01:00
l + = st ;
2014-05-24 06:35:47 +02:00
2017-03-05 16:44:50 +01:00
} else if ( i > p_to_line ) {
2014-05-24 06:35:47 +02:00
break ;
}
//print_line(itos(indent_stack.size())+","+itos(tc)+": "+l);
2017-03-05 16:44:50 +01:00
lines [ i ] = l ;
2014-05-24 06:35:47 +02:00
}
2017-03-05 16:44:50 +01:00
p_code = " " ;
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
if ( i > 0 )
p_code + = " \n " ;
p_code + = lines [ i ] ;
2014-05-24 06:35:47 +02:00
}
}
2016-09-12 15:52:29 +02:00
2016-09-13 03:40:46 +02:00
# ifdef TOOLS_ENABLED
2017-03-05 16:44:50 +01:00
Error GDScriptLanguage : : lookup_code ( const String & p_code , const String & p_symbol , const String & p_base_path , Object * p_owner , LookupResult & r_result ) {
2016-09-12 15:52:29 +02:00
//before parsing, try the usual stuff
2017-01-03 03:03:46 +01:00
if ( ClassDB : : class_exists ( p_symbol ) ) {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS ;
r_result . class_name = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < Variant : : VARIANT_MAX ; i + + ) {
2016-09-12 15:52:29 +02:00
Variant : : Type t = Variant : : Type ( i ) ;
2017-03-05 16:44:50 +01:00
if ( Variant : : get_type_name ( t ) = = p_symbol ) {
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS ;
r_result . class_name = Variant : : get_type_name ( t ) ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < GDFunctions : : FUNC_MAX ; i + + ) {
if ( GDFunctions : : get_func_name ( GDFunctions : : Function ( i ) ) = = p_symbol ) {
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_METHOD ;
r_result . class_name = " @GDScript " ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
GDParser p ;
2017-03-05 16:44:50 +01:00
p . parse ( p_code , p_base_path , false , " " , true ) ;
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
if ( p . get_completion_type ( ) = = GDParser : : COMPLETION_NONE )
2016-09-12 15:52:29 +02:00
return ERR_CANT_RESOLVE ;
GDCompletionContext context ;
2017-03-05 16:44:50 +01:00
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 ;
bool isfunction = false ;
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
switch ( p . get_completion_type ( ) ) {
2016-09-12 15:52:29 +02:00
2017-02-21 10:59:19 +01:00
case GDParser : : COMPLETION_GET_NODE :
2016-09-12 15:52:29 +02:00
case GDParser : : COMPLETION_NONE : {
} break ;
case GDParser : : COMPLETION_BUILT_IN_TYPE_CONSTANT : {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_CONSTANT ;
r_result . class_name = Variant : : get_type_name ( p . get_completion_built_in_constant ( ) ) ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
} break ;
case GDParser : : COMPLETION_FUNCTION : {
if ( context . _class & & context . _class - > functions . size ( ) ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > functions . size ( ) ; i + + ) {
if ( context . _class - > functions [ i ] - > name = = p_symbol ) {
r_result . type = ScriptLanguage : : LookupResult : : RESULT_SCRIPT_LOCATION ;
r_result . location = context . _class - > functions [ i ] - > line ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
}
Ref < GDScript > parent = _get_parent_class ( context ) ;
2017-03-05 16:44:50 +01:00
while ( parent . is_valid ( ) ) {
2016-09-12 15:52:29 +02:00
int line = parent - > get_member_line ( p_symbol ) ;
2017-03-05 16:44:50 +01:00
if ( line > = 0 ) {
r_result . type = ScriptLanguage : : LookupResult : : RESULT_SCRIPT_LOCATION ;
r_result . location = line ;
r_result . script = parent ;
2016-09-12 15:52:29 +02:00
return OK ;
}
2017-03-05 16:44:50 +01:00
parent = parent - > get_base ( ) ;
2016-09-12 15:52:29 +02:00
}
GDCompletionIdentifier identifier = _get_native_class ( context ) ;
2017-03-05 16:44:50 +01:00
print_line ( " identifier: " + String ( identifier . obj_type ) ) ;
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
if ( ClassDB : : has_method ( identifier . obj_type , p_symbol ) ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_METHOD ;
r_result . class_name = identifier . obj_type ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
} break ;
case GDParser : : COMPLETION_IDENTIFIER : {
//check if a function
if ( p . get_completion_identifier_is_function ( ) ) {
if ( context . _class & & context . _class - > functions . size ( ) ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > functions . size ( ) ; i + + ) {
if ( context . _class - > functions [ i ] - > name = = p_symbol ) {
r_result . type = ScriptLanguage : : LookupResult : : RESULT_SCRIPT_LOCATION ;
r_result . location = context . _class - > functions [ i ] - > line ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
}
Ref < GDScript > parent = _get_parent_class ( context ) ;
2017-03-05 16:44:50 +01:00
while ( parent . is_valid ( ) ) {
2016-09-12 15:52:29 +02:00
int line = parent - > get_member_line ( p_symbol ) ;
2017-03-05 16:44:50 +01:00
if ( line > = 0 ) {
r_result . type = ScriptLanguage : : LookupResult : : RESULT_SCRIPT_LOCATION ;
r_result . location = line ;
r_result . script = parent ;
2016-09-12 15:52:29 +02:00
return OK ;
}
2017-03-05 16:44:50 +01:00
parent = parent - > get_base ( ) ;
2016-09-12 15:52:29 +02:00
}
GDCompletionIdentifier identifier = _get_native_class ( context ) ;
2017-03-05 16:44:50 +01:00
if ( ClassDB : : has_method ( identifier . obj_type , p_symbol ) ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_METHOD ;
r_result . class_name = identifier . obj_type ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
} else {
2017-01-04 21:37:45 +01:00
GDCompletionIdentifier gdi = _get_native_class ( context ) ;
2017-03-05 16:44:50 +01:00
if ( gdi . obj_type ! = StringName ( ) ) {
2017-01-04 21:37:45 +01:00
bool valid ;
2017-03-05 16:44:50 +01:00
Variant : : Type t = ClassDB : : get_property_type ( gdi . obj_type , p_symbol , & valid ) ;
if ( t ! = Variant : : NIL & & valid ) {
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_PROPERTY ;
r_result . class_name = gdi . obj_type ;
r_result . class_member = p_symbol ;
2017-01-04 21:37:45 +01:00
return OK ;
}
}
2017-03-05 16:44:50 +01:00
const GDParser : : BlockNode * block = context . block ;
2016-09-12 15:52:29 +02:00
//search in blocks going up (local var?)
2017-03-05 16:44:50 +01:00
while ( block ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < block - > statements . size ( ) ; i + + ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
if ( block - > statements [ i ] - > line > p . get_completion_line ( ) )
2016-09-12 15:52:29 +02:00
continue ;
2017-03-05 16:44:50 +01:00
if ( block - > statements [ i ] - > type = = GDParser : : BlockNode : : TYPE_LOCAL_VAR ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
const GDParser : : LocalVarNode * lv = static_cast < const GDParser : : LocalVarNode * > ( block - > statements [ i ] ) ;
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
if ( lv - > assign & & lv - > name = = p_symbol ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_SCRIPT_LOCATION ;
r_result . location = block - > statements [ i ] - > line ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
}
2017-03-05 16:44:50 +01:00
block = block - > parent_block ;
2016-09-12 15:52:29 +02:00
}
//guess from function arguments
2017-03-05 16:44:50 +01:00
if ( context . function & & context . function - > name ! = StringName ( ) ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . function - > arguments . size ( ) ; i + + ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
if ( context . function - > arguments [ i ] = = p_symbol ) {
r_result . type = ScriptLanguage : : LookupResult : : RESULT_SCRIPT_LOCATION ;
r_result . location = context . function - > line ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
}
//guess in class constants
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > constant_expressions . size ( ) ; i + + ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
if ( context . _class - > constant_expressions [ i ] . identifier = = p_symbol ) {
r_result . type = ScriptLanguage : : LookupResult : : RESULT_SCRIPT_LOCATION ;
r_result . location = context . _class - > constant_expressions [ i ] . expression - > line ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
//guess in class variables
if ( ! ( context . function & & context . function - > _static ) ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < context . _class - > variables . size ( ) ; i + + ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
if ( context . _class - > variables [ i ] . identifier = = p_symbol ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_SCRIPT_LOCATION ;
r_result . location = context . _class - > variables [ i ] . line ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
}
//guess in autoloads as singletons
List < PropertyInfo > props ;
2017-07-19 22:00:46 +02:00
ProjectSettings : : get_singleton ( ) - > get_property_list ( & props ) ;
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
2016-09-12 15:52:29 +02:00
String s = E - > get ( ) . name ;
if ( ! s . begins_with ( " autoload/ " ) )
continue ;
2017-03-05 16:44:50 +01:00
String name = s . get_slice ( " / " , 1 ) ;
if ( name = = String ( p_symbol ) ) {
2016-09-12 15:52:29 +02:00
2017-07-19 22:00:46 +02:00
String path = ProjectSettings : : get_singleton ( ) - > get ( s ) ;
2016-09-12 15:52:29 +02:00
if ( path . begins_with ( " * " ) ) {
2017-03-05 16:44:50 +01:00
String script = path . substr ( 1 , path . length ( ) ) ;
2016-09-12 15:52:29 +02:00
if ( ! script . ends_with ( " .gd " ) ) {
//not a script, try find the script anyway,
//may have some success
2017-03-05 16:44:50 +01:00
script = script . get_basename ( ) + " .gd " ;
2016-09-12 15:52:29 +02:00
}
if ( FileAccess : : exists ( script ) ) {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_SCRIPT_LOCATION ;
r_result . location = 0 ;
r_result . script = ResourceLoader : : load ( script ) ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
}
}
//global
2017-03-05 16:44:50 +01:00
for ( Map < StringName , int > : : Element * E = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > key ( ) = = p_symbol ) {
2016-09-12 15:52:29 +02:00
Variant value = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ E - > get ( ) ] ;
2017-03-05 16:44:50 +01:00
if ( value . get_type ( ) = = Variant : : OBJECT ) {
2016-09-12 15:52:29 +02:00
Object * obj = value ;
if ( obj ) {
2017-08-24 22:58:51 +02:00
if ( Object : : cast_to < GDNativeClass > ( obj ) ) {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS ;
2017-08-24 22:58:51 +02:00
r_result . class_name = Object : : cast_to < GDNativeClass > ( obj ) - > get_name ( ) ;
2016-09-12 15:52:29 +02:00
} else {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS ;
r_result . class_name = obj - > get_class ( ) ;
2016-09-12 15:52:29 +02:00
}
return OK ;
}
} else {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_CONSTANT ;
r_result . class_name = " @Global Scope " ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
}
}
} break ;
case GDParser : : COMPLETION_PARENT_FUNCTION : {
} break ;
case GDParser : : COMPLETION_METHOD :
2017-03-05 16:44:50 +01:00
isfunction = true ;
2016-09-12 15:52:29 +02:00
case GDParser : : COMPLETION_INDEX : {
const GDParser : : Node * node = p . get_completion_node ( ) ;
2017-03-05 16:44:50 +01:00
if ( node - > type ! = GDParser : : Node : : TYPE_OPERATOR )
2016-09-12 15:52:29 +02:00
break ;
GDCompletionIdentifier t ;
2017-03-05 16:44:50 +01:00
if ( _guess_expression_type ( context , static_cast < const GDParser : : OperatorNode * > ( node ) - > arguments [ 0 ] , p . get_completion_line ( ) , t ) ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
if ( t . type = = Variant : : OBJECT & & t . obj_type = = " GDNativeClass " ) {
2016-09-12 15:52:29 +02:00
//native enum
Ref < GDNativeClass > gdn = t . value ;
if ( gdn . is_valid ( ) ) {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_CONSTANT ;
r_result . class_name = gdn - > get_name ( ) ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
2017-03-05 16:44:50 +01:00
} else if ( t . type = = Variant : : OBJECT & & t . obj_type ! = StringName ( ) ) {
2016-09-12 15:52:29 +02:00
Ref < GDScript > on_script ;
if ( t . value . get_type ( ) ) {
2017-03-05 16:44:50 +01:00
Object * obj = t . value ;
2016-09-12 15:52:29 +02:00
if ( obj ) {
2017-03-05 16:44:50 +01:00
on_script = obj - > get_script ( ) ;
2016-09-12 15:52:29 +02:00
if ( on_script . is_valid ( ) ) {
int loc = on_script - > get_member_line ( p_symbol ) ;
2017-03-05 16:44:50 +01:00
if ( loc > = 0 ) {
r_result . script = on_script ;
r_result . type = ScriptLanguage : : LookupResult : : RESULT_SCRIPT_LOCATION ;
r_result . location = loc ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
}
}
2017-03-05 16:44:50 +01:00
if ( ClassDB : : has_method ( t . obj_type , p_symbol ) ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_METHOD ;
r_result . class_name = t . obj_type ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
bool success ;
2017-03-05 16:44:50 +01:00
ClassDB : : get_integer_constant ( t . obj_type , p_symbol , & success ) ;
2016-09-12 15:52:29 +02:00
if ( success ) {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_CONSTANT ;
r_result . class_name = t . obj_type ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
2017-03-05 16:44:50 +01:00
ClassDB : : get_property_type ( t . obj_type , p_symbol , & success ) ;
2016-09-12 15:52:29 +02:00
if ( success ) {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_PROPERTY ;
r_result . class_name = t . obj_type ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
} else {
Variant : : CallError ce ;
2017-03-05 16:44:50 +01:00
Variant v = Variant : : construct ( t . type , NULL , 0 , ce ) ;
2016-09-12 15:52:29 +02:00
bool valid ;
2017-03-05 16:44:50 +01:00
v . get_numeric_constant_value ( t . type , p_symbol , & valid ) ;
2016-09-12 15:52:29 +02:00
if ( valid ) {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_CONSTANT ;
r_result . class_name = Variant : : get_type_name ( t . type ) ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
//todo check all inputevent types for property
2017-03-05 16:44:50 +01:00
v . get ( p_symbol , & valid ) ;
2016-09-12 15:52:29 +02:00
if ( valid ) {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_PROPERTY ;
r_result . class_name = Variant : : get_type_name ( t . type ) ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
if ( v . has_method ( p_symbol ) ) {
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_METHOD ;
r_result . class_name = Variant : : get_type_name ( t . type ) ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
}
} break ;
case GDParser : : COMPLETION_CALL_ARGUMENTS : {
return ERR_CANT_RESOLVE ;
} break ;
case GDParser : : COMPLETION_VIRTUAL_FUNC : {
GDCompletionIdentifier cid = _get_native_class ( context ) ;
2017-03-05 16:44:50 +01:00
if ( cid . obj_type ! = StringName ( ) ) {
2016-09-12 15:52:29 +02:00
List < MethodInfo > vm ;
2017-03-05 16:44:50 +01:00
ClassDB : : get_virtual_methods ( cid . obj_type , & vm ) ;
for ( List < MethodInfo > : : Element * E = vm . front ( ) ; E ; E = E - > next ( ) ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
if ( p_symbol = = E - > get ( ) . name ) {
2016-09-12 15:52:29 +02:00
2017-03-05 16:44:50 +01:00
r_result . type = ScriptLanguage : : LookupResult : : RESULT_CLASS_METHOD ;
r_result . class_name = cid . obj_type ;
r_result . class_member = p_symbol ;
2016-09-12 15:52:29 +02:00
return OK ;
}
}
}
} break ;
case GDParser : : COMPLETION_YIELD : {
return ERR_CANT_RESOLVE ;
} break ;
}
return ERR_CANT_RESOLVE ;
}
2016-09-13 03:40:46 +02:00
# endif