Skip to content

Commit

Permalink
GDExtension: add an interface for loading extra documentation
Browse files Browse the repository at this point in the history
Adds two new GDExtension interface methods:
 - `editor_help_load_xml_from_utf8_chars`
 - `editor_help_load_xml_from_utf8_chars_and_len`

Both of these methods parse the XML passed into an extra documentation
container which, when needed, is merged into the main doc container.

Co-Authored-By: Rémi Verschelde <rverschelde@gmail.com>
  • Loading branch information
Riteo and akien-mga committed Oct 22, 2023
1 parent f71f4b8 commit 24a69c5
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 0 deletions.
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 @@ -1183,4 +1185,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 @@ -195,6 +195,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 @@ -1375,6 +1377,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 @@ -1518,6 +1533,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 (*GDExtensionsInterfaceEditorHelpLoadXML)(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 (*GDExtensionsInterfaceEditorHelpLoadXMLAndLen)(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 @@ -1635,3 +1635,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 @@ -51,6 +51,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
41 changes: 41 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 @@ -2372,6 +2374,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);
}
}

static bool first_attempt = true;

static String _compute_doc_version_hash() {
Expand All @@ -2392,6 +2416,12 @@ void EditorHelp::_load_doc_thread(void *p_udata) {
first_attempt = false;
callable_mp_static(&EditorHelp::generate_doc).bind(true).call_deferred();
}

// Append extra doc data. This is needed because it would otherwise be wiped by
// the doc regeneration routine.
if (ext_doc) {
doc->merge_from(*ext_doc);
}
}

void EditorHelp::_gen_doc_thread(void *p_udata) {
Expand All @@ -2411,6 +2441,12 @@ void EditorHelp::_gen_doc_thread(void *p_udata) {
if (err) {
ERR_PRINT("Cannot save editor help cache (" + get_cache_full_path() + ").");
}

// Append extra doc data. This is needed because it would otherwise be wiped by
// the doc regeneration routine.
if (ext_doc) {
doc->merge_from(*ext_doc);
}
}

static bool doc_gen_use_threads = true;
Expand Down Expand Up @@ -2561,6 +2597,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 @@ -206,6 +207,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 @@ -225,6 +229,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("register_editor_types");
}

Expand Down

0 comments on commit 24a69c5

Please sign in to comment.