diff --git a/src/bin/eolian_mono/eolian/mono/blacklist.hh b/src/bin/eolian_mono/eolian/mono/blacklist.hh index 646452e57c..684b842c24 100644 --- a/src/bin/eolian_mono/eolian/mono/blacklist.hh +++ b/src/bin/eolian_mono/eolian/mono/blacklist.hh @@ -77,14 +77,19 @@ inline bool is_alias_blacklisted(attributes::alias_def const& alias) return name_helpers::alias_full_eolian_name(alias) == "Eina.Error"; } -inline bool is_property_blacklisted(attributes::property_def const& property) +inline bool is_property_blacklisted(std::string const& name) { - auto name = name_helpers::klass_full_concrete_or_interface_name(property.klass) + "." + name_helpers::property_managed_name(property); return name == "Efl.Input.Key.Key" || name == "Efl.Input.Hold.Hold" || name == "Efl.Text.Text"; } +inline bool is_property_blacklisted(attributes::property_def const& property) +{ + auto name = name_helpers::klass_full_concrete_or_interface_name(property.klass) + "." + name_helpers::property_managed_name(property); + return is_property_blacklisted(name); +} + } } diff --git a/src/bin/eolian_mono/eolian/mono/documentation.hh b/src/bin/eolian_mono/eolian/mono/documentation.hh index f78c58a9d4..4e9fc5d4b1 100644 --- a/src/bin/eolian_mono/eolian/mono/documentation.hh +++ b/src/bin/eolian_mono/eolian/mono/documentation.hh @@ -6,6 +6,8 @@ #include "grammar/html_escaped_string.hpp" #include "using_decl.hh" #include "name_helpers.hh" +#include "generation_contexts.hh" +#include "blacklist.hh" #include @@ -19,14 +21,185 @@ struct documentation_generator documentation_generator(int scope_size) : scope_size(scope_size) {} + + // Returns the number of parameters (values + keys) that a property method requires + // Specify if you want the Setter or the Getter method. + static int property_num_parameters(const ::Eolian_Function *function, ::Eolian_Function_Type ftype) + { + Eina_Iterator *itr = ::eolian_property_keys_get(function, ftype); + Eolian_Function_Parameter *pr; + int n = 0; + EINA_ITERATOR_FOREACH(itr, pr) { n++; } + eina_iterator_free(itr); + itr = ::eolian_property_values_get(function, ftype); + EINA_ITERATOR_FOREACH(itr, pr) { n++; } + eina_iterator_free(itr); + return n; + } + + // Turns a function name from EO convention to EFL# convention. + // The name_tail parameter is the last 4 chars of the original string, which + // could be ".set" or ".get" and in this case they are ignored by Eolian. + // We want them to know what the documentation intended to reference. + static std::string function_conversion(const ::Eolian_Object *klass, const ::Eolian_Function *function, std::string name_tail) + { + ::Eolian_Function_Type ftype = ::eolian_function_type_get(function); + const char* eo_name = ::eolian_function_name_get(function); + std::string name = name_helpers::managed_namespace(::eolian_object_name_get(klass)); + switch(ftype) + { + case ::EOLIAN_METHOD: + if (blacklist::is_function_blacklisted( + ::eolian_function_full_c_name_get(function, ftype, EINA_FALSE))) return ""; + name += "."; + name += name_helpers::managed_method_name( + ::eolian_object_short_name_get(klass), eo_name); + break; + case ::EOLIAN_PROP_SET: + name += ".Set"; + name += name_helpers::property_managed_name(eo_name); + break; + case ::EOLIAN_PROP_GET: + name += ".Get"; + name += name_helpers::property_managed_name(eo_name); + break; + case ::EOLIAN_PROPERTY: + { + int getter_params = property_num_parameters(function, ::EOLIAN_PROP_GET); + int setter_params = property_num_parameters(function, ::EOLIAN_PROP_SET); + std::string short_name = name_helpers::property_managed_name(eo_name); + bool blacklisted = blacklist::is_property_blacklisted(name + "." + short_name); + // EO properties with keys, with more than one value, or blacklisted, are not + // converted into C# properties. + // In these cases we refer to the getter method instead of the property. + if ((getter_params > 1) || (setter_params > 1) || (blacklisted)) name += ".Get" + short_name; + else if (name_tail == ".get") name += ".Get" + short_name; + else if (name_tail == ".set") name += ".Set" + short_name; + else name += "." + short_name; + } + break; + default: + break; + } + return name; + } + + // Turns an Eolian reference like @Efl.Input.Pointer.tool into a tag + static std::string ref_conversion(const ::Eolian_Doc_Token *token, const Eolian_State *state, std::string name_tail) + { + const Eolian_Object *data, *data2; + ::Eolian_Object_Type type = + ::eolian_doc_token_ref_resolve(token, state, &data, &data2); + std::string ref; + switch(type) + { + case ::EOLIAN_OBJECT_STRUCT_FIELD: + ref = name_helpers::managed_namespace(::eolian_object_name_get(data)); + ref += "."; + ref += ::eolian_object_name_get(data2); + if (blacklist::is_struct_blacklisted(ref)) return ""; + break; + case ::EOLIAN_OBJECT_EVENT: + ref = name_helpers::managed_namespace(::eolian_object_name_get(data)); + ref += "."; + ref += name_helpers::managed_event_name(::eolian_object_name_get(data2)); + break; + case ::EOLIAN_OBJECT_ENUM_FIELD: + ref = name_helpers::managed_namespace(::eolian_object_name_get(data)); + ref += "."; + ref += name_helpers::enum_field_managed_name(::eolian_object_name_get(data2)); + break; + case ::EOLIAN_OBJECT_FUNCTION: + ref += function_conversion(data, (const ::Eolian_Function *)data2, name_tail); + break; + case ::EOLIAN_OBJECT_UNKNOWN: + // If the reference cannot be resolved, just return an empty string and + // it won't be converted into a tag. + break; + default: + ref = name_helpers::managed_namespace(::eolian_object_name_get(data)); + break; + } + return ref; + } + + // Turns EO documentation syntax into C# triple-slash XML comment syntax + static std::string syntax_conversion(std::string text, const Eolian_State *state) + { + std::string new_text, ref; + ::Eolian_Doc_Token token; + const char *text_ptr = text.c_str(); + ::eolian_doc_token_init(&token); + while ((text_ptr = ::eolian_documentation_tokenize(text_ptr, &token)) != NULL) + { + std::string token_text, name_tail; + char *token_text_cstr = ::eolian_doc_token_text_get(&token); + if (token_text_cstr) + { + token_text = token_text_cstr; + free(token_text_cstr); + if (token_text.length() > 4) + name_tail = token_text.substr(token_text.size() - 4, 4); + } + switch(::eolian_doc_token_type_get(&token)) + { + case ::EOLIAN_DOC_TOKEN_TEXT: + new_text += token_text; + break; + case ::EOLIAN_DOC_TOKEN_REF: + ref = ref_conversion(&token, state, name_tail); + if (ref != "") + new_text += ""; + else + // Unresolved references are passed through. + // They will appear in the docs as plain text, without link, + // but at least they won't be removed by DocFX. + new_text += token_text; + break; + case ::EOLIAN_DOC_TOKEN_MARK_NOTE: + new_text += "NOTE: " + token_text; + break; + case ::EOLIAN_DOC_TOKEN_MARK_WARNING: + new_text += "WARNING: " + token_text; + break; + case ::EOLIAN_DOC_TOKEN_MARK_REMARK: + new_text += "REMARK: " + token_text; + break; + case ::EOLIAN_DOC_TOKEN_MARK_TODO: + new_text += "TODO: " + token_text; + break; + case ::EOLIAN_DOC_TOKEN_MARKUP_MONOSPACE: + new_text += "" + token_text + ""; + break; + default: + break; + } + } + return new_text; + } + /// Tag generator helpers template - bool generate_tag(OutputIterator sink, std::string const& tag, std::string const &text, Context const& context) const + bool generate_tag(OutputIterator sink, std::string const& tag, std::string const &text, Context const& context, std::string tag_params = "") const { - if (text.empty()) - return true; + std::string new_text; + if (!as_generator(html_escaped_string).generate(std::back_inserter(new_text), text, context)) + return false; + new_text = syntax_conversion( new_text, context_find_tag(context).state ); - return as_generator( scope_tab(scope_size) << "///<" << tag << ">" << html_escaped_string << "\n").generate(sink, text, context); + std::string tabs; + as_generator(scope_tab(scope_size) << "/// ").generate (std::back_inserter(tabs), attributes::unused, context); + + std::istringstream ss(new_text); + std::string para; + std::string final_text = "<" + tag + tag_params + ">"; + bool first = true; + while (std::getline(ss, para)) { + if (first) final_text += para; + else final_text += "\n" + tabs + para; + first = false; + } + return as_generator(scope_tab(scope_size) << "/// " << final_text << "\n").generate(sink, attributes::unused, context); } template @@ -35,23 +208,16 @@ struct documentation_generator return generate_tag(sink, "summary", text, context); } - template - bool generate_tag_para(OutputIterator sink, std::string const& text, Context const& context) const - { - return generate_tag(sink, "para", text, context); - } - template bool generate_tag_param(OutputIterator sink, std::string const& name, std::string const& text, Context const& context) const { - return as_generator( scope_tab(scope_size) << "///" - << html_escaped_string << "\n").generate(sink, text, context); + return generate_tag(sink, "param", text, context, " name=\"" + name + "\""); } template bool generate_tag_return(OutputIterator sink, std::string const& text, Context const& context) const { - return generate_tag(sink, "return", text, context); + return generate_tag(sink, "returns", text, context); } // Actual exported generators @@ -91,7 +257,7 @@ struct documentation_generator if (!generate_parameter(sink, param, context)) return false; - if (!generate_tag_return(sink, func.return_documentation.summary, context)) + if (!generate_tag_return(sink, func.return_documentation.full_text, context)) return false; return true; @@ -107,7 +273,7 @@ struct documentation_generator if (!generate_parameter(sink, param, context)) return false; - if (!generate_tag_return(sink, func.return_documentation.summary, context)) + if (!generate_tag_return(sink, func.return_documentation.full_text, context)) return false; return true; @@ -116,51 +282,13 @@ struct documentation_generator template bool generate_parameter(OutputIterator sink, attributes::parameter_def const& param, Context const& context) const { - return generate_tag_param(sink, name_helpers::escape_keyword(param.param_name), param.documentation.summary, context); + return generate_tag_param(sink, name_helpers::escape_keyword(param.param_name), param.documentation.full_text, context); } template bool generate(OutputIterator sink, attributes::documentation_def const& doc, Context const& context) const { - if (!generate_preamble(sink, doc, context)) - return false; - if (!generate_body(sink, doc, context)) - return false; - if (!generate_epilogue(sink, doc, context)) - return false; - - return true; - } - - template - bool generate_preamble(OutputIterator sink, attributes::documentation_def const& doc, Context const context) const - { - return generate_tag_summary(sink, doc.summary, context); - } - - - template - bool generate_body(OutputIterator sink, attributes::documentation_def const& doc, Context const context) const - { - for (auto&& para : doc.desc_paragraphs) - { - if (!generate_tag_para(sink, para, context)) - return false; - } - - return true; - } - - template - bool generate_epilogue(OutputIterator sink, attributes::documentation_def const& doc, Context const context) const - { - if (doc.since.empty()) - return true; - - if (!generate_tag_para(sink, doc.since, context)) - return false; - - return true; + return generate_tag_summary(sink, doc.full_text, context); } }; diff --git a/src/bin/eolian_mono/eolian/mono/generation_contexts.hh b/src/bin/eolian_mono/eolian/mono/generation_contexts.hh index 7f94de7736..ca1ca9e678 100644 --- a/src/bin/eolian_mono/eolian/mono/generation_contexts.hh +++ b/src/bin/eolian_mono/eolian/mono/generation_contexts.hh @@ -11,6 +11,7 @@ struct class_context inherit, inherit_native, structs, + enums, function_ptr, alias, }; @@ -40,6 +41,10 @@ library_context::actual_library_name(const std::string& filename) const return '"' + library_name + '"'; } +struct eolian_state_context { + const Eolian_State *state; +}; + } #endif diff --git a/src/bin/eolian_mono/eolian/mono/name_helpers.hh b/src/bin/eolian_mono/eolian/mono/name_helpers.hh index cfda7229f6..d953a09f9a 100644 --- a/src/bin/eolian_mono/eolian/mono/name_helpers.hh +++ b/src/bin/eolian_mono/eolian/mono/name_helpers.hh @@ -32,10 +32,6 @@ namespace name_helpers { namespace attributes = efl::eolian::grammar::attributes; namespace detail { -inline bool is_iequal(std::string const& lhs, std::string const& rhs) -{ - return strcasecmp(lhs.c_str(), rhs.c_str()) == 0; -} inline bool is_equal(std::string const& lhs, std::string const& rhs) { return lhs == rhs; @@ -176,21 +172,26 @@ inline std::string managed_namespace(std::string const& ns) return escape_keyword(utils::remove_all(ns, '_')); } -inline std::string managed_method_name(attributes::function_def const& f) +inline std::string managed_method_name(std::string const& klass, std::string const& name) { - std::vector names = utils::split(f.name, '_'); + std::vector names = utils::split(name, '_'); name_helpers::reorder_verb(names); std::string candidate = escape_keyword(utils::to_pascal_case(names)); // Some eolian methods have the same name as their parent class - if (candidate == f.klass.eolian_name) + if (candidate == klass) candidate = "Do" + candidate; return candidate; } +inline std::string managed_method_name(attributes::function_def const& f) +{ + return managed_method_name(f.klass.eolian_name, f.name); +} + inline std::string alias_full_eolian_name(attributes::alias_def const& alias) { return join_namespaces(alias.namespaces, '.') + alias.eolian_name; @@ -243,14 +244,19 @@ inline std::string to_field_name(std::string const& in) return utils::capitalize(in); } -inline std::string property_managed_name(attributes::property_def const& property) +inline std::string property_managed_name(const std::string name) { - auto names = utils::split(property.name, '_'); + auto names = utils::split(name, '_'); // No need to escape keyword here as it will be capitalized and already // namespaced inside the owner class. return utils::to_pascal_case(names); } +inline std::string property_managed_name(attributes::property_def const& property) +{ + return property_managed_name(property.name); +} + inline std::string managed_part_name(attributes::part_def const& part) { std::vector names = utils::split(part.name, '_'); diff --git a/src/bin/eolian_mono/eolian_mono.cc b/src/bin/eolian_mono/eolian_mono.cc index 2e0e8918cb..db888816a2 100644 --- a/src/bin/eolian_mono/eolian_mono.cc +++ b/src/bin/eolian_mono/eolian_mono.cc @@ -138,11 +138,14 @@ run(options_type const& opts) throw std::runtime_error("Failed to generate file preamble"); } - auto context = efl::eolian::grammar::context_add_tag(eolian_mono::library_context{opts.dllimport, + auto lib_context = efl::eolian::grammar::context_add_tag(eolian_mono::library_context{opts.dllimport, opts.v_major, opts.v_minor, opts.references_map}, efl::eolian::grammar::context_null()); + + auto context = efl::eolian::grammar::context_add_tag(eolian_mono::eolian_state_context{opts.state}, lib_context); + EINA_ITERATOR_FOREACH(aliases, tp) { if (eolian_typedecl_type_get(tp) == EOLIAN_TYPEDECL_FUNCTION_POINTER) @@ -179,7 +182,8 @@ run(options_type const& opts) , enum_last; enum_iterator != enum_last; ++enum_iterator) { efl::eolian::grammar::attributes::enum_def enum_(&*enum_iterator, opts.unit); - if (!eolian_mono::enum_definition.generate(iterator, enum_, efl::eolian::grammar::context_null())) + auto enum_cxt = context_add_tag(class_context{class_context::enums}, context); + if (!eolian_mono::enum_definition.generate(iterator, enum_, enum_cxt)) { throw std::runtime_error("Failed to generate enum"); } diff --git a/src/lib/eolian_cxx/grammar/klass_def.hpp b/src/lib/eolian_cxx/grammar/klass_def.hpp index ce54f35dc1..008b5aa0e6 100644 --- a/src/lib/eolian_cxx/grammar/klass_def.hpp +++ b/src/lib/eolian_cxx/grammar/klass_def.hpp @@ -174,10 +174,11 @@ struct documentation_def std::string description; std::string since; std::vector desc_paragraphs; + std::string full_text; documentation_def() = default; - documentation_def(std::string summary, std::string description, std::string since) - : summary(summary), description(description), since(since) + documentation_def(std::string summary, std::string description, std::string since, std::string full_text) + : summary(summary), description(description), since(since), full_text(full_text) {} documentation_def(Eolian_Documentation const* eolian_doc) { @@ -188,15 +189,19 @@ struct documentation_def str = eolian_documentation_summary_get(eolian_doc); if (str) - summary = str; + full_text = summary = str; str = eolian_documentation_description_get(eolian_doc); - if (str) + if (str) { description = str; + full_text += "\n" + description; + } str = eolian_documentation_since_get(eolian_doc); - if (str) + if (str) { since = str; + full_text += "\n" + since; + } efl::eina::ptr_list l(eolian_documentation_string_split(description.c_str())); for (auto&& i : l) @@ -441,7 +446,6 @@ struct alias_def } documentation = ::eolian_typedecl_documentation_get(alias_obj); - } }; @@ -627,6 +631,7 @@ struct function_def { Eolian_Type const* r_type = ::eolian_function_return_type_get(function, type); name = ::eolian_function_name_get(function); + return_documentation = eolian_function_return_documentation_get(function, type); if(r_type) return_type.set(r_type, unit, EOLIAN_C_TYPE_RETURN); if(type == EOLIAN_METHOD || type == EOLIAN_FUNCTION_POINTER) @@ -660,6 +665,9 @@ struct function_def if(!r_type && type == EOLIAN_PROP_GET && values.size() == 1) { return_type = values[0].type; + if (return_documentation.summary.empty()) + return_documentation = values[0].documentation; + } else if(type == EOLIAN_PROP_GET) { @@ -696,8 +704,6 @@ struct function_def is_protected = eolian_function_scope_get(function, type) == EOLIAN_SCOPE_PROTECTED; is_static = eolian_function_is_class(function); - return_documentation = eolian_function_return_documentation_get(function, type); - Eolian_Implement const* implement = eolian_function_implement_get(function); if (!implement) return; @@ -823,7 +829,7 @@ struct property_def property_def() = default; property_def(Eolian_Function const *function, efl::eina::optional getter - , efl::eina::optional setter, Eolian_Unit const* unit) + , efl::eina::optional setter, Eolian_Unit const*) : getter(getter), setter(setter) { name = ::eolian_function_name_get(function);