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