diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index 50a8bf7a..b90601fb 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.cc +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.cc @@ -35,16 +35,18 @@ void generator::generate_link( if (e.file().empty()) return; + auto context = element_context(e); + if (!m_config.generate_links().link.empty()) { ostr << " [[["; - inja::render_to( - ostr, m_config.generate_links().link, element_context(e)); + ostr << env().render( + std::string_view{m_config.generate_links().link}, context); } if (!m_config.generate_links().tooltip.empty()) { ostr << "{"; - inja::render_to( - ostr, m_config.generate_links().tooltip, element_context(e)); + ostr << env().render( + std::string_view{m_config.generate_links().tooltip}, context); ostr << "}"; } ostr << "]]]"; diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 5cadce54..aea10efe 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -248,6 +248,9 @@ void translation_unit_visitor::process_namespace( p->set_name(e.name()); p->set_namespace(package_parent); + if (e.comment().has_value()) + p->set_comment(e.comment().value()); + if (e.location().has_value()) { p->set_file(e.location().value().file); p->set_line(e.location().value().line); @@ -290,6 +293,9 @@ void translation_unit_visitor::process_enum_declaration( e.set_name(enm.name()); e.set_namespace(ctx.get_namespace()); + if (enm.comment().has_value()) + e.set_comment(enm.comment().value()); + if (enm.location().has_value()) { e.set_file(enm.location().value().file); e.set_line(enm.location().value().line); @@ -347,8 +353,10 @@ void translation_unit_visitor::process_class_declaration( c.set_name(cls.name()); c.set_namespace(ctx.get_namespace()); - if (cls.comment().has_value()) + if (cls.comment().has_value()) { + c.set_comment(cls.comment().value()); c.add_decorators(decorators::parse(cls.comment().value())); + } // Process class documentation comment if (cppast::is_templated(cls)) { @@ -879,6 +887,9 @@ void translation_unit_visitor::process_method( m.is_defaulted(false); m.is_static(false); + if (mf.comment().has_value()) + m.set_comment(mf.comment().value()); + if (mf.location().has_value()) { m.set_file(mf.location().value().file); m.set_line(mf.location().value().line); @@ -920,6 +931,9 @@ void translation_unit_visitor::process_template_method( m.is_defaulted(false); m.is_static(false); + if (mf.comment().has_value()) + m.set_comment(mf.comment().value()); + if (mf.location().has_value()) { m.set_file(mf.location().value().file); m.set_line(mf.location().value().line); @@ -956,6 +970,9 @@ void translation_unit_visitor::process_static_method( m.is_defaulted(false); m.is_static(true); + if (mf.comment().has_value()) + m.set_comment(mf.comment().value()); + if (mf.location().has_value()) { m.set_file(mf.location().value().file); m.set_line(mf.location().value().line); @@ -987,6 +1004,9 @@ void translation_unit_visitor::process_constructor( m.is_defaulted(false); m.is_static(true); + if (mf.comment().has_value()) + m.set_comment(mf.comment().value()); + if (mf.location().has_value()) { m.set_file(mf.location().value().file); m.set_line(mf.location().value().line); @@ -1016,6 +1036,9 @@ void translation_unit_visitor::process_destructor( m.is_defaulted(false); m.is_static(true); + if (mf.comment().has_value()) + m.set_comment(mf.comment().value()); + if (mf.location().has_value()) { m.set_file(mf.location().value().file); m.set_line(mf.location().value().line); diff --git a/src/common/generators/plantuml/generator.h b/src/common/generators/plantuml/generator.h index bc289ee7..15507af2 100644 --- a/src/common/generators/plantuml/generator.h +++ b/src/common/generators/plantuml/generator.h @@ -45,6 +45,7 @@ public: , m_model{model} { init_context(); + init_env(); } virtual ~generator() = default; @@ -64,17 +65,23 @@ public: void generate_link(std::ostream &ostr, const model::element &e) const; +protected: const inja::json &context() const; + inja::Environment &env() const; + template inja::json element_context(const E &e) const; private: void init_context(); + void init_env(); + protected: ConfigType &m_config; DiagramType &m_model; inja::json m_context; + mutable inja::Environment m_env; }; template @@ -90,17 +97,10 @@ const inja::json &generator::context() const return m_context; } -template void generator::init_context() +template +inja::Environment &generator::env() const { - if (m_config.git) { - m_context["git"]["branch"] = m_config.git().branch; - m_context["git"]["revision"] = m_config.git().revision; - m_context["git"]["commit"] = m_config.git().commit; - m_context["git"]["toplevel"] = m_config.git().toplevel; - } - - m_context["diagram"]["name"] = m_config.name; - m_context["diagram"]["type"] = to_string(m_config.type()); + return m_env; } template @@ -125,6 +125,13 @@ inja::json generator::element_context(const E &e) const ctx["element"]["source"]["line"] = e.line(); } + if (e.comment().has_value()) { + std::string c = e.comment().value(); + if (!c.empty()) { + ctx["element"]["comment"] = util::trim(c); + } + } + return ctx; } @@ -197,16 +204,18 @@ void generator::generate_link( if (!m_config.generate_links().link.empty()) { ostr << " [["; - inja::render_to( - ostr, m_config.generate_links().link, element_context(e)); + ostr << env().render(std::string_view{m_config.generate_links().link}, + element_context(e)); } if (!m_config.generate_links().tooltip.empty()) { ostr << "{"; - inja::render_to( - ostr, m_config.generate_links().tooltip, element_context(e)); + ostr << env().render( + std::string_view{m_config.generate_links().tooltip}, + element_context(e)); ostr << "}"; } + ostr << "]]"; } @@ -244,4 +253,53 @@ DiagramModel generate(const cppast::libclang_compilation_database &db, return std::move(d); } +template void generator::init_context() +{ + if (m_config.git) { + m_context["git"]["branch"] = m_config.git().branch; + m_context["git"]["revision"] = m_config.git().revision; + m_context["git"]["commit"] = m_config.git().commit; + m_context["git"]["toplevel"] = m_config.git().toplevel; + } + + m_context["diagram"]["name"] = m_config.name; + m_context["diagram"]["type"] = to_string(m_config.type()); +} + +template void generator::init_env() +{ + // Add basic string functions to inja environment + m_env.add_callback("empty", 1, [](inja::Arguments &args) { + return args.at(0)->get().empty(); + }); + + m_env.add_callback("ltrim", 1, [](inja::Arguments &args) { + return util::ltrim(args.at(0)->get()); + }); + + m_env.add_callback("rtrim", 1, [](inja::Arguments &args) { + return util::rtrim(args.at(0)->get()); + }); + + m_env.add_callback("trim", 1, [](inja::Arguments &args) { + return util::trim(args.at(0)->get()); + }); + + m_env.add_callback("abbrv", 2, [](inja::Arguments &args) { + return util::abbreviate( + args.at(0)->get(), args.at(1)->get()); + }); + + m_env.add_callback("replace", 3, [](inja::Arguments &args) { + std::string result = args[0]->get(); + std::regex pattern(args[1]->get()); + return std::regex_replace(result, pattern, args[2]->get()); + }); + + m_env.add_callback("split", 2, [](inja::Arguments &args) { + return util::split( + args[0]->get(), args[1]->get()); + }); +} + } diff --git a/src/common/model/decorated_element.cc b/src/common/model/decorated_element.cc index b828fec1..31888721 100644 --- a/src/common/model/decorated_element.cc +++ b/src/common/model/decorated_element.cc @@ -87,4 +87,11 @@ void decorated_element::append(const decorated_element &de) decorators_.push_back(d); } } + +std::optional decorated_element::comment() const +{ + return comment_; +} + +void decorated_element::set_comment(const std::string &c) { comment_ = c; } } diff --git a/src/common/model/decorated_element.h b/src/common/model/decorated_element.h index fad15119..1865b05e 100644 --- a/src/common/model/decorated_element.h +++ b/src/common/model/decorated_element.h @@ -22,6 +22,7 @@ #include "decorators/decorators.h" #include +#include #include #include @@ -45,8 +46,13 @@ public: void append(const decorated_element &de); + std::optional comment() const; + + void set_comment(const std::string &c); + private: std::vector> decorators_; + std::optional comment_; }; } diff --git a/tests/t00002/t00002.cc b/tests/t00002/t00002.cc index 6eb8a5de..0e485421 100644 --- a/tests/t00002/t00002.cc +++ b/tests/t00002/t00002.cc @@ -3,30 +3,50 @@ namespace clanguml { namespace t00002 { +/// This is class A class A { public: + /// Abstract foo_a virtual void foo_a() = 0; + /// Abstract foo_c virtual void foo_c() = 0; }; +/// This is class B class B : public A { public: virtual void foo_a() override { } }; +/// @brief This is class C - class C has a long comment +/// +/// Vivamus integer non suscipit taciti mus etiam at primis tempor sagittis sit, +/// euismod libero facilisi aptent elementum felis blandit cursus gravida sociis +/// erat ante, eleifend lectus nullam dapibus netus feugiat curae curabitur est +/// ad. class C : public A { public: + /// Do nothing unless override is provided virtual void foo_c() override { } }; +/// This is class D +/// which is a little like B +/// and a little like C class D : public B, public C { public: + /** + * Forward foo_a + */ void foo_a() override { for (auto a : as) a->foo_a(); } + /** + * Forward foo_c + */ void foo_c() override { for (auto a : as) @@ -34,6 +54,7 @@ public: } private: + /// All the A pointers std::vector as; }; @@ -44,12 +65,18 @@ private: // class E : virtual public B, virtual public C { public: + /// + /// Forward foo_a + /// void foo_a() override { for (auto a : as) a->foo_a(); } + /// + /// Forward foo_c + /// void foo_c() override { for (auto a : as) @@ -57,6 +84,7 @@ public: } private: + /// All the A pointers std::vector as; }; } diff --git a/tests/test_cases.cc b/tests/test_cases.cc index e8ab2337..f8a0c6cf 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -24,9 +24,8 @@ void inject_diagram_options(std::shared_ptr diagram) { // Inject links config to all test cases clanguml::config::generate_links_config links_config{ - "https://github.com/bkryza/clang-uml/blob/{{ git.commit }}/{{ " - "element.source.path }}#L{{ element.source.line }}", - "{{ element.name }}"}; + R"(https://github.com/bkryza/clang-uml/blob/{{ git.commit }}/{{ element.source.path }}#L{{ element.source.line }})", + R"({% if "comment" in element %}{{ abbrv(trim(replace(element.comment, "\n+", " ")), 256) }}{% else %}{{ element.name }}{% endif %})"}; diagram->generate_links.set(links_config); }