36#include <unordered_map>
57#if !ENABLE_MARKDOWN_TRACING
61#define AUTO_TRACE(...) (void)0
62#define AUTO_TRACE_ADD(...) (void)0
63#define AUTO_TRACE_EXIT(...) (void)0
79 return (c>=
'a' && c<=
'z') ||
82 (
static_cast<unsigned char>(c)>=0x80);
88 return c==
'-' || c==
'+' || c==
'!' || c==
'?' || c==
'$' || c==
'@' ||
89 c==
'&' || c==
'*' || c==
'_' || c==
'%' || c==
'[' || c==
'(' ||
90 c==
'.' || c==
'>' || c==
':' || c==
',' || c==
';' || c==
'\'' ||
91 c==
'"' || c==
'`' || c==
'\\';
97 return c==
'\n' || c==
' ' || c==
'\'' || c==
'<' ||
98 c==
'>' || c==
'{' || c==
'(' || c==
'[' ||
99 c==
',' || c==
':' || c==
';';
105 return c1==
static_cast<char>(0xc2) && c2==
static_cast<char>(0xa0);
111 (offset>1 && !
isUtf8Nbsp(data.data()[-2],data.data()[-1])));
118 return c==
'(' || c==
'{' || c==
'[' || (c==
'<' && cn!=
'/') || c==
'\\' || c==
'@';
149 int processLink(std::string_view data,
size_t offset);
157 int isHeaderline(std::string_view data,
bool allowAdjustLevel);
159 bool *pIsIdGenerated=
nullptr);
162 size_t blockStart,
size_t blockEnd);
191 a[
static_cast<unsigned int>(
'[')] = [](
Markdown::Private &obj,std::string_view data,
size_t offset) {
return obj.
processLink (data,offset); };
192 a[
static_cast<unsigned int>(
'!')] = [](
Markdown::Private &obj,std::string_view data,
size_t offset) {
return obj.
processLink (data,offset); };
202 :
prv(std::make_unique<
Private>(fileName,lineNr,indentLevel))
204 using namespace std::placeholders;
228 if (data[0] ==
'\n')
return 1;
232 return (data.size()>8 && data[8]==
' ') ? 9 : 8;
243 const char *p=s.
data();
247 if (c==
'"' && pc!=
'\\') result+=
'\\';
260 bool insideQuote=
FALSE;
262 const char *p=s.
data();
277 insideQuote=!insideQuote;
289 if ((p[0]==
':') && (p[1]==
':'))
301 case '\\':
if (!insideQuote) { result+=
'\\'; } result+=
'\\';
break;
302 case '@':
if (!insideQuote) { result+=
'\\'; } result+=
'@';
break;
305 case '#':
if (!insideQuote) { result+=
'\\'; } result+=
'#';
break;
306 case '$':
if (!insideQuote) { result+=
'\\'; } result+=
'$';
break;
307 case '&':
if (!insideQuote) { result+=
'\\'; } result+=
'&';
break;
322 if (leftMarker && rightMarker)
330 else if (rightMarker)
345 for (
const auto &attr_ : attrList)
348 int i = attr.
find(
':');
355 return attr.
mid(i+1);
393 using EndBlockFunc =
QCString (*)(
const std::string &,bool,char);
395 static constexpr auto getEndBlock = [](
const std::string &blockName,bool,char) ->
QCString
397 return "end"+blockName;
399 static constexpr auto getEndCode = [](
const std::string &blockName,
bool openBracket,char) ->
QCString
401 return openBracket ?
QCString(
"}") :
"end"+blockName;
403 static constexpr auto getEndUml = [](
const std::string &,bool,char) ->
QCString
407 static constexpr auto getEndFormula = [](
const std::string &,bool,
char nextChar) ->
QCString
411 case '$':
return "f$";
412 case '(':
return "f)";
413 case '[':
return "f]";
414 case '{':
return "f}";
420 static const std::unordered_map<std::string,EndBlockFunc> blockNames =
422 {
"dot", getEndBlock },
423 {
"code", getEndCode },
424 {
"icode", getEndBlock },
425 {
"msc", getEndBlock },
426 {
"verbatim", getEndBlock },
427 {
"iverbatim", getEndBlock },
428 {
"iliteral", getEndBlock },
429 {
"latexonly", getEndBlock },
430 {
"htmlonly", getEndBlock },
431 {
"xmlonly", getEndBlock },
432 {
"rtfonly", getEndBlock },
433 {
"manonly", getEndBlock },
434 {
"docbookonly", getEndBlock },
435 {
"startuml", getEndUml },
436 {
"f", getEndFormula }
439 const size_t size = data.
size();
440 bool openBracket = offset>0 && data.data()[-1]==
'{';
441 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
442 if (isEscaped)
return result;
445 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
446 if (
end==1)
return result;
447 std::string blockName(data.substr(1,
end-1));
448 auto it = blockNames.find(blockName);
449 if (it!=blockNames.end())
451 result = it->second(blockName, openBracket,
end<size ? data[
end] : 0);
461 using EndCmdFunc = size_t (*)(std::string_view,size_t);
463 static constexpr auto endOfLine = [](std::string_view data_,
size_t offset_) ->
size_t
468 while (offset_<data_.size() && ((c=data_[offset_])!=
'\n' || lc==
'\\'))
470 if (c==
'\\') lc=
'\\';
471 else if (c!=
' ') lc=0;
477 static constexpr auto endOfLabels = [](std::string_view data_,
size_t offset_,
bool multi_) ->
size_t
479 if (offset_<data_.size() && data_[offset_]==
' ')
487 while (offset_<data_.size() && data_[offset_]==
' ')
492 while (offset_<data_.size() && (c=data_[offset_])!=
' ' && c!=
',' && c!=
'\\' && c!=
'@' && c!=
'\n')
497 if (multi_ && offset_<data_.size() && (data_[offset_]==
',' || data_[offset_]==
' '))
499 size_t off = offset_;
500 while (off<data_.size() && data_[off]==
' ')
504 if (off<data_.size() && data_[off]==
',')
523 static constexpr auto endOfLabel = [](std::string_view data_,
size_t offset_) ->
size_t
525 return endOfLabels(data_,offset_,
false);
528 static constexpr auto endOfLabelOpt = [](std::string_view data_,
size_t offset_) ->
size_t
530 size_t index=offset_;
531 if (index<data_.size() && data_[index]==
' ')
534 while (index<data_.size() && data_[index]==
' ') index++;
536 if (index<data_.size() && data_[index]==
'{')
540 while (index<data_.size() && (c=data_[index])!=
'}' && c!=
'\\' && c!=
'@' && c!=
'\n') index++;
541 if (index==data_.size() || data_[index]!=
'}')
return 0;
544 return endOfLabel(data_,offset_);
547 static constexpr auto endOfParam = [](std::string_view data_,
size_t offset_) ->
size_t
549 size_t index=offset_;
550 if (index<data_.size() && data_[index]==
' ')
553 while (index<data_.size() && data_[index]==
' ') index++;
555 if (index<data_.size() && data_[index]==
'[')
559 while (index<data_.size() && (c=data_[index])!=
']' && c!=
'\n') index++;
560 if (index==data_.size() || data_[index]!=
']')
return 0;
563 return endOfLabels(data_,offset_,
true);
566 static constexpr auto endOfRetVal = [](std::string_view data_,
size_t offset_) ->
size_t
568 return endOfLabels(data_,offset_,
true);
571 static constexpr auto endOfFuncLike = [](std::string_view data_,
size_t offset_,
bool allowSpaces) ->
size_t
573 if (offset_<data_.size() && data_[offset_]==
' ')
578 while (offset_<data_.size() && data_[offset_]==
' ')
583 while (offset_<data_.size() && (c=data_[offset_])!=
'\n' && (allowSpaces || c!=
' ') && c!=
'(')
585 if (
literal_at(data_.substr(offset_),
"\\ilinebr "))
break;
592 while (offset_<data_.size() && (c=data_[offset_++]))
595 else if (c==
')') count--;
596 if (count==0)
return offset_;
604 static constexpr auto endOfFunc = [](std::string_view data_,
size_t offset_) ->
size_t
606 return endOfFuncLike(data_,offset_,
true);
609 static constexpr auto endOfGuard = [](std::string_view data_,
size_t offset_) ->
size_t
611 return endOfFuncLike(data_,offset_,
false);
614 static const std::unordered_map<std::string,EndCmdFunc> cmdNames =
617 {
"addindex", endOfLine },
618 {
"addtogroup", endOfLabel },
619 {
"anchor", endOfLabel },
622 {
"category", endOfLine },
623 {
"cite", endOfLabel },
624 {
"class", endOfLine },
625 {
"concept", endOfLine },
626 {
"copybrief", endOfFunc },
627 {
"copydetails", endOfFunc },
628 {
"copydoc", endOfFunc },
629 {
"def", endOfFunc },
630 {
"defgroup", endOfLabel },
631 {
"diafile", endOfLine },
632 {
"dir", endOfLine },
633 {
"dockbookinclude",endOfLine },
634 {
"dontinclude", endOfLine },
635 {
"dotfile", endOfLine },
637 {
"elseif", endOfGuard },
638 {
"em", endOfLabel },
639 {
"emoji", endOfLabel },
640 {
"enum", endOfLabel },
641 {
"example", endOfLine },
642 {
"exception", endOfLine },
643 {
"extends", endOfLabel },
644 {
"file", endOfLine },
646 {
"headerfile", endOfLine },
647 {
"htmlinclude", endOfLine },
648 {
"ianchor", endOfLabelOpt },
649 {
"idlexcept", endOfLine },
650 {
"if", endOfGuard },
651 {
"ifnot", endOfGuard },
652 {
"image", endOfLine },
653 {
"implements", endOfLine },
654 {
"include", endOfLine },
655 {
"includedoc", endOfLine },
656 {
"includelineno", endOfLine },
657 {
"ingroup", endOfLabel },
658 {
"interface", endOfLine },
659 {
"latexinclude", endOfLine },
660 {
"maninclude", endOfLine },
661 {
"memberof", endOfLabel },
662 {
"mscfile", endOfLine },
663 {
"namespace", endOfLabel },
664 {
"noop", endOfLine },
665 {
"overload", endOfLine },
667 {
"package", endOfLabel },
668 {
"page", endOfLabel },
669 {
"paragraph", endOfLabel },
670 {
"param", endOfParam },
671 {
"property", endOfLine },
672 {
"protocol", endOfLine },
673 {
"qualifier", endOfLine },
674 {
"ref", endOfLabel },
675 {
"refitem", endOfLine },
676 {
"related", endOfLabel },
677 {
"relatedalso", endOfLabel },
678 {
"relates", endOfLabel },
679 {
"relatesalso", endOfLabel },
680 {
"requirement", endOfLabel },
681 {
"retval", endOfRetVal},
682 {
"rtfinclude", endOfLine },
683 {
"section", endOfLabel },
684 {
"skip", endOfLine },
685 {
"skipline", endOfLine },
686 {
"snippet", endOfLine },
687 {
"snippetdoc", endOfLine },
688 {
"snippetlineno", endOfLine },
689 {
"struct", endOfLine },
690 {
"satisfies", endOfLabel },
691 {
"subpage", endOfLabel },
692 {
"subparagraph", endOfLabel },
693 {
"subsubparagraph",endOfLabel },
694 {
"subsection", endOfLabel },
695 {
"subsubsection", endOfLabel },
696 {
"throw", endOfLabel },
697 {
"throws", endOfLabel },
698 {
"tparam", endOfLabel },
699 {
"typedef", endOfLine },
700 {
"plantumlfile", endOfLine },
701 {
"union", endOfLine },
702 {
"until", endOfLine },
703 {
"var", endOfLine },
704 {
"verbinclude", endOfLine },
705 {
"verifies", endOfLabel },
706 {
"weakgroup", endOfLabel },
707 {
"xmlinclude", endOfLine },
708 {
"xrefitem", endOfLabel }
711 bool isEscaped = offset>0 && (data.data()[-1]==
'\\' || data.data()[-1]==
'@');
712 if (isEscaped)
return 0;
714 const size_t size = data.size();
716 while (
end<size && (data[
end]>=
'a' && data[
end]<=
'z'))
end++;
717 if (
end==1)
return 0;
718 std::string cmdName(data.substr(1,
end-1));
720 auto it = cmdNames.find(cmdName);
721 if (it!=cmdNames.end())
724 result = it->second(data,
end);
737 const size_t size = data.size();
741 while (i<size && data[i]!=c &&
742 data[i]!=
'\\' && data[i]!=
'@' &&
743 !(data[i]==
'/' && data[i-1]==
'<') &&
763 while (i+len<size && data[i+len]==c)
770 if (len!=c_size || (i+len<size &&
isIdChar(data[i+len])))
776 return static_cast<int>(i);
784 while (i < size && data[i] ==
'`')
792 while (i<size && enb<snb)
794 if (data[i]==
'`') enb++;
795 if (snb==1 && data[i]==
'\'')
break;
799 else if (data[i]==
'@' || data[i]==
'\\')
805 size_t l = endBlockName.
length();
808 if ((data[i]==
'\\' || data[i]==
'@') &&
809 data[i-1]!=
'\\' && data[i-1]!=
'@')
819 else if (i+1<size &&
isIdChar(data[i+1]))
828 else if (data[i-1]==
'<' && data[i]==
'/')
832 else if (data[i]==
'\n')
835 while (i<size && data[i]==
' ') i++;
836 if (i>=size || data[i]==
'\n')
854 const size_t size = data.size();
857 if (size>1 && data[0]==c && data[1]==c) { i=1; }
862 if (len==0) {
return 0; }
864 if (i>=size) {
return 0; }
866 if (i+1<size && data[i+1]==c)
871 if (data[i]==c && data[i-1]!=
' ' && data[i-1]!=
'\n')
877 return static_cast<int>(i+1);
888 const size_t size = data.size();
898 if (i+1<size && data[i]==c && data[i+1]==c && i && data[i-1]!=
' ' && data[i-1]!=
'\n')
900 if (c ==
'~')
out+=
"<strike>";
901 else out+=
"<strong>";
903 if (c ==
'~')
out+=
"</strike>";
904 else out+=
"</strong>";
906 return static_cast<int>(i+2);
920 const size_t size = data.size();
932 if (data[i]!=c || data[i-1]==
' ' || data[i-1]==
'\n')
937 if (i+2<size && data[i+1]==c && data[i+2]==c)
941 out+=
"</strong></em>";
943 return static_cast<int>(i+3);
945 else if (i+1<size && data[i+1]==c)
956 return static_cast<int>(len - 2);
970 return static_cast<int>(len - 1);
981 const size_t size = data.size();
985 if (i<size && data[i]==
'-')
990 if (i<size && data[i]==
'-')
995 if (i<size && data[i]==
'-')
999 if (count>=2 && offset>=2 &&
literal_at(data.data()-2,
"<!"))
1001 if (count==2 && size > 2 && data[2]==
'>')
1003 if (count==3 && size > 3 && data[3]==
'>')
1005 if (count==2 && (offset<8 || !
literal_at(data.data()-8,
"operator")))
1025 const size_t size = data.size();
1028 while (i<size && data[i]!=
'"' && nl<2)
1030 if (data[i]==
'\n') nl++;
1033 if (i<size && data[i]==
'"' && nl<2)
1035 out+=data.substr(0,i+1);
1037 return static_cast<int>(i+1);
1049 if (offset>0 && data.data()[-1]==
'\\') {
return 0; }
1051 const size_t size = data.size();
1057 while (i < size &&
isIdChar(data[i]))
1062 QCString tagName(data.substr(1,i-1));
1063 if (tagName.
lower()==
"pre")
1065 bool insideStr=
FALSE;
1069 if (!insideStr && c==
'<')
1071 if (data[i+1]==
'/' &&
1072 tolower(data[i+2])==
'p' && tolower(data[i+3])==
'r' &&
1073 tolower(data[i+4])==
'e' && tolower(data[i+5])==
'>')
1075 if (doWrite)
out+=data.substr(0,i+6);
1078 return static_cast<int>(i+6);
1081 else if (insideStr && c==
'"')
1083 if (data[i-1]!=
'\\') insideStr=
FALSE;
1096 if (data[i]==
'/' && i+1<size && data[i+1]==
'>')
1099 if (doWrite)
out+=data.substr(0,i+2);
1101 return static_cast<int>(i+2);
1103 else if (data[i]==
'>')
1106 if (doWrite)
out+=data.substr(0,i+1);
1108 return static_cast<int>(i+1);
1110 else if (data[i]==
' ')
1113 bool insideAttr=
FALSE;
1116 if (!insideAttr && data[i]==
'"')
1120 else if (data[i]==
'"' && data[i-1]!=
'\\')
1124 else if (!insideAttr && data[i]==
'>')
1127 if (doWrite)
out+=data.substr(0,i+1);
1129 return static_cast<int>(i+1);
1149 const size_t size = data.size();
1161 if (size>2 && c!=
'~' && data[1]!=c)
1164 if (data[1]==
' ' || data[1]==
'\n' ||
1172 if (size>3 && data[1]==c && data[2]!=c)
1174 if (data[2]==
' ' || data[2]==
'\n' ||
1182 if (size>4 && c!=
'~' && data[1]==c && data[2]==c && data[3]!=c)
1184 if (data[3]==
' ' || data[3]==
'\n' ||
1196 std::string_view
fmt,
bool inline_img,
bool explicitTitle,
1201 AUTO_TRACE(
"fmt={} inline_img={} explicitTitle={} title={} content={} link={} attrs={}",
1212 out+=link.
mid(fd ? 0 : 5);
1213 if (!explicitTitle && !content.
isEmpty())
1219 else if ((content.
isEmpty() || explicitTitle) && !title.
isEmpty())
1241 const size_t size = data.size();
1245 bool isImageLink =
FALSE;
1246 bool isImageInline =
FALSE;
1252 if (size<2 || data[1]!=
'[')
1261 while (pos>=-
static_cast<int>(offset) && numNLsNeeded>0)
1263 if (data.data()[pos]==
'\n') numNLsNeeded--;
1264 else if (data.data()[pos]!=
' ')
1274 size_t contentStart=i;
1281 if (data[i-1]==
'\\')
1284 else if (data[i]==
'[')
1288 else if (data[i]==
']')
1291 if (level<=0)
break;
1293 else if (data[i]==
'\n')
1296 if (nl>1) {
return 0; }
1302 if (i>=size)
return 0;
1303 size_t contentEnd=i;
1304 content = data.substr(contentStart,contentEnd-contentStart);
1306 if (!isImageLink && content.
isEmpty()) {
return 0; }
1309 bool whiteSpace =
false;
1311 while (i<size && data[i]==
' ') { whiteSpace =
true; i++; }
1312 if (i<size && data[i]==
'\n')
1317 while (i<size && data[i]==
' ') i++;
1319 if (whiteSpace && i<size && (data[i]==
'(' || data[i]==
'['))
return 0;
1321 bool explicitTitle=
FALSE;
1322 if (i<size && data[i]==
'(')
1325 while (i<size && data[i]==
' ') i++;
1326 bool uriFormat=
false;
1327 if (i<size && data[i]==
'<') { i++; uriFormat=
true; }
1331 while (i<size && data[i]!=
'\'' && data[i]!=
'"' && braceCount>0)
1337 if (nlConsec>1) {
return 0; }
1339 else if (data[i]==
'(')
1344 else if (data[i]==
')')
1349 else if (data[i]!=
' ')
1360 if (i>=size || data[i]==
'\n') {
return 0; }
1361 link = data.substr(linkStart,i-linkStart);
1364 if (link.
isEmpty()) {
return 0; }
1368 if (data[i]==
'\'' || data[i]==
'"')
1372 size_t titleStart=i;
1378 if (nl>1) {
return 0; }
1381 else if (data[i]==
'\\')
1385 else if (data[i]==c)
1396 size_t titleEnd = i-1;
1398 while (titleEnd>titleStart && data[titleEnd]==
' ') titleEnd--;
1399 if (data[titleEnd]==c)
1401 title = data.substr(titleStart,titleEnd-titleStart);
1405 if (data[i]==
' ')i++;
1406 else if (data[i] ==
')')
break;
1420 else if (i<size && data[i]==
'[')
1426 while (i<size && data[i]!=
']')
1431 if (nl>1) {
return 0; }
1435 if (i>=size) {
return 0; }
1437 link = data.substr(linkStart,i-linkStart);
1449 link = lr_it->second.link;
1450 title = lr_it->second.title;
1460 else if (i<size && data[i]!=
':' && !content.
isEmpty())
1467 link = lr_it->second.link;
1468 title = lr_it->second.title;
1472 else if (content==
"TOC")
1495 while (j<size && data[j]==
' ') { j++; }
1496 if (j<size && data[j]==
'{')
1501 size_t attributesStart=i;
1506 if (data[i-1]==
'\\')
1509 else if (data[i]==
'{')
1513 else if (data[i]==
'}')
1516 if (level<=0)
break;
1518 else if (data[i]==
'\n')
1521 if (nl>1) {
return 0; }
1526 if (i>=size)
return 0;
1527 size_t attributesEnd=i;
1528 attributes = data.substr(attributesStart,attributesEnd-attributesStart);
1537 while (pos<size && numNLsNeeded>0)
1539 if (data[pos]==
'\n') numNLsNeeded--;
1540 else if (data[pos]!=
' ')
1555 out+=
"@tableofcontents{html:";
1560 else if (isImageLink)
1564 if (link.
find(
"@ref ")!=-1 || link.
find(
"\\ref ")!=-1 ||
1569 writeMarkdownImage(
"html", isImageInline, explicitTitle, title, content, link, attributes, fd);
1570 writeMarkdownImage(
"latex", isImageInline, explicitTitle, title, content, link, attributes, fd);
1571 writeMarkdownImage(
"rtf", isImageInline, explicitTitle, title, content, link, attributes, fd);
1572 writeMarkdownImage(
"docbook", isImageInline, explicitTitle, title, content, link, attributes, fd);
1573 writeMarkdownImage(
"xml", isImageInline, explicitTitle, title, content, link, attributes, fd);
1595 if ((lp=link.
find(
"@ref "))!=-1 || (lp=link.
find(
"\\ref "))!=-1 || (lang==SrcLangExt::Markdown && !
isURL(link)))
1627 if (explicitTitle && !title.
isEmpty())
1637 else if ((lp=link.
find(
'#'))!=-1 || link.
find(
'/')!=-1 || link.
find(
'.')!=-1)
1639 if (lp==0 || (lp>0 && !
isURL(link) &&
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
1652 for (
int ii = 0; ii < nlTotal; ii++)
out+=
"\n";
1674 return static_cast<int>(i);
1681 const size_t size = data.size();
1685 while (nb<size && data[nb]==
'`')
1702 if (
end+1<size && data[
end+1]==
'`')
1717 if (
end+1<size && data[
end+1]==
'`')
1730 else if (data[
end]==
'\n')
1741 else if (!markdownStrict && data[
end]==
'\'' && nb==1 && (
end+1==size || (
end+1<size && data[
end+1]!=
'\'' && !
isIdChar(data[
end+1]))))
1744 out+=data.substr(nb,
end-nb);
1747 return static_cast<int>(
end+1);
1749 else if (!markdownStrict && data[
end]==
'\'' && nb==2 &&
end+1<size && data[
end+1]==
'\'')
1752 out+=data.substr(nb,
end-nb);
1755 return static_cast<int>(
end+2);
1759 if (data[
end]!=
' ') pc = data[
end];
1763 if (i < nb && end >= size)
1768 out+=data.substr(0,nb);
1769 return static_cast<int>(nb);
1773 while (
end<size && data[
end]==
'`')
1783 QCString codeFragment = data.substr(nb,
end-nb-nb);
1789 return static_cast<int>(
end);
1808 const size_t size = data.size();
1814 size_t l = endBlockName.
length();
1817 if ((data[i]==
'\\' || data[i]==
'@') &&
1818 data[i-1]!=
'\\' && data[i-1]!=
'@')
1825 return static_cast<int>(i+1+l);
1834 out+=data.substr(0,endPos);
1835 return static_cast<int>(endPos);
1837 if (size>1 && (data[0]==
'\\' || data[0]==
'@'))
1840 if (c==
'[' || c==
']' || c==
'*' || c==
'(' || c==
')' || c==
'`' || c==
'_')
1846 else if (c==
'\\' || c==
'@')
1848 out+=data.substr(0,2);
1852 else if (c==
'-' && size>3 && data[2]==
'-' && data[3]==
'-')
1854 out+=data.substr(1,3);
1858 else if (c==
'-' && size>2 && data[2]==
'-')
1860 out+=data.substr(1,2);
1874 const size_t size = data.size();
1880 out+=data.substr(i,
end-i);
1881 if (
end>=size)
break;
1884 int iend = action(*
this,data.substr(i),i);
1902 const size_t size = data.size();
1903 while (i<size && data[i]==
' ') i++;
1904 if (i==size)
return 0;
1909 while (i < size && data[i] ==
'=')
1914 while (i<size && data[i]==
' ') i++;
1915 int level = (c>1 && (i>=size || data[i]==
'\n')) ? 1 : 0;
1916 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
1931 while (i < size && data[i] ==
'-')
1936 while (i<size && data[i]==
' ') i++;
1937 return (c>1 && (i>=size || data[i]==
'\n')) ?
indentLevel+2 : 0;
1947 const size_t size = data.size();
1948 while (i<size && data[i]==
' ') i++;
1953 while (i<size && (data[i]==
'>' || data[i]==
' '))
1955 if (data[i]==
'>') level++;
1960 bool res = (level>0 && i<size && ((data[i-1]==
' ') || data[i]==
'\n')) || (level > 1);
1975 const size_t size = data.size();
1978 while (i<size && data[i]==
' ') i++;
1979 if (i>=size || data[i]!=
'[') {
return 0; }
1981 size_t refIdStart=i;
1982 while (i<size && data[i]!=
'\n' && data[i]!=
']') i++;
1983 if (i>=size || data[i]!=
']') {
return 0; }
1984 refid = data.substr(refIdStart,i-refIdStart);
1985 if (refid.
isEmpty()) {
return 0; }
1989 if (i>=size || data[i]!=
':') {
return 0; }
1993 while (i<size && data[i]==
' ') i++;
1994 if (i<size && data[i]==
'\n')
1997 while (i<size && data[i]==
' ') i++;
1999 if (i>=size) {
return 0; }
2001 if (i<size && data[i]==
'<') i++;
2003 while (i<size && data[i]!=
' ' && data[i]!=
'\n') i++;
2005 if (i<size && data[i]==
'>') i++;
2006 if (linkStart==linkEnd) {
return 0; }
2007 link = data.substr(linkStart,linkEnd-linkStart);
2009 if (link==
"@ref" || link==
"\\ref")
2012 while (i<size && data[i]!=
'\n' && data[i]!=
'"') i++;
2013 link+=data.substr(argStart,i-argStart);
2020 while (i<size && data[i]==
' ') i++;
2021 if (i<size && data[i]==
'\n')
2025 while (i<size && data[i]==
' ') i++;
2029 AUTO_TRACE_EXIT(
"result={}: end of isLinkRef while looking for title",i);
2034 if (c==
'\'' || c==
'"' || c==
'(')
2039 size_t titleStart=i;
2041 while (i<size && data[i]!=
'\n') i++;
2046 while (
end>titleStart && data[
end]!=c)
end--;
2049 title = data.substr(titleStart,
end-titleStart);
2053 while (i<size && data[i]==
' ') i++;
2065 size_t size = data.size();
2066 if (size>0 && data[size-1]==
'\n') size--;
2067 while (i<size && data[i]==
' ') i++;
2068 if (i>=size) {
AUTO_TRACE_EXIT(
"result=false: empty line");
return false; }
2070 if (c!=
'*' && c!=
'-' && c!=
'_')
2082 else if (data[i]!=
' ')
2097 static const reg::Ex r2(R
"({#(\a[\w-]*)}\s*$)");
2099 std::string ti = title.str();
2102 std::string
id = match[1].str();
2103 title = title.
left(match.position());
2106 warn(
fileName,
lineNr,
"An automatically generated id already has the name '{}'!",
id);
2112 if (((level>0) && (level<=
Config_getInt(TOC_INCLUDE_HEADINGS))) || (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB))
2115 if (pIsIdGenerated) *pIsIdGenerated=
true;
2130 int level = 0, blanks=0;
2131 const size_t size = data.size();
2134 while (i<size && data[i]==
' ') i++;
2135 if (i>=size || data[i]!=
'#')
2139 while (i < size && data[i] ==
'#')
2148 while (i < size && data[i] ==
' ')
2153 if (level==1 && blanks==0)
2160 while (
end<size && data[
end]!=
'\n')
end++;
2161 while (
end>i && (data[
end-1]==
'#' || data[
end-1]==
' '))
end--;
2164 header = data.substr(i,
end-i);
2168 int idx=
static_cast<int>(header.
length())-1;
2169 while (idx>=0 && (header.
at(idx)==
'#' || header.
at(idx)==
' ')) idx--;
2170 header=header.
left(idx+1);
2173 if (allowAdjustLevel && level==1 &&
indentLevel==-1)
2204 while (i<data.size())
2215 (data[(i)]=='<' && \
2216 (data[(i)+1]=='l' || data[(i)+1]=='L') && \
2217 (data[(i)+2]=='i' || data[(i)+2]=='I') && \
2226 const size_t size=data.size();
2230 bool listMarkerSkipped=
FALSE;
2233 (!listMarkerSkipped &&
2234 (data[i]==
'+' || data[i]==
'-' || data[i]==
'*' ||
2235 (data[i]==
'#' && i>0 && data[i-1]==
'-') ||
2236 (isDigit=(data[i]>=
'1' && data[i]<=
'9')) ||
2237 (isLi=(size>=3 && i+3<size &&
isLiTag(i)))
2246 while (j<size && ((data[j]>=
'0' && data[j]<=
'9') || data[j]==
'.'))
2250 if (j+1<size && data[j+1]==
' ')
2252 listMarkerSkipped=
TRUE;
2269 listMarkerSkipped=
TRUE;
2271 else if (data[i]==
'-' && size>=2 && i+2<size && data[i+1]==
'#' && data[i+2]==
' ')
2273 listMarkerSkipped=
TRUE;
2277 else if (data[i]!=
' ' && i+1<size && data[i+1]==
' ')
2279 listMarkerSkipped=
TRUE;
2281 if (data[i]!=
' ' && !listMarkerSkipped)
2295 size_t normalIndent = 0;
2296 while (normalIndent<data.size() && data[normalIndent]==
' ') normalIndent++;
2298 size_t result = listIndent>normalIndent ? listIndent : 0;
2309 while (i<data.size())
2315 else if (data[i]==
'\n')
2319 else if (data[i]!=
' ' && data[i]!=
'\t')
2331 QCString &lang,
size_t &start,
size_t &
end,
size_t &offset,
2335 const char dot =
'.';
2336 auto isAlphaChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z'); };
2337 auto isAlphaNChar = [ ](
char c) {
return (c>=
'A' && c<=
'Z') || (c>=
'a' && c<=
'z') || (c>=
'0' && c<=
'9') || (c==
'+'); };
2338 auto isLangChar = [&](
char c) {
return c==dot || isAlphaChar(c); };
2344 const size_t size = data.size();
2345 while (i < size && data[i] ==
' ')
2350 if (indent>=refIndent+4)
2352 AUTO_TRACE_EXIT(
"result=false: content is part of code block indent={} refIndent={}",indent,refIndent);
2356 if (i<size && data[i]==
'`') tildaChar=
'`';
2357 while (i < size && data[i] == tildaChar)
2364 AUTO_TRACE_EXIT(
"result=false: no fence marker found #tildes={}",startTildes);
2367 if (i<size && data[i]==
'{')
2370 if (data[i] == dot) i++;
2372 while (i<size && (data[i]!=
'\n' && data[i]!=
'}')) i++;
2373 if (i<size && data[i]==
'}')
2375 lang = data.substr(startLang,i-startLang);
2384 else if (i<size && isLangChar(data[i]))
2386 if (data[i] == dot) i++;
2388 if (i<size && isAlphaChar(data[i]))
2391 while (i<size && isAlphaNChar(data[i])) i++;
2393 lang = data.substr(startLang,i-startLang);
2403 if (data[i]==tildaChar)
2407 while (i < size && data[i] == tildaChar)
2412 while (i<size && data[i]==
' ') i++;
2414 if (endTildes==startTildes)
2417 AUTO_TRACE_EXIT(
"result=true: found end marker at offset {} lang='{}'",offset,lang);
2424 warn(fileName, lineNr,
"Ending Inside a fenced code block. Maybe the end marker for the block is missing?");
2429static bool isCodeBlock(std::string_view data,
size_t offset,
size_t &indent)
2436 const size_t size = data.size();
2437 while (i < size && data[i] ==
' ')
2445 AUTO_TRACE_EXIT(
"result={}: line is not indented enough {}<4",
false,indent0);
2448 if (indent0>=size || data[indent0]==
'\n')
2450 AUTO_TRACE_EXIT(
"result={}: only spaces at the end of a comment block",
false);
2457 int offset_i =
static_cast<int>(offset);
2461 int j =
static_cast<int>(i)-offset_i-1;
2463 size_t nl_size =
isNewline(std::string_view(data.data()+j,data.size()-j));
2466 nl_pos[nl++]=j+
static_cast<int>(nl_size);
2472 if (i==0 && nl==2) nl_pos[nl++]=-offset_i;
2483 if (!
isEmptyLine(std::string_view(data.data()+nl_pos[1],nl_pos[0]-nl_pos[1]-1)))
2492 std::string_view(data.data()+nl_pos[2],nl_pos[1]-nl_pos[2])));
2498 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2505 if (nl==1 && !
isEmptyLine(std::string_view(data.data()-offset,offset-1)))
2513 AUTO_TRACE_EXIT(
"result={}: code block if indent difference >4 spaces",res);
2529 const size_t size = data.size();
2532 while (i<size && data[i]==
' ') i++;
2533 if (i < size && data[i] ==
'|' && data[i] !=
'\n')
2542 while (i<size && (j =
isNewline(data.substr(i)))==0) i++;
2545 if (j>0 && i>0) i--;
2546 while (i>0 && data[i]==
' ') i--;
2547 if (i > 0 && data[i - 1] !=
'\\' && data[i] ==
'|')
2561 if (data[i]==
'|' && (i==0 || data[i-1]!=
'\\')) columns++;
2562 if (columns==1) columns++;
2566 if (n==2 && columns==0)
2578 size_t cc0=0, start=0,
end=0;
2582 if (i>=data.size() || cc0<1)
2594 if (data[j]!=
':' && data[j]!=
'-' && data[j]!=
'|' && data[j]!=
' ')
2603 AUTO_TRACE_EXIT(
"result=false: different number of columns as previous line {}!={}",cc1,cc0);
2618 const size_t size = data.size();
2620 size_t columns=0, start=0,
end=0;
2622 size_t headerStart = start;
2623 size_t headerEnd =
end;
2629 std::vector<Alignment> columnAlignment(columns);
2631 bool leftMarker=
false, rightMarker=
false, startFound=
false;
2637 if (data[j]==
':') { leftMarker=
TRUE; startFound=
TRUE; }
2638 if (data[j]==
'-') startFound=
TRUE;
2641 if (data[j]==
'-') rightMarker=
FALSE;
2642 else if (data[j]==
':') rightMarker=
TRUE;
2643 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2667 std::vector<std::vector<TableCell> > tableContents;
2669 size_t m = headerStart;
2670 std::vector<TableCell> headerContents(columns);
2671 for (k=0;k<columns;k++)
2673 while (m<=headerEnd && (data[m]!=
'|' || (m>0 && data[m-1]==
'\\')))
2675 headerContents[k].cellText += data[m++];
2680 headerContents[k].colSpan = headerContents[k].cellText.isEmpty();
2681 headerContents[k].cellText = headerContents[k].cellText.stripWhiteSpace();
2683 tableContents.push_back(headerContents);
2689 if (cc!=columns)
break;
2693 std::vector<TableCell> rowContents(columns);
2696 if (j<=
end+i && (data[j]==
'|' && (j==0 || data[j-1]!=
'\\')))
2700 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2701 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2706 rowContents[k].cellText += data[j];
2712 rowContents[k].colSpan = rowContents[k].cellText.isEmpty();
2713 rowContents[k].cellText = rowContents[k].cellText.stripWhiteSpace();
2714 tableContents.push_back(rowContents);
2720 out+=
"<table class=\"markdownTable\">";
2721 QCString cellTag(
"th"), cellClass(
"class=\"markdownTableHead");
2722 for (
size_t row = 0; row < tableContents.size(); row++)
2728 out+=
"\n<tr class=\"markdownTableRowOdd\">";
2732 out+=
"\n<tr class=\"markdownTableRowEven\">";
2737 out+=
"\n <tr class=\"markdownTableHead\">";
2739 for (
size_t c = 0; c < columns; c++)
2742 QCString cellText(tableContents[row][c].cellText);
2748 if (tableContents[row][c].cellText ==
"^")
2752 if (tableContents[row][c].colSpan)
2754 int cr =
static_cast<int>(c);
2755 while ( cr >= 0 && tableContents[row][cr].colSpan)
2759 if (cr >= 0 && tableContents[row][cr].cellText ==
"^")
continue;
2761 size_t rowSpan = 1, spanRow = row+1;
2762 while ((spanRow < tableContents.size()) &&
2763 (tableContents[spanRow][c].cellText ==
"^"))
2769 out+=
" <" + cellTag +
" " + cellClass;
2771 switch (columnAlignment[c])
2783 out+=
" rowspan=\"" + spanStr +
"\"";
2789 while ((c+1 < columns) && tableContents[row][c+1].colSpan)
2798 out+=
" colspan=\"" + spanStr +
"\"";
2802 out+=
"> " + cellText +
" \\ilinebr </" + cellTag +
">";
2805 cellClass =
"class=\"markdownTableBody";
2821 while (i<data.size() && data[i]!=
'\n')
2823 if (data[i]!=
' ' && data[i]!=
'\t') j++;
2826 if (i>=data.size()) {
return 0; }
2827 if (i<2) {
return 0; }
2828 bool res = (j>0 && data[i-1]==
' ' && data[i-2]==
' ');
2868 out+=
"</"+hTag+
">\n";
2871 else if (data.size()>0)
2873 size_t tmpSize = data.size();
2874 if (data[data.size()-1] ==
'\n') tmpSize--;
2875 out+=data.substr(0,tmpSize);
2879 out+=
"\\ilinebr<br>";
2881 if (tmpSize != data.size())
out+=
'\n';
2887 {
"[!note]",
"\\note" },
2888 {
"[!warning]",
"\\warning" },
2889 {
"[!tip]",
"\\remark" },
2890 {
"[!caution]",
"\\attention" },
2891 {
"[!important]",
"\\important" }
2900 const size_t size = data.size();
2901 std::string startCmd;
2902 int isGitHubAlert =
false;
2903 int isGitHubFirst =
false;
2908 while (
end<=size && data[
end-1]!=
'\n')
end++;
2913 while (j<
end && (data[j]==
' ' || data[j]==
'>'))
2915 if (data[j]==
'>') { level++; indent=j+1; }
2916 else if (j>0 && data[j-1]==
'>') indent=j+1;
2919 if (indent>0 && j>0 && data[j-1]==
'>' &&
2920 !(j==size || data[j]==
'\n'))
2937 isGitHubAlert =
true;
2938 isGitHubFirst =
true;
2939 startCmd = it->second;
2944 if (level!=1 || !isGitHubAlert)
2946 for (
int l=curLevel;l<level-1;l++)
2948 out+=
"<blockquote>";
2950 out +=
"<blockquote>‍";
2952 else if (!startCmd.empty())
2954 out += startCmd +
" ";
2957 else if (level<curLevel)
2960 if (level==0 && isGitHubAlert)
2966 out +=
"</blockquote>\\ilinebr ";
2975 if (curLevel!=0 || !isGitHubAlert)
2977 std::string_view txt = data.substr(indent,
end-indent);
2980 if (!isGitHubFirst)
out +=
"<br>";
2987 isGitHubFirst =
false;
3002 for (
int l=0;l<curLevel;l++)
3004 out+=
"</blockquote>";
3015 size_t size = data.size();
3016 while (i<data.size() && data[i]==
' ') i++;
3019 size_t locStart = i;
3020 if (i>offset) locStart--;
3023 while (i+9<size && data[i]!=
'\n')
3035 location=data.substr(locStart,i-locStart);
3037 while (indent > 0 && i < size && data[i] ==
' ')
3042 if (i<size && data[i]==
'\n') i++;
3053 const size_t size = data.size();
3056 out+=
"@iverbatim\n";
3058 std::string location;
3063 while (
end<=size && data[
end-1]!=
'\n')
end++;
3066 while (j <
end && data[j] ==
' ')
3080 while (emptyLines>0)
3088 std::string lineLoc;
3093 out+=data.substr(offset,
end-offset);
3101 out+=
"@endiverbatim";
3102 if (!location.empty())
3110 while (emptyLines>0)
3126 const size_t size = data.size();
3127 size_t nb=0,
end=offset+1, j=0;
3132 if ((data[
end-1]==
'\\' || data[
end-1]==
'@') &&
3133 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3140 size_t l = endBlockName.
length();
3141 for (;
end+l+1<size;
end++)
3143 if ((data[
end]==
'\\' || data[
end]==
'@') &&
3144 data[
end-1]!=
'\\' && data[
end-1]!=
'@'
3158 else if (nb==0 && data[
end-1]==
'<' && size>=6 &&
end+6<size &&
3159 (
end<=1 || (data[
end-2]!=
'\\' && data[
end-2]!=
'@'))
3162 if (tolower(data[
end])==
'p' && tolower(data[
end+1])==
'r' &&
3163 tolower(data[
end+2])==
'e' && (data[
end+3]==
'>' || data[
end+3]==
' '))
3174 else if (nb==0 && data[
end-1]==
'`')
3176 while (
end <= size && data[
end - 1] ==
'`')
3182 else if (nb>0 && data[
end-1]==
'`')
3185 while (
end <= size && data[
end - 1] ==
'`')
3203 size_t blockStart,
size_t blockEnd)
3206 if (!lang.empty() && lang[0]==
'.') lang=lang.substr(1);
3207 const size_t size=data.size();
3209 while (i<size && (data[i]==
' ' || data[i]==
'\t'))
3230 size_t pi=std::string::npos;
3231 bool newBlock =
false;
3232 bool insideList =
false;
3233 size_t currentIndent = refIndent;
3234 size_t listIndent = refIndent;
3235 const size_t size = data.size();
3242 size_t lineIndent=0;
3243 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3249 if (insideList && lineIndent<currentIndent)
3252 currentIndent = refIndent;
3260 if (listIndent<currentIndent+4)
3264 currentIndent = listIndent;
3271 currentIndent = listIndent;
3280 if (pi!=std::string::npos)
3282 size_t blockStart=0, blockEnd=0, blockOffset=0;
3285 auto addSpecialCommand = [&](
const QCString &startCmd,
const QCString &endCmd)
3287 size_t cmdPos = pi+blockStart+1;
3288 QCString pl = data.substr(cmdPos,blockEnd-blockStart-1);
3294 if (pl[ii]==
'\n') nl++;
3297 bool addNewLines =
false;
3299 (pl[ii]!=
'\\' && pl[ii]!=
'@') ||
3308 pl =
"@"+startCmd+
"\n" + pl +
"@"+endCmd;
3309 addNewLines =
false;
3324 if (addNewLines)
for (
int j=0;j<nl;j++)
out+=
'\n';
3326 if (addNewLines)
out+=
'\n';
3331 addSpecialCommand(
"startuml",
"enduml");
3335 addSpecialCommand(
"dot",
"enddot");
3337 else if (lang==
"msc")
3339 addSpecialCommand(
"msc",
"endmsc");
3346 pi=std::string::npos;
3350 else if (
isBlockQuote(data.substr(pi,i-pi),currentIndent))
3353 pi=std::string::npos;
3360 out+=data.substr(pi,i-pi);
3366 if (pi!=std::string::npos && pi<size)
3376 warn(
fileName,
lineNr,
"Ending inside a fenced code block. Maybe the end marker for the block is missing?");
3378 out+=data.substr(pi);
3392 size_t pi = std::string::npos;
3408 size_t currentIndent = indent;
3409 size_t listIndent = indent;
3410 bool insideList =
false;
3411 bool newBlock =
false;
3414 while (i<data.size())
3419 size_t lineIndent=0;
3421 while (lineIndent<
end && data[i+lineIndent]==
' ') lineIndent++;
3427 if (insideList && lineIndent<currentIndent)
3430 currentIndent = indent;
3438 if (listIndent<currentIndent+4)
3442 currentIndent = listIndent;
3449 currentIndent = listIndent;
3461 if (pi!=std::string::npos)
3463 size_t blockStart=0, blockEnd=0, blockOffset=0;
3465 size_t blockIndent = currentIndent;
3469 if (data[i]==
'@' || data[i]==
'\\') endBlockName =
isBlockCommand(data.substr(i),i);
3473 if (
isLinkRef(data.substr(pi,i-pi),
id,link,title))
3483 size_t l = endBlockName.
length();
3484 while (i+l<data.size())
3486 if ((data[i]==
'\\' || data[i]==
'@') &&
3487 data[i-1]!=
'\\' && data[i-1]!=
'@')
3504 while (pi<data.size() && data[pi]==
' ') pi++;
3505 QCString header = data.substr(pi,i-pi-1);
3512 out+=level==1?
"@section ":
"@subsection ";
3520 out+=level==1?
"<h1>":
"<h2>";
3522 out+=level==1?
"\n</h1>\n":
"\n</h2>\n";
3529 pi=std::string::npos;
3534 else if ((ref=
isLinkRef(data.substr(pi),
id,link,title)))
3548 pi=std::string::npos;
3556 pi=std::string::npos;
3563 pi=std::string::npos;
3576 if (pi!=std::string::npos && pi<data.size())
3578 if (
isLinkRef(data.substr(pi),
id,link,title))
3595#define OPC(x) if (literal_at(data,#x " ") || literal_at(data,#x "\n")) return true
3596 OPC(dir);
OPC(defgroup);
OPC(addtogroup);
OPC(weakgroup);
OPC(ingroup);
3599 OPC(protocol);
OPC(category);
OPC(
union);
OPC(
struct);
OPC(interface);
3600 OPC(idlexcept);
OPC(file);
3610 std::string_view data(docs.
str());
3611 const size_t size = data.size();
3614 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3621 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3627 (data[i]==
'\\' || data[i]==
'@') &&
3642 else if (i+1<size && (data[i]==
'\\' || data[i]==
'@') &&
isOtherPage(data.substr(i+1)))
3660 std::string_view data(docs_org.
str());
3661 const size_t size = data.size();
3663 while (i<size && (data[i]==
' ' || data[i]==
'\n'))
3665 if (data[i]==
'\n') prepend++;
3668 if (i>=size) {
return QCString(); }
3670 while (end1<size && data[end1-1]!=
'\n') end1++;
3677 while (end2<size && data[end2-1]!=
'\n') end2++;
3678 if (
prv->isHeaderline(data.substr(end1),
FALSE))
3680 title = data.substr(i,end1-i-1);
3681 docs+=
"\n\n"+docs_org.
mid(end2);
3682 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3688 if (i<end1 && prv->isAtxHeader(data.substr(i,end1-i),title,
id,
FALSE,&isIdGenerated)>0)
3691 docs+=docs_org.
mid(end1);
3696 id =
prv->extractTitleId(title, 0, &isIdGenerated);
3707 if (input.
isEmpty())
return input;
3712 if (s.
at(s.
length()-1)!=
'\n') s +=
"\n";
3713 s =
detab(s,refIndent);
3717 s =
prv->processQuotations(s.
view(),refIndent);
3721 s =
prv->processBlocks(s.
view(),refIndent);
3739 const char *p = result.
data();
3742 while (*p==
' ') p++;
3743 while (*p==
'\n') {startNewlines++;p++;};
3746 if (p>result.
data())
3749 result = result.
mid(
static_cast<int>(p-result.
data()));
3762 if (i!=-1) baseFn = baseFn.
left(i);
3786 const char *fileBuf,
3787 const std::shared_ptr<Entry> &root,
3790 std::shared_ptr<Entry> current = std::make_shared<Entry>();
3792 current->lang = SrcLangExt::Markdown;
3793 current->fileName = fileName;
3794 current->docFile = fileName;
3795 current->docLine = 1;
3801 bool isIdGenerated =
false;
3809 int indentLevel=title.
isEmpty() ? 0 : -1;
3816 bool wasEmpty =
id.isEmpty();
3817 if (wasEmpty)
id = mdFileNameId;
3823 if (!mdfileAsMainPage.
isEmpty() &&
3827 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3828 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3830 else if (
id==
"mainpage" ||
id==
"index")
3832 if (title.
isEmpty()) title = titleFn;
3833 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr ");
3834 docs.
prepend(
"@mainpage "+title+
"\\ilinebr ");
3836 else if (isSubdirDocs)
3840 docs.
prepend(
"@section " + generatedId +
" " + title +
"\\ilinebr ");
3842 docs.
prepend(
"@dir\\ilinebr ");
3853 docs.
prepend(
"@ianchor{" + title +
"} " +
id +
"\\ilinebr @ianchor{" + relFileName +
"} " + mdFileNameId +
"\\ilinebr ");
3855 else if (!generatedId.
isEmpty())
3857 docs.
prepend(
"@ianchor " + generatedId +
"\\ilinebr ");
3859 else if (
Config_getEnum(MARKDOWN_ID_STYLE)==MARKDOWN_ID_STYLE_t::GITHUB)
3862 docs.
prepend(
"@ianchor{" + title +
"} " + autoId +
"\\ilinebr ");
3864 docs.
prepend(
"@page "+
id+
" "+title+
"\\ilinebr ");
3866 for (
int i = 0; i < prepend; i++) docs.
prepend(
"\n");
3871 static const reg::Ex re(R
"([ ]*[\\@]page\s+(\a[\w-]*)(\s*[^\n]*)\n)");
3873 std::string s = docs.str();
3876 QCString orgLabel = match[1].str();
3877 QCString orgTitle = match[2].str();
3880 docs = docs.
left(match[1].position())+
3883 "\\ilinebr @ianchor{" + orgTitle +
"} "+orgLabel+
"\n"+
3895 p->commentScanner.enterFile(fileName,lineNr);
3897 bool needsEntry =
false;
3901 while (
p->commentScanner.parseCommentBlock(
3919 QCString docFile = current->docFile;
3920 root->moveToSubEntryAndRefresh(current);
3921 current->lang = SrcLangExt::Markdown;
3922 current->docFile = docFile;
3923 current->docLine = lineNr;
3928 root->moveToSubEntryAndKeep(current);
3930 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,...)
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.