Doxygen
Loading...
Searching...
No Matches
codefragment.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2023 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 <mutex>
17#include <unordered_map>
18#include <map>
19
20#include "codefragment.h"
21#include "util.h"
22#include "doxygen.h"
23#include "parserintf.h"
24#include "outputlist.h"
25#include "clangparser.h"
26#include "trace.h"
27#include "fileinfo.h"
28#include "filedef.h"
29#include "portable.h"
30#include "message.h"
31
33{
35 {
36 int indent=0;
37 std::string key;
38 std::vector<int> lines;
39 };
40
42 {
46 void findBlockMarkers();
48 std::map<int,BlockMarker> blocks;
49 std::map<std::string,const BlockMarker*> blocksById;
50 std::mutex mutex;
51 };
52
53 std::unordered_map<std::string,std::unique_ptr<FragmentInfo> > fragments;
54 std::mutex mutex;
55};
56
58{
59 AUTO_TRACE("findBlockMarkers() size={}",fileContents.size());
60 // give fileContents and a list of candidate [XYZ] labels with/without trim left flag (from commentscan?)
61 if (fileContents.length()==0) return;
62
63 // find the potential snippet blocks (can also be other array like stuff in the file)
64 const char *s=fileContents.data();
65 int lineNr=1;
66 char c=0;
67 const char *foundOpen=nullptr;
68 std::unordered_map<std::string,BlockMarker> candidates;
69 while ((c=*s))
70 {
71 if (c=='[')
72 {
73 foundOpen=s;
74 }
75 else if (foundOpen && c==']' && foundOpen+1<s) // non-empty [...] section
76 {
77 std::string key(foundOpen+1,s-foundOpen-1);
78 candidates[key].lines.push_back(lineNr);
79 }
80 else if (c=='\n')
81 {
82 foundOpen=nullptr;
83 lineNr++;
84 }
85 s++;
86 }
87
88 // Sort the valid snippet blocks by line number, Look for blocks that appears twice,
89 // where candidate has block id as key and the start and end line as value.
90 // Store the key in marker.key
91 for (auto &kv : candidates)
92 {
93 auto &marker = kv.second;
94 if (marker.lines.size()==2 && marker.lines[0]+1<=marker.lines[1]-1)
95 {
96 marker.key = kv.first;
97 int startLine = marker.lines[0];
98 blocks[startLine] = marker;
99 blocksById["["+kv.first+"]"] = &blocks[startLine];
100 }
101 }
102
103 // determine the shared indentation for each line in each block, and store it in marker.indent
104 s=fileContents.data();
105 static auto gotoLine = [](const char *startBuf, const char *startPos, int startLine, int targetLine) -> const char *
106 {
107 char cc=0;
108 if (targetLine<startLine)
109 {
110 //printf("gotoLine(pos=%p,start=%d,target=%d) backward\n",(void*)startPos,startLine,targetLine);
111 while (startLine>=targetLine && startPos>=startBuf && (cc=*startPos--)) { if (cc=='\n') startLine--; }
112 if (startPos>startBuf)
113 {
114 // given fragment:
115 // line1\n
116 // line2\n
117 // line3
118 // and targetLine==2 then startPos ends at character '1' of line 1 before we detect that startLine<targetLine,
119 // so we need to advance startPos with 2 to be at the start of line2, unless we are already at the first line.
120 startPos+=2;
121 }
122 //printf("result=[%s]\n",qPrint(QCString(startPos).left(20)));
123 }
124 else
125 {
126 //printf("gotoLine(pos=%p,start=%d,target=%d) forward\n",(void*)startPos,startLine,targetLine);
127 while (startLine<targetLine && (cc=*startPos++)) { if (cc=='\n') startLine++; }
128 //printf("result=[%s]\n",qPrint(QCString(startPos).left(20)));
129 }
130 return startPos;
131 };
132 static auto lineIndent = [](const char *&ss) -> int
133 {
134 int tabSize=Config_getInt(TAB_SIZE);
135 int col = 0;
136 char cc = 0;
137 while ((cc=*ss++))
138 {
139 if (cc==' ') col++;
140 else if (cc=='\t') col+=tabSize-(col%tabSize);
141 else
142 {
143 // goto end of the line
144 while ((cc=*ss++) && cc!='\n');
145 break;
146 }
147 }
148 return col;
149 };
150 lineNr=1;
151 const char *startBuf = s;
152 for (auto &kv : blocks)
153 {
154 auto &marker = kv.second;
155 s = gotoLine(startBuf,s,lineNr,marker.lines[0]+1);
156 lineNr=marker.lines[1];
157 const char *e = gotoLine(startBuf,s,marker.lines[0]+1,lineNr);
158
159 const char *ss = s;
160 int minIndent=100000;
161 while (ss<e)
162 {
163 int indent = lineIndent(ss);
164 if (indent<minIndent)
165 {
166 minIndent=indent;
167 if (minIndent==0) break;
168 }
169 }
170 marker.indent = minIndent;
171
172 AUTO_TRACE_ADD("found snippet key='{}' range=[{}..{}] indent={}",
173 marker.key,
174 marker.lines[0]+1,
175 marker.lines[1]-1,
176 marker.indent);
177 s=e;
178 }
179}
180
182{
183}
184
186
192
194{
195 AUTO_TRACE("file={}",file);
196 if (Portable::isAbsolutePath(file))
197 {
198 FileInfo fi(file.str());
199 if (fi.exists())
200 {
201 return fileToString(file,Config_getBool(FILTER_SOURCE_FILES));
202 }
203 }
204 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
205 for (const auto &s : examplePathList)
206 {
207 std::string absFileName = s+(Portable::pathSeparator()+file).str();
208 FileInfo fi(absFileName);
209 if (fi.exists())
210 {
211 return fileToString(QCString(absFileName),Config_getBool(FILTER_SOURCE_FILES));
212 }
213 }
214
215 // as a fallback we also look in the exampleNameDict
216 bool ambig=false;
218 if (fd)
219 {
220 if (ambig)
221 {
222 err("included file name '%s' is ambiguous.\nPossible candidates:\n%s\n",qPrint(file),
224 );
225 }
226 return fileToString(fd->absFilePath(),Config_getBool(FILTER_SOURCE_FILES));
227 }
228 else
229 {
230 err("included file %s is not found. Check your EXAMPLE_PATH\n",qPrint(file));
231 }
232 return QCString();
233}
234
235
237 const QCString & fileName,
238 const QCString & blockId,
239 const QCString & scopeName,
240 bool showLineNumbers,
241 bool trimLeft,
242 bool stripCodeComments
243 )
244{
245 AUTO_TRACE("CodeFragmentManager::parseCodeFragment({},blockId={},scopeName={},showLineNumber={},trimLeft={},stripCodeComments={}",
246 fileName, blockId, scopeName, showLineNumbers, trimLeft, stripCodeComments);
247 std::string fragmentKey=fileName.str()+":"+scopeName.str();
248 std::unordered_map< std::string,std::unique_ptr<Private::FragmentInfo> >::iterator it;
249 bool inserted = false;
250 {
251 // create new entry if it is not yet in the map
252 std::lock_guard lock(p->mutex);
253 it = p->fragments.find(fragmentKey);
254 if (it == p->fragments.end())
255 {
256 it = p->fragments.emplace(fragmentKey, std::make_unique<Private::FragmentInfo>()).first;
257 inserted = true;
258 AUTO_TRACE_ADD("new fragment");
259 }
260 }
261 // only lock the one item we are working with
262 auto &codeFragment = it->second;
263 std::lock_guard lock(codeFragment->mutex);
264 if (inserted) // new entry, need to parse the file and record the output and cache it
265 {
266 SrcLangExt langExt = getLanguageFromFileName(fileName);
267 FileInfo cfi( fileName.str() );
268 auto fd = createFileDef( cfi.dirPath(), cfi.fileName() );
269 auto intf = Doxygen::parserManager->getCodeParser(getFileNameExtension(fileName));
270 intf->resetCodeParserState();
271 bool filterSourceFiles = Config_getBool(FILTER_SOURCE_FILES);
272 bool needs2PassParsing =
273 Doxygen::parseSourcesNeeded && // we need to parse (filtered) sources for cross-references
274 !filterSourceFiles && // but user wants to show sources as-is
275 !getFileFilter(fileName,TRUE).isEmpty(); // and there is a filter used while parsing
276 codeFragment->fileContents = readTextFileByName(fileName);
277 //printf("fileContents=[%s]\n",qPrint(codeFragment->fileContents));
278 if (needs2PassParsing)
279 {
280 OutputCodeList devNullList;
281 devNullList.add<DevNullCodeGenerator>();
282 intf->parseCode(devNullList,
283 scopeName,
284 codeFragment->fileContents,
285 langExt,
286 stripCodeComments, // actually not important here
287 false,
288 QCString()
289 );
290 }
291 codeFragment->findBlockMarkers();
292 if (codeFragment->fileContents.length()>0) // parse the normal version
293 {
294 intf->parseCode(codeFragment->recorderCodeList,
295 scopeName,
296 codeFragment->fileContents,
297 langExt, // lang
298 false, // strip code comments (overruled before replaying)
299 false, // isExampleBlock
300 QCString(), // exampleName
301 fd.get(), // fileDef
302 -1, // startLine
303 -1, // endLine
304 true, // inlineFragment
305 nullptr, // memberDef
306 true, // showLineNumbers
307 nullptr, // searchCtx
308 false // collectXRefs
309 );
310 }
311 }
312 // use the recorded OutputCodeList from the cache to output a pre-recorded fragment
313 auto blockKv = codeFragment->blocksById.find(blockId.str());
314 if (blockKv != codeFragment->blocksById.end())
315 {
316 const auto &marker = blockKv->second;
317 int startLine = marker->lines[0];
318 int endLine = marker->lines[1];
319 int indent = marker->indent;
320 AUTO_TRACE_ADD("replay(start={},end={},indent={}) fileContentsTrimLeft.empty()={}",
321 startLine,endLine,indent,codeFragment->fileContentsTrimLeft.isEmpty());
322 auto recorder = codeFragment->recorderCodeList.get<OutputCodeRecorder>(OutputType::Recorder);
323 recorder->replay(codeOutList,
324 startLine+1,
325 endLine,
326 showLineNumbers,
327 stripCodeComments,
328 trimLeft ? static_cast<size_t>(indent) : 0
329 );
330 }
331 else
332 {
333 AUTO_TRACE_ADD("block not found!");
334 }
335}
336
void parseCodeFragment(OutputCodeList &codeOutList, const QCString &fileName, const QCString &blockId, const QCString &scopeName, bool showLineNumbers, bool trimLeft, bool stripCodeComments)
static CodeFragmentManager & instance()
std::unique_ptr< Private > p
Class implementing OutputCodeIntf by throwing away everything.
Definition devnullgen.h:21
static bool parseSourcesNeeded
Definition doxygen.h:123
static ParserManager * parserManager
Definition doxygen.h:131
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:103
A model of a file symbol.
Definition filedef.h:99
virtual QCString absFilePath() const =0
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
bool exists() const
Definition fileinfo.cpp:30
std::string fileName() const
Definition fileinfo.cpp:118
std::string dirPath(bool absPath=true) const
Definition fileinfo.cpp:137
Class representing a list of different code generators.
Definition outputlist.h:164
void add(OutputCodeIntfPtr &&p)
Definition outputlist.h:194
Implementation that allows capturing calls made to the code interface to later invoke them on a Outpu...
Definition outputlist.h:112
void replay(OutputCodeList &ol, int startLine, int endLine, bool showLineNumbers, bool stripComment, size_t stripIndentAmount)
This is an alternative implementation of QCString.
Definition qcstring.h:101
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:150
const std::string & str() const
Definition qcstring.h:537
static QCString readTextFileByName(const QCString &file)
#define Config_getInt(name)
Definition config.h:34
#define Config_getList(name)
Definition config.h:38
#define Config_getBool(name)
Definition config.h:33
std::vector< std::string > StringVector
Definition containers.h:33
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:47
#define AUTO_TRACE(...)
Definition docnode.cpp:46
std::unique_ptr< FileDef > createFileDef(const QCString &p, const QCString &n, const QCString &ref, const QCString &dn)
Definition filedef.cpp:267
#define err(fmt,...)
Definition message.h:84
bool isAbsolutePath(const QCString &fileName)
Definition portable.cpp:514
QCString pathSeparator()
Definition portable.cpp:391
Portable versions of functions that are platform dependent.
const char * qPrint(const char *s)
Definition qcstring.h:672
#define TRUE
Definition qcstring.h:37
std::map< std::string, const BlockMarker * > blocksById
std::map< int, BlockMarker > blocks
std::unordered_map< std::string, std::unique_ptr< FragmentInfo > > fragments
SrcLangExt
Language as given by extension.
Definition types.h:42
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5549
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3388
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1414
QCString getFileFilter(const QCString &name, bool isSourceCode)
Definition util.cpp:1342
QCString getFileNameExtension(const QCString &fn)
Definition util.cpp:5591
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:3262
A bunch of utility functions.