Doxygen
Loading...
Searching...
No Matches
docparser.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2022 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 "docparser.h"
18
19// standard includes
20#include <cstdint>
21#include <cstdio>
22
23// other includes
24#include "classlist.h"
25#include "config.h"
26#include "debug.h"
27#include "dir.h"
28#include "docparser_p.h"
29#include "doxygen.h"
30#include "filedef.h"
31#include "fileinfo.h"
32#include "filename.h"
33#include "groupdef.h"
34#include "htmldocvisitor.h"
35#include "indexlist.h"
36#include "message.h"
37#include "namespacedef.h"
38#include "outputlist.h"
39#include "pagedef.h"
40#include "portable.h"
41#include "printdocvisitor.h"
42#include "stringutil.h"
43#include "textdocvisitor.h"
44#include "trace.h"
45#include "util.h"
46
47#if !ENABLE_DOCPARSER_TRACING
48#undef AUTO_TRACE
49#undef AUTO_TRACE_ADD
50#undef AUTO_TRACE_EXIT
51#define AUTO_TRACE(...) (void)0
52#define AUTO_TRACE_ADD(...) (void)0
53#define AUTO_TRACE_EXIT(...) (void)0
54#endif
55
56
57//---------------------------------------------------------------------------
58
60{
61 return std::make_unique<DocParser>();
62}
63
65{
66 //DString indent;
67 //indent.fill(' ',contextStack.size()*2+2);
68 //printf("%sdocParserPushContext() count=%zu\n",qPrint(indent),context.nodeStack.size());
69
71 contextStack.emplace();
72 auto &ctx = contextStack.top();
73 ctx = context;
75 ctx.lineNo = tokenizer.getLineNr();
77}
78
80{
81 auto &ctx = contextStack.top();
82 context = ctx;
83 tokenizer.setFileName(ctx.fileName);
84 tokenizer.setLineNr(ctx.lineNo);
85 contextStack.pop();
88
89 //DString indent;
90 //indent.fill(' ',contextStack.size()*2+2);
91 //printf("%sdocParserPopContext() count=%zu\n",qPrint(indent),context.nodeStack.size());
92}
93
94//---------------------------------------------------------------------------
95
96/*! search for an image in the imageNameDict and if found
97 * copies the image to the output directory (which depends on the \a type
98 * parameter).
99 */
101{
102 DString result;
103 bool ambig = false;
104 FileDef *fd = Doxygen::imageNameLinkedMap->findFileDef(fileName,ambig);
105 //printf("Search for %s\n",fileName);
106 if (fd)
107 {
108 if (ambig & doWarn)
109 {
111 "image file name '{}' is ambiguous.\n"
112 "Possible candidates:\n{}", fileName,Doxygen::imageNameLinkedMap->showFileDefMatches(fileName));
113 }
114
115 DString inputFile = fd->absFilePath();
116 FileInfo infi(inputFile.str());
117 if (infi.exists())
118 {
119 result = fileName;
120 if (size_t i = result.rfind('/') ; i!=DString::npos || (i=result.rfind('\\'))!=DString::npos)
121 {
122 result = result.mid(i+1);
123 }
124 //printf("fileName=%s result=%s\n",fileName,qPrint(result));
125 DString outputDir;
126 switch(type)
127 {
128 case DocImage::Html:
129 if (!Config_getBool(GENERATE_HTML)) return result;
130 outputDir = Config_getString(HTML_OUTPUT);
131 break;
132 case DocImage::Latex:
133 if (!Config_getBool(GENERATE_LATEX)) return result;
134 outputDir = Config_getString(LATEX_OUTPUT);
135 break;
137 if (!Config_getBool(GENERATE_DOCBOOK)) return result;
138 outputDir = Config_getString(DOCBOOK_OUTPUT);
139 break;
140 case DocImage::Rtf:
141 if (!Config_getBool(GENERATE_RTF)) return result;
142 outputDir = Config_getString(RTF_OUTPUT);
143 break;
144 case DocImage::Xml:
145 if (!Config_getBool(GENERATE_XML)) return result;
146 outputDir = Config_getString(XML_OUTPUT);
147 break;
148 }
149 DString outputFile = outputDir+"/"+result;
150 FileInfo outfi(outputFile.str());
151 if (outfi.isSymLink())
152 {
153 Dir().remove(outputFile.str());
155 "destination of image {} is a symlink, replacing with image",
156 outputFile);
157 }
158 if (outputFile!=inputFile) // prevent copying to ourself
159 {
160 if (copyFile(inputFile,outputFile) && type==DocImage::Html)
161 {
163 }
164 }
165 }
166 else
167 {
169 "could not open image {}",fileName);
170 }
171
172 if (type==DocImage::Latex && Config_getBool(USE_PDFLATEX) &&
173 fd->name().endsWith(".eps")
174 )
175 { // we have an .eps image in pdflatex mode => convert it to a pdf.
176 DString outputDir = Config_getString(LATEX_OUTPUT);
177 DString baseName = fd->name().left(fd->name().length()-4);
178 DString epstopdfArgs(4096, DString::ExplicitSize);
179 epstopdfArgs.sprintf("\"%s/%s.eps\" --outfile=\"%s/%s.pdf\"",
180 qPrint(outputDir), qPrint(baseName),
181 qPrint(outputDir), qPrint(baseName));
182 if (Portable::system("epstopdf",epstopdfArgs)!=0)
183 {
184 err("Problems running epstopdf. Check your TeX installation!\n");
185 }
186 else
187 {
188 Dir().remove(outputDir.str()+"/"+baseName.str()+".eps");
189 }
190 return baseName;
191 }
192 }
193 else
194 {
195 result=fileName;
196 if (!result.startsWith("http:") && !result.startsWith("https:") && doWarn)
197 {
199 "image file {} is not found in IMAGE_PATH: "
200 "assuming external image.",fileName
201 );
202 }
203 }
204 return result;
205}
206
207/*! Collects the parameters found with \@param command
208 * in a list context.paramsFound. If
209 * the parameter is not an actual parameter of the current
210 * member context.memberDef, then a warning is raised (unless warnings
211 * are disabled altogether).
212 */
214{
215 if (!(Config_getBool(WARN_IF_DOC_ERROR) || Config_getBool(WARN_IF_INCOMPLETE_DOC))) return;
216 if (context.memberDef==nullptr) return; // not a member
217 std::string name = context.token->name.str();
222 context.numParameters = static_cast<int>(al.size());
223 //printf("isDocsForDefinition()=%d\n",context.memberDef->isDocsForDefinition());
224 if (al.empty()) return; // no argument list
225
226 static const reg::Ex re(R"(\$?\w+\.*)");
227 static const reg::Ex re_digits(R"(\d+)");
228 reg::Iterator it(name,re);
230 for (; it!=end ; ++it)
231 {
232 const auto &match = *it;
233 DString aName=match.str();
234 int number = reg::match(aName.view(),re_digits) ? std::atoi(aName.data()) : -1;
235 if (lang==SrcLangExt::Fortran) aName=aName.lower();
236 //printf("aName='%s'\n",qPrint(aName));
237 bool found=false;
238 int position=1;
239 for (const Argument &a : al)
240 {
241 DString argName = context.memberDef->isDefine() ? a.type : a.name;
242 if (lang==SrcLangExt::Fortran) argName=argName.lower();
243 argName=argName.stripWhiteSpace();
244 //printf("argName='%s' aName=%s\n",qPrint(argName),qPrint(aName));
245 if (argName.endsWith("...")) argName=argName.left(argName.length()-3);
246 bool sameName = aName==argName;
247 bool samePosition = position==number;
248 if (samePosition || sameName) // @param <number> or @param name
249 {
250 if (!sameName) // replace positional argument with real name or -
251 {
252 context.token->name = argName.empty() ? "-" : argName;
253 }
254 context.paramsFound.insert(aName.str());
255 found=true;
256 break;
257 }
258 else if (aName==".") // replace . by - in the output
259 {
260 context.token->name = "-";
261 }
262 position++;
263 }
264 if (!found)
265 {
266 //printf("member type=%d\n",context.memberDef->memberType());
268 if (!scope.empty()) scope+="::"; else scope="";
269 DString inheritedFrom = "";
270 DString docFile = context.memberDef->docFile();
271 int docLine = context.memberDef->docLine();
272 const MemberDef *inheritedMd = context.memberDef->inheritsDocsFrom();
273 if (inheritedMd) // documentation was inherited
274 {
275 inheritedFrom.sprintf(" inherited from member %s at line "
276 "%d in file %s",qPrint(inheritedMd->name()),
277 inheritedMd->docLine(),qPrint(inheritedMd->docFile()));
278 docFile = context.memberDef->getDefFileName();
279 docLine = context.memberDef->getDefLine();
280 }
281 DString alStr = argListToString(al);
282 if (number==0)
283 {
284 warn_doc_error(docFile,docLine,
285 "positional argument with value '0' of command @param "
286 "is invalid, first parameter has index '1' for {}{}{}{}",
287 scope, context.memberDef->name(),
288 alStr, inheritedFrom);
289 context.token->name = "-";
290 }
291 else if (number>0)
292 {
293 warn_doc_error(docFile,docLine,
294 "positional argument '{}' of command @param "
295 "is larger than the number of parameters ({}) for {}{}{}{}",
296 number, al.size(), scope, context.memberDef->name(),
297 alStr, inheritedFrom);
298 context.token->name = "-";
299 }
300 else
301 {
302 warn_doc_error(docFile,docLine,
303 "argument '{}' of command @param "
304 "is not found in the argument list of {}{}{}{}",
305 aName, scope, context.memberDef->name(),
306 alStr, inheritedFrom);
307 }
308 }
309 }
310}
311/*! Collects the return values found with \@retval command
312 * in a global list g_parserContext.retvalsFound.
313 */
315{
316 DString name = context.token->name;
317 if (!Config_getBool(WARN_IF_DOC_ERROR)) return;
318 if (context.memberDef==nullptr || name.empty()) return; // not a member or no valid name
319 if (context.retvalsFound.count(name.str())==1) // only report the first double entry
320 {
323 "return value '{}' of {} has multiple documentation sections",
325 }
326 context.retvalsFound.insert(name.str());
327}
328
329/*! Checks if the parameters that have been specified using \@param are
330 * indeed all parameters and that a parameter does not have multiple
331 * \@param blocks.
332 * Must be called after checkArgumentName() has been called for each
333 * argument.
334 */
336{
338 {
343 if (!al.empty())
344 {
345 ArgumentList undocParams;
346 int position = 1;
347 for (const Argument &a: al)
348 {
349 DString argName = context.memberDef->isDefine() ? a.type : a.name;
350 if (lang==SrcLangExt::Fortran) argName = argName.lower();
351 argName=argName.stripWhiteSpace();
352 DString aName = argName;
353 if (argName.endsWith("...")) argName=argName.left(argName.length()-3);
354 if (lang==SrcLangExt::Python && (argName=="self" || argName=="cls"))
355 {
356 // allow undocumented self / cls parameter for Python
357 }
358 else if (lang==SrcLangExt::Cpp && (a.type=="this" || a.type.startsWith("this ")))
359 {
360 // allow undocumented this (for C++23 deducing this), see issue #11123
361 }
362 else if (!argName.empty())
363 {
364 size_t count_named = context.paramsFound.count(argName.str());
365 size_t count_positional = context.paramsFound.count(std::to_string(position));
366 if (count_named==0 && count_positional==0 && a.docs.empty())
367 {
368 undocParams.push_back(a);
369 }
370 else if (count_named==1 && count_positional==1 && Config_getBool(WARN_IF_DOC_ERROR))
371 {
374 "argument {} from the argument list of {} has both named and positional @param documentation sections",
376 }
377 else if (count_named+count_positional>1 && Config_getBool(WARN_IF_DOC_ERROR))
378 {
381 "argument {} from the argument list of {} has multiple @param documentation sections",
383 }
384 }
385 position++;
386 }
387 if (!undocParams.empty() && Config_getBool(WARN_IF_INCOMPLETE_DOC))
388 {
389 bool first=true;
390 DString errMsg = "The following parameter";
391 if (undocParams.size()>1) errMsg+="s";
392 errMsg+=DString(" of ")+
394 argListToString(al) +
395 (undocParams.size()>1 ? " are" : " is") + " not documented:\n";
396 for (const Argument &a : undocParams)
397 {
398 DString argName = context.memberDef->isDefine() ? a.type : a.name;
399 if (lang==SrcLangExt::Fortran) argName = argName.lower();
400 argName=argName.stripWhiteSpace();
401 if (!first) errMsg+="\n";
402 first=false;
403 errMsg+=" parameter '"+argName+"'";
404 }
406 }
407
408 if (Config_getBool(WARN_IF_DOC_ERROR) && context.paramPosition-1 > context.numParameters)
409 {
412 "too many @param commands for function {}. Found {} while function has {} parameter{}",
414 }
415 }
416 else
417 {
418 if (context.paramsFound.empty() && Config_getBool(WARN_IF_DOC_ERROR))
419 {
422 "{} has @param documentation sections but no arguments",
424 }
425 }
426 }
427}
428
429
430//---------------------------------------------------------------------------
431
432//---------------------------------------------------------------------------
433
434//---------------------------------------------------------------------------
435/*! Looks for a documentation block with name commandName in the current
436 * context (g_parserContext.context). The resulting documentation string is
437 * put in pDoc, the definition in which the documentation was found is
438 * put in pDef.
439 * @retval true if name was found.
440 * @retval false if name was not found.
441 */
443 DString *pDoc,
444 DString *pBrief,
445 const Definition **pDef)
446{
447 AUTO_TRACE("commandName={}",commandName);
448 *pDoc="";
449 *pBrief="";
450 *pDef=nullptr;
451 DString cmdArg=commandName;
452 if (cmdArg.empty())
453 {
454 AUTO_TRACE_EXIT("empty");
455 return false;
456 }
457
458 const FileDef *fd=nullptr;
459 const GroupDef *gd=nullptr;
460 const PageDef *pd=nullptr;
461 gd = Doxygen::groupLinkedMap->find(cmdArg);
462 if (gd) // group
463 {
464 *pDoc=gd->documentation();
465 *pBrief=gd->briefDescription();
466 *pDef=gd;
467 AUTO_TRACE_EXIT("group");
468 return true;
469 }
470 pd = Doxygen::pageLinkedMap->find(cmdArg);
471 if (pd) // page
472 {
473 *pDoc=pd->documentation();
474 *pBrief=pd->briefDescription();
475 *pDef=pd;
476 AUTO_TRACE_EXIT("page");
477 return true;
478 }
479 bool ambig = false;
480 fd = Doxygen::inputNameLinkedMap->findFileDef(cmdArg,ambig);
481 if (fd && !ambig) // file
482 {
483 *pDoc=fd->documentation();
484 *pBrief=fd->briefDescription();
485 *pDef=fd;
486 AUTO_TRACE_EXIT("file");
487 return true;
488 }
489
490 // for symbols we need to normalize the separator, so A#B, or A\B, or A.B becomes A::B
491 cmdArg = substitute(cmdArg,"#","::");
492 cmdArg = substitute(cmdArg,"\\","::");
493 bool extractAnonNs = Config_getBool(EXTRACT_ANON_NSPACES);
494 if (extractAnonNs &&
495 cmdArg.startsWith("anonymous_namespace{")
496 )
497 {
498 size_t rightBracePos = cmdArg.find("}", dstrlen("anonymous_namespace{"));
499 if (rightBracePos != DString::npos)
500 {
501 DString leftPart = cmdArg.left(rightBracePos + 1);
502 DString rightPart = cmdArg.mid(rightBracePos + 1);
503 rightPart = substitute(rightPart, ".", "::");
504 cmdArg = leftPart + rightPart;
505 }
506 else
507 {
508 cmdArg = substitute(cmdArg,".","::");
509 }
510 }
511 else
512 {
513 cmdArg = substitute(cmdArg,".","::");
514 }
515
516 int l=static_cast<int>(cmdArg.length());
517
518 size_t funcStart=cmdArg.find('(');
519 if (funcStart==DString::npos)
520 {
521 funcStart=l;
522 }
523 else
524 {
525 // Check for the case of operator() and the like.
526 // beware of scenarios like operator()((foo)bar)
527 size_t secondParen = cmdArg.find('(', funcStart+1);
528 size_t leftParen = cmdArg.find(')', funcStart+1);
529 if (leftParen!=DString::npos && secondParen!=DString::npos)
530 {
531 if (leftParen<secondParen)
532 {
533 funcStart=secondParen;
534 }
535 }
536 }
537
538 DString name=removeRedundantWhiteSpace(cmdArg.left(funcStart));
539 DString args=cmdArg.right(l-funcStart);
540 // try if the link is to a member
541 GetDefInput input(
542 context.context.find('.')==DString::npos ? context.context : DString(), // find('.') is a hack to detect files
543 name,
544 args);
545 input.checkCV=true;
546 // the file of the documentation block, so that a static in that file can be found
547 if (input.currentFile==nullptr)
548 {
550 }
551 GetDefResult result = getDefs(input);
552 //printf("found=%d context=%s name=%s\n",result.found,qPrint(context.context),qPrint(name));
553 if (result.found && result.md)
554 {
555 *pDoc=result.md->documentation();
556 *pBrief=result.md->briefDescription();
557 *pDef=result.md;
558 AUTO_TRACE_EXIT("member");
559 return true;
560 }
561
562 int scopeOffset=static_cast<int>(context.context.length());
563 do // for each scope
564 {
565 DString fullName=cmdArg;
566 if (scopeOffset>0)
567 {
568 fullName.prepend(context.context.left(scopeOffset)+"::");
569 }
570 //printf("Trying fullName='%s'\n",qPrint(fullName));
571
572 // try class, namespace, group, page, file reference
573 const ClassDef *cd = Doxygen::classLinkedMap->find(fullName);
574 if (cd) // class
575 {
576 *pDoc=cd->documentation();
577 *pBrief=cd->briefDescription();
578 *pDef=cd;
579 AUTO_TRACE_EXIT("class");
580 return true;
581 }
582 const NamespaceDef *nd = Doxygen::namespaceLinkedMap->find(fullName);
583 if (nd) // namespace
584 {
585 *pDoc=nd->documentation();
586 *pBrief=nd->briefDescription();
587 *pDef=nd;
588 AUTO_TRACE_EXIT("namespace");
589 return true;
590 }
591 if (scopeOffset==0)
592 {
593 scopeOffset=-1;
594 }
595 else
596 {
597 size_t o = context.context.rfind("::",scopeOffset-1);
598 scopeOffset = o!=DString::npos ? static_cast<int>(o) : 0;
599 }
600 } while (scopeOffset>=0);
601
602 AUTO_TRACE_EXIT("not found");
603 return false;
604}
605
606//---------------------------------------------------------------------------
608 DocNodeList &children,const DString &txt)
609{
610 switch (tok.value())
611 {
612 case TokenRetval::TK_COMMAND_AT:
613 // fall through
614 case TokenRetval::TK_COMMAND_BS:
615 {
616 char cs[2] = { tok.command_to_char(), 0 };
617 children.append<DocWord>(this,parent,cs + context.token->name);
618 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a {}",
619 tok.command_to_char(),context.token->name,txt);
620 }
621 break;
622 case TokenRetval::TK_SYMBOL:
623 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a {}",
624 qPrint(context.token->name), qPrint(txt));
625 break;
626 case TokenRetval::TK_HTMLTAG:
627 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unsupported HTML tag <{}{}> found as part of a {}",
628 context.token->endTag ? "/" : "",context.token->name, txt);
629 break;
630 default:
631 children.append<DocWord>(this,parent,context.token->name);
632 warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unexpected token {} found as part of a {}",
633 tok.to_string(), txt);
634 break;
635 }
636}
637
638//---------------------------------------------------------------------------
639
641{
642 AUTO_TRACE("cmdName={}",cmdName);
643 DString saveCmdName = cmdName;
644 Token tok=tokenizer.lex();
645 size_t styleStackSizeAtStart = context.styleStack.size();
646 if (!tok.is(TokenRetval::TK_WHITESPACE))
647 {
648 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
649 saveCmdName);
650 return tok;
651 }
652 tok = tokenizer.lex();
653 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::TK_WHITESPACE,
654 TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, TokenRetval::TK_ENDLIST)
655 )
656 {
657 static const reg::Ex specialChar(R"([.,|()\‍[\‍]:;?])");
658 if (tok.is(TokenRetval::TK_WORD) && context.token->name.length()==1 &&
659 reg::match(context.token->name.str(),specialChar))
660 {
661 // special character that ends the markup command
662 AUTO_TRACE_ADD("special character ending style argument: '{}' styleStackSize {}->{}",context.token->name,styleStackSizeAtStart,context.styleStack.size());
663 if (context.styleStack.size() > styleStackSizeAtStart) // new styles opened inside command, but not closed
664 {
665 handlePendingStyleCommands(parent,children,context.styleStack.size()-styleStackSizeAtStart);
666 }
667 return tok;
668 }
669 if (!defaultHandleToken(parent,tok,children))
670 {
671 switch (tok.value())
672 {
673 case TokenRetval::TK_HTMLTAG:
675 {
676 // ignore </li> as the end of a style command
677 }
678 else
679 {
680 AUTO_TRACE_EXIT("end tok={}",tok.to_string());
681 return tok;
682 }
683 break;
684 default:
685 errorHandleDefaultToken(parent,tok,children,"\\" + saveCmdName + " command");
686 break;
687 }
688 break;
689 }
690 tok = tokenizer.lex();
691 }
692 AUTO_TRACE_EXIT("end tok={}",tok.to_string());
693 return (tok.is_any_of(TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM,TokenRetval::TK_ENDLIST)) ? tok : Token::make_RetVal_OK();
694}
695
696/*! Called when a style change starts. For instance a <b> command is
697 * encountered.
698 */
700 DocStyleChange::Style s,const DString &tagName,const HtmlAttribList *attribs)
701{
702 AUTO_TRACE("tagName={}",tagName);
703 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),s,tagName,true,
705 context.styleStack.push(&children.back());
707}
708
709/*! Called when a style change ends. For instance a </b> command is
710 * encountered.
711 */
713 DocStyleChange::Style s,const DString &tagName)
714{
715 AUTO_TRACE("tagName={}",tagName);
716 DString tagNameLower = DString(tagName).lower();
717
718 auto topStyleChange = [](const DocStyleChangeStack &stack) -> const DocStyleChange &
719 {
720 return std::get<DocStyleChange>(*stack.top());
721 };
722
723 if (context.styleStack.empty() || // no style change
724 topStyleChange(context.styleStack).style()!=s || // wrong style change
725 topStyleChange(context.styleStack).tagName()!=tagNameLower || // wrong style change
726 topStyleChange(context.styleStack).position()!=context.nodeStack.size() // wrong position
727 )
728 {
729 if (context.styleStack.empty())
730 {
731 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{0}> tag without matching <{0}>",tagName);
732 }
733 else if (topStyleChange(context.styleStack).tagName()!=tagNameLower ||
734 topStyleChange(context.styleStack).style()!=s)
735 {
736 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{}> tag while expecting </{}>",
737 tagName,topStyleChange(context.styleStack).tagName());
738 }
739 else
740 {
741 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found </{}> at different nesting level ({}) than expected ({})",
742 tagName,context.nodeStack.size(),topStyleChange(context.styleStack).position());
743 }
744 }
745 else // end the section
746 {
747 children.append<DocStyleChange>(
748 this,parent,context.nodeStack.size(),s,
749 topStyleChange(context.styleStack).tagName(),false);
750 context.styleStack.pop();
751 }
753 {
754 context.inCodeStyle = false;
755 }
756}
757
758/*! Called at the end of a paragraph to close all open style changes
759 * (e.g. a <b> without a </b>). The closed styles are pushed onto a stack
760 * and entered again at the start of a new paragraph.
761 */
762void DocParser::handlePendingStyleCommands(DocNodeVariant *parent,DocNodeList &children, size_t numberOfElementsToClose)
763{
764 AUTO_TRACE("context.styleStack.size()={} numberOfElementsToClose={}",context.styleStack.size(),numberOfElementsToClose);
765 if (!context.styleStack.empty())
766 {
767 if (numberOfElementsToClose==0) numberOfElementsToClose = context.styleStack.size(); // 0 is special value for "close all"
768 const DocStyleChange *sc = &std::get<DocStyleChange>(*context.styleStack.top());
769 while (sc && sc->position()>=context.nodeStack.size() && numberOfElementsToClose>0)
770 { // there are unclosed style modifiers in the paragraph
771 AUTO_TRACE_ADD("unclosed style {} at position {}",sc->styleString(),sc->position());
772 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),
773 sc->style(),sc->tagName(),false);
775 context.styleStack.pop();
776 sc = !context.styleStack.empty() ? &std::get<DocStyleChange>(*context.styleStack.top()) : nullptr;
777 numberOfElementsToClose--;
778 }
779 }
780}
781
783{
784 AUTO_TRACE();
785 while (!context.initialStyleStack.empty())
786 {
787 const DocStyleChange &sc = std::get<DocStyleChange>(*context.initialStyleStack.top());
788 handleStyleEnter(parent,children,sc.style(),sc.tagName(),&sc.attribs());
790 }
791}
792
794 const HtmlAttribList &tagHtmlAttribs)
795{
796 AUTO_TRACE();
797 size_t index=0;
798 Token retval = Token::make_RetVal_OK();
799 for (const auto &opt : tagHtmlAttribs)
800 {
801 if (opt.name=="name" || opt.name=="id") // <a name=label> or <a id=label> tag
802 {
803 if (!opt.value.empty())
804 {
805 children.append<DocAnchor>(this,parent,opt.value,true);
806 break; // stop looking for other tag attribs
807 }
808 else
809 {
810 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <a> tag with name option but without value!");
811 }
812 }
813 else if (opt.name=="href") // <a href=url>..</a> tag
814 {
815 // copy attributes
816 HtmlAttribList attrList = tagHtmlAttribs;
817 // and remove the href attribute
818 attrList.erase(attrList.begin()+index);
819 DString relPath;
820 if (opt.value.at(0) != '#') relPath = context.relPath;
821 children.append<DocHRef>(this, parent, attrList,
822 opt.value, relPath,
823 convertNameToFile(context.fileName, false, true));
825 retval = children.get_last<DocHRef>()->parse();
828 break;
829 }
830 else // unsupported option for tag a
831 {
832 }
833 ++index;
834 }
835 return retval;
836}
837
839{
840 AUTO_TRACE("content.initialStyleStack.size()={}",context.initialStyleStack.size());
841 if (!context.initialStyleStack.empty())
842 {
843 DString tagName = std::get<DocStyleChange>(*context.initialStyleStack.top()).tagName();
844 DString fileName = std::get<DocStyleChange>(*context.initialStyleStack.top()).fileName();
845 int lineNr = std::get<DocStyleChange>(*context.initialStyleStack.top()).lineNr();
848 if (lineNr != -1)
849 {
851 "end of comment block while expecting "
852 "command </{}> (Probable start '{}' at line {})",tagName, fileName, lineNr);
853 }
854 else
855 {
857 "end of comment block while expecting command </{}>",tagName);
858 }
859 }
860}
861
862void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bool ignoreAutoLinkFlag,bool typeLinkOnly)
863{
864 // helper to check if word w starts with any of the words in AUTOLINK_IGNORE_WORDS
865 auto ignoreWord = [](const DString &w) -> bool {
866 auto list = Config_getList(AUTOLINK_IGNORE_WORDS);
867 return std::find_if(list.begin(), list.end(),
868 [&w](const auto &ignore) { return w.startsWith(ignore); }
869 )!=list.end();
870 };
872 AUTO_TRACE("word={}",name);
873 if (!context.autolinkSupport || ignoreAutoLinkFlag || ignoreWord(context.token->name)) // no autolinking -> add as normal word
874 {
875 children.append<DocWord>(this,parent,name);
876 return;
877 }
878
879 // ------- try to turn the word 'name' into a link
880
881 const Definition *compound=nullptr;
882 const MemberDef *member=nullptr;
883 size_t len = context.token->name.length();
884 ClassDef *cd=nullptr;
885 bool ambig = false;
887 auto lang = context.lang;
888 bool inSeeBlock = context.inSeeBlock || context.inCodeStyle;
889 //printf("handleLinkedWord(%s) context.context=%s\n",qPrint(context.token->name),qPrint(context.context));
890 if (!context.insideHtmlLink &&
891 (resolveRef(context.context,context.token->name,inSeeBlock,&compound,&member,lang,true,fd,true)
892 || (!context.context.empty() && // also try with global scope
893 resolveRef(DString(),context.token->name,inSeeBlock,&compound,&member,lang,false,nullptr,true))
894 )
895 )
896 {
897 //printf("ADD %s = %p (linkable?=%d typeLinkOnly=%d)\n",
898 // qPrint(context.token->name),(void*)member,member ? member->isLinkable() : false, typeLinkOnly);
899 if (member && member->isLinkable())
900 {
901 if (!typeLinkOnly || context.token->name.startsWith("#") ||
902 member->isTypedef() || member->isEnumerate() || member->isEnumValue()) // filter on type links
903 {
904 AUTO_TRACE_ADD("resolved reference as member link");
905 if (member->isObjCMethod())
906 {
907 bool localLink = context.memberDef ? member->getClassDef()==context.memberDef->getClassDef() : false;
908 name = member->objCMethodName(localLink,inSeeBlock);
909 }
910 children.append<DocLinkedWord>(
911 this,parent,name,
912 member->getReference(),
913 member->getOutputFileBase(),
914 member->anchor(),
915 member->briefDescriptionAsTooltip());
916 }
917 else // explicit type link, but not a type
918 {
919 AUTO_TRACE_ADD("no link as request is type but member is not a type or explicit link");
920 children.append<DocWord>(this,parent,context.token->name);
921 }
922 }
923 else if (compound->isLinkable()) // compound link
924 {
925 AUTO_TRACE_ADD("resolved reference as compound link");
926 DString anchor = compound->anchor();
927 if (compound->definitionType()==Definition::TypeFile)
928 {
929 name=context.token->name;
930 }
931 else if (compound->definitionType()==Definition::TypeGroup)
932 {
933 name=toGroupDef(compound)->groupTitle();
934 }
935 children.append<DocLinkedWord>(
936 this,parent,name,
937 compound->getReference(),
938 compound->getOutputFileBase(),
939 anchor,
940 compound->briefDescriptionAsTooltip());
941 }
942 else if (compound->definitionType()==Definition::TypeFile &&
943 (toFileDef(compound))->generateSourceFile()
944 ) // undocumented file that has source code we can link to
945 {
946 AUTO_TRACE_ADD("resolved reference as source link");
947 children.append<DocLinkedWord>(
948 this,parent,context.token->name,
949 compound->getReference(),
950 compound->getSourceFileBase(),
951 "",
952 compound->briefDescriptionAsTooltip());
953 }
954 else // not linkable
955 {
956 AUTO_TRACE_ADD("resolved reference as unlinkable compound={} (linkable={}) member={} (linkable={})",
957 compound ? compound->name() : "<none>", compound ? (int)compound->isLinkable() : -1,
958 member ? member->name() : "<none>", member ? (int)member->isLinkable() : -1);
959 children.append<DocWord>(this,parent,name);
960 }
961 }
962 else if (!context.insideHtmlLink && len>1 && context.token->name.at(len-1)==':')
963 {
964 // special case, where matching Foo: fails to be an Obj-C reference,
965 // but Foo itself might be linkable.
967 handleLinkedWord(parent,children,ignoreAutoLinkFlag);
968 children.append<DocWord>(this,parent,":");
969 }
970 else if (!context.insideHtmlLink && (cd=getClass(context.token->name+"-p")))
971 {
972 // special case 2, where the token name is not a class, but could
973 // be a Obj-C protocol
974 children.append<DocLinkedWord>(
975 this,parent,name,
976 cd->getReference(),
977 cd->getOutputFileBase(),
978 cd->anchor(),
980 }
981 else if (const RequirementIntf *req = RequirementManager::instance().find(name); req!=nullptr) // link to requirement
982 {
983 if (Config_getBool(GENERATE_REQUIREMENTS))
984 {
985 children.append<DocLinkedWord>(
986 this,parent,name,
987 DString(), // link to local requirements overview also for external references
988 req->getOutputFileBase(),
989 req->id(),
990 req->title()
991 );
992 }
993 else // cannot link to a page that does not exist
994 {
995 children.append<DocWord>(this,parent,name);
996 }
997 }
998 else // normal non-linkable word
999 {
1000 AUTO_TRACE_ADD("non-linkable");
1001 if (context.token->name.startsWith("#"))
1002 {
1003 warn_doc_error(context.fileName,tokenizer.getLineNr(),"explicit link request to '{}' could not be resolved",name);
1004 }
1005 children.append<DocWord>(this,parent,context.token->name);
1006 }
1007}
1008
1010{
1011 DString name = context.token->name; // save token name
1012 AUTO_TRACE("name={}",name);
1013 DString name1;
1014 size_t p=0, i=0, ii=0;
1015 while ((i=paramTypes.find('|',p))!=DString::npos)
1016 {
1017 name1 = paramTypes.mid(p,i-p);
1018 ii=name1.find('[');
1019 context.token->name=ii!=DString::npos ? name1.mid(0,ii) : name1; // take part without []
1020 handleLinkedWord(parent,children);
1021 if (ii!=DString::npos) children.append<DocWord>(this,parent,name1.mid(ii)); // add [] part
1022 p=i+1;
1023 children.append<DocSeparator>(this,parent,"|");
1024 }
1025 name1 = paramTypes.mid(p);
1026 ii=name1.find('[');
1027 context.token->name=ii!=DString::npos ? name1.mid(0,ii) : name1;
1028 handleLinkedWord(parent,children);
1029 if (ii!=DString::npos) children.append<DocWord>(this,parent,name1.mid(ii));
1030 context.token->name = name; // restore original token name
1031}
1032
1034{
1035 Token tok=tokenizer.lex();
1036 DString tokenName = context.token->name;
1037 AUTO_TRACE("name={}",tokenName);
1038 if (!tok.is(TokenRetval::TK_WHITESPACE))
1039 {
1040 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", tokenName);
1041 return;
1042 }
1044 tok=tokenizer.lex(); // get the reference id
1045 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1046 {
1047 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
1048 tok.to_string(),tokenName);
1049 return;
1050 }
1051 children.append<DocInternalRef>(this,parent,context.token->name);
1052 children.get_last<DocInternalRef>()->parse();
1053}
1054
1056{
1057 AUTO_TRACE();
1058 Token tok=tokenizer.lex();
1059 if (!tok.is(TokenRetval::TK_WHITESPACE))
1060 {
1061 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
1062 context.token->name);
1063 return;
1064 }
1065
1068 tok=tokenizer.lex();
1069 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1070 {
1071 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
1072 "argument of command {}",context.token->name);
1073 return;
1074 }
1075 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1076 {
1077 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
1078 tok.to_string(),context.token->name);
1079 return;
1080 }
1081 }
1082 children.append<DocAnchor>(this,parent,context.token->name,false);
1083}
1084
1086{
1087 AUTO_TRACE();
1088 // get the argument of the cite command.
1089 Token tok=tokenizer.lex();
1090
1091 CiteInfoOption option;
1092 if (tok.is(TokenRetval::TK_WORD) && context.token->name=="{")
1093 {
1095 tokenizer.lex();
1096 StringVector optList=split(context.token->name.str(),",");
1097 for (auto const &opt : optList)
1098 {
1099 if (opt == "number")
1100 {
1101 if (!option.isUnknown())
1102 {
1103 warn(context.fileName,tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", context.token->name, opt);
1104 }
1105 else
1106 {
1107 option = CiteInfoOption::makeNumber();
1108 }
1109 }
1110 else if (opt == "year")
1111 {
1112 if (!option.isUnknown())
1113 {
1114 warn(context.fileName,tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", context.token->name, opt);
1115 }
1116 else
1117 {
1118 option = CiteInfoOption::makeYear();
1119 }
1120 }
1121 else if (opt == "shortauthor")
1122 {
1123 if (!option.isUnknown())
1124 {
1125 warn(context.fileName,tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", context.token->name, opt);
1126 }
1127 else
1128 {
1130 }
1131 }
1132 else if (opt == "nopar")
1133 {
1134 option.setNoPar();
1135 }
1136 else if (opt == "nocite")
1137 {
1138 option.setNoCite();
1139 }
1140 else
1141 {
1142 warn(context.fileName,tokenizer.getLineNr(),"Unknown option specified with \\{}, discarding '{}'", context.token->name, opt);
1143 }
1144 }
1145
1146 if (option.isUnknown()) option.changeToNumber();
1147
1149 tok=tokenizer.lex();
1150 if (!tok.is(TokenRetval::TK_WHITESPACE))
1151 {
1152 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
1153 context.token->name);
1154 return;
1155 }
1156 }
1157 else if (!tok.is(TokenRetval::TK_WHITESPACE))
1158 {
1159 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after '\\{}' command",
1160 context.token->name);
1161 return;
1162 }
1163 else
1164 {
1165 option = CiteInfoOption::makeNumber();
1166 }
1167
1170 tok=tokenizer.lex();
1171 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1172 {
1173 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
1174 "argument of command '\\{}'",context.token->name);
1175 return;
1176 }
1177 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1178 {
1179 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of '\\{}'",
1180 tok.to_string(),context.token->name);
1181 return;
1182 }
1184 children.append<DocCite>(this,parent,context.token->name,context.context,option);
1185 }
1186
1187}
1188
1190{
1191 AUTO_TRACE();
1192 Token tok=tokenizer.lex();
1193 if (!tok.is(TokenRetval::TK_WHITESPACE))
1194 {
1195 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command", context.token->name);
1196 return;
1197 }
1199 tok=tokenizer.lex();
1200 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1201 {
1202 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
1203 "argument of command {}",context.token->name);
1204 return;
1205 }
1206 else if (!tok.is(TokenRetval::TK_WORD))
1207 {
1208 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
1209 tok.to_string(),context.token->name);
1210 return;
1211 }
1214}
1215
1216/* Helper function that deals with the title, width, and height arguments of various commands.
1217 * @param[in] cmd Command id for which to extract caption and size info.
1218 * @param[in] parent Parent node, owner of the children list passed as
1219 * the third argument.
1220 * @param[in] children The list of child nodes to which the node representing
1221 * the token can be added.
1222 * @param[out] width the extracted width specifier
1223 * @param[out] height the extracted height specifier
1224 */
1226{
1227 AUTO_TRACE();
1228 auto ns = AutoNodeStack(this,parent);
1229
1230 // parse title
1232 Token tok = tokenizer.lex();
1233 while (!tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1234 {
1235 if (tok.is(TokenRetval::TK_WORD) && (context.token->name=="width=" || context.token->name=="height="))
1236 {
1237 // special case: no title, but we do have a size indicator
1238 break;
1239 }
1240 else if (tok.is(TokenRetval::TK_HTMLTAG))
1241 {
1243 break;
1244 }
1245 if (!defaultHandleToken(parent,tok,children))
1246 {
1247 errorHandleDefaultToken(parent,tok,children,Mappers::cmdMapper->find(cmd));
1248 }
1249 tok = tokenizer.lex();
1250 }
1251 // parse size attributes
1252 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1253 {
1254 tok=tokenizer.lex();
1255 }
1256 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG)) // there are values following the title
1257 {
1258 if (tok.is(TokenRetval::TK_WORD))
1259 {
1260 if (context.token->name=="width=" || context.token->name=="height=")
1261 {
1264 }
1265
1266 if (context.token->name=="width")
1267 {
1268 width = context.token->chars;
1269 }
1270 else if (context.token->name=="height")
1271 {
1272 height = context.token->chars;
1273 }
1274 else // other text after the title -> treat as normal text
1275 {
1277 //warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unknown option '{}' after \\{} command, expected 'width' or 'height'",
1278 // context.token->name, Mappers::cmdMapper->find(cmd));
1279 break;
1280 }
1281 }
1282
1283 tok=tokenizer.lex();
1284 // if we found something we did not expect, push it back to the stream
1285 // so it can still be processed
1286 if (tok.is_any_of(TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS))
1287 {
1289 tokenizer.unputString(tok.is(TokenRetval::TK_COMMAND_AT) ? "@" : "\\");
1290 break;
1291 }
1292 else if (tok.is(TokenRetval::TK_SYMBOL))
1293 {
1295 break;
1296 }
1297 else if (tok.is(TokenRetval::TK_HTMLTAG))
1298 {
1300 break;
1301 }
1302 }
1304
1306 AUTO_TRACE_EXIT("width={} height={}",width,height);
1307}
1308
1310{
1311 AUTO_TRACE();
1312 bool inlineImage = false;
1313 DString anchorStr;
1314
1315 Token tok=tokenizer.lex();
1316 if (!tok.is(TokenRetval::TK_WHITESPACE))
1317 {
1318 if (tok.is(TokenRetval::TK_WORD))
1319 {
1320 if (context.token->name == "{")
1321 {
1323 tokenizer.lex();
1325 StringVector optList=split(context.token->name.str(),",");
1326 for (const auto &opt : optList)
1327 {
1328 if (opt.empty()) continue;
1329 DString locOpt(opt);
1330 DString locOptLow;
1331 locOpt = locOpt.stripWhiteSpace();
1332 locOptLow = locOpt.lower();
1333 if (locOptLow == "inline")
1334 {
1335 inlineImage = true;
1336 }
1337 else if (locOptLow.startsWith("anchor:"))
1338 {
1339 if (!anchorStr.empty())
1340 {
1342 "multiple use of option 'anchor' for 'image' command, ignoring: '{}'",
1343 locOpt.mid(7));
1344 }
1345 else
1346 {
1347 anchorStr = locOpt.mid(7);
1348 }
1349 }
1350 else
1351 {
1353 "unknown option '{}' for 'image' command specified",
1354 locOpt);
1355 }
1356 }
1357 tok=tokenizer.lex();
1358 if (!tok.is(TokenRetval::TK_WHITESPACE))
1359 {
1360 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1361 return;
1362 }
1363 }
1364 }
1365 else
1366 {
1367 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1368 return;
1369 }
1370 }
1371 tok=tokenizer.lex();
1372 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
1373 {
1374 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of \\image",
1375 tok.to_string());
1376 return;
1377 }
1378 tok=tokenizer.lex();
1379 if (!tok.is(TokenRetval::TK_WHITESPACE))
1380 {
1381 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\image command");
1382 return;
1383 }
1385 DString imgType = context.token->name.lower();
1386 if (imgType=="html") t=DocImage::Html;
1387 else if (imgType=="latex") t=DocImage::Latex;
1388 else if (imgType=="docbook") t=DocImage::DocBook;
1389 else if (imgType=="rtf") t=DocImage::Rtf;
1390 else if (imgType=="xml") t=DocImage::Xml;
1391 else
1392 {
1393 warn_doc_error(context.fileName,tokenizer.getLineNr(),"output format `{}` specified as the first argument of "
1394 "\\image command is not valid", imgType);
1395 return;
1396 }
1398 tok=tokenizer.lex();
1400 if (!tok.is(TokenRetval::TK_WORD))
1401 {
1402 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of \\image", tok.to_string());
1403 return;
1404 }
1405 if (!anchorStr.empty())
1406 {
1407 children.append<DocAnchor>(this,parent,anchorStr,true);
1408 }
1409 HtmlAttribList attrList;
1410 children.append<DocImage>(this,parent,attrList,
1411 findAndCopyImage(context.token->name,t),t,"",inlineImage);
1412 children.get_last<DocImage>()->parse();
1413}
1414
1415void DocParser::handleRef(DocNodeVariant *parent, DocNodeList &children, char cmdChar, const DString &cmdName)
1416{
1417 AUTO_TRACE("cmdName={}",cmdName);
1418 DString saveCmdName = cmdName;
1420 Token tok=tokenizer.lex();
1421 if (!tok.is(TokenRetval::TK_WHITESPACE))
1422 {
1423 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
1424 cmdChar,qPrint(saveCmdName));
1425 return;
1426 }
1428 tok=tokenizer.lex(); // get the reference id
1429 if (!tok.is(TokenRetval::TK_WORD))
1430 {
1431 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
1432 tok.to_string(),cmdChar,saveCmdName);
1433 return;
1434 }
1435 children.append<DocRef>(this,parent,
1438 children.get_last<DocRef>()->parse(cmdChar,saveCmdName);
1439}
1440
1441void DocParser::handleIFile(char cmdChar,const DString &cmdName)
1442{
1443 AUTO_TRACE();
1444 Token tok=tokenizer.lex();
1445 if (!tok.is(TokenRetval::TK_WHITESPACE))
1446 {
1447 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
1448 cmdChar,cmdName);
1449 return;
1450 }
1452 tok=tokenizer.lex();
1454 if (!tok.is(TokenRetval::TK_WORD))
1455 {
1456 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
1457 tok.to_string(),cmdChar,cmdName);
1458 return;
1459 }
1461}
1462
1463void DocParser::handleILine(char cmdChar,const DString &cmdName)
1464{
1465 AUTO_TRACE();
1467 Token tok = tokenizer.lex();
1469 if (!tok.is(TokenRetval::TK_WORD))
1470 {
1471 warn_doc_error(context.fileName,tokenizer.getLineNr(),"invalid argument for command '{:c}{}'",
1472 cmdChar,cmdName);
1473 return;
1474 }
1475}
1476
1477/* Helper function that deals with the most common tokens allowed in
1478 * title like sections.
1479 * @param parent Parent node, owner of the children list passed as
1480 * the third argument.
1481 * @param tok The token to process.
1482 * @param children The list of child nodes to which the node representing
1483 * the token can be added.
1484 * @param handleWord Indicates if word token should be processed
1485 * @retval true The token was handled.
1486 * @retval false The token was not handled.
1487 */
1489{
1490 AUTO_TRACE("token={} handleWord={}",tok.to_string(),handleWord);
1491 if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL,
1492 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG)
1493 )
1494 {
1495 }
1496reparsetoken:
1497 DString tokenName = context.token->name;
1498 AUTO_TRACE_ADD("tokenName={}",tokenName);
1499 switch (tok.value())
1500 {
1501 case TokenRetval::TK_COMMAND_AT:
1502 // fall through
1503 case TokenRetval::TK_COMMAND_BS:
1504 switch (Mappers::cmdMapper->map(tokenName))
1505 {
1508 break;
1511 break;
1514 break;
1517 break;
1520 break;
1523 break;
1526 break;
1529 break;
1532 break;
1536 break;
1541 break;
1544 break;
1547 break;
1550 break;
1553 break;
1556 break;
1559 break;
1562 break;
1564 {
1565 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Italic,tokenName,true);
1566 tok=handleStyleArgument(parent,children,tokenName);
1567 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Italic,tokenName,false);
1568 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1569 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1570 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1571 {
1572 AUTO_TRACE_ADD("CommandType::CMD_EMPHASIS: reparsing");
1573 goto reparsetoken;
1574 }
1575 }
1576 break;
1578 {
1579 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Bold,tokenName,true);
1580 tok=handleStyleArgument(parent,children,tokenName);
1581 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Bold,tokenName,false);
1582 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1583 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1584 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1585 {
1586 AUTO_TRACE_ADD("CommandType::CMD_BOLD: reparsing");
1587 goto reparsetoken;
1588 }
1589 }
1590 break;
1592 {
1593 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Code,tokenName,true);
1594 tok=handleStyleArgument(parent,children,tokenName);
1595 children.append<DocStyleChange>(this,parent,context.nodeStack.size(),DocStyleChange::Code,tokenName,false);
1596 if (!tok.is(TokenRetval::TK_WORD)) children.append<DocWhiteSpace>(this,parent," ");
1597 if (tok.is(TokenRetval::TK_NEWPARA)) goto handlepara;
1598 else if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_HTMLTAG))
1599 {
1600 AUTO_TRACE_ADD("CommandType::CMD_CODE: reparsing");
1601 goto reparsetoken;
1602 }
1603 }
1604 break;
1606 {
1608 tok = tokenizer.lex();
1610 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1611 {
1612 warn_doc_error(context.fileName,tokenizer.getLineNr(),"htmlonly section ended without end marker");
1613 }
1615 }
1616 break;
1618 {
1620 tok = tokenizer.lex();
1622 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1623 {
1624 warn_doc_error(context.fileName,tokenizer.getLineNr(),"manonly section ended without end marker");
1625 }
1627 }
1628 break;
1630 {
1632 tok = tokenizer.lex();
1634 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1635 {
1636 warn_doc_error(context.fileName,tokenizer.getLineNr(),"rtfonly section ended without end marker");
1637 }
1639 }
1640 break;
1642 {
1644 tok = tokenizer.lex();
1646 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1647 {
1648 warn_doc_error(context.fileName,tokenizer.getLineNr(),"latexonly section ended without end marker");
1649 }
1651 }
1652 break;
1654 {
1656 tok = tokenizer.lex();
1658 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1659 {
1660 warn_doc_error(context.fileName,tokenizer.getLineNr(),"xmlonly section ended without end marker");
1661 }
1663 }
1664 break;
1666 {
1668 tok = tokenizer.lex();
1670 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1671 {
1672 warn_doc_error(context.fileName,tokenizer.getLineNr(),"docbookonly section ended without end marker");
1673 }
1675 }
1676 break;
1678 {
1679 children.append<DocFormula>(this,parent,context.token->id);
1680 }
1681 break;
1684 {
1685 handleAnchor(parent,children);
1686 }
1687 break;
1689 {
1690 handleCite(parent,children);
1691 }
1692 break;
1694 {
1695 handlePrefix(parent,children);
1696 }
1697 break;
1699 {
1700 handleInternalRef(parent,children);
1702 }
1703 break;
1705 {
1707 (void)tokenizer.lex();
1709 //printf("Found scope='%s'\n",qPrint(context.context));
1711 }
1712 break;
1714 handleImage(parent,children);
1715 break;
1717 handleILine(tok.command_to_char(),tokenName);
1718 break;
1720 handleIFile(tok.command_to_char(),tokenName);
1721 break;
1722 default:
1723 return false;
1724 }
1725 break;
1726 case TokenRetval::TK_HTMLTAG:
1727 {
1728 auto handleEnterLeaveStyle = [this,&parent,&children,&tokenName](DocStyleChange::Style style) {
1729 if (!context.token->endTag)
1730 {
1731 handleStyleEnter(parent,children,style,tokenName,&context.token->attribs);
1732 }
1733 else
1734 {
1735 handleStyleLeave(parent,children,style,tokenName);
1736 }
1737 };
1738 switch (Mappers::htmlTagMapper->map(tokenName))
1739 {
1741 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <div> tag in heading");
1742 break;
1744 warn_doc_error(context.fileName,tokenizer.getLineNr(),"found <pre> tag in heading");
1745 break;
1747 handleEnterLeaveStyle(DocStyleChange::Span);
1748 break;
1750 handleEnterLeaveStyle(DocStyleChange::Bold);
1751 break;
1753 handleEnterLeaveStyle(DocStyleChange::S);
1754 break;
1756 handleEnterLeaveStyle(DocStyleChange::Strike);
1757 break;
1759 handleEnterLeaveStyle(DocStyleChange::Del);
1760 break;
1762 handleEnterLeaveStyle(DocStyleChange::Underline);
1763 break;
1765 handleEnterLeaveStyle(DocStyleChange::Ins);
1766 break;
1768 case HtmlTagType::XML_C:
1769 handleEnterLeaveStyle(DocStyleChange::Code);
1770 break;
1772 handleEnterLeaveStyle(DocStyleChange::Kbd);
1773 break;
1775 handleEnterLeaveStyle(DocStyleChange::Typewriter);
1776 break;
1778 handleEnterLeaveStyle(DocStyleChange::Italic);
1779 break;
1781 handleEnterLeaveStyle(DocStyleChange::Subscript);
1782 break;
1784 handleEnterLeaveStyle(DocStyleChange::Superscript);
1785 break;
1787 handleEnterLeaveStyle(DocStyleChange::Center);
1788 break;
1790 handleEnterLeaveStyle(DocStyleChange::Small);
1791 break;
1793 handleEnterLeaveStyle(DocStyleChange::Cite);
1794 break;
1796 if (!context.token->endTag)
1797 {
1799 }
1800 break;
1801 default:
1802 return false;
1803 break;
1804 }
1805 }
1806 break;
1807 case TokenRetval::TK_SYMBOL:
1808 {
1811 {
1812 children.append<DocSymbol>(this,parent,s);
1813 }
1814 else
1815 {
1816 return false;
1817 }
1818 }
1819 break;
1820 case TokenRetval::TK_WHITESPACE:
1821 case TokenRetval::TK_NEWPARA:
1822handlepara:
1823 if (insidePRE(parent) || !children.empty())
1824 {
1825 children.append<DocWhiteSpace>(this,parent,context.token->chars);
1826 }
1827 break;
1828 case TokenRetval::TK_LNKWORD:
1829 if (handleWord)
1830 {
1831 handleLinkedWord(parent,children);
1832 }
1833 else
1834 return false;
1835 break;
1836 case TokenRetval::TK_WORD:
1837 if (handleWord)
1838 {
1839 children.append<DocWord>(this,parent,context.token->name);
1840 }
1841 else
1842 return false;
1843 break;
1844 case TokenRetval::TK_URL:
1846 {
1847 children.append<DocWord>(this,parent,context.token->name);
1848 }
1849 else
1850 {
1852 }
1853 break;
1854 default:
1855 return false;
1856 }
1857 return true;
1858}
1859
1860//---------------------------------------------------------------------------
1861
1863{
1864 AUTO_TRACE();
1865 bool found=false;
1866 size_t index=0;
1867 for (const auto &opt : tagHtmlAttribs)
1868 {
1869 AUTO_TRACE_ADD("option name={} value='{}'",opt.name,opt.value);
1870 if (opt.name=="src" && !opt.value.empty())
1871 {
1872 // copy attributes
1873 HtmlAttribList attrList = tagHtmlAttribs;
1874 // and remove the src attribute
1875 attrList.erase(attrList.begin()+index);
1877 children.append<DocImage>(
1878 this,parent,attrList,
1879 findAndCopyImage(opt.value,t,false),
1880 t,opt.value);
1881 found = true;
1882 }
1883 ++index;
1884 }
1885 if (!found)
1886 {
1887 warn_doc_error(context.fileName,tokenizer.getLineNr(),"IMG tag does not have a SRC attribute!");
1888 }
1889}
1890
1891//---------------------------------------------------------------------------
1892
1894 const DString &doc)
1895{
1896 AUTO_TRACE();
1897 Token retval = Token::make_RetVal_OK();
1898
1899 if (doc.empty()) return retval;
1900
1902
1903 // first parse any number of paragraphs
1904 bool isFirst=true;
1905 DocPara *lastPar=!children.empty() ? std::get_if<DocPara>(&children.back()): nullptr;
1906 if (lastPar)
1907 { // last child item was a paragraph
1908 isFirst=false;
1909 }
1910 do
1911 {
1912 children.append<DocPara>(this,parent);
1913 DocPara *par = children.get_last<DocPara>();
1914 if (isFirst) { par->markFirst(); isFirst=false; }
1915 retval=par->parse();
1916 if (!par->empty())
1917 {
1918 if (lastPar) lastPar->markLast(false);
1919 lastPar=par;
1920 }
1921 else
1922 {
1923 children.pop_back();
1924 }
1925 } while (retval.is(TokenRetval::TK_NEWPARA));
1926 if (lastPar) lastPar->markLast();
1927
1928 AUTO_TRACE_EXIT("isFirst={} isLast={}",lastPar?lastPar->isFirst():-1,lastPar?lastPar->isLast():-1);
1929 return retval;
1930}
1931
1932//---------------------------------------------------------------------------
1933
1935{
1936 AUTO_TRACE("file={} text={}",file,text);
1937 bool ambig = false;
1938 DString filePath = findExampleFilePath(file,ambig);
1939 if (!filePath.empty())
1940 {
1941 size_t indent = 0;
1942 text = detab(fileToString(filePath,Config_getBool(FILTER_SOURCE_FILES)),indent);
1943 if (ambig)
1944 {
1945 warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file name '{}' is ambiguous"
1946 "Possible candidates:\n{}",file, Doxygen::exampleNameLinkedMap->showFileDefMatches(file));
1947 }
1948 }
1949 else
1950 {
1951 warn_doc_error(context.fileName,tokenizer.getLineNr(),"included file '{}' is not found. "
1952 "Check your EXAMPLE_PATH",file);
1953 }
1954}
1955
1956//---------------------------------------------------------------------------
1957
1958static DString extractCopyDocId(const char *data, size_t &j, size_t len)
1959{
1960 size_t s=j;
1961 int round=0;
1962 bool insideDQuote=false;
1963 bool insideSQuote=false;
1964 bool found=false;
1965 while (j<len && !found)
1966 {
1967 if (!insideSQuote && !insideDQuote)
1968 {
1969 switch (data[j])
1970 {
1971 case '(': round++; break;
1972 case ')': round--; break;
1973 case '"': insideDQuote=true; break;
1974 case '\'': insideSQuote=true; break;
1975 case '\\': // fall through, begin of command
1976 case '@': // fall through, begin of command
1977 case '\t': // fall through
1978 case '\n':
1979 found=(round==0);
1980 break;
1981 case ' ': // allow spaces in cast operator (see issue #11169)
1982 found=(round==0) && (j<8 || !literal_at(data+j-8,"operator"));
1983 break;
1984 }
1985 }
1986 else if (insideSQuote) // look for single quote end
1987 {
1988 if (data[j]=='\'' && (j==0 || data[j]!='\\'))
1989 {
1990 insideSQuote=false;
1991 }
1992 }
1993 else if (insideDQuote) // look for double quote end
1994 {
1995 if (data[j]=='"' && (j==0 || data[j]!='\\'))
1996 {
1997 insideDQuote=false;
1998 }
1999 }
2000 if (!found) j++;
2001 }
2002
2003 // include const and volatile
2004 if (literal_at(data+j," const"))
2005 {
2006 j+=6;
2007 }
2008 else if (literal_at(data+j," volatile"))
2009 {
2010 j+=9;
2011 }
2012
2013 // allow '&' or '&&' or ' &' or ' &&' at the end
2014 size_t k=j;
2015 while (k<len && data[k]==' ') k++;
2016 if (k<len-1 && data[k]=='&' && data[k+1]=='&') j=k+2;
2017 else if (k<len && data[k]=='&' ) j=k+1;
2018
2019 // do not include punctuation added by Definition::_setBriefDescription()
2020 size_t e=j;
2021 if (j>0 && data[j-1]=='.') { e--; }
2022 DString id(data+s,e-s);
2023 //printf("extractCopyDocId='%s' input='%s'\n",qPrint(id),&data[s]);
2024 return id;
2025}
2026
2027// macro to check if the input starts with a specific command.
2028// note that data[i] should point to the start of the command (\ or @ character)
2029// and the sizeof(str) returns the size of str including the '\0' terminator;
2030// a fact we abuse to skip over the start of the command character.
2031// The command name must not continue with more identifier characters,
2032// so that e.g. \dotfile is not mistaken for \dot.
2033#define CHECK_FOR_COMMAND(str,action) \
2034 do if ((i+sizeof(str)<len) && literal_at(data+i+1,str) && !isId(data[i+sizeof(str)])) \
2035 { j=i+sizeof(str); action; } while(0)
2036
2037static size_t isCopyBriefOrDetailsCmd(const char *data, size_t i,size_t len,bool &brief)
2038{
2039 size_t j=0;
2040 if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
2041 {
2042 CHECK_FOR_COMMAND("copybrief",brief=true); // @copybrief or \copybrief
2043 CHECK_FOR_COMMAND("copydetails",brief=false); // @copydetails or \copydetails
2044 }
2045 return j;
2046}
2047
2048static size_t isVerbatimSection(const char *data,size_t i,size_t len,DString &endMarker)
2049{
2050 size_t j=0;
2051 if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command
2052 {
2053 CHECK_FOR_COMMAND("dot",endMarker="enddot");
2054 CHECK_FOR_COMMAND("icode",endMarker="endicode");
2055 CHECK_FOR_COMMAND("code",endMarker="endcode");
2056 CHECK_FOR_COMMAND("msc",endMarker="endmsc");
2057 CHECK_FOR_COMMAND("mermaid",endMarker="endmermaid");
2058 CHECK_FOR_COMMAND("iverbatim",endMarker="endiverbatim");
2059 CHECK_FOR_COMMAND("verbatim",endMarker="endverbatim");
2060 CHECK_FOR_COMMAND("iliteral",endMarker="endiliteral");
2061 CHECK_FOR_COMMAND("latexonly",endMarker="endlatexonly");
2062 CHECK_FOR_COMMAND("htmlonly",endMarker="endhtmlonly");
2063 CHECK_FOR_COMMAND("xmlonly",endMarker="endxmlonly");
2064 CHECK_FOR_COMMAND("rtfonly",endMarker="endrtfonly");
2065 CHECK_FOR_COMMAND("manonly",endMarker="endmanonly");
2066 CHECK_FOR_COMMAND("docbookonly",endMarker="enddocbookonly");
2067 CHECK_FOR_COMMAND("startuml",endMarker="enduml");
2068 }
2069 //printf("isVerbatimSection(%s)=%d)\n",qPrint(DString(&data[i]).left(10)),j);
2070 return j;
2071}
2072
2073static size_t skipToEndMarker(const char *data,size_t i,size_t len,const DString &endMarker)
2074{
2075 while (i<len)
2076 {
2077 if ((data[i]=='@' || data[i]=='\\') && // start of command character
2078 (i==0 || (data[i-1]!='@' && data[i-1]!='\\'))) // that is not escaped
2079 {
2080 if (i+endMarker.length()+1<=len && dstrncmp(data+i+1,endMarker.data(),endMarker.length())==0)
2081 {
2082 return i+endMarker.length()+1;
2083 }
2084 }
2085 i++;
2086 }
2087 // oops no endmarker found...
2088 return i<len ? i+1 : len;
2089}
2090
2091
2092DString DocParser::processCopyDoc(const char *data,size_t &len)
2093{
2094 AUTO_TRACE("data={} len={}",Trace::trunc(data),len);
2095 DString result;
2096 result.reserve(len+32);
2097 size_t i=0;
2098 int lineNr = tokenizer.getLineNr();
2099 while (i<len)
2100 {
2101 char c = data[i];
2102 if (c=='@' || c=='\\') // look for a command
2103 {
2104 bool isBrief=true;
2105 size_t j=isCopyBriefOrDetailsCmd(data,i,len,isBrief);
2106 if (j>0)
2107 {
2108 // skip whitespace
2109 while (j<len && (data[j]==' ' || data[j]=='\t')) j++;
2110 // extract the argument
2111 DString id = extractCopyDocId(data,j,len);
2112 const Definition *def = nullptr;
2113 DString doc,brief;
2114 //printf("resolving docs='%s'\n",qPrint(id));
2115 bool found = findDocsForMemberOrCompound(id,&doc,&brief,&def);
2116 if (found && def->isReference())
2117 {
2119 "@copy{} or @copydoc target '{}' found but is from a tag file, skipped",
2120 isBrief?"brief":"details", id);
2121 }
2122 else if (found)
2123 {
2124 //printf("found it def=%p brief='%s' doc='%s' isBrief=%d\n",def,qPrint(brief),qPrint(doc),isBrief);
2125 auto it = std::find(context.copyStack.begin(),context.copyStack.end(),def);
2126 if (it==context.copyStack.end()) // definition not parsed earlier
2127 {
2128 DString orgFileName = context.fileName;
2129 context.copyStack.push_back(def);
2130 auto addDocs = [&](const DString &file_,int line_,const DString &doc_)
2131 {
2132 result+=" \\ifile \""+file_+"\" ";
2133 result+="\\iline "+DString().setNum(line_)+" \\ilinebr ";
2134 size_t len_ = doc_.length();
2135 result+=processCopyDoc(doc_.data(),len_);
2136 };
2137 if (isBrief)
2138 {
2139 addDocs(def->briefFile(),def->briefLine(),brief);
2140 }
2141 else
2142 {
2143 addDocs(def->docFile(),def->docLine(),doc);
2145 {
2146 const MemberDef *md = toMemberDef(def);
2147 const ArgumentList &docArgList = md->templateMaster() ?
2148 md->templateMaster()->argumentList() :
2149 md->argumentList();
2150 result+=inlineArgListToDoc(docArgList);
2151 }
2152 }
2153 context.copyStack.pop_back();
2154 result+=" \\ilinebr \\ifile \""+context.fileName+"\" ";
2155 result+="\\iline "+DString().setNum(lineNr)+" ";
2156 }
2157 else
2158 {
2160 "Found recursive @copy{} or @copydoc relation for argument '{}'.",
2161 isBrief?"brief":"details",id);
2162 }
2163 }
2164 else
2165 {
2167 "@copy{} or @copydoc target '{}' not found", isBrief?"brief":"details",id);
2168 }
2169 // skip over command
2170 i=j;
2171 }
2172 else
2173 {
2174 DString endMarker;
2175 size_t k = isVerbatimSection(data,i,len,endMarker);
2176 if (k>0)
2177 {
2178 size_t orgPos = i;
2179 i=skipToEndMarker(data,k,len,endMarker);
2180 result+=DString(data+orgPos,i-orgPos);
2181 // TODO: adjust lineNr
2182 }
2183 else
2184 {
2185 result+=c;
2186 i++;
2187 }
2188 }
2189 }
2190 else // not a command, just copy
2191 {
2192 result+=c;
2193 i++;
2194 lineNr += (c=='\n') ? 1 : 0;
2195 }
2196 }
2197 len = result.length();
2198 AUTO_TRACE_EXIT("result={}",Trace::trunc(result));
2199 return result;
2200}
2201
2202
2203//---------------------------------------------------------------------------
2204
2206 const DString &fileName,
2207 int startLine,
2208 const Definition *ctx,
2209 const MemberDef *md,
2210 const DString &input,
2211 const DocOptions &options)
2212{
2213 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2214 ASSERT(parser!=nullptr);
2215 if (parser==nullptr) return nullptr;
2216 //printf("validatingParseDoc(%s,%s)=[%s]\n",ctx?qPrint(ctx->name()):"<none>",
2217 // md?qPrint(md->name()):"<none>",
2218 // qPrint(input));
2219 //printf("========== validating %s at line %d\n",qPrint(fileName),startLine);
2220 //printf("---------------- input --------------------\n%s\n----------- end input -------------------\n",qPrint(input));
2221
2222 // set initial token
2223 parser->context.token = parser->tokenizer.resetToken();
2224
2225 if (ctx && ctx!=Doxygen::globalScope &&
2228 )
2229 )
2230 {
2232 }
2233 else if (ctx && ctx->definitionType()==Definition::TypePage)
2234 {
2235 const Definition *scope = (toPageDef(ctx))->getPageScope();
2236 if (scope && scope!=Doxygen::globalScope)
2237 {
2238 parser->context.context = substitute(scope->name(),getLanguageSpecificSeparator(scope->getLanguage(),true),"::");
2239 }
2240 }
2241 else if (ctx && ctx->definitionType()==Definition::TypeGroup)
2242 {
2243 const Definition *scope = (toGroupDef(ctx))->getGroupScope();
2244 if (scope && scope!=Doxygen::globalScope)
2245 {
2246 parser->context.context = substitute(scope->name(),getLanguageSpecificSeparator(scope->getLanguage(),true),"::");
2247 }
2248 }
2249 else
2250 {
2251 parser->context.context = "";
2252 }
2253 parser->context.scope = ctx;
2254 parser->context.lang = getLanguageFromFileName(fileName);
2255
2256 if (options.indexWords() && Doxygen::searchIndex.enabled())
2257 {
2258 if (md)
2259 {
2260 parser->context.searchUrl=md->getOutputFileBase();
2262 }
2263 else if (ctx)
2264 {
2265 parser->context.searchUrl=ctx->getOutputFileBase();
2266 Doxygen::searchIndex.setCurrentDoc(ctx,ctx->anchor(),false);
2267 }
2268 }
2269 else
2270 {
2271 parser->context.searchUrl="";
2272 }
2273
2274 parser->context.fileName = fileName;
2275 parser->context.relPath = (!options.linkFromIndex() && ctx) ?
2277 DString("");
2278 //printf("ctx->name=%s relPath=%s\n",qPrint(ctx->name()),qPrint(parser->context.relPath));
2279 parser->context.memberDef = md;
2280 while (!parser->context.nodeStack.empty()) parser->context.nodeStack.pop();
2281 while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
2282 while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
2283 parser->context.inSeeBlock = false;
2284 parser->context.inCodeStyle = false;
2285 parser->context.xmlComment = false;
2286 parser->context.insideHtmlLink = false;
2287 parser->context.includeFileText = "";
2288 parser->context.includeFileOffset = 0;
2289 parser->context.includeFileLength = 0;
2290 parser->context.isExample = options.isExample();
2291 parser->context.exampleName = options.exampleName();
2292 parser->context.hasParamCommand = false;
2293 parser->context.hasReturnCommand = false;
2294 parser->context.retvalsFound.clear();
2295 parser->context.paramsFound.clear();
2296 parser->context.markdownSupport = options.markdownSupport();
2297 parser->context.autolinkSupport = options.autolinkSupport();
2298 if (md)
2299 {
2300 const ArgumentList &al=md->isDocsForDefinition() ? md->argumentList() : md->declArgumentList();
2301 parser->context.numParameters = static_cast<int>(al.size());
2302 }
2303 else
2304 {
2305 parser->context.numParameters = 0;
2306 }
2307 parser->context.paramPosition = 1;
2308
2309 //printf("Starting comment block at %s:%d\n",qPrint(parser->context.fileName),startLine);
2310 parser->tokenizer.setFileName(fileName);
2311 parser->tokenizer.setLineNr(startLine);
2312 size_t ioLen = input.length();
2313 DString inpStr = parser->processCopyDoc(input.data(),ioLen);
2314 if (inpStr.empty() || inpStr.at(inpStr.length()-1)!='\n')
2315 {
2316 inpStr+='\n';
2317 }
2318 //printf("processCopyDoc(in='%s' out='%s')\n",qPrint(input),qPrint(inpStr));
2319 parser->tokenizer.init(inpStr.data(),parser->context.fileName,
2321
2322 // build abstract syntax tree
2323 auto ast = std::make_unique<DocNodeAST>(DocRoot(parser,md!=nullptr,options.singleLine()));
2324 std::get<DocRoot>(ast->root).parse();
2325
2327 {
2328 // pretty print the result
2329 std::visit(PrintDocVisitor{},ast->root);
2330 }
2331
2332 if (md && md->isFunction())
2333 {
2335 }
2337
2338 // reset token
2339 parser->tokenizer.resetToken();
2340
2341 //printf(">>>>>> end validatingParseDoc(%s,%s)\n",ctx?qPrint(ctx->name()):"<none>",
2342 // md?qPrint(md->name()):"<none>");
2343
2344 return ast;
2345}
2346
2347IDocNodeASTPtr validatingParseTitle(IDocParser &parserIntf,const DString &fileName,int lineNr,const DString &input)
2348{
2349 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2350 ASSERT(parser!=nullptr);
2351 if (parser==nullptr) return nullptr;
2352
2353 // set initial token
2354 parser->context.token = parser->tokenizer.resetToken();
2355
2356 //printf("------------ input ---------\n%s\n"
2357 // "------------ end input -----\n",input);
2358 parser->context.context = "";
2359 parser->context.fileName = fileName;
2360 parser->context.relPath = "";
2361 parser->context.memberDef = nullptr;
2362 while (!parser->context.nodeStack.empty()) parser->context.nodeStack.pop();
2363 while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
2364 while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
2365 parser->context.inSeeBlock = false;
2366 parser->context.inCodeStyle = false;
2367 parser->context.xmlComment = false;
2368 parser->context.insideHtmlLink = false;
2369 parser->context.includeFileText = "";
2370 parser->context.includeFileOffset = 0;
2371 parser->context.includeFileLength = 0;
2372 parser->context.isExample = false;
2373 parser->context.exampleName = "";
2374 parser->context.hasParamCommand = false;
2375 parser->context.hasReturnCommand = false;
2376 parser->context.retvalsFound.clear();
2377 parser->context.paramsFound.clear();
2378 parser->context.searchUrl="";
2379 parser->context.lang = SrcLangExt::Unknown;
2380 parser->context.markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
2381 parser->context.autolinkSupport = false;
2382
2383 auto ast = std::make_unique<DocNodeAST>(DocTitle(parser,nullptr));
2384
2385 if (!input.empty())
2386 {
2387 // build abstract syntax tree from title string
2388 std::get<DocTitle>(ast->root).parseFromString(nullptr,input);
2389
2391 {
2392 // pretty print the result
2393 std::visit(PrintDocVisitor{},ast->root);
2394 }
2395 }
2396
2397 return ast;
2398}
2399
2401{
2402 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2403 ASSERT(parser!=nullptr);
2404 if (parser==nullptr) return nullptr;
2405
2406 // set initial token
2407 parser->context.token = parser->tokenizer.resetToken();
2408
2409 //printf("------------ input ---------\n%s\n"
2410 // "------------ end input -----\n",input);
2411 //parser->context.token = new TokenInfo;
2412 parser->context.context = "";
2413 parser->context.fileName = "<parseText>";
2414 parser->context.relPath = "";
2415 parser->context.memberDef = nullptr;
2416 while (!parser->context.nodeStack.empty()) parser->context.nodeStack.pop();
2417 while (!parser->context.styleStack.empty()) parser->context.styleStack.pop();
2418 while (!parser->context.initialStyleStack.empty()) parser->context.initialStyleStack.pop();
2419 parser->context.inSeeBlock = false;
2420 parser->context.inCodeStyle = false;
2421 parser->context.xmlComment = false;
2422 parser->context.insideHtmlLink = false;
2423 parser->context.includeFileText = "";
2424 parser->context.includeFileOffset = 0;
2425 parser->context.includeFileLength = 0;
2426 parser->context.isExample = false;
2427 parser->context.exampleName = "";
2428 parser->context.hasParamCommand = false;
2429 parser->context.hasReturnCommand = false;
2430 parser->context.retvalsFound.clear();
2431 parser->context.paramsFound.clear();
2432 parser->context.searchUrl="";
2433 parser->context.lang = SrcLangExt::Unknown;
2434 parser->context.markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
2435 parser->context.autolinkSupport = false;
2436
2437
2438 auto ast = std::make_unique<DocNodeAST>(DocText(parser));
2439
2440 if (!input.empty())
2441 {
2442 parser->tokenizer.setLineNr(1);
2443 parser->tokenizer.init(input.data(),parser->context.fileName,
2445
2446 // build abstract syntax tree
2447 std::get<DocText>(ast->root).parse();
2448
2450 {
2451 // pretty print the result
2452 std::visit(PrintDocVisitor{},ast->root);
2453 }
2454 }
2455
2456 return ast;
2457}
2458
2459IDocNodeASTPtr createRef(IDocParser &parserIntf,const DString &target,const DString &context, const DString &srcFile, int srcLine )
2460{
2461 DocParser *parser = dynamic_cast<DocParser*>(&parserIntf);
2462 ASSERT(parser!=nullptr);
2463 if (parser==nullptr) return nullptr;
2464 if (!srcFile.empty())
2465 {
2466 parser->context.fileName = srcFile;
2467 parser->tokenizer.setFileName(srcFile);
2468 parser->tokenizer.setLineNr(srcLine);
2469 }
2470 return std::make_unique<DocNodeAST>(DocRef(parser,nullptr,target,context));
2471}
2472
2473void docFindSections(const DString &input,
2474 const Definition *d,
2475 const DString &fileName)
2476{
2477 DocParser parser;
2478 parser.tokenizer.findSections(input,d,fileName);
2479}
2480
2481//--------------------------------------------------------------------------------------
2482
2483static int nextUTF8CharPosition(const DString &utf8Str,uint32_t len,uint32_t startPos)
2484{
2485 if (startPos>=len) return len;
2486 uint8_t c = static_cast<uint8_t>(utf8Str[startPos]);
2487 int bytes=getUTF8CharNumBytes(c);
2488 if (c=='&') // skip over character entities
2489 {
2490 bytes=1;
2491 int (*matcher)(int) = nullptr;
2492 c = static_cast<uint8_t>(utf8Str[startPos+bytes]);
2493 if (c=='#') // numerical entity?
2494 {
2495 bytes++;
2496 c = static_cast<uint8_t>(utf8Str[startPos+bytes]);
2497 if (c=='x') // hexadecimal entity?
2498 {
2499 bytes++;
2500 matcher = std::isxdigit;
2501 }
2502 else // decimal entity
2503 {
2504 matcher = std::isdigit;
2505 }
2506 }
2507 else if (std::isalnum(c)) // named entity?
2508 {
2509 bytes++;
2510 matcher = std::isalnum;
2511 }
2512 if (matcher)
2513 {
2514 while ((c = static_cast<uint8_t>(utf8Str[startPos+bytes]))!=0 && matcher(c))
2515 {
2516 bytes++;
2517 }
2518 }
2519 if (c!=';')
2520 {
2521 bytes=1; // not a valid entity, reset bytes counter
2522 }
2523 }
2524 return startPos+bytes;
2525}
2526
2527
2529 const DString &doc,const DString &fileName,int lineNr)
2530{
2531 if (doc.empty()) return "";
2532 static std::mutex s_docCacheMutex;
2533 static std::unordered_map<std::string,DString> s_docCache;
2534
2535 std::lock_guard lock(s_docCacheMutex);
2536 auto it = s_docCache.find(doc.str());
2537 if (it != s_docCache.end())
2538 {
2539 //printf("Cache: [%s]->[%s]\n",qPrint(doc),qPrint(it->second));
2540 return it->second;
2541 }
2542 //printf("parseCommentAsText(%s)\n",qPrint(doc));
2543 TextStream t;
2544 auto parser { createDocParser() };
2545 auto ast { validatingParseDoc(*parser.get(),
2546 fileName,
2547 lineNr,
2548 scope,
2549 md,
2550 doc,
2551 DocOptions()
2552 .setAutolinkSupport(false))
2553 };
2554 auto astImpl = dynamic_cast<const DocNodeAST*>(ast.get());
2555 if (astImpl)
2556 {
2557 TextDocVisitor visitor(t);
2558 std::visit(visitor,astImpl->root);
2559 }
2561 int i=0;
2562 int charCnt=0;
2563 int l=static_cast<int>(result.length());
2564 while ((i=nextUTF8CharPosition(result,l,i))<l)
2565 {
2566 charCnt++;
2567 if (charCnt>=80) break;
2568 }
2569 if (charCnt>=80) // try to truncate the string
2570 {
2571 while ((i=nextUTF8CharPosition(result,l,i))<l && charCnt<100)
2572 {
2573 charCnt++;
2574 if (result.at(i)==',' ||
2575 result.at(i)=='.' ||
2576 result.at(i)=='!' ||
2577 result.at(i)=='?' ||
2578 result.at(i)=='}') // good for UTF-16 characters and } otherwise also a good point to stop the string
2579 {
2580 i++; // we want to be "behind" last inspected character
2581 break;
2582 }
2583 }
2584 }
2585 if ( i < l) result=result.left(i)+"...";
2586 s_docCache.insert(std::make_pair(doc.str(),result));
2587 return result.data();
2588}
2589
2590//--------------------------------------------------------------------------------------
2591
2593 const DString &doc,const DString &fileName,int lineNr)
2594{
2595 static std::mutex s_docCacheMutex;
2596 static std::unordered_map<std::string,DString> s_docCache;
2597
2598 std::lock_guard lock(s_docCacheMutex);
2599 auto it = s_docCache.find(doc.str());
2600 if (it != s_docCache.end())
2601 {
2602 //printf("Cache: [%s]->[%s]\n",qPrint(doc),qPrint(it->second));
2603 return it->second;
2604 }
2605 auto parser { createDocParser() };
2606 auto ast { validatingParseTitle(*parser.get(),fileName,lineNr,doc) };
2607 auto astImpl = dynamic_cast<const DocNodeAST*>(ast.get());
2608 DString result;
2609 if (astImpl)
2610 {
2611 TextStream t;
2612 OutputCodeList codeList;
2613 codeList.add<HtmlCodeGenerator>(&t);
2614 HtmlDocVisitor visitor(t,codeList,scope,fileName);
2615 std::visit(visitor,astImpl->root);
2616 result = t.str();
2617 }
2618 else // fallback, should not happen
2619 {
2620 result = filterTitle(doc);
2621 }
2622 //printf("Conversion: [%s]->[%s]\n",qPrint(doc),qPrint(result));
2623 s_docCache.insert(std::make_pair(doc.str(),result));
2624 return result;
2625}
2626
This class contains the information about the argument of a function or template.
Definition arguments.h:27
This class represents an function or template argument list.
Definition arguments.h:66
size_t size() const
Definition arguments.h:101
void push_back(const Argument &a)
Definition arguments.h:103
bool empty() const
Definition arguments.h:100
constexpr void setNoCite() noexcept
Definition cite.h:35
static constexpr CiteInfoOption makeNumber()
Definition cite.h:29
constexpr void changeToNumber() noexcept
Definition cite.h:33
constexpr void setNoPar() noexcept
Definition cite.h:34
constexpr bool isUnknown() const noexcept
Definition cite.h:37
static constexpr CiteInfoOption makeYear()
Definition cite.h:31
static constexpr CiteInfoOption makeShortAuthor()
Definition cite.h:30
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
DString & setNum(short n)
Definition dstring.h:552
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
DString lower() const
Definition dstring.h:326
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
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:686
DString right(size_t len) const
Definition dstring.h:311
size_t size() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:154
DString & prepend(const char *s)
Definition dstring.h:515
size_t find(char c, size_t pos=0) const
Definition dstring.h:239
DString & sprintf(const char *format,...)
Definition dstring.cpp:34
void reserve(size_t size)
Reserve space for size bytes without changing the string contents.
Definition dstring.h:217
@ ExplicitSize
Definition dstring.h:131
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:337
DString left(size_t len) const
Definition dstring.h:306
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
bool startsWith(const char *s) const
Definition dstring.h:600
bool endsWith(const char *s) const
Definition dstring.h:617
DString & insert(size_t index, const DString &s)
Definition dstring.h:425
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:151
@ PrintTree
Definition debug.h:35
static bool isFlagSet(const DebugMask mask)
Definition debug.cpp:132
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual DString briefDescription(bool abbreviate=false) const =0
virtual DString briefFile() const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual int docLine() const =0
virtual DString getDefFileName() const =0
virtual DString documentation() const =0
virtual bool isLinkable() const =0
virtual int getDefLine() const =0
virtual DefType definitionType() const =0
virtual const DString & name() const =0
virtual DString briefDescriptionAsTooltip() const =0
virtual int briefLine() const =0
virtual DString qualifiedName() const =0
virtual DString anchor() const =0
virtual DString getReference() const =0
virtual DString docFile() const =0
virtual DString getSourceFileBase() const =0
virtual bool isReference() const =0
virtual DString getOutputFileBase() const =0
Class representing a directory in the file system.
Definition dir.h:73
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:320
Node representing an anchor.
Definition docnode.h:229
Node representing a citation of some bibliographic reference.
Definition docnode.h:245
Node representing an item of a cross-referenced list.
Definition docnode.h:529
Node representing a Hypertext reference.
Definition docnode.h:832
Node representing an image.
Definition docnode.h:642
@ DocBook
Definition docnode.h:644
Node representing an internal reference to some item.
Definition docnode.h:816
Node representing a word that can be linked to something.
Definition docnode.h:165
Class representing the abstract syntax tree of a documentation block.
Definition docnode.h:1471
void append(Args &&... args)
Append a new DocNodeVariant to the list by constructing it with type T and parameters Args.
Definition docnode.h:1404
T * get_last()
Returns a pointer to the last element in the list if that element exists and holds a T,...
Definition docnode.h:1415
Node representing a paragraph in the documentation tree.
Definition docnode.h:1089
bool isLast() const
Definition docnode.h:1097
bool isFirst() const
Definition docnode.h:1096
void markLast(bool v=true)
Definition docnode.h:1095
void handleIFile(char cmdChar, const DString &cmdName)
std::stack< DocParserContext > contextStack
void handlePendingStyleCommands(DocNodeVariant *parent, DocNodeList &children, size_t numberOfElementsToClose=0)
DocTokenizer tokenizer
void handleInternalRef(DocNodeVariant *parent, DocNodeList &children)
void readTextFileByName(const DString &file, DString &text)
void checkRetvalName()
void errorHandleDefaultToken(DocNodeVariant *parent, Token tok, DocNodeList &children, const DString &txt)
void handleRef(DocNodeVariant *parent, DocNodeList &children, char cmdChar, const DString &cmdName)
Token handleAHref(DocNodeVariant *parent, DocNodeList &children, const HtmlAttribList &tagHtmlAttribs)
void handleStyleEnter(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const DString &tagName, const HtmlAttribList *attribs)
void handleParameterType(DocNodeVariant *parent, DocNodeList &children, const DString &paramTypes)
bool defaultHandleToken(DocNodeVariant *parent, Token &tok, DocNodeList &children, bool handleWord=true)
void handleInitialStyleCommands(DocNodeVariant *parent, DocNodeList &children)
bool findDocsForMemberOrCompound(const DString &commandName, DString *pDoc, DString *pBrief, const Definition **pDef)
DString processCopyDoc(const char *data, size_t &len)
Token handleStyleArgument(DocNodeVariant *parent, DocNodeList &children, const DString &cmdName)
void handleStyleLeave(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const DString &tagName)
void checkUnOrMultipleDocumentedParams()
void popContext()
Definition docparser.cpp:79
void defaultHandleTitleAndSize(const CommandType cmd, DocNodeVariant *parent, DocNodeList &children, DString &width, DString &height)
void handleImage(DocNodeVariant *parent, DocNodeList &children)
void handleLinkedWord(DocNodeVariant *parent, DocNodeList &children, bool ignoreAutoLinkFlag=false, bool typeLinkOnly=false)
void handleCite(DocNodeVariant *parent, DocNodeList &children)
void handlePrefix(DocNodeVariant *parent, DocNodeList &children)
void handleILine(char cmdChar, const DString &cmdName)
void checkArgumentName()
DocParserContext context
Token internalValidatingParseDoc(DocNodeVariant *parent, DocNodeList &children, const DString &doc)
void handleAnchor(DocNodeVariant *parent, DocNodeList &children)
void handleImg(DocNodeVariant *parent, DocNodeList &children, const HtmlAttribList &tagHtmlAttribs)
void handleUnclosedStyleCommands()
void pushContext()
Definition docparser.cpp:64
DString findAndCopyImage(const DString &fileName, DocImage::Type type, bool doWarn=true)
Node representing a reference to some item.
Definition docnode.h:787
Root node of documentation tree.
Definition docnode.h:1318
Node representing a separator.
Definition docnode.h:365
Node representing a style change.
Definition docnode.h:268
const char * styleString() const
Definition docnode.cpp:140
const HtmlAttribList & attribs() const
Definition docnode.h:311
Style style() const
Definition docnode.h:307
DString tagName() const
Definition docnode.h:312
size_t position() const
Definition docnode.h:310
Node representing a special symbol.
Definition docnode.h:328
static HtmlEntityMapper::SymType decodeSymbol(const DString &symName)
Definition docnode.cpp:168
Root node of a text fragment.
Definition docnode.h:1309
Node representing a simple section title.
Definition docnode.h:608
TokenInfo * token()
DString getFileName() const
void setStateTitleAttrValue()
void unputString(const DString &tag)
void setStateCite()
void setStatePrefix()
void setLineNr(int lineno)
void setStateIFile()
void setStateAnchor()
void setStateRtfOnly()
void setStateTitle()
void setStateFile()
void setStateLatexOnly()
void setStateManOnly()
void findSections(const DString &input, const Definition *d, const DString &fileName)
int getLineNr() const
void setStateDbOnly()
void setStateHtmlOnly()
void setStateInternalRef()
void init(const char *input, const DString &fileName, bool markdownSupport, bool insideHtmlLink)
void setStateILine()
void setFileName(const DString &fileName)
void setStateOptions()
void setStatePara()
TokenInfo * resetToken()
void setStateXmlOnly()
void setStateSetScope()
Node representing a URL (or email address).
Definition docnode.h:188
Node representing a verbatim, unparsed text fragment.
Definition docnode.h:376
Node representing some amount of white space.
Definition docnode.h:354
Node representing a word.
Definition docnode.h:153
static NamespaceLinkedMap * namespaceLinkedMap
Definition doxygen.h:108
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:97
static ClassLinkedMap * classLinkedMap
Definition doxygen.h:88
static NamespaceDefMutable * globalScope
Definition doxygen.h:114
static FileNameLinkedMap * imageNameLinkedMap
Definition doxygen.h:98
static IndexList * indexList
Definition doxygen.h:125
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:92
static FileNameLinkedMap * exampleNameLinkedMap
Definition doxygen.h:95
static SearchIndexIntf searchIndex
Definition doxygen.h:117
static GroupLinkedMap * groupLinkedMap
Definition doxygen.h:107
A model of a file symbol.
Definition filedef.h:97
virtual DString absFilePath() const =0
Minimal replacement for QFileInfo.
Definition fileinfo.h:26
bool isSymLink() const
Definition fileinfo.cpp:81
bool exists() const
Definition fileinfo.cpp:34
DString showFileDefMatches(const DString &n) const
Returns a list of file definitions in fnMap that match the file name n.
Definition filename.cpp:131
FileDef * findFileDef(const DString &n, bool &ambig) const
Returns the file definition in fnMap that matches the file name n.
Definition filename.cpp:38
A model of a group of symbols.
Definition groupdef.h:48
virtual DString groupTitle() const =0
T & back()
access the last element
Definition growvector.h:135
void pop_back()
removes the last element
Definition growvector.h:115
bool empty() const
checks whether the container is empty
Definition growvector.h:140
Class representing a list of HTML attributes.
Definition htmlattrib.h:31
Generator for HTML code fragments.
Definition htmlgen.h:23
Concrete visitor implementation for HTML output.
static HtmlEntityMapper & instance()
Returns the one and only instance of the HTML entity mapper.
DString convertCharEntitiesToUTF8(const DString &s) const
opaque parser interface
Definition docparser.h:35
void addImageFile(const DString &name)
Definition indexlist.h:124
const T * find(const std::string &key) const
Definition linkedmap.h:47
A model of a class/file/namespace member symbol.
Definition memberdef.h:45
virtual bool isObjCMethod() const =0
virtual const MemberDef * inheritsDocsFrom() const =0
virtual DString objCMethodName(bool localLink, bool showStatic) const =0
virtual const ClassDef * getClassDef() const =0
virtual bool isTypedef() const =0
virtual const ArgumentList & argumentList() const =0
virtual bool isFunction() const =0
virtual bool isDocsForDefinition() const =0
virtual bool isDefine() const =0
virtual DString getScopeString() const =0
virtual const MemberDef * templateMaster() const =0
virtual bool isEnumerate() const =0
virtual void detectUndocumentedParams(bool hasParamCommand, bool hasReturnCommand) const =0
virtual const ArgumentList & declArgumentList() const =0
virtual bool isEnumValue() const =0
An abstract interface of a namespace symbol.
Class representing a list of different code generators.
Definition outputlist.h:162
void add(OutputCodeIntfPtr &&p)
Definition outputlist.h:192
A model of a page symbol.
Definition pagedef.h:27
static RequirementManager & instance()
void setCurrentDoc(const Definition *ctx, const DString &anchor, bool isSourceFile)
bool enabled() const
Concrete visitor implementation for TEXT output.
Text streaming class that buffers data.
Definition textstream.h:36
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h:232
bool is(TokenRetval rv) const
TOKEN_SPECIFICATIONS RETVAL_SPECIFICATIONS const char * to_string() const
TokenRetval value() const
bool is_any_of(ARGS... args) const
char command_to_char() const
DString verb
DString sectionId
DString chars
HtmlAttribList attribs
bool isEMailAddr
DString text
DString name
ClassDef * getClass(const DString &n)
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:245
CommandType
Definition cmdmapper.h:30
@ CMD_INTERNALREF
Definition cmdmapper.h:66
#define Config_getList(name)
Definition config.h:38
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
std::vector< std::string > StringVector
Definition containers.h:33
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:181
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:54
#define AUTO_TRACE(...)
Definition docnode.cpp:53
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:55
std::variant< DocWord, DocLinkedWord, DocURL, DocLineBreak, DocHorRuler, DocAnchor, DocCite, DocStyleChange, DocSymbol, DocEmoji, DocWhiteSpace, DocSeparator, DocVerbatim, DocInclude, DocIncOperator, DocFormula, DocIndexEntry, DocAutoList, DocAutoListItem, DocTitle, DocXRefItem, DocImage, DocDotFile, DocMscFile, DocDiaFile, DocVhdlFlow, DocLink, DocRef, DocInternalRef, DocHRef, DocHtmlHeader, DocHtmlDescTitle, DocHtmlDescList, DocSection, DocSecRefItem, DocSecRefList, DocInternal, DocParBlock, DocSimpleList, DocHtmlList, DocSimpleSect, DocSimpleSectSep, DocParamSect, DocPara, DocParamList, DocSimpleListItem, DocHtmlListItem, DocHtmlDescData, DocHtmlCell, DocHtmlCaption, DocHtmlRow, DocHtmlTable, DocHtmlBlockQuote, DocText, DocRoot, DocHtmlDetails, DocHtmlSummary, DocPlantUmlFile, DocMermaidFile > DocNodeVariant
Definition docnode.h:66
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1335
DString parseCommentAsText(const Definition *scope, const MemberDef *md, const DString &doc, const DString &fileName, int lineNr)
IDocNodeASTPtr validatingParseTitle(IDocParser &parserIntf, const DString &fileName, int lineNr, const DString &input)
IDocParserPtr createDocParser()
factory function to create a parser
Definition docparser.cpp:59
DString parseCommentAsHtml(const Definition *scope, const MemberDef *member, const DString &doc, const DString &fileName, int lineNr)
IDocNodeASTPtr createRef(IDocParser &parserIntf, const DString &target, const DString &context, const DString &srcFile, int srcLine)
static int nextUTF8CharPosition(const DString &utf8Str, uint32_t len, uint32_t startPos)
IDocNodeASTPtr validatingParseText(IDocParser &parserIntf, const DString &input)
static DString extractCopyDocId(const char *data, size_t &j, size_t len)
static size_t isVerbatimSection(const char *data, size_t i, size_t len, DString &endMarker)
void docFindSections(const DString &input, const Definition *d, const DString &fileName)
static size_t skipToEndMarker(const char *data, size_t i, size_t len, const DString &endMarker)
static size_t isCopyBriefOrDetailsCmd(const char *data, size_t i, size_t len, bool &brief)
IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf, const DString &fileName, int startLine, const Definition *ctx, const MemberDef *md, const DString &input, const DocOptions &options)
#define CHECK_FOR_COMMAND(str, action)
std::unique_ptr< IDocNodeAST > IDocNodeASTPtr
Definition docparser.h:57
std::unique_ptr< IDocParser > IDocParserPtr
pointer to parser interface
Definition docparser.h:41
Private header shared between docparser.cpp and docnode.cpp.
IterableStack< const DocNodeVariant * > DocStyleChangeStack
Definition docparser_p.h:54
bool insidePRE(const DocNodeVariant *n)
bool insideLI(const DocNodeVariant *n)
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:485
int dstrncmp(const char *str1, const char *str2, size_t len)
Definition dstring.h:56
uint32_t dstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition dstring.h:39
const char * qPrint(const char *s)
Definition dstring.h:783
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:2033
GroupDef * toGroupDef(Definition *d)
MemberDef * toMemberDef(Definition *d)
#define warn_incomplete_doc(file, line, fmt,...)
Definition message.h:107
#define warn(file, line, fmt,...)
Definition message.h:97
#define err(fmt,...)
Definition message.h:127
#define ASSERT(x)
Definition message.h:142
#define warn_doc_error(file, line, fmt,...)
Definition message.h:112
const Mapper< HtmlTagType > * htmlTagMapper
const Mapper< CommandType > * cmdMapper
int system(const DString &command, const DString &args, bool commandHasConsole=true)
Definition portable.cpp:121
DString trunc(const DString &s, size_t numChars=15)
Definition trace.h:56
bool match(std::string_view str, Match &match, const Ex &re)
Matches a given string str for a match against regular expression re.
Definition regex.cpp:861
PageDef * toPageDef(Definition *d)
Definition pagedef.cpp:656
Portable versions of functions that are platform dependent.
Some helper functions for std::string.
bool literal_at(const char *data, const char(&str)[N])
returns true iff data points to a substring that matches string literal str
Definition stringutil.h:101
StringVector split(const std::string &s, const std::string &delimiter, bool retainEmpty=false)
split input string s by string delimiter delimiter.
Definition stringutil.h:119
Helper class to pass options when calling OutputList::generateDoc().
Definition docoptions.h:24
bool autolinkSupport() const
Definition docoptions.h:33
DString exampleName() const
Definition docoptions.h:29
bool linkFromIndex() const
Definition docoptions.h:31
bool markdownSupport() const
Definition docoptions.h:32
bool indexWords() const
Definition docoptions.h:27
bool singleLine() const
Definition docoptions.h:30
bool isExample() const
Definition docoptions.h:28
StringMultiSet retvalsFound
Definition docparser_p.h:75
DocStyleChangeStack styleStack
Definition docparser_p.h:67
size_t includeFileLength
Definition docparser_p.h:89
DString includeFileText
Definition docparser_p.h:87
DocNodeStack nodeStack
Definition docparser_p.h:66
StringMultiSet paramsFound
Definition docparser_p.h:76
DefinitionStack copyStack
Definition docparser_p.h:69
const Definition * scope
Definition docparser_p.h:60
TokenInfo * token
Definition docparser_p.h:94
DocStyleChangeStack initialStyleStack
Definition docparser_p.h:68
SrcLangExt lang
Definition docparser_p.h:84
size_t includeFileOffset
Definition docparser_p.h:88
const MemberDef * memberDef
Definition docparser_p.h:79
DString exampleName
Definition docparser_p.h:81
const FileDef * currentFile
Definition util.h:86
bool checkCV
Definition util.h:87
const MemberDef * md
Definition util.h:95
bool found
Definition util.h:94
SrcLangExt
Definition types.h:207
uint8_t getUTF8CharNumBytes(char c)
Returns the number of bytes making up a single UTF8 character given the first byte in the sequence.
Definition utf8.cpp:27
DString inlineArgListToDoc(const ArgumentList &al)
Definition util.cpp:4874
DString filterTitle(const DString &title)
Definition util.cpp:4463
DString detab(const DString &s, size_t &refIndent)
Definition util.cpp:5203
bool resolveRef(const DString &scName, const DString &name, bool inSeeBlock, const Definition **resContext, const MemberDef **resMember, SrcLangExt lang, bool lookForSpecialization, const FileDef *currentFile, bool checkScope)
Definition util.cpp:2016
DString removeRedundantWhiteSpace(const DString &s)
Definition util.cpp:427
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2872
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4177
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1061
DString linkToText(SrcLangExt lang, const DString &link, bool ignoreDots)
Definition util.cpp:2273
DString relativePathToRoot(const DString &name)
Definition util.cpp:2921
DString argListToString(const ArgumentList &al, bool useCanonicalType, bool showDefVals)
Definition util.cpp:867
bool copyFile(const DString &src, const DString &dest)
Copies the contents of file with name src to the newly created file with name dest.
Definition util.cpp:4628
DString getLanguageSpecificSeparator(SrcLangExt lang, bool classScope)
Definition util.cpp:4638
GetDefResult getDefs(const GetDefInput &input)
Definition util.cpp:1874
DString findExampleFilePath(const DString &file, bool &ambig)
Definition util.cpp:2460
A bunch of utility functions.