4#ifdef QDOC_TEMPLATE_GENERATOR_ENABLED
6#include "linkresolver.h"
9#include "hrefresolver.h"
10#include "ir/contentblock.h"
13#include "qdocdatabase.h"
14#include "qdoclogging.h"
16using namespace Qt::Literals::StringLiterals;
20static Genus genusFromString(
const QString &s)
30 return Genus::DontCare;
33static bool isBrokenAutolink(
const IR::InlineContent &inline_)
35 return inline_.type == IR::InlineType::Link
36 && inline_.link.has_value()
37 && inline_.link->state == IR::LinkState::Broken
38 && inline_.link->origin == IR::LinkOrigin::Auto;
42
43
44
45
46LinkResolver::LinkResolver(QDocDatabase *qdb,
const HrefResolver &hrefResolver,
47 const LinkResolverConfig &config)
48 : m_qdb(qdb), m_hrefResolver(hrefResolver), m_config(config)
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70void LinkResolver::resolve(QList<IR::ContentBlock> &blocks, Context context)
72 if (!context.semantic)
73 context.semantic = context.page;
74 for (
auto &block : blocks)
75 resolveBlock(block, context);
79
80
81
82void LinkResolver::resolveBlock(IR::ContentBlock &block,
const Context &context)
84 resolveInlines(block.inlineContent, context);
85 for (
auto &child : block.children)
86 resolveBlock(child, context);
90
91
92
93
94void LinkResolver::resolveInlines(QList<IR::InlineContent> &inlines,
const Context &context)
96 for (
auto &inline_ : inlines) {
97 if (inline_.type == IR::InlineType::Link && !inline_.href.isEmpty())
98 resolveLink(inline_, context);
100 if (isBrokenAutolink(inline_)) {
101 qCDebug(lcQdoc) <<
"Autolink degraded to text:" << inline_.href;
102 inline_.type = IR::InlineType::Text;
103 inline_.text = inline_.plainText();
104 inline_.href.clear();
105 inline_.children.clear();
106 inline_.link.reset();
107 inline_.attributes = QJsonObject();
111 if (!inline_.children.isEmpty())
112 resolveInlines(inline_.children, context);
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141void LinkResolver::resolveLink(IR::InlineContent &link,
const Context &context)
143 Q_ASSERT(link.link.has_value());
145 if (link.link->state != IR::LinkState::Unresolved)
148 const QString &target = link.href;
151 if (target.startsWith(
"http:"_L1) || target.startsWith(
"https:"_L1)
152 || target.startsWith(
"ftp:"_L1) || target.startsWith(
"file:"_L1)
153 || target.startsWith(
"mailto:"_L1)) {
154 link.link->state = IR::LinkState::External;
160 if (target.startsWith(
'#'_L1)) {
161 link.link->state = IR::LinkState::Resolved;
167 const Genus genus = genusFromString(
168 link.attributes.value(
"linkGenus"_L1).toString());
169 const QString &moduleName =
170 link.attributes.value(
"linkModule"_L1).toString();
172 const Node *targetNode =
173 m_qdb->findNodeForTarget(target, context.semantic, genus, moduleName, &ref);
176 const auto reportAt = [&](
const QString &message) {
177 if (link.link->sourceLocation) {
178 Location loc(link.link->sourceLocation->filePath);
179 loc.setLineNo(link.link->sourceLocation->lineNo);
180 loc.warning(message);
181 }
else if (context.semantic) {
182 context.semantic->doc().location().warning(message);
185 if (link.link->origin == IR::LinkOrigin::Auto) {
186 if (m_config.autolinkErrors)
187 reportAt(u"Can't autolink to '%1'"_s.arg(target));
189 if (!m_config.noLinkErrors)
190 reportAt(u"Can't link to '%1'"_s.arg(target));
193 link.link->state = IR::LinkState::Broken;
201 if (link.link->origin == IR::LinkOrigin::Auto
202 && targetNode->isDeprecated() && context.semantic
203 && context.semantic->parent() != targetNode && !context.semantic->isDeprecated()) {
205 link.link->state = IR::LinkState::Suppressed;
217 QString url = targetNode->url();
218 const bool isCrossTreeIndexLoaded = context.page && !targetNode->isExternalPage()
219 && (targetNode->isIndexNode() || targetNode->tree() != context.page->tree());
220 if (url.isNull() || (isCrossTreeIndexLoaded && !url.isEmpty())) {
221 auto result = m_hrefResolver.hrefForNode(targetNode, context.page);
222 if (std::get_if<HrefSuppressReason>(&result)) {
224 link.link->state = IR::LinkState::Suppressed;
227 url = std::get<QString>(std::move(result));
228 }
else if (url.isEmpty()) {
230 link.link->state = IR::LinkState::Ignored;
237 if (!ref.isEmpty()) {
238 const qsizetype hashIndex = url.lastIndexOf(u'#');
240 url.truncate(hashIndex);
245 link.link->state = IR::LinkState::Resolved;