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