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 orgCol) -> 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 if (cc=='\n') return orgCol;
142 else
143 {
144 // goto end of the line
145 while ((cc=*ss++) && cc!='\n');
146 return col;
147 }
148 }
149 return orgCol;
150 };
151 lineNr=1;
152 const char *startBuf = s;
153 for (auto &kv : blocks)
154 {
155 auto &marker = kv.second;
156 s = gotoLine(startBuf,s,lineNr,marker.lines[0]+1);
157 lineNr=marker.lines[1];
158 const char *e = gotoLine(startBuf,s,marker.lines[0]+1,lineNr);
159
160 const char *ss = s;
161 int minIndent=100000;
162 int indent = minIndent;
163 while (ss<e)
164 {
165 indent = lineIndent(ss, indent);
166 if (indent<minIndent)
167 {
168 minIndent=indent;
169 if (minIndent==0) break;
170 }
171 }
172 marker.indent = minIndent;
173
174 AUTO_TRACE_ADD("found snippet key='{}' range=[{}..{}] indent={}",
175 marker.key,
176 marker.lines[0]+1,
177 marker.lines[1]-1,
178 marker.indent);
179 s=e;
180 }
181}
182
184{
185}
186
188
194
196{
197 AUTO_TRACE("file={}",file);
198 if (Portable::isAbsolutePath(file))
199 {
200 FileInfo fi(file.str());
201 if (fi.exists())
202 {
203 return fileToString(file,Config_getBool(FILTER_SOURCE_FILES));
204 }
205 }
206 const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
207 for (const auto &s : examplePathList)
208 {
209 std::string absFileName = s+(Portable::pathSeparator()+file).str();
210 FileInfo fi(absFileName);
211 if (fi.exists())
212 {
213 return fileToString(QCString(absFileName),Config_getBool(FILTER_SOURCE_FILES));
214 }
215 }
216
217 // as a fallback we also look in the exampleNameDict
218 bool ambig=false;
220 if (fd)
221 {
222 if (ambig)
223 {
224 err("included file name '{}' is ambiguous.\nPossible candidates:\n{}\n",file,
226 );
227 }
228 return fileToString(fd->absFilePath(),Config_getBool(FILTER_SOURCE_FILES));
229 }
230 else
231 {
232 err("included file {} is not found. Check your EXAMPLE_PATH\n",file);
233 }
234 return QCString();
235}
236
237
239 const QCString & fileName,
240 const QCString & blockId,
241 const QCString & scopeName,
242 bool showLineNumbers,
243 bool trimLeft,
244 bool stripCodeComments
245 )
246{
247 AUTO_TRACE("CodeFragmentManager::parseCodeFragment({},blockId={},scopeName={},showLineNumber={},trimLeft={},stripCodeComments={}",
248 fileName, blockId, scopeName, showLineNumbers, trimLeft, stripCodeComments);
249 std::string fragmentKey=fileName.str()+":"+scopeName.str();
250 std::unordered_map< std::string,std::unique_ptr<Private::FragmentInfo> >::iterator it;
251 bool inserted = false;
252 {
253 // create new entry if it is not yet in the map
254 std::lock_guard lock(p->mutex);
255 it = p->fragments.find(fragmentKey);
256 if (it == p->fragments.end())
257 {
258 it = p->fragments.emplace(fragmentKey, std::make_unique<Private::FragmentInfo>()).first;
259 inserted = true;
260 AUTO_TRACE_ADD("new fragment");
261 }
262 }
263 // only lock the one item we are working with
264 auto &codeFragment = it->second;
265 std::lock_guard lock(codeFragment->mutex);
266 if (inserted) // new entry, need to parse the file and record the output and cache it
267 {
268 SrcLangExt langExt = getLanguageFromFileName(fileName);
269 FileInfo cfi( fileName.str() );
270 auto fd = createFileDef( cfi.dirPath(), cfi.fileName() );
271 auto intf = Doxygen::parserManager->getCodeParser(getFileNameExtension(fileName));
272 intf->resetCodeParserState();
273 bool filterSourceFiles = Config_getBool(FILTER_SOURCE_FILES);
274 bool needs2PassParsing =
275 Doxygen::parseSourcesNeeded && // we need to parse (filtered) sources for cross-references
276 !filterSourceFiles && // but user wants to show sources as-is
277 !getFileFilter(fileName,TRUE).isEmpty(); // and there is a filter used while parsing
278 codeFragment->fileContents = readTextFileByName(fileName);
279 //printf("fileContents=[%s]\n",qPrint(codeFragment->fileContents));
280 if (needs2PassParsing)
281 {
282 OutputCodeList devNullList;
283 devNullList.add<DevNullCodeGenerator>();
284 intf->parseCode(devNullList,
285 scopeName,
286 codeFragment->fileContents,
287 langExt,
288 stripCodeComments, // actually not important here
289 false,
290 QCString()
291 );
292 }
293 codeFragment->findBlockMarkers();
294 if (codeFragment->fileContents.length()>0) // parse the normal version
295 {
296 intf->parseCode(codeFragment->recorderCodeList,
297 scopeName,
298 codeFragment->fileContents,
299 langExt, // lang
300 false, // strip code comments (overruled before replaying)
301 false, // isExampleBlock
302 QCString(), // exampleName
303 fd.get(), // fileDef
304 -1, // startLine
305 -1, // endLine
306 true, // inlineFragment
307 nullptr, // memberDef
308 true, // showLineNumbers
309 nullptr, // searchCtx
310 false // collectXRefs
311 );
312 }
313 }
314 // use the recorded OutputCodeList from the cache to output a pre-recorded fragment
315 auto blockKv = codeFragment->blocksById.find(blockId.str());
316 if (blockKv != codeFragment->blocksById.end())
317 {
318 const auto &marker = blockKv->second;
319 int startLine = marker->lines[0];
320 int endLine = marker->lines[1];
321 int indent = marker->indent;
322 AUTO_TRACE_ADD("replay(start={},end={},indent={}) fileContentsTrimLeft.empty()={}",
323 startLine,endLine,indent,codeFragment->fileContentsTrimLeft.isEmpty());
324 auto recorder = codeFragment->recorderCodeList.get<OutputCodeRecorder>(OutputType::Recorder);
325 recorder->replay(codeOutList,
326 startLine+1,
327 endLine,
328 showLineNumbers,
329 stripCodeComments,
330 trimLeft ? static_cast<size_t>(indent) : 0
331 );
332 }
333 else
334 {
335 AUTO_TRACE_ADD("block not found!");
336 }
337}
338
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:127
bool isAbsolutePath(const QCString &fileName)
Definition portable.cpp:514
QCString pathSeparator()
Definition portable.cpp:391
Portable versions of functions that are platform dependent.
#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:5645
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3474
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:5687
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:3348
A bunch of utility functions.