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// own header
17#include "docnode.h"
18
19// other headers
20#include "aliases.h"
21#include "anchor.h"
22#include "cite.h"
23#include "cmdmapper.h"
24#include "config.h"
25#include "configimpl.h"
26#include "configoptions.h"
27#include "datetime.h"
28#include "docparser_p.h"
29#include "doctokenizer.h"
30#include "doxygen.h"
31#include "emoji.h"
32#include "filedef.h"
33#include "filename.h"
34#include "formula.h"
35#include "groupdef.h"
36#include "htmlentity.h"
37#include "language.h"
38#include "markdown.h"
39#include "mermaid.h"
40#include "message.h"
41#include "namespacedef.h"
42#include "pagedef.h"
43#include "plantuml.h"
44#include "stringutil.h"
45#include "trace.h"
46#include "util.h"
47#include "vhdldocgen.h"
48
49#if !ENABLE_DOCPARSER_TRACING
50#undef AUTO_TRACE
51#undef AUTO_TRACE_ADD
52#undef AUTO_TRACE_EXIT
53#define AUTO_TRACE(...) (void)0
54#define AUTO_TRACE_ADD(...) (void)0
55#define AUTO_TRACE_EXIT(...) (void)0
56#endif
57
58#define INTERNAL_ASSERT(x) do {} while(0)
59//#define INTERNAL_ASSERT(x) if (!(x)) TRACE("INTERNAL_ASSERT({}) failed retval={:#x}: file={} line={}",#x,retval,__FILE__,__LINE__)
60
61//---------------------------------------------------------------------------
62
63static const char *g_sectionLevelToName[] =
64{
65 "page",
66 "section",
67 "subsection",
68 "subsubsection",
69 "paragraph",
70 "subparagraph"
71};
72
73
74//---------------------------------------------------------------------------
75
77 "uml", "bpm", "wire", "dot", "ditaa",
78 "salt", "math", "latex", "gantt", "mindmap",
79 "wbs", "yaml", "creole", "json", "flow",
80 "board", "git", "hcl", "regex", "ebnf",
81 "files", "chen", "chronology", "chart", "nwdiag",
82 "packetdiag", "project", "sprites"
83};
84
85//---------------------------------------------------------------------------
86
87// The file the documentation block being parsed came from, so that symbol
88// lookup can tell a file-scope static in this file from one in another file.
89static const FileDef *currentFileDef(const DocParser *parser)
90{
91 bool ambig = false;
93}
94
95// replaces { with < and } with > and also
96// replaces &lt; with < and &gt; with > within string s
97static void unescapeCRef(DString &s)
98{
99 DString result;
100 const char *p = s.data();
101 if (p)
102 {
103 char c = 0;
104 while ((c=*p++))
105 {
106 if (c=='{') c='<'; else if (c=='}') c='>';
107 result+=c;
108 }
109 }
110
111 result=substitute(result,"&lt;","<");
112 result=substitute(result,"&gt;",">");
113 s = result;
114}
115
116//---------------------------------------------------------------------------
117
118/*! Strips known html and tex extensions from \a text. */
120{
121 DString result=text;
122 if (result.endsWith(".tex"))
123 {
124 result=result.left(result.length()-4);
125 }
127 {
128 result=result.left(result.length()-Doxygen::htmlFileExtension.length());
129 }
130 return result;
131}
132
133static void setParent(DocNodeVariant *n,DocNodeVariant *newParent)
134{
135 std::visit([&](auto &&x)->decltype(auto) { return x.setParent(newParent); }, *n);
136}
137
138//----------- DocStyleChange
139
141{
142 switch (m_style)
143 {
144 case DocStyleChange::Bold: return "b";
145 case DocStyleChange::Italic: return "em";
146 case DocStyleChange::Code: return "code";
147 case DocStyleChange::Center: return "center";
148 case DocStyleChange::Small: return "small";
149 case DocStyleChange::Cite: return "cite";
150 case DocStyleChange::Subscript: return "subscript";
151 case DocStyleChange::Superscript: return "superscript";
152 case DocStyleChange::Preformatted: return "pre";
153 case DocStyleChange::Div: return "div";
154 case DocStyleChange::Span: return "span";
155 case DocStyleChange::Strike: return "strike";
156 case DocStyleChange::S: return "s";
157 case DocStyleChange::Del: return "del";
158 case DocStyleChange::Underline: return "u";
159 case DocStyleChange::Ins: return "ins";
160 case DocStyleChange::Kbd: return "kbd";
161 case DocStyleChange::Typewriter: return "tt";
162 }
163 return "<invalid>";
164}
165
166//----------- DocSymbol
167
172
173//----------- DocEmoji
174
176 DocNode(parser,parent), m_symName(symName), m_index(-1)
177{
178 DString locSymName = symName;
179 size_t len=locSymName.length();
180 if (len>0)
181 {
182 if (locSymName.at(len-1)!=':') locSymName.append(":");
183 if (locSymName.at(0)!=':') locSymName.prepend(":");
184 }
185 m_symName = locSymName;
187 if (m_index==-1)
188 {
189 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Found unsupported emoji symbol '{}'",m_symName);
190 }
191}
192
193//---------------------------------------------------------------------------
194
197{
198 //printf("new word %s url=%s\n",qPrint(word),qPrint(parser->context.searchUrl));
200 {
201 Doxygen::searchIndex.addWord(word,false);
202 }
203}
204
205//---------------------------------------------------------------------------
206
208 const DString &ref,const DString &file,
209 const DString &anchor,const DString &tooltip) :
213{
214 //printf("DocLinkedWord: new word %s url=%s tooltip='%s'\n",
215 // qPrint(word),qPrint(parser->context.searchUrl),qPrint(tooltip));
217 {
218 Doxygen::searchIndex.addWord(word,false);
219 }
220}
221
222//---------------------------------------------------------------------------
223
225{
226 if (id.empty())
227 {
229 return;
230 }
231
233 DString anchorPrefix = ct.anchorPrefix();
234 if (id.left(anchorPrefix.length()) == anchorPrefix)
235 {
236 const CiteInfo *cite = ct.find(id.mid(anchorPrefix.length()));
237 if (cite)
238 {
239 m_file = convertNameToFile(ct.fileName(),false,true);
240 m_anchor = id;
241 }
242 else
243 {
244 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid cite anchor id '{}'",id);
245 m_anchor = "invalid";
246 m_file = "invalid";
247 }
248 }
249 else if (newAnchor) // found <a name="label">
250 {
251 m_anchor = id;
252 }
253 else // found \anchor label
254 {
255 const SectionInfo *sec = SectionManager::instance().find(id);
256 if (sec)
257 {
258 //printf("Found anchor %s\n",qPrint(id));
259 m_file = sec->fileName();
260 m_anchor = sec->label();
261 }
262 else
263 {
264 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid anchor id '{}'",id);
265 m_anchor = "invalid";
266 m_file = "invalid";
267 }
268 }
269}
270
271//---------------------------------------------------------------------------
272
279
280
281//---------------------------------------------------------------------------
282
284{
285 AUTO_TRACE("file={} text={}",m_file,Trace::trunc(m_text));
286 switch(m_type)
287 {
288 case DontIncWithLines:
289 // fall through
290 case IncWithLines:
291 // fall through
292 case Include:
293 // fall through
294 case DontInclude:
303 //printf("parser->context.includeFile=<<%s>>\n",qPrint(parser->context.includeFileText));
304 break;
305 case VerbInclude:
306 // fall through
307 case HtmlInclude:
308 case LatexInclude:
314 break;
315 case Snippet:
316 case SnippetWithLines:
318 // check here for the existence of the blockId inside the file, so we
319 // only generate the warning once.
320 int count = 0;
321 if (!m_blockId.empty() && (count=m_text.contains(m_blockId.data()))!=2)
322 {
323 warn_doc_error(parser()->context.fileName,
324 parser()->tokenizer.getLineNr(),
325 "block marked with {} for \\snippet should appear twice in file {}, found it {:d} times",
326 m_blockId,m_file,count);
327 }
328 break;
329 }
330}
331
332//---------------------------------------------------------------------------
333
335{
336 if (parser()->context.includeFileName.empty())
337 {
338 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
339 "No previous '\\include' or '\\dontinclude' command for '\\{}' present",
340 typeAsString());
341 }
342 bool found = false;
343
345 const char *p = parser()->context.includeFileText.data();
346 size_t l = parser()->context.includeFileLength;
347 size_t o = parser()->context.includeFileOffset;
348 int il = parser()->context.includeFileLine;
349 AUTO_TRACE("text={} off={} len={}",Trace::trunc(p),o,l);
350 size_t so = o, bo = 0;
351 bool nonEmpty = false;
352 switch(type())
353 {
354 case Line:
355 while (o<l)
356 {
357 char c = p[o];
358 if (c=='\n')
359 {
361 if (nonEmpty) break; // we have a pattern to match
362 so=o+1; // no pattern, skip empty line
363 }
364 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
365 {
366 nonEmpty=true;
367 }
368 o++;
369 }
370 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
371 {
372 m_line = il;
374 found = true;
375 AUTO_TRACE_ADD("\\line {}",Trace::trunc(m_text));
376 }
377 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
380 break;
381 case SkipLine:
382 while (o<l)
383 {
384 so=o;
385 while (o<l)
386 {
387 char c = p[o];
388 if (c=='\n')
389 {
391 if (nonEmpty) break; // we have a pattern to match
392 so=o+1; // no pattern, skip empty line
393 }
394 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
395 {
396 nonEmpty=true;
397 }
398 o++;
399 }
400 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
401 {
402 m_line = il;
404 found = true;
405 AUTO_TRACE_ADD("\\skipline {}",Trace::trunc(m_text));
406 break;
407 }
408 o++; // skip new line
409 }
410 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
413 break;
414 case Skip:
415 while (o<l)
416 {
417 so=o;
418 while (o<l)
419 {
420 char c = p[o];
421 if (c=='\n')
422 {
424 if (nonEmpty) break; // we have a pattern to match
425 so=o+1; // no pattern, skip empty line
426 }
427 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
428 {
429 nonEmpty=true;
430 }
431 o++;
432 }
433 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
434 {
435 found = true;
436 break;
437 }
438 o++; // skip new line
439 }
440 parser()->context.includeFileOffset = so; // set pointer to start of new line
443 break;
444 case Until:
445 bo=o;
446 while (o<l)
447 {
448 so=o;
449 while (o<l)
450 {
451 char c = p[o];
452 if (c=='\n')
453 {
455 if (nonEmpty) break; // we have a pattern to match
456 so=o+1; // no pattern, skip empty line
457 }
458 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
459 {
460 nonEmpty=true;
461 }
462 o++;
463 }
464 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
465 {
466 m_line = il;
468 found = true;
469 AUTO_TRACE_ADD("\\until {}",Trace::trunc(m_text));
470 break;
471 }
472 o++; // skip new line
473 }
474 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
477 break;
478 }
479 if (!found)
480 {
481 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
482 "referenced pattern '{}' for command '\\{}' not found",m_pattern,typeAsString());
483 }
484}
485
486//---------------------------------------------------------------------------
487
492
494{
496 if (refList && refList->isEnabled())
497 {
498 RefItem *item = refList->find(m_id);
499 ASSERT(item!=nullptr);
500 if (item)
501 {
502 if (parser()->context.memberDef && parser()->context.memberDef->name().at(0)=='@')
503 {
504 m_file = "@"; // can't cross reference anonymous enum
505 m_anchor = "@";
506 }
507 else
508 {
509 m_file = refList->fileName();
510 m_anchor = item->anchor();
511 }
512 m_title = refList->sectionTitle();
513 //printf("DocXRefItem: file=%s anchor=%s title=%s\n",
514 // qPrint(m_file),qPrint(m_anchor),qPrint(m_title));
515
516 if (!item->text().empty())
517 {
518 DocParser::AutoSaveContext saveContext(*parser());
520 }
521 }
522 return true;
523 }
524 return false;
525}
526
527//---------------------------------------------------------------------------
528
530 m_relPath(parser->context.relPath)
531{
532 const Formula *formula = FormulaManager::instance().findFormula(id);
533 if (formula && !formula->text().empty())
534 {
535 m_id = id;
536 m_name.sprintf("form_%d",m_id);
537 m_text = formula->text();
538 }
539 else // wrong \_form#<n> command
540 {
541 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Wrong formula id {:d}",id);
542 m_id = -1;
543 }
544}
545
546//---------------------------------------------------------------------------
547
552
554{
555 AUTO_TRACE();
556 auto ns = AutoNodeStack(parser(),thisVariant());
557
559 Token tok = parser()->tokenizer.lex();
560 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
561 {
562 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
563 {
564 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"\\refitem");
565 }
566 tok = parser()->tokenizer.lex();
567 }
570
571 if (!m_target.empty())
572 {
574 if (sec==nullptr && parser()->context.lang==SrcLangExt::Markdown) // lookup as markdown file
575 {
577 }
578 if (sec) // ref to section or anchor
579 {
580 // set defaults
581 m_ref = sec->ref();
584 m_anchor = sec->label();
585 m_isSubPage = false;
586 // adjust if needed
587 switch (sec->type().level())
588 {
590 {
592 m_isSubPage = pd && pd->hasParentPage();
593 if (!m_isSubPage)
594 {
595 m_anchor="";
596 }
597 }
598 break;
601 break;
604 break;
607 break;
608 default:
609 break;
610 }
611 //printf("m_ref=%s,m_file=%s,type=%d\n",
612 // qPrint(m_ref),qPrint(m_file),m_refType);
613 }
614 else
615 {
616 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to unknown section {}",m_target);
617 }
618 }
619 else
620 {
621 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to empty target");
622 }
623}
624
625//---------------------------------------------------------------------------
626
628{
629 AUTO_TRACE();
630 auto ns = AutoNodeStack(parser(),thisVariant());
631
632 Token tok=parser()->tokenizer.lex();
633 // skip white space
634 while (tok.is_any_of(TokenRetval::TK_WHITESPACE, TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
635 // handle items
636 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
637 {
638 if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS))
639 {
640 switch (Mappers::cmdMapper->map(parser()->context.token->name))
641 {
643 {
644 tok=parser()->tokenizer.lex();
645 if (!tok.is(TokenRetval::TK_WHITESPACE))
646 {
647 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\refitem command");
648 break;
649 }
650 tok=parser()->tokenizer.lex();
651 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
652 {
653 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\refitem",
654 tok.to_string());
655 break;
656 }
657
660 }
661 break;
663 return;
664 default:
665 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' as part of a \\secreflist",
666 tok.command_to_char(),qPrint(parser()->context.token->name));
667 return;
668 }
669 }
670 else if (tok.is(TokenRetval::TK_WHITESPACE))
671 {
672 // ignore whitespace
673 }
674 else
675 {
676 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {} inside section reference list",
677 tok.to_string());
678 return;
679 }
680 tok=parser()->tokenizer.lex();
681 }
682
683}
684
685//---------------------------------------------------------------------------
686
689{
690 size_t i=ref.find('#');
691 if (i!=DString::npos)
692 {
693 m_anchor = ref.mid(i+1);
694 m_file = ref.left(i);
695 }
696 else
697 {
698 m_file = ref;
699 }
700}
701
703{
704 AUTO_TRACE();
705 auto ns = AutoNodeStack(parser(),thisVariant());
706
707 Token tok = parser()->tokenizer.lex();
708 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
709 {
710 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
711 {
713 }
714 tok=parser()->tokenizer.lex();
715 }
716
718}
719
720//---------------------------------------------------------------------------
721
724{
725 const Definition *compound = nullptr;
727 AUTO_TRACE("target='{}',context='{}'",target,context);
728 ASSERT(!target.empty());
730 auto lang = parser->context.lang;
732 if (sec==nullptr && !parser->context.prefix.empty())
733 {
734 sec = SectionManager::instance().find(target);
735 }
736
737 if (sec==nullptr && getLanguageFromFileName(target)==SrcLangExt::Markdown) // lookup as markdown file
738 {
740 }
741 if (sec) // ref to section or anchor
742 {
743 PageDef *pd = nullptr;
744 int secLevel = sec->type().level();
745 if (secLevel==SectionType::Page)
746 {
747 pd = Doxygen::pageLinkedMap->find(target);
748 }
749 m_text = sec->title();
750 if (m_text.empty()) m_text = sec->label();
751
752 m_ref = sec->ref();
754 if (secLevel==SectionType::Anchor)
755 {
757 }
758 else if (secLevel==SectionType::Table)
759 {
761 }
762 else if (secLevel==SectionType::Requirement)
763 {
765 if (!Config_getBool(GENERATE_REQUIREMENTS))
766 {
767 m_file.clear(); // prevent link when there is no requirements page
768 }
769 }
770 else
771 {
773 }
774 m_isSubPage = pd && pd->hasParentPage();
775 if (secLevel!=SectionType::Page || m_isSubPage)
776 {
777 m_anchor = pd ? pd->getOutputFileBase() : sec->label();
778 }
779 m_sectionType = sec->type();
780 //printf("m_text=%s,m_ref=%s,m_file=%s,type=%d\n",
781 // qPrint(m_text),qPrint(m_ref),qPrint(m_file),m_refType);
782 AUTO_TRACE_EXIT("section");
783 return;
784 }
785 else if (Config_getBool(IMPLICIT_DIR_DOCS) && target.lower().endsWith("/readme.md"))
786 {
787 DString dirTarget = target.left(target.length() - 9); // strip readme.md part
788 const auto &dd = Doxygen::dirLinkedMap->find(dirTarget);
789 if (dd)
790 {
791 m_text = target;
792 m_file = dd->getOutputFileBase();
793 AUTO_TRACE_EXIT("directory");
794 return;
795 }
796 }
797 else if (resolveLink(context,target,true,&compound,anchor,lang,parser->context.prefix,
799 {
800 bool isFile = compound ?
801 (compound->definitionType()==Definition::TypeFile ||
802 compound->definitionType()==Definition::TypePage ? true : false) :
803 false;
804 bool isDir = compound && compound->definitionType()==Definition::TypeDir;
805 if (compound && lang==SrcLangExt::Markdown) lang = compound->getLanguage();
806 m_text = linkToText(lang,target,isFile || isDir);
808 if (compound && compound->isLinkable()) // ref to compound
809 {
810 if (anchor.empty() && /* compound link */
811 compound->definitionType()==Definition::TypeGroup && /* is group */
812 !toGroupDef(compound)->groupTitle().empty() /* with title */
813 )
814 {
815 m_text=(toGroupDef(compound))->groupTitle(); // use group's title as link
816 }
817 else if (compound->definitionType()==Definition::TypeMember &&
818 toMemberDef(compound)->isObjCMethod())
819 {
820 // Objective C Method
821 const MemberDef *member = toMemberDef(compound);
822 bool localLink = parser->context.memberDef ? member->getClassDef()==parser->context.memberDef->getClassDef() : false;
823 m_text = member->objCMethodName(localLink,parser->context.inSeeBlock);
824 }
825 else if (Config_getBool(HIDE_SCOPE_NAMES))
826 {
827 size_t funcPos = m_text.find('(');
828 if (funcPos!=DString::npos) // see issue #11834
829 {
830 m_text=stripScope(m_text.left(funcPos))+m_text.mid(funcPos);
831 }
832 else
833 {
835 }
836 }
837
838 m_file = compound->getOutputFileBase();
839 m_ref = compound->getReference();
840 //printf("isFile=%d compound=%s (%d)\n",isFile,qPrint(compound->name()),
841 // compound->definitionType());
842 AUTO_TRACE_EXIT("compound");
843 return;
844 }
845 else if (compound && compound->definitionType()==Definition::TypeFile &&
846 toFileDef(compound)->generateSourceFile()
847 ) // undocumented file that has source code we can link to
848 {
849 m_file = compound->getSourceFileBase();
850 m_ref = compound->getReference();
851 AUTO_TRACE_EXIT("source");
852 return;
853 }
854 else
855 {
856 AUTO_TRACE_EXIT("compound '{}' not linkable",compound?compound->name():"none");
857 }
858 }
859 m_text = target;
860 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve reference to '{}' for \\ref command",
861 target);
862}
863
865{
866 for (auto &&elem : elements)
867 {
868 emplace_back(std::move(elem));
869 }
870 elements.clear();
871}
872
873static void flattenParagraphs(DocNodeVariant *root,DocNodeList &children)
874{
875 DocNodeList newChildren;
876 for (auto &dn : children)
877 {
878 DocPara *para = std::get_if<DocPara>(&dn);
879 if (para)
880 {
881 //// move the children of the paragraph to the end of the newChildren list
882 newChildren.move_append(para->children());
883 }
884 }
885
886 // replace the children list by the newChildren list
887 children.clear();
888 children.move_append(newChildren);
889 // reparent the children
890 for (auto &cn : children)
891 {
892 setParent(&cn,root);
893 // we also need to set the parent for each child of cn, as cn's address may have changed.
894 auto opt_children = call_method_children(&cn);
895 if (opt_children)
896 {
897 for (auto &ccn : *opt_children)
898 {
899 setParent(&ccn,&cn);
900 }
901 }
902 }
903}
904
905void DocRef::parse(char cmdChar,const DString &cmdName)
906{
907 AUTO_TRACE();
908 auto ns = AutoNodeStack(parser(),thisVariant());
909 char cmdCharStr[2] = { cmdChar, 0 };
910
911 Token tok = parser()->tokenizer.lex();
912 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
913 {
914 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
915 {
916 switch (tok.value())
917 {
918 case TokenRetval::TK_HTMLTAG:
919 break;
920 default:
921 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),cmdCharStr+cmdName);
922 break;
923 }
924 }
925 tok=parser()->tokenizer.lex();
926 }
927
928 if (children().empty() && !m_text.empty())
929 {
930 DString text = m_text;
931 if (parser()->context.insideHtmlLink)
932 {
933 // we already in a link/title only output anchor
934 text = m_anchor;
935 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
936 "Potential recursion while resolving {:c}{} command!",cmdChar,cmdName);
937 }
939 { DocParser::AutoSaveContext saveContext(*parser());
941 }
945 }
946
948}
949
950//---------------------------------------------------------------------------
951
953{
954 size_t numBibFiles = Config_getList(CITE_BIB_FILES).size();
955 //printf("DocCite::DocCite(target=%s)\n",qPrint(target));
956 ASSERT(!target.empty());
959 const CiteInfo *cite = ct.find(target);
960 //printf("cite=%p text='%s' numBibFiles=%d\n",cite,cite?qPrint(cite->text):"<null>",numBibFiles);
961 m_option = opt;
963 if (numBibFiles>0 && cite && !cite->text().empty()) // ref to citation
964 {
965 m_ref = "";
966 m_anchor = ct.anchorPrefix()+cite->label();
967 m_file = convertNameToFile(ct.fileName(),false,true);
968 //printf("CITE ==> m_text=%s,m_ref=%s,m_file=%s,m_anchor=%s\n",
969 // qPrint(m_text),qPrint(m_ref),qPrint(m_file),qPrint(m_anchor));
970 return;
971 }
972 if (numBibFiles==0)
973 {
974 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"\\cite command found but no bib files specified via CITE_BIB_FILES!");
975 }
976 else if (cite==nullptr)
977 {
978 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve reference to '{}' for \\cite command",
979 target);
980 }
981 else
982 {
983 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"\\cite command to '{}' does not have an associated number",
984 target);
985 }
986}
987
989{
990 DString txt;
991 auto opt = m_option;
993 const CiteInfo *citeInfo = ct.find(m_target);
994
995 if (!opt.noPar()) txt += "[";
996
997 if (citeInfo)
998 {
999 if (opt.isNumber()) txt += citeInfo->text();
1000 else if (opt.isShortAuthor()) txt += citeInfo->shortAuthor();
1001 else if (opt.isYear()) txt += citeInfo->year();
1002 }
1003
1004 if (!opt.noPar()) txt += "]";
1005 return txt;
1006}
1007
1008
1009//---------------------------------------------------------------------------
1010
1012{
1013 const Definition *compound = nullptr;
1015 m_refText = target;
1017 if (!m_refText.empty() && m_refText.at(0)=='#')
1018 {
1019 m_refText = m_refText.mid(1);
1020 }
1022 parser->context.inSeeBlock,&compound,anchor,
1025 {
1026 m_anchor = anchor;
1027 if (compound && compound->isLinkable())
1028 {
1029 m_file = compound->getOutputFileBase();
1030 m_ref = compound->getReference();
1031 }
1032 else if (compound && compound->definitionType()==Definition::TypeFile &&
1033 (toFileDef(compound))->generateSourceFile()
1034 ) // undocumented file that has source code we can link to
1035 {
1036 m_file = compound->getSourceFileBase();
1037 m_ref = compound->getReference();
1038 }
1039 return;
1040 }
1041
1042 // bogus link target
1043 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve link to '{}' for \\link command",
1044 target);
1045}
1046
1047
1048DString DocLink::parse(bool isJavaLink,bool isXmlLink)
1049{
1050 AUTO_TRACE();
1051 DString result;
1052 auto ns = AutoNodeStack(parser(),thisVariant());
1053
1054 Token tok = parser()->tokenizer.lex();
1055 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1056 {
1057 if (!parser()->defaultHandleToken(thisVariant(),tok,children(),false))
1058 {
1059 switch (tok.value())
1060 {
1061 case TokenRetval::TK_COMMAND_AT:
1062 // fall through
1063 case TokenRetval::TK_COMMAND_BS:
1064 switch (Mappers::cmdMapper->map(parser()->context.token->name))
1065 {
1067 if (isJavaLink)
1068 {
1069 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"'{{@link...' ended with '{:c}{}' command",
1070 tok.command_to_char(),parser()->context.token->name);
1071 }
1072 goto endlink;
1073 default:
1074 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' as part of a \\link",
1075 tok.command_to_char(),parser()->context.token->name);
1076 break;
1077 }
1078 break;
1079 case TokenRetval::TK_SYMBOL:
1080 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a \\link",
1081 parser()->context.token->name);
1082 break;
1083 case TokenRetval::TK_HTMLTAG:
1084 if (parser()->context.token->name!="see" || !isXmlLink)
1085 {
1086 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected xml/html command {} found as part of a \\link",
1087 parser()->context.token->name);
1088 }
1089 goto endlink;
1090 case TokenRetval::TK_LNKWORD:
1091 case TokenRetval::TK_WORD:
1092 if (isJavaLink) // special case to detect closing }
1093 {
1095 size_t p = 0;
1096 if (w=="}")
1097 {
1098 goto endlink;
1099 }
1100 else if ((p=w.find('}'))!=DString::npos)
1101 {
1102 size_t l = w.length();
1104 if (l>1 && p<l-1) // something left after the } (for instance a .)
1105 {
1106 result=w.right(l-p-1);
1107 }
1108 goto endlink;
1109 }
1110 }
1112 break;
1113 default:
1114 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",tok.to_string());
1115 break;
1116 }
1117 }
1118 tok = parser()->tokenizer.lex();
1119 }
1120 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1121 {
1122 warn_doc_error(parser()->context.fileName,
1123 parser()->tokenizer.getLineNr(),
1124 "Unexpected end of comment while inside link command");
1125 }
1126endlink:
1127
1128 if (children().empty()) // no link text
1129 {
1131 }
1132
1134 return result;
1135}
1136
1137
1138//---------------------------------------------------------------------------
1139
1146
1148{
1149 bool ok = false;
1151
1152 bool ambig = false;
1154 if (fd==nullptr && !p->name.endsWith(".dot")) // try with .dot extension as well
1155 {
1156 fd = Doxygen::dotFileNameLinkedMap->findFileDef(p->name+".dot",ambig);
1157 }
1158 if (fd)
1159 {
1160 p->file = fd->absFilePath();
1161 ok = true;
1162 if (ambig)
1163 {
1164 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dot file name '{}' is ambiguous.\n"
1165 "Possible candidates:\n{}",p->name,
1167 );
1168 }
1169 }
1170 else
1171 {
1172 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dot file '{}' is not found "
1173 "in any of the paths specified via DOTFILE_DIRS!",p->name);
1174 }
1175 return ok;
1176}
1177
1184
1186{
1187 bool ok = false;
1189
1190 bool ambig = false;
1192 if (fd==nullptr && !p->name.endsWith(".msc")) // try with .msc extension as well
1193 {
1194 fd = Doxygen::mscFileNameLinkedMap->findFileDef(p->name+".msc",ambig);
1195 }
1196 if (fd)
1197 {
1198 p->file = fd->absFilePath();
1199 ok = true;
1200 if (ambig)
1201 {
1202 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included msc file name '{}' is ambiguous.\n"
1203 "Possible candidates:\n{}",p->name,
1205 );
1206 }
1207 }
1208 else
1209 {
1210 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included msc file '{}' is not found "
1211 "in any of the paths specified via MSCFILE_DIRS!",p->name);
1212 }
1213 return ok;
1214}
1215
1216//---------------------------------------------------------------------------
1217
1224
1226{
1227 bool ok = false;
1229
1230 bool ambig = false;
1232 if (fd==nullptr && !p->name.endsWith(".dia")) // try with .dia extension as well
1233 {
1234 fd = Doxygen::diaFileNameLinkedMap->findFileDef(p->name+".dia",ambig);
1235 }
1236 if (fd)
1237 {
1238 p->file = fd->absFilePath();
1239 ok = true;
1240 if (ambig)
1241 {
1242 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dia file name '{}' is ambiguous.\n"
1243 "Possible candidates:\n{}",p->name,
1245 );
1246 }
1247 }
1248 else
1249 {
1250 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dia file '{}' is not found "
1251 "in any of the paths specified via DIAFILE_DIRS!",p->name);
1252 }
1253 return ok;
1254}
1255//---------------------------------------------------------------------------
1256
1263
1265{
1266 bool ok = false;
1268
1269 bool ambig = false;
1271 if (fd==nullptr && !p->name.endsWith(".puml")) // try with .puml extension as well
1272 {
1273 fd = Doxygen::plantUmlFileNameLinkedMap->findFileDef(p->name+".puml",ambig);
1274 if (fd==nullptr && !p->name.endsWith(".pu")) // try with .pu extension as well
1275 {
1276 fd = Doxygen::plantUmlFileNameLinkedMap->findFileDef(p->name+".pu",ambig);
1277 }
1278 }
1279 if (fd)
1280 {
1281 p->file = fd->absFilePath();
1282 ok = true;
1283 if (ambig)
1284 {
1285 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included uml file name '{}' is ambiguous.\n"
1286 "Possible candidates:\n{}",p->name,
1288 );
1289 }
1290 }
1291 else
1292 {
1293 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included uml file '{}' is not found "
1294 "in any of the paths specified via PLANTUMLFILE_DIRS!",p->name);
1295 }
1296 return ok;
1297}
1298
1299//---------------------------------------------------------------------------
1300
1307
1309{
1310 bool ok = false;
1312
1313 bool ambig = false;
1315 if (fd==nullptr && !p->name.endsWith(".mmd")) // try with .mmd extension as well
1316 {
1317 fd = Doxygen::mermaidFileNameLinkedMap->findFileDef(p->name+".mmd",ambig);
1318 }
1319 if (fd)
1320 {
1321 p->file = fd->absFilePath();
1322 ok = true;
1323 if (ambig)
1324 {
1325 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included mermaid file name '{}' is ambiguous.\n"
1326 "Possible candidates:\n{}",p->name,
1328 );
1329 }
1330 }
1331 else
1332 {
1333 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included mermaid file '{}' is not found "
1334 "in any of the paths specified via MERMAIDFILE_DIRS!",p->name);
1335 }
1336 return ok;
1337}
1338
1339//---------------------------------------------------------------------------
1340
1344
1346{
1347 AUTO_TRACE();
1348 auto ns = AutoNodeStack(parser(),thisVariant());
1349
1351 Token tok = parser()->tokenizer.lex();
1352 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1353 {
1354 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1355 {
1356 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"\\vhdlflow");
1357 }
1358 tok = parser()->tokenizer.lex();
1359 }
1360 parser()->tokenizer.lex();
1361
1364
1365 VhdlDocGen::createFlowChart(parser()->context.memberDef);
1366}
1367
1368
1369//---------------------------------------------------------------------------
1370
1372 Type t,const DString &url, bool inlineImage) :
1373 DocCompoundNode(parser,parent), p(std::make_unique<Private>(attribs, name, t, parser->context.relPath, url, inlineImage))
1374{
1375}
1376
1378{
1379 DString locName = p->url.empty() ? p->name : p->url;
1380 size_t len = locName.length();
1381 size_t fnd = locName.find('?'); // ignore part from ? until end
1382 if (fnd==DString::npos) fnd=len;
1383 return fnd>=4 && locName.mid(fnd-4,4)==".svg";
1384}
1385
1390
1391
1392//---------------------------------------------------------------------------
1393
1395{
1396 AUTO_TRACE();
1397 Token retval(TokenRetval::RetVal_OK);
1398 auto ns = AutoNodeStack(parser(),thisVariant());
1399
1400 Token tok = parser()->tokenizer.lex();
1401 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1402 {
1403 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1404 {
1405 switch (tok.value())
1406 {
1407 case TokenRetval::TK_HTMLTAG:
1408 {
1409 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1410 if (tagId==HtmlTagType::HTML_H1 && parser()->context.token->endTag) // found </h1> tag
1411 {
1412 if (m_level!=1)
1413 {
1414 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h1>",
1415 m_level);
1416 }
1417 goto endheader;
1418 }
1419 else if (tagId==HtmlTagType::HTML_H2 && parser()->context.token->endTag) // found </h2> tag
1420 {
1421 if (m_level!=2)
1422 {
1423 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h2>",
1424 m_level);
1425 }
1426 goto endheader;
1427 }
1428 else if (tagId==HtmlTagType::HTML_H3 && parser()->context.token->endTag) // found </h3> tag
1429 {
1430 if (m_level!=3)
1431 {
1432 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h3>",
1433 m_level);
1434 }
1435 goto endheader;
1436 }
1437 else if (tagId==HtmlTagType::HTML_H4 && parser()->context.token->endTag) // found </h4> tag
1438 {
1439 if (m_level!=4)
1440 {
1441 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h4>",
1442 m_level);
1443 }
1444 goto endheader;
1445 }
1446 else if (tagId==HtmlTagType::HTML_H5 && parser()->context.token->endTag) // found </h5> tag
1447 {
1448 if (m_level!=5)
1449 {
1450 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h5>",
1451 m_level);
1452 }
1453 goto endheader;
1454 }
1455 else if (tagId==HtmlTagType::HTML_H6 && parser()->context.token->endTag) // found </h6> tag
1456 {
1457 if (m_level!=6)
1458 {
1459 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h6>",
1460 m_level);
1461 }
1462 goto endheader;
1463 }
1464 else if (tagId==HtmlTagType::HTML_A)
1465 {
1466 if (!parser()->context.token->endTag)
1467 {
1468 parser()->handleAHref(thisVariant(),children(),parser()->context.token->attribs);
1469 }
1470 }
1471 else if (tagId==HtmlTagType::HTML_BR)
1472 {
1474 }
1475 else
1476 {
1477 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <h{:d}> context",
1478 parser()->context.token->endTag?"/":"",parser()->context.token->name,m_level);
1479 }
1480 }
1481 break;
1482 default:
1483 char tmp[20];
1484 snprintf(tmp,20,"<h%d> tag",m_level);
1486 }
1487 }
1488 tok = parser()->tokenizer.lex();
1489 }
1490 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1491 {
1492 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1493 " <h{:d}> tag",m_level);
1494 }
1495endheader:
1497 return retval;
1498}
1499//---------------------------------------------------------------------------
1500
1502{
1503 AUTO_TRACE();
1504 auto ns = AutoNodeStack(parser(),thisVariant());
1506 Token tok = parser()->tokenizer.lex();
1507 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1508 {
1509 // check of </summary>
1510 if (tok.value()==TokenRetval::TK_HTMLTAG &&
1511 (Mappers::htmlTagMapper->map(parser()->context.token->name))==HtmlTagType::XML_SUMMARY &&
1512 parser()->context.token->endTag
1513 )
1514 {
1515 break;
1516 }
1517 else if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS) &&
1518 (Mappers::cmdMapper->map(parser()->context.token->name)== CommandType::CMD_REF))
1519 {
1521 tok.command_to_char(),parser()->context.token->name);
1522 }
1523 else if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1524 {
1525 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"summary section");
1526 }
1527 tok = parser()->tokenizer.lex();
1528 }
1530 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1531 {
1532 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1533 " <summary> tag");
1534 }
1535}
1536
1537//---------------------------------------------------------------------------
1538
1540{
1541 AUTO_TRACE();
1542 Token retval(TokenRetval::TK_NONE);
1543 auto ns = AutoNodeStack(parser(),thisVariant());
1544
1545 // parse one or more paragraphs
1546 bool isFirst=true;
1547 DocPara *par=nullptr;
1548 do
1549 {
1551 par = children().get_last<DocPara>();
1552 if (isFirst) { par->markFirst(); isFirst=false; }
1553 retval=par->parse();
1554 }
1555 while (retval.is(TokenRetval::TK_NEWPARA));
1556 if (par) par->markLast();
1557
1558 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1559 {
1560 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <details> block");
1561 }
1562
1563 if (!summary())
1564 {
1565 HtmlAttribList summaryAttribs;
1567 DocHtmlSummary *summary = &std::get<DocHtmlSummary>(*m_summary);
1569 }
1570 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1571 return retval.is(TokenRetval::RetVal_EndHtmlDetails) ? Token::make_RetVal_OK() : retval;
1572}
1573
1581
1582//---------------------------------------------------------------------------
1583
1585{
1586 AUTO_TRACE();
1587 Token retval(TokenRetval::RetVal_OK);
1588 auto ns = AutoNodeStack(parser(),thisVariant());
1589
1590 Token tok = parser()->tokenizer.lex();
1591 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1592 {
1593 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1594 {
1595 switch (tok.value())
1596 {
1597 case TokenRetval::TK_HTMLTAG:
1598 {
1599 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1600 if (tagId==HtmlTagType::HTML_A && parser()->context.token->endTag) // found </a> tag
1601 {
1602 goto endhref;
1603 }
1604 else if (tagId==HtmlTagType::HTML_BR)
1605 {
1607 }
1608 else
1609 {
1610 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <a href=...> context",
1611 parser()->context.token->endTag?"/":"",parser()->context.token->name);
1612 }
1613 }
1614 break;
1615 default:
1616 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"<a>..</a> block");
1617 break;
1618 }
1619 }
1620 tok = parser()->tokenizer.lex();
1621 }
1622 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1623 {
1624 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1625 " <a href=...> tag");
1626 }
1627endhref:
1629 return retval;
1630}
1631
1632//---------------------------------------------------------------------------
1633
1635{
1636 AUTO_TRACE();
1637 Token retval(TokenRetval::RetVal_OK);
1638 auto ns = AutoNodeStack(parser(),thisVariant());
1639
1640 // first parse any number of paragraphs
1641 bool isFirst=true;
1642 DocPara *lastPar=nullptr;
1643 do
1644 {
1646 DocPara *par = children().get_last<DocPara>();
1647 if (isFirst) { par->markFirst(); isFirst=false; }
1648 retval=par->parse();
1649 if (!par->empty())
1650 {
1651 if (lastPar) lastPar->markLast(false);
1652 lastPar=par;
1653 }
1654 else
1655 {
1656 children().pop_back();
1657 }
1658 if (retval.is(TokenRetval::TK_LISTITEM))
1659 {
1660 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
1661 }
1662 } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF,
1663 TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection,
1664 TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph,
1665 TokenRetval::RetVal_EndInternal));
1666 if (lastPar) lastPar->markLast();
1667
1668 // then parse any number of level-n sections
1669 while ((level==1 && retval.is(TokenRetval::RetVal_Section)) ||
1670 (level==2 && retval.is(TokenRetval::RetVal_Subsection)) ||
1671 (level==3 && retval.is(TokenRetval::RetVal_Subsubsection)) ||
1672 (level==4 && retval.is(TokenRetval::RetVal_Paragraph)) ||
1673 (level==5 && retval.is(TokenRetval::RetVal_SubParagraph)) ||
1674 (level==6 && retval.is(TokenRetval::RetVal_SubSubParagraph))
1675 )
1676 {
1678 level,
1680 retval = children().get_last<DocSection>()->parse();
1681 }
1682
1683 if (retval.is(TokenRetval::RetVal_Internal))
1684 {
1685 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"\\internal command found inside internal section");
1686 }
1687
1688 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1689 return retval;
1690}
1691
1692//---------------------------------------------------------------------------
1693
1695{
1696 AUTO_TRACE();
1697 Token retval(TokenRetval::RetVal_OK);
1698 auto ns = AutoNodeStack(parser(),thisVariant());
1699 Token tok=parser()->tokenizer.lex();
1700 if (!tok.is(TokenRetval::TK_WHITESPACE))
1701 {
1702 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\addindex command");
1703 goto endindexentry;
1704 }
1706 m_entry="";
1707 tok = parser()->tokenizer.lex();
1708 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1709 {
1710 switch (tok.value())
1711 {
1712 case TokenRetval::TK_WHITESPACE:
1713 m_entry+=" ";
1714 break;
1715 case TokenRetval::TK_WORD:
1716 case TokenRetval::TK_LNKWORD:
1718 break;
1719 case TokenRetval::TK_SYMBOL:
1720 {
1721 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
1722 switch (s)
1723 {
1724 case HtmlEntityMapper::Sym_BSlash: m_entry+='\\'; break;
1725 case HtmlEntityMapper::Sym_At: m_entry+='@'; break;
1726 case HtmlEntityMapper::Sym_Less: m_entry+='<'; break;
1727 case HtmlEntityMapper::Sym_Greater: m_entry+='>'; break;
1728 case HtmlEntityMapper::Sym_Amp: m_entry+='&'; break;
1729 case HtmlEntityMapper::Sym_Dollar: m_entry+='$'; break;
1730 case HtmlEntityMapper::Sym_Hash: m_entry+='#'; break;
1731 case HtmlEntityMapper::Sym_Percent: m_entry+='%'; break;
1732 case HtmlEntityMapper::Sym_apos: m_entry+='\''; break;
1733 case HtmlEntityMapper::Sym_Quot: m_entry+='"'; break;
1734 case HtmlEntityMapper::Sym_lsquo: m_entry+='`'; break;
1735 case HtmlEntityMapper::Sym_rsquo: m_entry+='\''; break;
1736 case HtmlEntityMapper::Sym_ldquo: m_entry+="``"; break;
1737 case HtmlEntityMapper::Sym_rdquo: m_entry+="''"; break;
1738 case HtmlEntityMapper::Sym_ndash: m_entry+="--"; break;
1739 case HtmlEntityMapper::Sym_mdash: m_entry+="---"; break;
1740 default:
1741 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected symbol '{}' found as argument of \\addindex",parser()->context.token->name);
1742 break;
1743 }
1744 }
1745 break;
1746 case TokenRetval::TK_COMMAND_AT:
1747 // fall through
1748 case TokenRetval::TK_COMMAND_BS:
1749 switch (Mappers::cmdMapper->map(parser()->context.token->name))
1750 {
1751 case CommandType::CMD_BSLASH: m_entry+='\\'; break;
1752 case CommandType::CMD_AT: m_entry+='@'; break;
1753 case CommandType::CMD_LESS: m_entry+='<'; break;
1754 case CommandType::CMD_GREATER: m_entry+='>'; break;
1755 case CommandType::CMD_AMP: m_entry+='&'; break;
1756 case CommandType::CMD_DOLLAR: m_entry+='$'; break;
1757 case CommandType::CMD_HASH: m_entry+='#'; break;
1758 case CommandType::CMD_DCOLON: m_entry+="::"; break;
1759 case CommandType::CMD_PERCENT: m_entry+='%'; break;
1760 case CommandType::CMD_NDASH: m_entry+="--"; break;
1761 case CommandType::CMD_MDASH: m_entry+="---"; break;
1762 case CommandType::CMD_QUOTE: m_entry+='"'; break;
1763 case CommandType::CMD_PUNT: m_entry+='.'; break;
1764 case CommandType::CMD_PLUS: m_entry+='+'; break;
1765 case CommandType::CMD_MINUS: m_entry+='-'; break;
1766 case CommandType::CMD_EQUAL: m_entry+='='; break;
1767 case CommandType::CMD_EXCLAMATION: m_entry+='!'; break;
1768 case CommandType::CMD_QUESTION: m_entry+='?'; break;
1769 default:
1770 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command {} found as argument of \\addindex",
1771 parser()->context.token->name);
1772 break;
1773 }
1774 break;
1775 default:
1776 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",
1777 tok.to_string());
1778 break;
1779 }
1780 tok = parser()->tokenizer.lex();
1781 }
1784endindexentry:
1785 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1786 return retval;
1787}
1788
1789//---------------------------------------------------------------------------
1790
1793{
1794 m_hasCaptionId = false;
1795 for (const auto &opt : attribs)
1796 {
1797 if (opt.name=="id" && !opt.value.empty()) // interpret id attribute as an anchor
1798 {
1799 const SectionInfo *sec = SectionManager::instance().find(opt.value);
1800 if (sec)
1801 {
1802 //printf("Found anchor %s\n",qPrint(id));
1803 m_file = sec->fileName();
1804 m_anchor = sec->label();
1805 m_hasCaptionId = true;
1806 }
1807 else
1808 {
1809 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid caption id '{}'",opt.value);
1810 }
1811 }
1812 else // copy attribute
1813 {
1814 m_attribs.push_back(opt);
1815 }
1816 }
1817}
1818
1820{
1821 AUTO_TRACE();
1822 Token retval = Token::make_TK_NONE();
1823 auto ns = AutoNodeStack(parser(),thisVariant());
1824 Token tok = parser()->tokenizer.lex();
1825 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1826 {
1827 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1828 {
1829 switch (tok.value())
1830 {
1831 case TokenRetval::TK_HTMLTAG:
1832 {
1833 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1834 if (tagId==HtmlTagType::HTML_CAPTION && parser()->context.token->endTag) // found </caption> tag
1835 {
1836 retval = Token::make_RetVal_OK();
1837 goto endcaption;
1838 }
1839 else
1840 {
1841 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <caption> context",
1842 parser()->context.token->endTag?"/":"",parser()->context.token->name);
1843 }
1844 }
1845 break;
1846 case TokenRetval::TK_COMMAND_AT:
1847 // fall through
1848 case TokenRetval::TK_COMMAND_BS:
1849 {
1850 DString cmdName=parser()->context.token->name;
1851 bool isJavaLink=false;
1852 switch (Mappers::cmdMapper->map(cmdName))
1853 {
1855 {
1856 tok=parser()->tokenizer.lex();
1857 if (!tok.is(TokenRetval::TK_WHITESPACE))
1858 {
1859 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
1860 tok.command_to_char(),cmdName);
1861 }
1862 else
1863 {
1865 tok=parser()->tokenizer.lex(); // get the reference id
1866 if (!tok.is(TokenRetval::TK_WORD))
1867 {
1868 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}' command",
1869 tok.to_string(),tok.command_to_char(),cmdName);
1870 }
1871 else
1872 {
1874 children().get_last<DocRef>()->parse(tok.command_to_char(),cmdName);
1875 }
1877 }
1878 }
1879 break;
1881 isJavaLink=true;
1882 // fall through
1884 {
1885 tok=parser()->tokenizer.lex();
1886 if (!tok.is(TokenRetval::TK_WHITESPACE))
1887 {
1888 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
1889 cmdName);
1890 }
1891 else
1892 {
1894 tok=parser()->tokenizer.lex();
1895 if (!tok.is(TokenRetval::TK_WORD))
1896 {
1897 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\{} command",
1898 tok.to_string(),cmdName);
1899 }
1900 else
1901 {
1904 DocLink *lnk = children().get_last<DocLink>();
1905 DString leftOver = lnk->parse(isJavaLink);
1906 if (!leftOver.empty())
1907 {
1908 children().append<DocWord>(parser(),thisVariant(),leftOver);
1909 }
1910 }
1911 }
1912 }
1913 break;
1915 {
1917 }
1918 break;
1919 default:
1920 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a <caption> tag",
1921 tok.command_to_char(),cmdName);
1922 }
1923 }
1924 break;
1925 default:
1926 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"<caption> tag");
1927 break;
1928 }
1929 }
1930 tok = parser()->tokenizer.lex();
1931 }
1932 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1933 {
1934 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1935 " <caption> tag");
1936 }
1937endcaption:
1939 return retval;
1940}
1941
1942//---------------------------------------------------------------------------
1943
1945{
1946 AUTO_TRACE();
1947 Token retval = Token::make_RetVal_OK();
1948 auto ns = AutoNodeStack(parser(),thisVariant());
1949
1950 // parse one or more paragraphs
1951 bool isFirst=true;
1952 DocPara *par=nullptr;
1953 do
1954 {
1956 par = children().get_last<DocPara>();
1957 if (isFirst) { par->markFirst(); isFirst=false; }
1958 retval=par->parse();
1959 if (retval.is(TokenRetval::TK_HTMLTAG))
1960 {
1961 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1962 if (tagId==HtmlTagType::HTML_TD && parser()->context.token->endTag) // found </td> tag
1963 {
1964 retval = Token::make_TK_NEWPARA(); // ignore the tag
1965 }
1966 else if (tagId==HtmlTagType::HTML_TH && parser()->context.token->endTag) // found </th> tag
1967 {
1968 retval = Token::make_TK_NEWPARA(); // ignore the tag
1969 }
1970 }
1971 }
1972 while (retval.is_any_of(TokenRetval::TK_NEWPARA,TokenRetval::RetVal_EndParBlock));
1973 if (par) par->markLast();
1974
1975 return retval;
1976}
1977
1979{
1980 AUTO_TRACE();
1981 Token retval = Token::make_RetVal_OK();
1982 auto ns = AutoNodeStack(parser(),thisVariant());
1983
1984 // parse one or more paragraphs
1985 bool isFirst=true;
1986 DocPara *par=nullptr;
1987 do
1988 {
1990 par = children().get_last<DocPara>();
1991 if (isFirst) { par->markFirst(); isFirst=false; }
1992 retval=par->parse();
1993 if (retval.is(TokenRetval::TK_HTMLTAG))
1994 {
1995 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1996 if (tagId==HtmlTagType::XML_ITEM && parser()->context.token->endTag) // found </item> tag
1997 {
1998 retval = Token::make_TK_NEWPARA(); // ignore the tag
1999 }
2000 else if (tagId==HtmlTagType::XML_DESCRIPTION && parser()->context.token->endTag) // found </description> tag
2001 {
2002 retval = Token::make_TK_NEWPARA(); // ignore the tag
2003 }
2004 }
2005 }
2006 while (retval.is(TokenRetval::TK_NEWPARA));
2007 if (par) par->markLast();
2008
2009 return retval;
2010}
2011
2012uint32_t DocHtmlCell::rowSpan() const
2013{
2014 for (const auto &attr : attribs())
2015 {
2016 if (attr.name.lower()=="rowspan")
2017 {
2018 return attr.value.toUInt();
2019 }
2020 }
2021 return 0;
2022}
2023
2024uint32_t DocHtmlCell::colSpan() const
2025{
2026 for (const auto &attr : attribs())
2027 {
2028 if (attr.name.lower()=="colspan")
2029 {
2030 return std::max(1u,attr.value.toUInt());
2031 }
2032 }
2033 return 1;
2034}
2035
2037{
2038 for (const auto &attr : attribs())
2039 {
2040 DString attrName = attr.name.lower();
2041 DString attrValue = attr.value.lower();
2042 if (attrName=="align")
2043 {
2044 if (attrValue=="center")
2045 return Center;
2046 else if (attrValue=="right")
2047 return Right;
2048 else return Left;
2049 }
2050 else if (attrName=="class" && attrValue.startsWith("markdowntable"))
2051 {
2052 if (attrValue=="markdowntableheadcenter")
2053 return Center;
2054 else if (attrValue=="markdowntableheadright")
2055 return Right;
2056 else if (attrValue=="markdowntableheadleft")
2057 return Left;
2058 else if (attrValue=="markdowntableheadnone")
2059 return Center;
2060 else if (attrValue=="markdowntablebodycenter")
2061 return Center;
2062 else if (attrValue=="markdowntablebodyright")
2063 return Right;
2064 else if (attrValue=="markdowntablebodyleft")
2065 return Left;
2066 else if (attrValue=="markdowntablebodynone")
2067 return Left;
2068 else return Left;
2069 }
2070 }
2071 return Left;
2072}
2073
2075{
2076 for (const auto &attr : attribs())
2077 {
2078 DString attrName = attr.name.lower();
2079 DString attrValue = attr.value.lower();
2080 if (attrName=="valign")
2081 {
2082 if (attrValue=="top")
2083 return Top;
2084 else if (attrValue=="bottom")
2085 return Bottom;
2086 else if (attrValue=="middle")
2087 return Middle;
2088 else return Middle;
2089 }
2090 }
2091 return Middle;
2092}
2093
2094//---------------------------------------------------------------------------
2095
2097{ // a row is a table heading if all cells are marked as such
2098 bool heading=true;
2099 for (const auto &n : children())
2100 {
2101 const DocHtmlCell *cell = std::get_if<DocHtmlCell>(&n);
2102 if (cell && !cell->isHeading())
2103 {
2104 heading = false;
2105 break;
2106 }
2107 }
2108 return !children().empty() && heading;
2109}
2110
2112{
2113 AUTO_TRACE();
2114 // get next token
2115 Token tok=parser->tokenizer.lex();
2116 // skip whitespace and tbody, thead and tfoot tags
2117 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA,TokenRetval::TK_HTMLTAG,
2118 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS))
2119 {
2120 if (tok.is(TokenRetval::TK_HTMLTAG))
2121 {
2122 AUTO_TRACE_ADD("html_tag={}",parser->context.token->name);
2124 // skip over tbody, thead, tfoot tags
2125 if (tagId==HtmlTagType::HTML_TBODY ||
2126 tagId==HtmlTagType::HTML_THEAD ||
2128 {
2129 tok=parser->tokenizer.lex();
2130 }
2131 else
2132 {
2133 break;
2134 }
2135 }
2136 else if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS))
2137 {
2138 DString cmdName=parser->context.token->name;
2139 AUTO_TRACE_ADD("command={}",cmdName);
2140 auto cmdType = Mappers::cmdMapper->map(cmdName);
2141 if (cmdType==CommandType::CMD_ILINE)
2142 {
2143 { DocTokenizer::AutoSaveState saveState(parser->tokenizer);
2144 parser->tokenizer.setStateILine();
2145 tok = parser->tokenizer.lex();
2146 if (!tok.is(TokenRetval::TK_WORD))
2147 {
2148 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"invalid argument for command '{:c}{}'",
2149 tok.command_to_char(),cmdName);
2150 }
2151 }
2152 tok = parser->tokenizer.lex();
2153 }
2154 else
2155 {
2156 break;
2157 }
2158 }
2159 else
2160 {
2161 AUTO_TRACE_ADD("skip whitespace");
2162 tok=parser->tokenizer.lex();
2163 }
2164 }
2165 return tok;
2166}
2167
2168
2169
2170
2172{
2173 AUTO_TRACE();
2174 Token retval = Token::make_RetVal_OK();
2175 auto ns = AutoNodeStack(parser(),thisVariant());
2176
2177 bool isHeading=false;
2178 bool isFirst=true;
2179 DocHtmlCell *cell=nullptr;
2180
2182 // should find a html tag now
2183 if (tok.is(TokenRetval::TK_HTMLTAG))
2184 {
2185 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2186 if (tagId==HtmlTagType::HTML_TD && !parser()->context.token->endTag) // found <td> tag
2187 {
2188 }
2189 else if (tagId==HtmlTagType::HTML_TH && !parser()->context.token->endTag) // found <th> tag
2190 {
2191 isHeading=true;
2192 }
2193 else // found some other tag
2194 {
2195 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but "
2196 "found <{}{}> instead!",parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2197 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2198 goto endrow;
2199 }
2200 }
2201 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2202 {
2203 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2204 " for a html description title");
2205 goto endrow;
2206 }
2207 else // token other than html token
2208 {
2209 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but found {} token instead!",
2210 tok.to_string());
2211 goto endrow;
2212 }
2213
2214 // parse one or more cells
2215 do
2216 {
2219 isHeading);
2220 cell = children().get_last<DocHtmlCell>();
2221 cell->markFirst(isFirst);
2222 isFirst=false;
2223 retval=cell->parse();
2224 isHeading = retval.is(TokenRetval::RetVal_TableHCell);
2225 //printf("DocHtmlRow:retval=%s\n",retval.to_string());
2226 if (retval.is(TokenRetval::RetVal_EndTableCell))
2227 {
2228 // get next token
2229 retval = skipSpacesForTable(parser());
2230 //printf("DocHtmlRow:retval= next=%s name=%s endTag=%d\n",retval.to_string(),qPrint(parser()->context.token->name),parser()->context.token->endTag);
2231 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2232 if (tok.is(TokenRetval::TK_HTMLTAG))
2233 {
2234 if ((tagId==HtmlTagType::HTML_TD || tagId==HtmlTagType::HTML_TH) &&
2235 !parser()->context.token->endTag) // found new <td> or <td> tag
2236 {
2237 retval = Token::make_RetVal_TableCell();
2239 }
2240 else if (tagId==HtmlTagType::HTML_TR)
2241 {
2242 if (parser()->context.token->endTag) // found </tr> tag
2243 {
2244 retval = Token::make_RetVal_EndTableRow();
2245 }
2246 else // found <tr> tag
2247 {
2248 retval = Token::make_RetVal_TableRow();
2249 }
2250 }
2251 else if (tagId==HtmlTagType::HTML_TABLE && parser()->context.token->endTag) // found </table>
2252 {
2253 retval = Token::make_RetVal_EndTable();
2254 }
2255 else // found some other tag
2256 {
2257 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td>, <th> or <tr> tag but "
2258 "found <{}{}> instead!",parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2259 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2260 goto endrow;
2261 }
2262 }
2263 else // token other than html token
2264 {
2265 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td>, <th> or <tr> tag but found {} token instead!",
2266 tok.to_string());
2267 goto endrow;
2268 }
2269 }
2270 }
2271 while (retval.is_any_of(TokenRetval::RetVal_TableCell,TokenRetval::RetVal_TableHCell));
2272 cell->markLast(true);
2273
2274endrow:
2275 return retval;
2276}
2277
2279{
2280 AUTO_TRACE();
2281 Token retval = Token::make_RetVal_OK();
2282 auto ns = AutoNodeStack(parser(),thisVariant());
2283
2284 bool isFirst=true;
2285 DocHtmlCell *cell=nullptr;
2286
2287 // get next token
2288 Token tok=parser()->tokenizer.lex();
2289 // skip whitespace
2290 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2291 // should find a html tag now
2292 if (tok.is(TokenRetval::TK_HTMLTAG))
2293 {
2294 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2295 if (tagId==HtmlTagType::XML_TERM && !parser()->context.token->endTag) // found <term> tag
2296 {
2297 }
2298 else if (tagId==HtmlTagType::XML_DESCRIPTION && !parser()->context.token->endTag) // found <description> tag
2299 {
2300 }
2301 else // found some other tag
2302 {
2303 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <term> or <description> tag but "
2304 "found <{}> instead!",parser()->context.token->name);
2305 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2306 goto endrow;
2307 }
2308 }
2309 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2310 {
2311 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2312 " for a html description title");
2313 goto endrow;
2314 }
2315 else // token other than html token
2316 {
2317 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but found {} token instead!",
2318 tok.to_string());
2319 goto endrow;
2320 }
2321
2322 do
2323 {
2325 cell = children().get_last<DocHtmlCell>();
2326 cell->markFirst(isFirst);
2327 isFirst=false;
2328 retval=cell->parseXml();
2329 }
2330 while (retval.is_any_of(TokenRetval::RetVal_TableCell,TokenRetval::RetVal_TableHCell));
2331 cell->markLast(true);
2332
2333endrow:
2334 return retval;
2335}
2336
2337//---------------------------------------------------------------------------
2338
2340{
2341 return m_caption!=nullptr;
2342}
2343
2345{
2346 return m_caption.get();
2347}
2348
2350{
2351 size_t hl = 0;
2352 for (auto &rowNode : children())
2353 {
2354 const DocHtmlRow *row = std::get_if<DocHtmlRow>(&rowNode);
2355 if (row)
2356 {
2357 if (!row->isHeading()) break;
2358 hl++;
2359 }
2360 }
2361 return hl;
2362}
2363
2365{
2366 AUTO_TRACE();
2367 Token retval = Token::make_RetVal_OK();
2368 auto ns = AutoNodeStack(parser(),thisVariant());
2369
2370getrow:
2371 // skip whitespace and tbody, thead and tfoot tags
2373 // should find a html tag now
2374 if (tok.is(TokenRetval::TK_HTMLTAG))
2375 {
2376 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2377 if (tagId==HtmlTagType::HTML_TR && !parser()->context.token->endTag) // found <tr> tag
2378 {
2379 // no caption, just rows
2380 retval = Token::make_RetVal_TableRow();
2381 }
2382 else if (tagId==HtmlTagType::HTML_CAPTION && !parser()->context.token->endTag) // found <caption> tag
2383 {
2384 if (m_caption)
2385 {
2386 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"table already has a caption, found another one");
2387 }
2388 else
2389 {
2390 m_caption = createDocNode<DocHtmlCaption>(parser(),thisVariant(),parser()->context.token->attribs);
2391 retval=std::get<DocHtmlCaption>(*m_caption).parse();
2392
2393 if (retval.is(TokenRetval::RetVal_OK)) // caption was parsed ok
2394 {
2395 goto getrow;
2396 }
2397 }
2398 }
2399 else // found wrong token
2400 {
2401 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or <caption> tag but "
2402 "found <{}{}> instead!", parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2403 }
2404 }
2405 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2406 {
2407 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2408 " for a <tr> or <caption> tag");
2409 }
2410 else // token other than html token
2411 {
2412 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> tag but found {} token instead!",
2413 tok.to_string());
2414 }
2415
2416 // parse one or more rows
2417 while (retval.is(TokenRetval::RetVal_TableRow))
2418 {
2420 retval = children().get_last<DocHtmlRow>()->parse();
2421 //printf("DocHtmlTable::retval=%s\n",retval.to_string());
2422 if (retval.is(TokenRetval::RetVal_EndTableRow))
2423 {
2424 // get next token
2425 retval = skipSpacesForTable(parser());
2426 //printf("DocHtmlTable::retval= next=%s name=%s endTag=%d\n",retval.to_string(),qPrint(parser()->context.token->name),parser()->context.token->endTag);
2427 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2428 if (tagId==HtmlTagType::HTML_TR && !parser()->context.token->endTag)
2429 {
2430 retval = Token::make_RetVal_TableRow();
2431 }
2432 else if (tagId==HtmlTagType::HTML_TABLE && parser()->context.token->endTag)
2433 {
2434 retval = Token::make_RetVal_EndTable();
2435 }
2436 else if (retval.is(TokenRetval::TK_HTMLTAG)) // some other HTML tag
2437 {
2438 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or </table> tag but "
2439 "found <{}> instead!",parser()->context.token->name);
2440 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2441 }
2442 else // found some other tag
2443 {
2444 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or </table> tag but "
2445 "found token {} instead!",retval.to_string());
2446 retval=Token::make_RetVal_OK();
2447 break;
2448 }
2449 }
2450 }
2451
2453
2454 return retval.is(TokenRetval::RetVal_EndTable) ? Token::make_RetVal_OK() : retval;
2455}
2456
2458{
2459 AUTO_TRACE();
2460 Token retval = Token::make_RetVal_OK();
2461 auto ns = AutoNodeStack(parser(),thisVariant());
2462
2463 // get next token
2464 Token tok=parser()->tokenizer.lex();
2465 // skip whitespace
2466 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2467 // should find a html tag now
2469 bool isHeader=false;
2470 if (tok.is(TokenRetval::TK_HTMLTAG))
2471 {
2472 tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2473 if (tagId==HtmlTagType::XML_ITEM && !parser()->context.token->endTag) // found <item> tag
2474 {
2475 retval = Token::make_RetVal_TableRow();
2476 }
2477 if (tagId==HtmlTagType::XML_LISTHEADER && !parser()->context.token->endTag) // found <listheader> tag
2478 {
2479 retval = Token::make_RetVal_TableRow();
2480 isHeader=true;
2481 }
2482 }
2483
2484 // parse one or more rows
2485 while (retval.is(TokenRetval::RetVal_TableRow))
2486 {
2489 retval=tr->parseXml(isHeader);
2490 isHeader=false;
2491 }
2492
2494
2495 tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2496 return tagId==HtmlTagType::XML_LIST && parser()->context.token->endTag ? Token::make_RetVal_OK() : retval;
2497}
2498
2499/** Helper class to compute the grid for an HTML style table */
2501{
2502 ActiveRowSpan(uint32_t rows,uint32_t col) : rowsLeft(rows), column(col) {}
2503 uint32_t rowsLeft;
2504 uint32_t column;
2505};
2506
2507/** List of ActiveRowSpan classes. */
2508typedef std::vector<ActiveRowSpan> RowSpanList;
2509
2510/** determines the location of all cells in a grid, resolving row and
2511 column spans. For each the total number of visible cells is computed,
2512 and the total number of visible columns over all rows is stored.
2513 */
2515{
2516 //printf("computeTableGrid()\n");
2517 RowSpanList rowSpans;
2518 uint32_t maxCols=0;
2519 uint32_t rowIdx=1;
2520 for (auto &rowNode : children())
2521 {
2522 uint32_t colIdx=1;
2523 uint32_t cells=0;
2524 DocHtmlRow *row = std::get_if<DocHtmlRow>(&rowNode);
2525 if (row)
2526 {
2527 for (auto &cellNode : row->children())
2528 {
2529 DocHtmlCell *cell = std::get_if<DocHtmlCell>(&cellNode);
2530 if (cell)
2531 {
2532 uint32_t rs = cell->rowSpan();
2533 uint32_t cs = cell->colSpan();
2534
2535 for (size_t i=0;i<rowSpans.size();i++)
2536 {
2537 if (rowSpans[i].rowsLeft>0 &&
2538 rowSpans[i].column==colIdx)
2539 {
2540 colIdx=rowSpans[i].column+1;
2541 cells++;
2542 }
2543 }
2544 if (rs>0) rowSpans.emplace_back(rs,colIdx);
2545 //printf("found cell at (%d,%d)\n",rowIdx,colIdx);
2546 cell->setRowIndex(rowIdx);
2547 cell->setColumnIndex(colIdx);
2548 colIdx+=cs;
2549 cells++;
2550 }
2551 }
2552 for (size_t i=0;i<rowSpans.size();i++)
2553 {
2554 if (rowSpans[i].rowsLeft>0) rowSpans[i].rowsLeft--;
2555 }
2556 row->setVisibleCells(cells);
2557 row->setRowIndex(rowIdx);
2558 rowIdx++;
2559 }
2560 if (colIdx-1>maxCols) maxCols=colIdx-1;
2561 }
2562 m_numCols = maxCols;
2563}
2564
2565//---------------------------------------------------------------------------
2566
2568{
2569 AUTO_TRACE();
2570 Token retval = Token::make_TK_NONE();
2571 auto ns = AutoNodeStack(parser(),thisVariant());
2572
2573 Token tok = parser()->tokenizer.lex();
2574 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
2575 {
2576 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
2577 {
2578 switch (tok.value())
2579 {
2580 case TokenRetval::TK_COMMAND_AT:
2581 // fall through
2582 case TokenRetval::TK_COMMAND_BS:
2583 {
2584 DString cmdName=parser()->context.token->name;
2585 bool isJavaLink=false;
2586 switch (Mappers::cmdMapper->map(cmdName))
2587 {
2589 {
2590 tok=parser()->tokenizer.lex();
2591 if (!tok.is(TokenRetval::TK_WHITESPACE))
2592 {
2593 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
2594 tok.command_to_char(),cmdName);
2595 }
2596 else
2597 {
2599 tok=parser()->tokenizer.lex(); // get the reference id
2600 if (!tok.is(TokenRetval::TK_WORD))
2601 {
2602 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}' command",
2603 tok.to_string(),tok.command_to_char(),cmdName);
2604 }
2605 else
2606 {
2608 children().get_last<DocRef>()->parse(tok.command_to_char(),cmdName);
2609 }
2611 }
2612 }
2613 break;
2615 isJavaLink=true;
2616 // fall through
2618 {
2619 tok=parser()->tokenizer.lex();
2620 if (!tok.is(TokenRetval::TK_WHITESPACE))
2621 {
2622 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
2623 cmdName);
2624 }
2625 else
2626 {
2628 tok=parser()->tokenizer.lex();
2629 if (!tok.is(TokenRetval::TK_WORD))
2630 {
2631 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\{} command",
2632 tok.to_string(),cmdName);
2633 }
2634 else
2635 {
2638 DocLink *lnk = children().get_last<DocLink>();
2639 DString leftOver = lnk->parse(isJavaLink);
2640 if (!leftOver.empty())
2641 {
2642 children().append<DocWord>(parser(),thisVariant(),leftOver);
2643 }
2644 }
2645 }
2646 }
2647 break;
2649 {
2651 }
2652 break;
2653 default:
2654 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a <dt> tag",
2655 tok.command_to_char(),cmdName);
2656 }
2657 }
2658 break;
2659 case TokenRetval::TK_SYMBOL:
2660 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a <dt> tag",
2661 parser()->context.token->name);
2662 break;
2663 case TokenRetval::TK_HTMLTAG:
2664 {
2665 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2666 if (tagId==HtmlTagType::HTML_DD && !parser()->context.token->endTag) // found <dd> tag
2667 {
2668 retval = Token::make_RetVal_DescData();
2669 goto endtitle;
2670 }
2671 else if (tagId==HtmlTagType::HTML_DT && parser()->context.token->endTag)
2672 {
2673 // ignore </dt> tag.
2674 }
2675 else if (tagId==HtmlTagType::HTML_DT)
2676 {
2677 // missing <dt> tag.
2678 retval = Token::make_RetVal_DescTitle();
2679 goto endtitle;
2680 }
2681 else if (tagId==HtmlTagType::HTML_DL && parser()->context.token->endTag)
2682 {
2683 retval = Token::make_RetVal_EndDesc();
2684 goto endtitle;
2685 }
2686 else if (tagId==HtmlTagType::HTML_A)
2687 {
2688 if (!parser()->context.token->endTag)
2689 {
2690 parser()->handleAHref(thisVariant(),children(),parser()->context.token->attribs);
2691 }
2692 }
2693 else if (tagId==HtmlTagType::HTML_BR)
2694 {
2696 }
2697 else
2698 {
2699 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <dt> context",
2700 parser()->context.token->endTag?"/":"",parser()->context.token->name);
2701 }
2702 }
2703 break;
2704 default:
2705 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {} found as part of a <dt> tag",
2706 tok.to_string());
2707 break;
2708 }
2709 }
2710 tok = parser()->tokenizer.lex();
2711 }
2712 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2713 {
2714 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
2715 " <dt> tag");
2716 }
2717endtitle:
2719 return retval;
2720}
2721
2722
2723//---------------------------------------------------------------------------
2724
2726{
2727 AUTO_TRACE();
2729 Token retval = Token::make_TK_NONE();
2730 auto ns = AutoNodeStack(parser(),thisVariant());
2731
2732 bool isFirst=true;
2733 DocPara *par=nullptr;
2734 do
2735 {
2737 par = children().get_last<DocPara>();
2738 if (isFirst) { par->markFirst(); isFirst=false; }
2739 retval=par->parse();
2740 }
2741 while (retval.is(TokenRetval::TK_NEWPARA));
2742 if (par) par->markLast();
2743
2744 return retval;
2745}
2746
2747//---------------------------------------------------------------------------
2748
2750{
2751 AUTO_TRACE();
2752 Token retval = Token::make_RetVal_OK();
2753 auto ns = AutoNodeStack(parser(),thisVariant());
2754
2755 // get next token
2756 Token tok=parser()->tokenizer.lex();
2757 // skip whitespace
2758 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2759 // should find a html tag now
2760 if (tok.is(TokenRetval::TK_HTMLTAG))
2761 {
2762 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2763 if (tagId==HtmlTagType::HTML_DT && !parser()->context.token->endTag) // found <dt> tag
2764 {
2765 // continue
2766 }
2767 else // found some other tag
2768 {
2769 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <dt> tag but "
2770 "found <{}> instead!",parser()->context.token->name);
2771 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2772 goto enddesclist;
2773 }
2774 }
2775 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2776 {
2777 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2778 " for a html description title");
2779 goto enddesclist;
2780 }
2781 else // token other than html token
2782 {
2783 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <dt> tag but found {} token instead!",
2784 tok.to_string());
2785 goto enddesclist;
2786 }
2787
2788 do
2789 {
2794 retval=dt->parse();
2795 if (retval.is(TokenRetval::RetVal_DescData))
2796 {
2797 retval=dd->parse();
2798 while (retval.is(TokenRetval::RetVal_DescData))
2799 {
2802 retval=dd->parse();
2803 }
2804 }
2805 else if (!retval.is(TokenRetval::RetVal_DescTitle))
2806 {
2807 // error
2808 break;
2809 }
2810 } while (retval.is(TokenRetval::RetVal_DescTitle));
2811
2812 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2813 {
2814 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <dl> block");
2815 }
2816
2817enddesclist:
2818
2819 return retval.is(TokenRetval::RetVal_EndDesc) ? Token::make_RetVal_OK() : retval;
2820}
2821
2822//---------------------------------------------------------------------------
2823
2825{
2826 AUTO_TRACE();
2827 Token retval = Token::make_TK_NONE();
2828 auto ns = AutoNodeStack(parser(),thisVariant());
2829
2830 // parse one or more paragraphs
2831 bool isFirst=true;
2832 DocPara *par=nullptr;
2833 do
2834 {
2836 par = children().get_last<DocPara>();
2837 if (isFirst) { par->markFirst(); isFirst=false; }
2838 retval=par->parse();
2839 }
2840 while (retval.is(TokenRetval::TK_NEWPARA));
2841 if (par) par->markLast();
2842
2843 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2844 return retval;
2845}
2846
2848{
2849 AUTO_TRACE();
2850 Token retval = Token::make_TK_NONE();
2851 auto ns = AutoNodeStack(parser(),thisVariant());
2852
2853 // parse one or more paragraphs
2854 bool isFirst=true;
2855 DocPara *par=nullptr;
2856 do
2857 {
2859 par = children().get_last<DocPara>();
2860 if (isFirst) { par->markFirst(); isFirst=false; }
2861 retval=par->parse();
2862 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
2863
2864 //printf("new item: retval=%x parser()->context.token->name=%s parser()->context.token->endTag=%d\n",
2865 // retval,qPrint(parser()->context.token->name),parser()->context.token->endTag);
2866 if (retval.is(TokenRetval::RetVal_ListItem))
2867 {
2868 break;
2869 }
2870 }
2871 while (!retval.is(TokenRetval::RetVal_CloseXml));
2872
2873 if (par) par->markLast();
2874
2875 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2876 return retval;
2877}
2878
2879//---------------------------------------------------------------------------
2880
2882{
2883 AUTO_TRACE();
2884 Token retval = Token::make_RetVal_OK();
2885 int num=1;
2886 auto ns = AutoNodeStack(parser(),thisVariant());
2887
2888 // get next token
2889 Token tok=parser()->tokenizer.lex();
2890 // skip whitespace and paragraph breaks
2891 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2892 // should find a html tag now
2893 if (tok.is(TokenRetval::TK_HTMLTAG))
2894 {
2895 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2896 if (tagId==HtmlTagType::HTML_LI && !parser()->context.token->endTag) // found <li> tag
2897 {
2898 // ok, we can go on.
2899 }
2900 else if (((m_type==Unordered && tagId==HtmlTagType::HTML_UL) ||
2902 ) && parser()->context.token->endTag
2903 ) // found empty list
2904 {
2905 // add dummy item to obtain valid HTML
2907 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"empty list!");
2908 retval = Token::make_RetVal_EndList();
2909 goto endlist;
2910 }
2911 else // found some other tag
2912 {
2913 // add dummy item to obtain valid HTML
2915 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <li> tag but "
2916 "found <{}{}> instead!",parser()->context.token->endTag?"/":"",parser()->context.token->name);
2917 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2918 goto endlist;
2919 }
2920 }
2921 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2922 {
2923 // add dummy item to obtain valid HTML
2925 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2926 " for a html list item");
2927 goto endlist;
2928 }
2929 else // token other than html token
2930 {
2931 // add dummy item to obtain valid HTML
2933 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <li> tag but found {} token instead!",
2934 tok.to_string());
2935 goto endlist;
2936 }
2937
2938 do
2939 {
2942 retval=li->parse();
2943 } while (retval.is(TokenRetval::RetVal_ListItem));
2944
2945 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2946 {
2947 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <{:c}l> block",
2948 m_type==Unordered ? 'u' : 'o');
2949 }
2950
2951endlist:
2952 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2953 return retval.is(TokenRetval::RetVal_EndList) ? Token::make_RetVal_OK() : retval;
2954}
2955
2957{
2958 AUTO_TRACE();
2959 Token retval = Token::make_RetVal_OK();
2960 int num=1;
2961 auto ns = AutoNodeStack(parser(),thisVariant());
2962
2963 // get next token
2964 Token tok=parser()->tokenizer.lex();
2965 // skip whitespace and paragraph breaks
2966 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2967 // should find a html tag now
2968 if (tok.is(TokenRetval::TK_HTMLTAG))
2969 {
2970 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2971 //printf("parser()->context.token->name=%s parser()->context.token->endTag=%d\n",qPrint(parser()->context.token->name),parser()->context.token->endTag);
2972 if (tagId==HtmlTagType::XML_ITEM && !parser()->context.token->endTag) // found <item> tag
2973 {
2974 // ok, we can go on.
2975 }
2976 else // found some other tag
2977 {
2978 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <item> tag but "
2979 "found <{}> instead!",parser()->context.token->name);
2980 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2981 goto endlist;
2982 }
2983 }
2984 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2985 {
2986 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2987 " for a html list item");
2988 goto endlist;
2989 }
2990 else // token other than html token
2991 {
2992 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <item> tag but found {} token instead!",
2993 tok.to_string());
2994 goto endlist;
2995 }
2996
2997 do
2998 {
3001 retval=li->parseXml();
3002 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
3003 //printf("retval=%x parser()->context.token->name=%s\n",retval,qPrint(parser()->context.token->name));
3004 } while (retval.is(TokenRetval::RetVal_ListItem));
3005
3006 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3007 {
3008 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <list type=\"{}\"> block",
3009 m_type==Unordered ? "bullet" : "number");
3010 }
3011
3012endlist:
3013 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3014 return (retval.is_any_of(TokenRetval::RetVal_EndList,TokenRetval::RetVal_CloseXml) || parser()->context.token->name=="list") ?
3015 Token::make_RetVal_OK() : retval;
3016}
3017
3018//--------------------------------------------------------------------------
3019
3021{
3022 AUTO_TRACE();
3023 Token retval = Token::make_TK_NONE();
3024 auto ns = AutoNodeStack(parser(),thisVariant());
3025
3026 // parse one or more paragraphs
3027 bool isFirst=true;
3028 DocPara *par=nullptr;
3029 do
3030 {
3032 par = children().get_last<DocPara>();
3033 if (isFirst) { par->markFirst(); isFirst=false; }
3034 retval=par->parse();
3035 }
3036 while (retval.is(TokenRetval::TK_NEWPARA));
3037 if (par) par->markLast();
3038
3039 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3040 {
3041 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <blockquote> block");
3042 }
3043
3044 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3045 return retval.is(TokenRetval::RetVal_EndBlockQuote) ? Token::make_RetVal_OK() : retval;
3046}
3047
3048//---------------------------------------------------------------------------
3049
3051{
3052 AUTO_TRACE();
3053 Token retval = Token::make_TK_NONE();
3054 auto ns = AutoNodeStack(parser(),thisVariant());
3055
3056 // parse one or more paragraphs
3057 bool isFirst=true;
3058 DocPara *par=nullptr;
3059 do
3060 {
3062 par = children().get_last<DocPara>();
3063 if (isFirst) { par->markFirst(); isFirst=false; }
3064 retval=par->parse();
3065 }
3066 while (retval.is(TokenRetval::TK_NEWPARA));
3067 if (par) par->markLast();
3068
3069 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3070 return retval.is(TokenRetval::RetVal_EndBlockQuote) ? Token::make_RetVal_OK() : retval;
3071}
3072
3073//---------------------------------------------------------------------------
3074
3079
3080
3082{
3083 auto ns = AutoNodeStack(parser(),thisVariant());
3085 DocPara *par = &std::get<DocPara>(*m_paragraph);
3086 Token rv=par->parse();
3087 par->markFirst();
3088 par->markLast();
3089 return rv;
3090}
3091
3092//--------------------------------------------------------------------------
3093
3095{
3096 auto ns = AutoNodeStack(parser(),thisVariant());
3097 Token rv = Token::make_TK_NONE();
3098 do
3099 {
3102 rv=li->parse();
3103 } while (rv.is(TokenRetval::RetVal_ListItem));
3104 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3105}
3106
3107//--------------------------------------------------------------------------
3108
3113
3115{
3116 AUTO_TRACE();
3117 Token retval = Token::make_RetVal_OK();
3118 auto ns = AutoNodeStack(parser(),thisVariant());
3119
3120 // first parse any number of paragraphs
3121 bool isFirst=true;
3122 DocPara *lastPar=nullptr;
3123 do
3124 {
3126 DocPara *par = children().get_last<DocPara>();
3127 if (isFirst) { par->markFirst(); isFirst=false; }
3128 retval=par->parse();
3129 if (!par->empty())
3130 {
3131 if (lastPar) lastPar->markLast(false);
3132 lastPar=par;
3133 }
3134 else
3135 {
3136 children().pop_back();
3137 }
3138 // next paragraph should be more indented than the - marker to belong
3139 // to this item
3140 } while (retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->indent>m_indent);
3141 if (lastPar) lastPar->markLast();
3142
3143 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3144 return retval;
3145}
3146
3147//--------------------------------------------------------------------------
3148
3155
3157{
3158 AUTO_TRACE();
3159 Token retval = Token::make_RetVal_OK();
3160 int num=1;
3161 auto ns = AutoNodeStack(parser(),thisVariant());
3163 // first item or sub list => create new list
3164 do
3165 {
3166 switch (parser()->context.token->id)
3167 {
3168 case -1:
3169 break;
3170 case DocAutoList::Unchecked: // unchecked
3171 case DocAutoList::Checked_x: // checked with x
3172 case DocAutoList::Checked_X: // checked with X
3173 num = parser()->context.token->id;
3174 break;
3175 default: // explicitly numbered list
3176 num=parser()->context.token->id; // override num with real number given
3177 break;
3178 }
3179
3181 retval = children().get_last<DocAutoListItem>()->parse();
3182 //printf("DocAutoList::parse(): retval=0x%x parser()->context.token->indent=%d m_indent=%d "
3183 // "m_isEnumList=%d parser()->context.token->isEnumList=%d parser()->context.token->name=%s\n",
3184 // retval,parser()->context.token->indent,m_indent,m_isEnumList,parser()->context.token->isEnumList,
3185 // qPrint(parser()->context.token->name));
3186 //printf("num=%d parser()->context.token->id=%d\n",num,parser()->context.token->id);
3187 }
3188 while (retval.is(TokenRetval::TK_LISTITEM) && // new list item
3189 m_indent==parser()->context.token->indent && // at same indent level
3190 m_isEnumList==parser()->context.token->isEnumList && // of the same kind
3191 m_isCheckedList==parser()->context.token->isCheckedList && // of the same kind
3192 (parser()->context.token->id==-1 || parser()->context.token->id>=num) // increasing number (or no number or checked list)
3193 );
3194
3196
3197 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3198 return retval;
3199}
3200
3201//--------------------------------------------------------------------------
3202
3204{
3205 AUTO_TRACE();
3206 auto ns = AutoNodeStack(parser(),thisVariant());
3208 Token tok = parser()->tokenizer.lex();
3209 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
3210 {
3211 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
3212 {
3213 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"title section");
3214 }
3215 tok = parser()->tokenizer.lex();
3216 }
3219}
3220
3231
3232//--------------------------------------------------------------------------
3233
3238
3240{
3241 return m_title && std::get<DocTitle>(*m_title).hasTitle();
3242}
3243
3244Token DocSimpleSect::parse(bool userTitle,bool needsSeparator)
3245{
3246 AUTO_TRACE();
3247 auto ns = AutoNodeStack(parser(),thisVariant());
3248
3249 // handle case for user defined title
3250 if (userTitle)
3251 {
3253 std::get_if<DocTitle>(m_title.get())->parse();
3254 }
3255
3256 // add new paragraph as child
3257 if (!children().empty() && std::holds_alternative<DocPara>(children().back()))
3258 {
3259 std::get<DocPara>(children().back()).markLast(false);
3260 }
3261 bool markFirst = children().empty();
3262 if (needsSeparator)
3263 {
3265 }
3267 DocPara *par = children().get_last<DocPara>();
3268 if (markFirst)
3269 {
3270 par->markFirst();
3271 }
3272 par->markLast();
3273
3274 // parse the contents of the paragraph
3275 Token retval = par->parse();
3276
3277 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3278 return retval; // 0==EOF, TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, TokenRetval::TK_ENDLIST, TokenRetval::RetVal_SimpleSec
3279}
3280
3282{
3283 AUTO_TRACE();
3284 auto ns = AutoNodeStack(parser(),thisVariant());
3285
3287 DocTitle *title = &std::get<DocTitle>(*m_title);
3288 title->parseFromString(thisVariant(),parser()->context.token->name);
3289
3290 DString text = parser()->context.token->text;
3291 { DocParser::AutoSaveContext saveContext(*parser());
3293 }
3294
3295 return Token::make_RetVal_OK();
3296}
3297
3299{
3300 AUTO_TRACE();
3301 auto ns = AutoNodeStack(parser(),thisVariant());
3302
3303 Token retval = Token::make_RetVal_OK();
3304 for (;;)
3305 {
3306 // add new paragraph as child
3307 if (!children().empty() && std::holds_alternative<DocPara>(children().back()))
3308 {
3309 std::get<DocPara>(children().back()).markLast(false);
3310 }
3311 bool markFirst = children().empty();
3313 DocPara *par = children().get_last<DocPara>();
3314 if (markFirst)
3315 {
3316 par->markFirst();
3317 }
3318 par->markLast();
3319
3320 // parse the contents of the paragraph
3321 retval = par->parse();
3322 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
3323 if (retval.is(TokenRetval::RetVal_CloseXml))
3324 {
3325 retval = Token::make_RetVal_OK();
3326 break;
3327 }
3328 }
3329
3330 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3331 return retval;
3332}
3333
3335{
3336 DocPara *p=nullptr;
3337 if (children().empty() || (p=std::get_if<DocPara>(&children().back()))==nullptr)
3338 {
3340 p = children().get_last<DocPara>();
3341 }
3342 else
3343 {
3344 // Comma-separate <seealso> links.
3345 p->injectToken(Token::make_TK_WORD(),",");
3346 p->injectToken(Token::make_TK_WHITESPACE()," ");
3347 }
3348
3349 parser()->context.inSeeBlock=true;
3350 p->injectToken(Token::make_TK_LNKWORD(),word);
3351 parser()->context.inSeeBlock=false;
3352}
3353
3355{
3356 switch (m_type)
3357 {
3358 case Unknown: break;
3359 case See: return "see";
3360 case Return: return "return";
3361 case Author: // fall through
3362 case Authors: return "author";
3363 case Version: return "version";
3364 case Since: return "since";
3365 case Date: return "date";
3366 case Note: return "note";
3367 case Warning: return "warning";
3368 case Pre: return "pre";
3369 case Post: return "post";
3370 case Copyright: return "copyright";
3371 case Invar: return "invariant";
3372 case Remark: return "remark";
3373 case Attention: return "attention";
3374 case Important: return "important";
3375 case User: return "user";
3376 case Rcs: return "rcs";
3377 }
3378 return "unknown";
3379}
3380
3381//--------------------------------------------------------------------------
3382
3384{
3385 AUTO_TRACE();
3386 Token retval = Token::make_RetVal_OK();
3387 auto ns = AutoNodeStack(parser(),thisVariant());
3388 DocPara *par=nullptr;
3389 DString saveCmdName = cmdName;
3390 DocParserContext &context = parser()->context;
3391 DocTokenizer &tokenizer = parser()->tokenizer;
3392
3393 Token tok=tokenizer.lex();
3394 if (!tok.is(TokenRetval::TK_WHITESPACE))
3395 {
3396 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
3397 saveCmdName);
3398 retval = Token::make_RetVal_EndParBlock();
3399 goto endparamlist;
3400 }
3401 tokenizer.setStateParam();
3402 tok=tokenizer.lex();
3403 while (tok.is(TokenRetval::TK_WORD)) /* there is a parameter name */
3404 {
3406 {
3407 size_t typeSeparator = context.token->name.find('#'); // explicit type position
3408 if (typeSeparator!=DString::npos)
3409 {
3410 parser()->handleParameterType(thisVariant(),m_paramTypes,context.token->name.left(typeSeparator));
3411 context.token->name = context.token->name.mid(typeSeparator+1);
3412 context.hasParamCommand=true;
3413 if (parent() && std::holds_alternative<DocParamSect>(*parent()))
3414 {
3415 std::get<DocParamSect>(*parent()).m_hasTypeSpecifier=true;
3416 }
3417 }
3418 else
3419 {
3420 context.hasParamCommand=true;
3421 }
3423 }
3424 else if (m_type==DocParamSect::RetVal)
3425 {
3426 context.hasReturnCommand=true;
3428 }
3429 context.inSeeBlock=true;
3431 context.inSeeBlock=false;
3432 tok=tokenizer.lex();
3433 }
3434 tokenizer.setStatePara();
3435 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
3436 {
3437 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3438 "argument of command {}",saveCmdName);
3439 retval = Token::make_RetVal_EndParBlock();
3440 goto endparamlist;
3441 }
3442 if (!tok.is(TokenRetval::TK_WHITESPACE)) /* premature end of comment block */
3443 {
3444 if (!tok.is(TokenRetval::TK_NEWPARA)) /* empty param description */
3445 {
3446 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} in comment block while parsing the "
3447 "argument of command {}",tok.to_string(),saveCmdName);
3448 }
3449 retval = Token::make_RetVal_EndParBlock();
3450 goto endparamlist;
3451 }
3452
3454 par = m_paragraphs.get_last<DocPara>();
3455 retval = par->parse();
3456 par->markFirst();
3457 par->markLast();
3458
3459endparamlist:
3460 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3461 return retval;
3462}
3463
3465{
3466 AUTO_TRACE();
3467 Token retval = Token::make_RetVal_OK();
3468 auto ns = AutoNodeStack(parser(),thisVariant());
3469
3470 parser()->context.token->name = paramName;
3472 {
3475 }
3476 else if (m_type==DocParamSect::RetVal)
3477 {
3480 }
3481
3483
3484 do
3485 {
3488 retval = par->parse();
3489 if (par->empty()) // avoid adding an empty paragraph for the whitespace
3490 // after </para> and before </param>
3491 {
3493 break;
3494 }
3495 else // append the paragraph to the list
3496 {
3497 if (!m_paragraphs.empty())
3498 {
3500 }
3501 bool markFirst = m_paragraphs.empty();
3502 par = &std::get<DocPara>(m_paragraphs.back());
3503 if (markFirst)
3504 {
3505 par->markFirst();
3506 }
3507 par->markLast();
3508 }
3509
3510 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
3511
3512 } while (retval.is(TokenRetval::RetVal_CloseXml) &&
3513 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_PARAM &&
3514 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_TYPEPARAM &&
3515 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_EXCEPTION);
3516
3517 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) /* premature end of comment block */
3518 {
3519 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unterminated param or exception tag");
3520 }
3521 else
3522 {
3523 retval = Token::make_RetVal_OK();
3524 }
3525
3526 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3527 return retval;
3528}
3529
3530//--------------------------------------------------------------------------
3531
3532Token DocParamSect::parse(const DString &cmdName,bool xmlContext, Direction d)
3533{
3534 AUTO_TRACE();
3535 Token retval = Token::make_RetVal_OK();
3536 auto ns = AutoNodeStack(parser(),thisVariant());
3537
3538 if (d!=Unspecified)
3539 {
3541 }
3542
3543 if (!children().empty() && std::holds_alternative<DocParamList>(children().back()))
3544 {
3545 DocParamList &lastPl = std::get<DocParamList>(children().back());
3546 lastPl.markLast(false);
3547 }
3548 bool markFirst = children().empty();
3551 if (markFirst)
3552 {
3553 pl->markFirst();
3554 }
3555 pl->markLast();
3556 if (xmlContext)
3557 {
3558 retval = pl->parseXml(cmdName);
3559 }
3560 else
3561 {
3562 retval = pl->parse(cmdName);
3563 }
3564 if (retval.is(TokenRetval::RetVal_EndParBlock))
3565 {
3566 retval = Token::make_RetVal_OK();
3567 }
3568
3569 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3570 return retval;
3571}
3572
3573//--------------------------------------------------------------------------
3574
3580
3582{
3583 AUTO_TRACE();
3584 DocSimpleSect *ss=nullptr;
3585 bool needsSeparator = false;
3586 if (!children().empty() && // has previous element
3587 (ss=children().get_last<DocSimpleSect>()) && // was a simple sect
3588 ss->type()==t && // of same type
3589 t!=DocSimpleSect::User) // but not user defined
3590 {
3591 // append to previous section
3592 needsSeparator = true;
3593 }
3594 else // start new section
3595 {
3598 }
3599 Token rv = Token::make_RetVal_OK();
3600 if (xmlContext)
3601 {
3602 return ss->parseXml();
3603 }
3604 else
3605 {
3606 rv = ss->parse(t==DocSimpleSect::User,needsSeparator);
3607 }
3608 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3609}
3610
3613 bool xmlContext=false,
3614 int direction=DocParamSect::Unspecified)
3615{
3616 AUTO_TRACE();
3617 DocParamSect *ps = nullptr;
3618 if (!children().empty() && // previous element
3619 (ps=children().get_last<DocParamSect>()) && // was a param sect
3620 ps->type()==t) // of same type
3621 { // append to previous section ps
3622 }
3623 else // start new section
3624 {
3626 ps = children().get_last<DocParamSect>();
3627 }
3628 Token rv=ps->parse(cmdName,xmlContext,
3629 static_cast<DocParamSect::Direction>(direction));
3630 AUTO_TRACE_EXIT("retval={}",rv.to_string());
3631 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3632}
3633
3634void DocPara::handleEmoji(char cmdChar,const DString &cmdName)
3635{
3636 AUTO_TRACE();
3637 // get the argument of the emoji command.
3638 Token tok=parser()->tokenizer.lex();
3639 if (!tok.is(TokenRetval::TK_WHITESPACE))
3640 {
3641 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3642 cmdChar,cmdName);
3643 return;
3644 }
3646 tok=parser()->tokenizer.lex();
3647 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3648 {
3649 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"no emoji name given or unexpected end of comment block while parsing the "
3650 "argument of command '{:c}{}'",cmdChar,cmdName);
3652 return;
3653 }
3654 else if (!tok.is(TokenRetval::TK_WORD))
3655 {
3656 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3657 tok.to_string(),cmdChar,cmdName);
3659 return;
3660 }
3663}
3664
3665void DocPara::handleDoxyConfig(char cmdChar,const DString &cmdName)
3666{
3667 // get the argument of the cite command.
3668 Token tok=parser()->tokenizer.lex();
3669 if (!tok.is(TokenRetval::TK_WHITESPACE))
3670 {
3671 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3672 cmdChar,cmdName);
3673 return;
3674 }
3676 tok=parser()->tokenizer.lex();
3677 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3678 {
3679 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3680 "argument of command '{:c}{}'",cmdChar,cmdName);
3681 return;
3682 }
3683 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
3684 {
3685 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3686 tok.to_string(),cmdChar,cmdName);
3687 return;
3688 }
3689 ConfigOption * opt = ConfigImpl::instance()->get(parser()->context.token->name);
3690 if (opt)
3691 {
3692 DString optionValue;
3693 switch (opt->kind())
3694 {
3696 optionValue = *(static_cast<ConfigBool*>(opt)->valueStringRef());
3697 break;
3699 optionValue = *(static_cast<ConfigString*>(opt)->valueRef());
3700 break;
3702 optionValue = *(static_cast<ConfigEnum*>(opt)->valueRef());
3703 break;
3705 optionValue = *(static_cast<ConfigInt*>(opt)->valueStringRef());
3706 break;
3708 {
3709 StringVector *lst = static_cast<ConfigList*>(opt)->valueRef();
3710 optionValue="";
3711 if (!lst->empty())
3712 {
3713 std::string lstFormat = theTranslator->trWriteList(static_cast<int>(lst->size())).str();
3714 static const reg::Ex marker(R"(@(\d+))");
3715 reg::Iterator it(lstFormat,marker);
3717 size_t index=0;
3718 // now replace all markers with the real text
3719 for ( ; it!=end ; ++it)
3720 {
3721 const auto &match = *it;
3722 size_t newIndex = match.position();
3723 size_t matchLen = match.length();
3724 optionValue += lstFormat.substr(index,newIndex-index);
3725 unsigned long entryIndex = std::stoul(match[1].str());
3726 if (entryIndex<(unsigned long)lst->size())
3727 {
3728 optionValue += lst->at(entryIndex);
3729 }
3730 index=newIndex+matchLen;
3731 }
3732 optionValue+=lstFormat.substr(index);
3733 }
3734 }
3735 break;
3737 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Obsolete setting for '{:c}{}': '{}'",
3738 cmdChar,cmdName,parser()->context.token->name);
3739 break;
3741 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),
3742 "Disabled setting (i.e. not supported in this doxygen executable) for '{:c}{}': '{}'",
3743 cmdChar,cmdName,parser()->context.token->name);
3744 break;
3746 // nothing to show here
3747 break;
3748 }
3749 if (!optionValue.empty())
3750 {
3751 children().append<DocWord>(parser(),thisVariant(),optionValue);
3752 }
3753 }
3754 else
3755 {
3756 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Unknown option for '{:c}{}': '{}'",
3757 cmdChar,cmdName,parser()->context.token->name);
3759 }
3761}
3762
3764{
3765 AUTO_TRACE();
3766 Token retval=parser()->tokenizer.lex();
3767 ASSERT(retval.is(TokenRetval::TK_WHITESPACE));
3769 retval=parser()->tokenizer.lex();
3770 if (retval.is(TokenRetval::RetVal_OK))
3771 {
3775 if (!ref->parse())
3776 {
3777 children().pop_back();
3778 }
3779 }
3781 return retval;
3782}
3783
3784
3785void DocPara::handleShowDate(char cmdChar,const DString &cmdName)
3786{
3787 AUTO_TRACE();
3788 DString fmt;
3789 DString date;
3790 Token tok=parser()->tokenizer.lex();
3791 if (!tok.is(TokenRetval::TK_WHITESPACE))
3792 {
3793 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3794 cmdChar,cmdName);
3795 return;
3796 }
3798 tok = parser()->tokenizer.lex();
3799 if (!tok.is(TokenRetval::TK_WORD))
3800 {
3801 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <format> argument for command '{:c}{}'",
3802 cmdChar,cmdName);
3804 return;
3805 }
3806 fmt = parser()->context.token->name;
3807
3809 tok = parser()->tokenizer.lex();
3810
3811 DString specDateRaw = tok.is(TokenRetval::TK_WORD) ? parser()->context.token->name : DString();
3812 DString specDate = specDateRaw.stripWhiteSpace();
3813 bool specDateOnlyWS = !specDateRaw.empty() && specDate.empty();
3814 if (!specDate.empty() && !tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3815 {
3816 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}'",
3817 cmdChar,cmdName);
3819 return;
3820 }
3821
3822 std::tm dat{};
3823 int specFormat=0;
3824 DString err = dateTimeFromString(specDate,dat,specFormat);
3825 if (!err.empty())
3826 {
3827 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}': {}",
3828 cmdChar,cmdName,err);
3830 return;
3831 }
3832
3833 int usedFormat=0;
3834 DString dateTimeStr = formatDateTime(fmt,dat,usedFormat);
3835
3836 // warn the user if the format contains markers that are not explicitly filled in
3837 for (int i=0;i<SF_NumBits;i++)
3838 {
3839 int bitMask = 1<<i;
3840 if ((usedFormat&bitMask) && !(specFormat&bitMask)) // a part was used in the format string but its value was not specified.
3841 {
3842 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.",
3843 cmdChar,cmdName,fmt,SF_bit2str(i),specDate,SF_bit2str(i));
3844 }
3845 }
3846
3847 children().append<DocWord>(parser(),thisVariant(),dateTimeStr);
3848 if (specDateOnlyWS) // specDate is only whitespace
3849 {
3851 }
3853}
3854
3856{
3857 AUTO_TRACE("cmdName={}",cmdName);
3858 DString saveCmdName = cmdName;
3859 Token tok=parser()->tokenizer.lex();
3860 if (!tok.is(TokenRetval::TK_WHITESPACE))
3861 {
3862 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3863 saveCmdName);
3864 return;
3865 }
3867 tok=parser()->tokenizer.lex();
3869 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3870 {
3871 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3872 "argument of command {}", saveCmdName);
3873 return;
3874 }
3875 else if (!tok.is(TokenRetval::TK_WORD))
3876 {
3877 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3878 tok.to_string(),saveCmdName);
3879 return;
3880 }
3881 auto it1 = children().size()>=1 ? std::prev(children().end()) : children().end();
3882 auto it2 = children().size()>=2 ? std::prev(it1) : children().end();
3883 DocNodeVariant *n1 = it1!=children().end() ? &(*it1) : nullptr;
3884 DocNodeVariant *n2 = it2!=children().end() ? &(*it2) : nullptr;
3885 //TODO get from context the stripCodeComments()
3886 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
3890 stripCodeComments,
3893 );
3895 DocIncOperator *n1_docIncOp = std::get_if<DocIncOperator>(n1);
3896 DocWhiteSpace *n1_docWs = std::get_if<DocWhiteSpace >(n1);
3897 DocIncOperator *n2_docIncOp = std::get_if<DocIncOperator>(n2);
3898 bool isFirst = !n1 || // no last node
3899 (!n1_docIncOp && !n1_docWs) || // last node is not operator or whitespace
3900 (n1_docWs && n2 && !n2_docIncOp); // last node is not operator
3901 op->markFirst(isFirst);
3902 op->markLast(true);
3903 if (n1_docIncOp)
3904 {
3905 n1_docIncOp->markLast(false);
3906 }
3907 else if (n1_docWs && n2_docIncOp)
3908 {
3909 n2_docIncOp->markLast(false);
3910 }
3911 op->parse();
3912}
3913
3914template<class T>
3915void DocPara::handleFile(const DString &cmdName)
3916{
3917 AUTO_TRACE("cmdName={}",cmdName);
3918 DString saveCmdName = cmdName;
3919 Token tok=parser()->tokenizer.lex();
3920 if (!tok.is(TokenRetval::TK_WHITESPACE))
3921 {
3922 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3923 saveCmdName);
3924 return;
3925 }
3927 tok=parser()->tokenizer.lex();
3929 if (!tok.is(TokenRetval::TK_WORD))
3930 {
3931 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3932 tok.to_string(),saveCmdName);
3933 return;
3934 }
3935 DString name = parser()->context.token->name;
3936 children().append<T>(parser(),thisVariant(),name,
3940 auto df = children().get_last<T>();
3941 if (!df->parse())
3942 {
3943 children().pop_back();
3944 }
3945}
3946
3953
3954void DocPara::handleLink(const DString &cmdName,bool isJavaLink)
3955{
3956 AUTO_TRACE("cmdName={} isJavaLink={}",cmdName,isJavaLink);
3957 DString saveCmdName = cmdName;
3958 Token tok=parser()->tokenizer.lex();
3959 if (!tok.is(TokenRetval::TK_WHITESPACE))
3960 {
3961 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3962 saveCmdName);
3963 return;
3964 }
3966 tok=parser()->tokenizer.lex();
3967 if (!tok.is(TokenRetval::TK_WORD))
3968 {
3969 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"{} as the argument of {}",
3970 tok.to_string(),saveCmdName);
3971 return;
3972 }
3973 if (saveCmdName == "javalink")
3974 {
3976 parser()->context.nodeStack.size(),
3977 DocStyleChange::Code,cmdName,true);
3978 }
3981 DocLink *lnk = children().get_last<DocLink>();
3982 if (saveCmdName == "javalink")
3983 {
3985 parser()->context.nodeStack.size(),
3986 DocStyleChange::Code,cmdName,false);
3987 }
3988 DString leftOver = lnk->parse(isJavaLink);
3989 if (!leftOver.empty())
3990 {
3991 children().append<DocWord>(parser(),thisVariant(),leftOver);
3992 }
3993}
3994
3996{
3997 AUTO_TRACE("cmdName={}",cmdName);
3998 DString saveCmdName = cmdName;
3999 Token tok=parser()->tokenizer.lex();
4000 bool isBlock = false;
4001 bool trimLeft = false;
4002 bool localScope = false;
4003 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
4004 if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="{")
4005 {
4007 parser()->tokenizer.lex();
4009 StringVector optList=split(parser()->context.token->name.str(),",");
4010 auto contains = [&optList](const char *kw)
4011 {
4012 return std::find(optList.begin(),optList.end(),kw)!=optList.end();
4013 };
4014 localScope = contains("local");
4015 if (contains("nostrip"))
4016 {
4017 stripCodeComments = false;
4018 }
4019 else if (contains("strip"))
4020 {
4021 stripCodeComments = true;
4022 }
4023 if (t==DocInclude::Snippet && contains("trimleft"))
4024 {
4025 trimLeft = true;
4026 }
4027
4028 if (contains("lineno"))
4029 {
4033 }
4034 tok=parser()->tokenizer.lex();
4035 if (!tok.is(TokenRetval::TK_WHITESPACE))
4036 {
4037 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
4038 saveCmdName);
4039 return;
4040 }
4041 }
4042 else if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="[")
4043 {
4045 parser()->tokenizer.lex();
4046 isBlock = (parser()->context.token->name.stripWhiteSpace() == "block");
4048 parser()->tokenizer.lex();
4049 }
4050 else if (!tok.is(TokenRetval::TK_WHITESPACE))
4051 {
4052 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
4053 saveCmdName);
4054 return;
4055 }
4057 tok=parser()->tokenizer.lex();
4059 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4060 {
4061 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
4062 "argument of command {}",saveCmdName);
4063 return;
4064 }
4065 else if (!tok.is(TokenRetval::TK_WORD))
4066 {
4067 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
4068 tok.to_string(),saveCmdName);
4069 return;
4070 }
4071 DString fileName = parser()->context.token->name;
4072 DString blockId;
4074 {
4075 if (fileName == "this") fileName=parser()->context.fileName;
4077 tok=parser()->tokenizer.lex();
4079 if (!tok.is(TokenRetval::TK_WORD))
4080 {
4081 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected block identifier, but found token {} instead while parsing the {} command",
4082 tok.to_string(),saveCmdName);
4083 return;
4084 }
4085 blockId = "["+parser()->context.token->name+"]";
4086 }
4087
4089 thisVariant(),
4090 fileName,
4091 localScope ? parser()->context.context : "",
4092 t,
4093 stripCodeComments,
4096 blockId,isBlock,trimLeft);
4098}
4099
4100void DocPara::handleSection(char cmdChar,const DString &cmdName)
4101{
4102 AUTO_TRACE("cmdName={}",cmdName);
4103 DString saveCmdName = cmdName;
4104 // get the argument of the section command.
4105 Token tok=parser()->tokenizer.lex();
4106 if (!tok.is(TokenRetval::TK_WHITESPACE))
4107 {
4108 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
4109 cmdChar,saveCmdName);
4110 return;
4111 }
4112 tok=parser()->tokenizer.lex();
4113 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4114 {
4115 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
4116 "argument of command '{:c}{}'", cmdChar,saveCmdName);
4117 return;
4118 }
4119 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
4120 {
4121 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
4122 tok.to_string(),cmdChar,saveCmdName);
4123 return;
4124 }
4127 parser()->tokenizer.lex();
4129}
4130
4131Token DocPara::handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level)
4132{
4133 AUTO_TRACE();
4134 children().append<DocHtmlHeader>(parser(),thisVariant(),tagHtmlAttribs,level);
4135 Token retval = children().get_last<DocHtmlHeader>()->parse();
4136 return retval.is(TokenRetval::RetVal_OK) ? Token::make_TK_NEWPARA() : retval;
4137}
4138
4139// For XML tags whose content is stored in attributes rather than
4140// contained within the element, we need a way to inject the attribute
4141// text into the current paragraph.
4142bool DocPara::injectToken(Token tok,const DString &tokText)
4143{
4144 AUTO_TRACE();
4145 parser()->context.token->name = tokText;
4146 return parser()->defaultHandleToken(thisVariant(),tok,children());
4147}
4148
4150{
4151 AUTO_TRACE();
4152 Token retval = parser()->tokenizer.lex();
4153 DString lang = parser()->context.token->name;
4154 if (!lang.empty() && lang.at(0)!='.')
4155 {
4156 lang="."+lang;
4157 }
4158 if (parser()->context.xmlComment)
4159 {
4160 parser()->context.token->verb = substitute(substitute(parser()->context.token->verb,"&lt;","<"),"&gt;",">");
4161 }
4162 // search for the first non-whitespace line, index is stored in li
4163 size_t i=0,li=0,l=parser()->context.token->verb.length();
4164 while (i<l && (parser()->context.token->verb.at(i)==' ' || parser()->context.token->verb.at(i)=='\n'))
4165 {
4166 if (parser()->context.token->verb.at(i)=='\n') li=i+1;
4167 i++;
4168 }
4171 stripIndentation(parser()->context.token->verb.mid(li)),
4175 false,lang);
4176 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4177 {
4178 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"code section ended without end marker");
4179 }
4181 AUTO_TRACE_EXIT("retval={}",retval.to_string());
4182 return retval;
4183}
4184
4186{
4187 if (parser()->context.memberDef) // inheriting docs from a member
4188 {
4189 const MemberDef *reMd = parser()->context.memberDef->reimplements();
4190 if (reMd) // member from which was inherited.
4191 {
4192 const MemberDef *thisMd = parser()->context.memberDef;
4193 bool hasParamCommand = false;
4194 bool hasReturnCommand = false;
4195 StringMultiSet retvalsFound;
4196 StringMultiSet paramsFound;
4197 { DocParser::AutoSaveContext saveContext(*parser());
4198 //printf("{InheritDocs:%s=>%s}\n",qPrint(parser()->context.memberDef->qualifiedName()),qPrint(reMd->qualifiedName()));
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 hasParamCommand = parser()->context.hasParamCommand;
4212 hasReturnCommand = parser()->context.hasReturnCommand;
4213 retvalsFound = parser()->context.retvalsFound;
4214 paramsFound = parser()->context.paramsFound;
4215 }
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 DString &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;
4326 parser()->context.inSeeBlock=true;
4328 parser()->context.inSeeBlock=false;
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 DString fullMatch = parser()->context.token->verb;
4512 size_t idx = fullMatch.find('{');
4513 size_t idxEnd = idx!=DString::npos ? fullMatch.find("}",idx+1) : DString::npos;
4514 StringVector optList;
4515 if (idx!=DString::npos && idxEnd!=DString::npos) // options present
4516 {
4517 DString 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 DString locOpt(opt);
4523 locOpt = locOpt.stripWhiteSpace().lower();
4524 if (locOpt == "code")
4525 {
4527 }
4528 else if (!locOpt.empty())
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 DString 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 DString 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 bool plantumlEnabled = PlantumlManager::isEnabled();
4631 parser()->tokenizer.lex();
4632 DString fullMatch = parser()->context.token->sectionId;
4633 DString sectionId = "";
4634 size_t idx = fullMatch.find('{');
4635 size_t idxEnd = idx!=DString::npos ? fullMatch.find("}",idx+1) : DString::npos;
4636 StringVector optList;
4637 DString engine;
4638 if (idx!=DString::npos && idxEnd!=DString::npos) // options present
4639 {
4640 DString 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 DString locOpt(opt);
4647 locOpt = locOpt.stripWhiteSpace().lower();
4648 if (g_plantumlEngine.find(locOpt.str())!=g_plantumlEngine.end())
4649 {
4650 if (!engine.empty())
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.empty())
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.empty()) engine = "uml";
4675
4676 if (sectionId.empty())
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 DString plantFile(sectionId);
4691 false,plantFile);
4693 dv->setEngine(engine);
4695 DString width,height;
4696 parser()->defaultHandleTitleAndSize(CommandType::CMD_STARTUML,&children().back(),dv->children(),width,height);
4698 retval = parser()->tokenizer.lex();
4699 int line = 0;
4700 DString trimmedVerb = stripLeadingAndTrailingEmptyLines(parser()->context.token->verb,line);
4701 if (engine == "ditaa")
4702 {
4703 dv->setUseBitmap(true);
4704 }
4705 else if (engine == "uml")
4706 {
4707 size_t i = trimmedVerb.find('\n');
4708 DString firstLine = i==DString::npos ? 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 (!plantumlEnabled)
4716 {
4717 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"ignoring \\startuml command because neither PLANTUML_JAR_PATH nor PLANTUML_TOOL is 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 {
4730 parser()->tokenizer.lex();
4731 DString fullMatch = parser()->context.token->sectionId;
4732 DString sectionId = "";
4733 size_t idx = fullMatch.find('{');
4734 size_t idxEnd = idx!=DString::npos ? fullMatch.find("}",idx+1) : DString::npos;
4735 if (idx!=DString::npos && idxEnd!=DString::npos) // options present
4736 {
4737 sectionId = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4738 }
4739 else
4740 {
4741 sectionId = parser()->context.token->sectionId;
4742 }
4743
4744 if (sectionId.empty())
4745 {
4747 retval = parser()->tokenizer.lex();
4748 ASSERT(retval.is(TokenRetval::RetVal_OK));
4749
4750 sectionId = parser()->context.token->sectionId;
4751 sectionId = sectionId.stripWhiteSpace();
4752 }
4753
4754 DString mermaidFile(sectionId);
4759 false,mermaidFile);
4762 DString width,height;
4763 parser()->defaultHandleTitleAndSize(CommandType::CMD_MERMAID,&children().back(),dv->children(),width,height);
4765 retval = parser()->tokenizer.lex();
4766 int line = 0;
4767 DString trimmedVerb = stripLeadingAndTrailingEmptyLines(parser()->context.token->verb,line);
4768 dv->setText(trimmedVerb);
4769 dv->setWidth(width);
4770 dv->setHeight(height);
4771 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4772 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4773 {
4774 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"mermaid section ended without end marker");
4775 }
4777 }
4778 break;
4780 retval = Token::make_RetVal_EndParBlock();
4781 break;
4798 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4799 break;
4801 retval = handleParamSection(cmdName,DocParamSect::Param,false,parser()->context.token->paramDir);
4803 break;
4805 retval = handleParamSection(cmdName,DocParamSect::TemplateParam,false,parser()->context.token->paramDir);
4806 break;
4808 retval = handleParamSection(cmdName,DocParamSect::RetVal);
4809 break;
4812 break;
4814 retval = handleXRefItem();
4815 break;
4817 {
4819 }
4820 break;
4823 {
4825 }
4826 break;
4828 {
4830 }
4831 break;
4833 {
4837 retval = children().get_last<DocIndexEntry>()->parse();
4838 }
4839 break;
4841 retval = Token::make_RetVal_Internal();
4842 break;
4844 retval = Token::make_RetVal_EndInternal();
4845 break;
4847 {
4849 retval = children().get_last<DocParBlock>()->parse();
4850 }
4851 break;
4852 case CommandType::CMD_COPYDOC: // fall through
4853 case CommandType::CMD_COPYBRIEF: // fall through
4855 //retval = Token::make_RetVal_CopyDoc();
4856 // these commands should already be resolved by processCopyDoc()
4857 break;
4860 break;
4863 break;
4866 break;
4869 break;
4872 break;
4875 break;
4878 break;
4881 break;
4884 break;
4887 break;
4890 break;
4893 break;
4896 break;
4899 break;
4902 break;
4905 break;
4908 break;
4910 if (!Config_getBool(HAVE_DOT))
4911 {
4912 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
4913 "ignoring \\dotfile command because HAVE_DOT is not set");
4914 }
4915 else
4916 {
4917 handleFile<DocDotFile>(cmdName);
4918 }
4919 break;
4922 break;
4924 handleFile<DocMscFile>(cmdName);
4925 break;
4927 handleFile<DocDiaFile>(cmdName);
4928 break;
4931 break;
4934 break;
4936 handleLink(cmdName,false);
4937 break;
4939 handleLink(cmdName,true);
4940 break;
4942 {
4944 }
4945 break;
4947 handleEmoji(cmdChar,cmdName);
4948 break;
4950 handleDoxyConfig(cmdChar,cmdName);
4951 break;
4953 // fall through
4955 parser()->handleRef(thisVariant(),children(),cmdChar,cmdName);
4956 break;
4958 {
4961 }
4962 break;
4964 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4965 break;
4967 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4968 break;
4970 {
4972 }
4973 break;
4974 //case CommandType::CMD_LANGSWITCH:
4975 // retval = handleLanguageSwitch();
4976 // break;
4978 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4979 {
4982 }
4983 break;
4986 break;
4988 handleShowDate(cmdChar,cmdName);
4989 break;
4991 parser()->handleILine(cmdChar,cmdName);
4992 break;
4994 parser()->handleIFile(cmdChar,cmdName);
4995 break;
4997 {
4999 (void)parser()->tokenizer.lex();
5001 //printf("Found scope='%s'\n",qPrint(parser()->context.context));
5003 }
5004 break;
5005 default:
5006 // we should not get here!
5007 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' in paragraph context",cmdName);
5008 break;
5009 }
5010 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_OK,TokenRetval::RetVal_SimpleSec
5011 TokenRetval::TK_LISTITEM,TokenRetval::TK_ENDLIST,TokenRetval::TK_NEWPARA
5012 TokenRetval::RetVal_Section,TokenRetval::RetVal_EndList
5013 TokenRetval::RetVal_Internal,TokenRetval::RetVal_SwitchLang
5014 TokenRetval::RetVal_EndInternal)
5015 );
5016 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5017 return retval;
5018}
5019
5020static bool findAttribute(const HtmlAttribList &tagHtmlAttribs,
5021 const char *attrName,
5022 DString *result)
5023{
5024
5025 for (const auto &opt : tagHtmlAttribs)
5026 {
5027 if (opt.name==attrName)
5028 {
5029 *result = opt.value;
5030 return true;
5031 }
5032 }
5033 return false;
5034}
5035
5036Token DocPara::handleHtmlStartTag(const DString &tagName,const HtmlAttribList &tagHtmlAttribs)
5037{
5038 AUTO_TRACE("tagName={} #tagHtmlAttrs={}",tagName,tagHtmlAttribs.size());
5039 Token retval = Token::make_RetVal_OK();
5040 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
5041 if (parser()->context.token->emptyTag && !(tagId>HtmlTagType::XML_CmdMask) &&
5044 {
5045 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"HTML tag ('<{}/>') may not use the 'empty tag' XHTML syntax.",
5046 tagName);
5047 }
5048 switch (tagId)
5049 {
5051 if (!parser()->context.token->emptyTag)
5052 {
5054 tagHtmlAttribs,DocHtmlList::Unordered);
5055 retval=children().get_last<DocHtmlList>()->parse();
5056 }
5057 break;
5059 if (!parser()->context.token->emptyTag)
5060 {
5062 tagHtmlAttribs,DocHtmlList::Ordered);
5063 retval=children().get_last<DocHtmlList>()->parse();
5064 }
5065 break;
5067 if (parser()->context.token->emptyTag) break;
5069 {
5070 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <li> tag found");
5071 }
5072 else
5073 {
5074 retval = Token::make_RetVal_ListItem();
5075 }
5076 break;
5078 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Bold,tagName,&parser()->context.token->attribs);
5079 break;
5081 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::S,tagName,&parser()->context.token->attribs);
5082 break;
5084 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Strike,tagName,&parser()->context.token->attribs);
5085 break;
5087 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Del,tagName,&parser()->context.token->attribs);
5088 break;
5090 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Underline,tagName,&parser()->context.token->attribs);
5091 break;
5093 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Ins,tagName,&parser()->context.token->attribs);
5094 break;
5096 if (parser()->context.token->emptyTag) break;
5097 if (parser()->context.xmlComment)
5098 // for C# source or inside a <summary> or <remark> section we
5099 // treat <code> as an XML tag (so similar to @code)
5100 {
5102 retval = handleStartCode();
5103 }
5104 else // normal HTML markup
5105 {
5106 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
5107 }
5108 break;
5110 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Kbd,tagName,&parser()->context.token->attribs);
5111 break;
5113 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Typewriter,tagName,&parser()->context.token->attribs);
5114 break;
5116 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Italic,tagName,&parser()->context.token->attribs);
5117 break;
5119 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Div,tagName,&parser()->context.token->attribs);
5120 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Div,tagName);
5121 break;
5123 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Span,tagName,&parser()->context.token->attribs);
5124 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Span,tagName);
5125 break;
5127 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Subscript,tagName,&parser()->context.token->attribs);
5128 break;
5130 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Superscript,tagName,&parser()->context.token->attribs);
5131 break;
5133 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Center,tagName,&parser()->context.token->attribs);
5134 break;
5136 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Small,tagName,&parser()->context.token->attribs);
5137 break;
5139 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Cite,tagName,&parser()->context.token->attribs);
5140 break;
5142 if (parser()->context.token->emptyTag) break;
5143 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Preformatted,tagName,&parser()->context.token->attribs);
5145 parser()->tokenizer.setInsidePre(true);
5146 break;
5148 retval = Token::make_TK_NEWPARA();
5149 break;
5151 if (!parser()->context.token->emptyTag)
5152 {
5153 children().append<DocHtmlDescList>(parser(),thisVariant(),tagHtmlAttribs);
5154 retval=children().get_last<DocHtmlDescList>()->parse();
5155 }
5156 break;
5158 if (insideDL(thisVariant()))
5159 {
5160 retval = Token::make_RetVal_DescTitle();
5161 }
5162 else
5163 {
5164 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dt> found");
5165 }
5166 break;
5168 if (insideDL(thisVariant()))
5169 {
5170 retval = Token::make_RetVal_DescData();
5171 }
5172 else
5173 {
5174 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dd> found");
5175 }
5176 break;
5178 if (!parser()->context.token->emptyTag)
5179 {
5180 children().append<DocHtmlTable>(parser(),thisVariant(),tagHtmlAttribs);
5181 retval=children().get_last<DocHtmlTable>()->parse();
5182 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5183 }
5184 break;
5186 retval = Token::make_RetVal_TableRow();
5187 break;
5189 retval = Token::make_RetVal_TableCell();
5190 break;
5192 retval = Token::make_RetVal_TableHCell();
5193 break;
5197 // for time being ignore </t....> tag
5198 break;
5200 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <caption> found");
5201 break;
5203 {
5204 children().append<DocLineBreak>(parser(),thisVariant(),tagHtmlAttribs);
5205 }
5206 break;
5208 {
5209 children().append<DocHorRuler>(parser(),thisVariant(),tagHtmlAttribs);
5210 }
5211 break;
5213 retval = parser()->handleAHref(thisVariant(),children(),tagHtmlAttribs);
5214 break;
5216 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,1);
5217 break;
5219 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,2);
5220 break;
5222 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,3);
5223 break;
5225 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,4);
5226 break;
5228 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,5);
5229 break;
5231 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,6);
5232 break;
5234 {
5235 parser()->handleImg(thisVariant(),children(),tagHtmlAttribs);
5236 }
5237 break;
5239 if (!parser()->context.token->emptyTag)
5240 {
5241 children().append<DocHtmlDetails>(parser(),thisVariant(),tagHtmlAttribs);
5242 retval=children().get_last<DocHtmlDetails>()->parse();
5243 }
5244 break;
5246 if (!parser()->context.token->emptyTag)
5247 {
5248 children().append<DocHtmlBlockQuote>(parser(),thisVariant(),tagHtmlAttribs);
5249 retval = children().get_last<DocHtmlBlockQuote>()->parse();
5250 }
5251 break;
5252
5255 {
5256 if (!parser()->context.token->emptyTag)
5257 {
5259 while (n && !std::holds_alternative<DocHtmlDetails>(*n)) n=::parent(n);
5260 DocHtmlDetails *d = std::get_if<DocHtmlDetails>(n);
5261 if (d)
5262 {
5263 if (!d->summary()) // details section does not have a summary yet
5264 {
5265 d->parseSummary(n,parser()->context.token->attribs);
5266 }
5267 else
5268 {
5269 retval = Token::make_TK_NEWPARA();
5270 }
5271 }
5272 }
5273 }
5274 break;
5277 parser()->context.xmlComment=true;
5278 // fall through
5281 if (!children().empty())
5282 {
5283 retval = Token::make_TK_NEWPARA();
5284 }
5285 break;
5287 if (insideTable(thisVariant()))
5288 {
5289 retval = Token::make_RetVal_TableCell();
5290 }
5291 break;
5292 case HtmlTagType::XML_C:
5293 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
5294 break;
5297 {
5298 parser()->context.xmlComment=true;
5299 DString paramName;
5300 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5301 {
5302 if (paramName.empty())
5303 {
5304 if (Config_getBool(WARN_NO_PARAMDOC))
5305 {
5306 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"empty 'name' attribute for <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5307 }
5308 }
5309 else
5310 {
5311 retval = handleParamSection(paramName,
5313 true);
5314 }
5315 }
5316 else
5317 {
5318 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5319 }
5320 }
5321 break;
5324 {
5325 DString paramName;
5326 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5327 {
5328 //printf("paramName=%s\n",qPrint(paramName));
5330 children().append<DocWord>(parser(),thisVariant(),paramName);
5332 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
5333 }
5334 else
5335 {
5336 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}ref> tag.",tagId==HtmlTagType::XML_PARAMREF?"":"type");
5337 }
5338 }
5339 break;
5341 {
5342 parser()->context.xmlComment=true;
5343 DString exceptName;
5344 if (findAttribute(tagHtmlAttribs,"cref",&exceptName))
5345 {
5346 unescapeCRef(exceptName);
5347 retval = handleParamSection(exceptName,DocParamSect::Exception,true);
5348 }
5349 else
5350 {
5351 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <exception> tag.");
5352 }
5353 }
5354 break;
5357 if (insideTable(thisVariant()))
5358 {
5359 retval = Token::make_RetVal_TableRow();
5360 }
5361 else if (insideUL(thisVariant()) || insideOL(thisVariant()))
5362 {
5363 retval = Token::make_RetVal_ListItem();
5364 }
5365 else
5366 {
5367 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <item> tag found");
5368 }
5369 break;
5371 parser()->context.xmlComment=true;
5374 break;
5376 if (insideTable(thisVariant()))
5377 {
5378 retval = Token::make_RetVal_TableCell();
5379 }
5380 break;
5382 // I'm not sure if <see> is the same as <seealso> or if it
5383 // should you link a member without producing a section. The
5384 // C# specification is extremely vague about this (but what else
5385 // can we expect from Microsoft...)
5386 {
5387 DString cref;
5388 //printf("HtmlTagType::XML_SEE: empty tag=%d\n",parser()->context.token->emptyTag);
5389 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5390 {
5391 unescapeCRef(cref);
5392 if (parser()->context.token->emptyTag) // <see cref="..."/> style
5393 {
5394 bool inSeeBlock = parser()->context.inSeeBlock;
5395 parser()->context.token->name = cref;
5396 parser()->context.inSeeBlock = true;
5398 parser()->context.inSeeBlock = inSeeBlock;
5399 }
5400 else // <see cref="...">...</see> style
5401 {
5404 DocLink *lnk = children().get_last<DocLink>();
5405 DString leftOver = lnk->parse(false,true);
5406 if (!leftOver.empty())
5407 {
5408 children().append<DocWord>(parser(),thisVariant(),leftOver);
5409 }
5410 }
5411 }
5412 else if (findAttribute(tagHtmlAttribs,"langword",&cref)) // <see langword="..."/> or <see langword="..."></see>
5413 {
5414 bool inSeeBlock = parser()->context.inSeeBlock;
5415 parser()->context.token->name = cref;
5416 parser()->context.inSeeBlock = true;
5420 parser()->context.inSeeBlock = inSeeBlock;
5421 }
5422 else
5423 {
5424 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' or 'langword' attribute from <see> tag.");
5425 }
5426 }
5427 break;
5429 {
5430 parser()->context.xmlComment=true;
5431 DString cref;
5432 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5433 {
5434 unescapeCRef(cref);
5435 // Look for an existing "see" section
5436 DocNodeVariant *vss=nullptr;
5437 for (auto &n : children())
5438 {
5439 DocSimpleSect *candidate = std::get_if<DocSimpleSect>(&n);
5440 if (candidate && candidate->type()==DocSimpleSect::See)
5441 {
5442 vss = &n;
5443 }
5444 }
5445
5446 if (!vss) // start new section
5447 {
5449 vss = &children().back();
5450 }
5451
5452 std::get<DocSimpleSect>(*vss).appendLinkWord(cref);
5453 retval = Token::make_RetVal_OK();
5454 }
5455 else
5456 {
5457 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <seealso> tag.");
5458 }
5459 }
5460 break;
5462 {
5463 DString type;
5464 findAttribute(tagHtmlAttribs,"type",&type);
5466 HtmlAttribList emptyList;
5467 if (type=="number")
5468 {
5469 listType=DocHtmlList::Ordered;
5470 }
5471 if (type=="table")
5472 {
5473 children().append<DocHtmlTable>(parser(),thisVariant(),emptyList);
5474 retval=children().get_last<DocHtmlTable>()->parseXml();
5475 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5476 }
5477 else
5478 {
5479 children().append<DocHtmlList>(parser(),thisVariant(),emptyList,listType);
5480 retval=children().get_last<DocHtmlList>()->parseXml();
5481 }
5482 }
5483 break;
5486 // These tags are defined in .Net but are currently unsupported
5487 parser()->context.xmlComment=true;
5488 break;
5490 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag <{}> found", tagName);
5491 children().append<DocWord>(parser(),thisVariant(), "<"+tagName+parser()->context.token->attribsStr+">");
5492 break;
5495 break;
5496 default:
5497 // we should not get here!
5498 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected start tag {}",tagName);
5499 ASSERT(0);
5500 break;
5501 }
5502 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5503 return retval;
5504}
5505
5507{
5508 AUTO_TRACE("tagName={}",tagName);
5509 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
5510 Token retval = Token::make_RetVal_OK();
5511 switch (tagId)
5512 {
5514 if (!insideUL(thisVariant()))
5515 {
5516 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ul> tag without matching <ul>");
5517 }
5518 else
5519 {
5520 retval = Token::make_RetVal_EndList();
5521 }
5522 break;
5524 if (!insideOL(thisVariant()))
5525 {
5526 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ol> tag without matching <ol>");
5527 }
5528 else
5529 {
5530 retval = Token::make_RetVal_EndList();
5531 }
5532 break;
5534 if (!insideLI(thisVariant()))
5535 {
5536 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </li> tag without matching <li>");
5537 }
5538 else
5539 {
5540 // ignore </li> tags
5541 }
5542 break;
5544 if (!insideDetails(thisVariant()))
5545 {
5546 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </details> tag without matching <details>");
5547 }
5548 else
5549 {
5550 retval = Token::make_RetVal_EndHtmlDetails();
5551 }
5552 break;
5555 {
5556 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </blockquote> tag without matching <blockquote>");
5557 }
5558 else
5559 {
5560 retval = Token::make_RetVal_EndBlockQuote();
5561 }
5562 break;
5565 break;
5568 break;
5571 break;
5574 break;
5577 break;
5580 break;
5583 break;
5586 break;
5589 break;
5592 break;
5595 break;
5598 break;
5601 break;
5604 break;
5607 break;
5610 break;
5613 break;
5616 setInsidePreformatted(false);
5617 parser()->tokenizer.setInsidePre(false);
5618 break;
5620 retval = Token::make_TK_NEWPARA();
5621 break;
5623 retval = Token::make_RetVal_EndDesc();
5624 break;
5626 // ignore </dt> tag
5627 break;
5629 // ignore </dd> tag
5630 break;
5632 retval = Token::make_RetVal_EndTable();
5633 break;
5635 retval = Token::make_RetVal_EndTableRow();
5636 break;
5638 retval = Token::make_RetVal_EndTableCell();
5639 break;
5641 retval = Token::make_RetVal_EndTableCell();
5642 break;
5646 // for time being ignore </t....> tag
5647 break;
5649 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </caption> found");
5650 break;
5652 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </br> tag found");
5653 break;
5655 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h1> found");
5656 break;
5658 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h2> found");
5659 break;
5661 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h3> found");
5662 break;
5664 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h4> found");
5665 break;
5667 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h5> found");
5668 break;
5670 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h6> found");
5671 break;
5673 break;
5675 // warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </hr> tag found");
5676 break;
5678 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </a> found");
5679 // ignore </a> tag (can be part of <a name=...></a>
5680 break;
5681
5683 break;
5685 retval = Token::make_TK_NEWPARA();
5686 break;
5699 retval = Token::make_RetVal_CloseXml();
5700 break;
5701 case HtmlTagType::XML_C:
5703 break;
5711 // These tags are defined in .Net but are currently unsupported
5712 break;
5714 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag </{}> found", tagName);
5715 children().append<DocWord>(parser(),thisVariant(),"</"+tagName+">");
5716 break;
5717 default:
5718 // we should not get here!
5719 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end tag {}",tagName);
5720 ASSERT(0);
5721 break;
5722 }
5723 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5724 return retval;
5725}
5726
5728{
5729 // expected hierarchy:
5730 // 1. DocAutoListItem <- n
5731 // 2. DocAutoList <- parent(n)
5732 // 3. DocPara <- parent(parent(n))
5733
5734 // step 1
5735 if (!std::get_if<DocAutoListItem>(n)) // not inside a auto list item
5736 {
5737 return false;
5738 }
5739
5740 // step 2
5741 n = parent(n);
5742 int indent = 0;
5743 const auto &docAutoList = std::get_if<DocAutoList>(n);
5744 if (docAutoList) // capture indent
5745 {
5746 indent = docAutoList->indent();
5747 }
5748 else
5749 {
5750 return false;
5751 }
5752
5753 // step 3
5754 n = parent(n);
5755 const auto &docPara = std::get_if<DocPara>(n);
5756 if (docPara)
5757 {
5758 DString tagNameLower = DString(parser->context.token->name).lower();
5759 auto topStyleChange = [](const DocStyleChangeStack &stack) -> const DocStyleChange &
5760 {
5761 return std::get<DocStyleChange>(*stack.top());
5762 };
5763
5764 if (parser->context.styleStack.empty() || // no style change
5765 (topStyleChange(parser->context.styleStack).tagName()==tagNameLower && // correct style change
5766 topStyleChange(parser->context.styleStack).position()!=parser->context.nodeStack.size()) // wrong position, so normal close
5767 )
5768 {
5769 // insert an artificial 'end of autolist' marker and parse again
5770 DString indentStr;
5771 indentStr.fill(' ',indent);
5772 parser->tokenizer.unputString("\\ilinebr "+indentStr+".\\ilinebr"+indentStr+"</"+parser->context.token->name+">");
5773 return true;
5774 }
5775 }
5776 return false;
5777}
5778
5780{
5781 AUTO_TRACE();
5782 auto ns = AutoNodeStack(parser(),thisVariant());
5783 // handle style commands "inherited" from the previous paragraph
5785 Token tok=parser()->tokenizer.lex();
5786 Token retval = Token::make_TK_NONE();
5787 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
5788 {
5789reparsetoken:
5790 AUTO_TRACE_ADD("token '{}' at {}",tok.to_string(),parser()->tokenizer.getLineNr());
5791 if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL,
5792 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG)
5793 )
5794 {
5795 AUTO_TRACE_ADD("name={}",parser()->context.token->name);
5796 }
5797 switch(tok.value())
5798 {
5799 case TokenRetval::TK_WORD:
5801 break;
5802 case TokenRetval::TK_LNKWORD:
5804 break;
5805 case TokenRetval::TK_URL:
5807 break;
5808 case TokenRetval::TK_WHITESPACE:
5809 {
5810 // prevent leading whitespace and collapse multiple whitespace areas
5811 if (insidePRE(thisVariant()) || // all whitespace is relevant
5812 (
5813 // remove leading whitespace
5814 !children().empty() &&
5815 // and whitespace after certain constructs
5819 )
5820 )
5821 {
5823 }
5824 }
5825 break;
5826 case TokenRetval::TK_LISTITEM:
5827 {
5828 AUTO_TRACE_ADD("found list item at {}",parser()->context.token->indent);
5829 const DocNodeVariant *n=parent();
5830 while (n && !std::holds_alternative<DocAutoList>(*n)) n=::parent(n);
5831 const DocAutoList *al = std::get_if<DocAutoList>(n);
5832 if (al) // we found an auto list up in the hierarchy
5833 {
5834 AUTO_TRACE_ADD("previous list item at {}",al->indent());
5835 if (al->indent()>=parser()->context.token->indent)
5836 // new item at the same or lower indent level
5837 {
5838 retval = Token::make_TK_LISTITEM();
5839 goto endparagraph;
5840 }
5841 }
5842
5843 // determine list depth
5844 int depth = 0;
5845 n=parent();
5846 while (n)
5847 {
5848 al = std::get_if<DocAutoList>(n);
5849 if (al && al->isEnumList()) depth++;
5850 n=::parent(n);
5851 }
5852
5853 // first item or sub list => create new list
5854 do
5855 {
5858 parser()->context.token->isEnumList,depth,
5860 al = children().get_last<DocAutoList>();
5861 retval = children().get_last<DocAutoList>()->parse();
5862 } while (retval.is(TokenRetval::TK_LISTITEM) && // new list
5863 al->indent()==parser()->context.token->indent // at same indent level
5864 );
5865
5866 // check the return value
5867 if (retval.is(TokenRetval::RetVal_SimpleSec)) // auto list ended due to simple section command
5868 {
5869 // Reparse the token that ended the section at this level,
5870 // so a new simple section will be started at this level.
5871 // This is the same as unputting the last read token and continuing.
5873 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5874 {
5877 tok = Token::make_TK_RCSTAG();
5878 }
5879 else // other section
5880 {
5881 tok = Token::make_TK_COMMAND_BS();
5882 }
5883 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5884 goto reparsetoken;
5885 }
5886 else if (retval.is(TokenRetval::TK_ENDLIST))
5887 {
5888 if (al->indent()>parser()->context.token->indent) // end list
5889 {
5890 goto endparagraph;
5891 }
5892 else // continue with current paragraph
5893 {
5894 }
5895 }
5896 else // paragraph ended due to TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, or EOF
5897 {
5898 goto endparagraph;
5899 }
5900 }
5901 break;
5902 case TokenRetval::TK_ENDLIST:
5903 AUTO_TRACE_ADD("Found end of list inside of paragraph at line {}",parser()->tokenizer.getLineNr());
5904 if (std::get_if<DocAutoListItem>(parent()))
5905 {
5906 const DocAutoList *al = std::get_if<DocAutoList>(::parent(parent()));
5907 if (al && al->indent()>=parser()->context.token->indent)
5908 {
5909 // end of list marker ends this paragraph
5910 retval = Token::make_TK_ENDLIST();
5911 goto endparagraph;
5912 }
5913 else
5914 {
5915 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found "
5916 "has invalid indent level");
5917 }
5918 }
5919 else
5920 {
5921 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found without any preceding "
5922 "list items");
5923 }
5924 break;
5925 case TokenRetval::TK_COMMAND_AT:
5926 // fall through
5927 case TokenRetval::TK_COMMAND_BS:
5928 {
5929 // see if we have to start a simple section
5930 CommandType cmd = Mappers::cmdMapper->map(parser()->context.token->name);
5931 const DocNodeVariant *n=parent();
5932 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
5933 !std::holds_alternative<DocParamSect>(*n))
5934 {
5935 n=::parent(n);
5936 }
5938 {
5939 if (n) // already in a simple section
5940 {
5941 // simple section cannot start in this paragraph, need
5942 // to unwind the stack and remember the command.
5944 retval = Token::make_RetVal_SimpleSec();
5945 goto endparagraph;
5946 }
5947 }
5948 // see if we are in a simple list
5949 n=parent();
5950 while (n && !std::holds_alternative<DocSimpleListItem>(*n)) n=::parent(n);
5951 if (n)
5952 {
5953 if (cmd==CommandType::CMD_LI)
5954 {
5955 retval = Token::make_RetVal_ListItem();
5956 goto endparagraph;
5957 }
5958 }
5959
5960 // handle the command
5961 retval=handleCommand(tok.command_to_char(),parser()->context.token->name);
5962 AUTO_TRACE_ADD("handleCommand returns {}",retval.to_string());
5963
5964 // check the return value
5965 if (retval.is(TokenRetval::RetVal_SimpleSec))
5966 {
5967 // Reparse the token that ended the section at this level,
5968 // so a new simple section will be started at this level.
5969 // This is the same as unputting the last read token and continuing.
5971 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5972 {
5975 tok = Token::make_TK_RCSTAG();
5976 }
5977 else // other section
5978 {
5979 tok = Token::make_TK_COMMAND_BS();
5980 }
5981 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5982 goto reparsetoken;
5983 }
5984 else if (retval.value()>TokenRetval::TK_NONE && retval.value()<TokenRetval::RetVal_OK)
5985 {
5986 // the command ended with a new command, reparse this token
5987 tok = retval;
5988 goto reparsetoken;
5989 }
5990 else if (retval.value()!=TokenRetval::RetVal_OK) // end of file, end of paragraph, start or end of section
5991 // or some auto list marker
5992 {
5993 goto endparagraph;
5994 }
5995 }
5996 break;
5997 case TokenRetval::TK_HTMLTAG:
5998 {
5999 if (!parser()->context.token->endTag) // found a start tag
6000 {
6001 retval = handleHtmlStartTag(parser()->context.token->name,parser()->context.token->attribs);
6002 }
6003 else // found an end tag
6004 {
6006 {
6007 break; // new code has been pushed back to the scanner, need to reparse
6008 }
6009 retval = handleHtmlEndTag(parser()->context.token->name);
6010 }
6011 if (!retval.is(TokenRetval::RetVal_OK))
6012 {
6013 goto endparagraph;
6014 }
6015 }
6016 break;
6017 case TokenRetval::TK_SYMBOL:
6018 {
6019 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
6021 {
6023 }
6024 else
6025 {
6027 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
6028 parser()->context.token->name);
6029 }
6030 break;
6031 }
6032 case TokenRetval::TK_NEWPARA:
6033 retval = Token::make_TK_NEWPARA();
6034 goto endparagraph;
6035 case TokenRetval::TK_RCSTAG:
6036 {
6037 const DocNodeVariant *n=parent();
6038 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
6039 !std::holds_alternative<DocParamSect>(*n))
6040 {
6041 n=::parent(n);
6042 }
6043 if (n) // already in a simple section
6044 {
6045 // simple section cannot start in this paragraph, need
6046 // to unwind the stack and remember the command.
6049 retval = Token::make_RetVal_SimpleSec();
6050 goto endparagraph;
6051 }
6052
6053 // see if we are in a simple list
6055 children().get_last<DocSimpleSect>()->parseRcs();
6056 }
6057 break;
6058 default:
6059 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6060 "Found unexpected token (id={})",tok.to_string());
6061 break;
6062 }
6063 tok=parser()->tokenizer.lex();
6064 }
6065 retval=Token::make_TK_NONE();
6066endparagraph:
6068 DocPara *par = std::get_if<DocPara>(parser()->context.nodeStack.top());
6069 if (!parser()->context.token->endTag && par &&
6070 retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->name.lower() == "p")
6071 {
6072 par->setAttribs(parser()->context.token->attribs);
6073 }
6074 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM,
6075 TokenRetval::TK_ENDLIST,TokenRetval::RetVal_OK)
6076 );
6077
6078 AUTO_TRACE_EXIT("retval={}",retval.to_string());
6079 return retval;
6080}
6081
6082//--------------------------------------------------------------------------
6083
6085{
6086 AUTO_TRACE("start {} level={}", parser()->context.token->sectionId, m_level);
6087 Token retval = Token::make_RetVal_OK();
6088 auto ns = AutoNodeStack(parser(),thisVariant());
6089
6090 if (!m_id.empty())
6091 {
6093 if (sec)
6094 {
6095 m_file = sec->fileName();
6096 m_anchor = sec->label();
6097 DString titleStr = sec->title();
6098 if (titleStr.empty()) titleStr = sec->label();
6100 DocTitle *title = &std::get<DocTitle>(*m_title);
6101 title->parseFromString(thisVariant(),titleStr);
6102 }
6103 }
6104
6105 // first parse any number of paragraphs
6106 bool isFirst=true;
6107 DocPara *lastPar=nullptr;
6108 do
6109 {
6111 DocPara *par = children().get_last<DocPara>();
6112 if (isFirst) { par->markFirst(); isFirst=false; }
6113 retval=par->parse();
6114 if (!par->empty())
6115 {
6116 if (lastPar) lastPar->markLast(false);
6117 lastPar = par;
6118 }
6119 else
6120 {
6121 children().pop_back();
6122 }
6123 if (retval.is(TokenRetval::TK_LISTITEM))
6124 {
6125 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6126 }
6127 if (retval.is(TokenRetval::RetVal_Internal))
6128 {
6130 retval = children().get_last<DocInternal>()->parse(m_level+1);
6131 if (retval.is(TokenRetval::RetVal_EndInternal))
6132 {
6133 retval = Token::make_RetVal_OK();
6134 }
6135 }
6136 } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6137 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph,
6138 TokenRetval::RetVal_SubSubParagraph, TokenRetval::RetVal_EndInternal)
6139 );
6140
6141 if (lastPar) lastPar->markLast();
6142
6143 while (true)
6144 {
6145 if (retval.is(TokenRetval::RetVal_Subsection) && m_level<=1)
6146 {
6147 // then parse any number of nested sections
6148 while (retval.is(TokenRetval::RetVal_Subsection)) // more sections follow
6149 {
6151 2,
6153 retval = children().get_last<DocSection>()->parse();
6154 }
6155 break;
6156 }
6157 else if (retval.is(TokenRetval::RetVal_Subsubsection) && m_level<=2)
6158 {
6159 if ((m_level <= 1) &&
6160 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6161 {
6162 warn_doc_error(parser()->context.fileName,
6163 parser()->tokenizer.getLineNr(),
6164 "Unexpected subsubsection command found inside {}!",
6166 }
6167 // then parse any number of nested sections
6168 while (retval.is(TokenRetval::RetVal_Subsubsection)) // more sections follow
6169 {
6171 3,
6173 retval = children().get_last<DocSection>()->parse();
6174 }
6175 if (!(m_level < 2 && retval.is(TokenRetval::RetVal_Subsection))) break;
6176 }
6177 else if (retval.is(TokenRetval::RetVal_Paragraph) && m_level<=3)
6178 {
6179 if ((m_level <= 2) &&
6180 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6181 {
6182 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6183 "Unexpected paragraph command found inside {}!",
6185 }
6186 // then parse any number of nested sections
6187 while (retval.is(TokenRetval::RetVal_Paragraph)) // more sections follow
6188 {
6190 4,
6192 retval = children().get_last<DocSection>()->parse();
6193 }
6194 if (!(m_level<3 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection)))) break;
6195 }
6196 else if (retval.is(TokenRetval::RetVal_SubParagraph) && m_level<=4)
6197 {
6198 if ((m_level <= 3) &&
6199 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6200 {
6201 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6202 "Unexpected subparagraph command found inside {}!",
6204 }
6205 // then parse any number of nested sections
6206 while (retval.is(TokenRetval::RetVal_SubParagraph)) // more sections follow
6207 {
6209 5,
6211 retval = children().get_last<DocSection>()->parse();
6212 }
6213 if (!(m_level<4 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection,TokenRetval::RetVal_Paragraph)))) break;
6214 }
6215 else if (retval.is(TokenRetval::RetVal_SubSubParagraph) && m_level<=5)
6216 {
6217 if ((m_level <= 4) &&
6218 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6219 {
6220 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6221 "Unexpected subsubparagraph command found inside {}!",
6223 }
6224 // then parse any number of nested sections
6225 while (retval.is(TokenRetval::RetVal_SubSubParagraph)) // more sections follow
6226 {
6228 6,
6230 retval = children().get_last<DocSection>()->parse();
6231 }
6232 if (!(m_level<5 && (retval.is_any_of( TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection,
6233 TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph)))) break;
6234 }
6235 else
6236 {
6237 break;
6238 }
6239 }
6240
6241 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF,
6242 TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6243 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph,
6244 TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph,
6245 TokenRetval::RetVal_Internal, TokenRetval::RetVal_EndInternal)
6246 );
6247
6248 AUTO_TRACE_EXIT("retval={}", retval.to_string());
6249 return retval;
6250}
6251
6252//--------------------------------------------------------------------------
6253
6255{
6256 AUTO_TRACE();
6257 auto ns = AutoNodeStack(parser(),thisVariant());
6259
6260 Token tok = parser()->tokenizer.lex();
6261 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
6262 {
6263 switch(tok.value())
6264 {
6265 case TokenRetval::TK_WORD:
6267 break;
6268 case TokenRetval::TK_WHITESPACE:
6270 break;
6271 case TokenRetval::TK_SYMBOL:
6272 {
6273 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
6275 {
6277 }
6278 else
6279 {
6280 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
6281 parser()->context.token->name);
6282 }
6283 }
6284 break;
6285 case TokenRetval::TK_COMMAND_AT:
6286 // fall through
6287 case TokenRetval::TK_COMMAND_BS:
6288 switch (Mappers::cmdMapper->map(parser()->context.token->name))
6289 {
6292 break;
6295 break;
6298 break;
6301 break;
6304 break;
6307 break;
6310 break;
6313 break;
6316 break;
6320 break;
6325 break;
6328 break;
6331 break;
6334 break;
6337 break;
6340 break;
6343 break;
6346 break;
6347 default:
6348 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' found",
6349 parser()->context.token->name);
6350 break;
6351 }
6352 break;
6353 default:
6354 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",
6355 tok.to_string());
6356 break;
6357 }
6358 tok = parser()->tokenizer.lex();
6359 }
6360
6362
6363}
6364
6365
6366//--------------------------------------------------------------------------
6367
6369{
6370 AUTO_TRACE();
6371 auto ns = AutoNodeStack(parser(),thisVariant());
6373 Token retval = Token::make_TK_NONE();
6374
6375 // first parse any number of paragraphs
6376 bool isFirst=true;
6377 DocPara *lastPar = nullptr;
6378 do
6379 {
6380 {
6382 DocPara *par = children().get_last<DocPara>();
6383 if (isFirst) { par->markFirst(); isFirst=false; }
6384 retval=par->parse();
6385 if (par->empty() && par->attribs().empty())
6386 {
6387 children().pop_back();
6388 }
6389 else
6390 {
6391 lastPar = par;
6392 }
6393 }
6394 auto checkParagraph = [this,&retval](Token t,int level,const char *sectionType,const char *parentSectionType) {
6395 if (retval == t)
6396 {
6397 if (!AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6398 {
6399 warn_doc_error(parser()->context.fileName,
6400 parser()->tokenizer.getLineNr(),
6401 "found {} command (id: '{}') outside of {} context!",
6402 sectionType,parser()->context.token->sectionId,parentSectionType);
6403 }
6404 while (retval==t)
6405 {
6406 if (!parser()->context.token->sectionId.empty())
6407 {
6408 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6409 if (sec)
6410 {
6412 level,
6414 retval = children().get_last<DocSection>()->parse();
6415 }
6416 else
6417 {
6418 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid {} id '{}'; ignoring {}",
6419 sectionType,parser()->context.token->sectionId,sectionType);
6420 retval = Token::make_TK_NONE();
6421 }
6422 }
6423 else
6424 {
6425 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for {}; ignoring {}",sectionType,sectionType);
6426 retval = Token::make_TK_NONE();
6427 }
6428 }
6429 }
6430 };
6431 checkParagraph(Token::make_RetVal_SubSubParagraph(), 6, "subsubparagraph", "subparagraph" );
6432 checkParagraph(Token::make_RetVal_SubParagraph(), 5, "subparagraph", "paragraph" );
6433 checkParagraph(Token::make_RetVal_Paragraph(), 4, "paragraph", "subsubsection" );
6434 checkParagraph(Token::make_RetVal_Subsubsection(), 3, "subsubsection", "subsection" );
6435 checkParagraph(Token::make_RetVal_Subsection(), 2, "subsection", "section" );
6436
6437 if (retval.is(TokenRetval::TK_LISTITEM))
6438 {
6439 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6440 }
6441 if (retval.is(TokenRetval::RetVal_Internal))
6442 {
6444 retval = children().get_last<DocInternal>()->parse(1);
6445 }
6446 } while (!retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_Section));
6447 if (lastPar) lastPar->markLast();
6448
6449 //printf("DocRoot::parse() retval=%d %d\n",retval,TokenRetval::RetVal_Section);
6450 // then parse any number of level1 sections
6451 while (retval.is(TokenRetval::RetVal_Section))
6452 {
6453 if (!parser()->context.token->sectionId.empty())
6454 {
6455 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6456 if (sec)
6457 {
6459 1,
6461 retval = children().get_last<DocSection>()->parse();
6462 }
6463 else
6464 {
6465 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid section id '{}'; ignoring section",parser()->context.token->sectionId);
6466 retval = Token::make_TK_NONE();
6467 }
6468 }
6469 else
6470 {
6471 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for section; ignoring section");
6472 retval = Token::make_TK_NONE();
6473 }
6474 }
6475
6477}
bool isAliasCmd(std::string_view aliasCmd)
Definition aliases.cpp:519
static AnchorGenerator & instance()
Returns the singleton instance.
Definition anchor.cpp:41
Citation manager class.
Definition cite.h:85
const CiteInfo * find(const DString &label) const
Return the citation info for a given label.
Definition cite.cpp:106
static CitationManager & instance()
Definition cite.cpp:90
DString anchorPrefix() const
Definition cite.cpp:131
DString fileName() const
Definition cite.cpp:126
Class representing a Boolean type option.
Definition configimpl.h:251
DString * valueStringRef()
Definition configimpl.h:261
Class representing an enum type option.
Definition configimpl.h:153
DString * valueRef()
Definition configimpl.h:165
static ConfigImpl * instance()
Definition configimpl.h:347
ConfigOption * get(const DString &name) const
Definition configimpl.h:396
Class representing an integer type option.
Definition configimpl.h:216
DString * valueStringRef()
Definition configimpl.h:228
Class representing a list type option.
Definition configimpl.h:121
Abstract base class for any configuration option.
Definition configimpl.h:35
@ O_Disabled
Disabled compile time option.
Definition configimpl.h:51
@ O_List
A list of items.
Definition configimpl.h:45
@ O_Enum
A fixed set of items.
Definition configimpl.h:46
@ O_Bool
A boolean value.
Definition configimpl.h:49
@ O_String
A single item.
Definition configimpl.h:47
@ O_Obsolete
An obsolete option.
Definition configimpl.h:50
@ O_Int
An integer value.
Definition configimpl.h:48
@ O_Info
A section header.
Definition configimpl.h:44
OptionType kind() const
Definition configimpl.h:66
Class representing a string type option.
Definition configimpl.h:184
DString * valueRef()
Definition configimpl.h:197
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
void clear()
Definition dstring.h:214
DString fill(char c, size_t len)
Fills a string with a predefined character.
Definition dstring.h:278
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:318
DString lower() const
Definition dstring.h:326
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
DString substr(size_t pos=0, size_t count=npos) const
Returns a substring of length count starting at pos.
Definition dstring.h:223
std::string_view view() const
Definition dstring.h:162
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:178
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:686
DString & append(char c)
Definition dstring.h:489
DString right(size_t len) const
Definition dstring.h:311
DString & prepend(const char *s)
Definition dstring.h:515
int contains(char c, bool cs=true) const
Definition dstring.cpp:85
size_t find(char c, size_t pos=0) const
Definition dstring.h:239
DString & sprintf(const char *format,...)
Definition dstring.cpp:34
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:337
DString left(size_t len) const
Definition dstring.h:306
const std::string & str() const
Definition dstring.h:645
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:157
bool startsWith(const char *s) const
Definition dstring.h:600
bool endsWith(const char *s) const
Definition dstring.h:617
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:151
The common base class of all entity definitions found in the sources.
Definition definition.h:77
virtual DString briefDescription(bool abbreviate=false) const =0
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual DString documentation() const =0
virtual bool isLinkable() const =0
virtual DefType definitionType() const =0
virtual const DString & name() const =0
virtual DString getReference() const =0
virtual Definition * getOuterScope() const =0
virtual DString getSourceFileBase() const =0
virtual DString getOutputFileBase() const =0
DString m_file
Definition docnode.h:239
DString m_anchor
Definition docnode.h:238
DocAnchor(DocParser *parser, DocNodeVariant *parent, const DString &id, bool newAnchor)
Definition docnode.cpp:224
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:3156
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:3149
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:3109
DString getText() const
Definition docnode.cpp:988
DString m_file
Definition docnode.h:257
DString m_anchor
Definition docnode.h:260
DString m_ref
Definition docnode.h:259
DocCite(DocParser *parser, DocNodeVariant *parent, const DString &target, const DString &context, CiteInfoOption opt)
Definition docnode.cpp:952
DString m_relPath
Definition docnode.h:258
DString m_target
Definition docnode.h:261
DString target() const
Definition docnode.h:252
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:1225
DocDiaFile(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.cpp:1218
DString name() const
Definition docnode.h:684
DocDiagramFileBase(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.h:681
DString srcFile() const
Definition docnode.h:691
std::unique_ptr< Private > p
Definition docnode.h:708
DString context() const
Definition docnode.h:690
int srcLine() const
Definition docnode.h:692
bool parse()
Definition docnode.cpp:1147
DocDotFile(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.cpp:1140
Node representing an emoji.
Definition docnode.h:341
DocEmoji(DocParser *parser, DocNodeVariant *parent, const DString &symName)
Definition docnode.cpp:175
DString m_symName
Definition docnode.h:348
int m_index
Definition docnode.h:349
Node representing an item of a cross-referenced list.
Definition docnode.h:529
DString m_name
Definition docnode.h:544
DString relPath() const
Definition docnode.h:534
DString m_text
Definition docnode.h:545
DString m_relPath
Definition docnode.h:546
int id() const
Definition docnode.h:535
DocFormula(DocParser *parser, DocNodeVariant *parent, int id)
Definition docnode.cpp:529
Token parse()
Definition docnode.cpp:1584
Node representing a horizontal ruler.
Definition docnode.h:216
Node representing an HTML blockquote.
Definition docnode.h:1296
HtmlAttribList m_attribs
Definition docnode.h:1243
bool m_hasCaptionId
Definition docnode.h:1244
DocHtmlCaption(DocParser *parser, DocNodeVariant *parent, const HtmlAttribList &attribs)
Definition docnode.cpp:1791
DString m_anchor
Definition docnode.h:1246
const HtmlAttribList & attribs() const
Definition docnode.h:1236
DString m_file
Definition docnode.h:1245
Node representing a HTML table cell.
Definition docnode.h:1198
Valignment valignment() const
Definition docnode.cpp:2074
void setColumnIndex(uint32_t idx)
Definition docnode.h:1222
bool isFirst() const
Definition docnode.h:1206
Token parseXml()
Definition docnode.cpp:1978
void setRowIndex(uint32_t idx)
Definition docnode.h:1221
uint32_t rowSpan() const
Definition docnode.cpp:2012
void markLast(bool v=true)
Definition docnode.h:1209
void markFirst(bool v=true)
Definition docnode.h:1208
Alignment alignment() const
Definition docnode.cpp:2036
bool isHeading() const
Definition docnode.h:1205
const HtmlAttribList & attribs() const
Definition docnode.h:1210
Token parse()
Definition docnode.cpp:1944
uint32_t colSpan() const
Definition docnode.cpp:2024
Node representing a HTML description data.
Definition docnode.h:1186
HtmlAttribList m_attribs
Definition docnode.h:1193
Node representing a Html description list.
Definition docnode.h:910
Node representing a Html description item.
Definition docnode.h:897
Node Html details.
Definition docnode.h:866
const HtmlAttribList & attribs() const
Definition docnode.h:870
void parseSummary(DocNodeVariant *, HtmlAttribList &attribs)
Definition docnode.cpp:1574
const DocNodeVariant * summary() const
Definition docnode.h:873
std::unique_ptr< DocNodeVariant > m_summary
Definition docnode.h:877
Node Html heading.
Definition docnode.h:882
Token parse()
Definition docnode.cpp:1394
Node representing a Html list.
Definition docnode.h:1009
Type m_type
Definition docnode.h:1020
Token parseXml()
Definition docnode.cpp:2956
Token parse()
Definition docnode.cpp:2881
Node representing a HTML list item.
Definition docnode.h:1170
Node representing a HTML table row.
Definition docnode.h:1251
Token parseXml(bool header)
Definition docnode.cpp:2278
void setVisibleCells(uint32_t n)
Definition docnode.h:1261
bool isHeading() const
Definition docnode.cpp:2096
void setRowIndex(uint32_t idx)
Definition docnode.h:1266
Token parse()
Definition docnode.cpp:2171
Node Html summary.
Definition docnode.h:853
Node representing a HTML table.
Definition docnode.h:1274
Token parseXml()
Definition docnode.cpp:2457
size_t numberHeaderRows() const
Definition docnode.cpp:2349
std::unique_ptr< DocNodeVariant > m_caption
Definition docnode.h:1289
Token parse()
Definition docnode.cpp:2364
void computeTableGrid()
determines the location of all cells in a grid, resolving row and column spans.
Definition docnode.cpp:2514
size_t m_numCols
Definition docnode.h:1291
const DocNodeVariant * caption() const
Definition docnode.cpp:2344
bool hasCaption() const
Definition docnode.cpp:2339
const HtmlAttribList & attribs() const
Definition docnode.h:656
DocImage(DocParser *parser, DocNodeVariant *parent, const HtmlAttribList &attribs, const DString &name, Type t, const DString &url=DString(), bool inlineImage=true)
Definition docnode.cpp:1371
DString relPath() const
Definition docnode.h:652
DString name() const
Definition docnode.h:648
std::unique_ptr< Private > p
Definition docnode.h:675
DString url() const
Definition docnode.h:653
void parse()
Definition docnode.cpp:1386
bool isSVG() const
Definition docnode.cpp:1377
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
DString m_text
Definition docnode.h:516
DString m_includeFileName
Definition docnode.h:524
void markLast(bool v=true)
Definition docnode.h:505
DString context() const
Definition docnode.h:501
Type type() const
Definition docnode.h:485
DString m_pattern
Definition docnode.h:517
bool m_showLineNo
Definition docnode.h:515
Node representing an included text block from file.
Definition docnode.h:435
DString m_text
Definition docnode.h:465
void parse()
Definition docnode.cpp:283
DString m_file
Definition docnode.h:463
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
DString context() const
Definition docnode.h:453
bool m_stripCodeComments
Definition docnode.h:467
DString m_blockId
Definition docnode.h:472
Node representing an entry in the index.
Definition docnode.h:552
Token parse()
Definition docnode.cpp:1694
DString m_entry
Definition docnode.h:562
Node representing an internal section of documentation.
Definition docnode.h:978
Token parse(int)
Definition docnode.cpp:1634
DString m_anchor
Definition docnode.h:827
DString m_file
Definition docnode.h:825
DocInternalRef(DocParser *parser, DocNodeVariant *parent, const DString &target)
Definition docnode.cpp:687
DString m_relPath
Definition docnode.h:826
DString relPath() const
Definition docnode.h:821
Node representing a line break.
Definition docnode.h:202
DString word() const
Definition docnode.h:170
DString m_file
Definition docnode.h:180
DString m_relPath
Definition docnode.h:181
DString m_tooltip
Definition docnode.h:183
DString m_ref
Definition docnode.h:179
DString m_anchor
Definition docnode.h:182
DString anchor() const
Definition docnode.h:174
DString ref() const
Definition docnode.h:173
DString tooltip() const
Definition docnode.h:175
DString m_word
Definition docnode.h:178
DocLinkedWord(DocParser *parser, DocNodeVariant *parent, const DString &word, const DString &ref, const DString &file, const DString &anchor, const DString &tooltip)
Definition docnode.cpp:207
DString file() const
Definition docnode.h:171
DString relPath() const
Definition docnode.h:172
DocMermaidFile(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.cpp:1301
DocMscFile(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.cpp:1178
bool parse()
Definition docnode.cpp:1185
DocNode(DocParser *parser, DocNodeVariant *parent)
Definition docnode.h:84
void setInsidePreformatted(bool p)
Definition docnode.h:108
DocNodeVariant * thisVariant()
Definition docnode.h:92
DocParser * parser()
Definition docnode.h:97
DocNodeVariant * parent()
Definition docnode.h:89
@ Unknown
Definition docnode.h:109
@ Table
Definition docnode.h:109
@ Requirement
Definition docnode.h:109
@ Section
Definition docnode.h:109
@ Anchor
Definition docnode.h:109
void move_append(DocNodeList &l)
moves the element of list l at the end of this list.
Definition docnode.cpp:864
void append(Args &&... args)
Append a new DocNodeVariant to the list by constructing it with type T and parameters Args.
Definition docnode.h:1404
T * get_last()
Returns a pointer to the last element in the list if that element exists and holds a T,...
Definition docnode.h:1415
Node representing an block of paragraphs.
Definition docnode.h:988
Token parse()
Definition docnode.cpp:3050
Node representing a paragraph in the documentation tree.
Definition docnode.h:1089
void handleFile(const DString &cmdName)
Definition docnode.cpp:3915
void handleInheritDoc()
Definition docnode.cpp:4185
void handleShowDate(char cmdChar, const DString &cmdName)
Definition docnode.cpp:3785
DocPara(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:3575
void handleIncludeOperator(const DString &cmdName, DocIncOperator::Type t)
Definition docnode.cpp:3855
void handleLink(const DString &cmdName, bool isJavaLink)
Definition docnode.cpp:3954
Token handleCommand(char cmdChar, const DString &cmdName)
Definition docnode.cpp:4226
void handleInclude(const DString &cmdName, DocInclude::Type t)
Definition docnode.cpp:3995
Token handleHtmlEndTag(const DString &tagName)
Definition docnode.cpp:5506
Token handleHtmlStartTag(const DString &tagName, const HtmlAttribList &tagHtmlAttribs)
Definition docnode.cpp:5036
void markFirst(bool v=true)
Definition docnode.h:1094
Token handleSimpleSection(DocSimpleSect::Type t, bool xmlContext=false)
Definition docnode.cpp:3581
void handleEmoji(char cmdChar, const DString &cmdName)
Definition docnode.cpp:3634
bool isFirst() const
Definition docnode.h:1096
Token handleParamSection(const DString &cmdName, DocParamSect::Type t, bool xmlContext, int direction)
Definition docnode.cpp:3611
void setAttribs(const HtmlAttribList &attribs)
Definition docnode.h:1120
void handleSection(char cmdChar, const DString &cmdName)
Definition docnode.cpp:4100
bool empty() const
Definition docnode.h:1093
bool m_isFirst
Definition docnode.h:1123
Token parse()
Definition docnode.cpp:5779
void markLast(bool v=true)
Definition docnode.h:1095
void handleVhdlFlow()
Definition docnode.cpp:3947
void handleDoxyConfig(char cmdChar, const DString &cmdName)
Definition docnode.cpp:3665
bool injectToken(Token tok, const DString &tokText)
Definition docnode.cpp:4142
Token handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs, int level)
Definition docnode.cpp:4131
bool m_isLast
Definition docnode.h:1124
Token handleXRefItem()
Definition docnode.cpp:3763
Token handleStartCode()
Definition docnode.cpp:4149
DocNodeList m_paramTypes
Definition docnode.h:1149
DocNodeList m_paragraphs
Definition docnode.h:1147
void markFirst(bool b=true)
Definition docnode.h:1139
void markLast(bool b=true)
Definition docnode.h:1140
Token parse(const DString &cmdName)
Definition docnode.cpp:3383
DocParamSect::Type m_type
Definition docnode.h:1150
Token parseXml(const DString &paramName)
Definition docnode.cpp:3464
DocNodeList m_params
Definition docnode.h:1148
Node representing a parameter section.
Definition docnode.h:1062
Token parse(const DString &cmdName, bool xmlContext, Direction d)
Definition docnode.cpp:3532
friend class DocParamList
Definition docnode.h:1063
bool m_hasInOutSpecifier
Definition docnode.h:1083
Type type() const
Definition docnode.h:1077
void handleIFile(char cmdChar, const DString &cmdName)
void handlePendingStyleCommands(DocNodeVariant *parent, DocNodeList &children, size_t numberOfElementsToClose=0)
DocTokenizer tokenizer
void handleInternalRef(DocNodeVariant *parent, DocNodeList &children)
void readTextFileByName(const DString &file, DString &text)
void checkRetvalName()
void errorHandleDefaultToken(DocNodeVariant *parent, Token tok, DocNodeList &children, const DString &txt)
void handleRef(DocNodeVariant *parent, DocNodeList &children, char cmdChar, const DString &cmdName)
Token handleAHref(DocNodeVariant *parent, DocNodeList &children, const HtmlAttribList &tagHtmlAttribs)
void handleStyleEnter(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const DString &tagName, const HtmlAttribList *attribs)
void handleParameterType(DocNodeVariant *parent, DocNodeList &children, const DString &paramTypes)
bool defaultHandleToken(DocNodeVariant *parent, Token &tok, DocNodeList &children, bool handleWord=true)
void handleInitialStyleCommands(DocNodeVariant *parent, DocNodeList &children)
Token handleStyleArgument(DocNodeVariant *parent, DocNodeList &children, const DString &cmdName)
void handleStyleLeave(DocNodeVariant *parent, DocNodeList &children, DocStyleChange::Style s, const DString &tagName)
void defaultHandleTitleAndSize(const CommandType cmd, DocNodeVariant *parent, DocNodeList &children, DString &width, DString &height)
void handleImage(DocNodeVariant *parent, DocNodeList &children)
void handleLinkedWord(DocNodeVariant *parent, DocNodeList &children, bool ignoreAutoLinkFlag=false, bool typeLinkOnly=false)
void handleCite(DocNodeVariant *parent, DocNodeList &children)
void handlePrefix(DocNodeVariant *parent, DocNodeList &children)
void handleILine(char cmdChar, const DString &cmdName)
void checkArgumentName()
DocParserContext context
Token internalValidatingParseDoc(DocNodeVariant *parent, DocNodeList &children, const DString &doc)
void handleAnchor(DocNodeVariant *parent, DocNodeList &children)
void handleImg(DocNodeVariant *parent, DocNodeList &children, const HtmlAttribList &tagHtmlAttribs)
void handleUnclosedStyleCommands()
DocPlantUmlFile(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.cpp:1257
Node representing a reference to some item.
Definition docnode.h:787
SectionType m_sectionType
Definition docnode.h:805
DString m_ref
Definition docnode.h:809
DString anchor() const
Definition docnode.h:794
DString m_relPath
Definition docnode.h:808
DString m_anchor
Definition docnode.h:810
RefType m_refType
Definition docnode.h:804
void parse(char cmdChar, const DString &cmdName)
Definition docnode.cpp:905
DString m_text
Definition docnode.h:811
DString m_file
Definition docnode.h:807
bool m_isSubPage
Definition docnode.h:806
DocRef(DocParser *parser, DocNodeVariant *parent, const DString &target, const DString &context)
Definition docnode.cpp:722
void parse()
Definition docnode.cpp:6368
Node representing a reference to a section.
Definition docnode.h:944
DString m_anchor
Definition docnode.h:963
bool m_isSubPage
Definition docnode.h:959
DString relPath() const
Definition docnode.h:950
DocSecRefItem(DocParser *parser, DocNodeVariant *parent, const DString &target)
Definition docnode.cpp:548
DString m_ref
Definition docnode.h:962
DString m_relPath
Definition docnode.h:961
RefType m_refType
Definition docnode.h:958
DString target() const
Definition docnode.h:947
DString m_target
Definition docnode.h:957
DString m_file
Definition docnode.h:960
Node representing a list of section references.
Definition docnode.h:968
Node representing a normal section.
Definition docnode.h:923
std::unique_ptr< DocNodeVariant > m_title
Definition docnode.h:937
DocSection(DocParser *parser, DocNodeVariant *parent, int level, const DString &id)
Definition docnode.h:925
DString m_file
Definition docnode.h:939
Token parse()
Definition docnode.cpp:6084
DString m_anchor
Definition docnode.h:938
DString m_id
Definition docnode.h:936
const DocNodeVariant * title() const
Definition docnode.h:928
int m_level
Definition docnode.h:935
Node representing a simple list.
Definition docnode.h:999
Token parse()
Definition docnode.cpp:3094
Node representing a simple list item.
Definition docnode.h:1158
std::unique_ptr< DocNodeVariant > m_paragraph
Definition docnode.h:1165
DocSimpleListItem(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:3075
Node representing a simple section.
Definition docnode.h:1026
DString typeString() const
Definition docnode.cpp:3354
Type type() const
Definition docnode.h:1035
Token parse(bool userTitle, bool needsSeparator)
Definition docnode.cpp:3244
Token parseRcs()
Definition docnode.cpp:3281
DocSimpleSect(DocParser *parser, DocNodeVariant *parent, Type t)
Definition docnode.cpp:3234
const DocNodeVariant * title() const
Definition docnode.h:1042
Token parseXml()
Definition docnode.cpp:3298
void appendLinkWord(const DString &word)
Definition docnode.cpp:3334
bool hasTitle() const
Definition docnode.cpp:3239
std::unique_ptr< DocNodeVariant > m_title
Definition docnode.h:1046
Node representing a separator between two simple sections of the same type.
Definition docnode.h:1053
Node representing a style change.
Definition docnode.h:268
const char * styleString() const
Definition docnode.cpp:140
Style m_style
Definition docnode.h:318
Node representing a special symbol.
Definition docnode.h:328
static HtmlEntityMapper::SymType decodeSymbol(const DString &symName)
Definition docnode.cpp:168
void parse()
Definition docnode.cpp:6254
Node representing a simple section title.
Definition docnode.h:608
void parseFromString(DocNodeVariant *, const DString &title)
Definition docnode.cpp:3221
void parse()
Definition docnode.cpp:3203
void setStateILiteralOpt()
void setStateILiteral()
void unputString(const DString &tag)
void setStateSnippet()
void setStateEmoji()
void setStateMermaidOpt()
void setStateCode()
void setStatePattern()
void startAutoList()
void setStateSkipTitle()
void setStateParam()
void setStateBlock()
void setStatePlantUMLOpt()
void setStateRtfOnly()
void setStateVerbatim()
void setStateLink()
void setStateTitle()
void setStateFile()
void setStateLatexOnly()
void setStateManOnly()
void setStateShowDate()
void setInsidePre(bool b)
void setStateXRefItem()
int getLineNr() const
void setStateText()
void setStateXmlCode()
void setStateDbOnly()
void setStateHtmlOnly()
void pushBackHtmlTag(const DString &tag)
void setStateILine()
void setStateICode()
void setStateOptions()
void setStateDoxyConfig()
void setStateQuotedString()
void setStatePara()
void setStatePlantUML()
void setStateIVerbatim()
void setStateXmlOnly()
void setStateMermaid()
void setStateSetScope()
Node representing a URL (or email address).
Definition docnode.h:188
Node representing a verbatim, unparsed text fragment.
Definition docnode.h:376
DString text() const
Definition docnode.h:383
std::unique_ptr< Private > p
Definition docnode.h:429
bool isBlock() const
Definition docnode.h:389
bool isExample() const
Definition docnode.h:385
DString exampleFile() const
Definition docnode.h:386
DString context() const
Definition docnode.h:384
void setEngine(const DString &e)
Definition docnode.h:402
DString relPath() const
Definition docnode.h:387
@ JavaDocLiteral
Definition docnode.h:378
DocVerbatim(DocParser *parser, DocNodeVariant *parent, const DString &context, const DString &text, Type t, bool isExample, const DString &exampleFile, bool isBlock=false, const DString &lang=DString())
Definition docnode.cpp:273
Node representing a VHDL flow chart.
Definition docnode.h:758
DocVhdlFlow(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:1341
void parse()
Definition docnode.cpp:1345
Node representing some amount of white space.
Definition docnode.h:354
Node representing a word.
Definition docnode.h:153
DString word() const
Definition docnode.h:156
DString m_word
Definition docnode.h:159
DocWord(DocParser *parser, DocNodeVariant *parent, const DString &word)
Definition docnode.cpp:195
Node representing an item of a cross-referenced list.
Definition docnode.h:621
DString m_title
Definition docnode.h:636
DString m_relPath
Definition docnode.h:637
DString m_anchor
Definition docnode.h:635
DString key() const
Definition docnode.h:628
DocXRefItem(DocParser *parser, DocNodeVariant *parent, int id, const DString &key)
Definition docnode.cpp:488
DString m_key
Definition docnode.h:633
DString relPath() const
Definition docnode.h:627
bool parse()
Definition docnode.cpp:493
DString m_file
Definition docnode.h:634
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:102
static FileNameLinkedMap * inputNameLinkedMap
Definition doxygen.h:97
static FileNameLinkedMap * dotFileNameLinkedMap
Definition doxygen.h:99
static NamespaceDefMutable * globalScope
Definition doxygen.h:114
static FileNameLinkedMap * mscFileNameLinkedMap
Definition doxygen.h:100
static FileNameLinkedMap * mermaidFileNameLinkedMap
Definition doxygen.h:103
static FileNameLinkedMap * diaFileNameLinkedMap
Definition doxygen.h:101
static PageLinkedMap * pageLinkedMap
Definition doxygen.h:92
static DString htmlFileExtension
Definition doxygen.h:115
static DirLinkedMap * dirLinkedMap
Definition doxygen.h:120
static SearchIndexIntf searchIndex
Definition doxygen.h:117
static EmojiEntityMapper & instance()
Returns the one and only instance of the Emoji entity mapper.
Definition emoji.cpp:1981
int symbol2index(const std::string &symName) const
Returns a code for the requested Emoji entity name.
Definition emoji.cpp:1993
A model of a file symbol.
Definition filedef.h:97
virtual DString absFilePath() const =0
DString showFileDefMatches(const DString &n) const
Returns a list of file definitions in fnMap that match the file name n.
Definition filename.cpp:131
FileDef * findFileDef(const DString &n, bool &ambig) const
Returns the file definition in fnMap that matches the file name n.
Definition filename.cpp:38
Class representing a LaTeX formula as found in the documentation.
Definition formula.h:29
DString 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:31
SymType name2sym(const DString &symName) const
Give code of the requested HTML entity name.
static HtmlEntityMapper & instance()
Returns the one and only instance of the HTML entity mapper.
const T * find(const std::string &key) const
Definition linkedmap.h:47
A model of a class/file/namespace member symbol.
Definition memberdef.h:45
virtual DString objCMethodName(bool localLink, bool showStatic) const =0
virtual const ClassDef * getClassDef() const =0
virtual const MemberDef * reimplements() const =0
A model of a page symbol.
Definition pagedef.h:27
virtual bool hasParentPage() const =0
static bool isEnabled()
Returns true if doxygen has been configured to run PlantUML, i.e.
Definition plantuml.cpp:246
This struct represents an item in the list of references.
Definition reflist.h:33
DString anchor() const
Definition reflist.h:47
DString text() const
Definition reflist.h:46
List of cross-referenced items.
Definition reflist.h:81
DString sectionTitle() const
Definition reflist.h:105
DString fileName() const
Definition reflist.h:103
RefItem * find(int itemId)
Definition reflist.cpp:45
bool isEnabled() const
Definition reflist.cpp:51
static RefListManager & instance()
Definition reflist.h:122
class that provide information about a section.
Definition section.h:58
DString fileName() const
Definition section.h:74
SectionType type() const
Definition section.h:71
DString title() const
Definition section.h:70
DString ref() const
Definition section.h:72
DString label() const
Definition section.h:69
static SectionManager & instance()
returns a reference to the singleton
Definition section.h:179
static constexpr int Anchor
Definition section.h:40
static constexpr int Table
Definition section.h:41
constexpr int level() const
Definition section.h:46
static constexpr int Requirement
Definition section.h:42
static constexpr int Page
Definition section.h:31
bool is(TokenRetval rv) const
TOKEN_SPECIFICATIONS RETVAL_SPECIFICATIONS const char * to_string() const
TokenRetval value() const
bool is_any_of(ARGS... args) const
char command_to_char() const
DString simpleSectName
DString verb
DString sectionId
bool isEnumList
DString chars
HtmlAttribList attribs
DString simpleSectText
bool isCheckedList
DString attribsStr
bool isEMailAddr
DString text
DString name
virtual DString trDetails()=0
virtual DString trWriteList(int numEntries)=0
static void createFlowChart(const MemberDef *)
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:239
CommandType
Definition cmdmapper.h:30
@ CMD_ENDSECREFLIST
Definition cmdmapper.h:54
@ CMD_ENDLATEXONLY
Definition cmdmapper.h:52
@ CMD_ENDVERBATIM
Definition cmdmapper.h:55
@ CMD_DONTINCLUDE
Definition cmdmapper.h:47
@ CMD_SUBSUBSECTION
Definition cmdmapper.h:90
@ CMD_SUBSUBPARAGRAPH
Definition cmdmapper.h:162
@ CMD_INTERNALREF
Definition cmdmapper.h:66
@ CMD_ENDHTMLONLY
Definition cmdmapper.h:51
@ CMD_VERBINCLUDE
Definition cmdmapper.h:99
@ CMD_DOCBOOKINCLUDE
Definition cmdmapper.h:146
@ CMD_HTMLINCLUDE
Definition cmdmapper.h:61
@ CMD_SNIPWITHLINES
Definition cmdmapper.h:142
HtmlTagType
Definition cmdmapper.h:174
#define Config_getList(name)
Definition config.h:38
#define Config_getBool(name)
Definition config.h:33
std::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29
std::multiset< std::string > StringMultiSet
Definition containers.h:32
std::vector< std::string > StringVector
Definition containers.h:33
DString formatDateTime(const DString &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:176
DString dateTimeFromString(const DString &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:135
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:181
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:54
static const char * g_sectionLevelToName[]
Definition docnode.cpp:63
static const FileDef * currentFileDef(const DocParser *parser)
Definition docnode.cpp:89
#define AUTO_TRACE(...)
Definition docnode.cpp:53
static const StringUnorderedSet g_plantumlEngine
Definition docnode.cpp:76
#define INTERNAL_ASSERT(x)
Definition docnode.cpp:58
static void flattenParagraphs(DocNodeVariant *root, DocNodeList &children)
Definition docnode.cpp:873
static Token skipSpacesForTable(DocParser *parser)
Definition docnode.cpp:2111
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:55
static bool findAttribute(const HtmlAttribList &tagHtmlAttribs, const char *attrName, DString *result)
Definition docnode.cpp:5020
std::vector< ActiveRowSpan > RowSpanList
List of ActiveRowSpan classes.
Definition docnode.cpp:2508
static void setParent(DocNodeVariant *n, DocNodeVariant *newParent)
Definition docnode.cpp:133
static void unescapeCRef(DString &s)
Definition docnode.cpp:97
static bool checkIfHtmlEndTagEndsAutoList(DocParser *parser, const DocNodeVariant *n)
Definition docnode.cpp:5727
static DString stripKnownExtensions(const DString &text)
Definition docnode.cpp:119
constexpr bool holds_one_of_alternatives(const DocNodeVariant &v)
returns true iff v holds one of types passed as template parameters
Definition docnode.h:1371
std::variant< DocWord, DocLinkedWord, DocURL, DocLineBreak, DocHorRuler, DocAnchor, DocCite, DocStyleChange, DocSymbol, DocEmoji, DocWhiteSpace, DocSeparator, DocVerbatim, DocInclude, DocIncOperator, DocFormula, DocIndexEntry, DocAutoList, DocAutoListItem, DocTitle, DocXRefItem, DocImage, DocDotFile, DocMscFile, DocDiaFile, DocVhdlFlow, DocLink, DocRef, DocInternalRef, DocHRef, DocHtmlHeader, DocHtmlDescTitle, DocHtmlDescList, DocSection, DocSecRefItem, DocSecRefList, DocInternal, DocParBlock, DocSimpleList, DocHtmlList, DocSimpleSect, DocSimpleSectSep, DocParamSect, DocPara, DocParamList, DocSimpleListItem, DocHtmlListItem, DocHtmlDescData, DocHtmlCell, DocHtmlCaption, DocHtmlRow, DocHtmlTable, DocHtmlBlockQuote, DocText, DocRoot, DocHtmlDetails, DocHtmlSummary, DocPlantUmlFile, DocMermaidFile > DocNodeVariant
Definition docnode.h:66
DocNodeList * call_method_children(DocNodeVariant *v)
Definition docnode.h:1390
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1335
std::unique_ptr< DocNodeVariant > createDocNode(Args &&...args)
Definition docnode.h:1500
Private header shared between docparser.cpp and docnode.cpp.
bool insideUL(const DocNodeVariant *n)
bool insideTable(const DocNodeVariant *n)
IterableStack< const DocNodeVariant * > DocStyleChangeStack
Definition docparser_p.h:54
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)
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:485
const char * qPrint(const char *s)
Definition dstring.h:783
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:2027
GroupDef * toGroupDef(Definition *d)
Translator * theTranslator
Definition language.cpp:76
DString markdownFileNameToId(const DString &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 ASSERT(x)
Definition message.h:142
#define warn_doc_error(file, line, fmt,...)
Definition message.h:112
const Mapper< HtmlTagType > * htmlTagMapper
const Mapper< CommandType > * cmdMapper
DString trunc(const DString &s, size_t numChars=15)
Definition trace.h:56
Definition message.h:146
Definition dstring.h:913
Some helper functions for std::string.
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
Definition stringutil.h:117
uint32_t rowsLeft
Definition docnode.cpp:2503
uint32_t column
Definition docnode.cpp:2504
ActiveRowSpan(uint32_t rows, uint32_t col)
Definition docnode.cpp:2502
Citation-related data.
Definition cite.h:70
virtual DString text() const =0
virtual DString shortAuthor() const =0
virtual DString label() const =0
virtual DString year() const =0
Parser's context to store all global variables.
Definition docparser_p.h:59
StringMultiSet retvalsFound
Definition docparser_p.h:75
bool includeFileShowLineNo
Definition docparser_p.h:91
DocStyleChangeStack styleStack
Definition docparser_p.h:67
size_t includeFileLength
Definition docparser_p.h:89
DString includeFileText
Definition docparser_p.h:87
DocNodeStack nodeStack
Definition docparser_p.h:66
StringMultiSet paramsFound
Definition docparser_p.h:76
DefinitionStack copyStack
Definition docparser_p.h:69
const Definition * scope
Definition docparser_p.h:60
TokenInfo * token
Definition docparser_p.h:94
DString includeFileName
Definition docparser_p.h:86
SrcLangExt lang
Definition docparser_p.h:84
size_t includeFileOffset
Definition docparser_p.h:88
const MemberDef * memberDef
Definition docparser_p.h:79
DString exampleName
Definition docparser_p.h:81
DString stripLeadingAndTrailingEmptyLines(const DString &s, int &docLine)
Special version of DString::stripWhiteSpace() that only strips completely blank lines.
Definition util.cpp:3988
bool resolveLink(const DString &scName, const DString &lr, bool, const Definition **resContext, DString &resAnchor, SrcLangExt lang, const DString &prefix, const FileDef *currentFile)
Definition util.cpp:2308
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2864
DString stripScope(const DString &name)
Definition util.cpp:3110
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4169
DString linkToText(SrcLangExt lang, const DString &link, bool ignoreDots)
Definition util.cpp:2265
DString stripIndentation(const DString &s, bool skipFirstLine)
Definition util.cpp:4684
A bunch of utility functions.