27#include <clang/Basic/Version.h>
28#include <clang/Frontend/CompilerInstance.h>
29#include <clang/Tooling/CompilationDatabase.h>
30#include <clang/Tooling/Tooling.h>
31#include <glob/glob.hpp>
32#include <inja/inja.hpp>
41std::string
to_plantuml(
const relationship &r,
const config::diagram &cfg);
51template <
typename ConfigType,
typename DiagramType>
79 void generate(std::ostream &ostr)
const override;
111 std::ostream &ostr,
const std::vector<std::string> &directives)
const;
135 void generate_style(std::ostream &ostr,
const std::string &element_type,
166 template <
typename E>
189template <
typename C,
typename D>
197 if (!config.allow_empty_diagrams() && model.is_empty() &&
198 config.puml().before.empty() && config.puml().after.empty()) {
200 "Diagram configuration resulted in empty diagram."};
203 ostr <<
"@startuml" <<
'\n';
205 generate_title(ostr);
208 generate_plantuml_directives(ostr, config.puml().before);
213 generate_plantuml_directives(ostr, config.puml().after);
215 generate_metadata(ostr);
217 ostr <<
"@enduml" <<
'\n';
220template <
typename C,
typename D>
228 for (
const auto &[entity_name, hints] : config.layout()) {
229 for (
const auto &hint : hints) {
236 generate_row_column_hints(ostr, entity_name, hint);
239 generate_position_hints(ostr, entity_name, hint);
243 LOG_DBG(
"=== Skipping layout hint '{}' from {} due "
245 to_string(hint.hint), entity_name, e.what());
251template <
typename C,
typename D>
258 const auto &uns = config.using_namespace();
260 std::vector<std::string> group_elements;
261 std::vector<std::pair<std::string, std::string>> element_aliases_pairs;
263 group_elements.push_back(entity_name);
264 const auto &group_tail = std::get<std::vector<std::string>>(hint.
entity);
265 std::copy(group_tail.begin(), group_tail.end(),
266 std::back_inserter(group_elements));
268 auto element_opt = model.get(entity_name);
270 element_opt = model.get((uns | entity_name).
to_string());
272 for (
auto it = cbegin(group_elements);
273 it != cend(group_elements) && std::next(it) != cend(group_elements);
275 const auto &first = *it;
276 const auto &second = *std::next(it);
278 auto first_opt = model.get(first);
280 first_opt = model.get((uns | first).
to_string());
282 auto second_opt = model.get(second);
284 second_opt = model.get((uns | second).
to_string());
286 element_aliases_pairs.emplace_back(
287 first_opt.value().alias(), second_opt.value().alias());
290 std::string hint_direction =
293 for (
const auto &[f, s] : element_aliases_pairs) {
294 ostr << f <<
" -[hidden]" << hint_direction <<
"- " << s <<
'\n';
298template <
typename C,
typename D>
305 const auto &uns = config.using_namespace();
307 const auto &hint_entity = std::get<std::string>(hint.
entity);
309 auto element_opt = model.get(entity_name);
311 element_opt = model.get((uns | entity_name).
to_string());
313 auto hint_element_opt = model.get(hint_entity);
314 if (!hint_element_opt)
315 hint_element_opt = model.get((uns | hint_entity).
to_string());
317 if (!element_opt || !hint_element_opt)
320 std::stringstream hint_str;
322 hint_str << element_opt.value().alias() <<
" -[hidden]"
324 << hint_element_opt.value().alias() <<
'\n';
326 ostr << hint_str.str();
329template <
typename C,
typename D>
331 std::ostream &ostr,
const std::vector<std::string> &directives)
const
338 for (
const auto &d : directives) {
346 std::tuple<std::string, size_t, size_t> alias_match;
348 const auto full_name =
349 config.using_namespace() | std::get<0>(alias_match);
350 auto element_opt = model.get(full_name.to_string());
353 directive.replace(std::get<1>(alias_match),
354 std::get<2>(alias_match), element_opt.value().alias());
356 LOG_WARN(
"Cannot find clang-uml alias for element {}",
357 full_name.to_string());
358 directive.replace(std::get<1>(alias_match),
359 std::get<2>(alias_match),
"UNKNOWN_ALIAS");
363 ostr << directive <<
'\n';
366 LOG_WARN(
"Failed to render PlantUML directive due to unresolvable "
370 catch (
const inja::json::parse_error &e) {
371 LOG_WARN(
"Failed to parse Jinja template: {}", d);
373 catch (
const inja::json::exception &e) {
374 LOG_WARN(
"Failed to render PlantUML directive: \n{}\n due to: {}",
377 catch (
const std::regex_error &e) {
378 LOG_WARN(
"Failed to render PlantUML directive: \n{}\n due to "
379 "std::regex_error: {}",
382 catch (
const std::exception &e) {
383 LOG_WARN(
"Failed to render PlantUML directive: \n{}\n due to: {}",
389template <
typename C,
typename D>
395 if (el.
style() && !el.
style().value().empty())
396 ostr <<
" " << *el.
style();
397 else if (config.puml) {
398 if (
const auto config_style = config.puml().get_style(element_type);
400 ostr <<
" " << *config_style;
405template <
typename C,
typename D>
411 for (
const auto &decorator : e.
decorators()) {
412 auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
413 if (note && note->applies_to_diagram(config.name)) {
414 ostr <<
"note " << note->position <<
" of " << e.
alias() <<
'\n'
415 << note->text <<
'\n'
421template <
typename C,
typename D>
426 if (config.generate_metadata()) {
428 <<
"'Generated with clang-uml, version "
429 << clanguml::version::CLANG_UML_VERSION <<
'\n'
430 <<
"'LLVM version " << clang::getClangFullVersion() <<
'\n';
434template <
typename C,
typename D>
440 ostr <<
"title " << config.title() <<
'\n';
444template <
typename C,
typename D>
448 if (e.file().empty() && e.file_relative().empty())
453 if (!maybe_link_pattern) {
457 const auto &[link_prefix, link_pattern] = *maybe_link_pattern;
461 if (!link_pattern.empty()) {
464 ostr << generators::generator<C, D>::env().render(
465 std::string_view{link_pattern}, ec);
468 catch (
const inja::json::parse_error &e) {
469 LOG_ERROR(
"Failed to parse Jinja template: {}", link_pattern);
471 catch (
const std::exception &e) {
472 LOG_ERROR(
"Failed to render PlantUML directive: \n{}\n due to: {}",
473 link_pattern, e.what());
476 auto maybe_tooltip_pattern =
479 if (maybe_tooltip_pattern) {
480 const auto &[tooltip_prefix, tooltip_pattern] = *maybe_tooltip_pattern;
487 if (!tooltip_pattern.empty()) {
488 ostr << generators::generator<C, D>::env().render(
489 std::string_view{tooltip_pattern}, ec);
492 catch (
const inja::json::parse_error &e) {
493 LOG_ERROR(
"Failed to parse Jinja template: {}", tooltip_pattern);
495 catch (
const std::exception &e) {
496 LOG_ERROR(
"Failed to render PlantUML directive: \n{}\n due to: {}",
497 tooltip_pattern, e.what());
504template <
typename C,
typename D>
510 if (config.debug_mode())
511 ostr <<
"' " << e.
file() <<
":" << e.
line() <<
'\n';
514template <
typename DiagramModel,
typename DiagramConfig>