#include #include "docs.h" static int _indent_line(Eina_Strbuf *buf, int ind) { int i; for (i = 0; i < ind; ++i) eina_strbuf_append_char(buf, ' '); return ind; } #define DOC_LINE_LIMIT 79 #define DOC_LINE_TEST 59 #define DOC_LINE_OVER 39 #define DOC_LIMIT(ind) ((ind > DOC_LINE_TEST) ? (ind + DOC_LINE_OVER) \ : DOC_LINE_LIMIT) static void _generate_ref(const Eolian_State *state, const char *refn, Eina_Strbuf *wbuf, Eina_Bool use_legacy) { const Eolian_Object *decl = eolian_state_object_by_name_get(state, refn); if (decl) { char *n = strdup(eolian_object_name_get(decl)); char *p = n; while ((p = strchr(p, '.'))) *p = '_'; if (eolian_object_type_get(decl) == EOLIAN_OBJECT_VARIABLE) { const Eolian_Variable *v = (const Eolian_Variable *)decl; /* constants are emitted as macros */ if (eolian_variable_type_get(v) == EOLIAN_VAR_CONSTANT) eina_str_toupper(&n); } eina_strbuf_append(wbuf, n); free(n); return; } /* not a plain declaration, so it must be struct/enum field or func */ const char *sfx = strrchr(refn, '.'); if (!sfx) goto noref; Eina_Stringshare *bname = eina_stringshare_add_length(refn, sfx - refn); const Eolian_Typedecl *tp = eolian_state_struct_by_name_get(state, bname); if (tp) { if (!eolian_typedecl_struct_field_get(tp, sfx + 1)) { eina_stringshare_del(bname); goto noref; } _generate_ref(state, bname, wbuf, use_legacy); eina_strbuf_append(wbuf, sfx); eina_stringshare_del(bname); return; } tp = eolian_state_enum_by_name_get(state, bname); if (tp) { const Eolian_Enum_Type_Field *efl = eolian_typedecl_enum_field_get(tp, sfx + 1); if (!efl) { eina_stringshare_del(bname); goto noref; } Eina_Stringshare *str = eolian_typedecl_enum_field_c_name_get(efl); eina_strbuf_append(wbuf, str); eina_stringshare_del(bname); return; } const Eolian_Class *cl = eolian_state_class_by_name_get(state, bname); const Eolian_Function *fn = NULL; /* match methods and properties; we're only figuring out existence */ Eolian_Function_Type ftype = EOLIAN_UNRESOLVED; if (!cl) { const char *mname = NULL; if (!strcmp(sfx, ".get")) ftype = EOLIAN_PROP_GET; else if (!strcmp(sfx, ".set")) ftype = EOLIAN_PROP_SET; if (ftype != EOLIAN_UNRESOLVED) { eina_stringshare_del(bname); mname = sfx - 1; while ((mname != refn) && (*mname != '.')) --mname; if (mname == refn) goto noref; bname = eina_stringshare_add_length(refn, mname - refn); cl = eolian_state_class_by_name_get(state, bname); eina_stringshare_del(bname); } if (cl) { char *meth = strndup(mname + 1, sfx - mname - 1); fn = eolian_class_function_by_name_get(cl, meth, ftype); if (ftype == EOLIAN_UNRESOLVED) ftype = eolian_function_type_get(fn); free(meth); } } else { fn = eolian_class_function_by_name_get(cl, sfx + 1, ftype); ftype = eolian_function_type_get(fn); } if (!fn) goto noref; Eina_Stringshare *fcn = eolian_function_full_c_name_get(fn, ftype); if (!fcn) goto noref; eina_strbuf_append(wbuf, fcn); eina_stringshare_del(fcn); return; noref: eina_strbuf_append(wbuf, refn); } static int _append_section(const Eolian_State *state, const char *desc, int ind, int curl, Eina_Strbuf *buf, Eina_Strbuf *wbuf, Eina_Bool use_legacy) { Eina_Bool try_note = EINA_TRUE; while (*desc) { eina_strbuf_reset(wbuf); while (*desc && isspace(*desc) && (*desc != '\n')) eina_strbuf_append_char(wbuf, *desc++); if (try_note) { #define CHECK_NOTE(str) !strncmp(desc, str ": ", sizeof(str ":")) if (CHECK_NOTE("Note")) { eina_strbuf_append(wbuf, "@note "); desc += sizeof("Note:"); } else if (CHECK_NOTE("Warning")) { eina_strbuf_append(wbuf, "@warning "); desc += sizeof("Warning:"); } else if (CHECK_NOTE("Remark")) { eina_strbuf_append(wbuf, "@remark "); desc += sizeof("Remark:"); } else if (CHECK_NOTE("TODO")) { eina_strbuf_append(wbuf, "@todo "); desc += sizeof("TODO:"); } #undef CHECK_NOTE try_note = EINA_FALSE; } if (*desc == '\\') { desc++; if ((*desc != '@') && (*desc != '$')) eina_strbuf_append_char(wbuf, '\\'); eina_strbuf_append_char(wbuf, *desc++); } else if (*desc == '@') { const char *ref = ++desc; if (isalpha(*desc) || (*desc == '_')) { eina_strbuf_append(wbuf, "@ref "); while (isalnum(*desc) || (*desc == '.') || (*desc == '_')) ++desc; if (*(desc - 1) == '.') --desc; Eina_Stringshare *refn = eina_stringshare_add_length(ref, desc - ref); _generate_ref(state, refn, wbuf, use_legacy); eina_stringshare_del(refn); } else eina_strbuf_append_char(wbuf, '@'); } else if (*desc == '$') { desc++; if (isalpha(*desc)) eina_strbuf_append(wbuf, "@c "); else eina_strbuf_append_char(wbuf, '$'); } while (*desc && !isspace(*desc)) eina_strbuf_append_char(wbuf, *desc++); int limit = DOC_LIMIT(ind); int wlen = eina_strbuf_length_get(wbuf); if ((int)(curl + wlen) > limit) { curl = 3; eina_strbuf_append_char(buf, '\n'); curl += _indent_line(buf, ind); eina_strbuf_append(buf, " * "); if (*eina_strbuf_string_get(wbuf) == ' ') eina_strbuf_remove(wbuf, 0, 1); } curl += eina_strbuf_length_get(wbuf); eina_strbuf_append(buf, eina_strbuf_string_get(wbuf)); if (*desc == '\n') { desc++; eina_strbuf_append_char(buf, '\n'); while (*desc == '\n') { _indent_line(buf, ind); eina_strbuf_append(buf, " *\n"); desc++; try_note = EINA_TRUE; } curl = _indent_line(buf, ind) + 3; eina_strbuf_append(buf, " * "); } } return curl; } static int _append_since(const char *since, int indent, int curl, Eina_Strbuf *buf) { if (since) { eina_strbuf_append_char(buf, '\n'); _indent_line(buf, indent); eina_strbuf_append(buf, " *\n"); curl = _indent_line(buf, indent); eina_strbuf_append(buf, " * @since "); eina_strbuf_append(buf, since); curl += strlen(since) + sizeof(" * @since ") - 1; } return curl; } static int _append_extra(const char *el, int indent, int curl, Eina_Bool nl, Eina_Strbuf *buf) { if (el) { eina_strbuf_append_char(buf, '\n'); if (nl) { _indent_line(buf, indent); eina_strbuf_append(buf, " *\n"); } curl = _indent_line(buf, indent); eina_strbuf_append(buf, " * "); eina_strbuf_append(buf, el); curl += strlen(el) + sizeof(" * ") - 1; } return curl; } static char * _sanitize_group(const char *group) { if (!group) return NULL; char *ret = strdup(group); char *p; while ((p = strchr(ret, '.'))) *p = '_'; return ret; } static void _append_group(Eina_Strbuf *buf, char *sgrp, int indent) { if (!sgrp) return; eina_strbuf_append(buf, " * @ingroup "); eina_strbuf_append(buf, sgrp); eina_strbuf_append_char(buf, '\n'); _indent_line(buf, indent); free(sgrp); } static void _gen_doc_brief(const Eolian_State *state, const char *summary, const char *since, const char *group, const char *el, int indent, Eina_Strbuf *buf, Eina_Bool use_legacy) { int curl = 4 + indent; Eina_Strbuf *wbuf = eina_strbuf_new(); if (indent) eina_strbuf_append(buf, "/**< "); else eina_strbuf_append(buf, "/** "); curl = _append_section(state, summary, indent, curl, buf, wbuf, use_legacy); eina_strbuf_free(wbuf); curl = _append_extra(el, indent, curl, EINA_FALSE, buf); curl = _append_since(since, indent, curl, buf); char *sgrp = _sanitize_group(group); if (((curl + 3) > DOC_LIMIT(indent)) || sgrp) { eina_strbuf_append_char(buf, '\n'); _indent_line(buf, indent); if (sgrp) eina_strbuf_append(buf, " *"); } if (sgrp) { eina_strbuf_append_char(buf, '\n'); _indent_line(buf, indent); } _append_group(buf, sgrp, indent); eina_strbuf_append(buf, " */"); } static void _gen_doc_full(const Eolian_State *state, const char *summary, const char *description, const char *since, const char *group, const char *el, int indent, Eina_Strbuf *buf, Eina_Bool use_legacy) { int curl = 0; Eina_Strbuf *wbuf = eina_strbuf_new(); if (indent) eina_strbuf_append(buf, "/**<\n"); else eina_strbuf_append(buf, "/**\n"); curl += _indent_line(buf, indent); eina_strbuf_append(buf, " * @brief "); curl += sizeof(" * @brief ") - 1; _append_section(state, summary, indent, curl, buf, wbuf, use_legacy); eina_strbuf_append_char(buf, '\n'); _indent_line(buf, indent); eina_strbuf_append(buf, " *\n"); curl = _indent_line(buf, indent); eina_strbuf_append(buf, " * "); _append_section(state, description, indent, curl + 3, buf, wbuf, use_legacy); curl = _append_extra(el, indent, curl, EINA_TRUE, buf); curl = _append_since(since, indent, curl, buf); eina_strbuf_append_char(buf, '\n'); _indent_line(buf, indent); char *sgrp = _sanitize_group(group); if (sgrp) { eina_strbuf_append(buf, " *\n"); _indent_line(buf, indent); } _append_group(buf, sgrp, indent); eina_strbuf_append(buf, " */"); eina_strbuf_free(wbuf); } static Eina_Strbuf * _gen_doc_buf(const Eolian_State *state, const Eolian_Documentation *doc, const char *group, const char *el, int indent, Eina_Bool use_legacy) { if (!doc) return NULL; const char *sum = eolian_documentation_summary_get(doc); const char *desc = eolian_documentation_description_get(doc); const char *since = eolian_documentation_since_get(doc); Eina_Strbuf *buf = eina_strbuf_new(); if (!desc) _gen_doc_brief(state, sum, since, group, el, indent, buf, use_legacy); else _gen_doc_full(state, sum, desc, since, group, el, indent, buf, use_legacy); return buf; } Eina_Strbuf * eo_gen_docs_full_gen(const Eolian_State *state, const Eolian_Documentation *doc, const char *group, int indent, Eina_Bool use_legacy) { return _gen_doc_buf(state, doc, group, NULL, indent, use_legacy); } Eina_Strbuf * eo_gen_docs_event_gen(const Eolian_State *state, const Eolian_Event *ev, const char *group) { if (!ev) return NULL; const Eolian_Documentation *doc = eolian_event_documentation_get(ev); char buf[1024]; const Eolian_Type *rt = eolian_event_type_get(ev); const char *p = NULL; if (rt) { p = buf; Eina_Stringshare *rts = eolian_type_c_type_get(rt, EOLIAN_C_TYPE_DEFAULT); snprintf(buf, sizeof(buf), "@return %s", rts); eina_stringshare_del(rts); } if (!doc) { Eina_Strbuf *bufs = eina_strbuf_new(); eina_strbuf_append(bufs, "/**\n * No description\n"); if (p) { eina_strbuf_append(bufs, " * "); eina_strbuf_append(bufs, p); eina_strbuf_append_char(bufs, '\n'); } eina_strbuf_append(bufs, " */"); return bufs; } return _gen_doc_buf(state, doc, group, p, 0, EINA_FALSE); } Eina_Strbuf * eo_gen_docs_func_gen(const Eolian_State *state, const Eolian_Function *fid, Eolian_Function_Type ftype, int indent) { const Eolian_Function_Parameter *par = NULL; const Eolian_Function_Parameter *vpar = NULL; const Eolian_Documentation *doc, *pdoc, *rdoc; Eina_Iterator *itr = NULL; Eina_Iterator *vitr = NULL; Eina_Bool force_out = EINA_FALSE; Eina_Strbuf *buf = eina_strbuf_new(); Eina_Strbuf *wbuf = NULL; const char *sum = NULL, *desc = NULL, *since = NULL; int curl = 0; const char *group = eolian_class_name_get(eolian_function_class_get(fid)); const Eolian_Implement *fimp = eolian_function_implement_get(fid); if (ftype == EOLIAN_METHOD) { doc = eolian_implement_documentation_get(fimp, EOLIAN_METHOD); pdoc = NULL; } else { doc = eolian_implement_documentation_get(fimp, EOLIAN_PROPERTY); pdoc = eolian_implement_documentation_get(fimp, ftype); if (!doc && pdoc) doc = pdoc; if (pdoc == doc) pdoc = NULL; } rdoc = eolian_function_return_documentation_get(fid, ftype); if (doc) { sum = eolian_documentation_summary_get(doc); desc = eolian_documentation_description_get(doc); since = eolian_documentation_since_get(doc); if (pdoc && eolian_documentation_since_get(pdoc)) since = eolian_documentation_since_get(pdoc); } if (ftype == EOLIAN_METHOD) { itr = eolian_function_parameters_get(fid); } else { itr = eolian_property_keys_get(fid, ftype); vitr = eolian_property_values_get(fid, ftype); if (!vitr || !eina_iterator_next(vitr, (void**)&vpar)) { eina_iterator_free(vitr); vitr = NULL; } } if (!itr || !eina_iterator_next(itr, (void**)&par)) { eina_iterator_free(itr); itr = NULL; } /* when return is not set on getter, value becomes return instead of param */ if (ftype == EOLIAN_PROP_GET && !eolian_function_return_type_get(fid, ftype)) { const Eolian_Function_Parameter *rvpar = vpar; if (!eina_iterator_next(vitr, (void**)&vpar)) { /* one value - not out param */ eina_iterator_free(vitr); rdoc = rvpar ? eolian_parameter_documentation_get(rvpar) : NULL; vitr = NULL; vpar = NULL; } else { /* multiple values - always out params */ eina_iterator_free(vitr); vitr = eolian_property_values_get(fid, ftype); if (!vitr) vpar = NULL; else if (!eina_iterator_next(vitr, (void**)&vpar)) { eina_iterator_free(vitr); vitr = NULL; vpar = NULL; } } } if (!par) { /* no keys, try values */ itr = vitr; par = vpar; vitr = NULL; vpar = NULL; if (ftype == EOLIAN_PROP_GET) force_out = EINA_TRUE; } /* only summary, nothing else; generate standard brief doc */ if (!desc && !par && !vpar && !rdoc && (ftype == EOLIAN_METHOD || !pdoc)) { _gen_doc_brief(state, sum ? sum : "No description supplied.", since, group, NULL, indent, buf, EINA_FALSE); return buf; } wbuf = eina_strbuf_new(); eina_strbuf_append(buf, "/**\n"); curl += _indent_line(buf, indent); eina_strbuf_append(buf, " * @brief "); curl += sizeof(" * @brief ") - 1; _append_section(state, sum ? sum : "No description supplied.", indent, curl, buf, wbuf, EINA_FALSE); eina_strbuf_append_char(buf, '\n'); if (desc || since || par || rdoc || pdoc) { _indent_line(buf, indent); eina_strbuf_append(buf, " *\n"); } if (desc) { curl = _indent_line(buf, indent); eina_strbuf_append(buf, " * "); _append_section(state, desc, indent, curl + 3, buf, wbuf, EINA_FALSE); eina_strbuf_append_char(buf, '\n'); if (par || rdoc || pdoc || since) { _indent_line(buf, indent); eina_strbuf_append(buf, " *\n"); } } if (pdoc) { const char *pdesc = eolian_documentation_description_get(pdoc); curl = _indent_line(buf, indent); eina_strbuf_append(buf, " * "); _append_section(state, eolian_documentation_summary_get(pdoc), indent, curl + 3, buf, wbuf, EINA_FALSE); eina_strbuf_append_char(buf, '\n'); if (pdesc) { _indent_line(buf, indent); eina_strbuf_append(buf, " *\n"); curl = _indent_line(buf, indent); eina_strbuf_append(buf, " * "); _append_section(state, pdesc, indent, curl + 3, buf, wbuf, EINA_FALSE); eina_strbuf_append_char(buf, '\n'); } if (par || rdoc || since) { _indent_line(buf, indent); eina_strbuf_append(buf, " *\n"); } } if (!eolian_function_is_class(fid)) { _indent_line(buf, indent); eina_strbuf_append(buf, " * @param[in] obj The object.\n"); if (!par && (rdoc || since)) { _indent_line(buf, indent); eina_strbuf_append(buf, " *\n"); } } while (par) { const Eolian_Documentation *adoc = eolian_parameter_documentation_get(par); curl = _indent_line(buf, indent); Eolian_Parameter_Dir dir = EOLIAN_OUT_PARAM; if (!force_out) dir = eolian_parameter_direction_get(par); switch (dir) { case EOLIAN_OUT_PARAM: eina_strbuf_append(buf, " * @param[out] "); curl += sizeof(" * @param[out] ") - 1; break; case EOLIAN_INOUT_PARAM: eina_strbuf_append(buf, " * @param[in,out] "); curl += sizeof(" * @param[in,out] ") - 1; break; default: eina_strbuf_append(buf, " * @param[in] "); curl += sizeof(" * @param[in] ") - 1; break; } const char *nm = eolian_parameter_name_get(par); eina_strbuf_append(buf, nm); curl += strlen(nm); if (adoc) { eina_strbuf_append_char(buf, ' '); curl += 1; _append_section(state, eolian_documentation_summary_get(adoc), indent, curl, buf, wbuf, EINA_FALSE); } eina_strbuf_append_char(buf, '\n'); if (!eina_iterator_next(itr, (void**)&par)) { par = NULL; if (vpar) { eina_iterator_free(itr); itr = vitr; par = vpar; vitr = NULL; vpar = NULL; if (ftype == EOLIAN_PROP_GET) force_out = EINA_TRUE; } } if (!par && (rdoc || since)) { _indent_line(buf, indent); eina_strbuf_append(buf, " *\n"); } } eina_iterator_free(itr); if (rdoc) { curl = _indent_line(buf, indent); eina_strbuf_append(buf, " * @return "); curl += sizeof(" * @return ") - 1; _append_section(state, eolian_documentation_summary_get(rdoc), indent, curl, buf, wbuf, EINA_FALSE); eina_strbuf_append_char(buf, '\n'); if (since) { _indent_line(buf, indent); eina_strbuf_append(buf, " *\n"); } } if (since) { curl = _indent_line(buf, indent); eina_strbuf_append(buf, " * @since "); eina_strbuf_append(buf, since); eina_strbuf_append_char(buf, '\n'); } _indent_line(buf, indent); eina_strbuf_append(buf, " *\n"); _indent_line(buf, indent); _append_group(buf, _sanitize_group(group), indent); eina_strbuf_append(buf, " */"); eina_strbuf_free(wbuf); return buf; }