6#ifdef QDOC_TEMPLATE_GENERATOR_ENABLED
9#include "inclusionfilter.h"
10#include "outputcontext.h"
11#include "qmltypenode.h"
15#include <QtCore/qdir.h>
16#include <QtCore/qfileinfo.h>
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
39
40
41
42
43
44
45
47HrefResolver::HrefResolver(
const HrefResolverConfig &config)
53
54
55
56
57
58
59QString HrefResolver::fileBase(
const Node *node)
const
61 if (!node->isPageNode() && !node->isCollectionNode()) {
62 node = node->parent();
67 if (node->hasFileNameBase())
68 return node->fileNameBase();
70 auto it = m_fileBaseCache.constFind(node);
71 if (it != m_fileBaseCache.constEnd())
74 const OutputContext *ctx = m_config.context;
75 QString result = Utilities::computeFileBase(
77 [ctx](
const Node *n) {
return ctx->outputPrefix(n->genus()); },
78 [ctx](
const Node *n) {
return ctx->outputSuffix(n->genus()); });
80 m_fileBaseCache.insert(node, result);
85
86
87
88
89
90
91
92QString HrefResolver::fileName(
const Node *node)
const
94 if (!node->url().isEmpty())
97 const auto base = fileBase(node);
101 if (node->isTextPageNode() && !node->isCollectionNode()) {
102 QFileInfo originalName(node->name());
103 QString suffix = originalName.suffix();
104 if (!suffix.isEmpty() && suffix !=
"html"_L1)
105 return base +
'.'_L1 + suffix;
108 return base +
'.'_L1 + m_config.context->fileExtension;
112
113
114
115
116
117
118
119
120
121QString HrefResolver::anchorForNode(
const Node *node)
const
123 const QString ref = computeAnchorId(node);
126 return m_config.cleanRefFn ? m_config.cleanRefFn(ref) : ref;
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144HrefResult HrefResolver::hrefForNode(
const Node *node,
const Node *relative)
const
147 return HrefSuppressReason::NullNode;
149 const bool hasExplicitUrl = !node->url().isEmpty();
150 if (hasExplicitUrl && node->url().contains(
"://"_L1))
158 QString fn = hasExplicitUrl
159 ? node->url().section(
'/'_L1, -1)
162 return HrefSuppressReason::NoFileBase;
164 const NodeContext context = node->createContext();
165 if (!InclusionFilter::isIncluded(m_config.inclusionPolicy, context))
166 return HrefSuppressReason::ExcludedByPolicy;
168 if (!hasExplicitUrl && node->parent() && node->parent()->isQmlType()
169 && node->parent()->isAbstract()) {
170 const QmlTypeNode *qmlContext = m_config.qmlTypeContextFn
171 ? m_config.qmlTypeContextFn() :
nullptr;
173 if (qmlContext->inherits(node->parent())) {
174 fn = fileName(qmlContext);
175 }
else if (node->parent()->isInternal() && !m_config.context->noLinkErrors) {
176 node->doc().location().warning(
177 u"Cannot link to property in internal type '%1'"_s
178 .arg(node->parent()->name()));
179 return HrefSuppressReason::InternalAbstractQml;
186 if (!node->isPageNode() || node->isPropertyGroup()) {
187 QString ref = anchorForNode(node);
188 if (relative && fn == fileName(relative) && ref == anchorForNode(relative))
189 return HrefSuppressReason::SameFileAnchor;
195 if (relative && (node != relative) && !node->isExternalPage()
196 && (node->isIndexNode() || node->tree() != relative->tree())) {
197 link.prepend(crossModulePrefix(node, relative));
204
205
206
207
208
209
210
211
212
213
214
215
216
217QString HrefResolver::crossModulePrefix(
const Node *target,
const Node *source)
const
219 const QString &sourceDir = m_config.context->outputDir.path();
220 const QString currentSegment =
'/'_L1 + source->tree()->physicalModuleName() +
'/'_L1;
221 const qsizetype moduleIndex = sourceDir.indexOf(currentSegment);
225 const QString targetSegment =
'/'_L1 + target->tree()->physicalModuleName() +
'/'_L1;
226 QString targetDir = sourceDir;
227 targetDir.replace(moduleIndex, currentSegment.size(), targetSegment);
229 const QString relPath = QDir(sourceDir).relativeFilePath(targetDir);
230 if (relPath.isEmpty() || relPath ==
"."_L1)
232 return relPath +
'/'_L1;