Doxygen
Loading...
Searching...
No Matches
docnode.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2022 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16#include "docnode.h"
17#include "docparser_p.h"
18#include "htmlentity.h"
19#include "configimpl.h"
20#include "configoptions.h"
21#include "emoji.h"
22#include "message.h"
23#include "doxygen.h"
24#include "cite.h"
25#include "util.h"
26#include "stringutil.h"
27#include "formula.h"
28#include "markdown.h"
29#include "pagedef.h"
30#include "namespacedef.h"
31#include "groupdef.h"
32#include "cmdmapper.h"
33#include "config.h"
34#include "vhdldocgen.h"
35#include "doctokenizer.h"
36#include "plantuml.h"
37#include "mermaid.h"
38#include "language.h"
39#include "datetime.h"
40#include "trace.h"
41#include "anchor.h"
42#include "aliases.h"
43#include "requirement.h"
44#include "filename.h"
45#include "filedef.h"
46
47#if !ENABLE_DOCPARSER_TRACING
48#undef AUTO_TRACE
49#undef AUTO_TRACE_ADD
50#undef AUTO_TRACE_EXIT
51#define AUTO_TRACE(...) (void)0
52#define AUTO_TRACE_ADD(...) (void)0
53#define AUTO_TRACE_EXIT(...) (void)0
54#endif
55
56#define INTERNAL_ASSERT(x) do {} while(0)
57//#define INTERNAL_ASSERT(x) if (!(x)) TRACE("INTERNAL_ASSERT({}) failed retval={:#x}: file={} line={}",#x,retval,__FILE__,__LINE__)
58
59//---------------------------------------------------------------------------
60
61static const char *g_sectionLevelToName[] =
62{
63 "page",
64 "section",
65 "subsection",
66 "subsubsection",
67 "paragraph",
68 "subparagraph"
69};
70
71
72//---------------------------------------------------------------------------
73
75 "uml", "bpm", "wire", "dot", "ditaa",
76 "salt", "math", "latex", "gantt", "mindmap",
77 "wbs", "yaml", "creole", "json", "flow",
78 "board", "git", "hcl", "regex", "ebnf",
79 "files", "chen", "chronology", "chart", "nwdiag",
80 "packetdiag", "project", "sprites"
81};
82
83//---------------------------------------------------------------------------
84
85// replaces { with < and } with > and also
86// replaces &gt; with < and &gt; with > within string s
87static void unescapeCRef(DString &s)
88{
89 DString result;
90 const char *p = s.data();
91 if (p)
92 {
93 char c = 0;
94 while ((c=*p++))
95 {
96 if (c=='{') c='<'; else if (c=='}') c='>';
97 result+=c;
98 }
99 }
100
101 result=substitute(result,"&lt;","<");
102 result=substitute(result,"&gt;",">");
103 s = result;
104}
105
106//---------------------------------------------------------------------------
107
108/*! Strips known html and tex extensions from \a text. */
110{
111 DString result=text;
112 if (result.endsWith(".tex"))
113 {
114 result=result.left(result.length()-4);
115 }
117 {
118 result=result.left(result.length()-Doxygen::htmlFileExtension.length());
119 }
120 return result;
121}
122
123static void setParent(DocNodeVariant *n,DocNodeVariant *newParent)
124{
125 std::visit([&](auto &&x)->decltype(auto) { return x.setParent(newParent); }, *n);
126}
127
128//----------- DocStyleChange
129
131{
132 switch (m_style)
133 {
134 case DocStyleChange::Bold: return "b";
135 case DocStyleChange::Italic: return "em";
136 case DocStyleChange::Code: return "code";
137 case DocStyleChange::Center: return "center";
138 case DocStyleChange::Small: return "small";
139 case DocStyleChange::Cite: return "cite";
140 case DocStyleChange::Subscript: return "subscript";
141 case DocStyleChange::Superscript: return "superscript";
142 case DocStyleChange::Preformatted: return "pre";
143 case DocStyleChange::Div: return "div";
144 case DocStyleChange::Span: return "span";
145 case DocStyleChange::Strike: return "strike";
146 case DocStyleChange::S: return "s";
147 case DocStyleChange::Del: return "del";
148 case DocStyleChange::Underline: return "u";
149 case DocStyleChange::Ins: return "ins";
150 case DocStyleChange::Kbd: return "kbd";
151 case DocStyleChange::Typewriter: return "tt";
152 }
153 return "<invalid>";
154}
155
156//----------- DocSymbol
157
162
163//----------- DocEmoji
164
166 DocNode(parser,parent), m_symName(symName), m_index(-1)
167{
168 DString locSymName = symName;
169 size_t len=locSymName.length();
170 if (len>0)
171 {
172 if (locSymName.at(len-1)!=':') locSymName.append(":");
173 if (locSymName.at(0)!=':') locSymName.prepend(":");
174 }
175 m_symName = locSymName;
177 if (m_index==-1)
178 {
179 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Found unsupported emoji symbol '{}'",m_symName);
180 }
181}
182
183//---------------------------------------------------------------------------
184
187{
188 //printf("new word %s url=%s\n",qPrint(word),qPrint(parser->context.searchUrl));
190 {
191 Doxygen::searchIndex.addWord(word,false);
192 }
193}
194
195//---------------------------------------------------------------------------
196
198 const DString &ref,const DString &file,
199 const DString &anchor,const DString &tooltip) :
203{
204 //printf("DocLinkedWord: new word %s url=%s tooltip='%s'\n",
205 // qPrint(word),qPrint(parser->context.searchUrl),qPrint(tooltip));
207 {
208 Doxygen::searchIndex.addWord(word,false);
209 }
210}
211
212//---------------------------------------------------------------------------
213
215{
216 if (id.empty())
217 {
219 return;
220 }
221
223 DString anchorPrefix = ct.anchorPrefix();
224 if (id.left(anchorPrefix.length()) == anchorPrefix)
225 {
226 const CiteInfo *cite = ct.find(id.mid(anchorPrefix.length()));
227 if (cite)
228 {
229 m_file = convertNameToFile(ct.fileName(),false,true);
230 m_anchor = id;
231 }
232 else
233 {
234 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid cite anchor id '{}'",id);
235 m_anchor = "invalid";
236 m_file = "invalid";
237 }
238 }
239 else if (newAnchor) // found <a name="label">
240 {
241 m_anchor = id;
242 }
243 else // found \anchor label
244 {
245 const SectionInfo *sec = SectionManager::instance().find(id);
246 if (sec)
247 {
248 //printf("Found anchor %s\n",qPrint(id));
249 m_file = sec->fileName();
250 m_anchor = sec->label();
251 }
252 else
253 {
254 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid anchor id '{}'",id);
255 m_anchor = "invalid";
256 m_file = "invalid";
257 }
258 }
259}
260
261//---------------------------------------------------------------------------
262
269
270
271//---------------------------------------------------------------------------
272
274{
275 AUTO_TRACE("file={} text={}",m_file,Trace::trunc(m_text));
276 switch(m_type)
277 {
278 case DontIncWithLines:
279 // fall through
280 case IncWithLines:
281 // fall through
282 case Include:
283 // fall through
284 case DontInclude:
293 //printf("parser->context.includeFile=<<%s>>\n",qPrint(parser->context.includeFileText));
294 break;
295 case VerbInclude:
296 // fall through
297 case HtmlInclude:
298 case LatexInclude:
304 break;
305 case Snippet:
306 case SnippetWithLines:
308 // check here for the existence of the blockId inside the file, so we
309 // only generate the warning once.
310 int count = 0;
311 if (!m_blockId.empty() && (count=m_text.contains(m_blockId.data()))!=2)
312 {
313 warn_doc_error(parser()->context.fileName,
314 parser()->tokenizer.getLineNr(),
315 "block marked with {} for \\snippet should appear twice in file {}, found it {:d} times",
316 m_blockId,m_file,count);
317 }
318 break;
319 }
320}
321
322//---------------------------------------------------------------------------
323
325{
326 if (parser()->context.includeFileName.empty())
327 {
328 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
329 "No previous '\\include' or '\\dontinclude' command for '\\{}' present",
330 typeAsString());
331 }
332 bool found = false;
333
335 const char *p = parser()->context.includeFileText.data();
336 size_t l = parser()->context.includeFileLength;
337 size_t o = parser()->context.includeFileOffset;
338 int il = parser()->context.includeFileLine;
339 AUTO_TRACE("text={} off={} len={}",Trace::trunc(p),o,l);
340 size_t so = o, bo = 0;
341 bool nonEmpty = false;
342 switch(type())
343 {
344 case Line:
345 while (o<l)
346 {
347 char c = p[o];
348 if (c=='\n')
349 {
351 if (nonEmpty) break; // we have a pattern to match
352 so=o+1; // no pattern, skip empty line
353 }
354 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
355 {
356 nonEmpty=true;
357 }
358 o++;
359 }
360 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
361 {
362 m_line = il;
364 found = true;
365 AUTO_TRACE_ADD("\\line {}",Trace::trunc(m_text));
366 }
367 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
370 break;
371 case SkipLine:
372 while (o<l)
373 {
374 so=o;
375 while (o<l)
376 {
377 char c = p[o];
378 if (c=='\n')
379 {
381 if (nonEmpty) break; // we have a pattern to match
382 so=o+1; // no pattern, skip empty line
383 }
384 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
385 {
386 nonEmpty=true;
387 }
388 o++;
389 }
390 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
391 {
392 m_line = il;
394 found = true;
395 AUTO_TRACE_ADD("\\skipline {}",Trace::trunc(m_text));
396 break;
397 }
398 o++; // skip new line
399 }
400 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
403 break;
404 case Skip:
405 while (o<l)
406 {
407 so=o;
408 while (o<l)
409 {
410 char c = p[o];
411 if (c=='\n')
412 {
414 if (nonEmpty) break; // we have a pattern to match
415 so=o+1; // no pattern, skip empty line
416 }
417 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
418 {
419 nonEmpty=true;
420 }
421 o++;
422 }
423 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
424 {
425 found = true;
426 break;
427 }
428 o++; // skip new line
429 }
430 parser()->context.includeFileOffset = so; // set pointer to start of new line
433 break;
434 case Until:
435 bo=o;
436 while (o<l)
437 {
438 so=o;
439 while (o<l)
440 {
441 char c = p[o];
442 if (c=='\n')
443 {
445 if (nonEmpty) break; // we have a pattern to match
446 so=o+1; // no pattern, skip empty line
447 }
448 else if (!isspace(static_cast<uint8_t>(c))) // no white space char
449 {
450 nonEmpty=true;
451 }
452 o++;
453 }
454 if (parser()->context.includeFileText.mid(so,o-so).find(m_pattern)!=DString::npos)
455 {
456 m_line = il;
458 found = true;
459 AUTO_TRACE_ADD("\\until {}",Trace::trunc(m_text));
460 break;
461 }
462 o++; // skip new line
463 }
464 parser()->context.includeFileOffset = std::min(l,o+1); // set pointer to start of new line
467 break;
468 }
469 if (!found)
470 {
471 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
472 "referenced pattern '{}' for command '\\{}' not found",m_pattern,typeAsString());
473 }
474}
475
476//---------------------------------------------------------------------------
477
482
484{
486 if (refList && refList->isEnabled())
487 {
488 RefItem *item = refList->find(m_id);
489 ASSERT(item!=nullptr);
490 if (item)
491 {
492 if (parser()->context.memberDef && parser()->context.memberDef->name().at(0)=='@')
493 {
494 m_file = "@"; // can't cross reference anonymous enum
495 m_anchor = "@";
496 }
497 else
498 {
499 m_file = refList->fileName();
500 m_anchor = item->anchor();
501 }
502 m_title = refList->sectionTitle();
503 //printf("DocXRefItem: file=%s anchor=%s title=%s\n",
504 // qPrint(m_file),qPrint(m_anchor),qPrint(m_title));
505
506 if (!item->text().empty())
507 {
508 DocParser::AutoSaveContext saveContext(*parser());
510 }
511 }
512 return true;
513 }
514 return false;
515}
516
517//---------------------------------------------------------------------------
518
520 m_relPath(parser->context.relPath)
521{
522 const Formula *formula = FormulaManager::instance().findFormula(id);
523 if (formula && !formula->text().empty())
524 {
525 m_id = id;
526 m_name.sprintf("form_%d",m_id);
527 m_text = formula->text();
528 }
529 else // wrong \_form#<n> command
530 {
531 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Wrong formula id {:d}",id);
532 m_id = -1;
533 }
534}
535
536//---------------------------------------------------------------------------
537
542
544{
545 AUTO_TRACE();
546 auto ns = AutoNodeStack(parser(),thisVariant());
547
549 Token tok = parser()->tokenizer.lex();
550 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
551 {
552 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
553 {
554 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"\\refitem");
555 }
556 tok = parser()->tokenizer.lex();
557 }
560
561 if (!m_target.empty())
562 {
564 if (sec==nullptr && parser()->context.lang==SrcLangExt::Markdown) // lookup as markdown file
565 {
567 }
568 if (sec) // ref to section or anchor
569 {
570 // set defaults
571 m_ref = sec->ref();
574 m_anchor = sec->label();
575 m_isSubPage = false;
576 // adjust if needed
577 switch (sec->type().level())
578 {
580 {
582 m_isSubPage = pd && pd->hasParentPage();
583 if (!m_isSubPage)
584 {
585 m_anchor="";
586 }
587 }
588 break;
591 break;
594 break;
597 break;
598 default:
599 break;
600 }
601 //printf("m_ref=%s,m_file=%s,type=%d\n",
602 // qPrint(m_ref),qPrint(m_file),m_refType);
603 }
604 else
605 {
606 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to unknown section {}",m_target);
607 }
608 }
609 else
610 {
611 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"reference to empty target");
612 }
613}
614
615//---------------------------------------------------------------------------
616
618{
619 AUTO_TRACE();
620 auto ns = AutoNodeStack(parser(),thisVariant());
621
622 Token tok=parser()->tokenizer.lex();
623 // skip white space
624 while (tok.is_any_of(TokenRetval::TK_WHITESPACE, TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
625 // handle items
626 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
627 {
628 if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS))
629 {
630 switch (Mappers::cmdMapper->map(parser()->context.token->name))
631 {
633 {
634 tok=parser()->tokenizer.lex();
635 if (!tok.is(TokenRetval::TK_WHITESPACE))
636 {
637 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\refitem command");
638 break;
639 }
640 tok=parser()->tokenizer.lex();
641 if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
642 {
643 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\refitem",
644 tok.to_string());
645 break;
646 }
647
650 }
651 break;
653 return;
654 default:
655 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' as part of a \\secreflist",
656 tok.command_to_char(),qPrint(parser()->context.token->name));
657 return;
658 }
659 }
660 else if (tok.is(TokenRetval::TK_WHITESPACE))
661 {
662 // ignore whitespace
663 }
664 else
665 {
666 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {} inside section reference list",
667 tok.to_string());
668 return;
669 }
670 tok=parser()->tokenizer.lex();
671 }
672
673}
674
675//---------------------------------------------------------------------------
676
679{
680 size_t i=ref.find('#');
681 if (i!=DString::npos)
682 {
683 m_anchor = ref.mid(i+1);
684 m_file = ref.left(i);
685 }
686 else
687 {
688 m_file = ref;
689 }
690}
691
693{
694 AUTO_TRACE();
695 auto ns = AutoNodeStack(parser(),thisVariant());
696
697 Token tok = parser()->tokenizer.lex();
698 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
699 {
700 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
701 {
703 }
704 tok=parser()->tokenizer.lex();
705 }
706
708}
709
710//---------------------------------------------------------------------------
711
714{
715 const Definition *compound = nullptr;
717 AUTO_TRACE("target='{}',context='{}'",target,context);
718 ASSERT(!target.empty());
720 auto lang = parser->context.lang;
722 if (sec==nullptr && !parser->context.prefix.empty())
723 {
724 sec = SectionManager::instance().find(target);
725 }
726
727 if (sec==nullptr && getLanguageFromFileName(target)==SrcLangExt::Markdown) // lookup as markdown file
728 {
730 }
731 if (sec) // ref to section or anchor
732 {
733 PageDef *pd = nullptr;
734 int secLevel = sec->type().level();
735 if (secLevel==SectionType::Page)
736 {
737 pd = Doxygen::pageLinkedMap->find(target);
738 }
739 m_text = sec->title();
740 if (m_text.empty()) m_text = sec->label();
741
742 m_ref = sec->ref();
744 if (secLevel==SectionType::Anchor)
745 {
747 }
748 else if (secLevel==SectionType::Table)
749 {
751 }
752 else if (secLevel==SectionType::Requirement)
753 {
755 if (!Config_getBool(GENERATE_REQUIREMENTS))
756 {
757 m_file.clear(); // prevent link when there is no requirements page
758 }
759 }
760 else
761 {
763 }
764 m_isSubPage = pd && pd->hasParentPage();
765 if (secLevel!=SectionType::Page || m_isSubPage)
766 {
767 m_anchor = pd ? pd->getOutputFileBase() : sec->label();
768 }
769 m_sectionType = sec->type();
770 //printf("m_text=%s,m_ref=%s,m_file=%s,type=%d\n",
771 // qPrint(m_text),qPrint(m_ref),qPrint(m_file),m_refType);
772 AUTO_TRACE_EXIT("section");
773 return;
774 }
775 else if (Config_getBool(IMPLICIT_DIR_DOCS) && target.lower().endsWith("/readme.md"))
776 {
777 DString dirTarget = target.left(target.length() - 9); // strip readme.md part
778 const auto &dd = Doxygen::dirLinkedMap->find(dirTarget);
779 if (dd)
780 {
781 m_text = target;
782 m_file = dd->getOutputFileBase();
783 AUTO_TRACE_EXIT("directory");
784 return;
785 }
786 }
787 else if (resolveLink(context,target,true,&compound,anchor,lang,parser->context.prefix))
788 {
789 bool isFile = compound ?
790 (compound->definitionType()==Definition::TypeFile ||
791 compound->definitionType()==Definition::TypePage ? true : false) :
792 false;
793 bool isDir = compound && compound->definitionType()==Definition::TypeDir;
794 if (compound && lang==SrcLangExt::Markdown) lang = compound->getLanguage();
795 m_text = linkToText(lang,target,isFile || isDir);
797 if (compound && compound->isLinkable()) // ref to compound
798 {
799 if (anchor.empty() && /* compound link */
800 compound->definitionType()==Definition::TypeGroup && /* is group */
801 !toGroupDef(compound)->groupTitle().empty() /* with title */
802 )
803 {
804 m_text=(toGroupDef(compound))->groupTitle(); // use group's title as link
805 }
806 else if (compound->definitionType()==Definition::TypeMember &&
807 toMemberDef(compound)->isObjCMethod())
808 {
809 // Objective C Method
810 const MemberDef *member = toMemberDef(compound);
811 bool localLink = parser->context.memberDef ? member->getClassDef()==parser->context.memberDef->getClassDef() : false;
812 m_text = member->objCMethodName(localLink,parser->context.inSeeBlock);
813 }
814 else if (Config_getBool(HIDE_SCOPE_NAMES))
815 {
816 size_t funcPos = m_text.find('(');
817 if (funcPos!=DString::npos) // see issue #11834
818 {
819 m_text=stripScope(m_text.left(funcPos))+m_text.mid(funcPos);
820 }
821 else
822 {
824 }
825 }
826
827 m_file = compound->getOutputFileBase();
828 m_ref = compound->getReference();
829 //printf("isFile=%d compound=%s (%d)\n",isFile,qPrint(compound->name()),
830 // compound->definitionType());
831 AUTO_TRACE_EXIT("compound");
832 return;
833 }
834 else if (compound && compound->definitionType()==Definition::TypeFile &&
835 toFileDef(compound)->generateSourceFile()
836 ) // undocumented file that has source code we can link to
837 {
838 m_file = compound->getSourceFileBase();
839 m_ref = compound->getReference();
840 AUTO_TRACE_EXIT("source");
841 return;
842 }
843 else
844 {
845 AUTO_TRACE_EXIT("compound '{}' not linkable",compound?compound->name():"none");
846 }
847 }
848 m_text = target;
849 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve reference to '{}' for \\ref command",
850 target);
851}
852
854{
855 for (auto &&elem : elements)
856 {
857 emplace_back(std::move(elem));
858 }
859 elements.clear();
860}
861
862static void flattenParagraphs(DocNodeVariant *root,DocNodeList &children)
863{
864 DocNodeList newChildren;
865 for (auto &dn : children)
866 {
867 DocPara *para = std::get_if<DocPara>(&dn);
868 if (para)
869 {
870 //// move the children of the paragraph to the end of the newChildren list
871 newChildren.move_append(para->children());
872 }
873 }
874
875 // replace the children list by the newChildren list
876 children.clear();
877 children.move_append(newChildren);
878 // reparent the children
879 for (auto &cn : children)
880 {
881 setParent(&cn,root);
882 // we also need to set the parent for each child of cn, as cn's address may have changed.
883 auto opt_children = call_method_children(&cn);
884 if (opt_children)
885 {
886 for (auto &ccn : *opt_children)
887 {
888 setParent(&ccn,&cn);
889 }
890 }
891 }
892}
893
894void DocRef::parse(char cmdChar,const DString &cmdName)
895{
896 AUTO_TRACE();
897 auto ns = AutoNodeStack(parser(),thisVariant());
898 char cmdCharStr[2] = { cmdChar, 0 };
899
900 Token tok = parser()->tokenizer.lex();
901 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
902 {
903 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
904 {
905 switch (tok.value())
906 {
907 case TokenRetval::TK_HTMLTAG:
908 break;
909 default:
910 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),cmdCharStr+cmdName);
911 break;
912 }
913 }
914 tok=parser()->tokenizer.lex();
915 }
916
917 if (children().empty() && !m_text.empty())
918 {
919 DString text = m_text;
920 if (parser()->context.insideHtmlLink)
921 {
922 // we already in a link/title only output anchor
923 text = m_anchor;
924 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
925 "Potential recursion while resolving {:c}{} command!",cmdChar,cmdName);
926 }
928 { DocParser::AutoSaveContext saveContext(*parser());
930 }
934 }
935
937}
938
939//---------------------------------------------------------------------------
940
942{
943 size_t numBibFiles = Config_getList(CITE_BIB_FILES).size();
944 //printf("DocCite::DocCite(target=%s)\n",qPrint(target));
945 ASSERT(!target.empty());
948 const CiteInfo *cite = ct.find(target);
949 //printf("cite=%p text='%s' numBibFiles=%d\n",cite,cite?qPrint(cite->text):"<null>",numBibFiles);
950 m_option = opt;
952 if (numBibFiles>0 && cite && !cite->text().empty()) // ref to citation
953 {
954 m_ref = "";
955 m_anchor = ct.anchorPrefix()+cite->label();
956 m_file = convertNameToFile(ct.fileName(),false,true);
957 //printf("CITE ==> m_text=%s,m_ref=%s,m_file=%s,m_anchor=%s\n",
958 // qPrint(m_text),qPrint(m_ref),qPrint(m_file),qPrint(m_anchor));
959 return;
960 }
961 if (numBibFiles==0)
962 {
963 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"\\cite command found but no bib files specified via CITE_BIB_FILES!");
964 }
965 else if (cite==nullptr)
966 {
967 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve reference to '{}' for \\cite command",
968 target);
969 }
970 else
971 {
972 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"\\cite command to '{}' does not have an associated number",
973 target);
974 }
975}
976
978{
979 DString txt;
980 auto opt = m_option;
982 const CiteInfo *citeInfo = ct.find(m_target);
983
984 if (!opt.noPar()) txt += "[";
985
986 if (citeInfo)
987 {
988 if (opt.isNumber()) txt += citeInfo->text();
989 else if (opt.isShortAuthor()) txt += citeInfo->shortAuthor();
990 else if (opt.isYear()) txt += citeInfo->year();
991 }
992
993 if (!opt.noPar()) txt += "]";
994 return txt;
995}
996
997
998//---------------------------------------------------------------------------
999
1001{
1002 const Definition *compound = nullptr;
1004 m_refText = target;
1006 if (!m_refText.empty() && m_refText.at(0)=='#')
1007 {
1008 m_refText = m_refText.mid(1);
1009 }
1011 parser->context.inSeeBlock,&compound,anchor,
1013 {
1014 m_anchor = anchor;
1015 if (compound && compound->isLinkable())
1016 {
1017 m_file = compound->getOutputFileBase();
1018 m_ref = compound->getReference();
1019 }
1020 else if (compound && compound->definitionType()==Definition::TypeFile &&
1021 (toFileDef(compound))->generateSourceFile()
1022 ) // undocumented file that has source code we can link to
1023 {
1024 m_file = compound->getSourceFileBase();
1025 m_ref = compound->getReference();
1026 }
1027 return;
1028 }
1029
1030 // bogus link target
1031 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"unable to resolve link to '{}' for \\link command",
1032 target);
1033}
1034
1035
1036DString DocLink::parse(bool isJavaLink,bool isXmlLink)
1037{
1038 AUTO_TRACE();
1039 DString result;
1040 auto ns = AutoNodeStack(parser(),thisVariant());
1041
1042 Token tok = parser()->tokenizer.lex();
1043 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1044 {
1045 if (!parser()->defaultHandleToken(thisVariant(),tok,children(),false))
1046 {
1047 switch (tok.value())
1048 {
1049 case TokenRetval::TK_COMMAND_AT:
1050 // fall through
1051 case TokenRetval::TK_COMMAND_BS:
1052 switch (Mappers::cmdMapper->map(parser()->context.token->name))
1053 {
1055 if (isJavaLink)
1056 {
1057 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"'{{@link...' ended with '{:c}{}' command",
1058 tok.command_to_char(),parser()->context.token->name);
1059 }
1060 goto endlink;
1061 default:
1062 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' as part of a \\link",
1063 tok.command_to_char(),parser()->context.token->name);
1064 break;
1065 }
1066 break;
1067 case TokenRetval::TK_SYMBOL:
1068 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a \\link",
1069 parser()->context.token->name);
1070 break;
1071 case TokenRetval::TK_HTMLTAG:
1072 if (parser()->context.token->name!="see" || !isXmlLink)
1073 {
1074 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected xml/html command {} found as part of a \\link",
1075 parser()->context.token->name);
1076 }
1077 goto endlink;
1078 case TokenRetval::TK_LNKWORD:
1079 case TokenRetval::TK_WORD:
1080 if (isJavaLink) // special case to detect closing }
1081 {
1083 size_t p = 0;
1084 if (w=="}")
1085 {
1086 goto endlink;
1087 }
1088 else if ((p=w.find('}'))!=DString::npos)
1089 {
1090 size_t l = w.length();
1092 if (l>1 && p<l-1) // something left after the } (for instance a .)
1093 {
1094 result=w.right(l-p-1);
1095 }
1096 goto endlink;
1097 }
1098 }
1100 break;
1101 default:
1102 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",tok.to_string());
1103 break;
1104 }
1105 }
1106 tok = parser()->tokenizer.lex();
1107 }
1108 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1109 {
1110 warn_doc_error(parser()->context.fileName,
1111 parser()->tokenizer.getLineNr(),
1112 "Unexpected end of comment while inside link command");
1113 }
1114endlink:
1115
1116 if (children().empty()) // no link text
1117 {
1119 }
1120
1122 return result;
1123}
1124
1125
1126//---------------------------------------------------------------------------
1127
1134
1136{
1137 bool ok = false;
1139
1140 bool ambig = false;
1142 if (fd==nullptr && !p->name.endsWith(".dot")) // try with .dot extension as well
1143 {
1144 fd = Doxygen::dotFileNameLinkedMap->findFileDef(p->name+".dot",ambig);
1145 }
1146 if (fd)
1147 {
1148 p->file = fd->absFilePath();
1149 ok = true;
1150 if (ambig)
1151 {
1152 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dot file name '{}' is ambiguous.\n"
1153 "Possible candidates:\n{}",p->name,
1155 );
1156 }
1157 }
1158 else
1159 {
1160 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dot file '{}' is not found "
1161 "in any of the paths specified via DOTFILE_DIRS!",p->name);
1162 }
1163 return ok;
1164}
1165
1172
1174{
1175 bool ok = false;
1177
1178 bool ambig = false;
1180 if (fd==nullptr && !p->name.endsWith(".msc")) // try with .msc extension as well
1181 {
1182 fd = Doxygen::mscFileNameLinkedMap->findFileDef(p->name+".msc",ambig);
1183 }
1184 if (fd)
1185 {
1186 p->file = fd->absFilePath();
1187 ok = true;
1188 if (ambig)
1189 {
1190 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included msc file name '{}' is ambiguous.\n"
1191 "Possible candidates:\n{}",p->name,
1193 );
1194 }
1195 }
1196 else
1197 {
1198 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included msc file '{}' is not found "
1199 "in any of the paths specified via MSCFILE_DIRS!",p->name);
1200 }
1201 return ok;
1202}
1203
1204//---------------------------------------------------------------------------
1205
1212
1214{
1215 bool ok = false;
1217
1218 bool ambig = false;
1220 if (fd==nullptr && !p->name.endsWith(".dia")) // try with .dia extension as well
1221 {
1222 fd = Doxygen::diaFileNameLinkedMap->findFileDef(p->name+".dia",ambig);
1223 }
1224 if (fd)
1225 {
1226 p->file = fd->absFilePath();
1227 ok = true;
1228 if (ambig)
1229 {
1230 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dia file name '{}' is ambiguous.\n"
1231 "Possible candidates:\n{}",p->name,
1233 );
1234 }
1235 }
1236 else
1237 {
1238 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included dia file '{}' is not found "
1239 "in any of the paths specified via DIAFILE_DIRS!",p->name);
1240 }
1241 return ok;
1242}
1243//---------------------------------------------------------------------------
1244
1251
1253{
1254 bool ok = false;
1256
1257 bool ambig = false;
1259 if (fd==nullptr && !p->name.endsWith(".puml")) // try with .puml extension as well
1260 {
1261 fd = Doxygen::plantUmlFileNameLinkedMap->findFileDef(p->name+".puml",ambig);
1262 if (fd==nullptr && !p->name.endsWith(".pu")) // try with .pu extension as well
1263 {
1264 fd = Doxygen::plantUmlFileNameLinkedMap->findFileDef(p->name+".pu",ambig);
1265 }
1266 }
1267 if (fd)
1268 {
1269 p->file = fd->absFilePath();
1270 ok = true;
1271 if (ambig)
1272 {
1273 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included uml file name '{}' is ambiguous.\n"
1274 "Possible candidates:\n{}",p->name,
1276 );
1277 }
1278 }
1279 else
1280 {
1281 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included uml file '{}' is not found "
1282 "in any of the paths specified via PLANTUMLFILE_DIRS!",p->name);
1283 }
1284 return ok;
1285}
1286
1287//---------------------------------------------------------------------------
1288
1295
1297{
1298 bool ok = false;
1300
1301 bool ambig = false;
1303 if (fd==nullptr && !p->name.endsWith(".mmd")) // try with .mmd extension as well
1304 {
1305 fd = Doxygen::mermaidFileNameLinkedMap->findFileDef(p->name+".mmd",ambig);
1306 }
1307 if (fd)
1308 {
1309 p->file = fd->absFilePath();
1310 ok = true;
1311 if (ambig)
1312 {
1313 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included mermaid file name '{}' is ambiguous.\n"
1314 "Possible candidates:\n{}",p->name,
1316 );
1317 }
1318 }
1319 else
1320 {
1321 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"included mermaid file '{}' is not found "
1322 "in any of the paths specified via MERMAIDFILE_DIRS!",p->name);
1323 }
1324 return ok;
1325}
1326
1327//---------------------------------------------------------------------------
1328
1332
1334{
1335 AUTO_TRACE();
1336 auto ns = AutoNodeStack(parser(),thisVariant());
1337
1339 Token tok = parser()->tokenizer.lex();
1340 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1341 {
1342 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1343 {
1344 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"\\vhdlflow");
1345 }
1346 tok = parser()->tokenizer.lex();
1347 }
1348 parser()->tokenizer.lex();
1349
1352
1353 VhdlDocGen::createFlowChart(parser()->context.memberDef);
1354}
1355
1356
1357//---------------------------------------------------------------------------
1358
1360 Type t,const DString &url, bool inlineImage) :
1361 DocCompoundNode(parser,parent), p(std::make_unique<Private>(attribs, name, t, parser->context.relPath, url, inlineImage))
1362{
1363}
1364
1366{
1367 DString locName = p->url.empty() ? p->name : p->url;
1368 size_t len = locName.length();
1369 size_t fnd = locName.find('?'); // ignore part from ? until end
1370 if (fnd==DString::npos) fnd=len;
1371 return fnd>=4 && locName.mid(fnd-4,4)==".svg";
1372}
1373
1378
1379
1380//---------------------------------------------------------------------------
1381
1383{
1384 AUTO_TRACE();
1385 Token retval(TokenRetval::RetVal_OK);
1386 auto ns = AutoNodeStack(parser(),thisVariant());
1387
1388 Token tok = parser()->tokenizer.lex();
1389 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1390 {
1391 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1392 {
1393 switch (tok.value())
1394 {
1395 case TokenRetval::TK_HTMLTAG:
1396 {
1397 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1398 if (tagId==HtmlTagType::HTML_H1 && parser()->context.token->endTag) // found </h1> tag
1399 {
1400 if (m_level!=1)
1401 {
1402 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h1>",
1403 m_level);
1404 }
1405 goto endheader;
1406 }
1407 else if (tagId==HtmlTagType::HTML_H2 && parser()->context.token->endTag) // found </h2> tag
1408 {
1409 if (m_level!=2)
1410 {
1411 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h2>",
1412 m_level);
1413 }
1414 goto endheader;
1415 }
1416 else if (tagId==HtmlTagType::HTML_H3 && parser()->context.token->endTag) // found </h3> tag
1417 {
1418 if (m_level!=3)
1419 {
1420 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h3>",
1421 m_level);
1422 }
1423 goto endheader;
1424 }
1425 else if (tagId==HtmlTagType::HTML_H4 && parser()->context.token->endTag) // found </h4> tag
1426 {
1427 if (m_level!=4)
1428 {
1429 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h4>",
1430 m_level);
1431 }
1432 goto endheader;
1433 }
1434 else if (tagId==HtmlTagType::HTML_H5 && parser()->context.token->endTag) // found </h5> tag
1435 {
1436 if (m_level!=5)
1437 {
1438 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h5>",
1439 m_level);
1440 }
1441 goto endheader;
1442 }
1443 else if (tagId==HtmlTagType::HTML_H6 && parser()->context.token->endTag) // found </h6> tag
1444 {
1445 if (m_level!=6)
1446 {
1447 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"<h{:d}> ended with </h6>",
1448 m_level);
1449 }
1450 goto endheader;
1451 }
1452 else if (tagId==HtmlTagType::HTML_A)
1453 {
1454 if (!parser()->context.token->endTag)
1455 {
1456 parser()->handleAHref(thisVariant(),children(),parser()->context.token->attribs);
1457 }
1458 }
1459 else if (tagId==HtmlTagType::HTML_BR)
1460 {
1462 }
1463 else
1464 {
1465 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <h{:d}> context",
1466 parser()->context.token->endTag?"/":"",parser()->context.token->name,m_level);
1467 }
1468 }
1469 break;
1470 default:
1471 char tmp[20];
1472 snprintf(tmp,20,"<h%d> tag",m_level);
1474 }
1475 }
1476 tok = parser()->tokenizer.lex();
1477 }
1478 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1479 {
1480 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1481 " <h{:d}> tag",m_level);
1482 }
1483endheader:
1485 return retval;
1486}
1487//---------------------------------------------------------------------------
1488
1490{
1491 AUTO_TRACE();
1492 auto ns = AutoNodeStack(parser(),thisVariant());
1494 Token tok = parser()->tokenizer.lex();
1495 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1496 {
1497 // check of </summary>
1498 if (tok.value()==TokenRetval::TK_HTMLTAG &&
1499 (Mappers::htmlTagMapper->map(parser()->context.token->name))==HtmlTagType::XML_SUMMARY &&
1500 parser()->context.token->endTag
1501 )
1502 {
1503 break;
1504 }
1505 else if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS) &&
1506 (Mappers::cmdMapper->map(parser()->context.token->name)== CommandType::CMD_REF))
1507 {
1509 tok.command_to_char(),parser()->context.token->name);
1510 }
1511 else if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1512 {
1513 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"summary section");
1514 }
1515 tok = parser()->tokenizer.lex();
1516 }
1518 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1519 {
1520 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1521 " <summary> tag");
1522 }
1523}
1524
1525//---------------------------------------------------------------------------
1526
1528{
1529 AUTO_TRACE();
1530 Token retval(TokenRetval::TK_NONE);
1531 auto ns = AutoNodeStack(parser(),thisVariant());
1532
1533 // parse one or more paragraphs
1534 bool isFirst=true;
1535 DocPara *par=nullptr;
1536 do
1537 {
1539 par = children().get_last<DocPara>();
1540 if (isFirst) { par->markFirst(); isFirst=false; }
1541 retval=par->parse();
1542 }
1543 while (retval.is(TokenRetval::TK_NEWPARA));
1544 if (par) par->markLast();
1545
1546 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1547 {
1548 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <details> block");
1549 }
1550
1551 if (!summary())
1552 {
1553 HtmlAttribList summaryAttribs;
1555 DocHtmlSummary *summary = &std::get<DocHtmlSummary>(*m_summary);
1557 }
1558 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1559 return retval.is(TokenRetval::RetVal_EndHtmlDetails) ? Token::make_RetVal_OK() : retval;
1560}
1561
1569
1570//---------------------------------------------------------------------------
1571
1573{
1574 AUTO_TRACE();
1575 Token retval(TokenRetval::RetVal_OK);
1576 auto ns = AutoNodeStack(parser(),thisVariant());
1577
1578 Token tok = parser()->tokenizer.lex();
1579 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1580 {
1581 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1582 {
1583 switch (tok.value())
1584 {
1585 case TokenRetval::TK_HTMLTAG:
1586 {
1587 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1588 if (tagId==HtmlTagType::HTML_A && parser()->context.token->endTag) // found </a> tag
1589 {
1590 goto endhref;
1591 }
1592 else if (tagId==HtmlTagType::HTML_BR)
1593 {
1595 }
1596 else
1597 {
1598 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <a href=...> context",
1599 parser()->context.token->endTag?"/":"",parser()->context.token->name);
1600 }
1601 }
1602 break;
1603 default:
1604 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"<a>..</a> block");
1605 break;
1606 }
1607 }
1608 tok = parser()->tokenizer.lex();
1609 }
1610 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1611 {
1612 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1613 " <a href=...> tag");
1614 }
1615endhref:
1617 return retval;
1618}
1619
1620//---------------------------------------------------------------------------
1621
1623{
1624 AUTO_TRACE();
1625 Token retval(TokenRetval::RetVal_OK);
1626 auto ns = AutoNodeStack(parser(),thisVariant());
1627
1628 // first parse any number of paragraphs
1629 bool isFirst=true;
1630 DocPara *lastPar=nullptr;
1631 do
1632 {
1634 DocPara *par = children().get_last<DocPara>();
1635 if (isFirst) { par->markFirst(); isFirst=false; }
1636 retval=par->parse();
1637 if (!par->empty())
1638 {
1639 if (lastPar) lastPar->markLast(false);
1640 lastPar=par;
1641 }
1642 else
1643 {
1644 children().pop_back();
1645 }
1646 if (retval.is(TokenRetval::TK_LISTITEM))
1647 {
1648 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
1649 }
1650 } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF,
1651 TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection,
1652 TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph,
1653 TokenRetval::RetVal_EndInternal));
1654 if (lastPar) lastPar->markLast();
1655
1656 // then parse any number of level-n sections
1657 while ((level==1 && retval.is(TokenRetval::RetVal_Section)) ||
1658 (level==2 && retval.is(TokenRetval::RetVal_Subsection)) ||
1659 (level==3 && retval.is(TokenRetval::RetVal_Subsubsection)) ||
1660 (level==4 && retval.is(TokenRetval::RetVal_Paragraph)) ||
1661 (level==5 && retval.is(TokenRetval::RetVal_SubParagraph)) ||
1662 (level==6 && retval.is(TokenRetval::RetVal_SubSubParagraph))
1663 )
1664 {
1666 level,
1668 retval = children().get_last<DocSection>()->parse();
1669 }
1670
1671 if (retval.is(TokenRetval::RetVal_Internal))
1672 {
1673 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"\\internal command found inside internal section");
1674 }
1675
1676 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1677 return retval;
1678}
1679
1680//---------------------------------------------------------------------------
1681
1683{
1684 AUTO_TRACE();
1685 Token retval(TokenRetval::RetVal_OK);
1686 auto ns = AutoNodeStack(parser(),thisVariant());
1687 Token tok=parser()->tokenizer.lex();
1688 if (!tok.is(TokenRetval::TK_WHITESPACE))
1689 {
1690 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\addindex command");
1691 goto endindexentry;
1692 }
1694 m_entry="";
1695 tok = parser()->tokenizer.lex();
1696 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1697 {
1698 switch (tok.value())
1699 {
1700 case TokenRetval::TK_WHITESPACE:
1701 m_entry+=" ";
1702 break;
1703 case TokenRetval::TK_WORD:
1704 case TokenRetval::TK_LNKWORD:
1706 break;
1707 case TokenRetval::TK_SYMBOL:
1708 {
1709 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
1710 switch (s)
1711 {
1712 case HtmlEntityMapper::Sym_BSlash: m_entry+='\\'; break;
1713 case HtmlEntityMapper::Sym_At: m_entry+='@'; break;
1714 case HtmlEntityMapper::Sym_Less: m_entry+='<'; break;
1715 case HtmlEntityMapper::Sym_Greater: m_entry+='>'; break;
1716 case HtmlEntityMapper::Sym_Amp: m_entry+='&'; break;
1717 case HtmlEntityMapper::Sym_Dollar: m_entry+='$'; break;
1718 case HtmlEntityMapper::Sym_Hash: m_entry+='#'; break;
1719 case HtmlEntityMapper::Sym_Percent: m_entry+='%'; break;
1720 case HtmlEntityMapper::Sym_apos: m_entry+='\''; break;
1721 case HtmlEntityMapper::Sym_Quot: m_entry+='"'; break;
1722 case HtmlEntityMapper::Sym_lsquo: m_entry+='`'; break;
1723 case HtmlEntityMapper::Sym_rsquo: m_entry+='\''; break;
1724 case HtmlEntityMapper::Sym_ldquo: m_entry+="``"; break;
1725 case HtmlEntityMapper::Sym_rdquo: m_entry+="''"; break;
1726 case HtmlEntityMapper::Sym_ndash: m_entry+="--"; break;
1727 case HtmlEntityMapper::Sym_mdash: m_entry+="---"; break;
1728 default:
1729 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected symbol '{}' found as argument of \\addindex",parser()->context.token->name);
1730 break;
1731 }
1732 }
1733 break;
1734 case TokenRetval::TK_COMMAND_AT:
1735 // fall through
1736 case TokenRetval::TK_COMMAND_BS:
1737 switch (Mappers::cmdMapper->map(parser()->context.token->name))
1738 {
1739 case CommandType::CMD_BSLASH: m_entry+='\\'; break;
1740 case CommandType::CMD_AT: m_entry+='@'; break;
1741 case CommandType::CMD_LESS: m_entry+='<'; break;
1742 case CommandType::CMD_GREATER: m_entry+='>'; break;
1743 case CommandType::CMD_AMP: m_entry+='&'; break;
1744 case CommandType::CMD_DOLLAR: m_entry+='$'; break;
1745 case CommandType::CMD_HASH: m_entry+='#'; break;
1746 case CommandType::CMD_DCOLON: m_entry+="::"; break;
1747 case CommandType::CMD_PERCENT: m_entry+='%'; break;
1748 case CommandType::CMD_NDASH: m_entry+="--"; break;
1749 case CommandType::CMD_MDASH: m_entry+="---"; break;
1750 case CommandType::CMD_QUOTE: m_entry+='"'; break;
1751 case CommandType::CMD_PUNT: m_entry+='.'; break;
1752 case CommandType::CMD_PLUS: m_entry+='+'; break;
1753 case CommandType::CMD_MINUS: m_entry+='-'; break;
1754 case CommandType::CMD_EQUAL: m_entry+='='; break;
1755 case CommandType::CMD_EXCLAMATION: m_entry+='!'; break;
1756 case CommandType::CMD_QUESTION: m_entry+='?'; break;
1757 default:
1758 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command {} found as argument of \\addindex",
1759 parser()->context.token->name);
1760 break;
1761 }
1762 break;
1763 default:
1764 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",
1765 tok.to_string());
1766 break;
1767 }
1768 tok = parser()->tokenizer.lex();
1769 }
1772endindexentry:
1773 AUTO_TRACE_EXIT("retval={}",retval.to_string());
1774 return retval;
1775}
1776
1777//---------------------------------------------------------------------------
1778
1781{
1782 m_hasCaptionId = false;
1783 for (const auto &opt : attribs)
1784 {
1785 if (opt.name=="id" && !opt.value.empty()) // interpret id attribute as an anchor
1786 {
1787 const SectionInfo *sec = SectionManager::instance().find(opt.value);
1788 if (sec)
1789 {
1790 //printf("Found anchor %s\n",qPrint(id));
1791 m_file = sec->fileName();
1792 m_anchor = sec->label();
1793 m_hasCaptionId = true;
1794 }
1795 else
1796 {
1797 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"Invalid caption id '{}'",opt.value);
1798 }
1799 }
1800 else // copy attribute
1801 {
1802 m_attribs.push_back(opt);
1803 }
1804 }
1805}
1806
1808{
1809 AUTO_TRACE();
1810 Token retval = Token::make_TK_NONE();
1811 auto ns = AutoNodeStack(parser(),thisVariant());
1812 Token tok = parser()->tokenizer.lex();
1813 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
1814 {
1815 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
1816 {
1817 switch (tok.value())
1818 {
1819 case TokenRetval::TK_HTMLTAG:
1820 {
1821 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1822 if (tagId==HtmlTagType::HTML_CAPTION && parser()->context.token->endTag) // found </caption> tag
1823 {
1824 retval = Token::make_RetVal_OK();
1825 goto endcaption;
1826 }
1827 else
1828 {
1829 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <caption> context",
1830 parser()->context.token->endTag?"/":"",parser()->context.token->name);
1831 }
1832 }
1833 break;
1834 default:
1835 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"<caption> tag");
1836 break;
1837 }
1838 }
1839 tok = parser()->tokenizer.lex();
1840 }
1841 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
1842 {
1843 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
1844 " <caption> tag");
1845 }
1846endcaption:
1848 return retval;
1849}
1850
1851//---------------------------------------------------------------------------
1852
1854{
1855 AUTO_TRACE();
1856 Token retval = Token::make_RetVal_OK();
1857 auto ns = AutoNodeStack(parser(),thisVariant());
1858
1859 // parse one or more paragraphs
1860 bool isFirst=true;
1861 DocPara *par=nullptr;
1862 do
1863 {
1865 par = children().get_last<DocPara>();
1866 if (isFirst) { par->markFirst(); isFirst=false; }
1867 retval=par->parse();
1868 if (retval.is(TokenRetval::TK_HTMLTAG))
1869 {
1870 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1871 if (tagId==HtmlTagType::HTML_TD && parser()->context.token->endTag) // found </td> tag
1872 {
1873 retval = Token::make_TK_NEWPARA(); // ignore the tag
1874 }
1875 else if (tagId==HtmlTagType::HTML_TH && parser()->context.token->endTag) // found </th> tag
1876 {
1877 retval = Token::make_TK_NEWPARA(); // ignore the tag
1878 }
1879 }
1880 }
1881 while (retval.is_any_of(TokenRetval::TK_NEWPARA,TokenRetval::RetVal_EndParBlock));
1882 if (par) par->markLast();
1883
1884 return retval;
1885}
1886
1888{
1889 AUTO_TRACE();
1890 Token retval = Token::make_RetVal_OK();
1891 auto ns = AutoNodeStack(parser(),thisVariant());
1892
1893 // parse one or more paragraphs
1894 bool isFirst=true;
1895 DocPara *par=nullptr;
1896 do
1897 {
1899 par = children().get_last<DocPara>();
1900 if (isFirst) { par->markFirst(); isFirst=false; }
1901 retval=par->parse();
1902 if (retval.is(TokenRetval::TK_HTMLTAG))
1903 {
1904 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
1905 if (tagId==HtmlTagType::XML_ITEM && parser()->context.token->endTag) // found </item> tag
1906 {
1907 retval = Token::make_TK_NEWPARA(); // ignore the tag
1908 }
1909 else if (tagId==HtmlTagType::XML_DESCRIPTION && parser()->context.token->endTag) // found </description> tag
1910 {
1911 retval = Token::make_TK_NEWPARA(); // ignore the tag
1912 }
1913 }
1914 }
1915 while (retval.is(TokenRetval::TK_NEWPARA));
1916 if (par) par->markLast();
1917
1918 return retval;
1919}
1920
1921uint32_t DocHtmlCell::rowSpan() const
1922{
1923 for (const auto &attr : attribs())
1924 {
1925 if (attr.name.lower()=="rowspan")
1926 {
1927 return attr.value.toUInt();
1928 }
1929 }
1930 return 0;
1931}
1932
1933uint32_t DocHtmlCell::colSpan() const
1934{
1935 for (const auto &attr : attribs())
1936 {
1937 if (attr.name.lower()=="colspan")
1938 {
1939 return std::max(1u,attr.value.toUInt());
1940 }
1941 }
1942 return 1;
1943}
1944
1946{
1947 for (const auto &attr : attribs())
1948 {
1949 DString attrName = attr.name.lower();
1950 DString attrValue = attr.value.lower();
1951 if (attrName=="align")
1952 {
1953 if (attrValue=="center")
1954 return Center;
1955 else if (attrValue=="right")
1956 return Right;
1957 else return Left;
1958 }
1959 else if (attrName=="class" && attrValue.startsWith("markdowntable"))
1960 {
1961 if (attrValue=="markdowntableheadcenter")
1962 return Center;
1963 else if (attrValue=="markdowntableheadright")
1964 return Right;
1965 else if (attrValue=="markdowntableheadleft")
1966 return Left;
1967 else if (attrValue=="markdowntableheadnone")
1968 return Center;
1969 else if (attrValue=="markdowntablebodycenter")
1970 return Center;
1971 else if (attrValue=="markdowntablebodyright")
1972 return Right;
1973 else if (attrValue=="markdowntablebodyleft")
1974 return Left;
1975 else if (attrValue=="markdowntablebodynone")
1976 return Left;
1977 else return Left;
1978 }
1979 }
1980 return Left;
1981}
1982
1984{
1985 for (const auto &attr : attribs())
1986 {
1987 DString attrName = attr.name.lower();
1988 DString attrValue = attr.value.lower();
1989 if (attrName=="valign")
1990 {
1991 if (attrValue=="top")
1992 return Top;
1993 else if (attrValue=="bottom")
1994 return Bottom;
1995 else if (attrValue=="middle")
1996 return Middle;
1997 else return Middle;
1998 }
1999 }
2000 return Middle;
2001}
2002
2003//---------------------------------------------------------------------------
2004
2006{ // a row is a table heading if all cells are marked as such
2007 bool heading=true;
2008 for (const auto &n : children())
2009 {
2010 const DocHtmlCell *cell = std::get_if<DocHtmlCell>(&n);
2011 if (cell && !cell->isHeading())
2012 {
2013 heading = false;
2014 break;
2015 }
2016 }
2017 return !children().empty() && heading;
2018}
2019
2021{
2022 AUTO_TRACE();
2023 // get next token
2024 Token tok=parser->tokenizer.lex();
2025 // skip whitespace and tbody, thead and tfoot tags
2026 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA,TokenRetval::TK_HTMLTAG,
2027 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS))
2028 {
2029 if (tok.is(TokenRetval::TK_HTMLTAG))
2030 {
2031 AUTO_TRACE_ADD("html_tag={}",parser->context.token->name);
2033 // skip over tbody, thead, tfoot tags
2034 if (tagId==HtmlTagType::HTML_TBODY ||
2035 tagId==HtmlTagType::HTML_THEAD ||
2037 {
2038 tok=parser->tokenizer.lex();
2039 }
2040 else
2041 {
2042 break;
2043 }
2044 }
2045 else if (tok.is_any_of(TokenRetval::TK_COMMAND_AT, TokenRetval::TK_COMMAND_BS))
2046 {
2047 DString cmdName=parser->context.token->name;
2048 AUTO_TRACE_ADD("command={}",cmdName);
2049 auto cmdType = Mappers::cmdMapper->map(cmdName);
2050 if (cmdType==CommandType::CMD_ILINE)
2051 {
2052 { DocTokenizer::AutoSaveState saveState(parser->tokenizer);
2053 parser->tokenizer.setStateILine();
2054 tok = parser->tokenizer.lex();
2055 if (!tok.is(TokenRetval::TK_WORD))
2056 {
2057 warn_doc_error(parser->context.fileName,parser->tokenizer.getLineNr(),"invalid argument for command '{:c}{}'",
2058 tok.command_to_char(),cmdName);
2059 }
2060 }
2061 tok = parser->tokenizer.lex();
2062 }
2063 else
2064 {
2065 break;
2066 }
2067 }
2068 else
2069 {
2070 AUTO_TRACE_ADD("skip whitespace");
2071 tok=parser->tokenizer.lex();
2072 }
2073 }
2074 return tok;
2075}
2076
2077
2078
2079
2081{
2082 AUTO_TRACE();
2083 Token retval = Token::make_RetVal_OK();
2084 auto ns = AutoNodeStack(parser(),thisVariant());
2085
2086 bool isHeading=false;
2087 bool isFirst=true;
2088 DocHtmlCell *cell=nullptr;
2089
2091 // should find a html tag now
2092 if (tok.is(TokenRetval::TK_HTMLTAG))
2093 {
2094 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2095 if (tagId==HtmlTagType::HTML_TD && !parser()->context.token->endTag) // found <td> tag
2096 {
2097 }
2098 else if (tagId==HtmlTagType::HTML_TH && !parser()->context.token->endTag) // found <th> tag
2099 {
2100 isHeading=true;
2101 }
2102 else // found some other tag
2103 {
2104 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but "
2105 "found <{}{}> instead!",parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2106 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2107 goto endrow;
2108 }
2109 }
2110 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2111 {
2112 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2113 " for a html description title");
2114 goto endrow;
2115 }
2116 else // token other than html token
2117 {
2118 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but found {} token instead!",
2119 tok.to_string());
2120 goto endrow;
2121 }
2122
2123 // parse one or more cells
2124 do
2125 {
2128 isHeading);
2129 cell = children().get_last<DocHtmlCell>();
2130 cell->markFirst(isFirst);
2131 isFirst=false;
2132 retval=cell->parse();
2133 isHeading = retval.is(TokenRetval::RetVal_TableHCell);
2134 //printf("DocHtmlRow:retval=%s\n",retval.to_string());
2135 if (retval.is(TokenRetval::RetVal_EndTableCell))
2136 {
2137 // get next token
2138 retval = skipSpacesForTable(parser());
2139 //printf("DocHtmlRow:retval= next=%s name=%s endTag=%d\n",retval.to_string(),qPrint(parser()->context.token->name),parser()->context.token->endTag);
2140 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2141 if (tok.is(TokenRetval::TK_HTMLTAG))
2142 {
2143 if ((tagId==HtmlTagType::HTML_TD || tagId==HtmlTagType::HTML_TH) &&
2144 !parser()->context.token->endTag) // found new <td> or <td> tag
2145 {
2146 retval = Token::make_RetVal_TableCell();
2148 }
2149 else if (tagId==HtmlTagType::HTML_TR)
2150 {
2151 if (parser()->context.token->endTag) // found </tr> tag
2152 {
2153 retval = Token::make_RetVal_EndTableRow();
2154 }
2155 else // found <tr> tag
2156 {
2157 retval = Token::make_RetVal_TableRow();
2158 }
2159 }
2160 else if (tagId==HtmlTagType::HTML_TABLE && parser()->context.token->endTag) // found </table>
2161 {
2162 retval = Token::make_RetVal_EndTable();
2163 }
2164 else // found some other tag
2165 {
2166 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td>, <th> or <tr> tag but "
2167 "found <{}{}> instead!",parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2168 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2169 goto endrow;
2170 }
2171 }
2172 else // token other than html token
2173 {
2174 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td>, <th> or <tr> tag but found {} token instead!",
2175 tok.to_string());
2176 goto endrow;
2177 }
2178 }
2179 }
2180 while (retval.is_any_of(TokenRetval::RetVal_TableCell,TokenRetval::RetVal_TableHCell));
2181 cell->markLast(true);
2182
2183endrow:
2184 return retval;
2185}
2186
2188{
2189 AUTO_TRACE();
2190 Token retval = Token::make_RetVal_OK();
2191 auto ns = AutoNodeStack(parser(),thisVariant());
2192
2193 bool isFirst=true;
2194 DocHtmlCell *cell=nullptr;
2195
2196 // get next token
2197 Token tok=parser()->tokenizer.lex();
2198 // skip whitespace
2199 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2200 // should find a html tag now
2201 if (tok.is(TokenRetval::TK_HTMLTAG))
2202 {
2203 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2204 if (tagId==HtmlTagType::XML_TERM && !parser()->context.token->endTag) // found <term> tag
2205 {
2206 }
2207 else if (tagId==HtmlTagType::XML_DESCRIPTION && !parser()->context.token->endTag) // found <description> tag
2208 {
2209 }
2210 else // found some other tag
2211 {
2212 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <term> or <description> tag but "
2213 "found <{}> instead!",parser()->context.token->name);
2214 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2215 goto endrow;
2216 }
2217 }
2218 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2219 {
2220 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2221 " for a html description title");
2222 goto endrow;
2223 }
2224 else // token other than html token
2225 {
2226 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <td> or <th> tag but found {} token instead!",
2227 tok.to_string());
2228 goto endrow;
2229 }
2230
2231 do
2232 {
2234 cell = children().get_last<DocHtmlCell>();
2235 cell->markFirst(isFirst);
2236 isFirst=false;
2237 retval=cell->parseXml();
2238 }
2239 while (retval.is_any_of(TokenRetval::RetVal_TableCell,TokenRetval::RetVal_TableHCell));
2240 cell->markLast(true);
2241
2242endrow:
2243 return retval;
2244}
2245
2246//---------------------------------------------------------------------------
2247
2249{
2250 return m_caption!=nullptr;
2251}
2252
2254{
2255 return m_caption.get();
2256}
2257
2259{
2260 size_t hl = 0;
2261 for (auto &rowNode : children())
2262 {
2263 const DocHtmlRow *row = std::get_if<DocHtmlRow>(&rowNode);
2264 if (row)
2265 {
2266 if (!row->isHeading()) break;
2267 hl++;
2268 }
2269 }
2270 return hl;
2271}
2272
2274{
2275 AUTO_TRACE();
2276 Token retval = Token::make_RetVal_OK();
2277 auto ns = AutoNodeStack(parser(),thisVariant());
2278
2279getrow:
2280 // skip whitespace and tbody, thead and tfoot tags
2282 // should find a html tag now
2283 if (tok.is(TokenRetval::TK_HTMLTAG))
2284 {
2285 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2286 if (tagId==HtmlTagType::HTML_TR && !parser()->context.token->endTag) // found <tr> tag
2287 {
2288 // no caption, just rows
2289 retval = Token::make_RetVal_TableRow();
2290 }
2291 else if (tagId==HtmlTagType::HTML_CAPTION && !parser()->context.token->endTag) // found <caption> tag
2292 {
2293 if (m_caption)
2294 {
2295 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"table already has a caption, found another one");
2296 }
2297 else
2298 {
2299 m_caption = createDocNode<DocHtmlCaption>(parser(),thisVariant(),parser()->context.token->attribs);
2300 retval=std::get<DocHtmlCaption>(*m_caption).parse();
2301
2302 if (retval.is(TokenRetval::RetVal_OK)) // caption was parsed ok
2303 {
2304 goto getrow;
2305 }
2306 }
2307 }
2308 else // found wrong token
2309 {
2310 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or <caption> tag but "
2311 "found <{}{}> instead!", parser()->context.token->endTag ? "/" : "", parser()->context.token->name);
2312 }
2313 }
2314 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2315 {
2316 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2317 " for a <tr> or <caption> tag");
2318 }
2319 else // token other than html token
2320 {
2321 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> tag but found {} token instead!",
2322 tok.to_string());
2323 }
2324
2325 // parse one or more rows
2326 while (retval.is(TokenRetval::RetVal_TableRow))
2327 {
2329 retval = children().get_last<DocHtmlRow>()->parse();
2330 //printf("DocHtmlTable::retval=%s\n",retval.to_string());
2331 if (retval.is(TokenRetval::RetVal_EndTableRow))
2332 {
2333 // get next token
2334 retval = skipSpacesForTable(parser());
2335 //printf("DocHtmlTable::retval= next=%s name=%s endTag=%d\n",retval.to_string(),qPrint(parser()->context.token->name),parser()->context.token->endTag);
2336 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2337 if (tagId==HtmlTagType::HTML_TR && !parser()->context.token->endTag)
2338 {
2339 retval = Token::make_RetVal_TableRow();
2340 }
2341 else if (tagId==HtmlTagType::HTML_TABLE && parser()->context.token->endTag)
2342 {
2343 retval = Token::make_RetVal_EndTable();
2344 }
2345 else if (retval.is(TokenRetval::TK_HTMLTAG)) // some other HTML tag
2346 {
2347 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or </table> tag but "
2348 "found <{}> instead!",parser()->context.token->name);
2349 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2350 }
2351 else // found some other tag
2352 {
2353 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <tr> or </table> tag but "
2354 "found token {} instead!",retval.to_string());
2355 retval=Token::make_RetVal_OK();
2356 break;
2357 }
2358 }
2359 }
2360
2362
2363 return retval.is(TokenRetval::RetVal_EndTable) ? Token::make_RetVal_OK() : retval;
2364}
2365
2367{
2368 AUTO_TRACE();
2369 Token retval = Token::make_RetVal_OK();
2370 auto ns = AutoNodeStack(parser(),thisVariant());
2371
2372 // get next token
2373 Token tok=parser()->tokenizer.lex();
2374 // skip whitespace
2375 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2376 // should find a html tag now
2378 bool isHeader=false;
2379 if (tok.is(TokenRetval::TK_HTMLTAG))
2380 {
2381 tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2382 if (tagId==HtmlTagType::XML_ITEM && !parser()->context.token->endTag) // found <item> tag
2383 {
2384 retval = Token::make_RetVal_TableRow();
2385 }
2386 if (tagId==HtmlTagType::XML_LISTHEADER && !parser()->context.token->endTag) // found <listheader> tag
2387 {
2388 retval = Token::make_RetVal_TableRow();
2389 isHeader=true;
2390 }
2391 }
2392
2393 // parse one or more rows
2394 while (retval.is(TokenRetval::RetVal_TableRow))
2395 {
2398 retval=tr->parseXml(isHeader);
2399 isHeader=false;
2400 }
2401
2403
2404 tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2405 return tagId==HtmlTagType::XML_LIST && parser()->context.token->endTag ? Token::make_RetVal_OK() : retval;
2406}
2407
2408/** Helper class to compute the grid for an HTML style table */
2410{
2411 ActiveRowSpan(uint32_t rows,uint32_t col) : rowsLeft(rows), column(col) {}
2412 uint32_t rowsLeft;
2413 uint32_t column;
2414};
2415
2416/** List of ActiveRowSpan classes. */
2417typedef std::vector<ActiveRowSpan> RowSpanList;
2418
2419/** determines the location of all cells in a grid, resolving row and
2420 column spans. For each the total number of visible cells is computed,
2421 and the total number of visible columns over all rows is stored.
2422 */
2424{
2425 //printf("computeTableGrid()\n");
2426 RowSpanList rowSpans;
2427 uint32_t maxCols=0;
2428 uint32_t rowIdx=1;
2429 for (auto &rowNode : children())
2430 {
2431 uint32_t colIdx=1;
2432 uint32_t cells=0;
2433 DocHtmlRow *row = std::get_if<DocHtmlRow>(&rowNode);
2434 if (row)
2435 {
2436 for (auto &cellNode : row->children())
2437 {
2438 DocHtmlCell *cell = std::get_if<DocHtmlCell>(&cellNode);
2439 if (cell)
2440 {
2441 uint32_t rs = cell->rowSpan();
2442 uint32_t cs = cell->colSpan();
2443
2444 for (size_t i=0;i<rowSpans.size();i++)
2445 {
2446 if (rowSpans[i].rowsLeft>0 &&
2447 rowSpans[i].column==colIdx)
2448 {
2449 colIdx=rowSpans[i].column+1;
2450 cells++;
2451 }
2452 }
2453 if (rs>0) rowSpans.emplace_back(rs,colIdx);
2454 //printf("found cell at (%d,%d)\n",rowIdx,colIdx);
2455 cell->setRowIndex(rowIdx);
2456 cell->setColumnIndex(colIdx);
2457 colIdx+=cs;
2458 cells++;
2459 }
2460 }
2461 for (size_t i=0;i<rowSpans.size();i++)
2462 {
2463 if (rowSpans[i].rowsLeft>0) rowSpans[i].rowsLeft--;
2464 }
2465 row->setVisibleCells(cells);
2466 row->setRowIndex(rowIdx);
2467 rowIdx++;
2468 }
2469 if (colIdx-1>maxCols) maxCols=colIdx-1;
2470 }
2471 m_numCols = maxCols;
2472}
2473
2474//---------------------------------------------------------------------------
2475
2477{
2478 AUTO_TRACE();
2479 Token retval = Token::make_TK_NONE();
2480 auto ns = AutoNodeStack(parser(),thisVariant());
2481
2482 Token tok = parser()->tokenizer.lex();
2483 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
2484 {
2485 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
2486 {
2487 switch (tok.value())
2488 {
2489 case TokenRetval::TK_COMMAND_AT:
2490 // fall through
2491 case TokenRetval::TK_COMMAND_BS:
2492 {
2493 DString cmdName=parser()->context.token->name;
2494 bool isJavaLink=false;
2495 switch (Mappers::cmdMapper->map(cmdName))
2496 {
2498 {
2499 tok=parser()->tokenizer.lex();
2500 if (!tok.is(TokenRetval::TK_WHITESPACE))
2501 {
2502 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
2503 tok.command_to_char(),cmdName);
2504 }
2505 else
2506 {
2508 tok=parser()->tokenizer.lex(); // get the reference id
2509 if (!tok.is(TokenRetval::TK_WORD))
2510 {
2511 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}' command",
2512 tok.to_string(),tok.command_to_char(),cmdName);
2513 }
2514 else
2515 {
2517 children().get_last<DocRef>()->parse(tok.command_to_char(),cmdName);
2518 }
2520 }
2521 }
2522 break;
2524 isJavaLink=true;
2525 // fall through
2527 {
2528 tok=parser()->tokenizer.lex();
2529 if (!tok.is(TokenRetval::TK_WHITESPACE))
2530 {
2531 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
2532 cmdName);
2533 }
2534 else
2535 {
2537 tok=parser()->tokenizer.lex();
2538 if (!tok.is(TokenRetval::TK_WORD))
2539 {
2540 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of \\{} command",
2541 tok.to_string(),cmdName);
2542 }
2543 else
2544 {
2547 DocLink *lnk = children().get_last<DocLink>();
2548 DString leftOver = lnk->parse(isJavaLink);
2549 if (!leftOver.empty())
2550 {
2551 children().append<DocWord>(parser(),thisVariant(),leftOver);
2552 }
2553 }
2554 }
2555 }
2556 break;
2558 {
2560 }
2561 break;
2562 default:
2563 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal command '{:c}{}' found as part of a <dt> tag",
2564 tok.command_to_char(),cmdName);
2565 }
2566 }
2567 break;
2568 case TokenRetval::TK_SYMBOL:
2569 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found as part of a <dt> tag",
2570 parser()->context.token->name);
2571 break;
2572 case TokenRetval::TK_HTMLTAG:
2573 {
2574 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2575 if (tagId==HtmlTagType::HTML_DD && !parser()->context.token->endTag) // found <dd> tag
2576 {
2577 retval = Token::make_RetVal_DescData();
2578 goto endtitle;
2579 }
2580 else if (tagId==HtmlTagType::HTML_DT && parser()->context.token->endTag)
2581 {
2582 // ignore </dt> tag.
2583 }
2584 else if (tagId==HtmlTagType::HTML_DT)
2585 {
2586 // missing <dt> tag.
2587 retval = Token::make_RetVal_DescTitle();
2588 goto endtitle;
2589 }
2590 else if (tagId==HtmlTagType::HTML_DL && parser()->context.token->endTag)
2591 {
2592 retval = Token::make_RetVal_EndDesc();
2593 goto endtitle;
2594 }
2595 else if (tagId==HtmlTagType::HTML_A)
2596 {
2597 if (!parser()->context.token->endTag)
2598 {
2599 parser()->handleAHref(thisVariant(),children(),parser()->context.token->attribs);
2600 }
2601 }
2602 else if (tagId==HtmlTagType::HTML_BR)
2603 {
2605 }
2606 else
2607 {
2608 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected html tag <{}{}> found within <dt> context",
2609 parser()->context.token->endTag?"/":"",parser()->context.token->name);
2610 }
2611 }
2612 break;
2613 default:
2614 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {} found as part of a <dt> tag",
2615 tok.to_string());
2616 break;
2617 }
2618 }
2619 tok = parser()->tokenizer.lex();
2620 }
2621 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2622 {
2623 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end of comment while inside"
2624 " <dt> tag");
2625 }
2626endtitle:
2628 return retval;
2629}
2630
2631
2632//---------------------------------------------------------------------------
2633
2635{
2636 AUTO_TRACE();
2638 Token retval = Token::make_TK_NONE();
2639 auto ns = AutoNodeStack(parser(),thisVariant());
2640
2641 bool isFirst=true;
2642 DocPara *par=nullptr;
2643 do
2644 {
2646 par = children().get_last<DocPara>();
2647 if (isFirst) { par->markFirst(); isFirst=false; }
2648 retval=par->parse();
2649 }
2650 while (retval.is(TokenRetval::TK_NEWPARA));
2651 if (par) par->markLast();
2652
2653 return retval;
2654}
2655
2656//---------------------------------------------------------------------------
2657
2659{
2660 AUTO_TRACE();
2661 Token retval = Token::make_RetVal_OK();
2662 auto ns = AutoNodeStack(parser(),thisVariant());
2663
2664 // get next token
2665 Token tok=parser()->tokenizer.lex();
2666 // skip whitespace
2667 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2668 // should find a html tag now
2669 if (tok.is(TokenRetval::TK_HTMLTAG))
2670 {
2671 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2672 if (tagId==HtmlTagType::HTML_DT && !parser()->context.token->endTag) // found <dt> tag
2673 {
2674 // continue
2675 }
2676 else // found some other tag
2677 {
2678 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <dt> tag but "
2679 "found <{}> instead!",parser()->context.token->name);
2680 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2681 goto enddesclist;
2682 }
2683 }
2684 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2685 {
2686 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2687 " for a html description title");
2688 goto enddesclist;
2689 }
2690 else // token other than html token
2691 {
2692 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <dt> tag but found {} token instead!",
2693 tok.to_string());
2694 goto enddesclist;
2695 }
2696
2697 do
2698 {
2703 retval=dt->parse();
2704 if (retval.is(TokenRetval::RetVal_DescData))
2705 {
2706 retval=dd->parse();
2707 while (retval.is(TokenRetval::RetVal_DescData))
2708 {
2711 retval=dd->parse();
2712 }
2713 }
2714 else if (!retval.is(TokenRetval::RetVal_DescTitle))
2715 {
2716 // error
2717 break;
2718 }
2719 } while (retval.is(TokenRetval::RetVal_DescTitle));
2720
2721 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2722 {
2723 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <dl> block");
2724 }
2725
2726enddesclist:
2727
2728 return retval.is(TokenRetval::RetVal_EndDesc) ? Token::make_RetVal_OK() : retval;
2729}
2730
2731//---------------------------------------------------------------------------
2732
2734{
2735 AUTO_TRACE();
2736 Token retval = Token::make_TK_NONE();
2737 auto ns = AutoNodeStack(parser(),thisVariant());
2738
2739 // parse one or more paragraphs
2740 bool isFirst=true;
2741 DocPara *par=nullptr;
2742 do
2743 {
2745 par = children().get_last<DocPara>();
2746 if (isFirst) { par->markFirst(); isFirst=false; }
2747 retval=par->parse();
2748 }
2749 while (retval.is(TokenRetval::TK_NEWPARA));
2750 if (par) par->markLast();
2751
2752 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2753 return retval;
2754}
2755
2757{
2758 AUTO_TRACE();
2759 Token retval = Token::make_TK_NONE();
2760 auto ns = AutoNodeStack(parser(),thisVariant());
2761
2762 // parse one or more paragraphs
2763 bool isFirst=true;
2764 DocPara *par=nullptr;
2765 do
2766 {
2768 par = children().get_last<DocPara>();
2769 if (isFirst) { par->markFirst(); isFirst=false; }
2770 retval=par->parse();
2771 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
2772
2773 //printf("new item: retval=%x parser()->context.token->name=%s parser()->context.token->endTag=%d\n",
2774 // retval,qPrint(parser()->context.token->name),parser()->context.token->endTag);
2775 if (retval.is(TokenRetval::RetVal_ListItem))
2776 {
2777 break;
2778 }
2779 }
2780 while (!retval.is(TokenRetval::RetVal_CloseXml));
2781
2782 if (par) par->markLast();
2783
2784 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2785 return retval;
2786}
2787
2788//---------------------------------------------------------------------------
2789
2791{
2792 AUTO_TRACE();
2793 Token retval = Token::make_RetVal_OK();
2794 int num=1;
2795 auto ns = AutoNodeStack(parser(),thisVariant());
2796
2797 // get next token
2798 Token tok=parser()->tokenizer.lex();
2799 // skip whitespace and paragraph breaks
2800 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2801 // should find a html tag now
2802 if (tok.is(TokenRetval::TK_HTMLTAG))
2803 {
2804 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2805 if (tagId==HtmlTagType::HTML_LI && !parser()->context.token->endTag) // found <li> tag
2806 {
2807 // ok, we can go on.
2808 }
2809 else if (((m_type==Unordered && tagId==HtmlTagType::HTML_UL) ||
2811 ) && parser()->context.token->endTag
2812 ) // found empty list
2813 {
2814 // add dummy item to obtain valid HTML
2816 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"empty list!");
2817 retval = Token::make_RetVal_EndList();
2818 goto endlist;
2819 }
2820 else // found some other tag
2821 {
2822 // add dummy item to obtain valid HTML
2824 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <li> tag but "
2825 "found <{}{}> instead!",parser()->context.token->endTag?"/":"",parser()->context.token->name);
2826 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2827 goto endlist;
2828 }
2829 }
2830 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2831 {
2832 // add dummy item to obtain valid HTML
2834 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2835 " for a html list item");
2836 goto endlist;
2837 }
2838 else // token other than html token
2839 {
2840 // add dummy item to obtain valid HTML
2842 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <li> tag but found {} token instead!",
2843 tok.to_string());
2844 goto endlist;
2845 }
2846
2847 do
2848 {
2851 retval=li->parse();
2852 } while (retval.is(TokenRetval::RetVal_ListItem));
2853
2854 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2855 {
2856 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <{:c}l> block",
2857 m_type==Unordered ? 'u' : 'o');
2858 }
2859
2860endlist:
2861 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2862 return retval.is(TokenRetval::RetVal_EndList) ? Token::make_RetVal_OK() : retval;
2863}
2864
2866{
2867 AUTO_TRACE();
2868 Token retval = Token::make_RetVal_OK();
2869 int num=1;
2870 auto ns = AutoNodeStack(parser(),thisVariant());
2871
2872 // get next token
2873 Token tok=parser()->tokenizer.lex();
2874 // skip whitespace and paragraph breaks
2875 while (tok.is_any_of(TokenRetval::TK_WHITESPACE,TokenRetval::TK_NEWPARA)) tok=parser()->tokenizer.lex();
2876 // should find a html tag now
2877 if (tok.is(TokenRetval::TK_HTMLTAG))
2878 {
2879 HtmlTagType tagId=Mappers::htmlTagMapper->map(parser()->context.token->name);
2880 //printf("parser()->context.token->name=%s parser()->context.token->endTag=%d\n",qPrint(parser()->context.token->name),parser()->context.token->endTag);
2881 if (tagId==HtmlTagType::XML_ITEM && !parser()->context.token->endTag) // found <item> tag
2882 {
2883 // ok, we can go on.
2884 }
2885 else // found some other tag
2886 {
2887 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <item> tag but "
2888 "found <{}> instead!",parser()->context.token->name);
2889 parser()->tokenizer.pushBackHtmlTag(parser()->context.token->name);
2890 goto endlist;
2891 }
2892 }
2893 else if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
2894 {
2895 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while looking"
2896 " for a html list item");
2897 goto endlist;
2898 }
2899 else // token other than html token
2900 {
2901 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected <item> tag but found {} token instead!",
2902 tok.to_string());
2903 goto endlist;
2904 }
2905
2906 do
2907 {
2910 retval=li->parseXml();
2911 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
2912 //printf("retval=%x parser()->context.token->name=%s\n",retval,qPrint(parser()->context.token->name));
2913 } while (retval.is(TokenRetval::RetVal_ListItem));
2914
2915 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2916 {
2917 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <list type=\"{}\"> block",
2918 m_type==Unordered ? "bullet" : "number");
2919 }
2920
2921endlist:
2922 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2923 return (retval.is_any_of(TokenRetval::RetVal_EndList,TokenRetval::RetVal_CloseXml) || parser()->context.token->name=="list") ?
2924 Token::make_RetVal_OK() : retval;
2925}
2926
2927//--------------------------------------------------------------------------
2928
2930{
2931 AUTO_TRACE();
2932 Token retval = Token::make_TK_NONE();
2933 auto ns = AutoNodeStack(parser(),thisVariant());
2934
2935 // parse one or more paragraphs
2936 bool isFirst=true;
2937 DocPara *par=nullptr;
2938 do
2939 {
2941 par = children().get_last<DocPara>();
2942 if (isFirst) { par->markFirst(); isFirst=false; }
2943 retval=par->parse();
2944 }
2945 while (retval.is(TokenRetval::TK_NEWPARA));
2946 if (par) par->markLast();
2947
2948 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
2949 {
2950 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment while inside <blockquote> block");
2951 }
2952
2953 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2954 return retval.is(TokenRetval::RetVal_EndBlockQuote) ? Token::make_RetVal_OK() : retval;
2955}
2956
2957//---------------------------------------------------------------------------
2958
2960{
2961 AUTO_TRACE();
2962 Token retval = Token::make_TK_NONE();
2963 auto ns = AutoNodeStack(parser(),thisVariant());
2964
2965 // parse one or more paragraphs
2966 bool isFirst=true;
2967 DocPara *par=nullptr;
2968 do
2969 {
2971 par = children().get_last<DocPara>();
2972 if (isFirst) { par->markFirst(); isFirst=false; }
2973 retval=par->parse();
2974 }
2975 while (retval.is(TokenRetval::TK_NEWPARA));
2976 if (par) par->markLast();
2977
2978 AUTO_TRACE_EXIT("retval={}",retval.to_string());
2979 return retval.is(TokenRetval::RetVal_EndBlockQuote) ? Token::make_RetVal_OK() : retval;
2980}
2981
2982//---------------------------------------------------------------------------
2983
2988
2989
2991{
2992 auto ns = AutoNodeStack(parser(),thisVariant());
2994 DocPara *par = &std::get<DocPara>(*m_paragraph);
2995 Token rv=par->parse();
2996 par->markFirst();
2997 par->markLast();
2998 return rv;
2999}
3000
3001//--------------------------------------------------------------------------
3002
3004{
3005 auto ns = AutoNodeStack(parser(),thisVariant());
3006 Token rv = Token::make_TK_NONE();
3007 do
3008 {
3011 rv=li->parse();
3012 } while (rv.is(TokenRetval::RetVal_ListItem));
3013 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3014}
3015
3016//--------------------------------------------------------------------------
3017
3022
3024{
3025 AUTO_TRACE();
3026 Token retval = Token::make_RetVal_OK();
3027 auto ns = AutoNodeStack(parser(),thisVariant());
3028
3029 // first parse any number of paragraphs
3030 bool isFirst=true;
3031 DocPara *lastPar=nullptr;
3032 do
3033 {
3035 DocPara *par = children().get_last<DocPara>();
3036 if (isFirst) { par->markFirst(); isFirst=false; }
3037 retval=par->parse();
3038 if (!par->empty())
3039 {
3040 if (lastPar) lastPar->markLast(false);
3041 lastPar=par;
3042 }
3043 else
3044 {
3045 children().pop_back();
3046 }
3047 // next paragraph should be more indented than the - marker to belong
3048 // to this item
3049 } while (retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->indent>m_indent);
3050 if (lastPar) lastPar->markLast();
3051
3052 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3053 return retval;
3054}
3055
3056//--------------------------------------------------------------------------
3057
3064
3066{
3067 AUTO_TRACE();
3068 Token retval = Token::make_RetVal_OK();
3069 int num=1;
3070 auto ns = AutoNodeStack(parser(),thisVariant());
3072 // first item or sub list => create new list
3073 do
3074 {
3075 switch (parser()->context.token->id)
3076 {
3077 case -1:
3078 break;
3079 case DocAutoList::Unchecked: // unchecked
3080 case DocAutoList::Checked_x: // checked with x
3081 case DocAutoList::Checked_X: // checked with X
3082 num = parser()->context.token->id;
3083 break;
3084 default: // explicitly numbered list
3085 num=parser()->context.token->id; // override num with real number given
3086 break;
3087 }
3088
3090 retval = children().get_last<DocAutoListItem>()->parse();
3091 //printf("DocAutoList::parse(): retval=0x%x parser()->context.token->indent=%d m_indent=%d "
3092 // "m_isEnumList=%d parser()->context.token->isEnumList=%d parser()->context.token->name=%s\n",
3093 // retval,parser()->context.token->indent,m_indent,m_isEnumList,parser()->context.token->isEnumList,
3094 // qPrint(parser()->context.token->name));
3095 //printf("num=%d parser()->context.token->id=%d\n",num,parser()->context.token->id);
3096 }
3097 while (retval.is(TokenRetval::TK_LISTITEM) && // new list item
3098 m_indent==parser()->context.token->indent && // at same indent level
3099 m_isEnumList==parser()->context.token->isEnumList && // of the same kind
3100 m_isCheckedList==parser()->context.token->isCheckedList && // of the same kind
3101 (parser()->context.token->id==-1 || parser()->context.token->id>=num) // increasing number (or no number or checked list)
3102 );
3103
3105
3106 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3107 return retval;
3108}
3109
3110//--------------------------------------------------------------------------
3111
3113{
3114 AUTO_TRACE();
3115 auto ns = AutoNodeStack(parser(),thisVariant());
3117 Token tok = parser()->tokenizer.lex();
3118 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF))
3119 {
3120 if (!parser()->defaultHandleToken(thisVariant(),tok,children()))
3121 {
3122 parser()->errorHandleDefaultToken(thisVariant(),tok,children(),"title section");
3123 }
3124 tok = parser()->tokenizer.lex();
3125 }
3128}
3129
3140
3141//--------------------------------------------------------------------------
3142
3147
3149{
3150 return m_title && std::get<DocTitle>(*m_title).hasTitle();
3151}
3152
3153Token DocSimpleSect::parse(bool userTitle,bool needsSeparator)
3154{
3155 AUTO_TRACE();
3156 auto ns = AutoNodeStack(parser(),thisVariant());
3157
3158 // handle case for user defined title
3159 if (userTitle)
3160 {
3162 std::get_if<DocTitle>(m_title.get())->parse();
3163 }
3164
3165 // add new paragraph as child
3166 if (!children().empty() && std::holds_alternative<DocPara>(children().back()))
3167 {
3168 std::get<DocPara>(children().back()).markLast(false);
3169 }
3170 bool markFirst = children().empty();
3171 if (needsSeparator)
3172 {
3174 }
3176 DocPara *par = children().get_last<DocPara>();
3177 if (markFirst)
3178 {
3179 par->markFirst();
3180 }
3181 par->markLast();
3182
3183 // parse the contents of the paragraph
3184 Token retval = par->parse();
3185
3186 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3187 return retval; // 0==EOF, TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, TokenRetval::TK_ENDLIST, TokenRetval::RetVal_SimpleSec
3188}
3189
3191{
3192 AUTO_TRACE();
3193 auto ns = AutoNodeStack(parser(),thisVariant());
3194
3196 DocTitle *title = &std::get<DocTitle>(*m_title);
3197 title->parseFromString(thisVariant(),parser()->context.token->name);
3198
3199 DString text = parser()->context.token->text;
3200 { DocParser::AutoSaveContext saveContext(*parser());
3202 }
3203
3204 return Token::make_RetVal_OK();
3205}
3206
3208{
3209 AUTO_TRACE();
3210 auto ns = AutoNodeStack(parser(),thisVariant());
3211
3212 Token retval = Token::make_RetVal_OK();
3213 for (;;)
3214 {
3215 // add new paragraph as child
3216 if (!children().empty() && std::holds_alternative<DocPara>(children().back()))
3217 {
3218 std::get<DocPara>(children().back()).markLast(false);
3219 }
3220 bool markFirst = children().empty();
3222 DocPara *par = children().get_last<DocPara>();
3223 if (markFirst)
3224 {
3225 par->markFirst();
3226 }
3227 par->markLast();
3228
3229 // parse the contents of the paragraph
3230 retval = par->parse();
3231 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
3232 if (retval.is(TokenRetval::RetVal_CloseXml))
3233 {
3234 retval = Token::make_RetVal_OK();
3235 break;
3236 }
3237 }
3238
3239 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3240 return retval;
3241}
3242
3244{
3245 DocPara *p=nullptr;
3246 if (children().empty() || (p=std::get_if<DocPara>(&children().back()))==nullptr)
3247 {
3249 p = children().get_last<DocPara>();
3250 }
3251 else
3252 {
3253 // Comma-separate <seealso> links.
3254 p->injectToken(Token::make_TK_WORD(),",");
3255 p->injectToken(Token::make_TK_WHITESPACE()," ");
3256 }
3257
3258 parser()->context.inSeeBlock=true;
3259 p->injectToken(Token::make_TK_LNKWORD(),word);
3260 parser()->context.inSeeBlock=false;
3261}
3262
3264{
3265 switch (m_type)
3266 {
3267 case Unknown: break;
3268 case See: return "see";
3269 case Return: return "return";
3270 case Author: // fall through
3271 case Authors: return "author";
3272 case Version: return "version";
3273 case Since: return "since";
3274 case Date: return "date";
3275 case Note: return "note";
3276 case Warning: return "warning";
3277 case Pre: return "pre";
3278 case Post: return "post";
3279 case Copyright: return "copyright";
3280 case Invar: return "invariant";
3281 case Remark: return "remark";
3282 case Attention: return "attention";
3283 case Important: return "important";
3284 case User: return "user";
3285 case Rcs: return "rcs";
3286 }
3287 return "unknown";
3288}
3289
3290//--------------------------------------------------------------------------
3291
3293{
3294 AUTO_TRACE();
3295 Token retval = Token::make_RetVal_OK();
3296 auto ns = AutoNodeStack(parser(),thisVariant());
3297 DocPara *par=nullptr;
3298 DString saveCmdName = cmdName;
3299 DocParserContext &context = parser()->context;
3300 DocTokenizer &tokenizer = parser()->tokenizer;
3301
3302 Token tok=tokenizer.lex();
3303 if (!tok.is(TokenRetval::TK_WHITESPACE))
3304 {
3305 warn_doc_error(context.fileName,tokenizer.getLineNr(),"expected whitespace after \\{} command",
3306 saveCmdName);
3307 retval = Token::make_RetVal_EndParBlock();
3308 goto endparamlist;
3309 }
3310 tokenizer.setStateParam();
3311 tok=tokenizer.lex();
3312 while (tok.is(TokenRetval::TK_WORD)) /* there is a parameter name */
3313 {
3315 {
3316 size_t typeSeparator = context.token->name.find('#'); // explicit type position
3317 if (typeSeparator!=DString::npos)
3318 {
3319 parser()->handleParameterType(thisVariant(),m_paramTypes,context.token->name.left(typeSeparator));
3320 context.token->name = context.token->name.mid(typeSeparator+1);
3321 context.hasParamCommand=true;
3322 if (parent() && std::holds_alternative<DocParamSect>(*parent()))
3323 {
3324 std::get<DocParamSect>(*parent()).m_hasTypeSpecifier=true;
3325 }
3326 }
3327 else
3328 {
3329 context.hasParamCommand=true;
3330 }
3332 }
3333 else if (m_type==DocParamSect::RetVal)
3334 {
3335 context.hasReturnCommand=true;
3337 }
3338 context.inSeeBlock=true;
3340 context.inSeeBlock=false;
3341 tok=tokenizer.lex();
3342 }
3343 tokenizer.setStatePara();
3344 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) // premature end of comment
3345 {
3346 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3347 "argument of command {}",saveCmdName);
3348 retval = Token::make_RetVal_EndParBlock();
3349 goto endparamlist;
3350 }
3351 if (!tok.is(TokenRetval::TK_WHITESPACE)) /* premature end of comment block */
3352 {
3353 if (!tok.is(TokenRetval::TK_NEWPARA)) /* empty param description */
3354 {
3355 warn_doc_error(context.fileName,tokenizer.getLineNr(),"unexpected token {} in comment block while parsing the "
3356 "argument of command {}",tok.to_string(),saveCmdName);
3357 }
3358 retval = Token::make_RetVal_EndParBlock();
3359 goto endparamlist;
3360 }
3361
3363 par = m_paragraphs.get_last<DocPara>();
3364 retval = par->parse();
3365 par->markFirst();
3366 par->markLast();
3367
3368endparamlist:
3369 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3370 return retval;
3371}
3372
3374{
3375 AUTO_TRACE();
3376 Token retval = Token::make_RetVal_OK();
3377 auto ns = AutoNodeStack(parser(),thisVariant());
3378
3379 parser()->context.token->name = paramName;
3381 {
3384 }
3385 else if (m_type==DocParamSect::RetVal)
3386 {
3389 }
3390
3392
3393 do
3394 {
3397 retval = par->parse();
3398 if (par->empty()) // avoid adding an empty paragraph for the whitespace
3399 // after </para> and before </param>
3400 {
3402 break;
3403 }
3404 else // append the paragraph to the list
3405 {
3406 if (!m_paragraphs.empty())
3407 {
3409 }
3410 bool markFirst = m_paragraphs.empty();
3411 par = &std::get<DocPara>(m_paragraphs.back());
3412 if (markFirst)
3413 {
3414 par->markFirst();
3415 }
3416 par->markLast();
3417 }
3418
3419 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) break;
3420
3421 } while (retval.is(TokenRetval::RetVal_CloseXml) &&
3422 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_PARAM &&
3423 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_TYPEPARAM &&
3424 Mappers::htmlTagMapper->map(parser()->context.token->name)!=HtmlTagType::XML_EXCEPTION);
3425
3426 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF)) /* premature end of comment block */
3427 {
3428 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unterminated param or exception tag");
3429 }
3430 else
3431 {
3432 retval = Token::make_RetVal_OK();
3433 }
3434
3435 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3436 return retval;
3437}
3438
3439//--------------------------------------------------------------------------
3440
3441Token DocParamSect::parse(const DString &cmdName,bool xmlContext, Direction d)
3442{
3443 AUTO_TRACE();
3444 Token retval = Token::make_RetVal_OK();
3445 auto ns = AutoNodeStack(parser(),thisVariant());
3446
3447 if (d!=Unspecified)
3448 {
3450 }
3451
3452 if (!children().empty() && std::holds_alternative<DocParamList>(children().back()))
3453 {
3454 DocParamList &lastPl = std::get<DocParamList>(children().back());
3455 lastPl.markLast(false);
3456 }
3457 bool markFirst = children().empty();
3460 if (markFirst)
3461 {
3462 pl->markFirst();
3463 }
3464 pl->markLast();
3465 if (xmlContext)
3466 {
3467 retval = pl->parseXml(cmdName);
3468 }
3469 else
3470 {
3471 retval = pl->parse(cmdName);
3472 }
3473 if (retval.is(TokenRetval::RetVal_EndParBlock))
3474 {
3475 retval = Token::make_RetVal_OK();
3476 }
3477
3478 AUTO_TRACE_EXIT("retval={}",retval.to_string());
3479 return retval;
3480}
3481
3482//--------------------------------------------------------------------------
3483
3489
3491{
3492 AUTO_TRACE();
3493 DocSimpleSect *ss=nullptr;
3494 bool needsSeparator = false;
3495 if (!children().empty() && // has previous element
3496 (ss=children().get_last<DocSimpleSect>()) && // was a simple sect
3497 ss->type()==t && // of same type
3498 t!=DocSimpleSect::User) // but not user defined
3499 {
3500 // append to previous section
3501 needsSeparator = true;
3502 }
3503 else // start new section
3504 {
3507 }
3508 Token rv = Token::make_RetVal_OK();
3509 if (xmlContext)
3510 {
3511 return ss->parseXml();
3512 }
3513 else
3514 {
3515 rv = ss->parse(t==DocSimpleSect::User,needsSeparator);
3516 }
3517 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3518}
3519
3522 bool xmlContext=false,
3523 int direction=DocParamSect::Unspecified)
3524{
3525 AUTO_TRACE();
3526 DocParamSect *ps = nullptr;
3527 if (!children().empty() && // previous element
3528 (ps=children().get_last<DocParamSect>()) && // was a param sect
3529 ps->type()==t) // of same type
3530 { // append to previous section ps
3531 }
3532 else // start new section
3533 {
3535 ps = children().get_last<DocParamSect>();
3536 }
3537 Token rv=ps->parse(cmdName,xmlContext,
3538 static_cast<DocParamSect::Direction>(direction));
3539 AUTO_TRACE_EXIT("retval={}",rv.to_string());
3540 return (!rv.is(TokenRetval::TK_NEWPARA)) ? rv : Token::make_RetVal_OK();
3541}
3542
3543void DocPara::handleEmoji(char cmdChar,const DString &cmdName)
3544{
3545 AUTO_TRACE();
3546 // get the argument of the emoji command.
3547 Token tok=parser()->tokenizer.lex();
3548 if (!tok.is(TokenRetval::TK_WHITESPACE))
3549 {
3550 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3551 cmdChar,cmdName);
3552 return;
3553 }
3555 tok=parser()->tokenizer.lex();
3556 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3557 {
3558 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"no emoji name given or unexpected end of comment block while parsing the "
3559 "argument of command '{:c}{}'",cmdChar,cmdName);
3561 return;
3562 }
3563 else if (!tok.is(TokenRetval::TK_WORD))
3564 {
3565 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3566 tok.to_string(),cmdChar,cmdName);
3568 return;
3569 }
3572}
3573
3574void DocPara::handleDoxyConfig(char cmdChar,const DString &cmdName)
3575{
3576 // get the argument of the cite command.
3577 Token tok=parser()->tokenizer.lex();
3578 if (!tok.is(TokenRetval::TK_WHITESPACE))
3579 {
3580 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3581 cmdChar,cmdName);
3582 return;
3583 }
3585 tok=parser()->tokenizer.lex();
3586 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3587 {
3588 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3589 "argument of command '{:c}{}'",cmdChar,cmdName);
3590 return;
3591 }
3592 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
3593 {
3594 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
3595 tok.to_string(),cmdChar,cmdName);
3596 return;
3597 }
3598 ConfigOption * opt = ConfigImpl::instance()->get(parser()->context.token->name);
3599 if (opt)
3600 {
3601 DString optionValue;
3602 switch (opt->kind())
3603 {
3605 optionValue = *(static_cast<ConfigBool*>(opt)->valueStringRef());
3606 break;
3608 optionValue = *(static_cast<ConfigString*>(opt)->valueRef());
3609 break;
3611 optionValue = *(static_cast<ConfigEnum*>(opt)->valueRef());
3612 break;
3614 optionValue = *(static_cast<ConfigInt*>(opt)->valueStringRef());
3615 break;
3617 {
3618 StringVector *lst = static_cast<ConfigList*>(opt)->valueRef();
3619 optionValue="";
3620 if (!lst->empty())
3621 {
3622 std::string lstFormat = theTranslator->trWriteList(static_cast<int>(lst->size())).str();
3623 static const reg::Ex marker(R"(@(\d+))");
3624 reg::Iterator it(lstFormat,marker);
3626 size_t index=0;
3627 // now replace all markers with the real text
3628 for ( ; it!=end ; ++it)
3629 {
3630 const auto &match = *it;
3631 size_t newIndex = match.position();
3632 size_t matchLen = match.length();
3633 optionValue += lstFormat.substr(index,newIndex-index);
3634 unsigned long entryIndex = std::stoul(match[1].str());
3635 if (entryIndex<(unsigned long)lst->size())
3636 {
3637 optionValue += lst->at(entryIndex);
3638 }
3639 index=newIndex+matchLen;
3640 }
3641 optionValue+=lstFormat.substr(index);
3642 }
3643 }
3644 break;
3646 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Obsolete setting for '{:c}{}': '{}'",
3647 cmdChar,cmdName,parser()->context.token->name);
3648 break;
3650 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),
3651 "Disabled setting (i.e. not supported in this doxygen executable) for '{:c}{}': '{}'",
3652 cmdChar,cmdName,parser()->context.token->name);
3653 break;
3655 // nothing to show here
3656 break;
3657 }
3658 if (!optionValue.empty())
3659 {
3660 children().append<DocWord>(parser(),thisVariant(),optionValue);
3661 }
3662 }
3663 else
3664 {
3665 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Unknown option for '{:c}{}': '{}'",
3666 cmdChar,cmdName,parser()->context.token->name);
3668 }
3670}
3671
3673{
3674 AUTO_TRACE();
3675 Token retval=parser()->tokenizer.lex();
3676 ASSERT(retval.is(TokenRetval::TK_WHITESPACE));
3678 retval=parser()->tokenizer.lex();
3679 if (retval.is(TokenRetval::RetVal_OK))
3680 {
3684 if (!ref->parse())
3685 {
3686 children().pop_back();
3687 }
3688 }
3690 return retval;
3691}
3692
3693
3694void DocPara::handleShowDate(char cmdChar,const DString &cmdName)
3695{
3696 AUTO_TRACE();
3697 DString fmt;
3698 DString date;
3699 Token tok=parser()->tokenizer.lex();
3700 if (!tok.is(TokenRetval::TK_WHITESPACE))
3701 {
3702 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
3703 cmdChar,cmdName);
3704 return;
3705 }
3707 tok = parser()->tokenizer.lex();
3708 if (!tok.is(TokenRetval::TK_WORD))
3709 {
3710 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <format> argument for command '{:c}{}'",
3711 cmdChar,cmdName);
3713 return;
3714 }
3715 fmt = parser()->context.token->name;
3716
3718 tok = parser()->tokenizer.lex();
3719
3720 DString specDateRaw = tok.is(TokenRetval::TK_WORD) ? parser()->context.token->name : DString();
3721 DString specDate = specDateRaw.stripWhiteSpace();
3722 bool specDateOnlyWS = !specDateRaw.empty() && specDate.empty();
3723 if (!specDate.empty() && !tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3724 {
3725 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}'",
3726 cmdChar,cmdName);
3728 return;
3729 }
3730
3731 std::tm dat{};
3732 int specFormat=0;
3733 DString err = dateTimeFromString(specDate,dat,specFormat);
3734 if (!err.empty())
3735 {
3736 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '{:c}{}': {}",
3737 cmdChar,cmdName,err);
3739 return;
3740 }
3741
3742 int usedFormat=0;
3743 DString dateTimeStr = formatDateTime(fmt,dat,usedFormat);
3744
3745 // warn the user if the format contains markers that are not explicitly filled in
3746 for (int i=0;i<SF_NumBits;i++)
3747 {
3748 int bitMask = 1<<i;
3749 if ((usedFormat&bitMask) && !(specFormat&bitMask)) // a part was used in the format string but its value was not specified.
3750 {
3751 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.",
3752 cmdChar,cmdName,fmt,SF_bit2str(i),specDate,SF_bit2str(i));
3753 }
3754 }
3755
3756 children().append<DocWord>(parser(),thisVariant(),dateTimeStr);
3757 if (specDateOnlyWS) // specDate is only whitespace
3758 {
3760 }
3762}
3763
3765{
3766 AUTO_TRACE("cmdName={}",cmdName);
3767 DString saveCmdName = cmdName;
3768 Token tok=parser()->tokenizer.lex();
3769 if (!tok.is(TokenRetval::TK_WHITESPACE))
3770 {
3771 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3772 saveCmdName);
3773 return;
3774 }
3776 tok=parser()->tokenizer.lex();
3778 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3779 {
3780 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3781 "argument of command {}", saveCmdName);
3782 return;
3783 }
3784 else if (!tok.is(TokenRetval::TK_WORD))
3785 {
3786 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3787 tok.to_string(),saveCmdName);
3788 return;
3789 }
3790 auto it1 = children().size()>=1 ? std::prev(children().end()) : children().end();
3791 auto it2 = children().size()>=2 ? std::prev(it1) : children().end();
3792 DocNodeVariant *n1 = it1!=children().end() ? &(*it1) : nullptr;
3793 DocNodeVariant *n2 = it2!=children().end() ? &(*it2) : nullptr;
3794 //TODO get from context the stripCodeComments()
3795 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
3799 stripCodeComments,
3802 );
3804 DocIncOperator *n1_docIncOp = std::get_if<DocIncOperator>(n1);
3805 DocWhiteSpace *n1_docWs = std::get_if<DocWhiteSpace >(n1);
3806 DocIncOperator *n2_docIncOp = std::get_if<DocIncOperator>(n2);
3807 bool isFirst = !n1 || // no last node
3808 (!n1_docIncOp && !n1_docWs) || // last node is not operator or whitespace
3809 (n1_docWs && n2 && !n2_docIncOp); // last node is not operator
3810 op->markFirst(isFirst);
3811 op->markLast(true);
3812 if (n1_docIncOp)
3813 {
3814 n1_docIncOp->markLast(false);
3815 }
3816 else if (n1_docWs && n2_docIncOp)
3817 {
3818 n2_docIncOp->markLast(false);
3819 }
3820 op->parse();
3821}
3822
3823template<class T>
3824void DocPara::handleFile(const DString &cmdName)
3825{
3826 AUTO_TRACE("cmdName={}",cmdName);
3827 DString saveCmdName = cmdName;
3828 Token tok=parser()->tokenizer.lex();
3829 if (!tok.is(TokenRetval::TK_WHITESPACE))
3830 {
3831 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3832 saveCmdName);
3833 return;
3834 }
3836 tok=parser()->tokenizer.lex();
3838 if (!tok.is(TokenRetval::TK_WORD))
3839 {
3840 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3841 tok.to_string(),saveCmdName);
3842 return;
3843 }
3844 DString name = parser()->context.token->name;
3845 children().append<T>(parser(),thisVariant(),name,
3849 auto df = children().get_last<T>();
3850 if (!df->parse())
3851 {
3852 children().pop_back();
3853 }
3854}
3855
3862
3863void DocPara::handleLink(const DString &cmdName,bool isJavaLink)
3864{
3865 AUTO_TRACE("cmdName={} isJavaLink={}",cmdName,isJavaLink);
3866 DString saveCmdName = cmdName;
3867 Token tok=parser()->tokenizer.lex();
3868 if (!tok.is(TokenRetval::TK_WHITESPACE))
3869 {
3870 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3871 saveCmdName);
3872 return;
3873 }
3875 tok=parser()->tokenizer.lex();
3876 if (!tok.is(TokenRetval::TK_WORD))
3877 {
3878 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"{} as the argument of {}",
3879 tok.to_string(),saveCmdName);
3880 return;
3881 }
3882 if (saveCmdName == "javalink")
3883 {
3885 parser()->context.nodeStack.size(),
3886 DocStyleChange::Code,cmdName,true);
3887 }
3890 DocLink *lnk = children().get_last<DocLink>();
3891 if (saveCmdName == "javalink")
3892 {
3894 parser()->context.nodeStack.size(),
3895 DocStyleChange::Code,cmdName,false);
3896 }
3897 DString leftOver = lnk->parse(isJavaLink);
3898 if (!leftOver.empty())
3899 {
3900 children().append<DocWord>(parser(),thisVariant(),leftOver);
3901 }
3902}
3903
3905{
3906 AUTO_TRACE("cmdName={}",cmdName);
3907 DString saveCmdName = cmdName;
3908 Token tok=parser()->tokenizer.lex();
3909 bool isBlock = false;
3910 bool trimLeft = false;
3911 bool localScope = false;
3912 bool stripCodeComments = Config_getBool(STRIP_CODE_COMMENTS);
3913 if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="{")
3914 {
3916 parser()->tokenizer.lex();
3918 StringVector optList=split(parser()->context.token->name.str(),",");
3919 auto contains = [&optList](const char *kw)
3920 {
3921 return std::find(optList.begin(),optList.end(),kw)!=optList.end();
3922 };
3923 localScope = contains("local");
3924 if (contains("nostrip"))
3925 {
3926 stripCodeComments = false;
3927 }
3928 else if (contains("strip"))
3929 {
3930 stripCodeComments = true;
3931 }
3932 if (t==DocInclude::Snippet && contains("trimleft"))
3933 {
3934 trimLeft = true;
3935 }
3936
3937 if (contains("lineno"))
3938 {
3942 }
3943 tok=parser()->tokenizer.lex();
3944 if (!tok.is(TokenRetval::TK_WHITESPACE))
3945 {
3946 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3947 saveCmdName);
3948 return;
3949 }
3950 }
3951 else if (tok.is(TokenRetval::TK_WORD) && parser()->context.token->name=="[")
3952 {
3954 parser()->tokenizer.lex();
3955 isBlock = (parser()->context.token->name.stripWhiteSpace() == "block");
3957 parser()->tokenizer.lex();
3958 }
3959 else if (!tok.is(TokenRetval::TK_WHITESPACE))
3960 {
3961 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\{} command",
3962 saveCmdName);
3963 return;
3964 }
3966 tok=parser()->tokenizer.lex();
3968 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
3969 {
3970 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
3971 "argument of command {}",saveCmdName);
3972 return;
3973 }
3974 else if (!tok.is(TokenRetval::TK_WORD))
3975 {
3976 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of {}",
3977 tok.to_string(),saveCmdName);
3978 return;
3979 }
3980 DString fileName = parser()->context.token->name;
3981 DString blockId;
3983 {
3984 if (fileName == "this") fileName=parser()->context.fileName;
3986 tok=parser()->tokenizer.lex();
3988 if (!tok.is(TokenRetval::TK_WORD))
3989 {
3990 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected block identifier, but found token {} instead while parsing the {} command",
3991 tok.to_string(),saveCmdName);
3992 return;
3993 }
3994 blockId = "["+parser()->context.token->name+"]";
3995 }
3996
3998 thisVariant(),
3999 fileName,
4000 localScope ? parser()->context.context : "",
4001 t,
4002 stripCodeComments,
4005 blockId,isBlock,trimLeft);
4007}
4008
4009void DocPara::handleSection(char cmdChar,const DString &cmdName)
4010{
4011 AUTO_TRACE("cmdName={}",cmdName);
4012 DString saveCmdName = cmdName;
4013 // get the argument of the section command.
4014 Token tok=parser()->tokenizer.lex();
4015 if (!tok.is(TokenRetval::TK_WHITESPACE))
4016 {
4017 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '{:c}{}' command",
4018 cmdChar,saveCmdName);
4019 return;
4020 }
4021 tok=parser()->tokenizer.lex();
4022 if (tok.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4023 {
4024 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected end of comment block while parsing the "
4025 "argument of command '{:c}{}'", cmdChar,saveCmdName);
4026 return;
4027 }
4028 else if (!tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD))
4029 {
4030 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token {} as the argument of '{:c}{}'",
4031 tok.to_string(),cmdChar,saveCmdName);
4032 return;
4033 }
4036 parser()->tokenizer.lex();
4038}
4039
4040Token DocPara::handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs,int level)
4041{
4042 AUTO_TRACE();
4043 children().append<DocHtmlHeader>(parser(),thisVariant(),tagHtmlAttribs,level);
4044 Token retval = children().get_last<DocHtmlHeader>()->parse();
4045 return retval.is(TokenRetval::RetVal_OK) ? Token::make_TK_NEWPARA() : retval;
4046}
4047
4048// For XML tags whose content is stored in attributes rather than
4049// contained within the element, we need a way to inject the attribute
4050// text into the current paragraph.
4051bool DocPara::injectToken(Token tok,const DString &tokText)
4052{
4053 AUTO_TRACE();
4054 parser()->context.token->name = tokText;
4055 return parser()->defaultHandleToken(thisVariant(),tok,children());
4056}
4057
4059{
4060 AUTO_TRACE();
4061 Token retval = parser()->tokenizer.lex();
4062 DString lang = parser()->context.token->name;
4063 if (!lang.empty() && lang.at(0)!='.')
4064 {
4065 lang="."+lang;
4066 }
4067 if (parser()->context.xmlComment)
4068 {
4069 parser()->context.token->verb = substitute(substitute(parser()->context.token->verb,"&lt;","<"),"&gt;",">");
4070 }
4071 // search for the first non-whitespace line, index is stored in li
4072 size_t i=0,li=0,l=parser()->context.token->verb.length();
4073 while (i<l && (parser()->context.token->verb.at(i)==' ' || parser()->context.token->verb.at(i)=='\n'))
4074 {
4075 if (parser()->context.token->verb.at(i)=='\n') li=i+1;
4076 i++;
4077 }
4080 stripIndentation(parser()->context.token->verb.mid(li)),
4084 false,lang);
4085 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4086 {
4087 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"code section ended without end marker");
4088 }
4090 AUTO_TRACE_EXIT("retval={}",retval.to_string());
4091 return retval;
4092}
4093
4095{
4096 if (parser()->context.memberDef) // inheriting docs from a member
4097 {
4098 const MemberDef *reMd = parser()->context.memberDef->reimplements();
4099 if (reMd) // member from which was inherited.
4100 {
4101 const MemberDef *thisMd = parser()->context.memberDef;
4102 bool hasParamCommand = false;
4103 bool hasReturnCommand = false;
4104 StringMultiSet retvalsFound;
4105 StringMultiSet paramsFound;
4106 { DocParser::AutoSaveContext saveContext(*parser());
4107 //printf("{InheritDocs:%s=>%s}\n",qPrint(parser()->context.memberDef->qualifiedName()),qPrint(reMd->qualifiedName()));
4108 parser()->context.scope=reMd->getOuterScope();
4109 if (parser()->context.scope!=Doxygen::globalScope)
4110 {
4112 }
4113 parser()->context.memberDef=reMd;
4114 while (!parser()->context.styleStack.empty()) parser()->context.styleStack.pop();
4115 while (!parser()->context.nodeStack.empty()) parser()->context.nodeStack.pop();
4116 parser()->context.copyStack.push_back(reMd);
4119 parser()->context.copyStack.pop_back();
4120 hasParamCommand = parser()->context.hasParamCommand;
4121 hasReturnCommand = parser()->context.hasReturnCommand;
4122 retvalsFound = parser()->context.retvalsFound;
4123 paramsFound = parser()->context.paramsFound;
4124 }
4125 parser()->context.hasParamCommand = hasParamCommand;
4126 parser()->context.hasReturnCommand = hasReturnCommand;
4127 parser()->context.retvalsFound = retvalsFound;
4128 parser()->context.paramsFound = paramsFound;
4129 parser()->context.memberDef = thisMd;
4130 }
4131 }
4132}
4133
4134
4135Token DocPara::handleCommand(char cmdChar, const DString &cmdName)
4136{
4137 AUTO_TRACE("cmdName={}",cmdName);
4138 Token retval = Token::make_RetVal_OK();
4139 CommandType cmdId = Mappers::cmdMapper->map(cmdName);
4140 switch (cmdId)
4141 {
4143 {
4144 std::string str{cmdChar};
4145 children().append<DocWord>(parser(),thisVariant(),str.c_str() + cmdName);
4146 if (isAliasCmd(cmdName.view()))
4147 {
4148 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Found unexpanded alias '{:c}{}'. Check if number of arguments passed is correct.",cmdChar,cmdName);
4149 }
4150 else
4151 {
4152 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Found unknown command '{:c}{}'",cmdChar,cmdName);
4153 }
4154 }
4155 break;
4158 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4160 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4161 break;
4164 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4166 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4167 break;
4170 retval=parser()->handleStyleArgument(thisVariant(),children(),cmdName);
4172 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
4173 break;
4176 break;
4179 break;
4182 break;
4185 break;
4188 break;
4191 break;
4194 break;
4197 break;
4200 break;
4203 break;
4207 break;
4212 break;
4215 break;
4218 break;
4221 break;
4224 break;
4227 break;
4230 break;
4233 break;
4235 parser()->context.inSeeBlock=true;
4237 parser()->context.inSeeBlock=false;
4238 break;
4242 break;
4245 break;
4248 break;
4251 break;
4254 break;
4257 break;
4260 break;
4263 break;
4266 break;
4269 break;
4272 break;
4275 break;
4278 break;
4281 break;
4284 break;
4287 break;
4289 {
4291 retval = children().get_last<DocSimpleList>()->parse();
4292 }
4293 break;
4295 {
4296 handleSection(cmdChar,cmdName);
4297 retval = Token::make_RetVal_Section();
4298 }
4299 break;
4301 {
4302 handleSection(cmdChar,cmdName);
4303 retval = Token::make_RetVal_Subsection();
4304 }
4305 break;
4307 {
4308 handleSection(cmdChar,cmdName);
4309 retval = Token::make_RetVal_Subsubsection();
4310 }
4311 break;
4313 {
4314 handleSection(cmdChar,cmdName);
4315 retval = Token::make_RetVal_Paragraph();
4316 }
4317 break;
4319 {
4320 handleSection(cmdChar,cmdName);
4321 retval = Token::make_RetVal_SubParagraph();
4322 }
4323 break;
4325 {
4326 handleSection(cmdChar,cmdName);
4327 retval = Token::make_RetVal_SubSubParagraph();
4328 }
4329 break;
4331 {
4333 retval = handleStartCode();
4334 }
4335 break;
4337 {
4339 retval = handleStartCode();
4340 }
4341 break;
4343 {
4345 retval = parser()->tokenizer.lex();
4347 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4348 {
4349 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"htmlonly section ended without end marker");
4350 }
4352 }
4353 break;
4355 {
4357 retval = parser()->tokenizer.lex();
4359 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4360 {
4361 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"manonly section ended without end marker");
4362 }
4364 }
4365 break;
4367 {
4369 retval = parser()->tokenizer.lex();
4371 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4372 {
4373 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"rtfonly section ended without end marker");
4374 }
4376 }
4377 break;
4379 {
4381 retval = parser()->tokenizer.lex();
4383 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4384 {
4385 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"latexonly section ended without end marker");
4386 }
4388 }
4389 break;
4391 {
4393 retval = parser()->tokenizer.lex();
4395 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4396 {
4397 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"xmlonly section ended without end marker");
4398 }
4400 }
4401 break;
4403 {
4405 retval = parser()->tokenizer.lex();
4407 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4408 {
4409 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"docbookonly section ended without end marker");
4410 }
4412 }
4413 break;
4415 {
4418 parser()->tokenizer.lex();
4419
4420 DString fullMatch = parser()->context.token->verb;
4421 size_t idx = fullMatch.find('{');
4422 size_t idxEnd = idx!=DString::npos ? fullMatch.find("}",idx+1) : DString::npos;
4423 StringVector optList;
4424 if (idx!=DString::npos && idxEnd!=DString::npos) // options present
4425 {
4426 DString optStr = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4427 optList = split(optStr.str(),",");
4428 for (const auto &opt : optList)
4429 {
4430 if (opt.empty()) continue;
4431 DString locOpt(opt);
4432 locOpt = locOpt.stripWhiteSpace().lower();
4433 if (locOpt == "code")
4434 {
4436 }
4437 else if (!locOpt.empty())
4438 {
4439 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Unknown option '{}' for '\\iliteral'",opt);
4440 }
4441 }
4442 }
4443
4445 retval = parser()->tokenizer.lex();
4447 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4448 {
4449 if (t == DocVerbatim::JavaDocCode)
4450 {
4451 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"javadoc code section ended without end marker");
4452 }
4453 else
4454 {
4455 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"javadoc literal section ended without end marker");
4456 }
4457 }
4459 }
4460 break;
4463 {
4464 if (cmdId == CommandType::CMD_VERBATIM)
4465 {
4467 }
4468 else
4469 {
4471 }
4472 retval = parser()->tokenizer.lex();
4474 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4475 {
4476 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"verbatim section ended without end marker");
4477 }
4479 }
4480 break;
4482 {
4491 DString width,height;
4492 parser()->defaultHandleTitleAndSize(CommandType::CMD_DOT,&children().back(),dv->children(),width,height);
4494 retval = parser()->tokenizer.lex();
4495 dv->setText(parser()->context.token->verb);
4496 dv->setWidth(width);
4497 dv->setHeight(height);
4498 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4499 if (!Config_getBool(HAVE_DOT))
4500 {
4501 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"ignoring \\dot command because HAVE_DOT is not set");
4502 children().pop_back();
4503 }
4504 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4505 {
4506 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"dot section ended without end marker");
4507 }
4509 }
4510 break;
4512 {
4521 DString width,height;
4522 parser()->defaultHandleTitleAndSize(CommandType::CMD_MSC,&children().back(),dv->children(),width,height);
4524 retval = parser()->tokenizer.lex();
4525 dv->setText(parser()->context.token->verb);
4526 dv->setWidth(width);
4527 dv->setHeight(height);
4528 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4529 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4530 {
4531 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"msc section ended without end marker");
4532 }
4534 }
4535 break;
4537 {
4538 bool plantumlEnabled = PlantumlManager::isEnabled();
4540 parser()->tokenizer.lex();
4541 DString fullMatch = parser()->context.token->sectionId;
4542 DString sectionId = "";
4543 size_t idx = fullMatch.find('{');
4544 size_t idxEnd = idx!=DString::npos ? fullMatch.find("}",idx+1) : DString::npos;
4545 StringVector optList;
4546 DString engine;
4547 if (idx!=DString::npos && idxEnd!=DString::npos) // options present
4548 {
4549 DString optStr = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4550 optList = split(optStr.str(),",");
4551 for (const auto &opt : optList)
4552 {
4553 if (opt.empty()) continue;
4554 bool found = false;
4555 DString locOpt(opt);
4556 locOpt = locOpt.stripWhiteSpace().lower();
4557 if (g_plantumlEngine.find(locOpt.str())!=g_plantumlEngine.end())
4558 {
4559 if (!engine.empty())
4560 {
4561 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(), "Multiple definition of engine for '\\startuml'");
4562 }
4563 engine = locOpt;
4564 found = true;
4565 }
4566 if (!found)
4567 {
4568 if (sectionId.empty())
4569 {
4570 sectionId = opt;
4571 }
4572 else
4573 {
4574 warn(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Multiple use of filename for '\\startuml'");
4575 }
4576 }
4577 }
4578 }
4579 else
4580 {
4581 sectionId = parser()->context.token->sectionId;
4582 }
4583 if (engine.empty()) engine = "uml";
4584
4585 if (sectionId.empty())
4586 {
4588 retval = parser()->tokenizer.lex();
4589 assert(retval.is(TokenRetval::RetVal_OK));
4590
4591 sectionId = parser()->context.token->sectionId;
4592 sectionId = sectionId.stripWhiteSpace();
4593 }
4594
4595 DString plantFile(sectionId);
4600 false,plantFile);
4602 dv->setEngine(engine);
4604 DString width,height;
4605 parser()->defaultHandleTitleAndSize(CommandType::CMD_STARTUML,&children().back(),dv->children(),width,height);
4607 retval = parser()->tokenizer.lex();
4608 int line = 0;
4609 DString trimmedVerb = stripLeadingAndTrailingEmptyLines(parser()->context.token->verb,line);
4610 if (engine == "ditaa")
4611 {
4612 dv->setUseBitmap(true);
4613 }
4614 else if (engine == "uml")
4615 {
4616 size_t i = trimmedVerb.find('\n');
4617 DString firstLine = i==DString::npos ? trimmedVerb : trimmedVerb.left(i);
4618 if (firstLine.stripWhiteSpace() == "ditaa") dv->setUseBitmap(true);
4619 }
4620 dv->setText(trimmedVerb);
4621 dv->setWidth(width);
4622 dv->setHeight(height);
4623 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4624 if (!plantumlEnabled)
4625 {
4626 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"ignoring \\startuml command because neither PLANTUML_JAR_PATH nor PLANTUML_TOOL is set");
4627 children().pop_back();
4628 }
4629 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4630 {
4631 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"startuml section ended without end marker");
4632 }
4634 }
4635 break;
4637 {
4639 parser()->tokenizer.lex();
4640 DString fullMatch = parser()->context.token->sectionId;
4641 DString sectionId = "";
4642 size_t idx = fullMatch.find('{');
4643 size_t idxEnd = idx!=DString::npos ? fullMatch.find("}",idx+1) : DString::npos;
4644 if (idx!=DString::npos && idxEnd!=DString::npos) // options present
4645 {
4646 sectionId = fullMatch.mid(idx+1,idxEnd-idx-1).stripWhiteSpace();
4647 }
4648 else
4649 {
4650 sectionId = parser()->context.token->sectionId;
4651 }
4652
4653 if (sectionId.empty())
4654 {
4656 retval = parser()->tokenizer.lex();
4657 assert(retval.is(TokenRetval::RetVal_OK));
4658
4659 sectionId = parser()->context.token->sectionId;
4660 sectionId = sectionId.stripWhiteSpace();
4661 }
4662
4663 DString mermaidFile(sectionId);
4668 false,mermaidFile);
4671 DString width,height;
4672 parser()->defaultHandleTitleAndSize(CommandType::CMD_MERMAID,&children().back(),dv->children(),width,height);
4674 retval = parser()->tokenizer.lex();
4675 int line = 0;
4676 DString trimmedVerb = stripLeadingAndTrailingEmptyLines(parser()->context.token->verb,line);
4677 dv->setText(trimmedVerb);
4678 dv->setWidth(width);
4679 dv->setHeight(height);
4680 dv->setLocation(parser()->context.fileName,parser()->tokenizer.getLineNr());
4681 if (retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF))
4682 {
4683 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"mermaid section ended without end marker");
4684 }
4686 }
4687 break;
4689 retval = Token::make_RetVal_EndParBlock();
4690 break;
4707 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4708 break;
4710 retval = handleParamSection(cmdName,DocParamSect::Param,false,parser()->context.token->paramDir);
4712 break;
4714 retval = handleParamSection(cmdName,DocParamSect::TemplateParam,false,parser()->context.token->paramDir);
4715 break;
4717 retval = handleParamSection(cmdName,DocParamSect::RetVal);
4718 break;
4721 break;
4723 retval = handleXRefItem();
4724 break;
4726 {
4728 }
4729 break;
4732 {
4734 }
4735 break;
4737 {
4739 }
4740 break;
4742 {
4746 retval = children().get_last<DocIndexEntry>()->parse();
4747 }
4748 break;
4750 retval = Token::make_RetVal_Internal();
4751 break;
4753 retval = Token::make_RetVal_EndInternal();
4754 break;
4756 {
4758 retval = children().get_last<DocParBlock>()->parse();
4759 }
4760 break;
4761 case CommandType::CMD_COPYDOC: // fall through
4762 case CommandType::CMD_COPYBRIEF: // fall through
4764 //retval = Token::make_RetVal_CopyDoc();
4765 // these commands should already be resolved by processCopyDoc()
4766 break;
4769 break;
4772 break;
4775 break;
4778 break;
4781 break;
4784 break;
4787 break;
4790 break;
4793 break;
4796 break;
4799 break;
4802 break;
4805 break;
4808 break;
4811 break;
4814 break;
4817 break;
4819 if (!Config_getBool(HAVE_DOT))
4820 {
4821 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
4822 "ignoring \\dotfile command because HAVE_DOT is not set");
4823 }
4824 else
4825 {
4826 handleFile<DocDotFile>(cmdName);
4827 }
4828 break;
4831 break;
4833 handleFile<DocMscFile>(cmdName);
4834 break;
4836 handleFile<DocDiaFile>(cmdName);
4837 break;
4840 break;
4843 break;
4845 handleLink(cmdName,false);
4846 break;
4848 handleLink(cmdName,true);
4849 break;
4851 {
4853 }
4854 break;
4856 handleEmoji(cmdChar,cmdName);
4857 break;
4859 handleDoxyConfig(cmdChar,cmdName);
4860 break;
4862 // fall through
4864 parser()->handleRef(thisVariant(),children(),cmdChar,cmdName);
4865 break;
4867 {
4870 }
4871 break;
4873 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4874 break;
4876 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command '{:c}{}'",cmdChar,parser()->context.token->name);
4877 break;
4879 {
4881 }
4882 break;
4883 //case CommandType::CMD_LANGSWITCH:
4884 // retval = handleLanguageSwitch();
4885 // break;
4887 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected command {}",parser()->context.token->name);
4888 {
4891 }
4892 break;
4895 break;
4897 handleShowDate(cmdChar,cmdName);
4898 break;
4900 parser()->handleILine(cmdChar,cmdName);
4901 break;
4903 parser()->handleIFile(cmdChar,cmdName);
4904 break;
4906 {
4908 (void)parser()->tokenizer.lex();
4910 //printf("Found scope='%s'\n",qPrint(parser()->context.context));
4912 }
4913 break;
4914 default:
4915 // we should not get here!
4916 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' in paragraph context",cmdName);
4917 break;
4918 }
4919 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_OK,TokenRetval::RetVal_SimpleSec
4920 TokenRetval::TK_LISTITEM,TokenRetval::TK_ENDLIST,TokenRetval::TK_NEWPARA
4921 TokenRetval::RetVal_Section,TokenRetval::RetVal_EndList
4922 TokenRetval::RetVal_Internal,TokenRetval::RetVal_SwitchLang
4923 TokenRetval::RetVal_EndInternal)
4924 );
4925 AUTO_TRACE_EXIT("retval={}",retval.to_string());
4926 return retval;
4927}
4928
4929static bool findAttribute(const HtmlAttribList &tagHtmlAttribs,
4930 const char *attrName,
4931 DString *result)
4932{
4933
4934 for (const auto &opt : tagHtmlAttribs)
4935 {
4936 if (opt.name==attrName)
4937 {
4938 *result = opt.value;
4939 return true;
4940 }
4941 }
4942 return false;
4943}
4944
4945Token DocPara::handleHtmlStartTag(const DString &tagName,const HtmlAttribList &tagHtmlAttribs)
4946{
4947 AUTO_TRACE("tagName={} #tagHtmlAttrs={}",tagName,tagHtmlAttribs.size());
4948 Token retval = Token::make_RetVal_OK();
4949 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
4950 if (parser()->context.token->emptyTag && !(tagId>HtmlTagType::XML_CmdMask) &&
4953 {
4954 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"HTML tag ('<{}/>') may not use the 'empty tag' XHTML syntax.",
4955 tagName);
4956 }
4957 switch (tagId)
4958 {
4960 if (!parser()->context.token->emptyTag)
4961 {
4963 tagHtmlAttribs,DocHtmlList::Unordered);
4964 retval=children().get_last<DocHtmlList>()->parse();
4965 }
4966 break;
4968 if (!parser()->context.token->emptyTag)
4969 {
4971 tagHtmlAttribs,DocHtmlList::Ordered);
4972 retval=children().get_last<DocHtmlList>()->parse();
4973 }
4974 break;
4976 if (parser()->context.token->emptyTag) break;
4978 {
4979 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <li> tag found");
4980 }
4981 else
4982 {
4983 retval = Token::make_RetVal_ListItem();
4984 }
4985 break;
4987 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Bold,tagName,&parser()->context.token->attribs);
4988 break;
4990 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::S,tagName,&parser()->context.token->attribs);
4991 break;
4993 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Strike,tagName,&parser()->context.token->attribs);
4994 break;
4996 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Del,tagName,&parser()->context.token->attribs);
4997 break;
4999 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Underline,tagName,&parser()->context.token->attribs);
5000 break;
5002 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Ins,tagName,&parser()->context.token->attribs);
5003 break;
5005 if (parser()->context.token->emptyTag) break;
5006 if (parser()->context.xmlComment)
5007 // for C# source or inside a <summary> or <remark> section we
5008 // treat <code> as an XML tag (so similar to @code)
5009 {
5011 retval = handleStartCode();
5012 }
5013 else // normal HTML markup
5014 {
5015 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
5016 }
5017 break;
5019 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Kbd,tagName,&parser()->context.token->attribs);
5020 break;
5022 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Typewriter,tagName,&parser()->context.token->attribs);
5023 break;
5025 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Italic,tagName,&parser()->context.token->attribs);
5026 break;
5028 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Div,tagName,&parser()->context.token->attribs);
5029 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Div,tagName);
5030 break;
5032 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Span,tagName,&parser()->context.token->attribs);
5033 if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant(),children(),DocStyleChange::Span,tagName);
5034 break;
5036 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Subscript,tagName,&parser()->context.token->attribs);
5037 break;
5039 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Superscript,tagName,&parser()->context.token->attribs);
5040 break;
5042 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Center,tagName,&parser()->context.token->attribs);
5043 break;
5045 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Small,tagName,&parser()->context.token->attribs);
5046 break;
5048 if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Cite,tagName,&parser()->context.token->attribs);
5049 break;
5051 if (parser()->context.token->emptyTag) break;
5052 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Preformatted,tagName,&parser()->context.token->attribs);
5054 parser()->tokenizer.setInsidePre(true);
5055 break;
5057 retval = Token::make_TK_NEWPARA();
5058 break;
5060 if (!parser()->context.token->emptyTag)
5061 {
5062 children().append<DocHtmlDescList>(parser(),thisVariant(),tagHtmlAttribs);
5063 retval=children().get_last<DocHtmlDescList>()->parse();
5064 }
5065 break;
5067 if (insideDL(thisVariant()))
5068 {
5069 retval = Token::make_RetVal_DescTitle();
5070 }
5071 else
5072 {
5073 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dt> found");
5074 }
5075 break;
5077 if (insideDL(thisVariant()))
5078 {
5079 retval = Token::make_RetVal_DescData();
5080 }
5081 else
5082 {
5083 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <dd> found");
5084 }
5085 break;
5087 if (!parser()->context.token->emptyTag)
5088 {
5089 children().append<DocHtmlTable>(parser(),thisVariant(),tagHtmlAttribs);
5090 retval=children().get_last<DocHtmlTable>()->parse();
5091 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5092 }
5093 break;
5095 retval = Token::make_RetVal_TableRow();
5096 break;
5098 retval = Token::make_RetVal_TableCell();
5099 break;
5101 retval = Token::make_RetVal_TableHCell();
5102 break;
5106 // for time being ignore </t....> tag
5107 break;
5109 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag <caption> found");
5110 break;
5112 {
5113 children().append<DocLineBreak>(parser(),thisVariant(),tagHtmlAttribs);
5114 }
5115 break;
5117 {
5118 children().append<DocHorRuler>(parser(),thisVariant(),tagHtmlAttribs);
5119 }
5120 break;
5122 retval = parser()->handleAHref(thisVariant(),children(),tagHtmlAttribs);
5123 break;
5125 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,1);
5126 break;
5128 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,2);
5129 break;
5131 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,3);
5132 break;
5134 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,4);
5135 break;
5137 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,5);
5138 break;
5140 if (!parser()->context.token->emptyTag) retval=handleHtmlHeader(tagHtmlAttribs,6);
5141 break;
5143 {
5144 parser()->handleImg(thisVariant(),children(),tagHtmlAttribs);
5145 }
5146 break;
5148 if (!parser()->context.token->emptyTag)
5149 {
5150 children().append<DocHtmlDetails>(parser(),thisVariant(),tagHtmlAttribs);
5151 retval=children().get_last<DocHtmlDetails>()->parse();
5152 }
5153 break;
5155 if (!parser()->context.token->emptyTag)
5156 {
5157 children().append<DocHtmlBlockQuote>(parser(),thisVariant(),tagHtmlAttribs);
5158 retval = children().get_last<DocHtmlBlockQuote>()->parse();
5159 }
5160 break;
5161
5164 {
5165 if (!parser()->context.token->emptyTag)
5166 {
5168 while (n && !std::holds_alternative<DocHtmlDetails>(*n)) n=::parent(n);
5169 DocHtmlDetails *d = std::get_if<DocHtmlDetails>(n);
5170 if (d)
5171 {
5172 if (!d->summary()) // details section does not have a summary yet
5173 {
5174 d->parseSummary(n,parser()->context.token->attribs);
5175 }
5176 else
5177 {
5178 retval = Token::make_TK_NEWPARA();
5179 }
5180 }
5181 }
5182 }
5183 break;
5186 parser()->context.xmlComment=true;
5187 // fall through
5190 if (!children().empty())
5191 {
5192 retval = Token::make_TK_NEWPARA();
5193 }
5194 break;
5196 if (insideTable(thisVariant()))
5197 {
5198 retval = Token::make_RetVal_TableCell();
5199 }
5200 break;
5201 case HtmlTagType::XML_C:
5202 parser()->handleStyleEnter(thisVariant(),children(),DocStyleChange::Code,tagName,&parser()->context.token->attribs);
5203 break;
5206 {
5207 parser()->context.xmlComment=true;
5208 DString paramName;
5209 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5210 {
5211 if (paramName.empty())
5212 {
5213 if (Config_getBool(WARN_NO_PARAMDOC))
5214 {
5215 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"empty 'name' attribute for <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5216 }
5217 }
5218 else
5219 {
5220 retval = handleParamSection(paramName,
5222 true);
5223 }
5224 }
5225 else
5226 {
5227 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}> tag.",tagId==HtmlTagType::XML_PARAM?"":"type");
5228 }
5229 }
5230 break;
5233 {
5234 DString paramName;
5235 if (findAttribute(tagHtmlAttribs,"name",&paramName))
5236 {
5237 //printf("paramName=%s\n",qPrint(paramName));
5239 children().append<DocWord>(parser(),thisVariant(),paramName);
5241 if (!retval.is(TokenRetval::TK_WORD)) children().append<DocWhiteSpace>(parser(),thisVariant()," ");
5242 }
5243 else
5244 {
5245 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'name' attribute from <param{}ref> tag.",tagId==HtmlTagType::XML_PARAMREF?"":"type");
5246 }
5247 }
5248 break;
5250 {
5251 parser()->context.xmlComment=true;
5252 DString exceptName;
5253 if (findAttribute(tagHtmlAttribs,"cref",&exceptName))
5254 {
5255 unescapeCRef(exceptName);
5256 retval = handleParamSection(exceptName,DocParamSect::Exception,true);
5257 }
5258 else
5259 {
5260 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <exception> tag.");
5261 }
5262 }
5263 break;
5266 if (insideTable(thisVariant()))
5267 {
5268 retval = Token::make_RetVal_TableRow();
5269 }
5270 else if (insideUL(thisVariant()) || insideOL(thisVariant()))
5271 {
5272 retval = Token::make_RetVal_ListItem();
5273 }
5274 else
5275 {
5276 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"lonely <item> tag found");
5277 }
5278 break;
5280 parser()->context.xmlComment=true;
5283 break;
5285 if (insideTable(thisVariant()))
5286 {
5287 retval = Token::make_RetVal_TableCell();
5288 }
5289 break;
5291 // I'm not sure if <see> is the same as <seealso> or if it
5292 // should you link a member without producing a section. The
5293 // C# specification is extremely vague about this (but what else
5294 // can we expect from Microsoft...)
5295 {
5296 DString cref;
5297 //printf("HtmlTagType::XML_SEE: empty tag=%d\n",parser()->context.token->emptyTag);
5298 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5299 {
5300 unescapeCRef(cref);
5301 if (parser()->context.token->emptyTag) // <see cref="..."/> style
5302 {
5303 bool inSeeBlock = parser()->context.inSeeBlock;
5304 parser()->context.token->name = cref;
5305 parser()->context.inSeeBlock = true;
5307 parser()->context.inSeeBlock = inSeeBlock;
5308 }
5309 else // <see cref="...">...</see> style
5310 {
5313 DocLink *lnk = children().get_last<DocLink>();
5314 DString leftOver = lnk->parse(false,true);
5315 if (!leftOver.empty())
5316 {
5317 children().append<DocWord>(parser(),thisVariant(),leftOver);
5318 }
5319 }
5320 }
5321 else if (findAttribute(tagHtmlAttribs,"langword",&cref)) // <see langword="..."/> or <see langword="..."></see>
5322 {
5323 bool inSeeBlock = parser()->context.inSeeBlock;
5324 parser()->context.token->name = cref;
5325 parser()->context.inSeeBlock = true;
5329 parser()->context.inSeeBlock = inSeeBlock;
5330 }
5331 else
5332 {
5333 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' or 'langword' attribute from <see> tag.");
5334 }
5335 }
5336 break;
5338 {
5339 parser()->context.xmlComment=true;
5340 DString cref;
5341 if (findAttribute(tagHtmlAttribs,"cref",&cref))
5342 {
5343 unescapeCRef(cref);
5344 // Look for an existing "see" section
5345 DocNodeVariant *vss=nullptr;
5346 for (auto &n : children())
5347 {
5348 DocSimpleSect *candidate = std::get_if<DocSimpleSect>(&n);
5349 if (candidate && candidate->type()==DocSimpleSect::See)
5350 {
5351 vss = &n;
5352 }
5353 }
5354
5355 if (!vss) // start new section
5356 {
5358 vss = &children().back();
5359 }
5360
5361 std::get<DocSimpleSect>(*vss).appendLinkWord(cref);
5362 retval = Token::make_RetVal_OK();
5363 }
5364 else
5365 {
5366 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing 'cref' attribute from <seealso> tag.");
5367 }
5368 }
5369 break;
5371 {
5372 DString type;
5373 findAttribute(tagHtmlAttribs,"type",&type);
5375 HtmlAttribList emptyList;
5376 if (type=="number")
5377 {
5378 listType=DocHtmlList::Ordered;
5379 }
5380 if (type=="table")
5381 {
5382 children().append<DocHtmlTable>(parser(),thisVariant(),emptyList);
5383 retval=children().get_last<DocHtmlTable>()->parseXml();
5384 if (children().get_last<DocHtmlTable>()->children().empty()) children().pop_back();
5385 }
5386 else
5387 {
5388 children().append<DocHtmlList>(parser(),thisVariant(),emptyList,listType);
5389 retval=children().get_last<DocHtmlList>()->parseXml();
5390 }
5391 }
5392 break;
5395 // These tags are defined in .Net but are currently unsupported
5396 parser()->context.xmlComment=true;
5397 break;
5399 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag <{}> found", tagName);
5400 children().append<DocWord>(parser(),thisVariant(), "<"+tagName+parser()->context.token->attribsStr+">");
5401 break;
5404 break;
5405 default:
5406 // we should not get here!
5407 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected start tag {}",tagName);
5408 ASSERT(0);
5409 break;
5410 }
5411 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5412 return retval;
5413}
5414
5416{
5417 AUTO_TRACE("tagName={}",tagName);
5418 HtmlTagType tagId = Mappers::htmlTagMapper->map(tagName);
5419 Token retval = Token::make_RetVal_OK();
5420 switch (tagId)
5421 {
5423 if (!insideUL(thisVariant()))
5424 {
5425 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ul> tag without matching <ul>");
5426 }
5427 else
5428 {
5429 retval = Token::make_RetVal_EndList();
5430 }
5431 break;
5433 if (!insideOL(thisVariant()))
5434 {
5435 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </ol> tag without matching <ol>");
5436 }
5437 else
5438 {
5439 retval = Token::make_RetVal_EndList();
5440 }
5441 break;
5443 if (!insideLI(thisVariant()))
5444 {
5445 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </li> tag without matching <li>");
5446 }
5447 else
5448 {
5449 // ignore </li> tags
5450 }
5451 break;
5453 if (!insideDetails(thisVariant()))
5454 {
5455 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </details> tag without matching <details>");
5456 }
5457 else
5458 {
5459 retval = Token::make_RetVal_EndHtmlDetails();
5460 }
5461 break;
5464 {
5465 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"found </blockquote> tag without matching <blockquote>");
5466 }
5467 else
5468 {
5469 retval = Token::make_RetVal_EndBlockQuote();
5470 }
5471 break;
5474 break;
5477 break;
5480 break;
5483 break;
5486 break;
5489 break;
5492 break;
5495 break;
5498 break;
5501 break;
5504 break;
5507 break;
5510 break;
5513 break;
5516 break;
5519 break;
5522 break;
5525 setInsidePreformatted(false);
5526 parser()->tokenizer.setInsidePre(false);
5527 break;
5529 retval = Token::make_TK_NEWPARA();
5530 break;
5532 retval = Token::make_RetVal_EndDesc();
5533 break;
5535 // ignore </dt> tag
5536 break;
5538 // ignore </dd> tag
5539 break;
5541 retval = Token::make_RetVal_EndTable();
5542 break;
5544 retval = Token::make_RetVal_EndTableRow();
5545 break;
5547 retval = Token::make_RetVal_EndTableCell();
5548 break;
5550 retval = Token::make_RetVal_EndTableCell();
5551 break;
5555 // for time being ignore </t....> tag
5556 break;
5558 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </caption> found");
5559 break;
5561 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </br> tag found");
5562 break;
5564 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h1> found");
5565 break;
5567 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h2> found");
5568 break;
5570 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h3> found");
5571 break;
5573 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h4> found");
5574 break;
5576 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h5> found");
5577 break;
5579 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </h6> found");
5580 break;
5582 break;
5584 // warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Illegal </hr> tag found");
5585 break;
5587 //warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected tag </a> found");
5588 // ignore </a> tag (can be part of <a name=...></a>
5589 break;
5590
5592 break;
5594 retval = Token::make_TK_NEWPARA();
5595 break;
5608 retval = Token::make_RetVal_CloseXml();
5609 break;
5610 case HtmlTagType::XML_C:
5612 break;
5620 // These tags are defined in .Net but are currently unsupported
5621 break;
5623 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported xml/html tag </{}> found", tagName);
5624 children().append<DocWord>(parser(),thisVariant(),"</"+tagName+">");
5625 break;
5626 default:
5627 // we should not get here!
5628 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected end tag {}",tagName);
5629 ASSERT(0);
5630 break;
5631 }
5632 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5633 return retval;
5634}
5635
5637{
5638 // expected hierarchy:
5639 // 1. DocAutoListItem <- n
5640 // 2. DocAutoList <- parent(n)
5641 // 3. DocPara <- parent(parent(n))
5642
5643 // step 1
5644 if (!std::get_if<DocAutoListItem>(n)) // not inside a auto list item
5645 {
5646 return false;
5647 }
5648
5649 // step 2
5650 n = parent(n);
5651 int indent = 0;
5652 const auto &docAutoList = std::get_if<DocAutoList>(n);
5653 if (docAutoList) // capture indent
5654 {
5655 indent = docAutoList->indent();
5656 }
5657 else
5658 {
5659 return false;
5660 }
5661
5662 // step 3
5663 n = parent(n);
5664 const auto &docPara = std::get_if<DocPara>(n);
5665 if (docPara)
5666 {
5667 DString tagNameLower = DString(parser->context.token->name).lower();
5668 auto topStyleChange = [](const DocStyleChangeStack &stack) -> const DocStyleChange &
5669 {
5670 return std::get<DocStyleChange>(*stack.top());
5671 };
5672
5673 if (parser->context.styleStack.empty() || // no style change
5674 (topStyleChange(parser->context.styleStack).tagName()==tagNameLower && // correct style change
5675 topStyleChange(parser->context.styleStack).position()!=parser->context.nodeStack.size()) // wrong position, so normal close
5676 )
5677 {
5678 // insert an artificial 'end of autolist' marker and parse again
5679 DString indentStr;
5680 indentStr.fill(' ',indent);
5681 parser->tokenizer.unputString("\\ilinebr "+indentStr+".\\ilinebr"+indentStr+"</"+parser->context.token->name+">");
5682 return true;
5683 }
5684 }
5685 return false;
5686}
5687
5689{
5690 AUTO_TRACE();
5691 auto ns = AutoNodeStack(parser(),thisVariant());
5692 // handle style commands "inherited" from the previous paragraph
5694 Token tok=parser()->tokenizer.lex();
5695 Token retval = Token::make_TK_NONE();
5696 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
5697 {
5698reparsetoken:
5699 AUTO_TRACE_ADD("token '{}' at {}",tok.to_string(),parser()->tokenizer.getLineNr());
5700 if (tok.is_any_of(TokenRetval::TK_WORD,TokenRetval::TK_LNKWORD,TokenRetval::TK_SYMBOL,TokenRetval::TK_URL,
5701 TokenRetval::TK_COMMAND_AT,TokenRetval::TK_COMMAND_BS,TokenRetval::TK_HTMLTAG)
5702 )
5703 {
5704 AUTO_TRACE_ADD("name={}",parser()->context.token->name);
5705 }
5706 switch(tok.value())
5707 {
5708 case TokenRetval::TK_WORD:
5710 break;
5711 case TokenRetval::TK_LNKWORD:
5713 break;
5714 case TokenRetval::TK_URL:
5716 break;
5717 case TokenRetval::TK_WHITESPACE:
5718 {
5719 // prevent leading whitespace and collapse multiple whitespace areas
5720 if (insidePRE(thisVariant()) || // all whitespace is relevant
5721 (
5722 // remove leading whitespace
5723 !children().empty() &&
5724 // and whitespace after certain constructs
5728 )
5729 )
5730 {
5732 }
5733 }
5734 break;
5735 case TokenRetval::TK_LISTITEM:
5736 {
5737 AUTO_TRACE_ADD("found list item at {}",parser()->context.token->indent);
5738 const DocNodeVariant *n=parent();
5739 while (n && !std::holds_alternative<DocAutoList>(*n)) n=::parent(n);
5740 const DocAutoList *al = std::get_if<DocAutoList>(n);
5741 if (al) // we found an auto list up in the hierarchy
5742 {
5743 AUTO_TRACE_ADD("previous list item at {}",al->indent());
5744 if (al->indent()>=parser()->context.token->indent)
5745 // new item at the same or lower indent level
5746 {
5747 retval = Token::make_TK_LISTITEM();
5748 goto endparagraph;
5749 }
5750 }
5751
5752 // determine list depth
5753 int depth = 0;
5754 n=parent();
5755 while (n)
5756 {
5757 al = std::get_if<DocAutoList>(n);
5758 if (al && al->isEnumList()) depth++;
5759 n=::parent(n);
5760 }
5761
5762 // first item or sub list => create new list
5763 do
5764 {
5767 parser()->context.token->isEnumList,depth,
5769 al = children().get_last<DocAutoList>();
5770 retval = children().get_last<DocAutoList>()->parse();
5771 } while (retval.is(TokenRetval::TK_LISTITEM) && // new list
5772 al->indent()==parser()->context.token->indent // at same indent level
5773 );
5774
5775 // check the return value
5776 if (retval.is(TokenRetval::RetVal_SimpleSec)) // auto list ended due to simple section command
5777 {
5778 // Reparse the token that ended the section at this level,
5779 // so a new simple section will be started at this level.
5780 // This is the same as unputting the last read token and continuing.
5782 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5783 {
5786 tok = Token::make_TK_RCSTAG();
5787 }
5788 else // other section
5789 {
5790 tok = Token::make_TK_COMMAND_BS();
5791 }
5792 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5793 goto reparsetoken;
5794 }
5795 else if (retval.is(TokenRetval::TK_ENDLIST))
5796 {
5797 if (al->indent()>parser()->context.token->indent) // end list
5798 {
5799 goto endparagraph;
5800 }
5801 else // continue with current paragraph
5802 {
5803 }
5804 }
5805 else // paragraph ended due to TokenRetval::TK_NEWPARA, TokenRetval::TK_LISTITEM, or EOF
5806 {
5807 goto endparagraph;
5808 }
5809 }
5810 break;
5811 case TokenRetval::TK_ENDLIST:
5812 AUTO_TRACE_ADD("Found end of list inside of paragraph at line {}",parser()->tokenizer.getLineNr());
5813 if (std::get_if<DocAutoListItem>(parent()))
5814 {
5815 const DocAutoList *al = std::get_if<DocAutoList>(::parent(parent()));
5816 if (al && al->indent()>=parser()->context.token->indent)
5817 {
5818 // end of list marker ends this paragraph
5819 retval = Token::make_TK_ENDLIST();
5820 goto endparagraph;
5821 }
5822 else
5823 {
5824 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found "
5825 "has invalid indent level");
5826 }
5827 }
5828 else
5829 {
5830 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"End of list marker found without any preceding "
5831 "list items");
5832 }
5833 break;
5834 case TokenRetval::TK_COMMAND_AT:
5835 // fall through
5836 case TokenRetval::TK_COMMAND_BS:
5837 {
5838 // see if we have to start a simple section
5839 CommandType cmd = Mappers::cmdMapper->map(parser()->context.token->name);
5840 const DocNodeVariant *n=parent();
5841 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
5842 !std::holds_alternative<DocParamSect>(*n))
5843 {
5844 n=::parent(n);
5845 }
5847 {
5848 if (n) // already in a simple section
5849 {
5850 // simple section cannot start in this paragraph, need
5851 // to unwind the stack and remember the command.
5853 retval = Token::make_RetVal_SimpleSec();
5854 goto endparagraph;
5855 }
5856 }
5857 // see if we are in a simple list
5858 n=parent();
5859 while (n && !std::holds_alternative<DocSimpleListItem>(*n)) n=::parent(n);
5860 if (n)
5861 {
5862 if (cmd==CommandType::CMD_LI)
5863 {
5864 retval = Token::make_RetVal_ListItem();
5865 goto endparagraph;
5866 }
5867 }
5868
5869 // handle the command
5870 retval=handleCommand(tok.command_to_char(),parser()->context.token->name);
5871 AUTO_TRACE_ADD("handleCommand returns {}",retval.to_string());
5872
5873 // check the return value
5874 if (retval.is(TokenRetval::RetVal_SimpleSec))
5875 {
5876 // Reparse the token that ended the section at this level,
5877 // so a new simple section will be started at this level.
5878 // This is the same as unputting the last read token and continuing.
5880 if (parser()->context.token->name.startsWith("rcs:")) // RCS section
5881 {
5884 tok = Token::make_TK_RCSTAG();
5885 }
5886 else // other section
5887 {
5888 tok = Token::make_TK_COMMAND_BS();
5889 }
5890 AUTO_TRACE_ADD("reparsing command {}",parser()->context.token->name);
5891 goto reparsetoken;
5892 }
5893 else if (retval.value()>TokenRetval::TK_NONE && retval.value()<TokenRetval::RetVal_OK)
5894 {
5895 // the command ended with a new command, reparse this token
5896 tok = retval;
5897 goto reparsetoken;
5898 }
5899 else if (retval.value()!=TokenRetval::RetVal_OK) // end of file, end of paragraph, start or end of section
5900 // or some auto list marker
5901 {
5902 goto endparagraph;
5903 }
5904 }
5905 break;
5906 case TokenRetval::TK_HTMLTAG:
5907 {
5908 if (!parser()->context.token->endTag) // found a start tag
5909 {
5910 retval = handleHtmlStartTag(parser()->context.token->name,parser()->context.token->attribs);
5911 }
5912 else // found an end tag
5913 {
5915 {
5916 break; // new code has been pushed back to the scanner, need to reparse
5917 }
5918 retval = handleHtmlEndTag(parser()->context.token->name);
5919 }
5920 if (!retval.is(TokenRetval::RetVal_OK))
5921 {
5922 goto endparagraph;
5923 }
5924 }
5925 break;
5926 case TokenRetval::TK_SYMBOL:
5927 {
5928 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
5930 {
5932 }
5933 else
5934 {
5936 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
5937 parser()->context.token->name);
5938 }
5939 break;
5940 }
5941 case TokenRetval::TK_NEWPARA:
5942 retval = Token::make_TK_NEWPARA();
5943 goto endparagraph;
5944 case TokenRetval::TK_RCSTAG:
5945 {
5946 const DocNodeVariant *n=parent();
5947 while (n && !std::holds_alternative<DocSimpleSect>(*n) &&
5948 !std::holds_alternative<DocParamSect>(*n))
5949 {
5950 n=::parent(n);
5951 }
5952 if (n) // already in a simple section
5953 {
5954 // simple section cannot start in this paragraph, need
5955 // to unwind the stack and remember the command.
5958 retval = Token::make_RetVal_SimpleSec();
5959 goto endparagraph;
5960 }
5961
5962 // see if we are in a simple list
5964 children().get_last<DocSimpleSect>()->parseRcs();
5965 }
5966 break;
5967 default:
5968 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
5969 "Found unexpected token (id={})",tok.to_string());
5970 break;
5971 }
5972 tok=parser()->tokenizer.lex();
5973 }
5974 retval=Token::make_TK_NONE();
5975endparagraph:
5977 DocPara *par = std::get_if<DocPara>(parser()->context.nodeStack.top());
5978 if (!parser()->context.token->endTag && par &&
5979 retval.is(TokenRetval::TK_NEWPARA) && parser()->context.token->name.lower() == "p")
5980 {
5981 par->setAttribs(parser()->context.token->attribs);
5982 }
5983 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::TK_NEWPARA,TokenRetval::TK_LISTITEM,
5984 TokenRetval::TK_ENDLIST,TokenRetval::RetVal_OK)
5985 );
5986
5987 AUTO_TRACE_EXIT("retval={}",retval.to_string());
5988 return retval;
5989}
5990
5991//--------------------------------------------------------------------------
5992
5994{
5995 AUTO_TRACE("start {} level={}", parser()->context.token->sectionId, m_level);
5996 Token retval = Token::make_RetVal_OK();
5997 auto ns = AutoNodeStack(parser(),thisVariant());
5998
5999 if (!m_id.empty())
6000 {
6002 if (sec)
6003 {
6004 m_file = sec->fileName();
6005 m_anchor = sec->label();
6006 DString titleStr = sec->title();
6007 if (titleStr.empty()) titleStr = sec->label();
6009 DocTitle *title = &std::get<DocTitle>(*m_title);
6010 title->parseFromString(thisVariant(),titleStr);
6011 }
6012 }
6013
6014 // first parse any number of paragraphs
6015 bool isFirst=true;
6016 DocPara *lastPar=nullptr;
6017 do
6018 {
6020 DocPara *par = children().get_last<DocPara>();
6021 if (isFirst) { par->markFirst(); isFirst=false; }
6022 retval=par->parse();
6023 if (!par->empty())
6024 {
6025 if (lastPar) lastPar->markLast(false);
6026 lastPar = par;
6027 }
6028 else
6029 {
6030 children().pop_back();
6031 }
6032 if (retval.is(TokenRetval::TK_LISTITEM))
6033 {
6034 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6035 }
6036 if (retval.is(TokenRetval::RetVal_Internal))
6037 {
6039 retval = children().get_last<DocInternal>()->parse(m_level+1);
6040 if (retval.is(TokenRetval::RetVal_EndInternal))
6041 {
6042 retval = Token::make_RetVal_OK();
6043 }
6044 }
6045 } while (!retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF, TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6046 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph,
6047 TokenRetval::RetVal_SubSubParagraph, TokenRetval::RetVal_EndInternal)
6048 );
6049
6050 if (lastPar) lastPar->markLast();
6051
6052 while (true)
6053 {
6054 if (retval.is(TokenRetval::RetVal_Subsection) && m_level<=1)
6055 {
6056 // then parse any number of nested sections
6057 while (retval.is(TokenRetval::RetVal_Subsection)) // more sections follow
6058 {
6060 2,
6062 retval = children().get_last<DocSection>()->parse();
6063 }
6064 break;
6065 }
6066 else if (retval.is(TokenRetval::RetVal_Subsubsection) && m_level<=2)
6067 {
6068 if ((m_level <= 1) &&
6069 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6070 {
6071 warn_doc_error(parser()->context.fileName,
6072 parser()->tokenizer.getLineNr(),
6073 "Unexpected subsubsection command found inside {}!",
6075 }
6076 // then parse any number of nested sections
6077 while (retval.is(TokenRetval::RetVal_Subsubsection)) // more sections follow
6078 {
6080 3,
6082 retval = children().get_last<DocSection>()->parse();
6083 }
6084 if (!(m_level < 2 && retval.is(TokenRetval::RetVal_Subsection))) break;
6085 }
6086 else if (retval.is(TokenRetval::RetVal_Paragraph) && m_level<=3)
6087 {
6088 if ((m_level <= 2) &&
6089 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6090 {
6091 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6092 "Unexpected paragraph command found inside {}!",
6094 }
6095 // then parse any number of nested sections
6096 while (retval.is(TokenRetval::RetVal_Paragraph)) // more sections follow
6097 {
6099 4,
6101 retval = children().get_last<DocSection>()->parse();
6102 }
6103 if (!(m_level<3 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection)))) break;
6104 }
6105 else if (retval.is(TokenRetval::RetVal_SubParagraph) && m_level<=4)
6106 {
6107 if ((m_level <= 3) &&
6108 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6109 {
6110 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6111 "Unexpected subparagraph command found inside {}!",
6113 }
6114 // then parse any number of nested sections
6115 while (retval.is(TokenRetval::RetVal_SubParagraph)) // more sections follow
6116 {
6118 5,
6120 retval = children().get_last<DocSection>()->parse();
6121 }
6122 if (!(m_level<4 && (retval.is_any_of(TokenRetval::RetVal_Subsection,TokenRetval::RetVal_Subsubsection,TokenRetval::RetVal_Paragraph)))) break;
6123 }
6124 else if (retval.is(TokenRetval::RetVal_SubSubParagraph) && m_level<=5)
6125 {
6126 if ((m_level <= 4) &&
6127 !AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6128 {
6129 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),
6130 "Unexpected subsubparagraph command found inside {}!",
6132 }
6133 // then parse any number of nested sections
6134 while (retval.is(TokenRetval::RetVal_SubSubParagraph)) // more sections follow
6135 {
6137 6,
6139 retval = children().get_last<DocSection>()->parse();
6140 }
6141 if (!(m_level<5 && (retval.is_any_of( TokenRetval::RetVal_Subsection, TokenRetval::RetVal_Subsubsection,
6142 TokenRetval::RetVal_Paragraph, TokenRetval::RetVal_SubParagraph)))) break;
6143 }
6144 else
6145 {
6146 break;
6147 }
6148 }
6149
6150 INTERNAL_ASSERT(retval.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF,
6151 TokenRetval::RetVal_Section, TokenRetval::RetVal_Subsection,
6152 TokenRetval::RetVal_Subsubsection, TokenRetval::RetVal_Paragraph,
6153 TokenRetval::RetVal_SubParagraph, TokenRetval::RetVal_SubSubParagraph,
6154 TokenRetval::RetVal_Internal, TokenRetval::RetVal_EndInternal)
6155 );
6156
6157 AUTO_TRACE_EXIT("retval={}", retval.to_string());
6158 return retval;
6159}
6160
6161//--------------------------------------------------------------------------
6162
6164{
6165 AUTO_TRACE();
6166 auto ns = AutoNodeStack(parser(),thisVariant());
6168
6169 Token tok = parser()->tokenizer.lex();
6170 while (!tok.is_any_of(TokenRetval::TK_NONE, TokenRetval::TK_EOF)) // get the next token
6171 {
6172 switch(tok.value())
6173 {
6174 case TokenRetval::TK_WORD:
6176 break;
6177 case TokenRetval::TK_WHITESPACE:
6179 break;
6180 case TokenRetval::TK_SYMBOL:
6181 {
6182 HtmlEntityMapper::SymType s = DocSymbol::decodeSymbol(parser()->context.token->name);
6184 {
6186 }
6187 else
6188 {
6189 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unsupported symbol '{}' found",
6190 parser()->context.token->name);
6191 }
6192 }
6193 break;
6194 case TokenRetval::TK_COMMAND_AT:
6195 // fall through
6196 case TokenRetval::TK_COMMAND_BS:
6197 switch (Mappers::cmdMapper->map(parser()->context.token->name))
6198 {
6201 break;
6204 break;
6207 break;
6210 break;
6213 break;
6216 break;
6219 break;
6222 break;
6225 break;
6229 break;
6234 break;
6237 break;
6240 break;
6243 break;
6246 break;
6249 break;
6252 break;
6255 break;
6256 default:
6257 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected command '{}' found",
6258 parser()->context.token->name);
6259 break;
6260 }
6261 break;
6262 default:
6263 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Unexpected token {}",
6264 tok.to_string());
6265 break;
6266 }
6267 tok = parser()->tokenizer.lex();
6268 }
6269
6271
6272}
6273
6274
6275//--------------------------------------------------------------------------
6276
6278{
6279 AUTO_TRACE();
6280 auto ns = AutoNodeStack(parser(),thisVariant());
6282 Token retval = Token::make_TK_NONE();
6283
6284 // first parse any number of paragraphs
6285 bool isFirst=true;
6286 DocPara *lastPar = nullptr;
6287 do
6288 {
6289 {
6291 DocPara *par = children().get_last<DocPara>();
6292 if (isFirst) { par->markFirst(); isFirst=false; }
6293 retval=par->parse();
6294 if (par->empty() && par->attribs().empty())
6295 {
6296 children().pop_back();
6297 }
6298 else
6299 {
6300 lastPar = par;
6301 }
6302 }
6303 auto checkParagraph = [this,&retval](Token t,int level,const char *sectionType,const char *parentSectionType) {
6304 if (retval == t)
6305 {
6306 if (!AnchorGenerator::instance().isGenerated(parser()->context.token->sectionId.str()))
6307 {
6308 warn_doc_error(parser()->context.fileName,
6309 parser()->tokenizer.getLineNr(),
6310 "found {} command (id: '{}') outside of {} context!",
6311 sectionType,parser()->context.token->sectionId,parentSectionType);
6312 }
6313 while (retval==t)
6314 {
6315 if (!parser()->context.token->sectionId.empty())
6316 {
6317 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6318 if (sec)
6319 {
6321 level,
6323 retval = children().get_last<DocSection>()->parse();
6324 }
6325 else
6326 {
6327 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid {} id '{}'; ignoring {}",
6328 sectionType,parser()->context.token->sectionId,sectionType);
6329 retval = Token::make_TK_NONE();
6330 }
6331 }
6332 else
6333 {
6334 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for {}; ignoring {}",sectionType,sectionType);
6335 retval = Token::make_TK_NONE();
6336 }
6337 }
6338 }
6339 };
6340 checkParagraph(Token::make_RetVal_SubSubParagraph(), 6, "subsubparagraph", "subparagraph" );
6341 checkParagraph(Token::make_RetVal_SubParagraph(), 5, "subparagraph", "paragraph" );
6342 checkParagraph(Token::make_RetVal_Paragraph(), 4, "paragraph", "subsubsection" );
6343 checkParagraph(Token::make_RetVal_Subsubsection(), 3, "subsubsection", "subsection" );
6344 checkParagraph(Token::make_RetVal_Subsection(), 2, "subsection", "section" );
6345
6346 if (retval.is(TokenRetval::TK_LISTITEM))
6347 {
6348 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid list item found");
6349 }
6350 if (retval.is(TokenRetval::RetVal_Internal))
6351 {
6353 retval = children().get_last<DocInternal>()->parse(1);
6354 }
6355 } while (!retval.is_any_of(TokenRetval::TK_NONE,TokenRetval::TK_EOF,TokenRetval::RetVal_Section));
6356 if (lastPar) lastPar->markLast();
6357
6358 //printf("DocRoot::parse() retval=%d %d\n",retval,TokenRetval::RetVal_Section);
6359 // then parse any number of level1 sections
6360 while (retval.is(TokenRetval::RetVal_Section))
6361 {
6362 if (!parser()->context.token->sectionId.empty())
6363 {
6364 const SectionInfo *sec=SectionManager::instance().find(parser()->context.token->sectionId);
6365 if (sec)
6366 {
6368 1,
6370 retval = children().get_last<DocSection>()->parse();
6371 }
6372 else
6373 {
6374 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Invalid section id '{}'; ignoring section",parser()->context.token->sectionId);
6375 retval = Token::make_TK_NONE();
6376 }
6377 }
6378 else
6379 {
6380 warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"Missing id for section; ignoring section");
6381 retval = Token::make_TK_NONE();
6382 }
6383 }
6384
6386}
bool isAliasCmd(std::string_view aliasCmd)
Definition aliases.cpp:518
static AnchorGenerator & instance()
Returns the singleton instance.
Definition anchor.cpp:38
Citation manager class.
Definition cite.h:85
const CiteInfo * find(const DString &label) const
Return the citation info for a given label.
Definition cite.cpp:103
static CitationManager & instance()
Definition cite.cpp:87
DString anchorPrefix() const
Definition cite.cpp:128
DString fileName() const
Definition cite.cpp:123
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:88
void clear()
Definition dstring.h:218
DString fill(char c, size_t len)
Fills a string with a predefined character.
Definition dstring.h:282
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:322
DString lower() const
Definition dstring.h:330
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:152
DString substr(size_t pos=0, size_t count=npos) const
Returns a substring of length count starting at pos.
Definition dstring.h:227
std::string_view view() const
Definition dstring.h:166
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:182
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:690
DString & append(char c)
Definition dstring.h:493
DString right(size_t len) const
Definition dstring.h:315
DString & prepend(const char *s)
Definition dstring.h:519
int contains(char c, bool cs=true) const
Definition dstring.cpp:81
size_t find(char c, size_t pos=0) const
Definition dstring.h:243
DString & sprintf(const char *format,...)
Definition dstring.cpp:30
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:341
DString left(size_t len) const
Definition dstring.h:310
const std::string & str() const
Definition dstring.h:649
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:161
bool startsWith(const char *s) const
Definition dstring.h:604
bool endsWith(const char *s) const
Definition dstring.h:621
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:155
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:214
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:3065
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:3058
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:3018
DString getText() const
Definition docnode.cpp:977
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:941
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:1213
DocDiaFile(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.cpp:1206
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:1135
DocDotFile(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.cpp:1128
Node representing an emoji.
Definition docnode.h:341
DocEmoji(DocParser *parser, DocNodeVariant *parent, const DString &symName)
Definition docnode.cpp:165
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:519
Token parse()
Definition docnode.cpp:1572
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:1779
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:1983
void setColumnIndex(uint32_t idx)
Definition docnode.h:1222
bool isFirst() const
Definition docnode.h:1206
Token parseXml()
Definition docnode.cpp:1887
void setRowIndex(uint32_t idx)
Definition docnode.h:1221
uint32_t rowSpan() const
Definition docnode.cpp:1921
void markLast(bool v=true)
Definition docnode.h:1209
void markFirst(bool v=true)
Definition docnode.h:1208
Alignment alignment() const
Definition docnode.cpp:1945
bool isHeading() const
Definition docnode.h:1205
const HtmlAttribList & attribs() const
Definition docnode.h:1210
Token parse()
Definition docnode.cpp:1853
uint32_t colSpan() const
Definition docnode.cpp:1933
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:1562
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:1382
Node representing a Html list.
Definition docnode.h:1009
Type m_type
Definition docnode.h:1020
Token parseXml()
Definition docnode.cpp:2865
Token parse()
Definition docnode.cpp:2790
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:2187
void setVisibleCells(uint32_t n)
Definition docnode.h:1261
bool isHeading() const
Definition docnode.cpp:2005
void setRowIndex(uint32_t idx)
Definition docnode.h:1266
Token parse()
Definition docnode.cpp:2080
Node Html summary.
Definition docnode.h:853
Node representing a HTML table.
Definition docnode.h:1274
Token parseXml()
Definition docnode.cpp:2366
size_t numberHeaderRows() const
Definition docnode.cpp:2258
std::unique_ptr< DocNodeVariant > m_caption
Definition docnode.h:1289
Token parse()
Definition docnode.cpp:2273
void computeTableGrid()
determines the location of all cells in a grid, resolving row and column spans.
Definition docnode.cpp:2423
size_t m_numCols
Definition docnode.h:1291
const DocNodeVariant * caption() const
Definition docnode.cpp:2253
bool hasCaption() const
Definition docnode.cpp:2248
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:1359
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:1374
bool isSVG() const
Definition docnode.cpp:1365
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:273
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:1682
DString m_entry
Definition docnode.h:562
Node representing an internal section of documentation.
Definition docnode.h:978
Token parse(int)
Definition docnode.cpp:1622
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:677
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:197
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:1289
DocMscFile(DocParser *parser, DocNodeVariant *parent, const DString &name, const DString &context, const DString &srcFile, int srcLine)
Definition docnode.cpp:1166
bool parse()
Definition docnode.cpp:1173
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:853
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:2959
Node representing a paragraph in the documentation tree.
Definition docnode.h:1089
void handleFile(const DString &cmdName)
Definition docnode.cpp:3824
void handleInheritDoc()
Definition docnode.cpp:4094
void handleShowDate(char cmdChar, const DString &cmdName)
Definition docnode.cpp:3694
DocPara(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:3484
void handleIncludeOperator(const DString &cmdName, DocIncOperator::Type t)
Definition docnode.cpp:3764
void handleLink(const DString &cmdName, bool isJavaLink)
Definition docnode.cpp:3863
Token handleCommand(char cmdChar, const DString &cmdName)
Definition docnode.cpp:4135
void handleInclude(const DString &cmdName, DocInclude::Type t)
Definition docnode.cpp:3904
Token handleHtmlEndTag(const DString &tagName)
Definition docnode.cpp:5415
Token handleHtmlStartTag(const DString &tagName, const HtmlAttribList &tagHtmlAttribs)
Definition docnode.cpp:4945
void markFirst(bool v=true)
Definition docnode.h:1094
Token handleSimpleSection(DocSimpleSect::Type t, bool xmlContext=false)
Definition docnode.cpp:3490
void handleEmoji(char cmdChar, const DString &cmdName)
Definition docnode.cpp:3543
bool isFirst() const
Definition docnode.h:1096
Token handleParamSection(const DString &cmdName, DocParamSect::Type t, bool xmlContext, int direction)
Definition docnode.cpp:3520
void setAttribs(const HtmlAttribList &attribs)
Definition docnode.h:1120
void handleSection(char cmdChar, const DString &cmdName)
Definition docnode.cpp:4009
bool empty() const
Definition docnode.h:1093
bool m_isFirst
Definition docnode.h:1123
Token parse()
Definition docnode.cpp:5688
void markLast(bool v=true)
Definition docnode.h:1095
void handleVhdlFlow()
Definition docnode.cpp:3856
void handleDoxyConfig(char cmdChar, const DString &cmdName)
Definition docnode.cpp:3574
bool injectToken(Token tok, const DString &tokText)
Definition docnode.cpp:4051
Token handleHtmlHeader(const HtmlAttribList &tagHtmlAttribs, int level)
Definition docnode.cpp:4040
bool m_isLast
Definition docnode.h:1124
Token handleXRefItem()
Definition docnode.cpp:3672
Token handleStartCode()
Definition docnode.cpp:4058
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:3292
DocParamSect::Type m_type
Definition docnode.h:1150
Token parseXml(const DString &paramName)
Definition docnode.cpp:3373
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:3441
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:1245
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:894
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:712
void parse()
Definition docnode.cpp:6277
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:538
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:5993
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:3003
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:2984
Node representing a simple section.
Definition docnode.h:1026
DString typeString() const
Definition docnode.cpp:3263
Type type() const
Definition docnode.h:1035
Token parse(bool userTitle, bool needsSeparator)
Definition docnode.cpp:3153
Token parseRcs()
Definition docnode.cpp:3190
DocSimpleSect(DocParser *parser, DocNodeVariant *parent, Type t)
Definition docnode.cpp:3143
const DocNodeVariant * title() const
Definition docnode.h:1042
Token parseXml()
Definition docnode.cpp:3207
void appendLinkWord(const DString &word)
Definition docnode.cpp:3243
bool hasTitle() const
Definition docnode.cpp:3148
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:130
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:158
void parse()
Definition docnode.cpp:6163
Node representing a simple section title.
Definition docnode.h:608
void parseFromString(DocNodeVariant *, const DString &title)
Definition docnode.cpp:3130
void parse()
Definition docnode.cpp:3112
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:263
Node representing a VHDL flow chart.
Definition docnode.h:758
DocVhdlFlow(DocParser *parser, DocNodeVariant *parent)
Definition docnode.cpp:1329
void parse()
Definition docnode.cpp:1333
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:185
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:478
DString m_key
Definition docnode.h:633
DString relPath() const
Definition docnode.h:627
bool parse()
Definition docnode.cpp:483
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:1978
int symbol2index(const std::string &symName) const
Returns a code for the requested Emoji entity name.
Definition emoji.cpp:1990
A model of a file symbol.
Definition filedef.h: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:129
FileDef * findFileDef(const DString &n, bool &ambig) const
Returns the file definition in fnMap that matches the file name n.
Definition filename.cpp:36
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:704
static FormulaManager & instance()
Definition formula.cpp:53
void clear()
clears the contents
Definition growvector.h:143
size_t size() const
returns the number of elements
Definition growvector.h:93
iterator end()
returns an iterator to the end
Definition growvector.h:88
T & back()
access the last element
Definition growvector.h:135
void pop_back()
removes the last element
Definition growvector.h:115
bool empty() const
checks whether the container is empty
Definition growvector.h:140
void emplace_back(Args &&...args)
Definition growvector.h:108
Class representing a list of HTML attributes.
Definition htmlattrib.h: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:240
This struct represents an item in the list of references.
Definition reflist.h:31
DString anchor() const
Definition reflist.h:45
DString text() const
Definition reflist.h:44
List of cross-referenced items.
Definition reflist.h:79
DString sectionTitle() const
Definition reflist.h:103
DString fileName() const
Definition reflist.h:101
RefItem * find(int itemId)
Definition reflist.cpp:41
bool isEnabled() const
Definition reflist.cpp:47
static RefListManager & instance()
Definition reflist.h:120
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:174
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:133
constexpr const char * SF_bit2str(int bitNumber)
Helper function that returns the name related one of the SF bits.
Definition datetime.h:32
constexpr int SF_NumBits
number of bits in SF vector
Definition datetime.h:27
DirIterator end(const DirIterator &) noexcept
Definition dir.cpp:176
#define AUTO_TRACE_ADD(...)
Definition docnode.cpp:52
static const char * g_sectionLevelToName[]
Definition docnode.cpp:61
#define AUTO_TRACE(...)
Definition docnode.cpp:51
static const StringUnorderedSet g_plantumlEngine
Definition docnode.cpp:74
#define INTERNAL_ASSERT(x)
Definition docnode.cpp:56
static void flattenParagraphs(DocNodeVariant *root, DocNodeList &children)
Definition docnode.cpp:862
static Token skipSpacesForTable(DocParser *parser)
Definition docnode.cpp:2020
#define AUTO_TRACE_EXIT(...)
Definition docnode.cpp:53
static bool findAttribute(const HtmlAttribList &tagHtmlAttribs, const char *attrName, DString *result)
Definition docnode.cpp:4929
std::vector< ActiveRowSpan > RowSpanList
List of ActiveRowSpan classes.
Definition docnode.cpp:2417
static void setParent(DocNodeVariant *n, DocNodeVariant *newParent)
Definition docnode.cpp:123
static void unescapeCRef(DString &s)
Definition docnode.cpp:87
static bool checkIfHtmlEndTagEndsAutoList(DocParser *parser, const DocNodeVariant *n)
Definition docnode.cpp:5636
static DString stripKnownExtensions(const DString &text)
Definition docnode.cpp:109
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:481
const char * qPrint(const char *s)
Definition dstring.h:787
#define ASSERT(x)
Definition dstring.h:28
FileDef * toFileDef(Definition *d)
Definition filedef.cpp:1976
GroupDef * toGroupDef(Definition *d)
Translator * theTranslator
Definition language.cpp:71
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 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:144
Definition dstring.h:917
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:2412
uint32_t column
Definition docnode.cpp:2413
ActiveRowSpan(uint32_t rows, uint32_t col)
Definition docnode.cpp:2411
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:3989
bool resolveLink(const DString &scName, const DString &lr, bool, const Definition **resContext, DString &resAnchor, SrcLangExt lang, const DString &prefix)
Definition util.cpp:2310
DString convertNameToFile(const DString &name, bool allowDots, bool allowUnderscore)
Definition util.cpp:2865
DString stripScope(const DString &name)
Definition util.cpp:3111
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
Definition util.cpp:4170
DString linkToText(SrcLangExt lang, const DString &link, bool ignoreDots)
Definition util.cpp:2267
DString stripIndentation(const DString &s, bool skipFirstLine)
Definition util.cpp:4685
A bunch of utility functions.