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