41#include <unordered_map>
64#if !ENABLE_MARKDOWN_TRACING
68#define AUTO_TRACE(...) (void)0
69#define AUTO_TRACE_ADD(...) (void)0
70#define AUTO_TRACE_EXIT(...) (void)0
86 return (c>=
'a' && c<=
'z') ||
89 (
static_cast<unsigned char>(c)>=0x80);
95 return c==
'-' || c==
'+' || c==
'!' || c==
'?' || c==
'$' || c==
'@' ||
96 c==
'&' || c==
'*' || c==
'_' || c==
'%' || c==
'[' || c==
'(' ||
97 c==
'.' || c==
'>' || c==
':' || c==
',' || c==
';' || c==
'\'' ||
98 c==
'"' || c==
'`' || c==
'\\';
104 return c==
'\n' || c==
' ' || c==
'\'' || c==
'<' ||
105 c==
'>' || c==
'{' || c==
'(' || c==
'[' ||
106 c==
',' || c==
':' || c==
';';
112 return c1==
static_cast<char>(0xc2) && c2==
static_cast<char>(0xa0);
118 (offset>1 && !
isUtf8Nbsp(data.data()[-2],data.data()[-1])));
125 return c==
'(' || c==
'{' || c==
'[' || (c==
'<' && cn!=
'/') || c==
'\\' || c==
'@';
156 int processLink(std::string_view data,
size_t offset);
164 int isHeaderline(std::string_view data,
bool allowAdjustLevel);
166 bool *pIsIdGenerated=
nullptr);
169 size_t blockStart,
size_t blockEnd);
198 a[
static_cast<unsigned int>(
'[')] = [](
Markdown::Private &obj,std::string_view data,
size_t offset) {
return obj.
processLink (data,offset); };
199 a[
static_cast<unsigned int>(
'!')] = [](
Markdown::Private &obj,std::string_view data,
size_t offset) {
return obj.
processLink (data,offset); };
209 :
prv(
std::make_unique<
Private>(fileName,lineNr,indentLevel))
211 using namespace std::placeholders;
235 if (data[0] ==
'\n')
return 1;
239 return (data.size()>8 && data[8]==
' ') ? 9 : 8;
248 if (s.
empty())
return s;
250 const char *p=s.
data();
254 if (c==
'"' && pc!=
'\\') result+=
'\\';
266 if (s.
empty())
return s;
267 bool insideQuote=
false;
269 const char *p=s.
data();
284 insideQuote=!insideQuote;
296 if ((p[0]==
':') && (p[1]==
':'))
308 case '\\':
if (!insideQuote) { result+=
'\\'; } result+=
'\\';
break;
309 case '@':
if (!insideQuote) { result+=
'\\'; } result+=
'@';
break;
312 case '#':
if (!insideQuote) { result+=
'\\'; } result+=
'#';
break;
313 case '$':
if (!insideQuote) { result+=
'\\'; } result+=
'$';
break;
314 case '&':
if (!insideQuote) { result+=
'\\'; } result+=
'&';
break;
329 if (leftMarker && rightMarker)
337 else if (rightMarker)
352 for (
const auto &attr_ : attrList)
361 return attr.
mid(i+1);
400 using EndBlockFunc =
DString (*)(
const std::string &,bool,char);
402 static constexpr auto getEndBlock = [](
const std::string &blockName,bool,char) ->
DString
404 return "end"+blockName;
406 static constexpr auto getEndCode = [](
const std::string &blockName,
bool openBracket,char) ->
DString
408 return openBracket ?
DString(
"}") :
"end"+blockName;
410 static constexpr auto getEndUml = [](
const std::string &,bool,char) ->
DString
414 static constexpr auto getEndFormula = [](
const std::string &,bool,
char nextChar) ->
DString
418 case '$':
return "f$";
419 case '(':
return "f)";
420 case '[':
return "f]";
421 case '{':
return "f}";
427 static const std::unordered_map<std::string,EndBlockFunc> blockNames =
429 {
"dot", getEndBlock },
430 {
"code", getEndCode },
431 {
"icode", getEndBlock },
432 {
"msc", getEndBlock },
433 {
"verbatim", getEndBlock },
434 {
"iverbatim", getEndBlock },
435 {
"iliteral", getEndBlock },
436 {
"latexonly", getEndBlock },
437 {
"htmlonly", getEndBlock },
438 {
"xmlonly", getEndBlock },
439 {
"rtfonly", getEndBlock },
440 {
"manonly", getEndBlock },
441 {
"docbookonly", getEndBlock },
442 {
"startuml", getEndUml },
443 {
"mermaid", getEndBlock },
444 {
"f", getEndFormula }
447 const size_t size = data.
size();
448 bool openBracket = offset>0 && data.data()[-1]==
'{';
449 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
450 if (isEscaped)
return result;
453 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
454 if (
end==1)
return result;
455 std::string blockName(data.substr(1,
end-1));
456 auto it = blockNames.find(blockName);
457 if (it!=blockNames.end())
459 result = it->second(blockName, openBracket,
end<size ? data[
end] : 0);
469 using EndCmdFunc = size_t (*)(std::string_view,size_t);
471 static constexpr auto endOfLine = [](std::string_view data_,
size_t offset_) ->
size_t
476 while (offset_<data_.size() && ((c=data_[offset_])!=
'\n' || lc==
'\\'))
478 if (c==
'\\') lc=
'\\';
479 else if (c!=
' ') lc=0;
485 static constexpr auto endOfLabels = [](std::string_view data_,
size_t offset_,
bool multi_) ->
size_t
487 if (offset_<data_.size() && data_[offset_]==
' ')
495 while (offset_<data_.size() && data_[offset_]==
' ')
500 while (offset_<data_.size() && (c=data_[offset_])!=
' ' && c!=
',' && c!=
'\\' && c!=
'@' && c!=
'\n')
505 if (multi_ && offset_<data_.size() && (data_[offset_]==
',' || data_[offset_]==
' '))
507 size_t off = offset_;
508 while (off<data_.size() && data_[off]==
' ')
512 if (off<data_.size() && data_[off]==
',')
531 static constexpr auto endOfLabel = [](std::string_view data_,
size_t offset_) ->
size_t
533 return endOfLabels(data_,offset_,
false);
536 static constexpr auto endOfLabelOpt = [](std::string_view data_,
size_t offset_) ->
size_t
538 size_t index=offset_;
539 if (index<data_.size() && data_[index]==
' ')
542 while (index<data_.size() && data_[index]==
' ') index++;
544 if (index<data_.size() && data_[index]==
'{')
548 while (index<data_.size() && (c=data_[index])!=
'}' && c!=
'\\' && c!=
'@' && c!=
'\n') index++;
549 if (index==data_.size() || data_[index]!=
'}')
return 0;
552 return endOfLabel(data_,offset_);
555 static constexpr auto endOfParam = [](std::string_view data_,
size_t offset_) ->
size_t
557 size_t index=offset_;
558 if (index<data_.size() && data_[index]==
' ')
561 while (index<data_.size() && data_[index]==
' ') index++;
563 if (index<data_.size() && data_[index]==
'[')
567 while (index<data_.size() && (c=data_[index])!=
']' && c!=
'\n') index++;
568 if (index==data_.size() || data_[index]!=
']')
return 0;
571 return endOfLabels(data_,offset_,
true);
574 static constexpr auto endOfRetVal = [](std::string_view data_,
size_t offset_) ->
size_t
576 return endOfLabels(data_,offset_,
true);
579 static constexpr auto endOfFuncLike = [](std::string_view data_,
size_t offset_,
bool allowSpaces) ->
size_t
581 if (offset_<data_.size() && data_[offset_]==
' ')
586 while (offset_<data_.size() && data_[offset_]==
' ')
591 while (offset_<data_.size() && (c=data_[offset_])!=
'\n' && (allowSpaces || c!=
' ') && c!=
'(')
593 if (
literal_at(data_.substr(offset_),
"\\ilinebr "))
break;
600 while (offset_<data_.size() && (c=data_[offset_++]))
603 else if (c==
')') count--;
604 if (count==0)
return offset_;
612 static constexpr auto endOfFunc = [](std::string_view data_,
size_t offset_) ->
size_t
614 return endOfFuncLike(data_,offset_,
true);
617 static constexpr auto endOfGuard = [](std::string_view data_,
size_t offset_) ->
size_t
619 return endOfFuncLike(data_,offset_,
false);
622 static const std::unordered_map<std::string,EndCmdFunc> cmdNames =
625 {
"addindex", endOfLine },
626 {
"addtogroup", endOfLabel },
627 {
"anchor", endOfLabel },
630 {
"category", endOfLine },
631 {
"cite", endOfLabelOpt },
632 {
"class", endOfLine },
633 {
"concept", endOfLine },
634 {
"copybrief", endOfFunc },
635 {
"copydetails", endOfFunc },
636 {
"copydoc", endOfFunc },
637 {
"def", endOfFunc },
638 {
"defgroup", endOfLabel },
639 {
"diafile", endOfLine },
640 {
"dir", endOfLine },
641 {
"dockbookinclude",endOfLine },
642 {
"dontinclude", endOfLine },
643 {
"dotfile", endOfLine },
645 {
"elseif", endOfGuard },
646 {
"em", endOfLabel },
647 {
"emoji", endOfLabel },
648 {
"enum", endOfLabel },
649 {
"example", endOfLine },
650 {
"exception", endOfLabel },
651 {
"extends", endOfLabel },
652 {
"file", endOfLine },
654 {
"headerfile", endOfLine },
655 {
"htmlinclude", endOfLine },
656 {
"ianchor", endOfLabelOpt },
657 {
"idlexcept", endOfLine },
658 {
"if", endOfGuard },
659 {
"ifnot", endOfGuard },
660 {
"image", endOfLine },
661 {
"implements", endOfLine },
662 {
"include", endOfLine },
663 {
"includedoc", endOfLine },
664 {
"includelineno", endOfLine },
665 {
"ingroup", endOfLabel },
666 {
"interface", endOfLine },
667 {
"latexinclude", endOfLine },
668 {
"maninclude", endOfLine },
669 {
"memberof", endOfLabel },
670 {
"mermaidfile", endOfLine },
671 {
"mscfile", endOfLine },
672 {
"namespace", endOfLabel },
673 {
"noop", endOfLine },
674 {
"overload", endOfLine },
676 {
"package", endOfLabel },
677 {
"page", endOfLabel },
678 {
"paragraph", endOfLabel },
679 {
"param", endOfParam },
680 {
"property", endOfLine },
681 {
"protocol", endOfLine },
682 {
"qualifier", endOfLine },
683 {
"ref", endOfLabel },
684 {
"refitem", endOfLine },
685 {
"related", endOfLabel },
686 {
"relatedalso", endOfLabel },
687 {
"relates", endOfLabel },
688 {
"relatesalso", endOfLabel },
689 {
"requirement", endOfLabel },
690 {
"retval", endOfRetVal},
691 {
"rtfinclude", endOfLine },
692 {
"section", endOfLabel },
693 {
"skip", endOfLine },
694 {
"skipline", endOfLine },
695 {
"snippet", endOfLine },
696 {
"snippetdoc", endOfLine },
697 {
"snippetlineno", endOfLine },
698 {
"struct", endOfLine },
699 {
"satisfies", endOfLabel },
700 {
"subpage", endOfLabel },
701 {
"subparagraph", endOfLabel },
702 {
"subsubparagraph",endOfLabel },
703 {
"subsection", endOfLabel },
704 {
"subsubsection", endOfLabel },
705 {
"throw", endOfLabel },
706 {
"throws", endOfLabel },
707 {
"tparam", endOfLabel },
708 {
"typedef", endOfLine },
709 {
"plantumlfile", endOfLine },
710 {
"union", endOfLine },
711 {
"until", endOfLine },
712 {
"var", endOfLine },
713 {
"verbinclude", endOfLine },
714 {
"verifies", endOfLabel },
715 {
"weakgroup", endOfLabel },
716 {
"xmlinclude", endOfLine },
717 {
"xrefitem", endOfLabel }
720 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
721 if (isEscaped)
return 0;
723 const size_t size = data.size();
725 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
726 if (
end==1)
return 0;
727 std::string cmdName(data.substr(1,
end-1));
729 auto it = cmdNames.find(cmdName);
730 if (it!=cmdNames.end())
733 result = it->second(data,
end);
746 const size_t size = data.size();
750 while (i<size && data[i]!=c &&
751 data[i]!=
'\\' && data[i]!=
'@' &&
752 !(data[i]==
'/' && data[i-1]==
'<') &&
772 while (i+len<size && data[i+len]==c)
779 if (len!=c_size || (i+len<size &&
isIdChar(data[i+len])))
785 return static_cast<int>(i);
793 while (i < size && data[i] ==
'`')
801 while (i<size && enb<snb)
803 if (data[i]==
'`') enb++;
804 if (snb==1 && data[i]==
'\'')
break;
808 else if (data[i]==
'@' || data[i]==
'\\')
811 if (!endBlockName.
empty())
814 size_t l = endBlockName.
length();
817 if ((data[i]==
'\\' || data[i]==
'@') &&
818 data[i-1]!=
'\\' && data[i-1]!=
'@')
828 else if (i+1<size &&
isIdChar(data[i+1]))
837 else if (data[i-1]==
'<' && data[i]==
'/')
841 else if (data[i]==
'\n')
844 while (i<size && data[i]==
' ') i++;
845 if (i>=size || data[i]==
'\n')
863 const size_t size = data.size();
866 if (size>1 && data[0]==c && data[1]==c) { i=1; }
871 if (len==0) {
return 0; }
873 if (i>=size) {
return 0; }
875 if (i+1<size && data[i+1]==c)
880 if (data[i]==c && data[i-1]!=
' ' && data[i-1]!=
'\n')
886 return static_cast<int>(i+1);
897 const size_t size = data.size();
907 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=
' ' && data[i-1]!=
'\n')
909 if (c ==
'~')
out+=
"<strike>";
910 else out+=
"<strong>";
912 if (c ==
'~')
out+=
"</strike>";
913 else out+=
"</strong>";
915 return static_cast<int>(i+2);
929 const size_t size = data.size();
941 if (data[i]!=c || data[i-1]==
' ' || data[i-1]==
'\n')
946 if (i+2<size && data[i+1]==c && data[i+2]==c)
950 out+=
"</strong></em>";
952 return static_cast<int>(i+3);
954 else if (i+1<size && data[i+1]==c)
965 return static_cast<int>(len - 2);
979 return static_cast<int>(len - 1);
990 const size_t size = data.size();
994 if (i<size && data[i]==
'-')
999 if (i<size && data[i]==
'-')
1004 if (i<size && data[i]==
'-')
1008 if (count>=2 && offset>=2 &&
literal_at(data.data()-2,
"<!"))
1010 if (count==2 && size > 2 && data[2]==
'>')
1012 if (count==3 && size > 3 && data[3]==
'>')
1014 if (count==2 && (offset<8 || !
literal_at(data.data()-8,
"operator")))
1034 const size_t size = data.size();
1037 while (i<size && data[i]!=
'"' && nl<2)
1039 if (data[i]==
'\n') nl++;
1042 if (i<size && data[i]==
'"' && nl<2)
1044 out+=data.substr(0,i+1);
1046 return static_cast<int>(i+1);
1058 if (offset>0 && data.data()[-1]==
'\\') {
return 0; }
1060 const size_t size = data.size();
1066 while (i < size &&
isIdChar(data[i]))
1071 DString tagName(data.substr(1,i-1));
1072 if (tagName.
lower()==
"pre")
1074 bool insideStr=
false;
1078 if (!insideStr && c==
'<')
1080 if (data[i+1]==
'/' &&
1081 tolower(data[i+2])==
'p' && tolower(data[i+3])==
'r' &&
1082 tolower(data[i+4])==
'e' && tolower(data[i+5])==
'>')
1084 if (doWrite)
out+=data.substr(0,i+6);
1087 return static_cast<int>(i+6);
1090 else if (insideStr && c==
'"')
1092 if (data[i-1]!=
'\\') insideStr=
false;
1105 if (data[i]==
'/' && i+1<size && data[i+1]==
'>')
1108 if (doWrite)
out+=data.substr(0,i+2);
1110 return static_cast<int>(i+2);
1112 else if (data[i]==
'>')
1115 if (doWrite)
out+=data.substr(0,i+1);
1117 return static_cast<int>(i+1);
1119 else if (data[i]==
' ')
1122 bool insideAttr=
false;
1125 if (!insideAttr && data[i]==
'"')
1129 else if (data[i]==
'"' && data[i-1]!=
'\\')
1133 else if (!insideAttr && data[i]==
'>')
1136 if (doWrite)
out+=data.substr(0,i+1);
1138 return static_cast<int>(i+1);
1158 const size_t size = data.size();
1170 if (size>2 && c!=
'~' && data[1]!=c)
1173 if (data[1]==
' ' || data[1]==
'\n' ||
1181 if (size>3 && data[1]==c && data[2]!=c)
1183 if (data[2]==
' ' || data[2]==
'\n' ||
1191 if (size>4 && c!=
'~' && data[1]==c && data[2]==c && data[3]!=c)
1193 if (data[3]==
' ' || data[3]==
'\n' ||
1205 std::string_view
fmt,
bool inline_img,
bool explicitTitle,
1210 AUTO_TRACE(
"fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1221 out+=link.
mid(fd ? 0 : 5);
1222 if (!explicitTitle && !content.
empty())
1228 else if ((content.
empty() || explicitTitle) && !title.
empty())
1238 if (!attributes.
empty())
1250 const size_t size = data.size();
1255 bool isImageLink =
false;
1256 bool isImageInline =
false;
1262 if (size<2 || data[1]!=
'[')
1271 while (pos>=-
static_cast<int>(offset) && numNLsNeeded>0)
1273 if (data.data()[pos]==
'\n') numNLsNeeded--;
1274 else if (data.data()[pos]!=
' ')
1284 size_t contentStart=i;
1291 if (data[i-1]==
'\\')
1294 else if (data[i]==
'[')
1298 else if (data[i]==
']')
1301 if (level<=0)
break;
1303 else if (data[i]==
'\n')
1306 if (nl>1) {
return 0; }
1312 if (i>=size)
return 0;
1313 size_t contentEnd=i;
1314 content = data.
substr(contentStart,contentEnd-contentStart);
1316 if (!isImageLink && content.
empty()) {
return 0; }
1319 bool whiteSpace =
false;
1321 while (i<size && data[i]==
' ') { whiteSpace =
true; i++; }
1322 if (i<size && data[i]==
'\n')
1327 while (i<size && data[i]==
' ') i++;
1329 if (whiteSpace && i<size && (data[i]==
'(' || data[i]==
'['))
return 0;
1331 bool explicitTitle=
false;
1332 if (i<size && data[i]==
'(')
1335 while (i<size && data[i]==
' ') i++;
1336 bool uriFormat=
false;
1337 if (i<size && data[i]==
'<') { i++; uriFormat=
true; }
1341 while (i<size && data[i]!=
'\'' && data[i]!=
'"' && braceCount>0)
1347 if (nlConsec>1) {
return 0; }
1349 else if (data[i]==
'(')
1354 else if (data[i]==
')')
1359 else if (data[i]!=
' ')
1370 if (i>=size || data[i]==
'\n') {
return 0; }
1371 link = data.
substr(linkStart,i-linkStart);
1374 if (link.
empty()) {
return 0; }
1378 if (data[i]==
'\'' || data[i]==
'"')
1382 size_t titleStart=i;
1388 if (nl>1) {
return 0; }
1391 else if (data[i]==
'\\')
1395 else if (data[i]==c)
1406 size_t titleEnd = i-1;
1408 while (titleEnd>titleStart && data[titleEnd]==
' ') titleEnd--;
1409 if (data[titleEnd]==c)
1411 title = data.
substr(titleStart,titleEnd-titleStart);
1415 if (data[i]==
' ')i++;
1416 else if (data[i] ==
')')
break;
1430 else if (i<size && data[i]==
'[')
1436 while (i<size && data[i]!=
']')
1441 if (nl>1) {
return 0; }
1445 if (i>=size) {
return 0; }
1447 link = data.
substr(linkStart,i-linkStart);
1459 link = lr_it->second.link;
1460 title = lr_it->second.title;
1470 else if (i<size && data[i]!=
':' && !content.
empty())
1477 link = lr_it->second.link;
1478 title = lr_it->second.title;
1482 else if (content==
"TOC")
1505 while (j<size && data[j]==
' ') { j++; }
1506 if (j<size && data[j]==
'{')
1511 size_t attributesStart=i;
1516 if (data[i-1]==
'\\')
1519 else if (data[i]==
'{')
1523 else if (data[i]==
'}')
1526 if (level<=0)
break;
1528 else if (data[i]==
'\n')
1531 if (nl>1) {
return 0; }
1536 if (i>=size)
return 0;
1537 size_t attributesEnd=i;
1538 attributes = data.
substr(attributesStart,attributesEnd-attributesStart);
1547 while (pos<size && numNLsNeeded>0)
1549 if (data[pos]==
'\n') numNLsNeeded--;
1550 else if (data[pos]!=
' ')
1565 out+=
"@tableofcontents{html:";
1570 else if (isImageLink)
1579 writeMarkdownImage(
"html", isImageInline, explicitTitle, title, content, link, attributes, fd);
1580 writeMarkdownImage(
"latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
1581 writeMarkdownImage(
"rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
1582 writeMarkdownImage(
"docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
1583 writeMarkdownImage(
"xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
1607 (lang==SrcLangExt::Markdown && !
isURL(link)))
1639 if (explicitTitle && !title.
empty())
1668 for (
int ii = 0; ii < nlTotal; ii++)
out+=
"\n";
1681 bool foundNameRef =
false;
1682 if (!content.
empty() && (content.
at(0)==
'#' || content.
at(0)==
'@'))
1685 while (endOfId<content.
length() &&
isId(content.
at(endOfId))) endOfId++;
1692 foundNameRef =
true;
1711 return static_cast<int>(i);
1718 const size_t size = data.size();
1722 while (nb<size && data[nb]==
'`')
1739 if (
end+1<size && data[
end+1]==
'`')
1754 if (
end+1<size && data[
end+1]==
'`')
1767 else if (data[
end]==
'\n')
1778 else if (!markdownStrict && data[
end]==
'\'' && nb==1 && (
end+1==size || (
end+1<size && data[
end+1]!=
'\'' && !
isIdChar(data[
end+1]))))
1781 out+=data.substr(nb,
end-nb);
1784 return static_cast<int>(
end+1);
1786 else if (!markdownStrict && data[
end]==
'\'' && nb==2 &&
end+1<size && data[
end+1]==
'\'')
1789 out+=data.substr(nb,
end-nb);
1792 return static_cast<int>(
end+2);
1796 if (data[
end]!=
' ') pc = data[
end];
1800 if (i < nb && end >= size)
1805 out+=data.substr(0,nb);
1806 return static_cast<int>(nb);
1810 while (
end<size && data[
end]==
'`')
1826 return static_cast<int>(
end);
1845 const size_t size = data.size();
1848 if (!endBlockName.
empty())
1851 size_t l = endBlockName.
length();
1854 if ((data[i]==
'\\' || data[i]==
'@') &&
1855 data[i-1]!=
'\\' && data[i-1]!=
'@')
1862 return static_cast<int>(i+1+l);
1871 out+=data.substr(0,endPos);
1872 return static_cast<int>(endPos);
1874 if (size>1 && (data[0]==
'\\' || data[0]==
'@'))
1877 if (c==
'[' || c==
']' || c==
'*' || c==
'(' || c==
')' || c==
'`' || c==
'_')
1883 else if (c==
'\\' || c==
'@')
1885 out+=data.substr(0,2);
1889 else if (c==
'-' && size>3 && data[2]==
'-' && data[3]==
'-')
1891 out+=data.substr(1,3);
1895 else if (c==
'-' && size>2 && data[2]==
'-')
1897 out+=data.substr(1,2);
1911 const size_t size = data.size();
1917 out+=data.substr(i,
end-i);
1918 if (
end>=size)
break;
1921 int iend = action(*
this,data.substr(i),i);
1939 const size_t size = data.size();
1940 while (i<size && data[i]==
' ') i++;
1941 if (i==size)
return 0;
1946 while (i < size && data[i] ==
'=')
1951 while (i<size && data[i]==
' ') i++;
1952 int level = (c>1 && (i>=size || data[i]==
'\n')) ? 1 : 0;
1953 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
1968 while (i < size && data[i] ==
'-')
1973 while (i<size && data[i]==
' ') i++;
1974 return (c>1 && (i>=size || data[i]==
'\n')) ?
indentLevel+2 : 0;
1984 const size_t size = data.size();
1985 while (i<size && data[i]==
' ') i++;
1990 while (i<size && (data[i]==
'>' || data[i]==
' '))
1992 if (data[i]==
'>') level++;
1997 bool res = (level>0 && i<size && ((data[i-1]==
' ') || data[i]==
'\n')) || (level > 1);
2012 const size_t size = data.size();
2015 while (i<size && data[i]==
' ') i++;
2016 if (i>=size || data[i]!=
'[') {
return 0; }
2018 size_t refIdStart=i;
2019 while (i<size && data[i]!=
'\n' && data[i]!=
']') i++;
2020 if (i>=size || data[i]!=
']') {
return 0; }
2021 refid = data.
substr(refIdStart,i-refIdStart);
2022 if (refid.
empty()) {
return 0; }
2026 if (i>=size || data[i]!=
':') {
return 0; }
2030 while (i<size && data[i]==
' ') i++;
2031 if (i<size && data[i]==
'\n')
2034 while (i<size && data[i]==
' ') i++;
2036 if (i>=size) {
return 0; }
2038 if (i<size && data[i]==
'<') i++;
2040 while (i<size && data[i]!=
' ' && data[i]!=
'\n') i++;
2042 if (i<size && data[i]==
'>') i++;
2043 if (linkStart==linkEnd) {
return 0; }
2044 link = data.
substr(linkStart,linkEnd-linkStart);
2046 if (link==
"@ref" || link==
"\\ref")
2049 while (i<size && data[i]!=
'\n' && data[i]!=
'"') i++;
2050 link+=data.
substr(argStart,i-argStart);
2057 while (i<size && data[i]==
' ') i++;
2058 if (i<size && data[i]==
'\n')
2062 while (i<size && data[i]==
' ') i++;
2066 AUTO_TRACE_EXIT(
"result={}: end of isLinkRef while looking for title",i);
2071 if (c==
'\'' || c==
'"' || c==
'(')
2076 size_t titleStart=i;
2078 while (i<size && data[i]!=
'\n') i++;
2083 while (
end>titleStart && data[
end]!=c)
end--;
2086 title = data.
substr(titleStart,
end-titleStart);
2090 while (i<size && data[i]==
' ') i++;
2102 size_t size = data.size();
2103 if (size>0 && data[size-1]==
'\n') size--;
2104 while (i<size && data[i]==
' ') i++;
2105 if (i>=size) {
AUTO_TRACE_EXIT(
"result=false: empty line");
return false; }
2107 if (c!=
'*' && c!=
'-' && c!=
'_')
2119 else if (data[i]!=
' ')
2134 static const reg::Ex r2(R
"({#(\a[\w-]*)}\s*$)");
2136 std::string ti = title.str();
2139 std::string
id = match[1].str();
2140 title = title.
left(match.position());
2143 warn(
fileName,
lineNr,
"An automatically generated id already has the name '{}'!",
id);
2149 if (((level>0) && (level<=
Config_getInt(TOC_INCLUDE_HEADINGS))) || (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2152 if (pIsIdGenerated) *pIsIdGenerated=
true;
2163 DString &header,
DString &
id,
bool allowAdjustLevel,
bool *pIsIdGenerated)
2167 int level = 0, blanks=0;
2168 const size_t size = data.size();
2171 while (i<size && data[i]==
' ') i++;
2172 if (i>=size || data[i]!=
'#')
2176 while (i < size && data[i] ==
'#')
2185 while (i < size && data[i] ==
' ')
2190 if (level==1 && blanks==0)
2197 while (
end<size && data[
end]!=
'\n')
end++;
2198 while (
end>i && (data[
end-1]==
'#' || data[
end-1]==
' '))
end--;
2205 int idx=
static_cast<int>(header.
length())-1;
2206 while (idx>=0 && (header.
at(idx)==
'#' || header.
at(idx)==
' ')) idx--;
2207 header=header.
left(idx+1);
2210 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
2241 while (i<data.size())
2252 (data[(i)]=='<' && \
2253 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2254 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2263 const size_t size=data.size();
2267 bool listMarkerSkipped=
false;
2270 (!listMarkerSkipped &&
2271 (data[i]==
'+' || data[i]==
'-' || data[i]==
'*' ||
2272 (data[i]==
'#' && i>0 && data[i-1]==
'-') ||
2273 (isDigit=(data[i]>=
'1' && data[i]<=
'9')) ||
2274 (isLi=(size>=3 && i+3<size &&
isLiTag(i)))
2283 while (j<size && ((data[j]>=
'0' && data[j]<=
'9') || data[j]==
'.'))
2287 if (j+1<size && data[j+1]==
' ')
2289 listMarkerSkipped=
true;
2306 listMarkerSkipped=
true;
2308 else if (data[i]==
'-' && size>=2 && i+2<size && data[i+1]==
'#' && data[i+2]==
' ')
2310 listMarkerSkipped=
true;
2314 else if (data[i]!=
' ' && i+1<size && data[i+1]==
' ')
2316 listMarkerSkipped=
true;
2318 if (data[i]!=
' ' && !listMarkerSkipped)
2332 size_t normalIndent = 0;
2333 while (normalIndent<data.size() && data[normalIndent]==
' ') normalIndent++;
2335 size_t result = listIndent>normalIndent ? listIndent : 0;
2346 while (i<data.size())
2352 else if (data[i]==
'\n')
2356 else if (data[i]!=
' ' && data[i]!=
'\t')
2368 DString &lang,
size_t &start,
size_t &
end,
size_t &offset,
2372 const char dot =
'.';
2373 auto isAlphaChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z'); };
2374 auto isAlphaNChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z') || (c>=
'0' && c<=
'9') || (c==
'+'); };
2375 auto isLangChar = [&](
char c) {
return c==dot || isAlphaChar(c); };
2381 const size_t size = data.size();
2382 while (i < size && data[i] ==
' ')
2387 if (indent>=refIndent+4)
2389 AUTO_TRACE_EXIT(
"result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2393 if (i<size && data[i]==
'`') tildaChar=
'`';
2394 while (i < size && data[i] == tildaChar)
2401 AUTO_TRACE_EXIT(
"result=false: no fence marker found #tildes={}",startTildes);
2405 while (i<size && data[i]==
' ') { i++; }
2406 if (i<size && data[i]==
'{')
2409 if (data[i] == dot) i++;
2411 while (i<size && (data[i]!=
'\n' && data[i]!=
'}')) i++;
2412 if (i<size && data[i]==
'}')
2414 lang = data.
substr(startLang,i-startLang);
2423 else if (i<size && isLangChar(data[i]))
2425 if (data[i] == dot) i++;
2427 if (i<size && isAlphaChar(data[i]))
2430 while (i<size && isAlphaNChar(data[i])) i++;
2432 lang = data.
substr(startLang,i-startLang);
2442 if (data[i]==tildaChar)
2446 while (i < size && data[i] == tildaChar)
2451 while (i<size && data[i]==
' ') i++;
2453 if (endTildes==startTildes)
2456 AUTO_TRACE_EXIT(
"result=true: found end marker at offset {} lang='{}'",offset,lang);
2463 warn(fileName, lineNr,
"Ending Inside a fenced code block. Maybe the end marker for the block is missing?");
2468static bool isCodeBlock(std::string_view data,
size_t offset,
size_t &indent)
2475 const size_t size = data.size();
2476 while (i < size && data[i] ==
' ')
2484 AUTO_TRACE_EXIT(
"result={}: line is not indented enough {}<4",
false,indent0);
2487 if (indent0>=size || data[indent0]==
'\n')
2489 AUTO_TRACE_EXIT(
"result={}: only spaces at the end of a comment block",
false);
2496 int offset_i =
static_cast<int>(offset);
2500 int j =
static_cast<int>(i)-offset_i-1;
2502 size_t nl_size =
isNewline(std::string_view(data.data()+j,data.size()-j));
2505 nl_pos[nl++]=j+
static_cast<int>(nl_size);
2511 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2522 if (!
isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2531 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2537 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2544 if (nl==1 && !
isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2552 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2568 const size_t size = data.size();
2571 while (i<size && data[i]==
' ') i++;
2572 if (i < size && data[i] ==
'|' && data[i] !=
'\n')
2581 while (i<size && (j =
isNewline(data.substr(i)))==0) i++;
2584 if (j>0 && i>0) i--;
2585 while (i>0 && data[i]==
' ') i--;
2586 if (i > 0 && data[i - 1] !=
'\\' && data[i] ==
'|')
2600 if (data[i]==
'|' && (i==0 || data[i-1]!=
'\\')) columns++;
2601 if (columns==1) columns++;
2605 if (n==2 && columns==0)
2617 size_t cc0=0, start=0,
end=0;
2621 if (i>=data.size() || cc0<1)
2633 if (data[j]!=
':' && data[j]!=
'-' && data[j]!=
'|' && data[j]!=
' ')
2642 AUTO_TRACE_EXIT(
"result=false: different number of columns as previous line {}!={}",cc1,cc0);
2657 const size_t size = data.size();
2659 size_t columns=0, start=0,
end=0;
2661 size_t headerStart = start;
2662 size_t headerEnd =
end;
2668 std::vector<Alignment> columnAlignment(columns);
2670 bool leftMarker=
false, rightMarker=
false, startFound=
false;
2676 if (data[j]==
':') { leftMarker=
true; startFound=
true; }
2677 if (data[j]==
'-') startFound=
true;
2680 if (data[j]==
'-') rightMarker=
false;
2681 else if (data[j]==
':') rightMarker=
true;
2682 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2706 std::vector<std::vector<TableCell> > tableContents;
2708 size_t m = headerStart;
2709 std::vector<TableCell> headerContents(columns);
2710 for (k=0;k<columns;k++)
2712 while (m<=headerEnd && (data[m]!=
'|' || (m>0 && data[m-1]==
'\\')))
2714 headerContents[k].cellText += data[m++];
2719 headerContents[k].colSpan = headerContents[k].cellText.empty();
2720 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2722 tableContents.push_back(headerContents);
2728 if (cc!=columns)
break;
2732 std::vector<TableCell> rowContents(columns);
2735 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2739 rowContents[k].colSpan = rowContents[k].cellText.empty();
2740 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2745 rowContents[k].cellText += data[j];
2751 rowContents[k].colSpan = rowContents[k].cellText.empty();
2752 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2753 tableContents.push_back(rowContents);
2759 out+=
"<table class=\"markdownTable\">";
2760 DString cellTag(
"th"), cellClass(
"class=\"markdownTableHead");
2761 for (
size_t row = 0; row < tableContents.size(); row++)
2767 out+=
"\n<tr class=\"markdownTableRowOdd\">";
2771 out+=
"\n<tr class=\"markdownTableRowEven\">";
2776 out+=
"\n <tr class=\"markdownTableHead\">";
2778 for (
size_t c = 0; c < columns; c++)
2781 DString cellText(tableContents[row][c].cellText);
2787 if (tableContents[row][c].cellText ==
"^")
2791 if (tableContents[row][c].colSpan)
2793 int cr =
static_cast<int>(c);
2794 while ( cr >= 0 && tableContents[row][cr].colSpan)
2798 if (cr >= 0 && tableContents[row][cr].cellText ==
"^")
continue;
2800 size_t rowSpan = 1, spanRow = row+1;
2801 while ((spanRow < tableContents.size()) &&
2802 (tableContents[spanRow][c].cellText ==
"^"))
2808 out+=
" <" + cellTag +
" " + cellClass;
2810 switch (columnAlignment[c])
2822 out+=
" rowspan=\"" + spanStr +
"\"";
2828 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2837 out+=
" colspan=\"" + spanStr +
"\"";
2841 out+=
"> " + cellText +
" \\ilinebr </" + cellTag +
">";
2844 cellClass =
"class=\"markdownTableBody";
2860 while (i<data.size() && data[i]!=
'\n')
2862 if (data[i]!=
' ' && data[i]!=
'\t') j++;
2865 if (i>=data.size()) {
return 0; }
2866 if (i<2) {
return 0; }
2867 bool res = (j>0 && data[i-1]==
' ' && data[i-2]==
' ');
2883 else if ((level=
isAtxHeader(data,header,
id,
true)))
2907 out+=
"</"+hTag+
">\n";
2910 else if (data.size()>0)
2912 size_t tmpSize = data.size();
2913 if (data[data.size()-1] ==
'\n') tmpSize--;
2914 out+=data.substr(0,tmpSize);
2918 out+=
"\\ilinebr<br>";
2920 if (tmpSize != data.size())
out+=
'\n';
2926 {
"[!note]",
"\\note" },
2927 {
"[!warning]",
"\\warning" },
2928 {
"[!tip]",
"\\remark" },
2929 {
"[!caution]",
"\\attention" },
2930 {
"[!important]",
"\\important" }
2939 const size_t size = data.size();
2940 std::string startCmd;
2941 int isGitHubAlert =
false;
2942 int isGitHubFirst =
false;
2947 while (
end<=size && data[
end-1]!=
'\n')
end++;
2952 while (j<
end && (data[j]==
' ' || data[j]==
'>'))
2954 if (data[j]==
'>') { level++; indent=j+1; }
2955 else if (j>0 && data[j-1]==
'>') indent=j+1;
2958 if (indent>0 && j>0 && data[j-1]==
'>' &&
2959 !(j==size || data[j]==
'\n'))
2976 isGitHubAlert =
true;
2977 isGitHubFirst =
true;
2978 startCmd = it->second;
2983 if (level!=1 || !isGitHubAlert)
2985 for (
int l=curLevel;l<level-1;l++)
2987 out+=
"<blockquote>";
2989 out +=
"<blockquote>‍";
2991 else if (!startCmd.empty())
2993 out += startCmd +
" ";
2996 else if (level<curLevel)
2998 int decrLevel = curLevel;
2999 if (level==0 && isGitHubAlert)
3003 for (
int l=level;l<decrLevel;l++)
3005 out +=
"</blockquote>\\ilinebr ";
3014 if (curLevel!=0 || !isGitHubAlert)
3016 std::string_view txt = data.substr(indent,
end-indent);
3019 if (!isGitHubFirst)
out +=
"<br>";
3026 isGitHubFirst =
false;
3041 for (
int l=0;l<curLevel;l++)
3043 out+=
"</blockquote>";
3054 size_t size = data.size();
3055 while (i<data.size() && data[i]==
' ') i++;
3058 size_t locStart = i;
3059 if (i>offset) locStart--;
3062 while (i+9<size && data[i]!=
'\n')
3074 location=data.substr(locStart,i-locStart);
3076 while (indent > 0 && i < size && data[i] ==
' ')
3081 if (i<size && data[i]==
'\n') i++;
3092 const size_t size = data.size();
3095 out+=
"@iverbatim\n";
3097 std::string location;
3102 while (
end<=size && data[
end-1]!=
'\n')
end++;
3105 while (j <
end && data[j] ==
' ')
3119 while (emptyLines>0)
3127 std::string lineLoc;
3132 out+=data.substr(offset,
end-offset);
3140 out+=
"@endiverbatim";
3141 if (!location.empty())
3149 while (emptyLines>0)
3165 const size_t size = data.size();
3166 size_t nb=0,
end=offset+1, j=0;
3171 if ((data[
end-1]==
'\\' || data[
end-1]==
'@') &&
3172 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3177 if (!endBlockName.
empty())
3179 size_t l = endBlockName.
length();
3180 for (;
end+l+1<size;
end++)
3182 if ((data[
end]==
'\\' || data[
end]==
'@') &&
3183 data[
end-1]!=
'\\' && data[
end-1]!=
'@'
3197 else if (nb==0 && data[
end-1]==
'<' && size>=6 &&
end+6<size &&
3198 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3201 if (tolower(data[
end])==
'p' && tolower(data[
end+1])==
'r' &&
3202 tolower(data[
end+2])==
'e' && (data[
end+3]==
'>' || data[
end+3]==
' '))
3213 else if (nb==0 && data[
end-1]==
'`')
3215 while (
end <= size && data[
end - 1] ==
'`')
3221 else if (nb>0 && data[
end-1]==
'`')
3224 while (
end <= size && data[
end - 1] ==
'`')
3242 size_t blockStart,
size_t blockEnd)
3245 if (!lang.empty() && lang[0]==
'.') lang=lang.substr(1);
3246 const size_t size=data.size();
3248 while (i<size && (data[i]==
' ' || data[i]==
'\t'))
3269 size_t pi=std::string::npos;
3270 bool newBlock =
false;
3271 bool insideList =
false;
3272 size_t currentIndent = refIndent;
3273 size_t listIndent = refIndent;
3274 const size_t size = data.size();
3281 size_t lineIndent=0;
3282 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3288 if (insideList && lineIndent<currentIndent)
3291 currentIndent = refIndent;
3299 if (listIndent<currentIndent+4)
3303 currentIndent = listIndent;
3310 currentIndent = listIndent;
3319 if (pi!=std::string::npos)
3321 size_t blockStart=0, blockEnd=0, blockOffset=0;
3324 auto addSpecialCommand = [&](
const DString &startCmd,
const DString &endCmd)
3326 size_t cmdPos = pi+blockStart+1;
3333 if (pl[ii]==
'\n') nl++;
3336 bool addNewLines =
false;
3338 (pl[ii]!=
'\\' && pl[ii]!=
'@') ||
3347 pl =
"@"+startCmd+
"\n" + pl +
"@"+endCmd;
3349 addNewLines =
false;
3364 if (addNewLines)
for (
int j=0;j<nl;j++)
out+=
'\n';
3366 if (addNewLines)
out+=
'\n';
3371 addSpecialCommand(
"startuml",
"enduml");
3375 addSpecialCommand(
"dot",
"enddot");
3377 else if (lang==
"msc")
3379 addSpecialCommand(
"msc",
"endmsc");
3381 else if (lang==
"mermaid")
3383 addSpecialCommand(
"mermaid",
"endmermaid");
3390 pi=std::string::npos;
3394 else if (
isBlockQuote(data.substr(pi,i-pi),currentIndent))
3397 pi=std::string::npos;
3404 out+=data.substr(pi,i-pi);
3410 if (pi!=std::string::npos && pi<size)
3420 warn(
fileName,
lineNr,
"Ending inside a fenced code block. Maybe the end marker for the block is missing?");
3422 out+=data.substr(pi);
3436 size_t pi = std::string::npos;
3452 size_t currentIndent = indent;
3453 size_t listIndent = indent;
3454 bool insideList =
false;
3455 bool newBlock =
false;
3458 while (i<data.size())
3463 size_t lineIndent=0;
3465 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3471 if (insideList && lineIndent<currentIndent)
3474 currentIndent = indent;
3482 if (listIndent<currentIndent+4)
3486 currentIndent = listIndent;
3493 currentIndent = listIndent;
3505 if (pi!=std::string::npos)
3507 size_t blockStart=0, blockEnd=0, blockOffset=0;
3509 size_t blockIndent = currentIndent;
3513 if (data[i]==
'@' || data[i]==
'\\') endBlockName =
isBlockCommand(data.substr(i),i);
3514 if (!endBlockName.
empty())
3517 if (
isLinkRef(data.substr(pi,i-pi),
id,link,title))
3527 size_t l = endBlockName.
length();
3528 while (i+l<data.size())
3530 if ((data[i]==
'\\' || data[i]==
'@') &&
3531 data[i-1]!=
'\\' && data[i-1]!=
'@')
3548 while (pi<data.size() && data[pi]==
' ') pi++;
3552 if (!header.
empty())
3556 out+=level==1?
"@section ":
"@subsection ";
3564 out+=level==1?
"<h1>":
"<h2>";
3566 out+=level==1?
"\n</h1>\n":
"\n</h2>\n";
3573 pi=std::string::npos;
3578 else if ((ref=
isLinkRef(data.substr(pi),
id,link,title)))
3592 pi=std::string::npos;
3600 pi=std::string::npos;
3607 pi=std::string::npos;
3620 if (pi!=std::string::npos && pi<data.size())
3622 if (
isLinkRef(data.substr(pi),
id,link,title))
3639#define OPC(x) if (literal_at(data,#x " ") || literal_at(data,#x "\n")) return true
3640 OPC(dir);
OPC(defgroup);
OPC(addtogroup);
OPC(weakgroup);
OPC(ingroup);
3643 OPC(protocol);
OPC(category);
OPC(
union);
OPC(
struct);
OPC(interface);
3644 OPC(idlexcept);
OPC(file);
3654 std::string_view data(docs.
str());
3655 const size_t size = data.size();
3658 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3665 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3671 (data[i]==
'\\' || data[i]==
'@') &&
3686 else if (i+1<size && (data[i]==
'\\' || data[i]==
'@') &&
isOtherPage(data.substr(i+1)))
3704 std::string_view data(docs_org.
str());
3705 const size_t size = data.size();
3707 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3709 if (data[i]==
'\n') prepend++;
3712 if (i>=size) {
return DString(); }
3714 while (end1<size && data[end1-1]!=
'\n') end1++;
3721 while (end2<size && data[end2-1]!=
'\n') end2++;
3722 if (
prv->isHeaderline(data.substr(end1),
false))
3724 title = data.
substr(i,end1-i-1);
3725 docs+=
"\n\n"+docs_org.
mid(end2);
3726 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3732 if (i<end1 && prv->isAtxHeader(data.substr(i,end1-i),title,
id,
false,&isIdGenerated)>0)
3735 docs+=docs_org.
mid(end1);
3740 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3751 if (input.
empty())
return input;
3756 if (s.
at(s.
length()-1)!=
'\n') s +=
"\n";
3757 s =
detab(s,refIndent);
3761 s =
prv->processQuotations(s.
view(),refIndent);
3765 s =
prv->processBlocks(s.
view(),refIndent);
3783 const char *p = result.
data();
3786 while (*p==
' ') p++;
3787 while (*p==
'\n') {startNewlines++;p++;};
3790 if (p>result.
data())
3793 result = result.
mid(
static_cast<int>(p-result.
data()));
3829 const char *fileBuf,
3830 const std::shared_ptr<Entry> &root,
3833 std::shared_ptr<Entry> current = std::make_shared<Entry>();
3835 current->lang = SrcLangExt::Markdown;
3836 current->fileName = fileName;
3837 current->docFile = fileName;
3838 current->docLine = 1;
3844 bool isIdGenerated =
false;
3852 int indentLevel=title.
empty() ? 0 : -1;
3859 bool wasEmpty =
id.empty();
3860 if (wasEmpty)
id = mdFileNameId;
3866 if (!mdfileAsMainPage.
empty() &&
3870 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3871 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3873 else if (
id==
"mainpage" ||
id==
"index")
3875 if (title.
empty()) title = titleFn;
3876 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3877 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3879 else if (isSubdirDocs)
3883 docs.
prepend(
"@section " + generatedId +
" " + title +
"\\ilinebr ");
3885 docs.
prepend(
"@dir\\ilinebr ");
3896 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr @ianchor{" + relFileName +
"} " + mdFileNameId +
"\\ilinebr ");
3898 else if (!generatedId.
empty())
3900 docs.
prepend(
"@ianchor " + generatedId +
"\\ilinebr ");
3902 else if (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB)
3905 docs.
prepend(
"@ianchor{" + title +
"} " + autoId +
"\\ilinebr ");
3907 docs.
prepend(
"@page "+
id+
" "+title+
"\\ilinebr ");
3909 for (
int i = 0; i < prepend; i++) docs.
prepend(
"\n");
3914 static const reg::Ex re(R
"([ ]*[\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
3916 std::string s = docs.str();
3919 DString orgLabel = match[1].str();
3920 DString orgTitle = match[2].str();
3923 docs = docs.
left(match[1].position())+
3926 "\\ilinebr @ianchor{" + orgTitle +
"} "+orgLabel+
"\n"+
3927 docs.
mid(match.length());
3938 p->commentScanner.enterFile(fileName,lineNr);
3940 bool needsEntry =
false;
3944 while (
p->commentScanner.parseCommentBlock(
3962 DString docFile = current->docFile;
3963 root->moveToSubEntryAndRefresh(current);
3964 current->lang = SrcLangExt::Markdown;
3965 current->docFile = docFile;
3966 current->docLine = lineNr;
3971 root->moveToSubEntryAndKeep(current);
3973 p->commentScanner.leaveFile(fileName,lineNr);
#define eol
The end of line string for this machine.
static AnchorGenerator & instance()
Returns the singleton instance.
static std::string addPrefixIfNeeded(const std::string &anchor)
std::string generate(const std::string &title)
generates an anchor for a section with title.
Clang parser object for a single translation unit, which consists of a source file and the directly o...
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
DString & setNum(short n)
size_t rfind(char c, size_t pos=npos) const
DString mid(size_t index, size_t len=npos) const
DString simplifyWhiteSpace() const
return a copy of this string with leading and trailing whitespace removed and multiple internal white...
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
DString substr(size_t pos=0, size_t count=npos) const
Returns a substring of length count starting at pos.
std::string_view view() const
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
char & at(size_t i)
Returns a reference to the character at index i.
size_t size() const
Returns the length of the string, not counting the 0-terminator.
DString & prepend(const char *s)
size_t find(char c, size_t pos=0) const
DString & sprintf(const char *format,...)
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
DString left(size_t len) const
const std::string & str() const
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
bool startsWith(const char *s) const
bool endsWith(const char *s) const
size_t length() const
Returns the length of the string, not counting the 0-terminator.
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
static ParserManager * parserManager
static FileNameLinkedMap * imageNameLinkedMap
A model of a file symbol.
Minimal replacement for QFileInfo.
std::string fileName() const
std::string absFilePath() const
FileDef * findFileDef(const DString &n, bool &ambig) const
Returns the file definition in fnMap that matches the file name n.
Helper class to process markdown formatted text.
DString process(const DString &input, int &startNewlines, bool fromParseInput=false)
static ActionTable_t fill_table()
std::array< Action_t, 256 > ActionTable_t
std::unique_ptr< Private > prv
DString extractPageTitle(DString &docs, DString &id, int &prepend, bool &isIdGenerated)
static ActionTable_t actions
void setIndentLevel(int level)
std::function< int(Private &, std::string_view, size_t)> Action_t
Markdown(const DString &fileName, int lineNr, int indentLevel=0)
void parseInput(const DString &fileName, const char *fileBuf, const std::shared_ptr< Entry > &root, ClangTUParser *clangParser) override
Parses a single input file with the goal to build an Entry tree.
~MarkdownOutlineParser() override
void parsePrototype(const DString &text) override
Callback function called by the comment block scanner.
std::unique_ptr< Private > p
std::unique_ptr< OutlineParserInterface > getOutlineParser(const DString &extension)
Gets the interface to the parser associated with a given extension.
static bool isEnabled()
Returns true if doxygen has been configured to run PlantUML, i.e.
static constexpr int Section
static constexpr int MaxLevel
static constexpr int Subsection
static constexpr int Subsubsection
static constexpr int MinLevel
static constexpr int Paragraph
static constexpr int Subsubparagraph
static constexpr int Subparagraph
Class representing a regular expression.
Object representing the matching results.
#define Config_getInt(name)
#define Config_getBool(name)
#define Config_getString(name)
#define Config_getEnum(name)
std::vector< std::string > StringVector
DirIterator end(const DirIterator &) noexcept
#define AUTO_TRACE_ADD(...)
#define AUTO_TRACE_EXIT(...)
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
int dstrncmp(const char *str1, const char *str2, size_t len)
const char * qPrint(const char *s)
bool isId(int c)
Returns true if c is a valid character for an identifier.
static DString escapeSpecialChars(const DString &s)
static bool isOtherPage(std::string_view data)
static constexpr bool isAllowedEmphStr(const std::string_view &data, size_t offset)
static bool hasLineBreak(std::string_view data)
@ explicitMainPage
docs start with a mainpage command
@ explicitPage
docs start with a page command
@ notExplicit
docs doesn't start with either page or mainpage
@ explicitOtherPage
docs start with a dir / defgroup / addtogroup command
static bool isBlockQuote(std::string_view data, size_t indent)
returns true if this line starts a block quote
static constexpr Alignment markersToAlignment(bool leftMarker, bool rightMarker)
helper function to convert presence of left and/or right alignment markers to an alignment value
static bool isEndOfList(std::string_view data)
static bool isFencedCodeBlock(std::string_view data, size_t refIndent, DString &lang, size_t &start, size_t &end, size_t &offset, DString &fileName, int lineNr)
static size_t computeIndentExcludingListMarkers(std::string_view data)
static const char * g_doxy_nbsp
static constexpr bool ignoreCloseEmphChar(char c, char cn)
static DString getFilteredImageAttributes(std::string_view fmt, const DString &attrs)
parse the image attributes and return attributes for given format
static constexpr bool isOpenEmphChar(char c)
static bool isCodeBlock(std::string_view data, size_t offset, size_t &indent)
static bool isEmptyLine(std::string_view data)
#define AUTO_TRACE_EXIT(...)
static size_t findTableColumns(std::string_view data, size_t &start, size_t &end, size_t &columns)
Finds the location of the table's contains in the string data.
static const size_t codeBlockIndent
static constexpr bool isIdChar(char c)
static ExplicitPageResult isExplicitPage(const DString &docs)
static const char * g_utf8_nbsp
static const std::unordered_map< std::string, std::string > g_quotationHeaderMap
static size_t isListMarker(std::string_view data)
static bool isHRuler(std::string_view data)
bool skipOverFileAndLineCommands(std::string_view data, size_t indent, size_t &offset, std::string &location)
static size_t isLinkRef(std::string_view data, DString &refid, DString &link, DString &title)
returns end of the link ref if this is indeed a link reference.
static constexpr bool extraChar(char c)
static DString escapeDoubleQuotes(const DString &s)
static bool isTableBlock(std::string_view data)
Returns true iff data points to the start of a table block.
static constexpr bool isUtf8Nbsp(char c1, char c2)
size_t isNewline(std::string_view data)
DString markdownFileNameToId(const DString &fileName)
processes string s and converts markdown into doxygen/html commands.
#define warn(file, line, fmt,...)
const Mapper< CommandType > * cmdMapper
bool isAbsolutePath(const DString &fileName)
const char * strnstr(const char *haystack, const char *needle, size_t haystack_len)
DString trunc(const DString &s, size_t numChars=15)
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Portable versions of functions that are platform dependent.
Some helper functions for std::string.
bool literal_at(const char *data, const char(&str)[N])
returns true iff data points to a substring that matches string literal str
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
std::string_view stripWhiteSpace(std::string_view s)
Given a string view s, returns a new, narrower view on that string, skipping over any leading or trai...
LinkRef(const DString &l, const DString &t)
DString isBlockCommand(std::string_view data, size_t offset)
DString extractTitleId(DString &title, int level, bool *pIsIdGenerated=nullptr)
int processEmphasis1(std::string_view data, char c)
process single emphasis
int processQuoted(std::string_view data, size_t offset)
Process quoted section "...", can contain one embedded newline.
size_t writeTableBlock(std::string_view data)
size_t writeBlockQuote(std::string_view data)
Private(const DString &fn, int line, int indent)
void writeMarkdownImage(std::string_view fmt, bool inline_img, bool explicitTitle, const DString &title, const DString &content, const DString &link, const DString &attributes, const FileDef *fd)
size_t isSpecialCommand(std::string_view data, size_t offset)
int processEmphasis3(std::string_view data, char c)
Parsing triple emphasis.
int processCodeSpan(std::string_view data, size_t offset)
` parsing a code span (assuming codespan != 0)
int processSpecialCommand(std::string_view data, size_t offset)
void writeFencedCodeBlock(std::string_view data, std::string_view lang, size_t blockStart, size_t blockEnd)
int isHeaderline(std::string_view data, bool allowAdjustLevel)
returns whether the line is a setext-style hdr underline
size_t findEmphasisChar(std::string_view, char c, size_t c_size)
looks for the next emph char, skipping other constructs, and stopping when either it is found,...
std::unordered_map< std::string, LinkRef > linkRefs
void addStrEscapeUtf8Nbsp(std::string_view data)
size_t writeCodeBlock(std::string_view, size_t refIndent)
int processHtmlTag(std::string_view data, size_t offset)
int processEmphasis(std::string_view data, size_t offset)
int processLink(std::string_view data, size_t offset)
int isAtxHeader(std::string_view data, DString &header, DString &id, bool allowAdjustLevel, bool *pIsIdGenerated=nullptr)
int processHtmlTagWrite(std::string_view data, size_t offset, bool doWrite)
Process a HTML tag.
size_t findEndOfLine(std::string_view data, size_t offset)
int processEmphasis2(std::string_view data, char c)
process double emphasis
DString processQuotations(std::string_view data, size_t refIndent)
void processInline(std::string_view data)
int processNmdash(std::string_view data, size_t offset)
Process ndash and mdashes.
DString processBlocks(std::string_view data, size_t indent)
void writeOneLineHeaderOrRuler(std::string_view data)
CommentScanner commentScanner
bool isURL(const DString &url)
Checks whether the given url starts with a supported protocol.
DString detab(const DString &s, size_t &refIndent)
DString externalLinkTarget(const bool parent)
DString stripExtensionGeneral(const DString &fName, const DString &ext)
DString getFileNameExtension(const DString &fn)
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
DString escapeCharsInString(const DString &name, bool allowDots, bool allowUnderscore)
DString stripIndentation(const DString &s, bool skipFirstLine)
DString stripFromPath(const DString &path)
A bunch of utility functions.