Merge pull request #1374 from dsnopek/gdext-docs
Allow submitting documentation to the Godot editorpull/1461/head
commit
17a82e7f94
|
@ -2862,6 +2862,31 @@ typedef void (*GDExtensionInterfaceEditorAddPlugin)(GDExtensionConstStringNamePt
|
||||||
*/
|
*/
|
||||||
typedef void (*GDExtensionInterfaceEditorRemovePlugin)(GDExtensionConstStringNamePtr p_class_name);
|
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 a 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 a 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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -412,6 +412,7 @@ public:
|
||||||
method = p_method;
|
method = p_method;
|
||||||
generate_argument_types(sizeof...(P));
|
generate_argument_types(sizeof...(P));
|
||||||
set_argument_count(sizeof...(P));
|
set_argument_count(sizeof...(P));
|
||||||
|
set_const(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -578,6 +579,7 @@ public:
|
||||||
generate_argument_types(sizeof...(P));
|
generate_argument_types(sizeof...(P));
|
||||||
set_argument_count(sizeof...(P));
|
set_argument_count(sizeof...(P));
|
||||||
set_return(true);
|
set_return(true);
|
||||||
|
set_const(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -192,6 +192,13 @@ extern "C" GDExtensionInterfaceClassdbUnregisterExtensionClass gdextension_inter
|
||||||
extern "C" GDExtensionInterfaceGetLibraryPath gdextension_interface_get_library_path;
|
extern "C" GDExtensionInterfaceGetLibraryPath gdextension_interface_get_library_path;
|
||||||
extern "C" GDExtensionInterfaceEditorAddPlugin gdextension_interface_editor_add_plugin;
|
extern "C" GDExtensionInterfaceEditorAddPlugin gdextension_interface_editor_add_plugin;
|
||||||
extern "C" GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugin;
|
extern "C" GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugin;
|
||||||
|
extern "C" GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars gdextension_interface_editor_help_load_xml_from_utf8_chars;
|
||||||
|
extern "C" GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen gdextension_interface_editor_help_load_xml_from_utf8_chars_and_len;
|
||||||
|
|
||||||
|
class DocDataRegistration {
|
||||||
|
public:
|
||||||
|
DocDataRegistration(const char *p_hash, int p_uncompressed_size, int p_compressed_size, const unsigned char *p_data);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,38 @@ GDExtensionInterfaceClassdbUnregisterExtensionClass gdextension_interface_classd
|
||||||
GDExtensionInterfaceGetLibraryPath gdextension_interface_get_library_path = nullptr;
|
GDExtensionInterfaceGetLibraryPath gdextension_interface_get_library_path = nullptr;
|
||||||
GDExtensionInterfaceEditorAddPlugin gdextension_interface_editor_add_plugin = nullptr;
|
GDExtensionInterfaceEditorAddPlugin gdextension_interface_editor_add_plugin = nullptr;
|
||||||
GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugin = nullptr;
|
GDExtensionInterfaceEditorRemovePlugin gdextension_interface_editor_remove_plugin = nullptr;
|
||||||
|
GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars gdextension_interface_editor_help_load_xml_from_utf8_chars = nullptr;
|
||||||
|
GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen gdextension_interface_editor_help_load_xml_from_utf8_chars_and_len = nullptr;
|
||||||
|
|
||||||
|
struct DocData {
|
||||||
|
const char *hash = nullptr;
|
||||||
|
int uncompressed_size = 0;
|
||||||
|
int compressed_size = 0;
|
||||||
|
const unsigned char *data = nullptr;
|
||||||
|
|
||||||
|
inline bool is_valid() const {
|
||||||
|
return hash != nullptr && uncompressed_size > 0 && compressed_size > 0 && data != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_data() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
static DocData &get_doc_data() {
|
||||||
|
static DocData doc_data;
|
||||||
|
return doc_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
DocDataRegistration::DocDataRegistration(const char *p_hash, int p_uncompressed_size, int p_compressed_size, const unsigned char *p_data) {
|
||||||
|
DocData &doc_data = get_doc_data();
|
||||||
|
if (doc_data.is_valid()) {
|
||||||
|
printf("ERROR: Attempting to register documentation data when we already have some - discarding.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
doc_data.hash = p_hash;
|
||||||
|
doc_data.uncompressed_size = p_uncompressed_size;
|
||||||
|
doc_data.compressed_size = p_compressed_size;
|
||||||
|
doc_data.data = p_data;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
|
@ -440,6 +472,8 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
|
||||||
LOAD_PROC_ADDRESS(get_library_path, GDExtensionInterfaceGetLibraryPath);
|
LOAD_PROC_ADDRESS(get_library_path, GDExtensionInterfaceGetLibraryPath);
|
||||||
LOAD_PROC_ADDRESS(editor_add_plugin, GDExtensionInterfaceEditorAddPlugin);
|
LOAD_PROC_ADDRESS(editor_add_plugin, GDExtensionInterfaceEditorAddPlugin);
|
||||||
LOAD_PROC_ADDRESS(editor_remove_plugin, GDExtensionInterfaceEditorRemovePlugin);
|
LOAD_PROC_ADDRESS(editor_remove_plugin, GDExtensionInterfaceEditorRemovePlugin);
|
||||||
|
LOAD_PROC_ADDRESS(editor_help_load_xml_from_utf8_chars, GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars);
|
||||||
|
LOAD_PROC_ADDRESS(editor_help_load_xml_from_utf8_chars_and_len, GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen);
|
||||||
|
|
||||||
r_initialization->initialize = initialize_level;
|
r_initialization->initialize = initialize_level;
|
||||||
r_initialization->deinitialize = deinitialize_level;
|
r_initialization->deinitialize = deinitialize_level;
|
||||||
|
@ -469,6 +503,13 @@ void GDExtensionBinding::initialize_level(void *p_userdata, GDExtensionInitializ
|
||||||
ClassDB::initialize(p_level);
|
ClassDB::initialize(p_level);
|
||||||
}
|
}
|
||||||
level_initialized[p_level]++;
|
level_initialized[p_level]++;
|
||||||
|
|
||||||
|
if ((ModuleInitializationLevel)p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
|
||||||
|
const internal::DocData &doc_data = internal::get_doc_data();
|
||||||
|
if (doc_data.is_valid()) {
|
||||||
|
doc_data.load_data();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GDExtensionBinding::deinitialize_level(void *p_userdata, GDExtensionInitializationLevel p_level) {
|
void GDExtensionBinding::deinitialize_level(void *p_userdata, GDExtensionInitializationLevel p_level) {
|
||||||
|
@ -535,4 +576,15 @@ GDExtensionBool GDExtensionBinding::InitObject::init() const {
|
||||||
return GDExtensionBinding::init(get_proc_address, library, init_data, initialization);
|
return GDExtensionBinding::init(get_proc_address, library, init_data, initialization);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void internal::DocData::load_data() const {
|
||||||
|
PackedByteArray compressed;
|
||||||
|
compressed.resize(compressed_size);
|
||||||
|
memcpy(compressed.ptrw(), data, compressed_size);
|
||||||
|
|
||||||
|
// FileAccess::COMPRESSION_DEFLATE = 1
|
||||||
|
PackedByteArray decompressed = compressed.decompress(uncompressed_size, 1);
|
||||||
|
|
||||||
|
internal::gdextension_interface_editor_help_load_xml_from_utf8_chars_and_len(reinterpret_cast<const char *>(decompressed.ptr()), uncompressed_size);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace godot
|
} // namespace godot
|
||||||
|
|
|
@ -16,6 +16,10 @@ env = SConscript("../SConstruct")
|
||||||
env.Append(CPPPATH=["src/"])
|
env.Append(CPPPATH=["src/"])
|
||||||
sources = Glob("src/*.cpp")
|
sources = Glob("src/*.cpp")
|
||||||
|
|
||||||
|
if env["target"] in ["editor", "template_debug"]:
|
||||||
|
doc_data = env.GodotCPPDocData("src/gen/doc_data.gen.cpp", source=Glob("doc_classes/*.xml"))
|
||||||
|
sources.append(doc_data)
|
||||||
|
|
||||||
if env["platform"] == "macos":
|
if env["platform"] == "macos":
|
||||||
library = env.SharedLibrary(
|
library = env.SharedLibrary(
|
||||||
"project/bin/libgdexample.{}.{}.framework/libgdexample.{}.{}".format(
|
"project/bin/libgdexample.{}.{}.framework/libgdexample.{}.{}".format(
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<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">
|
||||||
|
<brief_description>
|
||||||
|
A test control defined in GDExtension.
|
||||||
|
</brief_description>
|
||||||
|
<description>
|
||||||
|
A control used for the automated GDExtension tests.
|
||||||
|
</description>
|
||||||
|
<tutorials>
|
||||||
|
</tutorials>
|
||||||
|
<methods>
|
||||||
|
<method name="simple_func">
|
||||||
|
<return type="void" />
|
||||||
|
<description>
|
||||||
|
Tests a simple function call.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
|
</methods>
|
||||||
|
<members>
|
||||||
|
</members>
|
||||||
|
<signals>
|
||||||
|
</signals>
|
||||||
|
<constants>
|
||||||
|
</constants>
|
||||||
|
</class>
|
|
@ -12,7 +12,7 @@ config_version=5
|
||||||
|
|
||||||
config/name="GDExtension Test Project"
|
config/name="GDExtension Test Project"
|
||||||
run/main_scene="res://main.tscn"
|
run/main_scene="res://main.tscn"
|
||||||
config/features=PackedStringArray("4.2")
|
config/features=PackedStringArray("4.3")
|
||||||
config/icon="res://icon.png"
|
config/icon="res://icon.png"
|
||||||
|
|
||||||
[native_extensions]
|
[native_extensions]
|
||||||
|
|
|
@ -325,6 +325,51 @@ def options(opts, env):
|
||||||
tool.options(opts)
|
tool.options(opts)
|
||||||
|
|
||||||
|
|
||||||
|
def make_doc_source(target, source, env):
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
dst = str(target[0])
|
||||||
|
g = open(dst, "w", encoding="utf-8")
|
||||||
|
buf = ""
|
||||||
|
docbegin = ""
|
||||||
|
docend = ""
|
||||||
|
for src in source:
|
||||||
|
src_path = str(src)
|
||||||
|
if not src_path.endswith(".xml"):
|
||||||
|
continue
|
||||||
|
with open(src_path, "r", encoding="utf-8") as f:
|
||||||
|
content = f.read()
|
||||||
|
buf += content
|
||||||
|
|
||||||
|
buf = (docbegin + buf + docend).encode("utf-8")
|
||||||
|
decomp_size = len(buf)
|
||||||
|
|
||||||
|
# Use maximum zlib compression level to further reduce file size
|
||||||
|
# (at the cost of initial build times).
|
||||||
|
buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
|
||||||
|
|
||||||
|
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
|
||||||
|
g.write("\n")
|
||||||
|
g.write("#include <godot_cpp/godot.hpp>\n")
|
||||||
|
g.write("\n")
|
||||||
|
|
||||||
|
g.write('static const char *_doc_data_hash = "' + str(hash(buf)) + '";\n')
|
||||||
|
g.write("static const int _doc_data_uncompressed_size = " + str(decomp_size) + ";\n")
|
||||||
|
g.write("static const int _doc_data_compressed_size = " + str(len(buf)) + ";\n")
|
||||||
|
g.write("static const unsigned char _doc_data_compressed[] = {\n")
|
||||||
|
for i in range(len(buf)):
|
||||||
|
g.write("\t" + str(buf[i]) + ",\n")
|
||||||
|
g.write("};\n")
|
||||||
|
g.write("\n")
|
||||||
|
|
||||||
|
g.write(
|
||||||
|
"static godot::internal::DocDataRegistration _doc_data_registration(_doc_data_hash, _doc_data_uncompressed_size, _doc_data_compressed_size, _doc_data_compressed);\n"
|
||||||
|
)
|
||||||
|
g.write("\n")
|
||||||
|
|
||||||
|
g.close()
|
||||||
|
|
||||||
|
|
||||||
def generate(env):
|
def generate(env):
|
||||||
# Default num_jobs to local cpu count if not user specified.
|
# Default num_jobs to local cpu count if not user specified.
|
||||||
# SCons has a peculiarity where user-specified options won't be overridden
|
# SCons has a peculiarity where user-specified options won't be overridden
|
||||||
|
@ -451,7 +496,8 @@ def generate(env):
|
||||||
# Builders
|
# Builders
|
||||||
env.Append(
|
env.Append(
|
||||||
BUILDERS={
|
BUILDERS={
|
||||||
"GodotCPPBindings": Builder(action=Action(scons_generate_bindings, "$GENCOMSTR"), emitter=scons_emit_files)
|
"GodotCPPBindings": Builder(action=Action(scons_generate_bindings, "$GENCOMSTR"), emitter=scons_emit_files),
|
||||||
|
"GodotCPPDocData": Builder(action=make_doc_source),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
env.AddMethod(_godot_cpp, "GodotCPP")
|
env.AddMethod(_godot_cpp, "GodotCPP")
|
||||||
|
|
Loading…
Reference in New Issue