Explorar o código

Merge pull request #83747 from Riteo/gdext-doc

GDExtension: Add an interface for loading extra documentation
Rémi Verschelde hai 1 ano
pai
achega
07d290e67e

+ 15 - 0
core/extension/gdextension.cpp

@@ -653,6 +653,8 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
 	if (!ext->is_reloading) {
 		self->extension_classes.erase(class_name);
 	}
+
+	GDExtensionEditorHelp::remove_class(class_name);
 #else
 	self->extension_classes.erase(class_name);
 #endif
@@ -1196,4 +1198,17 @@ void GDExtensionEditorPlugins::remove_extension_class(const StringName &p_class_
 		extension_classes.erase(p_class_name);
 	}
 }
+
+GDExtensionEditorHelp::EditorHelpLoadXmlBufferFunc GDExtensionEditorHelp::editor_help_load_xml_buffer = nullptr;
+GDExtensionEditorHelp::EditorHelpRemoveClassFunc GDExtensionEditorHelp::editor_help_remove_class = nullptr;
+
+void GDExtensionEditorHelp::load_xml_buffer(const uint8_t *p_buffer, int p_size) {
+	ERR_FAIL_NULL(editor_help_load_xml_buffer);
+	editor_help_load_xml_buffer(p_buffer, p_size);
+}
+
+void GDExtensionEditorHelp::remove_class(const String &p_class) {
+	ERR_FAIL_NULL(editor_help_remove_class);
+	editor_help_remove_class(p_class);
+}
 #endif // TOOLS_ENABLED

+ 20 - 0
core/extension/gdextension.h

@@ -197,6 +197,26 @@ public:
 		return extension_classes;
 	}
 };
+
+class GDExtensionEditorHelp {
+protected:
+	friend class EditorHelp;
+
+	// Similarly to EditorNode above, we need to be able to ask EditorHelp to parse
+	// new documentation data. Note though that, differently from EditorHelp, this
+	// is initialized even _before_ it gets instantiated, as we need to rely on
+	// this method while initializing the engine.
+	typedef void (*EditorHelpLoadXmlBufferFunc)(const uint8_t *p_buffer, int p_size);
+	static EditorHelpLoadXmlBufferFunc editor_help_load_xml_buffer;
+
+	typedef void (*EditorHelpRemoveClassFunc)(const String &p_class);
+	static EditorHelpRemoveClassFunc editor_help_remove_class;
+
+public:
+	static void load_xml_buffer(const uint8_t *p_buffer, int p_size);
+	static void remove_class(const String &p_class);
+};
+
 #endif // TOOLS_ENABLED
 
 #endif // GDEXTENSION_H

+ 17 - 0
core/extension/gdextension_interface.cpp

@@ -42,6 +42,8 @@
 #include "core/variant/variant.h"
 #include "core/version.h"
 
