Skip to content

Commit a434850

Browse files
committed
Allow submitting documentation to the Godot editor
1 parent 54fe2f9 commit a434850

File tree

8 files changed

+163
-2
lines changed

8 files changed

+163
-2
lines changed

gdextension/gdextension_interface.h

+25
Original file line numberDiff line numberDiff line change
@@ -2835,6 +2835,31 @@ typedef void (*GDExtensionInterfaceEditorAddPlugin)(GDExtensionConstStringNamePt
28352835
*/
28362836
typedef void (*GDExtensionInterfaceEditorRemovePlugin)(GDExtensionConstStringNamePtr p_class_name);
28372837

2838+
/**
2839+
* @name editor_help_load_xml_from_utf8_chars
2840+
* @since 4.3
2841+
*
2842+
* Loads new XML-formatted documentation data in the editor.
2843+
*
2844+
* The provided pointer can be immediately freed once the function returns.
2845+
*
2846+
* @param p_data A pointer to a UTF-8 encoded C string (null terminated).
2847+
*/
2848+
typedef void (*GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars)(const char *p_data);
2849+
2850+
/**
2851+
* @name editor_help_load_xml_from_utf8_chars_and_len
2852+
* @since 4.3
2853+
*
2854+
* Loads new XML-formatted documentation data in the editor.
2855+
*
2856+
* The provided pointer can be immediately freed once the function returns.
2857+
*
2858+
* @param p_data A pointer to a UTF-8 encoded C string.
2859+
* @param p_size The number of bytes (not code units).
2860+
*/
2861+
typedef void (*GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen)(const char *p_data, GDExtensionInt p_size);
2862+
28382863
#ifdef __cplusplus
28392864
}
28402865
#endif

include/godot_cpp/core/method_bind.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ class MethodBindTC : public MethodBind {
412412
method = p_method;
413413
generate_argument_types(sizeof...(P));
414414
set_argument_count(sizeof...(P));
415+
set_const(true);
415416
}
416417
};
417418

@@ -578,6 +579,7 @@ class MethodBindTRC : public MethodBind {
578579
generate_argument_types(sizeof...(P));
579580
set_argument_count(sizeof...(P));
580581
set_return(true);
582+
set_const(true);
581583
}
582584
};
583585

include/godot_cpp/godot.hpp

+7
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,13 @@ extern "C" GDExtensionInterfaceClassdbUnregisterExtensionClass gdextension_inter
190190
extern "C" GDExtensionInterfaceGetLibraryPath gdextension_interface_get_library_path;
191191
extern "C" GDExtensionInterfaceEditorAddPlugin gdextension_interface_editor_add_plugin;
192192
extern "C" GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugin;
193+
extern "C" GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars gdextension_interface_editor_help_load_xml_from_utf8_chars;
194+
extern "C" GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen gdextension_interface_editor_help_load_xml_from_utf8_chars_and_len;
195+
196+
class DocDataRegistration {
197+
public:
198+
DocDataRegistration(const char *p_hash, int p_uncompressed_size, int p_compressed_size, const unsigned char *p_data);
199+
};
193200

194201
} // namespace internal
195202

src/godot.cpp

+52
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,38 @@ GDExtensionInterfaceClassdbUnregisterExtensionClass gdextension_interface_classd
196196
GDExtensionInterfaceGetLibraryPath gdextension_interface_get_library_path = nullptr;
197197
GDExtensionInterfaceEditorAddPlugin gdextension_interface_editor_add_plugin = nullptr;
198198
GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugin = nullptr;
199+
GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars gdextension_interface_editor_help_load_xml_from_utf8_chars = nullptr;
200+
GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen gdextension_interface_editor_help_load_xml_from_utf8_chars_and_len = nullptr;
201+
202+
struct DocData {
203+
const char *hash = nullptr;
204+
int uncompressed_size = 0;
205+
int compressed_size = 0;
206+
const unsigned char *data = nullptr;
207+
208+
inline bool is_valid() const {
209+
return hash != nullptr && uncompressed_size > 0 && compressed_size > 0 && data != nullptr;
210+
}
211+
212+
void load_data() const;
213+
};
214+
215+
static DocData &get_doc_data() {
216+
static DocData doc_data;
217+
return doc_data;
218+
}
219+
220+
DocDataRegistration::DocDataRegistration(const char *p_hash, int p_uncompressed_size, int p_compressed_size, const unsigned char *p_data) {
221+
DocData &doc_data = get_doc_data();
222+
if (doc_data.is_valid()) {
223+
printf("ERROR: Attempting to register documentation data when we already have some - discarding.\n");
224+
return;
225+
}
226+
doc_data.hash = p_hash;
227+
doc_data.uncompressed_size = p_uncompressed_size;
228+
doc_data.compressed_size = p_compressed_size;
229+
doc_data.data = p_data;
230+
}
199231

200232
} // namespace internal
201233

@@ -436,6 +468,8 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
436468
LOAD_PROC_ADDRESS(get_library_path, GDExtensionInterfaceGetLibraryPath);
437469
LOAD_PROC_ADDRESS(editor_add_plugin, GDExtensionInterfaceEditorAddPlugin);
438470
LOAD_PROC_ADDRESS(editor_remove_plugin, GDExtensionInterfaceEditorRemovePlugin);
471+
LOAD_PROC_ADDRESS(editor_help_load_xml_from_utf8_chars, GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars);
472+
LOAD_PROC_ADDRESS(editor_help_load_xml_from_utf8_chars_and_len, GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen);
439473

