From 089d7fa171e3a3305991047e82b4043d4f05783f Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Mon, 15 Dec 2014 15:42:58 -0300 Subject: [PATCH 01/24] Small batch of fixes -=-=-=-=-=-=-=-=-=-= -Fixed looping error in AudioStreamResampled -winrt port progress -fixes in material in ambient light --- core/io/http_client.cpp | 2 + core/io/ip.cpp | 2 +- core/io/xml_parser.cpp | 2 + core/math/math_funcs.h | 6 +- core/variant.h | 6 +- drivers/gles2/shaders/material.glsl | 2 +- drivers/mpc/audio_stream_mpc.cpp | 2 +- drivers/theoraplayer/SCsub | 3 + drivers/unix/ip_unix.cpp | 2 +- drivers/windows/dir_access_windows.cpp | 7 +- drivers/windows/file_access_windows.cpp | 1 - .../java/src/com/android/godot/GodotIO.java | 22 +- platform/winrt/SCsub | 2 +- platform/winrt/app.cpp | 222 +++++++++++++++++- platform/winrt/app.h | 14 +- platform/winrt/detect.py | 35 ++- platform/winrt/os_winrt.cpp | 22 +- platform/winrt/os_winrt.h | 6 + scene/resources/audio_stream_resampled.cpp | 30 ++- scene/resources/audio_stream_resampled.h | 4 +- tools/editor/editor_node.cpp | 2 +- 21 files changed, 348 insertions(+), 46 deletions(-) diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 60a200af12c..faead675d48 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -29,6 +29,8 @@ #include "http_client.h" #include "io/stream_peer_ssl.h" +VARIANT_ENUM_CAST(HTTPClient::Status); + Error HTTPClient::connect_url(const String& p_url) { return OK; diff --git a/core/io/ip.cpp b/core/io/ip.cpp index d2a685f6b08..6ef6b311882 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -31,7 +31,7 @@ #include "os/semaphore.h" #include "hash_map.h" - +VARIANT_ENUM_CAST(IP::ResolverStatus); /************* RESOLVER ******************/ diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 5d3e4f61ad0..6306d22368b 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -30,6 +30,8 @@ #include "print_string.h" //#define DEBUG_XML +VARIANT_ENUM_CAST(XMLParser::NodeType); + static bool _equalsn(const CharType* str1, const CharType* str2, int len) { int i; for(i=0; str1[i] && str2[i] && i < len; ++i) diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index c98a088912f..28a84133254 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -136,7 +136,10 @@ public: static int b; -#if defined(_MSC_VER) && _MSC_VER < 1800 +#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0603 // windows 8? + b = (int)((a>0.0f) ? (a + 0.5f):(a -0.5f)); + +#elif defined(_MSC_VER) && _MSC_VER < 1800 __asm fld a __asm fistp b /*#elif defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) @@ -147,6 +150,7 @@ public: "fistpl %0 \n\t" : "=m" (b) : "m" (a));*/ + #else b=lrintf(a); //assuming everything but msvc 2012 or earlier has lrint #endif diff --git a/core/variant.h b/core/variant.h index 9109f4ad086..d6fd963d284 100644 --- a/core/variant.h +++ b/core/variant.h @@ -167,13 +167,17 @@ public: static String get_type_name(Variant::Type p_type); static bool can_convert(Type p_type_from,Type p_type_to); +#pragma runtime_checks( "", off ) + template static Type get_type_for() { GetSimpleType t; Variant v(t.type); - return v.get_type(); + Type r = v.get_type(); + return r; } +#pragma runtime_checks( "", restore ) bool is_ref() const; _FORCE_INLINE_ bool is_num() const { return type==INT || type==REAL; }; diff --git a/drivers/gles2/shaders/material.glsl b/drivers/gles2/shaders/material.glsl index f2d9eaf1e5e..718dd56249e 100644 --- a/drivers/gles2/shaders/material.glsl +++ b/drivers/gles2/shaders/material.glsl @@ -1230,7 +1230,7 @@ LIGHT_SHADER_CODE vec3 ambient = const_light_mult*ambient_light*diffuse.rgb; # if defined(LIGHT_TYPE_OMNI) || defined (LIGHT_TYPE_SPOT) - ambient*=diffuse_interp.a; //attenuation affects ambient too +// ambient*=diffuse_interp.a; //attenuation affects ambient too # endif diff --git a/drivers/mpc/audio_stream_mpc.cpp b/drivers/mpc/audio_stream_mpc.cpp index e1f9aacf5ff..d94f57e6834 100644 --- a/drivers/mpc/audio_stream_mpc.cpp +++ b/drivers/mpc/audio_stream_mpc.cpp @@ -275,7 +275,7 @@ void AudioStreamMPC::stop() { } bool AudioStreamMPC::is_playing() const { - return active; + return active || (get_total() - get_todo() -1 > 0); } void AudioStreamMPC::set_paused(bool p_paused) { diff --git a/drivers/theoraplayer/SCsub b/drivers/theoraplayer/SCsub index d4218debb61..cd8cabcc948 100644 --- a/drivers/theoraplayer/SCsub +++ b/drivers/theoraplayer/SCsub @@ -78,6 +78,9 @@ else: if env["platform"] == "android": env_theora.Append(CPPFLAGS=["-D_ANDROID"]) +if env["platform"] == "winrt": + env_theora.Append(CPPFLAGS=["-D_WINRT"]) + env_theora.Append(CPPPATH=["#drivers/theoraplayer/include/theoraplayer", "#drivers/theoraplayer/src/YUV", "#drivers/theoraplayer/src/YUV/libyuv/include", "#drivers/theoraplayer/src/Theora", "#drivers/theoraplayer/src/AVFoundation"]) objs = [] diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index c2217434571..841160f9411 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -28,7 +28,7 @@ /*************************************************************************/ #include "ip_unix.h" -#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED) +#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED) && !defined(WINRT_ENABLED) #ifdef WINDOWS_ENABLED diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index df6cc6c5e2e..d1e97661056 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -106,6 +106,7 @@ String DirAccessWindows::get_next() { return name; } else { +#ifndef WINRT_ENABLED _cisdir=(p->fu.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); String name=p->f.cFileName; @@ -117,7 +118,8 @@ String DirAccessWindows::get_next() { } return name; - +#endif + return ""; } } @@ -358,6 +360,7 @@ bool DirAccessWindows::dir_exists(String p_dir) { return (fileAttr&FILE_ATTRIBUTE_DIRECTORY); } else { +#ifndef WINRT_ENABLED DWORD fileAttr; fileAttr = GetFileAttributesExA(p_dir.ascii().get_data(), GetFileExInfoStandard, &fileInfo); @@ -366,8 +369,8 @@ bool DirAccessWindows::dir_exists(String p_dir) { return (fileAttr&FILE_ATTRIBUTE_DIRECTORY); +#endif } - return false; } diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 3cd065841f0..a6073cbb297 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -54,7 +54,6 @@ void FileAccessWindows::check_errors() const { Error FileAccessWindows::_open(const String& p_filename, int p_mode_flags) { String filename=fix_path(p_filename); - if (f) close(); diff --git a/platform/android/java/src/com/android/godot/GodotIO.java b/platform/android/java/src/com/android/godot/GodotIO.java index d149916893a..ff0eb5edcc1 100644 --- a/platform/android/java/src/com/android/godot/GodotIO.java +++ b/platform/android/java/src/com/android/godot/GodotIO.java @@ -438,8 +438,26 @@ public class GodotIO { try { Log.v("MyApp", "TRYING TO OPEN URI: " + p_uri); - Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(p_uri)); - activity.startActivity(myIntent); + String path = p_uri; + String type=""; + if (path.startsWith("/")) { + //absolute path to filesystem, prepend file:// + path="file://"+path; + if (p_uri.endsWith(".png") || p_uri.endsWith(".jpg") || p_uri.endsWith(".gif") || p_uri.endsWith(".webp")) { + + type="image/*"; + } + } + + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_VIEW); + if (!type.equals("")) { + intent.setDataAndType(Uri.parse(path), type); + } else { + intent.setData(Uri.parse(path)); + } + + activity.startActivity(intent); return 0; } catch (ActivityNotFoundException e) { diff --git a/platform/winrt/SCsub b/platform/winrt/SCsub index 07e2ba81a66..2ea8cbd0de5 100644 --- a/platform/winrt/SCsub +++ b/platform/winrt/SCsub @@ -8,4 +8,4 @@ files = [ 'os_winrt.cpp', ] -env.Program('#bin/godot_rt', files) +env.Program('#bin/godot', files) diff --git a/platform/winrt/app.cpp b/platform/winrt/app.cpp index e3213d574a4..f7fe91188c9 100644 --- a/platform/winrt/app.cpp +++ b/platform/winrt/app.cpp @@ -5,6 +5,8 @@ #include "app.h" #include "main/main.h" +#include "core/os/dir_access.h" +#include "core/os/file_access.h" using namespace Windows::ApplicationModel::Core; using namespace Windows::ApplicationModel::Activation; @@ -70,8 +72,9 @@ void App::Initialize(CoreApplicationView^ applicationView) } // Called when the CoreWindow object is created (or re-created). -void App::SetWindow(CoreWindow^ window) +void App::SetWindow(CoreWindow^ p_window) { + window = p_window; window->VisibilityChanged += ref new TypedEventHandler(this, &App::OnVisibilityChanged); @@ -89,23 +92,230 @@ void App::SetWindow(CoreWindow^ window) pointerVisualizationSettings->IsBarrelButtonFeedbackEnabled = false; #endif - // The CoreWindow has been created, so EGL can be initialized. + + window->PointerPressed += + ref new TypedEventHandler(this, &App::OnPointerPressed); + + window->PointerMoved += + ref new TypedEventHandler(this, &App::OnPointerMoved); + + window->PointerReleased += + ref new TypedEventHandler(this, &App::OnPointerReleased); + + //window->PointerWheelChanged += + // ref new TypedEventHandler(this, &App::OnPointerWheelChanged); + + + + char* args[] = {"-path", "game", NULL}; + Main::setup("winrt", 2, args, false); + + // The CoreWindow has been created, so EGL can be initialized. ContextEGL* context = memnew(ContextEGL(window)); os->set_gl_context(context); - UpdateWindowSize(Size(window->Bounds.Width, window->Bounds.Height)); + UpdateWindowSize(Size(window->Bounds.Width, window->Bounds.Height)); + + Main::setup2(); } +static int _get_button(Windows::UI::Input::PointerPoint ^pt) { + + using namespace Windows::UI::Input; + +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + return BUTTON_LEFT; +#else + switch (pt->Properties->PointerUpdateKind) + { + case PointerUpdateKind::LeftButtonPressed: + case PointerUpdateKind::LeftButtonReleased: + return BUTTON_LEFT; + + case PointerUpdateKind::RightButtonPressed: + case PointerUpdateKind::RightButtonReleased: + return BUTTON_RIGHT; + + case PointerUpdateKind::MiddleButtonPressed: + case PointerUpdateKind::MiddleButtonReleased: + return BUTTON_MIDDLE; + + case PointerUpdateKind::XButton1Pressed: + case PointerUpdateKind::XButton1Released: + return BUTTON_WHEEL_UP; + + case PointerUpdateKind::XButton2Pressed: + case PointerUpdateKind::XButton2Released: + return BUTTON_WHEEL_DOWN; + + default: + break; + } +#endif + + return 0; +}; + +static bool _is_touch(Windows::UI::Input::PointerPoint ^pointerPoint) { +#if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + return true; +#else + using namespace Windows::Devices::Input; + switch (pointerPoint->PointerDevice->PointerDeviceType) { + case PointerDeviceType::Touch: + case PointerDeviceType::Pen: + return true; + default: + return false; + } +#endif +} + + +static Windows::Foundation::Point _get_pixel_position(CoreWindow^ window, Windows::Foundation::Point rawPosition, OS* os) { + + Windows::Foundation::Point outputPosition; + + // Compute coordinates normalized from 0..1. + // If the coordinates need to be sized to the SDL window, + // we'll do that after. + #if WINAPI_FAMILY != WINAPI_FAMILY_PHONE_APP + outputPosition.X = rawPosition.X / window->Bounds.Width; + outputPosition.Y = rawPosition.Y / window->Bounds.Height; + #else + switch (DisplayProperties::CurrentOrientation) + { + case DisplayOrientations::Portrait: + outputPosition.X = rawPosition.X / window->Bounds.Width; + outputPosition.Y = rawPosition.Y / window->Bounds.Height; + break; + case DisplayOrientations::PortraitFlipped: + outputPosition.X = 1.0f - (rawPosition.X / window->Bounds.Width); + outputPosition.Y = 1.0f - (rawPosition.Y / window->Bounds.Height); + break; + case DisplayOrientations::Landscape: + outputPosition.X = rawPosition.Y / window->Bounds.Height; + outputPosition.Y = 1.0f - (rawPosition.X / window->Bounds.Width); + break; + case DisplayOrientations::LandscapeFlipped: + outputPosition.X = 1.0f - (rawPosition.Y / window->Bounds.Height); + outputPosition.Y = rawPosition.X / window->Bounds.Width; + break; + default: + break; + } + #endif + + OS::VideoMode vm = os->get_video_mode(); + outputPosition.X *= vm.width; + outputPosition.Y *= vm.height; + + return outputPosition; +}; + +static int _get_finger(uint32_t p_touch_id) { + + return p_touch_id % 31; // for now +}; + +void App::pointer_event(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args, bool p_pressed) { + + Windows::UI::Input::PointerPoint ^point = args->CurrentPoint; + Windows::Foundation::Point pos = _get_pixel_position(window, point->Position, os); + int but = _get_button(point); + if (_is_touch(point)) { + + InputEvent event; + event.type = InputEvent::SCREEN_TOUCH; + event.device = 0; + event.screen_touch.pressed = p_pressed; + event.screen_touch.x = pos.X; + event.screen_touch.y = pos.Y; + event.screen_touch.index = _get_finger(point->PointerId); + + last_touch_x[event.screen_touch.index] = pos.X; + last_touch_y[event.screen_touch.index] = pos.Y; + + os->input_event(event); + if (event.screen_touch.index != 0) + return; + + }; // fallthrought of sorts + + InputEvent event; + event.type = InputEvent::MOUSE_BUTTON; + event.device = 0; + event.mouse_button.pressed = p_pressed; + event.mouse_button.button_index = but; + event.mouse_button.x = pos.X; + event.mouse_button.y = pos.Y; + event.mouse_button.global_x = pos.X; + event.mouse_button.global_y = pos.Y; + + last_touch_x[31] = pos.X; + last_touch_y[31] = pos.Y; + + os->input_event(event); +}; + + +void App::OnPointerPressed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args) { + + pointer_event(sender, args, true); +}; + + +void App::OnPointerReleased(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args) { + + pointer_event(sender, args, false); +}; + +void App::OnPointerMoved(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args) { + + Windows::UI::Input::PointerPoint ^point = args->CurrentPoint; + Windows::Foundation::Point pos = _get_pixel_position(window, point->Position, os); + + if (_is_touch(point)) { + + InputEvent event; + event.type = InputEvent::SCREEN_DRAG; + event.device = 0; + event.screen_drag.x = pos.X; + event.screen_drag.y = pos.Y; + event.screen_drag.index = _get_finger(point->PointerId); + event.screen_drag.relative_x = event.screen_drag.x - last_touch_x[event.screen_drag.index]; + event.screen_drag.relative_y = event.screen_drag.y - last_touch_y[event.screen_drag.index]; + + os->input_event(event); + if (event.screen_drag.index != 0) + return; + + }; // fallthrought of sorts + + InputEvent event; + event.type = InputEvent::MOUSE_MOTION; + event.device = 0; + event.mouse_motion.x = pos.X; + event.mouse_motion.y = pos.Y; + event.mouse_motion.global_x = pos.X; + event.mouse_motion.global_y = pos.Y; + event.mouse_motion.relative_x = pos.X - last_touch_x[31]; + event.mouse_motion.relative_y = pos.Y - last_touch_y[31]; + + os->input_event(event); + +}; + + // Initializes scene resources void App::Load(Platform::String^ entryPoint) { - char** args = {NULL}; - Main::setup("winrt", 0, args); + //char* args[] = {"-test", "render", NULL}; + //Main::setup("winrt", 2, args); } // This method is called after the window becomes active. void App::Run() { - if (Main::start()) os->run(); } diff --git a/platform/winrt/app.h b/platform/winrt/app.h index a67b936cdfd..7926465ff85 100644 --- a/platform/winrt/app.h +++ b/platform/winrt/app.h @@ -32,6 +32,12 @@ namespace $ext_safeprojectname$ void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args); void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args); + void pointer_event(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args, bool p_pressed); + void OnPointerPressed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); + void OnPointerReleased(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); + void OnPointerMoved(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); + + void UpdateWindowSize(Windows::Foundation::Size size); void InitializeEGL(Windows::UI::Core::CoreWindow^ window); void CleanupEGL(); @@ -44,8 +50,12 @@ namespace $ext_safeprojectname$ EGLDisplay mEglDisplay; EGLContext mEglContext; EGLSurface mEglSurface; - + + CoreWindow^ window; OSWinrt* os; - }; + + int last_touch_x[32]; // 20 fingers, index 31 reserved for the mouse + int last_touch_y[32]; + }; } diff --git a/platform/winrt/detect.py b/platform/winrt/detect.py index 00913b0ade4..dd9d1877ecf 100644 --- a/platform/winrt/detect.py +++ b/platform/winrt/detect.py @@ -3,6 +3,7 @@ import os import sys +import string def is_active(): @@ -30,11 +31,11 @@ def configure(env): env.Append(CPPPATH=['#platform/winrt', '#platform/winrt/include']) - env['OBJSUFFIX'] = ".rt" + env['OBJSUFFIX'] - env['LIBSUFFIX'] = ".rt" + env['LIBSUFFIX'] + env.Append(LINKFLAGS=['/MANIFEST:NO', '/NXCOMPAT', '/DYNAMICBASE', "kernel32.lib", '/MACHINE:X64', '/WINMD', '/APPCONTAINER', '/MANIFESTUAC:NO', '/ERRORREPORT:PROMPT', '/NOLOGO', '/TLBID:1']) env.Append(LIBPATH=['#platform/winrt/x64/lib']) + if (env["target"]=="release"): env.Append(CCFLAGS=['/O2']) @@ -48,19 +49,24 @@ def configure(env): elif (env["target"]=="debug"): - env.Append(CCFLAGS=['/Zi','/DDEBUG_ENABLED','/DD3D_DEBUG_INFO','/O1']) + env.Append(CCFLAGS=['/Zi','/DDEBUG_ENABLED','/DD3D_DEBUG_INFO']) env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) - env.Append(LINKFLAGS=['/DEBUG']) + env.Append(LINKFLAGS=['/DEBUG', '/D_DEBUG']) elif (env["target"]=="profile"): env.Append(CCFLAGS=['-g','-pg']) env.Append(LINKFLAGS=['-pg']) - env.Append(CCFLAGS=['/Gd','/GR','/nologo', '/EHsc']) - env.Append(CXXFLAGS=['/TP', '/ZW']) - env.Append(CPPFLAGS=['/DMSVC', '/GR', ]) - #env.Append(CCFLAGS=['/I'+os.getenv("WindowsSdkDir")+"/Include"]) + + env.Append(CCFLAGS=string.split('/MP /GS /wd"4453" /wd"28204" /Zc:wchar_t /Gm- /Od /fp:precise /D "_UNICODE" /D "UNICODE" /D "WINAPI_FAMILY=WINAPI_FAMILY_APP" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /EHsc /nologo')) + env.Append(CXXFLAGS=string.split('/ZW')) + env.Append(CCFLAGS=['/AI', os.environ['VCINSTALLDIR']+'\\vcpackages', '/AI', os.environ['WINDOWSSDKDIR']+'\\References\\CommonConfiguration\\Neutral']) + + #env.Append(CCFLAGS=['/Gd','/GR','/nologo', '/EHsc']) + #env.Append(CXXFLAGS=['/TP', '/ZW']) + #env.Append(CPPFLAGS=['/DMSVC', '/GR', ]) + ##env.Append(CCFLAGS=['/I'+os.getenv("WindowsSdkDir")+"/Include"]) env.Append(CCFLAGS=['/DWINRT_ENABLED']) env.Append(CCFLAGS=['/DWINDOWS_ENABLED']) env.Append(CCFLAGS=['/DWINAPI_FAMILY=WINAPI_FAMILY_APP', '/D_WIN32_WINNT=0x0603', '/DNTDDI_VERSION=0x06030000']) @@ -70,8 +76,16 @@ def configure(env): env.Append(CCFLAGS=['/DGLES2_ENABLED']) #env.Append(CCFLAGS=['/DGLES1_ENABLED']) - env.Append(LIBS=['winmm','opengl32','dsound','kernel32','ole32','user32','gdi32', 'IPHLPAPI', 'wsock32', 'shell32','advapi32']) - + + LIBS=[ + #'winmm', + 'libEGL', + 'libGLESv2', + 'libANGLE', + #'kernel32','ole32','user32', 'advapi32' + ] + env.Append(LINKFLAGS=[p+".lib" for p in LIBS]) + import methods env.Append( BUILDERS = { 'GLSL120' : env.Builder(action = methods.build_legacygl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) env.Append( BUILDERS = { 'GLSL' : env.Builder(action = methods.build_glsl_headers, suffix = 'glsl.h',src_suffix = '.glsl') } ) @@ -80,4 +94,3 @@ def configure(env): env['ENV'] = os.environ; - diff --git a/platform/winrt/os_winrt.cpp b/platform/winrt/os_winrt.cpp index 99c4ad968ef..81f3d7adf5a 100644 --- a/platform/winrt/os_winrt.cpp +++ b/platform/winrt/os_winrt.cpp @@ -71,7 +71,7 @@ const char * OSWinrt::get_video_driver_name(int p_driver) const { OS::VideoMode OSWinrt::get_default_video_mode() const { - return VideoMode(800,600,false); + return video_mode; } int OSWinrt::get_audio_driver_count() const { @@ -148,6 +148,16 @@ void OSWinrt::initialize(const VideoMode& p_desired,int p_video_driver,int p_aud outside=true; gl_context->initialize(); + VideoMode vm; + vm.width = gl_context->get_window_width(); + vm.height = gl_context->get_window_height(); + vm.fullscreen = true; + vm.resizable = false; + + set_video_mode(vm); + + gl_context->make_current(); + rasterizer = memnew( RasterizerGLES2 ); visual_server = memnew( VisualServerRaster(rasterizer) ); if (get_render_thread_mode()!=RENDER_THREAD_UNSAFE) { @@ -270,6 +280,11 @@ String OSWinrt::get_clipboard() const { }; +void OSWinrt::input_event(InputEvent &p_event) { + p_event.ID = ++last_id; + input->parse_input_event(p_event); +}; + void OSWinrt::delete_main_loop() { if (main_loop) @@ -392,7 +407,7 @@ void OSWinrt::set_window_title(const String& p_title) { void OSWinrt::set_video_mode(const VideoMode& p_video_mode,int p_screen) { - + video_mode = p_video_mode; } OS::VideoMode OSWinrt::get_video_mode(int p_screen) const { @@ -512,7 +527,7 @@ Error OSWinrt::kill(const ProcessID& p_pid) { Error OSWinrt::set_cwd(const String& p_cwd) { - return OK; + return FAILED; } String OSWinrt::get_executable_path() const { @@ -634,6 +649,7 @@ OSWinrt::OSWinrt() { gl_context = NULL; + AudioDriverManagerSW::add_driver(&audio_driver); } diff --git a/platform/winrt/os_winrt.h b/platform/winrt/os_winrt.h index c309239af1d..13b77f7f010 100644 --- a/platform/winrt/os_winrt.h +++ b/platform/winrt/os_winrt.h @@ -40,6 +40,7 @@ #include "servers/spatial_sound/spatial_sound_server_sw.h" #include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h" #include "servers/physics_2d/physics_2d_server_sw.h" +#include "servers/audio/audio_driver_dummy.h" #include "gl_context_egl.h" @@ -124,6 +125,7 @@ class OSWinrt : public OS { MainLoop *main_loop; + AudioDriverDummy audio_driver; AudioServerSW *audio_server; SampleManagerMallocSW *sample_manager; SpatialSoundServerSW *spatial_sound_server; @@ -231,12 +233,16 @@ public: virtual void make_rendering_thread(); virtual void swap_buffers(); + virtual bool has_touchscreen_ui_hint() const { return true; }; + virtual Error shell_open(String p_uri); void run(); virtual bool get_swap_ok_cancel() { return true; } + void input_event(InputEvent &p_event); + OSWinrt(); ~OSWinrt(); diff --git a/scene/resources/audio_stream_resampled.cpp b/scene/resources/audio_stream_resampled.cpp index 8e694a61107..bc7bffa9d26 100644 --- a/scene/resources/audio_stream_resampled.cpp +++ b/scene/resources/audio_stream_resampled.cpp @@ -38,15 +38,18 @@ int AudioStreamResampled::get_channel_count() const { template -void AudioStreamResampled::_resample(int32_t *p_dest,int p_todo,int32_t p_increment) { +uint32_t AudioStreamResampled::_resample(int32_t *p_dest,int p_todo,int32_t p_increment) { + + uint32_t read=offset&MIX_FRAC_MASK; for (int i=0;i> MIX_FRAC_BITS; uint32_t frac = offset & MIX_FRAC_MASK; #ifndef FAST_AUDIO - ERR_FAIL_COND(pos>=rb_len); + ERR_FAIL_COND_V(pos>=rb_len,0); #endif uint32_t pos_next = (pos+1)&rb_mask; //printf("rb pos %i\n",pos); @@ -151,7 +154,7 @@ void AudioStreamResampled::_resample(int32_t *p_dest,int p_todo,int32_t p_increm } - rb_read_pos=offset>>MIX_FRAC_BITS; + return read>>MIX_FRAC_BITS;//rb_read_pos=offset>>MIX_FRAC_BITS; } @@ -173,10 +176,10 @@ bool AudioStreamResampled::mix(int32_t *p_dest, int p_frames) { } else if (rb_read_pos(p_dest,todo,increment); break; - case 2: _resample<2>(p_dest,todo,increment); break; - case 4: _resample<4>(p_dest,todo,increment); break; - case 6: _resample<6>(p_dest,todo,increment); break; + case 1: read=_resample<1>(p_dest,todo,increment); break; + case 2: read=_resample<2>(p_dest,todo,increment); break; + case 4: read=_resample<4>(p_dest,todo,increment); break; + case 6: read=_resample<6>(p_dest,todo,increment); break; } + if (read>rb_todo) + read=rb_todo; + + rb_read_pos = (rb_read_pos+read)&rb_mask; + + + + } return true; diff --git a/scene/resources/audio_stream_resampled.h b/scene/resources/audio_stream_resampled.h index f1e3629ac7e..a1b95e81d5b 100644 --- a/scene/resources/audio_stream_resampled.h +++ b/scene/resources/audio_stream_resampled.h @@ -57,7 +57,7 @@ class AudioStreamResampled : public AudioStream { template - void _resample(int32_t *p_dest,int p_todo,int32_t p_increment); + uint32_t _resample(int32_t *p_dest,int p_todo,int32_t p_increment); protected: @@ -97,7 +97,7 @@ protected: _FORCE_INLINE_ int16_t *get_write_buffer() { return read_buf; } _FORCE_INLINE_ void write(uint32_t p_frames) { - ERR_FAIL_COND(p_frames > rb_len); + ERR_FAIL_COND(p_frames >= rb_len); switch(channels) { case 1: { diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp index 2927e7c8703..dbc896cbb9e 100644 --- a/tools/editor/editor_node.cpp +++ b/tools/editor/editor_node.cpp @@ -3422,7 +3422,7 @@ EditorNode::EditorNode() { p->add_item("New Scene",FILE_NEW_SCENE); p->add_item("Open Scene..",FILE_OPEN_SCENE,KEY_MASK_CMD+KEY_O); p->add_item("Save Scene",FILE_SAVE_SCENE,KEY_MASK_CMD+KEY_S); - p->add_item("Save Scene As..",FILE_SAVE_AS_SCENE); + p->add_item("Save Scene As..",FILE_SAVE_AS_SCENE,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_S); p->add_separator(); p->add_item("Goto Prev. Scene",FILE_OPEN_PREV,KEY_MASK_SHIFT+KEY_MASK_CMD+KEY_P); p->add_submenu_item("Open Recent","RecentScenes",FILE_OPEN_RECENT); From 7eb5d049c24033e0bbfa3bab91163ddaaeea68be Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Mon, 15 Dec 2014 15:49:18 -0300 Subject: [PATCH 02/24] version bump to stable --- version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.py b/version.py index 1e88d24e546..86601a22f25 100644 --- a/version.py +++ b/version.py @@ -2,6 +2,6 @@ short_name="godot" name="Godot Engine" major=1 minor=0 -status="rc2" +status="stable" From 99cf6c0dc40a5124c21dd60379794baadfe296c2 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Mon, 15 Dec 2014 22:41:50 -0300 Subject: [PATCH 03/24] Update README.md --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index dba980bba9d..3456290f74c 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,6 @@ The editor, language and APIs are feature rich, yet simple to learn, allowing yo Godot has been developed by Juan Linietsky and Ariel Manzur for several years, and was born as an in-house engine, used to publish several work-for-hire titles. Development is sponsored by OKAM Studio (http://www.okamstudio.com). -### Godot is BETA. Collaborate!! - -Having been developed as in-house means that the user experience may still not be ideal for everyone. The features needed to make a great game are there, but we really need your help to fix all the rough edges and improve usability (via feedback and/or code contributions). -We know we are close to having an awesome, open source, game engine with nothing to envy from the best commercial offerings, but we can't do this alone. This is why Godot is now open source, so everyone can help us reach this goal. - ### Documentation Documentation has been moved to the [GitHub Wiki](https://github.com/okamstudio/godot/wiki). From ef02ef28675b02e9e8fb1e09ccc6acb1dd018b95 Mon Sep 17 00:00:00 2001 From: yg2f Date: Tue, 16 Dec 2014 15:17:29 +0100 Subject: [PATCH 04/24] fix demo 3d mousepick test add the missing camera parameter to the _input_event() --- demos/3d/mousepick_test/mousepick.gd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/3d/mousepick_test/mousepick.gd b/demos/3d/mousepick_test/mousepick.gd index cf3d9f1e4e8..c96091b6503 100644 --- a/demos/3d/mousepick_test/mousepick.gd +++ b/demos/3d/mousepick_test/mousepick.gd @@ -9,7 +9,7 @@ var gray_mat = FixedMaterial.new() var selected=false -func _input_event(event,pos,normal,shape): +func _input_event(camera,event,pos,normal,shape): if (event.type==InputEvent.MOUSE_BUTTON and event.pressed): if (not selected): get_node("mesh").set_material_override(gray_mat) From 378fb0bacec858847734452c080afb30eb308d7a Mon Sep 17 00:00:00 2001 From: orbitcowboy Date: Tue, 16 Dec 2014 21:16:48 +0100 Subject: [PATCH 05/24] xml_parser: check array length index before accessing the array. --- core/io/xml_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 6306d22368b..1d69f8e5e78 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -34,7 +34,7 @@ VARIANT_ENUM_CAST(XMLParser::NodeType); static bool _equalsn(const CharType* str1, const CharType* str2, int len) { int i; - for(i=0; str1[i] && str2[i] && i < len; ++i) + for(i=0; i < len && str1[i] && str2[i] ; ++i) if (str1[i] != str2[i]) return false; From bcf27feb980aec593c7cb771984e46113cfad757 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Tue, 16 Dec 2014 22:31:57 -0300 Subject: [PATCH 06/24] New Code Completion -=-=-=-=-=-=-=-=-=- -Massive improvement to code completion -Argument hinting for functions If you manage to out-smart the code-completion in a situation where completion should be possible to guess, let me know. Please enter the commit message for your changes. Lines starting --- core/method_bind.cpp | 4 +- core/object.cpp | 5 + core/object.h | 1 + core/script_language.h | 2 +- core/variant.cpp | 126 ++ core/variant.h | 2 + drivers/unix/os_unix.cpp | 6 + makefile | 30 - modules/gdscript/gd_compiler.cpp | 7 + modules/gdscript/gd_editor.cpp | 1703 ++++++++++++----- modules/gdscript/gd_parser.cpp | 295 ++- modules/gdscript/gd_parser.h | 55 +- modules/gdscript/gd_script.h | 20 +- modules/gdscript/gd_tokenizer.cpp | 6 +- modules/gdscript/gd_tokenizer.h | 1 + platform/windows/os_windows.cpp | 3 +- scene/animation/animation_player.cpp | 13 + scene/animation/animation_player.h | 3 + scene/gui/control.cpp | 96 +- scene/gui/text_edit.cpp | 679 ++++--- scene/gui/text_edit.h | 10 +- scene/main/node.cpp | 20 + scene/main/node.h | 1 + scene/resources/material.cpp | 16 + scene/resources/material.h | 1 + tools/editor/code_editor.cpp | 13 +- tools/editor/code_editor.h | 4 +- .../collision_polygon_editor_plugin.cpp | 2 + tools/editor/plugins/script_editor_plugin.cpp | 48 +- tools/editor/plugins/script_editor_plugin.h | 2 +- 30 files changed, 2245 insertions(+), 929 deletions(-) delete mode 100644 makefile diff --git a/core/method_bind.cpp b/core/method_bind.cpp index 739745f70f6..6ee48825715 100644 --- a/core/method_bind.cpp +++ b/core/method_bind.cpp @@ -87,7 +87,9 @@ Vector MethodBind::get_argument_names() const { void MethodBind::set_default_arguments(const Vector& p_defargs) { - default_arguments=p_defargs; default_argument_count=default_arguments.size(); + default_arguments=p_defargs; + default_argument_count=default_arguments.size(); + } #ifdef DEBUG_METHODS_ENABLED diff --git a/core/object.cpp b/core/object.cpp index 42d570042f4..82144ab4be1 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -1694,6 +1694,11 @@ void ObjectDB::debug_objects(DebugFunc p_func) { } +void Object::get_argument_options(const StringName& p_function,int p_idx,List*r_options) const { + + +} + int ObjectDB::get_object_count() { GLOBAL_LOCK_FUNCTION; diff --git a/core/object.h b/core/object.h index 7ce07869a64..97ca50cb1a9 100644 --- a/core/object.h +++ b/core/object.h @@ -583,6 +583,7 @@ public: virtual void get_translatable_strings(List *p_strings) const; + virtual void get_argument_options(const StringName& p_function,int p_idx,List*r_options) const; StringName XL_MESSAGE(const StringName& p_message) const; //translate message (internationalization) StringName tr(const StringName& p_message) const; //translate message (alternative) diff --git a/core/script_language.h b/core/script_language.h index d62e9849a18..802eff190a6 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -144,7 +144,7 @@ public: virtual bool has_named_classes() const=0; virtual int find_function(const String& p_function,const String& p_code) const=0; virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const=0; - virtual Error complete_keyword(const String& p_code, int p_line, const String& p_base_path, const String& p_keyword, List* r_options) { return ERR_UNAVAILABLE; } + virtual Error complete_code(const String& p_code, const String& p_base_path, Object*p_owner,List* r_options,String& r_call_hint) { return ERR_UNAVAILABLE; } virtual void auto_indent_code(String& p_code,int p_from_line,int p_to_line) const=0; /* DEBUGGER FUNCTIONS */ diff --git a/core/variant.cpp b/core/variant.cpp index fdb14c0c0f5..2f0eca9e911 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -2631,3 +2631,129 @@ Variant Variant::call(const StringName& p_method,VARIANT_ARG_DECLARE) { return ret; } + +String Variant::get_construct_string() const { + + switch( type ) { + + case NIL: return "null"; + case BOOL: return _data._bool ? "true" : "false"; + case INT: return String::num(_data._int); + case REAL: return String::num(_data._real); + case STRING: return "\""+*reinterpret_cast(_data._mem)+"\""; + case VECTOR2: return "Vector2("+operator Vector2()+")"; + case RECT2: return "Rect2("+operator Rect2()+")"; + case MATRIX32: return "Matrix32("+operator Matrix32()+")"; + case VECTOR3: return "Vector3("+operator Vector3()+")"; + case PLANE: return "Plane("+operator Plane()+")"; + //case QUAT: + case _AABB: return "AABB("+operator AABB()+")"; + case QUAT: return "Quat("+operator Quat()+")"; + case MATRIX3: return "Matrix3("+operator Matrix3()+")"; + case TRANSFORM: return "Transform("+operator Transform()+")"; + case NODE_PATH: return "@\""+operator NodePath()+"\""; + case INPUT_EVENT: return "InputEvent()"; + case COLOR: return "Color("+String::num( operator Color().r)+","+String::num( operator Color().g)+","+String::num( operator Color().b)+","+String::num( operator Color().a)+")" ; + case DICTIONARY: { + + const Dictionary &d =*reinterpret_cast(_data._mem); + //const String *K=NULL; + String str="{"; + List keys; + d.get_key_list(&keys); + + Vector<_VariantStrPair> pairs; + + for(List::Element *E=keys.front();E;E=E->next()) { + + _VariantStrPair sp; + sp.key=E->get().get_construct_string(); + sp.value=d[E->get()].get_construct_string(); + pairs.push_back(sp); + } + + pairs.sort(); + + for(int i=0;i0) + str+=", "; + str+="("+pairs[i].key+":"+pairs[i].value+")"; + } + str+="}"; + + return str; + } break; + case VECTOR3_ARRAY: { + + DVector vec = operator DVector(); + String str="["; + for(int i=0;i0) + str+=", "; + str+=Variant( vec[i] ).get_construct_string(); + } + return str+"]"; + } break; + case STRING_ARRAY: { + + DVector vec = operator DVector(); + String str="["; + for(int i=0;i0) + str+=", "; + str=str+=Variant( vec[i] ).get_construct_string(); + } + return str+"]"; + } break; + case INT_ARRAY: { + + DVector vec = operator DVector(); + String str="["; + for(int i=0;i0) + str+=", "; + str=str+itos(vec[i]); + } + return str+"]"; + } break; + case REAL_ARRAY: { + + DVector vec = operator DVector(); + String str="["; + for(int i=0;i0) + str+=", "; + str=str+rtos(vec[i]); + } + return str+"]"; + } break; + case ARRAY: { + + Array arr = operator Array(); + String str="["; + for (int i=0; iget_type()+".new()"; + else + return "null"; + + } break; + default: { + return "["+get_type_name(type)+"]"; + } + } + +} diff --git a/core/variant.h b/core/variant.h index 9109f4ad086..b85c1ea640f 100644 --- a/core/variant.h +++ b/core/variant.h @@ -415,6 +415,8 @@ public: static bool has_numeric_constant(Variant::Type p_type, const StringName& p_value); static int get_numeric_constant_value(Variant::Type p_type, const StringName& p_value); + String get_construct_string() const; + void operator=(const Variant& p_variant); // only this is enough for all the other types Variant(const Variant& p_variant); _FORCE_INLINE_ Variant() { type=NIL; } diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index e6458068ea8..2de975e5d11 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -332,6 +332,12 @@ Error OS_Unix::execute(const String& p_path, const List& p_arguments,boo Error OS_Unix::kill(const ProcessID& p_pid) { int ret = ::kill(p_pid,SIGKILL); + if (!ret) { + //avoid zombie process + int st; + ::waitpid(p_pid,&st,0); + + } return ret?ERR_INVALID_PARAMETER:OK; } diff --git a/makefile b/makefile deleted file mode 100644 index d24bd0cd321..00000000000 --- a/makefile +++ /dev/null @@ -1,30 +0,0 @@ -#*************************************************************************/ -#* This file is part of: */ -#* GODOT ENGINE */ -#* http://www.godotengine.org */ -#*************************************************************************/ -# Simple makefile to give support for external C/C++ IDEs */ -#*************************************************************************/ - -# Default build -all: debug - -# Release Build -release: - scons target="release" bin/godot - -# Profile Build -profile: - scons target="profile" bin/godot - -# Debug Build -debug: - # Debug information (code size gets severely affected): - # g: Default (same as g2) - # g0: no debug info - # g1: minimal info - # g3: maximal info - scons target="debug" CCFLAGS="-g" bin/godot - -clean: - scons -c bin/godot diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index 5595db95ded..d4fe8b626be 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -1168,6 +1168,7 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * codegen.current_line=0; codegen.call_max=0; codegen.debug_stack=ScriptDebugger::get_singleton()!=NULL; + Vector argnames; int stack_level=0; @@ -1175,6 +1176,9 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * for(int i=0;iarguments.size();i++) { int idx = i; codegen.add_stack_identifier(p_func->arguments[i],i); +#ifdef TOOLS_ENABLED + argnames.push_back(p_func->arguments[i]); +#endif } stack_level=p_func->arguments.size(); } @@ -1249,6 +1253,9 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * if (p_func) gdfunc->_static=p_func->_static; +#ifdef TOOLS_ENABLED + gdfunc->arg_names=argnames; +#endif //constants if (codegen.constant_map.size()) { gdfunc->_constant_count=codegen.constant_map.size(); diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index 546fed4e8ae..7bec0ebd912 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -312,171 +312,277 @@ String GDScriptLanguage::make_function(const String& p_class,const String& p_nam } -static void _parse_native_symbols(const StringName& p_native,bool p_static,List* r_options) { - - if (!p_static) { - List methods; - ObjectTypeDB::get_method_list(p_native,&methods); - for(List::Element *E=methods.front();E;E=E->next()) { - if (!E->get().name.begins_with("_")) { - r_options->push_back(E->get().name); - } - } - } - - List constants; - ObjectTypeDB::get_integer_constant_list(p_native,&constants); - - for(List::Element *E=constants.front();E;E=E->next()) { - r_options->push_back(E->get()); - } - -} - - -static bool _parse_script_symbols(const Ref& p_script,bool p_static,List* r_options,List::Element *p_indices); - - -static bool _parse_completion_variant(const Variant& p_var,List* r_options,List::Element *p_indices) { - - if (p_indices) { - - bool ok; - Variant si = p_var.get(p_indices->get(),&ok); - if (!ok) - return false; - return _parse_completion_variant(si,r_options,p_indices->next()); - } else { - - switch(p_var.get_type()) { - - - case Variant::DICTIONARY: { - - Dictionary d=p_var; - List vl; - d.get_key_list(&vl); - for (List::Element *E=vl.front();E;E=E->next()) { - - if (E->get().get_type()==Variant::STRING) - r_options->push_back(E->get()); - } - - - List ml; - p_var.get_method_list(&ml); - for(List::Element *E=ml.front();E;E=E->next()) { - r_options->push_back(E->get().name); - } - - } break; - case Variant::OBJECT: { - - - Object *o=p_var; - if (o) { - print_line("OBJECT: "+o->get_type()); - if (p_var.is_ref() && o->cast_to()) { - - Ref gds = p_var; - _parse_script_symbols(gds,true,r_options,NULL); - } else if (o->is_type("GDNativeClass")){ - - GDNativeClass *gnc = o->cast_to(); - _parse_native_symbols(gnc->get_name(),false,r_options); - } else { - - print_line("REGULAR BLEND"); - _parse_native_symbols(o->get_type(),false,r_options); - } - } - - } break; - default: { - - List pi; - p_var.get_property_list(&pi); - for(List::Element *E=pi.front();E;E=E->next()) { - r_options->push_back(E->get().name); - } - List cl; - - p_var.get_numeric_constants_for_type(p_var.get_type(),&cl); - for(List::Element *E=cl.front();E;E=E->next()) { - r_options->push_back(E->get()); - } - - List ml; - p_var.get_method_list(&ml); - for(List::Element *E=ml.front();E;E=E->next()) { - r_options->push_back(E->get().name); - } - - } break; - } - - return true; - } - - -} struct GDCompletionIdentifier { StringName obj_type; Variant::Type type; + Variant value; //im case there is a value, also return it }; -static GDCompletionIdentifier _guess_identifier_type(const GDParser::ClassNode *p_class,int p_line,const StringName& p_identifier); + +static GDCompletionIdentifier _get_type_from_variant(const Variant& p_variant) { + + GDCompletionIdentifier t; + t.type=p_variant.get_type(); + t.value=p_variant; + if (p_variant.get_type()==Variant::OBJECT) { + Object *obj = p_variant; + if (obj) { + //if (obj->cast_to()) { + // t.obj_type=obj->cast_to()->get_name(); + // t.value=Variant(); + //} else { + t.obj_type=obj->get_type(); + //} + } + } + return t; +} + +static GDCompletionIdentifier _get_type_from_pinfo(const PropertyInfo& p_info) { + + GDCompletionIdentifier t; + t.type=p_info.type; + if (p_info.hint==PROPERTY_HINT_RESOURCE_TYPE) { + t.obj_type=p_info.hint_string; + } + return t; +} + +struct GDCompletionContext { + + const GDParser::ClassNode *_class; + const GDParser::FunctionNode *function; + const GDParser::BlockNode *block; + Object* base; + String base_path; + +}; -static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_class,const GDParser::Node *p_node,int p_line,GDCompletionIdentifier &r_type) { +static Ref _get_parent_class(GDCompletionContext& context) { + + + + if (context._class->extends_used) { + //do inheritance + String path = context._class->extends_file; + + Ref script; + Ref native; + + if (path!="") { + //path (and optionally subclasses) + + if (path.is_rel_path()) { + + path=context.base_path.plus_file(path); + } + script = ResourceLoader::load(path); + if (script.is_null()) { + return REF(); + } + if (script->is_valid()) { + + return REF(); + } + //print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid)); + + if (context._class->extends_class.size()) { + + for(int i=0;iextends_class.size();i++) { + + String sub = context._class->extends_class[i]; + if (script->get_subclasses().has(sub)) { + + script=script->get_subclasses()[sub]; + } else { + + return REF(); + } + } + } + + if (script.is_valid()) + return script; + + } else { + + if (context._class->extends_class.size()==0) { + ERR_PRINT("BUG"); + return REF(); + } + + String base=context._class->extends_class[0]; + const GDParser::ClassNode *p = context._class->owner; + Ref base_class; +#if 0 + while(p) { + + if (p->subclasses.has(base)) { + + base_class=p->subclasses[base]; + break; + } + p=p->_owner; + } +#endif + if (base_class.is_valid()) { +#if 0 + for(int i=1;iextends_class.size();i++) { + + String subclass=context._class->extends_class[i]; + + if (base_class->subclasses.has(subclass)) { + + base_class=base_class->subclasses[subclass]; + } else { + + print_line("Could not find subclass: "+subclass); + return _get_type_from_class(context); //fail please + } + } + + script=base_class; +#endif + + } else { + + if (context._class->extends_class.size()>1) { + + return REF(); + + + } + //if not found, try engine classes + if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) { + + return REF(); + } + + int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base]; + native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx]; + if (!native.is_valid()) { + + print_line("Global not a class: '"+base+"'"); + + } + return native; + } + + + } + + } + + return Ref(); +} + + +static GDCompletionIdentifier _get_native_class(GDCompletionContext& context) { + + //eeh... + GDCompletionIdentifier id; + id.type=Variant::NIL; + + REF pc = _get_parent_class(context); + if (!pc.is_valid()) { + return id; + } + Ref nc = pc; + Ref s = pc; + + if (s.is_null() && nc.is_null()) { + return id; + } + while(!s.is_null()) { + nc=s->get_native(); + s=s->get_base(); + } + if (nc.is_null()) { + return id; + } + + + + id.type=Variant::OBJECT; + if (context.base) + id.value=context.base; + id.obj_type=nc->get_name(); + return id; +} + +static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type); + + +static bool _guess_expression_type(GDCompletionContext& context,const GDParser::Node* p_node,int p_line,GDCompletionIdentifier &r_type) { if (p_node->type==GDParser::Node::TYPE_CONSTANT) { const GDParser::ConstantNode *cn=static_cast(p_node); - r_type.type=cn->value.get_type(); - if (r_type.type==Variant::OBJECT) { - Object *obj = cn->value; - if (obj) { - r_type.obj_type=obj->get_type(); - } - } + r_type=_get_type_from_variant(cn->value); return true; } else if (p_node->type==GDParser::Node::TYPE_DICTIONARY) { r_type.type=Variant::DICTIONARY; + + + //what the heck, fill it anyway + const GDParser::DictionaryNode *an = static_cast(p_node); + Dictionary d; + for(int i=0;ielements.size();i++) { + GDCompletionIdentifier k; + if (_guess_expression_type(context,an->elements[i].key,p_line,k) && k.value.get_type()!=Variant::NIL) { + GDCompletionIdentifier v; + if (_guess_expression_type(context,an->elements[i].value,p_line,v)) { + d[k.value]=v.value; + } + + } + } + r_type.value=d; return true; } else if (p_node->type==GDParser::Node::TYPE_ARRAY) { r_type.type=Variant::ARRAY; + //what the heck, fill it anyway + const GDParser::ArrayNode *an = static_cast(p_node); + Array arr; + arr.resize(an->elements.size()); + for(int i=0;ielements.size();i++) { + GDCompletionIdentifier ci; + if (_guess_expression_type(context,an->elements[i],p_line,ci)) { + arr[i]=ci.value; + } + } + r_type.value=arr; return true; } else if (p_node->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) { MethodInfo mi = GDFunctions::get_info(static_cast(p_node)->function); - r_type.type=mi.return_val.type; - if (mi.return_val.hint==PROPERTY_HINT_RESOURCE_TYPE) { - r_type.obj_type=mi.return_val.hint_string; - } + r_type=_get_type_from_pinfo(mi.return_val); + return true; } else if (p_node->type==GDParser::Node::TYPE_IDENTIFIER) { - - r_type=_guess_identifier_type(p_class,p_line,static_cast(p_node)->name); - return true; + return _guess_identifier_type(context,p_line-1,static_cast(p_node)->name,r_type); } else if (p_node->type==GDParser::Node::TYPE_SELF) { //eeh... - return false; + + r_type=_get_native_class(context); + return r_type.type!=Variant::NIL; } else if (p_node->type==GDParser::Node::TYPE_OPERATOR) { + + const GDParser::OperatorNode *op = static_cast(p_node); if (op->op==GDParser::OperatorNode::OP_CALL) { - if (op->arguments[0]->type==GDParser::Node::TYPE_TYPE) { const GDParser::TypeNode *tn = static_cast(op->arguments[0]); @@ -486,25 +592,76 @@ static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_cl const GDParser::BuiltInFunctionNode *bin = static_cast(op->arguments[0]); - return _guess_identifier_type_in_expression(p_class,bin,p_line,r_type); + return _guess_expression_type(context,bin,p_line,r_type); } else if (op->arguments.size()>1 && op->arguments[1]->type==GDParser::Node::TYPE_IDENTIFIER) { + GDCompletionIdentifier base; - if (!_guess_identifier_type_in_expression(p_class,op->arguments[0],p_line,base)) + if (!_guess_expression_type(context,op->arguments[0],p_line,base)) return false; - StringName id = static_cast(p_node)->name; + + StringName id = static_cast(op->arguments[1])->name; + if (base.type==Variant::OBJECT) { + if (ObjectTypeDB::has_method(base.obj_type,id)) { + #ifdef TOOLS_ENABLED MethodBind *mb = ObjectTypeDB::get_method(base.obj_type,id); PropertyInfo pi = mb->get_argument_info(-1); + //try calling the function if constant and all args are constant, should not crash.. + Object *baseptr = base.value; + + if (baseptr && mb->is_const() && pi.type==Variant::OBJECT) { + bool all_valid=true; + Vector args; + for(int i=2;iarguments.size();i++) { + GDCompletionIdentifier arg; + + if (_guess_expression_type(context,op->arguments[i],p_line,arg)) { + if (arg.value.get_type()!=Variant::NIL && arg.value.get_type()!=Variant::OBJECT) { // calling with object seems dangerous, i don' t know + args.push_back(arg.value); + } else { + all_valid=false; + break; + } + } else { + all_valid=false; + } + } + if (all_valid) { + Vector argptr; + for(int i=0;icall(baseptr,argptr.ptr(),argptr.size(),ce); + + + if (ce.error==Variant::CallError::CALL_OK && ret.get_type()!=Variant::NIL) { + + if (ret.get_type()!=Variant::OBJECT || ret.operator Object*()!=NULL) { + + r_type=_get_type_from_variant(ret); + return true; + } + } + + } + } + r_type.type=pi.type; if (pi.hint==PROPERTY_HINT_RESOURCE_TYPE) { r_type.obj_type=pi.hint_string; } + + + + return true; #else return false; #endif @@ -534,49 +691,157 @@ static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_cl } + } else if (op->op==GDParser::OperatorNode::OP_INDEX || op->op==GDParser::OperatorNode::OP_INDEX_NAMED) { + + GDCompletionIdentifier p1; + GDCompletionIdentifier p2; + + + + if (op->op==GDParser::OperatorNode::OP_INDEX_NAMED) { + + if (op->arguments[1]->type==GDParser::Node::TYPE_IDENTIFIER) { + String id = static_cast(op->arguments[1])->name; + p2.type=Variant::STRING; + p2.value=id; + } + + } else { + if (op->arguments[1]) { + if (!_guess_expression_type(context,op->arguments[1],p_line,p2)) { + + return false; + } + } + } + + if (op->arguments[0]->type==GDParser::Node::TYPE_ARRAY) { + + const GDParser::ArrayNode *an = static_cast(op->arguments[0]); + if (p2.value.is_num()) { + int index = p2.value; + if (index<0 || index>=an->elements.size()) + return false; + return _guess_expression_type(context,an->elements[index],p_line,r_type); + } + + } else if (op->arguments[0]->type==GDParser::Node::TYPE_DICTIONARY) { + + const GDParser::DictionaryNode *dn = static_cast(op->arguments[0]); + + if (p2.value.get_type()==Variant::NIL) + return false; + + for(int i=0;ielements.size();i++) { + + GDCompletionIdentifier k; + + if (!_guess_expression_type(context,dn->elements[i].key,p_line,k)) { + + return false; + } + + if (k.value.get_type()==Variant::NIL) + return false; + + if (k.value==p2.value) { + + return _guess_expression_type(context,dn->elements[i].value,p_line,r_type); + } + } + + } else { + + if (op->arguments[0]) { + if (!_guess_expression_type(context,op->arguments[0],p_line,p1)) { + + return false; + } + + } + + if (p1.value.get_type()==Variant::OBJECT) { + //?? + } else if (p1.value.get_type()!=Variant::NIL) { + + bool valid; + Variant ret = p1.value.get(p2.value,&valid); + if (valid) { + r_type=_get_type_from_variant(ret); + return true; + } + + } else { + if (p1.type!=Variant::NIL) { + Variant::CallError ce; + Variant base = Variant::construct(p1.type,NULL,0,ce); + bool valid; + Variant ret = base.get(p2.value,&valid); + if (valid) { + r_type=_get_type_from_variant(ret); + return true; + } + } + } + } + } else { + Variant::Operator vop = Variant::OP_MAX; switch(op->op) { - case GDParser::OperatorNode::OP_ASSIGN_ADD: vop=Variant::OP_ADD; break; - case GDParser::OperatorNode::OP_ASSIGN_SUB: vop=Variant::OP_SUBSTRACT; break; - case GDParser::OperatorNode::OP_ASSIGN_MUL: vop=Variant::OP_MULTIPLY; break; - case GDParser::OperatorNode::OP_ASSIGN_DIV: vop=Variant::OP_DIVIDE; break; - case GDParser::OperatorNode::OP_ASSIGN_MOD: vop=Variant::OP_MODULE; break; - case GDParser::OperatorNode::OP_ASSIGN_SHIFT_LEFT: vop=Variant::OP_SHIFT_LEFT; break; - case GDParser::OperatorNode::OP_ASSIGN_SHIFT_RIGHT: vop=Variant::OP_SHIFT_RIGHT; break; - case GDParser::OperatorNode::OP_ASSIGN_BIT_AND: vop=Variant::OP_BIT_AND; break; - case GDParser::OperatorNode::OP_ASSIGN_BIT_OR: vop=Variant::OP_BIT_OR; break; - case GDParser::OperatorNode::OP_ASSIGN_BIT_XOR: vop=Variant::OP_BIT_XOR; break; + case GDParser::OperatorNode::OP_ADD: vop=Variant::OP_ADD; break; + case GDParser::OperatorNode::OP_SUB: vop=Variant::OP_SUBSTRACT; break; + case GDParser::OperatorNode::OP_MUL: vop=Variant::OP_MULTIPLY; break; + case GDParser::OperatorNode::OP_DIV: vop=Variant::OP_DIVIDE; break; + case GDParser::OperatorNode::OP_MOD: vop=Variant::OP_MODULE; break; + case GDParser::OperatorNode::OP_SHIFT_LEFT: vop=Variant::OP_SHIFT_LEFT; break; + case GDParser::OperatorNode::OP_SHIFT_RIGHT: vop=Variant::OP_SHIFT_RIGHT; break; + case GDParser::OperatorNode::OP_BIT_AND: vop=Variant::OP_BIT_AND; break; + case GDParser::OperatorNode::OP_BIT_OR: vop=Variant::OP_BIT_OR; break; + case GDParser::OperatorNode::OP_BIT_XOR: vop=Variant::OP_BIT_XOR; break; default:{} } + + if (vop==Variant::OP_MAX) return false; + + GDCompletionIdentifier p1; GDCompletionIdentifier p2; if (op->arguments[0]) { - if (!_guess_identifier_type_in_expression(p_class,op->arguments[0],p_line,p1)) + if (!_guess_expression_type(context,op->arguments[0],p_line,p1)) { + return false; + } + } if (op->arguments.size()>1) { - if (!_guess_identifier_type_in_expression(p_class,op->arguments[1],p_line,p2)) + if (!_guess_expression_type(context,op->arguments[1],p_line,p2)) { + return false; + } } Variant::CallError ce; - Variant v1 = Variant::construct(p1.type,NULL,0,ce); - Variant v2 = Variant::construct(p2.type,NULL,0,ce); + bool v1_use_value = p1.value.get_type()!=Variant::NIL && p1.value.get_type()!=Variant::OBJECT; + Variant v1 = (v1_use_value)?p1.value:Variant::construct(p1.type,NULL,0,ce); + bool v2_use_value = p2.value.get_type()!=Variant::NIL && p2.value.get_type()!=Variant::OBJECT; + Variant v2 = (v2_use_value)?p2.value:Variant::construct(p2.type,NULL,0,ce); // avoid potential invalid ops if ((vop==Variant::OP_DIVIDE || vop==Variant::OP_MODULE) && v2.get_type()==Variant::INT) { v2=1; + v2_use_value=false; } if (vop==Variant::OP_DIVIDE && v2.get_type()==Variant::REAL) { v2=1.0; + v2_use_value=false; } Variant r; @@ -585,6 +850,9 @@ static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_cl if (!valid) return false; r_type.type=r.get_type(); + if (v1_use_value && v2_use_value) + r_type.value=r; + return true; } @@ -594,46 +862,42 @@ static bool _guess_identifier_type_in_expression(const GDParser::ClassNode *p_cl return false; } -static bool _guess_identifier_type_in_block(const GDParser::ClassNode *p_class,const GDParser::BlockNode *p_block,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) { - - - for(int i=0;isub_blocks.size();i++) { - //parse inner first - if (p_line>=p_block->sub_blocks[i]->line && (p_line<=p_block->sub_blocks[i]->end_line || p_block->sub_blocks[i]->end_line==-1)) { - if (_guess_identifier_type_in_block(p_class,p_block->sub_blocks[i],p_line,p_identifier,r_type)) - return true; - } - } +static bool _guess_identifier_type_in_block(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) { const GDParser::Node *last_assign=NULL; int last_assign_line=-1; - for (int i=0;istatements.size();i++) { + for (int i=0;istatements.size();i++) { - if (p_block->statements[i]->line>p_line) - break; + if (context.block->statements[i]->line>p_line) + continue; - if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) { + if (context.block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) { + + const GDParser::LocalVarNode *lv=static_cast(context.block->statements[i]); - const GDParser::LocalVarNode *lv=static_cast(p_block->statements[i]); if (lv->assign && lv->name==p_identifier) { + last_assign=lv->assign; - last_assign_line=p_block->statements[i]->line; + last_assign_line=context.block->statements[i]->line; } } - if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_OPERATOR) { - const GDParser::OperatorNode *op = static_cast(p_block->statements[i]); + if (context.block->statements[i]->type==GDParser::BlockNode::TYPE_OPERATOR) { + const GDParser::OperatorNode *op = static_cast(context.block->statements[i]); if (op->op==GDParser::OperatorNode::OP_ASSIGN) { if (op->arguments.size() && op->arguments[0]->type==GDParser::Node::TYPE_IDENTIFIER) { + const GDParser::IdentifierNode *id = static_cast(op->arguments[0]); + if (id->name==p_identifier) { + last_assign=op->arguments[1]; - last_assign_line=p_block->statements[i]->line; + last_assign_line=context.block->statements[i]->line; } } } @@ -642,382 +906,830 @@ static bool _guess_identifier_type_in_block(const GDParser::ClassNode *p_class,c //use the last assignment, (then backwards?) if (last_assign) { - return _guess_identifier_type_in_expression(p_class,last_assign,last_assign_line-1,r_type); + + return _guess_expression_type(context,last_assign,last_assign_line,r_type); } + return false; } -static GDCompletionIdentifier _guess_identifier_type(const GDParser::ClassNode *p_class,int p_line,const StringName& p_identifier) { +static bool _guess_identifier_type(GDCompletionContext& context,int p_line,const StringName& p_identifier,GDCompletionIdentifier &r_type) { + + //go to block first - return GDCompletionIdentifier(); + const GDParser::BlockNode *block=context.block; + + while(block) { + + GDCompletionContext c = context; + c.block=block; + + if (_guess_identifier_type_in_block(c,p_line,p_identifier,r_type)) { + return true; + } + + block=block->parent_block; + } + + //TODO guess identifier type of arguments (ONLY if this is a virtual function) + if (context.function && context.function->name!=StringName()) { + + int argindex = -1; + + for(int i=0;iarguments.size();i++) { + + if (context.function->arguments[i]==p_identifier) { + argindex=i; + break; + } + + } + + if (argindex!=-1) { + GDCompletionIdentifier id =_get_native_class(context); + if (id.type==Variant::OBJECT && id.obj_type!=StringName()) { + //this kinda sucks but meh + + List vmethods; + ObjectTypeDB::get_virtual_methods(id.obj_type,&vmethods); + for (List::Element *E=vmethods.front();E;E=E->next()) { + + + if (E->get().name==context.function->name && argindexget().arguments.size()) { + + PropertyInfo arg=E->get().arguments[argindex]; + + int scp = arg.name.find(":"); + if (scp!=-1) { + + + r_type.type=Variant::OBJECT; + r_type.obj_type=arg.name.substr(scp+1,arg.name.length()); + return true; + + } else { + + r_type.type=arg.type; + if (arg.hint==PROPERTY_HINT_RESOURCE_TYPE) + r_type.obj_type=arg.hint_string; + return true; + } + } + } + } + } + } + + //guess type in constant + + for(int i=0;iconstant_expressions.size();i++) { + + if (context._class->constant_expressions[i].identifier==p_identifier) { + + ERR_FAIL_COND_V( context._class->constant_expressions[i].expression->type!=GDParser::Node::TYPE_CONSTANT, false ); + r_type=_get_type_from_variant(static_cast(context._class->constant_expressions[i].expression)->value ); + return true; + } + } + + if (context.function && !context.function->_static) { + + for(int i=0;ivariables.size();i++) { + + if (context._class->variables[i].identifier==p_identifier) { + + if (context._class->variables[i]._export.type!=Variant::NIL) { + + r_type=_get_type_from_pinfo(context._class->variables[i]._export); + return true; + } else if (context._class->variables[i].expression) { + return _guess_expression_type(context,context._class->variables[i].expression,context._class->variables[i].line,r_type); + } + } + } + } + + //globals + + for(Map::Element *E=GDScriptLanguage::get_singleton()->get_global_map().front();E;E=E->next()) { + if (E->key()==p_identifier) { + + r_type=_get_type_from_variant(GDScriptLanguage::get_singleton()->get_global_array()[E->get()]); + return true; + } + + } + return false; } -static void _parse_expression_node(const GDParser::ClassNode *p_class,const GDParser::Node *p_node,int p_line,List* r_options,List::Element *p_indices) { + +static void _find_identifiers_in_block(GDCompletionContext& context,int p_line,bool p_only_functions,Set& result) { + + if (p_only_functions) + return; + + for (int i=0;istatements.size();i++) { + + if (context.block->statements[i]->line>p_line) + continue; + + + if (context.block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) { + + const GDParser::LocalVarNode *lv=static_cast(context.block->statements[i]); + result.insert(lv->name.operator String()); + } + } +} + +static void _find_identifiers_in_class(GDCompletionContext& context,bool p_static,bool p_only_functions,Set& result) { + + if (!p_static && !p_only_functions) { + + for(int i=0;ivariables.size();i++) { + result.insert(context._class->variables[i].identifier); + } + } + if (!p_only_functions) { + + for(int i=0;iconstant_expressions.size();i++) { + result.insert(context._class->constant_expressions[i].identifier); + } + + for(int i=0;isubclasses.size();i++) { + result.insert(context._class->subclasses[i]->name); + } + + } + + for(int i=0;istatic_functions.size();i++) { + if (context._class->static_functions[i]->arguments.size()) + result.insert(context._class->static_functions[i]->name.operator String()+"("); + else + result.insert(context._class->static_functions[i]->name.operator String()+"()"); + } + + if (!p_static) { + + for(int i=0;ifunctions.size();i++) { + if (context._class->functions[i]->arguments.size()) + result.insert(context._class->functions[i]->name.operator String()+"("); + else + result.insert(context._class->functions[i]->name.operator String()+"()"); + } + } + + Ref base = _get_parent_class(context); + + while(true) { + + Ref script = base; + Ref nc = base; + if (script.is_valid()) { + + if (!p_static && !p_only_functions) { + for (const Set::Element *E=script->get_members().front();E;E=E->next()) { + result.insert(E->get().operator String()); + } + } + + if (!p_only_functions) { + for (const Map::Element *E=script->get_constants().front();E;E=E->next()) { + result.insert(E->key().operator String()); + } + } + + for (const Map::Element *E=script->get_member_functions().front();E;E=E->next()) { + if (!p_static || E->get().is_static()) { + if (E->get().get_argument_count()) + result.insert(E->key().operator String()+"("); + else + result.insert(E->key().operator String()+"()"); + } + } + + if (!p_only_functions) { + for (const Map >::Element *E=script->get_subclasses().front();E;E=E->next()) { + result.insert(E->key().operator String()); + } + } + + base=script->get_base(); + if (base.is_null()) + base=script->get_native(); + } else if (nc.is_valid()) { + + if (!p_only_functions) { + + StringName type = nc->get_name(); + List constants; + ObjectTypeDB::get_integer_constant_list(type,&constants); + for(List::Element *E=constants.front();E;E=E->next()) { + result.insert(E->get()); + } + + List methods; + ObjectTypeDB::get_method_list(type,&methods); + for(List::Element *E=methods.front();E;E=E->next()) { + if (E->get().arguments.size()) + result.insert(E->get().name+"("); + else + result.insert(E->get().name+"()"); + } + } + break; + } else + break; + + } + +} + +static void _find_identifiers(GDCompletionContext& context,int p_line,bool p_only_functions,Set& result) { + + const GDParser::BlockNode *block=context.block; + + while(block) { + + GDCompletionContext c = context; + c.block=block; + + _find_identifiers_in_block(c,p_line,p_only_functions,result); + block=block->parent_block; + } + + const GDParser::ClassNode *clss=context._class; + + bool _static=context.function->_static; + + while(clss) { + GDCompletionContext c = context; + c._class=clss; + c.block=NULL; + c.function=NULL; + _find_identifiers_in_class(c,_static,p_only_functions,result); + clss=clss->owner; + } + + for(int i=0;i::Element *E=GDScriptLanguage::get_singleton()->get_global_map().front();E;E=E->next()) { + result.insert(E->key().operator String()); + } +} + + +static String _get_visual_datatype(const PropertyInfo& p_info) { + + String n = p_info.name; + int idx = n.find(":"); + if (idx!=-1) { + return n.substr(idx+1,n.length()); + } + + if (p_info.type==Variant::OBJECT && p_info.hint==PROPERTY_HINT_RESOURCE_TYPE) + return p_info.hint_string; + if (p_info.type==Variant::NIL) + return "void"; + + return Variant::get_type_name(p_info.type); +} + +static void _make_function_hint(const GDParser::FunctionNode* p_func,int p_argidx,String& arghint) { + + arghint="func "+p_func->name+"("; + for (int i=0;iarguments.size();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+=p_func->arguments[i].operator String(); + int deffrom = p_func->arguments.size()-p_func->default_values.size(); + + if (i>=deffrom) { + int defidx = deffrom-i; + + if (defidx>=0 && defidxdefault_values.size()) { + + if (p_func->default_values[defidx]->type==GDParser::Node::TYPE_OPERATOR) { + + const GDParser::OperatorNode *op=static_cast(p_func->default_values[defidx]); + if (op->op==GDParser::OperatorNode::OP_ASSIGN) { + const GDParser::ConstantNode *cn=static_cast(op->arguments[1]); + arghint+="="+cn->value.get_construct_string(); + + } + } else { + + } + } + } + + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + } + if (p_func->arguments.size()>0) + arghint+=" "; + arghint+=")"; +} + + +static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const StringName& p_method,const GDCompletionIdentifier& id, int p_argidx, Set& result, String& arghint) { + + + if (id.type==Variant::OBJECT && id.obj_type!=StringName()) { + + + MethodBind *m = ObjectTypeDB::get_method(id.obj_type,p_method); + if (!m) + return; + + if (p_method.operator String()=="connect") { + + + if (p_argidx==0) { + List sigs; + ObjectTypeDB::get_signal_list(id.obj_type,&sigs); + for (List::Element *E=sigs.front();E;E=E->next()) { + result.insert("\""+E->get().name+"\""); + } + } + /*if (p_argidx==2) { + + ERR_FAIL_COND(p_node->type!=GDParser::Node::TYPE_OPERATOR); + const GDParser::OperatorNode *op=static_cast(p_node); + if (op->arguments.size()>) + + }*/ + } else { + + Object *obj=id.value; + if (obj) { + List options; + obj->get_argument_options(p_method,p_argidx,&options); + for(List::Element *E=options.front();E;E=E->next()) { + + result.insert(E->get()); + } + } + + } + + arghint = _get_visual_datatype(m->get_argument_info(-1))+" "+p_method.operator String()+String("("); + for(int i=0;iget_argument_count();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + String n = m->get_argument_info(i).name; + int dp = n.find(":"); + if (dp!=-1) + n=n.substr(0,dp); + arghint+=_get_visual_datatype(m->get_argument_info(i))+" "+n; + int deffrom = m->get_argument_count()-m->get_default_argument_count(); + + + if (i>=deffrom) { + int defidx = i-deffrom; + + if (defidx>=0 && defidxget_default_argument_count()) { + Variant v= m->get_default_argument(i); + arghint+="="+v.get_construct_string(); + } + } + + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + + } + if (m->get_argument_count()>0) + arghint+=" "; + + + arghint+=")"; + + } +} + + +static void _find_call_arguments(GDCompletionContext& context,const GDParser::Node* p_node, int p_line,int p_argidx, Set& result, String& arghint) { - if (p_node->type==GDParser::Node::TYPE_CONSTANT) { + if (!p_node || p_node->type!=GDParser::Node::TYPE_OPERATOR) { - const GDParser::ConstantNode *cn=static_cast(p_node); - _parse_completion_variant(cn->value,r_options,p_indices?p_indices->next():NULL); - } else if (p_node->type==GDParser::Node::TYPE_DICTIONARY) { + return; + } - const GDParser::DictionaryNode *dn=static_cast(p_node); - for (int i=0;ielements.size();i++) { + const GDParser::OperatorNode *op = static_cast(p_node); - if (dn->elements[i].key->type==GDParser::Node::TYPE_CONSTANT) { + if (op->op!=GDParser::OperatorNode::OP_CALL) { - const GDParser::ConstantNode *cn=static_cast(dn->elements[i].key); - if (cn->value.get_type()==Variant::STRING) { + return; + } - String str=cn->value; - if (p_indices) { + if (op->arguments[0]->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) { + //complete built-in function + const GDParser::BuiltInFunctionNode *fn = static_cast(op->arguments[0]); + MethodInfo mi = GDFunctions::get_info(fn->function); - if (str==p_indices->get()) { - _parse_expression_node(p_class,dn->elements[i].value,p_line,r_options,p_indices->next()); + arghint = _get_visual_datatype(mi.return_val)+" "+GDFunctions::get_func_name(fn->function)+String("("); + for(int i=0;i0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+=_get_visual_datatype(mi.arguments[i])+" "+mi.arguments[i].name; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + + } + if (mi.arguments.size()>0) + arghint+=" "; + arghint+=")"; + + } else if (op->arguments[0]->type==GDParser::Node::TYPE_TYPE) { + //complete built-in function + const GDParser::TypeNode *tn = static_cast(op->arguments[0]); + + List mil; + Variant::get_constructor_list(tn->vtype,&mil); + + for(List::Element *E=mil.front();E;E=E->next()) { + + MethodInfo mi = E->get(); + if (mi.arguments.size()==0) + continue; + if (E->prev()) + arghint+="\n"; + arghint += Variant::get_type_name(tn->vtype)+" "+Variant::get_type_name(tn->vtype)+String("("); + for(int i=0;i0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+=_get_visual_datatype(mi.arguments[i])+" "+mi.arguments[i].name; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + + } + if (mi.arguments.size()>0) + arghint+=" "; + arghint+=")"; + } + + } else if (op->arguments.size()>=2 && op->arguments[1]->type==GDParser::Node::TYPE_IDENTIFIER) { + //make sure identifier exists... + + const GDParser::IdentifierNode *id=static_cast(op->arguments[1]); + + if (op->arguments[0]->type==GDParser::Node::TYPE_SELF) { + //self, look up + + for(int i=0;istatic_functions.size();i++) { + if (context._class->static_functions[i]->name==id->name) { + _make_function_hint(context._class->static_functions[i],p_argidx,arghint); + return; + } + } + + if (context.function && !context.function->_static) { + + for(int i=0;ifunctions.size();i++) { + if (context._class->functions[i]->name==id->name) { + _make_function_hint(context._class->functions[i],p_argidx,arghint); + return; + } + } + } + + Ref base = _get_parent_class(context); + + while(true) { + + Ref script = base; + Ref nc = base; + if (script.is_valid()) { + + + for (const Map::Element *E=script->get_member_functions().front();E;E=E->next()) { + + if (E->key()==id->name) { + + if (context.function && context.function->_static && !E->get().is_static()) + continue; + + + arghint = "func "+id->name.operator String()+String("("); + for(int i=0;iget().get_argument_count();i++) { + if (i>0) + arghint+=", "; + else + arghint+=" "; + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + arghint+=E->get().get_argument_name(i); + int deffrom = E->get().get_argument_count()-E->get().get_default_argument_count(); + if (i>=deffrom) { + int defidx = deffrom-i; + if (defidx>=0 && defidxget().get_default_argument_count()) { + arghint+="="+E->get().get_default_argument(defidx).get_construct_string(); + } + } + if (i==p_argidx) { + arghint+=String::chr(0xFFFF); + } + + } + if (E->get().get_argument_count()>0) + arghint+=" "; + arghint+=")"; return; } - - } else { - r_options->push_back(str); } - } - } - } - } else if (p_node->type==GDParser::Node::TYPE_BUILT_IN_FUNCTION) { - MethodInfo mi = GDFunctions::get_info(static_cast(p_node)->function); - - Variant::CallError ce; - _parse_completion_variant(Variant::construct(mi.return_val.type,NULL,0,ce),r_options,p_indices?p_indices->next():NULL); - } else if (p_node->type==GDParser::Node::TYPE_IDENTIFIER) { - - //GDCompletionIdentifier idt = _guess_identifier_type(p_class,p_line-1,static_cast(p_node)->name); - //Variant::CallError ce; - //_parse_completion_variant(Variant::construct(mi.return_val.type,NULL,0,ce),r_options,p_indices?p_indices->next():NULL); - } -} - -static bool _parse_completion_block(const GDParser::ClassNode *p_class,const GDParser::BlockNode *p_block,int p_line,List* r_options,List::Element *p_indices) { - - print_line("COMPLETION BLOCK "+itos(p_block->line)+" -> "+itos(p_block->end_line)); - - for(int i=0;isub_blocks.size();i++) { - //parse inner first - if (p_line>=p_block->sub_blocks[i]->line && (p_line<=p_block->sub_blocks[i]->end_line || p_block->sub_blocks[i]->end_line==-1)) { - if (_parse_completion_block(p_class,p_block->sub_blocks[i],p_line,r_options,p_indices)) - return true; - } - } - - if (p_indices) { - - //parse indices in expressions :| - - const GDParser::Node *last_assign=NULL; - int last_assign_line=-1; - - for (int i=0;istatements.size();i++) { - - if (p_block->statements[i]->line>p_line) - break; - - - if (p_block->statements[i]->type==GDParser::BlockNode::TYPE_LOCAL_VAR) { - - const GDParser::LocalVarNode *lv=static_cast(p_block->statements[i]); - if (lv->assign && String(lv->name)==p_indices->get()) { - last_assign=lv->assign; - last_assign_line=p_block->statements[i]->line; - } - } - } - - //use the last assignment, (then backwards?) - if (last_assign) { - _parse_expression_node(p_class,last_assign,last_assign_line,r_options,p_indices->next()); - return true; - } - - } else { - //no indices, just add all variables and continue - for(int i=0;ivariables.size();i++) { - //parse variables second - if (p_line>=p_block->variable_lines[i]) { - r_options->push_back(p_block->variables[i]); - } else break; - - } - } - - return false; -} - - -static bool _parse_script_symbols(const Ref& p_script,bool p_static,List* r_options,List::Element *p_indices) { - - //for (Map >::Element ? - - if (!p_static && !p_indices) { - for(const Set::Element *E=p_script->get_members().front();E;E=E->next()) { - - r_options->push_back(E->get()); - } - } - - for (const Map::Element *E=p_script->get_constants().front();E;E=E->next()) { - - if( p_indices) { - if (p_indices->get()==String(E->get())) { - _parse_completion_variant(E->get(),r_options,p_indices->next()); - return true; - } - } else { - r_options->push_back(E->key()); - } - } - - if (!p_indices){ - for (const Map::Element *E=p_script->get_member_functions().front();E;E=E->next()) { - - if (E->get().is_static() || !p_static) - r_options->push_back(E->key()); - } - } - - if (p_script->get_base().is_valid()){ - if (_parse_script_symbols(p_script->get_base(),p_static,r_options,p_indices)) - return true; - } else if (p_script->get_native().is_valid() && !p_indices) { - _parse_native_symbols(p_script->get_native()->get_name(),p_static,r_options); - } - - return false; -} - - -static bool _parse_completion_class(const String& p_base_path,const GDParser::ClassNode *p_class,int p_line,List* r_options,List::Element *p_indices) { - - //checks known classes or built-in types for completion - - if (p_indices && !p_indices->next()) { - //built-in types do not have sub-classes, try these first if no sub-indices exist. - static const char*_type_names[Variant::VARIANT_MAX]={ - "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Trasnform", - "Color","Image","NodePath","RID","Object","InputEvent","Dictionary","Array","RawArray","IntArray","FloatArray","StringArray", - "Vector2Array","Vector3Array","ColorArray"}; - - for(int i=0;iget()==_type_names[i]) { - - List ic; - - Variant::get_numeric_constants_for_type(Variant::Type(i),&ic); - for(List::Element *E=ic.front();E;E=E->next()) { - r_options->push_back(E->get()); - } - return true; - } - } - } - - // check the sub-classes of current class - - for(int i=0;isubclasses.size();i++) { - - if (p_line>=p_class->subclasses[i]->line && (p_line<=p_class->subclasses[i]->end_line || p_class->subclasses[i]->end_line==-1)) { - // if OK in sub-classes, try completing the sub-class - if (_parse_completion_class(p_base_path,p_class->subclasses[i],p_line,r_options,p_indices)) - return true; - } - } - - bool in_static_func=false; - - for(int i=0;ifunctions.size();i++) { - - const GDParser::FunctionNode *fu = p_class->functions[i]; - - if (p_line>=fu->body->line && (p_line<=fu->body->end_line || fu->body->end_line==-1)) { - //if in function, first block stuff from outer to inner - if (_parse_completion_block(p_class,fu->body,p_line,r_options,p_indices)) - return true; - //then function arguments - if (!p_indices) { - for(int j=0;jarguments.size();j++) { - - r_options->push_back(fu->arguments[j]); - } - } - } - - } - - for(int i=0;istatic_functions.size();i++) { - - const GDParser::FunctionNode *fu = p_class->static_functions[i]; - - if (p_line>=fu->body->line && (p_line<=fu->body->end_line || fu->body->end_line==-1)) { - - //if in function, first block stuff from outer to inne - if (_parse_completion_block(p_class,fu->body,p_line,r_options,p_indices)) - return true; - //then function arguments - if (!p_indices) { - for(int j=0;jarguments.size();j++) { - - r_options->push_back(fu->arguments[j]); - } - } - - in_static_func=true; - } - - } - - - //add all local names - if (!p_indices) { - - if (!in_static_func) { - - for(int i=0;ivariables.size();i++) { - - r_options->push_back(p_class->variables[i].identifier); - } - } - - for(int i=0;iconstant_expressions.size();i++) { - - r_options->push_back(p_class->constant_expressions[i].identifier); - } - - if (!in_static_func) { - for(int i=0;ifunctions.size();i++) { - - r_options->push_back(p_class->functions[i]->name); - } - } - - for(int i=0;istatic_functions.size();i++) { - - r_options->push_back(p_class->static_functions[i]->name); - } - } - - - if (p_class->extends_used) { - //do inheritance - String path = p_class->extends_file; - - Ref script; - Ref native; - - if (path!="") { - //path (and optionally subclasses) - - script = ResourceLoader::load(path); - if (script.is_null()) { - return false; - } - - if (p_class->extends_class.size()) { - - for(int i=0;iextends_class.size();i++) { - - String sub = p_class->extends_class[i]; - if (script->get_subclasses().has(sub)) { - - script=script->get_subclasses()[sub]; - } else { - - return false; + base=script->get_base(); + if (base.is_null()) + base=script->get_native(); + } else if (nc.is_valid()) { + + if (context.function && !context.function->_static) { + + GDCompletionIdentifier ci; + ci.type=Variant::OBJECT; + ci.obj_type=nc->get_name(); + if (!context._class->owner) + ci.value=context.base; + + _find_type_arguments(p_node,p_line,id->name,ci,p_argidx,result,arghint); + //guess type.. + /* + List methods; + ObjectTypeDB::get_method_list(type,&methods); + for(List::Element *E=methods.front();E;E=E->next()) { + //if (E->get().arguments.size()) + // result.insert(E->get().name+"("); + //else + // result.insert(E->get().name+"()"); + }*/ } - } - } - - } else { - - ERR_FAIL_COND_V(p_class->extends_class.size()==0,false); - //look around for the subclasses - - String base=p_class->extends_class[0]; - Ref base_class; -#if 0 - while(p) { - - if (p->subclasses.has(base)) { - - base_class=p->subclasses[base]; break; - } - p=p->_owner; + } else + break; + } - - if (base_class.is_valid()) { - - for(int i=1;iextends_class.size();i++) { - - String subclass=p_class->extends_class[i]; - - if (base_class->subclasses.has(subclass)) { - - base_class=base_class->subclasses[subclass]; - } else { - - _set_error("Could not find subclass: "+subclass,p_class); - return ERR_FILE_NOT_FOUND; - } - } + } else { - } else { -#endif - if (p_class->extends_class.size()>1) { + GDCompletionIdentifier ci; + if (_guess_expression_type(context,op->arguments[0],p_line,ci)) { - return false; - - } - //if not found, try engine classes - if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) { - return false; - } - - int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base]; - native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx]; - if (!native.is_valid()) { - return false; - } -#if 0 + _find_type_arguments(p_node,p_line,id->name,ci,p_argidx,result,arghint); + return; } -#endif } - if (script.is_valid()) { - if (_parse_script_symbols(script,in_static_func,r_options,p_indices)) - return true; + } +#if 0 + bool _static=context.function->_static; - } else if (native.is_valid() && !p_indices) { - _parse_native_symbols(native->get_name(),in_static_func,r_options); + + + for(int i=0;istatic_functions.size();i++) { + if (context._class->static_functions[i]->arguments.size()) + result.insert(context._class->static_functions[i]->name.operator String()+"("); + else + result.insert(context._class->static_functions[i]->name.operator String()+"()"); + } + + if (!p_static) { + + for(int i=0;ifunctions.size();i++) { + if (context._class->functions[i]->arguments.size()) + result.insert(context._class->functions[i]->name.operator String()+"("); + else + result.insert(context._class->functions[i]->name.operator String()+"()"); } } - return false; + Ref base = _get_parent_class(context); + + while(true) { + + Ref script = base; + Ref nc = base; + if (script.is_valid()) { + + if (!p_static && !p_only_functions) { + for (const Set::Element *E=script->get_members().front();E;E=E->next()) { + result.insert(E->get().operator String()); + } + } + + if (!p_only_functions) { + for (const Map::Element *E=script->get_constants().front();E;E=E->next()) { + result.insert(E->key().operator String()); + } + } + + for (const Map::Element *E=script->get_member_functions().front();E;E=E->next()) { + if (!p_static || E->get().is_static()) { + if (E->get().get_argument_count()) + result.insert(E->key().operator String()+"("); + else + result.insert(E->key().operator String()+"()"); + } + } + + if (!p_only_functions) { + for (const Map >::Element *E=script->get_subclasses().front();E;E=E->next()) { + result.insert(E->key().operator String()); + } + } + + base=script->get_base(); + if (base.is_null()) + base=script->get_native(); + } else if (nc.is_valid()) { + + if (!p_only_functions) { + + StringName type = nc->get_name(); + List constants; + ObjectTypeDB::get_integer_constant_list(type,&constants); + for(List::Element *E=constants.front();E;E=E->next()) { + result.insert(E->get()); + } + + List methods; + ObjectTypeDB::get_method_list(type,&methods); + for(List::Element *E=methods.front();E;E=E->next()) { + if (E->get().arguments.size()) + result.insert(E->get().name+"("); + else + result.insert(E->get().name+"()"); + } + } + break; + } else + break; + + } + + for(int i=0;i* r_options, String &r_call_hint) { +/* bugs: + a[0]. does not work + functions should end in ( + when completing virtuals, ask for full back -Error GDScriptLanguage::complete_keyword(const String& p_code, int p_line, const String& p_base_path, const String& p_base, List* r_options) { - - + */ + //print_line( p_code.replace(String::chr(0xFFFF),"")); GDParser p; Error err = p.parse(p_code,p_base_path); + bool isfunction=false; + Set options; + + GDCompletionContext context; + context._class=p.get_completion_class(); + context.block=p.get_completion_block(); + context.function=p.get_completion_function(); + context.base=p_owner; + context.base_path=p_base_path; + + switch(p.get_completion_type()) { + + case GDParser::COMPLETION_NONE: { + print_line("No completion"); + } break; + case GDParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: { + print_line("Built in type constant"); + List constants; + Variant::get_numeric_constants_for_type(p.get_completion_built_in_constant(),&constants); + for(List::Element *E=constants.front();E;E=E->next()) { + options.insert(E->get().operator String()); + } + + + } break; + case GDParser::COMPLETION_FUNCTION: + isfunction=true; + case GDParser::COMPLETION_IDENTIFIER: { + + _find_identifiers(context,p.get_completion_line(),isfunction,options); + } break; + case GDParser::COMPLETION_PARENT_FUNCTION: { + print_line("parent function"); + + } break; + case GDParser::COMPLETION_METHOD: + isfunction=true; + case GDParser::COMPLETION_INDEX: { + + print_line("index"); + const GDParser::Node *node = p.get_completion_node(); + if (node->type!=GDParser::Node::TYPE_OPERATOR) + break; + + + + + GDCompletionIdentifier t; + if (_guess_expression_type(context,static_cast(node)->arguments[0],p.get_completion_line(),t)) { + + if (t.type==Variant::OBJECT && t.obj_type!=StringName()) { + if (!isfunction) { + ObjectTypeDB::get_integer_constant_list(t.obj_type,r_options); + } + List mi; + ObjectTypeDB::get_method_list(t.obj_type,&mi); + for (List::Element *E=mi.front();E;E=E->next()) { + + if (E->get().arguments.size()) + options.insert(E->get().name+"("); + else + options.insert(E->get().name+"()"); + + } + } else { + if (t.value.get_type()==Variant::NIL) { + Variant::CallError ce; + t.value=Variant::construct(t.type,NULL,0,ce); + } + + if (!isfunction) { + List pl; + t.value.get_property_list(&pl); + for (List::Element *E=pl.front();E;E=E->next()) { + + if (E->get().name.find("/")==-1) + options.insert(E->get().name); + } + } + + List mi; + t.value.get_method_list(&mi); + for (List::Element *E=mi.front();E;E=E->next()) { + if (E->get().arguments.size()) + options.insert(E->get().name+"("); + else + options.insert(E->get().name+"()"); + + } + } + } + + + + } break; + case GDParser::COMPLETION_CALL_ARGUMENTS: { + + _find_call_arguments(context,p.get_completion_node(),p.get_completion_line(),p.get_completion_argument_index(),options,r_call_hint); + } break; + + + } + + + for(Set::Element *E=options.front();E;E=E->next()) { + r_options->push_back(E->get()); + } +#if 0 // don't care much about error I guess const GDParser::Node* root = p.get_parse_tree(); ERR_FAIL_COND_V(root->type!=GDParser::Node::TYPE_CLASS,ERR_INVALID_DATA); @@ -1050,10 +1762,11 @@ Error GDScriptLanguage::complete_keyword(const String& p_code, int p_line, const r_options->push_back(E->key()); } } - +#endif return OK; } + void GDScriptLanguage::auto_indent_code(String& p_code,int p_from_line,int p_to_line) const { diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index 904b6ba52fd..1c46a2f4fa0 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -30,19 +30,6 @@ #include "print_string.h" #include "io/resource_loader.h" #include "os/file_access.h" -/* TODO: - - *Property reduce constant expressions - *Implement missing operators in variant? - *constructor - */ - -/* - todo: - fix post ++,-- - make sure ++,-- don't work on constant expressions - seems passing parent node as param is not needed - */ template T* GDParser::alloc_node() { @@ -116,14 +103,20 @@ bool GDParser::_enter_indent_block(BlockNode* p_block) { } } -bool GDParser::_parse_arguments(Node* p_parent,Vector& p_args,bool p_static) { +bool GDParser::_parse_arguments(Node* p_parent,Vector& p_args,bool p_static,bool p_can_codecomplete) { if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) { tokenizer->advance(); } else { + int argidx=0; + while(true) { + if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { + _make_completable_call(argidx); + completion_node=p_parent; + } Node*arg = _parse_expression(p_parent,p_static); if (!arg) @@ -144,6 +137,7 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector& p_args,bool p_stat } tokenizer->advance(); + argidx++; } else { // something is broken _set_error("Expected ',' or ')'"); @@ -158,6 +152,48 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector& p_args,bool p_stat } +void GDParser::_make_completable_call(int p_arg) { + + completion_cursor=StringName(); + completion_type=COMPLETION_CALL_ARGUMENTS; + completion_class=current_class; + completion_function=current_function; + completion_line=tokenizer->get_token_line(); + completion_argument=p_arg; + completion_block=current_block; + tokenizer->advance(); + +} + + +bool GDParser::_get_completable_identifier(CompletionType p_type,StringName& identifier) { + + identifier=StringName(); + if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) { + identifier=tokenizer->get_token_identifier(); + tokenizer->advance(); + } + if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { + + completion_cursor=identifier; + completion_type=p_type; + completion_class=current_class; + completion_function=current_function; + completion_line=tokenizer->get_token_line(); + completion_block=current_block; + tokenizer->advance(); + + if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) { + identifier=identifier.operator String() + tokenizer->get_token_identifier().operator String(); + tokenizer->advance(); + } + + return true; + } + + return false; +} + GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_allow_assign) { @@ -199,6 +235,9 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ tokenizer->advance(); expr=subexpr; + } else if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { + tokenizer->advance(); + continue; //no point in cursor in the middle of expression } else if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT) { @@ -327,12 +366,19 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ Variant::Type bi_type = tokenizer->get_token_type(); tokenizer->advance(2); - if (tokenizer->get_token()!=GDTokenizer::TK_IDENTIFIER) { + + StringName identifier; + + if (_get_completable_identifier(COMPLETION_BUILT_IN_TYPE_CONSTANT,identifier)) { + + completion_built_in_constant=bi_type; + } + + if (identifier!=StringName()) { _set_error("Built-in type constant expected after '.'"); return NULL; } - StringName identifier = tokenizer->get_token_identifier(); if (!Variant::has_numeric_constant(bi_type,identifier)) { _set_error("Static constant '"+identifier.operator String()+"' not present in built-in type "+Variant::get_type_name(bi_type)+"."); @@ -342,7 +388,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ ConstantNode *cn = alloc_node(); cn->value=Variant::get_numeric_constant_value(bi_type,identifier); expr=cn; - tokenizer->advance(); + } else if (tokenizer->get_token(1)==GDTokenizer::TK_PARENTHESIS_OPEN && (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_TYPE || tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER || tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_FUNC)) { //function or constructor @@ -355,23 +401,35 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ TypeNode *tn = alloc_node(); tn->vtype=tokenizer->get_token_type(); op->arguments.push_back(tn); + tokenizer->advance(2); } else if (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_FUNC) { BuiltInFunctionNode *bn = alloc_node(); bn->function=tokenizer->get_token_built_in_func(); op->arguments.push_back(bn); + tokenizer->advance(2); } else { SelfNode *self = alloc_node(); op->arguments.push_back(self); + StringName identifier; + if (_get_completable_identifier(COMPLETION_FUNCTION,identifier)) { + + } + IdentifierNode* id = alloc_node(); - id->name=tokenizer->get_token_identifier(); + id->name=identifier; op->arguments.push_back(id); + tokenizer->advance(1); } - tokenizer->advance(2); - if (!_parse_arguments(op,op->arguments,p_static)) + if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { + _make_completable_call(0); + completion_node=op; + + } + if (!_parse_arguments(op,op->arguments,p_static,true)) return NULL; expr=op; @@ -380,25 +438,27 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ //identifier (reference) const ClassNode* cln = static_cast(get_parse_tree()); - bool bfn = false; - StringName idn( tokenizer->get_token_identifier() ); - - for( int i=0; iconstant_expressions.size(); ++i ) { - - if( cln->constant_expressions[i].identifier == idn ) { - tokenizer->advance(); - expr = cln->constant_expressions[i].expression; - bfn = true; - break; - } - } - - if( !bfn ) { - IdentifierNode *id = alloc_node(); - id->name = idn; - tokenizer->advance(); - expr = id; - } + bool bfn = false; + StringName identifier; + if (_get_completable_identifier(COMPLETION_IDENTIFIER,identifier)) { + + } + + for( int i=0; iconstant_expressions.size(); ++i ) { + + if( cln->constant_expressions[i].identifier == identifier ) { + + expr = cln->constant_expressions[i].expression; + bfn = true; + break; + } + } + + if ( !bfn ) { + IdentifierNode *id = alloc_node(); + id->name = identifier; + expr = id; + } } else if (/*tokenizer->get_token()==GDTokenizer::TK_OP_ADD ||*/ tokenizer->get_token()==GDTokenizer::TK_OP_SUB || tokenizer->get_token()==GDTokenizer::TK_OP_NOT || tokenizer->get_token()==GDTokenizer::TK_OP_BIT_INVERT) { @@ -600,7 +660,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ expr=dict; - } else if (tokenizer->get_token()==GDTokenizer::TK_PERIOD && tokenizer->get_token(1)==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token(2)==GDTokenizer::TK_PARENTHESIS_OPEN) { + } else if (tokenizer->get_token()==GDTokenizer::TK_PERIOD && (tokenizer->get_token(1)==GDTokenizer::TK_IDENTIFIER || tokenizer->get_token(1)==GDTokenizer::TK_CURSOR) && tokenizer->get_token(2)==GDTokenizer::TK_PARENTHESIS_OPEN) { // parent call tokenizer->advance(); //goto identifier @@ -611,12 +671,16 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ /*SelfNode *self = alloc_node(); op->arguments.push_back(self); forbidden for now */ + StringName identifier; + if (_get_completable_identifier(COMPLETION_PARENT_FUNCTION,identifier)) { + //indexing stuff + } - IdentifierNode* id = alloc_node(); - id->name=tokenizer->get_token_identifier(); + IdentifierNode *id = alloc_node(); + id->name=identifier; op->arguments.push_back(id); - tokenizer->advance(2); + tokenizer->advance(1); if (!_parse_arguments(op,op->arguments,p_static)) return NULL; @@ -651,7 +715,7 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ //indexing using "." - if (tokenizer->get_token(1)!=GDTokenizer::TK_IDENTIFIER && tokenizer->get_token(1)!=GDTokenizer::TK_BUILT_IN_FUNC ) { + if (tokenizer->get_token(1)!=GDTokenizer::TK_CURSOR && tokenizer->get_token(1)!=GDTokenizer::TK_IDENTIFIER && tokenizer->get_token(1)!=GDTokenizer::TK_BUILT_IN_FUNC ) { _set_error("Expected identifier as member"); return NULL; } else if (tokenizer->get_token(2)==GDTokenizer::TK_PARENTHESIS_OPEN) { @@ -659,37 +723,67 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ OperatorNode * op = alloc_node(); op->op=OperatorNode::OP_CALL; + tokenizer->advance(); + IdentifierNode * id = alloc_node(); - if (tokenizer->get_token(1)==GDTokenizer::TK_BUILT_IN_FUNC ) { + if (tokenizer->get_token()==GDTokenizer::TK_BUILT_IN_FUNC ) { //small hack so built in funcs don't obfuscate methods - id->name=GDFunctions::get_func_name(tokenizer->get_token_built_in_func(1)); + id->name=GDFunctions::get_func_name(tokenizer->get_token_built_in_func()); + tokenizer->advance(); + } else { - id->name=tokenizer->get_token_identifier(1); + StringName identifier; + if (_get_completable_identifier(COMPLETION_METHOD,identifier)) { + completion_node=op; + //indexing stuff + } + + id->name=identifier; } op->arguments.push_back(expr); // call what op->arguments.push_back(id); // call func //get arguments - tokenizer->advance(3); - if (!_parse_arguments(op,op->arguments,p_static)) + tokenizer->advance(1); + if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) { + _make_completable_call(0); + completion_node=op; + + } + if (!_parse_arguments(op,op->arguments,p_static,true)) return NULL; expr=op; } else { //simple indexing! + + OperatorNode * op = alloc_node(); op->op=OperatorNode::OP_INDEX_NAMED; + tokenizer->advance(); + + + StringName identifier; + if (_get_completable_identifier(COMPLETION_INDEX,identifier)) { + + if (identifier==StringName()) { + identifier="@temp"; //so it parses allright + } + completion_node=op; + + //indexing stuff + } IdentifierNode * id = alloc_node(); - id->name=tokenizer->get_token_identifier(1); + id->name=identifier; op->arguments.push_back(expr); op->arguments.push_back(id); expr=op; - tokenizer->advance(2); + } } else if (tokenizer->get_token()==GDTokenizer::TK_BRACKET_OPEN) { @@ -1442,6 +1536,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { cf_if->arguments.push_back(condition); cf_if->body = alloc_node(); + cf_if->body->parent_block=p_block; p_block->sub_blocks.push_back(cf_if->body); if (!_enter_indent_block(cf_if->body)) { @@ -1449,7 +1544,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { return; } + current_block=cf_if->body; _parse_block(cf_if->body,p_static); + current_block=p_block; + if (error_set) return; p_block->statements.push_back(cf_if); @@ -1476,6 +1574,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { tokenizer->advance(); cf_if->body_else=alloc_node(); + cf_if->body_else->parent_block=p_block; p_block->sub_blocks.push_back(cf_if->body_else); ControlFlowNode *cf_else = alloc_node(); @@ -1491,6 +1590,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { cf_if->body_else->statements.push_back(cf_else); cf_if=cf_else; cf_if->body=alloc_node(); + cf_if->body->parent_block=p_block; p_block->sub_blocks.push_back(cf_if->body); @@ -1499,7 +1599,9 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { return; } + current_block=cf_else->body; _parse_block(cf_else->body,p_static); + current_block=p_block; if (error_set) return; @@ -1515,13 +1617,16 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { tokenizer->advance(); cf_if->body_else=alloc_node(); + cf_if->body_else->parent_block=p_block; p_block->sub_blocks.push_back(cf_if->body_else); if (!_enter_indent_block(cf_if->body_else)) { p_block->end_line=tokenizer->get_token_line(); return; } + current_block=cf_if->body_else; _parse_block(cf_if->body_else,p_static); + current_block=p_block; if (error_set) return; @@ -1548,6 +1653,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { cf_while->arguments.push_back(condition); cf_while->body = alloc_node(); + cf_while->body->parent_block=p_block; p_block->sub_blocks.push_back(cf_while->body); if (!_enter_indent_block(cf_while->body)) { @@ -1555,7 +1661,9 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { return; } + current_block=cf_while->body; _parse_block(cf_while->body,p_static); + current_block=p_block; if (error_set) return; p_block->statements.push_back(cf_while); @@ -1592,6 +1700,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { cf_for->arguments.push_back(container); cf_for->body = alloc_node(); + cf_for->body->parent_block=p_block; p_block->sub_blocks.push_back(cf_for->body); if (!_enter_indent_block(cf_for->body)) { @@ -1599,7 +1708,10 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { return; } + current_block=cf_for->body; _parse_block(cf_for->body,p_static); + current_block=p_block; + if (error_set) return; p_block->statements.push_back(cf_for); @@ -1865,7 +1977,9 @@ void GDParser::_parse_class(ClassNode *p_class) { ClassNode *newclass = alloc_node(); newclass->initializer = alloc_node(); + newclass->initializer->parent_class=newclass; newclass->name=name; + newclass->owner=p_class; p_class->subclasses.push_back(newclass); @@ -1882,7 +1996,9 @@ void GDParser::_parse_class(ClassNode *p_class) { _set_error("Indented block expected."); return; } + current_class=newclass; _parse_class(newclass); + current_class=p_class; } break; /* this is for functions.... @@ -2020,6 +2136,7 @@ void GDParser::_parse_class(ClassNode *p_class) { tokenizer->advance(); BlockNode *block = alloc_node(); + block->parent_class=p_class; if (name=="_init") { @@ -2095,8 +2212,12 @@ void GDParser::_parse_class(ClassNode *p_class) { p_class->functions.push_back(function); - _parse_block(block,_static); + current_function=function; function->body=block; + current_block=block; + _parse_block(block,_static); + current_block=NULL; + //arguments } break; case GDTokenizer::TK_PR_EXPORT: { @@ -2401,7 +2522,9 @@ void GDParser::_parse_class(ClassNode *p_class) { } member.identifier=tokenizer->get_token_identifier(); + member.expression=NULL; member._export.name=member.identifier; + member.line=tokenizer->get_token_line(); tokenizer->advance(); if (tokenizer->get_token()==GDTokenizer::TK_OP_ASSIGN) { @@ -2417,6 +2540,8 @@ void GDParser::_parse_class(ClassNode *p_class) { if (!subexpr) return; + member.expression=subexpr; + if (autoexport) { if (subexpr->type==Node::TYPE_ARRAY) { @@ -2608,6 +2733,8 @@ Error GDParser::_parse(const String& p_base_path) { //assume class ClassNode *main_class = alloc_node(); main_class->initializer = alloc_node(); + main_class->initializer->parent_class=main_class; + current_class=main_class; _parse_class(main_class); @@ -2625,6 +2752,12 @@ Error GDParser::_parse(const String& p_base_path) { Error GDParser::parse_bytecode(const Vector &p_bytecode,const String& p_base_path, const String &p_self_path) { + completion_type=COMPLETION_NONE; + completion_node=NULL; + completion_class=NULL; + completion_function=NULL; + completion_block=NULL; + self_path=p_self_path; GDTokenizerBuffer *tb = memnew( GDTokenizerBuffer ); tb->set_code_buffer(p_bytecode); @@ -2638,6 +2771,12 @@ Error GDParser::parse_bytecode(const Vector &p_bytecode,const String& p Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_just_validate, const String &p_self_path) { + completion_type=COMPLETION_NONE; + completion_node=NULL; + completion_class=NULL; + completion_function=NULL; + completion_block=NULL; + self_path=p_self_path; GDTokenizerText *tt = memnew( GDTokenizerText ); tt->set_code(p_code); @@ -2667,6 +2806,12 @@ void GDParser::clear() { head=NULL; list=NULL; + completion_type=COMPLETION_NONE; + completion_node=NULL; + completion_class=NULL; + completion_function=NULL; + completion_block=NULL; + validating=false; error_set=false; tab_level.clear(); @@ -2680,6 +2825,52 @@ void GDParser::clear() { } + +GDParser::CompletionType GDParser::get_completion_type() { + + return completion_type; +} + +StringName GDParser::get_completion_cursor() { + + return completion_cursor; +} + +int GDParser::get_completion_line() { + + return completion_line; +} + +Variant::Type GDParser::get_completion_built_in_constant(){ + + return completion_built_in_constant; +} + +GDParser::Node *GDParser::get_completion_node(){ + + return completion_node; +} + +GDParser::BlockNode *GDParser::get_completion_block() { + + return completion_block; +} + +GDParser::ClassNode *GDParser::get_completion_class(){ + + return completion_class; +} + +GDParser::FunctionNode *GDParser::get_completion_function(){ + + return completion_function; +} + +int GDParser::get_completion_argument_index() { + + return completion_argument; +} + GDParser::GDParser() { head=NULL; diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h index 3f82cafc612..107f1439c07 100644 --- a/modules/gdscript/gd_parser.h +++ b/modules/gdscript/gd_parser.h @@ -84,6 +84,8 @@ public: StringName identifier; StringName setter; StringName getter; + int line; + Node *expression; }; struct Constant { StringName identifier; @@ -96,10 +98,11 @@ public: Vector functions; Vector static_functions; BlockNode *initializer; + ClassNode *owner; //Vector initializers; int end_line; - ClassNode() { tool=false; type=TYPE_CLASS; extends_used=false; end_line=-1;} + ClassNode() { tool=false; type=TYPE_CLASS; extends_used=false; end_line=-1; owner=NULL;} }; @@ -118,6 +121,8 @@ public: struct BlockNode : public Node { + ClassNode *parent_class=NULL; + BlockNode *parent_block=NULL; Map locals; List statements; Vector variables; @@ -126,7 +131,7 @@ public: //the following is useful for code completion List sub_blocks; int end_line; - BlockNode() { type=TYPE_BLOCK; end_line=-1;} + BlockNode() { type=TYPE_BLOCK; end_line=-1; parent_block=NULL; parent_class=NULL; } }; struct TypeNode : public Node { @@ -349,6 +354,18 @@ public: }; */ + enum CompletionType { + COMPLETION_NONE, + COMPLETION_BUILT_IN_TYPE_CONSTANT, + COMPLETION_FUNCTION, + COMPLETION_IDENTIFIER, + COMPLETION_PARENT_FUNCTION, + COMPLETION_METHOD, + COMPLETION_CALL_ARGUMENTS, + COMPLETION_INDEX, + }; + + private: @@ -375,12 +392,31 @@ private: String base_path; String self_path; + + ClassNode *current_class; + FunctionNode *current_function; + BlockNode *current_block; + + bool _get_completable_identifier(CompletionType p_type,StringName& identifier); + void _make_completable_call(int p_arg); + + CompletionType completion_type; + StringName completion_cursor; + bool completion_static; + Variant::Type completion_built_in_constant; + Node *completion_node; + ClassNode *completion_class; + FunctionNode *completion_function; + BlockNode *completion_block; + int completion_line; + int completion_argument; + PropertyInfo current_export; void _set_error(const String& p_error, int p_line=-1, int p_column=-1); - bool _parse_arguments(Node* p_parent,Vector& p_args,bool p_static); + bool _parse_arguments(Node* p_parent, Vector& p_args, bool p_static, bool p_can_codecomplete=false); bool _enter_indent_block(BlockNode *p_block=NULL); bool _parse_newline(); Node* _parse_expression(Node *p_parent,bool p_static,bool p_allow_assign=false); @@ -404,6 +440,19 @@ public: const Node *get_parse_tree() const; + //completion info + + CompletionType get_completion_type(); + StringName get_completion_cursor(); + int get_completion_line(); + Variant::Type get_completion_built_in_constant(); + Node *get_completion_node(); + ClassNode *get_completion_class(); + BlockNode *get_completion_block(); + FunctionNode *get_completion_function(); + int get_completion_argument_index(); + + void clear(); GDParser(); ~GDParser(); diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index f4e4dffaa54..5574b30d449 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -129,6 +129,10 @@ friend class GDCompiler; const char*_func_cname; #endif +#ifdef TOOLS_ENABLED + Vector arg_names; +#endif + List stack_debug; _FORCE_INLINE_ Variant *_get_variant(int p_address,GDInstance *p_instance,GDScript *p_script,Variant &self,Variant *p_stack,String& r_error) const; @@ -169,6 +173,19 @@ public: _FORCE_INLINE_ bool is_empty() const { return _code_size==0; } int get_argument_count() const { return _argument_count; } + StringName get_argument_name(int p_idx) const { +#ifdef TOOLS_ENABLED + ERR_FAIL_INDEX_V(p_idx,arg_names.size(),StringName()); + return arg_names[p_idx]; +#endif + return StringName(); + + } + Variant get_default_argument(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx,default_arguments.size(),Variant()); + return default_arguments[p_idx]; + } + Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err,CallState *p_state=NULL); GDFunction(); @@ -293,6 +310,7 @@ protected: static void _bind_methods(); public: + bool is_valid() const { return valid; } const Map >& get_subclasses() const { return subclasses; } const Map& get_constants() const { return constants; } @@ -488,7 +506,7 @@ public: virtual bool has_named_classes() const; virtual int find_function(const String& p_function,const String& p_code) const; virtual String make_function(const String& p_class,const String& p_name,const StringArray& p_args) const; - virtual Error complete_keyword(const String& p_code, int p_line, const String& p_base_path,const String& p_keyword, List* r_options); + virtual Error complete_code(const String& p_code, const String& p_base_path, Object*p_owner,List* r_options,String& r_call_hint); virtual void auto_indent_code(String& p_code,int p_from_line,int p_to_line) const; /* DEBUGGER FUNCTIONS */ diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp index 0f6ee416165..6f968f2080c 100644 --- a/modules/gdscript/gd_tokenizer.cpp +++ b/modules/gdscript/gd_tokenizer.cpp @@ -110,7 +110,8 @@ const char* GDTokenizer::token_names[TK_MAX]={ "':'", "'\\n'", "Error", -"EOF"}; +"EOF", +"Cursor"}; const char *GDTokenizer::get_token_name(Token p_token) { @@ -648,6 +649,9 @@ void GDTokenizerText::_advance() { } } break; + case 0xFFFF: { + _make_token(TK_CURSOR); + } break; default: { if (_is_number(GETCHAR(0)) || (GETCHAR(0)=='.' && _is_number(GETCHAR(1)))) { diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h index fe7bfa73ca5..ff59c249a7f 100644 --- a/modules/gdscript/gd_tokenizer.h +++ b/modules/gdscript/gd_tokenizer.h @@ -118,6 +118,7 @@ public: TK_NEWLINE, TK_ERROR, TK_EOF, + TK_CURSOR, //used for code completion TK_MAX }; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 1ab15dcda38..a10152a025d 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1396,8 +1396,9 @@ void OS_Windows::set_window_title(const String& p_title) { void OS_Windows::set_video_mode(const VideoMode& p_video_mode,int p_screen) { - + } + OS::VideoMode OS_Windows::get_video_mode(int p_screen) const { return video_mode; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 011850138b3..f9d36138a26 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -1178,6 +1178,19 @@ NodePath AnimationPlayer::get_root() const { return root; } +void AnimationPlayer::get_argument_options(const StringName& p_function,int p_idx,List*r_options) const { + + String pf = p_function; + if (p_function=="play" || p_function=="remove_animation" || p_function=="has_animation" || p_function=="queue") { + List al; + get_animation_list(&al); + for (List::Element *E=al.front();E;E=E->next()) { + + r_options->push_back("\""+String(E->get())+"\""); + } + } + Node::get_argument_options(p_function,p_idx,r_options); +} void AnimationPlayer::_bind_methods() { diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index 038c43d5697..8ac5d96bf35 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -289,6 +289,9 @@ public: NodePath get_root() const; void clear_caches(); ///< must be called by hand if an animation was modified after added + + void get_argument_options(const StringName& p_function,int p_idx,List*r_options) const; + AnimationPlayer(); ~AnimationPlayer(); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index aea0aacf426..5a0706f01ef 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1325,9 +1325,12 @@ Size2 Control::get_minimum_size() const { Ref Control::get_icon(const StringName& p_name,const StringName& p_type) const { - const Ref* tex = data.icon_override.getptr(p_name); - if (tex) - return *tex; + if (p_type==StringName()) { + + const Ref* tex = data.icon_override.getptr(p_name); + if (tex) + return *tex; + } StringName type = p_type?p_type:get_type_name(); @@ -1353,12 +1356,11 @@ Ref Control::get_icon(const StringName& p_name,const StringName& p_type Ref Control::get_stylebox(const StringName& p_name,const StringName& p_type) const { - - const Ref* style = data.style_override.getptr(p_name); - - - if (style) - return *style; + if (p_type==StringName()) { + const Ref* style = data.style_override.getptr(p_name); + if (style) + return *style; + } StringName type = p_type?p_type:get_type_name(); @@ -1381,10 +1383,12 @@ Ref Control::get_stylebox(const StringName& p_name,const StringName& p } Ref Control::get_font(const StringName& p_name,const StringName& p_type) const { - - const Ref* font = data.font_override.getptr(p_name); - if (font) - return *font; + + if (p_type==StringName()) { + const Ref* font = data.font_override.getptr(p_name); + if (font) + return *font; + } StringName type = p_type?p_type:get_type_name(); @@ -1410,10 +1414,12 @@ Ref Control::get_font(const StringName& p_name,const StringName& p_type) c } Color Control::get_color(const StringName& p_name,const StringName& p_type) const { - - const Color* color = data.color_override.getptr(p_name); - if (color) - return *color; + + if (p_type==StringName()) { + const Color* color = data.color_override.getptr(p_name); + if (color) + return *color; + } StringName type = p_type?p_type:get_type_name(); // try with custom themes @@ -1437,10 +1443,12 @@ Color Control::get_color(const StringName& p_name,const StringName& p_type) cons } int Control::get_constant(const StringName& p_name,const StringName& p_type) const { - - const int* constant = data.constant_override.getptr(p_name); - if (constant) - return *constant; + + if (p_type==StringName()) { + const int* constant = data.constant_override.getptr(p_name); + if (constant) + return *constant; + } StringName type = p_type?p_type:get_type_name(); // try with custom themes @@ -1467,9 +1475,11 @@ int Control::get_constant(const StringName& p_name,const StringName& p_type) con bool Control::has_icon(const StringName& p_name,const StringName& p_type) const { - const Ref* tex = data.icon_override.getptr(p_name); - if (tex) - return true; + if (p_type==StringName()) { + const Ref* tex = data.icon_override.getptr(p_name); + if (tex) + return true; + } StringName type = p_type?p_type:get_type_name(); @@ -1494,11 +1504,12 @@ bool Control::has_icon(const StringName& p_name,const StringName& p_type) const } bool Control::has_stylebox(const StringName& p_name,const StringName& p_type) const { - - const Ref* style = data.style_override.getptr(p_name); - - if (style) - return true; + if (p_type==StringName()) { + const Ref* style = data.style_override.getptr(p_name); + + if (style) + return true; + } StringName type = p_type?p_type:get_type_name(); @@ -1523,9 +1534,11 @@ bool Control::has_stylebox(const StringName& p_name,const StringName& p_type) co } bool Control::has_font(const StringName& p_name,const StringName& p_type) const { - const Ref* font = data.font_override.getptr(p_name); - if (font) - return true; + if (p_type==StringName()) { + const Ref* font = data.font_override.getptr(p_name); + if (font) + return true; + } StringName type = p_type?p_type:get_type_name(); @@ -1551,9 +1564,11 @@ bool Control::has_font(const StringName& p_name,const StringName& p_type) const } bool Control::has_color(const StringName& p_name,const StringName& p_type) const { - const Color* color = data.color_override.getptr(p_name); - if (color) - return true; + if (p_type==StringName()) { + const Color* color = data.color_override.getptr(p_name); + if (color) + return true; + } StringName type = p_type?p_type:get_type_name(); @@ -1578,10 +1593,13 @@ bool Control::has_color(const StringName& p_name,const StringName& p_type) const } bool Control::has_constant(const StringName& p_name,const StringName& p_type) const { - - const int* constant = data.constant_override.getptr(p_name); - if (constant) - return true; + + if (p_type==StringName()) { + + const int* constant = data.constant_override.getptr(p_name); + if (constant) + return true; + } StringName type = p_type?p_type:get_type_name(); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index ba68948e6b0..715c3cbf7ea 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -359,385 +359,445 @@ void TextEdit::_update_scrollbars() { void TextEdit::_notification(int p_what) { - switch(p_what) { - case NOTIFICATION_ENTER_TREE: { + switch(p_what) { + case NOTIFICATION_ENTER_TREE: { - _update_caches(); - if (cursor_changed_dirty) - MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); - if (text_changed_dirty) - MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); + _update_caches(); + if (cursor_changed_dirty) + MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); + if (text_changed_dirty) + MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); - } break; - case NOTIFICATION_RESIZED: { + } break; + case NOTIFICATION_RESIZED: { - cache.size=get_size(); - adjust_viewport_to_cursor(); + cache.size=get_size(); + adjust_viewport_to_cursor(); - } break; - case NOTIFICATION_THEME_CHANGED: { + } break; + case NOTIFICATION_THEME_CHANGED: { - _update_caches(); - }; - case NOTIFICATION_DRAW: { + _update_caches(); + }; + case NOTIFICATION_DRAW: { - int line_number_char_count=0; + int line_number_char_count=0; - { - int lc=text.size()+1; - cache.line_number_w=0; - while(lc) { - cache.line_number_w+=1; - lc/=10; - }; + { + int lc=text.size()+1; + cache.line_number_w=0; + while(lc) { + cache.line_number_w+=1; + lc/=10; + }; - if (line_numbers) { + if (line_numbers) { - line_number_char_count=cache.line_number_w; - cache.line_number_w=(cache.line_number_w+1)*cache.font->get_char_size('0').width; - } else { - cache.line_number_w=0; - } + line_number_char_count=cache.line_number_w; + cache.line_number_w=(cache.line_number_w+1)*cache.font->get_char_size('0').width; + } else { + cache.line_number_w=0; + } - } - _update_scrollbars(); + } + _update_scrollbars(); - RID ci = get_canvas_item(); - int xmargin_beg=cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w; - int xmargin_end=cache.size.width-cache.style_normal->get_margin(MARGIN_RIGHT); - //let's do it easy for now: - cache.style_normal->draw(ci,Rect2(Point2(),cache.size)); - if (has_focus()) - cache.style_focus->draw(ci,Rect2(Point2(),cache.size)); + RID ci = get_canvas_item(); + int xmargin_beg=cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w; + int xmargin_end=cache.size.width-cache.style_normal->get_margin(MARGIN_RIGHT); + //let's do it easy for now: + cache.style_normal->draw(ci,Rect2(Point2(),cache.size)); + if (has_focus()) + cache.style_focus->draw(ci,Rect2(Point2(),cache.size)); - int ascent=cache.font->get_ascent(); + int ascent=cache.font->get_ascent(); - int visible_rows = get_visible_rows(); + int visible_rows = get_visible_rows(); - int tab_w = cache.font->get_char_size(' ').width*tab_size; + int tab_w = cache.font->get_char_size(' ').width*tab_size; - Color color = cache.font_color; - int in_region=-1; + Color color = cache.font_color; + int in_region=-1; - if (syntax_coloring) { + if (syntax_coloring) { - if (custom_bg_color.a>0.01) { + if (custom_bg_color.a>0.01) { - Point2i ofs = Point2i(cache.style_normal->get_offset())/2.0; - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(ofs, get_size()-cache.style_normal->get_minimum_size()+ofs),custom_bg_color); - } - //compute actual region to start (may be inside say, a comment). - //slow in very large documments :( but ok for source! + Point2i ofs = Point2i(cache.style_normal->get_offset())/2.0; + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(ofs, get_size()-cache.style_normal->get_minimum_size()+ofs),custom_bg_color); + } + //compute actual region to start (may be inside say, a comment). + //slow in very large documments :( but ok for source! - for(int i=0;i& cri_map=text.get_color_region_info(i); + const Map& cri_map=text.get_color_region_info(i); - if (in_region>=0 && color_regions[in_region].line_only) { - in_region=-1; //reset regions that end at end of line - } + if (in_region>=0 && color_regions[in_region].line_only) { + in_region=-1; //reset regions that end at end of line + } - for( const Map::Element* E= cri_map.front();E;E=E->next() ) { + for( const Map::Element* E= cri_map.front();E;E=E->next() ) { - const Text::ColorRegionInfo &cri=E->get(); + const Text::ColorRegionInfo &cri=E->get(); - if (in_region==-1) { + if (in_region==-1) { - if (!cri.end) { + if (!cri.end) { - in_region=cri.region; - } - } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise + in_region=cri.region; + } + } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise - if (cri.end || color_regions[cri.region].eq) { + if (cri.end || color_regions[cri.region].eq) { - in_region=-1; - } - } - } - } - } + in_region=-1; + } + } + } + } + } - int deregion=0; //force it to clear inrgion - Point2 cursor_pos; + int deregion=0; //force it to clear inrgion + Point2 cursor_pos; - for (int i=0;i=(int)text.size()) - continue; + if (line<0 || line>=(int)text.size()) + continue; - const String &str=text[line]; + const String &str=text[line]; - int char_margin=xmargin_beg-cursor.x_ofs; - int char_ofs=0; - int ofs_y=i*get_row_height()+cache.line_spacing/2; - bool prev_is_char=false; - bool in_keyword=false; - Color keyword_color; + int char_margin=xmargin_beg-cursor.x_ofs; + int char_ofs=0; + int ofs_y=i*get_row_height()+cache.line_spacing/2; + bool prev_is_char=false; + bool in_keyword=false; + Color keyword_color; - if (cache.line_number_w) { - Color fcol = cache.font_color; - fcol.a*=0.4; - String fc = String::num(line+1); - while (fc.length() < line_number_char_count) { - fc="0"+fc; - } + if (cache.line_number_w) { + Color fcol = cache.font_color; + fcol.a*=0.4; + String fc = String::num(line+1); + while (fc.length() < line_number_char_count) { + fc="0"+fc; + } - cache.font->draw(ci,Point2(cache.style_normal->get_margin(MARGIN_LEFT),ofs_y+cache.font->get_ascent()),fc,fcol); - } + cache.font->draw(ci,Point2(cache.style_normal->get_margin(MARGIN_LEFT),ofs_y+cache.font->get_ascent()),fc,fcol); + } - const Map& cri_map=text.get_color_region_info(line); + const Map& cri_map=text.get_color_region_info(line); - if (text.is_marked(line)) { + if (text.is_marked(line)) { - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.mark_color); - } + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.mark_color); + } - if (text.is_breakpoint(line)) { + if (text.is_breakpoint(line)) { - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.breakpoint_color); - } + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.breakpoint_color); + } - if (line==cursor.line) { + if (line==cursor.line) { - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.current_line_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.current_line_color); - } - for (int j=0;j0) { - deregion--; - if (deregion==0) - in_region=-1; - } - if (syntax_coloring && deregion==0) { + if (deregion>0) { + deregion--; + if (deregion==0) + in_region=-1; + } + if (syntax_coloring && deregion==0) { - color = cache.font_color; //reset - //find keyword - bool is_char = _is_text_char(str[j]); - bool is_symbol=_is_symbol(str[j]); + color = cache.font_color; //reset + //find keyword + bool is_char = _is_text_char(str[j]); + bool is_symbol=_is_symbol(str[j]); - if (j==0 && in_region>=0 && color_regions[in_region].line_only) { - in_region=-1; //reset regions that end at end of line - } + if (j==0 && in_region>=0 && color_regions[in_region].line_only) { + in_region=-1; //reset regions that end at end of line + } - if (is_symbol && cri_map.has(j)) { + if (is_symbol && cri_map.has(j)) { - const Text::ColorRegionInfo &cri=cri_map[j]; + const Text::ColorRegionInfo &cri=cri_map[j]; - if (in_region==-1) { + if (in_region==-1) { - if (!cri.end) { + if (!cri.end) { - in_region=cri.region; - } - } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise + in_region=cri.region; + } + } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise - if (cri.end || color_regions[cri.region].eq) { + if (cri.end || color_regions[cri.region].eq) { - deregion=color_regions[cri.region].eq?color_regions[cri.region].begin_key.length():color_regions[cri.region].end_key.length(); - } - } - } + deregion=color_regions[cri.region].eq?color_regions[cri.region].begin_key.length():color_regions[cri.region].end_key.length(); + } + } + } - if (!is_char) - in_keyword=false; + if (!is_char) + in_keyword=false; - if (in_region==-1 && !in_keyword && is_char && !prev_is_char) { + if (in_region==-1 && !in_keyword && is_char && !prev_is_char) { - int to=j; - while(_is_text_char(str[to]) && to=0) - color=color_regions[in_region].color; - else if (in_keyword) - color=keyword_color; - else if (is_symbol) - color=symbol_color; + if (in_region>=0) + color=color_regions[in_region].color; + else if (in_keyword) + color=keyword_color; + else if (is_symbol) + color=symbol_color; - prev_is_char=is_char; + prev_is_char=is_char; - } - int char_w; + } + int char_w; - //handle tabulator + //handle tabulator - if (str[j]=='\t') { - int left = char_ofs%tab_w; - if (left==0) - char_w=tab_w; - else - char_w=tab_w-char_ofs%tab_w; // is right... + if (str[j]=='\t') { + int left = char_ofs%tab_w; + if (left==0) + char_w=tab_w; + else + char_w=tab_w-char_ofs%tab_w; // is right... - } else { - char_w=cache.font->get_char_size(str[j],str[j+1]).width; - } + } else { + char_w=cache.font->get_char_size(str[j],str[j+1]).width; + } - if ( (char_ofs+char_margin)=xmargin_end) { - if (syntax_coloring) - continue; - else - break; - } + if ( (char_ofs+char_margin+char_w)>=xmargin_end) { + if (syntax_coloring) + continue; + else + break; + } - bool in_selection = (selection.active && line>=selection.from_line && line<=selection.to_line && (line>selection.from_line || j>=selection.from_column) && (line=selection.from_line && line<=selection.to_line && (line>selection.from_line || j>=selection.from_column) && (linecanvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y ), Size2i(char_w,get_row_height())),cache.selection_color); - } + if (in_selection) { + //inside selection! + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y ), Size2i(char_w,get_row_height())),cache.selection_color); + } - if (str[j]>=32) - cache.font->draw_char(ci,Point2i( char_ofs+char_margin, ofs_y+ascent),str[j],str[j+1],in_selection?cache.font_selected_color:color); - else if (draw_tabs && str[j]=='\t') { - int yofs= (get_row_height() - cache.tab_icon->get_height())/2; - cache.tab_icon->draw(ci, Point2(char_ofs+char_margin,ofs_y+yofs),in_selection?cache.font_selected_color:color); - } + if (str[j]>=32) + cache.font->draw_char(ci,Point2i( char_ofs+char_margin, ofs_y+ascent),str[j],str[j+1],in_selection?cache.font_selected_color:color); + else if (draw_tabs && str[j]=='\t') { + int yofs= (get_row_height() - cache.tab_icon->get_height())/2; + cache.tab_icon->draw(ci, Point2(char_ofs+char_margin,ofs_y+yofs),in_selection?cache.font_selected_color:color); + } - if (cursor.column==j && cursor.line==line) { + if (cursor.column==j && cursor.line==line) { - cursor_pos = Point2i( char_ofs+char_margin, ofs_y ); - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color); + cursor_pos = Point2i( char_ofs+char_margin, ofs_y ); + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color); - } - char_ofs+=char_w; + } + char_ofs+=char_w; - } + } - if (cursor.column==str.length() && cursor.line==line) { + if (cursor.column==str.length() && cursor.line==line) { - cursor_pos=Point2i( char_ofs+char_margin, ofs_y ); - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color); + cursor_pos=Point2i( char_ofs+char_margin, ofs_y ); + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color); - } - } + } + } - if (completion_active) { - // code completion box - Ref csb = get_stylebox("completion"); - Ref csel = get_stylebox("completion_selected"); - int maxlines = get_constant("completion_lines"); - int cmax_width = get_constant("completion_max_width")*cache.font->get_char_size('x').x; - Color existing = get_color("completion_existing"); - int scrollw = get_constant("completion_scroll_width"); - Color scrollc = get_color("completion_scroll_color"); + if (completion_active) { + // code completion box + Ref csb = get_stylebox("completion"); + Ref csel = get_stylebox("completion_selected"); + int maxlines = get_constant("completion_lines"); + int cmax_width = get_constant("completion_max_width")*cache.font->get_char_size('x').x; + Color existing = get_color("completion_existing"); + int scrollw = get_constant("completion_scroll_width"); + Color scrollc = get_color("completion_scroll_color"); - int lines = MIN(completion_options.size(),maxlines); - int w=0; - int h=lines*get_row_height(); - int nofs = cache.font->get_string_size(completion_base).width; + int lines = MIN(completion_options.size(),maxlines); + int w=0; + int h=lines*get_row_height(); + int nofs = cache.font->get_string_size(completion_base).width; - if (completion_options.size() < 50) { - for(int i=0;iget_string_size(completion_options[i]).x,cmax_width); - if (w2>w) - w=w2; - } - } else { - w=cmax_width; - } - int th = h + csb->get_minimum_size().y; - if (cursor_pos.y+get_row_height()+th > get_size().height) { - completion_rect.pos.y=cursor_pos.y-th; - } else { - completion_rect.pos.y=cursor_pos.y+get_row_height()+csb->get_offset().y; - } + if (completion_options.size() < 50) { + for(int i=0;iget_string_size(completion_options[i]).x,cmax_width); + if (w2>w) + w=w2; + } + } else { + w=cmax_width; + } - if (cursor_pos.x-nofs+w+scrollw > get_size().width) { - completion_rect.pos.x=get_size().width-w-scrollw; - } else { - completion_rect.pos.x=cursor_pos.x-nofs; - } + int th = h + csb->get_minimum_size().y; + if (cursor_pos.y+get_row_height()+th > get_size().height) { + completion_rect.pos.y=cursor_pos.y-th; + } else { + completion_rect.pos.y=cursor_pos.y+get_row_height()+csb->get_offset().y; - completion_rect.size.width=w; - completion_rect.size.height=h; - if (completion_options.size()<=maxlines) - scrollw=0; + } - draw_style_box(csb,Rect2(completion_rect.pos-csb->get_offset(),completion_rect.size+csb->get_minimum_size()+Size2(scrollw,0))); + if (cursor_pos.x-nofs+w+scrollw > get_size().width) { + completion_rect.pos.x=get_size().width-w-scrollw; + } else { + completion_rect.pos.x=cursor_pos.x-nofs; + } + completion_rect.size.width=w; + completion_rect.size.height=h; + if (completion_options.size()<=maxlines) + scrollw=0; - int line_from = CLAMP(completion_index - lines/2, 0, completion_options.size() - lines); - draw_style_box(csel,Rect2(Point2(completion_rect.pos.x,completion_rect.pos.y+(completion_index-line_from)*get_row_height()),Size2(completion_rect.size.width,get_row_height()))); + draw_style_box(csb,Rect2(completion_rect.pos-csb->get_offset(),completion_rect.size+csb->get_minimum_size()+Size2(scrollw,0))); - draw_rect(Rect2(completion_rect.pos,Size2(nofs,completion_rect.size.height)),existing); - for(int i=0;i= completion_options.size()); - draw_string(cache.font,Point2(completion_rect.pos.x,completion_rect.pos.y+i*get_row_height()+cache.font->get_ascent()),completion_options[l],cache.font_color,completion_rect.size.width); - } + draw_rect(Rect2(completion_rect.pos,Size2(nofs,completion_rect.size.height)),existing); - if (scrollw) { - //draw a small scroll rectangle to show a position in the options - float r = maxlines / (float)completion_options.size(); - float o = line_from / (float)completion_options.size(); - draw_rect(Rect2(completion_rect.pos.x+completion_rect.size.width,completion_rect.pos.y+o*completion_rect.size.y,scrollw,completion_rect.size.y*r),scrollc); - } + for(int i=0;i= completion_options.size()); + draw_string(cache.font,Point2(completion_rect.pos.x,completion_rect.pos.y+i*get_row_height()+cache.font->get_ascent()),completion_options[l],cache.font_color,completion_rect.size.width); + } - } + if (scrollw) { + //draw a small scroll rectangle to show a position in the options + float r = maxlines / (float)completion_options.size(); + float o = line_from / (float)completion_options.size(); + draw_rect(Rect2(completion_rect.pos.x+completion_rect.size.width,completion_rect.pos.y+o*completion_rect.size.y,scrollw,completion_rect.size.y*r),scrollc); + } + completion_line_ofs=line_from; + } - } break; - case NOTIFICATION_FOCUS_ENTER: { + if (completion_hint!="") { - if (OS::get_singleton()->has_virtual_keyboard()) - OS::get_singleton()->show_virtual_keyboard(get_text(),get_global_rect()); + Ref sb = get_stylebox("panel","TooltipPanel"); + Ref font = cache.font; + Color font_color = get_color("font_color","TooltipLabel"); - } break; - case NOTIFICATION_FOCUS_EXIT: { - if (OS::get_singleton()->has_virtual_keyboard()) - OS::get_singleton()->hide_virtual_keyboard(); + int max_w=0; + int sc = completion_hint.get_slice_count("\n"); + int offset=0; + int spacing=0; + for(int i=0;iget_string_size(l).x; + max_w = MAX(len,max_w); + if (i==0) { + offset = font->get_string_size(l.substr(0,l.find(String::chr(0xFFFF)))).x; + } else { + spacing+=cache.line_spacing; + } - } + + } + + + + Size2 size = Size2(max_w,sc*font->get_height()+spacing); + Size2 minsize = size+sb->get_minimum_size(); + + + if (completion_hint_offset==-0xFFFF) { + completion_hint_offset=cursor_pos.x-offset; + } + + + Point2 hint_ofs = Vector2(completion_hint_offset,cursor_pos.y-minsize.y); + draw_style_box(sb,Rect2(hint_ofs,minsize)); + + spacing=0; + for(int i=0;iget_string_size(l.substr(0,l.find(String::chr(0xFFFF)))).x; + end = font->get_string_size(l.substr(0,l.rfind(String::chr(0xFFFF)))).x; + } + + draw_string(font,hint_ofs+sb->get_offset()+Vector2(0,font->get_ascent()+font->get_height()*i+spacing),l.replace(String::chr(0xFFFF),""),font_color); + if (end>0) { + Vector2 b = hint_ofs+sb->get_offset()+Vector2(begin,font->get_height()+font->get_height()*i+spacing-1); + draw_line(b,b+Vector2(end-begin,0),font_color); + } + spacing+=cache.line_spacing; + } + } + + + } break; + case NOTIFICATION_FOCUS_ENTER: { + + if (OS::get_singleton()->has_virtual_keyboard()) + OS::get_singleton()->show_virtual_keyboard(get_text(),get_global_rect()); + + } break; + case NOTIFICATION_FOCUS_EXIT: { + + if (OS::get_singleton()->has_virtual_keyboard()) + OS::get_singleton()->hide_virtual_keyboard(); + + } break; + + } } void TextEdit::_consume_pair_symbol(CharType ch) { @@ -918,6 +978,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { return; } else { _cancel_completion(); + _cancel_code_hint(); } if (mb.pressed) { @@ -1172,6 +1233,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { } _cancel_completion(); + } /* TEST CONTROL FIRST!! */ @@ -1268,6 +1330,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { break; unselect=true; break; + default: if (k.unicode>=32 && !k.mod.command && !k.mod.alt && !k.mod.meta) clear=true; @@ -1318,6 +1381,13 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { _push_current_op(); } break; + case KEY_ESCAPE: { + if (completion_hint!="") { + completion_hint=""; + update(); + + } + } break; case KEY_TAB: { if (readonly) @@ -1454,6 +1524,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { if (k.mod.shift) _post_shift_selection(); + _cancel_code_hint(); } break; case KEY_DOWN: { @@ -1473,6 +1544,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { if (k.mod.shift) _post_shift_selection(); + _cancel_code_hint(); } break; @@ -2333,6 +2405,30 @@ String TextEdit::get_text() { }; +String TextEdit::get_text_for_completion() { + + String longthing; + int len = text.size(); + for (int i=0;i0 && l[cofs-1]>32 && !_is_symbol(l[cofs-1])) { - s=String::chr(l[cofs-1])+s; + + while(cofs>0 && l[cofs-1]>32 && _is_completable(l[cofs-1])) { + s=String::chr(l[cofs-1])+s; + if (l[cofs-1]=='\'' || l[cofs-1]=='"') + break; + cofs--; } + update(); if (s=="" && (cofs==0 || !completion_prefixes.has(String::chr(l[cofs-1])))) { @@ -3055,36 +3174,24 @@ void TextEdit::_update_completion_candidates() { completion_enabled=true; } + + void TextEdit::query_code_comple() { - String l = text[cursor.line]; - int ofs = CLAMP(cursor.column,0,l.length()); - String cs; - while(ofs>0 && l[ofs-1]>32) { + String l = text[cursor.line]; + int ofs = CLAMP(cursor.column,0,l.length()); - if (_is_symbol(l[ofs-1])) { - String s; - while(ofs>0 && l[ofs-1]>32 && _is_symbol(l[ofs-1])) { - s=String::chr(l[ofs-1])+s; - ofs--; - } - if (completion_prefixes.has(s)) - cs=s+cs; - else - break; - } else { + if (ofs>0 && (_is_completable(l[ofs-1]) || completion_prefixes.has(String::chr(l[ofs-1])))) + emit_signal("request_completion"); - cs=String::chr(l[ofs-1])+cs; - ofs--; - } +} - } - if (cs!="") { - emit_signal("request_completion",cs,cursor.line); - - } +void TextEdit::set_code_hint(const String& p_hint) { + completion_hint=p_hint; + completion_hint_offset=-0xFFFF; + update(); } void TextEdit::code_complete(const Vector &p_strings) { @@ -3236,7 +3343,7 @@ void TextEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("cursor_changed")); ADD_SIGNAL(MethodInfo("text_changed")); - ADD_SIGNAL(MethodInfo("request_completion",PropertyInfo(Variant::STRING,"keyword"),PropertyInfo(Variant::INT,"line"))); + ADD_SIGNAL(MethodInfo("request_completion")); } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index d70403a944c..120d5db54e6 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -185,6 +185,8 @@ class TextEdit : public Control { int completion_index; Rect2i completion_rect; int completion_line_ofs; + String completion_hint; + int completion_hint_offset; bool setting_text; @@ -261,6 +263,7 @@ class TextEdit : public Control { void _clear(); void _cancel_completion(); + void _cancel_code_hint(); void _confirm_completion(); void _update_completion_candidates(); @@ -350,7 +353,7 @@ public: void undo(); void redo(); - void clear_undo_history(); + void clear_undo_history(); void set_draw_tabs(bool p_draw); @@ -376,10 +379,13 @@ public: void set_tooltip_request_func(Object *p_obj, const StringName& p_function, const Variant& p_udata); - void set_completion(bool p_enabled,const Vector& p_prefixes); + void set_completion(bool p_enabled,const Vector& p_prefixes); void code_complete(const Vector &p_strings); + void set_code_hint(const String& p_hint); void query_code_comple(); + String get_text_for_completion(); + TextEdit(); ~TextEdit(); }; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 45a30d7bcac..d9b208d6d3a 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1731,6 +1731,26 @@ NodePath Node::get_import_path() const { #endif +static void _add_nodes_to_options(const Node *p_base,const Node *p_node,List*r_options) { + + if (p_node!=p_base && !p_node->get_owner()) + return; + String n = p_base->get_path_to(p_node); + r_options->push_back("\""+n+"\""); + for(int i=0;iget_child_count();i++) { + _add_nodes_to_options(p_base,p_node->get_child(i),r_options); + } +} + +void Node::get_argument_options(const StringName& p_function,int p_idx,List*r_options) const { + + String pf=p_function; + if ((pf=="has_node" || pf=="get_node") && p_idx==0) { + + _add_nodes_to_options(this,this,r_options); + } + Object::get_argument_options(p_function,p_idx,r_options); +} void Node::_bind_methods() { diff --git a/scene/main/node.h b/scene/main/node.h index 371a5325ca6..47f49eb625f 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -284,6 +284,7 @@ public: NodePath get_import_path() const; #endif + void get_argument_options(const StringName& p_function,int p_idx,List*r_options) const; _FORCE_INLINE_ Viewport *get_viewport() const { return data.viewport; } diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 2c278f4feda..e3427cbe2ce 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -458,6 +458,8 @@ FixedMaterial::~FixedMaterial() { } + + bool ShaderMaterial::_set(const StringName& p_name, const Variant& p_value) { if (p_name==SceneStringNames::get_singleton()->shader_shader) { @@ -558,7 +560,21 @@ void ShaderMaterial::_bind_methods() { } +void ShaderMaterial::get_argument_options(const StringName& p_function,int p_idx,List*r_options) const { + String f = p_function.operator String(); + if ((f=="get_shader_param" || f=="set_shader_param") && p_idx==0) { + + if (shader.is_valid()) { + List pl; + shader->get_param_list(&pl); + for (List::Element *E=pl.front();E;E=E->next()) { + r_options->push_back(E->get().name); + } + } + } + Material::get_argument_options(p_function,p_idx,r_options); +} ShaderMaterial::ShaderMaterial() :Material(VisualServer::get_singleton()->material_create()){ diff --git a/scene/resources/material.h b/scene/resources/material.h index 9c3feede08f..2b10078e163 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -243,6 +243,7 @@ public: void set_shader_param(const StringName& p_param,const Variant& p_value); Variant get_shader_param(const StringName& p_param) const; + void get_argument_options(const StringName& p_function,int p_idx,List*r_options) const; ShaderMaterial(); }; diff --git a/tools/editor/code_editor.cpp b/tools/editor/code_editor.cpp index 6de59f184ab..84bf7d33f4a 100644 --- a/tools/editor/code_editor.cpp +++ b/tools/editor/code_editor.cpp @@ -487,6 +487,7 @@ FindReplaceDialog::FindReplaceDialog() { vb->add_child(error_label); + set_hide_on_ok(false); } @@ -507,15 +508,19 @@ void CodeTextEditor::_text_changed() { } void CodeTextEditor::_code_complete_timer_timeout() { + if (!is_visible()) + return; if (enable_complete_timer) text_editor->query_code_comple(); } -void CodeTextEditor::_complete_request(const String& p_request, int p_line) { +void CodeTextEditor::_complete_request() { List entries; - _code_complete_script(text_editor->get_text(),p_request,p_line,&entries); + _code_complete_script(text_editor->get_text_for_completion(),&entries); // print_line("COMPLETE: "+p_request); + if (entries.size()==0) + return; Vector strs; strs.resize(entries.size()); int i=0; @@ -555,7 +560,7 @@ void CodeTextEditor::_on_settings_change() { // AUTO BRACE COMPLETION text_editor->set_auto_brace_completion( - EDITOR_DEF("text_editor/auto_brace_complete", false) + EDITOR_DEF("text_editor/auto_brace_complete", true) ); code_complete_timer->set_wait_time( @@ -632,6 +637,8 @@ CodeTextEditor::CodeTextEditor() { text_editor->connect("request_completion", this,"_complete_request"); Vector cs; cs.push_back("."); + cs.push_back(","); + cs.push_back("("); text_editor->set_completion(true,cs); idle->connect("timeout", this,"_text_changed_idle_timeout"); diff --git a/tools/editor/code_editor.h b/tools/editor/code_editor.h index 1804237f18b..f82eaf5ec5c 100644 --- a/tools/editor/code_editor.h +++ b/tools/editor/code_editor.h @@ -135,7 +135,7 @@ class CodeTextEditor : public Control { void _on_settings_change(); - void _complete_request(const String& p_request,int p_line); + void _complete_request(); protected: void set_error(const String& p_error); @@ -143,7 +143,7 @@ protected: virtual void _load_theme_settings() {} virtual void _validate_script()=0; - virtual void _code_complete_script(const String& p_code, const String& p_keyword,int p_line, List* r_options) {}; + virtual void _code_complete_script(const String& p_code, List* r_options) {}; void _text_changed_idle_timeout(); diff --git a/tools/editor/plugins/collision_polygon_editor_plugin.cpp b/tools/editor/plugins/collision_polygon_editor_plugin.cpp index 35f22aa6f88..b92acb60f95 100644 --- a/tools/editor/plugins/collision_polygon_editor_plugin.cpp +++ b/tools/editor/plugins/collision_polygon_editor_plugin.cpp @@ -120,6 +120,8 @@ void CollisionPolygonEditor::_wip_close() { bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event) { + if (!node) + return false; Transform gt = node->get_global_transform(); float depth = node->get_depth()*0.5; diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp index c5fb574c71f..159e0de7fd4 100644 --- a/tools/editor/plugins/script_editor_plugin.cpp +++ b/tools/editor/plugins/script_editor_plugin.cpp @@ -384,9 +384,35 @@ void ScriptTextEditor::_validate_script() { _update_name(); } -void ScriptTextEditor::_code_complete_script(const String& p_code, const String& p_keyword,int p_line, List* r_options) { - Error err = script->get_language()->complete_keyword(p_code,p_line,script->get_path().get_base_dir(),p_keyword,r_options); +static Node* _find_node_for_script(Node* p_base, Node*p_current, const Ref