2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* gdscript_editor.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2017-11-16 18:38:18 +01:00
# include "gdscript.h"
2017-08-27 21:07:15 +02:00
2020-06-10 19:53:25 -03:00
# include "gdscript_analyzer.h"
2020-05-01 19:14:56 -03:00
# include "gdscript_parser.h"
# include "gdscript_tokenizer.h"
2020-11-26 11:56:32 -03:00
# include "gdscript_utility_functions.h"
2017-08-27 21:07:15 +02:00
2023-06-13 16:56:21 +02:00
# ifdef TOOLS_ENABLED
2024-12-14 21:25:05 +03:00
# include "editor/gdscript_docgen.h"
2023-06-13 16:56:21 +02:00
# include "editor/script_templates/templates.gen.h"
# endif
# include "core/config/engine.h"
# include "core/core_constants.h"
# include "core/io/file_access.h"
2024-09-19 22:35:51 -07:00
# include "core/math/expression.h"
2023-06-13 16:56:21 +02:00
2017-04-16 21:09:17 +02:00
# ifdef TOOLS_ENABLED
2020-11-07 19:33:38 -03:00
# include "core/config/project_settings.h"
2025-04-06 08:21:43 +03:00
# include "editor/editor_node.h"
# include "editor/editor_string_names.h"
2025-06-10 16:47:26 +02:00
# include "editor/file_system/editor_file_system.h"
# include "editor/settings/editor_settings.h"
2017-04-16 21:09:17 +02:00
# endif
2014-02-09 22:10:30 -03:00
2025-01-15 21:47:43 +08:00
Vector < String > GDScriptLanguage : : get_comment_delimiters ( ) const {
static const Vector < String > delimiters = { " # " } ;
return delimiters ;
2014-02-09 22:10:30 -03:00
}
2019-04-05 23:15:12 +02:00
2025-01-15 21:47:43 +08:00
Vector < String > GDScriptLanguage : : get_doc_comment_delimiters ( ) const {
static const Vector < String > delimiters = { " ## " } ;
return delimiters ;
2023-02-05 12:01:01 +03:00
}
2025-01-15 21:47:43 +08:00
Vector < String > GDScriptLanguage : : get_string_delimiters ( ) const {
static const Vector < String > delimiters = {
" \" \" " ,
" ' ' " ,
" \" \" \" \" \" \" " ,
" ''' ''' " ,
} ;
2023-08-28 13:00:33 +03:00
// NOTE: StringName, NodePath and r-strings are not listed here.
2025-01-15 21:47:43 +08:00
return delimiters ;
2014-02-09 22:10:30 -03:00
}
2019-04-05 23:15:12 +02:00
2021-10-11 11:30:59 +02:00
bool GDScriptLanguage : : is_using_templates ( ) {
return true ;
}
2019-04-05 23:15:12 +02:00
2021-10-11 11:30:59 +02:00
Ref < Script > GDScriptLanguage : : make_template ( const String & p_template , const String & p_class_name , const String & p_base_class_name ) const {
2022-09-29 12:53:28 +03:00
Ref < GDScript > scr ;
scr . instantiate ( ) ;
2025-06-23 21:46:03 +03:00
2021-10-11 11:30:59 +02:00
String processed_template = p_template ;
2025-06-23 21:46:03 +03:00
2019-04-05 23:15:12 +02:00
# ifdef TOOLS_ENABLED
2025-06-23 21:46:03 +03:00
const bool type_hints = EditorSettings : : get_singleton ( ) - > get_setting ( " text_editor/completion/add_type_hints " ) ;
# else
const bool type_hints = true ;
2022-03-28 15:48:38 +02:00
# endif
2025-06-23 21:46:03 +03:00
2022-03-28 15:48:38 +02:00
if ( ! type_hints ) {
2021-10-11 11:30:59 +02:00
processed_template = processed_template . replace ( " : int " , " " )
2023-09-12 11:52:43 +03:00
. replace ( " : Shader.Mode " , " " )
. replace ( " : VisualShader.Type " , " " )
. replace ( " : float " , " " )
2021-10-11 11:30:59 +02:00
. replace ( " : String " , " " )
2022-03-27 23:00:02 -03:00
. replace ( " : Array[String] " , " " )
2023-09-12 11:52:43 +03:00
. replace ( " : Node " , " " )
2022-09-29 15:38:55 +02:00
. replace ( " : CharFXTransform " , " " )
2021-10-11 11:30:59 +02:00
. replace ( " := " , " = " )
2023-09-12 11:52:43 +03:00
. replace ( " -> void " , " " )
2022-09-29 15:38:55 +02:00
. replace ( " -> bool " , " " )
2023-09-12 11:52:43 +03:00
. replace ( " -> int " , " " )
. replace ( " -> PortType " , " " )
. replace ( " -> String " , " " )
. replace ( " -> Object " , " " ) ;
2019-04-05 23:15:12 +02:00
}
2021-10-11 11:30:59 +02:00
processed_template = processed_template . replace ( " _BASE_ " , p_base_class_name )
2024-09-22 19:31:58 +08:00
. replace ( " _CLASS_SNAKE_CASE_ " , p_class_name . to_snake_case ( ) . validate_unicode_identifier ( ) )
. replace ( " _CLASS_ " , p_class_name . to_pascal_case ( ) . validate_unicode_identifier ( ) )
2021-10-11 11:30:59 +02:00
. replace ( " _TS_ " , _get_indentation ( ) ) ;
2022-09-29 12:53:28 +03:00
scr - > set_source_code ( processed_template ) ;
2025-06-23 21:46:03 +03:00
2022-09-29 12:53:28 +03:00
return scr ;
2014-02-09 22:10:30 -03:00
}
2021-08-01 16:47:20 -03:00
Vector < ScriptLanguage : : ScriptTemplate > GDScriptLanguage : : get_built_in_templates ( const StringName & p_object ) {
2021-10-11 11:30:59 +02:00
Vector < ScriptLanguage : : ScriptTemplate > templates ;
2022-03-28 15:48:38 +02:00
# ifdef TOOLS_ENABLED
2021-10-11 11:30:59 +02:00
for ( int i = 0 ; i < TEMPLATES_ARRAY_SIZE ; i + + ) {
if ( TEMPLATES [ i ] . inherit = = p_object ) {
templates . append ( TEMPLATES [ i ] ) ;
}
}
2022-03-28 15:48:38 +02:00
# endif
2021-10-11 11:30:59 +02:00
return templates ;
2017-06-13 20:03:08 +00:00
}
2022-05-13 15:04:37 +02:00
static void get_function_names_recursively ( const GDScriptParser : : ClassNode * p_class , const String & p_prefix , HashMap < int , String > & r_funcs ) {
2020-05-01 19:14:56 -03:00
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
if ( p_class - > members [ i ] . type = = GDScriptParser : : ClassNode : : Member : : FUNCTION ) {
const GDScriptParser : : FunctionNode * function = p_class - > members [ i ] . function ;
2020-12-15 12:04:21 +00:00
r_funcs [ function - > start_line ] = p_prefix . is_empty ( ) ? String ( function - > identifier - > name ) : p_prefix + " . " + String ( function - > identifier - > name ) ;
2020-05-01 19:14:56 -03:00
} else if ( p_class - > members [ i ] . type = = GDScriptParser : : ClassNode : : Member : : CLASS ) {
String new_prefix = p_class - > members [ i ] . m_class - > identifier - > name ;
2020-12-15 12:04:21 +00:00
get_function_names_recursively ( p_class - > members [ i ] . m_class , p_prefix . is_empty ( ) ? new_prefix : p_prefix + " . " + new_prefix , r_funcs ) ;
2020-05-01 19:14:56 -03:00
}
}
}
2022-05-19 17:00:06 +02:00
bool GDScriptLanguage : : validate ( const String & p_script , const String & p_path , List < String > * r_functions , List < ScriptLanguage : : ScriptError > * r_errors , List < ScriptLanguage : : Warning > * r_warnings , HashSet < int > * r_safe_lines ) const {
2017-11-16 18:38:18 +01:00
GDScriptParser parser ;
2020-06-10 19:53:25 -03:00
GDScriptAnalyzer analyzer ( & parser ) ;
2014-02-09 22:10:30 -03:00
2020-05-01 19:14:56 -03:00
Error err = parser . parse ( p_script , p_path , false ) ;
2020-06-10 19:53:25 -03:00
if ( err = = OK ) {
err = analyzer . analyze ( ) ;
}
2020-06-11 19:31:28 -03:00
# ifdef DEBUG_ENABLED
if ( r_warnings ) {
2021-07-15 23:45:57 -04:00
for ( const GDScriptWarning & E : parser . get_warnings ( ) ) {
const GDScriptWarning & warn = E ;
2020-06-11 19:31:28 -03:00
ScriptLanguage : : Warning w ;
w . start_line = warn . start_line ;
w . end_line = warn . end_line ;
w . code = ( int ) warn . code ;
w . string_code = GDScriptWarning : : get_name_from_code ( warn . code ) ;
w . message = warn . get_message ( ) ;
r_warnings - > push_back ( w ) ;
}
}
# endif
2014-02-09 22:10:30 -03:00
if ( err ) {
2021-05-18 13:09:19 +10:00
if ( r_errors ) {
2023-07-24 15:49:39 +02:00
for ( const GDScriptParser : : ParserError & pe : parser . get_errors ( ) ) {
2021-05-18 13:09:19 +10:00
ScriptLanguage : : ScriptError e ;
2023-07-24 15:49:39 +02:00
e . path = p_path ;
2021-05-18 13:09:19 +10:00
e . line = pe . line ;
e . column = pe . column ;
e . message = pe . message ;
r_errors - > push_back ( e ) ;
}
2023-07-24 15:49:39 +02:00
2024-04-12 16:13:25 -07:00
for ( KeyValue < String , Ref < GDScriptParserRef > > E : parser . get_depended_parsers ( ) ) {
2023-07-24 15:49:39 +02:00
GDScriptParser * depended_parser = E . value - > get_parser ( ) ;
for ( const GDScriptParser : : ParserError & pe : depended_parser - > get_errors ( ) ) {
ScriptLanguage : : ScriptError e ;
e . path = E . key ;
e . line = pe . line ;
e . column = pe . column ;
e . message = pe . message ;
r_errors - > push_back ( e ) ;
}
}
2021-05-18 13:09:19 +10:00
}
2014-02-09 22:10:30 -03:00
return false ;
2025-05-08 17:29:15 +02:00
} else if ( r_functions ) {
2020-05-01 19:14:56 -03:00
const GDScriptParser : : ClassNode * cl = parser . get_tree ( ) ;
2022-05-13 15:04:37 +02:00
HashMap < int , String > funcs ;
2014-02-09 22:10:30 -03:00
2020-05-01 19:14:56 -03:00
get_function_names_recursively ( cl , " " , funcs ) ;
2018-06-11 16:39:55 -03:00
2021-08-09 14:13:42 -06:00
for ( const KeyValue < int , String > & E : funcs ) {
r_functions - > push_back ( E . value + " : " + itos ( E . key ) ) ;
2014-02-09 22:10:30 -03:00
}
}
2020-07-15 22:02:44 -03:00
# ifdef DEBUG_ENABLED
2020-07-06 12:24:24 -03:00
if ( r_safe_lines ) {
2022-05-19 17:00:06 +02:00
const HashSet < int > & unsafe_lines = parser . get_unsafe_lines ( ) ;
2020-07-06 12:24:24 -03:00
for ( int i = 1 ; i < = parser . get_last_line_number ( ) ; i + + ) {
if ( ! unsafe_lines . has ( i ) ) {
r_safe_lines - > insert ( i ) ;
}
}
}
2020-07-15 22:02:44 -03:00
# endif
2020-07-06 12:24:24 -03:00
2014-02-09 22:10:30 -03:00
return true ;
}
2017-10-24 01:54:47 +02:00
bool GDScriptLanguage : : supports_builtin_mode ( ) const {
return true ;
}
2020-11-29 09:12:06 +05:30
bool GDScriptLanguage : : supports_documentation ( ) const {
return true ;
}
2017-03-05 16:44:50 +01:00
int GDScriptLanguage : : find_function ( const String & p_function , const String & p_code ) const {
2024-01-22 11:31:55 -03:00
GDScriptTokenizerText tokenizer ;
2020-05-01 19:14:56 -03:00
tokenizer . set_source_code ( p_code ) ;
2017-03-05 16:44:50 +01:00
int indent = 0 ;
2020-05-01 19:14:56 -03:00
GDScriptTokenizer : : Token current = tokenizer . scan ( ) ;
while ( current . type ! = GDScriptTokenizer : : Token : : TK_EOF & & current . type ! = GDScriptTokenizer : : Token : : ERROR ) {
if ( current . type = = GDScriptTokenizer : : Token : : INDENT ) {
indent + + ;
} else if ( current . type = = GDScriptTokenizer : : Token : : DEDENT ) {
indent - - ;
2014-02-09 22:10:30 -03:00
}
2020-05-01 19:14:56 -03:00
if ( indent = = 0 & & current . type = = GDScriptTokenizer : : Token : : FUNC ) {
current = tokenizer . scan ( ) ;
if ( current . is_identifier ( ) ) {
String identifier = current . get_identifier ( ) ;
if ( identifier = = p_function ) {
return current . start_line ;
}
2014-02-09 22:10:30 -03:00
}
}
2020-05-01 19:14:56 -03:00
current = tokenizer . scan ( ) ;
2014-02-09 22:10:30 -03:00
}
return - 1 ;
}
Script * GDScriptLanguage : : create_script ( ) const {
2017-03-05 16:44:50 +01:00
return memnew ( GDScript ) ;
2014-02-09 22:10:30 -03:00
}
/* DEBUGGER FUNCTIONS */
2023-04-29 17:20:38 +02:00
thread_local int GDScriptLanguage : : _debug_parse_err_line = - 1 ;
thread_local String GDScriptLanguage : : _debug_parse_err_file ;
thread_local String GDScriptLanguage : : _debug_error ;
2017-03-05 16:44:50 +01:00
bool GDScriptLanguage : : debug_break_parse ( const String & p_file , int p_line , const String & p_error ) {
2021-10-11 11:30:59 +02:00
// break because of parse error
2014-02-09 22:10:30 -03:00
2020-02-27 03:30:20 +01:00
if ( EngineDebugger : : is_active ( ) & & Thread : : get_caller_id ( ) = = Thread : : get_main_id ( ) ) {
2017-03-05 16:44:50 +01:00
_debug_parse_err_line = p_line ;
_debug_parse_err_file = p_file ;
_debug_error = p_error ;
2020-02-27 03:30:20 +01:00
EngineDebugger : : get_script_debugger ( ) - > debug ( this , false , true ) ;
2023-04-29 17:20:38 +02:00
// Because this is thread local, clear the memory afterwards.
_debug_parse_err_file = String ( ) ;
_debug_error = String ( ) ;
2017-03-05 16:44:50 +01:00
return true ;
} else {
return false ;
}
2014-02-09 22:10:30 -03:00
}
2017-03-05 16:44:50 +01:00
bool GDScriptLanguage : : debug_break ( const String & p_error , bool p_allow_continue ) {
2023-04-29 17:20:38 +02:00
if ( EngineDebugger : : is_active ( ) ) {
2017-03-05 16:44:50 +01:00
_debug_parse_err_line = - 1 ;
_debug_parse_err_file = " " ;
_debug_error = p_error ;
2019-07-29 20:09:22 +02:00
bool is_error_breakpoint = p_error ! = " Breakpoint " ;
2020-02-27 03:30:20 +01:00
EngineDebugger : : get_script_debugger ( ) - > debug ( this , p_allow_continue , is_error_breakpoint ) ;
2023-04-29 17:20:38 +02:00
// Because this is thread local, clear the memory afterwards.
_debug_parse_err_file = String ( ) ;
_debug_error = String ( ) ;
2017-03-05 16:44:50 +01:00
return true ;
} else {
return false ;
}
2014-02-09 22:10:30 -03:00
}
String GDScriptLanguage : : debug_get_error ( ) const {
2017-03-05 16:44:50 +01:00
return _debug_error ;
2014-02-09 22:10:30 -03:00
}
int GDScriptLanguage : : debug_get_stack_level_count ( ) const {
2020-05-14 16:41:43 +02:00
if ( _debug_parse_err_line > = 0 ) {
2014-02-09 22:10:30 -03:00
return 1 ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2025-05-23 19:32:01 +02:00
return _call_stack_size ;
2014-02-09 22:10:30 -03:00
}
2020-05-14 14:29:06 +02:00
2014-02-09 22:10:30 -03:00
int GDScriptLanguage : : debug_get_stack_level_line ( int p_level ) const {
2020-05-14 16:41:43 +02:00
if ( _debug_parse_err_line > = 0 ) {
2014-02-09 22:10:30 -03:00
return _debug_parse_err_line ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2025-05-23 19:32:01 +02:00
ERR_FAIL_INDEX_V ( p_level , ( int ) _call_stack_size , - 1 ) ;
2014-02-09 22:10:30 -03:00
2025-05-23 19:32:01 +02:00
return * ( _get_stack_level ( p_level ) - > line ) ;
2014-02-09 22:10:30 -03:00
}
2020-05-14 14:29:06 +02:00
2014-02-09 22:10:30 -03:00
String GDScriptLanguage : : debug_get_stack_level_function ( int p_level ) const {
2020-05-14 16:41:43 +02:00
if ( _debug_parse_err_line > = 0 ) {
2014-02-09 22:10:30 -03:00
return " " ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2025-05-23 19:32:01 +02:00
ERR_FAIL_INDEX_V ( p_level , ( int ) _call_stack_size , " " ) ;
GDScriptFunction * func = _get_stack_level ( p_level ) - > function ;
return func ? func - > get_name ( ) . operator String ( ) : " " ;
2014-02-09 22:10:30 -03:00
}
2020-05-14 14:29:06 +02:00
2014-02-09 22:10:30 -03:00
String GDScriptLanguage : : debug_get_stack_level_source ( int p_level ) const {
2020-05-14 16:41:43 +02:00
if ( _debug_parse_err_line > = 0 ) {
2014-02-09 22:10:30 -03:00
return _debug_parse_err_file ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2025-05-23 19:32:01 +02:00
ERR_FAIL_INDEX_V ( p_level , ( int ) _call_stack_size , " " ) ;
return _get_stack_level ( p_level ) - > function - > get_source ( ) ;
2014-02-09 22:10:30 -03:00
}
2020-05-14 14:29:06 +02: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 ) {
2020-05-14 16:41:43 +02:00
if ( _debug_parse_err_line > = 0 ) {
2014-02-09 22:10:30 -03:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2025-05-23 19:32:01 +02:00
ERR_FAIL_INDEX ( p_level , ( int ) _call_stack_size ) ;
2014-02-09 22:10:30 -03:00
2025-05-23 19:32:01 +02:00
CallLevel * cl = _get_stack_level ( p_level ) ;
GDScriptFunction * f = cl - > function ;
2014-02-09 22:10:30 -03:00
2020-03-17 07:33:00 +01:00
List < Pair < StringName , int > > locals ;
2014-02-09 22:10:30 -03:00
2025-05-23 19:32:01 +02:00
f - > debug_get_stack_member_state ( * cl - > line , & locals ) ;
2021-07-24 15:46:25 +02:00
for ( const Pair < StringName , int > & E : locals ) {
2021-07-15 23:45:57 -04:00
p_locals - > push_back ( E . first ) ;
2025-05-23 19:32:01 +02:00
p_values - > push_back ( cl - > stack [ E . second ] ) ;
2017-03-05 16:44:50 +01:00
}
2014-02-09 22:10:30 -03:00
}
2020-05-14 14:29:06 +02: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 ) {
2020-05-14 16:41:43 +02:00
if ( _debug_parse_err_line > = 0 ) {
2014-02-09 22:10:30 -03:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2025-05-23 19:32:01 +02:00
ERR_FAIL_INDEX ( p_level , ( int ) _call_stack_size ) ;
2014-02-09 22:10:30 -03:00
2025-05-23 19:32:01 +02:00
CallLevel * cl = _get_stack_level ( p_level ) ;
GDScriptInstance * instance = cl - > instance ;
2014-02-09 22:10:30 -03:00
2020-05-14 16:41:43 +02:00
if ( ! instance ) {
2015-06-27 15:52:39 -03:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2022-09-29 12:53:28 +03:00
Ref < GDScript > scr = instance - > get_script ( ) ;
ERR_FAIL_COND ( scr . is_null ( ) ) ;
2014-02-09 22:10:30 -03:00
2022-09-29 12:53:28 +03:00
const HashMap < StringName , GDScript : : MemberInfo > & mi = scr - > debug_get_member_indices ( ) ;
2014-02-09 22:10:30 -03:00
2021-08-09 14:13:42 -06:00
for ( const KeyValue < StringName , GDScript : : MemberInfo > & E : mi ) {
p_members - > push_back ( E . key ) ;
p_values - > push_back ( instance - > debug_get_member_by_index ( E . value . index ) ) ;
2015-06-27 15:52:39 -03:00
}
2014-02-09 22:10:30 -03:00
}
2017-10-17 22:35:49 +08:00
ScriptInstance * GDScriptLanguage : : debug_get_stack_level_instance ( int p_level ) {
2020-05-14 16:41:43 +02:00
if ( _debug_parse_err_line > = 0 ) {
2020-04-02 01:20:12 +02:00
return nullptr ;
2020-05-14 16:41:43 +02:00
}
2020-01-16 22:12:24 +02:00
2025-05-23 19:32:01 +02:00
ERR_FAIL_INDEX_V ( p_level , ( int ) _call_stack_size , nullptr ) ;
2017-10-17 22:35:49 +08:00
2025-05-23 19:32:01 +02:00
return _get_stack_level ( p_level ) - > instance ;
2017-10-17 22:35:49 +08:00
}
void GDScriptLanguage : : debug_get_globals ( List < String > * p_globals , List < Variant > * p_values , int p_max_subitems , int p_max_depth ) {
2022-05-13 15:04:37 +02:00
const HashMap < StringName , int > & name_idx = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) ;
2022-09-29 12:53:28 +03:00
const Variant * gl_array = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) ;
2017-10-17 22:35:49 +08:00
2020-03-17 07:33:00 +01:00
List < Pair < String , Variant > > cinfo ;
2017-11-16 17:18:36 +08:00
get_public_constants ( & cinfo ) ;
2021-08-09 14:13:42 -06:00
for ( const KeyValue < StringName , int > & E : name_idx ) {
if ( ClassDB : : class_exists ( E . key ) | | Engine : : get_singleton ( ) - > has_singleton ( E . key ) ) {
2017-11-16 17:18:36 +08:00
continue ;
2020-05-14 16:41:43 +02:00
}
2017-11-16 17:18:36 +08:00
bool is_script_constant = false ;
2020-03-17 07:33:00 +01:00
for ( List < Pair < String , Variant > > : : Element * CE = cinfo . front ( ) ; CE ; CE = CE - > next ( ) ) {
2021-08-09 14:13:42 -06:00
if ( CE - > get ( ) . first = = E . key ) {
2017-11-16 17:18:36 +08:00
is_script_constant = true ;
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( is_script_constant ) {
2017-10-17 22:35:49 +08:00
continue ;
2020-05-14 16:41:43 +02:00
}
2017-10-17 22:35:49 +08:00
2022-09-29 12:53:28 +03:00
const Variant & var = gl_array [ E . value ] ;
2024-03-08 07:32:31 +01:00
bool freed = false ;
const Object * obj = var . get_validated_object_with_check ( freed ) ;
if ( obj & & ! freed ) {
2020-05-14 16:41:43 +02:00
if ( Object : : cast_to < GDScriptNativeClass > ( obj ) ) {
2017-10-17 22:35:49 +08:00
continue ;
2020-05-14 16:41:43 +02:00
}
2017-10-17 22:35:49 +08:00
}
bool skip = false ;
2020-11-07 19:33:38 -03:00
for ( int i = 0 ; i < CoreConstants : : get_global_constant_count ( ) ; i + + ) {
2021-08-09 14:13:42 -06:00
if ( E . key = = CoreConstants : : get_global_constant_name ( i ) ) {
2017-10-17 22:35:49 +08:00
skip = true ;
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( skip ) {
2017-10-17 22:35:49 +08:00
continue ;
2020-05-14 16:41:43 +02:00
}
2017-10-17 22:35:49 +08:00
2021-08-09 14:13:42 -06:00
p_globals - > push_back ( E . key ) ;
2017-10-17 22:35:49 +08:00
p_values - > push_back ( var ) ;
}
2014-02-09 22:10:30 -03:00
}
2017-10-17 22:35:49 +08: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 ) {
2024-09-19 22:35:51 -07:00
List < String > names ;
List < Variant > values ;
debug_get_stack_level_locals ( p_level , & names , & values , p_max_subitems , p_max_depth ) ;
Vector < String > name_vector ;
for ( const String & name : names ) {
name_vector . push_back ( name ) ;
}
Array value_array ;
for ( const Variant & value : values ) {
value_array . push_back ( value ) ;
}
Expression expression ;
if ( expression . parse ( p_expression , name_vector ) = = OK ) {
ScriptInstance * instance = debug_get_stack_level_instance ( p_level ) ;
if ( instance ) {
Variant return_val = expression . execute ( value_array , instance - > get_owner ( ) ) ;
return return_val . get_construct_string ( ) ;
}
}
return String ( ) ;
2014-02-09 22:10:30 -03:00
}
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 {
2020-11-26 11:56:32 -03:00
List < StringName > functions ;
GDScriptUtilityFunctions : : get_function_list ( & functions ) ;
2021-07-15 23:45:57 -04:00
for ( const StringName & E : functions ) {
p_functions - > push_back ( GDScriptUtilityFunctions : : get_function_info ( E ) ) ;
2014-02-09 22:10:30 -03:00
}
2016-02-09 16:58:03 -03:00
2020-11-26 11:56:32 -03:00
// Not really "functions", but show in documentation.
2016-02-09 16:58:03 -03:00
{
MethodInfo mi ;
2017-08-29 07:15:46 +02:00
mi . name = " preload " ;
2017-03-05 16:44:50 +01:00
mi . arguments . push_back ( PropertyInfo ( Variant : : STRING , " path " ) ) ;
mi . return_val = PropertyInfo ( Variant : : OBJECT , " " , PROPERTY_HINT_RESOURCE_TYPE , " Resource " ) ;
2016-02-09 16:58:03 -03:00
p_functions - > push_back ( mi ) ;
}
{
MethodInfo mi ;
2017-03-05 16:44:50 +01:00
mi . name = " assert " ;
2017-08-29 07:15:46 +02:00
mi . return_val . type = Variant : : NIL ;
2017-03-05 16:44:50 +01:00
mi . arguments . push_back ( PropertyInfo ( Variant : : BOOL , " condition " ) ) ;
2020-03-31 10:39:23 +02:00
mi . arguments . push_back ( PropertyInfo ( Variant : : STRING , " message " ) ) ;
mi . default_arguments . push_back ( String ( ) ) ;
2016-02-09 16:58:03 -03:00
p_functions - > push_back ( mi ) ;
}
2014-02-09 22:10:30 -03:00
}
2020-03-17 07:33:00 +01:00
void GDScriptLanguage : : get_public_constants ( List < Pair < String , Variant > > * p_constants ) const {
2017-03-05 16:44:50 +01:00
Pair < String , Variant > pi ;
pi . first = " PI " ;
2025-04-10 11:21:05 -05:00
pi . second = Math : : PI ;
2014-02-22 20:28:19 -03:00
p_constants - > push_back ( pi ) ;
2017-02-06 22:44:22 +00:00
2017-11-04 10:34:27 +01:00
Pair < String , Variant > tau ;
tau . first = " TAU " ;
2025-04-10 11:21:05 -05:00
tau . second = Math : : TAU ;
2017-11-04 10:34:27 +01:00
p_constants - > push_back ( tau ) ;
2017-02-06 22:44:22 +00:00
Pair < String , Variant > infinity ;
infinity . first = " INF " ;
2025-04-10 11:21:05 -05:00
infinity . second = Math : : INF ;
2017-02-06 22:44:22 +00:00
p_constants - > push_back ( infinity ) ;
Pair < String , Variant > nan ;
nan . first = " NAN " ;
2025-04-10 11:21:05 -05:00
nan . second = Math : : NaN ;
2017-02-06 22:44:22 +00:00
p_constants - > push_back ( nan ) ;
2014-02-22 20:28:19 -03:00
}
2022-07-04 18:56:34 +03:00
void GDScriptLanguage : : get_public_annotations ( List < MethodInfo > * p_annotations ) const {
GDScriptParser parser ;
List < MethodInfo > annotations ;
parser . get_annotation_list ( & annotations ) ;
for ( const MethodInfo & E : annotations ) {
p_annotations - > push_back ( E ) ;
}
}
2020-02-17 18:06:54 -03:00
String GDScriptLanguage : : make_function ( const String & p_class , const String & p_name , const PackedStringArray & p_args ) const {
2018-05-29 23:16:59 -03:00
# ifdef TOOLS_ENABLED
2025-06-23 21:46:03 +03:00
const bool type_hints = EditorSettings : : get_singleton ( ) - > get_setting ( " text_editor/completion/add_type_hints " ) ;
2018-05-29 23:16:59 -03:00
# else
2025-06-23 21:46:03 +03:00
const bool type_hints = true ;
2018-05-29 23:16:59 -03:00
# endif
2025-06-23 21:46:03 +03:00
String result = " func " + p_name + " ( " ;
2014-02-09 22:10:30 -03:00
if ( p_args . size ( ) ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_args . size ( ) ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( i > 0 ) {
2025-06-23 21:46:03 +03:00
result + = " , " ;
2020-05-14 16:41:43 +02:00
}
2025-06-23 21:46:03 +03:00
const String name_unstripped = p_args [ i ] . get_slicec ( ' : ' , 0 ) ;
result + = name_unstripped . strip_edges ( ) ;
if ( type_hints ) {
2025-06-28 13:39:59 +03:00
const String type_stripped = p_args [ i ] . substr ( name_unstripped . length ( ) + 1 ) . strip_edges ( ) ;
2025-06-23 21:46:03 +03:00
if ( ! type_stripped . is_empty ( ) ) {
result + = " : " + type_stripped ;
2018-05-29 23:16:59 -03:00
}
}
2014-02-09 22:10:30 -03:00
}
}
2025-06-23 21:46:03 +03:00
result + = String ( " ) " ) + ( type_hints ? " -> void " : " " ) + " : \n " +
_get_indentation ( ) + " pass # Replace with function body. \n " ;
2014-02-09 22:10:30 -03:00
2025-06-23 21:46:03 +03:00
return result ;
2014-02-09 22:10:30 -03:00
}
2018-06-20 22:41:26 -03:00
//////// COMPLETION //////////
2014-02-09 22:10:30 -03:00
2020-07-06 12:24:24 -03:00
# ifdef TOOLS_ENABLED
2020-08-26 15:38:23 -03:00
# define COMPLETION_RECURSION_LIMIT 200
2020-07-06 12:24:24 -03:00
struct GDScriptCompletionIdentifier {
GDScriptParser : : DataType type ;
String enumeration ;
Variant value ;
const GDScriptParser : : ExpressionNode * assigned_expression = nullptr ;
} ;
2022-03-09 00:03:36 +10:00
// LOCATION METHODS
// These methods are used to populate the `CodeCompletionOption::location` integer.
// For these methods, the location is based on the depth in the inheritance chain that the property
// appears. For example, if you are completing code in a class that inherits Node2D, a property found on Node2D
// will have a "better" (lower) location "score" than a property that is found on CanvasItem.
2021-08-01 16:47:20 -03:00
static int _get_property_location ( const StringName & p_class , const StringName & p_property ) {
2022-03-09 00:03:36 +10:00
if ( ! ClassDB : : has_property ( p_class , p_property ) ) {
return ScriptLanguage : : LOCATION_OTHER ;
}
int depth = 0 ;
StringName class_test = p_class ;
while ( class_test & & ! ClassDB : : has_property ( class_test , p_property , true ) ) {
class_test = ClassDB : : get_parent_class ( class_test ) ;
depth + + ;
}
return depth | ScriptLanguage : : LOCATION_PARENT_MASK ;
}
2023-11-22 20:47:36 +01:00
static int _get_property_location ( Ref < Script > p_script , const StringName & p_property ) {
int depth = 0 ;
Ref < Script > scr = p_script ;
while ( scr . is_valid ( ) ) {
if ( scr - > get_member_line ( p_property ) ! = - 1 ) {
return depth | ScriptLanguage : : LOCATION_PARENT_MASK ;
}
depth + + ;
scr = scr - > get_base_script ( ) ;
}
return depth + _get_property_location ( p_script - > get_instance_base_type ( ) , p_property ) ;
}
2021-08-01 16:47:20 -03:00
static int _get_constant_location ( const StringName & p_class , const StringName & p_constant ) {
2022-03-09 00:03:36 +10:00
if ( ! ClassDB : : has_integer_constant ( p_class , p_constant ) ) {
return ScriptLanguage : : LOCATION_OTHER ;
}
int depth = 0 ;
StringName class_test = p_class ;
while ( class_test & & ! ClassDB : : has_integer_constant ( class_test , p_constant , true ) ) {
class_test = ClassDB : : get_parent_class ( class_test ) ;
depth + + ;
}
return depth | ScriptLanguage : : LOCATION_PARENT_MASK ;
}
2023-11-22 20:47:36 +01:00
static int _get_constant_location ( Ref < Script > p_script , const StringName & p_constant ) {
int depth = 0 ;
Ref < Script > scr = p_script ;
while ( scr . is_valid ( ) ) {
if ( scr - > get_member_line ( p_constant ) ! = - 1 ) {
return depth | ScriptLanguage : : LOCATION_PARENT_MASK ;
}
depth + + ;
scr = scr - > get_base_script ( ) ;
}
return depth + _get_constant_location ( p_script - > get_instance_base_type ( ) , p_constant ) ;
}
2021-08-01 16:47:20 -03:00
static int _get_signal_location ( const StringName & p_class , const StringName & p_signal ) {
2022-03-09 00:03:36 +10:00
if ( ! ClassDB : : has_signal ( p_class , p_signal ) ) {
return ScriptLanguage : : LOCATION_OTHER ;
}
int depth = 0 ;
StringName class_test = p_class ;
while ( class_test & & ! ClassDB : : has_signal ( class_test , p_signal , true ) ) {
class_test = ClassDB : : get_parent_class ( class_test ) ;
depth + + ;
}
return depth | ScriptLanguage : : LOCATION_PARENT_MASK ;
}
2023-11-22 20:47:36 +01:00
static int _get_signal_location ( Ref < Script > p_script , const StringName & p_signal ) {
int depth = 0 ;
Ref < Script > scr = p_script ;
while ( scr . is_valid ( ) ) {
if ( scr - > get_member_line ( p_signal ) ! = - 1 ) {
return depth | ScriptLanguage : : LOCATION_PARENT_MASK ;
}
depth + + ;
scr = scr - > get_base_script ( ) ;
}
return depth + _get_signal_location ( p_script - > get_instance_base_type ( ) , p_signal ) ;
}
2021-08-01 16:47:20 -03:00
static int _get_method_location ( const StringName & p_class , const StringName & p_method ) {
2022-03-09 00:03:36 +10:00
if ( ! ClassDB : : has_method ( p_class , p_method ) ) {
return ScriptLanguage : : LOCATION_OTHER ;
}
int depth = 0 ;
StringName class_test = p_class ;
while ( class_test & & ! ClassDB : : has_method ( class_test , p_method , true ) ) {
class_test = ClassDB : : get_parent_class ( class_test ) ;
depth + + ;
}
return depth | ScriptLanguage : : LOCATION_PARENT_MASK ;
}
2021-08-01 16:47:20 -03:00
static int _get_enum_constant_location ( const StringName & p_class , const StringName & p_enum_constant ) {
2022-03-09 00:03:36 +10:00
if ( ! ClassDB : : get_integer_constant_enum ( p_class , p_enum_constant ) ) {
return ScriptLanguage : : LOCATION_OTHER ;
}
int depth = 0 ;
StringName class_test = p_class ;
while ( class_test & & ! ClassDB : : get_integer_constant_enum ( class_test , p_enum_constant , true ) ) {
class_test = ClassDB : : get_parent_class ( class_test ) ;
depth + + ;
}
return depth | ScriptLanguage : : LOCATION_PARENT_MASK ;
}
2024-03-15 20:20:36 +00:00
static int _get_enum_location ( const StringName & p_class , const StringName & p_enum ) {
if ( ! ClassDB : : has_enum ( p_class , p_enum ) ) {
return ScriptLanguage : : LOCATION_OTHER ;
}
int depth = 0 ;
StringName class_test = p_class ;
while ( class_test & & ! ClassDB : : has_enum ( class_test , p_enum , true ) ) {
class_test = ClassDB : : get_parent_class ( class_test ) ;
depth + + ;
}
return depth | ScriptLanguage : : LOCATION_PARENT_MASK ;
}
2022-03-09 00:03:36 +10:00
// END LOCATION METHODS
2023-04-05 17:46:22 +03:00
static String _trim_parent_class ( const String & p_class , const String & p_base_class ) {
if ( p_base_class . is_empty ( ) ) {
return p_class ;
}
Vector < String > names = p_class . split ( " . " , false , 1 ) ;
if ( names . size ( ) = = 2 ) {
2023-11-18 17:40:56 -05:00
const String & first = names [ 0 ] ;
2023-04-05 17:46:22 +03:00
if ( ClassDB : : class_exists ( p_base_class ) & & ClassDB : : class_exists ( first ) & & ClassDB : : is_parent_class ( p_base_class , first ) ) {
2023-11-18 17:40:56 -05:00
const String & rest = names [ 1 ] ;
2023-04-05 17:46:22 +03:00
return rest ;
2020-07-06 12:24:24 -03:00
}
}
2023-04-05 17:46:22 +03:00
return p_class ;
}
2020-07-06 12:24:24 -03:00
2023-04-05 17:46:22 +03:00
static String _get_visual_datatype ( const PropertyInfo & p_info , bool p_is_arg , const String & p_base_class = " " ) {
String class_name = p_info . class_name ;
bool is_enum = p_info . type = = Variant : : INT & & p_info . usage & PROPERTY_USAGE_CLASS_IS_ENUM ;
// PROPERTY_USAGE_CLASS_IS_BITFIELD: BitField[T] isn't supported (yet?), use plain int.
2020-07-06 12:24:24 -03:00
2023-04-05 17:46:22 +03:00
if ( ( p_info . type = = Variant : : OBJECT | | is_enum ) & & ! class_name . is_empty ( ) ) {
if ( is_enum & & CoreConstants : : is_global_enum ( p_info . class_name ) ) {
return class_name ;
2020-07-06 12:24:24 -03:00
}
2023-04-05 17:46:22 +03:00
return _trim_parent_class ( class_name , p_base_class ) ;
} else if ( p_info . type = = Variant : : ARRAY & & p_info . hint = = PROPERTY_HINT_ARRAY_TYPE & & ! p_info . hint_string . is_empty ( ) ) {
return " Array[ " + _trim_parent_class ( p_info . hint_string , p_base_class ) + " ] " ;
2023-06-24 13:03:28 -05:00
} else if ( p_info . type = = Variant : : DICTIONARY & & p_info . hint = = PROPERTY_HINT_DICTIONARY_TYPE & & ! p_info . hint_string . is_empty ( ) ) {
2024-11-16 17:16:07 +01:00
const String key = p_info . hint_string . get_slicec ( ' ; ' , 0 ) ;
const String value = p_info . hint_string . get_slicec ( ' ; ' , 1 ) ;
2023-06-24 13:03:28 -05:00
return " Dictionary[ " + _trim_parent_class ( key , p_base_class ) + " , " + _trim_parent_class ( value , p_base_class ) + " ] " ;
2023-04-05 17:46:22 +03:00
} else if ( p_info . type = = Variant : : NIL ) {
2020-07-06 12:24:24 -03:00
if ( p_is_arg | | ( p_info . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) ) {
return " Variant " ;
} else {
return " void " ;
}
}
return Variant : : get_type_name ( p_info . type ) ;
}
static String _make_arguments_hint ( const MethodInfo & p_info , int p_arg_idx , bool p_is_annotation = false ) {
String arghint ;
if ( ! p_is_annotation ) {
arghint + = _get_visual_datatype ( p_info . return_val , false ) + " " ;
}
arghint + = p_info . name + " ( " ;
int def_args = p_info . arguments . size ( ) - p_info . default_arguments . size ( ) ;
int i = 0 ;
2021-07-15 23:45:57 -04:00
for ( const PropertyInfo & E : p_info . arguments ) {
2020-07-06 12:24:24 -03:00
if ( i > 0 ) {
arghint + = " , " ;
}
if ( i = = p_arg_idx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
2021-07-15 23:45:57 -04:00
arghint + = E . name + " : " + _get_visual_datatype ( E , true ) ;
2020-07-06 12:24:24 -03:00
if ( i - def_args > = 0 ) {
arghint + = String ( " = " ) + p_info . default_arguments [ i - def_args ] . get_construct_string ( ) ;
}
if ( i = = p_arg_idx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
i + + ;
}
if ( p_info . flags & METHOD_FLAG_VARARG ) {
if ( p_info . arguments . size ( ) > 0 ) {
arghint + = " , " ;
}
if ( p_arg_idx > = p_info . arguments . size ( ) ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
2025-03-30 12:59:05 +03:00
arghint + = " ...args: Array " ; // `MethodInfo` does not support the rest parameter name.
2020-07-06 12:24:24 -03:00
if ( p_arg_idx > = p_info . arguments . size ( ) ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
}
arghint + = " ) " ;
return arghint ;
}
2024-05-22 13:47:19 +03:00
static String _make_arguments_hint ( const GDScriptParser : : FunctionNode * p_function , int p_arg_idx , bool p_just_args = false ) {
2022-07-15 05:36:25 +02:00
String arghint ;
2024-05-22 13:47:19 +03:00
if ( p_just_args ) {
arghint = " ( " ;
2022-07-15 05:36:25 +02:00
} else {
2024-05-22 13:47:19 +03:00
if ( p_function - > get_datatype ( ) . builtin_type = = Variant : : NIL ) {
2025-03-30 12:59:05 +03:00
arghint = " void " + p_function - > identifier - > name + " ( " ;
2024-05-22 13:47:19 +03:00
} else {
2025-03-30 12:59:05 +03:00
arghint = p_function - > get_datatype ( ) . to_string ( ) + " " + p_function - > identifier - > name + " ( " ;
2024-05-22 13:47:19 +03:00
}
2022-07-15 05:36:25 +02:00
}
2020-07-06 12:24:24 -03:00
for ( int i = 0 ; i < p_function - > parameters . size ( ) ; i + + ) {
if ( i > 0 ) {
arghint + = " , " ;
}
if ( i = = p_arg_idx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
const GDScriptParser : : ParameterNode * par = p_function - > parameters [ i ] ;
2022-07-15 04:41:47 +02:00
if ( ! par - > get_datatype ( ) . is_hard_type ( ) ) {
arghint + = par - > identifier - > name . operator String ( ) + " : Variant " ;
} else {
arghint + = par - > identifier - > name . operator String ( ) + " : " + par - > get_datatype ( ) . to_string ( ) ;
}
2020-07-06 12:24:24 -03:00
2022-12-22 22:43:36 +02:00
if ( par - > initializer ) {
2020-07-06 12:24:24 -03:00
String def_val = " <unknown> " ;
2022-12-22 22:43:36 +02:00
switch ( par - > initializer - > type ) {
2021-12-29 21:49:46 +05:00
case GDScriptParser : : Node : : LITERAL : {
2022-12-22 22:43:36 +02:00
const GDScriptParser : : LiteralNode * literal = static_cast < const GDScriptParser : : LiteralNode * > ( par - > initializer ) ;
2021-12-29 21:49:46 +05:00
def_val = literal - > value . get_construct_string ( ) ;
} break ;
case GDScriptParser : : Node : : IDENTIFIER : {
2022-12-22 22:43:36 +02:00
const GDScriptParser : : IdentifierNode * id = static_cast < const GDScriptParser : : IdentifierNode * > ( par - > initializer ) ;
2021-12-29 21:49:46 +05:00
def_val = id - > name . operator String ( ) ;
} break ;
case GDScriptParser : : Node : : CALL : {
2022-12-22 22:43:36 +02:00
const GDScriptParser : : CallNode * call = static_cast < const GDScriptParser : : CallNode * > ( par - > initializer ) ;
2021-12-29 21:49:46 +05:00
if ( call - > is_constant & & call - > reduced ) {
2024-07-23 19:35:42 +03:00
def_val = call - > reduced_value . get_construct_string ( ) ;
2025-06-23 19:18:22 +03:00
} else if ( call - > get_callee_type ( ) = = GDScriptParser : : Node : : IDENTIFIER ) {
2024-02-11 10:01:03 -05:00
def_val = call - > function_name . operator String ( ) + ( call - > arguments . is_empty ( ) ? " () " : " (...) " ) ;
2021-12-29 21:49:46 +05:00
}
} break ;
case GDScriptParser : : Node : : ARRAY : {
2022-12-22 22:43:36 +02:00
const GDScriptParser : : ArrayNode * arr = static_cast < const GDScriptParser : : ArrayNode * > ( par - > initializer ) ;
2021-12-29 21:49:46 +05:00
if ( arr - > is_constant & & arr - > reduced ) {
2024-07-23 19:35:42 +03:00
def_val = arr - > reduced_value . get_construct_string ( ) ;
2024-02-11 10:01:03 -05:00
} else {
def_val = arr - > elements . is_empty ( ) ? " [] " : " [...] " ;
2021-12-29 21:49:46 +05:00
}
} break ;
case GDScriptParser : : Node : : DICTIONARY : {
2022-12-22 22:43:36 +02:00
const GDScriptParser : : DictionaryNode * dict = static_cast < const GDScriptParser : : DictionaryNode * > ( par - > initializer ) ;
2021-12-29 21:49:46 +05:00
if ( dict - > is_constant & & dict - > reduced ) {
2024-07-23 19:35:42 +03:00
def_val = dict - > reduced_value . get_construct_string ( ) ;
2024-02-11 10:01:03 -05:00
} else {
def_val = dict - > elements . is_empty ( ) ? " {} " : " {...} " ;
2021-12-29 21:49:46 +05:00
}
} break ;
case GDScriptParser : : Node : : SUBSCRIPT : {
2022-12-22 22:43:36 +02:00
const GDScriptParser : : SubscriptNode * sub = static_cast < const GDScriptParser : : SubscriptNode * > ( par - > initializer ) ;
2024-07-23 19:35:42 +03:00
if ( sub - > is_attribute & & sub - > datatype . kind = = GDScriptParser : : DataType : : ENUM & & ! sub - > datatype . is_meta_type ) {
def_val = sub - > get_datatype ( ) . to_string ( ) + " . " + sub - > attribute - > name ;
} else if ( sub - > is_constant & & sub - > reduced ) {
def_val = sub - > reduced_value . get_construct_string ( ) ;
2021-12-29 21:49:46 +05:00
}
} break ;
default :
break ;
2020-07-06 12:24:24 -03:00
}
arghint + = " = " + def_val ;
}
if ( i = = p_arg_idx ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
}
2025-03-30 12:59:05 +03:00
if ( p_function - > is_vararg ( ) ) {
if ( ! p_function - > parameters . is_empty ( ) ) {
arghint + = " , " ;
}
if ( p_arg_idx > = p_function - > parameters . size ( ) ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
const GDScriptParser : : ParameterNode * rest_param = p_function - > rest_parameter ;
arghint + = " ... " + rest_param - > identifier - > name + " : " + rest_param - > get_datatype ( ) . to_string ( ) ;
if ( p_arg_idx > = p_function - > parameters . size ( ) ) {
arghint + = String : : chr ( 0xFFFF ) ;
}
}
2020-07-06 12:24:24 -03:00
arghint + = " ) " ;
return arghint ;
}
2024-01-15 00:48:06 +01:00
static void _get_directory_contents ( EditorFileSystemDirectory * p_dir , HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_list , const StringName & p_required_type = StringName ( ) ) {
2021-08-13 21:24:02 +02:00
const String quote_style = EDITOR_GET ( " text_editor/completion/use_single_quotes " ) ? " ' " : " \" " ;
2025-01-08 16:04:11 +01:00
const bool requires_type = ! p_required_type . is_empty ( ) ;
2020-07-06 12:24:24 -03:00
for ( int i = 0 ; i < p_dir - > get_file_count ( ) ; i + + ) {
2024-01-15 00:48:06 +01:00
if ( requires_type & & ! ClassDB : : is_parent_class ( p_dir - > get_file_type ( i ) , p_required_type ) ) {
continue ;
}
2025-02-13 22:49:03 +01:00
ScriptLanguage : : CodeCompletionOption option ( p_dir - > get_file_path ( i ) . quote ( quote_style ) , ScriptLanguage : : CODE_COMPLETION_KIND_FILE_PATH ) ;
2020-07-06 12:24:24 -03:00
r_list . insert ( option . display , option ) ;
}
for ( int i = 0 ; i < p_dir - > get_subdir_count ( ) ; i + + ) {
2024-01-15 00:48:06 +01:00
_get_directory_contents ( p_dir - > get_subdir ( i ) , r_list , p_required_type ) ;
2020-07-06 12:24:24 -03:00
}
}
2024-09-07 20:47:34 +02:00
static void _find_annotation_arguments ( const GDScriptParser : : AnnotationNode * p_annotation , int p_argument , const String p_quote_style , HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result , String & r_arghint ) {
2025-02-11 09:17:22 +01:00
ERR_FAIL_NULL ( p_annotation ) ;
if ( p_annotation - > info ! = nullptr ) {
r_arghint = _make_arguments_hint ( p_annotation - > info - > info , p_argument , true ) ;
}
2022-02-06 14:12:19 +01:00
if ( p_annotation - > name = = SNAME ( " @export_range " ) ) {
2022-06-19 12:22:18 +02:00
if ( p_argument = = 3 | | p_argument = = 4 | | p_argument = = 5 ) {
2020-07-06 12:24:24 -03:00
// Slider hint.
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption slider1 ( " or_greater " , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2021-08-13 21:24:02 +02:00
slider1 . insert_text = slider1 . display . quote ( p_quote_style ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( slider1 . display , slider1 ) ;
2022-03-27 19:16:03 +02:00
ScriptLanguage : : CodeCompletionOption slider2 ( " or_less " , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2021-08-13 21:24:02 +02:00
slider2 . insert_text = slider2 . display . quote ( p_quote_style ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( slider2 . display , slider2 ) ;
2022-10-03 11:59:33 -05:00
ScriptLanguage : : CodeCompletionOption slider3 ( " hide_slider " , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2022-06-19 12:22:18 +02:00
slider3 . insert_text = slider3 . display . quote ( p_quote_style ) ;
r_result . insert ( slider3 . display , slider3 ) ;
2020-07-06 12:24:24 -03:00
}
2022-02-06 14:12:19 +01:00
} else if ( p_annotation - > name = = SNAME ( " @export_exp_easing " ) ) {
2020-07-06 12:24:24 -03:00
if ( p_argument = = 0 | | p_argument = = 1 ) {
// Easing hint.
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption hint1 ( " attenuation " , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2021-08-13 21:24:02 +02:00
hint1 . insert_text = hint1 . display . quote ( p_quote_style ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( hint1 . display , hint1 ) ;
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption hint2 ( " inout " , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2021-08-13 21:24:02 +02:00
hint2 . insert_text = hint2 . display . quote ( p_quote_style ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( hint2 . display , hint2 ) ;
}
2022-02-06 14:12:19 +01:00
} else if ( p_annotation - > name = = SNAME ( " @export_node_path " ) ) {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption node ( " Node " , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS ) ;
2023-01-18 20:12:33 +03:00
node . insert_text = node . display . quote ( p_quote_style ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( node . display , node ) ;
2023-07-26 18:26:48 +03:00
2025-01-08 20:21:08 +08:00
LocalVector < StringName > native_classes ;
ClassDB : : get_inheriters_from_class ( " Node " , native_classes ) ;
2023-07-26 18:26:48 +03:00
for ( const StringName & E : native_classes ) {
2021-07-15 23:45:57 -04:00
if ( ! ClassDB : : is_class_exposed ( E ) ) {
2020-07-06 12:24:24 -03:00
continue ;
}
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS ) ;
2023-01-18 20:12:33 +03:00
option . insert_text = option . display . quote ( p_quote_style ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
2023-07-26 18:26:48 +03:00
List < StringName > global_script_classes ;
ScriptServer : : get_global_class_list ( & global_script_classes ) ;
for ( const StringName & E : global_script_classes ) {
if ( ! ClassDB : : is_parent_class ( ScriptServer : : get_global_class_native_base ( E ) , " Node " ) ) {
continue ;
}
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS ) ;
option . insert_text = option . display . quote ( p_quote_style ) ;
r_result . insert ( option . display , option ) ;
2020-07-06 12:24:24 -03:00
}
2025-04-06 08:21:43 +03:00
} else if ( p_annotation - > name = = SNAME ( " @export_tool_button " ) ) {
if ( p_argument = = 1 ) {
const Ref < Theme > theme = EditorNode : : get_singleton ( ) - > get_editor_theme ( ) ;
if ( theme . is_valid ( ) ) {
List < StringName > icon_list ;
theme - > get_icon_list ( EditorStringName ( EditorIcons ) , & icon_list ) ;
for ( const StringName & E : icon_list ) {
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS ) ;
option . insert_text = option . display . quote ( p_quote_style ) ;
r_result . insert ( option . display , option ) ;
}
}
}
2024-04-15 22:10:47 +03:00
} else if ( p_annotation - > name = = SNAME ( " @export_custom " ) ) {
switch ( p_argument ) {
case 0 : {
static HashMap < StringName , int64_t > items ;
if ( unlikely ( items . is_empty ( ) ) ) {
CoreConstants : : get_enum_values ( SNAME ( " PropertyHint " ) , & items ) ;
}
for ( const KeyValue < StringName , int64_t > & item : items ) {
ScriptLanguage : : CodeCompletionOption option ( item . key , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT ) ;
r_result . insert ( option . display , option ) ;
}
} break ;
case 2 : {
static HashMap < StringName , int64_t > items ;
if ( unlikely ( items . is_empty ( ) ) ) {
CoreConstants : : get_enum_values ( SNAME ( " PropertyUsageFlags " ) , & items ) ;
}
for ( const KeyValue < StringName , int64_t > & item : items ) {
ScriptLanguage : : CodeCompletionOption option ( item . key , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT ) ;
r_result . insert ( option . display , option ) ;
}
} break ;
}
2024-12-06 10:25:59 +03:00
} else if ( p_annotation - > name = = SNAME ( " @warning_ignore " ) | | p_annotation - > name = = SNAME ( " @warning_ignore_start " ) | | p_annotation - > name = = SNAME ( " @warning_ignore_restore " ) ) {
2022-01-04 09:32:43 -03:00
for ( int warning_code = 0 ; warning_code < GDScriptWarning : : WARNING_MAX ; warning_code + + ) {
2025-01-09 21:07:41 +03:00
# ifndef DISABLE_DEPRECATED
if ( warning_code > = GDScriptWarning : : FIRST_DEPRECATED_WARNING ) {
break ; // Don't suggest deprecated warnings as they are never produced.
2024-12-06 10:25:59 +03:00
}
2025-01-09 21:07:41 +03:00
# endif
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption warning ( GDScriptWarning : : get_name_from_code ( ( GDScriptWarning : : Code ) warning_code ) . to_lower ( ) , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2023-01-18 20:12:33 +03:00
warning . insert_text = warning . display . quote ( p_quote_style ) ;
2022-01-04 09:32:43 -03:00
r_result . insert ( warning . display , warning ) ;
}
2023-01-29 00:21:27 +03:00
} else if ( p_annotation - > name = = SNAME ( " @rpc " ) ) {
if ( p_argument = = 0 | | p_argument = = 1 | | p_argument = = 2 ) {
static const char * options [ 7 ] = { " call_local " , " call_remote " , " any_peer " , " authority " , " reliable " , " unreliable " , " unreliable_ordered " } ;
for ( int i = 0 ; i < 7 ; i + + ) {
ScriptLanguage : : CodeCompletionOption option ( options [ i ] , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
option . insert_text = option . display . quote ( p_quote_style ) ;
r_result . insert ( option . display , option ) ;
}
}
2020-07-06 12:24:24 -03:00
}
}
2022-05-13 15:04:37 +02:00
static void _find_built_in_variants ( HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result , bool exclude_nil = false ) {
2022-04-07 09:00:23 -05:00
for ( int i = 0 ; i < Variant : : VARIANT_MAX ; i + + ) {
if ( ! exclude_nil & & Variant : : Type ( i ) = = Variant : : Type : : NIL ) {
ScriptLanguage : : CodeCompletionOption option ( " null " , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS ) ;
r_result . insert ( option . display , option ) ;
} else {
ScriptLanguage : : CodeCompletionOption option ( Variant : : get_type_name ( Variant : : Type ( i ) ) , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS ) ;
r_result . insert ( option . display , option ) ;
}
}
}
2025-01-30 12:41:51 +01:00
static void _find_global_enums ( HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result ) {
List < StringName > global_enums ;
CoreConstants : : get_global_enums ( & global_enums ) ;
for ( const StringName & enum_name : global_enums ) {
ScriptLanguage : : CodeCompletionOption option ( enum_name , ScriptLanguage : : CODE_COMPLETION_KIND_ENUM , ScriptLanguage : : LOCATION_OTHER ) ;
r_result . insert ( option . display , option ) ;
}
}
2022-05-13 15:04:37 +02:00
static void _list_available_types ( bool p_inherit_only , GDScriptParser : : CompletionContext & p_context , HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result ) {
2022-04-07 09:00:23 -05:00
// Built-in Variant Types
_find_built_in_variants ( r_result , true ) ;
2020-07-06 12:24:24 -03:00
List < StringName > native_types ;
ClassDB : : get_class_list ( & native_types ) ;
2021-07-15 23:45:57 -04:00
for ( const StringName & E : native_types ) {
if ( ClassDB : : is_class_exposed ( E ) & & ! Engine : : get_singleton ( ) - > has_singleton ( E ) ) {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
}
2024-07-31 23:44:48 +02:00
// TODO: Unify with _find_identifiers_in_class.
2020-07-06 12:24:24 -03:00
if ( p_context . current_class ) {
if ( ! p_inherit_only & & p_context . current_class - > base_type . is_set ( ) ) {
// Native enums from base class
List < StringName > enums ;
ClassDB : : get_enum_list ( p_context . current_class - > base_type . native_type , & enums ) ;
2021-07-15 23:45:57 -04:00
for ( const StringName & E : enums ) {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_ENUM ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
}
2024-07-31 23:44:48 +02:00
// Check current class for potential types.
// TODO: Also check classes the current class inherits from.
2020-07-06 12:24:24 -03:00
const GDScriptParser : : ClassNode * current = p_context . current_class ;
2024-07-31 23:44:48 +02:00
int location_offset = 0 ;
2020-07-06 12:24:24 -03:00
while ( current ) {
for ( int i = 0 ; i < current - > members . size ( ) ; i + + ) {
const GDScriptParser : : ClassNode : : Member & member = current - > members [ i ] ;
switch ( member . type ) {
case GDScriptParser : : ClassNode : : Member : : CLASS : {
2024-07-31 23:44:48 +02:00
ScriptLanguage : : CodeCompletionOption option ( member . m_class - > identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS , ScriptLanguage : : LOCATION_LOCAL + location_offset ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
} break ;
case GDScriptParser : : ClassNode : : Member : : ENUM : {
if ( ! p_inherit_only ) {
2024-07-31 23:44:48 +02:00
ScriptLanguage : : CodeCompletionOption option ( member . m_enum - > identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_ENUM , ScriptLanguage : : LOCATION_LOCAL + location_offset ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
} break ;
case GDScriptParser : : ClassNode : : Member : : CONSTANT : {
2024-07-31 23:44:48 +02:00
if ( member . constant - > get_datatype ( ) . is_meta_type ) {
ScriptLanguage : : CodeCompletionOption option ( member . constant - > identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS , ScriptLanguage : : LOCATION_LOCAL + location_offset ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
} break ;
default :
break ;
}
}
2024-07-31 23:44:48 +02:00
location_offset + = 1 ;
2020-07-06 12:24:24 -03:00
current = current - > outer ;
}
}
// Global scripts
List < StringName > global_classes ;
ScriptServer : : get_global_class_list ( & global_classes ) ;
2021-07-15 23:45:57 -04:00
for ( const StringName & E : global_classes ) {
2022-03-09 00:03:36 +10:00
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS , ScriptLanguage : : LOCATION_OTHER_USER_CODE ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
2025-01-30 12:41:51 +01:00
// Global enums
if ( ! p_inherit_only ) {
_find_global_enums ( r_result ) ;
}
2020-07-06 12:24:24 -03:00
// Autoload singletons
2022-05-08 10:09:19 +02:00
HashMap < StringName , ProjectSettings : : AutoloadInfo > autoloads = ProjectSettings : : get_singleton ( ) - > get_autoload_list ( ) ;
for ( const KeyValue < StringName , ProjectSettings : : AutoloadInfo > & E : autoloads ) {
const ProjectSettings : : AutoloadInfo & info = E . value ;
2020-07-06 12:24:24 -03:00
if ( ! info . is_singleton | | info . path . get_extension ( ) . to_lower ( ) ! = " gd " ) {
continue ;
}
2022-03-09 00:03:36 +10:00
ScriptLanguage : : CodeCompletionOption option ( info . name , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS , ScriptLanguage : : LOCATION_OTHER_USER_CODE ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
}
2023-05-23 05:12:34 +02:00
static void _find_identifiers_in_suite ( const GDScriptParser : : SuiteNode * p_suite , HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result , int p_recursion_depth = 0 ) {
2020-07-06 12:24:24 -03:00
for ( int i = 0 ; i < p_suite - > locals . size ( ) ; i + + ) {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ;
2023-05-23 05:12:34 +02:00
int location = p_recursion_depth = = 0 ? ScriptLanguage : : LOCATION_LOCAL : ( p_recursion_depth | ScriptLanguage : : LOCATION_PARENT_MASK ) ;
2021-04-11 22:30:16 +08:00
if ( p_suite - > locals [ i ] . type = = GDScriptParser : : SuiteNode : : Local : : CONSTANT ) {
2023-05-23 05:12:34 +02:00
option = ScriptLanguage : : CodeCompletionOption ( p_suite - > locals [ i ] . name , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT , location ) ;
2021-05-19 14:40:03 +03:00
option . default_value = p_suite - > locals [ i ] . constant - > initializer - > reduced_value ;
2021-04-11 22:30:16 +08:00
} else {
2023-05-23 05:12:34 +02:00
option = ScriptLanguage : : CodeCompletionOption ( p_suite - > locals [ i ] . name , ScriptLanguage : : CODE_COMPLETION_KIND_VARIABLE , location ) ;
2021-04-11 22:30:16 +08:00
}
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
if ( p_suite - > parent_block ) {
2023-05-23 05:12:34 +02:00
_find_identifiers_in_suite ( p_suite - > parent_block , r_result , p_recursion_depth + 1 ) ;
2020-07-06 12:24:24 -03:00
}
}
2024-08-31 12:12:19 +02:00
static void _find_identifiers_in_base ( const GDScriptCompletionIdentifier & p_base , bool p_only_functions , bool p_types_only , bool p_add_braces , HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result , int p_recursion_depth ) ;
2020-08-26 15:38:23 -03:00
2024-08-31 12:12:19 +02:00
static void _find_identifiers_in_class ( const GDScriptParser : : ClassNode * p_class , bool p_only_functions , bool p_types_only , bool p_static , bool p_parent_only , bool p_add_braces , HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result , int p_recursion_depth ) {
2020-08-26 15:38:23 -03:00
ERR_FAIL_COND ( p_recursion_depth > COMPLETION_RECURSION_LIMIT ) ;
2020-07-06 12:24:24 -03:00
if ( ! p_parent_only ) {
bool outer = false ;
const GDScriptParser : : ClassNode * clss = p_class ;
2022-03-09 00:03:36 +10:00
int classes_processed = 0 ;
2020-07-06 12:24:24 -03:00
while ( clss ) {
for ( int i = 0 ; i < clss - > members . size ( ) ; i + + ) {
2023-05-23 05:12:34 +02:00
const int location = p_recursion_depth = = 0 ? classes_processed : ( p_recursion_depth | ScriptLanguage : : LOCATION_PARENT_MASK ) ;
2020-07-06 12:24:24 -03:00
const GDScriptParser : : ClassNode : : Member & member = clss - > members [ i ] ;
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ;
2020-07-06 12:24:24 -03:00
switch ( member . type ) {
case GDScriptParser : : ClassNode : : Member : : VARIABLE :
2023-11-06 19:08:12 +03:00
if ( p_types_only | | p_only_functions | | outer | | ( p_static & & ! member . variable - > is_static ) ) {
2020-07-06 12:24:24 -03:00
continue ;
}
2022-03-09 00:03:36 +10:00
option = ScriptLanguage : : CodeCompletionOption ( member . variable - > identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_MEMBER , location ) ;
2020-07-06 12:24:24 -03:00
break ;
case GDScriptParser : : ClassNode : : Member : : CONSTANT :
2024-07-31 23:44:48 +02:00
if ( ( p_types_only & & ! member . constant - > datatype . is_meta_type ) | | p_only_functions ) {
2020-07-06 12:24:24 -03:00
continue ;
}
2021-09-19 22:51:14 +03:00
if ( r_result . has ( member . constant - > identifier - > name ) ) {
continue ;
}
2022-03-09 00:03:36 +10:00
option = ScriptLanguage : : CodeCompletionOption ( member . constant - > identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT , location ) ;
2020-10-22 23:12:00 +03:00
if ( member . constant - > initializer ) {
option . default_value = member . constant - > initializer - > reduced_value ;
}
2020-07-06 12:24:24 -03:00
break ;
case GDScriptParser : : ClassNode : : Member : : CLASS :
if ( p_only_functions ) {
continue ;
}
2022-03-09 00:03:36 +10:00
option = ScriptLanguage : : CodeCompletionOption ( member . m_class - > identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS , location ) ;
2020-07-06 12:24:24 -03:00
break ;
case GDScriptParser : : ClassNode : : Member : : ENUM_VALUE :
2023-11-06 19:08:12 +03:00
if ( p_types_only | | p_only_functions ) {
2020-07-06 12:24:24 -03:00
continue ;
}
2022-03-09 00:03:36 +10:00
option = ScriptLanguage : : CodeCompletionOption ( member . enum_value . identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT , location ) ;
2020-07-06 12:24:24 -03:00
break ;
case GDScriptParser : : ClassNode : : Member : : ENUM :
if ( p_only_functions ) {
continue ;
}
2022-03-09 00:03:36 +10:00
option = ScriptLanguage : : CodeCompletionOption ( member . m_enum - > identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_ENUM , location ) ;
2020-07-06 12:24:24 -03:00
break ;
case GDScriptParser : : ClassNode : : Member : : FUNCTION :
2023-11-06 19:08:12 +03:00
if ( p_types_only | | outer | | ( p_static & & ! member . function - > is_static ) | | member . function - > identifier - > name . operator String ( ) . begins_with ( " @ " ) ) {
2020-07-06 12:24:24 -03:00
continue ;
}
2022-03-09 00:03:36 +10:00
option = ScriptLanguage : : CodeCompletionOption ( member . function - > identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION , location ) ;
2024-08-31 12:12:19 +02:00
if ( p_add_braces ) {
if ( member . function - > parameters . size ( ) > 0 | | ( member . function - > info . flags & METHOD_FLAG_VARARG ) ) {
option . insert_text + = " ( " ;
option . display + = U " ( \u2026 ) " ;
} else {
option . insert_text + = " () " ;
option . display + = " () " ;
}
2020-07-06 12:24:24 -03:00
}
break ;
case GDScriptParser : : ClassNode : : Member : : SIGNAL :
2023-11-06 19:08:12 +03:00
if ( p_types_only | | p_only_functions | | outer | | p_static ) {
2020-07-06 12:24:24 -03:00
continue ;
}
2022-03-09 00:03:36 +10:00
option = ScriptLanguage : : CodeCompletionOption ( member . signal - > identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_SIGNAL , location ) ;
2020-07-06 12:24:24 -03:00
break ;
2022-07-03 22:30:08 +03:00
case GDScriptParser : : ClassNode : : Member : : GROUP :
break ; // No-op, but silences warnings.
2020-07-06 12:24:24 -03:00
case GDScriptParser : : ClassNode : : Member : : UNDEFINED :
break ;
}
r_result . insert ( option . display , option ) ;
}
2023-11-06 19:08:12 +03:00
if ( p_types_only ) {
break ; // Otherwise, it will fill the results with types from the outer class (which is undesired for that case).
}
2020-07-06 12:24:24 -03:00
outer = true ;
clss = clss - > outer ;
2022-03-09 00:03:36 +10:00
classes_processed + + ;
2020-07-06 12:24:24 -03:00
}
}
// Parents.
GDScriptCompletionIdentifier base_type ;
base_type . type = p_class - > base_type ;
base_type . type . is_meta_type = p_static ;
2024-08-31 12:12:19 +02:00
_find_identifiers_in_base ( base_type , p_only_functions , p_types_only , p_add_braces , r_result , p_recursion_depth + 1 ) ;
2020-07-06 12:24:24 -03:00
}
2024-08-31 12:12:19 +02:00
static void _find_identifiers_in_base ( const GDScriptCompletionIdentifier & p_base , bool p_only_functions , bool p_types_only , bool p_add_braces , HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result , int p_recursion_depth ) {
2020-08-26 15:38:23 -03:00
ERR_FAIL_COND ( p_recursion_depth > COMPLETION_RECURSION_LIMIT ) ;
2020-07-06 12:24:24 -03:00
GDScriptParser : : DataType base_type = p_base . type ;
2023-11-06 19:08:12 +03:00
if ( ! p_types_only & & base_type . is_meta_type & & base_type . kind ! = GDScriptParser : : DataType : : BUILTIN & & base_type . kind ! = GDScriptParser : : DataType : : ENUM ) {
2022-04-06 14:14:38 -03:00
ScriptLanguage : : CodeCompletionOption option ( " new " , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION , ScriptLanguage : : LOCATION_LOCAL ) ;
2024-08-31 12:12:19 +02:00
if ( p_add_braces ) {
option . insert_text + = " ( " ;
option . display + = U " ( \u2026 ) " ;
}
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
while ( ! base_type . has_no_type ( ) ) {
switch ( base_type . kind ) {
case GDScriptParser : : DataType : : CLASS : {
2024-08-31 12:12:19 +02:00
_find_identifiers_in_class ( base_type . class_type , p_only_functions , p_types_only , base_type . is_meta_type , false , p_add_braces , r_result , p_recursion_depth ) ;
2020-07-06 12:24:24 -03:00
// This already finds all parent identifiers, so we are done.
base_type = GDScriptParser : : DataType ( ) ;
} break ;
case GDScriptParser : : DataType : : SCRIPT : {
Ref < Script > scr = base_type . script_type ;
if ( scr . is_valid ( ) ) {
2023-11-06 19:08:12 +03:00
if ( p_types_only ) {
// TODO: Need to implement Script::get_script_enum_list and retrieve the enum list from a script.
} else if ( ! p_only_functions ) {
2022-12-03 22:02:03 -05:00
if ( ! base_type . is_meta_type ) {
2020-07-06 12:24:24 -03:00
List < PropertyInfo > members ;
scr - > get_script_property_list ( & members ) ;
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & E : members ) {
2024-01-26 20:42:20 +01:00
if ( E . usage & ( PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL ) ) {
2023-11-21 23:30:51 +01:00
continue ;
}
2024-12-05 17:56:08 +01:00
if ( E . name . contains_char ( ' / ' ) ) {
2023-11-21 23:30:51 +01:00
continue ;
}
2023-11-22 20:47:36 +01:00
int location = p_recursion_depth + _get_property_location ( scr , E . name ) ;
2022-03-09 00:03:36 +10:00
ScriptLanguage : : CodeCompletionOption option ( E . name , ScriptLanguage : : CODE_COMPLETION_KIND_MEMBER , location ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
2022-12-13 12:41:29 +07:00
List < MethodInfo > signals ;
scr - > get_script_signal_list ( & signals ) ;
for ( const MethodInfo & E : signals ) {
2023-11-22 20:47:36 +01:00
int location = p_recursion_depth + _get_signal_location ( scr , E . name ) ;
2022-12-13 12:41:29 +07:00
ScriptLanguage : : CodeCompletionOption option ( E . name , ScriptLanguage : : CODE_COMPLETION_KIND_SIGNAL , location ) ;
r_result . insert ( option . display , option ) ;
}
2020-07-06 12:24:24 -03:00
}
2022-05-13 15:04:37 +02:00
HashMap < StringName , Variant > constants ;
2020-07-06 12:24:24 -03:00
scr - > get_constants ( & constants ) ;
2021-08-09 14:13:42 -06:00
for ( const KeyValue < StringName , Variant > & E : constants ) {
2023-11-22 20:47:36 +01:00
int location = p_recursion_depth + _get_constant_location ( scr , E . key ) ;
2022-03-09 00:03:36 +10:00
ScriptLanguage : : CodeCompletionOption option ( E . key . operator String ( ) , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT , location ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
}
2023-11-06 19:08:12 +03:00
if ( ! p_types_only ) {
List < MethodInfo > methods ;
scr - > get_script_method_list ( & methods ) ;
for ( const MethodInfo & E : methods ) {
if ( E . name . begins_with ( " @ " ) ) {
continue ;
}
int location = p_recursion_depth + _get_method_location ( scr - > get_class_name ( ) , E . name ) ;
ScriptLanguage : : CodeCompletionOption option ( E . name , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION , location ) ;
2024-08-31 12:12:19 +02:00
if ( p_add_braces ) {
if ( E . arguments . size ( ) | | ( E . flags & METHOD_FLAG_VARARG ) ) {
option . insert_text + = " ( " ;
option . display + = U " ( \u2026 ) " ;
} else {
option . insert_text + = " () " ;
option . display + = " () " ;
}
2023-11-06 19:08:12 +03:00
}
r_result . insert ( option . display , option ) ;
2020-07-06 12:24:24 -03:00
}
}
Ref < Script > base_script = scr - > get_base_script ( ) ;
if ( base_script . is_valid ( ) ) {
base_type . script_type = base_script ;
} else {
base_type . kind = GDScriptParser : : DataType : : NATIVE ;
2023-10-04 15:03:53 +03:00
base_type . builtin_type = Variant : : OBJECT ;
2020-07-06 12:24:24 -03:00
base_type . native_type = scr - > get_instance_base_type ( ) ;
}
} else {
return ;
}
} break ;
case GDScriptParser : : DataType : : NATIVE : {
2021-08-17 15:06:54 +02:00
StringName type = base_type . native_type ;
2020-07-06 12:24:24 -03:00
if ( ! ClassDB : : class_exists ( type ) ) {
return ;
}
2024-03-15 20:20:36 +00:00
List < StringName > enums ;
ClassDB : : get_enum_list ( type , & enums ) ;
for ( const StringName & E : enums ) {
int location = p_recursion_depth + _get_enum_location ( type , E ) ;
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_ENUM , location ) ;
r_result . insert ( option . display , option ) ;
}
2023-11-06 19:08:12 +03:00
if ( p_types_only ) {
return ;
}
2020-07-06 12:24:24 -03:00
if ( ! p_only_functions ) {
List < String > constants ;
ClassDB : : get_integer_constant_list ( type , & constants ) ;
2021-07-24 15:46:25 +02:00
for ( const String & E : constants ) {
2022-03-09 00:03:36 +10:00
int location = p_recursion_depth + _get_constant_location ( type , StringName ( E ) ) ;
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT , location ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
2022-12-03 22:02:03 -05:00
if ( ! base_type . is_meta_type | | Engine : : get_singleton ( ) - > has_singleton ( type ) ) {
2020-07-06 12:24:24 -03:00
List < PropertyInfo > pinfo ;
ClassDB : : get_property_list ( type , & pinfo ) ;
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & E : pinfo ) {
2024-01-26 20:42:20 +01:00
if ( E . usage & ( PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL ) ) {
2020-07-06 12:24:24 -03:00
continue ;
}
2024-12-05 17:56:08 +01:00
if ( E . name . contains_char ( ' / ' ) ) {
2020-07-06 12:24:24 -03:00
continue ;
}
2023-08-05 02:15:45 +02:00
int location = p_recursion_depth + _get_property_location ( type , E . name ) ;
2022-03-09 00:03:36 +10:00
ScriptLanguage : : CodeCompletionOption option ( E . name , ScriptLanguage : : CODE_COMPLETION_KIND_MEMBER , location ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
2022-12-13 12:41:29 +07:00
List < MethodInfo > signals ;
ClassDB : : get_signal_list ( type , & signals ) ;
for ( const MethodInfo & E : signals ) {
int location = p_recursion_depth + _get_signal_location ( type , StringName ( E . name ) ) ;
ScriptLanguage : : CodeCompletionOption option ( E . name , ScriptLanguage : : CODE_COMPLETION_KIND_SIGNAL , location ) ;
r_result . insert ( option . display , option ) ;
}
2020-07-06 12:24:24 -03:00
}
}
2022-12-03 22:02:03 -05:00
bool only_static = base_type . is_meta_type & & ! Engine : : get_singleton ( ) - > has_singleton ( type ) ;
2022-04-06 14:14:38 -03:00
List < MethodInfo > methods ;
ClassDB : : get_method_list ( type , & methods , false , true ) ;
for ( const MethodInfo & E : methods ) {
if ( only_static & & ( E . flags & METHOD_FLAG_STATIC ) = = 0 ) {
continue ;
}
if ( E . name . begins_with ( " _ " ) ) {
continue ;
}
int location = p_recursion_depth + _get_method_location ( type , E . name ) ;
ScriptLanguage : : CodeCompletionOption option ( E . name , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION , location ) ;
2024-08-31 12:12:19 +02:00
if ( p_add_braces ) {
if ( E . arguments . size ( ) | | ( E . flags & METHOD_FLAG_VARARG ) ) {
option . insert_text + = " ( " ;
option . display + = U " ( \u2026 ) " ;
} else {
option . insert_text + = " () " ;
option . display + = " () " ;
}
2020-07-06 12:24:24 -03:00
}
2022-04-06 14:14:38 -03:00
r_result . insert ( option . display , option ) ;
2020-07-06 12:24:24 -03:00
}
return ;
} break ;
2024-03-15 20:20:36 +00:00
case GDScriptParser : : DataType : : ENUM : {
2024-07-31 23:44:48 +02:00
if ( p_types_only ) {
return ;
}
2024-03-15 20:20:36 +00:00
String type_str = base_type . native_type ;
2025-01-30 12:41:51 +01:00
if ( type_str . contains_char ( ' . ' ) ) {
StringName type = type_str . get_slicec ( ' . ' , 0 ) ;
StringName type_enum = base_type . enum_type ;
List < StringName > enum_values ;
ClassDB : : get_enum_constants ( type , type_enum , & enum_values ) ;
for ( const StringName & E : enum_values ) {
int location = p_recursion_depth + _get_enum_constant_location ( type , E ) ;
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT , location ) ;
r_result . insert ( option . display , option ) ;
}
} else if ( CoreConstants : : is_global_enum ( base_type . enum_type ) ) {
HashMap < StringName , int64_t > enum_values ;
CoreConstants : : get_enum_values ( base_type . enum_type , & enum_values ) ;
for ( const KeyValue < StringName , int64_t > & enum_value : enum_values ) {
int location = p_recursion_depth + ScriptLanguage : : LOCATION_OTHER ;
ScriptLanguage : : CodeCompletionOption option ( enum_value . key , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT , location ) ;
r_result . insert ( option . display , option ) ;
}
2024-03-15 20:20:36 +00:00
}
}
[[fallthrough]] ;
2020-07-06 12:24:24 -03:00
case GDScriptParser : : DataType : : BUILTIN : {
2023-11-06 19:08:12 +03:00
if ( p_types_only ) {
return ;
}
2020-07-06 12:24:24 -03:00
Callable : : CallError err ;
2020-11-09 00:19:09 -03:00
Variant tmp ;
Variant : : construct ( base_type . builtin_type , tmp , nullptr , 0 , err ) ;
2020-07-06 12:24:24 -03:00
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
return ;
}
2023-12-31 14:25:07 +01:00
int location = ScriptLanguage : : LOCATION_OTHER ;
2020-07-06 12:24:24 -03:00
if ( ! p_only_functions ) {
List < PropertyInfo > members ;
if ( p_base . value . get_type ( ) ! = Variant : : NIL ) {
p_base . value . get_property_list ( & members ) ;
} else {
tmp . get_property_list ( & members ) ;
}
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & E : members ) {
2024-01-26 20:42:20 +01:00
if ( E . usage & ( PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL ) ) {
2023-11-21 23:30:51 +01:00
continue ;
}
2024-12-05 17:56:08 +01:00
if ( ! String ( E . name ) . contains_char ( ' / ' ) ) {
2023-12-31 14:25:07 +01:00
ScriptLanguage : : CodeCompletionOption option ( E . name , ScriptLanguage : : CODE_COMPLETION_KIND_MEMBER , location ) ;
if ( base_type . kind = = GDScriptParser : : DataType : : ENUM ) {
// Sort enum members in their declaration order.
location + = 1 ;
}
2023-03-12 10:33:38 +03:00
if ( GDScriptParser : : theme_color_names . has ( E . name ) ) {
option . theme_color_name = GDScriptParser : : theme_color_names [ E . name ] ;
}
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
}
}
List < MethodInfo > methods ;
tmp . get_method_list ( & methods ) ;
2021-07-15 23:45:57 -04:00
for ( const MethodInfo & E : methods ) {
2022-12-03 22:02:03 -05:00
if ( base_type . kind = = GDScriptParser : : DataType : : ENUM & & base_type . is_meta_type & & ! ( E . flags & METHOD_FLAG_CONST ) ) {
// Enum types are static and cannot change, therefore we skip non-const dictionary methods.
continue ;
}
2023-12-31 14:25:07 +01:00
ScriptLanguage : : CodeCompletionOption option ( E . name , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION , location ) ;
2024-08-31 12:12:19 +02:00
if ( p_add_braces ) {
if ( E . arguments . size ( ) | | ( E . flags & METHOD_FLAG_VARARG ) ) {
option . insert_text + = " ( " ;
option . display + = U " ( \u2026 ) " ;
} else {
option . insert_text + = " () " ;
option . display + = " () " ;
}
2020-07-06 12:24:24 -03:00
}
r_result . insert ( option . display , option ) ;
}
return ;
} break ;
default : {
return ;
} break ;
}
}
}
2024-08-31 12:12:19 +02:00
static void _find_identifiers ( const GDScriptParser : : CompletionContext & p_context , bool p_only_functions , bool p_add_braces , HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result , int p_recursion_depth ) {
2020-07-06 12:24:24 -03:00
if ( ! p_only_functions & & p_context . current_suite ) {
// This includes function parameters, since they are also locals.
_find_identifiers_in_suite ( p_context . current_suite , r_result ) ;
}
if ( p_context . current_class ) {
2024-08-31 12:12:19 +02:00
_find_identifiers_in_class ( p_context . current_class , p_only_functions , false , ( ! p_context . current_function | | p_context . current_function - > is_static ) , false , p_add_braces , r_result , p_recursion_depth ) ;
2020-07-06 12:24:24 -03:00
}
2020-11-26 11:56:32 -03:00
List < StringName > functions ;
GDScriptUtilityFunctions : : get_function_list ( & functions ) ;
2021-07-15 23:45:57 -04:00
for ( const StringName & E : functions ) {
MethodInfo function = GDScriptUtilityFunctions : : get_function_info ( E ) ;
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( String ( E ) , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION ) ;
2024-08-31 12:12:19 +02:00
if ( p_add_braces ) {
if ( function . arguments . size ( ) | | ( function . flags & METHOD_FLAG_VARARG ) ) {
option . insert_text + = " ( " ;
option . display + = U " ( \u2026 ) " ;
} else {
option . insert_text + = " () " ;
option . display + = " () " ;
}
2020-07-06 12:24:24 -03:00
}
r_result . insert ( option . display , option ) ;
}
if ( p_only_functions ) {
return ;
}
2022-04-07 09:00:23 -05:00
_find_built_in_variants ( r_result ) ;
2020-07-06 12:24:24 -03:00
static const char * _keywords [ ] = {
2022-10-06 23:47:19 +02:00
" true " , " false " , " PI " , " TAU " , " INF " , " NAN " , " null " , " self " , " super " ,
" break " , " breakpoint " , " continue " , " pass " , " return " ,
2021-04-05 14:02:50 +02:00
nullptr
2020-07-06 12:24:24 -03:00
} ;
const char * * kw = _keywords ;
while ( * kw ) {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( * kw , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
kw + + ;
}
2020-11-05 09:40:10 +01:00
static const char * _keywords_with_space [ ] = {
2022-10-06 23:47:19 +02:00
" and " , " not " , " or " , " in " , " as " , " class " , " class_name " , " extends " , " is " , " func " , " signal " , " await " ,
2025-06-19 04:53:15 -07:00
" const " , " enum " , " static " , " var " , " if " , " elif " , " else " , " for " , " match " , " when " , " while " ,
2021-04-05 14:02:50 +02:00
nullptr
2020-11-05 09:40:10 +01:00
} ;
const char * * kws = _keywords_with_space ;
while ( * kws ) {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( * kws , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2020-11-05 09:40:10 +01:00
option . insert_text + = " " ;
r_result . insert ( option . display , option ) ;
kws + + ;
}
static const char * _keywords_with_args [ ] = {
" assert " , " preload " ,
2021-04-05 14:02:50 +02:00
nullptr
2020-11-05 09:40:10 +01:00
} ;
const char * * kwa = _keywords_with_args ;
while ( * kwa ) {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( * kwa , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION ) ;
2024-08-31 12:12:19 +02:00
if ( p_add_braces ) {
option . insert_text + = " ( " ;
option . display + = U " ( \u2026 ) " ;
}
2020-11-05 09:40:10 +01:00
r_result . insert ( option . display , option ) ;
kwa + + ;
}
2021-10-10 22:22:07 +03:00
List < StringName > utility_func_names ;
Variant : : get_utility_function_list ( & utility_func_names ) ;
2025-02-03 14:16:27 +08:00
for ( const StringName & util_func_name : utility_func_names ) {
ScriptLanguage : : CodeCompletionOption option ( util_func_name , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION ) ;
2024-08-31 12:12:19 +02:00
if ( p_add_braces ) {
option . insert_text + = " ( " ;
option . display + = U " ( \u2026 ) " ; // As all utility functions contain an argument or more, this is hardcoded here.
}
2021-10-10 22:22:07 +03:00
r_result . insert ( option . display , option ) ;
}
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , ProjectSettings : : AutoloadInfo > & E : ProjectSettings : : get_singleton ( ) - > get_autoload_list ( ) ) {
if ( ! E . value . is_singleton ) {
2020-07-06 12:24:24 -03:00
continue ;
}
2022-05-08 10:09:19 +02:00
ScriptLanguage : : CodeCompletionOption option ( E . key , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
// Native classes and global constants.
2021-08-09 14:13:42 -06:00
for ( const KeyValue < StringName , int > & E : GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) ) {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ;
2021-08-09 14:13:42 -06:00
if ( ClassDB : : class_exists ( E . key ) | | Engine : : get_singleton ( ) - > has_singleton ( E . key ) ) {
2022-03-26 16:48:43 +01:00
option = ScriptLanguage : : CodeCompletionOption ( E . key . operator String ( ) , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS ) ;
2020-07-06 12:24:24 -03:00
} else {
2022-03-26 16:48:43 +01:00
option = ScriptLanguage : : CodeCompletionOption ( E . key . operator String ( ) , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT ) ;
2020-07-06 12:24:24 -03:00
}
r_result . insert ( option . display , option ) ;
}
2023-01-16 00:25:24 +07:00
2025-01-30 12:41:51 +01:00
// Global enums
_find_global_enums ( r_result ) ;
2023-01-16 00:25:24 +07:00
// Global classes
List < StringName > global_classes ;
ScriptServer : : get_global_class_list ( & global_classes ) ;
for ( const StringName & E : global_classes ) {
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_CLASS , ScriptLanguage : : LOCATION_OTHER_USER_CODE ) ;
r_result . insert ( option . display , option ) ;
}
2020-07-06 12:24:24 -03:00
}
2024-01-16 23:04:10 +01:00
static GDScriptCompletionIdentifier _type_from_variant ( const Variant & p_value , GDScriptParser : : CompletionContext & p_context ) {
2020-07-06 12:24:24 -03:00
GDScriptCompletionIdentifier ci ;
ci . value = p_value ;
ci . type . is_constant = true ;
ci . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
ci . type . kind = GDScriptParser : : DataType : : BUILTIN ;
ci . type . builtin_type = p_value . get_type ( ) ;
if ( ci . type . builtin_type = = Variant : : OBJECT ) {
Object * obj = p_value . operator Object * ( ) ;
if ( ! obj ) {
return ci ;
}
ci . type . native_type = obj - > get_class_name ( ) ;
Ref < Script > scr = p_value ;
if ( scr . is_valid ( ) ) {
ci . type . is_meta_type = true ;
} else {
ci . type . is_meta_type = false ;
scr = obj - > get_script ( ) ;
}
if ( scr . is_valid ( ) ) {
2024-01-16 23:04:10 +01:00
ci . type . script_path = scr - > get_path ( ) ;
2024-07-16 08:49:41 +02:00
ci . type . script_type = scr ;
ci . type . native_type = scr - > get_instance_base_type ( ) ;
ci . type . kind = GDScriptParser : : DataType : : SCRIPT ;
2024-01-16 23:04:10 +01:00
if ( scr - > get_path ( ) . ends_with ( " .gd " ) ) {
2024-07-16 08:49:41 +02:00
Ref < GDScriptParserRef > parser = p_context . parser - > get_depended_parser_for ( scr - > get_path ( ) ) ;
if ( parser . is_valid ( ) & & parser - > raise_status ( GDScriptParserRef : : INTERFACE_SOLVED ) = = OK ) {
2024-01-16 23:04:10 +01:00
ci . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
ci . type . class_type = parser - > get_parser ( ) - > get_tree ( ) ;
ci . type . kind = GDScriptParser : : DataType : : CLASS ;
return ci ;
}
}
2020-07-06 12:24:24 -03:00
} else {
ci . type . kind = GDScriptParser : : DataType : : NATIVE ;
}
}
return ci ;
}
static GDScriptCompletionIdentifier _type_from_property ( const PropertyInfo & p_property ) {
GDScriptCompletionIdentifier ci ;
if ( p_property . type = = Variant : : NIL ) {
// Variant
2024-05-31 10:19:03 +02:00
ci . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
ci . type . kind = GDScriptParser : : DataType : : VARIANT ;
2020-07-06 12:24:24 -03:00
return ci ;
}
2022-06-24 11:16:37 +02:00
if ( p_property . usage & ( PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD ) ) {
2020-07-06 12:24:24 -03:00
ci . enumeration = p_property . class_name ;
}
ci . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
ci . type . builtin_type = p_property . type ;
if ( p_property . type = = Variant : : OBJECT ) {
2023-11-22 14:12:50 +01:00
if ( ScriptServer : : is_global_class ( p_property . class_name ) ) {
ci . type . kind = GDScriptParser : : DataType : : SCRIPT ;
ci . type . script_path = ScriptServer : : get_global_class_path ( p_property . class_name ) ;
ci . type . native_type = ScriptServer : : get_global_class_native_base ( p_property . class_name ) ;
Ref < Script > scr = ResourceLoader : : load ( ScriptServer : : get_global_class_path ( p_property . class_name ) ) ;
if ( scr . is_valid ( ) ) {
ci . type . script_type = scr ;
}
} else {
ci . type . kind = GDScriptParser : : DataType : : NATIVE ;
ci . type . native_type = p_property . class_name = = StringName ( ) ? " Object " : p_property . class_name ;
}
2020-07-06 12:24:24 -03:00
} else {
ci . type . kind = GDScriptParser : : DataType : : BUILTIN ;
}
return ci ;
}
2024-08-31 12:12:19 +02:00
static GDScriptCompletionIdentifier _callable_type_from_method_info ( const MethodInfo & p_method ) {
GDScriptCompletionIdentifier ci ;
ci . type . kind = GDScriptParser : : DataType : : BUILTIN ;
ci . type . builtin_type = Variant : : CALLABLE ;
ci . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
ci . type . is_constant = true ;
ci . type . method_info = p_method ;
return ci ;
}
2023-02-20 14:55:13 -03:00
# define MAX_COMPLETION_RECURSION 100
struct RecursionCheck {
int * counter ;
_FORCE_INLINE_ bool check ( ) {
return ( * counter ) > MAX_COMPLETION_RECURSION ;
}
RecursionCheck ( int * p_counter ) :
counter ( p_counter ) {
( * counter ) + + ;
}
~ RecursionCheck ( ) {
( * counter ) - - ;
}
} ;
2023-07-25 14:21:49 +03:00
static bool _guess_identifier_type ( GDScriptParser : : CompletionContext & p_context , const GDScriptParser : : IdentifierNode * p_identifier , GDScriptCompletionIdentifier & r_type ) ;
2020-07-06 12:24:24 -03:00
static bool _guess_identifier_type_from_base ( GDScriptParser : : CompletionContext & p_context , const GDScriptCompletionIdentifier & p_base , const StringName & p_identifier , GDScriptCompletionIdentifier & r_type ) ;
static bool _guess_method_return_type_from_base ( GDScriptParser : : CompletionContext & p_context , const GDScriptCompletionIdentifier & p_base , const StringName & p_method , GDScriptCompletionIdentifier & r_type ) ;
2022-02-22 00:54:15 +00:00
static bool _is_expression_named_identifier ( const GDScriptParser : : ExpressionNode * p_expression , const StringName & p_name ) {
if ( p_expression ) {
switch ( p_expression - > type ) {
case GDScriptParser : : Node : : IDENTIFIER : {
const GDScriptParser : : IdentifierNode * id = static_cast < const GDScriptParser : : IdentifierNode * > ( p_expression ) ;
if ( id - > name = = p_name ) {
return true ;
}
} break ;
case GDScriptParser : : Node : : CAST : {
const GDScriptParser : : CastNode * cn = static_cast < const GDScriptParser : : CastNode * > ( p_expression ) ;
return _is_expression_named_identifier ( cn - > operand , p_name ) ;
} break ;
default :
break ;
}
}
return false ;
}
2020-07-06 12:24:24 -03:00
static bool _guess_expression_type ( GDScriptParser : : CompletionContext & p_context , const GDScriptParser : : ExpressionNode * p_expression , GDScriptCompletionIdentifier & r_type ) {
bool found = false ;
2021-11-21 20:58:52 +01:00
if ( p_expression = = nullptr ) {
return false ;
}
2023-02-20 14:55:13 -03:00
static int recursion_depth = 0 ;
RecursionCheck recursion ( & recursion_depth ) ;
if ( unlikely ( recursion . check ( ) ) ) {
ERR_FAIL_V_MSG ( false , " Reached recursion limit while trying to guess type. " ) ;
}
2020-07-06 12:24:24 -03:00
if ( p_expression - > is_constant ) {
// Already has a value, so just use that.
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( p_expression - > reduced_value , p_context ) ;
2023-07-28 17:06:08 +02:00
switch ( p_expression - > get_datatype ( ) . kind ) {
case GDScriptParser : : DataType : : ENUM :
case GDScriptParser : : DataType : : CLASS :
r_type . type = p_expression - > get_datatype ( ) ;
break ;
default :
break ;
2022-12-03 22:02:03 -05:00
}
2020-07-06 12:24:24 -03:00
found = true ;
} else {
switch ( p_expression - > type ) {
case GDScriptParser : : Node : : LITERAL : {
const GDScriptParser : : LiteralNode * literal = static_cast < const GDScriptParser : : LiteralNode * > ( p_expression ) ;
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( literal - > value , p_context ) ;
2020-07-06 12:24:24 -03:00
found = true ;
} break ;
case GDScriptParser : : Node : : SELF : {
if ( p_context . current_class ) {
2023-12-20 00:27:01 +03:00
r_type . type = p_context . current_class - > get_datatype ( ) ;
r_type . type . is_meta_type = false ;
2020-07-06 12:24:24 -03:00
found = true ;
}
} break ;
case GDScriptParser : : Node : : IDENTIFIER : {
const GDScriptParser : : IdentifierNode * id = static_cast < const GDScriptParser : : IdentifierNode * > ( p_expression ) ;
2023-07-25 14:21:49 +03:00
found = _guess_identifier_type ( p_context , id , r_type ) ;
2020-07-06 12:24:24 -03:00
} break ;
case GDScriptParser : : Node : : DICTIONARY : {
// Try to recreate the dictionary.
const GDScriptParser : : DictionaryNode * dn = static_cast < const GDScriptParser : : DictionaryNode * > ( p_expression ) ;
Dictionary d ;
bool full = true ;
for ( int i = 0 ; i < dn - > elements . size ( ) ; i + + ) {
GDScriptCompletionIdentifier key ;
if ( _guess_expression_type ( p_context , dn - > elements [ i ] . key , key ) ) {
if ( ! key . type . is_constant ) {
full = false ;
break ;
}
GDScriptCompletionIdentifier value ;
if ( _guess_expression_type ( p_context , dn - > elements [ i ] . value , value ) ) {
if ( ! value . type . is_constant ) {
full = false ;
break ;
}
d [ key . value ] = value . value ;
} else {
full = false ;
break ;
}
} else {
full = false ;
break ;
}
}
if ( full ) {
r_type . value = d ;
r_type . type . is_constant = true ;
}
r_type . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
r_type . type . kind = GDScriptParser : : DataType : : BUILTIN ;
r_type . type . builtin_type = Variant : : DICTIONARY ;
found = true ;
} break ;
case GDScriptParser : : Node : : ARRAY : {
// Try to recreate the array
const GDScriptParser : : ArrayNode * an = static_cast < const GDScriptParser : : ArrayNode * > ( p_expression ) ;
Array a ;
bool full = true ;
a . resize ( an - > elements . size ( ) ) ;
for ( int i = 0 ; i < an - > elements . size ( ) ; i + + ) {
GDScriptCompletionIdentifier value ;
if ( _guess_expression_type ( p_context , an - > elements [ i ] , value ) ) {
if ( value . type . is_constant ) {
a [ i ] = value . value ;
} else {
full = false ;
break ;
}
} else {
full = false ;
break ;
}
}
if ( full ) {
// If not fully constant, setting this value is detrimental to the inference.
r_type . value = a ;
2023-11-05 21:52:04 +01:00
r_type . type . is_constant = true ;
2020-07-06 12:24:24 -03:00
}
r_type . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
r_type . type . kind = GDScriptParser : : DataType : : BUILTIN ;
r_type . type . builtin_type = Variant : : ARRAY ;
found = true ;
} break ;
case GDScriptParser : : Node : : CAST : {
const GDScriptParser : : CastNode * cn = static_cast < const GDScriptParser : : CastNode * > ( p_expression ) ;
GDScriptCompletionIdentifier value ;
if ( _guess_expression_type ( p_context , cn - > operand , r_type ) ) {
r_type . type = cn - > get_datatype ( ) ;
found = true ;
}
} break ;
case GDScriptParser : : Node : : CALL : {
const GDScriptParser : : CallNode * call = static_cast < const GDScriptParser : : CallNode * > ( p_expression ) ;
if ( GDScriptParser : : get_builtin_type ( call - > function_name ) < Variant : : VARIANT_MAX ) {
r_type . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
r_type . type . kind = GDScriptParser : : DataType : : BUILTIN ;
r_type . type . builtin_type = GDScriptParser : : get_builtin_type ( call - > function_name ) ;
found = true ;
break ;
2020-11-26 11:56:32 -03:00
} else if ( GDScriptUtilityFunctions : : function_exists ( call - > function_name ) ) {
MethodInfo mi = GDScriptUtilityFunctions : : get_function_info ( call - > function_name ) ;
2020-07-06 12:24:24 -03:00
r_type = _type_from_property ( mi . return_val ) ;
found = true ;
break ;
} else {
GDScriptParser : : CompletionContext c = p_context ;
c . current_line = call - > start_line ;
2020-08-05 14:41:46 -05:00
GDScriptParser : : Node : : Type callee_type = call - > get_callee_type ( ) ;
2020-07-06 12:24:24 -03:00
GDScriptCompletionIdentifier base ;
2020-08-05 14:41:46 -05:00
if ( callee_type = = GDScriptParser : : Node : : IDENTIFIER | | call - > is_super ) {
2020-07-06 12:24:24 -03:00
// Simple call, so base is 'self'.
if ( p_context . current_class ) {
2022-07-29 10:21:11 +02:00
if ( call - > is_super ) {
base . type = p_context . current_class - > base_type ;
base . value = p_context . base ;
} else {
base . type . kind = GDScriptParser : : DataType : : CLASS ;
base . type . type_source = GDScriptParser : : DataType : : INFERRED ;
base . type . is_constant = true ;
base . type . class_type = p_context . current_class ;
base . value = p_context . base ;
}
2020-07-06 12:24:24 -03:00
} else {
break ;
}
2020-08-05 14:41:46 -05:00
} else if ( callee_type = = GDScriptParser : : Node : : SUBSCRIPT & & static_cast < const GDScriptParser : : SubscriptNode * > ( call - > callee ) - > is_attribute ) {
2020-07-06 12:24:24 -03:00
if ( ! _guess_expression_type ( c , static_cast < const GDScriptParser : : SubscriptNode * > ( call - > callee ) - > base , base ) ) {
found = false ;
break ;
}
} else {
break ;
}
// Try call if constant methods with constant arguments
if ( base . type . is_constant & & base . value . get_type ( ) = = Variant : : OBJECT ) {
GDScriptParser : : DataType native_type = base . type ;
while ( native_type . kind = = GDScriptParser : : DataType : : CLASS ) {
native_type = native_type . class_type - > base_type ;
}
while ( native_type . kind = = GDScriptParser : : DataType : : SCRIPT ) {
if ( native_type . script_type . is_valid ( ) ) {
Ref < Script > parent = native_type . script_type - > get_base_script ( ) ;
if ( parent . is_valid ( ) ) {
native_type . script_type = parent ;
} else {
native_type . kind = GDScriptParser : : DataType : : NATIVE ;
2023-10-04 15:03:53 +03:00
native_type . builtin_type = Variant : : OBJECT ;
2020-07-06 12:24:24 -03:00
native_type . native_type = native_type . script_type - > get_instance_base_type ( ) ;
if ( ! ClassDB : : class_exists ( native_type . native_type ) ) {
2021-08-17 15:06:54 +02:00
native_type . kind = GDScriptParser : : DataType : : UNRESOLVED ;
2020-07-06 12:24:24 -03:00
}
}
}
}
if ( native_type . kind = = GDScriptParser : : DataType : : NATIVE ) {
MethodBind * mb = ClassDB : : get_method ( native_type . native_type , call - > function_name ) ;
if ( mb & & mb - > is_const ( ) ) {
bool all_is_const = true ;
Vector < Variant > args ;
for ( int i = 0 ; all_is_const & & i < call - > arguments . size ( ) ; i + + ) {
GDScriptCompletionIdentifier arg ;
if ( ! call - > arguments [ i ] - > is_constant ) {
all_is_const = false ;
}
}
Object * baseptr = base . value ;
2022-02-06 14:12:19 +01:00
if ( all_is_const & & call - > function_name = = SNAME ( " get_node " ) & & ClassDB : : is_parent_class ( native_type . native_type , SNAME ( " Node " ) ) & & args . size ( ) ) {
2020-07-06 12:24:24 -03:00
String arg1 = args [ 0 ] ;
if ( arg1 . begins_with ( " /root/ " ) ) {
2024-11-16 17:16:07 +01:00
String which = arg1 . get_slicec ( ' / ' , 2 ) ;
2021-12-09 03:42:46 -06:00
if ( ! which . is_empty ( ) ) {
2020-07-06 12:24:24 -03:00
// Try singletons first
if ( GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) . has ( which ) ) {
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) [ which ] , p_context ) ;
2020-07-06 12:24:24 -03:00
found = true ;
} else {
2022-05-08 10:09:19 +02:00
for ( const KeyValue < StringName , ProjectSettings : : AutoloadInfo > & E : ProjectSettings : : get_singleton ( ) - > get_autoload_list ( ) ) {
String name = E . key ;
2020-07-06 12:24:24 -03:00
if ( name = = which ) {
2022-05-08 10:09:19 +02:00
String script = E . value . path ;
2020-07-06 12:24:24 -03:00
if ( ! script . begins_with ( " res:// " ) ) {
script = " res:// " + script ;
}
if ( ! script . ends_with ( " .gd " ) ) {
2021-10-11 11:30:59 +02:00
// not a script, try find the script anyway,
// may have some success
2020-07-06 12:24:24 -03:00
script = script . get_basename ( ) + " .gd " ;
}
if ( FileAccess : : exists ( script ) ) {
2024-07-16 08:49:41 +02:00
Ref < GDScriptParserRef > parser = p_context . parser - > get_depended_parser_for ( script ) ;
if ( parser . is_valid ( ) & & parser - > raise_status ( GDScriptParserRef : : INTERFACE_SOLVED ) = = OK ) {
2020-07-06 12:24:24 -03:00
r_type . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
r_type . type . script_path = script ;
r_type . type . class_type = parser - > get_parser ( ) - > get_tree ( ) ;
r_type . type . is_constant = false ;
r_type . type . kind = GDScriptParser : : DataType : : CLASS ;
r_type . value = Variant ( ) ;
found = true ;
}
}
break ;
}
}
}
}
}
}
if ( ! found & & all_is_const & & baseptr ) {
Vector < const Variant * > argptr ;
for ( int i = 0 ; i < args . size ( ) ; i + + ) {
argptr . push_back ( & args [ i ] ) ;
}
Callable : : CallError ce ;
Variant ret = mb - > call ( baseptr , ( const Variant * * ) argptr . ptr ( ) , argptr . size ( ) , ce ) ;
if ( ce . error = = Callable : : CallError : : CALL_OK & & ret . get_type ( ) ! = Variant : : NIL ) {
if ( ret . get_type ( ) ! = Variant : : OBJECT | | ret . operator Object * ( ) ! = nullptr ) {
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( ret , p_context ) ;
2020-07-06 12:24:24 -03:00
found = true ;
}
}
}
}
}
}
2023-10-31 19:34:30 +01:00
if ( ! found ) {
2020-07-06 12:24:24 -03:00
found = _guess_method_return_type_from_base ( c , base , call - > function_name , r_type ) ;
}
}
} break ;
case GDScriptParser : : Node : : SUBSCRIPT : {
const GDScriptParser : : SubscriptNode * subscript = static_cast < const GDScriptParser : : SubscriptNode * > ( p_expression ) ;
if ( subscript - > is_attribute ) {
GDScriptParser : : CompletionContext c = p_context ;
c . current_line = subscript - > start_line ;
GDScriptCompletionIdentifier base ;
if ( ! _guess_expression_type ( c , subscript - > base , base ) ) {
found = false ;
break ;
}
if ( base . value . get_type ( ) = = Variant : : DICTIONARY & & base . value . operator Dictionary ( ) . has ( String ( subscript - > attribute - > name ) ) ) {
Variant value = base . value . operator Dictionary ( ) [ String ( subscript - > attribute - > name ) ] ;
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( value , p_context ) ;
2020-07-06 12:24:24 -03:00
found = true ;
break ;
}
const GDScriptParser : : DictionaryNode * dn = nullptr ;
if ( subscript - > base - > type = = GDScriptParser : : Node : : DICTIONARY ) {
dn = static_cast < const GDScriptParser : : DictionaryNode * > ( subscript - > base ) ;
} else if ( base . assigned_expression & & base . assigned_expression - > type = = GDScriptParser : : Node : : DICTIONARY ) {
dn = static_cast < const GDScriptParser : : DictionaryNode * > ( base . assigned_expression ) ;
}
if ( dn ) {
for ( int i = 0 ; i < dn - > elements . size ( ) ; i + + ) {
GDScriptCompletionIdentifier key ;
if ( ! _guess_expression_type ( c , dn - > elements [ i ] . key , key ) ) {
continue ;
}
if ( key . value = = String ( subscript - > attribute - > name ) ) {
r_type . assigned_expression = dn - > elements [ i ] . value ;
found = _guess_expression_type ( c , dn - > elements [ i ] . value , r_type ) ;
break ;
}
}
}
if ( ! found ) {
found = _guess_identifier_type_from_base ( c , base , subscript - > attribute - > name , r_type ) ;
}
} else {
if ( subscript - > index = = nullptr ) {
found = false ;
break ;
}
GDScriptParser : : CompletionContext c = p_context ;
c . current_line = subscript - > start_line ;
GDScriptCompletionIdentifier base ;
if ( ! _guess_expression_type ( c , subscript - > base , base ) ) {
found = false ;
break ;
}
GDScriptCompletionIdentifier index ;
if ( ! _guess_expression_type ( c , subscript - > index , index ) ) {
found = false ;
break ;
}
2024-07-01 14:49:39 +02:00
{
bool valid ;
Variant value = base . value . get ( index . value , & valid ) ;
if ( valid ) {
r_type = _type_from_variant ( value , p_context ) ;
found = true ;
break ;
}
2020-07-06 12:24:24 -03:00
}
// Look if it is a dictionary node.
const GDScriptParser : : DictionaryNode * dn = nullptr ;
if ( subscript - > base - > type = = GDScriptParser : : Node : : DICTIONARY ) {
dn = static_cast < const GDScriptParser : : DictionaryNode * > ( subscript - > base ) ;
} else if ( base . assigned_expression & & base . assigned_expression - > type = = GDScriptParser : : Node : : DICTIONARY ) {
dn = static_cast < const GDScriptParser : : DictionaryNode * > ( base . assigned_expression ) ;
}
if ( dn ) {
for ( int i = 0 ; i < dn - > elements . size ( ) ; i + + ) {
GDScriptCompletionIdentifier key ;
if ( ! _guess_expression_type ( c , dn - > elements [ i ] . key , key ) ) {
continue ;
}
if ( key . value = = index . value ) {
r_type . assigned_expression = dn - > elements [ i ] . value ;
found = _guess_expression_type ( p_context , dn - > elements [ i ] . value , r_type ) ;
break ;
}
}
}
// Look if it is an array node.
if ( ! found & & index . value . is_num ( ) ) {
int idx = index . value ;
const GDScriptParser : : ArrayNode * an = nullptr ;
if ( subscript - > base - > type = = GDScriptParser : : Node : : ARRAY ) {
an = static_cast < const GDScriptParser : : ArrayNode * > ( subscript - > base ) ;
} else if ( base . assigned_expression & & base . assigned_expression - > type = = GDScriptParser : : Node : : ARRAY ) {
an = static_cast < const GDScriptParser : : ArrayNode * > ( base . assigned_expression ) ;
}
if ( an & & idx > = 0 & & an - > elements . size ( ) > idx ) {
r_type . assigned_expression = an - > elements [ idx ] ;
found = _guess_expression_type ( c , an - > elements [ idx ] , r_type ) ;
break ;
}
}
// Look for valid indexing in other types
2023-12-28 14:44:23 -08:00
if ( ! found & & ( index . value . is_string ( ) | | index . value . get_type ( ) = = Variant : : NODE_PATH ) ) {
2020-07-06 12:24:24 -03:00
StringName id = index . value ;
found = _guess_identifier_type_from_base ( c , base , id , r_type ) ;
} else if ( ! found & & index . type . kind = = GDScriptParser : : DataType : : BUILTIN ) {
Callable : : CallError err ;
2020-11-09 00:19:09 -03:00
Variant base_val ;
Variant : : construct ( base . type . builtin_type , base_val , nullptr , 0 , err ) ;
2020-07-06 12:24:24 -03:00
bool valid = false ;
Variant res = base_val . get ( index . value , & valid ) ;
if ( valid ) {
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( res , p_context ) ;
2020-07-06 12:24:24 -03:00
r_type . value = Variant ( ) ;
r_type . type . is_constant = false ;
found = true ;
}
}
}
} break ;
case GDScriptParser : : Node : : BINARY_OPERATOR : {
const GDScriptParser : : BinaryOpNode * op = static_cast < const GDScriptParser : : BinaryOpNode * > ( p_expression ) ;
if ( op - > variant_op = = Variant : : OP_MAX ) {
break ;
}
GDScriptParser : : CompletionContext context = p_context ;
context . current_line = op - > start_line ;
GDScriptCompletionIdentifier p1 ;
GDScriptCompletionIdentifier p2 ;
if ( ! _guess_expression_type ( context , op - > left_operand , p1 ) ) {
found = false ;
break ;
}
if ( ! _guess_expression_type ( context , op - > right_operand , p2 ) ) {
found = false ;
break ;
}
Callable : : CallError ce ;
bool v1_use_value = p1 . value . get_type ( ) ! = Variant : : NIL & & p1 . value . get_type ( ) ! = Variant : : OBJECT ;
2020-11-09 00:19:09 -03:00
Variant d1 ;
Variant : : construct ( p1 . type . builtin_type , d1 , nullptr , 0 , ce ) ;
Variant d2 ;
Variant : : construct ( p2 . type . builtin_type , d2 , nullptr , 0 , ce ) ;
Variant v1 = ( v1_use_value ) ? p1 . value : d1 ;
2020-07-06 12:24:24 -03:00
bool v2_use_value = p2 . value . get_type ( ) ! = Variant : : NIL & & p2 . value . get_type ( ) ! = Variant : : OBJECT ;
2020-11-09 00:19:09 -03:00
Variant v2 = ( v2_use_value ) ? p2 . value : d2 ;
2020-07-06 12:24:24 -03:00
// avoid potential invalid ops
if ( ( op - > variant_op = = Variant : : OP_DIVIDE | | op - > variant_op = = Variant : : OP_MODULE ) & & v2 . get_type ( ) = = Variant : : INT ) {
v2 = 1 ;
v2_use_value = false ;
}
if ( op - > variant_op = = Variant : : OP_DIVIDE & & v2 . get_type ( ) = = Variant : : FLOAT ) {
v2 = 1.0 ;
v2_use_value = false ;
}
Variant res ;
bool valid ;
Variant : : evaluate ( op - > variant_op , v1 , v2 , res , valid ) ;
if ( ! valid ) {
found = false ;
break ;
}
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( res , p_context ) ;
2020-07-06 12:24:24 -03:00
if ( ! v1_use_value | | ! v2_use_value ) {
r_type . value = Variant ( ) ;
r_type . type . is_constant = false ;
}
found = true ;
} break ;
default :
break ;
}
}
// It may have found a null, but that's never useful
if ( found & & r_type . type . kind = = GDScriptParser : : DataType : : BUILTIN & & r_type . type . builtin_type = = Variant : : NIL ) {
found = false ;
}
2024-05-07 10:45:28 +02:00
// If the found type was not fully analyzed we analyze it now.
if ( found & & r_type . type . kind = = GDScriptParser : : DataType : : CLASS & & ! r_type . type . class_type - > resolved_body ) {
Error err ;
Ref < GDScriptParserRef > r = GDScriptCache : : get_parser ( r_type . type . script_path , GDScriptParserRef : : FULLY_SOLVED , err ) ;
}
2020-07-06 12:24:24 -03:00
// Check type hint last. For collections we want chance to get the actual value first
// This way we can detect types from the content of dictionaries and arrays
if ( ! found & & p_expression - > get_datatype ( ) . is_hard_type ( ) ) {
r_type . type = p_expression - > get_datatype ( ) ;
if ( ! r_type . assigned_expression ) {
r_type . assigned_expression = p_expression ;
}
found = true ;
}
return found ;
}
2023-07-25 14:21:49 +03:00
static bool _guess_identifier_type ( GDScriptParser : : CompletionContext & p_context , const GDScriptParser : : IdentifierNode * p_identifier , GDScriptCompletionIdentifier & r_type ) {
2023-02-20 14:55:13 -03:00
static int recursion_depth = 0 ;
RecursionCheck recursion ( & recursion_depth ) ;
if ( unlikely ( recursion . check ( ) ) ) {
ERR_FAIL_V_MSG ( false , " Reached recursion limit while trying to guess type. " ) ;
}
2020-07-06 12:24:24 -03:00
// Look in blocks first.
int last_assign_line = - 1 ;
const GDScriptParser : : ExpressionNode * last_assigned_expression = nullptr ;
2024-08-30 13:05:26 +02:00
GDScriptCompletionIdentifier id_type ;
2020-07-06 12:24:24 -03:00
GDScriptParser : : SuiteNode * suite = p_context . current_suite ;
bool is_function_parameter = false ;
2023-07-25 14:21:49 +03:00
bool can_be_local = true ;
switch ( p_identifier - > source ) {
case GDScriptParser : : IdentifierNode : : MEMBER_VARIABLE :
case GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT :
case GDScriptParser : : IdentifierNode : : MEMBER_FUNCTION :
case GDScriptParser : : IdentifierNode : : MEMBER_SIGNAL :
case GDScriptParser : : IdentifierNode : : MEMBER_CLASS :
case GDScriptParser : : IdentifierNode : : INHERITED_VARIABLE :
case GDScriptParser : : IdentifierNode : : STATIC_VARIABLE :
can_be_local = false ;
break ;
default :
break ;
}
if ( can_be_local & & suite & & suite - > has_local ( p_identifier - > name ) ) {
const GDScriptParser : : SuiteNode : : Local & local = suite - > get_local ( p_identifier - > name ) ;
2024-08-30 13:05:26 +02:00
id_type . type = local . get_datatype ( ) ;
2023-07-25 14:21:49 +03:00
// Check initializer as the first assignment.
switch ( local . type ) {
case GDScriptParser : : SuiteNode : : Local : : VARIABLE :
if ( local . variable - > initializer ) {
last_assign_line = local . variable - > initializer - > end_line ;
last_assigned_expression = local . variable - > initializer ;
}
break ;
case GDScriptParser : : SuiteNode : : Local : : CONSTANT :
if ( local . constant - > initializer ) {
last_assign_line = local . constant - > initializer - > end_line ;
last_assigned_expression = local . constant - > initializer ;
}
break ;
case GDScriptParser : : SuiteNode : : Local : : PARAMETER :
if ( local . parameter - > initializer ) {
last_assign_line = local . parameter - > initializer - > end_line ;
last_assigned_expression = local . parameter - > initializer ;
}
is_function_parameter = true ;
break ;
default :
break ;
2020-07-06 12:24:24 -03:00
}
2023-11-22 16:39:30 +01:00
} else {
if ( p_context . current_class ) {
GDScriptCompletionIdentifier base_identifier ;
GDScriptCompletionIdentifier base ;
base . value = p_context . base ;
base . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
base . type . kind = GDScriptParser : : DataType : : CLASS ;
base . type . class_type = p_context . current_class ;
base . type . is_meta_type = p_context . current_function & & p_context . current_function - > is_static ;
if ( _guess_identifier_type_from_base ( p_context , base , p_identifier - > name , base_identifier ) ) {
2024-08-30 13:05:26 +02:00
id_type = base_identifier ;
2023-11-22 16:39:30 +01:00
}
}
2020-07-06 12:24:24 -03:00
}
while ( suite ) {
for ( int i = 0 ; i < suite - > statements . size ( ) ; i + + ) {
2022-07-04 10:08:41 +03:00
if ( suite - > statements [ i ] - > end_line > = p_context . current_line ) {
2020-07-06 12:24:24 -03:00
break ;
}
switch ( suite - > statements [ i ] - > type ) {
case GDScriptParser : : Node : : ASSIGNMENT : {
const GDScriptParser : : AssignmentNode * assign = static_cast < const GDScriptParser : : AssignmentNode * > ( suite - > statements [ i ] ) ;
if ( assign - > end_line > last_assign_line & & assign - > assignee & & assign - > assigned_value & & assign - > assignee - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
const GDScriptParser : : IdentifierNode * id = static_cast < const GDScriptParser : : IdentifierNode * > ( assign - > assignee ) ;
2023-07-25 14:21:49 +03:00
if ( id - > name = = p_identifier - > name & & id - > source = = p_identifier - > source ) {
2020-07-06 12:24:24 -03:00
last_assign_line = assign - > assigned_value - > end_line ;
last_assigned_expression = assign - > assigned_value ;
}
}
} break ;
default :
// TODO: Check sub blocks (control flow statements) as they might also reassign stuff.
break ;
}
}
2023-02-17 01:16:24 +02:00
if ( suite - > parent_if & & suite - > parent_if - > condition & & suite - > parent_if - > condition - > type = = GDScriptParser : : Node : : TYPE_TEST ) {
2020-07-06 12:24:24 -03:00
// Operator `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.
2025-01-08 11:56:49 +02:00
// TODO: this could be hacked to detect AND-ed conditions too...
2023-02-17 01:16:24 +02:00
const GDScriptParser : : TypeTestNode * type_test = static_cast < const GDScriptParser : : TypeTestNode * > ( suite - > parent_if - > condition ) ;
2023-07-25 14:21:49 +03:00
if ( type_test - > operand & & type_test - > test_type & & type_test - > operand - > type = = GDScriptParser : : Node : : IDENTIFIER & & static_cast < const GDScriptParser : : IdentifierNode * > ( type_test - > operand ) - > name = = p_identifier - > name & & static_cast < const GDScriptParser : : IdentifierNode * > ( type_test - > operand ) - > source = = p_identifier - > source ) {
2020-07-06 12:24:24 -03:00
// Bingo.
GDScriptParser : : CompletionContext c = p_context ;
2023-02-17 01:16:24 +02:00
c . current_line = type_test - > operand - > start_line ;
2020-07-06 12:24:24 -03:00
c . current_suite = suite ;
2023-03-09 23:53:16 +02:00
if ( type_test - > test_datatype . is_hard_type ( ) ) {
2024-08-30 13:05:26 +02:00
id_type . type = type_test - > test_datatype ;
2020-07-06 12:24:24 -03:00
if ( last_assign_line < c . current_line ) {
// Override last assignment.
last_assign_line = c . current_line ;
last_assigned_expression = nullptr ;
}
}
}
}
suite = suite - > parent_block ;
}
2022-07-04 02:49:36 +03:00
if ( last_assigned_expression & & last_assign_line < p_context . current_line ) {
2020-07-06 12:24:24 -03:00
GDScriptParser : : CompletionContext c = p_context ;
c . current_line = last_assign_line ;
2023-11-22 16:39:30 +01:00
GDScriptCompletionIdentifier assigned_type ;
if ( _guess_expression_type ( c , last_assigned_expression , assigned_type ) ) {
2024-05-31 10:19:03 +02:00
if ( id_type . type . is_set ( ) & & ( assigned_type . type . kind = = GDScriptParser : : DataType : : VARIANT | | ( assigned_type . type . is_set ( ) & & ! GDScriptAnalyzer : : check_type_compatibility ( id_type . type , assigned_type . type ) ) ) ) {
2023-11-22 16:39:30 +01:00
// The assigned type is incompatible. The annotated type takes priority.
2024-08-30 13:05:26 +02:00
r_type = id_type ;
2023-11-22 16:39:30 +01:00
r_type . assigned_expression = last_assigned_expression ;
} else {
r_type = assigned_type ;
}
2020-07-06 12:24:24 -03:00
return true ;
}
}
2021-09-02 19:54:55 -03:00
if ( is_function_parameter & & p_context . current_function & & p_context . current_function - > source_lambda = = nullptr & & p_context . current_class ) {
2020-07-06 12:24:24 -03:00
// Check if it's override of native function, then we can assume the type from the signature.
GDScriptParser : : DataType base_type = p_context . current_class - > base_type ;
while ( base_type . is_set ( ) ) {
switch ( base_type . kind ) {
case GDScriptParser : : DataType : : CLASS :
if ( base_type . class_type - > has_function ( p_context . current_function - > identifier - > name ) ) {
GDScriptParser : : FunctionNode * parent_function = base_type . class_type - > get_member ( p_context . current_function - > identifier - > name ) . function ;
2023-07-25 14:21:49 +03:00
if ( parent_function - > parameters_indices . has ( p_identifier - > name ) ) {
const GDScriptParser : : ParameterNode * parameter = parent_function - > parameters [ parent_function - > parameters_indices [ p_identifier - > name ] ] ;
2024-08-30 13:05:26 +02:00
if ( ( ! id_type . type . is_set ( ) | | id_type . type . is_variant ( ) ) & & parameter - > get_datatype ( ) . is_hard_type ( ) ) {
id_type . type = parameter - > get_datatype ( ) ;
2023-02-02 22:47:41 +10:00
}
if ( parameter - > initializer ) {
GDScriptParser : : CompletionContext c = p_context ;
c . current_function = parent_function ;
c . current_class = base_type . class_type ;
c . base = nullptr ;
if ( _guess_expression_type ( c , parameter - > initializer , r_type ) ) {
return true ;
}
2020-07-06 12:24:24 -03:00
}
}
}
2021-04-05 16:34:02 +02:00
base_type = base_type . class_type - > base_type ;
2020-07-06 12:24:24 -03:00
break ;
case GDScriptParser : : DataType : : NATIVE : {
2024-08-30 13:05:26 +02:00
if ( id_type . type . is_set ( ) & & ! id_type . type . is_variant ( ) ) {
2020-07-06 12:24:24 -03:00
base_type = GDScriptParser : : DataType ( ) ;
break ;
}
MethodInfo info ;
2021-08-17 15:06:54 +02:00
if ( ClassDB : : get_method_info ( base_type . native_type , p_context . current_function - > identifier - > name , & info ) ) {
2021-07-15 23:45:57 -04:00
for ( const PropertyInfo & E : info . arguments ) {
2023-07-25 14:21:49 +03:00
if ( E . name = = p_identifier - > name ) {
2021-07-15 23:45:57 -04:00
r_type = _type_from_property ( E ) ;
2020-07-06 12:24:24 -03:00
return true ;
}
}
}
base_type = GDScriptParser : : DataType ( ) ;
} break ;
default :
break ;
}
}
}
2024-08-30 13:05:26 +02:00
if ( id_type . type . is_set ( ) & & ! id_type . type . is_variant ( ) ) {
r_type = id_type ;
2020-07-06 12:24:24 -03:00
return true ;
}
// Check global scripts.
2023-07-25 14:21:49 +03:00
if ( ScriptServer : : is_global_class ( p_identifier - > name ) ) {
String script = ScriptServer : : get_global_class_path ( p_identifier - > name ) ;
2020-07-06 12:24:24 -03:00
if ( script . to_lower ( ) . ends_with ( " .gd " ) ) {
2024-07-16 08:49:41 +02:00
Ref < GDScriptParserRef > parser = p_context . parser - > get_depended_parser_for ( script ) ;
if ( parser . is_valid ( ) & & parser - > raise_status ( GDScriptParserRef : : INTERFACE_SOLVED ) = = OK ) {
2020-07-06 12:24:24 -03:00
r_type . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
r_type . type . script_path = script ;
r_type . type . class_type = parser - > get_parser ( ) - > get_tree ( ) ;
2022-12-13 12:41:29 +07:00
r_type . type . is_meta_type = true ;
2020-07-06 12:24:24 -03:00
r_type . type . is_constant = false ;
r_type . type . kind = GDScriptParser : : DataType : : CLASS ;
r_type . value = Variant ( ) ;
return true ;
}
} else {
2023-07-25 14:21:49 +03:00
Ref < Script > scr = ResourceLoader : : load ( ScriptServer : : get_global_class_path ( p_identifier - > name ) ) ;
2020-07-06 12:24:24 -03:00
if ( scr . is_valid ( ) ) {
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( scr , p_context ) ;
2020-07-06 12:24:24 -03:00
r_type . type . is_meta_type = true ;
return true ;
}
}
return false ;
}
2022-07-18 16:35:25 +03:00
// Check global variables (including autoloads).
2023-07-25 14:21:49 +03:00
if ( GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) . has ( p_identifier - > name ) ) {
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( GDScriptLanguage : : get_singleton ( ) - > get_named_globals_map ( ) [ p_identifier - > name ] , p_context ) ;
2020-07-06 12:24:24 -03:00
return true ;
}
// Check ClassDB.
2023-07-25 14:21:49 +03:00
if ( ClassDB : : class_exists ( p_identifier - > name ) & & ClassDB : : is_class_exposed ( p_identifier - > name ) ) {
2020-07-06 12:24:24 -03:00
r_type . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
r_type . type . kind = GDScriptParser : : DataType : : NATIVE ;
2023-10-04 15:03:53 +03:00
r_type . type . builtin_type = Variant : : OBJECT ;
2023-07-25 14:21:49 +03:00
r_type . type . native_type = p_identifier - > name ;
2020-07-06 12:24:24 -03:00
r_type . type . is_constant = true ;
2023-07-25 14:21:49 +03:00
if ( Engine : : get_singleton ( ) - > has_singleton ( p_identifier - > name ) ) {
2022-08-15 23:28:49 +08:00
r_type . type . is_meta_type = false ;
2023-07-25 14:21:49 +03:00
r_type . value = Engine : : get_singleton ( ) - > get_singleton_object ( p_identifier - > name ) ;
2022-08-15 23:28:49 +08:00
} else {
r_type . type . is_meta_type = true ;
r_type . value = Variant ( ) ;
}
2024-07-31 23:44:48 +02:00
return true ;
2020-07-06 12:24:24 -03:00
}
return false ;
}
static bool _guess_identifier_type_from_base ( GDScriptParser : : CompletionContext & p_context , const GDScriptCompletionIdentifier & p_base , const StringName & p_identifier , GDScriptCompletionIdentifier & r_type ) {
2023-02-20 14:55:13 -03:00
static int recursion_depth = 0 ;
RecursionCheck recursion ( & recursion_depth ) ;
if ( unlikely ( recursion . check ( ) ) ) {
ERR_FAIL_V_MSG ( false , " Reached recursion limit while trying to guess type. " ) ;
}
2020-07-06 12:24:24 -03:00
GDScriptParser : : DataType base_type = p_base . type ;
bool is_static = base_type . is_meta_type ;
while ( base_type . is_set ( ) ) {
switch ( base_type . kind ) {
case GDScriptParser : : DataType : : CLASS :
if ( base_type . class_type - > has_member ( p_identifier ) ) {
const GDScriptParser : : ClassNode : : Member & member = base_type . class_type - > get_member ( p_identifier ) ;
switch ( member . type ) {
case GDScriptParser : : ClassNode : : Member : : CONSTANT :
r_type . type = member . constant - > get_datatype ( ) ;
if ( member . constant - > initializer & & member . constant - > initializer - > is_constant ) {
r_type . value = member . constant - > initializer - > reduced_value ;
}
return true ;
case GDScriptParser : : ClassNode : : Member : : VARIABLE :
2023-10-11 16:06:03 +03:00
if ( ! is_static | | member . variable - > is_static ) {
2022-07-06 04:25:44 +03:00
if ( member . variable - > get_datatype ( ) . is_set ( ) & & ! member . variable - > get_datatype ( ) . is_variant ( ) ) {
r_type . type = member . variable - > get_datatype ( ) ;
return true ;
} else if ( member . variable - > initializer ) {
2020-07-06 12:24:24 -03:00
const GDScriptParser : : ExpressionNode * init = member . variable - > initializer ;
if ( init - > is_constant ) {
r_type . value = init - > reduced_value ;
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( init - > reduced_value , p_context ) ;
2020-07-06 12:24:24 -03:00
return true ;
} else if ( init - > start_line = = p_context . current_line ) {
return false ;
2022-02-22 00:54:15 +00:00
// Detects if variable is assigned to itself
} else if ( _is_expression_named_identifier ( init , member . variable - > identifier - > name ) ) {
if ( member . variable - > initializer - > get_datatype ( ) . is_set ( ) ) {
r_type . type = member . variable - > initializer - > get_datatype ( ) ;
} else if ( member . variable - > get_datatype ( ) . is_set ( ) & & ! member . variable - > get_datatype ( ) . is_variant ( ) ) {
r_type . type = member . variable - > get_datatype ( ) ;
}
return true ;
2020-07-06 12:24:24 -03:00
} else if ( _guess_expression_type ( p_context , init , r_type ) ) {
return true ;
} else if ( init - > get_datatype ( ) . is_set ( ) & & ! init - > get_datatype ( ) . is_variant ( ) ) {
r_type . type = init - > get_datatype ( ) ;
return true ;
}
}
}
// TODO: Check assignments in constructor.
return false ;
case GDScriptParser : : ClassNode : : Member : : ENUM :
r_type . type = member . m_enum - > get_datatype ( ) ;
r_type . enumeration = member . m_enum - > identifier - > name ;
return true ;
case GDScriptParser : : ClassNode : : Member : : ENUM_VALUE :
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( member . enum_value . value , p_context ) ;
2020-07-06 12:24:24 -03:00
return true ;
case GDScriptParser : : ClassNode : : Member : : SIGNAL :
r_type . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
r_type . type . kind = GDScriptParser : : DataType : : BUILTIN ;
r_type . type . builtin_type = Variant : : SIGNAL ;
2024-08-31 12:12:19 +02:00
r_type . type . method_info = member . signal - > method_info ;
2020-07-06 12:24:24 -03:00
return true ;
case GDScriptParser : : ClassNode : : Member : : FUNCTION :
if ( is_static & & ! member . function - > is_static ) {
return false ;
}
2024-08-31 12:12:19 +02:00
r_type = _callable_type_from_method_info ( member . function - > info ) ;
2020-07-06 12:24:24 -03:00
return true ;
case GDScriptParser : : ClassNode : : Member : : CLASS :
r_type . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
r_type . type . kind = GDScriptParser : : DataType : : CLASS ;
r_type . type . class_type = member . m_class ;
2022-12-13 12:41:29 +07:00
r_type . type . is_meta_type = true ;
2020-07-06 12:24:24 -03:00
return true ;
2022-07-03 22:30:08 +03:00
case GDScriptParser : : ClassNode : : Member : : GROUP :
return false ; // No-op, but silences warnings.
2020-07-06 12:24:24 -03:00
case GDScriptParser : : ClassNode : : Member : : UNDEFINED :
return false ; // Unreachable.
}
return false ;
}
base_type = base_type . class_type - > base_type ;
break ;
case GDScriptParser : : DataType : : SCRIPT : {
Ref < Script > scr = base_type . script_type ;
if ( scr . is_valid ( ) ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , Variant > constants ;
2020-07-06 12:24:24 -03:00
scr - > get_constants ( & constants ) ;
if ( constants . has ( p_identifier ) ) {
2024-01-16 23:04:10 +01:00
r_type = _type_from_variant ( constants [ p_identifier ] , p_context ) ;
2020-07-06 12:24:24 -03:00
return true ;
}
2023-10-11 16:06:03 +03:00
List < PropertyInfo > members ;
if ( is_static ) {
scr - > get_property_list ( & members ) ;
} else {
2020-07-06 12:24:24 -03:00
scr - > get_script_property_list ( & members ) ;
2023-10-11 16:06:03 +03:00
}
for ( const PropertyInfo & prop : members ) {
if ( prop . name = = p_identifier ) {
r_type = _type_from_property ( prop ) ;
return true ;
2020-07-06 12:24:24 -03:00
}
}
2023-10-11 16:06:03 +03:00
2024-08-31 12:12:19 +02:00
if ( scr - > has_method ( p_identifier ) ) {
MethodInfo mi = scr - > get_method_info ( p_identifier ) ;
r_type = _callable_type_from_method_info ( mi ) ;
return true ;
}
2020-07-06 12:24:24 -03:00
Ref < Script > parent = scr - > get_base_script ( ) ;
if ( parent . is_valid ( ) ) {
base_type . script_type = parent ;
} else {
base_type . kind = GDScriptParser : : DataType : : NATIVE ;
2023-10-04 15:03:53 +03:00
base_type . builtin_type = Variant : : OBJECT ;
2020-07-06 12:24:24 -03:00
base_type . native_type = scr - > get_instance_base_type ( ) ;
}
} else {
return false ;
}
} break ;
case GDScriptParser : : DataType : : NATIVE : {
2021-08-17 15:06:54 +02:00
StringName class_name = base_type . native_type ;
2020-07-06 12:24:24 -03:00
if ( ! ClassDB : : class_exists ( class_name ) ) {
return false ;
}
// Skip constants since they're all integers. Type does not matter because int has no members.
PropertyInfo prop ;
if ( ClassDB : : get_property_info ( class_name , p_identifier , & prop ) ) {
StringName getter = ClassDB : : get_property_getter ( class_name , p_identifier ) ;
if ( getter ! = StringName ( ) ) {
MethodBind * g = ClassDB : : get_method ( class_name , getter ) ;
if ( g ) {
r_type = _type_from_property ( g - > get_return_info ( ) ) ;
return true ;
}
} else {
r_type = _type_from_property ( prop ) ;
return true ;
}
}
2024-08-31 12:12:19 +02:00
MethodInfo method ;
if ( ClassDB : : get_method_info ( class_name , p_identifier , & method ) ) {
r_type = _callable_type_from_method_info ( method ) ;
return true ;
}
2024-07-31 23:44:48 +02:00
if ( ClassDB : : has_enum ( class_name , p_identifier ) ) {
r_type . type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
r_type . type . kind = GDScriptParser : : DataType : : ENUM ;
r_type . type . enum_type = p_identifier ;
r_type . type . is_constant = true ;
r_type . type . is_meta_type = true ;
r_type . type . native_type = String ( class_name ) + " . " + p_identifier ;
return true ;
}
2020-07-06 12:24:24 -03:00
return false ;
} break ;
case GDScriptParser : : DataType : : BUILTIN : {
2024-08-31 12:12:19 +02:00
if ( Variant : : has_builtin_method ( base_type . builtin_type , p_identifier ) ) {
r_type = _callable_type_from_method_info ( Variant : : get_builtin_method_info ( base_type . builtin_type , p_identifier ) ) ;
2020-07-06 12:24:24 -03:00
return true ;
2024-08-31 12:12:19 +02:00
} else {
Callable : : CallError err ;
Variant tmp ;
Variant : : construct ( base_type . builtin_type , tmp , nullptr , 0 , err ) ;
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
return false ;
}
bool valid = false ;
Variant res = tmp . get ( p_identifier , & valid ) ;
if ( valid ) {
r_type = _type_from_variant ( res , p_context ) ;
r_type . value = Variant ( ) ;
r_type . type . is_constant = false ;
return true ;
}
2020-07-06 12:24:24 -03:00
}
return false ;
} break ;
default : {
return false ;
} break ;
}
}
return false ;
}
static void _find_last_return_in_block ( GDScriptParser : : CompletionContext & p_context , int & r_last_return_line , const GDScriptParser : : ExpressionNode * * r_last_returned_value ) {
if ( ! p_context . current_suite ) {
return ;
}
for ( int i = 0 ; i < p_context . current_suite - > statements . size ( ) ; i + + ) {
if ( p_context . current_suite - > statements [ i ] - > start_line < r_last_return_line ) {
break ;
}
GDScriptParser : : CompletionContext c = p_context ;
switch ( p_context . current_suite - > statements [ i ] - > type ) {
case GDScriptParser : : Node : : FOR :
c . current_suite = static_cast < const GDScriptParser : : ForNode * > ( p_context . current_suite - > statements [ i ] ) - > loop ;
_find_last_return_in_block ( c , r_last_return_line , r_last_returned_value ) ;
break ;
case GDScriptParser : : Node : : WHILE :
c . current_suite = static_cast < const GDScriptParser : : WhileNode * > ( p_context . current_suite - > statements [ i ] ) - > loop ;
_find_last_return_in_block ( c , r_last_return_line , r_last_returned_value ) ;
break ;
case GDScriptParser : : Node : : IF : {
const GDScriptParser : : IfNode * _if = static_cast < const GDScriptParser : : IfNode * > ( p_context . current_suite - > statements [ i ] ) ;
c . current_suite = _if - > true_block ;
_find_last_return_in_block ( c , r_last_return_line , r_last_returned_value ) ;
if ( _if - > false_block ) {
c . current_suite = _if - > false_block ;
_find_last_return_in_block ( c , r_last_return_line , r_last_returned_value ) ;
}
} break ;
case GDScriptParser : : Node : : MATCH : {
const GDScriptParser : : MatchNode * match = static_cast < const GDScriptParser : : MatchNode * > ( p_context . current_suite - > statements [ i ] ) ;
2020-07-15 22:02:44 -03:00
for ( int j = 0 ; j < match - > branches . size ( ) ; j + + ) {
c . current_suite = match - > branches [ j ] - > block ;
2020-07-06 12:24:24 -03:00
_find_last_return_in_block ( c , r_last_return_line , r_last_returned_value ) ;
}
} break ;
case GDScriptParser : : Node : : RETURN : {
const GDScriptParser : : ReturnNode * ret = static_cast < const GDScriptParser : : ReturnNode * > ( p_context . current_suite - > statements [ i ] ) ;
if ( ret - > return_value ) {
if ( ret - > start_line > r_last_return_line ) {
r_last_return_line = ret - > start_line ;
* r_last_returned_value = ret - > return_value ;
}
}
} break ;
default :
break ;
}
}
}
static bool _guess_method_return_type_from_base ( GDScriptParser : : CompletionContext & p_context , const GDScriptCompletionIdentifier & p_base , const StringName & p_method , GDScriptCompletionIdentifier & r_type ) {
2023-02-20 14:55:13 -03:00
static int recursion_depth = 0 ;
RecursionCheck recursion ( & recursion_depth ) ;
if ( unlikely ( recursion . check ( ) ) ) {
ERR_FAIL_V_MSG ( false , " Reached recursion limit while trying to guess type. " ) ;
}
2020-07-06 12:24:24 -03:00
GDScriptParser : : DataType base_type = p_base . type ;
bool is_static = base_type . is_meta_type ;
2022-02-06 14:12:19 +01:00
if ( is_static & & p_method = = SNAME ( " new " ) ) {
2020-07-06 12:24:24 -03:00
r_type . type = base_type ;
r_type . type . is_meta_type = false ;
r_type . type . is_constant = false ;
return true ;
}
while ( base_type . is_set ( ) & & ! base_type . is_variant ( ) ) {
switch ( base_type . kind ) {
case GDScriptParser : : DataType : : CLASS :
if ( base_type . class_type - > has_function ( p_method ) ) {
2024-07-29 21:23:12 -07:00
GDScriptParser : : FunctionNode * method = base_type . class_type - > get_member ( p_method ) . function ;
2020-07-06 12:24:24 -03:00
if ( ! is_static | | method - > is_static ) {
2022-12-11 14:17:27 +07:00
if ( method - > get_datatype ( ) . is_set ( ) & & ! method - > get_datatype ( ) . is_variant ( ) ) {
r_type . type = method - > get_datatype ( ) ;
return true ;
}
2020-07-06 12:24:24 -03:00
int last_return_line = - 1 ;
const GDScriptParser : : ExpressionNode * last_returned_value = nullptr ;
GDScriptParser : : CompletionContext c = p_context ;
c . current_class = base_type . class_type ;
2024-07-29 21:23:12 -07:00
c . current_function = method ;
2020-07-06 12:24:24 -03:00
c . current_suite = method - > body ;
_find_last_return_in_block ( c , last_return_line , & last_returned_value ) ;
if ( last_returned_value ) {
c . current_line = c . current_suite - > end_line ;
if ( _guess_expression_type ( c , last_returned_value , r_type ) ) {
return true ;
}
}
}
}
base_type = base_type . class_type - > base_type ;
break ;
case GDScriptParser : : DataType : : SCRIPT : {
Ref < Script > scr = base_type . script_type ;
if ( scr . is_valid ( ) ) {
List < MethodInfo > methods ;
scr - > get_script_method_list ( & methods ) ;
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & mi : methods ) {
2020-07-06 12:24:24 -03:00
if ( mi . name = = p_method ) {
r_type = _type_from_property ( mi . return_val ) ;
return true ;
}
}
Ref < Script > base_script = scr - > get_base_script ( ) ;
if ( base_script . is_valid ( ) ) {
base_type . script_type = base_script ;
} else {
base_type . kind = GDScriptParser : : DataType : : NATIVE ;
2023-10-04 15:03:53 +03:00
base_type . builtin_type = Variant : : OBJECT ;
2020-07-06 12:24:24 -03:00
base_type . native_type = scr - > get_instance_base_type ( ) ;
}
} else {
return false ;
}
} break ;
case GDScriptParser : : DataType : : NATIVE : {
2021-08-17 15:06:54 +02:00
if ( ! ClassDB : : class_exists ( base_type . native_type ) ) {
2020-07-06 12:24:24 -03:00
return false ;
}
2021-08-17 15:06:54 +02:00
MethodBind * mb = ClassDB : : get_method ( base_type . native_type , p_method ) ;
2020-07-06 12:24:24 -03:00
if ( mb ) {
r_type = _type_from_property ( mb - > get_return_info ( ) ) ;
return true ;
}
return false ;
} break ;
case GDScriptParser : : DataType : : BUILTIN : {
Callable : : CallError err ;
2020-11-09 00:19:09 -03:00
Variant tmp ;
Variant : : construct ( base_type . builtin_type , tmp , nullptr , 0 , err ) ;
2020-07-06 12:24:24 -03:00
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
return false ;
}
List < MethodInfo > methods ;
tmp . get_method_list ( & methods ) ;
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & mi : methods ) {
2020-07-06 12:24:24 -03:00
if ( mi . name = = p_method ) {
r_type = _type_from_property ( mi . return_val ) ;
return true ;
}
}
return false ;
} break ;
default : {
return false ;
}
}
}
return false ;
}
2024-08-31 12:12:19 +02:00
static bool _guess_expecting_callable ( GDScriptParser : : CompletionContext & p_context ) {
if ( p_context . call . call ! = nullptr & & p_context . call . call - > type = = GDScriptParser : : Node : : CALL ) {
GDScriptParser : : CallNode * call_node = static_cast < GDScriptParser : : CallNode * > ( p_context . call . call ) ;
GDScriptCompletionIdentifier ci ;
if ( _guess_expression_type ( p_context , call_node - > callee , ci ) ) {
if ( ci . type . kind = = GDScriptParser : : DataType : : BUILTIN & & ci . type . builtin_type = = Variant : : CALLABLE ) {
if ( p_context . call . argument > = 0 & & p_context . call . argument < ci . type . method_info . arguments . size ( ) ) {
return ci . type . method_info . arguments . get ( p_context . call . argument ) . type = = Variant : : CALLABLE ;
}
}
}
}
return false ;
}
2022-05-13 15:04:37 +02:00
static void _find_enumeration_candidates ( GDScriptParser : : CompletionContext & p_context , const String & p_enum_hint , HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result ) {
2024-12-05 17:56:08 +01:00
if ( ! p_enum_hint . contains_char ( ' . ' ) ) {
2020-07-06 12:24:24 -03:00
// Global constant or in the current class.
StringName current_enum = p_enum_hint ;
if ( p_context . current_class & & p_context . current_class - > has_member ( current_enum ) & & p_context . current_class - > get_member ( current_enum ) . type = = GDScriptParser : : ClassNode : : Member : : ENUM ) {
const GDScriptParser : : EnumNode * _enum = p_context . current_class - > get_member ( current_enum ) . m_enum ;
for ( int i = 0 ; i < _enum - > values . size ( ) ; i + + ) {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( _enum - > values [ i ] . identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_ENUM ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
} else {
2020-11-07 19:33:38 -03:00
for ( int i = 0 ; i < CoreConstants : : get_global_constant_count ( ) ; i + + ) {
if ( CoreConstants : : get_global_constant_enum ( i ) = = current_enum ) {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( CoreConstants : : get_global_constant_name ( i ) , ScriptLanguage : : CODE_COMPLETION_KIND_ENUM ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
}
}
} else {
2024-11-16 17:16:07 +01:00
String class_name = p_enum_hint . get_slicec ( ' . ' , 0 ) ;
String enum_name = p_enum_hint . get_slicec ( ' . ' , 1 ) ;
2020-07-06 12:24:24 -03:00
if ( ! ClassDB : : class_exists ( class_name ) ) {
return ;
}
List < StringName > enum_constants ;
ClassDB : : get_enum_constants ( class_name , enum_name , & enum_constants ) ;
2021-07-24 15:46:25 +02:00
for ( const StringName & E : enum_constants ) {
2021-07-15 23:45:57 -04:00
String candidate = class_name + " . " + E ;
2022-03-09 00:03:36 +10:00
int location = _get_enum_constant_location ( class_name , E ) ;
ScriptLanguage : : CodeCompletionOption option ( candidate , ScriptLanguage : : CODE_COMPLETION_KIND_ENUM , location ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
}
}
2025-06-22 12:04:09 -07:00
static void _list_call_arguments ( GDScriptParser : : CompletionContext & p_context , const GDScriptCompletionIdentifier & p_base , const GDScriptParser : : CallNode * p_call , int p_argidx , bool p_static , HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result , String & r_arghint ) {
2020-07-06 12:24:24 -03:00
Variant base = p_base . value ;
GDScriptParser : : DataType base_type = p_base . type ;
2025-06-22 12:04:09 -07:00
const StringName & method = p_call - > function_name ;
2020-07-06 12:24:24 -03:00
2021-08-13 21:24:02 +02:00
const String quote_style = EDITOR_GET ( " text_editor/completion/use_single_quotes " ) ? " ' " : " \" " ;
2022-10-06 22:08:01 +02:00
const bool use_string_names = EDITOR_GET ( " text_editor/completion/add_string_name_literals " ) ;
const bool use_node_paths = EDITOR_GET ( " text_editor/completion/add_node_path_literals " ) ;
2020-07-06 12:24:24 -03:00
while ( base_type . is_set ( ) & & ! base_type . is_variant ( ) ) {
switch ( base_type . kind ) {
case GDScriptParser : : DataType : : CLASS : {
2025-06-22 12:04:09 -07:00
if ( base_type . is_meta_type & & method = = SNAME ( " new " ) ) {
2024-05-22 13:47:19 +03:00
const GDScriptParser : : ClassNode * current = base_type . class_type ;
do {
if ( current - > has_member ( " _init " ) ) {
const GDScriptParser : : ClassNode : : Member & member = current - > get_member ( " _init " ) ;
if ( member . type = = GDScriptParser : : ClassNode : : Member : : FUNCTION ) {
r_arghint = base_type . class_type - > get_datatype ( ) . to_string ( ) + " new " + _make_arguments_hint ( member . function , p_argidx , true ) ;
return ;
}
}
current = current - > base_type . class_type ;
} while ( current ! = nullptr ) ;
r_arghint = base_type . class_type - > get_datatype ( ) . to_string ( ) + " new() " ;
return ;
}
2025-06-22 12:04:09 -07:00
if ( base_type . class_type - > has_member ( method ) ) {
const GDScriptParser : : ClassNode : : Member & member = base_type . class_type - > get_member ( method ) ;
2020-07-06 12:24:24 -03:00
if ( member . type = = GDScriptParser : : ClassNode : : Member : : FUNCTION ) {
r_arghint = _make_arguments_hint ( member . function , p_argidx ) ;
return ;
}
}
base_type = base_type . class_type - > base_type ;
} break ;
2023-11-26 14:37:45 +01:00
case GDScriptParser : : DataType : : SCRIPT : {
2025-06-22 12:04:09 -07:00
if ( base_type . script_type - > is_valid ( ) & & base_type . script_type - > has_method ( method ) ) {
r_arghint = _make_arguments_hint ( base_type . script_type - > get_method_info ( method ) , p_argidx ) ;
2023-11-26 14:37:45 +01:00
return ;
}
Ref < Script > base_script = base_type . script_type - > get_base_script ( ) ;
if ( base_script . is_valid ( ) ) {
base_type . script_type = base_script ;
} else {
base_type . kind = GDScriptParser : : DataType : : NATIVE ;
base_type . builtin_type = Variant : : OBJECT ;
base_type . native_type = base_type . script_type - > get_instance_base_type ( ) ;
}
} break ;
2020-07-06 12:24:24 -03:00
case GDScriptParser : : DataType : : NATIVE : {
2021-08-17 15:06:54 +02:00
StringName class_name = base_type . native_type ;
2020-07-06 12:24:24 -03:00
if ( ! ClassDB : : class_exists ( class_name ) ) {
base_type . kind = GDScriptParser : : DataType : : UNRESOLVED ;
break ;
}
MethodInfo info ;
int method_args = 0 ;
2025-06-22 12:04:09 -07:00
if ( ClassDB : : get_method_info ( class_name , method , & info ) ) {
2020-07-06 12:24:24 -03:00
method_args = info . arguments . size ( ) ;
if ( base . get_type ( ) = = Variant : : OBJECT ) {
Object * obj = base . operator Object * ( ) ;
if ( obj ) {
List < String > options ;
2025-06-22 12:04:09 -07:00
obj - > get_argument_options ( method , p_argidx , & options ) ;
2021-10-01 17:06:48 +02:00
for ( String & opt : options ) {
2022-10-06 22:08:01 +02:00
// Handle user preference.
2021-10-01 17:06:48 +02:00
if ( opt . is_quoted ( ) ) {
2022-10-06 22:08:01 +02:00
opt = opt . unquote ( ) . quote ( quote_style ) ;
2024-05-07 12:48:51 +02:00
if ( use_string_names & & info . arguments [ p_argidx ] . type = = Variant : : STRING_NAME ) {
2025-06-22 12:04:09 -07:00
if ( p_call - > arguments . size ( ) > p_argidx & & p_call - > arguments [ p_argidx ] & & p_call - > arguments [ p_argidx ] - > type = = GDScriptParser : : Node : : LITERAL ) {
GDScriptParser : : LiteralNode * literal = static_cast < GDScriptParser : : LiteralNode * > ( p_call - > arguments [ p_argidx ] ) ;
if ( literal - > value . get_type ( ) = = Variant : : STRING ) {
opt = " & " + opt ;
}
} else {
opt = " & " + opt ;
}
2024-05-07 12:48:51 +02:00
} else if ( use_node_paths & & info . arguments [ p_argidx ] . type = = Variant : : NODE_PATH ) {
2025-06-22 12:04:09 -07:00
if ( p_call - > arguments . size ( ) > p_argidx & & p_call - > arguments [ p_argidx ] & & p_call - > arguments [ p_argidx ] - > type = = GDScriptParser : : Node : : LITERAL ) {
GDScriptParser : : LiteralNode * literal = static_cast < GDScriptParser : : LiteralNode * > ( p_call - > arguments [ p_argidx ] ) ;
if ( literal - > value . get_type ( ) = = Variant : : STRING ) {
opt = " ^ " + opt ;
}
} else {
opt = " ^ " + opt ;
}
2022-10-06 22:08:01 +02:00
}
2021-10-01 17:06:48 +02:00
}
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( opt , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
}
}
if ( p_argidx < method_args ) {
2024-05-07 12:48:51 +02:00
const PropertyInfo & arg_info = info . arguments [ p_argidx ] ;
2022-06-24 11:16:37 +02:00
if ( arg_info . usage & ( PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD ) ) {
2020-07-06 12:24:24 -03:00
_find_enumeration_candidates ( p_context , arg_info . class_name , r_result ) ;
}
}
r_arghint = _make_arguments_hint ( info , p_argidx ) ;
}
2025-06-22 12:04:09 -07:00
if ( p_argidx = = 1 & & p_call & & ClassDB : : is_parent_class ( class_name , SNAME ( " Tween " ) ) & & method = = SNAME ( " tween_property " ) ) {
2023-04-29 23:10:12 +02:00
// Get tweened objects properties.
2025-06-22 12:04:09 -07:00
if ( p_call - > arguments . is_empty ( ) ) {
2025-01-16 10:40:13 +02:00
base_type . kind = GDScriptParser : : DataType : : UNRESOLVED ;
break ;
}
2025-06-22 12:04:09 -07:00
GDScriptParser : : ExpressionNode * tweened_object = p_call - > arguments [ 0 ] ;
2025-01-16 10:40:13 +02:00
if ( ! tweened_object ) {
base_type . kind = GDScriptParser : : DataType : : UNRESOLVED ;
break ;
}
2023-04-29 23:10:12 +02:00
StringName native_type = tweened_object - > datatype . native_type ;
switch ( tweened_object - > datatype . kind ) {
case GDScriptParser : : DataType : : SCRIPT : {
Ref < Script > script = tweened_object - > datatype . script_type ;
native_type = script - > get_instance_base_type ( ) ;
int n = 0 ;
while ( script . is_valid ( ) ) {
List < PropertyInfo > properties ;
script - > get_script_property_list ( & properties ) ;
for ( const PropertyInfo & E : properties ) {
if ( E . usage & ( PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL ) ) {
continue ;
}
2024-06-11 22:28:18 +02:00
String name = E . name . quote ( quote_style ) ;
if ( use_node_paths ) {
2025-06-22 12:04:09 -07:00
if ( p_call - > arguments . size ( ) > p_argidx & & p_call - > arguments [ p_argidx ] & & p_call - > arguments [ p_argidx ] - > type = = GDScriptParser : : Node : : LITERAL ) {
GDScriptParser : : LiteralNode * literal = static_cast < GDScriptParser : : LiteralNode * > ( p_call - > arguments [ p_argidx ] ) ;
if ( literal - > value . get_type ( ) = = Variant : : STRING ) {
name = " ^ " + name ;
}
} else {
name = " ^ " + name ;
}
2024-06-11 22:28:18 +02:00
}
ScriptLanguage : : CodeCompletionOption option ( name , ScriptLanguage : : CODE_COMPLETION_KIND_MEMBER , ScriptLanguage : : CodeCompletionLocation : : LOCATION_LOCAL + n ) ;
2023-04-29 23:10:12 +02:00
r_result . insert ( option . display , option ) ;
}
script = script - > get_base_script ( ) ;
n + + ;
}
} break ;
case GDScriptParser : : DataType : : CLASS : {
GDScriptParser : : ClassNode * clss = tweened_object - > datatype . class_type ;
native_type = clss - > base_type . native_type ;
int n = 0 ;
while ( clss ) {
for ( GDScriptParser : : ClassNode : : Member member : clss - > members ) {
if ( member . type = = GDScriptParser : : ClassNode : : Member : : VARIABLE ) {
2024-06-11 22:28:18 +02:00
String name = member . get_name ( ) . quote ( quote_style ) ;
if ( use_node_paths ) {
2025-06-22 12:04:09 -07:00
if ( p_call - > arguments . size ( ) > p_argidx & & p_call - > arguments [ p_argidx ] & & p_call - > arguments [ p_argidx ] - > type = = GDScriptParser : : Node : : LITERAL ) {
GDScriptParser : : LiteralNode * literal = static_cast < GDScriptParser : : LiteralNode * > ( p_call - > arguments [ p_argidx ] ) ;
if ( literal - > value . get_type ( ) = = Variant : : STRING ) {
name = " ^ " + name ;
}
} else {
name = " ^ " + name ;
}
2024-06-11 22:28:18 +02:00
}
ScriptLanguage : : CodeCompletionOption option ( name , ScriptLanguage : : CODE_COMPLETION_KIND_MEMBER , ScriptLanguage : : CodeCompletionLocation : : LOCATION_LOCAL + n ) ;
2023-04-29 23:10:12 +02:00
r_result . insert ( option . display , option ) ;
}
}
if ( clss - > base_type . kind = = GDScriptParser : : DataType : : Kind : : CLASS ) {
clss = clss - > base_type . class_type ;
n + + ;
} else {
native_type = clss - > base_type . native_type ;
clss = nullptr ;
}
}
} break ;
default :
break ;
}
List < PropertyInfo > properties ;
ClassDB : : get_property_list ( native_type , & properties ) ;
for ( const PropertyInfo & E : properties ) {
if ( E . usage & ( PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL ) ) {
continue ;
}
2024-06-11 22:28:18 +02:00
String name = E . name . quote ( quote_style ) ;
if ( use_node_paths ) {
2025-06-22 12:04:09 -07:00
if ( p_call - > arguments . size ( ) > p_argidx & & p_call - > arguments [ p_argidx ] & & p_call - > arguments [ p_argidx ] - > type = = GDScriptParser : : Node : : LITERAL ) {
GDScriptParser : : LiteralNode * literal = static_cast < GDScriptParser : : LiteralNode * > ( p_call - > arguments [ p_argidx ] ) ;
if ( literal - > value . get_type ( ) = = Variant : : STRING ) {
name = " ^ " + name ;
}
} else {
name = " ^ " + name ;
}
2024-06-11 22:28:18 +02:00
}
ScriptLanguage : : CodeCompletionOption option ( name , ScriptLanguage : : CODE_COMPLETION_KIND_MEMBER ) ;
2023-04-29 23:10:12 +02:00
r_result . insert ( option . display , option ) ;
}
}
2025-06-22 12:04:09 -07:00
if ( p_argidx = = 0 & & ClassDB : : is_parent_class ( class_name , SNAME ( " Node " ) ) & & ( method = = SNAME ( " get_node " ) | | method = = SNAME ( " has_node " ) ) ) {
2020-07-06 12:24:24 -03:00
// Get autoloads
List < PropertyInfo > props ;
ProjectSettings : : get_singleton ( ) - > get_property_list ( & props ) ;
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & E : props ) {
2021-07-15 23:45:57 -04:00
String s = E . name ;
2020-07-06 12:24:24 -03:00
if ( ! s . begins_with ( " autoload/ " ) ) {
continue ;
}
2024-11-16 17:16:07 +01:00
String name = s . get_slicec ( ' / ' , 1 ) ;
2024-06-11 22:28:18 +02:00
String path = ( " /root/ " + name ) . quote ( quote_style ) ;
if ( use_node_paths ) {
2025-06-22 12:04:09 -07:00
if ( p_call - > arguments . size ( ) > p_argidx & & p_call - > arguments [ p_argidx ] & & p_call - > arguments [ p_argidx ] - > type = = GDScriptParser : : Node : : LITERAL ) {
GDScriptParser : : LiteralNode * literal = static_cast < GDScriptParser : : LiteralNode * > ( p_call - > arguments [ p_argidx ] ) ;
if ( literal - > value . get_type ( ) = = Variant : : STRING ) {
path = " ^ " + path ;
}
} else {
path = " ^ " + path ;
}
2024-06-11 22:28:18 +02:00
}
ScriptLanguage : : CodeCompletionOption option ( path , ScriptLanguage : : CODE_COMPLETION_KIND_NODE_PATH ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
}
2025-06-22 12:04:09 -07:00
if ( p_argidx = = 0 & & method_args > 0 & & ClassDB : : is_parent_class ( class_name , SNAME ( " InputEvent " ) ) & & method . operator String ( ) . contains ( " action " ) ) {
2020-07-06 12:24:24 -03:00
// Get input actions
List < PropertyInfo > props ;
ProjectSettings : : get_singleton ( ) - > get_property_list ( & props ) ;
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & E : props ) {
2021-07-15 23:45:57 -04:00
String s = E . name ;
2020-07-06 12:24:24 -03:00
if ( ! s . begins_with ( " input/ " ) ) {
continue ;
}
2024-11-16 17:16:07 +01:00
String name = s . get_slicec ( ' / ' , 1 ) . quote ( quote_style ) ;
2024-06-11 22:28:18 +02:00
if ( use_string_names ) {
2025-06-22 12:04:09 -07:00
if ( p_call - > arguments . size ( ) > p_argidx & & p_call - > arguments [ p_argidx ] & & p_call - > arguments [ p_argidx ] - > type = = GDScriptParser : : Node : : LITERAL ) {
GDScriptParser : : LiteralNode * literal = static_cast < GDScriptParser : : LiteralNode * > ( p_call - > arguments [ p_argidx ] ) ;
if ( literal - > value . get_type ( ) = = Variant : : STRING ) {
name = " & " + name ;
}
} else {
name = " & " + name ;
}
2024-06-11 22:28:18 +02:00
}
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( name , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT ) ;
2020-07-06 12:24:24 -03:00
r_result . insert ( option . display , option ) ;
}
}
2024-01-15 00:48:06 +01:00
if ( EDITOR_GET ( " text_editor/completion/complete_file_paths " ) ) {
2025-06-22 12:04:09 -07:00
if ( p_argidx = = 0 & & method = = SNAME ( " change_scene_to_file " ) & & ClassDB : : is_parent_class ( class_name , SNAME ( " SceneTree " ) ) ) {
2024-01-15 00:48:06 +01:00
HashMap < String , ScriptLanguage : : CodeCompletionOption > list ;
_get_directory_contents ( EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) , list , SNAME ( " PackedScene " ) ) ;
for ( const KeyValue < String , ScriptLanguage : : CodeCompletionOption > & key_value_pair : list ) {
ScriptLanguage : : CodeCompletionOption option = key_value_pair . value ;
r_result . insert ( option . display , option ) ;
}
}
}
2020-07-06 12:24:24 -03:00
base_type . kind = GDScriptParser : : DataType : : UNRESOLVED ;
} break ;
case GDScriptParser : : DataType : : BUILTIN : {
if ( base . get_type ( ) = = Variant : : NIL ) {
Callable : : CallError err ;
2020-11-09 00:19:09 -03:00
Variant : : construct ( base_type . builtin_type , base , nullptr , 0 , err ) ;
2020-07-06 12:24:24 -03:00
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
return ;
}
}
List < MethodInfo > methods ;
base . get_method_list ( & methods ) ;
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & E : methods ) {
2025-06-22 12:04:09 -07:00
if ( E . name = = method ) {
2021-07-15 23:45:57 -04:00
r_arghint = _make_arguments_hint ( E , p_argidx ) ;
2020-07-06 12:24:24 -03:00
return ;
}
}
base_type . kind = GDScriptParser : : DataType : : UNRESOLVED ;
} break ;
default : {
base_type . kind = GDScriptParser : : DataType : : UNRESOLVED ;
} break ;
}
}
}
2022-11-14 23:25:52 +03:00
static bool _get_subscript_type ( GDScriptParser : : CompletionContext & p_context , const GDScriptParser : : SubscriptNode * p_subscript , GDScriptParser : : DataType & r_base_type , Variant * r_base = nullptr ) {
2022-12-06 10:29:55 +03:00
if ( p_context . base = = nullptr ) {
return false ;
}
2023-07-12 19:27:21 +02:00
2022-12-06 10:29:55 +03:00
const GDScriptParser : : GetNodeNode * get_node = nullptr ;
switch ( p_subscript - > base - > type ) {
case GDScriptParser : : Node : : GET_NODE : {
get_node = static_cast < GDScriptParser : : GetNodeNode * > ( p_subscript - > base ) ;
} break ;
2022-11-14 23:25:52 +03:00
2022-12-06 10:29:55 +03:00
case GDScriptParser : : Node : : IDENTIFIER : {
const GDScriptParser : : IdentifierNode * identifier_node = static_cast < GDScriptParser : : IdentifierNode * > ( p_subscript - > base ) ;
2022-11-14 23:25:52 +03:00
2022-12-06 10:29:55 +03:00
switch ( identifier_node - > source ) {
case GDScriptParser : : IdentifierNode : : Source : : MEMBER_VARIABLE : {
if ( p_context . current_class ! = nullptr ) {
const StringName & member_name = identifier_node - > name ;
const GDScriptParser : : ClassNode * current_class = p_context . current_class ;
2022-11-14 23:25:52 +03:00
2022-12-06 10:29:55 +03:00
if ( current_class - > has_member ( member_name ) ) {
const GDScriptParser : : ClassNode : : Member & member = current_class - > get_member ( member_name ) ;
2022-11-14 23:25:52 +03:00
2022-12-06 10:29:55 +03:00
if ( member . type = = GDScriptParser : : ClassNode : : Member : : VARIABLE ) {
const GDScriptParser : : VariableNode * variable = static_cast < GDScriptParser : : VariableNode * > ( member . variable ) ;
if ( variable - > initializer & & variable - > initializer - > type = = GDScriptParser : : Node : : GET_NODE ) {
get_node = static_cast < GDScriptParser : : GetNodeNode * > ( variable - > initializer ) ;
}
2022-11-14 23:25:52 +03:00
}
}
}
2022-12-06 10:29:55 +03:00
} break ;
case GDScriptParser : : IdentifierNode : : Source : : LOCAL_VARIABLE : {
2025-06-17 17:03:59 +02:00
// TODO: Do basic assignment flow analysis like in `_guess_expression_type`.
const GDScriptParser : : SuiteNode : : Local local = identifier_node - > suite - > get_local ( identifier_node - > name ) ;
switch ( local . type ) {
case GDScriptParser : : SuiteNode : : Local : : CONSTANT : {
if ( local . constant - > initializer & & local . constant - > initializer - > type = = GDScriptParser : : Node : : GET_NODE ) {
get_node = static_cast < GDScriptParser : : GetNodeNode * > ( local . constant - > initializer ) ;
}
} break ;
case GDScriptParser : : SuiteNode : : Local : : VARIABLE : {
if ( local . variable - > initializer & & local . variable - > initializer - > type = = GDScriptParser : : Node : : GET_NODE ) {
get_node = static_cast < GDScriptParser : : GetNodeNode * > ( local . variable - > initializer ) ;
}
} break ;
default : {
} break ;
2022-12-06 10:29:55 +03:00
}
} break ;
default : {
} break ;
}
} break ;
default : {
} break ;
}
2022-11-14 23:25:52 +03:00
2022-12-06 10:29:55 +03:00
if ( get_node ! = nullptr ) {
const Object * node = p_context . base - > call ( " get_node_or_null " , NodePath ( get_node - > full_path ) ) ;
if ( node ! = nullptr ) {
2024-05-22 18:54:43 +02:00
GDScriptParser : : DataType assigned_type = _type_from_variant ( node , p_context ) . type ;
GDScriptParser : : DataType base_type = p_subscript - > base - > datatype ;
if ( p_subscript - > base - > type = = GDScriptParser : : Node : : IDENTIFIER & & base_type . type_source = = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT & & ( assigned_type . kind ! = base_type . kind | | assigned_type . script_path ! = base_type . script_path | | assigned_type . native_type ! = base_type . native_type ) ) {
// Annotated type takes precedence.
return false ;
}
2022-12-06 10:29:55 +03:00
if ( r_base ! = nullptr ) {
* r_base = node ;
2022-11-14 23:25:52 +03:00
}
2023-07-12 19:27:21 +02:00
r_base_type . type_source = GDScriptParser : : DataType : : INFERRED ;
2022-12-06 10:29:55 +03:00
r_base_type . builtin_type = Variant : : OBJECT ;
2023-10-04 15:03:53 +03:00
r_base_type . native_type = node - > get_class_name ( ) ;
2023-07-12 19:27:21 +02:00
Ref < Script > scr = node - > get_script ( ) ;
if ( scr . is_null ( ) ) {
r_base_type . kind = GDScriptParser : : DataType : : NATIVE ;
} else {
r_base_type . kind = GDScriptParser : : DataType : : SCRIPT ;
r_base_type . script_type = scr ;
}
2022-12-06 10:29:55 +03:00
return true ;
2022-11-14 23:25:52 +03:00
}
}
return false ;
}
2022-05-13 15:04:37 +02:00
static void _find_call_arguments ( GDScriptParser : : CompletionContext & p_context , const GDScriptParser : : Node * p_call , int p_argidx , HashMap < String , ScriptLanguage : : CodeCompletionOption > & r_result , bool & r_forced , String & r_arghint ) {
2020-07-06 12:24:24 -03:00
if ( p_call - > type = = GDScriptParser : : Node : : PRELOAD ) {
2022-10-18 16:43:37 +02:00
if ( p_argidx = = 0 & & bool ( EDITOR_GET ( " text_editor/completion/complete_file_paths " ) ) ) {
2020-07-06 12:24:24 -03:00
_get_directory_contents ( EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) , r_result ) ;
}
MethodInfo mi ( PropertyInfo ( Variant : : OBJECT , " resource " , PROPERTY_HINT_RESOURCE_TYPE , " Resource " ) , " preload " , PropertyInfo ( Variant : : STRING , " path " ) ) ;
r_arghint = _make_arguments_hint ( mi , p_argidx ) ;
return ;
} else if ( p_call - > type ! = GDScriptParser : : Node : : CALL ) {
return ;
}
Variant base ;
GDScriptParser : : DataType base_type ;
bool _static = false ;
const GDScriptParser : : CallNode * call = static_cast < const GDScriptParser : : CallNode * > ( p_call ) ;
2020-11-30 09:26:32 +01:00
GDScriptParser : : Node : : Type callee_type = call - > get_callee_type ( ) ;
2020-07-06 12:24:24 -03:00
2022-08-10 11:51:53 +03:00
if ( callee_type = = GDScriptParser : : Node : : SUBSCRIPT ) {
2020-07-06 12:24:24 -03:00
const GDScriptParser : : SubscriptNode * subscript = static_cast < const GDScriptParser : : SubscriptNode * > ( call - > callee ) ;
2022-03-10 14:55:54 +03:00
if ( subscript - > base ! = nullptr & & subscript - > base - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
const GDScriptParser : : IdentifierNode * base_identifier = static_cast < const GDScriptParser : : IdentifierNode * > ( subscript - > base ) ;
Variant : : Type method_type = GDScriptParser : : get_builtin_type ( base_identifier - > name ) ;
if ( method_type < Variant : : VARIANT_MAX ) {
Variant v ;
Callable : : CallError err ;
Variant : : construct ( method_type , v , nullptr , 0 , err ) ;
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
return ;
}
List < MethodInfo > methods ;
v . get_method_list ( & methods ) ;
for ( MethodInfo & E : methods ) {
if ( p_argidx > = E . arguments . size ( ) ) {
continue ;
}
if ( E . name = = call - > function_name ) {
r_arghint + = _make_arguments_hint ( E , p_argidx ) ;
return ;
}
}
}
}
2022-12-07 14:41:21 +03:00
if ( subscript - > is_attribute ) {
2022-11-14 23:25:52 +03:00
bool found_type = _get_subscript_type ( p_context , subscript , base_type , & base ) ;
if ( ! found_type ) {
GDScriptCompletionIdentifier ci ;
if ( _guess_expression_type ( p_context , subscript - > base , ci ) ) {
base_type = ci . type ;
base = ci . value ;
} else {
return ;
}
2020-07-06 12:24:24 -03:00
}
_static = base_type . is_meta_type ;
}
2022-08-10 11:51:53 +03:00
} else if ( Variant : : has_utility_function ( call - > function_name ) ) {
MethodInfo info = Variant : : get_utility_function_info ( call - > function_name ) ;
r_arghint = _make_arguments_hint ( info , p_argidx ) ;
return ;
} else if ( GDScriptUtilityFunctions : : function_exists ( call - > function_name ) ) {
MethodInfo info = GDScriptUtilityFunctions : : get_function_info ( call - > function_name ) ;
r_arghint = _make_arguments_hint ( info , p_argidx ) ;
return ;
} else if ( GDScriptParser : : get_builtin_type ( call - > function_name ) < Variant : : VARIANT_MAX ) {
// Complete constructor.
List < MethodInfo > constructors ;
Variant : : get_constructor_list ( GDScriptParser : : get_builtin_type ( call - > function_name ) , & constructors ) ;
int i = 0 ;
for ( const MethodInfo & E : constructors ) {
if ( p_argidx > = E . arguments . size ( ) ) {
continue ;
}
if ( i > 0 ) {
r_arghint + = " \n " ;
}
r_arghint + = _make_arguments_hint ( E , p_argidx ) ;
i + + ;
}
return ;
} else if ( call - > is_super | | callee_type = = GDScriptParser : : Node : : IDENTIFIER ) {
base = p_context . base ;
if ( p_context . current_class ) {
base_type = p_context . current_class - > get_datatype ( ) ;
_static = ! p_context . current_function | | p_context . current_function - > is_static ;
}
2020-07-06 12:24:24 -03:00
} else {
return ;
}
GDScriptCompletionIdentifier ci ;
ci . type = base_type ;
ci . value = base ;
2025-06-22 12:04:09 -07:00
_list_call_arguments ( p_context , ci , call , p_argidx , _static , r_result , r_arghint ) ;
2020-07-06 12:24:24 -03:00
r_forced = r_result . size ( ) > 0 ;
}
2022-03-26 16:48:43 +01:00
: : Error GDScriptLanguage : : complete_code ( const String & p_code , const String & p_path , Object * p_owner , List < ScriptLanguage : : CodeCompletionOption > * r_options , bool & r_forced , String & r_call_hint ) {
2021-08-13 21:24:02 +02:00
const String quote_style = EDITOR_GET ( " text_editor/completion/use_single_quotes " ) ? " ' " : " \" " ;
2020-07-06 12:24:24 -03:00
GDScriptParser parser ;
GDScriptAnalyzer analyzer ( & parser ) ;
parser . parse ( p_code , p_path , true ) ;
analyzer . analyze ( ) ;
r_forced = false ;
2022-05-13 15:04:37 +02:00
HashMap < String , ScriptLanguage : : CodeCompletionOption > options ;
2020-07-06 12:24:24 -03:00
GDScriptParser : : CompletionContext completion_context = parser . get_completion_context ( ) ;
2024-09-12 20:08:30 +02:00
if ( completion_context . current_class ! = nullptr & & completion_context . current_class - > outer = = nullptr ) {
completion_context . base = p_owner ;
}
2020-07-06 12:24:24 -03:00
bool is_function = false ;
switch ( completion_context . type ) {
case GDScriptParser : : COMPLETION_NONE :
break ;
case GDScriptParser : : COMPLETION_ANNOTATION : {
List < MethodInfo > annotations ;
parser . get_annotation_list ( & annotations ) ;
2021-07-15 23:45:57 -04:00
for ( const MethodInfo & E : annotations ) {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( E . name . substr ( 1 ) , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2021-07-15 23:45:57 -04:00
if ( E . arguments . size ( ) > 0 ) {
2020-07-06 12:24:24 -03:00
option . insert_text + = " ( " ;
}
options . insert ( option . display , option ) ;
}
r_forced = true ;
} break ;
case GDScriptParser : : COMPLETION_ANNOTATION_ARGUMENTS : {
if ( completion_context . node = = nullptr | | completion_context . node - > type ! = GDScriptParser : : Node : : ANNOTATION ) {
break ;
}
const GDScriptParser : : AnnotationNode * annotation = static_cast < const GDScriptParser : : AnnotationNode * > ( completion_context . node ) ;
2024-09-07 20:47:34 +02:00
_find_annotation_arguments ( annotation , completion_context . current_argument , quote_style , options , r_call_hint ) ;
2020-07-06 12:24:24 -03:00
r_forced = true ;
} break ;
2022-03-10 14:55:54 +03:00
case GDScriptParser : : COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD : {
// Constants.
{
List < StringName > constants ;
Variant : : get_constants_for_type ( completion_context . builtin_type , & constants ) ;
for ( const StringName & E : constants ) {
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_CONSTANT ) ;
bool valid = false ;
Variant default_value = Variant : : get_constant_value ( completion_context . builtin_type , E , & valid ) ;
if ( valid ) {
option . default_value = default_value ;
}
options . insert ( option . display , option ) ;
}
}
// Methods.
{
List < StringName > methods ;
Variant : : get_builtin_method_list ( completion_context . builtin_type , & methods ) ;
for ( const StringName & E : methods ) {
if ( Variant : : is_builtin_method_static ( completion_context . builtin_type , E ) ) {
ScriptLanguage : : CodeCompletionOption option ( E , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION ) ;
2024-08-31 12:12:19 +02:00
if ( ! _guess_expecting_callable ( completion_context ) ) {
if ( Variant : : get_builtin_method_argument_count ( completion_context . builtin_type , E ) > 0 | | Variant : : is_builtin_method_vararg ( completion_context . builtin_type , E ) ) {
option . insert_text + = " ( " ;
} else {
option . insert_text + = " () " ;
}
2022-03-10 14:55:54 +03:00
}
options . insert ( option . display , option ) ;
}
2020-10-22 23:12:00 +03:00
}
2020-07-06 12:24:24 -03:00
}
} break ;
case GDScriptParser : : COMPLETION_INHERIT_TYPE : {
_list_available_types ( true , completion_context , options ) ;
r_forced = true ;
} break ;
case GDScriptParser : : COMPLETION_TYPE_NAME_OR_VOID : {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( " void " , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2020-07-06 12:24:24 -03:00
options . insert ( option . display , option ) ;
}
[[fallthrough]] ;
case GDScriptParser : : COMPLETION_TYPE_NAME : {
_list_available_types ( false , completion_context , options ) ;
r_forced = true ;
} break ;
case GDScriptParser : : COMPLETION_PROPERTY_DECLARATION_OR_TYPE : {
_list_available_types ( false , completion_context , options ) ;
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption get ( " get " , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2020-07-06 12:24:24 -03:00
options . insert ( get . display , get ) ;
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption set ( " set " , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2020-07-06 12:24:24 -03:00
options . insert ( set . display , set ) ;
r_forced = true ;
} break ;
case GDScriptParser : : COMPLETION_PROPERTY_DECLARATION : {
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption get ( " get " , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2020-07-06 12:24:24 -03:00
options . insert ( get . display , get ) ;
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption set ( " set " , ScriptLanguage : : CODE_COMPLETION_KIND_PLAIN_TEXT ) ;
2020-07-06 12:24:24 -03:00
options . insert ( set . display , set ) ;
r_forced = true ;
} break ;
case GDScriptParser : : COMPLETION_PROPERTY_METHOD : {
if ( ! completion_context . current_class ) {
break ;
}
for ( int i = 0 ; i < completion_context . current_class - > members . size ( ) ; i + + ) {
const GDScriptParser : : ClassNode : : Member & member = completion_context . current_class - > members [ i ] ;
if ( member . type ! = GDScriptParser : : ClassNode : : Member : : FUNCTION ) {
continue ;
}
if ( member . function - > is_static ) {
continue ;
}
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( member . function - > identifier - > name , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION ) ;
2020-07-06 12:24:24 -03:00
options . insert ( option . display , option ) ;
}
r_forced = true ;
} break ;
case GDScriptParser : : COMPLETION_ASSIGN : {
GDScriptCompletionIdentifier type ;
if ( ! completion_context . node | | completion_context . node - > type ! = GDScriptParser : : Node : : ASSIGNMENT ) {
break ;
}
if ( ! _guess_expression_type ( completion_context , static_cast < const GDScriptParser : : AssignmentNode * > ( completion_context . node ) - > assignee , type ) ) {
2024-08-31 12:12:19 +02:00
_find_identifiers ( completion_context , false , true , options , 0 ) ;
2020-07-06 12:24:24 -03:00
r_forced = true ;
break ;
}
2020-12-15 12:04:21 +00:00
if ( ! type . enumeration . is_empty ( ) ) {
2020-07-06 12:24:24 -03:00
_find_enumeration_candidates ( completion_context , type . enumeration , options ) ;
r_forced = options . size ( ) > 0 ;
} else {
2024-08-31 12:12:19 +02:00
_find_identifiers ( completion_context , false , true , options , 0 ) ;
2020-07-06 12:24:24 -03:00
r_forced = true ;
}
} break ;
case GDScriptParser : : COMPLETION_METHOD :
is_function = true ;
[[fallthrough]] ;
case GDScriptParser : : COMPLETION_IDENTIFIER : {
2024-08-31 12:12:19 +02:00
_find_identifiers ( completion_context , is_function , ! _guess_expecting_callable ( completion_context ) , options , 0 ) ;
2020-07-06 12:24:24 -03:00
} break ;
case GDScriptParser : : COMPLETION_ATTRIBUTE_METHOD :
is_function = true ;
[[fallthrough]] ;
case GDScriptParser : : COMPLETION_ATTRIBUTE : {
r_forced = true ;
const GDScriptParser : : SubscriptNode * attr = static_cast < const GDScriptParser : : SubscriptNode * > ( completion_context . node ) ;
if ( attr - > base ) {
GDScriptCompletionIdentifier base ;
2022-11-14 23:25:52 +03:00
bool found_type = _get_subscript_type ( completion_context , attr , base . type ) ;
2022-09-12 18:39:44 +03:00
if ( ! found_type & & ! _guess_expression_type ( completion_context , attr - > base , base ) ) {
2020-07-06 12:24:24 -03:00
break ;
}
2024-08-31 12:12:19 +02:00
_find_identifiers_in_base ( base , is_function , false , ! _guess_expecting_callable ( completion_context ) , options , 0 ) ;
2020-07-06 12:24:24 -03:00
}
} break ;
case GDScriptParser : : COMPLETION_SUBSCRIPT : {
const GDScriptParser : : SubscriptNode * subscript = static_cast < const GDScriptParser : : SubscriptNode * > ( completion_context . node ) ;
GDScriptCompletionIdentifier base ;
2023-07-12 15:59:25 +02:00
const bool res = _guess_expression_type ( completion_context , subscript - > base , base ) ;
// If the type is not known, we assume it is BUILTIN, since indices on arrays is the most common use case.
if ( ! subscript - > is_attribute & & ( ! res | | base . type . kind = = GDScriptParser : : DataType : : BUILTIN | | base . type . is_variant ( ) ) ) {
if ( base . value . get_type ( ) = = Variant : : DICTIONARY ) {
List < PropertyInfo > members ;
base . value . get_property_list ( & members ) ;
2020-07-06 12:24:24 -03:00
2023-07-12 15:59:25 +02:00
for ( const PropertyInfo & E : members ) {
ScriptLanguage : : CodeCompletionOption option ( E . name . quote ( quote_style ) , ScriptLanguage : : CODE_COMPLETION_KIND_MEMBER , ScriptLanguage : : LOCATION_LOCAL ) ;
options . insert ( option . display , option ) ;
}
}
if ( ! subscript - > index | | subscript - > index - > type ! = GDScriptParser : : Node : : LITERAL ) {
2024-08-31 12:12:19 +02:00
_find_identifiers ( completion_context , false , ! _guess_expecting_callable ( completion_context ) , options , 0 ) ;
2023-07-12 15:59:25 +02:00
}
} else if ( res ) {
if ( ! subscript - > is_attribute ) {
// Quote the options if they are not accessed as attribute.
HashMap < String , ScriptLanguage : : CodeCompletionOption > opt ;
2024-08-31 12:12:19 +02:00
_find_identifiers_in_base ( base , false , false , false , opt , 0 ) ;
2023-07-12 15:59:25 +02:00
for ( const KeyValue < String , CodeCompletionOption > & E : opt ) {
ScriptLanguage : : CodeCompletionOption option ( E . value . insert_text . quote ( quote_style ) , E . value . kind , E . value . location ) ;
options . insert ( option . display , option ) ;
}
} else {
2024-08-31 12:12:19 +02:00
_find_identifiers_in_base ( base , false , false , ! _guess_expecting_callable ( completion_context ) , options , 0 ) ;
2023-07-12 15:59:25 +02:00
}
}
2020-07-06 12:24:24 -03:00
} break ;
case GDScriptParser : : COMPLETION_TYPE_ATTRIBUTE : {
if ( ! completion_context . current_class ) {
break ;
}
2024-07-31 23:44:48 +02:00
2020-07-06 12:24:24 -03:00
const GDScriptParser : : TypeNode * type = static_cast < const GDScriptParser : : TypeNode * > ( completion_context . node ) ;
2024-07-31 23:44:48 +02:00
ERR_FAIL_INDEX_V_MSG ( completion_context . type_chain_index - 1 , type - > type_chain . size ( ) , Error : : ERR_BUG , " Could not complete type argument with out of bounds type chain index. " ) ;
2023-11-06 19:08:12 +03:00
2020-07-06 12:24:24 -03:00
GDScriptCompletionIdentifier base ;
2023-11-06 19:08:12 +03:00
2024-07-31 23:44:48 +02:00
if ( _guess_identifier_type ( completion_context , type - > type_chain [ 0 ] , base ) ) {
bool found = true ;
for ( int i = 1 ; i < completion_context . type_chain_index ; i + + ) {
2023-11-06 19:08:12 +03:00
GDScriptCompletionIdentifier ci ;
2024-07-31 23:44:48 +02:00
found = _guess_identifier_type_from_base ( completion_context , base , type - > type_chain [ i ] - > name , ci ) ;
base = ci ;
if ( ! found ) {
2023-11-06 19:08:12 +03:00
break ;
}
2024-07-31 23:44:48 +02:00
}
if ( found ) {
_find_identifiers_in_base ( base , false , true , true , options , 0 ) ;
2020-07-06 12:24:24 -03:00
}
}
r_forced = true ;
} break ;
case GDScriptParser : : COMPLETION_RESOURCE_PATH : {
2022-10-18 16:43:37 +02:00
if ( EDITOR_GET ( " text_editor/completion/complete_file_paths " ) ) {
2020-07-06 12:24:24 -03:00
_get_directory_contents ( EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) , options ) ;
r_forced = true ;
}
} break ;
case GDScriptParser : : COMPLETION_CALL_ARGUMENTS : {
if ( ! completion_context . node ) {
break ;
}
_find_call_arguments ( completion_context , completion_context . node , completion_context . current_argument , options , r_forced , r_call_hint ) ;
} break ;
case GDScriptParser : : COMPLETION_OVERRIDE_METHOD : {
GDScriptParser : : DataType native_type = completion_context . current_class - > base_type ;
2025-05-08 06:22:05 -03:00
GDScriptParser : : FunctionNode * function_node = static_cast < GDScriptParser : : FunctionNode * > ( completion_context . node ) ;
bool is_static = function_node ! = nullptr & & function_node - > is_static ;
2020-07-06 12:24:24 -03:00
while ( native_type . is_set ( ) & & native_type . kind ! = GDScriptParser : : DataType : : NATIVE ) {
switch ( native_type . kind ) {
case GDScriptParser : : DataType : : CLASS : {
2025-05-08 06:22:05 -03:00
for ( const GDScriptParser : : ClassNode : : Member & member : native_type . class_type - > members ) {
if ( member . type ! = GDScriptParser : : ClassNode : : Member : : FUNCTION ) {
continue ;
}
2025-06-22 16:25:36 +02:00
if ( options . has ( member . function - > identifier - > name ) ) {
continue ;
}
if ( completion_context . current_class - > has_function ( member . get_name ( ) ) & & completion_context . current_class - > get_member ( member . get_name ( ) ) . function ! = function_node ) {
2025-05-08 06:22:05 -03:00
continue ;
}
if ( is_static ! = member . function - > is_static ) {
continue ;
}
String display_name = member . function - > identifier - > name ;
display_name + = member . function - > signature + " : " ;
ScriptLanguage : : CodeCompletionOption option ( display_name , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION ) ;
options . insert ( member . function - > identifier - > name , option ) ; // Insert name instead of display to track duplicates.
}
2020-07-06 12:24:24 -03:00
native_type = native_type . class_type - > base_type ;
} break ;
default : {
native_type . kind = GDScriptParser : : DataType : : UNRESOLVED ;
} break ;
}
}
if ( ! native_type . is_set ( ) ) {
break ;
}
2021-08-17 15:06:54 +02:00
StringName class_name = native_type . native_type ;
2020-07-06 12:24:24 -03:00
if ( ! ClassDB : : class_exists ( class_name ) ) {
break ;
}
2025-06-23 21:46:03 +03:00
const bool type_hints = EditorSettings : : get_singleton ( ) - > get_setting ( " text_editor/completion/add_type_hints " ) ;
2020-07-06 12:24:24 -03:00
List < MethodInfo > virtual_methods ;
2025-05-08 06:22:05 -03:00
if ( is_static ) {
2023-04-19 11:10:35 -03:00
// Not truly a virtual method, but can also be "overridden".
MethodInfo static_init ( " _static_init " ) ;
static_init . return_val . type = Variant : : NIL ;
static_init . flags | = METHOD_FLAG_STATIC | METHOD_FLAG_VIRTUAL ;
virtual_methods . push_back ( static_init ) ;
2025-05-08 06:22:05 -03:00
} else {
ClassDB : : get_virtual_methods ( class_name , & virtual_methods ) ;
2023-04-19 11:10:35 -03:00
}
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & mi : virtual_methods ) {
2025-06-22 16:25:36 +02:00
if ( options . has ( mi . name ) ) {
continue ;
}
if ( completion_context . current_class - > has_function ( mi . name ) & & completion_context . current_class - > get_member ( mi . name ) . function ! = function_node ) {
2025-05-08 06:22:05 -03:00
continue ;
}
2020-07-06 12:24:24 -03:00
String method_hint = mi . name ;
2024-12-05 17:56:08 +01:00
if ( method_hint . contains_char ( ' : ' ) ) {
2024-11-16 17:16:07 +01:00
method_hint = method_hint . get_slicec ( ' : ' , 0 ) ;
2020-07-06 12:24:24 -03:00
}
method_hint + = " ( " ;
2024-05-07 12:48:51 +02:00
for ( int64_t i = 0 ; i < mi . arguments . size ( ) ; + + i ) {
if ( i > 0 ) {
2024-04-15 15:18:34 +02:00
method_hint + = " , " ;
}
2024-05-07 12:48:51 +02:00
String arg = mi . arguments [ i ] . name ;
2024-12-05 17:56:08 +01:00
if ( arg . contains_char ( ' : ' ) ) {
2024-11-16 18:52:15 +01:00
arg = arg . substr ( 0 , arg . find_char ( ' : ' ) ) ;
2024-04-15 15:18:34 +02:00
}
method_hint + = arg ;
2025-06-23 21:46:03 +03:00
if ( type_hints ) {
2024-05-07 12:48:51 +02:00
method_hint + = " : " + _get_visual_datatype ( mi . arguments [ i ] , true , class_name ) ;
2020-07-06 12:24:24 -03:00
}
}
2025-03-30 12:59:05 +03:00
if ( mi . flags & METHOD_FLAG_VARARG ) {
if ( ! mi . arguments . is_empty ( ) ) {
method_hint + = " , " ;
}
method_hint + = " ...args " ; // `MethodInfo` does not support the rest parameter name.
2025-06-23 21:46:03 +03:00
if ( type_hints ) {
2025-03-30 12:59:05 +03:00
method_hint + = " : Array " ;
}
}
2020-07-06 12:24:24 -03:00
method_hint + = " ) " ;
2025-06-23 21:46:03 +03:00
if ( type_hints ) {
2023-04-05 17:46:22 +03:00
method_hint + = " -> " + _get_visual_datatype ( mi . return_val , false , class_name ) ;
2020-07-06 12:24:24 -03:00
}
method_hint + = " : " ;
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( method_hint , ScriptLanguage : : CODE_COMPLETION_KIND_FUNCTION ) ;
2020-07-06 12:24:24 -03:00
options . insert ( option . display , option ) ;
}
} break ;
case GDScriptParser : : COMPLETION_GET_NODE : {
2021-10-01 17:06:48 +02:00
// Handles the `$Node/Path` or `$"Some NodePath"` syntax specifically.
2020-07-06 12:24:24 -03:00
if ( p_owner ) {
List < String > opts ;
p_owner - > get_argument_options ( " get_node " , 0 , & opts ) ;
2023-11-26 15:41:29 +01:00
bool for_unique_name = false ;
if ( completion_context . node ! = nullptr & & completion_context . node - > type = = GDScriptParser : : Node : : GET_NODE & & ! static_cast < GDScriptParser : : GetNodeNode * > ( completion_context . node ) - > use_dollar ) {
for_unique_name = true ;
}
2021-07-24 15:46:25 +02:00
for ( const String & E : opts ) {
2021-10-01 17:06:48 +02:00
r_forced = true ;
2021-07-15 23:45:57 -04:00
String opt = E . strip_edges ( ) ;
2020-07-06 12:24:24 -03:00
if ( opt . is_quoted ( ) ) {
2021-10-01 17:06:48 +02:00
// Remove quotes so that we can handle user preferred quote style,
// or handle NodePaths which are valid identifiers and don't need quotes.
opt = opt . unquote ( ) ;
2020-07-06 12:24:24 -03:00
}
2023-11-26 15:41:29 +01:00
if ( for_unique_name ) {
if ( ! opt . begins_with ( " % " ) ) {
continue ;
}
opt = opt . substr ( 1 ) ;
}
2024-09-06 23:41:32 +03:00
// The path needs quotes if at least one of its components (excluding `%` prefix and `/` separations)
2024-02-07 18:50:58 +01:00
// is not a valid identifier.
bool path_needs_quote = false ;
2024-09-06 23:41:32 +03:00
for ( const String & part : opt . trim_prefix ( " % " ) . split ( " / " ) ) {
2024-08-23 14:30:51 +08:00
if ( ! part . is_valid_ascii_identifier ( ) ) {
2024-02-07 18:50:58 +01:00
path_needs_quote = true ;
break ;
}
}
if ( path_needs_quote ) {
2023-01-07 20:23:26 -06:00
// Ignore quote_style and just use double quotes for paths with apostrophes.
// Double quotes don't need to be checked because they're not valid in node and property names.
2024-12-05 17:56:08 +01:00
opt = opt . quote ( opt . contains_char ( ' \' ' ) ? " \" " : quote_style ) ; // Handle user preference.
2021-10-01 17:06:48 +02:00
}
2022-03-26 16:48:43 +01:00
ScriptLanguage : : CodeCompletionOption option ( opt , ScriptLanguage : : CODE_COMPLETION_KIND_NODE_PATH ) ;
2021-10-01 17:06:48 +02:00
options . insert ( option . display , option ) ;
2020-07-06 12:24:24 -03:00
}
2023-11-26 15:41:29 +01:00
if ( ! for_unique_name ) {
// Get autoloads.
for ( const KeyValue < StringName , ProjectSettings : : AutoloadInfo > & E : ProjectSettings : : get_singleton ( ) - > get_autoload_list ( ) ) {
String path = " /root/ " + E . key ;
ScriptLanguage : : CodeCompletionOption option ( path . quote ( quote_style ) , ScriptLanguage : : CODE_COMPLETION_KIND_NODE_PATH ) ;
options . insert ( option . display , option ) ;
}
2020-07-06 12:24:24 -03:00
}
}
} break ;
case GDScriptParser : : COMPLETION_SUPER_METHOD : {
if ( ! completion_context . current_class ) {
break ;
}
2024-08-31 12:12:19 +02:00
_find_identifiers_in_class ( completion_context . current_class , true , false , false , true , ! _guess_expecting_callable ( completion_context ) , options , 0 ) ;
2020-07-06 12:24:24 -03:00
} break ;
}
2022-03-26 16:48:43 +01:00
for ( const KeyValue < String , ScriptLanguage : : CodeCompletionOption > & E : options ) {
2021-08-09 14:13:42 -06:00
r_options - > push_back ( E . value ) ;
2020-07-06 12:24:24 -03:00
}
return OK ;
}
2024-10-20 08:55:27 +03:00
# else // !TOOLS_ENABLED
2020-07-06 12:24:24 -03:00
2022-03-26 16:48:43 +01:00
Error GDScriptLanguage : : complete_code ( const String & p_code , const String & p_path , Object * p_owner , List < ScriptLanguage : : CodeCompletionOption > * r_options , bool & r_forced , String & r_call_hint ) {
2020-05-01 19:14:56 -03:00
return OK ;
2018-06-20 22:41:26 -03:00
}
2014-12-16 22:31:57 -03:00
2024-10-20 08:55:27 +03:00
# endif // TOOLS_ENABLED
2020-07-06 12:24:24 -03:00
2020-05-01 19:14:56 -03:00
//////// END COMPLETION //////////
2018-06-20 22:41:26 -03:00
2020-05-01 19:14:56 -03:00
String GDScriptLanguage : : _get_indentation ( ) const {
# ifdef TOOLS_ENABLED
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2022-03-06 21:39:19 +01:00
bool use_space_indentation = EDITOR_GET ( " text_editor/behavior/indent/type " ) ;
2019-02-08 22:39:08 +01:00
2020-05-01 19:14:56 -03:00
if ( use_space_indentation ) {
2022-03-06 21:39:19 +01:00
int indent_size = EDITOR_GET ( " text_editor/behavior/indent/size " ) ;
2023-01-29 00:52:05 +01:00
return String ( " " ) . repeat ( indent_size ) ;
2018-06-20 22:41:26 -03:00
}
}
2020-05-01 19:14:56 -03:00
# endif
return " \t " ;
2018-06-20 22:41:26 -03:00
}
2017-08-27 19:03:19 -03:00
2020-05-01 19:14:56 -03:00
void GDScriptLanguage : : auto_indent_code ( String & p_code , int p_from_line , int p_to_line ) const {
String indent = _get_indentation ( ) ;
2014-12-08 15:47:45 -03:00
2020-05-01 19:14:56 -03:00
Vector < String > lines = p_code . split ( " \n " ) ;
List < int > indent_stack ;
2014-12-08 15:47:45 -03:00
2020-05-01 19:14:56 -03:00
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
String l = lines [ i ] ;
int tc = 0 ;
for ( int j = 0 ; j < l . length ( ) ; j + + ) {
if ( l [ j ] = = ' ' | | l [ j ] = = ' \t ' ) {
tc + + ;
} else {
2018-06-20 22:41:26 -03:00
break ;
2017-08-25 00:34:32 -03:00
}
}
2025-02-26 11:41:11 +01:00
String st = l . substr ( tc ) . strip_edges ( ) ;
2021-12-09 03:42:46 -06:00
if ( st . is_empty ( ) | | st . begins_with ( " # " ) ) {
2020-05-01 19:14:56 -03:00
continue ; //ignore!
2014-12-08 15:47:45 -03:00
}
2020-05-01 19:14:56 -03:00
int ilevel = 0 ;
if ( indent_stack . size ( ) ) {
ilevel = indent_stack . back ( ) - > get ( ) ;
2014-12-08 15:47:45 -03:00
}
2020-05-01 19:14:56 -03:00
if ( tc > ilevel ) {
indent_stack . push_back ( tc ) ;
} else if ( tc < ilevel ) {
while ( indent_stack . size ( ) & & indent_stack . back ( ) - > get ( ) > tc ) {
indent_stack . pop_back ( ) ;
2018-10-28 01:53:48 +02:00
}
2014-02-09 22:10:30 -03:00
2020-05-01 19:14:56 -03:00
if ( indent_stack . size ( ) & & indent_stack . back ( ) - > get ( ) ! = tc ) {
2021-10-11 11:30:59 +02:00
indent_stack . push_back ( tc ) ; // this is not right but gets the job done
2014-02-09 22:10:30 -03:00
}
}
2015-12-28 16:36:29 -03:00
2020-05-01 19:14:56 -03:00
if ( i > = p_from_line ) {
2023-01-29 00:52:05 +01:00
l = indent . repeat ( indent_stack . size ( ) ) + st ;
2020-05-01 19:14:56 -03:00
} else if ( i > p_to_line ) {
break ;
2018-06-20 22:41:26 -03:00
}
2018-02-11 11:29:30 +02:00
2020-05-01 19:14:56 -03:00
lines . write [ i ] = l ;
2018-06-20 22:41:26 -03:00
}
2018-02-11 11:29:30 +02:00
2020-05-01 19:14:56 -03:00
p_code = " " ;
for ( int i = 0 ; i < lines . size ( ) ; i + + ) {
if ( i > 0 ) {
p_code + = " \n " ;
2018-06-20 22:41:26 -03:00
}
2020-05-01 19:14:56 -03:00
p_code + = lines [ i ] ;
2018-06-20 22:41:26 -03:00
}
2014-12-16 22:31:57 -03:00
}
2014-02-09 22:10:30 -03:00
2020-05-01 19:14:56 -03:00
# ifdef TOOLS_ENABLED
2016-09-12 10:52:29 -03:00
2024-12-14 21:25:05 +03:00
static Error _lookup_symbol_from_base ( const GDScriptParser : : DataType & p_base , const String & p_symbol , GDScriptLanguage : : LookupResult & r_result ) {
2020-07-06 12:24:24 -03:00
GDScriptParser : : DataType base_type = p_base ;
2024-12-14 21:25:05 +03:00
while ( true ) {
2020-07-06 12:24:24 -03:00
switch ( base_type . kind ) {
case GDScriptParser : : DataType : : CLASS : {
2024-12-14 21:25:05 +03:00
ERR_FAIL_NULL_V ( base_type . class_type , ERR_BUG ) ;
String name = p_symbol ;
if ( name = = " new " ) {
name = " _init " ;
}
if ( ! base_type . class_type - > has_member ( name ) ) {
2020-07-06 12:24:24 -03:00
base_type = base_type . class_type - > base_type ;
2024-12-14 21:25:05 +03:00
break ;
}
const GDScriptParser : : ClassNode : : Member & member = base_type . class_type - > get_member ( name ) ;
switch ( member . type ) {
case GDScriptParser : : ClassNode : : Member : : UNDEFINED :
case GDScriptParser : : ClassNode : : Member : : GROUP :
return ERR_BUG ;
case GDScriptParser : : ClassNode : : Member : : CLASS : {
2025-01-04 17:12:55 +03:00
String doc_type_name ;
String doc_enum_name ;
GDScriptDocGen : : doctype_from_gdtype ( GDScriptAnalyzer : : type_from_metatype ( member . get_datatype ( ) ) , doc_type_name , doc_enum_name ) ;
2024-12-14 21:25:05 +03:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS ;
2025-01-04 17:12:55 +03:00
r_result . class_name = doc_type_name ;
2024-12-14 21:25:05 +03:00
} break ;
case GDScriptParser : : ClassNode : : Member : : CONSTANT :
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_CONSTANT ;
break ;
case GDScriptParser : : ClassNode : : Member : : FUNCTION :
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_METHOD ;
break ;
case GDScriptParser : : ClassNode : : Member : : SIGNAL :
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_SIGNAL ;
break ;
case GDScriptParser : : ClassNode : : Member : : VARIABLE :
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_PROPERTY ;
break ;
case GDScriptParser : : ClassNode : : Member : : ENUM :
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_ENUM ;
break ;
case GDScriptParser : : ClassNode : : Member : : ENUM_VALUE :
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_CONSTANT ;
break ;
}
if ( member . type ! = GDScriptParser : : ClassNode : : Member : : CLASS ) {
2025-01-04 17:12:55 +03:00
String doc_type_name ;
String doc_enum_name ;
GDScriptDocGen : : doctype_from_gdtype ( GDScriptAnalyzer : : type_from_metatype ( base_type ) , doc_type_name , doc_enum_name ) ;
2024-12-14 21:25:05 +03:00
2025-01-04 17:12:55 +03:00
r_result . class_name = doc_type_name ;
2024-12-14 21:25:05 +03:00
r_result . class_member = name ;
2020-07-06 12:24:24 -03:00
}
2024-12-14 21:25:05 +03:00
Error err = OK ;
r_result . script = GDScriptCache : : get_shallow_script ( base_type . script_path , err ) ;
r_result . script_path = base_type . script_path ;
r_result . location = member . get_line ( ) ;
return err ;
2020-07-06 12:24:24 -03:00
} break ;
case GDScriptParser : : DataType : : SCRIPT : {
2024-12-14 21:25:05 +03:00
const Ref < Script > scr = base_type . script_type ;
if ( scr . is_null ( ) ) {
return ERR_CANT_RESOLVE ;
}
String name = p_symbol ;
if ( name = = " new " ) {
name = " _init " ;
}
const int line = scr - > get_member_line ( name ) ;
if ( line > = 0 ) {
bool found_type = false ;
r_result . type = ScriptLanguage : : LOOKUP_RESULT_SCRIPT_LOCATION ;
{
List < PropertyInfo > properties ;
scr - > get_script_property_list ( & properties ) ;
for ( const PropertyInfo & property : properties ) {
if ( property . name = = name & & ( property . usage & PROPERTY_USAGE_SCRIPT_VARIABLE ) ) {
found_type = true ;
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_PROPERTY ;
r_result . class_name = scr - > get_doc_class_name ( ) ;
r_result . class_member = name ;
break ;
}
}
2020-07-06 12:24:24 -03:00
}
2024-12-14 21:25:05 +03:00
if ( ! found_type ) {
List < MethodInfo > methods ;
scr - > get_script_method_list ( & methods ) ;
for ( const MethodInfo & method : methods ) {
if ( method . name = = name ) {
found_type = true ;
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_METHOD ;
r_result . class_name = scr - > get_doc_class_name ( ) ;
r_result . class_member = name ;
break ;
}
}
}
if ( ! found_type ) {
List < MethodInfo > signals ;
scr - > get_script_method_list ( & signals ) ;
for ( const MethodInfo & signal : signals ) {
if ( signal . name = = name ) {
found_type = true ;
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_SIGNAL ;
r_result . class_name = scr - > get_doc_class_name ( ) ;
r_result . class_member = name ;
break ;
}
}
2020-07-06 12:24:24 -03:00
}
2024-12-14 21:25:05 +03:00
if ( ! found_type ) {
const Ref < GDScript > gds = scr ;
if ( gds . is_valid ( ) ) {
const Ref < GDScript > * subclass = gds - > get_subclasses ( ) . getptr ( name ) ;
if ( subclass ! = nullptr ) {
found_type = true ;
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS ;
r_result . class_name = subclass - > ptr ( ) - > get_doc_class_name ( ) ;
}
// TODO: enums.
}
}
if ( ! found_type ) {
HashMap < StringName , Variant > constants ;
scr - > get_constants ( & constants ) ;
if ( constants . has ( name ) ) {
found_type = true ;
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_CONSTANT ;
r_result . class_name = scr - > get_doc_class_name ( ) ;
r_result . class_member = name ;
}
}
r_result . script = scr ;
r_result . script_path = base_type . script_path ;
r_result . location = line ;
return OK ;
}
const Ref < Script > base_script = scr - > get_base_script ( ) ;
if ( base_script . is_valid ( ) ) {
base_type . script_type = base_script ;
2020-07-06 12:24:24 -03:00
} else {
2024-12-14 21:25:05 +03:00
base_type . kind = GDScriptParser : : DataType : : NATIVE ;
base_type . builtin_type = Variant : : OBJECT ;
base_type . native_type = scr - > get_instance_base_type ( ) ;
2020-07-06 12:24:24 -03:00
}
} break ;
case GDScriptParser : : DataType : : NATIVE : {
2024-12-14 21:25:05 +03:00
const StringName & class_name = base_type . native_type ;
ERR_FAIL_COND_V ( ! ClassDB : : class_exists ( class_name ) , ERR_BUG ) ;
2020-07-06 12:24:24 -03:00
if ( ClassDB : : has_method ( class_name , p_symbol , true ) ) {
2022-03-26 16:48:43 +01:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_METHOD ;
2024-12-14 21:25:05 +03:00
r_result . class_name = class_name ;
2020-07-06 12:24:24 -03:00
r_result . class_member = p_symbol ;
return OK ;
}
List < MethodInfo > virtual_methods ;
ClassDB : : get_virtual_methods ( class_name , & virtual_methods , true ) ;
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & E : virtual_methods ) {
2021-07-15 23:45:57 -04:00
if ( E . name = = p_symbol ) {
2022-03-26 16:48:43 +01:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_METHOD ;
2024-12-14 21:25:05 +03:00
r_result . class_name = class_name ;
2020-07-06 12:24:24 -03:00
r_result . class_member = p_symbol ;
return OK ;
}
}
2022-05-12 16:07:05 +03:00
if ( ClassDB : : has_signal ( class_name , p_symbol , true ) ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_SIGNAL ;
2024-12-14 21:25:05 +03:00
r_result . class_name = class_name ;
2022-05-12 16:07:05 +03:00
r_result . class_member = p_symbol ;
return OK ;
}
2024-06-24 21:53:39 +03:00
List < StringName > enums ;
ClassDB : : get_enum_list ( class_name , & enums ) ;
for ( const StringName & E : enums ) {
if ( E = = p_symbol ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_ENUM ;
2024-12-14 21:25:05 +03:00
r_result . class_name = class_name ;
2024-06-24 21:53:39 +03:00
r_result . class_member = p_symbol ;
return OK ;
}
}
if ( ! String ( ClassDB : : get_integer_constant_enum ( class_name , p_symbol , true ) ) . is_empty ( ) ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_CONSTANT ;
2024-12-14 21:25:05 +03:00
r_result . class_name = class_name ;
2024-06-24 21:53:39 +03:00
r_result . class_member = p_symbol ;
2020-07-06 12:24:24 -03:00
return OK ;
}
List < String > constants ;
ClassDB : : get_integer_constant_list ( class_name , & constants , true ) ;
2021-07-24 15:46:25 +02:00
for ( const String & E : constants ) {
2021-07-15 23:45:57 -04:00
if ( E = = p_symbol ) {
2022-03-26 16:48:43 +01:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_CONSTANT ;
2024-12-14 21:25:05 +03:00
r_result . class_name = class_name ;
2020-07-06 12:24:24 -03:00
r_result . class_member = p_symbol ;
return OK ;
}
}
if ( ClassDB : : has_property ( class_name , p_symbol , true ) ) {
2024-01-26 20:42:20 +01:00
PropertyInfo prop_info ;
ClassDB : : get_property_info ( class_name , p_symbol , & prop_info , true ) ;
if ( prop_info . usage & PROPERTY_USAGE_INTERNAL ) {
return ERR_CANT_RESOLVE ;
}
2022-03-26 16:48:43 +01:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_PROPERTY ;
2024-12-14 21:25:05 +03:00
r_result . class_name = class_name ;
2020-07-06 12:24:24 -03:00
r_result . class_member = p_symbol ;
return OK ;
}
2024-12-14 21:25:05 +03:00
const StringName parent_class = ClassDB : : get_parent_class ( class_name ) ;
if ( parent_class ! = StringName ( ) ) {
base_type . native_type = parent_class ;
2020-07-06 12:24:24 -03:00
} else {
2024-12-14 21:25:05 +03:00
return ERR_CANT_RESOLVE ;
2020-07-06 12:24:24 -03:00
}
} break ;
case GDScriptParser : : DataType : : BUILTIN : {
2024-12-14 21:25:05 +03:00
if ( base_type . is_meta_type ) {
if ( Variant : : has_enum ( base_type . builtin_type , p_symbol ) ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_ENUM ;
r_result . class_name = Variant : : get_type_name ( base_type . builtin_type ) ;
r_result . class_member = p_symbol ;
return OK ;
}
2020-07-06 12:24:24 -03:00
2024-12-14 21:25:05 +03:00
if ( Variant : : has_constant ( base_type . builtin_type , p_symbol ) ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_CONSTANT ;
r_result . class_name = Variant : : get_type_name ( base_type . builtin_type ) ;
r_result . class_member = p_symbol ;
return OK ;
}
2020-07-06 12:24:24 -03:00
} else {
2024-12-14 21:25:05 +03:00
if ( Variant : : has_member ( base_type . builtin_type , p_symbol ) ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_PROPERTY ;
r_result . class_name = Variant : : get_type_name ( base_type . builtin_type ) ;
r_result . class_member = p_symbol ;
return OK ;
2020-07-06 12:24:24 -03:00
}
}
2024-12-14 21:25:05 +03:00
if ( Variant : : has_builtin_method ( base_type . builtin_type , p_symbol ) ) {
2022-03-26 16:48:43 +01:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_METHOD ;
2020-07-06 12:24:24 -03:00
r_result . class_name = Variant : : get_type_name ( base_type . builtin_type ) ;
r_result . class_member = p_symbol ;
return OK ;
}
2024-12-14 21:25:05 +03:00
return ERR_CANT_RESOLVE ;
2020-07-06 12:24:24 -03:00
} break ;
2024-06-24 21:53:39 +03:00
case GDScriptParser : : DataType : : ENUM : {
2024-12-14 21:25:05 +03:00
if ( base_type . is_meta_type ) {
if ( base_type . enum_values . has ( p_symbol ) ) {
2025-01-04 17:12:55 +03:00
String doc_type_name ;
String doc_enum_name ;
GDScriptDocGen : : doctype_from_gdtype ( GDScriptAnalyzer : : type_from_metatype ( base_type ) , doc_type_name , doc_enum_name ) ;
2024-12-14 21:25:05 +03:00
2025-01-04 17:12:55 +03:00
if ( CoreConstants : : is_global_enum ( doc_enum_name ) ) {
2024-12-14 21:25:05 +03:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_CONSTANT ;
r_result . class_name = " @GlobalScope " ;
r_result . class_member = p_symbol ;
return OK ;
} else {
2025-01-04 17:12:55 +03:00
const int dot_pos = doc_enum_name . rfind_char ( ' . ' ) ;
2024-12-14 21:25:05 +03:00
if ( dot_pos > = 0 ) {
2025-02-04 10:25:19 +01:00
Error err = OK ;
2024-12-14 21:25:05 +03:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_CONSTANT ;
2025-01-04 17:12:55 +03:00
if ( base_type . class_type ! = nullptr ) {
2025-02-04 10:25:19 +01:00
// For script enums the value isn't accessible as class constant so we need the full enum name.
r_result . class_name = doc_enum_name ;
r_result . class_member = p_symbol ;
r_result . script = GDScriptCache : : get_shallow_script ( base_type . script_path , err ) ;
r_result . script_path = base_type . script_path ;
2025-01-04 17:12:55 +03:00
const String enum_name = doc_enum_name . substr ( dot_pos + 1 ) ;
if ( base_type . class_type - > has_member ( enum_name ) ) {
const GDScriptParser : : ClassNode : : Member member = base_type . class_type - > get_member ( enum_name ) ;
if ( member . type = = GDScriptParser : : ClassNode : : Member : : ENUM ) {
for ( const GDScriptParser : : EnumNode : : Value & value : member . m_enum - > values ) {
if ( value . identifier - > name = = p_symbol ) {
r_result . location = value . line ;
break ;
}
}
}
}
2025-02-04 10:25:19 +01:00
} else if ( base_type . script_type . is_valid ( ) ) {
// For script enums the value isn't accessible as class constant so we need the full enum name.
r_result . class_name = doc_enum_name ;
r_result . class_member = p_symbol ;
r_result . script = base_type . script_type ;
r_result . script_path = base_type . script_path ;
// TODO: Find a way to obtain enum value location for a script
r_result . location = base_type . script_type - > get_member_line ( doc_enum_name . substr ( dot_pos + 1 ) ) ;
} else {
r_result . class_name = doc_enum_name . left ( dot_pos ) ;
r_result . class_member = p_symbol ;
2025-01-04 17:12:55 +03:00
}
2025-02-04 10:25:19 +01:00
return err ;
2024-12-14 21:25:05 +03:00
}
2024-08-06 17:56:09 +03:00
}
2024-12-14 21:25:05 +03:00
} else if ( Variant : : has_builtin_method ( Variant : : DICTIONARY , p_symbol ) ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_METHOD ;
r_result . class_name = " Dictionary " ;
r_result . class_member = p_symbol ;
return OK ;
2024-08-06 17:56:09 +03:00
}
2024-06-24 21:53:39 +03:00
}
2024-12-14 21:25:05 +03:00
return ERR_CANT_RESOLVE ;
2024-06-24 21:53:39 +03:00
} break ;
2024-12-14 21:25:05 +03:00
case GDScriptParser : : DataType : : VARIANT : {
if ( base_type . is_meta_type ) {
const String enum_name = " Variant. " + p_symbol ;
if ( CoreConstants : : is_global_enum ( enum_name ) ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_ENUM ;
r_result . class_name = " @GlobalScope " ;
r_result . class_member = enum_name ;
return OK ;
}
}
return ERR_CANT_RESOLVE ;
} break ;
case GDScriptParser : : DataType : : RESOLVING :
case GDScriptParser : : DataType : : UNRESOLVED : {
return ERR_CANT_RESOLVE ;
2020-07-06 12:24:24 -03:00
} break ;
}
}
return ERR_CANT_RESOLVE ;
}
2021-05-18 13:09:19 +10:00
: : Error GDScriptLanguage : : lookup_code ( const String & p_code , const String & p_symbol , const String & p_path , Object * p_owner , LookupResult & r_result ) {
2022-05-25 01:22:19 +01:00
// Before parsing, try the usual stuff.
2020-07-06 12:24:24 -03:00
if ( ClassDB : : class_exists ( p_symbol ) ) {
2022-03-26 16:48:43 +01:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS ;
2020-07-06 12:24:24 -03:00
r_result . class_name = p_symbol ;
return OK ;
}
2024-11-27 18:15:49 +03:00
if ( Variant : : get_type_by_name ( p_symbol ) < Variant : : VARIANT_MAX ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS ;
r_result . class_name = p_symbol ;
return OK ;
2020-07-06 12:24:24 -03:00
}
2024-12-14 21:25:05 +03:00
if ( p_symbol = = " Variant " ) {
2023-09-29 11:20:22 +03:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS ;
r_result . class_name = " Variant " ;
return OK ;
}
2024-12-14 21:25:05 +03:00
if ( p_symbol = = " PI " | | p_symbol = = " TAU " | | p_symbol = = " INF " | | p_symbol = = " NAN " ) {
2022-03-26 16:48:43 +01:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_CONSTANT ;
2020-07-06 12:24:24 -03:00
r_result . class_name = " @GDScript " ;
r_result . class_member = p_symbol ;
return OK ;
}
GDScriptParser parser ;
parser . parse ( p_code , p_path , true ) ;
GDScriptParser : : CompletionContext context = parser . get_completion_context ( ) ;
2022-12-07 14:41:21 +03:00
context . base = p_owner ;
2020-07-06 12:24:24 -03:00
2022-11-28 15:36:51 +03:00
// Allows class functions with the names like built-ins to be handled properly.
if ( context . type ! = GDScriptParser : : COMPLETION_ATTRIBUTE ) {
2024-12-14 21:25:05 +03:00
// Need special checks for `assert` and `preload` as they are technically
// keywords, so are not registered in `GDScriptUtilityFunctions`.
if ( GDScriptUtilityFunctions : : function_exists ( p_symbol ) | | p_symbol = = " assert " | | p_symbol = = " preload " ) {
2022-11-28 15:36:51 +03:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_METHOD ;
r_result . class_name = " @GDScript " ;
r_result . class_member = p_symbol ;
return OK ;
}
}
GDScriptAnalyzer analyzer ( & parser ) ;
analyzer . analyze ( ) ;
2020-07-06 12:24:24 -03:00
if ( context . current_class & & context . current_class - > extends . size ( ) > 0 ) {
2023-11-06 19:08:12 +03:00
StringName class_name = context . current_class - > extends [ 0 ] - > name ;
2020-07-06 12:24:24 -03:00
bool success = false ;
2023-11-06 19:08:12 +03:00
ClassDB : : get_integer_constant ( class_name , p_symbol , & success ) ;
2020-07-06 12:24:24 -03:00
if ( success ) {
2022-03-26 16:48:43 +01:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_CONSTANT ;
2023-11-06 19:08:12 +03:00
r_result . class_name = class_name ;
2020-07-06 12:24:24 -03:00
r_result . class_member = p_symbol ;
return OK ;
}
2023-11-06 19:08:12 +03:00
do {
List < StringName > enums ;
ClassDB : : get_enum_list ( class_name , & enums , true ) ;
for ( const StringName & enum_name : enums ) {
if ( enum_name = = p_symbol ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_ENUM ;
r_result . class_name = class_name ;
r_result . class_member = p_symbol ;
return OK ;
}
}
class_name = ClassDB : : get_parent_class_nocheck ( class_name ) ;
} while ( class_name ! = StringName ( ) ) ;
}
const GDScriptParser : : TypeNode * type_node = dynamic_cast < const GDScriptParser : : TypeNode * > ( context . node ) ;
if ( type_node ! = nullptr & & ! type_node - > type_chain . is_empty ( ) ) {
StringName class_name = type_node - > type_chain [ 0 ] - > name ;
if ( ScriptServer : : is_global_class ( class_name ) ) {
class_name = ScriptServer : : get_global_class_native_base ( class_name ) ;
}
do {
List < StringName > enums ;
ClassDB : : get_enum_list ( class_name , & enums , true ) ;
for ( const StringName & enum_name : enums ) {
if ( enum_name = = p_symbol ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_ENUM ;
r_result . class_name = class_name ;
r_result . class_member = p_symbol ;
return OK ;
}
}
class_name = ClassDB : : get_parent_class_nocheck ( class_name ) ;
} while ( class_name ! = StringName ( ) ) ;
2020-07-06 12:24:24 -03:00
}
bool is_function = false ;
switch ( context . type ) {
2022-03-10 14:55:54 +03:00
case GDScriptParser : : COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD : {
GDScriptParser : : DataType base_type ;
base_type . kind = GDScriptParser : : DataType : : BUILTIN ;
base_type . builtin_type = context . builtin_type ;
2024-12-14 21:25:05 +03:00
base_type . is_meta_type = true ;
if ( _lookup_symbol_from_base ( base_type , p_symbol , r_result ) = = OK ) {
2022-03-10 14:55:54 +03:00
return OK ;
}
2020-07-06 12:24:24 -03:00
} break ;
case GDScriptParser : : COMPLETION_SUPER_METHOD :
2024-12-14 21:25:05 +03:00
case GDScriptParser : : COMPLETION_METHOD :
2022-05-25 11:41:36 +01:00
case GDScriptParser : : COMPLETION_ASSIGN :
2021-04-10 15:10:29 -04:00
case GDScriptParser : : COMPLETION_CALL_ARGUMENTS :
2022-11-05 21:39:45 -05:00
case GDScriptParser : : COMPLETION_IDENTIFIER :
2023-12-27 17:46:17 +01:00
case GDScriptParser : : COMPLETION_PROPERTY_METHOD :
case GDScriptParser : : COMPLETION_SUBSCRIPT : {
2020-07-06 12:24:24 -03:00
GDScriptParser : : DataType base_type ;
if ( context . current_class ) {
if ( context . type ! = GDScriptParser : : COMPLETION_SUPER_METHOD ) {
base_type = context . current_class - > get_datatype ( ) ;
} else {
base_type = context . current_class - > base_type ;
}
} else {
break ;
}
if ( ! is_function & & context . current_suite ) {
// Lookup local variables.
const GDScriptParser : : SuiteNode * suite = context . current_suite ;
while ( suite ) {
if ( suite - > has_local ( p_symbol ) ) {
2024-12-14 21:25:05 +03:00
const GDScriptParser : : SuiteNode : : Local & local = suite - > get_local ( p_symbol ) ;
switch ( local . type ) {
case GDScriptParser : : SuiteNode : : Local : : UNDEFINED :
return ERR_BUG ;
case GDScriptParser : : SuiteNode : : Local : : CONSTANT :
r_result . type = ScriptLanguage : : LOOKUP_RESULT_LOCAL_CONSTANT ;
r_result . description = local . constant - > doc_data . description ;
r_result . is_deprecated = local . constant - > doc_data . is_deprecated ;
r_result . deprecated_message = local . constant - > doc_data . deprecated_message ;
r_result . is_experimental = local . constant - > doc_data . is_experimental ;
r_result . experimental_message = local . constant - > doc_data . experimental_message ;
if ( local . constant - > initializer ! = nullptr ) {
r_result . value = GDScriptDocGen : : docvalue_from_expression ( local . constant - > initializer ) ;
}
break ;
case GDScriptParser : : SuiteNode : : Local : : VARIABLE :
r_result . type = ScriptLanguage : : LOOKUP_RESULT_LOCAL_VARIABLE ;
r_result . description = local . variable - > doc_data . description ;
r_result . is_deprecated = local . variable - > doc_data . is_deprecated ;
r_result . deprecated_message = local . variable - > doc_data . deprecated_message ;
r_result . is_experimental = local . variable - > doc_data . is_experimental ;
r_result . experimental_message = local . variable - > doc_data . experimental_message ;
if ( local . variable - > initializer ! = nullptr ) {
r_result . value = GDScriptDocGen : : docvalue_from_expression ( local . variable - > initializer ) ;
}
break ;
case GDScriptParser : : SuiteNode : : Local : : PARAMETER :
case GDScriptParser : : SuiteNode : : Local : : FOR_VARIABLE :
case GDScriptParser : : SuiteNode : : Local : : PATTERN_BIND :
r_result . type = ScriptLanguage : : LOOKUP_RESULT_LOCAL_VARIABLE ;
break ;
}
GDScriptDocGen : : doctype_from_gdtype ( local . get_datatype ( ) , r_result . doc_type , r_result . enumeration ) ;
Error err = OK ;
r_result . script = GDScriptCache : : get_shallow_script ( base_type . script_path , err ) ;
r_result . script_path = base_type . script_path ;
r_result . location = local . start_line ;
return err ;
2020-07-06 12:24:24 -03:00
}
suite = suite - > parent_block ;
}
}
2024-12-14 21:25:05 +03:00
if ( _lookup_symbol_from_base ( base_type , p_symbol , r_result ) = = OK ) {
2020-07-06 12:24:24 -03:00
return OK ;
}
if ( ! is_function ) {
if ( ProjectSettings : : get_singleton ( ) - > has_autoload ( p_symbol ) ) {
2021-06-30 01:29:36 -07:00
const ProjectSettings : : AutoloadInfo & autoload = ProjectSettings : : get_singleton ( ) - > get_autoload ( p_symbol ) ;
if ( autoload . is_singleton ) {
2022-09-29 12:53:28 +03:00
String scr_path = autoload . path ;
if ( ! scr_path . ends_with ( " .gd " ) ) {
2024-12-14 21:25:05 +03:00
// Not a script, try find the script anyway, may have some success.
2022-09-29 12:53:28 +03:00
scr_path = scr_path . get_basename ( ) + " .gd " ;
2020-07-06 12:24:24 -03:00
}
2022-09-29 12:53:28 +03:00
if ( FileAccess : : exists ( scr_path ) ) {
2024-12-14 21:25:05 +03:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS ;
r_result . class_name = p_symbol ;
2022-09-29 12:53:28 +03:00
r_result . script = ResourceLoader : : load ( scr_path ) ;
2024-12-14 21:25:05 +03:00
r_result . script_path = scr_path ;
r_result . location = 0 ;
2020-07-06 12:24:24 -03:00
return OK ;
}
}
}
2024-12-14 21:25:05 +03:00
if ( ScriptServer : : is_global_class ( p_symbol ) ) {
const String scr_path = ScriptServer : : get_global_class_path ( p_symbol ) ;
const Ref < Script > scr = ResourceLoader : : load ( scr_path ) ;
if ( scr . is_null ( ) ) {
return ERR_BUG ;
}
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS ;
r_result . class_name = scr - > get_doc_class_name ( ) ;
r_result . script = scr ;
r_result . script_path = scr_path ;
r_result . location = 0 ;
return OK ;
}
const HashMap < StringName , int > & global_map = GDScriptLanguage : : get_singleton ( ) - > get_global_map ( ) ;
if ( global_map . has ( p_symbol ) ) {
Variant value = GDScriptLanguage : : get_singleton ( ) - > get_global_array ( ) [ global_map [ p_symbol ] ] ;
2020-07-06 12:24:24 -03:00
if ( value . get_type ( ) = = Variant : : OBJECT ) {
2024-12-14 21:25:05 +03:00
const Object * obj = value ;
2020-07-06 12:24:24 -03:00
if ( obj ) {
if ( Object : : cast_to < GDScriptNativeClass > ( obj ) ) {
2022-03-26 16:48:43 +01:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS ;
2020-07-06 12:24:24 -03:00
r_result . class_name = Object : : cast_to < GDScriptNativeClass > ( obj ) - > get_name ( ) ;
} else {
2022-03-26 16:48:43 +01:00
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS ;
2020-07-06 12:24:24 -03:00
r_result . class_name = obj - > get_class ( ) ;
}
return OK ;
}
2021-08-30 21:51:56 +02:00
}
2020-07-06 12:24:24 -03:00
}
2024-12-14 21:25:05 +03:00
if ( CoreConstants : : is_global_enum ( p_symbol ) ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_ENUM ;
r_result . class_name = " @GlobalScope " ;
r_result . class_member = p_symbol ;
return OK ;
}
if ( CoreConstants : : is_global_constant ( p_symbol ) ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_CONSTANT ;
r_result . class_name = " @GlobalScope " ;
r_result . class_member = p_symbol ;
return OK ;
}
if ( Variant : : has_utility_function ( p_symbol ) ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_METHOD ;
r_result . class_name = " @GlobalScope " ;
r_result . class_member = p_symbol ;
return OK ;
}
2020-07-06 12:24:24 -03:00
}
} break ;
2024-12-14 21:25:05 +03:00
case GDScriptParser : : COMPLETION_ATTRIBUTE_METHOD :
2020-07-06 12:24:24 -03:00
case GDScriptParser : : COMPLETION_ATTRIBUTE : {
if ( context . node - > type ! = GDScriptParser : : Node : : SUBSCRIPT ) {
break ;
}
const GDScriptParser : : SubscriptNode * subscript = static_cast < const GDScriptParser : : SubscriptNode * > ( context . node ) ;
if ( ! subscript - > is_attribute ) {
break ;
}
GDScriptCompletionIdentifier base ;
2022-12-07 14:41:21 +03:00
bool found_type = _get_subscript_type ( context , subscript , base . type ) ;
if ( ! found_type & & ! _guess_expression_type ( context , subscript - > base , base ) ) {
2020-07-06 12:24:24 -03:00
break ;
}
2024-12-14 21:25:05 +03:00
if ( _lookup_symbol_from_base ( base . type , p_symbol , r_result ) = = OK ) {
2020-07-06 12:24:24 -03:00
return OK ;
}
} break ;
2022-12-02 00:37:53 -05:00
case GDScriptParser : : COMPLETION_TYPE_ATTRIBUTE : {
if ( context . node = = nullptr | | context . node - > type ! = GDScriptParser : : Node : : TYPE ) {
break ;
}
const GDScriptParser : : TypeNode * type = static_cast < const GDScriptParser : : TypeNode * > ( context . node ) ;
GDScriptParser : : DataType base_type ;
const GDScriptParser : : IdentifierNode * prev = nullptr ;
for ( const GDScriptParser : : IdentifierNode * E : type - > type_chain ) {
if ( E - > name = = p_symbol & & prev ! = nullptr ) {
base_type = prev - > get_datatype ( ) ;
break ;
}
prev = E ;
}
if ( base_type . kind ! = GDScriptParser : : DataType : : CLASS ) {
GDScriptCompletionIdentifier base ;
if ( ! _guess_expression_type ( context , prev , base ) ) {
break ;
}
base_type = base . type ;
}
2024-12-14 21:25:05 +03:00
if ( _lookup_symbol_from_base ( base_type , p_symbol , r_result ) = = OK ) {
2022-12-02 00:37:53 -05:00
return OK ;
}
} break ;
2020-07-06 12:24:24 -03:00
case GDScriptParser : : COMPLETION_OVERRIDE_METHOD : {
GDScriptParser : : DataType base_type = context . current_class - > base_type ;
2024-12-14 21:25:05 +03:00
if ( _lookup_symbol_from_base ( base_type , p_symbol , r_result ) = = OK ) {
2020-07-06 12:24:24 -03:00
return OK ;
}
} break ;
2022-12-02 00:37:53 -05:00
case GDScriptParser : : COMPLETION_PROPERTY_DECLARATION_OR_TYPE :
2020-07-06 12:24:24 -03:00
case GDScriptParser : : COMPLETION_TYPE_NAME_OR_VOID :
case GDScriptParser : : COMPLETION_TYPE_NAME : {
GDScriptParser : : DataType base_type = context . current_class - > get_datatype ( ) ;
2024-12-14 21:25:05 +03:00
if ( _lookup_symbol_from_base ( base_type , p_symbol , r_result ) = = OK ) {
2020-07-06 12:24:24 -03:00
return OK ;
}
} break ;
2022-07-04 18:56:34 +03:00
case GDScriptParser : : COMPLETION_ANNOTATION : {
const String annotation_symbol = " @ " + p_symbol ;
if ( parser . annotation_exists ( annotation_symbol ) ) {
r_result . type = ScriptLanguage : : LOOKUP_RESULT_CLASS_ANNOTATION ;
r_result . class_name = " @GDScript " ;
r_result . class_member = annotation_symbol ;
return OK ;
}
} break ;
2020-07-06 12:24:24 -03:00
default : {
}
}
2016-09-12 10:52:29 -03:00
return ERR_CANT_RESOLVE ;
}
2016-09-12 22:40:46 -03:00
2024-10-20 08:55:27 +03:00
# endif // TOOLS_ENABLED