Added initial support for MermaidJS package diagrams
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
#include "include_diagram/generators/plantuml/include_diagram_generator.h"
|
||||
#include "indicators/indicators.hpp"
|
||||
#include "package_diagram/generators/json/package_diagram_generator.h"
|
||||
#include "package_diagram/generators/mermaid/package_diagram_generator.h"
|
||||
#include "package_diagram/generators/plantuml/package_diagram_generator.h"
|
||||
#include "sequence_diagram/generators/json/sequence_diagram_generator.h"
|
||||
#include "sequence_diagram/generators/mermaid/sequence_diagram_generator.h"
|
||||
@@ -174,11 +175,11 @@ struct diagram_generator_t<clanguml::config::sequence_diagram,
|
||||
mermaid_generator_tag> {
|
||||
using type = clanguml::sequence_diagram::generators::mermaid::generator;
|
||||
};
|
||||
// template <>
|
||||
// struct diagram_generator_t<clanguml::config::package_diagram,
|
||||
// mermaid_generator_tag> {
|
||||
// using type = clanguml::package_diagram::generators::mermaid::generator;
|
||||
// };
|
||||
template <>
|
||||
struct diagram_generator_t<clanguml::config::package_diagram,
|
||||
mermaid_generator_tag> {
|
||||
using type = clanguml::package_diagram::generators::mermaid::generator;
|
||||
};
|
||||
// template <>
|
||||
// struct diagram_generator_t<clanguml::config::include_diagram,
|
||||
// mermaid_generator_tag> {
|
||||
|
||||
@@ -126,7 +126,7 @@ public:
|
||||
* @param ostr Output stream
|
||||
* @param element Element to which the note should be attached
|
||||
*/
|
||||
void generate_notes(
|
||||
virtual void generate_notes(
|
||||
std::ostream &ostr, const model::element &element) const;
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* @file src/package_diagram/generators/mermaid/package_diagram_generator.cc
|
||||
*
|
||||
* Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "package_diagram_generator.h"
|
||||
|
||||
#include "util/error.h"
|
||||
|
||||
namespace clanguml::package_diagram::generators::mermaid {
|
||||
|
||||
using clanguml::common::generators::mermaid::indent;
|
||||
|
||||
generator::generator(diagram_config &config, diagram_model &model)
|
||||
: common_generator<diagram_config, diagram_model>{config, model}
|
||||
, together_group_stack_{false}
|
||||
{
|
||||
}
|
||||
|
||||
void generator::generate_relationships(
|
||||
const package &p, std::ostream &ostr) const
|
||||
{
|
||||
LOG_DBG("Generating relationships for package {}", p.full_name(true));
|
||||
|
||||
// Generate this packages relationship
|
||||
if (model().should_include(relationship_t::kDependency)) {
|
||||
for (const auto &r : p.relationships()) {
|
||||
std::stringstream relstr;
|
||||
try {
|
||||
auto destination = model().to_alias(r.destination());
|
||||
if (!destination.empty()) {
|
||||
relstr << p.alias() << " -.-> " << destination << '\n';
|
||||
ostr << indent(1) << relstr.str();
|
||||
}
|
||||
}
|
||||
catch (error::uml_alias_missing &e) {
|
||||
LOG_DBG("=== Skipping dependency relation from {} to {} due "
|
||||
"to: {}",
|
||||
p.full_name(false), r.destination(), e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process it's subpackages relationships
|
||||
for (const auto &subpackage : p) {
|
||||
generate_relationships(
|
||||
dynamic_cast<const package &>(*subpackage), ostr);
|
||||
}
|
||||
}
|
||||
|
||||
void generator::generate(const package &p, std::ostream &ostr) const
|
||||
{
|
||||
LOG_DBG("Generating package {}", p.name());
|
||||
|
||||
together_group_stack_.enter();
|
||||
|
||||
const auto &uns = config().using_namespace();
|
||||
|
||||
// Don't generate packages from namespaces filtered out by
|
||||
// using_namespace
|
||||
if (!uns.starts_with({p.full_name(false)})) {
|
||||
ostr << indent(1) << "subgraph " << p.alias() << "[" << p.name()
|
||||
<< "]\n";
|
||||
|
||||
if (p.is_deprecated())
|
||||
ostr << indent(1) << "%% <<deprecated>>";
|
||||
}
|
||||
|
||||
for (const auto &subpackage : p) {
|
||||
auto &pkg = dynamic_cast<package &>(*subpackage);
|
||||
if (model().should_include(pkg)) {
|
||||
auto together_group =
|
||||
config().get_together_group(pkg.full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), &pkg);
|
||||
}
|
||||
else {
|
||||
generate(pkg, ostr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_groups(ostr);
|
||||
|
||||
if (!uns.starts_with({p.full_name(false)})) {
|
||||
ostr << indent(1) << "end" << '\n';
|
||||
}
|
||||
|
||||
generate_notes(ostr, p);
|
||||
|
||||
together_group_stack_.leave();
|
||||
}
|
||||
|
||||
void generator::generate_groups(std::ostream &ostr) const
|
||||
{
|
||||
for (const auto &[group_name, group_elements] :
|
||||
together_group_stack_.get_current_groups()) {
|
||||
ostr << indent(1) << "%% together group - not rendered in MermaidJS \n";
|
||||
|
||||
for (auto *pkg : group_elements) {
|
||||
generate(*pkg, ostr);
|
||||
}
|
||||
|
||||
ostr << indent(1) << "%% end together group\n";
|
||||
}
|
||||
}
|
||||
|
||||
void generator::generate_notes(
|
||||
std::ostream &ostr, const common::model::element &element) const
|
||||
{
|
||||
const auto &config =
|
||||
common_generator<diagram_config, diagram_model>::config();
|
||||
|
||||
for (const auto &decorator : element.decorators()) {
|
||||
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
|
||||
if (note && note->applies_to_diagram(config.name)) {
|
||||
auto note_id_str = fmt::format("N_{}", note_id_++);
|
||||
|
||||
ostr << indent(1) << note_id_str << "(" << note->text << ")\n";
|
||||
|
||||
ostr << indent(1) << note_id_str << "-.-" << element.alias()
|
||||
<< '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generator::generate_diagram(std::ostream &ostr) const
|
||||
{
|
||||
ostr << "flowchart\n";
|
||||
|
||||
for (const auto &p : model()) {
|
||||
auto &pkg = dynamic_cast<package &>(*p);
|
||||
if (model().should_include(pkg)) {
|
||||
auto together_group =
|
||||
config().get_together_group(pkg.full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), &pkg);
|
||||
}
|
||||
else {
|
||||
generate(pkg, ostr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_groups(ostr);
|
||||
|
||||
// Process package relationships
|
||||
for (const auto &p : model()) {
|
||||
if (model().should_include(dynamic_cast<package &>(*p)))
|
||||
generate_relationships(dynamic_cast<package &>(*p), ostr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace clanguml::package_diagram::generators::mermaid
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* @file src/package_diagram/generators/mermaid/package_diagram_generator.h
|
||||
*
|
||||
* Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/generators/mermaid/generator.h"
|
||||
#include "common/generators/nested_element_stack.h"
|
||||
#include "common/model/package.h"
|
||||
#include "common/model/relationship.h"
|
||||
#include "config/config.h"
|
||||
#include "package_diagram/model/diagram.h"
|
||||
#include "package_diagram/visitor/translation_unit_visitor.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace clanguml {
|
||||
namespace package_diagram {
|
||||
namespace generators {
|
||||
namespace mermaid {
|
||||
|
||||
using diagram_config = clanguml::config::package_diagram;
|
||||
using diagram_model = clanguml::package_diagram::model::diagram;
|
||||
|
||||
template <typename C, typename D>
|
||||
using common_generator = clanguml::common::generators::mermaid::generator<C, D>;
|
||||
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::relationship_t;
|
||||
using namespace clanguml::util;
|
||||
|
||||
/**
|
||||
* @brief Package diagram MermaidJS generator
|
||||
*/
|
||||
class generator : public common_generator<diagram_config, diagram_model> {
|
||||
public:
|
||||
generator(diagram_config &config, diagram_model &model);
|
||||
|
||||
using common_generator<diagram_config, diagram_model>::generate;
|
||||
|
||||
/**
|
||||
* @brief Main generator method.
|
||||
*
|
||||
* This method is called first and coordinates the entire diagram
|
||||
* generation.
|
||||
*
|
||||
* @param ostr Output stream.
|
||||
*/
|
||||
void generate_diagram(std::ostream &ostr) const override;
|
||||
|
||||
/**
|
||||
* @brief Generate relationships originating from package `p`
|
||||
*
|
||||
* @param p Diagram element
|
||||
* @param parent Output stream
|
||||
*/
|
||||
void generate_relationships(const package &p, std::ostream &ostr) const;
|
||||
|
||||
/**
|
||||
* @brief Generate diagram package element
|
||||
*
|
||||
* @param p Package diagram element
|
||||
* @param parent Output stream
|
||||
*/
|
||||
void generate(const package &e, std::ostream &ostr) const;
|
||||
|
||||
/**
|
||||
* @brief Generate package elements grouped using `together` MermaidJS tag
|
||||
*
|
||||
* @param ostr Output stream
|
||||
*/
|
||||
void generate_groups(std::ostream &ostr) const;
|
||||
|
||||
/**
|
||||
* @brief Generate notes attached to packages
|
||||
*
|
||||
* @param ostr Output stream
|
||||
* @param element Element with a note
|
||||
*/
|
||||
void generate_notes(std::ostream &ostr,
|
||||
const common::model::element &element) const override;
|
||||
|
||||
private:
|
||||
mutable unsigned long note_id_{0UL};
|
||||
mutable common::generators::nested_element_stack<common::model::package>
|
||||
together_group_stack_;
|
||||
};
|
||||
|
||||
} // namespace mermaid
|
||||
} // namespace generators
|
||||
} // namespace package_diagram
|
||||
} // namespace clanguml
|
||||
@@ -82,4 +82,10 @@ TEST_CASE("t30001", "[test-case][package]")
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto mmd = generate_package_mermaid(diagram, *model);
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,4 +121,10 @@ TEST_CASE("t30002", "[test-case][package]")
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto mmd = generate_package_mermaid(diagram, *model);
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,4 +63,10 @@ TEST_CASE("t30003", "[test-case][package]")
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto mmd = generate_package_mermaid(diagram, *model);
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,4 +58,10 @@ TEST_CASE("t30004", "[test-case][package]")
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto mmd = generate_package_mermaid(diagram, *model);
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,4 +65,10 @@ TEST_CASE("t30005", "[test-case][package]")
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto mmd = generate_package_mermaid(diagram, *model);
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,4 +59,10 @@ TEST_CASE("t30006", "[test-case][package]")
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto mmd = generate_package_mermaid(diagram, *model);
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,4 +63,10 @@ TEST_CASE("t30007", "[test-case][package]")
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto mmd = generate_package_mermaid(diagram, *model);
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,4 +77,10 @@ TEST_CASE("t30008", "[test-case][package]")
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto mmd = generate_package_mermaid(diagram, *model);
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,4 +64,10 @@ TEST_CASE("t30009", "[test-case][package]")
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto mmd = generate_package_mermaid(diagram, *model);
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
|
||||
}
|
||||
}
|
||||
@@ -57,4 +57,10 @@ TEST_CASE("t30010", "[test-case][package]")
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto mmd = generate_package_mermaid(diagram, *model);
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
|
||||
}
|
||||
}
|
||||
@@ -57,4 +57,10 @@ TEST_CASE("t30011", "[test-case][package]")
|
||||
|
||||
save_json(config.output_directory(), diagram->name + ".json", j);
|
||||
}
|
||||
|
||||
{
|
||||
auto mmd = generate_package_mermaid(diagram, *model);
|
||||
|
||||
save_mermaid(config.output_directory(), diagram->name + ".mmd", mmd);
|
||||
}
|
||||
}
|
||||
@@ -243,6 +243,14 @@ std::string generate_sequence_mermaid(
|
||||
config, model);
|
||||
}
|
||||
|
||||
std::string generate_package_mermaid(
|
||||
std::shared_ptr<clanguml::config::diagram> config,
|
||||
clanguml::package_diagram::model::diagram &model)
|
||||
{
|
||||
return detail::generate_diagram_mermaid<clanguml::config::package_diagram>(
|
||||
config, model);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void save_diagram(const std::filesystem::path &path, const T &diagram)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user