Doxygen
Loading...
Searching...
No Matches
linkifytext.cpp
Go to the documentation of this file.
1/*****************************************************************************
2 *
3 * Copyright (C) 1997-2026 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include "linkifytext.h"
17#include "trace.h"
18#include "regex.h"
19#include "symbolresolver.h"
20#include "filedef.h"
21#include "conceptdef.h"
22#include "memberdef.h"
23#include "util.h"
24
25void linkifyText(const TextGeneratorIntf &out, const DString &text,
26 const LinkifyTextOptions &options)
27{
28 const Definition *scope = options.scope();
29 const FileDef *fileScope = options.fileScope();
30 const Definition *self = options.self();
31 AUTO_TRACE("scope={} fileScope={} text={} autoBreak={} external={} keepSpaces={} indentLevel={}",
32 scope?scope->name():"",fileScope?fileScope->name():"",
33 text,options.autoBreak(),options.external(),options.keepSpaces(),options.indentLevel());
34 if (text.empty()) return;
35
36 //printf("linkify='%s'\n",qPrint(text));
37 std::string_view txtStr=text.view();
38 size_t strLen = txtStr.length();
39 if (strLen==0) return;
40
41 static const reg::Ex regExp(R"((::)?\a[\w~!\\.:$"]*)");
42 reg::Iterator it(txtStr,regExp);
44
45 //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%zu external=%d\n",
46 // scope ? qPrint(scope->name()):"<none>",
47 // fileScope ? qPrint(fileScope->name()) : "<none>",
48 // qPrint(txtStr),strLen,options.external());
49 size_t index=0;
50 size_t skipIndex=0;
51 size_t floatingIndex=0;
52 for (; it!=end ; ++it) // for each word from the text string
53 {
54 const auto &match = *it;
55 size_t newIndex = match.position();
56 size_t matchLen = match.length();
57 floatingIndex+=newIndex-skipIndex+matchLen;
58 if (newIndex>0 && txtStr.at(newIndex-1)=='0') // ignore hex numbers (match x00 in 0x00)
59 {
60 std::string_view part = txtStr.substr(skipIndex,newIndex+matchLen-skipIndex);
61 out.writeString(part,options.keepSpaces());
62 skipIndex=index=newIndex+matchLen;
63 continue;
64 }
65
66 // add non-word part to the result
67 bool insideString=false;
68 for (size_t i=index;i<newIndex;i++)
69 {
70 if (txtStr.at(i)=='"') insideString=!insideString;
71 if (txtStr.at(i)=='\\') i++; // skip next character it is escaped
72 }
73
74 //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,options.autoBreak());
75 if (strLen>options.breakThreshold()+5 && floatingIndex>options.breakThreshold() &&
76 options.autoBreak()) // try to insert a split point
77 {
78 std::string_view splitText = txtStr.substr(skipIndex,newIndex-skipIndex);
79 size_t splitLength = splitText.length();
80 size_t offset=1;
81 size_t i = splitText.find(',');
82 if (i==std::string::npos) { i=splitText.find('<'); if (i!=std::string::npos) offset=0; }
83 if (i==std::string::npos) { i=splitText.find("||"); if (i!=std::string::npos) offset=2; }
84 if (i==std::string::npos) { i=splitText.find("&&"); if (i!=std::string::npos) offset=2; }
85 if (i==std::string::npos) { i=splitText.find(">>"); if (i!=std::string::npos) offset=2; }
86 if (i==std::string::npos) i=splitText.find('>');
87 if (i==std::string::npos) i=splitText.find(' ');
88 //printf("splitText=[%s] len=%d i=%d offset=%d\n",qPrint(splitText),splitLength,i,offset);
89 if (i!=std::string::npos) // add a link-break at i in case of Html output
90 {
91 std::string_view part1 = splitText.substr(0,i+offset);
92 out.writeString(part1,options.keepSpaces());
93 out.writeBreak(options.indentLevel()==0 ? 0 : options.indentLevel()+1);
94 std::string_view part2 = splitText.substr(i+offset);
95 out.writeString(part2,options.keepSpaces());
96 floatingIndex=splitLength-i-offset+matchLen;
97 }
98 else
99 {
100 out.writeString(splitText,options.keepSpaces());
101 }
102 }
103 else
104 {
105 //ol.docify(txtStr.mid(skipIndex,newIndex-skipIndex));
106 std::string_view part = txtStr.substr(skipIndex,newIndex-skipIndex);
107 out.writeString(part,options.keepSpaces());
108 }
109 // get word from string
110 std::string_view word=txtStr.substr(newIndex,matchLen);
111 DString matchWord = substitute(substitute(word,"\\","::"),".","::");
112 bool found=false;
113 // check for argument name
114 if (options.argumentList())
115 {
116 for (auto it1 = options.argumentList()->begin(); it1!=options.argumentList()->end(); ++it1)
117 {
118 if (it1->name == matchWord)
119 {
120 out.writeString(matchWord.data(),options.keepSpaces());
121 found = true;
122 break;
123 }
124 }
125 }
126 //printf("linkifyText word=%s matchWord=%s scope=%s\n",
127 // qPrint(word),qPrint(matchWord),scope ? qPrint(scope->name()) : "<none>");
128 if (!insideString)
129 {
130 const ClassDef *cd=nullptr;
131 const ConceptDef *cnd=nullptr;
132 const Definition *d=nullptr;
133 //printf("** Match word '%s'\n",qPrint(matchWord));
134
135 SymbolResolver resolver(fileScope);
136 cd=resolver.resolveClass(scope,matchWord);
137 const MemberDef *typeDef = resolver.getTypedef();
138 if (typeDef) // First look at typedef then class, see bug 584184.
139 {
140 if (options.external() ? typeDef->isLinkable() : typeDef->isLinkableInProject())
141 {
142 if (typeDef->getOuterScope()!=self)
143 {
144 //printf("Found typedef %s word='%s'\n",qPrint(typeDef->name()),qPrint(word));
145 out.writeLink(typeDef->getReference(),
146 typeDef->getOutputFileBase(),
147 typeDef->anchor(),
148 word);
149 found=true;
150 }
151 }
152 }
153 auto writeCompoundName = [&](const auto *cd_) {
154 if (options.external() ? cd_->isLinkable() : cd_->isLinkableInProject())
155 {
156 if (self==nullptr || cd_->qualifiedName()!=self->qualifiedName())
157 {
158 //printf("Found compound %s word='%s'\n",qPrint(cd->name()),qPrint(word));
159 out.writeLink(cd_->getReference(),cd_->getOutputFileBase(),cd_->anchor(),word);
160 found=true;
161 }
162 }
163 };
164
165 if (found)
166 {
167 //printf(" -> skip\n");
168 }
169 else if ((cd=getClass(matchWord)))
170 {
171 writeCompoundName(cd);
172 }
173 else if ((cd=getClass(matchWord+"-p"))) // search for Obj-C protocols as well
174 {
175 writeCompoundName(cd);
176 }
177 else if ((cnd=getConcept(matchWord))) // search for concepts
178 {
179 writeCompoundName(cnd);
180 }
181 else if ((d=resolver.resolveSymbol(scope,matchWord)))
182 {
183 writeCompoundName(d);
184 }
185 else
186 {
187 //printf(" -> nothing\n");
188 }
189
190 size_t m = matchWord.rfind("::");
191 DString scopeName;
192 if (scope &&
195 )
196 )
197 {
198 scopeName=scope->name();
199 }
200 else if (m!=DString::npos)
201 {
202 scopeName = matchWord.left(m);
203 matchWord = matchWord.mid(m+2);
204 }
205
206 //printf("ScopeName=%s\n",qPrint(scopeName));
207 //if (!found) printf("Trying to link '%s' in '%s'\n",qPrint(word),qPrint(scopeName));
208 if (!found)
209 {
210 GetDefInput input(scopeName,matchWord,DString());
211 GetDefResult result = getDefs(input);
212 if (result.found && result.md &&
213 (options.external() ? result.md->isLinkable() : result.md->isLinkableInProject())
214 )
215 {
216 //printf("Found ref scope=%s\n",d ? qPrint(d->name()) : "<global>");
217 //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(),
218 // md->anchor(),word);
219 if (result.md!=self && (self==nullptr || result.md->name()!=self->name()))
220 // name check is needed for overloaded members, where getDefs just returns one
221 {
222 /* in case of Fortran scope and the variable is a non Fortran variable: don't link,
223 * see also getLink in fortrancode.l
224 */
225 if (!(scope &&
226 (scope->getLanguage() == SrcLangExt::Fortran) &&
227 result.md->isVariable() &&
228 (result.md->getLanguage() != SrcLangExt::Fortran)
229 )
230 )
231 {
232 //printf("found symbol %s word='%s'\n",qPrint(result.md->name()),qPrint(word));
233 out.writeLink(result.md->getReference(),result.md->getOutputFileBase(),
234 result.md->anchor(),word);
235 found=true;
236 }
237 }
238 }
239 }
240 }
241
242 if (!found) // add word to the result
243 {
244 out.writeString(word,options.keepSpaces());
245 }
246 // set next start point in the string
247 //printf("index=%d/%d\n",index,txtStr.length());
248 skipIndex=index=newIndex+matchLen;
249 }
250 // add last part of the string to the result.
251 std::string_view lastPart = txtStr.substr(skipIndex);
252 out.writeString(lastPart,options.keepSpaces());
253}
iterator end()
Definition arguments.h:94
iterator begin()
Definition arguments.h:93
A abstract class representing of a compound symbol.
Definition classdef.h:104
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:89
DString()=default
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:249
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:323
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:153
std::string_view view() const
Definition dstring.h:167
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:183
DString left(size_t len) const
Definition dstring.h:311
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:162
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual bool isLinkable() const =0
virtual DefType definitionType() const =0
virtual const DString & name() const =0
virtual bool isLinkableInProject() const =0
virtual DString qualifiedName() const =0
virtual DString anchor() const =0
virtual DString getReference() const =0
virtual Definition * getOuterScope() const =0
virtual DString getOutputFileBase() const =0
A model of a file symbol.
Definition filedef.h:99
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual bool isVariable() const =0
const ClassDef * resolveClass(const Definition *scope, const DString &name, bool maybeUnlinkable=false, bool mayBeHidden=false)
Find the class definition matching name within the scope set.
const Definition * resolveSymbol(const Definition *scope, const DString &name, const DString &args=DString(), bool checkCV=false, bool insideCode=false, bool onlyLinkable=false)
Find the symbool definition matching name within the scope set.
const MemberDef * getTypedef() const
In case a call to resolveClass() resolves to a type member (e.g. an enum) this method will return it.
Abstract interface for a hyperlinked text fragment.
Definition linkifytext.h:28
virtual void writeString(std::string_view, bool) const =0
virtual void writeBreak(int indent) const =0
virtual void writeLink(const DString &extRef, const DString &file, const DString &anchor, std::string_view text) const =0
ClassDef * getClass(const DString &n)
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:239
ConceptDef * getConcept(const DString &n)
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
#define AUTO_TRACE(...)
Definition docnode.cpp:48
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:476
void linkifyText(const TextGeneratorIntf &out, const DString &text, const LinkifyTextOptions &options)
const MemberDef * md
Definition util.h:82
bool found
Definition util.h:81
int indentLevel() const
Definition linkifytext.h:52
bool external() const
Definition linkifytext.h:50
const ArgumentList * argumentList() const
Definition linkifytext.h:48
const Definition * scope() const
Definition linkifytext.h:45
const Definition * self() const
Definition linkifytext.h:47
size_t breakThreshold() const
Definition linkifytext.h:53
bool keepSpaces() const
Definition linkifytext.h:51
const FileDef * fileScope() const
Definition linkifytext.h:46
bool autoBreak() const
Definition linkifytext.h:49
GetDefResult getDefs(const GetDefInput &input)
Definition util.cpp:2033
A bunch of utility functions.