Added support for 'together' layout hint in package diagrams

This commit is contained in:
Bartek Kryza
2023-02-04 16:30:12 +01:00
parent 4423b14b62
commit edba233030
9 changed files with 149 additions and 14 deletions

View File

@@ -142,7 +142,7 @@ std::vector<std::string> diagram::get_translation_units() const
return translation_units;
}
std::optional<std::string> class_diagram::get_together_group(
std::optional<std::string> diagram::get_together_group(
const std::string &full_name) const
{
const auto relative_name = using_namespace().relative(full_name);

View File

@@ -148,6 +148,7 @@ struct inheritable_diagram_options {
option<bool> generate_packages{"generate_packages", false};
option<generate_links_config> generate_links{"generate_links"};
option<git_config> git{"git"};
option<layout_hints> layout{"layout"};
option<std::filesystem::path> base_directory{"__parent_path"};
option<std::filesystem::path> relative_to{"relative_to"};
option<bool> generate_system_headers{"generate_system_headers", false};
@@ -172,6 +173,9 @@ struct diagram : public inheritable_diagram_options {
std::vector<std::string> get_translation_units() const;
std::optional<std::string> 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_hints> layout{"layout"};
std::optional<std::string> 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_hints> layout{"layout"};
};
struct include_diagram : public diagram {
~include_diagram() override = default;
common::model::diagram_t type() const override;
option<layout_hints> layout{"layout"};
};
struct config : public inheritable_diagram_options {

View File

@@ -24,6 +24,7 @@ namespace clanguml::package_diagram::generators::plantuml {
generator::generator(diagram_config &config, diagram_model &model)
: common_generator<diagram_config, diagram_model>{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<package &>(*subpackage)))
generate(dynamic_cast<const package &>(*subpackage), ostr);
auto &pkg = dynamic_cast<package &>(*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<package &>(*p)))
generate(dynamic_cast<package &>(*p), ostr);
auto &pkg = dynamic_cast<package &>(*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<package &>(*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

View File

@@ -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<common::model::package>
together_group_stack_;
};
} // namespace plantuml

17
tests/t30009/.clang-uml Normal file
View File

@@ -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]

22
tests/t30009/t30009.cc Normal file
View File

@@ -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 {
}
}
}

46
tests/t30009/test_case.h Normal file
View File

@@ -0,0 +1,46 @@
/**
* tests/t30009/test_case.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.
*/
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);
}

View File

@@ -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

View File

@@ -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