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// replaces { with < and } with > and also
88// replaces &gt; with < and &gt; with > within string s
89static void unescapeCRef(DString &s)
90{
91 DString result;
92 const char *p = s.data();
93 if (p)
94 {
95 char c = 0;
96 while ((c=*p++))
97 {
98 if (c=='{') c='<'; else if (c=='}') c='>';
99 result+=c;
100 }
101 }
102
103 result=substitute(result,"&lt;","<");
104 result=substitute(result,"&gt;",">");
105 s = result;
106}
107
108//---------------------------------------------------------------------------
109
110/*! Strips known html and tex extensions from \a text. */
112{
113 DString result=text;
114 if (result.endsWith(".tex"))
115 {
116 result=result.left(result.length()-4);
117 }
119 {
120 result=result.left(result.length()-Doxygen::htmlFileExtension.length());
121 }
122 return result;
123}
124
125static void setParent(DocNodeVariant *n,DocNodeVariant *newParent)
126{
127 std::visit([&](auto &&x)->decltype(auto) { return x.setParent(newParent); }, *n);
128}
129
130//----------- DocStyleChange
131
133{
134 switch (m_style)
135 {
136 case DocStyleChange::Bold: return "b";
137 case DocStyleChange::Italic: return "em";
138 case DocStyleChange::Code: return "code";
139 case DocStyleChange::Center: return "center";
140 case DocStyleChange::Small: return "small";
141 case DocStyleChange::Cite: return "cite";
142 case DocStyleChange::Subscript: return "subscript";
143 case DocStyleChange::Superscript: return "superscript";
144 case DocStyleChange::Preformatted: return "pre";
145 case DocStyleChange::Div: return "div";
146 case DocStyleChange::Span: return "span";
147 case DocStyleChange::Strike: return "strike";
148 case DocStyleChange::S: return "s";
149 case DocStyleChange::Del: return "del";
150 case DocStyleChange::Underline: return "u";
151 case DocStyleChange::Ins: return "ins";
152 case DocStyleChange::Kbd: return "kbd";
153 case DocStyleChange::Typewriter: return "tt";
154 }
155 return "<invalid>";
156}
157
158//----------- DocSymbol
159
164
165//----------- DocEmoji
166
168 DocNode(parser,parent), m_symName(symName), m_index(-1)
169{
170 DString locSymName = symName;
171 size_t len=locSymName.length();
172 if (len>0)
173 {
174 if (locSymName.at(len-1)!=':') locSymName.append(":");
175 if (locSymName.at(0)!=':') locSymName.prepend(":");
176 }
177 m_symName = locSymName;
179 if (m_index==-1)
180 {
181 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Found unsupported emoji symbol '{}'",m_symName);
182 }
183}
184
185//---------------------------------------------------------------------------
186
189{
190 //printf("new word %s url=%s\n",qPrint(word),qPrint(parser->context.searchUrl));
192 {
193 Doxygen::searchIndex.addWord(word,false);
194 }
195}
196
197//---------------------------------------------------------------------------
198
200 const DString &ref,const DString &file,
201 const DString &anchor,const DString &tooltip) :
205{
206 //printf("DocLinkedWord: new word %s url=%s tooltip='%s'\n",
207 // qPrint(word),qPrint(parser->context.searchUrl),qPrint(tooltip));
209 {
210 Doxygen::searchIndex.addWord(word,false);
211 }
212}
213
214//---------------------------------------------------------------------------
215
217{
218 if (id.empty())
219 {
221 return;
222 }
223
225 DString anchorPrefix = ct.anchorPrefix();
226 if (id.left(anchorPrefix.length()) == anchorPrefix)
227 {
228 const CiteInfo *cite = ct.find(id.mid(anchorPrefix.length()));
229 if (cite)
230 {
231 m_file = convertNameToFile(ct.fileName(),false,true);
232 m_anchor = id;
233 }
234 else
235 {
236 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid cite anchor id '{}'",id);
237 m_anchor = "invalid";
238 m_file = "invalid";
239 }
240 }
241 else if (newAnchor) // found <a name="label">
242 {
243 m_anchor = id;
244 }
245 else // found \anchor label
246 {
247 const SectionInfo *sec = SectionManager::instance().find(id);
248 if (sec)
249 {
250 //printf("Found anchor %s\n",qPrint(id));
251 m_file = sec->fileName();
252 m_anchor = sec->label();
253 }
254 else
255 {
256 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid anchor id '{}'",id);
257 m_anchor = "invalid";
258 m_file = "invalid";
259 }
260 }
261}
262
263//---------------------------------------------------------------------------
264
271
272
273//---------------------------------------------------------------------------
274
276{
277 AUTO_TRACE("file={} text={}",m_file,Trace::trunc(m_text));
278 switch(m_type)
279 {
280 case DontIncWithLines:
281 // fall through
282 case IncWithLines:
283 // fall through
284 case Include:
285 // fall through
286 case DontInclude:
295 //printf("parser->context.includeFile=<<%s>>\n",qPrint(parser->context.includeFileText));
296 break;
297 case VerbInclude:
298 // fall through
299 case HtmlInclude:
300 case LatexInclude:
306 break;
307 case Snippet:
308 case SnippetWithLines:
310 // check here for the existence of the blockId inside the file, so we
311 // only generate the warning once.
312 int count = 0;
313 if (!m_blockId.empty() && (count=m_text.contains(m_blockId.data()))!=2)
314 {
315 warn_doc_error(parser()->context.fileName,
316 parser()->tokenizer.getLineNr(),
317 "block marked with {} for \\snippet should appear twice in file {}, found it {:d} times",
318 m_blockId,m_file,count);
319 }
320 break;
321 }
322}
323
324//---------------------------------------------------------------------------
325
327{
328 if (parser()->context.includeFileName.empty())
329 {
330 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
331 "No previous '\\include' or '\\dontinclude' command for '\\{}' present",
332 typeAsString());
333 }
334 bool found = false;
335
337 const char *p = parser()->context.includeFileText.data();
338 size_t l = parser()->context.includeFileLength;
339 size_t o = parser()->context.includeFileOffset;
340 int il = parser()->context.includeFileLine;
341 AUTO_TRACE("text={} off={} len={}",Trace::trunc(p),o,l);
342 size_t so = o, bo = 0;
343 bool nonEmpty = false;
344 switch(type())
345 {
346 case Line:
347 while (o<l)
348 {
349 char c = p[o];
350 if (c=='\n')
351 {
353 if (nonEmpty) break; // we have a pattern to match
354 so=o+1; // no pattern, skip empty line
355 }
356 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
357 {
358 nonEmpty=true;
359 }
360 o++;
361 }
362 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
363 {
364 m_line = il;
366 found = true;
367 AUTO_TRACE_ADD("\\line {}",Trace::trunc(m_text));
368 }
369 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
372 break;
373 case SkipLine:
374 while (o<l)
375 {
376 so=o;
377 while (o<l)
378 {
379 char c = p[o];
380 if (c=='\n')
381 {
383 if (nonEmpty) break; // we have a pattern to match
384 so=o+1; // no pattern, skip empty line
385 }
386 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
387 {
388 nonEmpty=true;
389 }
390 o++;
391 }
392 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
393 {
394 m_line = il;
396 found = true;
397 AUTO_TRACE_ADD("\\skipline {}",Trace::trunc(m_text));
398 break;
399 }
400 o++; // skip new line
401 }
402 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
405 break;
406 case Skip:
407 while (o<l)
408 {
409 so=o;
410 while (o<l)
411 {
412 char c = p[o];
413 if (c=='\n')
414 {
416 if (nonEmpty) break; // we have a pattern to match
417 so=o+1; // no pattern, skip empty line
418 }
419 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
420 {
421 nonEmpty=true;
422 }
423 o++;
424 }
425 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
426 {
427 found = true;
428 break;
429 }
430 o++; // skip new line
431 }
432 parser()->context.includeFileOffset = so; // set pointer to start of new line
435 break;
436 case Until:
437 bo=o;
438 while (o<l)
439 {
440 so=o;
441 while (o<l)
442 {
443 char c = p[o];
444 if (c=='\n')
445 {
447 if (nonEmpty) break; // we have a pattern to match
448 so=o+1; // no pattern, skip empty line
449 }
450 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
451 {
452 nonEmpty=true;
453 }
454 o++;
455 }
456 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
457 {
458 m_line = il;
460 found = true;
461 AUTO_TRACE_ADD("\\until {}",Trace::trunc(m_text));
462 break;
463 }
464 o++; // skip new line
465 }
466 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
469 break;
470 }
471 if (!found)
472 {
473 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
474 "referenced pattern '{}' for command '\\{}' not found",m_pattern,typeAsString());
475 }
476}
477
478//---------------------------------------------------------------------------
479
484
486{
488 if (refList && refList->isEnabled())
489 {
490 RefItem *item = refList->find(m_id);
491 ASSERT(item!=nullptr);
492 if (item)
493 {
494 if (parser()->context.memberDef && parser()->context.memberDef->name().at(0)=='@')
495 {
496 m_file = "@"; // can't cross reference anonymous enum
497 m_anchor = "@";
498 }
499 else
500 {
501 m_file = refList->fileName();
502 m_anchor = item->anchor();
503 }
504 m_title = refList->sectionTitle();
505 //printf("DocXRefItem: file=%s anchor=%s title=%s\n",
506 // qPrint(m_file),qPrint(m_anchor),qPrint(m_title));
507
508 if (!item->text().empty())
509 {
510 DocParser::AutoSaveContext saveContext(*parser());
512 }
513 }
514 return true;
515 }
516 return false;
517}
518
519//---------------------------------------------------------------------------
520
522 m_relPath(parser->context.relPath)
523{
524 const Formula *formula = FormulaManager::instance().findFormula(id);
525 if (formula && !formula->text().empty())
526 {
527 m_id = id;
528 m_name.sprintf("form_%d",m_id);
529 m_text = formula->text();
530 }
531 else // wrong \_form#<n> command
532 {
533 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Wrong formula id {:d}",id);
534 m_id = -1;
535 }
536}
537
538//---------------------------------------------------------------------------
539
544
546{
547 AUTO_TRACE();
548 auto ns = AutoNodeStack(parser(),thisVariant());
549
551 Token tok = parser()->tokenizer.lex();
552 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
553 {
554 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
555 {
556 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"\\refitem");
557 }
558 tok = parser()->tokenizer.lex();
559 }
562
563 if (!m_target.empty())
564 {
566 if (sec==nullptr && parser()->context.lang==SrcLangExt::Markdown) // lookup as markdown file
567 {
569 }
570 if (sec) // ref to section or anchor
571 {
572 // set defaults
573 m_ref = sec->ref();
576 m_anchor = sec->label();
577 m_isSubPage = false;
578 // adjust if needed
579 switch (sec->type().level())
580 {
582 {
584 m_isSubPage = pd && pd->hasParentPage();
585 if (!m_isSubPage)
586 {
587 m_anchor="";
588 }
589 }
590 break;
593 break;
596 break;
599 break;
600 default:
601 break;
602 }
603 //printf("m_ref=%s,m_file=%s,type=%d\n",
604 // qPrint(m_ref),qPrint(m_file),m_refType);
605 }
606 else
607 {
608 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to unknown section {}",m_target);
609 }
610 }
611 else
612 {
613 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to empty target");
614 }
615}
616
617//---------------------------------------------------------------------------
618
620{
621 AUTO_TRACE();
622 auto ns = AutoNodeStack(parser(),thisVariant());
623
624 Token tok=parser()->tokenizer.lex();
625 // skip white space
626 while (tok.is_any_of(TokenRetval::TK_WHITESPACE, TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
627 // handle items
628 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
629 {
630 if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS))
631 {
632 switch (Mappers::cmdMapper->map(parser()->context.token->name))
633 {
635 {
636 tok=parser()->tokenizer.lex();
637 if (!tok.is(TokenRetval::TK_WHITESPACE))
638 {
639 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\refitem command");
640 break;
641 }
642 tok=parser()->tokenizer.lex();
643 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
644 {
645 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\refitem",
646 tok.to_string());
647 break;
648 }
649
652 }
653 break;
655 return;
656 default:
657 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' as part of a \\secreflist",
658 tok.command_to_char(),qPrint(parser()->context.token->name));
659 return;
660 }
661 }
662 else if (tok.is(TokenRetval::TK_WHITESPACE))
663 {
664 // ignore whitespace
665 }
666 else
667 {
668 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {} inside section reference list",
669 tok.to_string());
670 return;
671 }
672 tok=parser()->tokenizer.lex();
673 }
674
675}
676
677//---------------------------------------------------------------------------
678
681{
682 size_t i=ref.find('#');
683 if (i!=DString::npos)
684 {
685 m_anchor = ref.mid(i+1);
686 m_file = ref.left(i);
687 }
688 else
689 {
690 m_file = ref;
691 }
692}
693
695{
696 AUTO_TRACE();
697 auto ns = AutoNodeStack(parser(),thisVariant());
698
699 Token tok = parser()->tokenizer.lex();
700 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
701 {
702 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
703 {
705 }
706 tok=parser()->tokenizer.lex();
707 }
708
710}
711
712//---------------------------------------------------------------------------
713
716{
717 const Definition *compound = nullptr;
719 AUTO_TRACE("target='{}',context='{}'",target,context);
720 ASSERT(!target.empty());
722 auto lang = parser->context.lang;
724 if (sec==nullptr && !parser->context.prefix.empty())
725 {
726 sec = SectionManager::instance().find(target);
727 }
728
729 if (sec==nullptr && getLanguageFromFileName(target)==SrcLangExt::Markdown) // lookup as markdown file
730 {
732 }
733 if (sec) // ref to section or anchor
734 {
735 PageDef *pd = nullptr;
736 int secLevel = sec->type().level();
737 if (secLevel==SectionType::Page)
738 {
739 pd = Doxygen::pageLinkedMap->find(target);
740 }
741 m_text = sec->title();
742 if (m_text.empty()) m_text = sec->label();
743
744 m_ref = sec->ref();
746 if (secLevel==SectionType::Anchor)
747 {
749 }
750 else if (secLevel==SectionType::Table)
751 {
753 }
754 else if (secLevel==SectionType::Requirement)
755 {
757 if (!Config_getBool(GENERATE_REQUIREMENTS))
758 {
759 m_file.clear(); // prevent link when there is no requirements page
760 }
761 }
762 else
763 {
765 }
766 m_isSubPage = pd && pd->hasParentPage();
767 if (secLevel!=SectionType::Page || m_isSubPage)
768 {
769 m_anchor = pd ? pd->getOutputFileBase() : sec->label();
770 }
771 m_sectionType = sec->type();
772 //printf("m_text=%s,m_ref=%s,m_file=%s,type=%d\n",
773 // qPrint(m_text),qPrint(m_ref),qPrint(m_file),m_refType);
774 AUTO_TRACE_EXIT("section");
775 return;
776 }
777 else if (Config_getBool(IMPLICIT_DIR_DOCS) && target.lower().endsWith("/readme.md"))
778 {
779 DString dirTarget = target.left(target.length() - 9); // strip readme.md part
780 const auto &dd = Doxygen::dirLinkedMap->find(dirTarget);
781 if (dd)
782 {
783 m_text = target;
784 m_file = dd->getOutputFileBase();
785 AUTO_TRACE_EXIT("directory");
786 return;
787 }
788 }
789 else if (resolveLink(context,target,true,&compound,anchor,lang,parser->context.prefix))
790 {
791 bool isFile = compound ?
792 (compound->definitionType()==Definition::TypeFile ||
793 compound->definitionType()==Definition::TypePage ? true : false) :
794 false;
795 bool isDir = compound && compound->definitionType()==Definition::TypeDir;
796 if (compound && lang==SrcLangExt::Markdown) lang = compound->getLanguage();
797 m_text = linkToText(lang,target,isFile || isDir);
799 if (compound && compound->isLinkable()) // ref to compound
800 {
801 if (anchor.empty() && /* compound link */
802 compound->definitionType()==Definition::TypeGroup && /* is group */
803 !toGroupDef(compound)->groupTitle().empty() /* with title */
804 )
805 {
806 m_text=(toGroupDef(compound))->groupTitle(); // use group's title as link
807 }
808 else if (compound->definitionType()==Definition::TypeMember &&
809 toMemberDef(compound)->isObjCMethod())
810 {
811 // Objective C Method
812 const MemberDef *member = toMemberDef(compound);
813 bool localLink = parser->context.memberDef ? member->getClassDef()==parser->context.memberDef->getClassDef() : false;
814 m_text = member->objCMethodName(localLink,parser->context.inSeeBlock);
815 }
816 else if (Config_getBool(HIDE_SCOPE_NAMES))
817 {
818 size_t funcPos = m_text.find('(');
819 if (funcPos!=DString::npos) // see issue #11834
820 {
821 m_text=stripScope(m_text.left(funcPos))+m_text.mid(funcPos);
822 }
823 else
824 {
826 }
827 }
828
829 m_file = compound->getOutputFileBase();
830 m_ref = compound->getReference();
831 //printf("isFile=%d compound=%s (%d)\n",isFile,qPrint(compound->name()),
832 // compound->definitionType());
833 AUTO_TRACE_EXIT("compound");
834 return;
835 }
836 else if (compound && compound->definitionType()==Definition::TypeFile &&
837 toFileDef(compound)->generateSourceFile()
838 ) // undocumented file that has source code we can link to
839 {
840 m_file = compound->getSourceFileBase();
841 m_ref = compound->getReference();
842 AUTO_TRACE_EXIT("source");
843 return;
844 }
845 else
846 {
847 AUTO_TRACE_EXIT("compound '{}' not linkable",compound?compound->name():"none");
848 }
849 }
850 m_text = target;
851 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve reference to '{}' for \\ref command",
852 target);
853}
854
856{
857 for (auto &&elem : elements)
858 {
859 emplace_back(std::move(elem));
860 }
861 elements.clear();
862}
863
864static void flattenParagraphs(DocNodeVariant *root,DocNodeList &children)
865{
866 DocNodeList newChildren;
867 for (auto &dn : children)
868 {
869 DocPara *para = std::get_if<DocPara>(&dn);
870 if (para)
871 {
872 //// move the children of the paragraph to the end of the newChildren list
873 newChildren.move_append(para->children());
874 }
875 }
876
877 // replace the children list by the newChildren list
878 children.clear();
879 children.move_append(newChildren);
880 // reparent the children
881 for (auto &cn : children)
882 {
883 setParent(&cn,root);
884 // we also need to set the parent for each child of cn, as cn's address may have changed.
885 auto opt_children = call_method_children(&cn);
886 if (opt_children)
887 {
888 for (auto &ccn : *opt_children)
889 {
890 setParent(&ccn,&cn);
891 }
892 }
893 }
894}
895
896void DocRef::parse(char cmdChar,const DString &cmdName)
897{
898 AUTO_TRACE();
899 auto ns = AutoNodeStack(parser(),thisVariant());
900 char cmdCharStr[2] = { cmdChar, 0 };
901
902 Token tok = parser()->tokenizer.lex();
903 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
904 {
905 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
906 {
907 switch (tok.value())
908 {
909 case TokenRetval::TK_HTMLTAG:
910 break;
911 default:
912 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),cmdCharStr+cmdName);
913 break;
914 }
915 }
916 tok=parser()->tokenizer.lex();
917 }
918
919 if (children().empty() && !m_text.empty())
920 {
921 DString text = m_text;
922 if (parser()->context.insideHtmlLink)
923 {
924 // we already in a link/title only output anchor
925 text = m_anchor;
926 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
927 "Potential recursion while resolving {:c}{} command!",cmdChar,cmdName);
928 }
930 { DocParser::AutoSaveContext saveContext(*parser());
932 }
936 }
937
939}
940
941//---------------------------------------------------------------------------
942
944{
945 size_t numBibFiles = Config_getList(CITE_BIB_FILES).size();
946 //printf("DocCite::DocCite(target=%s)\n",qPrint(target));
947 ASSERT(!target.empty());
950 const CiteInfo *cite = ct.find(target);
951 //printf("cite=%p text='%s' numBibFiles=%d\n",cite,cite?qPrint(cite->text):"<null>",numBibFiles);
952 m_option = opt;
954 if (numBibFiles>0 && cite && !cite->text().empty()) // ref to citation
955 {
956 m_ref = "";
957 m_anchor = ct.anchorPrefix()+cite->label();
958 m_file = convertNameToFile(ct.fileName(),false,true);
959 //printf("CITE ==> m_text=%s,m_ref=%s,m_file=%s,m_anchor=%s\n",
960 // qPrint(m_text),qPrint(m_ref),qPrint(m_file),qPrint(m_anchor));
961 return;
962 }
963 if (numBibFiles==0)
964 {
965 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"\\cite command found but no bib files specified via CITE_BIB_FILES!");
966 }
967 else if (cite==nullptr)
968 {
969 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve reference to '{}' for \\cite command",
970 target);
971 }
972 else
973 {
974 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"\\cite command to '{}' does not have an associated number",
975 target);
976 }
977}
978
980{
981 DString txt;
982 auto opt = m_option;
984 const CiteInfo *citeInfo = ct.find(m_target);
985
986 if (!opt.noPar()) txt += "[";
987
988 if (citeInfo)
989 {
990 if (opt.isNumber()) txt += citeInfo->text();
991 else if (opt.isShortAuthor()) txt += citeInfo->shortAuthor();
992 else if (opt.isYear()) txt += citeInfo->year();
993 }
994
995 if (!opt.noPar()) txt += "]";
996 return txt;
997}
998
999
1000//---------------------------------------------------------------------------
1001
1003{
1004 const Definition *compound = nullptr;
1006 m_refText = target;
1008 if (!m_refText.empty() && m_refText.at(0)=='#')
1009 {
1010 m_refText = m_refText.mid(1);
1011 }
1013 parser->context.inSeeBlock,&compound,anchor,
1015 {
1016 m_anchor = anchor;
1017 if (compound && compound->isLinkable())
1018 {
1019 m_file = compound->getOutputFileBase();
1020 m_ref = compound->getReference();
1021 }
1022 else if (compound && compound->definitionType()==Definition::TypeFile &&
1023 (toFileDef(compound))->generateSourceFile()
1024 ) // undocumented file that has source code we can link to
1025 {
1026 m_file = compound->getSourceFileBase();
1027 m_ref = compound->getReference();
1028 }
1029 return;
1030 }
1031
1032 // bogus link target
1033 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve link to '{}' for \\link command",
1034 target);
1035}
1036
1037
1038DString DocLink::parse(bool isJavaLink,bool isXmlLink)
1039{
1040 AUTO_TRACE();
1041 DString result;
1042 auto ns = AutoNodeStack(parser(),thisVariant());
1043
1044 Token tok = parser()->tokenizer.lex();
1045 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1046 {
1047 if (!parser()->defaultHandleToken(thisVariant(),tok,children(),false))
1048 {
1049 switch (tok.value())
1050 {
1051 case TokenRetval::TK_COMMAND_AT:
1052 // fall through
1053 case TokenRetval::TK_COMMAND_BS:
1054 switch (Mappers::cmdMapper->map(parser()->context.token->name))
1055 {
1057 if (isJavaLink)
1058 {
1059 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"'{{@link...' ended with '{:c}{}' command",
1060 tok.command_to_char(),parser()->context.token->name);
1061 }
1062 goto endlink;
1063 default:
1064 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' as part of a \\link",
1065 tok.command_to_char(),parser()->context.token->name);
1066 break;
1067 }
1068 break;
1069 case TokenRetval::TK_SYMBOL:
1070 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a \\link",
1071 parser()->context.token->name);
1072 break;
1073 case TokenRetval::TK_HTMLTAG:
1074 if (parser()->context.token->name!="see" || !isXmlLink)
1075 {
1076 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected xml/html command {} found as part of a \\link",
1077 parser()->context.token->name);
1078 }
1079 goto endlink;
1080 case TokenRetval::TK_LNKWORD:
1081 case TokenRetval::TK_WORD:
1082 if (isJavaLink) // special case to detect closing }
1083 {
1085 size_t p = 0;
1086 if (w=="}")
1087 {
1088 goto endlink;
1089 }
1090 else if ((p=w.find('}'))!=DString::npos)
1091 {
1092 size_t l = w.length();
1094 if (l>1 && p<l-1) // something left after the } (for instance a .)
1095 {
1096 result=w.right(l-p-1);
1097 }
1098 goto endlink;
1099 }
1100 }
1102 break;
1103 default:
1104 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",tok.to_string());
1105 break;
1106 }
1107 }
1108 tok = parser()->tokenizer.lex();
1109 }
1110 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1111 {
1112 warn_doc_error(parser()->context.fileName,
1113 parser()->tokenizer.getLineNr(),
1114 "Unexpected end of comment while inside link command");
1115 }
1116endlink:
1117
1118 if (children().empty()) // no link text
1119 {
1121 }
1122
1124 return result;
1125}
1126
1127
1128//---------------------------------------------------------------------------
1129
1136
1138{
1139 bool ok = false;
1141
1142 bool ambig = false;
1144 if (fd==nullptr && !p->name.endsWith(".dot")) // try with .dot extension as well
1145 {
1146 fd = Doxygen::dotFileNameLinkedMap->findFileDef(p->name+".dot",ambig);
1147 }
1148 if (fd)
1149 {
1150 p->file = fd->absFilePath();
1151 ok = true;
1152 if (ambig)
1153 {
1154 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dot file name '{}' is ambiguous.\n"
1155 "Possible candidates:\n{}",p->name,
1157 );
1158 }
1159 }
1160 else
1161 {
1162 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dot file '{}' is not found "
1163 "in any of the paths specified via DOTFILE_DIRS!",p->name);
1164 }
1165 return ok;
1166}
1167
1174
1176{
1177 bool ok = false;
1179
1180 bool ambig = false;
1182 if (fd==nullptr && !p->name.endsWith(".msc")) // try with .msc extension as well
1183 {
1184 fd = Doxygen::mscFileNameLinkedMap->findFileDef(p->name+".msc",ambig);
1185 }
1186 if (fd)
1187 {
1188 p->file = fd->absFilePath();
1189 ok = true;
1190 if (ambig)
1191 {
1192 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included msc file name '{}' is ambiguous.\n"
1193 "Possible candidates:\n{}",p->name,
1195 );
1196 }
1197 }
1198 else
1199 {
1200 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included msc file '{}' is not found "
1201 "in any of the paths specified via MSCFILE_DIRS!",p->name);
1202 }
1203 return ok;
1204}
1205
1206//---------------------------------------------------------------------------
1207
1214
1216{
1217 bool ok = false;
1219
1220 bool ambig = false;
1222 if (fd==nullptr && !p->name.endsWith(".dia")) // try with .dia extension as well
1223 {
1224 fd = Doxygen::diaFileNameLinkedMap->findFileDef(p->name+".dia",ambig);
1225 }
1226 if (fd)
1227 {
1228 p->file = fd->absFilePath();
1229 ok = true;
1230 if (ambig)
1231 {
1232 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dia file name '{}' is ambiguous.\n"
1233 "Possible candidates:\n{}",p->name,
1235 );
1236 }
1237 }
1238 else
1239 {
1240 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dia file '{}' is not found "
1241 "in any of the paths specified via DIAFILE_DIRS!",p->name);
1242 }
1243 return ok;
1244}
1245//---------------------------------------------------------------------------
1246
1253
1255{
1256 bool ok = false;
1258
1259 bool ambig = false;
1261 if (fd==nullptr && !p->name.endsWith(".puml")) // try with .puml extension as well
1262 {
1263 fd = Doxygen::plantUmlFileNameLinkedMap->findFileDef(p->name+".puml",ambig);
1264 if (fd==nullptr && !p->name.endsWith(".pu")) // try with .pu extension as well
1265 {
1266 fd = Doxygen::plantUmlFileNameLinkedMap->findFileDef(p->name+".pu",ambig);
1267 }
1268 }
1269 if (fd)
1270 {
1271 p->file = fd->absFilePath();
1272 ok = true;
1273 if (ambig)
1274 {
1275 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included uml file name '{}' is ambiguous.\n"
1276 "Possible candidates:\n{}",p->name,
1278 );
1279 }
1280 }
1281 else
1282 {
1283 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included uml file '{}' is not found "
1284 "in any of the paths specified via PLANTUMLFILE_DIRS!",p->name);
1285 }
1286 return ok;
1287}
1288
1289//---------------------------------------------------------------------------
1290
1297
1299{
1300 bool ok = false;
1302
1303 bool ambig = false;
1305 if (fd==nullptr && !p->name.endsWith(".mmd")) // try with .mmd extension as well
1306 {
1307 fd = Doxygen::mermaidFileNameLinkedMap->findFileDef(p->name+".mmd",ambig);
1308 }
1309 if (fd)
1310 {
1311 p->file = fd->absFilePath();
1312 ok = true;
1313 if (ambig)
1314 {
1315 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included mermaid file name '{}' is ambiguous.\n"
1316 "Possible candidates:\n{}",p->name,
1318 );
1319 }
1320 }
1321 else
1322 {
1323 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included mermaid file '{}' is not found "
1324 "in any of the paths specified via MERMAIDFILE_DIRS!",p->name);
1325 }
1326 return ok;
1327}
1328
1329//---------------------------------------------------------------------------
1330
1334
1336{
1337 AUTO_TRACE();
1338 auto ns = AutoNodeStack(parser(),thisVariant());
1339
1341 Token tok = parser()->tokenizer.lex();
1342 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1343 {
1344 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1345 {
1346 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"\\vhdlflow");
1347 }
1348 tok = parser()->tokenizer.lex();
1349 }
1350 parser()->tokenizer.lex();
1351
1354
1355 VhdlDocGen::createFlowChart(parser()->context.memberDef);
1356}
1357
1358
1359//---------------------------------------------------------------------------
1360
1362 Type t,const DString &url, bool inlineImage) :
1363 DocCompoundNode(parser,parent), p(std::make_unique<Private>(attribs, name, t, parser->context.relPath, url, inlineImage))
1364{
1365}
1366
1368{
1369 DString locName = p->url.empty() ? p->name : p->url;
1370 size_t len = locName.length();
1371 size_t fnd = locName.find('?'); // ignore part from ? until end
1372 if (fnd==DString::npos) fnd=len;
1373 return fnd>=4 && locName.mid(fnd-4,4)==".svg";
1374}
1375
1380
1381
1382//---------------------------------------------------------------------------
1383
1385{
1386 AUTO_TRACE();
1387 Token retval(TokenRetval::RetVal_OK);
1388 auto ns = AutoNodeStack(parser(),thisVariant());
1389
1390 Token tok = parser()->tokenizer.lex();
1391 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1392 {
1393 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1394 {
1395 switch (tok.value())
1396 {
1397 case TokenRetval::TK_HTMLTAG:
1398 {
1399 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1400 if (tagId==HtmlTagType::HTML_H1 && parser()->context.token->endTag) // found </h1> tag
1401 {
1402 if (m_level!=1)
1403 {
1404 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h1>",
1405 m_level);
1406 }
1407 goto endheader;
1408 }
1409 else if (tagId==HtmlTagType::HTML_H2 && parser()->context.token->endTag) // found </h2> tag
1410 {
1411 if (m_level!=2)
1412 {
1413 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h2>",
1414 m_level);
1415 }
1416 goto endheader;
1417 }
1418 else if (tagId==HtmlTagType::HTML_H3 && parser()->context.token->endTag) // found </h3> tag
1419 {
1420 if (m_level!=3)
1421 {
1422 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h3>",
1423 m_level);
1424 }
1425 goto endheader;
1426 }
1427 else if (tagId==HtmlTagType::HTML_H4 && parser()->context.token->endTag) // found </h4> tag
1428 {
1429 if (m_level!=4)
1430 {
1431 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h4>",
1432 m_level);
1433 }
1434 goto endheader;
1435 }
1436 else if (tagId==HtmlTagType::HTML_H5 && parser()->context.token->endTag) // found </h5> tag
1437 {
1438 if (m_level!=5)
1439 {
1440 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h5>",
1441 m_level);
1442 }
1443 goto endheader;
1444 }
1445 else if (tagId==HtmlTagType::HTML_H6 && parser()->context.token->endTag) // found </h6> tag
1446 {
1447 if (m_level!=6)
1448 {
1449 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h6>",
1450 m_level);
1451 }
1452 goto endheader;
1453 }
1454 else if (tagId==HtmlTagType::HTML_A)
1455 {
1456 if (!parser()->context.token->endTag)
1457 {
1458 parser()->handleAHref(thisVariant(),children(),parser()->context.token->attribs);
1459 }
1460 }
1461 else if (tagId==HtmlTagType::HTML_BR)
1462 {
1464 }
1465 else
1466 {
1467 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <h{:d}> context",
1468 parser()->context.token->endTag?"/":"",parser()->context.token->name,m_level);
1469 }
1470 }
1471 break;
1472 default:
1473 char tmp[20];
1474 snprintf(tmp,20,"<h%d> tag",m_level);
1476 }
1477 }
1478 tok = parser()->tokenizer.lex();
1479 }
1480 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1481 {
1482 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1483 " <h{:d}> tag",m_level);
1484 }
1485endheader:
1487 return retval;
1488}
1489//---------------------------------------------------------------------------
1490
1492{
1493 AUTO_TRACE();
1494 auto ns = AutoNodeStack(parser(),thisVariant());
1496 Token tok = parser()->tokenizer.lex();
1497 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1498 {
1499 // check of </summary>
1500 if (tok.value()==TokenRetval::TK_HTMLTAG &&
1501 (Mappers::htmlTagMapper->map(parser()->context.token->name))==HtmlTagType::XML_SUMMARY &&
1502 parser()->context.token->endTag
1503 )
1504 {
1505 break;
1506 }
1507 else if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS) &&
1508 (Mappers::cmdMapper->map(parser()->context.token->name)== CommandType::CMD_REF))
1509 {
1511 tok.command_to_char(),parser()->context.token->name);
1512 }
1513 else if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1514 {
1515 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"summary section");
1516 }
1517 tok = parser()->tokenizer.lex();
1518 }
1520 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1521 {
1522 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1523 " <summary> tag");
1524 }
1525}
1526
1527//---------------------------------------------------------------------------
1528
1530{
1531 AUTO_TRACE();
1532 Token retval(TokenRetval::TK_NONE);
1533 auto ns = AutoNodeStack(parser(),thisVariant());
1534
1535 // parse one or more paragraphs
1536 bool isFirst=true;
1537 DocPara *par=nullptr;
1538 do
1539 {
1541 par = children().get_last<DocPara>();
1542 if (isFirst) { par->markFirst(); isFirst=false; }
1543 retval=par->parse();
1544 }
1545 while (retval.is(TokenRetval::TK_NEWPARA));
1546 if (par) par->markLast();
1547
1548 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1549 {
1550 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <details> block");
1551 }
1552
1553 if (!summary())
1554 {
1555 HtmlAttribList summaryAttribs;
1557 DocHtmlSummary *summary = &std::get<DocHtmlSummary>(*m_summary);
1559 }
1560 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1561 return retval.is(TokenRetval::RetVal_EndHtmlDetails) ? Token::make_RetVal_OK() : retval;
1562}
1563
1571
1572//---------------------------------------------------------------------------
1573
1575{
1576 AUTO_TRACE();
1577 Token retval(TokenRetval::RetVal_OK);
1578 auto ns = AutoNodeStack(parser(),thisVariant());
1579
1580 Token tok = parser()->tokenizer.lex();
1581 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1582 {
1583 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1584 {
1585 switch (tok.value())
1586 {
1587 case TokenRetval::TK_HTMLTAG:
1588 {
1589 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1590 if (tagId==HtmlTagType::HTML_A && parser()->context.token->endTag) // found </a> tag
1591 {
1592 goto endhref;
1593 }
1594 else if (tagId==HtmlTagType::HTML_BR)
1595 {
1597 }
1598 else
1599 {
1600 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <a href=...> context",
1601 parser()->context.token->endTag?"/":"",parser()->context.token->name);
1602 }
1603 }
1604 break;
1605 default:
1606 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"<a>..</a> block");
1607 break;
1608 }
1609 }
1610 tok = parser()->tokenizer.lex();
1611 }
1612 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1613 {
1614 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1615 " <a href=...> tag");
1616 }
1617endhref:
1619 return retval;
1620}
1621
1622//---------------------------------------------------------------------------
1623
1625{
1626 AUTO_TRACE();
1627 Token retval(TokenRetval::RetVal_OK);
1628 auto ns = AutoNodeStack(parser(),thisVariant());
1629
1630 // first parse any number of paragraphs
1631 bool isFirst=true;
1632 DocPara *lastPar=nullptr;
1633 do
1634 {
1636 DocPara *par = children().get_last<DocPara>();
1637 if (isFirst) { par->markFirst(); isFirst=false; }
1638 retval=par->parse();
1639 if (!par->empty())
1640 {
1641 if (lastPar) lastPar->markLast(false);
1642 lastPar=par;
1643 }
1644 else
1645 {
1646 children().pop_back();
1647 }
1648 if (retval.is(TokenRetval::TK_LISTITEM))
1649 {
1650 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
1651 }
1652 } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF,
1653 TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection,
1654 TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph,
1655 TokenRetval::RetVal_EndInternal));
1656 if (lastPar) lastPar->markLast();
1657
1658 // then parse any number of level-n sections
1659 while ((level==1 && retval.is(TokenRetval::RetVal_Section)) ||
1660 (level==2 && retval.is(TokenRetval::RetVal_Subsection)) ||
1661 (level==3 && retval.is(TokenRetval::RetVal_Subsubsection)) ||
1662 (level==4 && retval.is(TokenRetval::RetVal_Paragraph)) ||
1663 (level==5 && retval.is(TokenRetval::RetVal_SubParagraph)) ||
1664 (level==6 && retval.is(TokenRetval::RetVal_SubSubParagraph))
1665 )
1666 {
1668 level,
1670 retval = children().get_last<DocSection>()->parse();
1671 }
1672
1673 if (retval.is(TokenRetval::RetVal_Internal))
1674 {
1675 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"\\internal command found inside internal section");
1676 }
1677
1678 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1679 return retval;
1680}
1681
1682//---------------------------------------------------------------------------
1683
1685{
1686 AUTO_TRACE();
1687 Token retval(TokenRetval::RetVal_OK);
1688 auto ns = AutoNodeStack(parser(),thisVariant());
1689 Token tok=parser()->tokenizer.lex();
1690 if (!tok.is(TokenRetval::TK_WHITESPACE))
1691 {
1692 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\addindex command");
1693 goto endindexentry;
1694 }
1696 m_entry="";
1697 tok = parser()->tokenizer.lex();
1698 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1699 {
1700 switch (tok.value())
1701 {
1702 case TokenRetval::TK_WHITESPACE:
1703 m_entry+=" ";
1704 break;
1705 case TokenRetval::TK_WORD:
1706 case TokenRetval::TK_LNKWORD:
1708 break;
1709 case TokenRetval::TK_SYMBOL:
1710 {
1711 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
1712 switch (s)
1713 {
1714 case HtmlEntityMapper::Sym_BSlash: m_entry+='\\'; break;
1715 case HtmlEntityMapper::Sym_At: m_entry+='@'; break;
1716 case HtmlEntityMapper::Sym_Less: m_entry+='<'; break;
1717 case HtmlEntityMapper::Sym_Greater: m_entry+='>'; break;
1718 case HtmlEntityMapper::Sym_Amp: m_entry+='&'; break;
1719 case HtmlEntityMapper::Sym_Dollar: m_entry+='$'; break;
1720 case HtmlEntityMapper::Sym_Hash: m_entry+='#'; break;
1721 case HtmlEntityMapper::Sym_Percent: m_entry+='%'; break;
1722 case HtmlEntityMapper::Sym_apos: m_entry+='\''; break;
1723 case HtmlEntityMapper::Sym_Quot: m_entry+='"'; break;
1724 case HtmlEntityMapper::Sym_lsquo: m_entry+='`'; break;
1725 case HtmlEntityMapper::Sym_rsquo: m_entry+='\''; break;
1726 case HtmlEntityMapper::Sym_ldquo: m_entry+="``"; break;
1727 case HtmlEntityMapper::Sym_rdquo: m_entry+="''"; break;
1728 case HtmlEntityMapper::Sym_ndash: m_entry+="--"; break;
1729 case HtmlEntityMapper::Sym_mdash: m_entry+="---"; break;
1730 default:
1731 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected symbol '{}' found as argument of \\addindex",parser()->context.token->name);
1732 break;
1733 }
1734 }
1735 break;
1736 case TokenRetval::TK_COMMAND_AT:
1737 // fall through
1738 case TokenRetval::TK_COMMAND_BS:
1739 switch (Mappers::cmdMapper->map(parser()->context.token->name))
1740 {
1741 case CommandType::CMD_BSLASH: m_entry+='\\'; break;
1742 case CommandType::CMD_AT: m_entry+='@'; break;
1743 case CommandType::CMD_LESS: m_entry+='<'; break;
1744 case CommandType::CMD_GREATER: m_entry+='>'; break;
1745 case CommandType::CMD_AMP: m_entry+='&'; break;
1746 case CommandType::CMD_DOLLAR: m_entry+='$'; break;
1747 case CommandType::CMD_HASH: m_entry+='#'; break;
1748 case CommandType::CMD_DCOLON: m_entry+="::"; break;
1749 case CommandType::CMD_PERCENT: m_entry+='%'; break;
1750 case CommandType::CMD_NDASH: m_entry+="--"; break;
1751 case CommandType::CMD_MDASH: m_entry+="---"; break;
1752 case CommandType::CMD_QUOTE: m_entry+='"'; break;
1753 case CommandType::CMD_PUNT: m_entry+='.'; break;
1754 case CommandType::CMD_PLUS: m_entry+='+'; break;
1755 case CommandType::CMD_MINUS: m_entry+='-'; break;
1756 case CommandType::CMD_EQUAL: m_entry+='='; break;
1757 case CommandType::CMD_EXCLAMATION: m_entry+='!'; break;
1758 case CommandType::CMD_QUESTION: m_entry+='?'; break;
1759 default:
1760 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command {} found as argument of \\addindex",
1761 parser()->context.token->name);
1762 break;
1763 }
1764 break;
1765 default:
1766 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",
1767 tok.to_string());
1768 break;
1769 }
1770 tok = parser()->tokenizer.lex();
1771 }
1774endindexentry:
1775 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1776 return retval;
1777}
1778
1779//---------------------------------------------------------------------------
1780
1783{
1784 m_hasCaptionId = false;
1785 for (const auto &opt : attribs)
1786 {
1787 if (opt.name=="id" && !opt.value.empty()) // interpret id attribute as an anchor
1788 {
1789 const SectionInfo *sec = SectionManager::instance().find(opt.value);
1790 if (sec)
1791 {
1792 //printf("Found anchor %s\n",qPrint(id));
1793 m_file = sec->fileName();
1794 m_anchor = sec->label();
1795 m_hasCaptionId = true;
1796 }
1797 else
1798 {
1799 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid caption id '{}'",opt.value);
1800 }
1801 }
1802 else // copy attribute
1803 {
1804 m_attribs.push_back(opt);
1805 }
1806 }
1807}
1808
1810{
1811 AUTO_TRACE();
1812 Token retval = Token::make_TK_NONE();
1813 auto ns = AutoNodeStack(parser(),thisVariant());
1814 Token tok = parser()->tokenizer.lex();
1815 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1816 {
1817 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1818 {
1819 switch (tok.value())
1820 {
1821 case TokenRetval::TK_HTMLTAG:
1822 {
1823 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1824 if (tagId==HtmlTagType::HTML_CAPTION && parser()->context.token->endTag) // found </caption> tag
1825 {
1826 retval = Token::make_RetVal_OK();
1827 goto endcaption;
1828 }
1829 else
1830 {
1831 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <caption> context",
1832 parser()->context.token->endTag?"/":"",parser()->context.token->name);
1833 }
1834 }
1835 break;
1836 case TokenRetval::TK_COMMAND_AT:
1837 // fall through
1838 case TokenRetval::TK_COMMAND_BS:
1839 {
1840 DString cmdName=parser()->context.token->name;
1841 bool isJavaLink=false;
1842 switch (Mappers::cmdMapper->map(cmdName))
1843 {
1845 {
1846 tok=parser()->tokenizer.lex();
1847 if (!tok.is(TokenRetval::TK_WHITESPACE))
1848 {
1849 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
1850 tok.command_to_char(),cmdName);
1851 }
1852 else
1853 {
1855 tok=parser()->tokenizer.lex(); // get the reference id
1856 if (!tok.is(TokenRetval::TK_WORD))
1857 {
1858 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}' command",
1859 tok.to_string(),tok.command_to_char(),cmdName);
1860 }
1861 else
1862 {
1864 children().get_last<DocRef>()->parse(tok.command_to_char(),cmdName);
1865 }
1867 }
1868 }
1869 break;
1871 isJavaLink=true;
1872 // fall through
1874 {
1875 tok=parser()->tokenizer.lex();
1876 if (!tok.is(TokenRetval::TK_WHITESPACE))
1877 {
1878 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
1879 cmdName);
1880 }
1881 else
1882 {
1884 tok=parser()->tokenizer.lex();
1885 if (!tok.is(TokenRetval::TK_WORD))
1886 {
1887 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\{} command",
1888 tok.to_string(),cmdName);
1889 }
1890 else
1891 {
1894 DocLink *lnk = children().get_last<DocLink>();
1895 DString leftOver = lnk->parse(isJavaLink);
1896 if (!leftOver.empty())
1897 {
1898 children().append<DocWord>(parser(),thisVariant(),leftOver);
1899 }
1900 }
1901 }
1902 }
1903 break;
1905 {
1907 }
1908 break;
1909 default:
1910 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a <caption> tag",
1911 tok.command_to_char(),cmdName);
1912 }
1913 }
1914 break;
1915 default:
1916 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"<caption> tag");
1917 break;
1918 }
1919 }
1920 tok = parser()->tokenizer.lex();
1921 }
1922 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1923 {
1924 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1925 " <caption> tag");
1926 }
1927endcaption:
1929 return retval;
1930}
1931
1932//---------------------------------------------------------------------------
1933
1935{
1936 AUTO_TRACE();
1937 Token retval = Token::make_RetVal_OK();
1938 auto ns = AutoNodeStack(parser(),thisVariant());
1939
1940 // parse one or more paragraphs
1941 bool isFirst=true;
1942 DocPara *par=nullptr;
1943 do
1944 {
1946 par = children().get_last<DocPara>();
1947 if (isFirst) { par->markFirst(); isFirst=false; }
1948 retval=par->parse();
1949 if (retval.is(TokenRetval::TK_HTMLTAG))
1950 {
1951 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1952 if (tagId==HtmlTagType::HTML_TD && parser()->context.token->endTag) // found </td> tag
1953 {
1954 retval = Token::make_TK_NEWPARA(); // ignore the tag
1955 }
1956 else if (tagId==HtmlTagType::HTML_TH && parser()->context.token->endTag) // found </th> tag
1957 {
1958 retval = Token::make_TK_NEWPARA(); // ignore the tag
1959 }
1960 }
1961 }
1962 while (retval.is_any_of(TokenRetval::TK_NEWPARA,TokenRetval::RetVal_EndParBlock));
1963 if (par) par->markLast();
1964
1965 return retval;
1966}
1967
1969{
1970 AUTO_TRACE();
1971 Token retval = Token::make_RetVal_OK();
1972 auto ns = AutoNodeStack(parser(),thisVariant());
1973
1974 // parse one or more paragraphs
1975 bool isFirst=true;
1976 DocPara *par=nullptr;
1977 do
1978 {
1980 par = children().get_last<DocPara>();
1981 if (isFirst) { par->markFirst(); isFirst=false; }
1982 retval=par->parse();
1983 if (retval.is(TokenRetval::TK_HTMLTAG))
1984 {
1985 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1986 if (tagId==HtmlTagType::XML_ITEM && parser()->context.token->endTag) // found </item> tag
1987 {
1988 retval = Token::make_TK_NEWPARA(); // ignore the tag
1989 }
1990 else if (tagId==HtmlTagType::XML_DESCRIPTION && parser()->context.token->endTag) // found </description> tag
1991 {
1992 retval = Token::make_TK_NEWPARA(); // ignore the tag
1993 }
1994 }
1995 }
1996 while (retval.is(TokenRetval::TK_NEWPARA));
1997 if (par) par->markLast();
1998
1999 return retval;
2000}
2001
2002uint32_t DocHtmlCell::rowSpan() const
2003{
2004 for (const auto &attr : attribs())
2005 {
2006 if (attr.name.lower()=="rowspan")
2007 {
2008 return attr.value.toUInt();
2009 }
2010 }
2011 return 0;
2012}
2013
2014uint32_t DocHtmlCell::colSpan() const
2015{
2016 for (const auto &attr : attribs())
2017 {
2018 if (attr.name.lower()=="colspan")
2019 {
2020 return std::max(1u,attr.value.toUInt());
2021 }
2022 }
2023 return 1;
2024}
2025
2027{
2028 for (const auto &attr : attribs())
2029 {
2030 DString attrName = attr.name.lower();
2031 DString attrValue = attr.value.lower();
2032 if (attrName=="align")
2033 {
2034 if (attrValue=="center")
2035 return Center;
2036 else if (attrValue=="right")
2037 return Right;
2038 else return Left;
2039 }
2040 else if (attrName=="class" && attrValue.startsWith("markdowntable"))
2041 {
2042 if (attrValue=="markdowntableheadcenter")
2043 return Center;
2044 else if (attrValue=="markdowntableheadright")
2045 return Right;
2046 else if (attrValue=="markdowntableheadleft")
2047 return Left;
2048 else if (attrValue=="markdowntableheadnone")
2049 return Center;
2050 else if (attrValue=="markdowntablebodycenter")
2051 return Center;
2052 else if (attrValue=="markdowntablebodyright")
2053 return Right;
2054 else if (attrValue=="markdowntablebodyleft")
2055 return Left;
2056 else if (attrValue=="markdowntablebodynone")
2057 return Left;
2058 else return Left;
2059 }
2060 }
2061 return Left;
2062}
2063
2065{
2066 for (const auto &attr : attribs())
2067 {
2068 DString attrName = attr.name.lower();
2069 DString attrValue = attr.value.lower();
2070 if (attrName=="valign")
2071 {
2072 if (attrValue=="top")
2073 return Top;
2074 else if (attrValue=="bottom")
2075 return Bottom;
2076 else if (attrValue=="middle")
2077 return Middle;
2078 else return Middle;
2079 }
2080 }
2081 return Middle;
2082}
2083
2084//---------------------------------------------------------------------------
2085
2087{ // a row is a table heading if all cells are marked as such
2088 bool heading=true;
2089 for (const auto &n : children())
2090 {
2091 const DocHtmlCell *cell = std::get_if<DocHtmlCell>(&n);
2092 if (cell && !cell->isHeading())
2093 {
2094 heading = false;
2095 break;
2096 }
2097 }
2098 return !children().empty() && heading;
2099}
2100
2102{
2103 AUTO_TRACE();
2104 // get next token
2105 Token tok=parser->tokenizer.lex();
2106 // skip whitespace and tbody, thead and tfoot tags
2107 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA,TokenRetval::TK_HTMLTAG,
2108 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS))
2109 {
2110 if (tok.is(TokenRetval::TK_HTMLTAG))
2111 {
2112 AUTO_TRACE_ADD("html_tag={}",parser->context.token->name);
2114 // skip over tbody, thead, tfoot tags
2115 if (tagId==HtmlTagType::HTML_TBODY ||
2116 tagId==HtmlTagType::HTML_THEAD ||
2118 {
2119 tok=parser->tokenizer.lex();
2120 }
2121 else
2122 {
2123 break;
2124 }
2125 }
2126 else if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS))
2127 {
2128 DString cmdName=parser->context.token->name;
2129 AUTO_TRACE_ADD("command={}",cmdName);
2130 auto cmdType = Mappers::cmdMapper->map(cmdName);
2131 if (cmdType==CommandType::CMD_ILINE)
2132 {
2133 { DocTokenizer::AutoSaveState saveState(parser->tokenizer);
2134 parser->tokenizer.setStateILine();
2135 tok = parser->tokenizer.lex();
2136 if (!tok.is(TokenRetval::TK_WORD))
2137 {
2138 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"invalid argument for command '{:c}{}'",
2139 tok.command_to_char(),cmdName);
2140 }
2141 }
2142 tok = parser->tokenizer.lex();
2143 }
2144 else
2145 {
2146 break;
2147 }
2148 }
2149 else
2150 {
2151 AUTO_TRACE_ADD("skip whitespace");
2152 tok=parser->tokenizer.lex();
2153 }
2154 }
2155 return tok;
2156}
2157
2158
2159
2160
2162{
2163 AUTO_TRACE();
2164 Token retval = Token::make_RetVal_OK();
2165 auto ns = AutoNodeStack(parser(),thisVariant());
2166
2167 bool isHeading=false;
2168 bool isFirst=true;
2169 DocHtmlCell *cell=nullptr;
2170
2172 // should find a html tag now
2173 if (tok.is(TokenRetval::TK_HTMLTAG))
2174 {
2175 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2176 if (tagId==HtmlTagType::HTML_TD && !parser()->context.token->endTag) // found <td> tag
2177 {
2178 }
2179 else if (tagId==HtmlTagType::HTML_TH && !parser()->context.token->endTag) // found <th> tag
2180 {
2181 isHeading=true;
2182 }
2183 else // found some other tag
2184 {
2185 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but "
2186 "found <{}{}> instead!",parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2187 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2188 goto endrow;
2189 }
2190 }
2191 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2192 {
2193 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2194 " for a html description title");
2195 goto endrow;
2196 }
2197 else // token other than html token
2198 {
2199 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but found {} token instead!",
2200 tok.to_string());
2201 goto endrow;
2202 }
2203
2204 // parse one or more cells
2205 do
2206 {
2209 isHeading);
2210 cell = children().get_last<DocHtmlCell>();
2211 cell->markFirst(isFirst);
2212 isFirst=false;
2213 retval=cell->parse();
2214 isHeading = retval.is(TokenRetval::RetVal_TableHCell);
2215 //printf("DocHtmlRow:retval=%s\n",retval.to_string());
2216 if (retval.is(TokenRetval::RetVal_EndTableCell))
2217 {
2218 // get next token
2219 retval = skipSpacesForTable(parser());
2220 //printf("DocHtmlRow:retval= next=%s name=%s endTag=%d\n",retval.to_string(),qPrint(parser()->context.token->name),parser()->context.token->endTag);
2221 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2222 if (tok.is(TokenRetval::TK_HTMLTAG))
2223 {
2224 if ((tagId==HtmlTagType::HTML_TD || tagId==HtmlTagType::HTML_TH) &&
2225 !parser()->context.token->endTag) // found new <td> or <td> tag
2226 {
2227 retval = Token::make_RetVal_TableCell();
2229 }
2230 else if (tagId==HtmlTagType::HTML_TR)
2231 {
2232 if (parser()->context.token->endTag) // found </tr> tag
2233 {
2234 retval = Token::make_RetVal_EndTableRow();
2235 }
2236 else // found <tr> tag
2237 {
2238 retval = Token::make_RetVal_TableRow();
2239 }
2240 }
2241 else if (tagId==HtmlTagType::HTML_TABLE && parser()->context.token->endTag) // found </table>
2242 {
2243 retval = Token::make_RetVal_EndTable();
2244 }
2245 else // found some other tag
2246 {
2247 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td>, <th> or <tr> tag but "
2248 "found <{}{}> instead!",parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2249 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2250 goto endrow;
2251 }
2252 }
2253 else // token other than html token
2254 {
2255 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td>, <th> or <tr> tag but found {} token instead!",
2256 tok.to_string());
2257 goto endrow;
2258 }
2259 }
2260 }
2261 while (retval.is_any_of(TokenRetval::RetVal_TableCell,TokenRetval::RetVal_TableHCell));
2262 cell->markLast(true);
2263
2264endrow:
2265 return retval;
2266}
2267
2269{
2270 AUTO_TRACE();
2271 Token retval = Token::make_RetVal_OK();
2272 auto ns = AutoNodeStack(parser(),thisVariant());
2273
2274 bool isFirst=true;
2275 DocHtmlCell *cell=nullptr;
2276
2277 // get next token
2278 Token tok=parser()->tokenizer.lex();
2279 // skip whitespace
2280 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2281 // should find a html tag now
2282 if (tok.is(TokenRetval::TK_HTMLTAG))
2283 {
2284 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2285 if (tagId==HtmlTagType::XML_TERM && !parser()->context.token->endTag) // found <term> tag
2286 {
2287 }
2288 else if (tagId==HtmlTagType::XML_DESCRIPTION && !parser()->context.token->endTag) // found <description> tag
2289 {
2290 }
2291 else // found some other tag
2292 {
2293 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <term> or <description> tag but "
2294 "found <{}> instead!",parser()->context.token->name);
2295 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2296 goto endrow;
2297 }
2298 }
2299 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2300 {
2301 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2302 " for a html description title");
2303 goto endrow;
2304 }
2305 else // token other than html token
2306 {
2307 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but found {} token instead!",
2308 tok.to_string());
2309 goto endrow;
2310 }
2311
2312 do
2313 {
2315 cell = children().get_last<DocHtmlCell>();
2316 cell->markFirst(isFirst);
2317 isFirst=false;
2318 retval=cell->parseXml();
2319 }
2320 while (retval.is_any_of(TokenRetval::RetVal_TableCell,TokenRetval::RetVal_TableHCell));
2321 cell->markLast(true);
2322
2323endrow:
2324 return retval;
2325}
2326
2327//---------------------------------------------------------------------------
2328
2330{
2331 return m_caption!=nullptr;
2332}
2333
2335{
2336 return m_caption.get();
2337}
2338
2340{
2341 size_t hl = 0;
2342 for (auto &rowNode : children())
2343 {
2344 const DocHtmlRow *row = std::get_if<DocHtmlRow>(&rowNode);
2345 if (row)
2346 {
2347 if (!row->isHeading()) break;
2348 hl++;
2349 }
2350 }
2351 return hl;
2352}
2353
2355{
2356 AUTO_TRACE();
2357 Token retval = Token::make_RetVal_OK();
2358 auto ns = AutoNodeStack(parser(),thisVariant());
2359
2360getrow:
2361 // skip whitespace and tbody, thead and tfoot tags
2363 // should find a html tag now
2364 if (tok.is(TokenRetval::TK_HTMLTAG))
2365 {
2366 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2367 if (tagId==HtmlTagType::HTML_TR && !parser()->context.token->endTag) // found <tr> tag
2368 {
2369 // no caption, just rows
2370 retval = Token::make_RetVal_TableRow();
2371 }
2372 else if (tagId==HtmlTagType::HTML_CAPTION && !parser()->context.token->endTag) // found <caption> tag
2373 {
2374 if (m_caption)
2375 {
2376 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"table already has a caption, found another one");
2377 }
2378 else
2379 {
2380 m_caption = createDocNode<DocHtmlCaption>(parser(),thisVariant(),parser()->context.token->attribs);
2381 retval=std::get<DocHtmlCaption>(*m_caption).parse();
2382
2383 if (retval.is(TokenRetval::RetVal_OK)) // caption was parsed ok
2384 {
2385 goto getrow;
2386 }
2387 }
2388 }
2389 else // found wrong token
2390 {
2391 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or <caption> tag but "
2392 "found <{}{}> instead!", parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2393 }
2394 }
2395 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2396 {
2397 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2398 " for a <tr> or <caption> tag");
2399 }
2400 else // token other than html token
2401 {
2402 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> tag but found {} token instead!",
2403 tok.to_string());
2404 }
2405
2406 // parse one or more rows
2407 while (retval.is(TokenRetval::RetVal_TableRow))
2408 {
2410 retval = children().get_last<DocHtmlRow>()->parse();
2411 //printf("DocHtmlTable::retval=%s\n",retval.to_string());
2412 if (retval.is(TokenRetval::RetVal_EndTableRow))
2413 {
2414 // get next token
2415 retval = skipSpacesForTable(parser());
2416 //printf("DocHtmlTable::retval= next=%s name=%s endTag=%d\n",retval.to_string(),qPrint(parser()->context.token->name),parser()->context.token->endTag);
2417 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2418 if (tagId==HtmlTagType::HTML_TR && !parser()->context.token->endTag)
2419 {
2420 retval = Token::make_RetVal_TableRow();
2421 }
2422 else if (tagId==HtmlTagType::HTML_TABLE && parser()->context.token->endTag)
2423 {
2424 retval = Token::make_RetVal_EndTable();
2425 }
2426 else if (retval.is(TokenRetval::TK_HTMLTAG)) // some other HTML tag
2427 {
2428 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or </table> tag but "
2429 "found <{}> instead!",parser()->context.token->name);
2430 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2431 }
2432 else // found some other tag
2433 {
2434 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or </table> tag but "
2435 "found token {} instead!",retval.to_string());
2436 retval=Token::make_RetVal_OK();
2437 break;
2438 }
2439 }
2440 }
2441
2443
2444 return retval.is(TokenRetval::RetVal_EndTable) ? Token::make_RetVal_OK() : retval;
2445}
2446
2448{
2449 AUTO_TRACE();
2450 Token retval = Token::make_RetVal_OK();
2451 auto ns = AutoNodeStack(parser(),thisVariant());
2452
2453 // get next token
2454 Token tok=parser()->tokenizer.lex();
2455 // skip whitespace
2456 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2457 // should find a html tag now
2459 bool isHeader=false;
2460 if (tok.is(TokenRetval::TK_HTMLTAG))
2461 {
2462 tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2463 if (tagId==HtmlTagType::XML_ITEM && !parser()->context.token->endTag) // found <item> tag
2464 {
2465 retval = Token::make_RetVal_TableRow();
2466 }
2467 if (tagId==HtmlTagType::XML_LISTHEADER && !parser()->context.token->endTag) // found <listheader> tag
2468 {
2469 retval = Token::make_RetVal_TableRow();
2470 isHeader=true;
2471 }
2472 }
2473
2474 // parse one or more rows
2475 while (retval.is(TokenRetval::RetVal_TableRow))
2476 {
2479 retval=tr->parseXml(isHeader);
2480 isHeader=false;
2481 }
2482
2484
2485 tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2486 return tagId==HtmlTagType::XML_LIST && parser()->context.token->endTag ? Token::make_RetVal_OK() : retval;
2487}
2488
2489/** Helper class to compute the grid for an HTML style table */
2491{
2492 ActiveRowSpan(uint32_t rows,uint32_t col) : rowsLeft(rows), column(col) {}
2493 uint32_t rowsLeft;
2494 uint32_t column;
2495};
2496
2497/** List of ActiveRowSpan classes. */
2498typedef std::vector<ActiveRowSpan> RowSpanList;
2499
2500/** determines the location of all cells in a grid, resolving row and
2501 column spans. For each the total number of visible cells is computed,
2502 and the total number of visible columns over all rows is stored.
2503 */
2505{
2506 //printf("computeTableGrid()\n");
2507 RowSpanList rowSpans;
2508 uint32_t maxCols=0;
2509 uint32_t rowIdx=1;
2510 for (auto &rowNode : children())
2511 {
2512 uint32_t colIdx=1;
2513 uint32_t cells=0;
2514 DocHtmlRow *row = std::get_if<DocHtmlRow>(&rowNode);
2515 if (row)
2516 {
2517 for (auto &cellNode : row->children())
2518 {
2519 DocHtmlCell *cell = std::get_if<DocHtmlCell>(&cellNode);
2520 if (cell)
2521 {
2522 uint32_t rs = cell->rowSpan();
2523 uint32_t cs = cell->colSpan();
2524
2525 for (size_t i=0;i<rowSpans.size();i++)
2526 {
2527 if (rowSpans[i].rowsLeft>0 &&
2528 rowSpans[i].column==colIdx)
2529 {
2530 colIdx=rowSpans[i].column+1;
2531 cells++;
2532 }
2533 }
2534 if (rs>0) rowSpans.emplace_back(rs,colIdx);
2535 //printf("found cell at (%d,%d)\n",rowIdx,colIdx);
2536 cell->setRowIndex(rowIdx);
2537 cell->setColumnIndex(colIdx);
2538 colIdx+=cs;
2539 cells++;
2540 }
2541 }
2542 for (size_t i=0;i<rowSpans.size();i++)
2543 {
2544 if (rowSpans[i].rowsLeft>0) rowSpans[i].rowsLeft--;
2545 }
2546 row->setVisibleCells(cells);
2547 row->setRowIndex(rowIdx);
2548 rowIdx++;
2549 }
2550 if (colIdx-1>maxCols) maxCols=colIdx-1;
2551 }
2552 m_numCols = maxCols;
2553}
2554
2555//---------------------------------------------------------------------------
2556
2558{
2559 AUTO_TRACE();
2560 Token retval = Token::make_TK_NONE();
2561 auto ns = AutoNodeStack(parser(),thisVariant());
2562
2563 Token tok = parser()->tokenizer.lex();
2564 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
2565 {
2566 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
2567 {
2568 switch (tok.value())
2569 {
2570 case TokenRetval::TK_COMMAND_AT:
2571 // fall through
2572 case TokenRetval::TK_COMMAND_BS:
2573 {
2574 DString cmdName=parser()->context.token->name;
2575 bool isJavaLink=false;
2576 switch (Mappers::cmdMapper->map(cmdName))
2577 {
2579 {
2580 tok=parser()->tokenizer.lex();
2581 if (!tok.is(TokenRetval::TK_WHITESPACE))
2582 {
2583 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
2584 tok.command_to_char(),cmdName);
2585 }
2586 else
2587 {
2589 tok=parser()->tokenizer.lex(); // get the reference id
2590 if (!tok.is(TokenRetval::TK_WORD))
2591 {
2592 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}' command",
2593 tok.to_string(),tok.command_to_char(),cmdName);
2594 }
2595 else
2596 {
2598 children().get_last<DocRef>()->parse(tok.command_to_char(),cmdName);
2599 }
2601 }
2602 }
2603 break;
2605 isJavaLink=true;
2606 // fall through
2608 {
2609 tok=parser()->tokenizer.lex();
2610 if (!tok.is(TokenRetval::TK_WHITESPACE))
2611 {
2612 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
2613 cmdName);
2614 }
2615 else
2616 {
2618 tok=parser()->tokenizer.lex();
2619 if (!tok.is(TokenRetval::TK_WORD))
2620 {
2621 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\{} command",
2622 tok.to_string(),cmdName);
2623 }
2624 else
2625 {
2628 DocLink *lnk = children().get_last<DocLink>();
2629 DString leftOver = lnk->parse(isJavaLink);
2630 if (!leftOver.empty())
2631 {
2632 children().append<DocWord>(parser(),thisVariant(),leftOver);
2633 }
2634 }
2635 }
2636 }
2637 break;
2639 {
2641 }
2642 break;
2643 default:
2644 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a <dt> tag",
2645 tok.command_to_char(),cmdName);
2646 }
2647 }
2648 break;
2649 case TokenRetval::TK_SYMBOL:
2650 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a <dt> tag",
2651 parser()->context.token->name);
2652 break;
2653 case TokenRetval::TK_HTMLTAG:
2654 {
2655 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2656 if (tagId==HtmlTagType::HTML_DD && !parser()->context.token->endTag) // found <dd> tag
2657 {
2658 retval = Token::make_RetVal_DescData();
2659 goto endtitle;
2660 }
2661 else if (tagId==HtmlTagType::HTML_DT && parser()->context.token->endTag)
2662 {
2663 // ignore </dt> tag.
2664 }
2665 else if (tagId==HtmlTagType::HTML_DT)
2666 {
2667 // missing <dt> tag.
2668 retval = Token::make_RetVal_DescTitle();
2669 goto endtitle;
2670 }
2671 else if (tagId==HtmlTagType::HTML_DL && parser()->context.token->endTag)
2672 {
2673 retval = Token::make_RetVal_EndDesc();
2674 goto endtitle;
2675 }
2676 else if (tagId==HtmlTagType::HTML_A)
2677 {
2678 if (!parser()->context.token->endTag)
2679 {
2680 parser()->handleAHref(thisVariant(),children(),parser()->context.token->attribs);
2681 }
2682 }
2683 else if (tagId==HtmlTagType::HTML_BR)
2684 {
2686 }
2687 else
2688 {
2689 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <dt> context",
2690 parser()->context.token->endTag?"/":"",parser()->context.token->name);
2691 }
2692 }
2693 break;
2694 default:
2695 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {} found as part of a <dt> tag",
2696 tok.to_string());
2697 break;
2698 }
2699 }
2700 tok = parser()->tokenizer.lex();
2701 }
2702 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2703 {
2704 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
2705 " <dt> tag");
2706 }
2707endtitle:
2709 return retval;
2710}
2711
2712
2713//---------------------------------------------------------------------------
2714
2716{
2717 AUTO_TRACE();
2719 Token retval = Token::make_TK_NONE();
2720 auto ns = AutoNodeStack(parser(),thisVariant());
2721
2722 bool isFirst=true;
2723 DocPara *par=nullptr;
2724 do
2725 {
2727 par = children().get_last<DocPara>();
2728 if (isFirst) { par->markFirst(); isFirst=false; }
2729 retval=par->parse();
2730 }
2731 while (retval.is(TokenRetval::TK_NEWPARA));
2732 if (par) par->markLast();
2733
2734 return retval;
2735}
2736
2737//---------------------------------------------------------------------------
2738
2740{
2741 AUTO_TRACE();
2742 Token retval = Token::make_RetVal_OK();
2743 auto ns = AutoNodeStack(parser(),thisVariant());
2744
2745 // get next token
2746 Token tok=parser()->tokenizer.lex();
2747 // skip whitespace
2748 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2749 // should find a html tag now
2750 if (tok.is(TokenRetval::TK_HTMLTAG))
2751 {
2752 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2753 if (tagId==HtmlTagType::HTML_DT && !parser()->context.token->endTag) // found <dt> tag
2754 {
2755 // continue
2756 }
2757 else // found some other tag
2758 {
2759 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <dt> tag but "
2760 "found <{}> instead!",parser()->context.token->name);
2761 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2762 goto enddesclist;
2763 }
2764 }
2765 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2766 {
2767 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2768 " for a html description title");
2769 goto enddesclist;
2770 }
2771 else // token other than html token
2772 {
2773 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <dt> tag but found {} token instead!",
2774 tok.to_string());
2775 goto enddesclist;
2776 }
2777
2778 do
2779 {
2784 retval=dt->parse();
2785 if (retval.is(TokenRetval::RetVal_DescData))
2786 {
2787 retval=dd->parse();
2788 while (retval.is(TokenRetval::RetVal_DescData))
2789 {
2792 retval=dd->parse();
2793 }
2794 }
2795 else if (!retval.is(TokenRetval::RetVal_DescTitle))
2796 {
2797 // error
2798 break;
2799 }
2800 } while (retval.is(TokenRetval::RetVal_DescTitle));
2801
2802 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2803 {
2804 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <dl> block");
2805 }
2806
2807enddesclist:
2808
2809 return retval.is(TokenRetval::RetVal_EndDesc) ? Token::make_RetVal_OK() : retval;
2810}
2811
2812//---------------------------------------------------------------------------
2813
2815{
2816 AUTO_TRACE();
2817 Token retval = Token::make_TK_NONE();
2818 auto ns = AutoNodeStack(parser(),thisVariant());
2819
2820 // parse one or more paragraphs
2821 bool isFirst=true;
2822 DocPara *par=nullptr;
2823 do
2824 {
2826 par = children().get_last<DocPara>();
2827 if (isFirst) { par->markFirst(); isFirst=false; }
2828 retval=par->parse();
2829 }
2830 while (retval.is(TokenRetval::TK_NEWPARA));
2831 if (par) par->markLast();
2832
2833 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2834 return retval;
2835}
2836
2838{
2839 AUTO_TRACE();
2840 Token retval = Token::make_TK_NONE();
2841 auto ns = AutoNodeStack(parser(),thisVariant());
2842
2843 // parse one or more paragraphs
2844 bool isFirst=true;
2845 DocPara *par=nullptr;
2846 do
2847 {
2849 par = children().get_last<DocPara>();
2850 if (isFirst) { par->markFirst(); isFirst=false; }
2851 retval=par->parse();
2852 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
2853
2854 //printf("new item: retval=%x parser()->context.token->name=%s parser()->context.token->endTag=%d\n",
2855 // retval,qPrint(parser()->context.token->name),parser()->context.token->endTag);
2856 if (retval.is(TokenRetval::RetVal_ListItem))
2857 {
2858 break;
2859 }
2860 }
2861 while (!retval.is(TokenRetval::RetVal_CloseXml));
2862
2863 if (par) par->markLast();
2864
2865 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2866 return retval;
2867}
2868
2869//---------------------------------------------------------------------------
2870
2872{
2873 AUTO_TRACE();
2874 Token retval = Token::make_RetVal_OK();
2875 int num=1;
2876 auto ns = AutoNodeStack(parser(),thisVariant());
2877
2878 // get next token
2879 Token tok=parser()->tokenizer.lex();
2880 // skip whitespace and paragraph breaks
2881 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2882 // should find a html tag now
2883 if (tok.is(TokenRetval::TK_HTMLTAG))
2884 {
2885 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2886 if (tagId==HtmlTagType::HTML_LI && !parser()->context.token->endTag) // found <li> tag
2887 {
2888 // ok, we can go on.
2889 }
2890 else if (((m_type==Unordered && tagId==HtmlTagType::HTML_UL) ||
2892 ) && parser()->context.token->endTag
2893 ) // found empty list
2894 {
2895 // add dummy item to obtain valid HTML
2897 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"empty list!");
2898 retval = Token::make_RetVal_EndList();
2899 goto endlist;
2900 }
2901 else // found some other tag
2902 {
2903 // add dummy item to obtain valid HTML
2905 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <li> tag but "
2906 "found <{}{}> instead!",parser()->context.token->endTag?"/":"",parser()->context.token->name);
2907 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2908 goto endlist;
2909 }
2910 }
2911 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2912 {
2913 // add dummy item to obtain valid HTML
2915 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2916 " for a html list item");
2917 goto endlist;
2918 }
2919 else // token other than html token
2920 {
2921 // add dummy item to obtain valid HTML
2923 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <li> tag but found {} token instead!",
2924 tok.to_string());
2925 goto endlist;
2926 }
2927
2928 do
2929 {
2932 retval=li->parse();
2933 } while (retval.is(TokenRetval::RetVal_ListItem));
2934
2935 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2936 {
2937 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <{:c}l> block",
2938 m_type==Unordered ? 'u' : 'o');
2939 }
2940
2941endlist:
2942 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2943 return retval.is(TokenRetval::RetVal_EndList) ? Token::make_RetVal_OK() : retval;
2944}
2945
2947{
2948 AUTO_TRACE();
2949 Token retval = Token::make_RetVal_OK();
2950 int num=1;
2951 auto ns = AutoNodeStack(parser(),thisVariant());
2952
2953 // get next token
2954 Token tok=parser()->tokenizer.lex();
2955 // skip whitespace and paragraph breaks
2956 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2957 // should find a html tag now
2958 if (tok.is(TokenRetval::TK_HTMLTAG))
2959 {
2960 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2961 //printf("parser()->context.token->name=%s parser()->context.token->endTag=%d\n",qPrint(parser()->context.token->name),parser()->context.token->endTag);
2962 if (tagId==HtmlTagType::XML_ITEM && !parser()->context.token->endTag) // found <item> tag
2963 {
2964 // ok, we can go on.
2965 }
2966 else // found some other tag
2967 {
2968 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <item> tag but "
2969 "found <{}> instead!",parser()->context.token->name);
2970 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2971 goto endlist;
2972 }
2973 }
2974 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2975 {
2976 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2977 " for a html list item");
2978 goto endlist;
2979 }
2980 else // token other than html token
2981 {
2982 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <item> tag but found {} token instead!",
2983 tok.to_string());
2984 goto endlist;
2985 }
2986
2987 do
2988 {
2991 retval=li->parseXml();
2992 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
2993 //printf("retval=%x parser()->context.token->name=%s\n",retval,qPrint(parser()->context.token->name));
2994 } while (retval.is(TokenRetval::RetVal_ListItem));
2995
2996 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2997 {
2998 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <list type=\"{}\"> block",
2999 m_type==Unordered ? "bullet" : "number");
3000 }
3001
3002endlist:
3003 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3004 return (retval.is_any_of(TokenRetval::RetVal_EndList,TokenRetval::RetVal_CloseXml) || parser()->context.token->name=="list") ?
3005 Token::make_RetVal_OK() : retval;
3006}
3007
3008//--------------------------------------------------------------------------
3009
3011{
3012 AUTO_TRACE();
3013 Token retval = Token::make_TK_NONE();
3014 auto ns = AutoNodeStack(parser(),thisVariant());
3015
3016 // parse one or more paragraphs
3017 bool isFirst=true;
3018 DocPara *par=nullptr;
3019 do
3020 {
3022 par = children().get_last<DocPara>();
3023 if (isFirst) { par->markFirst(); isFirst=false; }
3024 retval=par->parse();
3025 }
3026 while (retval.is(TokenRetval::TK_NEWPARA));
3027 if (par) par->markLast();
3028
3029 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3030 {
3031 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <blockquote> block");
3032 }
3033
3034 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3035 return retval.is(TokenRetval::RetVal_EndBlockQuote) ? Token::make_RetVal_OK() : retval;
3036}
3037
3038//---------------------------------------------------------------------------
3039
3041{
3042 AUTO_TRACE();
3043 Token retval = Token::make_TK_NONE();
3044 auto ns = AutoNodeStack(parser(),thisVariant());
3045
3046 // parse one or more paragraphs
3047 bool isFirst=true;
3048 DocPara *par=nullptr;
3049 do
3050 {
3052 par = children().get_last<DocPara>();
3053 if (isFirst) { par->markFirst(); isFirst=false; }
3054 retval=par->parse();
3055 }
3056 while (retval.is(TokenRetval::TK_NEWPARA));
3057 if (par) par->markLast();
3058
3059 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3060 return retval.is(TokenRetval::RetVal_EndBlockQuote) ? Token::make_RetVal_OK() : retval;
3061}
3062
3063//---------------------------------------------------------------------------
3064
3069
3070
3072{
3073 auto ns = AutoNodeStack(parser(),thisVariant());
3075 DocPara *par = &std::get<DocPara>(*m_paragraph);
3076 Token rv=par->parse();
3077 par->markFirst();
3078 par->markLast();
3079 return rv;
3080}
3081
3082//--------------------------------------------------------------------------
3083
3085{
3086 auto ns = AutoNodeStack(parser(),thisVariant());
3087 Token rv = Token::make_TK_NONE();
3088 do
3089 {
3092 rv=li->parse();
3093 } while (rv.is(TokenRetval::RetVal_ListItem));
3094 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3095}
3096
3097//--------------------------------------------------------------------------
3098
3103
3105{
3106 AUTO_TRACE();
3107 Token retval = Token::make_RetVal_OK();
3108 auto ns = AutoNodeStack(parser(),thisVariant());
3109
3110 // first parse any number of paragraphs
3111 bool isFirst=true;
3112 DocPara *lastPar=nullptr;
3113 do
3114 {
3116 DocPara *par = children().get_last<DocPara>();
3117 if (isFirst) { par->markFirst(); isFirst=false; }
3118 retval=par->parse();
3119 if (!par->empty())
3120 {
3121 if (lastPar) lastPar->markLast(false);
3122 lastPar=par;
3123 }
3124 else
3125 {
3126 children().pop_back();
3127 }
3128 // next paragraph should be more indented than the - marker to belong
3129 // to this item
3130 } while (retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->indent>m_indent);
3131 if (lastPar) lastPar->markLast();
3132
3133 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3134 return retval;
3135}
3136
3137//--------------------------------------------------------------------------
3138
3145
3147{
3148 AUTO_TRACE();
3149 Token retval = Token::make_RetVal_OK();
3150 int num=1;
3151 auto ns = AutoNodeStack(parser(),thisVariant());
3153 // first item or sub list => create new list
3154 do
3155 {
3156 switch (parser()->context.token->id)
3157 {
3158 case -1:
3159 break;
3160 case DocAutoList::Unchecked: // unchecked
3161 case DocAutoList::Checked_x: // checked with x
3162 case DocAutoList::Checked_X: // checked with X
3163 num = parser()->context.token->id;
3164 break;
3165 default: // explicitly numbered list
3166 num=parser()->context.token->id; // override num with real number given
3167 break;
3168 }
3169
3171 retval = children().get_last<DocAutoListItem>()->parse();
3172 //printf("DocAutoList::parse(): retval=0x%x parser()->context.token->indent=%d m_indent=%d "
3173 // "m_isEnumList=%d parser()->context.token->isEnumList=%d parser()->context.token->name=%s\n",
3174 // retval,parser()->context.token->indent,m_indent,m_isEnumList,parser()->context.token->isEnumList,
3175 // qPrint(parser()->context.token->name));
3176 //printf("num=%d parser()->context.token->id=%d\n",num,parser()->context.token->id);
3177 }
3178 while (retval.is(TokenRetval::TK_LISTITEM) && // new list item
3179 m_indent==parser()->context.token->indent && // at same indent level
3180 m_isEnumList==parser()->context.token->isEnumList && // of the same kind
3181 m_isCheckedList==parser()->context.token->isCheckedList && // of the same kind
3182 (parser()->context.token->id==-1 || parser()->context.token->id>=num) // increasing number (or no number or checked list)
3183 );
3184
3186
3187 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3188 return retval;
3189}
3190
3191//--------------------------------------------------------------------------
3192
3194{
3195 AUTO_TRACE();
3196 auto ns = AutoNodeStack(parser(),thisVariant());
3198 Token tok = parser()->tokenizer.lex();
3199 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
3200 {
3201 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
3202 {
3203 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"title section");
3204 }
3205 tok = parser()->tokenizer.lex();
3206 }
3209}
3210
3221
3222//--------------------------------------------------------------------------
3223
3228
3230{
3231 return m_title && std::get<DocTitle>(*m_title).hasTitle();
3232}
3233
3234Token DocSimpleSect::parse(bool userTitle,bool needsSeparator)
3235{
3236 AUTO_TRACE();
3237 auto ns = AutoNodeStack(parser(),thisVariant());
3238
3239 // handle case for user defined title
3240 if (userTitle)
3241 {
3243 std::get_if<DocTitle>(m_title.get())->parse();
3244 }
3245
3246 // add new paragraph as child
3247 if (!children().empty() && std::holds_alternative<DocPara>(children().back()))
3248 {
3249 std::get<DocPara>(children().back()).markLast(false);
3250 }
3251 bool markFirst = children().empty();
3252 if (needsSeparator)
3253 {
3255 }
3257 DocPara *par = children().get_last<DocPara>();
3258 if (markFirst)
3259 {
3260 par->markFirst();
3261 }
3262 par->markLast();
3263
3264 // parse the contents of the paragraph
3265 Token retval = par->parse();
3266
3267 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3268 return retval; // 0==EOF, TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, TokenRetval::TK_ENDLIST, TokenRetval::RetVal_SimpleSec
3269}
3270
3272{
3273 AUTO_TRACE();
3274 auto ns = AutoNodeStack(parser(),thisVariant());
3275
3277 DocTitle *title = &std::get<DocTitle>(*m_title);
3278 title->parseFromString(thisVariant(),parser()->context.token->name);
3279
3280 DString text = parser()->context.token->text;
3281 { DocParser::AutoSaveContext saveContext(*parser());
3283 }
3284
3285 return Token::make_RetVal_OK();
3286}
3287
3289{
3290 AUTO_TRACE();
3291 auto ns = AutoNodeStack(parser(),thisVariant());
3292
3293 Token retval = Token::make_RetVal_OK();
3294 for (;;)
3295 {
3296 // add new paragraph as child
3297 if (!children().empty() && std::holds_alternative<DocPara>(children().back()))
3298 {
3299 std::get<DocPara>(children().back()).markLast(false);
3300 }
3301 bool markFirst = children().empty();
3303 DocPara *par = children().get_last<DocPara>();
3304 if (markFirst)
3305 {
3306 par->markFirst();
3307 }
3308 par->markLast();
3309
3310 // parse the contents of the paragraph
3311 retval = par->parse();
3312 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
3313 if (retval.is(TokenRetval::RetVal_CloseXml))
3314 {
3315 retval = Token::make_RetVal_OK();
3316 break;
3317 }
3318 }
3319
3320 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3321 return retval;
3322}
3323
3325{
3326 DocPara *p=nullptr;
3327 if (children().empty() || (p=std::get_if<DocPara>(&children().back()))==nullptr)
3328 {
3330 p = children().get_last<DocPara>();
3331 }
3332 else
3333 {
3334 // Comma-separate <seealso> links.
3335 p->injectToken(Token::make_TK_WORD(),",");
3336 p->injectToken(Token::make_TK_WHITESPACE()," ");
3337 }
3338
3339 parser()->context.inSeeBlock=true;
3340 p->injectToken(Token::make_TK_LNKWORD(),word);
3341 parser()->context.inSeeBlock=false;
3342}
3343
3345{
3346 switch (m_type)
3347 {
3348 case Unknown: break;
3349 case See: return "see";
3350 case Return: return "return";
3351 case Author: // fall through
3352 case Authors: return "author";
3353 case Version: return "version";
3354 case Since: return "since";
3355 case Date: return "date";
3356 case Note: return "note";
3357 case Warning: return "warning";
3358 case Pre: return "pre";
3359 case Post: return "post";
3360 case Copyright: return "copyright";
3361 case Invar: return "invariant";
3362 case Remark: return "remark";
3363 case Attention: return "attention";
3364 case Important: return "important";
3365 case User: return "user";
3366 case Rcs: return "rcs";
3367 }
3368 return "unknown";
3369}
3370
3371//--------------------------------------------------------------------------
3372
3374{
3375 AUTO_TRACE();
3376 Token retval = Token::make_RetVal_OK();
3377 auto ns = AutoNodeStack(parser(),thisVariant());
3378 DocPara *par=nullptr;
3379 DString saveCmdName = cmdName;
3380 DocParserContext &context = parser()->context;
3381 DocTokenizer &tokenizer = parser()->tokenizer;
3382
3383 Token tok=tokenizer.lex();
3384 if (!tok.is(TokenRetval::TK_WHITESPACE))
3385 {
3386 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
3387 saveCmdName);
3388 retval = Token::make_RetVal_EndParBlock();
3389 goto endparamlist;
3390 }
3391 tokenizer.setStateParam();
3392 tok=tokenizer.lex();
3393 while (tok.is(TokenRetval::TK_WORD)) /* there is a parameter name */
3394 {
3396 {
3397 size_t typeSeparator = context.token->name.find('#'); // explicit type position
3398 if (typeSeparator!=DString::npos)
3399 {
3400 parser()->handleParameterType(thisVariant(),m_paramTypes,context.token->name.left(typeSeparator));
3401 context.token->name = context.token->name.mid(typeSeparator+1);
3402 context.hasParamCommand=true;
3403 if (parent() && std::holds_alternative<DocParamSect>(*parent()))
3404 {
3405 std::get<DocParamSect>(*parent()).m_hasTypeSpecifier=true;
3406 }
3407 }
3408 else
3409 {
3410 context.hasParamCommand=true;
3411 }
3413 }
3414 else if (m_type==DocParamSect::RetVal)
3415 {
3416 context.hasReturnCommand=true;
3418 }
3419 context.inSeeBlock=true;
3421 context.inSeeBlock=false;
3422 tok=tokenizer.lex();
3423 }
3424 tokenizer.setStatePara();
3425 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
3426 {
3427 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3428 "argument of command {}",saveCmdName);
3429 retval = Token::make_RetVal_EndParBlock();
3430 goto endparamlist;
3431 }
3432 if (!tok.is(TokenRetval::TK_WHITESPACE)) /* premature end of comment block */
3433 {
3434 if (!tok.is(TokenRetval::TK_NEWPARA)) /* empty param description */
3435 {
3436 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} in comment block while parsing the "
3437 "argument of command {}",tok.to_string(),saveCmdName);
3438 }
3439 retval = Token::make_RetVal_EndParBlock();
3440 goto endparamlist;
3441 }
3442
3444 par = m_paragraphs.get_last<DocPara>();
3445 retval = par->parse();
3446 par->markFirst();
3447 par->markLast();
3448
3449endparamlist:
3450 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3451 return retval;
3452}
3453
3455{
3456 AUTO_TRACE();
3457 Token retval = Token::make_RetVal_OK();
3458 auto ns = AutoNodeStack(parser(),thisVariant());
3459
3460 parser()->context.token->name = paramName;
3462 {
3465 }
3466 else if (m_type==DocParamSect::RetVal)
3467 {
3470 }
3471
3473
3474 do
3475 {
3478 retval = par->parse();
3479 if (par->empty()) // avoid adding an empty paragraph for the whitespace
3480 // after </para> and before </param>
3481 {
3483 break;
3484 }
3485 else // append the paragraph to the list
3486 {
3487 if (!m_paragraphs.empty())
3488 {
3490 }
3491 bool markFirst = m_paragraphs.empty();
3492 par = &std::get<DocPara>(m_paragraphs.back());
3493 if (markFirst)
3494 {
3495 par->markFirst();
3496 }
3497 par->markLast();
3498 }
3499
3500 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
3501
3502 } while (retval.is(TokenRetval::RetVal_CloseXml) &&
3503 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_PARAM &&
3504 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_TYPEPARAM &&
3505 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_EXCEPTION);
3506
3507 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) /* premature end of comment block */
3508 {
3509 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unterminated param or exception tag");
3510 }
3511 else
3512 {
3513 retval = Token::make_RetVal_OK();
3514 }
3515
3516 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3517 return retval;
3518}
3519
3520//--------------------------------------------------------------------------
3521
3522Token DocParamSect::parse(const DString &cmdName,bool xmlContext, Direction d)
3523{
3524 AUTO_TRACE();
3525 Token retval = Token::make_RetVal_OK();
3526 auto ns = AutoNodeStack(parser(),thisVariant());
3527
3528 if (d!=Unspecified)
3529 {
3531 }
3532
3533 if (!children().empty() && std::holds_alternative<DocParamList>(children().back()))
3534 {
3535 DocParamList &lastPl = std::get<DocParamList>(children().back());
3536 lastPl.markLast(false);
3537 }
3538 bool markFirst = children().empty();
3541 if (markFirst)
3542 {
3543 pl->markFirst();
3544 }
3545 pl->markLast();
3546 if (xmlContext)
3547 {
3548 retval = pl->parseXml(cmdName);
3549 }
3550 else
3551 {
3552 retval = pl->parse(cmdName);
3553 }
3554 if (retval.is(TokenRetval::RetVal_EndParBlock))
3555 {
3556 retval = Token::make_RetVal_OK();
3557 }
3558
3559 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3560 return retval;
3561}
3562
3563//--------------------------------------------------------------------------
3564
3570
3572{
3573 AUTO_TRACE();
3574 DocSimpleSect *ss=nullptr;
3575 bool needsSeparator = false;
3576 if (!children().empty() && // has previous element
3577 (ss=children().get_last<DocSimpleSect>()) && // was a simple sect
3578 ss->type()==t && // of same type
3579 t!=DocSimpleSect::User) // but not user defined
3580 {
3581 // append to previous section
3582 needsSeparator = true;
3583 }
3584 else // start new section
3585 {
3588 }
3589 Token rv = Token::make_RetVal_OK();
3590 if (xmlContext)
3591 {
3592 return ss->parseXml();
3593 }
3594 else
3595 {
3596 rv = ss->parse(t==DocSimpleSect::User,needsSeparator);
3597 }
3598 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3599}
3600
3603 bool xmlContext=false,
3604 int direction=DocParamSect::Unspecified)
3605{
3606 AUTO_TRACE();
3607 DocParamSect *ps = nullptr;
3608 if (!children().empty() && // previous element
3609 (ps=children().get_last<DocParamSect>()) && // was a param sect
3610 ps->type()==t) // of same type
3611 { // append to previous section ps
3612 }
3613 else // start new section
3614 {
3616 ps = children().get_last<DocParamSect>();
3617 }
3618 Token rv=ps->parse(cmdName,xmlContext,
3619 static_cast<DocParamSect::Direction>(direction));
3620 AUTO_TRACE_EXIT("retval={}",rv.to_string());
3621 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3622}
3623
3624void DocPara::handleEmoji(char cmdChar,const DString &cmdName)
3625{
3626 AUTO_TRACE();
3627 // get the argument of the emoji command.
3628 Token tok=parser()->tokenizer.lex();
3629 if (!tok.is(TokenRetval::TK_WHITESPACE))
3630 {
3631 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3632 cmdChar,cmdName);
3633 return;
3634 }
3636 tok=parser()->tokenizer.lex();
3637 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3638 {
3639 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"no emoji name given or unexpected end of comment block while parsing the "
3640 "argument of command '{:c}{}'",cmdChar,cmdName);
3642 return;
3643 }
3644 else if (!tok.is(TokenRetval::TK_WORD))
3645 {
3646 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3647 tok.to_string(),cmdChar,cmdName);
3649 return;
3650 }
3653}
3654
3655void DocPara::handleDoxyConfig(char cmdChar,const DString &cmdName)
3656{
3657 // get the argument of the cite command.
3658 Token tok=parser()->tokenizer.lex();
3659 if (!tok.is(TokenRetval::TK_WHITESPACE))
3660 {
3661 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3662 cmdChar,cmdName);
3663 return;
3664 }
3666 tok=parser()->tokenizer.lex();
3667 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3668 {
3669 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3670 "argument of command '{:c}{}'",cmdChar,cmdName);
3671 return;
3672 }
3673 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
3674 {
3675 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3676 tok.to_string(),cmdChar,cmdName);
3677 return;
3678 }
3679 ConfigOption * opt = ConfigImpl::instance()->get(parser()->context.token->name);
3680 if (opt)
3681 {
3682 DString optionValue;
3683 switch (opt->kind())
3684 {
3686 optionValue = *(static_cast<ConfigBool*>(opt)->valueStringRef());
3687 break;
3689 optionValue = *(static_cast<ConfigString*>(opt)->valueRef());
3690 break;
3692 optionValue = *(static_cast<ConfigEnum*>(opt)->valueRef());
3693 break;
3695 optionValue = *(static_cast<ConfigInt*>(opt)->valueStringRef());
3696 break;
3698 {
3699 StringVector *lst = static_cast<ConfigList*>(opt)->valueRef();
3700 optionValue="";
3701 if (!lst->empty())
3702 {
3703 std::string lstFormat = theTranslator->trWriteList(static_cast<int>(lst->size())).str();
3704 static const reg::Ex marker(R"(@(\d+))");
3705 reg::Iterator it(lstFormat,marker);
3707 size_t index=0;
3708 // now replace all markers with the real text
3709 for ( ; it!=end ; ++it)
3710 {
3711 const auto &match = *it;
3712 size_t newIndex = match.position();
3713 size_t matchLen = match.length();
3714 optionValue += lstFormat.substr(index,newIndex-index);
3715 unsigned long entryIndex = std::stoul(match[1].str());
3716 if (entryIndex<(unsigned long)lst->size())
3717 {
3718 optionValue += lst->at(entryIndex);
3719 }
3720 index=newIndex+matchLen;
3721 }
3722 optionValue+=lstFormat.substr(index);
3723 }
3724 }
3725 break;
3727 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Obsolete setting for '{:c}{}': '{}'",
3728 cmdChar,cmdName,parser()->context.token->name);
3729 break;
3731 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),
3732 "Disabled setting (i.e. not supported in this doxygen executable) for '{:c}{}': '{}'",
3733 cmdChar,cmdName,parser()->context.token->name);
3734 break;
3736 // nothing to show here
3737 break;
3738 }
3739 if (!optionValue.empty())
3740 {
3741 children().append<DocWord>(parser(),thisVariant(),optionValue);
3742 }
3743 }
3744 else
3745 {
3746 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Unknown option for '{:c}{}': '{}'",
3747 cmdChar,cmdName,parser()->context.token->name);
3749 }
3751}
3752
3754{
3755 AUTO_TRACE();
3756 Token retval=parser()->tokenizer.lex();
3757 ASSERT(retval.is(TokenRetval::TK_WHITESPACE));
3759 retval=parser()->tokenizer.lex();
3760 if (retval.is(TokenRetval::RetVal_OK))
3761 {
3765 if (!ref->parse())
3766 {
3767 children().pop_back();
3768 }
3769 }
3771 return retval;
3772}
3773
3774
3775void DocPara::handleShowDate(char cmdChar,const DString &cmdName)
3776{
3777 AUTO_TRACE();
3778 DString fmt;
3779 DString date;
3780 Token tok=parser()->tokenizer.lex();
3781 if (!tok.is(TokenRetval::TK_WHITESPACE))
3782 {
3783 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3784 cmdChar,cmdName);
3785 return;
3786 }
3788 tok = parser()->tokenizer.lex();
3789 if (!tok.is(TokenRetval::TK_WORD))
3790 {
3791 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <format> argument for command '{:c}{}'",
3792 cmdChar,cmdName);
3794 return;
3795 }
3796 fmt = parser()->context.token->name;
3797
3799 tok = parser()->tokenizer.lex();
3800
3801 DString specDateRaw = tok.is(TokenRetval::TK_WORD) ? parser()->context.token->name : DString();
3802 DString specDate = specDateRaw.stripWhiteSpace();
3803 bool specDateOnlyWS = !specDateRaw.empty() && specDate.empty();
3804 if (!specDate.empty() && !tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3805 {
3806 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}'",
3807 cmdChar,cmdName);
3809 return;
3810 }
3811
3812 std::tm dat{};
3813 int specFormat=0;
3814 DString err = dateTimeFromString(specDate,dat,specFormat);
3815 if (!err.empty())
3816 {
3817 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}': {}",
3818 cmdChar,cmdName,err);
3820 return;
3821 }
3822
3823 int usedFormat=0;
3824 DString dateTimeStr = formatDateTime(fmt,dat,usedFormat);
3825
3826 // warn the user if the format contains markers that are not explicitly filled in
3827 for (int i=0;i<SF_NumBits;i++)
3828 {
3829 int bitMask = 1<<i;
3830 if ((usedFormat&bitMask) && !(specFormat&bitMask)) // a part was used in the format string but its value was not specified.
3831 {
3832 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.",
3833 cmdChar,cmdName,fmt,SF_bit2str(i),specDate,SF_bit2str(i));
3834 }
3835 }
3836
3837 children().append<DocWord>(parser(),thisVariant(),dateTimeStr);
3838 if (specDateOnlyWS) // specDate is only whitespace
3839 {
3841 }
3843}
3844
3846{
3847 AUTO_TRACE("cmdName={}",cmdName);
3848 DString saveCmdName = cmdName;
3849 Token tok=parser()->tokenizer.lex();
3850 if (!tok.is(TokenRetval::TK_WHITESPACE))
3851 {
3852 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3853 saveCmdName);
3854 return;
3855 }
3857 tok=parser()->tokenizer.lex();
3859 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3860 {
3861 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3862 "argument of command {}", saveCmdName);
3863 return;
3864 }
3865 else if (!tok.is(TokenRetval::TK_WORD))
3866 {
3867 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3868 tok.to_string(),saveCmdName);
3869 return;
3870 }
3871 auto it1 = children().size()>=1 ? std::prev(children().end()) : children().end();
3872 auto it2 = children().size()>=2 ? std::prev(it1) : children().end();
3873 DocNodeVariant *n1 = it1!=children().end() ? &(*it1) : nullptr;
3874 DocNodeVariant *n2 = it2!=children().end() ? &(*it2) : nullptr;
3875 //TODO get from context the stripCodeComments()
3876 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
3880 stripCodeComments,
3883 );
3885 DocIncOperator *n1_docIncOp = std::get_if<DocIncOperator>(n1);
3886 DocWhiteSpace *n1_docWs = std::get_if<DocWhiteSpace >(n1);
3887 DocIncOperator *n2_docIncOp = std::get_if<DocIncOperator>(n2);
3888 bool isFirst = !n1 || // no last node
3889 (!n1_docIncOp && !n1_docWs) || // last node is not operator or whitespace
3890 (n1_docWs && n2 && !n2_docIncOp); // last node is not operator
3891 op->markFirst(isFirst);
3892 op->markLast(true);
3893 if (n1_docIncOp)
3894 {
3895 n1_docIncOp->markLast(false);
3896 }
3897 else if (n1_docWs && n2_docIncOp)
3898 {
3899 n2_docIncOp->markLast(false);
3900 }
3901 op->parse();
3902}
3903
3904template<class T>
3905void DocPara::handleFile(const DString &cmdName)
3906{
3907 AUTO_TRACE("cmdName={}",cmdName);
3908 DString saveCmdName = cmdName;
3909 Token tok=parser()->tokenizer.lex();
3910 if (!tok.is(TokenRetval::TK_WHITESPACE))
3911 {
3912 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3913 saveCmdName);
3914 return;
3915 }
3917 tok=parser()->tokenizer.lex();
3919 if (!tok.is(TokenRetval::TK_WORD))
3920 {
3921 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3922 tok.to_string(),saveCmdName);
3923 return;
3924 }
3925 DString name = parser()->context.token->name;
3926 children().append<T>(parser(),thisVariant(),name,
3930 auto df = children().get_last<T>();
3931 if (!df->parse())
3932 {
3933 children().pop_back();
3934 }
3935}
3936
3943
3944void DocPara::handleLink(const DString &cmdName,bool isJavaLink)
3945{
3946 AUTO_TRACE("cmdName={} isJavaLink={}",cmdName,isJavaLink);
3947 DString saveCmdName = cmdName;
3948 Token tok=parser()->tokenizer.lex();
3949 if (!tok.is(TokenRetval::TK_WHITESPACE))
3950 {
3951 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3952 saveCmdName);
3953 return;
3954 }
3956 tok=parser()->tokenizer.lex();
3957 if (!tok.is(TokenRetval::TK_WORD))
3958 {
3959 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"{} as the argument of {}",
3960 tok.to_string(),saveCmdName);
3961 return;
3962 }
3963 if (saveCmdName == "javalink")
3964 {
3966 parser()->context.nodeStack.size(),
3967 DocStyleChange::Code,cmdName,true);
3968 }
3971 DocLink *lnk = children().get_last<DocLink>();
3972 if (saveCmdName == "javalink")
3973 {
3975 parser()->context.nodeStack.size(),
3976 DocStyleChange::Code,cmdName,false);
3977 }
3978 DString leftOver = lnk->parse(isJavaLink);
3979 if (!leftOver.empty())
3980 {
3981 children().append<DocWord>(parser(),thisVariant(),leftOver);
3982 }
3983}
3984
3986{
3987 AUTO_TRACE("cmdName={}",cmdName);
3988 DString saveCmdName = cmdName;
3989 Token tok=parser()->tokenizer.lex();
3990 bool isBlock = false;
3991 bool trimLeft = false;
3992 bool localScope = false;
3993 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
3994 if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="{")
3995 {
3997 parser()->tokenizer.lex();
3999 StringVector optList=split(parser()->context.token->name.str(),",");
4000 auto contains = [&optList](const char *kw)
4001 {
4002 return std::find(optList.begin(),optList.end(),kw)!=optList.end();
4003 };
4004 localScope = contains("local");
4005 if (contains("nostrip"))
4006 {
4007 stripCodeComments = false;
4008 }
4009 else if (contains("strip"))
4010 {
4011 stripCodeComments = true;
4012 }
4013 if (t==DocInclude::Snippet && contains("trimleft"))
4014 {
4015 trimLeft = true;
4016 }
4017
4018 if (contains("lineno"))
4019 {
4023 }
4024 tok=parser()->tokenizer.lex();
4025 if (!tok.is(TokenRetval::TK_WHITESPACE))
4026 {
4027 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
4028 saveCmdName);
4029 return;
4030 }
4031 }
4032 else if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="[")
4033 {
4035 parser()->tokenizer.lex();
4036 isBlock = (parser()->context.token->name.stripWhiteSpace() == "block");
4038 parser()->tokenizer.lex();
4039 }
4040 else if (!tok.is(TokenRetval::TK_WHITESPACE))
4041 {
4042 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
4043 saveCmdName);
4044 return;
4045 }
4047 tok=parser()->tokenizer.lex();
4049 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4050 {
4051 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
4052 "argument of command {}",saveCmdName);
4053 return;
4054 }
4055 else if (!tok.is(TokenRetval::TK_WORD))
4056 {
4057 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
4058 tok.to_string(),saveCmdName);
4059 return;
4060 }
4061 DString fileName = parser()->context.token->name;
4062 DString blockId;
4064 {
4065 if (fileName == "this") fileName=parser()->context.fileName;
4067 tok=parser()->tokenizer.lex();
4069 if (!tok.is(TokenRetval::TK_WORD))
4070 {
4071 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected block identifier, but found token {} instead while parsing the {} command",
4072 tok.to_string(),saveCmdName);
4073 return;
4074 }
4075 blockId = "["+parser()->context.token->name+"]";
4076 }
4077
4079 thisVariant(),
4080 fileName,
4081 localScope ? parser()->context.context : "",
4082 t,
4083 stripCodeComments,
4086 blockId,isBlock,trimLeft);
4088}
4089
4090void DocPara::handleSection(char cmdChar,const DString &cmdName)
4091{
4092 AUTO_TRACE("cmdName={}",cmdName);
4093 DString saveCmdName = cmdName;
4094 // get the argument of the section command.
4095 Token tok=parser()->tokenizer.lex();
4096 if (!tok.is(TokenRetval::TK_WHITESPACE))
4097 {
4098 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
4099 cmdChar,saveCmdName);
4100 return;
4101 }
4102 tok=parser()->tokenizer.lex();
4103 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4104 {
4105 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
4106 "argument of command '{:c}{}'", cmdChar,saveCmdName);
4107 return;
4108 }
4109 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
4110 {
4111 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
4112 tok.to_string(),cmdChar,saveCmdName);
4113 return;
4114 }
4117 parser()->tokenizer.lex();
4119}
4120
4121Token DocPara::handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level)
4122{
4123 AUTO_TRACE();
4124 children().append<DocHtmlHeader>(parser(),thisVariant(),tagHtmlAttribs,level);
4125 Token retval = children().get_last<DocHtmlHeader>()->parse();
4126 return retval.is(TokenRetval::RetVal_OK) ? Token::make_TK_NEWPARA() : retval;
4127}
4128
4129// For XML tags whose content is stored in attributes rather than
4130// contained within the element, we need a way to inject the attribute
4131// text into the current paragraph.
4132bool DocPara::injectToken(Token tok,const DString &tokText)
4133{
4134 AUTO_TRACE();
4135 parser()->context.token->name = tokText;
4136 return parser()->defaultHandleToken(thisVariant(),tok,children());
4137}
4138
4140{
4141 AUTO_TRACE();
4142 Token retval = parser()->tokenizer.lex();
4143 DString lang = parser()->context.token->name;
4144 if (!lang.empty() && lang.at(0)!='.')
4145 {
4146 lang="."+lang;
4147 }
4148 if (parser()->context.xmlComment)
4149 {
4150 parser()->context.token->verb = substitute(substitute(parser()->context.token->verb,"&lt;","<"),"&gt;",">");
4151 }
4152 // search for the first non-whitespace line, index is stored in li
4153 size_t i=0,li=0,l=parser()->context.token->verb.length();
4154 while (i<l && (parser()->context.token->verb.at(i)==' ' || parser()->context.token->verb.at(i)=='\n'))
4155 {
4156 if (parser()->context.token->verb.at(i)=='\n') li=i+1;
4157 i++;
4158 }
4161 stripIndentation(parser()->context.token->verb.mid(li)),
4165 false,lang);
4166 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4167 {
4168 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"code section ended without end marker");
4169 }
4171 AUTO_TRACE_EXIT("retval={}",retval.to_string());
4172 return retval;
4173}
4174
4176{
4177 if (parser()->context.memberDef) // inheriting docs from a member
4178 {
4179 const MemberDef *reMd = parser()->context.memberDef->reimplements();
4180 if (reMd) // member from which was inherited.
4181 {
4182 const MemberDef *thisMd = parser()->context.memberDef;
4183 bool hasParamCommand = false;
4184 bool hasReturnCommand = false;
4185 StringMultiSet retvalsFound;
4186 StringMultiSet paramsFound;
4187 { DocParser::AutoSaveContext saveContext(*parser());
4188 //printf("{InheritDocs:%s=>%s}\n",qPrint(parser()->context.memberDef->qualifiedName()),qPrint(reMd->qualifiedName()));
4189 parser()->context.scope=reMd->getOuterScope();
4190 if (parser()->context.scope!=Doxygen::globalScope)
4191 {
4193 }
4194 parser()->context.memberDef=reMd;
4195 while (!parser()->context.styleStack.empty()) parser()->context.styleStack.pop();
4196 while (!parser()->context.nodeStack.empty()) parser()->context.nodeStack.pop();
4197 parser()->context.copyStack.push_back(reMd);
4200 parser()->context.copyStack.pop_back();
4201 hasParamCommand = parser()->context.hasParamCommand;
4202 hasReturnCommand = parser()->context.hasReturnCommand;
4203 retvalsFound = parser()->context.retvalsFound;
4204 paramsFound = parser()->context.paramsFound;
4205 }
4206 parser()->context.hasParamCommand = hasParamCommand;
4207 parser()->context.hasReturnCommand = hasReturnCommand;
4208 parser()->context.retvalsFound = retvalsFound;
4209 parser()->context.paramsFound = paramsFound;
4210 parser()->context.memberDef = thisMd;
4211 }
4212 }
4213}
4214
4215
4216Token DocPara::handleCommand(char cmdChar, const DString &cmdName)
4217{
4218 AUTO_TRACE("cmdName={}",cmdName);
4219 Token retval = Token::make_RetVal_OK();
4220 CommandType cmdId = Mappers::cmdMapper->map(cmdName);
4221 switch (cmdId)
4222 {
4224 {
4225 std::string str{cmdChar};
4226 children().append<DocWord>(parser(),thisVariant(),str.c_str() + cmdName);
4227 if (isAliasCmd(cmdName.view()))
4228 {
4229 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Found unexpanded alias '{:c}{}'. Check if number of arguments passed is correct.",cmdChar,cmdName);
4230 }
4231 else
4232 {
4233 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Found unknown command '{:c}{}'",cmdChar,cmdName);
4234 }
4235 }
4236 break;
4239 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4241 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4242 break;
4245 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4247 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4248 break;
4251 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4253 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4254 break;
4257 break;
4260 break;
4263 break;
4266 break;
4269 break;
4272 break;
4275 break;
4278 break;
4281 break;
4284 break;
4288 break;
4293 break;
4296 break;
4299 break;
4302 break;
4305 break;
4308 break;
4311 break;
4314 break;
4316 parser()->context.inSeeBlock=true;
4318 parser()->context.inSeeBlock=false;
4319 break;
4323 break;
4326 break;
4329 break;
4332 break;
4335 break;
4338 break;
4341 break;
4344 break;
4347 break;
4350 break;
4353 break;
4356 break;
4359 break;
4362 break;
4365 break;
4368 break;
4370 {
4372 retval = children().get_last<DocSimpleList>()->parse();
4373 }
4374 break;
4376 {
4377 handleSection(cmdChar,cmdName);
4378 retval = Token::make_RetVal_Section();
4379 }
4380 break;
4382 {
4383 handleSection(cmdChar,cmdName);
4384 retval = Token::make_RetVal_Subsection();
4385 }
4386 break;
4388 {
4389 handleSection(cmdChar,cmdName);
4390 retval = Token::make_RetVal_Subsubsection();
4391 }
4392 break;
4394 {
4395 handleSection(cmdChar,cmdName);
4396 retval = Token::make_RetVal_Paragraph();
4397 }
4398 break;
4400 {
4401 handleSection(cmdChar,cmdName);
4402 retval = Token::make_RetVal_SubParagraph();
4403 }
4404 break;
4406 {
4407 handleSection(cmdChar,cmdName);
4408 retval = Token::make_RetVal_SubSubParagraph();
4409 }
4410 break;
4412 {
4414 retval = handleStartCode();
4415 }
4416 break;
4418 {
4420 retval = handleStartCode();
4421 }
4422 break;
4424 {
4426 retval = parser()->tokenizer.lex();
4428 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4429 {
4430 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"htmlonly section ended without end marker");
4431 }
4433 }
4434 break;
4436 {
4438 retval = parser()->tokenizer.lex();
4440 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4441 {
4442 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"manonly section ended without end marker");
4443 }
4445 }
4446 break;
4448 {
4450 retval = parser()->tokenizer.lex();
4452 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4453 {
4454 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"rtfonly section ended without end marker");
4455 }
4457 }
4458 break;
4460 {
4462 retval = parser()->tokenizer.lex();
4464 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4465 {
4466 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"latexonly section ended without end marker");
4467 }
4469 }
4470 break;
4472 {
4474 retval = parser()->tokenizer.lex();
4476 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4477 {
4478 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"xmlonly section ended without end marker");
4479 }
4481 }
4482 break;
4484 {
4486 retval = parser()->tokenizer.lex();
4488 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4489 {
4490 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"docbookonly section ended without end marker");
4491 }
4493 }
4494 break;
4496 {
4499 parser()->tokenizer.lex();
4500
4501 DString fullMatch = parser()->context.token->verb;
4502 size_t idx = fullMatch.find('{');
4503 size_t idxEnd = idx!=DString::npos ? fullMatch.find("}",idx+1) : DString::npos;
4504 StringVector optList;
4505 if (idx!=DString::npos && idxEnd!=DString::npos) // options present
4506 {
4507 DString optStr = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4508 optList = split(optStr.str(),",");
4509 for (const auto &opt : optList)
4510 {
4511 if (opt.empty()) continue;
4512 DString locOpt(opt);
4513 locOpt = locOpt.stripWhiteSpace().lower();
4514 if (locOpt == "code")
4515 {
4517 }
4518 else if (!locOpt.empty())
4519 {
4520 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Unknown option '{}' for '\\iliteral'",opt);
4521 }
4522 }
4523 }
4524
4526 retval = parser()->tokenizer.lex();
4528 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4529 {
4530 if (t == DocVerbatim::JavaDocCode)
4531 {
4532 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"javadoc code section ended without end marker");
4533 }
4534 else
4535 {
4536 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"javadoc literal section ended without end marker");
4537 }
4538 }
4540 }
4541 break;
4544 {
4545 if (cmdId == CommandType::CMD_VERBATIM)
4546 {
4548 }
4549 else
4550 {
4552 }
4553 retval = parser()->tokenizer.lex();
4555 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4556 {
4557 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"verbatim section ended without end marker");
4558 }
4560 }
4561 break;
4563 {
4572 DString width,height;
4573 parser()->defaultHandleTitleAndSize(CommandType::CMD_DOT,&children().back(),dv->children(),width,height);
4575 retval = parser()->tokenizer.lex();
4576 dv->setText(parser()->context.token->verb);
4577 dv->setWidth(width);
4578 dv->setHeight(height);
4579 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4580 if (!Config_getBool(HAVE_DOT))
4581 {
4582 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"ignoring \\dot command because HAVE_DOT is not set");
4583 children().pop_back();
4584 }
4585 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4586 {
4587 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"dot section ended without end marker");
4588 }
4590 }
4591 break;
4593 {
4602 DString width,height;
4603 parser()->defaultHandleTitleAndSize(CommandType::CMD_MSC,&children().back(),dv->children(),width,height);
4605 retval = parser()->tokenizer.lex();
4606 dv->setText(parser()->context.token->verb);
4607 dv->setWidth(width);
4608 dv->setHeight(height);
4609 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4610 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4611 {
4612 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"msc section ended without end marker");
4613 }
4615 }
4616 break;
4618 {
4619 bool plantumlEnabled = PlantumlManager::isEnabled();
4621 parser()->tokenizer.lex();
4622 DString fullMatch = parser()->context.token->sectionId;
4623 DString sectionId = "";
4624 size_t idx = fullMatch.find('{');
4625 size_t idxEnd = idx!=DString::npos ? fullMatch.find("}",idx+1) : DString::npos;
4626 StringVector optList;
4627 DString engine;
4628 if (idx!=DString::npos && idxEnd!=DString::npos) // options present
4629 {
4630 DString optStr = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4631 optList = split(optStr.str(),",");
4632 for (const auto &opt : optList)
4633 {
4634 if (opt.empty()) continue;
4635 bool found = false;
4636 DString locOpt(opt);
4637 locOpt = locOpt.stripWhiteSpace().lower();
4638 if (g_plantumlEngine.find(locOpt.str())!=g_plantumlEngine.end())
4639 {
4640 if (!engine.empty())
4641 {
4642 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Multiple definition of engine for '\\startuml'");
4643 }
4644 engine = locOpt;
4645 found = true;
4646 }
4647 if (!found)
4648 {
4649 if (sectionId.empty())
4650 {
4651 sectionId = opt;
4652 }
4653 else
4654 {
4655 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Multiple use of filename for '\\startuml'");
4656 }
4657 }
4658 }
4659 }
4660 else
4661 {
4662 sectionId = parser()->context.token->sectionId;
4663 }
4664 if (engine.empty()) engine = "uml";
4665
4666 if (sectionId.empty())
4667 {
4669 retval = parser()->tokenizer.lex();
4670 ASSERT(retval.is(TokenRetval::RetVal_OK));
4671
4672 sectionId = parser()->context.token->sectionId;
4673 sectionId = sectionId.stripWhiteSpace();
4674 }
4675
4676 DString plantFile(sectionId);
4681 false,plantFile);
4683 dv->setEngine(engine);
4685 DString width,height;
4686 parser()->defaultHandleTitleAndSize(CommandType::CMD_STARTUML,&children().back(),dv->children(),width,height);
4688 retval = parser()->tokenizer.lex();
4689 int line = 0;
4690 DString trimmedVerb = stripLeadingAndTrailingEmptyLines(parser()->context.token->verb,line);
4691 if (engine == "ditaa")
4692 {
4693 dv->setUseBitmap(true);
4694 }
4695 else if (engine == "uml")
4696 {
4697 size_t i = trimmedVerb.find('\n');
4698 DString firstLine = i==DString::npos ? trimmedVerb : trimmedVerb.left(i);
4699 if (firstLine.stripWhiteSpace() == "ditaa") dv->setUseBitmap(true);
4700 }
4701 dv->setText(trimmedVerb);
4702 dv->setWidth(width);
4703 dv->setHeight(height);
4704 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4705 if (!plantumlEnabled)
4706 {
4707 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"ignoring \\startuml command because neither PLANTUML_JAR_PATH nor PLANTUML_TOOL is set");
4708 children().pop_back();
4709 }
4710 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4711 {
4712 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"startuml section ended without end marker");
4713 }
4715 }
4716 break;
4718 {
4720 parser()->tokenizer.lex();
4721 DString fullMatch = parser()->context.token->sectionId;
4722 DString sectionId = "";
4723 size_t idx = fullMatch.find('{');
4724 size_t idxEnd = idx!=DString::npos ? fullMatch.find("}",idx+1) : DString::npos;
4725 if (idx!=DString::npos && idxEnd!=DString::npos) // options present
4726 {
4727 sectionId = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4728 }
4729 else
4730 {
4731 sectionId = parser()->context.token->sectionId;
4732 }
4733
4734 if (sectionId.empty())
4735 {
4737 retval = parser()->tokenizer.lex();
4738 ASSERT(retval.is(TokenRetval::RetVal_OK));
4739
4740 sectionId = parser()->context.token->sectionId;
4741 sectionId = sectionId.stripWhiteSpace();
4742 }
4743
4744 DString mermaidFile(sectionId);
4749 false,mermaidFile);
4752 DString width,height;
4753 parser()->defaultHandleTitleAndSize(CommandType::CMD_MERMAID,&children().back(),dv->children(),width,height);
4755 retval = parser()->tokenizer.lex();
4756 int line = 0;
4757 DString trimmedVerb = stripLeadingAndTrailingEmptyLines(parser()->context.token->verb,line);
4758 dv->setText(trimmedVerb);
4759 dv->setWidth(width);
4760 dv->setHeight(height);
4761 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4762 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4763 {
4764 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"mermaid section ended without end marker");
4765 }
4767 }
4768 break;
4770 retval = Token::make_RetVal_EndParBlock();
4771 break;
4788 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4789 break;
4791 retval = handleParamSection(cmdName,DocParamSect::Param,false,parser()->context.token->paramDir);
4793 break;
4795 retval = handleParamSection(cmdName,DocParamSect::TemplateParam,false,parser()->context.token->paramDir);
4796 break;
4798 retval = handleParamSection(cmdName,DocParamSect::RetVal);
4799 break;
4802 break;
4804 retval = handleXRefItem();
4805 break;
4807 {
4809 }
4810 break;
4813 {
4815 }
4816 break;
4818 {
4820 }
4821 break;
4823 {
4827 retval = children().get_last<DocIndexEntry>()->parse();
4828 }
4829 break;
4831 retval = Token::make_RetVal_Internal();
4832 break;
4834 retval = Token::make_RetVal_EndInternal();
4835 break;
4837 {
4839 retval = children().get_last<DocParBlock>()->parse();
4840 }
4841 break;
4842 case CommandType::CMD_COPYDOC: // fall through
4843 case CommandType::CMD_COPYBRIEF: // fall through
4845 //retval = Token::make_RetVal_CopyDoc();
4846 // these commands should already be resolved by processCopyDoc()
4847 break;
4850 break;
4853 break;
4856 break;
4859 break;
4862 break;
4865 break;
4868 break;
4871 break;
4874 break;
4877 break;
4880 break;
4883 break;
4886 break;
4889 break;
4892 break;
4895 break;
4898 break;
4900 if (!Config_getBool(HAVE_DOT))
4901 {
4902 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
4903 "ignoring \\dotfile command because HAVE_DOT is not set");
4904 }
4905 else
4906 {
4907 handleFile<DocDotFile>(cmdName);
4908 }
4909 break;
4912 break;
4914 handleFile<DocMscFile>(cmdName);
4915 break;
4917 handleFile<DocDiaFile>(cmdName);
4918 break;
4921 break;
4924 break;
4926 handleLink(cmdName,false);
4927 break;
4929 handleLink(cmdName,true);
4930 break;
4932 {
4934 }
4935 break;
4937 handleEmoji(cmdChar,cmdName);
4938 break;
4940 handleDoxyConfig(cmdChar,cmdName);
4941 break;
4943 // fall through
4945 parser()->handleRef(thisVariant(),children(),cmdChar,cmdName);
4946 break;
4948 {
4951 }
4952 break;
4954 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4955 break;
4957 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4958 break;
4960 {
4962 }
4963 break;
4964 //case CommandType::CMD_LANGSWITCH:
4965 // retval = handleLanguageSwitch();
4966 // break;
4968 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4969 {
4972 }
4973 break;
4976 break;
4978 handleShowDate(cmdChar,cmdName);
4979 break;
4981 parser()->handleILine(cmdChar,cmdName);
4982 break;
4984 parser()->handleIFile(cmdChar,cmdName);
4985 break;
4987 {
4989 (void)parser()->tokenizer.lex();
4991 //printf("Found scope='%s'\n",qPrint(parser()->context.context));
4993 }
4994 break;
4995 default:
4996 // we should not get here!
4997 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' in paragraph context",cmdName);
4998 break;
4999 }
5000 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_OK,TokenRetval::RetVal_SimpleSec
5001 TokenRetval::TK_LISTITEM,TokenRetval::TK_ENDLIST,TokenRetval::TK_NEWPARA
5002 TokenRetval::RetVal_Section,TokenRetval::RetVal_EndList
5003 TokenRetval::RetVal_Internal,TokenRetval::RetVal_SwitchLang
5004 TokenRetval::RetVal_EndInternal)
5005 );
5006 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5007 return retval;
5008}
5009
5010static bool findAttribute(const HtmlAttribList &tagHtmlAttribs,
5011 const char *attrName,
5012 DString *result)
5013{
5014
5015 for (const auto &opt : tagHtmlAttribs)
5016 {
5017 if (opt.name==attrName)
5018 {
5019 *result = opt.value;
5020 return true;
5021 }
5022 }
5023 return false;
5024}
5025
5026Token DocPara::handleHtmlStartTag(const DString &tagName,const HtmlAttribList &tagHtmlAttribs)
5027{
5028 AUTO_TRACE("tagName={} #tagHtmlAttrs={}",tagName,tagHtmlAttribs.size());
5029 Token retval = Token::make_RetVal_OK();
5030 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
5031 if (parser()->context.token->emptyTag && !(tagId>HtmlTagType::XML_CmdMask) &&
5034 {
5035 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"HTML tag ('<{}/>') may not use the 'empty tag' XHTML syntax.",
5036 tagName);
5037 }
5038 switch (tagId)
5039 {
5041 if (!parser()->context.token->emptyTag)
5042 {
5044 tagHtmlAttribs,DocHtmlList::Unordered);
5045 retval=children().get_last<DocHtmlList>()->parse();
5046 }
5047 break;
5049 if (!parser()->context.token->emptyTag)
5050 {
5052 tagHtmlAttribs,DocHtmlList::Ordered);
5053 retval=children().get_last<DocHtmlList>()->parse();
5054 }
5055 break;
5057 if (parser()->context.token->emptyTag) break;
5059 {
5060 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <li> tag found");
5061 }
5062 else
5063 {
5064 retval = Token::make_RetVal_ListItem();
5065 }
5066 break;
5068 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Bold,tagName,&parser()->context.token->attribs);
5069 break;
5071 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::S,tagName,&parser()->context.token->attribs);
5072 break;
5074 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Strike,tagName,&parser()->context.token->attribs);
5075 break;
5077 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Del,tagName,&parser()->context.token->attribs);
5078 break;
5080 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Underline,tagName,&parser()->context.token->attribs);
5081 break;
5083 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Ins,tagName,&parser()->context.token->attribs);
5084 break;
5086 if (parser()->context.token->emptyTag) break;
5087 if (parser()->context.xmlComment)
5088 // for C# source or inside a <summary> or <remark> section we
5089 // treat <code> as an XML tag (so similar to @code)
5090 {
5092 retval = handleStartCode();
5093 }
5094 else // normal HTML markup
5095 {
5096 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
5097 }
5098 break;
5100 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Kbd,tagName,&parser()->context.token->attribs);
5101 break;
5103 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Typewriter,tagName,&parser()->context.token->attribs);
5104 break;
5106 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Italic,tagName,&parser()->context.token->attribs);
5107 break;
5109 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Div,tagName,&parser()->context.token->attribs);
5110 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Div,tagName);
5111 break;
5113 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Span,tagName,&parser()->context.token->attribs);
5114 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Span,tagName);
5115 break;
5117 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Subscript,tagName,&parser()->context.token->attribs);
5118 break;
5120 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Superscript,tagName,&parser()->context.token->attribs);
5121 break;
5123 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Center,tagName,&parser()->context.token->attribs);
5124 break;
5126 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Small,tagName,&parser()->context.token->attribs);
5127 break;
5129 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Cite,tagName,&parser()->context.token->attribs);
5130 break;
5132 if (parser()->context.token->emptyTag) break;
5133 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Preformatted,tagName,&parser()->context.token->attribs);
5135 parser()->tokenizer.setInsidePre(true);
5136 break;
5138 retval = Token::make_TK_NEWPARA();
5139 break;
5141 if (!parser()->context.token->emptyTag)
5142 {
5143 children().append<DocHtmlDescList>(parser(),thisVariant(),tagHtmlAttribs);
5144 retval=children().get_last<DocHtmlDescList>()->parse();
5145 }
5146 break;
5148 if (insideDL(thisVariant()))
5149 {
5150 retval = Token::make_RetVal_DescTitle();
5151 }
5152 else
5153 {
5154 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dt> found");
5155 }
5156 break;
5158 if (insideDL(thisVariant()))
5159 {
5160 retval = Token::make_RetVal_DescData();
5161 }
5162 else
5163 {
5164 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dd> found");
5165 }
5166 break;
5168 if (!parser()->context.token->emptyTag)
5169 {
5170 children().append<DocHtmlTable>(parser(),thisVariant(),tagHtmlAttribs);
5171 retval=children().get_last<DocHtmlTable>()->parse();
5172 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5173 }
5174 break;
5176 retval = Token::make_RetVal_TableRow();
5177 break;
5179 retval = Token::make_RetVal_TableCell();
5180 break;
5182 retval = Token::make_RetVal_TableHCell();
5183 break;
5187 // for time being ignore </t....> tag
5188 break;
5190 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <caption> found");
5191 break;
5193 {
5194 children().append<DocLineBreak>(parser(),thisVariant(),tagHtmlAttribs);
5195 }
5196 break;
5198 {
5199 children().append<DocHorRuler>(parser(),thisVariant(),tagHtmlAttribs);
5200 }
5201 break;
5203 retval = parser()->handleAHref(thisVariant(),children(),tagHtmlAttribs);
5204 break;
5206 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,1);
5207 break;
5209 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,2);
5210 break;
5212 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,3);
5213 break;
5215 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,4);
5216 break;
5218 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,5);
5219 break;
5221 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,6);
5222 break;
5224 {
5225 parser()->handleImg(thisVariant(),children(),tagHtmlAttribs);
5226 }
5227 break;
5229 if (!parser()->context.token->emptyTag)
5230 {
5231 children().append<DocHtmlDetails>(parser(),thisVariant(),tagHtmlAttribs);
5232 retval=children().get_last<DocHtmlDetails>()->parse();
5233 }
5234 break;
5236 if (!parser()->context.token->emptyTag)
5237 {
5238 children().append<DocHtmlBlockQuote>(parser(),thisVariant(),tagHtmlAttribs);
5239 retval = children().get_last<DocHtmlBlockQuote>()->parse();
5240 }
5241 break;
5242
5245 {
5246 if (!parser()->context.token->emptyTag)
5247 {
5249 while (n && !std::holds_alternative<DocHtmlDetails>(*n)) n=::parent(n);
5250 DocHtmlDetails *d = std::get_if<DocHtmlDetails>(n);
5251 if (d)
5252 {
5253 if (!d->summary()) // details section does not have a summary yet
5254 {
5255 d->parseSummary(n,parser()->context.token->attribs);
5256 }
5257 else
5258 {
5259 retval = Token::make_TK_NEWPARA();
5260 }
5261 }
5262 }
5263 }
5264 break;
5267 parser()->context.xmlComment=true;
5268 // fall through
5271 if (!children().empty())
5272 {
5273 retval = Token::make_TK_NEWPARA();
5274 }
5275 break;
5277 if (insideTable(thisVariant()))
5278 {
5279 retval = Token::make_RetVal_TableCell();
5280 }
5281 break;
5282 case HtmlTagType::XML_C:
5283 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
5284 break;
5287 {
5288 parser()->context.xmlComment=true;
5289 DString paramName;
5290 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5291 {
5292 if (paramName.empty())
5293 {
5294 if (Config_getBool(WARN_NO_PARAMDOC))
5295 {
5296 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"empty 'name' attribute for <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5297 }
5298 }
5299 else
5300 {
5301 retval = handleParamSection(paramName,
5303 true);
5304 }
5305 }
5306 else
5307 {
5308 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5309 }
5310 }
5311 break;
5314 {
5315 DString paramName;
5316 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5317 {
5318 //printf("paramName=%s\n",qPrint(paramName));
5320 children().append<DocWord>(parser(),thisVariant(),paramName);
5322 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
5323 }
5324 else
5325 {
5326 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}ref> tag.",tagId==HtmlTagType::XML_PARAMREF?"":"type");
5327 }
5328 }
5329 break;
5331 {
5332 parser()->context.xmlComment=true;
5333 DString exceptName;
5334 if (findAttribute(tagHtmlAttribs,"cref",&exceptName))
5335 {
5336 unescapeCRef(exceptName);
5337 retval = handleParamSection(exceptName,DocParamSect::Exception,true);
5338 }
5339 else
5340 {
5341 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <exception> tag.");
5342 }
5343 }
5344 break;
5347 if (insideTable(thisVariant()))
5348 {
5349 retval = Token::make_RetVal_TableRow();
5350 }
5351 else if (insideUL(thisVariant()) || insideOL(thisVariant()))
5352 {
5353 retval = Token::make_RetVal_ListItem();
5354 }
5355 else
5356 {
5357 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <item> tag found");
5358 }
5359 break;
5361 parser()->context.xmlComment=true;
5364 break;
5366 if (insideTable(thisVariant()))
5367 {
5368 retval = Token::make_RetVal_TableCell();
5369 }
5370 break;
5372 // I'm not sure if <see> is the same as <seealso> or if it
5373 // should you link a member without producing a section. The
5374 // C# specification is extremely vague about this (but what else
5375 // can we expect from Microsoft...)
5376 {
5377 DString cref;
5378 //printf("HtmlTagType::XML_SEE: empty tag=%d\n",parser()->context.token->emptyTag);
5379 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5380 {
5381 unescapeCRef(cref);
5382 if (parser()->context.token->emptyTag) // <see cref="..."/> style
5383 {
5384 bool inSeeBlock = parser()->context.inSeeBlock;
5385 parser()->context.token->name = cref;
5386 parser()->context.inSeeBlock = true;
5388 parser()->context.inSeeBlock = inSeeBlock;
5389 }
5390 else // <see cref="...">...</see> style
5391 {
5394 DocLink *lnk = children().get_last<DocLink>();
5395 DString leftOver = lnk->parse(false,true);
5396 if (!leftOver.empty())
5397 {
5398 children().append<DocWord>(parser(),thisVariant(),leftOver);
5399 }
5400 }
5401 }
5402 else if (findAttribute(tagHtmlAttribs,"langword",&cref)) // <see langword="..."/> or <see langword="..."></see>
5403 {
5404 bool inSeeBlock = parser()->context.inSeeBlock;
5405 parser()->context.token->name = cref;
5406 parser()->context.inSeeBlock = true;
5410 parser()->context.inSeeBlock = inSeeBlock;
5411 }
5412 else
5413 {
5414 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' or 'langword' attribute from <see> tag.");
5415 }
5416 }
5417 break;
5419 {
5420 parser()->context.xmlComment=true;
5421 DString cref;
5422 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5423 {
5424 unescapeCRef(cref);
5425 // Look for an existing "see" section
5426 DocNodeVariant *vss=nullptr;
5427 for (auto &n : children())
5428 {
5429 DocSimpleSect *candidate = std::get_if<DocSimpleSect>(&n);
5430 if (candidate && candidate->type()==DocSimpleSect::See)
5431 {
5432 vss = &n;
5433 }
5434 }
5435
5436 if (!vss) // start new section
5437 {
5439 vss = &children().back();
5440 }
5441
5442 std::get<DocSimpleSect>(*vss).appendLinkWord(cref);
5443 retval = Token::make_RetVal_OK();
5444 }
5445 else
5446 {
5447 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <seealso> tag.");
5448 }
5449 }
5450 break;
5452 {
5453 DString type;
5454 findAttribute(tagHtmlAttribs,"type",&type);
5456 HtmlAttribList emptyList;
5457 if (type=="number")
5458 {
5459 listType=DocHtmlList::Ordered;
5460 }
5461 if (type=="table")
5462 {
5463 children().append<DocHtmlTable>(parser(),thisVariant(),emptyList);
5464 retval=children().get_last<DocHtmlTable>()->parseXml();
5465 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5466 }
5467 else
5468 {
5469 children().append<DocHtmlList>(parser(),thisVariant(),emptyList,listType);
5470 retval=children().get_last<DocHtmlList>()->parseXml();
5471 }
5472 }
5473 break;
5476 // These tags are defined in .Net but are currently unsupported
5477 parser()->context.xmlComment=true;
5478 break;
5480 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag <{}> found", tagName);
5481 children().append<DocWord>(parser(),thisVariant(), "<"+tagName+parser()->context.token->attribsStr+">");
5482 break;
5485 break;
5486 default:
5487 // we should not get here!
5488 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected start tag {}",tagName);
5489 ASSERT(0);
5490 break;
5491 }
5492 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5493 return retval;
5494}
5495
5497{
5498 AUTO_TRACE("tagName={}",tagName);
5499 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
5500 Token retval = Token::make_RetVal_OK();
5501 switch (tagId)
5502 {
5504 if (!insideUL(thisVariant()))
5505 {
5506 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ul> tag without matching <ul>");
5507 }
5508 else
5509 {
5510 retval = Token::make_RetVal_EndList();
5511 }
5512 break;
5514 if (!insideOL(thisVariant()))
5515 {
5516 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ol> tag without matching <ol>");
5517 }
5518 else
5519 {
5520 retval = Token::make_RetVal_EndList();
5521 }
5522 break;
5524 if (!insideLI(thisVariant()))
5525 {
5526 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </li> tag without matching <li>");
5527 }
5528 else
5529 {
5530 // ignore </li> tags
5531 }
5532 break;
5534 if (!insideDetails(thisVariant()))
5535 {
5536 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </details> tag without matching <details>");
5537 }
5538 else
5539 {
5540 retval = Token::make_RetVal_EndHtmlDetails();
5541 }
5542 break;
5545 {
5546 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </blockquote> tag without matching <blockquote>");
5547 }
5548 else
5549 {
5550 retval = Token::make_RetVal_EndBlockQuote();
5551 }
5552 break;
5555 break;
5558 break;
5561 break;
5564 break;
5567 break;
5570 break;
5573 break;
5576 break;
5579 break;
5582 break;
5585 break;
5588 break;
5591 break;
5594 break;
5597 break;
5600 break;
5603 break;
5606 setInsidePreformatted(false);
5607 parser()->tokenizer.setInsidePre(false);
5608 break;
5610 retval = Token::make_TK_NEWPARA();
5611 break;
5613 retval = Token::make_RetVal_EndDesc();
5614 break;
5616 // ignore </dt> tag
5617 break;
5619 // ignore </dd> tag
5620 break;
5622 retval = Token::make_RetVal_EndTable();
5623 break;
5625 retval = Token::make_RetVal_EndTableRow();
5626 break;
5628 retval = Token::make_RetVal_EndTableCell();
5629 break;
5631 retval = Token::make_RetVal_EndTableCell();
5632 break;
5636 // for time being ignore </t....> tag
5637 break;
5639 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </caption> found");
5640 break;
5642 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </br> tag found");
5643 break;
5645 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h1> found");
5646 break;
5648 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h2> found");
5649 break;
5651 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h3> found");
5652 break;
5654 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h4> found");
5655 break;
5657 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h5> found");
5658 break;
5660 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h6> found");
5661 break;
5663 break;
5665 // warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </hr> tag found");
5666 break;
5668 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </a> found");
5669 // ignore </a> tag (can be part of <a name=...></a>
5670 break;
5671
5673 break;
5675 retval = Token::make_TK_NEWPARA();
5676 break;
5689 retval = Token::make_RetVal_CloseXml();
5690 break;
5691 case HtmlTagType::XML_C:
5693 break;
5701 // These tags are defined in .Net but are currently unsupported
5702 break;
5704 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag </{}> found", tagName);
5705 children().append<DocWord>(parser(),thisVariant(),"</"+tagName+">");
5706 break;
5707 default:
5708 // we should not get here!
5709 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end tag {}",tagName);
5710 ASSERT(0);
5711 break;
5712 }
5713 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5714 return retval;
5715}
5716
5718{
5719 // expected hierarchy:
5720 // 1. DocAutoListItem <- n
5721 // 2. DocAutoList <- parent(n)
5722 // 3. DocPara <- parent(parent(n))
5723
5724 // step 1
5725 if (!std::get_if<DocAutoListItem>(n)) // not inside a auto list item
5726 {
5727 return false;
5728 }
5729
5730 // step 2
5731 n = parent(n);
5732 int indent = 0;
5733 const auto &docAutoList = std::get_if<DocAutoList>(n);
5734 if (docAutoList) // capture indent
5735 {
5736 indent = docAutoList->indent();
5737 }
5738 else
5739 {
5740 return false;
5741 }
5742
5743 // step 3
5744 n = parent(n);
5745 const auto &docPara = std::get_if<DocPara>(n);
5746 if (docPara)
5747 {
5748 DString tagNameLower = DString(parser->context.token->name).lower();
5749 auto topStyleChange = [](const DocStyleChangeStack &stack) -> const DocStyleChange &
5750 {
5751 return std::get<DocStyleChange>(*stack.top());
5752 };
5753
5754 if (parser->context.styleStack.empty() || // no style change
5755 (topStyleChange(parser->context.styleStack).tagName()==tagNameLower && // correct style change
5756 topStyleChange(parser->context.styleStack).position()!=parser->context.nodeStack.size()) // wrong position, so normal close
5757 )
5758 {
5759 // insert an artificial 'end of autolist' marker and parse again
5760 DString indentStr;
5761 indentStr.fill(' ',indent);
5762 parser->tokenizer.unputString("\\ilinebr "+indentStr+".\\ilinebr"+indentStr+"</"+parser->context.token->name+">");
5763 return true;
5764 }
5765 }
5766 return false;
5767}
5768
5770{
5771 AUTO_TRACE();
5772 auto ns = AutoNodeStack(parser(),thisVariant());
5773 // handle style commands "inherited" from the previous paragraph
5775 Token tok=parser()->tokenizer.lex();
5776 Token retval = Token::make_TK_NONE();
5777 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
5778 {
5779reparsetoken:
5780 AUTO_TRACE_ADD("token '{}' at {}",tok.to_string(),parser()->tokenizer.getLineNr());
5781 if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL,
5782 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG)
5783 )
5784 {
5785 AUTO_TRACE_ADD("name={}",parser()->context.token->name);
5786 }
5787 switch(tok.value())
5788 {
5789 case TokenRetval::TK_WORD:
5791 break;
5792 case TokenRetval::TK_LNKWORD:
5794 break;
5795 case TokenRetval::TK_URL:
5797 break;
5798 case TokenRetval::TK_WHITESPACE:
5799 {
5800 // prevent leading whitespace and collapse multiple whitespace areas
5801 if (insidePRE(thisVariant()) || // all whitespace is relevant
5802 (
5803 // remove leading whitespace
5804 !children().empty() &&
5805 // and whitespace after certain constructs
5809 )
5810 )
5811 {
5813 }
5814 }
5815 break;
5816 case TokenRetval::TK_LISTITEM:
5817 {
5818 AUTO_TRACE_ADD("found list item at {}",parser()->context.token->indent);
5819 const DocNodeVariant *n=parent();
5820 while (n && !std::holds_alternative<DocAutoList>(*n)) n=::parent(n);
5821 const DocAutoList *al = std::get_if<DocAutoList>(n);
5822 if (al) // we found an auto list up in the hierarchy
5823 {
5824 AUTO_TRACE_ADD("previous list item at {}",al->indent());
5825 if (al->indent()>=parser()->context.token->indent)
5826 // new item at the same or lower indent level
5827 {
5828 retval = Token::make_TK_LISTITEM();
5829 goto endparagraph;
5830 }
5831 }
5832
5833 // determine list depth
5834 int depth = 0;
5835 n=parent();
5836 while (n)
5837 {
5838 al = std::get_if<DocAutoList>(n);
5839 if (al && al->isEnumList()) depth++;
5840 n=::parent(n);
5841 }
5842
5843 // first item or sub list => create new list
5844 do
5845 {
5848 parser()->context.token->isEnumList,depth,
5850 al = children().get_last<DocAutoList>();
5851 retval = children().get_last<DocAutoList>()->parse();
5852 } while (retval.is(TokenRetval::TK_LISTITEM) && // new list
5853 al->indent()==parser()->context.token->indent // at same indent level
5854 );
5855
5856 // check the return value
5857 if (retval.is(TokenRetval::RetVal_SimpleSec)) // auto list ended due to simple section command
5858 {
5859 // Reparse the token that ended the section at this level,
5860 // so a new simple section will be started at this level.
5861 // This is the same as unputting the last read token and continuing.
5863 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5864 {
5867 tok = Token::make_TK_RCSTAG();
5868 }
5869 else // other section
5870 {
5871 tok = Token::make_TK_COMMAND_BS();
5872 }
5873 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5874 goto reparsetoken;
5875 }
5876 else if (retval.is(TokenRetval::TK_ENDLIST))
5877 {
5878 if (al->indent()>parser()->context.token->indent) // end list
5879 {
5880 goto endparagraph;
5881 }
5882 else // continue with current paragraph
5883 {
5884 }
5885 }
5886 else // paragraph ended due to TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, or EOF
5887 {
5888 goto endparagraph;
5889 }
5890 }
5891 break;
5892 case TokenRetval::TK_ENDLIST:
5893 AUTO_TRACE_ADD("Found end of list inside of paragraph at line {}",parser()->tokenizer.getLineNr());
5894 if (std::get_if<DocAutoListItem>(parent()))
5895 {
5896 const DocAutoList *al = std::get_if<DocAutoList>(::parent(parent()));
5897 if (al && al->indent()>=parser()->context.token->indent)
5898 {
5899 // end of list marker ends this paragraph
5900 retval = Token::make_TK_ENDLIST();
5901 goto endparagraph;
5902 }
5903 else
5904 {
5905 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found "
5906 "has invalid indent level");
5907 }
5908 }
5909 else
5910 {
5911 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found without any preceding "
5912 "list items");
5913 }
5914 break;
5915 case TokenRetval::TK_COMMAND_AT:
5916 // fall through
5917 case TokenRetval::TK_COMMAND_BS:
5918 {
5919 // see if we have to start a simple section
5920 CommandType cmd = Mappers::cmdMapper->map(parser()->context.token->name);
5921 const DocNodeVariant *n=parent();
5922 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
5923 !std::holds_alternative<DocParamSect>(*n))
5924 {
5925 n=::parent(n);
5926 }
5928 {
5929 if (n) // already in a simple section
5930 {
5931 // simple section cannot start in this paragraph, need
5932 // to unwind the stack and remember the command.
5934 retval = Token::make_RetVal_SimpleSec();
5935 goto endparagraph;
5936 }
5937 }
5938 // see if we are in a simple list
5939 n=parent();
5940 while (n && !std::holds_alternative<DocSimpleListItem>(*n)) n=::parent(n);
5941 if (n)
5942 {
5943 if (cmd==CommandType::CMD_LI)
5944 {
5945 retval = Token::make_RetVal_ListItem();
5946 goto endparagraph;
5947 }
5948 }
5949
5950 // handle the command
5951 retval=handleCommand(tok.command_to_char(),parser()->context.token->name);
5952 AUTO_TRACE_ADD("handleCommand returns {}",retval.to_string());
5953
5954 // check the return value
5955 if (retval.is(TokenRetval::RetVal_SimpleSec))
5956 {
5957 // Reparse the token that ended the section at this level,
5958 // so a new simple section will be started at this level.
5959 // This is the same as unputting the last read token and continuing.
5961 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5962 {
5965 tok = Token::make_TK_RCSTAG();
5966 }
5967 else // other section
5968 {
5969 tok = Token::make_TK_COMMAND_BS();
5970 }
5971 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5972 goto reparsetoken;
5973 }
5974 else if (retval.value()>TokenRetval::TK_NONE && retval.value()<TokenRetval::RetVal_OK)
5975 {
5976 // the command ended with a new command, reparse this token
5977 tok = retval;
5978 goto reparsetoken;
5979 }
5980 else if (retval.value()!=TokenRetval::RetVal_OK) // end of file, end of paragraph, start or end of section
5981 // or some auto list marker
5982 {
5983 goto endparagraph;
5984 }
5985 }
5986 break;
5987 case TokenRetval::TK_HTMLTAG:
5988 {
5989 if (!parser()->context.token->endTag) // found a start tag
5990 {
5991 retval = handleHtmlStartTag(parser()->context.token->name,parser()->context.token->attribs);
5992 }
5993 else // found an end tag
5994 {
5996 {
5997 break; // new code has been pushed back to the scanner, need to reparse
5998 }
5999 retval = handleHtmlEndTag(parser()->context.token->name);
6000 }
6001 if (!retval.is(TokenRetval::RetVal_OK))
6002 {
6003 goto endparagraph;
6004 }
6005 }
6006 break;
6007 case TokenRetval::TK_SYMBOL:
6008 {
6009 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
6011 {
6013 }
6014 else
6015 {
6017 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
6018 parser()->context.token->name);
6019 }
6020 break;
6021 }
6022 case TokenRetval::TK_NEWPARA:
6023 retval = Token::make_TK_NEWPARA();
6024 goto endparagraph;
6025 case TokenRetval::TK_RCSTAG:
6026 {
6027 const DocNodeVariant *n=parent();
6028 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
6029 !std::holds_alternative<DocParamSect>(*n))
6030 {
6031 n=::parent(n);
6032 }
6033 if (n) // already in a simple section
6034 {
6035 // simple section cannot start in this paragraph, need
6036 // to unwind the stack and remember the command.
6039 retval = Token::make_RetVal_SimpleSec();
6040 goto endparagraph;
6041 }
6042
6043 // see if we are in a simple list
6045 children().get_last<DocSimpleSect>()->parseRcs();
6046 }
6047 break;
6048 default:
6049 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6050 "Found unexpected token (id={})",tok.to_string());
6051 break;
6052 }
6053 tok=parser()->tokenizer.lex();
6054 }
6055 retval=Token::make_TK_NONE();
6056endparagraph:
6058 DocPara *par = std::get_if<DocPara>(parser()->context.nodeStack.top());
6059 if (!parser()->context.token->endTag && par &&
6060 retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->name.lower() == "p")
6061 {
6062 par->setAttribs(parser()->context.token->attribs);
6063 }
6064 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM,
6065 TokenRetval::TK_ENDLIST,TokenRetval::RetVal_OK)
6066 );
6067
6068 AUTO_TRACE_EXIT("retval={}",retval.to_string());
6069 return retval;
6070}
6071
6072//--------------------------------------------------------------------------
6073
6075{
6076 AUTO_TRACE("start {} level={}", parser()->context.token->sectionId, m_level);
6077 Token retval = Token::make_RetVal_OK();
6078 auto ns = AutoNodeStack(parser(),thisVariant());
6079
6080 if (!m_id.empty())
6081 {
6083 if (sec)
6084 {
6085 m_file = sec->fileName();
6086 m_anchor = sec->label();
6087 DString titleStr = sec->title();
6088 if (titleStr.empty()) titleStr = sec->label();
6090 DocTitle *title = &std::get<DocTitle>(*m_title);
6091 title->parseFromString(thisVariant(),titleStr);
6092 }
6093 }
6094
6095 // first parse any number of paragraphs
6096 bool isFirst=true;
6097 DocPara *lastPar=nullptr;
6098 do
6099 {
6101 DocPara *par = children().get_last<DocPara>();
6102 if (isFirst) { par->markFirst(); isFirst=false; }
6103 retval=par->parse();
6104 if (!par->empty())
6105 {
6106 if (lastPar) lastPar->markLast(false);
6107 lastPar = par;
6108 }
6109 else
6110 {
6111 children().pop_back();
6112 }
6113 if (retval.is(TokenRetval::TK_LISTITEM))
6114 {
6115 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6116 }
6117 if (retval.is(TokenRetval::RetVal_Internal))
6118 {
6120 retval = children().get_last<DocInternal>()->parse(m_level+1);
6121 if (retval.is(TokenRetval::RetVal_EndInternal))
6122 {
6123 retval = Token::make_RetVal_OK();
6124 }
6125 }
6126 } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6127 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph,
6128 TokenRetval::RetVal_SubSubParagraph, TokenRetval::RetVal_EndInternal)
6129 );
6130
6131 if (lastPar) lastPar->markLast();
6132
6133 while (true)
6134 {
6135 if (retval.is(TokenRetval::RetVal_Subsection) && m_level<=1)
6136 {
6137 // then parse any number of nested sections
6138 while (retval.is(TokenRetval::RetVal_Subsection)) // more sections follow
6139 {
6141 2,
6143 retval = children().get_last<DocSection>()->parse();
6144 }
6145 break;
6146 }
6147 else if (retval.is(TokenRetval::RetVal_Subsubsection) && m_level<=2)
6148 {
6149 if ((m_level <= 1) &&
6150 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6151 {
6152 warn_doc_error(parser()->context.fileName,
6153 parser()->tokenizer.getLineNr(),
6154 "Unexpected subsubsection command found inside {}!",
6156 }
6157 // then parse any number of nested sections
6158 while (retval.is(TokenRetval::RetVal_Subsubsection)) // more sections follow
6159 {
6161 3,
6163 retval = children().get_last<DocSection>()->parse();
6164 }
6165 if (!(m_level < 2 && retval.is(TokenRetval::RetVal_Subsection))) break;
6166 }
6167 else if (retval.is(TokenRetval::RetVal_Paragraph) && m_level<=3)
6168 {
6169 if ((m_level <= 2) &&
6170 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6171 {
6172 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6173 "Unexpected paragraph command found inside {}!",
6175 }
6176 // then parse any number of nested sections
6177 while (retval.is(TokenRetval::RetVal_Paragraph)) // more sections follow
6178 {
6180 4,
6182 retval = children().get_last<DocSection>()->parse();
6183 }
6184 if (!(m_level<3 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection)))) break;
6185 }
6186 else if (retval.is(TokenRetval::RetVal_SubParagraph) && m_level<=4)
6187 {
6188 if ((m_level <= 3) &&
6189 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6190 {
6191 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6192 "Unexpected subparagraph command found inside {}!",
6194 }
6195 // then parse any number of nested sections
6196 while (retval.is(TokenRetval::RetVal_SubParagraph)) // more sections follow
6197 {
6199 5,
6201 retval = children().get_last<DocSection>()->parse();
6202 }
6203 if (!(m_level<4 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection,TokenRetval::RetVal_Paragraph)))) break;
6204 }
6205 else if (retval.is(TokenRetval::RetVal_SubSubParagraph) && m_level<=5)
6206 {
6207 if ((m_level <= 4) &&
6208 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6209 {
6210 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6211 "Unexpected subsubparagraph command found inside {}!",
6213 }
6214 // then parse any number of nested sections
6215 while (retval.is(TokenRetval::RetVal_SubSubParagraph)) // more sections follow
6216 {
6218 6,
6220 retval = children().get_last<DocSection>()->parse();
6221 }
6222 if (!(m_level<5 && (retval.is_any_of( TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection,
6223 TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph)))) break;
6224 }
6225 else
6226 {
6227 break;
6228 }
6229 }
6230
6231 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF,
6232 TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6233 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph,
6234 TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph,
6235 TokenRetval::RetVal_Internal, TokenRetval::RetVal_EndInternal)
6236 );
6237
6238 AUTO_TRACE_EXIT("retval={}", retval.to_string());
6239 return retval;
6240}
6241
6242//--------------------------------------------------------------------------
6243
6245{
6246 AUTO_TRACE();
6247 auto ns = AutoNodeStack(parser(),thisVariant());
6249
6250 Token tok = parser()->tokenizer.lex();
6251 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
6252 {
6253 switch(tok.value())
6254 {
6255 case TokenRetval::TK_WORD:
6257 break;
6258 case TokenRetval::TK_WHITESPACE:
6260 break;
6261 case TokenRetval::TK_SYMBOL:
6262 {
6263 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
6265 {
6267 }
6268 else
6269 {
6270 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
6271 parser()->context.token->name);
6272 }
6273 }
6274 break;
6275 case TokenRetval::TK_COMMAND_AT:
6276 // fall through
6277 case TokenRetval::TK_COMMAND_BS:
6278 switch (Mappers::cmdMapper->map(parser()->context.token->name))
6279 {
6282 break;
6285 break;
6288 break;
6291 break;
6294 break;
6297 break;
6300 break;
6303 break;
6306 break;
6310 break;
6315 break;
6318 break;
6321 break;
6324 break;
6327 break;
6330 break;
6333 break;
6336 break;
6337 default:
6338 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' found",
6339 parser()->context.token->name);
6340 break;
6341 }
6342 break;
6343 default:
6344 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",
6345 tok.to_string());
6346 break;
6347 }
6348 tok = parser()->tokenizer.lex();
6349 }
6350
6352
6353}
6354
6355
6356//--------------------------------------------------------------------------
6357
6359{
6360 AUTO_TRACE();
6361 auto ns = AutoNodeStack(parser(),thisVariant());
6363 Token retval = Token::make_TK_NONE();
6364
6365 // first parse any number of paragraphs
6366 bool isFirst=true;
6367 DocPara *lastPar = nullptr;
6368 do
6369 {
6370 {
6372 DocPara *par = children().get_last<DocPara>();
6373 if (isFirst) { par->markFirst(); isFirst=false; }
6374 retval=par->parse();
6375 if (par->empty() && par->attribs().empty())
6376 {
6377 children().pop_back();
6378 }
6379 else
6380 {
6381 lastPar = par;
6382 }
6383 }
6384 auto checkParagraph = [this,&retval](Token t,int level,const char *sectionType,const char *parentSectionType) {
6385 if (retval == t)
6386 {
6387 if (!AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6388 {
6389 warn_doc_error(parser()->context.fileName,
6390 parser()->tokenizer.getLineNr(),
6391 "found {} command (id: '{}') outside of {} context!",
6392 sectionType,parser()->context.token->sectionId,parentSectionType);
6393 }
6394 while (retval==t)
6395 {
6396 if (!parser()->context.token->sectionId.empty())
6397 {
6398 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6399 if (sec)
6400 {
6402 level,
6404 retval = children().get_last<DocSection>()->parse();
6405 }
6406 else
6407 {
6408 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid {} id '{}'; ignoring {}",
6409 sectionType,parser()->context.token->sectionId,sectionType);
6410 retval = Token::make_TK_NONE();
6411 }
6412 }
6413 else
6414 {
6415 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for {}; ignoring {}",sectionType,sectionType);
6416 retval = Token::make_TK_NONE();
6417 }
6418 }
6419 }
6420 };
6421 checkParagraph(Token::make_RetVal_SubSubParagraph(), 6, "subsubparagraph", "subparagraph" );
6422 checkParagraph(Token::make_RetVal_SubParagraph(), 5, "subparagraph", "paragraph" );
6423 checkParagraph(Token::make_RetVal_Paragraph(), 4, "paragraph", "subsubsection" );
6424 checkParagraph(Token::make_RetVal_Subsubsection(), 3, "subsubsection", "subsection" );
6425 checkParagraph(Token::make_RetVal_Subsection(), 2, "subsection", "section" );
6426
6427 if (retval.is(TokenRetval::TK_LISTITEM))
6428 {
6429 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6430 }
6431 if (retval.is(TokenRetval::RetVal_Internal))
6432 {
6434 retval = children().get_last<DocInternal>()->parse(1);
6435 }
6436 } while (!retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_Section));
6437 if (lastPar) lastPar->markLast();
6438
6439 //printf("DocRoot::parse() retval=%d %d\n",retval,TokenRetval::RetVal_Section);
6440 // then parse any number of level1 sections
6441 while (retval.is(TokenRetval::RetVal_Section))
6442 {
6443 if (!parser()->context.token->sectionId.empty())
6444 {
6445 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6446 if (sec)
6447 {
6449 1,
6451 retval = children().get_last<DocSection>()->parse();
6452 }
6453 else
6454 {
6455 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid section id '{}'; ignoring section",parser()->context.token->sectionId);
6456 retval = Token::make_TK_NONE();
6457 }
6458 }
6459 else
6460 {
6461 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for section; ignoring section");
6462 retval = Token::make_TK_NONE();
6463 }
6464 }
6465
6467}
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:216
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:3146
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:3139
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:3099
DString getText() const
Definition docnode.cpp:979
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:943
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:1215
DocDiaFile(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.cpp:1208
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:1137
DocDotFile(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.cpp:1130
Node representing an emoji.
Definition docnode.h:341
DocEmoji(DocParser *parser, DocNodeVariant *parent, const DString &symName)
Definition docnode.cpp:167
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:521
Token parse()
Definition docnode.cpp:1574
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:1781
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:2064
void setColumnIndex(uint32_t idx)
Definition docnode.h:1222
bool isFirst() const
Definition docnode.h:1206
Token parseXml()
Definition docnode.cpp:1968
void setRowIndex(uint32_t idx)
Definition docnode.h:1221
uint32_t rowSpan() const
Definition docnode.cpp:2002
void markLast(bool v=true)
Definition docnode.h:1209
void markFirst(bool v=true)
Definition docnode.h:1208
Alignment alignment() const
Definition docnode.cpp:2026
bool isHeading() const
Definition docnode.h:1205
const HtmlAttribList & attribs() const
Definition docnode.h:1210
Token parse()
Definition docnode.cpp:1934
uint32_t colSpan() const
Definition docnode.cpp:2014
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:1564
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:1384
Node representing a Html list.
Definition docnode.h:1009
Type m_type
Definition docnode.h:1020
Token parseXml()
Definition docnode.cpp:2946
Token parse()
Definition docnode.cpp:2871
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:2268
void setVisibleCells(uint32_t n)
Definition docnode.h:1261
bool isHeading() const
Definition docnode.cpp:2086
void setRowIndex(uint32_t idx)
Definition docnode.h:1266
Token parse()
Definition docnode.cpp:2161
Node Html summary.
Definition docnode.h:853
Node representing a HTML table.
Definition docnode.h:1274
Token parseXml()
Definition docnode.cpp:2447
size_t numberHeaderRows() const
Definition docnode.cpp:2339
std::unique_ptr< DocNodeVariant > m_caption
Definition docnode.h:1289
Token parse()
Definition docnode.cpp:2354
void computeTableGrid()
determines the location of all cells in a grid, resolving row and column spans.
Definition docnode.cpp:2504
size_t m_numCols
Definition docnode.h:1291
const DocNodeVariant * caption() const
Definition docnode.cpp:2334
bool hasCaption() const
Definition docnode.cpp:2329
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:1361
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:1376
bool isSVG() const
Definition docnode.cpp:1367
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:275
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:1684
DString m_entry
Definition docnode.h:562
Node representing an internal section of documentation.
Definition docnode.h:978
Token parse(int)
Definition docnode.cpp:1624
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:679
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:199
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:1291
DocMscFile(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.cpp:1168
bool parse()
Definition docnode.cpp:1175
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:855
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:3040
Node representing a paragraph in the documentation tree.
Definition docnode.h:1089
void handleFile(const DString &cmdName)
Definition docnode.cpp:3905
void handleInheritDoc()
Definition docnode.cpp:4175
void handleShowDate(char cmdChar, const DString &cmdName)
Definition docnode.cpp:3775
DocPara(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:3565
void handleIncludeOperator(const DString &cmdName, DocIncOperator::Type t)
Definition docnode.cpp:3845
void handleLink(const DString &cmdName, bool isJavaLink)
Definition docnode.cpp:3944
Token handleCommand(char cmdChar, const DString &cmdName)
Definition docnode.cpp:4216
void handleInclude(const DString &cmdName, DocInclude::Type t)
Definition docnode.cpp:3985
Token handleHtmlEndTag(const DString &tagName)
Definition docnode.cpp:5496
Token handleHtmlStartTag(const DString &tagName, const HtmlAttribList &tagHtmlAttribs)
Definition docnode.cpp:5026
void markFirst(bool v=true)
Definition docnode.h:1094
Token handleSimpleSection(DocSimpleSect::Type t, bool xmlContext=false)
Definition docnode.cpp:3571
void handleEmoji(char cmdChar, const DString &cmdName)
Definition docnode.cpp:3624
bool isFirst() const
Definition docnode.h:1096
Token handleParamSection(const DString &cmdName, DocParamSect::Type t, bool xmlContext, int direction)
Definition docnode.cpp:3601
void setAttribs(const HtmlAttribList &attribs)
Definition docnode.h:1120
void handleSection(char cmdChar, const DString &cmdName)
Definition docnode.cpp:4090
bool empty() const
Definition docnode.h:1093
bool m_isFirst
Definition docnode.h:1123
Token parse()
Definition docnode.cpp:5769
void markLast(bool v=true)
Definition docnode.h:1095
void handleVhdlFlow()
Definition docnode.cpp:3937
void handleDoxyConfig(char cmdChar, const DString &cmdName)
Definition docnode.cpp:3655
bool injectToken(Token tok, const DString &tokText)
Definition docnode.cpp:4132
Token handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs, int level)
Definition docnode.cpp:4121
bool m_isLast
Definition docnode.h:1124
Token handleXRefItem()
Definition docnode.cpp:3753
Token handleStartCode()
Definition docnode.cpp:4139
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:3373
DocParamSect::Type m_type
Definition docnode.h:1150
Token parseXml(const DString &paramName)
Definition docnode.cpp:3454
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:3522
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:1247
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:896
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:714
void parse()
Definition docnode.cpp:6358
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:540
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:6074
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:3084
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:3065
Node representing a simple section.
Definition docnode.h:1026
DString typeString() const
Definition docnode.cpp:3344
Type type() const
Definition docnode.h:1035
Token parse(bool userTitle, bool needsSeparator)
Definition docnode.cpp:3234
Token parseRcs()
Definition docnode.cpp:3271
DocSimpleSect(DocParser *parser, DocNodeVariant *parent, Type t)
Definition docnode.cpp:3224
const DocNodeVariant * title() const
Definition docnode.h:1042
Token parseXml()
Definition docnode.cpp:3288
void appendLinkWord(const DString &word)
Definition docnode.cpp:3324
bool hasTitle() const
Definition docnode.cpp:3229
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:132
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:160
void parse()
Definition docnode.cpp:6244
Node representing a simple section title.
Definition docnode.h:608
void parseFromString(DocNodeVariant *, const DString &title)
Definition docnode.cpp:3211
void parse()
Definition docnode.cpp:3193
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:265
Node representing a VHDL flow chart.
Definition docnode.h:758
DocVhdlFlow(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:1331
void parse()
Definition docnode.cpp:1335
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:187
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:480
DString m_key
Definition docnode.h:633
DString relPath() const
Definition docnode.h:627
bool parse()
Definition docnode.cpp:485
DString m_file
Definition docnode.h:634
static FileNameLinkedMap * plantUmlFileNameLinkedMap
Definition doxygen.h:102
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
#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:864
static Token skipSpacesForTable(DocParser *parser)
Definition docnode.cpp:2101
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:55
static bool findAttribute(const HtmlAttribList &tagHtmlAttribs, const char *attrName, DString *result)
Definition docnode.cpp:5010
std::vector< ActiveRowSpan > RowSpanList
List of ActiveRowSpan classes.
Definition docnode.cpp:2498
static void setParent(DocNodeVariant *n, DocNodeVariant *newParent)
Definition docnode.cpp:125
static void unescapeCRef(DString &s)
Definition docnode.cpp:89
static bool checkIfHtmlEndTagEndsAutoList(DocParser *parser, const DocNodeVariant *n)
Definition docnode.cpp:5717
static DString stripKnownExtensions(const DString &text)
Definition docnode.cpp:111
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:1973
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:2493
uint32_t column
Definition docnode.cpp:2494
ActiveRowSpan(uint32_t rows, uint32_t col)
Definition docnode.cpp:2492
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:3987
bool resolveLink(const DString &scName, const DString &lr, bool, const Definition **resContext, DString &resAnchor, SrcLangExt lang, const DString &prefix)
Definition util.cpp:2308
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2863
DString stripScope(const DString &name)
Definition util.cpp:3109
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4168
DString linkToText(SrcLangExt lang, const DString &link, bool ignoreDots)
Definition util.cpp:2265
DString stripIndentation(const DString &s, bool skipFirstLine)
Definition util.cpp:4683
A bunch of utility functions.