Doxygen
Loading...
Searching...
No Matches
docnode.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#include "docnode.h"
17#include "docparser_p.h"
18#include "htmlentity.h"
19#include "configimpl.h"
20#include "configoptions.h"
21#include "emoji.h"
22#include "message.h"
23#include "doxygen.h"
24#include "cite.h"
25#include "util.h"
26#include "formula.h"
27#include "markdown.h"
28#include "pagedef.h"
29#include "namespacedef.h"
30#include "groupdef.h"
31#include "cmdmapper.h"
32#include "config.h"
33#include "vhdldocgen.h"
34#include "doctokenizer.h"
35#include "plantuml.h"
36#include "mermaid.h"
37#include "language.h"
38#include "datetime.h"
39#include "trace.h"
40#include "anchor.h"
41#include "aliases.h"
42#include "requirement.h"
43
44#if !ENABLE_DOCPARSER_TRACING
45#undef AUTO_TRACE
46#undef AUTO_TRACE_ADD
47#undef AUTO_TRACE_EXIT
48#define AUTO_TRACE(...) (void)0
49#define AUTO_TRACE_ADD(...) (void)0
50#define AUTO_TRACE_EXIT(...) (void)0
51#endif
52
53#define INTERNAL_ASSERT(x) do {} while(0)
54//#define INTERNAL_ASSERT(x) if (!(x)) TRACE("INTERNAL_ASSERT({}) failed retval={:#x}: file={} line={}",#x,retval,__FILE__,__LINE__)
55
56//---------------------------------------------------------------------------
57
58static const char *g_sectionLevelToName[] =
59{
60 "page",
61 "section",
62 "subsection",
63 "subsubsection",
64 "paragraph",
65 "subparagraph"
66};
67
68
69//---------------------------------------------------------------------------
70
72 "uml", "bpm", "wire", "dot", "ditaa",
73 "salt", "math", "latex", "gantt", "mindmap",
74 "wbs", "yaml", "creole", "json", "flow",
75 "board", "git", "hcl", "regex", "ebnf",
76 "files", "chen", "chronology", "chart", "nwdiag",
77 "packetdiag", "project", "sprites"
78};
79
80//---------------------------------------------------------------------------
81
82// replaces { with < and } with > and also
83// replaces &gt; with < and &gt; with > within string s
84static void unescapeCRef(QCString &s)
85{
86 QCString result;
87 const char *p = s.data();
88 if (p)
89 {
90 char c = 0;
91 while ((c=*p++))
92 {
93 if (c=='{') c='<'; else if (c=='}') c='>';
94 result+=c;
95 }
96 }
97
98 result=substitute(result,"&lt;","<");
99 result=substitute(result,"&gt;",">");
100 s = result;
101}
102
103//---------------------------------------------------------------------------
104
105/*! Strips known html and tex extensions from \a text. */
107{
108 QCString result=text;
109 if (result.endsWith(".tex"))
110 {
111 result=result.left(result.length()-4);
112 }
114 {
115 result=result.left(result.length()-Doxygen::htmlFileExtension.length());
116 }
117 return result;
118}
119
120static void setParent(DocNodeVariant *n,DocNodeVariant *newParent)
121{
122 std::visit([&](auto &&x)->decltype(auto) { return x.setParent(newParent); }, *n);
123}
124
125//----------- DocStyleChange
126
128{
129 switch (m_style)
130 {
131 case DocStyleChange::Bold: return "b";
132 case DocStyleChange::Italic: return "em";
133 case DocStyleChange::Code: return "code";
134 case DocStyleChange::Center: return "center";
135 case DocStyleChange::Small: return "small";
136 case DocStyleChange::Cite: return "cite";
137 case DocStyleChange::Subscript: return "subscript";
138 case DocStyleChange::Superscript: return "superscript";
139 case DocStyleChange::Preformatted: return "pre";
140 case DocStyleChange::Div: return "div";
141 case DocStyleChange::Span: return "span";
142 case DocStyleChange::Strike: return "strike";
143 case DocStyleChange::S: return "s";
144 case DocStyleChange::Del: return "del";
145 case DocStyleChange::Underline: return "u";
146 case DocStyleChange::Ins: return "ins";
147 case DocStyleChange::Kbd: return "kbd";
148 case DocStyleChange::Typewriter: return "tt";
149 }
150 return "<invalid>";
151}
152
153//----------- DocSymbol
154
159
160//----------- DocEmoji
161
163 DocNode(parser,parent), m_symName(symName), m_index(-1)
164{
165 QCString locSymName = symName;
166 size_t len=locSymName.length();
167 if (len>0)
168 {
169 if (locSymName.at(len-1)!=':') locSymName.append(":");
170 if (locSymName.at(0)!=':') locSymName.prepend(":");
171 }
172 m_symName = locSymName;
174 if (m_index==-1)
175 {
176 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Found unsupported emoji symbol '{}'",m_symName);
177 }
178}
179
180//---------------------------------------------------------------------------
181
184{
185 //printf("new word %s url=%s\n",qPrint(word),qPrint(parser->context.searchUrl));
186 if (Doxygen::searchIndex.enabled() && !parser->context.searchUrl.isEmpty())
187 {
188 Doxygen::searchIndex.addWord(word,false);
189 }
190}
191
192//---------------------------------------------------------------------------
193
195 const QCString &ref,const QCString &file,
196 const QCString &anchor,const QCString &tooltip) :
200{
201 //printf("DocLinkedWord: new word %s url=%s tooltip='%s'\n",
202 // qPrint(word),qPrint(parser->context.searchUrl),qPrint(tooltip));
203 if (Doxygen::searchIndex.enabled() && !parser->context.searchUrl.isEmpty())
204 {
205 Doxygen::searchIndex.addWord(word,false);
206 }
207}
208
209//---------------------------------------------------------------------------
210
212{
213 if (id.isEmpty())
214 {
215 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Empty anchor label");
216 return;
217 }
218
220 QCString anchorPrefix = ct.anchorPrefix();
221 if (id.left(anchorPrefix.length()) == anchorPrefix)
222 {
223 const CiteInfo *cite = ct.find(id.mid(anchorPrefix.length()));
224 if (cite)
225 {
227 m_anchor = id;
228 }
229 else
230 {
231 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid cite anchor id '{}'",id);
232 m_anchor = "invalid";
233 m_file = "invalid";
234 }
235 }
236 else if (newAnchor) // found <a name="label">
237 {
238 m_anchor = id;
239 }
240 else // found \anchor label
241 {
242 const SectionInfo *sec = SectionManager::instance().find(id);
243 if (sec)
244 {
245 //printf("Found anchor %s\n",qPrint(id));
246 m_file = sec->fileName();
247 m_anchor = sec->label();
248 }
249 else
250 {
251 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid anchor id '{}'",id);
252 m_anchor = "invalid";
253 m_file = "invalid";
254 }
255 }
256}
257
258//---------------------------------------------------------------------------
259
266
267
268//---------------------------------------------------------------------------
269
271{
272 AUTO_TRACE("file={} text={}",m_file,Trace::trunc(m_text));
273 switch(m_type)
274 {
275 case DontIncWithLines:
276 // fall through
277 case IncWithLines:
278 // fall through
279 case Include:
280 // fall through
281 case DontInclude:
290 //printf("parser->context.includeFile=<<%s>>\n",qPrint(parser->context.includeFileText));
291 break;
292 case VerbInclude:
293 // fall through
294 case HtmlInclude:
295 case LatexInclude:
301 break;
302 case Snippet:
303 case SnippetWithLines:
305 // check here for the existence of the blockId inside the file, so we
306 // only generate the warning once.
307 int count = 0;
308 if (!m_blockId.isEmpty() && (count=m_text.contains(m_blockId.data()))!=2)
309 {
310 warn_doc_error(parser()->context.fileName,
311 parser()->tokenizer.getLineNr(),
312 "block marked with {} for \\snippet should appear twice in file {}, found it {:d} times",
313 m_blockId,m_file,count);
314 }
315 break;
316 }
317}
318
319//---------------------------------------------------------------------------
320
322{
323 if (parser()->context.includeFileName.isEmpty())
324 {
325 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
326 "No previous '\\include' or '\\dontinclude' command for '\\{}' present",
327 typeAsString());
328 }
329 bool found = false;
330
332 const char *p = parser()->context.includeFileText.data();
333 size_t l = parser()->context.includeFileLength;
334 size_t o = parser()->context.includeFileOffset;
335 int il = parser()->context.includeFileLine;
336 AUTO_TRACE("text={} off={} len={}",Trace::trunc(p),o,l);
337 size_t so = o, bo = 0;
338 bool nonEmpty = FALSE;
339 switch(type())
340 {
341 case Line:
342 while (o<l)
343 {
344 char c = p[o];
345 if (c=='\n')
346 {
348 if (nonEmpty) break; // we have a pattern to match
349 so=o+1; // no pattern, skip empty line
350 }
351 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
352 {
353 nonEmpty=TRUE;
354 }
355 o++;
356 }
357 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=-1)
358 {
359 m_line = il;
361 found = true;
362 AUTO_TRACE_ADD("\\line {}",Trace::trunc(m_text));
363 }
364 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
367 break;
368 case SkipLine:
369 while (o<l)
370 {
371 so=o;
372 while (o<l)
373 {
374 char c = p[o];
375 if (c=='\n')
376 {
378 if (nonEmpty) break; // we have a pattern to match
379 so=o+1; // no pattern, skip empty line
380 }
381 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
382 {
383 nonEmpty=TRUE;
384 }
385 o++;
386 }
387 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=-1)
388 {
389 m_line = il;
391 found = true;
392 AUTO_TRACE_ADD("\\skipline {}",Trace::trunc(m_text));
393 break;
394 }
395 o++; // skip new line
396 }
397 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
400 break;
401 case Skip:
402 while (o<l)
403 {
404 so=o;
405 while (o<l)
406 {
407 char c = p[o];
408 if (c=='\n')
409 {
411 if (nonEmpty) break; // we have a pattern to match
412 so=o+1; // no pattern, skip empty line
413 }
414 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
415 {
416 nonEmpty=TRUE;
417 }
418 o++;
419 }
420 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=-1)
421 {
422 found = true;
423 break;
424 }
425 o++; // skip new line
426 }
427 parser()->context.includeFileOffset = so; // set pointer to start of new line
430 break;
431 case Until:
432 bo=o;
433 while (o<l)
434 {
435 so=o;
436 while (o<l)
437 {
438 char c = p[o];
439 if (c=='\n')
440 {
442 if (nonEmpty) break; // we have a pattern to match
443 so=o+1; // no pattern, skip empty line
444 }
445 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
446 {
447 nonEmpty=TRUE;
448 }
449 o++;
450 }
451 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=-1)
452 {
453 m_line = il;
455 found = true;
456 AUTO_TRACE_ADD("\\until {}",Trace::trunc(m_text));
457 break;
458 }
459 o++; // skip new line
460 }
461 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
464 break;
465 }
466 if (!found)
467 {
468 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
469 "referenced pattern '{}' for command '\\{}' not found",m_pattern,typeAsString());
470 }
471}
472
473//---------------------------------------------------------------------------
474
479
481{
483 if (refList && refList->isEnabled())
484 {
485 RefItem *item = refList->find(m_id);
486 ASSERT(item!=nullptr);
487 if (item)
488 {
489 if (parser()->context.memberDef && parser()->context.memberDef->name().at(0)=='@')
490 {
491 m_file = "@"; // can't cross reference anonymous enum
492 m_anchor = "@";
493 }
494 else
495 {
496 m_file = refList->fileName();
497 m_anchor = item->anchor();
498 }
499 m_title = refList->sectionTitle();
500 //printf("DocXRefItem: file=%s anchor=%s title=%s\n",
501 // qPrint(m_file),qPrint(m_anchor),qPrint(m_title));
502
503 if (!item->text().isEmpty())
504 {
505 parser()->pushContext();
507 parser()->popContext();
508 }
509 }
510 return TRUE;
511 }
512 return FALSE;
513}
514
515//---------------------------------------------------------------------------
516
518 m_relPath(parser->context.relPath)
519{
520 const Formula *formula = FormulaManager::instance().findFormula(id);
521 if (formula && !formula->text().isEmpty())
522 {
523 m_id = id;
524 m_name.sprintf("form_%d",m_id);
525 m_text = formula->text();
526 }
527 else // wrong \_form#<n> command
528 {
529 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Wrong formula id {:d}",id);
530 m_id = -1;
531 }
532}
533
534//---------------------------------------------------------------------------
535
540
542{
543 AUTO_TRACE();
544 auto ns = AutoNodeStack(parser(),thisVariant());
545
547 Token tok = parser()->tokenizer.lex();
548 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
549 {
550 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
551 {
552 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"\\refitem");
553 }
554 tok = parser()->tokenizer.lex();
555 }
558
559 if (!m_target.isEmpty())
560 {
562 if (sec==nullptr && parser()->context.lang==SrcLangExt::Markdown) // lookup as markdown file
563 {
565 }
566 if (sec) // ref to section or anchor
567 {
568 // set defaults
569 m_ref = sec->ref();
572 m_anchor = sec->label();
573 m_isSubPage = false;
574 // adjust if needed
575 switch (sec->type().level())
576 {
578 {
580 m_isSubPage = pd && pd->hasParentPage();
581 if (!m_isSubPage)
582 {
583 m_anchor="";
584 }
585 }
586 break;
589 break;
592 break;
595 break;
596 default:
597 break;
598 }
599 //printf("m_ref=%s,m_file=%s,type=%d\n",
600 // qPrint(m_ref),qPrint(m_file),m_refType);
601 }
602 else
603 {
604 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to unknown section {}",m_target);
605 }
606 }
607 else
608 {
609 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to empty target");
610 }
611}
612
613//---------------------------------------------------------------------------
614
616{
617 AUTO_TRACE();
618 auto ns = AutoNodeStack(parser(),thisVariant());
619
620 Token tok=parser()->tokenizer.lex();
621 // skip white space
622 while (tok.is_any_of(TokenRetval::TK_WHITESPACE, TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
623 // handle items
624 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
625 {
626 if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS))
627 {
628 switch (Mappers::cmdMapper->map(parser()->context.token->name))
629 {
631 {
632 tok=parser()->tokenizer.lex();
633 if (!tok.is(TokenRetval::TK_WHITESPACE))
634 {
635 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\refitem command");
636 break;
637 }
638 tok=parser()->tokenizer.lex();
639 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
640 {
641 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\refitem",
642 tok.to_string());
643 break;
644 }
645
648 }
649 break;
651 return;
652 default:
653 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' as part of a \\secreflist",
654 tok.command_to_char(),qPrint(parser()->context.token->name));
655 return;
656 }
657 }
658 else if (tok.is(TokenRetval::TK_WHITESPACE))
659 {
660 // ignore whitespace
661 }
662 else
663 {
664 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {} inside section reference list",
665 tok.to_string());
666 return;
667 }
668 tok=parser()->tokenizer.lex();
669 }
670
671}
672
673//---------------------------------------------------------------------------
674
677{
678 int i=ref.find('#');
679 if (i!=-1)
680 {
681 m_anchor = ref.right(static_cast<int>(ref.length())-i-1);
682 m_file = ref.left(i);
683 }
684 else
685 {
686 m_file = ref;
687 }
688}
689
691{
692 AUTO_TRACE();
693 auto ns = AutoNodeStack(parser(),thisVariant());
694
695 Token tok = parser()->tokenizer.lex();
696 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
697 {
698 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
699 {
701 }
702 tok=parser()->tokenizer.lex();
703 }
704
706}
707
708//---------------------------------------------------------------------------
709
712{
713 const Definition *compound = nullptr;
715 AUTO_TRACE("target='{}',context='{}'",target,context);
716 ASSERT(!target.isEmpty());
717 m_relPath = parser->context.relPath;
718 auto lang = parser->context.lang;
719 const SectionInfo *sec = SectionManager::instance().find(parser->context.prefix+target);
720 if (sec==nullptr && !parser->context.prefix.isEmpty())
721 {
722 sec = SectionManager::instance().find(target);
723 }
724
725 if (sec==nullptr && getLanguageFromFileName(target)==SrcLangExt::Markdown) // lookup as markdown file
726 {
728 }
729 if (sec) // ref to section or anchor
730 {
731 PageDef *pd = nullptr;
732 int secLevel = sec->type().level();
733 if (secLevel==SectionType::Page)
734 {
735 pd = Doxygen::pageLinkedMap->find(target);
736 }
737 m_text = sec->title();
738 if (m_text.isEmpty()) m_text = sec->label();
739
740 m_ref = sec->ref();
742 if (secLevel==SectionType::Anchor)
743 {
745 }
746 else if (secLevel==SectionType::Table)
747 {
749 }
750 else if (secLevel==SectionType::Requirement)
751 {
753 if (!Config_getBool(GENERATE_REQUIREMENTS))
754 {
755 m_file.clear(); // prevent link when there is no requirements page
756 }
757 }
758 else
759 {
761 }
762 m_isSubPage = pd && pd->hasParentPage();
763 if (secLevel!=SectionType::Page || m_isSubPage)
764 {
765 m_anchor = pd ? pd->getOutputFileBase() : sec->label();
766 }
767 m_sectionType = sec->type();
768 //printf("m_text=%s,m_ref=%s,m_file=%s,type=%d\n",
769 // qPrint(m_text),qPrint(m_ref),qPrint(m_file),m_refType);
770 AUTO_TRACE_EXIT("section");
771 return;
772 }
773 else if (Config_getBool(IMPLICIT_DIR_DOCS) && target.lower().endsWith("/readme.md"))
774 {
775 QCString dirTarget = target.left(target.length() - 9); // strip readme.md part
776 const auto &dd = Doxygen::dirLinkedMap->find(dirTarget);
777 if (dd)
778 {
779 m_text = target;
780 m_file = dd->getOutputFileBase();
781 AUTO_TRACE_EXIT("directory");
782 return;
783 }
784 }
785 else if (resolveLink(context,target,true,&compound,anchor,lang,parser->context.prefix))
786 {
787 bool isFile = compound ?
788 (compound->definitionType()==Definition::TypeFile ||
790 FALSE;
791 if (compound && lang==SrcLangExt::Markdown) lang = compound->getLanguage();
792 m_text = linkToText(lang,target,isFile);
794 if (compound && compound->isLinkable()) // ref to compound
795 {
796 if (anchor.isEmpty() && /* compound link */
797 compound->definitionType()==Definition::TypeGroup && /* is group */
798 !toGroupDef(compound)->groupTitle().isEmpty() /* with title */
799 )
800 {
801 m_text=(toGroupDef(compound))->groupTitle(); // use group's title as link
802 }
803 else if (compound->definitionType()==Definition::TypeMember &&
804 toMemberDef(compound)->isObjCMethod())
805 {
806 // Objective C Method
807 const MemberDef *member = toMemberDef(compound);
808 bool localLink = parser->context.memberDef ? member->getClassDef()==parser->context.memberDef->getClassDef() : FALSE;
809 m_text = member->objCMethodName(localLink,parser->context.inSeeBlock);
810 }
811 else if (Config_getBool(HIDE_SCOPE_NAMES))
812 {
813 int funcPos = m_text.find('(');
814 if (funcPos!=-1) // see issue #11834
815 {
816 m_text=stripScope(m_text.left(funcPos))+m_text.mid(funcPos);
817 }
818 else
819 {
821 }
822 }
823
824 m_file = compound->getOutputFileBase();
825 m_ref = compound->getReference();
826 //printf("isFile=%d compound=%s (%d)\n",isFile,qPrint(compound->name()),
827 // compound->definitionType());
828 AUTO_TRACE_EXIT("compound");
829 return;
830 }
831 else if (compound && compound->definitionType()==Definition::TypeFile &&
832 toFileDef(compound)->generateSourceFile()
833 ) // undocumented file that has source code we can link to
834 {
835 m_file = compound->getSourceFileBase();
836 m_ref = compound->getReference();
837 AUTO_TRACE_EXIT("source");
838 return;
839 }
840 else
841 {
842 AUTO_TRACE_EXIT("compound '{}' not linkable",compound?compound->name():"none");
843 }
844 }
845 m_text = target;
846 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve reference to '{}' for \\ref command",
847 target);
848}
849
851{
852 for (auto &&elem : elements)
853 {
854 emplace_back(std::move(elem));
855 }
856 elements.clear();
857}
858
859static void flattenParagraphs(DocNodeVariant *root,DocNodeList &children)
860{
861 DocNodeList newChildren;
862 for (auto &dn : children)
863 {
864 DocPara *para = std::get_if<DocPara>(&dn);
865 if (para)
866 {
867 //// move the children of the paragraph to the end of the newChildren list
868 newChildren.move_append(para->children());
869 }
870 }
871
872 // replace the children list by the newChildren list
873 children.clear();
874 children.move_append(newChildren);
875 // reparent the children
876 for (auto &cn : children)
877 {
878 setParent(&cn,root);
879 // we also need to set the parent for each child of cn, as cn's address may have changed.
880 auto opt_children = call_method_children(&cn);
881 if (opt_children)
882 {
883 for (auto &ccn : *opt_children)
884 {
885 setParent(&ccn,&cn);
886 }
887 }
888 }
889}
890
891void DocRef::parse(char cmdChar,const QCString &cmdName)
892{
893 AUTO_TRACE();
894 auto ns = AutoNodeStack(parser(),thisVariant());
895 char cmdCharStr[2] = { cmdChar, 0 };
896
897 Token tok = parser()->tokenizer.lex();
898 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
899 {
900 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
901 {
902 switch (tok.value())
903 {
904 case TokenRetval::TK_HTMLTAG:
905 break;
906 default:
907 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),cmdCharStr+cmdName);
908 break;
909 }
910 }
911 tok=parser()->tokenizer.lex();
912 }
913
914 if (children().empty() && !m_text.isEmpty())
915 {
916 QCString text = m_text;
917 if (parser()->context.insideHtmlLink)
918 {
919 // we already in a link/title only output anchor
920 text = m_anchor;
921 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
922 "Potential recursion while resolving {:c}{} command!",cmdChar,cmdName);
923 }
925 parser()->pushContext();
927 parser()->popContext();
931 }
932
934}
935
936//---------------------------------------------------------------------------
937
939{
940 size_t numBibFiles = Config_getList(CITE_BIB_FILES).size();
941 //printf("DocCite::DocCite(target=%s)\n",qPrint(target));
942 ASSERT(!target.isEmpty());
943 m_relPath = parser->context.relPath;
945 const CiteInfo *cite = ct.find(target);
946 //printf("cite=%p text='%s' numBibFiles=%d\n",cite,cite?qPrint(cite->text):"<null>",numBibFiles);
947 m_option = opt;
949 if (numBibFiles>0 && cite && !cite->text().isEmpty()) // ref to citation
950 {
951 m_ref = "";
952 m_anchor = ct.anchorPrefix()+cite->label();
954 //printf("CITE ==> m_text=%s,m_ref=%s,m_file=%s,m_anchor=%s\n",
955 // qPrint(m_text),qPrint(m_ref),qPrint(m_file),qPrint(m_anchor));
956 return;
957 }
958 if (numBibFiles==0)
959 {
960 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"\\cite command found but no bib files specified via CITE_BIB_FILES!");
961 }
962 else if (cite==nullptr)
963 {
964 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve reference to '{}' for \\cite command",
965 target);
966 }
967 else
968 {
969 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"\\cite command to '{}' does not have an associated number",
970 target);
971 }
972}
973
975{
976 QCString txt;
977 auto opt = m_option;
979 const CiteInfo *citeInfo = ct.find(m_target);
980
981 if (!opt.noPar()) txt += "[";
982
983 if (citeInfo)
984 {
985 if (opt.isNumber()) txt += citeInfo->text();
986 else if (opt.isShortAuthor()) txt += citeInfo->shortAuthor();
987 else if (opt.isYear()) txt += citeInfo->year();
988 }
989
990 if (!opt.noPar()) txt += "]";
991 return txt;
992}
993
994
995//---------------------------------------------------------------------------
996
998{
999 const Definition *compound = nullptr;
1001 m_refText = target;
1002 m_relPath = parser->context.relPath;
1003 if (!m_refText.isEmpty() && m_refText.at(0)=='#')
1004 {
1005 m_refText = m_refText.right(m_refText.length()-1);
1006 }
1007 if (resolveLink(parser->context.context,stripKnownExtensions(target),
1008 parser->context.inSeeBlock,&compound,anchor,
1009 parser->context.lang,parser->context.prefix))
1010 {
1011 m_anchor = anchor;
1012 if (compound && compound->isLinkable())
1013 {
1014 m_file = compound->getOutputFileBase();
1015 m_ref = compound->getReference();
1016 }
1017 else if (compound && compound->definitionType()==Definition::TypeFile &&
1018 (toFileDef(compound))->generateSourceFile()
1019 ) // undocumented file that has source code we can link to
1020 {
1021 m_file = compound->getSourceFileBase();
1022 m_ref = compound->getReference();
1023 }
1024 return;
1025 }
1026
1027 // bogus link target
1028 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve link to '{}' for \\link command",
1029 target);
1030}
1031
1032
1033QCString DocLink::parse(bool isJavaLink,bool isXmlLink)
1034{
1035 AUTO_TRACE();
1036 QCString result;
1037 auto ns = AutoNodeStack(parser(),thisVariant());
1038
1039 Token tok = parser()->tokenizer.lex();
1040 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1041 {
1042 if (!parser()->defaultHandleToken(thisVariant(),tok,children(),FALSE))
1043 {
1044 switch (tok.value())
1045 {
1046 case TokenRetval::TK_COMMAND_AT:
1047 // fall through
1048 case TokenRetval::TK_COMMAND_BS:
1049 switch (Mappers::cmdMapper->map(parser()->context.token->name))
1050 {
1052 if (isJavaLink)
1053 {
1054 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"'{{@link...' ended with '{:c}{}' command",
1055 tok.command_to_char(),parser()->context.token->name);
1056 }
1057 goto endlink;
1058 default:
1059 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' as part of a \\link",
1060 tok.command_to_char(),parser()->context.token->name);
1061 break;
1062 }
1063 break;
1064 case TokenRetval::TK_SYMBOL:
1065 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a \\link",
1066 parser()->context.token->name);
1067 break;
1068 case TokenRetval::TK_HTMLTAG:
1069 if (parser()->context.token->name!="see" || !isXmlLink)
1070 {
1071 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected xml/html command {} found as part of a \\link",
1072 parser()->context.token->name);
1073 }
1074 goto endlink;
1075 case TokenRetval::TK_LNKWORD:
1076 case TokenRetval::TK_WORD:
1077 if (isJavaLink) // special case to detect closing }
1078 {
1080 int p = 0;
1081 if (w=="}")
1082 {
1083 goto endlink;
1084 }
1085 else if ((p=w.find('}'))!=-1)
1086 {
1087 int l = static_cast<int>(w.length());
1089 if (p<l-1) // something left after the } (for instance a .)
1090 {
1091 result=w.right(l-p-1);
1092 }
1093 goto endlink;
1094 }
1095 }
1097 break;
1098 default:
1099 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",tok.to_string());
1100 break;
1101 }
1102 }
1103 tok = parser()->tokenizer.lex();
1104 }
1105 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1106 {
1107 warn_doc_error(parser()->context.fileName,
1108 parser()->tokenizer.getLineNr(),
1109 "Unexpected end of comment while inside link command");
1110 }
1111endlink:
1112
1113 if (children().empty()) // no link text
1114 {
1116 }
1117
1119 return result;
1120}
1121
1122
1123//---------------------------------------------------------------------------
1124
1131
1133{
1134 bool ok = false;
1136
1137 bool ambig = false;
1139 if (fd==nullptr && !p->name.endsWith(".dot")) // try with .dot extension as well
1140 {
1141 fd = findFileDef(Doxygen::dotFileNameLinkedMap,p->name+".dot",ambig);
1142 }
1143 if (fd)
1144 {
1145 p->file = fd->absFilePath();
1146 ok = true;
1147 if (ambig)
1148 {
1149 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dot file name '{}' is ambiguous.\n"
1150 "Possible candidates:\n{}",p->name,
1152 );
1153 }
1154 }
1155 else
1156 {
1157 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dot file '{}' is not found "
1158 "in any of the paths specified via DOTFILE_DIRS!",p->name);
1159 }
1160 return ok;
1161}
1162
1169
1171{
1172 bool ok = false;
1174
1175 bool ambig = false;
1177 if (fd==nullptr && !p->name.endsWith(".msc")) // try with .msc extension as well
1178 {
1179 fd = findFileDef(Doxygen::mscFileNameLinkedMap,p->name+".msc",ambig);
1180 }
1181 if (fd)
1182 {
1183 p->file = fd->absFilePath();
1184 ok = true;
1185 if (ambig)
1186 {
1187 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included msc file name '{}' is ambiguous.\n"
1188 "Possible candidates:\n{}",qPrint(p->name),
1190 );
1191 }
1192 }
1193 else
1194 {
1195 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included msc file '{}' is not found "
1196 "in any of the paths specified via MSCFILE_DIRS!",p->name);
1197 }
1198 return ok;
1199}
1200
1201//---------------------------------------------------------------------------
1202
1209
1211{
1212 bool ok = false;
1214
1215 bool ambig = false;
1217 if (fd==nullptr && !p->name.endsWith(".dia")) // try with .dia extension as well
1218 {
1219 fd = findFileDef(Doxygen::diaFileNameLinkedMap,p->name+".dia",ambig);
1220 }
1221 if (fd)
1222 {
1223 p->file = fd->absFilePath();
1224 ok = true;
1225 if (ambig)
1226 {
1227 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dia file name '{}' is ambiguous.\n"
1228 "Possible candidates:\n{}",p->name,
1230 );
1231 }
1232 }
1233 else
1234 {
1235 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dia file '{}' is not found "
1236 "in any of the paths specified via DIAFILE_DIRS!",p->name);
1237 }
1238 return ok;
1239}
1240//---------------------------------------------------------------------------
1241
1248
1250{
1251 bool ok = false;
1253
1254 bool ambig = false;
1256 if (fd==nullptr && !p->name.endsWith(".puml")) // try with .puml extension as well
1257 {
1258 fd = findFileDef(Doxygen::plantUmlFileNameLinkedMap,p->name+".puml",ambig);
1259 if (fd==nullptr && !p->name.endsWith(".pu")) // try with .pu extension as well
1260 {
1261 fd = findFileDef(Doxygen::plantUmlFileNameLinkedMap,p->name+".pu",ambig);
1262 }
1263 }
1264 if (fd)
1265 {
1266 p->file = fd->absFilePath();
1267 ok = true;
1268 if (ambig)
1269 {
1270 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included uml file name '{}' is ambiguous.\n"
1271 "Possible candidates:\n{}",p->name,
1273 );
1274 }
1275 }
1276 else
1277 {
1278 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included uml file '{}' is not found "
1279 "in any of the paths specified via PLANTUMLFILE_DIRS!",p->name);
1280 }
1281 return ok;
1282}
1283
1284//---------------------------------------------------------------------------
1285
1292
1294{
1295 bool ok = false;
1297
1298 bool ambig = false;
1300 if (fd==nullptr && !p->name.endsWith(".mmd")) // try with .mmd extension as well
1301 {
1302 fd = findFileDef(Doxygen::mermaidFileNameLinkedMap,p->name+".mmd",ambig);
1303 }
1304 if (fd)
1305 {
1306 p->file = fd->absFilePath();
1307 ok = true;
1308 if (ambig)
1309 {
1310 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included mermaid file name '{}' is ambiguous.\n"
1311 "Possible candidates:\n{}",p->name,
1313 );
1314 }
1315 }
1316 else
1317 {
1318 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included mermaid file '{}' is not found "
1319 "in any of the paths specified via MERMAIDFILE_DIRS!",p->name);
1320 }
1321 return ok;
1322}
1323
1324//---------------------------------------------------------------------------
1325
1329
1331{
1332 AUTO_TRACE();
1333 auto ns = AutoNodeStack(parser(),thisVariant());
1334
1336 Token tok = parser()->tokenizer.lex();
1337 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1338 {
1339 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1340 {
1341 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"\\vhdlflow");
1342 }
1343 tok = parser()->tokenizer.lex();
1344 }
1345 parser()->tokenizer.lex();
1346
1349
1350 VhdlDocGen::createFlowChart(parser()->context.memberDef);
1351}
1352
1353
1354//---------------------------------------------------------------------------
1355
1357 Type t,const QCString &url, bool inlineImage) :
1358 DocCompoundNode(parser,parent), p(std::make_unique<Private>(attribs, name, t, parser->context.relPath, url, inlineImage))
1359{
1360}
1361
1363{
1364 QCString locName = p->url.isEmpty() ? p->name : p->url;
1365 int len = static_cast<int>(locName.length());
1366 int fnd = locName.find('?'); // ignore part from ? until end
1367 if (fnd==-1) fnd=len;
1368 return fnd>=4 && locName.mid(fnd-4,4)==".svg";
1369}
1370
1375
1376
1377//---------------------------------------------------------------------------
1378
1380{
1381 AUTO_TRACE();
1382 Token retval(TokenRetval::RetVal_OK);
1383 auto ns = AutoNodeStack(parser(),thisVariant());
1384
1385 Token tok = parser()->tokenizer.lex();
1386 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1387 {
1388 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1389 {
1390 switch (tok.value())
1391 {
1392 case TokenRetval::TK_HTMLTAG:
1393 {
1394 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1395 if (tagId==HtmlTagType::HTML_H1 && parser()->context.token->endTag) // found </h1> tag
1396 {
1397 if (m_level!=1)
1398 {
1399 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h1>",
1400 m_level);
1401 }
1402 goto endheader;
1403 }
1404 else if (tagId==HtmlTagType::HTML_H2 && parser()->context.token->endTag) // found </h2> tag
1405 {
1406 if (m_level!=2)
1407 {
1408 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h2>",
1409 m_level);
1410 }
1411 goto endheader;
1412 }
1413 else if (tagId==HtmlTagType::HTML_H3 && parser()->context.token->endTag) // found </h3> tag
1414 {
1415 if (m_level!=3)
1416 {
1417 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h3>",
1418 m_level);
1419 }
1420 goto endheader;
1421 }
1422 else if (tagId==HtmlTagType::HTML_H4 && parser()->context.token->endTag) // found </h4> tag
1423 {
1424 if (m_level!=4)
1425 {
1426 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h4>",
1427 m_level);
1428 }
1429 goto endheader;
1430 }
1431 else if (tagId==HtmlTagType::HTML_H5 && parser()->context.token->endTag) // found </h5> tag
1432 {
1433 if (m_level!=5)
1434 {
1435 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h5>",
1436 m_level);
1437 }
1438 goto endheader;
1439 }
1440 else if (tagId==HtmlTagType::HTML_H6 && parser()->context.token->endTag) // found </h6> tag
1441 {
1442 if (m_level!=6)
1443 {
1444 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h6>",
1445 m_level);
1446 }
1447 goto endheader;
1448 }
1449 else if (tagId==HtmlTagType::HTML_A)
1450 {
1451 if (!parser()->context.token->endTag)
1452 {
1453 parser()->handleAHref(thisVariant(),children(),parser()->context.token->attribs);
1454 }
1455 }
1456 else if (tagId==HtmlTagType::HTML_BR)
1457 {
1459 }
1460 else
1461 {
1462 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <h{:d}> context",
1463 parser()->context.token->endTag?"/":"",parser()->context.token->name,m_level);
1464 }
1465 }
1466 break;
1467 default:
1468 char tmp[20];
1469 qsnprintf(tmp,20,"<h%d> tag",m_level);
1471 }
1472 }
1473 tok = parser()->tokenizer.lex();
1474 }
1475 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1476 {
1477 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1478 " <h{:d}> tag",m_level);
1479 }
1480endheader:
1482 return retval;
1483}
1484//---------------------------------------------------------------------------
1485
1487{
1488 AUTO_TRACE();
1489 auto ns = AutoNodeStack(parser(),thisVariant());
1491 Token tok = parser()->tokenizer.lex();
1492 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1493 {
1494 // check of </summary>
1495 if (tok.value()==TokenRetval::TK_HTMLTAG &&
1496 (Mappers::htmlTagMapper->map(parser()->context.token->name))==HtmlTagType::XML_SUMMARY &&
1497 parser()->context.token->endTag
1498 )
1499 {
1500 break;
1501 }
1502 else if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS) &&
1503 (Mappers::cmdMapper->map(parser()->context.token->name)== CommandType::CMD_REF))
1504 {
1506 tok.command_to_char(),parser()->context.token->name);
1507 }
1508 else if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1509 {
1510 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"summary section");
1511 }
1512 tok = parser()->tokenizer.lex();
1513 }
1515 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1516 {
1517 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1518 " <summary> tag");
1519 }
1520}
1521
1522//---------------------------------------------------------------------------
1523
1525{
1526 AUTO_TRACE();
1527 Token retval(TokenRetval::TK_NONE);
1528 auto ns = AutoNodeStack(parser(),thisVariant());
1529
1530 // parse one or more paragraphs
1531 bool isFirst=TRUE;
1532 DocPara *par=nullptr;
1533 do
1534 {
1536 par = children().get_last<DocPara>();
1537 if (isFirst) { par->markFirst(); isFirst=FALSE; }
1538 retval=par->parse();
1539 }
1540 while (retval.is(TokenRetval::TK_NEWPARA));
1541 if (par) par->markLast();
1542
1543 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1544 {
1545 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <details> block");
1546 }
1547
1548 if (!summary())
1549 {
1550 HtmlAttribList summaryAttribs;
1552 DocHtmlSummary *summary = &std::get<DocHtmlSummary>(*m_summary);
1553 summary->children().append<DocWord>(parser(),thisVariant(),theTranslator->trDetails());
1554 }
1555 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1556 return retval.is(TokenRetval::RetVal_EndHtmlDetails) ? Token::make_RetVal_OK() : retval;
1557}
1558
1566
1567//---------------------------------------------------------------------------
1568
1570{
1571 AUTO_TRACE();
1572 Token retval(TokenRetval::RetVal_OK);
1573 auto ns = AutoNodeStack(parser(),thisVariant());
1574
1575 Token tok = parser()->tokenizer.lex();
1576 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1577 {
1578 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1579 {
1580 switch (tok.value())
1581 {
1582 case TokenRetval::TK_HTMLTAG:
1583 {
1584 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1585 if (tagId==HtmlTagType::HTML_A && parser()->context.token->endTag) // found </a> tag
1586 {
1587 goto endhref;
1588 }
1589 else if (tagId==HtmlTagType::HTML_BR)
1590 {
1592 }
1593 else
1594 {
1595 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <a href=...> context",
1596 parser()->context.token->endTag?"/":"",parser()->context.token->name);
1597 }
1598 }
1599 break;
1600 default:
1601 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"<a>..</a> block");
1602 break;
1603 }
1604 }
1605 tok = parser()->tokenizer.lex();
1606 }
1607 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1608 {
1609 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1610 " <a href=...> tag");
1611 }
1612endhref:
1614 return retval;
1615}
1616
1617//---------------------------------------------------------------------------
1618
1620{
1621 AUTO_TRACE();
1622 Token retval(TokenRetval::RetVal_OK);
1623 auto ns = AutoNodeStack(parser(),thisVariant());
1624
1625 // first parse any number of paragraphs
1626 bool isFirst=TRUE;
1627 DocPara *lastPar=nullptr;
1628 do
1629 {
1631 DocPara *par = children().get_last<DocPara>();
1632 if (isFirst) { par->markFirst(); isFirst=FALSE; }
1633 retval=par->parse();
1634 if (!par->isEmpty())
1635 {
1636 if (lastPar) lastPar->markLast(FALSE);
1637 lastPar=par;
1638 }
1639 else
1640 {
1641 children().pop_back();
1642 }
1643 if (retval.is(TokenRetval::TK_LISTITEM))
1644 {
1645 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
1646 }
1647 } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF,
1648 TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection,
1649 TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph,
1650 TokenRetval::RetVal_EndInternal));
1651 if (lastPar) lastPar->markLast();
1652
1653 // then parse any number of level-n sections
1654 while ((level==1 && retval.is(TokenRetval::RetVal_Section)) ||
1655 (level==2 && retval.is(TokenRetval::RetVal_Subsection)) ||
1656 (level==3 && retval.is(TokenRetval::RetVal_Subsubsection)) ||
1657 (level==4 && retval.is(TokenRetval::RetVal_Paragraph)) ||
1658 (level==5 && retval.is(TokenRetval::RetVal_SubParagraph)) ||
1659 (level==6 && retval.is(TokenRetval::RetVal_SubSubParagraph))
1660 )
1661 {
1663 level,
1665 retval = children().get_last<DocSection>()->parse();
1666 }
1667
1668 if (retval.is(TokenRetval::RetVal_Internal))
1669 {
1670 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"\\internal command found inside internal section");
1671 }
1672
1673 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1674 return retval;
1675}
1676
1677//---------------------------------------------------------------------------
1678
1680{
1681 AUTO_TRACE();
1682 Token retval(TokenRetval::RetVal_OK);
1683 auto ns = AutoNodeStack(parser(),thisVariant());
1684 Token tok=parser()->tokenizer.lex();
1685 if (!tok.is(TokenRetval::TK_WHITESPACE))
1686 {
1687 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\addindex command");
1688 goto endindexentry;
1689 }
1691 m_entry="";
1692 tok = parser()->tokenizer.lex();
1693 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1694 {
1695 switch (tok.value())
1696 {
1697 case TokenRetval::TK_WHITESPACE:
1698 m_entry+=" ";
1699 break;
1700 case TokenRetval::TK_WORD:
1701 case TokenRetval::TK_LNKWORD:
1703 break;
1704 case TokenRetval::TK_SYMBOL:
1705 {
1706 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
1707 switch (s)
1708 {
1709 case HtmlEntityMapper::Sym_BSlash: m_entry+='\\'; break;
1710 case HtmlEntityMapper::Sym_At: m_entry+='@'; break;
1711 case HtmlEntityMapper::Sym_Less: m_entry+='<'; break;
1712 case HtmlEntityMapper::Sym_Greater: m_entry+='>'; break;
1713 case HtmlEntityMapper::Sym_Amp: m_entry+='&'; break;
1714 case HtmlEntityMapper::Sym_Dollar: m_entry+='$'; break;
1715 case HtmlEntityMapper::Sym_Hash: m_entry+='#'; break;
1716 case HtmlEntityMapper::Sym_Percent: m_entry+='%'; break;
1717 case HtmlEntityMapper::Sym_apos: m_entry+='\''; break;
1718 case HtmlEntityMapper::Sym_Quot: m_entry+='"'; break;
1719 case HtmlEntityMapper::Sym_lsquo: m_entry+='`'; break;
1720 case HtmlEntityMapper::Sym_rsquo: m_entry+='\''; break;
1721 case HtmlEntityMapper::Sym_ldquo: m_entry+="``"; break;
1722 case HtmlEntityMapper::Sym_rdquo: m_entry+="''"; break;
1723 case HtmlEntityMapper::Sym_ndash: m_entry+="--"; break;
1724 case HtmlEntityMapper::Sym_mdash: m_entry+="---"; break;
1725 default:
1726 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected symbol '{}' found as argument of \\addindex",parser()->context.token->name);
1727 break;
1728 }
1729 }
1730 break;
1731 case TokenRetval::TK_COMMAND_AT:
1732 // fall through
1733 case TokenRetval::TK_COMMAND_BS:
1734 switch (Mappers::cmdMapper->map(parser()->context.token->name))
1735 {
1736 case CommandType::CMD_BSLASH: m_entry+='\\'; break;
1737 case CommandType::CMD_AT: m_entry+='@'; break;
1738 case CommandType::CMD_LESS: m_entry+='<'; break;
1739 case CommandType::CMD_GREATER: m_entry+='>'; break;
1740 case CommandType::CMD_AMP: m_entry+='&'; break;
1741 case CommandType::CMD_DOLLAR: m_entry+='$'; break;
1742 case CommandType::CMD_HASH: m_entry+='#'; break;
1743 case CommandType::CMD_DCOLON: m_entry+="::"; break;
1744 case CommandType::CMD_PERCENT: m_entry+='%'; break;
1745 case CommandType::CMD_NDASH: m_entry+="--"; break;
1746 case CommandType::CMD_MDASH: m_entry+="---"; break;
1747 case CommandType::CMD_QUOTE: m_entry+='"'; break;
1748 case CommandType::CMD_PUNT: m_entry+='.'; break;
1749 case CommandType::CMD_PLUS: m_entry+='+'; break;
1750 case CommandType::CMD_MINUS: m_entry+='-'; break;
1751 case CommandType::CMD_EQUAL: m_entry+='='; break;
1752 case CommandType::CMD_EXCLAMATION: m_entry+='!'; break;
1753 case CommandType::CMD_QUESTION: m_entry+='?'; break;
1754 default:
1755 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command {} found as argument of \\addindex",
1756 parser()->context.token->name);
1757 break;
1758 }
1759 break;
1760 default:
1761 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",
1762 tok.to_string());
1763 break;
1764 }
1765 tok = parser()->tokenizer.lex();
1766 }
1768 m_entry = m_entry.stripWhiteSpace();
1769endindexentry:
1770 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1771 return retval;
1772}
1773
1774//---------------------------------------------------------------------------
1775
1778{
1780 for (const auto &opt : attribs)
1781 {
1782 if (opt.name=="id" && !opt.value.isEmpty()) // interpret id attribute as an anchor
1783 {
1784 const SectionInfo *sec = SectionManager::instance().find(opt.value);
1785 if (sec)
1786 {
1787 //printf("Found anchor %s\n",qPrint(id));
1788 m_file = sec->fileName();
1789 m_anchor = sec->label();
1791 }
1792 else
1793 {
1794 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid caption id '{}'",opt.value);
1795 }
1796 }
1797 else // copy attribute
1798 {
1799 m_attribs.push_back(opt);
1800 }
1801 }
1802}
1803
1805{
1806 AUTO_TRACE();
1807 Token retval = Token::make_TK_NONE();
1808 auto ns = AutoNodeStack(parser(),thisVariant());
1809 Token tok = parser()->tokenizer.lex();
1810 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1811 {
1812 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1813 {
1814 switch (tok.value())
1815 {
1816 case TokenRetval::TK_HTMLTAG:
1817 {
1818 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1819 if (tagId==HtmlTagType::HTML_CAPTION && parser()->context.token->endTag) // found </caption> tag
1820 {
1821 retval = Token::make_RetVal_OK();
1822 goto endcaption;
1823 }
1824 else
1825 {
1826 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <caption> context",
1827 parser()->context.token->endTag?"/":"",parser()->context.token->name);
1828 }
1829 }
1830 break;
1831 default:
1832 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"<caption> tag");
1833 break;
1834 }
1835 }
1836 tok = parser()->tokenizer.lex();
1837 }
1838 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1839 {
1840 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1841 " <caption> tag");
1842 }
1843endcaption:
1845 return retval;
1846}
1847
1848//---------------------------------------------------------------------------
1849
1851{
1852 AUTO_TRACE();
1853 Token retval = Token::make_RetVal_OK();
1854 auto ns = AutoNodeStack(parser(),thisVariant());
1855
1856 // parse one or more paragraphs
1857 bool isFirst=TRUE;
1858 DocPara *par=nullptr;
1859 do
1860 {
1862 par = children().get_last<DocPara>();
1863 if (isFirst) { par->markFirst(); isFirst=FALSE; }
1864 retval=par->parse();
1865 if (retval.is(TokenRetval::TK_HTMLTAG))
1866 {
1867 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1868 if (tagId==HtmlTagType::HTML_TD && parser()->context.token->endTag) // found </td> tag
1869 {
1870 retval = Token::make_TK_NEWPARA(); // ignore the tag
1871 }
1872 else if (tagId==HtmlTagType::HTML_TH && parser()->context.token->endTag) // found </th> tag
1873 {
1874 retval = Token::make_TK_NEWPARA(); // ignore the tag
1875 }
1876 }
1877 }
1878 while (retval.is_any_of(TokenRetval::TK_NEWPARA,TokenRetval::RetVal_EndParBlock));
1879 if (par) par->markLast();
1880
1881 return retval;
1882}
1883
1885{
1886 AUTO_TRACE();
1887 Token retval = Token::make_RetVal_OK();
1888 auto ns = AutoNodeStack(parser(),thisVariant());
1889
1890 // parse one or more paragraphs
1891 bool isFirst=TRUE;
1892 DocPara *par=nullptr;
1893 do
1894 {
1896 par = children().get_last<DocPara>();
1897 if (isFirst) { par->markFirst(); isFirst=FALSE; }
1898 retval=par->parse();
1899 if (retval.is(TokenRetval::TK_HTMLTAG))
1900 {
1901 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1902 if (tagId==HtmlTagType::XML_ITEM && parser()->context.token->endTag) // found </item> tag
1903 {
1904 retval = Token::make_TK_NEWPARA(); // ignore the tag
1905 }
1906 else if (tagId==HtmlTagType::XML_DESCRIPTION && parser()->context.token->endTag) // found </description> tag
1907 {
1908 retval = Token::make_TK_NEWPARA(); // ignore the tag
1909 }
1910 }
1911 }
1912 while (retval.is(TokenRetval::TK_NEWPARA));
1913 if (par) par->markLast();
1914
1915 return retval;
1916}
1917
1918uint32_t DocHtmlCell::rowSpan() const
1919{
1920 for (const auto &attr : attribs())
1921 {
1922 if (attr.name.lower()=="rowspan")
1923 {
1924 return attr.value.toUInt();
1925 }
1926 }
1927 return 0;
1928}
1929
1930uint32_t DocHtmlCell::colSpan() const
1931{
1932 for (const auto &attr : attribs())
1933 {
1934 if (attr.name.lower()=="colspan")
1935 {
1936 return std::max(1u,attr.value.toUInt());
1937 }
1938 }
1939 return 1;
1940}
1941
1943{
1944 for (const auto &attr : attribs())
1945 {
1946 QCString attrName = attr.name.lower();
1947 QCString attrValue = attr.value.lower();
1948 if (attrName=="align")
1949 {
1950 if (attrValue=="center")
1951 return Center;
1952 else if (attrValue=="right")
1953 return Right;
1954 else return Left;
1955 }
1956 else if (attrName=="class" && attrValue.startsWith("markdowntable"))
1957 {
1958 if (attrValue=="markdowntableheadcenter")
1959 return Center;
1960 else if (attrValue=="markdowntableheadright")
1961 return Right;
1962 else if (attrValue=="markdowntableheadleft")
1963 return Left;
1964 else if (attrValue=="markdowntableheadnone")
1965 return Center;
1966 else if (attrValue=="markdowntablebodycenter")
1967 return Center;
1968 else if (attrValue=="markdowntablebodyright")
1969 return Right;
1970 else if (attrValue=="markdowntablebodyleft")
1971 return Left;
1972 else if (attrValue=="markdowntablebodynone")
1973 return Left;
1974 else return Left;
1975 }
1976 }
1977 return Left;
1978}
1979
1981{
1982 for (const auto &attr : attribs())
1983 {
1984 QCString attrName = attr.name.lower();
1985 QCString attrValue = attr.value.lower();
1986 if (attrName=="valign")
1987 {
1988 if (attrValue=="top")
1989 return Top;
1990 else if (attrValue=="bottom")
1991 return Bottom;
1992 else if (attrValue=="middle")
1993 return Middle;
1994 else return Middle;
1995 }
1996 }
1997 return Middle;
1998}
1999
2000//---------------------------------------------------------------------------
2001
2003{ // a row is a table heading if all cells are marked as such
2004 bool heading=TRUE;
2005 for (const auto &n : children())
2006 {
2007 const DocHtmlCell *cell = std::get_if<DocHtmlCell>(&n);
2008 if (cell && !cell->isHeading())
2009 {
2010 heading = FALSE;
2011 break;
2012 }
2013 }
2014 return !children().empty() && heading;
2015}
2016
2018{
2019 AUTO_TRACE();
2020 // get next token
2021 Token tok=parser->tokenizer.lex();
2022 // skip whitespace and tbody, thead and tfoot tags
2023 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA,TokenRetval::TK_HTMLTAG,
2024 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS))
2025 {
2026 if (tok.is(TokenRetval::TK_HTMLTAG))
2027 {
2028 AUTO_TRACE_ADD("html_tag={}",parser->context.token->name);
2030 // skip over tbody, thead, tfoot tags
2031 if (tagId==HtmlTagType::HTML_TBODY ||
2032 tagId==HtmlTagType::HTML_THEAD ||
2034 {
2035 tok=parser->tokenizer.lex();
2036 }
2037 else
2038 {
2039 break;
2040 }
2041 }
2042 else if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS))
2043 {
2044 QCString cmdName=parser->context.token->name;
2045 AUTO_TRACE_ADD("command={}",cmdName);
2046 auto cmdType = Mappers::cmdMapper->map(cmdName);
2047 if (cmdType==CommandType::CMD_ILINE)
2048 {
2049 parser->tokenizer.pushState();
2050 parser->tokenizer.setStateILine();
2051 tok = parser->tokenizer.lex();
2052 if (!tok.is(TokenRetval::TK_WORD))
2053 {
2054 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"invalid argument for command '{:c}{}'",
2055 tok.command_to_char(),cmdName);
2056 }
2057 parser->tokenizer.popState();
2058 tok = parser->tokenizer.lex();
2059 }
2060 else
2061 {
2062 break;
2063 }
2064 }
2065 else
2066 {
2067 AUTO_TRACE_ADD("skip whitespace");
2068 tok=parser->tokenizer.lex();
2069 }
2070 }
2071 return tok;
2072}
2073
2074
2075
2076
2078{
2079 AUTO_TRACE();
2080 Token retval = Token::make_RetVal_OK();
2081 auto ns = AutoNodeStack(parser(),thisVariant());
2082
2083 bool isHeading=FALSE;
2084 bool isFirst=TRUE;
2085 DocHtmlCell *cell=nullptr;
2086
2088 // should find a html tag now
2089 if (tok.is(TokenRetval::TK_HTMLTAG))
2090 {
2091 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2092 if (tagId==HtmlTagType::HTML_TD && !parser()->context.token->endTag) // found <td> tag
2093 {
2094 }
2095 else if (tagId==HtmlTagType::HTML_TH && !parser()->context.token->endTag) // found <th> tag
2096 {
2098 }
2099 else // found some other tag
2100 {
2101 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but "
2102 "found <{}{}> instead!",parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2103 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2104 goto endrow;
2105 }
2106 }
2107 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2108 {
2109 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2110 " for a html description title");
2111 goto endrow;
2112 }
2113 else // token other than html token
2114 {
2115 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but found {} token instead!",
2116 tok.to_string());
2117 goto endrow;
2118 }
2119
2120 // parse one or more cells
2121 do
2122 {
2125 isHeading);
2126 cell = children().get_last<DocHtmlCell>();
2127 cell->markFirst(isFirst);
2128 isFirst=FALSE;
2129 retval=cell->parse();
2130 isHeading = retval.is(TokenRetval::RetVal_TableHCell);
2131 //printf("DocHtmlRow:retval=%s\n",retval.to_string());
2132 if (retval.is(TokenRetval::RetVal_EndTableCell))
2133 {
2134 // get next token
2135 retval = skipSpacesForTable(parser());
2136 //printf("DocHtmlRow:retval= next=%s name=%s endTag=%d\n",retval.to_string(),qPrint(parser()->context.token->name),parser()->context.token->endTag);
2137 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2138 if (tok.is(TokenRetval::TK_HTMLTAG))
2139 {
2140 if ((tagId==HtmlTagType::HTML_TD || tagId==HtmlTagType::HTML_TH) &&
2141 !parser()->context.token->endTag) // found new <td> or <td> tag
2142 {
2143 retval = Token::make_RetVal_TableCell();
2145 }
2146 else if (tagId==HtmlTagType::HTML_TR)
2147 {
2148 if (parser()->context.token->endTag) // found </tr> tag
2149 {
2150 retval = Token::make_RetVal_EndTableRow();
2151 }
2152 else // found <tr> tag
2153 {
2154 retval = Token::make_RetVal_TableRow();
2155 }
2156 }
2157 else if (tagId==HtmlTagType::HTML_TABLE && parser()->context.token->endTag) // found </table>
2158 {
2159 retval = Token::make_RetVal_EndTable();
2160 }
2161 else // found some other tag
2162 {
2163 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td>, <th> or <tr> tag but "
2164 "found <{}{}> instead!",parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2165 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2166 goto endrow;
2167 }
2168 }
2169 else // token other than html token
2170 {
2171 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td>, <th> or <tr> tag but found {} token instead!",
2172 tok.to_string());
2173 goto endrow;
2174 }
2175 }
2176 }
2177 while (retval.is_any_of(TokenRetval::RetVal_TableCell,TokenRetval::RetVal_TableHCell));
2178 cell->markLast(TRUE);
2179
2180endrow:
2181 return retval;
2182}
2183
2185{
2186 AUTO_TRACE();
2187 Token retval = Token::make_RetVal_OK();
2188 auto ns = AutoNodeStack(parser(),thisVariant());
2189
2190 bool isFirst=TRUE;
2191 DocHtmlCell *cell=nullptr;
2192
2193 // get next token
2194 Token tok=parser()->tokenizer.lex();
2195 // skip whitespace
2196 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2197 // should find a html tag now
2198 if (tok.is(TokenRetval::TK_HTMLTAG))
2199 {
2200 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2201 if (tagId==HtmlTagType::XML_TERM && !parser()->context.token->endTag) // found <term> tag
2202 {
2203 }
2204 else if (tagId==HtmlTagType::XML_DESCRIPTION && !parser()->context.token->endTag) // found <description> tag
2205 {
2206 }
2207 else // found some other tag
2208 {
2209 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <term> or <description> tag but "
2210 "found <{}> instead!",parser()->context.token->name);
2211 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2212 goto endrow;
2213 }
2214 }
2215 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2216 {
2217 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2218 " for a html description title");
2219 goto endrow;
2220 }
2221 else // token other than html token
2222 {
2223 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but found {} token instead!",
2224 tok.to_string());
2225 goto endrow;
2226 }
2227
2228 do
2229 {
2231 cell = children().get_last<DocHtmlCell>();
2232 cell->markFirst(isFirst);
2233 isFirst=FALSE;
2234 retval=cell->parseXml();
2235 }
2236 while (retval.is_any_of(TokenRetval::RetVal_TableCell,TokenRetval::RetVal_TableHCell));
2237 cell->markLast(TRUE);
2238
2239endrow:
2240 return retval;
2241}
2242
2243//---------------------------------------------------------------------------
2244
2246{
2247 return m_caption!=nullptr;
2248}
2249
2251{
2252 return m_caption.get();
2253}
2254
2256{
2257 size_t hl = 0;
2258 for (auto &rowNode : children())
2259 {
2260 const DocHtmlRow *row = std::get_if<DocHtmlRow>(&rowNode);
2261 if (row)
2262 {
2263 if (!row->isHeading()) break;
2264 hl++;
2265 }
2266 }
2267 return hl;
2268}
2269
2271{
2272 AUTO_TRACE();
2273 Token retval = Token::make_RetVal_OK();
2274 auto ns = AutoNodeStack(parser(),thisVariant());
2275
2276getrow:
2277 // skip whitespace and tbody, thead and tfoot tags
2279 // should find a html tag now
2280 if (tok.is(TokenRetval::TK_HTMLTAG))
2281 {
2282 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2283 if (tagId==HtmlTagType::HTML_TR && !parser()->context.token->endTag) // found <tr> tag
2284 {
2285 // no caption, just rows
2286 retval = Token::make_RetVal_TableRow();
2287 }
2288 else if (tagId==HtmlTagType::HTML_CAPTION && !parser()->context.token->endTag) // found <caption> tag
2289 {
2290 if (m_caption)
2291 {
2292 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"table already has a caption, found another one");
2293 }
2294 else
2295 {
2296 m_caption = createDocNode<DocHtmlCaption>(parser(),thisVariant(),parser()->context.token->attribs);
2297 retval=std::get<DocHtmlCaption>(*m_caption).parse();
2298
2299 if (retval.is(TokenRetval::RetVal_OK)) // caption was parsed ok
2300 {
2301 goto getrow;
2302 }
2303 }
2304 }
2305 else // found wrong token
2306 {
2307 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or <caption> tag but "
2308 "found <{}{}> instead!", parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2309 }
2310 }
2311 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2312 {
2313 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2314 " for a <tr> or <caption> tag");
2315 }
2316 else // token other than html token
2317 {
2318 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> tag but found {} token instead!",
2319 tok.to_string());
2320 }
2321
2322 // parse one or more rows
2323 while (retval.is(TokenRetval::RetVal_TableRow))
2324 {
2326 retval = children().get_last<DocHtmlRow>()->parse();
2327 //printf("DocHtmlTable::retval=%s\n",retval.to_string());
2328 if (retval.is(TokenRetval::RetVal_EndTableRow))
2329 {
2330 // get next token
2331 retval = skipSpacesForTable(parser());
2332 //printf("DocHtmlTable::retval= next=%s name=%s endTag=%d\n",retval.to_string(),qPrint(parser()->context.token->name),parser()->context.token->endTag);
2333 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2334 if (tagId==HtmlTagType::HTML_TR && !parser()->context.token->endTag)
2335 {
2336 retval = Token::make_RetVal_TableRow();
2337 }
2338 else if (tagId==HtmlTagType::HTML_TABLE && parser()->context.token->endTag)
2339 {
2340 retval = Token::make_RetVal_EndTable();
2341 }
2342 else if (retval.is(TokenRetval::TK_HTMLTAG)) // some other HTML tag
2343 {
2344 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or </table> tag but "
2345 "found <{}> instead!",parser()->context.token->name);
2346 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2347 }
2348 else // found some other tag
2349 {
2350 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or </table> tag but "
2351 "found token {} instead!",retval.to_string());
2352 retval=Token::make_RetVal_OK();
2353 break;
2354 }
2355 }
2356 }
2357
2359
2360 return retval.is(TokenRetval::RetVal_EndTable) ? Token::make_RetVal_OK() : retval;
2361}
2362
2364{
2365 AUTO_TRACE();
2366 Token retval = Token::make_RetVal_OK();
2367 auto ns = AutoNodeStack(parser(),thisVariant());
2368
2369 // get next token
2370 Token tok=parser()->tokenizer.lex();
2371 // skip whitespace
2372 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2373 // should find a html tag now
2375 bool isHeader=FALSE;
2376 if (tok.is(TokenRetval::TK_HTMLTAG))
2377 {
2378 tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2379 if (tagId==HtmlTagType::XML_ITEM && !parser()->context.token->endTag) // found <item> tag
2380 {
2381 retval = Token::make_RetVal_TableRow();
2382 }
2383 if (tagId==HtmlTagType::XML_LISTHEADER && !parser()->context.token->endTag) // found <listheader> tag
2384 {
2385 retval = Token::make_RetVal_TableRow();
2386 isHeader=TRUE;
2387 }
2388 }
2389
2390 // parse one or more rows
2391 while (retval.is(TokenRetval::RetVal_TableRow))
2392 {
2395 retval=tr->parseXml(isHeader);
2396 isHeader=FALSE;
2397 }
2398
2400
2401 tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2402 return tagId==HtmlTagType::XML_LIST && parser()->context.token->endTag ? Token::make_RetVal_OK() : retval;
2403}
2404
2405/** Helper class to compute the grid for an HTML style table */
2407{
2408 ActiveRowSpan(uint32_t rows,uint32_t col) : rowsLeft(rows), column(col) {}
2409 uint32_t rowsLeft;
2410 uint32_t column;
2411};
2412
2413/** List of ActiveRowSpan classes. */
2414typedef std::vector<ActiveRowSpan> RowSpanList;
2415
2416/** determines the location of all cells in a grid, resolving row and
2417 column spans. For each the total number of visible cells is computed,
2418 and the total number of visible columns over all rows is stored.
2419 */
2421{
2422 //printf("computeTableGrid()\n");
2423 RowSpanList rowSpans;
2424 uint32_t maxCols=0;
2425 uint32_t rowIdx=1;
2426 for (auto &rowNode : children())
2427 {
2428 uint32_t colIdx=1;
2429 uint32_t cells=0;
2430 DocHtmlRow *row = std::get_if<DocHtmlRow>(&rowNode);
2431 if (row)
2432 {
2433 for (auto &cellNode : row->children())
2434 {
2435 DocHtmlCell *cell = std::get_if<DocHtmlCell>(&cellNode);
2436 if (cell)
2437 {
2438 uint32_t rs = cell->rowSpan();
2439 uint32_t cs = cell->colSpan();
2440
2441 for (size_t i=0;i<rowSpans.size();i++)
2442 {
2443 if (rowSpans[i].rowsLeft>0 &&
2444 rowSpans[i].column==colIdx)
2445 {
2446 colIdx=rowSpans[i].column+1;
2447 cells++;
2448 }
2449 }
2450 if (rs>0) rowSpans.emplace_back(rs,colIdx);
2451 //printf("found cell at (%d,%d)\n",rowIdx,colIdx);
2452 cell->setRowIndex(rowIdx);
2453 cell->setColumnIndex(colIdx);
2454 colIdx+=cs;
2455 cells++;
2456 }
2457 }
2458 for (size_t i=0;i<rowSpans.size();i++)
2459 {
2460 if (rowSpans[i].rowsLeft>0) rowSpans[i].rowsLeft--;
2461 }
2462 row->setVisibleCells(cells);
2463 row->setRowIndex(rowIdx);
2464 rowIdx++;
2465 }
2466 if (colIdx-1>maxCols) maxCols=colIdx-1;
2467 }
2468 m_numCols = maxCols;
2469}
2470
2471//---------------------------------------------------------------------------
2472
2474{
2475 AUTO_TRACE();
2476 Token retval = Token::make_TK_NONE();
2477 auto ns = AutoNodeStack(parser(),thisVariant());
2478
2479 Token tok = parser()->tokenizer.lex();
2480 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
2481 {
2482 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
2483 {
2484 switch (tok.value())
2485 {
2486 case TokenRetval::TK_COMMAND_AT:
2487 // fall through
2488 case TokenRetval::TK_COMMAND_BS:
2489 {
2490 QCString cmdName=parser()->context.token->name;
2491 bool isJavaLink=FALSE;
2492 switch (Mappers::cmdMapper->map(cmdName))
2493 {
2495 {
2496 tok=parser()->tokenizer.lex();
2497 if (!tok.is(TokenRetval::TK_WHITESPACE))
2498 {
2499 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
2500 tok.command_to_char(),cmdName);
2501 }
2502 else
2503 {
2505 tok=parser()->tokenizer.lex(); // get the reference id
2506 if (!tok.is(TokenRetval::TK_WORD))
2507 {
2508 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}' command",
2509 tok.to_string(),tok.command_to_char(),cmdName);
2510 }
2511 else
2512 {
2514 children().get_last<DocRef>()->parse(tok.command_to_char(),cmdName);
2515 }
2517 }
2518 }
2519 break;
2521 isJavaLink=TRUE;
2522 // fall through
2524 {
2525 tok=parser()->tokenizer.lex();
2526 if (!tok.is(TokenRetval::TK_WHITESPACE))
2527 {
2528 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
2529 cmdName);
2530 }
2531 else
2532 {
2534 tok=parser()->tokenizer.lex();
2535 if (!tok.is(TokenRetval::TK_WORD))
2536 {
2537 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\{} command",
2538 tok.to_string(),cmdName);
2539 }
2540 else
2541 {
2544 DocLink *lnk = children().get_last<DocLink>();
2545 QCString leftOver = lnk->parse(isJavaLink);
2546 if (!leftOver.isEmpty())
2547 {
2548 children().append<DocWord>(parser(),thisVariant(),leftOver);
2549 }
2550 }
2551 }
2552 }
2553 break;
2555 {
2557 }
2558 break;
2559 default:
2560 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a <dt> tag",
2561 tok.command_to_char(),cmdName);
2562 }
2563 }
2564 break;
2565 case TokenRetval::TK_SYMBOL:
2566 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a <dt> tag",
2567 parser()->context.token->name);
2568 break;
2569 case TokenRetval::TK_HTMLTAG:
2570 {
2571 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2572 if (tagId==HtmlTagType::HTML_DD && !parser()->context.token->endTag) // found <dd> tag
2573 {
2574 retval = Token::make_RetVal_DescData();
2575 goto endtitle;
2576 }
2577 else if (tagId==HtmlTagType::HTML_DT && parser()->context.token->endTag)
2578 {
2579 // ignore </dt> tag.
2580 }
2581 else if (tagId==HtmlTagType::HTML_DT)
2582 {
2583 // missing <dt> tag.
2584 retval = Token::make_RetVal_DescTitle();
2585 goto endtitle;
2586 }
2587 else if (tagId==HtmlTagType::HTML_DL && parser()->context.token->endTag)
2588 {
2589 retval = Token::make_RetVal_EndDesc();
2590 goto endtitle;
2591 }
2592 else if (tagId==HtmlTagType::HTML_A)
2593 {
2594 if (!parser()->context.token->endTag)
2595 {
2596 parser()->handleAHref(thisVariant(),children(),parser()->context.token->attribs);
2597 }
2598 }
2599 else if (tagId==HtmlTagType::HTML_BR)
2600 {
2602 }
2603 else
2604 {
2605 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <dt> context",
2606 parser()->context.token->endTag?"/":"",parser()->context.token->name);
2607 }
2608 }
2609 break;
2610 default:
2611 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {} found as part of a <dt> tag",
2612 tok.to_string());
2613 break;
2614 }
2615 }
2616 tok = parser()->tokenizer.lex();
2617 }
2618 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2619 {
2620 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
2621 " <dt> tag");
2622 }
2623endtitle:
2625 return retval;
2626}
2627
2628
2629//---------------------------------------------------------------------------
2630
2632{
2633 AUTO_TRACE();
2635 Token retval = Token::make_TK_NONE();
2636 auto ns = AutoNodeStack(parser(),thisVariant());
2637
2638 bool isFirst=TRUE;
2639 DocPara *par=nullptr;
2640 do
2641 {
2643 par = children().get_last<DocPara>();
2644 if (isFirst) { par->markFirst(); isFirst=FALSE; }
2645 retval=par->parse();
2646 }
2647 while (retval.is(TokenRetval::TK_NEWPARA));
2648 if (par) par->markLast();
2649
2650 return retval;
2651}
2652
2653//---------------------------------------------------------------------------
2654
2656{
2657 AUTO_TRACE();
2658 Token retval = Token::make_RetVal_OK();
2659 auto ns = AutoNodeStack(parser(),thisVariant());
2660
2661 // get next token
2662 Token tok=parser()->tokenizer.lex();
2663 // skip whitespace
2664 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2665 // should find a html tag now
2666 if (tok.is(TokenRetval::TK_HTMLTAG))
2667 {
2668 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2669 if (tagId==HtmlTagType::HTML_DT && !parser()->context.token->endTag) // found <dt> tag
2670 {
2671 // continue
2672 }
2673 else // found some other tag
2674 {
2675 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <dt> tag but "
2676 "found <{}> instead!",parser()->context.token->name);
2677 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2678 goto enddesclist;
2679 }
2680 }
2681 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2682 {
2683 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2684 " for a html description title");
2685 goto enddesclist;
2686 }
2687 else // token other than html token
2688 {
2689 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <dt> tag but found {} token instead!",
2690 tok.to_string());
2691 goto enddesclist;
2692 }
2693
2694 do
2695 {
2700 retval=dt->parse();
2701 if (retval.is(TokenRetval::RetVal_DescData))
2702 {
2703 retval=dd->parse();
2704 while (retval.is(TokenRetval::RetVal_DescData))
2705 {
2708 retval=dd->parse();
2709 }
2710 }
2711 else if (!retval.is(TokenRetval::RetVal_DescTitle))
2712 {
2713 // error
2714 break;
2715 }
2716 } while (retval.is(TokenRetval::RetVal_DescTitle));
2717
2718 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2719 {
2720 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <dl> block");
2721 }
2722
2723enddesclist:
2724
2725 return retval.is(TokenRetval::RetVal_EndDesc) ? Token::make_RetVal_OK() : retval;
2726}
2727
2728//---------------------------------------------------------------------------
2729
2731{
2732 AUTO_TRACE();
2733 Token retval = Token::make_TK_NONE();
2734 auto ns = AutoNodeStack(parser(),thisVariant());
2735
2736 // parse one or more paragraphs
2737 bool isFirst=TRUE;
2738 DocPara *par=nullptr;
2739 do
2740 {
2742 par = children().get_last<DocPara>();
2743 if (isFirst) { par->markFirst(); isFirst=FALSE; }
2744 retval=par->parse();
2745 }
2746 while (retval.is(TokenRetval::TK_NEWPARA));
2747 if (par) par->markLast();
2748
2749 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2750 return retval;
2751}
2752
2754{
2755 AUTO_TRACE();
2756 Token retval = Token::make_TK_NONE();
2757 auto ns = AutoNodeStack(parser(),thisVariant());
2758
2759 // parse one or more paragraphs
2760 bool isFirst=TRUE;
2761 DocPara *par=nullptr;
2762 do
2763 {
2765 par = children().get_last<DocPara>();
2766 if (isFirst) { par->markFirst(); isFirst=FALSE; }
2767 retval=par->parse();
2768 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
2769
2770 //printf("new item: retval=%x parser()->context.token->name=%s parser()->context.token->endTag=%d\n",
2771 // retval,qPrint(parser()->context.token->name),parser()->context.token->endTag);
2772 if (retval.is(TokenRetval::RetVal_ListItem))
2773 {
2774 break;
2775 }
2776 }
2777 while (!retval.is(TokenRetval::RetVal_CloseXml));
2778
2779 if (par) par->markLast();
2780
2781 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2782 return retval;
2783}
2784
2785//---------------------------------------------------------------------------
2786
2788{
2789 AUTO_TRACE();
2790 Token retval = Token::make_RetVal_OK();
2791 int num=1;
2792 auto ns = AutoNodeStack(parser(),thisVariant());
2793
2794 // get next token
2795 Token tok=parser()->tokenizer.lex();
2796 // skip whitespace and paragraph breaks
2797 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2798 // should find a html tag now
2799 if (tok.is(TokenRetval::TK_HTMLTAG))
2800 {
2801 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2802 if (tagId==HtmlTagType::HTML_LI && !parser()->context.token->endTag) // found <li> tag
2803 {
2804 // ok, we can go on.
2805 }
2806 else if (((m_type==Unordered && tagId==HtmlTagType::HTML_UL) ||
2808 ) && parser()->context.token->endTag
2809 ) // found empty list
2810 {
2811 // add dummy item to obtain valid HTML
2813 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"empty list!");
2814 retval = Token::make_RetVal_EndList();
2815 goto endlist;
2816 }
2817 else // found some other tag
2818 {
2819 // add dummy item to obtain valid HTML
2821 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <li> tag but "
2822 "found <{}{}> instead!",parser()->context.token->endTag?"/":"",parser()->context.token->name);
2823 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2824 goto endlist;
2825 }
2826 }
2827 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2828 {
2829 // add dummy item to obtain valid HTML
2831 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2832 " for a html list item");
2833 goto endlist;
2834 }
2835 else // token other than html token
2836 {
2837 // add dummy item to obtain valid HTML
2839 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <li> tag but found {} token instead!",
2840 tok.to_string());
2841 goto endlist;
2842 }
2843
2844 do
2845 {
2848 retval=li->parse();
2849 } while (retval.is(TokenRetval::RetVal_ListItem));
2850
2851 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2852 {
2853 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <{:c}l> block",
2854 m_type==Unordered ? 'u' : 'o');
2855 }
2856
2857endlist:
2858 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2859 return retval.is(TokenRetval::RetVal_EndList) ? Token::make_RetVal_OK() : retval;
2860}
2861
2863{
2864 AUTO_TRACE();
2865 Token retval = Token::make_RetVal_OK();
2866 int num=1;
2867 auto ns = AutoNodeStack(parser(),thisVariant());
2868
2869 // get next token
2870 Token tok=parser()->tokenizer.lex();
2871 // skip whitespace and paragraph breaks
2872 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2873 // should find a html tag now
2874 if (tok.is(TokenRetval::TK_HTMLTAG))
2875 {
2876 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2877 //printf("parser()->context.token->name=%s parser()->context.token->endTag=%d\n",qPrint(parser()->context.token->name),parser()->context.token->endTag);
2878 if (tagId==HtmlTagType::XML_ITEM && !parser()->context.token->endTag) // found <item> tag
2879 {
2880 // ok, we can go on.
2881 }
2882 else // found some other tag
2883 {
2884 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <item> tag but "
2885 "found <{}> instead!",parser()->context.token->name);
2886 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2887 goto endlist;
2888 }
2889 }
2890 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2891 {
2892 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2893 " for a html list item");
2894 goto endlist;
2895 }
2896 else // token other than html token
2897 {
2898 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <item> tag but found {} token instead!",
2899 tok.to_string());
2900 goto endlist;
2901 }
2902
2903 do
2904 {
2907 retval=li->parseXml();
2908 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
2909 //printf("retval=%x parser()->context.token->name=%s\n",retval,qPrint(parser()->context.token->name));
2910 } while (retval.is(TokenRetval::RetVal_ListItem));
2911
2912 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2913 {
2914 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <list type=\"{}\"> block",
2915 m_type==Unordered ? "bullet" : "number");
2916 }
2917
2918endlist:
2919 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2920 return (retval.is_any_of(TokenRetval::RetVal_EndList,TokenRetval::RetVal_CloseXml) || parser()->context.token->name=="list") ?
2921 Token::make_RetVal_OK() : retval;
2922}
2923
2924//--------------------------------------------------------------------------
2925
2927{
2928 AUTO_TRACE();
2929 Token retval = Token::make_TK_NONE();
2930 auto ns = AutoNodeStack(parser(),thisVariant());
2931
2932 // parse one or more paragraphs
2933 bool isFirst=TRUE;
2934 DocPara *par=nullptr;
2935 do
2936 {
2938 par = children().get_last<DocPara>();
2939 if (isFirst) { par->markFirst(); isFirst=FALSE; }
2940 retval=par->parse();
2941 }
2942 while (retval.is(TokenRetval::TK_NEWPARA));
2943 if (par) par->markLast();
2944
2945 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2946 {
2947 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <blockquote> block");
2948 }
2949
2950 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2951 return retval.is(TokenRetval::RetVal_EndBlockQuote) ? Token::make_RetVal_OK() : retval;
2952}
2953
2954//---------------------------------------------------------------------------
2955
2957{
2958 AUTO_TRACE();
2959 Token retval = Token::make_TK_NONE();
2960 auto ns = AutoNodeStack(parser(),thisVariant());
2961
2962 // parse one or more paragraphs
2963 bool isFirst=TRUE;
2964 DocPara *par=nullptr;
2965 do
2966 {
2968 par = children().get_last<DocPara>();
2969 if (isFirst) { par->markFirst(); isFirst=FALSE; }
2970 retval=par->parse();
2971 }
2972 while (retval.is(TokenRetval::TK_NEWPARA));
2973 if (par) par->markLast();
2974
2975 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2976 return retval.is(TokenRetval::RetVal_EndBlockQuote) ? Token::make_RetVal_OK() : retval;
2977}
2978
2979//---------------------------------------------------------------------------
2980
2985
2986
2988{
2989 auto ns = AutoNodeStack(parser(),thisVariant());
2991 DocPara *par = &std::get<DocPara>(*m_paragraph);
2992 Token rv=par->parse();
2993 par->markFirst();
2994 par->markLast();
2995 return rv;
2996}
2997
2998//--------------------------------------------------------------------------
2999
3001{
3002 auto ns = AutoNodeStack(parser(),thisVariant());
3003 Token rv = Token::make_TK_NONE();
3004 do
3005 {
3008 rv=li->parse();
3009 } while (rv.is(TokenRetval::RetVal_ListItem));
3010 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3011}
3012
3013//--------------------------------------------------------------------------
3014
3019
3021{
3022 AUTO_TRACE();
3023 Token retval = Token::make_RetVal_OK();
3024 auto ns = AutoNodeStack(parser(),thisVariant());
3025
3026 // first parse any number of paragraphs
3027 bool isFirst=TRUE;
3028 DocPara *lastPar=nullptr;
3029 do
3030 {
3032 DocPara *par = children().get_last<DocPara>();
3033 if (isFirst) { par->markFirst(); isFirst=FALSE; }
3034 retval=par->parse();
3035 if (!par->isEmpty())
3036 {
3037 if (lastPar) lastPar->markLast(FALSE);
3038 lastPar=par;
3039 }
3040 else
3041 {
3042 children().pop_back();
3043 }
3044 // next paragraph should be more indented than the - marker to belong
3045 // to this item
3046 } while (retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->indent>m_indent);
3047 if (lastPar) lastPar->markLast();
3048
3049 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3050 return retval;
3051}
3052
3053//--------------------------------------------------------------------------
3054
3061
3063{
3064 AUTO_TRACE();
3065 Token retval = Token::make_RetVal_OK();
3066 int num=1;
3067 auto ns = AutoNodeStack(parser(),thisVariant());
3069 // first item or sub list => create new list
3070 do
3071 {
3072 switch (parser()->context.token->id)
3073 {
3074 case -1:
3075 break;
3076 case DocAutoList::Unchecked: // unchecked
3077 case DocAutoList::Checked_x: // checked with x
3078 case DocAutoList::Checked_X: // checked with X
3079 num = parser()->context.token->id;
3080 break;
3081 default: // explicitly numbered list
3082 num=parser()->context.token->id; // override num with real number given
3083 break;
3084 }
3085
3087 retval = children().get_last<DocAutoListItem>()->parse();
3088 //printf("DocAutoList::parse(): retval=0x%x parser()->context.token->indent=%d m_indent=%d "
3089 // "m_isEnumList=%d parser()->context.token->isEnumList=%d parser()->context.token->name=%s\n",
3090 // retval,parser()->context.token->indent,m_indent,m_isEnumList,parser()->context.token->isEnumList,
3091 // qPrint(parser()->context.token->name));
3092 //printf("num=%d parser()->context.token->id=%d\n",num,parser()->context.token->id);
3093 }
3094 while (retval.is(TokenRetval::TK_LISTITEM) && // new list item
3095 m_indent==parser()->context.token->indent && // at same indent level
3096 m_isEnumList==parser()->context.token->isEnumList && // of the same kind
3097 m_isCheckedList==parser()->context.token->isCheckedList && // of the same kind
3098 (parser()->context.token->id==-1 || parser()->context.token->id>=num) // increasing number (or no number or checked list)
3099 );
3100
3102
3103 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3104 return retval;
3105}
3106
3107//--------------------------------------------------------------------------
3108
3110{
3111 AUTO_TRACE();
3112 auto ns = AutoNodeStack(parser(),thisVariant());
3114 Token tok = parser()->tokenizer.lex();
3115 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
3116 {
3117 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
3118 {
3119 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"title section");
3120 }
3121 tok = parser()->tokenizer.lex();
3122 }
3125}
3126
3128{
3130 parser()->pushContext(); // this will create a new parser->context.token
3132 parser()->popContext(); // this will restore the old parser->context.token
3136}
3137
3138//--------------------------------------------------------------------------
3139
3144
3146{
3147 return m_title && std::get<DocTitle>(*m_title).hasTitle();
3148}
3149
3150Token DocSimpleSect::parse(bool userTitle,bool needsSeparator)
3151{
3152 AUTO_TRACE();
3153 auto ns = AutoNodeStack(parser(),thisVariant());
3154
3155 // handle case for user defined title
3156 if (userTitle)
3157 {
3159 std::get_if<DocTitle>(m_title.get())->parse();
3160 }
3161
3162 // add new paragraph as child
3163 if (!children().empty() && std::holds_alternative<DocPara>(children().back()))
3164 {
3165 std::get<DocPara>(children().back()).markLast(FALSE);
3166 }
3167 bool markFirst = children().empty();
3168 if (needsSeparator)
3169 {
3171 }
3173 DocPara *par = children().get_last<DocPara>();
3174 if (markFirst)
3175 {
3176 par->markFirst();
3177 }
3178 par->markLast();
3179
3180 // parse the contents of the paragraph
3181 Token retval = par->parse();
3182
3183 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3184 return retval; // 0==EOF, TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, TokenRetval::TK_ENDLIST, TokenRetval::RetVal_SimpleSec
3185}
3186
3188{
3189 AUTO_TRACE();
3190 auto ns = AutoNodeStack(parser(),thisVariant());
3191
3193 DocTitle *title = &std::get<DocTitle>(*m_title);
3194 title->parseFromString(thisVariant(),parser()->context.token->name);
3195
3196 QCString text = parser()->context.token->text;
3197 parser()->pushContext(); // this will create a new parser->context.token
3199 parser()->popContext(); // this will restore the old parser->context.token
3200
3201 return Token::make_RetVal_OK();
3202}
3203
3205{
3206 AUTO_TRACE();
3207 auto ns = AutoNodeStack(parser(),thisVariant());
3208
3209 Token retval = Token::make_RetVal_OK();
3210 for (;;)
3211 {
3212 // add new paragraph as child
3213 if (!children().empty() && std::holds_alternative<DocPara>(children().back()))
3214 {
3215 std::get<DocPara>(children().back()).markLast(false);
3216 }
3217 bool markFirst = children().empty();
3219 DocPara *par = children().get_last<DocPara>();
3220 if (markFirst)
3221 {
3222 par->markFirst();
3223 }
3224 par->markLast();
3225
3226 // parse the contents of the paragraph
3227 retval = par->parse();
3228 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
3229 if (retval.is(TokenRetval::RetVal_CloseXml))
3230 {
3231 retval = Token::make_RetVal_OK();
3232 break;
3233 }
3234 }
3235
3236 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3237 return retval;
3238}
3239
3241{
3242 DocPara *p=nullptr;
3243 if (children().empty() || (p=std::get_if<DocPara>(&children().back()))==nullptr)
3244 {
3246 p = children().get_last<DocPara>();
3247 }
3248 else
3249 {
3250 // Comma-separate <seealso> links.
3251 p->injectToken(Token::make_TK_WORD(),",");
3252 p->injectToken(Token::make_TK_WHITESPACE()," ");
3253 }
3254
3256 p->injectToken(Token::make_TK_LNKWORD(),word);
3258}
3259
3261{
3262 switch (m_type)
3263 {
3264 case Unknown: break;
3265 case See: return "see";
3266 case Return: return "return";
3267 case Author: // fall through
3268 case Authors: return "author";
3269 case Version: return "version";
3270 case Since: return "since";
3271 case Date: return "date";
3272 case Note: return "note";
3273 case Warning: return "warning";
3274 case Pre: return "pre";
3275 case Post: return "post";
3276 case Copyright: return "copyright";
3277 case Invar: return "invariant";
3278 case Remark: return "remark";
3279 case Attention: return "attention";
3280 case Important: return "important";
3281 case User: return "user";
3282 case Rcs: return "rcs";
3283 }
3284 return "unknown";
3285}
3286
3287//--------------------------------------------------------------------------
3288
3290{
3291 AUTO_TRACE();
3292 Token retval = Token::make_RetVal_OK();
3293 auto ns = AutoNodeStack(parser(),thisVariant());
3294 DocPara *par=nullptr;
3295 QCString saveCmdName = cmdName;
3296 DocParserContext &context = parser()->context;
3297 DocTokenizer &tokenizer = parser()->tokenizer;
3298
3299 Token tok=tokenizer.lex();
3300 if (!tok.is(TokenRetval::TK_WHITESPACE))
3301 {
3302 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
3303 saveCmdName);
3304 retval = Token::make_RetVal_EndParBlock();
3305 goto endparamlist;
3306 }
3307 tokenizer.setStateParam();
3308 tok=tokenizer.lex();
3309 while (tok.is(TokenRetval::TK_WORD)) /* there is a parameter name */
3310 {
3312 {
3313 int typeSeparator = context.token->name.find('#'); // explicit type position
3314 if (typeSeparator!=-1)
3315 {
3316 parser()->handleParameterType(thisVariant(),m_paramTypes,context.token->name.left(typeSeparator));
3317 context.token->name = context.token->name.mid(typeSeparator+1);
3318 context.hasParamCommand=TRUE;
3319 if (parent() && std::holds_alternative<DocParamSect>(*parent()))
3320 {
3321 std::get<DocParamSect>(*parent()).m_hasTypeSpecifier=true;
3322 }
3323 }
3324 else
3325 {
3326 context.hasParamCommand=TRUE;
3327 }
3329 }
3330 else if (m_type==DocParamSect::RetVal)
3331 {
3332 context.hasReturnCommand=TRUE;
3334 }
3335 context.inSeeBlock=true;
3337 context.inSeeBlock=false;
3338 tok=tokenizer.lex();
3339 }
3340 tokenizer.setStatePara();
3341 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
3342 {
3343 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3344 "argument of command {}",saveCmdName);
3345 retval = Token::make_RetVal_EndParBlock();
3346 goto endparamlist;
3347 }
3348 if (!tok.is(TokenRetval::TK_WHITESPACE)) /* premature end of comment block */
3349 {
3350 if (!tok.is(TokenRetval::TK_NEWPARA)) /* empty param description */
3351 {
3352 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} in comment block while parsing the "
3353 "argument of command {}",tok.to_string(),saveCmdName);
3354 }
3355 retval = Token::make_RetVal_EndParBlock();
3356 goto endparamlist;
3357 }
3358
3360 par = m_paragraphs.get_last<DocPara>();
3361 retval = par->parse();
3362 par->markFirst();
3363 par->markLast();
3364
3365endparamlist:
3366 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3367 return retval;
3368}
3369
3371{
3372 AUTO_TRACE();
3373 Token retval = Token::make_RetVal_OK();
3374 auto ns = AutoNodeStack(parser(),thisVariant());
3375
3376 parser()->context.token->name = paramName;
3378 {
3381 }
3382 else if (m_type==DocParamSect::RetVal)
3383 {
3386 }
3387
3389
3390 do
3391 {
3393 DocPara *par = m_paragraphs.get_last<DocPara>();
3394 retval = par->parse();
3395 if (par->isEmpty()) // avoid adding an empty paragraph for the whitespace
3396 // after </para> and before </param>
3397 {
3398 m_paragraphs.pop_back();
3399 break;
3400 }
3401 else // append the paragraph to the list
3402 {
3403 if (!m_paragraphs.empty())
3404 {
3405 m_paragraphs.get_last<DocPara>()->markLast(FALSE);
3406 }
3407 bool markFirst = m_paragraphs.empty();
3408 par = &std::get<DocPara>(m_paragraphs.back());
3409 if (markFirst)
3410 {
3411 par->markFirst();
3412 }
3413 par->markLast();
3414 }
3415
3416 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
3417
3418 } while (retval.is(TokenRetval::RetVal_CloseXml) &&
3419 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_PARAM &&
3420 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_TYPEPARAM &&
3421 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_EXCEPTION);
3422
3423 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) /* premature end of comment block */
3424 {
3425 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unterminated param or exception tag");
3426 }
3427 else
3428 {
3429 retval = Token::make_RetVal_OK();
3430 }
3431
3432 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3433 return retval;
3434}
3435
3436//--------------------------------------------------------------------------
3437
3438Token DocParamSect::parse(const QCString &cmdName,bool xmlContext, Direction d)
3439{
3440 AUTO_TRACE();
3441 Token retval = Token::make_RetVal_OK();
3442 auto ns = AutoNodeStack(parser(),thisVariant());
3443
3444 if (d!=Unspecified)
3445 {
3447 }
3448
3449 if (!children().empty() && std::holds_alternative<DocParamList>(children().back()))
3450 {
3451 DocParamList &lastPl = std::get<DocParamList>(children().back());
3452 lastPl.markLast(false);
3453 }
3454 bool markFirst = children().empty();
3457 if (markFirst)
3458 {
3459 pl->markFirst();
3460 }
3461 pl->markLast();
3462 if (xmlContext)
3463 {
3464 retval = pl->parseXml(cmdName);
3465 }
3466 else
3467 {
3468 retval = pl->parse(cmdName);
3469 }
3470 if (retval.is(TokenRetval::RetVal_EndParBlock))
3471 {
3472 retval = Token::make_RetVal_OK();
3473 }
3474
3475 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3476 return retval;
3477}
3478
3479//--------------------------------------------------------------------------
3480
3486
3488{
3489 AUTO_TRACE();
3490 DocSimpleSect *ss=nullptr;
3491 bool needsSeparator = FALSE;
3492 if (!children().empty() && // has previous element
3493 (ss=children().get_last<DocSimpleSect>()) && // was a simple sect
3494 ss->type()==t && // of same type
3495 t!=DocSimpleSect::User) // but not user defined
3496 {
3497 // append to previous section
3498 needsSeparator = TRUE;
3499 }
3500 else // start new section
3501 {
3504 }
3505 Token rv = Token::make_RetVal_OK();
3506 if (xmlContext)
3507 {
3508 return ss->parseXml();
3509 }
3510 else
3511 {
3512 rv = ss->parse(t==DocSimpleSect::User,needsSeparator);
3513 }
3514 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3515}
3516
3519 bool xmlContext=FALSE,
3520 int direction=DocParamSect::Unspecified)
3521{
3522 AUTO_TRACE();
3523 DocParamSect *ps = nullptr;
3524 if (!children().empty() && // previous element
3525 (ps=children().get_last<DocParamSect>()) && // was a param sect
3526 ps->type()==t) // of same type
3527 { // append to previous section ps
3528 }
3529 else // start new section
3530 {
3532 ps = children().get_last<DocParamSect>();
3533 }
3534 Token rv=ps->parse(cmdName,xmlContext,
3535 static_cast<DocParamSect::Direction>(direction));
3536 AUTO_TRACE_EXIT("retval={}",rv.to_string());
3537 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3538}
3539
3540void DocPara::handleCite(char cmdChar,const QCString &cmdName)
3541{
3542 AUTO_TRACE();
3543 QCString saveCmdName = cmdName;
3544 // get the argument of the cite command.
3545 Token tok=parser()->tokenizer.lex();
3546
3547 CiteInfoOption option;
3548 if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="{")
3549 {
3551 parser()->tokenizer.lex();
3552 StringVector optList=split(parser()->context.token->name.str(),",");
3553 for (auto const &opt : optList)
3554 {
3555 if (opt == "number")
3556 {
3557 if (!option.isUnknown())
3558 {
3559 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", saveCmdName, opt);
3560 }
3561 else
3562 {
3563 option = CiteInfoOption::makeNumber();
3564 }
3565 }
3566 else if (opt == "year")
3567 {
3568 if (!option.isUnknown())
3569 {
3570 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", saveCmdName, opt);
3571 }
3572 else
3573 {
3574 option = CiteInfoOption::makeYear();
3575 }
3576 }
3577 else if (opt == "shortauthor")
3578 {
3579 if (!option.isUnknown())
3580 {
3581 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", saveCmdName, opt);
3582 }
3583 else
3584 {
3586 }
3587 }
3588 else if (opt == "nopar")
3589 {
3590 option.setNoPar();
3591 }
3592 else if (opt == "nocite")
3593 {
3594 option.setNoCite();
3595 }
3596 else
3597 {
3598 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unknown option specified with \\{}, discarding '{}'", saveCmdName, opt);
3599 }
3600 }
3601
3602 if (option.isUnknown()) option.changeToNumber();
3603
3605 tok=parser()->tokenizer.lex();
3606 if (!tok.is(TokenRetval::TK_WHITESPACE))
3607 {
3608 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3609 saveCmdName);
3610 return;
3611 }
3612 }
3613 else if (!tok.is(TokenRetval::TK_WHITESPACE))
3614 {
3615 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3616 cmdChar,saveCmdName);
3617 return;
3618 }
3619 else
3620 {
3621 option = CiteInfoOption::makeNumber();
3622 }
3623
3625 tok=parser()->tokenizer.lex();
3626 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3627 {
3628 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"THE ONE unexpected end of comment block while parsing the "
3629 "argument of command '{:c}{}'",cmdChar,saveCmdName);
3630 return;
3631 }
3632 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
3633 {
3634 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3635 tok.to_string(),cmdChar,saveCmdName);
3636 return;
3637 }
3641
3643}
3644
3645void DocPara::handleEmoji(char cmdChar,const QCString &cmdName)
3646{
3647 AUTO_TRACE();
3648 // get the argument of the emoji command.
3649 Token tok=parser()->tokenizer.lex();
3650 if (!tok.is(TokenRetval::TK_WHITESPACE))
3651 {
3652 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3653 cmdChar,cmdName);
3654 return;
3655 }
3657 tok=parser()->tokenizer.lex();
3658 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3659 {
3660 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"no emoji name given or unexpected end of comment block while parsing the "
3661 "argument of command '{:c}{}'",cmdChar,cmdName);
3663 return;
3664 }
3665 else if (!tok.is(TokenRetval::TK_WORD))
3666 {
3667 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3668 tok.to_string(),cmdChar,cmdName);
3670 return;
3671 }
3674}
3675
3676void DocPara::handleDoxyConfig(char cmdChar,const QCString &cmdName)
3677{
3678 // get the argument of the cite command.
3679 Token tok=parser()->tokenizer.lex();
3680 if (!tok.is(TokenRetval::TK_WHITESPACE))
3681 {
3682 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3683 cmdChar,cmdName);
3684 return;
3685 }
3687 tok=parser()->tokenizer.lex();
3688 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3689 {
3690 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3691 "argument of command '{:c}{}'",cmdChar,cmdName);
3692 return;
3693 }
3694 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
3695 {
3696 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3697 tok.to_string(),cmdChar,cmdName);
3698 return;
3699 }
3700 ConfigOption * opt = ConfigImpl::instance()->get(parser()->context.token->name);
3701 if (opt)
3702 {
3703 QCString optionValue;
3704 switch (opt->kind())
3705 {
3707 optionValue = *(static_cast<ConfigBool*>(opt)->valueStringRef());
3708 break;
3710 optionValue = *(static_cast<ConfigString*>(opt)->valueRef());
3711 break;
3713 optionValue = *(static_cast<ConfigEnum*>(opt)->valueRef());
3714 break;
3716 optionValue = *(static_cast<ConfigInt*>(opt)->valueStringRef());
3717 break;
3719 {
3720 StringVector *lst = static_cast<ConfigList*>(opt)->valueRef();
3721 optionValue="";
3722 if (!lst->empty())
3723 {
3724 std::string lstFormat = theTranslator->trWriteList(static_cast<int>(lst->size())).str();
3725 static const reg::Ex marker(R"(@(\d+))");
3726 reg::Iterator it(lstFormat,marker);
3728 size_t index=0;
3729 // now replace all markers with the real text
3730 for ( ; it!=end ; ++it)
3731 {
3732 const auto &match = *it;
3733 size_t newIndex = match.position();
3734 size_t matchLen = match.length();
3735 optionValue += lstFormat.substr(index,newIndex-index);
3736 unsigned long entryIndex = std::stoul(match[1].str());
3737 if (entryIndex<(unsigned long)lst->size())
3738 {
3739 optionValue += lst->at(entryIndex);
3740 }
3741 index=newIndex+matchLen;
3742 }
3743 optionValue+=lstFormat.substr(index);
3744 }
3745 }
3746 break;
3748 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Obsolete setting for '{:c}{}': '{}'",
3749 cmdChar,cmdName,parser()->context.token->name);
3750 break;
3752 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),
3753 "Disabled setting (i.e. not supported in this doxygen executable) for '{:c}{}': '{}'",
3754 cmdChar,cmdName,parser()->context.token->name);
3755 break;
3757 // nothing to show here
3758 break;
3759 }
3760 if (!optionValue.isEmpty())
3761 {
3762 children().append<DocWord>(parser(),thisVariant(),optionValue);
3763 }
3764 }
3765 else
3766 {
3767 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Unknown option for '{:c}{}': '{}'",
3768 cmdChar,cmdName,parser()->context.token->name);
3770 }
3772}
3773
3775{
3776 AUTO_TRACE();
3777 Token retval=parser()->tokenizer.lex();
3778 ASSERT(retval.is(TokenRetval::TK_WHITESPACE));
3780 retval=parser()->tokenizer.lex();
3781 if (retval.is(TokenRetval::RetVal_OK))
3782 {
3786 if (!ref->parse())
3787 {
3788 children().pop_back();
3789 }
3790 }
3792 return retval;
3793}
3794
3795
3796void DocPara::handleShowDate(char cmdChar,const QCString &cmdName)
3797{
3798 AUTO_TRACE();
3799 QCString fmt;
3800 QCString date;
3801 Token tok=parser()->tokenizer.lex();
3802 if (!tok.is(TokenRetval::TK_WHITESPACE))
3803 {
3804 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3805 cmdChar,cmdName);
3806 return;
3807 }
3809 tok = parser()->tokenizer.lex();
3810 if (!tok.is(TokenRetval::TK_WORD))
3811 {
3812 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <format> argument for command '{:c}{}'",
3813 cmdChar,cmdName);
3815 return;
3816 }
3817 fmt = parser()->context.token->name;
3818
3820 tok = parser()->tokenizer.lex();
3821
3822 QCString specDateRaw = tok.is(TokenRetval::TK_WORD) ? parser()->context.token->name : QCString();
3823 QCString specDate = specDateRaw.stripWhiteSpace();
3824 bool specDateOnlyWS = !specDateRaw.isEmpty() && specDate.isEmpty();
3825 if (!specDate.isEmpty() && !tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3826 {
3827 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}'",
3828 cmdChar,cmdName);
3830 return;
3831 }
3832
3833 std::tm dat{};
3834 int specFormat=0;
3835 QCString err = dateTimeFromString(specDate,dat,specFormat);
3836 if (!err.isEmpty())
3837 {
3838 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}': {}",
3839 cmdChar,cmdName,err);
3841 return;
3842 }
3843
3844 int usedFormat=0;
3845 QCString dateTimeStr = formatDateTime(fmt,dat,usedFormat);
3846
3847 // warn the user if the format contains markers that are not explicitly filled in
3848 for (int i=0;i<SF_NumBits;i++)
3849 {
3850 int bitMask = 1<<i;
3851 if ((usedFormat&bitMask) && !(specFormat&bitMask)) // a part was used in the format string but its value was not specified.
3852 {
3853 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"'{:c}{}' <format> parameter '{}' has {} related markers which are not specified in the <date_time> parameter '{}'. Filling in the current value for {} instead.",
3854 cmdChar,cmdName,fmt,SF_bit2str(i),specDate,SF_bit2str(i));
3855 }
3856 }
3857
3858 children().append<DocWord>(parser(),thisVariant(),dateTimeStr);
3859 if (specDateOnlyWS) // specDate is only whitespace
3860 {
3862 }
3864}
3865
3867{
3868 AUTO_TRACE("cmdName={}",cmdName);
3869 QCString saveCmdName = cmdName;
3870 Token tok=parser()->tokenizer.lex();
3871 if (!tok.is(TokenRetval::TK_WHITESPACE))
3872 {
3873 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3874 saveCmdName);
3875 return;
3876 }
3878 tok=parser()->tokenizer.lex();
3880 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3881 {
3882 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3883 "argument of command {}", saveCmdName);
3884 return;
3885 }
3886 else if (!tok.is(TokenRetval::TK_WORD))
3887 {
3888 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3889 tok.to_string(),saveCmdName);
3890 return;
3891 }
3892 auto it1 = children().size()>=1 ? std::prev(children().end()) : children().end();
3893 auto it2 = children().size()>=2 ? std::prev(it1) : children().end();
3894 DocNodeVariant *n1 = it1!=children().end() ? &(*it1) : nullptr;
3895 DocNodeVariant *n2 = it2!=children().end() ? &(*it2) : nullptr;
3896 //TODO get from context the stripCodeComments()
3897 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
3901 stripCodeComments,
3904 );
3906 DocIncOperator *n1_docIncOp = std::get_if<DocIncOperator>(n1);
3907 DocWhiteSpace *n1_docWs = std::get_if<DocWhiteSpace >(n1);
3908 DocIncOperator *n2_docIncOp = std::get_if<DocIncOperator>(n2);
3909 bool isFirst = !n1 || // no last node
3910 (!n1_docIncOp && !n1_docWs) || // last node is not operator or whitespace
3911 (n1_docWs && n2 && !n2_docIncOp); // last node is not operator
3912 op->markFirst(isFirst);
3913 op->markLast(true);
3914 if (n1_docIncOp)
3915 {
3916 n1_docIncOp->markLast(false);
3917 }
3918 else if (n1_docWs && n2_docIncOp)
3919 {
3920 n2_docIncOp->markLast(false);
3921 }
3922 op->parse();
3923}
3924
3925template<class T>
3926void DocPara::handleFile(const QCString &cmdName)
3927{
3928 AUTO_TRACE("cmdName={}",cmdName);
3929 QCString saveCmdName = cmdName;
3930 Token tok=parser()->tokenizer.lex();
3931 if (!tok.is(TokenRetval::TK_WHITESPACE))
3932 {
3933 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3934 saveCmdName);
3935 return;
3936 }
3938 tok=parser()->tokenizer.lex();
3940 if (!tok.is(TokenRetval::TK_WORD))
3941 {
3942 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3943 tok.to_string(),saveCmdName);
3944 return;
3945 }
3946 QCString name = parser()->context.token->name;
3947 children().append<T>(parser(),thisVariant(),name,
3951 auto df = children().get_last<T>();
3952 if (!df->parse())
3953 {
3954 children().pop_back();
3955 }
3956}
3957
3964
3965void DocPara::handleLink(const QCString &cmdName,bool isJavaLink)
3966{
3967 AUTO_TRACE("cmdName={} isJavaLink={}",cmdName,isJavaLink);
3968 QCString saveCmdName = cmdName;
3969 Token tok=parser()->tokenizer.lex();
3970 if (!tok.is(TokenRetval::TK_WHITESPACE))
3971 {
3972 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3973 saveCmdName);
3974 return;
3975 }
3977 tok=parser()->tokenizer.lex();
3978 if (!tok.is(TokenRetval::TK_WORD))
3979 {
3980 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"{} as the argument of {}",
3981 tok.to_string(),saveCmdName);
3982 return;
3983 }
3984 if (saveCmdName == "javalink")
3985 {
3987 parser()->context.nodeStack.size(),
3988 DocStyleChange::Code,cmdName,TRUE);
3989 }
3992 DocLink *lnk = children().get_last<DocLink>();
3993 if (saveCmdName == "javalink")
3994 {
3996 parser()->context.nodeStack.size(),
3997 DocStyleChange::Code,cmdName,FALSE);
3998 }
3999 QCString leftOver = lnk->parse(isJavaLink);
4000 if (!leftOver.isEmpty())
4001 {
4002 children().append<DocWord>(parser(),thisVariant(),leftOver);
4003 }
4004}
4005
4007{
4008 AUTO_TRACE("cmdName={}",cmdName);
4009 QCString saveCmdName = cmdName;
4010 Token tok=parser()->tokenizer.lex();
4011 bool isBlock = false;
4012 bool trimLeft = false;
4013 bool localScope = false;
4014 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
4015 if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="{")
4016 {
4018 parser()->tokenizer.lex();
4020 StringVector optList=split(parser()->context.token->name.str(),",");
4021 auto contains = [&optList](const char *kw)
4022 {
4023 return std::find(optList.begin(),optList.end(),kw)!=optList.end();
4024 };
4025 localScope = contains("local");
4026 if (contains("nostrip"))
4027 {
4028 stripCodeComments = false;
4029 }
4030 else if (contains("strip"))
4031 {
4032 stripCodeComments = true;
4033 }
4034 if (t==DocInclude::Snippet && contains("trimleft"))
4035 {
4036 trimLeft = true;
4037 }
4038
4039 if (contains("lineno"))
4040 {
4044 }
4045 tok=parser()->tokenizer.lex();
4046 if (!tok.is(TokenRetval::TK_WHITESPACE))
4047 {
4048 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
4049 saveCmdName);
4050 return;
4051 }
4052 }
4053 else if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="[")
4054 {
4056 parser()->tokenizer.lex();
4057 isBlock = (parser()->context.token->name.stripWhiteSpace() == "block");
4059 parser()->tokenizer.lex();
4060 }
4061 else if (!tok.is(TokenRetval::TK_WHITESPACE))
4062 {
4063 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
4064 saveCmdName);
4065 return;
4066 }
4068 tok=parser()->tokenizer.lex();
4070 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4071 {
4072 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
4073 "argument of command {}",saveCmdName);
4074 return;
4075 }
4076 else if (!tok.is(TokenRetval::TK_WORD))
4077 {
4078 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
4079 tok.to_string(),saveCmdName);
4080 return;
4081 }
4082 QCString fileName = parser()->context.token->name;
4083 QCString blockId;
4085 {
4086 if (fileName == "this") fileName=parser()->context.fileName;
4088 tok=parser()->tokenizer.lex();
4090 if (!tok.is(TokenRetval::TK_WORD))
4091 {
4092 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected block identifier, but found token {} instead while parsing the {} command",
4093 tok.to_string(),saveCmdName);
4094 return;
4095 }
4096 blockId = "["+parser()->context.token->name+"]";
4097 }
4098
4100 thisVariant(),
4101 fileName,
4102 localScope ? parser()->context.context : "",
4103 t,
4104 stripCodeComments,
4107 blockId,isBlock,trimLeft);
4109}
4110
4111void DocPara::handleSection(char cmdChar,const QCString &cmdName)
4112{
4113 AUTO_TRACE("cmdName={}",cmdName);
4114 QCString saveCmdName = cmdName;
4115 // get the argument of the section command.
4116 Token tok=parser()->tokenizer.lex();
4117 if (!tok.is(TokenRetval::TK_WHITESPACE))
4118 {
4119 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
4120 cmdChar,saveCmdName);
4121 return;
4122 }
4123 tok=parser()->tokenizer.lex();
4124 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4125 {
4126 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
4127 "argument of command '{:c}{}'", cmdChar,saveCmdName);
4128 return;
4129 }
4130 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
4131 {
4132 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
4133 tok.to_string(),cmdChar,saveCmdName);
4134 return;
4135 }
4138 parser()->tokenizer.lex();
4140}
4141
4142Token DocPara::handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level)
4143{
4144 AUTO_TRACE();
4145 children().append<DocHtmlHeader>(parser(),thisVariant(),tagHtmlAttribs,level);
4146 Token retval = children().get_last<DocHtmlHeader>()->parse();
4147 return retval.is(TokenRetval::RetVal_OK) ? Token::make_TK_NEWPARA() : retval;
4148}
4149
4150// For XML tags whose content is stored in attributes rather than
4151// contained within the element, we need a way to inject the attribute
4152// text into the current paragraph.
4153bool DocPara::injectToken(Token tok,const QCString &tokText)
4154{
4155 AUTO_TRACE();
4156 parser()->context.token->name = tokText;
4157 return parser()->defaultHandleToken(thisVariant(),tok,children());
4158}
4159
4161{
4162 AUTO_TRACE();
4163 Token retval = parser()->tokenizer.lex();
4164 QCString lang = parser()->context.token->name;
4165 if (!lang.isEmpty() && lang.at(0)!='.')
4166 {
4167 lang="."+lang;
4168 }
4169 if (parser()->context.xmlComment)
4170 {
4171 parser()->context.token->verb = substitute(substitute(parser()->context.token->verb,"&lt;","<"),"&gt;",">");
4172 }
4173 // search for the first non-whitespace line, index is stored in li
4174 size_t i=0,li=0,l=parser()->context.token->verb.length();
4175 while (i<l && (parser()->context.token->verb.at(i)==' ' || parser()->context.token->verb.at(i)=='\n'))
4176 {
4177 if (parser()->context.token->verb.at(i)=='\n') li=i+1;
4178 i++;
4179 }
4182 stripIndentation(parser()->context.token->verb.mid(li)),
4186 FALSE,lang);
4187 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4188 {
4189 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"code section ended without end marker");
4190 }
4192 AUTO_TRACE_EXIT("retval={}",retval.to_string());
4193 return retval;
4194}
4195
4197{
4198 if (parser()->context.memberDef) // inheriting docs from a member
4199 {
4200 const MemberDef *reMd = parser()->context.memberDef->reimplements();
4201 if (reMd) // member from which was inherited.
4202 {
4203 const MemberDef *thisMd = parser()->context.memberDef;
4204 //printf("{InheritDocs:%s=>%s}\n",qPrint(parser()->context.memberDef->qualifiedName()),qPrint(reMd->qualifiedName()));
4205 parser()->pushContext();
4206 parser()->context.scope=reMd->getOuterScope();
4207 if (parser()->context.scope!=Doxygen::globalScope)
4208 {
4210 }
4211 parser()->context.memberDef=reMd;
4212 while (!parser()->context.styleStack.empty()) parser()->context.styleStack.pop();
4213 while (!parser()->context.nodeStack.empty()) parser()->context.nodeStack.pop();
4214 parser()->context.copyStack.push_back(reMd);
4217 parser()->context.copyStack.pop_back();
4218 auto hasParamCommand = parser()->context.hasParamCommand;
4219 auto hasReturnCommand = parser()->context.hasReturnCommand;
4220 auto retvalsFound = parser()->context.retvalsFound;
4221 auto paramsFound = parser()->context.paramsFound;
4222 parser()->popContext();
4223 parser()->context.hasParamCommand = hasParamCommand;
4224 parser()->context.hasReturnCommand = hasReturnCommand;
4225 parser()->context.retvalsFound = retvalsFound;
4226 parser()->context.paramsFound = paramsFound;
4227 parser()->context.memberDef = thisMd;
4228 }
4229 }
4230}
4231
4232
4233Token DocPara::handleCommand(char cmdChar, const QCString &cmdName)
4234{
4235 AUTO_TRACE("cmdName={}",cmdName);
4236 Token retval = Token::make_RetVal_OK();
4237 CommandType cmdId = Mappers::cmdMapper->map(cmdName);
4238 switch (cmdId)
4239 {
4241 {
4242 std::string str{cmdChar};
4243 children().append<DocWord>(parser(),thisVariant(),str.c_str() + cmdName);
4244 if (isAliasCmd(cmdName.view()))
4245 {
4246 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Found unexpanded alias '{:c}{}'. Check if number of arguments passed is correct.",cmdChar,cmdName);
4247 }
4248 else
4249 {
4250 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Found unknown command '{:c}{}'",cmdChar,cmdName);
4251 }
4252 }
4253 break;
4256 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4258 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4259 break;
4262 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4264 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4265 break;
4268 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4270 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4271 break;
4274 break;
4277 break;
4280 break;
4283 break;
4286 break;
4289 break;
4292 break;
4295 break;
4298 break;
4301 break;
4305 break;
4310 break;
4313 break;
4316 break;
4319 break;
4322 break;
4325 break;
4328 break;
4331 break;
4336 break;
4340 break;
4343 break;
4346 break;
4349 break;
4352 break;
4355 break;
4358 break;
4361 break;
4364 break;
4367 break;
4370 break;
4373 break;
4376 break;
4379 break;
4382 break;
4385 break;
4387 {
4389 retval = children().get_last<DocSimpleList>()->parse();
4390 }
4391 break;
4393 {
4394 handleSection(cmdChar,cmdName);
4395 retval = Token::make_RetVal_Section();
4396 }
4397 break;
4399 {
4400 handleSection(cmdChar,cmdName);
4401 retval = Token::make_RetVal_Subsection();
4402 }
4403 break;
4405 {
4406 handleSection(cmdChar,cmdName);
4407 retval = Token::make_RetVal_Subsubsection();
4408 }
4409 break;
4411 {
4412 handleSection(cmdChar,cmdName);
4413 retval = Token::make_RetVal_Paragraph();
4414 }
4415 break;
4417 {
4418 handleSection(cmdChar,cmdName);
4419 retval = Token::make_RetVal_SubParagraph();
4420 }
4421 break;
4423 {
4424 handleSection(cmdChar,cmdName);
4425 retval = Token::make_RetVal_SubSubParagraph();
4426 }
4427 break;
4429 {
4431 retval = handleStartCode();
4432 }
4433 break;
4435 {
4437 retval = handleStartCode();
4438 }
4439 break;
4441 {
4443 retval = parser()->tokenizer.lex();
4445 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4446 {
4447 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"htmlonly section ended without end marker");
4448 }
4450 }
4451 break;
4453 {
4455 retval = parser()->tokenizer.lex();
4457 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4458 {
4459 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"manonly section ended without end marker");
4460 }
4462 }
4463 break;
4465 {
4467 retval = parser()->tokenizer.lex();
4469 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4470 {
4471 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"rtfonly section ended without end marker");
4472 }
4474 }
4475 break;
4477 {
4479 retval = parser()->tokenizer.lex();
4481 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4482 {
4483 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"latexonly section ended without end marker");
4484 }
4486 }
4487 break;
4489 {
4491 retval = parser()->tokenizer.lex();
4493 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4494 {
4495 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"xmlonly section ended without end marker");
4496 }
4498 }
4499 break;
4501 {
4503 retval = parser()->tokenizer.lex();
4505 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4506 {
4507 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"docbookonly section ended without end marker");
4508 }
4510 }
4511 break;
4513 {
4516 parser()->tokenizer.lex();
4517
4518 QCString fullMatch = parser()->context.token->verb;
4519 int idx = fullMatch.find('{');
4520 int idxEnd = fullMatch.find("}",idx+1);
4521 StringVector optList;
4522 if (idx != -1) // options present
4523 {
4524 QCString optStr = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4525 optList = split(optStr.str(),",");
4526 for (const auto &opt : optList)
4527 {
4528 if (opt.empty()) continue;
4529 QCString locOpt(opt);
4530 locOpt = locOpt.stripWhiteSpace().lower();
4531 if (locOpt == "code")
4532 {
4534 }
4535 else if (!locOpt.isEmpty())
4536 {
4537 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Unknown option '{}' for '\\iliteral'",opt);
4538 }
4539 }
4540 }
4541
4543 retval = parser()->tokenizer.lex();
4545 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4546 {
4547 if (t == DocVerbatim::JavaDocCode)
4548 {
4549 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"javadoc code section ended without end marker");
4550 }
4551 else
4552 {
4553 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"javadoc literal section ended without end marker");
4554 }
4555 }
4557 }
4558 break;
4561 {
4562 if (cmdId == CommandType::CMD_VERBATIM)
4563 {
4565 }
4566 else
4567 {
4569 }
4570 retval = parser()->tokenizer.lex();
4572 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4573 {
4574 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"verbatim section ended without end marker");
4575 }
4577 }
4578 break;
4580 {
4589 QCString width,height;
4590 parser()->defaultHandleTitleAndSize(CommandType::CMD_DOT,&children().back(),dv->children(),width,height);
4592 retval = parser()->tokenizer.lex();
4593 dv->setText(parser()->context.token->verb);
4594 dv->setWidth(width);
4595 dv->setHeight(height);
4596 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4597 if (!Config_getBool(HAVE_DOT))
4598 {
4599 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"ignoring \\dot command because HAVE_DOT is not set");
4600 children().pop_back();
4601 }
4602 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4603 {
4604 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"dot section ended without end marker");
4605 }
4607 }
4608 break;
4610 {
4619 QCString width,height;
4620 parser()->defaultHandleTitleAndSize(CommandType::CMD_MSC,&children().back(),dv->children(),width,height);
4622 retval = parser()->tokenizer.lex();
4623 dv->setText(parser()->context.token->verb);
4624 dv->setWidth(width);
4625 dv->setHeight(height);
4626 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4627 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4628 {
4629 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"msc section ended without end marker");
4630 }
4632 }
4633 break;
4635 {
4636 QCString jarPath = Config_getString(PLANTUML_JAR_PATH);
4638 parser()->tokenizer.lex();
4639 QCString fullMatch = parser()->context.token->sectionId;
4640 QCString sectionId = "";
4641 int idx = fullMatch.find('{');
4642 int idxEnd = fullMatch.find("}",idx+1);
4643 StringVector optList;
4644 QCString engine;
4645 if (idx != -1) // options present
4646 {
4647 QCString optStr = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4648 optList = split(optStr.str(),",");
4649 for (const auto &opt : optList)
4650 {
4651 if (opt.empty()) continue;
4652 bool found = false;
4653 QCString locOpt(opt);
4654 locOpt = locOpt.stripWhiteSpace().lower();
4655 if (g_plantumlEngine.find(locOpt.str())!=g_plantumlEngine.end())
4656 {
4657 if (!engine.isEmpty())
4658 {
4659 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Multiple definition of engine for '\\startuml'");
4660 }
4661 engine = locOpt;
4662 found = true;
4663 }
4664 if (!found)
4665 {
4666 if (sectionId.isEmpty())
4667 {
4668 sectionId = opt;
4669 }
4670 else
4671 {
4672 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Multiple use of filename for '\\startuml'");
4673 }
4674 }
4675 }
4676 }
4677 else
4678 {
4679 sectionId = parser()->context.token->sectionId;
4680 }
4681 if (engine.isEmpty()) engine = "uml";
4682
4683 if (sectionId.isEmpty())
4684 {
4686 retval = parser()->tokenizer.lex();
4687 assert(retval.is(TokenRetval::RetVal_OK));
4688
4689 sectionId = parser()->context.token->sectionId;
4690 sectionId = sectionId.stripWhiteSpace();
4691 }
4692
4693 QCString plantFile(sectionId);
4698 FALSE,plantFile);
4700 dv->setEngine(engine);
4702 QCString width,height;
4703 parser()->defaultHandleTitleAndSize(CommandType::CMD_STARTUML,&children().back(),dv->children(),width,height);
4705 retval = parser()->tokenizer.lex();
4706 int line = 0;
4707 QCString trimmedVerb = stripLeadingAndTrailingEmptyLines(parser()->context.token->verb,line);
4708 if (engine == "ditaa")
4709 {
4710 dv->setUseBitmap(true);
4711 }
4712 else if (engine == "uml")
4713 {
4714 int i = trimmedVerb.find('\n');
4715 QCString firstLine = i==-1 ? trimmedVerb : trimmedVerb.left(i);
4716 if (firstLine.stripWhiteSpace() == "ditaa") dv->setUseBitmap(true);
4717 }
4718 dv->setText(trimmedVerb);
4719 dv->setWidth(width);
4720 dv->setHeight(height);
4721 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4722 if (jarPath.isEmpty())
4723 {
4724 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"ignoring \\startuml command because PLANTUML_JAR_PATH is not set");
4725 children().pop_back();
4726 }
4727 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4728 {
4729 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"startuml section ended without end marker");
4730 }
4732 }
4733 break;
4735 {
4737 parser()->tokenizer.lex();
4738 QCString fullMatch = parser()->context.token->sectionId;
4739 QCString sectionId = "";
4740 int idx = fullMatch.find('{');
4741 int idxEnd = fullMatch.find("}",idx+1);
4742 if (idx != -1) // options present
4743 {
4744 sectionId = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4745 }
4746 else
4747 {
4748 sectionId = parser()->context.token->sectionId;
4749 }
4750
4751 if (sectionId.isEmpty())
4752 {
4754 retval = parser()->tokenizer.lex();
4755 assert(retval.is(TokenRetval::RetVal_OK));
4756
4757 sectionId = parser()->context.token->sectionId;
4758 sectionId = sectionId.stripWhiteSpace();
4759 }
4760
4761 QCString mermaidFile(sectionId);
4766 FALSE,mermaidFile);
4769 QCString width,height;
4770 parser()->defaultHandleTitleAndSize(CommandType::CMD_MERMAID,&children().back(),dv->children(),width,height);
4772 retval = parser()->tokenizer.lex();
4773 int line = 0;
4774 QCString trimmedVerb = stripLeadingAndTrailingEmptyLines(parser()->context.token->verb,line);
4775 dv->setText(trimmedVerb);
4776 dv->setWidth(width);
4777 dv->setHeight(height);
4778 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4779 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4780 {
4781 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"mermaid section ended without end marker");
4782 }
4784 }
4785 break;
4787 retval = Token::make_RetVal_EndParBlock();
4788 break;
4805 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4806 break;
4808 retval = handleParamSection(cmdName,DocParamSect::Param,FALSE,parser()->context.token->paramDir);
4810 break;
4812 retval = handleParamSection(cmdName,DocParamSect::TemplateParam,FALSE,parser()->context.token->paramDir);
4813 break;
4815 retval = handleParamSection(cmdName,DocParamSect::RetVal);
4816 break;
4819 break;
4821 retval = handleXRefItem();
4822 break;
4824 {
4826 }
4827 break;
4830 {
4832 }
4833 break;
4835 {
4837 }
4838 break;
4840 {
4844 retval = children().get_last<DocIndexEntry>()->parse();
4845 }
4846 break;
4848 retval = Token::make_RetVal_Internal();
4849 break;
4851 retval = Token::make_RetVal_EndInternal();
4852 break;
4854 {
4856 retval = children().get_last<DocParBlock>()->parse();
4857 }
4858 break;
4859 case CommandType::CMD_COPYDOC: // fall through
4860 case CommandType::CMD_COPYBRIEF: // fall through
4862 //retval = Token::make_RetVal_CopyDoc();
4863 // these commands should already be resolved by processCopyDoc()
4864 break;
4867 break;
4870 break;
4873 break;
4876 break;
4879 break;
4882 break;
4885 break;
4888 break;
4891 break;
4894 break;
4897 break;
4900 break;
4903 break;
4906 break;
4909 break;
4912 break;
4915 break;
4917 if (!Config_getBool(HAVE_DOT))
4918 {
4919 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
4920 "ignoring \\dotfile command because HAVE_DOT is not set");
4921 }
4922 else
4923 {
4924 handleFile<DocDotFile>(cmdName);
4925 }
4926 break;
4929 break;
4931 handleFile<DocMscFile>(cmdName);
4932 break;
4934 handleFile<DocDiaFile>(cmdName);
4935 break;
4938 break;
4941 break;
4943 handleLink(cmdName,FALSE);
4944 break;
4946 handleLink(cmdName,TRUE);
4947 break;
4949 handleCite(cmdChar,cmdName);
4950 break;
4952 handleEmoji(cmdChar,cmdName);
4953 break;
4955 handleDoxyConfig(cmdChar,cmdName);
4956 break;
4958 // fall through
4960 parser()->handleRef(thisVariant(),children(),cmdChar,cmdName);
4961 break;
4963 {
4966 }
4967 break;
4969 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4970 break;
4972 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4973 break;
4975 {
4977 }
4978 break;
4979 //case CommandType::CMD_LANGSWITCH:
4980 // retval = handleLanguageSwitch();
4981 // break;
4983 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4984 {
4987 }
4988 break;
4991 break;
4993 handleShowDate(cmdChar,cmdName);
4994 break;
4996 parser()->handleILine(cmdChar,cmdName);
4997 break;
4999 parser()->handleIFile(cmdChar,cmdName);
5000 break;
5002 {
5004 (void)parser()->tokenizer.lex();
5006 //printf("Found scope='%s'\n",qPrint(parser()->context.context));
5008 }
5009 break;
5010 default:
5011 // we should not get here!
5012 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' in paragraph context",cmdName);
5013 break;
5014 }
5015 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_OK,TokenRetval::RetVal_SimpleSec
5016 TokenRetval::TK_LISTITEM,TokenRetval::TK_ENDLIST,TokenRetval::TK_NEWPARA
5017 TokenRetval::RetVal_Section,TokenRetval::RetVal_EndList
5018 TokenRetval::RetVal_Internal,TokenRetval::RetVal_SwitchLang
5019 TokenRetval::RetVal_EndInternal)
5020 );
5021 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5022 return retval;
5023}
5024
5025static bool findAttribute(const HtmlAttribList &tagHtmlAttribs,
5026 const char *attrName,
5027 QCString *result)
5028{
5029
5030 for (const auto &opt : tagHtmlAttribs)
5031 {
5032 if (opt.name==attrName)
5033 {
5034 *result = opt.value;
5035 return TRUE;
5036 }
5037 }
5038 return FALSE;
5039}
5040
5041Token DocPara::handleHtmlStartTag(const QCString &tagName,const HtmlAttribList &tagHtmlAttribs)
5042{
5043 AUTO_TRACE("tagName={} #tagHtmlAttrs={}",tagName,tagHtmlAttribs.size());
5044 Token retval = Token::make_RetVal_OK();
5045 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
5046 if (parser()->context.token->emptyTag && !(tagId>HtmlTagType::XML_CmdMask) &&
5049 {
5050 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"HTML tag ('<{}/>') may not use the 'empty tag' XHTML syntax.",
5051 tagName);
5052 }
5053 switch (tagId)
5054 {
5056 if (!parser()->context.token->emptyTag)
5057 {
5059 tagHtmlAttribs,DocHtmlList::Unordered);
5060 retval=children().get_last<DocHtmlList>()->parse();
5061 }
5062 break;
5064 if (!parser()->context.token->emptyTag)
5065 {
5067 tagHtmlAttribs,DocHtmlList::Ordered);
5068 retval=children().get_last<DocHtmlList>()->parse();
5069 }
5070 break;
5072 if (parser()->context.token->emptyTag) break;
5074 {
5075 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <li> tag found");
5076 }
5077 else
5078 {
5079 retval = Token::make_RetVal_ListItem();
5080 }
5081 break;
5083 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Bold,tagName,&parser()->context.token->attribs);
5084 break;
5086 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::S,tagName,&parser()->context.token->attribs);
5087 break;
5089 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Strike,tagName,&parser()->context.token->attribs);
5090 break;
5092 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Del,tagName,&parser()->context.token->attribs);
5093 break;
5095 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Underline,tagName,&parser()->context.token->attribs);
5096 break;
5098 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Ins,tagName,&parser()->context.token->attribs);
5099 break;
5101 if (parser()->context.token->emptyTag) break;
5102 if (parser()->context.xmlComment)
5103 // for C# source or inside a <summary> or <remark> section we
5104 // treat <code> as an XML tag (so similar to @code)
5105 {
5107 retval = handleStartCode();
5108 }
5109 else // normal HTML markup
5110 {
5111 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
5112 }
5113 break;
5115 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Kbd,tagName,&parser()->context.token->attribs);
5116 break;
5118 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Typewriter,tagName,&parser()->context.token->attribs);
5119 break;
5121 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Italic,tagName,&parser()->context.token->attribs);
5122 break;
5124 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Div,tagName,&parser()->context.token->attribs);
5125 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Div,tagName);
5126 break;
5128 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Span,tagName,&parser()->context.token->attribs);
5129 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Span,tagName);
5130 break;
5132 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Subscript,tagName,&parser()->context.token->attribs);
5133 break;
5135 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Superscript,tagName,&parser()->context.token->attribs);
5136 break;
5138 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Center,tagName,&parser()->context.token->attribs);
5139 break;
5141 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Small,tagName,&parser()->context.token->attribs);
5142 break;
5144 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Cite,tagName,&parser()->context.token->attribs);
5145 break;
5147 if (parser()->context.token->emptyTag) break;
5148 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Preformatted,tagName,&parser()->context.token->attribs);
5151 break;
5153 retval = Token::make_TK_NEWPARA();
5154 break;
5156 if (!parser()->context.token->emptyTag)
5157 {
5158 children().append<DocHtmlDescList>(parser(),thisVariant(),tagHtmlAttribs);
5159 retval=children().get_last<DocHtmlDescList>()->parse();
5160 }
5161 break;
5163 if (insideDL(thisVariant()))
5164 {
5165 retval = Token::make_RetVal_DescTitle();
5166 }
5167 else
5168 {
5169 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dt> found");
5170 }
5171 break;
5173 if (insideDL(thisVariant()))
5174 {
5175 retval = Token::make_RetVal_DescData();
5176 }
5177 else
5178 {
5179 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dd> found");
5180 }
5181 break;
5183 if (!parser()->context.token->emptyTag)
5184 {
5185 children().append<DocHtmlTable>(parser(),thisVariant(),tagHtmlAttribs);
5186 retval=children().get_last<DocHtmlTable>()->parse();
5187 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5188 }
5189 break;
5191 retval = Token::make_RetVal_TableRow();
5192 break;
5194 retval = Token::make_RetVal_TableCell();
5195 break;
5197 retval = Token::make_RetVal_TableHCell();
5198 break;
5202 // for time being ignore </t....> tag
5203 break;
5205 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <caption> found");
5206 break;
5208 {
5209 children().append<DocLineBreak>(parser(),thisVariant(),tagHtmlAttribs);
5210 }
5211 break;
5213 {
5214 children().append<DocHorRuler>(parser(),thisVariant(),tagHtmlAttribs);
5215 }
5216 break;
5218 retval = parser()->handleAHref(thisVariant(),children(),tagHtmlAttribs);
5219 break;
5221 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,1);
5222 break;
5224 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,2);
5225 break;
5227 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,3);
5228 break;
5230 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,4);
5231 break;
5233 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,5);
5234 break;
5236 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,6);
5237 break;
5239 {
5240 parser()->handleImg(thisVariant(),children(),tagHtmlAttribs);
5241 }
5242 break;
5244 if (!parser()->context.token->emptyTag)
5245 {
5246 children().append<DocHtmlDetails>(parser(),thisVariant(),tagHtmlAttribs);
5247 retval=children().get_last<DocHtmlDetails>()->parse();
5248 }
5249 break;
5251 if (!parser()->context.token->emptyTag)
5252 {
5253 children().append<DocHtmlBlockQuote>(parser(),thisVariant(),tagHtmlAttribs);
5254 retval = children().get_last<DocHtmlBlockQuote>()->parse();
5255 }
5256 break;
5257
5260 {
5261 if (!parser()->context.token->emptyTag)
5262 {
5264 while (n && !std::holds_alternative<DocHtmlDetails>(*n)) n=::parent(n);
5265 DocHtmlDetails *d = std::get_if<DocHtmlDetails>(n);
5266 if (d)
5267 {
5268 if (!d->summary()) // details section does not have a summary yet
5269 {
5270 d->parseSummary(n,parser()->context.token->attribs);
5271 }
5272 else
5273 {
5274 retval = Token::make_TK_NEWPARA();
5275 }
5276 }
5277 }
5278 }
5279 break;
5283 // fall through
5286 if (!children().empty())
5287 {
5288 retval = Token::make_TK_NEWPARA();
5289 }
5290 break;
5292 if (insideTable(thisVariant()))
5293 {
5294 retval = Token::make_RetVal_TableCell();
5295 }
5296 break;
5297 case HtmlTagType::XML_C:
5298 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
5299 break;
5302 {
5304 QCString paramName;
5305 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5306 {
5307 if (paramName.isEmpty())
5308 {
5309 if (Config_getBool(WARN_NO_PARAMDOC))
5310 {
5311 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"empty 'name' attribute for <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5312 }
5313 }
5314 else
5315 {
5316 retval = handleParamSection(paramName,
5318 TRUE);
5319 }
5320 }
5321 else
5322 {
5323 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5324 }
5325 }
5326 break;
5329 {
5330 QCString paramName;
5331 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5332 {
5333 //printf("paramName=%s\n",qPrint(paramName));
5335 children().append<DocWord>(parser(),thisVariant(),paramName);
5337 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
5338 }
5339 else
5340 {
5341 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}ref> tag.",tagId==HtmlTagType::XML_PARAMREF?"":"type");
5342 }
5343 }
5344 break;
5346 {
5348 QCString exceptName;
5349 if (findAttribute(tagHtmlAttribs,"cref",&exceptName))
5350 {
5351 unescapeCRef(exceptName);
5352 retval = handleParamSection(exceptName,DocParamSect::Exception,TRUE);
5353 }
5354 else
5355 {
5356 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <exception> tag.");
5357 }
5358 }
5359 break;
5362 if (insideTable(thisVariant()))
5363 {
5364 retval = Token::make_RetVal_TableRow();
5365 }
5366 else if (insideUL(thisVariant()) || insideOL(thisVariant()))
5367 {
5368 retval = Token::make_RetVal_ListItem();
5369 }
5370 else
5371 {
5372 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <item> tag found");
5373 }
5374 break;
5379 break;
5381 if (insideTable(thisVariant()))
5382 {
5383 retval = Token::make_RetVal_TableCell();
5384 }
5385 break;
5387 // I'm not sure if <see> is the same as <seealso> or if it
5388 // should you link a member without producing a section. The
5389 // C# specification is extremely vague about this (but what else
5390 // can we expect from Microsoft...)
5391 {
5392 QCString cref;
5393 //printf("HtmlTagType::XML_SEE: empty tag=%d\n",parser()->context.token->emptyTag);
5394 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5395 {
5396 unescapeCRef(cref);
5397 if (parser()->context.token->emptyTag) // <see cref="..."/> style
5398 {
5399 bool inSeeBlock = parser()->context.inSeeBlock;
5400 parser()->context.token->name = cref;
5403 parser()->context.inSeeBlock = inSeeBlock;
5404 }
5405 else // <see cref="...">...</see> style
5406 {
5409 DocLink *lnk = children().get_last<DocLink>();
5410 QCString leftOver = lnk->parse(FALSE,TRUE);
5411 if (!leftOver.isEmpty())
5412 {
5413 children().append<DocWord>(parser(),thisVariant(),leftOver);
5414 }
5415 }
5416 }
5417 else if (findAttribute(tagHtmlAttribs,"langword",&cref)) // <see langword="..."/> or <see langword="..."></see>
5418 {
5419 bool inSeeBlock = parser()->context.inSeeBlock;
5420 parser()->context.token->name = cref;
5425 parser()->context.inSeeBlock = inSeeBlock;
5426 }
5427 else
5428 {
5429 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' or 'langword' attribute from <see> tag.");
5430 }
5431 }
5432 break;
5434 {
5436 QCString cref;
5437 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5438 {
5439 unescapeCRef(cref);
5440 // Look for an existing "see" section
5441 DocNodeVariant *vss=nullptr;
5442 for (auto &n : children())
5443 {
5444 DocSimpleSect *candidate = std::get_if<DocSimpleSect>(&n);
5445 if (candidate && candidate->type()==DocSimpleSect::See)
5446 {
5447 vss = &n;
5448 }
5449 }
5450
5451 if (!vss) // start new section
5452 {
5454 vss = &children().back();
5455 }
5456
5457 std::get<DocSimpleSect>(*vss).appendLinkWord(cref);
5458 retval = Token::make_RetVal_OK();
5459 }
5460 else
5461 {
5462 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <seealso> tag.");
5463 }
5464 }
5465 break;
5467 {
5468 QCString type;
5469 findAttribute(tagHtmlAttribs,"type",&type);
5471 HtmlAttribList emptyList;
5472 if (type=="number")
5473 {
5474 listType=DocHtmlList::Ordered;
5475 }
5476 if (type=="table")
5477 {
5478 children().append<DocHtmlTable>(parser(),thisVariant(),emptyList);
5479 retval=children().get_last<DocHtmlTable>()->parseXml();
5480 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5481 }
5482 else
5483 {
5484 children().append<DocHtmlList>(parser(),thisVariant(),emptyList,listType);
5485 retval=children().get_last<DocHtmlList>()->parseXml();
5486 }
5487 }
5488 break;
5491 // These tags are defined in .Net but are currently unsupported
5493 break;
5495 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag <{}> found", tagName);
5496 children().append<DocWord>(parser(),thisVariant(), "<"+tagName+parser()->context.token->attribsStr+">");
5497 break;
5500 break;
5501 default:
5502 // we should not get here!
5503 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected start tag {}",tagName);
5504 ASSERT(0);
5505 break;
5506 }
5507 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5508 return retval;
5509}
5510
5512{
5513 AUTO_TRACE("tagName={}",tagName);
5514 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
5515 Token retval = Token::make_RetVal_OK();
5516 switch (tagId)
5517 {
5519 if (!insideUL(thisVariant()))
5520 {
5521 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ul> tag without matching <ul>");
5522 }
5523 else
5524 {
5525 retval = Token::make_RetVal_EndList();
5526 }
5527 break;
5529 if (!insideOL(thisVariant()))
5530 {
5531 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ol> tag without matching <ol>");
5532 }
5533 else
5534 {
5535 retval = Token::make_RetVal_EndList();
5536 }
5537 break;
5539 if (!insideLI(thisVariant()))
5540 {
5541 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </li> tag without matching <li>");
5542 }
5543 else
5544 {
5545 // ignore </li> tags
5546 }
5547 break;
5549 if (!insideDetails(thisVariant()))
5550 {
5551 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </details> tag without matching <details>");
5552 }
5553 else
5554 {
5555 retval = Token::make_RetVal_EndHtmlDetails();
5556 }
5557 break;
5560 {
5561 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </blockquote> tag without matching <blockquote>");
5562 }
5563 else
5564 {
5565 retval = Token::make_RetVal_EndBlockQuote();
5566 }
5567 break;
5570 break;
5573 break;
5576 break;
5579 break;
5582 break;
5585 break;
5588 break;
5591 break;
5594 break;
5597 break;
5600 break;
5603 break;
5606 break;
5609 break;
5612 break;
5615 break;
5618 break;
5623 break;
5625 retval = Token::make_TK_NEWPARA();
5626 break;
5628 retval = Token::make_RetVal_EndDesc();
5629 break;
5631 // ignore </dt> tag
5632 break;
5634 // ignore </dd> tag
5635 break;
5637 retval = Token::make_RetVal_EndTable();
5638 break;
5640 retval = Token::make_RetVal_EndTableRow();
5641 break;
5643 retval = Token::make_RetVal_EndTableCell();
5644 break;
5646 retval = Token::make_RetVal_EndTableCell();
5647 break;
5651 // for time being ignore </t....> tag
5652 break;
5654 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </caption> found");
5655 break;
5657 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </br> tag found");
5658 break;
5660 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h1> found");
5661 break;
5663 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h2> found");
5664 break;
5666 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h3> found");
5667 break;
5669 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h4> found");
5670 break;
5672 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h5> found");
5673 break;
5675 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h6> found");
5676 break;
5678 break;
5680 // warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </hr> tag found");
5681 break;
5683 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </a> found");
5684 // ignore </a> tag (can be part of <a name=...></a>
5685 break;
5686
5688 break;
5690 retval = Token::make_TK_NEWPARA();
5691 break;
5704 retval = Token::make_RetVal_CloseXml();
5705 break;
5706 case HtmlTagType::XML_C:
5708 break;
5716 // These tags are defined in .Net but are currently unsupported
5717 break;
5719 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag </{}> found", tagName);
5720 children().append<DocWord>(parser(),thisVariant(),"</"+tagName+">");
5721 break;
5722 default:
5723 // we should not get here!
5724 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end tag {}",tagName);
5725 ASSERT(0);
5726 break;
5727 }
5728 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5729 return retval;
5730}
5731
5733{
5734 // expected hierarchy:
5735 // 1. DocAutoListItem <- n
5736 // 2. DocAutoList <- parent(n)
5737 // 3. DocPara <- parent(parent(n))
5738
5739 // step 1
5740 if (!std::get_if<DocAutoListItem>(n)) // not inside a auto list item
5741 {
5742 return false;
5743 }
5744
5745 // step 2
5746 n = parent(n);
5747 int indent = 0;
5748 const auto docAutoList = std::get_if<DocAutoList>(n);
5749 if (docAutoList) // capture indent
5750 {
5751 indent = docAutoList->indent();
5752 }
5753 else
5754 {
5755 return false;
5756 }
5757
5758 // step 3
5759 n = parent(n);
5760 const auto docPara = std::get_if<DocPara>(n);
5761 if (docPara)
5762 {
5763 QCString tagNameLower = QCString(parser->context.token->name).lower();
5764 auto topStyleChange = [](const DocStyleChangeStack &stack) -> const DocStyleChange &
5765 {
5766 return std::get<DocStyleChange>(*stack.top());
5767 };
5768
5769 if (parser->context.styleStack.empty() || // no style change
5770 (topStyleChange(parser->context.styleStack).tagName()==tagNameLower && // correct style change
5771 topStyleChange(parser->context.styleStack).position()!=parser->context.nodeStack.size()) // wrong position, so normal close
5772 )
5773 {
5774 // insert an artificial 'end of autolist' marker and parse again
5775 QCString indentStr;
5776 indentStr.fill(' ',indent);
5777 parser->tokenizer.unputString("\\ilinebr "+indentStr+".\\ilinebr"+indentStr+"</"+parser->context.token->name+">");
5778 return true;
5779 }
5780 }
5781 return false;
5782}
5783
5785{
5786 AUTO_TRACE();
5787 auto ns = AutoNodeStack(parser(),thisVariant());
5788 // handle style commands "inherited" from the previous paragraph
5790 Token tok=parser()->tokenizer.lex();
5791 Token retval = Token::make_TK_NONE();
5792 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
5793 {
5794reparsetoken:
5795 AUTO_TRACE_ADD("token '{}' at {}",tok.to_string(),parser()->tokenizer.getLineNr());
5796 if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL,
5797 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG)
5798 )
5799 {
5800 AUTO_TRACE_ADD("name={}",parser()->context.token->name);
5801 }
5802 switch(tok.value())
5803 {
5804 case TokenRetval::TK_WORD:
5806 break;
5807 case TokenRetval::TK_LNKWORD:
5809 break;
5810 case TokenRetval::TK_URL:
5812 break;
5813 case TokenRetval::TK_WHITESPACE:
5814 {
5815 // prevent leading whitespace and collapse multiple whitespace areas
5816 if (insidePRE(thisVariant()) || // all whitespace is relevant
5817 (
5818 // remove leading whitespace
5819 !children().empty() &&
5820 // and whitespace after certain constructs
5824 )
5825 )
5826 {
5828 }
5829 }
5830 break;
5831 case TokenRetval::TK_LISTITEM:
5832 {
5833 AUTO_TRACE_ADD("found list item at {}",parser()->context.token->indent);
5834 const DocNodeVariant *n=parent();
5835 while (n && !std::holds_alternative<DocAutoList>(*n)) n=::parent(n);
5836 const DocAutoList *al = std::get_if<DocAutoList>(n);
5837 if (al) // we found an auto list up in the hierarchy
5838 {
5839 AUTO_TRACE_ADD("previous list item at {}",al->indent());
5840 if (al->indent()>=parser()->context.token->indent)
5841 // new item at the same or lower indent level
5842 {
5843 retval = Token::make_TK_LISTITEM();
5844 goto endparagraph;
5845 }
5846 }
5847
5848 // determine list depth
5849 int depth = 0;
5850 n=parent();
5851 while (n)
5852 {
5853 al = std::get_if<DocAutoList>(n);
5854 if (al && al->isEnumList()) depth++;
5855 n=::parent(n);
5856 }
5857
5858 // first item or sub list => create new list
5859 do
5860 {
5863 parser()->context.token->isEnumList,depth,
5865 al = children().get_last<DocAutoList>();
5866 retval = children().get_last<DocAutoList>()->parse();
5867 } while (retval.is(TokenRetval::TK_LISTITEM) && // new list
5868 al->indent()==parser()->context.token->indent // at same indent level
5869 );
5870
5871 // check the return value
5872 if (retval.is(TokenRetval::RetVal_SimpleSec)) // auto list ended due to simple section command
5873 {
5874 // Reparse the token that ended the section at this level,
5875 // so a new simple section will be started at this level.
5876 // This is the same as unputting the last read token and continuing.
5878 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5879 {
5882 tok = Token::make_TK_RCSTAG();
5883 }
5884 else // other section
5885 {
5886 tok = Token::make_TK_COMMAND_BS();
5887 }
5888 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5889 goto reparsetoken;
5890 }
5891 else if (retval.is(TokenRetval::TK_ENDLIST))
5892 {
5893 if (al->indent()>parser()->context.token->indent) // end list
5894 {
5895 goto endparagraph;
5896 }
5897 else // continue with current paragraph
5898 {
5899 }
5900 }
5901 else // paragraph ended due to TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, or EOF
5902 {
5903 goto endparagraph;
5904 }
5905 }
5906 break;
5907 case TokenRetval::TK_ENDLIST:
5908 AUTO_TRACE_ADD("Found end of list inside of paragraph at line {}",parser()->tokenizer.getLineNr());
5909 if (std::get_if<DocAutoListItem>(parent()))
5910 {
5911 const DocAutoList *al = std::get_if<DocAutoList>(::parent(parent()));
5912 if (al && al->indent()>=parser()->context.token->indent)
5913 {
5914 // end of list marker ends this paragraph
5915 retval = Token::make_TK_ENDLIST();
5916 goto endparagraph;
5917 }
5918 else
5919 {
5920 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found "
5921 "has invalid indent level");
5922 }
5923 }
5924 else
5925 {
5926 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found without any preceding "
5927 "list items");
5928 }
5929 break;
5930 case TokenRetval::TK_COMMAND_AT:
5931 // fall through
5932 case TokenRetval::TK_COMMAND_BS:
5933 {
5934 // see if we have to start a simple section
5935 CommandType cmd = Mappers::cmdMapper->map(parser()->context.token->name);
5936 const DocNodeVariant *n=parent();
5937 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
5938 !std::holds_alternative<DocParamSect>(*n))
5939 {
5940 n=::parent(n);
5941 }
5943 {
5944 if (n) // already in a simple section
5945 {
5946 // simple section cannot start in this paragraph, need
5947 // to unwind the stack and remember the command.
5949 retval = Token::make_RetVal_SimpleSec();
5950 goto endparagraph;
5951 }
5952 }
5953 // see if we are in a simple list
5954 n=parent();
5955 while (n && !std::holds_alternative<DocSimpleListItem>(*n)) n=::parent(n);
5956 if (n)
5957 {
5958 if (cmd==CommandType::CMD_LI)
5959 {
5960 retval = Token::make_RetVal_ListItem();
5961 goto endparagraph;
5962 }
5963 }
5964
5965 // handle the command
5966 retval=handleCommand(tok.command_to_char(),parser()->context.token->name);
5967 AUTO_TRACE_ADD("handleCommand returns {}",retval.to_string());
5968
5969 // check the return value
5970 if (retval.is(TokenRetval::RetVal_SimpleSec))
5971 {
5972 // Reparse the token that ended the section at this level,
5973 // so a new simple section will be started at this level.
5974 // This is the same as unputting the last read token and continuing.
5976 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5977 {
5980 tok = Token::make_TK_RCSTAG();
5981 }
5982 else // other section
5983 {
5984 tok = Token::make_TK_COMMAND_BS();
5985 }
5986 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5987 goto reparsetoken;
5988 }
5989 else if (retval.value()>TokenRetval::TK_NONE && retval.value()<TokenRetval::RetVal_OK)
5990 {
5991 // the command ended with a new command, reparse this token
5992 tok = retval;
5993 goto reparsetoken;
5994 }
5995 else if (retval.value()!=TokenRetval::RetVal_OK) // end of file, end of paragraph, start or end of section
5996 // or some auto list marker
5997 {
5998 goto endparagraph;
5999 }
6000 }
6001 break;
6002 case TokenRetval::TK_HTMLTAG:
6003 {
6004 if (!parser()->context.token->endTag) // found a start tag
6005 {
6006 retval = handleHtmlStartTag(parser()->context.token->name,parser()->context.token->attribs);
6007 }
6008 else // found an end tag
6009 {
6011 {
6012 break; // new code has been pushed back to the scanner, need to reparse
6013 }
6014 retval = handleHtmlEndTag(parser()->context.token->name);
6015 }
6016 if (!retval.is(TokenRetval::RetVal_OK))
6017 {
6018 goto endparagraph;
6019 }
6020 }
6021 break;
6022 case TokenRetval::TK_SYMBOL:
6023 {
6024 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
6026 {
6028 }
6029 else
6030 {
6032 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
6033 parser()->context.token->name);
6034 }
6035 break;
6036 }
6037 case TokenRetval::TK_NEWPARA:
6038 retval = Token::make_TK_NEWPARA();
6039 goto endparagraph;
6040 case TokenRetval::TK_RCSTAG:
6041 {
6042 const DocNodeVariant *n=parent();
6043 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
6044 !std::holds_alternative<DocParamSect>(*n))
6045 {
6046 n=::parent(n);
6047 }
6048 if (n) // already in a simple section
6049 {
6050 // simple section cannot start in this paragraph, need
6051 // to unwind the stack and remember the command.
6054 retval = Token::make_RetVal_SimpleSec();
6055 goto endparagraph;
6056 }
6057
6058 // see if we are in a simple list
6060 children().get_last<DocSimpleSect>()->parseRcs();
6061 }
6062 break;
6063 default:
6064 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6065 "Found unexpected token (id={})",tok.to_string());
6066 break;
6067 }
6068 tok=parser()->tokenizer.lex();
6069 }
6070 retval=Token::make_TK_NONE();
6071endparagraph:
6073 DocPara *par = std::get_if<DocPara>(parser()->context.nodeStack.top());
6074 if (!parser()->context.token->endTag && par &&
6075 retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->name.lower() == "p")
6076 {
6077 par->setAttribs(parser()->context.token->attribs);
6078 }
6079 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM,
6080 TokenRetval::TK_ENDLIST,TokenRetval::RetVal_OK)
6081 );
6082
6083 AUTO_TRACE_EXIT("retval={}",retval.to_string());
6084 return retval;
6085}
6086
6087//--------------------------------------------------------------------------
6088
6090{
6091 AUTO_TRACE("start {} level={}", parser()->context.token->sectionId, m_level);
6092 Token retval = Token::make_RetVal_OK();
6093 auto ns = AutoNodeStack(parser(),thisVariant());
6094
6095 if (!m_id.isEmpty())
6096 {
6098 if (sec)
6099 {
6100 m_file = sec->fileName();
6101 m_anchor = sec->label();
6102 QCString titleStr = sec->title();
6103 if (titleStr.isEmpty()) titleStr = sec->label();
6105 DocTitle *title = &std::get<DocTitle>(*m_title);
6106 title->parseFromString(thisVariant(),titleStr);
6107 }
6108 }
6109
6110 // first parse any number of paragraphs
6111 bool isFirst=TRUE;
6112 DocPara *lastPar=nullptr;
6113 do
6114 {
6116 DocPara *par = children().get_last<DocPara>();
6117 if (isFirst) { par->markFirst(); isFirst=FALSE; }
6118 retval=par->parse();
6119 if (!par->isEmpty())
6120 {
6121 if (lastPar) lastPar->markLast(FALSE);
6122 lastPar = par;
6123 }
6124 else
6125 {
6126 children().pop_back();
6127 }
6128 if (retval.is(TokenRetval::TK_LISTITEM))
6129 {
6130 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6131 }
6132 if (retval.is(TokenRetval::RetVal_Internal))
6133 {
6135 retval = children().get_last<DocInternal>()->parse(m_level+1);
6136 if (retval.is(TokenRetval::RetVal_EndInternal))
6137 {
6138 retval = Token::make_RetVal_OK();
6139 }
6140 }
6141 } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6142 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph,
6143 TokenRetval::RetVal_SubSubParagraph, TokenRetval::RetVal_EndInternal)
6144 );
6145
6146 if (lastPar) lastPar->markLast();
6147
6148 while (true)
6149 {
6150 if (retval.is(TokenRetval::RetVal_Subsection) && m_level<=1)
6151 {
6152 // then parse any number of nested sections
6153 while (retval.is(TokenRetval::RetVal_Subsection)) // more sections follow
6154 {
6156 2,
6158 retval = children().get_last<DocSection>()->parse();
6159 }
6160 break;
6161 }
6162 else if (retval.is(TokenRetval::RetVal_Subsubsection) && m_level<=2)
6163 {
6164 if ((m_level <= 1) &&
6165 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6166 {
6167 warn_doc_error(parser()->context.fileName,
6168 parser()->tokenizer.getLineNr(),
6169 "Unexpected subsubsection command found inside {}!",
6171 }
6172 // then parse any number of nested sections
6173 while (retval.is(TokenRetval::RetVal_Subsubsection)) // more sections follow
6174 {
6176 3,
6178 retval = children().get_last<DocSection>()->parse();
6179 }
6180 if (!(m_level < 2 && retval.is(TokenRetval::RetVal_Subsection))) break;
6181 }
6182 else if (retval.is(TokenRetval::RetVal_Paragraph) && m_level<=3)
6183 {
6184 if ((m_level <= 2) &&
6185 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6186 {
6187 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6188 "Unexpected paragraph command found inside {}!",
6190 }
6191 // then parse any number of nested sections
6192 while (retval.is(TokenRetval::RetVal_Paragraph)) // more sections follow
6193 {
6195 4,
6197 retval = children().get_last<DocSection>()->parse();
6198 }
6199 if (!(m_level<3 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection)))) break;
6200 }
6201 else if (retval.is(TokenRetval::RetVal_SubParagraph) && m_level<=4)
6202 {
6203 if ((m_level <= 3) &&
6204 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6205 {
6206 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6207 "Unexpected subparagraph command found inside {}!",
6209 }
6210 // then parse any number of nested sections
6211 while (retval.is(TokenRetval::RetVal_SubParagraph)) // more sections follow
6212 {
6214 5,
6216 retval = children().get_last<DocSection>()->parse();
6217 }
6218 if (!(m_level<4 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection,TokenRetval::RetVal_Paragraph)))) break;
6219 }
6220 else if (retval.is(TokenRetval::RetVal_SubSubParagraph) && m_level<=5)
6221 {
6222 if ((m_level <= 4) &&
6223 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6224 {
6225 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6226 "Unexpected subsubparagraph command found inside {}!",
6228 }
6229 // then parse any number of nested sections
6230 while (retval.is(TokenRetval::RetVal_SubSubParagraph)) // more sections follow
6231 {
6233 6,
6235 retval = children().get_last<DocSection>()->parse();
6236 }
6237 if (!(m_level<5 && (retval.is_any_of( TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection,
6238 TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph)))) break;
6239 }
6240 else
6241 {
6242 break;
6243 }
6244 }
6245
6246 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF,
6247 TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6248 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph,
6249 TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph,
6250 TokenRetval::RetVal_Internal, TokenRetval::RetVal_EndInternal)
6251 );
6252
6253 AUTO_TRACE_EXIT("retval={}", retval.to_string());
6254 return retval;
6255}
6256
6257//--------------------------------------------------------------------------
6258
6260{
6261 AUTO_TRACE();
6262 auto ns = AutoNodeStack(parser(),thisVariant());
6264
6265 Token tok = parser()->tokenizer.lex();
6266 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
6267 {
6268 switch(tok.value())
6269 {
6270 case TokenRetval::TK_WORD:
6272 break;
6273 case TokenRetval::TK_WHITESPACE:
6275 break;
6276 case TokenRetval::TK_SYMBOL:
6277 {
6278 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
6280 {
6282 }
6283 else
6284 {
6285 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
6286 parser()->context.token->name);
6287 }
6288 }
6289 break;
6290 case TokenRetval::TK_COMMAND_AT:
6291 // fall through
6292 case TokenRetval::TK_COMMAND_BS:
6293 switch (Mappers::cmdMapper->map(parser()->context.token->name))
6294 {
6297 break;
6300 break;
6303 break;
6306 break;
6309 break;
6312 break;
6315 break;
6318 break;
6321 break;
6325 break;
6330 break;
6333 break;
6336 break;
6339 break;
6342 break;
6345 break;
6348 break;
6351 break;
6352 default:
6353 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' found",
6354 parser()->context.token->name);
6355 break;
6356 }
6357 break;
6358 default:
6359 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",
6360 tok.to_string());
6361 break;
6362 }
6363 tok = parser()->tokenizer.lex();
6364 }
6365
6367
6368}
6369
6370
6371//--------------------------------------------------------------------------
6372
6374{
6375 AUTO_TRACE();
6376 auto ns = AutoNodeStack(parser(),thisVariant());
6378 Token retval = Token::make_TK_NONE();
6379
6380 // first parse any number of paragraphs
6381 bool isFirst=TRUE;
6382 DocPara *lastPar = nullptr;
6383 do
6384 {
6385 {
6387 DocPara *par = children().get_last<DocPara>();
6388 if (isFirst) { par->markFirst(); isFirst=FALSE; }
6389 retval=par->parse();
6390 if (par->isEmpty() && par->attribs().empty())
6391 {
6392 children().pop_back();
6393 }
6394 else
6395 {
6396 lastPar = par;
6397 }
6398 }
6399 auto checkParagraph = [this,&retval](Token t,int level,const char *sectionType,const char *parentSectionType) {
6400 if (retval == t)
6401 {
6402 if (!AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6403 {
6404 warn_doc_error(parser()->context.fileName,
6405 parser()->tokenizer.getLineNr(),
6406 "found {} command (id: '{}') outside of {} context!",
6407 sectionType,parser()->context.token->sectionId,parentSectionType);
6408 }
6409 while (retval==t)
6410 {
6411 if (!parser()->context.token->sectionId.isEmpty())
6412 {
6413 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6414 if (sec)
6415 {
6417 level,
6419 retval = children().get_last<DocSection>()->parse();
6420 }
6421 else
6422 {
6423 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid {} id '{}'; ignoring {}",
6424 sectionType,parser()->context.token->sectionId,sectionType);
6425 retval = Token::make_TK_NONE();
6426 }
6427 }
6428 else
6429 {
6430 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for {}; ignoring {}",sectionType,sectionType);
6431 retval = Token::make_TK_NONE();
6432 }
6433 }
6434 }
6435 };
6436 checkParagraph(Token::make_RetVal_SubSubParagraph(), 6, "subsubparagraph", "subparagraph" );
6437 checkParagraph(Token::make_RetVal_SubParagraph(), 5, "subparagraph", "paragraph" );
6438 checkParagraph(Token::make_RetVal_Paragraph(), 4, "paragraph", "subsubsection" );
6439 checkParagraph(Token::make_RetVal_Subsubsection(), 3, "subsubsection", "subsection" );
6440 checkParagraph(Token::make_RetVal_Subsection(), 2, "subsection", "section" );
6441
6442 if (retval.is(TokenRetval::TK_LISTITEM))
6443 {
6444 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6445 }
6446 if (retval.is(TokenRetval::RetVal_Internal))
6447 {
6449 retval = children().get_last<DocInternal>()->parse(1);
6450 }
6451 } while (!retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_Section));
6452 if (lastPar) lastPar->markLast();
6453
6454 //printf("DocRoot::parse() retval=%d %d\n",retval,TokenRetval::RetVal_Section);
6455 // then parse any number of level1 sections
6456 while (retval.is(TokenRetval::RetVal_Section))
6457 {
6458 if (!parser()->context.token->sectionId.isEmpty())
6459 {
6460 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6461 if (sec)
6462 {
6464 1,
6466 retval = children().get_last<DocSection>()->parse();
6467 }
6468 else
6469 {
6470 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid section id '{}'; ignoring section",parser()->context.token->sectionId);
6471 retval = Token::make_TK_NONE();
6472 }
6473 }
6474 else
6475 {
6476 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for section; ignoring section");
6477 retval = Token::make_TK_NONE();
6478 }
6479 }
6480
6482}
bool isAliasCmd(std::string_view aliasCmd)
Definition aliases.cpp:518
static AnchorGenerator & instance()
Returns the singleton instance.
Definition anchor.cpp:38
Citation manager class.
Definition cite.h:85
QCString anchorPrefix() const
Definition cite.cpp:126
const CiteInfo * find(const QCString &label) const
Return the citation info for a given label.
Definition cite.cpp:101
static CitationManager & instance()
Definition cite.cpp:85
QCString fileName() const
Definition cite.cpp:121
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
Class representing a Boolean type option.
Definition configimpl.h:255
QCString * valueStringRef()
Definition configimpl.h:265
Class representing an enum type option.
Definition configimpl.h:157
QCString * valueRef()
Definition configimpl.h:169
static ConfigImpl * instance()
Definition configimpl.h:351
ConfigOption * get(const QCString &name) const
Definition configimpl.h:400
Class representing an integer type option.
Definition configimpl.h:220
QCString * valueStringRef()
Definition configimpl.h:232
Class representing a list type option.
Definition configimpl.h:125
Abstract base class for any configuration option.
Definition configimpl.h:39
@ O_Disabled
Disabled compile time option.
Definition configimpl.h:55
@ O_List
A list of items.
Definition configimpl.h:49
@ O_Enum
A fixed set of items.
Definition configimpl.h:50
@ O_Bool
A boolean value.
Definition configimpl.h:53
@ O_String
A single item.
Definition configimpl.h:51
@ O_Obsolete
An obsolete option.
Definition configimpl.h:54
@ O_Int
An integer value.
Definition configimpl.h:52
@ O_Info
A section header.
Definition configimpl.h:48
OptionType kind() const
Definition configimpl.h:70
Class representing a string type option.
Definition configimpl.h:188
QCString * valueRef()
Definition configimpl.h:201
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual bool isLinkable() const =0
virtual DefType definitionType() const =0
virtual QCString briefDescription(bool abbreviate=FALSE) const =0
virtual QCString getReference() const =0
virtual QCString getSourceFileBase() const =0
virtual QCString documentation() const =0
virtual QCString getOutputFileBase() const =0
virtual Definition * getOuterScope() const =0
virtual const QCString & name() const =0
DocAnchor(DocParser *parser, DocNodeVariant *parent, const QCString &id, bool newAnchor)
Definition docnode.cpp:211
QCString m_anchor
Definition docnode.h:238
QCString m_file
Definition docnode.h:239
Node representing an auto List.
Definition docnode.h:571
int m_depth
Definition docnode.h:590
bool isCheckedList() const
Definition docnode.h:582
bool isEnumList() const
Definition docnode.h:580
int depth() const
Definition docnode.h:583
int m_indent
Definition docnode.h:587
Token parse()
Definition docnode.cpp:3062
bool m_isCheckedList
Definition docnode.h:589
int indent() const
Definition docnode.h:581
DocAutoList(DocParser *parser, DocNodeVariant *parent, int indent, bool isEnumList, int depth, bool isCheckedList)
Definition docnode.cpp:3055
bool m_isEnumList
Definition docnode.h:588
Node representing an item of a auto list.
Definition docnode.h:595
DocAutoListItem(DocParser *parser, DocNodeVariant *parent, int indent, int num)
Definition docnode.cpp:3015
Node representing a citation of some bibliographic reference.
Definition docnode.h:245
QCString m_anchor
Definition docnode.h:260
QCString getText() const
Definition docnode.cpp:974
QCString m_target
Definition docnode.h:261
QCString m_relPath
Definition docnode.h:258
QCString m_ref
Definition docnode.h:259
QCString target() const
Definition docnode.h:252
DocCite(DocParser *parser, DocNodeVariant *parent, const QCString &target, const QCString &context, CiteInfoOption opt)
Definition docnode.cpp:938
QCString m_file
Definition docnode.h:257
CiteInfoOption m_option
Definition docnode.h:262
DocNodeList & children()
Definition docnode.h:143
DocCompoundNode(DocParser *parser, DocNodeVariant *parent)
Definition docnode.h:141
bool parse()
Definition docnode.cpp:1210
DocDiaFile(DocParser *parser, DocNodeVariant *parent, const QCString &name, const QCString &context, const QCString &srcFile, int srcLine)
Definition docnode.cpp:1203
QCString srcFile() const
Definition docnode.h:691
std::unique_ptr< Private > p
Definition docnode.h:708
int srcLine() const
Definition docnode.h:692
DocDiagramFileBase(DocParser *parser, DocNodeVariant *parent, const QCString &name, const QCString &context, const QCString &srcFile, int srcLine)
Definition docnode.h:681
QCString context() const
Definition docnode.h:690
QCString name() const
Definition docnode.h:684
bool parse()
Definition docnode.cpp:1132
DocDotFile(DocParser *parser, DocNodeVariant *parent, const QCString &name, const QCString &context, const QCString &srcFile, int srcLine)
Definition docnode.cpp:1125
Node representing an emoji.
Definition docnode.h:341
DocEmoji(DocParser *parser, DocNodeVariant *parent, const QCString &symName)
Definition docnode.cpp:162
int m_index
Definition docnode.h:349
QCString m_symName
Definition docnode.h:348
Node representing an item of a cross-referenced list.
Definition docnode.h:529
QCString m_relPath
Definition docnode.h:546
QCString m_text
Definition docnode.h:545
int id() const
Definition docnode.h:535
QCString m_name
Definition docnode.h:544
DocFormula(DocParser *parser, DocNodeVariant *parent, int id)
Definition docnode.cpp:517
QCString relPath() const
Definition docnode.h:534
Token parse()
Definition docnode.cpp:1569
Node representing a horizontal ruler.
Definition docnode.h:216
Node representing an HTML blockquote.
Definition docnode.h:1297
HtmlAttribList m_attribs
Definition docnode.h:1244
bool m_hasCaptionId
Definition docnode.h:1245
DocHtmlCaption(DocParser *parser, DocNodeVariant *parent, const HtmlAttribList &attribs)
Definition docnode.cpp:1776
QCString m_file
Definition docnode.h:1246
const HtmlAttribList & attribs() const
Definition docnode.h:1237
QCString m_anchor
Definition docnode.h:1247
Node representing a HTML table cell.
Definition docnode.h:1199
Valignment valignment() const
Definition docnode.cpp:1980
void setColumnIndex(uint32_t idx)
Definition docnode.h:1223
bool isFirst() const
Definition docnode.h:1207
Token parseXml()
Definition docnode.cpp:1884
void setRowIndex(uint32_t idx)
Definition docnode.h:1222
void markLast(bool v=TRUE)
Definition docnode.h:1210
uint32_t rowSpan() const
Definition docnode.cpp:1918
void markFirst(bool v=TRUE)
Definition docnode.h:1209
Alignment alignment() const
Definition docnode.cpp:1942
bool isHeading() const
Definition docnode.h:1206
const HtmlAttribList & attribs() const
Definition docnode.h:1211
Token parse()
Definition docnode.cpp:1850
uint32_t colSpan() const
Definition docnode.cpp:1930
Node representing a HTML description data.
Definition docnode.h:1187
HtmlAttribList m_attribs
Definition docnode.h:1194
Node representing a Html description list.
Definition docnode.h:910
Node representing a Html description item.
Definition docnode.h:897
Node Html details.
Definition docnode.h:866
const HtmlAttribList & attribs() const
Definition docnode.h:870
void parseSummary(DocNodeVariant *, HtmlAttribList &attribs)
Definition docnode.cpp:1559
const DocNodeVariant * summary() const
Definition docnode.h:873
std::unique_ptr< DocNodeVariant > m_summary
Definition docnode.h:877
Node Html heading.
Definition docnode.h:882
Token parse()
Definition docnode.cpp:1379
Node representing a Html list.
Definition docnode.h:1009
Type m_type
Definition docnode.h:1020
Token parseXml()
Definition docnode.cpp:2862
Token parse()
Definition docnode.cpp:2787
Node representing a HTML list item.
Definition docnode.h:1171
Node representing a HTML table row.
Definition docnode.h:1252
Token parseXml(bool header)
Definition docnode.cpp:2184
void setVisibleCells(uint32_t n)
Definition docnode.h:1262
bool isHeading() const
Definition docnode.cpp:2002
void setRowIndex(uint32_t idx)
Definition docnode.h:1267
Token parse()
Definition docnode.cpp:2077
Node Html summary.
Definition docnode.h:853
Node representing a HTML table.
Definition docnode.h:1275
Token parseXml()
Definition docnode.cpp:2363
size_t numberHeaderRows() const
Definition docnode.cpp:2255
std::unique_ptr< DocNodeVariant > m_caption
Definition docnode.h:1290
Token parse()
Definition docnode.cpp:2270
void computeTableGrid()
determines the location of all cells in a grid, resolving row and column spans.
Definition docnode.cpp:2420
size_t m_numCols
Definition docnode.h:1292
const DocNodeVariant * caption() const
Definition docnode.cpp:2250
bool hasCaption() const
Definition docnode.cpp:2245
const HtmlAttribList & attribs() const
Definition docnode.h:656
QCString relPath() const
Definition docnode.h:652
QCString name() const
Definition docnode.h:648
QCString url() const
Definition docnode.h:653
DocImage(DocParser *parser, DocNodeVariant *parent, const HtmlAttribList &attribs, const QCString &name, Type t, const QCString &url=QCString(), bool inlineImage=TRUE)
Definition docnode.cpp:1356
std::unique_ptr< Private > p
Definition docnode.h:675
void parse()
Definition docnode.cpp:1371
bool isSVG() const
Definition docnode.cpp:1362
Node representing a include/dontinclude operator block.
Definition docnode.h:477
bool m_stripCodeComments
Definition docnode.h:521
const char * typeAsString() const
Definition docnode.h:486
QCString m_includeFileName
Definition docnode.h:524
QCString context() const
Definition docnode.h:501
Type type() const
Definition docnode.h:485
QCString m_pattern
Definition docnode.h:517
void markLast(bool v=TRUE)
Definition docnode.h:505
QCString m_text
Definition docnode.h:516
bool m_showLineNo
Definition docnode.h:515
Node representing an included text block from file.
Definition docnode.h:435
void parse()
Definition docnode.cpp:270
QCString m_text
Definition docnode.h:465
Type m_type
Definition docnode.h:466
@ LatexInclude
Definition docnode.h:437
@ SnippetWithLines
Definition docnode.h:438
@ DontIncWithLines
Definition docnode.h:439
@ IncWithLines
Definition docnode.h:438
@ HtmlInclude
Definition docnode.h:437
@ VerbInclude
Definition docnode.h:437
@ DontInclude
Definition docnode.h:437
@ DocbookInclude
Definition docnode.h:439
QCString m_blockId
Definition docnode.h:472
bool m_stripCodeComments
Definition docnode.h:467
QCString context() const
Definition docnode.h:453
QCString m_file
Definition docnode.h:463
Node representing an entry in the index.
Definition docnode.h:552
QCString m_entry
Definition docnode.h:562
Token parse()
Definition docnode.cpp:1679
Node representing an internal section of documentation.
Definition docnode.h:978
Token parse(int)
Definition docnode.cpp:1619
QCString m_file
Definition docnode.h:825
QCString m_anchor
Definition docnode.h:827
DocInternalRef(DocParser *parser, DocNodeVariant *parent, const QCString &target)
Definition docnode.cpp:675
QCString relPath() const
Definition docnode.h:821
QCString m_relPath
Definition docnode.h:826
Node representing a line break.
Definition docnode.h:202
QCString m_word
Definition docnode.h:178
QCString m_anchor
Definition docnode.h:182
QCString file() const
Definition docnode.h:171
QCString relPath() const
Definition docnode.h:172
QCString ref() const
Definition docnode.h:173
QCString word() const
Definition docnode.h:170
QCString m_file
Definition docnode.h:180
QCString anchor() const
Definition docnode.h:174
QCString m_ref
Definition docnode.h:179
QCString m_relPath
Definition docnode.h:181
DocLinkedWord(DocParser *parser, DocNodeVariant *parent, const QCString &word, const QCString &ref, const QCString &file, const QCString &anchor, const QCString &tooltip)
Definition docnode.cpp:194
QCString m_tooltip
Definition docnode.h:183
QCString tooltip() const
Definition docnode.h:175
DocMermaidFile(DocParser *parser, DocNodeVariant *parent, const QCString &name, const QCString &context, const QCString &srcFile, int srcLine)
Definition docnode.cpp:1286
DocMscFile(DocParser *parser, DocNodeVariant *parent, const QCString &name, const QCString &context, const QCString &srcFile, int srcLine)
Definition docnode.cpp:1163
bool parse()
Definition docnode.cpp:1170
DocNode(DocParser *parser, DocNodeVariant *parent)
Definition docnode.h:85
void setInsidePreformatted(bool p)
Definition docnode.h:109
DocNodeVariant * thisVariant()
Definition docnode.h:93
DocParser * parser()
Definition docnode.h:98
DocNodeVariant * parent()
Definition docnode.h:90
@ Unknown
Definition docnode.h:110
@ Table
Definition docnode.h:110
@ Requirement
Definition docnode.h:110
@ Section
Definition docnode.h:110
@ Anchor
Definition docnode.h:110
Node representing an block of paragraphs.
Definition docnode.h:988
Token parse()
Definition docnode.cpp:2956
Node representing a paragraph in the documentation tree.
Definition docnode.h:1089
Token handleSimpleSection(DocSimpleSect::Type t, bool xmlContext=FALSE)
Definition docnode.cpp:3487
void handleLink(const QCString &cmdName, bool isJavaLink)
Definition docnode.cpp:3965
void handleInheritDoc()
Definition docnode.cpp:4196
void handleCite(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3540
DocPara(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:3481
void handleInclude(const QCString &cmdName, DocInclude::Type t)
Definition docnode.cpp:4006
Token handleCommand(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:4233
void handleDoxyConfig(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3676
void handleSection(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:4111
void handleFile(const QCString &cmdName)
Definition docnode.cpp:3926
Token handleParamSection(const QCString &cmdName, DocParamSect::Type t, bool xmlContext, int direction)
Definition docnode.cpp:3517
void markLast(bool v=TRUE)
Definition docnode.h:1095
Token handleHtmlStartTag(const QCString &tagName, const HtmlAttribList &tagHtmlAttribs)
Definition docnode.cpp:5041
void handleEmoji(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3645
void handleIncludeOperator(const QCString &cmdName, DocIncOperator::Type t)
Definition docnode.cpp:3866
bool isFirst() const
Definition docnode.h:1096
void markFirst(bool v=TRUE)
Definition docnode.h:1094
void setAttribs(const HtmlAttribList &attribs)
Definition docnode.h:1121
bool m_isFirst
Definition docnode.h:1124
Token parse()
Definition docnode.cpp:5784
void handleVhdlFlow()
Definition docnode.cpp:3958
Token handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs, int level)
Definition docnode.cpp:4142
void handleShowDate(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3796
bool m_isLast
Definition docnode.h:1125
Token handleXRefItem()
Definition docnode.cpp:3774
Token handleHtmlEndTag(const QCString &tagName)
Definition docnode.cpp:5511
Token handleStartCode()
Definition docnode.cpp:4160
bool injectToken(Token tok, const QCString &tokText)
Definition docnode.cpp:4153
DocNodeList m_paramTypes
Definition docnode.h:1150
DocNodeList m_paragraphs
Definition docnode.h:1148
void markFirst(bool b=TRUE)
Definition docnode.h:1140
Token parseXml(const QCString &paramName)
Definition docnode.cpp:3370
void markLast(bool b=TRUE)
Definition docnode.h:1141
Token parse(const QCString &cmdName)
Definition docnode.cpp:3289
DocParamSect::Type m_type
Definition docnode.h:1151
DocNodeList m_params
Definition docnode.h:1149
Node representing a parameter section.
Definition docnode.h:1062
friend class DocParamList
Definition docnode.h:1063
Token parse(const QCString &cmdName, bool xmlContext, Direction d)
Definition docnode.cpp:3438
bool m_hasInOutSpecifier
Definition docnode.h:1083
Type type() const
Definition docnode.h:1077
void handleLinkedWord(DocNodeVariant *parent, DocNodeList &children, bool ignoreAutoLinkFlag=FALSE)
DocTokenizer tokenizer
void handleInternalRef(DocNodeVariant *parent, DocNodeList &children)
void handleParameterType(DocNodeVariant *parent, DocNodeList &children, const QCString &paramTypes)
void checkRetvalName()
void readTextFileByName(const QCString &file, QCString &text)
bool defaultHandleToken(DocNodeVariant *parent, Token &tok, DocNodeList &children, bool handleWord=TRUE)
void handleRef(DocNodeVariant *parent, DocNodeList &children, char cmdChar, const QCString &cmdName)
Token handleAHref(DocNodeVariant *parent, DocNodeList &children, const HtmlAttribList &tagHtmlAttribs)
Token internalValidatingParseDoc(DocNodeVariant *parent, DocNodeList &children, const QCString &doc)
void handleInitialStyleCommands(DocNodeVariant *parent, DocNodeList &children)
void handleStyleLeave(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const QCString &tagName)
void handlePendingStyleCommands(DocNodeVariant *parent, DocNodeList &children)
void popContext()
Definition docparser.cpp:75
void handleImage(DocNodeVariant *parent, DocNodeList &children)
void handleStyleEnter(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const QCString &tagName, const HtmlAttribList *attribs)
void handlePrefix(DocNodeVariant *parent, DocNodeList &children)
Token handleStyleArgument(DocNodeVariant *parent, DocNodeList &children, const QCString &cmdName)
void handleIFile(char cmdChar, const QCString &cmdName)
void handleILine(char cmdChar, const QCString &cmdName)
void checkArgumentName()
DocParserContext context
void handleAnchor(DocNodeVariant *parent, DocNodeList &children)
void handleImg(DocNodeVariant *parent, DocNodeList &children, const HtmlAttribList &tagHtmlAttribs)
void defaultHandleTitleAndSize(const CommandType cmd, DocNodeVariant *parent, DocNodeList &children, QCString &width, QCString &height)
void handleUnclosedStyleCommands()
void pushContext()
Definition docparser.cpp:60
void errorHandleDefaultToken(DocNodeVariant *parent, Token tok, DocNodeList &children, const QCString &txt)
DocPlantUmlFile(DocParser *parser, DocNodeVariant *parent, const QCString &name, const QCString &context, const QCString &srcFile, int srcLine)
Definition docnode.cpp:1242
Node representing a reference to some item.
Definition docnode.h:787
QCString anchor() const
Definition docnode.h:794
QCString m_file
Definition docnode.h:807
SectionType m_sectionType
Definition docnode.h:805
QCString m_text
Definition docnode.h:811
QCString m_ref
Definition docnode.h:809
QCString m_relPath
Definition docnode.h:808
DocRef(DocParser *parser, DocNodeVariant *parent, const QCString &target, const QCString &context)
Definition docnode.cpp:710
void parse(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:891
RefType m_refType
Definition docnode.h:804
QCString m_anchor
Definition docnode.h:810
bool m_isSubPage
Definition docnode.h:806
void parse()
Definition docnode.cpp:6373
Node representing a reference to a section.
Definition docnode.h:944
QCString m_file
Definition docnode.h:960
QCString m_target
Definition docnode.h:957
QCString relPath() const
Definition docnode.h:950
bool m_isSubPage
Definition docnode.h:959
QCString m_anchor
Definition docnode.h:963
QCString target() const
Definition docnode.h:947
QCString m_ref
Definition docnode.h:962
DocSecRefItem(DocParser *parser, DocNodeVariant *parent, const QCString &target)
Definition docnode.cpp:536
QCString m_relPath
Definition docnode.h:961
RefType m_refType
Definition docnode.h:958
Node representing a list of section references.
Definition docnode.h:968
Node representing a normal section.
Definition docnode.h:923
std::unique_ptr< DocNodeVariant > m_title
Definition docnode.h:937
QCString m_id
Definition docnode.h:936
QCString m_file
Definition docnode.h:939
Token parse()
Definition docnode.cpp:6089
DocSection(DocParser *parser, DocNodeVariant *parent, int level, const QCString &id)
Definition docnode.h:925
const DocNodeVariant * title() const
Definition docnode.h:928
QCString m_anchor
Definition docnode.h:938
int m_level
Definition docnode.h:935
Node representing a simple list.
Definition docnode.h:999
Token parse()
Definition docnode.cpp:3000
Node representing a simple list item.
Definition docnode.h:1159
std::unique_ptr< DocNodeVariant > m_paragraph
Definition docnode.h:1166
DocSimpleListItem(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:2981
Node representing a simple section.
Definition docnode.h:1026
QCString typeString() const
Definition docnode.cpp:3260
Type type() const
Definition docnode.h:1035
Token parse(bool userTitle, bool needsSeparator)
Definition docnode.cpp:3150
Token parseRcs()
Definition docnode.cpp:3187
DocSimpleSect(DocParser *parser, DocNodeVariant *parent, Type t)
Definition docnode.cpp:3140
const DocNodeVariant * title() const
Definition docnode.h:1042
Token parseXml()
Definition docnode.cpp:3204
void appendLinkWord(const QCString &word)
Definition docnode.cpp:3240
bool hasTitle() const
Definition docnode.cpp:3145
std::unique_ptr< DocNodeVariant > m_title
Definition docnode.h:1046
Node representing a separator between two simple sections of the same type.
Definition docnode.h:1053
Node representing a style change.
Definition docnode.h:268
const char * styleString() const
Definition docnode.cpp:127
Style m_style
Definition docnode.h:318
Node representing a special symbol.
Definition docnode.h:328
static HtmlEntityMapper::SymType decodeSymbol(const QCString &symName)
Definition docnode.cpp:155
void parse()
Definition docnode.cpp:6259
Node representing a simple section title.
Definition docnode.h:608
void parse()
Definition docnode.cpp:3109
void parseFromString(DocNodeVariant *, const QCString &title)
Definition docnode.cpp:3127
void setStateILiteralOpt()
void setStateILiteral()
void setStateCite()
void setStateSnippet()
void setStateEmoji()
void setStateMermaidOpt()
void setStateCode()
void setStatePattern()
void startAutoList()
void setStateSkipTitle()
void setStateParam()
void setStateBlock()
void setStatePlantUMLOpt()
void setStateRtfOnly()
void setStateVerbatim()
void setStateLink()
void setStateTitle()
void setStateFile()
void setStateLatexOnly()
void setStateManOnly()
void setStateShowDate()
void setInsidePre(bool b)
void setStateXRefItem()
int getLineNr() const
void setStateText()
void setStateXmlCode()
void unputString(const QCString &tag)
void setStateDbOnly()
void setStateHtmlOnly()
void setStateILine()
void setStateICode()
void setStateOptions()
void setStateDoxyConfig()
void setStateQuotedString()
void setStatePara()
void setStatePlantUML()
void setStateIVerbatim()
void setStateXmlOnly()
void setStateMermaid()
void setStateSetScope()
void pushBackHtmlTag(const QCString &tag)
Node representing a URL (or email address).
Definition docnode.h:188
Node representing a verbatim, unparsed text fragment.
Definition docnode.h:376
DocVerbatim(DocParser *parser, DocNodeVariant *parent, const QCString &context, const QCString &text, Type t, bool isExample, const QCString &exampleFile, bool isBlock=FALSE, const QCString &lang=QCString())
Definition docnode.cpp:260
std::unique_ptr< Private > p
Definition docnode.h:429
bool isBlock() const
Definition docnode.h:389
bool isExample() const
Definition docnode.h:385
QCString context() const
Definition docnode.h:384
QCString text() const
Definition docnode.h:383
QCString exampleFile() const
Definition docnode.h:386
QCString relPath() const
Definition docnode.h:387
void setEngine(const QCString &e)
Definition docnode.h:402
@ JavaDocLiteral
Definition docnode.h:378
Node representing a VHDL flow chart.
Definition docnode.h:758
DocVhdlFlow(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:1326
void parse()
Definition docnode.cpp:1330
Node representing some amount of white space.
Definition docnode.h:354
Node representing a word.
Definition docnode.h:153
DocWord(DocParser *parser, DocNodeVariant *parent, const QCString &word)
Definition docnode.cpp:182
QCString m_word
Definition docnode.h:159
QCString word() const
Definition docnode.h:156
Node representing an item of a cross-referenced list.
Definition docnode.h:621
QCString m_anchor
Definition docnode.h:635
DocXRefItem(DocParser *parser, DocNodeVariant *parent, int id, const QCString &key)
Definition docnode.cpp:475
QCString key() const
Definition docnode.h:628
QCString relPath() const
Definition docnode.h:627
QCString m_file
Definition docnode.h:634
QCString m_key
Definition docnode.h:633
QCString m_title
Definition docnode.h:636
bool parse()
Definition docnode.cpp:480
QCString m_relPath
Definition docnode.h:637
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:109
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:106
static NamespaceDefMutable * globalScope
Definition doxygen.h:121
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:107
static FileNameLinkedMap * mermaidFileNameLinkedMap
Definition doxygen.h:110
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:108
static QCString htmlFileExtension
Definition doxygen.h:122
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:99
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:127
static SearchIndexIntf searchIndex
Definition doxygen.h:124
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1978
int symbol2index(const std::string &symName) const
Returns a code for the requested Emoji entity name.
Definition emoji.cpp:1990
A model of a file symbol.
Definition filedef.h:99
virtual QCString absFilePath() const =0
Class representing a LaTeX formula as found in the documentation.
Definition formula.h:29
QCString text() const
Definition formula.h:37
const Formula * findFormula(int formulaId) const
Definition formula.cpp:704
static FormulaManager & instance()
Definition formula.cpp:53
void clear()
clears the contents
Definition growvector.h:143
size_t size() const
returns the number of elements
Definition growvector.h:93
iterator end()
returns an iterator to the end
Definition growvector.h:88
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
void emplace_back(Args &&...args)
Definition growvector.h:108
Class representing a list of HTML attributes.
Definition htmlattrib.h:33
static HtmlEntityMapper & instance()
Returns the one and only instance of the HTML entity mapper.
SymType name2sym(const QCString &symName) const
Give code of the requested HTML entity name.
const T * find(const std::string &key) const
Definition linkedmap.h:47
A model of a class/file/namespace member symbol.
Definition memberdef.h:48
virtual const ClassDef * getClassDef() const =0
virtual const MemberDef * reimplements() const =0
virtual QCString objCMethodName(bool localLink, bool showStatic) const =0
A model of a page symbol.
Definition pagedef.h:26
virtual bool hasParentPage() const =0
This is an alternative implementation of QCString.
Definition qcstring.h:101
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
QCString & prepend(const char *s)
Definition qcstring.h:422
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:166
bool startsWith(const char *s) const
Definition qcstring.h:507
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:241
QCString lower() const
Definition qcstring.h:249
bool endsWith(const char *s) const
Definition qcstring.h:524
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:593
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:260
QCString fill(char c, int len=-1)
Fills a string with a predefined character.
Definition qcstring.h:193
const std::string & str() const
Definition qcstring.h:552
QCString & append(char c)
Definition qcstring.h:396
QCString right(size_t len) const
Definition qcstring.h:234
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:172
std::string_view view() const
Definition qcstring.h:174
QCString left(size_t len) const
Definition qcstring.h:229
This struct represents an item in the list of references.
Definition reflist.h:32
QCString text() const
Definition reflist.h:45
QCString anchor() const
Definition reflist.h:46
List of cross-referenced items.
Definition reflist.h:80
QCString sectionTitle() const
Definition reflist.h:104
QCString fileName() const
Definition reflist.h:102
RefItem * find(int itemId)
Definition reflist.cpp:40
bool isEnabled() const
Definition reflist.cpp:46
static RefListManager & instance()
Definition reflist.h:121
class that provide information about a section.
Definition section.h:58
QCString label() const
Definition section.h:69
QCString ref() const
Definition section.h:72
QCString fileName() const
Definition section.h:74
QCString title() const
Definition section.h:70
SectionType type() const
Definition section.h:71
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:179
static constexpr int Anchor
Definition section.h:40
static constexpr int Table
Definition section.h:41
constexpr int level() const
Definition section.h:46
static constexpr int Requirement
Definition section.h:42
static constexpr int Page
Definition section.h:31
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
static void createFlowChart(const MemberDef *)
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:230
CommandType
Definition cmdmapper.h:29
@ CMD_ENDSECREFLIST
Definition cmdmapper.h:53
@ CMD_ENDLATEXONLY
Definition cmdmapper.h:51
@ CMD_ENDVERBATIM
Definition cmdmapper.h:54
@ CMD_DONTINCLUDE
Definition cmdmapper.h:46
@ CMD_SUBSUBSECTION
Definition cmdmapper.h:89
@ CMD_SUBSUBPARAGRAPH
Definition cmdmapper.h:161
@ CMD_INTERNALREF
Definition cmdmapper.h:65
@ CMD_ENDHTMLONLY
Definition cmdmapper.h:50
@ CMD_VERBINCLUDE
Definition cmdmapper.h:98
@ CMD_DOCBOOKINCLUDE
Definition cmdmapper.h:145
@ CMD_HTMLINCLUDE
Definition cmdmapper.h:60
@ CMD_SNIPWITHLINES
Definition cmdmapper.h:141
HtmlTagType
Definition cmdmapper.h:173
#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::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29
std::vector< std::string > StringVector
Definition containers.h:33
QCString formatDateTime(const QCString &format, const std::tm &dt, int &formatUsed)
Return a string representation for a given std::tm value that is formatted according to the pattern g...
Definition datetime.cpp:174
QCString dateTimeFromString(const QCString &spec, std::tm &dt, int &format)
Returns the filled in std::tm for a given string representing a date and/or time.
Definition datetime.cpp:133
constexpr const char * SF_bit2str(int bitNumber)
Helper function that returns the name related one of the SF bits.
Definition datetime.h:32
constexpr int SF_NumBits
number of bits in SF vector
Definition datetime.h:27
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:175
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:49
static const char * g_sectionLevelToName[]
Definition docnode.cpp:58
#define AUTO_TRACE(...)
Definition docnode.cpp:48
static QCString stripKnownExtensions(const QCString &text)
Definition docnode.cpp:106
static void unescapeCRef(QCString &s)
Definition docnode.cpp:84
static const StringUnorderedSet g_plantumlEngine
Definition docnode.cpp:71
#define INTERNAL_ASSERT(x)
Definition docnode.cpp:53
static void flattenParagraphs(DocNodeVariant *root, DocNodeList &children)
Definition docnode.cpp:859
static Token skipSpacesForTable(DocParser *parser)
Definition docnode.cpp:2017
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:50
static bool findAttribute(const HtmlAttribList &tagHtmlAttribs, const char *attrName, QCString *result)
Definition docnode.cpp:5025
std::vector< ActiveRowSpan > RowSpanList
List of ActiveRowSpan classes.
Definition docnode.cpp:2414
static void setParent(DocNodeVariant *n, DocNodeVariant *newParent)
Definition docnode.cpp:120
static bool checkIfHtmlEndTagEndsAutoList(DocParser *parser, const DocNodeVariant *n)
Definition docnode.cpp:5732
constexpr bool holds_one_of_alternatives(const DocNodeVariant &v)
returns true iff v holds one of types passed as template parameters
Definition docnode.h:1372
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:67
DocNodeList * call_method_children(DocNodeVariant *v)
Definition docnode.h:1391
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:1336
std::unique_ptr< DocNodeVariant > createDocNode(Args &&...args)
Definition docnode.h:1501
Private header shared between docparser.cpp and docnode.cpp.
bool insideUL(const DocNodeVariant *n)
bool insideTable(const DocNodeVariant *n)
IterableStack< const DocNodeVariant * > DocStyleChangeStack
Definition docparser_p.h:55
bool insidePRE(const DocNodeVariant *n)
bool insideLI(const DocNodeVariant *n)
bool insideDL(const DocNodeVariant *n)
bool insideBlockQuote(const DocNodeVariant *n)
bool insideDetails(const DocNodeVariant *n)
bool insideOL(const DocNodeVariant *n)
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1966
GroupDef * toGroupDef(Definition *d)
Translator * theTranslator
Definition language.cpp:71
QCString markdownFileNameToId(const QCString &fileName)
processes string s and converts markdown into doxygen/html commands.
MemberDef * toMemberDef(Definition *d)
#define warn(file, line, fmt,...)
Definition message.h:97
#define err(fmt,...)
Definition message.h:127
#define warn_doc_error(file, line, fmt,...)
Definition message.h:112
const Mapper< HtmlTagType > * htmlTagMapper
const Mapper< CommandType > * cmdMapper
QCString trunc(const QCString &s, size_t numChars=15)
Definition trace.h:56
Definition message.h:144
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:571
#define qsnprintf
Definition qcstring.h:49
const char * qPrint(const char *s)
Definition qcstring.h:687
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
#define ASSERT(x)
Definition qcstring.h:39
uint32_t rowsLeft
Definition docnode.cpp:2409
uint32_t column
Definition docnode.cpp:2410
ActiveRowSpan(uint32_t rows, uint32_t col)
Definition docnode.cpp:2408
Citation-related data.
Definition cite.h:70
virtual QCString text() const =0
virtual QCString shortAuthor() const =0
virtual QCString label() const =0
virtual QCString year() const =0
void move_append(DocNodeList &l)
moves the element of list l at the end of this list.
Definition docnode.cpp:850
void append(Args &&... args)
Append a new DocNodeVariant to the list by constructing it with type T and parameters Args.
Definition docnode.h:1405
T * get_last()
Returns a pointer to the last element in the list if that element exists and holds a T,...
Definition docnode.h:1416
Parser's context to store all global variables.
Definition docparser_p.h:60
StringMultiSet retvalsFound
Definition docparser_p.h:76
bool includeFileShowLineNo
Definition docparser_p.h:92
DocStyleChangeStack styleStack
Definition docparser_p.h:68
size_t includeFileLength
Definition docparser_p.h:90
QCString fileName
Definition docparser_p.h:71
DocNodeStack nodeStack
Definition docparser_p.h:67
StringMultiSet paramsFound
Definition docparser_p.h:77
DefinitionStack copyStack
Definition docparser_p.h:70
QCString exampleName
Definition docparser_p.h:82
const Definition * scope
Definition docparser_p.h:61
QCString includeFileText
Definition docparser_p.h:88
TokenInfo * token
Definition docparser_p.h:95
QCString includeFileName
Definition docparser_p.h:87
size_t includeFileOffset
Definition docparser_p.h:89
const MemberDef * memberDef
Definition docparser_p.h:80
QCString verb
bool isEnumList
QCString text
QCString sectionId
QCString chars
HtmlAttribList attribs
bool isCheckedList
QCString simpleSectText
QCString name
QCString attribsStr
bool isEMailAddr
QCString simpleSectName
QCString linkToText(SrcLangExt lang, const QCString &link, bool isFileName)
Definition util.cpp:2698
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5221
QCString stripIndentation(const QCString &s, bool skipFirstLine)
Definition util.cpp:5979
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3029
QCString stripScope(const QCString &name)
Definition util.cpp:3790
bool resolveLink(const QCString &scName, const QCString &lr, bool, const Definition **resContext, QCString &resAnchor, SrcLangExt lang, const QCString &prefix)
Definition util.cpp:2724
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3515
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition util.cpp:6648
QCString stripLeadingAndTrailingEmptyLines(const QCString &s, int &docLine)
Special version of QCString::stripWhiteSpace() that only strips completely blank lines.
Definition util.cpp:5040
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:2894
A bunch of utility functions.