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