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));
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));
204 {
205 Doxygen::searchIndex.addWord(word,false);
206 }
207}
208
209//---------------------------------------------------------------------------
210
212{
213 if (id.isEmpty())
214 {
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 {
226 m_file = convertNameToFile(ct.fileName(),false,true);
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 DocParser::AutoSaveContext saveContext(*parser());
507 }
508 }
509 return true;
510 }
511 return false;
512}
513
514//---------------------------------------------------------------------------
515
517 m_relPath(parser->context.relPath)
518{
519 const Formula *formula = FormulaManager::instance().findFormula(id);
520 if (formula && !formula->text().isEmpty())
521 {
522 m_id = id;
523 m_name.sprintf("form_%d",m_id);
524 m_text = formula->text();
525 }
526 else // wrong \_form#<n> command
527 {
528 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Wrong formula id {:d}",id);
529 m_id = -1;
530 }
531}
532
533//---------------------------------------------------------------------------
534
539
541{
542 AUTO_TRACE();
543 auto ns = AutoNodeStack(parser(),thisVariant());
544
546 Token tok = parser()->tokenizer.lex();
547 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
548 {
549 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
550 {
551 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"\\refitem");
552 }
553 tok = parser()->tokenizer.lex();
554 }
557
558 if (!m_target.isEmpty())
559 {
561 if (sec==nullptr && parser()->context.lang==SrcLangExt::Markdown) // lookup as markdown file
562 {
564 }
565 if (sec) // ref to section or anchor
566 {
567 // set defaults
568 m_ref = sec->ref();
571 m_anchor = sec->label();
572 m_isSubPage = false;
573 // adjust if needed
574 switch (sec->type().level())
575 {
577 {
579 m_isSubPage = pd && pd->hasParentPage();
580 if (!m_isSubPage)
581 {
582 m_anchor="";
583 }
584 }
585 break;
588 break;
591 break;
594 break;
595 default:
596 break;
597 }
598 //printf("m_ref=%s,m_file=%s,type=%d\n",
599 // qPrint(m_ref),qPrint(m_file),m_refType);
600 }
601 else
602 {
603 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to unknown section {}",m_target);
604 }
605 }
606 else
607 {
608 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to empty target");
609 }
610}
611
612//---------------------------------------------------------------------------
613
615{
616 AUTO_TRACE();
617 auto ns = AutoNodeStack(parser(),thisVariant());
618
619 Token tok=parser()->tokenizer.lex();
620 // skip white space
621 while (tok.is_any_of(TokenRetval::TK_WHITESPACE, TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
622 // handle items
623 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
624 {
625 if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS))
626 {
627 switch (Mappers::cmdMapper->map(parser()->context.token->name))
628 {
630 {
631 tok=parser()->tokenizer.lex();
632 if (!tok.is(TokenRetval::TK_WHITESPACE))
633 {
634 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\refitem command");
635 break;
636 }
637 tok=parser()->tokenizer.lex();
638 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
639 {
640 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\refitem",
641 tok.to_string());
642 break;
643 }
644
647 }
648 break;
650 return;
651 default:
652 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' as part of a \\secreflist",
653 tok.command_to_char(),qPrint(parser()->context.token->name));
654 return;
655 }
656 }
657 else if (tok.is(TokenRetval::TK_WHITESPACE))
658 {
659 // ignore whitespace
660 }
661 else
662 {
663 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {} inside section reference list",
664 tok.to_string());
665 return;
666 }
667 tok=parser()->tokenizer.lex();
668 }
669
670}
671
672//---------------------------------------------------------------------------
673
676{
677 int i=ref.find('#');
678 if (i!=-1)
679 {
680 m_anchor = ref.right(static_cast<int>(ref.length())-i-1);
681 m_file = ref.left(i);
682 }
683 else
684 {
685 m_file = ref;
686 }
687}
688
690{
691 AUTO_TRACE();
692 auto ns = AutoNodeStack(parser(),thisVariant());
693
694 Token tok = parser()->tokenizer.lex();
695 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
696 {
697 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
698 {
700 }
701 tok=parser()->tokenizer.lex();
702 }
703
705}
706
707//---------------------------------------------------------------------------
708
711{
712 const Definition *compound = nullptr;
714 AUTO_TRACE("target='{}',context='{}'",target,context);
715 ASSERT(!target.isEmpty());
717 auto lang = parser->context.lang;
719 if (sec==nullptr && !parser->context.prefix.isEmpty())
720 {
721 sec = SectionManager::instance().find(target);
722 }
723
724 if (sec==nullptr && getLanguageFromFileName(target)==SrcLangExt::Markdown) // lookup as markdown file
725 {
727 }
728 if (sec) // ref to section or anchor
729 {
730 PageDef *pd = nullptr;
731 int secLevel = sec->type().level();
732 if (secLevel==SectionType::Page)
733 {
734 pd = Doxygen::pageLinkedMap->find(target);
735 }
736 m_text = sec->title();
737 if (m_text.isEmpty()) m_text = sec->label();
738
739 m_ref = sec->ref();
741 if (secLevel==SectionType::Anchor)
742 {
744 }
745 else if (secLevel==SectionType::Table)
746 {
748 }
749 else if (secLevel==SectionType::Requirement)
750 {
752 if (!Config_getBool(GENERATE_REQUIREMENTS))
753 {
754 m_file.clear(); // prevent link when there is no requirements page
755 }
756 }
757 else
758 {
760 }
761 m_isSubPage = pd && pd->hasParentPage();
762 if (secLevel!=SectionType::Page || m_isSubPage)
763 {
764 m_anchor = pd ? pd->getOutputFileBase() : sec->label();
765 }
766 m_sectionType = sec->type();
767 //printf("m_text=%s,m_ref=%s,m_file=%s,type=%d\n",
768 // qPrint(m_text),qPrint(m_ref),qPrint(m_file),m_refType);
769 AUTO_TRACE_EXIT("section");
770 return;
771 }
772 else if (Config_getBool(IMPLICIT_DIR_DOCS) && target.lower().endsWith("/readme.md"))
773 {
774 QCString dirTarget = target.left(target.length() - 9); // strip readme.md part
775 const auto &dd = Doxygen::dirLinkedMap->find(dirTarget);
776 if (dd)
777 {
778 m_text = target;
779 m_file = dd->getOutputFileBase();
780 AUTO_TRACE_EXIT("directory");
781 return;
782 }
783 }
784 else if (resolveLink(context,target,true,&compound,anchor,lang,parser->context.prefix))
785 {
786 bool isFile = compound ?
787 (compound->definitionType()==Definition::TypeFile ||
788 compound->definitionType()==Definition::TypePage ? true : false) :
789 false;
790 bool isDir = compound && compound->definitionType()==Definition::TypeDir;
791 if (compound && lang==SrcLangExt::Markdown) lang = compound->getLanguage();
792 m_text = linkToText(lang,target,isFile || isDir);
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 { DocParser::AutoSaveContext saveContext(*parser());
927 }
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));
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();
953 m_file = convertNameToFile(ct.fileName(),false,true);
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;
1003 if (!m_refText.isEmpty() && m_refText.at(0)=='#')
1004 {
1005 m_refText = m_refText.right(m_refText.length()-1);
1006 }
1008 parser->context.inSeeBlock,&compound,anchor,
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);
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 }
1769endindexentry:
1770 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1771 return retval;
1772}
1773
1774//---------------------------------------------------------------------------
1775
1778{
1779 m_hasCaptionId = false;
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();
1790 m_hasCaptionId = true;
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 { DocTokenizer::AutoSaveState saveState(parser->tokenizer);
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 }
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 {
2097 isHeading=true;
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
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 { DocParser::AutoSaveContext saveContext(*parser());
3199 }
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
3255 parser()->context.inSeeBlock=true;
3256 p->injectToken(Token::make_TK_LNKWORD(),word);
3257 parser()->context.inSeeBlock=false;
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 {
3394 retval = par->parse();
3395 if (par->isEmpty()) // avoid adding an empty paragraph for the whitespace
3396 // after </para> and before </param>
3397 {
3399 break;
3400 }
3401 else // append the paragraph to the list
3402 {
3403 if (!m_paragraphs.empty())
3404 {
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::handleEmoji(char cmdChar,const QCString &cmdName)
3541{
3542 AUTO_TRACE();
3543 // get the argument of the emoji command.
3544 Token tok=parser()->tokenizer.lex();
3545 if (!tok.is(TokenRetval::TK_WHITESPACE))
3546 {
3547 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3548 cmdChar,cmdName);
3549 return;
3550 }
3552 tok=parser()->tokenizer.lex();
3553 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3554 {
3555 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"no emoji name given or unexpected end of comment block while parsing the "
3556 "argument of command '{:c}{}'",cmdChar,cmdName);
3558 return;
3559 }
3560 else if (!tok.is(TokenRetval::TK_WORD))
3561 {
3562 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3563 tok.to_string(),cmdChar,cmdName);
3565 return;
3566 }
3569}
3570
3571void DocPara::handleDoxyConfig(char cmdChar,const QCString &cmdName)
3572{
3573 // get the argument of the cite command.
3574 Token tok=parser()->tokenizer.lex();
3575 if (!tok.is(TokenRetval::TK_WHITESPACE))
3576 {
3577 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3578 cmdChar,cmdName);
3579 return;
3580 }
3582 tok=parser()->tokenizer.lex();
3583 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3584 {
3585 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3586 "argument of command '{:c}{}'",cmdChar,cmdName);
3587 return;
3588 }
3589 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
3590 {
3591 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3592 tok.to_string(),cmdChar,cmdName);
3593 return;
3594 }
3595 ConfigOption * opt = ConfigImpl::instance()->get(parser()->context.token->name);
3596 if (opt)
3597 {
3598 QCString optionValue;
3599 switch (opt->kind())
3600 {
3602 optionValue = *(static_cast<ConfigBool*>(opt)->valueStringRef());
3603 break;
3605 optionValue = *(static_cast<ConfigString*>(opt)->valueRef());
3606 break;
3608 optionValue = *(static_cast<ConfigEnum*>(opt)->valueRef());
3609 break;
3611 optionValue = *(static_cast<ConfigInt*>(opt)->valueStringRef());
3612 break;
3614 {
3615 StringVector *lst = static_cast<ConfigList*>(opt)->valueRef();
3616 optionValue="";
3617 if (!lst->empty())
3618 {
3619 std::string lstFormat = theTranslator->trWriteList(static_cast<int>(lst->size())).str();
3620 static const reg::Ex marker(R"(@(\d+))");
3621 reg::Iterator it(lstFormat,marker);
3623 size_t index=0;
3624 // now replace all markers with the real text
3625 for ( ; it!=end ; ++it)
3626 {
3627 const auto &match = *it;
3628 size_t newIndex = match.position();
3629 size_t matchLen = match.length();
3630 optionValue += lstFormat.substr(index,newIndex-index);
3631 unsigned long entryIndex = std::stoul(match[1].str());
3632 if (entryIndex<(unsigned long)lst->size())
3633 {
3634 optionValue += lst->at(entryIndex);
3635 }
3636 index=newIndex+matchLen;
3637 }
3638 optionValue+=lstFormat.substr(index);
3639 }
3640 }
3641 break;
3643 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Obsolete setting for '{:c}{}': '{}'",
3644 cmdChar,cmdName,parser()->context.token->name);
3645 break;
3647 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),
3648 "Disabled setting (i.e. not supported in this doxygen executable) for '{:c}{}': '{}'",
3649 cmdChar,cmdName,parser()->context.token->name);
3650 break;
3652 // nothing to show here
3653 break;
3654 }
3655 if (!optionValue.isEmpty())
3656 {
3657 children().append<DocWord>(parser(),thisVariant(),optionValue);
3658 }
3659 }
3660 else
3661 {
3662 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Unknown option for '{:c}{}': '{}'",
3663 cmdChar,cmdName,parser()->context.token->name);
3665 }
3667}
3668
3670{
3671 AUTO_TRACE();
3672 Token retval=parser()->tokenizer.lex();
3673 ASSERT(retval.is(TokenRetval::TK_WHITESPACE));
3675 retval=parser()->tokenizer.lex();
3676 if (retval.is(TokenRetval::RetVal_OK))
3677 {
3681 if (!ref->parse())
3682 {
3683 children().pop_back();
3684 }
3685 }
3687 return retval;
3688}
3689
3690
3691void DocPara::handleShowDate(char cmdChar,const QCString &cmdName)
3692{
3693 AUTO_TRACE();
3694 QCString fmt;
3695 QCString date;
3696 Token tok=parser()->tokenizer.lex();
3697 if (!tok.is(TokenRetval::TK_WHITESPACE))
3698 {
3699 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3700 cmdChar,cmdName);
3701 return;
3702 }
3704 tok = parser()->tokenizer.lex();
3705 if (!tok.is(TokenRetval::TK_WORD))
3706 {
3707 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <format> argument for command '{:c}{}'",
3708 cmdChar,cmdName);
3710 return;
3711 }
3712 fmt = parser()->context.token->name;
3713
3715 tok = parser()->tokenizer.lex();
3716
3717 QCString specDateRaw = tok.is(TokenRetval::TK_WORD) ? parser()->context.token->name : QCString();
3718 QCString specDate = specDateRaw.stripWhiteSpace();
3719 bool specDateOnlyWS = !specDateRaw.isEmpty() && specDate.isEmpty();
3720 if (!specDate.isEmpty() && !tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3721 {
3722 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}'",
3723 cmdChar,cmdName);
3725 return;
3726 }
3727
3728 std::tm dat{};
3729 int specFormat=0;
3730 QCString err = dateTimeFromString(specDate,dat,specFormat);
3731 if (!err.isEmpty())
3732 {
3733 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}': {}",
3734 cmdChar,cmdName,err);
3736 return;
3737 }
3738
3739 int usedFormat=0;
3740 QCString dateTimeStr = formatDateTime(fmt,dat,usedFormat);
3741
3742 // warn the user if the format contains markers that are not explicitly filled in
3743 for (int i=0;i<SF_NumBits;i++)
3744 {
3745 int bitMask = 1<<i;
3746 if ((usedFormat&bitMask) && !(specFormat&bitMask)) // a part was used in the format string but its value was not specified.
3747 {
3748 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.",
3749 cmdChar,cmdName,fmt,SF_bit2str(i),specDate,SF_bit2str(i));
3750 }
3751 }
3752
3753 children().append<DocWord>(parser(),thisVariant(),dateTimeStr);
3754 if (specDateOnlyWS) // specDate is only whitespace
3755 {
3757 }
3759}
3760
3762{
3763 AUTO_TRACE("cmdName={}",cmdName);
3764 QCString saveCmdName = cmdName;
3765 Token tok=parser()->tokenizer.lex();
3766 if (!tok.is(TokenRetval::TK_WHITESPACE))
3767 {
3768 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3769 saveCmdName);
3770 return;
3771 }
3773 tok=parser()->tokenizer.lex();
3775 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3776 {
3777 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3778 "argument of command {}", saveCmdName);
3779 return;
3780 }
3781 else if (!tok.is(TokenRetval::TK_WORD))
3782 {
3783 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3784 tok.to_string(),saveCmdName);
3785 return;
3786 }
3787 auto it1 = children().size()>=1 ? std::prev(children().end()) : children().end();
3788 auto it2 = children().size()>=2 ? std::prev(it1) : children().end();
3789 DocNodeVariant *n1 = it1!=children().end() ? &(*it1) : nullptr;
3790 DocNodeVariant *n2 = it2!=children().end() ? &(*it2) : nullptr;
3791 //TODO get from context the stripCodeComments()
3792 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
3796 stripCodeComments,
3799 );
3801 DocIncOperator *n1_docIncOp = std::get_if<DocIncOperator>(n1);
3802 DocWhiteSpace *n1_docWs = std::get_if<DocWhiteSpace >(n1);
3803 DocIncOperator *n2_docIncOp = std::get_if<DocIncOperator>(n2);
3804 bool isFirst = !n1 || // no last node
3805 (!n1_docIncOp && !n1_docWs) || // last node is not operator or whitespace
3806 (n1_docWs && n2 && !n2_docIncOp); // last node is not operator
3807 op->markFirst(isFirst);
3808 op->markLast(true);
3809 if (n1_docIncOp)
3810 {
3811 n1_docIncOp->markLast(false);
3812 }
3813 else if (n1_docWs && n2_docIncOp)
3814 {
3815 n2_docIncOp->markLast(false);
3816 }
3817 op->parse();
3818}
3819
3820template<class T>
3821void DocPara::handleFile(const QCString &cmdName)
3822{
3823 AUTO_TRACE("cmdName={}",cmdName);
3824 QCString saveCmdName = cmdName;
3825 Token tok=parser()->tokenizer.lex();
3826 if (!tok.is(TokenRetval::TK_WHITESPACE))
3827 {
3828 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3829 saveCmdName);
3830 return;
3831 }
3833 tok=parser()->tokenizer.lex();
3835 if (!tok.is(TokenRetval::TK_WORD))
3836 {
3837 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3838 tok.to_string(),saveCmdName);
3839 return;
3840 }
3841 QCString name = parser()->context.token->name;
3842 children().append<T>(parser(),thisVariant(),name,
3846 auto df = children().get_last<T>();
3847 if (!df->parse())
3848 {
3849 children().pop_back();
3850 }
3851}
3852
3859
3860void DocPara::handleLink(const QCString &cmdName,bool isJavaLink)
3861{
3862 AUTO_TRACE("cmdName={} isJavaLink={}",cmdName,isJavaLink);
3863 QCString saveCmdName = cmdName;
3864 Token tok=parser()->tokenizer.lex();
3865 if (!tok.is(TokenRetval::TK_WHITESPACE))
3866 {
3867 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3868 saveCmdName);
3869 return;
3870 }
3872 tok=parser()->tokenizer.lex();
3873 if (!tok.is(TokenRetval::TK_WORD))
3874 {
3875 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"{} as the argument of {}",
3876 tok.to_string(),saveCmdName);
3877 return;
3878 }
3879 if (saveCmdName == "javalink")
3880 {
3882 parser()->context.nodeStack.size(),
3883 DocStyleChange::Code,cmdName,true);
3884 }
3887 DocLink *lnk = children().get_last<DocLink>();
3888 if (saveCmdName == "javalink")
3889 {
3891 parser()->context.nodeStack.size(),
3892 DocStyleChange::Code,cmdName,false);
3893 }
3894 QCString leftOver = lnk->parse(isJavaLink);
3895 if (!leftOver.isEmpty())
3896 {
3897 children().append<DocWord>(parser(),thisVariant(),leftOver);
3898 }
3899}
3900
3902{
3903 AUTO_TRACE("cmdName={}",cmdName);
3904 QCString saveCmdName = cmdName;
3905 Token tok=parser()->tokenizer.lex();
3906 bool isBlock = false;
3907 bool trimLeft = false;
3908 bool localScope = false;
3909 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
3910 if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="{")
3911 {
3913 parser()->tokenizer.lex();
3915 StringVector optList=split(parser()->context.token->name.str(),",");
3916 auto contains = [&optList](const char *kw)
3917 {
3918 return std::find(optList.begin(),optList.end(),kw)!=optList.end();
3919 };
3920 localScope = contains("local");
3921 if (contains("nostrip"))
3922 {
3923 stripCodeComments = false;
3924 }
3925 else if (contains("strip"))
3926 {
3927 stripCodeComments = true;
3928 }
3929 if (t==DocInclude::Snippet && contains("trimleft"))
3930 {
3931 trimLeft = true;
3932 }
3933
3934 if (contains("lineno"))
3935 {
3939 }
3940 tok=parser()->tokenizer.lex();
3941 if (!tok.is(TokenRetval::TK_WHITESPACE))
3942 {
3943 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3944 saveCmdName);
3945 return;
3946 }
3947 }
3948 else if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="[")
3949 {
3951 parser()->tokenizer.lex();
3952 isBlock = (parser()->context.token->name.stripWhiteSpace() == "block");
3954 parser()->tokenizer.lex();
3955 }
3956 else if (!tok.is(TokenRetval::TK_WHITESPACE))
3957 {
3958 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3959 saveCmdName);
3960 return;
3961 }
3963 tok=parser()->tokenizer.lex();
3965 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3966 {
3967 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3968 "argument of command {}",saveCmdName);
3969 return;
3970 }
3971 else if (!tok.is(TokenRetval::TK_WORD))
3972 {
3973 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3974 tok.to_string(),saveCmdName);
3975 return;
3976 }
3977 QCString fileName = parser()->context.token->name;
3978 QCString blockId;
3980 {
3981 if (fileName == "this") fileName=parser()->context.fileName;
3983 tok=parser()->tokenizer.lex();
3985 if (!tok.is(TokenRetval::TK_WORD))
3986 {
3987 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected block identifier, but found token {} instead while parsing the {} command",
3988 tok.to_string(),saveCmdName);
3989 return;
3990 }
3991 blockId = "["+parser()->context.token->name+"]";
3992 }
3993
3995 thisVariant(),
3996 fileName,
3997 localScope ? parser()->context.context : "",
3998 t,
3999 stripCodeComments,
4002 blockId,isBlock,trimLeft);
4004}
4005
4006void DocPara::handleSection(char cmdChar,const QCString &cmdName)
4007{
4008 AUTO_TRACE("cmdName={}",cmdName);
4009 QCString saveCmdName = cmdName;
4010 // get the argument of the section command.
4011 Token tok=parser()->tokenizer.lex();
4012 if (!tok.is(TokenRetval::TK_WHITESPACE))
4013 {
4014 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
4015 cmdChar,saveCmdName);
4016 return;
4017 }
4018 tok=parser()->tokenizer.lex();
4019 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4020 {
4021 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
4022 "argument of command '{:c}{}'", cmdChar,saveCmdName);
4023 return;
4024 }
4025 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
4026 {
4027 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
4028 tok.to_string(),cmdChar,saveCmdName);
4029 return;
4030 }
4033 parser()->tokenizer.lex();
4035}
4036
4037Token DocPara::handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level)
4038{
4039 AUTO_TRACE();
4040 children().append<DocHtmlHeader>(parser(),thisVariant(),tagHtmlAttribs,level);
4041 Token retval = children().get_last<DocHtmlHeader>()->parse();
4042 return retval.is(TokenRetval::RetVal_OK) ? Token::make_TK_NEWPARA() : retval;
4043}
4044
4045// For XML tags whose content is stored in attributes rather than
4046// contained within the element, we need a way to inject the attribute
4047// text into the current paragraph.
4048bool DocPara::injectToken(Token tok,const QCString &tokText)
4049{
4050 AUTO_TRACE();
4051 parser()->context.token->name = tokText;
4052 return parser()->defaultHandleToken(thisVariant(),tok,children());
4053}
4054
4056{
4057 AUTO_TRACE();
4058 Token retval = parser()->tokenizer.lex();
4059 QCString lang = parser()->context.token->name;
4060 if (!lang.isEmpty() && lang.at(0)!='.')
4061 {
4062 lang="."+lang;
4063 }
4064 if (parser()->context.xmlComment)
4065 {
4066 parser()->context.token->verb = substitute(substitute(parser()->context.token->verb,"&lt;","<"),"&gt;",">");
4067 }
4068 // search for the first non-whitespace line, index is stored in li
4069 size_t i=0,li=0,l=parser()->context.token->verb.length();
4070 while (i<l && (parser()->context.token->verb.at(i)==' ' || parser()->context.token->verb.at(i)=='\n'))
4071 {
4072 if (parser()->context.token->verb.at(i)=='\n') li=i+1;
4073 i++;
4074 }
4077 stripIndentation(parser()->context.token->verb.mid(li)),
4081 false,lang);
4082 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4083 {
4084 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"code section ended without end marker");
4085 }
4087 AUTO_TRACE_EXIT("retval={}",retval.to_string());
4088 return retval;
4089}
4090
4092{
4093 if (parser()->context.memberDef) // inheriting docs from a member
4094 {
4095 const MemberDef *reMd = parser()->context.memberDef->reimplements();
4096 if (reMd) // member from which was inherited.
4097 {
4098 const MemberDef *thisMd = parser()->context.memberDef;
4099 bool hasParamCommand = false;
4100 bool hasReturnCommand = false;
4101 StringMultiSet retvalsFound;
4102 StringMultiSet paramsFound;
4103 { DocParser::AutoSaveContext saveContext(*parser());
4104 //printf("{InheritDocs:%s=>%s}\n",qPrint(parser()->context.memberDef->qualifiedName()),qPrint(reMd->qualifiedName()));
4105 parser()->context.scope=reMd->getOuterScope();
4106 if (parser()->context.scope!=Doxygen::globalScope)
4107 {
4109 }
4110 parser()->context.memberDef=reMd;
4111 while (!parser()->context.styleStack.empty()) parser()->context.styleStack.pop();
4112 while (!parser()->context.nodeStack.empty()) parser()->context.nodeStack.pop();
4113 parser()->context.copyStack.push_back(reMd);
4116 parser()->context.copyStack.pop_back();
4117 hasParamCommand = parser()->context.hasParamCommand;
4118 hasReturnCommand = parser()->context.hasReturnCommand;
4119 retvalsFound = parser()->context.retvalsFound;
4120 paramsFound = parser()->context.paramsFound;
4121 }
4122 parser()->context.hasParamCommand = hasParamCommand;
4123 parser()->context.hasReturnCommand = hasReturnCommand;
4124 parser()->context.retvalsFound = retvalsFound;
4125 parser()->context.paramsFound = paramsFound;
4126 parser()->context.memberDef = thisMd;
4127 }
4128 }
4129}
4130
4131
4132Token DocPara::handleCommand(char cmdChar, const QCString &cmdName)
4133{
4134 AUTO_TRACE("cmdName={}",cmdName);
4135 Token retval = Token::make_RetVal_OK();
4136 CommandType cmdId = Mappers::cmdMapper->map(cmdName);
4137 switch (cmdId)
4138 {
4140 {
4141 std::string str{cmdChar};
4142 children().append<DocWord>(parser(),thisVariant(),str.c_str() + cmdName);
4143 if (isAliasCmd(cmdName.view()))
4144 {
4145 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Found unexpanded alias '{:c}{}'. Check if number of arguments passed is correct.",cmdChar,cmdName);
4146 }
4147 else
4148 {
4149 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Found unknown command '{:c}{}'",cmdChar,cmdName);
4150 }
4151 }
4152 break;
4155 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4157 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4158 break;
4161 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4163 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4164 break;
4167 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4169 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4170 break;
4173 break;
4176 break;
4179 break;
4182 break;
4185 break;
4188 break;
4191 break;
4194 break;
4197 break;
4200 break;
4204 break;
4209 break;
4212 break;
4215 break;
4218 break;
4221 break;
4224 break;
4227 break;
4230 break;
4232 parser()->context.inSeeBlock=true;
4234 parser()->context.inSeeBlock=false;
4235 break;
4239 break;
4242 break;
4245 break;
4248 break;
4251 break;
4254 break;
4257 break;
4260 break;
4263 break;
4266 break;
4269 break;
4272 break;
4275 break;
4278 break;
4281 break;
4284 break;
4286 {
4288 retval = children().get_last<DocSimpleList>()->parse();
4289 }
4290 break;
4292 {
4293 handleSection(cmdChar,cmdName);
4294 retval = Token::make_RetVal_Section();
4295 }
4296 break;
4298 {
4299 handleSection(cmdChar,cmdName);
4300 retval = Token::make_RetVal_Subsection();
4301 }
4302 break;
4304 {
4305 handleSection(cmdChar,cmdName);
4306 retval = Token::make_RetVal_Subsubsection();
4307 }
4308 break;
4310 {
4311 handleSection(cmdChar,cmdName);
4312 retval = Token::make_RetVal_Paragraph();
4313 }
4314 break;
4316 {
4317 handleSection(cmdChar,cmdName);
4318 retval = Token::make_RetVal_SubParagraph();
4319 }
4320 break;
4322 {
4323 handleSection(cmdChar,cmdName);
4324 retval = Token::make_RetVal_SubSubParagraph();
4325 }
4326 break;
4328 {
4330 retval = handleStartCode();
4331 }
4332 break;
4334 {
4336 retval = handleStartCode();
4337 }
4338 break;
4340 {
4342 retval = parser()->tokenizer.lex();
4344 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4345 {
4346 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"htmlonly section ended without end marker");
4347 }
4349 }
4350 break;
4352 {
4354 retval = parser()->tokenizer.lex();
4356 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4357 {
4358 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"manonly section ended without end marker");
4359 }
4361 }
4362 break;
4364 {
4366 retval = parser()->tokenizer.lex();
4368 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4369 {
4370 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"rtfonly section ended without end marker");
4371 }
4373 }
4374 break;
4376 {
4378 retval = parser()->tokenizer.lex();
4380 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4381 {
4382 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"latexonly section ended without end marker");
4383 }
4385 }
4386 break;
4388 {
4390 retval = parser()->tokenizer.lex();
4392 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4393 {
4394 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"xmlonly section ended without end marker");
4395 }
4397 }
4398 break;
4400 {
4402 retval = parser()->tokenizer.lex();
4404 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4405 {
4406 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"docbookonly section ended without end marker");
4407 }
4409 }
4410 break;
4412 {
4415 parser()->tokenizer.lex();
4416
4417 QCString fullMatch = parser()->context.token->verb;
4418 int idx = fullMatch.find('{');
4419 int idxEnd = fullMatch.find("}",idx+1);
4420 StringVector optList;
4421 if (idx != -1) // options present
4422 {
4423 QCString optStr = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4424 optList = split(optStr.str(),",");
4425 for (const auto &opt : optList)
4426 {
4427 if (opt.empty()) continue;
4428 QCString locOpt(opt);
4429 locOpt = locOpt.stripWhiteSpace().lower();
4430 if (locOpt == "code")
4431 {
4433 }
4434 else if (!locOpt.isEmpty())
4435 {
4436 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Unknown option '{}' for '\\iliteral'",opt);
4437 }
4438 }
4439 }
4440
4442 retval = parser()->tokenizer.lex();
4444 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4445 {
4446 if (t == DocVerbatim::JavaDocCode)
4447 {
4448 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"javadoc code section ended without end marker");
4449 }
4450 else
4451 {
4452 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"javadoc literal section ended without end marker");
4453 }
4454 }
4456 }
4457 break;
4460 {
4461 if (cmdId == CommandType::CMD_VERBATIM)
4462 {
4464 }
4465 else
4466 {
4468 }
4469 retval = parser()->tokenizer.lex();
4471 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4472 {
4473 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"verbatim section ended without end marker");
4474 }
4476 }
4477 break;
4479 {
4488 QCString width,height;
4489 parser()->defaultHandleTitleAndSize(CommandType::CMD_DOT,&children().back(),dv->children(),width,height);
4491 retval = parser()->tokenizer.lex();
4492 dv->setText(parser()->context.token->verb);
4493 dv->setWidth(width);
4494 dv->setHeight(height);
4495 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4496 if (!Config_getBool(HAVE_DOT))
4497 {
4498 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"ignoring \\dot command because HAVE_DOT is not set");
4499 children().pop_back();
4500 }
4501 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4502 {
4503 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"dot section ended without end marker");
4504 }
4506 }
4507 break;
4509 {
4518 QCString width,height;
4519 parser()->defaultHandleTitleAndSize(CommandType::CMD_MSC,&children().back(),dv->children(),width,height);
4521 retval = parser()->tokenizer.lex();
4522 dv->setText(parser()->context.token->verb);
4523 dv->setWidth(width);
4524 dv->setHeight(height);
4525 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4526 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4527 {
4528 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"msc section ended without end marker");
4529 }
4531 }
4532 break;
4534 {
4535 QCString jarPath = Config_getString(PLANTUML_JAR_PATH);
4537 parser()->tokenizer.lex();
4538 QCString fullMatch = parser()->context.token->sectionId;
4539 QCString sectionId = "";
4540 int idx = fullMatch.find('{');
4541 int idxEnd = fullMatch.find("}",idx+1);
4542 StringVector optList;
4543 QCString engine;
4544 if (idx != -1) // options present
4545 {
4546 QCString optStr = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4547 optList = split(optStr.str(),",");
4548 for (const auto &opt : optList)
4549 {
4550 if (opt.empty()) continue;
4551 bool found = false;
4552 QCString locOpt(opt);
4553 locOpt = locOpt.stripWhiteSpace().lower();
4554 if (g_plantumlEngine.find(locOpt.str())!=g_plantumlEngine.end())
4555 {
4556 if (!engine.isEmpty())
4557 {
4558 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Multiple definition of engine for '\\startuml'");
4559 }
4560 engine = locOpt;
4561 found = true;
4562 }
4563 if (!found)
4564 {
4565 if (sectionId.isEmpty())
4566 {
4567 sectionId = opt;
4568 }
4569 else
4570 {
4571 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Multiple use of filename for '\\startuml'");
4572 }
4573 }
4574 }
4575 }
4576 else
4577 {
4578 sectionId = parser()->context.token->sectionId;
4579 }
4580 if (engine.isEmpty()) engine = "uml";
4581
4582 if (sectionId.isEmpty())
4583 {
4585 retval = parser()->tokenizer.lex();
4586 assert(retval.is(TokenRetval::RetVal_OK));
4587
4588 sectionId = parser()->context.token->sectionId;
4589 sectionId = sectionId.stripWhiteSpace();
4590 }
4591
4592 QCString plantFile(sectionId);
4597 false,plantFile);
4599 dv->setEngine(engine);
4601 QCString width,height;
4602 parser()->defaultHandleTitleAndSize(CommandType::CMD_STARTUML,&children().back(),dv->children(),width,height);
4604 retval = parser()->tokenizer.lex();
4605 int line = 0;
4606 QCString trimmedVerb = stripLeadingAndTrailingEmptyLines(parser()->context.token->verb,line);
4607 if (engine == "ditaa")
4608 {
4609 dv->setUseBitmap(true);
4610 }
4611 else if (engine == "uml")
4612 {
4613 int i = trimmedVerb.find('\n');
4614 QCString firstLine = i==-1 ? trimmedVerb : trimmedVerb.left(i);
4615 if (firstLine.stripWhiteSpace() == "ditaa") dv->setUseBitmap(true);
4616 }
4617 dv->setText(trimmedVerb);
4618 dv->setWidth(width);
4619 dv->setHeight(height);
4620 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4621 if (jarPath.isEmpty())
4622 {
4623 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"ignoring \\startuml command because PLANTUML_JAR_PATH is not set");
4624 children().pop_back();
4625 }
4626 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4627 {
4628 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"startuml section ended without end marker");
4629 }
4631 }
4632 break;
4634 {
4636 parser()->tokenizer.lex();
4637 QCString fullMatch = parser()->context.token->sectionId;
4638 QCString sectionId = "";
4639 int idx = fullMatch.find('{');
4640 int idxEnd = fullMatch.find("}",idx+1);
4641 if (idx != -1) // options present
4642 {
4643 sectionId = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4644 }
4645 else
4646 {
4647 sectionId = parser()->context.token->sectionId;
4648 }
4649
4650 if (sectionId.isEmpty())
4651 {
4653 retval = parser()->tokenizer.lex();
4654 assert(retval.is(TokenRetval::RetVal_OK));
4655
4656 sectionId = parser()->context.token->sectionId;
4657 sectionId = sectionId.stripWhiteSpace();
4658 }
4659
4660 QCString mermaidFile(sectionId);
4665 false,mermaidFile);
4668 QCString width,height;
4669 parser()->defaultHandleTitleAndSize(CommandType::CMD_MERMAID,&children().back(),dv->children(),width,height);
4671 retval = parser()->tokenizer.lex();
4672 int line = 0;
4673 QCString trimmedVerb = stripLeadingAndTrailingEmptyLines(parser()->context.token->verb,line);
4674 dv->setText(trimmedVerb);
4675 dv->setWidth(width);
4676 dv->setHeight(height);
4677 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4678 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4679 {
4680 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"mermaid section ended without end marker");
4681 }
4683 }
4684 break;
4686 retval = Token::make_RetVal_EndParBlock();
4687 break;
4704 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4705 break;
4707 retval = handleParamSection(cmdName,DocParamSect::Param,false,parser()->context.token->paramDir);
4709 break;
4711 retval = handleParamSection(cmdName,DocParamSect::TemplateParam,false,parser()->context.token->paramDir);
4712 break;
4714 retval = handleParamSection(cmdName,DocParamSect::RetVal);
4715 break;
4718 break;
4720 retval = handleXRefItem();
4721 break;
4723 {
4725 }
4726 break;
4729 {
4731 }
4732 break;
4734 {
4736 }
4737 break;
4739 {
4743 retval = children().get_last<DocIndexEntry>()->parse();
4744 }
4745 break;
4747 retval = Token::make_RetVal_Internal();
4748 break;
4750 retval = Token::make_RetVal_EndInternal();
4751 break;
4753 {
4755 retval = children().get_last<DocParBlock>()->parse();
4756 }
4757 break;
4758 case CommandType::CMD_COPYDOC: // fall through
4759 case CommandType::CMD_COPYBRIEF: // fall through
4761 //retval = Token::make_RetVal_CopyDoc();
4762 // these commands should already be resolved by processCopyDoc()
4763 break;
4766 break;
4769 break;
4772 break;
4775 break;
4778 break;
4781 break;
4784 break;
4787 break;
4790 break;
4793 break;
4796 break;
4799 break;
4802 break;
4805 break;
4808 break;
4811 break;
4814 break;
4816 if (!Config_getBool(HAVE_DOT))
4817 {
4818 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
4819 "ignoring \\dotfile command because HAVE_DOT is not set");
4820 }
4821 else
4822 {
4823 handleFile<DocDotFile>(cmdName);
4824 }
4825 break;
4828 break;
4830 handleFile<DocMscFile>(cmdName);
4831 break;
4833 handleFile<DocDiaFile>(cmdName);
4834 break;
4837 break;
4840 break;
4842 handleLink(cmdName,false);
4843 break;
4845 handleLink(cmdName,true);
4846 break;
4848 {
4850 }
4851 break;
4853 handleEmoji(cmdChar,cmdName);
4854 break;
4856 handleDoxyConfig(cmdChar,cmdName);
4857 break;
4859 // fall through
4861 parser()->handleRef(thisVariant(),children(),cmdChar,cmdName);
4862 break;
4864 {
4867 }
4868 break;
4870 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4871 break;
4873 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4874 break;
4876 {
4878 }
4879 break;
4880 //case CommandType::CMD_LANGSWITCH:
4881 // retval = handleLanguageSwitch();
4882 // break;
4884 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4885 {
4888 }
4889 break;
4892 break;
4894 handleShowDate(cmdChar,cmdName);
4895 break;
4897 parser()->handleILine(cmdChar,cmdName);
4898 break;
4900 parser()->handleIFile(cmdChar,cmdName);
4901 break;
4903 {
4905 (void)parser()->tokenizer.lex();
4907 //printf("Found scope='%s'\n",qPrint(parser()->context.context));
4909 }
4910 break;
4911 default:
4912 // we should not get here!
4913 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' in paragraph context",cmdName);
4914 break;
4915 }
4916 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_OK,TokenRetval::RetVal_SimpleSec
4917 TokenRetval::TK_LISTITEM,TokenRetval::TK_ENDLIST,TokenRetval::TK_NEWPARA
4918 TokenRetval::RetVal_Section,TokenRetval::RetVal_EndList
4919 TokenRetval::RetVal_Internal,TokenRetval::RetVal_SwitchLang
4920 TokenRetval::RetVal_EndInternal)
4921 );
4922 AUTO_TRACE_EXIT("retval={}",retval.to_string());
4923 return retval;
4924}
4925
4926static bool findAttribute(const HtmlAttribList &tagHtmlAttribs,
4927 const char *attrName,
4928 QCString *result)
4929{
4930
4931 for (const auto &opt : tagHtmlAttribs)
4932 {
4933 if (opt.name==attrName)
4934 {
4935 *result = opt.value;
4936 return true;
4937 }
4938 }
4939 return false;
4940}
4941
4942Token DocPara::handleHtmlStartTag(const QCString &tagName,const HtmlAttribList &tagHtmlAttribs)
4943{
4944 AUTO_TRACE("tagName={} #tagHtmlAttrs={}",tagName,tagHtmlAttribs.size());
4945 Token retval = Token::make_RetVal_OK();
4946 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
4947 if (parser()->context.token->emptyTag && !(tagId>HtmlTagType::XML_CmdMask) &&
4950 {
4951 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"HTML tag ('<{}/>') may not use the 'empty tag' XHTML syntax.",
4952 tagName);
4953 }
4954 switch (tagId)
4955 {
4957 if (!parser()->context.token->emptyTag)
4958 {
4960 tagHtmlAttribs,DocHtmlList::Unordered);
4961 retval=children().get_last<DocHtmlList>()->parse();
4962 }
4963 break;
4965 if (!parser()->context.token->emptyTag)
4966 {
4968 tagHtmlAttribs,DocHtmlList::Ordered);
4969 retval=children().get_last<DocHtmlList>()->parse();
4970 }
4971 break;
4973 if (parser()->context.token->emptyTag) break;
4975 {
4976 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <li> tag found");
4977 }
4978 else
4979 {
4980 retval = Token::make_RetVal_ListItem();
4981 }
4982 break;
4984 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Bold,tagName,&parser()->context.token->attribs);
4985 break;
4987 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::S,tagName,&parser()->context.token->attribs);
4988 break;
4990 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Strike,tagName,&parser()->context.token->attribs);
4991 break;
4993 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Del,tagName,&parser()->context.token->attribs);
4994 break;
4996 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Underline,tagName,&parser()->context.token->attribs);
4997 break;
4999 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Ins,tagName,&parser()->context.token->attribs);
5000 break;
5002 if (parser()->context.token->emptyTag) break;
5003 if (parser()->context.xmlComment)
5004 // for C# source or inside a <summary> or <remark> section we
5005 // treat <code> as an XML tag (so similar to @code)
5006 {
5008 retval = handleStartCode();
5009 }
5010 else // normal HTML markup
5011 {
5012 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
5013 }
5014 break;
5016 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Kbd,tagName,&parser()->context.token->attribs);
5017 break;
5019 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Typewriter,tagName,&parser()->context.token->attribs);
5020 break;
5022 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Italic,tagName,&parser()->context.token->attribs);
5023 break;
5025 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Div,tagName,&parser()->context.token->attribs);
5026 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Div,tagName);
5027 break;
5029 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Span,tagName,&parser()->context.token->attribs);
5030 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Span,tagName);
5031 break;
5033 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Subscript,tagName,&parser()->context.token->attribs);
5034 break;
5036 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Superscript,tagName,&parser()->context.token->attribs);
5037 break;
5039 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Center,tagName,&parser()->context.token->attribs);
5040 break;
5042 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Small,tagName,&parser()->context.token->attribs);
5043 break;
5045 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Cite,tagName,&parser()->context.token->attribs);
5046 break;
5048 if (parser()->context.token->emptyTag) break;
5049 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Preformatted,tagName,&parser()->context.token->attribs);
5051 parser()->tokenizer.setInsidePre(true);
5052 break;
5054 retval = Token::make_TK_NEWPARA();
5055 break;
5057 if (!parser()->context.token->emptyTag)
5058 {
5059 children().append<DocHtmlDescList>(parser(),thisVariant(),tagHtmlAttribs);
5060 retval=children().get_last<DocHtmlDescList>()->parse();
5061 }
5062 break;
5064 if (insideDL(thisVariant()))
5065 {
5066 retval = Token::make_RetVal_DescTitle();
5067 }
5068 else
5069 {
5070 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dt> found");
5071 }
5072 break;
5074 if (insideDL(thisVariant()))
5075 {
5076 retval = Token::make_RetVal_DescData();
5077 }
5078 else
5079 {
5080 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dd> found");
5081 }
5082 break;
5084 if (!parser()->context.token->emptyTag)
5085 {
5086 children().append<DocHtmlTable>(parser(),thisVariant(),tagHtmlAttribs);
5087 retval=children().get_last<DocHtmlTable>()->parse();
5088 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5089 }
5090 break;
5092 retval = Token::make_RetVal_TableRow();
5093 break;
5095 retval = Token::make_RetVal_TableCell();
5096 break;
5098 retval = Token::make_RetVal_TableHCell();
5099 break;
5103 // for time being ignore </t....> tag
5104 break;
5106 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <caption> found");
5107 break;
5109 {
5110 children().append<DocLineBreak>(parser(),thisVariant(),tagHtmlAttribs);
5111 }
5112 break;
5114 {
5115 children().append<DocHorRuler>(parser(),thisVariant(),tagHtmlAttribs);
5116 }
5117 break;
5119 retval = parser()->handleAHref(thisVariant(),children(),tagHtmlAttribs);
5120 break;
5122 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,1);
5123 break;
5125 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,2);
5126 break;
5128 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,3);
5129 break;
5131 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,4);
5132 break;
5134 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,5);
5135 break;
5137 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,6);
5138 break;
5140 {
5141 parser()->handleImg(thisVariant(),children(),tagHtmlAttribs);
5142 }
5143 break;
5145 if (!parser()->context.token->emptyTag)
5146 {
5147 children().append<DocHtmlDetails>(parser(),thisVariant(),tagHtmlAttribs);
5148 retval=children().get_last<DocHtmlDetails>()->parse();
5149 }
5150 break;
5152 if (!parser()->context.token->emptyTag)
5153 {
5154 children().append<DocHtmlBlockQuote>(parser(),thisVariant(),tagHtmlAttribs);
5155 retval = children().get_last<DocHtmlBlockQuote>()->parse();
5156 }
5157 break;
5158
5161 {
5162 if (!parser()->context.token->emptyTag)
5163 {
5165 while (n && !std::holds_alternative<DocHtmlDetails>(*n)) n=::parent(n);
5166 DocHtmlDetails *d = std::get_if<DocHtmlDetails>(n);
5167 if (d)
5168 {
5169 if (!d->summary()) // details section does not have a summary yet
5170 {
5171 d->parseSummary(n,parser()->context.token->attribs);
5172 }
5173 else
5174 {
5175 retval = Token::make_TK_NEWPARA();
5176 }
5177 }
5178 }
5179 }
5180 break;
5183 parser()->context.xmlComment=true;
5184 // fall through
5187 if (!children().empty())
5188 {
5189 retval = Token::make_TK_NEWPARA();
5190 }
5191 break;
5193 if (insideTable(thisVariant()))
5194 {
5195 retval = Token::make_RetVal_TableCell();
5196 }
5197 break;
5198 case HtmlTagType::XML_C:
5199 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
5200 break;
5203 {
5204 parser()->context.xmlComment=true;
5205 QCString paramName;
5206 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5207 {
5208 if (paramName.isEmpty())
5209 {
5210 if (Config_getBool(WARN_NO_PARAMDOC))
5211 {
5212 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"empty 'name' attribute for <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5213 }
5214 }
5215 else
5216 {
5217 retval = handleParamSection(paramName,
5219 true);
5220 }
5221 }
5222 else
5223 {
5224 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5225 }
5226 }
5227 break;
5230 {
5231 QCString paramName;
5232 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5233 {
5234 //printf("paramName=%s\n",qPrint(paramName));
5236 children().append<DocWord>(parser(),thisVariant(),paramName);
5238 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
5239 }
5240 else
5241 {
5242 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}ref> tag.",tagId==HtmlTagType::XML_PARAMREF?"":"type");
5243 }
5244 }
5245 break;
5247 {
5248 parser()->context.xmlComment=true;
5249 QCString exceptName;
5250 if (findAttribute(tagHtmlAttribs,"cref",&exceptName))
5251 {
5252 unescapeCRef(exceptName);
5253 retval = handleParamSection(exceptName,DocParamSect::Exception,true);
5254 }
5255 else
5256 {
5257 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <exception> tag.");
5258 }
5259 }
5260 break;
5263 if (insideTable(thisVariant()))
5264 {
5265 retval = Token::make_RetVal_TableRow();
5266 }
5267 else if (insideUL(thisVariant()) || insideOL(thisVariant()))
5268 {
5269 retval = Token::make_RetVal_ListItem();
5270 }
5271 else
5272 {
5273 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <item> tag found");
5274 }
5275 break;
5277 parser()->context.xmlComment=true;
5280 break;
5282 if (insideTable(thisVariant()))
5283 {
5284 retval = Token::make_RetVal_TableCell();
5285 }
5286 break;
5288 // I'm not sure if <see> is the same as <seealso> or if it
5289 // should you link a member without producing a section. The
5290 // C# specification is extremely vague about this (but what else
5291 // can we expect from Microsoft...)
5292 {
5293 QCString cref;
5294 //printf("HtmlTagType::XML_SEE: empty tag=%d\n",parser()->context.token->emptyTag);
5295 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5296 {
5297 unescapeCRef(cref);
5298 if (parser()->context.token->emptyTag) // <see cref="..."/> style
5299 {
5300 bool inSeeBlock = parser()->context.inSeeBlock;
5301 parser()->context.token->name = cref;
5302 parser()->context.inSeeBlock = true;
5304 parser()->context.inSeeBlock = inSeeBlock;
5305 }
5306 else // <see cref="...">...</see> style
5307 {
5310 DocLink *lnk = children().get_last<DocLink>();
5311 QCString leftOver = lnk->parse(false,true);
5312 if (!leftOver.isEmpty())
5313 {
5314 children().append<DocWord>(parser(),thisVariant(),leftOver);
5315 }
5316 }
5317 }
5318 else if (findAttribute(tagHtmlAttribs,"langword",&cref)) // <see langword="..."/> or <see langword="..."></see>
5319 {
5320 bool inSeeBlock = parser()->context.inSeeBlock;
5321 parser()->context.token->name = cref;
5322 parser()->context.inSeeBlock = true;
5326 parser()->context.inSeeBlock = inSeeBlock;
5327 }
5328 else
5329 {
5330 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' or 'langword' attribute from <see> tag.");
5331 }
5332 }
5333 break;
5335 {
5336 parser()->context.xmlComment=true;
5337 QCString cref;
5338 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5339 {
5340 unescapeCRef(cref);
5341 // Look for an existing "see" section
5342 DocNodeVariant *vss=nullptr;
5343 for (auto &n : children())
5344 {
5345 DocSimpleSect *candidate = std::get_if<DocSimpleSect>(&n);
5346 if (candidate && candidate->type()==DocSimpleSect::See)
5347 {
5348 vss = &n;
5349 }
5350 }
5351
5352 if (!vss) // start new section
5353 {
5355 vss = &children().back();
5356 }
5357
5358 std::get<DocSimpleSect>(*vss).appendLinkWord(cref);
5359 retval = Token::make_RetVal_OK();
5360 }
5361 else
5362 {
5363 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <seealso> tag.");
5364 }
5365 }
5366 break;
5368 {
5369 QCString type;
5370 findAttribute(tagHtmlAttribs,"type",&type);
5372 HtmlAttribList emptyList;
5373 if (type=="number")
5374 {
5375 listType=DocHtmlList::Ordered;
5376 }
5377 if (type=="table")
5378 {
5379 children().append<DocHtmlTable>(parser(),thisVariant(),emptyList);
5380 retval=children().get_last<DocHtmlTable>()->parseXml();
5381 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5382 }
5383 else
5384 {
5385 children().append<DocHtmlList>(parser(),thisVariant(),emptyList,listType);
5386 retval=children().get_last<DocHtmlList>()->parseXml();
5387 }
5388 }
5389 break;
5392 // These tags are defined in .Net but are currently unsupported
5393 parser()->context.xmlComment=true;
5394 break;
5396 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag <{}> found", tagName);
5397 children().append<DocWord>(parser(),thisVariant(), "<"+tagName+parser()->context.token->attribsStr+">");
5398 break;
5401 break;
5402 default:
5403 // we should not get here!
5404 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected start tag {}",tagName);
5405 ASSERT(0);
5406 break;
5407 }
5408 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5409 return retval;
5410}
5411
5413{
5414 AUTO_TRACE("tagName={}",tagName);
5415 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
5416 Token retval = Token::make_RetVal_OK();
5417 switch (tagId)
5418 {
5420 if (!insideUL(thisVariant()))
5421 {
5422 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ul> tag without matching <ul>");
5423 }
5424 else
5425 {
5426 retval = Token::make_RetVal_EndList();
5427 }
5428 break;
5430 if (!insideOL(thisVariant()))
5431 {
5432 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ol> tag without matching <ol>");
5433 }
5434 else
5435 {
5436 retval = Token::make_RetVal_EndList();
5437 }
5438 break;
5440 if (!insideLI(thisVariant()))
5441 {
5442 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </li> tag without matching <li>");
5443 }
5444 else
5445 {
5446 // ignore </li> tags
5447 }
5448 break;
5450 if (!insideDetails(thisVariant()))
5451 {
5452 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </details> tag without matching <details>");
5453 }
5454 else
5455 {
5456 retval = Token::make_RetVal_EndHtmlDetails();
5457 }
5458 break;
5461 {
5462 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </blockquote> tag without matching <blockquote>");
5463 }
5464 else
5465 {
5466 retval = Token::make_RetVal_EndBlockQuote();
5467 }
5468 break;
5471 break;
5474 break;
5477 break;
5480 break;
5483 break;
5486 break;
5489 break;
5492 break;
5495 break;
5498 break;
5501 break;
5504 break;
5507 break;
5510 break;
5513 break;
5516 break;
5519 break;
5522 setInsidePreformatted(false);
5523 parser()->tokenizer.setInsidePre(false);
5524 break;
5526 retval = Token::make_TK_NEWPARA();
5527 break;
5529 retval = Token::make_RetVal_EndDesc();
5530 break;
5532 // ignore </dt> tag
5533 break;
5535 // ignore </dd> tag
5536 break;
5538 retval = Token::make_RetVal_EndTable();
5539 break;
5541 retval = Token::make_RetVal_EndTableRow();
5542 break;
5544 retval = Token::make_RetVal_EndTableCell();
5545 break;
5547 retval = Token::make_RetVal_EndTableCell();
5548 break;
5552 // for time being ignore </t....> tag
5553 break;
5555 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </caption> found");
5556 break;
5558 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </br> tag found");
5559 break;
5561 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h1> found");
5562 break;
5564 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h2> found");
5565 break;
5567 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h3> found");
5568 break;
5570 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h4> found");
5571 break;
5573 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h5> found");
5574 break;
5576 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h6> found");
5577 break;
5579 break;
5581 // warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </hr> tag found");
5582 break;
5584 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </a> found");
5585 // ignore </a> tag (can be part of <a name=...></a>
5586 break;
5587
5589 break;
5591 retval = Token::make_TK_NEWPARA();
5592 break;
5605 retval = Token::make_RetVal_CloseXml();
5606 break;
5607 case HtmlTagType::XML_C:
5609 break;
5617 // These tags are defined in .Net but are currently unsupported
5618 break;
5620 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag </{}> found", tagName);
5621 children().append<DocWord>(parser(),thisVariant(),"</"+tagName+">");
5622 break;
5623 default:
5624 // we should not get here!
5625 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end tag {}",tagName);
5626 ASSERT(0);
5627 break;
5628 }
5629 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5630 return retval;
5631}
5632
5634{
5635 // expected hierarchy:
5636 // 1. DocAutoListItem <- n
5637 // 2. DocAutoList <- parent(n)
5638 // 3. DocPara <- parent(parent(n))
5639
5640 // step 1
5641 if (!std::get_if<DocAutoListItem>(n)) // not inside a auto list item
5642 {
5643 return false;
5644 }
5645
5646 // step 2
5647 n = parent(n);
5648 int indent = 0;
5649 const auto docAutoList = std::get_if<DocAutoList>(n);
5650 if (docAutoList) // capture indent
5651 {
5652 indent = docAutoList->indent();
5653 }
5654 else
5655 {
5656 return false;
5657 }
5658
5659 // step 3
5660 n = parent(n);
5661 const auto docPara = std::get_if<DocPara>(n);
5662 if (docPara)
5663 {
5664 QCString tagNameLower = QCString(parser->context.token->name).lower();
5665 auto topStyleChange = [](const DocStyleChangeStack &stack) -> const DocStyleChange &
5666 {
5667 return std::get<DocStyleChange>(*stack.top());
5668 };
5669
5670 if (parser->context.styleStack.empty() || // no style change
5671 (topStyleChange(parser->context.styleStack).tagName()==tagNameLower && // correct style change
5672 topStyleChange(parser->context.styleStack).position()!=parser->context.nodeStack.size()) // wrong position, so normal close
5673 )
5674 {
5675 // insert an artificial 'end of autolist' marker and parse again
5676 QCString indentStr;
5677 indentStr.fill(' ',indent);
5678 parser->tokenizer.unputString("\\ilinebr "+indentStr+".\\ilinebr"+indentStr+"</"+parser->context.token->name+">");
5679 return true;
5680 }
5681 }
5682 return false;
5683}
5684
5686{
5687 AUTO_TRACE();
5688 auto ns = AutoNodeStack(parser(),thisVariant());
5689 // handle style commands "inherited" from the previous paragraph
5691 Token tok=parser()->tokenizer.lex();
5692 Token retval = Token::make_TK_NONE();
5693 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
5694 {
5695reparsetoken:
5696 AUTO_TRACE_ADD("token '{}' at {}",tok.to_string(),parser()->tokenizer.getLineNr());
5697 if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL,
5698 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG)
5699 )
5700 {
5701 AUTO_TRACE_ADD("name={}",parser()->context.token->name);
5702 }
5703 switch(tok.value())
5704 {
5705 case TokenRetval::TK_WORD:
5707 break;
5708 case TokenRetval::TK_LNKWORD:
5710 break;
5711 case TokenRetval::TK_URL:
5713 break;
5714 case TokenRetval::TK_WHITESPACE:
5715 {
5716 // prevent leading whitespace and collapse multiple whitespace areas
5717 if (insidePRE(thisVariant()) || // all whitespace is relevant
5718 (
5719 // remove leading whitespace
5720 !children().empty() &&
5721 // and whitespace after certain constructs
5725 )
5726 )
5727 {
5729 }
5730 }
5731 break;
5732 case TokenRetval::TK_LISTITEM:
5733 {
5734 AUTO_TRACE_ADD("found list item at {}",parser()->context.token->indent);
5735 const DocNodeVariant *n=parent();
5736 while (n && !std::holds_alternative<DocAutoList>(*n)) n=::parent(n);
5737 const DocAutoList *al = std::get_if<DocAutoList>(n);
5738 if (al) // we found an auto list up in the hierarchy
5739 {
5740 AUTO_TRACE_ADD("previous list item at {}",al->indent());
5741 if (al->indent()>=parser()->context.token->indent)
5742 // new item at the same or lower indent level
5743 {
5744 retval = Token::make_TK_LISTITEM();
5745 goto endparagraph;
5746 }
5747 }
5748
5749 // determine list depth
5750 int depth = 0;
5751 n=parent();
5752 while (n)
5753 {
5754 al = std::get_if<DocAutoList>(n);
5755 if (al && al->isEnumList()) depth++;
5756 n=::parent(n);
5757 }
5758
5759 // first item or sub list => create new list
5760 do
5761 {
5764 parser()->context.token->isEnumList,depth,
5766 al = children().get_last<DocAutoList>();
5767 retval = children().get_last<DocAutoList>()->parse();
5768 } while (retval.is(TokenRetval::TK_LISTITEM) && // new list
5769 al->indent()==parser()->context.token->indent // at same indent level
5770 );
5771
5772 // check the return value
5773 if (retval.is(TokenRetval::RetVal_SimpleSec)) // auto list ended due to simple section command
5774 {
5775 // Reparse the token that ended the section at this level,
5776 // so a new simple section will be started at this level.
5777 // This is the same as unputting the last read token and continuing.
5779 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5780 {
5783 tok = Token::make_TK_RCSTAG();
5784 }
5785 else // other section
5786 {
5787 tok = Token::make_TK_COMMAND_BS();
5788 }
5789 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5790 goto reparsetoken;
5791 }
5792 else if (retval.is(TokenRetval::TK_ENDLIST))
5793 {
5794 if (al->indent()>parser()->context.token->indent) // end list
5795 {
5796 goto endparagraph;
5797 }
5798 else // continue with current paragraph
5799 {
5800 }
5801 }
5802 else // paragraph ended due to TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, or EOF
5803 {
5804 goto endparagraph;
5805 }
5806 }
5807 break;
5808 case TokenRetval::TK_ENDLIST:
5809 AUTO_TRACE_ADD("Found end of list inside of paragraph at line {}",parser()->tokenizer.getLineNr());
5810 if (std::get_if<DocAutoListItem>(parent()))
5811 {
5812 const DocAutoList *al = std::get_if<DocAutoList>(::parent(parent()));
5813 if (al && al->indent()>=parser()->context.token->indent)
5814 {
5815 // end of list marker ends this paragraph
5816 retval = Token::make_TK_ENDLIST();
5817 goto endparagraph;
5818 }
5819 else
5820 {
5821 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found "
5822 "has invalid indent level");
5823 }
5824 }
5825 else
5826 {
5827 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found without any preceding "
5828 "list items");
5829 }
5830 break;
5831 case TokenRetval::TK_COMMAND_AT:
5832 // fall through
5833 case TokenRetval::TK_COMMAND_BS:
5834 {
5835 // see if we have to start a simple section
5836 CommandType cmd = Mappers::cmdMapper->map(parser()->context.token->name);
5837 const DocNodeVariant *n=parent();
5838 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
5839 !std::holds_alternative<DocParamSect>(*n))
5840 {
5841 n=::parent(n);
5842 }
5844 {
5845 if (n) // already in a simple section
5846 {
5847 // simple section cannot start in this paragraph, need
5848 // to unwind the stack and remember the command.
5850 retval = Token::make_RetVal_SimpleSec();
5851 goto endparagraph;
5852 }
5853 }
5854 // see if we are in a simple list
5855 n=parent();
5856 while (n && !std::holds_alternative<DocSimpleListItem>(*n)) n=::parent(n);
5857 if (n)
5858 {
5859 if (cmd==CommandType::CMD_LI)
5860 {
5861 retval = Token::make_RetVal_ListItem();
5862 goto endparagraph;
5863 }
5864 }
5865
5866 // handle the command
5867 retval=handleCommand(tok.command_to_char(),parser()->context.token->name);
5868 AUTO_TRACE_ADD("handleCommand returns {}",retval.to_string());
5869
5870 // check the return value
5871 if (retval.is(TokenRetval::RetVal_SimpleSec))
5872 {
5873 // Reparse the token that ended the section at this level,
5874 // so a new simple section will be started at this level.
5875 // This is the same as unputting the last read token and continuing.
5877 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5878 {
5881 tok = Token::make_TK_RCSTAG();
5882 }
5883 else // other section
5884 {
5885 tok = Token::make_TK_COMMAND_BS();
5886 }
5887 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5888 goto reparsetoken;
5889 }
5890 else if (retval.value()>TokenRetval::TK_NONE && retval.value()<TokenRetval::RetVal_OK)
5891 {
5892 // the command ended with a new command, reparse this token
5893 tok = retval;
5894 goto reparsetoken;
5895 }
5896 else if (retval.value()!=TokenRetval::RetVal_OK) // end of file, end of paragraph, start or end of section
5897 // or some auto list marker
5898 {
5899 goto endparagraph;
5900 }
5901 }
5902 break;
5903 case TokenRetval::TK_HTMLTAG:
5904 {
5905 if (!parser()->context.token->endTag) // found a start tag
5906 {
5907 retval = handleHtmlStartTag(parser()->context.token->name,parser()->context.token->attribs);
5908 }
5909 else // found an end tag
5910 {
5912 {
5913 break; // new code has been pushed back to the scanner, need to reparse
5914 }
5915 retval = handleHtmlEndTag(parser()->context.token->name);
5916 }
5917 if (!retval.is(TokenRetval::RetVal_OK))
5918 {
5919 goto endparagraph;
5920 }
5921 }
5922 break;
5923 case TokenRetval::TK_SYMBOL:
5924 {
5925 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
5927 {
5929 }
5930 else
5931 {
5933 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
5934 parser()->context.token->name);
5935 }
5936 break;
5937 }
5938 case TokenRetval::TK_NEWPARA:
5939 retval = Token::make_TK_NEWPARA();
5940 goto endparagraph;
5941 case TokenRetval::TK_RCSTAG:
5942 {
5943 const DocNodeVariant *n=parent();
5944 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
5945 !std::holds_alternative<DocParamSect>(*n))
5946 {
5947 n=::parent(n);
5948 }
5949 if (n) // already in a simple section
5950 {
5951 // simple section cannot start in this paragraph, need
5952 // to unwind the stack and remember the command.
5955 retval = Token::make_RetVal_SimpleSec();
5956 goto endparagraph;
5957 }
5958
5959 // see if we are in a simple list
5961 children().get_last<DocSimpleSect>()->parseRcs();
5962 }
5963 break;
5964 default:
5965 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
5966 "Found unexpected token (id={})",tok.to_string());
5967 break;
5968 }
5969 tok=parser()->tokenizer.lex();
5970 }
5971 retval=Token::make_TK_NONE();
5972endparagraph:
5974 DocPara *par = std::get_if<DocPara>(parser()->context.nodeStack.top());
5975 if (!parser()->context.token->endTag && par &&
5976 retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->name.lower() == "p")
5977 {
5978 par->setAttribs(parser()->context.token->attribs);
5979 }
5980 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM,
5981 TokenRetval::TK_ENDLIST,TokenRetval::RetVal_OK)
5982 );
5983
5984 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5985 return retval;
5986}
5987
5988//--------------------------------------------------------------------------
5989
5991{
5992 AUTO_TRACE("start {} level={}", parser()->context.token->sectionId, m_level);
5993 Token retval = Token::make_RetVal_OK();
5994 auto ns = AutoNodeStack(parser(),thisVariant());
5995
5996 if (!m_id.isEmpty())
5997 {
5999 if (sec)
6000 {
6001 m_file = sec->fileName();
6002 m_anchor = sec->label();
6003 QCString titleStr = sec->title();
6004 if (titleStr.isEmpty()) titleStr = sec->label();
6006 DocTitle *title = &std::get<DocTitle>(*m_title);
6007 title->parseFromString(thisVariant(),titleStr);
6008 }
6009 }
6010
6011 // first parse any number of paragraphs
6012 bool isFirst=true;
6013 DocPara *lastPar=nullptr;
6014 do
6015 {
6017 DocPara *par = children().get_last<DocPara>();
6018 if (isFirst) { par->markFirst(); isFirst=false; }
6019 retval=par->parse();
6020 if (!par->isEmpty())
6021 {
6022 if (lastPar) lastPar->markLast(false);
6023 lastPar = par;
6024 }
6025 else
6026 {
6027 children().pop_back();
6028 }
6029 if (retval.is(TokenRetval::TK_LISTITEM))
6030 {
6031 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6032 }
6033 if (retval.is(TokenRetval::RetVal_Internal))
6034 {
6036 retval = children().get_last<DocInternal>()->parse(m_level+1);
6037 if (retval.is(TokenRetval::RetVal_EndInternal))
6038 {
6039 retval = Token::make_RetVal_OK();
6040 }
6041 }
6042 } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6043 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph,
6044 TokenRetval::RetVal_SubSubParagraph, TokenRetval::RetVal_EndInternal)
6045 );
6046
6047 if (lastPar) lastPar->markLast();
6048
6049 while (true)
6050 {
6051 if (retval.is(TokenRetval::RetVal_Subsection) && m_level<=1)
6052 {
6053 // then parse any number of nested sections
6054 while (retval.is(TokenRetval::RetVal_Subsection)) // more sections follow
6055 {
6057 2,
6059 retval = children().get_last<DocSection>()->parse();
6060 }
6061 break;
6062 }
6063 else if (retval.is(TokenRetval::RetVal_Subsubsection) && m_level<=2)
6064 {
6065 if ((m_level <= 1) &&
6066 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6067 {
6068 warn_doc_error(parser()->context.fileName,
6069 parser()->tokenizer.getLineNr(),
6070 "Unexpected subsubsection command found inside {}!",
6072 }
6073 // then parse any number of nested sections
6074 while (retval.is(TokenRetval::RetVal_Subsubsection)) // more sections follow
6075 {
6077 3,
6079 retval = children().get_last<DocSection>()->parse();
6080 }
6081 if (!(m_level < 2 && retval.is(TokenRetval::RetVal_Subsection))) break;
6082 }
6083 else if (retval.is(TokenRetval::RetVal_Paragraph) && m_level<=3)
6084 {
6085 if ((m_level <= 2) &&
6086 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6087 {
6088 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6089 "Unexpected paragraph command found inside {}!",
6091 }
6092 // then parse any number of nested sections
6093 while (retval.is(TokenRetval::RetVal_Paragraph)) // more sections follow
6094 {
6096 4,
6098 retval = children().get_last<DocSection>()->parse();
6099 }
6100 if (!(m_level<3 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection)))) break;
6101 }
6102 else if (retval.is(TokenRetval::RetVal_SubParagraph) && m_level<=4)
6103 {
6104 if ((m_level <= 3) &&
6105 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6106 {
6107 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6108 "Unexpected subparagraph command found inside {}!",
6110 }
6111 // then parse any number of nested sections
6112 while (retval.is(TokenRetval::RetVal_SubParagraph)) // more sections follow
6113 {
6115 5,
6117 retval = children().get_last<DocSection>()->parse();
6118 }
6119 if (!(m_level<4 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection,TokenRetval::RetVal_Paragraph)))) break;
6120 }
6121 else if (retval.is(TokenRetval::RetVal_SubSubParagraph) && m_level<=5)
6122 {
6123 if ((m_level <= 4) &&
6124 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6125 {
6126 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6127 "Unexpected subsubparagraph command found inside {}!",
6129 }
6130 // then parse any number of nested sections
6131 while (retval.is(TokenRetval::RetVal_SubSubParagraph)) // more sections follow
6132 {
6134 6,
6136 retval = children().get_last<DocSection>()->parse();
6137 }
6138 if (!(m_level<5 && (retval.is_any_of( TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection,
6139 TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph)))) break;
6140 }
6141 else
6142 {
6143 break;
6144 }
6145 }
6146
6147 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF,
6148 TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6149 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph,
6150 TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph,
6151 TokenRetval::RetVal_Internal, TokenRetval::RetVal_EndInternal)
6152 );
6153
6154 AUTO_TRACE_EXIT("retval={}", retval.to_string());
6155 return retval;
6156}
6157
6158//--------------------------------------------------------------------------
6159
6161{
6162 AUTO_TRACE();
6163 auto ns = AutoNodeStack(parser(),thisVariant());
6165
6166 Token tok = parser()->tokenizer.lex();
6167 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
6168 {
6169 switch(tok.value())
6170 {
6171 case TokenRetval::TK_WORD:
6173 break;
6174 case TokenRetval::TK_WHITESPACE:
6176 break;
6177 case TokenRetval::TK_SYMBOL:
6178 {
6179 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
6181 {
6183 }
6184 else
6185 {
6186 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
6187 parser()->context.token->name);
6188 }
6189 }
6190 break;
6191 case TokenRetval::TK_COMMAND_AT:
6192 // fall through
6193 case TokenRetval::TK_COMMAND_BS:
6194 switch (Mappers::cmdMapper->map(parser()->context.token->name))
6195 {
6198 break;
6201 break;
6204 break;
6207 break;
6210 break;
6213 break;
6216 break;
6219 break;
6222 break;
6226 break;
6231 break;
6234 break;
6237 break;
6240 break;
6243 break;
6246 break;
6249 break;
6252 break;
6253 default:
6254 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' found",
6255 parser()->context.token->name);
6256 break;
6257 }
6258 break;
6259 default:
6260 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",
6261 tok.to_string());
6262 break;
6263 }
6264 tok = parser()->tokenizer.lex();
6265 }
6266
6268
6269}
6270
6271
6272//--------------------------------------------------------------------------
6273
6275{
6276 AUTO_TRACE();
6277 auto ns = AutoNodeStack(parser(),thisVariant());
6279 Token retval = Token::make_TK_NONE();
6280
6281 // first parse any number of paragraphs
6282 bool isFirst=true;
6283 DocPara *lastPar = nullptr;
6284 do
6285 {
6286 {
6288 DocPara *par = children().get_last<DocPara>();
6289 if (isFirst) { par->markFirst(); isFirst=false; }
6290 retval=par->parse();
6291 if (par->isEmpty() && par->attribs().empty())
6292 {
6293 children().pop_back();
6294 }
6295 else
6296 {
6297 lastPar = par;
6298 }
6299 }
6300 auto checkParagraph = [this,&retval](Token t,int level,const char *sectionType,const char *parentSectionType) {
6301 if (retval == t)
6302 {
6303 if (!AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6304 {
6305 warn_doc_error(parser()->context.fileName,
6306 parser()->tokenizer.getLineNr(),
6307 "found {} command (id: '{}') outside of {} context!",
6308 sectionType,parser()->context.token->sectionId,parentSectionType);
6309 }
6310 while (retval==t)
6311 {
6312 if (!parser()->context.token->sectionId.isEmpty())
6313 {
6314 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6315 if (sec)
6316 {
6318 level,
6320 retval = children().get_last<DocSection>()->parse();
6321 }
6322 else
6323 {
6324 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid {} id '{}'; ignoring {}",
6325 sectionType,parser()->context.token->sectionId,sectionType);
6326 retval = Token::make_TK_NONE();
6327 }
6328 }
6329 else
6330 {
6331 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for {}; ignoring {}",sectionType,sectionType);
6332 retval = Token::make_TK_NONE();
6333 }
6334 }
6335 }
6336 };
6337 checkParagraph(Token::make_RetVal_SubSubParagraph(), 6, "subsubparagraph", "subparagraph" );
6338 checkParagraph(Token::make_RetVal_SubParagraph(), 5, "subparagraph", "paragraph" );
6339 checkParagraph(Token::make_RetVal_Paragraph(), 4, "paragraph", "subsubsection" );
6340 checkParagraph(Token::make_RetVal_Subsubsection(), 3, "subsubsection", "subsection" );
6341 checkParagraph(Token::make_RetVal_Subsection(), 2, "subsection", "section" );
6342
6343 if (retval.is(TokenRetval::TK_LISTITEM))
6344 {
6345 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6346 }
6347 if (retval.is(TokenRetval::RetVal_Internal))
6348 {
6350 retval = children().get_last<DocInternal>()->parse(1);
6351 }
6352 } while (!retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_Section));
6353 if (lastPar) lastPar->markLast();
6354
6355 //printf("DocRoot::parse() retval=%d %d\n",retval,TokenRetval::RetVal_Section);
6356 // then parse any number of level1 sections
6357 while (retval.is(TokenRetval::RetVal_Section))
6358 {
6359 if (!parser()->context.token->sectionId.isEmpty())
6360 {
6361 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6362 if (sec)
6363 {
6365 1,
6367 retval = children().get_last<DocSection>()->parse();
6368 }
6369 else
6370 {
6371 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid section id '{}'; ignoring section",parser()->context.token->sectionId);
6372 retval = Token::make_TK_NONE();
6373 }
6374 }
6375 else
6376 {
6377 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for section; ignoring section");
6378 retval = Token::make_TK_NONE();
6379 }
6380 }
6381
6383}
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
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
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:516
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:1296
HtmlAttribList m_attribs
Definition docnode.h:1243
bool m_hasCaptionId
Definition docnode.h:1244
DocHtmlCaption(DocParser *parser, DocNodeVariant *parent, const HtmlAttribList &attribs)
Definition docnode.cpp:1776
QCString m_file
Definition docnode.h:1245
const HtmlAttribList & attribs() const
Definition docnode.h:1236
QCString m_anchor
Definition docnode.h:1246
Node representing a HTML table cell.
Definition docnode.h:1198
Valignment valignment() const
Definition docnode.cpp:1980
void setColumnIndex(uint32_t idx)
Definition docnode.h:1222
bool isFirst() const
Definition docnode.h:1206
Token parseXml()
Definition docnode.cpp:1884
void setRowIndex(uint32_t idx)
Definition docnode.h:1221
uint32_t rowSpan() const
Definition docnode.cpp:1918
void markLast(bool v=true)
Definition docnode.h:1209
void markFirst(bool v=true)
Definition docnode.h:1208
Alignment alignment() const
Definition docnode.cpp:1942
bool isHeading() const
Definition docnode.h:1205
const HtmlAttribList & attribs() const
Definition docnode.h:1210
Token parse()
Definition docnode.cpp:1850
uint32_t colSpan() const
Definition docnode.cpp:1930
Node representing a HTML description data.
Definition docnode.h:1186
HtmlAttribList m_attribs
Definition docnode.h:1193
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:1170
Node representing a HTML table row.
Definition docnode.h:1251
Token parseXml(bool header)
Definition docnode.cpp:2184
void setVisibleCells(uint32_t n)
Definition docnode.h:1261
bool isHeading() const
Definition docnode.cpp:2002
void setRowIndex(uint32_t idx)
Definition docnode.h:1266
Token parse()
Definition docnode.cpp:2077
Node Html summary.
Definition docnode.h:853
Node representing a HTML table.
Definition docnode.h:1274
Token parseXml()
Definition docnode.cpp:2363
size_t numberHeaderRows() const
Definition docnode.cpp:2255
std::unique_ptr< DocNodeVariant > m_caption
Definition docnode.h:1289
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:1291
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
void markLast(bool v=true)
Definition docnode.h:505
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
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:674
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
void handleLink(const QCString &cmdName, bool isJavaLink)
Definition docnode.cpp:3860
void handleInheritDoc()
Definition docnode.cpp:4091
DocPara(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:3481
void handleInclude(const QCString &cmdName, DocInclude::Type t)
Definition docnode.cpp:3901
Token handleCommand(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:4132
void handleDoxyConfig(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3571
void handleSection(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:4006
void handleFile(const QCString &cmdName)
Definition docnode.cpp:3821
Token handleParamSection(const QCString &cmdName, DocParamSect::Type t, bool xmlContext, int direction)
Definition docnode.cpp:3517
Token handleHtmlStartTag(const QCString &tagName, const HtmlAttribList &tagHtmlAttribs)
Definition docnode.cpp:4942
void handleEmoji(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3540
void markFirst(bool v=true)
Definition docnode.h:1094
Token handleSimpleSection(DocSimpleSect::Type t, bool xmlContext=false)
Definition docnode.cpp:3487
void handleIncludeOperator(const QCString &cmdName, DocIncOperator::Type t)
Definition docnode.cpp:3761
bool isFirst() const
Definition docnode.h:1096
void setAttribs(const HtmlAttribList &attribs)
Definition docnode.h:1120
bool m_isFirst
Definition docnode.h:1123
Token parse()
Definition docnode.cpp:5685
void markLast(bool v=true)
Definition docnode.h:1095
void handleVhdlFlow()
Definition docnode.cpp:3853
Token handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs, int level)
Definition docnode.cpp:4037
void handleShowDate(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3691
bool m_isLast
Definition docnode.h:1124
Token handleXRefItem()
Definition docnode.cpp:3669
Token handleHtmlEndTag(const QCString &tagName)
Definition docnode.cpp:5412
Token handleStartCode()
Definition docnode.cpp:4055
bool injectToken(Token tok, const QCString &tokText)
Definition docnode.cpp:4048
DocNodeList m_paramTypes
Definition docnode.h:1149
DocNodeList m_paragraphs
Definition docnode.h:1147
void markFirst(bool b=true)
Definition docnode.h:1139
void markLast(bool b=true)
Definition docnode.h:1140
Token parseXml(const QCString &paramName)
Definition docnode.cpp:3370
Token parse(const QCString &cmdName)
Definition docnode.cpp:3289
DocParamSect::Type m_type
Definition docnode.h:1150
DocNodeList m_params
Definition docnode.h:1148
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 handlePendingStyleCommands(DocNodeVariant *parent, DocNodeList &children, size_t numberOfElementsToClose=0)
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)
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)
bool defaultHandleToken(DocNodeVariant *parent, Token &tok, DocNodeList &children, bool handleWord=true)
void handleInitialStyleCommands(DocNodeVariant *parent, DocNodeList &children)
void handleStyleLeave(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const QCString &tagName)
void handleImage(DocNodeVariant *parent, DocNodeList &children)
void handleLinkedWord(DocNodeVariant *parent, DocNodeList &children, bool ignoreAutoLinkFlag=false, bool typeLinkOnly=false)
void handleStyleEnter(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const QCString &tagName, const HtmlAttribList *attribs)
void handleCite(DocNodeVariant *parent, DocNodeList &children)
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 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:709
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:6274
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:535
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:5990
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:1158
std::unique_ptr< DocNodeVariant > m_paragraph
Definition docnode.h:1165
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:6160
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 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
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
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
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:705
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:97
QCString & prepend(const char *s)
Definition qcstring.h:420
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:164
bool startsWith(const char *s) const
Definition qcstring.h:505
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:239
QCString lower() const
Definition qcstring.h:247
bool endsWith(const char *s) const
Definition qcstring.h:522
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:591
int find(char c, int index=0, bool cs=true) const
Definition qcstring.cpp:43
bool isEmpty() const
Returns true iff the string is empty.
Definition qcstring.h:161
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:258
QCString fill(char c, int len=-1)
Fills a string with a predefined character.
Definition qcstring.h:191
const std::string & str() const
Definition qcstring.h:550
QCString & append(char c)
Definition qcstring.h:394
QCString right(size_t len) const
Definition qcstring.h:232
QCString & sprintf(const char *format,...)
Definition qcstring.cpp:29
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:170
std::string_view view() const
Definition qcstring.h:172
int contains(char c, bool cs=true) const
Definition qcstring.cpp:148
QCString left(size_t len) const
Definition qcstring.h:227
void clear()
Definition qcstring.h:180
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
virtual QCString trWriteList(int numEntries)=0
virtual QCString trDetails()=0
static void createFlowChart(const MemberDef *)
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:236
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::multiset< std::string > StringMultiSet
Definition containers.h:32
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:4926
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:5633
constexpr bool holds_one_of_alternatives(const DocNodeVariant &v)
returns true iff v holds one of types passed as template parameters
Definition docnode.h:1371
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:1390
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1335
std::unique_ptr< DocNodeVariant > createDocNode(Args &&...args)
Definition docnode.h:1500
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:43
const char * qPrint(const char *s)
Definition qcstring.h:685
#define ASSERT(x)
Definition qcstring.h:33
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:1404
T * get_last()
Returns a pointer to the last element in the list if that element exists and holds a T,...
Definition docnode.h:1415
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
SrcLangExt lang
Definition docparser_p.h:85
QCString searchUrl
Definition docparser_p.h:83
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
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5253
QCString stripIndentation(const QCString &s, bool skipFirstLine)
Definition util.cpp:5989
QCString linkToText(SrcLangExt lang, const QCString &link, bool ignoreDots)
Definition util.cpp:2708
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3054
QCString stripScope(const QCString &name)
Definition util.cpp:3818
bool resolveLink(const QCString &scName, const QCString &lr, bool, const Definition **resContext, QCString &resAnchor, SrcLangExt lang, const QCString &prefix)
Definition util.cpp:2750
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:3543
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition util.cpp:6658
QCString stripLeadingAndTrailingEmptyLines(const QCString &s, int &docLine)
Special version of QCString::stripWhiteSpace() that only strips completely blank lines.
Definition util.cpp:5072
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:2919
A bunch of utility functions.