diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index bf5b717c19dc..cc21a18aece9 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -365,6 +365,14 @@ void DocTools::remove_doc(const String &p_class_name) { class_list.erase(p_class_name); } +void DocTools::remove_script_doc_by_path(const String &p_path) { + for (KeyValue &E : class_list) + if (E.value.is_script_doc && E.value.script_path == p_path) { + remove_doc(E.key); + return; + } +} + bool DocTools::has_doc(const String &p_class_name) { if (p_class_name.is_empty()) { return false; diff --git a/editor/doc_tools.h b/editor/doc_tools.h index 3be59bf233cf..43d302c873c7 100644 --- a/editor/doc_tools.h +++ b/editor/doc_tools.h @@ -45,6 +45,7 @@ class DocTools { void merge_from(const DocTools &p_data); void add_doc(const DocData::ClassDoc &p_class_doc); void remove_doc(const String &p_class_name); + void remove_script_doc_by_path(const String &p_path); bool has_doc(const String &p_class_name); enum GenerateFlags { GENERATE_FLAG_SKIP_BASIC_TYPES = (1 << 0), diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index f75e438582c8..1652131b4933 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1864,6 +1864,11 @@ void EditorFileSystem::_update_script_documentation() { if (!efd || index < 0) { // The file was removed + DocTools *doc = EditorHelp::get_doc_data(); + if (doc) + doc->remove_script_doc_by_path(path); + else + ERR_PRINT("Cannot update documentation for deleted file: EditorHelp::DocTools not initialized."); continue; } diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 683e4e5cdaac..fa6d33b75ad6 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -36,6 +36,7 @@ #include "core/input/input.h" #include "core/object/script_language.h" #include "core/os/keyboard.h" +#include "core/os/time.h" #include "core/string/string_builder.h" #include "core/version_generated.gen.h" #include "editor/doc_data_compressed.gen.h" @@ -2859,6 +2860,10 @@ String EditorHelp::get_cache_full_path() { return EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("editor_doc_cache-%d.%d.res", VERSION_MAJOR, VERSION_MINOR)); } +String EditorHelp::get_script_doc_cache_full_path() { + return EditorPaths::get_singleton()->get_project_data_dir().path_join("editor_script_doc_cache.res"); +} + void EditorHelp::load_xml_buffer(const uint8_t *p_buffer, int p_size) { if (!ext_doc) { ext_doc = memnew(DocTools); @@ -2889,6 +2894,7 @@ void EditorHelp::_load_doc_thread(void *p_udata) { doc->add_doc(DocData::ClassDoc::from_dict(classes[i])); } + _load_script_doc_cache(); // Extensions' docs are not cached. Generate them now (on the main thread). callable_mp_static(&EditorHelp::_gen_extensions_docs).call_deferred(); } else { @@ -2923,6 +2929,9 @@ void EditorHelp::_gen_doc_thread(void *p_udata) { ERR_PRINT("Cannot save editor help cache (" + get_cache_full_path() + ")."); } + // Load script docs after native ones are cached so native cache doesn't contain script docs. + _load_script_doc_cache(); + OS::get_singleton()->benchmark_end_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count)); } @@ -2935,6 +2944,36 @@ void EditorHelp::_gen_extensions_docs() { } } +// Called in 1) _load_doc_thread() from worker thread after other caches have been read, +// or 2) in _gen_doc_thread() **only from main thread**. This avoids worker thread loading +// script cache and main thread generating native cache accessing DocTools simultaneously. +void EditorHelp::_load_script_doc_cache() { + Ref script_doc_cache_res = ResourceLoader::load(get_script_doc_cache_full_path()); + if (script_doc_cache_res.is_valid()) { + Array classes = script_doc_cache_res->get_meta("classes", Array()); + for (int i = 0; i < classes.size(); i++) { + DocData::ClassDoc cd = DocData::ClassDoc::from_dict(classes[i]); + if (FileAccess::create(FileAccess::ACCESS_RESOURCES)->file_exists(cd.script_path)) + doc->add_doc(cd); + } + } +} + +void EditorHelp::save_script_doc_cache() { + Ref cache_res; + cache_res.instantiate(); + Array classes; + for (const KeyValue &E : doc->class_list) + if (E.value.is_script_doc) + classes.push_back(DocData::ClassDoc::to_dict(E.value)); + + cache_res->set_meta("classes", classes); + Error err = ResourceSaver::save(cache_res, get_script_doc_cache_full_path(), ResourceSaver::FLAG_COMPRESS); + if (err) { + ERR_PRINT("Cannot save editor help script documentation cache (" + get_script_doc_cache_full_path() + ")."); + } +} + void EditorHelp::generate_doc(bool p_use_cache) { doc_generation_count++; OS::get_singleton()->benchmark_begin_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count)); diff --git a/editor/editor_help.h b/editor/editor_help.h index 93f74cb2c123..d3da2421f2ed 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -199,6 +199,7 @@ class EditorHelp : public VBoxContainer { static void _load_doc_thread(void *p_udata); static void _gen_doc_thread(void *p_udata); static void _gen_extensions_docs(); + static void _load_script_doc_cache(); static void _compute_doc_version_hash(); struct PropertyCompare { @@ -221,7 +222,9 @@ class EditorHelp : public VBoxContainer { static void generate_doc(bool p_use_cache = true); static DocTools *get_doc_data(); static void cleanup_doc(); + static void save_script_doc_cache(); static String get_cache_full_path(); + static String get_script_doc_cache_full_path(); static void load_xml_buffer(const uint8_t *p_buffer, int p_size); static void remove_class(const String &p_class); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index d1dffba2ab56..9e9a413c2a0e 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -813,6 +813,11 @@ void EditorNode::_notification(int p_what) { } #endif } break; + + case NOTIFICATION_PREDELETE: { + EditorHelp::save_script_doc_cache(); + } break; + } }