+#include <string.h>
+
 class CallableCustomExtension : public CallableCustom {
 	void *userdata;
 	void *token;
@@ -1373,6 +1375,19 @@ static void gdextension_editor_remove_plugin(GDExtensionConstStringNamePtr p_cla
 #endif
 }
 
+static void gdextension_editor_help_load_xml_from_utf8_chars_and_len(const char *p_data, GDExtensionInt p_size) {
+#ifdef TOOLS_ENABLED
+	GDExtensionEditorHelp::load_xml_buffer((const uint8_t *)p_data, p_size);
+#endif
+}
+
+static void gdextension_editor_help_load_xml_from_utf8_chars(const char *p_data) {
+#ifdef TOOLS_ENABLED
+	size_t len = strlen(p_data);
+	gdextension_editor_help_load_xml_from_utf8_chars_and_len(p_data, len);
+#endif
+}
+
 #define REGISTER_INTERFACE_FUNC(m_name) GDExtension::register_interface_function(#m_name, (GDExtensionInterfaceFunctionPtr)&gdextension_##m_name)
 
 void gdextension_setup_interface() {
@@ -1516,6 +1531,8 @@ void gdextension_setup_interface() {
 	REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
 	REGISTER_INTERFACE_FUNC(editor_add_plugin);
 	REGISTER_INTERFACE_FUNC(editor_remove_plugin);
+	REGISTER_INTERFACE_FUNC(editor_help_load_xml_from_utf8_chars);
+	REGISTER_INTERFACE_FUNC(editor_help_load_xml_from_utf8_chars_and_len);
 }
 
 #undef REGISTER_INTERFACE_FUNCTION

+ 25 - 0
core/extension/gdextension_interface.h

@@ -2617,6 +2617,31 @@ typedef void (*GDExtensionInterfaceEditorAddPlugin)(GDExtensionConstStringNamePt
  */
 typedef void (*GDExtensionInterfaceEditorRemovePlugin)(GDExtensionConstStringNamePtr p_class_name);
 
+/**
+ * @name editor_help_load_xml_from_utf8_chars
+ * @since 4.3
+ *
+ * Loads new XML-formatted documentation data in the editor.
+ *
+ * The provided pointer can be immediately freed once the function returns.
+ *
+ * @param p_data A pointer to an UTF-8 encoded C string (null terminated).
+ */
+typedef void (*GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars)(const char *p_data);
+
+/**
+ * @name editor_help_load_xml_from_utf8_chars_and_len
+ * @since 4.3
+ *
+ * Loads new XML-formatted documentation data in the editor.
+ *
+ * The provided pointer can be immediately freed once the function returns.
+ *
+ * @param p_data A pointer to an UTF-8 encoded C string.
+ * @param p_size The number of bytes (not code units).
+ */
+typedef void (*GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen)(const char *p_data, GDExtensionInt p_size);
+
 #ifdef __cplusplus
 }
 #endif

+ 12 - 0
editor/doc_tools.cpp

@@ -1652,3 +1652,15 @@ Error DocTools::load_compressed(const uint8_t *p_data, int p_compressed_size, in
 
 	return OK;
 }
+
+Error DocTools::load_xml(const uint8_t *p_data, int p_size) {
+	Ref<XMLParser> parser = memnew(XMLParser);
+	Error err = parser->_open_buffer(p_data, p_size);
+	if (err) {
+		return err;
+	}
+
+	_load(parser);
+
+	return OK;
+}

+ 1 - 0
editor/doc_tools.h

@@ -56,6 +56,7 @@ public:
 
 	Error _load(Ref<XMLParser> parser);
 	Error load_compressed(const uint8_t *p_data, int p_compressed_size, int p_uncompressed_size);
+	Error load_xml(const uint8_t *p_data, int p_size);
 };
 
 #endif // DOC_TOOLS_H

+ 34 - 0
editor/editor_help.cpp

@@ -31,6 +31,7 @@
 #include "editor_help.h"
 
 #include "core/core_constants.h"
+#include "core/extension/gdextension.h"
 #include "core/input/input.h"
 #include "core/object/script_language.h"
 #include "core/os/keyboard.h"
@@ -83,6 +84,7 @@ const Vector<String> classes_with_csharp_differences = {
 // TODO: this is sometimes used directly as doc->something, other times as EditorHelp::get_doc_data(), which is thread-safe.
 // Might this be a problem?
 DocTools *EditorHelp::doc = nullptr;
+DocTools *EditorHelp::ext_doc = nullptr;
 
 static bool _attempt_doc_load(const String &p_class) {
 	// Docgen always happens in the outer-most class: it also generates docs for inner classes.
@@ -2369,6 +2371,28 @@ String EditorHelp::get_cache_full_path() {
 	return EditorPaths::get_singleton()->get_cache_dir().path_join("editor_doc_cache.res");
 }
 
+void EditorHelp::load_xml_buffer(const uint8_t *p_buffer, int p_size) {
+	if (!ext_doc) {
+		ext_doc = memnew(DocTools);
+	}
+
+	ext_doc->load_xml(p_buffer, p_size);
+
+	if (doc) {
+		doc->load_xml(p_buffer, p_size);
+	}
+}
+
+void EditorHelp::remove_class(const String &p_class) {
+	if (ext_doc && ext_doc->has_doc(p_class)) {
+		ext_doc->remove_doc(p_class);
+	}
+
+	if (doc && doc->has_doc(p_class)) {
+		doc->remove_doc(p_class);
+	}
+}
+
 void EditorHelp::_load_doc_thread(void *p_udata) {
 	Ref<Resource> cache_res = ResourceLoader::load(get_cache_full_path());
 	if (cache_res.is_valid() && cache_res->get_meta("version_hash", "") == doc_version_hash) {
@@ -2416,6 +2440,11 @@ void EditorHelp::_gen_doc_thread(void *p_udata) {
 
 void EditorHelp::_gen_extensions_docs() {
 	doc->generate((DocTools::GENERATE_FLAG_SKIP_BASIC_TYPES | DocTools::GENERATE_FLAG_EXTENSION_CLASSES_ONLY));
+
+	// Append extra doc data, as it gets overridden by the generation step.
+	if (ext_doc) {
+		doc->merge_from(*ext_doc);
+	}
 }
 
 void EditorHelp::generate_doc(bool p_use_cache) {
@@ -2554,6 +2583,11 @@ void EditorHelp::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("go_to_help"));
 }
 
+void EditorHelp::init_gdext_pointers() {
+	GDExtensionEditorHelp::editor_help_load_xml_buffer = &EditorHelp::load_xml_buffer;
+	GDExtensionEditorHelp::editor_help_remove_class = &EditorHelp::remove_class;
+}
+
 EditorHelp::EditorHelp() {
 	set_custom_minimum_size(Size2(150 * EDSCALE, 0));
 

+ 6 - 0
editor/editor_help.h

@@ -113,6 +113,7 @@ class EditorHelp : public VBoxContainer {
 	RichTextLabel *class_desc = nullptr;
 	HSplitContainer *h_split = nullptr;
 	static DocTools *doc;
+	static DocTools *ext_doc;
 
 	ConfirmationDialog *search_dialog = nullptr;
 	LineEdit *search = nullptr;
@@ -209,6 +210,9 @@ public:
 	static void cleanup_doc();
 	static String get_cache_full_path();
 
+	static void load_xml_buffer(const uint8_t *p_buffer, int p_size);
+	static void remove_class(const String &p_class);
+
 	void go_to_help(const String &p_help);
 	void go_to_class(const String &p_class);
 	void update_doc();
@@ -228,6 +232,8 @@ public:
 
 	void update_toggle_scripts_button();
 
+	static void init_gdext_pointers();
+
 	EditorHelp();
 	~EditorHelp();
 };

+ 4 - 0
editor/register_editor_types.cpp

@@ -282,6 +282,10 @@ void register_editor_types() {
 	ei_singleton.editor_only = true;
 	Engine::get_singleton()->add_singleton(ei_singleton);
 
+	// Required as GDExtensions can register docs at init time way before this
+	// class is actually instantiated.
+	EditorHelp::init_gdext_pointers();
+
 	OS::get_singleton()->benchmark_end_measure("Editor", "Register Types");
 }