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// own header
17#include "linkifytext.h"
18
19// other includes
20#include "conceptdef.h"
21#include "filedef.h"
22#include "memberdef.h"
23#include "regex.h"
24#include "symbolresolver.h"
25#include "trace.h"
26#include "util.h"
27
28void linkifyText(const TextGeneratorIntf &out, const DString &text,
29 const LinkifyTextOptions &options)
30{
31 const Definition *scope = options.scope();
32 const FileDef *fileScope = options.fileScope();
33 const Definition *self = options.self();
34 AUTO_TRACE("scope={} fileScope={} text={} autoBreak={} external={} keepSpaces={} indentLevel={}",
35 scope?scope->name():"",fileScope?fileScope->name():"",
36 text,options.autoBreak(),options.external(),options.keepSpaces(),options.indentLevel());
37 if (text.empty()) return;
38
39 //printf("linkify='%s'\n",qPrint(text));
40 std::string_view txtStr=text.view();
41 size_t strLen = txtStr.length();
42 if (strLen==0) return;
43
44 static const reg::Ex regExp(R"((::)?\a[\w~!\\.:$"]*)");
45 reg::Iterator it(txtStr,regExp);
47
48 //printf("linkifyText scope=%s fileScope=%s strtxt=%s strlen=%zu external=%d\n",
49 // scope ? qPrint(scope->name()):"<none>",
50 // fileScope ? qPrint(fileScope->name()) : "<none>",
51 // qPrint(txtStr),strLen,options.external());
52 size_t index=0;
53 size_t skipIndex=0;
54 size_t floatingIndex=0;
55 for (; it!=end ; ++it) // for each word from the text string
56 {
57 const auto &match = *it;
58 size_t newIndex = match.position();
59 size_t matchLen = match.length();
60 floatingIndex+=newIndex-skipIndex+matchLen;
61 if (newIndex>0 && txtStr.at(newIndex-1)=='0') // ignore hex numbers (match x00 in 0x00)
62 {
63 std::string_view part = txtStr.substr(skipIndex,newIndex+matchLen-skipIndex);
64 out.writeString(part,options.keepSpaces());
65 skipIndex=index=newIndex+matchLen;
66 continue;
67 }
68
69 // add non-word part to the result
70 bool insideString=false;
71 for (size_t i=index;i<newIndex;i++)
72 {
73 if (txtStr.at(i)=='"') insideString=!insideString;
74 if (txtStr.at(i)=='\\') i++; // skip next character it is escaped
75 }
76
77 //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,options.autoBreak());
78 if (strLen>options.breakThreshold()+5 && floatingIndex>options.breakThreshold() &&
79 options.autoBreak()) // try to insert a split point
80 {
81 std::string_view splitText = txtStr.substr(skipIndex,newIndex-skipIndex);
82 size_t splitLength = splitText.length();
83 size_t offset=1;
84 size_t i = splitText.find(',');
85 if (i==std::string::npos) { i=splitText.find('<'); if (i!=std::string::npos) offset=0; }
86 if (i==std::string::npos) { i=splitText.find("||"); if (i!=std::string::npos) offset=2; }
87 if (i==std::string::npos) { i=splitText.find("&&"); if (i!=std::string::npos) offset=2; }
88 if (i==std::string::npos) { i=splitText.find(">>"); if (i!=std::string::npos) offset=2; }
89 if (i==std::string::npos) i=splitText.find('>');
90 if (i==std::string::npos) i=splitText.find(' ');
91 //printf("splitText=[%s] len=%d i=%d offset=%d\n",qPrint(splitText),splitLength,i,offset);
92 if (i!=std::string::npos) // add a link-break at i in case of Html output
93 {
94 std::string_view part1 = splitText.substr(0,i+offset);
95 out.writeString(part1,options.keepSpaces());
96 out.writeBreak(options.indentLevel()==0 ? 0 : options.indentLevel()+1);
97 std::string_view part2 = splitText.substr(i+offset);
98 out.writeString(part2,options.keepSpaces());
99 floatingIndex=splitLength-i-offset+matchLen;
100 }
101 else
102 {
103 out.writeString(splitText,options.keepSpaces());
104 }
105 }
106 else
107 {
108 //ol.docify(txtStr.mid(skipIndex,newIndex-skipIndex));
109 std::string_view part = txtStr.substr(skipIndex,newIndex-skipIndex);
110 out.writeString(part,options.keepSpaces());
111 }
112 // get word from string
113 std::string_view word=txtStr.substr(newIndex,matchLen);
114 DString matchWord = substitute(substitute(word,"\\","::"),".","::");
115 bool found=false;
116 // check for argument name
117 if (options.argumentList())
118 {
119 for (auto it1 = options.argumentList()->begin(); it1!=options.argumentList()->end(); ++it1)
120 {
121 if (it1->name == matchWord)
122 {
123 out.writeString(matchWord.data(),options.keepSpaces());
124 found = true;
125 break;
126 }
127 }
128 }
129 //printf("linkifyText word=%s matchWord=%s scope=%s\n",
130 // qPrint(word),qPrint(matchWord),scope ? qPrint(scope->name()) : "<none>");
131 if (!insideString)
132 {
133 const ClassDef *cd=nullptr;
134 const ConceptDef *cnd=nullptr;
135 const Definition *d=nullptr;
136 //printf("** Match word '%s'\n",qPrint(matchWord));
137
138 SymbolResolver resolver(fileScope);
139 cd=resolver.resolveClass(scope,matchWord);
140 const MemberDef *typeDef = resolver.getTypedef();
141 if (typeDef) // First look at typedef then class, see bug 584184.
142 {
143 if (options.external() ? typeDef->isLinkable() : typeDef->isLinkableInProject())
144 {
145 if (typeDef->getOuterScope()!=self)
146 {
147 //printf("Found typedef %s word='%s'\n",qPrint(typeDef->name()),qPrint(word));
148 out.writeLink(typeDef->getReference(),
149 typeDef->getOutputFileBase(),
150 typeDef->anchor(),
151 word);
152 found=true;
153 }
154 }
155 }
156 auto writeCompoundName = [&](const auto *cd_) {
157 if (options.external() ? cd_->isLinkable() : cd_->isLinkableInProject())
158 {
159 if (self==nullptr || cd_->qualifiedName()!=self->qualifiedName())
160 {
161 //printf("Found compound %s word='%s'\n",qPrint(cd->name()),qPrint(word));
162 out.writeLink(cd_->getReference(),cd_->getOutputFileBase(),cd_->anchor(),word);
163 found=true;
164 }
165 }
166 };
167
168 if (found)
169 {
170 //printf(" -> skip\n");
171 }
172 else if ((cd=getClass(matchWord)))
173 {
174 writeCompoundName(cd);
175 }
176 else if ((cd=getClass(matchWord+"-p"))) // search for Obj-C protocols as well
177 {
178 writeCompoundName(cd);
179 }
180 else if ((cnd=getConcept(matchWord))) // search for concepts
181 {
182 writeCompoundName(cnd);
183 }
184 else if ((d=resolver.resolveSymbol(scope,matchWord)))
185 {
186 writeCompoundName(d);
187 }
188 else
189 {
190 //printf(" -> nothing\n");
191 }
192
193 size_t m = matchWord.rfind("::");
194 DString scopeName;
195 if (scope &&
198 )
199 )
200 {
201 scopeName=scope->name();
202 }
203 else if (m!=DString::npos)
204 {
205 scopeName = matchWord.left(m);
206 matchWord = matchWord.mid(m+2);
207 }
208
209 //printf("ScopeName=%s\n",qPrint(scopeName));
210 //if (!found) printf("Trying to link '%s' in '%s'\n",qPrint(word),qPrint(scopeName));
211 if (!found)
212 {
213 GetDefInput input(scopeName,matchWord,DString());
214 GetDefResult result = getDefs(input);
215 if (result.found && result.md &&
216 (options.external() ? result.md->isLinkable() : result.md->isLinkableInProject())
217 )
218 {
219 //printf("Found ref scope=%s\n",d ? qPrint(d->name()) : "<global>");
220 //ol.writeObjectLink(d->getReference(),d->getOutputFileBase(),
221 // md->anchor(),word);
222 if (result.md!=self && (self==nullptr || result.md->name()!=self->name()))
223 // name check is needed for overloaded members, where getDefs just returns one
224 {
225 /* in case of Fortran scope and the variable is a non Fortran variable: don't link,
226 * see also getLink in fortrancode.l
227 */
228 if (!(scope &&
229 (scope->getLanguage() == SrcLangExt::Fortran) &&
230 result.md->isVariable() &&
231 (result.md->getLanguage() != SrcLangExt::Fortran)
232 )
233 )
234 {
235 //printf("found symbol %s word='%s'\n",qPrint(result.md->name()),qPrint(word));
236 out.writeLink(result.md->getReference(),result.md->getOutputFileBase(),
237 result.md->anchor(),word);
238 found=true;
239 }
240 }
241 }
242 }
243 }
244
245 if (!found) // add word to the result
246 {
247 out.writeString(word,options.keepSpaces());
248 }
249 // set next start point in the string
250 //printf("index=%d/%d\n",index,txtStr.length());
251 skipIndex=index=newIndex+matchLen;
252 }
253 // add last part of the string to the result.
254 std::string_view lastPart = txtStr.substr(skipIndex);
255 out.writeString(lastPart,options.keepSpaces());
256}
iterator end()
Definition arguments.h:95
iterator begin()
Definition arguments.h:94
A abstract class representing of a compound symbol.
Definition classdef.h:100
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:244
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:318
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
std::string_view view() const
Definition dstring.h:162
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:178
DString left(size_t len) const
Definition dstring.h:306
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:157
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:97
A model of a class/file/namespace member symbol.
Definition memberdef.h:45
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:30
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:181
#define AUTO_TRACE(...)
Definition docnode.cpp:53
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:485
void linkifyText(const TextGeneratorIntf &out, const DString &text, const LinkifyTextOptions &options)
const MemberDef * md
Definition util.h:95
bool found
Definition util.h:94
int indentLevel() const
Definition linkifytext.h:54
bool external() const
Definition linkifytext.h:52
const ArgumentList * argumentList() const
Definition linkifytext.h:50
const Definition * scope() const
Definition linkifytext.h:47
const Definition * self() const
Definition linkifytext.h:49
size_t breakThreshold() const
Definition linkifytext.h:55
bool keepSpaces() const
Definition linkifytext.h:53
const FileDef * fileScope() const
Definition linkifytext.h:48
bool autoBreak() const
Definition linkifytext.h:51
GetDefResult getDefs(const GetDefInput &input)
Definition util.cpp:1866
A bunch of utility functions.