440474
r_initialization->initialize = initialize_level;
441475
r_initialization->deinitialize = deinitialize_level;
@@ -465,6 +499,13 @@ void GDExtensionBinding::initialize_level(void *p_userdata, GDExtensionInitializ
465499
ClassDB::initialize(p_level);
466500
}
467501
level_initialized[p_level]++;
502+
503+
if ((ModuleInitializationLevel)p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
504+
const internal::DocData &doc_data = internal::get_doc_data();
505+
if (doc_data.is_valid()) {
506+
doc_data.load_data();
507+
}
508+
}
468509
}
469510

470511
void GDExtensionBinding::deinitialize_level(void *p_userdata, GDExtensionInitializationLevel p_level) {
@@ -531,4 +572,15 @@ GDExtensionBool GDExtensionBinding::InitObject::init() const {
531572
return GDExtensionBinding::init(get_proc_address, library, init_data, initialization);
532573
}
533574

575+
void internal::DocData::load_data() const {
576+
PackedByteArray compressed;
577+
compressed.resize(compressed_size);
578+
memcpy(compressed.ptrw(), data, compressed_size);
579+
580+
// FileAccess::COMPRESSION_DEFLATE = 1
581+
PackedByteArray decompressed = compressed.decompress(uncompressed_size, 1);
582+
583+
internal::gdextension_interface_editor_help_load_xml_from_utf8_chars_and_len(reinterpret_cast<const char *>(decompressed.ptr()), uncompressed_size);
584+
}
585+
534586
} // namespace godot

test/SConstruct

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ env = SConscript("../SConstruct")
1616
env.Append(CPPPATH=["src/"])
1717
sources = Glob("src/*.cpp")
1818

19+
if env["target"] in ["editor", "template_debug"]:
20+
doc_data = env.GodotCPPDocData("src/gen/doc_data.gen.cpp", source=Glob("doc_classes/*.xml"))
21+
sources.append(doc_data)
22+
1923
if env["platform"] == "macos":
2024
library = env.SharedLibrary(
2125
"project/bin/libgdexample.{}.{}.framework/libgdexample.{}.{}".format(

test/doc_classes/Example.xml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="Example" inherits="Control" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/godotengine/godot/master/doc/class.xsd">
3+
<brief_description>
4+
A test control defined in GDExtension.
5+
</brief_description>
6+
<description>
7+
A control used for the automated GDExtension tests.
8+
</description>
9+
<tutorials>
10+
</tutorials>
11+
<methods>
12+
<method name="simple_func">
13+
<return type="void" />
14+
<description>
15+
Tests a simple function call.
16+
</description>
17+
</method>
18+
</methods>
19+
<members>
20+
</members>
21+
<signals>
22+
</signals>
23+
<constants>
24+
</constants>
25+
</class>

test/project/project.godot

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ config_version=5
1212

1313
config/name="GDExtension Test Project"
1414
run/main_scene="res://main.tscn"
15-
config/features=PackedStringArray("4.2")
15+
config/features=PackedStringArray("4.3")
1616
config/icon="res://icon.png"
1717

1818
[native_extensions]

tools/godotcpp.py

+47-1
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,51 @@ def options(opts, env):
325325
tool.options(opts)
326326

327327

328+
def make_doc_source(target, source, env):
329+
import zlib
330+
331+
dst = str(target[0])
332+
g = open(dst, "w", encoding="utf-8")
333+
buf = ""
334+
docbegin = ""
335+
docend = ""
336+
for src in source:
337+
src_path = str(src)
338+
if not src_path.endswith(".xml"):
339+
continue
340+
with open(src_path, "r", encoding="utf-8") as f:
341+
content = f.read()
342+
buf += content
343+
344+
buf = (docbegin + buf + docend).encode("utf-8")
345+
decomp_size = len(buf)
346+
347+
# Use maximum zlib compression level to further reduce file size
348+
# (at the cost of initial build times).
349+
buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
350+
351+
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
352+
g.write("\n")
353+
g.write("#include <godot_cpp/godot.hpp>\n")
354+
g.write("\n")
355+
356+
g.write('static const char *_doc_data_hash = "' + str(hash(buf)) + '";\n')
357+
g.write("static const int _doc_data_uncompressed_size = " + str(decomp_size) + ";\n")
358+
g.write("static const int _doc_data_compressed_size = " + str(len(buf)) + ";\n")
359+
g.write("static const unsigned char _doc_data_compressed[] = {\n")
360+
for i in range(len(buf)):
361+
g.write("\t" + str(buf[i]) + ",\n")
362+
g.write("};\n")
363+
g.write("\n")
364+
365+
g.write(
366+
"static godot::internal::DocDataRegistration _doc_data_registration(_doc_data_hash, _doc_data_uncompressed_size, _doc_data_compressed_size, _doc_data_compressed);\n"
367+
)
368+
g.write("\n")
369+
370+
g.close()
371+
372+
328373
def generate(env):
329374
# Default num_jobs to local cpu count if not user specified.
330375
# SCons has a peculiarity where user-specified options won't be overridden
@@ -451,7 +496,8 @@ def generate(env):
451496
# Builders
452497
env.Append(
453498
BUILDERS={
454-
"GodotCPPBindings": Builder(action=Action(scons_generate_bindings, "$GENCOMSTR"), emitter=scons_emit_files)
499+
"GodotCPPBindings": Builder(action=Action(scons_generate_bindings, "$GENCOMSTR"), emitter=scons_emit_files),
500+
"GodotCPPDocData": Builder(action=make_doc_source),
455501
}
456502
)
457503
env.AddMethod(_godot_cpp, "GodotCPP")

0 commit comments

Comments
 (0)