36#include <unordered_map>
59#if !ENABLE_MARKDOWN_TRACING
63#define AUTO_TRACE(...) (void)0
64#define AUTO_TRACE_ADD(...) (void)0
65#define AUTO_TRACE_EXIT(...) (void)0
81 return (c>=
'a' && c<=
'z') ||
84 (
static_cast<unsigned char>(c)>=0x80);
90 return c==
'-' || c==
'+' || c==
'!' || c==
'?' || c==
'$' || c==
'@' ||
91 c==
'&' || c==
'*' || c==
'_' || c==
'%' || c==
'[' || c==
'(' ||
92 c==
'.' || c==
'>' || c==
':' || c==
',' || c==
';' || c==
'\'' ||
93 c==
'"' || c==
'`' || c==
'\\';
99 return c==
'\n' || c==
' ' || c==
'\'' || c==
'<' ||
100 c==
'>' || c==
'{' || c==
'(' || c==
'[' ||
101 c==
',' || c==
':' || c==
';';
107 return c1==
static_cast<char>(0xc2) && c2==
static_cast<char>(0xa0);
113 (offset>1 && !
isUtf8Nbsp(data.data()[-2],data.data()[-1])));
120 return c==
'(' || c==
'{' || c==
'[' || (c==
'<' && cn!=
'/') || c==
'\\' || c==
'@';
151 int processLink(std::string_view data,
size_t offset);
159 int isHeaderline(std::string_view data,
bool allowAdjustLevel);
161 bool *pIsIdGenerated=
nullptr);
164 size_t blockStart,
size_t blockEnd);
193 a[
static_cast<unsigned int>(
'[')] = [](
Markdown::Private &obj,std::string_view data,
size_t offset) {
return obj.
processLink (data,offset); };
194 a[
static_cast<unsigned int>(
'!')] = [](
Markdown::Private &obj,std::string_view data,
size_t offset) {
return obj.
processLink (data,offset); };
204 :
prv(
std::make_unique<
Private>(fileName,lineNr,indentLevel))
206 using namespace std::placeholders;
230 if (data[0] ==
'\n')
return 1;
234 return (data.size()>8 && data[8]==
' ') ? 9 : 8;
243 if (s.
empty())
return s;
245 const char *p=s.
data();
249 if (c==
'"' && pc!=
'\\') result+=
'\\';
261 if (s.
empty())
return s;
262 bool insideQuote=
false;
264 const char *p=s.
data();
279 insideQuote=!insideQuote;
291 if ((p[0]==
':') && (p[1]==
':'))
303 case '\\':
if (!insideQuote) { result+=
'\\'; } result+=
'\\';
break;
304 case '@':
if (!insideQuote) { result+=
'\\'; } result+=
'@';
break;
307 case '#':
if (!insideQuote) { result+=
'\\'; } result+=
'#';
break;
308 case '$':
if (!insideQuote) { result+=
'\\'; } result+=
'$';
break;
309 case '&':
if (!insideQuote) { result+=
'\\'; } result+=
'&';
break;
324 if (leftMarker && rightMarker)
332 else if (rightMarker)
347 for (
const auto &attr_ : attrList)
356 return attr.
mid(i+1);
395 using EndBlockFunc =
DString (*)(
const std::string &,bool,char);
397 static constexpr auto getEndBlock = [](
const std::string &blockName,bool,char) ->
DString
399 return "end"+blockName;
401 static constexpr auto getEndCode = [](
const std::string &blockName,
bool openBracket,char) ->
DString
403 return openBracket ?
DString(
"}") :
"end"+blockName;
405 static constexpr auto getEndUml = [](
const std::string &,bool,char) ->
DString
409 static constexpr auto getEndFormula = [](
const std::string &,bool,
char nextChar) ->
DString
413 case '$':
return "f$";
414 case '(':
return "f)";
415 case '[':
return "f]";
416 case '{':
return "f}";
422 static const std::unordered_map<std::string,EndBlockFunc> blockNames =
424 {
"dot", getEndBlock },
425 {
"code", getEndCode },
426 {
"icode", getEndBlock },
427 {
"msc", getEndBlock },
428 {
"verbatim", getEndBlock },
429 {
"iverbatim", getEndBlock },
430 {
"iliteral", getEndBlock },
431 {
"latexonly", getEndBlock },
432 {
"htmlonly", getEndBlock },
433 {
"xmlonly", getEndBlock },
434 {
"rtfonly", getEndBlock },
435 {
"manonly", getEndBlock },
436 {
"docbookonly", getEndBlock },
437 {
"startuml", getEndUml },
438 {
"mermaid", getEndBlock },
439 {
"f", getEndFormula }
442 const size_t size = data.
size();
443 bool openBracket = offset>0 && data.data()[-1]==
'{';
444 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
445 if (isEscaped)
return result;
448 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
449 if (
end==1)
return result;
450 std::string blockName(data.substr(1,
end-1));
451 auto it = blockNames.find(blockName);
452 if (it!=blockNames.end())
454 result = it->second(blockName, openBracket,
end<size ? data[
end] : 0);
464 using EndCmdFunc = size_t (*)(std::string_view,size_t);
466 static constexpr auto endOfLine = [](std::string_view data_,
size_t offset_) ->
size_t
471 while (offset_<data_.size() && ((c=data_[offset_])!=
'\n' || lc==
'\\'))
473 if (c==
'\\') lc=
'\\';
474 else if (c!=
' ') lc=0;
480 static constexpr auto endOfLabels = [](std::string_view data_,
size_t offset_,
bool multi_) ->
size_t
482 if (offset_<data_.size() && data_[offset_]==
' ')
490 while (offset_<data_.size() && data_[offset_]==
' ')
495 while (offset_<data_.size() && (c=data_[offset_])!=
' ' && c!=
',' && c!=
'\\' && c!=
'@' && c!=
'\n')
500 if (multi_ && offset_<data_.size() && (data_[offset_]==
',' || data_[offset_]==
' '))
502 size_t off = offset_;
503 while (off<data_.size() && data_[off]==
' ')
507 if (off<data_.size() && data_[off]==
',')
526 static constexpr auto endOfLabel = [](std::string_view data_,
size_t offset_) ->
size_t
528 return endOfLabels(data_,offset_,
false);
531 static constexpr auto endOfLabelOpt = [](std::string_view data_,
size_t offset_) ->
size_t
533 size_t index=offset_;
534 if (index<data_.size() && data_[index]==
' ')
537 while (index<data_.size() && data_[index]==
' ') index++;
539 if (index<data_.size() && data_[index]==
'{')
543 while (index<data_.size() && (c=data_[index])!=
'}' && c!=
'\\' && c!=
'@' && c!=
'\n') index++;
544 if (index==data_.size() || data_[index]!=
'}')
return 0;
547 return endOfLabel(data_,offset_);
550 static constexpr auto endOfParam = [](std::string_view data_,
size_t offset_) ->
size_t
552 size_t index=offset_;
553 if (index<data_.size() && data_[index]==
' ')
556 while (index<data_.size() && data_[index]==
' ') index++;
558 if (index<data_.size() && data_[index]==
'[')
562 while (index<data_.size() && (c=data_[index])!=
']' && c!=
'\n') index++;
563 if (index==data_.size() || data_[index]!=
']')
return 0;
566 return endOfLabels(data_,offset_,
true);
569 static constexpr auto endOfRetVal = [](std::string_view data_,
size_t offset_) ->
size_t
571 return endOfLabels(data_,offset_,
true);
574 static constexpr auto endOfFuncLike = [](std::string_view data_,
size_t offset_,
bool allowSpaces) ->
size_t
576 if (offset_<data_.size() && data_[offset_]==
' ')
581 while (offset_<data_.size() && data_[offset_]==
' ')
586 while (offset_<data_.size() && (c=data_[offset_])!=
'\n' && (allowSpaces || c!=
' ') && c!=
'(')
588 if (
literal_at(data_.substr(offset_),
"\\ilinebr "))
break;
595 while (offset_<data_.size() && (c=data_[offset_++]))
598 else if (c==
')') count--;
599 if (count==0)
return offset_;
607 static constexpr auto endOfFunc = [](std::string_view data_,
size_t offset_) ->
size_t
609 return endOfFuncLike(data_,offset_,
true);
612 static constexpr auto endOfGuard = [](std::string_view data_,
size_t offset_) ->
size_t
614 return endOfFuncLike(data_,offset_,
false);
617 static const std::unordered_map<std::string,EndCmdFunc> cmdNames =
620 {
"addindex", endOfLine },
621 {
"addtogroup", endOfLabel },
622 {
"anchor", endOfLabel },
625 {
"category", endOfLine },
626 {
"cite", endOfLabelOpt },
627 {
"class", endOfLine },
628 {
"concept", endOfLine },
629 {
"copybrief", endOfFunc },
630 {
"copydetails", endOfFunc },
631 {
"copydoc", endOfFunc },
632 {
"def", endOfFunc },
633 {
"defgroup", endOfLabel },
634 {
"diafile", endOfLine },
635 {
"dir", endOfLine },
636 {
"dockbookinclude",endOfLine },
637 {
"dontinclude", endOfLine },
638 {
"dotfile", endOfLine },
640 {
"elseif", endOfGuard },
641 {
"em", endOfLabel },
642 {
"emoji", endOfLabel },
643 {
"enum", endOfLabel },
644 {
"example", endOfLine },
645 {
"exception", endOfLabel },
646 {
"extends", endOfLabel },
647 {
"file", endOfLine },
649 {
"headerfile", endOfLine },
650 {
"htmlinclude", endOfLine },
651 {
"ianchor", endOfLabelOpt },
652 {
"idlexcept", endOfLine },
653 {
"if", endOfGuard },
654 {
"ifnot", endOfGuard },
655 {
"image", endOfLine },
656 {
"implements", endOfLine },
657 {
"include", endOfLine },
658 {
"includedoc", endOfLine },
659 {
"includelineno", endOfLine },
660 {
"ingroup", endOfLabel },
661 {
"interface", endOfLine },
662 {
"latexinclude", endOfLine },
663 {
"maninclude", endOfLine },
664 {
"memberof", endOfLabel },
665 {
"mermaidfile", endOfLine },
666 {
"mscfile", endOfLine },
667 {
"namespace", endOfLabel },
668 {
"noop", endOfLine },
669 {
"overload", endOfLine },
671 {
"package", endOfLabel },
672 {
"page", endOfLabel },
673 {
"paragraph", endOfLabel },
674 {
"param", endOfParam },
675 {
"property", endOfLine },
676 {
"protocol", endOfLine },
677 {
"qualifier", endOfLine },
678 {
"ref", endOfLabel },
679 {
"refitem", endOfLine },
680 {
"related", endOfLabel },
681 {
"relatedalso", endOfLabel },
682 {
"relates", endOfLabel },
683 {
"relatesalso", endOfLabel },
684 {
"requirement", endOfLabel },
685 {
"retval", endOfRetVal},
686 {
"rtfinclude", endOfLine },
687 {
"section", endOfLabel },
688 {
"skip", endOfLine },
689 {
"skipline", endOfLine },
690 {
"snippet", endOfLine },
691 {
"snippetdoc", endOfLine },
692 {
"snippetlineno", endOfLine },
693 {
"struct", endOfLine },
694 {
"satisfies", endOfLabel },
695 {
"subpage", endOfLabel },
696 {
"subparagraph", endOfLabel },
697 {
"subsubparagraph",endOfLabel },
698 {
"subsection", endOfLabel },
699 {
"subsubsection", endOfLabel },
700 {
"throw", endOfLabel },
701 {
"throws", endOfLabel },
702 {
"tparam", endOfLabel },
703 {
"typedef", endOfLine },
704 {
"plantumlfile", endOfLine },
705 {
"union", endOfLine },
706 {
"until", endOfLine },
707 {
"var", endOfLine },
708 {
"verbinclude", endOfLine },
709 {
"verifies", endOfLabel },
710 {
"weakgroup", endOfLabel },
711 {
"xmlinclude", endOfLine },
712 {
"xrefitem", endOfLabel }
715 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
716 if (isEscaped)
return 0;
718 const size_t size = data.size();
720 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
721 if (
end==1)
return 0;
722 std::string cmdName(data.substr(1,
end-1));
724 auto it = cmdNames.find(cmdName);
725 if (it!=cmdNames.end())
728 result = it->second(data,
end);
741 const size_t size = data.size();
745 while (i<size && data[i]!=c &&
746 data[i]!=
'\\' && data[i]!=
'@' &&
747 !(data[i]==
'/' && data[i-1]==
'<') &&
767 while (i+len<size && data[i+len]==c)
774 if (len!=c_size || (i+len<size &&
isIdChar(data[i+len])))
780 return static_cast<int>(i);
788 while (i < size && data[i] ==
'`')
796 while (i<size && enb<snb)
798 if (data[i]==
'`') enb++;
799 if (snb==1 && data[i]==
'\'')
break;
803 else if (data[i]==
'@' || data[i]==
'\\')
806 if (!endBlockName.
empty())
809 size_t l = endBlockName.
length();
812 if ((data[i]==
'\\' || data[i]==
'@') &&
813 data[i-1]!=
'\\' && data[i-1]!=
'@')
823 else if (i+1<size &&
isIdChar(data[i+1]))
832 else if (data[i-1]==
'<' && data[i]==
'/')
836 else if (data[i]==
'\n')
839 while (i<size && data[i]==
' ') i++;
840 if (i>=size || data[i]==
'\n')
858 const size_t size = data.size();
861 if (size>1 && data[0]==c && data[1]==c) { i=1; }
866 if (len==0) {
return 0; }
868 if (i>=size) {
return 0; }
870 if (i+1<size && data[i+1]==c)
875 if (data[i]==c && data[i-1]!=
' ' && data[i-1]!=
'\n')
881 return static_cast<int>(i+1);
892 const size_t size = data.size();
902 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=
' ' && data[i-1]!=
'\n')
904 if (c ==
'~')
out+=
"<strike>";
905 else out+=
"<strong>";
907 if (c ==
'~')
out+=
"</strike>";
908 else out+=
"</strong>";
910 return static_cast<int>(i+2);
924 const size_t size = data.size();
936 if (data[i]!=c || data[i-1]==
' ' || data[i-1]==
'\n')
941 if (i+2<size && data[i+1]==c && data[i+2]==c)
945 out+=
"</strong></em>";
947 return static_cast<int>(i+3);
949 else if (i+1<size && data[i+1]==c)
960 return static_cast<int>(len - 2);
974 return static_cast<int>(len - 1);
985 const size_t size = data.size();
989 if (i<size && data[i]==
'-')
994 if (i<size && data[i]==
'-')
999 if (i<size && data[i]==
'-')
1003 if (count>=2 && offset>=2 &&
literal_at(data.data()-2,
"<!"))
1005 if (count==2 && size > 2 && data[2]==
'>')
1007 if (count==3 && size > 3 && data[3]==
'>')
1009 if (count==2 && (offset<8 || !
literal_at(data.data()-8,
"operator")))
1029 const size_t size = data.size();
1032 while (i<size && data[i]!=
'"' && nl<2)
1034 if (data[i]==
'\n') nl++;
1037 if (i<size && data[i]==
'"' && nl<2)
1039 out+=data.substr(0,i+1);
1041 return static_cast<int>(i+1);
1053 if (offset>0 && data.data()[-1]==
'\\') {
return 0; }
1055 const size_t size = data.size();
1061 while (i < size &&
isIdChar(data[i]))
1066 DString tagName(data.substr(1,i-1));
1067 if (tagName.
lower()==
"pre")
1069 bool insideStr=
false;
1073 if (!insideStr && c==
'<')
1075 if (data[i+1]==
'/' &&
1076 tolower(data[i+2])==
'p' && tolower(data[i+3])==
'r' &&
1077 tolower(data[i+4])==
'e' && tolower(data[i+5])==
'>')
1079 if (doWrite)
out+=data.substr(0,i+6);
1082 return static_cast<int>(i+6);
1085 else if (insideStr && c==
'"')
1087 if (data[i-1]!=
'\\') insideStr=
false;
1100 if (data[i]==
'/' && i+1<size && data[i+1]==
'>')
1103 if (doWrite)
out+=data.substr(0,i+2);
1105 return static_cast<int>(i+2);
1107 else if (data[i]==
'>')
1110 if (doWrite)
out+=data.substr(0,i+1);
1112 return static_cast<int>(i+1);
1114 else if (data[i]==
' ')
1117 bool insideAttr=
false;
1120 if (!insideAttr && data[i]==
'"')
1124 else if (data[i]==
'"' && data[i-1]!=
'\\')
1128 else if (!insideAttr && data[i]==
'>')
1131 if (doWrite)
out+=data.substr(0,i+1);
1133 return static_cast<int>(i+1);
1153 const size_t size = data.size();
1165 if (size>2 && c!=
'~' && data[1]!=c)
1168 if (data[1]==
' ' || data[1]==
'\n' ||
1176 if (size>3 && data[1]==c && data[2]!=c)
1178 if (data[2]==
' ' || data[2]==
'\n' ||
1186 if (size>4 && c!=
'~' && data[1]==c && data[2]==c && data[3]!=c)
1188 if (data[3]==
' ' || data[3]==
'\n' ||
1200 std::string_view
fmt,
bool inline_img,
bool explicitTitle,
1205 AUTO_TRACE(
"fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1216 out+=link.
mid(fd ? 0 : 5);
1217 if (!explicitTitle && !content.
empty())
1223 else if ((content.
empty() || explicitTitle) && !title.
empty())
1233 if (!attributes.
empty())
1245 const size_t size = data.size();
1250 bool isImageLink =
false;
1251 bool isImageInline =
false;
1257 if (size<2 || data[1]!=
'[')
1266 while (pos>=-
static_cast<int>(offset) && numNLsNeeded>0)
1268 if (data.data()[pos]==
'\n') numNLsNeeded--;
1269 else if (data.data()[pos]!=
' ')
1279 size_t contentStart=i;
1286 if (data[i-1]==
'\\')
1289 else if (data[i]==
'[')
1293 else if (data[i]==
']')
1296 if (level<=0)
break;
1298 else if (data[i]==
'\n')
1301 if (nl>1) {
return 0; }
1307 if (i>=size)
return 0;
1308 size_t contentEnd=i;
1309 content = data.
substr(contentStart,contentEnd-contentStart);
1311 if (!isImageLink && content.
empty()) {
return 0; }
1314 bool whiteSpace =
false;
1316 while (i<size && data[i]==
' ') { whiteSpace =
true; i++; }
1317 if (i<size && data[i]==
'\n')
1322 while (i<size && data[i]==
' ') i++;
1324 if (whiteSpace && i<size && (data[i]==
'(' || data[i]==
'['))
return 0;
1326 bool explicitTitle=
false;
1327 if (i<size && data[i]==
'(')
1330 while (i<size && data[i]==
' ') i++;
1331 bool uriFormat=
false;
1332 if (i<size && data[i]==
'<') { i++; uriFormat=
true; }
1336 while (i<size && data[i]!=
'\'' && data[i]!=
'"' && braceCount>0)
1342 if (nlConsec>1) {
return 0; }
1344 else if (data[i]==
'(')
1349 else if (data[i]==
')')
1354 else if (data[i]!=
' ')
1365 if (i>=size || data[i]==
'\n') {
return 0; }
1366 link = data.
substr(linkStart,i-linkStart);
1369 if (link.
empty()) {
return 0; }
1373 if (data[i]==
'\'' || data[i]==
'"')
1377 size_t titleStart=i;
1383 if (nl>1) {
return 0; }
1386 else if (data[i]==
'\\')
1390 else if (data[i]==c)
1401 size_t titleEnd = i-1;
1403 while (titleEnd>titleStart && data[titleEnd]==
' ') titleEnd--;
1404 if (data[titleEnd]==c)
1406 title = data.
substr(titleStart,titleEnd-titleStart);
1410 if (data[i]==
' ')i++;
1411 else if (data[i] ==
')')
break;
1425 else if (i<size && data[i]==
'[')
1431 while (i<size && data[i]!=
']')
1436 if (nl>1) {
return 0; }
1440 if (i>=size) {
return 0; }
1442 link = data.
substr(linkStart,i-linkStart);
1454 link = lr_it->second.link;
1455 title = lr_it->second.title;
1465 else if (i<size && data[i]!=
':' && !content.
empty())
1472 link = lr_it->second.link;
1473 title = lr_it->second.title;
1477 else if (content==
"TOC")
1500 while (j<size && data[j]==
' ') { j++; }
1501 if (j<size && data[j]==
'{')
1506 size_t attributesStart=i;
1511 if (data[i-1]==
'\\')
1514 else if (data[i]==
'{')
1518 else if (data[i]==
'}')
1521 if (level<=0)
break;
1523 else if (data[i]==
'\n')
1526 if (nl>1) {
return 0; }
1531 if (i>=size)
return 0;
1532 size_t attributesEnd=i;
1533 attributes = data.
substr(attributesStart,attributesEnd-attributesStart);
1542 while (pos<size && numNLsNeeded>0)
1544 if (data[pos]==
'\n') numNLsNeeded--;
1545 else if (data[pos]!=
' ')
1560 out+=
"@tableofcontents{html:";
1565 else if (isImageLink)
1574 writeMarkdownImage(
"html", isImageInline, explicitTitle, title, content, link, attributes, fd);
1575 writeMarkdownImage(
"latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
1576 writeMarkdownImage(
"rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
1577 writeMarkdownImage(
"docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
1578 writeMarkdownImage(
"xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
1602 (lang==SrcLangExt::Markdown && !
isURL(link)))
1634 if (explicitTitle && !title.
empty())
1649 if (lp==0 || (lp>0 && !
isURL(link) &&
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
1663 for (
int ii = 0; ii < nlTotal; ii++)
out+=
"\n";
1676 bool foundNameRef =
false;
1677 if (!content.
empty() && (content.
at(0)==
'#' || content.
at(0)==
'@'))
1680 while (endOfId<content.
length() &&
isId(content.
at(endOfId))) endOfId++;
1687 foundNameRef =
true;
1706 return static_cast<int>(i);
1713 const size_t size = data.size();
1717 while (nb<size && data[nb]==
'`')
1734 if (
end+1<size && data[
end+1]==
'`')
1749 if (
end+1<size && data[
end+1]==
'`')
1762 else if (data[
end]==
'\n')
1773 else if (!markdownStrict && data[
end]==
'\'' && nb==1 && (
end+1==size || (
end+1<size && data[
end+1]!=
'\'' && !
isIdChar(data[
end+1]))))
1776 out+=data.substr(nb,
end-nb);
1779 return static_cast<int>(
end+1);
1781 else if (!markdownStrict && data[
end]==
'\'' && nb==2 &&
end+1<size && data[
end+1]==
'\'')
1784 out+=data.substr(nb,
end-nb);
1787 return static_cast<int>(
end+2);
1791 if (data[
end]!=
' ') pc = data[
end];
1795 if (i < nb && end >= size)
1800 out+=data.substr(0,nb);
1801 return static_cast<int>(nb);
1805 while (
end<size && data[
end]==
'`')
1821 return static_cast<int>(
end);
1840 const size_t size = data.size();
1843 if (!endBlockName.
empty())
1846 size_t l = endBlockName.
length();
1849 if ((data[i]==
'\\' || data[i]==
'@') &&
1850 data[i-1]!=
'\\' && data[i-1]!=
'@')
1857 return static_cast<int>(i+1+l);
1866 out+=data.substr(0,endPos);
1867 return static_cast<int>(endPos);
1869 if (size>1 && (data[0]==
'\\' || data[0]==
'@'))
1872 if (c==
'[' || c==
']' || c==
'*' || c==
'(' || c==
')' || c==
'`' || c==
'_')
1878 else if (c==
'\\' || c==
'@')
1880 out+=data.substr(0,2);
1884 else if (c==
'-' && size>3 && data[2]==
'-' && data[3]==
'-')
1886 out+=data.substr(1,3);
1890 else if (c==
'-' && size>2 && data[2]==
'-')
1892 out+=data.substr(1,2);
1906 const size_t size = data.size();
1912 out+=data.substr(i,
end-i);
1913 if (
end>=size)
break;
1916 int iend = action(*
this,data.substr(i),i);
1934 const size_t size = data.size();
1935 while (i<size && data[i]==
' ') i++;
1936 if (i==size)
return 0;
1941 while (i < size && data[i] ==
'=')
1946 while (i<size && data[i]==
' ') i++;
1947 int level = (c>1 && (i>=size || data[i]==
'\n')) ? 1 : 0;
1948 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
1963 while (i < size && data[i] ==
'-')
1968 while (i<size && data[i]==
' ') i++;
1969 return (c>1 && (i>=size || data[i]==
'\n')) ?
indentLevel+2 : 0;
1979 const size_t size = data.size();
1980 while (i<size && data[i]==
' ') i++;
1985 while (i<size && (data[i]==
'>' || data[i]==
' '))
1987 if (data[i]==
'>') level++;
1992 bool res = (level>0 && i<size && ((data[i-1]==
' ') || data[i]==
'\n')) || (level > 1);
2007 const size_t size = data.size();
2010 while (i<size && data[i]==
' ') i++;
2011 if (i>=size || data[i]!=
'[') {
return 0; }
2013 size_t refIdStart=i;
2014 while (i<size && data[i]!=
'\n' && data[i]!=
']') i++;
2015 if (i>=size || data[i]!=
']') {
return 0; }
2016 refid = data.
substr(refIdStart,i-refIdStart);
2017 if (refid.
empty()) {
return 0; }
2021 if (i>=size || data[i]!=
':') {
return 0; }
2025 while (i<size && data[i]==
' ') i++;
2026 if (i<size && data[i]==
'\n')
2029 while (i<size && data[i]==
' ') i++;
2031 if (i>=size) {
return 0; }
2033 if (i<size && data[i]==
'<') i++;
2035 while (i<size && data[i]!=
' ' && data[i]!=
'\n') i++;
2037 if (i<size && data[i]==
'>') i++;
2038 if (linkStart==linkEnd) {
return 0; }
2039 link = data.
substr(linkStart,linkEnd-linkStart);
2041 if (link==
"@ref" || link==
"\\ref")
2044 while (i<size && data[i]!=
'\n' && data[i]!=
'"') i++;
2045 link+=data.
substr(argStart,i-argStart);
2052 while (i<size && data[i]==
' ') i++;
2053 if (i<size && data[i]==
'\n')
2057 while (i<size && data[i]==
' ') i++;
2061 AUTO_TRACE_EXIT(
"result={}: end of isLinkRef while looking for title",i);
2066 if (c==
'\'' || c==
'"' || c==
'(')
2071 size_t titleStart=i;
2073 while (i<size && data[i]!=
'\n') i++;
2078 while (
end>titleStart && data[
end]!=c)
end--;
2081 title = data.
substr(titleStart,
end-titleStart);
2085 while (i<size && data[i]==
' ') i++;
2097 size_t size = data.size();
2098 if (size>0 && data[size-1]==
'\n') size--;
2099 while (i<size && data[i]==
' ') i++;
2100 if (i>=size) {
AUTO_TRACE_EXIT(
"result=false: empty line");
return false; }
2102 if (c!=
'*' && c!=
'-' && c!=
'_')
2114 else if (data[i]!=
' ')
2129 static const reg::Ex r2(R
"({#(\a[\w-]*)}\s*$)");
2131 std::string ti = title.str();
2134 std::string
id = match[1].str();
2135 title = title.
left(match.position());
2138 warn(
fileName,
lineNr,
"An automatically generated id already has the name '{}'!",
id);
2144 if (((level>0) && (level<=
Config_getInt(TOC_INCLUDE_HEADINGS))) || (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2147 if (pIsIdGenerated) *pIsIdGenerated=
true;
2158 DString &header,
DString &
id,
bool allowAdjustLevel,
bool *pIsIdGenerated)
2162 int level = 0, blanks=0;
2163 const size_t size = data.size();
2166 while (i<size && data[i]==
' ') i++;
2167 if (i>=size || data[i]!=
'#')
2171 while (i < size && data[i] ==
'#')
2180 while (i < size && data[i] ==
' ')
2185 if (level==1 && blanks==0)
2192 while (
end<size && data[
end]!=
'\n')
end++;
2193 while (
end>i && (data[
end-1]==
'#' || data[
end-1]==
' '))
end--;
2200 int idx=
static_cast<int>(header.
length())-1;
2201 while (idx>=0 && (header.
at(idx)==
'#' || header.
at(idx)==
' ')) idx--;
2202 header=header.
left(idx+1);
2205 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
2236 while (i<data.size())
2247 (data[(i)]=='<' && \
2248 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2249 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2258 const size_t size=data.size();
2262 bool listMarkerSkipped=
false;
2265 (!listMarkerSkipped &&
2266 (data[i]==
'+' || data[i]==
'-' || data[i]==
'*' ||
2267 (data[i]==
'#' && i>0 && data[i-1]==
'-') ||
2268 (isDigit=(data[i]>=
'1' && data[i]<=
'9')) ||
2269 (isLi=(size>=3 && i+3<size &&
isLiTag(i)))
2278 while (j<size && ((data[j]>=
'0' && data[j]<=
'9') || data[j]==
'.'))
2282 if (j+1<size && data[j+1]==
' ')
2284 listMarkerSkipped=
true;
2301 listMarkerSkipped=
true;
2303 else if (data[i]==
'-' && size>=2 && i+2<size && data[i+1]==
'#' && data[i+2]==
' ')
2305 listMarkerSkipped=
true;
2309 else if (data[i]!=
' ' && i+1<size && data[i+1]==
' ')
2311 listMarkerSkipped=
true;
2313 if (data[i]!=
' ' && !listMarkerSkipped)
2327 size_t normalIndent = 0;
2328 while (normalIndent<data.size() && data[normalIndent]==
' ') normalIndent++;
2330 size_t result = listIndent>normalIndent ? listIndent : 0;
2341 while (i<data.size())
2347 else if (data[i]==
'\n')
2351 else if (data[i]!=
' ' && data[i]!=
'\t')
2363 DString &lang,
size_t &start,
size_t &
end,
size_t &offset,
2367 const char dot =
'.';
2368 auto isAlphaChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z'); };
2369 auto isAlphaNChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z') || (c>=
'0' && c<=
'9') || (c==
'+'); };
2370 auto isLangChar = [&](
char c) {
return c==dot || isAlphaChar(c); };
2376 const size_t size = data.size();
2377 while (i < size && data[i] ==
' ')
2382 if (indent>=refIndent+4)
2384 AUTO_TRACE_EXIT(
"result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2388 if (i<size && data[i]==
'`') tildaChar=
'`';
2389 while (i < size && data[i] == tildaChar)
2396 AUTO_TRACE_EXIT(
"result=false: no fence marker found #tildes={}",startTildes);
2400 while (i<size && data[i]==
' ') { i++; }
2401 if (i<size && data[i]==
'{')
2404 if (data[i] == dot) i++;
2406 while (i<size && (data[i]!=
'\n' && data[i]!=
'}')) i++;
2407 if (i<size && data[i]==
'}')
2409 lang = data.
substr(startLang,i-startLang);
2418 else if (i<size && isLangChar(data[i]))
2420 if (data[i] == dot) i++;
2422 if (i<size && isAlphaChar(data[i]))
2425 while (i<size && isAlphaNChar(data[i])) i++;
2427 lang = data.
substr(startLang,i-startLang);
2437 if (data[i]==tildaChar)
2441 while (i < size && data[i] == tildaChar)
2446 while (i<size && data[i]==
' ') i++;
2448 if (endTildes==startTildes)
2451 AUTO_TRACE_EXIT(
"result=true: found end marker at offset {} lang='{}'",offset,lang);
2458 warn(fileName, lineNr,
"Ending Inside a fenced code block. Maybe the end marker for the block is missing?");
2463static bool isCodeBlock(std::string_view data,
size_t offset,
size_t &indent)
2470 const size_t size = data.size();
2471 while (i < size && data[i] ==
' ')
2479 AUTO_TRACE_EXIT(
"result={}: line is not indented enough {}<4",
false,indent0);
2482 if (indent0>=size || data[indent0]==
'\n')
2484 AUTO_TRACE_EXIT(
"result={}: only spaces at the end of a comment block",
false);
2491 int offset_i =
static_cast<int>(offset);
2495 int j =
static_cast<int>(i)-offset_i-1;
2497 size_t nl_size =
isNewline(std::string_view(data.data()+j,data.size()-j));
2500 nl_pos[nl++]=j+
static_cast<int>(nl_size);
2506 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2517 if (!
isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2526 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2532 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2539 if (nl==1 && !
isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2547 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2563 const size_t size = data.size();
2566 while (i<size && data[i]==
' ') i++;
2567 if (i < size && data[i] ==
'|' && data[i] !=
'\n')
2576 while (i<size && (j =
isNewline(data.substr(i)))==0) i++;
2579 if (j>0 && i>0) i--;
2580 while (i>0 && data[i]==
' ') i--;
2581 if (i > 0 && data[i - 1] !=
'\\' && data[i] ==
'|')
2595 if (data[i]==
'|' && (i==0 || data[i-1]!=
'\\')) columns++;
2596 if (columns==1) columns++;
2600 if (n==2 && columns==0)
2612 size_t cc0=0, start=0,
end=0;
2616 if (i>=data.size() || cc0<1)
2628 if (data[j]!=
':' && data[j]!=
'-' && data[j]!=
'|' && data[j]!=
' ')
2637 AUTO_TRACE_EXIT(
"result=false: different number of columns as previous line {}!={}",cc1,cc0);
2652 const size_t size = data.size();
2654 size_t columns=0, start=0,
end=0;
2656 size_t headerStart = start;
2657 size_t headerEnd =
end;
2663 std::vector<Alignment> columnAlignment(columns);
2665 bool leftMarker=
false, rightMarker=
false, startFound=
false;
2671 if (data[j]==
':') { leftMarker=
true; startFound=
true; }
2672 if (data[j]==
'-') startFound=
true;
2675 if (data[j]==
'-') rightMarker=
false;
2676 else if (data[j]==
':') rightMarker=
true;
2677 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2701 std::vector<std::vector<TableCell> > tableContents;
2703 size_t m = headerStart;
2704 std::vector<TableCell> headerContents(columns);
2705 for (k=0;k<columns;k++)
2707 while (m<=headerEnd && (data[m]!=
'|' || (m>0 && data[m-1]==
'\\')))
2709 headerContents[k].cellText += data[m++];
2714 headerContents[k].colSpan = headerContents[k].cellText.empty();
2715 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2717 tableContents.push_back(headerContents);
2723 if (cc!=columns)
break;
2727 std::vector<TableCell> rowContents(columns);
2730 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2734 rowContents[k].colSpan = rowContents[k].cellText.empty();
2735 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2740 rowContents[k].cellText += data[j];
2746 rowContents[k].colSpan = rowContents[k].cellText.empty();
2747 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2748 tableContents.push_back(rowContents);
2754 out+=
"<table class=\"markdownTable\">";
2755 DString cellTag(
"th"), cellClass(
"class=\"markdownTableHead");
2756 for (
size_t row = 0; row < tableContents.size(); row++)
2762 out+=
"\n<tr class=\"markdownTableRowOdd\">";
2766 out+=
"\n<tr class=\"markdownTableRowEven\">";
2771 out+=
"\n <tr class=\"markdownTableHead\">";
2773 for (
size_t c = 0; c < columns; c++)
2776 DString cellText(tableContents[row][c].cellText);
2782 if (tableContents[row][c].cellText ==
"^")
2786 if (tableContents[row][c].colSpan)
2788 int cr =
static_cast<int>(c);
2789 while ( cr >= 0 && tableContents[row][cr].colSpan)
2793 if (cr >= 0 && tableContents[row][cr].cellText ==
"^")
continue;
2795 size_t rowSpan = 1, spanRow = row+1;
2796 while ((spanRow < tableContents.size()) &&
2797 (tableContents[spanRow][c].cellText ==
"^"))
2803 out+=
" <" + cellTag +
" " + cellClass;
2805 switch (columnAlignment[c])
2817 out+=
" rowspan=\"" + spanStr +
"\"";
2823 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2832 out+=
" colspan=\"" + spanStr +
"\"";
2836 out+=
"> " + cellText +
" \\ilinebr </" + cellTag +
">";
2839 cellClass =
"class=\"markdownTableBody";
2855 while (i<data.size() && data[i]!=
'\n')
2857 if (data[i]!=
' ' && data[i]!=
'\t') j++;
2860 if (i>=data.size()) {
return 0; }
2861 if (i<2) {
return 0; }
2862 bool res = (j>0 && data[i-1]==
' ' && data[i-2]==
' ');
2878 else if ((level=
isAtxHeader(data,header,
id,
true)))
2902 out+=
"</"+hTag+
">\n";
2905 else if (data.size()>0)
2907 size_t tmpSize = data.size();
2908 if (data[data.size()-1] ==
'\n') tmpSize--;
2909 out+=data.substr(0,tmpSize);
2913 out+=
"\\ilinebr<br>";
2915 if (tmpSize != data.size())
out+=
'\n';
2921 {
"[!note]",
"\\note" },
2922 {
"[!warning]",
"\\warning" },
2923 {
"[!tip]",
"\\remark" },
2924 {
"[!caution]",
"\\attention" },
2925 {
"[!important]",
"\\important" }
2934 const size_t size = data.size();
2935 std::string startCmd;
2936 int isGitHubAlert =
false;
2937 int isGitHubFirst =
false;
2942 while (
end<=size && data[
end-1]!=
'\n')
end++;
2947 while (j<
end && (data[j]==
' ' || data[j]==
'>'))
2949 if (data[j]==
'>') { level++; indent=j+1; }
2950 else if (j>0 && data[j-1]==
'>') indent=j+1;
2953 if (indent>0 && j>0 && data[j-1]==
'>' &&
2954 !(j==size || data[j]==
'\n'))
2971 isGitHubAlert =
true;
2972 isGitHubFirst =
true;
2973 startCmd = it->second;
2978 if (level!=1 || !isGitHubAlert)
2980 for (
int l=curLevel;l<level-1;l++)
2982 out+=
"<blockquote>";
2984 out +=
"<blockquote>‍";
2986 else if (!startCmd.empty())
2988 out += startCmd +
" ";
2991 else if (level<curLevel)
2993 int decrLevel = curLevel;
2994 if (level==0 && isGitHubAlert)
2998 for (
int l=level;l<decrLevel;l++)
3000 out +=
"</blockquote>\\ilinebr ";
3009 if (curLevel!=0 || !isGitHubAlert)
3011 std::string_view txt = data.substr(indent,
end-indent);
3014 if (!isGitHubFirst)
out +=
"<br>";
3021 isGitHubFirst =
false;
3036 for (
int l=0;l<curLevel;l++)
3038 out+=
"</blockquote>";
3049 size_t size = data.size();
3050 while (i<data.size() && data[i]==
' ') i++;
3053 size_t locStart = i;
3054 if (i>offset) locStart--;
3057 while (i+9<size && data[i]!=
'\n')
3069 location=data.substr(locStart,i-locStart);
3071 while (indent > 0 && i < size && data[i] ==
' ')
3076 if (i<size && data[i]==
'\n') i++;
3087 const size_t size = data.size();
3090 out+=
"@iverbatim\n";
3092 std::string location;
3097 while (
end<=size && data[
end-1]!=
'\n')
end++;
3100 while (j <
end && data[j] ==
' ')
3114 while (emptyLines>0)
3122 std::string lineLoc;
3127 out+=data.substr(offset,
end-offset);
3135 out+=
"@endiverbatim";
3136 if (!location.empty())
3144 while (emptyLines>0)
3160 const size_t size = data.size();
3161 size_t nb=0,
end=offset+1, j=0;
3166 if ((data[
end-1]==
'\\' || data[
end-1]==
'@') &&
3167 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3172 if (!endBlockName.
empty())
3174 size_t l = endBlockName.
length();
3175 for (;
end+l+1<size;
end++)
3177 if ((data[
end]==
'\\' || data[
end]==
'@') &&
3178 data[
end-1]!=
'\\' && data[
end-1]!=
'@'
3192 else if (nb==0 && data[
end-1]==
'<' && size>=6 &&
end+6<size &&
3193 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3196 if (tolower(data[
end])==
'p' && tolower(data[
end+1])==
'r' &&
3197 tolower(data[
end+2])==
'e' && (data[
end+3]==
'>' || data[
end+3]==
' '))
3208 else if (nb==0 && data[
end-1]==
'`')
3210 while (
end <= size && data[
end - 1] ==
'`')
3216 else if (nb>0 && data[
end-1]==
'`')
3219 while (
end <= size && data[
end - 1] ==
'`')
3237 size_t blockStart,
size_t blockEnd)
3240 if (!lang.empty() && lang[0]==
'.') lang=lang.substr(1);
3241 const size_t size=data.size();
3243 while (i<size && (data[i]==
' ' || data[i]==
'\t'))
3264 size_t pi=std::string::npos;
3265 bool newBlock =
false;
3266 bool insideList =
false;
3267 size_t currentIndent = refIndent;
3268 size_t listIndent = refIndent;
3269 const size_t size = data.size();
3276 size_t lineIndent=0;
3277 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3283 if (insideList && lineIndent<currentIndent)
3286 currentIndent = refIndent;
3294 if (listIndent<currentIndent+4)
3298 currentIndent = listIndent;
3305 currentIndent = listIndent;
3314 if (pi!=std::string::npos)
3316 size_t blockStart=0, blockEnd=0, blockOffset=0;
3319 auto addSpecialCommand = [&](
const DString &startCmd,
const DString &endCmd)
3321 size_t cmdPos = pi+blockStart+1;
3328 if (pl[ii]==
'\n') nl++;
3331 bool addNewLines =
false;
3333 (pl[ii]!=
'\\' && pl[ii]!=
'@') ||
3342 pl =
"@"+startCmd+
"\n" + pl +
"@"+endCmd;
3344 addNewLines =
false;
3359 if (addNewLines)
for (
int j=0;j<nl;j++)
out+=
'\n';
3361 if (addNewLines)
out+=
'\n';
3366 addSpecialCommand(
"startuml",
"enduml");
3370 addSpecialCommand(
"dot",
"enddot");
3372 else if (lang==
"msc")
3374 addSpecialCommand(
"msc",
"endmsc");
3376 else if (lang==
"mermaid")
3378 addSpecialCommand(
"mermaid",
"endmermaid");
3385 pi=std::string::npos;
3389 else if (
isBlockQuote(data.substr(pi,i-pi),currentIndent))
3392 pi=std::string::npos;
3399 out+=data.substr(pi,i-pi);
3405 if (pi!=std::string::npos && pi<size)
3415 warn(
fileName,
lineNr,
"Ending inside a fenced code block. Maybe the end marker for the block is missing?");
3417 out+=data.substr(pi);
3431 size_t pi = std::string::npos;
3447 size_t currentIndent = indent;
3448 size_t listIndent = indent;
3449 bool insideList =
false;
3450 bool newBlock =
false;
3453 while (i<data.size())
3458 size_t lineIndent=0;
3460 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3466 if (insideList && lineIndent<currentIndent)
3469 currentIndent = indent;
3477 if (listIndent<currentIndent+4)
3481 currentIndent = listIndent;
3488 currentIndent = listIndent;
3500 if (pi!=std::string::npos)
3502 size_t blockStart=0, blockEnd=0, blockOffset=0;
3504 size_t blockIndent = currentIndent;
3508 if (data[i]==
'@' || data[i]==
'\\') endBlockName =
isBlockCommand(data.substr(i),i);
3509 if (!endBlockName.
empty())
3512 if (
isLinkRef(data.substr(pi,i-pi),
id,link,title))
3522 size_t l = endBlockName.
length();
3523 while (i+l<data.size())
3525 if ((data[i]==
'\\' || data[i]==
'@') &&
3526 data[i-1]!=
'\\' && data[i-1]!=
'@')
3543 while (pi<data.size() && data[pi]==
' ') pi++;
3547 if (!header.
empty())
3551 out+=level==1?
"@section ":
"@subsection ";
3559 out+=level==1?
"<h1>":
"<h2>";
3561 out+=level==1?
"\n</h1>\n":
"\n</h2>\n";
3568 pi=std::string::npos;
3573 else if ((ref=
isLinkRef(data.substr(pi),
id,link,title)))
3587 pi=std::string::npos;
3595 pi=std::string::npos;
3602 pi=std::string::npos;
3615 if (pi!=std::string::npos && pi<data.size())
3617 if (
isLinkRef(data.substr(pi),
id,link,title))
3634#define OPC(x) if (literal_at(data,#x " ") || literal_at(data,#x "\n")) return true
3635 OPC(dir);
OPC(defgroup);
OPC(addtogroup);
OPC(weakgroup);
OPC(ingroup);
3638 OPC(protocol);
OPC(category);
OPC(
union);
OPC(
struct);
OPC(interface);
3639 OPC(idlexcept);
OPC(file);
3649 std::string_view data(docs.
str());
3650 const size_t size = data.size();
3653 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3660 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3666 (data[i]==
'\\' || data[i]==
'@') &&
3681 else if (i+1<size && (data[i]==
'\\' || data[i]==
'@') &&
isOtherPage(data.substr(i+1)))
3699 std::string_view data(docs_org.
str());
3700 const size_t size = data.size();
3702 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3704 if (data[i]==
'\n') prepend++;
3707 if (i>=size) {
return DString(); }
3709 while (end1<size && data[end1-1]!=
'\n') end1++;
3716 while (end2<size && data[end2-1]!=
'\n') end2++;
3717 if (
prv->isHeaderline(data.substr(end1),
false))
3719 title = data.
substr(i,end1-i-1);
3720 docs+=
"\n\n"+docs_org.
mid(end2);
3721 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3727 if (i<end1 && prv->isAtxHeader(data.substr(i,end1-i),title,
id,
false,&isIdGenerated)>0)
3730 docs+=docs_org.
mid(end1);
3735 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3746 if (input.
empty())
return input;
3751 if (s.
at(s.
length()-1)!=
'\n') s +=
"\n";
3752 s =
detab(s,refIndent);
3756 s =
prv->processQuotations(s.
view(),refIndent);
3760 s =
prv->processBlocks(s.
view(),refIndent);
3778 const char *p = result.
data();
3781 while (*p==
' ') p++;
3782 while (*p==
'\n') {startNewlines++;p++;};
3785 if (p>result.
data())
3788 result = result.
mid(
static_cast<int>(p-result.
data()));
3824 const char *fileBuf,
3825 const std::shared_ptr<Entry> &root,
3828 std::shared_ptr<Entry> current = std::make_shared<Entry>();
3830 current->lang = SrcLangExt::Markdown;
3831 current->fileName = fileName;
3832 current->docFile = fileName;
3833 current->docLine = 1;
3839 bool isIdGenerated =
false;
3847 int indentLevel=title.
empty() ? 0 : -1;
3854 bool wasEmpty =
id.empty();
3855 if (wasEmpty)
id = mdFileNameId;
3861 if (!mdfileAsMainPage.
empty() &&
3865 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3866 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3868 else if (
id==
"mainpage" ||
id==
"index")
3870 if (title.
empty()) title = titleFn;
3871 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3872 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3874 else if (isSubdirDocs)
3878 docs.
prepend(
"@section " + generatedId +
" " + title +
"\\ilinebr ");
3880 docs.
prepend(
"@dir\\ilinebr ");
3891 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr @ianchor{" + relFileName +
"} " + mdFileNameId +
"\\ilinebr ");
3893 else if (!generatedId.
empty())
3895 docs.
prepend(
"@ianchor " + generatedId +
"\\ilinebr ");
3897 else if (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB)
3900 docs.
prepend(
"@ianchor{" + title +
"} " + autoId +
"\\ilinebr ");
3902 docs.
prepend(
"@page "+
id+
" "+title+
"\\ilinebr ");
3904 for (
int i = 0; i < prepend; i++) docs.
prepend(
"\n");
3909 static const reg::Ex re(R
"([ ]*[\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
3911 std::string s = docs.str();
3914 DString orgLabel = match[1].str();
3915 DString orgTitle = match[2].str();
3918 docs = docs.
left(match[1].position())+
3921 "\\ilinebr @ianchor{" + orgTitle +
"} "+orgLabel+
"\n"+
3922 docs.
mid(match.length());
3933 p->commentScanner.enterFile(fileName,lineNr);
3935 bool needsEntry =
false;
3939 while (
p->commentScanner.parseCommentBlock(
3957 DString docFile = current->docFile;
3958 root->moveToSubEntryAndRefresh(current);
3959 current->lang = SrcLangExt::Markdown;
3960 current->docFile = docFile;
3961 current->docLine = lineNr;
3966 root->moveToSubEntryAndKeep(current);
3968 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 whitespace cha...
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.
FileInfo(const std::string &name)
std::string fileName() const
std::string absFilePath() const
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 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)
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)
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)
DString markdownFileNameToId(const DString &fileName)
processes string s and converts markdown into doxygen/html commands.
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
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)
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const DString &n, bool &ambig)
DString getFileNameExtension(const DString &fn)
SrcLangExt getLanguageFromFileName(const DString &fileName, SrcLangExt defLang)
DString escapeCharsInString(const DString &name, bool allowDots, bool allowUnderscore)
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
DString stripIndentation(const DString &s, bool skipFirstLine)
DString stripFromPath(const DString &path)
A bunch of utility functions.