Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GDExtension: Add an interface for loading extra documentation #83747

Merged
merged 1 commit into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions core/extension/gdextension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 changes: 20 additions & 0 deletions core/extension/gdextension.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,26 @@ class GDExtensionEditorPlugins {
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 changes: 17 additions & 0 deletions core/extension/gdextension_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
#include "core/variant/variant.h"
#include "core/version.h"

#include <string.h>

class CallableCustomExtension : public CallableCustom {
void *userdata;
void *token;
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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 changes: 25 additions & 0 deletions core/extension/gdextension_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions editor/doc_tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 change: 1 addition & 0 deletions editor/doc_tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class DocTools {

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 changes: 34 additions & 0 deletions editor/editor_help.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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));

Expand Down
6 changes: 6 additions & 0 deletions editor/editor_help.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -209,6 +210,9 @@ class EditorHelp : public VBoxContainer {
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();
Expand All @@ -228,6 +232,8 @@ class EditorHelp : public VBoxContainer {

void update_toggle_scripts_button();

static void init_gdext_pointers();

EditorHelp();
~EditorHelp();
};
Expand Down
4 changes: 4 additions & 0 deletions editor/register_editor_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

Expand Down
Loading