Fixed mermaid verbatim directives handling

This commit is contained in:
Bartek Kryza
2023-09-10 12:14:11 +02:00
parent 9a6def801c
commit 4a19c8ba23
15 changed files with 178 additions and 21 deletions

View File

@@ -33,6 +33,11 @@ generator::generator(diagram_config &config, diagram_model &model)
{ {
} }
void generator::generate_diagram_type(std::ostream &ostr) const
{
ostr << "classDiagram\n";
}
void generator::generate_alias( void generator::generate_alias(
const common::model::element &c, std::ostream &ostr) const const common::model::element &c, std::ostream &ostr) const
{ {
@@ -696,8 +701,6 @@ void generator::generate_relationships(
void generator::generate_diagram(std::ostream &ostr) const void generator::generate_diagram(std::ostream &ostr) const
{ {
ostr << "classDiagram\n";
generate_top_level_elements(ostr); generate_top_level_elements(ostr);
generate_groups(ostr); generate_groups(ostr);

View File

@@ -79,6 +79,13 @@ public:
*/ */
void generate_diagram(std::ostream &ostr) const override; void generate_diagram(std::ostream &ostr) const override;
/**
* @brief Generate the diagram type
*
* @param ostr Output stream
*/
void generate_diagram_type(std::ostream &ostr) const override;
/** /**
* @brief In a nested diagram, generate the top level elements. * @brief In a nested diagram, generate the top level elements.
* *

View File

@@ -107,6 +107,16 @@ public:
void generate_mermaid_directives( void generate_mermaid_directives(
std::ostream &ostr, const std::vector<std::string> &directives) const; std::ostream &ostr, const std::vector<std::string> &directives) const;
/**
* @brief Generate the diagram type
*
* This method must be overriden for each diagram type (e.g. it renders
* a single line `classDiagram` for Mermaid class diagrams.
*
* @param ostr Output stream
*/
virtual void generate_diagram_type(std::ostream &ostr) const = 0;
/** /**
* @brief Generate diagram notes * @brief Generate diagram notes
* *
@@ -228,13 +238,13 @@ void generator<C, D>::generate(std::ostream &ostr) const
update_context(); update_context();
// generate_mermaid_diagram_type(ostr, config); generate_diagram_type(ostr);
generate_mermaid_directives(ostr, config.puml().before); generate_mermaid_directives(ostr, config.mermaid().before);
generate_diagram(ostr); generate_diagram(ostr);
generate_mermaid_directives(ostr, config.puml().after); generate_mermaid_directives(ostr, config.mermaid().after);
generate_metadata(ostr); generate_metadata(ostr);
} }
@@ -304,6 +314,61 @@ template <typename C, typename D>
void generator<C, D>::generate_mermaid_directives( void generator<C, D>::generate_mermaid_directives(
std::ostream &ostr, const std::vector<std::string> &directives) const std::ostream &ostr, const std::vector<std::string> &directives) const
{ {
const auto &config = generators::generator<C, D>::config();
const auto &model = generators::generator<C, D>::model();
using common::model::namespace_;
for (const auto &d : directives) {
try {
// Render the directive with template engine first
std::string directive{env().render(std::string_view{d}, context())};
// Now search for alias `@A()` directives in the text
// (this is deprecated)
std::tuple<std::string, size_t, size_t> alias_match;
while (util::find_element_alias(directive, alias_match)) {
const auto full_name =
config.using_namespace() | std::get<0>(alias_match);
auto element_opt = model.get(full_name.to_string());
if (element_opt)
directive.replace(std::get<1>(alias_match),
std::get<2>(alias_match), element_opt.value().alias());
else {
LOG_ERROR("Cannot find clang-uml alias for element {}",
full_name.to_string());
directive.replace(std::get<1>(alias_match),
std::get<2>(alias_match), "UNKNOWN_ALIAS");
}
}
ostr << indent(1) << directive << '\n';
}
catch (const clanguml::error::uml_alias_missing &e) {
LOG_ERROR(
"Failed to render MermaidJS directive due to unresolvable "
"alias: {}",
e.what());
}
catch (const inja::json::parse_error &e) {
LOG_ERROR("Failed to parse Jinja template: {}", d);
}
catch (const inja::json::exception &e) {
LOG_ERROR("Failed to render MermaidJS directive: \n{}\n due to: {}",
d, e.what());
}
catch (const std::regex_error &e) {
LOG_ERROR("Failed to render MermaidJS directive: \n{}\n due to "
"std::regex_error: {}",
d, e.what());
}
catch (const std::exception &e) {
LOG_ERROR("Failed to render PlantUML directive: \n{}\n due to: {}",
d, e.what());
}
}
} }
template <typename C, typename D> template <typename C, typename D>

View File

@@ -42,6 +42,7 @@ using clanguml::config::include_diagram;
using clanguml::config::layout_hint; using clanguml::config::layout_hint;
using clanguml::config::location_t; using clanguml::config::location_t;
using clanguml::config::member_order_t; using clanguml::config::member_order_t;
using clanguml::config::mermaid;
using clanguml::config::method_arguments; using clanguml::config::method_arguments;
using clanguml::config::method_type; using clanguml::config::method_type;
using clanguml::config::package_diagram; using clanguml::config::package_diagram;
@@ -389,6 +390,18 @@ template <> struct convert<plantuml> {
} }
}; };
template <> struct convert<mermaid> {
static bool decode(const Node &node, mermaid &rhs)
{
if (node["before"])
rhs.before = node["before"].as<decltype(rhs.before)>();
if (node["after"])
rhs.after = node["after"].as<decltype(rhs.after)>();
return true;
}
};
template <> struct convert<string_or_regex> { template <> struct convert<string_or_regex> {
static bool decode(const Node &node, string_or_regex &rhs) static bool decode(const Node &node, string_or_regex &rhs)
{ {
@@ -531,6 +544,7 @@ template <typename T> bool decode_diagram(const Node &node, T &rhs)
get_option(node, rhs.include); get_option(node, rhs.include);
get_option(node, rhs.exclude); get_option(node, rhs.exclude);
get_option(node, rhs.puml); get_option(node, rhs.puml);
get_option(node, rhs.mermaid);
get_option(node, rhs.git); get_option(node, rhs.git);
get_option(node, rhs.generate_links); get_option(node, rhs.generate_links);
get_option(node, rhs.type_aliases); get_option(node, rhs.type_aliases);
@@ -762,6 +776,7 @@ template <> struct convert<config> {
get_option(node, rhs.remove_compile_flags); get_option(node, rhs.remove_compile_flags);
get_option(node, rhs.include_relations_also_as_members); get_option(node, rhs.include_relations_also_as_members);
get_option(node, rhs.puml); get_option(node, rhs.puml);
get_option(node, rhs.mermaid);
get_option(node, rhs.generate_method_arguments); get_option(node, rhs.generate_method_arguments);
get_option(node, rhs.generate_packages); get_option(node, rhs.generate_packages);
get_option(node, rhs.package_type); get_option(node, rhs.package_type);

View File

@@ -29,6 +29,11 @@ generator::generator(diagram_config &config, diagram_model &model)
{ {
} }
void generator::generate_diagram_type(std::ostream &ostr) const
{
ostr << "flowchart\n";
}
void generator::generate_relationships( void generator::generate_relationships(
const source_file &f, std::ostream &ostr) const const source_file &f, std::ostream &ostr) const
{ {
@@ -114,8 +119,6 @@ void generator::generate_notes(
void generator::generate_diagram(std::ostream &ostr) const void generator::generate_diagram(std::ostream &ostr) const
{ {
ostr << "flowchart\n";
// Generate files and folders // Generate files and folders
util::for_each(model(), [this, &ostr](const auto &f) { util::for_each(model(), [this, &ostr](const auto &f) {
generate(dynamic_cast<source_file &>(*f), ostr); generate(dynamic_cast<source_file &>(*f), ostr);

View File

@@ -64,6 +64,13 @@ public:
*/ */
void generate_diagram(std::ostream &ostr) const override; void generate_diagram(std::ostream &ostr) const override;
/**
* @brief Generate the diagram type
*
* @param ostr Output stream
*/
void generate_diagram_type(std::ostream &ostr) const override;
/** /**
* @brief Generate relationships originating from source_file `f` * @brief Generate relationships originating from source_file `f`
* *

View File

@@ -30,6 +30,11 @@ generator::generator(diagram_config &config, diagram_model &model)
{ {
} }
void generator::generate_diagram_type(std::ostream &ostr) const
{
ostr << "flowchart\n";
}
void generator::generate_relationships( void generator::generate_relationships(
const package &p, std::ostream &ostr) const const package &p, std::ostream &ostr) const
{ {
@@ -144,8 +149,6 @@ void generator::generate_notes(
void generator::generate_diagram(std::ostream &ostr) const void generator::generate_diagram(std::ostream &ostr) const
{ {
ostr << "flowchart\n";
for (const auto &p : model()) { for (const auto &p : model()) {
auto &pkg = dynamic_cast<package &>(*p); auto &pkg = dynamic_cast<package &>(*p);
if (model().should_include(pkg)) { if (model().should_include(pkg)) {

View File

@@ -66,6 +66,13 @@ public:
*/ */
void generate_diagram(std::ostream &ostr) const override; void generate_diagram(std::ostream &ostr) const override;
/**
* @brief Generate the diagram type
*
* @param ostr Output stream
*/
void generate_diagram_type(std::ostream &ostr) const override;
/** /**
* @brief Generate relationships originating from package `p` * @brief Generate relationships originating from package `p`
* *

View File

@@ -48,6 +48,11 @@ generator::generator(
{ {
} }
void generator::generate_diagram_type(std::ostream &ostr) const
{
ostr << "sequenceDiagram\n";
}
void generator::generate_call(const message &m, std::ostream &ostr) const void generator::generate_call(const message &m, std::ostream &ostr) const
{ {
const auto &from = model().get_participant<model::participant>(m.from()); const auto &from = model().get_participant<model::participant>(m.from());
@@ -389,8 +394,6 @@ void generator::generate_diagram(std::ostream &ostr) const
{ {
model().print(); model().print();
ostr << "sequenceDiagram\n";
if (config().participants_order.has_value) { if (config().participants_order.has_value) {
for (const auto &p : config().participants_order()) { for (const auto &p : config().participants_order()) {
LOG_DBG("Pregenerating participant {}", p); LOG_DBG("Pregenerating participant {}", p);

View File

@@ -60,6 +60,13 @@ public:
*/ */
void generate_diagram(std::ostream &ostr) const override; void generate_diagram(std::ostream &ostr) const override;
/**
* @brief Generate the diagram type
*
* @param ostr Output stream
*/
void generate_diagram_type(std::ostream &ostr) const override;
/** /**
* @brief Generate sequence diagram message. * @brief Generate sequence diagram message.
* *

View File

@@ -96,4 +96,10 @@ TEST_CASE("t20006", "[test-case][sequence]")
save_json(config.output_directory(), diagram->name + ".json", j); save_json(config.output_directory(), diagram->name + ".json", j);
} }
{
auto mmd = generate_sequence_mermaid(diagram, *model);
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
}
} }

View File

@@ -121,4 +121,10 @@ TEST_CASE("t20012", "[test-case][sequence]")
save_json(config.output_directory(), diagram->name + ".json", j); save_json(config.output_directory(), diagram->name + ".json", j);
} }
{
auto mmd = generate_sequence_mermaid(diagram, *model);
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
}
} }

View File

@@ -18,3 +18,15 @@ diagrams:
- 'class "Boo" as C_002' - 'class "Boo" as C_002'
- 'class C_002 {' - 'class C_002 {'
- '}' - '}'
mermaid:
before:
- 'class C_001["Foo"]'
- 'class C_001 {'
- ' +int value'
- '}'
- 'C_001 <|-- ArrayList'
- 'note for C_001 "This is a very important class."'
- 'note "This is a\nfloating note"'
- 'class C_002["Boo"]'
- 'class C_002 {'
- '}'

View File

@@ -26,6 +26,7 @@ TEST_CASE("t90000", "[test-case][config]")
REQUIRE(model->name() == "t90000_class"); REQUIRE(model->name() == "t90000_class");
{
auto puml = generate_class_puml(diagram, *model); auto puml = generate_class_puml(diagram, *model);
AliasMatcher _A(puml); AliasMatcher _A(puml);
@@ -36,4 +37,19 @@ TEST_CASE("t90000", "[test-case][config]")
REQUIRE_THAT(puml, IsClass(_A("Boo"))); REQUIRE_THAT(puml, IsClass(_A("Boo")));
save_puml(config.output_directory(), diagram->name + ".puml", puml); save_puml(config.output_directory(), diagram->name + ".puml", puml);
}
{
auto j = generate_class_json(diagram, *model);
using namespace json;
save_json(config.output_directory(), diagram->name + ".json", j);
}
{
auto mmd = generate_class_mermaid(diagram, *model);
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
}
} }

View File

@@ -282,7 +282,6 @@ void save_puml(const std::string &path, const std::string &filename,
const std::string &puml) const std::string &puml)
{ {
std::filesystem::path p{path}; std::filesystem::path p{path};
p /= "puml";
p /= filename; p /= filename;
save_diagram(p, puml); save_diagram(p, puml);
} }
@@ -291,7 +290,6 @@ void save_json(const std::string &path, const std::string &filename,
const nlohmann::json &j) const nlohmann::json &j)
{ {
std::filesystem::path p{path}; std::filesystem::path p{path};
p /= "json";
p /= filename; p /= filename;
save_diagram(p, j); save_diagram(p, j);
} }
@@ -300,7 +298,6 @@ void save_mermaid(const std::string &path, const std::string &filename,
const std::string &mmd) const std::string &mmd)
{ {
std::filesystem::path p{path}; std::filesystem::path p{path};
p /= "mermaid";
p /= filename; p /= filename;
save_diagram(p, mmd); save_diagram(p, mmd);
} }