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
42#if !ENABLE_DOCPARSER_TRACING
43#undef AUTO_TRACE
44#undef AUTO_TRACE_ADD
45#undef AUTO_TRACE_EXIT
46#define AUTO_TRACE(...) (void)0
47#define AUTO_TRACE_ADD(...) (void)0
48#define AUTO_TRACE_EXIT(...) (void)0
49#endif
50
51#define INTERNAL_ASSERT(x) do {} while(0)
52//#define INTERNAL_ASSERT(x) if (!(x)) TRACE("INTERNAL_ASSERT({}) failed retval={:#x}: file={} line={}",#x,retval,__FILE__,__LINE__)
53
54//---------------------------------------------------------------------------
55
56static const char *g_sectionLevelToName[] =
57{
58 "page",
59 "section",
60 "subsection",
61 "subsubsection",
62 "paragraph",
63 "subparagraph"
64};
65
66
67//---------------------------------------------------------------------------
68
70 "uml", "bpm", "wire", "dot", "ditaa",
71 "salt", "math", "latex", "gantt", "mindmap",
72 "wbs", "yaml", "creole", "json", "flow",
73 "board", "git", "hcl", "regex", "ebnf",
74 "files", "chen", "chronology"
75};
76
77//---------------------------------------------------------------------------
78
79// replaces { with < and } with > and also
80// replaces &gt; with < and &gt; with > within string s
81static void unescapeCRef(QCString &s)
82{
83 QCString result;
84 const char *p = s.data();
85 if (p)
86 {
87 char c = 0;
88 while ((c=*p++))
89 {
90 if (c=='{') c='<'; else if (c=='}') c='>';
91 result+=c;
92 }
93 }
94
95 result=substitute(result,"&lt;","<");
96 result=substitute(result,"&gt;",">");
97 s = result;
98}
99
100//---------------------------------------------------------------------------
101
102/*! Strips known html and tex extensions from \a text. */
104{
105 QCString result=text;
106 if (result.endsWith(".tex"))
107 {
108 result=result.left(result.length()-4);
109 }
110 else if (result.right(Doxygen::htmlFileExtension.length())==
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;
591 default:
592 break;
593 }
594 //printf("m_ref=%s,m_file=%s,type=%d\n",
595 // qPrint(m_ref),qPrint(m_file),m_refType);
596 }
597 else
598 {
599 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to unknown section {}",m_target);
600 }
601 }
602 else
603 {
604 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to empty target");
605 }
606}
607
608//---------------------------------------------------------------------------
609
611{
612 AUTO_TRACE();
613 auto ns = AutoNodeStack(parser(),thisVariant());
614
615 Token tok=parser()->tokenizer.lex();
616 // skip white space
617 while (tok.is_any_of(TokenRetval::TK_WHITESPACE, TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
618 // handle items
619 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
620 {
621 if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS))
622 {
623 switch (Mappers::cmdMapper->map(parser()->context.token->name))
624 {
626 {
627 tok=parser()->tokenizer.lex();
628 if (!tok.is(TokenRetval::TK_WHITESPACE))
629 {
630 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\refitem command");
631 break;
632 }
633 tok=parser()->tokenizer.lex();
634 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
635 {
636 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\refitem",
637 tok.to_string());
638 break;
639 }
640
643 }
644 break;
646 return;
647 default:
648 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' as part of a \\secreflist",
649 tok.command_to_char(),qPrint(parser()->context.token->name));
650 return;
651 }
652 }
653 else if (tok.is(TokenRetval::TK_WHITESPACE))
654 {
655 // ignore whitespace
656 }
657 else
658 {
659 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {} inside section reference list",
660 tok.to_string());
661 return;
662 }
663 tok=parser()->tokenizer.lex();
664 }
665
666}
667
668//---------------------------------------------------------------------------
669
672{
673 int i=ref.find('#');
674 if (i!=-1)
675 {
676 m_anchor = ref.right(static_cast<int>(ref.length())-i-1);
677 m_file = ref.left(i);
678 }
679 else
680 {
681 m_file = ref;
682 }
683}
684
686{
687 AUTO_TRACE();
688 auto ns = AutoNodeStack(parser(),thisVariant());
689
690 Token tok = parser()->tokenizer.lex();
691 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
692 {
693 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
694 {
696 }
697 tok=parser()->tokenizer.lex();
698 }
699
701}
702
703//---------------------------------------------------------------------------
704
707{
708 const Definition *compound = nullptr;
710 AUTO_TRACE("target='{}',context='{}'",target,context);
711 ASSERT(!target.isEmpty());
712 m_relPath = parser->context.relPath;
713 auto lang = parser->context.lang;
714 const SectionInfo *sec = SectionManager::instance().find(parser->context.prefix+target);
715 if (sec==nullptr && !parser->context.prefix.isEmpty())
716 {
717 sec = SectionManager::instance().find(target);
718 }
719
720 if (sec==nullptr && getLanguageFromFileName(target)==SrcLangExt::Markdown) // lookup as markdown file
721 {
723 }
724 if (sec) // ref to section or anchor
725 {
726 PageDef *pd = nullptr;
727 int secLevel = sec->type().level();
728 if (secLevel==SectionType::Page)
729 {
730 pd = Doxygen::pageLinkedMap->find(target);
731 }
732 m_text = sec->title();
733 if (m_text.isEmpty()) m_text = sec->label();
734
735 m_ref = sec->ref();
737 if (secLevel==SectionType::Anchor)
738 {
740 }
741 else if (secLevel==SectionType::Table)
742 {
744 }
745 else
746 {
748 }
749 m_isSubPage = pd && pd->hasParentPage();
750 if (secLevel!=SectionType::Page || m_isSubPage)
751 {
752 m_anchor = pd ? pd->getOutputFileBase() : sec->label();
753 }
754 m_sectionType = sec->type();
755 //printf("m_text=%s,m_ref=%s,m_file=%s,type=%d\n",
756 // qPrint(m_text),qPrint(m_ref),qPrint(m_file),m_refType);
757 AUTO_TRACE_EXIT("section");
758 return;
759 }
760 else if (Config_getBool(IMPLICIT_DIR_DOCS) && target.lower().endsWith("/readme.md"))
761 {
762 QCString dirTarget = target.left(target.length() - 9); // strip readme.md part
763 const auto &dd = Doxygen::dirLinkedMap->find(dirTarget);
764 if (dd)
765 {
766 m_text = target;
767 m_file = dd->getOutputFileBase();
768 AUTO_TRACE_EXIT("directory");
769 return;
770 }
771 }
772 else if (resolveLink(context,target,true,&compound,anchor,lang,parser->context.prefix))
773 {
774 bool isFile = compound ?
775 (compound->definitionType()==Definition::TypeFile ||
777 FALSE;
778 if (compound && lang==SrcLangExt::Markdown) lang = compound->getLanguage();
779 m_text = linkToText(lang,target,isFile);
781 if (compound && compound->isLinkable()) // ref to compound
782 {
783 if (anchor.isEmpty() && /* compound link */
784 compound->definitionType()==Definition::TypeGroup && /* is group */
785 !toGroupDef(compound)->groupTitle().isEmpty() /* with title */
786 )
787 {
788 m_text=(toGroupDef(compound))->groupTitle(); // use group's title as link
789 }
790 else if (compound->definitionType()==Definition::TypeMember &&
791 toMemberDef(compound)->isObjCMethod())
792 {
793 // Objective C Method
794 const MemberDef *member = toMemberDef(compound);
795 bool localLink = parser->context.memberDef ? member->getClassDef()==parser->context.memberDef->getClassDef() : FALSE;
796 m_text = member->objCMethodName(localLink,parser->context.inSeeBlock);
797 }
798 else if (Config_getBool(HIDE_SCOPE_NAMES))
799 {
801 }
802
803 m_file = compound->getOutputFileBase();
804 m_ref = compound->getReference();
805 //printf("isFile=%d compound=%s (%d)\n",isFile,qPrint(compound->name()),
806 // compound->definitionType());
807 AUTO_TRACE_EXIT("compound");
808 return;
809 }
810 else if (compound && compound->definitionType()==Definition::TypeFile &&
811 toFileDef(compound)->generateSourceFile()
812 ) // undocumented file that has source code we can link to
813 {
814 m_file = compound->getSourceFileBase();
815 m_ref = compound->getReference();
816 AUTO_TRACE_EXIT("source");
817 return;
818 }
819 else
820 {
821 AUTO_TRACE_EXIT("compound '{}' not linkable",compound?compound->name():"none");
822 }
823 }
824 m_text = target;
825 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve reference to '{}' for \\ref command",
826 target);
827}
828
830{
831 for (auto &&elem : elements)
832 {
833 emplace_back(std::move(elem));
834 }
835 elements.clear();
836}
837
838static void flattenParagraphs(DocNodeVariant *root,DocNodeList &children)
839{
840 DocNodeList newChildren;
841 for (auto &dn : children)
842 {
843 DocPara *para = std::get_if<DocPara>(&dn);
844 if (para)
845 {
846 //// move the children of the paragraph to the end of the newChildren list
847 newChildren.move_append(para->children());
848 }
849 }
850
851 // replace the children list by the newChildren list
852 children.clear();
853 children.move_append(newChildren);
854 // reparent the children
855 for (auto &cn : children)
856 {
857 setParent(&cn,root);
858 // we also need to set the parent for each child of cn, as cn's address may have changed.
859 auto opt_children = call_method_children(&cn);
860 if (opt_children)
861 {
862 for (auto &ccn : *opt_children)
863 {
864 setParent(&ccn,&cn);
865 }
866 }
867 }
868}
869
871{
872 AUTO_TRACE();
873 auto ns = AutoNodeStack(parser(),thisVariant());
874
875 Token tok = parser()->tokenizer.lex();
876 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
877 {
878 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
879 {
880 switch (tok.value())
881 {
882 case TokenRetval::TK_HTMLTAG:
883 break;
884 default:
886 break;
887 }
888 }
889 tok=parser()->tokenizer.lex();
890 }
891
892 if (children().empty() && !m_text.isEmpty())
893 {
894 QCString text = m_text;
895 if (parser()->context.insideHtmlLink)
896 {
897 // we already in a link/title only output anchor
898 text = m_anchor;
899 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
900 "Potential recursion while resolving \\ref command!");
901 }
903 parser()->pushContext();
905 parser()->popContext();
909 }
910
912}
913
914//---------------------------------------------------------------------------
915
917{
918 size_t numBibFiles = Config_getList(CITE_BIB_FILES).size();
919 //printf("DocCite::DocCite(target=%s)\n",qPrint(target));
920 ASSERT(!target.isEmpty());
921 m_relPath = parser->context.relPath;
923 const CiteInfo *cite = ct.find(target);
924 //printf("cite=%p text='%s' numBibFiles=%d\n",cite,cite?qPrint(cite->text):"<null>",numBibFiles);
925 m_option = opt;
927 if (numBibFiles>0 && cite && !cite->text().isEmpty()) // ref to citation
928 {
929 m_ref = "";
930 m_anchor = ct.anchorPrefix()+cite->label();
932 //printf("CITE ==> m_text=%s,m_ref=%s,m_file=%s,m_anchor=%s\n",
933 // qPrint(m_text),qPrint(m_ref),qPrint(m_file),qPrint(m_anchor));
934 return;
935 }
936 if (numBibFiles==0)
937 {
938 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"\\cite command found but no bib files specified via CITE_BIB_FILES!");
939 }
940 else if (cite==nullptr)
941 {
942 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve reference to '{}' for \\cite command",
943 target);
944 }
945 else
946 {
947 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"\\cite command to '{}' does not have an associated number",
948 target);
949 }
950}
951
953{
954 QCString txt;
955 auto opt = m_option;
957 const CiteInfo *citeInfo = ct.find(m_target);
958
959 if (!opt.noPar()) txt += "[";
960
961 if (citeInfo)
962 {
963 if (opt.isNumber()) txt += citeInfo->text();
964 else if (opt.isShortAuthor()) txt += citeInfo->shortAuthor();
965 else if (opt.isYear()) txt += citeInfo->year();
966 }
967
968 if (!opt.noPar()) txt += "]";
969 return txt;
970}
971
972
973//---------------------------------------------------------------------------
974
976{
977 const Definition *compound = nullptr;
979 m_refText = target;
980 m_relPath = parser->context.relPath;
981 if (!m_refText.isEmpty() && m_refText.at(0)=='#')
982 {
983 m_refText = m_refText.right(m_refText.length()-1);
984 }
985 if (resolveLink(parser->context.context,stripKnownExtensions(target),
986 parser->context.inSeeBlock,&compound,anchor,
987 parser->context.lang,parser->context.prefix))
988 {
989 m_anchor = anchor;
990 if (compound && compound->isLinkable())
991 {
992 m_file = compound->getOutputFileBase();
993 m_ref = compound->getReference();
994 }
995 else if (compound && compound->definitionType()==Definition::TypeFile &&
996 (toFileDef(compound))->generateSourceFile()
997 ) // undocumented file that has source code we can link to
998 {
999 m_file = compound->getSourceFileBase();
1000 m_ref = compound->getReference();
1001 }
1002 return;
1003 }
1004
1005 // bogus link target
1006 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve link to '{}' for \\link command",
1007 target);
1008}
1009
1010
1011QCString DocLink::parse(bool isJavaLink,bool isXmlLink)
1012{
1013 AUTO_TRACE();
1014 QCString result;
1015 auto ns = AutoNodeStack(parser(),thisVariant());
1016
1017 Token tok = parser()->tokenizer.lex();
1018 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1019 {
1020 if (!parser()->defaultHandleToken(thisVariant(),tok,children(),FALSE))
1021 {
1022 switch (tok.value())
1023 {
1024 case TokenRetval::TK_COMMAND_AT:
1025 // fall through
1026 case TokenRetval::TK_COMMAND_BS:
1027 switch (Mappers::cmdMapper->map(parser()->context.token->name))
1028 {
1030 if (isJavaLink)
1031 {
1032 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"'{{@link...' ended with '{:c}{}' command",
1033 tok.command_to_char(),parser()->context.token->name);
1034 }
1035 goto endlink;
1036 default:
1037 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' as part of a \\link",
1038 tok.command_to_char(),parser()->context.token->name);
1039 break;
1040 }
1041 break;
1042 case TokenRetval::TK_SYMBOL:
1043 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a \\link",
1044 parser()->context.token->name);
1045 break;
1046 case TokenRetval::TK_HTMLTAG:
1047 if (parser()->context.token->name!="see" || !isXmlLink)
1048 {
1049 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected xml/html command {} found as part of a \\link",
1050 parser()->context.token->name);
1051 }
1052 goto endlink;
1053 case TokenRetval::TK_LNKWORD:
1054 case TokenRetval::TK_WORD:
1055 if (isJavaLink) // special case to detect closing }
1056 {
1058 int p = 0;
1059 if (w=="}")
1060 {
1061 goto endlink;
1062 }
1063 else if ((p=w.find('}'))!=-1)
1064 {
1065 int l = static_cast<int>(w.length());
1067 if (p<l-1) // something left after the } (for instance a .)
1068 {
1069 result=w.right(l-p-1);
1070 }
1071 goto endlink;
1072 }
1073 }
1075 break;
1076 default:
1077 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",tok.to_string());
1078 break;
1079 }
1080 }
1081 tok = parser()->tokenizer.lex();
1082 }
1083 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1084 {
1085 warn_doc_error(parser()->context.fileName,
1086 parser()->tokenizer.getLineNr(),
1087 "Unexpected end of comment while inside link command");
1088 }
1089endlink:
1090
1091 if (children().empty()) // no link text
1092 {
1094 }
1095
1097 return result;
1098}
1099
1100
1101//---------------------------------------------------------------------------
1102
1109
1111{
1112 bool ok = false;
1114
1115 bool ambig = false;
1117 if (fd==nullptr && !p->name.endsWith(".dot")) // try with .dot extension as well
1118 {
1119 fd = findFileDef(Doxygen::dotFileNameLinkedMap,p->name+".dot",ambig);
1120 }
1121 if (fd)
1122 {
1123 p->file = fd->absFilePath();
1124 ok = true;
1125 if (ambig)
1126 {
1127 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dot file name '{}' is ambiguous.\n"
1128 "Possible candidates:\n{}",p->name,
1130 );
1131 }
1132 }
1133 else
1134 {
1135 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dot file '{}' is not found "
1136 "in any of the paths specified via DOTFILE_DIRS!",p->name);
1137 }
1138 return ok;
1139}
1140
1147
1149{
1150 bool ok = false;
1152
1153 bool ambig = false;
1155 if (fd==nullptr && !p->name.endsWith(".msc")) // try with .msc extension as well
1156 {
1157 fd = findFileDef(Doxygen::mscFileNameLinkedMap,p->name+".msc",ambig);
1158 }
1159 if (fd)
1160 {
1161 p->file = fd->absFilePath();
1162 ok = true;
1163 if (ambig)
1164 {
1165 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included msc file name '{}' is ambiguous.\n"
1166 "Possible candidates:\n{}",qPrint(p->name),
1168 );
1169 }
1170 }
1171 else
1172 {
1173 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included msc file '{}' is not found "
1174 "in any of the paths specified via MSCFILE_DIRS!",p->name);
1175 }
1176 return ok;
1177}
1178
1179//---------------------------------------------------------------------------
1180
1187
1189{
1190 bool ok = false;
1192
1193 bool ambig = false;
1195 if (fd==nullptr && !p->name.endsWith(".dia")) // try with .dia extension as well
1196 {
1197 fd = findFileDef(Doxygen::diaFileNameLinkedMap,p->name+".dia",ambig);
1198 }
1199 if (fd)
1200 {
1201 p->file = fd->absFilePath();
1202 ok = true;
1203 if (ambig)
1204 {
1205 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dia file name '{}' is ambiguous.\n"
1206 "Possible candidates:\n{}",p->name,
1208 );
1209 }
1210 }
1211 else
1212 {
1213 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dia file '{}' is not found "
1214 "in any of the paths specified via DIAFILE_DIRS!",p->name);
1215 }
1216 return ok;
1217}
1218//---------------------------------------------------------------------------
1219
1226
1228{
1229 bool ok = false;
1231
1232 bool ambig = false;
1234 if (fd==nullptr && !p->name.endsWith(".puml")) // try with .puml extension as well
1235 {
1236 fd = findFileDef(Doxygen::plantUmlFileNameLinkedMap,p->name+".puml",ambig);
1237 if (fd==nullptr && !p->name.endsWith(".pu")) // try with .pu extension as well
1238 {
1239 fd = findFileDef(Doxygen::plantUmlFileNameLinkedMap,p->name+".pu",ambig);
1240 }
1241 }
1242 if (fd)
1243 {
1244 p->file = fd->absFilePath();
1245 ok = true;
1246 if (ambig)
1247 {
1248 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included uml file name '{}' is ambiguous.\n"
1249 "Possible candidates:\n{}",p->name,
1251 );
1252 }
1253 }
1254 else
1255 {
1256 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included uml file '{}' is not found "
1257 "in any of the paths specified via PLANTUMLFILE_DIRS!",p->name);
1258 }
1259 return ok;
1260}
1261
1262//---------------------------------------------------------------------------
1263
1267
1269{
1270 AUTO_TRACE();
1271 auto ns = AutoNodeStack(parser(),thisVariant());
1272
1274 Token tok = parser()->tokenizer.lex();
1275 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1276 {
1277 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1278 {
1279 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"\\vhdlflow");
1280 }
1281 tok = parser()->tokenizer.lex();
1282 }
1283 parser()->tokenizer.lex();
1284
1287
1288 VhdlDocGen::createFlowChart(parser()->context.memberDef);
1289}
1290
1291
1292//---------------------------------------------------------------------------
1293
1295 Type t,const QCString &url, bool inlineImage) :
1296 DocCompoundNode(parser,parent), p(std::make_unique<Private>(attribs, name, t, parser->context.relPath, url, inlineImage))
1297{
1298}
1299
1301{
1302 QCString locName = p->url.isEmpty() ? p->name : p->url;
1303 int len = static_cast<int>(locName.length());
1304 int fnd = locName.find('?'); // ignore part from ? until end
1305 if (fnd==-1) fnd=len;
1306 return fnd>=4 && locName.mid(fnd-4,4)==".svg";
1307}
1308
1313
1314
1315//---------------------------------------------------------------------------
1316
1318{
1319 AUTO_TRACE();
1320 Token retval(TokenRetval::RetVal_OK);
1321 auto ns = AutoNodeStack(parser(),thisVariant());
1322
1323 Token tok = parser()->tokenizer.lex();
1324 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1325 {
1326 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1327 {
1328 switch (tok.value())
1329 {
1330 case TokenRetval::TK_HTMLTAG:
1331 {
1332 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1333 if (tagId==HtmlTagType::HTML_H1 && parser()->context.token->endTag) // found </h1> tag
1334 {
1335 if (m_level!=1)
1336 {
1337 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h1>",
1338 m_level);
1339 }
1340 goto endheader;
1341 }
1342 else if (tagId==HtmlTagType::HTML_H2 && parser()->context.token->endTag) // found </h2> tag
1343 {
1344 if (m_level!=2)
1345 {
1346 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h2>",
1347 m_level);
1348 }
1349 goto endheader;
1350 }
1351 else if (tagId==HtmlTagType::HTML_H3 && parser()->context.token->endTag) // found </h3> tag
1352 {
1353 if (m_level!=3)
1354 {
1355 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h3>",
1356 m_level);
1357 }
1358 goto endheader;
1359 }
1360 else if (tagId==HtmlTagType::HTML_H4 && parser()->context.token->endTag) // found </h4> tag
1361 {
1362 if (m_level!=4)
1363 {
1364 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h4>",
1365 m_level);
1366 }
1367 goto endheader;
1368 }
1369 else if (tagId==HtmlTagType::HTML_H5 && parser()->context.token->endTag) // found </h5> tag
1370 {
1371 if (m_level!=5)
1372 {
1373 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h5>",
1374 m_level);
1375 }
1376 goto endheader;
1377 }
1378 else if (tagId==HtmlTagType::HTML_H6 && parser()->context.token->endTag) // found </h6> tag
1379 {
1380 if (m_level!=6)
1381 {
1382 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h6>",
1383 m_level);
1384 }
1385 goto endheader;
1386 }
1387 else if (tagId==HtmlTagType::HTML_A)
1388 {
1389 if (!parser()->context.token->endTag)
1390 {
1391 parser()->handleAHref(thisVariant(),children(),parser()->context.token->attribs);
1392 }
1393 }
1394 else if (tagId==HtmlTagType::HTML_BR)
1395 {
1397 }
1398 else
1399 {
1400 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <h{:d}> context",
1401 parser()->context.token->endTag?"/":"",parser()->context.token->name,m_level);
1402 }
1403 }
1404 break;
1405 default:
1406 char tmp[20];
1407 qsnprintf(tmp,20,"<h%d> tag",m_level);
1409 }
1410 }
1411 tok = parser()->tokenizer.lex();
1412 }
1413 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1414 {
1415 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1416 " <h{:d}> tag",m_level);
1417 }
1418endheader:
1420 return retval;
1421}
1422//---------------------------------------------------------------------------
1423
1425{
1426 AUTO_TRACE();
1427 auto ns = AutoNodeStack(parser(),thisVariant());
1429 Token tok = parser()->tokenizer.lex();
1430 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1431 {
1433 // check of </summary>
1434 if (tok.value()==TokenRetval::TK_HTMLTAG &&
1435 (tagId=Mappers::htmlTagMapper->map(parser()->context.token->name))==HtmlTagType::XML_SUMMARY &&
1436 parser()->context.token->endTag
1437 )
1438 {
1439 break;
1440 }
1441 else if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1442 {
1443 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"summary section");
1444 }
1445 tok = parser()->tokenizer.lex();
1446 }
1448 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1449 {
1450 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1451 " <summary> tag");
1452 }
1453}
1454
1455//---------------------------------------------------------------------------
1456
1458{
1459 AUTO_TRACE();
1460 Token retval(TokenRetval::TK_NONE);
1461 auto ns = AutoNodeStack(parser(),thisVariant());
1462
1463 // parse one or more paragraphs
1464 bool isFirst=TRUE;
1465 DocPara *par=nullptr;
1466 do
1467 {
1469 par = children().get_last<DocPara>();
1470 if (isFirst) { par->markFirst(); isFirst=FALSE; }
1471 retval=par->parse();
1472 }
1473 while (retval.is(TokenRetval::TK_NEWPARA));
1474 if (par) par->markLast();
1475
1476 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1477 {
1478 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <details> block");
1479 }
1480
1481 if (!summary())
1482 {
1483 HtmlAttribList summaryAttribs;
1485 DocHtmlSummary *summary = &std::get<DocHtmlSummary>(*m_summary);
1486 summary->children().append<DocWord>(parser(),thisVariant(),theTranslator->trDetails());
1487 }
1488 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1489 return retval.is(TokenRetval::RetVal_EndHtmlDetails) ? Token::make_RetVal_OK() : retval;
1490}
1491
1499
1500//---------------------------------------------------------------------------
1501
1503{
1504 AUTO_TRACE();
1505 Token retval(TokenRetval::RetVal_OK);
1506 auto ns = AutoNodeStack(parser(),thisVariant());
1507
1508 Token tok = parser()->tokenizer.lex();
1509 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1510 {
1511 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1512 {
1513 switch (tok.value())
1514 {
1515 case TokenRetval::TK_HTMLTAG:
1516 {
1517 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1518 if (tagId==HtmlTagType::HTML_A && parser()->context.token->endTag) // found </a> tag
1519 {
1520 goto endhref;
1521 }
1522 else if (tagId==HtmlTagType::HTML_BR)
1523 {
1525 }
1526 else
1527 {
1528 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <a href=...> context",
1529 parser()->context.token->endTag?"/":"",parser()->context.token->name);
1530 }
1531 }
1532 break;
1533 default:
1534 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"<a>..</a> block");
1535 break;
1536 }
1537 }
1538 tok = parser()->tokenizer.lex();
1539 }
1540 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1541 {
1542 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1543 " <a href=...> tag");
1544 }
1545endhref:
1547 return retval;
1548}
1549
1550//---------------------------------------------------------------------------
1551
1553{
1554 AUTO_TRACE();
1555 Token retval(TokenRetval::RetVal_OK);
1556 auto ns = AutoNodeStack(parser(),thisVariant());
1557
1558 // first parse any number of paragraphs
1559 bool isFirst=TRUE;
1560 DocPara *lastPar=nullptr;
1561 do
1562 {
1564 DocPara *par = children().get_last<DocPara>();
1565 if (isFirst) { par->markFirst(); isFirst=FALSE; }
1566 retval=par->parse();
1567 if (!par->isEmpty())
1568 {
1569 if (lastPar) lastPar->markLast(FALSE);
1570 lastPar=par;
1571 }
1572 else
1573 {
1574 children().pop_back();
1575 }
1576 if (retval.is(TokenRetval::TK_LISTITEM))
1577 {
1578 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
1579 }
1580 } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF,
1581 TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection,
1582 TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph,
1583 TokenRetval::RetVal_EndInternal));
1584 if (lastPar) lastPar->markLast();
1585
1586 // then parse any number of level-n sections
1587 while ((level==1 && retval.is(TokenRetval::RetVal_Section)) ||
1588 (level==2 && retval.is(TokenRetval::RetVal_Subsection)) ||
1589 (level==3 && retval.is(TokenRetval::RetVal_Subsubsection)) ||
1590 (level==4 && retval.is(TokenRetval::RetVal_Paragraph)) ||
1591 (level==5 && retval.is(TokenRetval::RetVal_SubParagraph)) ||
1592 (level==6 && retval.is(TokenRetval::RetVal_SubSubParagraph))
1593 )
1594 {
1596 level,
1598 retval = children().get_last<DocSection>()->parse();
1599 }
1600
1601 if (retval.is(TokenRetval::RetVal_Internal))
1602 {
1603 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"\\internal command found inside internal section");
1604 }
1605
1606 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1607 return retval;
1608}
1609
1610//---------------------------------------------------------------------------
1611
1613{
1614 AUTO_TRACE();
1615 Token retval(TokenRetval::RetVal_OK);
1616 auto ns = AutoNodeStack(parser(),thisVariant());
1617 Token tok=parser()->tokenizer.lex();
1618 if (!tok.is(TokenRetval::TK_WHITESPACE))
1619 {
1620 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\addindex command");
1621 goto endindexentry;
1622 }
1624 m_entry="";
1625 tok = parser()->tokenizer.lex();
1626 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1627 {
1628 switch (tok.value())
1629 {
1630 case TokenRetval::TK_WHITESPACE:
1631 m_entry+=" ";
1632 break;
1633 case TokenRetval::TK_WORD:
1634 case TokenRetval::TK_LNKWORD:
1636 break;
1637 case TokenRetval::TK_SYMBOL:
1638 {
1640 switch (s)
1641 {
1642 case HtmlEntityMapper::Sym_BSlash: m_entry+='\\'; break;
1643 case HtmlEntityMapper::Sym_At: m_entry+='@'; break;
1644 case HtmlEntityMapper::Sym_Less: m_entry+='<'; break;
1645 case HtmlEntityMapper::Sym_Greater: m_entry+='>'; break;
1646 case HtmlEntityMapper::Sym_Amp: m_entry+='&'; break;
1647 case HtmlEntityMapper::Sym_Dollar: m_entry+='$'; break;
1648 case HtmlEntityMapper::Sym_Hash: m_entry+='#'; break;
1649 case HtmlEntityMapper::Sym_Percent: m_entry+='%'; break;
1650 case HtmlEntityMapper::Sym_apos: m_entry+='\''; break;
1651 case HtmlEntityMapper::Sym_Quot: m_entry+='"'; break;
1652 case HtmlEntityMapper::Sym_lsquo: m_entry+='`'; break;
1653 case HtmlEntityMapper::Sym_rsquo: m_entry+='\''; break;
1654 case HtmlEntityMapper::Sym_ldquo: m_entry+="``"; break;
1655 case HtmlEntityMapper::Sym_rdquo: m_entry+="''"; break;
1656 case HtmlEntityMapper::Sym_ndash: m_entry+="--"; break;
1657 case HtmlEntityMapper::Sym_mdash: m_entry+="---"; break;
1658 default:
1659 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected symbol '{}' found as argument of \\addindex",parser()->context.token->name);
1660 break;
1661 }
1662 }
1663 break;
1664 case TokenRetval::TK_COMMAND_AT:
1665 // fall through
1666 case TokenRetval::TK_COMMAND_BS:
1667 switch (Mappers::cmdMapper->map(parser()->context.token->name))
1668 {
1669 case CommandType::CMD_BSLASH: m_entry+='\\'; break;
1670 case CommandType::CMD_AT: m_entry+='@'; break;
1671 case CommandType::CMD_LESS: m_entry+='<'; break;
1672 case CommandType::CMD_GREATER: m_entry+='>'; break;
1673 case CommandType::CMD_AMP: m_entry+='&'; break;
1674 case CommandType::CMD_DOLLAR: m_entry+='$'; break;
1675 case CommandType::CMD_HASH: m_entry+='#'; break;
1676 case CommandType::CMD_DCOLON: m_entry+="::"; break;
1677 case CommandType::CMD_PERCENT: m_entry+='%'; break;
1678 case CommandType::CMD_NDASH: m_entry+="--"; break;
1679 case CommandType::CMD_MDASH: m_entry+="---"; break;
1680 case CommandType::CMD_QUOTE: m_entry+='"'; break;
1681 case CommandType::CMD_PUNT: m_entry+='.'; break;
1682 case CommandType::CMD_PLUS: m_entry+='+'; break;
1683 case CommandType::CMD_MINUS: m_entry+='-'; break;
1684 case CommandType::CMD_EQUAL: m_entry+='='; break;
1685 case CommandType::CMD_EXCLAMATION: m_entry+='!'; break;
1686 case CommandType::CMD_QUESTION: m_entry+='?'; break;
1687 default:
1688 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command {} found as argument of \\addindex",
1689 parser()->context.token->name);
1690 break;
1691 }
1692 break;
1693 default:
1694 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",
1695 tok.to_string());
1696 break;
1697 }
1698 tok = parser()->tokenizer.lex();
1699 }
1701 m_entry = m_entry.stripWhiteSpace();
1702endindexentry:
1703 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1704 return retval;
1705}
1706
1707//---------------------------------------------------------------------------
1708
1711{
1713 for (const auto &opt : attribs)
1714 {
1715 if (opt.name=="id" && !opt.value.isEmpty()) // interpret id attribute as an anchor
1716 {
1717 const SectionInfo *sec = SectionManager::instance().find(opt.value);
1718 if (sec)
1719 {
1720 //printf("Found anchor %s\n",qPrint(id));
1721 m_file = sec->fileName();
1722 m_anchor = sec->label();
1724 }
1725 else
1726 {
1727 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid caption id '{}'",opt.value);
1728 }
1729 }
1730 else // copy attribute
1731 {
1732 m_attribs.push_back(opt);
1733 }
1734 }
1735}
1736
1738{
1739 AUTO_TRACE();
1740 Token retval = Token::make_TK_NONE();
1741 auto ns = AutoNodeStack(parser(),thisVariant());
1742 Token tok = parser()->tokenizer.lex();
1743 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1744 {
1745 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1746 {
1747 switch (tok.value())
1748 {
1749 case TokenRetval::TK_HTMLTAG:
1750 {
1751 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1752 if (tagId==HtmlTagType::HTML_CAPTION && parser()->context.token->endTag) // found </caption> tag
1753 {
1754 retval = Token::make_RetVal_OK();
1755 goto endcaption;
1756 }
1757 else
1758 {
1759 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <caption> context",
1760 parser()->context.token->endTag?"/":"",parser()->context.token->name);
1761 }
1762 }
1763 break;
1764 default:
1765 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"<caption> tag");
1766 break;
1767 }
1768 }
1769 tok = parser()->tokenizer.lex();
1770 }
1771 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1772 {
1773 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1774 " <caption> tag");
1775 }
1776endcaption:
1778 return retval;
1779}
1780
1781//---------------------------------------------------------------------------
1782
1784{
1785 AUTO_TRACE();
1786 Token retval = Token::make_RetVal_OK();
1787 auto ns = AutoNodeStack(parser(),thisVariant());
1788
1789 // parse one or more paragraphs
1790 bool isFirst=TRUE;
1791 DocPara *par=nullptr;
1792 do
1793 {
1795 par = children().get_last<DocPara>();
1796 if (isFirst) { par->markFirst(); isFirst=FALSE; }
1797 retval=par->parse();
1798 if (retval.is(TokenRetval::TK_HTMLTAG))
1799 {
1800 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1801 if (tagId==HtmlTagType::HTML_TD && parser()->context.token->endTag) // found </td> tag
1802 {
1803 retval = Token::make_TK_NEWPARA(); // ignore the tag
1804 }
1805 else if (tagId==HtmlTagType::HTML_TH && parser()->context.token->endTag) // found </th> tag
1806 {
1807 retval = Token::make_TK_NEWPARA(); // ignore the tag
1808 }
1809 }
1810 }
1811 while (retval.is_any_of(TokenRetval::TK_NEWPARA,TokenRetval::RetVal_EndParBlock));
1812 if (par) par->markLast();
1813
1814 return retval;
1815}
1816
1818{
1819 AUTO_TRACE();
1820 Token retval = Token::make_RetVal_OK();
1821 auto ns = AutoNodeStack(parser(),thisVariant());
1822
1823 // parse one or more paragraphs
1824 bool isFirst=TRUE;
1825 DocPara *par=nullptr;
1826 do
1827 {
1829 par = children().get_last<DocPara>();
1830 if (isFirst) { par->markFirst(); isFirst=FALSE; }
1831 retval=par->parse();
1832 if (retval.is(TokenRetval::TK_HTMLTAG))
1833 {
1834 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1835 if (tagId==HtmlTagType::XML_ITEM && parser()->context.token->endTag) // found </item> tag
1836 {
1837 retval = Token::make_TK_NEWPARA(); // ignore the tag
1838 }
1839 else if (tagId==HtmlTagType::XML_DESCRIPTION && parser()->context.token->endTag) // found </description> tag
1840 {
1841 retval = Token::make_TK_NEWPARA(); // ignore the tag
1842 }
1843 }
1844 }
1845 while (retval.is(TokenRetval::TK_NEWPARA));
1846 if (par) par->markLast();
1847
1848 return retval;
1849}
1850
1851uint32_t DocHtmlCell::rowSpan() const
1852{
1853 for (const auto &attr : attribs())
1854 {
1855 if (attr.name.lower()=="rowspan")
1856 {
1857 return attr.value.toUInt();
1858 }
1859 }
1860 return 0;
1861}
1862
1863uint32_t DocHtmlCell::colSpan() const
1864{
1865 for (const auto &attr : attribs())
1866 {
1867 if (attr.name.lower()=="colspan")
1868 {
1869 return std::max(1u,attr.value.toUInt());
1870 }
1871 }
1872 return 1;
1873}
1874
1876{
1877 for (const auto &attr : attribs())
1878 {
1879 QCString attrName = attr.name.lower();
1880 QCString attrValue = attr.value.lower();
1881 if (attrName=="align")
1882 {
1883 if (attrValue=="center")
1884 return Center;
1885 else if (attrValue=="right")
1886 return Right;
1887 else return Left;
1888 }
1889 else if (attrName=="class" && attrValue.startsWith("markdowntable"))
1890 {
1891 if (attrValue=="markdowntableheadcenter")
1892 return Center;
1893 else if (attrValue=="markdowntableheadright")
1894 return Right;
1895 else if (attrValue=="markdowntableheadleft")
1896 return Left;
1897 else if (attrValue=="markdowntableheadnone")
1898 return Center;
1899 else if (attrValue=="markdowntablebodycenter")
1900 return Center;
1901 else if (attrValue=="markdowntablebodyright")
1902 return Right;
1903 else if (attrValue=="markdowntablebodyleft")
1904 return Left;
1905 else if (attrValue=="markdowntablebodynone")
1906 return Left;
1907 else return Left;
1908 }
1909 }
1910 return Left;
1911}
1912
1914{
1915 for (const auto &attr : attribs())
1916 {
1917 QCString attrName = attr.name.lower();
1918 QCString attrValue = attr.value.lower();
1919 if (attrName=="valign")
1920 {
1921 if (attrValue=="top")
1922 return Top;
1923 else if (attrValue=="bottom")
1924 return Bottom;
1925 else if (attrValue=="middle")
1926 return Middle;
1927 else return Middle;
1928 }
1929 }
1930 return Middle;
1931}
1932
1933//---------------------------------------------------------------------------
1934
1936{ // a row is a table heading if all cells are marked as such
1937 bool heading=TRUE;
1938 for (const auto &n : children())
1939 {
1940 const DocHtmlCell *cell = std::get_if<DocHtmlCell>(&n);
1941 if (cell && !cell->isHeading())
1942 {
1943 heading = FALSE;
1944 break;
1945 }
1946 }
1947 return !children().empty() && heading;
1948}
1949
1951{
1952 // get next token
1953 Token tok=parser->tokenizer.lex();
1954 // skip whitespace and tbody, thead and tfoot tags
1955 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA,TokenRetval::TK_HTMLTAG))
1956 {
1957 if (tok.is(TokenRetval::TK_HTMLTAG))
1958 {
1960 // skip over tbody, thead, tfoot tags
1961 if (tagId==HtmlTagType::HTML_TBODY ||
1962 tagId==HtmlTagType::HTML_THEAD ||
1964 {
1965 tok=parser->tokenizer.lex();
1966 }
1967 else
1968 {
1969 break;
1970 }
1971 }
1972 else
1973 {
1974 tok=parser->tokenizer.lex();
1975 }
1976 }
1977 return tok;
1978}
1979
1980
1982{
1983 AUTO_TRACE();
1984 Token retval = Token::make_RetVal_OK();
1985 auto ns = AutoNodeStack(parser(),thisVariant());
1986
1987 bool isHeading=FALSE;
1988 bool isFirst=TRUE;
1989 DocHtmlCell *cell=nullptr;
1990
1992 // should find a html tag now
1993 if (tok.is(TokenRetval::TK_HTMLTAG))
1994 {
1995 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1996 if (tagId==HtmlTagType::HTML_TD && !parser()->context.token->endTag) // found <td> tag
1997 {
1998 }
1999 else if (tagId==HtmlTagType::HTML_TH && !parser()->context.token->endTag) // found <th> tag
2000 {
2002 }
2003 else // found some other tag
2004 {
2005 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but "
2006 "found <{}{}> instead!",parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2007 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2008 goto endrow;
2009 }
2010 }
2011 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2012 {
2013 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2014 " for a html description title");
2015 goto endrow;
2016 }
2017 else // token other than html token
2018 {
2019 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but found {} token instead!",
2020 tok.to_string());
2021 goto endrow;
2022 }
2023
2024 // parse one or more cells
2025 do
2026 {
2029 isHeading);
2030 cell = children().get_last<DocHtmlCell>();
2031 cell->markFirst(isFirst);
2032 isFirst=FALSE;
2033 retval=cell->parse();
2034 isHeading = retval.is(TokenRetval::RetVal_TableHCell);
2035 //printf("DocHtmlRow:retval=%s\n",retval.to_string());
2036 if (retval.is(TokenRetval::RetVal_EndTableCell))
2037 {
2038 // get next token
2039 retval = skipSpacesForTable(parser());
2040 //printf("DocHtmlRow:retval= next=%s name=%s endTag=%d\n",retval.to_string(),qPrint(parser()->context.token->name),parser()->context.token->endTag);
2041 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2042 if (tok.is(TokenRetval::TK_HTMLTAG))
2043 {
2044 if ((tagId==HtmlTagType::HTML_TD || tagId==HtmlTagType::HTML_TH) &&
2045 !parser()->context.token->endTag) // found new <td> or <td> tag
2046 {
2047 retval = Token::make_RetVal_TableCell();
2049 }
2050 else if (tagId==HtmlTagType::HTML_TR)
2051 {
2052 if (parser()->context.token->endTag) // found </tr> tag
2053 {
2054 retval = Token::make_RetVal_EndTableRow();
2055 }
2056 else // found <tr> tag
2057 {
2058 retval = Token::make_RetVal_TableRow();
2059 }
2060 }
2061 else if (tagId==HtmlTagType::HTML_TABLE && parser()->context.token->endTag) // found </table>
2062 {
2063 retval = Token::make_RetVal_EndTable();
2064 }
2065 else // found some other tag
2066 {
2067 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td>, <th> or <tr> tag but "
2068 "found <{}{}> instead!",parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2069 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2070 goto endrow;
2071 }
2072 }
2073 else // token other than html token
2074 {
2075 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td>, <th> or <tr> tag but found {} token instead!",
2076 tok.to_string());
2077 goto endrow;
2078 }
2079 }
2080 }
2081 while (retval.is_any_of(TokenRetval::RetVal_TableCell,TokenRetval::RetVal_TableHCell));
2082 cell->markLast(TRUE);
2083
2084endrow:
2085 return retval;
2086}
2087
2089{
2090 AUTO_TRACE();
2091 Token retval = Token::make_RetVal_OK();
2092 auto ns = AutoNodeStack(parser(),thisVariant());
2093
2094 bool isFirst=TRUE;
2095 DocHtmlCell *cell=nullptr;
2096
2097 // get next token
2098 Token tok=parser()->tokenizer.lex();
2099 // skip whitespace
2100 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2101 // should find a html tag now
2102 if (tok.is(TokenRetval::TK_HTMLTAG))
2103 {
2104 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2105 if (tagId==HtmlTagType::XML_TERM && !parser()->context.token->endTag) // found <term> tag
2106 {
2107 }
2108 else if (tagId==HtmlTagType::XML_DESCRIPTION && !parser()->context.token->endTag) // found <description> tag
2109 {
2110 }
2111 else // found some other tag
2112 {
2113 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <term> or <description> tag but "
2114 "found <{}> instead!",parser()->context.token->name);
2115 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2116 goto endrow;
2117 }
2118 }
2119 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2120 {
2121 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2122 " for a html description title");
2123 goto endrow;
2124 }
2125 else // token other than html token
2126 {
2127 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but found {} token instead!",
2128 tok.to_string());
2129 goto endrow;
2130 }
2131
2132 do
2133 {
2135 cell = children().get_last<DocHtmlCell>();
2136 cell->markFirst(isFirst);
2137 isFirst=FALSE;
2138 retval=cell->parseXml();
2139 }
2140 while (retval.is_any_of(TokenRetval::RetVal_TableCell,TokenRetval::RetVal_TableHCell));
2141 cell->markLast(TRUE);
2142
2143endrow:
2144 return retval;
2145}
2146
2147//---------------------------------------------------------------------------
2148
2150{
2151 return m_caption!=nullptr;
2152}
2153
2155{
2156 return m_caption.get();
2157}
2158
2160{
2161 if (!children().empty() && std::holds_alternative<DocHtmlRow>(children().front()))
2162 {
2163 return &children().front();
2164 }
2165 return nullptr;
2166}
2167
2169{
2170 AUTO_TRACE();
2171 Token retval = Token::make_RetVal_OK();
2172 auto ns = AutoNodeStack(parser(),thisVariant());
2173
2174getrow:
2175 // skip whitespace and tbody, thead and tfoot tags
2177 // should find a html tag now
2178 if (tok.is(TokenRetval::TK_HTMLTAG))
2179 {
2180 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2181 if (tagId==HtmlTagType::HTML_TR && !parser()->context.token->endTag) // found <tr> tag
2182 {
2183 // no caption, just rows
2184 retval = Token::make_RetVal_TableRow();
2185 }
2186 else if (tagId==HtmlTagType::HTML_CAPTION && !parser()->context.token->endTag) // found <caption> tag
2187 {
2188 if (m_caption)
2189 {
2190 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"table already has a caption, found another one");
2191 }
2192 else
2193 {
2194 m_caption = createDocNode<DocHtmlCaption>(parser(),thisVariant(),parser()->context.token->attribs);
2195 retval=std::get<DocHtmlCaption>(*m_caption).parse();
2196
2197 if (retval.is(TokenRetval::RetVal_OK)) // caption was parsed ok
2198 {
2199 goto getrow;
2200 }
2201 }
2202 }
2203 else // found wrong token
2204 {
2205 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or <caption> tag but "
2206 "found <{}{}> instead!", parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2207 }
2208 }
2209 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2210 {
2211 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2212 " for a <tr> or <caption> tag");
2213 }
2214 else // token other than html token
2215 {
2216 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> tag but found {} token instead!",
2217 tok.to_string());
2218 }
2219
2220 // parse one or more rows
2221 while (retval.is(TokenRetval::RetVal_TableRow))
2222 {
2224 retval = children().get_last<DocHtmlRow>()->parse();
2225 //printf("DocHtmlTable::retval=%s\n",retval.to_string());
2226 if (retval.is(TokenRetval::RetVal_EndTableRow))
2227 {
2228 // get next token
2229 retval = skipSpacesForTable(parser());
2230 //printf("DocHtmlTable::retval= next=%s name=%s endTag=%d\n",retval.to_string(),qPrint(parser()->context.token->name),parser()->context.token->endTag);
2231 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2232 if (tagId==HtmlTagType::HTML_TR && !parser()->context.token->endTag)
2233 {
2234 retval = Token::make_RetVal_TableRow();
2235 }
2236 else if (tagId==HtmlTagType::HTML_TABLE && parser()->context.token->endTag)
2237 {
2238 retval = Token::make_RetVal_EndTable();
2239 }
2240 else // found some other tag
2241 {
2242 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or </table> tag but "
2243 "found token {} instead!",retval.to_string());
2244 retval=Token::make_RetVal_OK();
2245 break;
2246 }
2247 }
2248 }
2249
2251
2252 return retval.is(TokenRetval::RetVal_EndTable) ? Token::make_RetVal_OK() : retval;
2253}
2254
2256{
2257 AUTO_TRACE();
2258 Token retval = Token::make_RetVal_OK();
2259 auto ns = AutoNodeStack(parser(),thisVariant());
2260
2261 // get next token
2262 Token tok=parser()->tokenizer.lex();
2263 // skip whitespace
2264 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2265 // should find a html tag now
2267 bool isHeader=FALSE;
2268 if (tok.is(TokenRetval::TK_HTMLTAG))
2269 {
2270 tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2271 if (tagId==HtmlTagType::XML_ITEM && !parser()->context.token->endTag) // found <item> tag
2272 {
2273 retval = Token::make_RetVal_TableRow();
2274 }
2275 if (tagId==HtmlTagType::XML_LISTHEADER && !parser()->context.token->endTag) // found <listheader> tag
2276 {
2277 retval = Token::make_RetVal_TableRow();
2278 isHeader=TRUE;
2279 }
2280 }
2281
2282 // parse one or more rows
2283 while (retval.is(TokenRetval::RetVal_TableRow))
2284 {
2287 retval=tr->parseXml(isHeader);
2288 isHeader=FALSE;
2289 }
2290
2292
2293 tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2294 return tagId==HtmlTagType::XML_LIST && parser()->context.token->endTag ? Token::make_RetVal_OK() : retval;
2295}
2296
2297/** Helper class to compute the grid for an HTML style table */
2299{
2300 ActiveRowSpan(uint32_t rows,uint32_t col) : rowsLeft(rows), column(col) {}
2301 uint32_t rowsLeft;
2302 uint32_t column;
2303};
2304
2305/** List of ActiveRowSpan classes. */
2306typedef std::vector<ActiveRowSpan> RowSpanList;
2307
2308/** determines the location of all cells in a grid, resolving row and
2309 column spans. For each the total number of visible cells is computed,
2310 and the total number of visible columns over all rows is stored.
2311 */
2313{
2314 //printf("computeTableGrid()\n");
2315 RowSpanList rowSpans;
2316 uint32_t maxCols=0;
2317 uint32_t rowIdx=1;
2318 for (auto &rowNode : children())
2319 {
2320 uint32_t colIdx=1;
2321 uint32_t cells=0;
2322 DocHtmlRow *row = std::get_if<DocHtmlRow>(&rowNode);
2323 if (row)
2324 {
2325 for (auto &cellNode : row->children())
2326 {
2327 DocHtmlCell *cell = std::get_if<DocHtmlCell>(&cellNode);
2328 if (cell)
2329 {
2330 uint32_t rs = cell->rowSpan();
2331 uint32_t cs = cell->colSpan();
2332
2333 for (size_t i=0;i<rowSpans.size();i++)
2334 {
2335 if (rowSpans[i].rowsLeft>0 &&
2336 rowSpans[i].column==colIdx)
2337 {
2338 colIdx=rowSpans[i].column+1;
2339 cells++;
2340 }
2341 }
2342 if (rs>0) rowSpans.emplace_back(rs,colIdx);
2343 //printf("found cell at (%d,%d)\n",rowIdx,colIdx);
2344 cell->setRowIndex(rowIdx);
2345 cell->setColumnIndex(colIdx);
2346 colIdx+=cs;
2347 cells++;
2348 }
2349 }
2350 for (size_t i=0;i<rowSpans.size();i++)
2351 {
2352 if (rowSpans[i].rowsLeft>0) rowSpans[i].rowsLeft--;
2353 }
2354 row->setVisibleCells(cells);
2355 row->setRowIndex(rowIdx);
2356 rowIdx++;
2357 }
2358 if (colIdx-1>maxCols) maxCols=colIdx-1;
2359 }
2360 m_numCols = maxCols;
2361}
2362
2363//---------------------------------------------------------------------------
2364
2366{
2367 AUTO_TRACE();
2368 Token retval = Token::make_TK_NONE();
2369 auto ns = AutoNodeStack(parser(),thisVariant());
2370
2371 Token tok = parser()->tokenizer.lex();
2372 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
2373 {
2374 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
2375 {
2376 switch (tok.value())
2377 {
2378 case TokenRetval::TK_COMMAND_AT:
2379 // fall through
2380 case TokenRetval::TK_COMMAND_BS:
2381 {
2382 QCString cmdName=parser()->context.token->name;
2383 bool isJavaLink=FALSE;
2384 switch (Mappers::cmdMapper->map(cmdName))
2385 {
2387 {
2388 tok=parser()->tokenizer.lex();
2389 if (!tok.is(TokenRetval::TK_WHITESPACE))
2390 {
2391 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
2392 tok.command_to_char(),cmdName);
2393 }
2394 else
2395 {
2397 tok=parser()->tokenizer.lex(); // get the reference id
2398 if (!tok.is(TokenRetval::TK_WORD))
2399 {
2400 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}' command",
2401 tok.to_string(),tok.command_to_char(),cmdName);
2402 }
2403 else
2404 {
2406 children().get_last<DocRef>()->parse();
2407 }
2409 }
2410 }
2411 break;
2413 isJavaLink=TRUE;
2414 // fall through
2416 {
2417 tok=parser()->tokenizer.lex();
2418 if (!tok.is(TokenRetval::TK_WHITESPACE))
2419 {
2420 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
2421 cmdName);
2422 }
2423 else
2424 {
2426 tok=parser()->tokenizer.lex();
2427 if (!tok.is(TokenRetval::TK_WORD))
2428 {
2429 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\{} command",
2430 tok.to_string(),cmdName);
2431 }
2432 else
2433 {
2436 DocLink *lnk = children().get_last<DocLink>();
2437 QCString leftOver = lnk->parse(isJavaLink);
2438 if (!leftOver.isEmpty())
2439 {
2440 children().append<DocWord>(parser(),thisVariant(),leftOver);
2441 }
2442 }
2443 }
2444 }
2445
2446 break;
2447 default:
2448 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a <dt> tag",
2449 tok.command_to_char(),cmdName);
2450 }
2451 }
2452 break;
2453 case TokenRetval::TK_SYMBOL:
2454 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a <dt> tag",
2455 parser()->context.token->name);
2456 break;
2457 case TokenRetval::TK_HTMLTAG:
2458 {
2459 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2460 if (tagId==HtmlTagType::HTML_DD && !parser()->context.token->endTag) // found <dd> tag
2461 {
2462 retval = Token::make_RetVal_DescData();
2463 goto endtitle;
2464 }
2465 else if (tagId==HtmlTagType::HTML_DT && parser()->context.token->endTag)
2466 {
2467 // ignore </dt> tag.
2468 }
2469 else if (tagId==HtmlTagType::HTML_DT)
2470 {
2471 // missing <dt> tag.
2472 retval = Token::make_RetVal_DescTitle();
2473 goto endtitle;
2474 }
2475 else if (tagId==HtmlTagType::HTML_DL && parser()->context.token->endTag)
2476 {
2477 retval = Token::make_RetVal_EndDesc();
2478 goto endtitle;
2479 }
2480 else if (tagId==HtmlTagType::HTML_A)
2481 {
2482 if (!parser()->context.token->endTag)
2483 {
2484 parser()->handleAHref(thisVariant(),children(),parser()->context.token->attribs);
2485 }
2486 }
2487 else
2488 {
2489 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <dt> context",
2490 parser()->context.token->endTag?"/":"",parser()->context.token->name);
2491 }
2492 }
2493 break;
2494 default:
2495 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {} found as part of a <dt> tag",
2496 tok.to_string());
2497 break;
2498 }
2499 }
2500 tok = parser()->tokenizer.lex();
2501 }
2502 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2503 {
2504 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
2505 " <dt> tag");
2506 }
2507endtitle:
2509 return retval;
2510}
2511
2512//---------------------------------------------------------------------------
2513
2515{
2516 AUTO_TRACE();
2518 Token retval = Token::make_TK_NONE();
2519 auto ns = AutoNodeStack(parser(),thisVariant());
2520
2521 bool isFirst=TRUE;
2522 DocPara *par=nullptr;
2523 do
2524 {
2526 par = children().get_last<DocPara>();
2527 if (isFirst) { par->markFirst(); isFirst=FALSE; }
2528 retval=par->parse();
2529 }
2530 while (retval.is(TokenRetval::TK_NEWPARA));
2531 if (par) par->markLast();
2532
2533 return retval;
2534}
2535
2536//---------------------------------------------------------------------------
2537
2539{
2540 AUTO_TRACE();
2541 Token retval = Token::make_RetVal_OK();
2542 auto ns = AutoNodeStack(parser(),thisVariant());
2543
2544 // get next token
2545 Token tok=parser()->tokenizer.lex();
2546 // skip whitespace
2547 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2548 // should find a html tag now
2549 if (tok.is(TokenRetval::TK_HTMLTAG))
2550 {
2551 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2552 if (tagId==HtmlTagType::HTML_DT && !parser()->context.token->endTag) // found <dt> tag
2553 {
2554 // continue
2555 }
2556 else // found some other tag
2557 {
2558 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <dt> tag but "
2559 "found <{}> instead!",parser()->context.token->name);
2560 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2561 goto enddesclist;
2562 }
2563 }
2564 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2565 {
2566 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2567 " for a html description title");
2568 goto enddesclist;
2569 }
2570 else // token other than html token
2571 {
2572 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <dt> tag but found {} token instead!",
2573 tok.to_string());
2574 goto enddesclist;
2575 }
2576
2577 do
2578 {
2583 retval=dt->parse();
2584 if (retval.is(TokenRetval::RetVal_DescData))
2585 {
2586 retval=dd->parse();
2587 while (retval.is(TokenRetval::RetVal_DescData))
2588 {
2591 retval=dd->parse();
2592 }
2593 }
2594 else if (!retval.is(TokenRetval::RetVal_DescTitle))
2595 {
2596 // error
2597 break;
2598 }
2599 } while (retval.is(TokenRetval::RetVal_DescTitle));
2600
2601 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2602 {
2603 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <dl> block");
2604 }
2605
2606enddesclist:
2607
2608 return retval.is(TokenRetval::RetVal_EndDesc) ? Token::make_RetVal_OK() : retval;
2609}
2610
2611//---------------------------------------------------------------------------
2612
2614{
2615 AUTO_TRACE();
2616 Token retval = Token::make_TK_NONE();
2617 auto ns = AutoNodeStack(parser(),thisVariant());
2618
2619 // parse one or more paragraphs
2620 bool isFirst=TRUE;
2621 DocPara *par=nullptr;
2622 do
2623 {
2625 par = children().get_last<DocPara>();
2626 if (isFirst) { par->markFirst(); isFirst=FALSE; }
2627 retval=par->parse();
2628 }
2629 while (retval.is(TokenRetval::TK_NEWPARA));
2630 if (par) par->markLast();
2631
2632 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2633 return retval;
2634}
2635
2637{
2638 AUTO_TRACE();
2639 Token retval = Token::make_TK_NONE();
2640 auto ns = AutoNodeStack(parser(),thisVariant());
2641
2642 // parse one or more paragraphs
2643 bool isFirst=TRUE;
2644 DocPara *par=nullptr;
2645 do
2646 {
2648 par = children().get_last<DocPara>();
2649 if (isFirst) { par->markFirst(); isFirst=FALSE; }
2650 retval=par->parse();
2651 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
2652
2653 //printf("new item: retval=%x parser()->context.token->name=%s parser()->context.token->endTag=%d\n",
2654 // retval,qPrint(parser()->context.token->name),parser()->context.token->endTag);
2655 if (retval.is(TokenRetval::RetVal_ListItem))
2656 {
2657 break;
2658 }
2659 }
2660 while (!retval.is(TokenRetval::RetVal_CloseXml));
2661
2662 if (par) par->markLast();
2663
2664 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2665 return retval;
2666}
2667
2668//---------------------------------------------------------------------------
2669
2671{
2672 AUTO_TRACE();
2673 Token retval = Token::make_RetVal_OK();
2674 int num=1;
2675 auto ns = AutoNodeStack(parser(),thisVariant());
2676
2677 // get next token
2678 Token tok=parser()->tokenizer.lex();
2679 // skip whitespace and paragraph breaks
2680 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2681 // should find a html tag now
2682 if (tok.is(TokenRetval::TK_HTMLTAG))
2683 {
2684 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2685 if (tagId==HtmlTagType::HTML_LI && !parser()->context.token->endTag) // found <li> tag
2686 {
2687 // ok, we can go on.
2688 }
2689 else if (((m_type==Unordered && tagId==HtmlTagType::HTML_UL) ||
2691 ) && parser()->context.token->endTag
2692 ) // found empty list
2693 {
2694 // add dummy item to obtain valid HTML
2696 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"empty list!");
2697 retval = Token::make_RetVal_EndList();
2698 goto endlist;
2699 }
2700 else // found some other tag
2701 {
2702 // add dummy item to obtain valid HTML
2704 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <li> tag but "
2705 "found <{}{}> instead!",parser()->context.token->endTag?"/":"",parser()->context.token->name);
2706 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2707 goto endlist;
2708 }
2709 }
2710 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2711 {
2712 // add dummy item to obtain valid HTML
2714 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2715 " for a html list item");
2716 goto endlist;
2717 }
2718 else // token other than html token
2719 {
2720 // add dummy item to obtain valid HTML
2722 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <li> tag but found {} token instead!",
2723 tok.to_string());
2724 goto endlist;
2725 }
2726
2727 do
2728 {
2731 retval=li->parse();
2732 } while (retval.is(TokenRetval::RetVal_ListItem));
2733
2734 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2735 {
2736 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <{:c}l> block",
2737 m_type==Unordered ? 'u' : 'o');
2738 }
2739
2740endlist:
2741 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2742 return retval.is(TokenRetval::RetVal_EndList) ? Token::make_RetVal_OK() : retval;
2743}
2744
2746{
2747 AUTO_TRACE();
2748 Token retval = Token::make_RetVal_OK();
2749 int num=1;
2750 auto ns = AutoNodeStack(parser(),thisVariant());
2751
2752 // get next token
2753 Token tok=parser()->tokenizer.lex();
2754 // skip whitespace and paragraph breaks
2755 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2756 // should find a html tag now
2757 if (tok.is(TokenRetval::TK_HTMLTAG))
2758 {
2759 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2760 //printf("parser()->context.token->name=%s parser()->context.token->endTag=%d\n",qPrint(parser()->context.token->name),parser()->context.token->endTag);
2761 if (tagId==HtmlTagType::XML_ITEM && !parser()->context.token->endTag) // found <item> tag
2762 {
2763 // ok, we can go on.
2764 }
2765 else // found some other tag
2766 {
2767 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <item> tag but "
2768 "found <{}> instead!",parser()->context.token->name);
2769 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2770 goto endlist;
2771 }
2772 }
2773 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2774 {
2775 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2776 " for a html list item");
2777 goto endlist;
2778 }
2779 else // token other than html token
2780 {
2781 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <item> tag but found {} token instead!",
2782 tok.to_string());
2783 goto endlist;
2784 }
2785
2786 do
2787 {
2790 retval=li->parseXml();
2791 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
2792 //printf("retval=%x parser()->context.token->name=%s\n",retval,qPrint(parser()->context.token->name));
2793 } while (retval.is(TokenRetval::RetVal_ListItem));
2794
2795 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2796 {
2797 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <list type=\"{}\"> block",
2798 m_type==Unordered ? "bullet" : "number");
2799 }
2800
2801endlist:
2802 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2803 return (retval.is_any_of(TokenRetval::RetVal_EndList,TokenRetval::RetVal_CloseXml) || parser()->context.token->name=="list") ?
2804 Token::make_RetVal_OK() : retval;
2805}
2806
2807//--------------------------------------------------------------------------
2808
2810{
2811 AUTO_TRACE();
2812 Token retval = Token::make_TK_NONE();
2813 auto ns = AutoNodeStack(parser(),thisVariant());
2814
2815 // parse one or more paragraphs
2816 bool isFirst=TRUE;
2817 DocPara *par=nullptr;
2818 do
2819 {
2821 par = children().get_last<DocPara>();
2822 if (isFirst) { par->markFirst(); isFirst=FALSE; }
2823 retval=par->parse();
2824 }
2825 while (retval.is(TokenRetval::TK_NEWPARA));
2826 if (par) par->markLast();
2827
2828 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2829 {
2830 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <blockquote> block");
2831 }
2832
2833 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2834 return retval.is(TokenRetval::RetVal_EndBlockQuote) ? Token::make_RetVal_OK() : retval;
2835}
2836
2837//---------------------------------------------------------------------------
2838
2840{
2841 AUTO_TRACE();
2842 Token retval = Token::make_TK_NONE();
2843 auto ns = AutoNodeStack(parser(),thisVariant());
2844
2845 // parse one or more paragraphs
2846 bool isFirst=TRUE;
2847 DocPara *par=nullptr;
2848 do
2849 {
2851 par = children().get_last<DocPara>();
2852 if (isFirst) { par->markFirst(); isFirst=FALSE; }
2853 retval=par->parse();
2854 }
2855 while (retval.is(TokenRetval::TK_NEWPARA));
2856 if (par) par->markLast();
2857
2858 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2859 return retval.is(TokenRetval::RetVal_EndBlockQuote) ? Token::make_RetVal_OK() : retval;
2860}
2861
2862//---------------------------------------------------------------------------
2863
2868
2869
2871{
2872 auto ns = AutoNodeStack(parser(),thisVariant());
2874 DocPara *par = &std::get<DocPara>(*m_paragraph);
2875 Token rv=par->parse();
2876 par->markFirst();
2877 par->markLast();
2878 return rv;
2879}
2880
2881//--------------------------------------------------------------------------
2882
2884{
2885 auto ns = AutoNodeStack(parser(),thisVariant());
2886 Token rv = Token::make_TK_NONE();
2887 do
2888 {
2891 rv=li->parse();
2892 } while (rv.is(TokenRetval::RetVal_ListItem));
2893 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
2894}
2895
2896//--------------------------------------------------------------------------
2897
2902
2904{
2905 AUTO_TRACE();
2906 Token retval = Token::make_RetVal_OK();
2907 auto ns = AutoNodeStack(parser(),thisVariant());
2908
2909 // first parse any number of paragraphs
2910 bool isFirst=TRUE;
2911 DocPara *lastPar=nullptr;
2912 do
2913 {
2915 DocPara *par = children().get_last<DocPara>();
2916 if (isFirst) { par->markFirst(); isFirst=FALSE; }
2917 retval=par->parse();
2918 if (!par->isEmpty())
2919 {
2920 if (lastPar) lastPar->markLast(FALSE);
2921 lastPar=par;
2922 }
2923 else
2924 {
2925 children().pop_back();
2926 }
2927 // next paragraph should be more indented than the - marker to belong
2928 // to this item
2929 } while (retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->indent>m_indent);
2930 if (lastPar) lastPar->markLast();
2931
2932 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2933 return retval;
2934}
2935
2936//--------------------------------------------------------------------------
2937
2944
2946{
2947 AUTO_TRACE();
2948 Token retval = Token::make_RetVal_OK();
2949 int num=1;
2950 auto ns = AutoNodeStack(parser(),thisVariant());
2952 // first item or sub list => create new list
2953 do
2954 {
2955 switch (parser()->context.token->id)
2956 {
2957 case -1:
2958 break;
2959 case DocAutoList::Unchecked: // unchecked
2960 case DocAutoList::Checked_x: // checked with x
2961 case DocAutoList::Checked_X: // checked with X
2962 num = parser()->context.token->id;
2963 break;
2964 default: // explicitly numbered list
2965 num=parser()->context.token->id; // override num with real number given
2966 break;
2967 }
2968
2970 retval = children().get_last<DocAutoListItem>()->parse();
2971 //printf("DocAutoList::parse(): retval=0x%x parser()->context.token->indent=%d m_indent=%d "
2972 // "m_isEnumList=%d parser()->context.token->isEnumList=%d parser()->context.token->name=%s\n",
2973 // retval,parser()->context.token->indent,m_indent,m_isEnumList,parser()->context.token->isEnumList,
2974 // qPrint(parser()->context.token->name));
2975 //printf("num=%d parser()->context.token->id=%d\n",num,parser()->context.token->id);
2976 }
2977 while (retval.is(TokenRetval::TK_LISTITEM) && // new list item
2978 m_indent==parser()->context.token->indent && // at same indent level
2979 m_isEnumList==parser()->context.token->isEnumList && // of the same kind
2980 m_isCheckedList==parser()->context.token->isCheckedList && // of the same kind
2981 (parser()->context.token->id==-1 || parser()->context.token->id>=num) // increasing number (or no number or checked list)
2982 );
2983
2985 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2986 return retval;
2987}
2988
2989//--------------------------------------------------------------------------
2990
2992{
2993 AUTO_TRACE();
2994 auto ns = AutoNodeStack(parser(),thisVariant());
2996 Token tok = parser()->tokenizer.lex();
2997 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
2998 {
2999 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
3000 {
3001 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"title section");
3002 }
3003 tok = parser()->tokenizer.lex();
3004 }
3007}
3008
3010{
3012 parser()->pushContext(); // this will create a new parser->context.token
3014 parser()->popContext(); // this will restore the old parser->context.token
3018}
3019
3020//--------------------------------------------------------------------------
3021
3026
3028{
3029 return m_title && std::get<DocTitle>(*m_title).hasTitle();
3030}
3031
3032Token DocSimpleSect::parse(bool userTitle,bool needsSeparator)
3033{
3034 AUTO_TRACE();
3035 auto ns = AutoNodeStack(parser(),thisVariant());
3036
3037 // handle case for user defined title
3038 if (userTitle)
3039 {
3041 std::get_if<DocTitle>(m_title.get())->parse();
3042 }
3043
3044 // add new paragraph as child
3045 if (!children().empty() && std::holds_alternative<DocPara>(children().back()))
3046 {
3047 std::get<DocPara>(children().back()).markLast(FALSE);
3048 }
3049 bool markFirst = children().empty();
3050 if (needsSeparator)
3051 {
3053 }
3055 DocPara *par = children().get_last<DocPara>();
3056 if (markFirst)
3057 {
3058 par->markFirst();
3059 }
3060 par->markLast();
3061
3062 // parse the contents of the paragraph
3063 Token retval = par->parse();
3064
3065 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3066 return retval; // 0==EOF, TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, TokenRetval::TK_ENDLIST, TokenRetval::RetVal_SimpleSec
3067}
3068
3070{
3071 AUTO_TRACE();
3072 auto ns = AutoNodeStack(parser(),thisVariant());
3073
3075 DocTitle *title = &std::get<DocTitle>(*m_title);
3076 title->parseFromString(thisVariant(),parser()->context.token->name);
3077
3078 QCString text = parser()->context.token->text;
3079 parser()->pushContext(); // this will create a new parser->context.token
3081 parser()->popContext(); // this will restore the old parser->context.token
3082
3083 return Token::make_RetVal_OK();
3084}
3085
3087{
3088 AUTO_TRACE();
3089 auto ns = AutoNodeStack(parser(),thisVariant());
3090
3091 Token retval = Token::make_RetVal_OK();
3092 for (;;)
3093 {
3094 // add new paragraph as child
3095 if (!children().empty() && std::holds_alternative<DocPara>(children().back()))
3096 {
3097 std::get<DocPara>(children().back()).markLast(false);
3098 }
3099 bool markFirst = children().empty();
3101 DocPara *par = children().get_last<DocPara>();
3102 if (markFirst)
3103 {
3104 par->markFirst();
3105 }
3106 par->markLast();
3107
3108 // parse the contents of the paragraph
3109 retval = par->parse();
3110 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
3111 if (retval.is(TokenRetval::RetVal_CloseXml))
3112 {
3113 retval = Token::make_RetVal_OK();
3114 break;
3115 }
3116 }
3117
3118 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3119 return retval;
3120}
3121
3123{
3124 DocPara *p=nullptr;
3125 if (children().empty() || (p=std::get_if<DocPara>(&children().back()))==nullptr)
3126 {
3128 p = children().get_last<DocPara>();
3129 }
3130 else
3131 {
3132 // Comma-separate <seealso> links.
3133 p->injectToken(Token::make_TK_WORD(),",");
3134 p->injectToken(Token::make_TK_WHITESPACE()," ");
3135 }
3136
3138 p->injectToken(Token::make_TK_LNKWORD(),word);
3140}
3141
3143{
3144 switch (m_type)
3145 {
3146 case Unknown: break;
3147 case See: return "see";
3148 case Return: return "return";
3149 case Author: // fall through
3150 case Authors: return "author";
3151 case Version: return "version";
3152 case Since: return "since";
3153 case Date: return "date";
3154 case Note: return "note";
3155 case Warning: return "warning";
3156 case Pre: return "pre";
3157 case Post: return "post";
3158 case Copyright: return "copyright";
3159 case Invar: return "invariant";
3160 case Remark: return "remark";
3161 case Attention: return "attention";
3162 case Important: return "important";
3163 case User: return "user";
3164 case Rcs: return "rcs";
3165 }
3166 return "unknown";
3167}
3168
3169//--------------------------------------------------------------------------
3170
3172{
3173 AUTO_TRACE();
3174 Token retval = Token::make_RetVal_OK();
3175 auto ns = AutoNodeStack(parser(),thisVariant());
3176 DocPara *par=nullptr;
3177 QCString saveCmdName = cmdName;
3178
3179 Token tok=parser()->tokenizer.lex();
3180 if (!tok.is(TokenRetval::TK_WHITESPACE))
3181 {
3182 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3183 saveCmdName);
3184 retval = Token::make_RetVal_EndParBlock();
3185 goto endparamlist;
3186 }
3188 tok=parser()->tokenizer.lex();
3189 while (tok.is(TokenRetval::TK_WORD)) /* there is a parameter name */
3190 {
3192 {
3193 int typeSeparator = parser()->context.token->name.find('#'); // explicit type position
3194 if (typeSeparator!=-1)
3195 {
3196 parser()->handleParameterType(thisVariant(),m_paramTypes,parser()->context.token->name.left(typeSeparator));
3197 parser()->context.token->name = parser()->context.token->name.mid(typeSeparator+1);
3200 if (parent() && std::holds_alternative<DocParamSect>(*parent()))
3201 {
3202 std::get<DocParamSect>(*parent()).m_hasTypeSpecifier=true;
3203 }
3204 }
3205 else
3206 {
3209 }
3210 }
3211 else if (m_type==DocParamSect::RetVal)
3212 {
3215 }
3216 //m_params.append(parser()->context.token->name);
3218 tok=parser()->tokenizer.lex();
3219 }
3221 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
3222 {
3223 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3224 "argument of command {}",saveCmdName);
3225 retval = Token::make_RetVal_EndParBlock();
3226 goto endparamlist;
3227 }
3228 if (!tok.is(TokenRetval::TK_WHITESPACE)) /* premature end of comment block */
3229 {
3230 if (!tok.is(TokenRetval::TK_NEWPARA)) /* empty param description */
3231 {
3232 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} in comment block while parsing the "
3233 "argument of command {}",tok.to_string(),saveCmdName);
3234 }
3235 retval = Token::make_RetVal_EndParBlock();
3236 goto endparamlist;
3237 }
3238
3240 par = m_paragraphs.get_last<DocPara>();
3241 retval = par->parse();
3242 par->markFirst();
3243 par->markLast();
3244
3245endparamlist:
3246 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3247 return retval;
3248}
3249
3251{
3252 AUTO_TRACE();
3253 Token retval = Token::make_RetVal_OK();
3254 auto ns = AutoNodeStack(parser(),thisVariant());
3255
3256 parser()->context.token->name = paramName;
3258 {
3261 }
3262 else if (m_type==DocParamSect::RetVal)
3263 {
3266 }
3267
3269
3270 do
3271 {
3273 DocPara *par = m_paragraphs.get_last<DocPara>();
3274 retval = par->parse();
3275 if (par->isEmpty()) // avoid adding an empty paragraph for the whitespace
3276 // after </para> and before </param>
3277 {
3278 m_paragraphs.pop_back();
3279 break;
3280 }
3281 else // append the paragraph to the list
3282 {
3283 if (!m_paragraphs.empty())
3284 {
3285 m_paragraphs.get_last<DocPara>()->markLast(FALSE);
3286 }
3287 bool markFirst = m_paragraphs.empty();
3288 par = &std::get<DocPara>(m_paragraphs.back());
3289 if (markFirst)
3290 {
3291 par->markFirst();
3292 }
3293 par->markLast();
3294 }
3295
3296 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
3297
3298 } while (retval.is(TokenRetval::RetVal_CloseXml) &&
3299 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_PARAM &&
3300 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_TYPEPARAM &&
3301 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_EXCEPTION);
3302
3303 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) /* premature end of comment block */
3304 {
3305 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unterminated param or exception tag");
3306 }
3307 else
3308 {
3309 retval = Token::make_RetVal_OK();
3310 }
3311
3312 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3313 return retval;
3314}
3315
3316//--------------------------------------------------------------------------
3317
3318Token DocParamSect::parse(const QCString &cmdName,bool xmlContext, Direction d)
3319{
3320 AUTO_TRACE();
3321 Token retval = Token::make_RetVal_OK();
3322 auto ns = AutoNodeStack(parser(),thisVariant());
3323
3324 if (d!=Unspecified)
3325 {
3327 }
3328
3329 if (!children().empty() && std::holds_alternative<DocParamList>(children().back()))
3330 {
3331 DocParamList &lastPl = std::get<DocParamList>(children().back());
3332 lastPl.markLast(false);
3333 }
3334 bool markFirst = children().empty();
3337 if (markFirst)
3338 {
3339 pl->markFirst();
3340 }
3341 pl->markLast();
3342 if (xmlContext)
3343 {
3344 retval = pl->parseXml(cmdName);
3345 }
3346 else
3347 {
3348 retval = pl->parse(cmdName);
3349 }
3350 if (retval.is(TokenRetval::RetVal_EndParBlock))
3351 {
3352 retval = Token::make_RetVal_OK();
3353 }
3354
3355 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3356 return retval;
3357}
3358
3359//--------------------------------------------------------------------------
3360
3366
3368{
3369 AUTO_TRACE();
3370 DocSimpleSect *ss=nullptr;
3371 bool needsSeparator = FALSE;
3372 if (!children().empty() && // has previous element
3373 (ss=children().get_last<DocSimpleSect>()) && // was a simple sect
3374 ss->type()==t && // of same type
3375 t!=DocSimpleSect::User) // but not user defined
3376 {
3377 // append to previous section
3378 needsSeparator = TRUE;
3379 }
3380 else // start new section
3381 {
3384 }
3385 Token rv = Token::make_RetVal_OK();
3386 if (xmlContext)
3387 {
3388 return ss->parseXml();
3389 }
3390 else
3391 {
3392 rv = ss->parse(t==DocSimpleSect::User,needsSeparator);
3393 }
3394 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3395}
3396
3399 bool xmlContext=FALSE,
3400 int direction=DocParamSect::Unspecified)
3401{
3402 AUTO_TRACE();
3403 DocParamSect *ps = nullptr;
3404 if (!children().empty() && // previous element
3405 (ps=children().get_last<DocParamSect>()) && // was a param sect
3406 ps->type()==t) // of same type
3407 { // append to previous section ps
3408 }
3409 else // start new section
3410 {
3412 ps = children().get_last<DocParamSect>();
3413 }
3414 Token rv=ps->parse(cmdName,xmlContext,
3415 static_cast<DocParamSect::Direction>(direction));
3416 AUTO_TRACE_EXIT("retval={}",rv.to_string());
3417 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3418}
3419
3420void DocPara::handleCite(char cmdChar,const QCString &cmdName)
3421{
3422 AUTO_TRACE();
3423 QCString saveCmdName = cmdName;
3424 // get the argument of the cite command.
3425 Token tok=parser()->tokenizer.lex();
3426
3427 CiteInfoOption option;
3428 if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="{")
3429 {
3431 parser()->tokenizer.lex();
3432 StringVector optList=split(parser()->context.token->name.str(),",");
3433 for (auto const &opt : optList)
3434 {
3435 if (opt == "number")
3436 {
3437 if (!option.isUnknown())
3438 {
3439 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", saveCmdName, opt);
3440 }
3441 else
3442 {
3443 option = CiteInfoOption::makeNumber();
3444 }
3445 }
3446 else if (opt == "year")
3447 {
3448 if (!option.isUnknown())
3449 {
3450 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", saveCmdName, opt);
3451 }
3452 else
3453 {
3454 option = CiteInfoOption::makeYear();
3455 }
3456 }
3457 else if (opt == "shortauthor")
3458 {
3459 if (!option.isUnknown())
3460 {
3461 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Multiple options specified with \\{}, discarding '{}'", saveCmdName, opt);
3462 }
3463 else
3464 {
3466 }
3467 }
3468 else if (opt == "nopar")
3469 {
3470 option.setNoPar();
3471 }
3472 else if (opt == "nocite")
3473 {
3474 option.setNoCite();
3475 }
3476 else
3477 {
3478 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unknown option specified with \\{}, discarding '{}'", saveCmdName, opt);
3479 }
3480 }
3481
3482 if (option.isUnknown()) option.changeToNumber();
3483
3485 tok=parser()->tokenizer.lex();
3486 if (!tok.is(TokenRetval::TK_WHITESPACE))
3487 {
3488 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3489 saveCmdName);
3490 return;
3491 }
3492 }
3493 else if (!tok.is(TokenRetval::TK_WHITESPACE))
3494 {
3495 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3496 cmdChar,saveCmdName);
3497 return;
3498 }
3499 else
3500 {
3501 option = CiteInfoOption::makeNumber();
3502 }
3503
3505 tok=parser()->tokenizer.lex();
3506 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3507 {
3508 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"THE ONE unexpected end of comment block while parsing the "
3509 "argument of command '{:c}{}'",cmdChar,saveCmdName);
3510 return;
3511 }
3512 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
3513 {
3514 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3515 tok.to_string(),cmdChar,saveCmdName);
3516 return;
3517 }
3521
3523}
3524
3525void DocPara::handleEmoji(char cmdChar,const QCString &cmdName)
3526{
3527 AUTO_TRACE();
3528 // get the argument of the emoji command.
3529 Token tok=parser()->tokenizer.lex();
3530 if (!tok.is(TokenRetval::TK_WHITESPACE))
3531 {
3532 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3533 cmdChar,cmdName);
3534 return;
3535 }
3537 tok=parser()->tokenizer.lex();
3538 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3539 {
3540 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"no emoji name given or unexpected end of comment block while parsing the "
3541 "argument of command '{:c}{}'",cmdChar,cmdName);
3543 return;
3544 }
3545 else if (!tok.is(TokenRetval::TK_WORD))
3546 {
3547 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3548 tok.to_string(),cmdChar,cmdName);
3550 return;
3551 }
3554}
3555
3556void DocPara::handleDoxyConfig(char cmdChar,const QCString &cmdName)
3557{
3558 // get the argument of the cite command.
3559 Token tok=parser()->tokenizer.lex();
3560 if (!tok.is(TokenRetval::TK_WHITESPACE))
3561 {
3562 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3563 cmdChar,cmdName);
3564 return;
3565 }
3567 tok=parser()->tokenizer.lex();
3568 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3569 {
3570 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3571 "argument of command '{:c}{}'",cmdChar,cmdName);
3572 return;
3573 }
3574 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
3575 {
3576 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3577 tok.to_string(),cmdChar,cmdName);
3578 return;
3579 }
3580 ConfigOption * opt = ConfigImpl::instance()->get(parser()->context.token->name);
3581 if (opt)
3582 {
3583 QCString optionValue;
3584 switch (opt->kind())
3585 {
3587 optionValue = *(static_cast<ConfigBool*>(opt)->valueStringRef());
3588 break;
3590 optionValue = *(static_cast<ConfigString*>(opt)->valueRef());
3591 break;
3593 optionValue = *(static_cast<ConfigEnum*>(opt)->valueRef());
3594 break;
3596 optionValue = *(static_cast<ConfigInt*>(opt)->valueStringRef());
3597 break;
3599 {
3600 StringVector *lst = static_cast<ConfigList*>(opt)->valueRef();
3601 optionValue="";
3602 if (!lst->empty())
3603 {
3604 std::string lstFormat = theTranslator->trWriteList(static_cast<int>(lst->size())).str();
3605 static const reg::Ex marker(R"(@(\d+))");
3606 reg::Iterator it(lstFormat,marker);
3608 size_t index=0;
3609 // now replace all markers with the real text
3610 for ( ; it!=end ; ++it)
3611 {
3612 const auto &match = *it;
3613 size_t newIndex = match.position();
3614 size_t matchLen = match.length();
3615 optionValue += lstFormat.substr(index,newIndex-index);
3616 unsigned long entryIndex = std::stoul(match[1].str());
3617 if (entryIndex<(unsigned long)lst->size())
3618 {
3619 optionValue += lst->at(entryIndex);
3620 }
3621 index=newIndex+matchLen;
3622 }
3623 optionValue+=lstFormat.substr(index);
3624 }
3625 }
3626 break;
3628 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Obsolete setting for '{:c}{}': '{}'",
3629 cmdChar,cmdName,parser()->context.token->name);
3630 break;
3632 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),
3633 "Disabled setting (i.e. not supported in this doxygen executable) for '{:c}{}': '{}'",
3634 cmdChar,cmdName,parser()->context.token->name);
3635 break;
3637 // nothing to show here
3638 break;
3639 }
3640 if (!optionValue.isEmpty())
3641 {
3642 children().append<DocWord>(parser(),thisVariant(),optionValue);
3643 }
3644 }
3645 else
3646 {
3647 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Unknown option for '{:c}{}': '{}'",
3648 cmdChar,cmdName,parser()->context.token->name);
3650 }
3652}
3653
3655{
3656 AUTO_TRACE();
3657 Token retval=parser()->tokenizer.lex();
3658 ASSERT(retval.is(TokenRetval::TK_WHITESPACE));
3660 retval=parser()->tokenizer.lex();
3661 if (retval.is(TokenRetval::RetVal_OK))
3662 {
3666 if (!ref->parse())
3667 {
3668 children().pop_back();
3669 }
3670 }
3672 return retval;
3673}
3674
3675void DocPara::handleShowDate(char cmdChar,const QCString &cmdName)
3676{
3677 AUTO_TRACE();
3678 QCString fmt;
3679 QCString date;
3680 Token tok=parser()->tokenizer.lex();
3681 if (!tok.is(TokenRetval::TK_WHITESPACE))
3682 {
3683 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3684 cmdChar,cmdName);
3685 return;
3686 }
3688 tok = parser()->tokenizer.lex();
3689 if (!tok.is(TokenRetval::TK_WORD))
3690 {
3691 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <format> argument for command '{:c}{}'",
3692 cmdChar,cmdName);
3694 return;
3695 }
3696 fmt = parser()->context.token->name;
3697
3699 tok = parser()->tokenizer.lex();
3700
3701 QCString specDateRaw = tok.is(TokenRetval::TK_WORD) ? parser()->context.token->name : QCString();
3702 QCString specDate = specDateRaw.stripWhiteSpace();
3703 bool specDateOnlyWS = !specDateRaw.isEmpty() && specDate.isEmpty();
3704 if (!specDate.isEmpty() && !tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3705 {
3706 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}'",
3707 cmdChar,cmdName);
3709 return;
3710 }
3711
3712 std::tm dat{};
3713 int specFormat=0;
3714 QCString err = dateTimeFromString(specDate,dat,specFormat);
3715 if (!err.isEmpty())
3716 {
3717 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}': {}",
3718 cmdChar,cmdName,err);
3720 return;
3721 }
3722
3723 int usedFormat=0;
3724 QCString dateTimeStr = formatDateTime(fmt,dat,usedFormat);
3725
3726 // warn the user if the format contains markers that are not explicitly filled in
3727 for (int i=0;i<SF_NumBits;i++)
3728 {
3729 int bitMask = 1<<i;
3730 if ((usedFormat&bitMask) && !(specFormat&bitMask)) // a part was used in the format string but its value was not specified.
3731 {
3732 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.",
3733 cmdChar,cmdName,fmt,SF_bit2str(i),specDate,SF_bit2str(i));
3734 }
3735 }
3736
3737 children().append<DocWord>(parser(),thisVariant(),dateTimeStr);
3738 if (specDateOnlyWS) // specDate is only whitespace
3739 {
3741 }
3743}
3744
3745void DocPara::handleILine(char cmdChar,const QCString &cmdName)
3746{
3747 AUTO_TRACE();
3749 Token tok = parser()->tokenizer.lex();
3750 if (!tok.is(TokenRetval::TK_WORD))
3751 {
3752 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid argument for command '{:c}{}'",
3753 cmdChar,cmdName);
3754 return;
3755 }
3757}
3758
3759void DocPara::handleIFile(char cmdChar,const QCString &cmdName)
3760{
3761 AUTO_TRACE();
3762 Token tok=parser()->tokenizer.lex();
3763 if (!tok.is(TokenRetval::TK_WHITESPACE))
3764 {
3765 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3766 cmdChar,cmdName);
3767 return;
3768 }
3770 tok=parser()->tokenizer.lex();
3772 if (!tok.is(TokenRetval::TK_WORD))
3773 {
3774 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3775 tok.to_string(),cmdChar,cmdName);
3776 return;
3777 }
3780}
3781
3782
3784{
3785 AUTO_TRACE("cmdName={}",cmdName);
3786 QCString saveCmdName = cmdName;
3787 Token tok=parser()->tokenizer.lex();
3788 if (!tok.is(TokenRetval::TK_WHITESPACE))
3789 {
3790 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3791 saveCmdName);
3792 return;
3793 }
3795 tok=parser()->tokenizer.lex();
3797 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3798 {
3799 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3800 "argument of command {}", saveCmdName);
3801 return;
3802 }
3803 else if (!tok.is(TokenRetval::TK_WORD))
3804 {
3805 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3806 tok.to_string(),saveCmdName);
3807 return;
3808 }
3809 auto it1 = children().size()>=1 ? std::prev(children().end()) : children().end();
3810 auto it2 = children().size()>=2 ? std::prev(it1) : children().end();
3811 DocNodeVariant *n1 = it1!=children().end() ? &(*it1) : nullptr;
3812 DocNodeVariant *n2 = it2!=children().end() ? &(*it2) : nullptr;
3813 //TODO get from context the stripCodeComments()
3814 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
3818 stripCodeComments,
3821 );
3823 DocIncOperator *n1_docIncOp = std::get_if<DocIncOperator>(n1);
3824 DocWhiteSpace *n1_docWs = std::get_if<DocWhiteSpace >(n1);
3825 DocIncOperator *n2_docIncOp = std::get_if<DocIncOperator>(n2);
3826 bool isFirst = !n1 || // no last node
3827 (!n1_docIncOp && !n1_docWs) || // last node is not operator or whitespace
3828 (n1_docWs && n2 && !n2_docIncOp); // last node is not operator
3829 op->markFirst(isFirst);
3830 op->markLast(true);
3831 if (n1_docIncOp)
3832 {
3833 n1_docIncOp->markLast(false);
3834 }
3835 else if (n1_docWs && n2_docIncOp)
3836 {
3837 n2_docIncOp->markLast(false);
3838 }
3839 op->parse();
3840}
3841
3842template<class T>
3843void DocPara::handleFile(const QCString &cmdName)
3844{
3845 AUTO_TRACE("cmdName={}",cmdName);
3846 QCString saveCmdName = cmdName;
3847 Token tok=parser()->tokenizer.lex();
3848 if (!tok.is(TokenRetval::TK_WHITESPACE))
3849 {
3850 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3851 saveCmdName);
3852 return;
3853 }
3855 tok=parser()->tokenizer.lex();
3857 if (!tok.is(TokenRetval::TK_WORD))
3858 {
3859 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3860 tok.to_string(),saveCmdName);
3861 return;
3862 }
3863 QCString name = parser()->context.token->name;
3864 children().append<T>(parser(),thisVariant(),name,
3868 auto df = children().get_last<T>();
3869 if (!df->parse())
3870 {
3871 children().pop_back();
3872 }
3873}
3874
3881
3882void DocPara::handleLink(const QCString &cmdName,bool isJavaLink)
3883{
3884 AUTO_TRACE("cmdName={} isJavaLink={}",cmdName,isJavaLink);
3885 QCString saveCmdName = cmdName;
3886 Token tok=parser()->tokenizer.lex();
3887 if (!tok.is(TokenRetval::TK_WHITESPACE))
3888 {
3889 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3890 saveCmdName);
3891 return;
3892 }
3894 tok=parser()->tokenizer.lex();
3895 if (!tok.is(TokenRetval::TK_WORD))
3896 {
3897 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"{} as the argument of {}",
3898 tok.to_string(),saveCmdName);
3899 return;
3900 }
3901 if (saveCmdName == "javalink")
3902 {
3904 parser()->context.nodeStack.size(),
3905 DocStyleChange::Code,cmdName,TRUE);
3906 }
3909 DocLink *lnk = children().get_last<DocLink>();
3910 if (saveCmdName == "javalink")
3911 {
3913 parser()->context.nodeStack.size(),
3914 DocStyleChange::Code,cmdName,FALSE);
3915 }
3916 QCString leftOver = lnk->parse(isJavaLink);
3917 if (!leftOver.isEmpty())
3918 {
3919 children().append<DocWord>(parser(),thisVariant(),leftOver);
3920 }
3921}
3922
3923void DocPara::handleRef(char cmdChar,const QCString &cmdName)
3924{
3925 AUTO_TRACE("cmdName={}",cmdName);
3926 QCString saveCmdName = cmdName;
3927 Token tok=parser()->tokenizer.lex();
3928 if (!tok.is(TokenRetval::TK_WHITESPACE))
3929 {
3930 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3931 cmdChar,qPrint(saveCmdName));
3932 return;
3933 }
3935 tok=parser()->tokenizer.lex(); // get the reference id
3936 if (!tok.is(TokenRetval::TK_WORD))
3937 {
3938 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3939 tok.to_string(),cmdChar,saveCmdName);
3940 goto endref;
3941 }
3945 children().get_last<DocRef>()->parse();
3946endref:
3948}
3949
3951{
3952 AUTO_TRACE("cmdName={}",cmdName);
3953 QCString saveCmdName = cmdName;
3954 Token tok=parser()->tokenizer.lex();
3955 bool isBlock = false;
3956 bool trimLeft = false;
3957 bool localScope = false;
3958 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
3959 if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="{")
3960 {
3962 parser()->tokenizer.lex();
3964 StringVector optList=split(parser()->context.token->name.str(),",");
3965 auto contains = [&optList](const char *kw)
3966 {
3967 return std::find(optList.begin(),optList.end(),kw)!=optList.end();
3968 };
3969 localScope = contains("local");
3970 if (contains("nostrip"))
3971 {
3972 stripCodeComments = false;
3973 }
3974 else if (contains("strip"))
3975 {
3976 stripCodeComments = true;
3977 }
3978 if (t==DocInclude::Snippet && contains("trimleft"))
3979 {
3980 trimLeft = true;
3981 }
3982
3983 if (contains("lineno"))
3984 {
3988 }
3989 tok=parser()->tokenizer.lex();
3990 if (!tok.is(TokenRetval::TK_WHITESPACE))
3991 {
3992 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3993 saveCmdName);
3994 return;
3995 }
3996 }
3997 else if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="[")
3998 {
4000 parser()->tokenizer.lex();
4001 isBlock = (parser()->context.token->name.stripWhiteSpace() == "block");
4003 parser()->tokenizer.lex();
4004 }
4005 else if (!tok.is(TokenRetval::TK_WHITESPACE))
4006 {
4007 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
4008 saveCmdName);
4009 return;
4010 }
4012 tok=parser()->tokenizer.lex();
4014 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4015 {
4016 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
4017 "argument of command {}",saveCmdName);
4018 return;
4019 }
4020 else if (!tok.is(TokenRetval::TK_WORD))
4021 {
4022 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
4023 tok.to_string(),saveCmdName);
4024 return;
4025 }
4026 QCString fileName = parser()->context.token->name;
4027 QCString blockId;
4029 {
4030 if (fileName == "this") fileName=parser()->context.fileName;
4032 tok=parser()->tokenizer.lex();
4034 if (!tok.is(TokenRetval::TK_WORD))
4035 {
4036 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected block identifier, but found token {} instead while parsing the {} command",
4037 tok.to_string(),saveCmdName);
4038 return;
4039 }
4040 blockId = "["+parser()->context.token->name+"]";
4041 }
4042
4044 thisVariant(),
4045 fileName,
4046 localScope ? parser()->context.context : "",
4047 t,
4048 stripCodeComments,
4051 blockId,isBlock,trimLeft);
4053}
4054
4055void DocPara::handleSection(char cmdChar,const QCString &cmdName)
4056{
4057 AUTO_TRACE("cmdName={}",cmdName);
4058 QCString saveCmdName = cmdName;
4059 // get the argument of the section command.
4060 Token tok=parser()->tokenizer.lex();
4061 if (!tok.is(TokenRetval::TK_WHITESPACE))
4062 {
4063 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
4064 cmdChar,saveCmdName);
4065 return;
4066 }
4067 tok=parser()->tokenizer.lex();
4068 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4069 {
4070 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
4071 "argument of command '{:c}{}'", cmdChar,saveCmdName);
4072 return;
4073 }
4074 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
4075 {
4076 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
4077 tok.to_string(),cmdChar,saveCmdName);
4078 return;
4079 }
4082 parser()->tokenizer.lex();
4084}
4085
4086Token DocPara::handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level)
4087{
4088 AUTO_TRACE();
4089 children().append<DocHtmlHeader>(parser(),thisVariant(),tagHtmlAttribs,level);
4090 Token retval = children().get_last<DocHtmlHeader>()->parse();
4091 return retval.is(TokenRetval::RetVal_OK) ? Token::make_TK_NEWPARA() : retval;
4092}
4093
4094// For XML tags whose content is stored in attributes rather than
4095// contained within the element, we need a way to inject the attribute
4096// text into the current paragraph.
4097bool DocPara::injectToken(Token tok,const QCString &tokText)
4098{
4099 AUTO_TRACE();
4100 parser()->context.token->name = tokText;
4101 return parser()->defaultHandleToken(thisVariant(),tok,children());
4102}
4103
4105{
4106 AUTO_TRACE();
4107 Token retval = parser()->tokenizer.lex();
4108 QCString lang = parser()->context.token->name;
4109 if (!lang.isEmpty() && lang.at(0)!='.')
4110 {
4111 lang="."+lang;
4112 }
4113 if (parser()->context.xmlComment)
4114 {
4115 parser()->context.token->verb = substitute(substitute(parser()->context.token->verb,"&lt;","<"),"&gt;",">");
4116 }
4117 // search for the first non-whitespace line, index is stored in li
4118 size_t i=0,li=0,l=parser()->context.token->verb.length();
4119 while (i<l && (parser()->context.token->verb.at(i)==' ' || parser()->context.token->verb.at(i)=='\n'))
4120 {
4121 if (parser()->context.token->verb.at(i)=='\n') li=i+1;
4122 i++;
4123 }
4126 stripIndentation(parser()->context.token->verb.mid(li)),
4130 FALSE,lang);
4131 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4132 {
4133 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"code section ended without end marker");
4134 }
4136 AUTO_TRACE_EXIT("retval={}",retval.to_string());
4137 return retval;
4138}
4139
4141{
4142 if (parser()->context.memberDef) // inheriting docs from a member
4143 {
4144 const MemberDef *reMd = parser()->context.memberDef->reimplements();
4145 if (reMd) // member from which was inherited.
4146 {
4147 const MemberDef *thisMd = parser()->context.memberDef;
4148 //printf("{InheritDocs:%s=>%s}\n",qPrint(parser()->context.memberDef->qualifiedName()),qPrint(reMd->qualifiedName()));
4149 parser()->pushContext();
4150 parser()->context.scope=reMd->getOuterScope();
4151 if (parser()->context.scope!=Doxygen::globalScope)
4152 {
4154 }
4155 parser()->context.memberDef=reMd;
4156 while (!parser()->context.styleStack.empty()) parser()->context.styleStack.pop();
4157 while (!parser()->context.nodeStack.empty()) parser()->context.nodeStack.pop();
4158 parser()->context.copyStack.push_back(reMd);
4161 parser()->context.copyStack.pop_back();
4162 auto hasParamCommand = parser()->context.hasParamCommand;
4163 auto hasReturnCommand = parser()->context.hasReturnCommand;
4164 auto retvalsFound = parser()->context.retvalsFound;
4165 auto paramsFound = parser()->context.paramsFound;
4166 parser()->popContext();
4167 parser()->context.hasParamCommand = hasParamCommand;
4168 parser()->context.hasReturnCommand = hasReturnCommand;
4169 parser()->context.retvalsFound = retvalsFound;
4170 parser()->context.paramsFound = paramsFound;
4171 parser()->context.memberDef = thisMd;
4172 }
4173 }
4174}
4175
4176
4177Token DocPara::handleCommand(char cmdChar, const QCString &cmdName)
4178{
4179 AUTO_TRACE("cmdName={}",cmdName);
4180 Token retval = Token::make_RetVal_OK();
4181 CommandType cmdId = Mappers::cmdMapper->map(cmdName);
4182 switch (cmdId)
4183 {
4185 {
4186 std::string str{cmdChar};
4187 children().append<DocWord>(parser(),thisVariant(),str.c_str() + cmdName);
4188 if (isAliasCmd(cmdName.view()))
4189 {
4190 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Found unexpanded alias '{:c}{}'. Check if number of arguments passed is correct.",cmdChar,cmdName);
4191 }
4192 else
4193 {
4194 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Found unknown command '{:c}{}'",cmdChar,cmdName);
4195 }
4196 }
4197 break;
4200 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4202 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4203 break;
4206 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4208 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4209 break;
4212 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4214 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4215 break;
4218 break;
4221 break;
4224 break;
4227 break;
4230 break;
4233 break;
4236 break;
4239 break;
4242 break;
4245 break;
4249 break;
4254 break;
4257 break;
4260 break;
4263 break;
4266 break;
4269 break;
4272 break;
4275 break;
4280 break;
4284 break;
4287 break;
4290 break;
4293 break;
4296 break;
4299 break;
4302 break;
4305 break;
4308 break;
4311 break;
4314 break;
4317 break;
4320 break;
4323 break;
4326 break;
4329 break;
4331 {
4333 retval = children().get_last<DocSimpleList>()->parse();
4334 }
4335 break;
4337 {
4338 handleSection(cmdChar,cmdName);
4339 retval = Token::make_RetVal_Section();
4340 }
4341 break;
4343 {
4344 handleSection(cmdChar,cmdName);
4345 retval = Token::make_RetVal_Subsection();
4346 }
4347 break;
4349 {
4350 handleSection(cmdChar,cmdName);
4351 retval = Token::make_RetVal_Subsubsection();
4352 }
4353 break;
4355 {
4356 handleSection(cmdChar,cmdName);
4357 retval = Token::make_RetVal_Paragraph();
4358 }
4359 break;
4361 {
4362 handleSection(cmdChar,cmdName);
4363 retval = Token::make_RetVal_SubParagraph();
4364 }
4365 break;
4367 {
4368 handleSection(cmdChar,cmdName);
4369 retval = Token::make_RetVal_SubSubParagraph();
4370 }
4371 break;
4373 {
4375 retval = handleStartCode();
4376 }
4377 break;
4379 {
4381 retval = handleStartCode();
4382 }
4383 break;
4385 {
4387 retval = parser()->tokenizer.lex();
4389 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4390 {
4391 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"htmlonly section ended without end marker");
4392 }
4394 }
4395 break;
4397 {
4399 retval = parser()->tokenizer.lex();
4401 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4402 {
4403 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"manonly section ended without end marker");
4404 }
4406 }
4407 break;
4409 {
4411 retval = parser()->tokenizer.lex();
4413 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4414 {
4415 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"rtfonly section ended without end marker");
4416 }
4418 }
4419 break;
4421 {
4423 retval = parser()->tokenizer.lex();
4425 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4426 {
4427 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"latexonly section ended without end marker");
4428 }
4430 }
4431 break;
4433 {
4435 retval = parser()->tokenizer.lex();
4437 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4438 {
4439 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"xmlonly section ended without end marker");
4440 }
4442 }
4443 break;
4445 {
4447 retval = parser()->tokenizer.lex();
4449 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4450 {
4451 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"docbookonly section ended without end marker");
4452 }
4454 }
4455 break;
4457 {
4460 parser()->tokenizer.lex();
4461
4462 QCString fullMatch = parser()->context.token->verb;
4463 int idx = fullMatch.find('{');
4464 int idxEnd = fullMatch.find("}",idx+1);
4465 StringVector optList;
4466 if (idx != -1) // options present
4467 {
4468 QCString optStr = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4469 optList = split(optStr.str(),",");
4470 for (const auto &opt : optList)
4471 {
4472 if (opt.empty()) continue;
4473 QCString locOpt(opt);
4474 locOpt = locOpt.stripWhiteSpace().lower();
4475 if (locOpt == "code")
4476 {
4478 }
4479 else if (!locOpt.isEmpty())
4480 {
4481 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Unknown option '{}' for '\\iliteral'",opt);
4482 }
4483 }
4484 }
4485
4487 retval = parser()->tokenizer.lex();
4489 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4490 {
4491 if (t == DocVerbatim::JavaDocCode)
4492 {
4493 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"javadoc code section ended without end marker");
4494 }
4495 else
4496 {
4497 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"javadoc literal section ended without end marker");
4498 }
4499 }
4501 }
4502 break;
4505 {
4506 if (cmdId == CommandType::CMD_VERBATIM)
4507 {
4509 }
4510 else
4511 {
4513 }
4514 retval = parser()->tokenizer.lex();
4516 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4517 {
4518 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"verbatim section ended without end marker");
4519 }
4521 }
4522 break;
4524 {
4533 QCString width,height;
4534 parser()->defaultHandleTitleAndSize(CommandType::CMD_DOT,&children().back(),dv->children(),width,height);
4536 retval = parser()->tokenizer.lex();
4537 dv->setText(parser()->context.token->verb);
4538 dv->setWidth(width);
4539 dv->setHeight(height);
4540 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4541 if (!Config_getBool(HAVE_DOT))
4542 {
4543 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"ignoring \\dot command because HAVE_DOT is not set");
4544 children().pop_back();
4545 }
4546 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4547 {
4548 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"dot section ended without end marker");
4549 }
4551 }
4552 break;
4554 {
4563 QCString width,height;
4564 parser()->defaultHandleTitleAndSize(CommandType::CMD_MSC,&children().back(),dv->children(),width,height);
4566 retval = parser()->tokenizer.lex();
4567 dv->setText(parser()->context.token->verb);
4568 dv->setWidth(width);
4569 dv->setHeight(height);
4570 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4571 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4572 {
4573 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"msc section ended without end marker");
4574 }
4576 }
4577 break;
4579 {
4580 QCString jarPath = Config_getString(PLANTUML_JAR_PATH);
4582 parser()->tokenizer.lex();
4583 QCString fullMatch = parser()->context.token->sectionId;
4584 QCString sectionId = "";
4585 int idx = fullMatch.find('{');
4586 int idxEnd = fullMatch.find("}",idx+1);
4587 StringVector optList;
4588 QCString engine;
4589 if (idx != -1) // options present
4590 {
4591 QCString optStr = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4592 optList = split(optStr.str(),",");
4593 for (const auto &opt : optList)
4594 {
4595 if (opt.empty()) continue;
4596 bool found = false;
4597 QCString locOpt(opt);
4598 locOpt = locOpt.stripWhiteSpace().lower();
4599 if (g_plantumlEngine.find(locOpt.str())!=g_plantumlEngine.end())
4600 {
4601 if (!engine.isEmpty())
4602 {
4603 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Multiple definition of engine for '\\startuml'");
4604 }
4605 engine = locOpt;
4606 found = true;
4607 }
4608 if (!found)
4609 {
4610 if (sectionId.isEmpty())
4611 {
4612 sectionId = opt;
4613 }
4614 else
4615 {
4616 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Multiple use of filename for '\\startuml'");
4617 }
4618 }
4619 }
4620 }
4621 else
4622 {
4623 sectionId = parser()->context.token->sectionId;
4624 }
4625 if (engine.isEmpty()) engine = "uml";
4626
4627 if (sectionId.isEmpty())
4628 {
4630 retval = parser()->tokenizer.lex();
4631 assert(retval.is(TokenRetval::RetVal_OK));
4632
4633 sectionId = parser()->context.token->sectionId;
4634 sectionId = sectionId.stripWhiteSpace();
4635 }
4636
4637 QCString plantFile(sectionId);
4642 FALSE,plantFile);
4644 dv->setEngine(engine);
4646 QCString width,height;
4647 parser()->defaultHandleTitleAndSize(CommandType::CMD_STARTUML,&children().back(),dv->children(),width,height);
4649 retval = parser()->tokenizer.lex();
4650 int line = 0;
4651 QCString trimmedVerb = stripLeadingAndTrailingEmptyLines(parser()->context.token->verb,line);
4652 if (engine == "ditaa")
4653 {
4654 dv->setUseBitmap(true);
4655 }
4656 else if (engine == "uml")
4657 {
4658 int i = trimmedVerb.find('\n');
4659 QCString firstLine = i==-1 ? trimmedVerb : trimmedVerb.left(i);
4660 if (firstLine.stripWhiteSpace() == "ditaa") dv->setUseBitmap(true);
4661 }
4662 dv->setText(trimmedVerb);
4663 dv->setWidth(width);
4664 dv->setHeight(height);
4665 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4666 if (jarPath.isEmpty())
4667 {
4668 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"ignoring \\startuml command because PLANTUML_JAR_PATH is not set");
4669 children().pop_back();
4670 }
4671 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4672 {
4673 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"startuml section ended without end marker");
4674 }
4676 }
4677 break;
4679 retval = Token::make_RetVal_EndParBlock();
4680 break;
4696 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4697 break;
4699 retval = handleParamSection(cmdName,DocParamSect::Param,FALSE,parser()->context.token->paramDir);
4700 break;
4702 retval = handleParamSection(cmdName,DocParamSect::TemplateParam,FALSE,parser()->context.token->paramDir);
4703 break;
4705 retval = handleParamSection(cmdName,DocParamSect::RetVal);
4706 break;
4709 break;
4711 retval = handleXRefItem();
4712 break;
4714 {
4716 }
4717 break;
4720 {
4722 }
4723 break;
4725 {
4727 }
4728 break;
4730 {
4734 retval = children().get_last<DocIndexEntry>()->parse();
4735 }
4736 break;
4738 retval = Token::make_RetVal_Internal();
4739 break;
4741 retval = Token::make_RetVal_EndInternal();
4742 break;
4744 {
4746 retval = children().get_last<DocParBlock>()->parse();
4747 }
4748 break;
4749 case CommandType::CMD_COPYDOC: // fall through
4750 case CommandType::CMD_COPYBRIEF: // fall through
4752 //retval = Token::make_RetVal_CopyDoc();
4753 // these commands should already be resolved by processCopyDoc()
4754 break;
4757 break;
4760 break;
4763 break;
4766 break;
4769 break;
4772 break;
4775 break;
4778 break;
4781 break;
4784 break;
4787 break;
4790 break;
4793 break;
4796 break;
4799 break;
4802 break;
4805 break;
4807 if (!Config_getBool(HAVE_DOT))
4808 {
4809 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
4810 "ignoring \\dotfile command because HAVE_DOT is not set");
4811 }
4812 else
4813 {
4814 handleFile<DocDotFile>(cmdName);
4815 }
4816 break;
4819 break;
4821 handleFile<DocMscFile>(cmdName);
4822 break;
4824 handleFile<DocDiaFile>(cmdName);
4825 break;
4828 break;
4830 handleLink(cmdName,FALSE);
4831 break;
4833 handleLink(cmdName,TRUE);
4834 break;
4836 handleCite(cmdChar,cmdName);
4837 break;
4839 handleEmoji(cmdChar,cmdName);
4840 break;
4842 handleDoxyConfig(cmdChar,cmdName);
4843 break;
4844 case CommandType::CMD_REF: // fall through
4846 handleRef(cmdChar,cmdName);
4847 break;
4849 {
4852 }
4853 break;
4855 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4856 break;
4858 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4859 break;
4861 {
4863 }
4864 break;
4865 //case CommandType::CMD_LANGSWITCH:
4866 // retval = handleLanguageSwitch();
4867 // break;
4869 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4870 {
4873 }
4874 break;
4877 break;
4879 handleShowDate(cmdChar,cmdName);
4880 break;
4882 handleILine(cmdChar,cmdName);
4883 break;
4885 handleIFile(cmdChar,cmdName);
4886 break;
4888 {
4890 (void)parser()->tokenizer.lex();
4892 //printf("Found scope='%s'\n",qPrint(parser()->context.context));
4894 }
4895 break;
4896 default:
4897 // we should not get here!
4898 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' in paragraph context",cmdName);
4899 break;
4900 }
4901 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_OK,TokenRetval::RetVal_SimpleSec
4902 TokenRetval::TK_LISTITEM,TokenRetval::TK_ENDLIST,TokenRetval::TK_NEWPARA
4903 TokenRetval::RetVal_Section,TokenRetval::RetVal_EndList
4904 TokenRetval::RetVal_Internal,TokenRetval::RetVal_SwitchLang
4905 TokenRetval::RetVal_EndInternal)
4906 );
4907 AUTO_TRACE_EXIT("retval={}",retval.to_string());
4908 return retval;
4909}
4910
4911static bool findAttribute(const HtmlAttribList &tagHtmlAttribs,
4912 const char *attrName,
4913 QCString *result)
4914{
4915
4916 for (const auto &opt : tagHtmlAttribs)
4917 {
4918 if (opt.name==attrName)
4919 {
4920 *result = opt.value;
4921 return TRUE;
4922 }
4923 }
4924 return FALSE;
4925}
4926
4927Token DocPara::handleHtmlStartTag(const QCString &tagName,const HtmlAttribList &tagHtmlAttribs)
4928{
4929 AUTO_TRACE("tagName={} #tagHtmlAttrs={}",tagName,tagHtmlAttribs.size());
4930 Token retval = Token::make_RetVal_OK();
4931 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
4932 if (parser()->context.token->emptyTag && !(tagId>HtmlTagType::XML_CmdMask) &&
4935 {
4936 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"HTML tag ('<{}/>') may not use the 'empty tag' XHTML syntax.",
4937 tagName);
4938 }
4939 switch (tagId)
4940 {
4942 if (!parser()->context.token->emptyTag)
4943 {
4945 tagHtmlAttribs,DocHtmlList::Unordered);
4946 retval=children().get_last<DocHtmlList>()->parse();
4947 }
4948 break;
4950 if (!parser()->context.token->emptyTag)
4951 {
4953 tagHtmlAttribs,DocHtmlList::Ordered);
4954 retval=children().get_last<DocHtmlList>()->parse();
4955 }
4956 break;
4958 if (parser()->context.token->emptyTag) break;
4960 {
4961 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <li> tag found");
4962 }
4963 else
4964 {
4965 retval = Token::make_RetVal_ListItem();
4966 }
4967 break;
4969 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Bold,tagName,&parser()->context.token->attribs);
4970 break;
4972 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::S,tagName,&parser()->context.token->attribs);
4973 break;
4975 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Strike,tagName,&parser()->context.token->attribs);
4976 break;
4978 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Del,tagName,&parser()->context.token->attribs);
4979 break;
4981 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Underline,tagName,&parser()->context.token->attribs);
4982 break;
4984 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Ins,tagName,&parser()->context.token->attribs);
4985 break;
4987 if (parser()->context.token->emptyTag) break;
4988 if (parser()->context.xmlComment)
4989 // for C# source or inside a <summary> or <remark> section we
4990 // treat <code> as an XML tag (so similar to @code)
4991 {
4993 retval = handleStartCode();
4994 }
4995 else // normal HTML markup
4996 {
4997 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
4998 }
4999 break;
5001 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Kbd,tagName,&parser()->context.token->attribs);
5002 break;
5004 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Typewriter,tagName,&parser()->context.token->attribs);
5005 break;
5007 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Italic,tagName,&parser()->context.token->attribs);
5008 break;
5010 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Div,tagName,&parser()->context.token->attribs);
5011 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Div,tagName);
5012 break;
5014 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Span,tagName,&parser()->context.token->attribs);
5015 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Span,tagName);
5016 break;
5018 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Subscript,tagName,&parser()->context.token->attribs);
5019 break;
5021 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Superscript,tagName,&parser()->context.token->attribs);
5022 break;
5024 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Center,tagName,&parser()->context.token->attribs);
5025 break;
5027 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Small,tagName,&parser()->context.token->attribs);
5028 break;
5030 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Cite,tagName,&parser()->context.token->attribs);
5031 break;
5033 if (parser()->context.token->emptyTag) break;
5034 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Preformatted,tagName,&parser()->context.token->attribs);
5037 break;
5039 retval = Token::make_TK_NEWPARA();
5040 break;
5042 if (!parser()->context.token->emptyTag)
5043 {
5044 children().append<DocHtmlDescList>(parser(),thisVariant(),tagHtmlAttribs);
5045 retval=children().get_last<DocHtmlDescList>()->parse();
5046 }
5047 break;
5049 if (insideDL(thisVariant()))
5050 {
5051 retval = Token::make_RetVal_DescTitle();
5052 }
5053 else
5054 {
5055 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dt> found");
5056 }
5057 break;
5059 if (insideDL(thisVariant()))
5060 {
5061 retval = Token::make_RetVal_DescData();
5062 }
5063 else
5064 {
5065 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dd> found");
5066 }
5067 break;
5069 if (!parser()->context.token->emptyTag)
5070 {
5071 children().append<DocHtmlTable>(parser(),thisVariant(),tagHtmlAttribs);
5072 retval=children().get_last<DocHtmlTable>()->parse();
5073 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5074 }
5075 break;
5077 retval = Token::make_RetVal_TableRow();
5078 break;
5080 retval = Token::make_RetVal_TableCell();
5081 break;
5083 retval = Token::make_RetVal_TableHCell();
5084 break;
5088 // for time being ignore </t....> tag
5089 break;
5091 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <caption> found");
5092 break;
5094 {
5095 children().append<DocLineBreak>(parser(),thisVariant(),tagHtmlAttribs);
5096 }
5097 break;
5099 {
5100 children().append<DocHorRuler>(parser(),thisVariant(),tagHtmlAttribs);
5101 }
5102 break;
5104 retval = parser()->handleAHref(thisVariant(),children(),tagHtmlAttribs);
5105 break;
5107 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,1);
5108 break;
5110 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,2);
5111 break;
5113 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,3);
5114 break;
5116 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,4);
5117 break;
5119 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,5);
5120 break;
5122 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,6);
5123 break;
5125 {
5126 parser()->handleImg(thisVariant(),children(),tagHtmlAttribs);
5127 }
5128 break;
5130 if (!parser()->context.token->emptyTag)
5131 {
5132 children().append<DocHtmlDetails>(parser(),thisVariant(),tagHtmlAttribs);
5133 retval=children().get_last<DocHtmlDetails>()->parse();
5134 }
5135 break;
5137 if (!parser()->context.token->emptyTag)
5138 {
5139 children().append<DocHtmlBlockQuote>(parser(),thisVariant(),tagHtmlAttribs);
5140 retval = children().get_last<DocHtmlBlockQuote>()->parse();
5141 }
5142 break;
5143
5146 {
5147 if (!parser()->context.token->emptyTag)
5148 {
5150 while (n && !std::holds_alternative<DocHtmlDetails>(*n)) n=::parent(n);
5151 DocHtmlDetails *d = std::get_if<DocHtmlDetails>(n);
5152 if (d)
5153 {
5154 if (!d->summary()) // details section does not have a summary yet
5155 {
5156 d->parseSummary(n,parser()->context.token->attribs);
5157 }
5158 else
5159 {
5160 retval = Token::make_TK_NEWPARA();
5161 }
5162 }
5163 }
5164 }
5165 break;
5169 // fall through
5172 if (!children().empty())
5173 {
5174 retval = Token::make_TK_NEWPARA();
5175 }
5176 break;
5178 if (insideTable(thisVariant()))
5179 {
5180 retval = Token::make_RetVal_TableCell();
5181 }
5182 break;
5183 case HtmlTagType::XML_C:
5184 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
5185 break;
5188 {
5190 QCString paramName;
5191 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5192 {
5193 if (paramName.isEmpty())
5194 {
5195 if (Config_getBool(WARN_NO_PARAMDOC))
5196 {
5197 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"empty 'name' attribute for <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5198 }
5199 }
5200 else
5201 {
5202 retval = handleParamSection(paramName,
5204 TRUE);
5205 }
5206 }
5207 else
5208 {
5209 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5210 }
5211 }
5212 break;
5215 {
5216 QCString paramName;
5217 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5218 {
5219 //printf("paramName=%s\n",qPrint(paramName));
5221 children().append<DocWord>(parser(),thisVariant(),paramName);
5223 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
5224 }
5225 else
5226 {
5227 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}ref> tag.",tagId==HtmlTagType::XML_PARAMREF?"":"type");
5228 }
5229 }
5230 break;
5232 {
5234 QCString exceptName;
5235 if (findAttribute(tagHtmlAttribs,"cref",&exceptName))
5236 {
5237 unescapeCRef(exceptName);
5238 retval = handleParamSection(exceptName,DocParamSect::Exception,TRUE);
5239 }
5240 else
5241 {
5242 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <exception> tag.");
5243 }
5244 }
5245 break;
5248 if (insideTable(thisVariant()))
5249 {
5250 retval = Token::make_RetVal_TableRow();
5251 }
5252 else if (insideUL(thisVariant()) || insideOL(thisVariant()))
5253 {
5254 retval = Token::make_RetVal_ListItem();
5255 }
5256 else
5257 {
5258 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <item> tag found");
5259 }
5260 break;
5265 break;
5267 if (insideTable(thisVariant()))
5268 {
5269 retval = Token::make_RetVal_TableCell();
5270 }
5271 break;
5273 // I'm not sure if <see> is the same as <seealso> or if it
5274 // should you link a member without producing a section. The
5275 // C# specification is extremely vague about this (but what else
5276 // can we expect from Microsoft...)
5277 {
5278 QCString cref;
5279 //printf("HtmlTagType::XML_SEE: empty tag=%d\n",parser()->context.token->emptyTag);
5280 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5281 {
5282 unescapeCRef(cref);
5283 if (parser()->context.token->emptyTag) // <see cref="..."/> style
5284 {
5285 bool inSeeBlock = parser()->context.inSeeBlock;
5286 parser()->context.token->name = cref;
5289 parser()->context.inSeeBlock = inSeeBlock;
5290 }
5291 else // <see cref="...">...</see> style
5292 {
5293 //DocRef *ref = new DocRef(this,cref);
5294 //children().append(ref);
5295 //ref->parse();
5298 DocLink *lnk = children().get_last<DocLink>();
5299 QCString leftOver = lnk->parse(FALSE,TRUE);
5300 if (!leftOver.isEmpty())
5301 {
5302 children().append<DocWord>(parser(),thisVariant(),leftOver);
5303 }
5304 }
5305 }
5306 else if (findAttribute(tagHtmlAttribs,"langword",&cref)) // <see langword="..."/> or <see langword="..."></see>
5307 {
5308 bool inSeeBlock = parser()->context.inSeeBlock;
5309 parser()->context.token->name = cref;
5314 parser()->context.inSeeBlock = inSeeBlock;
5315 }
5316 else
5317 {
5318 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' or 'langword' attribute from <see> tag.");
5319 }
5320 }
5321 break;
5323 {
5325 QCString cref;
5326 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5327 {
5328 unescapeCRef(cref);
5329 // Look for an existing "see" section
5330 DocNodeVariant *vss=nullptr;
5331 for (auto &n : children())
5332 {
5333 DocSimpleSect *candidate = std::get_if<DocSimpleSect>(&n);
5334 if (candidate && candidate->type()==DocSimpleSect::See)
5335 {
5336 vss = &n;
5337 }
5338 }
5339
5340 if (!vss) // start new section
5341 {
5343 vss = &children().back();
5344 }
5345
5346 std::get<DocSimpleSect>(*vss).appendLinkWord(cref);
5347 retval = Token::make_RetVal_OK();
5348 }
5349 else
5350 {
5351 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <seealso> tag.");
5352 }
5353 }
5354 break;
5356 {
5357 QCString type;
5358 findAttribute(tagHtmlAttribs,"type",&type);
5360 HtmlAttribList emptyList;
5361 if (type=="number")
5362 {
5363 listType=DocHtmlList::Ordered;
5364 }
5365 if (type=="table")
5366 {
5367 children().append<DocHtmlTable>(parser(),thisVariant(),emptyList);
5368 retval=children().get_last<DocHtmlTable>()->parseXml();
5369 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5370 }
5371 else
5372 {
5373 children().append<DocHtmlList>(parser(),thisVariant(),emptyList,listType);
5374 retval=children().get_last<DocHtmlList>()->parseXml();
5375 }
5376 }
5377 break;
5380 // These tags are defined in .Net but are currently unsupported
5382 break;
5384 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag <{}> found", tagName);
5385 children().append<DocWord>(parser(),thisVariant(), "<"+tagName+parser()->context.token->attribsStr+">");
5386 break;
5389 break;
5390 default:
5391 // we should not get here!
5392 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected start tag {}",tagName);
5393 ASSERT(0);
5394 break;
5395 }
5396 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5397 return retval;
5398}
5399
5401{
5402 AUTO_TRACE("tagName={}",tagName);
5403 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
5404 Token retval = Token::make_RetVal_OK();
5405 switch (tagId)
5406 {
5408 if (!insideUL(thisVariant()))
5409 {
5410 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ul> tag without matching <ul>");
5411 }
5412 else
5413 {
5414 retval = Token::make_RetVal_EndList();
5415 }
5416 break;
5418 if (!insideOL(thisVariant()))
5419 {
5420 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ol> tag without matching <ol>");
5421 }
5422 else
5423 {
5424 retval = Token::make_RetVal_EndList();
5425 }
5426 break;
5428 if (!insideLI(thisVariant()))
5429 {
5430 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </li> tag without matching <li>");
5431 }
5432 else
5433 {
5434 // ignore </li> tags
5435 }
5436 break;
5438 if (!insideDetails(thisVariant()))
5439 {
5440 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </details> tag without matching <details>");
5441 }
5442 else
5443 {
5444 retval = Token::make_RetVal_EndHtmlDetails();
5445 }
5446 break;
5449 {
5450 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </blockquote> tag without matching <blockquote>");
5451 }
5452 else
5453 {
5454 retval = Token::make_RetVal_EndBlockQuote();
5455 }
5456 break;
5459 break;
5462 break;
5465 break;
5468 break;
5471 break;
5474 break;
5477 break;
5480 break;
5483 break;
5486 break;
5489 break;
5492 break;
5495 break;
5498 break;
5501 break;
5504 break;
5507 break;
5512 break;
5514 retval = Token::make_TK_NEWPARA();
5515 break;
5517 retval = Token::make_RetVal_EndDesc();
5518 break;
5520 // ignore </dt> tag
5521 break;
5523 // ignore </dd> tag
5524 break;
5526 retval = Token::make_RetVal_EndTable();
5527 break;
5529 retval = Token::make_RetVal_EndTableRow();
5530 break;
5532 retval = Token::make_RetVal_EndTableCell();
5533 break;
5535 retval = Token::make_RetVal_EndTableCell();
5536 break;
5540 // for time being ignore </t....> tag
5541 break;
5543 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </caption> found");
5544 break;
5546 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </br> tag found");
5547 break;
5549 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h1> found");
5550 break;
5552 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h2> found");
5553 break;
5555 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h3> found");
5556 break;
5558 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h4> found");
5559 break;
5561 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h5> found");
5562 break;
5564 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h6> found");
5565 break;
5567 break;
5569 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </hr> tag found");
5570 break;
5572 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </a> found");
5573 // ignore </a> tag (can be part of <a name=...></a>
5574 break;
5575
5577 break;
5579 retval = Token::make_TK_NEWPARA();
5580 break;
5593 retval = Token::make_RetVal_CloseXml();
5594 break;
5595 case HtmlTagType::XML_C:
5597 break;
5605 // These tags are defined in .Net but are currently unsupported
5606 break;
5608 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag </{}> found", tagName);
5609 children().append<DocWord>(parser(),thisVariant(),"</"+tagName+">");
5610 break;
5611 default:
5612 // we should not get here!
5613 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end tag {}",tagName);
5614 ASSERT(0);
5615 break;
5616 }
5617 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5618 return retval;
5619}
5620
5622{
5623 // expected hierarchy:
5624 // 1. DocAutoListItem <- n
5625 // 2. DocAutoList <- parent(n)
5626 // 3. DocPara <- parent(parent(n))
5627
5628 // step 1
5629 if (!std::get_if<DocAutoListItem>(n)) // not inside a auto list item
5630 {
5631 return false;
5632 }
5633
5634 // step 2
5635 n = parent(n);
5636 int indent = 0;
5637 const auto docAutoList = std::get_if<DocAutoList>(n);
5638 if (docAutoList) // capture indent
5639 {
5640 indent = docAutoList->indent();
5641 }
5642 else
5643 {
5644 return false;
5645 }
5646
5647 // step 3
5648 n = parent(n);
5649 const auto docPara = std::get_if<DocPara>(n);
5650 if (docPara)
5651 {
5652 QCString tagNameLower = QCString(parser->context.token->name).lower();
5653 auto topStyleChange = [](const DocStyleChangeStack &stack) -> const DocStyleChange &
5654 {
5655 return std::get<DocStyleChange>(*stack.top());
5656 };
5657
5658 if (parser->context.styleStack.empty() || // no style change
5659 (topStyleChange(parser->context.styleStack).tagName()==tagNameLower && // correct style change
5660 topStyleChange(parser->context.styleStack).position()!=parser->context.nodeStack.size()) // wrong position, so normal close
5661 )
5662 {
5663 // insert an artificial 'end of autolist' marker and parse again
5664 QCString indentStr;
5665 indentStr.fill(' ',indent);
5666 parser->tokenizer.unputString("\\ilinebr "+indentStr+".\\ilinebr"+indentStr+"</"+parser->context.token->name+">");
5667 return true;
5668 }
5669 }
5670 return false;
5671}
5672
5674{
5675 AUTO_TRACE();
5676 auto ns = AutoNodeStack(parser(),thisVariant());
5677 // handle style commands "inherited" from the previous paragraph
5679 Token tok=parser()->tokenizer.lex();
5680 Token retval = Token::make_TK_NONE();
5681 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
5682 {
5683reparsetoken:
5684 AUTO_TRACE_ADD("token '{}' at {}",tok.to_string(),parser()->tokenizer.getLineNr());
5685 if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL,
5686 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG)
5687 )
5688 {
5689 AUTO_TRACE_ADD("name={}",parser()->context.token->name);
5690 }
5691 switch(tok.value())
5692 {
5693 case TokenRetval::TK_WORD:
5695 break;
5696 case TokenRetval::TK_LNKWORD:
5698 break;
5699 case TokenRetval::TK_URL:
5701 break;
5702 case TokenRetval::TK_WHITESPACE:
5703 {
5704 // prevent leading whitespace and collapse multiple whitespace areas
5705 if (insidePRE(thisVariant()) || // all whitespace is relevant
5706 (
5707 // remove leading whitespace
5708 !children().empty() &&
5709 // and whitespace after certain constructs
5713 )
5714 )
5715 {
5717 }
5718 }
5719 break;
5720 case TokenRetval::TK_LISTITEM:
5721 {
5722 AUTO_TRACE_ADD("found list item at {}",parser()->context.token->indent);
5723 const DocNodeVariant *n=parent();
5724 while (n && !std::holds_alternative<DocAutoList>(*n)) n=::parent(n);
5725 const DocAutoList *al = std::get_if<DocAutoList>(n);
5726 if (al) // we found an auto list up in the hierarchy
5727 {
5728 AUTO_TRACE_ADD("previous list item at {}",al->indent());
5729 if (al->indent()>=parser()->context.token->indent)
5730 // new item at the same or lower indent level
5731 {
5732 retval = Token::make_TK_LISTITEM();
5733 goto endparagraph;
5734 }
5735 }
5736
5737 // determine list depth
5738 int depth = 0;
5739 n=parent();
5740 while (n)
5741 {
5742 al = std::get_if<DocAutoList>(n);
5743 if (al && al->isEnumList()) depth++;
5744 n=::parent(n);
5745 }
5746
5747 // first item or sub list => create new list
5748 do
5749 {
5752 parser()->context.token->isEnumList,depth,
5754 al = children().get_last<DocAutoList>();
5755 retval = children().get_last<DocAutoList>()->parse();
5756 } while (retval.is(TokenRetval::TK_LISTITEM) && // new list
5757 al->indent()==parser()->context.token->indent // at same indent level
5758 );
5759
5760 // check the return value
5761 if (retval.is(TokenRetval::RetVal_SimpleSec)) // auto list ended due to simple section command
5762 {
5763 // Reparse the token that ended the section at this level,
5764 // so a new simple section will be started at this level.
5765 // This is the same as unputting the last read token and continuing.
5767 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5768 {
5771 tok = Token::make_TK_RCSTAG();
5772 }
5773 else // other section
5774 {
5775 tok = Token::make_TK_COMMAND_BS();
5776 }
5777 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5778 goto reparsetoken;
5779 }
5780 else if (retval.is(TokenRetval::TK_ENDLIST))
5781 {
5782 if (al->indent()>parser()->context.token->indent) // end list
5783 {
5784 goto endparagraph;
5785 }
5786 else // continue with current paragraph
5787 {
5788 }
5789 }
5790 else // paragraph ended due to TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, or EOF
5791 {
5792 goto endparagraph;
5793 }
5794 }
5795 break;
5796 case TokenRetval::TK_ENDLIST:
5797 AUTO_TRACE_ADD("Found end of list inside of paragraph at line {}",parser()->tokenizer.getLineNr());
5798 if (std::get_if<DocAutoListItem>(parent()))
5799 {
5800 const DocAutoList *al = std::get_if<DocAutoList>(::parent(parent()));
5801 if (al && al->indent()>=parser()->context.token->indent)
5802 {
5803 // end of list marker ends this paragraph
5804 retval = Token::make_TK_ENDLIST();
5805 goto endparagraph;
5806 }
5807 else
5808 {
5809 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found "
5810 "has invalid indent level");
5811 }
5812 }
5813 else
5814 {
5815 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found without any preceding "
5816 "list items");
5817 }
5818 break;
5819 case TokenRetval::TK_COMMAND_AT:
5820 // fall through
5821 case TokenRetval::TK_COMMAND_BS:
5822 {
5823 // see if we have to start a simple section
5824 CommandType cmd = Mappers::cmdMapper->map(parser()->context.token->name);
5825 const DocNodeVariant *n=parent();
5826 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
5827 !std::holds_alternative<DocParamSect>(*n))
5828 {
5829 n=::parent(n);
5830 }
5832 {
5833 if (n) // already in a simple section
5834 {
5835 // simple section cannot start in this paragraph, need
5836 // to unwind the stack and remember the command.
5838 retval = Token::make_RetVal_SimpleSec();
5839 goto endparagraph;
5840 }
5841 }
5842 // see if we are in a simple list
5843 n=parent();
5844 while (n && !std::holds_alternative<DocSimpleListItem>(*n)) n=::parent(n);
5845 if (n)
5846 {
5847 if (cmd==CommandType::CMD_LI)
5848 {
5849 retval = Token::make_RetVal_ListItem();
5850 goto endparagraph;
5851 }
5852 }
5853
5854 // handle the command
5855 retval=handleCommand(tok.command_to_char(),parser()->context.token->name);
5856 AUTO_TRACE_ADD("handleCommand returns {}",retval.to_string());
5857
5858 // check the return value
5859 if (retval.is(TokenRetval::RetVal_SimpleSec))
5860 {
5861 // Reparse the token that ended the section at this level,
5862 // so a new simple section will be started at this level.
5863 // This is the same as unputting the last read token and continuing.
5865 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5866 {
5869 tok = Token::make_TK_RCSTAG();
5870 }
5871 else // other section
5872 {
5873 tok = Token::make_TK_COMMAND_BS();
5874 }
5875 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5876 goto reparsetoken;
5877 }
5878 else if (retval.value()>TokenRetval::TK_NONE && retval.value()<TokenRetval::RetVal_OK)
5879 {
5880 // the command ended with a new command, reparse this token
5881 tok = retval;
5882 goto reparsetoken;
5883 }
5884 else if (retval.value()!=TokenRetval::RetVal_OK) // end of file, end of paragraph, start or end of section
5885 // or some auto list marker
5886 {
5887 goto endparagraph;
5888 }
5889 }
5890 break;
5891 case TokenRetval::TK_HTMLTAG:
5892 {
5893 if (!parser()->context.token->endTag) // found a start tag
5894 {
5895 retval = handleHtmlStartTag(parser()->context.token->name,parser()->context.token->attribs);
5896 }
5897 else // found an end tag
5898 {
5900 {
5901 break; // new code has been pushed back to the scanner, need to reparse
5902 }
5903 retval = handleHtmlEndTag(parser()->context.token->name);
5904 }
5905 if (!retval.is(TokenRetval::RetVal_OK))
5906 {
5907 goto endparagraph;
5908 }
5909 }
5910 break;
5911 case TokenRetval::TK_SYMBOL:
5912 {
5915 {
5917 }
5918 else
5919 {
5921 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
5922 parser()->context.token->name);
5923 }
5924 break;
5925 }
5926 case TokenRetval::TK_NEWPARA:
5927 retval = Token::make_TK_NEWPARA();
5928 goto endparagraph;
5929 case TokenRetval::TK_RCSTAG:
5930 {
5931 const DocNodeVariant *n=parent();
5932 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
5933 !std::holds_alternative<DocParamSect>(*n))
5934 {
5935 n=::parent(n);
5936 }
5937 if (n) // already in a simple section
5938 {
5939 // simple section cannot start in this paragraph, need
5940 // to unwind the stack and remember the command.
5943 retval = Token::make_RetVal_SimpleSec();
5944 goto endparagraph;
5945 }
5946
5947 // see if we are in a simple list
5949 children().get_last<DocSimpleSect>()->parseRcs();
5950 }
5951 break;
5952 default:
5953 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
5954 "Found unexpected token (id={})",tok.to_string());
5955 break;
5956 }
5957 tok=parser()->tokenizer.lex();
5958 }
5959 retval=Token::make_TK_NONE();
5960endparagraph:
5962 DocPara *par = std::get_if<DocPara>(parser()->context.nodeStack.top());
5963 if (!parser()->context.token->endTag && par &&
5964 retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->name.lower() == "p")
5965 {
5966 par->setAttribs(parser()->context.token->attribs);
5967 }
5968 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM,
5969 TokenRetval::TK_ENDLIST,TokenRetval::RetVal_OK)
5970 );
5971
5972 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5973 return retval;
5974}
5975
5976//--------------------------------------------------------------------------
5977
5979{
5980 AUTO_TRACE("start {} level={}", parser()->context.token->sectionId, m_level);
5981 Token retval = Token::make_RetVal_OK();
5982 auto ns = AutoNodeStack(parser(),thisVariant());
5983
5984 if (!m_id.isEmpty())
5985 {
5987 if (sec)
5988 {
5989 m_file = sec->fileName();
5990 m_anchor = sec->label();
5991 QCString titleStr = sec->title();
5992 if (titleStr.isEmpty()) titleStr = sec->label();
5994 DocTitle *title = &std::get<DocTitle>(*m_title);
5995 title->parseFromString(thisVariant(),titleStr);
5996 }
5997 }
5998
5999 // first parse any number of paragraphs
6000 bool isFirst=TRUE;
6001 DocPara *lastPar=nullptr;
6002 do
6003 {
6005 DocPara *par = children().get_last<DocPara>();
6006 if (isFirst) { par->markFirst(); isFirst=FALSE; }
6007 retval=par->parse();
6008 if (!par->isEmpty())
6009 {
6010 if (lastPar) lastPar->markLast(FALSE);
6011 lastPar = par;
6012 }
6013 else
6014 {
6015 children().pop_back();
6016 }
6017 if (retval.is(TokenRetval::TK_LISTITEM))
6018 {
6019 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6020 }
6021 if (retval.is(TokenRetval::RetVal_Internal))
6022 {
6024 retval = children().get_last<DocInternal>()->parse(m_level+1);
6025 if (retval.is(TokenRetval::RetVal_EndInternal))
6026 {
6027 retval = Token::make_RetVal_OK();
6028 }
6029 }
6030 } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6031 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph,
6032 TokenRetval::RetVal_SubSubParagraph, TokenRetval::RetVal_EndInternal)
6033 );
6034
6035 if (lastPar) lastPar->markLast();
6036
6037 while (true)
6038 {
6039 if (retval.is(TokenRetval::RetVal_Subsection) && m_level<=1)
6040 {
6041 // then parse any number of nested sections
6042 while (retval.is(TokenRetval::RetVal_Subsection)) // more sections follow
6043 {
6045 2,
6047 retval = children().get_last<DocSection>()->parse();
6048 }
6049 break;
6050 }
6051 else if (retval.is(TokenRetval::RetVal_Subsubsection) && m_level<=2)
6052 {
6053 if ((m_level <= 1) &&
6054 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6055 {
6056 warn_doc_error(parser()->context.fileName,
6057 parser()->tokenizer.getLineNr(),
6058 "Unexpected subsubsection command found inside {}!",
6060 }
6061 // then parse any number of nested sections
6062 while (retval.is(TokenRetval::RetVal_Subsubsection)) // more sections follow
6063 {
6065 3,
6067 retval = children().get_last<DocSection>()->parse();
6068 }
6069 if (!(m_level < 2 && retval.is(TokenRetval::RetVal_Subsection))) break;
6070 }
6071 else if (retval.is(TokenRetval::RetVal_Paragraph) && m_level<=3)
6072 {
6073 if ((m_level <= 2) &&
6074 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6075 {
6076 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6077 "Unexpected paragraph command found inside {}!",
6079 }
6080 // then parse any number of nested sections
6081 while (retval.is(TokenRetval::RetVal_Paragraph)) // more sections follow
6082 {
6084 4,
6086 retval = children().get_last<DocSection>()->parse();
6087 }
6088 if (!(m_level<3 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection)))) break;
6089 }
6090 else if (retval.is(TokenRetval::RetVal_SubParagraph) && m_level<=4)
6091 {
6092 if ((m_level <= 3) &&
6093 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6094 {
6095 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6096 "Unexpected subparagraph command found inside {}!",
6098 }
6099 // then parse any number of nested sections
6100 while (retval.is(TokenRetval::RetVal_SubParagraph)) // more sections follow
6101 {
6103 5,
6105 retval = children().get_last<DocSection>()->parse();
6106 }
6107 if (!(m_level<4 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection,TokenRetval::RetVal_Paragraph)))) break;
6108 }
6109 else if (retval.is(TokenRetval::RetVal_SubSubParagraph) && m_level<=5)
6110 {
6111 if ((m_level <= 4) &&
6112 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6113 {
6114 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6115 "Unexpected subsubparagraph command found inside {}!",
6117 }
6118 // then parse any number of nested sections
6119 while (retval.is(TokenRetval::RetVal_SubSubParagraph)) // more sections follow
6120 {
6122 6,
6124 retval = children().get_last<DocSection>()->parse();
6125 }
6126 if (!(m_level<5 && (retval.is_any_of( TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection,
6127 TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph)))) break;
6128 }
6129 else
6130 {
6131 break;
6132 }
6133 }
6134
6135 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF,
6136 TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6137 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph,
6138 TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph,
6139 TokenRetval::RetVal_Internal, TokenRetval::RetVal_EndInternal)
6140 );
6141
6142 AUTO_TRACE_EXIT("retval={}", retval.to_string());
6143 return retval;
6144}
6145
6146//--------------------------------------------------------------------------
6147
6149{
6150 AUTO_TRACE();
6151 auto ns = AutoNodeStack(parser(),thisVariant());
6153
6154 Token tok = parser()->tokenizer.lex();
6155 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
6156 {
6157 switch(tok.value())
6158 {
6159 case TokenRetval::TK_WORD:
6161 break;
6162 case TokenRetval::TK_WHITESPACE:
6164 break;
6165 case TokenRetval::TK_SYMBOL:
6166 {
6169 {
6171 }
6172 else
6173 {
6174 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
6175 parser()->context.token->name);
6176 }
6177 }
6178 break;
6179 case TokenRetval::TK_COMMAND_AT:
6180 // fall through
6181 case TokenRetval::TK_COMMAND_BS:
6182 switch (Mappers::cmdMapper->map(parser()->context.token->name))
6183 {
6186 break;
6189 break;
6192 break;
6195 break;
6198 break;
6201 break;
6204 break;
6207 break;
6210 break;
6214 break;
6219 break;
6222 break;
6225 break;
6228 break;
6231 break;
6234 break;
6237 break;
6240 break;
6241 default:
6242 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' found",
6243 parser()->context.token->name);
6244 break;
6245 }
6246 break;
6247 default:
6248 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",
6249 tok.to_string());
6250 break;
6251 }
6252 tok = parser()->tokenizer.lex();
6253 }
6254
6256
6257}
6258
6259
6260//--------------------------------------------------------------------------
6261
6263{
6264 AUTO_TRACE();
6265 auto ns = AutoNodeStack(parser(),thisVariant());
6267 Token retval = Token::make_TK_NONE();
6268
6269 // first parse any number of paragraphs
6270 bool isFirst=TRUE;
6271 DocPara *lastPar = nullptr;
6272 do
6273 {
6274 {
6276 DocPara *par = children().get_last<DocPara>();
6277 if (isFirst) { par->markFirst(); isFirst=FALSE; }
6278 retval=par->parse();
6279 if (par->isEmpty() && par->attribs().empty())
6280 {
6281 children().pop_back();
6282 }
6283 else
6284 {
6285 lastPar = par;
6286 }
6287 }
6288 auto checkParagraph = [this,&retval](Token t,int level,const char *sectionType,const char *parentSectionType) {
6289 if (retval == t)
6290 {
6291 if (!AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6292 {
6293 warn_doc_error(parser()->context.fileName,
6294 parser()->tokenizer.getLineNr(),
6295 "found {} command (id: '{}') outside of {} context!",
6296 sectionType,parser()->context.token->sectionId,parentSectionType);
6297 }
6298 while (retval==t)
6299 {
6300 if (!parser()->context.token->sectionId.isEmpty())
6301 {
6302 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6303 if (sec)
6304 {
6306 level,
6308 retval = children().get_last<DocSection>()->parse();
6309 }
6310 else
6311 {
6312 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid {} id '{}'; ignoring {}",
6313 sectionType,parser()->context.token->sectionId,sectionType);
6314 retval = Token::make_TK_NONE();
6315 }
6316 }
6317 else
6318 {
6319 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for {}; ignoring {}",sectionType,sectionType);
6320 retval = Token::make_TK_NONE();
6321 }
6322 }
6323 }
6324 };
6325 checkParagraph(Token::make_RetVal_SubSubParagraph(), 6, "subsubparagraph", "subparagraph" );
6326 checkParagraph(Token::make_RetVal_SubParagraph(), 5, "subparagraph", "paragraph" );
6327 checkParagraph(Token::make_RetVal_Paragraph(), 4, "paragraph", "subsubsection" );
6328 checkParagraph(Token::make_RetVal_Subsubsection(), 3, "subsubsection", "subsection" );
6329 checkParagraph(Token::make_RetVal_Subsection(), 2, "subsection", "section" );
6330
6331 if (retval.is(TokenRetval::TK_LISTITEM))
6332 {
6333 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6334 }
6335 if (retval.is(TokenRetval::RetVal_Internal))
6336 {
6338 retval = children().get_last<DocInternal>()->parse(1);
6339 }
6340 } while (!retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_Section));
6341 if (lastPar) lastPar->markLast();
6342
6343 //printf("DocRoot::parse() retval=%d %d\n",retval,TokenRetval::RetVal_Section);
6344 // then parse any number of level1 sections
6345 while (retval.is(TokenRetval::RetVal_Section))
6346 {
6347 if (!parser()->context.token->sectionId.isEmpty())
6348 {
6349 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6350 if (sec)
6351 {
6353 1,
6355 retval = children().get_last<DocSection>()->parse();
6356 }
6357 else
6358 {
6359 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid section id '{}'; ignoring section",parser()->context.token->sectionId);
6360 retval = Token::make_TK_NONE();
6361 }
6362 }
6363 else
6364 {
6365 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for section; ignoring section");
6366 retval = Token::make_TK_NONE();
6367 }
6368 }
6369
6371}
bool isAliasCmd(std::string_view aliasCmd)
Definition aliases.cpp:528
static AnchorGenerator & instance()
Returns the singleton instance.
Definition anchor.cpp:38
Citation manager class.
Definition cite.h:85
QCString anchorPrefix() const
Definition cite.cpp:128
const CiteInfo * find(const QCString &label) const
Return the citation info for a given label.
Definition cite.cpp:102
static CitationManager & instance()
Definition cite.cpp:86
QCString fileName() const
Definition cite.cpp:123
static CiteInfoOption makeYear()
Definition cite.h:31
static CiteInfoOption makeNumber()
Definition cite.h:29
void setNoCite()
Definition cite.h:35
bool isUnknown() const
Definition cite.h:37
void changeToNumber()
Definition cite.h:33
static CiteInfoOption makeShortAuthor()
Definition cite.h:30
void setNoPar()
Definition cite.h:34
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:76
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:2945
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:2938
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:2898
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:952
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:916
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:1188
DocDiaFile(DocParser *parser, DocNodeVariant *parent, const QCString &name, const QCString &context, const QCString &srcFile, int srcLine)
Definition docnode.cpp:1181
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:1110
DocDotFile(DocParser *parser, DocNodeVariant *parent, const QCString &name, const QCString &context, const QCString &srcFile, int srcLine)
Definition docnode.cpp:1103
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:1502
Node representing a horizontal ruler.
Definition docnode.h:216
Node representing an HTML blockquote.
Definition docnode.h:1291
HtmlAttribList m_attribs
Definition docnode.h:1238
bool m_hasCaptionId
Definition docnode.h:1239
DocHtmlCaption(DocParser *parser, DocNodeVariant *parent, const HtmlAttribList &attribs)
Definition docnode.cpp:1709
QCString m_file
Definition docnode.h:1240
const HtmlAttribList & attribs() const
Definition docnode.h:1231
QCString m_anchor
Definition docnode.h:1241
Node representing a HTML table cell.
Definition docnode.h:1193
Valignment valignment() const
Definition docnode.cpp:1913
void setColumnIndex(uint32_t idx)
Definition docnode.h:1217
bool isFirst() const
Definition docnode.h:1201
Token parseXml()
Definition docnode.cpp:1817
void setRowIndex(uint32_t idx)
Definition docnode.h:1216
void markLast(bool v=TRUE)
Definition docnode.h:1204
uint32_t rowSpan() const
Definition docnode.cpp:1851
void markFirst(bool v=TRUE)
Definition docnode.h:1203
Alignment alignment() const
Definition docnode.cpp:1875
bool isHeading() const
Definition docnode.h:1200
const HtmlAttribList & attribs() const
Definition docnode.h:1205
Token parse()
Definition docnode.cpp:1783
uint32_t colSpan() const
Definition docnode.cpp:1863
Node representing a HTML description data.
Definition docnode.h:1181
HtmlAttribList m_attribs
Definition docnode.h:1188
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:1492
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:1317
Node representing a Html list.
Definition docnode.h:1000
Type m_type
Definition docnode.h:1011
Token parseXml()
Definition docnode.cpp:2745
Token parse()
Definition docnode.cpp:2670
Node representing a HTML list item.
Definition docnode.h:1165
Node representing a HTML table row.
Definition docnode.h:1246
Token parseXml(bool header)
Definition docnode.cpp:2088
void setVisibleCells(uint32_t n)
Definition docnode.h:1256
bool isHeading() const
Definition docnode.cpp:1935
void setRowIndex(uint32_t idx)
Definition docnode.h:1261
Token parse()
Definition docnode.cpp:1981
Node Html summary.
Definition docnode.h:844
Node representing a HTML table.
Definition docnode.h:1269
Token parseXml()
Definition docnode.cpp:2255
std::unique_ptr< DocNodeVariant > m_caption
Definition docnode.h:1284
Token parse()
Definition docnode.cpp:2168
void computeTableGrid()
determines the location of all cells in a grid, resolving row and column spans.
Definition docnode.cpp:2312
size_t m_numCols
Definition docnode.h:1286
const DocNodeVariant * caption() const
Definition docnode.cpp:2154
bool hasCaption() const
Definition docnode.cpp:2149
const DocNodeVariant * firstRow() const
Definition docnode.cpp:2159
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:1294
std::unique_ptr< Private > p
Definition docnode.h:675
void parse()
Definition docnode.cpp:1309
bool isSVG() const
Definition docnode.cpp:1300
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:1612
Node representing an internal section of documentation.
Definition docnode.h:969
Token parse(int)
Definition docnode.cpp:1552
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:670
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:1141
bool parse()
Definition docnode.cpp:1148
DocNode(DocParser *parser, DocNodeVariant *parent)
Definition docnode.h:85
void setInsidePreformatted(bool p)
Definition docnode.h:109
DocNodeVariant * thisVariant()
Definition docnode.h:93
DocParser * parser()
Definition docnode.h:98
DocNodeVariant * parent()
Definition docnode.h:90
@ Unknown
Definition docnode.h:110
@ Table
Definition docnode.h:110
@ 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:2839
Node representing a paragraph in the documentation tree.
Definition docnode.h:1080
Token handleSimpleSection(DocSimpleSect::Type t, bool xmlContext=FALSE)
Definition docnode.cpp:3367
void handleLink(const QCString &cmdName, bool isJavaLink)
Definition docnode.cpp:3882
void handleInheritDoc()
Definition docnode.cpp:4140
void handleCite(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3420
DocPara(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:3361
void handleInclude(const QCString &cmdName, DocInclude::Type t)
Definition docnode.cpp:3950
Token handleCommand(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:4177
void handleDoxyConfig(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3556
void handleSection(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:4055
void handleFile(const QCString &cmdName)
Definition docnode.cpp:3843
void handleIFile(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3759
Token handleParamSection(const QCString &cmdName, DocParamSect::Type t, bool xmlContext, int direction)
Definition docnode.cpp:3397
void markLast(bool v=TRUE)
Definition docnode.h:1086
Token handleHtmlStartTag(const QCString &tagName, const HtmlAttribList &tagHtmlAttribs)
Definition docnode.cpp:4927
void handleEmoji(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3525
void handleIncludeOperator(const QCString &cmdName, DocIncOperator::Type t)
Definition docnode.cpp:3783
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:3923
void handleILine(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3745
void setAttribs(const HtmlAttribList &attribs)
Definition docnode.h:1115
bool m_isFirst
Definition docnode.h:1118
Token parse()
Definition docnode.cpp:5673
void handleVhdlFlow()
Definition docnode.cpp:3875
Token handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs, int level)
Definition docnode.cpp:4086
void handleShowDate(char cmdChar, const QCString &cmdName)
Definition docnode.cpp:3675
bool m_isLast
Definition docnode.h:1119
Token handleXRefItem()
Definition docnode.cpp:3654
Token handleHtmlEndTag(const QCString &tagName)
Definition docnode.cpp:5400
Token handleStartCode()
Definition docnode.cpp:4104
bool injectToken(Token tok, const QCString &tokText)
Definition docnode.cpp:4097
DocNodeList m_paramTypes
Definition docnode.h:1144
DocNodeList m_paragraphs
Definition docnode.h:1142
void markFirst(bool b=TRUE)
Definition docnode.h:1134
Token parseXml(const QCString &paramName)
Definition docnode.cpp:3250
void markLast(bool b=TRUE)
Definition docnode.h:1135
Token parse(const QCString &cmdName)
Definition docnode.cpp:3171
DocParamSect::Type m_type
Definition docnode.h:1145
DocNodeList m_params
Definition docnode.h:1143
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:3318
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:74
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 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:1220
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
void parse()
Definition docnode.cpp:870
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:705
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:6262
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:5978
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:2883
Node representing a simple list item.
Definition docnode.h:1153
std::unique_ptr< DocNodeVariant > m_paragraph
Definition docnode.h:1160
DocSimpleListItem(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:2864
Node representing a simple section.
Definition docnode.h:1017
QCString typeString() const
Definition docnode.cpp:3142
Type type() const
Definition docnode.h:1026
Token parse(bool userTitle, bool needsSeparator)
Definition docnode.cpp:3032
Token parseRcs()
Definition docnode.cpp:3069
DocSimpleSect(DocParser *parser, DocNodeVariant *parent, Type t)
Definition docnode.cpp:3022
const DocNodeVariant * title() const
Definition docnode.h:1033
Token parseXml()
Definition docnode.cpp:3086
void appendLinkWord(const QCString &word)
Definition docnode.cpp:3122
bool hasTitle() const
Definition docnode.cpp:3027
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:6148
Node representing a simple section title.
Definition docnode.h:608
void parse()
Definition docnode.cpp:2991
void parseFromString(DocNodeVariant *, const QCString &title)
Definition docnode.cpp:3009
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()
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()
int getLineNr(void)
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:1264
void parse()
Definition docnode.cpp:1268
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:110
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:107
static NamespaceDefMutable * globalScope
Definition doxygen.h:121
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:108
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:109
static QCString htmlFileExtension
Definition doxygen.h:122
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:100
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:129
static SearchIndexIntf searchIndex
Definition doxygen.h:124
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1978
int symbol2index(const std::string &symName) const
Returns a code for the requested Emoji entity name.
Definition emoji.cpp:1990
A model of a file symbol.
Definition filedef.h:99
virtual QCString absFilePath() const =0
Class representing a LaTeX formula as found in the documentation.
Definition formula.h:29
QCString text() const
Definition formula.h:37
const Formula * findFormula(int formulaId) const
Definition formula.cpp:705
static FormulaManager & instance()
Definition formula.cpp: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
T & front()
access the first element
Definition growvector.h:130
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
void fill(char c, int len=-1)
Fills a string with a predefined character.
Definition qcstring.h:180
QCString & prepend(const char *s)
Definition qcstring.h:407
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:153
bool startsWith(const char *s) const
Definition qcstring.h:492
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:226
QCString lower() const
Definition qcstring.h:234
bool endsWith(const char *s) const
Definition qcstring.h:509
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:578
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:150
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:245
const std::string & str() const
Definition qcstring.h:537
QCString & append(char c)
Definition qcstring.h:381
QCString right(size_t len) const
Definition qcstring.h:219
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:159
std::string_view view() const
Definition qcstring.h:161
QCString left(size_t len) const
Definition qcstring.h:214
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:57
QCString label() const
Definition section.h:68
QCString ref() const
Definition section.h:71
QCString fileName() const
Definition section.h:73
QCString title() const
Definition section.h:69
SectionType type() const
Definition section.h:70
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:178
static constexpr int Anchor
Definition section.h:40
static constexpr int Table
Definition section.h:41
constexpr int level() const
Definition section.h:45
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:232
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:169
#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:175
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:134
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:47
static const char * g_sectionLevelToName[]
Definition docnode.cpp:56
#define AUTO_TRACE(...)
Definition docnode.cpp:46
static QCString stripKnownExtensions(const QCString &text)
Definition docnode.cpp:103
static void unescapeCRef(QCString &s)
Definition docnode.cpp:81
static const StringUnorderedSet g_plantumlEngine
Definition docnode.cpp:69
#define INTERNAL_ASSERT(x)
Definition docnode.cpp:51
static void flattenParagraphs(DocNodeVariant *root, DocNodeList &children)
Definition docnode.cpp:838
static Token skipSpacesForTable(DocParser *parser)
Definition docnode.cpp:1950
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:48
static bool findAttribute(const HtmlAttribList &tagHtmlAttribs, const char *attrName, QCString *result)
Definition docnode.cpp:4911
std::vector< ActiveRowSpan > RowSpanList
List of ActiveRowSpan classes.
Definition docnode.cpp:2306
static void setParent(DocNodeVariant *n, DocNodeVariant *newParent)
Definition docnode.cpp:118
static bool checkIfHtmlEndTagEndsAutoList(DocParser *parser, const DocNodeVariant *n)
Definition docnode.cpp:5621
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
constexpr bool holds_one_of_alternatives(const DocNodeVariant &v)
returns true iff v holds one of types passed as template parameters
Definition docnode.h:1366
DocNodeList * call_method_children(DocNodeVariant *v)
Definition docnode.h:1385
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:1330
std::unique_ptr< DocNodeVariant > createDocNode(Args &&...args)
Definition docnode.h:1495
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:1932
GroupDef * toGroupDef(Definition *d)
int line
Definition htmlgen.cpp:159
if(macrofile.isEmpty()) return ""
Convert a set of LaTeX commands \‍(re)newcommand to a form readable by MathJax LaTeX syntax: \newcomm...
QCString s
Definition htmlgen.cpp:154
size_t i
Definition htmlgen.cpp:161
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:477
#define qsnprintf
Definition qcstring.h:49
const char * qPrint(const char *s)
Definition qcstring.h:672
#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:2301
uint32_t column
Definition docnode.cpp:2302
ActiveRowSpan(uint32_t rows, uint32_t col)
Definition docnode.cpp:2300
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:829
void append(Args &&... args)
Append a new DocNodeVariant to the list by constructing it with type T and parameters Args.
Definition docnode.h:1399
T * get_last()
Returns a pointer to the last element in the list if that element exists and holds a T,...
Definition docnode.h:1410
StringMultiSet retvalsFound
Definition docparser_p.h:75
bool includeFileShowLineNo
Definition docparser_p.h:89
DocStyleChangeStack styleStack
Definition docparser_p.h:67
size_t includeFileLength
Definition docparser_p.h:87
QCString fileName
Definition docparser_p.h:70
DocNodeStack nodeStack
Definition docparser_p.h:66
StringMultiSet paramsFound
Definition docparser_p.h:76
DefinitionStack copyStack
Definition docparser_p.h:69
QCString exampleName
Definition docparser_p.h:79
const Definition * scope
Definition docparser_p.h:61
QCString includeFileText
Definition docparser_p.h:85
TokenInfo * token
Definition docparser_p.h:92
QCString includeFileName
Definition docparser_p.h:84
size_t includeFileOffset
Definition docparser_p.h:86
const MemberDef * memberDef
Definition docparser_p.h:77
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:3221
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
Definition util.cpp:5719
QCString stripIndentation(const QCString &s, bool skipFirstLine)
Definition util.cpp:6463
QCString showFileDefMatches(const FileNameLinkedMap *fnMap, const QCString &n)
Definition util.cpp:3543
QCString stripScope(const QCString &name)
Definition util.cpp:4293
bool resolveLink(const QCString &scName, const QCString &lr, bool, const Definition **resContext, QCString &resAnchor, SrcLangExt lang, const QCString &prefix)
Definition util.cpp:3247
QCString convertNameToFile(const QCString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:4018
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition util.cpp:7130
QCString stripLeadingAndTrailingEmptyLines(const QCString &s, int &docLine)
Special version of QCString::stripWhiteSpace() that only strips completely blank lines.
Definition util.cpp:5538
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
Definition util.cpp:3417
A bunch of utility functions.