diff --git a/.gitignore b/.gitignore index 0ced08ee..574cea29 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ __MACOSX # auto-generated sources /jsb.gen.h /jsb_project_preset.gen.cpp -/weaver-editor/templates/templates.gen.h +/weaver-editor/templates/templates.ts.gen.h +/weaver-editor/templates/templates.js.gen.h .vscode diff --git a/bridge/jsb_class_info.cpp b/bridge/jsb_class_info.cpp index 4a67de16..ae39805e 100644 --- a/bridge/jsb_class_info.cpp +++ b/bridge/jsb_class_info.cpp @@ -238,7 +238,7 @@ namespace jsb void ScriptClassInfo::instantiate(const StringName& p_module_id, const v8::Local& p_self) { const String source_path = internal::PathUtil::convert_javascript_path(p_module_id); - const Ref script = ResourceLoader::load(source_path); + const Ref script = ResourceLoader::load(source_path); if (script.is_valid()) { jsb_unused(script->can_instantiate()); // make it loaded immediately diff --git a/internal/jsb_string_names.h b/internal/jsb_string_names.h index bb0bf703..ec3c03ec 100644 --- a/internal/jsb_string_names.h +++ b/internal/jsb_string_names.h @@ -6,22 +6,24 @@ #define jsb_string_name(name) ::jsb::internal::StringNames::get_singleton().sn_##name #define jsb_literal(name) (sizeof(::jsb::internal::StringNames::sn_##name) == sizeof(StringName), #name) -class GodotJSScriptLanguage; +class GodotJSScriptLanguageBase; namespace jsb::internal { class StringNames { private: - friend class ::GodotJSScriptLanguage; + friend class ::GodotJSScriptLanguageBase; static StringNames* singleton_; static void create() { singleton_ = memnew(StringNames); } static void free() { - memdelete(singleton_); - singleton_ = nullptr; + if (singleton_) { + memdelete(singleton_); + singleton_ = nullptr; + } } // we need to ignore some names used in godot (such as XXX.name) to avoid conflicts in javascript. diff --git a/register_types.cpp b/register_types.cpp index 17b55ad4..2b6069a9 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -1,6 +1,8 @@ #include "register_types.h" #include "weaver/jsb_script_language.h" +#include "weaver/jsb_script_language_ts.h" +#include "weaver/jsb_script_language_js.h" #include "weaver/jsb_script.h" #include "weaver/jsb_resource_loader.h" #include "weaver/jsb_resource_saver.h" @@ -12,35 +14,39 @@ #include "weaver-editor/jsb_export_plugin.h" #endif -static Ref resource_loader_js; -static Ref resource_saver_js; +static Ref resource_loader; +static Ref resource_saver; void jsb_initialize_module(ModuleInitializationLevel p_level) { if (p_level == MODULE_INITIALIZATION_LEVEL_SERVERS) { GDREGISTER_CLASS(GodotJSScript); + GDREGISTER_CLASS(GodotJavaScript); jsb::impl::GlobalInitialize::init(); // register javascript language - GodotJSScriptLanguage* script_language_js = memnew(GodotJSScriptLanguage()); - ScriptServer::register_language(script_language_js); + ScriptServer::register_language(GodotJSScriptLanguage::create_singleton()); + ScriptServer::register_language(GodotJavascriptLanguage::create_singleton()); - resource_loader_js.instantiate(); - ResourceLoader::add_resource_format_loader(resource_loader_js); + resource_loader.instantiate(); + resource_loader->register_resource_extension(JSB_TYPESCRIPT_EXT, GodotJSScriptLanguage::get_singleton()); + resource_loader->register_resource_extension(JSB_JAVASCRIPT_EXT, GodotJavascriptLanguage::get_singleton()); + ResourceLoader::add_resource_format_loader(resource_loader); + + resource_saver.instantiate(); + ResourceSaver::add_resource_format_saver(resource_saver); - resource_saver_js.instantiate(); - ResourceSaver::add_resource_format_saver(resource_saver_js); #ifdef TOOLS_ENABLED EditorNode::add_init_callback([] { - GodotJSEditorPlugin* plugin = memnew(GodotJSEditorPlugin); + GodotJSEditorPlugin* plugin = memnew(GodotJSEditorPlugin(GodotJSScriptLanguage::get_singleton())); EditorNode::add_editor_plugin(plugin); Ref exporter; - exporter.instantiate(); + exporter.instantiate(GodotJSScriptLanguage::get_singleton()->get_environment()); EditorExport::get_singleton()->add_export_plugin(exporter); plugin->set_name(jsb_typename(GodotJSEditorPlugin)); @@ -53,15 +59,17 @@ void jsb_uninitialize_module(ModuleInitializationLevel p_level) { if (p_level == MODULE_INITIALIZATION_LEVEL_CORE) { - ResourceLoader::remove_resource_format_loader(resource_loader_js); - resource_loader_js.unref(); + ResourceLoader::remove_resource_format_loader(resource_loader); + resource_loader.unref(); + + ResourceSaver::remove_resource_format_saver(resource_saver); + resource_saver.unref(); + + ScriptServer::unregister_language(GodotJSScriptLanguage::get_singleton()); + GodotJSScriptLanguage::destroy_singleton(); - ResourceSaver::remove_resource_format_saver(resource_saver_js); - resource_saver_js.unref(); + ScriptServer::unregister_language(GodotJavascriptLanguage::get_singleton()); + GodotJavascriptLanguage::destroy_singleton(); - GodotJSScriptLanguage *script_language_js = GodotJSScriptLanguage::get_singleton(); - jsb_check(script_language_js); - ScriptServer::unregister_language(script_language_js); - memdelete(script_language_js); } } diff --git a/weaver-editor/icons/GodotJavaScript.svg b/weaver-editor/icons/GodotJavaScript.svg new file mode 100644 index 00000000..a00f06f6 --- /dev/null +++ b/weaver-editor/icons/GodotJavaScript.svg @@ -0,0 +1,52 @@ + + + + + + + file_type_typescript_official + + + diff --git a/weaver-editor/jsb_editor_pch.h b/weaver-editor/jsb_editor_pch.h index 3298b73b..7e0f9a32 100644 --- a/weaver-editor/jsb_editor_pch.h +++ b/weaver-editor/jsb_editor_pch.h @@ -14,6 +14,7 @@ #include "../internal/jsb_settings.h" #include "../weaver/jsb_script_language.h" +#include "../weaver/jsb_script_language_ts.h" #if GODOT_4_3_OR_NEWER # include "editor/plugins/editor_plugin.h" diff --git a/weaver-editor/jsb_editor_plugin.cpp b/weaver-editor/jsb_editor_plugin.cpp index bf1ccde8..cf693800 100644 --- a/weaver-editor/jsb_editor_plugin.cpp +++ b/weaver-editor/jsb_editor_plugin.cpp @@ -41,7 +41,7 @@ void GodotJSEditorPlugin::_notification(int p_what) switch (p_what) { case NOTIFICATION_APPLICATION_FOCUS_IN: - if (GodotJSScriptLanguage* lang = GodotJSScriptLanguage::get_singleton()) + if (GodotJSScriptLanguage* lang = lang_) { lang->scan_external_changes(); } @@ -61,7 +61,7 @@ void GodotJSEditorPlugin::_on_menu_pressed(int p_what) } } -GodotJSEditorPlugin::GodotJSEditorPlugin() +GodotJSEditorPlugin::GodotJSEditorPlugin(GodotJSScriptLanguage * lang) : lang_(lang) { // jsb::internal::Settings::on_editor_init(); PopupMenu *menu = memnew(PopupMenu); @@ -369,7 +369,7 @@ void GodotJSEditorPlugin::generate_godot_dts() install_files(filter_files(editor_plugin->install_files_, jsb::weaver::CH_D_TS)); } - GodotJSScriptLanguage* lang = GodotJSScriptLanguage::get_singleton(); + GodotJSScriptLanguage * lang = get_singleton()->lang_; jsb_check(lang); Error err; const String code = jsb_format(R"--((function(){const mod = require("jsb.editor.codegen"); (new mod.default("%s")).emit();})())--", "./" JSB_TYPE_ROOT); @@ -382,7 +382,7 @@ void GodotJSEditorPlugin::generate_godot_dts() void GodotJSEditorPlugin::load_editor_entry_module() { - GodotJSScriptLanguage* lang = GodotJSScriptLanguage::get_singleton(); + GodotJSScriptLanguage * lang = get_singleton()->lang_; jsb_check(lang); const Error err = lang->get_environment()->load("jsb.editor.main"); ERR_FAIL_COND_MSG(err != OK, "failed to evaluate jsb.editor.main"); @@ -458,7 +458,7 @@ GodotJSEditorPlugin* GodotJSEditorPlugin::get_singleton() void GodotJSEditorPlugin::ensure_tsc_installed() { - GodotJSScriptLanguage* lang = GodotJSScriptLanguage::get_singleton(); + GodotJSScriptLanguage * lang = get_singleton()->lang_; jsb_check(lang); Error err; diff --git a/weaver-editor/jsb_editor_plugin.h b/weaver-editor/jsb_editor_plugin.h index 9d389f2e..4b92f930 100644 --- a/weaver-editor/jsb_editor_plugin.h +++ b/weaver-editor/jsb_editor_plugin.h @@ -53,6 +53,8 @@ class GodotJSEditorPlugin : public EditorPlugin std::shared_ptr tsc_; + GodotJSScriptLanguage * lang_; + protected: static void _bind_methods(); @@ -70,9 +72,11 @@ class GodotJSEditorPlugin : public EditorPlugin static bool delete_file(const String& p_file); public: - GodotJSEditorPlugin(); + GodotJSEditorPlugin(GodotJSScriptLanguage * lang_); virtual ~GodotJSEditorPlugin() override; + GodotJSScriptLanguage * get_lang() const { return lang_; } + void start_tsc_watch(); bool is_tsc_watching(); void kill_tsc(); diff --git a/weaver-editor/jsb_export_plugin.cpp b/weaver-editor/jsb_export_plugin.cpp index b98cd479..1495e23a 100644 --- a/weaver-editor/jsb_export_plugin.cpp +++ b/weaver-editor/jsb_export_plugin.cpp @@ -2,14 +2,13 @@ #define JSB_EXPORTER_LOG(Severity, Format, ...) JSB_LOG_IMPL(JSExporter, Severity, Format, ##__VA_ARGS__) -GodotJSExportPlugin::GodotJSExportPlugin() : super() +GodotJSExportPlugin::GodotJSExportPlugin(std::shared_ptr & env) : super(), env_(env) { // explicitly ignored files (not used by runtime) ignored_paths_.insert("res://jsconfig.json"); ignored_paths_.insert("res://tsconfig.json"); ignored_paths_.insert("res://package.json"); ignored_paths_.insert("res://package-lock.json"); - env_ = GodotJSScriptLanguage::get_singleton()->get_environment(); jsb_check(env_); } diff --git a/weaver-editor/jsb_export_plugin.h b/weaver-editor/jsb_export_plugin.h index c9c9f93c..e0baf787 100644 --- a/weaver-editor/jsb_export_plugin.h +++ b/weaver-editor/jsb_export_plugin.h @@ -15,7 +15,7 @@ class GodotJSExportPlugin: public EditorExportPlugin GDCLASS(GodotJSExportPlugin, EditorExportPlugin) public: - GodotJSExportPlugin(); + GodotJSExportPlugin(std::shared_ptr & env); virtual String get_name() const override; virtual bool supports_platform(const Ref& p_export_platform) const override; diff --git a/weaver-editor/jsb_repl.cpp b/weaver-editor/jsb_repl.cpp index 47c464df..bbbaf089 100644 --- a/weaver-editor/jsb_repl.cpp +++ b/weaver-editor/jsb_repl.cpp @@ -174,7 +174,7 @@ void GodotJSREPL::check_install() void GodotJSREPL::_gc_pressed() { - GodotJSScriptLanguage* lang = GodotJSScriptLanguage::get_singleton(); + GodotJSScriptLanguage* lang = GodotJSEditorPlugin::get_singleton()->get_lang(); jsb_check(lang); lang->get_environment()->gc(); add_line("Explicit GC requested"); @@ -301,7 +301,7 @@ void GodotJSREPL::_input_submitted(const String &p_text) jsb::JSValueMove GodotJSREPL::eval_source(const String& p_code) { - GodotJSScriptLanguage* lang = GodotJSScriptLanguage::get_singleton(); + GodotJSScriptLanguage* lang = GodotJSEditorPlugin::get_singleton()->get_lang(); jsb_check(lang); Error err; return lang->eval_source(p_code, err); diff --git a/weaver-editor/jsb_statistics_viewer.cpp b/weaver-editor/jsb_statistics_viewer.cpp index 501bdaec..211bbf41 100644 --- a/weaver-editor/jsb_statistics_viewer.cpp +++ b/weaver-editor/jsb_statistics_viewer.cpp @@ -1,5 +1,6 @@ #include "jsb_statistics_viewer.h" #include "jsb_editor_pch.h" +#include "jsb_editor_plugin.h" GodotJSStatisticsViewer::GodotJSStatisticsViewer() { @@ -41,7 +42,7 @@ void GodotJSStatisticsViewer::activate(bool p_active) void GodotJSStatisticsViewer::on_timer() { - const GodotJSScriptLanguage* lang = GodotJSScriptLanguage::get_singleton(); + const GodotJSScriptLanguage* lang = GodotJSEditorPlugin::get_singleton()->get_lang(); if (!lang) return; const std::shared_ptr env = lang->get_environment(); if (!env) return; diff --git a/weaver-editor/templates/SCsub b/weaver-editor/templates/SCsub index 597b88f8..3ade3334 100644 --- a/weaver-editor/templates/SCsub +++ b/weaver-editor/templates/SCsub @@ -42,11 +42,10 @@ else: src_suffix = ".ts", ) -language = "ts" if env["use_typescript"] else "js" -print("generate templates for", language) - # Template files # the suffix should be `.ts`, but `editor/template_builders.py` hardcoded the delimiter with `.cs` -templates_sources = Glob(f"*/*.{language}.cs") +templates_ts_sources = Glob(f"*/*.ts.cs") +env.Alias("editor_template_ts", [env.MakeGodotJSTemplateBuilder("templates.ts.gen.h", templates_ts_sources)]) -env.Alias("editor_template_ts", [env.MakeGodotJSTemplateBuilder("templates.gen.h", templates_sources)]) +templates_js_sources = Glob(f"*/*.js.cs") +env.Alias("editor_template_js", [env.MakeGodotJSTemplateBuilder("templates.js.gen.h", templates_js_sources)]) diff --git a/weaver/jsb_resource_loader.cpp b/weaver/jsb_resource_loader.cpp index 56fd584d..a2fef4f1 100644 --- a/weaver/jsb_resource_loader.cpp +++ b/weaver/jsb_resource_loader.cpp @@ -3,14 +3,33 @@ #include "jsb_script_language.h" #include "jsb_script.h" + +void ResourceFormatLoaderGodotJSScript::register_resource_extension(String ext, GodotJSScriptLanguageBase * lang) +{ + lang_map_.insert(ext, lang); +} + Ref ResourceFormatLoaderGodotJSScript::load(const String& p_path, const String& p_original_path, Error* r_error, bool p_use_sub_threads, float* r_progress, CacheMode p_cache_mode) { JSB_BENCHMARK_SCOPE(ResourceFormatLoaderGodotJSScript, load); + GodotJSScriptLanguageBase * lang = nullptr; + for (auto const & i: lang_map_) { + if (p_path.ends_with(i.key)) { + lang = i.value; + break; + } + } + + if (!lang) { + if (r_error) *r_error = ERR_FILE_UNRECOGNIZED; + return {}; + } + { //TODO a dirty but approaching solution for hot-reloading - MutexLock lock(GodotJSScriptLanguage::singleton_->mutex_); - SelfList *elem = GodotJSScriptLanguage::singleton_->script_list_.first(); + MutexLock lock(lang->mutex_); + SelfList *elem = lang->script_list_.first(); while (elem) { if (elem->self()->get_path() == p_path) @@ -33,7 +52,6 @@ Ref ResourceFormatLoaderGodotJSScript::load(const String& p_path, cons return {}; } #endif - jsb_check(p_path.ends_with(JSB_TYPESCRIPT_EXT) || p_path.ends_with(JSB_JAVASCRIPT_EXT)); // in case `node_modules` is not ignored (which is not expected though), we do not want any GodotJSScript to be generated from it. if (p_path.begins_with("res://node_modules")) @@ -41,6 +59,7 @@ Ref ResourceFormatLoaderGodotJSScript::load(const String& p_path, cons if (r_error) *r_error = ERR_CANT_RESOLVE; return {}; } + // exclude this extension if (p_path.ends_with("." JSB_DTS_EXT)) { if (r_error) *r_error = ERR_FILE_UNRECOGNIZED; @@ -49,8 +68,7 @@ Ref ResourceFormatLoaderGodotJSScript::load(const String& p_path, cons JSB_LOG(VeryVerbose, "loading script resource %s on thread %s", p_path, uitos(Thread::get_caller_id())); // return a skeleton script which only contains path and source code without actually loaded in `realm` since `load` may called from background threads - Ref spt; - spt.instantiate(); + Ref spt = lang->create_godotjsscript(); spt->attach_source(p_path); if (r_error) *r_error = OK; return spt; @@ -58,19 +76,30 @@ Ref ResourceFormatLoaderGodotJSScript::load(const String& p_path, cons void ResourceFormatLoaderGodotJSScript::get_recognized_extensions(List* p_extensions) const { - p_extensions->push_back(JSB_TYPESCRIPT_EXT); - p_extensions->push_back(JSB_JAVASCRIPT_EXT); + for (auto const & i: lang_map_) { + p_extensions->push_back(i.key); + } } bool ResourceFormatLoaderGodotJSScript::handles_type(const String& p_type) const { - return (p_type == "Script" || p_type == jsb_typename(GodotJSScript)); + if (p_type == "Script") + return true; + for (auto const & i: lang_map_) { + if (p_type == i.value->get_type()) + return true; + } + return false; } String ResourceFormatLoaderGodotJSScript::get_resource_type(const String& p_path) const { const String el = p_path.get_extension().to_lower(); - return (el == JSB_TYPESCRIPT_EXT || el == JSB_JAVASCRIPT_EXT) ? jsb_typename(GodotJSScript) : ""; + for (auto const & i: lang_map_) { + if (el == i.key) + return i.value->get_type(); + } + return ""; } void ResourceFormatLoaderGodotJSScript::get_dependencies(const String& p_path, List* p_dependencies, bool p_add_types) diff --git a/weaver/jsb_resource_loader.h b/weaver/jsb_resource_loader.h index b695900d..e43bbda6 100644 --- a/weaver/jsb_resource_loader.h +++ b/weaver/jsb_resource_loader.h @@ -3,9 +3,21 @@ #include "core/io/resource_loader.h" +#include "jsb_script_language.h" + class ResourceFormatLoaderGodotJSScript : public ResourceFormatLoader { + + ResourceFormatLoaderGodotJSScript(ResourceFormatLoaderGodotJSScript const &) = delete; + ResourceFormatLoaderGodotJSScript & operator=(ResourceFormatLoaderGodotJSScript const &) = delete; + + HashMap lang_map_; + public: + ResourceFormatLoaderGodotJSScript() { } + + void register_resource_extension(String ext, GodotJSScriptLanguageBase * lang); + virtual Ref load(const String& p_path, const String& p_original_path = "", Error* r_error = nullptr, bool p_use_sub_threads = false, float* r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override; virtual void get_recognized_extensions(List* p_extensions) const override; virtual bool handles_type(const String& p_type) const override; diff --git a/weaver/jsb_resource_saver.cpp b/weaver/jsb_resource_saver.cpp index 54dc5666..48acab88 100644 --- a/weaver/jsb_resource_saver.cpp +++ b/weaver/jsb_resource_saver.cpp @@ -6,14 +6,14 @@ // @seealso: gdscript.cpp ResourceFormatSaverGDScript::save Error ResourceFormatSaverGodotJSScript::save(const Ref& p_resource, const String& p_path, uint32_t p_flags) { - const Ref sqscr = p_resource; + const Ref sqscr = p_resource; ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER); Error err; const Ref file = FileAccess::open(p_path, FileAccess::WRITE, &err); if (err) { - JSB_LOG(Error, "Cannot save %s file '%s'.", jsb_typename(GodotJSScript), p_path); + JSB_LOG(Error, "Cannot save %s file '%s'.", jsb_typename(GodotJSScriptBase), p_path); return err; } file->store_string(sqscr->get_source_code()); @@ -25,7 +25,7 @@ Error ResourceFormatSaverGodotJSScript::save(const Ref& p_resource, co if (ScriptServer::is_reload_scripts_on_save_enabled()) { // WTF?? - GodotJSScriptLanguage::get_singleton()->reload_tool_script(p_resource, true); + sqscr->get_language()->reload_tool_script(p_resource, true); } return OK; @@ -33,7 +33,7 @@ Error ResourceFormatSaverGodotJSScript::save(const Ref& p_resource, co void ResourceFormatSaverGodotJSScript::get_recognized_extensions(const Ref& p_resource, List* p_extensions) const { - if (Object::cast_to(*p_resource)) + if (Object::cast_to(*p_resource)) { p_extensions->push_back(JSB_TYPESCRIPT_EXT); p_extensions->push_back(JSB_JAVASCRIPT_EXT); @@ -42,5 +42,5 @@ void ResourceFormatSaverGodotJSScript::get_recognized_extensions(const Ref& p_resource) const { - return Object::cast_to(*p_resource) != nullptr; + return Object::cast_to(*p_resource) != nullptr; } diff --git a/weaver/jsb_script.cpp b/weaver/jsb_script.cpp index 87affdef..9638298b 100644 --- a/weaver/jsb_script.cpp +++ b/weaver/jsb_script.cpp @@ -1,5 +1,7 @@ #include "jsb_script.h" #include "jsb_script_language.h" +#include "jsb_script_language_ts.h" +#include "jsb_script_language_js.h" #include "jsb_script_instance.h" #include "../internal/jsb_path_util.h" @@ -10,34 +12,32 @@ #include "editor/editor_help.h" #endif -GodotJSScript::GodotJSScript(): script_list_(this) +GodotJSScriptBase::GodotJSScriptBase(GodotJSScriptLanguageBase * lang): script_list_(this), lang_(lang) { - { - JSB_BENCHMARK_SCOPE(GodotJSScript, Construct); - GodotJSScriptLanguage* lang = GodotJSScriptLanguage::get_singleton(); - MutexLock lock(lang->mutex_); + if (lang_) { + JSB_BENCHMARK_SCOPE(GodotJSScriptBase, Construct); + MutexLock lock(lang_->mutex_); lang->script_list_.add(&script_list_); } - JSB_LOG(VeryVerbose, "new GodotJSScript addr:%d", (uintptr_t) this); + JSB_LOG(VeryVerbose, "new GodotJSScriptBase addr:%d", (uintptr_t) this); } -GodotJSScript::~GodotJSScript() +GodotJSScriptBase::~GodotJSScriptBase() { - JSB_LOG(VeryVerbose, "delete GodotJSScript addr:%d", (uintptr_t) this); + JSB_LOG(VeryVerbose, "delete GodotJSScriptBase addr:%d", (uintptr_t) this); release_cached_methods(); - { - JSB_BENCHMARK_SCOPE(GodotJSScript, Destruct); - const GodotJSScriptLanguage* lang = GodotJSScriptLanguage::get_singleton(); - MutexLock lock(lang->mutex_); + if (lang_) { + JSB_BENCHMARK_SCOPE(GodotJSScriptBase, Destruct); + MutexLock lock(lang_->mutex_); script_list_.remove_from_list(); } } // GDScript::can_instantiate() -bool GodotJSScript::can_instantiate() const +bool GodotJSScriptBase::can_instantiate() const { ensure_module_loaded(); #ifdef TOOLS_ENABLED @@ -47,7 +47,7 @@ bool GodotJSScript::can_instantiate() const #endif } -bool GodotJSScript::is_tool() const +bool GodotJSScriptBase::is_tool() const { if (valid_) { @@ -57,12 +57,12 @@ bool GodotJSScript::is_tool() const return false; } -void GodotJSScript::set_source_code(const String& p_code) +void GodotJSScriptBase::set_source_code(const String& p_code) { source_ = p_code; } -Ref