Added package diagram generation from C++20 modules (#101)
This commit is contained in:
@@ -25,8 +25,9 @@
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
element::element(namespace_ using_namespace)
|
||||
: using_namespace_{std::move(using_namespace)}
|
||||
element::element(namespace_ using_namespace, path_type pt)
|
||||
: ns_{pt}
|
||||
, using_namespace_{std::move(using_namespace)}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,9 @@ namespace clanguml::common::model {
|
||||
*/
|
||||
class element : public diagram_element {
|
||||
public:
|
||||
element(namespace_ using_namespace);
|
||||
element(namespace_ using_namespace, path_type pt = path_type::kNamespace);
|
||||
|
||||
element(path_type pt);
|
||||
|
||||
~element() override = default;
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
#include <sstream>
|
||||
|
||||
namespace clanguml::common::model {
|
||||
package::package(const common::model::namespace_ &using_namespace)
|
||||
: element{using_namespace}
|
||||
package::package(const common::model::namespace_ &using_namespace, path_type pt)
|
||||
: element{using_namespace, pt}
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ class package : public element,
|
||||
public stylable_element,
|
||||
public nested_trait<element, path> {
|
||||
public:
|
||||
package(const common::model::path &using_namespace);
|
||||
package(const common::model::path &using_namespace,
|
||||
path_type pt = path_type::kNamespace);
|
||||
|
||||
package(const package &) = delete;
|
||||
package(package &&) = default;
|
||||
|
||||
@@ -386,6 +386,8 @@ public:
|
||||
*/
|
||||
path_type type() const { return path_type_; }
|
||||
|
||||
const container_type &tokens() const { return path_; }
|
||||
|
||||
private:
|
||||
path_type path_type_;
|
||||
container_type path_;
|
||||
|
||||
@@ -135,6 +135,9 @@ public:
|
||||
if (parent_path.type() == common::model::path_type::kNamespace) {
|
||||
return add_with_namespace_path(std::move(e));
|
||||
}
|
||||
else if (parent_path.type() == common::model::path_type::kModule) {
|
||||
return add_with_module_path(parent_path, std::move(e));
|
||||
}
|
||||
|
||||
return add_with_filesystem_path(parent_path, std::move(e));
|
||||
}
|
||||
@@ -155,6 +158,17 @@ public:
|
||||
inja::json context() const override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Add element using module as diagram path
|
||||
*
|
||||
* @tparam ElementT Element type
|
||||
* @param e Element to add
|
||||
* @return True, if the element was added
|
||||
*/
|
||||
template <typename ElementT>
|
||||
bool add_with_module_path(
|
||||
const common::model::path &parent_path, std::unique_ptr<ElementT> &&e);
|
||||
|
||||
/**
|
||||
* @brief Add element using namespace as diagram path
|
||||
*
|
||||
@@ -237,6 +251,55 @@ bool diagram::add_with_namespace_path(std::unique_ptr<ElementT> &&p)
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename ElementT>
|
||||
bool diagram::add_with_module_path(
|
||||
const common::model::path &parent_path, std::unique_ptr<ElementT> &&p)
|
||||
{
|
||||
LOG_DBG("Adding package: {}, {}, {}, [{}]", p->name(), p->full_name(false),
|
||||
parent_path.to_string(), p->id());
|
||||
|
||||
// Make sure all parent modules are already packages in the
|
||||
// model
|
||||
std::string module_path = p->using_namespace().to_string();
|
||||
for (auto it = parent_path.begin(); it != parent_path.end(); it++) {
|
||||
auto pkg = std::make_unique<common::model::package>(
|
||||
p->using_namespace(), common::model::path_type::kModule);
|
||||
pkg->set_name(*it);
|
||||
|
||||
auto ns = common::model::path(
|
||||
parent_path.begin(), it, common::model::path_type::kModule);
|
||||
pkg->set_module(module_path);
|
||||
pkg->set_namespace(ns);
|
||||
|
||||
std::string package_id_path;
|
||||
if (module_path.empty())
|
||||
package_id_path = pkg->name();
|
||||
else
|
||||
package_id_path = module_path + "." + pkg->name();
|
||||
|
||||
pkg->set_id(common::to_id(package_id_path));
|
||||
|
||||
auto p_ref = std::ref(*pkg);
|
||||
|
||||
auto res = add_element(ns, std::move(pkg));
|
||||
if (res)
|
||||
element_view<ElementT>::add(p_ref);
|
||||
|
||||
if (module_path.empty())
|
||||
module_path = *it;
|
||||
else
|
||||
module_path += fmt::format(".{}", *it);
|
||||
}
|
||||
|
||||
auto p_ref = std::ref(*p);
|
||||
|
||||
auto res = add_element(parent_path, std::move(p));
|
||||
if (res)
|
||||
element_view<ElementT>::add(p_ref);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template <typename ElementT>
|
||||
bool diagram::add_with_filesystem_path(
|
||||
const common::model::path &parent_path, std::unique_ptr<ElementT> &&p)
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "common/clang_utils.h"
|
||||
#include "common/model/namespace.h"
|
||||
|
||||
#include "clang/Basic/Module.h"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <deque>
|
||||
@@ -45,7 +47,7 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns)
|
||||
{
|
||||
assert(ns != nullptr);
|
||||
|
||||
if (config().package_type() == config::package_type_t::kDirectory)
|
||||
if (config().package_type() != config::package_type_t::kNamespace)
|
||||
return true;
|
||||
|
||||
if (ns->isAnonymousNamespace() || ns->isInline())
|
||||
@@ -237,6 +239,43 @@ void translation_unit_visitor::add_relationships(
|
||||
if (diagram().should_include(*pkg))
|
||||
diagram().add(parent_path, std::move(pkg));
|
||||
}
|
||||
else if (config().package_type() == config::package_type_t::kModule) {
|
||||
const auto *module = cls->getOwningModule();
|
||||
|
||||
if (module == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string module_path_str = module->Name;
|
||||
if (module->isPrivateModule())
|
||||
module_path_str = module->getTopLevelModule()->Name;
|
||||
|
||||
common::model::path module_path{
|
||||
module_path_str, common::model::path_type::kModule};
|
||||
module_path.pop_back();
|
||||
|
||||
auto relative_module =
|
||||
config().make_module_relative(std::optional{module_path_str});
|
||||
|
||||
common::model::path parent_path{
|
||||
relative_module, common::model::path_type::kModule};
|
||||
auto pkg_name = parent_path.name();
|
||||
parent_path.pop_back();
|
||||
|
||||
auto pkg = std::make_unique<common::model::package>(
|
||||
config().using_module(), common::model::path_type::kModule);
|
||||
|
||||
pkg->set_name(pkg_name);
|
||||
pkg->set_id(get_package_id(cls));
|
||||
// This is for diagram filters
|
||||
pkg->set_module(module_path.to_string());
|
||||
// This is for rendering nested package structure
|
||||
pkg->set_namespace(parent_path);
|
||||
set_source_location(*cls, *pkg);
|
||||
|
||||
if (diagram().should_include(*pkg))
|
||||
diagram().add(parent_path, std::move(pkg));
|
||||
}
|
||||
|
||||
auto current_package_id = get_package_id(cls);
|
||||
|
||||
@@ -284,6 +323,18 @@ common::model::diagram_element::id_t translation_unit_visitor::get_package_id(
|
||||
|
||||
return {};
|
||||
}
|
||||
else if (config().package_type() == config::package_type_t::kModule) {
|
||||
const auto *module = cls->getOwningModule();
|
||||
if (module != nullptr) {
|
||||
std::string module_path = module->Name;
|
||||
if (module->isPrivateModule()) {
|
||||
module_path = module->getTopLevelModule()->Name;
|
||||
}
|
||||
return common::to_id(module_path);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto file =
|
||||
source_manager().getFilename(cls->getSourceRange().getBegin()).str();
|
||||
@@ -578,6 +629,15 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (config().package_type() ==
|
||||
config::package_type_t::kModule) {
|
||||
const auto *module = cxxrecord_decl->getOwningModule();
|
||||
if (module != nullptr) {
|
||||
const auto target_id = get_package_id(cxxrecord_decl);
|
||||
relationships.emplace_back(target_id, relationship_hint);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (diagram().should_include(
|
||||
namespace_{common::get_qualified_name(
|
||||
@@ -591,8 +651,8 @@ bool translation_unit_visitor::find_relationships(const clang::QualType &type,
|
||||
}
|
||||
else if (const auto *record_decl = type->getAsRecordDecl();
|
||||
record_decl != nullptr) {
|
||||
// This is only possible for plain C translation unit, so we don't
|
||||
// need to consider namespaces here
|
||||
// This is only possible for plain C translation unit, so we
|
||||
// don't need to consider namespaces or modules here
|
||||
if (config().package_type() == config::package_type_t::kDirectory) {
|
||||
if (diagram().should_include(
|
||||
namespace_{common::get_qualified_name(*record_decl)})) {
|
||||
|
||||
Reference in New Issue
Block a user