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
60void LinkResolver::resolve(QList<IR::ContentBlock> &blocks,
const Node *relative)
62 for (
auto &block : blocks)
63 resolveBlock(block, relative);
67
68
69
70void LinkResolver::resolveBlock(IR::ContentBlock &block,
const Node *relative)
72 resolveInlines(block.inlineContent, relative);
73 for (
auto &child : block.children)
74 resolveBlock(child, relative);
78
79
80
81
82void LinkResolver::resolveInlines(QList<IR::InlineContent> &inlines,
const Node *relative)
84 for (
auto &inline_ : inlines) {
85 if (inline_.type == IR::InlineType::Link && !inline_.href.isEmpty())
86 resolveLink(inline_, relative);
88 if (isBrokenAutolink(inline_)) {
89 qCDebug(lcQdoc) <<
"Autolink degraded to text:" << inline_.href;
90 inline_.type = IR::InlineType::Text;
91 inline_.text = inline_.plainText();
93 inline_.children.clear();
95 inline_.attributes = QJsonObject();
99 if (!inline_.children.isEmpty())
100 resolveInlines(inline_.children, relative);
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126void LinkResolver::resolveLink(IR::InlineContent &link,
const Node *relative)
128 Q_ASSERT(link.link.has_value());
130 if (link.link->state != IR::LinkState::Unresolved)
133 const QString &target = link.href;
136 if (target.startsWith(
"http:"_L1) || target.startsWith(
"https:"_L1)
137 || target.startsWith(
"ftp:"_L1) || target.startsWith(
"file:"_L1)
138 || target.startsWith(
"mailto:"_L1)) {
139 link.link->state = IR::LinkState::External;
145 if (target.startsWith(
'#'_L1)) {
146 link.link->state = IR::LinkState::Resolved;
152 const Genus genus = genusFromString(
153 link.attributes.value(
"linkGenus"_L1).toString());
154 const QString &moduleName =
155 link.attributes.value(
"linkModule"_L1).toString();
157 const Node *targetNode =
158 m_qdb->findNodeForTarget(target, relative, genus, moduleName, &ref);
161 const auto reportAt = [&](
const QString &message) {
162 if (link.link->sourceLocation) {
163 Location loc(link.link->sourceLocation->filePath);
164 loc.setLineNo(link.link->sourceLocation->lineNo);
165 loc.warning(message);
166 }
else if (relative) {
167 relative->doc().location().warning(message);
170 if (link.link->origin == IR::LinkOrigin::Auto) {
171 if (m_config.autolinkErrors)
172 reportAt(u"Can't autolink to '%1'"_s.arg(target));
174 if (!m_config.noLinkErrors)
175 reportAt(u"Can't link to '%1'"_s.arg(target));
178 link.link->state = IR::LinkState::Broken;
187 if (targetNode->isDeprecated() && relative
188 && relative->parent() != targetNode && !relative->isDeprecated()) {
190 link.link->state = IR::LinkState::Suppressed;
201 QString url = targetNode->url();
202 const bool isCrossTreeIndexLoaded = relative && !targetNode->isExternalPage()
203 && (targetNode->isIndexNode() || targetNode->tree() != relative->tree());
204 if (url.isNull() || (isCrossTreeIndexLoaded && !url.isEmpty())) {
205 auto result = m_hrefResolver.hrefForNode(targetNode, relative);
206 if (std::get_if<HrefSuppressReason>(&result)) {
208 link.link->state = IR::LinkState::Suppressed;
211 url = std::get<QString>(std::move(result));
212 }
else if (url.isEmpty()) {
214 link.link->state = IR::LinkState::Ignored;
221 if (!ref.isEmpty()) {
222 const qsizetype hashIndex = url.lastIndexOf(u'#');
224 url.truncate(hashIndex);
229 link.link->state = IR::LinkState::Resolved;