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;
245 const char *p=s.
data();
249 if (c==
'"' && pc!=
'\\') result+=
'\\';
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)
350 int i = attr.
find(
':');
357 return attr.
mid(i+1);
396 using EndBlockFunc =
QCString (*)(
const std::string &,bool,char);
398 static constexpr auto getEndBlock = [](
const std::string &blockName,bool,char) ->
QCString
400 return "end"+blockName;
402 static constexpr auto getEndCode = [](
const std::string &blockName,
bool openBracket,char) ->
QCString
404 return openBracket ?
QCString(
"}") :
"end"+blockName;
406 static constexpr auto getEndUml = [](
const std::string &,bool,char) ->
QCString
410 static constexpr auto getEndFormula = [](
const std::string &,bool,
char nextChar) ->
QCString
414 case '$':
return "f$";
415 case '(':
return "f)";
416 case '[':
return "f]";
417 case '{':
return "f}";
423 static const std::unordered_map<std::string,EndBlockFunc> blockNames =
425 {
"dot", getEndBlock },
426 {
"code", getEndCode },
427 {
"icode", getEndBlock },
428 {
"msc", getEndBlock },
429 {
"verbatim", getEndBlock },
430 {
"iverbatim", getEndBlock },
431 {
"iliteral", getEndBlock },
432 {
"latexonly", getEndBlock },
433 {
"htmlonly", getEndBlock },
434 {
"xmlonly", getEndBlock },
435 {
"rtfonly", getEndBlock },
436 {
"manonly", getEndBlock },
437 {
"docbookonly", getEndBlock },
438 {
"startuml", getEndUml },
439 {
"mermaid", getEndBlock },
440 {
"f", getEndFormula }
443 const size_t size = data.
size();
444 bool openBracket = offset>0 && data.data()[-1]==
'{';
445 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
446 if (isEscaped)
return result;
449 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
450 if (
end==1)
return result;
451 std::string blockName(data.substr(1,
end-1));
452 auto it = blockNames.find(blockName);
453 if (it!=blockNames.end())
455 result = it->second(blockName, openBracket,
end<size ? data[
end] : 0);
465 using EndCmdFunc = size_t (*)(std::string_view,size_t);
467 static constexpr auto endOfLine = [](std::string_view data_,
size_t offset_) ->
size_t
472 while (offset_<data_.size() && ((c=data_[offset_])!=
'\n' || lc==
'\\'))
474 if (c==
'\\') lc=
'\\';
475 else if (c!=
' ') lc=0;
481 static constexpr auto endOfLabels = [](std::string_view data_,
size_t offset_,
bool multi_) ->
size_t
483 if (offset_<data_.size() && data_[offset_]==
' ')
491 while (offset_<data_.size() && data_[offset_]==
' ')
496 while (offset_<data_.size() && (c=data_[offset_])!=
' ' && c!=
',' && c!=
'\\' && c!=
'@' && c!=
'\n')
501 if (multi_ && offset_<data_.size() && (data_[offset_]==
',' || data_[offset_]==
' '))
503 size_t off = offset_;
504 while (off<data_.size() && data_[off]==
' ')
508 if (off<data_.size() && data_[off]==
',')
527 static constexpr auto endOfLabel = [](std::string_view data_,
size_t offset_) ->
size_t
529 return endOfLabels(data_,offset_,
false);
532 static constexpr auto endOfLabelOpt = [](std::string_view data_,
size_t offset_) ->
size_t
534 size_t index=offset_;
535 if (index<data_.size() && data_[index]==
' ')
538 while (index<data_.size() && data_[index]==
' ') index++;
540 if (index<data_.size() && data_[index]==
'{')
544 while (index<data_.size() && (c=data_[index])!=
'}' && c!=
'\\' && c!=
'@' && c!=
'\n') index++;
545 if (index==data_.size() || data_[index]!=
'}')
return 0;
548 return endOfLabel(data_,offset_);
551 static constexpr auto endOfParam = [](std::string_view data_,
size_t offset_) ->
size_t
553 size_t index=offset_;
554 if (index<data_.size() && data_[index]==
' ')
557 while (index<data_.size() && data_[index]==
' ') index++;
559 if (index<data_.size() && data_[index]==
'[')
563 while (index<data_.size() && (c=data_[index])!=
']' && c!=
'\n') index++;
564 if (index==data_.size() || data_[index]!=
']')
return 0;
567 return endOfLabels(data_,offset_,
true);
570 static constexpr auto endOfRetVal = [](std::string_view data_,
size_t offset_) ->
size_t
572 return endOfLabels(data_,offset_,
true);
575 static constexpr auto endOfFuncLike = [](std::string_view data_,
size_t offset_,
bool allowSpaces) ->
size_t
577 if (offset_<data_.size() && data_[offset_]==
' ')
582 while (offset_<data_.size() && data_[offset_]==
' ')
587 while (offset_<data_.size() && (c=data_[offset_])!=
'\n' && (allowSpaces || c!=
' ') && c!=
'(')
589 if (
literal_at(data_.substr(offset_),
"\\ilinebr "))
break;
596 while (offset_<data_.size() && (c=data_[offset_++]))
599 else if (c==
')') count--;
600 if (count==0)
return offset_;
608 static constexpr auto endOfFunc = [](std::string_view data_,
size_t offset_) ->
size_t
610 return endOfFuncLike(data_,offset_,
true);
613 static constexpr auto endOfGuard = [](std::string_view data_,
size_t offset_) ->
size_t
615 return endOfFuncLike(data_,offset_,
false);
618 static const std::unordered_map<std::string,EndCmdFunc> cmdNames =
621 {
"addindex", endOfLine },
622 {
"addtogroup", endOfLabel },
623 {
"anchor", endOfLabel },
626 {
"category", endOfLine },
627 {
"cite", endOfLabel },
628 {
"class", endOfLine },
629 {
"concept", endOfLine },
630 {
"copybrief", endOfFunc },
631 {
"copydetails", endOfFunc },
632 {
"copydoc", endOfFunc },
633 {
"def", endOfFunc },
634 {
"defgroup", endOfLabel },
635 {
"diafile", endOfLine },
636 {
"dir", endOfLine },
637 {
"dockbookinclude",endOfLine },
638 {
"dontinclude", endOfLine },
639 {
"dotfile", endOfLine },
641 {
"elseif", endOfGuard },
642 {
"em", endOfLabel },
643 {
"emoji", endOfLabel },
644 {
"enum", endOfLabel },
645 {
"example", endOfLine },
646 {
"exception", endOfLabel },
647 {
"extends", endOfLabel },
648 {
"file", endOfLine },
650 {
"headerfile", endOfLine },
651 {
"htmlinclude", endOfLine },
652 {
"ianchor", endOfLabelOpt },
653 {
"idlexcept", endOfLine },
654 {
"if", endOfGuard },
655 {
"ifnot", endOfGuard },
656 {
"image", endOfLine },
657 {
"implements", endOfLine },
658 {
"include", endOfLine },
659 {
"includedoc", endOfLine },
660 {
"includelineno", endOfLine },
661 {
"ingroup", endOfLabel },
662 {
"interface", endOfLine },
663 {
"latexinclude", endOfLine },
664 {
"maninclude", endOfLine },
665 {
"memberof", endOfLabel },
666 {
"mermaidfile", endOfLine },
667 {
"mscfile", endOfLine },
668 {
"namespace", endOfLabel },
669 {
"noop", endOfLine },
670 {
"overload", endOfLine },
672 {
"package", endOfLabel },
673 {
"page", endOfLabel },
674 {
"paragraph", endOfLabel },
675 {
"param", endOfParam },
676 {
"property", endOfLine },
677 {
"protocol", endOfLine },
678 {
"qualifier", endOfLine },
679 {
"ref", endOfLabel },
680 {
"refitem", endOfLine },
681 {
"related", endOfLabel },
682 {
"relatedalso", endOfLabel },
683 {
"relates", endOfLabel },
684 {
"relatesalso", endOfLabel },
685 {
"requirement", endOfLabel },
686 {
"retval", endOfRetVal},
687 {
"rtfinclude", endOfLine },
688 {
"section", endOfLabel },
689 {
"skip", endOfLine },
690 {
"skipline", endOfLine },
691 {
"snippet", endOfLine },
692 {
"snippetdoc", endOfLine },
693 {
"snippetlineno", endOfLine },
694 {
"struct", endOfLine },
695 {
"satisfies", endOfLabel },
696 {
"subpage", endOfLabel },
697 {
"subparagraph", endOfLabel },
698 {
"subsubparagraph",endOfLabel },
699 {
"subsection", endOfLabel },
700 {
"subsubsection", endOfLabel },
701 {
"throw", endOfLabel },
702 {
"throws", endOfLabel },
703 {
"tparam", endOfLabel },
704 {
"typedef", endOfLine },
705 {
"plantumlfile", endOfLine },
706 {
"union", endOfLine },
707 {
"until", endOfLine },
708 {
"var", endOfLine },
709 {
"verbinclude", endOfLine },
710 {
"verifies", endOfLabel },
711 {
"weakgroup", endOfLabel },
712 {
"xmlinclude", endOfLine },
713 {
"xrefitem", endOfLabel }
716 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
717 if (isEscaped)
return 0;
719 const size_t size = data.size();
721 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
722 if (
end==1)
return 0;
723 std::string cmdName(data.substr(1,
end-1));
725 auto it = cmdNames.find(cmdName);
726 if (it!=cmdNames.end())
729 result = it->second(data,
end);
742 const size_t size = data.size();
746 while (i<size && data[i]!=c &&
747 data[i]!=
'\\' && data[i]!=
'@' &&
748 !(data[i]==
'/' && data[i-1]==
'<') &&
768 while (i+len<size && data[i+len]==c)
775 if (len!=c_size || (i+len<size &&
isIdChar(data[i+len])))
781 return static_cast<int>(i);
789 while (i < size && data[i] ==
'`')
797 while (i<size && enb<snb)
799 if (data[i]==
'`') enb++;
800 if (snb==1 && data[i]==
'\'')
break;
804 else if (data[i]==
'@' || data[i]==
'\\')
810 size_t l = endBlockName.
length();
813 if ((data[i]==
'\\' || data[i]==
'@') &&
814 data[i-1]!=
'\\' && data[i-1]!=
'@')
824 else if (i+1<size &&
isIdChar(data[i+1]))
833 else if (data[i-1]==
'<' && data[i]==
'/')
837 else if (data[i]==
'\n')
840 while (i<size && data[i]==
' ') i++;
841 if (i>=size || data[i]==
'\n')
859 const size_t size = data.size();
862 if (size>1 && data[0]==c && data[1]==c) { i=1; }
867 if (len==0) {
return 0; }
869 if (i>=size) {
return 0; }
871 if (i+1<size && data[i+1]==c)
876 if (data[i]==c && data[i-1]!=
' ' && data[i-1]!=
'\n')
882 return static_cast<int>(i+1);
893 const size_t size = data.size();
903 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=
' ' && data[i-1]!=
'\n')
905 if (c ==
'~')
out+=
"<strike>";
906 else out+=
"<strong>";
908 if (c ==
'~')
out+=
"</strike>";
909 else out+=
"</strong>";
911 return static_cast<int>(i+2);
925 const size_t size = data.size();
937 if (data[i]!=c || data[i-1]==
' ' || data[i-1]==
'\n')
942 if (i+2<size && data[i+1]==c && data[i+2]==c)
946 out+=
"</strong></em>";
948 return static_cast<int>(i+3);
950 else if (i+1<size && data[i+1]==c)
961 return static_cast<int>(len - 2);
975 return static_cast<int>(len - 1);
986 const size_t size = data.size();
990 if (i<size && data[i]==
'-')
995 if (i<size && data[i]==
'-')
1000 if (i<size && data[i]==
'-')
1004 if (count>=2 && offset>=2 &&
literal_at(data.data()-2,
"<!"))
1006 if (count==2 && size > 2 && data[2]==
'>')
1008 if (count==3 && size > 3 && data[3]==
'>')
1010 if (count==2 && (offset<8 || !
literal_at(data.data()-8,
"operator")))
1030 const size_t size = data.size();
1033 while (i<size && data[i]!=
'"' && nl<2)
1035 if (data[i]==
'\n') nl++;
1038 if (i<size && data[i]==
'"' && nl<2)
1040 out+=data.substr(0,i+1);
1042 return static_cast<int>(i+1);
1054 if (offset>0 && data.data()[-1]==
'\\') {
return 0; }
1056 const size_t size = data.size();
1062 while (i < size &&
isIdChar(data[i]))
1067 QCString tagName(data.substr(1,i-1));
1068 if (tagName.
lower()==
"pre")
1070 bool insideStr=
FALSE;
1074 if (!insideStr && c==
'<')
1076 if (data[i+1]==
'/' &&
1077 tolower(data[i+2])==
'p' && tolower(data[i+3])==
'r' &&
1078 tolower(data[i+4])==
'e' && tolower(data[i+5])==
'>')
1080 if (doWrite)
out+=data.substr(0,i+6);
1083 return static_cast<int>(i+6);
1086 else if (insideStr && c==
'"')
1088 if (data[i-1]!=
'\\') insideStr=
FALSE;
1101 if (data[i]==
'/' && i+1<size && data[i+1]==
'>')
1104 if (doWrite)
out+=data.substr(0,i+2);
1106 return static_cast<int>(i+2);
1108 else if (data[i]==
'>')
1111 if (doWrite)
out+=data.substr(0,i+1);
1113 return static_cast<int>(i+1);
1115 else if (data[i]==
' ')
1118 bool insideAttr=
FALSE;
1121 if (!insideAttr && data[i]==
'"')
1125 else if (data[i]==
'"' && data[i-1]!=
'\\')
1129 else if (!insideAttr && data[i]==
'>')
1132 if (doWrite)
out+=data.substr(0,i+1);
1134 return static_cast<int>(i+1);
1154 const size_t size = data.size();
1166 if (size>2 && c!=
'~' && data[1]!=c)
1169 if (data[1]==
' ' || data[1]==
'\n' ||
1177 if (size>3 && data[1]==c && data[2]!=c)
1179 if (data[2]==
' ' || data[2]==
'\n' ||
1187 if (size>4 && c!=
'~' && data[1]==c && data[2]==c && data[3]!=c)
1189 if (data[3]==
' ' || data[3]==
'\n' ||
1201 std::string_view
fmt,
bool inline_img,
bool explicitTitle,
1206 AUTO_TRACE(
"fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1217 out+=link.
mid(fd ? 0 : 5);
1218 if (!explicitTitle && !content.
isEmpty())
1224 else if ((content.
isEmpty() || explicitTitle) && !title.
isEmpty())
1246 const size_t size = data.size();
1251 bool isImageLink =
FALSE;
1252 bool isImageInline =
FALSE;
1258 if (size<2 || data[1]!=
'[')
1267 while (pos>=-
static_cast<int>(offset) && numNLsNeeded>0)
1269 if (data.data()[pos]==
'\n') numNLsNeeded--;
1270 else if (data.data()[pos]!=
' ')
1280 size_t contentStart=i;
1287 if (data[i-1]==
'\\')
1290 else if (data[i]==
'[')
1294 else if (data[i]==
']')
1297 if (level<=0)
break;
1299 else if (data[i]==
'\n')
1302 if (nl>1) {
return 0; }
1308 if (i>=size)
return 0;
1309 size_t contentEnd=i;
1310 content = data.substr(contentStart,contentEnd-contentStart);
1312 if (!isImageLink && content.
isEmpty()) {
return 0; }
1315 bool whiteSpace =
false;
1317 while (i<size && data[i]==
' ') { whiteSpace =
true; i++; }
1318 if (i<size && data[i]==
'\n')
1323 while (i<size && data[i]==
' ') i++;
1325 if (whiteSpace && i<size && (data[i]==
'(' || data[i]==
'['))
return 0;
1327 bool explicitTitle=
FALSE;
1328 if (i<size && data[i]==
'(')
1331 while (i<size && data[i]==
' ') i++;
1332 bool uriFormat=
false;
1333 if (i<size && data[i]==
'<') { i++; uriFormat=
true; }
1337 while (i<size && data[i]!=
'\'' && data[i]!=
'"' && braceCount>0)
1343 if (nlConsec>1) {
return 0; }
1345 else if (data[i]==
'(')
1350 else if (data[i]==
')')
1355 else if (data[i]!=
' ')
1366 if (i>=size || data[i]==
'\n') {
return 0; }
1367 link = data.substr(linkStart,i-linkStart);
1370 if (link.
isEmpty()) {
return 0; }
1374 if (data[i]==
'\'' || data[i]==
'"')
1378 size_t titleStart=i;
1384 if (nl>1) {
return 0; }
1387 else if (data[i]==
'\\')
1391 else if (data[i]==c)
1402 size_t titleEnd = i-1;
1404 while (titleEnd>titleStart && data[titleEnd]==
' ') titleEnd--;
1405 if (data[titleEnd]==c)
1407 title = data.substr(titleStart,titleEnd-titleStart);
1411 if (data[i]==
' ')i++;
1412 else if (data[i] ==
')')
break;
1426 else if (i<size && data[i]==
'[')
1432 while (i<size && data[i]!=
']')
1437 if (nl>1) {
return 0; }
1441 if (i>=size) {
return 0; }
1443 link = data.substr(linkStart,i-linkStart);
1455 link = lr_it->second.link;
1456 title = lr_it->second.title;
1466 else if (i<size && data[i]!=
':' && !content.
isEmpty())
1473 link = lr_it->second.link;
1474 title = lr_it->second.title;
1478 else if (content==
"TOC")
1501 while (j<size && data[j]==
' ') { j++; }
1502 if (j<size && data[j]==
'{')
1507 size_t attributesStart=i;
1512 if (data[i-1]==
'\\')
1515 else if (data[i]==
'{')
1519 else if (data[i]==
'}')
1522 if (level<=0)
break;
1524 else if (data[i]==
'\n')
1527 if (nl>1) {
return 0; }
1532 if (i>=size)
return 0;
1533 size_t attributesEnd=i;
1534 attributes = data.substr(attributesStart,attributesEnd-attributesStart);
1543 while (pos<size && numNLsNeeded>0)
1545 if (data[pos]==
'\n') numNLsNeeded--;
1546 else if (data[pos]!=
' ')
1561 out+=
"@tableofcontents{html:";
1566 else if (isImageLink)
1570 if (link.
find(
"@ref ")!=-1 || link.
find(
"\\ref ")!=-1 ||
1575 writeMarkdownImage(
"html", isImageInline, explicitTitle, title, content, link, attributes, fd);
1576 writeMarkdownImage(
"latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
1577 writeMarkdownImage(
"rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
1578 writeMarkdownImage(
"docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
1579 writeMarkdownImage(
"xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
1601 if ((lp=link.
find(
"@ref "))!=-1 || (lp=link.
find(
"\\ref "))!=-1 || (lang==SrcLangExt::Markdown && !
isURL(link)))
1633 if (explicitTitle && !title.
isEmpty())
1643 else if ((lp=link.
find(
'#'))!=-1 || link.
find(
'/')!=-1 || link.
find(
'.')!=-1)
1646 if (lp==0 || (lp>0 && !
isURL(link) &&
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
1660 for (
int ii = 0; ii < nlTotal; ii++)
out+=
"\n";
1673 bool foundNameRef =
false;
1674 if (!content.
isEmpty() && (content.
at(0)==
'#' || content.
at(0)==
'@'))
1677 while (endOfId<content.
length() &&
isId(content.
at(endOfId))) endOfId++;
1684 foundNameRef =
true;
1703 return static_cast<int>(i);
1710 const size_t size = data.size();
1714 while (nb<size && data[nb]==
'`')
1731 if (
end+1<size && data[
end+1]==
'`')
1746 if (
end+1<size && data[
end+1]==
'`')
1759 else if (data[
end]==
'\n')
1770 else if (!markdownStrict && data[
end]==
'\'' && nb==1 && (
end+1==size || (
end+1<size && data[
end+1]!=
'\'' && !
isIdChar(data[
end+1]))))
1773 out+=data.substr(nb,
end-nb);
1776 return static_cast<int>(
end+1);
1778 else if (!markdownStrict && data[
end]==
'\'' && nb==2 &&
end+1<size && data[
end+1]==
'\'')
1781 out+=data.substr(nb,
end-nb);
1784 return static_cast<int>(
end+2);
1788 if (data[
end]!=
' ') pc = data[
end];
1792 if (i < nb && end >= size)
1797 out+=data.substr(0,nb);
1798 return static_cast<int>(nb);
1802 while (
end<size && data[
end]==
'`')
1812 QCString codeFragment = data.substr(nb,
end-nb-nb);
1818 return static_cast<int>(
end);
1837 const size_t size = data.size();
1843 size_t l = endBlockName.
length();
1846 if ((data[i]==
'\\' || data[i]==
'@') &&
1847 data[i-1]!=
'\\' && data[i-1]!=
'@')
1854 return static_cast<int>(i+1+l);
1863 out+=data.substr(0,endPos);
1864 return static_cast<int>(endPos);
1866 if (size>1 && (data[0]==
'\\' || data[0]==
'@'))
1869 if (c==
'[' || c==
']' || c==
'*' || c==
'(' || c==
')' || c==
'`' || c==
'_')
1875 else if (c==
'\\' || c==
'@')
1877 out+=data.substr(0,2);
1881 else if (c==
'-' && size>3 && data[2]==
'-' && data[3]==
'-')
1883 out+=data.substr(1,3);
1887 else if (c==
'-' && size>2 && data[2]==
'-')
1889 out+=data.substr(1,2);
1903 const size_t size = data.size();
1909 out+=data.substr(i,
end-i);
1910 if (
end>=size)
break;
1913 int iend = action(*
this,data.substr(i),i);
1931 const size_t size = data.size();
1932 while (i<size && data[i]==
' ') i++;
1933 if (i==size)
return 0;
1938 while (i < size && data[i] ==
'=')
1943 while (i<size && data[i]==
' ') i++;
1944 int level = (c>1 && (i>=size || data[i]==
'\n')) ? 1 : 0;
1945 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
1960 while (i < size && data[i] ==
'-')
1965 while (i<size && data[i]==
' ') i++;
1966 return (c>1 && (i>=size || data[i]==
'\n')) ?
indentLevel+2 : 0;
1976 const size_t size = data.size();
1977 while (i<size && data[i]==
' ') i++;
1982 while (i<size && (data[i]==
'>' || data[i]==
' '))
1984 if (data[i]==
'>') level++;
1989 bool res = (level>0 && i<size && ((data[i-1]==
' ') || data[i]==
'\n')) || (level > 1);
2004 const size_t size = data.size();
2007 while (i<size && data[i]==
' ') i++;
2008 if (i>=size || data[i]!=
'[') {
return 0; }
2010 size_t refIdStart=i;
2011 while (i<size && data[i]!=
'\n' && data[i]!=
']') i++;
2012 if (i>=size || data[i]!=
']') {
return 0; }
2013 refid = data.substr(refIdStart,i-refIdStart);
2014 if (refid.
isEmpty()) {
return 0; }
2018 if (i>=size || data[i]!=
':') {
return 0; }
2022 while (i<size && data[i]==
' ') i++;
2023 if (i<size && data[i]==
'\n')
2026 while (i<size && data[i]==
' ') i++;
2028 if (i>=size) {
return 0; }
2030 if (i<size && data[i]==
'<') i++;
2032 while (i<size && data[i]!=
' ' && data[i]!=
'\n') i++;
2034 if (i<size && data[i]==
'>') i++;
2035 if (linkStart==linkEnd) {
return 0; }
2036 link = data.substr(linkStart,linkEnd-linkStart);
2038 if (link==
"@ref" || link==
"\\ref")
2041 while (i<size && data[i]!=
'\n' && data[i]!=
'"') i++;
2042 link+=data.substr(argStart,i-argStart);
2049 while (i<size && data[i]==
' ') i++;
2050 if (i<size && data[i]==
'\n')
2054 while (i<size && data[i]==
' ') i++;
2058 AUTO_TRACE_EXIT(
"result={}: end of isLinkRef while looking for title",i);
2063 if (c==
'\'' || c==
'"' || c==
'(')
2068 size_t titleStart=i;
2070 while (i<size && data[i]!=
'\n') i++;
2075 while (
end>titleStart && data[
end]!=c)
end--;
2078 title = data.substr(titleStart,
end-titleStart);
2082 while (i<size && data[i]==
' ') i++;
2094 size_t size = data.size();
2095 if (size>0 && data[size-1]==
'\n') size--;
2096 while (i<size && data[i]==
' ') i++;
2097 if (i>=size) {
AUTO_TRACE_EXIT(
"result=false: empty line");
return false; }
2099 if (c!=
'*' && c!=
'-' && c!=
'_')
2111 else if (data[i]!=
' ')
2126 static const reg::Ex r2(R
"({#(\a[\w-]*)}\s*$)");
2128 std::string ti = title.str();
2131 std::string
id = match[1].str();
2132 title = title.
left(match.position());
2135 warn(
fileName,
lineNr,
"An automatically generated id already has the name '{}'!",
id);
2141 if (((level>0) && (level<=
Config_getInt(TOC_INCLUDE_HEADINGS))) || (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2144 if (pIsIdGenerated) *pIsIdGenerated=
true;
2159 int level = 0, blanks=0;
2160 const size_t size = data.size();
2163 while (i<size && data[i]==
' ') i++;
2164 if (i>=size || data[i]!=
'#')
2168 while (i < size && data[i] ==
'#')
2177 while (i < size && data[i] ==
' ')
2182 if (level==1 && blanks==0)
2189 while (
end<size && data[
end]!=
'\n')
end++;
2190 while (
end>i && (data[
end-1]==
'#' || data[
end-1]==
' '))
end--;
2193 header = data.substr(i,
end-i);
2197 int idx=
static_cast<int>(header.
length())-1;
2198 while (idx>=0 && (header.
at(idx)==
'#' || header.
at(idx)==
' ')) idx--;
2199 header=header.
left(idx+1);
2202 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
2233 while (i<data.size())
2244 (data[(i)]=='<' && \
2245 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2246 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2255 const size_t size=data.size();
2259 bool listMarkerSkipped=
FALSE;
2262 (!listMarkerSkipped &&
2263 (data[i]==
'+' || data[i]==
'-' || data[i]==
'*' ||
2264 (data[i]==
'#' && i>0 && data[i-1]==
'-') ||
2265 (isDigit=(data[i]>=
'1' && data[i]<=
'9')) ||
2266 (isLi=(size>=3 && i+3<size &&
isLiTag(i)))
2275 while (j<size && ((data[j]>=
'0' && data[j]<=
'9') || data[j]==
'.'))
2279 if (j+1<size && data[j+1]==
' ')
2281 listMarkerSkipped=
TRUE;
2298 listMarkerSkipped=
TRUE;
2300 else if (data[i]==
'-' && size>=2 && i+2<size && data[i+1]==
'#' && data[i+2]==
' ')
2302 listMarkerSkipped=
TRUE;
2306 else if (data[i]!=
' ' && i+1<size && data[i+1]==
' ')
2308 listMarkerSkipped=
TRUE;
2310 if (data[i]!=
' ' && !listMarkerSkipped)
2324 size_t normalIndent = 0;
2325 while (normalIndent<data.size() && data[normalIndent]==
' ') normalIndent++;
2327 size_t result = listIndent>normalIndent ? listIndent : 0;
2338 while (i<data.size())
2344 else if (data[i]==
'\n')
2348 else if (data[i]!=
' ' && data[i]!=
'\t')
2360 QCString &lang,
size_t &start,
size_t &
end,
size_t &offset,
2364 const char dot =
'.';
2365 auto isAlphaChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z'); };
2366 auto isAlphaNChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z') || (c>=
'0' && c<=
'9') || (c==
'+'); };
2367 auto isLangChar = [&](
char c) {
return c==dot || isAlphaChar(c); };
2373 const size_t size = data.size();
2374 while (i < size && data[i] ==
' ')
2379 if (indent>=refIndent+4)
2381 AUTO_TRACE_EXIT(
"result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2385 if (i<size && data[i]==
'`') tildaChar=
'`';
2386 while (i < size && data[i] == tildaChar)
2393 AUTO_TRACE_EXIT(
"result=false: no fence marker found #tildes={}",startTildes);
2397 while (i<size && data[i]==
' ') { i++; }
2398 if (i<size && data[i]==
'{')
2401 if (data[i] == dot) i++;
2403 while (i<size && (data[i]!=
'\n' && data[i]!=
'}')) i++;
2404 if (i<size && data[i]==
'}')
2406 lang = data.substr(startLang,i-startLang);
2415 else if (i<size && isLangChar(data[i]))
2417 if (data[i] == dot) i++;
2419 if (i<size && isAlphaChar(data[i]))
2422 while (i<size && isAlphaNChar(data[i])) i++;
2424 lang = data.substr(startLang,i-startLang);
2434 if (data[i]==tildaChar)
2438 while (i < size && data[i] == tildaChar)
2443 while (i<size && data[i]==
' ') i++;
2445 if (endTildes==startTildes)
2448 AUTO_TRACE_EXIT(
"result=true: found end marker at offset {} lang='{}'",offset,lang);
2455 warn(fileName, lineNr,
"Ending Inside a fenced code block. Maybe the end marker for the block is missing?");
2460static bool isCodeBlock(std::string_view data,
size_t offset,
size_t &indent)
2467 const size_t size = data.size();
2468 while (i < size && data[i] ==
' ')
2476 AUTO_TRACE_EXIT(
"result={}: line is not indented enough {}<4",
false,indent0);
2479 if (indent0>=size || data[indent0]==
'\n')
2481 AUTO_TRACE_EXIT(
"result={}: only spaces at the end of a comment block",
false);
2488 int offset_i =
static_cast<int>(offset);
2492 int j =
static_cast<int>(i)-offset_i-1;
2494 size_t nl_size =
isNewline(std::string_view(data.data()+j,data.size()-j));
2497 nl_pos[nl++]=j+
static_cast<int>(nl_size);
2503 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2514 if (!
isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2523 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2529 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2536 if (nl==1 && !
isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2544 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2560 const size_t size = data.size();
2563 while (i<size && data[i]==
' ') i++;
2564 if (i < size && data[i] ==
'|' && data[i] !=
'\n')
2573 while (i<size && (j =
isNewline(data.substr(i)))==0) i++;
2576 if (j>0 && i>0) i--;
2577 while (i>0 && data[i]==
' ') i--;
2578 if (i > 0 && data[i - 1] !=
'\\' && data[i] ==
'|')
2592 if (data[i]==
'|' && (i==0 || data[i-1]!=
'\\')) columns++;
2593 if (columns==1) columns++;
2597 if (n==2 && columns==0)
2609 size_t cc0=0, start=0,
end=0;
2613 if (i>=data.size() || cc0<1)
2625 if (data[j]!=
':' && data[j]!=
'-' && data[j]!=
'|' && data[j]!=
' ')
2634 AUTO_TRACE_EXIT(
"result=false: different number of columns as previous line {}!={}",cc1,cc0);
2649 const size_t size = data.size();
2651 size_t columns=0, start=0,
end=0;
2653 size_t headerStart = start;
2654 size_t headerEnd =
end;
2660 std::vector<Alignment> columnAlignment(columns);
2662 bool leftMarker=
false, rightMarker=
false, startFound=
false;
2668 if (data[j]==
':') { leftMarker=
TRUE; startFound=
TRUE; }
2669 if (data[j]==
'-') startFound=
TRUE;
2672 if (data[j]==
'-') rightMarker=
FALSE;
2673 else if (data[j]==
':') rightMarker=
TRUE;
2674 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2698 std::vector<std::vector<TableCell> > tableContents;
2700 size_t m = headerStart;
2701 std::vector<TableCell> headerContents(columns);
2702 for (k=0;k<columns;k++)
2704 while (m<=headerEnd && (data[m]!=
'|' || (m>0 && data[m-1]==
'\\')))
2706 headerContents[k].cellText += data[m++];
2711 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2712 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2714 tableContents.push_back(headerContents);
2720 if (cc!=columns)
break;
2724 std::vector<TableCell> rowContents(columns);
2727 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2731 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2732 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2737 rowContents[k].cellText += data[j];
2743 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2744 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2745 tableContents.push_back(rowContents);
2751 out+=
"<table class=\"markdownTable\">";
2752 QCString cellTag(
"th"), cellClass(
"class=\"markdownTableHead");
2753 for (
size_t row = 0; row < tableContents.size(); row++)
2759 out+=
"\n<tr class=\"markdownTableRowOdd\">";
2763 out+=
"\n<tr class=\"markdownTableRowEven\">";
2768 out+=
"\n <tr class=\"markdownTableHead\">";
2770 for (
size_t c = 0; c < columns; c++)
2773 QCString cellText(tableContents[row][c].cellText);
2779 if (tableContents[row][c].cellText ==
"^")
2783 if (tableContents[row][c].colSpan)
2785 int cr =
static_cast<int>(c);
2786 while ( cr >= 0 && tableContents[row][cr].colSpan)
2790 if (cr >= 0 && tableContents[row][cr].cellText ==
"^")
continue;
2792 size_t rowSpan = 1, spanRow = row+1;
2793 while ((spanRow < tableContents.size()) &&
2794 (tableContents[spanRow][c].cellText ==
"^"))
2800 out+=
" <" + cellTag +
" " + cellClass;
2802 switch (columnAlignment[c])
2814 out+=
" rowspan=\"" + spanStr +
"\"";
2820 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2829 out+=
" colspan=\"" + spanStr +
"\"";
2833 out+=
"> " + cellText +
" \\ilinebr </" + cellTag +
">";
2836 cellClass =
"class=\"markdownTableBody";
2852 while (i<data.size() && data[i]!=
'\n')
2854 if (data[i]!=
' ' && data[i]!=
'\t') j++;
2857 if (i>=data.size()) {
return 0; }
2858 if (i<2) {
return 0; }
2859 bool res = (j>0 && data[i-1]==
' ' && data[i-2]==
' ');
2899 out+=
"</"+hTag+
">\n";
2902 else if (data.size()>0)
2904 size_t tmpSize = data.size();
2905 if (data[data.size()-1] ==
'\n') tmpSize--;
2906 out+=data.substr(0,tmpSize);
2910 out+=
"\\ilinebr<br>";
2912 if (tmpSize != data.size())
out+=
'\n';
2918 {
"[!note]",
"\\note" },
2919 {
"[!warning]",
"\\warning" },
2920 {
"[!tip]",
"\\remark" },
2921 {
"[!caution]",
"\\attention" },
2922 {
"[!important]",
"\\important" }
2931 const size_t size = data.size();
2932 std::string startCmd;
2933 int isGitHubAlert =
false;
2934 int isGitHubFirst =
false;
2939 while (
end<=size && data[
end-1]!=
'\n')
end++;
2944 while (j<
end && (data[j]==
' ' || data[j]==
'>'))
2946 if (data[j]==
'>') { level++; indent=j+1; }
2947 else if (j>0 && data[j-1]==
'>') indent=j+1;
2950 if (indent>0 && j>0 && data[j-1]==
'>' &&
2951 !(j==size || data[j]==
'\n'))
2968 isGitHubAlert =
true;
2969 isGitHubFirst =
true;
2970 startCmd = it->second;
2975 if (level!=1 || !isGitHubAlert)
2977 for (
int l=curLevel;l<level-1;l++)
2979 out+=
"<blockquote>";
2981 out +=
"<blockquote>‍";
2983 else if (!startCmd.empty())
2985 out += startCmd +
" ";
2988 else if (level<curLevel)
2991 if (level==0 && isGitHubAlert)
2997 out +=
"</blockquote>\\ilinebr ";
3006 if (curLevel!=0 || !isGitHubAlert)
3008 std::string_view txt = data.substr(indent,
end-indent);
3011 if (!isGitHubFirst)
out +=
"<br>";
3018 isGitHubFirst =
false;
3033 for (
int l=0;l<curLevel;l++)
3035 out+=
"</blockquote>";
3046 size_t size = data.size();
3047 while (i<data.size() && data[i]==
' ') i++;
3050 size_t locStart = i;
3051 if (i>offset) locStart--;
3054 while (i+9<size && data[i]!=
'\n')
3066 location=data.substr(locStart,i-locStart);
3068 while (indent > 0 && i < size && data[i] ==
' ')
3073 if (i<size && data[i]==
'\n') i++;
3084 const size_t size = data.size();
3087 out+=
"@iverbatim\n";
3089 std::string location;
3094 while (
end<=size && data[
end-1]!=
'\n')
end++;
3097 while (j <
end && data[j] ==
' ')
3111 while (emptyLines>0)
3119 std::string lineLoc;
3124 out+=data.substr(offset,
end-offset);
3132 out+=
"@endiverbatim";
3133 if (!location.empty())
3141 while (emptyLines>0)
3157 const size_t size = data.size();
3158 size_t nb=0,
end=offset+1, j=0;
3163 if ((data[
end-1]==
'\\' || data[
end-1]==
'@') &&
3164 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3171 size_t l = endBlockName.
length();
3172 for (;
end+l+1<size;
end++)
3174 if ((data[
end]==
'\\' || data[
end]==
'@') &&
3175 data[
end-1]!=
'\\' && data[
end-1]!=
'@'
3189 else if (nb==0 && data[
end-1]==
'<' && size>=6 &&
end+6<size &&
3190 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3193 if (tolower(data[
end])==
'p' && tolower(data[
end+1])==
'r' &&
3194 tolower(data[
end+2])==
'e' && (data[
end+3]==
'>' || data[
end+3]==
' '))
3205 else if (nb==0 && data[
end-1]==
'`')
3207 while (
end <= size && data[
end - 1] ==
'`')
3213 else if (nb>0 && data[
end-1]==
'`')
3216 while (
end <= size && data[
end - 1] ==
'`')
3234 size_t blockStart,
size_t blockEnd)
3237 if (!lang.empty() && lang[0]==
'.') lang=lang.substr(1);
3238 const size_t size=data.size();
3240 while (i<size && (data[i]==
' ' || data[i]==
'\t'))
3261 size_t pi=std::string::npos;
3262 bool newBlock =
false;
3263 bool insideList =
false;
3264 size_t currentIndent = refIndent;
3265 size_t listIndent = refIndent;
3266 const size_t size = data.size();
3273 size_t lineIndent=0;
3274 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3280 if (insideList && lineIndent<currentIndent)
3283 currentIndent = refIndent;
3291 if (listIndent<currentIndent+4)
3295 currentIndent = listIndent;
3302 currentIndent = listIndent;
3311 if (pi!=std::string::npos)
3313 size_t blockStart=0, blockEnd=0, blockOffset=0;
3316 auto addSpecialCommand = [&](
const QCString &startCmd,
const QCString &endCmd)
3318 size_t cmdPos = pi+blockStart+1;
3319 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3325 if (pl[ii]==
'\n') nl++;
3328 bool addNewLines =
false;
3330 (pl[ii]!=
'\\' && pl[ii]!=
'@') ||
3339 pl =
"@"+startCmd+
"\n" + pl +
"@"+endCmd;
3340 addNewLines =
false;
3355 if (addNewLines)
for (
int j=0;j<nl;j++)
out+=
'\n';
3357 if (addNewLines)
out+=
'\n';
3362 addSpecialCommand(
"startuml",
"enduml");
3366 addSpecialCommand(
"dot",
"enddot");
3368 else if (lang==
"msc")
3370 addSpecialCommand(
"msc",
"endmsc");
3372 else if (lang==
"mermaid")
3374 addSpecialCommand(
"mermaid",
"endmermaid");
3381 pi=std::string::npos;
3385 else if (
isBlockQuote(data.substr(pi,i-pi),currentIndent))
3388 pi=std::string::npos;
3395 out+=data.substr(pi,i-pi);
3401 if (pi!=std::string::npos && pi<size)
3411 warn(
fileName,
lineNr,
"Ending inside a fenced code block. Maybe the end marker for the block is missing?");
3413 out+=data.substr(pi);
3427 size_t pi = std::string::npos;
3443 size_t currentIndent = indent;
3444 size_t listIndent = indent;
3445 bool insideList =
false;
3446 bool newBlock =
false;
3449 while (i<data.size())
3454 size_t lineIndent=0;
3456 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3462 if (insideList && lineIndent<currentIndent)
3465 currentIndent = indent;
3473 if (listIndent<currentIndent+4)
3477 currentIndent = listIndent;
3484 currentIndent = listIndent;
3496 if (pi!=std::string::npos)
3498 size_t blockStart=0, blockEnd=0, blockOffset=0;
3500 size_t blockIndent = currentIndent;
3504 if (data[i]==
'@' || data[i]==
'\\') endBlockName =
isBlockCommand(data.substr(i),i);
3508 if (
isLinkRef(data.substr(pi,i-pi),
id,link,title))
3518 size_t l = endBlockName.
length();
3519 while (i+l<data.size())
3521 if ((data[i]==
'\\' || data[i]==
'@') &&
3522 data[i-1]!=
'\\' && data[i-1]!=
'@')
3539 while (pi<data.size() && data[pi]==
' ') pi++;
3540 QCString header = data.substr(pi,i-pi-1);
3547 out+=level==1?
"@section ":
"@subsection ";
3555 out+=level==1?
"<h1>":
"<h2>";
3557 out+=level==1?
"\n</h1>\n":
"\n</h2>\n";
3564 pi=std::string::npos;
3569 else if ((ref=
isLinkRef(data.substr(pi),
id,link,title)))
3583 pi=std::string::npos;
3591 pi=std::string::npos;
3598 pi=std::string::npos;
3611 if (pi!=std::string::npos && pi<data.size())
3613 if (
isLinkRef(data.substr(pi),
id,link,title))
3630#define OPC(x) if (literal_at(data,#x " ") || literal_at(data,#x "\n")) return true
3631 OPC(dir);
OPC(defgroup);
OPC(addtogroup);
OPC(weakgroup);
OPC(ingroup);
3634 OPC(protocol);
OPC(category);
OPC(
union);
OPC(
struct);
OPC(interface);
3635 OPC(idlexcept);
OPC(file);
3645 std::string_view data(docs.
str());
3646 const size_t size = data.size();
3649 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3656 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3662 (data[i]==
'\\' || data[i]==
'@') &&
3677 else if (i+1<size && (data[i]==
'\\' || data[i]==
'@') &&
isOtherPage(data.substr(i+1)))
3695 std::string_view data(docs_org.
str());
3696 const size_t size = data.size();
3698 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3700 if (data[i]==
'\n') prepend++;
3703 if (i>=size) {
return QCString(); }
3705 while (end1<size && data[end1-1]!=
'\n') end1++;
3712 while (end2<size && data[end2-1]!=
'\n') end2++;
3713 if (
prv->isHeaderline(data.substr(end1),
FALSE))
3715 title = data.substr(i,end1-i-1);
3716 docs+=
"\n\n"+docs_org.
mid(end2);
3717 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3723 if (i<end1 && prv->isAtxHeader(data.substr(i,end1-i),title,
id,
FALSE,&isIdGenerated)>0)
3726 docs+=docs_org.
mid(end1);
3731 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3742 if (input.
isEmpty())
return input;
3747 if (s.
at(s.
length()-1)!=
'\n') s +=
"\n";
3748 s =
detab(s,refIndent);
3752 s =
prv->processQuotations(s.
view(),refIndent);
3756 s =
prv->processBlocks(s.
view(),refIndent);
3774 const char *p = result.
data();
3777 while (*p==
' ') p++;
3778 while (*p==
'\n') {startNewlines++;p++;};
3781 if (p>result.
data())
3784 result = result.
mid(
static_cast<int>(p-result.
data()));
3797 if (i!=-1) baseFn = baseFn.
left(i);
3821 const char *fileBuf,
3822 const std::shared_ptr<Entry> &root,
3825 std::shared_ptr<Entry> current = std::make_shared<Entry>();
3827 current->lang = SrcLangExt::Markdown;
3828 current->fileName = fileName;
3829 current->docFile = fileName;
3830 current->docLine = 1;
3836 bool isIdGenerated =
false;
3844 int indentLevel=title.
isEmpty() ? 0 : -1;
3851 bool wasEmpty =
id.isEmpty();
3852 if (wasEmpty)
id = mdFileNameId;
3858 if (!mdfileAsMainPage.
isEmpty() &&
3862 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3863 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3865 else if (
id==
"mainpage" ||
id==
"index")
3867 if (title.
isEmpty()) title = titleFn;
3868 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3869 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3871 else if (isSubdirDocs)
3875 docs.
prepend(
"@section " + generatedId +
" " + title +
"\\ilinebr ");
3877 docs.
prepend(
"@dir\\ilinebr ");
3888 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr @ianchor{" + relFileName +
"} " + mdFileNameId +
"\\ilinebr ");
3890 else if (!generatedId.
isEmpty())
3892 docs.
prepend(
"@ianchor " + generatedId +
"\\ilinebr ");
3894 else if (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB)
3897 docs.
prepend(
"@ianchor{" + title +
"} " + autoId +
"\\ilinebr ");
3899 docs.
prepend(
"@page "+
id+
" "+title+
"\\ilinebr ");
3901 for (
int i = 0; i < prepend; i++) docs.
prepend(
"\n");
3906 static const reg::Ex re(R
"([ ]*[\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
3908 std::string s = docs.str();
3911 QCString orgLabel = match[1].str();
3912 QCString orgTitle = match[2].str();
3915 docs = docs.
left(match[1].position())+
3918 "\\ilinebr @ianchor{" + orgTitle +
"} "+orgLabel+
"\n"+
3930 p->commentScanner.enterFile(fileName,lineNr);
3932 bool needsEntry =
false;
3936 while (
p->commentScanner.parseCommentBlock(
3954 QCString docFile = current->docFile;
3955 root->moveToSubEntryAndRefresh(current);
3956 current->lang = SrcLangExt::Markdown;
3957 current->docFile = docFile;
3958 current->docLine = lineNr;
3963 root->moveToSubEntryAndKeep(current);
3965 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...
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
Helper class to process markdown formatted text.
static ActionTable_t fill_table()
std::array< Action_t, 256 > ActionTable_t
std::unique_ptr< Private > prv
static ActionTable_t actions
void setIndentLevel(int level)
QCString extractPageTitle(QCString &docs, QCString &id, int &prepend, bool &isIdGenerated)
Markdown(const QCString &fileName, int lineNr, int indentLevel=0)
QCString process(const QCString &input, int &startNewlines, bool fromParseInput=false)
std::function< int(Private &, std::string_view, size_t)> Action_t
void parseInput(const QCString &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 QCString &text) override
Callback function called by the comment block scanner.
std::unique_ptr< Private > p
This is an alternative implementation of QCString.
int find(char c, int index=0, bool cs=TRUE) const
QCString & prepend(const char *s)
size_t length() const
Returns the length of the string, not counting the 0-terminator.
bool startsWith(const char *s) const
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
bool endsWith(const char *s) const
char & at(size_t i)
Returns a reference to the character at index i.
bool isEmpty() const
Returns TRUE iff the string is empty.
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
const std::string & str() const
QCString & setNum(short n)
QCString simplifyWhiteSpace() const
return a copy of this string with leading and trailing whitespace removed and multiple whitespace cha...
QCString right(size_t len) const
size_t size() const
Returns the length of the string, not counting the 0-terminator.
QCString & sprintf(const char *format,...)
int findRev(char c, int index=-1, bool cs=TRUE) const
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
std::string_view view() const
QCString left(size_t len) const
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(...)
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 size_t isLinkRef(std::string_view data, QCString &refid, QCString &link, QCString &title)
returns end of the link ref if this is indeed a link reference.
static QCString escapeDoubleQuotes(const QCString &s)
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 size_t computeIndentExcludingListMarkers(std::string_view data)
static const char * g_doxy_nbsp
static QCString escapeSpecialChars(const QCString &s)
static constexpr bool ignoreCloseEmphChar(char c, char cn)
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 QCString &docs)
static bool isFencedCodeBlock(std::string_view data, size_t refIndent, QCString &lang, size_t &start, size_t &end, size_t &offset, QCString &fileName, int lineNr)
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)
static QCString getFilteredImageAttributes(std::string_view fmt, const QCString &attrs)
parse the image attributes and return attributes for given format
bool skipOverFileAndLineCommands(std::string_view data, size_t indent, size_t &offset, std::string &location)
static constexpr bool extraChar(char c)
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)
QCString markdownFileNameToId(const QCString &fileName)
processes string s and converts markdown into doxygen/html commands.
#define warn(file, line, fmt,...)
const Mapper< CommandType > * cmdMapper
bool isAbsolutePath(const QCString &fileName)
const char * strnstr(const char *haystack, const char *needle, size_t haystack_len)
QCString trunc(const QCString &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.
static void decrLevel(yyscan_t yyscanner)
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
int qstrncmp(const char *str1, const char *str2, size_t len)
const char * qPrint(const char *s)
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 QCString &l, const QCString &t)
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.
void writeMarkdownImage(std::string_view fmt, bool inline_img, bool explicitTitle, const QCString &title, const QCString &content, const QCString &link, const QCString &attributes, const FileDef *fd)
size_t writeTableBlock(std::string_view data)
size_t writeBlockQuote(std::string_view data)
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)
QCString extractTitleId(QCString &title, int level, bool *pIsIdGenerated=nullptr)
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)
QCString isBlockCommand(std::string_view data, size_t offset)
size_t writeCodeBlock(std::string_view, size_t refIndent)
int processHtmlTag(std::string_view data, size_t offset)
QCString processQuotations(std::string_view data, size_t refIndent)
QCString processBlocks(std::string_view data, size_t indent)
int processEmphasis(std::string_view data, size_t offset)
int processLink(std::string_view data, size_t offset)
int processHtmlTagWrite(std::string_view data, size_t offset, bool doWrite)
Process a HTML tag.
int isAtxHeader(std::string_view data, QCString &header, QCString &id, bool allowAdjustLevel, bool *pIsIdGenerated=nullptr)
size_t findEndOfLine(std::string_view data, size_t offset)
int processEmphasis2(std::string_view data, char c)
process double emphasis
void processInline(std::string_view data)
Private(const QCString &fn, int line, int indent)
int processNmdash(std::string_view data, size_t offset)
Process ndash and mdashes.
void writeOneLineHeaderOrRuler(std::string_view data)
CommentScanner commentScanner
SrcLangExt getLanguageFromFileName(const QCString &fileName, SrcLangExt defLang)
QCString stripIndentation(const QCString &s, bool skipFirstLine)
QCString escapeCharsInString(const QCString &name, bool allowDots, bool allowUnderscore)
QCString stripExtensionGeneral(const QCString &fName, const QCString &ext)
bool isURL(const QCString &url)
Checks whether the given url starts with a supported protocol.
static QCString stripFromPath(const QCString &p, const StringVector &l)
QCString detab(const QCString &s, size_t &refIndent)
StringVector split(const std::string &s, const std::string &delimiter)
split input string s by string delimiter delimiter.
QCString externalLinkTarget(const bool parent)
QCString getFileNameExtension(const QCString &fn)
FileDef * findFileDef(const FileNameLinkedMap *fnMap, const QCString &n, bool &ambig)
A bunch of utility functions.