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>
43std::string
to_plantuml(
const relationship &r,
const config::diagram &cfg);
53template <
typename ConfigType,
typename DiagramType>
81 void generate(std::ostream &ostr)
const override;
113 std::ostream &ostr,
const std::vector<std::string> &directives)
const;
137 void generate_style(std::ostream &ostr,
const std::string &element_type,
168 template <
typename E>
199template <
typename C,
typename D>
207 if (!config.allow_empty_diagrams() && model.is_empty() &&
208 config.puml().before.empty() && config.puml().after.empty()) {
210 "Diagram configuration resulted in empty diagram."};
213 ostr <<
"@startuml" <<
'\n';
215 generate_title(ostr);
218 generate_plantuml_directives(ostr, config.puml().before);
223 generate_plantuml_directives(ostr, config.puml().after);
225 generate_metadata(ostr);
227 ostr <<
"@enduml" <<
'\n';
230template <
typename C,
typename D>
238 for (
const auto &[entity_name, hints] : config.layout()) {
239 for (
const auto &hint : hints) {
246 generate_row_column_hints(ostr, entity_name, hint);
249 generate_position_hints(ostr, entity_name, hint);
253 LOG_DBG(
"=== Skipping layout hint '{}' from {} due "
255 to_string(hint.hint), entity_name, e.what());
261template <
typename C,
typename D>
268 const auto &uns = config.using_namespace();
270 std::vector<std::string> group_elements;
271 std::vector<std::pair<std::string, std::string>> element_aliases_pairs;
273 group_elements.push_back(entity_name);
274 const auto &group_tail = std::get<std::vector<std::string>>(hint.
entity);
275 std::copy(group_tail.begin(), group_tail.end(),
276 std::back_inserter(group_elements));
278 auto element_opt = model.get(entity_name);
280 element_opt = model.get((uns | entity_name).
to_string());
282 for (
auto it = cbegin(group_elements);
283 it != cend(group_elements) && std::next(it) != cend(group_elements);
285 const auto &first = *it;
286 const auto &second = *std::next(it);
288 auto first_opt = model.get(first);
290 first_opt = model.get((uns | first).
to_string());
292 auto second_opt = model.get(second);
294 second_opt = model.get((uns | second).
to_string());
296 element_aliases_pairs.emplace_back(
297 first_opt.value().alias(), second_opt.value().alias());
300 std::string hint_direction =
303 for (
const auto &[f, s] : element_aliases_pairs) {
304 ostr << f <<
" -[hidden]" << hint_direction <<
"- " << s <<
'\n';
308template <
typename C,
typename D>
315 const auto &uns = config.using_namespace();
317 const auto &hint_entity = std::get<std::string>(hint.
entity);
319 auto element_opt = model.get(entity_name);
321 element_opt = model.get((uns | entity_name).
to_string());
323 auto hint_element_opt = model.get(hint_entity);
324 if (!hint_element_opt)
325 hint_element_opt = model.get((uns | hint_entity).
to_string());
327 if (!element_opt || !hint_element_opt)
330 std::stringstream hint_str;
332 hint_str << element_opt.value().alias() <<
" -[hidden]"
334 << hint_element_opt.value().alias() <<
'\n';
336 ostr << hint_str.str();
339template <
typename C,
typename D>
341 std::ostream &ostr,
const std::vector<std::string> &directives)
const
345 for (
const auto &d : directives) {
346 auto rendered_directive =
350 if (rendered_directive)
351 ostr << *rendered_directive <<
'\n';
355template <
typename C,
typename D>
361 if (el.
style() && !el.
style().value().empty())
362 ostr <<
" " << *el.
style();
363 else if (config.puml) {
364 if (
const auto config_style = config.puml().get_style(element_type);
366 ostr <<
" " << *config_style;
371template <
typename C,
typename D>
377 for (
const auto &decorator : e.
decorators()) {
378 auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
379 if (note && note->applies_to_diagram(config.name)) {
380 ostr <<
"note " << note->position <<
" of " << e.
alias() <<
'\n'
381 << note->text <<
'\n'
387template <
typename C,
typename D>
392 if (config.generate_metadata()) {
394 <<
"'Generated with clang-uml, version "
396 <<
"'LLVM version " << clang::getClangFullVersion() <<
'\n';
400template <
typename C,
typename D>
406 ostr <<
"title " << config.title() <<
'\n';
410template <
typename C,
typename D>
417 if (!maybe_link && !maybe_tooltip)
422 ostr << maybe_link.value_or(
"");
425 ostr <<
"{" << *maybe_tooltip <<
"}";
430template <
typename C,
typename D>
437 if (!maybe_link && !maybe_tooltip)
442 ostr << maybe_link.value_or(
"");
445 ostr <<
"{" << *maybe_tooltip <<
"}";
450template <
typename C,
typename D>
456 if (config.debug_mode()) {
460 else if (!e.
file().empty()) {
461 ostr <<
"' " << e.
file() <<
":" << e.
line() <<
'\n';
466template <
typename DiagramModel,
typename DiagramConfig>