From edba23303074cc277d3121a5f81943aaa4de0959 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sat, 4 Feb 2023 16:30:12 +0100 Subject: [PATCH] Added support for 'together' layout hint in package diagrams --- src/config/config.cc | 2 +- src/config/config.h | 13 ++--- .../plantuml/package_diagram_generator.cc | 52 +++++++++++++++++-- .../plantuml/package_diagram_generator.h | 7 +++ tests/t30009/.clang-uml | 17 ++++++ tests/t30009/t30009.cc | 22 ++++++++ tests/t30009/test_case.h | 46 ++++++++++++++++ tests/test_cases.cc | 1 + tests/test_cases.yaml | 3 ++ 9 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 tests/t30009/.clang-uml create mode 100644 tests/t30009/t30009.cc create mode 100644 tests/t30009/test_case.h diff --git a/src/config/config.cc b/src/config/config.cc index e408fbc3..367da492 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -142,7 +142,7 @@ std::vector diagram::get_translation_units() const return translation_units; } -std::optional class_diagram::get_together_group( +std::optional diagram::get_together_group( const std::string &full_name) const { const auto relative_name = using_namespace().relative(full_name); diff --git a/src/config/config.h b/src/config/config.h index 977116de..e9cd7d72 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -148,6 +148,7 @@ struct inheritable_diagram_options { option generate_packages{"generate_packages", false}; option generate_links{"generate_links"}; option git{"git"}; + option layout{"layout"}; option base_directory{"__parent_path"}; option relative_to{"relative_to"}; option generate_system_headers{"generate_system_headers", false}; @@ -172,6 +173,9 @@ struct diagram : public inheritable_diagram_options { std::vector get_translation_units() const; + std::optional get_together_group( + const std::string &full_name) const; + void initialize_type_aliases(); std::string name; @@ -182,11 +186,6 @@ struct class_diagram : public diagram { common::model::diagram_t type() const override; - option layout{"layout"}; - - std::optional get_together_group( - const std::string &full_name) const; - void initialize_relationship_hints(); }; @@ -202,16 +201,12 @@ struct package_diagram : public diagram { ~package_diagram() override = default; common::model::diagram_t type() const override; - - option layout{"layout"}; }; struct include_diagram : public diagram { ~include_diagram() override = default; common::model::diagram_t type() const override; - - option layout{"layout"}; }; struct config : public inheritable_diagram_options { diff --git a/src/package_diagram/generators/plantuml/package_diagram_generator.cc b/src/package_diagram/generators/plantuml/package_diagram_generator.cc index 244f65f6..b97ebe5c 100644 --- a/src/package_diagram/generators/plantuml/package_diagram_generator.cc +++ b/src/package_diagram/generators/plantuml/package_diagram_generator.cc @@ -24,6 +24,7 @@ namespace clanguml::package_diagram::generators::plantuml { generator::generator(diagram_config &config, diagram_model &model) : common_generator{config, model} + , together_group_stack_{false} { } @@ -62,6 +63,8 @@ void generator::generate(const package &p, std::ostream &ostr) const { LOG_DBG("Generating package {}", p.name()); + together_group_stack_.enter(); + const auto &uns = m_config.using_namespace(); // Don't generate packages from namespaces filtered out by @@ -84,15 +87,29 @@ void generator::generate(const package &p, std::ostream &ostr) const } for (const auto &subpackage : p) { - if (m_model.should_include(dynamic_cast(*subpackage))) - generate(dynamic_cast(*subpackage), ostr); + auto &pkg = dynamic_cast(*subpackage); + if (m_model.should_include(pkg)) { + auto together_group = + m_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 << "}" << '\n'; } generate_notes(ostr, p); + + together_group_stack_.leave(); } void generator::generate(std::ostream &ostr) const @@ -104,10 +121,22 @@ void generator::generate(std::ostream &ostr) const generate_plantuml_directives(ostr, m_config.puml().before); for (const auto &p : m_model) { - if (m_model.should_include(dynamic_cast(*p))) - generate(dynamic_cast(*p), ostr); + auto &pkg = dynamic_cast(*p); + if (m_model.should_include(pkg)) { + auto together_group = + m_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 : m_model) { if (m_model.should_include(dynamic_cast(*p))) @@ -120,4 +149,19 @@ void generator::generate(std::ostream &ostr) const ostr << "@enduml" << '\n'; } + +void generator::generate_groups(std::ostream &ostr) const +{ + for (const auto &[group_name, group_elements] : + together_group_stack_.get_current_groups()) { + ostr << "together {\n"; + + for (auto *pkg : group_elements) { + generate(*pkg, ostr); + } + + ostr << "}\n"; + } +} + } // namespace clanguml::package_diagram::generators::plantuml diff --git a/src/package_diagram/generators/plantuml/package_diagram_generator.h b/src/package_diagram/generators/plantuml/package_diagram_generator.h index a8165aaa..9dd35f69 100644 --- a/src/package_diagram/generators/plantuml/package_diagram_generator.h +++ b/src/package_diagram/generators/plantuml/package_diagram_generator.h @@ -17,6 +17,7 @@ */ #pragma once +#include "common/generators/nested_element_stack.h" #include "common/generators/plantuml/generator.h" #include "common/model/package.h" #include "common/model/relationship.h" @@ -58,6 +59,12 @@ public: void generate(const package &e, std::ostream &ostr) const; void generate(std::ostream &ostr) const override; + + void generate_groups(std::ostream &ostr) const; + +private: + mutable common::generators::nested_element_stack + together_group_stack_; }; } // namespace plantuml diff --git a/tests/t30009/.clang-uml b/tests/t30009/.clang-uml new file mode 100644 index 00000000..9452c160 --- /dev/null +++ b/tests/t30009/.clang-uml @@ -0,0 +1,17 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t30009_package: + type: package + glob: + - ../../tests/t30009/t30009.cc + include: + namespaces: + - clanguml::t30009 + using_namespace: + - clanguml::t30009 + layout: + One::A: + - together: [One::C] + Two::B: + - together: [Two::C, Two::D] \ No newline at end of file diff --git a/tests/t30009/t30009.cc b/tests/t30009/t30009.cc new file mode 100644 index 00000000..58cb0854 --- /dev/null +++ b/tests/t30009/t30009.cc @@ -0,0 +1,22 @@ +namespace clanguml::t30009 { +namespace One { +namespace A { +} +namespace B { +} +namespace C { +} +namespace D { +} +} +namespace Two { +namespace A { +} +namespace B { +} +namespace C { +} +namespace D { +} +} +} diff --git a/tests/t30009/test_case.h b/tests/t30009/test_case.h new file mode 100644 index 00000000..90343059 --- /dev/null +++ b/tests/t30009/test_case.h @@ -0,0 +1,46 @@ +/** + * tests/t30009/test_case.h + * + * Copyright (c) 2021-2023 Bartek Kryza + * + * 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. + */ + +TEST_CASE("t30009", "[test-case][package]") +{ + auto [config, db] = load_config("t30009"); + + auto diagram = config.diagrams["t30009_package"]; + + REQUIRE(diagram->name == "t30009_package"); + + auto model = generate_package_diagram(*db, diagram); + + REQUIRE(model->name() == "t30009_package"); + + auto puml = generate_package_puml(diagram, *model); + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + // Check if all packages exist + REQUIRE_THAT(puml, IsPackage("One")); + REQUIRE_THAT(puml, IsPackage("Two")); + REQUIRE_THAT(puml, IsPackage("A")); + REQUIRE_THAT(puml, IsPackage("B")); + REQUIRE_THAT(puml, IsPackage("C")); + REQUIRE_THAT(puml, IsPackage("D")); + + save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml); +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 48770f20..91a64892 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -297,6 +297,7 @@ using namespace clanguml::test::matchers; #include "t30006/test_case.h" #include "t30007/test_case.h" #include "t30008/test_case.h" +#include "t30009/test_case.h" /// /// Include diagram tests diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 9ef26121..12de8099 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -272,6 +272,9 @@ test_cases: - name: t30008 title: Dependants and dependencies package diagram filter test description: + - name: t30009 + title: Together layout hint test + description: Include diagrams: - name: t40001 title: Basic include graph diagram test case