0.6.0
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
generator.h
Go to the documentation of this file.
1/**
2 * @file src/common/generators/mermaid/generator.h
3 *
4 * Copyright (c) 2021-2025 Bartek Kryza <bkryza@gmail.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18#pragma once
19
22#include "config/config.h"
23#include "util/error.h"
24#include "util/util.h"
25#include "version/version.h"
26
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>
33
35
42
43std::string to_mermaid(relationship_t r);
44std::string to_mermaid(access_t scope);
45std::string to_mermaid(message_t r);
46
47std::string indent(unsigned level);
48
49std::string escape_name(std::string name, bool round_brackets = true);
50
51/**
52 * @brief Base class for diagram generators
53 *
54 * @tparam ConfigType Configuration type
55 * @tparam DiagramType Diagram model type
56 */
57template <typename ConfigType, typename DiagramType>
59 : public clanguml::common::generators::generator<ConfigType, DiagramType> {
60public:
61 /**
62 * @brief Constructor
63 *
64 * @param config Reference to instance of @link clanguml::config::diagram
65 * @param model Reference to instance of @link clanguml::model::diagram
66 */
67 generator(ConfigType &config, DiagramType &model)
68 : clanguml::common::generators::generator<ConfigType, DiagramType>{
70 {
71 }
72
73 ~generator() override = default;
74
75 /**
76 * @brief Generate diagram
77 *
78 * This is the main diagram generation entrypoint. It is responsible for
79 * calling other methods in appropriate order to generate the diagram into
80 * the output stream. It generates diagram elements, that are common
81 * to all types of diagrams in a given generator.
82 *
83 * @param ostr Output stream
84 */
85 void generate(std::ostream &ostr) const override;
86
87 /**
88 * @brief Generate diagram specific part
89 *
90 * This method must be implemented in subclasses for specific diagram
91 * types.
92 *
93 * @param ostr Output stream
94 */
95 virtual void generate_diagram(std::ostream &ostr) const = 0;
96
97 /**
98 * @brief Generate MermaidJS directives from config file.
99 *
100 * This method renders the MermaidJS directives provided in the
101 * configuration file, including resolving any element aliases and Jinja
102 * templates.
103 *
104 * @param ostr Output stream
105 * @param directives List of directives from the configuration file
106 */
108 std::ostream &ostr, const std::vector<std::string> &directives) const;
109
110 /**
111 * @brief Generate the diagram type
112 *
113 * This method must be overriden for each diagram type (e.g. it renders
114 * a single line `classDiagram` for Mermaid class diagrams.
115 *
116 * @param ostr Output stream
117 */
118 virtual void generate_diagram_type(std::ostream &ostr) const = 0;
119
120 /**
121 * @brief Generate diagram notes
122 *
123 * This method adds any notes in the diagram, which were declared in the
124 * code using inline directives
125 *
126 * @param ostr Output stream
127 * @param element Element to which the note should be attached
128 */
129 virtual void generate_notes(
130 std::ostream &ostr, const model::diagram_element &element) const;
131
132 /**
133 * @brief Generate comment with diagram metadata
134 *
135 * @param ostr Output stream
136 */
137 void generate_metadata(std::ostream &ostr) const;
138
139 /**
140 * @brief Generate diagram title
141 *
142 * Generates a MermaidJS diagram title directive if diagram title
143 * is provided in the diagram configuration.
144 *
145 * @param ostr Output stream
146 */
147 void generate_title(std::ostream &ostr) const;
148
149 /**
150 * @brief Generate hyper link to element
151 *
152 * This method renders links to URL's based on templates provided
153 * in the configuration file (e.g. Git browser with specific line and
154 * column offset)
155 *
156 * @param ostr Output stream
157 * @param e Reference to diagram element
158 * @tparam E Diagram element type
159 */
160 template <typename E>
161 void generate_link(std::ostream &ostr, const E &e) const;
162
163 /**
164 * @brief Print debug information in diagram comments
165 *
166 * @param m Diagram element to describe
167 * @param ostr Output stream
168 */
169 void print_debug(
170 const common::model::source_location &e, std::ostream &ostr) const;
171
172protected:
173 mutable std::set<std::string> m_generated_aliases;
174};
175
176template <typename C, typename D>
177void generator<C, D>::generate(std::ostream &ostr) const
178{
179 const auto &config = generators::generator<C, D>::config();
180 const auto &model = generators::generator<C, D>::model();
181
182 if (!config.allow_empty_diagrams() && model.is_empty() &&
183 config.mermaid().before.empty() && config.mermaid().after.empty()) {
184 throw clanguml::error::empty_diagram_error{model.type(), model.name(),
185 "Diagram configuration resulted in empty diagram."};
186 }
187
189
190 generate_title(ostr);
191
192 generate_diagram_type(ostr);
193
194 generate_mermaid_directives(ostr, config.mermaid().before);
195
196 generate_diagram(ostr);
197
198 generate_mermaid_directives(ostr, config.mermaid().after);
199
200 generate_metadata(ostr);
201}
202
203template <typename C, typename D>
204template <typename E>
205void generator<C, D>::generate_link(std::ostream &ostr, const E &e) const
206{
207 const auto maybe_link = generator<C, D>::render_link(e);
208 const auto maybe_tooltip = generator<C, D>::render_tooltip(e);
209
210 if (!maybe_link && !maybe_tooltip)
211 return;
212
213 if (maybe_link) {
214 ostr << indent(1) << "click " << e.alias() << " href \"";
215
216 if (!maybe_link || maybe_link->empty())
217 ostr << " ";
218 else
219 ostr << *maybe_link;
220
221 ostr << "\"";
222 }
223
224 if (maybe_tooltip && !maybe_tooltip->empty()) {
225 auto tooltip = *maybe_tooltip;
226 ostr << " \"";
227
228 util::replace_all(tooltip, "\"", "&bdquo;");
229 ostr << tooltip;
230
231 ostr << "\"";
232 }
233
234 ostr << "\n";
235}
236
237template <typename C, typename D>
239 std::ostream &ostr, const std::vector<std::string> &directives) const
240{
242
243 for (const auto &d : directives) {
244 auto rendered_directive = common::jinja::render_template(
246 if (rendered_directive)
247 ostr << indent(1) << *rendered_directive << '\n';
248 }
249}
250
251template <typename C, typename D>
253 std::ostream &ostr, const model::diagram_element &e) const
254{
255 const auto &config = generators::generator<C, D>::config();
256
257 for (const auto &decorator : e.decorators()) {
258 auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
259 if (note && note->applies_to_diagram(config.name)) {
260 ostr << indent(1) << "note for " << e.alias() << " \"" << note->text
261 << "\"" << '\n';
262 }
263 }
264}
265
266template <typename C, typename D>
267void generator<C, D>::generate_metadata(std::ostream &ostr) const
268{
269 const auto &config = generators::generator<C, D>::config();
270
271 if (config.generate_metadata()) {
272 ostr << '\n'
273 << "%% Generated with clang-uml, version "
274 << clanguml::version::version() << '\n'
275 << "%% LLVM version " << clang::getClangFullVersion() << '\n';
276 }
277}
278
279template <typename C, typename D>
280void generator<C, D>::generate_title(std::ostream &ostr) const
281{
282 const auto &config = generators::generator<C, D>::config();
283
284 if (config.title) {
285 ostr << "---\n";
286 ostr << "title: " << config.title() << '\n';
287 ostr << "---\n";
288 }
289}
290
291template <typename C, typename D>
293 const common::model::source_location &e, std::ostream &ostr) const
294{
295 const auto &config = generators::generator<C, D>::config();
296
297 if (config.debug_mode()) {
298 if (!e.file_relative().empty()) {
299 ostr << "%% " << e.file_relative() << ":" << e.line() << '\n';
300 }
301 else if (!e.file().empty()) {
302 ostr << "%% " << e.file() << ":" << e.line() << '\n';
303 }
304 }
305}
306
307template <typename DiagramModel, typename DiagramConfig>
308std::ostream &operator<<(
309 std::ostream &os, const generator<DiagramModel, DiagramConfig> &g)
310{
311 g.generate(os);
312 return os;
313}
314} // namespace clanguml::common::generators::mermaid