Compare commits
1 Commits
refactor-f
...
add-addres
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b10885155 |
1
.sanitize-blacklist
Normal file
1
.sanitize-blacklist
Normal file
@@ -0,0 +1 @@
|
||||
fun:*SmallVector*
|
||||
@@ -70,6 +70,23 @@ if(CODE_COVERAGE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov --coverage")
|
||||
endif(CODE_COVERAGE)
|
||||
|
||||
#
|
||||
# Setup Address Sanitizer
|
||||
#
|
||||
option(ADDRESS_SANITIZER "" OFF)
|
||||
if(ADDRESS_SANITIZER)
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} \
|
||||
-fno-omit-frame-pointer \
|
||||
-fsanitize=address \
|
||||
-fsanitize-blacklist=${CMAKE_CURRENT_SOURCE_DIR}/.sanitize-blacklist")
|
||||
set(CMAKE_EXE_LINKER_FLAGS
|
||||
"${CMAKE_EXE_LINKER_FLAGS} \
|
||||
-fno-omit-frame-pointer \
|
||||
-fsanitize=address \
|
||||
-fsanitize-blacklist=${CMAKE_CURRENT_SOURCE_DIR}/.sanitize-blacklist")
|
||||
endif(ADDRESS_SANITIZER)
|
||||
|
||||
#
|
||||
# Setup LLVM
|
||||
#
|
||||
|
||||
3
Makefile
3
Makefile
@@ -38,6 +38,7 @@ CMAKE_CXX_FLAGS ?=
|
||||
CMAKE_EXE_LINKER_FLAGS ?=
|
||||
CMAKE_GENERATOR ?= Unix Makefiles
|
||||
CODE_COVERAGE ?= OFF
|
||||
ADDRESS_SANITIZER ?= OFF
|
||||
|
||||
ENABLE_CXX_MODULES_TEST_CASES ?= OFF
|
||||
ENABLE_CUDA_TEST_CASES ?= OFF
|
||||
@@ -69,6 +70,7 @@ debug/CMakeLists.txt:
|
||||
-DENABLE_CUDA_TEST_CASES=$(ENABLE_CUDA_TEST_CASES) \
|
||||
-DENABLE_CXX_MODULES_TEST_CASES=$(ENABLE_CXX_MODULES_TEST_CASES) \
|
||||
-DCODE_COVERAGE=$(CODE_COVERAGE) \
|
||||
-DADDRESS_SANITIZER=$(ADDRESS_SANITIZER) \
|
||||
-DCLANG_UML_ENABLE_BACKTRACE=$(CLANG_UML_ENABLE_BACKTRACE)
|
||||
|
||||
release/CMakeLists.txt:
|
||||
@@ -84,6 +86,7 @@ release/CMakeLists.txt:
|
||||
-DLLVM_CONFIG_PATH=${LLVM_CONFIG_PATH} \
|
||||
-DLINK_LLVM_SHARED=${LLVM_SHARED} \
|
||||
-DCMAKE_PREFIX=${CMAKE_PREFIX} \
|
||||
-DADDRESS_SANITIZER=$(ADDRESS_SANITIZER) \
|
||||
-DENABLE_CUDA_TEST_CASES=$(ENABLE_CUDA_TEST_CASES) \
|
||||
-DENABLE_CXX_MODULES_TEST_CASES=$(ENABLE_CXX_MODULES_TEST_CASES) \
|
||||
-DCLANG_UML_ENABLE_BACKTRACE=$(CLANG_UML_ENABLE_BACKTRACE)
|
||||
|
||||
@@ -158,17 +158,26 @@ void generator::generate_top_level_elements(nlohmann::json &parent) const
|
||||
{
|
||||
for (const auto &p : model()) {
|
||||
if (auto *pkg = dynamic_cast<package *>(p.get()); pkg) {
|
||||
if (!pkg->is_empty())
|
||||
if (!pkg->is_empty() &&
|
||||
!pkg->all_of([this](const common::model::element &e) {
|
||||
return !model().should_include(e);
|
||||
}))
|
||||
generate(*pkg, parent);
|
||||
}
|
||||
else if (auto *cls = dynamic_cast<class_ *>(p.get()); cls) {
|
||||
generate(*cls, parent);
|
||||
if (model().should_include(*cls)) {
|
||||
generate(*cls, parent);
|
||||
}
|
||||
}
|
||||
else if (auto *enm = dynamic_cast<enum_ *>(p.get()); enm) {
|
||||
generate(*enm, parent);
|
||||
if (model().should_include(*enm)) {
|
||||
generate(*enm, parent);
|
||||
}
|
||||
}
|
||||
else if (auto *cpt = dynamic_cast<concept_ *>(p.get()); cpt) {
|
||||
generate(*cpt, parent);
|
||||
if (model().should_include(*cpt)) {
|
||||
generate(*cpt, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,7 +203,10 @@ void generator::generate(const package &p, nlohmann::json &parent) const
|
||||
for (const auto &subpackage : p) {
|
||||
if (dynamic_cast<package *>(subpackage.get()) != nullptr) {
|
||||
const auto &sp = dynamic_cast<package &>(*subpackage);
|
||||
if (!sp.is_empty()) {
|
||||
if (!sp.is_empty() &&
|
||||
!sp.all_of([this](const common::model::element &e) {
|
||||
return !model().should_include(e);
|
||||
})) {
|
||||
if (config().generate_packages())
|
||||
generate(sp, package_object);
|
||||
else
|
||||
@@ -202,22 +214,28 @@ void generator::generate(const package &p, nlohmann::json &parent) const
|
||||
}
|
||||
}
|
||||
else if (auto *cls = dynamic_cast<class_ *>(subpackage.get()); cls) {
|
||||
if (config().generate_packages())
|
||||
generate(*cls, package_object);
|
||||
else
|
||||
generate(*cls, parent);
|
||||
if (model().should_include(*cls)) {
|
||||
if (config().generate_packages())
|
||||
generate(*cls, package_object);
|
||||
else
|
||||
generate(*cls, parent);
|
||||
}
|
||||
}
|
||||
else if (auto *enm = dynamic_cast<enum_ *>(subpackage.get()); enm) {
|
||||
if (config().generate_packages())
|
||||
generate(*enm, package_object);
|
||||
else
|
||||
generate(*enm, parent);
|
||||
if (model().should_include(*enm)) {
|
||||
if (config().generate_packages())
|
||||
generate(*enm, package_object);
|
||||
else
|
||||
generate(*enm, parent);
|
||||
}
|
||||
}
|
||||
else if (auto *cpt = dynamic_cast<concept_ *>(subpackage.get()); cpt) {
|
||||
if (config().generate_packages())
|
||||
generate(*cpt, package_object);
|
||||
else
|
||||
generate(*cpt, parent);
|
||||
if (model().should_include(*cpt)) {
|
||||
if (config().generate_packages())
|
||||
generate(*cpt, package_object);
|
||||
else
|
||||
generate(*cpt, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,9 +253,6 @@ void generator::generate(const class_ &c, nlohmann::json &parent) const
|
||||
object["display_name"] =
|
||||
common::generators::json::render_name(c.full_name_no_ns());
|
||||
|
||||
object["display_name"] =
|
||||
config().simplify_template_type(object["display_name"]);
|
||||
|
||||
for (auto &tp : object["template_parameters"]) {
|
||||
if (tp.contains("type") && tp.at("type").is_string()) {
|
||||
tp["type"] = config().using_namespace().relative(tp.at("type"));
|
||||
@@ -283,13 +298,19 @@ void generator::generate_relationships(nlohmann::json &parent) const
|
||||
generate_relationships(*pkg, parent);
|
||||
}
|
||||
else if (auto *cls = dynamic_cast<class_ *>(p.get()); cls) {
|
||||
generate_relationships(*cls, parent);
|
||||
if (model().should_include(*cls)) {
|
||||
generate_relationships(*cls, parent);
|
||||
}
|
||||
}
|
||||
else if (auto *enm = dynamic_cast<enum_ *>(p.get()); enm) {
|
||||
generate_relationships(*enm, parent);
|
||||
if (model().should_include(*enm)) {
|
||||
generate_relationships(*enm, parent);
|
||||
}
|
||||
}
|
||||
else if (auto *cpt = dynamic_cast<concept_ *>(p.get()); cpt) {
|
||||
generate_relationships(*cpt, parent);
|
||||
if (model().should_include(*cpt)) {
|
||||
generate_relationships(*cpt, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,6 +319,9 @@ void generator::generate_relationships(
|
||||
const class_ &c, nlohmann::json &parent) const
|
||||
{
|
||||
for (const auto &r : c.relationships()) {
|
||||
if (!model().should_include(r))
|
||||
continue;
|
||||
|
||||
auto target_element = model().get(r.destination());
|
||||
if (!target_element.has_value()) {
|
||||
LOG_DBG("Skipping {} relation from {} to {} due "
|
||||
@@ -326,6 +350,9 @@ void generator::generate_relationships(
|
||||
const enum_ &c, nlohmann::json &parent) const
|
||||
{
|
||||
for (const auto &r : c.relationships()) {
|
||||
if (!model().should_include(r))
|
||||
continue;
|
||||
|
||||
auto target_element = model().get(r.destination());
|
||||
if (!target_element.has_value()) {
|
||||
LOG_DBG("Skipping {} relation from {} to {} due "
|
||||
@@ -344,6 +371,9 @@ void generator::generate_relationships(
|
||||
const concept_ &c, nlohmann::json &parent) const
|
||||
{
|
||||
for (const auto &r : c.relationships()) {
|
||||
if (!model().should_include(r))
|
||||
continue;
|
||||
|
||||
auto target_element = model().get(r.destination());
|
||||
if (!target_element.has_value()) {
|
||||
LOG_DBG("Skipping {} relation from {} to {} due "
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
namespace clanguml::class_diagram::generators::mermaid {
|
||||
|
||||
using clanguml::common::eid_t;
|
||||
using clanguml::common::generators::mermaid::escape_name;
|
||||
using clanguml::common::generators::mermaid::indent;
|
||||
using clanguml::common::generators::mermaid::render_name;
|
||||
|
||||
@@ -51,8 +50,8 @@ void generator::generate_alias(
|
||||
|
||||
auto class_label = config().simplify_template_type(render_name(full_name));
|
||||
|
||||
ostr << indent(1) << "class " << c.alias() << "[\""
|
||||
<< escape_name(class_label) << "\"]\n";
|
||||
ostr << indent(1) << "class " << c.alias() << "[\"" << class_label
|
||||
<< "\"]\n";
|
||||
|
||||
// Register the added alias
|
||||
m_generated_aliases.emplace(c.alias());
|
||||
@@ -89,6 +88,9 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
|
||||
std::stringstream all_relations_str;
|
||||
for (const auto &r : c.relationships()) {
|
||||
if (!model().should_include(r.type()))
|
||||
continue;
|
||||
|
||||
try {
|
||||
generate_relationship(r, rendered_relations);
|
||||
}
|
||||
@@ -108,6 +110,9 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
sort_class_elements(members);
|
||||
|
||||
for (const auto &m : members) {
|
||||
if (!model().should_include(m))
|
||||
continue;
|
||||
|
||||
if (!config().include_relations_also_as_members() &&
|
||||
rendered_relations.find(m.name()) != rendered_relations.end())
|
||||
continue;
|
||||
@@ -150,7 +155,11 @@ void generator::generate_methods(
|
||||
sort_class_elements(sorted_methods);
|
||||
|
||||
for (const auto &m : sorted_methods) {
|
||||
if (!model().should_include(m))
|
||||
continue;
|
||||
|
||||
generate_method(m, ostr);
|
||||
|
||||
ostr << '\n';
|
||||
}
|
||||
}
|
||||
@@ -160,11 +169,17 @@ generator::method_groups_t generator::group_methods(
|
||||
{
|
||||
std::map<std::string, std::vector<class_method>> result;
|
||||
|
||||
// First get rid of methods which don't pass the filters
|
||||
std::vector<class_method> filtered_methods;
|
||||
std::copy_if(methods.cbegin(), methods.cend(),
|
||||
std::back_inserter(filtered_methods),
|
||||
[this](auto &m) { return model().should_include(m); });
|
||||
|
||||
for (const auto &g : method_groups_) {
|
||||
result[g] = {};
|
||||
}
|
||||
|
||||
for (const auto &m : methods) {
|
||||
for (const auto &m : filtered_methods) {
|
||||
if (m.is_constructor() || m.is_destructor()) {
|
||||
result["constructors"].push_back(m);
|
||||
}
|
||||
@@ -242,7 +257,7 @@ void generator::generate_method(
|
||||
ostr << fmt::format("[{}] ", fmt::join(method_mods, ","));
|
||||
}
|
||||
|
||||
ostr << escape_name(render_name(type));
|
||||
ostr << render_name(type);
|
||||
|
||||
if (m.is_pure_virtual())
|
||||
ostr << "*";
|
||||
@@ -261,8 +276,8 @@ void generator::generate_member(
|
||||
|
||||
ostr << indent(2) << mermaid_common::to_mermaid(m.access()) << m.name()
|
||||
<< " : "
|
||||
<< escape_name(uns.relative(
|
||||
config().simplify_template_type(render_name(m.type()))));
|
||||
<< render_name(
|
||||
uns.relative(config().simplify_template_type(m.type())));
|
||||
}
|
||||
|
||||
void generator::generate(const concept_ &c, std::ostream &ostr) const
|
||||
@@ -279,7 +294,7 @@ void generator::generate(const concept_ &c, std::ostream &ostr) const
|
||||
parameters.reserve(c.requires_parameters().size());
|
||||
for (const auto &p : c.requires_parameters()) {
|
||||
parameters.emplace_back(
|
||||
escape_name(p.to_string(config().using_namespace())));
|
||||
render_name(p.to_string(config().using_namespace())));
|
||||
}
|
||||
|
||||
ostr << indent(2)
|
||||
@@ -287,7 +302,7 @@ void generator::generate(const concept_ &c, std::ostream &ostr) const
|
||||
|
||||
for (const auto &req : c.requires_statements()) {
|
||||
ostr << indent(2)
|
||||
<< fmt::format("\"{}\"\n", escape_name(req, false));
|
||||
<< fmt::format("\"{}\"\n", render_name(req, false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,13 +332,19 @@ void generator::generate_relationships(std::ostream &ostr) const
|
||||
generate_relationships(*pkg, ostr);
|
||||
}
|
||||
else if (auto *cls = dynamic_cast<class_ *>(p.get()); cls) {
|
||||
generate_relationships(*cls, ostr);
|
||||
if (model().should_include(*cls)) {
|
||||
generate_relationships(*cls, ostr);
|
||||
}
|
||||
}
|
||||
else if (auto *enm = dynamic_cast<enum_ *>(p.get()); enm) {
|
||||
generate_relationships(*enm, ostr);
|
||||
if (model().should_include(*enm)) {
|
||||
generate_relationships(*enm, ostr);
|
||||
}
|
||||
}
|
||||
else if (auto *cpt = dynamic_cast<concept_ *>(p.get()); cpt) {
|
||||
generate_relationships(*cpt, ostr);
|
||||
if (model().should_include(*cpt)) {
|
||||
generate_relationships(*cpt, ostr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,6 +400,9 @@ void generator::generate_relationships(
|
||||
std::set<std::string> unique_relations;
|
||||
|
||||
for (const auto &r : c.relationships()) {
|
||||
if (!model().should_include(r.type()))
|
||||
continue;
|
||||
|
||||
LOG_DBG("== Processing relationship {}", to_string(r.type()));
|
||||
|
||||
std::stringstream relstr;
|
||||
@@ -484,6 +508,9 @@ void generator::generate_relationships(
|
||||
std::set<std::string> unique_relations;
|
||||
|
||||
for (const auto &r : c.relationships()) {
|
||||
if (!model().should_include(r.type()))
|
||||
continue;
|
||||
|
||||
LOG_DBG("== Processing relationship {}", to_string(r.type()));
|
||||
|
||||
std::stringstream relstr;
|
||||
@@ -555,6 +582,9 @@ void generator::generate_relationships(
|
||||
void generator::generate_relationships(const enum_ &e, std::ostream &ostr) const
|
||||
{
|
||||
for (const auto &r : e.relationships()) {
|
||||
if (!model().should_include(r.type()))
|
||||
continue;
|
||||
|
||||
eid_t destination{};
|
||||
std::stringstream relstr;
|
||||
try {
|
||||
@@ -687,7 +717,10 @@ void generator::generate_relationships(
|
||||
// packages which do not contain anything but other
|
||||
// packages are skipped
|
||||
const auto &sp = dynamic_cast<package &>(*subpackage);
|
||||
if (!sp.is_empty())
|
||||
if (!sp.is_empty() &&
|
||||
!sp.all_of([this](const common::model::element &e) {
|
||||
return !model().should_include(e);
|
||||
}))
|
||||
generate_relationships(sp, ostr);
|
||||
}
|
||||
else if (dynamic_cast<class_ *>(subpackage.get()) != nullptr) {
|
||||
@@ -724,43 +757,52 @@ void generator::generate_top_level_elements(std::ostream &ostr) const
|
||||
{
|
||||
for (const auto &p : model()) {
|
||||
if (auto *pkg = dynamic_cast<package *>(p.get()); pkg) {
|
||||
if (!pkg->is_empty())
|
||||
if (!pkg->is_empty() &&
|
||||
!pkg->all_of([this](const common::model::element &e) {
|
||||
return !model().should_include(e);
|
||||
}))
|
||||
generate(*pkg, ostr);
|
||||
}
|
||||
else if (auto *cls = dynamic_cast<class_ *>(p.get()); cls) {
|
||||
auto together_group =
|
||||
config().get_together_group(cls->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), cls);
|
||||
}
|
||||
else {
|
||||
generate_alias(*cls, ostr);
|
||||
generate(*cls, ostr);
|
||||
if (model().should_include(*cls)) {
|
||||
auto together_group =
|
||||
config().get_together_group(cls->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), cls);
|
||||
}
|
||||
else {
|
||||
generate_alias(*cls, ostr);
|
||||
generate(*cls, ostr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto *enm = dynamic_cast<enum_ *>(p.get()); enm) {
|
||||
auto together_group =
|
||||
config().get_together_group(enm->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), enm);
|
||||
}
|
||||
else {
|
||||
generate_alias(*enm, ostr);
|
||||
generate(*enm, ostr);
|
||||
if (model().should_include(*enm)) {
|
||||
auto together_group =
|
||||
config().get_together_group(enm->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), enm);
|
||||
}
|
||||
else {
|
||||
generate_alias(*enm, ostr);
|
||||
generate(*enm, ostr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto *cpt = dynamic_cast<concept_ *>(p.get()); cpt) {
|
||||
auto together_group =
|
||||
config().get_together_group(cpt->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), cpt);
|
||||
}
|
||||
else {
|
||||
generate_alias(*cpt, ostr);
|
||||
generate(*cpt, ostr);
|
||||
if (model().should_include(*cpt)) {
|
||||
auto together_group =
|
||||
config().get_together_group(cpt->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), cpt);
|
||||
}
|
||||
else {
|
||||
generate_alias(*cpt, ostr);
|
||||
generate(*cpt, ostr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,24 +38,16 @@ void generator::generate_link(
|
||||
|
||||
auto context = element_context(e);
|
||||
|
||||
auto maybe_link_pattern = get_link_pattern(e);
|
||||
if (maybe_link_pattern) {
|
||||
const auto &[link_prefix, link_pattern] = *maybe_link_pattern;
|
||||
auto ec = element_context(e);
|
||||
common::generators::make_context_source_relative(ec, link_prefix);
|
||||
|
||||
if (!config().generate_links().link.empty()) {
|
||||
ostr << " [[[";
|
||||
ostr << env().render(std::string_view{link_pattern}, context);
|
||||
ostr << env().render(
|
||||
std::string_view{config().generate_links().link}, context);
|
||||
}
|
||||
|
||||
auto maybe_tooltip_pattern = get_tooltip_pattern(e);
|
||||
|
||||
if (maybe_tooltip_pattern) {
|
||||
const auto &[tooltip_prefix, tooltip_pattern] = *maybe_tooltip_pattern;
|
||||
auto ec = element_context(e);
|
||||
common::generators::make_context_source_relative(ec, tooltip_prefix);
|
||||
if (!config().generate_links().tooltip.empty()) {
|
||||
ostr << "{";
|
||||
ostr << env().render(std::string_view{tooltip_pattern}, ec);
|
||||
ostr << env().render(
|
||||
std::string_view{config().generate_links().tooltip}, context);
|
||||
ostr << "}";
|
||||
}
|
||||
ostr << "]]]";
|
||||
@@ -165,6 +157,9 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
|
||||
std::stringstream all_relations_str;
|
||||
for (const auto &r : c.relationships()) {
|
||||
if (!model().should_include(r.type()))
|
||||
continue;
|
||||
|
||||
try {
|
||||
generate_relationship(r, rendered_relations);
|
||||
}
|
||||
@@ -187,6 +182,9 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
ostr << "__\n";
|
||||
|
||||
for (const auto &m : members) {
|
||||
if (!model().should_include(m))
|
||||
continue;
|
||||
|
||||
if (!config().include_relations_also_as_members() &&
|
||||
rendered_relations.find(m.name()) != rendered_relations.end())
|
||||
continue;
|
||||
@@ -230,7 +228,11 @@ void generator::generate_methods(
|
||||
sort_class_elements(sorted_methods);
|
||||
|
||||
for (const auto &m : sorted_methods) {
|
||||
if (!model().should_include(m))
|
||||
continue;
|
||||
|
||||
generate_method(m, ostr);
|
||||
|
||||
ostr << '\n';
|
||||
}
|
||||
}
|
||||
@@ -240,11 +242,17 @@ generator::method_groups_t generator::group_methods(
|
||||
{
|
||||
std::map<std::string, std::vector<class_method>> result;
|
||||
|
||||
// First get rid of methods which don't pass the filters
|
||||
std::vector<class_method> filtered_methods;
|
||||
std::copy_if(methods.cbegin(), methods.cend(),
|
||||
std::back_inserter(filtered_methods),
|
||||
[this](auto &m) { return model().should_include(m); });
|
||||
|
||||
for (const auto &g : method_groups_) {
|
||||
result[g] = {};
|
||||
}
|
||||
|
||||
for (const auto &m : methods) {
|
||||
for (const auto &m : filtered_methods) {
|
||||
if (m.is_constructor() || m.is_destructor()) {
|
||||
result["constructors"].push_back(m);
|
||||
}
|
||||
@@ -409,13 +417,19 @@ void generator::generate_relationships(std::ostream &ostr) const
|
||||
generate_relationships(*pkg, ostr);
|
||||
}
|
||||
else if (auto *cls = dynamic_cast<class_ *>(p.get()); cls) {
|
||||
generate_relationships(*cls, ostr);
|
||||
if (model().should_include(*cls)) {
|
||||
generate_relationships(*cls, ostr);
|
||||
}
|
||||
}
|
||||
else if (auto *enm = dynamic_cast<enum_ *>(p.get()); enm) {
|
||||
generate_relationships(*enm, ostr);
|
||||
if (model().should_include(*enm)) {
|
||||
generate_relationships(*enm, ostr);
|
||||
}
|
||||
}
|
||||
else if (auto *cpt = dynamic_cast<concept_ *>(p.get()); cpt) {
|
||||
generate_relationships(*cpt, ostr);
|
||||
if (model().should_include(*cpt)) {
|
||||
generate_relationships(*cpt, ostr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -467,6 +481,9 @@ void generator::generate_relationships(
|
||||
std::set<std::string> unique_relations;
|
||||
|
||||
for (const auto &r : c.relationships()) {
|
||||
if (!model().should_include(r.type()))
|
||||
continue;
|
||||
|
||||
LOG_DBG("== Processing relationship {}",
|
||||
plantuml_common::to_plantuml(r, config()));
|
||||
|
||||
@@ -644,6 +661,9 @@ void generator::generate(const enum_ &e, std::ostream &ostr) const
|
||||
void generator::generate_relationships(const enum_ &e, std::ostream &ostr) const
|
||||
{
|
||||
for (const auto &r : e.relationships()) {
|
||||
if (!model().should_include(r.type()))
|
||||
continue;
|
||||
|
||||
eid_t destination{};
|
||||
std::stringstream relstr;
|
||||
try {
|
||||
@@ -704,7 +724,10 @@ void generator::generate(const package &p, std::ostream &ostr) const
|
||||
if (dynamic_cast<package *>(subpackage.get()) != nullptr) {
|
||||
// TODO: add option - generate_empty_packages
|
||||
const auto &sp = dynamic_cast<package &>(*subpackage);
|
||||
if (!sp.is_empty()) {
|
||||
if (!sp.is_empty() &&
|
||||
!sp.all_of([this](const common::model::element &e) {
|
||||
return !model().should_include(e);
|
||||
})) {
|
||||
together_group_stack_.enter();
|
||||
|
||||
generate(sp, ostr);
|
||||
@@ -799,7 +822,10 @@ void generator::generate_relationships(
|
||||
// packages which do not contain anything but other
|
||||
// packages are skipped
|
||||
const auto &sp = dynamic_cast<package &>(*subpackage);
|
||||
if (!sp.is_empty())
|
||||
if (!sp.is_empty() &&
|
||||
!sp.all_of([this](const common::model::element &e) {
|
||||
return !model().should_include(e);
|
||||
}))
|
||||
generate_relationships(sp, ostr);
|
||||
}
|
||||
else if (dynamic_cast<class_ *>(subpackage.get()) != nullptr) {
|
||||
@@ -838,43 +864,52 @@ void generator::generate_top_level_elements(std::ostream &ostr) const
|
||||
{
|
||||
for (const auto &p : model()) {
|
||||
if (auto *pkg = dynamic_cast<package *>(p.get()); pkg) {
|
||||
if (!pkg->is_empty())
|
||||
if (!pkg->is_empty() &&
|
||||
!pkg->all_of([this](const common::model::element &e) {
|
||||
return !model().should_include(e);
|
||||
}))
|
||||
generate(*pkg, ostr);
|
||||
}
|
||||
else if (auto *cls = dynamic_cast<class_ *>(p.get()); cls) {
|
||||
auto together_group =
|
||||
config().get_together_group(cls->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), cls);
|
||||
}
|
||||
else {
|
||||
generate_alias(*cls, ostr);
|
||||
generate(*cls, ostr);
|
||||
if (model().should_include(*cls)) {
|
||||
auto together_group =
|
||||
config().get_together_group(cls->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), cls);
|
||||
}
|
||||
else {
|
||||
generate_alias(*cls, ostr);
|
||||
generate(*cls, ostr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto *enm = dynamic_cast<enum_ *>(p.get()); enm) {
|
||||
auto together_group =
|
||||
config().get_together_group(enm->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), enm);
|
||||
}
|
||||
else {
|
||||
generate_alias(*enm, ostr);
|
||||
generate(*enm, ostr);
|
||||
if (model().should_include(*enm)) {
|
||||
auto together_group =
|
||||
config().get_together_group(enm->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), enm);
|
||||
}
|
||||
else {
|
||||
generate_alias(*enm, ostr);
|
||||
generate(*enm, ostr);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (auto *cpt = dynamic_cast<concept_ *>(p.get()); cpt) {
|
||||
auto together_group =
|
||||
config().get_together_group(cpt->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), cpt);
|
||||
}
|
||||
else {
|
||||
generate_alias(*cpt, ostr);
|
||||
generate(*cpt, ostr);
|
||||
if (model().should_include(*cpt)) {
|
||||
auto together_group =
|
||||
config().get_together_group(cpt->full_name(false));
|
||||
if (together_group) {
|
||||
together_group_stack_.group_together(
|
||||
together_group.value(), cpt);
|
||||
}
|
||||
else {
|
||||
generate_alias(*cpt, ostr);
|
||||
generate(*cpt, ostr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "class.h"
|
||||
|
||||
#include "common/model/filters/diagram_filter.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <sstream>
|
||||
@@ -64,7 +63,6 @@ const std::vector<class_member> &class_::members() const { return members_; }
|
||||
const std::vector<class_method> &class_::methods() const { return methods_; }
|
||||
|
||||
const std::vector<class_parent> &class_::parents() const { return bases_; }
|
||||
std::vector<class_parent> &class_::parents() { return bases_; }
|
||||
|
||||
bool operator==(const class_ &l, const class_ &r) { return l.id() == r.id(); }
|
||||
|
||||
@@ -113,21 +111,6 @@ bool class_::is_abstract() const
|
||||
[](const auto &method) { return method.is_pure_virtual(); });
|
||||
}
|
||||
|
||||
void class_::apply_filter(
|
||||
const common::model::diagram_filter &filter, const std::set<eid_t> &removed)
|
||||
{
|
||||
diagram_element::apply_filter(filter, removed);
|
||||
|
||||
common::model::apply_filter(members_, filter);
|
||||
common::model::apply_filter(methods_, filter);
|
||||
|
||||
// Remove class bases which are no longer in the diagram
|
||||
parents().erase(
|
||||
std::remove_if(parents().begin(), parents().end(),
|
||||
[&removed](auto &&p) { return removed.count(p.id()) > 0; }),
|
||||
parents().end());
|
||||
}
|
||||
|
||||
std::optional<std::string> class_::doxygen_link() const
|
||||
{
|
||||
const auto *type = is_struct() ? "struct" : "class";
|
||||
|
||||
@@ -30,10 +30,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clanguml::common::model {
|
||||
class diagram_filter;
|
||||
}
|
||||
|
||||
namespace clanguml::class_diagram::model {
|
||||
|
||||
/**
|
||||
@@ -130,7 +126,6 @@ public:
|
||||
* @return Reference to class parents.
|
||||
*/
|
||||
const std::vector<class_parent> &parents() const;
|
||||
std::vector<class_parent> &parents();
|
||||
|
||||
/**
|
||||
* @brief Get class full name.
|
||||
@@ -171,9 +166,6 @@ public:
|
||||
*/
|
||||
std::optional<std::string> doxygen_link() const override;
|
||||
|
||||
void apply_filter(const common::model::diagram_filter &filter,
|
||||
const std::set<eid_t> &removed) override;
|
||||
|
||||
private:
|
||||
bool is_struct_{false};
|
||||
bool is_union_{false};
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "diagram.h"
|
||||
|
||||
#include "common/model/filters/diagram_filter.h"
|
||||
#include "common/model/diagram_filter.h"
|
||||
#include "util/error.h"
|
||||
#include "util/util.h"
|
||||
|
||||
@@ -256,39 +256,6 @@ void diagram::remove_redundant_dependencies()
|
||||
}
|
||||
}
|
||||
|
||||
void diagram::apply_filter()
|
||||
{
|
||||
// First find all element ids which should be removed
|
||||
std::set<eid_t> to_remove;
|
||||
|
||||
for (const auto &c : element_view<class_>::view())
|
||||
if (!filter().should_include(c.get()))
|
||||
to_remove.emplace(c.get().id());
|
||||
|
||||
for (const auto &e : element_view<enum_>::view())
|
||||
if (!filter().should_include(e.get()))
|
||||
to_remove.emplace(e.get().id());
|
||||
|
||||
for (const auto &c : element_view<concept_>::view())
|
||||
if (!filter().should_include(c.get()))
|
||||
to_remove.emplace(c.get().id());
|
||||
|
||||
element_view<class_>::remove(to_remove);
|
||||
element_view<enum_>::remove(to_remove);
|
||||
element_view<concept_>::remove(to_remove);
|
||||
|
||||
nested_trait_ns::remove(to_remove);
|
||||
|
||||
for (auto &c : element_view<class_>::view())
|
||||
c.get().apply_filter(filter(), to_remove);
|
||||
|
||||
for (auto &e : element_view<enum_>::view())
|
||||
e.get().apply_filter(filter(), to_remove);
|
||||
|
||||
for (auto &c : element_view<concept_>::view())
|
||||
c.get().apply_filter(filter(), to_remove);
|
||||
}
|
||||
|
||||
bool diagram::is_empty() const
|
||||
{
|
||||
return element_view<class_>::is_empty() &&
|
||||
|
||||
@@ -256,8 +256,6 @@ public:
|
||||
*/
|
||||
bool is_empty() const override;
|
||||
|
||||
void apply_filter() override;
|
||||
|
||||
private:
|
||||
template <typename ElementT>
|
||||
bool add_with_namespace_path(std::unique_ptr<ElementT> &&e);
|
||||
|
||||
@@ -76,8 +76,7 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns)
|
||||
p->set_id(common::to_id(*ns));
|
||||
id_mapper().add(ns->getID(), p->id());
|
||||
|
||||
if (config().filter_mode() == config::filter_mode_t::advanced ||
|
||||
(diagram().should_include(*p) && !diagram().get(p->id()))) {
|
||||
if (diagram().should_include(*p) && !diagram().get(p->id())) {
|
||||
process_comment(*ns, *p);
|
||||
set_source_location(*ns, *p);
|
||||
|
||||
@@ -668,6 +667,10 @@ void translation_unit_visitor::process_concept_specialization_relationships(
|
||||
|
||||
bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls)
|
||||
{
|
||||
// Skip system headers
|
||||
if (source_manager().isInSystemHeader(cls->getSourceRange().getBegin()))
|
||||
return true;
|
||||
|
||||
if (!should_include(cls))
|
||||
return true;
|
||||
|
||||
|
||||
@@ -941,27 +941,12 @@ bool parse_source_location(const std::string &location_str, std::string &file,
|
||||
clang::RawComment *get_expression_raw_comment(const clang::SourceManager &sm,
|
||||
const clang::ASTContext &context, const clang::Stmt *stmt)
|
||||
{
|
||||
return get_raw_comment(sm, context, stmt->getSourceRange());
|
||||
}
|
||||
|
||||
clang::RawComment *get_declaration_raw_comment(const clang::SourceManager &sm,
|
||||
const clang::ASTContext &context, const clang::Decl *decl)
|
||||
{
|
||||
return get_raw_comment(sm, context, decl->getSourceRange());
|
||||
}
|
||||
|
||||
clang::RawComment *get_raw_comment(const clang::SourceManager &sm,
|
||||
const clang::ASTContext &context, const clang::SourceRange &source_range)
|
||||
{
|
||||
auto expr_begin = source_range.getBegin();
|
||||
// First get the first line of the expression
|
||||
auto expr_begin = stmt->getSourceRange().getBegin();
|
||||
const auto expr_begin_line = sm.getSpellingLineNumber(expr_begin);
|
||||
|
||||
std::string file_Path = sm.getFilename(expr_begin).str();
|
||||
|
||||
auto file_id = sm.getFileID(expr_begin);
|
||||
|
||||
if (!context.Comments.empty() &&
|
||||
context.Comments.getCommentsInFile(file_id) != nullptr) {
|
||||
context.Comments.getCommentsInFile(sm.getFileID(expr_begin)) != nullptr)
|
||||
for (const auto [offset, raw_comment] :
|
||||
*context.Comments.getCommentsInFile(sm.getFileID(expr_begin))) {
|
||||
const auto comment_end_line = sm.getSpellingLineNumber(
|
||||
@@ -971,7 +956,6 @@ clang::RawComment *get_raw_comment(const clang::SourceManager &sm,
|
||||
expr_begin_line == comment_end_line + 1)
|
||||
return raw_comment;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -299,12 +299,6 @@ consume_type_context(clang::QualType type);
|
||||
clang::RawComment *get_expression_raw_comment(const clang::SourceManager &sm,
|
||||
const clang::ASTContext &context, const clang::Stmt *stmt);
|
||||
|
||||
clang::RawComment *get_declaration_raw_comment(const clang::SourceManager &sm,
|
||||
const clang::ASTContext &context, const clang::Decl *decl);
|
||||
|
||||
clang::RawComment *get_raw_comment(const clang::SourceManager &sm,
|
||||
const clang::ASTContext &context, const clang::SourceRange &source_range);
|
||||
|
||||
/**
|
||||
* Check if function or method declaration is a C++20 coroutine.
|
||||
*
|
||||
|
||||
@@ -17,22 +17,10 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/model/source_location.h"
|
||||
#include "util/error.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <inja/inja.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
namespace clanguml::common::generators {
|
||||
|
||||
void make_context_source_relative(
|
||||
inja::json &context, const std::string &prefix);
|
||||
|
||||
/**
|
||||
* @brief Common diagram generator interface
|
||||
*
|
||||
@@ -50,8 +38,6 @@ public:
|
||||
: config_{config}
|
||||
, model_{model}
|
||||
{
|
||||
init_context();
|
||||
init_env();
|
||||
}
|
||||
|
||||
virtual ~generator() = default;
|
||||
@@ -82,253 +68,9 @@ public:
|
||||
*/
|
||||
const DiagramType &model() const { return model_; }
|
||||
|
||||
template <typename E> inja::json element_context(const E &e) const;
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> get_link_pattern(
|
||||
const common::model::source_location &sl) const;
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> get_tooltip_pattern(
|
||||
const common::model::source_location &sl) const;
|
||||
|
||||
/**
|
||||
* @brief Initialize diagram Jinja context
|
||||
*/
|
||||
void init_context();
|
||||
|
||||
/**
|
||||
* @brief Update diagram Jinja context
|
||||
*
|
||||
* This method updates the diagram context with models properties
|
||||
* which can be used to render Jinja templates in the diagram (e.g.
|
||||
* in notes or links)
|
||||
*/
|
||||
void update_context() const;
|
||||
|
||||
void init_env();
|
||||
|
||||
const inja::json &context() const;
|
||||
|
||||
inja::Environment &env() const;
|
||||
|
||||
protected:
|
||||
mutable inja::json m_context;
|
||||
mutable inja::Environment m_env;
|
||||
|
||||
private:
|
||||
ConfigType &config_;
|
||||
DiagramType &model_;
|
||||
};
|
||||
|
||||
template <typename C, typename D> void generator<C, D>::init_context()
|
||||
{
|
||||
const auto &config = generators::generator<C, D>::config();
|
||||
|
||||
if (config.git) {
|
||||
m_context["git"]["branch"] = config.git().branch;
|
||||
m_context["git"]["revision"] = config.git().revision;
|
||||
m_context["git"]["commit"] = config.git().commit;
|
||||
m_context["git"]["toplevel"] = config.git().toplevel;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename C, typename D> void generator<C, D>::update_context() const
|
||||
{
|
||||
m_context["diagram"] = model().context();
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
const inja::json &generator<C, D>::context() const
|
||||
{
|
||||
return m_context;
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
inja::Environment &generator<C, D>::env() const
|
||||
{
|
||||
return m_env;
|
||||
}
|
||||
|
||||
template <typename C, typename D> void generator<C, D>::init_env()
|
||||
{
|
||||
const auto &model = generators::generator<C, D>::model();
|
||||
const auto &config = generators::generator<C, D>::config();
|
||||
|
||||
//
|
||||
// Add basic string functions to inja environment
|
||||
//
|
||||
|
||||
// Check if string is empty
|
||||
m_env.add_callback("empty", 1, [](inja::Arguments &args) {
|
||||
return args.at(0)->get<std::string>().empty();
|
||||
});
|
||||
|
||||
// Remove spaces from the left of a string
|
||||
m_env.add_callback("ltrim", 1, [](inja::Arguments &args) {
|
||||
return util::ltrim(args.at(0)->get<std::string>());
|
||||
});
|
||||
|
||||
// Remove trailing spaces from a string
|
||||
m_env.add_callback("rtrim", 1, [](inja::Arguments &args) {
|
||||
return util::rtrim(args.at(0)->get<std::string>());
|
||||
});
|
||||
|
||||
// Remove spaces before and after a string
|
||||
m_env.add_callback("trim", 1, [](inja::Arguments &args) {
|
||||
return util::trim(args.at(0)->get<std::string>());
|
||||
});
|
||||
|
||||
// Make a string shorted with a limit to
|
||||
m_env.add_callback("abbrv", 2, [](inja::Arguments &args) {
|
||||
return util::abbreviate(
|
||||
args.at(0)->get<std::string>(), args.at(1)->get<unsigned>());
|
||||
});
|
||||
|
||||
m_env.add_callback("replace", 3, [](inja::Arguments &args) {
|
||||
std::string result = args[0]->get<std::string>();
|
||||
std::regex pattern(args[1]->get<std::string>());
|
||||
return std::regex_replace(result, pattern, args[2]->get<std::string>());
|
||||
});
|
||||
|
||||
m_env.add_callback("split", 2, [](inja::Arguments &args) {
|
||||
return util::split(
|
||||
args[0]->get<std::string>(), args[1]->get<std::string>());
|
||||
});
|
||||
|
||||
//
|
||||
// Add PlantUML specific functions
|
||||
//
|
||||
|
||||
// Return the entire element JSON context based on element name
|
||||
// e.g.:
|
||||
// {{ element("clanguml::t00050::A").comment }}
|
||||
//
|
||||
m_env.add_callback("element", 1, [&model, &config](inja::Arguments &args) {
|
||||
inja::json res{};
|
||||
auto element_opt = model.get_with_namespace(
|
||||
args[0]->get<std::string>(), config.using_namespace());
|
||||
|
||||
if (element_opt.has_value())
|
||||
res = element_opt.value().context();
|
||||
|
||||
return res;
|
||||
});
|
||||
|
||||
// Convert C++ entity to PlantUML alias, e.g.
|
||||
// "note left of {{ alias("A") }}: This is a note"
|
||||
// Shortcut to:
|
||||
// {{ element("A").alias }}
|
||||
//
|
||||
m_env.add_callback("alias", 1, [&model, &config](inja::Arguments &args) {
|
||||
auto element_opt = model.get_with_namespace(
|
||||
args[0]->get<std::string>(), config.using_namespace());
|
||||
|
||||
if (!element_opt.has_value())
|
||||
throw clanguml::error::uml_alias_missing(
|
||||
args[0]->get<std::string>());
|
||||
|
||||
return element_opt.value().alias();
|
||||
});
|
||||
|
||||
// Get elements' comment:
|
||||
// "note left of {{ alias("A") }}: {{ comment("A") }}"
|
||||
// Shortcut to:
|
||||
// {{ element("A").comment }}
|
||||
//
|
||||
m_env.add_callback("comment", 1, [&model, &config](inja::Arguments &args) {
|
||||
inja::json res{};
|
||||
auto element_opt = model.get_with_namespace(
|
||||
args[0]->get<std::string>(), config.using_namespace());
|
||||
|
||||
if (!element_opt.has_value())
|
||||
throw clanguml::error::uml_alias_missing(
|
||||
args[0]->get<std::string>());
|
||||
|
||||
auto comment = element_opt.value().comment();
|
||||
|
||||
if (comment.has_value()) {
|
||||
assert(comment.value().is_object());
|
||||
res = comment.value();
|
||||
}
|
||||
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
template <typename E>
|
||||
inja::json generator<C, D>::element_context(const E &e) const
|
||||
{
|
||||
const auto &diagram_context = context();
|
||||
|
||||
inja::json ctx;
|
||||
ctx["element"] = e.context();
|
||||
#if _MSC_VER
|
||||
if (ctx.contains("git")) {
|
||||
#else
|
||||
if (diagram_context.template contains("git")) {
|
||||
#endif
|
||||
ctx["git"] = diagram_context["git"];
|
||||
}
|
||||
|
||||
if (!e.file().empty()) {
|
||||
std::filesystem::path file{e.file()};
|
||||
std::string git_relative_path = file.string();
|
||||
if (!e.file_relative().empty()) {
|
||||
#if _MSC_VER
|
||||
if (file.is_absolute() && ctx.contains("git")) {
|
||||
#else
|
||||
if (file.is_absolute() &&
|
||||
diagram_context.template contains("git")) {
|
||||
#endif
|
||||
git_relative_path = std::filesystem::relative(
|
||||
file, diagram_context["git"]["toplevel"])
|
||||
.string();
|
||||
ctx["element"]["source"]["path"] =
|
||||
util::path_to_url(git_relative_path);
|
||||
}
|
||||
else {
|
||||
ctx["element"]["source"]["path"] = e.file();
|
||||
}
|
||||
}
|
||||
else {
|
||||
git_relative_path = "";
|
||||
ctx["element"]["source"]["path"] = e.file();
|
||||
}
|
||||
|
||||
ctx["element"]["source"]["full_path"] = file.string();
|
||||
ctx["element"]["source"]["name"] = file.filename().string();
|
||||
ctx["element"]["source"]["line"] = e.line();
|
||||
}
|
||||
|
||||
const auto &maybe_comment = e.comment();
|
||||
if (maybe_comment) {
|
||||
ctx["element"]["comment"] = maybe_comment.value();
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
std::optional<std::pair<std::string, std::string>>
|
||||
generator<C, D>::get_link_pattern(
|
||||
const common::model::source_location &sl) const
|
||||
{
|
||||
if (sl.file_relative().empty()) {
|
||||
return config().generate_links().get_link_pattern(sl.file());
|
||||
}
|
||||
|
||||
return config().generate_links().get_link_pattern(sl.file_relative());
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
std::optional<std::pair<std::string, std::string>>
|
||||
generator<C, D>::get_tooltip_pattern(
|
||||
const common::model::source_location &sl) const
|
||||
{
|
||||
if (sl.file_relative().empty()) {
|
||||
return config().generate_links().get_tooltip_pattern(sl.file());
|
||||
}
|
||||
|
||||
return config().generate_links().get_tooltip_pattern(sl.file_relative());
|
||||
}
|
||||
} // namespace clanguml::common::generators
|
||||
@@ -21,28 +21,6 @@
|
||||
#include "progress_indicator.h"
|
||||
|
||||
namespace clanguml::common::generators {
|
||||
void make_context_source_relative(
|
||||
inja::json &context, const std::string &prefix)
|
||||
{
|
||||
if (!context.contains("element"))
|
||||
return;
|
||||
|
||||
if (!context["element"].contains("source"))
|
||||
return;
|
||||
|
||||
auto &source = context["element"]["source"];
|
||||
|
||||
if (source.at("path").empty())
|
||||
return;
|
||||
|
||||
auto path = std::filesystem::path(source.at("path"));
|
||||
auto prefix_path = std::filesystem::path(prefix);
|
||||
if (path.is_absolute() && util::is_relative_to(path, prefix_path)) {
|
||||
source["path"] = relative(path, prefix_path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void find_translation_units_for_diagrams(
|
||||
const std::vector<std::string> &diagram_names,
|
||||
clanguml::config::config &config,
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "class_diagram/generators/plantuml/class_diagram_generator.h"
|
||||
#include "cli/cli_handler.h"
|
||||
#include "common/compilation_database.h"
|
||||
#include "common/model/filters/diagram_filter_factory.h"
|
||||
#include "common/model/diagram_filter.h"
|
||||
#include "config/config.h"
|
||||
#include "include_diagram/generators/json/include_diagram_generator.h"
|
||||
#include "include_diagram/generators/mermaid/include_diagram_generator.h"
|
||||
@@ -369,7 +369,7 @@ std::unique_ptr<DiagramModel> generate(const common::compilation_database &db,
|
||||
auto diagram = std::make_unique<DiagramModel>();
|
||||
diagram->set_name(name);
|
||||
diagram->set_filter(
|
||||
model::diagram_filter_factory::create(*diagram, config));
|
||||
std::make_unique<model::diagram_filter>(*diagram, config));
|
||||
|
||||
LOG_DBG("Found translation units for diagram {}: {}", name,
|
||||
fmt::join(translation_units, ", "));
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/generators/generator.h"
|
||||
#include "common/model/filters/diagram_filter.h"
|
||||
#include "common/model/diagram_filter.h"
|
||||
#include "config/config.h"
|
||||
#include "util/error.h"
|
||||
#include "util/util.h"
|
||||
|
||||
@@ -78,14 +78,7 @@ std::string indent(const unsigned level)
|
||||
return std::string(level * kIndentWidth, ' '); // NOLINT
|
||||
}
|
||||
|
||||
std::string render_name(std::string name)
|
||||
{
|
||||
util::replace_all(name, "##", "::");
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string escape_name(std::string name, bool round_brackets)
|
||||
std::string render_name(std::string name, bool round_brackets)
|
||||
{
|
||||
util::replace_all(name, "<", "<");
|
||||
util::replace_all(name, ">", ">");
|
||||
@@ -93,6 +86,7 @@ std::string escape_name(std::string name, bool round_brackets)
|
||||
util::replace_all(name, "(", "(");
|
||||
util::replace_all(name, ")", ")");
|
||||
}
|
||||
util::replace_all(name, "##", "::");
|
||||
util::replace_all(name, "{", "{");
|
||||
util::replace_all(name, "}", "}");
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/generators/generator.h"
|
||||
#include "common/model/filters/diagram_filter.h"
|
||||
#include "common/model/diagram_filter.h"
|
||||
#include "config/config.h"
|
||||
#include "util/error.h"
|
||||
#include "util/util.h"
|
||||
@@ -44,8 +44,7 @@ std::string to_mermaid(message_t r);
|
||||
|
||||
std::string indent(unsigned level);
|
||||
|
||||
std::string render_name(std::string name);
|
||||
std::string escape_name(std::string name, bool round_brackets = true);
|
||||
std::string render_name(std::string name, bool round_brackets = true);
|
||||
|
||||
/**
|
||||
* @brief Base class for diagram generators
|
||||
@@ -67,6 +66,8 @@ public:
|
||||
: clanguml::common::generators::generator<ConfigType, DiagramType>{
|
||||
config, model}
|
||||
{
|
||||
init_context();
|
||||
init_env();
|
||||
}
|
||||
|
||||
~generator() override = default;
|
||||
@@ -167,11 +168,84 @@ public:
|
||||
*/
|
||||
void print_debug(
|
||||
const common::model::source_location &e, std::ostream &ostr) const;
|
||||
/**
|
||||
* @brief Update diagram Jinja context
|
||||
*
|
||||
* This method updates the diagram context with models properties
|
||||
* which can be used to render Jinja templates in the diagram (e.g.
|
||||
* in notes or links)
|
||||
*/
|
||||
void update_context() const;
|
||||
|
||||
protected:
|
||||
const inja::json &context() const;
|
||||
|
||||
inja::Environment &env() const;
|
||||
|
||||
template <typename E> inja::json element_context(const E &e) const;
|
||||
|
||||
private:
|
||||
void init_context();
|
||||
|
||||
void init_env();
|
||||
|
||||
protected:
|
||||
mutable std::set<std::string> m_generated_aliases;
|
||||
mutable inja::json m_context;
|
||||
mutable inja::Environment m_env;
|
||||
};
|
||||
|
||||
template <typename C, typename D>
|
||||
const inja::json &generator<C, D>::context() const
|
||||
{
|
||||
return m_context;
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
inja::Environment &generator<C, D>::env() const
|
||||
{
|
||||
return m_env;
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
template <typename E>
|
||||
inja::json generator<C, D>::element_context(const E &e) const
|
||||
{
|
||||
auto ctx = context();
|
||||
|
||||
ctx["element"] = e.context();
|
||||
|
||||
if (!e.file().empty()) {
|
||||
std::filesystem::path file{e.file()};
|
||||
std::string git_relative_path = file.string();
|
||||
if (!e.file_relative().empty()) {
|
||||
#if _MSC_VER
|
||||
if (file.is_absolute() && ctx.contains("git"))
|
||||
#else
|
||||
if (file.is_absolute() && ctx.template contains("git"))
|
||||
#endif
|
||||
git_relative_path =
|
||||
std::filesystem::relative(file, ctx["git"]["toplevel"])
|
||||
.string();
|
||||
}
|
||||
else {
|
||||
git_relative_path = "";
|
||||
}
|
||||
|
||||
ctx["element"]["source"]["path"] = util::path_to_url(git_relative_path);
|
||||
ctx["element"]["source"]["full_path"] = file.string();
|
||||
ctx["element"]["source"]["name"] = file.filename().string();
|
||||
ctx["element"]["source"]["line"] = e.line();
|
||||
}
|
||||
|
||||
const auto maybe_comment = e.comment();
|
||||
if (maybe_comment) {
|
||||
ctx["element"]["comment"] = maybe_comment.value();
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
void generator<C, D>::generate(std::ostream &ostr) const
|
||||
{
|
||||
@@ -184,7 +258,7 @@ void generator<C, D>::generate(std::ostream &ostr) const
|
||||
"Diagram configuration resulted in empty diagram."};
|
||||
}
|
||||
|
||||
generators::generator<C, D>::update_context();
|
||||
update_context();
|
||||
|
||||
generate_title(ostr);
|
||||
|
||||
@@ -203,71 +277,59 @@ template <typename C, typename D>
|
||||
template <typename E>
|
||||
void generator<C, D>::generate_link(std::ostream &ostr, const E &e) const
|
||||
{
|
||||
if (e.file().empty() && e.file_relative().empty())
|
||||
const auto &config = generators::generator<C, D>::config();
|
||||
|
||||
if (e.file().empty())
|
||||
return;
|
||||
|
||||
auto maybe_link_pattern = generators::generator<C, D>::get_link_pattern(e);
|
||||
|
||||
if (!maybe_link_pattern)
|
||||
if (config.generate_links().link.empty() &&
|
||||
config.generate_links().tooltip.empty())
|
||||
return;
|
||||
|
||||
const auto &[link_prefix, link_pattern] = *maybe_link_pattern;
|
||||
|
||||
ostr << indent(1) << "click " << e.alias() << " href \"";
|
||||
try {
|
||||
auto ec = generators::generator<C, D>::element_context(e);
|
||||
common::generators::make_context_source_relative(ec, link_prefix);
|
||||
std::string link{};
|
||||
if (!link_pattern.empty()) {
|
||||
link = generators::generator<C, D>::env().render(
|
||||
std::string_view{link_pattern}, ec);
|
||||
if (!config.generate_links().link.empty()) {
|
||||
link = env().render(std::string_view{config.generate_links().link},
|
||||
element_context(e));
|
||||
}
|
||||
if (link.empty())
|
||||
link = " ";
|
||||
ostr << link;
|
||||
}
|
||||
catch (const inja::json::parse_error &e) {
|
||||
LOG_ERROR("Failed to parse Jinja template: {}", link_pattern);
|
||||
LOG_ERROR(
|
||||
"Failed to parse Jinja template: {}", config.generate_links().link);
|
||||
ostr << " ";
|
||||
}
|
||||
catch (const inja::json::exception &e) {
|
||||
LOG_ERROR("Failed to render comment directive: \n{}\n due to: {}",
|
||||
link_pattern, e.what());
|
||||
config.generate_links().link, e.what());
|
||||
ostr << " ";
|
||||
}
|
||||
ostr << "\"";
|
||||
|
||||
auto maybe_tooltip_pattern =
|
||||
generators::generator<C, D>::get_tooltip_pattern(e);
|
||||
|
||||
if (maybe_tooltip_pattern) {
|
||||
const auto &[tooltip_prefix, tooltip_pattern] = *maybe_tooltip_pattern;
|
||||
|
||||
if (!tooltip_pattern.empty()) {
|
||||
ostr << " \"";
|
||||
try {
|
||||
auto ec = generators::generator<C, D>::element_context(e);
|
||||
common::generators::make_context_source_relative(
|
||||
ec, tooltip_prefix);
|
||||
auto tooltip_text = generators::generator<C, D>::env().render(
|
||||
std::string_view{tooltip_pattern}, ec);
|
||||
util::replace_all(tooltip_text, "\"", "„");
|
||||
ostr << tooltip_text;
|
||||
}
|
||||
catch (const inja::json::parse_error &e) {
|
||||
LOG_ERROR(
|
||||
"Failed to parse Jinja template: {}", tooltip_pattern);
|
||||
ostr << " ";
|
||||
}
|
||||
catch (const inja::json::exception &e) {
|
||||
LOG_ERROR(
|
||||
"Failed to render PlantUML directive: \n{}\n due to: {}",
|
||||
tooltip_pattern, e.what());
|
||||
ostr << " ";
|
||||
}
|
||||
|
||||
ostr << "\"";
|
||||
if (!config.generate_links().tooltip.empty()) {
|
||||
ostr << " \"";
|
||||
try {
|
||||
auto tooltip_text =
|
||||
env().render(std::string_view{config.generate_links().tooltip},
|
||||
element_context(e));
|
||||
util::replace_all(tooltip_text, "\"", "„");
|
||||
ostr << tooltip_text;
|
||||
}
|
||||
catch (const inja::json::parse_error &e) {
|
||||
LOG_ERROR("Failed to parse Jinja template: {}",
|
||||
config.generate_links().link);
|
||||
ostr << " ";
|
||||
}
|
||||
catch (const inja::json::exception &e) {
|
||||
LOG_ERROR("Failed to render PlantUML directive: \n{}\n due to: {}",
|
||||
config.generate_links().link, e.what());
|
||||
ostr << " ";
|
||||
}
|
||||
|
||||
ostr << "\"";
|
||||
}
|
||||
ostr << "\n";
|
||||
}
|
||||
@@ -285,8 +347,7 @@ void generator<C, D>::generate_mermaid_directives(
|
||||
for (const auto &d : directives) {
|
||||
try {
|
||||
// Render the directive with template engine first
|
||||
std::string directive{generators::generator<C, D>::env().render(
|
||||
std::string_view{d}, generators::generator<C, D>::context())};
|
||||
std::string directive{env().render(std::string_view{d}, context())};
|
||||
|
||||
// Now search for alias `@A()` directives in the text
|
||||
// (this is deprecated)
|
||||
@@ -391,4 +452,127 @@ std::ostream &operator<<(
|
||||
g.generate(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename C, typename D> void generator<C, D>::init_context()
|
||||
{
|
||||
const auto &config = generators::generator<C, D>::config();
|
||||
|
||||
if (config.git) {
|
||||
m_context["git"]["branch"] = config.git().branch;
|
||||
m_context["git"]["revision"] = config.git().revision;
|
||||
m_context["git"]["commit"] = config.git().commit;
|
||||
m_context["git"]["toplevel"] = config.git().toplevel;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename C, typename D> void generator<C, D>::update_context() const
|
||||
{
|
||||
m_context["diagram"] = generators::generator<C, D>::model().context();
|
||||
}
|
||||
|
||||
template <typename C, typename D> void generator<C, D>::init_env()
|
||||
{
|
||||
const auto &model = generators::generator<C, D>::model();
|
||||
const auto &config = generators::generator<C, D>::config();
|
||||
|
||||
//
|
||||
// Add basic string functions to inja environment
|
||||
//
|
||||
|
||||
// Check if string is empty
|
||||
m_env.add_callback("empty", 1, [](inja::Arguments &args) {
|
||||
return args.at(0)->get<std::string>().empty();
|
||||
});
|
||||
|
||||
// Remove spaces from the left of a string
|
||||
m_env.add_callback("ltrim", 1, [](inja::Arguments &args) {
|
||||
return util::ltrim(args.at(0)->get<std::string>());
|
||||
});
|
||||
|
||||
// Remove trailing spaces from a string
|
||||
m_env.add_callback("rtrim", 1, [](inja::Arguments &args) {
|
||||
return util::rtrim(args.at(0)->get<std::string>());
|
||||
});
|
||||
|
||||
// Remove spaces before and after a string
|
||||
m_env.add_callback("trim", 1, [](inja::Arguments &args) {
|
||||
return util::trim(args.at(0)->get<std::string>());
|
||||
});
|
||||
|
||||
// Make a string shorted with a limit to
|
||||
m_env.add_callback("abbrv", 2, [](inja::Arguments &args) {
|
||||
return util::abbreviate(
|
||||
args.at(0)->get<std::string>(), args.at(1)->get<unsigned>());
|
||||
});
|
||||
|
||||
m_env.add_callback("replace", 3, [](inja::Arguments &args) {
|
||||
std::string result = args[0]->get<std::string>();
|
||||
std::regex pattern(args[1]->get<std::string>());
|
||||
return std::regex_replace(result, pattern, args[2]->get<std::string>());
|
||||
});
|
||||
|
||||
m_env.add_callback("split", 2, [](inja::Arguments &args) {
|
||||
return util::split(
|
||||
args[0]->get<std::string>(), args[1]->get<std::string>());
|
||||
});
|
||||
|
||||
//
|
||||
// Add MermaidJS specific functions
|
||||
//
|
||||
|
||||
// Return the entire element JSON context based on element name
|
||||
// e.g.:
|
||||
// {{ element("clanguml::t00050::A").comment }}
|
||||
//
|
||||
m_env.add_callback("element", 1, [&model, &config](inja::Arguments &args) {
|
||||
inja::json res{};
|
||||
auto element_opt = model.get_with_namespace(
|
||||
args[0]->get<std::string>(), config.using_namespace());
|
||||
|
||||
if (element_opt.has_value())
|
||||
res = element_opt.value().context();
|
||||
|
||||
return res;
|
||||
});
|
||||
|
||||
// Convert C++ entity to MermaidJS alias, e.g.
|
||||
// "note left of {{ alias("A") }}: This is a note"
|
||||
// Shortcut to:
|
||||
// {{ element("A").alias }}
|
||||
//
|
||||
m_env.add_callback("alias", 1, [&model, &config](inja::Arguments &args) {
|
||||
auto element_opt = model.get_with_namespace(
|
||||
args[0]->get<std::string>(), config.using_namespace());
|
||||
|
||||
if (!element_opt.has_value())
|
||||
throw clanguml::error::uml_alias_missing(
|
||||
args[0]->get<std::string>());
|
||||
|
||||
return element_opt.value().alias();
|
||||
});
|
||||
|
||||
// Get elements' comment:
|
||||
// "note left of {{ alias("A") }}: {{ comment("A") }}"
|
||||
// Shortcut to:
|
||||
// {{ element("A").comment }}
|
||||
//
|
||||
m_env.add_callback("comment", 1, [&model, &config](inja::Arguments &args) {
|
||||
inja::json res{};
|
||||
auto element_opt = model.get_with_namespace(
|
||||
args[0]->get<std::string>(), config.using_namespace());
|
||||
|
||||
if (!element_opt.has_value())
|
||||
throw clanguml::error::uml_alias_missing(
|
||||
args[0]->get<std::string>());
|
||||
|
||||
auto comment = element_opt.value().comment();
|
||||
|
||||
if (comment.has_value()) {
|
||||
assert(comment.value().is_object());
|
||||
res = comment.value();
|
||||
}
|
||||
|
||||
return res;
|
||||
});
|
||||
}
|
||||
} // namespace clanguml::common::generators::mermaid
|
||||
@@ -18,9 +18,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/generators/generator.h"
|
||||
#include "common/model/filters/diagram_filter.h"
|
||||
#include "common/model/diagram_filter.h"
|
||||
#include "common/model/relationship.h"
|
||||
#include "config/config.h"
|
||||
#include "util/error.h"
|
||||
#include "util/util.h"
|
||||
#include "version.h"
|
||||
|
||||
@@ -62,6 +63,8 @@ public:
|
||||
: clanguml::common::generators::generator<ConfigType, DiagramType>{
|
||||
config, model}
|
||||
{
|
||||
init_context();
|
||||
init_env();
|
||||
}
|
||||
|
||||
~generator() override = default;
|
||||
@@ -174,6 +177,21 @@ public:
|
||||
*/
|
||||
void print_debug(
|
||||
const common::model::source_location &e, std::ostream &ostr) const;
|
||||
/**
|
||||
* @brief Update diagram Jinja context
|
||||
*
|
||||
* This method updates the diagram context with models properties
|
||||
* which can be used to render Jinja templates in the diagram (e.g.
|
||||
* in notes or links)
|
||||
*/
|
||||
void update_context() const;
|
||||
|
||||
protected:
|
||||
const inja::json &context() const;
|
||||
|
||||
inja::Environment &env() const;
|
||||
|
||||
template <typename E> inja::json element_context(const E &e) const;
|
||||
|
||||
private:
|
||||
void generate_row_column_hints(std::ostream &ostr,
|
||||
@@ -182,17 +200,74 @@ private:
|
||||
void generate_position_hints(std::ostream &ostr,
|
||||
const std::string &entity_name, const config::layout_hint &hint) const;
|
||||
|
||||
void init_context();
|
||||
|
||||
void init_env();
|
||||
|
||||
protected:
|
||||
mutable std::set<std::string> m_generated_aliases;
|
||||
mutable inja::json m_context;
|
||||
mutable inja::Environment m_env;
|
||||
};
|
||||
|
||||
template <typename C, typename D>
|
||||
const inja::json &generator<C, D>::context() const
|
||||
{
|
||||
return m_context;
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
inja::Environment &generator<C, D>::env() const
|
||||
{
|
||||
return m_env;
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
template <typename E>
|
||||
inja::json generator<C, D>::element_context(const E &e) const
|
||||
{
|
||||
auto ctx = context();
|
||||
|
||||
ctx["element"] = e.context();
|
||||
|
||||
if (!e.file().empty()) {
|
||||
std::filesystem::path file{e.file()};
|
||||
std::string git_relative_path = file.string();
|
||||
if (!e.file_relative().empty()) {
|
||||
#if _MSC_VER
|
||||
if (file.is_absolute() && ctx.contains("git"))
|
||||
#else
|
||||
if (file.is_absolute() && ctx.template contains("git"))
|
||||
#endif
|
||||
git_relative_path =
|
||||
std::filesystem::relative(file, ctx["git"]["toplevel"])
|
||||
.string();
|
||||
}
|
||||
else {
|
||||
git_relative_path = "";
|
||||
}
|
||||
|
||||
ctx["element"]["source"]["path"] = util::path_to_url(git_relative_path);
|
||||
ctx["element"]["source"]["full_path"] = file.string();
|
||||
ctx["element"]["source"]["name"] = file.filename().string();
|
||||
ctx["element"]["source"]["line"] = e.line();
|
||||
}
|
||||
|
||||
const auto &maybe_comment = e.comment();
|
||||
if (maybe_comment) {
|
||||
ctx["element"]["comment"] = maybe_comment.value();
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
void generator<C, D>::generate(std::ostream &ostr) const
|
||||
{
|
||||
const auto &config = generators::generator<C, D>::config();
|
||||
const auto &model = generators::generator<C, D>::model();
|
||||
|
||||
generators::generator<C, D>::update_context();
|
||||
update_context();
|
||||
|
||||
if (!config.allow_empty_diagrams() && model.is_empty() &&
|
||||
config.puml().before.empty() && config.puml().after.empty()) {
|
||||
@@ -338,8 +413,7 @@ void generator<C, D>::generate_plantuml_directives(
|
||||
for (const auto &d : directives) {
|
||||
try {
|
||||
// Render the directive with template engine first
|
||||
std::string directive{generators::generator<C, D>::env().render(
|
||||
std::string_view{d}, generators::generator<C, D>::context())};
|
||||
std::string directive{env().render(std::string_view{d}, context())};
|
||||
|
||||
// Now search for alias `@A()` directives in the text
|
||||
// (this is deprecated)
|
||||
@@ -445,59 +519,45 @@ template <typename C, typename D>
|
||||
template <typename E>
|
||||
void generator<C, D>::generate_link(std::ostream &ostr, const E &e) const
|
||||
{
|
||||
if (e.file().empty() && e.file_relative().empty())
|
||||
const auto &config = generators::generator<C, D>::config();
|
||||
|
||||
if (e.file_relative().empty())
|
||||
return;
|
||||
|
||||
auto maybe_link_pattern = generators::generator<C, D>::get_link_pattern(e);
|
||||
|
||||
if (!maybe_link_pattern) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &[link_prefix, link_pattern] = *maybe_link_pattern;
|
||||
|
||||
ostr << " [[";
|
||||
try {
|
||||
if (!link_pattern.empty()) {
|
||||
auto ec = generators::generator<C, D>::element_context(e);
|
||||
common::generators::make_context_source_relative(ec, link_prefix);
|
||||
ostr << generators::generator<C, D>::env().render(
|
||||
std::string_view{link_pattern}, ec);
|
||||
if (!config.generate_links().link.empty()) {
|
||||
ostr << env().render(std::string_view{config.generate_links().link},
|
||||
element_context(e));
|
||||
}
|
||||
}
|
||||
catch (const inja::json::parse_error &e) {
|
||||
LOG_ERROR("Failed to parse Jinja template: {}", link_pattern);
|
||||
LOG_ERROR(
|
||||
"Failed to parse Jinja template: {}", config.generate_links().link);
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
catch (const inja::json::exception &e) {
|
||||
LOG_ERROR("Failed to render PlantUML directive: \n{}\n due to: {}",
|
||||
link_pattern, e.what());
|
||||
config.generate_links().link, e.what());
|
||||
}
|
||||
|
||||
auto maybe_tooltip_pattern =
|
||||
generators::generator<C, D>::get_tooltip_pattern(e);
|
||||
|
||||
if (maybe_tooltip_pattern) {
|
||||
const auto &[tooltip_prefix, tooltip_pattern] = *maybe_tooltip_pattern;
|
||||
|
||||
ostr << "{";
|
||||
try {
|
||||
auto ec = generators::generator<C, D>::element_context(e);
|
||||
common::generators::make_context_source_relative(
|
||||
ec, tooltip_prefix);
|
||||
if (!tooltip_pattern.empty()) {
|
||||
ostr << generators::generator<C, D>::env().render(
|
||||
std::string_view{tooltip_pattern}, ec);
|
||||
}
|
||||
ostr << "{";
|
||||
try {
|
||||
if (!config.generate_links().tooltip.empty()) {
|
||||
ostr << env().render(
|
||||
std::string_view{config.generate_links().tooltip},
|
||||
element_context(e));
|
||||
}
|
||||
catch (const inja::json::parse_error &e) {
|
||||
LOG_ERROR("Failed to parse Jinja template: {}", tooltip_pattern);
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
LOG_ERROR("Failed to render PlantUML directive: \n{}\n due to: {}",
|
||||
tooltip_pattern, e.what());
|
||||
}
|
||||
ostr << "}";
|
||||
}
|
||||
catch (const inja::json::parse_error &e) {
|
||||
LOG_ERROR(
|
||||
"Failed to parse Jinja template: {}", config.generate_links().link);
|
||||
}
|
||||
catch (const inja::json::exception &e) {
|
||||
LOG_ERROR("Failed to render PlantUML directive: \n{}\n due to: {}",
|
||||
config.generate_links().link, e.what());
|
||||
}
|
||||
|
||||
ostr << "}";
|
||||
ostr << "]]";
|
||||
}
|
||||
|
||||
@@ -519,4 +579,126 @@ std::ostream &operator<<(
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename C, typename D> void generator<C, D>::init_context()
|
||||
{
|
||||
const auto &config = generators::generator<C, D>::config();
|
||||
|
||||
if (config.git) {
|
||||
m_context["git"]["branch"] = config.git().branch;
|
||||
m_context["git"]["revision"] = config.git().revision;
|
||||
m_context["git"]["commit"] = config.git().commit;
|
||||
m_context["git"]["toplevel"] = config.git().toplevel;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename C, typename D> void generator<C, D>::update_context() const
|
||||
{
|
||||
m_context["diagram"] = generators::generator<C, D>::model().context();
|
||||
}
|
||||
|
||||
template <typename C, typename D> void generator<C, D>::init_env()
|
||||
{
|
||||
const auto &model = generators::generator<C, D>::model();
|
||||
const auto &config = generators::generator<C, D>::config();
|
||||
|
||||
//
|
||||
// Add basic string functions to inja environment
|
||||
//
|
||||
|
||||
// Check if string is empty
|
||||
m_env.add_callback("empty", 1, [](inja::Arguments &args) {
|
||||
return args.at(0)->get<std::string>().empty();
|
||||
});
|
||||
|
||||
// Remove spaces from the left of a string
|
||||
m_env.add_callback("ltrim", 1, [](inja::Arguments &args) {
|
||||
return util::ltrim(args.at(0)->get<std::string>());
|
||||
});
|
||||
|
||||
// Remove trailing spaces from a string
|
||||
m_env.add_callback("rtrim", 1, [](inja::Arguments &args) {
|
||||
return util::rtrim(args.at(0)->get<std::string>());
|
||||
});
|
||||
|
||||
// Remove spaces before and after a string
|
||||
m_env.add_callback("trim", 1, [](inja::Arguments &args) {
|
||||
return util::trim(args.at(0)->get<std::string>());
|
||||
});
|
||||
|
||||
// Make a string shorted with a limit to
|
||||
m_env.add_callback("abbrv", 2, [](inja::Arguments &args) {
|
||||
return util::abbreviate(
|
||||
args.at(0)->get<std::string>(), args.at(1)->get<unsigned>());
|
||||
});
|
||||
|
||||
m_env.add_callback("replace", 3, [](inja::Arguments &args) {
|
||||
std::string result = args[0]->get<std::string>();
|
||||
std::regex pattern(args[1]->get<std::string>());
|
||||
return std::regex_replace(result, pattern, args[2]->get<std::string>());
|
||||
});
|
||||
|
||||
m_env.add_callback("split", 2, [](inja::Arguments &args) {
|
||||
return util::split(
|
||||
args[0]->get<std::string>(), args[1]->get<std::string>());
|
||||
});
|
||||
|
||||
//
|
||||
// Add PlantUML specific functions
|
||||
//
|
||||
|
||||
// Return the entire element JSON context based on element name
|
||||
// e.g.:
|
||||
// {{ element("clanguml::t00050::A").comment }}
|
||||
//
|
||||
m_env.add_callback("element", 1, [&model, &config](inja::Arguments &args) {
|
||||
inja::json res{};
|
||||
auto element_opt = model.get_with_namespace(
|
||||
args[0]->get<std::string>(), config.using_namespace());
|
||||
|
||||
if (element_opt.has_value())
|
||||
res = element_opt.value().context();
|
||||
|
||||
return res;
|
||||
});
|
||||
|
||||
// Convert C++ entity to PlantUML alias, e.g.
|
||||
// "note left of {{ alias("A") }}: This is a note"
|
||||
// Shortcut to:
|
||||
// {{ element("A").alias }}
|
||||
//
|
||||
m_env.add_callback("alias", 1, [&model, &config](inja::Arguments &args) {
|
||||
auto element_opt = model.get_with_namespace(
|
||||
args[0]->get<std::string>(), config.using_namespace());
|
||||
|
||||
if (!element_opt.has_value())
|
||||
throw clanguml::error::uml_alias_missing(
|
||||
args[0]->get<std::string>());
|
||||
|
||||
return element_opt.value().alias();
|
||||
});
|
||||
|
||||
// Get elements' comment:
|
||||
// "note left of {{ alias("A") }}: {{ comment("A") }}"
|
||||
// Shortcut to:
|
||||
// {{ element("A").comment }}
|
||||
//
|
||||
m_env.add_callback("comment", 1, [&model, &config](inja::Arguments &args) {
|
||||
inja::json res{};
|
||||
auto element_opt = model.get_with_namespace(
|
||||
args[0]->get<std::string>(), config.using_namespace());
|
||||
|
||||
if (!element_opt.has_value())
|
||||
throw clanguml::error::uml_alias_missing(
|
||||
args[0]->get<std::string>());
|
||||
|
||||
auto comment = element_opt.value().comment();
|
||||
|
||||
if (comment.has_value()) {
|
||||
assert(comment.value().is_object());
|
||||
res = comment.value();
|
||||
}
|
||||
|
||||
return res;
|
||||
});
|
||||
}
|
||||
} // namespace clanguml::common::generators::plantuml
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "diagram.h"
|
||||
|
||||
#include "filters/diagram_filter.h"
|
||||
#include "diagram_filter.h"
|
||||
#include "namespace.h"
|
||||
|
||||
namespace clanguml::common::model {
|
||||
@@ -59,35 +59,19 @@ void diagram::set_complete(bool complete) { complete_ = complete; }
|
||||
|
||||
bool diagram::complete() const { return complete_; }
|
||||
|
||||
void diagram::finalize()
|
||||
{
|
||||
// Remove elements that do not match the filter
|
||||
apply_filter();
|
||||
filtered_ = true;
|
||||
}
|
||||
void diagram::finalize() { }
|
||||
|
||||
bool diagram::should_include(const element &e) const
|
||||
{
|
||||
if (filtered_)
|
||||
return true;
|
||||
|
||||
if (filter_.get() == nullptr)
|
||||
return true;
|
||||
|
||||
if (!complete()) {
|
||||
return filter_->should_include(
|
||||
dynamic_cast<const source_location &>(e));
|
||||
}
|
||||
|
||||
return filter_->should_include(e) &&
|
||||
filter_->should_include(dynamic_cast<const source_location &>(e));
|
||||
}
|
||||
|
||||
bool diagram::should_include(const namespace_ &ns) const
|
||||
{
|
||||
if (filtered_)
|
||||
return true;
|
||||
|
||||
if (filter_.get() == nullptr)
|
||||
return true;
|
||||
|
||||
|
||||
@@ -171,13 +171,10 @@ public:
|
||||
*/
|
||||
virtual bool is_empty() const = 0;
|
||||
|
||||
virtual void apply_filter() { }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::unique_ptr<diagram_filter> filter_;
|
||||
bool complete_{false};
|
||||
bool filtered_{false};
|
||||
};
|
||||
|
||||
template <typename DiagramT> bool check_diagram_type(diagram_t t);
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "diagram_element.h"
|
||||
|
||||
#include "common/model/filters/diagram_filter.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <ostream>
|
||||
@@ -103,19 +102,6 @@ bool diagram_element::complete() const { return complete_; }
|
||||
|
||||
void diagram_element::complete(bool completed) { complete_ = completed; }
|
||||
|
||||
void diagram_element::apply_filter(
|
||||
const diagram_filter &filter, const std::set<eid_t> &removed)
|
||||
{
|
||||
common::model::apply_filter(relationships(), filter);
|
||||
|
||||
auto &rels = relationships();
|
||||
rels.erase(std::remove_if(std::begin(rels), std::end(rels),
|
||||
[&removed](auto &&r) {
|
||||
return removed.count(r.destination()) > 0;
|
||||
}),
|
||||
std::end(rels));
|
||||
}
|
||||
|
||||
bool operator==(const diagram_element &l, const diagram_element &r)
|
||||
{
|
||||
return l.id() == r.id();
|
||||
|
||||
@@ -26,14 +26,11 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
class diagram_filter;
|
||||
|
||||
/**
|
||||
* @brief Base class for standalone diagram elements.
|
||||
*
|
||||
@@ -187,9 +184,6 @@ public:
|
||||
*/
|
||||
void complete(bool completed);
|
||||
|
||||
virtual void apply_filter(
|
||||
const diagram_filter &filter, const std::set<eid_t> &removed);
|
||||
|
||||
private:
|
||||
eid_t id_{};
|
||||
std::optional<eid_t> parent_element_id_{};
|
||||
|
||||
@@ -99,12 +99,6 @@ tvl::value_t filter_visitor::match(
|
||||
return {};
|
||||
}
|
||||
|
||||
tvl::value_t filter_visitor::match(
|
||||
const diagram &d, const common::model::relationship &r) const
|
||||
{
|
||||
return match(d, r.type());
|
||||
}
|
||||
|
||||
tvl::value_t filter_visitor::match(
|
||||
const diagram & /*d*/, const common::model::relationship_t & /*r*/) const
|
||||
{
|
||||
@@ -165,10 +159,6 @@ bool filter_visitor::is_exclusive() const
|
||||
|
||||
filter_t filter_visitor::type() const { return type_; }
|
||||
|
||||
filter_mode_t filter_visitor::mode() const { return mode_; }
|
||||
|
||||
void filter_visitor::set_mode(filter_mode_t mode) { mode_ = mode; }
|
||||
|
||||
anyof_filter::anyof_filter(
|
||||
filter_t type, std::vector<std::unique_ptr<filter_visitor>> filters)
|
||||
: filter_visitor{type}
|
||||
@@ -179,116 +169,22 @@ anyof_filter::anyof_filter(
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const common::model::element &e) const
|
||||
{
|
||||
return match_anyof(d, e);
|
||||
}
|
||||
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const common::model::relationship_t &r) const
|
||||
{
|
||||
return match_anyof(d, r);
|
||||
}
|
||||
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const common::model::access_t &a) const
|
||||
{
|
||||
return match_anyof(d, a);
|
||||
}
|
||||
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const common::model::namespace_ &ns) const
|
||||
{
|
||||
return match_anyof(d, ns);
|
||||
}
|
||||
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const common::model::source_file &f) const
|
||||
{
|
||||
return match_anyof(d, f);
|
||||
}
|
||||
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const common::model::source_location &f) const
|
||||
{
|
||||
return match_anyof(d, f);
|
||||
}
|
||||
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const class_diagram::model::class_method &m) const
|
||||
{
|
||||
return match_anyof(d, m);
|
||||
}
|
||||
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const class_diagram::model::class_member &m) const
|
||||
{
|
||||
return match_anyof(d, m);
|
||||
return tvl::any_of(filters_.begin(), filters_.end(),
|
||||
[&d, &e](const auto &f) { return f->match(d, e); });
|
||||
}
|
||||
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const sequence_diagram::model::participant &p) const
|
||||
{
|
||||
return match_anyof(d, p);
|
||||
return tvl::any_of(filters_.begin(), filters_.end(),
|
||||
[&d, &p](const auto &f) { return f->match(d, p); });
|
||||
}
|
||||
|
||||
allof_filter::allof_filter(
|
||||
filter_t type, std::vector<std::unique_ptr<filter_visitor>> filters)
|
||||
: filter_visitor{type}
|
||||
, filters_{std::move(filters)}
|
||||
tvl::value_t anyof_filter::match(
|
||||
const diagram &d, const common::model::source_file &e) const
|
||||
{
|
||||
}
|
||||
|
||||
tvl::value_t allof_filter::match(
|
||||
const diagram &d, const common::model::element &e) const
|
||||
{
|
||||
return match_allof(d, e);
|
||||
}
|
||||
|
||||
tvl::value_t allof_filter::match(
|
||||
const diagram &d, const common::model::relationship_t &r) const
|
||||
{
|
||||
return match_allof(d, r);
|
||||
}
|
||||
|
||||
tvl::value_t allof_filter::match(
|
||||
const diagram &d, const common::model::access_t &a) const
|
||||
{
|
||||
return match_allof(d, a);
|
||||
}
|
||||
|
||||
tvl::value_t allof_filter::match(
|
||||
const diagram &d, const common::model::namespace_ &ns) const
|
||||
{
|
||||
return match_allof(d, ns);
|
||||
}
|
||||
|
||||
tvl::value_t allof_filter::match(
|
||||
const diagram &d, const common::model::source_file &f) const
|
||||
{
|
||||
return match_allof(d, f);
|
||||
}
|
||||
|
||||
tvl::value_t allof_filter::match(
|
||||
const diagram &d, const common::model::source_location &f) const
|
||||
{
|
||||
return match_allof(d, f);
|
||||
}
|
||||
|
||||
tvl::value_t allof_filter::match(
|
||||
const diagram &d, const class_diagram::model::class_method &m) const
|
||||
{
|
||||
return match_allof(d, m);
|
||||
}
|
||||
|
||||
tvl::value_t allof_filter::match(
|
||||
const diagram &d, const class_diagram::model::class_member &m) const
|
||||
{
|
||||
return match_allof(d, m);
|
||||
}
|
||||
|
||||
tvl::value_t allof_filter::match(
|
||||
const diagram &d, const sequence_diagram::model::participant &p) const
|
||||
{
|
||||
return match_allof(d, p);
|
||||
return tvl::any_of(filters_.begin(), filters_.end(),
|
||||
[&d, &e](const auto &f) { return f->match(d, e); });
|
||||
}
|
||||
|
||||
namespace_filter::namespace_filter(
|
||||
@@ -389,12 +285,6 @@ tvl::value_t namespace_filter::match(const diagram &d, const element &e) const
|
||||
return result;
|
||||
}
|
||||
|
||||
tvl::value_t namespace_filter::match(
|
||||
const diagram &d, const sequence_diagram::model::participant &p) const
|
||||
{
|
||||
return match(d, dynamic_cast<const element &>(p));
|
||||
}
|
||||
|
||||
modules_filter::modules_filter(
|
||||
filter_t type, std::vector<common::string_or_regex> modules)
|
||||
: filter_visitor{type}
|
||||
@@ -955,8 +845,8 @@ bool context_filter::is_outward(relationship_t r) const
|
||||
return r != relationship_t::kAssociation;
|
||||
}
|
||||
|
||||
paths_filter::paths_filter(filter_t type, const std::vector<std::string> &p,
|
||||
const std::filesystem::path &root)
|
||||
paths_filter::paths_filter(filter_t type, const std::filesystem::path &root,
|
||||
const std::vector<std::string> &p)
|
||||
: filter_visitor{type}
|
||||
, root_{root}
|
||||
{
|
||||
@@ -1009,7 +899,7 @@ tvl::value_t paths_filter::match(
|
||||
return {};
|
||||
}
|
||||
|
||||
// Matching source paths doesn't make sense if they are not absolute
|
||||
// Matching source paths doesn't make sens if they are not absolute
|
||||
if (!p.is_absolute()) {
|
||||
return {};
|
||||
}
|
||||
@@ -1082,19 +972,11 @@ tvl::value_t class_member_filter::match(
|
||||
return access_filter_->match(d, m.access());
|
||||
}
|
||||
|
||||
diagram_filter::diagram_filter(const common::model::diagram &d,
|
||||
const config::diagram & /*c*/, private_constructor_tag_t /*unused*/)
|
||||
diagram_filter::diagram_filter(
|
||||
const common::model::diagram &d, const config::diagram &c)
|
||||
: diagram_{d}
|
||||
{
|
||||
}
|
||||
|
||||
void diagram_filter::add_filter(
|
||||
filter_t filter_type, std::unique_ptr<filter_visitor> fv)
|
||||
{
|
||||
if (filter_type == filter_t::kInclusive)
|
||||
add_inclusive_filter(std::move(fv));
|
||||
else
|
||||
add_exclusive_filter(std::move(fv));
|
||||
init_filters(c);
|
||||
}
|
||||
|
||||
void diagram_filter::add_inclusive_filter(std::unique_ptr<filter_visitor> fv)
|
||||
@@ -1121,6 +1003,254 @@ bool diagram_filter::should_include(
|
||||
return false;
|
||||
}
|
||||
|
||||
void diagram_filter::init_filters(const config::diagram &c)
|
||||
{
|
||||
using specializations_filter_t =
|
||||
edge_traversal_filter<class_diagram::model::diagram,
|
||||
class_diagram::model::class_, common::string_or_regex>;
|
||||
|
||||
using class_dependants_filter_t =
|
||||
edge_traversal_filter<class_diagram::model::diagram,
|
||||
class_diagram::model::class_, common::string_or_regex>;
|
||||
using class_dependencies_filter_t =
|
||||
edge_traversal_filter<class_diagram::model::diagram,
|
||||
class_diagram::model::class_, common::string_or_regex>;
|
||||
|
||||
using package_dependants_filter_t =
|
||||
edge_traversal_filter<package_diagram::model::diagram,
|
||||
common::model::package, common::string_or_regex>;
|
||||
using package_dependencies_filter_t =
|
||||
edge_traversal_filter<package_diagram::model::diagram,
|
||||
common::model::package, common::string_or_regex>;
|
||||
|
||||
using source_file_dependency_filter_t =
|
||||
edge_traversal_filter<include_diagram::model::diagram,
|
||||
common::model::source_file, std::string,
|
||||
common::model::source_file>;
|
||||
|
||||
// Process inclusive filters
|
||||
if (c.include) {
|
||||
add_inclusive_filter(std::make_unique<namespace_filter>(
|
||||
filter_t::kInclusive, c.include().namespaces));
|
||||
|
||||
add_inclusive_filter(std::make_unique<modules_filter>(
|
||||
filter_t::kInclusive, c.include().modules));
|
||||
|
||||
add_inclusive_filter(std::make_unique<module_access_filter>(
|
||||
filter_t::kInclusive, c.include().module_access));
|
||||
|
||||
add_inclusive_filter(std::make_unique<relationship_filter>(
|
||||
filter_t::kInclusive, c.include().relationships));
|
||||
|
||||
add_inclusive_filter(std::make_unique<access_filter>(
|
||||
filter_t::kInclusive, c.include().access));
|
||||
|
||||
add_inclusive_filter(std::make_unique<paths_filter>(
|
||||
filter_t::kInclusive, c.root_directory(), c.include().paths));
|
||||
|
||||
add_inclusive_filter(
|
||||
std::make_unique<class_method_filter>(filter_t::kInclusive,
|
||||
std::make_unique<access_filter>(
|
||||
filter_t::kInclusive, c.include().access),
|
||||
std::make_unique<method_type_filter>(
|
||||
filter_t::kInclusive, c.include().method_types)));
|
||||
|
||||
add_inclusive_filter(
|
||||
std::make_unique<class_member_filter>(filter_t::kInclusive,
|
||||
std::make_unique<access_filter>(
|
||||
filter_t::kInclusive, c.include().access)));
|
||||
|
||||
// Include any of these matches even if one them does not match
|
||||
std::vector<std::unique_ptr<filter_visitor>> element_filters;
|
||||
|
||||
element_filters.emplace_back(std::make_unique<element_filter>(
|
||||
filter_t::kInclusive, c.include().elements));
|
||||
|
||||
element_filters.emplace_back(std::make_unique<element_type_filter>(
|
||||
filter_t::kInclusive, c.include().element_types));
|
||||
|
||||
if (c.type() == diagram_t::kClass) {
|
||||
element_filters.emplace_back(std::make_unique<subclass_filter>(
|
||||
filter_t::kInclusive, c.include().subclasses));
|
||||
|
||||
element_filters.emplace_back(std::make_unique<parents_filter>(
|
||||
filter_t::kInclusive, c.include().parents));
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<specializations_filter_t>(filter_t::kInclusive,
|
||||
relationship_t::kInstantiation,
|
||||
c.include().specializations));
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<class_dependants_filter_t>(
|
||||
filter_t::kInclusive, relationship_t::kDependency,
|
||||
c.include().dependants));
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<class_dependencies_filter_t>(
|
||||
filter_t::kInclusive, relationship_t::kDependency,
|
||||
c.include().dependencies, true));
|
||||
}
|
||||
else if (c.type() == diagram_t::kSequence) {
|
||||
element_filters.emplace_back(std::make_unique<callee_filter>(
|
||||
filter_t::kInclusive, c.include().callee_types));
|
||||
}
|
||||
else if (c.type() == diagram_t::kPackage) {
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<package_dependants_filter_t>(
|
||||
filter_t::kInclusive, relationship_t::kDependency,
|
||||
c.include().dependants));
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<package_dependencies_filter_t>(
|
||||
filter_t::kInclusive, relationship_t::kDependency,
|
||||
c.include().dependencies, true));
|
||||
}
|
||||
else if (c.type() == diagram_t::kInclude) {
|
||||
std::vector<std::string> dependants;
|
||||
std::vector<std::string> dependencies;
|
||||
|
||||
for (auto &&path : c.include().dependants) {
|
||||
if (auto p = path.get<std::string>(); p.has_value()) {
|
||||
const std::filesystem::path dep_path{*p};
|
||||
dependants.emplace_back(
|
||||
dep_path.lexically_normal().string());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &&path : c.include().dependencies) {
|
||||
if (auto p = path.get<std::string>(); p.has_value()) {
|
||||
const std::filesystem::path dep_path{*p};
|
||||
dependencies.emplace_back(
|
||||
dep_path.lexically_normal().string());
|
||||
}
|
||||
}
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<source_file_dependency_filter_t>(
|
||||
filter_t::kInclusive, relationship_t::kAssociation,
|
||||
dependants, false));
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<source_file_dependency_filter_t>(
|
||||
filter_t::kInclusive, relationship_t::kAssociation,
|
||||
dependencies, true));
|
||||
}
|
||||
|
||||
element_filters.emplace_back(std::make_unique<context_filter>(
|
||||
filter_t::kInclusive, c.include().context));
|
||||
|
||||
add_inclusive_filter(std::make_unique<anyof_filter>(
|
||||
filter_t::kInclusive, std::move(element_filters)));
|
||||
}
|
||||
|
||||
// Process exclusive filters
|
||||
if (c.exclude) {
|
||||
add_exclusive_filter(std::make_unique<namespace_filter>(
|
||||
filter_t::kExclusive, c.exclude().namespaces));
|
||||
|
||||
add_exclusive_filter(std::make_unique<modules_filter>(
|
||||
filter_t::kExclusive, c.exclude().modules));
|
||||
|
||||
add_exclusive_filter(std::make_unique<module_access_filter>(
|
||||
filter_t::kExclusive, c.exclude().module_access));
|
||||
|
||||
add_exclusive_filter(std::make_unique<paths_filter>(
|
||||
filter_t::kExclusive, c.root_directory(), c.exclude().paths));
|
||||
|
||||
add_exclusive_filter(std::make_unique<element_filter>(
|
||||
filter_t::kExclusive, c.exclude().elements));
|
||||
|
||||
add_exclusive_filter(std::make_unique<element_type_filter>(
|
||||
filter_t::kExclusive, c.exclude().element_types));
|
||||
|
||||
add_exclusive_filter(std::make_unique<relationship_filter>(
|
||||
filter_t::kExclusive, c.exclude().relationships));
|
||||
|
||||
add_exclusive_filter(std::make_unique<access_filter>(
|
||||
filter_t::kExclusive, c.exclude().access));
|
||||
|
||||
add_exclusive_filter(
|
||||
std::make_unique<class_method_filter>(filter_t::kExclusive,
|
||||
std::make_unique<access_filter>(
|
||||
filter_t::kExclusive, c.exclude().access),
|
||||
std::make_unique<method_type_filter>(
|
||||
filter_t::kExclusive, c.exclude().method_types)));
|
||||
|
||||
add_exclusive_filter(
|
||||
std::make_unique<class_member_filter>(filter_t::kExclusive,
|
||||
std::make_unique<access_filter>(
|
||||
filter_t::kExclusive, c.exclude().access)));
|
||||
|
||||
add_exclusive_filter(std::make_unique<subclass_filter>(
|
||||
filter_t::kExclusive, c.exclude().subclasses));
|
||||
|
||||
add_exclusive_filter(std::make_unique<parents_filter>(
|
||||
filter_t::kExclusive, c.exclude().parents));
|
||||
|
||||
add_exclusive_filter(
|
||||
std::make_unique<specializations_filter_t>(filter_t::kExclusive,
|
||||
relationship_t::kInstantiation, c.exclude().specializations));
|
||||
|
||||
if (c.type() == diagram_t::kClass) {
|
||||
add_exclusive_filter(std::make_unique<class_dependants_filter_t>(
|
||||
filter_t::kExclusive, relationship_t::kDependency,
|
||||
c.exclude().dependants));
|
||||
|
||||
add_exclusive_filter(std::make_unique<class_dependencies_filter_t>(
|
||||
filter_t::kExclusive, relationship_t::kDependency,
|
||||
c.exclude().dependencies, true));
|
||||
}
|
||||
else if (c.type() == diagram_t::kSequence) {
|
||||
add_exclusive_filter(std::make_unique<callee_filter>(
|
||||
filter_t::kExclusive, c.exclude().callee_types));
|
||||
}
|
||||
else if (c.type() == diagram_t::kPackage) {
|
||||
add_exclusive_filter(
|
||||
std::make_unique<package_dependencies_filter_t>(
|
||||
filter_t::kExclusive, relationship_t::kDependency,
|
||||
c.exclude().dependencies, true));
|
||||
|
||||
add_exclusive_filter(std::make_unique<package_dependants_filter_t>(
|
||||
filter_t::kExclusive, relationship_t::kDependency,
|
||||
c.exclude().dependants));
|
||||
}
|
||||
else if (c.type() == diagram_t::kInclude) {
|
||||
std::vector<std::string> dependants;
|
||||
std::vector<std::string> dependencies;
|
||||
|
||||
for (auto &&path : c.exclude().dependants) {
|
||||
if (auto p = path.get<std::string>(); p.has_value()) {
|
||||
std::filesystem::path dep_path{*p};
|
||||
dependants.emplace_back(
|
||||
dep_path.lexically_normal().string());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &&path : c.exclude().dependencies) {
|
||||
if (auto p = path.get<std::string>(); p.has_value()) {
|
||||
std::filesystem::path dep_path{*p};
|
||||
dependencies.emplace_back(
|
||||
dep_path.lexically_normal().string());
|
||||
}
|
||||
}
|
||||
|
||||
add_exclusive_filter(
|
||||
std::make_unique<source_file_dependency_filter_t>(
|
||||
filter_t::kExclusive, relationship_t::kAssociation,
|
||||
dependants, false));
|
||||
|
||||
add_exclusive_filter(
|
||||
std::make_unique<source_file_dependency_filter_t>(
|
||||
filter_t::kExclusive, relationship_t::kAssociation,
|
||||
dependencies, true));
|
||||
}
|
||||
|
||||
add_exclusive_filter(std::make_unique<context_filter>(
|
||||
filter_t::kExclusive, c.exclude().context));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
bool diagram_filter::should_include<std::string>(const std::string &name) const
|
||||
{
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @file src/common/model/filters/diagram_filter.h
|
||||
* @file src/common/model/diagram_filter.h
|
||||
*
|
||||
* Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
|
||||
*
|
||||
@@ -25,21 +25,19 @@
|
||||
#include "common/model/element.h"
|
||||
#include "common/model/enums.h"
|
||||
#include "common/model/namespace.h"
|
||||
#include "common/model/source_file.h"
|
||||
#include "common/model/tvl.h"
|
||||
#include "config/config.h"
|
||||
#include "diagram.h"
|
||||
#include "include_diagram/model/diagram.h"
|
||||
#include "sequence_diagram/model/participant.h"
|
||||
#include "source_file.h"
|
||||
#include "tvl.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <utility>
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
class diagram_filter_factory;
|
||||
|
||||
using clanguml::common::eid_t;
|
||||
using clanguml::config::filter_mode_t;
|
||||
|
||||
/**
|
||||
* Diagram filters can be add in 2 modes:
|
||||
@@ -86,9 +84,6 @@ public:
|
||||
virtual tvl::value_t match(
|
||||
const diagram &d, const common::model::element &e) const;
|
||||
|
||||
virtual tvl::value_t match(
|
||||
const diagram &d, const common::model::relationship &r) const;
|
||||
|
||||
virtual tvl::value_t match(
|
||||
const diagram &d, const common::model::relationship_t &r) const;
|
||||
|
||||
@@ -117,12 +112,9 @@ public:
|
||||
bool is_exclusive() const;
|
||||
|
||||
filter_t type() const;
|
||||
filter_mode_t mode() const;
|
||||
void set_mode(filter_mode_t mode);
|
||||
|
||||
private:
|
||||
filter_t type_;
|
||||
filter_mode_t mode_{filter_mode_t::basic};
|
||||
};
|
||||
|
||||
struct anyof_filter : public filter_visitor {
|
||||
@@ -134,87 +126,13 @@ struct anyof_filter : public filter_visitor {
|
||||
tvl::value_t match(
|
||||
const diagram &d, const common::model::element &e) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const common::model::relationship_t &r) const override;
|
||||
|
||||
tvl::value_t match(
|
||||
const diagram &d, const common::model::access_t &a) const override;
|
||||
|
||||
tvl::value_t match(
|
||||
const diagram &d, const common::model::namespace_ &ns) const override;
|
||||
|
||||
tvl::value_t match(
|
||||
const diagram &d, const common::model::source_file &f) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const common::model::source_location &f) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const class_diagram::model::class_method &m) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const class_diagram::model::class_member &m) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const sequence_diagram::model::participant &p) const override;
|
||||
|
||||
private:
|
||||
template <typename E>
|
||||
tvl::value_t match_anyof(const diagram &d, const E &element) const
|
||||
{
|
||||
auto result = tvl::any_of(filters_.begin(), filters_.end(),
|
||||
[&d, &element](const auto &f) { return f->match(d, element); });
|
||||
|
||||
if (mode() == filter_mode_t::advanced && !d.complete())
|
||||
return type() == filter_t::kInclusive;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<filter_visitor>> filters_;
|
||||
};
|
||||
|
||||
struct allof_filter : public filter_visitor {
|
||||
allof_filter(
|
||||
filter_t type, std::vector<std::unique_ptr<filter_visitor>> filters);
|
||||
|
||||
~allof_filter() override = default;
|
||||
|
||||
tvl::value_t match(
|
||||
const diagram &d, const common::model::element &e) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const common::model::relationship_t &r) const override;
|
||||
|
||||
tvl::value_t match(
|
||||
const diagram &d, const common::model::access_t &a) const override;
|
||||
|
||||
tvl::value_t match(
|
||||
const diagram &d, const common::model::namespace_ &ns) const override;
|
||||
|
||||
tvl::value_t match(
|
||||
const diagram &d, const common::model::source_file &f) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const common::model::source_location &f) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const class_diagram::model::class_method &m) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const class_diagram::model::class_member &m) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const sequence_diagram::model::participant &p) const override;
|
||||
const diagram &d, const common::model::source_file &e) const override;
|
||||
|
||||
private:
|
||||
template <typename E>
|
||||
tvl::value_t match_allof(const diagram &d, const E &element) const
|
||||
{
|
||||
return tvl::all_of(filters_.begin(), filters_.end(),
|
||||
[&d, &element](const auto &f) { return f->match(d, element); });
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<filter_visitor>> filters_;
|
||||
};
|
||||
|
||||
@@ -232,9 +150,6 @@ struct namespace_filter : public filter_visitor {
|
||||
|
||||
tvl::value_t match(const diagram &d, const element &e) const override;
|
||||
|
||||
tvl::value_t match(const diagram &d,
|
||||
const sequence_diagram::model::participant &p) const override;
|
||||
|
||||
private:
|
||||
std::vector<common::namespace_or_regex> namespaces_;
|
||||
};
|
||||
@@ -365,8 +280,8 @@ template <typename DiagramT, typename ElementT,
|
||||
typename ConfigEntryT = std::string,
|
||||
typename MatchOverrideT = common::model::element>
|
||||
struct edge_traversal_filter : public filter_visitor {
|
||||
edge_traversal_filter(filter_t type, std::vector<ConfigEntryT> roots,
|
||||
relationship_t relationship, bool forward = false)
|
||||
edge_traversal_filter(filter_t type, relationship_t relationship,
|
||||
std::vector<ConfigEntryT> roots, bool forward = false)
|
||||
: filter_visitor{type}
|
||||
, roots_{std::move(roots)}
|
||||
, relationship_{relationship}
|
||||
@@ -697,8 +612,8 @@ private:
|
||||
* a specified file paths.
|
||||
*/
|
||||
struct paths_filter : public filter_visitor {
|
||||
paths_filter(filter_t type, const std::vector<std::string> &p,
|
||||
const std::filesystem::path &root);
|
||||
paths_filter(filter_t type, const std::filesystem::path &root,
|
||||
const std::vector<std::string> &p);
|
||||
|
||||
~paths_filter() override = default;
|
||||
|
||||
@@ -745,8 +660,6 @@ private:
|
||||
std::unique_ptr<access_filter> access_filter_;
|
||||
};
|
||||
|
||||
class diagram_filter_factory;
|
||||
|
||||
/**
|
||||
* @brief Composite of all diagrams filters.
|
||||
*
|
||||
@@ -758,14 +671,8 @@ class diagram_filter_factory;
|
||||
* @see clanguml::common::model::filter_visitor
|
||||
*/
|
||||
class diagram_filter {
|
||||
private:
|
||||
struct private_constructor_tag_t { };
|
||||
|
||||
public:
|
||||
diagram_filter(const common::model::diagram &d, const config::diagram &c,
|
||||
private_constructor_tag_t unused);
|
||||
|
||||
void add_filter(filter_t filter_type, std::unique_ptr<filter_visitor> fv);
|
||||
diagram_filter(const common::model::diagram &d, const config::diagram &c);
|
||||
|
||||
/**
|
||||
* Add inclusive filter.
|
||||
@@ -818,9 +725,16 @@ public:
|
||||
return static_cast<bool>(tvl::is_undefined(inc) || tvl::is_true(inc));
|
||||
}
|
||||
|
||||
friend class diagram_filter_factory;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Initialize filters.
|
||||
*
|
||||
* Some filters require initialization.
|
||||
*
|
||||
* @param c Diagram config.
|
||||
*/
|
||||
void init_filters(const config::diagram &c);
|
||||
|
||||
/*! List of inclusive filters */
|
||||
std::vector<std::unique_ptr<filter_visitor>> inclusive_;
|
||||
|
||||
@@ -831,27 +745,6 @@ private:
|
||||
const common::model::diagram &diagram_;
|
||||
};
|
||||
|
||||
template <typename Collection>
|
||||
void apply_filter(Collection &col, const diagram_filter &filter)
|
||||
{
|
||||
col.erase(std::remove_if(col.begin(), col.end(),
|
||||
[&filter](auto &&element) {
|
||||
return !filter.should_include(element);
|
||||
}),
|
||||
col.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void apply_filter(
|
||||
std::vector<std::reference_wrapper<T>> &col, const diagram_filter &filter)
|
||||
{
|
||||
col.erase(std::remove_if(col.begin(), col.end(),
|
||||
[&filter](auto &&element) {
|
||||
return !filter.should_include(element.get());
|
||||
}),
|
||||
col.end());
|
||||
}
|
||||
|
||||
template <>
|
||||
bool diagram_filter::should_include<std::string>(const std::string &name) const;
|
||||
} // namespace clanguml::common::model
|
||||
@@ -78,15 +78,6 @@ public:
|
||||
*/
|
||||
bool is_empty() const { return elements_.empty(); }
|
||||
|
||||
void remove(const std::set<eid_t> &element_ids)
|
||||
{
|
||||
elements_.erase(std::remove_if(elements_.begin(), elements_.end(),
|
||||
[&element_ids](auto &&e) {
|
||||
return element_ids.count(e.get().id()) > 0;
|
||||
}),
|
||||
elements_.end());
|
||||
}
|
||||
|
||||
private:
|
||||
reference_vector<T> elements_;
|
||||
};
|
||||
|
||||
@@ -1,366 +0,0 @@
|
||||
/**
|
||||
* @file src/common/model/filters/diagram_filter_factory.cc
|
||||
*
|
||||
* Copyright (c) 2021-2024 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 "diagram_filter_factory.h"
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
void basic_diagram_filter_initializer::initialize()
|
||||
{
|
||||
// Process inclusive filters
|
||||
if (diagram_config.include) {
|
||||
df.add_inclusive_filter(std::make_unique<namespace_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().namespaces));
|
||||
|
||||
df.add_inclusive_filter(std::make_unique<modules_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().modules));
|
||||
|
||||
df.add_inclusive_filter(std::make_unique<module_access_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().module_access));
|
||||
|
||||
df.add_inclusive_filter(std::make_unique<relationship_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().relationships));
|
||||
|
||||
df.add_inclusive_filter(std::make_unique<access_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().access));
|
||||
|
||||
df.add_inclusive_filter(std::make_unique<paths_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().paths,
|
||||
diagram_config.root_directory()));
|
||||
|
||||
df.add_inclusive_filter(
|
||||
std::make_unique<class_method_filter>(filter_t::kInclusive,
|
||||
std::make_unique<access_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().access),
|
||||
std::make_unique<method_type_filter>(filter_t::kInclusive,
|
||||
diagram_config.include().method_types)));
|
||||
|
||||
df.add_inclusive_filter(
|
||||
std::make_unique<class_member_filter>(filter_t::kInclusive,
|
||||
std::make_unique<access_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().access)));
|
||||
|
||||
// Include any of these matches even if one them does not match
|
||||
std::vector<std::unique_ptr<filter_visitor>> element_filters;
|
||||
|
||||
element_filters.emplace_back(std::make_unique<element_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().elements));
|
||||
|
||||
element_filters.emplace_back(std::make_unique<element_type_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().element_types));
|
||||
|
||||
if (diagram_config.type() == diagram_t::kClass) {
|
||||
element_filters.emplace_back(std::make_unique<subclass_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().subclasses));
|
||||
|
||||
element_filters.emplace_back(std::make_unique<parents_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().parents));
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<specializations_filter_t>(filter_t::kInclusive,
|
||||
diagram_config.include().specializations,
|
||||
relationship_t::kInstantiation));
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<class_dependants_filter_t>(
|
||||
filter_t::kInclusive, diagram_config.include().dependants,
|
||||
relationship_t::kDependency));
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<class_dependencies_filter_t>(
|
||||
filter_t::kInclusive, diagram_config.include().dependencies,
|
||||
relationship_t::kDependency, true));
|
||||
}
|
||||
else if (diagram_config.type() == diagram_t::kSequence) {
|
||||
element_filters.emplace_back(std::make_unique<callee_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().callee_types));
|
||||
}
|
||||
else if (diagram_config.type() == diagram_t::kPackage) {
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<package_dependants_filter_t>(
|
||||
filter_t::kInclusive, diagram_config.include().dependants,
|
||||
relationship_t::kDependency));
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<package_dependencies_filter_t>(
|
||||
filter_t::kInclusive, diagram_config.include().dependencies,
|
||||
relationship_t::kDependency, true));
|
||||
}
|
||||
else if (diagram_config.type() == diagram_t::kInclude) {
|
||||
std::vector<std::string> dependants;
|
||||
std::vector<std::string> dependencies;
|
||||
|
||||
for (auto &&path : diagram_config.include().dependants) {
|
||||
if (auto p = path.get<std::string>(); p.has_value()) {
|
||||
const std::filesystem::path dep_path{*p};
|
||||
dependants.emplace_back(
|
||||
dep_path.lexically_normal().string());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &&path : diagram_config.include().dependencies) {
|
||||
if (auto p = path.get<std::string>(); p.has_value()) {
|
||||
const std::filesystem::path dep_path{*p};
|
||||
dependencies.emplace_back(
|
||||
dep_path.lexically_normal().string());
|
||||
}
|
||||
}
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<source_file_dependency_filter_t>(
|
||||
filter_t::kInclusive, dependants,
|
||||
relationship_t::kAssociation, false));
|
||||
|
||||
element_filters.emplace_back(
|
||||
std::make_unique<source_file_dependency_filter_t>(
|
||||
filter_t::kInclusive, dependencies,
|
||||
relationship_t::kAssociation, true));
|
||||
}
|
||||
|
||||
element_filters.emplace_back(std::make_unique<context_filter>(
|
||||
filter_t::kInclusive, diagram_config.include().context));
|
||||
|
||||
df.add_inclusive_filter(std::make_unique<anyof_filter>(
|
||||
filter_t::kInclusive, std::move(element_filters)));
|
||||
}
|
||||
|
||||
// Process exclusive filters
|
||||
if (diagram_config.exclude) {
|
||||
df.add_exclusive_filter(std::make_unique<namespace_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().namespaces));
|
||||
|
||||
df.add_exclusive_filter(std::make_unique<modules_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().modules));
|
||||
|
||||
df.add_exclusive_filter(std::make_unique<module_access_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().module_access));
|
||||
|
||||
df.add_exclusive_filter(std::make_unique<paths_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().paths,
|
||||
diagram_config.root_directory()));
|
||||
|
||||
df.add_exclusive_filter(std::make_unique<element_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().elements));
|
||||
|
||||
df.add_exclusive_filter(std::make_unique<element_type_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().element_types));
|
||||
|
||||
df.add_exclusive_filter(std::make_unique<relationship_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().relationships));
|
||||
|
||||
df.add_exclusive_filter(std::make_unique<access_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().access));
|
||||
|
||||
df.add_exclusive_filter(
|
||||
std::make_unique<class_method_filter>(filter_t::kExclusive,
|
||||
std::make_unique<access_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().access),
|
||||
std::make_unique<method_type_filter>(filter_t::kExclusive,
|
||||
diagram_config.exclude().method_types)));
|
||||
|
||||
df.add_exclusive_filter(
|
||||
std::make_unique<class_member_filter>(filter_t::kExclusive,
|
||||
std::make_unique<access_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().access)));
|
||||
|
||||
df.add_exclusive_filter(std::make_unique<subclass_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().subclasses));
|
||||
|
||||
df.add_exclusive_filter(std::make_unique<parents_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().parents));
|
||||
|
||||
df.add_exclusive_filter(std::make_unique<specializations_filter_t>(
|
||||
filter_t::kExclusive, diagram_config.exclude().specializations,
|
||||
relationship_t::kInstantiation));
|
||||
|
||||
if (diagram_config.type() == diagram_t::kClass) {
|
||||
df.add_exclusive_filter(std::make_unique<class_dependants_filter_t>(
|
||||
filter_t::kExclusive, diagram_config.exclude().dependants,
|
||||
relationship_t::kDependency));
|
||||
|
||||
df.add_exclusive_filter(
|
||||
std::make_unique<class_dependencies_filter_t>(
|
||||
filter_t::kExclusive, diagram_config.exclude().dependencies,
|
||||
relationship_t::kDependency, true));
|
||||
}
|
||||
else if (diagram_config.type() == diagram_t::kSequence) {
|
||||
df.add_exclusive_filter(std::make_unique<callee_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().callee_types));
|
||||
}
|
||||
else if (diagram_config.type() == diagram_t::kPackage) {
|
||||
df.add_exclusive_filter(
|
||||
std::make_unique<package_dependencies_filter_t>(
|
||||
filter_t::kExclusive, diagram_config.exclude().dependencies,
|
||||
relationship_t::kDependency, true));
|
||||
|
||||
df.add_exclusive_filter(
|
||||
std::make_unique<package_dependants_filter_t>(
|
||||
filter_t::kExclusive, diagram_config.exclude().dependants,
|
||||
relationship_t::kDependency));
|
||||
}
|
||||
else if (diagram_config.type() == diagram_t::kInclude) {
|
||||
std::vector<std::string> dependants;
|
||||
std::vector<std::string> dependencies;
|
||||
|
||||
for (auto &&path : diagram_config.exclude().dependants) {
|
||||
if (auto p = path.get<std::string>(); p.has_value()) {
|
||||
std::filesystem::path dep_path{*p};
|
||||
dependants.emplace_back(
|
||||
dep_path.lexically_normal().string());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &&path : diagram_config.exclude().dependencies) {
|
||||
if (auto p = path.get<std::string>(); p.has_value()) {
|
||||
std::filesystem::path dep_path{*p};
|
||||
dependencies.emplace_back(
|
||||
dep_path.lexically_normal().string());
|
||||
}
|
||||
}
|
||||
|
||||
df.add_exclusive_filter(
|
||||
std::make_unique<source_file_dependency_filter_t>(
|
||||
filter_t::kExclusive, dependants,
|
||||
relationship_t::kAssociation, false));
|
||||
|
||||
df.add_exclusive_filter(
|
||||
std::make_unique<source_file_dependency_filter_t>(
|
||||
filter_t::kExclusive, dependencies,
|
||||
relationship_t::kAssociation, true));
|
||||
}
|
||||
|
||||
df.add_exclusive_filter(std::make_unique<context_filter>(
|
||||
filter_t::kExclusive, diagram_config.exclude().context));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void advanced_diagram_filter_initializer::add_filter<
|
||||
source_file_dependency_filter_t>(const filter_t &filter_type,
|
||||
const std::vector<common::string_or_regex> &filter_config,
|
||||
std::vector<std::unique_ptr<filter_visitor>> &result,
|
||||
relationship_t &&rt, // NOLINT
|
||||
bool &&direction // NOLINT
|
||||
)
|
||||
{
|
||||
std::vector<std::string> deps;
|
||||
for (auto &&path : filter_config) {
|
||||
if (auto p = path.get<std::string>(); p.has_value()) {
|
||||
const std::filesystem::path dep_path{*p};
|
||||
deps.emplace_back(dep_path.lexically_normal().string());
|
||||
}
|
||||
}
|
||||
|
||||
result.emplace_back(std::make_unique<source_file_dependency_filter_t>(
|
||||
filter_type, deps, rt, direction));
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<filter_visitor>>
|
||||
advanced_diagram_filter_initializer::build(
|
||||
filter_t filter_type, const config::filter &filter_config)
|
||||
{
|
||||
std::vector<std::unique_ptr<filter_visitor>> result;
|
||||
|
||||
// At any level, only allof, anyof, or a set of other non-operator
|
||||
// filters can be present
|
||||
if (filter_config.allof) {
|
||||
std::vector<std::unique_ptr<filter_visitor>> allof_filters =
|
||||
build(filter_type, *filter_config.allof);
|
||||
|
||||
auto allof_filter = std::make_unique<common::model::allof_filter>(
|
||||
filter_type, std::move(allof_filters));
|
||||
|
||||
result.emplace_back(std::move(allof_filter));
|
||||
}
|
||||
|
||||
if (filter_config.anyof) {
|
||||
std::vector<std::unique_ptr<filter_visitor>> anyof_filters =
|
||||
build(filter_type, *filter_config.anyof);
|
||||
|
||||
auto anyof_filter = std::make_unique<common::model::anyof_filter>(
|
||||
filter_type, std::move(anyof_filters));
|
||||
|
||||
result.emplace_back(std::move(anyof_filter));
|
||||
}
|
||||
|
||||
add_filter<namespace_filter>(filter_type, filter_config.namespaces, result);
|
||||
add_filter<modules_filter>(filter_type, filter_config.modules, result);
|
||||
add_filter<module_access_filter>(
|
||||
filter_type, filter_config.module_access, result);
|
||||
add_filter<relationship_filter>(
|
||||
filter_type, filter_config.relationships, result);
|
||||
add_filter<access_filter>(filter_type, filter_config.access, result);
|
||||
add_filter<method_type_filter>(
|
||||
filter_type, filter_config.method_types, result);
|
||||
|
||||
add_filter<paths_filter>(filter_type, filter_config.paths, result,
|
||||
diagram_config.root_directory());
|
||||
|
||||
add_filter<element_filter>(filter_type, filter_config.elements, result);
|
||||
add_filter<element_type_filter>(
|
||||
filter_type, filter_config.element_types, result);
|
||||
add_filter<subclass_filter>(filter_type, filter_config.subclasses, result);
|
||||
add_filter<parents_filter>(filter_type, filter_config.parents, result);
|
||||
|
||||
add_filter<specializations_filter_t>(filter_type,
|
||||
filter_config.specializations, result, relationship_t::kInstantiation,
|
||||
false);
|
||||
add_filter<class_dependants_filter_t>(filter_type, filter_config.dependants,
|
||||
result, relationship_t::kDependency, false);
|
||||
add_filter<class_dependencies_filter_t>(filter_type,
|
||||
filter_config.dependencies, result, relationship_t::kDependency, true);
|
||||
|
||||
add_filter<callee_filter>(filter_type, filter_config.callee_types, result);
|
||||
|
||||
add_filter<package_dependants_filter_t>(filter_type,
|
||||
filter_config.dependants, result, relationship_t::kDependency, false);
|
||||
add_filter<package_dependencies_filter_t>(filter_type,
|
||||
filter_config.dependencies, result, relationship_t::kDependency, true);
|
||||
|
||||
add_filter<context_filter>(filter_type, filter_config.context, result);
|
||||
|
||||
if (diagram_config.type() == diagram_t::kInclude) {
|
||||
add_filter<source_file_dependency_filter_t>(filter_type,
|
||||
filter_config.dependants, result, relationship_t::kAssociation,
|
||||
false);
|
||||
add_filter<source_file_dependency_filter_t>(filter_type,
|
||||
filter_config.dependencies, result, relationship_t::kAssociation,
|
||||
true);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void advanced_diagram_filter_initializer::initialize()
|
||||
{
|
||||
if (diagram_config.include) {
|
||||
auto inclusive_filter =
|
||||
build(filter_t::kInclusive, diagram_config.include());
|
||||
for (auto &f : inclusive_filter)
|
||||
df.add_filter(filter_t::kInclusive, std::move(f));
|
||||
}
|
||||
|
||||
if (diagram_config.exclude) {
|
||||
auto exclusive_filter =
|
||||
build(filter_t::kExclusive, diagram_config.exclude());
|
||||
for (auto &f : exclusive_filter)
|
||||
df.add_filter(filter_t::kExclusive, std::move(f));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace clanguml::common::model
|
||||
@@ -1,126 +0,0 @@
|
||||
/**
|
||||
* @file src/common/model/filters/diagram_filter_factory.h
|
||||
*
|
||||
* Copyright (c) 2021-2024 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 "diagram_filter.h"
|
||||
#include "package_diagram/model/diagram.h"
|
||||
|
||||
namespace clanguml::common::model {
|
||||
using specializations_filter_t =
|
||||
edge_traversal_filter<class_diagram::model::diagram,
|
||||
class_diagram::model::class_, common::string_or_regex>;
|
||||
|
||||
using class_dependants_filter_t =
|
||||
edge_traversal_filter<class_diagram::model::diagram,
|
||||
class_diagram::model::class_, common::string_or_regex>;
|
||||
using class_dependencies_filter_t =
|
||||
edge_traversal_filter<class_diagram::model::diagram,
|
||||
class_diagram::model::class_, common::string_or_regex>;
|
||||
|
||||
using package_dependants_filter_t =
|
||||
edge_traversal_filter<package_diagram::model::diagram,
|
||||
common::model::package, common::string_or_regex>;
|
||||
using package_dependencies_filter_t =
|
||||
edge_traversal_filter<package_diagram::model::diagram,
|
||||
common::model::package, common::string_or_regex>;
|
||||
|
||||
using source_file_dependency_filter_t =
|
||||
edge_traversal_filter<include_diagram::model::diagram,
|
||||
common::model::source_file, std::string, common::model::source_file>;
|
||||
|
||||
class diagram_filter_initializer {
|
||||
public:
|
||||
diagram_filter_initializer(const config::diagram &c, diagram_filter &filter)
|
||||
: diagram_config{c}
|
||||
, df{filter}
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~diagram_filter_initializer() = default;
|
||||
|
||||
virtual void initialize() = 0;
|
||||
|
||||
protected:
|
||||
const config::diagram &diagram_config;
|
||||
diagram_filter &df;
|
||||
};
|
||||
|
||||
class basic_diagram_filter_initializer : public diagram_filter_initializer {
|
||||
public:
|
||||
using diagram_filter_initializer::diagram_filter_initializer;
|
||||
|
||||
~basic_diagram_filter_initializer() override = default;
|
||||
|
||||
void initialize() override;
|
||||
};
|
||||
|
||||
class advanced_diagram_filter_initializer : public diagram_filter_initializer {
|
||||
public:
|
||||
using diagram_filter_initializer::diagram_filter_initializer;
|
||||
|
||||
~advanced_diagram_filter_initializer() override = default;
|
||||
|
||||
void initialize() override;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<filter_visitor>> build(
|
||||
filter_t filter_type, const config::filter &filter_config);
|
||||
|
||||
template <typename FT, typename T, typename... Args>
|
||||
void add_filter(const filter_t &filter_type,
|
||||
const std::vector<T> &filter_config,
|
||||
std::vector<std::unique_ptr<filter_visitor>> &result, Args &&...args)
|
||||
{
|
||||
if (!filter_config.empty()) {
|
||||
auto filter = std::make_unique<FT>(
|
||||
filter_type, filter_config, std::forward<Args>(args)...);
|
||||
filter->set_mode(filter_mode_t::advanced);
|
||||
result.emplace_back(std::move(filter));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
void advanced_diagram_filter_initializer::add_filter<
|
||||
source_file_dependency_filter_t>(const filter_t &filter_type,
|
||||
const std::vector<common::string_or_regex> &filter_config,
|
||||
std::vector<std::unique_ptr<filter_visitor>> &result, relationship_t &&rt,
|
||||
bool &&direction);
|
||||
|
||||
class diagram_filter_factory {
|
||||
public:
|
||||
static std::unique_ptr<diagram_filter> create(
|
||||
const common::model::diagram &d, const config::diagram &c)
|
||||
{
|
||||
auto filter = std::make_unique<diagram_filter>(
|
||||
d, c, diagram_filter::private_constructor_tag_t{});
|
||||
|
||||
if (c.filter_mode() == config::filter_mode_t::basic) {
|
||||
basic_diagram_filter_initializer init{c, *filter};
|
||||
init.initialize();
|
||||
}
|
||||
else {
|
||||
advanced_diagram_filter_initializer init{c, *filter};
|
||||
init.initialize();
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clanguml::common::model
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -246,23 +245,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void remove(const std::set<eid_t> &element_ids)
|
||||
{
|
||||
// First remove all matching elements on this level
|
||||
elements_.erase(std::remove_if(elements_.begin(), elements_.end(),
|
||||
[&element_ids](auto &&e) {
|
||||
return element_ids.count(e->id()) > 0;
|
||||
}),
|
||||
elements_.end());
|
||||
|
||||
// Now recurse to any packages on this level
|
||||
for (auto &p : elements_) {
|
||||
if (dynamic_cast<nested_trait<T, Path> *>(p.get()))
|
||||
dynamic_cast<nested_trait<T, Path> *>(p.get())->remove(
|
||||
element_ids);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<T>> elements_;
|
||||
};
|
||||
|
||||
@@ -253,7 +253,8 @@ public:
|
||||
|
||||
comment_visitor_->visit(decl, e);
|
||||
|
||||
auto *comment = decl.getASTContext().getRawCommentForDeclNoCache(&decl);
|
||||
const auto *comment =
|
||||
decl.getASTContext().getRawCommentForDeclNoCache(&decl);
|
||||
|
||||
process_comment(comment, decl.getASTContext().getDiagnostics(), e);
|
||||
}
|
||||
@@ -289,30 +290,21 @@ public:
|
||||
return stripped_comment;
|
||||
}
|
||||
|
||||
bool skip_system_header_decl(const clang::NamedDecl *decl) const
|
||||
{
|
||||
return !config().include_system_headers() &&
|
||||
source_manager().isInSystemHeader(
|
||||
decl->getSourceRange().getBegin());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the diagram should include a declaration.
|
||||
*
|
||||
* @param decl Clang declaration.
|
||||
* @return True, if the entity should be included in the diagram.
|
||||
*/
|
||||
bool should_include(const clang::NamedDecl *decl) const
|
||||
bool should_include(const clang::NamedDecl *decl)
|
||||
{
|
||||
if (decl == nullptr)
|
||||
return false;
|
||||
|
||||
if (skip_system_header_decl(decl))
|
||||
if (source_manager().isInSystemHeader(
|
||||
decl->getSourceRange().getBegin()))
|
||||
return false;
|
||||
|
||||
if (config().filter_mode() == config::filter_mode_t::advanced)
|
||||
return true;
|
||||
|
||||
auto should_include_namespace = diagram().should_include(
|
||||
common::model::namespace_{decl->getQualifiedNameAsString()});
|
||||
|
||||
|
||||
@@ -186,19 +186,6 @@ std::string to_string(context_direction_t cd)
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string(filter_mode_t fm)
|
||||
{
|
||||
switch (fm) {
|
||||
case filter_mode_t::basic:
|
||||
return "basic";
|
||||
case filter_mode_t::advanced:
|
||||
return "advanced";
|
||||
default:
|
||||
assert(false);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> plantuml::get_style(
|
||||
const common::model::relationship_t relationship_type) const
|
||||
{
|
||||
@@ -241,8 +228,6 @@ void inheritable_diagram_options::inherit(
|
||||
using_module.override(parent.using_module);
|
||||
include_relations_also_as_members.override(
|
||||
parent.include_relations_also_as_members);
|
||||
filter_mode.override(parent.filter_mode);
|
||||
include_system_headers.override(parent.include_system_headers);
|
||||
include.override(parent.include);
|
||||
exclude.override(parent.exclude);
|
||||
puml.override(parent.puml);
|
||||
|
||||
@@ -116,13 +116,6 @@ struct plantuml_keyword_mapping_t {
|
||||
relationships;
|
||||
};
|
||||
|
||||
enum class filter_mode_t {
|
||||
basic, /*!< Default filter structure without logical operators */
|
||||
advanced /*!< Advanced filter config with logical operators */
|
||||
};
|
||||
|
||||
std::string to_string(filter_mode_t cp);
|
||||
|
||||
/**
|
||||
* @brief PlantUML diagram config section
|
||||
*
|
||||
@@ -194,9 +187,6 @@ struct diagram_template {
|
||||
};
|
||||
|
||||
struct filter {
|
||||
std::shared_ptr<filter> anyof;
|
||||
std::shared_ptr<filter> allof;
|
||||
|
||||
/*! @brief Namespaces filter
|
||||
*
|
||||
* Example:
|
||||
@@ -455,20 +445,8 @@ struct layout_hint {
|
||||
using layout_hints = std::map<std::string, std::vector<layout_hint>>;
|
||||
|
||||
struct generate_links_config {
|
||||
std::map</* path */ std::string, /* pattern */ std::string> link;
|
||||
std::map</* path */ std::string, /* pattern */ std::string> tooltip;
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> get_link_pattern(
|
||||
const std::string &path) const
|
||||
{
|
||||
return util::find_entry_by_path_prefix(link, path);
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> get_tooltip_pattern(
|
||||
const std::string &path) const
|
||||
{
|
||||
return util::find_entry_by_path_prefix(tooltip, path);
|
||||
}
|
||||
std::string link;
|
||||
std::string tooltip;
|
||||
};
|
||||
|
||||
struct git_config {
|
||||
@@ -566,8 +544,6 @@ struct inheritable_diagram_options {
|
||||
option<std::string> using_module{"using_module"};
|
||||
option<bool> include_relations_also_as_members{
|
||||
"include_relations_also_as_members", true};
|
||||
option<filter_mode_t> filter_mode{"filter_mode", filter_mode_t::basic};
|
||||
option<bool> include_system_headers{"include_system_headers", false};
|
||||
option<filter> include{"include"};
|
||||
option<filter> exclude{"exclude"};
|
||||
option<plantuml> puml{"plantuml", option_inherit_mode::kAppend};
|
||||
|
||||
@@ -37,8 +37,8 @@ types:
|
||||
- abbreviated
|
||||
- none
|
||||
generate_links_t:
|
||||
link: !optional [string, map_t<string;string>]
|
||||
tooltip: !optional [string, map_t<string;string>]
|
||||
link: string
|
||||
tooltip: string
|
||||
git_t:
|
||||
branch: string
|
||||
revision: [string, int]
|
||||
@@ -156,8 +156,6 @@ types:
|
||||
paths: !optional [string]
|
||||
method_types: !optional [method_type_filter_t]
|
||||
callee_types: !optional [callee_type_filter_t]
|
||||
anyof: !optional filter_t
|
||||
allof: !optional filter_t
|
||||
function_location_t:
|
||||
function: string
|
||||
marker_location_t:
|
||||
@@ -165,9 +163,6 @@ types:
|
||||
source_location_t:
|
||||
- function_location_t
|
||||
- marker_location_t
|
||||
filter_mode_t: !variant
|
||||
- basic
|
||||
- advanced
|
||||
class_diagram_t:
|
||||
type: !variant [class]
|
||||
#
|
||||
@@ -176,8 +171,6 @@ types:
|
||||
__parent_path: !optional string
|
||||
comment_parser: !optional comment_parser_t
|
||||
debug_mode: !optional bool
|
||||
filter_mode: !optional filter_mode_t
|
||||
include_system_headers: !optional bool
|
||||
exclude: !optional filter_t
|
||||
generate_links: !optional generate_links_t
|
||||
git: !optional git_t
|
||||
@@ -222,8 +215,6 @@ types:
|
||||
debug_mode: !optional bool
|
||||
exclude: !optional filter_t
|
||||
generate_links: !optional generate_links_t
|
||||
filter_mode: !optional filter_mode_t
|
||||
include_system_headers: !optional bool
|
||||
git: !optional git_t
|
||||
glob: !optional [string]
|
||||
include: !optional filter_t
|
||||
@@ -267,8 +258,6 @@ types:
|
||||
generate_links: !optional generate_links_t
|
||||
git: !optional git_t
|
||||
glob: !optional [string]
|
||||
filter_mode: !optional filter_mode_t
|
||||
include_system_headers: !optional bool
|
||||
include: !optional filter_t
|
||||
plantuml: !optional
|
||||
before: !optional [string]
|
||||
@@ -297,8 +286,6 @@ types:
|
||||
__parent_path: !optional string
|
||||
comment_parser: !optional comment_parser_t
|
||||
debug_mode: !optional bool
|
||||
filter_mode: !optional filter_mode_t
|
||||
include_system_headers: !optional bool
|
||||
exclude: !optional filter_t
|
||||
generate_links: !optional generate_links_t
|
||||
git: !optional git_t
|
||||
@@ -384,8 +371,6 @@ root:
|
||||
generate_template_argument_dependencies: !optional bool
|
||||
skip_redundant_dependencies: !optional bool
|
||||
type_aliases: !optional map_t<string;string>
|
||||
filter_mode: !optional filter_mode_t
|
||||
include_system_headers: !optional bool
|
||||
)";
|
||||
|
||||
} // namespace clanguml::config
|
||||
@@ -158,21 +158,6 @@ void get_option<clanguml::config::comment_parser_t>(const Node &node,
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void get_option<clanguml::config::filter_mode_t>(const Node &node,
|
||||
clanguml::config::option<clanguml::config::filter_mode_t> &option)
|
||||
{
|
||||
if (node[option.name]) {
|
||||
const auto &val = node[option.name].as<std::string>();
|
||||
if (val == "basic")
|
||||
option.set(clanguml::config::filter_mode_t::basic);
|
||||
else if (val == "advanced")
|
||||
option.set(clanguml::config::filter_mode_t::advanced);
|
||||
else
|
||||
throw std::runtime_error("Invalid comment_parser value: " + val);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void get_option<std::map<std::string, clanguml::config::diagram_template>>(
|
||||
const Node &node,
|
||||
@@ -536,14 +521,6 @@ template <> struct convert<namespace_or_regex> {
|
||||
template <> struct convert<filter> {
|
||||
static bool decode(const Node &node, filter &rhs)
|
||||
{
|
||||
if (node["anyof"]) {
|
||||
rhs.anyof = std::make_unique<filter>(node["anyof"].as<filter>());
|
||||
}
|
||||
|
||||
if (node["allof"]) {
|
||||
rhs.allof = std::make_unique<filter>(node["allof"].as<filter>());
|
||||
}
|
||||
|
||||
if (node["namespaces"]) {
|
||||
auto namespace_list =
|
||||
node["namespaces"].as<decltype(rhs.namespaces)>();
|
||||
@@ -616,19 +593,11 @@ template <> struct convert<filter> {
|
||||
template <> struct convert<generate_links_config> {
|
||||
static bool decode(const Node &node, generate_links_config &rhs)
|
||||
{
|
||||
if (node["link"]) {
|
||||
if (node["link"].IsMap())
|
||||
rhs.link = node["link"].as<decltype(rhs.link)>();
|
||||
else
|
||||
rhs.link.emplace(".", node["link"].as<std::string>());
|
||||
}
|
||||
if (node["link"])
|
||||
rhs.link = node["link"].as<decltype(rhs.link)>();
|
||||
|
||||
if (node["tooltip"]) {
|
||||
if (node["tooltip"].IsMap())
|
||||
rhs.tooltip = node["tooltip"].as<decltype(rhs.tooltip)>();
|
||||
else
|
||||
rhs.tooltip.emplace(".", node["tooltip"].as<std::string>());
|
||||
}
|
||||
if (node["tooltip"])
|
||||
rhs.tooltip = node["tooltip"].as<decltype(rhs.tooltip)>();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -662,8 +631,6 @@ template <typename T> bool decode_diagram(const Node &node, T &rhs)
|
||||
get_option(node, rhs.glob);
|
||||
get_option(node, rhs.using_namespace);
|
||||
get_option(node, rhs.using_module);
|
||||
get_option(node, rhs.filter_mode);
|
||||
get_option(node, rhs.include_system_headers);
|
||||
get_option(node, rhs.include);
|
||||
get_option(node, rhs.exclude);
|
||||
get_option(node, rhs.puml);
|
||||
@@ -882,7 +849,6 @@ template <> struct convert<config> {
|
||||
get_option(node, rhs.glob);
|
||||
get_option(node, rhs.using_namespace);
|
||||
get_option(node, rhs.using_module);
|
||||
get_option(node, rhs.filter_mode);
|
||||
get_option(node, rhs.output_directory);
|
||||
get_option(node, rhs.query_driver);
|
||||
get_option(node, rhs.allow_empty_diagrams);
|
||||
|
||||
@@ -39,11 +39,14 @@ void generator::generate_relationships(
|
||||
});
|
||||
}
|
||||
else {
|
||||
for (const auto &r : f.relationships()) {
|
||||
nlohmann::json rel = r;
|
||||
rel["source"] = std::to_string(f.id().value());
|
||||
parent["relationships"].push_back(std::move(rel));
|
||||
}
|
||||
util::for_each_if(
|
||||
f.relationships(),
|
||||
[this](const auto &r) { return model().should_include(r.type()); },
|
||||
[&f, &parent](const auto &r) {
|
||||
nlohmann::json rel = r;
|
||||
rel["source"] = std::to_string(f.id().value());
|
||||
parent["relationships"].push_back(std::move(rel));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,15 +73,17 @@ void generator::generate(const source_file &f, nlohmann::json &parent) const
|
||||
parent["elements"].push_back(std::move(j));
|
||||
}
|
||||
else {
|
||||
LOG_DBG("Generating file {}", f.name());
|
||||
if (model().should_include(f)) {
|
||||
LOG_DBG("Generating file {}", f.name());
|
||||
|
||||
j["type"] = "file";
|
||||
j["file_kind"] = to_string(f.type());
|
||||
if (f.type() == common::model::source_file_t::kHeader) {
|
||||
j["is_system"] = f.is_system_header();
|
||||
j["type"] = "file";
|
||||
j["file_kind"] = to_string(f.type());
|
||||
if (f.type() == common::model::source_file_t::kHeader) {
|
||||
j["is_system"] = f.is_system_header();
|
||||
}
|
||||
|
||||
parent["elements"].push_back(std::move(j));
|
||||
}
|
||||
|
||||
parent["elements"].push_back(std::move(j));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,13 +49,21 @@ void generator::generate_relationships(
|
||||
});
|
||||
}
|
||||
else {
|
||||
for (const auto &r : f.relationships()) {
|
||||
ostr << indent(1) << f.alias() << " "
|
||||
<< (r.type() == common::model::relationship_t::kDependency
|
||||
? "-.->"
|
||||
: "-->")
|
||||
<< " " << model().get(r.destination()).value().alias() << '\n';
|
||||
}
|
||||
util::for_each_if(
|
||||
f.relationships(),
|
||||
[this](const auto &r) {
|
||||
return model().should_include(r.type()) &&
|
||||
util::contains(m_generated_aliases,
|
||||
model().get(r.destination()).value().alias());
|
||||
},
|
||||
[&f, &ostr, this](const auto &r) {
|
||||
ostr << indent(1) << f.alias() << " "
|
||||
<< (r.type() == common::model::relationship_t::kDependency
|
||||
? "-.->"
|
||||
: "-->")
|
||||
<< " " << model().get(r.destination()).value().alias()
|
||||
<< '\n';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,9 +86,11 @@ void generator::generate(const source_file &f, std::ostream &ostr) const
|
||||
else {
|
||||
LOG_DBG("Generating file {}", f.name());
|
||||
|
||||
ostr << indent(1) << f.alias() << "[" << f.name() << "]\n";
|
||||
if (model().should_include(f)) {
|
||||
ostr << indent(1) << f.alias() << "[" << f.name() << "]\n";
|
||||
|
||||
m_generated_aliases.emplace(f.alias());
|
||||
m_generated_aliases.emplace(f.alias());
|
||||
}
|
||||
}
|
||||
|
||||
if (config().generate_links) {
|
||||
|
||||
@@ -44,11 +44,18 @@ void generator::generate_relationships(
|
||||
});
|
||||
}
|
||||
else {
|
||||
for (const auto &r : f.relationships()) {
|
||||
ostr << f.alias() << " "
|
||||
<< plantuml_common::to_plantuml(r, config()) << " "
|
||||
<< model().get(r.destination()).value().alias() << '\n';
|
||||
}
|
||||
util::for_each_if(
|
||||
f.relationships(),
|
||||
[this](const auto &r) {
|
||||
return model().should_include(r.type()) &&
|
||||
util::contains(m_generated_aliases,
|
||||
model().get(r.destination()).value().alias());
|
||||
},
|
||||
[&f, &ostr, this](const auto &r) {
|
||||
ostr << f.alias() << " "
|
||||
<< plantuml_common::to_plantuml(r, config()) << " "
|
||||
<< model().get(r.destination()).value().alias() << '\n';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,15 +79,17 @@ void generator::generate(const source_file &f, std::ostream &ostr) const
|
||||
else {
|
||||
LOG_DBG("Generating file {}", f.name());
|
||||
|
||||
ostr << "file \"" << f.name() << "\" as " << f.alias();
|
||||
if (model().should_include(f)) {
|
||||
ostr << "file \"" << f.name() << "\" as " << f.alias();
|
||||
|
||||
if (config().generate_links) {
|
||||
generate_link(ostr, f);
|
||||
if (config().generate_links) {
|
||||
generate_link(ostr, f);
|
||||
}
|
||||
|
||||
ostr << '\n';
|
||||
|
||||
m_generated_aliases.emplace(f.alias());
|
||||
}
|
||||
|
||||
ostr << '\n';
|
||||
|
||||
m_generated_aliases.emplace(f.alias());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "diagram.h"
|
||||
|
||||
#include "common/model/filters/diagram_filter.h"
|
||||
#include "util/error.h"
|
||||
#include "util/util.h"
|
||||
|
||||
@@ -54,6 +53,8 @@ void diagram::add_file(std::unique_ptr<common::model::source_file> &&f)
|
||||
assert(!ff.name().empty());
|
||||
assert(ff.id().value() != 0);
|
||||
|
||||
element_view<source_file>::add(ff);
|
||||
|
||||
auto p = ff.path();
|
||||
|
||||
if (!f->path().is_empty()) {
|
||||
@@ -85,8 +86,7 @@ void diagram::add_file(std::unique_ptr<common::model::source_file> &&f)
|
||||
|
||||
assert(p.type() == common::model::path_type::kFilesystem);
|
||||
|
||||
if (add_element(p, std::move(f)))
|
||||
element_view<source_file>::add(ff);
|
||||
add_element(p, std::move(f));
|
||||
}
|
||||
|
||||
const common::reference_vector<common::model::source_file> &
|
||||
@@ -133,25 +133,6 @@ inja::json diagram::context() const
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void diagram::apply_filter()
|
||||
{
|
||||
// First find all element ids which should be removed
|
||||
std::set<eid_t> to_remove;
|
||||
|
||||
for (auto &f : element_view<source_file>::view())
|
||||
if (f.get().type() != common::model::source_file_t::kDirectory &&
|
||||
!filter().should_include(f.get())) {
|
||||
to_remove.emplace(f.get().id());
|
||||
}
|
||||
|
||||
for (auto &sf : element_view<source_file>::view())
|
||||
sf.get().apply_filter(filter(), to_remove);
|
||||
|
||||
element_view<source_file>::remove(to_remove);
|
||||
|
||||
nested_trait_fspath::remove(to_remove);
|
||||
}
|
||||
|
||||
bool diagram::is_empty() const { return element_view<source_file>::is_empty(); }
|
||||
|
||||
} // namespace clanguml::include_diagram::model
|
||||
|
||||
@@ -33,15 +33,13 @@ using clanguml::common::opt_ref;
|
||||
using clanguml::common::model::diagram_element;
|
||||
using clanguml::common::model::source_file;
|
||||
|
||||
using nested_trait_fspath = clanguml::common::model::nested_trait<source_file,
|
||||
clanguml::common::model::filesystem_path>;
|
||||
|
||||
/**
|
||||
* @brief Class representing an include diagram model.
|
||||
*/
|
||||
class diagram : public clanguml::common::model::diagram,
|
||||
public clanguml::common::model::element_view<source_file>,
|
||||
public nested_trait_fspath {
|
||||
public clanguml::common::model::nested_trait<source_file,
|
||||
clanguml::common::model::filesystem_path> {
|
||||
public:
|
||||
diagram() = default;
|
||||
|
||||
@@ -130,8 +128,6 @@ public:
|
||||
* @return True, if diagram is empty
|
||||
*/
|
||||
bool is_empty() const override;
|
||||
|
||||
void apply_filter() override;
|
||||
};
|
||||
|
||||
template <typename ElementT>
|
||||
|
||||
@@ -39,7 +39,9 @@ void generator::generate_relationships(
|
||||
|
||||
auto destination_package = model().get(r.destination());
|
||||
|
||||
if (!destination_package)
|
||||
if (!destination_package ||
|
||||
!model().should_include(
|
||||
dynamic_cast<const package &>(*destination_package)))
|
||||
continue;
|
||||
|
||||
rel["source"] = std::to_string(p.id().value());
|
||||
@@ -86,7 +88,9 @@ void generator::generate(const package &p, nlohmann::json &parent) const
|
||||
|
||||
for (const auto &subpackage : p) {
|
||||
auto &pkg = dynamic_cast<package &>(*subpackage);
|
||||
generate(pkg, j);
|
||||
if (model().should_include(pkg)) {
|
||||
generate(pkg, j);
|
||||
}
|
||||
}
|
||||
|
||||
parent["elements"].push_back(std::move(j));
|
||||
@@ -94,7 +98,9 @@ void generator::generate(const package &p, nlohmann::json &parent) const
|
||||
else {
|
||||
for (const auto &subpackage : p) {
|
||||
auto &pkg = dynamic_cast<package &>(*subpackage);
|
||||
generate(pkg, parent);
|
||||
if (model().should_include(pkg)) {
|
||||
generate(pkg, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,12 +120,15 @@ void generator::generate_diagram(nlohmann::json &parent) const
|
||||
|
||||
for (const auto &p : model()) {
|
||||
auto &pkg = dynamic_cast<package &>(*p);
|
||||
generate(pkg, parent);
|
||||
if (model().should_include(pkg)) {
|
||||
generate(pkg, parent);
|
||||
}
|
||||
}
|
||||
|
||||
// Process package relationships
|
||||
for (const auto &p : model()) {
|
||||
generate_relationships(dynamic_cast<package &>(*p), parent);
|
||||
if (model().should_include(dynamic_cast<package &>(*p)))
|
||||
generate_relationships(dynamic_cast<package &>(*p), parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,9 @@ void generator::generate_relationships(
|
||||
try {
|
||||
auto destination_package = model().get(r.destination());
|
||||
|
||||
if (!destination_package)
|
||||
if (!destination_package ||
|
||||
!model().should_include(
|
||||
dynamic_cast<const package &>(*destination_package)))
|
||||
continue;
|
||||
|
||||
auto destination_alias = model().to_alias(r.destination());
|
||||
@@ -92,12 +94,16 @@ void generator::generate(const package &p, std::ostream &ostr) const
|
||||
|
||||
for (const auto &subpackage : p) {
|
||||
auto &pkg = dynamic_cast<package &>(*subpackage);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,12 +159,16 @@ void generator::generate_diagram(std::ostream &ostr) const
|
||||
{
|
||||
for (const auto &p : model()) {
|
||||
auto &pkg = dynamic_cast<package &>(*p);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +176,8 @@ void generator::generate_diagram(std::ostream &ostr) const
|
||||
|
||||
// Process package relationships
|
||||
for (const auto &p : model()) {
|
||||
generate_relationships(dynamic_cast<package &>(*p), ostr);
|
||||
if (model().should_include(dynamic_cast<package &>(*p)))
|
||||
generate_relationships(dynamic_cast<package &>(*p), ostr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,10 @@ void generator::generate_relationships(
|
||||
std::stringstream relstr;
|
||||
try {
|
||||
auto destination_package = model().get(r.destination());
|
||||
if (!destination_package)
|
||||
|
||||
if (!destination_package ||
|
||||
!model().should_include(
|
||||
dynamic_cast<const package &>(*destination_package)))
|
||||
continue;
|
||||
|
||||
auto destination_alias = model().to_alias(r.destination());
|
||||
@@ -59,8 +62,9 @@ void generator::generate_relationships(
|
||||
|
||||
// Process it's subpackages relationships
|
||||
for (const auto &subpackage : p) {
|
||||
generate_relationships(
|
||||
dynamic_cast<const package &>(*subpackage), ostr);
|
||||
if (model().should_include(dynamic_cast<const package &>(*subpackage)))
|
||||
generate_relationships(
|
||||
dynamic_cast<const package &>(*subpackage), ostr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,12 +96,16 @@ void generator::generate(const package &p, std::ostream &ostr) const
|
||||
|
||||
for (const auto &subpackage : p) {
|
||||
auto &pkg = dynamic_cast<package &>(*subpackage);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,12 +138,16 @@ void generator::generate_diagram(std::ostream &ostr) const
|
||||
{
|
||||
for (const auto &p : model()) {
|
||||
auto &pkg = dynamic_cast<package &>(*p);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +155,8 @@ void generator::generate_diagram(std::ostream &ostr) const
|
||||
|
||||
// Process package relationships
|
||||
for (const auto &p : model()) {
|
||||
generate_relationships(dynamic_cast<package &>(*p), ostr);
|
||||
if (model().should_include(dynamic_cast<package &>(*p)))
|
||||
generate_relationships(dynamic_cast<package &>(*p), ostr);
|
||||
}
|
||||
|
||||
generate_config_layout_hints(ostr);
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include "diagram.h"
|
||||
|
||||
#include "common/model/filters/diagram_filter.h"
|
||||
#include "util/error.h"
|
||||
#include "util/util.h"
|
||||
|
||||
@@ -75,23 +74,6 @@ inja::json diagram::context() const
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void diagram::apply_filter()
|
||||
{
|
||||
// First find all element ids which should be removed
|
||||
std::set<eid_t> to_remove;
|
||||
|
||||
for (const auto &c : packages())
|
||||
if (!filter().should_include(c.get()))
|
||||
to_remove.emplace(c.get().id());
|
||||
|
||||
element_view<package>::remove(to_remove);
|
||||
|
||||
nested_trait_ns::remove(to_remove);
|
||||
|
||||
for (auto &c : element_view<package>::view())
|
||||
c.get().apply_filter(filter(), to_remove);
|
||||
}
|
||||
|
||||
bool diagram::is_empty() const { return element_view<package>::is_empty(); }
|
||||
} // namespace clanguml::package_diagram::model
|
||||
|
||||
|
||||
@@ -32,16 +32,14 @@ using clanguml::common::model::diagram_element;
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::path;
|
||||
|
||||
using nested_trait_ns =
|
||||
clanguml::common::model::nested_trait<clanguml::common::model::element,
|
||||
clanguml::common::model::namespace_>;
|
||||
|
||||
/**
|
||||
* @brief Package diagram model.
|
||||
*/
|
||||
class diagram : public clanguml::common::model::diagram,
|
||||
public clanguml::common::model::element_view<package>,
|
||||
public nested_trait_ns {
|
||||
public clanguml::common::model::nested_trait<
|
||||
clanguml::common::model::element,
|
||||
clanguml::common::model::namespace_> {
|
||||
public:
|
||||
diagram() = default;
|
||||
|
||||
@@ -167,8 +165,6 @@ public:
|
||||
*/
|
||||
bool is_empty() const override;
|
||||
|
||||
void apply_filter() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Add element using module as diagram path
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "diagram.h"
|
||||
|
||||
#include "common/model/filters/diagram_filter.h"
|
||||
#include "common/model/diagram_filter.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
@@ -380,7 +380,7 @@ private:
|
||||
struct method : public function {
|
||||
method(const common::model::namespace_ &using_namespace);
|
||||
|
||||
method(const method &) = delete;
|
||||
method(const function &) = delete;
|
||||
method(method &&) noexcept = delete;
|
||||
method &operator=(const method &) = delete;
|
||||
method &operator=(method &&) = delete;
|
||||
|
||||
@@ -518,8 +518,7 @@ bool translation_unit_visitor::TraverseLambdaExpr(clang::LambdaExpr *expr)
|
||||
|
||||
bool translation_unit_visitor::TraverseCallExpr(clang::CallExpr *expr)
|
||||
{
|
||||
if (!config().include_system_headers() &&
|
||||
source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
|
||||
if (source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
|
||||
return true;
|
||||
|
||||
LOG_TRACE("Entering call expression at {}",
|
||||
@@ -542,8 +541,7 @@ bool translation_unit_visitor::TraverseCallExpr(clang::CallExpr *expr)
|
||||
bool translation_unit_visitor::TraverseCUDAKernelCallExpr(
|
||||
clang::CUDAKernelCallExpr *expr)
|
||||
{
|
||||
if (!config().include_system_headers() &&
|
||||
source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
|
||||
if (source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
|
||||
return true;
|
||||
|
||||
LOG_TRACE("Entering CUDA kernel call expression at {}",
|
||||
@@ -566,8 +564,7 @@ bool translation_unit_visitor::TraverseCUDAKernelCallExpr(
|
||||
bool translation_unit_visitor::TraverseCXXMemberCallExpr(
|
||||
clang::CXXMemberCallExpr *expr)
|
||||
{
|
||||
if (!config().include_system_headers() &&
|
||||
source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
|
||||
if (source_manager().isInSystemHeader(expr->getSourceRange().getBegin()))
|
||||
return true;
|
||||
|
||||
LOG_TRACE("Entering member call expression at {}",
|
||||
@@ -1088,9 +1085,6 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
auto generated_message_from_comment = generate_message_from_comment(m);
|
||||
|
||||
if (!generated_message_from_comment && !should_include(expr)) {
|
||||
LOG_DBG("Skipping call expression due to filter at: {}",
|
||||
expr->getBeginLoc().printToString(source_manager()));
|
||||
|
||||
processed_comments().erase(raw_expr_comment);
|
||||
return true;
|
||||
}
|
||||
@@ -1184,7 +1178,7 @@ bool translation_unit_visitor::VisitCallExpr(clang::CallExpr *expr)
|
||||
auto success = process_function_call_expression(m, expr);
|
||||
|
||||
if (!success) {
|
||||
LOG_DBG("Skipping call expression at: {}",
|
||||
LOG_DBG("Skipping call to call expression at: {}",
|
||||
expr->getBeginLoc().printToString(source_manager()));
|
||||
|
||||
return true;
|
||||
@@ -1714,9 +1708,7 @@ translation_unit_visitor::create_class_model(clang::CXXRecordDecl *cls)
|
||||
.get_participant<clanguml::sequence_diagram::model::class_>(
|
||||
*id_opt);
|
||||
|
||||
if (!parent_class) {
|
||||
return {};
|
||||
}
|
||||
assert(parent_class);
|
||||
|
||||
c.set_namespace(ns);
|
||||
if (cls->getNameAsString().empty()) {
|
||||
@@ -2104,7 +2096,6 @@ translation_unit_visitor::create_lambda_method_model(
|
||||
ns.pop_back();
|
||||
method_model_ptr->set_name(ns.name());
|
||||
ns.pop_back();
|
||||
method_model_ptr->set_namespace(ns);
|
||||
|
||||
method_model_ptr->is_defaulted(declaration->isDefaulted());
|
||||
method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() ||
|
||||
@@ -2144,7 +2135,6 @@ translation_unit_visitor::create_method_model(clang::CXXMethodDecl *declaration)
|
||||
ns.pop_back();
|
||||
method_model_ptr->set_name(ns.name());
|
||||
ns.pop_back();
|
||||
method_model_ptr->set_namespace(ns);
|
||||
|
||||
method_model_ptr->is_defaulted(declaration->isDefaulted());
|
||||
method_model_ptr->is_assignment(declaration->isCopyAssignmentOperator() ||
|
||||
@@ -2199,8 +2189,14 @@ translation_unit_visitor::create_method_model(clang::CXXMethodDecl *declaration)
|
||||
|
||||
bool translation_unit_visitor::should_include(const clang::TagDecl *decl) const
|
||||
{
|
||||
return visitor_specialization_t::should_include(
|
||||
dynamic_cast<const clang::NamedDecl *>(decl));
|
||||
if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin()))
|
||||
return false;
|
||||
|
||||
const auto decl_file = decl->getLocation().printToString(source_manager());
|
||||
|
||||
return diagram().should_include(
|
||||
namespace_{decl->getQualifiedNameAsString()}) &&
|
||||
diagram().should_include(common::model::source_file{decl_file});
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::should_include(
|
||||
@@ -2238,11 +2234,8 @@ bool translation_unit_visitor::should_include(const clang::CallExpr *expr) const
|
||||
if (callee_decl != nullptr) {
|
||||
const auto *callee_function = callee_decl->getAsFunction();
|
||||
|
||||
if ((callee_function == nullptr) || !should_include(callee_function)) {
|
||||
LOG_DBG("Skipping call expression at {}",
|
||||
expr->getBeginLoc().printToString(source_manager()));
|
||||
if ((callee_function == nullptr) || !should_include(callee_function))
|
||||
return false;
|
||||
}
|
||||
|
||||
return should_include(callee_function);
|
||||
}
|
||||
@@ -2268,19 +2261,30 @@ bool translation_unit_visitor::should_include(
|
||||
bool translation_unit_visitor::should_include(
|
||||
const clang::FunctionDecl *decl) const
|
||||
{
|
||||
return visitor_specialization_t::should_include(decl);
|
||||
const auto decl_file = decl->getLocation().printToString(source_manager());
|
||||
|
||||
return diagram().should_include(
|
||||
namespace_{decl->getQualifiedNameAsString()}) &&
|
||||
diagram().should_include(common::model::source_file{decl_file});
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::should_include(
|
||||
const clang::FunctionTemplateDecl *decl) const
|
||||
{
|
||||
return visitor_specialization_t::should_include(decl->getAsFunction());
|
||||
return should_include(decl->getAsFunction());
|
||||
}
|
||||
|
||||
bool translation_unit_visitor::should_include(
|
||||
const clang::ClassTemplateDecl *decl) const
|
||||
{
|
||||
return visitor_specialization_t::should_include(decl);
|
||||
if (source_manager().isInSystemHeader(decl->getSourceRange().getBegin()))
|
||||
return false;
|
||||
|
||||
const auto decl_file = decl->getLocation().printToString(source_manager());
|
||||
|
||||
return diagram().should_include(
|
||||
namespace_{decl->getQualifiedNameAsString()}) &&
|
||||
diagram().should_include(common::model::source_file{decl_file});
|
||||
}
|
||||
|
||||
std::optional<std::string> translation_unit_visitor::get_expression_comment(
|
||||
|
||||
@@ -496,75 +496,4 @@ std::string format_message_comment(const std::string &comment, unsigned width)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::filesystem::path normalize_relative_path(const std::filesystem::path &path)
|
||||
{
|
||||
if (path.is_absolute())
|
||||
return path;
|
||||
|
||||
std::filesystem::path result;
|
||||
|
||||
for (const auto &part : path) {
|
||||
if (part == ".") {
|
||||
continue;
|
||||
}
|
||||
result /= part;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_subpath(
|
||||
const std::filesystem::path &path, const std::filesystem::path &base)
|
||||
{
|
||||
if (path.empty())
|
||||
return false;
|
||||
|
||||
auto normalized_path = normalize_relative_path(path);
|
||||
auto normalized_base = normalize_relative_path(base);
|
||||
|
||||
if (normalized_path == normalized_base)
|
||||
return true;
|
||||
|
||||
auto rel = std::filesystem::relative(normalized_path, normalized_base);
|
||||
|
||||
std::string rel_str = rel.string();
|
||||
return !rel_str.empty() && rel.native()[0] != '.';
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> find_entry_by_path_prefix(
|
||||
const std::map<std::string, std::string> &m, const std::string &path)
|
||||
{
|
||||
if (m.empty())
|
||||
return {};
|
||||
|
||||
// Extract keys and sort them by length in descending order
|
||||
std::vector<std::string> keys;
|
||||
keys.reserve(m.size());
|
||||
for (const auto &[key, pattern] : m) {
|
||||
keys.push_back(key);
|
||||
}
|
||||
|
||||
std::sort(keys.begin(), keys.end(),
|
||||
[](const std::string &a, const std::string &b) {
|
||||
return a.size() > b.size();
|
||||
});
|
||||
|
||||
std::filesystem::path file_path{path};
|
||||
|
||||
for (const auto &key : keys) {
|
||||
const auto &pattern = m.at(key);
|
||||
std::filesystem::path key_path{key};
|
||||
|
||||
if (is_subpath(file_path, key_path)) {
|
||||
return {{key, pattern}};
|
||||
}
|
||||
}
|
||||
|
||||
if ((path.empty() || file_path.is_relative() || path == ".") &&
|
||||
m.count(".") > 0) {
|
||||
return {{".", m.at(".")}};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
} // namespace clanguml::util
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
@@ -462,9 +460,4 @@ bool is_relative_to(
|
||||
std::string format_message_comment(
|
||||
const std::string &c, unsigned width = kDefaultMessageCommentWidth);
|
||||
|
||||
bool is_subpath(
|
||||
const std::filesystem::path &path, const std::filesystem::path &prefix);
|
||||
|
||||
std::optional<std::pair<std::string, std::string>> find_entry_by_path_prefix(
|
||||
const std::map<std::string, std::string> &m, const std::string &prefix);
|
||||
} // namespace clanguml::util
|
||||
@@ -93,7 +93,6 @@ set(TEST_NAMES
|
||||
test_config
|
||||
test_cli_handler
|
||||
test_filters
|
||||
test_filters_advanced
|
||||
test_thread_pool_executor
|
||||
test_query_driver_output_extractor
|
||||
test_progress_indicator)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
diagrams:
|
||||
t00080_class:
|
||||
type: class
|
||||
filter_mode: advanced
|
||||
comment_parser: clang
|
||||
include_system_headers: true
|
||||
glob:
|
||||
- t00080.cc
|
||||
include:
|
||||
anyof:
|
||||
namespaces:
|
||||
- clanguml::t00080
|
||||
elements:
|
||||
- std::thread
|
||||
using_namespace: clanguml::t00080
|
||||
@@ -1,24 +0,0 @@
|
||||
#include <thread>
|
||||
|
||||
namespace clanguml {
|
||||
namespace t00080 {
|
||||
|
||||
class Worker : public std::thread {
|
||||
public:
|
||||
using std::thread::thread;
|
||||
|
||||
~Worker()
|
||||
{
|
||||
if (this->joinable()) {
|
||||
this->join();
|
||||
}
|
||||
}
|
||||
|
||||
void start(int delay) { }
|
||||
};
|
||||
|
||||
struct R {
|
||||
Worker *w;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* tests/t00080/test_case.h
|
||||
*
|
||||
* Copyright (c) 2021-2024 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("t00080")
|
||||
{
|
||||
using namespace clanguml::test;
|
||||
using namespace std::string_literals;
|
||||
|
||||
auto [config, db, diagram, model] =
|
||||
CHECK_CLASS_MODEL("t00080", "t00080_class");
|
||||
|
||||
CHECK_CLASS_DIAGRAM(*config, diagram, *model, [](const auto &src) {
|
||||
REQUIRE(IsClass(src, "Worker"));
|
||||
REQUIRE(IsClass(src, "R"));
|
||||
REQUIRE(IsClass(src, "std::thread"));
|
||||
REQUIRE(!IsClass(src, "std::jthread"));
|
||||
|
||||
REQUIRE(IsAssociation<Public>(src, "R", "Worker", "w"));
|
||||
});
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
diagrams:
|
||||
t00081_class:
|
||||
type: class
|
||||
glob:
|
||||
- t00081.cc
|
||||
filter_mode: advanced
|
||||
include_system_headers: true
|
||||
include:
|
||||
allof:
|
||||
namespaces:
|
||||
- clanguml::t00081
|
||||
- std
|
||||
context:
|
||||
- match:
|
||||
radius: 2
|
||||
pattern: clanguml::t00081::A
|
||||
exclude:
|
||||
anyof:
|
||||
access:
|
||||
- private
|
||||
- public
|
||||
- protected
|
||||
relationships:
|
||||
- dependency
|
||||
using_namespace: clanguml::t00081
|
||||
@@ -1,18 +0,0 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace clanguml {
|
||||
namespace t00081_detail {
|
||||
struct C { };
|
||||
} // namespace t00081_detail
|
||||
namespace t00081 {
|
||||
struct A {
|
||||
std::vector<std::string> as;
|
||||
std::string s;
|
||||
std::map<std::string, std::string> ms;
|
||||
|
||||
t00081_detail::C *c;
|
||||
};
|
||||
} // namespace t00081
|
||||
} // namespace clanguml
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* tests/t00081/test_case.h
|
||||
*
|
||||
* Copyright (c) 2021-2024 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("t00081")
|
||||
{
|
||||
using namespace clanguml::test;
|
||||
using namespace std::string_literals;
|
||||
|
||||
auto [config, db, diagram, model] =
|
||||
CHECK_CLASS_MODEL("t00081", "t00081_class");
|
||||
|
||||
CHECK_CLASS_DIAGRAM(*config, diagram, *model, [](const auto &src) {
|
||||
REQUIRE(IsClass(src, "A"));
|
||||
REQUIRE(!IsClass(src, "C"));
|
||||
|
||||
REQUIRE(IsClass(src, "std::string"));
|
||||
REQUIRE(IsClass(src, "std::vector<std::string>"));
|
||||
REQUIRE(IsClass(src, "std::map<std::string,std::string>"));
|
||||
});
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
diagrams:
|
||||
t00082_class:
|
||||
type: class
|
||||
glob:
|
||||
- t00082.cc
|
||||
generate_packages: true
|
||||
filter_mode: advanced
|
||||
include:
|
||||
anyof:
|
||||
subclasses:
|
||||
- clanguml::t00082::ns1::nsA::A1
|
||||
namespaces:
|
||||
- clanguml::t00082::ns2::nsB
|
||||
context:
|
||||
- clanguml::t00082::ns3::nsC::B3
|
||||
exclude:
|
||||
allof:
|
||||
elements:
|
||||
- clanguml::t00082::ns1::nsA::A1
|
||||
using_namespace: clanguml::t00082
|
||||
@@ -1,30 +0,0 @@
|
||||
namespace clanguml::t00082 {
|
||||
namespace ns1 {
|
||||
namespace nsA {
|
||||
struct A1 { };
|
||||
struct B1 : public A1 { };
|
||||
struct C1 : public B1 { };
|
||||
struct D1 { };
|
||||
}
|
||||
}
|
||||
namespace ns2 {
|
||||
namespace nsB {
|
||||
struct A2 { };
|
||||
struct B2 : public A2 { };
|
||||
struct C2 : public B2 { };
|
||||
}
|
||||
}
|
||||
namespace ns3 {
|
||||
namespace nsC {
|
||||
struct A3 { };
|
||||
struct B3 : public A3 { };
|
||||
struct C3 : public B3 { };
|
||||
struct D3 { };
|
||||
}
|
||||
}
|
||||
namespace ns4 {
|
||||
namespace nsD {
|
||||
struct A4 { };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* tests/t00082/test_case.h
|
||||
*
|
||||
* Copyright (c) 2021-2024 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("t00082")
|
||||
{
|
||||
using namespace clanguml::test;
|
||||
using namespace std::string_literals;
|
||||
|
||||
auto [config, db, diagram, model] =
|
||||
CHECK_CLASS_MODEL("t00082", "t00082_class");
|
||||
|
||||
CHECK_CLASS_DIAGRAM(*config, diagram, *model, [](const auto &src) {
|
||||
REQUIRE(!IsClass(src, {"ns1::nsA", "A1"}));
|
||||
REQUIRE(IsClass(src, {"ns1::nsA", "B1"}));
|
||||
REQUIRE(IsClass(src, {"ns1::nsA", "C1"}));
|
||||
REQUIRE(!IsClass(src, {"ns1::nsA", "D1"}));
|
||||
|
||||
REQUIRE(IsClass(src, {"ns2::nsB", "A2"}));
|
||||
REQUIRE(IsClass(src, {"ns2::nsB", "B2"}));
|
||||
REQUIRE(IsClass(src, {"ns2::nsB", "C2"}));
|
||||
|
||||
REQUIRE(IsClass(src, {"ns3::nsC", "A3"}));
|
||||
REQUIRE(IsClass(src, {"ns3::nsC", "B3"}));
|
||||
REQUIRE(IsClass(src, {"ns3::nsC", "C3"}));
|
||||
REQUIRE(!IsClass(src, {"ns3::nsC", "D3"}));
|
||||
|
||||
REQUIRE(!IsNamespacePackage(src, "ns4"s));
|
||||
|
||||
REQUIRE(!IsClass(src, {"ns4::nsD", "A4"}));
|
||||
});
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
diagrams:
|
||||
t00083_class:
|
||||
type: class
|
||||
glob:
|
||||
- t00083.cc
|
||||
generate_packages: true
|
||||
filter_mode: advanced
|
||||
exclude:
|
||||
anyof:
|
||||
subclasses:
|
||||
- clanguml::t00083::ns1::nsA::A1
|
||||
namespaces:
|
||||
- clanguml::t00083::ns2::nsB
|
||||
context:
|
||||
- clanguml::t00083::ns3::nsC::B3
|
||||
using_namespace: clanguml::t00083
|
||||
@@ -1,30 +0,0 @@
|
||||
namespace clanguml::t00083 {
|
||||
namespace ns1 {
|
||||
namespace nsA {
|
||||
struct A1 { };
|
||||
struct B1 : public A1 { };
|
||||
struct C1 : public B1 { };
|
||||
struct D1 { };
|
||||
}
|
||||
}
|
||||
namespace ns2 {
|
||||
namespace nsB {
|
||||
struct A2 { };
|
||||
struct B2 : public A2 { };
|
||||
struct C2 : public B2 { };
|
||||
}
|
||||
}
|
||||
namespace ns3 {
|
||||
namespace nsC {
|
||||
struct A3 { };
|
||||
struct B3 : public A3 { };
|
||||
struct C3 : public B3 { };
|
||||
struct D3 { };
|
||||
}
|
||||
}
|
||||
namespace ns4 {
|
||||
namespace nsD {
|
||||
struct A4 { };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* tests/t00083/test_case.h
|
||||
*
|
||||
* Copyright (c) 2021-2024 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("t00083")
|
||||
{
|
||||
using namespace clanguml::test;
|
||||
using namespace std::string_literals;
|
||||
|
||||
auto [config, db, diagram, model] =
|
||||
CHECK_CLASS_MODEL("t00083", "t00083_class");
|
||||
|
||||
CHECK_CLASS_DIAGRAM(*config, diagram, *model, [](const auto &src) {
|
||||
REQUIRE(!IsClass(src, {"ns1::nsA", "A1"}));
|
||||
REQUIRE(!IsClass(src, {"ns1::nsA", "B1"}));
|
||||
REQUIRE(!IsClass(src, {"ns1::nsA", "C1"}));
|
||||
REQUIRE(IsClass(src, {"ns1::nsA", "D1"}));
|
||||
|
||||
REQUIRE(!IsClass(src, {"ns2::nsB", "A2"}));
|
||||
REQUIRE(!IsClass(src, {"ns2::nsB", "B2"}));
|
||||
REQUIRE(!IsClass(src, {"ns2::nsB", "C2"}));
|
||||
|
||||
REQUIRE(!IsClass(src, {"ns3::nsC", "A3"}));
|
||||
REQUIRE(!IsClass(src, {"ns3::nsC", "B3"}));
|
||||
REQUIRE(!IsClass(src, {"ns3::nsC", "C3"}));
|
||||
REQUIRE(IsClass(src, {"ns3::nsC", "D3"}));
|
||||
|
||||
REQUIRE(IsNamespacePackage(src, "ns4"s));
|
||||
|
||||
REQUIRE(IsClass(src, {"ns4::nsD", "A4"}));
|
||||
});
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
namespace clanguml {
|
||||
namespace t20002 {
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
diagrams:
|
||||
t20055_sequence:
|
||||
type: sequence
|
||||
filter_mode: advanced
|
||||
glob:
|
||||
- t20055.cc
|
||||
include:
|
||||
anyof:
|
||||
namespaces:
|
||||
- clanguml::t20055::ns2
|
||||
elements:
|
||||
- clanguml::t20055::ns1::B
|
||||
- clanguml::t20055::ns1::d()
|
||||
using_namespace: clanguml::t20055
|
||||
from:
|
||||
- function: "clanguml::t20055::ns2::tmain()"
|
||||
@@ -1,40 +0,0 @@
|
||||
namespace clanguml {
|
||||
namespace t20055 {
|
||||
namespace ns1 {
|
||||
|
||||
void d() { }
|
||||
|
||||
struct A {
|
||||
void a() { }
|
||||
};
|
||||
|
||||
struct B {
|
||||
A a;
|
||||
void b()
|
||||
{
|
||||
a.a();
|
||||
d();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ns1
|
||||
namespace ns2 {
|
||||
void f() { }
|
||||
struct C {
|
||||
ns1::B b;
|
||||
void c()
|
||||
{
|
||||
b.b();
|
||||
f();
|
||||
}
|
||||
};
|
||||
|
||||
void tmain()
|
||||
{
|
||||
C c;
|
||||
c.c();
|
||||
}
|
||||
|
||||
} // namespace ns2
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/**
|
||||
* tests/t20055/test_case.h
|
||||
*
|
||||
* Copyright (c) 2021-2024 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("t20055")
|
||||
{
|
||||
using namespace clanguml::test;
|
||||
using namespace std::string_literals;
|
||||
|
||||
auto [config, db, diagram, model] =
|
||||
CHECK_SEQUENCE_MODEL("t20055", "t20055_sequence");
|
||||
|
||||
CHECK_SEQUENCE_DIAGRAM(*config, diagram, *model, [](const auto &src) {
|
||||
REQUIRE(MessageOrder(src,
|
||||
{
|
||||
//
|
||||
{{"ns2", "tmain()"}, {"ns2", "C"}, "c()"},
|
||||
{{"ns2", "C"}, {"ns1", "B"}, "b()"},
|
||||
{{"ns1", "B"}, {"ns1", "d()"}, ""},
|
||||
{{"ns2", "C"}, {"ns2", "f()"}, ""}
|
||||
//
|
||||
}));
|
||||
|
||||
REQUIRE(!HasMessage(src, {{"ns1", "B"}, {"ns1", "A"}, "a()"}));
|
||||
});
|
||||
}
|
||||
@@ -9,6 +9,25 @@ module;
|
||||
export module t30015.app;
|
||||
import t30015.lib1;
|
||||
|
||||
// import t30015.app;
|
||||
// import t30015.mod2;
|
||||
// import t30015.mod3;
|
||||
// import t30015.mod4;
|
||||
// import t30015.mod5;
|
||||
// import t30015.mod6;
|
||||
// import t30015.mod7;
|
||||
// import t30015.mod8;
|
||||
// import t30015.mod9;
|
||||
// import t30015.mod10;
|
||||
// import t30015.mod11;
|
||||
// import t30015.mod12;
|
||||
// import t30015.mod13;
|
||||
// import t30015.mod14;
|
||||
// import t30015.mod15;
|
||||
// import t30015.mod16;
|
||||
// import t30015.mod17;
|
||||
// import t30015.mod18;
|
||||
|
||||
export namespace clanguml::t30015 {
|
||||
|
||||
class CBA : public CF {
|
||||
|
||||
@@ -30,14 +30,11 @@
|
||||
void inject_diagram_options(std::shared_ptr<clanguml::config::diagram> diagram)
|
||||
{
|
||||
// Inject links config to all test cases
|
||||
clanguml::config::generate_links_config links_config;
|
||||
clanguml::config::generate_links_config links_config{
|
||||
R"(https://github.com/bkryza/clang-uml/blob/{{ git.commit }}/{{ element.source.path }}#L{{ element.source.line }})",
|
||||
R"({% if existsIn(element, "comment") and existsIn(element.comment, "brief") %}{{ abbrv(trim(replace(element.comment.brief.0, "\n+", " ")), 256) }}{% else %}{{ element.name }}{% endif %})"};
|
||||
|
||||
links_config.link.emplace(".",
|
||||
R"(https://github.com/bkryza/clang-uml/blob/{{ git.commit }}/{{ element.source.path }}#L{{ element.source.line }})");
|
||||
links_config.tooltip.emplace(".",
|
||||
R"({% if existsIn(element, "comment") and existsIn(element.comment, "brief") %}{{ abbrv(trim(replace(element.comment.brief.0, "\n+", " ")), 256) }}{% else %}{{ element.name }}{% endif %})");
|
||||
|
||||
diagram->generate_links.set(std::move(links_config));
|
||||
diagram->generate_links.set(links_config);
|
||||
}
|
||||
|
||||
std::pair<clanguml::config::config_ptr,
|
||||
@@ -252,8 +249,7 @@ void try_run_test_case(const diagram_source_storage &diagrams, TC &&tc)
|
||||
tc(diagrams.get<T>());
|
||||
}
|
||||
catch (doctest::TestFailureException &e) {
|
||||
std::cout << "---------------------------------------------"
|
||||
"--------"
|
||||
std::cout << "-----------------------------------------------------"
|
||||
"--------------------------\n";
|
||||
std::cout << "Test case failed for diagram type "
|
||||
<< T::diagram_type_name << ": "
|
||||
@@ -557,10 +553,6 @@ void CHECK_INCLUDE_DIAGRAM(const clanguml::config::config &config,
|
||||
#include "t00077/test_case.h"
|
||||
#include "t00078/test_case.h"
|
||||
#include "t00079/test_case.h"
|
||||
#include "t00080/test_case.h"
|
||||
#include "t00081/test_case.h"
|
||||
#include "t00082/test_case.h"
|
||||
#include "t00083/test_case.h"
|
||||
|
||||
///
|
||||
/// Sequence diagram tests
|
||||
@@ -627,7 +619,6 @@ void CHECK_INCLUDE_DIAGRAM(const clanguml::config::config &config,
|
||||
#include "t20052/test_case.h"
|
||||
#include "t20053/test_case.h"
|
||||
#include "t20054/test_case.h"
|
||||
#include "t20055/test_case.h"
|
||||
|
||||
///
|
||||
/// Package diagram tests
|
||||
|
||||
@@ -339,16 +339,8 @@ std::optional<nlohmann::json> get_participant(
|
||||
if (!j.contains("participants"))
|
||||
return {};
|
||||
|
||||
std::string using_namespace{};
|
||||
if (j.contains("using_namespace")) {
|
||||
using_namespace =
|
||||
fmt::format("{}::", j["using_namespace"].get<std::string>());
|
||||
}
|
||||
|
||||
for (const nlohmann::json &e : j.at("participants")) {
|
||||
if (e["display_name"] == name ||
|
||||
e["full_name"].get<std::string>().substr(using_namespace.size()) ==
|
||||
name)
|
||||
if (e["display_name"] == name)
|
||||
return {e};
|
||||
}
|
||||
|
||||
@@ -2490,7 +2482,7 @@ int64_t FindMessage(
|
||||
if (!fail)
|
||||
return -1;
|
||||
|
||||
std::cout << "FindMessage failed with error: " << e.what() << "\n";
|
||||
std::cout << "FindMessage failed with error " << e.what() << "\n";
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -234,18 +234,6 @@ test_cases:
|
||||
- name: t00079
|
||||
title: Test case for context diagram exclude filter with relationships option
|
||||
description:
|
||||
- name: t00080
|
||||
title: Test case for including elements from system headers
|
||||
description:
|
||||
- name: t00081
|
||||
title: Test case for class members relationships to std types
|
||||
description:
|
||||
- name: t00082
|
||||
title: Test case for advanced diagram filter inclusion test with subclasses and namespaces
|
||||
description:
|
||||
- name: t00083
|
||||
title: Test case for advanced diagram filter exclusion test with subclasses and namespaces
|
||||
description:
|
||||
Sequence diagrams:
|
||||
- name: t20001
|
||||
title: Basic sequence diagram test case
|
||||
@@ -409,9 +397,6 @@ test_cases:
|
||||
- name: t20054
|
||||
title: Test case for sequence diagram with nested classes
|
||||
description:
|
||||
- name: t20055
|
||||
title: Test case for advanced filter in sequence diagram
|
||||
description:
|
||||
Package diagrams:
|
||||
- name: t30001
|
||||
title: Basic package diagram test case
|
||||
|
||||
@@ -43,10 +43,10 @@ TEST_CASE("Test config simple")
|
||||
CHECK(diagram.generate_packages() == true);
|
||||
CHECK(diagram.generate_template_argument_dependencies() == false);
|
||||
CHECK(diagram.generate_links == true);
|
||||
CHECK(diagram.generate_links().link.at(".") ==
|
||||
CHECK(diagram.generate_links().link ==
|
||||
"https://github.com/bkryza/clang-uml/blob/{{ git.branch }}/{{ "
|
||||
"element.source.file }}#L{{ element.source.line }}");
|
||||
CHECK(diagram.generate_links().tooltip.at(".") == "{{ element.comment }}");
|
||||
CHECK(diagram.generate_links().tooltip == "{{ element.comment }}");
|
||||
|
||||
CHECK(
|
||||
diagram.comment_parser() == clanguml::config::comment_parser_t::clang);
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
compilation_database_dir: debug
|
||||
output_directory: output
|
||||
filter_mode: advanced
|
||||
diagrams:
|
||||
include_test:
|
||||
type: include
|
||||
relative_to: ../../../src
|
||||
glob:
|
||||
- src/**/*.cc
|
||||
- src/**/*.h
|
||||
include:
|
||||
allof:
|
||||
paths:
|
||||
- class_d*/
|
||||
- common
|
||||
- util/*.h
|
||||
- util/*.cc
|
||||
- main.cc
|
||||
exclude:
|
||||
allof:
|
||||
paths:
|
||||
- sequence_diagram
|
||||
- util/error.h
|
||||
anyof_test:
|
||||
type: class
|
||||
relative_to: ../../../src
|
||||
glob:
|
||||
- src/**/*.cc
|
||||
- src/**/*.h
|
||||
include:
|
||||
anyof:
|
||||
namespaces:
|
||||
- ns1::ns2
|
||||
elements:
|
||||
- std::thread
|
||||
exclude:
|
||||
anyof:
|
||||
namespaces:
|
||||
- ns1::ns2::detail
|
||||
modules_test:
|
||||
type: class
|
||||
include:
|
||||
anyof:
|
||||
modules:
|
||||
- mod1::mod2
|
||||
namespaces:
|
||||
- ns1::ns2
|
||||
method_type_include_test:
|
||||
type: class
|
||||
include:
|
||||
anyof:
|
||||
namespaces:
|
||||
- ns1::ns2
|
||||
method_types:
|
||||
- constructor
|
||||
- operator
|
||||
regex_elements_test:
|
||||
type: class
|
||||
include:
|
||||
elements:
|
||||
- ns1::ClassA
|
||||
- r: 'ns1::ns2::Class.+'
|
||||
- r: 'ns1::.+::ns3::.+'
|
||||
exclude:
|
||||
elements:
|
||||
- ns1::ns2::ClassZ
|
||||
regex_elements_and_namespaces:
|
||||
type: class
|
||||
include:
|
||||
allof:
|
||||
elements:
|
||||
- ns1::ClassA
|
||||
- r: 'ns1::ns2::Class.+'
|
||||
- r: 'ns1::.+::ns3::.+'
|
||||
namespaces:
|
||||
- r: '.+ns2.+'
|
||||
edge_filter_and_namespaces:
|
||||
type: class
|
||||
filter_mode: advanced
|
||||
include:
|
||||
anyof:
|
||||
subclasses:
|
||||
- ns1::nsA::A
|
||||
namespaces:
|
||||
- ns2::nsB
|
||||
context:
|
||||
- ns1::nsA::C
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "class_diagram/model/class.h"
|
||||
#include "cli/cli_handler.h"
|
||||
#include "common/model/filters/diagram_filter_factory.h"
|
||||
#include "common/model/diagram_filter.h"
|
||||
#include "common/model/source_file.h"
|
||||
#include "config/config.h"
|
||||
#include "include_diagram/model/diagram.h"
|
||||
@@ -32,7 +32,6 @@
|
||||
TEST_CASE("Test diagram paths filter")
|
||||
{
|
||||
using clanguml::common::model::diagram_filter;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
using clanguml::common::model::source_file;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
@@ -40,8 +39,7 @@ TEST_CASE("Test diagram paths filter")
|
||||
auto &config = *cfg.diagrams["include_test"];
|
||||
clanguml::include_diagram::model::diagram diagram;
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
auto make_path = [&](std::string_view p) {
|
||||
return source_file{config.root_directory() / p};
|
||||
@@ -61,7 +59,6 @@ TEST_CASE("Test method_types include filter")
|
||||
using clanguml::class_diagram::model::class_method;
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::diagram_filter;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
using clanguml::common::model::source_file;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
@@ -69,8 +66,7 @@ TEST_CASE("Test method_types include filter")
|
||||
auto &config = *cfg.diagrams["method_type_include_test"];
|
||||
clanguml::class_diagram::model::diagram diagram;
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
class_method cm{access_t::kPublic, "A", ""};
|
||||
cm.is_constructor(true);
|
||||
@@ -88,7 +84,6 @@ TEST_CASE("Test method_types exclude filter")
|
||||
using clanguml::class_diagram::model::class_method;
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::diagram_filter;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
using clanguml::common::model::source_file;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
@@ -96,8 +91,7 @@ TEST_CASE("Test method_types exclude filter")
|
||||
auto &config = *cfg.diagrams["method_type_exclude_test"];
|
||||
clanguml::class_diagram::model::diagram diagram;
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
class_method cm{access_t::kPublic, "A", ""};
|
||||
|
||||
@@ -115,23 +109,22 @@ TEST_CASE("Test method_types exclude filter")
|
||||
|
||||
TEST_CASE("Test namespaces filter")
|
||||
{
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::class_diagram::model::class_method;
|
||||
using clanguml::class_diagram::model::class_parent;
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::diagram_filter;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
using clanguml::common::model::namespace_;
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::source_file;
|
||||
|
||||
using clanguml::class_diagram::model::class_;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
|
||||
auto &config = *cfg.diagrams["namespace_test"];
|
||||
clanguml::class_diagram::model::diagram diagram;
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
class_ c{{}};
|
||||
|
||||
@@ -180,21 +173,20 @@ TEST_CASE("Test namespaces filter")
|
||||
|
||||
TEST_CASE("Test elements regexp filter")
|
||||
{
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::class_diagram::model::class_method;
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::diagram_filter;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
using clanguml::common::model::namespace_;
|
||||
using clanguml::common::model::source_file;
|
||||
|
||||
using clanguml::class_diagram::model::class_;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
|
||||
auto &config = *cfg.diagrams["regex_elements_test"];
|
||||
clanguml::class_diagram::model::diagram diagram;
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
class_ c{{}};
|
||||
|
||||
@@ -221,23 +213,22 @@ TEST_CASE("Test elements regexp filter")
|
||||
|
||||
TEST_CASE("Test namespaces regexp filter")
|
||||
{
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::class_diagram::model::class_method;
|
||||
using clanguml::class_diagram::model::class_parent;
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::diagram_filter;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
using clanguml::common::model::namespace_;
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::source_file;
|
||||
|
||||
using clanguml::class_diagram::model::class_;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
|
||||
auto &config = *cfg.diagrams["regex_namespace_test"];
|
||||
clanguml::class_diagram::model::diagram diagram;
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
class_ c{{}};
|
||||
|
||||
@@ -290,8 +281,8 @@ TEST_CASE("Test subclasses regexp filter")
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::source_file;
|
||||
using namespace std::string_literals;
|
||||
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
|
||||
@@ -362,8 +353,7 @@ TEST_CASE("Test subclasses regexp filter")
|
||||
|
||||
diagram.set_complete(true);
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
CHECK(filter.should_include(*diagram.find<class_>("ns1::ns2::A1")));
|
||||
CHECK(filter.should_include(*diagram.find<class_>("ns1::ns2::B1")));
|
||||
@@ -381,8 +371,8 @@ TEST_CASE("Test parents regexp filter")
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::source_file;
|
||||
using namespace std::string_literals;
|
||||
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
|
||||
@@ -453,8 +443,7 @@ TEST_CASE("Test parents regexp filter")
|
||||
|
||||
diagram.set_complete(true);
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
CHECK(filter.should_include(*diagram.find<class_>("ns1::ns2::BaseA")));
|
||||
CHECK(filter.should_include(*diagram.find<class_>("ns1::ns2::BaseB")));
|
||||
@@ -475,8 +464,8 @@ TEST_CASE("Test specializations regexp filter")
|
||||
using clanguml::common::model::source_file;
|
||||
using clanguml::common::model::template_parameter;
|
||||
using namespace std::string_literals;
|
||||
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
|
||||
@@ -519,8 +508,7 @@ TEST_CASE("Test specializations regexp filter")
|
||||
|
||||
diagram.set_complete(true);
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
CHECK(filter.should_include(*diagram.find<class_>("A<int,std::string>")));
|
||||
CHECK(!filter.should_include(*diagram.find<class_>("A<double>")));
|
||||
@@ -541,8 +529,8 @@ TEST_CASE("Test context regexp filter")
|
||||
using clanguml::common::model::source_file;
|
||||
using clanguml::common::model::template_parameter;
|
||||
using namespace std::string_literals;
|
||||
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
|
||||
@@ -600,8 +588,7 @@ TEST_CASE("Test context regexp filter")
|
||||
|
||||
diagram.set_complete(true);
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
CHECK(filter.should_include(*diagram.find<class_>("A")));
|
||||
CHECK(filter.should_include(*diagram.find<class_>("A1")));
|
||||
@@ -632,7 +619,6 @@ TEST_CASE("Test dependencies regexp filter")
|
||||
using clanguml::common::model::template_parameter;
|
||||
using namespace std::string_literals;
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
|
||||
@@ -687,8 +673,7 @@ TEST_CASE("Test dependencies regexp filter")
|
||||
|
||||
diagram.set_complete(true);
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
CHECK(filter.should_include(*diagram.find<class_>("A")));
|
||||
CHECK(!filter.should_include(*diagram.find<class_>("A1")));
|
||||
@@ -718,7 +703,6 @@ TEST_CASE("Test dependants regexp filter")
|
||||
using clanguml::common::model::template_parameter;
|
||||
using namespace std::string_literals;
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
|
||||
auto cfg = clanguml::config::load("./test_config_data/filters.yml");
|
||||
|
||||
@@ -773,8 +757,7 @@ TEST_CASE("Test dependants regexp filter")
|
||||
|
||||
diagram.set_complete(true);
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
CHECK(filter.should_include(*diagram.find<class_>("A")));
|
||||
CHECK(filter.should_include(*diagram.find<class_>("A1")));
|
||||
@@ -792,7 +775,6 @@ TEST_CASE("Test callee_types filter")
|
||||
{
|
||||
using clanguml::common::to_id;
|
||||
using clanguml::common::model::diagram_filter;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
using clanguml::sequence_diagram::model::class_;
|
||||
using clanguml::sequence_diagram::model::function;
|
||||
using clanguml::sequence_diagram::model::function_template;
|
||||
@@ -830,8 +812,7 @@ TEST_CASE("Test callee_types filter")
|
||||
diagram.add_participant(std::move(p));
|
||||
|
||||
diagram.set_complete(true);
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
diagram_filter filter(diagram, config);
|
||||
|
||||
CHECK(
|
||||
filter.should_include(*diagram.get_participant<function>(to_id("A"s))));
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
/**
|
||||
* @file tests/test_filters_advanced.cc
|
||||
*
|
||||
* Copyright (c) 2021-2024 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.
|
||||
*/
|
||||
#define DOCTEST_CONFIG_IMPLEMENT
|
||||
|
||||
#include "doctest/doctest.h"
|
||||
|
||||
#include "class_diagram/model/class.h"
|
||||
#include "cli/cli_handler.h"
|
||||
#include "common/model/filters/diagram_filter_factory.h"
|
||||
#include "common/model/source_file.h"
|
||||
#include "config/config.h"
|
||||
#include "include_diagram/model/diagram.h"
|
||||
#include "sequence_diagram/model/diagram.h"
|
||||
|
||||
#include <filesystem>
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::class_diagram::model::class_method;
|
||||
using clanguml::common::eid_t;
|
||||
using clanguml::common::model::access_t;
|
||||
using clanguml::common::model::diagram_filter;
|
||||
using clanguml::common::model::diagram_filter_factory;
|
||||
using clanguml::common::model::namespace_;
|
||||
using clanguml::common::model::package;
|
||||
using clanguml::common::model::source_file;
|
||||
using clanguml::config::filter_mode_t;
|
||||
|
||||
TEST_CASE("Test diagram paths filter")
|
||||
{
|
||||
auto cfg =
|
||||
clanguml::config::load("./test_config_data/filters_advanced.yml");
|
||||
|
||||
auto &config = *cfg.diagrams["include_test"];
|
||||
clanguml::include_diagram::model::diagram diagram;
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
|
||||
auto make_path = [&](std::string_view p) {
|
||||
return source_file{config.root_directory() / p};
|
||||
};
|
||||
|
||||
CHECK(filter.should_include(
|
||||
make_path("class_diagram/visitor/translation_unit_visitor.h")));
|
||||
CHECK(filter.should_include(make_path("main.cc")));
|
||||
CHECK(filter.should_include(make_path("util/util.cc")));
|
||||
CHECK_FALSE(filter.should_include(make_path("util/error.h")));
|
||||
CHECK_FALSE(filter.should_include(
|
||||
make_path("sequence_diagram/visitor/translation_unit_visitor.h")));
|
||||
}
|
||||
|
||||
TEST_CASE("Test advanced diagram filter anyof")
|
||||
{
|
||||
auto cfg =
|
||||
clanguml::config::load("./test_config_data/filters_advanced.yml");
|
||||
|
||||
auto &config = *cfg.diagrams["anyof_test"];
|
||||
clanguml::include_diagram::model::diagram diagram;
|
||||
|
||||
diagram.set_complete(true);
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
|
||||
CHECK(config.filter_mode() == filter_mode_t::advanced);
|
||||
CHECK(filter.should_include(namespace_{"ns1::ns2"}));
|
||||
CHECK_FALSE(filter.should_include(namespace_{"std::string"}));
|
||||
|
||||
clanguml::common::model::element std_thread{{}};
|
||||
std_thread.set_namespace(namespace_{"std"});
|
||||
std_thread.set_name("thread");
|
||||
CHECK(filter.should_include(std_thread));
|
||||
|
||||
std_thread.set_name("jthread");
|
||||
CHECK_FALSE(filter.should_include(std_thread));
|
||||
|
||||
CHECK_FALSE(filter.should_include(namespace_{"ns1::ns2::detail"}));
|
||||
}
|
||||
|
||||
TEST_CASE("Test advanced diagram filter modules")
|
||||
{
|
||||
auto cfg =
|
||||
clanguml::config::load("./test_config_data/filters_advanced.yml");
|
||||
|
||||
auto &config = *cfg.diagrams["modules_test"];
|
||||
clanguml::include_diagram::model::diagram diagram;
|
||||
diagram.set_complete(true);
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
|
||||
CHECK(config.filter_mode() == filter_mode_t::advanced);
|
||||
CHECK(filter.should_include(namespace_{"ns1::ns2"}));
|
||||
CHECK_FALSE(filter.should_include(namespace_{"std::string"}));
|
||||
|
||||
clanguml::common::model::element std_string{{}};
|
||||
std_string.set_namespace(namespace_{"std"});
|
||||
std_string.set_name("string");
|
||||
|
||||
CHECK_FALSE(filter.should_include(std_string));
|
||||
|
||||
CHECK(filter.should_include(namespace_{"ns1"}));
|
||||
|
||||
clanguml::common::model::element e1{{}};
|
||||
e1.set_module("mod1::mod2");
|
||||
e1.set_namespace(namespace_{"ns5::ns6"});
|
||||
e1.set_name("ClassA");
|
||||
CHECK(filter.should_include(e1));
|
||||
|
||||
e1.set_module("mod1::mod3");
|
||||
CHECK_FALSE(filter.should_include(e1));
|
||||
}
|
||||
|
||||
TEST_CASE("Test method_types include filter")
|
||||
{
|
||||
auto cfg =
|
||||
clanguml::config::load("./test_config_data/filters_advanced.yml");
|
||||
|
||||
auto &config = *cfg.diagrams["method_type_include_test"];
|
||||
clanguml::class_diagram::model::diagram diagram;
|
||||
diagram.set_complete(true);
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
|
||||
class_method cm{access_t::kPublic, "A", ""};
|
||||
cm.is_constructor(true);
|
||||
|
||||
CHECK(filter.should_include(cm));
|
||||
|
||||
cm.is_constructor(false);
|
||||
cm.is_destructor(true);
|
||||
|
||||
CHECK_FALSE(filter.should_include(cm));
|
||||
}
|
||||
|
||||
TEST_CASE("Test elements and namespaces regexp filter")
|
||||
{
|
||||
auto cfg =
|
||||
clanguml::config::load("./test_config_data/filters_advanced.yml");
|
||||
|
||||
auto &config = *cfg.diagrams["regex_elements_and_namespaces"];
|
||||
clanguml::class_diagram::model::diagram diagram;
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
|
||||
class_ c{{}};
|
||||
|
||||
c.set_namespace(namespace_{"ns1"});
|
||||
c.set_name("ClassA");
|
||||
|
||||
CHECK_FALSE(filter.should_include(c));
|
||||
|
||||
c.set_namespace(namespace_{"ns1::ns2"});
|
||||
c.set_name("ClassA");
|
||||
|
||||
CHECK(filter.should_include(c));
|
||||
|
||||
c.set_namespace(namespace_{"ns1::ns2"});
|
||||
c.set_name("ClassZ");
|
||||
|
||||
CHECK(filter.should_include(c));
|
||||
|
||||
c.set_namespace(namespace_{"ns1::ns5::ns3"});
|
||||
c.set_name("ClassA");
|
||||
|
||||
CHECK_FALSE(filter.should_include(c));
|
||||
}
|
||||
|
||||
TEST_CASE("Test edge filter and namespaces filter")
|
||||
{
|
||||
auto cfg =
|
||||
clanguml::config::load("./test_config_data/filters_advanced.yml");
|
||||
|
||||
auto &config = *cfg.diagrams["edge_filter_and_namespaces"];
|
||||
clanguml::class_diagram::model::diagram diagram;
|
||||
|
||||
auto filter_ptr = diagram_filter_factory::create(diagram, config);
|
||||
diagram_filter &filter = *filter_ptr;
|
||||
|
||||
diagram.set_complete(true);
|
||||
|
||||
uint64_t id{1};
|
||||
|
||||
{
|
||||
auto ns1 = std::make_unique<package>(namespace_{});
|
||||
ns1->set_name("ns1");
|
||||
ns1->set_id(eid_t{id++});
|
||||
diagram.add(namespace_{}, std::move(ns1));
|
||||
}
|
||||
{
|
||||
auto ns1__nsA = std::make_unique<package>(namespace_{});
|
||||
ns1__nsA->set_name("nsA");
|
||||
ns1__nsA->set_namespace(namespace_{"ns1"});
|
||||
ns1__nsA->set_id(eid_t{id++});
|
||||
diagram.add(namespace_{}, std::move(ns1__nsA));
|
||||
}
|
||||
{
|
||||
auto A = std::make_unique<class_>(namespace_{});
|
||||
A->set_namespace(namespace_{"ns1::nsA"});
|
||||
A->set_name("A");
|
||||
A->set_id(eid_t{id});
|
||||
diagram.add(namespace_{"ns1::nsA"}, std::move(A));
|
||||
}
|
||||
|
||||
CHECK(filter.should_include(*diagram.get(eid_t{id})));
|
||||
|
||||
class_ c{{}};
|
||||
c.set_namespace(namespace_{"ns2::nsB"});
|
||||
c.set_name("B");
|
||||
|
||||
CHECK(filter.should_include(c));
|
||||
|
||||
{
|
||||
auto C = std::make_unique<class_>(namespace_{});
|
||||
C->set_namespace(namespace_{"ns1::nsA"});
|
||||
C->set_name("C");
|
||||
C->set_id(eid_t{++id});
|
||||
diagram.add(namespace_{"ns1::nsA"}, std::move(C));
|
||||
}
|
||||
|
||||
c.set_namespace(namespace_{"ns2::nsB"});
|
||||
c.set_name("C");
|
||||
|
||||
CHECK(filter.should_include(c));
|
||||
|
||||
c.set_namespace(namespace_{"ns1::nsA"});
|
||||
c.set_name("R");
|
||||
|
||||
CHECK_FALSE(filter.should_include(c));
|
||||
}
|
||||
|
||||
///
|
||||
/// Main test function
|
||||
///
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
doctest::Context context;
|
||||
|
||||
context.applyCommandLine(argc, argv);
|
||||
|
||||
clanguml::cli::cli_handler clih;
|
||||
|
||||
std::vector<const char *> argvv = {
|
||||
"clang-uml", "--config", "./test_config_data/simple.yml"};
|
||||
|
||||
argvv.push_back("-q");
|
||||
|
||||
clih.handle_options(argvv.size(), argvv.data());
|
||||
|
||||
int res = context.run();
|
||||
|
||||
if (context.shouldExit())
|
||||
return res;
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -474,64 +474,4 @@ TEST_CASE("Test parse_source_location")
|
||||
CHECK(column == 456);
|
||||
|
||||
result = false, file = "", line = 0, column = 0;
|
||||
}
|
||||
|
||||
TEST_CASE("Test is_subpath")
|
||||
{
|
||||
using clanguml::util::is_subpath;
|
||||
|
||||
CHECK(is_subpath("./include/f.h", "."));
|
||||
CHECK(is_subpath("include/f.h", "."));
|
||||
CHECK(is_subpath("./include/f.h", "./"));
|
||||
CHECK(is_subpath("./include/f.h", "./include"));
|
||||
CHECK(is_subpath("./include/f.h", "include"));
|
||||
CHECK(is_subpath("include/f.h", "./include"));
|
||||
CHECK(is_subpath("include/f.h", "include"));
|
||||
|
||||
CHECK(is_subpath("include/f.h", "include/f.h"));
|
||||
CHECK(is_subpath("./include/f.h", "include/f.h"));
|
||||
CHECK(is_subpath("include/f.h", "./include/f.h"));
|
||||
CHECK(is_subpath("./include/f.h", "include/f.h"));
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
CHECK(is_subpath("/usr/local/include/f.h", "/usr/local"));
|
||||
CHECK_FALSE(is_subpath("/usr/local/include/f.h", "/usr/local/lib"));
|
||||
CHECK_FALSE(is_subpath("/usr/include/f.h", "."));
|
||||
#else
|
||||
CHECK(is_subpath("E:\\test\\src\\main.cpp", "E:\\test"));
|
||||
CHECK_FALSE(is_subpath("E:\\test\\src\\main.cpp", "C:\\test"));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("Test find_entry_by_path_prefix")
|
||||
{
|
||||
using clanguml::util::find_entry_by_path_prefix;
|
||||
|
||||
std::map<std::string, std::string> m;
|
||||
m.emplace(".", "default");
|
||||
m.emplace("./src", "internal_sources");
|
||||
m.emplace("./src/thirdparty/", "thirdparty_sources");
|
||||
m.emplace("/usr/include/boost", "boost_sources");
|
||||
m.emplace("../my_other_project/src", "my_other_project_sources");
|
||||
|
||||
auto kv = find_entry_by_path_prefix(m, "/tmp");
|
||||
CHECK_FALSE(kv.has_value());
|
||||
|
||||
kv = find_entry_by_path_prefix(m, "./src/main.cc");
|
||||
CHECK(kv.value().second == "internal_sources");
|
||||
|
||||
kv = find_entry_by_path_prefix(m, "src/main.cc");
|
||||
CHECK(kv.value().second == "internal_sources");
|
||||
|
||||
kv = find_entry_by_path_prefix(m, "src/thirdparty/main.cc");
|
||||
CHECK(kv.value().second == "thirdparty_sources");
|
||||
|
||||
kv = find_entry_by_path_prefix(m, "include/f.h");
|
||||
CHECK(kv.value().second == "default");
|
||||
|
||||
kv = find_entry_by_path_prefix(m, "./include/f.h");
|
||||
CHECK(kv.value().second == "default");
|
||||
|
||||
kv = find_entry_by_path_prefix(m, "../my_other_project/src/lib.cc");
|
||||
CHECK(kv.value().second == "my_other_project_sources");
|
||||
}
|
||||
@@ -3,7 +3,7 @@ title: Diagram filter visitor class hierarchy
|
||||
include_relations_also_as_members: true
|
||||
generate_packages: true
|
||||
glob:
|
||||
- src/common/model/filters/*.cc
|
||||
- src/common/model/diagram_filter.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml
|
||||
|
||||
Reference in New Issue
Block a user